This repository has been archived on 2026-05-31. You can view files and clone it, but cannot push or open issues or pull requests.
2018-03-05 20:37:40 +01:00

257 lines
10 KiB
C++

#include "systemservice.h"
#include "bluetoothuuids.h"
#include "loggingcategory.h"
#include "loopd.h"
#include <QJsonDocument>
#include <QJsonParseError>
#include <QLowEnergyDescriptorData>
#include <QLowEnergyCharacteristicData>
SystemService::SystemService(QLowEnergyService *service, QObject *parent) :
QObject(parent),
m_service(service)
{
connect(m_service, &QLowEnergyService::characteristicChanged, this, &SystemService::characteristicChanged);
connect(m_service, &QLowEnergyService::characteristicRead, this, &SystemService::characteristicChanged);
connect(m_service, &QLowEnergyService::characteristicWritten, this, &SystemService::characteristicWritten);
connect(m_service, &QLowEnergyService::descriptorWritten, this, &SystemService::descriptorWritten);
connect(m_service, SIGNAL(error(QLowEnergyService::ServiceError)), this, SLOT(serviceError(QLowEnergyService::ServiceError)));
connect(Loopd::instance()->snapdControl(), &SnapdControl::updateRunningChanged, this, &SystemService::onUpdateRunningChanged);
}
QLowEnergyService *SystemService::service()
{
return m_service;
}
QLowEnergyServiceData SystemService::serviceData()
{
QLowEnergyServiceData serviceData;
serviceData.setType(QLowEnergyServiceData::ServiceTypePrimary);
serviceData.setUuid(systemServiceUuid);
QLowEnergyDescriptorData clientConfigDescriptorData(QBluetoothUuid::ClientCharacteristicConfiguration, QByteArray(2, 0));
// System commander characterisitc e081fed1-f757-4449-b9c9-bfa83133f7fc
QLowEnergyCharacteristicData systemCommanderCharacteristicData;
systemCommanderCharacteristicData.setUuid(systemCommanderCharacteristicUuid);
systemCommanderCharacteristicData.setProperties(QLowEnergyCharacteristic::Write);
systemCommanderCharacteristicData.setValueLength(0, 20);
serviceData.addCharacteristic(systemCommanderCharacteristicData);
// Response characterisitc e081fed2-f757-4449-b9c9-bfa83133f7fc
QLowEnergyCharacteristicData systemResponseCharacteristicData;
systemResponseCharacteristicData.setUuid(systemResponseCharacteristicUuid);
systemResponseCharacteristicData.setProperties(QLowEnergyCharacteristic::Notify);
systemResponseCharacteristicData.addDescriptor(clientConfigDescriptorData);
systemResponseCharacteristicData.setValueLength(0, 20);
serviceData.addCharacteristic(systemResponseCharacteristicData);
return serviceData;
// System update indicator characterisitc e081fed3-f757-4449-b9c9-bfa83133f7fc
QLowEnergyCharacteristicData systemUpdateCharacteristicData;
systemUpdateCharacteristicData.setUuid(systemUpdateCharacteristicUuid);
systemUpdateCharacteristicData.setProperties(QLowEnergyCharacteristic::Read | QLowEnergyCharacteristic::Notify);
systemUpdateCharacteristicData.addDescriptor(clientConfigDescriptorData);
systemUpdateCharacteristicData.setValueLength(1, 1);
systemUpdateCharacteristicData.setValue(QByteArray::number((int)Loopd::instance()->snapdControl()->updateRunning()));
serviceData.addCharacteristic(systemUpdateCharacteristicData);
return serviceData;
}
void SystemService::streamData(const QVariantMap &responseMap)
{
QLowEnergyCharacteristic characteristic = m_service->characteristic(systemResponseCharacteristicUuid);
if (!characteristic.isValid()) {
qCWarning(dcBluetoothServer()) << "SystemService: System response characteristic not valid";
return;
}
QByteArray data = QJsonDocument::fromVariant(responseMap).toJson(QJsonDocument::Compact) + '\n';
qCDebug(dcBluetoothServer()) << "SystemService: Start streaming response data:" << data.count() << "bytes";
int sentDataLength = 0;
QByteArray remainingData = data;
while (!remainingData.isEmpty()) {
QByteArray package = remainingData.left(20);
sentDataLength += package.count();
m_service->writeCharacteristic(characteristic, package);
remainingData = remainingData.remove(0, package.count());
}
qCDebug(dcBluetoothServer()) << "SystemService: Finished streaming response data";
}
QVariantMap SystemService::createResponse(const SystemService::SystemServiceCommand &command, const SystemService::SystemServiceResponse &responseCode)
{
QVariantMap response;
response.insert("c", (int)command);
response.insert("r", (int)responseCode);
return response;
}
void SystemService::characteristicChanged(const QLowEnergyCharacteristic &characteristic, const QByteArray &value)
{
// Command
if (characteristic.uuid() == systemCommanderCharacteristicUuid) {
// Check if currently reading
if (m_readingInputData) {
m_inputDataStream.append(value);
} else {
m_inputDataStream.clear();
m_readingInputData = true;
m_inputDataStream.append(value);
}
// If command finished
if (value.endsWith('\n')) {
QJsonParseError error;
QJsonDocument jsonDocument = QJsonDocument::fromJson(m_inputDataStream, &error);
if (error.error != QJsonParseError::NoError) {
qCWarning(dcBluetoothServer()) << "SystemService: Got invalid json object" << m_inputDataStream;
m_inputDataStream.clear();
m_readingInputData = false;
return;
}
qCDebug(dcBluetoothServer()) << "SystemService: Got command stream" << jsonDocument.toJson();
processCommand(jsonDocument.toVariant().toMap());
m_inputDataStream.clear();
m_readingInputData = false;
}
// Limit possible data stream to prevent overflow
if (m_inputDataStream.length() >= 20 * 1024) {
m_inputDataStream.clear();
m_readingInputData = false;
return;
}
}
}
void SystemService::characteristicRead(const QLowEnergyCharacteristic &characteristic, const QByteArray &value)
{
qCDebug(dcBluetoothServer()) << "SystemService: Characteristic read" << characteristic.uuid().toString() << value;
}
void SystemService::characteristicWritten(const QLowEnergyCharacteristic &characteristic, const QByteArray &value)
{
qCDebug(dcBluetoothServer()) << "SystemService: Characteristic written" << characteristic.uuid().toString() << value;
}
void SystemService::descriptorRead(const QLowEnergyDescriptor &descriptor, const QByteArray &value)
{
qCDebug(dcBluetoothServer()) << "SystemService: Descriptor read" << descriptor.uuid().toString() << value;
}
void SystemService::descriptorWritten(const QLowEnergyDescriptor &descriptor, const QByteArray &value)
{
qCDebug(dcBluetoothServer()) << "SystemService: Descriptor written" << descriptor.uuid().toString() << value;
}
void SystemService::serviceError(const QLowEnergyService::ServiceError &error)
{
QString errorString;
switch (error) {
case QLowEnergyService::NoError:
errorString = "No error";
break;
case QLowEnergyService::OperationError:
errorString = "Operation error";
break;
case QLowEnergyService::CharacteristicReadError:
errorString = "Characteristic read error";
break;
case QLowEnergyService::CharacteristicWriteError:
errorString = "Characteristic write error";
break;
case QLowEnergyService::DescriptorReadError:
errorString = "Descriptor read error";
break;
case QLowEnergyService::DescriptorWriteError:
errorString = "Descriptor write error";
break;
case QLowEnergyService::UnknownError:
errorString = "Unknown error";
break;
default:
errorString = "Unhandled error";
break;
}
qCWarning(dcBluetoothServer()) << "SystemService: Error:" << errorString;
}
void SystemService::processCommand(const QVariantMap &request)
{
if (!request.contains("c")) {
qCWarning(dcBluetoothServer()) << "SystemService: Invalid request. Command value missing.";
streamData(createResponse(SystemServiceCommandPushAuthentication, SystemServiceResponseInvalidValue));
return;
}
bool commandIntOk;
int command = request.value("c").toInt(&commandIntOk);
if (!commandIntOk) {
qCWarning(dcBluetoothServer()) << "SystemService: Invalid request. Could not convert method to interger.";
streamData(createResponse(SystemServiceCommandPushAuthentication, SystemServiceResponseInvalidValue));
return;
}
// Process method
switch (command) {
case SystemServiceCommandPushAuthentication:
commandPressPushButton();
break;
default:
qCWarning(dcBluetoothServer()) << "SystemService: Invalid request. Unknown command" << command;
streamData(createResponse(SystemServiceCommandPushAuthentication, SystemServiceResponseInvalidCommand));
break;
}
}
void SystemService::commandPressPushButton()
{
if (!m_service) {
qCWarning(dcBluetoothServer()) << "SystemService: Could not press push button. Service not valid.";
return;
}
QLowEnergyCharacteristic characteristic = m_service->characteristic(systemResponseCharacteristicUuid);
if (!characteristic.isValid()) {
qCWarning(dcBluetoothServer()) << "SystemService: System response characteristic not valid";
return;
}
if (!Loopd::instance()->nymeaService()->available()) {
streamData(createResponse(SystemServiceCommandPushAuthentication, SystemServiceResponsePushServiceUnavailable));
return;
}
Loopd::instance()->nymeaService()->pushButtonPressed();
streamData(createResponse(SystemServiceCommandPushAuthentication, SystemServiceResponseSuccess));
}
void SystemService::onUpdateRunningChanged(const bool &running)
{
if (!m_service) {
qCWarning(dcBluetoothServer()) << "SystemService: Could not set system update running characteristic. Service not valid.";
return;
}
QLowEnergyCharacteristic characteristic = m_service->characteristic(systemUpdateCharacteristicUuid);
if (!characteristic.isValid()) {
qCWarning(dcBluetoothServer()) << "SystemService: Could not set system update running characteristic. Characteristic not valid";
return;
}
qCDebug(dcBluetoothServer()) << "SystemService: Notify system update running changed:" << (running ? "running" : "finished");
m_service->writeCharacteristic(characteristic, running ? QByteArray::fromHex("01") : QByteArray::fromHex("00"));
return;
}