diff --git a/ground/openpilotgcs/src/plugins/notify/NotificationItem.h b/ground/openpilotgcs/src/plugins/notify/NotificationItem.h index 1898f1a55..d4929a1e5 100644 --- a/ground/openpilotgcs/src/plugins/notify/NotificationItem.h +++ b/ground/openpilotgcs/src/plugins/notify/NotificationItem.h @@ -63,8 +63,8 @@ public: QString getSayOrder() const { return _sayOrder; } void setSayOrder(QString text) { _sayOrder = text; } - double singleValue() const { return _singleValue; } - void setSingleValue(double value) { _singleValue = value; } + QVariant singleValue() const { return _singleValue; } + void setSingleValue(QVariant value) { _singleValue = value; } double valueRange2() const { return _valueRange2; } void setValueRange2(double value) { _valueRange2 = value; } @@ -131,6 +131,7 @@ public: QTimer* getTimer() const { return _timer; } void startTimer(int value); + void restartTimer(); void stopTimer(); void disposeTimer(); @@ -141,7 +142,7 @@ public: void disposeExpireTimer(); bool isNowPlaying; - bool firstStart; + bool _isPlayed; static QStringList sayOrderValues; static QStringList retryValues; @@ -184,8 +185,8 @@ private: //! order in what sounds 1-3 will be played QString _sayOrder; - //! one-side range, value maybe lower or greater - double _singleValue; + //! one-side range, value(numeric or ENUM type) maybe lower, greater or in range + QVariant _singleValue; //! both-side range, value should be inside the range //double _valueRange1; diff --git a/ground/openpilotgcs/src/plugins/notify/notificationitem.cpp b/ground/openpilotgcs/src/plugins/notify/notificationitem.cpp index 1b0e634a6..ec86aabcd 100644 --- a/ground/openpilotgcs/src/plugins/notify/notificationitem.cpp +++ b/ground/openpilotgcs/src/plugins/notify/notificationitem.cpp @@ -59,7 +59,7 @@ QStringList NotificationItem::retryValues; NotificationItem::NotificationItem(QObject *parent) : QObject(parent) , isNowPlaying(0) - , firstStart(true) + , _isPlayed(false) , _timer(NULL) , _expireTimer(NULL) , _soundCollectionPath("") @@ -77,10 +77,12 @@ NotificationItem::NotificationItem(QObject *parent) , _expireTimeout(eDefaultTimeout) , _mute(false) { + NotificationItem::sayOrderValues.clear(); NotificationItem::sayOrderValues.append(cStrBefore1st); NotificationItem::sayOrderValues.append(cStrBefore2nd); NotificationItem::sayOrderValues.append(cStrAfter2nd); + NotificationItem::retryValues.clear(); NotificationItem::retryValues.append(cStrRetryOnce); NotificationItem::retryValues.append(cStrRetryInstantly); NotificationItem::retryValues.append(cStrRetry10sec); @@ -92,7 +94,7 @@ NotificationItem::NotificationItem(QObject *parent) void NotificationItem::copyTo(NotificationItem* that) const { that->isNowPlaying = isNowPlaying; - that->firstStart = firstStart; + that->_isPlayed = _isPlayed; that->_soundCollectionPath = _soundCollectionPath; that->_currentLanguage = _currentLanguage; that->_soundCollectionPath = _soundCollectionPath; @@ -142,7 +144,8 @@ void NotificationItem::restoreState(QSettings* settings) setSound2(settings->value(QLatin1String("Sound2"), tr("")).toString()); setSound3(settings->value(QLatin1String("Sound3"), tr("")).toString()); setSayOrder(settings->value(QLatin1String("SayOrder"), tr("")).toString()); - setSingleValue(settings->value(QLatin1String("Value1"), tr("")).toDouble()); + QVariant value = settings->value(QLatin1String("Value1"), tr("")); + setSingleValue(value); setValueRange2(settings->value(QLatin1String("Value2"), tr("")).toDouble()); setRetryString(settings->value(QLatin1String("Repeat"), tr("")).toString()); setLifetime(settings->value(QLatin1String("ExpireTimeout"), tr("")).toInt()); @@ -185,16 +188,26 @@ void NotificationItem::deseriaize(QDataStream& stream) stream >> this->_mute; } -void NotificationItem::startTimer(int value) +void NotificationItem::startTimer(int msec) { if (!_timer) { _timer = new QTimer(this); - _timer->setInterval(value); + _timer->setInterval(msec); } if (!_timer->isActive()) _timer->start(); } + +void NotificationItem::restartTimer() +{ + if (!_timer) { + if (!_timer->isActive()) + _timer->start(); + } +} + + void NotificationItem::stopTimer() { if (_timer) { @@ -311,17 +324,26 @@ QStringList valueToSoundList(QString value) return digitWavs; } +QString stringFromValue(QVariant value, UAVObjectField* field) +{ + Q_ASSERT(field); + Q_ASSERT(!value.isNull()); + QString str; + if (UAVObjectField::ENUM == field->getType()) { + if(!field->getOptions().contains(value.toString())) + return QString(); + str = value.toString(); + } else { + str = QString("%L1").arg(value.toDouble()); + } + return str; +} + QString NotificationItem::toString() { QString str; - QString value; UAVObjectField* field = getUAVObjectField(); - if (UAVObjectField::ENUM == field->getType()) { - Q_ASSERT(singleValue() < field->getOptions().size()); - value = field->getOptions().at(singleValue()); - } else { - value = QString("%L1").arg(singleValue()); - } + QString value = stringFromValue(singleValue(), field); int pos = getValuePosition(getSayOrder().trimmed()); QStringList lst; @@ -353,15 +375,8 @@ QStringList& NotificationItem::toSoundList() // tips: // check of *.wav files exist needed for playing phonon queues; // if phonon player don't find next file in queue, it buzz - - QString value; UAVObjectField* field = getUAVObjectField(); - if (UAVObjectField::ENUM == field->getType()) { - Q_ASSERT(singleValue() < field->getOptions().size()); - value = field->getOptions().at(singleValue()); - } else { - value = QString("%L1").arg(singleValue()); - } + QString value = stringFromValue(singleValue(), field); // generate queue of sound files to play _messageSequence.clear(); diff --git a/ground/openpilotgcs/src/plugins/notify/notifylogging.cpp b/ground/openpilotgcs/src/plugins/notify/notifylogging.cpp index 0a29cc6e6..3cb11d111 100644 --- a/ground/openpilotgcs/src/plugins/notify/notifylogging.cpp +++ b/ground/openpilotgcs/src/plugins/notify/notifylogging.cpp @@ -31,7 +31,7 @@ #ifdef DEBUG_NOTIFIES_ENABLE QDebug qNotifyDebug() { - return qDebug(); + return qDebug() << "[NOTIFY_PLG]"; } #endif diff --git a/ground/openpilotgcs/src/plugins/notify/notifyplugin.cpp b/ground/openpilotgcs/src/plugins/notify/notifyplugin.cpp index 88cb23621..5ac05776a 100644 --- a/ground/openpilotgcs/src/plugins/notify/notifyplugin.cpp +++ b/ground/openpilotgcs/src/plugins/notify/notifyplugin.cpp @@ -48,7 +48,6 @@ static const QString VERSION = "1.0.0"; SoundNotifyPlugin::SoundNotifyPlugin() { phonon.mo = NULL; - configured = false; } SoundNotifyPlugin::~SoundNotifyPlugin() @@ -72,13 +71,10 @@ bool SoundNotifyPlugin::initialize(const QStringList& args, QString *errMsg) void SoundNotifyPlugin::extensionsInitialized() { Core::ICore::instance()->readSettings(this); - if ( !configured ){ - readConfig_0_0_0(); - } ExtensionSystem::PluginManager* pm = ExtensionSystem::PluginManager::instance(); connect(pm, SIGNAL(objectAdded(QObject*)), this, SLOT(onTelemetryManagerAdded(QObject*))); - removedNotifies.clear(); + _toRemoveNotifications.clear(); connectNotifications(); } @@ -96,46 +92,19 @@ void SoundNotifyPlugin::saveConfig( QSettings* settings, UAVConfigInfo *configIn settings->endGroup(); settings->beginWriteArray("listNotifies"); - for (int i = 0; i < lstNotifications.size(); i++) { + for (int i = 0; i < _notificationList.size(); i++) { settings->setArrayIndex(i); - lstNotifications.at(i)->saveState(settings); + _notificationList.at(i)->saveState(settings); } settings->endArray(); settings->setValue(QLatin1String("EnableSound"), enableSound); } -void SoundNotifyPlugin::readConfig( QSettings* settings, UAVConfigInfo *configInfo){ - - if ( configInfo->version() == UAVConfigVersion() ){ - // Just for migration to the new format. - configured = false; - return; - } - - settings->beginReadArray("Current"); - settings->setArrayIndex(0); - currentNotification.restoreState(settings); - settings->endArray(); - - // read list of notifications from settings - int size = settings->beginReadArray("listNotifies"); - for (int i = 0; i < size; ++i) { - settings->setArrayIndex(i); - NotificationItem* notification = new NotificationItem; - notification->restoreState(settings); - lstNotifications.append(notification); - } - settings->endArray(); - setEnableSound(settings->value(QLatin1String("EnableSound"),0).toBool()); - - configured = true; -} - -void SoundNotifyPlugin::readConfig_0_0_0(){ - - settings = Core::ICore::instance()->settings(); - settings->beginGroup(QLatin1String("NotifyPlugin")); +void SoundNotifyPlugin::readConfig( QSettings* settings, UAVConfigInfo *configInfo) +{ + // Just for migration to the new format. + //Q_ASSERT(configInfo->version() == UAVConfigVersion()); settings->beginReadArray("Current"); settings->setArrayIndex(0); @@ -148,18 +117,10 @@ void SoundNotifyPlugin::readConfig_0_0_0(){ settings->setArrayIndex(i); NotificationItem* notification = new NotificationItem; notification->restoreState(settings); - lstNotifications.append(notification); + _notificationList.append(notification); } settings->endArray(); setEnableSound(settings->value(QLatin1String("EnableSound"),0).toBool()); - settings->endGroup(); - - ExtensionSystem::PluginManager* pm = ExtensionSystem::PluginManager::instance(); - connect(pm, SIGNAL(objectAdded(QObject*)), this, SLOT(onTelemetryManagerAdded(QObject*))); - removedNotifies.clear(); - connectNotifications(); - - configured = true; } void SoundNotifyPlugin::onTelemetryManagerAdded(QObject* obj) @@ -186,11 +147,11 @@ void SoundNotifyPlugin::onAutopilotDisconnect() void SoundNotifyPlugin::resetNotification(void) { //first, reject empty args and unknown fields. - foreach(NotificationItem* ntf, lstNotifications) { + foreach(NotificationItem* ntf, _notificationList) { ntf->disposeTimer(); - disconnect(ntf->getTimer(), SIGNAL(timeout()), this, SLOT(repeatTimerHandler())); + disconnect(ntf->getTimer(), SIGNAL(timeout()), this, SLOT(on_timerRepeated_Notification())); ntf->disposeExpireTimer(); - disconnect(ntf->getExpireTimer(), SIGNAL(timeout()), this, SLOT(repeatTimerHandler())); + disconnect(ntf->getExpireTimer(), SIGNAL(timeout()), this, SLOT(on_timerRepeated_Notification())); } } @@ -200,10 +161,10 @@ void SoundNotifyPlugin::resetNotification(void) */ void SoundNotifyPlugin::updateNotificationList(QList list) { - removedNotifies.clear(); + _toRemoveNotifications.clear(); resetNotification(); - lstNotifications.clear(); - lstNotifications=list; + _notificationList.clear(); + _notificationList=list; connectNotifications(); Core::ICore::instance()->saveSettings(this); @@ -213,7 +174,7 @@ void SoundNotifyPlugin::connectNotifications() { foreach(UAVDataObject* obj,lstNotifiedUAVObjects) { if (obj != NULL) - disconnect(obj,SIGNAL(objectUpdated(UAVObject*)),this,SLOT(appendNotification(UAVObject*))); + disconnect(obj,SIGNAL(objectUpdated(UAVObject*)),this,SLOT(on_arrived_Notification(UAVObject*))); } if (phonon.mo != NULL) { delete phonon.mo; @@ -226,29 +187,33 @@ void SoundNotifyPlugin::connectNotifications() UAVObjectManager *objManager = pm->getObject(); lstNotifiedUAVObjects.clear(); - pendingNotifications.clear(); - lstNotifications.append(removedNotifies); - removedNotifies.clear(); + _pendingNotifications.clear(); + _notificationList.append(_toRemoveNotifications); + _toRemoveNotifications.clear(); //first, reject empty args and unknown fields. - foreach(NotificationItem* notify, lstNotifications) { - notify->firstStart=true; + foreach(NotificationItem* notify, _notificationList) { + notify->_isPlayed = false; notify->isNowPlaying=false; if(notify->mute()) continue; + // check is all sounds presented for notification, + // if not - we must not subscribe to it at all + if(notify->toSoundList().isEmpty()) continue; UAVDataObject* obj = dynamic_cast( objManager->getObject(notify->getDataObject()) ); if (obj != NULL ) { if (!lstNotifiedUAVObjects.contains(obj)) { lstNotifiedUAVObjects.append(obj); - connect(obj, SIGNAL(objectUpdated(UAVObject*)), this, SLOT(appendNotification(UAVObject*))); + + connect(obj, SIGNAL(objectUpdated(UAVObject*)), this, SLOT(on_arrived_Notification(UAVObject*))); } } else { qNotifyDebug() << "Error: Object is unknown (" << notify->getDataObject() << ")."; } } - if (lstNotifications.isEmpty()) return; + if (_notificationList.isEmpty()) return; // set notification message to current event phonon.mo = Phonon::createPlayer(Phonon::NotificationCategory); phonon.mo->clearQueue(); @@ -262,138 +227,66 @@ void SoundNotifyPlugin::connectNotifications() this, SLOT(stateChanged(Phonon::State,Phonon::State))); } -void SoundNotifyPlugin::appendNotification(UAVObject *object) +void SoundNotifyPlugin::on_arrived_Notification(UAVObject *object) { - disconnect(object, SIGNAL(objectUpdated(UAVObject*)), this, SLOT(appendNotification(UAVObject*))); - - foreach(NotificationItem* ntf, lstNotifications) { + foreach(NotificationItem* ntf, _notificationList) { if (object->getName() != ntf->getDataObject()) continue; - if (nowPlayingConfiguration == ntf) + + // skip duplicate notifications + if (_nowPlayingNotification == ntf) continue; + // skip periodical notifications + // this condition accepts: + // 1. Periodical notifications played firstly; + // NOTE: At first time it will be played, then it played only by timer, + // when conditions became false firstStart flag has been cleared and + // notification can be accepted again; + // 2. Once time notifications, they removed immediately after first playing; + // 3. Instant notifications(played one by one without interval); if (ntf->retryString() != "Repeat Instantly" && - ntf->retryString() != "Repeat Once" && !ntf->firstStart) + ntf->retryString() != "Repeat Once" && ntf->_isPlayed) continue; - checkNotificationRule(ntf,object); + qNotifyDebug() << QString("new notification: | %1 | %2 | val1: %3 | val2: %4") + .arg(ntf->getDataObject()) + .arg(ntf->getObjectField()) + .arg(ntf->singleValue().toString()) + .arg(ntf->valueRange2()); + + QString fieldName = ntf->getObjectField(); + UAVObjectField* field = object->getField(fieldName); + + qNotifyDebug() << QString("UAV object: %1 | value: %2") + .arg(object->getName()) + .arg(field->getValue().toString()); + + checkNotificationRule(ntf, object); } - connect(object, SIGNAL(objectUpdated(UAVObject*)), this, SLOT(appendNotification(UAVObject*))); + connect(object, SIGNAL(objectUpdated(UAVObject*)), + this, SLOT(on_arrived_Notification(UAVObject*)), Qt::UniqueConnection); } -void SoundNotifyPlugin::checkNotificationRule(NotificationItem* notification, UAVObject* object) -{ - bool condition=false; - double threshold; - QString direction; - QString fieldName; - - threshold = notification->singleValue(); - direction = notification->range(); - fieldName = notification->getObjectField(); - UAVObjectField* field = object->getField(fieldName); - - if (field->getName().isEmpty()) - return; - - double value = field->getDouble(); - switch(direction[0].toAscii()) - { - case 'E': - condition = (value == threshold); - break; - - case 'G': - condition = (value > threshold); - break; - - case 'L': - condition = (value < threshold); - break; - - case 'R': - condition = (value > threshold) && (value < notification->valueRange2()); - break; - - } - - if (!condition) - return; - - if (!playNotification(notification)) { - if (!pendingNotifications.contains(notification)) { - notification->stopTimer(); - - qNotifyDebug() << "add to pending list - " << notification->toString(); - // if audio is busy, start expiration timer - //ms = (notification->getExpiredTimeout()[in sec])*1000 - //QxtTimer::singleShot(notification->getExpireTimeout()*1000, this, SLOT(expirationTimerHandler(NotificationItem*)), qVariantFromValue(notification)); - pendingNotifications.append(notification); - notification->startExpireTimer(); - connect(notification->getExpireTimer(), SIGNAL(timeout()), this, SLOT(expireTimerHandler())); - } - } -} - -bool SoundNotifyPlugin::playNotification(NotificationItem* notification) -{ - // Check: race condition, if phonon.mo got deleted don't go further - if (phonon.mo == NULL) - return false; - - if (notification->mute()) - return false; - - qNotifyDebug() << "Phonon State: " << phonon.mo->state(); - if ((phonon.mo->state()==Phonon::PausedState) - || (phonon.mo->state()==Phonon::StoppedState) - || phonon.firstPlay) - { - // don't fire expire timer - nowPlayingConfiguration = notification; - notification->stopExpireTimer(); - - if (notification->retryString() == "Repeat Once") { - removedNotifies.append(lstNotifications.takeAt(lstNotifications.indexOf(notification))); - } else { - if (notification->retryString() != "Repeat Instantly") { - QRegExp rxlen("(\\d+)"); - QString value; - int timer_value; - int pos = rxlen.indexIn(notification->retryString()); - if (pos > -1) { - value = rxlen.cap(1); // "189" - timer_value = (value.toInt()+8)*1000; //ms*1000 + average duration of meassage - } - - notification->startTimer(timer_value); - connect(notification->getTimer(), SIGNAL(timeout()), this, SLOT(repeatTimerHandler())); - } - } - notification->firstStart=false; - phonon.mo->clear(); - qNotifyDebug() << "play notification - " << notification->toString(); - foreach (QString item, notification->toSoundList()) { - Phonon::MediaSource *ms = new Phonon::MediaSource(item); - ms->setAutoDelete(true); - phonon.mo->enqueue(*ms); - } - phonon.mo->play(); - phonon.firstPlay = false; // On Linux, you sometimes have to nudge Phonon to play 1 time before - // the state is not "Loading" anymore. - } else { - return false; // if audio is busy - } - return true; -} - -void SoundNotifyPlugin::repeatTimerHandler() +void SoundNotifyPlugin::on_timerRepeated_Notification() { NotificationItem* notification = static_cast(sender()->parent()); + if (!notification) + return; + // skip duplicate notifications + // WARNING: generally we shoudn't ever trap here + // this means, that timer fires to early and notification overlap itself + if (_nowPlayingNotification == notification) { + qNotifyDebug() << "WARN: on_timerRepeated - notification was skipped!"; + notification->restartTimer(); + return; + } - qNotifyDebug() << "repeatTimerHandler - " << notification->toString(); + qNotifyDebug() << QString("repeatTimer: %1% | %2 | %3").arg(notification->getDataObject()) + .arg(notification->getObjectField()) + .arg(notification->toString()); ExtensionSystem::PluginManager *pm = ExtensionSystem::PluginManager::instance(); UAVObjectManager *objManager = pm->getObject(); @@ -402,15 +295,23 @@ void SoundNotifyPlugin::repeatTimerHandler() checkNotificationRule(notification,object); } -void SoundNotifyPlugin::expireTimerHandler() + +void SoundNotifyPlugin::on_expiredTimer_Notification() { // fire expire timer NotificationItem* notification = static_cast(sender()->parent()); + if(!notification) + return; notification->stopExpireTimer(); - if (!pendingNotifications.isEmpty()) { - qNotifyDebug() << "expireTimerHandler - " << notification->toString(); - pendingNotifications.removeOne(notification); + volatile QMutexLocker lock(&_mutex); + + if (!_pendingNotifications.isEmpty()) { + qNotifyDebug() << QString("expireTimer: %1% | %2 | %3").arg(notification->getDataObject()) + .arg(notification->getObjectField()) + .arg(notification->toString()); + + _pendingNotifications.removeOne(notification); } } @@ -418,8 +319,7 @@ void SoundNotifyPlugin::stateChanged(Phonon::State newstate, Phonon::State oldst { Q_UNUSED(oldstate) - qNotifyDebug() << "File length (ms): " << phonon.mo->totalTime(); - qNotifyDebug() << "New State: " << newstate; + //qNotifyDebug() << "File length (ms): " << phonon.mo->totalTime(); #ifndef Q_OS_WIN // This is a hack to force Linux to wait until the end of the @@ -432,11 +332,18 @@ void SoundNotifyPlugin::stateChanged(Phonon::State newstate, Phonon::State oldst if ((newstate == Phonon::PausedState) || (newstate == Phonon::StoppedState)) { - nowPlayingConfiguration=NULL; - if (!pendingNotifications.isEmpty()) + qNotifyDebug() << "New State: " << QVariant(newstate).toString(); + + volatile QMutexLocker lock(&_mutex); + // assignment to NULL needed to detect that palying is finished + // it's useful in repeat timer handler, where we can detect + // that notification has not overlap with itself + _nowPlayingNotification = NULL; + + if (!_pendingNotifications.isEmpty()) { - NotificationItem* notification = pendingNotifications.takeFirst(); - qNotifyDebug() << "play audioFree - " << notification->toString(); + NotificationItem* notification = _pendingNotifications.takeFirst(); + qNotifyDebug_if(notification) << "play audioFree - " << notification->toString(); playNotification(notification); } } else { @@ -449,4 +356,163 @@ void SoundNotifyPlugin::stateChanged(Phonon::State newstate, Phonon::State oldst } } +bool checkRange(QString fieldValue, QString enumValue, QStringList values, char direction) +{ + + bool ret = false; + switch(direction) + { + case 'E': + ret = !QString::compare(enumValue, fieldValue, Qt::CaseInsensitive) ? true : false; + break; + + default: + ret = true; + break; + }; + return ret; +} + +bool checkRange(double fieldValue, double min, double max, char direction) +{ + bool ret = false; + Q_ASSERT(min < max); + switch(direction) + { + case 'E': + ret = (fieldValue == min); + break; + + case 'G': + ret = (fieldValue > min); + break; + + case 'L': + ret = (fieldValue < min); + break; + + default: + ret = (fieldValue > min) && (fieldValue < max); + break; + }; + + return ret; +} + +void SoundNotifyPlugin::checkNotificationRule(NotificationItem* notification, UAVObject* object) +{ + bool condition=false; + + if (notification->mute()) + return; + + QString direction = notification->range(); + QString fieldName = notification->getObjectField(); + UAVObjectField* field = object->getField(fieldName); + + if (field->getName().isEmpty()) + return; + + QVariant value = field->getValue(); + if(UAVObjectField::ENUM == field->getType()) { + condition = checkRange(value.toString(), + notification->singleValue().toString(), + field->getOptions(), + direction[0].toAscii()); + } else { + condition = checkRange(value.toDouble(), + notification->singleValue().toDouble(), + notification->valueRange2(), + direction[0].toAscii()); + } + + notification->_isPlayed = condition; + // if condition has been changed, and already in false state + // we should reset _isPlayed flag and stop repeat timer + if (!notification->_isPlayed) { + notification->stopTimer(); + return; + } + + volatile QMutexLocker lock(&_mutex); + + if (!playNotification(notification)) { + if (!_pendingNotifications.contains(notification) + && (_nowPlayingNotification != notification)) { + notification->stopTimer(); + + qNotifyDebug() << "add to pending list - " << notification->toString(); + // if audio is busy, start expiration timer + //ms = (notification->getExpiredTimeout()[in sec])*1000 + //QxtTimer::singleShot(notification->getExpireTimeout()*1000, this, SLOT(expirationTimerHandler(NotificationItem*)), qVariantFromValue(notification)); + _pendingNotifications.append(notification); + notification->startExpireTimer(); + connect(notification->getExpireTimer(), SIGNAL(timeout()), + this, SLOT(on_expiredTimer_Notification()), Qt::UniqueConnection); + } + } +} + +bool SoundNotifyPlugin::playNotification(NotificationItem* notification) +{ + if(!notification) + return false; + + // Check: race condition, if phonon.mo got deleted don't go further + if (phonon.mo == NULL) + return false; + + //qNotifyDebug() << "Phonon State: " << phonon.mo->state(); + + if ((phonon.mo->state()==Phonon::PausedState) + || (phonon.mo->state()==Phonon::StoppedState) + || phonon.firstPlay) + { + _nowPlayingNotification = notification; + notification->stopExpireTimer(); + + if (notification->retryString() == "Repeat Once") { + _toRemoveNotifications.append(_notificationList.takeAt(_notificationList.indexOf(notification))); + } else { + if (notification->retryString() != "Repeat Instantly") { + QRegExp rxlen("(\\d+)"); + QString value; + int timer_value; + int pos = rxlen.indexIn(notification->retryString()); + if (pos > -1) { + value = rxlen.cap(1); // "189" + + // needs to correct repeat timer value, + // acording to message play duration, + // we don't measure duration of each message, + // simply take average duration + enum { eAverageDurationSec = 8 }; + + enum { eSecToMsec = 1000 }; + + timer_value = (value.toInt() + eAverageDurationSec) * eSecToMsec; + } + + notification->startTimer(timer_value); + connect(notification->getTimer(), SIGNAL(timeout()), + this, SLOT(on_timerRepeated_Notification()), Qt::UniqueConnection); + } + } + phonon.mo->clear(); + qNotifyDebug() << "play: " << notification->toString(); + foreach (QString item, notification->toSoundList()) { + Phonon::MediaSource *ms = new Phonon::MediaSource(item); + ms->setAutoDelete(true); + phonon.mo->enqueue(*ms); + } + phonon.mo->play(); + phonon.firstPlay = false; // On Linux, you sometimes have to nudge Phonon to play 1 time before + // the state is not "Loading" anymore. + return true; + + } + + return false; +} + Q_EXPORT_PLUGIN(SoundNotifyPlugin) diff --git a/ground/openpilotgcs/src/plugins/notify/notifyplugin.h b/ground/openpilotgcs/src/plugins/notify/notifyplugin.h index 0b7a31f3b..6a142ff03 100644 --- a/ground/openpilotgcs/src/plugins/notify/notifyplugin.h +++ b/ground/openpilotgcs/src/plugins/notify/notifyplugin.h @@ -63,7 +63,7 @@ public: void shutdown(); - QList getListNotifications() { return lstNotifications; } + QList getListNotifications() { return _notificationList; } NotificationItem* getCurrentNotification(){ return ¤tNotification;} bool getEnableSound() const { return enableSound; } @@ -83,14 +83,12 @@ private slots: void connectNotifications(); void updateNotificationList(QList list); void resetNotification(void); - void appendNotification(UAVObject *object); - void repeatTimerHandler(void); - void expireTimerHandler(void); + void on_arrived_Notification(UAVObject *object); + void on_timerRepeated_Notification(void); + void on_expiredTimer_Notification(void); void stateChanged(Phonon::State newstate, Phonon::State oldstate); private: - - bool configured; // just for migration,delete later bool enableSound; QList< QList* > lstMediaSource; QStringList mediaSource; @@ -98,17 +96,17 @@ private: QSettings* settings; QList lstNotifiedUAVObjects; - QList lstNotifications; - QList pendingNotifications; - QList removedNotifies; + QList _notificationList; + QList _pendingNotifications; + QList _toRemoveNotifications; NotificationItem currentNotification; - NotificationItem* nowPlayingConfiguration; + NotificationItem* _nowPlayingNotification; - QString m_field; PhononObject phonon; - NotifyPluginOptionsPage *mop; + NotifyPluginOptionsPage* mop; TelemetryManager* telMngr; + QMutex _mutex; }; #endif // SOUNDNOTIFYPLUGIN_H diff --git a/ground/openpilotgcs/src/plugins/notify/notifypluginoptionspage.cpp b/ground/openpilotgcs/src/plugins/notify/notifypluginoptionspage.cpp index fdf0f5cc9..49b274e38 100644 --- a/ground/openpilotgcs/src/plugins/notify/notifypluginoptionspage.cpp +++ b/ground/openpilotgcs/src/plugins/notify/notifypluginoptionspage.cpp @@ -57,11 +57,10 @@ NotifyPluginOptionsPage::NotifyPluginOptionsPage(QObject *parent) : IOptionsPage(parent) , _objManager(*ExtensionSystem::PluginManager::instance()->getObject()) , _owner(qobject_cast(parent)) - , _currentCollectionPath("") - , _valueRange(NULL) + , _dynamicFieldLimit(NULL) + , _dynamicFieldWidget(NULL) + , _dynamicFieldType(-1) , _sayOrder(NULL) - , _fieldValue(NULL) - , _fieldType(-1) , _form(NULL) , _selectedNotification(NULL) {} @@ -74,23 +73,21 @@ QWidget *NotifyPluginOptionsPage::createPage(QWidget *parent) _optionsPage.reset(new Ui::NotifyPluginOptionsPage()); //main widget QWidget* optionsPageWidget = new QWidget; - _fieldValue = NULL; - _valueRange = NULL; + _dynamicFieldWidget = NULL; + _dynamicFieldLimit = NULL; resetFieldType(); //save ref to form, needed for binding dynamic fields in future _form = optionsPageWidget; //main layout _optionsPage->setupUi(optionsPageWidget); - _listSoundFiles.clear(); - _optionsPage->SoundDirectoryPathChooser->setExpectedKind(Utils::PathChooser::Directory); _optionsPage->SoundDirectoryPathChooser->setPromptDialogTitle(tr("Choose sound collection directory")); connect(_optionsPage->SoundDirectoryPathChooser, SIGNAL(changed(const QString&)), - this, SLOT(on_buttonSoundFolder_clicked(const QString&))); + this, SLOT(on_clicked_buttonSoundFolder(const QString&))); connect(_optionsPage->SoundCollectionList, SIGNAL(currentIndexChanged (int)), - this, SLOT(on_soundLanguage_indexChanged(int))); + this, SLOT(on_changedIndex_soundLanguage(int))); connect(this, SIGNAL(updateNotifications(QList)), _owner, SLOT(updateNotificationList(QList))); @@ -100,8 +97,8 @@ QWidget *NotifyPluginOptionsPage::createPage(QWidget *parent) // [1] - _selectedNotification = _owner->getCurrentNotification(); - addDynamicValueLayout(); + setSelectedNotification(_owner->getCurrentNotification()); + addDynamicFieldLayout(); // [2] updateConfigView(_selectedNotification); @@ -109,7 +106,8 @@ QWidget *NotifyPluginOptionsPage::createPage(QWidget *parent) initButtons(); initPhononPlayer(); - _notifyRulesSelection->setCurrentIndex(_notifyRulesModel->index(0, 0, QModelIndex()), + int curr_row = _privListNotifications.indexOf(_selectedNotification); + _notifyRulesSelection->setCurrentIndex(_notifyRulesModel->index(curr_row, 0, QModelIndex()), QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows); return optionsPageWidget; @@ -125,151 +123,41 @@ void NotifyPluginOptionsPage::apply() void NotifyPluginOptionsPage::finish() { disconnect(_optionsPage->UAVObjectField, SIGNAL(currentIndexChanged(QString)), - this, SLOT(on_UAVField_indexChanged(QString))); + this, SLOT(on_changedIndex_UAVField(QString))); - disconnect(_notifySound.data(),SIGNAL(stateChanged(Phonon::State,Phonon::State)), - this,SLOT(on_changeButtonText(Phonon::State,Phonon::State))); - if (_notifySound) { - _notifySound->stop(); - _notifySound->clear(); + disconnect(_testSound.data(),SIGNAL(stateChanged(Phonon::State,Phonon::State)), + this,SLOT(on_changed_playButtonText(Phonon::State,Phonon::State))); + if (_testSound) { + _testSound->stop(); + _testSound->clear(); } } -void NotifyPluginOptionsPage::addDynamicValueLayout() -{ - NotificationItem* curr = _owner->getCurrentNotification(); - Q_ASSERT(curr); - _optionsPage->dynamicValueLayout->addWidget(new QLabel("Say order ", _form)); - - _sayOrder = new QComboBox(_form); - _optionsPage->dynamicValueLayout->addWidget(_sayOrder); - _sayOrder->addItems(NotificationItem::sayOrderValues); - - _optionsPage->dynamicValueLayout->addWidget(new QLabel("Value is ", _form)); - - UAVDataObject* obj = dynamic_cast(_objManager.getObject(curr->getDataObject())); - UAVObjectField* field = obj->getField(curr->getObjectField()); - Q_ASSERT(obj); - Q_ASSERT(field); - _valueRange = new QComboBox(_form); - _optionsPage->dynamicValueLayout->addWidget(_valueRange); - - addDynamicField(field); -} - -void NotifyPluginOptionsPage::resetValueRange() -{ - (static_cast(_fieldValue))->setInputMask("999.99 - 999.99;"); - (static_cast(_fieldValue))->setText("0000000000"); - (static_cast(_fieldValue))->setCursorPosition(0); -} - -void NotifyPluginOptionsPage::on_rangeValue_indexChanged(QString rangeStr) -{ - Q_ASSERT(_fieldValue); - UAVObjectField* field = getObjectFieldFromPage(); - Q_ASSERT(!!field); - setDynamicValueWidget(field); - setDynamicValueField(_selectedNotification); -} - -void NotifyPluginOptionsPage::resetFieldType() -{ - _fieldType = -1; -} - -void NotifyPluginOptionsPage::addDynamicField(UAVObjectField* objField) -{ - //qDebugNotify_ if (!objField || !parent) << "null input params"; - Q_ASSERT(objField); - if (objField->getType() == _fieldType) { - if (QComboBox* fieldValue = dynamic_cast(_fieldValue)) { - fieldValue->clear(); - QStringList enumValues(objField->getOptions()); - fieldValue->addItems(enumValues); - } - return; - } - - disconnect(_valueRange, SIGNAL(currentIndexChanged(QString)), - this, SLOT(on_rangeValue_indexChanged(QString))); - - _valueRange->clear(); - QStringList rangeValues; - if (UAVObjectField::ENUM == objField->getType()) { - rangeValues << cStrEqualTo << cStrInRange; - _valueRange->addItems(rangeValues); - _valueRange->setCurrentIndex(rangeValues.indexOf(_selectedNotification->range())); - - } else { - rangeValues << cStrEqualTo << cStrLargeThan << cStrLowerThan << cStrInRange; - _valueRange->addItems(rangeValues); - connect(_valueRange, SIGNAL(currentIndexChanged(QString)), - this, SLOT(on_rangeValue_indexChanged(QString))); - } - - setDynamicValueWidget(objField); -} - -void NotifyPluginOptionsPage::setDynamicValueWidget(UAVObjectField* objField) -{ - Q_ASSERT(_valueRange); - - // check if dynamic fileld already settled, - // so if its exists remove it and install new field - if (_fieldValue) { - _optionsPage->dynamicValueLayout->removeWidget(_fieldValue); - delete _fieldValue; - _fieldValue = NULL; - } - _fieldType = objField->getType(); - switch(_fieldType) - { - case UAVObjectField::ENUM: - { - _fieldValue = new QComboBox(_form); - QStringList enumValues(objField->getOptions()); - (dynamic_cast(_fieldValue))->addItems(enumValues); - } - break; - - default: - if (_valueRange->currentText() == cStrInRange) { - _fieldValue = new QLineEdit(_form); - resetValueRange(); - } else { - _fieldValue = new QSpinBox(_form); - } - break; - }; - _optionsPage->dynamicValueLayout->addWidget(_fieldValue); -} - void NotifyPluginOptionsPage::initButtons() { _optionsPage->chkEnableSound->setChecked(_owner->getEnableSound()); connect(_optionsPage->chkEnableSound, SIGNAL(toggled(bool)), - this, SLOT(on_checkEnableSound_toggled(bool))); + this, SLOT(on_toggled_checkEnableSound(bool))); _optionsPage->buttonModify->setEnabled(false); _optionsPage->buttonDelete->setEnabled(false); _optionsPage->buttonPlayNotification->setEnabled(false); connect(_optionsPage->buttonAdd, SIGNAL(pressed()), - this, SLOT(on_button_AddNotification_clicked())); + this, SLOT(on_clicked_buttonAddNotification())); connect(_optionsPage->buttonDelete, SIGNAL(pressed()), - this, SLOT(on_button_DeleteNotification_clicked())); + this, SLOT(on_clicked_buttonDeleteNotification())); connect(_optionsPage->buttonModify, SIGNAL(pressed()), - this, SLOT(on_button_ModifyNotification_clicked())); + this, SLOT(on_clicked_buttonModifyNotification())); connect(_optionsPage->buttonPlayNotification, SIGNAL(clicked()), - this, SLOT(on_button_TestSoundNotification_clicked())); + this, SLOT(on_clicked_buttonTestSoundNotification())); } void NotifyPluginOptionsPage::initPhononPlayer() { - _notifySound.reset(Phonon::createPlayer(Phonon::NotificationCategory)); - connect(_notifySound.data(),SIGNAL(stateChanged(Phonon::State,Phonon::State)), - this,SLOT(on_changeButtonText(Phonon::State,Phonon::State))); - connect(_notifySound.data(), SIGNAL(finished(void)), this, SLOT(on_FinishedPlaying(void))); + _testSound.reset(Phonon::createPlayer(Phonon::NotificationCategory)); + connect(_testSound.data(),SIGNAL(stateChanged(Phonon::State,Phonon::State)), + this,SLOT(on_changed_playButtonText(Phonon::State,Phonon::State))); + connect(_testSound.data(), SIGNAL(finished(void)), this, SLOT(on_FinishedPlaying(void))); } void NotifyPluginOptionsPage::initRulesTable() @@ -280,7 +168,7 @@ void NotifyPluginOptionsPage::initRulesTable() _notifyRulesSelection = new QItemSelectionModel(_notifyRulesModel.data()); connect(_notifyRulesSelection, SIGNAL(selectionChanged ( const QItemSelection &, const QItemSelection & )), - this, SLOT(on_table_changeSelection( const QItemSelection & , const QItemSelection & ))); + this, SLOT(on_changedSelection_notifyTable( const QItemSelection & , const QItemSelection & ))); connect(this, SIGNAL(entryUpdated(int)), _notifyRulesModel.data(), SLOT(entryUpdated(int))); @@ -299,6 +187,159 @@ void NotifyPluginOptionsPage::initRulesTable() _optionsPage->notifyRulesView->setDragDropMode(QAbstractItemView::InternalMove); } +UAVObjectField* NotifyPluginOptionsPage::getObjectFieldFromSelected() +{ + return (_currUAVObject) ? _currUAVObject->getField(_selectedNotification->getObjectField()) : NULL; +} + +void NotifyPluginOptionsPage::setSelectedNotification(NotificationItem* ntf) +{ + _selectedNotification = ntf; + _currUAVObject = dynamic_cast(_objManager.getObject(_selectedNotification->getDataObject())); + if(!_currUAVObject) { + qNotifyDebug() << "no such UAVObject: " << _selectedNotification->getDataObject(); + } +} + +void NotifyPluginOptionsPage::addDynamicFieldLayout() +{ + // there is no need to check (objField == NULL), + // thus it doesn't use in this field directly + + QSizePolicy labelSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + labelSizePolicy.setHorizontalStretch(0); + labelSizePolicy.setVerticalStretch(0); +// labelSizePolicy.setHeightForWidth(UAVObject->sizePolicy().hasHeightForWidth()); + + + QLabel* labelSayOrder = new QLabel("Say order ", _form); + labelSayOrder->setSizePolicy(labelSizePolicy); + + _optionsPage->dynamicValueLayout->addWidget(labelSayOrder); + _sayOrder = new QComboBox(_form); + _optionsPage->dynamicValueLayout->addWidget(_sayOrder); + _sayOrder->addItems(NotificationItem::sayOrderValues); + + QLabel* labelValueIs = new QLabel("Value is ", _form); + labelValueIs->setSizePolicy(labelSizePolicy); + _optionsPage->dynamicValueLayout->addWidget(labelValueIs); + + _dynamicFieldLimit = new QComboBox(_form); + _optionsPage->dynamicValueLayout->addWidget(_dynamicFieldLimit); + UAVObjectField* field = getObjectFieldFromSelected(); + addDynamicField(field); +} + +void NotifyPluginOptionsPage::addDynamicField(UAVObjectField* objField) +{ + if(!objField) { + qNotifyDebug() << "addDynamicField | input objField == NULL"; + return; + } + if (objField->getType() == _dynamicFieldType) { + if (QComboBox* fieldValue = dynamic_cast(_dynamicFieldWidget)) { + fieldValue->clear(); + QStringList enumValues(objField->getOptions()); + fieldValue->addItems(enumValues); + } + return; + } + + disconnect(_dynamicFieldLimit, SIGNAL(currentIndexChanged(QString)), + this, SLOT(on_changedIndex_rangeValue(QString))); + + _dynamicFieldLimit->clear(); + QStringList rangeValues; + if (UAVObjectField::ENUM == objField->getType()) { + rangeValues << cStrEqualTo << cStrInRange; + _dynamicFieldLimit->addItems(rangeValues); + _dynamicFieldLimit->setCurrentIndex(rangeValues.indexOf(_selectedNotification->range())); + + } else { + rangeValues << cStrEqualTo << cStrLargeThan << cStrLowerThan << cStrInRange; + _dynamicFieldLimit->addItems(rangeValues); + connect(_dynamicFieldLimit, SIGNAL(currentIndexChanged(QString)), + this, SLOT(on_changedIndex_rangeValue(QString))); + } + + addDynamicFieldWidget(objField); +} + +void NotifyPluginOptionsPage::addDynamicFieldWidget(UAVObjectField* objField) +{ + if(!objField) { + qNotifyDebug() << "objField == NULL!"; + return; + } + // check if dynamic fileld already settled, + // so if its exists remove it and install new field + if (_dynamicFieldWidget) { + _optionsPage->dynamicValueLayout->removeWidget(_dynamicFieldWidget); + delete _dynamicFieldWidget; + _dynamicFieldWidget = NULL; + } + + QSizePolicy sizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + sizePolicy.setHorizontalStretch(0); + sizePolicy.setVerticalStretch(0); + + _dynamicFieldType = objField->getType(); + switch(_dynamicFieldType) + { + case UAVObjectField::ENUM: + { + _dynamicFieldWidget = new QComboBox(_form); + QStringList enumValues(objField->getOptions()); + (dynamic_cast(_dynamicFieldWidget))->addItems(enumValues); + } + break; + + default: + Q_ASSERT(_dynamicFieldLimit); + if (_dynamicFieldLimit->currentText() == cStrInRange) { + _dynamicFieldWidget = new QLineEdit(_form); + + (static_cast(_dynamicFieldWidget))->setInputMask("999.99 - 999.99;"); + (static_cast(_dynamicFieldWidget))->setText("0000000000"); + (static_cast(_dynamicFieldWidget))->setCursorPosition(0); + } else { + _dynamicFieldWidget = new QDoubleSpinBox(_form); + (dynamic_cast(_dynamicFieldWidget))->setRange(000.00, 999.99); + } + break; + }; + enum { eDynamicFieldWidth = 100 }; + _dynamicFieldWidget->setSizePolicy(sizePolicy); + _dynamicFieldWidget->setFixedWidth(eDynamicFieldWidth); + _optionsPage->dynamicValueLayout->addWidget(_dynamicFieldWidget); +} + +void NotifyPluginOptionsPage::setDynamicFieldValue(NotificationItem* notification) +{ + if (QDoubleSpinBox* seValue = dynamic_cast(_dynamicFieldWidget)) + seValue->setValue(notification->singleValue().toDouble()); + else { + if (QComboBox* cbValue = dynamic_cast(_dynamicFieldWidget)) { + int idx = cbValue->findText(notification->singleValue().toString()); + if(-1 != idx) + cbValue->setCurrentIndex(idx); + } else { + if (QLineEdit* rangeValue = dynamic_cast(_dynamicFieldWidget)) { + QString str = QString("%1%2").arg(notification->singleValue().toDouble(), 5, 'f', 2, '0') + .arg(notification->valueRange2(), 5, 'f', 2, '0'); + rangeValue->setText(str); + } else { + qNotifyDebug() << "NotifyPluginOptionsPage::setDynamicValueField | unknown _fieldValue: " << _dynamicFieldWidget; + } + } + } +} + +void NotifyPluginOptionsPage::resetFieldType() +{ + _dynamicFieldType = -1; +} + void NotifyPluginOptionsPage::getOptionsPageValues(NotificationItem* notification) { Q_ASSERT(notification); @@ -310,14 +351,14 @@ void NotifyPluginOptionsPage::getOptionsPageValues(NotificationItem* notificatio notification->setSound2(_optionsPage->Sound2->currentText()); notification->setSound3(_optionsPage->Sound3->currentText()); notification->setSayOrder(_sayOrder->currentText()); - notification->setRange(_valueRange->currentText()); - if (QSpinBox* spinValue = dynamic_cast(_fieldValue)) + notification->setRange(_dynamicFieldLimit->currentText()); + if (QDoubleSpinBox* spinValue = dynamic_cast(_dynamicFieldWidget)) notification->setSingleValue(spinValue->value()); else { - if (QComboBox* comboBoxValue = dynamic_cast(_fieldValue)) - notification->setSingleValue(comboBoxValue->currentIndex()); + if (QComboBox* comboBoxValue = dynamic_cast(_dynamicFieldWidget)) + notification->setSingleValue(comboBoxValue->currentText()); else { - if (QLineEdit* rangeValue = dynamic_cast(_fieldValue)) { + if (QLineEdit* rangeValue = dynamic_cast(_dynamicFieldWidget)) { QString str = rangeValue->text(); QStringList range = str.split('-'); notification->setSingleValue(range.at(0).toDouble()); @@ -327,113 +368,13 @@ void NotifyPluginOptionsPage::getOptionsPageValues(NotificationItem* notificatio } } -void NotifyPluginOptionsPage::on_UAVField_indexChanged(QString field) -{ - resetFieldType(); - UAVDataObject* obj = dynamic_cast( _objManager.getObject(_optionsPage->UAVObject->currentText())); - addDynamicField(obj->getField(field)); - -} - -void NotifyPluginOptionsPage::on_UAVObject_indexChanged(QString val) -{ - resetFieldType(); - UAVDataObject* obj = dynamic_cast( _objManager.getObject(val) ); - QList fieldList = obj->getFields(); - disconnect(_optionsPage->UAVObjectField, SIGNAL(currentIndexChanged(QString)), this, SLOT(on_UAVField_indexChanged(QString))); - _optionsPage->UAVObjectField->clear(); - foreach (UAVObjectField* field, fieldList) { - _optionsPage->UAVObjectField->addItem(field->getName()); - } - connect(_optionsPage->UAVObjectField, SIGNAL(currentIndexChanged(QString)), this, SLOT(on_UAVField_indexChanged(QString))); - addDynamicField(fieldList.at(0)); -} - -void NotifyPluginOptionsPage::on_buttonSoundFolder_clicked(const QString& path) -{ - QDir dirPath(path); - _listDirCollections = dirPath.entryList(QDir::AllDirs | QDir::NoDotAndDotDot); - _optionsPage->SoundCollectionList->clear(); - _optionsPage->SoundCollectionList->addItems(_listDirCollections); -} - -void NotifyPluginOptionsPage::on_soundLanguage_indexChanged(int index) -{ - _optionsPage->SoundCollectionList->setCurrentIndex(index); - _currentCollectionPath = _optionsPage->SoundDirectoryPathChooser->path() - + QDir::toNativeSeparators("/" + _optionsPage->SoundCollectionList->currentText()); - - QDir dirPath(_currentCollectionPath); - QStringList filters; - filters << "*.mp3" << "*.wav"; - dirPath.setNameFilters(filters); - _listSoundFiles = dirPath.entryList(filters); - _listSoundFiles.replaceInStrings(QRegExp(".mp3|.wav"), ""); - _optionsPage->Sound1->clear(); - _optionsPage->Sound2->clear(); - _optionsPage->Sound3->clear(); - _optionsPage->Sound1->addItems(_listSoundFiles); - _optionsPage->Sound2->addItem(""); - _optionsPage->Sound2->addItems(_listSoundFiles); - _optionsPage->Sound3->addItem(""); - _optionsPage->Sound3->addItems(_listSoundFiles); -} - -void NotifyPluginOptionsPage::on_changeButtonText(Phonon::State newstate, Phonon::State oldstate) -{ - //Q_ASSERT(Phonon::ErrorState != newstate); - - if (newstate == Phonon::PausedState || newstate == Phonon::StoppedState) { - _optionsPage->buttonPlayNotification->setText("Play"); - _optionsPage->buttonPlayNotification->setIcon(QPixmap(":/notify/images/play.png")); - } else { - if (newstate == Phonon::PlayingState) { - _optionsPage->buttonPlayNotification->setText("Stop"); - _optionsPage->buttonPlayNotification->setIcon(QPixmap(":/notify/images/stop.png")); - } - } -} - -void NotifyPluginOptionsPage::on_FinishedPlaying() -{ - _notifySound->clear(); -} - -void NotifyPluginOptionsPage::on_button_TestSoundNotification_clicked() -{ - NotificationItem* notification = NULL; - qNotifyDebug() << "on_buttonTestSoundNotification_clicked"; - Q_ASSERT(-1 != _notifyRulesSelection->currentIndex().row()); - _notifySound->clearQueue(); - notification = _privListNotifications.at(_notifyRulesSelection->currentIndex().row()); - QStringList sequence = notification->toSoundList(); - if (sequence.isEmpty()) { - qNotifyDebug() << "message sequense is empty!"; - return; - } - foreach(QString item, sequence) { - qNotifyDebug() << item; - _notifySound->enqueue(Phonon::MediaSource(item)); - } - _notifySound->play(); -} - -void NotifyPluginOptionsPage::on_checkEnableSound_toggled(bool state) -{ - bool state1 = 1^state; - - QList listOutputs = _notifySound->outputPaths(); - Phonon::AudioOutput * audioOutput = (Phonon::AudioOutput*)listOutputs.last().sink(); - audioOutput->setMuted(state1); -} - void NotifyPluginOptionsPage::updateConfigView(NotificationItem* notification) { Q_ASSERT(notification); disconnect(_optionsPage->UAVObject, SIGNAL(currentIndexChanged(QString)), - this, SLOT(on_UAVObject_indexChanged(QString))); + this, SLOT(on_changedIndex_UAVObject(QString))); disconnect(_optionsPage->UAVObjectField, SIGNAL(currentIndexChanged(QString)), - this, SLOT(on_UAVField_indexChanged(QString))); + this, SLOT(on_changedIndex_UAVField(QString))); QString path = notification->getSoundCollectionPath(); if (path.isEmpty()) { @@ -461,13 +402,10 @@ void NotifyPluginOptionsPage::updateConfigView(NotificationItem* notification) } _optionsPage->UAVObjectField->clear(); - QString uavDataObject = notification->getDataObject(); - UAVDataObject* obj = dynamic_cast(_objManager.getObject(uavDataObject)); - if (obj != NULL ) { - QList fieldList = obj->getFields(); - foreach (UAVObjectField* field, fieldList) { + if(_currUAVObject) { + QList fieldList = _currUAVObject->getFields(); + foreach (UAVObjectField* field, fieldList) _optionsPage->UAVObjectField->addItem(field->getName()); - } } if (-1 != _optionsPage->UAVObjectField->findText(notification->getObjectField())) { @@ -498,63 +436,105 @@ void NotifyPluginOptionsPage::updateConfigView(NotificationItem* notification) _optionsPage->Sound3->setCurrentIndex(_optionsPage->Sound3->findText(notification->getSound3())); } - if (-1 != _valueRange->findText(notification->range())) { - _valueRange->setCurrentIndex(_valueRange->findText(notification->range())); + if (-1 != _dynamicFieldLimit->findText(notification->range())) { + _dynamicFieldLimit->setCurrentIndex(_dynamicFieldLimit->findText(notification->range())); } if (-1 != _sayOrder->findText(notification->getSayOrder())) { _sayOrder->setCurrentIndex(_sayOrder->findText(notification->getSayOrder())); } - setDynamicValueField(notification); + setDynamicFieldValue(notification); connect(_optionsPage->UAVObject, SIGNAL(currentIndexChanged(QString)), - this, SLOT(on_UAVObject_indexChanged(QString))); + this, SLOT(on_changedIndex_UAVObject(QString))); connect(_optionsPage->UAVObjectField, SIGNAL(currentIndexChanged(QString)), - this, SLOT(on_UAVField_indexChanged(QString))); + this, SLOT(on_changedIndex_UAVField(QString))); } -void NotifyPluginOptionsPage::setDynamicValueField(NotificationItem* notification) +void NotifyPluginOptionsPage::on_changedIndex_rangeValue(QString rangeStr) { - if (QSpinBox* spinValue = dynamic_cast(_fieldValue)) - spinValue->setValue(notification->singleValue()); - else { - if (QComboBox* comboBoxValue = dynamic_cast(_fieldValue)) - comboBoxValue->setCurrentIndex(notification->singleValue()); - else { - if (QLineEdit* rangeValue = dynamic_cast(_fieldValue)) { - //resetValueRange(); - QString str = QString("%1%2").arg(notification->singleValue(), 5, 'f', 2, '0') - .arg(notification->valueRange2(), 5, 'f', 2, '0'); - rangeValue->setText(str); - } else { - qNotifyDebug() << "NotifyPluginOptionsPage::setDynamicValueField | unknown _fieldValue: " << _fieldValue; - } + Q_ASSERT(_dynamicFieldWidget); + UAVObjectField* field = getObjectFieldFromSelected(); + Q_ASSERT(!!field); + addDynamicFieldWidget(field); + setDynamicFieldValue(_selectedNotification); +} + +void NotifyPluginOptionsPage::on_changedIndex_UAVField(QString field) +{ + resetFieldType(); + Q_ASSERT(_currUAVObject); + addDynamicField(_currUAVObject->getField(field)); + +} + +void NotifyPluginOptionsPage::on_changedIndex_UAVObject(QString val) +{ + resetFieldType(); + _currUAVObject = dynamic_cast( _objManager.getObject(val) ); + if(!_currUAVObject) { + qNotifyDebug() << "on_UAVObject_indexChanged | no such UAVOBject"; + return; + } + QList fieldList = _currUAVObject->getFields(); + disconnect(_optionsPage->UAVObjectField, SIGNAL(currentIndexChanged(QString)), this, SLOT(on_changedIndex_UAVField(QString))); + _optionsPage->UAVObjectField->clear(); + foreach (UAVObjectField* field, fieldList) { + _optionsPage->UAVObjectField->addItem(field->getName()); + } + connect(_optionsPage->UAVObjectField, SIGNAL(currentIndexChanged(QString)), this, SLOT(on_changedIndex_UAVField(QString))); + _selectedNotification->setObjectField(fieldList.at(0)->getName()); + addDynamicField(fieldList.at(0)); +} + + +void NotifyPluginOptionsPage::on_changedIndex_soundLanguage(int index) +{ + _optionsPage->SoundCollectionList->setCurrentIndex(index); + QString collectionPath = _optionsPage->SoundDirectoryPathChooser->path() + + QDir::toNativeSeparators("/" + _optionsPage->SoundCollectionList->currentText()); + + QDir dirPath(collectionPath); + QStringList filters; + filters << "*.mp3" << "*.wav"; + dirPath.setNameFilters(filters); + QStringList listSoundFiles = dirPath.entryList(filters); + listSoundFiles.replaceInStrings(QRegExp(".mp3|.wav"), ""); + _optionsPage->Sound1->clear(); + _optionsPage->Sound2->clear(); + _optionsPage->Sound3->clear(); + _optionsPage->Sound1->addItems(listSoundFiles); + _optionsPage->Sound2->addItem(""); + _optionsPage->Sound2->addItems(listSoundFiles); + _optionsPage->Sound3->addItem(""); + _optionsPage->Sound3->addItems(listSoundFiles); +} + + +void NotifyPluginOptionsPage::on_changed_playButtonText(Phonon::State newstate, Phonon::State oldstate) +{ + //Q_ASSERT(Phonon::ErrorState != newstate); + + if (newstate == Phonon::PausedState || newstate == Phonon::StoppedState) { + _optionsPage->buttonPlayNotification->setText("Play"); + _optionsPage->buttonPlayNotification->setIcon(QPixmap(":/notify/images/play.png")); + } else { + if (newstate == Phonon::PlayingState) { + _optionsPage->buttonPlayNotification->setText("Stop"); + _optionsPage->buttonPlayNotification->setIcon(QPixmap(":/notify/images/stop.png")); } } } -UAVObjectField* NotifyPluginOptionsPage::getObjectFieldFromPage() -{ - UAVDataObject* obj = dynamic_cast( _objManager.getObject(_optionsPage->UAVObject->currentText())); - return obj->getField(_optionsPage->UAVObjectField->currentText()); -} - - -UAVObjectField* NotifyPluginOptionsPage::getObjectFieldFromSelected() -{ - UAVDataObject* obj = dynamic_cast(_objManager.getObject(_selectedNotification->getDataObject())); - return obj->getField(_selectedNotification->getObjectField()); -} - -void NotifyPluginOptionsPage::on_table_changeSelection( const QItemSelection & selected, const QItemSelection & deselected ) +void NotifyPluginOptionsPage::on_changedSelection_notifyTable(const QItemSelection & selected, const QItemSelection & deselected ) { bool select = false; - _notifySound->stop(); + _testSound->stop(); if (selected.indexes().size()) { select = true; - _selectedNotification = _privListNotifications.at(selected.indexes().at(0).row()); + setSelectedNotification(_privListNotifications.at(selected.indexes().at(0).row())); UAVObjectField* field = getObjectFieldFromSelected(); addDynamicField(field); updateConfigView(_selectedNotification); @@ -565,7 +545,34 @@ void NotifyPluginOptionsPage::on_table_changeSelection( const QItemSelection & s _optionsPage->buttonPlayNotification->setEnabled(select); } -void NotifyPluginOptionsPage::on_button_AddNotification_clicked() +void NotifyPluginOptionsPage::on_clicked_buttonSoundFolder(const QString& path) +{ + QDir dirPath(path); + QStringList listDirCollections = dirPath.entryList(QDir::AllDirs | QDir::NoDotAndDotDot); + _optionsPage->SoundCollectionList->clear(); + _optionsPage->SoundCollectionList->addItems(listDirCollections); +} + +void NotifyPluginOptionsPage::on_clicked_buttonTestSoundNotification() +{ + NotificationItem* notification = NULL; + qNotifyDebug() << "on_buttonTestSoundNotification_clicked"; + Q_ASSERT(-1 != _notifyRulesSelection->currentIndex().row()); + _testSound->clearQueue(); + notification = _privListNotifications.at(_notifyRulesSelection->currentIndex().row()); + QStringList sequence = notification->toSoundList(); + if (sequence.isEmpty()) { + qNotifyDebug() << "message sequense is empty!"; + return; + } + foreach(QString item, sequence) { + qNotifyDebug() << item; + _testSound->enqueue(Phonon::MediaSource(item)); + } + _testSound->play(); +} + +void NotifyPluginOptionsPage::on_clicked_buttonAddNotification() { NotificationItem* notification = new NotificationItem; @@ -574,27 +581,15 @@ void NotifyPluginOptionsPage::on_button_AddNotification_clicked() textPalette.setColor(QPalette::Normal,QPalette::Text, Qt::red); _optionsPage->SoundDirectoryPathChooser->setPalette(textPalette); _optionsPage->SoundDirectoryPathChooser->setPath("please select sound collection folder"); + delete notification; return; } + getOptionsPageValues(notification); - notification->setSoundCollectionPath(_optionsPage->SoundDirectoryPathChooser->path()); - notification->setCurrentLanguage(_optionsPage->SoundCollectionList->currentText()); - notification->setDataObject(_optionsPage->UAVObject->currentText()); - notification->setObjectField(_optionsPage->UAVObjectField->currentText()); - notification->setRange(_valueRange->currentText()); - - if (QSpinBox* spinValue = dynamic_cast(_fieldValue)) - notification->setSingleValue(spinValue->value()); - - if (_optionsPage->Sound1->currentText().size() > 0) - notification->setSound1(_optionsPage->Sound1->currentText()); - - notification->setSound2(_optionsPage->Sound2->currentText()); - notification->setSound3(_optionsPage->Sound3->currentText()); - - if ( ((!_optionsPage->Sound2->currentText().size()) && (_sayOrder->currentText()=="After second")) - || ((!_optionsPage->Sound3->currentText().size()) && (_sayOrder->currentText()=="After third")) ) { - return; + if ( ((!_optionsPage->Sound2->currentText().isEmpty()) && (_sayOrder->currentText()=="After second")) + || ((!_optionsPage->Sound3->currentText().isEmpty()) && (_sayOrder->currentText()=="After third")) ) { + delete notification; + return; } else { notification->setSayOrder(_sayOrder->currentText()); } @@ -604,7 +599,7 @@ void NotifyPluginOptionsPage::on_button_AddNotification_clicked() QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows); } -void NotifyPluginOptionsPage::on_button_DeleteNotification_clicked() +void NotifyPluginOptionsPage::on_clicked_buttonDeleteNotification() { _notifyRulesModel->removeRow(_notifyRulesSelection->currentIndex().row()); if (!_notifyRulesModel->rowCount() @@ -617,7 +612,7 @@ void NotifyPluginOptionsPage::on_button_DeleteNotification_clicked() } } -void NotifyPluginOptionsPage::on_button_ModifyNotification_clicked() +void NotifyPluginOptionsPage::on_clicked_buttonModifyNotification() { NotificationItem* notification = new NotificationItem; getOptionsPageValues(notification); @@ -628,3 +623,17 @@ void NotifyPluginOptionsPage::on_button_ModifyNotification_clicked() _privListNotifications.replace(_notifyRulesSelection->currentIndex().row(),notification); entryUpdated(_notifyRulesSelection->currentIndex().row()); } + +void NotifyPluginOptionsPage::on_FinishedPlaying() +{ + _testSound->clear(); +} + +void NotifyPluginOptionsPage::on_toggled_checkEnableSound(bool state) +{ + bool state1 = 1^state; + + QList listOutputs = _testSound->outputPaths(); + Phonon::AudioOutput * audioOutput = (Phonon::AudioOutput*)listOutputs.last().sink(); + audioOutput->setMuted(state1); +} diff --git a/ground/openpilotgcs/src/plugins/notify/notifypluginoptionspage.h b/ground/openpilotgcs/src/plugins/notify/notifypluginoptionspage.h index 3f36f3f4d..b270e72a0 100644 --- a/ground/openpilotgcs/src/plugins/notify/notifypluginoptionspage.h +++ b/ground/openpilotgcs/src/plugins/notify/notifypluginoptionspage.h @@ -73,33 +73,28 @@ public: void finish(); void restoreFromSettings(); - void updateConfigView(NotificationItem* notification); - void getOptionsPageValues(NotificationItem* notification); - UAVObjectField* getObjectFieldFromPage(); - UAVObjectField* getObjectFieldFromSelected(); - signals: void updateNotifications(QList list); void entryUpdated(int index); private slots: - void on_button_TestSoundNotification_clicked(); - void on_button_AddNotification_clicked(); - void on_button_DeleteNotification_clicked(); - void on_button_ModifyNotification_clicked(); + void on_clicked_buttonTestSoundNotification(); + void on_clicked_buttonAddNotification(); + void on_clicked_buttonDeleteNotification(); + void on_clicked_buttonModifyNotification(); /** * We can use continuous selection, to select simultaneously * multiple rows to move them(using drag & drop) inside table ranges. */ - void on_table_changeSelection( const QItemSelection & selected, const QItemSelection & deselected ); + void on_changedSelection_notifyTable( const QItemSelection & selected, const QItemSelection & deselected ); - void on_soundLanguage_indexChanged(int index); - void on_buttonSoundFolder_clicked(const QString& path); - void on_UAVObject_indexChanged(QString val); - void on_UAVField_indexChanged(QString val); - void on_changeButtonText(Phonon::State newstate, Phonon::State oldstate); - void on_checkEnableSound_toggled(bool state); + void on_changedIndex_soundLanguage(int index); + void on_clicked_buttonSoundFolder(const QString& path); + void on_changedIndex_UAVObject(QString val); + void on_changedIndex_UAVField(QString val); + void on_changed_playButtonText(Phonon::State newstate, Phonon::State oldstate); + void on_toggled_checkEnableSound(bool state); /** * Important when we change to or from "In range" value @@ -107,7 +102,7 @@ private slots: * we need to change UI to show edit line, * to have possibility assign range limits for value. */ - void on_rangeValue_indexChanged(QString); + void on_changedIndex_rangeValue(QString); void on_FinishedPlaying(void); @@ -115,29 +110,31 @@ private slots: private: Q_DISABLE_COPY(NotifyPluginOptionsPage) - void resetValueRange(); - void resetFieldType(); - - void setDynamicValueField(NotificationItem* notification); - void addDynamicField(UAVObjectField* objField); - void addDynamicValueLayout(); - void setDynamicValueWidget(UAVObjectField* objField); - void initButtons(); void initPhononPlayer(); void initRulesTable(); + void setSelectedNotification(NotificationItem* ntf); + void resetValueRange(); + void resetFieldType(); + + void updateConfigView(NotificationItem* notification); + void getOptionsPageValues(NotificationItem* notification); + UAVObjectField* getObjectFieldFromPage(); + UAVObjectField* getObjectFieldFromSelected(); + + void addDynamicFieldLayout(); + void addDynamicField(UAVObjectField* objField); + void addDynamicFieldWidget(UAVObjectField* objField); + void setDynamicFieldValue(NotificationItem* notification); + private: UAVObjectManager& _objManager; SoundNotifyPlugin* _owner; - QStringList _listDirCollections; - QStringList _listSoundFiles; - QString _currentCollectionPath; - Phonon::MediaObject* _sound1; - Phonon::MediaObject* _sound2; - QScopedPointer _notifySound; - Phonon::AudioOutput* _audioOutput; + + //! Media object uses to test sound playing + QScopedPointer _testSound; QScopedPointer _notifyRulesModel; QItemSelectionModel* _notifyRulesSelection; @@ -154,34 +151,36 @@ private: QScopedPointer _optionsPage; - //! widget to convinient selection of condition for field value (equal, lower, greater) - QComboBox* _valueRange; + //! Widget to convinient selection of condition for field value (equal, lower, greater) + QComboBox* _dynamicFieldLimit; - //! widget to convinient selection of order in which sounds will be played + //! Represents edit widget for dynamic UAVObjectfield, + //! can be spinbox - for numerics, combobox - enums, or + //! lineedit - for numerics with range constraints + QWidget* _dynamicFieldWidget; + + //! Type of UAVObjectField - numeric or ENUM, + //! this variable needs to correctly set appropriate dynamic UI element (_dynamicFieldWidget) + //! NOTE: ocassionaly it should be invalidated (= -1) to reset _dynamicFieldWidget + int _dynamicFieldType; + + //! Widget to convinient selection of position of + //! between sounds[1..3] QComboBox* _sayOrder; - //! widget to represent edit widget for UAVObjectfield, - //! can be spinbox - for numerics, combobox - enums, or - //! lineedit - for range limits - QWidget* _fieldValue; - - //! type of UAVObjectField - numeric or ENUM - //! this variable needs to correctly set dynamic UI elemen _fieldValue - //! NOTE: ocassionaly it should be invalidated (= -1) to reset _fieldValue - int _fieldType; - - //! actualy reference to optionsPageWidget, + //! Actualy reference to optionsPageWidget, //! we MUST hold it beyond the scope of createPage func //! to have possibility change dynamic parts of options page layout in future QWidget* _form; - //! needs to correctly update UI during transitions from "In Range" to other - //! _valueRange entries and back direction as well - QString _prevRangeValue; - //! Currently selected notification, all controls filled accroding to it. //! On options page startup, always points to first row. NotificationItem* _selectedNotification; + + //! Retrieved from UAVObjectManager by name from _selectedNotification, + //! if UAVObjectManager doesn't have such object, this field will be NULL + UAVDataObject* _currUAVObject; + }; #endif // NOTIFYPLUGINOPTIONSPAGE_H diff --git a/ground/openpilotgcs/src/plugins/notify/notifytablemodel.cpp b/ground/openpilotgcs/src/plugins/notify/notifytablemodel.cpp index b1fd1c737..ed4eb0e56 100644 --- a/ground/openpilotgcs/src/plugins/notify/notifytablemodel.cpp +++ b/ground/openpilotgcs/src/plugins/notify/notifytablemodel.cpp @@ -45,19 +45,19 @@ bool NotifyTableModel::setData(const QModelIndex &index, const QVariant &value, int role) { if (index.isValid() && role == Qt::DisplayRole) { - if(eMessageName == index.column()) { + if (eMessageName == index.column()) { emit dataChanged(index, index); return true; } } if (index.isValid() && role == Qt::EditRole) { - if(eRepeatValue == index.column()) + if (eRepeatValue == index.column()) _list.at(index.row())->setRetryString(value.toString()); else { - if(eExpireTimer == index.column()) + if (eExpireTimer == index.column()) _list.at(index.row())->setLifetime(value.toInt()); else { - if(eTurnOn == index.column()) + if (eTurnOn == index.column()) _list.at(index.row())->setMute(value.toBool()); } } @@ -114,7 +114,7 @@ QVariant NotifyTableModel::headerData(int section, Qt::Orientation orientation, if (orientation == Qt::Horizontal) return _headerStrings.at(section); else - if(orientation == Qt::Vertical) + if (orientation == Qt::Vertical) return QString("%1").arg(section); return QVariant(); @@ -141,7 +141,7 @@ bool NotifyTableModel::removeRows(int position, int rows, const QModelIndex& ind { Q_UNUSED(index); - if((-1 == position) || (-1 == rows) ) + if ((-1 == position) || (-1 == rows) ) return false; beginRemoveRows(QModelIndex(), position, position + rows - 1); @@ -201,7 +201,7 @@ bool NotifyTableModel::dropMimeData( const QMimeData * data, Qt::DropAction acti beginRow = rowCount(QModelIndex()); } - if(-1 == beginRow) + if (-1 == beginRow) return false; QByteArray encodedData = data->data(mime_type_notify_table); @@ -217,15 +217,18 @@ bool NotifyTableModel::dropMimeData( const QMimeData * data, Qt::DropAction acti // or from bottom rows to top rows (UP_DIRECTION) enum { UP_DIRECTION, DOWN_DIRECTION }; int direction = (dragged < rows) ? DOWN_DIRECTION : (dragged += 1, UP_DIRECTION); - Q_ASSERT(insertRows(rows + direction, 1, QModelIndex())); - if(-1 == dragged || rows >= _list.size() || dragged == rows) { + // check drop bounds + if (dragged < 0 || ((dragged + 1) >= _list.size() && direction == DOWN_DIRECTION) || dragged == rows) { qNotifyDebug() << "no such item"; - - return false; + continue; } + // addiional check in case dropping of multiple rows + if(rows + direction > _list.size()) continue; + + Q_ASSERT(insertRows(rows + direction, 1, QModelIndex())); _list.replace(rows + direction, item); Q_ASSERT(removeRows(dragged, 1, QModelIndex())); - if(direction == UP_DIRECTION) + if (direction == UP_DIRECTION) ++rows; };