ConfigManager.cpp 6.67 KB
Newer Older
1
2
3
/**
 * @file
 * @brief Implementation of config manager
4
 * @copyright Copyright (c) 2017 CERN and the Corryvreckan authors.
5
6
 * 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
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
 * Intergovernmental Organization or submit itself to any jurisdiction.
 */

#include "ConfigManager.hpp"

#include <algorithm>
#include <fstream>
#include <string>
#include <vector>

#include "Configuration.hpp"
#include "core/utils/file.h"
#include "core/utils/log.h"
#include "exceptions.h"

using namespace corryvreckan;

/**
25
 * @throws ConfigFileUnavailableError If the main configuration file cannot be accessed
26
 */
27
28
29
ConfigManager::ConfigManager(std::string file_name,
                             std::initializer_list<std::string> global,
                             std::initializer_list<std::string> ignore) {
30
    // Check if the file exists
31
    std::ifstream file(file_name);
32
    if(!file) {
33
        throw ConfigFileUnavailableError(file_name);
34
35
36
    }

    // Convert main file to absolute path
37
38
    file_name = corryvreckan::get_canonical_path(file_name);
    LOG(TRACE) << "Reading main configuration";
39

40
    // Read the file
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
    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
74
75
76
77
78
79
80
81
82
83
    auto detector_file_names = global_config_.getPathArray("detectors_file", true);
    LOG(TRACE) << "Reading detector configurations";

    for(auto& detector_file_name : detector_file_names) {
        std::ifstream detector_file(detector_file_name);
        ConfigReader detector_reader(detector_file, detector_file_name);
        auto detector_configs = detector_reader.getConfigurations();
        detector_configs_.splice(detector_configs_.end(),
                                 std::list<Configuration>(detector_configs.begin(), detector_configs.end()));
    }
84
85
86
}

/**
87
 * The global configuration is the combination of all sections with a global header.
88
 */
89
90
Configuration& ConfigManager::getGlobalConfiguration() {
    return global_config_;
91
92
93
}

/**
94
95
 * 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.
96
 */
97
98
99
100
101
102
bool ConfigManager::loadModuleOptions(const std::vector<std::string>& options) {
    bool optionsApplied = false;

    // Parse the options
    for(auto& option : options) {
        module_option_parser_.parseOption(option);
103
    }
104
105
106
107
108
109
110
111
112
113

    // 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;
114
115
}

116
/**
117
118
119
 * 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.
120
 */
121
122
123
124
125
bool ConfigManager::loadDetectorOptions(const std::vector<std::string>& options) {
    bool optionsApplied = false;

    // Create the parser
    OptionParser detector_option_parser;
126

127
128
129
    // Parse the options
    for(auto& option : options) {
        detector_option_parser.parseOption(option);
130
131
    }

132
133
134
    // Apply detector options
    for(auto& config : detector_configs_) {
        optionsApplied = detector_option_parser.applyOptions(config.getName(), config) || optionsApplied;
135
    }
136
137

    return optionsApplied;
138
139
}

140
141
142
143
144
145
146
147
148
149
150
/**
 * 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_;
151
152
153
}

/**
154
155
156
157
 * @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.
158
 */
159
160
161
162
163
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]);
164
165
    }

166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
    // 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_;
185
}