diff options
-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 |