1
0
forked from fsfw/fsfw
fsfw/tmstorage/TmStore.cpp
2016-06-15 23:48:49 +02:00

705 lines
19 KiB
C++

/*
* TmStore.cpp
*
* Created on: 05.02.2015
* Author: baetz
*/
#include <framework/memory/AcceptsMemoryMessagesIF.h>
#include <framework/memory/MemoryMessage.h>
#include <framework/serviceinterface/ServiceInterfaceStream.h>
#include <framework/tmstorage/TmStore.h>
#include <framework/tmtcservices/AcceptsTelemetryIF.h>
#include <framework/tmtcservices/TmTcMessage.h>
TmStore::TmStore(TmStoreFrontendIF* owner, object_id_t memoryObject,
uint32_t storeAddress, uint32_t size, bool overwriteOld,
uint32_t maxDumpPacketsPerCylce, uint32_t maxDumpedBytesPerCylce,
uint32_t updateRemotePtrsMs, uint32_t chunkSize) :
owner(owner), eventProxy(NULL), info(&oldestPacket, &newestPacket), infoSize(
info.getSerializedSize()), ring(storeAddress + infoSize,
size - infoSize, overwriteOld), state(OFF), writeState(
WRITE_IDLE), readState(READ_IDLE), memoryObject(memoryObject), memoryQueue(), ipcStore(
NULL), timer(), pendingDataToWrite(0), maximumAmountToRead(0), storedPacketCounter(0), pendingStoredPackets(0), splitWrite(
false), splitRead(NO_SPLIT), splitReadTotalSize(0), tmBuffer(chunkSize), dumpBuffer(
NULL), pointerAddress(storeAddress), updateRemotePtrsMs(
updateRemotePtrsMs), maxDumpPacketsPerCycle(
maxDumpPacketsPerCylce), maxDumpedBytesPerCycle(maxDumpedBytesPerCylce), tryToSetStoreInfo(false) {
dumpBuffer = new uint8_t[chunkSize];
}
TmStore::~TmStore() {
delete[] dumpBuffer;
}
ReturnValue_t TmStore::performOperation() {
doStateMachine();
checkMemoryQueue();
if (localBufferTimer.hasTimedOut() && (state == READY)) {
sendTmBufferToStore();
localBufferTimer.setTimeout(LOCAL_BUFFER_TIMEOUT_MS);
}
return RETURN_OK;
}
ReturnValue_t TmStore::storePacket(TmPacketMinimal* tmPacket) {
if (tmPacket->getFullSize() > chunkSize()) {
return TOO_LARGE;
}
ReturnValue_t result = RETURN_FAILED;
if (hasEnoughSpaceFor(tmPacket->getFullSize())) {
localBufferTimer.setTimeout(LOCAL_BUFFER_TIMEOUT_MS);
result = storeOrForwardPacket(tmPacket->getWholeData(),
tmPacket->getFullSize());
} else {
result = handleFullStore(tmPacket->getWholeData(),
tmPacket->getFullSize());
}
if (result == RETURN_OK) {
pendingNewestPacket.setContent(tmPacket);
if (!pendingOldestPacket.isValid()) {
pendingOldestPacket.setContent(tmPacket);
}
pendingStoredPackets++;
}
return result;
}
ReturnValue_t TmStore::fetchPackets(bool useAddress, uint32_t startAtAddress) {
if (!isReady()) {
return NOT_READY;
}
if (ring.isEmpty()) {
return EMPTY;
}
if (readState != READ_IDLE) {
return BUSY;
}
// Never download more than the currently available data.
maximumAmountToRead = ring.availableReadData();
if (useAddress) {
ring.setRead(startAtAddress, TEMP_READ_PTR);
} else {
ring.setRead(ring.getRead(READ_PTR), TEMP_READ_PTR);
}
ReturnValue_t result = requestChunkOfData();
if (result != RETURN_OK) {
return result;
}
setReadState(DUMPING_PACKETS);
return RETURN_OK;
}
ReturnValue_t TmStore::requestStoreInfo() {
CommandMessage message;
MemoryMessage::setMemoryDumpCommand(&message, pointerAddress, infoSize);
ReturnValue_t result = memoryQueue.sendToDefault(&message);
if (result != RETURN_OK) {
return result;
}
return RETURN_OK;
}
ReturnValue_t TmStore::setStoreInfo() {
store_address_t storeId;
uint8_t* data = NULL;
uint32_t size = 0;
ReturnValue_t result = ipcStore->getFreeElement(&storeId, infoSize, &data);
if (result != RETURN_OK) {
return result;
}
info.setContent(ring.getRead(), ring.getWrite(), storedPacketCounter);
result = info.serialize(&data, &size, infoSize, true);
if (result != RETURN_OK) {
ipcStore->deleteData(storeId);
return result;
}
CommandMessage message;
MemoryMessage::setMemoryLoadCommand(&message, pointerAddress, storeId);
result = memoryQueue.sendToDefault(&message);
if (result != RETURN_OK) {
ipcStore->deleteData(storeId);
return result;
}
return RETURN_OK;
}
void TmStore::doStateMachine() {
switch (state) {
case OFF:
break;
case STARTUP: {
ReturnValue_t result = requestStoreInfo();
if (result == RETURN_OK) {
setState(FETCH_STORE_INFORMATION);
setReadState(FETCHING_STORE_INFO);
timer.setTimeout(DEFAULT_TIMEOUT_MS);
} else {
eventProxy->forwardEvent(STORE_INIT_FAILED, result, 0);
setState(OFF);
}
}
break;
case FETCH_STORE_INFORMATION:
if (timer.hasTimedOut()) {
eventProxy->forwardEvent(STORE_INIT_FAILED, TIMEOUT, 1);
setState(OFF);
}
break;
case READY:
if (tryToSetStoreInfo) {
if ((writeState == WRITE_IDLE) && (readState == READ_IDLE)) {
if (setStoreInfo() == RETURN_OK) {
setWriteState(SETTING_STORE_INFO);
}
tryToSetStoreInfo = false;
}
}
break;
default:
//do nothing.
break;
}
}
void TmStore::checkMemoryQueue() {
CommandMessage message;
for (ReturnValue_t result = memoryQueue.receiveMessage(&message);
result == RETURN_OK;
result = memoryQueue.receiveMessage(&message)) {
switch (message.getCommand()) {
case CommandMessage::REPLY_COMMAND_OK:
handleLoadSuccess();
break;
case MemoryMessage::REPLY_MEMORY_DUMP:
handleDump(MemoryMessage::getStoreID(&message));
break;
case MemoryMessage::REPLY_MEMORY_FAILED:
case CommandMessage::REPLY_REJECTED: // REPLY_REJECTED uses the same protocol.
switch (MemoryMessage::getInitialCommand(&message)) {
case MemoryMessage::CMD_MEMORY_LOAD:
handleLoadFailed(MemoryMessage::getErrorCode(&message));
break;
case MemoryMessage::CMD_MEMORY_DUMP:
handleDumpFailed(MemoryMessage::getErrorCode(&message));
break;
default:
debug << "TmStore: Unknown InitialCommand: "
<< MemoryMessage::getInitialCommand(&message)
<< std::endl;
break;
}
break;
default:
eventProxy->forwardEvent(UNEXPECTED_MSG, 0, 0);
break;
}
}
}
void TmStore::handleLoadSuccess() {
switch (writeState) {
case SETTING_STORE_INFO:
setWriteState(WRITE_IDLE);
break;
case SENDING_PACKETS:
if (splitWrite) {
ReturnValue_t result = sendRemainingTmPiece();
if (result != RETURN_OK) {
eventProxy->forwardEvent(STORE_SEND_WRITE_FAILED, result, 0);
resetStore(true);
pendingDataToWrite = 0;
}
splitWrite = false;
} else {
ring.writeData(pendingDataToWrite);
pendingDataToWrite = 0;
storedPacketCounter += pendingStoredPackets;
newestPacket.setContent(&pendingNewestPacket);
if (!oldestPacket.isValid()) {
oldestPacket.setContent(&pendingOldestPacket);
}
pendingStoredPackets = 0;
setWriteState(WRITE_IDLE);
tryToSetStoreInfo = true;
}
break;
default:
eventProxy->forwardEvent(UNEXPECTED_MSG, 0, 1);
}
}
void TmStore::handleDump(store_address_t storeId) {
const uint8_t* buffer = NULL;
uint32_t size = 0;
ReturnValue_t result = ipcStore->getData(storeId, &buffer, &size);
if (result != RETURN_OK) {
return;
}
switch (readState) {
case FETCHING_STORE_INFO:
readStoreInfo(buffer, size);
break;
case DUMPING_PACKETS:
case DELETING_OLD:
case DELETING_MORE:
handleSplitRead(buffer, size);
break;
default:
setReadState(READ_IDLE);
//No break.
case READ_IDLE:
eventProxy->forwardEvent(UNEXPECTED_MSG, 0, 2);
break;
}
ipcStore->deleteData(storeId);
}
void TmStore::handleSplitRead(const uint8_t* buffer, uint32_t size) {
switch (splitRead) {
case NO_SPLIT:
if (readState == DELETING_OLD || readState == DELETING_MORE) {
deleteOld(buffer, size);
} else {
dumpPackets(buffer, size);
}
break;
case SPLIT_STARTED:
if (size <= chunkSize()) {
ReturnValue_t result = requestRemainingDumpPiece(size);
if (result == RETURN_OK) {
memcpy(dumpBuffer, buffer, size);
splitRead = ONE_RECEIVED;
} else {
eventProxy->forwardEvent(STORE_SEND_READ_FAILED, result, 0);
splitRead = NO_SPLIT;
maximumAmountToRead = 0;
}
} else {
eventProxy->forwardEvent(STORE_SEND_READ_FAILED, TOO_LARGE, 0);
splitRead = NO_SPLIT;
maximumAmountToRead = 0;
}
break;
case ONE_RECEIVED:
memcpy(&dumpBuffer[splitReadTotalSize - size], buffer, size);
if (readState == DELETING_OLD || readState == DELETING_MORE) {
deleteOld(dumpBuffer, splitReadTotalSize);
} else {
dumpPackets(dumpBuffer, splitReadTotalSize);
}
splitRead = NO_SPLIT;
break;
}
}
void TmStore::handleLoadFailed(ReturnValue_t errorCode) {
eventProxy->forwardEvent(STORE_WRITE_FAILED, errorCode, 0);
setWriteState(WRITE_IDLE);
setState(READY);
splitWrite = false;
pendingDataToWrite = 0;
}
void TmStore::handleDumpFailed(ReturnValue_t errorCode) {
switch (readState) {
case FETCHING_STORE_INFO:
eventProxy->forwardEvent(STORE_INIT_FAILED, errorCode, 2);
setState(OFF);
break;
case DUMPING_PACKETS:
owner->handleRetrievalFailed(errorCode);
//No break
default:
case READ_IDLE:
case DELETING_OLD:
case DELETING_MORE:
eventProxy->forwardEvent(STORE_READ_FAILED, errorCode, 0);
setState(READY);
break;
}
setReadState(READ_IDLE);
splitRead = NO_SPLIT;
maximumAmountToRead = 0;
}
void TmStore::readStoreInfo(const uint8_t* buffer, uint32_t size) {
setReadState(READ_IDLE);
if (::Calculate_CRC(buffer, infoSize) != 0) {
eventProxy->forwardEvent(STORE_INIT_EMPTY, 0, 0);
setState(READY);
return;
}
int32_t deSize = size;
ReturnValue_t result = info.deSerialize(&buffer, &deSize, true);
if (result != RETURN_OK) {
//An error here is extremely unlikely.
eventProxy->forwardEvent(STORE_INIT_FAILED, result, 3);
setState(OFF);
return;
}
eventProxy->forwardEvent(STORE_INIT_DONE);
ring.setWrite(info.writeP);
ring.setRead(info.readP);
storedPacketCounter = info.storedPacketCount;
setState(READY);
}
void TmStore::dumpPackets(const uint8_t* buffer, uint32_t size) {
ReturnValue_t result = RETURN_OK;
uint32_t tempSize = 0;
uint32_t dumpedPacketCounter = 0;
uint32_t dumpedBytesCounter = 0;
while (size >= TmPacketMinimal::MINIMUM_SIZE
&& dumpedPacketCounter < maxDumpPacketsPerCycle
&& dumpedBytesCounter < maxDumpedBytesPerCycle
&& result == RETURN_OK) {
TmPacketMinimal packet(buffer);
tempSize = packet.getFullSize();
if (tempSize > size) {
if (tempSize > ring.availableReadData(READ_PTR) || tempSize > chunkSize()) {
owner->handleRetrievalFailed(DUMP_ERROR);
eventProxy->forwardEvent(STORE_CONTENT_CORRUPTED, 0, tempSize);
result = DUMP_ERROR;
}
break;
}
size -= tempSize;
buffer += tempSize;
dumpedBytesCounter += tempSize;
if (maximumAmountToRead > tempSize) {
result = owner->packetRetrieved(&packet,
ring.getRead(TEMP_READ_PTR));
maximumAmountToRead -= tempSize;
} else {
//packet is complete and last.
result = owner->packetRetrieved(&packet,
ring.getRead(TEMP_READ_PTR), true);
maximumAmountToRead = 0;
break;
}
if (result == RETURN_OK) {
ring.readData(tempSize, TEMP_READ_PTR);
dumpedPacketCounter++;
}
}
if (result == RETURN_OK && maximumAmountToRead != 0) {
result = requestChunkOfData();
if (result != HasReturnvaluesIF::RETURN_OK) {
owner->handleRetrievalFailed(result);
}
} else {
setReadState(READ_IDLE);
}
}
ReturnValue_t TmStore::sendDataToTmStore(const uint8_t* data, uint32_t size) {
//Unfortunately, cleanup is quite complex.
store_address_t storeId;
ReturnValue_t result = RETURN_FAILED;
if (size > ring.writeTillWrap()) {
//Two-folded write
result = ipcStore->addData(&storeId, data, ring.writeTillWrap());
if (result != RETURN_OK) {
return result;
}
splitWrite = true;
result = ipcStore->addData(&splitWriteStoreId,
data + ring.writeTillWrap(), size - ring.writeTillWrap());
if (result != RETURN_OK) {
ipcStore->deleteData(storeId);
}
} else {
//Normal write
splitWrite = false;
result = ipcStore->addData(&storeId, data, size);
}
if (result != RETURN_OK) {
splitWrite = false;
return result;
}
CommandMessage message;
MemoryMessage::setMemoryLoadCommand(&message, ring.getWrite(), storeId);
if ((result = memoryQueue.sendToDefault(&message)) == RETURN_OK) {
setWriteState(SENDING_PACKETS);
pendingDataToWrite = size;
} else {
if (splitWrite) {
ipcStore->deleteData(splitWriteStoreId);
}
splitWrite = false;
ipcStore->deleteData(storeId);
}
return result;
}
ReturnValue_t TmStore::requestChunkOfData() {
uint32_t readSize =
(maximumAmountToRead > chunkSize()) ?
chunkSize() : maximumAmountToRead;
CommandMessage message;
if (readSize > ring.readTillWrap(TEMP_READ_PTR)) {
//Wrap
splitReadTotalSize = readSize;
splitRead = SPLIT_STARTED;
MemoryMessage::setMemoryDumpCommand(&message,
ring.getRead(TEMP_READ_PTR), ring.readTillWrap(TEMP_READ_PTR));
} else {
splitRead = NO_SPLIT;
MemoryMessage::setMemoryDumpCommand(&message,
ring.getRead(TEMP_READ_PTR), readSize);
}
ReturnValue_t result = memoryQueue.sendToDefault(&message);
if (result != RETURN_OK) {
splitRead = NO_SPLIT;
maximumAmountToRead = 0;
}
return result;
}
ReturnValue_t TmStore::initialize() {
AcceptsMemoryMessagesIF* memoryTarget = objectManager->get<
AcceptsMemoryMessagesIF>(memoryObject);
if (memoryTarget == NULL) {
return RETURN_FAILED;
}
memoryQueue.setDefaultDestination(memoryTarget->getCommandQueue());
ipcStore = objectManager->get<StorageManagerIF>(objects::IPC_STORE);
if (ipcStore == NULL) {
return RETURN_FAILED;
}
//assert that Store is larger than local store and potential data on the way.
if (ring.availableWriteSpace(READ_PTR) < (2 * tmBuffer.maxSize())) {
//Ring buffer is too small.
return RETURN_FAILED;
}
//Ugly cast to have the correct link to eventProxy:
eventProxy = dynamic_cast<EventReportingProxyIF*>(owner);
if (eventProxy == NULL) {
return RETURN_FAILED;
}
return RETURN_OK;
}
ReturnValue_t TmStore::initializeStore() {
setState(STARTUP);
eventProxy->forwardEvent(STORE_INITIALIZE, 0, 0);
return RETURN_OK;
}
bool TmStore::isReady() {
return (state == READY);
}
ReturnValue_t TmStore::sendRemainingTmPiece() {
if (writeState != SENDING_PACKETS) {
return RETURN_FAILED;
}
CommandMessage message;
MemoryMessage::setMemoryLoadCommand(&message, ring.getStart(),
splitWriteStoreId);
return memoryQueue.sendToDefault(&message);
}
ReturnValue_t TmStore::requestRemainingDumpPiece(uint32_t firstPartSize) {
CommandMessage message;
MemoryMessage::setMemoryDumpCommand(&message, ring.getStart(),
splitReadTotalSize - firstPartSize);
return memoryQueue.sendToDefault(&message);
}
uint32_t TmStore::availableData() {
return ring.availableReadData();
}
ReturnValue_t TmStore::storeOrForwardPacket(const uint8_t* data,
uint32_t size) {
if (tmBuffer.remaining() >= size) {
memcpy(&tmBuffer[tmBuffer.size], data, size);
tmBuffer.size += size;
return RETURN_OK;
} else {
if (writeState != WRITE_IDLE) {
return BUSY;
}
if (!isReady()) {
return NOT_READY;
}
sendTmBufferToStore();
memcpy(tmBuffer.front(), data, size);
tmBuffer.size += size;
return RETURN_OK;
}
}
bool TmStore::hasEnoughSpaceFor(uint32_t size) {
//Correct size configuration is asserted in initialize().
if ((ring.availableWriteSpace(READ_PTR) - tmBuffer.size - pendingDataToWrite)
> size) {
return true;
} else {
return false;
}
}
ReturnValue_t TmStore::deleteContent(bool deletePart, uint32_t upToAddress,
uint32_t nDeletedPackets, TmPacketMinimal* newOldestPacket) {
if (deletePart) {
ring.setRead(upToAddress, READ_PTR);
storedPacketCounter -= nDeletedPackets;
if (newOldestPacket != NULL) {
oldestPacket.setContent(newOldestPacket);
}
} else {
resetStore();
}
tryToSetStoreInfo = true;
return RETURN_OK;
}
ReturnValue_t TmStore::handleFullStore(const uint8_t* data, uint32_t size) {
if (!isReady()) {
return NOT_READY;
}
if (!ring.overwritesOld()) {
sendTmBufferToStore();
return FULL;
}
ReturnValue_t result = FULL;
if ((writeState == WRITE_IDLE) && (readState == READ_IDLE)) {
sendTmBufferToStore();
maximumAmountToRead = chunkSize();
ring.setRead(ring.getRead(READ_PTR), TEMP_READ_PTR);
result = requestChunkOfData();
if (result != RETURN_OK) {
return result;
}
setReadState(DELETING_OLD);
//Store data in cleared store.
memcpy(tmBuffer.front(), data, size);
tmBuffer.size += size;
result = RETURN_OK;
} else if (readState == DELETING_OLD) {
//Try to store local as long as possible
if (tmBuffer.remaining() >= size) {
memcpy(&tmBuffer[tmBuffer.size], data, size);
tmBuffer.size += size;
result = RETURN_OK;
}
}
return result;
}
void TmStore::deleteOld(const uint8_t* buffer, uint32_t size) {
ReturnValue_t result = RETURN_OK;
uint32_t tempSize = 0;
while (size >= TmPacketMinimal::MINIMUM_SIZE && result == RETURN_OK) {
TmPacketMinimal packet(buffer);
tempSize = packet.getFullSize();
if (readState == DELETING_MORE) {
if (size < (TmPacketMinimal::MINIMUM_SIZE + tempSize)) {
oldestPacket.setContent(&packet);
break;
}
} else {
if (tempSize > size) {
//Don't delete the last packet half in store...
break;
}
}
result = ring.readData(tempSize, READ_PTR);
ring.readData(tempSize, TEMP_READ_PTR);
size -= tempSize;
buffer += tempSize;
storedPacketCounter--;
}
if (readState == DELETING_OLD) {
result = requestChunkOfData();
if (result != HasReturnvaluesIF::RETURN_OK) {
oldestPacket.reset();
eventProxy->forwardEvent(STORE_SEND_WRITE_FAILED, result, 1);
setReadState(READ_IDLE);
} else {
setReadState(DELETING_MORE);
}
} else {
setReadState(READ_IDLE);
owner->restDownlinkedPacketCount();
tryToSetStoreInfo = true;
}
}
void TmStore::sendTmBufferToStore() {
//No need to send empty data packet.
if (tmBuffer.size > 0) {
ReturnValue_t result = sendDataToTmStore(tmBuffer.front(), tmBuffer.size);
if (result != HasReturnvaluesIF::RETURN_OK) {
eventProxy->forwardEvent(STORE_SEND_WRITE_FAILED, result, 2);
}
tmBuffer.clear();
}
}
void TmStore::resetStore(bool resetWrite, bool resetRead) {
ring.clear();
setState(READY);
storedPacketCounter = 0;
clearPending();
tmBuffer.clear();
oldestPacket.reset();
newestPacket.reset();
tryToSetStoreInfo = true;
if (resetWrite) {
setWriteState(WRITE_IDLE);
splitWrite = false;
pendingDataToWrite = 0;
}
if (resetRead) {
maximumAmountToRead = 0;
splitRead = NO_SPLIT;
setReadState(READ_IDLE);
}
}
void TmStore::setState(State state) {
// debug << "TmStore::setState: " << state << std::endl;
this->state = state;
}
void TmStore::setWriteState(WriteState writeState) {
// debug << "TmStore::setWriteState: " << writeState << std::endl;
this->writeState = writeState;
}
void TmStore::setReadState(ReadState readState) {
// debug << "TmStore::setReadState: " << readState << std::endl;
this->readState = readState;
}
float TmStore::getPercentageFilled() const {
return ring.availableReadData(READ_PTR) / float(ring.maxSize());
}
uint32_t TmStore::getStoredPacketsCount() const {
return storedPacketCounter;
}
TmPacketInformation* TmStore::getOldestPacket() {
return &oldestPacket;
}
TmPacketInformation* TmStore::getYoungestPacket() {
return &newestPacket;
}
uint32_t TmStore::chunkSize() {
return tmBuffer.maxSize();
}
void TmStore::clearPending() {
pendingOldestPacket.reset();
pendingNewestPacket.reset();
pendingStoredPackets = 0;
}