- 30 Sep 2020
Reading configuration files by INDI drivers
Each INDI driver has an associated config file and this file has the same name as the driver (or symlink). The file is loaded when the driver starts, and the information is held for the lifetime of the driver. The file is read once and is not written to, but the information that is held in it can be updated. These changes will be lost when the driver is restarted.
The format of the config file is basically a simple name-value pair:
The name should only be composed of letters, numbers and underscores, capitalization should be matched exactly, as the names are case sensitive. There may be comments in the files, they are marked with a "#" and everything after it until the end of a line will be ignored:
# This is a line comment.
value_entry 10 # So is this.
There are no data types associated with the entries; the user can intrepret them as any type. All are read in and kept as strings, however. One detail about the entries is that an entry will be naturally broken by whitespace. An exception to this is if the value is enclosed in double quotes:
In this case, both entries have the exact same value, "joe" since it has no spaces, but:
camera_name1 little joe
camera_name2 "little joe"
will give an error for "camera_name1", whereas "camera_name2" will be loaded correctly.
A config file will be complete if entries are only made this way, but another feature is to have groups of data. This can be done two ways:
name "little joe"
camera.name "little joe"
In the first case, the "[" and "]" must surround the name of the group, and be at the begining and end of the section demarking the group. In the second case, the group name is in front of each entry and separated from the name by a ".". In this form, entries in this group may not be together, but may be sprinkled throughout the file, yet still be stored as part of the group. When referencing entries in either case, use the full name. For example, "camera.name" or "camera.width".
Using The Software Framework To Read The Config File
There is no enforcement in the config classes as to having the config file have the same name as the executable; this is done using other code. The config file is initialized using a name and a path to the correct file.
pcf::Config::init( "/absolute/path/to/file/", "filename" );
catch ( const std::runtime_error &err )
// Do something on the failure of reading the config file.
This is usually done in main, and after it is successful, classes can be instantiated to read the config file values:
string szName = cfReader.get<string>( "camera.name", "default name" );
int nWidth = cfReader.get<int>( "camera.width", 3000 );
int nHeight = cfReader.get<int>( "camera.height", 3000 );
bool oEnable = cfReader.get<bool>( "camera.enable", false );
This "Config" instance can be on the stack; they are merely interfaces into the underlying singleton. As can be seen, the methods are templated to allow the underlying data to be interpreted as any type. A default is given to ensure that if the entry does not exist, some value will be assigned, regardless.
Another more sophisticated way to read config file entries is to use the instatiation and stream operator in one line:
Config( "camera.name", "default name" ) >> szName;
Config( "camera.width", 3000 ) >> nWidth;
Config( "camera.height", 3000 ) >> nHeight;
Config( "camera.enable", false ) >> oEnable;
This is another layer on top of the direct calls. To take it yet another step further, entire groups can be read out by mapping each config file group to an INDI property by making the assumption that the property name is the group name, and each entry is an element name. The property generated this way will be a 'text' property:
IProperty propSettings( IProperty::Text, IBase::getDevice(), "camera", IProperty::Idle, IProperty::ReadOnly );
propSettings.add( IElement( "name", "default name", "The camera name" ) );
propSettings.add( IElement( "width", 3000, "The width, in pixels" ) );
propSettings.add( IElement( "height", 3000, "The height, in pixels" ) );
propSettings.add( IElement( "enable", false, "Enable the camera" ) );
IConfig( "camera", propSettings ) >> propSettings;
The class is not "Config" as before, but called "IConfig". You can fill in a property as shown, then use that as the default for reading in an entire property, set in the file as a group. If an entry is not in the file, it will stay as it is in the default.