BatteryWidget.cpp
Go to the documentation of this file.
1#include "BatteryWidget.h"
2
3#include <QToolTip>
4
5namespace armarx
6{
7
8 constexpr std::string_view battery_master_default_name = "DiagnosticsUnit";
9
10 BatteryIcon::BatteryIcon(QWidget* parent) : QWidget(parent)
11 {
12 QSizePolicy policy{QSizePolicy::Policy::Fixed, QSizePolicy::Policy::Fixed};
13 setSizePolicy(policy);
14 }
15
16 void
17 BatteryIcon::paintEvent(QPaintEvent* event)
18 {
19 QWidget::paintEvent(event);
20
21 int filled = std::clamp(percentage, 0, 100); // between 0 and 100
22 QColor fg(0, 200, 0);
23
24 if (filled < 15)
25 {
26 fg = QColor(200, 0, 0);
27 }
28 else if (filled < 45)
29 {
30 fg = QColor(200, 100, 0);
31 }
32
33 if (state != dto::BatteryState::discharging)
34 {
35 fg = QColor(255, 255, 255);
36 }
37
38 // Draw inner and cap.
39 QColor bg(fg.red() * .6, fg.green() * .6, fg.blue() * .6);
40 int ht = event->rect().height();
41 int wd = ht / 2 - 1;
42 QPainter painter;
43 // painter.setRenderHint(QPainter::Antialiasing, true);
44 painter.begin(this);
45 int m = 1 * ht / 10 + 1;
46 QRect cap(QPoint(m, m), QPoint(wd - m, 2 * m));
47 painter.fillRect(cap, filled == 100 ? fg : bg);
48 QRect body(QPoint(0, 2 * m), QPoint(wd, ht - m));
49 painter.fillRect(body, bg);
50 float dist = (1.0 - filled * .01);
51 QRect bodyFull(QPoint(0, 2 * m + dist * (ht - 3 * m)), QPoint(wd, ht - m));
52 painter.fillRect(bodyFull, fg);
53
54 // Draw a border.
55 int lWd = ht / 40;
56 QRect a;
57 a = QRect(QPoint(m, m), QPoint(wd - m, m + lWd));
58 painter.fillRect(a, bg);
59 a = QRect(QPoint(wd - m - lWd, m), QPoint(wd - m, 2 * m + lWd));
60 painter.fillRect(a, bg);
61 a = QRect(QPoint(wd - m - lWd, 2 * m), QPoint(wd, 2 * m + lWd));
62 painter.fillRect(a, bg);
63 a = QRect(QPoint(wd - lWd, 2 * m), QPoint(wd, ht - m));
64 painter.fillRect(a, bg);
65 a = QRect(QPoint(0, ht - m - lWd), QPoint(wd, ht - m));
66 painter.fillRect(a, bg);
67 a = QRect(QPoint(0, 2 * m), QPoint(lWd, ht - m));
68 painter.fillRect(a, bg);
69 a = QRect(QPoint(0, 2 * m), QPoint(m + lWd, 2 * m + lWd));
70 painter.fillRect(a, bg);
71 a = QRect(QPoint(m, m), QPoint(m + lWd, 2 * m + lWd));
72 painter.fillRect(a, bg);
73
74 // Draw lightning if charging.
75 if (state == dto::BatteryState::charging)
76 {
77 QPolygon poly;
78 float lHeight = .6 * ht;
79 int xOff = .1 * ht + 1;
80 int yOff = .2 * ht + 1;
81 poly << QPoint(xOff + 7 * lHeight / 20, yOff)
82 << QPoint(xOff + 5 * lHeight / 20, yOff + 4.5 * lHeight / 10)
83 << QPoint(xOff + 10 * lHeight / 20, yOff + 4.5 * lHeight / 10)
84 << QPoint(xOff + 3 * lHeight / 20, yOff + lHeight)
85 << QPoint(xOff + 5 * lHeight / 20, yOff + 5.5 * lHeight / 10)
86 << QPoint(xOff + 0 * lHeight / 20, yOff + 5.5 * lHeight / 10)
87 << QPoint(xOff + 7 * lHeight / 20, yOff);
88 QPainterPath path;
89 path.addPolygon(poly);
90 painter.fillPath(path, QColor(40, 40, 40));
91 }
92
93 // Draw cross if unavailable.
94 if (state == dto::BatteryState::unavailable)
95 {
96 int u = .1 * wd;
97 QPolygon poly;
98 poly << QPoint(u, .1 * ht) << QPoint(wd - 2 * u, .9 * ht) << QPoint(wd - u, .9 * ht)
99 << QPoint(2 * u, .1 * ht) << QPoint(u, .1 * ht);
100 QPainterPath path;
101 path.addPolygon(poly);
102 painter.fillPath(path, QColor(200, 0, 0));
103 QPolygon poly2;
104 poly2 << QPoint(wd - 2 * u, .1 * ht) << QPoint(wd - u, .1 * ht)
105 << QPoint(2 * u, .9 * ht) << QPoint(u, .9 * ht) << QPoint(wd - 2 * u, .1 * ht);
106 QPainterPath path2;
107 path2.addPolygon(poly2);
108 painter.fillPath(path2, QColor(200, 0, 0));
109 }
110
111 painter.end();
112 }
113
114 void
116 {
117 this->percentage = std::clamp(percentage, 0, 100);
118 }
119
120 void
121 BatteryIcon::setState(dto::BatteryState state)
122 {
123 this->state = state;
124 }
125
126 QSize
128 {
129 return QSize{16, 32};
130 }
131
132 BatteryWidget::BatteryWidget(QWidget* parent, ArmarXMainWindow* mainWindow) :
133 mainWindow(mainWindow), timer(new QTimer(this))
134 {
135 qRegisterMetaType<dto::BatteryStatus>("dto::BatteryStatus");
136 QGridLayout* layout = new QGridLayout(this->getWidget());
137 this->getWidget()->setContentsMargins(4, 0, 0, 0);
138 icon = new BatteryIcon();
139 layout->addWidget(icon, 0, 0);
140 layout->setContentsMargins(0, 0, 0, 0);
141 this->getWidget()->setLayout(layout);
142 icon->setVisible(true);
143 lbl = new QLabel();
144 txtPercentage = QString("%1%").arg(lastStatus.energyFromFullCharge_pct);
145 lbl->setText(txtPercentage);
146 lbl->setVisible(false);
147 layout->addWidget(lbl, 0, 1);
148
149 qRegisterMetaType<dto::BatteryState>("BatteryState");
150
151 timer->setInterval(std::chrono::milliseconds(1'000));
152 connect(timer, &QTimer::timeout, this, &BatteryWidget::updateBatteryStatus);
153 connect(this, &BatteryWidget::startPeriodicStateUpdate, timer, qOverload<>(&QTimer::start));
154 connect(this, &BatteryWidget::stopPeriodicStateUpdate, timer, &QTimer::stop);
155
156 QString style = QString("QToolTip {"
157 // StyleSheet for tool tip
158 " background: white;"
159 " padding: 3px;"
160 "}");
161 getWidget()->setStyleSheet(style);
162
164 }
165
166 QWidget*
168 {
169 return this->getWidget();
170 }
171
172 void
174 {
175 dto::BatteryState state = lastStatus.state;
176
177 QString text;
178
179 if (state == dto::BatteryState::unavailable)
180 {
181 text = "No battery detected.";
182 }
183 else if (state == dto::BatteryState::full)
184 {
185 text = "Battery fully charged.";
186 }
187 else
188 {
189 QString status;
190 switch (state)
191 {
192 case dto::BatteryState::charging:
193 status = "charging";
194 break;
195 case dto::BatteryState::discharging:
196 status = "discharging";
197 break;
198 case dto::BatteryState::full:
199 status = "full";
200 break;
201 case dto::BatteryState::notCharging:
202 status = "not charging";
203 break;
204 case dto::BatteryState::unavailable:
205 status = "unavailable";
206 break;
207 }
208
209 QString detailedReport;
210 {
211 QString charge = QString("&nbsp;- Charge: %1<br>").arg(txtPercentage);
212
213 QString energy = QString("&nbsp;- Energy: %2 Wh / %3 Wh<br>")
214 .arg(lastStatus.energy_Wh, 0, 'f', 1)
215 .arg(lastStatus.fullChargeEnergy_Wh, 0, 'f', 1);
216
217 QString power = QString("&nbsp;- %1: %2 W (%3 A, %4 V)<br>")
218 .arg(state == dto::BatteryState::charging ? "Charging power"
219 : "Provided power")
220 .arg(std::abs(lastStatus.power_W), 0, 'f', 1)
221 .arg(std::abs(lastStatus.current_A), 0, 'f', 1)
222 .arg(lastStatus.voltage_V, 0, 'f', 1);
223
224 bool showPrognosis =
225 state == dto::BatteryState::charging or state == dto::BatteryState::discharging;
226 QString prognosis = "";
227 if (showPrognosis)
228 {
229 QString fullOrEmpty = state == dto::BatteryState::charging ? "Full" : "Empty";
230 prognosis = QString("&nbsp;- %1 in: &asymp; %2 h <br>")
231 .arg(fullOrEmpty)
232 .arg(lastStatus.remainingTime_h, 0, 'f', 1);
233 }
234
235 QString temperature = QString("&nbsp;- Temperature: %6 °C<br>")
236 .arg(lastStatus.temperature_degC, 0, 'f', 1);
237
238 QString health = QString("&nbsp;- Health: %7%")
239 .arg(lastStatus.fullEnergyFromDesignEnergy_pct, 0, 'f', 1);
240
241 detailedReport = QString("%1%2%3%4%5%6")
242 .arg(charge, energy, power, prognosis, temperature, health);
243 }
244
245 QString errorsText =
246 lastStatus.hasError
247 ? "<br><b><font color='red'>Battery error occured! Check the logs.</font></b>"
248 : "";
249
250 text = QString("Battery %1.<br>%2%3").arg(status).arg(detailedReport).arg(errorsText);
251 }
252
253 getWidget()->setToolTip(text);
254
255 if (not QToolTip::isVisible())
256 {
257 return;
258 }
259
260 QPoint p = getWidget()->mapFromGlobal(QCursor::pos());
261
262 if (p.x() >= 0 and p.y() >= 0 and p.x() < getWidget()->width() and
263 p.y() < getWidget()->height())
264 {
265 QToolTip::showText(QCursor::pos(), text);
266 }
267 }
268
269 void
274
275 void
277 {
278 QMetaObject::invokeMethod(icon, "setVisible", Qt::QueuedConnection, Q_ARG(bool, true));
279
280 ARMARX_INFO << "Battery widget connected.";
282 std::string{battery_master_default_name});
283
285 }
286
287 void
289 {
291 << "Battery widget disconnected. This is expected if the earlier connected "
292 "robot unit shut down.";
293
294 QMetaObject::invokeMethod(this,
295 "setBatteryStatus",
296 Qt::QueuedConnection,
297 Q_ARG(dto::BatteryStatus, invalidStatus));
298
300 }
301
302 void
303 BatteryWidget::loadSettings(QSettings* settings)
304 {
305 ;
306 }
307
308 void
309 BatteryWidget::saveSettings(QSettings* settings)
310 {
311 ;
312 }
313
314 void
316 {
317 dto::BatteryStatus newStatus;
318
319 try
320 {
321 newStatus = batteryManagement->getBatteryStatus();
322 }
323 catch (...)
324 {
325 ARMARX_DEBUG << deactivateSpam(60) << "Failed to get current battery percentage";
326 newStatus.state = dto::BatteryState::unavailable;
327 }
328
329 QMetaObject::invokeMethod(
330 this, "setBatteryStatus", Qt::QueuedConnection, Q_ARG(dto::BatteryStatus, newStatus));
331 }
332
333 void
334 BatteryWidget::setBatteryStatus(dto::BatteryStatus batteryStatus)
335 {
336 lastStatus = batteryStatus;
337
338 icon->setPercentage(lastStatus.energyFromFullCharge_pct);
339 txtPercentage = QString("%1%").arg(lastStatus.energyFromFullCharge_pct, 0, 'f', 1);
340 lbl->setText(txtPercentage);
341
342 dto::BatteryState state = lastStatus.state;
343
344 icon->setState(state);
345 if (state == dto::BatteryState::unavailable)
346 {
347 lbl->setVisible(false);
348 }
349 else
350 {
351 lbl->setVisible(true);
352 }
353
355 icon->update();
356 }
357
358 std::string
360 {
361 return "BatteryWidget" + iceNameUUID;
362 }
363
364} // namespace armarx
SpamFilterDataPtr deactivateSpam(SpamFilterDataPtr const &spamFilter, float deactivationDurationSec, const std::string &identifier, bool deactivate)
Definition Logging.cpp:75
The ArmarXMainWindow class.
virtual QPointer< QWidget > getWidget()
getWidget returns a pointer to the a widget of this controller.
void paintEvent(QPaintEvent *event) Q_DECL_OVERRIDE
QSize sizeHint() const Q_DECL_OVERRIDE
void setPercentage(int percentage)
void setState(dto::BatteryState state)
BatteryIcon(QWidget *parent=0)
void onInitComponent() override
Pure virtual hook for the subclass.
BatteryWidget(QWidget *parent=0, ArmarXMainWindow *mainWindow=0)
void setBatteryStatus(dto::BatteryStatus batteryStatus)
void onDisconnectComponent() override
Hook for subclass.
void loadSettings(QSettings *settings) override
Implement to load the settings that are part of the GUI configuration.
void saveSettings(QSettings *settings) override
Implement to save the settings as part of the GUI configuration.
void onConnectComponent() override
Pure virtual hook for the subclass.
std::string getDefaultName() const override
Retrieve default name of component.
bool usingProxy(const std::string &name, const std::string &endpoints="")
Registers a proxy for retrieval after initialization and adds it to the dependency list.
Ice::ObjectPrx getProxy(long timeoutMs=0, bool waitForScheduler=true) const
Returns the proxy of this object (optionally it waits for the proxy)
#define ARMARX_INFO
The normal logging level.
Definition Logging.h:181
#define ARMARX_IMPORTANT
The logging level for always important information, but expected behaviour (in contrast to ARMARX_WAR...
Definition Logging.h:190
#define ARMARX_DEBUG
The logging level for output that is only interesting while debugging.
Definition Logging.h:184
This file offers overloads of toIce() and fromIce() functions for STL container types.
constexpr std::string_view battery_master_default_name