Concepts
Configuration and properties
A device keeps its complete configuration as properties: values with a name, in one store of the type fair::mq::ProgOptions. The command-line options, the channel configuration and the values from plugins all go into this store. The device, its plugins and your code read the configuration from there, also while the device runs.
The property store
Section titled “The property store”A property has a key of the type std::string and a value of any C++ type. The value is a fair::mq::Property, which is a boost::any. fair::mq::Properties is a std::map<std::string, Property> for several properties. The store protects its data with a mutex, so all threads can use it.
A device has a pointer to its store: fConfig, also from GetConfig(). A device that you make with the constructor Device() has its own internal store. SetConfig() replaces it with another store and discards the internal store (Device.cxx). With runDevice.h, the device runner makes the store and gives it to the device with SetConfig().
The old name FairMQProgOptions is a deprecated alias of fair::mq::ProgOptions. Refer to Move from the FairMQ* names.
Where the properties come from
Section titled “Where the properties come from”A device made with runDevice.h gets its properties in this sequence (DeviceRunner.cxx):
- The device runner loads the config plugin and the control plugin, and collects the options of all plugins and your
addCustomOptions(). - It parses the command line. Each option becomes a property with the name of the option, for example
idorseverity. An option that you do not give gets its default, if it has one. - It makes the device with your
getDevice(), gives it the store, and makes the plugins. - It logs all properties with the severity
debug, and starts the state machine. - When the device goes into the state
INITIALIZING DEVICE, the config plugin reads--mq-configor--channel-config. It stores each channel property aschans.<name>.<index>.<property>. - When the controller requests the transition
COMPLETE INIT, the device reads its properties and makes its sub-channels.
Without DeviceRunner, your program parses the command line itself. See Parse options without DeviceRunner.
Plugins and your code can set properties at all times with SetProperty(). The device also writes a property: after it binds or connects a sub-channel, it writes the address that it uses to chans.<name>.<index>.address (Device.cxx).
When the device reads a property
Section titled “When the device reads a property”The device reads most properties once, at the request of the transition COMPLETE INIT (Device.cxx). A later change has an effect only when the device goes through INITIALIZING DEVICE again, for example after the transition RESET DEVICE.
| Properties | The device reads them | A later change |
|---|---|---|
id, rate, init-timeout, transport, network-interface |
At the request of COMPLETE INIT |
No effect |
chans.<name>.<index>.<property> |
At the request of COMPLETE INIT |
No effect, except for the address of a connecting sub-channel that the device could not connect yet |
io-threads, session, shmid, the shm-* and bad-alloc-* options |
When the device makes the transport, at the request of COMPLETE INIT |
No effect |
control, catch-signals |
When the device runner makes the control plugin | No effect |
severity, file-severity, verbosity, color, log-to-file |
After the parse of the command line | The device runner applies the new value at once (DeviceRunner.cxx) |
| Your custom options | When your code reads them | Your code decides |
Read and change properties
Section titled “Read and change properties”The device and the plugins use the same functions. A device calls them on fConfig. A plugin calls them on itself, for example Plugin::GetProperty(). In a plugin, Count() has the name PropertyExists().
| Function | Effect | If the key does not exist |
|---|---|---|
GetProperty<T>(key) |
Returns the value. | Throws PropertyNotFoundError. |
GetProperty<T>(key, ifNotFound) |
Returns the value. | Returns ifNotFound. |
GetPropertyAsString(key) |
Returns the value as text. | Throws PropertyNotFoundError. |
GetPropertyAsString(key, ifNotFound) |
Returns the value as text. | Returns ifNotFound. |
GetProperties(regex) |
Returns the properties whose full key matches the regular expression. | Returns an empty map and logs a warning. |
GetPropertiesStartingWith(prefix) |
Returns the properties whose key starts with prefix. |
Returns an empty map. |
GetPropertiesAsString(regex) |
The same as GetProperties(), with the values as text. |
Returns an empty map and logs a warning. |
GetPropertiesAsStringStartingWith(prefix) |
The same as GetPropertiesStartingWith(), with the values as text. |
Returns an empty map. |
GetPropertyKeys() |
Returns all keys. | |
Count(key) |
Returns 1 if the property exists, otherwise 0. | |
SetProperty<T>(key, value) |
Sets the value. | Makes the property. |
UpdateProperty<T>(key, value) |
Sets the value and returns true. |
Returns false. |
SetProperties(properties) |
Sets several values. | Makes the properties. |
UpdateProperties(properties) |
Sets several values and returns true. |
Returns false and changes nothing. |
DeleteProperty(key) |
Removes the property. | Does nothing. |
The type T must be the exact type of the stored value. For another type, the read throws boost::bad_any_cast. The type of an option is the type in its declaration, for example uint64_t for --max-iterations of the 1-1 sampler. Command-line options shows the type of each option.
Subscribe to changes
Section titled “Subscribe to changes”Subscribe<T>() registers a callback for the changes of all properties of the type T. SubscribeAsString() registers a callback that gets each change of each type, with the value as text. SetProperty(), UpdateProperty(), SetProperties() and UpdateProperties() call the callbacks (ProgOptions.h).
- The callback gets the key and the new value. It runs in the thread that changes the property, before the change function returns.
- Each subscription has the name of a subscriber. A second subscription with the same name and the same type replaces the first (EventManager.h).
Unsubscribe<T>()andUnsubscribeAsString()remove a subscription. For a name without a subscription, they throwstd::out_of_range.- A plugin uses
SubscribeToPropertyChange<T>(). The name of the plugin is the name of the subscriber.
This test of the plugin services shows the callbacks for three types, and the effect of an unsubscribe:
TEST_F(PluginServices, ConfigCallbacks){ mServices.SubscribeToPropertyChange<string>("test", [](const string& key, string value) { if (key == "chans.data.0.address") { ASSERT_EQ(value, "tcp://localhost:4321"); } if (key == "custom1") { ASSERT_EQ(value, "test1"); } if (key == "custom2") { ASSERT_EQ(value, "test2"); } });
mServices.SubscribeToPropertyChange<int>("test", [](const string& key, int /*value*/) { if (key == "chans.data.0.rcvBufSize") { FAIL(); // should not be called because we unsubscribed } });
mServices.SubscribeToPropertyChange<double>("test", [](const string& key, double value) { if (key == "data-rate") { ASSERT_EQ(value, 0.9); } });
mServices.SetProperty<string>("chans.data.0.address", "tcp://localhost:4321"); mServices.SetProperty<double>("data-rate", 0.9);
mServices.UnsubscribeFromPropertyChange<int>("test");
mServices.SetProperty<int>("chans.data.0.rcvBufSize", 100);}Your own types
Section titled “Your own types”A property can hold a value of your own type. To convert it to text, register the type with PropertyHelper::AddType<T>(). The type must have an operator<< for std::ostream. Without the registration, GetPropertyAsString() returns [unidentified_type]. SetProperties(), UpdateProperties(), GetPropertiesAsString() and GetPropertiesAsStringStartingWith() throw std::out_of_range for such a value (Properties.h).
Channel properties
Section titled “Channel properties”The configuration of a sub-channel is a group of properties with the keys chans.<name>.<index>.<property>, for example chans.data.0.address. Channel properties lists all of them. The parsers of --channel-config and JSON files make all 15 properties of each sub-channel.
GetChannelInfo()counts the sub-channels of each channel by theirtypeproperties (ProgOptions.cxx). A sub-channel without atypeproperty does not exist for the device.- The device makes the sub-channels with the indexes 0 to N-1, where N is the count. Thus use the indexes 0, 1, 2 and so on, without gaps.
- The device reads the channel properties at the request of
COMPLETE INIT. Set them before this request, for example in a plugin in the stateINITIALIZING DEVICE, or in the hookInit()of the device.
Configure channels shows the three ways to give the channel properties.
The config plugin
Section titled “The config plugin”The config plugin is a part of each device made with runDevice.h. It declares the options of the groups Device and Shared memory in the table of options, and reads the channel configuration (Config.cxx).
When the device goes into the state INITIALIZING DEVICE, the config plugin:
- Takes the value of
--config-key, or the value of--idif--config-keyis not there. - Reads the JSON file of
--mq-configfor this value. If there is no--mq-config, it reads--channel-config. - Stores the channel properties with
SetProperties().
If the parser throws an exception, the plugin logs the message with the severity error, and the device continues without these channels. If --id and --config-key are empty, the plugin logs a warning and reads nothing. JSON configuration lists the errors.
The device runner makes the plugins of -P before the config plugin. In INITIALIZING DEVICE, their callbacks thus run first. The config plugin then replaces the values of all channel properties that the configuration also gives.
Custom options
Section titled “Custom options”Your own options become properties as well. With runDevice.h, declare them in addCustomOptions(), and read them with GetProperty<T>(), usually in InitTask(). Command-line options shows the code of the 1-1 example.
Look at the configuration
Section titled “Look at the configuration”--print-optionsprints all options and their values, and stops. It does not show the channel properties, because the config plugin reads them later.- At the start, the device logs all properties with the severity
debug, after the lineConfiguration:. - With
--severity trace, the parsers log each property of each sub-channel. - With
--severity debug, the device logsAttached channel <name> to <endpoint> (bind|connect) (<type>)for each endpoint. - In your code,
GetPropertiesAsStringStartingWith("chans.")returns all channel properties as text.
Related pages
Section titled “Related pages”- Command-line options: all options, with their types and defaults.
- Channel properties: the keys of the channel properties.
- Configure channels:
--channel-config, JSON files and properties from code. - Plugins and control: the plugins that read and set properties.
- Device states: the states
INITIALIZING DEVICE,BINDINGandCONNECTING.