When implementing a Plugin, we recommend being able to switch between different implementations or alter values in runtime. This may be helpful when making decisions such as which algorithm to use or which value for a constant to use.
For example, there may be cases where some parameters of the video analytics algorithm need to be fine-tuned for testing the Plugin on real data; or there may be two obvious ways to write a certain performance-critical piece of code. In these cases, programmers would just hard-code the values in the code (best case, make named constants), and just comment-out alternative algorithms (best case, via conditional compilation).
For a better approach, nx_kit offers IniConfig which has several advantages:
- The code looks as simple as when using the traditional techniques.
- All branches (algorithm versions) are compiled unconditionally (as opposed to commenting-out).
- Otherwise, the commented-out parts of the code are prone to becoming outdated after a future refactoring.
- The intention that some value or decision is configurable is clearly expressed in the code.
- The choices can be made at runtime by changing the options in the .ini file and restarting the application. Sometimes the application reloads certain options without a restart and without having to recompile, allowing non-developers (testers or field engineers) to experiment with them.
IniConfig is not intended for end users. So, if any parameter needs to be changed after deploying in production, implement this parameter through Plugin settings.
See the full description of the IniConfig mechanism in the Doxygen documentation for nx/kit/ini_config.h.
Code Example
Let's imagine the following initial piece of code, written using the traditional techniques:
#define USE_FAST_DETECTION
const double detectionAccuracy = 0.8;
void executeDetection(Detector* detector)
{
#if defined(USE_FAST_DETECTION)
detector->execute(detectionAccuracy, Mode::fast);
#else
detector->execute(Mode::precise);
#endif
}
Now let's improve this code — instead of introducing macros and named constants, let's make a struct with the flags and constants as fields, and assign them some reasonable defaults. Let’s call them options.
We will make a single instance of this struct, and use its fields in the code where a constant would be used. When a choice between different code fragments needs to be made, we use a boolean option, and a simple if in the code. At first, it may look like this:
struct Ini
{
const double detectionAccuracy = 0.8;
const bool useFastDetection = true;
}
const Ini ini;
void executeDetection(Detector* detector)
{
if (useFastDetection)
detector->execute(ini.detectionAccuracy, Mode::fast);
else
detector->execute(Mode::precise);
}
And now let's further improve this code by using the IniConfig mechanism: we will inherit such a structure from nx::kit::IniConfig, and use the macros provided in nx/kit/ini_config.h to declare its fields (note the new code in bold):
#include <nx/kit/ini_config.h>
struct Ini: nx::kit::IniConfig
{
Ini(): IniConfig("my_module.ini") { reload(); }
NX_INI_FLAG(true, useFastDetection, Try the fast detection algorithm.");
NX_INI_FLOAT(0.8, detectionAccuracy, "Detection accuracy.");
}
static Ini& ini()
{
static Ini ini;
return ini;
}
void executeDetection(Detector* detector)
{
if (ini().useFastDetection)
detector->execute(ini().detectionAccuracy, Mode::fast);
else
detector->execute(Mode::precise);
}
If the struct with options is needed in more than one translation unit (.cpp file), put it into a header and move the ini() function definition to a .cpp file.
How it works
When using the IniConfig mechanism, the following magic will take place automatically, without any additional code:
- When the ini() function is first called, IniConfig checks whether the .ini file with the name supplied to the IniConfig constructor exists in the specifically determined directory for the current process.
- See the exact algorithm for locating .ini files directory in the Doxygen documentation for nx/kit/ini_config.h.
- Typically, such a directory is located as follows:
- On Windows, when started as a service:
C:\Windows\System32\config\systemprofile\AppData\Local\nx_ini\
- On Windows, when started manually under a user:
C:\Users\<user>\AppData\Local\nx_ini\
- On Linux, when started manually under a non-root user:
$HOME/.config/nx_ini/
- On Linux, when started under root (e.g. as a service):
/etc/nx_ini/
- On Windows, when started as a service:
- For Nx Witness Server, there is a REST API call that shows its .ini files directory:
GET /api/iniConfig
. To call it, run the Server, and open the following URL in a web browser: http://localhost:7001/api/iniConfig/
- If the .ini file exists, it is being parsed, and the options are set to the values specified in the file.
- If the .ini file exists but is empty, the file is filled with the default values and the option descriptions. This is the recommended way to initially create such files.
- If the .ini file does not exist, the program works with the default option values. Thus, the only performance overhead will be checking whether a file exists, and this check will usually be made once per run.
- If you want to re-check at certain places in the code whether the .ini file has appeared or was changed, simply call
ini().reload()
. In simple cases, call it just once in the struct Ini constructor, as shown in the example above.
- If you want to re-check at certain places in the code whether the .ini file has appeared or was changed, simply call
When a .ini file is found missing or is (re)loaded, its full path and option values are printed to stderr for convenience.
For the example above, the auto-generated .ini file will look like this (note the parts in bold):
# Accuracy for the fast detection algorithm, 0..1. Default: 0.8
#detectionAccuracy=0.8
# Use the fast algorithm instead of the precise one. Default: 0
#useFastDetection=0
To change a value, do not forget to uncomment (remove the leading #) its line:
#useFastDetection=0
becomes
useFastDetection=1
Note: When the code defining the options and their values is updated, the contents of auto-generated .ini files may become outdated. Currently, there is no auto-update mechanism for this case, so special care must be taken if the old .ini files remain in the .ini files directory.
It may help that the files are auto-generated with values being commented out — at least when the defaults change in the code, the new values will be used instead of loading the previous defaults from the file.
Comments
0 comments
Article is closed for comments.