mirror of
				https://github.com/f4exb/sdrangel.git
				synced 2025-11-04 05:30:32 -05:00 
			
		
		
		
	
		
			
				
	
	
		
			578 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			578 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
///////////////////////////////////////////////////////////////////////////////////
 | 
						|
// Copyright (C) 2022 Jon Beniston, M7RCE                                        //
 | 
						|
//                                                                               //
 | 
						|
// This program is free software; you can redistribute it and/or modify          //
 | 
						|
// it under the terms of the GNU General Public License as published by          //
 | 
						|
// the Free Software Foundation as version 3 of the License, or                  //
 | 
						|
// (at your option) any later version.                                           //
 | 
						|
//                                                                               //
 | 
						|
// This program is distributed in the hope that it will be useful,               //
 | 
						|
// but WITHOUT ANY WARRANTY; without even the implied warranty of                //
 | 
						|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the                  //
 | 
						|
// GNU General Public License V3 for more details.                               //
 | 
						|
//                                                                               //
 | 
						|
// You should have received a copy of the GNU General Public License             //
 | 
						|
// along with this program. If not, see <http://www.gnu.org/licenses/>.          //
 | 
						|
///////////////////////////////////////////////////////////////////////////////////
 | 
						|
 | 
						|
#include <QDebug>
 | 
						|
#include <QUrl>
 | 
						|
#include <QUrlQuery>
 | 
						|
#include <QNetworkReply>
 | 
						|
#include <QJsonDocument>
 | 
						|
#include <QJsonObject>
 | 
						|
#include <QRegularExpression>
 | 
						|
 | 
						|
#include "util/simpleserializer.h"
 | 
						|
#include "util/iot/device.h"
 | 
						|
#include "util/iot/tplink.h"
 | 
						|
#include "util/iot/homeassistant.h"
 | 
						|
#include "util/iot/visa.h"
 | 
						|
 | 
						|
