DroneKit-LA has been designed from the ground-up to provide an extensible framework for supporting different log input and output formats, and for creating tests (analyzers) which are independent of those formats and of each other.
This article provides a brief overview of the main framework classes and some suggestions on the how to write a new analyzer. There is additional information in the Analyzer Class Reference.
Tip
We highly recommend that you check out the existing analyzers for ideas/examples.
The main framework classes are the analyzer base class
(Analyzer
), the results base classes
(Analyzer_Result
, Analyzer_Result_Period
, Analyzer_Result_Event
,
Analyzer_Result_Summary
) and the vehicle/log class which is used to expose log information
to the analyzer (AnalyzerVehicle::Base
).
Analyzers must be derived from Analyzer
and override its virtual methods.
The first two methods set the name and description reported by the analyzer in the output log:
/* Analyzer name. This string is displayed in the test output's
``name`` field for the test. */
virtual const std::string name() const = 0;
/* Analyzer description. This string is displayed in the test
output's ``description`` field. */
virtual const std::string description() const = 0;
The Analyzer::evaluate()
and Analyzer::end_of_log()
methods are called
by the framework for every logged message, to gather relevant information and report the results
(respectively). Some analyzers may choose to report the results only at the end of the
available log data.
/* Evaluate message. This method is called by the analyzer framework for every message in the log.
Implementations should evaluate available data and store anything needed for generating a result. */
virtual void evaluate() { }
/* Generate results. This method is called by the analyzer framework for every message in the log.
Implementations should store results based on the evaluation information generated by the `evaluate()` method */
virtual void end_of_log(uint32_t packet_count UNUSED) { }
The evaluate()
method gets data about the vehicle (log) to analyze
using the base-classes protected _vehicle
member
(an AnalyzerVehicle::Base
, cast to the the actual vehicle type).
The key information from the evaluation is typically saved as part of the new analyzer’s private data.
The new analyzer should also define a results class (an instance of which is usually owned by your Analyzer-derived class). The results class must be derived from one of the base classes listed below:
Analyzer_Result
- Base class for analyzer results.
Analyzer_Result_Period
- Base class for an Analyzer Result which does not span any time.
Analyzer_Result_Event
- Base class for an Analyzer Result which spans a period.
Analyzer_Result_Summary
- Base class for an Analyzer Result which provides information derived over the entire period of data input.
In your end_of_log()
implementation you can populate this results object
using the Analyzer_Result::set_status()
, set_reason()
,
add_evidence()
, add_source()
and
set_severity_score()
or
increase_severity_score()
(severity-score) methods
and then save it to the analyzer using Analyzer::add_result()
.
An example from the Arming Check analyzer is shown below:
result->set_status(analyzer_status_fail);
result->set_reason("Some of the arming checks were disabled when the craft was armed");
result->add_evidence(string_format("Arming flags: %u", result->arming_check()));
result->add_source(_data_sources.get("PARAM"));
result->add_source(_data_sources.get("ARMING"));
...
//Add the result to the Analyzer
add_result(result);
If the analyzer detects multiple issues it can repopulate its result
object and call add_result()
for each issue.
Analyzer
provides a number of other methods that you can optionally use/over ride in your classes.
One example is configure()
, which can be overridden to support
setting failure levels using a configuration file.
The easiest way to start creating a new analyzer is to duplicate an existing analyzer and modify the virtual methods described in the previous section.
Tip
A good one to copy might be the Battery Analyzer (header, source) because it is relatively small and straightforward.
All of the existing analyzers have been implemented in just two files (the header and source). They are placed in the /analyzer/ folder and prefixed with the string analyzer_.
You’ll need to add the source files into the project Makefile:
SRCS_CPP += analyzer/analyzer_your_problem.cpp
You’ll also need to setup the code to instantiate the new analyzer in analyze.cpp. This code usually also sets up any trigger values from the configuration file, as shown:
#include "analyzer/analyzer_your_problem.h"
...
Analyzer_Your_Problem* analyzer_your_problem = new Analyzer_Your_Problem(vehicle,_data_sources);
if (analyzer_your_problem != NULL) {
configure_analyzer(config, analyzer_your_problem);
} else {
syslog(LOG_INFO, "Failed to create analyzer_your_problem");
}
Tip
Not all information your analyzer might need will necessary be available
in the AnalyzerVehicle::Base
class (accessed through analyzer’s _vehicle
member). If required
information is present in the original log but not the analyzer’s vehicle model then you can add it using
the process described in Extending the Vehicle-Log Model.