Merged in f5soh/librepilot/update_credits (pull request #529)
[librepilot.git] / ground / gcs / src / plugins / systemhealth / systemhealthgadgetwidget.cpp
blob779dfd2b493e52bddafb014eddc5d3d39145505d
1 /**
2 ******************************************************************************
4 * @file systemhealthgadgetwidget.cpp
5 * @author OpenPilot Team & Edouard Lafargue Copyright (C) 2012.
6 * @addtogroup GCSPlugins GCS Plugins
7 * @{
8 * @addtogroup SystemHealthPlugin System Health Plugin
9 * @{
10 * @brief The System Health gadget plugin
11 *****************************************************************************/
13 * This program is free software; you can redistribute it and/or modify
14 * it under the terms of the GNU General Public License as published by
15 * the Free Software Foundation; either version 3 of the License, or
16 * (at your option) any later version.
18 * This program is distributed in the hope that it will be useful, but
19 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
20 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
21 * for more details.
23 * You should have received a copy of the GNU General Public License along
24 * with this program; if not, write to the Free Software Foundation, Inc.,
25 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
28 #include "systemalarms.h"
29 #include "systemhealthgadgetwidget.h"
31 #include "utils/stylehelper.h"
32 #include "extensionsystem/pluginmanager.h"
33 #include "uavobjectmanager.h"
34 #include <uavtalk/telemetrymanager.h>
36 #include <QDebug>
37 #include <QWhatsThis>
40 * Initialize the widget
42 SystemHealthGadgetWidget::SystemHealthGadgetWidget(QWidget *parent) : QGraphicsView(parent)
44 setMinimumSize(128, 128);
45 setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
46 setScene(new QGraphicsScene(this));
49 m_renderer = new QSvgRenderer();
50 background = new QGraphicsSvgItem();
51 foreground = new QGraphicsSvgItem();
52 nolink = new QGraphicsSvgItem();
53 logreplay = new QGraphicsSvgItem();
54 logreplay2 = new QGraphicsSvgItem();
55 missingElements = new QStringList();
56 paint();
58 // Now connect the widget to the SystemAlarms UAVObject
59 ExtensionSystem::PluginManager *pm = ExtensionSystem::PluginManager::instance();
60 UAVObjectManager *objManager = pm->getObject<UAVObjectManager>();
62 SystemAlarms *obj = dynamic_cast<SystemAlarms *>(objManager->getObject(QString("SystemAlarms")));
63 connect(obj, SIGNAL(objectUpdated(UAVObject *)), this, SLOT(updateAlarms(UAVObject *)));
65 // Listen to autopilot connection events
66 TelemetryManager *telMngr = pm->getObject<TelemetryManager>();
67 connect(telMngr, SIGNAL(connected()), this, SLOT(onAutopilotConnect()));
68 connect(telMngr, SIGNAL(disconnected()), this, SLOT(onAutopilotDisconnect()));
69 connect(telMngr, SIGNAL(telemetryUpdated(double, double)), this, SLOT(onTelemetryUpdated(double, double)));
71 setToolTip(tr("Displays flight system errors. Click on an alarm for more information."));
74 /**
75 * Hide/Show the "Log Replay" overlay
77 void SystemHealthGadgetWidget::onTelemetryUpdated(double txRate, double rxRate)
79 // Return if a real board is connected or log file end (no telemetry)
80 if (boardConnected || ((txRate + rxRate) == 0)) {
81 return;
84 logreplayDelay++;
85 // With real board not connected, display Logreplay after a little delay
86 // and avoid Logreplay display when real board is connected.
87 if (logreplayDelay > 3) {
88 // Blink sequence, tell user Log replay runs.
89 if ((logreplayDelay % 2) == 0) {
90 logreplay->setVisible(true);
91 logreplay2->setVisible(false);
92 } else {
93 logreplay->setVisible(false);
94 logreplay2->setVisible(true);
96 nolink->setVisible(false);
101 * Hide the "No Link" overlay
103 void SystemHealthGadgetWidget::onAutopilotConnect()
105 nolink->setVisible(false);
106 logreplay->setVisible(false);
107 logreplay2->setVisible(false);
108 boardConnected = true;
112 * Show the "No Link" overlay
114 void SystemHealthGadgetWidget::onAutopilotDisconnect()
116 nolink->setVisible(true);
117 logreplay->setVisible(false);
118 logreplay2->setVisible(false);
119 boardConnected = false;
120 logreplayDelay = 0;
123 void SystemHealthGadgetWidget::updateAlarms(UAVObject *systemAlarm)
125 // This code does not know anything about alarms beforehand, and
126 // I found no efficient way to locate items inside the scene by
127 // name, so it's just as simple to reset the scene:
128 // And add the one with the right name.
129 QGraphicsScene *m_scene = scene();
131 foreach(QGraphicsItem * item, background->childItems()) {
132 m_scene->removeItem(item);
133 delete item; // removeItem does _not_ delete the item.
136 QMatrix backgroundMatrix = (m_renderer->matrixForElement(background->elementId())).inverted();
138 QString alarm = systemAlarm->getName();
139 foreach(UAVObjectField * field, systemAlarm->getFields()) {
140 for (uint i = 0; i < field->getNumElements(); ++i) {
141 QString element = field->getElementNames()[i];
142 QString value = field->getValue(i).toString();
143 if (!missingElements->contains(element)) {
144 if (m_renderer->elementExists(element)) {
145 QString element2 = element + "-" + value;
146 if (!missingElements->contains(element2)) {
147 if (m_renderer->elementExists(element2)) {
148 // element2 is in global coordinates
149 // transform its matrix into the coordinates of background
150 QMatrix blockMatrix = backgroundMatrix * m_renderer->matrixForElement(element2);
151 // use this composed projection to get the position in background coordinates
152 QRectF rectProjected = blockMatrix.mapRect(m_renderer->boundsOnElement(element2));
154 QGraphicsSvgItem *ind = new QGraphicsSvgItem();
155 ind->setSharedRenderer(m_renderer);
156 ind->setElementId(element2);
157 ind->setParentItem(background);
158 QTransform matrix;
159 matrix.translate(rectProjected.x(), rectProjected.y());
160 ind->setTransform(matrix, false);
161 } else {
162 if (value.compare("Uninitialised") != 0) {
163 missingElements->append(element2);
164 qDebug() << "Warning: element " << element2 << " not found in SVG.";
168 } else {
169 missingElements->append(element);
170 qDebug() << "Warning: Element " << element << " not found in SVG.";
177 SystemHealthGadgetWidget::~SystemHealthGadgetWidget()
179 // Do nothing
183 void SystemHealthGadgetWidget::setSystemFile(QString dfn)
185 // Clear the list of elements not found on svg
186 missingElements->clear();
187 setBackgroundBrush(QBrush(Utils::StyleHelper::baseColor()));
188 if (QFile::exists(dfn)) {
189 m_renderer->load(dfn);
190 if (m_renderer->isValid()) {
191 fgenabled = false;
192 background->setSharedRenderer(m_renderer);
193 background->setElementId("background");
195 if (m_renderer->elementExists("foreground")) {
196 foreground->setSharedRenderer(m_renderer);
197 foreground->setElementId("foreground");
198 foreground->setZValue(99);
199 fgenabled = true;
201 if (m_renderer->elementExists("logreplay")) {
202 logreplay->setSharedRenderer(m_renderer);
203 logreplay->setElementId("logreplay");
204 logreplay->setZValue(100);
206 if (m_renderer->elementExists("logreplay2")) {
207 logreplay2->setSharedRenderer(m_renderer);
208 logreplay2->setElementId("logreplay2");
209 logreplay2->setZValue(100);
211 if (m_renderer->elementExists("nolink")) {
212 nolink->setSharedRenderer(m_renderer);
213 nolink->setElementId("nolink");
214 nolink->setZValue(101);
217 QGraphicsScene *l_scene = scene();
218 l_scene->setSceneRect(background->boundingRect());
219 fitInView(background, Qt::KeepAspectRatio);
221 // Check whether the autopilot is connected already, by the way:
222 ExtensionSystem::PluginManager *pm = ExtensionSystem::PluginManager::instance();
223 UAVObjectManager *objManager = pm->getObject<UAVObjectManager>();
224 TelemetryManager *telMngr = pm->getObject<TelemetryManager>();
225 if (telMngr->isConnected()) {
226 onAutopilotConnect();
227 SystemAlarms *obj = dynamic_cast<SystemAlarms *>(objManager->getObject(QString("SystemAlarms")));
228 updateAlarms(obj);
231 } else { qDebug() << "SystemHealthGadget: no file"; }
234 void SystemHealthGadgetWidget::paint()
236 QGraphicsScene *l_scene = scene();
238 l_scene->clear();
239 l_scene->addItem(background);
240 l_scene->addItem(foreground);
241 l_scene->addItem(logreplay);
242 l_scene->addItem(logreplay2);
243 l_scene->addItem(nolink);
244 update();
247 void SystemHealthGadgetWidget::paintEvent(QPaintEvent *event)
249 // Skip painting until the dial file is loaded
250 if (!m_renderer->isValid()) {
251 qDebug() << "SystemHealthGadget: System file not loaded, not rendering";
252 return;
254 QGraphicsView::paintEvent(event);
257 // This event enables the dial to be dynamically resized
258 // whenever the gadget is resized, taking advantage of the vector
259 // nature of SVG dials.
260 void SystemHealthGadgetWidget::resizeEvent(QResizeEvent *event)
262 Q_UNUSED(event);
263 fitInView(background, Qt::KeepAspectRatio);
266 void SystemHealthGadgetWidget::mousePressEvent(QMouseEvent *event)
268 QGraphicsScene *graphicsScene = scene();
270 if (graphicsScene) {
271 QPoint point = event->pos();
272 bool haveAlarmItem = false;
273 foreach(QGraphicsItem * sceneItem, items(point)) {
274 QGraphicsSvgItem *clickedItem = dynamic_cast<QGraphicsSvgItem *>(sceneItem);
276 if (clickedItem) {
277 if ((clickedItem != foreground) && (clickedItem != background)) {
278 // Clicked an actual alarm. We need to set haveAlarmItem to true
279 // as two of the items in this loop will always be foreground and
280 // background. Without this flag, at some point in the loop we
281 // would always call showAllAlarmDescriptions...
282 haveAlarmItem = true;
283 QString itemId = clickedItem->elementId();
284 if (itemId.contains("OK")) {
285 // No alarm set for this item
286 showAlarmDescriptionForItemId("AlarmOK", event->globalPos());
287 } else {
288 // Warning, error or critical alarm
289 showAlarmDescriptionForItemId(itemId, event->globalPos());
291 } else if (!haveAlarmItem) {
292 // Clicked foreground or background
293 showAllAlarmDescriptions(event->globalPos());
300 void SystemHealthGadgetWidget::showAlarmDescriptionForItemId(const QString itemId, const QPoint & location)
302 QFile alarmDescription(":/systemhealth/html/" + itemId + ".html");
304 if (alarmDescription.open(QIODevice::ReadOnly | QIODevice::Text)) {
305 QTextStream textStream(&alarmDescription);
306 textStream.setCodec("UTF-8");
307 QWhatsThis::showText(location, textStream.readAll());
311 void SystemHealthGadgetWidget::showAllAlarmDescriptions(const QPoint & location)
313 QGraphicsScene *graphicsScene = scene();
315 if (graphicsScene) {
316 QString alarmsText;
318 // Loop through all items in the scene looking for svg items that represent alarms
319 foreach(QGraphicsItem * curItem, graphicsScene->items()) {
320 QGraphicsSvgItem *curSvgItem = dynamic_cast<QGraphicsSvgItem *>(curItem);
322 if (curSvgItem && (curSvgItem != foreground) && (curSvgItem != background)) {
323 QString elementId = curSvgItem->elementId();
324 if (!elementId.contains("OK")) {
325 // Found an alarm, get its corresponding alarm html file contents
326 // and append to the cumulative string for all alarms.
327 QFile alarmDescription(":/systemhealth/html/" + elementId + ".html");
328 if (alarmDescription.open(QIODevice::ReadOnly | QIODevice::Text)) {
329 QTextStream textStream(&alarmDescription);
330 textStream.setCodec("UTF-8");
331 alarmsText.append(textStream.readAll());
337 // Show alarms text if we have any
338 if (alarmsText.length() > 0) {
339 QWhatsThis::showText(location, alarmsText);