Skip to content

Commit 9c95a86

Browse files
committed
feat: add configurable params dir for SITL, improve logging and make generators write-if-changed
1 parent 43ddb9d commit 9c95a86

8 files changed

Lines changed: 208 additions & 81 deletions

File tree

include/libparams/storage.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,18 @@ int8_t paramsSave();
108108
*/
109109
int8_t paramsResetToDefault();
110110

111+
/**
112+
* @brief Set directory for params files (SITL).
113+
* @return LIBPARAMS_OK on success, otherwise < 0.
114+
*/
115+
int8_t paramsSetDir(const char* dir);
116+
117+
/**
118+
* @brief Get directory for params files (SITL).
119+
* @return C-string on success, otherwise empty string.
120+
*/
121+
const char* paramsGetDir();
122+
111123
/**
112124
* @note Get the parameter name
113125
* @return C-string on success, otherwise NULL.

platform_specific/ubuntu/SimpleLogger.hpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,14 @@ class SimpleLogger {
1919
void info(Args... args) {
2020
std::stringstream ss;
2121
(ss << ... << args);
22-
std::cout << module << ": " << ss.str() << std::endl;
22+
std::cout << "[" << module << "] [INFO]: " << ss.str() << std::endl;
2323
}
2424

2525
template <typename... Args>
2626
void error(Args... args) {
2727
std::stringstream ss;
2828
(ss << ... << args);
29-
std::cerr << module << ": " << ss.str() << std::endl;
29+
std::cerr << "[" << module << "]: [ERROR]: " << ss.str() << std::endl;
3030
}
3131

3232
private:

platform_specific/ubuntu/YamlParameters.cpp

Lines changed: 28 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -54,42 +54,52 @@ int8_t YamlParameters::set_temp_file_name(std::string file_name) {
5454
return LIBPARAMS_OK;
5555
}
5656

57-
int8_t YamlParameters::read_from_dir(const std::string& path) {
58-
if (path.empty()) {
59-
return LIBPARAMS_WRONG_ARGS;
57+
58+
int8_t YamlParameters::read_from_dir(const std::string& path_str) {
59+
namespace fs = std::filesystem;
60+
if (!path_str.empty()) {
61+
logger.info("Initializing with ", "LIBPARAMS_PARAMS_DIR=", fs::absolute(fs::path(path_str)).lexically_normal());
6062
}
63+
64+
if (path_str.empty()) return LIBPARAMS_WRONG_ARGS;
65+
66+
// Make the base directory absolute (no filesystem access needed)
67+
fs::path base = fs::absolute(fs::path(path_str)).lexically_normal();
68+
6169
int8_t res;
62-
char file_name[256];
6370
uint16_t int_param_idx = 0;
6471
uint16_t str_param_idx = 0;
65-
// read params values for each page
72+
6673
for (uint16_t idx = 0; idx < flash.num_pages; idx++) {
6774
std::ifstream params_storage_file;
6875

69-
// check if temp file for the page already exists, else read from init file
70-
snprintf(file_name, sizeof(file_name), "%s/%s_%d.yaml",
71-
path.c_str(), temp_file_name.c_str(), idx);
72-
params_storage_file.open(file_name, std::ios_base::in);
76+
fs::path temp = base / (temp_file_name + "_" + std::to_string(idx) + ".yaml");
77+
params_storage_file.open(temp, std::ios_base::in);
78+
7379
if (!params_storage_file) {
74-
logger.info(file_name, " could not be opened for reading!");
75-
snprintf(file_name, sizeof(file_name), "%s/%s_%d.yaml",
76-
path.c_str(), init_file_name.c_str(), idx);
77-
params_storage_file.open(file_name, std::ios_base::in);
80+
logger.info(temp.string(), " could not be opened for reading!");
81+
82+
fs::path init = base / (init_file_name + "_" + std::to_string(idx) + ".yaml");
83+
params_storage_file.open(init, std::ios_base::in);
84+
7885
if (!params_storage_file) {
79-
logger.error(file_name, " could not be opened for reading!");
86+
logger.error(init.string(), " could not be opened for reading!");
8087
return LIBPARAMS_WRONG_ARGS;
8188
}
89+
90+
logger.info("Reading params from the init file ", init.string(), ":");
91+
} else {
92+
logger.info("Reading params from the temp file ", temp.string(), ":");
8293
}
83-
logger.info("data read from ", file_name);
8494

8595
if ((int_param_idx > params.num_int_params) || (str_param_idx > params.num_str_params)) {
8696
break;
8797
}
98+
8899
res = __read_page(params_storage_file, &int_param_idx, &str_param_idx);
89100
params_storage_file.close();
90-
if (res != LIBPARAMS_OK) {
91-
return res;
92-
}
101+
102+
if (res != LIBPARAMS_OK) return res;
93103
}
94104

95105
if (int_param_idx != params.num_int_params || str_param_idx != params.num_str_params) {

platform_specific/ubuntu/YamlParameters.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
#include "SimpleLogger.hpp"
1414
#include "FlashMemoryLayout.hpp"
1515

16-
static SimpleLogger logger("YamlParameters");
16+
static SimpleLogger logger("libparams-ubuntu");
1717
class YamlParameters {
1818
ParametersLayout_t params;
1919
std::string init_file_name = "init_params";

platform_specific/ubuntu/flash_driver.cpp

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -109,9 +109,17 @@ uint16_t flashGetPageSize() {
109109
}
110110

111111
int32_t __save_to_files() {
112-
return yaml_params.write_to_dir(LIBPARAMS_PARAMS_DIR);
112+
const char* dir = paramsGetDir();
113+
if (dir == nullptr || dir[0] == '\0') {
114+
return LIBPARAMS_WRONG_ARGS;
115+
}
116+
return yaml_params.write_to_dir(dir);
113117
}
114118

115119
int8_t __read_from_files() {
116-
return yaml_params.read_from_dir(LIBPARAMS_PARAMS_DIR);
120+
const char* dir = paramsGetDir();
121+
if (dir == nullptr || dir[0] == '\0') {
122+
return LIBPARAMS_WRONG_ARGS;
123+
}
124+
return yaml_params.read_from_dir(dir);
117125
}

scripts/generate_docs.py

Lines changed: 62 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818

1919
from params import IntegerParam, StringParam
2020

21+
LOG_PREFIX = "[libparams-doc]"
22+
2123
logger = logging.getLogger(__name__)
2224

2325
LANGUAGE_C = 0
@@ -82,6 +84,18 @@ def write_parameters(file, all_params : list) -> None:
8284
else:
8385
file.write("The node doesn't have registers:\n\n")
8486

87+
def _write_if_changed(path, content: str) -> str:
88+
if os.path.exists(path):
89+
with open(path, 'r', encoding="utf-8") as existing_file:
90+
if existing_file.read() == content:
91+
return "unchanged"
92+
status = "updated"
93+
else:
94+
status = "added"
95+
with open(path, 'w', encoding="utf-8") as out_file:
96+
out_file.write(content)
97+
return status
98+
8599
def generate_markdown_doc(cyphal_pubs : dict,
86100
cyphal_subs : dict,
87101
all_params : list,
@@ -91,21 +105,53 @@ def generate_markdown_doc(cyphal_pubs : dict,
91105
assert isinstance(all_params, list)
92106
assert isinstance(output_markdown_filename, str)
93107

94-
with open(output_markdown_filename, 'w', encoding="utf-8") as file:
95-
if len(cyphal_pubs) + len(cyphal_subs) >= 1:
96-
file.write("The node has the following interface:\n\n")
108+
content = ""
109+
if len(cyphal_pubs) + len(cyphal_subs) >= 1:
110+
content += "The node has the following interface:\n\n"
111+
112+
if len(cyphal_pubs) >= 1:
113+
content += "Cyphal Publishers:\n"
114+
content += _render_cyphal_topics(cyphal_pubs)
115+
116+
if len(cyphal_subs) >= 1:
117+
content += "Cyphal Subscribers:\n"
118+
content += _render_cyphal_topics(cyphal_subs)
97119

98-
if len(cyphal_pubs) >= 1:
99-
file.write("Cyphal Publishers:\n")
100-
write_cyphal_topics(file, cyphal_pubs)
120+
content += _render_parameters(all_params)
101121

102-
if len(cyphal_subs) >= 1:
103-
file.write("Cyphal Subscribers:\n")
104-
write_cyphal_topics(file, cyphal_subs)
122+
content += "> This docs was automatically generated. Do not edit it manually.\n\n"
123+
return _write_if_changed(output_markdown_filename, content)
105124

106-
write_parameters(file, all_params)
125+
def _render_cyphal_topics(cyphal_topics: dict) -> str:
126+
assert isinstance(cyphal_topics, dict)
107127

108-
file.write("> This docs was automatically generated. Do not edit it manually.\n\n")
128+
content = "| Data type and topic name | Description |\n"
129+
content += "| ------------------------- | ----------- |\n"
130+
for port_name, port_info in cyphal_topics.items():
131+
data_type = port_data_type_to_md(port_info['data_type'])
132+
topic_name = port_name[11:]
133+
note = port_info.get('note')
134+
note = note.replace('\n', '</br>') if note is not None else ""
135+
content += f"| {data_type} </br> {topic_name} | {note}|\n"
136+
content += "\n"
137+
return content
138+
139+
def _render_parameters(all_params : list) -> str:
140+
assert isinstance(all_params, list)
141+
142+
content = ""
143+
if len(all_params) >= 1:
144+
content += "The node has the following registers:\n\n"
145+
content += "| Register name | Description |\n"
146+
content += "| ----------------------- | ----------- |\n"
147+
148+
for param in all_params:
149+
param.name = param.name.replace('"', '')
150+
content += f"| {param.name.ljust(23)} | {param.note} |\n"
151+
content += "\n"
152+
else:
153+
content += "The node doesn't have registers:\n\n"
154+
return content
109155

110156
def parse_yaml_files(input_yaml_files : list) -> Tuple[dict, dict, list]:
111157
assert isinstance(input_yaml_files, list) and len(input_yaml_files) != 0
@@ -147,16 +193,17 @@ def parse_yaml_files_and_generate_doc(input_yaml_files : list, output_markdown_p
147193
os.makedirs(directory)
148194

149195
cyphal_pubs, cyphal_subs, all_params = parse_yaml_files(input_yaml_files)
150-
generate_markdown_doc(cyphal_pubs, cyphal_subs, all_params, output_markdown_path)
196+
return generate_markdown_doc(cyphal_pubs, cyphal_subs, all_params, output_markdown_path)
151197

152198
if __name__=="__main__":
153-
logging.basicConfig(level=logging.INFO)
199+
logging.basicConfig(level=logging.INFO, format=f"{LOG_PREFIX} [%(levelname)s] %(message)s")
154200
logger.setLevel(logging.INFO)
155201

156202
parser = ArgumentParser(description=__doc__)
157203
parser.add_argument('files', nargs='+', help='YAML files to process')
158204
parser.add_argument("--output", type=str, required=False, default='README.md')
159205
args = parser.parse_args()
160206

161-
print("Params docs generator:")
162-
parse_yaml_files_and_generate_doc(args.files, args.output)
207+
logger.info("Generating docs: output=%s, files=%d", args.output, len(args.files))
208+
status = parse_yaml_files_and_generate_doc(args.files, args.output)
209+
logger.info("Docs %s", status)

scripts/generate_params.py

Lines changed: 66 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
"""Parameters generator."""
1010

11+
import logging
1112
import os
1213
import sys
1314
from color_logging import log_err
@@ -56,45 +57,65 @@ def add_string(self, param : StringParam):
5657
def generate(self):
5758
if not os.path.exists(self.dir):
5859
os.makedirs(self.dir)
59-
with open(f"{self.dir}/{self.name}.cpp", 'w', encoding="utf-8") as cpp_file:
60-
cpp_content = (
61-
f"{LICENSE_HEADER}\n"
62-
"#include \"params.hpp\"\n"
63-
"\n"
64-
"IntegerDesc_t integer_desc_pool[] = {\n"
65-
f"{self.integers_array}"
66-
"};\n"
67-
"IntegerParamValue_t "
68-
"integer_values_pool[sizeof(integer_desc_pool) / sizeof(IntegerDesc_t)];\n"
69-
"\n"
70-
"StringDesc_t string_desc_pool[NUM_OF_STR_PARAMS] = {\n"
71-
f"{self.strings_array}"
72-
"};\n"
73-
"StringParamValue_t "
74-
"string_values_pool[sizeof(string_desc_pool) / sizeof(StringDesc_t)];\n"
75-
)
76-
cpp_file.write(cpp_content)
77-
78-
with open(f"{self.dir}/{self.name}.hpp", 'w', encoding="utf-8") as hpp_file:
79-
hpp_content = (
80-
f"{LICENSE_HEADER}\n"
81-
"#pragma once\n"
82-
"#include \"storage.h\"\n\n"
83-
"enum IntParamsIndexes {\n"
84-
f"{self.integers_enums}\n"
85-
" INTEGER_PARAMS_AMOUNT\n"
60+
cpp_content = (
61+
f"{LICENSE_HEADER}\n"
62+
"#include \"params.hpp\"\n"
63+
"\n"
64+
"IntegerDesc_t integer_desc_pool[] = {\n"
65+
f"{self.integers_array}"
66+
"};\n"
67+
"IntegerParamValue_t "
68+
"integer_values_pool[sizeof(integer_desc_pool) / sizeof(IntegerDesc_t)];\n"
69+
"\n"
70+
"StringDesc_t string_desc_pool[NUM_OF_STR_PARAMS] = {\n"
71+
f"{self.strings_array}"
72+
"};\n"
73+
"StringParamValue_t "
74+
"string_values_pool[sizeof(string_desc_pool) / sizeof(StringDesc_t)];\n"
75+
)
76+
cpp_status = _write_if_changed(f"{self.dir}/{self.name}.cpp", cpp_content)
77+
78+
hpp_content = (
79+
f"{LICENSE_HEADER}\n"
80+
"#pragma once\n"
81+
"#include \"storage.h\"\n\n"
82+
"enum IntParamsIndexes {\n"
83+
f"{self.integers_enums}\n"
84+
" INTEGER_PARAMS_AMOUNT\n"
85+
"};\n"
86+
)
87+
if self.strings_amount > 0:
88+
hpp_content += (
89+
"enum StrParamsIndexes {\n"
90+
f"{self.strings_enums}\n"
8691
"};\n"
87-
8892
)
89-
if self.strings_amount > 0:
90-
hpp_content += (
91-
"enum StrParamsIndexes {\n"
92-
f"{self.strings_enums}\n"
93-
"};\n"
94-
)
9593

96-
hpp_content += f"#define NUM_OF_STR_PARAMS {self.strings_amount}\n"
97-
hpp_file.write(hpp_content)
94+
hpp_content += f"#define NUM_OF_STR_PARAMS {self.strings_amount}\n"
95+
hpp_status = _write_if_changed(f"{self.dir}/{self.name}.hpp", hpp_content)
96+
return cpp_status, hpp_status
97+
98+
def _write_if_changed(path, content):
99+
if os.path.exists(path):
100+
with open(path, 'r', encoding="utf-8") as existing_file:
101+
if existing_file.read() == content:
102+
return "unchanged"
103+
status = "updated"
104+
else:
105+
status = "added"
106+
with open(path, 'w', encoding="utf-8") as out_file:
107+
out_file.write(content)
108+
return status
109+
110+
def _setup_logger():
111+
logger = logging.getLogger("libparams-gen")
112+
if not logger.handlers:
113+
handler = logging.StreamHandler()
114+
formatter = logging.Formatter("[libparams-gen] [%(levelname)s] %(message)s")
115+
handler.setFormatter(formatter)
116+
logger.addHandler(handler)
117+
logger.setLevel(logging.INFO)
118+
return logger
98119

99120
if __name__=="__main__":
100121
from argparse import ArgumentParser
@@ -105,11 +126,13 @@ def generate(self):
105126
parser.add_argument('-f','--files', type=str, required=True, help='', nargs='+')
106127
args = parser.parse_args()
107128

108-
print("Parameters generator:")
109-
print("1. out_dir:", args.out_dir)
110-
print("2. language:", args.language)
111-
print("3. out-file-name:", args.out_file_name)
112-
print("4. files:", args.files)
129+
logger = _setup_logger()
130+
logger.info(
131+
"Generating params: out_dir=%s, out_file=%s, files=%d",
132+
args.out_dir,
133+
args.out_file_name,
134+
len(args.files),
135+
)
113136

114137
# Check args for basic errors
115138
for yaml_file_path in args.files:
@@ -140,4 +163,5 @@ def generate(self):
140163
log_err(f"Unknown type: {param_name}.type={data['type']}!")
141164
sys.exit(1)
142165

143-
gen.generate()
166+
cpp_status, hpp_status = gen.generate()
167+
logger.info("params.cpp %s / params.hpp %s", cpp_status, hpp_status)

0 commit comments

Comments
 (0)