diff options
-rw-r--r-- | src/BugList.txt | 61 | ||||
-rw-r--r-- | src/Configuration.h | 2 | ||||
-rw-r--r-- | src/Duet/Webserver.cpp | 10 | ||||
-rw-r--r-- | src/GCodes/GCodes.cpp | 3 | ||||
-rw-r--r-- | src/GCodes/GCodes2.cpp | 7 | ||||
-rw-r--r-- | src/Heating/Sensors/DhtSensor.cpp | 23 | ||||
-rw-r--r-- | src/Libraries/Fatfs/ff.h | 6 | ||||
-rw-r--r-- | src/Networking/HttpResponder.cpp | 10 | ||||
-rw-r--r-- | src/OutputMemory.cpp | 6 | ||||
-rw-r--r-- | src/OutputMemory.h | 2 | ||||
-rw-r--r-- | src/Platform.h | 2 | ||||
-rw-r--r-- | src/RepRap.cpp | 236 | ||||
-rw-r--r-- | src/RepRap.h | 4 | ||||
-rw-r--r-- | src/Storage/FileStore.cpp | 10 | ||||
-rw-r--r-- | src/Version.h | 4 |
15 files changed, 206 insertions, 180 deletions
diff --git a/src/BugList.txt b/src/BugList.txt index 41a4892b..619a7bcd 100644 --- a/src/BugList.txt +++ b/src/BugList.txt @@ -162,7 +162,7 @@ Fixes in RC5: - [done] Extra axes were ignored in G2/G3 moves - [done] DWC and the Duet could deadlock if the Duet ran out of output buffers -Fixes post RC5: +Fixes in RC6: - [done] M569 report includes chopper configuration register for smart drivers - [done] If getting file info timed out it didn't close the file - [done] DHT sensor task was running out of stack space @@ -171,50 +171,69 @@ Fixes post RC5: - [done] Multiple DHT sensor support - [done] Support DHT sensor on Duet Maestro - [done] Pausing between the segments of a segmented move didn't happen even if the jerk settings were high enough -- [done - need to test filament monitors!!!] If filament monitors were deleted or the type changed, this could result in an exception -- [seems ok] Test Upload & print function, see https://forum.duet3d.com/topic/5394/new-firmware-2-0rc5-available/52 +- [done, ok] If filament monitors were deleted or the type changed, this could result in an exception +- [done] Support S0/S1 for simple filament monitors - [done, test] When step rate limiting happens, various problems, see https://forum.duet3d.com/topic/5445/unable-to-run-128-or-256-microstepping (adjust move start time) -- [can't reproduce] Kulitorum report that first move after a pause doesn't extrude, https://forum.duet3d.com/topic/5408/does-not-extrude-first-g1-after-a-pause/7 - [done, test] when the SD card is removed it says 1 file was invalidated even if it was more - [done, ok] When SD card is removed during a print we get lots of internal error messages, and no "print abandoned" or similar message, also the heaters stay on - [done] Emergency stop should turn off all spindles -- [looks like was caused by using M502] Report that filaments no longer work +- [seems ok] Test Upload & print function, see https://forum.duet3d.com/topic/5394/new-firmware-2-0rc5-available/52 + +Fixes in release 2.0: +- [done, test] Kulitorum report that first move after a pause doesn't extrude, https://forum.duet3d.com/topic/5408/does-not-extrude-first-g1-after-a-pause/7 +- [done] Add error codes to SD card error messages +- [tested, documentation error] chrishamm says DHT sensors on CS pins 1-4 don't work +- [done] add facility to return rr_filelist in chunks? +- [done] load config.g.bak if config.g not present, not default.g +- [done] can't load filaments because DWC doesn't give the option unless a filament is already loaded +- look at wifi disconnect issue, https://forum.duet3d.com/topic/4341/wifi-module-keeps-disconnecting/64 + +Planned for 2.01: +- Bezier speed curves or other S-curve acceleration, e.g. look at https://github.com/MarlinFirmware/Marlin/pull/10373/files +- Option to send M280 servo commands just a few times instead of continuously, for E3D and BLTouch +- Recovery from SD card errors during a print (Dan) +- Danny's modified SCARA kinematics (workpiece is on a plate, extruder is fixed) +- Laser support via G1 S parameter, see https://forum.duet3d.com/topic/4702/laser-cnc-support-in-rrf-gcode-semantics/4 +- Look at pushover notification support, https://forum.duet3d.com/topic/169/notification-via-pushover-or-other-service/45 - M3 R parameter so it can restore the spinder/laser after a pause (https://forum.duet3d.com/topic/5418/cnc-laser-m3-unpause-r-parameter) +- M81: don't give low voltage warnings when main power has just been turned off +- Does bltouch need a default recovery time? +- Should bltouch use digital probe mode? Some users having problems with P25 in G31 command. +- M584 when assigning a drive, unmap any existing assignment. Also allow an axis to be mapped to driver -1. +- Add S4 option to G1 command, like S1 but no endstop checks (needed for CoreXY, CoreXZ) - CNC: look at G17/18/19, see https://forum.duet3d.com/topic/4669/ooznest-workbee-screw-driven -- PanelDue won't connect if VIN velow minimum for stepper motors [PanelDue firmware needs to recognise status 'O'] +- M569 command to allow selection of smart/dumb driver, also allow all 12 drivers to be smart +- M569 TOFF parameter, https://forum.duet3d.com/topic/5392/does-m906-set-rms-or-peak-current/28 +- Apostrophe in quoted filename: can we make apostrophe special in SSIDs/passwords but not filenames? +- If wifi module gets stuck in starting or changing mode state, reset it again +- If wifi disconnects when in client mode, keep retrying the connection + +Other (some of these may be in 2.01): +- Update PanelDue firmware via Duet +- Use Heat task to read DHT sensors? +- Add option R4 to M915 command, to do an emergency stop (useful for Z motor) +- GCodes corresponding to rr_move and rr_mkdir, https://forum.duet3d.com/topic/5470/firmware-2-0rc6-and-1-21-1rc6-released/13 - Add parameter to M116 to specify acceptable temperature difference -- Laser support, see https://forum.duet3d.com/topic/4702/laser-cnc-support-in-rrf-gcode-semantics/4 -- Pushover notification support, https://forum.duet3d.com/topic/169/notification-via-pushover-or-other-service/45 - support M205 for setting jerk - support G12 clean tool? - Add fan PWM limit, https://forum.duet3d.com/topic/5370/m106-feature-request-limit-max-pwm-parameter/4 - M116 extra "close enough" parameter -- Danny's modified SCARA kinematics (workpiece is on a plate, extruder is fixed) -- Look at Bezier speed curves or other S-curve acceleration, https://github.com/MarlinFirmware/Marlin/pull/10373/files - after homing, warn if outside M208 movement limits on SCARA, polar etc. - Unexpected heaters off/tool selection behaviour, https://www.duet3d.com/forum/thread.php?pid=43059#p43059 - warn when using : where ; was probably meant - Error message if you attempt movement with VIN < minimum - min/max RSSI display? -- save theta, phi in move and then in DDA? - report RSSI in M552? - When VIN power too low and stepper drivers turned off, flag axes as not homed? -- M81: don't give low voltage warnings when main power has just been turned off -- Return "Powered down" status when VIN power has never been seen or has gone down after sending M81 - Auto mount main SD card when inserted - Workplace offsets are supposed to be persistant (check NIST), https://www.duet3d.com/forum/thread.php?pid=43755#p43755 - At the end of a simulation, restore the original workplace coordinate selection -- apostrophe in quoted filename - looks like we get a Pop underflow message when you send DWC jog commands and axes are not homed - Add warning message when print exceeds bounds -- Does bltouch need a default recovery time? -- Should bltouch use digital probe mode? Some users having problems with P25 in G31 command. -- M584 when assigning a drive, unmap any existing assignment. Also allow an axis to be mapped to driver -1. -- Add S4 option to G1 command, like S1 but no endstop checks (needed for CoreXY, CoreXZ) - When uploading while a file is being printed, don't allow the currently-printing file to be replaced -- if a homing command in an SD print file is aborted due to e.g. G1 Z5 in the homing file, error message should be written to both DWC and PanelDue +- [check] if a homing command in an SD print file is aborted due to e.g. G1 Z5 in the homing file, error message should be written to both DWC and PanelDue - [no fault] stall detect on Z axis - Files generated by Cura doesn't detect layer changes, see https://www.duet3d.com/forum/thread.php?pid=40865#p40865 - [re-test using new DuetWiFiServer] "Failed to change mode" messages after M552 S2/S0/S1 cycle @@ -231,7 +250,6 @@ Fixes post RC5: - check that we never enable the drivers before we set vsense - [don't do] Don't report the motor current for a non-existent extruder -- SD card read error handling? - dual extrusion layer counting, see https://www.duet3d.com/forum/thread.php?pid=34816#p34816 - M140/M190 with no P parameter sets all bed heater temperatures (same for M141/M191) - M291 to lock movement and wait for it to finish? @@ -245,9 +263,6 @@ Fixes post RC5: - Send reduce power command to PanelDue when main power turned off? - Document multiple bed and chamber heaters - [mostly done] Check all classes for correct initialisation -- sd_mmc_spi doesn't acquire/release the SPI bus. Need to change this for RTOS. -- If wifi module gets stuck in starting state, reset it again -- If wifi disconnects when in client mode, keep retrying the connection Bug investigations: - [done] step errors, https://www.duet3d.com/forum/thread.php?pid=33741#p33741 diff --git a/src/Configuration.h b/src/Configuration.h index 3a94dfd6..13f5c9d8 100644 --- a/src/Configuration.h +++ b/src/Configuration.h @@ -259,7 +259,7 @@ constexpr size_t FILE_BUFFER_SIZE = 128; #define MENU_DIR "0:/menu/" // Directory for menu files #define CONFIG_FILE "config.g" -#define DEFAULT_FILE "default.g" +#define CONFIG_BACKUP_FILE "config.g.bak" #define DEFAULT_LOG_FILE "eventlog.txt" #define EOF_STRING "<!-- **EoF** -->" diff --git a/src/Duet/Webserver.cpp b/src/Duet/Webserver.cpp index cb163698..3ec41216 100644 --- a/src/Duet/Webserver.cpp +++ b/src/Duet/Webserver.cpp @@ -951,19 +951,23 @@ void Webserver::HttpInterpreter::GetJsonResponse(const char* request, OutputBuff else if (StringEquals(request, "filelist") && GetKeyValue("dir") != nullptr) { OutputBuffer::Release(response); - response = reprap.GetFilelistResponse(GetKeyValue("dir")); + const char* const firstVal = GetKeyValue("first"); + const unsigned int startAt = (firstVal == nullptr) ? 0 : (unsigned int)SafeStrtol(firstVal); + response = reprap.GetFilelistResponse(GetKeyValue("dir"), startAt); // this may return nullptr } else if (StringEquals(request, "files")) { + OutputBuffer::Release(response); const char* dir = GetKeyValue("dir"); if (dir == nullptr) { dir = platform->GetGCodeDir(); } + const char* const firstVal = GetKeyValue("first"); + const unsigned int startAt = (firstVal == nullptr) ? 0 : SafeStrtol(firstVal); const char* const flagDirsVal = GetKeyValue("flagDirs"); const bool flagDirs = flagDirsVal != nullptr && atoi(flagDirsVal) == 1; - OutputBuffer::Release(response); - response = reprap.GetFilesResponse(dir, flagDirs); + response = reprap.GetFilesResponse(dir, startAt, flagDirs); // this may return nullptr } else if (StringEquals(request, "fileinfo")) { diff --git a/src/GCodes/GCodes.cpp b/src/GCodes/GCodes.cpp index 44bc3075..47f7947e 100644 --- a/src/GCodes/GCodes.cpp +++ b/src/GCodes/GCodes.cpp @@ -2115,11 +2115,11 @@ bool GCodes::LoadExtrusionAndFeedrateFromGCode(GCodeBuffer& gb) moveBuffer.coords[drive] = 0.0; } moveBuffer.hasExtrusion = false; + moveBuffer.virtualExtruderPosition = virtualExtruderPosition; // save this before we update it // Check if we are extruding if (moveBuffer.isCoordinated && gb.Seen(extrudeLetter)) { - // Check that we have a tool to extrude with Tool* const tool = reprap.GetCurrentTool(); if (tool == nullptr) @@ -2610,7 +2610,6 @@ void GCodes::FinaliseMove(GCodeBuffer& gb) { moveBuffer.canPauseAfter = (moveBuffer.endStopsToCheck == 0); moveBuffer.filePos = (&gb == fileGCode) ? gb.GetFilePosition(fileInput->BytesCached()) : noFilePosition; - moveBuffer.virtualExtruderPosition = virtualExtruderPosition; if (totalSegments > 1) { diff --git a/src/GCodes/GCodes2.cpp b/src/GCodes/GCodes2.cpp index 7d8d78e4..af298d35 100644 --- a/src/GCodes/GCodes2.cpp +++ b/src/GCodes/GCodes2.cpp @@ -564,6 +564,7 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, const StringRef& reply) } { const int sparam = (gb.Seen('S')) ? gb.GetIValue() : 0; + const unsigned int rparam = (gb.Seen('R')) ? gb.GetUIValue() : 0; String<MaxFilenameLength> dir; if (gb.Seen('P')) { @@ -577,7 +578,7 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, const StringRef& reply) OutputBuffer *fileResponse; if (sparam == 2) { - fileResponse = reprap.GetFilesResponse(dir.c_str(), true); // Send the file list in JSON format + fileResponse = reprap.GetFilesResponse(dir.c_str(), rparam, true); // send the file list in JSON format if (fileResponse == nullptr) { return false; @@ -586,7 +587,7 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, const StringRef& reply) } else if (sparam == 3) { - fileResponse = reprap.GetFilelistResponse(dir.c_str()); + fileResponse = reprap.GetFilelistResponse(dir.c_str(), rparam); if (fileResponse == nullptr) { return false; @@ -597,7 +598,7 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, const StringRef& reply) { if (!OutputBuffer::Allocate(fileResponse)) { - return false; // Cannot allocate an output buffer, try again later + return false; // cannot allocate an output buffer, try again later } // To mimic the behaviour of the official RepRapPro firmware: diff --git a/src/Heating/Sensors/DhtSensor.cpp b/src/Heating/Sensors/DhtSensor.cpp index 016723af..15da14bb 100644 --- a/src/Heating/Sensors/DhtSensor.cpp +++ b/src/Heating/Sensors/DhtSensor.cpp @@ -39,6 +39,7 @@ DhtSensorHardwareInterface::DhtSensorHardwareInterface(Pin p_pin) : sensorPin(p_pin), type(DhtSensorType::none), lastResult(TemperatureError::notInitialised), lastTemperature(BAD_ERROR_TEMPERATURE), lastHumidity(BAD_ERROR_TEMPERATURE), badTemperatureCount(0) { + IoPort::SetPinMode(sensorPin, INPUT_PULLUP); } TemperatureError DhtSensorHardwareInterface::GetTemperatureOrHumidity(float& t, bool wantHumidity) const @@ -117,7 +118,7 @@ GCodeResult DhtSensorHardwareInterface::Configure(TemperatureSensor *ts, unsigne return rslt; } -// Create a hardware interface object re the specified channel if there isn't already +// Create a hardware interface object for the specified channel if there isn't already DhtSensorHardwareInterface *DhtSensorHardwareInterface::Create(unsigned int relativeChannel) { if (relativeChannel >= MaxSpiTempSensors) @@ -170,19 +171,13 @@ void DhtSensorHardwareInterface::Interrupt() if (numPulses < ARRAY_SIZE(pulses)) { const uint16_t now = Platform::GetInterruptClocks16(); - if (digitalRead(sensorPin)) + if (IoPort::ReadPin(sensorPin)) { lastPulseTime = now; } else if (lastPulseTime != 0) { pulses[numPulses++] = now - lastPulseTime; -#if 0 - if (numPulses == ARRAY_SIZE(pulses)) - { - vTaskNotifyGiveFromISR(dhtTask->GetHandle(), nullptr); // wake up the task - } -#endif } } } @@ -191,12 +186,8 @@ void DhtSensorHardwareInterface::TakeReading() { if (type != DhtSensorType::none) // if sensor has been configured { - // Send start signal. See DHT datasheet for full signal diagram: - // http://www.adafruit.com/datasheets/Digital%20humidity%20and%20temperature%20sensor%20AM2302.pdf - pinMode(sensorPin, OUTPUT_HIGH); - delay(250); - - digitalWrite(sensorPin, LOW); + // Send the start bit. This must be at least 18ms for the DHT11, 0.8ms for the DHT21, and 1ms long for the DHT22. + IoPort::SetPinMode(sensorPin, OUTPUT_LOW); delay(20); { @@ -204,11 +195,11 @@ void DhtSensorHardwareInterface::TakeReading() // End the start signal by setting data line high. the sensor will respond with the start bit in 20 to 40us. // We need only force the data line high long enough to charge the line capacitance, after that the pullup resistor keeps it high. - digitalWrite(sensorPin, HIGH); // this will generate an interrupt, but we will ignore it + IoPort::WriteDigital(sensorPin, HIGH); // this will generate an interrupt, but we will ignore it delayMicroseconds(3); // Now start reading the data line to get the value from the DHT sensor - pinMode(sensorPin, INPUT_PULLUP); + IoPort::SetPinMode(sensorPin, INPUT_PULLUP); // It appears that switching the pin to an output disables the interrupt, so we need to call attachInterrupt here // We are likely to get an immediate interrupt at this point corresponding to the low-to-high transition. We must ignore this. diff --git a/src/Libraries/Fatfs/ff.h b/src/Libraries/Fatfs/ff.h index 406f9d29..7f0d91ab 100644 --- a/src/Libraries/Fatfs/ff.h +++ b/src/Libraries/Fatfs/ff.h @@ -178,14 +178,14 @@ typedef struct { typedef enum { FR_OK = 0, /* (0) Succeeded */ - FR_DISK_ERR, /* (1) A hard error occured in the low level disk I/O layer */ + FR_DISK_ERR, /* (1) A hard error occurred in the low level disk I/O layer */ FR_INT_ERR, /* (2) Assertion failed */ FR_NOT_READY, /* (3) The physical drive cannot work */ FR_NO_FILE, /* (4) Could not find the file */ FR_NO_PATH, /* (5) Could not find the path */ FR_INVALID_NAME, /* (6) The path name format is invalid */ - FR_DENIED, /* (7) Acces denied due to prohibited access or directory full */ - FR_EXIST, /* (8) Acces denied due to prohibited access */ + FR_DENIED, /* (7) Access denied due to prohibited access or directory full */ + FR_EXIST, /* (8) Access denied due to prohibited access */ FR_INVALID_OBJECT, /* (9) The file/directory object is invalid */ FR_WRITE_PROTECTED, /* (10) The physical drive is write protected */ FR_INVALID_DRIVE, /* (11) The logical drive number is invalid */ diff --git a/src/Networking/HttpResponder.cpp b/src/Networking/HttpResponder.cpp index 00ee1145..b96fcac2 100644 --- a/src/Networking/HttpResponder.cpp +++ b/src/Networking/HttpResponder.cpp @@ -533,19 +533,23 @@ bool HttpResponder::GetJsonResponse(const char* request, OutputBuffer *&response else if (StringEquals(request, "filelist") && GetKeyValue("dir") != nullptr) { OutputBuffer::Release(response); - response = reprap.GetFilelistResponse(GetKeyValue("dir")); // this may return nullptr + const char* const firstVal = GetKeyValue("first"); + const unsigned int startAt = (firstVal == nullptr) ? 0 : (unsigned int)SafeStrtol(firstVal); + response = reprap.GetFilelistResponse(GetKeyValue("dir"), startAt); // this may return nullptr } else if (StringEquals(request, "files")) { + OutputBuffer::Release(response); const char* dir = GetKeyValue("dir"); if (dir == nullptr) { dir = GetPlatform().GetGCodeDir(); } + const char* const firstVal = GetKeyValue("first"); + const unsigned int startAt = (firstVal == nullptr) ? 0 : SafeStrtol(firstVal); const char* const flagDirsVal = GetKeyValue("flagDirs"); const bool flagDirs = flagDirsVal != nullptr && SafeStrtol(flagDirsVal) == 1; - OutputBuffer::Release(response); - response = reprap.GetFilesResponse(dir, flagDirs); // this may return nullptr + response = reprap.GetFilesResponse(dir, startAt, flagDirs); // this may return nullptr } else if (StringEquals(request, "fileinfo")) { diff --git a/src/OutputMemory.cpp b/src/OutputMemory.cpp index d7f49728..22200a1f 100644 --- a/src/OutputMemory.cpp +++ b/src/OutputMemory.cpp @@ -231,13 +231,17 @@ size_t OutputBuffer::cat(StringRef &str) } // Encode a string in JSON format and append it to a string buffer and return the number of bytes written -size_t OutputBuffer::EncodeString(const char *src, size_t srcLength, bool allowControlChars, bool encapsulateString) +size_t OutputBuffer::EncodeString(const char *src, size_t srcLength, bool allowControlChars, bool encapsulateString, bool prependAsterisk) { size_t bytesWritten = 0; if (encapsulateString) { bytesWritten += cat('"'); } + if (prependAsterisk) + { + bytesWritten += cat('*'); + } if (srcLength != 0) { diff --git a/src/OutputMemory.h b/src/OutputMemory.h index 4fcdc442..c5d31787 100644 --- a/src/OutputMemory.h +++ b/src/OutputMemory.h @@ -54,7 +54,7 @@ class OutputBuffer size_t cat(const char *src, size_t len); size_t cat(StringRef &str); - size_t EncodeString(const char *src, size_t srcLength, bool allowControlChars, bool encapsulateString = true); + size_t EncodeString(const char *src, size_t srcLength, bool allowControlChars, bool encapsulateString = true, bool prependAsterisk = false); size_t EncodeString(const StringRef& str, bool allowControlChars, bool encapsulateString = true); size_t EncodeReply(OutputBuffer *src, bool allowControlChars); diff --git a/src/Platform.h b/src/Platform.h index 5c448de2..9d040849 100644 --- a/src/Platform.h +++ b/src/Platform.h @@ -952,7 +952,7 @@ inline const char* Platform::GetConfigFile() const inline const char* Platform::GetDefaultFile() const { - return DEFAULT_FILE; + return CONFIG_BACKUP_FILE; } //***************************************************************************************************************** diff --git a/src/RepRap.cpp b/src/RepRap.cpp index 443aaed2..914b860a 100644 --- a/src/RepRap.cpp +++ b/src/RepRap.cpp @@ -1254,11 +1254,15 @@ OutputBuffer *RepRap::GetStatusResponse(uint8_t type, ResponseSource source) if (tool->GetFilament() != nullptr) { const char *filamentName = tool->GetFilament()->GetName(); +#if 0 // DC this change broke filament loading/unloading in DWC if (filamentName[0] != 0) { +#endif response->catf(",\"filament\":"); response->EncodeString(filamentName, strlen(filamentName), false); +#if 0 // DC see above } +#endif } // Offsets @@ -1681,7 +1685,7 @@ OutputBuffer *RepRap::GetLegacyStatusResponse(uint8_t type, int seq) // Get the list of files in the specified directory in JSON format. // If flagDirs is true then we prefix each directory with a * character. -OutputBuffer *RepRap::GetFilesResponse(const char *dir, bool flagsDirs) +OutputBuffer *RepRap::GetFilesResponse(const char *dir, unsigned int startAt, bool flagsDirs) { // Need something to write to... OutputBuffer *response; @@ -1692,8 +1696,9 @@ OutputBuffer *RepRap::GetFilesResponse(const char *dir, bool flagsDirs) response->copy("{\"dir\":"); response->EncodeString(dir, strlen(dir), false); - response->cat(",\"files\":["); + response->catf(",\"first\":%u,\"files\":[", startAt); unsigned int err; + unsigned int nextFile = 0; if (!platform->GetMassStorage()->CheckDriveMounted(dir)) { @@ -1707,50 +1712,138 @@ OutputBuffer *RepRap::GetFilesResponse(const char *dir, bool flagsDirs) { err = 0; FileInfo fileInfo; - bool firstFile = true; + unsigned int filesFound = 0; bool gotFile = platform->GetMassStorage()->FindFirst(dir, fileInfo); // TODO error handling here size_t bytesLeft = OutputBuffer::GetBytesLeft(response); // don't write more bytes than we can - char filename[MaxFilenameLength]; - filename[0] = '*'; - const char *fname; while (gotFile) { if (fileInfo.fileName[0] != '.') // ignore Mac resource files and Linux hidden files { - // Get the long filename if possible - if (flagsDirs && fileInfo.isDirectory) - { - SafeStrncpy(filename + 1, fileInfo.fileName, ARRAY_SIZE(fileInfo.fileName) - 1); - fname = filename; - } - else + if (filesFound >= startAt) { - fname = fileInfo.fileName; - } + // Make sure we can end this response properly + if (bytesLeft < strlen(fileInfo.fileName) * 2 + 20) + { + // No more space available - stop here + platform->GetMassStorage()->AbandonFindNext(); + nextFile = filesFound; + break; + } - // Make sure we can end this response properly - if (bytesLeft < strlen(fname) * 2 + 4) - { - // No more space available - stop here - platform->GetMassStorage()->AbandonFindNext(); - break; + // Write separator and filename + if (filesFound > startAt) + { + bytesLeft -= response->cat(','); + } + + bytesLeft -= response->EncodeString(fileInfo.fileName, MaxFilenameLength, false, true, flagsDirs && fileInfo.isDirectory); } + ++filesFound; + } + gotFile = platform->GetMassStorage()->FindNext(fileInfo); // TODO error handling here + } + } + + if (err != 0) + { + response->catf("],\"err\":%u}", err); + } + else + { + response->catf("],\"next\":%u,\"err\":%u}", nextFile, err); + } + return response; +} + +// Get a JSON-style filelist including file types and sizes +OutputBuffer *RepRap::GetFilelistResponse(const char *dir, unsigned int startAt) +{ + // Need something to write to... + OutputBuffer *response; + if (!OutputBuffer::Allocate(response)) + { + return nullptr; + } + + response->copy("{\"dir\":"); + response->EncodeString(dir, strlen(dir), false); + response->catf(",\"first\":%u,\"files\":[", startAt); + unsigned int err; + unsigned int nextFile = 0; + + if (!platform->GetMassStorage()->CheckDriveMounted(dir)) + { + err = 1; + } + else if (!platform->GetMassStorage()->DirectoryExists(dir)) + { + err = 2; + } + else + { + err = 0; + FileInfo fileInfo; + unsigned int filesFound = 0; + bool gotFile = platform->GetMassStorage()->FindFirst(dir, fileInfo); + size_t bytesLeft = OutputBuffer::GetBytesLeft(response); // don't write more bytes than we can - // Write separator and filename - if (!firstFile) + while (gotFile) + { + if (fileInfo.fileName[0] != '.') // ignore Mac resource files and Linux hidden files + { + if (filesFound >= startAt) { - bytesLeft -= response->cat(','); - } - bytesLeft -= response->EncodeString(fname, MaxFilenameLength, false); + // Make sure we can end this response properly + if (bytesLeft < strlen(fileInfo.fileName) * 2 + 50) + { + // No more space available - stop here + platform->GetMassStorage()->AbandonFindNext(); + nextFile = filesFound; + break; + } + + // Write delimiter + if (filesFound != 0) + { + bytesLeft -= response->cat(','); + } + + // Write another file entry + bytesLeft -= response->catf("{\"type\":\"%c\",\"name\":", fileInfo.isDirectory ? 'd' : 'f'); + bytesLeft -= response->EncodeString(fileInfo.fileName, MaxFilenameLength, false); + bytesLeft -= response->catf(",\"size\":%" PRIu32, fileInfo.size); - firstFile = false; + const struct tm * const timeInfo = gmtime(&fileInfo.lastModified); + if (timeInfo->tm_year <= /*19*/80) + { + // Don't send the last modified date if it is invalid + bytesLeft -= response->cat('}'); + } + else + { + bytesLeft -= response->catf(",\"date\":\"%04u-%02u-%02uT%02u:%02u:%02u\"}", + timeInfo->tm_year + 1900, timeInfo->tm_mon + 1, timeInfo->tm_mday, + timeInfo->tm_hour, timeInfo->tm_min, timeInfo->tm_sec); + } + } + ++filesFound; } - gotFile = platform->GetMassStorage()->FindNext(fileInfo); // TODO error handling here + gotFile = platform->GetMassStorage()->FindNext(fileInfo); } } - response->catf("],\"err\":%u}", err); + + // If there is no error, don't append "err":0 because if we do then DWC thinks there has been an error - looks like it doesn't check the value + if (err != 0) + { + response->catf("],\"err\":%u}", err); + } + else + { + response->catf("],\"next\":%u}", nextFile); + } + return response; } @@ -1840,91 +1933,6 @@ bool RepRap::GetFileInfoResponse(const char *filename, OutputBuffer *&response, return true; } -// Get a JSON-style filelist including file types and sizes -OutputBuffer *RepRap::GetFilelistResponse(const char *dir) -{ - // Need something to write to... - OutputBuffer *response; - if (!OutputBuffer::Allocate(response)) - { - return nullptr; - } - - response->copy("{\"dir\":"); - response->EncodeString(dir, strlen(dir), false); - response->cat(",\"files\":["); - unsigned int err; - - if (!platform->GetMassStorage()->CheckDriveMounted(dir)) - { - err = 1; - } - else if (!platform->GetMassStorage()->DirectoryExists(dir)) - { - err = 2; - } - else - { - err = 0; - FileInfo fileInfo; - bool firstFile = true; - bool gotFile = platform->GetMassStorage()->FindFirst(dir, fileInfo); - size_t bytesLeft = OutputBuffer::GetBytesLeft(response); // don't write more bytes than we can - - while (gotFile) - { - if (fileInfo.fileName[0] != '.') // ignore Mac resource files and Linux hidden files - { - // Make sure we can end this response properly - if (bytesLeft < strlen(fileInfo.fileName) + 70) - { - // No more space available - stop here - platform->GetMassStorage()->AbandonFindNext(); - break; - } - - // Write delimiter - if (!firstFile) - { - bytesLeft -= response->cat(','); - } - firstFile = false; - - // Write another file entry - bytesLeft -= response->catf("{\"type\":\"%c\",\"name\":", fileInfo.isDirectory ? 'd' : 'f'); - bytesLeft -= response->EncodeString(fileInfo.fileName, MaxFilenameLength, false); - bytesLeft -= response->catf(",\"size\":%" PRIu32, fileInfo.size); - - const struct tm * const timeInfo = gmtime(&fileInfo.lastModified); - if (timeInfo->tm_year <= /*19*/80) - { - // Don't send the last modified date if it is invalid - bytesLeft -= response->cat('}'); - } - else - { - bytesLeft -= response->catf(",\"date\":\"%04u-%02u-%02uT%02u:%02u:%02u\"}", - timeInfo->tm_year + 1900, timeInfo->tm_mon + 1, timeInfo->tm_mday, - timeInfo->tm_hour, timeInfo->tm_min, timeInfo->tm_sec); - } - } - gotFile = platform->GetMassStorage()->FindNext(fileInfo); - } - } - - // If there is no error, don't append "err":0 because if we do then DWC thinks there has been an error - looks like it doesn't check the value - if (err != 0) - { - response->catf("],\"err\":%u}", err); - } - else - { - response->cat("]}"); - } - - return response; -} - // Send a beep. We send it to both PanelDue and the web interface. void RepRap::Beep(unsigned int freq, unsigned int ms) { diff --git a/src/RepRap.h b/src/RepRap.h index 213adde3..64d2d459 100644 --- a/src/RepRap.h +++ b/src/RepRap.h @@ -101,8 +101,8 @@ public: OutputBuffer *GetStatusResponse(uint8_t type, ResponseSource source); OutputBuffer *GetConfigResponse(); OutputBuffer *GetLegacyStatusResponse(uint8_t type, int seq); - OutputBuffer *GetFilesResponse(const char* dir, bool flagsDirs); - OutputBuffer *GetFilelistResponse(const char* dir); + OutputBuffer *GetFilesResponse(const char* dir, unsigned int startAt, bool flagsDirs); + OutputBuffer *GetFilelistResponse(const char* dir, unsigned int startAt); bool GetFileInfoResponse(const char *filename, OutputBuffer *&response, bool quitEarly); void Beep(unsigned int freq, unsigned int ms); diff --git a/src/Storage/FileStore.cpp b/src/Storage/FileStore.cpp index 1a3b25e7..07322cb9 100644 --- a/src/Storage/FileStore.cpp +++ b/src/Storage/FileStore.cpp @@ -77,7 +77,7 @@ bool FileStore::Open(const char* directory, const char* fileName, OpenMode mode) filePath[i] = 0; if (!reprap.GetPlatform().GetMassStorage()->DirectoryExists(filePath.GetRef()) && !reprap.GetPlatform().GetMassStorage()->MakeDirectory(filePath.c_str())) { - reprap.GetPlatform().MessageF(ErrorMessage, "Failed to create directory %s while trying to open file %s\n", filePath.c_str(), location.c_str()); + reprap.GetPlatform().MessageF(ErrorMessage, "Failed to create folder %s while trying to open file %s\n", filePath.c_str(), location.c_str()); return false; } filePath[i] = '/'; @@ -102,7 +102,7 @@ bool FileStore::Open(const char* directory, const char* fileName, OpenMode mode) // It is up to the caller to report an error if necessary. if (reprap.Debug(modulePlatform)) { - reprap.GetPlatform().MessageF(ErrorMessage, "Can't open %s to %s, error code %d\n", location.c_str(), (writing) ? "write" : "read", openReturn); + reprap.GetPlatform().MessageF(ErrorMessage, "Can't open %s to %s, error code %d\n", location.c_str(), (writing) ? "write" : "read", (int)openReturn); } return false; } @@ -286,7 +286,7 @@ int FileStore::Read(char* extBuf, size_t nBytes) FRESULT readStatus = f_read(&file, extBuf, nBytes, &bytes_read); if (readStatus != FR_OK) { - reprap.GetPlatform().Message(ErrorMessage, "Cannot read file.\n"); + reprap.GetPlatform().MessageF(ErrorMessage, "Cannot read file, error code %d.\n", (int)readStatus); return -1; } return (int)bytes_read; @@ -397,7 +397,7 @@ bool FileStore::Write(const char *s, size_t len) if ((writeStatus != FR_OK) || (totalBytesWritten != len)) { - reprap.GetPlatform().Message(ErrorMessage, "Failed to write to file. Drive may be full.\n"); + reprap.GetPlatform().MessageF(ErrorMessage, "Failed to write to file, error code %d. Card may be full.\n", (int)writeStatus); return false; } return true; @@ -432,7 +432,7 @@ bool FileStore::Flush() if ((writeStatus != FR_OK) || (bytesToWrite != bytesWritten)) { - reprap.GetPlatform().Message(ErrorMessage, "Failed to write to file. Drive may be full.\n"); + reprap.GetPlatform().MessageF(ErrorMessage, "Failed to flush data to file, error code %d. Card may be full.\n", (int)writeStatus); return false; } } diff --git a/src/Version.h b/src/Version.h index 0a6ddc9e..6c5ebe24 100644 --- a/src/Version.h +++ b/src/Version.h @@ -18,11 +18,11 @@ # define RTOSVER #endif -# define VERSION MAIN_VERSION RTOSVER "RC6" +# define VERSION MAIN_VERSION RTOSVER #endif #ifndef DATE -# define DATE "2018-05-29b4" +# define DATE "2018-06-05b3" #endif #define AUTHORS "reprappro, dc42, chrishamm, t3p3, dnewman" |