17 changed files with 646 additions and 10 deletions
@ -0,0 +1,27 @@ |
|||||||
|
/****************************************************************************
|
||||||
|
* |
||||||
|
* (c) 2020 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
|
||||||
|
* |
||||||
|
* QGroundControl is licensed according to the terms in the file |
||||||
|
* COPYING.md in the root of the source code directory. |
||||||
|
* |
||||||
|
****************************************************************************/ |
||||||
|
|
||||||
|
#include "libevents_definitions.h" |
||||||
|
|
||||||
|
#include <QGCLoggingCategory.h> |
||||||
|
|
||||||
|
QGC_LOGGING_CATEGORY(EventsLog, "EventsLog"); |
||||||
|
|
||||||
|
void qgc_events_parser_debug_printf(const char *fmt, ...) { |
||||||
|
char msg[256]; |
||||||
|
va_list argptr; |
||||||
|
va_start(argptr, fmt); |
||||||
|
vsnprintf(msg, sizeof(msg), fmt, argptr); |
||||||
|
va_end(argptr); |
||||||
|
msg[sizeof(msg)-1] = '\0'; |
||||||
|
int len = strlen(msg); |
||||||
|
if (len > 0) msg[len-1] = '\0'; // remove newline
|
||||||
|
qCDebug(EventsLog) << msg; |
||||||
|
} |
||||||
|
|
@ -0,0 +1,37 @@ |
|||||||
|
/****************************************************************************
|
||||||
|
* |
||||||
|
* (c) 2020 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
|
||||||
|
* |
||||||
|
* QGroundControl is licensed according to the terms in the file |
||||||
|
* COPYING.md in the root of the source code directory. |
||||||
|
* |
||||||
|
****************************************************************************/ |
||||||
|
|
||||||
|
/*
|
||||||
|
* This header defines the events::EventType type. |
||||||
|
* |
||||||
|
*/ |
||||||
|
|
||||||
|
#pragma once |
||||||
|
|
||||||
|
#include <cstdlib> |
||||||
|
#include <cstdarg> |
||||||
|
|
||||||
|
#include <QLoggingCategory> |
||||||
|
|
||||||
|
Q_DECLARE_LOGGING_CATEGORY(EventsLog) |
||||||
|
|
||||||
|
void qgc_events_parser_debug_printf(const char *fmt, ...); |
||||||
|
|
||||||
|
//#define LIBEVENTS_PARSER_DEBUG_PRINTF qgc_events_parser_debug_printf
|
||||||
|
#define LIBEVENTS_DEBUG_PRINTF qgc_events_parser_debug_printf |
||||||
|
|
||||||
|
#include "MAVLinkProtocol.h" |
||||||
|
|
||||||
|
namespace events |
||||||
|
{ |
||||||
|
using EventType = mavlink_event_t; |
||||||
|
} // namespace events
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1 +1 @@ |
|||||||
Subproject commit 9e07c7d0b6eb91f0fd84f911dc91e68e865ba7ed |
Subproject commit aef704108f3c1e7520338c34171c28565211a607 |
@ -0,0 +1,23 @@ |
|||||||
|
/****************************************************************************
|
||||||
|
* |
||||||
|
* (c) 2020 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
|
||||||
|
* |
||||||
|
* QGroundControl is licensed according to the terms in the file |
||||||
|
* COPYING.md in the root of the source code directory. |
||||||
|
* |
||||||
|
****************************************************************************/ |
||||||
|
|
||||||
|
#include "CompInfoEvents.h" |
||||||
|
#include "Vehicle.h" |
||||||
|
|
||||||
|
CompInfoEvents::CompInfoEvents(uint8_t compId, Vehicle* vehicle, QObject* parent) |
||||||
|
: CompInfo(COMP_METADATA_TYPE_EVENTS, compId, vehicle, parent) |
||||||
|
{ |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
void CompInfoEvents::setJson(const QString& metadataJsonFileName, const QString& translationJsonFileName) |
||||||
|
{ |
||||||
|
vehicle->setEventsMetadata(compId, metadataJsonFileName, translationJsonFileName); |
||||||
|
} |
||||||
|
|
@ -0,0 +1,31 @@ |
|||||||
|
/****************************************************************************
|
||||||
|
* |
||||||
|
* (c) 2020 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
|
||||||
|
* |
||||||
|
* QGroundControl is licensed according to the terms in the file |
||||||
|
* COPYING.md in the root of the source code directory. |
||||||
|
* |
||||||
|
****************************************************************************/ |
||||||
|
|
||||||
|
#pragma once |
||||||
|
|
||||||
|
#include "CompInfo.h" |
||||||
|
|
||||||
|
#include <QObject> |
||||||
|
|
||||||
|
class FactMetaData; |
||||||
|
class Vehicle; |
||||||
|
class FirmwarePlugin; |
||||||
|
|
||||||
|
class CompInfoEvents : public CompInfo |
||||||
|
{ |
||||||
|
Q_OBJECT |
||||||
|
|
||||||
|
public: |
||||||
|
CompInfoEvents(uint8_t compId, Vehicle* vehicle, QObject* parent = nullptr); |
||||||
|
|
||||||
|
// Overrides from CompInfo
|
||||||
|
void setJson(const QString& metadataJsonFileName, const QString& translationJsonFileName) override; |
||||||
|
|
||||||
|
private: |
||||||
|
}; |
@ -0,0 +1,104 @@ |
|||||||
|
/****************************************************************************
|
||||||
|
* |
||||||
|
* (c) 2020 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
|
||||||
|
* |
||||||
|
* QGroundControl is licensed according to the terms in the file |
||||||
|
* COPYING.md in the root of the source code directory. |
||||||
|
* |
||||||
|
****************************************************************************/ |
||||||
|
|
||||||
|
#include <QSharedPointer> |
||||||
|
|
||||||
|
#include "EventHandler.h" |
||||||
|
#include "QGCLoggingCategory.h" |
||||||
|
|
||||||
|
Q_DECLARE_METATYPE(QSharedPointer<events::parser::ParsedEvent>); |
||||||
|
|
||||||
|
|
||||||
|
EventHandler::EventHandler(QObject* parent, const QString& profile, handle_event_f handleEventCB, |
||||||
|
send_request_event_message_f sendRequestCB, |
||||||
|
uint8_t ourSystemId, uint8_t ourComponentId, uint8_t systemId, uint8_t componentId) |
||||||
|
: QObject(parent), _timer(parent), |
||||||
|
_handleEventCB(handleEventCB), |
||||||
|
_sendRequestCB(sendRequestCB), |
||||||
|
_compid(componentId) |
||||||
|
{ |
||||||
|
auto error_cb = [componentId](int num_events_lost) { |
||||||
|
qCWarning(EventsLog) << "Events got lost:" << num_events_lost << "comp_id:" << componentId; |
||||||
|
}; |
||||||
|
|
||||||
|
auto timeout_cb = [this](int timeout_ms) { |
||||||
|
if (timeout_ms < 0) { |
||||||
|
_timer.stop(); |
||||||
|
} else { |
||||||
|
_timer.setSingleShot(true); |
||||||
|
_timer.start(timeout_ms); |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
_parser.setProfile(profile.toStdString()); |
||||||
|
|
||||||
|
_parser.formatters().url = [](const std::string& content, const std::string& link) { |
||||||
|
return "<a href=\""+link+"\">"+content+"</a>"; }; |
||||||
|
|
||||||
|
events::ReceiveProtocol::Callbacks callbacks{error_cb, _sendRequestCB, |
||||||
|
std::bind(&EventHandler::gotEvent, this, std::placeholders::_1), timeout_cb}; |
||||||
|
_protocol = new events::ReceiveProtocol(callbacks, ourSystemId, ourComponentId, systemId, componentId); |
||||||
|
|
||||||
|
connect(&_timer, &QTimer::timeout, this, [this]() { _protocol->timerEvent(); }); |
||||||
|
|
||||||
|
qRegisterMetaType<QSharedPointer<events::parser::ParsedEvent>>("ParsedEvent"); |
||||||
|
} |
||||||
|
|
||||||
|
EventHandler::~EventHandler() |
||||||
|
{ |
||||||
|
delete _protocol; |
||||||
|
} |
||||||
|
|
||||||
|
void EventHandler::gotEvent(const mavlink_event_t& event) |
||||||
|
{ |
||||||
|
if (!_parser.hasDefinitions()) { |
||||||
|
if (_pendingEvents.size() > 50) { // limit size (not expected to happen)
|
||||||
|
_pendingEvents.clear(); |
||||||
|
} |
||||||
|
qCDebug(EventsLog) << "No metadata, queuing event, ID:" << event.id << "num pending:" << _pendingEvents.size(); |
||||||
|
_pendingEvents.push_back(event); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
std::unique_ptr<events::parser::ParsedEvent> parsed_event = _parser.parse(event); |
||||||
|
if (parsed_event == nullptr) { |
||||||
|
qCWarning(EventsLog) << "Got Event w/o known metadata: ID:" << event.id << "comp id:" << _compid; |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
qCDebug(EventsLog) << "Got Event: ID:" << parsed_event->id() << "namespace:" << parsed_event->eventNamespace().c_str() << |
||||||
|
"name:" << parsed_event->name().c_str() << "msg:" << parsed_event->message().c_str(); |
||||||
|
|
||||||
|
_handleEventCB(std::move(parsed_event)); |
||||||
|
} |
||||||
|
|
||||||
|
void EventHandler::handleEvents(const mavlink_message_t& message) |
||||||
|
{ |
||||||
|
_protocol->processMessage(message); |
||||||
|
} |
||||||
|
|
||||||
|
void EventHandler::setMetadata(const QString &metadataJsonFileName, const QString &translationJsonFileName) |
||||||
|
{ |
||||||
|
auto translate = [](const std::string& s) { |
||||||
|
// TODO: use translation file
|
||||||
|
return s; |
||||||
|
}; |
||||||
|
if (_parser.loadDefinitionsFile(metadataJsonFileName.toStdString(), translate)) { |
||||||
|
if (_parser.hasDefinitions()) { |
||||||
|
// do we have queued events?
|
||||||
|
for (const auto& event : _pendingEvents) { |
||||||
|
gotEvent(event); |
||||||
|
} |
||||||
|
_pendingEvents.clear(); |
||||||
|
} |
||||||
|
} else { |
||||||
|
qCWarning(EventsLog) << "Failed to load events JSON metadata file"; |
||||||
|
} |
||||||
|
} |
||||||
|
|
@ -0,0 +1,53 @@ |
|||||||
|
/****************************************************************************
|
||||||
|
* |
||||||
|
* (c) 2020 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
|
||||||
|
* |
||||||
|
* QGroundControl is licensed according to the terms in the file |
||||||
|
* COPYING.md in the root of the source code directory. |
||||||
|
* |
||||||
|
****************************************************************************/ |
||||||
|
|
||||||
|
#pragma once |
||||||
|
|
||||||
|
#include <QString> |
||||||
|
#include <QVector> |
||||||
|
#include <QTimer> |
||||||
|
|
||||||
|
#include <functional> |
||||||
|
|
||||||
|
#include "HealthAndArmingChecks.h" |
||||||
|
|
||||||
|
#include <libevents/libs/cpp/protocol/receive.h> |
||||||
|
#include <libevents/libs/cpp/parse/parser.h> |
||||||
|
#include <libevents/libs/cpp/generated/events_generated.h> |
||||||
|
|
||||||
|
class EventHandler : public QObject |
||||||
|
{ |
||||||
|
Q_OBJECT |
||||||
|
public: |
||||||
|
using send_request_event_message_f = std::function<void(const mavlink_request_event_t& msg)>; |
||||||
|
using handle_event_f = std::function<void(std::unique_ptr<events::parser::ParsedEvent>)>; |
||||||
|
|
||||||
|
EventHandler(QObject* parent, const QString& profile, handle_event_f handleEventCB, |
||||||
|
send_request_event_message_f sendRequestCB, |
||||||
|
uint8_t ourSystemId, uint8_t ourComponentId, uint8_t systemId, uint8_t componentId); |
||||||
|
~EventHandler(); |
||||||
|
|
||||||
|
|
||||||
|
void handleEvents(const mavlink_message_t& message); |
||||||
|
|
||||||
|
void setMetadata(const QString& metadataJsonFileName, const QString& translationJsonFileName); |
||||||
|
|
||||||
|
HealthAndArmingCheckHandler& healthAndArmingChecks() { return _healthAndArmingChecks; } |
||||||
|
private: |
||||||
|
void gotEvent(const mavlink_event_t& event); |
||||||
|
|
||||||
|
events::ReceiveProtocol* _protocol{nullptr}; |
||||||
|
QTimer _timer; |
||||||
|
events::parser::Parser _parser; |
||||||
|
HealthAndArmingCheckHandler _healthAndArmingChecks; |
||||||
|
QVector<mavlink_event_t> _pendingEvents; ///< stores incoming events until we have the metadata loaded
|
||||||
|
handle_event_f _handleEventCB; |
||||||
|
send_request_event_message_f _sendRequestCB; |
||||||
|
const uint8_t _compid; |
||||||
|
}; |
@ -0,0 +1,125 @@ |
|||||||
|
/****************************************************************************
|
||||||
|
* |
||||||
|
* (c) 2020 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
|
||||||
|
* |
||||||
|
* QGroundControl is licensed according to the terms in the file |
||||||
|
* COPYING.md in the root of the source code directory. |
||||||
|
* |
||||||
|
****************************************************************************/ |
||||||
|
|
||||||
|
#include "HealthAndArmingChecks.h" |
||||||
|
|
||||||
|
#include <QGCLoggingCategory.h> |
||||||
|
|
||||||
|
QGC_LOGGING_CATEGORY(HealthAndArmingChecks, "HealthAndArmingChecks"); |
||||||
|
|
||||||
|
using health_component_t = events::common::enums::health_component_t; |
||||||
|
using navigation_mode_category_t = events::common::enums::navigation_mode_category_t; |
||||||
|
|
||||||
|
void HealthAndArmingCheckHandler::handleEvent(const events::parser::ParsedEvent& event) |
||||||
|
{ |
||||||
|
Type type; |
||||||
|
if (event.eventNamespace() == "common" && event.name() == "arming_check_summary") { |
||||||
|
type = Type::ArmingCheckSummary; |
||||||
|
} else if (event.eventNamespace() == "common" && event.name() == "health_summary") { |
||||||
|
type = Type::HealthSummary; |
||||||
|
} else { |
||||||
|
type = Type::Other; |
||||||
|
} |
||||||
|
|
||||||
|
// the expected order of receiving is:
|
||||||
|
// - ArmingCheckSummary
|
||||||
|
// - N Other
|
||||||
|
// - HealthSummary
|
||||||
|
|
||||||
|
if (type != _expectedEvent) { |
||||||
|
if (_expectedEvent == Type::Other && type == Type::HealthSummary) { |
||||||
|
// all good
|
||||||
|
} else if (type == Type::ArmingCheckSummary) { |
||||||
|
qCDebug(HealthAndArmingChecks) << "Unexpected ArmingCheckSummary event, resetting. Expected:" << (int)_expectedEvent; |
||||||
|
// accept & reset
|
||||||
|
} else { |
||||||
|
qCDebug(HealthAndArmingChecks) << "Unexpected event, resetting. Expected:" << (int)_expectedEvent |
||||||
|
<< "Got:" << (int)type; |
||||||
|
_expectedEvent = Type::ArmingCheckSummary; |
||||||
|
return; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
switch (type) { |
||||||
|
case Type::ArmingCheckSummary: |
||||||
|
reset(); |
||||||
|
if (event.id() == (uint32_t)events::common::event_id_t::arming_check_summary) { |
||||||
|
ArmingCheckSummary &arming = _results[_currentResult].arming; |
||||||
|
events::common::decode_arming_check_summary(event.eventData(), arming.error, arming.warning, arming.canArm); |
||||||
|
_expectedEvent = Type::Other; |
||||||
|
} |
||||||
|
break; |
||||||
|
case Type::Other: { |
||||||
|
Check check; |
||||||
|
check.type = event.group() == "health" ? CheckType::Health : CheckType::ArmingCheck; |
||||||
|
check.message = QString::fromStdString(event.message()); |
||||||
|
check.description = QString::fromStdString(event.description()); |
||||||
|
check.affectedModes = (events::common::enums::navigation_mode_category_t)event.argumentValue(0).value.val_uint8_t; |
||||||
|
check.affectedHealthComponentIndex = event.argumentValue(1).value.val_uint8_t; |
||||||
|
check.logLevel = events::externalLogLevel(event.eventData().log_levels); |
||||||
|
_results[_currentResult].checks.append(check); |
||||||
|
} |
||||||
|
break; |
||||||
|
case Type::HealthSummary: |
||||||
|
if (event.id() == (uint32_t)events::common::event_id_t::health_summary) { |
||||||
|
HealthSummary &health = _results[_currentResult].health; |
||||||
|
events::common::decode_health_summary(event.eventData(), health.isPresent, health.error, health.warning); |
||||||
|
_currentResult = (_currentResult + 1) % 2; |
||||||
|
emit update(); |
||||||
|
testReport(); |
||||||
|
} |
||||||
|
reset(); |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void HealthAndArmingCheckHandler::reset() |
||||||
|
{ |
||||||
|
_results[_currentResult].reset(); |
||||||
|
_expectedEvent = Type::ArmingCheckSummary; |
||||||
|
} |
||||||
|
|
||||||
|
void HealthAndArmingCheckHandler::testReport() |
||||||
|
{ |
||||||
|
// just for testing...
|
||||||
|
// qWarning() << "Got Health/Arming checks update";
|
||||||
|
// qWarning() << "Arming possible in current mode: " << (results().arming.canArm & navigation_mode_category_t::current);
|
||||||
|
// qWarning() << "Can a mission be flown: " << (results().arming.canArm & navigation_mode_category_t::mission);
|
||||||
|
// qWarning() << "Autonomous (e.g. Takeoff) flight possible: " << (results().arming.canArm & navigation_mode_category_t::autonomous);
|
||||||
|
//
|
||||||
|
// QString gps_icon_color;
|
||||||
|
// if ((results().health.error & health_component_t::sensor_gps) ||
|
||||||
|
// (results().health.error & health_component_t::global_position_estimate) ||
|
||||||
|
// (results().arming.error & health_component_t::sensor_gps) ||
|
||||||
|
// (results().arming.error & health_component_t::global_position_estimate)) {
|
||||||
|
// gps_icon_color = "red";
|
||||||
|
// } else if((results().health.warning & health_component_t::sensor_gps) ||
|
||||||
|
// (results().health.warning & health_component_t::global_position_estimate) ||
|
||||||
|
// (results().arming.warning & health_component_t::sensor_gps) ||
|
||||||
|
// (results().arming.warning & health_component_t::global_position_estimate)) {
|
||||||
|
// gps_icon_color = "yellow";
|
||||||
|
// } else {
|
||||||
|
// gps_icon_color = "green";
|
||||||
|
// }
|
||||||
|
// qWarning() << "GPS/Position icon color: " << gps_icon_color;
|
||||||
|
//
|
||||||
|
// // display events that are relevant for current mode:
|
||||||
|
// qWarning() << "Current flight mode:";
|
||||||
|
// for (const auto& check : results().checks) {
|
||||||
|
// if (check.affectedModes & navigation_mode_category_t::current) {
|
||||||
|
// qWarning() << " " << (int)check.logLevel << check.message;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// qWarning() << "Other flight modes:";
|
||||||
|
// for (const auto& check : results().checks) {
|
||||||
|
// if (!(check.affectedModes & navigation_mode_category_t::current)) {
|
||||||
|
// qWarning() << " " << (int)check.logLevel << check.message;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
} |
@ -0,0 +1,84 @@ |
|||||||
|
/****************************************************************************
|
||||||
|
* |
||||||
|
* (c) 2020 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
|
||||||
|
* |
||||||
|
* QGroundControl is licensed according to the terms in the file |
||||||
|
* COPYING.md in the root of the source code directory. |
||||||
|
* |
||||||
|
****************************************************************************/ |
||||||
|
|
||||||
|
#pragma once |
||||||
|
|
||||||
|
#include <QObject> |
||||||
|
#include <QLoggingCategory> |
||||||
|
#include <QVector> |
||||||
|
|
||||||
|
#include <libevents/libs/cpp/parse/parser.h> |
||||||
|
#include <libevents/libs/cpp/generated/events_generated.h> |
||||||
|
|
||||||
|
Q_DECLARE_LOGGING_CATEGORY(HealthAndArmingChecks) |
||||||
|
|
||||||
|
class HealthAndArmingCheckHandler : public QObject |
||||||
|
{ |
||||||
|
Q_OBJECT |
||||||
|
public: |
||||||
|
|
||||||
|
enum class CheckType { |
||||||
|
ArmingCheck, |
||||||
|
Health |
||||||
|
}; |
||||||
|
struct Check { |
||||||
|
CheckType type; |
||||||
|
QString message; |
||||||
|
QString description; |
||||||
|
events::common::enums::navigation_mode_category_t affectedModes; |
||||||
|
uint8_t affectedHealthComponentIndex; ///< index for events::common::enums::health_component_t, can be 0xff
|
||||||
|
events::Log logLevel; |
||||||
|
}; |
||||||
|
|
||||||
|
struct HealthSummary { |
||||||
|
events::common::enums::health_component_t isPresent; |
||||||
|
events::common::enums::health_component_t error; |
||||||
|
events::common::enums::health_component_t warning; |
||||||
|
}; |
||||||
|
struct ArmingCheckSummary { |
||||||
|
events::common::enums::health_component_t error; |
||||||
|
events::common::enums::health_component_t warning; |
||||||
|
events::common::enums::navigation_mode_category_t canArm; |
||||||
|
}; |
||||||
|
|
||||||
|
struct Results { |
||||||
|
HealthSummary health{}; |
||||||
|
ArmingCheckSummary arming{}; |
||||||
|
QVector<Check> checks{}; |
||||||
|
|
||||||
|
void reset() { |
||||||
|
health = {}; |
||||||
|
arming = {}; |
||||||
|
checks.clear(); |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
void handleEvent(const events::parser::ParsedEvent& event); |
||||||
|
|
||||||
|
const Results& results() const { return _results[(_currentResult + 1) % 2]; } |
||||||
|
|
||||||
|
signals: |
||||||
|
void update(); |
||||||
|
private: |
||||||
|
|
||||||
|
enum class Type { |
||||||
|
ArmingCheckSummary, |
||||||
|
Other, |
||||||
|
HealthSummary, |
||||||
|
}; |
||||||
|
|
||||||
|
void reset(); |
||||||
|
|
||||||
|
void testReport(); |
||||||
|
|
||||||
|
Type _expectedEvent{Type::ArmingCheckSummary}; |
||||||
|
Results _results[2]; ///< store the last full set and currently updating one
|
||||||
|
int _currentResult{0}; ///< index for the currently updating/adding results
|
||||||
|
}; |
||||||
|
|
Loading…
Reference in new issue