Device::Device(DeviceDiscoverer::DeviceInfo *info)
 | 
						|
{
 | 
						|
    if (info) {
 | 
						|
        m_info = *info;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
Device* Device::create(const QHash<QString, QVariant>& settings, const QString& protocol, DeviceDiscoverer::DeviceInfo *info)
 | 
						|
{
 | 
						|
    if (checkSettings(settings, protocol))
 | 
						|
    {
 | 
						|
        if (protocol == "TPLink")
 | 
						|
        {
 | 
						|
            if (settings.contains("deviceId"))
 | 
						|
            {
 | 
						|
                return new TPLinkDevice(settings.value("username").toString(),
 | 
						|
                                        settings.value("password").toString(),
 | 
						|
                                        settings.value("deviceId").toString(),
 | 
						|
                                        info);
 | 
						|
            }
 | 
						|
            else
 | 
						|
            {
 | 
						|
                qDebug() << "Device::create: A deviceId is required for: " << protocol;
 | 
						|
            }
 | 
						|
        }
 | 
						|
        else if (protocol == "HomeAssistant")
 | 
						|
        {
 | 
						|
            if (settings.contains("deviceId"))
 | 
						|
            {
 | 
						|
                return new HomeAssistantDevice(settings.value("apiKey").toString(),
 | 
						|
                                               settings.value("url").toString(),
 | 
						|
                                               settings.value("deviceId").toString(),
 | 
						|
                                               settings.value("controlIds").toStringList(),
 | 
						|
                                               settings.value("sensorIds").toStringList(),
 | 
						|
                                               info);
 | 
						|
            }
 | 
						|
            else
 | 
						|
            {
 | 
						|
                qDebug() << "Device::create: A deviceId is required for: " << protocol;
 | 
						|
            }
 | 
						|
        }
 | 
						|
        else if (protocol == "VISA")
 | 
						|
        {
 | 
						|
            if (settings.contains("deviceId"))
 | 
						|
            {
 | 
						|
                return new VISADevice(settings,
 | 
						|
                                      settings.value("deviceId").toString(),
 | 
						|
                                      settings.value("controlIds").toStringList(),
 | 
						|
                                      settings.value("sensorIds").toStringList(),
 | 
						|
                                      info);
 | 
						|
            }
 | 
						|
            else
 | 
						|
            {
 | 
						|
                qDebug() << "Device::create: A deviceId is required for: " << protocol;
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
    return nullptr;
 | 
						|
}
 | 
						|
 | 
						|
bool Device::checkSettings(const QHash<QString, QVariant>& settings, const QString& protocol)
 | 
						|
{
 | 
						|
    if (protocol == "TPLink")
 | 
						|
    {
 | 
						|
        if (settings.contains("username") && settings.contains("password"))
 | 
						|
        {
 | 
						|
            return true;
 | 
						|
        }
 | 
						|
        else
 | 
						|
        {
 | 
						|
            qDebug() << "Device::checkSettings: A username and password are required for: " << protocol;
 | 
						|
            return false;
 | 
						|
        }
 | 
						|
    }
 | 
						|
    else if (protocol == "HomeAssistant")
 | 
						|
    {
 | 
						|
        if (settings.contains("apiKey"))
 | 
						|
        {
 | 
						|
            if (settings.contains("url"))
 | 
						|
            {
 | 
						|
                return true;
 | 
						|
            }
 | 
						|
            else
 | 
						|
            {
 | 
						|
                qDebug() << "Device::checkSettings: A host url is required for: " << protocol;
 | 
						|
                return false;
 | 
						|
            }
 | 
						|
        }
 | 
						|
        else
 | 
						|
        {
 | 
						|
            qDebug() << "Device::checkSettings: An apiKey is required for: " << protocol;
 | 
						|
            return false;
 | 
						|
        }
 | 
						|
    }
 | 
						|
    else if (protocol == "VISA")
 | 
						|
    {
 | 
						|
        return true;
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
        qDebug() << "Device::checkSettings: Unsupported protocol: " << protocol;
 | 
						|
        return false;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void Device::setState(const QString &controlId, bool state)
 | 
						|
{
 | 
						|
    qDebug() << "Device::setState: " << getProtocol() << " doesn't support bool. Can't set " << controlId << " to " << state;
 | 
						|
}
 | 
						|
 | 
						|
void Device::setState(const QString &controlId, int state)
 | 
						|
{
 | 
						|
    qDebug() << "Device::setState: " << getProtocol() << " doesn't support int. Can't set " << controlId << " to " << state;
 | 
						|
}
 | 
						|
 | 
						|
void Device::setState(const QString &controlId, float state)
 | 
						|
{
 | 
						|
    qDebug() << "Device::setState: " << getProtocol() << " doesn't support float. Can't set " << controlId << " to " << state;
 | 
						|
}
 | 
						|
 | 
						|
void Device::setState(const QString &controlId, const QString &state)
 | 
						|
{
 | 
						|
    qDebug() << "Device::setState: " << getProtocol() << " doesn't support QString. Can't set " << controlId << " to " << state;
 | 
						|
}
 | 
						|
 | 
						|
void Device::recordGetRequest(void *ptr)
 | 
						|
{
 | 
						|
    m_getRequests.insert(ptr, QDateTime::currentDateTime());
 | 
						|
}
 | 
						|
 | 
						|
void Device::removeGetRequest(void *ptr)
 | 
						|
{
 | 
						|
    m_getRequests.remove(ptr);
 | 
						|
}
 | 
						|
 | 
						|
void Device::recordSetRequest(const QString &id, int guardMS)
 | 
						|
{
 | 
						|
    m_setRequests.insert(id, QDateTime::currentDateTime().addMSecs(guardMS));
 | 
						|
}
 | 
						|
 | 
						|
bool Device::getAfterSet(void *ptr, const QString &id)
 | 
						|
{
 | 
						|
    if (m_getRequests.contains(ptr) && m_setRequests.contains(id))
 | 
						|
    {
 | 
						|
        QDateTime getTime = m_getRequests.value(ptr);
 | 
						|
        QDateTime setTime = m_setRequests.value(id);
 | 
						|
        return getTime > setTime;
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
        return true;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
const QStringList DeviceDiscoverer::m_typeStrings = {
 | 
						|
    "Auto",
 | 
						|
    "Boolean",
 | 
						|
    "Integer",
 | 
						|
    "Float",
 | 
						|
    "String",
 | 
						|
    "List",
 | 
						|
    "Button"
 | 
						|
};
 | 
						|
 | 
						|
const QStringList DeviceDiscoverer::m_widgetTypeStrings = {
 | 
						|
    "Spin box",
 | 
						|
    "Dial",
 | 
						|
    "Slider"
 | 
						|
};
 | 
						|
 | 
						|
DeviceDiscoverer::DeviceDiscoverer()
 | 
						|
{
 | 
						|
}
 | 
						|
 | 
						|
DeviceDiscoverer *DeviceDiscoverer::getDiscoverer(const QHash<QString, QVariant>& settings, const QString& protocol)
 | 
						|
{
 | 
						|
    if (Device::checkSettings(settings, protocol))
 | 
						|
    {
 | 
						|
        if (protocol == "TPLink")
 | 
						|
        {
 | 
						|
            return new TPLinkDeviceDiscoverer(settings.value("username").toString(), settings.value("password").toString());
 | 
						|
        }
 | 
						|
        else if (protocol == "HomeAssistant")
 | 
						|
        {
 | 
						|
            return new HomeAssistantDeviceDiscoverer(settings.value("apiKey").toString(), settings.value("url").toString());
 | 
						|
        }
 | 
						|
        else if (protocol == "VISA")
 | 
						|
        {
 | 
						|
            return new VISADeviceDiscoverer(settings.value("resourceFilter").toString());
 | 
						|
        }
 | 
						|
    }
 | 
						|
    return nullptr;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
DeviceDiscoverer::DeviceInfo::DeviceInfo()
 | 
						|
{
 | 
						|
}
 | 
						|
 | 
						|
DeviceDiscoverer::DeviceInfo::DeviceInfo(const DeviceInfo &info)
 | 
						|
{
 | 
						|
    m_name = info.m_name;
 | 
						|
    m_id = info.m_id;
 | 
						|
    m_model = info.m_model;
 | 
						|
    // Take deep-copy of controls and sensors
 | 
						|
    for (auto const control : info.m_controls) {
 | 
						|
        ControlInfo *ci = control->clone();
 | 
						|
        m_controls.append(ci);
 | 
						|
    }
 | 
						|
    for (auto const sensor : info.m_sensors) {
 | 
						|
        m_sensors.append(sensor->clone());
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
DeviceDiscoverer::DeviceInfo& DeviceDiscoverer::DeviceInfo::operator=(const DeviceInfo &info)
 | 
						|
{
 | 
						|
    m_name = info.m_name;
 | 
						|
    m_id = info.m_id;
 | 
						|
    m_model = info.m_model;
 | 
						|
    qDeleteAll(m_controls);
 | 
						|
    m_controls.clear();
 | 
						|
    qDeleteAll(m_sensors);
 | 
						|
    m_sensors.clear();
 | 
						|
    // Take deep-copy of controls and sensors
 | 
						|
    for (auto const control : info.m_controls) {
 | 
						|
        m_controls.append(control->clone());
 | 
						|
    }
 | 
						|
    for (auto const sensor : info.m_sensors) {
 | 
						|
        m_sensors.append(sensor->clone());
 | 
						|
    }
 | 
						|
    return *this;
 | 
						|
}
 | 
						|
 | 
						|
DeviceDiscoverer::DeviceInfo::~DeviceInfo()
 | 
						|
{
 | 
						|
    qDeleteAll(m_controls);
 | 
						|
    m_controls.clear();
 | 
						|
    qDeleteAll(m_sensors);
 | 
						|
    m_sensors.clear();
 | 
						|
}
 | 
						|
 | 
						|
DeviceDiscoverer::DeviceInfo::operator QString() const
 | 
						|
{
 | 
						|
    QString controls;
 | 
						|
    QString sensors;
 | 
						|
 | 
						|
    for (auto control : m_controls) {
 | 
						|
        controls.append((QString)*control);
 | 
						|
    }
 | 
						|
    for (auto sensor : m_sensors) {
 | 
						|
        sensors.append((QString)*sensor);
 | 
						|
    }
 | 
						|
 | 
						|
    return QString("DeviceInfo: m_name: %1 m_id: %2 m_model: %3 m_controls: %4 m_sensors: %5")
 | 
						|
                .arg(m_name)
 | 
						|
                .arg(m_id)
 | 
						|
                .arg(m_model)
 | 
						|
                .arg(controls)
 | 
						|
                .arg(sensors);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
DeviceDiscoverer::ControlInfo::ControlInfo() :
 | 
						|
    m_type(AUTO),
 | 
						|
    m_min(-1000000),
 | 
						|
    m_max(1000000),
 | 
						|
    m_scale(1.0f),
 | 
						|
    m_precision(3),
 | 
						|
    m_widgetType(SPIN_BOX)
 | 
						|
{
 | 
						|
}
 | 
						|
 | 
						|
DeviceDiscoverer::ControlInfo::operator QString() const
 | 
						|
{
 | 
						|
    return QString("ControlInfo: m_name: %1 m_id: %2 m_type: %3")
 | 
						|
                .arg(m_name)
 | 
						|
                .arg(m_id)
 | 
						|
                .arg(DeviceDiscoverer::m_typeStrings[m_type]);
 | 
						|
}
 | 
						|
 | 
						|
DeviceDiscoverer::ControlInfo *DeviceDiscoverer::ControlInfo::clone() const
 | 
						|
{
 | 
						|
    return new ControlInfo(*this);
 | 
						|
}
 | 
						|
 | 
						|
QByteArray DeviceDiscoverer::ControlInfo::serialize() const
 | 
						|
{
 | 
						|
    SimpleSerializer s(1);
 | 
						|
 | 
						|
    s.writeString(1, m_name);
 | 
						|
    s.writeString(2, m_id);
 | 
						|
    s.writeS32(3, (int)m_type);
 | 
						|
    s.writeFloat(4, m_min);
 | 
						|
    s.writeFloat(5, m_max);
 | 
						|
    s.writeFloat(6, m_scale);
 | 
						|
    s.writeS32(7, m_precision);
 | 
						|
    s.writeList(8, m_values);
 | 
						|
    s.writeS32(9, (int)m_widgetType);
 | 
						|
    s.writeString(10, m_units);
 | 
						|
 | 
						|
    return s.final();
 | 
						|
}
 | 
						|
 | 
						|
bool DeviceDiscoverer::ControlInfo::deserialize(const QByteArray& data)
 | 
						|
{
 | 
						|
    SimpleDeserializer d(data);
 | 
						|
 | 
						|
    if (!d.isValid()) {
 | 
						|
        return false;
 | 
						|
    }
 | 
						|
 | 
						|
    if (d.getVersion() == 1)
 | 
						|
    {
 | 
						|
        d.readString(1, &m_name);
 | 
						|
        d.readString(2, &m_id);
 | 
						|
        d.readS32(3, (int*)&m_type);
 | 
						|
        d.readFloat(4, &m_min);
 | 
						|
        d.readFloat(5, &m_max);
 | 
						|
        d.readFloat(6, &m_scale, 1.0f);
 | 
						|
        d.readS32(7, &m_precision, 3);
 | 
						|
        d.readList(8, &m_values);
 | 
						|
        d.readS32(9, (int *)&m_widgetType);
 | 
						|
        d.readString(10, &m_units);
 | 
						|
        return true;
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
        return false;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
DeviceDiscoverer::SensorInfo::operator QString() const
 | 
						|
{
 | 
						|
    return QString("SensorInfo: m_name: %1 m_id: %2 m_type: %3")
 | 
						|
                .arg(m_name)
 | 
						|
                .arg(m_id)
 | 
						|
                .arg(DeviceDiscoverer::m_typeStrings[m_type]);
 | 
						|
}
 | 
						|
 | 
						|
DeviceDiscoverer::SensorInfo *DeviceDiscoverer::SensorInfo::clone() const
 | 
						|
{
 | 
						|
    return new SensorInfo(*this);
 | 
						|
}
 | 
						|
 | 
						|
QByteArray DeviceDiscoverer::SensorInfo::serialize() const
 | 
						|
{
 | 
						|
    SimpleSerializer s(1);
 | 
						|
 | 
						|
    s.writeString(1, m_name);
 | 
						|
    s.writeString(2, m_id);
 | 
						|
    s.writeS32(3, (int)m_type);
 | 
						|
    s.writeString(4, m_units);
 | 
						|
 | 
						|
    return s.final();
 | 
						|
}
 | 
						|
 | 
						|
bool DeviceDiscoverer::SensorInfo::deserialize(const QByteArray& data)
 | 
						|
{
 | 
						|
    SimpleDeserializer d(data);
 | 
						|
 | 
						|
    if (!d.isValid()) {
 | 
						|
        return false;
 | 
						|
    }
 | 
						|
 | 
						|
    if (d.getVersion() == 1)
 | 
						|
    {
 | 
						|
        d.readString(1, &m_name);
 | 
						|
        d.readString(2, &m_id);
 | 
						|
        d.readS32(3, (int*)&m_type);
 | 
						|
        d.readString(4, &m_units);
 | 
						|
        return true;
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
        return false;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
QByteArray DeviceDiscoverer::DeviceInfo::serialize() const
 | 
						|
{
 | 
						|
    SimpleSerializer s(1);
 | 
						|
 | 
						|
    s.writeString(1, m_name);
 | 
						|
    s.writeString(2, m_id);
 | 
						|
    s.writeString(3, m_model);
 | 
						|
    s.writeList(10, m_controls);
 | 
						|
    s.writeList(11, m_sensors);
 | 
						|
 | 
						|
    return s.final();
 | 
						|
}
 | 
						|
 | 
						|
bool DeviceDiscoverer::DeviceInfo::deserialize(const QByteArray& data)
 | 
						|
{
 | 
						|
    SimpleDeserializer d(data);
 | 
						|
 | 
						|
    if (!d.isValid()) {
 | 
						|
        return false;
 | 
						|
    }
 | 
						|
 | 
						|
    if (d.getVersion() == 1)
 | 
						|
    {
 | 
						|
        QByteArray blob;
 | 
						|
 | 
						|
        d.readString(1, &m_name);
 | 
						|
        d.readString(2, &m_id);
 | 
						|
        d.readString(3, &m_model);
 | 
						|
        d.readList(10, &m_controls);
 | 
						|
        d.readList(11, &m_sensors);
 | 
						|
        return true;
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
        return false;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
DeviceDiscoverer::ControlInfo *DeviceDiscoverer::DeviceInfo::getControl(const QString &id) const
 | 
						|
{
 | 
						|
    for (auto c : m_controls)
 | 
						|
    {
 | 
						|
        if (c->m_id == id) {
 | 
						|
            return c;
 | 
						|
        }
 | 
						|
    }
 | 
						|
    return nullptr;
 | 
						|
}
 | 
						|
 | 
						|
DeviceDiscoverer::SensorInfo *DeviceDiscoverer::DeviceInfo::getSensor(const QString &id) const
 | 
						|
{
 | 
						|
    for (auto s : m_sensors)
 | 
						|
    {
 | 
						|
        if (s->m_id == id) {
 | 
						|
            return s;
 | 
						|
        }
 | 
						|
    }
 | 
						|
    return nullptr;
 | 
						|
}
 | 
						|
 | 
						|
void DeviceDiscoverer::DeviceInfo::deleteControl(const QString &id)
 | 
						|
{
 | 
						|
    for (int i = 0; i < m_controls.size(); i++)
 | 
						|
    {
 | 
						|
        if (m_controls[i]->m_id == id)
 | 
						|
        {
 | 
						|
            delete m_controls.takeAt(i);
 | 
						|
            return;
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void DeviceDiscoverer::DeviceInfo::deleteSensor(const QString &id)
 | 
						|
{
 | 
						|
    for (int i = 0; i < m_sensors.size(); i++)
 | 
						|
    {
 | 
						|
        if (m_sensors[i]->m_id == id)
 | 
						|
        {
 | 
						|
            delete m_sensors.takeAt(i);
 | 
						|
            return;
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
QDataStream& operator<<(QDataStream& out, const DeviceDiscoverer::ControlInfo* control)
 | 
						|
{
 | 
						|
    int typeId;
 | 
						|
    if (dynamic_cast<const VISADevice::VISAControl *>(control)) {
 | 
						|
        typeId = 1;
 | 
						|
    } else {
 | 
						|
        typeId = 0;
 | 
						|
    }
 | 
						|
    out << typeId;
 | 
						|
    out << control->serialize();
 | 
						|
    return out;
 | 
						|
}
 | 
						|
 | 
						|
QDataStream& operator>>(QDataStream& in, DeviceDiscoverer::ControlInfo*& control)
 | 
						|
{
 | 
						|
    QByteArray data;
 | 
						|
    int typeId;
 | 
						|
    in >> typeId;
 | 
						|
    if (typeId == 1) {
 | 
						|
        control = new VISADevice::VISAControl();
 | 
						|
    } else {
 | 
						|
        control = new DeviceDiscoverer::ControlInfo();
 | 
						|
    }
 | 
						|
    in >> data;
 | 
						|
    control->deserialize(data);
 | 
						|
    return in;
 | 
						|
}
 | 
						|
 | 
						|
QDataStream& operator<<(QDataStream& out, const DeviceDiscoverer::SensorInfo* sensor)
 | 
						|
{
 | 
						|
    int typeId;
 | 
						|
    if (dynamic_cast<const VISADevice::VISASensor *>(sensor)) {
 | 
						|
        typeId = 1;
 | 
						|
    } else {
 | 
						|
        typeId = 0;
 | 
						|
    }
 | 
						|
    out << typeId;
 | 
						|
    out << sensor->serialize();
 | 
						|
    return out;
 | 
						|
}
 | 
						|
 | 
						|
QDataStream& operator>>(QDataStream& in, DeviceDiscoverer::SensorInfo*& sensor)
 | 
						|
{
 | 
						|
 | 
						|
    QByteArray data;
 | 
						|
    int typeId;
 | 
						|
    in >> typeId;
 | 
						|
    if (typeId == 1) {
 | 
						|
        sensor = new VISADevice::VISASensor();
 | 
						|
    } else {
 | 
						|
        sensor = new DeviceDiscoverer::SensorInfo();
 | 
						|
    }
 | 
						|
    in >> data;
 | 
						|
    sensor->deserialize(data);
 | 
						|
    return in;
 | 
						|
}
 | 
						|
 | 
						|
QDataStream& operator<<(QDataStream& out, const VISADevice::VISASensor &sensor)
 | 
						|
{
 | 
						|
    out << sensor.serialize();
 | 
						|
    return out;
 | 
						|
}
 | 
						|
 | 
						|
QDataStream& operator>>(QDataStream& in, VISADevice::VISASensor& sensor)
 | 
						|
{
 | 
						|
    QByteArray data;
 | 
						|
    in >> data;
 | 
						|
    sensor.deserialize(data);
 | 
						|
    return in;
 | 
						|
}
 | 
						|
 | 
						|
QDataStream& operator<<(QDataStream& out, const VISADevice::VISAControl &control)
 | 
						|
{
 | 
						|
    out << control.serialize();
 | 
						|
    return out;
 | 
						|
}
 | 
						|
 | 
						|
QDataStream& operator>>(QDataStream& in, VISADevice::VISAControl& control)
 | 
						|
{
 | 
						|
    QByteArray data;
 | 
						|
    in >> data;
 | 
						|
    control.deserialize(data);
 | 
						|
    return in;
 | 
						|
}
 |