/*
* Copyright (C) 2018 Iot.Bzh
*
* 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 "qafbwebsocketclient.h"
#include <QJsonDocument>
#include <QJsonArray>
#include <QJsonObject>
/*!
* \brief Default constructor.
* \param parent Parent object.
*/
QAfbWebsocketClient::QAfbWebsocketClient(QObject* parent)
: QObject{parent}
, m_nextCallId{0}
{
connect(&m_socket, SIGNAL(connected()), this, SLOT(onSocketConnected()));
connect(&m_socket, SIGNAL(disconnected()), this, SLOT(onSocketDisconnected()));
connect(&m_socket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(onSocketError(QAbstractSocket::SocketError)));
connect(&m_socket, SIGNAL(textMessageReceived(QString)), this, SLOT(onSocketTextReceived(QString)));
}
/*!
* \brief Get last error code.
* \return Return the last error code.
*/
QAbstractSocket::SocketError QAfbWebsocketClient::error() const
{
return m_socket.error();
}
/*!
* \brief Get last error as a string.
* \return Return the last error as a string.
*/
QString QAfbWebsocketClient::errorString() const
{
return m_socket.errorString();
}
/*!
* \brief Check if connection is ready or not.
* \return Return \c true if the connected is ready to read and write, \c false otherwise.
*/
bool QAfbWebsocketClient::isValid() const
{
return m_socket.isValid();
}
/*!
* \brief Open the connection.
* \param u Url to connect to.
*/
void QAfbWebsocketClient::open(const QUrl& u)
{
m_socket.open(u);
}
/*!
* \brief Close the connection.
*/
void QAfbWebsocketClient::close()
{
m_socket.close();
}
/*!
* \brief Call an api's verb with an argument.
* \param api Api to call.
* \param verb Verb to call.
* \param arg Argument to pass.
* \param closure callback to call at the verb reply
*/
void QAfbWebsocketClient::call(const QString& api, const QString& verb, const QJsonValue& arg, closure_t closure)
{
QString callId = QString::number(m_nextCallId++);
if (closure)
m_closures[callId] = closure;
QJsonArray msg;
msg.append(2); // Call
msg.append(callId);
msg.append(api + "/" + verb);
msg.append(arg);
QJsonDocument value;
value.setArray(msg);
sendTextMessage(value.toJson(QJsonDocument::Compact));
}
/*!
* \brief Send a text message over the websocket.
* \param msg Message to send.
* This is use for test only, you should not use this method as
* it sent text as-is, so you have to follow the binder's
* protocol by your self.
*/
void QAfbWebsocketClient::sendTextMessage(QString msg)
{
m_socket.sendTextMessage(msg);
qDebug() << "WebSocket Text Sent: " << msg;
emit textSent(msg);
}
/*!
* \brief Called when socket signals to be connected.
*/
void QAfbWebsocketClient::onSocketConnected()
{
emit connected();
}
/*!
* \brief Called when socket signals to be disconnected.
*/
void QAfbWebsocketClient::onSocketDisconnected()
{
emit disconnected();
}
/*!
* \brief Called when socket signals an error.
* \param e Error code.
*/
void QAfbWebsocketClient::onSocketError(QAbstractSocket::SocketError e)
{
emit error(e);
}
/*!
* \brief Called when socket signals a received text.
* \param msg Message received.
*/
void QAfbWebsocketClient::onSocketTextReceived(QString msg)
{
emit textReceived(msg);
qDebug() << "WebSocket Text Received: " << msg;
QJsonDocument doc = QJsonDocument::fromJson(msg.toUtf8());
QJsonArray arr = doc.array();
switch(arr[0].toInt())
{
case 3: // RetOK
case 4: // RetErr
{
auto it = m_closures.find(arr[1].toString());
if (it != m_closures.end())
{
closure_t closure = *it;
m_closures.erase(it);
closure(arr[0].toInt() == 3, arr[2]);
}
break;
}
case 5: // Events
emit eventReceived(arr[1].toString(), arr[2].toObject()["data"]);
break;
}
}