17 changed files with 646 additions and 10 deletions
@ -0,0 +1,27 @@
@@ -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 @@
@@ -0,0 +1 @@
|
||||
Subproject commit b3df80adf5e9a1ffd3520a699d751acddd07763c |
@ -0,0 +1,37 @@
@@ -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 @@
@@ -1 +1 @@
|
||||
Subproject commit 9e07c7d0b6eb91f0fd84f911dc91e68e865ba7ed |
||||
Subproject commit aef704108f3c1e7520338c34171c28565211a607 |
@ -0,0 +1,23 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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