From a5aa1af4336767263d2345afde6364a200e1d387 Mon Sep 17 00:00:00 2001 From: Matteo Cypriani Date: Tue, 16 Feb 2010 16:19:39 +0100 Subject: [PATCH] [Positioning] Add logging support Add interface InputLogMedium and class InputLogCSV. Input: Add logging support. UserInterface: Add logging options. Configuration: - Rename functions (suppress "get_"). - Add functions to allow multiple values in one option (needed by logging options): - value_exists_in_string_vector() - string_vector_value() InputCSV: Add a destructor. posexcept.hh: Add no_log_csv_file exception. TestUtil: - Improve set_up(). - Add functions used in InputLogCSV_test: - functions to compare Request & Co, - functions to compare the sizes of two files. --- owlps-positioning/Makefile | 9 +- owlps-positioning/configuration.cc | 32 +++- owlps-positioning/configuration.hh | 10 +- owlps-positioning/input.cc | 58 ++++++- owlps-positioning/input.hh | 7 + owlps-positioning/inputcsv.cc | 6 + owlps-positioning/inputcsv.hh | 1 + owlps-positioning/inputlogcsv.cc | 81 ++++++++++ owlps-positioning/inputlogcsv.hh | 28 ++++ owlps-positioning/inputlogmedium.hh | 22 +++ owlps-positioning/posexcept.cc | 6 + owlps-positioning/posexcept.hh | 7 + owlps-positioning/tests/inputlogcsv_test.hh | 89 +++++++++++ owlps-positioning/tests/testutil.cc | 143 ++++++++++++++++-- owlps-positioning/tests/testutil.hh | 16 ++ owlps-positioning/tests/userinterface_test.hh | 4 +- owlps-positioning/userinterface.cc | 15 +- 17 files changed, 502 insertions(+), 32 deletions(-) create mode 100644 owlps-positioning/inputlogcsv.cc create mode 100644 owlps-positioning/inputlogcsv.hh create mode 100644 owlps-positioning/inputlogmedium.hh create mode 100644 owlps-positioning/tests/inputlogcsv_test.hh diff --git a/owlps-positioning/Makefile b/owlps-positioning/Makefile index 3251868..639669a 100644 --- a/owlps-positioning/Makefile +++ b/owlps-positioning/Makefile @@ -31,8 +31,10 @@ TARGET = owlps-positioning OBJ = posutil.o stock.o timestamp.o point3d.o referencepoint.o \ waypoint.o building.o area.o wifidevice.o accesspoint.o \ mobile.o measurement.o calibrationmeasurement.o request.o \ - inputcsv.o configuration.o userinterface.o input.o + configuration.o userinterface.o input.o \ + inputcsv.o inputlogcsv.o OBJ_NOTEST = posexcept.o +INTERFACES = inputmedium.hh inputlogmedium.hh TESTS_XX = $(TESTS_DIR)/tests.cc TESTS_OBJ = $(TESTS_DIR)/tests.o @@ -67,6 +69,7 @@ measurement.o: accesspoint.o calibrationmeasurement.o: measurement.o referencepoint.o request.o: timestamp.o measurement.o inputcsv.o: inputmedium.hh request.o stock.o +inputlogcsv.o: inputlogmedium.hh request.o input.o: posexcept.o # Specific targets @@ -114,7 +117,7 @@ style: $(OBJ:.o=.cc) \ $(OBJ_NOTEST:.o=.hh) \ $(OBJ_NOTEST:.o=.cc) \ - inputmedium.hh \ + $(INTERFACES) \ $(TESTS_DIR)/*.hh \ $(TESTS_DIR)/*.cc @@ -122,4 +125,4 @@ check: @$(CPPCHECK) \ $(OBJ:.o=.hh) $(OBJ:.o=.cc) \ $(OBJ_NOTEST:.o=.hh) $(OBJ_NOTEST:.o=.cc) \ - inputmedium.hh + $(INTERFACES) diff --git a/owlps-positioning/configuration.cc b/owlps-positioning/configuration.cc index da8ce29..ce6700d 100644 --- a/owlps-positioning/configuration.cc +++ b/owlps-positioning/configuration.cc @@ -1,5 +1,6 @@ #include "configuration.hh" +using namespace std ; namespace po = boost::program_options ; @@ -19,7 +20,7 @@ po::variables_map& Configuration::getw_configuration() } -bool Configuration::is_configured(const std::string &key) +bool Configuration::is_configured(const string &key) { if (configuration.count(key)) return true ; @@ -28,13 +29,36 @@ bool Configuration::is_configured(const std::string &key) const std::string& -Configuration::get_string_value(const std::string &key) +Configuration::string_value(const string &key) { - return configuration[key].as() ; + return configuration[key].as() ; } -int Configuration::get_int_value(const std::string &key) +int Configuration::int_value(const string &key) { return configuration[key].as() ; } + + +bool Configuration:: +value_exists_in_string_vector(const string &key, const string &value) +{ + if (! is_configured(key)) + return false ; + + vector key_values = configuration[key].as< vector >() ; + + for (vector::const_iterator i = key_values.begin() ; + i != key_values.end() ; ++i) + if (*i == value) + return true ; + + return false ; +} + + +const vector& Configuration::string_vector_value(const string &key) +{ + return configuration[key].as< vector >() ; +} diff --git a/owlps-positioning/configuration.hh b/owlps-positioning/configuration.hh index 67d0a57..2b7911b 100644 --- a/owlps-positioning/configuration.hh +++ b/owlps-positioning/configuration.hh @@ -2,6 +2,7 @@ #define _OWLPS_POSITIONING_CONFIGURATION_HH_ #include +#include #include /// Stocks the configuration of the program @@ -18,9 +19,14 @@ public: /// Checks if the key exists in the configuration static bool is_configured(const std::string &key) ; /// Get the string value corresponding to \em key - static const std::string& get_string_value(const std::string &key) ; + static const std::string& string_value(const std::string &key) ; /// Get the int value corresponding to \em key - static int get_int_value(const std::string &key) ; + static int int_value(const std::string &key) ; + static bool value_exists_in_string_vector( + const std::string &key, + const std::string &value) ; + static const std::vector& + string_vector_value(const std::string &key) ; } ; #endif // _OWLPS_POSITIONING_CONFIGURATION_HH_ diff --git a/owlps-positioning/input.cc b/owlps-positioning/input.cc index a74cb9c..d5b7bf7 100644 --- a/owlps-positioning/input.cc +++ b/owlps-positioning/input.cc @@ -1,5 +1,6 @@ #include "input.hh" #include "inputcsv.hh" +#include "inputlogcsv.hh" #include "request.hh" #include "configuration.hh" #include "posexcept.hh" @@ -14,6 +15,7 @@ Input::Input() { medium = NULL ; initialise_input_medium() ; + initialise_log_media() ; } @@ -33,14 +35,14 @@ void Input::initialise_input_medium() throw no_input_medium() ; const string &medium_name = - Configuration::get_string_value("input.medium") ; + Configuration::string_value("input.medium") ; if (medium_name == "CSV") { if (! Configuration::is_configured("input.csv-file")) throw no_input_csv_file() ; medium = new - InputCSV(Configuration::get_string_value("input.csv-file")) ; + InputCSV(Configuration::string_value("input.csv-file")) ; } else @@ -48,11 +50,49 @@ void Input::initialise_input_medium() } +void Input::initialise_log_media() +{ + if (! Configuration::is_configured("log.medium")) + return ; + + if (Configuration::value_exists_in_string_vector("log.medium", "none")) + return ; + + const vector &media_names = + Configuration::string_vector_value("log.medium") ; + + for (vector::const_iterator i = media_names.begin() ; + i != media_names.end() ; ++i) + { + if (*i == "CSV") + initialise_log_csv() ; + + else + throw input_medium_type_unknown(*i) ; + } +} + + +void Input::initialise_log_csv() +{ + if (! Configuration::is_configured("log.csv-file")) + throw no_log_csv_file() ; + + log_media.push_back( + new InputLogCSV( + Configuration::string_value("log.csv-file"))) ; +} + + const Request& Input::get_next_request() const { - if (medium == NULL) - throw null_input_medium() ; - return medium->get_next_request() ; + if (! eof()) + { + medium->get_next_request() ; + log_current_request() ; + } + + return medium->get_current_request() ; } @@ -62,3 +102,11 @@ bool Input::eof() const throw null_input_medium() ; return medium->eof() ; } + + +void Input::log_current_request() const +{ + for (vector::const_iterator i = log_media.begin() ; + i != log_media.end() ; ++i) + (*i)->log_request(medium->get_current_request()) ; +} diff --git a/owlps-positioning/input.hh b/owlps-positioning/input.hh index a43b77a..a8c0b1c 100644 --- a/owlps-positioning/input.hh +++ b/owlps-positioning/input.hh @@ -2,17 +2,24 @@ #define _OWLPS_POSITIONING_INPUT_HH_ class InputMedium ; +class InputLogMedium ; class Request ; #include +#include /// Handles the inputs class Input { protected: InputMedium *medium ; ///< Input medium used + /// List of input log media used + std::vector log_media ; void initialise_input_medium(void) ; + void initialise_log_media(void) ; + void initialise_log_csv(void) ; + void log_current_request(void) const ; public: Input(void) ; diff --git a/owlps-positioning/inputcsv.cc b/owlps-positioning/inputcsv.cc index c9bedb2..6bd2ebf 100644 --- a/owlps-positioning/inputcsv.cc +++ b/owlps-positioning/inputcsv.cc @@ -38,6 +38,12 @@ InputCSV::InputCSV(const string &filename) } +InputCSV::~InputCSV() +{ + input_file.close() ; +} + + /* *** Operations *** */ diff --git a/owlps-positioning/inputcsv.hh b/owlps-positioning/inputcsv.hh index ca54ded..3c1b1e8 100644 --- a/owlps-positioning/inputcsv.hh +++ b/owlps-positioning/inputcsv.hh @@ -28,6 +28,7 @@ protected: public: /// Constructs an InputCSV from a CSV input file name InputCSV(const std::string &filename) ; + ~InputCSV(void) ; /** @name Read accessors */ //@{ diff --git a/owlps-positioning/inputlogcsv.cc b/owlps-positioning/inputlogcsv.cc new file mode 100644 index 0000000..6f32218 --- /dev/null +++ b/owlps-positioning/inputlogcsv.cc @@ -0,0 +1,81 @@ +#include "inputlogcsv.hh" +#include "posexcept.hh" +#include "mobile.hh" + +#include + +using namespace std ; +using std::tr1::unordered_map ; + + + +/* *** Constructors *** */ + + +/** + * Prepares the InputLogCSV to write to a CSV file. + * @param filename The name of the file to open. + * @throw error_opening_input_file if the file cannot be opened. + */ +InputLogCSV::InputLogCSV(const string &filename) +{ + log_file_name = filename ; + + log_file.open(log_file_name.c_str()) ; + if (! log_file) + throw error_opening_input_file(log_file_name) ; +} + + +InputLogCSV::~InputLogCSV() +{ + log_file.close() ; +} + + + +/* *** Operations *** */ + + +/** + * @return true if the request has been logged successfuly, or false + * if not. + */ +bool InputLogCSV::log_request(const Request &request) +{ + if (! log_file) + return false ; + + log_file << request_to_csv(request).c_str() ; + return true ; +} + + +const string InputLogCSV::request_to_csv(const Request &request) const +{ + ostringstream csv_line ; + if (request.get_mobile() != NULL) + csv_line << request.get_mobile()->get_mac_addr() ; + csv_line << ';' << request.get_timestamp().get_timestamp_ms() ; + csv_line << ";0;0;0;0" ; // TODO: handle calibration request + + const unordered_map &measurements = + request.get_measurements() ; + for (unordered_map::const_iterator i + = measurements.begin() ; i != measurements.end() ; ++i) + { + const vector &ss_list = i->second.get_ss_list() ; + const string &ap_mac = i->second.get_ap() != NULL + ? i->second.get_ap()->get_mac_addr() + : "" ; + for (vector::const_iterator i = ss_list.begin() ; + i != ss_list.end() ; ++i) + { + csv_line << ';' << ap_mac ; + csv_line << ';' << *i ; + } + } + + csv_line << '\n' ; + return csv_line.str() ; +} diff --git a/owlps-positioning/inputlogcsv.hh b/owlps-positioning/inputlogcsv.hh new file mode 100644 index 0000000..3356cab --- /dev/null +++ b/owlps-positioning/inputlogcsv.hh @@ -0,0 +1,28 @@ +#ifndef _OWLPS_POSITIONING_INPUTLOGCSV_HH_ +#define _OWLPS_POSITIONING_INPUTLOGCSV_HH_ + +#include "inputlogmedium.hh" + +#include +#include + +/// Log Request to a CSV file +class InputLogCSV: public InputLogMedium +{ +protected: + std::string log_file_name ; + std::ofstream log_file ; + + const std::string request_to_csv(const Request &request) const ; + +public: + InputLogCSV(const std::string &filename) ; + ~InputLogCSV(void) ; + + /** @name Operations */ + //@{ + bool log_request(const Request &request) ; + //@} +} ; + +#endif // _OWLPS_POSITIONING_INPUTLOGCSV_HH_ diff --git a/owlps-positioning/inputlogmedium.hh b/owlps-positioning/inputlogmedium.hh new file mode 100644 index 0000000..a086db4 --- /dev/null +++ b/owlps-positioning/inputlogmedium.hh @@ -0,0 +1,22 @@ +#ifndef _OWLPS_POSITIONING_INPUTLOGMEDIUM_HH_ +#define _OWLPS_POSITIONING_INPUTLOGMEDIUM_HH_ + +#include "request.hh" + +/// Super class of all input log media +/** + * Provide interface for input log media, i.e. to log Request received + * by the InputMedium. + */ +class InputLogMedium +{ +public: + virtual ~InputLogMedium(void) {} + + /** @name Operations */ + //@{ + virtual bool log_request(const Request &request) = 0 ; + //@} // End Operations +} ; + +#endif // _OWLPS_POSITIONING_INPUTLOGMEDIUM_HH_ diff --git a/owlps-positioning/posexcept.cc b/owlps-positioning/posexcept.cc index b06a4e9..fb9af41 100644 --- a/owlps-positioning/posexcept.cc +++ b/owlps-positioning/posexcept.cc @@ -49,6 +49,12 @@ const char* no_input_csv_file::what() const throw() } +const char* no_log_csv_file::what() const throw() +{ + return "No log CSV file specified in the configuration!" ; +} + + error_opening_input_file:: error_opening_input_file(const string &_file_name) throw(): file_name(_file_name) {} diff --git a/owlps-positioning/posexcept.hh b/owlps-positioning/posexcept.hh index c1f85c4..3d1f137 100644 --- a/owlps-positioning/posexcept.hh +++ b/owlps-positioning/posexcept.hh @@ -50,6 +50,13 @@ public: } ; +class no_log_csv_file: public std::exception +{ +public: + const char* what() const throw() ; +} ; + + class error_opening_input_file: public std::exception { private: diff --git a/owlps-positioning/tests/inputlogcsv_test.hh b/owlps-positioning/tests/inputlogcsv_test.hh new file mode 100644 index 0000000..2751961 --- /dev/null +++ b/owlps-positioning/tests/inputlogcsv_test.hh @@ -0,0 +1,89 @@ +#include + +#include "inputlogcsv.hh" + +#include + +class InputLogCSV_test: public CxxTest::TestSuite +{ +private: + std::string csv_file_name ; + std::string log_file_name ; + +public: + + InputLogCSV_test(void) + { + // If we cannot open the file, we want to stop the test + CxxTest::setAbortTestOnFail(true) ; + + // Clear the stock + Stock::clear() ; + + csv_file_name = "/tmp/InputLogCSV_test_csv_file.csv" ; + TestUtil::create_test_csv_file(csv_file_name) ; + + log_file_name = "/tmp/InputLogCSV_test_log_file.csv" ; + + // Back to the normal behaviour (i.e. do not abort on fail) + CxxTest::setAbortTestOnFail(false) ; + } + + + ~InputLogCSV_test(void) + { + TestUtil::remove_file(csv_file_name) ; + TestUtil::remove_file(log_file_name) ; + Stock::clear() ; + } + + + static InputLogCSV_test* createSuite(void) + { + return new InputLogCSV_test() ; + } + + + static void destroySuite(InputLogCSV_test *suite) + { + delete suite ; + } + + + void test_inputlogcsv(void) + { + // Note: we need to dynamically create and delete an InputLogCSV + // instance in order to be able to close the file (and therefore + // flush the stream) before to read it with InputCSV + InputLogCSV *inputlogcsv1 = new InputLogCSV(log_file_name) ; + for (std::vector::const_iterator i = + TestUtil::requests.begin() ; + i != TestUtil::requests.end() ; ++i) + inputlogcsv1->log_request(*i) ; + delete inputlogcsv1 ; // Close the output file + + // Even if data is not in the same order, size of the two files + // must be identical + TS_ASSERT(TestUtil::file_size_equals(csv_file_name, log_file_name)) ; + + // Read the two files and compare to the reference Request list + InputCSV inputcsv_csv(csv_file_name) ; + InputCSV inputcsv_log(log_file_name) ; + for (std::vector::const_iterator i = + TestUtil::requests.begin() ; + i != TestUtil::requests.end() ; ++i) + { + Stock::clear() ; + Request request_csv = inputcsv_csv.get_next_request() ; + TS_ASSERT(TestUtil::request_equals(*i, request_csv)) ; + + Stock::clear() ; + Request request_log = inputcsv_log.get_next_request() ; + TS_ASSERT(TestUtil::request_equals(*i, request_log)) ; + } + + TS_ASSERT(inputcsv_csv.eof()) ; + TS_ASSERT(inputcsv_log.eof()) ; + } + +} ; diff --git a/owlps-positioning/tests/testutil.cc b/owlps-positioning/tests/testutil.cc index e6c1ff2..655d4d6 100644 --- a/owlps-positioning/tests/testutil.cc +++ b/owlps-positioning/tests/testutil.cc @@ -1,11 +1,9 @@ #include "testutil.hh" #include -#include "measurement.hh" - #include #include -#include +#include using namespace std ; using std::tr1::unordered_map ; @@ -28,15 +26,22 @@ void TestUtil::set_up() tear_down() ; // Create mobile list - mobiles.push_back(Mobile("192.168.0.42", "aa:bb:cc:dd:ee:ff")) ; - mobiles.push_back(Mobile("192.168.1.87", "aa:bb:cc:dd:ee:77")) ; + Mobile mobile1 ; + mobile1.set_mac_addr("aa:bb:cc:dd:ee:ff") ; + mobiles.push_back(mobile1) ; + mobile1.set_mac_addr("aa:bb:cc:dd:ee:77") ; + mobiles.push_back(mobile1) ; // Create AP list - aps.push_back(AccessPoint(Point3D(1,2,3), "10.0.0.1", "11:22:33:44:55:01")) ; - aps.push_back(AccessPoint(Point3D(4,5,6), "10.0.0.2", "11:22:33:44:55:02")) ; - aps.push_back(AccessPoint(Point3D(7,8,9), "10.0.0.3", "11:22:33:44:55:03")) ; + AccessPoint ap1 ; + ap1.set_mac_addr("11:22:33:44:55:01") ; + aps.push_back(ap1) ; + ap1.set_mac_addr("11:22:33:44:55:02") ; + aps.push_back(ap1) ; + ap1.set_mac_addr("11:22:33:44:55:03") ; + aps.push_back(ap1) ; - // Create measurement list + // Create request list vector < unordered_map > measurements(3) ; Measurement measurement1 ; @@ -91,9 +96,12 @@ void TestUtil::set_up() timestamps.push_back(Timestamp(1265120912345)) ; // Create request list - requests.push_back(Request(timestamps[0], measurements[0])) ; - requests.push_back(Request(timestamps[1], measurements[1])) ; - requests.push_back(Request(timestamps[2], measurements[2])) ; + requests.push_back(Request(&mobiles[0], timestamps[0], + measurements[0])) ; + requests.push_back(Request(&mobiles[1], timestamps[1], + measurements[1])) ; + requests.push_back(Request(&mobiles[0], timestamps[2], + measurements[2])) ; } @@ -241,3 +249,114 @@ create_test_csv_file(const string &file_name, bool with_spaces) // Create and fill the test CSV file fill_file(file_name, csv_lines) ; } + + +// Test equality of two Request comparing pointed values instead of +// pointers and taking care of the unordered_map +bool TestUtil::request_equals(const Request &first, + const Request &second) +{ + // Try a classical comparison + if (first == second) + return true ; + + // Compare timestamp + if (first.get_timestamp() != second.get_timestamp()) + return false ; + + // Compare mobile values + if (first.get_mobile() == NULL || second.get_mobile() == NULL) + { + if (first.get_mobile() != NULL || second.get_mobile() != NULL) + return false ; + } + else if (*first.get_mobile() != *second.get_mobile()) + return false ; + + // Compare measurements + if (first.get_measurements() != second.get_measurements()) + if (! measurements_unordered_map_equals(first.get_measurements(), + second.get_measurements())) + return false ; + + return true ; +} + + +// Test equality of two unordered_map +bool TestUtil:: +measurements_unordered_map_equals( + unordered_map first, + unordered_map second) +{ + if (first.size() != second.size()) + return false ; + + // For each element in 'first', we look for the same element + // in 'second' + for (unordered_map::const_iterator i = + first.begin() ; i != first.end() ; ++i) + { + unordered_map::const_iterator + second_measurement = second.find(i->first) ; + if (second_measurement == second.end()) + return false ; + if (! measurement_equals(i->second, second_measurement->second)) + return false ; + } + + return true ; +} + + +// Test equality of two Measurement comparing pointed values instead of +// pointers +bool TestUtil::measurement_equals(const Measurement &first, + const Measurement &second) +{ + // Try a classical comparison + if (first == second) + return true ; + + // Compare average_ss + if (first.get_average_ss() != second.get_average_ss()) + return false ; + + // Compare ap values + if (first.get_ap() == NULL || second.get_ap() == NULL) + { + if (first.get_ap() != NULL || second.get_ap() != NULL) + return false ; + } + else if (*first.get_ap() != *second.get_ap()) + return false ; + + // Compare ss_list + if (first.get_ss_list() != second.get_ss_list()) + return false ; + + return true ; +} + + +bool TestUtil::file_size_equals(const string &file1_name, + const string &file2_name) +{ + try + { + return (file_size(file1_name) == file_size(file2_name)) ; + } + catch (...) + { + return false ; + } +} + + +off_t TestUtil::file_size(const string &file_name) +{ + struct stat file_info ; + if (stat(file_name.c_str(), &file_info) != 0) + throw "Cannot get information for file `" + file_name + "`!" ; + return file_info.st_size ; +} diff --git a/owlps-positioning/tests/testutil.hh b/owlps-positioning/tests/testutil.hh index 40c9479..358a909 100644 --- a/owlps-positioning/tests/testutil.hh +++ b/owlps-positioning/tests/testutil.hh @@ -4,9 +4,11 @@ #include "accesspoint.hh" #include "mobile.hh" #include "request.hh" +#include "measurement.hh" #include #include +#include class TestUtil { @@ -24,6 +26,20 @@ public: static void create_test_csv_file(const std::string &file_name, bool with_spaces = false) ; + + static bool request_equals(const Request &first, + const Request &second) ; + static bool measurements_unordered_map_equals( + std::tr1::unordered_map first, + std::tr1::unordered_map second) ; + static bool measurement_equals(const Measurement &first, + const Measurement &second) ; + + + static bool file_size_equals(const std::string &file1_name, + const std::string &file2_name) ; + static off_t file_size(const std::string &file_name) ; + } ; #endif // _OWLPS_POSITIONING_TESTS_TESTUTIL_HH_ diff --git a/owlps-positioning/tests/userinterface_test.hh b/owlps-positioning/tests/userinterface_test.hh index 269b04e..39286e3 100644 --- a/owlps-positioning/tests/userinterface_test.hh +++ b/owlps-positioning/tests/userinterface_test.hh @@ -67,8 +67,8 @@ public: TS_ASSERT(Configuration::is_configured("server.port")) ; TS_ASSERT(Configuration::is_configured("config-file")) ; - TS_ASSERT_EQUALS(Configuration::get_int_value("server.port"), 42) ; - TS_ASSERT_EQUALS(Configuration::get_string_value("config-file"), + TS_ASSERT_EQUALS(Configuration::int_value("server.port"), 42) ; + TS_ASSERT_EQUALS(Configuration::string_value("config-file"), config_file_name) ; } diff --git a/owlps-positioning/userinterface.cc b/owlps-positioning/userinterface.cc index c58bd33..1067bca 100644 --- a/owlps-positioning/userinterface.cc +++ b/owlps-positioning/userinterface.cc @@ -71,12 +71,19 @@ void UserInterface::fill_file_options() // Server options ("server.port,l", po::value() ->default_value(DEFAULT_LISTENING_PORT), - "Server listening port") + "Server listening port.") // Input options ("input.medium,I", po::value(), - "Medium from which requests are read") + "Medium from which requests are read. Allowed: CSV.") ("input.csv-file,C", po::value(), - "CSV file to use for input (when input.medium = CSV)") + "CSV file to use for input (when input.medium = CSV).") + // Log options + ("log.medium,L", po::value< vector >()->composing(), + "Medium to which the requests will be logged. You can specify \ +this option more than once. Allowed: none, CSV. The `none` value \ +completely disables logging.") + ("log.csv-file", po::value(), + "CSV file to use for logging (when log.medium = CSV).") ; } @@ -88,7 +95,7 @@ void UserInterface::parse_options() // Was the config file name specified on the command line? if (Configuration::is_configured("config-file")) - config_file_name = Configuration::get_string_value("config-file") ; + config_file_name = Configuration::string_value("config-file") ; parse_file() ;