CCSDSPack
C++ Library for CCSDS Space Packet manipulation. i.e. generation, extraction, analisys and more
Loading...
Searching...
No Matches
CCSDSConfig.cpp
Go to the documentation of this file.
1#include "CCSDSConfig.h"
2#include <cstddef>
3#include <iomanip>
4#include <algorithm>
5#include <charconv>
6
7#include <fstream>
8
9//###########################################################################
10#define VERBOSE 1
11
12CCSDS::ResultBool Config::load(const std::string &filename) {
13 std::ifstream file(filename);
14 RET_IF_ERR_MSG(!file.is_open(),CCSDS::ErrorCode::CONFIG_FILE_ERROR, "Failed to open configuration file");
15
16 std::string line;
17 while (std::getline(file, line)) {
18 if (line.empty() || line.front() == '#') continue;
19
20 auto [key, type, valueStr] = parseLine(line);
21 RET_IF_ERR_MSG(key.empty() || type.empty(),CCSDS::ErrorCode::CONFIG_FILE_ERROR, "Failed to parse configuration file");
22 ConfigValue value;
23 if (type == "string") {
24 value = valueStr;
25 } else if (type == "int") {
26 std::uint8_t base = 10;
27 if (valueStr.size() > 2 && valueStr.substr(0, 2) == "0x") {
28 valueStr = valueStr.substr(2);
29 base = 16;
30 }
31 value = std::stoi(valueStr, nullptr, base);
32 } else if (type == "float") {
33 value = std::stof(valueStr);
34 } else if (type == "bool") {
35 value = (valueStr == "true" || valueStr == "1");
36 } else if (type == "bytes") {
37 ASSIGN_CP( value, parseBytes(valueStr) );
38 } else {
39 return CCSDS::Error{CCSDS::ErrorCode::CONFIG_FILE_ERROR, " unknown type: " + type};
40 }
41
42 values[key] = value;
43 }
44
45 return true;
46}
47
48bool Config::isKey(const std::string &key) const {
49 if (values.find(key) != values.end()) {
50 return true;
51 }
52 return false;
53}
54
55std::tuple<std::string, std::string, std::string> Config::parseLine(const std::string& line) {
56 auto colonPos = line.find(':');
57 auto equalPos = line.find('=');
58
59 if (colonPos == std::string::npos || equalPos == std::string::npos || equalPos < colonPos)
60 return {"", "", ""};
61
62 std::string key = line.substr(0, colonPos);
63 std::string type = line.substr(colonPos + 1, equalPos - colonPos - 1);
64 std::string value = line.substr(equalPos + 1);
65
66 if (!value.empty() && value.front() == '"' && value.back() == '"')
67 value = value.substr(1, value.size() - 2);
68
69 return {key, type, value};
70}
71
72CCSDS::ResultBuffer Config::parseBytes(const std::string &valueStr) {
73 std::vector<uint8_t> result{};
74 RET_IF_ERR_MSG(valueStr.empty() || valueStr.front() != '[' || valueStr.back() != ']',
76 "Config: Invalid buffer formatting []");
77
78 if (valueStr == "[]" || valueStr == "[ ]") {
79 return result;
80 }
81
82 // Strip surrounding [ ... ]
83 std::string inner = valueStr.substr(1, valueStr.size() - 2);
84 std::stringstream ss(inner);
85 std::string token;
86
87 while (std::getline(ss, token, ',')) {
88 // Remove all spaces inside each token
89 token.erase(std::remove_if(token.begin(), token.end(),
90 [](unsigned char c){ return std::isspace(c); }),
91 token.end());
92
93 // Empty token after trimming is invalid (e.g., "[12, ,34]")
94 if (token.empty()) {
95 return CCSDS::Error{CCSDS::ErrorCode::CONFIG_FILE_ERROR, "Invalid byte value: <empty>"};
96 }
97
98 std::uint8_t base = 10;
99 std::string_view sv{token};
100
101 // Allow 0x / 0X prefix for hex
102 if (sv.size() > 2 && sv[0] == '0' && (sv[1] == 'x' || sv[1] == 'X')) {
103 sv.remove_prefix(2);
104 base = 16;
105 if (sv.empty()) {
106 return CCSDS::Error{CCSDS::ErrorCode::CONFIG_FILE_ERROR, "Invalid byte value: 0x"};
107 }
108 }
109
110 // Parse without exceptions
111 std::uint8_t tmp = 0;
112 const char* first = sv.data();
113 const char* last = sv.data() + sv.size();
114 auto res = std::from_chars(first, last, tmp, base);
115
116 // Valid if parsed ok, consumed all characters, and fits in a byte
117 if (res.ec != std::errc{} || res.ptr != last || tmp > 0xFFu) {
119 std::string("Invalid byte value: ") + std::string(token)};
120 }
121 result.push_back(static_cast<uint8_t>(tmp));
122 }
123
124 return result;
125}
#define RET_IF_ERR_MSG(condition, errorCode, message)
Macro to return an error with an error message if a condition is met.
#define ASSIGN_CP(var, result)
Macro to assign a result value to a variable or return an error by copy.
Represents an error with both an error code and a message.
Definition CCSDSResult.h:44
Encapsulates a result that can hold either a value or an Error.
Definition CCSDSResult.h:82
std::variant< std::string, int, float, bool, std::vector< uint8_t > > ConfigValue
Definition CCSDSConfig.h:13
bool isKey(const std::string &key) const
static CCSDS::ResultBuffer parseBytes(const std::string &valueStr)
Parse string "[1,2,3]" into vector<uint8_t>
std::unordered_map< std::string, ConfigValue > values
Definition CCSDSConfig.h:33
CCSDS::ResultBool load(const std::string &filename)
Load config file.
CCSDS::Result< T > get(const std::string &key) const
Get value by key and type.
Definition CCSDSConfig.h:20
static std::tuple< std::string, std::string, std::string > parseLine(const std::string &line)
Parse a single line from config.
@ CONFIG_FILE_ERROR
Configuration file error.
Definition CCSDSResult.h:34