Skip to content
GitLab
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in
Toggle navigation
Menu
Open sidebar
Corryvreckan
Corryvreckan
Commits
86f785c1
Commit
86f785c1
authored
Dec 13, 2018
by
Simon Spannagel
Browse files
Update ConfigManager, add OptionParser
parent
a8eb9a9e
Changes
5
Hide whitespace changes
Inline
Side-by-side
src/core/CMakeLists.txt
View file @
86f785c1
...
...
@@ -12,6 +12,7 @@ ADD_LIBRARY(CorryvreckanCore SHARED
config/ConfigReader.cpp
config/Configuration.cpp
config/exceptions.cpp
config/OptionParser.cpp
module/Module.cpp
module/ModuleManager.cpp
)
...
...
src/core/config/ConfigManager.cpp
View file @
86f785c1
/**
* @file
* @brief Implementation of config manager
* @copyright Copyright (c) 2017 CERN and the
Allpix Squared
authors.
* @copyright Copyright (c) 2017 CERN and the
Corryvreckan
authors.
* This software is distributed under the terms of the MIT License, copied verbatim in the file "LICENSE.md".
* In applying this license, CERN does not waive the privileges and immunities granted to it by virtue of its status as an
* Intergovernmental Organization or submit itself to any jurisdiction.
...
...
@@ -24,117 +24,159 @@ using namespace corryvreckan;
/**
* @throws ConfigFileUnavailableError If the main configuration file cannot be accessed
*/
ConfigManager
::
ConfigManager
(
std
::
string
file_name
)
:
file_name_
(
std
::
move
(
file_name
))
{
LOG
(
TRACE
)
<<
"Using "
<<
file_name_
<<
" as main configuration file"
;
ConfigManager
::
ConfigManager
(
std
::
string
file_name
,
std
::
initializer_list
<
std
::
string
>
global
,
std
::
initializer_list
<
std
::
string
>
ignore
)
{
// Check if the file exists
std
::
ifstream
file
(
file_name
_
);
std
::
ifstream
file
(
file_name
);
if
(
!
file
)
{
throw
ConfigFileUnavailableError
(
file_name
_
);
throw
ConfigFileUnavailableError
(
file_name
);
}
// Convert main file to absolute path
file_name_
=
corryvreckan
::
get_canonical_path
(
file_name_
);
// Initialize global base configuration with absolute file name
// FIXME do not hard-code name!
global_base_config_
=
Configuration
(
"Corryvreckan"
,
file_name_
);
file_name
=
corryvreckan
::
get_canonical_path
(
file_name
);
LOG
(
TRACE
)
<<
"Reading main configuration"
;
// Read the file
reader_
.
add
(
file
,
file_name_
);
ConfigReader
reader
(
file
,
file_name
);
// Convert all global and ignored names to lower case and store them
auto
lowercase
=
[](
const
std
::
string
&
in
)
{
std
::
string
out
(
in
);
std
::
transform
(
out
.
begin
(),
out
.
end
(),
out
.
begin
(),
::
tolower
);
return
out
;
};
std
::
transform
(
global
.
begin
(),
global
.
end
(),
std
::
inserter
(
global_names_
,
global_names_
.
end
()),
lowercase
);
std
::
transform
(
ignore
.
begin
(),
ignore
.
end
(),
std
::
inserter
(
ignore_names_
,
ignore_names_
.
end
()),
lowercase
);
// Initialize global base configuration
global_config_
=
reader
.
getHeaderConfiguration
();
// Store all the configurations read
for
(
auto
&
config
:
reader
.
getConfigurations
())
{
// Skip all ignored sections
std
::
string
config_name
=
config
.
getName
();
std
::
transform
(
config_name
.
begin
(),
config_name
.
end
(),
config_name
.
begin
(),
::
tolower
);
if
(
ignore_names_
.
find
(
config_name
)
!=
ignore_names_
.
end
())
{
continue
;
}
// Merge all global section with the global config
if
(
global_names_
.
find
(
config_name
)
!=
global_names_
.
end
())
{
global_config_
.
merge
(
config
);
continue
;
}
module_configs_
.
push_back
(
config
);
}
// Reading detector file
std
::
string
detector_file_name
=
global_config_
.
getPath
(
"detectors_file"
,
true
);
LOG
(
TRACE
)
<<
"Reading detector configuration"
;
std
::
ifstream
detector_file
(
detector_file_name
);
ConfigReader
detector_reader
(
detector_file
,
detector_file_name
);
auto
detector_configs
=
detector_reader
.
getConfigurations
();
detector_configs_
=
std
::
list
<
Configuration
>
(
detector_configs
.
begin
(),
detector_configs
.
end
());
}
/**
*
@warning Only one header can be added in this way to define its main name
*
The global configuration is the combination of all sections with a global header.
*/
void
ConfigManager
::
setGlobalHeaderName
(
std
::
string
name
)
{
global_default_name_
=
name
;
std
::
transform
(
name
.
begin
(),
name
.
end
(),
name
.
begin
(),
::
tolower
);
global_names_
.
emplace
(
std
::
move
(
name
));
}
void
ConfigManager
::
addGlobalHeaderName
(
std
::
string
name
)
{
std
::
transform
(
name
.
begin
(),
name
.
end
(),
name
.
begin
(),
::
tolower
);
global_names_
.
emplace
(
std
::
move
(
name
));
Configuration
&
ConfigManager
::
getGlobalConfiguration
()
{
return
global_config_
;
}
/**
* The global configuration is the combination of all sections with a global header.
* Load all extra options that should be added on top of the configuration in the file. The options loaded here are
* automatically applied to the module instance when these are added later.
*/
Configuration
ConfigManager
::
getGlobalConfiguration
()
{
// Copy base config and set name
Configuration
global_config
=
global_base_config_
;
// FIXME needs to be done when loading
// global_config.setName(global_default_name_);
// Add all other global configuration
for
(
auto
&
global_name
:
global_names_
)
{
auto
configs
=
reader_
.
getConfigurations
(
global_name
);
for
(
auto
&
config
:
configs
)
{
global_config
.
merge
(
config
);
}
bool
ConfigManager
::
loadModuleOptions
(
const
std
::
vector
<
std
::
string
>&
options
)
{
bool
optionsApplied
=
false
;
// Parse the options
for
(
auto
&
option
:
options
)
{
module_option_parser_
.
parseOption
(
option
);
}
return
global_config
;
}
void
ConfigManager
::
addIgnoreHeaderName
(
std
::
string
name
)
{
std
::
transform
(
name
.
begin
(),
name
.
end
(),
name
.
begin
(),
::
tolower
);
ignore_names_
.
emplace
(
std
::
move
(
name
));
// Apply global options
optionsApplied
=
module_option_parser_
.
applyGlobalOptions
(
global_config_
)
||
optionsApplied
;
// Apply module options
for
(
auto
&
config
:
module_configs_
)
{
optionsApplied
=
module_option_parser_
.
applyOptions
(
config
.
getName
(),
config
)
||
optionsApplied
;
}
return
optionsApplied
;
}
/**
* Option is split in a key / value pair, an error is thrown if that is not possible. When the key contains at least one dot
* it is interpreted as a relative configuration with the module identified by the first dot. In that case the option is
* applied during module loading when either the unique or the configuration name match. Otherwise the key is interpreted as
* global key and is added to the global header.
* Load all extra options that should be added on top of the detector configuration in the file. The options loaded here are
* automatically applied to the detector instance when these are added later and will be taken into account when possibly
* loading customized detector models.
*/
void
ConfigManager
::
parseOption
(
std
::
string
line
)
{
line
=
corryvreckan
::
trim
(
line
);
auto
key_value
=
ConfigReader
::
parseKeyValue
(
line
);
auto
key
=
key_value
.
first
;
auto
value
=
key_value
.
second
;
auto
dot_pos
=
key
.
find
(
'.'
);
if
(
dot_pos
==
std
::
string
::
npos
)
{
// Global option, add to the global base config
global_base_config_
.
setText
(
key
,
value
);
}
else
{
// Other identifier bound option is passed
auto
identifier
=
key
.
substr
(
0
,
dot_pos
);
key
=
key
.
substr
(
dot_pos
+
1
);
identifier_options_
[
identifier
].
push_back
(
std
::
make_pair
(
key
,
value
));
}
}
bool
ConfigManager
::
loadDetectorOptions
(
const
std
::
vector
<
std
::
string
>&
options
)
{
bool
optionsApplied
=
false
;
// Create the parser
OptionParser
detector_option_parser
;
bool
ConfigManager
::
applyOptions
(
const
std
::
string
&
identifier
,
Configuration
&
config
)
{
if
(
identifier_options_
.
find
(
identifier
)
==
identifier_options_
.
end
()
)
{
r
et
urn
false
;
// Parse the options
for
(
auto
&
option
:
options
)
{
d
et
ector_option_parser
.
parseOption
(
option
)
;
}
for
(
auto
&
key_value
:
identifier_options_
[
identifier
])
{
config
.
setText
(
key_value
.
first
,
key_value
.
second
);
// Apply detector options
for
(
auto
&
config
:
detector_configs_
)
{
optionsApplied
=
detector_option_parser
.
applyOptions
(
config
.
getName
(),
config
)
||
optionsApplied
;
}
return
true
;
return
optionsApplied
;
}
bool
ConfigManager
::
hasConfiguration
(
const
std
::
string
&
name
)
{
return
reader_
.
hasConfiguration
(
name
);
/**
* All special global and ignored sections are not included in the list of module configurations.
*/
std
::
list
<
Configuration
>&
ConfigManager
::
getModuleConfigurations
()
{
return
module_configs_
;
}
/**
* The list of detector configurations is read from the configuration defined in 'detector_file'
*/
std
::
list
<
Configuration
>&
ConfigManager
::
getDetectorConfigurations
()
{
return
detector_configs_
;
}
/**
* All special global and ignored sections are removed before returning the rest of the configurations. The list of normal
* sections is used by the ModuleManager to instantiate all the required modules.
* @warning A previously stored configuration is directly invalidated if the same unique name is used again
*
* An instance configuration is a specialized configuration for a particular module instance. If an unique name already
* exists the previous record is deleted and a new configuration record corresponding to the replaced instance is added.
*/
std
::
vector
<
Configuration
>
ConfigManager
::
getConfigurations
()
const
{
std
::
vector
<
Configuration
>
result
;
for
(
auto
&
config
:
reader_
.
getConfigurations
())
{
// ignore all global and ignores names
std
::
string
config_name
=
config
.
getName
();
std
::
transform
(
config_name
.
begin
(),
config_name
.
end
(),
config_name
.
begin
(),
::
tolower
);
if
(
global_names_
.
find
(
config_name
)
!=
global_names_
.
end
()
||
ignore_names_
.
find
(
config_name
)
!=
ignore_names_
.
end
())
{
continue
;
}
result
.
push_back
(
config
);
Configuration
&
ConfigManager
::
addInstanceConfiguration
(
const
ModuleIdentifier
&
identifier
,
const
Configuration
&
config
)
{
std
::
string
unique_name
=
identifier
.
getUniqueName
();
// Check uniqueness
if
(
instance_name_to_config_
.
find
(
unique_name
)
!=
instance_name_to_config_
.
end
())
{
instance_configs_
.
erase
(
instance_name_to_config_
[
unique_name
]);
}
return
result
;
// Add configuration
instance_configs_
.
push_back
(
config
);
Configuration
&
ret_config
=
instance_configs_
.
back
();
instance_name_to_config_
[
unique_name
]
=
--
instance_configs_
.
end
();
// Add identifier key to config
ret_config
.
set
<
std
::
string
>
(
"identifier"
,
identifier
.
getIdentifier
());
// Apply instance options
module_option_parser_
.
applyOptions
(
unique_name
,
ret_config
);
return
ret_config
;
}
/**
* The list of instance configurations can contain configurations with duplicate names, but the instance configuration is
* guaranteed to have a configuration value 'identifier' that contains an unique identifier for every same config name
*/
std
::
list
<
Configuration
>&
ConfigManager
::
getInstanceConfigurations
()
{
return
instance_configs_
;
}
src/core/config/ConfigManager.hpp
View file @
86f785c1
/**
* @file
* @brief Interface to the main configuration and its normal and special sections
* @copyright Copyright (c) 2017 CERN and the
Allpix Squared
authors.
* @copyright Copyright (c) 2017 CERN and the
Corryvreckan
authors.
* This software is distributed under the terms of the MIT License, copied verbatim in the file "LICENSE.md".
* In applying this license, CERN does not waive the privileges and immunities granted to it by virtue of its status as an
* Intergovernmental Organization or submit itself to any jurisdiction.
...
...
@@ -16,6 +16,8 @@
#include
"ConfigReader.hpp"
#include
"Configuration.hpp"
#include
"OptionParser.hpp"
#include
"core/module/ModuleIdentifier.hpp"
namespace
corryvreckan
{
...
...
@@ -35,8 +37,12 @@ namespace corryvreckan {
/**
* @brief Construct the configuration manager
* @param file_name Path to the main configuration file
* @param global List of sections representing the global configuration (excluding the empty header section)
* @param ignore List of sections that should be ignored
*/
explicit
ConfigManager
(
std
::
string
file_name
);
explicit
ConfigManager
(
std
::
string
file_name
,
std
::
initializer_list
<
std
::
string
>
global
=
{},
std
::
initializer_list
<
std
::
string
>
ignore
=
{
"Ignore"
});
/**
* @brief Use default destructor
*/
...
...
@@ -59,70 +65,63 @@ namespace corryvreckan {
/// @}
/**
* @brief
S
et the
name of the global header and add to the global names
* @
param name Name of a global header that should be used as the name
* @brief
G
et the
global configuration
* @
return Reference to global configuration
*/
// TODO [doc] Should only set the name and do not add it
void
setGlobalHeaderName
(
std
::
string
name
);
Configuration
&
getGlobalConfiguration
();
/**
* @brief
Add a globa
l he
ader name
* @
param name Name of a global header sec
tion
* @brief
Get al
l
t
he
module configurations
* @
return Reference to list of module configura
tion
s
*/
// TODO [doc] Rename to addGlobalHeader
void
addGlobalHeaderName
(
std
::
string
name
);
std
::
list
<
Configuration
>&
getModuleConfigurations
();
/**
* @brief Get the global configuration
* @return Global configuration
* @brief Add a new module instance configuration and applies instance options
* @param identifier Identifier for this module instance
* @param config Instance configuration to store
* @return Reference to stored instance configuration
*/
Configuration
getGlobalConfiguration
();
Configuration
&
addInstanceConfiguration
(
const
ModuleIdentifier
&
identifier
,
const
Configuration
&
config
);
/**
* @brief
Add a header name to fully ignore
* @
param name Name of a header to ignore
* @brief
Get all the instance configurations
* @
return Reference to list of instance configurations
*/
// TODO [doc] Rename to ignoreHeader
void
addIgnoreHeaderName
(
std
::
string
name
);
std
::
list
<
Configuration
>&
getInstanceConfigurations
();
/**
* @brief Parse an extra configuration option
* @param line Line with the option
* @brief Load module options and directly apply them to the global configuration and the module configurations
* @param options List of options to load and apply
* @return True if any actual options where applied
*
* @note Instance configuration options are applied in \ref ConfigManager::addInstanceConfiguration instead
*/
void
parseOption
(
std
::
string
line
);
bool
loadModuleOptions
(
const
std
::
vector
<
std
::
string
>&
options
);
/**
* @brief Apply all relevant options to the passed configuration
* @param identifier Identifier to select the options to apply
* @param config Configuration option where the options should be applied to
* @return True if the configuration was changed because of applied options
* @brief Get all the detector configurations
* @return Reference to list of detector configurations
*/
bool
applyOptions
(
const
std
::
string
&
identifier
,
Configuration
&
config
);
std
::
list
<
Configuration
>&
getDetector
Configuration
s
(
);
/**
* @brief
Return if
sec
tion with given name exist
s
* @param
name Name of the section
* @return True if a
t least one sec
tion w
ith that name exists, false otherwise
* @brief
Load detector
s
p
ec
ific option
s
* @param
options List of options to load and apply
* @return True if a
ny actual op
tion
s
w
here applied
*/
bool
hasConfiguration
(
const
std
::
string
&
);
/**
* @brief Get all configurations that are not global or ignored
* @return List of all normal configurations
*/
std
::
vector
<
Configuration
>
getConfigurations
()
const
;
bool
loadDetectorOptions
(
const
std
::
vector
<
std
::
string
>&
options
);
private:
std
::
string
file_name_
;
std
::
set
<
std
::
string
>
global_names_
{};
std
::
set
<
std
::
string
>
ignore_names_
{};
ConfigReader
read
er_
;
OptionParser
module_option_pars
er_
;
Configuration
global_base_config_
;
std
::
string
global_default_name_
{};
std
::
set
<
std
::
string
>
global_names_
{};
std
::
list
<
Configuration
>
module_configs_
;
Configuration
global_config_
;
std
::
set
<
std
::
string
>
ignore_names_
{}
;
std
::
list
<
Configuration
>
detector_configs_
;
std
::
map
<
std
::
string
,
std
::
vector
<
std
::
pair
<
std
::
string
,
std
::
string
>>>
identifier_options_
{};
std
::
list
<
Configuration
>
instance_configs_
;
std
::
map
<
std
::
string
,
std
::
list
<
Configuration
>::
iterator
>
instance_name_to_config_
;
};
}
// namespace corryvreckan
...
...
src/core/config/OptionParser.cpp
0 → 100644
View file @
86f785c1
/**
* @file
* @brief Implementation of option parser
* @copyright Copyright (c) 2017 CERN and the Corryvreckan authors.
* This software is distributed under the terms of the MIT License, copied verbatim in the file "LICENSE.md".
* In applying this license, CERN does not waive the privileges and immunities granted to it by virtue of its status as an
* Intergovernmental Organization or submit itself to any jurisdiction.
*/
#include
"OptionParser.hpp"
#include
"ConfigReader.hpp"
using
namespace
corryvreckan
;
/**
* Option is split in a key / value pair, an error is thrown if that is not possible. When the key contains at least one dot
* it is interpreted as a relative configuration with the module / detector identified by the first dot. In that case the
* option is applied during loading when either the unique or the configuration name match. Otherwise the key is interpreted
* as global key and is added to the global header.
*/
void
OptionParser
::
parseOption
(
std
::
string
line
)
{
line
=
corryvreckan
::
trim
(
line
);
auto
key_value
=
ConfigReader
::
parseKeyValue
(
line
);
auto
key
=
key_value
.
first
;
auto
value
=
key_value
.
second
;
auto
dot_pos
=
key
.
find
(
'.'
);
if
(
dot_pos
==
std
::
string
::
npos
)
{
// Global option, add to the global options list
global_options_
.
emplace_back
(
key
,
value
);
}
else
{
// Other identifier bound option is passed
auto
identifier
=
key
.
substr
(
0
,
dot_pos
);
key
=
key
.
substr
(
dot_pos
+
1
);
identifier_options_
[
identifier
].
push_back
(
std
::
make_pair
(
key
,
value
));
}
}
bool
OptionParser
::
applyGlobalOptions
(
Configuration
&
config
)
{
for
(
auto
&
key_value
:
global_options_
)
{
config
.
setText
(
key_value
.
first
,
key_value
.
second
);
}
return
!
global_options_
.
empty
();
}
bool
OptionParser
::
applyOptions
(
const
std
::
string
&
identifier
,
Configuration
&
config
)
{
if
(
identifier_options_
.
find
(
identifier
)
==
identifier_options_
.
end
())
{
return
false
;
}
for
(
auto
&
key_value
:
identifier_options_
[
identifier
])
{
config
.
setText
(
key_value
.
first
,
key_value
.
second
);
}
return
true
;
}
src/core/config/OptionParser.hpp
0 → 100644
View file @
86f785c1
/**
* @file
* @brief Option parser for additional command line options
* @copyright Copyright (c) 2017 CERN and the Corryvreckan authors.
* This software is distributed under the terms of the MIT License, copied verbatim in the file "LICENSE.md".
* In applying this license, CERN does not waive the privileges and immunities granted to it by virtue of its status as an
* Intergovernmental Organization or submit itself to any jurisdiction.
*/
#ifndef CORRYVRECKAN_OPTION_PARSER_H
#define CORRYVRECKAN_OPTION_PARSER_H
#include
<set>
#include
<string>
#include
<vector>
#include
"Configuration.hpp"
namespace
corryvreckan
{
/**
* @ingroup Managers
* @brief Option parser responsible for parsing and caching command line arguments
*
* The option parser stores additional configuration items provided via the command line interface for later reference,
* since most of the parameters can only be applied once all modules have been instantiated.
*/
class
OptionParser
{
public:
/**
* @brief Use default constructor
*/
OptionParser
()
=
default
;
/**
* @brief Use default destructor
*/
~
OptionParser
()
=
default
;
/// @{
/**
* @brief Copying the option parser is not allowed
*/
OptionParser
(
const
OptionParser
&
)
=
delete
;
OptionParser
&
operator
=
(
const
OptionParser
&
)
=
delete
;
/// @}
/// @{
/**
* @brief Use default move behaviour
*/
OptionParser
(
OptionParser
&&
)
noexcept
=
default
;
OptionParser
&
operator
=
(
OptionParser
&&
)
noexcept
=
default
;
/// @}
/**
* @brief Parse an extra configuration option
* @param line Line with the option
*/
void
parseOption
(
std
::
string
line
);
/**
* @brief Apply all global options to a given global configuration object
* @param config Global configuration option to which the options should be applied to
* @return True if new global configuration options were applied
*/
bool
applyGlobalOptions
(
Configuration
&
config
);
/**
* @brief Apply all relevant options to the given configuration object
* @param identifier Identifier to select the options to apply
* @param config Configuration option to which the options should be applied to
* @return True if the configuration was changed because of applied options
*/
bool
applyOptions
(
const
std
::
string
&
identifier
,
Configuration
&
config
);
private:
std
::
vector
<
std
::
pair
<
std
::
string
,
std
::
string
>>
global_options_
{};
std
::
map
<
std
::
string
,
std
::
vector
<
std
::
pair
<
std
::
string
,
std
::
string
>>>
identifier_options_
{};
};
}
// namespace corryvreckan
#endif
/* CORRYVRECKAN_OPTION_PARSER_H */
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment