Skip to content

Commit

Permalink
config file handling (#34)
Browse files Browse the repository at this point in the history
  • Loading branch information
neri14 authored Sep 1, 2024
1 parent ae7084c commit 43fce03
Show file tree
Hide file tree
Showing 7 changed files with 236 additions and 11 deletions.
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,12 +46,14 @@ Packages required in system to build the application
- CMake >= 3.23
- Make / Ninja
- GCC >= 14.x
- googlemock
- gstreamer >= 1.24
- cairo
- pango
- pugixml

For testing

- googlemock

## Alignment Mode

Expand Down
146 changes: 138 additions & 8 deletions src/arguments.cpp
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
#include "arguments.h"
#include "utils/argument_parser.h"

#include <fstream>
#include <sstream>

namespace vgraph {
namespace consts {
int defult_alignment_clip_time(60);
const int defult_alignment_clip_time(60);
const std::string whitespaces(" \t\v\r\n");
}
namespace key {
std::string help("help");
std::string config("config");
std::string debug("debug");
std::string gpu("gpu");
std::string timecode("timecode");
Expand All @@ -25,13 +30,77 @@ namespace key {
utils::argument_parser prepare_parser();

arguments read_args(const utils::argument_parser& parser, utils::logging::logger& log);

bool parse_config_file(const std::string& path, arguments& args);

void assert_arguments_valid(const arguments& args, utils::logging::logger& log);

template <typename T>
bool read_value(const utils::argument_parser& parser, const std::string& name, utils::logging::logger& log, std::optional<T>& value_out);

bool parse_resolution(const std::string& str, utils::logging::logger& log, std::optional<std::pair<int, int>>& out);

/* specialized parse_value functions */
template <typename T>
bool parse_value(const std::string& str, utils::logging::logger& log, T& value_out)
{
log.error("Unsupported value type");
return false;
}

template <>
bool parse_value<bool>(const std::string& str, utils::logging::logger& log, bool& value_out)
{
if (str == "true") {
value_out = true;
return true;
} else if (str == "false") {
value_out = false;
return true;
}

return false;
}

template <>
bool parse_value<std::optional<std::string>>(const std::string& str, utils::logging::logger& log, std::optional<std::string>& value_out)
{
value_out = str;
return true;
}

template <>
bool parse_value<std::optional<int>>(const std::string& str, utils::logging::logger& log, std::optional<int>& value_out)
{
try {
value_out = std::stoi(str);
} catch (std::invalid_argument) {
log.error("Could not parse value '{}' as integer point number", str);
return false;
} catch (std::out_of_range) {
log.error("Value '{}' is out of range", str);
return false;
}

return true;
}

template <>
bool parse_value<std::optional<double>>(const std::string& str, utils::logging::logger& log, std::optional<double>& value_out)
{
try {
value_out = std::stod(str);
} catch (std::invalid_argument) {
log.error("Could not parse value '{}' as floating point number", str);
return false;
} catch (std::out_of_range) {
log.error("Value '{}' is out of range", str);
return false;
}

return true;
}

/* interface */
arguments arguments::parse(int argc, char* argv[])
{
Expand Down Expand Up @@ -69,9 +138,10 @@ utils::argument_parser prepare_parser()
utils::argument_parser parser("vgraph");

parser.add_argument(key::help, utils::argument().flag().option("-h").option("--help") .description("Print this help message"));
parser.add_argument(key::config, utils::argument() .option("-c").option("--config") .description("Load config file"));
parser.add_argument(key::debug, utils::argument().flag().option("-d").option("--debug") .description("Enable debug logs"));
parser.add_argument(key::gpu, utils::argument().flag().option("-g").option("--gpu") .description("Use Nvidia GPU for processing"));
parser.add_argument(key::timecode, utils::argument().flag().option("-c").option("--timecode") .description("Draw a timecode on each frame"));
parser.add_argument(key::timecode, utils::argument().flag().option("-m").option("--timecode") .description("Draw a timecode on each frame"));
parser.add_argument(key::alignment, utils::argument().flag().option("-l").option("--alignment") .description(std::format("Enable alignment mode ({}s clip unless overriden by -T)", consts::defult_alignment_clip_time)));
parser.add_argument(key::telemetry, utils::argument() .option("-t").option("--telemetry") .description("Telemetry file path"));
parser.add_argument(key::layout, utils::argument() .option("-a").option("--layout") .description("Layout file path"));
Expand All @@ -90,10 +160,14 @@ arguments read_args(const utils::argument_parser& parser, utils::logging::logger
arguments a;
bool valid = true;

a.debug = parser.get<bool>(key::debug);
a.gpu = parser.get<bool>(key::gpu);
a.timecode = parser.get<bool>(key::timecode);
a.alignment_mode = parser.get<bool>(key::alignment);
if (parser.has(key::config)) {
valid = parse_config_file(parser.get<std::string>(key::config), a);
}

a.debug = a.debug || parser.get<bool>(key::debug);
a.gpu = a.gpu || parser.get<bool>(key::gpu);
a.timecode = a.timecode || parser.get<bool>(key::timecode);
a.alignment_mode = a.alignment_mode || parser.get<bool>(key::alignment);

valid = read_value<std::string>(parser, key::telemetry, log, a.telemetry) && valid;
valid = read_value<std::string>(parser, key::layout, log, a.layout) && valid;
Expand All @@ -119,6 +193,64 @@ arguments read_args(const utils::argument_parser& parser, utils::logging::logger
return std::move(a);
}

/* Expected format:
#comments are ignored
key=value
key=value
key=value
*/
bool parse_config_file(const std::string& path, arguments& args)
{
utils::logging::logger log{"arguments::parse_config_file()"};

std::ifstream file(path);
if (!file.is_open()) {
log.error("Failed to open '{}' file", path);
return false;
}

std::string line;
while(getline(file, line)) {
line = line.substr(line.find_first_not_of(consts::whitespaces)); //remove leading whitespaces
if (line.starts_with('#')) {
continue;
}

int pos = line.find('=');
const std::string& key = line.substr(0, pos);
const std::string& value = line.substr(pos+1);

log.debug("key: '{}' value: '{}'", key, value);
if (key == key::debug) {
parse_value(value, log, args.debug);
} else if (key == key::gpu) {
parse_value(value, log, args.gpu);
} else if (key == key::timecode) {
parse_value(value, log, args.timecode);
} else if (key == key::alignment) {
parse_value(value, log, args.alignment_mode);
} else if (key == key::telemetry) {
parse_value(value, log, args.telemetry);
} else if (key == key::layout) {
parse_value(value, log, args.layout);
} else if (key == key::input) {
parse_value(value, log, args.input);
} else if (key == key::output) {
parse_value(value, log, args.output);
} else if (key == key::offset) {
parse_value(value, log, args.offset);
} else if (key == key::resolution) {
parse_resolution(value, log, args.resolution);
} else if (key == key::bitrate) {
parse_value(value, log, args.bitrate);
} else if (key == key::clip_time) {
parse_value(value, log, args.clip_time);
}
}

return true;
}

void assert_arguments_valid(const arguments& args, utils::logging::logger& log)
{
bool valid = true;
Expand Down Expand Up @@ -178,6 +310,4 @@ bool parse_resolution(const std::string& str, utils::logging::logger& log, std::
return true;
}



}
4 changes: 2 additions & 2 deletions src/utils/argument_parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -268,11 +268,11 @@ void argument_parser::print_help_details() const
str = std::format("{} {}", str, helper::to_upper(key));
}

if (str.length() > 20) {
if (str.length() > 30) {
std::cout << std::format(" {}", str) << std::endl;
std::cout << std::format(" {}", arg.description_) << std::endl;
} else {
std::cout << std::format(" {:<16} {}", str, arg.description_) << std::endl;
std::cout << std::format(" {:<30} {}", str, arg.description_) << std::endl;
}
}
}
Expand Down
1 change: 1 addition & 0 deletions test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ cmake_minimum_required(VERSION 3.20.0)
target_sources(vgraph_test
PRIVATE
main.cpp
config_file_parsing_test.cpp
)

add_subdirectory(telemetry)
Expand Down
66 changes: 66 additions & 0 deletions test/config_file_parsing_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
#include <gtest/gtest.h>
#include "arguments.h"
#include "testutils/testdata.h"

#include <filesystem>

namespace vgraph {
namespace consts {
const std::filesystem::path full_conf = std::filesystem::path(TESTDATA_DIR) / "full.conf";
const std::filesystem::path partial_conf = std::filesystem::path(TESTDATA_DIR) / "partial.conf";
}

//forward declaration because function under test is internal to cpp
bool parse_config_file(const std::string& path, arguments& args);

TEST(config_file_parsing_test, full_config_file)
{
arguments args;

EXPECT_TRUE(parse_config_file(consts::full_conf, args));

EXPECT_TRUE(args.debug);
EXPECT_TRUE(args.gpu);
EXPECT_TRUE(args.timecode);
EXPECT_TRUE(args.alignment_mode);
EXPECT_EQ("/some/absolute/path", args.telemetry);
EXPECT_EQ("./a/different/relative/path", args.layout);
EXPECT_EQ("mp4_file.mp4", args.input);
EXPECT_EQ("/absolute/mp4/file.mp4", args.output);
EXPECT_EQ(100000, args.bitrate);

ASSERT_TRUE(args.resolution);
EXPECT_EQ(1234, args.resolution->first);
EXPECT_EQ(567, args.resolution->second);

ASSERT_TRUE(args.offset);
EXPECT_NEAR(123.456, *args.offset, 0.001);

ASSERT_TRUE(args.clip_time);
EXPECT_NEAR(60, *args.clip_time, 0.001);
}

TEST(config_file_parsing_test, partial_config_file)
{
arguments args;

EXPECT_TRUE(parse_config_file(consts::partial_conf, args));

EXPECT_FALSE(args.debug);
EXPECT_TRUE(args.gpu);
EXPECT_FALSE(args.timecode);
EXPECT_FALSE(args.alignment_mode);
EXPECT_EQ("./test/testdata/correct.fit", args.telemetry);
EXPECT_EQ("./test/testdata/layout.xml", args.layout);
EXPECT_FALSE(args.input);
EXPECT_FALSE(args.output);
EXPECT_EQ(80000, args.bitrate);
EXPECT_FALSE(args.offset);
EXPECT_FALSE(args.clip_time);

ASSERT_TRUE(args.resolution);
EXPECT_EQ(3840, args.resolution->first);
EXPECT_EQ(2160, args.resolution->second);
}

} // namespace vgraph
14 changes: 14 additions & 0 deletions test/testdata/full.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#comment is ignored
debug=true
gpu=true
timecode=true
alignment=true
telemetry=/some/absolute/path
layout=./a/different/relative/path
offset=123.456
input=mp4_file.mp4
output=/absolute/mp4/file.mp4
resolution=1234x567
bitrate=100000
clip_time=60
#comment is ignored
12 changes: 12 additions & 0 deletions test/testdata/partial.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# debug=true
gpu=true
# timecode=true
# alignment=true
telemetry=./test/testdata/correct.fit
layout=./test/testdata/layout.xml
# offset=123.456
# input=mp4_file.mp4
# output=/absolute/mp4/file.mp4
resolution=3840x2160
bitrate=80000
# clip_time=60

0 comments on commit 43fce03

Please sign in to comment.