add new VerificationReporterIF
This commit is contained in:
parent
332e9dbfd5
commit
75c824ec80
@ -33,6 +33,7 @@ enum framework_objects : object_id_t {
|
||||
TC_STORE = 0x534f0100,
|
||||
TM_STORE = 0x534f0200,
|
||||
TIME_STAMPER = 0x53500010,
|
||||
TC_VERIFICATOR = 0x53500020,
|
||||
|
||||
FSFW_OBJECTS_END = 0x53ffffff,
|
||||
NO_OBJECT = 0xFFFFFFFF
|
||||
|
@ -96,13 +96,13 @@ ReturnValue_t CService200ModeCommanding::handleReply(const CommandMessage *reply
|
||||
ReturnValue_t CService200ModeCommanding::prepareModeReply(const CommandMessage *reply,
|
||||
object_id_t objectId) {
|
||||
ModePacket modeReplyPacket(objectId, ModeMessage::getMode(reply), ModeMessage::getSubmode(reply));
|
||||
return tmHelper.sendTmPacket(Subservice::REPLY_MODE_REPLY, modeReplyPacket);
|
||||
return sendTmPacket(Subservice::REPLY_MODE_REPLY, modeReplyPacket);
|
||||
}
|
||||
|
||||
ReturnValue_t CService200ModeCommanding::prepareWrongModeReply(const CommandMessage *reply,
|
||||
object_id_t objectId) {
|
||||
ModePacket wrongModeReply(objectId, ModeMessage::getMode(reply), ModeMessage::getSubmode(reply));
|
||||
ReturnValue_t result = tmHelper.sendTmPacket(Subservice::REPLY_WRONG_MODE_REPLY, wrongModeReply);
|
||||
ReturnValue_t result = sendTmPacket(Subservice::REPLY_WRONG_MODE_REPLY, wrongModeReply);
|
||||
if (result == RETURN_OK) {
|
||||
// We want to produce an error here in any case because the mode was not correct
|
||||
return RETURN_FAILED;
|
||||
@ -113,8 +113,7 @@ ReturnValue_t CService200ModeCommanding::prepareWrongModeReply(const CommandMess
|
||||
ReturnValue_t CService200ModeCommanding::prepareCantReachModeReply(const CommandMessage *reply,
|
||||
object_id_t objectId) {
|
||||
CantReachModePacket cantReachModePacket(objectId, ModeMessage::getCantReachModeReason(reply));
|
||||
ReturnValue_t result =
|
||||
tmHelper.sendTmPacket(Subservice::REPLY_CANT_REACH_MODE, cantReachModePacket);
|
||||
ReturnValue_t result = sendTmPacket(Subservice::REPLY_CANT_REACH_MODE, cantReachModePacket);
|
||||
if (result == RETURN_OK) {
|
||||
// We want to produce an error here in any case because the mode was not reached
|
||||
return RETURN_FAILED;
|
||||
|
@ -102,5 +102,5 @@ ReturnValue_t CService201HealthCommanding::handleReply(const CommandMessage *rep
|
||||
auto health = static_cast<uint8_t>(HealthMessage::getHealth(reply));
|
||||
auto oldHealth = static_cast<uint8_t>(HealthMessage::getOldHealth(reply));
|
||||
HealthSetReply healthSetReply(health, oldHealth);
|
||||
return tmHelper.sendTmPacket(Subservice::REPLY_HEALTH_SET, healthSetReply);
|
||||
return sendTmPacket(Subservice::REPLY_HEALTH_SET, healthSetReply);
|
||||
}
|
||||
|
@ -15,11 +15,19 @@ Service17Test::~Service17Test() = default;
|
||||
ReturnValue_t Service17Test::handleRequest(uint8_t subservice) {
|
||||
switch (subservice) {
|
||||
case Subservice::CONNECTION_TEST: {
|
||||
return tmHelper.sendTmPacket(Subservice::CONNECTION_TEST_REPORT);
|
||||
ReturnValue_t result = tmHelper.prepareTmPacket(Subservice::CONNECTION_TEST_REPORT);
|
||||
if (result != HasReturnvaluesIF::RETURN_OK) {
|
||||
return result;
|
||||
}
|
||||
return tmHelper.storeAndSendTmPacket();
|
||||
}
|
||||
case Subservice::EVENT_TRIGGER_TEST: {
|
||||
triggerEvent(TEST, 1234, 5678);
|
||||
return tmHelper.sendTmPacket(Subservice::EVENT_TRIGGER_TEST);
|
||||
ReturnValue_t result = tmHelper.prepareTmPacket(Subservice::EVENT_TRIGGER_TEST);
|
||||
if (result != HasReturnvaluesIF::RETURN_OK) {
|
||||
return result;
|
||||
}
|
||||
return tmHelper.storeAndSendTmPacket();
|
||||
}
|
||||
default:
|
||||
return AcceptsTelecommandsIF::INVALID_SUBSERVICE;
|
||||
|
@ -176,8 +176,7 @@ ReturnValue_t Service20ParameterManagement::handleReply(const CommandMessage* re
|
||||
ParameterId_t parameterId = ParameterMessage::getParameterId(reply);
|
||||
ParameterDumpReply parameterReply(objectId, parameterId, parameterData.second.data(),
|
||||
parameterData.second.size());
|
||||
tmHelper.sendTmPacket(static_cast<uint8_t>(Subservice::PARAMETER_DUMP_REPLY), parameterReply);
|
||||
return HasReturnvaluesIF::RETURN_OK;
|
||||
return sendTmPacket(static_cast<uint8_t>(Subservice::PARAMETER_DUMP_REPLY), parameterReply);
|
||||
}
|
||||
default:
|
||||
return CommandingServiceBase::INVALID_REPLY;
|
||||
|
@ -148,7 +148,11 @@ void Service2DeviceAccess::sendWiretappingTm(CommandMessage* reply, uint8_t subs
|
||||
// Init our dummy packet and correct endianness of object ID before
|
||||
// sending it back.
|
||||
WiretappingPacket tmPacket(DeviceHandlerMessage::getDeviceObjectId(reply), data);
|
||||
tmHelper.sendTmPacket(subservice, tmPacket.objectId, tmPacket.data, size);
|
||||
result = sendTmPacket(subservice, tmPacket.objectId, tmPacket.data, size);
|
||||
if (result != retval::OK) {
|
||||
// TODO: Warning
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
MessageQueueId_t Service2DeviceAccess::getDeviceQueue() { return commandQueue->getId(); }
|
||||
|
@ -290,8 +290,7 @@ ReturnValue_t Service3Housekeeping::generateHkReply(const CommandMessage* hkMess
|
||||
}
|
||||
|
||||
HkPacket hkPacket(sid, resultPair.second.data(), resultPair.second.size());
|
||||
return tmHelper.sendTmPacket(static_cast<uint8_t>(subserviceId), hkPacket.hkData,
|
||||
hkPacket.hkSize);
|
||||
return sendTmPacket(static_cast<uint8_t>(subserviceId), hkPacket.hkData, hkPacket.hkSize);
|
||||
}
|
||||
|
||||
sid_t Service3Housekeeping::buildSid(object_id_t objectId, const uint8_t** tcData,
|
||||
|
@ -138,8 +138,7 @@ ReturnValue_t Service8FunctionManagement::handleDataReply(const CommandMessage*
|
||||
return result;
|
||||
}
|
||||
DataReply dataReply(objectId, actionId, buffer, size);
|
||||
result = tmHelper.sendTmPacket(static_cast<uint8_t>(Subservice::REPLY_DIRECT_COMMANDING_DATA),
|
||||
dataReply);
|
||||
result = sendTmPacket(static_cast<uint8_t>(Subservice::REPLY_DIRECT_COMMANDING_DATA), dataReply);
|
||||
|
||||
auto deletionResult = ipcStore->deleteData(storeId);
|
||||
if (deletionResult != HasReturnvaluesIF::RETURN_OK) {
|
||||
|
6
src/fsfw/retval.h
Normal file
6
src/fsfw/retval.h
Normal file
@ -0,0 +1,6 @@
|
||||
#ifndef FSFW_RETVAL_H
|
||||
#define FSFW_RETVAL_H
|
||||
|
||||
#include "fsfw/returnvalues/HasReturnvaluesIF.h"
|
||||
|
||||
#endif // FSFW_RETVAL_H
|
@ -12,7 +12,6 @@ PusDistributor::PusDistributor(StorageManagerIF* store_, uint16_t setApid, objec
|
||||
: TcDistributor(setObjectId),
|
||||
store(store_),
|
||||
checker(setApid, ccsds::PacketType::TC),
|
||||
verifyChannel(),
|
||||
tcStatus(RETURN_FAILED),
|
||||
packetSource(setPacketSource) {}
|
||||
|
||||
@ -120,13 +119,14 @@ ReturnValue_t PusDistributor::callbackAfterSending(ReturnValue_t queueStatus) {
|
||||
tcStatus = queueStatus;
|
||||
}
|
||||
if (tcStatus != RETURN_OK) {
|
||||
this->verifyChannel.sendFailureReport(tcverif::ACCEPTANCE_FAILURE, &reader, tcStatus);
|
||||
verifyChannel->sendFailureReport(
|
||||
VerifFailureParams(tcverif::ACCEPTANCE_FAILURE, reader, tcStatus));
|
||||
// A failed packet is deleted immediately after reporting,
|
||||
// otherwise it will block memory.
|
||||
store->deleteData(currentMessage.getStorageId());
|
||||
return RETURN_FAILED;
|
||||
} else {
|
||||
this->verifyChannel.sendSuccessReport(tcverif::ACCEPTANCE_SUCCESS, &reader);
|
||||
verifyChannel->sendSuccessReport(VerifSuccessParams(tcverif::ACCEPTANCE_SUCCESS, reader));
|
||||
return RETURN_OK;
|
||||
}
|
||||
}
|
||||
|
@ -48,7 +48,7 @@ class PusDistributor : public TcDistributor, public PUSDistributorIF, public Acc
|
||||
* With this class, verification messages are sent to the
|
||||
* TC Verification service.
|
||||
*/
|
||||
VerificationReporter verifyChannel;
|
||||
VerificationReporterIF* verifyChannel = nullptr;
|
||||
/**
|
||||
* The currently handled packet is stored here.
|
||||
*/
|
||||
|
@ -1,11 +1,11 @@
|
||||
#ifndef FSFW_TMTCSERVICES_ACCEPTSVERIFICATIONMESSAGEIF_H_
|
||||
#define FSFW_TMTCSERVICES_ACCEPTSVERIFICATIONMESSAGEIF_H_
|
||||
|
||||
#include "../ipc/MessageQueueSenderIF.h"
|
||||
#include "fsfw/ipc/MessageQueueSenderIF.h"
|
||||
|
||||
class AcceptsVerifyMessageIF {
|
||||
public:
|
||||
virtual ~AcceptsVerifyMessageIF() {}
|
||||
virtual ~AcceptsVerifyMessageIF() = default;
|
||||
virtual MessageQueueId_t getVerificationQueue() = 0;
|
||||
};
|
||||
|
||||
|
@ -15,13 +15,15 @@ object_id_t CommandingServiceBase::defaultPacketDestination = objects::NO_OBJECT
|
||||
|
||||
CommandingServiceBase::CommandingServiceBase(object_id_t setObjectId, uint16_t apid,
|
||||
uint8_t service, uint8_t numberOfParallelCommands,
|
||||
uint16_t commandTimeoutSeconds, size_t queueDepth)
|
||||
uint16_t commandTimeoutSeconds, size_t queueDepth,
|
||||
VerificationReporterIF* verificationReporter)
|
||||
: SystemObject(setObjectId),
|
||||
apid(apid),
|
||||
service(service),
|
||||
timeoutSeconds(commandTimeoutSeconds),
|
||||
tmStoreHelper(apid),
|
||||
tmHelper(service, tmStoreHelper, tmSendHelper),
|
||||
verificationReporter(verificationReporter),
|
||||
commandMap(numberOfParallelCommands) {
|
||||
commandQueue = QueueFactory::instance()->createMessageQueue(queueDepth);
|
||||
requestQueue = QueueFactory::instance()->createMessageQueue(queueDepth);
|
||||
@ -105,12 +107,19 @@ ReturnValue_t CommandingServiceBase::initialize() {
|
||||
tmSendHelper.setInternalErrorReporter(errReporter);
|
||||
}
|
||||
}
|
||||
if (verificationReporter == nullptr) {
|
||||
verificationReporter =
|
||||
ObjectManager::instance()->get<VerificationReporterIF>(objects::TC_VERIFICATOR);
|
||||
if (verificationReporter == nullptr) {
|
||||
return ObjectManagerIF::CHILD_INIT_FAILED;
|
||||
}
|
||||
}
|
||||
return RETURN_OK;
|
||||
}
|
||||
|
||||
void CommandingServiceBase::handleCommandQueue() {
|
||||
CommandMessage reply;
|
||||
ReturnValue_t result = RETURN_FAILED;
|
||||
ReturnValue_t result;
|
||||
while (true) {
|
||||
result = commandQueue->receiveMessage(&reply);
|
||||
if (result == HasReturnvaluesIF::RETURN_OK) {
|
||||
@ -175,16 +184,14 @@ void CommandingServiceBase::handleCommandMessage(CommandMessage* reply) {
|
||||
break;
|
||||
default:
|
||||
if (isStep) {
|
||||
verificationReporter.sendFailureReport(
|
||||
tcverif::PROGRESS_FAILURE, iter->second.tcInfo.ackFlags, iter->second.tcInfo.tcPacketId,
|
||||
iter->second.tcInfo.tcSequenceControl, result, ++iter->second.step, failureParameter1,
|
||||
failureParameter2);
|
||||
prepareVerificationFailureWithFullInfo(tcverif::PROGRESS_FAILURE, iter->second.tcInfo,
|
||||
result, true);
|
||||
failParams.step = ++iter->second.step;
|
||||
} else {
|
||||
verificationReporter.sendFailureReport(
|
||||
tcverif::COMPLETION_FAILURE, iter->second.tcInfo.ackFlags,
|
||||
iter->second.tcInfo.tcPacketId, iter->second.tcInfo.tcSequenceControl, result, 0,
|
||||
failureParameter1, failureParameter2);
|
||||
prepareVerificationFailureWithFullInfo(tcverif::COMPLETION_FAILURE, iter->second.tcInfo,
|
||||
result, true);
|
||||
}
|
||||
verificationReporter->sendFailureReport(failParams);
|
||||
failureParameter1 = 0;
|
||||
failureParameter2 = 0;
|
||||
checkAndExecuteFifo(iter);
|
||||
@ -207,28 +214,26 @@ void CommandingServiceBase::handleReplyHandlerResult(ReturnValue_t result, Comma
|
||||
|
||||
if (sendResult == RETURN_OK) {
|
||||
if (isStep and result != NO_STEP_MESSAGE) {
|
||||
verificationReporter.sendSuccessReport(
|
||||
tcverif::PROGRESS_SUCCESS, iter->second.tcInfo.ackFlags, iter->second.tcInfo.tcPacketId,
|
||||
iter->second.tcInfo.tcSequenceControl, ++iter->second.step);
|
||||
prepareVerificationSuccessWithFullInfo(tcverif::PROGRESS_SUCCESS, iter->second.tcInfo);
|
||||
successParams.step = ++iter->second.step;
|
||||
verificationReporter->sendSuccessReport(successParams);
|
||||
} else {
|
||||
verificationReporter.sendSuccessReport(
|
||||
tcverif::COMPLETION_SUCCESS, iter->second.tcInfo.ackFlags, iter->second.tcInfo.tcPacketId,
|
||||
iter->second.tcInfo.tcSequenceControl, 0);
|
||||
prepareVerificationSuccessWithFullInfo(tcverif::COMPLETION_SUCCESS, iter->second.tcInfo);
|
||||
verificationReporter->sendSuccessReport(successParams);
|
||||
checkAndExecuteFifo(iter);
|
||||
}
|
||||
} else {
|
||||
if (isStep) {
|
||||
prepareVerificationFailureWithFullInfo(tcverif::PROGRESS_FAILURE, iter->second.tcInfo, result,
|
||||
true);
|
||||
failParams.step = ++iter->second.step;
|
||||
nextCommand->clearCommandMessage();
|
||||
verificationReporter.sendFailureReport(
|
||||
tcverif::PROGRESS_FAILURE, iter->second.tcInfo.ackFlags, iter->second.tcInfo.tcPacketId,
|
||||
iter->second.tcInfo.tcSequenceControl, sendResult, ++iter->second.step, failureParameter1,
|
||||
failureParameter2);
|
||||
verificationReporter->sendFailureReport(failParams);
|
||||
} else {
|
||||
prepareVerificationFailureWithFullInfo(tcverif::COMPLETION_FAILURE, iter->second.tcInfo,
|
||||
result, true);
|
||||
nextCommand->clearCommandMessage();
|
||||
verificationReporter.sendFailureReport(
|
||||
tcverif::COMPLETION_FAILURE, iter->second.tcInfo.ackFlags, iter->second.tcInfo.tcPacketId,
|
||||
iter->second.tcInfo.tcSequenceControl, sendResult, 0, failureParameter1,
|
||||
failureParameter2);
|
||||
verificationReporter->sendFailureReport(failParams);
|
||||
}
|
||||
failureParameter1 = 0;
|
||||
failureParameter2 = 0;
|
||||
@ -247,19 +252,19 @@ void CommandingServiceBase::handleRequestQueue() {
|
||||
result = setUpTcReader(message.getStorageId());
|
||||
if (result != HasReturnvaluesIF::RETURN_OK) {
|
||||
// TODO: Warning?
|
||||
rejectPacket(tcverif::START_FAILURE, address, &tcReader, result);
|
||||
rejectPacket(tcverif::START_FAILURE, address, result);
|
||||
continue;
|
||||
}
|
||||
if ((tcReader.getSubService() == 0) or
|
||||
(isValidSubservice(tcReader.getSubService()) != RETURN_OK)) {
|
||||
rejectPacket(tcverif::START_FAILURE, address, &tcReader, INVALID_SUBSERVICE);
|
||||
rejectPacket(tcverif::START_FAILURE, address, INVALID_SUBSERVICE);
|
||||
continue;
|
||||
}
|
||||
|
||||
result = getMessageQueueAndObject(tcReader.getSubService(), tcReader.getUserData(),
|
||||
tcReader.getUserDataLen(), &queue, &objectId);
|
||||
if (result != HasReturnvaluesIF::RETURN_OK) {
|
||||
rejectPacket(tcverif::START_FAILURE, address, &tcReader, result);
|
||||
rejectPacket(tcverif::START_FAILURE, address, result);
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -270,14 +275,14 @@ void CommandingServiceBase::handleRequestQueue() {
|
||||
if (iter != commandMap.end()) {
|
||||
result = iter->second.fifo.insert(address);
|
||||
if (result != RETURN_OK) {
|
||||
rejectPacket(tcverif::START_FAILURE, address, &tcReader, OBJECT_BUSY);
|
||||
rejectPacket(tcverif::START_FAILURE, address, OBJECT_BUSY);
|
||||
}
|
||||
} else {
|
||||
CommandInfo newInfo; // Info will be set by startExecution if neccessary
|
||||
newInfo.objectId = objectId;
|
||||
result = commandMap.insert(queue, newInfo, &iter);
|
||||
if (result != RETURN_OK) {
|
||||
rejectPacket(tcverif::START_FAILURE, address, &tcReader, BUSY);
|
||||
rejectPacket(tcverif::START_FAILURE, address, BUSY);
|
||||
} else {
|
||||
startExecution(address, iter);
|
||||
}
|
||||
@ -287,23 +292,36 @@ void CommandingServiceBase::handleRequestQueue() {
|
||||
|
||||
ReturnValue_t CommandingServiceBase::sendTmPacket(uint8_t subservice, const uint8_t* sourceData,
|
||||
size_t sourceDataLen) {
|
||||
return tmHelper.sendTmPacket(subservice, sourceData, sourceDataLen);
|
||||
ReturnValue_t result = tmHelper.prepareTmPacket(subservice, sourceData, sourceDataLen);
|
||||
if (result != retval::OK) {
|
||||
return result;
|
||||
}
|
||||
return tmHelper.storeAndSendTmPacket();
|
||||
}
|
||||
|
||||
ReturnValue_t CommandingServiceBase::sendTmPacket(uint8_t subservice, object_id_t objectId,
|
||||
const uint8_t* data, size_t dataLen) {
|
||||
return tmHelper.sendTmPacket(subservice, objectId, data, dataLen);
|
||||
telemetry::DataWithObjectIdPrefix dataWithObjId(objectId, data, dataLen);
|
||||
ReturnValue_t result = tmHelper.prepareTmPacket(subservice, dataWithObjId);
|
||||
if (result != retval::OK) {
|
||||
return result;
|
||||
}
|
||||
return tmHelper.storeAndSendTmPacket();
|
||||
}
|
||||
|
||||
ReturnValue_t CommandingServiceBase::sendTmPacket(uint8_t subservice, SerializeIF& sourceData) {
|
||||
return tmHelper.sendTmPacket(subservice, sourceData);
|
||||
ReturnValue_t result = tmHelper.prepareTmPacket(subservice, sourceData);
|
||||
if (result != retval::OK) {
|
||||
return result;
|
||||
}
|
||||
return tmHelper.storeAndSendTmPacket();
|
||||
}
|
||||
|
||||
void CommandingServiceBase::startExecution(store_address_t storeId, CommandMapIter iter) {
|
||||
ReturnValue_t result = RETURN_OK;
|
||||
void CommandingServiceBase::startExecution(store_address_t storeId, CommandMapIter& iter) {
|
||||
CommandMessage command;
|
||||
iter->second.subservice = tcReader.getSubService();
|
||||
result = prepareCommand(&command, iter->second.subservice, tcReader.getUserData(),
|
||||
ReturnValue_t result =
|
||||
prepareCommand(&command, iter->second.subservice, tcReader.getUserData(),
|
||||
tcReader.getUserDataLen(), &iter->second.state, iter->second.objectId);
|
||||
|
||||
ReturnValue_t sendResult = RETURN_OK;
|
||||
@ -320,10 +338,10 @@ void CommandingServiceBase::startExecution(store_address_t storeId, CommandMapIt
|
||||
iter->second.tcInfo.ackFlags = tcReader.getAcknowledgeFlags();
|
||||
iter->second.tcInfo.tcPacketId = tcReader.getPacketIdRaw();
|
||||
iter->second.tcInfo.tcSequenceControl = tcReader.getPacketSeqCtrlRaw();
|
||||
acceptPacket(tcverif::START_SUCCESS, storeId, &tcReader);
|
||||
acceptPacket(tcverif::START_SUCCESS, storeId);
|
||||
} else {
|
||||
command.clearCommandMessage();
|
||||
rejectPacket(tcverif::START_FAILURE, storeId, &tcReader, sendResult);
|
||||
rejectPacket(tcverif::START_FAILURE, storeId, sendResult);
|
||||
checkAndExecuteFifo(iter);
|
||||
}
|
||||
break;
|
||||
@ -333,31 +351,31 @@ void CommandingServiceBase::startExecution(store_address_t storeId, CommandMapIt
|
||||
sendResult = commandQueue->sendMessage(iter.value->first, &command);
|
||||
}
|
||||
if (sendResult == RETURN_OK) {
|
||||
verificationReporter.sendSuccessReport(tcverif::START_SUCCESS, &tcReader);
|
||||
acceptPacket(tcverif::COMPLETION_SUCCESS, storeId, &tcReader);
|
||||
verificationReporter->sendSuccessReport(
|
||||
VerifSuccessParams(tcverif::START_SUCCESS, tcReader));
|
||||
acceptPacket(tcverif::COMPLETION_SUCCESS, storeId);
|
||||
checkAndExecuteFifo(iter);
|
||||
} else {
|
||||
command.clearCommandMessage();
|
||||
rejectPacket(tcverif::START_FAILURE, storeId, &tcReader, sendResult);
|
||||
rejectPacket(tcverif::START_FAILURE, storeId, sendResult);
|
||||
checkAndExecuteFifo(iter);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
rejectPacket(tcverif::START_FAILURE, storeId, &tcReader, result);
|
||||
rejectPacket(tcverif::START_FAILURE, storeId, result);
|
||||
checkAndExecuteFifo(iter);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void CommandingServiceBase::rejectPacket(uint8_t reportId, store_address_t tcStoreId,
|
||||
PusTcReader* correspondingTc, ReturnValue_t errorCode) {
|
||||
verificationReporter.sendFailureReport(reportId, correspondingTc, errorCode);
|
||||
ReturnValue_t errorCode) {
|
||||
verificationReporter->sendFailureReport(VerifFailureParams(reportId, tcReader, errorCode));
|
||||
tcStore->deleteData(tcStoreId);
|
||||
}
|
||||
|
||||
void CommandingServiceBase::acceptPacket(uint8_t reportId, store_address_t tcStoreId,
|
||||
PusTcReader* packet) {
|
||||
verificationReporter.sendSuccessReport(reportId, packet);
|
||||
void CommandingServiceBase::acceptPacket(uint8_t reportId, store_address_t tcStoreId) {
|
||||
verificationReporter->sendSuccessReport(VerifSuccessParams(reportId, tcReader));
|
||||
tcStore->deleteData(tcStoreId);
|
||||
}
|
||||
|
||||
@ -371,7 +389,7 @@ void CommandingServiceBase::checkAndExecuteFifo(CommandMapIter& iter) {
|
||||
startExecution(address, iter);
|
||||
} else {
|
||||
// TODO: Warning?
|
||||
rejectPacket(tcverif::START_FAILURE, address, &tcReader, result);
|
||||
rejectPacket(tcverif::START_FAILURE, address, result);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -390,9 +408,9 @@ void CommandingServiceBase::checkTimeout() {
|
||||
CommandMapIter iter;
|
||||
for (iter = commandMap.begin(); iter != commandMap.end(); ++iter) {
|
||||
if ((iter->second.uptimeOfStart + (timeoutSeconds * 1000)) < uptime) {
|
||||
verificationReporter.sendFailureReport(
|
||||
tcverif::COMPLETION_FAILURE, iter->second.tcInfo.ackFlags, iter->second.tcInfo.tcPacketId,
|
||||
iter->second.tcInfo.tcSequenceControl, TIMEOUT);
|
||||
prepareVerificationFailureWithFullInfo(tcverif::COMPLETION_FAILURE, iter->second.tcInfo,
|
||||
TIMEOUT, false);
|
||||
verificationReporter->sendFailureReport(failParams);
|
||||
checkAndExecuteFifo(iter);
|
||||
}
|
||||
}
|
||||
@ -406,3 +424,25 @@ void CommandingServiceBase::setCustomTmStore(StorageManagerIF& store) {
|
||||
ReturnValue_t CommandingServiceBase::setUpTcReader(store_address_t storeId) {
|
||||
return tc::prepareTcReader(tcStore, storeId, tcReader);
|
||||
}
|
||||
|
||||
void CommandingServiceBase::prepareVerificationFailureWithFullInfo(uint8_t reportId,
|
||||
CommandInfo::TcInfo& tcInfo,
|
||||
ReturnValue_t errorCode,
|
||||
bool setCachedFailParams) {
|
||||
failParams.reportId = reportId;
|
||||
failParams.tcPacketId = tcInfo.tcPacketId;
|
||||
failParams.tcPsc = tcInfo.tcSequenceControl;
|
||||
failParams.ackFlags = tcInfo.ackFlags;
|
||||
failParams.errorCode = errorCode;
|
||||
if (setCachedFailParams) {
|
||||
failParams.errorParam1 = failureParameter1;
|
||||
failParams.errorParam2 = failureParameter2;
|
||||
}
|
||||
}
|
||||
void CommandingServiceBase::prepareVerificationSuccessWithFullInfo(
|
||||
uint8_t reportId, CommandingServiceBase::CommandInfo::TcInfo& tcInfo) {
|
||||
failParams.reportId = reportId;
|
||||
failParams.tcPacketId = tcInfo.tcPacketId;
|
||||
failParams.tcPsc = tcInfo.tcSequenceControl;
|
||||
failParams.ackFlags = tcInfo.ackFlags;
|
||||
}
|
||||
|
@ -68,7 +68,7 @@ class CommandingServiceBase : public SystemObject,
|
||||
*/
|
||||
CommandingServiceBase(object_id_t setObjectId, uint16_t apid, uint8_t service,
|
||||
uint8_t numberOfParallelCommands, uint16_t commandTimeoutSeconds,
|
||||
size_t queueDepth = 20);
|
||||
size_t queueDepth = 20, VerificationReporterIF* reporter = nullptr);
|
||||
~CommandingServiceBase() override;
|
||||
|
||||
void setCustomTmStore(StorageManagerIF& store);
|
||||
@ -218,7 +218,7 @@ class CommandingServiceBase : public SystemObject,
|
||||
virtual void doPeriodicOperation();
|
||||
|
||||
struct CommandInfo : public SerializeIF {
|
||||
struct tcInfo {
|
||||
struct TcInfo {
|
||||
uint8_t ackFlags;
|
||||
uint16_t tcPacketId;
|
||||
uint16_t tcSequenceControl;
|
||||
@ -263,7 +263,7 @@ class CommandingServiceBase : public SystemObject,
|
||||
MessageQueueIF* commandQueue = nullptr;
|
||||
MessageQueueIF* requestQueue = nullptr;
|
||||
|
||||
VerificationReporter verificationReporter;
|
||||
VerificationReporterIF* verificationReporter;
|
||||
|
||||
InternalErrorReporterIF* errReporter = nullptr;
|
||||
|
||||
@ -284,19 +284,19 @@ class CommandingServiceBase : public SystemObject,
|
||||
*/
|
||||
PeriodicTaskIF* executingTask = nullptr;
|
||||
|
||||
[[deprecated("Use function with same name provided by tmHelper")]] ReturnValue_t sendTmPacket(
|
||||
uint8_t subservice, const uint8_t* sourceData, size_t sourceDataLen);
|
||||
[[deprecated("Use function with same name provided by tmHelper")]] ReturnValue_t sendTmPacket(
|
||||
uint8_t subservice, object_id_t objectId, const uint8_t* data, size_t dataLen);
|
||||
[[deprecated("Use function with same name provided by tmHelper")]] ReturnValue_t sendTmPacket(
|
||||
uint8_t subservice, SerializeIF& sourceData);
|
||||
ReturnValue_t sendTmPacket(uint8_t subservice, const uint8_t* sourceData, size_t sourceDataLen);
|
||||
ReturnValue_t sendTmPacket(uint8_t subservice, object_id_t objectId, const uint8_t* data,
|
||||
size_t dataLen);
|
||||
ReturnValue_t sendTmPacket(uint8_t subservice, SerializeIF& sourceData);
|
||||
|
||||
void checkAndExecuteFifo(CommandMapIter& iter);
|
||||
VerifFailureParams failParams;
|
||||
VerifSuccessParams successParams;
|
||||
|
||||
private:
|
||||
/**
|
||||
* This method handles internal execution of a command,
|
||||
* once it has been started by @sa{startExecution()} in the request
|
||||
* once it has been started by @startExecution in the request
|
||||
* queue handler.
|
||||
* It handles replies generated by the devices and relayed by the specific
|
||||
* service implementation. This means that it determines further course of
|
||||
@ -316,26 +316,28 @@ class CommandingServiceBase : public SystemObject,
|
||||
* @brief Handler function for request queue
|
||||
* @details
|
||||
* Sequence of request queue handling:
|
||||
* isValidSubservice -> getMessageQueueAndObject -> startExecution
|
||||
* @isValidSubservice -> @getMessageQueueAndObject -> @startExecution
|
||||
* Generates a Start Success Reports TM[1,3] in subfunction
|
||||
* @sa{startExecution()} or a Start Failure Report TM[1,4] by using the
|
||||
* @startExecution or a Start Failure Report TM[1,4] by using the
|
||||
* TC Verification Service.
|
||||
*/
|
||||
void handleRequestQueue();
|
||||
|
||||
ReturnValue_t setUpTcReader(store_address_t storeId);
|
||||
|
||||
void rejectPacket(uint8_t reportId, store_address_t tcStoreId, PusTcReader* tcPacket,
|
||||
ReturnValue_t errorCode);
|
||||
void rejectPacket(uint8_t reportId, store_address_t tcStoreId, ReturnValue_t errorCode);
|
||||
|
||||
void acceptPacket(uint8_t reportId, store_address_t tcStoreId, PusTcReader* tcPacket);
|
||||
void acceptPacket(uint8_t reportId, store_address_t tcStoreId);
|
||||
|
||||
void startExecution(store_address_t storeId, CommandMapIter iter);
|
||||
void startExecution(store_address_t storeId, CommandMapIter& iter);
|
||||
|
||||
void handleCommandMessage(CommandMessage* reply);
|
||||
void handleReplyHandlerResult(ReturnValue_t result, CommandMapIter iter,
|
||||
CommandMessage* nextCommand, CommandMessage* reply, bool& isStep);
|
||||
|
||||
void prepareVerificationFailureWithFullInfo(uint8_t reportId, CommandInfo::TcInfo& tcInfo,
|
||||
ReturnValue_t errorCode, bool setCachedFailParams);
|
||||
void prepareVerificationSuccessWithFullInfo(uint8_t reportId, CommandInfo::TcInfo& tcInfo);
|
||||
void checkTimeout();
|
||||
};
|
||||
|
||||
|
@ -12,8 +12,12 @@
|
||||
object_id_t PusServiceBase::packetSource = 0;
|
||||
object_id_t PusServiceBase::packetDestination = 0;
|
||||
|
||||
PusServiceBase::PusServiceBase(object_id_t setObjectId, uint16_t setApid, uint8_t setServiceId)
|
||||
: SystemObject(setObjectId), apid(setApid), serviceId(setServiceId) {
|
||||
PusServiceBase::PusServiceBase(object_id_t setObjectId, uint16_t setApid, uint8_t setServiceId,
|
||||
VerificationReporterIF* verifyReporter)
|
||||
: SystemObject(setObjectId),
|
||||
apid(setApid),
|
||||
serviceId(setServiceId),
|
||||
verifyReporter(verifyReporter) {
|
||||
requestQueue = QueueFactory::instance()->createMessageQueue(PUS_SERVICE_MAX_RECEPTION);
|
||||
}
|
||||
|
||||
@ -65,16 +69,21 @@ void PusServiceBase::handleRequestQueue() {
|
||||
}
|
||||
result = tc::prepareTcReader(tcStore, message.getStorageId(), currentPacket);
|
||||
if (result != HasReturnvaluesIF::RETURN_OK) {
|
||||
this->verifyReporter.sendFailureReport(tcverif::START_FAILURE, &this->currentPacket, result,
|
||||
0, errorParameter1, errorParameter2);
|
||||
auto params = VerifFailureParams(tcverif::START_FAILURE, currentPacket, result);
|
||||
params.errorParam1 = errorParameter1;
|
||||
params.errorParam2 = errorParameter2;
|
||||
verifyReporter->sendFailureReport(params);
|
||||
continue;
|
||||
}
|
||||
result = this->handleRequest(currentPacket.getSubService());
|
||||
result = handleRequest(currentPacket.getSubService());
|
||||
if (result == RETURN_OK) {
|
||||
this->verifyReporter.sendSuccessReport(tcverif::COMPLETION_SUCCESS, &this->currentPacket);
|
||||
verifyReporter->sendSuccessReport(
|
||||
VerifSuccessParams(tcverif::COMPLETION_SUCCESS, currentPacket));
|
||||
} else {
|
||||
this->verifyReporter.sendFailureReport(tcverif::COMPLETION_FAILURE, &this->currentPacket,
|
||||
result, 0, errorParameter1, errorParameter2);
|
||||
auto params = VerifFailureParams(tcverif::COMPLETION_FAILURE, currentPacket, result);
|
||||
params.errorParam1 = errorParameter1;
|
||||
params.errorParam2 = errorParameter2;
|
||||
verifyReporter->sendFailureReport(params);
|
||||
}
|
||||
tcStore->deleteData(message.getStorageId());
|
||||
errorParameter1 = 0;
|
||||
@ -109,6 +118,13 @@ ReturnValue_t PusServiceBase::initialize() {
|
||||
return ObjectManagerIF::CHILD_INIT_FAILED;
|
||||
}
|
||||
}
|
||||
if (verifyReporter == nullptr) {
|
||||
verifyReporter =
|
||||
ObjectManager::instance()->get<VerificationReporterIF>(objects::TC_VERIFICATOR);
|
||||
if (verifyReporter == nullptr) {
|
||||
return ObjectManagerIF::CHILD_INIT_FAILED;
|
||||
}
|
||||
}
|
||||
return HasReturnvaluesIF::RETURN_OK;
|
||||
}
|
||||
|
||||
@ -145,3 +161,7 @@ void PusServiceBase::initializeTmSendHelper(TmSendHelper& tmSendHelper) {
|
||||
void PusServiceBase::initializeTmStoreHelper(TmStoreHelper& tmStoreHelper) const {
|
||||
tmStoreHelper.setApid(apid);
|
||||
}
|
||||
|
||||
void PusServiceBase::setVerificationReporter(VerificationReporterIF* reporter) {
|
||||
verifyReporter = reporter;
|
||||
}
|
||||
|
@ -53,18 +53,33 @@ class PusServiceBase : public ExecutableObjectIF,
|
||||
* @param setServiceId
|
||||
* The Service Identifier as specified in ECSS PUS.
|
||||
*/
|
||||
PusServiceBase(object_id_t setObjectId, uint16_t setApid, uint8_t setServiceId);
|
||||
PusServiceBase(object_id_t setObjectId, uint16_t setApid, uint8_t setServiceId,
|
||||
VerificationReporterIF* reporter = nullptr);
|
||||
/**
|
||||
* The destructor is empty.
|
||||
*/
|
||||
~PusServiceBase() override;
|
||||
|
||||
void setCustomTcStore(StorageManagerIF* tcStore);
|
||||
void setVerificationReporter(VerificationReporterIF* reporter);
|
||||
void setCustomErrorReporter(InternalErrorReporterIF* errReporter);
|
||||
|
||||
/**
|
||||
* Helper methods if the implementing class wants to send telemetry
|
||||
* @param tmSendHelper
|
||||
*/
|
||||
void initializeTmSendHelper(TmSendHelper& tmSendHelper);
|
||||
/**
|
||||
* Helper methods if the implementing class wants to store telemetry
|
||||
* @param tmSendHelper
|
||||
*/
|
||||
void initializeTmStoreHelper(TmStoreHelper& tmStoreHelper) const;
|
||||
/**
|
||||
* Helper methods if the implementing class wants to both send and store telemetry
|
||||
* @param tmSendHelper
|
||||
*/
|
||||
void initializeTmHelpers(TmSendHelper& tmSendHelper, TmStoreHelper& tmStoreHelper);
|
||||
|
||||
/**
|
||||
* @brief The handleRequest method shall handle any kind of Telecommand
|
||||
* Request immediately.
|
||||
@ -146,7 +161,7 @@ class PusServiceBase : public ExecutableObjectIF,
|
||||
* An instance of the VerificationReporter class, that simplifies
|
||||
* sending any kind of verification message to the TC Verification Service.
|
||||
*/
|
||||
VerificationReporter verifyReporter;
|
||||
VerificationReporterIF* verifyReporter;
|
||||
|
||||
/**
|
||||
* The current Telecommand to be processed.
|
||||
@ -159,6 +174,7 @@ class PusServiceBase : public ExecutableObjectIF,
|
||||
static object_id_t packetSource;
|
||||
|
||||
static object_id_t packetDestination;
|
||||
VerifSuccessParams successParams;
|
||||
|
||||
private:
|
||||
/**
|
||||
|
@ -24,29 +24,36 @@ ReturnValue_t TmStoreAndSendWrapper::storeAndSendTmPacket() {
|
||||
return result;
|
||||
}
|
||||
|
||||
ReturnValue_t TmStoreAndSendWrapper::sendTmPacket(uint8_t subservice, const uint8_t* sourceData,
|
||||
ReturnValue_t TmStoreAndSendWrapper::prepareTmPacket(uint8_t subservice, const uint8_t* sourceData,
|
||||
size_t sourceDataLen) {
|
||||
storeHelper.preparePacket(defaultService, subservice, sendCounter);
|
||||
storeHelper.setSourceDataRaw(sourceData, sourceDataLen);
|
||||
return storeAndSendTmPacket();
|
||||
ReturnValue_t result = storeHelper.preparePacket(defaultService, subservice, sendCounter);
|
||||
if (result != HasReturnvaluesIF::RETURN_OK) {
|
||||
return result;
|
||||
}
|
||||
return storeHelper.setSourceDataRaw(sourceData, sourceDataLen);
|
||||
}
|
||||
|
||||
ReturnValue_t TmStoreAndSendWrapper::sendTmPacket(uint8_t subservice, object_id_t objectId,
|
||||
const uint8_t* data, size_t dataLen) {
|
||||
telemetry::DataWithObjectIdPrefix dataWithObjId(objectId, data, dataLen);
|
||||
storeHelper.preparePacket(defaultService, subservice, sendCounter);
|
||||
storeHelper.setSourceDataSerializable(dataWithObjId);
|
||||
return storeAndSendTmPacket();
|
||||
ReturnValue_t TmStoreAndSendWrapper::prepareTmPacket(
|
||||
uint8_t subservice, telemetry::DataWithObjectIdPrefix& dataWithObjectId) {
|
||||
ReturnValue_t result = storeHelper.preparePacket(defaultService, subservice, sendCounter);
|
||||
if (result != HasReturnvaluesIF::RETURN_OK) {
|
||||
return result;
|
||||
}
|
||||
return storeHelper.setSourceDataSerializable(dataWithObjectId);
|
||||
}
|
||||
|
||||
ReturnValue_t TmStoreAndSendWrapper::sendTmPacket(uint8_t subservice, SerializeIF& sourceData) {
|
||||
storeHelper.preparePacket(defaultService, subservice, sendCounter);
|
||||
storeHelper.setSourceDataSerializable(sourceData);
|
||||
return storeAndSendTmPacket();
|
||||
ReturnValue_t TmStoreAndSendWrapper::prepareTmPacket(uint8_t subservice, SerializeIF& sourceData) {
|
||||
ReturnValue_t result = storeHelper.preparePacket(defaultService, subservice, sendCounter);
|
||||
if (result != HasReturnvaluesIF::RETURN_OK) {
|
||||
return result;
|
||||
}
|
||||
return storeHelper.setSourceDataSerializable(sourceData);
|
||||
}
|
||||
|
||||
ReturnValue_t TmStoreAndSendWrapper::sendTmPacket(uint8_t subservice) {
|
||||
storeHelper.preparePacket(defaultService, subservice, sendCounter);
|
||||
storeHelper.setSourceDataRaw(nullptr, 0);
|
||||
return storeAndSendTmPacket();
|
||||
ReturnValue_t TmStoreAndSendWrapper::prepareTmPacket(uint8_t subservice) {
|
||||
ReturnValue_t result = storeHelper.preparePacket(defaultService, subservice, sendCounter);
|
||||
if (result != HasReturnvaluesIF::RETURN_OK) {
|
||||
return result;
|
||||
}
|
||||
return storeHelper.setSourceDataRaw(nullptr, 0);
|
||||
}
|
||||
|
@ -3,6 +3,7 @@
|
||||
|
||||
#include "TmSendHelper.h"
|
||||
#include "TmStoreHelper.h"
|
||||
#include "tmHelpers.h"
|
||||
|
||||
/**
|
||||
* Wrapper class intended to help with PUS TM handling. This wrapper class also caches the current
|
||||
@ -12,37 +13,41 @@ class TmStoreAndSendWrapper {
|
||||
public:
|
||||
TmStoreAndSendWrapper(uint8_t defaultService, TmStoreHelper& storeHelper,
|
||||
TmSendHelper& sendHelper);
|
||||
ReturnValue_t storeAndSendTmPacket();
|
||||
|
||||
ReturnValue_t sendTmPacket(uint8_t subservice);
|
||||
/**
|
||||
* @brief Send TM data from pointer to data.
|
||||
* If a header is supplied it is added before data
|
||||
* Prepares a TM packet with the given parameters. It will also set the default service.
|
||||
* @param subservice
|
||||
* @return
|
||||
*/
|
||||
ReturnValue_t prepareTmPacket(uint8_t subservice);
|
||||
/**
|
||||
* Prepares a TM packet with the given parameters. It will also set the default service.
|
||||
* @param subservice Number of subservice
|
||||
* @param sourceData Custom source data
|
||||
* @param sourceDataLen Lenght of data in the Packet
|
||||
*/
|
||||
ReturnValue_t sendTmPacket(uint8_t subservice, const uint8_t* sourceData, size_t sourceDataLen);
|
||||
ReturnValue_t prepareTmPacket(uint8_t subservice, const uint8_t* sourceData,
|
||||
size_t sourceDataLen);
|
||||
|
||||
/**
|
||||
* @brief To send TM packets of objects that still need to be serialized
|
||||
* and consist of an object ID with appended data.
|
||||
* Prepares a TM packet with the given parameters. It will also set the default service.
|
||||
* @param subservice Number of subservice
|
||||
* @param objectId ObjectId is placed before data
|
||||
* @param data Data to append to the packet
|
||||
* @param dataLen Length of Data
|
||||
*/
|
||||
ReturnValue_t sendTmPacket(uint8_t subservice, object_id_t objectId, const uint8_t* data,
|
||||
size_t dataLen);
|
||||
ReturnValue_t prepareTmPacket(uint8_t subservice,
|
||||
telemetry::DataWithObjectIdPrefix& dataWithObjectId);
|
||||
|
||||
/**
|
||||
* @brief To send packets which are contained inside a class implementing
|
||||
* SerializeIF.
|
||||
* Prepares a TM packet with the given parameters. It will also set the default service.
|
||||
* @param subservice Number of subservice
|
||||
* @param content This is a pointer to the serialized packet
|
||||
* @param header Serialize IF header which will be placed before content
|
||||
*/
|
||||
ReturnValue_t sendTmPacket(uint8_t subservice, SerializeIF& sourceData);
|
||||
ReturnValue_t prepareTmPacket(uint8_t subservice, SerializeIF& sourceData);
|
||||
|
||||
ReturnValue_t storeAndSendTmPacket();
|
||||
|
||||
bool incrementSendCounter = true;
|
||||
TmStoreHelper& storeHelper;
|
||||
|
@ -1,29 +1,25 @@
|
||||
#include "fsfw/tmtcservices/VerificationReporter.h"
|
||||
|
||||
#include "fsfw/ipc/MessageQueueIF.h"
|
||||
#include "fsfw/objectmanager/ObjectManager.h"
|
||||
#include "fsfw/objectmanager/frameworkObjects.h"
|
||||
#include "fsfw/serviceinterface/ServiceInterface.h"
|
||||
#include "fsfw/tmtcservices/AcceptsVerifyMessageIF.h"
|
||||
#include "fsfw/tmtcservices/PusVerificationReport.h"
|
||||
|
||||
object_id_t VerificationReporter::messageReceiver = objects::PUS_SERVICE_1_VERIFICATION;
|
||||
|
||||
VerificationReporter::VerificationReporter() : acknowledgeQueue(MessageQueueIF::NO_QUEUE) {}
|
||||
VerificationReporter::VerificationReporter(AcceptsVerifyMessageIF* receiver, object_id_t objectId)
|
||||
: SystemObject(objectId) {
|
||||
if (receiver != nullptr) {
|
||||
acknowledgeQueue = receiver->getVerificationQueue();
|
||||
}
|
||||
}
|
||||
|
||||
VerificationReporter::~VerificationReporter() = default;
|
||||
|
||||
void VerificationReporter::sendSuccessReport(uint8_t set_report_id, PusTcReader* correspondingTc,
|
||||
uint8_t set_step) {
|
||||
if (acknowledgeQueue == MessageQueueIF::NO_QUEUE) {
|
||||
this->initialize();
|
||||
}
|
||||
if (correspondingTc == nullptr) {
|
||||
return;
|
||||
}
|
||||
PusVerificationMessage message(set_report_id, correspondingTc->getAcknowledgeFlags(),
|
||||
correspondingTc->getPacketIdRaw(),
|
||||
correspondingTc->getPacketSeqCtrlRaw(), 0, set_step);
|
||||
void VerificationReporter::setReceiver(AcceptsVerifyMessageIF& receiver) {
|
||||
acknowledgeQueue = receiver.getVerificationQueue();
|
||||
}
|
||||
|
||||
ReturnValue_t VerificationReporter::sendFailureReport(VerifFailureParams params) {
|
||||
PusVerificationMessage message(params.reportId, params.ackFlags, params.tcPacketId, params.tcPsc,
|
||||
params.errorCode, params.step, params.errorParam1,
|
||||
params.errorParam2);
|
||||
ReturnValue_t status = MessageQueueSenderIF::sendMessage(acknowledgeQueue, &message);
|
||||
if (status != HasReturnvaluesIF::RETURN_OK) {
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
@ -31,16 +27,12 @@ void VerificationReporter::sendSuccessReport(uint8_t set_report_id, PusTcReader*
|
||||
<< "to queue. Code: " << std::hex << status << std::dec << std::endl;
|
||||
#endif
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
void VerificationReporter::sendSuccessReport(uint8_t set_report_id, uint8_t ackFlags,
|
||||
uint16_t tcPacketId, uint16_t tcSequenceControl,
|
||||
uint8_t set_step) {
|
||||
if (acknowledgeQueue == MessageQueueIF::NO_QUEUE) {
|
||||
this->initialize();
|
||||
}
|
||||
PusVerificationMessage message(set_report_id, ackFlags, tcPacketId, tcSequenceControl, 0,
|
||||
set_step);
|
||||
ReturnValue_t VerificationReporter::sendSuccessReport(VerifSuccessParams params) {
|
||||
PusVerificationMessage message(params.reportId, params.ackFlags, params.tcPacketId, params.tcPsc,
|
||||
retval::OK, params.step);
|
||||
ReturnValue_t status = MessageQueueSenderIF::sendMessage(acknowledgeQueue, &message);
|
||||
if (status != HasReturnvaluesIF::RETURN_OK) {
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
@ -48,64 +40,5 @@ void VerificationReporter::sendSuccessReport(uint8_t set_report_id, uint8_t ackF
|
||||
<< "to queue. Code: " << std::hex << status << std::dec << std::endl;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void VerificationReporter::sendFailureReport(uint8_t report_id, PusTcReader* correspondingTc,
|
||||
ReturnValue_t errorCode, uint8_t step,
|
||||
uint32_t parameter1, uint32_t parameter2) {
|
||||
if (acknowledgeQueue == MessageQueueIF::NO_QUEUE) {
|
||||
this->initialize();
|
||||
}
|
||||
if (correspondingTc == nullptr) {
|
||||
return;
|
||||
}
|
||||
PusVerificationMessage message(
|
||||
report_id, correspondingTc->getAcknowledgeFlags(), correspondingTc->getPacketIdRaw(),
|
||||
correspondingTc->getPacketSeqCtrlRaw(), errorCode, step, parameter1, parameter2);
|
||||
ReturnValue_t status = MessageQueueSenderIF::sendMessage(acknowledgeQueue, &message);
|
||||
if (status != HasReturnvaluesIF::RETURN_OK) {
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::error << "VerificationReporter::sendFailureReport: Error writing "
|
||||
<< "to queue. Code: " << std::hex << "0x" << status << std::dec << std::endl;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void VerificationReporter::sendFailureReport(uint8_t report_id, uint8_t ackFlags,
|
||||
uint16_t tcPacketId, uint16_t tcSequenceControl,
|
||||
ReturnValue_t error_code, uint8_t step,
|
||||
uint32_t parameter1, uint32_t parameter2) {
|
||||
if (acknowledgeQueue == MessageQueueIF::NO_QUEUE) {
|
||||
this->initialize();
|
||||
}
|
||||
PusVerificationMessage message(report_id, ackFlags, tcPacketId, tcSequenceControl, error_code,
|
||||
step, parameter1, parameter2);
|
||||
ReturnValue_t status = MessageQueueSenderIF::sendMessage(acknowledgeQueue, &message);
|
||||
if (status != HasReturnvaluesIF::RETURN_OK) {
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::error << "VerificationReporter::sendFailureReport: Error writing "
|
||||
<< "to queue. Code: " << std::hex << "0x" << status << std::dec << std::endl;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void VerificationReporter::initialize() {
|
||||
if (messageReceiver == objects::NO_OBJECT) {
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::warning << "VerificationReporter::initialize: Verification message"
|
||||
" receiver object ID not set yet in Factory!"
|
||||
<< std::endl;
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
auto* temp = ObjectManager::instance()->get<AcceptsVerifyMessageIF>(messageReceiver);
|
||||
if (temp == nullptr) {
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::error << "VerificationReporter::initialize: Message "
|
||||
<< "receiver invalid. Make sure it is set up properly and "
|
||||
<< "implementsAcceptsVerifyMessageIF" << std::endl;
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
this->acknowledgeQueue = temp->getVerificationQueue();
|
||||
return status;
|
||||
}
|
||||
|
@ -2,12 +2,11 @@
|
||||
#define FSFW_TMTCSERVICES_VERIFICATIONREPORTER_H_
|
||||
|
||||
#include "PusVerificationReport.h"
|
||||
#include "VerificationReporterIF.h"
|
||||
#include "fsfw/objectmanager/ObjectManagerIF.h"
|
||||
#include "fsfw/objectmanager/SystemObject.h"
|
||||
#include "fsfw/tmtcpacket/pus/tc/PusTcCreator.h"
|
||||
|
||||
namespace Factory {
|
||||
void setStaticFrameworkObjectIds();
|
||||
}
|
||||
#include "fsfw/tmtcservices/AcceptsVerifyMessageIF.h"
|
||||
|
||||
/**
|
||||
* @brief This helper object is used to forward verification messages
|
||||
@ -20,30 +19,21 @@ void setStaticFrameworkObjectIds();
|
||||
* to the PUS standard.
|
||||
*
|
||||
*/
|
||||
class VerificationReporter {
|
||||
friend void(Factory::setStaticFrameworkObjectIds)();
|
||||
|
||||
class VerificationReporter : public SystemObject, public VerificationReporterIF {
|
||||
public:
|
||||
VerificationReporter();
|
||||
virtual ~VerificationReporter();
|
||||
explicit VerificationReporter(AcceptsVerifyMessageIF* receiver,
|
||||
object_id_t objectId = objects::TC_VERIFICATOR);
|
||||
~VerificationReporter() override;
|
||||
|
||||
void setReceiver(AcceptsVerifyMessageIF& receiver);
|
||||
|
||||
// TODO: The API is a little bit bloated. It might be better to group all the parameters
|
||||
// into a dedicated struct
|
||||
void sendSuccessReport(uint8_t set_report_id, PusTcReader* correspondingTc, uint8_t set_step = 0);
|
||||
void sendSuccessReport(uint8_t set_report_id, uint8_t ackFlags, uint16_t tcPacketId,
|
||||
uint16_t tcSequenceControl, uint8_t set_step = 0);
|
||||
ReturnValue_t sendSuccessReport(VerifSuccessParams params) override;
|
||||
|
||||
void sendFailureReport(uint8_t report_id, PusTcReader* correspondingTc,
|
||||
ReturnValue_t errorCode = 0, uint8_t step = 0, uint32_t parameter1 = 0,
|
||||
uint32_t parameter2 = 0);
|
||||
void sendFailureReport(uint8_t report_id, uint8_t ackFlags, uint16_t tcPacketId,
|
||||
uint16_t tcSequenceControl, ReturnValue_t errorCode = 0, uint8_t step = 0,
|
||||
uint32_t parameter1 = 0, uint32_t parameter2 = 0);
|
||||
|
||||
void initialize();
|
||||
ReturnValue_t sendFailureReport(VerifFailureParams params) override;
|
||||
|
||||
private:
|
||||
static object_id_t messageReceiver;
|
||||
MessageQueueId_t acknowledgeQueue;
|
||||
};
|
||||
|
||||
|
57
src/fsfw/tmtcservices/VerificationReporterIF.h
Normal file
57
src/fsfw/tmtcservices/VerificationReporterIF.h
Normal file
@ -0,0 +1,57 @@
|
||||
#ifndef FSFW_TMTCSERVICES_VERIFICATIONREPORTERIF_H
|
||||
#define FSFW_TMTCSERVICES_VERIFICATIONREPORTERIF_H
|
||||
|
||||
#include "fsfw/retval.h"
|
||||
#include "fsfw/tmtcpacket/pus/tc.h"
|
||||
|
||||
struct VerifParamsBase {
|
||||
VerifParamsBase() : reportId(0), tcPacketId(0), tcPsc(0) {}
|
||||
VerifParamsBase(uint8_t reportId, uint16_t tcPacketId, uint16_t tcPsc)
|
||||
: reportId(reportId), tcPacketId(tcPacketId), tcPsc(tcPsc) {}
|
||||
uint8_t reportId;
|
||||
uint16_t tcPacketId;
|
||||
uint16_t tcPsc;
|
||||
uint8_t ackFlags = ecss::ACK_ALL;
|
||||
uint8_t step = 0;
|
||||
};
|
||||
|
||||
struct VerifSuccessParams : public VerifParamsBase {
|
||||
VerifSuccessParams() = default;
|
||||
VerifSuccessParams(uint8_t reportId, uint16_t tcPacketId, uint16_t tcPsc)
|
||||
: VerifParamsBase(reportId, tcPacketId, tcPsc) {}
|
||||
VerifSuccessParams(uint8_t reportId, PusTcIF& tc)
|
||||
: VerifParamsBase(reportId, tc.getPacketIdRaw(), tc.getPacketSeqCtrlRaw()) {}
|
||||
};
|
||||
|
||||
struct VerifFailureParams : public VerifParamsBase {
|
||||
VerifFailureParams() = default;
|
||||
VerifFailureParams(uint8_t reportId, uint16_t tcPacketId, uint16_t tcPsc, ReturnValue_t errorCode,
|
||||
uint32_t errorParam1, uint32_t errorParams2)
|
||||
: VerifParamsBase(reportId, tcPacketId, tcPsc), errorCode(errorCode) {
|
||||
errorParam1 = errorParam1;
|
||||
errorParams2 = errorParams2;
|
||||
}
|
||||
VerifFailureParams(uint8_t reportId, uint16_t tcPacketId, uint16_t tcPsc, ReturnValue_t errorCode)
|
||||
: VerifParamsBase(reportId, tcPacketId, tcPsc), errorCode(errorCode) {}
|
||||
VerifFailureParams(uint8_t reportId, uint16_t tcPacketId, uint16_t tcPsc)
|
||||
: VerifParamsBase(reportId, tcPacketId, tcPsc) {}
|
||||
VerifFailureParams(uint8_t reportId, PusTcIF& tc, ReturnValue_t errorCode)
|
||||
: VerifParamsBase(reportId, tc.getPacketIdRaw(), tc.getPacketSeqCtrlRaw()),
|
||||
errorCode(errorCode) {}
|
||||
VerifFailureParams(uint8_t reportId, PusTcIF& tc)
|
||||
: VerifParamsBase(reportId, tc.getPacketIdRaw(), tc.getPacketSeqCtrlRaw()) {}
|
||||
|
||||
ReturnValue_t errorCode = retval::OK;
|
||||
uint8_t step = 0;
|
||||
uint32_t errorParam1 = 0;
|
||||
uint32_t errorParam2 = 0;
|
||||
};
|
||||
class VerificationReporterIF {
|
||||
public:
|
||||
virtual ~VerificationReporterIF() = default;
|
||||
|
||||
virtual ReturnValue_t sendSuccessReport(VerifSuccessParams params) = 0;
|
||||
virtual ReturnValue_t sendFailureReport(VerifFailureParams params) = 0;
|
||||
};
|
||||
|
||||
#endif // FSFW_TMTCSERVICES_VERIFICATIONREPORTERIF_H
|
@ -10,38 +10,60 @@ namespace telemetry {
|
||||
class DataWithObjectIdPrefix : public SerializeIF {
|
||||
public:
|
||||
DataWithObjectIdPrefix(object_id_t objectId, const uint8_t* srcData, size_t srcDataLen)
|
||||
: objectId(objectId), srcData(srcData), srcDataLen(srcDataLen) {}
|
||||
: objectId(objectId) {
|
||||
dataWrapper.type = ecss::DataTypes::RAW;
|
||||
dataWrapper.dataUnion.raw.data = srcData;
|
||||
dataWrapper.dataUnion.raw.len = srcDataLen;
|
||||
}
|
||||
|
||||
DataWithObjectIdPrefix(object_id_t objectId, SerializeIF& serializable) : objectId(objectId) {
|
||||
dataWrapper.type = ecss::DataTypes::SERIALIZABLE;
|
||||
dataWrapper.dataUnion.serializable = &serializable;
|
||||
}
|
||||
|
||||
ReturnValue_t serialize(uint8_t** buffer, size_t* size, size_t maxSize,
|
||||
Endianness streamEndianness) const override {
|
||||
if (*size + getSerializedSize() > maxSize) {
|
||||
return SerializeIF::BUFFER_TOO_SHORT;
|
||||
}
|
||||
if (dataWrapper.type != ecss::DataTypes::RAW) {
|
||||
if ((dataWrapper.dataUnion.raw.data == nullptr) and (dataWrapper.dataUnion.raw.len > 0)) {
|
||||
return retval::FAILED;
|
||||
}
|
||||
} else if (dataWrapper.type == ecss::DataTypes::SERIALIZABLE) {
|
||||
if (dataWrapper.dataUnion.serializable == nullptr) {
|
||||
return retval::FAILED;
|
||||
}
|
||||
}
|
||||
ReturnValue_t result =
|
||||
SerializeAdapter::serialize(&objectId, buffer, size, maxSize, streamEndianness);
|
||||
if (result != HasReturnvaluesIF::RETURN_OK) {
|
||||
return result;
|
||||
}
|
||||
std::memcpy(*buffer, srcData, srcDataLen);
|
||||
*buffer += srcDataLen;
|
||||
*size += srcDataLen;
|
||||
return HasReturnvaluesIF::RETURN_OK;
|
||||
}
|
||||
|
||||
[[nodiscard]] size_t getSerializedSize() const override { return sizeof(objectId) + srcDataLen; }
|
||||
|
||||
ReturnValue_t deSerialize(const uint8_t** buffer, size_t* size,
|
||||
Endianness streamEndianness) override {
|
||||
ReturnValue_t result = SerializeAdapter::deSerialize(&objectId, buffer, size, streamEndianness);
|
||||
if (result != retval::OK) {
|
||||
return result;
|
||||
}
|
||||
if (dataWrapper.type != ecss::DataTypes::RAW) {
|
||||
std::memcpy(*buffer, dataWrapper.dataUnion.raw.data, dataWrapper.dataUnion.raw.len);
|
||||
*buffer += dataWrapper.dataUnion.raw.len;
|
||||
*size += dataWrapper.dataUnion.raw.len;
|
||||
} else {
|
||||
return dataWrapper.dataUnion.serializable->serialize(buffer, size, maxSize, streamEndianness);
|
||||
}
|
||||
return retval::OK;
|
||||
}
|
||||
|
||||
[[nodiscard]] size_t getSerializedSize() const override {
|
||||
return sizeof(objectId) + dataWrapper.getLength();
|
||||
}
|
||||
|
||||
ReturnValue_t deSerialize(const uint8_t** buffer, size_t* size,
|
||||
Endianness streamEndianness) override {
|
||||
// As long as there is no way to know how long the expected data will be, this function
|
||||
// does not make sense
|
||||
return retval::FAILED;
|
||||
}
|
||||
|
||||
private:
|
||||
object_id_t objectId;
|
||||
const uint8_t* srcData;
|
||||
size_t srcDataLen;
|
||||
ecss::DataWrapper dataWrapper{};
|
||||
};
|
||||
|
||||
} // namespace telemetry
|
||||
|
@ -60,8 +60,6 @@ void Factory::setStaticFrameworkObjectIds() {
|
||||
CommandingServiceBase::defaultPacketSource = objects::NO_OBJECT;
|
||||
CommandingServiceBase::defaultPacketDestination = objects::NO_OBJECT;
|
||||
|
||||
VerificationReporter::messageReceiver = objects::PUS_SERVICE_1_VERIFICATION;
|
||||
|
||||
DeviceHandlerBase::powerSwitcherId = objects::NO_OBJECT;
|
||||
DeviceHandlerBase::rawDataReceiverId = objects::NO_OBJECT;
|
||||
|
||||
|
30
unittests/mocks/PusServiceBaseMock.cpp
Normal file
30
unittests/mocks/PusServiceBaseMock.cpp
Normal file
@ -0,0 +1,30 @@
|
||||
#include "PusServiceBaseMock.h"
|
||||
|
||||
ReturnValue_t PsbMock::handleRequest(uint8_t subservice) {
|
||||
handleRequestCallCnt++;
|
||||
subserviceQueue.push(subservice);
|
||||
if (handleReqFailPair.first) {
|
||||
handleReqFailPair.first = false;
|
||||
return handleReqFailPair.second;
|
||||
}
|
||||
return HasReturnvaluesIF::RETURN_OK;
|
||||
}
|
||||
|
||||
ReturnValue_t PsbMock::performService() {
|
||||
performServiceCallCnt++;
|
||||
if (performServiceFailPair.first) {
|
||||
performServiceFailPair.first = false;
|
||||
return performServiceFailPair.second;
|
||||
}
|
||||
return HasReturnvaluesIF::RETURN_OK;
|
||||
}
|
||||
|
||||
void PsbMock::reset() {
|
||||
handleRequestCallCnt = 0;
|
||||
performServiceCallCnt = 0;
|
||||
std::queue<uint8_t>().swap(subserviceQueue);
|
||||
}
|
||||
void PsbMock::makeNextHandleReqCallFail(ReturnValue_t retval) {
|
||||
handleReqFailPair.first = true;
|
||||
handleReqFailPair.second = retval;
|
||||
}
|
23
unittests/mocks/PusServiceBaseMock.h
Normal file
23
unittests/mocks/PusServiceBaseMock.h
Normal file
@ -0,0 +1,23 @@
|
||||
#ifndef FSFW_TESTS_PUSSERVICEBASEMOCK_H
|
||||
#define FSFW_TESTS_PUSSERVICEBASEMOCK_H
|
||||
|
||||
#include <queue>
|
||||
|
||||
#include "fsfw/tmtcservices/PusServiceBase.h"
|
||||
|
||||
class PsbMock : public PusServiceBase {
|
||||
public:
|
||||
unsigned int handleRequestCallCnt = 0;
|
||||
std::queue<uint8_t> subserviceQueue;
|
||||
unsigned int performServiceCallCnt = 0;
|
||||
|
||||
std::pair<bool, ReturnValue_t> handleReqFailPair;
|
||||
std::pair<bool, ReturnValue_t> performServiceFailPair;
|
||||
ReturnValue_t handleRequest(uint8_t subservice) override;
|
||||
ReturnValue_t performService() override;
|
||||
|
||||
void makeNextHandleReqCallFail(ReturnValue_t retval);
|
||||
void reset();
|
||||
};
|
||||
|
||||
#endif // FSFW_TESTS_PUSSERVICEBASEMOCK_H
|
@ -1,4 +1,5 @@
|
||||
target_sources(${FSFW_TEST_TGT} PRIVATE
|
||||
testStoreHelper.cpp
|
||||
testSendHelper.cpp
|
||||
testStoreAndSendHelper.cpp
|
||||
)
|
||||
|
5
unittests/tmtcservices/testPsb.cpp
Normal file
5
unittests/tmtcservices/testPsb.cpp
Normal file
@ -0,0 +1,5 @@
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
#include "fsfw/tmtcservices/PusServiceBase.h"
|
||||
|
||||
TEST_CASE("Pus Service Base", "[pus-service-base]") { auto psb = PusServiceBase() }
|
@ -2,8 +2,12 @@
|
||||
|
||||
#include "fsfw/storagemanager/LocalPool.h"
|
||||
#include "fsfw/tmtcservices/TmSendHelper.h"
|
||||
#include "fsfw/tmtcservices/TmStoreAndSendHelper.h"
|
||||
#include "fsfw/tmtcservices/TmStoreHelper.h"
|
||||
#include "mocks/CdsShortTimestamperMock.h"
|
||||
#include "mocks/InternalErrorReporterMock.h"
|
||||
#include "mocks/MessageQueueMock.h"
|
||||
#include "mocks/SimpleSerializable.h"
|
||||
|
||||
TEST_CASE("TM Store And Send Helper", "[tm-store-send-helper]") {
|
||||
auto timeStamper = CdsShortTimestamperMock();
|
||||
@ -16,4 +20,68 @@ TEST_CASE("TM Store And Send Helper", "[tm-store-send-helper]") {
|
||||
auto msgQueue = MessageQueueMock();
|
||||
msgQueue.setDefaultDestination(destId);
|
||||
TmSendHelper sendHelper(msgQueue, errReporter, destId);
|
||||
TmStoreAndSendWrapper tmHelper(17, storeHelper, sendHelper);
|
||||
|
||||
SECTION("State") {
|
||||
CHECK(tmHelper.sendCounter == 0);
|
||||
CHECK(tmHelper.defaultService == 17);
|
||||
CHECK(tmHelper.delOnFailure);
|
||||
CHECK(tmHelper.incrementSendCounter);
|
||||
CHECK(&tmHelper.sendHelper == &sendHelper);
|
||||
CHECK(&tmHelper.storeHelper == &storeHelper);
|
||||
}
|
||||
|
||||
SECTION("Base Test") {
|
||||
tmHelper.prepareTmPacket(2);
|
||||
auto& creator = storeHelper.getCreatorRef();
|
||||
REQUIRE(creator.getSubService() == 2);
|
||||
REQUIRE(creator.getService() == 17);
|
||||
auto& params = creator.getParams();
|
||||
REQUIRE(params.dataWrapper.type == ecss::DataTypes::RAW);
|
||||
REQUIRE(params.dataWrapper.dataUnion.raw.data == nullptr);
|
||||
REQUIRE(params.dataWrapper.dataUnion.raw.len == 0);
|
||||
REQUIRE(tmHelper.sendCounter == 0);
|
||||
REQUIRE(tmHelper.storeAndSendTmPacket() == retval::OK);
|
||||
REQUIRE(tmHelper.sendCounter == 1);
|
||||
auto storeId = storeHelper.getCurrentAddr();
|
||||
REQUIRE(msgQueue.wasMessageSent());
|
||||
REQUIRE(msgQueue.numberOfSentMessagesToDefault() == 1);
|
||||
TmTcMessage msg;
|
||||
REQUIRE(msgQueue.getNextSentMessage(msg) == retval::OK);
|
||||
REQUIRE(msg.getStorageId() == storeId);
|
||||
REQUIRE(pool.hasDataAtId(msg.getStorageId()));
|
||||
storeHelper.deletePacket();
|
||||
}
|
||||
|
||||
SECTION("Raw Data Helper") {
|
||||
std::array<uint8_t, 3> data = {1, 2, 3};
|
||||
REQUIRE(tmHelper.prepareTmPacket(2, data.data(), data.size()) == retval::OK);
|
||||
auto& creator = storeHelper.getCreatorRef();
|
||||
auto& params = creator.getParams();
|
||||
REQUIRE(params.dataWrapper.type == ecss::DataTypes::RAW);
|
||||
REQUIRE(params.dataWrapper.dataUnion.raw.data == data.data());
|
||||
REQUIRE(params.dataWrapper.dataUnion.raw.len == data.size());
|
||||
}
|
||||
|
||||
SECTION("Serializable Helper") {
|
||||
auto simpleSer = SimpleSerializable();
|
||||
REQUIRE(tmHelper.prepareTmPacket(2, simpleSer) == retval::OK);
|
||||
auto& creator = storeHelper.getCreatorRef();
|
||||
auto& params = creator.getParams();
|
||||
REQUIRE(params.dataWrapper.type == ecss::DataTypes::SERIALIZABLE);
|
||||
REQUIRE(params.dataWrapper.dataUnion.serializable == &simpleSer);
|
||||
}
|
||||
|
||||
SECTION("Object ID prefix Helper") {
|
||||
uint32_t objectId = 0x01020304;
|
||||
std::array<uint8_t, 3> data = {1, 2, 3};
|
||||
telemetry::DataWithObjectIdPrefix dataWithObjId(objectId, data.data(), data.size());
|
||||
REQUIRE(tmHelper.prepareTmPacket(2, dataWithObjId) == retval::OK);
|
||||
auto& creator = storeHelper.getCreatorRef();
|
||||
auto& params = creator.getParams();
|
||||
REQUIRE(params.dataWrapper.type == ecss::DataTypes::SERIALIZABLE);
|
||||
REQUIRE(params.dataWrapper.dataUnion.serializable == &dataWithObjId);
|
||||
}
|
||||
|
||||
// TODO: Error handling
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user