EMANE  1.0.1
Configuration Service

The ConfigurationRegistrar is used by components to register configuration items. Configuration registration provides the emulator with all the knowledge it needs to enforce the correctness of input parameters.

Registering Configuration Items

Configuration items can only be registered during Component::initialize. The ConfigurationRegistrar is accessible via the initialize method's Registrar argument.

The ConfigurationRegistrar has two registration template methods which allow components to register numeric and non-numeric configuration items:

Numeric configuration items may have any one of the following types:

  • std::int64_t
  • std::int32_t
  • std::int16_t
  • std::int8_t
  • std::uint64_t
  • std::uint32_t
  • std::uint16_t
  • std::uint8_t
  • bool
  • float
  • double
configRegistrar.registerNumeric<float>("jitter",
ConfigurationProperties::DEFAULT |
ConfigurationProperties::MODIFIABLE,
{0},
"Defines delay jitter in seconds applied to each transmitted packet."
" The jitter is added to the configured delay based on a uniform"
" random distribution between +/- the configured jitter value.",
0.0f);
configRegistrar.registerNumeric<float>("delay",
ConfigurationProperties::DEFAULT |
ConfigurationProperties::MODIFIABLE,
{0},
"Defines an additional fixed delay in seconds applied to each"
" transmitted packet.",
0.0f);
configRegistrar.registerNumeric<bool>("flowcontrolenable",
ConfigurationProperties::DEFAULT,
{false},
"Defines whether flow control is enabled. Flow control only works"
" with the virtual transport and the setting must match the setting"
" within the virtual transport configuration.");
configRegistrar.registerNumeric<std::uint16_t>("flowcontroltokens",
ConfigurationProperties::DEFAULT,
{10},
"Defines the maximum number of flow control tokens"
" (packet transmission units) that can be processed from the"
" virtual transport without being refreshed. The number of"
" available tokens at any given time is coordinated with the"
" virtual transport and when the token count reaches zero, no"
" further packets are transmitted causing application socket"
" queues to backup.");

Non-Numeric configuration items may have any one of the following types:

  • std::string
  • INETAddr
configRegistrar.registerNonNumeric<std::string>("pcrcurveuri",
ConfigurationProperties::REQUIRED,
{},
"Defines the absolute URI of the Packet Completion Rate (PCR) curve"
" file. The PCR curve file contains probability of reception curves"
" as a function of Signal to Interference plus Noise Ratio (SINR).");

Both register calls have parameters to specify a description string, 0 or more default values, min/max occurrence counts (for multiplicity - xml paramlists), item properties such as running-state modifiable and a regex for further validation. Additionally, registerNumeric supports specifying a value range. The emulator will enforce data type, value range, occurrence count, regex match and running-state modification permission without any component interaction.

Configuration Item properties:

Validating Configuration Item Relationships

An optional validator callable is used by the emulator framework to validate configuration item relationships prior to calling Component::configure or RunningStateMutable::processConfiguration on a component.

A component can register a validator callable using ConfigurationRegistrar::registerValidator.

auto & configRegistrar = registrar.configurationRegistrar();
macConfig_.registerConfiguration(configRegistrar);
configRegistrar.registerValidator(&configurationValidator);

This callable is passed all known configuration items and returns a std::pair<std::string,bool> where second is either true or false depending on whether the relationship of those configuration items are valid. When an invalid parameter value is detected, first should contain an error description. Configuration updates are transactional, so when a validator returns false the component will not see any of the configuration passed in via configure or processConfiguration.

For example, a model may have a min and max contention window. An invariant would be that min <= max. If a validator testing that invariant was registered, the component would not have configure or processConfiguration called on it if an attempt was made to configure min > max. The validator would detect the error and return false.

The framework keeps a cache of the current running configuration of each component. If an attempt was made to only change a single parameter, in this illustrative case min contention window, the validator would receive all the current cached configuration with the exception of min contention window which would reflect the proposed value (min > max). This provides the validator with all it needs in order to verify all parameter relationships.

The validator callback is executed on an internal framework thread. You should not need to access any shared data in the callable since all known configuration is passed in. However, if for some reason a component needs to access shared data in the callable a synchronization object must be used.

Once processConfiguration is called a component has no ability to reject configuration - that opportunity is provided by the validator callable. The framework will update it's configuration cache and report those values as the new running state.

Both configure and processConfiguration are passed a single ConfigurationUpdate parameter.

std::pair<std::string,std::vector<EMANE::Any>>;
std::vector<ConfigurationNameAnyValues>;

Handling Configuration Items

For a configuration item with a min/max instance of [0:1], you can safely process the configuration value by accessing element 0. You will only have a configuration item in ConfigurationUpdate if it has 1 or more values.

else if(item.first == "txpower")
{
dTxPowerdBm_ = item.second[0].asDouble();
LOGGER_STANDARD_LOGGING(pPlatformService_->logService(),
"PHYI %03hu FrameworkPHY::%s: %s = %3.2f dBm",
id_,
__func__,
item.first.c_str(),
dTxPowerdBm_);
}

For a parameter with multiple values, you will have to iterate over them.

else if(item.first == "frequencyofinterest")
{
for(const auto & any : item.second)
{
std::uint64_t u64Value{any.asUINT64()};
// try to add value to foi set
if(foi.insert(u64Value).second)
{
LOGGER_STANDARD_LOGGING(pPlatformService_->logService(),
"PHYI %03hu FrameworkPHY::%s: %s = %ju Hz",
id_,
__func__,
item.first.c_str(),
u64Value);
}
else
{
throw makeException<ConfigureException>("FrameworkPHY duplicate frequency of interest found: %ju",
u64Value);
}
}
}

Running-State Configuration Updates

When a running-state configuration update is received it is pushed onto the NEM's functor queue as an RunningStateMutable::processConfiguration method. In order for an update to be passed to an NEM it must pass configuration item property checks and any registered validator.