fsfw/tmstorage/TmStoreManager.cpp
2016-06-15 23:48:49 +02:00

512 lines
15 KiB
C++

/*
* TmStoreManager.cpp
*
* Created on: 18.02.2015
* Author: baetz
*/
#include <framework/tmstorage/TmStoreManager.h>
#include <framework/tmstorage/TmStoreMessage.h>
#include <framework/tmtcpacket/SpacePacketBase.h>
#include <framework/tmtcservices/AcceptsTelemetryIF.h>
#include <framework/tmtcservices/TmTcMessage.h>
TmStoreManager::TmStoreManager(object_id_t objectId, object_id_t setDumpTarget,
uint8_t setVC, uint32_t setTimeoutMs) :
SystemObject(objectId), backend(NULL), tmForwardStore(NULL), dumpTarget(
setDumpTarget), virtualChannel(setVC), fetchState(
NOTHING_FETCHED), addressOfFetchCandidate(0), deletionStarted(
false), lastAddressToDelete(0), pendingPacketsToDelete(0), state(IDLE), storingEnabled(
false), timeoutMs(setTimeoutMs), ipcStore(NULL), downlinkedPacketsCount(
0), fullEventThrown(false) {
}
TmStoreManager::~TmStoreManager() {
}
ReturnValue_t TmStoreManager::initialize() {
ReturnValue_t result = SystemObject::initialize();
if (result != RETURN_OK) {
return result;
}
AcceptsTelemetryIF* telemetryDestimation = objectManager->get<
AcceptsTelemetryIF>(dumpTarget);
if (telemetryDestimation == NULL) {
return RETURN_FAILED;
}
tmQueue.setDefaultDestination(
telemetryDestimation->getReportReceptionQueue(virtualChannel));
tmForwardStore = objectManager->get<StorageManagerIF>(objects::TM_STORE);
if (tmForwardStore == NULL) {
return RETURN_FAILED;
}
result = backend->initialize();
if (result != HasReturnvaluesIF::RETURN_OK) {
return result;
}
ipcStore = objectManager->get<StorageManagerIF>(objects::IPC_STORE);
if (ipcStore == NULL) {
return RETURN_FAILED;
}
return matcher.initialize();
}
ReturnValue_t TmStoreManager::fetchPackets(ApidSsc start, ApidSsc end) {
//Check mode, store ids, start fetching.
if (state != IDLE) {
return BUSY;
}
if (start.apid < SpacePacketBase::LIMIT_APID) {
firstPacketToFetch = start;
fetchCandidate.apid = start.apid;
fetchState = NOTHING_FETCHED;
} else {
firstPacketToFetch.apid = SpacePacketBase::LIMIT_APID;
firstPacketToFetch.ssc = 0;
//There's no start defined, so we're in range automatically.
fetchState = IN_RANGE;
}
if (end.apid < SpacePacketBase::LIMIT_APID) {
lastPacketToFetch = end;
} else {
lastPacketToFetch.apid = SpacePacketBase::LIMIT_APID;
lastPacketToFetch.ssc = SpacePacketBase::LIMIT_SEQUENCE_COUNT;
}
//Advanced: start fetching at a certain position
ReturnValue_t result = backend->fetchPackets();
if (result != RETURN_OK) {
return result;
}
downlinkedPacketsCount = 0;
state = RETRIEVING_PACKETS;
return result;
}
ReturnValue_t TmStoreManager::performOperation() {
backend->performOperation();
checkCommandQueue();
return RETURN_OK;
}
ReturnValue_t TmStoreManager::packetRetrieved(TmPacketMinimal* packet,
uint32_t address, bool isLastPacket) {
ReturnValue_t result = RETURN_FAILED;
switch (state) {
case DELETING_PACKETS:
result = checkDeletionLimit(packet, address);
break;
case RETRIEVING_PACKETS:
result = checkRetrievalLimit(packet, address);
break;
case GET_OLDEST_PACKET_INFO: {
backend->deleteContent(true, lastAddressToDelete,
pendingPacketsToDelete, packet);
result = LAST_PACKET_FOUND;
}
break;
default:
triggerEvent(TmStoreBackendIF::UNEXPECTED_MSG, 0, packet->getPacketSequenceCount());
break;
}
switch (result) {
case RETURN_OK:
//We go on. But if lastPacket...
if (isLastPacket) {
//... we'll stop successfully.
replySuccess();
if (state == DELETING_PACKETS) {
//Store is completely deleted, so we can reset oldest and newest packet.
restDownlinkedPacketCount();
backend->resetStore();
}
state = IDLE;
}
break;
case LAST_PACKET_FOUND:
//All ok
replySuccess();
state = IDLE;
break;
case STOP_FETCH:
//This means, we started a new fetch. Returning STOP_FETCH will stop current scan. Stay in state.
break;
default:
//Some error occurred.
replyFailure(result);
state = IDLE;
break;
}
return result;
}
ReturnValue_t TmStoreManager::checkRetrievalLimit(TmPacketMinimal* packet,
uint32_t address) {
switch (fetchState) {
case NOTHING_FETCHED:
case BEFORE_RANGE:
if (packet->getAPID() != firstPacketToFetch.apid) {
//Dump all packets of unknown APID.
dumpPacket(packet);
return RETURN_OK;
}
if (packet->getPacketSequenceCount() < firstPacketToFetch.ssc) {
addressOfFetchCandidate = address;
fetchCandidate.ssc = packet->getPacketSequenceCount();
fetchState = BEFORE_RANGE;
return RETURN_OK;
} else if ((packet->getPacketSequenceCount() == firstPacketToFetch.ssc)
|| (fetchState == NOTHING_FETCHED)) {
//We have either found the right packet or one with higher count without having an older packet. So dump.
fetchState = IN_RANGE;
dumpPacket(packet);
if (packet->getPacketSequenceCount() < lastPacketToFetch.ssc) {
return RETURN_OK;
} else {
return LAST_PACKET_FOUND;
}
} else {
//We're in range, but the expected SSC is not in store. So we shall restart from the first older packet.
ReturnValue_t result = backend->fetchPackets(true,
addressOfFetchCandidate);
if (result != HasReturnvaluesIF::RETURN_OK) {
return result;
}
fetchState = NOTHING_FETCHED;
firstPacketToFetch = fetchCandidate;
return STOP_FETCH;
}
case IN_RANGE:
dumpPacket(packet);
if ((packet->getAPID() == lastPacketToFetch.apid)
&& (packet->getPacketSequenceCount() >= lastPacketToFetch.ssc)) {
return LAST_PACKET_FOUND;
} else {
return RETURN_OK;
}
default:
break;
}
return RETURN_FAILED;
}
ReturnValue_t TmStoreManager::deletePackets(ApidSsc upTo) {
if (state != IDLE) {
return BUSY;
}
if (upTo.apid >= SpacePacketBase::LIMIT_APID) {
ReturnValue_t result = backend->deleteContent();
if (result != HasReturnvaluesIF::RETURN_OK) {
return result;
}
restDownlinkedPacketCount();
return result;
} else {
state = DELETING_PACKETS;
restDownlinkedPacketCount();
lastPacketToDelete = upTo;
deletionStarted = false;
pendingPacketsToDelete = 0;
return backend->fetchPackets();
}
}
ReturnValue_t TmStoreManager::checkDeletionLimit(TmPacketMinimal* packet,
uint32_t address) {
uint32_t addressOfLastByte = address + packet->getFullSize();
if (packet->getAPID() == lastPacketToDelete.apid) {
if (packet->getPacketSequenceCount() < lastPacketToDelete.ssc) {
if (deletionStarted) {
backend->deleteContent(true, lastAddressToDelete,
pendingPacketsToDelete, packet);
pendingPacketsToDelete = 0;
} else {
deletionStarted = true;
}
pendingPacketsToDelete++;
lastAddressToDelete = addressOfLastByte;
return RETURN_OK;
} else if (packet->getPacketSequenceCount() == lastPacketToDelete.ssc) {
lastAddressToDelete = addressOfLastByte;
pendingPacketsToDelete++;
state = GET_OLDEST_PACKET_INFO;
return RETURN_OK;
} else {
return LAST_PACKET_FOUND;
}
} else {
lastAddressToDelete = addressOfLastByte;
pendingPacketsToDelete++;
return RETURN_OK;
}
}
void TmStoreManager::setBackend(TmStoreBackendIF* backend) {
this->backend = backend;
}
TmStoreBackendIF* TmStoreManager::getBackend() const {
return backend;
}
ReturnValue_t TmStoreManager::checkPacket(SpacePacketBase* tmPacket) {
if (!storingEnabled) {
return RETURN_OK;
}
TmPacketMinimal testPacket(tmPacket->getWholeData());
if (matcher.match(&testPacket)) {
ReturnValue_t result = backend->storePacket(&testPacket);
if (result != HasReturnvaluesIF::RETURN_OK) {
//Generate only one event to avoid full event store loop.
if (!fullEventThrown) {
triggerEvent(TmStoreBackendIF::STORING_FAILED, result, testPacket.getPacketSequenceCount());
fullEventThrown = true;
}
} else {
fullEventThrown = false;
}
return result;
} else {
return RETURN_OK;
}
}
void TmStoreManager::dumpPacket(SpacePacketBase* packet) {
store_address_t forwardStoreId;
TmTcMessage message;
ReturnValue_t result = tmForwardStore->addData(&forwardStoreId,
packet->getWholeData(), packet->getFullSize());
if (result != RETURN_OK) {
triggerEvent(TmStoreBackendIF::TM_DUMP_FAILED, result, packet->getPacketSequenceCount());
return;
}
message.setStorageId(forwardStoreId);
result = tmQueue.sendToDefault(&message);
if (result != RETURN_OK) {
triggerEvent(TmStoreBackendIF::TM_DUMP_FAILED, result, packet->getPacketSequenceCount());
tmForwardStore->deleteData(forwardStoreId);
return;
}
downlinkedPacketsCount.entry++;
}
bool TmStoreManager::isEnabled() const {
return storingEnabled;
}
void TmStoreManager::setEnabled(bool enabled) {
storingEnabled = enabled;
}
void TmStoreManager::checkCommandQueue() {
if (state != IDLE) {
if (timer.hasTimedOut()) {
replyFailure(TIMEOUT);
state = IDLE;
}
return;
}
CommandMessage message;
for (ReturnValue_t result = commandQueue.receiveMessage(&message);
result == RETURN_OK;
result = commandQueue.receiveMessage(&message)) {
switch (message.getCommand()) {
case TmStoreMessage::ENABLE_STORING:
setEnabled(TmStoreMessage::getEnableStoring(&message));
replySuccess();
break;
case TmStoreMessage::CHANGE_SELECTION_DEFINITION: {
uint32_t errorCount = 0;
result = changeSelectionDefinition(
TmStoreMessage::getAddToSelection(&message),
TmStoreMessage::getStoreId(&message), &errorCount);
if (result != RETURN_OK) {
replyFailure(result, errorCount);
} else {
replySuccess();
}
break;
}
case TmStoreMessage::DOWNLINK_STORE_CONTENT:
result = fetchPackets(TmStoreMessage::getPacketId1(&message),
TmStoreMessage::getPacketId2(&message));
if (result != RETURN_OK) {
if (result == TmStoreBackendIF::EMPTY) {
replySuccess();
} else {
replyFailure(result);
}
} else {
timer.setTimeout(timeoutMs);
}
break;
case TmStoreMessage::DELETE_STORE_CONTENT:
result = deletePackets(TmStoreMessage::getPacketId1(&message));
if (result == RETURN_OK) {
if (state == IDLE) {
//We're finished
replySuccess();
} else {
timer.setTimeout(timeoutMs);
}
} else {
replyFailure(result);
}
break;
case TmStoreMessage::REPORT_SELECTION_DEFINITION:
result = sendMatchTree();
if (result != RETURN_OK) {
replyFailure(result);
}
break;
case TmStoreMessage::REPORT_STORE_CATALOGUE:
result = sendStatistics();
if (result != RETURN_OK) {
replyFailure(result);
}
break;
default:
replyFailure(CommandMessage::UNKNOW_COMMAND, message.getCommand());
break;
}
}
}
void TmStoreManager::replySuccess() {
CommandMessage reply(CommandMessage::REPLY_COMMAND_OK, 0, 0);
commandQueue.reply(&reply);
}
MessageQueueId_t TmStoreManager::getCommandQueue() {
return commandQueue.getId();
}
void TmStoreManager::replyFailure(ReturnValue_t errorCode, uint32_t parameter) {
CommandMessage reply(CommandMessage::REPLY_REJECTED, errorCode, parameter);
commandQueue.reply(&reply);
}
ReturnValue_t TmStoreManager::changeSelectionDefinition(bool addSelection,
store_address_t storeId, uint32_t* errorCount) {
const uint8_t* pData;
uint32_t size = 0;
ReturnValue_t result = ipcStore->getData(storeId, &pData, &size);
if (result != HasReturnvaluesIF::RETURN_OK) {
return result;
}
int32_t iSize = size;
ChangeSelectionDefinition content;
result = content.deSerialize(&pData, &iSize, true);
if (result != HasReturnvaluesIF::RETURN_OK) {
return result;
}
ReturnValue_t totalResult = RETURN_OK;
if (content.serviceList.size == 0) {
return updateMatch(addSelection, content.apid, NO_SERVICE,
NO_SUBSERVICE);
}
for (auto iter = content.serviceList.begin();
iter != content.serviceList.end(); iter++) {
if (iter->subservices.size == 0) {
result = updateMatch(addSelection, content.apid,
iter->service, NO_SUBSERVICE);
if (result != HasReturnvaluesIF::RETURN_OK) {
totalResult = result;
(*errorCount)++;
}
} else {
for (auto iter2 = iter->subservices.begin();
iter2 != iter->subservices.end(); iter2++) {
result = updateMatch(addSelection, content.apid,
iter->service, *(iter2.value));
if (result != HasReturnvaluesIF::RETURN_OK) {
totalResult = result;
(*errorCount)++;
}
}
}
}
return totalResult;
}
ReturnValue_t TmStoreManager::sendMatchTree() {
store_address_t storeId;
uint8_t* buffer;
uint32_t maxSize = matcher.getSerializedSize();
ReturnValue_t result = ipcStore->getFreeElement(&storeId, maxSize, &buffer);
if (result != HasReturnvaluesIF::RETURN_OK) {
return result;
}
uint32_t size = 0;
result = matcher.serialize(&buffer, &size, maxSize, true);
if (result != HasReturnvaluesIF::RETURN_OK) {
ipcStore->deleteData(storeId);
return result;
}
CommandMessage reply;
TmStoreMessage::setSelectionDefinitionReportMessage(&reply, storeId);
result = commandQueue.reply(&reply);
if (result != RETURN_OK) {
ipcStore->deleteData(storeId);
}
return result;
}
void TmStoreManager::restDownlinkedPacketCount() {
downlinkedPacketsCount = 0;
}
ReturnValue_t TmStoreManager::updateMatch(bool addSelection, uint16_t apid, uint8_t serviceType,
uint8_t serviceSubtype) {
return matcher.changeMatch(addSelection, apid, serviceType, serviceSubtype);
}
void TmStoreManager::handleRetrievalFailed(ReturnValue_t errorCode,
uint32_t parameter1, uint32_t parameter2) {
restDownlinkedPacketCount();
state = IDLE;
replyFailure(errorCode, parameter1);
}
ReturnValue_t TmStoreManager::sendStatistics() {
LinkedElement<SerializeIF> linkedOldestPacket(backend->getOldestPacket());
LinkedElement<SerializeIF> linkedYoungestPacket(
backend->getYoungestPacket());
SerializeElement<float> percentageFilled(backend->getPercentageFilled());
float pDownlinked =
(backend->getStoredPacketsCount() != 0) ?
(float) downlinkedPacketsCount
/ (float) backend->getStoredPacketsCount() :
0.0;
SerializeElement<float> percentageDownlinked(pDownlinked);
SerializeElement<uint32_t> storedPacketsCount(
backend->getStoredPacketsCount());
linkedOldestPacket.setNext(&linkedYoungestPacket);
linkedYoungestPacket.setNext(&percentageFilled);
percentageFilled.setNext(&percentageDownlinked);
percentageDownlinked.setNext(&storedPacketsCount);
storedPacketsCount.setNext(&downlinkedPacketsCount);
store_address_t storeId;
uint8_t* buffer;
uint32_t maxSize = SerialLinkedListAdapter<SerializeIF>::getSerializedSize(
&linkedOldestPacket);
ReturnValue_t result = ipcStore->getFreeElement(&storeId, maxSize, &buffer);
if (result != HasReturnvaluesIF::RETURN_OK) {
return result;
}
uint32_t size = 0;
result = SerialLinkedListAdapter<SerializeIF>::serialize(
&linkedOldestPacket, &buffer, &size, maxSize, true);
if (result != HasReturnvaluesIF::RETURN_OK) {
ipcStore->deleteData(storeId);
return result;
}
CommandMessage reply;
TmStoreMessage::setStoreCatalogueReportMessage(&reply, storeId);
result = commandQueue.reply(&reply);
if (result != RETURN_OK) {
ipcStore->deleteData(storeId);
}
return result;
}