diff options
author | Scott Murray <scott.murray@konsulko.com> | 2019-12-20 15:45:26 -0500 |
---|---|---|
committer | Scott Murray <scott.murray@konsulko.com> | 2019-12-20 15:45:26 -0500 |
commit | aa6b53c6a1f1a2e138a95f24795f767d6a15d31e (patch) | |
tree | 05f0a876d05869935f3312aa3b851458d5d7facb | |
parent | 100316af08e530eb0e56165344363d6ad2edf395 (diff) |
Backport voice message support
Backported changes from master branch include:
72eb23f fix parsing for all events, overloading for specific needs
21bba7d fix parsing event payload in presence of '\n' chars, and include signal emit for voice status and cbl changes.
9f3896c voice: register for all vshl events on start up
0ff12f7 voice: remove vshl-capabilities dependencies
16c4429 voice: add status events to VshlCoreVoiceMessages.
da09164 voice: add initial support for voice agents configuration
Bug-AGL: SPEC-2981, SPEC-2880, SPEC-3060
Signed-off-by: Scott Murray <scott.murray@konsulko.com>
Change-Id: Ia75ff0c8d180324459fe8a2d8fb844b7b288d057
-rw-r--r-- | CMakeLists.txt | 1 | ||||
-rw-r--r-- | message.h | 11 | ||||
-rw-r--r-- | messageengine.cpp | 5 | ||||
-rw-r--r-- | voice/CMakeLists.txt | 4 | ||||
-rw-r--r-- | voice/voice.cpp | 199 | ||||
-rw-r--r-- | voice/voice.h | 73 | ||||
-rw-r--r-- | voice/voiceagentmodel.cpp | 220 | ||||
-rw-r--r-- | voice/voiceagentmodel.h | 65 | ||||
-rw-r--r-- | voice/voiceagentprofile.cpp | 142 | ||||
-rw-r--r-- | voice/voiceagentprofile.h | 78 | ||||
-rw-r--r-- | voice/voiceagentregistry.cpp | 154 | ||||
-rw-r--r-- | voice/voiceagentregistry.h | 76 | ||||
-rw-r--r-- | voice/voicemessage.cpp | 115 | ||||
-rw-r--r-- | voice/voicemessage.h | 59 |
14 files changed, 1197 insertions, 5 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index acee230..df37791 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -55,6 +55,7 @@ set (SUBDIRS radio signal-composer telephony + voice weather) add_headers(message.h messageengine.h responsemessage.h) @@ -1,5 +1,5 @@ /* - * Copyright (C) 2017, 2018 Konsulko Group + * Copyright (C) 2017, 2018, 2019 Konsulko Group * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -42,6 +42,7 @@ enum MessageType { RadioEventMessage, MapEventMessage, NavigationEventMessage, + VoiceEventMessage, SignalComposerEventMessage, }; @@ -55,7 +56,7 @@ class Message : public QObject Message(); bool fromJson(QByteArray jsonData); - bool fromJDoc(QJsonDocument jdocData); + virtual bool fromJDoc(QJsonDocument jdocData); QByteArray toJson(QJsonDocument::JsonFormat format = QJsonDocument::Compact); bool createRequest(QString api, QString verb, QJsonValue parameter = "None"); inline QString eventApi() const @@ -88,17 +89,17 @@ class Message : public QObject return m_reply_data; } - inline bool isEvent() const + inline bool isEvent() const { return m_event; } - inline bool isReply() const + inline bool isReply() const { return m_reply; } - inline bool isValid() const + inline bool isValid() const { return m_init; } diff --git a/messageengine.cpp b/messageengine.cpp index 9ea59a3..d2644e8 100644 --- a/messageengine.cpp +++ b/messageengine.cpp @@ -26,6 +26,7 @@ #include "responsemessage.h" #include "telephonymessage.h" #include "weathermessage.h" +#include "voicemessage.h" #include "signalcomposermessage.h" #include <QJsonArray> @@ -84,6 +85,7 @@ void MessageEngine::onDisconnected() void MessageEngine::onTextMessageReceived(QString jsonStr) { + jsonStr = jsonStr.simplified(); QJsonDocument jdoc(QJsonDocument::fromJson(jsonStr.toUtf8())); if (jdoc.isEmpty()) { qWarning() << "Received invalid JSON: empty appfw message"; @@ -138,6 +140,9 @@ void MessageEngine::onTextMessageReceived(QString jsonStr) } else if (api == "bluetooth-map") { message = new MapMessage; type = MapEventMessage; + } else if (api == "vshl-core" ) { + message = new VoiceMessage; + type = VoiceEventMessage; } else if (api == "signal-composer") { message = new SignalComposerMessage; type = SignalComposerEventMessage; diff --git a/voice/CMakeLists.txt b/voice/CMakeLists.txt new file mode 100644 index 0000000..61b8fe2 --- /dev/null +++ b/voice/CMakeLists.txt @@ -0,0 +1,4 @@ +add_headers(voice.h voicemessage.h + voiceagentregistry.h voiceagentprofile.h voiceagentmodel.h) +add_sources(voice.cpp voicemessage.cpp + voiceagentregistry.cpp voiceagentprofile.cpp voiceagentmodel.cpp) diff --git a/voice/voice.cpp b/voice/voice.cpp new file mode 100644 index 0000000..40349d1 --- /dev/null +++ b/voice/voice.cpp @@ -0,0 +1,199 @@ +/* + * Copyright (C) 2019 Konsulko Group + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <QStringList> +#include "voice.h" +#include "message.h" +#include "messageengine.h" +#include "responsemessage.h" +#include "voicemessage.h" +#include "voiceagentregistry.h" + +Voice::Voice (QUrl &url, QQmlContext *context, QObject *parent) : + QObject(parent), + m_loop(nullptr) +{ + m_loop = new MessageEngine(url); + m_var = new VoiceAgentRegistry(this, context, parent); + + QObject::connect(m_loop, &MessageEngine::connected, + this, &Voice::onConnected); + QObject::connect(m_loop, &MessageEngine::disconnected, + this, &Voice::onDisconnected); + QObject::connect(m_loop, &MessageEngine::messageReceived, + this, &Voice::onMessageReceived); +} + +Voice::~Voice() +{ + delete m_loop; + delete m_var; +} + +void Voice::scan() +{ + VoiceMessage *vmsg = new VoiceMessage(); + QJsonObject parameter; + + vmsg->createRequest("enumerateVoiceAgents", parameter); + m_loop->sendMessage(vmsg); + delete vmsg; +} + +void Voice::getCBLpair(QString id) +{ + triggerCBLProcess(id); +} + +void Voice::subscribeAgentToVshlEvents(QString id) +{ + QJsonArray events = QJsonArray::fromStringList(vshl_events); + VoiceMessage *vmsg = new VoiceMessage(); + QJsonObject parameter; + + parameter.insert("va_id", id); + parameter.insert("events", events); + vmsg->createRequest("subscribe", parameter); + m_loop->sendMessage(vmsg); + delete vmsg; +} + +void Voice::unsubscribeAgentFromVshlEvents(QString id) +{ + QJsonArray events = QJsonArray::fromStringList(vshl_events); + VoiceMessage *vmsg = new VoiceMessage(); + QJsonObject parameter; + + parameter.insert("va_id", id); + parameter.insert("events", events); + vmsg->createRequest("unsubscribe", parameter); + m_loop->sendMessage(vmsg); + delete vmsg; +} + +void Voice::triggerCBLProcess(QString id) +{ + QJsonArray events; + VoiceMessage *vmsg = new VoiceMessage(); + QJsonObject parameter; + + parameter.insert("va_id", id); + parameter.insert("events", events); + vmsg->createRequest("subscribeToLoginEvents", parameter); + m_loop->sendMessage(vmsg); + delete vmsg; +} + +void Voice::parseAgentsList(QJsonArray agents) +{ + for (auto value : agents) { + QJsonObject a = value.toObject(); + QString id = m_var->addAgent(a); + subscribeAgentToVshlEvents(id); + } +} + + + +void Voice::processEvent(VoiceMessage *vmsg) +{ + const QString str = vmsg->eventName(); + QJsonObject data = vmsg->eventData(); + QString agentId = data.value("va_id").toString(); + QString state = data.value("state").toString(); + + if (vmsg->isAuthStateEvent()) { + m_var->setAuthState( + agentId, + static_cast<VoiceAgentRegistry::ServiceAuthState>( + m_var->stringToEnum(state, "ServiceAuthState"))); + + return; + } + else if (vmsg->isConnectionStateEvent()) { + m_var->setConnectionState( + agentId, + static_cast<VoiceAgentRegistry::AgentConnectionState>( + m_var->stringToEnum(state, "AgentConnectionState"))); + return; + } + else if (vmsg->isDialogStateEvent()) { + m_var->setDialogState( + agentId, + static_cast<VoiceAgentRegistry::VoiceDialogState>( + m_var->stringToEnum(state, "VoiceDialogState"))); + return; + } + else if (vmsg->isCblEvent()) { + auto payload_iter = data.find("payload"); + if (payload_iter == data.end()) + qWarning() << "no top-level payload field in event"; + auto payload_stringval = payload_iter.value().toString(); + if (!payload_stringval.isEmpty()) + payload_stringval.remove('\n'); + QJsonDocument infodoc = QJsonDocument::fromJson(payload_stringval.toUtf8()); + QJsonObject info = infodoc.object(); + QJsonObject properties = info.value("payload").toObject(); + QString url = properties.value("url").toString(); + QString code = properties.value("code").toString(); + if (str.contains("expired")) + m_var->updateLoginData(agentId, code, url, true); + else if (str.contains("received")) { + m_var->updateLoginData(agentId, code, url, false); + } else + qWarning() << "unknown cbl event"; + return; + } + + qWarning() << "Unknown vshl event:" << str; +} + +void Voice::processReply(ResponseMessage *rmsg) +{ + if (rmsg->replyStatus() == "failed") { + qWarning() << "Reply Failed received for verb:" << rmsg->requestVerb(); + } else if (rmsg->requestVerb() == "enumerateVoiceAgents") { + parseAgentsList(rmsg->replyData().value("agents").toArray()); + m_var->setDefaultId( + rmsg->replyData().value("default").toString()); + } else + qWarning() << "discarding reply received for verb:" << + rmsg->requestVerb(); +} + +void Voice::onConnected() +{ + scan(); +} + +void Voice::onDisconnected() +{ + QStringList mvarlist = m_var->getAgentsListById(); + QStringList::iterator it; + for (it = mvarlist.begin(); it != mvarlist.end(); ++it) + unsubscribeAgentFromVshlEvents(*it); +} + +void Voice::onMessageReceived(MessageType type, Message *msg) +{ + if (msg->isEvent() && type == VoiceEventMessage) { + processEvent(qobject_cast<VoiceMessage*>(msg)); + } else if (msg->isReply() && (type == ResponseRequestMessage)) { + processReply(qobject_cast<ResponseMessage*>(msg)); + } else + qWarning() << "Received unknown message type:" << type; + msg->deleteLater(); +} diff --git a/voice/voice.h b/voice/voice.h new file mode 100644 index 0000000..d4c288e --- /dev/null +++ b/voice/voice.h @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2019 Konsulko Group + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef VOICE_H +#define VOICE_H + +#include <QDebug> +#include <QObject> +#include <QJsonArray> +#include <QtQml/QQmlContext> +#include "message.h" + +class MessageEngine; +class ResponseMessage; +class VoiceMessage; +class VoiceAgentRegistry; + +class Voice : public QObject +{ + Q_OBJECT + + public: + explicit Voice(QUrl &url, QQmlContext *context, + QObject * parent = Q_NULLPTR); + virtual ~Voice(); + + //enumerate agents: + Q_INVOKABLE void scan(); + //obtain code based login params: + Q_INVOKABLE void getCBLpair(QString id); + + private: + MessageEngine *m_loop; + VoiceAgentRegistry *m_var; + + void subscribeAgentToVshlEvents(QString id); + void unsubscribeAgentFromVshlEvents(QString id); + void triggerCBLProcess(QString id); + void parseAgentsList(QJsonArray agents); + void processVshlEvent(VoiceMessage *vmsg); + void processLoginEvent(VoiceMessage *vmsg); + + void processEvent(VoiceMessage *vmsg); + void processReply(ResponseMessage *rmsg); + + // slots + void onConnected(); + void onDisconnected(); + void onMessageReceived(MessageType type, Message *msg); + + const QStringList vshl_events { + "voice_authstate_event", + "voice_dialogstate_event", + "voice_connectionstate_event", + "voice_cbl_codepair_received_event", + "voice_cbl_codepair_expired_event", + }; +}; + +#endif // VOICE_H diff --git a/voice/voiceagentmodel.cpp b/voice/voiceagentmodel.cpp new file mode 100644 index 0000000..3f4f50d --- /dev/null +++ b/voice/voiceagentmodel.cpp @@ -0,0 +1,220 @@ +/* + * Copyright (C) 2019 Konsulko Group + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "voiceagentmodel.h" +#include "voiceagentprofile.h" +#include <QVector> +#include <QDebug> + +VoiceAgentModel::VoiceAgentModel(QObject *parent) + : QAbstractListModel(parent) +{ +} + +QVariant VoiceAgentModel::data(const QModelIndex &index, int role) const +{ + QVariant ret; + + if (!index.isValid()) + return ret; + + if (index.row() < 0 || index.row() >= m_agents.count()) + return ret; + + const VoiceAgentProfile *vap = m_agents[index.row()]; + switch (role) { + case IdRole: + return vap->vaid(); + case NameRole: + return vap->name(); + case WuwRole: + return vap->activewuw(); + case AuthStateRole: + return vap->authstate(); + case ConnStateRole: + return vap->connstate(); + case DialogStateRole: + return vap->dialogstate(); + case LoginParamsRole: + return readLoginParams(index); + case ActiveRole: + return vap->isactive()? "active" : "inactive"; + } + return ret; +} + +int VoiceAgentModel::rowCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent); + return m_agents.count(); +} + +QVariantList VoiceAgentModel::readLoginParams(const QModelIndex &index) const +{ + QVariantList ret; + + if (!index.isValid()) + return ret; + + if (index.row() < 0 || index.row() >= this->m_agents.count()) + return ret; + + const VoiceAgentProfile *vap = this->m_agents[index.row()]; + ret.append(vap->logincode()); + ret.append(vap->loginurl()); + ret.append(vap->isloginpairexpired()? "expired" : "valid"); + return ret; +} + +void VoiceAgentModel::addAgent(VoiceAgentProfile *vap) +{ + beginInsertRows(QModelIndex(), rowCount(), rowCount()); + m_agents.insert(rowCount(), vap); + endInsertRows(); +} + +void VoiceAgentModel::removeAgent(VoiceAgentProfile *vap) +{ + if (m_agents.isEmpty()) + return; + + int row = m_agents.indexOf(vap); + beginRemoveRows(QModelIndex(), row, row); + m_agents.removeAt(row); + endRemoveRows(); + delete vap; +} + +void VoiceAgentModel::removeAll() +{ + if (m_agents.isEmpty()) + return; + + beginRemoveRows(QModelIndex(), 0, m_agents.count() -1); + qDeleteAll(m_agents.begin(), m_agents.end()); + endRemoveRows(); + m_agents.clear(); +} + +bool VoiceAgentModel::agentExists(QString name, QString id, QString api) const +{ + VoiceAgentProfile *vap = getAgentFromName(name); + if (!vap) + return false; + bool sameid = id == vap->vaid(); + bool sameapi = api == vap->vaapi(); + return sameapi && (sameid || id != "UNKNOWN"); +} + +VoiceAgentProfile* VoiceAgentModel::getAgentFromName(QString name) const +{ + if (m_agents.isEmpty()) + return nullptr; + + for (auto agent : m_agents) { + if (agent->name() == name) + return agent; + } + return nullptr; +} + +VoiceAgentProfile* VoiceAgentModel::getAgentFromId(QString id) const +{ + if (m_agents.isEmpty()) + return nullptr; + + for (auto agent : m_agents) { + if (agent->vaid() == id) + return agent; + } + return nullptr; +} + +void VoiceAgentModel::updateAgentProperties(QString name, QString id, QString api, + bool active, QString wuw) +{ + QVector<int> vroles; + VoiceAgentProfile *vap = getAgentFromName(name); + if (!vap) { + qWarning() << "Unknown agent"; + return; + } + if ((vap->vaapi() == api) && (vap->vaid() != id) && (id != "UNKNOWN")) { + vap->setVaid(id); + vroles.push_back(IdRole); + } + vap->setActive(active); + vroles.push_back(ActiveRole); + if (!wuw.isEmpty()) { + vap->setWuw(wuw); + vroles.push_back(WuwRole); + } + if (!vroles.isEmpty()) + emit dataChanged(indexOf(vap), indexOf(vap), vroles); +} + +void VoiceAgentModel::updateAgentState(QString id) +{ + QVector<int> vroles; + VoiceAgentProfile *vap = getAgentFromId(id); + + if (!vap) { + qWarning() << "Unknown agent"; + return; + } + + vroles.push_back(AuthStateRole); + vroles.push_back(ConnStateRole); + vroles.push_back(DialogStateRole); + + if (!vroles.isEmpty()) + emit dataChanged(indexOf(vap), indexOf(vap), vroles); +} + +void VoiceAgentModel::updateAgentLoginData(QString id) +{ + QVector<int> vroles; + VoiceAgentProfile *vap = getAgentFromId(id); + + if (!vap) { + qWarning() << "Unknown agent"; + return; + } + + vroles.push_back(LoginParamsRole); + if (!vroles.isEmpty()) + emit dataChanged(indexOf(vap), indexOf(vap), vroles); +} + +QModelIndex VoiceAgentModel::indexOf(VoiceAgentProfile *vap) +{ + int row = m_agents.indexOf(vap); + return index(row); +} + +QHash<int, QByteArray> VoiceAgentModel::roleNames() const +{ + QHash<int, QByteArray> roles; + roles[NameRole] = "name"; + roles[IdRole] = "id"; + roles[WuwRole] = "wuw"; + roles[AuthStateRole] = "authstate"; + roles[ConnStateRole] = "connstate"; + roles[DialogStateRole] = "dialogstate"; + roles[LoginParamsRole] = "usrauth"; + roles[ActiveRole] = "active"; + return roles; +} diff --git a/voice/voiceagentmodel.h b/voice/voiceagentmodel.h new file mode 100644 index 0000000..3cd84e6 --- /dev/null +++ b/voice/voiceagentmodel.h @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2019 Konsulko Group + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef VOICEAGENTMODEL_H +#define VOICEAGENTMODEL_H + +#include <QAbstractListModel> +#include <QStringList> +#include <QtQml/QQmlContext> +#include <QJsonObject> + +#include "voiceagentprofile.h" + +class VoiceAgentModel : public QAbstractListModel +{ + Q_OBJECT + + public: + enum VoiceAgentRoles { + IdRole = Qt::UserRole + 1, + NameRole, + WuwRole, + AuthStateRole, + ConnStateRole, + DialogStateRole, + LoginParamsRole, + ActiveRole, + }; + + VoiceAgentModel(QObject *parent = Q_NULLPTR); + + QVariant data(const QModelIndex &index, + int role = Qt::DisplayRole) const; + int rowCount(const QModelIndex &parent = QModelIndex()) const; + QVariantList readLoginParams(const QModelIndex &index) const; + void addAgent(VoiceAgentProfile *vap); + void removeAgent(VoiceAgentProfile* vap); + void removeAll(); + bool agentExists(QString name, QString id, QString api) const; + VoiceAgentProfile *getAgentFromName(QString name) const; + VoiceAgentProfile *getAgentFromId(QString id) const; + void updateAgentProperties(QString name, QString id, + QString api, bool active, QString wuw); + void updateAgentState(QString id); + void updateAgentLoginData(QString id); + + private: + QList<VoiceAgentProfile *> m_agents; + QModelIndex indexOf(VoiceAgentProfile *agent); + QHash<int, QByteArray> roleNames() const; +}; +#endif // VOICEAGENTMODEL_H diff --git a/voice/voiceagentprofile.cpp b/voice/voiceagentprofile.cpp new file mode 100644 index 0000000..05e3839 --- /dev/null +++ b/voice/voiceagentprofile.cpp @@ -0,0 +1,142 @@ +/* + * Copyright (C) 2019 Konsulko Group + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "voiceagentprofile.h" + +VoiceAgentProfile::VoiceAgentProfile(const QString &name, + const QString &id, + const QString &api, + bool active, + const QString &wuw, + const QString &vendor, + const QString &wuws) + : m_name(name), m_vaid(id), m_vaapi(api), m_active(active), + m_activewuw(wuw), m_vendor(vendor), m_wuws(wuws), + m_authstate("UNITIALIZED"), m_connstate("DISCONNECTED"), + m_dialogstate("MICROPHONEOFF"), m_logincode(QString()), + m_loginurl(QString()), m_expired(true) +{ +} + +QString VoiceAgentProfile::name() const +{ + return m_name; +} + +QString VoiceAgentProfile::vaid() const +{ + return m_vaid; +} + +QString VoiceAgentProfile::vaapi() const +{ + return m_vaapi; +} + +bool VoiceAgentProfile::isactive() const +{ + return m_active; +} + +QString VoiceAgentProfile::activewuw() const +{ + return m_activewuw; +} + +QString VoiceAgentProfile::vendor() const +{ + return m_vendor; +} + +QString VoiceAgentProfile::wuws() const +{ + return m_wuws; +} + +QString VoiceAgentProfile::authstate() const +{ + return m_authstate; +} + +QString VoiceAgentProfile::connstate() const +{ + return m_connstate; +} + +QString VoiceAgentProfile::dialogstate() const +{ + return m_dialogstate; +} + +QString VoiceAgentProfile::logincode() const +{ + return m_logincode; +} + +QString VoiceAgentProfile::loginurl() const +{ + return m_loginurl; +} + +bool VoiceAgentProfile::isloginpairexpired() const +{ + return m_expired; +} + +void VoiceAgentProfile::setVaid(const QString id) +{ + m_vaid = id; +} + +void VoiceAgentProfile::setActive(bool active) +{ + m_active = active; +} + +void VoiceAgentProfile::setAuthState(const QString state) +{ + m_authstate = state; +} + +void VoiceAgentProfile::setConnState(const QString state) +{ + m_connstate = state; +} + +void VoiceAgentProfile::setDialogState(const QString state) +{ + m_dialogstate = state; +} + +void VoiceAgentProfile::setLoginCode(const QString usrcode) +{ + m_logincode = usrcode; +} + +void VoiceAgentProfile::setLoginUrl(const QString usrurl) +{ + m_loginurl = usrurl; +} + +void VoiceAgentProfile::setLoginPairExpired(bool expired) +{ + m_expired = expired; +} + +void VoiceAgentProfile::setWuw(const QString newwuw) +{ + m_activewuw = newwuw; +} diff --git a/voice/voiceagentprofile.h b/voice/voiceagentprofile.h new file mode 100644 index 0000000..dda96c5 --- /dev/null +++ b/voice/voiceagentprofile.h @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2019 Konsulko Group + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef VOICEAGENTPROFILE_H +#define VOICEAGENTPROFILE_H + +#include <QString> + +class VoiceAgentProfile +{ + public: + VoiceAgentProfile(const QString &name, + const QString &id, + const QString &api, + bool active, + const QString &wuw, + const QString &vendor, + const QString &wuws); + + QString name() const; + QString vaid() const; + QString vaapi() const; + bool isactive() const; + QString activewuw() const; + QString vendor() const; + QString wuws() const; + QString authstate() const; + QString connstate() const; + QString dialogstate() const; + QString logincode() const; + QString loginurl() const; + bool isloginpairexpired() const; + + void setVaid(const QString newid); + void setActive(bool activemode); + void setAuthState(const QString newauthstate); + void setConnState(const QString newconnstate); + void setDialogState(const QString newdialogstate); + void setLoginCode(const QString newtoken); + void setLoginUrl(const QString newurl); + void setLoginPairExpired(bool expired); + void setWuw(const QString newwuw); + + bool operator==(const VoiceAgentProfile& rhs) { + return (m_name == rhs.name() && + m_vaid == rhs.vaid() && + m_vaapi == rhs.vaapi()); }; + + private: + QString m_name; + QString m_vaid; + QString m_vaapi; + bool m_active; + QString m_activewuw; + QString m_vendor; + QString m_wuws; + QString m_authstate; + QString m_connstate; + QString m_dialogstate; + QString m_logincode; + QString m_loginurl; + bool m_expired; +}; + +#endif // VOICEAGENTPROFILE_H diff --git a/voice/voiceagentregistry.cpp b/voice/voiceagentregistry.cpp new file mode 100644 index 0000000..62e8cd6 --- /dev/null +++ b/voice/voiceagentregistry.cpp @@ -0,0 +1,154 @@ +/* + * Copyright (C) 2019 Konsulko Group + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <QMetaEnum> +#include <QSortFilterProxyModel> +#include <QtQml/QQmlEngine> + +#include "voice.h" +#include "voiceagentregistry.h" +#include "voiceagentmodel.h" +#include "voiceagentprofile.h" + +VoiceAgentRegistry::VoiceAgentRegistry(Voice *voice, QQmlContext *context, QObject *parent) : + QObject(parent), + m_model(nullptr), + vc(voice) +{ + m_model = new VoiceAgentModel(); + QSortFilterProxyModel *model = new QSortFilterProxyModel(); + model->setSourceModel(m_model); + model->setSortRole(VoiceAgentModel::VoiceAgentRoles::IdRole); + model->setSortCaseSensitivity(Qt::CaseInsensitive); + model->sort(0); + + context->setContextProperty("VoiceAgentModel", m_model); + context->setContextProperty("VoiceAgent", this); +} + +VoiceAgentRegistry::~VoiceAgentRegistry() +{ + delete m_model; +} + +QString VoiceAgentRegistry::addAgent(QJsonObject va) +{ + bool active = va.value("active").toBool(); + QString wuw = va.value("activewakeword").toString(); + QString api = va.value("api").toString(); + QString desc = va.value("description").toString(); + QString id = va.value("id").toString(); + QString name = va.value("name").toString(); + QString vendor = va.value("vendor").toString(); + QString wuws = va.value("wakewords").toString(); + + if (!m_model->agentExists(name, id, api)) { + VoiceAgentProfile *vap = new VoiceAgentProfile(name, id, api, + active, wuw, + vendor, wuws); + m_model->addAgent(vap); + m_regids.append(id); + } + else + m_model->updateAgentProperties(name, id, api, active, wuw); + return id; +} + +bool VoiceAgentRegistry::removeAgent(QString id) +{ + VoiceAgentProfile *vap = m_model->getAgentFromId(id); + if (!vap) + return false; + m_model->removeAgent(vap); + return true; +} + +void VoiceAgentRegistry::clearRegistry() +{ + m_default_aid.clear(); + m_regids.clear(); + m_model->removeAll(); +} + +QStringList VoiceAgentRegistry::getAgentsListById() const +{ + return m_regids; +} + +QString VoiceAgentRegistry::getDefaultId() const +{ + return m_default_aid.isEmpty()? "UNKNOWN" : m_default_aid; +} +void VoiceAgentRegistry::setDefaultId(QString id) +{ + m_default_aid = id; +} + +void VoiceAgentRegistry::setAuthState(QString id, ServiceAuthState state) +{ + QMetaEnum metaEnum = QMetaEnum::fromType<VoiceAgentRegistry::ServiceAuthState>(); + auto stateStr = metaEnum.valueToKey(state); + VoiceAgentProfile *vap = m_model->getAgentFromId(id); + if (vap) { + vap->setAuthState(stateStr); + m_model->updateAgentState(id); + } +} + +void VoiceAgentRegistry::setConnectionState(QString id, AgentConnectionState state) +{ + QMetaEnum metaEnum = QMetaEnum::fromType<VoiceAgentRegistry::AgentConnectionState>(); + auto stateStr = metaEnum.valueToKey(state); + + VoiceAgentProfile *vap = m_model->getAgentFromId(id); + if (vap) { + vap->setConnState(stateStr); + m_model->updateAgentState(id); + } +} + +void VoiceAgentRegistry::setDialogState(QString id, VoiceDialogState state) +{ + QMetaEnum metaEnum = QMetaEnum::fromType<VoiceAgentRegistry::VoiceDialogState>(); + auto stateStr = metaEnum.valueToKey(state); + + VoiceAgentProfile *vap = m_model->getAgentFromId(id); + if (vap) { + vap->setDialogState(stateStr); + m_model->updateAgentState(id); + } +} + +void VoiceAgentRegistry::updateLoginData(QString id, QString code, QString url, + bool expired) +{ + VoiceAgentProfile *vap = m_model->getAgentFromId(id); + if (vap) { + vap->setLoginCode(url); + vap->setLoginUrl(code); + vap->setLoginPairExpired(expired); + m_model->updateAgentLoginData(id); + }; +} + +int VoiceAgentRegistry::stringToEnum(const QString key, const QString enumtype) +{ + const QMetaObject *metaObject = VoiceAgentRegistry::metaObject(); + int enumIndex = metaObject->indexOfEnumerator(enumtype.toUtf8().constData()); + QMetaEnum metaEnum = metaObject->enumerator(enumIndex); + int value = metaEnum.keyToValue(key.toUtf8().constData()); + return (value < 0)? 0 : value; +} diff --git a/voice/voiceagentregistry.h b/voice/voiceagentregistry.h new file mode 100644 index 0000000..cd7dc2c --- /dev/null +++ b/voice/voiceagentregistry.h @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2019 Konsulko Group + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef VOICEAGENTREGISTRY_H +#define VOICEAGENTREGISTRY_H + +#include <QDebug> +#include <QObject> +#include <QJsonArray> +#include <QtQml/QQmlContext> + +class Voice; +class VoiceAgentModel; + +class VoiceAgentRegistry : public QObject +{ + Q_OBJECT + public: + explicit VoiceAgentRegistry(Voice *voice, QQmlContext *context, + QObject *parent); + virtual ~VoiceAgentRegistry(); + + enum AgentConnectionState { + DISCONNECTED = 0, + CONNECTED, + }; + Q_ENUM(AgentConnectionState) + + enum VoiceDialogState { + IDLE = 0, + LISTENING, + THINKING, + SPEAKING, + MICROPHONEOFF, + }; + Q_ENUM(VoiceDialogState) + + enum ServiceAuthState { + UNINITIALIZED = 0, + REFRESHED, + }; + Q_ENUM(ServiceAuthState) + + QString addAgent(QJsonObject va); + bool removeAgent(QString id); + void clearRegistry(); + QStringList getAgentsListById() const; + QString getDefaultId() const; + void setDefaultId(QString id); + void setAuthState(QString id, ServiceAuthState state); + void setConnectionState(QString id, AgentConnectionState state); + void setDialogState(QString id, VoiceDialogState state); + void updateLoginData(QString id, QString code, QString url, + bool expired); + int stringToEnum(QString value, QString enumtype); + private: + VoiceAgentModel *m_model; + Voice *vc; + QString m_default_aid; + QStringList m_regids; +}; + +#endif // VOICEAGENTREGISTRY_H diff --git a/voice/voicemessage.cpp b/voice/voicemessage.cpp new file mode 100644 index 0000000..493b04e --- /dev/null +++ b/voice/voicemessage.cpp @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2019 Konsulko Group + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <QDebug> +#include <QJsonArray> +#include <QJsonDocument> +#include <QJsonObject> + +#include "voicemessage.h" + +bool VoiceMessage::createRequest(QString verb, QJsonObject parameter) +{ + if (!verbs.contains(verb)) + return false; + return Message::createRequest("vshl-core", verb, parameter); +} + +bool VoiceMessage::fromJDoc(QJsonDocument jdoc) +{ + // Validate message is array + if (!jdoc.isArray()) { + qWarning("Invalid appfw message: not an array"); + return false; + } + QJsonArray msg = jdoc.array(); + + // Validate array is proper length + if ((msg.size() < 3) || (msg.size() > 4)) { + qWarning("Invalid appfw message: invalid array size"); + return false; + } + + // Validate msgid type + double msgid; + if (msg[0].isDouble()) { + msgid = msg[0].toDouble(); + } else { + qWarning("Invalid appfw message: invalid msgid type"); + return false; + } + + // Validate msgid element + if ((msgid < Call) || (msgid > Event)) { + qWarning("Invalid appfw message: msgid out of range"); + return false; + } + + // Validate that the payload has a request object + QJsonObject payload; + if (msg[2].isObject()) { + payload = msg[2].toObject(); + } else { + qWarning("Invalid appfw payload: no JSON object"); + return false; + } + + if ((msgid == RetOk) || (msgid == RetErr)) { + auto request_iter = payload.find("request"); + auto request = request_iter.value().toObject(); + if (request.empty()) { + qWarning("Invalid appfw reply message: empty request data"); + return false; + } + auto status_iter = request.find("status"); + auto info_iter = request.find("info"); + auto response_iter = payload.find("response"); + auto response = response_iter.value().toObject(); + m_reply_status = status_iter.value().toString(); + m_reply_info = info_iter.value().toString(); + m_reply_data = response; + m_reply = true; + } else if (msgid == Event) { + // If event, save data object + auto data_iter = payload.find("data"); + auto data = data_iter.value().toObject(); + auto data_string = data_iter.value().toString(); + if (!data_string.isEmpty()) + data_string.remove('\n'); + + QJsonDocument datadoc = QJsonDocument::fromJson(data_string.toUtf8()); + m_event_data = datadoc.object(); + + auto event_iter = payload.find("event"); + auto event_string = event_iter.value().toString(); + if (event_string.isEmpty()) { + qWarning("Invalid appfw event message: empty event name"); + return false; + } + QStringList event_strings = event_string.split(QRegExp("/")); + if (event_strings.size() != 2) { + qWarning("Invalid appfw event message: malformed event name"); + return false; + } + m_event_api = event_strings[0]; + m_event_name = event_strings[1]; + m_event = true; + } + + m_jdoc = jdoc; + m_init = true; + return m_init; +} diff --git a/voice/voicemessage.h b/voice/voicemessage.h new file mode 100644 index 0000000..712631e --- /dev/null +++ b/voice/voicemessage.h @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2019 Konsulko Group + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef VOICE_MESSAGE_H +#define VOICE_MESSAGE_H + +#include "message.h" + +class VoiceMessage : public Message +{ + Q_OBJECT + public: + virtual ~VoiceMessage() {}; + bool fromJDoc(QJsonDocument jdocData) override; + + bool isAuthStateEvent() const { + return (this->eventName().contains("voice_authstate_event")); }; + bool isConnectionStateEvent() const { + return (this->eventName().contains("voice_connectionstate_event")); }; + bool isDialogStateEvent() const { + return (this->eventName().contains("voice_dialogstate_event")); }; + bool isCblEvent() const { + return (this->eventName().contains("cbl")); }; + bool createRequest(QString verb, QJsonObject parameter); + + private: + QStringList verbs { + "startListening", + "cancelListening", + "subscribe", + "unsubscribe", + "enumerateVoiceAgents", + "setDefaultVoiceAgent", + "subscribeToLoginEvents", + }; + QStringList events { + "voice_authstate_event", + "voice_dialogstate_event", + "voice_connectionstate_event", + "voice_cbl_codepair_received_event", + "voice_cbl_codepair_expired_event", + }; +}; + + +#endif // VOICE_MESSAGE_H |