Version 0.78j

Refactored printing to strings and web server json response handling in
preparation for adding support for LCD display via serial link
Added a timer to record how long block writes to SD card take
This commit is contained in:
David Crocker 2014-08-17 22:14:33 +01:00
parent 8ec5f6170f
commit 46d78148b0
23 changed files with 484 additions and 386 deletions

View file

@ -24,9 +24,9 @@ Licence: GPL
#define CONFIGURATION_H
#define NAME "RepRapFirmware"
#define VERSION "0.78i-dc42"
#define DATE "2014-08-01"
#define AUTHORS "reprappro, dc42. zpl"
#define VERSION "0.78j-dc42"
#define DATE "2014-08-17"
#define AUTHORS "reprappro, dc42, zpl"
// Other firmware that we might switch to be compatible with.

View file

@ -350,7 +350,7 @@ bool GCodes::LoadMoveBufferFromGCode(GCodeBuffer *gb, bool doingG92, bool applyL
gb->GetFloatArray(eMovement, eMoveCount);
if(tool->DriveCount() != eMoveCount)
{
snprintf(scratchString, STRING_LENGTH, "Wrong number of extruder drives for the selected tool: %s\n", gb->Buffer());
scratchString.printf("Wrong number of extruder drives for the selected tool: %s\n", gb->Buffer());
platform->Message(HOST_MESSAGE, scratchString);
return false;
}
@ -665,7 +665,7 @@ bool GCodes::OffsetAxes(GCodeBuffer* gb)
// Returns true if completed, false if needs to be called again.
// 'reply' is only written if there is an error.
// 'error' is false on entry, gets changed to true if there is an error.
bool GCodes::DoHome(char* reply, bool& error)
bool GCodes::DoHome(StringRef& reply, bool& error)
//pre(reply.upb == STRING_LENGTH)
{
if (homeX && homeY && homeZ)
@ -705,7 +705,7 @@ bool GCodes::DoHome(char* reply, bool& error)
if (platform->MustHomeXYBeforeZ() && (!axisIsHomed[X_AXIS] || !axisIsHomed[Y_AXIS]))
{
// We can only home Z if X and Y have already been homed
strncpy(reply, "Must home X and Y before homing Z", STRING_LENGTH);
reply.copy("Must home X and Y before homing Z");
error = true;
homeZ = false;
return true;
@ -843,7 +843,7 @@ bool GCodes::DoSingleZProbe()
// then that value is used. If it's less than SILLY_Z_VALUE the bed is
// probed and that value is used.
bool GCodes::SetSingleZProbeAtAPosition(GCodeBuffer *gb, char *reply)
bool GCodes::SetSingleZProbeAtAPosition(GCodeBuffer *gb, StringRef& reply)
{
if (!AllMovesAreFinishedAndMoveBufferIsLoaded())
return false;
@ -895,11 +895,11 @@ bool GCodes::SetSingleZProbeAtAPosition(GCodeBuffer *gb, char *reply)
// triangle or four in the corners), then sets the bed transformation to compensate
// for the bed not quite being the plane Z = 0.
bool GCodes::DoMultipleZProbe(char *reply)
bool GCodes::DoMultipleZProbe(StringRef& reply)
{
if (reprap.GetMove()->NumberOfXYProbePoints() < 3)
{
strncpy(reply, "Bed probing: there needs to be 3 or more points set.\n", STRING_LENGTH);
reply.copy("Bed probing: there needs to be 3 or more points set.\n");
return true;
}
@ -930,7 +930,7 @@ bool GCodes::GetProbeCoordinates(int count, float& x, float& y, float& z) const
return zProbesSet;
}
bool GCodes::SetPrintZProbe(GCodeBuffer* gb, char* reply)
bool GCodes::SetPrintZProbe(GCodeBuffer* gb, StringRef& reply)
{
if (!AllMovesAreFinishedAndMoveBufferIsLoaded())
return false;
@ -970,13 +970,13 @@ bool GCodes::SetPrintZProbe(GCodeBuffer* gb, char* reply)
switch(platform->GetZProbeSecondaryValues(v1, v2))
{
case 1:
snprintf(reply, STRING_LENGTH, "%d (%d)", v0, v1);
reply.printf("%d (%d)", v0, v1);
break;
case 2:
snprintf(reply, STRING_LENGTH, "%d (%d, %d)", v0, v1, v2);
reply.printf("%d (%d, %d)", v0, v1, v2);
break;
default:
snprintf(reply, STRING_LENGTH, "%d", v0);
reply.printf("%d", v0);
break;
}
}
@ -994,12 +994,12 @@ const char* GCodes::GetCurrentCoordinates() const
float liveCoordinates[DRIVES + 1];
reprap.GetMove()->LiveCoordinates(liveCoordinates);
snprintf(scratchString, STRING_LENGTH, "X:%.2f Y:%.2f Z:%.2f ", liveCoordinates[X_AXIS], liveCoordinates[Y_AXIS], liveCoordinates[Z_AXIS]);
scratchString.printf("X:%.2f Y:%.2f Z:%.2f ", liveCoordinates[X_AXIS], liveCoordinates[Y_AXIS], liveCoordinates[Z_AXIS]);
for(int i = AXES; i< DRIVES; i++)
{
sncatf(scratchString, STRING_LENGTH, "E%d:%.1f ", i-AXES, liveCoordinates[i]);
scratchString.catf("E%d:%.1f ", i-AXES, liveCoordinates[i]);
}
return scratchString;
return scratchString.Pointer();
}
bool GCodes::OpenFileToWrite(const char* directory, const char* fileName, GCodeBuffer *gb)
@ -1085,8 +1085,8 @@ void GCodes::WriteGCodeToFile(GCodeBuffer *gb)
{
if (gb->Seen('P'))
{
snprintf(scratchString, STRING_LENGTH, "%s", gb->GetIValue());
HandleReply(false, gb == serialGCode, scratchString, 'G', 998, true);
scratchString.printf("%s", gb->GetIValue());
HandleReply(false, gb == serialGCode, scratchString.Pointer(), 'G', 998, true);
return;
}
}
@ -1128,7 +1128,7 @@ void GCodes::DeleteFile(const char* fileName)
{
if(!platform->GetMassStorage()->Delete(platform->GetGCodeDir(), fileName))
{
snprintf(scratchString, STRING_LENGTH, "Unsuccessful attempt to delete: %s\n", fileName);
scratchString.printf("Unsuccessful attempt to delete: %s\n", fileName);
platform->Message(BOTH_ERROR_MESSAGE, scratchString);
}
}
@ -1205,7 +1205,7 @@ bool GCodes::DoDwellTime(float dwell)
// Set working and standby temperatures for
// a tool. I.e. handle a G10.
void GCodes::SetOrReportOffsets(char* reply, GCodeBuffer *gb)
void GCodes::SetOrReportOffsets(StringRef& reply, GCodeBuffer *gb)
{
if(gb->Seen('P'))
{
@ -1214,7 +1214,7 @@ void GCodes::SetOrReportOffsets(char* reply, GCodeBuffer *gb)
Tool* tool = reprap.GetTool(toolNumber);
if(tool == NULL)
{
snprintf(reply, STRING_LENGTH, "Attempt to set/report offsets and temperatures for non-existent tool: %d\n", toolNumber);
reply.printf("Attempt to set/report offsets and temperatures for non-existent tool: %d\n", toolNumber);
return;
}
float standby[HEATERS];
@ -1240,18 +1240,17 @@ void GCodes::SetOrReportOffsets(char* reply, GCodeBuffer *gb)
}
else
{
reply[0] = 0;
snprintf(reply, STRING_LENGTH, "Tool %d - Active/standby temperature(s): ", toolNumber);
reply.printf("Tool %d - Active/standby temperature(s): ", toolNumber);
for(int8_t heater = 0; heater < hCount; heater++)
{
sncatf(reply, STRING_LENGTH, "%.1f/%.1f ", active[heater], standby[heater]);
reply.catf("%.1f/%.1f ", active[heater], standby[heater]);
}
}
}
}
}
void GCodes::AddNewTool(GCodeBuffer *gb, char *reply)
void GCodes::AddNewTool(GCodeBuffer *gb, StringRef& reply)
{
if(!gb->Seen('P'))
{
@ -1384,7 +1383,7 @@ void GCodes::SetMACAddress(GCodeBuffer *gb)
{
if(ipString[sp] == ':')
{
mac[ipp] = strtol(&ipString[spp], NULL, 0);
mac[ipp] = strtoul(&ipString[spp], NULL, 16);
ipp++;
if(ipp > 5)
{
@ -1401,7 +1400,7 @@ void GCodes::SetMACAddress(GCodeBuffer *gb)
sp++;
}
}
mac[ipp] = strtol(&ipString[spp], NULL, 0);
mac[ipp] = strtoul(&ipString[spp], NULL, 16);
if(ipp == 5)
{
platform->SetMACAddress(mac);
@ -1504,13 +1503,13 @@ void GCodes::HandleReply(bool error, bool fromLine, const char* reply, char gMOr
if (s != 0)
{
snprintf(scratchString, STRING_LENGTH, "Emulation of %s is not yet supported.\n", s);
scratchString.printf("Emulation of %s is not yet supported.\n", s);
platform->Message(HOST_MESSAGE, scratchString);
}
}
// Set PID parameters (M301 or M303 command). 'heater' is the default heater number to use.
void GCodes::SetPidParameters(GCodeBuffer *gb, int heater, char reply[STRING_LENGTH])
void GCodes::SetPidParameters(GCodeBuffer *gb, int heater, StringRef& reply)
{
if (gb->Seen('H'))
{
@ -1563,13 +1562,13 @@ void GCodes::SetPidParameters(GCodeBuffer *gb, int heater, char reply[STRING_LEN
}
else
{
snprintf(reply, STRING_LENGTH, "Heater %d P:%.2f I:%.3f D:%.2f T:%.2f S:%.2f W:%.1f B:%.1f\n",
reply.printf("Heater %d P:%.2f I:%.3f D:%.2f T:%.2f S:%.2f W:%.1f B:%.1f\n",
heater, pp.kP, pp.kI * platform->HeatSampleTime(), pp.kD/platform->HeatSampleTime(), pp.kT, pp.kS, pp.pidMax, pp.fullBand);
}
}
}
void GCodes::SetHeaterParameters(GCodeBuffer *gb, char reply[STRING_LENGTH])
void GCodes::SetHeaterParameters(GCodeBuffer *gb, StringRef& reply)
{
if (gb->Seen('P'))
{
@ -1628,7 +1627,7 @@ void GCodes::SetHeaterParameters(GCodeBuffer *gb, char reply[STRING_LENGTH])
}
else
{
snprintf(reply, STRING_LENGTH, "T:%.1f B:%.1f R:%.1f L:%.1f H:%.1f\n",
reply.printf("T:%.1f B:%.1f R:%.1f L:%.1f H:%.1f\n",
r25, beta, pp.thermistorSeriesR, pp.adcLowOffset, pp.adcHighOffset);
}
}
@ -1687,8 +1686,9 @@ bool GCodes::HandleGcode(GCodeBuffer* gb)
bool result = true;
bool error = false;
bool resend = false;
char reply[STRING_LENGTH];
reply[0] = 0;
char replyBuffer[STRING_LENGTH];
StringRef reply(replyBuffer, ARRAY_SIZE(replyBuffer));
reply.Clear();
int code = gb->GetIValue();
switch (code)
@ -1760,7 +1760,7 @@ bool GCodes::HandleGcode(GCodeBuffer* gb)
if (!(axisIsHomed[X_AXIS] && axisIsHomed[Y_AXIS]))
{
// We can only do bed levelling if X and Y have already been homed
strncpy(reply, "Must home X and Y before bed probing", STRING_LENGTH);
reply.copy("Must home X and Y before bed probing");
error = true;
result = true;
}
@ -1788,11 +1788,11 @@ bool GCodes::HandleGcode(GCodeBuffer* gb)
default:
error = true;
snprintf(reply, STRING_LENGTH, "invalid G Code: %s", gb->Buffer());
reply.printf("invalid G Code: %s", gb->Buffer());
}
if (result)
{
HandleReply(error, gb == serialGCode, reply, 'G', code, resend);
HandleReply(error, gb == serialGCode, reply.Pointer(), 'G', code, resend);
}
return result;
}
@ -1802,8 +1802,9 @@ bool GCodes::HandleMcode(GCodeBuffer* gb)
bool result = true;
bool error = false;
bool resend = false;
char reply[STRING_LENGTH];
reply[0] = 0;
char replyBuffer[STRING_LENGTH];
StringRef reply(replyBuffer, ARRAY_SIZE(replyBuffer));
reply.Clear();
int code = gb->GetIValue();
switch (code)
@ -1842,12 +1843,12 @@ bool GCodes::HandleMcode(GCodeBuffer* gb)
bool encapsulate_list;
if (platform->Emulating() == me || platform->Emulating() == reprapFirmware)
{
strcpy(reply, "GCode files:\n");
reply.copy("GCode files:\n");
encapsulate_list = false;
}
else
{
strcpy(reply, "");
reply.Clear();
encapsulate_list = true;
}
@ -1858,20 +1859,20 @@ bool GCodes::HandleMcode(GCodeBuffer* gb)
do {
if (encapsulate_list)
{
sncatf(reply, STRING_LENGTH -1, "%c%s%c%c", FILE_LIST_BRACKET, file_info.fileName, FILE_LIST_BRACKET, FILE_LIST_SEPARATOR);
reply.catf("%c%s%c%c", FILE_LIST_BRACKET, file_info.fileName, FILE_LIST_BRACKET, FILE_LIST_SEPARATOR);
}
else
{
sncatf(reply, STRING_LENGTH -1, "%s\n", file_info.fileName);
reply.catf("%s\n", file_info.fileName);
}
} while (platform->GetMassStorage()->FindNext(file_info));
// remove the last character
reply[strlen(reply) - 1] = 0;
reply[reply.strlen() - 1] = 0;
}
else
{
strcat(reply, "NONE");
reply.cat("NONE");
}
}
break;
@ -1884,7 +1885,7 @@ bool GCodes::HandleMcode(GCodeBuffer* gb)
QueueFileToPrint(gb->GetUnprecedentedString());
if (fileToPrint.IsLive() && platform->Emulating() == marlin)
{
snprintf(reply, STRING_LENGTH, "%s", "File opened\nFile selected\n");
reply.copy("File opened\nFile selected\n");
}
break;
@ -1901,11 +1902,11 @@ bool GCodes::HandleMcode(GCodeBuffer* gb)
case 27: // Report print status - Deprecated
if (fileBeingPrinted.IsLive())
{
strncpy(reply, "SD printing.", STRING_LENGTH);
reply.copy("SD printing.");
}
else
{
strncpy(reply, "Not SD printing.", STRING_LENGTH);
reply.copy("Not SD printing.");
}
break;
@ -1915,11 +1916,11 @@ bool GCodes::HandleMcode(GCodeBuffer* gb)
bool ok = OpenFileToWrite(platform->GetGCodeDir(), str, gb);
if (ok)
{
snprintf(reply, STRING_LENGTH, "Writing to file: %s", str);
reply.printf("Writing to file: %s", str);
}
else
{
snprintf(reply, STRING_LENGTH, "Can't open file %s for writing.\n", str);
reply.printf("Can't open file %s for writing.\n", str);
error = true;
}
}
@ -1987,7 +1988,7 @@ bool GCodes::HandleMcode(GCodeBuffer* gb)
gb->GetFloatArray(eVals, eCount);
if(eCount != DRIVES-AXES)
{
snprintf(scratchString, STRING_LENGTH, "Setting steps/mm - wrong number of E drives: %s\n", gb->Buffer());
scratchString.printf("Setting steps/mm - wrong number of E drives: %s\n", gb->Buffer());
platform->Message(HOST_MESSAGE, scratchString);
}
else
@ -2001,15 +2002,15 @@ bool GCodes::HandleMcode(GCodeBuffer* gb)
if(!seen)
{
snprintf(reply, STRING_LENGTH, "Steps/mm: X: %.3f, Y: %.3f, Z: %.3f, E: ",
reply.printf("Steps/mm: X: %.3f, Y: %.3f, Z: %.3f, E: ",
platform->DriveStepsPerUnit(X_AXIS), platform->DriveStepsPerUnit(Y_AXIS),
platform->DriveStepsPerUnit(Z_AXIS));
for(int8_t drive = AXES; drive < DRIVES; drive++)
{
sncatf(reply, STRING_LENGTH, "%.3f", platform->DriveStepsPerUnit(drive));
reply.catf("%.3f", platform->DriveStepsPerUnit(drive));
if(drive < DRIVES-1)
{
sncatf(reply, STRING_LENGTH, ":");
reply.cat(":");
}
}
}
@ -2051,15 +2052,15 @@ bool GCodes::HandleMcode(GCodeBuffer* gb)
break;
case 105: // Deprecated...
strncpy(reply, "T:", STRING_LENGTH);
reply.copy("T:");
for(int8_t heater = 1; heater < HEATERS; heater++)
{
if(reprap.GetHeat()->GetStatus(heater) != Heat::HS_off)
{
sncatf(reply, STRING_LENGTH, "%.1f ", reprap.GetHeat()->GetTemperature(heater));
reply.catf("%.1f ", reprap.GetHeat()->GetTemperature(heater));
}
}
sncatf(reply, STRING_LENGTH, "B: %.1f ", reprap.GetHeat()->GetTemperature(0));
reply.catf("B: %.1f ", reprap.GetHeat()->GetTemperature(0));
break;
case 106: // Fan on or off
@ -2143,7 +2144,7 @@ bool GCodes::HandleMcode(GCodeBuffer* gb)
const char* str = GetCurrentCoordinates();
if (str != 0)
{
strncpy(reply, str, STRING_LENGTH);
reply.copy(str);
}
else
{
@ -2153,8 +2154,7 @@ bool GCodes::HandleMcode(GCodeBuffer* gb)
break;
case 115: // Print firmware version
snprintf(reply, STRING_LENGTH, "FIRMWARE_NAME:%s FIRMWARE_VERSION:%s ELECTRONICS:%s DATE:%s", NAME, VERSION,
ELECTRONICS, DATE);
reply.printf("FIRMWARE_NAME:%s FIRMWARE_VERSION:%s ELECTRONICS:%s DATE:%s", NAME, VERSION, ELECTRONICS, DATE);
break;
case 116: // Wait for everything, especially set temperatures
@ -2182,7 +2182,7 @@ bool GCodes::HandleMcode(GCodeBuffer* gb)
case 119:
{
snprintf(reply, STRING_LENGTH, "Endstops - ");
reply.copy("Endstops - ");
char comma = ',';
for(int8_t axis = 0; axis < AXES; axis++)
{
@ -2211,7 +2211,7 @@ bool GCodes::HandleMcode(GCodeBuffer* gb)
comma = ' ';
}
sncatf(reply, STRING_LENGTH, "%c: %s%c ", axisLetters[axis], es, comma);
reply.catf("%c: %s%c ", axisLetters[axis], es, comma);
}
}
break;
@ -2253,7 +2253,7 @@ bool GCodes::HandleMcode(GCodeBuffer* gb)
}
else
{
snprintf(reply, STRING_LENGTH, "Heat sample time is %.3f seconds.", platform->HeatSampleTime());
reply.printf("Heat sample time is %.3f seconds.", platform->HeatSampleTime());
}
break;
@ -2323,7 +2323,7 @@ bool GCodes::HandleMcode(GCodeBuffer* gb)
gb->GetFloatArray(eVals, eCount);
if(eCount != DRIVES-AXES)
{
snprintf(scratchString, STRING_LENGTH, "Setting accelerations - wrong number of E drives: %s\n", gb->Buffer());
scratchString.printf("Setting accelerations - wrong number of E drives: %s\n", gb->Buffer());
platform->Message(HOST_MESSAGE, scratchString);
}
else
@ -2337,15 +2337,15 @@ bool GCodes::HandleMcode(GCodeBuffer* gb)
if(!seen)
{
snprintf(reply, STRING_LENGTH, "Accelerations: X: %.1f, Y: %.1f, Z: %.1f, E: ",
reply.printf("Accelerations: X: %.1f, Y: %.1f, Z: %.1f, E: ",
platform->Acceleration(X_AXIS)/distanceScale, platform->Acceleration(Y_AXIS)/distanceScale,
platform->Acceleration(Z_AXIS)/distanceScale);
for(int8_t drive = AXES; drive < DRIVES; drive++)
{
sncatf(reply, STRING_LENGTH, "%.1f", platform->Acceleration(drive)/distanceScale);
reply.catf("%.1f", platform->Acceleration(drive)/distanceScale);
if(drive < DRIVES-1)
{
sncatf(reply, STRING_LENGTH, ":");
reply.cat(":");
}
}
}
@ -2372,7 +2372,7 @@ bool GCodes::HandleMcode(GCodeBuffer* gb)
gb->GetFloatArray(eVals, eCount);
if(eCount != DRIVES-AXES)
{
snprintf(scratchString, STRING_LENGTH, "Setting feedrates - wrong number of E drives: %s\n", gb->Buffer());
scratchString.printf("Setting feedrates - wrong number of E drives: %s\n", gb->Buffer());
platform->Message(HOST_MESSAGE, scratchString);
}
else
@ -2386,15 +2386,15 @@ bool GCodes::HandleMcode(GCodeBuffer* gb)
if(!seen)
{
snprintf(reply, STRING_LENGTH, "Maximum feedrates: X: %.1f, Y: %.1f, Z: %.1f, E: ",
reply.printf("Maximum feedrates: X: %.1f, Y: %.1f, Z: %.1f, E: ",
platform->MaxFeedrate(X_AXIS)/(distanceScale * secondsToMinutes), platform->MaxFeedrate(Y_AXIS)/(distanceScale * secondsToMinutes),
platform->MaxFeedrate(Z_AXIS)/(distanceScale * secondsToMinutes));
for(int8_t drive = AXES; drive < DRIVES; drive++)
{
sncatf(reply, STRING_LENGTH, "%.1f", platform->MaxFeedrate(drive)/(distanceScale * secondsToMinutes));
reply.catf("%.1f", platform->MaxFeedrate(drive)/(distanceScale * secondsToMinutes));
if(drive < DRIVES-1)
{
sncatf(reply, STRING_LENGTH, ":");
reply.cat(":");
}
}
}
@ -2405,7 +2405,7 @@ bool GCodes::HandleMcode(GCodeBuffer* gb)
// This is superseded in this firmware by M codes for the separate types (e.g. M566).
break;
case 206: // Offset axes - Depricated
case 206: // Offset axes - Deprecated
result = OffsetAxes(gb);
break;
@ -2432,7 +2432,7 @@ bool GCodes::HandleMcode(GCodeBuffer* gb)
if (!seen)
{
snprintf(reply, STRING_LENGTH, "Axis limits - ");
reply.copy("Axis limits - ");
char comma = ',';
for(int8_t axis = 0; axis < AXES; axis++)
{
@ -2440,7 +2440,7 @@ bool GCodes::HandleMcode(GCodeBuffer* gb)
{
comma = ' ';
}
sncatf(reply, STRING_LENGTH, "%c: %.1f min, %.1f max%c ", axisLetters[axis],
reply.catf("%c: %.1f min, %.1f max%c ", axisLetters[axis],
platform->AxisMinimum(axis), platform->AxisMaximum(axis), comma);
}
}
@ -2462,7 +2462,7 @@ bool GCodes::HandleMcode(GCodeBuffer* gb)
if(!seen)
{
snprintf(reply, STRING_LENGTH, "Homing feedrates (mm/min) - ");
reply.copy("Homing feedrates (mm/min) - ");
char comma = ',';
for(int8_t axis = 0; axis < AXES; axis++)
{
@ -2471,7 +2471,7 @@ bool GCodes::HandleMcode(GCodeBuffer* gb)
comma = ' ';
}
sncatf(reply, STRING_LENGTH, "%c: %.1f%c ", axisLetters[axis],
reply.catf("%c: %.1f%c ", axisLetters[axis],
platform->HomeFeedRate(axis) * 60.0 / distanceScale, comma);
}
}
@ -2490,7 +2490,7 @@ bool GCodes::HandleMcode(GCodeBuffer* gb)
}
else
{
snprintf(reply, STRING_LENGTH, "Speed factor override: %.1f%%\n", speedFactor * (60.0 * 100.0));
reply.printf("Speed factor override: %.1f%%\n", speedFactor * (60.0 * 100.0));
}
break;
@ -2516,7 +2516,7 @@ bool GCodes::HandleMcode(GCodeBuffer* gb)
}
else
{
snprintf(reply, STRING_LENGTH, "Extrusion factor override for drive %d: %.1f%%\n", drive, extrusionFactors[drive] * 100.0);
reply.printf("Extrusion factor override for drive %d: %.1f%%\n", drive, extrusionFactors[drive] * 100.0);
}
}
@ -2540,7 +2540,7 @@ bool GCodes::HandleMcode(GCodeBuffer* gb)
}
else
{
snprintf(reply, STRING_LENGTH, "Cold extrudes are %s, use M302 P[1/0] to allow or deny them",
reply.printf("Cold extrudes are %s, use M302 P[1/0] to allow or deny them",
reprap.ColdExtrude() ? "enabled" : "disabled");
}
break;
@ -2568,7 +2568,7 @@ bool GCodes::HandleMcode(GCodeBuffer* gb)
else
{
const byte* mac = platform->MACAddress();
snprintf(reply, STRING_LENGTH, "MAC: %x:%x:%x:%x:%x:%x ", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
reply.printf("MAC: %x:%x:%x:%x:%x:%x ", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
}
break;
@ -2579,7 +2579,7 @@ bool GCodes::HandleMcode(GCodeBuffer* gb)
}
else
{
snprintf(reply, STRING_LENGTH, "RepRap name: %s ", reprap.GetWebserver()->GetName());
reply.printf("RepRap name: %s ", reprap.GetWebserver()->GetName());
}
break;
@ -2598,7 +2598,7 @@ bool GCodes::HandleMcode(GCodeBuffer* gb)
else
{
const byte *ip = platform->IPAddress();
snprintf(reply, STRING_LENGTH, "IP address: %d.%d.%d.%d\n ", ip[0], ip[1], ip[2], ip[3]);
reply.printf("IP address: %d.%d.%d.%d\n ", ip[0], ip[1], ip[2], ip[3]);
}
break;
@ -2610,7 +2610,7 @@ bool GCodes::HandleMcode(GCodeBuffer* gb)
else
{
const byte *nm = platform->NetMask();
snprintf(reply, STRING_LENGTH, "Net mask: %d.%d.%d.%d\n ", nm[0], nm[1], nm[2], nm[3]);
reply.printf("Net mask: %d.%d.%d.%d\n ", nm[0], nm[1], nm[2], nm[3]);
}
break;
@ -2622,7 +2622,7 @@ bool GCodes::HandleMcode(GCodeBuffer* gb)
else
{
const byte *gw = platform->GateWay();
snprintf(reply, STRING_LENGTH, "Gateway: %d.%d.%d.%d\n ", gw[0], gw[1], gw[2], gw[3]);
reply.printf("Gateway: %d.%d.%d.%d\n ", gw[0], gw[1], gw[2], gw[3]);
}
break;
@ -2633,32 +2633,32 @@ bool GCodes::HandleMcode(GCodeBuffer* gb)
}
else
{
snprintf(reply, STRING_LENGTH, "Emulating ");
reply.copy("Emulating ");
switch(platform->Emulating())
{
case me:
case reprapFirmware:
sncatf(reply, STRING_LENGTH, "RepRap Firmware (i.e. in native mode)");
reply.cat("RepRap Firmware (i.e. in native mode)");
break;
case marlin:
sncatf(reply, STRING_LENGTH, "Marlin");
reply.cat("Marlin");
break;
case teacup:
sncatf(reply, STRING_LENGTH, "Teacup");
reply.cat("Teacup");
break;
case sprinter:
sncatf(reply, STRING_LENGTH, "Sprinter");
reply.cat("Sprinter");
break;
case repetier:
sncatf(reply, STRING_LENGTH, "Repetier");
reply.cat("Repetier");
break;
default:
sncatf(reply, STRING_LENGTH, "Unknown: (%d)", platform->Emulating());
reply.catf("Unknown: (%d)", platform->Emulating());
}
}
break;
@ -2677,7 +2677,7 @@ bool GCodes::HandleMcode(GCodeBuffer* gb)
}
else
{
snprintf(reply, STRING_LENGTH, "Axis compensations - XY: %.5f, YZ: %.5f, ZX: %.5f ",
reply.printf("Axis compensations - XY: %.5f, YZ: %.5f, ZX: %.5f ",
reprap.GetMove()->AxisCompensation(X_AXIS),
reprap.GetMove()->AxisCompensation(Y_AXIS),
reprap.GetMove()->AxisCompensation(Z_AXIS));
@ -2702,7 +2702,7 @@ bool GCodes::HandleMcode(GCodeBuffer* gb)
if(!seen)
{
snprintf(reply, STRING_LENGTH, "Probe point %d - [%.1f, %.1f]", point,
reply.printf("Probe point %d - [%.1f, %.1f]", point,
reprap.GetMove()->XBedProbePoint(point),
reprap.GetMove()->YBedProbePoint(point));
}
@ -2735,12 +2735,12 @@ bool GCodes::HandleMcode(GCodeBuffer* gb)
}
else
{
snprintf(reply, STRING_LENGTH, "Z Probe type is %d and it is used for these axes:", platform->GetZProbeType());
reply.printf("Z Probe type is %d and it is used for these axes:", platform->GetZProbeType());
for(int axis=0; axis<AXES; axis++)
{
if (zProbeAxes[axis])
{
sncatf(reply, STRING_LENGTH, " %c", axisLetters[axis]);
reply.catf(" %c", axisLetters[axis]);
}
}
}
@ -2753,11 +2753,11 @@ bool GCodes::HandleMcode(GCodeBuffer* gb)
bool ok = OpenFileToWrite(platform->GetSysDir(), str, gb);
if (ok)
{
snprintf(reply, STRING_LENGTH, "Writing to file: %s", str);
reply.printf("Writing to file: %s", str);
}
else
{
snprintf(reply, STRING_LENGTH, "Can't open file %s for writing.\n", str);
reply.printf("Can't open file %s for writing.\n", str);
error = true;
}
}
@ -2769,11 +2769,11 @@ bool GCodes::HandleMcode(GCodeBuffer* gb)
bool ok = OpenFileToWrite(platform->GetWebDir(), str, gb);
if (ok)
{
snprintf(reply, STRING_LENGTH, "Writing to file: %s", str);
reply.printf("Writing to file: %s", str);
}
else
{
snprintf(reply, STRING_LENGTH, "Can't open file %s for writing.\n", str);
reply.printf("Can't open file %s for writing.\n", str);
error = true;
}
}
@ -2822,7 +2822,7 @@ bool GCodes::HandleMcode(GCodeBuffer* gb)
gb->GetFloatArray(eVals, eCount);
if(eCount != DRIVES-AXES)
{
snprintf(reply, STRING_LENGTH, "Setting feedrates - wrong number of E drives: %s\n", gb->Buffer());
reply.printf("Setting feedrates - wrong number of E drives: %s\n", gb->Buffer());
}
else
{
@ -2834,15 +2834,15 @@ bool GCodes::HandleMcode(GCodeBuffer* gb)
}
else if(!seen)
{
snprintf(reply, STRING_LENGTH, "Minimum feedrates: X: %.1f, Y: %.1f, Z: %.1f, E: ",
reply.printf("Minimum feedrates: X: %.1f, Y: %.1f, Z: %.1f, E: ",
platform->InstantDv(X_AXIS)/(distanceScale * secondsToMinutes), platform->InstantDv(Y_AXIS)/(distanceScale * secondsToMinutes),
platform->InstantDv(Z_AXIS)/(distanceScale * secondsToMinutes));
for(int8_t drive = AXES; drive < DRIVES; drive++)
{
sncatf(reply, STRING_LENGTH, "%.1f", platform->InstantDv(drive)/(distanceScale * secondsToMinutes));
reply.catf("%.1f", platform->InstantDv(drive)/(distanceScale * secondsToMinutes));
if(drive < DRIVES-1)
{
sncatf(reply, STRING_LENGTH, ":");
reply.cat(":");
}
}
}
@ -2868,7 +2868,7 @@ bool GCodes::HandleMcode(GCodeBuffer* gb)
gb->GetFloatArray(eVals, eCount);
if(eCount != DRIVES-AXES)
{
snprintf(reply, STRING_LENGTH, "Setting motor currents - wrong number of E drives: %s\n", gb->Buffer());
reply.printf("Setting motor currents - wrong number of E drives: %s\n", gb->Buffer());
}
else
{
@ -2880,15 +2880,15 @@ bool GCodes::HandleMcode(GCodeBuffer* gb)
}
else if (!seen)
{
snprintf(reply, STRING_LENGTH, "Axis currents (mA) - X:%.1f, Y:%.1f, Z:%.1f, E:",
reply.printf("Axis currents (mA) - X:%.1f, Y:%.1f, Z:%.1f, E:",
platform->MotorCurrent(X_AXIS), platform->MotorCurrent(Y_AXIS),
platform->MotorCurrent(Z_AXIS));
for(int8_t drive = AXES; drive < DRIVES; drive++)
{
sncatf(reply, STRING_LENGTH, "%.1f", platform->MotorCurrent(drive));
reply.catf("%.1f", platform->MotorCurrent(drive));
if(drive < DRIVES-1)
{
sncatf(reply, STRING_LENGTH, ":");
reply.cat(":");
}
}
}
@ -2898,7 +2898,7 @@ bool GCodes::HandleMcode(GCodeBuffer* gb)
case 998:
if (gb->Seen('P'))
{
snprintf(reply, STRING_LENGTH, "%s", gb->GetIValue());
reply.printf("%d", gb->GetIValue());
resend = true;
}
break;
@ -2913,11 +2913,11 @@ bool GCodes::HandleMcode(GCodeBuffer* gb)
default:
error = true;
snprintf(reply, STRING_LENGTH, "invalid M Code: %s", gb->Buffer());
reply.printf("invalid M Code: %s", gb->Buffer());
}
if (result)
{
HandleReply(error, gb == serialGCode, reply, 'M', code, resend);
HandleReply(error, gb == serialGCode, reply.Pointer(), 'M', code, resend);
}
return result;
}
@ -2953,8 +2953,8 @@ bool GCodes::ChangeTool(int newToolNumber)
case 0: // Pre-release sequence for the old tool (if any)
if(oldTool != NULL)
{
snprintf(scratchString, STRING_LENGTH, "tfree%d.g", oldTool->Number());
if(DoFileCannedCycles(scratchString))
scratchString.printf("tfree%d.g", oldTool->Number());
if(DoFileCannedCycles(scratchString.Pointer()))
{
toolChangeSequence++;
}
@ -2976,8 +2976,8 @@ bool GCodes::ChangeTool(int newToolNumber)
case 2: // Run the pre-tool-change canned cycle for the new tool (if any)
if(newTool != NULL)
{
snprintf(scratchString, STRING_LENGTH, "tpre%d.g", newToolNumber);
if(DoFileCannedCycles(scratchString))
scratchString.printf("tpre%d.g", newToolNumber);
if(DoFileCannedCycles(scratchString.Pointer()))
{
toolChangeSequence++;
}
@ -2996,8 +2996,8 @@ bool GCodes::ChangeTool(int newToolNumber)
case 4: // Run the post-tool-change canned cycle for the new tool (if any)
if(newTool != NULL)
{
snprintf(scratchString, STRING_LENGTH, "tpost%d.g", newToolNumber);
if(DoFileCannedCycles(scratchString))
scratchString.printf("tpost%d.g", newToolNumber);
if(DoFileCannedCycles(scratchString.Pointer()))
{
toolChangeSequence++;
}
@ -3217,7 +3217,7 @@ const void GCodeBuffer::GetFloatArray(float a[], int& returnedLength)
{
if(length >= returnedLength) // Array limit has been set in here
{
snprintf(scratchString, STRING_LENGTH, "GCodes: Attempt to read a GCode float array that is too long: %s\n", gcodeBuffer);
scratchString.printf("GCodes: Attempt to read a GCode float array that is too long: %s\n", gcodeBuffer);
platform->Message(HOST_MESSAGE, scratchString);
readPointer = -1;
returnedLength = 0;
@ -3271,7 +3271,7 @@ const void GCodeBuffer::GetLongArray(long l[], int& returnedLength)
{
if(length >= returnedLength) // Array limit has been set in here
{
snprintf(scratchString, STRING_LENGTH, "GCodes: Attempt to read a GCode long array that is too long: %s\n", gcodeBuffer);
scratchString.printf("GCodes: Attempt to read a GCode long array that is too long: %s\n", gcodeBuffer);
platform->Message(HOST_MESSAGE, scratchString);
readPointer = -1;
returnedLength = 0;

View file

@ -116,13 +116,13 @@ class GCodes
int SetUpMove(GCodeBuffer* gb); // Pass a move on to the Move module
bool DoDwell(GCodeBuffer *gb); // Wait for a bit
bool DoDwellTime(float dwell); // Really wait for a bit
bool DoHome(char *reply, bool& error); // Home some axes
bool DoHome(StringRef& reply, bool& error); // Home some axes
bool DoSingleZProbeAtPoint(); // Probe at a given point
bool DoSingleZProbe(); // Probe where we are
bool SetSingleZProbeAtAPosition(GCodeBuffer *gb, char *reply); // Probes at a given position - see the comment at the head of the function itself
bool DoMultipleZProbe(char *reply); // Probes a series of points and sets the bed equation
bool SetPrintZProbe(GCodeBuffer *gb, char *reply); // Either return the probe value, or set its threshold
void SetOrReportOffsets(char* reply, GCodeBuffer *gb); // Deal with a G10
bool SetSingleZProbeAtAPosition(GCodeBuffer *gb, StringRef& reply); // Probes at a given position - see the comment at the head of the function itself
bool DoMultipleZProbe(StringRef& reply); // Probes a series of points and sets the bed equation
bool SetPrintZProbe(GCodeBuffer *gb, StringRef& reply); // Either return the probe value, or set its threshold
void SetOrReportOffsets(StringRef& reply, GCodeBuffer *gb); // Deal with a G10
bool SetPositions(GCodeBuffer *gb); // Deal with a G92
bool LoadMoveBufferFromGCode(GCodeBuffer *gb, // Set up a move for the Move class
bool doingG92, bool applyLimits);
@ -140,10 +140,10 @@ class GCodes
bool SendConfigToLine(); // Deal with M503
void WriteHTMLToFile(char b, GCodeBuffer *gb); // 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, char reply[STRING_LENGTH]); // Set the P/I/D parameters for a heater
void SetHeaterParameters(GCodeBuffer *gb, char reply[STRING_LENGTH]); // Set the thermistor and ADC parameters for a heater
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
int8_t Heater(int8_t head) const; // Legacy G codes start heaters at 0, but we use 0 for the bed. This sorts that out.
void AddNewTool(GCodeBuffer *gb, char* reply); // Create a new tool definition
void AddNewTool(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 ChangeTool(int newToolNumber); // Select a new tool
bool ToolHeatersAtSetTemperatures(const Tool *tool) const; // Wait for the heaters associated with the specified tool to reach their set temperatures

View file

@ -73,7 +73,7 @@ void Heat::Diagnostics()
{
if (pids[heater]->active)
{
snprintf(scratchString, STRING_LENGTH, "Heater %d: I-accumulator = %.1f\n", heater, pids[heater]->temp_iState);
scratchString.printf("Heater %d: I-accumulator = %.1f\n", heater, pids[heater]->temp_iState);
platform->AppendMessage(BOTH_MESSAGE, scratchString);
}
}
@ -163,7 +163,7 @@ void PID::Spin()
platform->SetHeater(heater, 0.0);
temperatureFault = true;
switchedOff = true;
snprintf(scratchString, STRING_LENGTH, "Temperature fault on heater %d, T = %.1f\n", heater, temperature);
scratchString.printf("Temperature fault on heater %d, T = %.1f\n", heater, temperature);
platform->Message(BOTH_MESSAGE, scratchString);
reprap.FlagTemperatureFault(heater);
}
@ -191,8 +191,7 @@ void PID::Spin()
platform->SetHeater(heater, 0.0);
temperatureFault = true;
switchedOff = true;
snprintf(scratchString, STRING_LENGTH, "Heating fault on heater %d, T = %.1f C; still not at temperature after %f seconds.\n",
heater, temperature, tim);
scratchString.printf("Heating fault on heater %d, T = %.1f C; still not at temperature after %f seconds.\n", heater, temperature, tim);
platform->Message(BOTH_MESSAGE, scratchString);
reprap.FlagTemperatureFault(heater);
}

View file

@ -136,7 +136,7 @@
/ Always set 512 for memory card and hard disk but a larger value may be
/ required for on-board flash memory, floppy disk and optical disk.
/ When _MAX_SS is larger than 512, it configures FatFs to variable sector size
/ and GET_SECTOR_SIZE command must be implememted to the disk_ioctl function. */
/ and GET_SECTOR_SIZE command must be implemented to the disk_ioctl function. */
#define _MULTI_PARTITION 0 /* 0:Single partition, 1/2:Enable multiple partition */
@ -147,7 +147,7 @@
#define _USE_ERASE 0 /* 0:Disable or 1:Enable */
/* To enable sector erase feature, set _USE_ERASE to 1. CTRL_ERASE_SECTOR command
/ should be added to the disk_ioctl functio. */
/ should be added to the disk_ioctl function. */

View file

@ -44,6 +44,9 @@
#include "../SD_HSMCI.h"
#include "hsmci.h"
extern debugPrintf(const char *fmt, ...);
#define hsmci_debug(_fmt, ...) debugPrintf(_fmt, __VA_ARGS__)
/**
* \ingroup sam_drivers_hsmci
* \defgroup sam_drivers_hsmci_internal High Speed MultiMedia Card Interface
@ -84,7 +87,7 @@
#if (SAM3S || SAM4S)
// PDC is used for transferts
// PDC is used for transfers
#elif (SAM3U || SAM3XA_SERIES)
// DMA is used for transferts
# include "dmac.h"
@ -173,7 +176,7 @@ static bool hsmci_wait_busy(void)
do {
sr = HSMCI->HSMCI_SR;
if (busy_wait-- == 0) {
// printf("%s: timeout\n\r", __func__);
hsmci_debug("%s: timeout\n\r", __func__);
hsmci_reset();
return false;
}
@ -184,7 +187,7 @@ static bool hsmci_wait_busy(void)
/** \brief Send a command
*
* \param cmdr CMDR resgister bit to use for this command
* \param cmdr CMDR register bit to use for this command
* \param cmd Command definition
* \param arg Argument of the command
*
@ -222,7 +225,7 @@ static bool hsmci_send_cmd_execute(uint32_t cmdr, sdmmc_cmd_def_t cmd,
if (sr & (HSMCI_SR_CSTOE | HSMCI_SR_RTOE
| HSMCI_SR_RCRCE
| HSMCI_SR_RDIRE)) {
// printf("%s: CMD 0x%08x sr 0x%08x CMD 3 error\n\r",__func__, cmd, sr);
hsmci_debug("%s: CMD 0x%08x sr 0x%08x CMD 3 error\n\r",__func__, cmd, sr);
hsmci_reset();
return false;
}
@ -230,7 +233,7 @@ static bool hsmci_send_cmd_execute(uint32_t cmdr, sdmmc_cmd_def_t cmd,
if (sr & (HSMCI_SR_CSTOE | HSMCI_SR_RTOE
| HSMCI_SR_RENDE | HSMCI_SR_RCRCE
| HSMCI_SR_RDIRE | HSMCI_SR_RINDE)) {
// printf("%s: CMD 0x%08x sr 0x%08x RESP_CRC error\n\r",__func__, cmd, sr);
hsmci_debug("%s: CMD 0x%08x sr 0x%08x RESP_CRC error\n\r",__func__, cmd, sr);
hsmci_reset();
return false;
}
@ -238,7 +241,7 @@ static bool hsmci_send_cmd_execute(uint32_t cmdr, sdmmc_cmd_def_t cmd,
if (sr & (HSMCI_SR_CSTOE | HSMCI_SR_RTOE
| HSMCI_SR_RENDE
| HSMCI_SR_RDIRE | HSMCI_SR_RINDE)) {
// printf("%s: CMD 0x%08x sr 0x%08x error\n\r",__func__, cmd, sr);
hsmci_debug("%s: CMD 0x%08x sr 0x%08x error\n\r",__func__, cmd, sr);
hsmci_reset();
return false;
}
@ -485,7 +488,7 @@ bool hsmci_read_word(uint32_t* value)
sr = HSMCI->HSMCI_SR;
if (sr & (HSMCI_SR_UNRE | HSMCI_SR_OVRE | \
HSMCI_SR_DTOE | HSMCI_SR_DCRCE)) {
// printf("%s: DMA sr 0x%08x error\n\r",__func__, sr);
hsmci_debug("%s: DMA sr 0x%08x error\n\r",__func__, sr);
hsmci_reset();
return false;
}
@ -504,7 +507,7 @@ bool hsmci_read_word(uint32_t* value)
sr = HSMCI->HSMCI_SR;
if (sr & (HSMCI_SR_UNRE | HSMCI_SR_OVRE | \
HSMCI_SR_DTOE | HSMCI_SR_DCRCE)) {
// printf("%s: DMA sr 0x%08x error\n\r",__func__, sr);
hsmci_debug("%s: DMA sr 0x%08x error\n\r",__func__, sr);
hsmci_reset();
return false;
}
@ -523,7 +526,7 @@ bool hsmci_write_word(uint32_t value)
sr = HSMCI->HSMCI_SR;
if (sr & (HSMCI_SR_UNRE | HSMCI_SR_OVRE | \
HSMCI_SR_DTOE | HSMCI_SR_DCRCE)) {
// printf("%s: DMA sr 0x%08x error\n\r",__func__, sr);
hsmci_debug("%s: DMA sr 0x%08x error\n\r",__func__, sr);
hsmci_reset();
return false;
}
@ -542,7 +545,7 @@ bool hsmci_write_word(uint32_t value)
sr = HSMCI->HSMCI_SR;
if (sr & (HSMCI_SR_UNRE | HSMCI_SR_OVRE | \
HSMCI_SR_DTOE | HSMCI_SR_DCRCE)) {
// printf("%s: DMA sr 0x%08x error\n\r",__func__, sr);
hsmci_debug("%s: DMA sr 0x%08x error\n\r",__func__, sr);
hsmci_reset();
return false;
}
@ -617,7 +620,7 @@ bool hsmci_wait_end_of_read_blocks(void)
sr = HSMCI->HSMCI_SR;
if (sr & (HSMCI_SR_UNRE | HSMCI_SR_OVRE | \
HSMCI_SR_DTOE | HSMCI_SR_DCRCE)) {
// printf("%s: DMA sr 0x%08x error\n\r",__func__, sr);
hsmci_debug("%s: DMA sr 0x%08x error\n\r",__func__, sr);
hsmci_reset();
// Disable DMA
dmac_channel_disable(DMAC, CONF_HSMCI_DMA_CHANNEL);
@ -699,7 +702,7 @@ bool hsmci_wait_end_of_write_blocks(void)
sr = HSMCI->HSMCI_SR;
if (sr & (HSMCI_SR_UNRE | HSMCI_SR_OVRE | \
HSMCI_SR_DTOE | HSMCI_SR_DCRCE)) {
//printf("%s: DMA sr 0x%08x error\n\r",__func__, sr);
hsmci_debug("%s: DMA sr 0x%08x error\n\r",__func__, sr);
hsmci_reset();
// Disable DMA
dmac_channel_disable(DMAC, CONF_HSMCI_DMA_CHANNEL);
@ -749,7 +752,7 @@ bool hsmci_wait_end_of_read_blocks(void)
sr = HSMCI->HSMCI_SR;
if (sr & (HSMCI_SR_UNRE | HSMCI_SR_OVRE | \
HSMCI_SR_DTOE | HSMCI_SR_DCRCE)) {
//printf("%s: PDC sr 0x%08x error\n\r",__func__, sr);
hsmci_debug("%s: PDC sr 0x%08x error\n\r",__func__, sr);
HSMCI->HSMCI_PTCR = HSMCI_PTCR_RXTDIS | HSMCI_PTCR_TXTDIS;
hsmci_reset();
return false;
@ -766,7 +769,7 @@ bool hsmci_wait_end_of_read_blocks(void)
sr = HSMCI->HSMCI_SR;
if (sr & (HSMCI_SR_UNRE | HSMCI_SR_OVRE | \
HSMCI_SR_DTOE | HSMCI_SR_DCRCE)) {
//printf("%s: PDC sr 0x%08x last transfer error\n\r",
hsmci_debug("%s: PDC sr 0x%08x last transfer error\n\r",
__func__, sr);
hsmci_reset();
return false;
@ -805,8 +808,7 @@ bool hsmci_wait_end_of_write_blocks(void)
if (sr &
(HSMCI_SR_UNRE | HSMCI_SR_OVRE | \
HSMCI_SR_DTOE | HSMCI_SR_DCRCE)) {
//printf("%s: PDC sr 0x%08x error\n\r",
__func__, sr);
hsmci_debug("%s: PDC sr 0x%08x error\n\r", __func__, sr);
hsmci_reset();
HSMCI->HSMCI_PTCR = HSMCI_PTCR_RXTDIS | HSMCI_PTCR_TXTDIS;
return false;
@ -823,7 +825,7 @@ bool hsmci_wait_end_of_write_blocks(void)
sr = HSMCI->HSMCI_SR;
if (sr & (HSMCI_SR_UNRE | HSMCI_SR_OVRE | \
HSMCI_SR_DTOE | HSMCI_SR_DCRCE)) {
//printf("%s: PDC sr 0x%08x last transfer error\n\r",__func__, sr);
hsmci_debug("%s: PDC sr 0x%08x last transfer error\n\r",__func__, sr);
hsmci_reset();
return false;
}

View file

@ -805,7 +805,7 @@ float Move::TriangleZ(float x, float y) const
return 0.0;
}
void Move::SetProbedBedEquation(char *reply)
void Move::SetProbedBedEquation(StringRef& reply)
{
switch(NumberOfProbePoints())
{
@ -871,10 +871,10 @@ void Move::SetProbedBedEquation(char *reply)
platform->Message(HOST_MESSAGE, "Attempt to set bed compensation before all probe points have been recorded.");
}
snprintf(reply, STRING_LENGTH, "Bed equation fits points");
reply.copy("Bed equation fits points");
for(int8_t point = 0; point < NumberOfProbePoints(); point++)
{
sncatf(reply, STRING_LENGTH, " [%.1f, %.1f, %.3f]", xBedProbePoints[point], yBedProbePoints[point], zBedProbePoints[point]);
reply.catf(" [%.1f, %.1f, %.3f]", xBedProbePoints[point], yBedProbePoints[point], zBedProbePoints[point]);
}
}
@ -1142,7 +1142,9 @@ MovementProfile DDA::Init(LookAhead* lookAhead, float& u, float& v)
void DDA::Start()
{
for(int8_t drive = 0; drive < DRIVES; drive++)
{
platform->SetDirection(drive, directions[drive]);
}
platform->SetInterrupt(timeStep); // seconds
active = true;
@ -1157,7 +1159,6 @@ void DDA::Step()
return;
int drivesMoving = 0;
// uint8_t extrudersMoving = 0;
for(size_t drive = 0; drive < DRIVES; drive++)
{
@ -1214,7 +1215,7 @@ void DDA::Step()
else if(stepCount >= startDStep)
{
velocity -= acceleration*timeStep;
if(velocity < instantDv)
if (velocity < instantDv)
{
velocity = instantDv;
}
@ -1337,7 +1338,7 @@ long LookAhead::EndPointToMachine(int8_t drive, float coord)
void LookAhead::PrintMove()
{
snprintf(scratchString, STRING_LENGTH, "X,Y,Z: %.1f %.1f %.1f, min v: %.2f, max v: %.1f, acc: %.1f, feed: %.1f, u: %.3f, v: %.3f\n",
scratchString.printf("X,Y,Z: %.1f %.1f %.1f, min v: %.2f, max v: %.1f, acc: %.1f, feed: %.1f, u: %.3f, v: %.3f\n",
MachineToEndPoint(X_AXIS), MachineToEndPoint(Y_AXIS), MachineToEndPoint(Z_AXIS),
MinSpeed(), MaxSpeed(), Acceleration(), FeedRate(), Previous()->V(), V());
platform->Message(HOST_MESSAGE, scratchString);

2
Move.h
View file

@ -191,7 +191,7 @@ class Move
bool AllProbeCoordinatesSet(int index) const; // XY, and Z all set for this one?
bool XYProbeCoordinatesSet(int index) const; // Just XY set for this one?
void SetZProbing(bool probing); // Set the Z probe live
void SetProbedBedEquation(char *reply); // When we have a full set of probed points, work out the bed's equation
void SetProbedBedEquation(StringRef& reply); // When we have a full set of probed points, work out the bed's equation
float SecondDegreeTransformZ(float x, float y) const; // Used for second degree bed equation
float GetLastProbedZ() const; // What was the Z when the probe last fired?
void SetAxisCompensation(int8_t axis, float tangent); // Set an axis-pair compensation angle

View file

@ -758,44 +758,44 @@ void Platform::Diagnostics()
const char *ramstart = (char *) 0x20070000;
const struct mallinfo mi = mallinfo();
AppendMessage(BOTH_MESSAGE, "Memory usage:\n\n");
snprintf(scratchString, STRING_LENGTH, "Program static ram used: %d\n", &_end - ramstart);
scratchString.printf("Program static ram used: %d\n", &_end - ramstart);
AppendMessage(BOTH_MESSAGE, scratchString);
snprintf(scratchString, STRING_LENGTH, "Dynamic ram used: %d\n", mi.uordblks);
scratchString.printf("Dynamic ram used: %d\n", mi.uordblks);
AppendMessage(BOTH_MESSAGE, scratchString);
snprintf(scratchString, STRING_LENGTH, "Recycled dynamic ram: %d\n", mi.fordblks);
scratchString.printf("Recycled dynamic ram: %d\n", mi.fordblks);
AppendMessage(BOTH_MESSAGE, scratchString);
size_t currentStack, maxStack, neverUsed;
GetStackUsage(&currentStack, &maxStack, &neverUsed);
snprintf(scratchString, STRING_LENGTH, "Current stack ram used: %d\n", currentStack);
scratchString.printf("Current stack ram used: %d\n", currentStack);
AppendMessage(BOTH_MESSAGE, scratchString);
snprintf(scratchString, STRING_LENGTH, "Maximum stack ram used: %d\n", maxStack);
scratchString.printf("Maximum stack ram used: %d\n", maxStack);
AppendMessage(BOTH_MESSAGE, scratchString);
snprintf(scratchString, STRING_LENGTH, "Never used ram: %d\n", neverUsed);
scratchString.printf("Never used ram: %d\n", neverUsed);
AppendMessage(BOTH_MESSAGE, scratchString);
// Show the up time and reason for the last reset
const uint32_t now = (uint32_t)Time(); // get up time in seconds
const char* resetReasons[8] = { "power up", "backup", "watchdog", "software", "external", "?", "?", "?" };
snprintf(scratchString, STRING_LENGTH, "Last reset %02d:%02d:%02d ago, cause: %s\n",
scratchString.printf("Last reset %02d:%02d:%02d ago, cause: %s\n",
(unsigned int)(now/3600), (unsigned int)((now % 3600)/60), (unsigned int)(now % 60),
resetReasons[(REG_RSTC_SR & RSTC_SR_RSTTYP_Msk) >> RSTC_SR_RSTTYP_Pos]);
AppendMessage(BOTH_MESSAGE, scratchString);
// Show the error code stored at the last software reset
snprintf(scratchString, STRING_LENGTH, "Last software reset code & available RAM: 0x%04x, %u\n", nvData.resetReason, nvData.neverUsedRam);
scratchString.printf("Last software reset code & available RAM: 0x%04x, %u\n", nvData.resetReason, nvData.neverUsedRam);
AppendMessage(BOTH_MESSAGE, scratchString);
// Show the current error codes
snprintf(scratchString, STRING_LENGTH, "Error status: %u\n", errorCodeBits);
scratchString.printf("Error status: %u\n", errorCodeBits);
AppendMessage(BOTH_MESSAGE, scratchString);
// Show the current probe position heights
strncpy(scratchString, "Bed probe heights:", STRING_LENGTH);
scratchString.copy("Bed probe heights:");
for (size_t i = 0; i < NUMBER_OF_PROBE_POINTS; ++i)
{
sncatf(scratchString, STRING_LENGTH, " %.3f", reprap.GetMove()->ZBedProbePoint(i));
scratchString.catf(" %.3f", reprap.GetMove()->ZBedProbePoint(i));
}
sncatf(scratchString, STRING_LENGTH, "\n");
scratchString.cat("\n");
AppendMessage(BOTH_MESSAGE, scratchString);
// Show the number of free entries in the file table
@ -807,8 +807,13 @@ void Platform::Diagnostics()
++numFreeFiles;
}
}
snprintf(scratchString, STRING_LENGTH, "Free file entries: %u\n", numFreeFiles);
scratchString.printf("Free file entries: %u\n", numFreeFiles);
AppendMessage(BOTH_MESSAGE, scratchString);
// Show the longest write time
scratchString.printf("Longest block write time: %.1fms\n", FileStore::GetAndClearLongestWriteTime());
AppendMessage(BOTH_MESSAGE, scratchString);
reprap.Timing();
#if LWIP_STATS
@ -859,7 +864,7 @@ void Platform::ClassReport(char* className, float &lastTime)
if (Time() - lastTime < LONG_TIME)
return;
lastTime = Time();
snprintf(scratchString, STRING_LENGTH, "Class %s spinning.\n", className);
scratchString.printf("Class %s spinning.\n", className);
Message(HOST_MESSAGE, scratchString);
}
@ -1753,8 +1758,8 @@ bool FileStore::WriteBuffer()
{
if (bufferPointer != 0)
{
FRESULT writeStatus = f_write(&file, buf, bufferPointer, &lastBufferEntry);
if ((writeStatus != FR_OK) || (lastBufferEntry != bufferPointer))
bool ok = InternalWriteBlock((const char*)buf, bufferPointer);
if (!ok)
{
platform->Message(HOST_MESSAGE, "Error writing file. Disc may be full.\n");
return false;
@ -1810,9 +1815,19 @@ bool FileStore::Write(const char *s, unsigned int len)
{
return false;
}
InternalWriteBlock(s, len);
}
bool FileStore::InternalWriteBlock(const char *s, unsigned int len)
{
unsigned int bytesWritten;
uint32_t time = micros();
FRESULT writeStatus = f_write(&file, s, len, &bytesWritten);
time = micros() - time;
if (time > longestWriteTime)
{
longestWriteTime = time;
}
if ((writeStatus != FR_OK) || (bytesWritten != len))
{
platform->Message(HOST_MESSAGE, "Error writing file. Disc may be full.\n");
@ -1835,6 +1850,14 @@ bool FileStore::Flush()
return f_sync(&file) == FR_OK;
}
float FileStore::GetAndClearLongestWriteTime()
{
float ret = (float)longestWriteTime/1000.0;
longestWriteTime = 0;
return ret;
}
uint32_t FileStore::longestWriteTime = 0;
//***************************************************************************************************

View file

@ -364,6 +364,7 @@ public:
unsigned long Length(); // File size in bytes
void Duplicate(); // Create a second reference to this file
bool Flush(); // Write remaining buffer data
static float GetAndClearLongestWriteTime(); // Return the longest time it took to write a block to a file, in milliseconds
friend class Platform;
@ -381,12 +382,15 @@ private:
bool ReadBuffer();
bool WriteBuffer();
bool InternalWriteBlock(const char *s, unsigned int len);
FIL file;
Platform* platform;
bool writing;
unsigned int lastBufferEntry;
unsigned int openCount;
static uint32_t longestWriteTime;
};
@ -577,8 +581,10 @@ public:
void Message(char type, const char* message); // Send a message. Messages may simply flash an LED, or,
// say, display the messages on an LCD. This may also transmit the messages to the host.
void Message(char type, const StringRef& message) { return Message(type, message.Pointer()); }
void AppendMessage(char type, const char* message); // Send a message. Messages may simply flash an LED, or,
// say, display the messages on an LCD. This may also transmit the messages to the host.
void AppendMessage(char type, const StringRef& message) { return AppendMessage(type, message.Pointer()); }
void PushMessageIndent();
void PopMessageIndent();

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -192,7 +192,7 @@ void RepRap::Init()
coldExtrude = true; // DC42 changed default to true for compatibility because for now we are aiming for copatibility with RRP 0.78
active = true; // must do this before we start the network, else the watchdog may time out
snprintf(scratchString, STRING_LENGTH, "%s Version %s dated %s\n", NAME, VERSION, DATE);
scratchString.printf("%s Version %s dated %s\n", NAME, VERSION, DATE);
platform->Message(HOST_MESSAGE, scratchString);
platform->Message(HOST_MESSAGE, ".\n\nExecuting ");
@ -201,8 +201,8 @@ void RepRap::Init()
// We inject an M98 into the serial input stream to run the start-up macro
snprintf(scratchString, STRING_LENGTH, "M98 P%s\n", platform->GetConfigFile());
platform->GetLine()->InjectString(scratchString);
scratchString.printf("M98 P%s\n", platform->GetConfigFile());
platform->GetLine()->InjectString(scratchString.Pointer());
bool runningTheFile = false;
bool initialisingInProgress = true;
@ -228,7 +228,7 @@ void RepRap::Init()
network->Init(); // Need to do this here, as the configuration GCodes may set IP address etc.
platform->Message(HOST_MESSAGE, "\n");
snprintf(scratchString, STRING_LENGTH, "%s is up and running.\n", NAME);
scratchString.printf("%s is up and running.\n", NAME);
platform->Message(HOST_MESSAGE, scratchString);
fastLoop = FLT_MAX;
slowLoop = 0.0;
@ -295,7 +295,7 @@ void RepRap::Spin()
void RepRap::Timing()
{
snprintf(scratchString, STRING_LENGTH, "Slowest main loop (seconds): %f; fastest: %f\n", slowLoop, fastLoop);
scratchString.printf("Slowest main loop (seconds): %f; fastest: %f\n", slowLoop, fastLoop);
platform->AppendMessage(BOTH_MESSAGE, scratchString);
fastLoop = FLT_MAX;
slowLoop = 0.0;
@ -394,7 +394,7 @@ void RepRap::SelectTool(int toolNumber)
}
void RepRap::PrintTool(int toolNumber, char* reply) const
void RepRap::PrintTool(int toolNumber, StringRef& reply) const
{
for(Tool *tool = toolList; tool != NULL; tool = tool->next)
{
@ -404,7 +404,7 @@ void RepRap::PrintTool(int toolNumber, char* reply) const
return;
}
}
strcpy(reply, "Attempt to print details of non-existent tool.");
reply.copy("Attempt to print details of non-existent tool.");
}
void RepRap::StandbyTool(int toolNumber)
@ -425,7 +425,7 @@ void RepRap::StandbyTool(int toolNumber)
tool = tool->Next();
}
snprintf(scratchString, STRING_LENGTH, "Attempt to standby a non-existent tool: %d.\n", toolNumber);
scratchString.printf("Attempt to standby a non-existent tool: %d.\n", toolNumber);
platform->Message(HOST_MESSAGE, scratchString);
}
@ -456,7 +456,7 @@ void RepRap::SetToolVariables(int toolNumber, float* standbyTemperatures, float*
tool = tool->Next();
}
snprintf(scratchString, STRING_LENGTH, "Attempt to set variables for a non-existent tool: %d.\n", toolNumber);
scratchString.printf("Attempt to set variables for a non-existent tool: %d.\n", toolNumber);
platform->Message(HOST_MESSAGE, scratchString);
}
@ -490,39 +490,79 @@ void RepRap::Tick()
}
//*************************************************************************************************
// StringRef class member implementations
size_t StringRef::strlen() const
{
return strnlen(p, len - 1);
}
int StringRef::printf(const char *fmt, ...)
{
va_list vargs;
va_start(vargs, fmt);
int ret = vsnprintf(p, len, fmt, vargs);
va_end(vargs);
return ret;
}
int StringRef::vprintf(const char *fmt, va_list vargs)
{
return vsnprintf(p, len, fmt, vargs);
}
int StringRef::catf(const char *fmt, ...)
{
size_t n = strlen();
if (n + 1 < len) // if room for at least 1 more character and a null
{
va_list vargs;
va_start(vargs, fmt);
int ret = vsnprintf(p + n, len - n, fmt, vargs);
va_end(vargs);
return ret + n;
}
return 0;
}
// This is quicker than printf for printing constant strings
size_t StringRef::copy(const char* src)
{
size_t length = strnlen(src, len - 1);
memcpy(p, src, length);
p[length] = 0;
return length;
}
// This is quicker than catf for printing constant strings
size_t StringRef::cat(const char* src)
{
size_t length = strlen();
size_t toCopy = strnlen(src, len - length - 1);
memcpy(p + length, src, toCopy);
length += toCopy;
p[length] = 0;
return length;
}
//*************************************************************************************************
// Utilities and storage not part of any class
char scratchString[STRING_LENGTH];
static char scratchStringBuffer[STRING_LENGTH];
StringRef scratchString(scratchStringBuffer, ARRAY_SIZE(scratchStringBuffer));
// For debug use
void debugPrintf(const char* fmt, ...)
{
va_list p;
va_start(p, fmt);
vsnprintf(scratchString, ARRAY_SIZE(scratchString), fmt, p);
va_end(p);
scratchString[ARRAY_UPB(scratchString)] = 0;
va_list vargs;
va_start(vargs, fmt);
scratchString.vprintf(fmt, vargs);
va_end(vargs);
reprap.GetPlatform()->Message(DEBUG_MESSAGE, scratchString);
}
// This behaves like snprintf but appends to an existing string
// The second parameter is the length of the entire destination buffer, not the length remaining
int sncatf(char *dst, size_t len, const char* fmt, ...)
{
size_t n = strnlen(dst, len);
if (n + 1 < len) // if room for at least 1 more character and a null
{
va_list p;
va_start(p, fmt);
int ret = vsnprintf(dst + n, len - n, fmt, p);
va_end(p);
return ret + n;
}
return 0;
}
// String testing
bool StringEndsWith(const char* string, const char* ending)

View file

@ -47,8 +47,6 @@ extern RepRap reprap;
extern "C" void debugPrintf(const char* fmt, ...);
int sncatf(char *dst, size_t len, const char* fmt, ...);
bool StringEndsWith(const char* string, const char* ending);
bool StringStartsWith(const char* string, const char* starting);
bool StringEquals(const char* s1, const char* s2);
@ -59,7 +57,33 @@ int StringContains(const char* string, const char* match);
// 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[];
// Class to describe a string buffer, including its length. This saved passing buffer lengths around everywhere.
class StringRef
{
char *p; // pointer to the storage
size_t len; // number of characters in the storage
public:
StringRef(char *pp, size_t pl) : p(pp), len(pl) { }
size_t Length() const { return len; }
size_t strlen() const;
char *Pointer() { return p; }
const char *Pointer() const { return p; }
char& operator[](size_t index) { return p[index]; }
char operator[](size_t index) const { return p[index]; }
void Clear() { p[0] = 0; }
int printf(const char *fmt, ...);
int vprintf(const char *fmt, va_list vargs);
int catf(const char *fmt, ...);
size_t copy(const char* src);
size_t cat(const char *src);
};
extern StringRef scratchString;
#include "Arduino.h"
#include "Configuration.h"

View file

@ -44,7 +44,7 @@ class RepRap
void AllowColdExtrude();
void DenyColdExtrude();
bool ColdExtrude() const;
void PrintTool(int toolNumber, char* reply) const;
void PrintTool(int toolNumber, StringRef& reply) const;
void FlagTemperatureFault(int8_t dudHeater);
void ClearTemperatureFault(int8_t wasDudHeater);
Platform* GetPlatform() const;

View file

@ -1,4 +1,4 @@
; tfree1.g
; Put G/M Codes in here to run when Tool 1 is freed
G1 X1 Y5 F15000
G1 X1 Y5 F6000

View file

@ -1,4 +1,4 @@
; tfree2.g
; Put G/M Codes in here to run when Tool 2 is freed
G1 X1 Y5 F15000
G1 X1 Y5 F6000

View file

@ -75,9 +75,9 @@ Tool::Tool(int toolNumber, long d[], int dCount, long h[], int hCount)
}
}
void Tool::Print(char* reply)
void Tool::Print(StringRef& reply)
{
snprintf(reply, STRING_LENGTH, "Tool %d - drives: ", myNumber);
reply.printf("Tool %d - drives: ", myNumber);
char comma = ',';
for(int8_t drive = 0; drive < driveCount; drive++)
{
@ -85,10 +85,10 @@ void Tool::Print(char* reply)
{
comma = ';';
}
sncatf(reply, STRING_LENGTH, "%d%c", drives[drive], comma);
reply.catf("%d%c", drives[drive], comma);
}
sncatf(reply, STRING_LENGTH, "heaters (active/standby temps): ");
reply.cat("heaters (active/standby temps): ");
comma = ',';
for(int8_t heater = 0; heater < heaterCount; heater++)
{
@ -96,11 +96,11 @@ void Tool::Print(char* reply)
{
comma = ';';
}
sncatf(reply, STRING_LENGTH, "%d (%.1f/%.1f)%c", heaters[heater],
reply.catf("%d (%.1f/%.1f)%c", heaters[heater],
activeTemperatures[heater], standbyTemperatures[heater], comma);
}
sncatf(reply, STRING_LENGTH, " status: %s", active ? "selected" : "standby");
reply.catf(" status: %s", active ? "selected" : "standby");
}
float Tool::MaxFeedrate() const

2
Tool.h
View file

@ -47,7 +47,7 @@ public:
bool Mixing() const;
float MaxFeedrate() const;
float InstantDv() const;
void Print(char* reply);
void Print(StringRef& reply);
friend class RepRap;

View file

@ -101,7 +101,8 @@ const float pasvPortTimeout = 10.0; // seconds to wait for the FTP data po
// Constructor and initialisation
Webserver::Webserver(Platform* p) : platform(p), webserverActive(false), readingConnection(NULL),
fileInfoDetected(false), filamentCount(DRIVES - AXES), printStartTime(0.0)
fileInfoDetected(false), printStartTime(0.0),
gcodeReply(gcodeReplyBuffer, ARRAY_SIZE(gcodeReplyBuffer))
{
httpInterpreter = new HttpInterpreter(p, this);
ftpInterpreter = new FtpInterpreter(p, this);
@ -319,7 +320,7 @@ void Webserver::ProcessGcode(const char* gc)
}
else if (StringStartsWith(gc, "M23 ")) // select SD card file to print next
{
fileInfoDetected = GetFileInfo(platform->GetGCodeDir(), &gc[4], length, height, filament, filamentCount, layerHeight, generatedBy, ARRAY_SIZE(generatedBy));
fileInfoDetected = GetFileInfo(platform->GetGCodeDir(), &gc[4], currentFileInfo);
printStartTime = platform->Time();
strncpy(fileBeingPrinted, &gc[4], ARRAY_SIZE(fileBeingPrinted));
reprap.GetGCodes()->QueueFileToPrint(&gc[4]);
@ -354,7 +355,7 @@ void Webserver::ProcessGcode(const char* gc)
gcodeReply[i] = 0;
httpInterpreter->ReceivedGcodeReply();
telnetInterpreter->HandleGcodeReply(gcodeReply);
telnetInterpreter->HandleGcodeReply(gcodeReply.Pointer());
}
}
else if (StringStartsWith(gc, "M25") && !isDigit(gc[3])) // pause SD card print
@ -511,17 +512,17 @@ void Webserver::MessageStringToWebInterface(const char *s, bool error)
if (strlen(s) == 0 && !error)
{
strcpy(gcodeReply, "ok");
gcodeReply.copy("ok");
}
else
{
if (error)
{
snprintf(gcodeReply, ARRAY_SIZE(gcodeReply), "Error: %s", s);
gcodeReply.printf("Error: %s", s);
}
else
{
snprintf(gcodeReply, ARRAY_SIZE(gcodeReply), "%s", s);
gcodeReply.printf("%s", s);
}
}
@ -536,7 +537,7 @@ void Webserver::AppendReplyToWebInterface(const char *s, bool error)
return;
}
sncatf(gcodeReply, ARRAY_SIZE(gcodeReply), "%s", s);
gcodeReply.cat(s);
httpInterpreter->ReceivedGcodeReply();
telnetInterpreter->HandleGcodeReply(s);
}
@ -762,14 +763,36 @@ void Webserver::HttpInterpreter::SendJsonResponse(const char* command)
RequestState *req = net->GetRequest();
bool keepOpen = false;
bool mayKeepOpen;
bool found;
char jsonResponseBuffer[jsonReplyLength];
StringRef jsonResponse(jsonResponseBuffer, ARRAY_SIZE(jsonResponseBuffer));
if (numQualKeys == 0)
{
mayKeepOpen = GetJsonResponse(command, "", "", 0);
found = GetJsonResponse(command, jsonResponse, "", "", 0, mayKeepOpen);
}
else
{
mayKeepOpen = GetJsonResponse(command, qualifiers[0].key, qualifiers[0].value, qualifiers[1].key - qualifiers[0].value - 1);
found = GetJsonResponse(command, jsonResponse, qualifiers[0].key, qualifiers[0].value, qualifiers[1].key - qualifiers[0].value - 1, mayKeepOpen);
}
if (found)
{
jsonResponseBuffer[ARRAY_UPB(jsonResponseBuffer)] = 0;
if (webDebug)
{
platform->Message(HOST_MESSAGE, "JSON response: ");
platform->Message(HOST_MESSAGE, jsonResponseBuffer);
platform->Message(HOST_MESSAGE, " queued\n");
}
}
else
{
jsonResponseBuffer[0] = 0;
platform->Message(HOST_MESSAGE, "KnockOut request: ");
platform->Message(HOST_MESSAGE, command);
platform->Message(HOST_MESSAGE, " not recognised\n");
}
if (mayKeepOpen)
{
// Check that the browser wants to persist the connection too
@ -785,9 +808,9 @@ void Webserver::HttpInterpreter::SendJsonResponse(const char* command)
}
req->Write("HTTP/1.1 200 OK\n");
req->Write("Content-Type: application/json\n");
req->Printf("Content-Length: %u\n", strlen(jsonResponse));
req->Printf("Content-Length: %u\n", jsonResponse.strlen());
req->Printf("Connection: %s\n\n", keepOpen ? "keep-alive" : "close");
req->Write(jsonResponse);
req->Write(jsonResponse.Pointer());
net->SendAndClose(NULL, keepOpen);
}
@ -795,58 +818,37 @@ void Webserver::HttpInterpreter::SendJsonResponse(const char* command)
// Input from the client
void Webserver::HttpInterpreter::JsonReport(bool ok, const char* request)
{
if (ok)
{
jsonResponse[ARRAY_UPB(jsonResponse)] = 0;
if (webDebug)
{
platform->Message(HOST_MESSAGE, "JSON response: ");
platform->Message(HOST_MESSAGE, jsonResponse);
platform->Message(HOST_MESSAGE, " queued\n");
}
}
else
{
jsonResponse[0] = 0;
platform->Message(HOST_MESSAGE, "KnockOut request: ");
platform->Message(HOST_MESSAGE, request);
platform->Message(HOST_MESSAGE, " not recognised\n");
}
}
// Get the Json response for this command.
// 'value' is null-terminated, but we also pass its length in case it contains embedded nulls, which matter when uploading files.
bool Webserver::HttpInterpreter::GetJsonResponse(const char* request, const char* key, const char* value, size_t valueLength)
bool Webserver::HttpInterpreter::GetJsonResponse(const char* request, StringRef& response, const char* key, const char* value, size_t valueLength, bool& keepOpen)
{
bool found = true; // assume success
bool keepOpen = false; // assume we don't want to persist the connection
keepOpen = false; // assume we don't want to persist the connection
if (StringEquals(request, "status")) // new style status request
{
GetStatusResponse(1);
GetStatusResponse(response, 1);
}
else if (StringEquals(request, "poll")) // old style status request
{
GetStatusResponse(0);
GetStatusResponse(response, 0);
}
else if (StringEquals(request, "gcode") && StringEquals(key, "gcode"))
{
webserver->LoadGcodeBuffer(value);
snprintf(jsonResponse, ARRAY_SIZE(jsonResponse), "{\"buff\":%u}", webserver->GetGcodeBufferSpace());
response.printf("{\"buff\":%u}", webserver->GetGcodeBufferSpace());
}
else if (StringEquals(request, "upload_begin") && StringEquals(key, "name"))
{
FileStore *file = platform->GetFileStore("0:/", value, true);
StartUpload(file);
GetJsonUploadResponse();
GetJsonUploadResponse(response);
}
else if (StringEquals(request, "upload_data") && StringEquals(key, "data"))
{
StoreUploadData(value, valueLength);
GetJsonUploadResponse();
GetJsonUploadResponse(response);
keepOpen = true;
}
else if (StringEquals(request, "upload_end") && StringEquals(key, "size"))
@ -854,17 +856,17 @@ bool Webserver::HttpInterpreter::GetJsonResponse(const char* request, const char
long file_length = strtoul(value, NULL, 10);
FinishUpload(file_length);
GetJsonUploadResponse();
GetJsonUploadResponse(response);
}
else if (StringEquals(request, "upload_cancel"))
{
CancelUpload();
snprintf(jsonResponse, ARRAY_SIZE(jsonResponse), "{\"err\":%d}", 0);
response.printf("{\"err\":%d}", 0);
}
else if (StringEquals(request, "delete") && StringEquals(key, "name"))
{
bool ok = platform->GetMassStorage()->Delete("0:/", value);
snprintf(jsonResponse, ARRAY_SIZE(jsonResponse), "{\"err\":%d}", (ok) ? 0 : 1);
response.printf("{\"err\":%d}", (ok) ? 0 : 1);
}
else if (StringEquals(request, "files"))
{
@ -873,19 +875,20 @@ bool Webserver::HttpInterpreter::GetJsonResponse(const char* request, const char
FileInfo file_info;
if (platform->GetMassStorage()->FindFirst(dir, file_info))
{
strcpy(jsonResponse, "{\"files\":[");
response.copy("{\"files\":[");
do {
// build the file list here, but keep 2 characters free to terminate the JSON message
sncatf(jsonResponse, ARRAY_SIZE(jsonResponse) - 2, "%c%s%c%c", FILE_LIST_BRACKET, file_info.fileName, FILE_LIST_BRACKET, FILE_LIST_SEPARATOR);
response.catf("%c%s%c%c", FILE_LIST_BRACKET, file_info.fileName, FILE_LIST_BRACKET, FILE_LIST_SEPARATOR);
} while (platform->GetMassStorage()->FindNext(file_info));
jsonResponse[strlen(jsonResponse) -1] = 0;
sncatf(jsonResponse, ARRAY_SIZE(jsonResponse), "]}");
response[response.strlen() - 1] = 0; // remove last separator
response[response.Length() - 3] = 0; // ensure room for 2 more characters and a null
response.cat("]}");
}
else
{
strcpy(jsonResponse, "{\"files\":[NONE]}");
response.copy("{\"files\":[NONE]}");
}
}
else if (StringEquals(request, "fileinfo"))
@ -893,68 +896,62 @@ bool Webserver::HttpInterpreter::GetJsonResponse(const char* request, const char
// Poll file info for a specific file
if (StringEquals(key, "name"))
{
unsigned long length;
float height, filament[DRIVES - AXES], layerHeight;
unsigned int numFilaments = DRIVES - AXES;
char generatedBy[50];
bool found = webserver->GetFileInfo("0:/", value, length, height, filament, numFilaments, layerHeight, generatedBy, ARRAY_SIZE(generatedBy));
GcodeFileInfo info;
bool found = webserver->GetFileInfo("0:/", value, info);
if (found)
{
snprintf(jsonResponse, ARRAY_SIZE(jsonResponse),
"{\"err\":0,\"size\":%lu,\"height\":%.2f,\"layerHeight\":%.2f,\"filament\":",
length, height, layerHeight);
response.printf("{\"err\":0,\"size\":%lu,\"height\":%.2f,\"layerHeight\":%.2f,\"filament\":",
info.fileSize, info.objectHeight, info.layerHeight);
char ch = '[';
if (numFilaments == 0)
if (info.numFilaments == 0)
{
sncatf(jsonResponse, ARRAY_SIZE(jsonResponse), "%c", ch);
response.catf("%c", ch);
}
else
{
for (unsigned int i = 0; i < numFilaments; ++i)
for (unsigned int i = 0; i < info.numFilaments; ++i)
{
sncatf(jsonResponse, ARRAY_SIZE(jsonResponse), "%c%.1f", ch, filament[i]);
response.catf("%c%.1f", ch, info.filamentNeeded[i]);
ch = ',';
}
}
sncatf(jsonResponse, ARRAY_SIZE(jsonResponse), "],\"generatedBy\":\"%s\"}", generatedBy);
response.catf("],\"generatedBy\":\"%s\"}", info.generatedBy);
}
else
{
snprintf(jsonResponse, ARRAY_SIZE(jsonResponse), "{\"err\":1}");
response.printf("{\"err\":1}");
}
}
else if (reprap.GetGCodes()->PrintingAFile() && webserver->fileInfoDetected)
{
// Poll file info about a file currently being printed
snprintf(jsonResponse, ARRAY_SIZE(jsonResponse),
"{\"err\":0,\"size\":%lu,\"height\":%.2f,\"layerHeight\":%.2f,\"filament\":",
webserver->length, webserver->height, webserver->layerHeight);
response.printf("{\"err\":0,\"size\":%lu,\"height\":%.2f,\"layerHeight\":%.2f,\"filament\":",
webserver->currentFileInfo.fileSize, webserver->currentFileInfo.objectHeight, webserver->currentFileInfo.layerHeight);
char ch = '[';
if (webserver->filamentCount == 0)
if (webserver->currentFileInfo.numFilaments == 0)
{
sncatf(jsonResponse, ARRAY_SIZE(jsonResponse), "%c", ch);
response.catf("%c", ch);
}
else
{
for (unsigned int i = 0; i < webserver->filamentCount; ++i)
for (unsigned int i = 0; i < webserver->currentFileInfo.numFilaments; ++i)
{
sncatf(jsonResponse, ARRAY_SIZE(jsonResponse), "%c%.1f", ch, webserver->filament[i]);
response.catf("%c%.1f", ch, webserver->currentFileInfo.filamentNeeded[i]);
ch = ',';
}
}
sncatf(jsonResponse, ARRAY_SIZE(jsonResponse), "],\"generatedBy\":\"%s\",\"printDuration\":%d,\"fileName\":\"%s\"}",
webserver->generatedBy, (int)((platform->Time() - webserver->printStartTime) * 1000.0), webserver->fileBeingPrinted);
response.catf("],\"generatedBy\":\"%s\",\"printDuration\":%d,\"fileName\":\"%s\"}",
webserver->currentFileInfo.generatedBy, (int)((platform->Time() - webserver->printStartTime) * 1000.0), webserver->fileBeingPrinted);
}
else
{
snprintf(jsonResponse, ARRAY_SIZE(jsonResponse), "{\"err\":1}");
response.copy("{\"err\":1}");
}
}
else if (StringEquals(request, "name"))
{
snprintf(jsonResponse, ARRAY_SIZE(jsonResponse), "{\"myName\":\"");
size_t j = strlen(jsonResponse);
response.printf("{\"myName\":\"");
size_t j = response.strlen();
const char *myName = webserver->GetName();
for (size_t i = 0; i < SHORT_STRING_LENGTH; ++i)
{
@ -964,50 +961,49 @@ bool Webserver::HttpInterpreter::GetJsonResponse(const char* request, const char
if (c == '"' || c == '\\')
{
// Need to escape the quote-mark or backslash for JSON
jsonResponse[j++] = '\\';
response[j++] = '\\';
}
jsonResponse[j++] = c;
response[j++] = c;
}
jsonResponse[j++] = '"';
jsonResponse[j++] = '}';
jsonResponse[j] = 0;
response[j++] = '"';
response[j++] = '}';
response[j] = 0;
}
else if (StringEquals(request, "password") && StringEquals(key, "password"))
{
gotPassword = webserver->CheckPassword(value);
snprintf(jsonResponse, ARRAY_SIZE(jsonResponse), "{\"password\":\"%s\"}", (gotPassword) ? "right" : "wrong");
response.printf("{\"password\":\"%s\"}", (gotPassword) ? "right" : "wrong");
}
else if (StringEquals(request, "axes"))
{
strcpy(jsonResponse, "{\"axes\":");
response.copy("{\"axes\":");
char ch = '[';
for (int8_t drive = 0; drive < AXES; drive++)
{
sncatf(jsonResponse, ARRAY_SIZE(jsonResponse) - 2, "%c%.1f", ch, platform->AxisTotalLength(drive));
response.catf("%c%.1f", ch, platform->AxisTotalLength(drive));
ch = ',';
}
sncatf(jsonResponse, ARRAY_SIZE(jsonResponse), "]}");
response.cat("]}");
}
else if (StringEquals(request, "connect"))
{
CancelUpload();
GetStatusResponse(1);
GetStatusResponse(response, 1);
}
else
{
found = false;
}
JsonReport(found, request);
return keepOpen;
return found;
}
void Webserver::HttpInterpreter::GetJsonUploadResponse()
void Webserver::HttpInterpreter::GetJsonUploadResponse(StringRef& response)
{
snprintf(jsonResponse, ARRAY_SIZE(jsonResponse), "{\"ubuff\":%u,\"err\":%d}", webUploadBufferSize, (uploadState == uploadOK) ? 0 : 1);
response.printf("{\"ubuff\":%u,\"err\":%d}", webUploadBufferSize, (uploadState == uploadOK) ? 0 : 1);
}
void Webserver::HttpInterpreter::GetStatusResponse(uint8_t type)
void Webserver::HttpInterpreter::GetStatusResponse(StringRef& response, uint8_t type)
{
GCodes *gc = reprap.GetGCodes();
if (type == 1)
@ -1015,78 +1011,78 @@ void Webserver::HttpInterpreter::GetStatusResponse(uint8_t type)
// New-style status request
// Send the printing/idle status
char ch = (reprap.IsStopped()) ? 'S' : (gc->PrintingAFile()) ? 'P' : 'I';
snprintf(jsonResponse, ARRAY_SIZE(jsonResponse), "{\"status\":\"%c\",\"heaters\":", ch);
response.printf("{\"status\":\"%c\",\"heaters\":", ch);
// Send the heater actual temperatures
const Heat *heat = reprap.GetHeat();
ch = '[';
for (int8_t heater = 0; heater < reprap.GetHeatersInUse(); heater++)
{
sncatf(jsonResponse, ARRAY_SIZE(jsonResponse), "%c%.1f", ch, heat->GetTemperature(heater));
response.catf("%c%.1f", ch, heat->GetTemperature(heater));
ch = ',';
}
// Send the heater active temperatures
sncatf(jsonResponse, ARRAY_SIZE(jsonResponse), "],\"active\":");
response.catf("],\"active\":");
ch = '[';
for (int8_t heater = 0; heater < reprap.GetHeatersInUse(); heater++)
{
sncatf(jsonResponse, ARRAY_SIZE(jsonResponse), "%c%.1f", ch, heat->GetActiveTemperature(heater));
response.catf("%c%.1f", ch, heat->GetActiveTemperature(heater));
ch = ',';
}
// Send the heater standby temperatures
sncatf(jsonResponse, ARRAY_SIZE(jsonResponse), "],\"standby\":");
response.catf("],\"standby\":");
ch = '[';
for (int8_t heater = 0; heater < reprap.GetHeatersInUse(); heater++)
{
sncatf(jsonResponse, ARRAY_SIZE(jsonResponse), "%c%.1f", ch, heat->GetStandbyTemperature(heater));
response.catf("%c%.1f", ch, heat->GetStandbyTemperature(heater));
ch = ',';
}
// Send the heater statuses (0=off, 1=standby, 2=active)
sncatf(jsonResponse, ARRAY_SIZE(jsonResponse), "],\"hstat\":");
response.cat("],\"hstat\":");
ch = '[';
for (int8_t heater = 0; heater < reprap.GetHeatersInUse(); heater++)
{
sncatf(jsonResponse, ARRAY_SIZE(jsonResponse), "%c%d", ch, (int)heat->GetStatus(heater));
response.catf("%c%d", ch, (int)heat->GetStatus(heater));
ch = ',';
}
// Send XYZ positions
float liveCoordinates[DRIVES + 1];
reprap.GetMove()->LiveCoordinates(liveCoordinates);
sncatf(jsonResponse, ARRAY_SIZE(jsonResponse), "],\"pos\":"); // announce the XYZ position
response.catf("],\"pos\":"); // announce the XYZ position
ch = '[';
for (int8_t drive = 0; drive < AXES; drive++)
{
sncatf(jsonResponse, ARRAY_SIZE(jsonResponse), "%c%.2f", ch, liveCoordinates[drive]);
response.catf("%c%.2f", ch, liveCoordinates[drive]);
ch = ',';
}
// Send extruder total extrusion since power up, last G92 or last M23
sncatf(jsonResponse, ARRAY_SIZE(jsonResponse), "],\"extr\":"); // announce the extruder positions
response.catf("],\"extr\":"); // announce the extruder positions
ch = '[';
for (int8_t drive = 0; drive < reprap.GetExtrudersInUse(); drive++) // loop through extruders
{
sncatf(jsonResponse, ARRAY_SIZE(jsonResponse), "%c%.1f", ch, gc->GetExtruderPosition(drive));
response.catf("%c%.1f", ch, gc->GetExtruderPosition(drive));
ch = ',';
}
sncatf(jsonResponse, ARRAY_SIZE(jsonResponse), "]");
response.cat("]");
// Send the speed and extruder override factors
sncatf(jsonResponse, ARRAY_SIZE(jsonResponse), ",\"sfactor\":%.2f,\"efactor:\":", gc->GetSpeedFactor() * 100.0);
response.catf(",\"sfactor\":%.2f,\"efactor:\":", gc->GetSpeedFactor() * 100.0);
const float *extrusionFactors = gc->GetExtrusionFactors();
for (unsigned int i = 0; i < reprap.GetExtrudersInUse(); ++i)
{
sncatf(jsonResponse, ARRAY_SIZE(jsonResponse), "%c%.2f", (i == 0) ? '[' : ',', extrusionFactors[i] * 100.0);
response.catf("%c%.2f", (i == 0) ? '[' : ',', extrusionFactors[i] * 100.0);
}
sncatf(jsonResponse, ARRAY_SIZE(jsonResponse), "]");
response.cat("]");
// Send the current tool number
Tool* currentTool = reprap.GetCurrentTool();
int toolNumber = (currentTool == NULL) ? 0 : currentTool->Number();
sncatf(jsonResponse, ARRAY_SIZE(jsonResponse), ",\"tool\":%d", toolNumber);
response.catf(",\"tool\":%d", toolNumber);
}
else
{
@ -1095,10 +1091,10 @@ void Webserver::HttpInterpreter::GetStatusResponse(uint8_t type)
// 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.
// RRP reversed the order at version 0.65 to send the positions before the heaters, but we haven't yet done that.
char c = (gc->PrintingAFile()) ? 'P' : 'I';
snprintf(jsonResponse, ARRAY_SIZE(jsonResponse), "{\"poll\":[\"%c\",", c); // Printing
response.printf("{\"poll\":[\"%c\",", c); // Printing
for (int8_t heater = 0; heater < HEATERS; heater++)
{
sncatf(jsonResponse, ARRAY_SIZE(jsonResponse), "\"%.1f\",", reprap.GetHeat()->GetTemperature(heater));
response.catf("\"%.1f\",", reprap.GetHeat()->GetTemperature(heater));
}
// Send XYZ and extruder positions
float liveCoordinates[DRIVES + 1];
@ -1106,7 +1102,7 @@ void Webserver::HttpInterpreter::GetStatusResponse(uint8_t type)
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
sncatf(jsonResponse, ARRAY_SIZE(jsonResponse), "\"%.2f\"%c", liveCoordinates[drive], ch);
response.catf("\"%.2f\"%c", liveCoordinates[drive], ch);
}
}
@ -1116,45 +1112,45 @@ void Webserver::HttpInterpreter::GetStatusResponse(uint8_t type)
switch (platform->GetZProbeSecondaryValues(v1, v2))
{
case 1:
sncatf(jsonResponse, ARRAY_SIZE(jsonResponse), ",\"probe\":\"%d (%d)\"", v0, v1);
response.catf(",\"probe\":\"%d (%d)\"", v0, v1);
break;
case 2:
sncatf(jsonResponse, ARRAY_SIZE(jsonResponse), ",\"probe\":\"%d (%d, %d)\"", v0, v1, v2);
response.catf(",\"probe\":\"%d (%d, %d)\"", v0, v1, v2);
break;
default:
sncatf(jsonResponse, ARRAY_SIZE(jsonResponse), ",\"probe\":\"%d\"", v0);
response.catf(",\"probe\":\"%d\"", v0);
break;
}
// Send the amount of buffer space available for gcodes
sncatf(jsonResponse, ARRAY_SIZE(jsonResponse), ",\"buff\":%u", webserver->GetGcodeBufferSpace());
response.catf(",\"buff\":%u", webserver->GetGcodeBufferSpace());
// 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)
{
sncatf(jsonResponse, ARRAY_SIZE(jsonResponse), ",\"homed\":[%d,%d,%d]",
response.catf(",\"homed\":[%d,%d,%d]",
(gc->GetAxisIsHomed(0)) ? 1 : 0,
(gc->GetAxisIsHomed(1)) ? 1 : 0,
(gc->GetAxisIsHomed(2)) ? 1 : 0);
}
else
{
sncatf(jsonResponse, ARRAY_SIZE(jsonResponse), ",\"hx\":%d,\"hy\":%d,\"hz\":%d",
response.catf(",\"hx\":%d,\"hy\":%d,\"hz\":%d",
(gc->GetAxisIsHomed(0)) ? 1 : 0,
(gc->GetAxisIsHomed(1)) ? 1 : 0,
(gc->GetAxisIsHomed(2)) ? 1 : 0);
}
// Retrieve the gcode buffer from Webserver
const char *p = webserver->gcodeReply;
const char *p = webserver->gcodeReply.Pointer();
// Send the response sequence number
sncatf(jsonResponse, ARRAY_SIZE(jsonResponse), ",\"seq\":%u", (unsigned int) seq);
response.catf(",\"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.
sncatf(jsonResponse, ARRAY_SIZE(jsonResponse), ",\"resp\":\"");
size_t jp = strnlen(jsonResponse, ARRAY_SIZE(jsonResponse));
while (*p != 0 && jp < ARRAY_SIZE(jsonResponse) - 3) // leave room for the final '"}\0'
response.cat(",\"resp\":\"");
size_t jp = response.strlen();
while (*p != 0 && jp < response.Length() - 3) // leave room for the final '"}\0'
{
char c = *p++;
char esc;
@ -1181,20 +1177,20 @@ void Webserver::HttpInterpreter::GetStatusResponse(uint8_t type)
}
if (esc)
{
if (jp == ARRAY_SIZE(jsonResponse) - 4)
if (jp == response.Length() - 4)
{
break;
}
jsonResponse[jp++] = '\\';
jsonResponse[jp++] = esc;
response[jp++] = '\\';
response[jp++] = esc;
}
else
{
jsonResponse[jp++] = c;
response[jp++] = c;
}
}
jsonResponse[jp] = 0;
sncatf(jsonResponse, ARRAY_SIZE(jsonResponse), "\"}");
response[jp] = 0;
response.cat("\"}");
}
void Webserver::HttpInterpreter::ResetState()
@ -1713,7 +1709,7 @@ bool Webserver::FtpInterpreter::CharFromClient(char c)
if (DebugEnabled())
{
snprintf(scratchString, STRING_LENGTH, "FtpInterpreter::ProcessLine called with state %d:\n%s\n", state, clientMessage);
scratchString.printf("FtpInterpreter::ProcessLine called with state %d:\n%s\n", state, clientMessage);
platform->Message(DEBUG_MESSAGE, scratchString);
}
@ -2271,9 +2267,9 @@ void Webserver::FtpInterpreter::ChangeDirectory(const char *newDirectory)
strncpy(combinedPath, currentDir, maxFilenameLength);
if (strlen(currentDir) > 1)
{
sncatf(combinedPath, maxFilenameLength, "/");
strncat(combinedPath, "/", maxFilenameLength - strlen(combinedPath) - 1);
}
sncatf(combinedPath, maxFilenameLength, "%s", newDirectory);
strncat(combinedPath, newDirectory, maxFilenameLength - strlen(combinedPath) - 1);
}
}
@ -2454,20 +2450,19 @@ void Webserver::TelnetInterpreter::HandleGcodeReply(const char *reply)
//********************************************************************************************
// Get information for a file on the SD card
bool Webserver::GetFileInfo(const char *directory, const char *fileName, unsigned long& length, float& height,
float *filamentUsed, unsigned int& numFilaments, float& layerHeight, char* generatedBy, size_t generatedByLength)
bool Webserver::GetFileInfo(const char *directory, const char *fileName, GcodeFileInfo& info)
{
FileStore *f = platform->GetFileStore(directory, fileName, false);
FileStore *f = reprap.GetPlatform()->GetFileStore(directory, fileName, false);
if (f != NULL)
{
// Try to find the object height by looking for the last G1 Zxxx command in the file
length = f->Length();
height = 0.0;
layerHeight = 0.0;
numFilaments = DRIVES - AXES;
generatedBy[0] = 0;
info.fileSize = f->Length();
info.objectHeight = 0.0;
info.layerHeight = 0.0;
info.numFilaments = DRIVES - AXES;
info.generatedBy[0] = 0;
if (length != 0 && (StringEndsWith(fileName, ".gcode") || StringEndsWith(fileName, ".g") || StringEndsWith(fileName, ".gco") || StringEndsWith(fileName, ".gc")))
if (info.fileSize != 0 && (StringEndsWith(fileName, ".gcode") || StringEndsWith(fileName, ".g") || StringEndsWith(fileName, ".gco") || StringEndsWith(fileName, ".gc")))
{
const size_t readSize = 512; // read 512 bytes at a time (1K doesn't seem to work when we read from the end)
const size_t overlap = 100;
@ -2476,37 +2471,37 @@ bool Webserver::GetFileInfo(const char *directory, const char *fileName, unsigne
// Get slic3r settings by reading from the start of the file. We only read the first 1K or so, everything we are looking for should be there.
{
size_t sizeToRead = (size_t)min<unsigned long>(length, readSize + overlap);
size_t sizeToRead = (size_t)min<unsigned long>(info.fileSize, readSize + overlap);
int nbytes = f->Read(buf, sizeToRead);
if (nbytes == (int)sizeToRead)
{
buf[sizeToRead] = 0;
// Look for layer height
foundLayerHeight = FindLayerHeight(buf, sizeToRead, layerHeight);
foundLayerHeight = FindLayerHeight(buf, sizeToRead, info.layerHeight);
const char* generatedByString = "; generated by ";
const char* pos = strstr(buf, generatedByString);
size_t generatedByLength = ARRAY_SIZE(info.generatedBy);
if (pos != NULL)
{
pos += strlen(generatedByString);
while (generatedByLength > 1 && *pos >= ' ')
size_t i = 0;
while (i < ARRAY_SIZE(info.generatedBy) - 1 && *pos >= ' ')
{
char c = *pos++;
if (c == '"' || c == '\\')
{
// Need to escape the quote-mark for JSON
if (generatedByLength < 3)
if ( i > ARRAY_SIZE(info.generatedBy) - 3)
{
break;
}
*generatedBy++ = '\\';
--generatedByLength;
info.generatedBy[i++] = '\\';
}
*generatedBy++ = c;
--generatedByLength;
info.generatedBy[i++] = c;
}
*generatedBy = 0;
info.generatedBy[i] = 0;
}
// Add code to look for other values here...
@ -2516,19 +2511,19 @@ bool Webserver::GetFileInfo(const char *directory, const char *fileName, unsigne
// Now get the object height and filament used by reading the end of the file
{
size_t sizeToRead;
if (length <= readSize + overlap)
if (info.fileSize <= readSize + overlap)
{
sizeToRead = length; // read the whole file in one go
sizeToRead = info.fileSize; // read the whole file in one go
}
else
{
sizeToRead = length % readSize;
sizeToRead = info.fileSize % readSize;
if (sizeToRead <= overlap)
{
sizeToRead += readSize;
}
}
unsigned long seekPos = length - sizeToRead; // read on a 512b boundary
unsigned long seekPos = info.fileSize - sizeToRead; // read on a 512b boundary
size_t sizeToScan = sizeToRead;
unsigned int filamentsFound = 0;
for (;;)
@ -2549,25 +2544,25 @@ bool Webserver::GetFileInfo(const char *directory, const char *fileName, unsigne
unsigned int nFilaments = FindFilamentUsed(buf, sizeToScan, filaments, DRIVES - AXES);
if (nFilaments != 0 && nFilaments >= filamentsFound)
{
filamentsFound = min<unsigned int>(nFilaments, numFilaments);
filamentsFound = min<unsigned int>(nFilaments, info.numFilaments);
for (unsigned int i = 0; i < filamentsFound; ++i)
{
filamentUsed[i] = filaments[i];
info.filamentNeeded[i] = filaments[i];
}
}
// Search for layer height
if (!foundLayerHeight)
{
foundLayerHeight = FindLayerHeight(buf, sizeToScan, layerHeight);
foundLayerHeight = FindLayerHeight(buf, sizeToScan, info.layerHeight);
}
// Search for object height
if (FindHeight(buf, sizeToScan, height))
if (FindHeight(buf, sizeToScan, info.objectHeight))
{
break; // quit if found height
}
if (seekPos == 0 || length - seekPos >= 200000uL) // scan up to about the last 200K of the file (32K wasn't enough)
if (seekPos == 0 || info.fileSize - seekPos >= 200000uL) // scan up to about the last 200K of the file (32K wasn't enough)
{
break; // quit if reached start of file or already scanned the last 32K of the file
}
@ -2576,7 +2571,7 @@ bool Webserver::GetFileInfo(const char *directory, const char *fileName, unsigne
sizeToScan = readSize + overlap;
memcpy(buf + sizeToRead, buf, overlap);
}
numFilaments = filamentsFound;
info.numFilaments = filamentsFound;
}
}
f->Close();

View file

@ -62,6 +62,18 @@ const unsigned int telnetMessageLength = 256; // maximum line length for incomin
class Webserver;
// Class to hold Gcode file information
class GcodeFileInfo
{
public:
unsigned long fileSize;
float objectHeight;
float filamentNeeded[DRIVES - AXES];
unsigned int numFilaments;
float layerHeight;
char generatedBy[50];
};
// This is the abstract class for all supported protocols
// Any inherited class should implement a state machine to increase performance and reduce memory usage.
class ProtocolInterpreter
@ -135,10 +147,8 @@ class Webserver
// File information about the file being printed
bool fileInfoDetected;
unsigned long length;
float height, filament[DRIVES - AXES], layerHeight;
unsigned int filamentCount;
char generatedBy[50], fileBeingPrinted[255];
char fileBeingPrinted[255];
GcodeFileInfo currentFileInfo;
float printStartTime;
void MessageStringToWebInterface(const char *s, bool error);
@ -189,12 +199,11 @@ class Webserver
void SendFile(const char* nameOfFileToSend);
void SendJsonResponse(const char* command);
bool GetJsonResponse(const char* request, const char* key, const char* value, size_t valueLength);
void GetJsonUploadResponse();
void GetStatusResponse(uint8_t type);
bool GetJsonResponse(const char* request, StringRef& response, const char* key, const char* value, size_t valueLength, bool& keepOpen);
void GetJsonUploadResponse(StringRef& response);
void GetStatusResponse(StringRef& response, uint8_t type);
bool ProcessMessage();
bool RejectMessage(const char* s, unsigned int code = 500);
void JsonReport(bool ok, const char* request);
HttpState state;
@ -220,7 +229,6 @@ class Webserver
unsigned int numHeaderKeys; // number of keys we have found, <= maxHeaders
// Buffers to hold reply
char jsonResponse[jsonReplyLength];
char decodeChar;
uint16_t seq; // reply sequence number, so that the client can tell if a json reply is new or not
bool webDebug;
@ -277,7 +285,7 @@ class Webserver
bool CharFromClient(const char c);
void ResetState();
void HandleGcodeReply(const char *reply);
void HandleGcodeReply(const char* reply);
private:
@ -302,8 +310,7 @@ class Webserver
void StoreGcodeData(const char* data, size_t len);
// File info methods
bool GetFileInfo(const char *directory, const char *fileName, unsigned long& length, float& height, float *filamentUsed,
unsigned int& numFilaments, float& layerHeight, char* generatedBy, size_t generatedByLength);
static bool GetFileInfo(const char *directory, const char *fileName, GcodeFileInfo& info);
static bool FindHeight(const char* buf, size_t len, float& height);
static bool FindLayerHeight(const char* buf, size_t len, float& layerHeight);
static unsigned int FindFilamentUsed(const char* buf, size_t len, float *filamentUsed, unsigned int maxFilaments);
@ -312,7 +319,8 @@ class Webserver
// Buffer to hold gcode that is ready for processing
char gcodeBuffer[gcodeBufLength];
unsigned int gcodeReadIndex, gcodeWriteIndex; // head and tail indices into gcodeBuffer
char gcodeReply[2048];
char gcodeReplyBuffer[2048];
StringRef gcodeReply;
// Misc
char password[SHORT_STRING_LENGTH + 1];