2 ******************************************************************************
4 * @file systemhealthgadgetwidget.cpp
5 * @author OpenPilot Team & Edouard Lafargue Copyright (C) 2012.
6 * @addtogroup GCSPlugins GCS Plugins
8 * @addtogroup SystemHealthPlugin System Health Plugin
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
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>
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();
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."));
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)) {
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);
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;
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
);
159 matrix
.translate(rectProjected
.x(), rectProjected
.y());
160 ind
->setTransform(matrix
, false);
162 if (value
.compare("Uninitialised") != 0) {
163 missingElements
->append(element2
);
164 qDebug() << "Warning: element " << element2
<< " not found in SVG.";
169 missingElements
->append(element
);
170 qDebug() << "Warning: Element " << element
<< " not found in SVG.";
177 SystemHealthGadgetWidget::~SystemHealthGadgetWidget()
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()) {
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);
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")));
231 } else { qDebug() << "SystemHealthGadget: no file"; }
234 void SystemHealthGadgetWidget::paint()
236 QGraphicsScene
*l_scene
= scene();
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
);
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";
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
)
263 fitInView(background
, Qt::KeepAspectRatio
);
266 void SystemHealthGadgetWidget::mousePressEvent(QMouseEvent
*event
)
268 QGraphicsScene
*graphicsScene
= scene();
271 QPoint point
= event
->pos();
272 bool haveAlarmItem
= false;
273 foreach(QGraphicsItem
* sceneItem
, items(point
)) {
274 QGraphicsSvgItem
*clickedItem
= dynamic_cast<QGraphicsSvgItem
*>(sceneItem
);
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());
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();
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
);