1 /*
2 KSysGuard, the KDE System Guard
3
4 Copyright (c) 2001 Tobias Koenig <tokoe@kde.org>
5
6 This program is free software; you can redistribute it and/or
7 modify it under the terms of the GNU General Public
8 License as published by the Free Software Foundation; either
9 version 2 of the License, or (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19
20 */
21
22 #include <QApplication>
23 #include <QAbstractTableModel>
24 #include <QDate>
25 #include <QFile>
26 #include <QTextStream>
27 #include <QContextMenuEvent>
28 #include <QHeaderView>
29 #include <QMenu>
30 #include <QHBoxLayout>
31 #include <QDomNodeList>
32 #include <QDomDocument>
33 #include <QDomElement>
34
35 #include <KIconLoader>
36 #include <KLocalizedString>
37 #include <KNotification>
38 #include <ksgrd/SensorManager.h>
39 #include "StyleEngine.h"
40
41 #include "SensorLoggerDlg.h"
42 #include "SensorLoggerSettings.h"
43 #include "SensorLogger.h"
44
45 #define NONE -1
46
LogSensorView(QWidget * parent)47 LogSensorView::LogSensorView( QWidget *parent )
48 : QTreeView( parent )
49 {
50 }
51
contextMenuEvent(QContextMenuEvent * event)52 void LogSensorView::contextMenuEvent( QContextMenuEvent *event )
53 {
54 const QModelIndex index = indexAt( event->pos() );
55
56 emit contextMenuRequest( index, viewport()->mapToGlobal( event->pos() ) );
57 }
58
59 class LogSensorModel : public QAbstractTableModel
60 {
61 public:
LogSensorModel(QObject * parent=nullptr)62 LogSensorModel( QObject *parent = nullptr )
63 : QAbstractTableModel( parent )
64 {
65 }
66
columnCount(const QModelIndex & parent=QModelIndex ()) const67 int columnCount( const QModelIndex &parent = QModelIndex() ) const override
68 {
69 Q_UNUSED( parent );
70
71 return 5;
72 }
73
rowCount(const QModelIndex & parent=QModelIndex ()) const74 int rowCount( const QModelIndex &parent = QModelIndex() ) const override
75 {
76 Q_UNUSED( parent );
77
78 return mSensors.count();
79 }
80
data(const QModelIndex & index,int role=Qt::DisplayRole) const81 QVariant data( const QModelIndex &index, int role = Qt::DisplayRole ) const override
82 {
83 if ( !index.isValid() )
84 return QVariant();
85
86 if ( index.row() >= mSensors.count() || index.row() < 0 )
87 return QVariant();
88
89 LogSensor *sensor = mSensors[ index.row() ];
90
91 if ( role == Qt::DisplayRole ) {
92 switch ( index.column() ) {
93 case 1:
94 return sensor->timerInterval();
95 case 2:
96 return sensor->sensorName();
97 case 3:
98 return sensor->hostName();
99 case 4:
100 return sensor->fileName();
101 }
102 } else if ( role == Qt::DecorationRole ) {
103 static QPixmap runningPixmap = KIconLoader::global()->loadIcon( QStringLiteral("running"), KIconLoader::Small, KIconLoader::SizeSmall );
104 static QPixmap waitingPixmap = KIconLoader::global()->loadIcon( QStringLiteral("waiting"), KIconLoader::Small, KIconLoader::SizeSmall );
105
106 if ( index.column() == 0 ) {
107 if ( sensor->isLogging() )
108 return runningPixmap;
109 else
110 return waitingPixmap;
111 }
112 } else if ( role == Qt::ForegroundRole ) {
113 if ( sensor->limitReached() )
114 return mAlarmColor;
115 else
116 return mForegroundColor;
117 } else if ( role == Qt::BackgroundRole ) {
118 return mBackgroundColor;
119 }
120
121 return QVariant();
122 }
123
headerData(int section,Qt::Orientation orientation,int role=Qt::DisplayRole) const124 QVariant headerData( int section, Qt::Orientation orientation, int role = Qt::DisplayRole ) const override
125 {
126 if ( orientation == Qt::Vertical )
127 return QVariant();
128
129 if ( role == Qt::DisplayRole ) {
130 switch ( section ) {
131 case 0:
132 return i18nc("@title:column", "Logging");
133 case 1:
134 return i18nc("@title:column", "Timer Interval");
135 case 2:
136 return i18nc("@title:column", "Sensor Name");
137 case 3:
138 return i18nc("@title:column", "Host Name");
139 case 4:
140 return i18nc("@title:column", "Log File");
141 default:
142 return QVariant();
143 }
144 }
145
146 return QVariant();
147 }
148
addSensor(LogSensor * sensor)149 void addSensor( LogSensor *sensor )
150 {
151 mSensors.append( sensor );
152
153 connect( sensor, SIGNAL(changed()), this, SIGNAL(layoutChanged()) );
154
155 emit layoutChanged();
156 }
157
removeSensor(LogSensor * sensor)158 void removeSensor( LogSensor *sensor )
159 {
160 delete mSensors.takeAt( mSensors.indexOf( sensor ) );
161
162 emit layoutChanged();
163 }
164
sensor(const QModelIndex & index) const165 LogSensor* sensor( const QModelIndex &index ) const
166 {
167 if ( !index.isValid() || index.row() >= mSensors.count() || index.row() < 0 )
168 return nullptr;
169
170 return mSensors[ index.row() ];
171 }
172
clear()173 void clear()
174 {
175 qDeleteAll( mSensors );
176 mSensors.clear();
177 }
178
sensors() const179 const QList<LogSensor*> sensors() const
180 {
181 return mSensors;
182 }
183
setForegroundColor(const QColor & color)184 void setForegroundColor( const QColor &color ) { mForegroundColor = color; }
foregroundColor() const185 QColor foregroundColor() const { return mForegroundColor; }
186
setBackgroundColor(const QColor & color)187 void setBackgroundColor( const QColor &color ) { mBackgroundColor = color; }
backgroundColor() const188 QColor backgroundColor() const { return mBackgroundColor; }
189
setAlarmColor(const QColor & color)190 void setAlarmColor( const QColor &color ) { mAlarmColor = color; }
alarmColor() const191 QColor alarmColor() const { return mAlarmColor; }
192
193 private:
194
195 QColor mForegroundColor;
196 QColor mBackgroundColor;
197 QColor mAlarmColor;
198
199 QList<LogSensor*> mSensors;
200 };
201
LogSensor(QObject * parent)202 LogSensor::LogSensor( QObject *parent )
203 : QObject( parent ),
204 mTimerID( NONE ),
205 mLowerLimitActive( false ),
206 mUpperLimitActive( 0 ),
207 mLowerLimit( 0 ),
208 mUpperLimit( 0 ),
209 mLimitReached( false )
210 {
211 }
212
~LogSensor()213 LogSensor::~LogSensor()
214 {
215 }
216
setHostName(const QString & name)217 void LogSensor::setHostName( const QString& name )
218 {
219 mHostName = name;
220 }
221
hostName() const222 QString LogSensor::hostName() const
223 {
224 return mHostName;
225 }
226
setSensorName(const QString & name)227 void LogSensor::setSensorName( const QString& name )
228 {
229 mSensorName = name;
230 }
231
sensorName() const232 QString LogSensor::sensorName() const
233 {
234 return mSensorName;
235 }
236
setFileName(const QString & name)237 void LogSensor::setFileName( const QString& name )
238 {
239 mFileName = name;
240 }
241
fileName() const242 QString LogSensor::fileName() const
243 {
244 return mFileName;
245 }
246
setUpperLimitActive(bool value)247 void LogSensor::setUpperLimitActive( bool value )
248 {
249 mUpperLimitActive = value;
250 }
251
upperLimitActive() const252 bool LogSensor::upperLimitActive() const
253 {
254 return mUpperLimitActive;
255 }
256
setLowerLimitActive(bool value)257 void LogSensor::setLowerLimitActive( bool value )
258 {
259 mLowerLimitActive = value;
260 }
261
lowerLimitActive() const262 bool LogSensor::lowerLimitActive() const
263 {
264 return mLowerLimitActive;
265 }
266
setUpperLimit(double value)267 void LogSensor::setUpperLimit( double value )
268 {
269 mUpperLimit = value;
270 }
271
upperLimit() const272 double LogSensor::upperLimit() const
273 {
274 return mUpperLimit;
275 }
276
setLowerLimit(double value)277 void LogSensor::setLowerLimit( double value )
278 {
279 mLowerLimit = value;
280 }
281
lowerLimit() const282 double LogSensor::lowerLimit() const
283 {
284 return mLowerLimit;
285 }
286
setTimerInterval(int interval)287 void LogSensor::setTimerInterval( int interval )
288 {
289 mTimerInterval = interval;
290
291 if ( mTimerID != NONE ) {
292 timerOff();
293 timerOn();
294 }
295 }
296
timerInterval() const297 int LogSensor::timerInterval() const
298 {
299 return mTimerInterval;
300 }
301
isLogging() const302 bool LogSensor::isLogging() const
303 {
304 return mTimerID != NONE;
305 }
306
limitReached() const307 bool LogSensor::limitReached() const
308 {
309 return mLimitReached;
310 }
311
timerOff()312 void LogSensor::timerOff()
313 {
314 if ( mTimerID > 0 )
315 killTimer( mTimerID );
316 mTimerID = NONE;
317 }
318
timerOn()319 void LogSensor::timerOn()
320 {
321 mTimerID = startTimer( mTimerInterval * 1000 );
322 }
323
startLogging()324 void LogSensor::startLogging()
325 {
326 timerOn();
327 }
328
stopLogging()329 void LogSensor::stopLogging()
330 {
331 timerOff();
332 }
333
timerEvent(QTimerEvent * event)334 void LogSensor::timerEvent ( QTimerEvent * event )
335 {
336 Q_UNUSED(event);
337 KSGRD::SensorMgr->sendRequest( mHostName, mSensorName, static_cast<KSGRD::SensorClient*>(this), 42 );
338 }
339
answerReceived(int id,const QList<QByteArray> & answer)340 void LogSensor::answerReceived( int id, const QList<QByteArray>& answer ) //virtual
341 {
342 QFile mLogFile( mFileName );
343
344 if ( !mLogFile.open( QIODevice::ReadWrite | QIODevice::Append ) ) {
345 stopLogging();
346 return;
347 }
348
349 switch ( id ) {
350 case 42: {
351 QTextStream stream( &mLogFile );
352 double value = 0;
353 if ( !answer.isEmpty() )
354 value = answer[ 0 ].toDouble();
355
356 if ( mLowerLimitActive && value < mLowerLimit ) {
357 timerOff();
358 mLimitReached = true;
359
360 // send notification
361 KNotification::event( QStringLiteral("sensor_alarm"), QStringLiteral( "sensor '%1' at '%2' reached lower limit" )
362 .arg( mSensorName ).arg( mHostName), QPixmap(), nullptr );
363
364 timerOn();
365 } else if ( mUpperLimitActive && value > mUpperLimit ) {
366 timerOff();
367 mLimitReached = true;
368
369 // send notification
370 KNotification::event( QStringLiteral("sensor_alarm"), QStringLiteral( "sensor '%1' at '%2' reached upper limit" )
371 .arg( mSensorName).arg( mHostName), QPixmap(), nullptr );
372
373 timerOn();
374 } else {
375 mLimitReached = false;
376 }
377
378 const QDate date = QDateTime::currentDateTime().date();
379 const QTime time = QDateTime::currentDateTime().time();
380
381 stream << QStringLiteral( "%1 %2 %3 %4 %5: %6\n" ).arg( QLocale().monthName( date.month() ) )
382 .arg( date.day() ).arg( time.toString() )
383 .arg( mHostName).arg( mSensorName ).arg( value );
384 }
385 }
386
387 emit changed();
388
389 mLogFile.close();
390 }
391
SensorLogger(QWidget * parent,const QString & title,SharedSettings * workSheetSettings)392 SensorLogger::SensorLogger( QWidget *parent, const QString& title, SharedSettings *workSheetSettings )
393 : KSGRD::SensorDisplay( parent, title, workSheetSettings )
394 {
395 mModel = new LogSensorModel( this );
396 mModel->setForegroundColor( KSGRD::Style->firstForegroundColor() );
397 mModel->setBackgroundColor( KSGRD::Style->backgroundColor() );
398 mModel->setAlarmColor( KSGRD::Style->alarmColor() );
399
400 QLayout *layout = new QHBoxLayout(this);
401 mView = new LogSensorView( this );
402 layout->addWidget(mView);
403 setLayout(layout);
404
405 mView->header()->setStretchLastSection( true );
406 mView->setRootIsDecorated( false );
407 mView->setItemsExpandable( false );
408 mView->setModel( mModel );
409 setPlotterWidget( mView );
410
411 connect( mView, &LogSensorView::contextMenuRequest,
412 this, &SensorLogger::contextMenuRequest );
413
414 QPalette palette = mView->palette();
415 palette.setColor( QPalette::Base, KSGRD::Style->backgroundColor() );
416 mView->setPalette( palette );
417
418 setTitle( i18n( "Sensor Logger" ) );
419 setMinimumSize( 50, 25 );
420 }
421
~SensorLogger(void)422 SensorLogger::~SensorLogger(void)
423 {
424 }
425
addSensor(const QString & hostName,const QString & sensorName,const QString & sensorType,const QString &)426 bool SensorLogger::addSensor( const QString& hostName, const QString& sensorName, const QString& sensorType, const QString& )
427 {
428 if ( sensorType != QLatin1String("integer") && sensorType != QLatin1String("float") )
429 return false;
430
431 SensorLoggerDlg dlg( this );
432
433 if ( dlg.exec() ) {
434 if ( !dlg.fileName().isEmpty() ) {
435 LogSensor *sensor = new LogSensor( mModel );
436
437 sensor->setHostName( hostName );
438 sensor->setSensorName( sensorName );
439 sensor->setFileName( dlg.fileName() );
440 sensor->setTimerInterval( dlg.timerInterval() );
441 sensor->setLowerLimitActive( dlg.lowerLimitActive() );
442 sensor->setUpperLimitActive( dlg.upperLimitActive() );
443 sensor->setLowerLimit( dlg.lowerLimit() );
444 sensor->setUpperLimit( dlg.upperLimit() );
445
446 mModel->addSensor( sensor );
447 }
448 } else {
449 return false; //User cancelled dialog, so don't add sensor logger
450 }
451
452 return true;
453 }
454
editSensor(LogSensor * sensor)455 bool SensorLogger::editSensor( LogSensor* sensor )
456 {
457 SensorLoggerDlg dlg( this );
458
459 dlg.setFileName( sensor->fileName() );
460 dlg.setTimerInterval( sensor->timerInterval() );
461 dlg.setLowerLimitActive( sensor->lowerLimitActive() );
462 dlg.setLowerLimit( sensor->lowerLimit() );
463 dlg.setUpperLimitActive( sensor->upperLimitActive() );
464 dlg.setUpperLimit( sensor->upperLimit() );
465
466 if ( dlg.exec() ) {
467 if ( !dlg.fileName().isEmpty() ) {
468 sensor->setFileName( dlg.fileName() );
469 sensor->setTimerInterval( dlg.timerInterval() );
470 sensor->setLowerLimitActive( dlg.lowerLimitActive() );
471 sensor->setUpperLimitActive( dlg.upperLimitActive() );
472 sensor->setLowerLimit( dlg.lowerLimit() );
473 sensor->setUpperLimit( dlg.upperLimit() );
474 }
475 }
476
477 return true;
478 }
479
configureSettings()480 void SensorLogger::configureSettings()
481 {
482 SensorLoggerSettings dlg( this );
483
484 dlg.setTitle( title() );
485 dlg.setForegroundColor( mModel->foregroundColor() );
486 dlg.setBackgroundColor( mModel->backgroundColor() );
487 dlg.setAlarmColor( mModel->alarmColor() );
488
489 if ( dlg.exec() ) {
490 setTitle( dlg.title() );
491
492 mModel->setForegroundColor( dlg.foregroundColor() );
493 mModel->setBackgroundColor( dlg.backgroundColor() );
494 mModel->setAlarmColor( dlg.alarmColor() );
495
496 QPalette palette = mView->palette();
497 palette.setColor( QPalette::Base, dlg.backgroundColor() );
498 mView->setPalette( palette );
499 }
500 }
501
applyStyle()502 void SensorLogger::applyStyle()
503 {
504 mModel->setForegroundColor( KSGRD::Style->firstForegroundColor() );
505 mModel->setBackgroundColor( KSGRD::Style->backgroundColor() );
506 mModel->setAlarmColor( KSGRD::Style->alarmColor() );
507
508 QPalette palette = mView->palette();
509 palette.setColor( QPalette::Base, KSGRD::Style->backgroundColor() );
510 mView->setPalette( palette );
511 }
512
restoreSettings(QDomElement & element)513 bool SensorLogger::restoreSettings( QDomElement& element )
514 {
515 mModel->setForegroundColor( restoreColor( element, QStringLiteral("textColor"), Qt::green) );
516 mModel->setBackgroundColor( restoreColor( element, QStringLiteral("backgroundColor"), Qt::black ) );
517 mModel->setAlarmColor( restoreColor( element, QStringLiteral("alarmColor"), Qt::red ) );
518
519 mModel->clear();
520
521 QDomNodeList dnList = element.elementsByTagName( QStringLiteral("logsensors") );
522 for ( int i = 0; i < dnList.count(); i++ ) {
523 QDomElement element = dnList.item( i ).toElement();
524 LogSensor* sensor = new LogSensor( mModel );
525
526 sensor->setHostName( element.attribute(QStringLiteral("hostName")) );
527 sensor->setSensorName( element.attribute(QStringLiteral("sensorName")) );
528 sensor->setFileName( element.attribute(QStringLiteral("fileName")) );
529 sensor->setTimerInterval( element.attribute(QStringLiteral("timerInterval")).toInt() );
530 sensor->setLowerLimitActive( element.attribute(QStringLiteral("lowerLimitActive")).toInt() );
531 sensor->setLowerLimit( element.attribute(QStringLiteral("lowerLimit")).toDouble() );
532 sensor->setUpperLimitActive( element.attribute(QStringLiteral("upperLimitActive")).toInt() );
533 sensor->setUpperLimit( element.attribute(QStringLiteral("upperLimit")).toDouble() );
534
535 mModel->addSensor( sensor );
536 }
537
538 SensorDisplay::restoreSettings( element );
539
540 QPalette palette = mView->palette();
541 palette.setColor( QPalette::Base, mModel->backgroundColor() );
542 mView->setPalette( palette );
543
544 return true;
545 }
546
saveSettings(QDomDocument & doc,QDomElement & element)547 bool SensorLogger::saveSettings( QDomDocument& doc, QDomElement& element )
548 {
549 saveColor( element, QStringLiteral("textColor"), mModel->foregroundColor() );
550 saveColor( element, QStringLiteral("backgroundColor"), mModel->backgroundColor() );
551 saveColor( element, QStringLiteral("alarmColor"), mModel->alarmColor() );
552
553 const QList<LogSensor*> sensors = mModel->sensors();
554 for ( int i = 0; i < sensors.count(); ++i ) {
555 LogSensor *sensor = sensors[ i ];
556 QDomElement log = doc.createElement( QStringLiteral("logsensors") );
557 log.setAttribute(QStringLiteral("sensorName"), sensor->sensorName());
558 log.setAttribute(QStringLiteral("hostName"), sensor->hostName());
559 log.setAttribute(QStringLiteral("fileName"), sensor->fileName());
560 log.setAttribute(QStringLiteral("timerInterval"), sensor->timerInterval());
561 log.setAttribute(QStringLiteral("lowerLimitActive"), QStringLiteral("%1").arg(sensor->lowerLimitActive()));
562 log.setAttribute(QStringLiteral("lowerLimit"), QStringLiteral("%1").arg(sensor->lowerLimit()));
563 log.setAttribute(QStringLiteral("upperLimitActive"), QStringLiteral("%1").arg(sensor->upperLimitActive()));
564 log.setAttribute(QStringLiteral("upperLimit"), QStringLiteral("%1").arg(sensor->upperLimit()));
565
566 element.appendChild( log );
567 }
568
569 SensorDisplay::saveSettings( doc, element );
570
571 return true;
572 }
573
answerReceived(int,const QList<QByteArray> &)574 void SensorLogger::answerReceived( int, const QList<QByteArray>& ) //virtual
575 {
576 // we do not use this, since all answers are received by the LogSensors
577 }
578
contextMenuRequest(const QModelIndex & index,const QPoint & point)579 void SensorLogger::contextMenuRequest( const QModelIndex &index, const QPoint &point )
580 {
581 LogSensor *sensor = mModel->sensor( index );
582
583 QMenu pm;
584
585 QAction *action = nullptr;
586 if (hasSettingsDialog()) {
587 action = pm.addAction(i18n("&Properties"));
588 action->setData( 1 );
589 }
590 if(!mSharedSettings->locked) {
591
592 action = pm.addAction(i18n("&Remove Display"));
593 action->setData( 2 );
594
595 pm.addSeparator();
596
597 action = pm.addAction(i18n("&Remove Sensor"));
598 action->setData( 3 );
599 if ( !sensor )
600 action->setEnabled( false );
601
602 action = pm.addAction(i18n("&Edit Sensor..."));
603 action->setData( 4 );
604 if ( !sensor )
605 action->setEnabled( false );
606 }
607
608 if ( sensor ) {
609 if ( sensor->isLogging() ) {
610 action = pm.addAction(i18n("St&op Logging"));
611 action->setData( 6 );
612 } else {
613 action = pm.addAction(i18n("S&tart Logging"));
614 action->setData( 5 );
615 }
616 }
617
618 action = pm.exec( point );
619 if ( !action )
620 return;
621
622 switch (action->data().toInt())
623 {
624 case 1:
625 configureSettings();
626 break;
627 case 2: {
628 KSGRD::SensorDisplay::DeleteEvent *ev = new KSGRD::SensorDisplay::DeleteEvent( this );
629 qApp->postEvent(parent(), ev);
630 break;
631 }
632 case 3:
633 if ( sensor )
634 mModel->removeSensor( sensor );
635 break;
636 case 4:
637 if ( sensor )
638 editSensor( sensor );
639 break;
640 case 5:
641 if ( sensor )
642 sensor->startLogging();
643 break;
644 case 6:
645 if ( sensor )
646 sensor->stopLogging();
647 break;
648 }
649 }
650
651
652