Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • acubesat/obc/ecss-services
  • elefthca/ecss-services
  • ggkogkou/ecss-services
  • kongr45gpen/ecss-services
  • avidmohajerani/ecss-services
  • xlxs4/ecss-services
6 results
Show changes
Commits on Source (16)
......@@ -18,6 +18,7 @@ add_custom_target(check
add_library(common OBJECT
src/Service.cpp
src/ErrorHandler.cpp
src/Logger.cpp
src/Message.cpp
src/MessageParser.cpp
src/ServicePool.cpp
......@@ -39,10 +40,11 @@ add_library(common OBJECT
# Specify the .cpp files for the executables
file(GLOB x86_main_SRC "src/Platform/x86/*.cpp")
add_executable(ecss_services
src/main.cpp
$<TARGET_OBJECTS:common>
${x86_main_SRC}
)
set_target_properties(ecss_services
PROPERTIES COMPILE_DEFINITIONS LOGLEVEL_TRACE)
IF (EXISTS "${PROJECT_SOURCE_DIR}/lib/Catch2/CMakeLists.txt")
# Gather all the .cpp files corresponding to tests
......
......@@ -12,13 +12,8 @@ cd "$(dirname "$0")/.."
echo -e "\u001b[34;1mGetting prerequisites...\u001b[0m"
# grab the MISRA addon and the cppcheck addon interface from github
curl https://raw.githubusercontent.com/danmar/cppcheck/f4b5b156d720c712f6ce99f6e01d8c1b3f800d52/addons/misra.py > ci/misra.py
curl https://raw.githubusercontent.com/danmar/cppcheck/f4b5b156d720c712f6ce99f6e01d8c1b3f800d52/addons/cppcheckdata.py > ci/cppcheckdata.py
# clean up old files
echo -e "\u001b[34;1mRemoving old files...\u001b[0m"
echo > ci/report.msr # clear the report file
find inc/ src/ -type f -name "*.dump" | xargs rm
curl https://raw.githubusercontent.com/danmar/cppcheck/master/addons/misra.py > ci/misra.py
curl https://raw.githubusercontent.com/danmar/cppcheck/master/addons/cppcheckdata.py > ci/cppcheckdata.py
# generate dump files (XML representations of AST etc.) for all headers, source files etc.
echo -e "\u001b[34;1mGenerating dump files...\u001b[0m"
......@@ -30,9 +25,18 @@ find inc/ src/ -type f -name "*.dump" | xargs python3 ci/misra.py >> ci/report.m
# pre-process the generated report to remove all useless strings
echo -e "\u001b[34;1mPre-processing report...\u001b[0m"
sed -i -r 's/(.*Script.*)|(.*Checking.*)|(.*MISRA.*)//gm; /(^$)/d; s/(\s\(.*\)\s)//gm; s/(\]|\[)//gm; s/(misra-c2012-)//gm' ci/report.msr
sed -i -r 's/(.*Script.*)|(.*Checking.*)|(.*MISRA.*)|(.*Undefined: .*)|(.* \(-\):.*)//gm; /(^$)/d; s/(\s\(.*\)\s)//gm; s/(\]|\[)//gm; s/(misra-c2012-)/:/gm' ci/report.msr
# run the summarizer for a nice, clean summary of errors
echo -e "\u001b[34;1mSummarizing results...\u001b[0m"
python3 ci/summarizer.py --report ci/report.msr --suppress 3.1 5.1 5.2 5.3 12.3 14.4 15.5 16.3 18.8
python3 ci/summarizer.py --report ci/report.msr --suppress 3.1 5.1 5.2 5.3 12.3 13.4 14.4 15.5 16.3 18.4 18.8
RETURN=$?
# clean up old files
echo -e "\u001b[34;1mRemoving dump files...\u001b[0m"
echo > ci/report.msr # clear the report file
find inc/ src/ -type f -name "*.dump" | xargs rm
# finally return the return value of the summarizer.py script
exit $RETURN
......@@ -58,7 +58,7 @@ class Summarizer(object):
with open(os.path.abspath(file_name)) as code_file:
code_lines = code_file.readlines() # Read the source code file
line_of_interest = code_lines[int(violation[0]) - 1] # Get the desired violation line
if line_of_interest.find("// Ignore-MISRA") >= 0:
if line_of_interest.find("// Ignore-MISRA") >= 0 or line_of_interest.find("/* Ignore-MISRA */") >= 0:
continue
if file_name not in self.violations_map.keys():
......
......@@ -10,4 +10,5 @@
echo -e "\033[0;34mRunning vera++...\033[0m"
cd "$(dirname "$0")/.."
vera++ --error --parameter max-line-length=120 --profile custom `find src inc test -type f -regextype posix-egrep -regex '.*\.(cpp|hpp|c|h)'`
vera++ --error --parameter max-line-length=120 --profile custom \
`find src inc test -type f -regextype posix-egrep -regex '.*\.(cpp|hpp|c|h)' -not -wholename 'inc/Logger.hpp'`
......@@ -53,4 +53,9 @@
#define ECSS_MAX_DELTA_OF_RELEASE_TIME 60
// release time and the actual release time
/**
* @brief The maximum size of a log message
*/
#define LOGGER_MAX_MESSAGE_SIZE 512
#endif // ECSS_SERVICES_ECSS_DEFINITIONS_H
#ifndef ECSS_SERVICES_LOGGER_HPP
#define ECSS_SERVICES_LOGGER_HPP
#include <cstdint>
#include <etl/String.hpp>
#include <etl/to_string.h>
#include <ECSS_Definitions.hpp>
#if defined LOGLEVEL_TRACE
#define LOGLEVEL Logger::trace // Ignore-MISRA
#elif defined LOGLEVEL_DEBUG
#define LOGLEVEL Logger::debug // Ignore-MISRA
#elif defined LOGLEVEL_INFO
#define LOGLEVEL Logger::info // Ignore-MISRA
#elif defined LOGLEVEL_NOTICE
#define LOGLEVEL Logger::notice // Ignore-MISRA
#elif defined LOGLEVEL_WARNING
#define LOGLEVEL Logger::warning // Ignore-MISRA
#elif defined LOGLEVEL_ERROR
#define LOGLEVEL Logger::error // Ignore-MISRA
#elif defined LOGLEVEL_EMERGENCY
#define LOGLEVEL Logger::emergency // Ignore-MISRA
#else
#define LOGLEVEL Logger::disabled // Ignore-MISRA
#endif
/**
* Internal define to check if logging is enabled for a level
* @param level A log level
* @return bool
*/
#define _ac_LOGGER_ENABLED_LEVEL(level) /* Ignore-MISRA */ \
(( (Logger::LogLevelType) LOGLEVEL) <= ( (Logger::LogLevelType) level))
/**
* Create a stream to log a Message
*
* This functions appends one line to the Logs (which could be printed to screen, transferred via UART or stored for
* later use.)
*
* Examples of usage: \n
* `LOG(Logger::debug) << "Reached point of no return";` \n
* `LOG(Logger::error) << "More than " << 50 << " dogs found!";`
*
* You can also use one of the \ref LOG_TRACE, \ref LOG_DEBUG, \ref LOG_INFO, \ref LOG_NOTICE, \ref LOG_WARNING,
* \ref LOG_ERROR or \ref LOG_EMERGENCY defines, which avoid the need of explicitly passing the log level: \n
* `LOG_DEBUG << "Reached point of no return";` \n
* `LOG_ERROR << "More than " << 50 << " dogs found!";`
*
* See \ref Logger::LogLevel for an explanation of the different log levels.
*
* @par Implementation details
* This macro uses a trick to pass an object where the `<<` operator can be used, and which is logged when the statement
* is complete. It uses an `if` statement, initializing a variable within its condition. According to the C++98
* standard (1998), Clause 3.3.2.4, "Names declared in the [..] condition of the if statement are local to the if [...]
* statement (including the controlled statement) [...]". This result in the \ref Logger::LogEntry::~LogEntry()
* to be called as soon as the statement is complete. The bottom `if` statement serves this purpose, and is always
* evaluated to true to ensure execution.
*
* @par
* Additionally, the top `if` checks the sufficiency of the log level. It should be optimized away at compile-time on
* invisible log entries, meaning that there is no performance overhead for unused calls to LOG.
*
* @section GlobalLogLevels Global log levels
* The **global log level** defines the minimum severity of events to be displayed. Log entries with a severity equal
* to or higher than the global log level will be shown. Log entries with a severity smaller than the global log level
* will not be shown.
*
* The global log level can be set by defining one of the following constants:
* - `LOGLEVEL_TRACE`
* - `LOGLEVEL_DEBUG`
* - `LOGLEVEL_INFO`
* - `LOGLEVEL_NOTICE`
* - `LOGLEVEL_WARNING`
* - `LOGLEVEL_ERROR`
* - `LOGLEVEL_EMERGENCY`
*
* @relates Logger
* @param level The log level. A value of \ref Logger::LogEntry
*/
#define LOG(level) /* Ignore-MISRA */ \
if (_ac_LOGGER_ENABLED_LEVEL(level)) \
if (Logger::LogEntry entry(level); true) \
entry
#define LOG_TRACE LOG(Logger::trace) ///< @see LOG @relates Logger
#define LOG_DEBUG LOG(Logger::debug) ///< @see LOG @relates Logger
#define LOG_INFO LOG(Logger::info) ///< @see LOG @relates Logger
#define LOG_NOTICE LOG(Logger::notice) ///< @see LOG @relates Logger
#define LOG_WARNING LOG(Logger::warning) ///< @see LOG @relates Logger
#define LOG_ERROR LOG(Logger::error) ///< @see LOG @relates Logger
#define LOG_EMERGENCY LOG(Logger::emergency) ///< @see LOG @relates Logger
/**
* A logging class for ECSS Services that supports ETL's String and is lightweight enough to be used in embedded
* development.
*
* @note Always use the \ref LOG macro and its associated utility macros to log. Do not directly use the Logger class.
*/
class Logger {
public:
/**
* No need to instantiate a Logger object for now.
*/
Logger() = delete;
/**
* The underlying type to be used for values of Logger::LogLevel.
*/
typedef uint8_t LogLevelType;
/**
* Log levels supported by the logger. Each level represents a different severity of the logged Message,
* and messages of lower severities can be filtered on top of more significant ones.
*
* Each severity is tied to a number. The higher the number, the higher the severity.
*/
enum LogLevel {
trace = 32, ///< Very detailed information, useful for tracking the individual steps of an operation
debug = 64, ///< General debugging information
info = 96, ///< Noteworthy or periodical events
notice = 128, ///< Uncommon but expected events
warning = 160, ///< Unexpected events that do not compromise the operability of a function
error = 192, ///< Unexpected failure of an operation
emergency = 254, ///< Unexpected failure that renders the entire system unusable
disabled = 255, ///< Use this log level to disable logging entirely. No message should be logged as disabled.
};
/**
* A class that defines a log message.
*
* Instead of using this class, prefer one of the above macros
*/
struct LogEntry {
String<LOGGER_MAX_MESSAGE_SIZE> message = ""; ///< The current log message itself, starting from a blank slate
etl::format_spec format; ///< ETL's string format specification
LogLevel level; ///< The log level of this message
explicit LogEntry(LogLevel level); ///< Create a new LogEntry
/**
* The LogEntry destructor gets called whenever a log message is finalized, and ready to be shown to the
* user. This function is responsible for calling the Logger::log function.
*/
~LogEntry();
LogEntry(LogEntry const&) = delete; ///< Unimplemented copy constructor
};
/**
* Store a new log message
*/
static void log(LogLevel level, String<LOGGER_MAX_MESSAGE_SIZE>& message);
};
/**
* Stream operator to append new values to a log record
*
* @relates Logger
* @tparam T The type of value to append
* @param entry The already existing Logger::LogEntry
* @param value The new value to add
* @return The new Logger::LogEntry where the value has been appended
*/
template <class T>
Logger::LogEntry& operator<<(Logger::LogEntry& entry, const T value) {
etl::to_string(value, entry.message, entry.format, true);
return entry;
}
Logger::LogEntry& operator<<(Logger::LogEntry& entry, const std::string & value);
#endif //ECSS_SERVICES_LOGGER_HPP
......@@ -85,7 +85,7 @@ public:
// We allocate this data statically, in order to make sure there is predictability in the
// handling and storage of messages
// TODO: Is it a good idea to not initialise this to 0?
uint8_t data[ECSS_MAX_MESSAGE_SIZE] = {'\0'};
uint8_t data[ECSS_MAX_MESSAGE_SIZE] = { 0 };
// private:
uint8_t currentBit = 0;
......
......@@ -3,7 +3,6 @@
#include <cstdint>
#include "Message.hpp"
#include <iostream> // This file should be removed
class ServicePool;
......@@ -12,8 +11,6 @@ class ServicePool;
*
* A member of the Service class should be used as a singleton, i.e. must be created only once in
* the code
*
* @todo Disable copy constructor
*/
class Service {
private:
......
Subproject commit f2f23395cde34704686233094d9619ad8d06453a
Subproject commit 5a7e181100dae73659db133783ae964c7661437b
#include <Logger.hpp>
// Reimplementation of the function for variable C strings
template <>
Logger::LogEntry& operator<<(Logger::LogEntry& entry, char* value) {
entry.message.append(value);
return entry;
}
// Reimplementation of the function for C strings
template <>
Logger::LogEntry& operator<<(Logger::LogEntry& entry, const char* value) {
entry.message.append(value);
return entry;
}
Logger::LogEntry::LogEntry(LogLevel level) : level(level) {
format.precision(3); // Set precision to 3 decimal digits
}
Logger::LogEntry::~LogEntry() {
// When the destructor is called, the log message is fully "designed". Now we can finally "display" it to the user.
Logger::log(level, message);
}
......@@ -134,7 +134,7 @@ void Message::readString(char* string, uint8_t size) {
ASSERT_REQUEST(size < ECSS_MAX_STRING_SIZE, ErrorHandler::StringTooShort);
memcpy(string, data + readPosition, size);
string[size] = '\0'; // todo: Use that for now to avoid problems. Later to be removed
string[size] = 0; // todo: Use that for now to avoid problems. Later to be removed
readPosition += size;
}
......@@ -144,7 +144,7 @@ void Message::readString(uint8_t* string, uint16_t size) {
ASSERT_REQUEST(size < ECSS_MAX_STRING_SIZE, ErrorHandler::StringTooShort);
memcpy(string, data + readPosition, size);
string[size] = '\0'; // todo: Use that for now to avoid problems. Later to be removed
string[size] = 0; // todo: Use that for now to avoid problems. Later to be removed
readPosition += size;
}
......
......@@ -7,6 +7,7 @@
#include <cxxabi.h>
#include <ErrorHandler.hpp>
#include <Message.hpp>
#include <Logger.hpp>
// TODO: Find a way to reduce the number of copies of this chunk
template void ErrorHandler::logError(const Message&, ErrorHandler::AcceptanceErrorType);
......@@ -18,23 +19,22 @@ template void ErrorHandler::logError(ErrorHandler::InternalErrorType);
template <typename ErrorType>
void ErrorHandler::logError(const Message& message, ErrorType errorType) {
std::cerr
LOG_ERROR
/*
* Gets the error class name from the template
* Note: This is g++-dependent code and should only be used for debugging.
*/
<< abi::__cxa_demangle(typeid(ErrorType).name(), nullptr, nullptr, nullptr) << " Error "
<< "[" << static_cast<uint16_t>(message.serviceType) << "," << static_cast<uint16_t>(message.messageType)
<< "]: " << errorType << std::endl;
<< "]: " << errorType;
}
template <typename ErrorType>
void ErrorHandler::logError(ErrorType errorType) {
std::cerr
LOG_ERROR
/*
* Gets the error class name from the template
* Note: This is g++-dependent code and should only be used for debugging.
*/
<< abi::__cxa_demangle(typeid(ErrorType).name(), nullptr, nullptr, nullptr) << " Error: " << errorType
<< std::endl;
<< abi::__cxa_demangle(typeid(ErrorType).name(), nullptr, nullptr, nullptr) << " Error: " << errorType;
}
#include <Logger.hpp>
#include <etl/String.hpp>
#include <iostream>
#include <ECSS_Definitions.hpp>
#include <chrono>
#include <iomanip>
// The implementation of this function appends ANSI codes that should add colours to a compatible terminal
void Logger::log(Logger::LogLevel level, String<LOGGER_MAX_MESSAGE_SIZE> & message) {
// Get the current time & date
std::time_t t = std::time(nullptr);
std::tm tm = *std::localtime(&t);
// Get the log level and its colour
std::string name;
std::string colour;
bool keepColour = false; // Whether to keep the colour in the rest of the message
if (level <= Logger::trace) {
name = "trace";
colour = "90"; // bright black
keepColour = true;
} else if (level <= Logger::debug) {
name = "debug";
colour = "90"; // bright black
} else if (level <= Logger::info) {
name = "info";
colour = "32"; // green
} else if (level <= Logger::notice) {
name = "notice";
colour = "36"; // cyan
} else if (level <= Logger::warning) {
name = "warning";
colour = "33"; // yellow
} else if (level <= Logger::error) {
name = "error";
colour = "31"; // red
} else {
name = "emergency";
colour = "31"; // red
keepColour = true;
}
std::ostringstream ss; // A string stream to create the log message
ss << "\033" "[0;90m" << std::put_time(&tm, "%FT%T%z") << "\033" "[0m "; // The date
ss << "[\033" "[1;" << colour << "m" << std::setfill(' ') << std::setw(7) << std::right
<< name << std::setw(0) << "\033" "[0m] "; // The log level
if (keepColour) {
ss << "\033" "[0;" << colour << "m";
}
ss << message.c_str(); // The message itself
if (keepColour) {
ss << "\033" "[0m";
}
ss << "\n";
std::cerr << ss.str();
}
// Reimplementation of the log function for C++ strings
// This is kept in the Platform files, since we don't want to mess with std::strings in the microcontroller
Logger::LogEntry& operator<<(Logger::LogEntry& entry, const std::string & value) {
entry.message.append(value.c_str());
return entry;
}
#include <iostream>
#include <iomanip>
#include <Logger.hpp>
#include "Service.hpp"
void Service::storeMessage(Message& message) {
// appends the remaining bits to complete a byte
message.finalize();
// Create a new stream to display the packet
std::ostringstream ss;
// Just print it to the screen
std::cout << "New " << ((message.packetType == Message::TM) ? "TM" : "TC") << "["
<< std::hex
// << std::dec
<< static_cast<int>(message.serviceType) << "," << static_cast<int>(message.messageType)
<< "] message!\n";
// std::cout << std::hex << std::setfill('0') << std::setw(2);
for (int i = 0; i < message.dataSize; i++) {
std::cout << static_cast<int>(message.data[i]);
std::cout << " ";
ss << "New " << ((message.packetType == Message::TM) ? "TM" : "TC") << "["
<< std::hex
<< static_cast<int>(message.serviceType) << "," << static_cast<int>(message.messageType)
<< "] message! ";
for (unsigned int i = 0; i < message.dataSize; i++) {
ss << static_cast<int>(message.data[i]) << " "; // Ignore-MISRA
}
std::cout << std::endl;
LOG_DEBUG << ss.str();
}
#include <iostream>
#include <ServicePool.hpp>
#include <Logger.hpp>
#include "Helpers/CRCHelper.hpp"
#include "Helpers/TimeHelper.hpp"
#include "Services/TestService.hpp"
......@@ -20,6 +21,8 @@
#include "etl/String.hpp"
int main() {
LOG_NOTICE << "ECSS Services test application";
Message packet = Message(0, 0, Message::TC, 1);
packet.appendString<5>("hello");
......@@ -71,7 +74,7 @@ int main() {
char pStr[4];
*pStr = 'T';
*(pStr + 1) = 'G';
*(pStr + 2) = '\0';
*(pStr + 2) = 0;
MemoryManagementService& memMangService = Services.memoryManagement;
Message rcvPack = Message(6, 5, Message::TC, 1);
......@@ -335,5 +338,6 @@ int main() {
receivedMsg = Message(11, 12, Message::TC, 1);
timeBasedSchedulingService.summaryReportActivitiesByID(receivedMsg);
LOG_NOTICE << "ECSS Services test complete";
return 0;
}
......@@ -39,7 +39,7 @@ void FunctionManagementService::call(Message& msg) {
void FunctionManagementService::include(String<FUNC_NAME_LENGTH> funcName, void (*ptr)(String<MAX_ARG_LENGTH>)) {
if (not funcPtrIndex.full()) { // CAUTION: etl::map won't check by itself if it's full
// before attempting to insert a key-value pair, causing segmentation faults. Check first!
funcName.append(FUNC_NAME_LENGTH - funcName.length(), '\0');
funcName.append(FUNC_NAME_LENGTH - funcName.length(), 0);
funcPtrIndex.insert(std::make_pair(funcName, ptr));
} else {
ErrorHandler::reportInternalError(ErrorHandler::InternalErrorType::FunctionMapFull);
......
......@@ -17,7 +17,7 @@ TEST_CASE("Error: Failed Acceptance", "[errors]") {
CHECK(report.readBits(3) == CCSDS_PACKET_VERSION);
CHECK(report.readBits(1) == static_cast<uint16_t>(Message::TC));
CHECK(report.readBits(1) == false);
CHECK(report.readBits(1) == 0);
CHECK(report.readBits(11) == 47);
CHECK(report.readBits(2) == ECSS_SEQUENCE_FLAGS);
CHECK(report.readBits(14) == failedMessage.packetSequenceCount);
......@@ -39,7 +39,7 @@ TEST_CASE("Error: Failed Execution Start", "[errors]") {
CHECK(report.readBits(3) == CCSDS_PACKET_VERSION);
CHECK(report.readBits(1) == static_cast<uint16_t>(Message::TC));
CHECK(report.readBits(1) == false);
CHECK(report.readBits(1) == 0);
CHECK(report.readBits(11) == 56);
CHECK(report.readBits(2) == ECSS_SEQUENCE_FLAGS);
CHECK(report.readBits(14) == failedMessage.packetSequenceCount);
......@@ -61,7 +61,7 @@ TEST_CASE("Error: Failed Execution Progress", "[errors]") {
CHECK(report.readBits(3) == CCSDS_PACKET_VERSION);
CHECK(report.readBits(1) == static_cast<uint16_t>(Message::TC));
CHECK(report.readBits(1) == false);
CHECK(report.readBits(1) == 0);
CHECK(report.readBits(11) == 56);
CHECK(report.readBits(2) == ECSS_SEQUENCE_FLAGS);
CHECK(report.readBits(14) == failedMessage.packetSequenceCount);
......@@ -84,7 +84,7 @@ TEST_CASE("Error: Failed Execution Completion", "[errors]") {
CHECK(report.readBits(3) == CCSDS_PACKET_VERSION);
CHECK(report.readBits(1) == static_cast<uint16_t>(Message::TC));
CHECK(report.readBits(1) == false);
CHECK(report.readBits(1) == 0);
CHECK(report.readBits(11) == 56);
CHECK(report.readBits(2) == ECSS_SEQUENCE_FLAGS);
CHECK(report.readBits(14) == failedMessage.packetSequenceCount);
......@@ -106,7 +106,7 @@ TEST_CASE("Error: Failed Routing", "[errors]") {
CHECK(report.readBits(3) == CCSDS_PACKET_VERSION);
CHECK(report.readBits(1) == static_cast<uint16_t>(Message::TC));
CHECK(report.readBits(1) == false);
CHECK(report.readBits(1) == 0);
CHECK(report.readBits(11) == 71);
CHECK(report.readBits(2) == ECSS_SEQUENCE_FLAGS);
CHECK(report.readBits(14) == failedMessage.packetSequenceCount);
......
......@@ -3,6 +3,7 @@
#include <catch2/catch.hpp>
#include <Message.hpp>
#include <Service.hpp>
#include <Logger.hpp>
#include "Services/ServiceTests.hpp"
// Explicit template specializations for the logError() function
......@@ -34,6 +35,10 @@ void ErrorHandler::logError(ErrorType errorType) {
ServiceTests::addError(ErrorHandler::findErrorSource(errorType), errorType);
}
void Logger::log(Logger::LogLevel level, String<LOGGER_MAX_MESSAGE_SIZE> & message) {
// Logs while testing are completely ignored
}
struct ServiceTestsListener : Catch::TestEventListenerBase {
using TestEventListenerBase::TestEventListenerBase; // inherit constructor
......