From c321d5140d1c4a327209fdb8a8a9299f7c38458e Mon Sep 17 00:00:00 2001 From: Claudio Scafuri <claudio.scafuri@elettra.eu> Date: Wed, 19 Aug 2020 16:44:28 +0200 Subject: [PATCH] imported from CVS repo fermi/panels/interlock/general release_11 --- .gitignore | 25 ++ CHANGELOG | 26 ++ README | 11 + README.md | 11 + general.pro | 51 +++ general.qrc | 6 + icons/alarm.png | Bin 0 -> 1887 bytes properties.txt | 0 sounds/generalintlkpanel_bark.ogg | Bin 0 -> 20319 bytes sounds/generalintlkpanel_beep.ogg | Bin 0 -> 39784 bytes src/Sound.cpp | 67 ++++ src/Sound.h | 39 +++ src/interlock.cpp | 393 +++++++++++++++++++++ src/interlock.h | 78 +++++ src/interlock.ui | 553 ++++++++++++++++++++++++++++++ src/main.cpp | 58 ++++ src/mysqlmodel.cpp | 44 +++ src/mysqlmodel.h | 39 +++ src/queryconfiguration.cpp | 106 ++++++ src/queryconfiguration.h | 61 ++++ src/queryconfiguration.ui | 159 +++++++++ src/tabwidget.cpp | 21 ++ src/tabwidget.h | 31 ++ src/tlabelitem.cpp | 137 ++++++++ src/tlabelitem.h | 60 ++++ src/treeitem.cpp | 12 + src/treeitem.h | 18 + src/ttablewidget.cpp | 61 ++++ src/ttablewidget.h | 33 ++ src/ttreeview.cpp | 26 ++ src/ttreeview.h | 24 ++ src/ttreewidget.cpp | 184 ++++++++++ src/ttreewidget.h | 56 +++ 33 files changed, 2390 insertions(+) create mode 100644 .gitignore create mode 100644 CHANGELOG create mode 100644 README create mode 100644 general.pro create mode 100644 general.qrc create mode 100644 icons/alarm.png create mode 100644 properties.txt create mode 100644 sounds/generalintlkpanel_bark.ogg create mode 100644 sounds/generalintlkpanel_beep.ogg create mode 100644 src/Sound.cpp create mode 100644 src/Sound.h create mode 100644 src/interlock.cpp create mode 100644 src/interlock.h create mode 100644 src/interlock.ui create mode 100644 src/main.cpp create mode 100644 src/mysqlmodel.cpp create mode 100644 src/mysqlmodel.h create mode 100644 src/queryconfiguration.cpp create mode 100644 src/queryconfiguration.h create mode 100644 src/queryconfiguration.ui create mode 100644 src/tabwidget.cpp create mode 100644 src/tabwidget.h create mode 100644 src/tlabelitem.cpp create mode 100644 src/tlabelitem.h create mode 100644 src/treeitem.cpp create mode 100644 src/treeitem.h create mode 100644 src/ttablewidget.cpp create mode 100644 src/ttablewidget.h create mode 100644 src/ttreeview.cpp create mode 100644 src/ttreeview.h create mode 100644 src/ttreewidget.cpp create mode 100644 src/ttreewidget.h diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4003a79 --- /dev/null +++ b/.gitignore @@ -0,0 +1,25 @@ +.pydevproject +.project +.cproject +.settings +Makefile +moc +obj +bin +objects +core* +*~ +*.pyc +*.so +*.so* +.pylintrc +.metadata +.idea +.cvsignore +.nse_depinfo +software +oldsrc +ui_* +qrc*.cpp +.qmake.stash +CVS diff --git a/CHANGELOG b/CHANGELOG new file mode 100644 index 0000000..38f7011 --- /dev/null +++ b/CHANGELOG @@ -0,0 +1,26 @@ +release_10: porting to QT5 (from phonon to Qt Multimedia) + +release_09: Fixed sorting problem on the tree widgets. Added missing modulators to the combo box. + +release_08: Fixed scroll issue on updates in TTreeWidget + Ported to QTango5 + +release_07: added Test Sound button + +release_06: Ticket 975: General Interlock: sounds and important/blocking alarm + Added sound support. + Tabs with recent alarms are evidentiated by an alarm bell icon. + Recent alarms are bold. + + +release_04: Ticket 774 ferminterlock: manca history per "PS and Vacuum" + Added search box on each tab. Text based search. + Queries have been modified. + See http://acswww.elettra.trieste.it/bugzilla/show_bug.cgi?id=774 + + +release_05: ticket 983: +srv-tango-srf/interlock/plc_mod_ll --> f/interlock/plc_mod_ll +srv-tango-srf/interlock/plc_int_ll.01 --> f/interlock/plc_int_ll.01 +srv-tango-srf/interlock/plc_int_ll.02 --> f/interlock/plc_int_l +database: su srv-log-srf --> srv-db-srf diff --git a/README b/README new file mode 100644 index 0000000..1eb9ab1 --- /dev/null +++ b/README @@ -0,0 +1,11 @@ +ferminterlock + +USAGE: + +ferminterlock f/interlock/plc_mod_ll f/interlock/plc_int_ll.01 f/interlock/plc_int_ll.02 srv-padres-srf:20000/srv-padres-srf/interlock/plc_int_ll.03 --sound-file-name /runtime/share/sounds/generalintlkpanel_beep.ogg + +Contacts: + +giacomo.strangolino@elettra.... +graziano.scalamera@elettra... + diff --git a/README.md b/README.md index 7a48adc..e103249 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,14 @@ # ferminterlock pannello fermi interlock + + +# usage + +ferminterlock f/interlock/plc_mod_ll f/interlock/plc_int_ll.01 f/interlock/plc_int_ll.02 srv-padres-srf:20000/srv-padres-srf/interlock/plc_int_ll.03 --sound-file-name /runtime/share/sounds/generalintlkpanel_beep.ogg + +# contatcts + +giacomo.strangolino@elettra.... +graziano.scalamera@elettra... + diff --git a/general.pro b/general.pro new file mode 100644 index 0000000..c33b9c8 --- /dev/null +++ b/general.pro @@ -0,0 +1,51 @@ +include(/usr/local/qtango/include/qtango6/qtango.pri) + +CONFIG += debug +QT += sql +QT += multimedia + +DEFINES -= QT_NO_DEBUG_OUTPUT + +RESOURCES += general.qrc + +# DEFINES -= QT_NO_DEBUG_OUTPUT + + +SOURCES += src/tlabelitem.cpp \ + src/ttreewidget.cpp \ + src/ttablewidget.cpp \ + src/interlock.cpp \ + src/main.cpp \ + src/queryconfiguration.cpp \ + src/mysqlmodel.cpp \ + src/ttreeview.cpp \ + src/Sound.cpp \ + src/tabwidget.cpp \ + src/treeitem.cpp + +HEADERS += src/tlabelitem.h \ + src/ttreewidget.h \ + src/ttablewidget.h \ + src/interlock.h \ + src/queryconfiguration.h \ + src/mysqlmodel.h \ + src/ttreeview.h \ + src/Sound.h \ + src/tabwidget.h \ + src/treeitem.h + + +FORMS = src/interlock.ui src/queryconfiguration.ui + +TARGET = bin/ferminterlock-gui + +sounds.files = sounds/generalintlkpanel_beep.ogg +sounds.path = $${INSTALL_ROOT}/share/sounds + +INSTALLS += sounds + +OTHER_FILES += \ + properties.txt + + +QMAKE_POST_LINK = $(STRIP) $(TARGET) diff --git a/general.qrc b/general.qrc new file mode 100644 index 0000000..af52b0e --- /dev/null +++ b/general.qrc @@ -0,0 +1,6 @@ +<!DOCTYPE RCC><RCC version="1.0"> + <qresource> + <file>icons/alarm.png</file> + </qresource> +</RCC> + diff --git a/icons/alarm.png b/icons/alarm.png new file mode 100644 index 0000000000000000000000000000000000000000..38859f8adc3df521c9a345fc859b048b0b18530f GIT binary patch literal 1887 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 Ze*uuo%eCNJse=Fj002ovPDHLkV1moDa3}x( literal 0 HcmV?d00001 diff --git a/properties.txt b/properties.txt new file mode 100644 index 0000000..e69de29 diff --git a/sounds/generalintlkpanel_bark.ogg b/sounds/generalintlkpanel_bark.ogg new file mode 100644 index 0000000000000000000000000000000000000000..045d609cd69238da7e360a4667a889a02d8c7fe1 GIT binary patch literal 20319 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 aOVr{wQzh+YS)&vhm=COTSz9s!pwcE0vyUMF literal 0 HcmV?d00001 diff --git a/sounds/generalintlkpanel_beep.ogg b/sounds/generalintlkpanel_beep.ogg new file mode 100644 index 0000000000000000000000000000000000000000..20de4c76c7cfd5f8e0aecbcc691f71a43f3b3c74 GIT binary patch literal 39784 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 P#AR;2Ym|}^0Q!akSTrck literal 0 HcmV?d00001 diff --git a/src/Sound.cpp b/src/Sound.cpp new file mode 100644 index 0000000..8b81201 --- /dev/null +++ b/src/Sound.cpp @@ -0,0 +1,67 @@ +#include "Sound.h" +#include <QString> +#include <QSound> +#include <QtDebug> +#include <macros.h> + +Sound::Sound(const QString& sound_file) +{ + m_init(sound_file); +} + +Sound::Sound(QObject *parent, const QString &sound_file) : QObject(parent) +{ + m_init(sound_file); +} + +void Sound::m_init(const QString& sound_file) +{ + mSoundEffect.setSource(QUrl::fromLocalFile(sound_file)); + set_loop_delay(500); + m_loop_timer.setSingleShot(true); + m_loop_enabled = false; + connect(&mSoundEffect, SIGNAL(playingChanged()), this, SLOT(playingChanged())); + connect(&m_loop_timer, SIGNAL(timeout()), &mSoundEffect, SLOT(play())); +} + +void Sound::play() +{ + if (m_loop_enabled) return; + mSoundEffect.play(); +} + +void Sound::loop() +{ + if(!m_loop_enabled){ + m_loop_enabled = true; + mSoundEffect.play(); + } +} + +void Sound::playingChanged() +{ + if(m_loop_enabled && !mSoundEffect.isPlaying()) + m_loop_timer.start(); +} + +void Sound::stop() +{ + m_loop_enabled = false; + m_loop_timer.stop(); + mSoundEffect.stop(); +} + +void Sound::set_loop_delay(int delay) +{ + m_loop_timer.setInterval(delay); +} + +int Sound::get_loop_delay() +{ + return m_loop_timer.interval(); +} + +void Sound::timer_timeout() { + mSoundEffect.play(); +} + diff --git a/src/Sound.h b/src/Sound.h new file mode 100644 index 0000000..f143721 --- /dev/null +++ b/src/Sound.h @@ -0,0 +1,39 @@ +#ifndef SOUND_H +#define SOUND_H + +#include <QString> +#include <QObject> +#include <QTimer> +#include <QSoundEffect> +#include <QFile> + +class Sound:public QObject +{ + Q_OBJECT +public: + Sound(const QString& sound_file); + Sound(QObject *parent, const QString& sound_file); + void set_loop_delay(int); + int get_loop_delay(); +public slots: + void play(); + void stop(); + void loop(); +private slots: + void playingChanged(); + void timer_timeout(); +private: + bool m_loop_enabled; + QTimer m_loop_timer; + QSoundEffect mSoundEffect; + + void m_init(const QString &sound_file); +}; + + + + +#endif + + + diff --git a/src/interlock.cpp b/src/interlock.cpp new file mode 100644 index 0000000..ff2a443 --- /dev/null +++ b/src/interlock.cpp @@ -0,0 +1,393 @@ +/*************************************************************************** + * Copyright (C) 2007 by Vincenzo Forchi`,,, * + * vincenzo.forchi@elettra.trieste.it * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + + +#include "interlock.h" +#include "Sound.h" +#include <qtango.h> +#include <device_factory.h> +#include <QtSql> +#include <QScrollBar> +#include "mysqlmodel.h" +#include <TLog> +#include <QMessageBox> +#include <elettracolors.h> + +Interlock::Interlock(QWidget *parent) : QWidget(parent) +{ + QStringList args = qApp->arguments(); + if(args.size() < 5) + { + QMessageBox::information(this, "Usage", + QString("%1 f/interlock/plc_mod_ll f/interlock/plc_int_ll.01" + "\nf/interlock/plc_int_ll.02 srv-padres-srf:20000/srv-tango-srf/interlock/plc_int_ll.03"). + arg(args.first())); + exit(EXIT_FAILURE); + } + + if(args.contains("--sound-file-name") && args.size() > args.indexOf("--sound-file-name")) + { + Sound *sound = new Sound(this, args.at(args.indexOf("--sound-file-name") + 1)); + } + else + { + printf("\e[1;35m - if you want to enable sounds for alarms, provide as second argument the name of the sound file\e[0m\n"); + ui.pbTestSound->setHidden(true); + ui.line->setHidden(true); + } + + modDeviceName = QApplication::arguments()[1]; + vacPsDeviceName = QApplication::arguments()[2]; + + Config::instance()->setOverrideValuesAttributePropertyName("labels"); + + ui.setupUi(this); + setupDb(); + configureUi(); + createConnections(); + QTimer::singleShot(50, this, SLOT(connectTango())); +} + +void Interlock::setupDb() +{ + /* connect to db */ + db = QSqlDatabase::addDatabase("QMYSQL"); + db.setHostName("srv-db-srf-02"); + db.setDatabaseName("intlkfermi"); + db.setUserName("ifermi"); + db.setPassword("ferm"); + if (!db.open()) + QMessageBox::critical(0, "Error", "Can't establish connection with db intlkboost on host log"); + + queryType = QueryConfiguration::LastDay; + modulatorModel = new MySqlModel(); + psvacModel = new MySqlModel(); + ui.treeLogs->setModel(modulatorModel); + ui.treeLogsPSVAC->setModel(psvacModel); + + dbTimer = new QTimer(this); + connect(dbTimer, SIGNAL(timeout()), this, SLOT(readFromDb())); +} + +void Interlock::createConnections() +{ + connect(ui.treeAlarms, SIGNAL(acknowledgeSelected()), this, SLOT(acknowledgeSelected())); + connect(ui.pushRefresh, SIGNAL(clicked()), this, SLOT(readFromDb())); + connect(ui.pushRefreshPSVacuum, SIGNAL(clicked()), this, SLOT(readFromDb())); + connect(ui.pushQueryConfiguration, SIGNAL(clicked()), this, SLOT(queryConfiguration())); + connect(ui.pushQueryConfigurationPSVAC, SIGNAL(clicked()), this, SLOT(queryConfiguration())); + connect(ui.comboAck, SIGNAL(currentIndexChanged(int)), this, SLOT(changeAckTarget(int))); + connect(ui.tabWidget, SIGNAL(currentChanged(int)), this, SLOT(tabChanged(int))); + + /* 1 tab: mod filter */ + connect(ui.pbClearModFilter, SIGNAL(clicked()), ui.leFilterMod, SLOT(clear())); + connect(ui.leFilterMod, SIGNAL(textChanged(QString)), ui.treeAlarms, SLOT(filter(QString))); + + /* 2 tab: mod history */ + connect(ui.pbClearModHist, SIGNAL(clicked()), ui.leFilterModHist, SLOT(clear())); + connect(ui.leFilterModHist, SIGNAL(textChanged(QString)), ui.treeLogs, SLOT(filter(QString))); + + /* 3 tab: ps vac linac */ + connect(ui.pbClearFilterVacLinac, SIGNAL(clicked()), ui.leFilterPsVacLinac, SLOT(clear())); + connect(ui.leFilterPsVacLinac, SIGNAL(textChanged(QString)), ui.treeAlarmsVPS, SLOT(filter(QString))); + + /* 4 tab: ps vacuum uh */ + connect(ui.pbClearFilterPSVacUH, SIGNAL(clicked()), ui.leFilterPsVacUH, SLOT(clear())); + connect(ui.leFilterPsVacUH, SIGNAL(textChanged(QString)), ui.treeAlarmsVUH, SLOT(filter(QString))); + + /* 5 tab ps vacuum padres */ + connect(ui.pbClearFilterPsVacPadres, SIGNAL(clicked()), ui.leFilterPsVacPadres, SLOT(clear())); + connect(ui.leFilterPsVacPadres, SIGNAL(textChanged(QString)), ui.treeAlarmsPadres, SLOT(filter(QString))); + + /* 6. tab ps and vacuum history */ + connect(ui.pbClearFilterPsVacHistory, SIGNAL(clicked()), ui.leFilterPsVacHistory, SLOT(clear())); + connect(ui.leFilterPsVacHistory, SIGNAL(textChanged(QString)), ui.treeLogsPSVAC, SLOT(filter(QString))); + + /* sound */ + Sound *sound = findChild<Sound *>(); + if(sound) + { + connect(ui.treeAlarms, SIGNAL(newAlarmArrived()), sound, SLOT(play())); + connect(ui.treeAlarmsPadres, SIGNAL(newAlarmArrived()), sound, SLOT(play())); + connect(ui.treeAlarmsVPS, SIGNAL(newAlarmArrived()), sound, SLOT(play())); + connect(ui.treeAlarmsVUH, SIGNAL(newAlarmArrived()), sound, SLOT(play())); + connect(ui.pbTestSound, SIGNAL(clicked()), sound, SLOT(play())); + } + connect(ui.treeAlarms, SIGNAL(newAlarmArrived()), this, SLOT(newAlarmArrived())); + connect(ui.treeAlarmsPadres, SIGNAL(newAlarmArrived()), this, SLOT(newAlarmArrived())); + connect(ui.treeAlarmsVPS, SIGNAL(newAlarmArrived()), this, SLOT(newAlarmArrived())); + connect(ui.treeAlarmsVUH, SIGNAL(newAlarmArrived()), this, SLOT(newAlarmArrived())); +} + +void Interlock::configureUi() +{ + QStringList headers; + headers << "Date Time" << "Name" << "Description"; + foreach(TTreeWidget *ttw, findChildren<TTreeWidget *>(QRegExp("treeAlarms.*"))) + ttw->setHeaderLabels(headers); + QStringList mods; + for(int i = 0; i <= 15; i++) + mods << QString("mod%1").arg(i, 2, 10, QChar('0')); + mods.push_front("General"); + ui.comboAck->insertItems(0, mods); +} + +Interlock::~Interlock() +{ +} + +void Interlock::connectTango() +{ + /* mod */ + ui.treeAlarms->setPeriod(1000); + ui.treeAlarms->setSource(modDeviceName + "/Db_alarm"); + ui.tAck->setTargets(modDeviceName + QString("->Acknowledge(%1)").arg(ui.comboAck->itemData(0).toInt())); + /* vacuum and power supply */ + ui.treeAlarmsVPS->setPeriod(1000); + ui.treeAlarmsVPS->setSource(vacPsDeviceName + "/Db_alarm"); + /* Ps and vacuum UH */ + ui.treeAlarmsVUH->setSource("$3/Db_alarm"); + /* padres */ + ui.treeAlarmsPadres->setSource("$4/Db_alarm"); +} + +void Interlock::changeAckTarget(int idx) +{ + /* item list (idx == 0) starts with "general". + * Then modulators follow, from mod00 (idx == 1) to mod12. + * So the correct argin is obtained subtracting 1 to the combo box index. + */ + int argin = idx - 1; + ui.tAck->setTargets(modDeviceName + QString("->Acknowledge(%1)").arg(argin)); +} + +void Interlock::newAlarmArrived() +{ + /* get sender */ + QString oName = sender()->objectName(); + int tabIndex = -1; + if(oName == "treeAlarms") + tabIndex = 0; + else if(oName == "treeAlarmsVPS") + tabIndex = 2; + else if(oName == "treeAlarmsVUH") + tabIndex = 3; + else if(oName == "treeAlarmsPadres") + tabIndex = 4; + + ui.tabWidget->setTabIcon(tabIndex, QIcon(":icons/alarm.png")); +} + +/* icon removal is made inside custom TabBar. + * See tabwidget.h + */ +void Interlock::tabChanged(int idx) +{ + if(idx == ModHistory || idx == PsVacHistory) + { + dbTimer->start(10000); + this->readFromDb(); + } + else + { + dbTimer->stop(); + } +} + +void Interlock::acknowledgeSelected() +{ + /// ////////////////// + printf("\e[1;33m - da implementare - \e[0m\n"); + return; + /// ////////////////// + + QVector<double> v_attr(ui.treeAlarms->dataSize(), 0); + // QVector<long> v_comm(ui.treeAlarms->dataSize(), 0); + foreach (QTreeWidgetItem *it, ui.treeAlarms->selectedItems()) + { + //qDebug() << it->text(3); + v_attr[it->text(3).toInt()] = 1; + // v_comm << it->text(3).toLong(); + } + try { + DeviceAttribute attr; + attr.set_name("Db_alarm"); + vector<double> dati = v_attr.toStdVector(); + attr << dati; + Tango::DeviceProxy *dpr = DeviceFactory::instance()->getDeviceProxy(modDeviceName); + if(dpr) + dpr->write_attribute(attr); + } + catch (DevFailed &e) + { + TLog log(e); + time_t now = time(NULL); + QPair<time_t, DevErrorList> err(now, e.errors); + TUtil::instance()->addLog(log.toTimeErrlistPair(), log.formatMessage()); + QMessageBox::critical(0, "Error", log.formatMessage()); + } + ui.treeAlarms->clearSelection(); +} + +void Interlock::queryConfiguration() +{ + QueryConfiguration win; + win.setQueryType(queryType); + if (queryType == QueryConfiguration::Interval) + { + win.setStartDateTime(begin); + win.setStopDateTime(end); + } + + if (win.exec() == QDialog::Accepted) + { + queryType = win.queryType(); + begin = win.startDateTime(); + end = win.stopDateTime(); + readFromDb(); + } +} + +void Interlock::readFromDb() +{ + MySqlModel *model = NULL; + QString dbnam; + bool modulator = true; + if(ui.tabWidget->currentIndex() == ModHistory) + { + model = modulatorModel; + dbnam = "real_time_"; + } + else if(ui.tabWidget->currentIndex() == PsVacHistory) + { + modulator = false; + model = psvacModel; + dbnam = "vac_mag_"; + } + else + return; + + if (!db.isOpen()) + { + if (!db.open()) + { + QMessageBox::critical(0, "Error", "Can't establish connection with db FastInterlock on host log"); + return; + } + } + QDateTime adesso = QDateTime::currentDateTime(); + int year = adesso.date().year(); + + QString query, endquery; + if(modulator) + { + query = "SELECT FROM_UNIXTIME(" + dbnam + "%1.timestamp) AS 'Date Time', mod_description.name AS 'Name', " + + dbnam + "%1.event AS 'Event', " + "mod_description.comment AS Description, mod_description.modulator AS 'Modulator' FROM " + + dbnam + "%1 left join mod_description on (" + dbnam + + "%1.id_mod_description=mod_description.id_mod_description) WHERE timestamp>%2 " + ; + endquery = " ORDER BY timestamp desc"; + } + else + { + query = "SELECT FROM_UNIXTIME(" + dbnam + "%1.timestamp) AS 'Date Time', vac_mag_desc.name AS 'Name', " + + dbnam + "%1.event AS 'Event', " + "vac_mag_desc.comment AS Description, vac_mag_desc.plc AS 'PLC' FROM " + + dbnam + "%1 left join vac_mag_desc on (" + dbnam + + "%1.id_vac_mag_desc=vac_mag_desc.id_vac_mag_desc) WHERE timestamp>%2 " + ; + endquery = " ORDER BY timestamp desc"; + } + unsigned int inizioRicerca; + switch (queryType) + { + case QueryConfiguration::LastDay: + inizioRicerca = adesso.addDays(-1).toTime_t(); + year = adesso.date().year(); + query = QString(query).arg(year).arg(inizioRicerca) + endquery; + break; + case QueryConfiguration::LastWeek: + inizioRicerca = adesso.addDays(-7).toTime_t(); + year = adesso.date().year(); + query = QString(query).arg(year).arg(inizioRicerca) + endquery; + break; + case QueryConfiguration::LastMonth: + inizioRicerca = adesso.addMonths(-1).toTime_t(); + year = adesso.date().year(); + query = QString(query).arg(year).arg(inizioRicerca) + endquery; + break; + case QueryConfiguration::Interval: + year = end.date().year(); + query = QString(query).arg(year).arg(begin.toTime_t()); + query += QString(" AND timestamp<%1").arg(end.toTime_t()) + endquery; + break; + default: + break; + } + model->setQuery(query); + + /* must apply filter if set */ + if(modulator) + ui.treeLogs->updateFilter(); + else + ui.treeLogsPSVAC->updateFilter(); + + QSqlError error = model->lastError(); + if (error.type() != QSqlError::NoError) + { + QMessageBox::critical(0, "Database Error", QString("Error reading from Db:\n") + error.text() + "\nreconnecting"); + db.close(); + if (!db.open()) + QMessageBox::critical(0, "Error", "Can't establish connection with db FastInterlock on host log"); + } +} + +void Interlock::on_tabWidget_currentChanged(int idx) +{ + if (idx == 3) + { + readFromDb(); + dbTimer->start(); + } + else + dbTimer->stop(); +} + +void Interlock::modSelected(bool en) +{ + // ui.cbStatSelection->setEnabled(en); + // changeStatView(ui.cbStatSelection->currentIndex()); /* will also update descriptions */ +} + +void Interlock::changeStatView(int index) +{ + QString attname; + QString devName = modDeviceName; + /* if Select is checked, then set Stat_mod_NUMBER, otherwise Stat_system */ + // if(ui.rbSelectMod->isChecked()) + // attname = QString("Stat_mod_%1").arg(index); + // else + attname = "Stat_system"; + + // QString src = devName + "/" + attname; + // ui.tableStates->setSource(src); +} + diff --git a/src/interlock.h b/src/interlock.h new file mode 100644 index 0000000..e8e283b --- /dev/null +++ b/src/interlock.h @@ -0,0 +1,78 @@ +/*************************************************************************** + * Copyright (C) 2007 by Vincenzo Forchi`,,, * + * vincenzo.forchi@elettra.trieste.it * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + + +#ifndef interlock_H +#define interlock_H + +#include "ui_interlock.h" +#include "queryconfiguration.h" +#include <QString> +#include <QSqlDatabase> + +//class QGraphicsScene; +class MySqlModel; + +class Interlock: public QWidget +{ + Q_OBJECT + +public: + + enum Tabs { Mod, ModHistory, PsVacLinac, PsVacUH, PsVacPadres, PsVacHistory }; + + Interlock(QWidget * =NULL); + ~Interlock(); + +// QStringList getProperty(string, string); + +protected slots: + void connectTango(); + void acknowledgeSelected(); + void queryConfiguration(); + void readFromDb(); + void on_tabWidget_currentChanged(int); + void changeStatView(int index); + void modSelected(bool en); + void changeAckTarget(int); + void tabChanged(int idx); + void newAlarmArrived(); + +private: + Ui::Interlock ui; + QString modDeviceName, vacPsDeviceName; + + QSqlDatabase db; + MySqlModel *modulatorModel, *psvacModel; + + QueryConfiguration::QueryType queryType; + QDateTime begin, end; + QTimer *dbTimer; + + void setupDb(); + void configureUi(); + void createConnections(); + +// QGraphicsScene *boosterScene; + +}; + + +#endif diff --git a/src/interlock.ui b/src/interlock.ui new file mode 100644 index 0000000..daffa12 --- /dev/null +++ b/src/interlock.ui @@ -0,0 +1,553 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>Interlock</class> + <widget class="QWidget" name="Interlock"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>898</width> + <height>530</height> + </rect> + </property> + <property name="windowTitle"> + <string>General Fermi Interlock</string> + </property> + <layout class="QVBoxLayout"> + <property name="spacing"> + <number>6</number> + </property> + <property name="margin"> + <number>9</number> + </property> + <item> + <widget class="TabWidget" name="tabWidget"> + <property name="currentIndex"> + <number>0</number> + </property> + <widget class="QWidget" name="tabAlarms"> + <attribute name="title"> + <string>Modulator</string> + </attribute> + <layout class="QGridLayout"> + <property name="margin"> + <number>9</number> + </property> + <property name="spacing"> + <number>6</number> + </property> + <item row="1" column="8"> + <widget class="QPushButton" name="pbTestSound"> + <property name="text"> + <string>Test Sound</string> + </property> + </widget> + </item> + <item row="1" column="1"> + <widget class="QLineEdit" name="leFilterMod"> + <property name="toolTip"> + <string><html><head/><body><p><span style=" font-weight:600;">Filter</span></p><p>Start typing text to filter items on the second column of the view.</p><p>The search is <span style=" font-style:italic;">case insensitive</span>.</p><p>Click on the <span style=" font-style:italic;">Clear</span> button to clear the text on the box.</p></body></html></string> + </property> + </widget> + </item> + <item row="1" column="3"> + <spacer> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item row="1" column="6"> + <widget class="TPushButton" name="tAck"> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="text"> + <string>Ack</string> + </property> + </widget> + </item> + <item row="0" column="0" colspan="9"> + <widget class="TTreeWidget" name="treeAlarms"> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="alternatingRowColors"> + <bool>true</bool> + </property> + <property name="selectionMode"> + <enum>QAbstractItemView::SingleSelection</enum> + </property> + <property name="rootIsDecorated"> + <bool>false</bool> + </property> + <column> + <property name="text"> + <string notr="true">1</string> + </property> + </column> + </widget> + </item> + <item row="1" column="5"> + <widget class="TComboBox" name="comboAck"> + <property name="enabled"> + <bool>true</bool> + </property> + </widget> + </item> + <item row="1" column="2"> + <widget class="QPushButton" name="pbClearModFilter"> + <property name="text"> + <string>Clear</string> + </property> + </widget> + </item> + <item row="1" column="0"> + <widget class="QLabel" name="label"> + <property name="text"> + <string>Filter by label:</string> + </property> + </widget> + </item> + <item row="1" column="7"> + <widget class="Line" name="line"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + </widget> + </item> + </layout> + </widget> + <widget class="QWidget" name="tab"> + <attribute name="title"> + <string>Modulator History</string> + </attribute> + <layout class="QVBoxLayout"> + <property name="spacing"> + <number>6</number> + </property> + <property name="margin"> + <number>9</number> + </property> + <item> + <widget class="TTreeView" name="treeLogs"> + <property name="alternatingRowColors"> + <bool>true</bool> + </property> + <property name="selectionMode"> + <enum>QAbstractItemView::NoSelection</enum> + </property> + <property name="rootIsDecorated"> + <bool>false</bool> + </property> + <property name="itemsExpandable"> + <bool>false</bool> + </property> + <property name="allColumnsShowFocus"> + <bool>true</bool> + </property> + </widget> + </item> + <item> + <layout class="QHBoxLayout"> + <property name="spacing"> + <number>6</number> + </property> + <property name="margin"> + <number>0</number> + </property> + <item> + <widget class="QPushButton" name="pushRefresh"> + <property name="text"> + <string>Refresh</string> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="label_2"> + <property name="text"> + <string>Filter by name:</string> + </property> + </widget> + </item> + <item> + <widget class="QLineEdit" name="leFilterModHist"> + <property name="toolTip"> + <string><html><head/><body><p><span style=" font-weight:600;">Filter</span></p><p>Start typing text to filter items on the second column of the view.</p><p>The search is <span style=" font-style:italic;">case insensitive</span>.</p><p>Click on the <span style=" font-style:italic;">Clear</span> button to clear the text on the box.</p></body></html></string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="pbClearModHist"> + <property name="text"> + <string>Clear</string> + </property> + </widget> + </item> + <item> + <spacer> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QPushButton" name="pushQueryConfiguration"> + <property name="text"> + <string>Log Period</string> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + <widget class="QWidget" name="tab_2"> + <attribute name="title"> + <string>PS and Vacuum Linac</string> + </attribute> + <layout class="QGridLayout" name="gridLayout_2"> + <item row="1" column="4"> + <spacer name="horizontalSpacer_2"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item row="1" column="2"> + <widget class="QLineEdit" name="leFilterPsVacLinac"> + <property name="toolTip"> + <string><html><head/><body><p><span style=" font-weight:600;">Filter</span></p><p>Start typing text to filter items on the second column of the view.</p><p>The search is <span style=" font-style:italic;">case insensitive</span>.</p><p>Click on the <span style=" font-style:italic;">Clear</span> button to clear the text on the box.</p></body></html></string> + </property> + </widget> + </item> + <item row="1" column="0"> + <widget class="QLabel" name="label_3"> + <property name="text"> + <string>Filter by label:</string> + </property> + </widget> + </item> + <item row="0" column="0" colspan="7"> + <widget class="TTreeWidget" name="treeAlarmsVPS"> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="alternatingRowColors"> + <bool>true</bool> + </property> + <property name="selectionMode"> + <enum>QAbstractItemView::MultiSelection</enum> + </property> + <property name="rootIsDecorated"> + <bool>false</bool> + </property> + <column> + <property name="text"> + <string notr="true">1</string> + </property> + </column> + </widget> + </item> + <item row="1" column="6"> + <widget class="TPushButton" name="tAckPSV"> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>Ack</string> + </property> + <property name="targets"> + <string>$2->Acknowledge(0);;</string> + </property> + </widget> + </item> + <item row="1" column="3"> + <widget class="QPushButton" name="pbClearFilterVacLinac"> + <property name="text"> + <string>Clear</string> + </property> + </widget> + </item> + </layout> + </widget> + <widget class="QWidget" name="tab_3"> + <attribute name="title"> + <string>PS &and Vacuum UH</string> + </attribute> + <layout class="QGridLayout" name="gridLayout_3"> + <item row="1" column="6"> + <widget class="TPushButton" name="tAckPSUH"> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>Ack</string> + </property> + <property name="targets"> + <string>$3->Acknowledge(0);;;</string> + </property> + </widget> + </item> + <item row="0" column="0" colspan="7"> + <widget class="TTreeWidget" name="treeAlarmsVUH"> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="alternatingRowColors"> + <bool>true</bool> + </property> + <property name="selectionMode"> + <enum>QAbstractItemView::MultiSelection</enum> + </property> + <property name="rootIsDecorated"> + <bool>false</bool> + </property> + <column> + <property name="text"> + <string notr="true">1</string> + </property> + </column> + </widget> + </item> + <item row="1" column="5"> + <spacer name="horizontalSpacer"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item row="1" column="4"> + <widget class="QPushButton" name="pbClearFilterPSVacUH"> + <property name="text"> + <string>Clear</string> + </property> + </widget> + </item> + <item row="1" column="2"> + <widget class="QLabel" name="label_4"> + <property name="text"> + <string>Filter by label:</string> + </property> + </widget> + </item> + <item row="1" column="3"> + <widget class="QLineEdit" name="leFilterPsVacUH"> + <property name="toolTip"> + <string><html><head/><body><p><span style=" font-weight:600;">Filter</span></p><p>Start typing text to filter items on the second column of the view.</p><p>The search is <span style=" font-style:italic;">case insensitive</span>.</p><p>Click on the <span style=" font-style:italic;">Clear</span> button to clear the text on the box.</p></body></html></string> + </property> + </widget> + </item> + </layout> + </widget> + <widget class="QWidget" name="tab_4"> + <attribute name="title"> + <string>PS and Vacuum Padres</string> + </attribute> + <layout class="QGridLayout" name="gridLayout"> + <item row="2" column="4"> + <spacer name="horizontalSpacer_3"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item row="2" column="5"> + <widget class="TPushButton" name="tpbAckPSVPadres"> + <property name="text"> + <string>Ack</string> + </property> + <property name="targets"> + <string>$4->Acknowledge(0);;</string> + </property> + </widget> + </item> + <item row="2" column="2"> + <widget class="QPushButton" name="pbClearFilterPsVacPadres"> + <property name="text"> + <string>Clear</string> + </property> + </widget> + </item> + <item row="2" column="1"> + <widget class="QLineEdit" name="leFilterPsVacPadres"> + <property name="toolTip"> + <string><html><head/><body><p><span style=" font-weight:600;">Filter</span></p><p>Start typing text to filter items on the second column of the view.</p><p>The search is <span style=" font-style:italic;">case insensitive</span>.</p><p>Click on the <span style=" font-style:italic;">Clear</span> button to clear the text on the box.</p></body></html></string> + </property> + </widget> + </item> + <item row="2" column="0"> + <widget class="QLabel" name="label_5"> + <property name="text"> + <string>Filter by label:</string> + </property> + </widget> + </item> + <item row="0" column="0" colspan="6"> + <widget class="TTreeWidget" name="treeAlarmsPadres"> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="alternatingRowColors"> + <bool>true</bool> + </property> + <property name="selectionMode"> + <enum>QAbstractItemView::MultiSelection</enum> + </property> + <property name="rootIsDecorated"> + <bool>false</bool> + </property> + <column> + <property name="text"> + <string notr="true">1</string> + </property> + </column> + </widget> + </item> + </layout> + </widget> + <widget class="QWidget" name="tab_5"> + <attribute name="title"> + <string>PS and Vacuum History</string> + </attribute> + <layout class="QGridLayout" name="gridLayout_4"> + <item row="0" column="0" colspan="6"> + <widget class="TTreeView" name="treeLogsPSVAC"> + <property name="alternatingRowColors"> + <bool>true</bool> + </property> + <property name="selectionMode"> + <enum>QAbstractItemView::NoSelection</enum> + </property> + <property name="rootIsDecorated"> + <bool>false</bool> + </property> + <property name="itemsExpandable"> + <bool>false</bool> + </property> + <property name="allColumnsShowFocus"> + <bool>true</bool> + </property> + </widget> + </item> + <item row="1" column="0"> + <widget class="QPushButton" name="pushRefreshPSVacuum"> + <property name="text"> + <string>Refresh</string> + </property> + </widget> + </item> + <item row="1" column="1"> + <widget class="QLabel" name="label_6"> + <property name="text"> + <string>Filter by name:</string> + </property> + </widget> + </item> + <item row="1" column="2"> + <widget class="QLineEdit" name="leFilterPsVacHistory"> + <property name="toolTip"> + <string><html><head/><body><p><span style=" font-weight:600;">Filter</span></p><p>Start typing text to filter items on the second column of the view.</p><p>The search is <span style=" font-style:italic;">case insensitive</span>.</p><p>Click on the <span style=" font-style:italic;">Clear</span> button to clear the text on the box.</p></body></html></string> + </property> + </widget> + </item> + <item row="1" column="3"> + <widget class="QPushButton" name="pbClearFilterPsVacHistory"> + <property name="text"> + <string>Clear</string> + </property> + </widget> + </item> + <item row="1" column="4"> + <spacer name="horizontalSpacer_4"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>245</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item row="1" column="5"> + <widget class="QPushButton" name="pushQueryConfigurationPSVAC"> + <property name="text"> + <string>Log Period</string> + </property> + </widget> + </item> + </layout> + </widget> + </widget> + </item> + </layout> + </widget> + <layoutdefault spacing="6" margin="11"/> + <pixmapfunction>qPixmapFromMimeSource</pixmapfunction> + <customwidgets> + <customwidget> + <class>TComboBox</class> + <extends>QComboBox</extends> + <header>TComboBox</header> + </customwidget> + <customwidget> + <class>TPushButton</class> + <extends>QPushButton</extends> + <header>TPushButton</header> + </customwidget> + <customwidget> + <class>TTreeWidget</class> + <extends>QTreeWidget</extends> + <header>ttreewidget.h</header> + </customwidget> + <customwidget> + <class>TTreeView</class> + <extends>QTreeView</extends> + <header>ttreeview.h</header> + </customwidget> + <customwidget> + <class>TabWidget</class> + <extends>QTabWidget</extends> + <header>tabwidget.h</header> + <container>1</container> + </customwidget> + </customwidgets> + <tabstops> + <tabstop>treeAlarms</tabstop> + <tabstop>tAck</tabstop> + </tabstops> + <resources/> + <connections/> +</ui> diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..041d61a --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,58 @@ +/*************************************************************************** + * Copyright (C) 2007 by Vincenzo Forchi`,,, * + * vincenzo.forchi@elettra.trieste.it * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + + +#include <TApplication> +#include "interlock.h" +#include <X11/Xlib.h> +#include <QX11Info> +#include <tutil.h> +#include <elettracolors.h> + +#define CVSVERSION "$Name: $" + +/* uncomment to set application color */ +//#include <elettracolors.h> + +int main( int argc, char ** argv ) { + + TApplication a( argc, argv ); + /* uncomment to set application color */ + a.setPalette(EPalette("fermi")); + QString version(CVSVERSION); + a.setApplicationVersion(version); + a.setApplicationName("FermiInterlock"); + a.setProperty("author", "Giacomo"); + a.setProperty("mail", "giacomo.strangolino@elettra.trieste.it"); + a.setProperty("phone", "375-8073"); + a.setProperty("office", "T2PT025"); + a.setProperty("hwReferent", "Graziano"); + + Interlock mw; + mw.setWindowTitle("Fermi Interlock"); + mw.show(); + + /* register to window manager */ + Display *disp = QX11Info::display(); + Window root_win = (Window) mw.winId(); + XSetCommand(disp, root_win, argv, argc); + + return a.exec(); +} diff --git a/src/mysqlmodel.cpp b/src/mysqlmodel.cpp new file mode 100644 index 0000000..fefdbb6 --- /dev/null +++ b/src/mysqlmodel.cpp @@ -0,0 +1,44 @@ +/**************************************************************************** +** +** Copyright (C) 2004-2006 Trolltech ASA. All rights reserved. +** +** This file is part of the example classes of the Qt Toolkit. +** +** This file may be used under the terms of the GNU General Public +** License version 2.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of +** this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** http://www.trolltech.com/products/qt/opensource.html +** +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://www.trolltech.com/products/qt/licensing.html or contact the +** sales department at sales@trolltech.com. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +****************************************************************************/ + +#include <QtGui> + +#include "mysqlmodel.h" + +MySqlModel::MySqlModel(QObject *parent) : QSqlQueryModel(parent) +{ +} + +QVariant MySqlModel::data(const QModelIndex &index, int role) const +{ + QVariant value = QSqlQueryModel::data(index, role); + if (value.isValid() && role == Qt::DisplayRole) { + if (index.column() == 0) + { + QString d = value.toString(); + d.replace('T', ' '); + return d; + } + } + return value; +} diff --git a/src/mysqlmodel.h b/src/mysqlmodel.h new file mode 100644 index 0000000..339749c --- /dev/null +++ b/src/mysqlmodel.h @@ -0,0 +1,39 @@ +/**************************************************************************** +** +** Copyright (C) 2004-2006 Trolltech ASA. All rights reserved. +** +** This file is part of the example classes of the Qt Toolkit. +** +** This file may be used under the terms of the GNU General Public +** License version 2.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of +** this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** http://www.trolltech.com/products/qt/opensource.html +** +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://www.trolltech.com/products/qt/licensing.html or contact the +** sales department at sales@trolltech.com. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +****************************************************************************/ + +#ifndef MYSQLMODEL_H +#define MYSQLMODEL_H + +#include <QSqlQueryModel> + +class MySqlModel : public QSqlQueryModel +{ + Q_OBJECT + +public: + MySqlModel(QObject *parent = 0); + + QVariant data(const QModelIndex &item, int role) const; +}; + +#endif diff --git a/src/queryconfiguration.cpp b/src/queryconfiguration.cpp new file mode 100644 index 0000000..6f23ae0 --- /dev/null +++ b/src/queryconfiguration.cpp @@ -0,0 +1,106 @@ +/*************************************************************************** + * Copyright (C) 2006 by Vincenzo Forchi`,,, * + * tus@ken * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + + +#include "queryconfiguration.h" + +#include <QDateTime> +#include <QMessageBox> + +#include <QtDebug> + +QueryConfiguration::QueryConfiguration(QDialog *parent) : QDialog(parent) +{ + ui.setupUi(this); + ui.dateStart->setDate(QDate::currentDate()); + ui.dateStop->setDate(QDate::currentDate()); + ui.timeStart->setTime(QTime::currentTime()); + ui.timeStop->setTime(QTime::currentTime()); + connect(ui.comboPeriod, SIGNAL(currentIndexChanged(int)), this, SLOT(enableFrame(int))); + connect((QObject*) ui.buttonBox->button(QDialogButtonBox::Ok), SIGNAL(clicked()), this, SLOT(ok())); +} + +QueryConfiguration::~QueryConfiguration() +{ +} + +void QueryConfiguration::enableFrame(int index) +{ + if (index == 3) + ui.frame->setEnabled(true); + else + ui.frame->setEnabled(false); +} + +void QueryConfiguration::ok() +{ + if (ui.comboPeriod->currentIndex() == 3) + { + QDateTime begin, end; + begin.setDate(ui.dateStart->date()); + begin.setTime(ui.timeStart->time()); + end.setDate(ui.dateStop->date()); + end.setTime(ui.timeStop->time()); + if (begin >= end) + QMessageBox::warning(0, "Warning", "Invalid interval: check starting and ending dates"); + else + accept(); + } + else + accept(); +} + +void QueryConfiguration::setQueryType(QueryType qt) +{ + ui.comboPeriod->setCurrentIndex((int) qt); +} + +QueryConfiguration::QueryType QueryConfiguration::queryType() +{ + return (QueryType) ui.comboPeriod->currentIndex(); +} + +void QueryConfiguration::setStartDateTime(QDateTime dt) +{ + ui.dateStart->setDate(dt.date()); + ui.timeStart->setTime(dt.time()); +} + +void QueryConfiguration::setStopDateTime(QDateTime dt) +{ + ui.dateStop->setDate(dt.date()); + ui.timeStop->setTime(dt.time()); +} + +QDateTime QueryConfiguration::startDateTime() +{ + QDateTime dt; + dt.setDate(ui.dateStart->date()); + dt.setTime(ui.timeStart->time()); + return dt; +} + +QDateTime QueryConfiguration::stopDateTime() +{ + QDateTime dt; + dt.setDate(ui.dateStop->date()); + dt.setTime(ui.timeStop->time()); + return dt; +} diff --git a/src/queryconfiguration.h b/src/queryconfiguration.h new file mode 100644 index 0000000..40cf385 --- /dev/null +++ b/src/queryconfiguration.h @@ -0,0 +1,61 @@ +/*************************************************************************** + * Copyright (C) 2006 by Vincenzo Forchi`,,, * + * tus@ken * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + + +#ifndef QUERYCONFIGURATION_H +#define QUERYCONFIGURATION_H + +#include "ui_queryconfiguration.h" + +class QueryConfiguration : public QDialog +{ + Q_OBJECT + +public: + QueryConfiguration(QDialog * =NULL); + ~QueryConfiguration(); + + enum QueryType + { + LastDay, + LastWeek, + LastMonth, + Interval, + All + }; + + void setQueryType(QueryType); + QueryType queryType(); + + void setStartDateTime(QDateTime); + void setStopDateTime(QDateTime); + QDateTime startDateTime(); + QDateTime stopDateTime(); + +protected slots: + void enableFrame(int); + void ok(); + +private: + Ui::QueryConfiguration ui; +}; + + +#endif diff --git a/src/queryconfiguration.ui b/src/queryconfiguration.ui new file mode 100644 index 0000000..e6be309 --- /dev/null +++ b/src/queryconfiguration.ui @@ -0,0 +1,159 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>QueryConfiguration</class> + <widget class="QDialog" name="QueryConfiguration"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>380</width> + <height>194</height> + </rect> + </property> + <property name="windowTitle"> + <string>Display</string> + </property> + <layout class="QGridLayout"> + <property name="margin"> + <number>9</number> + </property> + <property name="spacing"> + <number>6</number> + </property> + <item row="1" column="0"> + <widget class="QFrame" name="frame"> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="minimumSize"> + <size> + <width>220</width> + <height>60</height> + </size> + </property> + <property name="frameShape"> + <enum>QFrame::StyledPanel</enum> + </property> + <property name="frameShadow"> + <enum>QFrame::Raised</enum> + </property> + <layout class="QGridLayout"> + <property name="margin"> + <number>9</number> + </property> + <property name="spacing"> + <number>6</number> + </property> + <item row="1" column="1"> + <widget class="QDateEdit" name="dateStop"> + <property name="displayFormat"> + <string>dd MMM yyyy</string> + </property> + <property name="calendarPopup"> + <bool>true</bool> + </property> + </widget> + </item> + <item row="0" column="1"> + <widget class="QDateEdit" name="dateStart"> + <property name="minimumSize"> + <size> + <width>80</width> + <height>0</height> + </size> + </property> + <property name="displayFormat"> + <string>dd MMM yyyy</string> + </property> + <property name="calendarPopup"> + <bool>true</bool> + </property> + </widget> + </item> + <item row="1" column="2"> + <widget class="QTimeEdit" name="timeStop"> + <property name="displayFormat"> + <string>h:mm</string> + </property> + </widget> + </item> + <item row="0" column="2"> + <widget class="QTimeEdit" name="timeStart"> + <property name="displayFormat"> + <string>h:mm </string> + </property> + </widget> + </item> + <item row="0" column="0"> + <widget class="QLabel" name="label"> + <property name="text"> + <string>From:</string> + </property> + </widget> + </item> + <item row="1" column="0"> + <widget class="QLabel" name="label_2"> + <property name="text"> + <string>To:</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item row="2" column="0"> + <widget class="QDialogButtonBox" name="buttonBox"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="standardButtons"> + <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> + </property> + </widget> + </item> + <item row="0" column="0"> + <widget class="QComboBox" name="comboPeriod"> + <item> + <property name="text"> + <string>Last Day</string> + </property> + </item> + <item> + <property name="text"> + <string>Last Week</string> + </property> + </item> + <item> + <property name="text"> + <string>Last Month</string> + </property> + </item> + <item> + <property name="text"> + <string>Interval</string> + </property> + </item> + </widget> + </item> + </layout> + </widget> + <resources/> + <connections> + <connection> + <sender>buttonBox</sender> + <signal>rejected()</signal> + <receiver>QueryConfiguration</receiver> + <slot>reject()</slot> + <hints> + <hint type="sourcelabel"> + <x>249</x> + <y>134</y> + </hint> + <hint type="destinationlabel"> + <x>258</x> + <y>148</y> + </hint> + </hints> + </connection> + </connections> +</ui> diff --git a/src/tabwidget.cpp b/src/tabwidget.cpp new file mode 100644 index 0000000..cbad1dc --- /dev/null +++ b/src/tabwidget.cpp @@ -0,0 +1,21 @@ +#include "tabwidget.h" + +void TabBar::mouseReleaseEvent(QMouseEvent *e) +{ + int ci = currentIndex(); + QIcon curIcon = tabIcon(ci); + if(!curIcon.isNull()) + { + setTabIcon(ci, QIcon()); + } + QTabBar::mouseReleaseEvent(e); +} + + +TabWidget::TabWidget(QWidget *parent) : + QTabWidget(parent) +{ + setTabBar(new TabBar(this)); +} + + diff --git a/src/tabwidget.h b/src/tabwidget.h new file mode 100644 index 0000000..eca8ae5 --- /dev/null +++ b/src/tabwidget.h @@ -0,0 +1,31 @@ +#ifndef TABWIDGET_H +#define TABWIDGET_H + +#include <QTabWidget> +#include <QTabBar> + +class TabBar : public QTabBar +{ + Q_OBJECT +public: + TabBar(QWidget *parent ) : QTabBar(parent) {} + +protected: + void mouseReleaseEvent(QMouseEvent *e); + +}; + +class TabWidget : public QTabWidget +{ + Q_OBJECT +public: + explicit TabWidget(QWidget *parent = 0); + +signals: + +public slots: + + +}; + +#endif // TABICON_H diff --git a/src/tlabelitem.cpp b/src/tlabelitem.cpp new file mode 100644 index 0000000..b86edd2 --- /dev/null +++ b/src/tlabelitem.cpp @@ -0,0 +1,137 @@ +#include <QtGui> + +#include "tlabelitem.h" +#include <elettracolors.h> +#include <QGraphicsSceneHoverEvent> + +TLabelItem::TLabelItem(QObject *p) : QObject(p), QTangoComProxyReader(this)//, app(0) +{ + m_stateColors = QVector<QColor>(14, EColor(Elettra::darkYellow)); + m_stateColors[Tango::ON] = EColor(Elettra::green); + m_stateColors[Tango::FAULT] = EColor(Elettra::red); + m_stateColors[Tango::ALARM] = EColor(Elettra::red); + + m_trueColor = Qt::green; + m_trueString = "True"; + m_falseColor = Qt::red; + m_falseString = "False"; + + m_mode = Simple; + ellipseRect = QRectF(0,0,10,10); + + setAcceptHoverEvents(true); + setFlags(QGraphicsItem::ItemIsMovable); + + connect(qtangoComHandle(), SIGNAL(newData(const TVariant&)), this, SLOT(refresh(const TVariant&)), + Qt::DirectConnection); +} + +void TLabelItem::refresh(const TVariant& v) +{ + //setToolTip(QString::fromStdString(getDeviceName())); + if (v.quality() == ATTR_INVALID) + { + brushColor = Qt::gray; + setText("####"); + } + else + { + if (v.canConvertToState()) + { + Tango::DevState s = v.toState(); + brushColor = m_stateColors.at(s); + setText(Config::instance()->stateString(s)); + } + else if (v.canConvertToBool()) + { + if (v.toBool()) + { + brushColor = m_trueColor; + setText(m_trueString); + } + else + { + brushColor = m_falseColor; + setText(m_falseString); + } + } + else if (v.canConvertToString()) + { + brushColor = Qt::white; + setText(v.toString()); + } + } +} + +void TLabelItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) +{ + painter->setPen(Qt::black); + painter->setBrush(brushColor); + + QRectF r = boundingRect(); + if (m_mode == Simple) + { + painter->drawEllipse(ellipseRect); + } + else + { + painter->drawRect(r.x()-2, r.y()-1, r.width()+4, r.height()+2); + QGraphicsSimpleTextItem::paint(painter, option, widget); + } +} + +/*void TLabelItem::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event) +{ + Q_UNUSED(event); + if (helperApplication.isNull()) + { + string a = TLoggingFactory::instance()->findHelperApplication(getDeviceName()); + if (a != "") + { + helperApplication = QString::fromStdString(a + " " + getDeviceName()); + } + else + helperApplication = QString("atkpanel ") + QString::fromStdString(getDeviceName()); + } + if (!helperApplication.isNull()) + { + launchHelperApplication(); + } +}*/ + +/*void TLabelItem::launchHelperApplication() +{ + if (!app) + app = new EApplicationLauncher(helperApplication); + app->Rise(); +}*/ + +void TLabelItem::hoverEnterEvent(QGraphicsSceneHoverEvent *event) +{ + Q_UNUSED(event); + if (ellipseRect.contains(event->pos())) + m_mode = Zoomed; + update(); +} + +void TLabelItem::hoverMoveEvent(QGraphicsSceneHoverEvent *event) +{ + Q_UNUSED(event); + if (ellipseRect.contains(event->pos())) + m_mode = Zoomed; + update(); +} + +void TLabelItem::hoverLeaveEvent(QGraphicsSceneHoverEvent *event) +{ + Q_UNUSED(event); + m_mode = Simple; + update(); +} + +/*QRectF TLabelItem::boundingRect() +{ + qDebug("TLabelItem::boundingRect"); + return QGraphicsSimpleTextItem::boundingRect(); +}*/ + diff --git a/src/tlabelitem.h b/src/tlabelitem.h new file mode 100644 index 0000000..cffb8a9 --- /dev/null +++ b/src/tlabelitem.h @@ -0,0 +1,60 @@ +#ifndef TLABELITEM_H +#define TLABELITEM_H + +#include <QGraphicsSimpleTextItem> +#include <qtangocore.h> +#include <QObject> +#include <QVector> +#include <QRectF> +#include <EApplicationLauncher> +#include <com_proxy_reader.h> + +class TLabelItem : public QObject, public QGraphicsSimpleTextItem, public QTangoComProxyReader +{ +Q_OBJECT + + enum Mode { Simple, Zoomed }; + +public: + TLabelItem(QObject * = 0); + + void setTrueColor(QColor c) { m_trueColor = c; }; + QColor trueColor() { return m_trueColor; }; + + void setFalseColor(QColor c) { m_falseColor = c; }; + QColor falseColor() { return m_falseColor; }; + + void setTrueString(QString s) { m_trueString = s; }; + QString trueString() { return m_trueString; }; + + void setFalseString(QString s) { m_falseString = s; }; + QString falseString() { return m_falseString; }; + +// QRectF boundingRect(); + +protected slots: + void refresh(const TVariant& v); + +protected: + void paint(QPainter *, const QStyleOptionGraphicsItem *, QWidget *); +// void mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event); + void hoverEnterEvent(QGraphicsSceneHoverEvent *event); + void hoverLeaveEvent(QGraphicsSceneHoverEvent *event); + void hoverMoveEvent(QGraphicsSceneHoverEvent *event); +// void launchHelperApplication(); + + QVector<QColor> m_stateColors; + QColor m_trueColor; + QColor m_falseColor; + QString m_trueString; + QString m_falseString; + + QColor brushColor; + Mode m_mode; + QRectF ellipseRect; +// EApplicationLauncher *app; +// QString helperApplication; + +}; + +#endif diff --git a/src/treeitem.cpp b/src/treeitem.cpp new file mode 100644 index 0000000..63070c4 --- /dev/null +++ b/src/treeitem.cpp @@ -0,0 +1,12 @@ +#include "treeitem.h" + +TreeItem::TreeItem(QTreeWidget *parent, const QStringList& items) + : QTreeWidgetItem(parent, items) +{ + +} + +bool TreeItem::operator< (const QTreeWidgetItem &other) const +{ + return this->data(0, Qt::UserRole).toInt() < other.data(0, Qt::UserRole).toInt(); +} diff --git a/src/treeitem.h b/src/treeitem.h new file mode 100644 index 0000000..4c99846 --- /dev/null +++ b/src/treeitem.h @@ -0,0 +1,18 @@ +#ifndef TREEITEM_H +#define TREEITEM_H + +#include <QTreeWidgetItem> + +class TreeItem : public QTreeWidgetItem +{ +public: + TreeItem(QTreeWidget *parent, const QStringList& items); + + bool operator <(const QTreeWidgetItem &other) const; +signals: + +public slots: + +}; + +#endif // TREEITEM_H diff --git a/src/ttablewidget.cpp b/src/ttablewidget.cpp new file mode 100644 index 0000000..a79f45a --- /dev/null +++ b/src/ttablewidget.cpp @@ -0,0 +1,61 @@ +#include <QtDebug> + +#include "ttablewidget.h" +#include <elettracolors.h> + +TTableWidget::TTableWidget(QWidget *p) : QTableWidget(p), QTangoComProxyReader(this) +{ + setColumnCount(16); + setAutoConfiguration(true); + connect(qtangoComHandle(), SIGNAL(newData(const TVariant&)), this, SLOT(refresh(const TVariant&)), + Qt::DirectConnection); + connect(qtangoComHandle(), SIGNAL(attributeAutoConfigured(const TangoConfigurationParameters *)), this, SLOT(configure(const TangoConfigurationParameters *))); + setDesiredAttributePropertyList(QStringList() << "labels"); +} + +void TTableWidget::configure(const TangoConfigurationParameters *cp) +{ + if(cp->propertyFound("labels")) + { + _labels = cp->attributePropertyValue("labels"); + printf("\e[1;32m configuring labels on ttable\e[0m\n"); + qslisttoc(_labels); + refresh(currentValue()); + } + else + perr("TTableWidget::configure(): source \"%s\" hasn't got the values property", qstoc(source())); + +} + +void TTableWidget::refresh(const TVariant& v) +{ + printf("\e[0;33mrefresh()\e[0m\n"); + if (v.quality() == ATTR_INVALID) + { + setDisabled(true); + } + else if (v.canConvertToBoolVector()) + { + setDisabled(false); + QVector<bool> data = v.toBoolVector(); + if (data == oldData) + return; + oldData = data; + clear(); + setRowCount(data.size() / columnCount() + (data.size()%columnCount() ? 1 : 0)); + for (unsigned int i = 0; i < data.size(); i++) + { + QTableWidgetItem *item = new QTableWidgetItem(); + item->setFlags(Qt::ItemIsEnabled); + item->setBackground(QBrush(EColor(data[i] ? Elettra::green : Elettra::red))); + item->setText(_labels.value(i)); + item->setTextAlignment(Qt::AlignCenter); + item->setToolTip(_descriptions.value(i)); + setItem(i / columnCount(), i % columnCount(), item); + printf("\e[1;36msetting label \"%s\" ", qstoc(_labels.value(i))); + } + printf("\n\e[1;32m --- done refresh()\e[0m\n"); + resizeColumnsToContents(); + } +} + diff --git a/src/ttablewidget.h b/src/ttablewidget.h new file mode 100644 index 0000000..b74ee28 --- /dev/null +++ b/src/ttablewidget.h @@ -0,0 +1,33 @@ +#ifndef TTABLEWIDGET_H +#define TTABLEWIDGET_H + +#include <QTableWidget> +#include <QStringList> +#include <qtangocore.h> +#include <com_proxy_reader.h> + +class TTableWidget : public QTableWidget, public QTangoComProxyReader +{ +Q_OBJECT + +public: + TTableWidget(QWidget * = 0); + + void setLabels(QStringList l){ _labels = l; }; + QStringList labels(){ return _labels; }; + + void setDescriptions(QStringList d){ _descriptions = d; }; + QStringList descriptions(){ return _descriptions; }; + +protected slots: + void refresh(const TVariant &v); + void configure(const TangoConfigurationParameters *cp); + +protected: + QStringList _labels; + QStringList _descriptions; + QVector<bool> oldData; + +}; + +#endif diff --git a/src/ttreeview.cpp b/src/ttreeview.cpp new file mode 100644 index 0000000..e1654e6 --- /dev/null +++ b/src/ttreeview.cpp @@ -0,0 +1,26 @@ +#include "ttreeview.h" +#include <QHeaderView> + +TTreeView::TTreeView(QWidget *parent) : + QTreeView(parent) +{ + header()->setSectionResizeMode(QHeaderView::ResizeToContents); +} + +void TTreeView::updateFilter() +{ + filter(mCurrentFilter); +} + +void TTreeView::filter(const QString& text) +{ + mCurrentFilter = text; + for(int i = 0; i< this->model()->rowCount(); i++) + { + if(text.isEmpty()) + setRowHidden(i, QModelIndex(), false); + + setRowHidden(i, QModelIndex(), + !(model()->data(model()->index(i, 1)).toString().contains(text, Qt::CaseInsensitive))); + } +} diff --git a/src/ttreeview.h b/src/ttreeview.h new file mode 100644 index 0000000..8988ffd --- /dev/null +++ b/src/ttreeview.h @@ -0,0 +1,24 @@ +#ifndef TTREEVIEW_H +#define TTREEVIEW_H + +#include <QTreeView> + +class TTreeView : public QTreeView +{ + Q_OBJECT +public: + explicit TTreeView(QWidget *parent = 0); + +signals: + +public slots: + void filter(const QString& text); + + void updateFilter(); + +private: + QString mCurrentFilter; + +}; + +#endif // TTREEVIEW_H diff --git a/src/ttreewidget.cpp b/src/ttreewidget.cpp new file mode 100644 index 0000000..d2df60e --- /dev/null +++ b/src/ttreewidget.cpp @@ -0,0 +1,184 @@ +#include <QtDebug> + +#include <QMenu> +#include <QContextMenuEvent> +#include <QHeaderView> +#include <QDateTime> +#include "ttreewidget.h" +#include <elettracolors.h> +#include <QScrollBar> +#include "treeitem.h" + +#define DECOLOR_COUNT_MAX 10 +#define DECOLOR_INTERVAL 500 /* milliseconds of decoloring effect */ + +TTreeWidget::TTreeWidget(QWidget *p) : QTreeWidget(p), QTangoComProxyReader(this), _dataSize(0) +{ + setAutoConfiguration(true); + setDesiredAttributePropertyList(QStringList() << "labels" << "positions"); + header()->setSectionResizeMode(QHeaderView::ResizeToContents); + setSortingEnabled(true); + sortByColumn(0); + connect(qtangoComHandle(), SIGNAL(newData(const TVariant&)), this, SLOT(refresh(const TVariant&)), + Qt::DirectConnection); + connect(qtangoComHandle(), SIGNAL(attributeAutoConfigured(const TangoConfigurationParameters *)), this, SLOT(configure(const TangoConfigurationParameters *))); + + tmpCounter = 1; + +} + +void TTreeWidget::configure(const TangoConfigurationParameters *cp) +{ + if(cp->propertyFound("labels")) + _labels = cp->attributePropertyValue("labels"); + + if(cp->propertyFound("positions")) + _descriptions = cp->attributePropertyValue("positions"); +} + +void TTreeWidget::refresh(const TVariant& v) +{ + /* save scrollbar value */ + int scrollPos = this->verticalScrollBar()->value(); + /* first time do not emit signal for sound */ + bool alarmPresent; +// setToolTip(getMessage()); + if (v.quality() == ATTR_INVALID) + { + setDisabled(true); + } +/* else if (canConvertToBoolVector()) + { + setDisabled(false); + clear(); + vector<bool> data = getDataAsBoolVector(); + QList<QTreeWidgetItem *> items; + for (unsigned int i = 0; i < data.size(); i++) + { + QTreeWidgetItem *item = new QTreeWidgetItem((QTreeWidget*) 0, QStringList(_labels.value(i))); + items.append(item); + } + insertTopLevelItems(0, items); + }*/ + else if (v.canConvertToDoubleVector()) + { + setDisabled(false); + QVector<double> data = v.toDoubleVector(); + // qSort(data.begin(), data.end()); + + if (data == oldData) + return; + + qDebug() << __FUNCTION__ << data << data.size(); + + double lastTimestamp = data.last(); + double previousLastTimestamp; + if(oldData.isEmpty()) /* first refresh, no matter if visible or not */ + { + previousLastTimestamp = QDateTime::currentDateTime().toTime_t(); + qDebug() << "setting last timestamp seen in first time " << objectName() << + previousLastTimestamp << QDateTime::currentDateTime(); + mLastTimestampSeen = previousLastTimestamp; + } + else + previousLastTimestamp = oldData.last(); + + alarmPresent = lastTimestamp > previousLastTimestamp && !oldData.isEmpty(); + if(lastTimestamp > previousLastTimestamp) + mAlarmColor = KRED; + + /* now we can update oldData, sorted */ + oldData = data; + clear(); + _dataSize = data.size(); + + + QList<QTreeWidgetItem *> items; + + for (int i = data.size() - 1; i >= 0; i--) + { + if (data[i]) + { + QStringList cells; + QDateTime date; + date.setTime_t((int) data[i]); + date = date.addMSecs((int)(1000 *(data[i] - (int) data[i]))); + cells << date.toString("d MMM yyyy hh:mm:ss.zzz") << _labels.value(i) << _descriptions.value(i) << QString().setNum(i); + TreeItem *item = new TreeItem(this, cells); + item->setData(0, Qt::UserRole, data[i]); + items.append(item); + if(data[i] > mLastTimestampSeen) /* red colour new alarms */ + { + QFont f = item->font(0); + f.setBold(true); + for(int i = 0; i < item->columnCount(); i++) + item->setFont(i, f); + } + } + } + insertTopLevelItems(0, items); + + if(alarmPresent) + { + emit newAlarmArrived(); + } + } + filter(mCurrentFilter); + + /* restore last scroll position */ + if(scrollPos != this->verticalScrollBar()->value()) + this->verticalScrollBar()->setValue(scrollPos); +} + +void TTreeWidget::filter(const QString& text) +{ + mCurrentFilter = text; + QList<QTreeWidgetItem *> items = this->findItems("*", Qt::MatchWildcard, 0); + if(text.isEmpty()) /* show all items */ + { + foreach(QTreeWidgetItem* it, items) + if(it->isHidden()) + it->setHidden(false); + } + else + { + foreach(QTreeWidgetItem* it, items) + { + it->setHidden(!it->text(1).contains(text, Qt::CaseInsensitive)); + } + } +} + +void TTreeWidget::ackSelected() +{ + emit acknowledgeSelected(); +} + +void TTreeWidget::ackAll() +{ + emit acknowledgeAll(); +} + +void TTreeWidget::contextMenuEvent(QContextMenuEvent *event) +{ + QMenu *menu = new QMenu(this); + menu->addAction("Acknowledge Selected", this, SLOT(ackSelected())); + menu->addAction("Acknowledge All", this, SLOT(ackAll())); + menu->exec(event->globalPos()); +} + +void TTreeWidget::hideEvent(QHideEvent *e) +{ + QTreeWidget::hideEvent(e); + mLastTimestampSeen = QDateTime::currentDateTime().toTime_t(); + qDebug() << "hide event: last timestamp seen for " << objectName() << QDateTime::currentDateTime() << mLastTimestampSeen; +} + +void TTreeWidget::showEvent(QShowEvent *e) +{ + QTreeWidget::showEvent(e); + mLastTimestampSeen = QDateTime::currentDateTime().toTime_t(); + qDebug() << "show event : last timestamp seen for " << objectName() << QDateTime::currentDateTime() << mLastTimestampSeen; + +} + diff --git a/src/ttreewidget.h b/src/ttreewidget.h new file mode 100644 index 0000000..ff88275 --- /dev/null +++ b/src/ttreewidget.h @@ -0,0 +1,56 @@ +#ifndef TTREEWIDGET_H +#define TTREEWIDGET_H + +#include <QTreeWidget> +#include <QStringList> +#include <qtangocore.h> +#include <com_proxy_reader.h> + +class TTreeWidget : public QTreeWidget, public QTangoComProxyReader +{ +Q_OBJECT + +public: + TTreeWidget(QWidget * = 0); + + void setLabels(QStringList l){ _labels = l; }; + QStringList labels(){ return _labels; }; + + void setDescriptions(QStringList d){ _descriptions = d; }; + QStringList descriptions(){ return _descriptions; }; + + unsigned int dataSize(){ return _dataSize; }; + +signals: + void acknowledgeAll(); + void acknowledgeSelected(); + void newAlarmArrived(); + +protected slots: + void refresh(const TVariant &v); + void configure(const TangoConfigurationParameters *cp); + void ackSelected(); + void ackAll(); + + void filter(const QString& text); + +protected: + void contextMenuEvent(QContextMenuEvent *); + QStringList _labels; + QStringList _descriptions; + unsigned int _dataSize; + QVector<double> oldData; + + void showEvent(QShowEvent *e); + void hideEvent(QHideEvent *); + +private: + QString mCurrentFilter; + int mDecolorCount; + QColor mAlarmColor; + double mLastTimestampSeen; + + int tmpCounter; +}; + +#endif -- GitLab