Skip to content
Snippets Groups Projects
Commit 12806ba2 authored by Grigoris Pavlakis's avatar Grigoris Pavlakis
Browse files

Merge remote-tracking branch 'origin/ST-20-ParameterService-FP' into ST-20-ParameterService-FP

# Conflicts:
#	inc/Services/Parameter.hpp
#	src/Services/ParameterService.cpp
#	test/Services/ParameterService.cpp
parents 187a4ffb 807648b6
No related branches found
No related tags found
No related merge requests found
......@@ -53,4 +53,8 @@
#define ECSS_MAX_DELTA_OF_RELEASE_TIME 60
// release time and the actual release time
/**
* @brief Size of the map holding the Parameter objects for the ST[20] parameter service
*/
#define ECSS_ST_20_MAX_PARAMETERS 5
#endif // ECSS_SERVICES_ECSS_DEFINITIONS_H
......@@ -70,9 +70,13 @@ public:
*/
OtherMessageType = 9,
/**
* Attempt to insert new function in a full function map (ST[08])
* Attempt to insert new element in a full map (ST[08], ST[20])
*/
FunctionMapFull = 10,
MapFull = 10,
/**
* Attempt to overwrite an existing parameter (ST[20])
*/
ExistingParameterId = 11
};
/**
......
......@@ -4,7 +4,7 @@
#include "etl/bitset.h"
// Number of binary flags in every parameter. Final number TBD.
#define NUM_OF_FLAGS 5
#define NUM_OF_FLAGS 3
/**
* Implementation of a Parameter field, as specified in ECSS-E-ST-70-41C.
......@@ -20,10 +20,12 @@
* @typedef ParamId: the unique ID of a parameter, used for searching
* @typedef ValueType: the type of the parameter's value (changing types is WIP)
* @typedef UpdatePtr: pointer to a void function, with a single ValueType* argument (return address)
* @typedef Flags: container for the binary flags
*/
typedef uint16_t ParamId;
typedef uint32_t ValueType;
typedef void(*UpdatePtr)(ValueType*);
typedef etl::bitset<NUM_OF_FLAGS> Flags;
/**
* Parameter class - Breakdown of fields
......@@ -36,18 +38,20 @@ typedef void(*UpdatePtr)(ValueType*);
* @todo: Find a way to store arbitrary types in currentValue
*
* Additional features (not included in standard):
* @private flags: Various binary flags (number and meaning TBD). Ideas:
* update with priority, do not poll, do not allow manual manipulation etc.
* @private flags: Various binary flags (number and meaning TBD).
* @warning Current flag meanings (starting from LSB, big-endian):
* Index 0: update with priority
* Index 1: manual update available
* Index 2: automatic update available
*
*
* Methods:
* @public Parameter(): default constructor, do not use.
* @public Parameter(uint8_t newPtc, uint8_t newPfc, uint32_t initialValue = 0, UpdatePtr newPtr = nullptr):
* Create a new Parameter object with newPtc PTC, newPfc PFC, initialValue as its starting value and newPtr
* as its update function pointer. Arguments initialValue and newPtr are optional, and have default values of
* 0 and nullptr respectively.
*
* @public setCurrentValue(): Changes the current value of the parameter (todo: if the respective flag is not set)
* @public setCurrentValue(): Changes the current value of the parameter
* @public getCurrentValue(): Gets the current value of the parameter
* @public getPTC(), getPFC(): Returns the PFC and PTC of the parameter
*/
......@@ -55,15 +59,14 @@ class Parameter {
uint8_t ptc;
uint8_t pfc;
UpdatePtr ptr;
etl::bitset<NUM_OF_FLAGS> flags;
Flags flags;
ValueType currentValue = 0;
public:
Parameter();
Parameter(uint8_t newPtc, uint8_t newPfc, uint32_t initialValue = 0, UpdatePtr newPtr = nullptr);
void setCurrentValue(ValueType newVal);
//void setFlag();
void setFlags(const char* flags);
ValueType getCurrentValue();
uint8_t getPTC();
......
#ifndef ECSS_SERVICES_PARAMETERSERVICE_HPP
#define ECSS_SERVICES_PARAMETERSERVICE_HPP
#include "ECSS_Definitions.hpp"
#include "Service.hpp"
#include "ErrorHandler.hpp"
#include "Parameter.hpp"
#include "etl/map.h"
// Number of stored parameters. MAX_PARAMS is just a dummy number for now.
#define MAX_PARAMS 5
// TODO: 1) Rework the parameter setting and report functions
// TODO: 2) Implement flags and use them above
// TODO: 3) Write more and better tests
// TODO: 4) Make sure that docs are up to date
// TODO: 5) Optimize stuff if possible
#include "etl/vector.h"
/**
* Implementation of the ST[20] parameter management service,
......@@ -27,45 +21,44 @@
* for parameter reporting and modification.
*
* The parameter list is stored in a map with the parameter IDs as keys and values
* corresponding Parameter structs containing the PTC, PFC and the parameter's value.
* corresponding Parameter classes containing the PTC, PFC and the parameter's value.
*/
class ParameterService : public Service {
private:
etl::map<ParamId, Parameter, MAX_PARAMS> paramsList;
uint16_t numOfValidIds(Message idMsg); // count the valid ids in a given TC[20, 1]
etl::map<ParamId, Parameter, ECSS_ST_20_MAX_PARAMETERS> paramsList;
public:
/**
* Initializes the parameter list.
* @brief Initializes the parameter list.
*/
ParameterService();
/**
* Adds a new parameter. If the parameter has not been added (either because the map is full or because it already
* exists in it) then returns false.
* @brief Adds a new parameter. Emits an InternalError::MapFull if an attempt is made to insert
* parameters in a full map or an InternalError::ExistingParameterId if the given parameter ID
* exists already.
* @param id: the desired ID for this parameter
* @param param: the parameter field to be included
* @param flags: the flags to be set for this field (see Parameter.hpp)
*/
bool addNewParameter(uint8_t ptc, uint8_t pfc, uint32_t initialValue = 0, UpdatePtr ptr = nullptr);
void addNewParameter(uint16_t id, Parameter param, const char* flags = "110");
/**
* This function receives a TC[20, 1] packet and returns a TM[20, 2] packet
* containing the current configuration
* **for the parameters specified in the carried valid IDs**.
*
* No sophisticated error checking for now, just whether the packet is of the correct type
* and whether the requested IDs are valid, ignoring the invalid ones.
* If the packet has an incorrect header, an InternalError::UnacceptablePacket is raised.
* The packet is checked for errors in service and message type, as well as for the
* validity of the IDs contained. For every invalid ID an ExecutionStartErrorType::UnknownExecutionStartError
* is raised.
* If the packet has an incorrect header and service type, an InternalError::UnacceptableMessage is raised.
* If no IDs are correct, the returned message shall be empty.
*
* @param paramId: a valid TC[20, 1] packet carrying the requested parameter IDs
* @param paramId: a TC[20, 1] packet carrying the requested parameter IDs
* @return None (messages are stored using storeMessage())
*
*
* NOTES:
* Method for valid ID counting is a hack (clones the message and figures out the number
* separately, due to message access being non-random). Should be enough for now.
*
* Everything apart from the setting data is uint16 (setting data are uint32 for now)
*/
void reportParameterIds(Message& paramIds);
......@@ -73,7 +66,8 @@ public:
/**
* This function receives a TC[20, 3] message and after checking whether its type is correct,
* iterates over all contained parameter IDs and replaces the settings for each valid parameter,
* while ignoring all invalid IDs.
* while ignoring all invalid IDs. If the manual update flag is not set, the parameter's value should
* not change.
*
* @param newParamValues: a valid TC[20, 3] message carrying parameter ID and replacement value
* @return None
......
......@@ -42,7 +42,7 @@ void FunctionManagementService::include(String<FUNC_NAME_LENGTH> funcName, void
funcName.append(FUNC_NAME_LENGTH - funcName.length(), 0);
funcPtrIndex.insert(std::make_pair(funcName, ptr));
} else {
ErrorHandler::reportInternalError(ErrorHandler::InternalErrorType::FunctionMapFull);
ErrorHandler::reportInternalError(ErrorHandler::InternalErrorType::MapFull);
}
}
......
#include "Services/Parameter.hpp"
Parameter::Parameter() {
ptc = 0;
pfc = 0;
ptr = nullptr;
}
Parameter::Parameter(uint8_t newPtc, uint8_t newPfc, uint32_t initialValue, UpdatePtr newPtr) {
Parameter::Parameter(uint8_t newPtc, uint8_t newPfc, ValueType initialValue, UpdatePtr newPtr) {
ptc = newPtc;
pfc = newPfc;
ptr = newPtr;
// see Parameter.hpp for explanation on flags
// by default: no update priority, manual and automatic update available
if (ptr != nullptr) {
(*ptr)(&currentValue); // call the update function for the initial value
} else {
......@@ -19,7 +16,10 @@ Parameter::Parameter(uint8_t newPtc, uint8_t newPfc, uint32_t initialValue, Upda
}
void Parameter::setCurrentValue(ValueType newVal) {
currentValue = newVal;
// set the value only if the parameter can be updated manually
if (flags[1]) {
currentValue = newVal;
}
}
ValueType Parameter::getCurrentValue() {
......@@ -33,3 +33,7 @@ uint8_t Parameter::getPTC() {
uint8_t Parameter::getPFC() {
return pfc;
}
void Parameter::setFlags(const char* flags) {
this->flags = Flags(flags);
}
......@@ -7,26 +7,35 @@ ParameterService::ParameterService() {
// addNewParameter(3, 14);
}
bool ParameterService::addNewParameter(uint8_t ptc, uint8_t pfc, uint32_t initialValue, UpdatePtr ptr) {
Parameter param = Parameter(ptc, pfc, initialValue, ptr);
try {
// second element of the returned std::pair is whether the given item was inserted or not
return paramsList.insert(std::make_pair(paramsList.size() + 1, param)).second;
void ParameterService::addNewParameter(uint16_t id, Parameter param, const char* flags) {
if (paramsList.full()) {
ErrorHandler::reportInternalError(ErrorHandler::InternalErrorType::MapFull);
return;
}
catch(etl::map_full) {
return false;
else {
if (paramsList.find(id) == paramsList.end()) {
param.setFlags(flags);
paramsList.insert(std::make_pair(id, param));
return;
}
else {
ErrorHandler::reportInternalError(ErrorHandler::InternalErrorType::ExistingParameterId);
return;
}
}
}
void ParameterService::reportParameterIds(Message& paramIds) {
paramIds.assertTC(20, 1);
Message reqParam(20, 2, Message::TM, 1); // empty TM[20, 2] parameter report message
etl::vector<std::pair<uint16_t, ValueType>, ECSS_ST_20_MAX_PARAMETERS> validParams;
Message reqParam(20, 2, Message::TM, 1);
// empty TM[20, 2] parameter report message
paramIds.resetRead(); // since we're passing a reference, the reading position shall be reset
paramIds.resetRead();
// since we're passing a reference, the reading position shall be reset
// to its default before any read operations (to ensure the correct data is being read)
// assertion: correct message, packet and service type (at failure throws an
// InternalError::UnacceptablePacket)
// InternalError::UnacceptableMessage)
ErrorHandler::assertRequest(paramIds.packetType == Message::TC, paramIds,
ErrorHandler::AcceptanceErrorType::UnacceptableMessage);
ErrorHandler::assertRequest(paramIds.messageType == 1, paramIds,
......@@ -34,29 +43,35 @@ void ParameterService::reportParameterIds(Message& paramIds) {
ErrorHandler::assertRequest(paramIds.serviceType == 20, paramIds,
ErrorHandler::AcceptanceErrorType::UnacceptableMessage);
uint16_t numOfIds = paramIds.readUint16(); // number of parameter IDs carried in the message
// uint16_t numOfValidIds = 0; // number of IDs that are actually included in the list
// reqParam.skipBytes(2); // skip the first 16 bits where the number of valid IDs will be included
reqParam.appendUint16(numOfValidIds(paramIds)); // include the number of valid IDs
uint16_t numOfIds = paramIds.readUint16(); // total number of parameter IDs carried in the message
uint16_t validIds = 0; // number of valid IDs
for (uint16_t i = 0; i < numOfIds; i++) {
uint16_t currId = paramIds.readUint16(); // current ID to be appended
uint16_t currId = paramIds.readUint16();
if (paramsList.find(currId) != paramsList.end()) {
reqParam.appendUint16(currId);
reqParam.appendUint32(paramsList[currId].getCurrentValue());
} else {
std::pair<uint16_t, ValueType> p = std::make_pair(currId, paramsList.at(currId).getCurrentValue());
// pair containing the parameter's ID as first element and its current value as second
validParams.push_back(p);
validIds++;
}
else {
ErrorHandler::reportError(paramIds, ErrorHandler::ExecutionStartErrorType::UnknownExecutionStartError);
continue; // generate failed start of execution notification & ignore
}
}
storeMessage(reqParam);
reqParam.appendUint16(validIds); // append the number of valid IDs
for (auto i: validParams) {
reqParam.appendUint16(i.first); // append the parameter ID
reqParam.appendUint32(i.second); // and its value
}
storeMessage(reqParam); // then store the message
}
void ParameterService::setParameterIds(Message& newParamValues) {
newParamValues.assertTC(20, 3);
// assertion: correct message, packet and service type (at failure throws an
// InternalError::UnacceptablePacket which gets logged)
......@@ -71,39 +86,17 @@ void ParameterService::setParameterIds(Message& newParamValues) {
for (uint16_t i = 0; i < numOfIds; i++) {
uint16_t currId = newParamValues.readUint16();
// the parameter is checked for read-only status and manual update availability
if (paramsList.find(currId) != paramsList.end()) {
paramsList[currId].setCurrentValue(newParamValues.readUint32()); // TODO: add a check here with the new
// flag functionality
} else {
paramsList.at(currId).setCurrentValue(newParamValues.readUint32());
}
else {
ErrorHandler::reportError(newParamValues,
ErrorHandler::ExecutionStartErrorType::UnknownExecutionStartError);
ErrorHandler::ExecutionStartErrorType::UnknownExecutionStartError);
continue; // generate failed start of execution notification & ignore
}
}
}
uint16_t ParameterService::numOfValidIds(Message idMsg) {
idMsg.resetRead();
// start reading from the beginning of the idMsg object
// (original obj. will not be influenced if this is called by value)
uint16_t ids = idMsg.readUint16(); // first 16bits of the packet are # of IDs
uint16_t validIds = 0;
for (uint16_t i = 0; i < ids; i++) {
uint16_t currId = idMsg.readUint16();
if (idMsg.messageType == 3) {
idMsg.readUint32(); // skip the 32bit settings blocks, we need only the IDs
}
if (paramsList.find(currId) != paramsList.end()) {
validIds++;
}
}
return validIds;
}
void ParameterService::execute(Message& message) {
switch (message.messageType) {
......
......@@ -71,6 +71,6 @@ TEST_CASE("ST[08] - Insert Tests") {
name += std::to_string(i); // different names to fill up the map
fms.include(String<FUNC_NAME_LENGTH>(name.c_str()), &test);
}
CHECK(ServiceTests::thrownError(ErrorHandler::InternalErrorType::FunctionMapFull));
CHECK(ServiceTests::thrownError(ErrorHandler::InternalErrorType::MapFull));
}
}
......@@ -9,114 +9,174 @@ void foo(ValueType* bar) { // sample function
*bar = 0xDEADBEEF;
}
/* test ideas:
* parameter setting while flag is active
*
*
*/
TEST_CASE("Parameter Service - General") {
SECTION("Parameter Setup") {
pserv.addNewParameter(3, 14); // this one has ID 0
pserv.addNewParameter(1, 7, 12); // this one has 1
pserv.addNewParameter(4, 12, 3, nullptr); // this one has 2
pserv.addNewParameter(12, 3, 6, &foo); // this one has 3
pserv.addNewParameter(15, 7, 3, &foo); //and this one 4
SECTION("Addition to full map") {
Parameter param0 = Parameter(3, 14);
Parameter param1 = Parameter(1, 7, 12);
Parameter param2 = Parameter(4, 12, 3, nullptr);
Parameter param3 = Parameter(12, 3, 6, &foo);
Parameter param4 = Parameter(15, 7, 3, &foo);
Parameter param5 = Parameter(15, 5, 4);
pserv.addNewParameter(0, param0);
pserv.addNewParameter(1, param1);
pserv.addNewParameter(2, param2);
pserv.addNewParameter(3, param3);
pserv.addNewParameter(4, param4);
pserv.addNewParameter(5, param5); // addNewParameter should return false
CHECK(ServiceTests::thrownError(ErrorHandler::InternalErrorType::MapFull));
ServiceTests::reset();
Services.reset(); // reset all services
}
SECTION("Addition to full map") {
CHECK(pserv.addNewParameter(15, 5, 4));
SECTION("Addition of already existing parameter") {
Parameter param0 = Parameter(1, 3);
pserv.addNewParameter(0, param0);
pserv.addNewParameter(0, param0);
CHECK(ServiceTests::thrownError(ErrorHandler::InternalErrorType::ExistingParameterId));
ServiceTests::reset();
Services.reset();
}
}
TEST_CASE("Parameter Report Subservice") {
SECTION("All requested parameters invalid") {
Message request = Message(20, 1, Message::TC, 1);
request.appendUint16(3);
request.appendUint16(54432);
request.appendUint16(60000);
request.appendUint16(65535);
SECTION("Faulty Instruction Handling Test") {
Message request(20, 1, Message::TC, 1);
Message report(20, 2, Message::TM, 1);
MessageParser::execute(request);
CHECK(ServiceTests::get(0).serviceType == 1);
CHECK(ServiceTests::get(0).messageType == 4);
CHECK(ServiceTests::get(1).serviceType == 1);
CHECK(ServiceTests::get(1).messageType == 4);
CHECK(ServiceTests::get(2).serviceType == 1);
CHECK(ServiceTests::get(2).messageType == 4);
Message report = ServiceTests::get(3);
CHECK(report.serviceType == 20);
CHECK(report.messageType == 2);
CHECK(report.readUint16() == 0); // the message shall be empty
ServiceTests::reset();
Services.reset();
}
SECTION("Faulty instruction handling") {
Parameter param0 = Parameter(3, 14);
Parameter param1 = Parameter(1, 7, 12);
Parameter param2 = Parameter(4, 12, 3, nullptr);
pserv.addNewParameter(0, param0);
pserv.addNewParameter(1, param1);
pserv.addNewParameter(2, param2);
Message request(20, 1, Message::TC, 1);
request.appendUint16(2); // number of requested IDs
request.appendUint16(65535); // faulty ID in this context
request.appendUint16(65535); // invalid ID in this context
request.appendUint16(1); // valid
MessageParser::execute(request);
Message report = ServiceTests::get(1);
CHECK(((ServiceTests::get(0).messageType == 4) && (ServiceTests::get(0).serviceType == 1)));
CHECK(ServiceTests::get(0).messageType == 4);
CHECK(ServiceTests::get(0).serviceType == 1);
// check for an ST[1,4] message caused by the faulty ID
CHECK((ServiceTests::thrownError(ErrorHandler::ExecutionStartErrorType::UnknownExecutionStartError)));
// check for the thrown UnknownExecutionStartError
CHECK(((ServiceTests::get(1).messageType == 2) && (ServiceTests::get(1).serviceType == 20)));
CHECK(report.messageType == 2);
CHECK(report.serviceType == 20);
// check for an ST[20,2] message (the one that contains the settings)
ServiceTests::reset();
}
CHECK(report.readUint16() == 1); // only one parameter shall be contained
CHECK(report.readUint16() == 1); // check for parameter ID
CHECK(report.readUint32() == 12); // check for value (defined when adding parameters)
ServiceTests::reset(); // clear all errors
Services.reset(); // reset the services
}
// **WARNING**
// TODO: Update this test (and all tests in general) to use the error handler's output when
// checking for assertions.
SECTION("Wrong Message Type Handling Test") {
Message falseRequest(15, 3, Message::TM, 1); // a totally wrong message
Message falseRequest(62, 3, Message::TM, 1); // a totally wrong message
MessageParser::execute(falseRequest);
Message errorNotif = ServiceTests::get(0);
CHECK(errorNotif.messageType == 4); // check for proper failed start of
// execution notification
CHECK(errorNotif.serviceType == 1);
Message response = ServiceTests::get(1);
CHECK(response.messageType == 2);
CHECK(response.serviceType == 20);
CHECK(response.packetType == Message::TM);
CHECK(response.readPosition == 0); // if empty, this should't change from 0
CHECK(ServiceTests::thrownError(ErrorHandler::InternalErrorType::OtherMessageType));
ServiceTests::reset();
Services.reset();
}
}
TEST_CASE("Parameter Setting Subservice") {
SECTION("Faulty Instruction Handling Test") {
Message setRequest(20, 3, Message::TC, 1);
Message reportRequest(20, 1, Message::TC, 1);
Parameter param0 = Parameter(3, 14);
Parameter param1 = Parameter(1, 7, 12);
Parameter param2 = Parameter(4, 12, 3, nullptr);
pserv.addNewParameter(0, param0);
pserv.addNewParameter(1, param1);
pserv.addNewParameter(2, param2);
Message setRequest(20, 3, Message::TC, 1);
setRequest.appendUint16(2); // total number of IDs
setRequest.appendUint16(1); // correct ID in this context
setRequest.appendUint32(3735928559); // 0xDEADBEEF in hex (new setting)
setRequest.appendUint16(16742); // faulty ID in this context
setRequest.appendUint16(65535); // faulty ID
setRequest.appendUint32(3131746989); // 0xBAAAAAAD (this shouldn't be found in the report)
reportRequest.appendUint16(2);
reportRequest.appendUint16(16742);
reportRequest.appendUint16(1); // used to be 3, which pointed the bug with
// numOfValidIds out, now is 1 in order to be a valid ID (a separate test for
// numOfValidIds shall be introduced)
MessageParser::execute(setRequest);
CHECK(ServiceTests::get(0).serviceType == 1);
CHECK(ServiceTests::get(0).messageType == 4);
Message reportRequest(20, 1, Message::TC, 1);
reportRequest.appendUint16(1);
reportRequest.appendUint16(1); // the changed parameter has ID 1
// Since every reporting and setting is called with the same (sometimes faulty) parameters,
// and there are errors generated (as should be) it is important to catch and check for
// them in order to preserve the integrity of the test.
MessageParser::execute(reportRequest);
Message errorNotif1 = ServiceTests::get(0);
CHECK(errorNotif1.messageType == 4);
CHECK(errorNotif1.serviceType == 1);
Message report = ServiceTests::get(1);
CHECK(report.serviceType == 20);
CHECK(report.messageType == 2);
CHECK(report.readUint16() == 1); // only 1 ID contained
CHECK(report.readUint16() == 1); // contained ID should be ID 1
CHECK(report.readUint32() == 3735928559); // whose value is 0xDEADBEEF
ServiceTests::reset();
Services.reset();
}
SECTION("Attempt to set parameter with no manual update availability") {
Parameter param1 = Parameter(1, 7, 12);
pserv.addNewParameter(1, param1, "100");
Message before = ServiceTests::get(1);
Message setRequest = Message(20, 3, Message::TC, 1);
setRequest.appendUint16(1);
setRequest.appendUint16(1);
setRequest.appendUint32(0xBAAAAAAD);
MessageParser::execute(setRequest);
Message errorNotif2 = ServiceTests::get(2);
CHECK(errorNotif2.messageType == 4);
CHECK(errorNotif2.serviceType == 1);
MessageParser::execute(reportRequest);
Message errorNotif3 = ServiceTests::get(3);
CHECK(errorNotif3.messageType == 4);
CHECK(errorNotif3.serviceType == 1);
Message infoRequest = Message(20, 1, Message::TC, 1);
infoRequest.appendUint16(1);
infoRequest.appendUint16(1);
Message after = ServiceTests::get(4);
MessageParser::execute(infoRequest);
before.readUint16();
after.readUint16(); // skip the number of IDs
Message report = ServiceTests::get(0);
while (after.readPosition <= after.dataSize) {
CHECK(before.readUint16() == after.readUint16()); // check if all IDs are present
CHECK_FALSE(after.readUint32() == 0xBAAAAAAD); // fail if any settings are BAAAAAAD :P
}
CHECK(report.readUint16() == 1);
CHECK(report.readUint16() == 1);
CHECK_FALSE(report.readUint32() == 0xBAAAAAAD);
ServiceTests::reset();
Services.reset();
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment