This repository has been archived on 2025-02-01. You can view files and clone it, but cannot push or open issues or pull requests.
reprapfirmware-dc42/src/FileStore.cpp
David Crocker f3a1b62c0a Version 1.11-beta1
Changed to CoreNG
Implemented chrishamm's latest Network and Webserver changes, including
Telnet streaming
Added S parameter to M997 command for DuetNG
Increased file read block size to 2K when getting file info
2016-04-10 21:50:16 +01:00

342 lines
6.5 KiB
C++

//------------------------------------------------------------------------------------------------
#include "RepRapFirmware.h"
#include "FileStore.h"
#include "MassStorage.h"
#include "Platform.h"
uint32_t FileStore::longestWriteTime = 0;
FileStore::FileStore(Platform* p) : platform(p)
{
}
void FileStore::Init()
{
bufferPointer = 0;
inUse = false;
writing = false;
lastBufferEntry = 0;
openCount = 0;
closeRequested = false;
}
// Open a local file (for example on an SD card).
// This is protected - only Platform can access it.
bool FileStore::Open(const char* directory, const char* fileName, bool write)
{
const char* location = (directory != nullptr)
? platform->GetMassStorage()->CombineName(directory, fileName)
: fileName;
writing = write;
lastBufferEntry = FileBufLen;
FRESULT openReturn = f_open(&file, location, (writing) ? FA_CREATE_ALWAYS | FA_WRITE : FA_OPEN_EXISTING | FA_READ);
if (openReturn != FR_OK)
{
// We no longer report an error if opening a file in read mode fails unless debugging is enabled, because sometimes that is quite normal.
// It is up to the caller to report an error if necessary.
if (reprap.Debug(modulePlatform))
{
platform->MessageF(GENERIC_MESSAGE, "Can't open %s to %s, error code %d\n", location, (writing) ? "write" : "read", openReturn);
}
return false;
}
bufferPointer = (writing) ? 0 : FileBufLen;
inUse = true;
openCount = 1;
return true;
}
void FileStore::Duplicate()
{
if (!inUse)
{
platform->Message(GENERIC_MESSAGE, "Error: Attempt to dup a non-open file.\n");
return;
}
irqflags_t flags = cpu_irq_save();
++openCount;
cpu_irq_restore(flags);
}
// This may be called from an ISR, in which case we need to defer the close
bool FileStore::Close()
{
if (inInterrupt())
{
if (!inUse)
{
return false;
}
irqflags_t flags = cpu_irq_save();
if (openCount > 1)
{
--openCount;
}
else
{
closeRequested = true;
}
cpu_irq_restore(flags);
return true;
}
if (!inUse)
{
platform->Message(GENERIC_MESSAGE, "Error: Attempt to close a non-open file.\n");
return false;
}
irqflags_t flags = cpu_irq_save();
--openCount;
bool leaveOpen = (openCount != 0);
cpu_irq_restore(flags);
if (leaveOpen)
{
return true;
}
bool ok = true;
if (writing)
{
ok = Flush();
}
FRESULT fr = f_close(&file);
inUse = false;
writing = false;
lastBufferEntry = 0;
closeRequested = false;
return ok && fr == FR_OK;
}
bool FileStore::Seek(FilePosition pos)
{
if (!inUse)
{
platform->Message(GENERIC_MESSAGE, "Error: Attempt to seek on a non-open file.\n");
return false;
}
if (writing)
{
WriteBuffer();
}
FRESULT fr = f_lseek(&file, pos);
bufferPointer = (writing) ? 0 : FileBufLen;
return fr == FR_OK;
}
FilePosition FileStore::Position() const
{
FilePosition pos = file.fptr;
if (writing)
{
pos += bufferPointer;
}
else if (bufferPointer < lastBufferEntry)
{
pos -= (lastBufferEntry - bufferPointer);
}
return pos;
}
#if 0 // not currently used
bool FileStore::GoToEnd()
{
return Seek(Length());
}
#endif
FilePosition FileStore::Length() const
{
if (!inUse)
{
platform->Message(GENERIC_MESSAGE, "Error: Attempt to size non-open file.\n");
return 0;
}
return file.fsize;
}
float FileStore::FractionRead() const
{
FilePosition len = Length();
if (len == 0)
{
return 0.0;
}
return (float)Position() / (float)len;
}
uint8_t FileStore::Status()
{
if (!inUse)
return (uint8_t)IOStatus::nothing;
if (lastBufferEntry == FileBufLen)
return (uint8_t)IOStatus::byteAvailable;
if (bufferPointer < lastBufferEntry)
return (uint8_t)IOStatus::byteAvailable;
return (uint8_t)IOStatus::nothing;
}
bool FileStore::ReadBuffer()
{
FRESULT readStatus = f_read(&file, GetBuffer(), FileBufLen, &lastBufferEntry); // Read a chunk of file
if (readStatus != FR_OK)
{
platform->Message(GENERIC_MESSAGE, "Error: Cannot read file.\n");
return false;
}
bufferPointer = 0;
return true;
}
// Single character read via the buffer
bool FileStore::Read(char& b)
{
if (!inUse)
{
platform->Message(GENERIC_MESSAGE, "Error: Attempt to read from a non-open file.\n");
return false;
}
if (bufferPointer >= FileBufLen)
{
bool ok = ReadBuffer();
if (!ok)
{
return false;
}
}
if (bufferPointer >= lastBufferEntry)
{
b = 0; // Good idea?
return false;
}
b = (char) GetBuffer()[bufferPointer];
bufferPointer++;
return true;
}
// Block read, doesn't use the buffer
// Returns the number of bytes read or -1 if the read process failed
int FileStore::Read(char* extBuf, size_t nBytes)
{
if (!inUse)
{
platform->Message(GENERIC_MESSAGE, "Error: Attempt to read from a non-open file.\n");
return -1;
}
bufferPointer = FileBufLen; // invalidate the buffer
UINT bytes_read;
FRESULT readStatus = f_read(&file, extBuf, nBytes, &bytes_read);
if (readStatus != FR_OK)
{
platform->Message(GENERIC_MESSAGE, "Error: Cannot read file.\n");
return -1;
}
return (int)bytes_read;
}
bool FileStore::WriteBuffer()
{
if (bufferPointer != 0)
{
if (!InternalWriteBlock((const char*)GetBuffer(), bufferPointer))
{
return false;
}
bufferPointer = 0;
}
return true;
}
bool FileStore::Write(char b)
{
if (!inUse)
{
platform->Message(GENERIC_MESSAGE, "Error: Attempt to write byte to a non-open file.\n");
return false;
}
GetBuffer()[bufferPointer] = b;
bufferPointer++;
if (bufferPointer >= FileBufLen)
{
return WriteBuffer();
}
return true;
}
bool FileStore::Write(const char* b)
{
return Write(b, strlen(b));
}
// Direct block write that bypasses the buffer. Used when uploading files.
bool FileStore::Write(const char *s, size_t len)
{
if (!inUse)
{
platform->Message(GENERIC_MESSAGE, "Error: Attempt to write block to a non-open file.\n");
return false;
}
if (!WriteBuffer())
{
return false;
}
return InternalWriteBlock(s, len);
}
bool FileStore::InternalWriteBlock(const char *s, size_t len)
{
size_t 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(GENERIC_MESSAGE, "Error: Cannot write to file. Disc may be full.\n");
return false;
}
return true;
}
bool FileStore::Flush()
{
if (!inUse)
{
platform->Message(GENERIC_MESSAGE, "Error: Attempt to flush a non-open file.\n");
return false;
}
if (!WriteBuffer())
{
return false;
}
return f_sync(&file) == FR_OK;
}
float FileStore::GetAndClearLongestWriteTime()
{
float ret = (float)longestWriteTime/1000.0;
longestWriteTime = 0;
return ret;
}
// End