diff options
author | David Crocker <dcrocker@eschertech.com> | 2016-05-10 14:51:48 +0300 |
---|---|---|
committer | David Crocker <dcrocker@eschertech.com> | 2016-05-10 14:52:19 +0300 |
commit | 1b2be24ead88cb0df4b871d2ec5a6d39445fd2d7 (patch) | |
tree | 521fdd5c35d6659e9c96dec01ac44e394958b82b /src | |
parent | 55b28891271c3fc2a7e174cbbfb08888fa65760d (diff) |
Version 1.12a
1. Changed thermocouple/RTD support to use new (fixed) version of
SharedSpi in CoreNG
2. PrintMonitor fixes:
- Speed up getting file info, because it was slow enough to cause Ajax
timeouts on some systems
- Reduce the number and volume of messages printed when debugging was
enabled, and send them only to the USB interface, because it was
overloading the message buffer system and causing Ajax errors
- Added separate time stats for file seeks
Diffstat (limited to 'src')
-rw-r--r-- | src/Configuration.h | 4 | ||||
-rw-r--r-- | src/FileStore.cpp | 20 | ||||
-rw-r--r-- | src/FileStore.h | 4 | ||||
-rw-r--r-- | src/Libraries/TemperatureSensor/TemperatureSensor.cpp | 9 | ||||
-rw-r--r-- | src/PrintMonitor.cpp | 601 | ||||
-rw-r--r-- | src/PrintMonitor.h | 6 |
6 files changed, 320 insertions, 324 deletions
diff --git a/src/Configuration.h b/src/Configuration.h index e650dec1..71ffc4ea 100644 --- a/src/Configuration.h +++ b/src/Configuration.h @@ -26,11 +26,11 @@ Licence: GPL // Firmware name is now defined in the Pins file #ifndef VERSION -# define VERSION "1.12" +# define VERSION "1.12a" #endif #ifndef DATE -# define DATE "2016-05-07" +# define DATE "2016-05-10" #endif #define AUTHORS "reprappro, dc42, zpl, t3p3, dnewman" diff --git a/src/FileStore.cpp b/src/FileStore.cpp index 3014b206..be3ca3d9 100644 --- a/src/FileStore.cpp +++ b/src/FileStore.cpp @@ -339,4 +339,24 @@ float FileStore::GetAndClearLongestWriteTime() return ret; } +#if 0 // not currently used + +// Provide a cluster map for fast seeking. Needs _USE_FASTSEEK defined as 1 in conf_fatfs to make any difference. +// The first element of the table must be set to the total number of 32-bit entries in the table before calling this. +bool FileStore::SetClusterMap(uint32_t tbl[]) +{ + if (!inUse) + { + platform->Message(GENERIC_MESSAGE, "Error: Attempt to set cluster map for a non-open file.\n"); + return false; + } + + file.cltbl = tbl; + FRESULT ret = f_lseek(&file, CREATE_LINKMAP); +// debugPrintf("ret %d need %u\n", (int)ret, tbl[0]); + return ret == FR_OK; +} + +#endif + // End diff --git a/src/FileStore.h b/src/FileStore.h index a0f2fea1..e1de5fa1 100644 --- a/src/FileStore.h +++ b/src/FileStore.h @@ -41,6 +41,10 @@ public: float FractionRead() const; // How far in we are void Duplicate(); // Create a second reference to this file bool Flush(); // Write remaining buffer data + +#if 0 // not currently used + bool SetClusterMap(uint32_t[]); // Provide a cluster map for fast seeking +#endif static float GetAndClearLongestWriteTime(); // Return the longest time it took to write a block to a file, in milliseconds friend class Platform; diff --git a/src/Libraries/TemperatureSensor/TemperatureSensor.cpp b/src/Libraries/TemperatureSensor/TemperatureSensor.cpp index 266ddd33..db061f83 100644 --- a/src/Libraries/TemperatureSensor/TemperatureSensor.cpp +++ b/src/Libraries/TemperatureSensor/TemperatureSensor.cpp @@ -45,9 +45,6 @@ // dan.newman@mtbaldy.us // GPL v3 -#define PERIPHERAL_CHANNEL_ID 3 -#define PERIPHERAL_CHANNEL_CS_PIN 78 // NPCS3 - const uint32_t MAX31855_Frequency = 4000000; // maximum for MAX31855 is 5MHz const uint32_t MAX31865_Frequency = 4000000; // maximum for MAX31865 is also 5MHz @@ -121,9 +118,6 @@ void TemperatureSensor::InitThermocouple(uint8_t cs) device.csPin = cs; device.spiMode = MAX31855_SpiMode; device.clockFrequency = MAX31855_Frequency; -#ifndef DUET_NG - device.id = PERIPHERAL_CHANNEL_ID; // Peripheral channel -#endif sspi_master_init(&device, 8); lastReadingTime = millis(); @@ -137,9 +131,6 @@ void TemperatureSensor::InitRtd(uint8_t cs) device.csPin = cs; device.spiMode = MAX31865_SpiMode; device.clockFrequency = MAX31865_Frequency; -#ifndef DUET_NG - device.id = PERIPHERAL_CHANNEL_ID; // Peripheral channel -#endif sspi_master_init(&device, 8); TemperatureError rslt; diff --git a/src/PrintMonitor.cpp b/src/PrintMonitor.cpp index 6a186e5b..79cedc06 100644 --- a/src/PrintMonitor.cpp +++ b/src/PrintMonitor.cpp @@ -23,8 +23,8 @@ PrintMonitor::PrintMonitor(Platform *p, GCodes *gc) : platform(p), gCodes(gc), i printStartTime(0), pauseStartTime(0.0), totalPauseTime(0.0), heatingUp(false), currentLayer(0), warmUpDuration(0.0), firstLayerDuration(0.0), firstLayerFilament(0.0), firstLayerProgress(0.0), lastLayerChangeTime(0.0), lastLayerFilament(0.0), lastLayerZ(0.0), numLayerSamples(0), layerEstimatedTimeLeft(0.0), parseState(notParsing), - fileBeingParsed(nullptr), fileOverlapLength(0), printingFileParsed(false), accumulatedParseTime(0.0), - accumulatedReadTime(0.0) + fileBeingParsed(nullptr), fileOverlapLength(0), printingFileParsed(false), accumulatedParseTime(0), + accumulatedReadTime(0), accumulatedSeekTime(0) { filenameBeingPrinted[0] = 0; } @@ -271,25 +271,16 @@ void PrintMonitor::StoppedPrint() bool PrintMonitor::GetFileInfo(const char *directory, const char *fileName, GCodeFileInfo& info) { - // Webserver may call rr_fileinfo for a directory, check this case here - if (reprap.GetPlatform()->GetMassStorage()->DirectoryExists(directory, fileName)) + if (parseState == notParsing) { - info.isValid = false; - return true; - } - - // Are we still parsing a file? - if (parseState != notParsing) - { - if (!StringEquals(fileName, filenameBeingParsed)) + // See if we can access the file + // Webserver may call rr_fileinfo for a directory, check this case here + if (reprap.GetPlatform()->GetMassStorage()->DirectoryExists(directory, fileName)) { - // Yes - but it's not the file we're processing. Try again later - return false; + info.isValid = false; + return true; } - } - else if (parseState == notParsing) - { - // No - see if we can access the file + fileBeingParsed = platform->GetFileStore(directory, fileName, false); if (fileBeingParsed == nullptr) { @@ -319,11 +310,11 @@ bool PrintMonitor::GetFileInfo(const char *directory, const char *fileName, GCod // Record some debug values here if (reprap.Debug(modulePrintMonitor)) { - accumulatedReadTime = accumulatedParseTime = 0.0; - platform->MessageF(GENERIC_MESSAGE, "-- Parsing file %s --\n", fileName); + accumulatedReadTime = accumulatedParseTime = 0; + platform->MessageF(HOST_MESSAGE, "-- Parsing file %s --\n", fileName); } - // If the file is empty or no G-Code file, we don't need to parse anything + // If the file is empty or not a G-Code file, we don't need to parse anything if (fileBeingParsed->Length() == 0 || (!StringEndsWith(fileName, ".gcode") && !StringEndsWith(fileName, ".g") && !StringEndsWith(fileName, ".gco") && !StringEndsWith(fileName, ".gc"))) { @@ -333,256 +324,261 @@ bool PrintMonitor::GetFileInfo(const char *directory, const char *fileName, GCod } parseState = parsingHeader; } + else if (!StringEquals(fileName, filenameBeingParsed)) + { + // We are already parsing a different file. Try again later. + return false; + } - // First try to process the header of the file - float startTime = platform->Time(); - uint32_t buf32[(GCODE_READ_SIZE + GCODE_OVERLAP_SIZE + 3)/4 + 1]; // buffer should be 32-bit aligned for HSMCI (need the +1 so we can add a null terminator) - char* const buf = reinterpret_cast<char*>(buf32); - size_t sizeToRead, sizeToScan; // number of bytes we want to read and scan in this go - - if (parseState == parsingHeader) + // Getting file information take a few runs. Speed it up when we are not printing by calling it several times. + const uint32_t loopStartTime = millis(); + do { - bool headerInfoComplete = true; + uint32_t buf32[(GCODE_READ_SIZE + GCODE_OVERLAP_SIZE + 3)/4 + 1]; // buffer should be 32-bit aligned for HSMCI (need the +1 so we can add a null terminator) + char* const buf = reinterpret_cast<char*>(buf32); + size_t sizeToRead, sizeToScan; // number of bytes we want to read and scan in this go - // Read a chunk from the header. On the first run only process GCODE_READ_SIZE bytes, but use overlap next times. - sizeToRead = (size_t)min<FilePosition>(fileBeingParsed->Length() - fileBeingParsed->Position(), GCODE_READ_SIZE); - if (fileOverlapLength > 0) - { - memcpy(buf, fileOverlap, fileOverlapLength); - sizeToScan = sizeToRead + fileOverlapLength; - } - else + if (parseState == parsingHeader) { - sizeToScan = sizeToRead; - } + bool headerInfoComplete = true; - int nbytes = fileBeingParsed->Read(&buf[fileOverlapLength], sizeToRead); - if (nbytes != (int)sizeToRead) - { - platform->MessageF(HOST_MESSAGE, "Error: Failed to read header of G-Code file \"%s\"\n", fileName); - parseState = notParsing; - fileBeingParsed->Close(); - info = parsedFileInfo; - return true; - } - buf[sizeToScan] = 0; + // Read a chunk from the header. On the first run only process GCODE_READ_SIZE bytes, but use overlap next times. + sizeToRead = (size_t)min<FilePosition>(fileBeingParsed->Length() - fileBeingParsed->Position(), GCODE_READ_SIZE); + if (fileOverlapLength > 0) + { + memcpy(buf, fileOverlap, fileOverlapLength); + sizeToScan = sizeToRead + fileOverlapLength; + } + else + { + sizeToScan = sizeToRead; + } - // Record performance data - if (reprap.Debug(modulePrintMonitor)) - { - const float now = platform->Time(); + uint32_t startTime = millis(); + int nbytes = fileBeingParsed->Read(&buf[fileOverlapLength], sizeToRead); + if (nbytes != (int)sizeToRead) + { + platform->MessageF(HOST_MESSAGE, "Error: Failed to read header of G-Code file \"%s\"\n", fileName); + parseState = notParsing; + fileBeingParsed->Close(); + info = parsedFileInfo; + return true; + } + buf[sizeToScan] = 0; + + // Record performance data + uint32_t now = millis(); accumulatedReadTime += now - startTime; startTime = now; - } - // Search for filament usage (Cura puts it at the beginning of a G-code file) - if (parsedFileInfo.numFilaments == 0) - { - parsedFileInfo.numFilaments = FindFilamentUsed(buf, sizeToScan, parsedFileInfo.filamentNeeded, DRIVES - AXES); - headerInfoComplete &= (parsedFileInfo.numFilaments != 0); - } + // Search for filament usage (Cura puts it at the beginning of a G-code file) + if (parsedFileInfo.numFilaments == 0) + { + parsedFileInfo.numFilaments = FindFilamentUsed(buf, sizeToScan, parsedFileInfo.filamentNeeded, DRIVES - AXES); + headerInfoComplete &= (parsedFileInfo.numFilaments != 0); + } - // Look for first layer height - if (parsedFileInfo.firstLayerHeight == 0.0) - { - headerInfoComplete &= FindFirstLayerHeight(buf, sizeToScan, parsedFileInfo.firstLayerHeight); - } + // Look for first layer height + if (parsedFileInfo.firstLayerHeight == 0.0) + { + headerInfoComplete &= FindFirstLayerHeight(buf, sizeToScan, parsedFileInfo.firstLayerHeight); + } - // Look for layer height - if (parsedFileInfo.layerHeight == 0.0) - { - headerInfoComplete &= FindLayerHeight(buf, sizeToScan, parsedFileInfo.layerHeight); - } + // Look for layer height + if (parsedFileInfo.layerHeight == 0.0) + { + headerInfoComplete &= FindLayerHeight(buf, sizeToScan, parsedFileInfo.layerHeight); + } - // Look for slicer program - if (parsedFileInfo.generatedBy[0] == 0) - { - // Slic3r and S3D - const char* generatedByString = "generated by "; - char* pos = strstr(buf, generatedByString); - if (pos != nullptr) + // Look for slicer program + if (parsedFileInfo.generatedBy[0] == 0) { - pos += strlen(generatedByString); - size_t i = 0; - while (i < ARRAY_SIZE(parsedFileInfo.generatedBy) - 1 && *pos >= ' ') + // Slic3r and S3D + const char* generatedByString = "generated by "; + char* pos = strstr(buf, generatedByString); + if (pos != nullptr) { - char c = *pos++; - if (c == '"' || c == '\\') + pos += strlen(generatedByString); + size_t i = 0; + while (i < ARRAY_SIZE(parsedFileInfo.generatedBy) - 1 && *pos >= ' ') { - // Need to escape the quote-mark for JSON - if (i > ARRAY_SIZE(parsedFileInfo.generatedBy) - 3) + char c = *pos++; + if (c == '"' || c == '\\') { - break; + // Need to escape the quote-mark for JSON + if (i > ARRAY_SIZE(parsedFileInfo.generatedBy) - 3) + { + break; + } + parsedFileInfo.generatedBy[i++] = '\\'; } - parsedFileInfo.generatedBy[i++] = '\\'; + parsedFileInfo.generatedBy[i++] = c; } - parsedFileInfo.generatedBy[i++] = c; + parsedFileInfo.generatedBy[i] = 0; } - parsedFileInfo.generatedBy[i] = 0; - } - // Cura - const char* slicedAtString = ";Sliced at: "; - pos = strstr(buf, slicedAtString); - if (pos != nullptr) - { - pos += strlen(slicedAtString); - strcpy(parsedFileInfo.generatedBy, "Cura at "); - size_t i = 8; - while (i < ARRAY_SIZE(parsedFileInfo.generatedBy) - 1 && *pos >= ' ') + // Cura + const char* slicedAtString = ";Sliced at: "; + pos = strstr(buf, slicedAtString); + if (pos != nullptr) { - char c = *pos++; - if (c == '"' || c == '\\') + pos += strlen(slicedAtString); + strcpy(parsedFileInfo.generatedBy, "Cura at "); + size_t i = 8; + while (i < ARRAY_SIZE(parsedFileInfo.generatedBy) - 1 && *pos >= ' ') { - if (i > ARRAY_SIZE(parsedFileInfo.generatedBy) - 3) + char c = *pos++; + if (c == '"' || c == '\\') { - break; + if (i > ARRAY_SIZE(parsedFileInfo.generatedBy) - 3) + { + break; + } + parsedFileInfo.generatedBy[i++] = '\\'; } - parsedFileInfo.generatedBy[i++] = '\\'; + parsedFileInfo.generatedBy[i++] = c; } - parsedFileInfo.generatedBy[i++] = c; + parsedFileInfo.generatedBy[i] = 0; } - parsedFileInfo.generatedBy[i] = 0; - } - // KISSlicer - const char* kisslicerStart = "; KISSlicer"; - if (StringStartsWith(buf, kisslicerStart)) - { - size_t stringLength = 0; - for(size_t i = 2; i < ARRAY_UPB(parsedFileInfo.generatedBy); i++) + // KISSlicer + const char* kisslicerStart = "; KISSlicer"; + if (StringStartsWith(buf, kisslicerStart)) { - if (buf[i] == '\r' || buf[i] == '\n') + size_t stringLength = 0; + for(size_t i = 2; i < ARRAY_UPB(parsedFileInfo.generatedBy); i++) { - break; - } + if (buf[i] == '\r' || buf[i] == '\n') + { + break; + } - parsedFileInfo.generatedBy[stringLength++] = buf[i]; + parsedFileInfo.generatedBy[stringLength++] = buf[i]; + } + parsedFileInfo.generatedBy[stringLength] = 0; } - parsedFileInfo.generatedBy[stringLength] = 0; } - } - headerInfoComplete &= (parsedFileInfo.generatedBy[0] != 0); + headerInfoComplete &= (parsedFileInfo.generatedBy[0] != 0); - // Keep track of the time stats - if (reprap.Debug(modulePrintMonitor)) - { - accumulatedParseTime += platform->Time() - startTime; + // Keep track of the time stats + accumulatedParseTime += millis() - startTime; + + // Can we proceed to the footer? Don't scan more than the first 4KB of the file + FilePosition pos = fileBeingParsed->Position(); + if (headerInfoComplete || pos >= GCODE_HEADER_SIZE || pos == fileBeingParsed->Length()) + { + // Yes - see if we need to output some debug info + if (reprap.Debug(modulePrintMonitor)) + { + platform->MessageF(HOST_MESSAGE, "Header complete, processed %lu bytes, read time %.3fs, parse time %.3fs\n", + fileBeingParsed->Position(), (float)accumulatedReadTime/1000.0, (float)accumulatedParseTime/1000.0); + } + + // Go to the last chunk and proceed from there on + startTime = millis(); + const FilePosition seekFromEnd = ((fileBeingParsed->Length() - 1) % GCODE_READ_SIZE) + 1; + fileBeingParsed->Seek(fileBeingParsed->Length() - seekFromEnd); + accumulatedSeekTime = millis() - startTime; + accumulatedReadTime = accumulatedParseTime = 0; + fileOverlapLength = 0; + parseState = parsingFooter; + } + else + { + // No - copy the last chunk of the buffer for overlapping search + fileOverlapLength = min<size_t>(sizeToRead, GCODE_OVERLAP_SIZE); + memcpy(fileOverlap, &buf[sizeToRead - fileOverlapLength], fileOverlapLength); + } } - // Can we proceed to the footer? Don't scan more than the first 4KB of the file - FilePosition pos = fileBeingParsed->Position(); - if (headerInfoComplete || pos >= GCODE_HEADER_SIZE || pos == fileBeingParsed->Length()) + if (parseState == parsingFooter) { - // Yes - see if we need to output some debug info - if (reprap.Debug(modulePrintMonitor)) + // Processing the footer. See how many bytes we need to read and if we can reuse the overlap + bool footerInfoComplete = true; + FilePosition pos = fileBeingParsed->Position(); + sizeToRead = (size_t)min<FilePosition>(fileBeingParsed->Length() - pos, GCODE_READ_SIZE); + if (fileOverlapLength > 0) + { + memcpy(&buf[sizeToRead], fileOverlap, fileOverlapLength); + sizeToScan = sizeToRead + fileOverlapLength; + } + else { - platform->MessageF(GENERIC_MESSAGE, "Header complete, processed %lu bytes total\n", fileBeingParsed->Position()); - platform->MessageF(GENERIC_MESSAGE, "Accumulated file read time: %fs, accumulated parsing time: %fs\n", accumulatedReadTime, accumulatedParseTime); - accumulatedReadTime = accumulatedParseTime = 0.0; + sizeToScan = sizeToRead; } - // Go to the last chunk and proceed from there on - const FilePosition seekFromEnd = ((fileBeingParsed->Length() - 1) % GCODE_READ_SIZE) + 1; - fileBeingParsed->Seek(fileBeingParsed->Length() - seekFromEnd); - fileOverlapLength = 0; - parseState = parsingFooter; - } - else - { - // No - copy the last chunk of the buffer for overlapping search - fileOverlapLength = min<size_t>(sizeToRead, GCODE_OVERLAP_SIZE); - memcpy(fileOverlap, &buf[sizeToRead - fileOverlapLength], fileOverlapLength); - } - return false; - } + // Read another chunk from the footer + uint32_t startTime = millis(); + int nbytes = fileBeingParsed->Read(buf, sizeToRead); + if (nbytes != (int)sizeToRead) + { + platform->MessageF(HOST_MESSAGE, "Error: Failed to read footer from G-Code file \"%s\"\n", fileName); + parseState = notParsing; + fileBeingParsed->Close(); + info = parsedFileInfo; + return true; + } + buf[sizeToScan] = 0; - // Processing the footer. See how many bytes we need to read and if we can reuse the overlap - bool footerInfoComplete = true; - FilePosition pos = fileBeingParsed->Position(); - sizeToRead = (size_t)min<FilePosition>(fileBeingParsed->Length() - pos, GCODE_READ_SIZE); - if (fileOverlapLength > 0) - { - memcpy(&buf[sizeToRead], fileOverlap, fileOverlapLength); - sizeToScan = sizeToRead + fileOverlapLength; - } - else - { - sizeToScan = sizeToRead; - } + // Record performance data + uint32_t now = millis(); + accumulatedReadTime += now - startTime; + startTime = now; - // Read another chunk from the footer - int nbytes = fileBeingParsed->Read(buf, sizeToRead); - if (nbytes != (int)sizeToRead) - { - platform->MessageF(HOST_MESSAGE, "Error: Failed to read footer from G-Code file \"%s\"\n", fileName); - parseState = notParsing; - fileBeingParsed->Close(); - info = parsedFileInfo; - return true; - } - buf[sizeToScan] = 0; + // Search for filament used + if (parsedFileInfo.numFilaments == 0) + { + parsedFileInfo.numFilaments = FindFilamentUsed(buf, sizeToScan, parsedFileInfo.filamentNeeded, DRIVES - AXES); + footerInfoComplete &= (parsedFileInfo.numFilaments != 0); + } - // Record performance data - if (reprap.Debug(modulePrintMonitor)) - { - const float now = platform->Time(); - accumulatedReadTime += now - startTime; - startTime = now; - } + // Search for layer height + if (parsedFileInfo.layerHeight == 0.0) + { + footerInfoComplete &= FindLayerHeight(buf, sizeToScan, parsedFileInfo.layerHeight); + } - // Search for filament used - if (parsedFileInfo.numFilaments == 0) - { - parsedFileInfo.numFilaments = FindFilamentUsed(buf, sizeToScan, parsedFileInfo.filamentNeeded, DRIVES - AXES); - footerInfoComplete &= (parsedFileInfo.numFilaments != 0); - } + // Search for object height + if (parsedFileInfo.objectHeight == 0.0) + { + footerInfoComplete &= FindHeight(buf, sizeToScan, parsedFileInfo.objectHeight); + } - // Search for layer height - if (parsedFileInfo.layerHeight == 0.0) - { - footerInfoComplete &= FindLayerHeight(buf, sizeToScan, parsedFileInfo.layerHeight); - } + // Keep track of the time stats + accumulatedParseTime += millis() - startTime; - // Search for object height - if (parsedFileInfo.objectHeight == 0.0) - { - footerInfoComplete &= FindHeight(buf, sizeToScan, parsedFileInfo.objectHeight); - } + // If we've collected all details, scanned the last 192K of the file or if we cannot go any further, stop here. + if (footerInfoComplete || pos == 0 || fileBeingParsed->Length() - pos >= GCODE_FOOTER_SIZE) + { + if (reprap.Debug(modulePrintMonitor)) + { + platform->MessageF(HOST_MESSAGE, "Footer complete, processed %lu bytes, read time %.3fs, parse time %.3fs, seek time %.3fs\n", + fileBeingParsed->Length() - fileBeingParsed->Position() + GCODE_READ_SIZE, + (float)accumulatedReadTime/1000.0, (float)accumulatedParseTime/1000.0, (float)accumulatedSeekTime/1000.0); + } + parseState = notParsing; + fileBeingParsed->Close(); + info = parsedFileInfo; + return true; + } - // Keep track of the time stats - if (reprap.Debug(modulePrintMonitor)) - { - accumulatedParseTime += platform->Time() - startTime; - } + // Else go back further + startTime = millis(); + size_t seekOffset = (size_t)min<FilePosition>(pos, GCODE_READ_SIZE); + if (!fileBeingParsed->Seek(pos - seekOffset)) + { + platform->Message(HOST_MESSAGE, "Error: Could not seek from end of file!\n"); + parseState = notParsing; + fileBeingParsed->Close(); + info = parsedFileInfo; + return true; + } + accumulatedSeekTime += millis() - startTime; - // If we've collected all details, scanned the last 128K of the file or if we cannot go any further, stop here. - if (footerInfoComplete || pos == 0 || fileBeingParsed->Length() - pos >= GCODE_FOOTER_SIZE) - { - if (reprap.Debug(modulePrintMonitor)) - { - platform->MessageF(GENERIC_MESSAGE, "Footer complete, processed %lu bytes total\n", fileBeingParsed->Length() - fileBeingParsed->Position() + GCODE_READ_SIZE); - platform->MessageF(GENERIC_MESSAGE, "Accumulated file read time: %fs, accumulated parsing time: %fs\n", accumulatedReadTime, accumulatedParseTime); + fileOverlapLength = (size_t)min<FilePosition>(sizeToScan, GCODE_OVERLAP_SIZE); + memcpy(fileOverlap, buf, fileOverlapLength); } - parseState = notParsing; - fileBeingParsed->Close(); - info = parsedFileInfo; - return true; - } - - // Else go back further - size_t seekOffset = (size_t)min<FilePosition>(pos, GCODE_READ_SIZE); - if (!fileBeingParsed->Seek(pos - seekOffset)) - { - platform->Message(HOST_MESSAGE, "Error: Could not seek from end of file!\n"); - parseState = notParsing; - fileBeingParsed->Close(); - info = parsedFileInfo; - return true; - } - - fileOverlapLength = (size_t)min<FilePosition>(sizeToScan, GCODE_OVERLAP_SIZE); - memcpy(fileOverlap, buf, fileOverlapLength); + } while (!isPrinting && millis() - loopStartTime < MAX_FILEINFO_PROCESS_TIME); return false; } @@ -593,16 +589,7 @@ bool PrintMonitor::GetFileInfoResponse(const char *filename, OutputBuffer *&resp if (filename != nullptr && filename[0] != 0) { GCodeFileInfo info; - - // Getting file information take a few runs. Speed it up when we are not printing by calling it several times. - uint32_t startTime = millis(); - bool gotFileInfo; - do - { - gotFileInfo = GetFileInfo(FS_PREFIX, filename, info); - } while (!gotFileInfo && !isPrinting && millis() - startTime < MAX_FILEINFO_PROCESS_TIME); - - if (!gotFileInfo) + if (!GetFileInfo(FS_PREFIX, filename, info)) { return false; } @@ -904,112 +891,106 @@ float PrintMonitor::RawFilamentExtruded() const return reprap.GetGCodes()->GetTotalRawExtrusion(); } -// Scan the buffer in reverse for a G1 Zxxx command. The buffer is null-terminated. +// Scan the buffer for a G1 Zxxx command. The buffer is null-terminated. +// This parsing algorithm needs to be fast. The old one sometimes took 5 seconds or more to parse about 120K of data. +// To speed up parsing, we now parse forwards from the start of the buffer. This means we can't stop when we have found a G1 Z command, +// we have to look for a later G1 Z command in the buffer. But it is faster in the (common) case that we don't find a match in the buffer at all. bool PrintMonitor::FindHeight(const char* buf, size_t len, float& height) const { - if (len < 5) + bool foundHeight = false; + bool inRelativeMode = false; + for(;;) { - // Don't start if the buffer is not big enough - return false; - } + // Skip to next newline + char c; + while (len >= 6 && (c = *buf) != '\r' && c != '\n') + { + ++buf; + --len; + } - //debugPrintf("Scanning %u bytes starting %.100s\n", len, buf); - bool inComment, inRelativeMode = false; - unsigned int zPos; - for(size_t i = len - 5; i > 0; i--) - { - if (inRelativeMode) + // Skip the newline and any leading spaces + do { - inRelativeMode = !(buf[i] == 'G' && buf[i + 1] == '9' && buf[i + 2] == '1' && buf[i + 3] <= ' '); + ++buf; // skip the newline + --len; + c = *buf; + } while (len >= 5 && (c == ' ' || c == '\t' || c == '\r' || c == '\n')); + + if (len < 5) + { + break; } - else if (buf[i] == 'G') + + ++buf; // move to 1 character beyond c + --len; + + if (c == 'G') { - // Ignore G0/G1 codes if absolute mode was switched back using G90 (typical for Cura files) - if (buf[i + 1] == '9' && buf[i + 2] == '0' && buf[i + 3] <= ' ') - { - inRelativeMode = true; - } - // Look for last "G0/G1 ... Z#HEIGHT#" command as generated by common slicers - else if ((buf[i + 1] == '0' || buf[i + 1] == '1') && buf[i + 2] == ' ') + if (inRelativeMode) { - // Looks like we found a controlled move, however it could be in a comment, especially when using slic3r 1.1.1 - inComment = false; - size_t j = i; - while (j != 0) + if (buf[0] == '9' && buf[1] == '0' && (buf[2] < '0' || buf[2] > '9')) { - --j; - char c = buf[j]; - if (c == '\n' || c == '\r') - { - // It's not in a comment - break; - } - if (c == ';') - { - // It is in a comment, so give up on this one - inComment = true; - break; - } + // It's a G90 command so go back to absolute mode + inRelativeMode = false; } - if (inComment) - continue; - - // Find 'Z' position and grab that value - zPos = 0; - for(size_t j = i + 3; j < len - 2; j++) + } + else if (*buf == '1' || *buf == '0') + { + // It could be a G0 or G1 command + ++buf; + --len; + if (*buf < '0' || *buf > '9') { - char c = buf[j]; - if (c == ';') - { - inComment = true; - } - else if (c == 'Z' && !inComment) + // It is a G0 or G1 command. See if it has a Z parameter. + while (len >= 4) { - zPos = j; - } - else if (c == '\n') - { - if (zPos != 0) + c = *buf++; + --len; + if (c == 'Z') { + const char* zpos = buf; // Check special case of this code ending with ";E" or "; E" - ignore such codes - for(j = zPos + 1; j < len - 3; j++) + while (len > 2 && *buf != '\n' && *buf != '\r' && *buf != ';') { - c = buf[j]; - if (c == ';' && (buf[j + 1] == 'E' || buf[j + 2] == 'E')) - { - zPos = 0; - break; - } - if (c == '\n') - { - break; - } + ++buf; + --len; } - - if (zPos != 0) + if ((len >= 2 && StringStartsWith(buf, ";E")) || (len >= 3 && StringStartsWith(buf, "; E"))) { - // Z position is valid - read it - height = strtod(&buf[zPos + 1], nullptr); - return true; + // Ignore this G1 Z command } + else + { + height = strtod(zpos, nullptr); + foundHeight = true; + } + break; // carry on looking for a later G1 Z command + } + if (c == ';' || c == '\n' || c == '\r') + { + break; // no Z parameter } - break; } } } + else if (buf[0] == '9' && buf[1] == '1' && (buf[2] < '0' || buf[2] > '9')) + { + // It's a G91 command + inRelativeMode = true; + } } - // Special case: KISSlicer generates object height as a comment - else + else if (c == ';') { - const char *kisslicerHeightString = "; END_LAYER_OBJECT z="; - if (i + 32 < len && StringStartsWith(buf + i, kisslicerHeightString)) + static const char kisslicerHeightString[] = " END_LAYER_OBJECT z="; + if (len > 31 && StringStartsWith(buf, kisslicerHeightString)) { - height = strtod(buf + i + strlen(kisslicerHeightString), nullptr); + height = strtod(buf + sizeof(kisslicerHeightString)/sizeof(char) - 1, nullptr); return true; } } } - return false; + return foundHeight; } // Scan the buffer for the layer height. The buffer is null-terminated. diff --git a/src/PrintMonitor.h b/src/PrintMonitor.h index 380bfc6a..0662b151 100644 --- a/src/PrintMonitor.h +++ b/src/PrintMonitor.h @@ -21,7 +21,7 @@ Licence: GPL #define PRINTMONITOR_H const FilePosition GCODE_HEADER_SIZE = 8192uL; // How many bytes to read from the header -const FilePosition GCODE_FOOTER_SIZE = 192000uL; // How many bytes to read from the footer +const FilePosition GCODE_FOOTER_SIZE = 400000uL; // How many bytes to read from the footer #ifdef DUET_NG const size_t GCODE_READ_SIZE = 4096; // How many bytes to read in one go in GetFileInfo() (should be a multiple of 512 for read efficiency) @@ -39,7 +39,7 @@ const float ESTIMATION_MIN_FILE_USAGE = 0.001; // Minium per cent of the file t const float FIRST_LAYER_SPEED_FACTOR = 0.25; // First layer speed factor compared to other layers (only for layer-based estimation) const uint32_t PRINTMONITOR_UPDATE_INTERVAL = 200; // Update interval in milliseconds -const uint32_t MAX_FILEINFO_PROCESS_TIME = 100; // Maximum time to spend polling for file info in each call +const uint32_t MAX_FILEINFO_PROCESS_TIME = 200; // Maximum time to spend polling for file info in each call enum PrintEstimationMethod { @@ -144,7 +144,7 @@ class PrintMonitor bool FindLayerHeight(const char* buf, size_t len, float& layerHeight) const; unsigned int FindFilamentUsed(const char* buf, size_t len, float *filamentUsed, unsigned int maxFilaments) const; - float accumulatedParseTime, accumulatedReadTime; + uint32_t accumulatedParseTime, accumulatedReadTime, accumulatedSeekTime; }; inline bool PrintMonitor::IsPrinting() const { return isPrinting; } |