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