Protobuf rpc (#34)
* Setup the nanopb environment * Recursive checkout * Include .pri file * Add bytesWritten information to AbstractSerialOperation * Add Start RPC Session operation * Better ScreenStream log messages * Incomplete screen streaming protobuf implementation - saving work * Working sreen streaming over protobuf [no input] * Common class for all Protobuf messages * Rename ProtobufMessage to MainProtobufMessage * Finished NanoPB wrapper * Add new protobuf headers * Implement DeviceInfo through Protobuf * File structure reorganisation * Even better protobuf implementation * Migrate StorageStat operation to protobuf * Add Gui input request * Bring back input events * Reusable response messages * Update protobuf message definitions * Migrate Storage List to protobuf * Add missing storage messages * Add missing system messages * Disable screen streaming for the time being * Migrate Storage Write to protobuf * Migrate Storage Read command to protobuf * Working Backup operation * Fix date conversion * Migrate Storage Remove to protobuf * Migrate Storage mkdir command to protobuf * Error message tweaks * Fix StorageWrite operation * Working Storage Write operation via protobuf * Code cleanup * Add comment * Add timeout to AbstractSerialOperation * Migrate FactoryReset operation to protobuf * Migrate SystemReboot operation to protobuf * Rename some files * Unified serial (#32) * Use only one pre-initialised instance of QSerialPort - draft * Correct error string descriptions * Rewind the output file for write operation * Simplify serial port management * Simplify DeviceInfoHelper * Keep the serial port instance in a shared pointer * Add Start and stop screen streaming operations * Integrate Gui operatons into CommandInterface * Implement a shared instance of CommandInterface * Bring back ScreenStreaming * Disable screen streaming - it doesn't work * Bug fixes * Remove unnecessary output * Use DeviceState in screen streamer * Arch refactoring (#33) * Add periodic update checking * Add error message for update registry * Application backend is now a QObject * Rename some classes * Use dynamically allocated QObjects everywhere for consistency * Add useful function stubs * Move MetaType registration into separate static method * Revert to static allocation in Application class because of QML errors * Simplify MainWindow code * Implement working update procedure w/screen streaming * Fix windows build(again!) * Remove extra include * Improve serial port handling * Handle the absence of SD card properly * Close and delete serial port instances correctly * Rename slots * Remove unused signal * Cleanup screen streamer code * Add started and stopped signals for sreen streamer * Almost get rid of FirmwareUpdater class * Bring back all operations * Remove deviceRegistry context property * Add debriefing screen * Remove mentions of firmwareUpdater from QML code * Remove FirmwareUpdater class * Increase shadow opacity * Bring back full screen streaming * Remove offline devices after an operaton * Implement self update dialog * Remove unneeded condition
This commit is contained in:
parent
e3a70cfcc4
commit
ae7ee417fe
|
@ -1,3 +1,6 @@
|
|||
[submodule "driver-tool/libwdi"]
|
||||
path = driver-tool/libwdi
|
||||
url = https://github.com/pbatard/libwdi.git
|
||||
[submodule "3rdparty/nanopb"]
|
||||
path = 3rdparty/nanopb
|
||||
url = https://github.com/nanopb/nanopb.git
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
CONFIG -= qt
|
||||
|
||||
TEMPLATE = lib
|
||||
CONFIG += staticlib c++11
|
||||
|
||||
include(../qflipper_common.pri)
|
||||
|
||||
SOURCES += \
|
||||
nanopb/pb_common.c \
|
||||
nanopb/pb_decode.c \
|
||||
nanopb/pb_encode.c
|
||||
|
||||
HEADERS += \
|
||||
nanopb/pb.h \
|
||||
nanopb/pb_common.h \
|
||||
nanopb/pb_decode.h \
|
||||
nanopb/pb_encode.h
|
|
@ -0,0 +1 @@
|
|||
Subproject commit 13666952914f3cf43a70c6b9738a7dc0dd06a6dc
|
|
@ -10,8 +10,8 @@
|
|||
#include <QLoggingCategory>
|
||||
#include <QtQuickControls2/QQuickStyle>
|
||||
|
||||
#include "qflipperbackend.h"
|
||||
#include "updateregistry.h"
|
||||
#include "deviceregistry.h"
|
||||
#include "screencanvas.h"
|
||||
#include "preferences.h"
|
||||
#include "logger.h"
|
||||
|
@ -44,7 +44,7 @@ const QString Application::commitNumber()
|
|||
return APP_COMMIT;
|
||||
}
|
||||
|
||||
AppUpdater *Application::updater()
|
||||
ApplicationUpdater *Application::updater()
|
||||
{
|
||||
return &m_updater;
|
||||
}
|
||||
|
@ -65,9 +65,8 @@ void Application::initContextProperties()
|
|||
{
|
||||
//TODO: Replace context properties with QML singletons
|
||||
m_engine.rootContext()->setContextProperty("app", this);
|
||||
m_engine.rootContext()->setContextProperty("deviceRegistry", &m_backend.deviceRegistry);
|
||||
m_engine.rootContext()->setContextProperty("firmwareUpdates", &m_backend.firmwareUpdates);
|
||||
m_engine.rootContext()->setContextProperty("applicationUpdates", &m_backend.applicationUpdates);
|
||||
m_engine.rootContext()->setContextProperty("firmwareUpdates", m_backend.firmwareUpdates());
|
||||
m_engine.rootContext()->setContextProperty("applicationUpdates",m_backend.applicationUpdates());
|
||||
}
|
||||
|
||||
void Application::initTranslations()
|
||||
|
@ -86,10 +85,12 @@ void Application::initTranslations()
|
|||
void Application::initQmlTypes()
|
||||
{
|
||||
qmlRegisterType<ScreenCanvas>("QFlipper", 1, 0, "ScreenCanvas");
|
||||
qmlRegisterType<AppUpdater>("QFlipper", 1, 0, "AppUpdater");
|
||||
qmlRegisterType<ApplicationUpdater>("QFlipper", 1, 0, "AppUpdater");
|
||||
|
||||
qmlRegisterSingletonInstance("QFlipper", 1, 0, "Logger", globalLogger);
|
||||
qmlRegisterSingletonInstance("QFlipper", 1, 0, "Preferences", globalPrefs);
|
||||
qmlRegisterSingletonInstance("QFlipper", 1, 0, "Backend", &m_backend);
|
||||
qmlRegisterSingletonInstance("QFlipper", 1, 0, "Application", this);
|
||||
}
|
||||
|
||||
void Application::initImports()
|
||||
|
|
|
@ -3,9 +3,8 @@
|
|||
#include <QApplication>
|
||||
#include <QQmlApplicationEngine>
|
||||
|
||||
#include "appupdater.h"
|
||||
#include "flipperupdates.h"
|
||||
#include "qflipperbackend.h"
|
||||
#include "applicationupdater.h"
|
||||
#include "applicationbackend.h"
|
||||
|
||||
class Application : public QApplication
|
||||
{
|
||||
|
@ -13,15 +12,14 @@ class Application : public QApplication
|
|||
Q_PROPERTY(QString name READ applicationName NOTIFY applicationNameChanged)
|
||||
Q_PROPERTY(QString version READ applicationVersion NOTIFY applicationVersionChanged)
|
||||
Q_PROPERTY(QString commit READ commitNumber CONSTANT)
|
||||
Q_PROPERTY(AppUpdater* updater READ updater CONSTANT)
|
||||
Q_PROPERTY(ApplicationUpdater* updater READ updater CONSTANT)
|
||||
|
||||
public:
|
||||
Application(int &argc, char **argv);
|
||||
~Application();
|
||||
|
||||
static const QString commitNumber();
|
||||
|
||||
AppUpdater *updater();
|
||||
ApplicationUpdater *updater();
|
||||
|
||||
private:
|
||||
void initLogger();
|
||||
|
@ -33,8 +31,8 @@ private:
|
|||
void initFonts();
|
||||
void initGUI();
|
||||
|
||||
AppUpdater m_updater;
|
||||
QFlipperBackend m_backend;
|
||||
ApplicationUpdater m_updater;
|
||||
ApplicationBackend m_backend;
|
||||
QQmlApplicationEngine m_engine;
|
||||
};
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ CONFIG += c++11
|
|||
|
||||
SOURCES += \
|
||||
application.cpp \
|
||||
appupdater.cpp \
|
||||
applicationupdater.cpp \
|
||||
main.cpp \
|
||||
screencanvas.cpp
|
||||
|
||||
|
@ -30,17 +30,20 @@ QML_IMPORT_PATH = $$PWD/imports
|
|||
unix|win32 {
|
||||
LIBS += \
|
||||
-L$$OUT_PWD/../backend/ -lbackend \
|
||||
-L$$OUT_PWD/../3rdparty/ -l3rdparty \
|
||||
-L$$OUT_PWD/../dfu/ -ldfu
|
||||
}
|
||||
|
||||
win32:!win32-g++ {
|
||||
PRE_TARGETDEPS += \
|
||||
$$OUT_PWD/../backend/backend.lib \
|
||||
$$OUT_PWD/../3rdparty/3rdparty.lib \
|
||||
$$OUT_PWD/../dfu/dfu.lib
|
||||
|
||||
} else:unix|win32-g++ {
|
||||
PRE_TARGETDEPS += \
|
||||
$$OUT_PWD/../backend/libbackend.a \
|
||||
$$OUT_PWD/../3rdparty/lib3rdparty.a \
|
||||
$$OUT_PWD/../dfu/libdfu.a
|
||||
}
|
||||
|
||||
|
@ -68,11 +71,12 @@ INCLUDEPATH += \
|
|||
|
||||
DEPENDPATH += \
|
||||
$$PWD/../dfu \
|
||||
$$PWD/../backend
|
||||
$$PWD/../backend \
|
||||
$$PWD/../3rdparty \
|
||||
|
||||
HEADERS += \
|
||||
application.h \
|
||||
appupdater.h \
|
||||
applicationupdater.h \
|
||||
screencanvas.h
|
||||
|
||||
DISTFILES +=
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#include "appupdater.h"
|
||||
#include "applicationupdater.h"
|
||||
|
||||
#include <QDir>
|
||||
#include <QFile>
|
||||
|
@ -19,23 +19,23 @@ static const QString chopRcSuffix(const QString &str) {
|
|||
return suffixIdx < 0 ? str : str.chopped(str.length() - suffixIdx);
|
||||
}
|
||||
|
||||
AppUpdater::AppUpdater(QObject *parent):
|
||||
ApplicationUpdater::ApplicationUpdater(QObject *parent):
|
||||
QObject(parent),
|
||||
m_state(State::Idle),
|
||||
m_progress(0)
|
||||
{}
|
||||
|
||||
AppUpdater::State AppUpdater::state() const
|
||||
ApplicationUpdater::State ApplicationUpdater::state() const
|
||||
{
|
||||
return m_state;
|
||||
}
|
||||
|
||||
double AppUpdater::progress() const
|
||||
double ApplicationUpdater::progress() const
|
||||
{
|
||||
return m_progress;
|
||||
}
|
||||
|
||||
bool AppUpdater::canUpdate(const Flipper::Updates::VersionInfo &versionInfo)
|
||||
bool ApplicationUpdater::canUpdate(const Flipper::Updates::VersionInfo &versionInfo)
|
||||
{
|
||||
const auto appDate = QDateTime::fromSecsSinceEpoch(APP_TIMESTAMP).date();
|
||||
const auto appVersion = QStringLiteral(APP_VERSION);
|
||||
|
@ -61,7 +61,7 @@ bool AppUpdater::canUpdate(const Flipper::Updates::VersionInfo &versionInfo)
|
|||
}
|
||||
}
|
||||
|
||||
void AppUpdater::installUpdate(const Flipper::Updates::VersionInfo &versionInfo)
|
||||
void ApplicationUpdater::installUpdate(const Flipper::Updates::VersionInfo &versionInfo)
|
||||
{
|
||||
#ifdef Q_OS_WINDOWS
|
||||
const auto fileInfo = versionInfo.fileInfo(QStringLiteral("installer"), QStringLiteral("windows/amd64"));
|
||||
|
@ -138,7 +138,7 @@ void AppUpdater::installUpdate(const Flipper::Updates::VersionInfo &versionInfo)
|
|||
}
|
||||
});
|
||||
|
||||
connect(fetcher, &RemoteFileFetcher::progressChanged, this, &AppUpdater::setProgress);
|
||||
connect(fetcher, &RemoteFileFetcher::progressChanged, this, &ApplicationUpdater::setProgress);
|
||||
|
||||
if(!fetcher->fetch(fileInfo, file)) {
|
||||
qCWarning(CATEGORY_SELFUPDATES) << "Failed to start downloading the update package.";
|
||||
|
@ -153,7 +153,7 @@ void AppUpdater::installUpdate(const Flipper::Updates::VersionInfo &versionInfo)
|
|||
}
|
||||
}
|
||||
|
||||
void AppUpdater::setState(State state)
|
||||
void ApplicationUpdater::setState(State state)
|
||||
{
|
||||
if(m_state == state) {
|
||||
return;
|
||||
|
@ -163,7 +163,7 @@ void AppUpdater::setState(State state)
|
|||
emit stateChanged();
|
||||
}
|
||||
|
||||
void AppUpdater::setProgress(double progress)
|
||||
void ApplicationUpdater::setProgress(double progress)
|
||||
{
|
||||
if(qFuzzyCompare(m_progress, progress)) {
|
||||
return;
|
||||
|
@ -173,7 +173,7 @@ void AppUpdater::setProgress(double progress)
|
|||
emit progressChanged();
|
||||
}
|
||||
|
||||
bool AppUpdater::performUpdate(const QString &path)
|
||||
bool ApplicationUpdater::performUpdate(const QString &path)
|
||||
{
|
||||
const auto exitApplication = []() {
|
||||
qCInfo(CATEGORY_SELFUPDATES) << "Update started, exiting the application...";
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
#include "flipperupdates.h"
|
||||
|
||||
class AppUpdater : public QObject
|
||||
class ApplicationUpdater : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(double progress READ progress NOTIFY progressChanged)
|
||||
|
@ -20,7 +20,7 @@ public:
|
|||
|
||||
Q_ENUM(State)
|
||||
|
||||
AppUpdater(QObject *parent = nullptr);
|
||||
ApplicationUpdater(QObject *parent = nullptr);
|
||||
|
||||
State state() const;
|
||||
double progress() const;
|
|
@ -1,11 +1,13 @@
|
|||
import QtQuick 2.15
|
||||
|
||||
import QFlipper 1.0
|
||||
|
||||
Item {
|
||||
id: overlay
|
||||
|
||||
property Rectangle backgroundRect
|
||||
|
||||
readonly property var device: deviceRegistry.currentDevice
|
||||
readonly property var device: Backend.currentDevice
|
||||
readonly property var deviceState: device ? device.state : undefined
|
||||
readonly property var deviceInfo: deviceState ? deviceState.info : undefined
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@ ColumnLayout {
|
|||
property alias reinstallAction: reinstallAction
|
||||
property alias selfUpdateAction: selfUpdateAction
|
||||
|
||||
readonly property var device: deviceRegistry.currentDevice
|
||||
readonly property var device: Backend.currentDevice
|
||||
readonly property var deviceState: device ? device.state : undefined
|
||||
|
||||
TransparentLabel {
|
||||
|
@ -146,13 +146,12 @@ ColumnLayout {
|
|||
Action {
|
||||
id: reinstallAction
|
||||
text: qsTr("Reinstall")
|
||||
enabled: firmwareUpdates.isReady && !!deviceState && !deviceState.isRecoveryMode &&
|
||||
!(device.updater.canUpdate(firmwareUpdates.latestVersion) || device.updater.canInstall())
|
||||
enabled: Backend.updateStatus === Backend.NoUpdates
|
||||
}
|
||||
|
||||
Action {
|
||||
id: selfUpdateAction
|
||||
text: qsTr("Update qFlipper")
|
||||
enabled: applicationUpdates.isReady && app.updater.canUpdate(applicationUpdates.latestVersion)
|
||||
text: qsTr("Check app updates")
|
||||
enabled: false//applicationUpdates.isReady && app.updater.canUpdate(applicationUpdates.latestVersion)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,13 +1,14 @@
|
|||
import QtQuick 2.15
|
||||
import QtQuick.Layouts 1.15
|
||||
|
||||
import QFlipper 1.0
|
||||
import Theme 1.0
|
||||
|
||||
RowLayout {
|
||||
id: control
|
||||
spacing: 30
|
||||
|
||||
readonly property var device: deviceRegistry.currentDevice
|
||||
readonly property var device: Backend.currentDevice
|
||||
readonly property var deviceState: device ? device.state : undefined
|
||||
readonly property var deviceInfo: deviceState ? deviceState.info : undefined
|
||||
readonly property bool extraFields: deviceState ? !deviceState.isRecoveryMode : false
|
||||
|
|
|
@ -9,7 +9,7 @@ Image {
|
|||
|
||||
signal screenStreamRequested
|
||||
|
||||
readonly property var device: deviceRegistry.currentDevice
|
||||
readonly property var device: Backend.currentDevice
|
||||
readonly property var deviceState: device ? device.state : undefined
|
||||
|
||||
visible: opacity > 0
|
||||
|
|
|
@ -0,0 +1,72 @@
|
|||
import QtQuick 2.15
|
||||
import QtQuick.Controls 2.15
|
||||
|
||||
import QFlipper 1.0
|
||||
import Theme 1.0
|
||||
|
||||
AbstractOverlay {
|
||||
id: overlay
|
||||
|
||||
TextLabel {
|
||||
id: updateLabel
|
||||
capitalized: false
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
y: 24
|
||||
|
||||
color: Backend.state === Backend.ErrorOccured ? Theme.color.lightred3 : Theme.color.lightorange2
|
||||
|
||||
font.family: "Born2bSportyV2"
|
||||
font.pixelSize: 48
|
||||
|
||||
text: Backend.state === Backend.ErrorOccured ? qsTr("Epic fail!"): qsTr("Success!")
|
||||
}
|
||||
|
||||
TextLabel {
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
|
||||
anchors.topMargin: 45
|
||||
anchors.leftMargin: 35
|
||||
|
||||
text: "Work in progress"
|
||||
}
|
||||
|
||||
TextLabel {
|
||||
anchors.top: parent.top
|
||||
anchors.right: parent.right
|
||||
|
||||
anchors.topMargin: 45
|
||||
anchors.rightMargin: 35
|
||||
|
||||
text: "Work in progress"
|
||||
}
|
||||
|
||||
TextLabel {
|
||||
id: messageLabel
|
||||
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.bottomMargin: 105
|
||||
|
||||
text: Backend.state === Backend.Finished ? qsTr("You're good to go!") : deviceState ? deviceState.errorString : qsTr("Cannot connect to device")
|
||||
}
|
||||
|
||||
Button {
|
||||
id: backButton
|
||||
action: backAction
|
||||
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.bottomMargin: 35
|
||||
|
||||
icon.width: 24
|
||||
icon.height: 24
|
||||
icon.source: "qrc:/assets/gfx/symbolic/arrow-back.svg"
|
||||
}
|
||||
|
||||
Action {
|
||||
id: backAction
|
||||
text: qsTr("Back")
|
||||
onTriggered: Backend.finalizeOperation()
|
||||
}
|
||||
}
|
|
@ -128,8 +128,16 @@ AbstractOverlay {
|
|||
x: Math.round(centerX - width / 2)
|
||||
y: 265
|
||||
|
||||
accent: !(firmwareUpdates.isReady && deviceState) ? accent : deviceState.isRecoveryMode ? UpdateButton.Blue :
|
||||
device.updater.canUpdate(firmwareUpdates.latestVersion) ? UpdateButton.Green : UpdateButton.Default
|
||||
accent: {
|
||||
switch(Backend.updateStatus) {
|
||||
case Backend.CanRepair:
|
||||
return UpdateButton.Blue;
|
||||
case Backend.CanUpdate:
|
||||
return UpdateButton.Green;
|
||||
default:
|
||||
return UpdateButton.Default;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LinkButton {
|
||||
|
@ -168,12 +176,22 @@ AbstractOverlay {
|
|||
Action {
|
||||
id: updateButtonAction
|
||||
|
||||
enabled: firmwareUpdates.isReady && !!deviceState &&
|
||||
(deviceState.isRecoveryMode || device.updater.canUpdate(firmwareUpdates.latestVersion) || device.updater.canInstall())
|
||||
|
||||
text: !(firmwareUpdates.isReady && deviceState) ? qsTr("No data") : deviceState.isRecoveryMode ? qsTr("Repair") :
|
||||
device.updater.canUpdate(firmwareUpdates.latestVersion) ? qsTr("Update") :
|
||||
device.updater.canInstall() ? qsTr("Install") : qsTr("No updates")
|
||||
enabled: (Backend.updateStatus !== Backend.Unknown) &&
|
||||
(Backend.updateStatus !== Backend.NoUpdates)
|
||||
text: {
|
||||
switch(Backend.updateStatus) {
|
||||
case Backend.CanRepair:
|
||||
return qsTr("Repair");
|
||||
case Backend.CanUpdate:
|
||||
return qsTr("Update");
|
||||
case Backend.CanInstall:
|
||||
return qsTr("Install");
|
||||
case Backend.NoUpdates:
|
||||
return qsTr("No updates");
|
||||
case Backend.Unknown:
|
||||
return qsTr("No data");
|
||||
}
|
||||
}
|
||||
|
||||
onTriggered: updateButtonFunc()
|
||||
}
|
||||
|
@ -213,23 +231,13 @@ AbstractOverlay {
|
|||
const channelName = Preferences.updateChannel;
|
||||
const latestVersion = firmwareUpdates.latestVersion;
|
||||
|
||||
let messageObj, actionFunc;
|
||||
|
||||
if(deviceState.isRecoveryMode) {
|
||||
messageObj = {
|
||||
const messageObj = deviceState.isRecoveryMode ? {
|
||||
title : qsTr("Repair Device?"),
|
||||
customText: qsTr("Repair"),
|
||||
message : qsTr("Firmware <font color=\"%1\">%2</font><br/>will be installed")
|
||||
.arg(releaseButton.linkColor)
|
||||
.arg(releaseButton.text)
|
||||
};
|
||||
|
||||
actionFunc = function() {
|
||||
device.updater.fullRepair(latestVersion);
|
||||
}
|
||||
|
||||
} else {
|
||||
messageObj = {
|
||||
} : {
|
||||
title : qsTr("Update firmware?"),
|
||||
customText: qsTr("Update"),
|
||||
message: qsTr("New firmware <font color=\"%1\">%2</font><br/>will be installed")
|
||||
|
@ -237,12 +245,7 @@ AbstractOverlay {
|
|||
.arg(releaseButton.text),
|
||||
};
|
||||
|
||||
actionFunc = function() {
|
||||
device.updater.fullUpdate(latestVersion);
|
||||
}
|
||||
}
|
||||
|
||||
confirmationDialog.openWithMessage(actionFunc, messageObj);
|
||||
confirmationDialog.openWithMessage(Backend.mainAction, messageObj);
|
||||
}
|
||||
|
||||
function installFromFile() {
|
||||
|
@ -257,7 +260,7 @@ AbstractOverlay {
|
|||
};
|
||||
|
||||
const actionFunc = function() {
|
||||
device.updater.localFirmwareInstall(fileDialog.fileUrl);
|
||||
Backend.installFirmware(fileDialog.fileUrl);
|
||||
}
|
||||
|
||||
confirmationDialog.openWithMessage(actionFunc, messageObj);
|
||||
|
@ -278,7 +281,7 @@ AbstractOverlay {
|
|||
};
|
||||
|
||||
const actionFunc = function() {
|
||||
device.updater.backupInternalStorage(fileDialog.fileUrl);
|
||||
Backend.createBackup(fileDialog.fileUrl);
|
||||
}
|
||||
|
||||
confirmationDialog.openWithMessage(actionFunc, messageObj);
|
||||
|
@ -299,7 +302,7 @@ AbstractOverlay {
|
|||
};
|
||||
|
||||
const actionFunc = function() {
|
||||
device.updater.restoreInternalStorage(fileDialog.fileUrl);
|
||||
Backend.restoreBackup(fileDialog.fileUrl);
|
||||
}
|
||||
|
||||
confirmationDialog.openWithMessage(actionFunc, messageObj);
|
||||
|
@ -316,11 +319,7 @@ AbstractOverlay {
|
|||
customText: qsTr("Erase")
|
||||
};
|
||||
|
||||
const actionFunc = function() {
|
||||
device.updater.factoryReset();
|
||||
}
|
||||
|
||||
confirmationDialog.openWithMessage(actionFunc, messageObj);
|
||||
confirmationDialog.openWithMessage(Backend.factoryReset, messageObj);
|
||||
}
|
||||
|
||||
function reinstallFirmware() {
|
||||
|
@ -330,11 +329,7 @@ AbstractOverlay {
|
|||
message: qsTr("Current firmware version will be reinstalled")
|
||||
};
|
||||
|
||||
const actionFunc = function() {
|
||||
device.updater.fullUpdate(firmwareUpdates.latestVersion);
|
||||
}
|
||||
|
||||
confirmationDialog.openWithMessage(actionFunc, messageObj);
|
||||
confirmationDialog.openWithMessage(Backend.mainAction, messageObj);
|
||||
}
|
||||
|
||||
function baseName(fileUrl) {
|
||||
|
|
|
@ -10,14 +10,6 @@ import QFlipper 1.0
|
|||
Item {
|
||||
id: mainWindow
|
||||
|
||||
enum WindowState {
|
||||
NoDevice,
|
||||
Ready,
|
||||
Updating,
|
||||
Streaming,
|
||||
SelfUpdating
|
||||
}
|
||||
|
||||
signal expandStarted
|
||||
signal expandFinished
|
||||
signal collapseStarted
|
||||
|
@ -29,40 +21,17 @@ Item {
|
|||
|
||||
readonly property int baseWidth: 830
|
||||
readonly property int baseHeight: 500
|
||||
// TODO: remember log height
|
||||
|
||||
readonly property int logHeight: 200
|
||||
readonly property int minimumLogHeight: 200
|
||||
|
||||
readonly property int shadowSize: 16
|
||||
readonly property int shadowOffset: 4
|
||||
|
||||
readonly property var device: deviceRegistry.currentDevice
|
||||
readonly property var device: Backend.currentDevice
|
||||
readonly property var deviceState: device ? device.state : undefined
|
||||
readonly property var deviceInfo: deviceState ? deviceState.info : undefined
|
||||
|
||||
readonly property int windowState: {
|
||||
if(app.updater.state != AppUpdater.Idle) {
|
||||
MainWindow.SelfUpdating
|
||||
} else if(!deviceState ) {
|
||||
MainWindow.NoDevice
|
||||
} else if(deviceState.isPersistent) {
|
||||
MainWindow.Updating
|
||||
} else if(streamOverlay.visible) {
|
||||
MainWindow.Streaming
|
||||
} else {
|
||||
MainWindow.Ready
|
||||
}
|
||||
}
|
||||
|
||||
onWindowStateChanged: {
|
||||
if(windowState !== MainWindow.NoDevice) {
|
||||
device.streamer.enabled = (windowState === MainWindow.Ready) ||
|
||||
(windowState === MainWindow.Streaming);
|
||||
} else {
|
||||
streamOverlay.opacity = 0;
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
if(applicationUpdates.isReady) {
|
||||
askForSelfUpdate();
|
||||
|
@ -111,6 +80,12 @@ Item {
|
|||
parent: bg
|
||||
}
|
||||
|
||||
SelfUpdateDialog {
|
||||
id: selfUpdateDialog
|
||||
radius: bg.radius
|
||||
parent: bg
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: blackBorder
|
||||
anchors.fill: parent
|
||||
|
@ -135,7 +110,7 @@ Item {
|
|||
samples: shadowSize * 2 + 1
|
||||
horizontalOffset: 0
|
||||
verticalOffset: shadowOffset
|
||||
color: Qt.rgba(0, 0, 0, 0.3)
|
||||
color: Qt.rgba(0, 0, 0, 0.7)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -190,44 +165,46 @@ Item {
|
|||
|
||||
DeviceWidget {
|
||||
id: deviceWidget
|
||||
opacity: (windowState === MainWindow.Streaming) || (windowState === MainWindow.SelfUpdating) ? 0 : 1
|
||||
x: windowState === MainWindow.Ready ? Math.round(mainContent.width / 2) : 216
|
||||
opacity: streamOverlay.visible ? 0 : 1
|
||||
x: Backend.state === Backend.Ready ? Math.round(mainContent.width / 2) : 216
|
||||
y: 85
|
||||
|
||||
onScreenStreamRequested: streamOverlay.opacity = 1
|
||||
onScreenStreamRequested: Backend.startFullScreenStreaming()
|
||||
}
|
||||
|
||||
NoDeviceOverlay {
|
||||
id: noDeviceOverlay
|
||||
anchors.fill: parent
|
||||
opacity: windowState === MainWindow.NoDevice ? 1 : 0
|
||||
opacity: Backend.state === Backend.WaitingForDevices ? 1 : 0
|
||||
}
|
||||
|
||||
HomeOverlay {
|
||||
id: homeOverlay
|
||||
backgroundRect: bg
|
||||
anchors.fill: parent
|
||||
opacity: windowState === MainWindow.Ready ? 1 : 0
|
||||
opacity: Backend.state === Backend.Ready ? 1 : 0
|
||||
}
|
||||
|
||||
UpdateOverlay {
|
||||
id: updateOverlay
|
||||
backgroundRect: bg
|
||||
anchors.fill: parent
|
||||
opacity: windowState === MainWindow.Updating ? 1 : 0
|
||||
opacity: (Backend.state > Backend.ScreenStreaming) &&
|
||||
(Backend.state < Backend.Finished) ? 1 : 0
|
||||
}
|
||||
|
||||
SelfUpdateOverlay {
|
||||
id: selfUpdateOverlay
|
||||
FinishOverlay {
|
||||
id: finishOverlay
|
||||
backgroundRect: bg
|
||||
anchors.fill: parent
|
||||
opacity: windowState === MainWindow.SelfUpdating ? 1 : 0
|
||||
opacity: (Backend.state === Backend.Finished) ||
|
||||
(Backend.state === Backend.ErrorOccured) ? 1 : 0
|
||||
}
|
||||
|
||||
StreamOverlay {
|
||||
id: streamOverlay
|
||||
anchors.fill: parent
|
||||
opacity: 0
|
||||
opacity: Backend.state === Backend.ScreenStreaming ? 1 : 0
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -351,16 +328,7 @@ Item {
|
|||
|
||||
function askForSelfUpdate() {
|
||||
if(app.updater.canUpdate(applicationUpdates.latestVersion)) {
|
||||
const messageObj = {
|
||||
title : qsTr("Update qFlipper?"),
|
||||
message: qsTr("Newer version of qFlipper<br/>will be installed"),
|
||||
customText: qsTr("Update")
|
||||
};
|
||||
|
||||
confirmationDialog.openWithMessage(function() {
|
||||
app.updater.installUpdate(applicationUpdates.latestVersion);
|
||||
}, messageObj);
|
||||
selfUpdateDialog.open();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,116 @@
|
|||
import QtQuick 2.15
|
||||
import QtQuick.Layouts 1.15
|
||||
import QtQuick.Controls 2.15
|
||||
|
||||
import Theme 1.0
|
||||
import QFlipper 1.0
|
||||
|
||||
CustomDialog {
|
||||
id: control
|
||||
|
||||
closable: false
|
||||
closePolicy: Popup.NoAutoClose
|
||||
|
||||
title: app.updater.state === AppUpdater.Idle ? qsTr("Update qFlipper?") : qsTr("Updating qFlipper")
|
||||
|
||||
contentWidget: Item {
|
||||
implicitWidth: 430
|
||||
implicitHeight: layout.implicitHeight
|
||||
|
||||
ColumnLayout {
|
||||
id: layout
|
||||
width: parent.implicitWidth
|
||||
|
||||
TextLabel {
|
||||
id: messageLabel
|
||||
visible: app.updater.state === AppUpdater.Idle
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
text: qsTr("Newer version of qFlipper<br/>will be installed")
|
||||
|
||||
lineHeight: 1.4
|
||||
wrapMode: Text.Wrap
|
||||
|
||||
Layout.topMargin: 24
|
||||
Layout.bottomMargin: -8
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
// TODO: Find a way to use a DialogButtonBox properly
|
||||
|
||||
RowLayout {
|
||||
id: buttonBox
|
||||
visible: app.updater.state === AppUpdater.Idle
|
||||
spacing: 30
|
||||
Layout.margins: 20
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 42
|
||||
layoutDirection: Qt.platform.os === "osx" ? Qt.RightToLeft : Qt.LeftToRight
|
||||
|
||||
SmallButton {
|
||||
radius: 7
|
||||
text: qsTr("Update")
|
||||
highlighted: true
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
onClicked: app.updater.installUpdate(applicationUpdates.latestVersion);
|
||||
}
|
||||
|
||||
SmallButton {
|
||||
radius: 7
|
||||
text: qsTr("Cancel")
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
onClicked: control.rejected()
|
||||
}
|
||||
}
|
||||
|
||||
ProgressBar {
|
||||
id: progressBar
|
||||
|
||||
visible: app.updater.state !== AppUpdater.Idle
|
||||
|
||||
implicitWidth: 286
|
||||
implicitHeight: 56
|
||||
|
||||
from: 0
|
||||
to: 100
|
||||
|
||||
value: app.updater.progress
|
||||
indeterminate: value < 0
|
||||
|
||||
Layout.topMargin: 40
|
||||
Layout.bottomMargin: 20
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
}
|
||||
|
||||
TextLabel {
|
||||
id: progressLabel
|
||||
padding: 0
|
||||
|
||||
visible: app.updater.state !== AppUpdater.Idle
|
||||
|
||||
text: {
|
||||
switch(app.updater.state) {
|
||||
case AppUpdater.Downloading:
|
||||
return qsTr("Downloading latest version...");
|
||||
case AppUpdater.Updating:
|
||||
return qsTr("Starting update process...");
|
||||
case AppUpdater.ErrorOccured:
|
||||
return qsTr("Update failed");
|
||||
default:
|
||||
return qsTr("Preparing...");
|
||||
}
|
||||
}
|
||||
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.bottomMargin: 14
|
||||
|
||||
color: app.updater.state === AppUpdater.ErrorOccured ? Theme.color.lightred3 : Theme.color.lightorange2
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,60 +0,0 @@
|
|||
import QtQuick 2.15
|
||||
import QtQuick.Controls 2.15
|
||||
|
||||
import Theme 1.0
|
||||
import QFlipper 1.0
|
||||
|
||||
AbstractOverlay {
|
||||
id: overlay
|
||||
|
||||
TextLabel {
|
||||
id: updateLabel
|
||||
capitalized: false
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
y: 24
|
||||
|
||||
color: Theme.color.lightorange2
|
||||
|
||||
font.family: "Born2bSportyV2"
|
||||
font.pixelSize: 48
|
||||
|
||||
text: qsTr("Updating qFlipper")
|
||||
}
|
||||
|
||||
ProgressBar {
|
||||
id: progressBar
|
||||
|
||||
width: 280
|
||||
height: 56
|
||||
|
||||
from: 0
|
||||
to: 100
|
||||
|
||||
x: Math.round((parent.width - width) / 2)
|
||||
y: 265
|
||||
|
||||
value: app.updater.progress
|
||||
indeterminate: value < 0
|
||||
}
|
||||
|
||||
TextLabel {
|
||||
id: messageLabel
|
||||
anchors.top: progressBar.bottom
|
||||
anchors.topMargin: 20
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
|
||||
text: {
|
||||
switch(app.updater.state) {
|
||||
case AppUpdater.Downloading:
|
||||
return qsTr("Downloading latest version...");
|
||||
case AppUpdater.Updating:
|
||||
return qsTr("Starting update process...");
|
||||
case AppUpdater.ErrorOccured:
|
||||
return qsTr("Update failed");
|
||||
default:
|
||||
return qsTr("Preparing...");
|
||||
}
|
||||
}
|
||||
color: Theme.color.lightorange2
|
||||
}
|
||||
}
|
|
@ -63,6 +63,9 @@ AbstractOverlay {
|
|||
|
||||
Button {
|
||||
action: backAction
|
||||
|
||||
icon.width: 24
|
||||
icon.height: 24
|
||||
icon.source: "qrc:/assets/gfx/symbolic/arrow-back.svg"
|
||||
}
|
||||
|
||||
|
@ -74,8 +77,8 @@ AbstractOverlay {
|
|||
action: saveAction
|
||||
Layout.alignment: Qt.AlignRight
|
||||
|
||||
icon.height: 20
|
||||
icon.width: 20
|
||||
icon.height: 20
|
||||
icon.source: "qrc:/assets/gfx/symbolic/save-symbolic.svg"
|
||||
}
|
||||
}
|
||||
|
@ -123,7 +126,7 @@ AbstractOverlay {
|
|||
id: backAction
|
||||
text: qsTr("Back")
|
||||
enabled: overlay.enabled
|
||||
onTriggered: opacity = 0
|
||||
onTriggered: Backend.stopFullScreenStreaming()
|
||||
}
|
||||
|
||||
Action {
|
||||
|
|
|
@ -90,7 +90,6 @@
|
|||
<file>assets/fonts/ProggySquare.ttf</file>
|
||||
<file>assets/fonts/ShareTechMono-Regular.ttf</file>
|
||||
<file>components/TextView.qml</file>
|
||||
<file>components/SelfUpdateOverlay.qml</file>
|
||||
<file>components/AbstractOverlay.qml</file>
|
||||
<file>style/Menu.qml</file>
|
||||
<file>style/MenuItem.qml</file>
|
||||
|
@ -100,5 +99,7 @@
|
|||
<file>assets/gfx/symbolic/save-symbolic.svg</file>
|
||||
<file>assets/gfx/symbolic/info-big.svg</file>
|
||||
<file>assets/gfx/images/streaming-help.svg</file>
|
||||
<file>components/FinishOverlay.qml</file>
|
||||
<file>components/SelfUpdateDialog.qml</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
|
|
@ -2,15 +2,15 @@
|
|||
|
||||
#include <QTimer>
|
||||
|
||||
#include "debug.h"
|
||||
|
||||
AbstractOperation::AbstractOperation(QObject *parent):
|
||||
QObject(parent),
|
||||
m_timeout(new QTimer(this)),
|
||||
m_timeoutTimer(new QTimer(this)),
|
||||
m_operationState(BasicOperationState::Ready)
|
||||
{
|
||||
connect(m_timeout, &QTimer::timeout, this, &AbstractOperation::onOperationTimeout);
|
||||
m_timeout->setSingleShot(true);
|
||||
connect(m_timeoutTimer, &QTimer::timeout, this, &AbstractOperation::onOperationTimeout);
|
||||
|
||||
m_timeoutTimer->setSingleShot(true);
|
||||
m_timeoutTimer->setInterval(10000);
|
||||
}
|
||||
|
||||
void AbstractOperation::finish()
|
||||
|
@ -26,6 +26,11 @@ int AbstractOperation::operationState() const
|
|||
return m_operationState;
|
||||
}
|
||||
|
||||
void AbstractOperation::setTimeout(int msec)
|
||||
{
|
||||
m_timeoutTimer->setInterval(msec);
|
||||
}
|
||||
|
||||
void AbstractOperation::onOperationTimeout()
|
||||
{
|
||||
finishWithError(QStringLiteral("Operation timeout (generic)"));
|
||||
|
@ -42,12 +47,12 @@ void AbstractOperation::finishWithError(const QString &errorMsg)
|
|||
finish();
|
||||
}
|
||||
|
||||
void AbstractOperation::startTimeout(int msec)
|
||||
void AbstractOperation::startTimeout()
|
||||
{
|
||||
m_timeout->start(msec);
|
||||
m_timeoutTimer->start();
|
||||
}
|
||||
|
||||
void AbstractOperation::stopTimeout()
|
||||
{
|
||||
m_timeout->stop();
|
||||
m_timeoutTimer->stop();
|
||||
}
|
||||
|
|
|
@ -25,6 +25,7 @@ public:
|
|||
virtual void finish();
|
||||
|
||||
int operationState() const;
|
||||
void setTimeout(int msec);
|
||||
|
||||
signals:
|
||||
void started();
|
||||
|
@ -33,14 +34,14 @@ signals:
|
|||
protected slots:
|
||||
virtual void onOperationTimeout();
|
||||
|
||||
void startTimeout();
|
||||
void stopTimeout();
|
||||
|
||||
protected:
|
||||
void setOperationState(int state);
|
||||
void finishWithError(const QString &errorMsg);
|
||||
|
||||
void startTimeout(int msec = 10000);
|
||||
void stopTimeout();
|
||||
|
||||
private:
|
||||
QTimer *m_timeout;
|
||||
QTimer *m_timeoutTimer;
|
||||
int m_operationState;
|
||||
};
|
||||
|
|
|
@ -6,8 +6,6 @@
|
|||
|
||||
#include "abstractoperation.h"
|
||||
|
||||
#define CALL_LATER(obj, func) (QTimer::singleShot(0, obj, func))
|
||||
|
||||
Q_LOGGING_CATEGORY(CATEGORY_DEFAULT, "DEFAULT")
|
||||
|
||||
AbstractOperationRunner::AbstractOperationRunner(QObject *parent):
|
||||
|
@ -15,30 +13,6 @@ AbstractOperationRunner::AbstractOperationRunner(QObject *parent):
|
|||
m_state(State::Idle)
|
||||
{}
|
||||
|
||||
bool AbstractOperationRunner::onQueueStarted()
|
||||
{
|
||||
//Default empty implementation
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AbstractOperationRunner::onQueueFinished()
|
||||
{
|
||||
//Default empty implementation
|
||||
return true;
|
||||
}
|
||||
|
||||
void AbstractOperationRunner::onOperationStarted(AbstractOperation *operation)
|
||||
{
|
||||
Q_UNUSED(operation);
|
||||
//Default empty implementation
|
||||
}
|
||||
|
||||
void AbstractOperationRunner::onOperationFinished(AbstractOperation *operation)
|
||||
{
|
||||
Q_UNUSED(operation);
|
||||
//Default empty implementation
|
||||
}
|
||||
|
||||
const QLoggingCategory &AbstractOperationRunner::loggingCategory() const
|
||||
{
|
||||
return CATEGORY_DEFAULT();
|
||||
|
@ -48,13 +22,7 @@ void AbstractOperationRunner::enqueueOperation(AbstractOperation *operation)
|
|||
{
|
||||
if(m_state == State::Idle) {
|
||||
m_state = State::Running;
|
||||
|
||||
if(!onQueueStarted()) {
|
||||
setError(QStringLiteral("Failed to start the operation queue"));
|
||||
return;
|
||||
}
|
||||
|
||||
CALL_LATER(this, &AbstractOperationRunner::processQueue);
|
||||
QTimer::singleShot(0, this, &AbstractOperationRunner::processQueue);
|
||||
}
|
||||
|
||||
m_queue.enqueue(operation);
|
||||
|
@ -64,11 +32,6 @@ void AbstractOperationRunner::processQueue()
|
|||
{
|
||||
if(m_queue.isEmpty()) {
|
||||
m_state = State::Idle;
|
||||
|
||||
if(!onQueueFinished()) {
|
||||
setError(QStringLiteral("Failed to finish the operation queue"));
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -82,18 +45,16 @@ void AbstractOperationRunner::processQueue()
|
|||
processQueue();
|
||||
|
||||
} else {
|
||||
CALL_LATER(this, &AbstractOperationRunner::processQueue);
|
||||
QTimer::singleShot(0, this, &AbstractOperationRunner::processQueue);
|
||||
}
|
||||
|
||||
qCInfo(loggingCategory()).noquote() << operation->description() << (operation->isError() ? QStringLiteral("ERROR: ") + operation->errorString() : QStringLiteral("SUCCESS"));
|
||||
|
||||
onOperationFinished(operation);
|
||||
qCInfo(loggingCategory()).noquote() << operation->description() << (operation->isError() ? QStringLiteral("ERROR: ") +
|
||||
operation->errorString() : QStringLiteral("SUCCESS"));
|
||||
operation->deleteLater();
|
||||
});
|
||||
|
||||
qCInfo(loggingCategory()).noquote() << operation->description() << "START";
|
||||
|
||||
onOperationStarted(operation);
|
||||
operation->start();
|
||||
}
|
||||
|
||||
|
|
|
@ -21,11 +21,8 @@ public:
|
|||
AbstractOperationRunner(QObject *parent = nullptr);
|
||||
|
||||
protected:
|
||||
virtual bool onQueueStarted();
|
||||
virtual bool onQueueFinished();
|
||||
|
||||
virtual void onOperationStarted(AbstractOperation *operation);
|
||||
virtual void onOperationFinished(AbstractOperation *operation);
|
||||
template<class T>
|
||||
T* registerOperation(T* operation);
|
||||
|
||||
virtual const QLoggingCategory &loggingCategory() const;
|
||||
|
||||
|
@ -40,3 +37,10 @@ private:
|
|||
OperationQueue m_queue;
|
||||
};
|
||||
|
||||
template<class T>
|
||||
T *AbstractOperationRunner::registerOperation(T *operation)
|
||||
{
|
||||
enqueueOperation(operation);
|
||||
return operation;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,181 @@
|
|||
#pragma once
|
||||
|
||||
#include <QSerialPort>
|
||||
|
||||
#include <pb.h>
|
||||
#include <pb_encode.h>
|
||||
#include <pb_decode.h>
|
||||
|
||||
template<typename Msg>
|
||||
class AbstractProtobufMessage
|
||||
{
|
||||
public:
|
||||
AbstractProtobufMessage(QSerialPort *serialPort);
|
||||
virtual ~AbstractProtobufMessage() {}
|
||||
|
||||
protected:
|
||||
QSerialPort *serialPort() const;
|
||||
|
||||
Msg *pbMessage();
|
||||
const Msg *pbMessage() const;
|
||||
|
||||
private:
|
||||
QSerialPort *m_serialPort;
|
||||
Msg m_message;
|
||||
};
|
||||
|
||||
template<const pb_msgdesc_t *MsgDesc, typename Msg>
|
||||
class AbstractProtobufRequest : public AbstractProtobufMessage<Msg>
|
||||
{
|
||||
public:
|
||||
AbstractProtobufRequest(QSerialPort *serialPort);
|
||||
virtual ~AbstractProtobufRequest() {}
|
||||
|
||||
bool send();
|
||||
qint64 bytesWritten() const;
|
||||
|
||||
private:
|
||||
static bool outputCallback(pb_ostream_t *stream, const pb_byte_t *buf, size_t count);
|
||||
qint64 m_bytesWritten;
|
||||
};
|
||||
|
||||
template<const pb_msgdesc_t *MsgDesc, typename Msg>
|
||||
class AbstractProtobufResponse : public AbstractProtobufMessage<Msg>
|
||||
{
|
||||
public:
|
||||
AbstractProtobufResponse(QSerialPort *serialPort);
|
||||
virtual ~AbstractProtobufResponse();
|
||||
|
||||
bool receive();
|
||||
bool isComplete() const;
|
||||
void release();
|
||||
|
||||
private:
|
||||
static bool inputCallback(pb_istream_t *stream, pb_byte_t *buf, size_t count);
|
||||
bool m_isComplete;
|
||||
};
|
||||
|
||||
template<typename Msg>
|
||||
AbstractProtobufMessage<Msg>::AbstractProtobufMessage(QSerialPort *serialPort):
|
||||
m_serialPort(serialPort)
|
||||
{
|
||||
memset(&m_message, 0, sizeof(Msg));
|
||||
}
|
||||
|
||||
template<typename Msg>
|
||||
QSerialPort *AbstractProtobufMessage<Msg>::serialPort() const
|
||||
{
|
||||
return m_serialPort;
|
||||
}
|
||||
|
||||
template<typename Msg>
|
||||
Msg *AbstractProtobufMessage<Msg>::pbMessage()
|
||||
{
|
||||
return &m_message;
|
||||
}
|
||||
|
||||
template<typename Msg>
|
||||
const Msg *AbstractProtobufMessage<Msg>::pbMessage() const
|
||||
{
|
||||
return &m_message;
|
||||
}
|
||||
|
||||
template<const pb_msgdesc_t *MsgDesc, typename Msg>
|
||||
AbstractProtobufRequest<MsgDesc, Msg>::AbstractProtobufRequest(QSerialPort *serialPort):
|
||||
AbstractProtobufMessage<Msg>(serialPort),
|
||||
m_bytesWritten(0)
|
||||
{}
|
||||
|
||||
template<const pb_msgdesc_t *MsgDesc, typename Msg>
|
||||
bool AbstractProtobufRequest<MsgDesc, Msg>::send()
|
||||
{
|
||||
auto *serialPort = AbstractProtobufMessage<Msg>::serialPort();
|
||||
|
||||
pb_ostream_t ostream {
|
||||
outputCallback,
|
||||
serialPort,
|
||||
SIZE_MAX,
|
||||
0,
|
||||
nullptr
|
||||
};
|
||||
|
||||
const auto success = pb_encode_ex(&ostream, MsgDesc, AbstractProtobufMessage<Msg>::pbMessage(),
|
||||
PB_ENCODE_DELIMITED) && serialPort->flush();
|
||||
|
||||
m_bytesWritten = success ? ostream.bytes_written : -1;
|
||||
return success;
|
||||
}
|
||||
|
||||
template<const pb_msgdesc_t *MsgDesc, typename Msg>
|
||||
qint64 AbstractProtobufRequest<MsgDesc, Msg>::bytesWritten() const
|
||||
{
|
||||
return m_bytesWritten;
|
||||
}
|
||||
|
||||
template<const pb_msgdesc_t *MsgDesc, typename Msg>
|
||||
bool AbstractProtobufRequest<MsgDesc, Msg>::outputCallback(pb_ostream_t *stream, const pb_byte_t *buf, size_t count)
|
||||
{
|
||||
auto *serialPort = (QSerialPort*)stream->state;
|
||||
return serialPort->write((const char*)buf, count) == (qint64)count;
|
||||
}
|
||||
|
||||
template<const pb_msgdesc_t *MsgDesc, typename Msg>
|
||||
AbstractProtobufResponse<MsgDesc, Msg>::AbstractProtobufResponse(QSerialPort *serialPort):
|
||||
AbstractProtobufMessage<Msg> (serialPort),
|
||||
m_isComplete(false)
|
||||
{}
|
||||
|
||||
template<const pb_msgdesc_t *MsgDesc, typename Msg>
|
||||
AbstractProtobufResponse<MsgDesc, Msg>::~AbstractProtobufResponse()
|
||||
{
|
||||
release();
|
||||
}
|
||||
|
||||
template<const pb_msgdesc_t *MsgDesc, typename Msg>
|
||||
bool AbstractProtobufResponse<MsgDesc, Msg>::receive()
|
||||
{
|
||||
// Allow for re-use: release any previously allocated data
|
||||
release();
|
||||
|
||||
auto *serialPort = AbstractProtobufMessage<Msg>::serialPort();
|
||||
|
||||
pb_istream_t istream {
|
||||
inputCallback,
|
||||
serialPort,
|
||||
(size_t)serialPort->bytesAvailable(),
|
||||
nullptr
|
||||
};
|
||||
|
||||
serialPort->startTransaction();
|
||||
|
||||
m_isComplete = pb_decode_ex(&istream, MsgDesc, AbstractProtobufMessage<Msg>::pbMessage(), PB_DECODE_DELIMITED);
|
||||
|
||||
if(m_isComplete) {
|
||||
serialPort->commitTransaction();
|
||||
} else {
|
||||
serialPort->rollbackTransaction();
|
||||
}
|
||||
|
||||
return m_isComplete;
|
||||
}
|
||||
|
||||
template<const pb_msgdesc_t *MsgDesc, typename Msg>
|
||||
bool AbstractProtobufResponse<MsgDesc, Msg>::isComplete() const
|
||||
{
|
||||
return m_isComplete;
|
||||
}
|
||||
|
||||
template<const pb_msgdesc_t *MsgDesc, typename Msg>
|
||||
void AbstractProtobufResponse<MsgDesc, Msg>::release()
|
||||
{
|
||||
if(m_isComplete) {
|
||||
pb_release(MsgDesc, AbstractProtobufMessage<Msg>::pbMessage());
|
||||
}
|
||||
}
|
||||
|
||||
template<const pb_msgdesc_t *MsgDesc, typename Msg>
|
||||
bool AbstractProtobufResponse<MsgDesc, Msg>::inputCallback(pb_istream_t *stream, pb_byte_t *buf, size_t count)
|
||||
{
|
||||
auto *serialPort = (QSerialPort*)stream->state;
|
||||
return serialPort->read((char*)buf, count) == (qint64)count;
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
#include "abstractprotobufoperation.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QSerialPort>
|
||||
#include <QLoggingCategory>
|
||||
|
||||
AbstractProtobufOperation::AbstractProtobufOperation(QSerialPort *serialPort, QObject *parent):
|
||||
AbstractSerialOperation(serialPort, parent)
|
||||
{}
|
||||
|
||||
AbstractProtobufOperation::~AbstractProtobufOperation()
|
||||
{}
|
|
@ -0,0 +1,13 @@
|
|||
#pragma once
|
||||
|
||||
#include "abstractserialoperation.h"
|
||||
|
||||
class AbstractProtobufOperation : public AbstractSerialOperation
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
AbstractProtobufOperation(QSerialPort *serialPort, QObject *parent = nullptr);
|
||||
virtual ~AbstractProtobufOperation();
|
||||
};
|
||||
|
|
@ -3,27 +3,34 @@
|
|||
#include <QTimer>
|
||||
#include <QSerialPort>
|
||||
|
||||
#include "debug.h"
|
||||
|
||||
#define CALL_LATER(obj, func) (QTimer::singleShot(0, obj, func))
|
||||
|
||||
AbstractSerialOperation::AbstractSerialOperation(QSerialPort *serialPort, QObject *parent):
|
||||
AbstractOperation(parent),
|
||||
m_serialPort(serialPort)
|
||||
m_serialPort(serialPort),
|
||||
m_totalBytesWritten(0)
|
||||
{}
|
||||
|
||||
void AbstractSerialOperation::start()
|
||||
{
|
||||
connect(m_serialPort, &QSerialPort::readyRead, this, &AbstractSerialOperation::startTimeout);
|
||||
connect(m_serialPort, &QSerialPort::readyRead, this, &AbstractSerialOperation::onSerialPortReadyRead);
|
||||
connect(m_serialPort, &QSerialPort::errorOccurred, this, &AbstractSerialOperation::onSerialPortError);
|
||||
connect(m_serialPort, &QSerialPort::bytesWritten, this, &AbstractSerialOperation::onSerialPortBytesWritten);
|
||||
|
||||
CALL_LATER(this, &AbstractSerialOperation::begin);
|
||||
QTimer::singleShot(0, this, [=]() {
|
||||
if(!begin()) {
|
||||
finishWithError(QStringLiteral("Failed to begin operation"));
|
||||
} else {
|
||||
startTimeout();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void AbstractSerialOperation::finish()
|
||||
{
|
||||
disconnect(m_serialPort, &QSerialPort::readyRead, this, &AbstractSerialOperation::startTimeout);
|
||||
disconnect(m_serialPort, &QSerialPort::readyRead, this, &AbstractSerialOperation::onSerialPortReadyRead);
|
||||
disconnect(m_serialPort, &QSerialPort::errorOccurred, this, &AbstractSerialOperation::onSerialPortError);
|
||||
disconnect(m_serialPort, &QSerialPort::bytesWritten, this, &AbstractSerialOperation::onSerialPortBytesWritten);
|
||||
|
||||
AbstractOperation::finish();
|
||||
}
|
||||
|
@ -33,6 +40,32 @@ QSerialPort *AbstractSerialOperation::serialPort() const
|
|||
return m_serialPort;
|
||||
}
|
||||
|
||||
qint64 AbstractSerialOperation::totalBytesWritten() const
|
||||
{
|
||||
return m_totalBytesWritten;
|
||||
}
|
||||
|
||||
void AbstractSerialOperation::resetTotalBytesWritten()
|
||||
{
|
||||
m_totalBytesWritten = 0;
|
||||
}
|
||||
|
||||
void AbstractSerialOperation::onSerialPortReadyRead()
|
||||
{
|
||||
// Empty default implementation
|
||||
}
|
||||
|
||||
void AbstractSerialOperation::onTotalBytesWrittenChanged()
|
||||
{
|
||||
// Empty default implementation
|
||||
}
|
||||
|
||||
void AbstractSerialOperation::onSerialPortBytesWritten(qint64 numBytes)
|
||||
{
|
||||
m_totalBytesWritten += numBytes;
|
||||
onTotalBytesWrittenChanged();
|
||||
}
|
||||
|
||||
void AbstractSerialOperation::onSerialPortError()
|
||||
{
|
||||
finishWithError(m_serialPort->errorString());
|
||||
|
|
|
@ -18,12 +18,19 @@ public:
|
|||
protected:
|
||||
QSerialPort *serialPort() const;
|
||||
|
||||
qint64 totalBytesWritten() const;
|
||||
void resetTotalBytesWritten();
|
||||
|
||||
private slots:
|
||||
virtual void onSerialPortReadyRead() = 0;
|
||||
virtual void onSerialPortReadyRead();
|
||||
virtual void onTotalBytesWrittenChanged();
|
||||
|
||||
void onSerialPortBytesWritten(qint64 numBytes);
|
||||
void onSerialPortError();
|
||||
|
||||
private:
|
||||
virtual bool begin() = 0;
|
||||
|
||||
QSerialPort *m_serialPort;
|
||||
qint64 m_totalBytesWritten;
|
||||
};
|
||||
|
|
|
@ -0,0 +1,226 @@
|
|||
#include "applicationbackend.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QLoggingCategory>
|
||||
|
||||
#include "deviceregistry.h"
|
||||
#include "updateregistry.h"
|
||||
|
||||
#include "preferences.h"
|
||||
#include "flipperupdates.h"
|
||||
|
||||
#include "flipperzero/flipperzero.h"
|
||||
#include "flipperzero/devicestate.h"
|
||||
#include "flipperzero/assetmanifest.h"
|
||||
#include "flipperzero/screenstreamer.h"
|
||||
|
||||
#include "flipperzero/helper/toplevelhelper.h"
|
||||
|
||||
Q_LOGGING_CATEGORY(LOG_BACKEND, "BACKEND")
|
||||
|
||||
using namespace Flipper;
|
||||
using namespace Zero;
|
||||
|
||||
ApplicationBackend::ApplicationBackend(QObject *parent):
|
||||
QObject(parent),
|
||||
m_deviceRegistry(new DeviceRegistry(this)),
|
||||
m_firmwareUpdates(new FirmwareUpdates("https://update.flipperzero.one/firmware/directory.json", this)),
|
||||
m_applicationUpdates(new ApplicationUpdates("https://update.flipperzero.one/qFlipper/directory.json", this)),
|
||||
m_state(State::WaitingForDevices),
|
||||
m_updateStatus(UpdateStatus::Unknown)
|
||||
{
|
||||
registerMetaTypes();
|
||||
registerComparators();
|
||||
|
||||
initConnections();
|
||||
}
|
||||
|
||||
ApplicationBackend::State ApplicationBackend::state() const
|
||||
{
|
||||
return m_state;
|
||||
}
|
||||
|
||||
ApplicationBackend::UpdateStatus ApplicationBackend::updateStatus() const
|
||||
{
|
||||
if(!currentDevice() || !m_firmwareUpdates->isReady()) {
|
||||
return UpdateStatus::Unknown;
|
||||
}
|
||||
|
||||
const auto &latestVersion = m_firmwareUpdates->latestVersion();
|
||||
|
||||
if (currentDevice()->canRepair(latestVersion)) {
|
||||
return UpdateStatus::CanRepair;
|
||||
} else if(currentDevice()->canUpdate(latestVersion)) {
|
||||
return UpdateStatus::CanUpdate;
|
||||
} else if(currentDevice()->canInstall(latestVersion)) {
|
||||
return UpdateStatus::CanInstall;
|
||||
} else{
|
||||
return UpdateStatus::NoUpdates;
|
||||
}
|
||||
}
|
||||
|
||||
FlipperZero *ApplicationBackend::currentDevice() const
|
||||
{
|
||||
return m_deviceRegistry->currentDevice();
|
||||
}
|
||||
|
||||
void ApplicationBackend::mainAction()
|
||||
{
|
||||
AbstractOperationHelper *helper;
|
||||
|
||||
if(currentDevice()->deviceState()->isRecoveryMode()) {
|
||||
setState(State::RepairingDevice);
|
||||
helper = new RepairTopLevelHelper(m_firmwareUpdates, currentDevice(), this);
|
||||
|
||||
} else {
|
||||
setState(State::UpdatingDevice);
|
||||
helper = new UpdateTopLevelHelper(m_firmwareUpdates, currentDevice(), this);
|
||||
}
|
||||
|
||||
connect(helper, &AbstractOperationHelper::finished, this, [=]() {
|
||||
if(helper->isError()) {
|
||||
qCCritical(LOG_BACKEND).noquote() << "Failed to complete the operation:" << helper->errorString();
|
||||
}
|
||||
|
||||
helper->deleteLater();
|
||||
});
|
||||
}
|
||||
|
||||
void ApplicationBackend::createBackup(const QUrl &directoryUrl)
|
||||
{
|
||||
setState(State::CreatingBackup);
|
||||
currentDevice()->createBackup(directoryUrl);
|
||||
}
|
||||
|
||||
void ApplicationBackend::restoreBackup(const QUrl &directoryUrl)
|
||||
{
|
||||
setState(State::RestoringBackup);
|
||||
currentDevice()->restoreBackup(directoryUrl);
|
||||
}
|
||||
|
||||
void ApplicationBackend::factoryReset()
|
||||
{
|
||||
setState(State::FactoryResetting);
|
||||
currentDevice()->factoryReset();
|
||||
}
|
||||
|
||||
void ApplicationBackend::installFirmware(const QUrl &fileUrl)
|
||||
{
|
||||
setState(State::InstallingFirmware);
|
||||
currentDevice()->installFirmware(fileUrl);
|
||||
}
|
||||
|
||||
void ApplicationBackend::installWirelessStack(const QUrl &fileUrl)
|
||||
{
|
||||
setState(State::InstallingWirelessStack);
|
||||
currentDevice()->installWirelessStack(fileUrl);
|
||||
}
|
||||
|
||||
void ApplicationBackend::installFUS(const QUrl &fileUrl, uint32_t address)
|
||||
{
|
||||
setState(State::InstallingFUS);
|
||||
currentDevice()->installFUS(fileUrl, address);
|
||||
}
|
||||
|
||||
void ApplicationBackend::startFullScreenStreaming()
|
||||
{
|
||||
setState(State::ScreenStreaming);
|
||||
}
|
||||
|
||||
void ApplicationBackend::stopFullScreenStreaming()
|
||||
{
|
||||
setState(State::Ready);
|
||||
}
|
||||
|
||||
void ApplicationBackend::finalizeOperation()
|
||||
{
|
||||
//TODO: clean up all non-online devices here
|
||||
m_deviceRegistry->cleanupOffline();
|
||||
|
||||
if(currentDevice()) {
|
||||
setState(State::Ready);
|
||||
} else {
|
||||
setState(State::WaitingForDevices);
|
||||
}
|
||||
}
|
||||
|
||||
void ApplicationBackend::onCurrentDeviceChanged()
|
||||
{
|
||||
// Should not happen during an ongoing operation
|
||||
if(m_state > State::ScreenStreaming && m_state != State::Finished) {
|
||||
setState(State::ErrorOccured);
|
||||
qCCritical(LOG_BACKEND) << "Current operation was interrupted";
|
||||
|
||||
} else if(m_deviceRegistry->currentDevice()) {
|
||||
// No need to disconnect the old device, as it has been destroyed at this point
|
||||
connect(currentDevice(), &FlipperZero::operationFinished, this, &ApplicationBackend::onDeviceOperationFinished);
|
||||
connect(currentDevice(), &FlipperZero::stateChanged, this, &ApplicationBackend::updateStatusChanged);
|
||||
setState(State::Ready);
|
||||
|
||||
} else {
|
||||
setState(State::WaitingForDevices);
|
||||
}
|
||||
}
|
||||
|
||||
void ApplicationBackend::onDeviceOperationFinished()
|
||||
{
|
||||
// TODO: Some error handling?
|
||||
if(!currentDevice() || currentDevice()->deviceState()->isError()) {
|
||||
setState(State::ErrorOccured);
|
||||
} else {
|
||||
setState(State::Finished);
|
||||
}
|
||||
}
|
||||
|
||||
void ApplicationBackend::initConnections()
|
||||
{
|
||||
connect(m_deviceRegistry, &DeviceRegistry::currentDeviceChanged, this, &ApplicationBackend::currentDeviceChanged);
|
||||
connect(m_deviceRegistry, &DeviceRegistry::currentDeviceChanged, this, &ApplicationBackend::onCurrentDeviceChanged);
|
||||
|
||||
connect(m_deviceRegistry, &DeviceRegistry::currentDeviceChanged, this, &ApplicationBackend::updateStatusChanged);
|
||||
connect(m_firmwareUpdates, &UpdateRegistry::latestVersionChanged, this, &ApplicationBackend::updateStatusChanged);
|
||||
}
|
||||
|
||||
void ApplicationBackend::setState(State newState)
|
||||
{
|
||||
if(m_state == newState) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_state = newState;
|
||||
emit stateChanged();
|
||||
}
|
||||
|
||||
UpdateRegistry *ApplicationBackend::firmwareUpdates() const
|
||||
{
|
||||
return m_firmwareUpdates;
|
||||
}
|
||||
|
||||
UpdateRegistry *ApplicationBackend::applicationUpdates() const
|
||||
{
|
||||
return m_applicationUpdates;
|
||||
}
|
||||
|
||||
void ApplicationBackend::registerMetaTypes()
|
||||
{
|
||||
qRegisterMetaType<Preferences*>("Preferences*");
|
||||
qRegisterMetaType<Flipper::Updates::FileInfo>("Flipper::Updates::FileInfo");
|
||||
qRegisterMetaType<Flipper::Updates::VersionInfo>("Flipper::Updates::VersionInfo");
|
||||
qRegisterMetaType<Flipper::Updates::ChannelInfo>("Flipper::Updates::ChannelInfo");
|
||||
|
||||
qRegisterMetaType<Flipper::Zero::DeviceInfo>("Flipper::Zero::DeviceInfo");
|
||||
qRegisterMetaType<Flipper::Zero::HardwareInfo>("Flipper::Zero::HardwareInfo");
|
||||
qRegisterMetaType<Flipper::Zero::SoftwareInfo>("Flipper::Zero::SoftwareInfo");
|
||||
qRegisterMetaType<Flipper::Zero::StorageInfo>("Flipper::Zero::StorageInfo");
|
||||
|
||||
qRegisterMetaType<Flipper::FlipperZero*>("Flipper::FlipperZero*");
|
||||
qRegisterMetaType<Flipper::Zero::DeviceState*>("Flipper::Zero::DeviceState*");
|
||||
qRegisterMetaType<Flipper::Zero::ScreenStreamer*>("Flipper::Zero::ScreenStreamer*");
|
||||
|
||||
qRegisterMetaType<Flipper::Zero::AssetManifest::FileInfo>();
|
||||
}
|
||||
|
||||
void ApplicationBackend::registerComparators()
|
||||
{
|
||||
QMetaType::registerComparators<Flipper::Zero::AssetManifest::FileInfo>();
|
||||
}
|
|
@ -0,0 +1,100 @@
|
|||
#pragma once
|
||||
|
||||
#include <QObject>
|
||||
|
||||
namespace Flipper {
|
||||
|
||||
class FlipperZero;
|
||||
class DeviceRegistry;
|
||||
class UpdateRegistry;
|
||||
class FirmwareUpdates;
|
||||
class ApplicationUpdates;
|
||||
}
|
||||
|
||||
class ApplicationBackend : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(State state READ state NOTIFY stateChanged)
|
||||
Q_PROPERTY(UpdateStatus updateStatus READ updateStatus NOTIFY updateStatusChanged)
|
||||
Q_PROPERTY(Flipper::FlipperZero* currentDevice READ currentDevice NOTIFY currentDeviceChanged)
|
||||
|
||||
public:
|
||||
enum class State {
|
||||
WaitingForDevices,
|
||||
Ready,
|
||||
ScreenStreaming,
|
||||
UpdatingDevice,
|
||||
RepairingDevice,
|
||||
CreatingBackup,
|
||||
RestoringBackup,
|
||||
FactoryResetting,
|
||||
InstallingFirmware,
|
||||
InstallingWirelessStack,
|
||||
InstallingFUS,
|
||||
Finished,
|
||||
ErrorOccured
|
||||
};
|
||||
|
||||
Q_ENUM(State)
|
||||
|
||||
enum class UpdateStatus {
|
||||
Unknown,
|
||||
CanUpdate,
|
||||
CanInstall,
|
||||
CanRepair,
|
||||
NoUpdates
|
||||
};
|
||||
|
||||
Q_ENUM(UpdateStatus)
|
||||
|
||||
ApplicationBackend(QObject *parent = nullptr);
|
||||
|
||||
State state() const;
|
||||
UpdateStatus updateStatus() const;
|
||||
|
||||
Flipper::FlipperZero *currentDevice() const;
|
||||
Flipper::UpdateRegistry *firmwareUpdates() const;
|
||||
Flipper::UpdateRegistry *applicationUpdates() const;
|
||||
|
||||
/* Actions available from the GUI.
|
||||
* Applies to the currently active device. */
|
||||
|
||||
Q_INVOKABLE void mainAction();
|
||||
|
||||
Q_INVOKABLE void createBackup(const QUrl &directoryUrl);
|
||||
Q_INVOKABLE void restoreBackup(const QUrl &directoryUrl);
|
||||
Q_INVOKABLE void factoryReset();
|
||||
|
||||
Q_INVOKABLE void installFirmware(const QUrl &fileUrl);
|
||||
Q_INVOKABLE void installWirelessStack(const QUrl &fileUrl);
|
||||
Q_INVOKABLE void installFUS(const QUrl &fileUrl, uint32_t address);
|
||||
|
||||
Q_INVOKABLE void startFullScreenStreaming();
|
||||
Q_INVOKABLE void stopFullScreenStreaming();
|
||||
|
||||
Q_INVOKABLE void finalizeOperation();
|
||||
|
||||
signals:
|
||||
void stateChanged();
|
||||
void updateStatusChanged();
|
||||
void currentDeviceChanged();
|
||||
|
||||
private slots:
|
||||
void onCurrentDeviceChanged();
|
||||
void onDeviceOperationFinished();
|
||||
|
||||
private:
|
||||
static void registerMetaTypes();
|
||||
static void registerComparators();
|
||||
|
||||
void initConnections();
|
||||
|
||||
void setState(State newState);
|
||||
|
||||
Flipper::DeviceRegistry *m_deviceRegistry;
|
||||
Flipper::FirmwareUpdates *m_firmwareUpdates;
|
||||
Flipper::ApplicationUpdates *m_applicationUpdates;
|
||||
|
||||
State m_state;
|
||||
UpdateStatus m_updateStatus;
|
||||
};
|
|
@ -12,25 +12,48 @@ SOURCES += \
|
|||
abstractoperation.cpp \
|
||||
abstractoperationhelper.cpp \
|
||||
abstractoperationrunner.cpp \
|
||||
abstractprotobufoperation.cpp \
|
||||
abstractserialoperation.cpp \
|
||||
applicationbackend.cpp \
|
||||
deviceregistry.cpp \
|
||||
filenode.cpp \
|
||||
flipperupdates.cpp \
|
||||
flipperzero/assetmanifest.cpp \
|
||||
flipperzero/cli/deviceinfooperation.cpp \
|
||||
flipperzero/cli/dfuoperation.cpp \
|
||||
flipperzero/cli/factoryresetclioperation.cpp \
|
||||
flipperzero/cli/rebootoperation.cpp \
|
||||
flipperzero/cli/guistartstreamoperation.cpp \
|
||||
flipperzero/cli/guistopstreamoperation.cpp \
|
||||
flipperzero/cli/startrpcoperation.cpp \
|
||||
flipperzero/cli/stoprpcoperation.cpp \
|
||||
flipperzero/cli/storageinfooperation.cpp \
|
||||
flipperzero/cli/storagelistoperation.cpp \
|
||||
flipperzero/cli/storagemkdiroperation.cpp \
|
||||
flipperzero/cli/storagereadoperation.cpp \
|
||||
flipperzero/cli/storageremoveoperation.cpp \
|
||||
flipperzero/cli/storagestatoperation.cpp \
|
||||
flipperzero/cli/storagewriteoperation.cpp \
|
||||
flipperzero/cli/systemdeviceinfooperation.cpp \
|
||||
flipperzero/cli/systemfactoryresetoperation.cpp \
|
||||
flipperzero/cli/systemrebootoperation.cpp \
|
||||
flipperzero/commandinterface.cpp \
|
||||
flipperzero/cli/skipmotdoperation.cpp \
|
||||
flipperzero/devicestate.cpp \
|
||||
flipperzero/factoryinfo.cpp \
|
||||
flipperzero/firmwareupdater.cpp \
|
||||
flipperzero/flipperzero.cpp \
|
||||
flipperzero/helper/deviceinfohelper.cpp \
|
||||
flipperzero/helper/firmwarehelper.cpp \
|
||||
flipperzero/helper/radiomanifesthelper.cpp \
|
||||
flipperzero/helper/scriptshelper.cpp \
|
||||
flipperzero/helper/serialinithelper.cpp \
|
||||
flipperzero/helper/toplevelhelper.cpp \
|
||||
flipperzero/protobuf/guiprotobufmessage.cpp \
|
||||
flipperzero/protobuf/mainprotobufmessage.cpp \
|
||||
flipperzero/protobuf/messages/application.pb.c \
|
||||
flipperzero/protobuf/messages/flipper.pb.c \
|
||||
flipperzero/protobuf/messages/gui.pb.c \
|
||||
flipperzero/protobuf/messages/status.pb.c \
|
||||
flipperzero/protobuf/messages/storage.pb.c \
|
||||
flipperzero/protobuf/messages/system.pb.c \
|
||||
flipperzero/protobuf/storageprotobufmessage.cpp \
|
||||
flipperzero/protobuf/systemprotobufmessage.cpp \
|
||||
flipperzero/radiomanifest.cpp \
|
||||
flipperzero/recovery.cpp \
|
||||
flipperzero/recovery/abstractrecoveryoperation.cpp \
|
||||
|
@ -42,12 +65,6 @@ SOURCES += \
|
|||
flipperzero/recovery/setbootmodeoperation.cpp \
|
||||
flipperzero/recovery/wirelessstackdownloadoperation.cpp \
|
||||
flipperzero/recoveryinterface.cpp \
|
||||
flipperzero/cli/listoperation.cpp \
|
||||
flipperzero/cli/mkdiroperation.cpp \
|
||||
flipperzero/cli/readoperation.cpp \
|
||||
flipperzero/cli/removeoperation.cpp \
|
||||
flipperzero/cli/statoperation.cpp \
|
||||
flipperzero/cli/writeoperation.cpp \
|
||||
flipperzero/screenstreamer.cpp \
|
||||
flipperzero/toplevel/abstracttopleveloperation.cpp \
|
||||
flipperzero/toplevel/factoryresetoperation.cpp \
|
||||
|
@ -69,7 +86,6 @@ SOURCES += \
|
|||
gzipuncompressor.cpp \
|
||||
logger.cpp \
|
||||
preferences.cpp \
|
||||
qflipperbackend.cpp \
|
||||
remotefilefetcher.cpp \
|
||||
serialfinder.cpp \
|
||||
simpleserialoperation.cpp \
|
||||
|
@ -82,28 +98,52 @@ HEADERS += \
|
|||
abstractoperation.h \
|
||||
abstractoperationhelper.h \
|
||||
abstractoperationrunner.h \
|
||||
abstractprotobufmessage.h \
|
||||
abstractprotobufoperation.h \
|
||||
abstractserialoperation.h \
|
||||
applicationbackend.h \
|
||||
deviceregistry.h \
|
||||
failable.h \
|
||||
fileinfo.h \
|
||||
filenode.h \
|
||||
flipperupdates.h \
|
||||
flipperzero/assetmanifest.h \
|
||||
flipperzero/cli/deviceinfooperation.h \
|
||||
flipperzero/cli/dfuoperation.h \
|
||||
flipperzero/cli/factoryresetclioperation.h \
|
||||
flipperzero/cli/rebootoperation.h \
|
||||
flipperzero/cli/guistartstreamoperation.h \
|
||||
flipperzero/cli/guistopstreamoperation.h \
|
||||
flipperzero/cli/startrpcoperation.h \
|
||||
flipperzero/cli/stoprpcoperation.h \
|
||||
flipperzero/cli/storageinfooperation.h \
|
||||
flipperzero/cli/storagelistoperation.h \
|
||||
flipperzero/cli/storagemkdiroperation.h \
|
||||
flipperzero/cli/storagereadoperation.h \
|
||||
flipperzero/cli/storageremoveoperation.h \
|
||||
flipperzero/cli/storagestatoperation.h \
|
||||
flipperzero/cli/storagewriteoperation.h \
|
||||
flipperzero/cli/systemdeviceinfooperation.h \
|
||||
flipperzero/cli/systemfactoryresetoperation.h \
|
||||
flipperzero/cli/systemrebootoperation.h \
|
||||
flipperzero/commandinterface.h \
|
||||
flipperzero/cli/skipmotdoperation.h \
|
||||
flipperzero/deviceinfo.h \
|
||||
flipperzero/devicestate.h \
|
||||
flipperzero/factoryinfo.h \
|
||||
flipperzero/firmwareupdater.h \
|
||||
flipperzero/flipperzero.h \
|
||||
flipperzero/helper/deviceinfohelper.h \
|
||||
flipperzero/helper/firmwarehelper.h \
|
||||
flipperzero/helper/radiomanifesthelper.h \
|
||||
flipperzero/helper/scriptshelper.h \
|
||||
flipperzero/helper/serialinithelper.h \
|
||||
flipperzero/helper/toplevelhelper.h \
|
||||
flipperzero/protobuf/guiprotobufmessage.h \
|
||||
flipperzero/protobuf/mainprotobufmessage.h \
|
||||
flipperzero/protobuf/messages/application.pb.h \
|
||||
flipperzero/protobuf/messages/flipper.pb.h \
|
||||
flipperzero/protobuf/messages/gui.pb.h \
|
||||
flipperzero/protobuf/messages/status.pb.h \
|
||||
flipperzero/protobuf/messages/storage.pb.h \
|
||||
flipperzero/protobuf/messages/system.pb.h \
|
||||
flipperzero/protobuf/storageprotobufmessage.h \
|
||||
flipperzero/protobuf/systemprotobufmessage.h \
|
||||
flipperzero/radiomanifest.h \
|
||||
flipperzero/recovery.h \
|
||||
flipperzero/recovery/abstractrecoveryoperation.h \
|
||||
|
@ -115,12 +155,6 @@ HEADERS += \
|
|||
flipperzero/recovery/setbootmodeoperation.h \
|
||||
flipperzero/recovery/wirelessstackdownloadoperation.h \
|
||||
flipperzero/recoveryinterface.h \
|
||||
flipperzero/cli/listoperation.h \
|
||||
flipperzero/cli/mkdiroperation.h \
|
||||
flipperzero/cli/readoperation.h \
|
||||
flipperzero/cli/removeoperation.h \
|
||||
flipperzero/cli/statoperation.h \
|
||||
flipperzero/cli/writeoperation.h \
|
||||
flipperzero/screenstreamer.h \
|
||||
flipperzero/toplevel/abstracttopleveloperation.h \
|
||||
flipperzero/toplevel/factoryresetoperation.h \
|
||||
|
@ -142,7 +176,6 @@ HEADERS += \
|
|||
gzipuncompressor.h \
|
||||
logger.h \
|
||||
preferences.h \
|
||||
qflipperbackend.h \
|
||||
remotefilefetcher.h \
|
||||
serialfinder.h \
|
||||
signalingfailable.h \
|
||||
|
@ -153,15 +186,22 @@ HEADERS += \
|
|||
updateregistry.h
|
||||
|
||||
unix|win32 {
|
||||
LIBS += -L$$OUT_PWD/../dfu/ -ldfu
|
||||
LIBS += -L$$OUT_PWD/../dfu/ -ldfu \
|
||||
-L$$OUT_PWD/../3rdparty/ -l3rdparty
|
||||
}
|
||||
|
||||
win32:!win32-g++ {
|
||||
PRE_TARGETDEPS += $$OUT_PWD/../dfu/dfu.lib
|
||||
PRE_TARGETDEPS += $$OUT_PWD/../dfu/dfu.lib \
|
||||
$$OUT_PWD/../3rdparty/3rdparty.lib
|
||||
|
||||
} else:unix|win32-g++ {
|
||||
PRE_TARGETDEPS += $$OUT_PWD/../dfu/libdfu.a
|
||||
PRE_TARGETDEPS += $$OUT_PWD/../dfu/libdfu.a \
|
||||
$$OUT_PWD/../3rdparty/lib3rdparty.a
|
||||
}
|
||||
|
||||
INCLUDEPATH += $$PWD/../dfu
|
||||
DEPENDPATH += $$PWD/../dfu
|
||||
INCLUDEPATH += $$PWD/../dfu \
|
||||
$$PWD/../3rdparty \
|
||||
$$PWD/../3rdparty/nanopb
|
||||
|
||||
DEPENDPATH += $$PWD/../dfu \
|
||||
$$PWD/../3rdparty
|
||||
|
|
|
@ -1,18 +1,21 @@
|
|||
#include "deviceregistry.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QMetaObject>
|
||||
#include <QLoggingCategory>
|
||||
|
||||
#include "flipperzero/helper/deviceinfohelper.h"
|
||||
#include "flipperzero/flipperzero.h"
|
||||
#include "flipperzero/devicestate.h"
|
||||
|
||||
#include "usbdevice.h"
|
||||
#include "debug.h"
|
||||
|
||||
#define FLIPPER_ZERO_VID 0x0483
|
||||
#define FLIPPER_ZERO_PID_VCP 0x5740
|
||||
#define FLIPPER_ZERO_PID_DFU 0xdf11
|
||||
|
||||
Q_LOGGING_CATEGORY(CAT_DEVREG, "DEVREG");
|
||||
|
||||
using namespace Flipper;
|
||||
|
||||
DeviceRegistry::DeviceRegistry(QObject *parent):
|
||||
|
@ -26,7 +29,7 @@ DeviceRegistry::DeviceRegistry(QObject *parent):
|
|||
USBDeviceInfo(FLIPPER_ZERO_VID, FLIPPER_ZERO_PID_VCP)
|
||||
.withManufacturer("Flipper Devices Inc.")
|
||||
.withProductDescription("Flipper Control Virtual ComPort")
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
FlipperZero *DeviceRegistry::currentDevice() const
|
||||
|
@ -36,14 +39,15 @@ FlipperZero *DeviceRegistry::currentDevice() const
|
|||
|
||||
void DeviceRegistry::insertDevice(const USBDeviceInfo &info)
|
||||
{
|
||||
check_return_void(info.isValid(), "A new invalid device has been detected, skipping...");
|
||||
if(!info.isValid()) {
|
||||
qCCritical(CAT_DEVREG) << "A new invalid device has been detected, skipping...";
|
||||
|
||||
if(info.vendorID() == FLIPPER_ZERO_VID) {
|
||||
auto *fetcher = Zero::AbstractDeviceInfoHelper::create(info, this);
|
||||
connect(fetcher, &Zero::AbstractDeviceInfoHelper::finished, this, &DeviceRegistry::processDevice);
|
||||
} else if(info.vendorID() != FLIPPER_ZERO_VID) {
|
||||
qCCritical(CAT_DEVREG) << "Unexpected device VID and PID";
|
||||
|
||||
} else {
|
||||
error_msg("Unexpected device VID and PID.");
|
||||
auto *fetcher = Zero::AbstractDeviceInfoHelper::create(info, this);
|
||||
connect(fetcher, &Zero::AbstractDeviceInfoHelper::finished, this, &DeviceRegistry::processDevice);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -60,7 +64,8 @@ void DeviceRegistry::removeDevice(const USBDeviceInfo &info)
|
|||
|
||||
if(!device->deviceState()->isPersistent()) {
|
||||
m_devices.takeAt(idx)->deleteLater();
|
||||
emit devicesChanged();
|
||||
emit deviceCountChanged();
|
||||
emit currentDeviceChanged();
|
||||
|
||||
} else {
|
||||
device->deviceState()->setOnline(false);
|
||||
|
@ -68,6 +73,22 @@ void DeviceRegistry::removeDevice(const USBDeviceInfo &info)
|
|||
}
|
||||
}
|
||||
|
||||
void DeviceRegistry::cleanupOffline()
|
||||
{
|
||||
auto it = std::remove_if(m_devices.begin(), m_devices.end(), [](Flipper::FlipperZero *arg) {
|
||||
return !arg->deviceState()->isOnline();
|
||||
});
|
||||
|
||||
for(const auto end = m_devices.end(); it != end; ++it) {
|
||||
qCDebug(CAT_DEVREG).noquote() << "Removed zombie device:" << (*it)->deviceState()->name();
|
||||
|
||||
m_devices.erase(it);
|
||||
emit deviceCountChanged();
|
||||
|
||||
(*it)->deleteLater();
|
||||
}
|
||||
}
|
||||
|
||||
void DeviceRegistry::processDevice()
|
||||
{
|
||||
auto *fetcher = qobject_cast<Zero::AbstractDeviceInfoHelper*>(sender());
|
||||
|
@ -75,7 +96,7 @@ void DeviceRegistry::processDevice()
|
|||
fetcher->deleteLater();
|
||||
|
||||
if(fetcher->isError()) {
|
||||
error_msg(QStringLiteral("An error has occured: %1").arg(fetcher->errorString()));
|
||||
qCCritical(CAT_DEVREG).noquote() << QStringLiteral("An error has occured:") << fetcher->errorString();
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -87,14 +108,17 @@ void DeviceRegistry::processDevice()
|
|||
|
||||
if(it != m_devices.end()) {
|
||||
// Preserving the old instance
|
||||
(*it)->deviceState()->reset(info);
|
||||
(*it)->deviceState()->setDeviceInfo(info);
|
||||
|
||||
} else {
|
||||
auto *device = new FlipperZero(info, this);
|
||||
m_devices.append(device);
|
||||
|
||||
emit deviceConnected(device);
|
||||
emit devicesChanged();
|
||||
emit deviceCountChanged();
|
||||
|
||||
if(m_devices.size() == 1) {
|
||||
emit currentDeviceChanged();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@ class FlipperZero;
|
|||
class DeviceRegistry : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(Flipper::FlipperZero* currentDevice READ currentDevice NOTIFY devicesChanged)
|
||||
Q_PROPERTY(Flipper::FlipperZero* currentDevice READ currentDevice NOTIFY currentDeviceChanged)
|
||||
|
||||
using DeviceList = QVector<FlipperZero*>;
|
||||
|
||||
|
@ -22,12 +22,13 @@ public:
|
|||
FlipperZero *currentDevice() const;
|
||||
|
||||
signals:
|
||||
void deviceConnected(FlipperZero*);
|
||||
void devicesChanged();
|
||||
void currentDeviceChanged();
|
||||
void deviceCountChanged();
|
||||
|
||||
public slots:
|
||||
void insertDevice(const USBDeviceInfo &info);
|
||||
void removeDevice(const USBDeviceInfo &info);
|
||||
void cleanupOffline();
|
||||
|
||||
private slots:
|
||||
void processDevice();
|
||||
|
|
|
@ -1,118 +0,0 @@
|
|||
#include "deviceinfooperation.h"
|
||||
|
||||
#include <QBuffer>
|
||||
#include <QSerialPort>
|
||||
|
||||
using namespace Flipper;
|
||||
using namespace Zero;
|
||||
|
||||
DeviceInfoOperation::DeviceInfoOperation(QSerialPort *serialPort, QObject *parent):
|
||||
SimpleSerialOperation(serialPort, parent)
|
||||
{}
|
||||
|
||||
const QString DeviceInfoOperation::description() const
|
||||
{
|
||||
return QStringLiteral("Device Info @%1").arg(QString(serialPort()->portName()));
|
||||
}
|
||||
|
||||
const DeviceInfo &DeviceInfoOperation::result() const
|
||||
{
|
||||
return m_deviceInfo;
|
||||
}
|
||||
|
||||
QByteArray DeviceInfoOperation::endOfMessageToken() const
|
||||
{
|
||||
return QByteArrayLiteral("\r\n\r\n>: \a");
|
||||
}
|
||||
|
||||
QByteArray DeviceInfoOperation::commandLine() const
|
||||
{
|
||||
return QByteArrayLiteral("device_info\r\n");
|
||||
}
|
||||
|
||||
bool DeviceInfoOperation::parseReceivedData()
|
||||
{
|
||||
QBuffer buf;
|
||||
|
||||
if(!buf.open(QIODevice::ReadWrite)) {
|
||||
finishWithError(buf.errorString());
|
||||
return false;
|
||||
}
|
||||
|
||||
buf.write(receivedData());
|
||||
buf.seek(0);
|
||||
|
||||
m_deviceInfo.fusVersion = QStringLiteral("0.0.0");
|
||||
m_deviceInfo.radioVersion = QStringLiteral("0.0.0");
|
||||
|
||||
while(buf.canReadLine()) {
|
||||
parseLine(buf.readLine());
|
||||
}
|
||||
|
||||
buf.close();
|
||||
return true;
|
||||
}
|
||||
|
||||
void DeviceInfoOperation::parseLine(const QByteArray &line)
|
||||
{
|
||||
// TODO: Add more fields
|
||||
if(line.count(':') != 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto validx = line.indexOf(':');
|
||||
const auto key = line.left(validx).trimmed();
|
||||
const auto value = line.mid(validx + 1).trimmed();
|
||||
|
||||
if(key == QByteArrayLiteral("hardware_name")) {
|
||||
m_deviceInfo.name = value;
|
||||
} else if(key == QByteArrayLiteral("hardware_target")) {
|
||||
m_deviceInfo.hardware.target = QStringLiteral("f") + value;
|
||||
} else if(key == QByteArrayLiteral("hardware_ver")) {
|
||||
m_deviceInfo.hardware.version = value;
|
||||
} else if(key == QByteArrayLiteral("hardware_body")) {
|
||||
m_deviceInfo.hardware.body = QStringLiteral("b") + value;
|
||||
} else if(key == QByteArrayLiteral("hardware_connect")) {
|
||||
m_deviceInfo.hardware.connect = QStringLiteral("c") + value;
|
||||
} else if(key == QByteArrayLiteral("hardware_color")) {
|
||||
m_deviceInfo.hardware.color = (HardwareInfo::Color)value.toInt();
|
||||
} else if(key == QByteArrayLiteral("firmware_version")) {
|
||||
m_deviceInfo.firmware.version = value;
|
||||
} else if(key == QByteArrayLiteral("firmware_commit")) {
|
||||
m_deviceInfo.firmware.commit = value;
|
||||
} else if(key == QByteArrayLiteral("firmware_branch")) {
|
||||
m_deviceInfo.firmware.branch = value;
|
||||
} else if(key == QByteArrayLiteral("firmware_build_date")) {
|
||||
m_deviceInfo.firmware.date = QDate::fromString(value, QStringLiteral("dd-MM-yyyy"));
|
||||
|
||||
} else if(key == QByteArrayLiteral("radio_stack_major")) {
|
||||
auto fields = m_deviceInfo.radioVersion.split('.');
|
||||
fields.replace(0, value);
|
||||
m_deviceInfo.radioVersion = fields.join('.');
|
||||
|
||||
} else if(key == QByteArrayLiteral("radio_stack_minor")) {
|
||||
auto fields = m_deviceInfo.radioVersion.split('.');
|
||||
fields.replace(1, value);
|
||||
m_deviceInfo.radioVersion = fields.join('.');
|
||||
|
||||
} else if(key == QByteArrayLiteral("radio_stack_sub")) {
|
||||
auto fields = m_deviceInfo.radioVersion.split('.');
|
||||
fields.replace(2, value);
|
||||
m_deviceInfo.radioVersion = fields.join('.');
|
||||
|
||||
} else if(key == QByteArrayLiteral("radio_fus_major")) {
|
||||
auto fields = m_deviceInfo.fusVersion.split('.');
|
||||
fields.replace(0, value);
|
||||
m_deviceInfo.fusVersion = fields.join('.');
|
||||
|
||||
} else if(key == QByteArrayLiteral("radio_fus_minor")) {
|
||||
auto fields = m_deviceInfo.fusVersion.split('.');
|
||||
fields.replace(1, value);
|
||||
m_deviceInfo.fusVersion = fields.join('.');
|
||||
|
||||
} else if(key == QByteArrayLiteral("radio_fus_sub")) {
|
||||
auto fields = m_deviceInfo.fusVersion.split('.');
|
||||
fields.replace(2, value);
|
||||
m_deviceInfo.fusVersion = fields.join('.');
|
||||
} else {}
|
||||
}
|
|
@ -1,30 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include "simpleserialoperation.h"
|
||||
#include "flipperzero/deviceinfo.h"
|
||||
|
||||
namespace Flipper {
|
||||
namespace Zero {
|
||||
|
||||
class DeviceInfoOperation : public SimpleSerialOperation
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
DeviceInfoOperation(QSerialPort *serialPort, QObject *parent = nullptr);
|
||||
const QString description() const override;
|
||||
const DeviceInfo &result() const;
|
||||
|
||||
private:
|
||||
QByteArray endOfMessageToken() const override;
|
||||
QByteArray commandLine() const override;
|
||||
bool parseReceivedData() override;
|
||||
|
||||
void parseLine(const QByteArray &line);
|
||||
|
||||
DeviceInfo m_deviceInfo;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -1,35 +0,0 @@
|
|||
#include "dfuoperation.h"
|
||||
|
||||
#include <QTimer>
|
||||
#include <QSerialPort>
|
||||
|
||||
#define CALL_LATER(obj, func) (QTimer::singleShot(0, obj, func))
|
||||
|
||||
using namespace Flipper;
|
||||
using namespace Zero;
|
||||
|
||||
DFUOperation::DFUOperation(QSerialPort *serialPort, QObject *parent):
|
||||
AbstractSerialOperation(serialPort, parent)
|
||||
{}
|
||||
|
||||
const QString DFUOperation::description() const
|
||||
{
|
||||
return QStringLiteral("Boot to recovery @%1").arg(QString(serialPort()->portName()));
|
||||
}
|
||||
|
||||
void DFUOperation::onSerialPortReadyRead()
|
||||
{
|
||||
// This operation does not need serial output, discarding it
|
||||
serialPort()->clear();
|
||||
}
|
||||
|
||||
bool DFUOperation::begin()
|
||||
{
|
||||
const auto success = (serialPort()->write("\rdfu\r\n") > 0) && serialPort()->flush();
|
||||
|
||||
if(success) {
|
||||
CALL_LATER(this, &AbstractOperation::finish);
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
|
@ -1,30 +0,0 @@
|
|||
#include "factoryresetclioperation.h"
|
||||
|
||||
#include <QSerialPort>
|
||||
|
||||
using namespace Flipper;
|
||||
using namespace Zero;
|
||||
|
||||
FactoryResetCliOperation::FactoryResetCliOperation(QSerialPort *serialPort, QObject *parent):
|
||||
SimpleSerialOperation(serialPort, parent)
|
||||
{}
|
||||
|
||||
const QString FactoryResetCliOperation::description() const
|
||||
{
|
||||
return QStringLiteral("Factory reset @%1").arg(QString(serialPort()->portName()));
|
||||
}
|
||||
|
||||
QByteArray FactoryResetCliOperation::endOfMessageToken() const
|
||||
{
|
||||
return QByteArrayLiteral("Are you sure (y/n)?\r\n");
|
||||
}
|
||||
|
||||
QByteArray FactoryResetCliOperation::commandLine() const
|
||||
{
|
||||
return QByteArrayLiteral("factory_reset\r");
|
||||
}
|
||||
|
||||
bool FactoryResetCliOperation::parseReceivedData()
|
||||
{
|
||||
return (serialPort()->write("y\r\n") > 0) && serialPort()->flush();
|
||||
}
|
|
@ -1,24 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include "simpleserialoperation.h"
|
||||
|
||||
namespace Flipper {
|
||||
namespace Zero {
|
||||
|
||||
class FactoryResetCliOperation : public SimpleSerialOperation
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
FactoryResetCliOperation(QSerialPort *serialPort, QObject *parent = nullptr);
|
||||
const QString description() const override;
|
||||
|
||||
private:
|
||||
QByteArray endOfMessageToken() const override;
|
||||
QByteArray commandLine() const override;
|
||||
bool parseReceivedData() override;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
#include "guistartstreamoperation.h"
|
||||
|
||||
#include <QSerialPort>
|
||||
|
||||
#include "flipperzero/protobuf/guiprotobufmessage.h"
|
||||
|
||||
using namespace Flipper;
|
||||
using namespace Zero;
|
||||
|
||||
GuiStartStreamOperation::GuiStartStreamOperation(QSerialPort *serialPort, QObject *parent):
|
||||
AbstractProtobufOperation(serialPort, parent)
|
||||
{}
|
||||
|
||||
const QString GuiStartStreamOperation::description() const
|
||||
{
|
||||
return QStringLiteral("Start screen streaming @%1").arg(serialPort()->portName());
|
||||
}
|
||||
|
||||
void GuiStartStreamOperation::onSerialPortReadyRead()
|
||||
{
|
||||
MainEmptyResponse response(serialPort());
|
||||
|
||||
if(!response.receive()) {
|
||||
return;
|
||||
} else if(!response.isOk()) {
|
||||
finishWithError(QStringLiteral("Device replied with an error response: %1").arg(response.commandStatusString()));
|
||||
} else if(!response.isValidType()) {
|
||||
finishWithError(QStringLiteral("Expected empty reply, got something else"));
|
||||
} else {
|
||||
finish();
|
||||
}
|
||||
}
|
||||
|
||||
bool GuiStartStreamOperation::begin()
|
||||
{
|
||||
GuiStartScreenStreamRequest request(serialPort());
|
||||
return request.send();
|
||||
}
|
|
@ -1,16 +1,16 @@
|
|||
#pragma once
|
||||
|
||||
#include "abstractserialoperation.h"
|
||||
#include "abstractprotobufoperation.h"
|
||||
|
||||
namespace Flipper {
|
||||
namespace Zero {
|
||||
|
||||
class DFUOperation : public AbstractSerialOperation
|
||||
class GuiStartStreamOperation : public AbstractProtobufOperation
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
DFUOperation(QSerialPort *serialPort, QObject *parent = nullptr);
|
||||
GuiStartStreamOperation(QSerialPort *serialPort, QObject *parent = nullptr);
|
||||
const QString description() const override;
|
||||
|
||||
private slots:
|
|
@ -0,0 +1,46 @@
|
|||
#include "guistopstreamoperation.h"
|
||||
|
||||
#include <QSerialPort>
|
||||
|
||||
#include "flipperzero/protobuf/guiprotobufmessage.h"
|
||||
|
||||
using namespace Flipper;
|
||||
using namespace Zero;
|
||||
|
||||
GuiStopStreamOperation::GuiStopStreamOperation(QSerialPort *serialPort, QObject *parent):
|
||||
AbstractProtobufOperation(serialPort, parent)
|
||||
{}
|
||||
|
||||
const QString GuiStopStreamOperation::description() const
|
||||
{
|
||||
return QStringLiteral("Stop screen streaming @%1").arg(serialPort()->portName());
|
||||
}
|
||||
|
||||
void GuiStopStreamOperation::onSerialPortReadyRead()
|
||||
{
|
||||
MainEmptyResponse response(serialPort());
|
||||
|
||||
while(response.receive()) {
|
||||
if(!response.isOk()) {
|
||||
finishWithError(QStringLiteral("Device replied with an error response: %1").arg(response.commandStatusString()));
|
||||
|
||||
} else if(!response.isValidType()) {
|
||||
if(response.whichContent() != GuiScreenFrameResponse::tag()) {
|
||||
finishWithError(QStringLiteral("Expected empty or screen frame reply, got something else"));
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
|
||||
} else {
|
||||
finish();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
bool GuiStopStreamOperation::begin()
|
||||
{
|
||||
GuiStopScreenStreamRequest request(serialPort());
|
||||
return request.send();
|
||||
}
|
|
@ -1,16 +1,16 @@
|
|||
#pragma once
|
||||
|
||||
#include "abstractserialoperation.h"
|
||||
#include "abstractprotobufoperation.h"
|
||||
|
||||
namespace Flipper {
|
||||
namespace Zero {
|
||||
|
||||
class RebootOperation : public AbstractSerialOperation
|
||||
class GuiStopStreamOperation : public AbstractProtobufOperation
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
RebootOperation(QSerialPort *serialPort, QObject *parent = nullptr);
|
||||
GuiStopStreamOperation(QSerialPort *serialPort, QObject *parent = nullptr);
|
||||
const QString description() const override;
|
||||
|
||||
private slots:
|
|
@ -1,85 +0,0 @@
|
|||
#include "listoperation.h"
|
||||
|
||||
#include <QBuffer>
|
||||
|
||||
#include "debug.h"
|
||||
|
||||
#define FILE_PREFIX QByteArrayLiteral("[F]")
|
||||
#define DIRECTORY_PREFIX QByteArrayLiteral("[D]")
|
||||
|
||||
using namespace Flipper;
|
||||
using namespace Zero;
|
||||
|
||||
ListOperation::ListOperation(QSerialPort *serialPort, const QByteArray &dirName, QObject *parent):
|
||||
SimpleSerialOperation(serialPort, parent),
|
||||
m_dirName(dirName)
|
||||
{}
|
||||
|
||||
const QString ListOperation::description() const
|
||||
{
|
||||
return QStringLiteral("List @%1").arg(QString(m_dirName));
|
||||
}
|
||||
|
||||
const FileInfoList &ListOperation::result() const
|
||||
{
|
||||
return m_result;
|
||||
}
|
||||
|
||||
QByteArray ListOperation::endOfMessageToken() const
|
||||
{
|
||||
return QByteArrayLiteral("\r\n\r\n>: \a");
|
||||
}
|
||||
|
||||
QByteArray ListOperation::commandLine() const
|
||||
{
|
||||
return QByteArrayLiteral("storage list \"") + m_dirName + QByteArrayLiteral("\"\r\n");
|
||||
}
|
||||
|
||||
bool ListOperation::parseReceivedData()
|
||||
{
|
||||
QBuffer buf;
|
||||
|
||||
if(!buf.open(QIODevice::ReadWrite)) {
|
||||
finishWithError(buf.errorString());
|
||||
return false;
|
||||
}
|
||||
|
||||
buf.write(receivedData());
|
||||
buf.seek(0);
|
||||
|
||||
while(buf.canReadLine()) {
|
||||
const auto line = buf.readLine().trimmed();
|
||||
if(line.startsWith(FILE_PREFIX)) {
|
||||
parseFile(line);
|
||||
} else if(line.startsWith(DIRECTORY_PREFIX)) {
|
||||
parseDirectory(line);
|
||||
} else {}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ListOperation::parseDirectory(const QByteArray &line)
|
||||
{
|
||||
FileInfo info;
|
||||
info.name = line.mid(DIRECTORY_PREFIX.size() + 1);
|
||||
info.absolutePath = m_dirName + QByteArrayLiteral("/") + info.name;
|
||||
info.type = FileType::Directory;
|
||||
info.size = 0;
|
||||
|
||||
m_result.append(info);
|
||||
}
|
||||
|
||||
void ListOperation::parseFile(const QByteArray &line)
|
||||
{
|
||||
const auto sizeIdx = line.lastIndexOf(' ') + 1;
|
||||
const auto nameIdx = FILE_PREFIX.size() + 1;
|
||||
|
||||
FileInfo info;
|
||||
info.name = line.mid(nameIdx, sizeIdx - nameIdx - 1);
|
||||
info.absolutePath = m_dirName + QByteArrayLiteral("/") + info.name;
|
||||
info.type = FileType::RegularFile;
|
||||
info.size = line.mid(sizeIdx, line.size() - sizeIdx - 1).toLongLong(nullptr, 10);
|
||||
|
||||
m_result.append(info);
|
||||
}
|
|
@ -1,36 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include "simpleserialoperation.h"
|
||||
|
||||
#include <QList>
|
||||
|
||||
#include "fileinfo.h"
|
||||
|
||||
namespace Flipper {
|
||||
namespace Zero {
|
||||
|
||||
class ListOperation : public SimpleSerialOperation
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ListOperation(QSerialPort *serialPort, const QByteArray &dirName, QObject *parent = nullptr);
|
||||
const QString description() const override;
|
||||
const FileInfoList &result() const;
|
||||
|
||||
private:
|
||||
QByteArray endOfMessageToken() const override;
|
||||
QByteArray commandLine() const override;
|
||||
|
||||
bool parseReceivedData() override;
|
||||
void parseDirectory(const QByteArray &line);
|
||||
void parseFile(const QByteArray &line);
|
||||
|
||||
private:
|
||||
QByteArray m_dirName;
|
||||
FileInfoList m_result;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -1,49 +0,0 @@
|
|||
#include "mkdiroperation.h"
|
||||
#include "debug.h"
|
||||
|
||||
using namespace Flipper;
|
||||
using namespace Zero;
|
||||
|
||||
MkDirOperation::MkDirOperation(QSerialPort *serialPort, const QByteArray &dirName, QObject *parent):
|
||||
SimpleSerialOperation(serialPort, parent),
|
||||
m_dirName(dirName)
|
||||
{}
|
||||
|
||||
const QString MkDirOperation::description() const
|
||||
{
|
||||
return QStringLiteral("MkDir @%1").arg(QString(m_dirName));
|
||||
}
|
||||
|
||||
QByteArray MkDirOperation::endOfMessageToken() const
|
||||
{
|
||||
return QByteArrayLiteral("\r\n\r\n>: \a");
|
||||
}
|
||||
|
||||
QByteArray MkDirOperation::commandLine() const
|
||||
{
|
||||
return QByteArrayLiteral("storage mkdir \"") + m_dirName + QByteArrayLiteral("\"\r\n");
|
||||
}
|
||||
|
||||
bool MkDirOperation::parseReceivedData()
|
||||
{
|
||||
const auto lines = receivedData().split('\n');
|
||||
|
||||
if(lines.size() == 4) {
|
||||
const auto msg = lines.at(1).trimmed();
|
||||
|
||||
if(!msg.startsWith(QByteArrayLiteral("Storage error: "))) {
|
||||
return false;
|
||||
} else if(!msg.endsWith(QByteArrayLiteral("file/dir already exist"))) {
|
||||
setError(msg);
|
||||
} else {
|
||||
debug_msg(QStringLiteral("Warning: directory %1 already exists.").arg(QString(m_dirName)));
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
} else if(lines.size() == 3) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -1,27 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include "simpleserialoperation.h"
|
||||
|
||||
namespace Flipper {
|
||||
namespace Zero {
|
||||
|
||||
class MkDirOperation : public SimpleSerialOperation
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
MkDirOperation(QSerialPort *serialPort, const QByteArray &dirName, QObject *parent = nullptr);
|
||||
const QString description() const override;
|
||||
|
||||
private:
|
||||
QByteArray endOfMessageToken() const override;
|
||||
QByteArray commandLine() const override;
|
||||
bool parseReceivedData() override;
|
||||
|
||||
QByteArray m_dirName;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -1,127 +0,0 @@
|
|||
#include "readoperation.h"
|
||||
|
||||
#include <QSerialPort>
|
||||
|
||||
#define READY_PROMPT QByteArrayLiteral("\r\nReady?\r\n")
|
||||
#define FINISH_PROMPT QByteArrayLiteral("\r\n\r\n>: ")
|
||||
|
||||
#define READY_PROMPT_LINE_COUNT 5
|
||||
#define FINISH_PROMPT_LINE_COUNT 4
|
||||
|
||||
#define CHUNK_SIZE 512
|
||||
|
||||
using namespace Flipper;
|
||||
using namespace Zero;
|
||||
|
||||
ReadOperation::ReadOperation(QSerialPort *serialPort, const QByteArray &fileName, QIODevice *file, QObject *parent):
|
||||
AbstractSerialOperation(serialPort, parent),
|
||||
m_size(0),
|
||||
m_fileName(fileName),
|
||||
m_file(file)
|
||||
{}
|
||||
|
||||
const QString ReadOperation::description() const
|
||||
{
|
||||
return QStringLiteral("Read @%1").arg(QString(m_fileName));
|
||||
}
|
||||
|
||||
void ReadOperation::onSerialPortReadyRead()
|
||||
{
|
||||
startTimeout();
|
||||
|
||||
m_receivedData += serialPort()->readAll();
|
||||
|
||||
if(operationState() == State::SettingUp) {
|
||||
if(m_receivedData.endsWith(READY_PROMPT)) {
|
||||
if(!parseSetupReply()) {
|
||||
finish();
|
||||
} else {
|
||||
setOperationState(State::ReceivingData);
|
||||
serialPort()->write("\n");
|
||||
}
|
||||
|
||||
m_receivedData.clear();
|
||||
} else if(m_receivedData.endsWith(FINISH_PROMPT)) {
|
||||
parseError();
|
||||
finish();
|
||||
}
|
||||
|
||||
} else if(operationState() == State::ReceivingData) {
|
||||
if(m_receivedData.endsWith(READY_PROMPT)) {
|
||||
m_file->write(m_receivedData.chopped(READY_PROMPT.size()));
|
||||
m_receivedData.clear();
|
||||
serialPort()->write("\n");
|
||||
|
||||
} else if(m_receivedData.endsWith(FINISH_PROMPT)) {
|
||||
m_file->write(m_receivedData.chopped(FINISH_PROMPT.size()));
|
||||
m_file->seek(0);
|
||||
finish();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool ReadOperation::begin()
|
||||
{
|
||||
const auto cmdLine = QByteArrayLiteral("storage read_chunks \"") + m_fileName + QByteArrayLiteral("\" ") +
|
||||
QByteArray::number(CHUNK_SIZE, 10) + QByteArrayLiteral("\r");
|
||||
const auto success = (serialPort()->write(cmdLine) == cmdLine.size()) && serialPort()->flush();
|
||||
|
||||
if(success) {
|
||||
setOperationState(State::SettingUp);
|
||||
startTimeout();
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
bool ReadOperation::parseError()
|
||||
{
|
||||
const auto lines = m_receivedData.split('\n');
|
||||
|
||||
if(lines.size() != FINISH_PROMPT_LINE_COUNT) {
|
||||
setError(QStringLiteral("Unexpected error message line count"));
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto &msg = lines.at(1).trimmed();
|
||||
|
||||
if(!msg.startsWith("Storage error:")) {
|
||||
setError(QStringLiteral("Unexpected error message format"));
|
||||
return false;
|
||||
}
|
||||
|
||||
setError(msg);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ReadOperation::parseSetupReply()
|
||||
{
|
||||
const auto lines = m_receivedData.split('\n');
|
||||
if(lines.size() != READY_PROMPT_LINE_COUNT) {
|
||||
setError(QStringLiteral("Unexpected setup message line count"));
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto &sizeMsg = lines.at(1);
|
||||
if(!sizeMsg.startsWith("Size:")) {
|
||||
setError(QStringLiteral("Unexpected setup message format"));
|
||||
return false;
|
||||
}
|
||||
|
||||
return parseSize(sizeMsg);
|
||||
}
|
||||
|
||||
bool ReadOperation::parseSize(const QByteArray &s)
|
||||
{
|
||||
const auto tokens = s.split(':');
|
||||
|
||||
if(tokens.size() != 2) {
|
||||
setError(QStringLiteral("Unexpected size message format"));
|
||||
return false;
|
||||
}
|
||||
|
||||
bool success;
|
||||
m_size = tokens.at(1).toLongLong(&success, 10);
|
||||
|
||||
return success;
|
||||
}
|
|
@ -1,43 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include "abstractserialoperation.h"
|
||||
|
||||
#include <QByteArray>
|
||||
|
||||
class QIODevice;
|
||||
|
||||
namespace Flipper {
|
||||
namespace Zero {
|
||||
|
||||
class ReadOperation : public AbstractSerialOperation
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
enum State {
|
||||
SettingUp = BasicOperationState::Ready,
|
||||
ReceivingData
|
||||
};
|
||||
|
||||
public:
|
||||
ReadOperation(QSerialPort *serialPort, const QByteArray &fileName, QIODevice *file, QObject *parent = nullptr);
|
||||
const QString description() const override;
|
||||
|
||||
private slots:
|
||||
void onSerialPortReadyRead() override;
|
||||
|
||||
private:
|
||||
bool begin() override;
|
||||
|
||||
bool parseError();
|
||||
bool parseSetupReply();
|
||||
bool parseSize(const QByteArray &s);
|
||||
|
||||
qint64 m_size;
|
||||
QByteArray m_fileName;
|
||||
QIODevice *m_file;
|
||||
QByteArray m_receivedData;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -1,35 +0,0 @@
|
|||
#include "rebootoperation.h"
|
||||
|
||||
#include <QTimer>
|
||||
#include <QSerialPort>
|
||||
|
||||
#define CALL_LATER(obj, func) (QTimer::singleShot(0, obj, func))
|
||||
|
||||
using namespace Flipper;
|
||||
using namespace Zero;
|
||||
|
||||
RebootOperation::RebootOperation(QSerialPort *serialPort, QObject *parent):
|
||||
AbstractSerialOperation(serialPort, parent)
|
||||
{}
|
||||
|
||||
const QString RebootOperation::description() const
|
||||
{
|
||||
return QStringLiteral("Reboot @%1").arg(QString(serialPort()->portName()));
|
||||
}
|
||||
|
||||
void RebootOperation::onSerialPortReadyRead()
|
||||
{
|
||||
// This operation does not need serial output, discarding it
|
||||
serialPort()->clear();
|
||||
}
|
||||
|
||||
bool RebootOperation::begin()
|
||||
{
|
||||
const auto success = (serialPort()->write("\rreboot\r\n") > 0) && serialPort()->flush();
|
||||
|
||||
if(success) {
|
||||
CALL_LATER(this, &AbstractOperation::finish);
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
|
@ -1,50 +0,0 @@
|
|||
#include "removeoperation.h"
|
||||
|
||||
#include "debug.h"
|
||||
|
||||
using namespace Flipper;
|
||||
using namespace Zero;
|
||||
|
||||
RemoveOperation::RemoveOperation(QSerialPort *serialPort, const QByteArray &fileName, QObject *parent):
|
||||
SimpleSerialOperation(serialPort, parent),
|
||||
m_fileName(fileName)
|
||||
{}
|
||||
|
||||
const QString RemoveOperation::description() const
|
||||
{
|
||||
return QStringLiteral("Remove @%1").arg(QString(m_fileName));
|
||||
}
|
||||
|
||||
QByteArray RemoveOperation::endOfMessageToken() const
|
||||
{
|
||||
return QByteArrayLiteral("\r\n\r\n>: \a");
|
||||
}
|
||||
|
||||
QByteArray RemoveOperation::commandLine() const
|
||||
{
|
||||
return QByteArrayLiteral("storage remove \"") + m_fileName + QByteArrayLiteral("\"\r\n");
|
||||
}
|
||||
|
||||
bool RemoveOperation::parseReceivedData()
|
||||
{
|
||||
const auto lines = receivedData().split('\n');
|
||||
|
||||
if(lines.size() == 4) {
|
||||
const auto msg = lines.at(1).trimmed();
|
||||
|
||||
if(!msg.startsWith(QByteArrayLiteral("Storage error: "))) {
|
||||
return false;
|
||||
} else if(!msg.endsWith(QByteArrayLiteral("file/dir not exist"))) {
|
||||
setError(msg);
|
||||
} else {
|
||||
debug_msg(QStringLiteral("Warning: file %1 does not exist.").arg(QString(m_fileName)));
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
} else if(lines.size() == 3) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -1,27 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include "simpleserialoperation.h"
|
||||
|
||||
namespace Flipper {
|
||||
namespace Zero {
|
||||
|
||||
class RemoveOperation : public SimpleSerialOperation
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
RemoveOperation(QSerialPort *serialPort, const QByteArray &fileName, QObject *parent = nullptr);
|
||||
const QString description() const override;
|
||||
|
||||
private:
|
||||
QByteArray endOfMessageToken() const override;
|
||||
QByteArray commandLine() const override;
|
||||
|
||||
bool parseReceivedData() override;
|
||||
|
||||
QByteArray m_fileName;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
#include "startrpcoperation.h"
|
||||
|
||||
#include <QSerialPort>
|
||||
|
||||
#include "flipperzero/protobuf/systemprotobufmessage.h"
|
||||
|
||||
using namespace Flipper;
|
||||
using namespace Zero;
|
||||
|
||||
StartRPCOperation::StartRPCOperation(QSerialPort *serialPort, QObject *parent):
|
||||
AbstractProtobufOperation(serialPort, parent)
|
||||
{}
|
||||
|
||||
const QString StartRPCOperation::description() const
|
||||
{
|
||||
return QStringLiteral("Start RPC session @%1").arg(QString(serialPort()->portName()));
|
||||
}
|
||||
|
||||
void StartRPCOperation::onSerialPortReadyRead()
|
||||
{
|
||||
if(operationState() == State::LeavingCli) {
|
||||
if(serialPort()->bytesAvailable() < s_cmd.length() + 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto str = serialPort()->readAll();
|
||||
|
||||
if(!str.startsWith(s_cmd)) {
|
||||
finishWithError(QStringLiteral("Failed to send the command due to interference"));
|
||||
return;
|
||||
}
|
||||
|
||||
SystemPingRequest request(serialPort());
|
||||
|
||||
if(!request.send()) {
|
||||
finishWithError(QStringLiteral("Failed to send the ping request"));
|
||||
} else {
|
||||
setOperationState(State::WaitingForPing);
|
||||
}
|
||||
|
||||
|
||||
} else if(operationState() == State::WaitingForPing) {
|
||||
SystemPingResponse response(serialPort());
|
||||
|
||||
if(!response.receive()) {
|
||||
return;
|
||||
} else if(!response.isOk()) {
|
||||
finishWithError(QStringLiteral("Device replied with error: %1").arg(response.commandStatusString()));
|
||||
} else if(!response.isValidType()) {
|
||||
finishWithError(QStringLiteral("Expected system ping response, got something else"));
|
||||
} else {
|
||||
finish();
|
||||
}
|
||||
|
||||
} else {
|
||||
finishWithError(QStringLiteral("Received data in an unexpected state"));
|
||||
}
|
||||
}
|
||||
|
||||
bool StartRPCOperation::begin()
|
||||
{
|
||||
setOperationState(State::LeavingCli);
|
||||
return (serialPort()->write(s_cmd) == s_cmd.size()) && serialPort()->flush();
|
||||
}
|
||||
|
||||
const QByteArray StartRPCOperation::s_cmd("start_rpc_session\r");
|
|
@ -0,0 +1,31 @@
|
|||
#pragma once
|
||||
|
||||
#include "abstractprotobufoperation.h"
|
||||
|
||||
namespace Flipper {
|
||||
namespace Zero {
|
||||
|
||||
class StartRPCOperation : public AbstractProtobufOperation
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
enum State {
|
||||
LeavingCli = AbstractOperation::User,
|
||||
WaitingForPing
|
||||
};
|
||||
|
||||
public:
|
||||
StartRPCOperation(QSerialPort *serialPort, QObject *parent = nullptr);
|
||||
const QString description() const override;
|
||||
|
||||
private slots:
|
||||
void onSerialPortReadyRead() override;
|
||||
|
||||
private:
|
||||
bool begin() override;
|
||||
static const QByteArray s_cmd;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -1,132 +0,0 @@
|
|||
#include "statoperation.h"
|
||||
|
||||
#include <QRegExp>
|
||||
#include <QSerialPort>
|
||||
|
||||
#include "debug.h"
|
||||
|
||||
using namespace Flipper;
|
||||
using namespace Zero;
|
||||
|
||||
static qint64 fromStringSize(const QByteArray &s);
|
||||
|
||||
StatOperation::StatOperation(QSerialPort *serialPort, const QByteArray &fileName, QObject *parent):
|
||||
SimpleSerialOperation(serialPort, parent),
|
||||
m_fileName(fileName),
|
||||
m_size(-1),
|
||||
m_sizeFree(-1),
|
||||
m_type(Type::Invalid)
|
||||
{}
|
||||
|
||||
const QString StatOperation::description() const
|
||||
{
|
||||
return QStringLiteral("Stat @%1").arg(QString(m_fileName));
|
||||
}
|
||||
|
||||
const QByteArray &StatOperation::fileName() const
|
||||
{
|
||||
return m_fileName;
|
||||
}
|
||||
|
||||
qint64 StatOperation::size() const
|
||||
{
|
||||
return m_size;
|
||||
}
|
||||
|
||||
qint64 StatOperation::sizeFree() const
|
||||
{
|
||||
return m_sizeFree;
|
||||
}
|
||||
|
||||
StatOperation::Type StatOperation::type() const
|
||||
{
|
||||
return m_type;
|
||||
}
|
||||
|
||||
QByteArray StatOperation::endOfMessageToken() const
|
||||
{
|
||||
return QByteArrayLiteral("\r\n\r\n>: \a");
|
||||
}
|
||||
|
||||
QByteArray StatOperation::commandLine() const
|
||||
{
|
||||
return QByteArrayLiteral("storage stat \"") + m_fileName + QByteArrayLiteral("\"\r\n");
|
||||
}
|
||||
|
||||
bool StatOperation::parseReceivedData()
|
||||
{
|
||||
const auto lines = receivedData().split('\n');
|
||||
check_return_bool(lines.size() == 4, "Wrong reply line count.");
|
||||
|
||||
const auto reply = lines[1].trimmed().toLower();
|
||||
|
||||
if(reply == "storage error: file/dir not exist") {
|
||||
m_type = Type::NotFound;
|
||||
|
||||
} else if(reply == "storage error: invalid name/path") {
|
||||
m_type = Type::Invalid;
|
||||
|
||||
} else if(reply == "storage error: internal error") {
|
||||
m_type = Type::InternalError;
|
||||
|
||||
} else if(parseFileSize(reply)) {
|
||||
m_type = Type::RegularFile;
|
||||
|
||||
} else if(reply.contains("directory")) {
|
||||
m_type = Type::Directory;
|
||||
|
||||
} else if(parseStorageSize(reply)) {
|
||||
m_type = Type::Storage;
|
||||
|
||||
} else {
|
||||
error_msg("Unexpected stat reply string.");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool StatOperation::parseFileSize(const QByteArray &data)
|
||||
{
|
||||
QRegExp expr("file, size: ([0-9]+k?i?b)");
|
||||
|
||||
if(!expr.exactMatch(data) || (expr.captureCount() != 1)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto captures = expr.capturedTexts();
|
||||
m_size = fromStringSize(captures[1].toLocal8Bit());
|
||||
|
||||
return m_size >= 0;
|
||||
}
|
||||
|
||||
bool StatOperation::parseStorageSize(const QByteArray &data)
|
||||
{
|
||||
QRegExp expr("storage, ([0-9]+k?i?b) total, ([0-9]+k?i?b) free");
|
||||
|
||||
if(!expr.exactMatch(data) || (expr.captureCount() != 2)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto captures = expr.capturedTexts();
|
||||
m_size = fromStringSize(captures[1].toLocal8Bit());
|
||||
m_sizeFree = fromStringSize(captures[2].toLocal8Bit());
|
||||
|
||||
return (m_size >= 0) && (m_sizeFree >= 0);
|
||||
}
|
||||
|
||||
static qint64 fromStringSize(const QByteArray &s)
|
||||
{
|
||||
qint64 result = -1;
|
||||
bool cr = true;
|
||||
|
||||
if(s.endsWith("kb")) {
|
||||
result = s.chopped(2).toULongLong(&cr, 10) * 1024; //TODO: fix incorrect multipliers in the firmware?
|
||||
} else if(s.endsWith("kib")) {
|
||||
result = s.chopped(3).toULongLong(&cr, 10) * 1024;
|
||||
} else if(s.endsWith('b')) {
|
||||
result = s.chopped(1).toULongLong(&cr, 10);
|
||||
}
|
||||
|
||||
return cr ? result : -1;
|
||||
}
|
|
@ -1,49 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include "simpleserialoperation.h"
|
||||
|
||||
namespace Flipper {
|
||||
namespace Zero {
|
||||
|
||||
class StatOperation : public SimpleSerialOperation
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
enum class Type {
|
||||
RegularFile,
|
||||
Directory,
|
||||
Storage,
|
||||
NotFound,
|
||||
InternalError,
|
||||
Invalid
|
||||
};
|
||||
|
||||
Q_ENUM(Type)
|
||||
|
||||
StatOperation(QSerialPort *serialPort, const QByteArray &fileName, QObject *parent = nullptr);
|
||||
|
||||
const QString description() const override;
|
||||
|
||||
const QByteArray &fileName() const;
|
||||
qint64 size() const;
|
||||
qint64 sizeFree() const;
|
||||
Type type() const;
|
||||
|
||||
private:
|
||||
QByteArray endOfMessageToken() const override;
|
||||
QByteArray commandLine() const override;
|
||||
bool parseReceivedData() override;
|
||||
|
||||
bool parseFileSize(const QByteArray &data);
|
||||
bool parseStorageSize(const QByteArray &data);
|
||||
|
||||
QByteArray m_fileName;
|
||||
qint64 m_size;
|
||||
qint64 m_sizeFree;
|
||||
Type m_type;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
#include "stoprpcoperation.h"
|
||||
|
||||
#include <QSerialPort>
|
||||
|
||||
#include "flipperzero/protobuf/mainprotobufmessage.h"
|
||||
|
||||
using namespace Flipper;
|
||||
using namespace Zero;
|
||||
|
||||
StopRPCOperation::StopRPCOperation(QSerialPort *serialPort, QObject *parent):
|
||||
AbstractProtobufOperation(serialPort, parent)
|
||||
{}
|
||||
|
||||
const QString StopRPCOperation::description() const
|
||||
{
|
||||
return QStringLiteral("Stop RPC session @%1").arg(QString(serialPort()->portName()));
|
||||
}
|
||||
|
||||
void StopRPCOperation::onSerialPortReadyRead()
|
||||
{
|
||||
m_receivedData.append(serialPort()->readAll());
|
||||
if(m_receivedData.endsWith(QByteArrayLiteral("\r\n>: "))) {
|
||||
finish();
|
||||
}
|
||||
}
|
||||
|
||||
bool StopRPCOperation::begin()
|
||||
{
|
||||
MainStopSessionRequest request(serialPort());
|
||||
return request.send();
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
#pragma once
|
||||
|
||||
#include "abstractprotobufoperation.h"
|
||||
|
||||
namespace Flipper {
|
||||
namespace Zero {
|
||||
|
||||
class StopRPCOperation : public AbstractProtobufOperation
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
StopRPCOperation(QSerialPort *serialPort, QObject *parent = nullptr);
|
||||
const QString description() const override;
|
||||
|
||||
private slots:
|
||||
void onSerialPortReadyRead() override;
|
||||
|
||||
private:
|
||||
bool begin() override;
|
||||
|
||||
QByteArray m_receivedData;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
#include "storageinfooperation.h"
|
||||
|
||||
#include "flipperzero/protobuf/storageprotobufmessage.h"
|
||||
|
||||
using namespace Flipper;
|
||||
using namespace Zero;
|
||||
|
||||
StorageInfoOperation::StorageInfoOperation(QSerialPort *serialPort, const QByteArray &path, QObject *parent):
|
||||
AbstractProtobufOperation(serialPort, parent),
|
||||
m_path(path),
|
||||
m_isPresent(false),
|
||||
m_sizeFree(0),
|
||||
m_sizeTotal(0)
|
||||
{}
|
||||
|
||||
const QString StorageInfoOperation::description() const
|
||||
{
|
||||
return QStringLiteral("Storage info @%1").arg(QString(m_path));
|
||||
}
|
||||
|
||||
bool StorageInfoOperation::isPresent() const
|
||||
{
|
||||
return m_isPresent;
|
||||
}
|
||||
|
||||
quint64 StorageInfoOperation::sizeFree() const
|
||||
{
|
||||
return m_sizeFree;
|
||||
}
|
||||
|
||||
quint64 StorageInfoOperation::sizeTotal() const
|
||||
{
|
||||
return m_sizeTotal;
|
||||
}
|
||||
|
||||
void StorageInfoOperation::onSerialPortReadyRead()
|
||||
{
|
||||
StorageInfoResponse response(serialPort());
|
||||
|
||||
if(!response.receive()) {
|
||||
return;
|
||||
|
||||
} else if(!response.isOk()) {
|
||||
const auto status = response.commandStatus();
|
||||
// TODO: more flexible error handling
|
||||
if(status == PB_CommandStatus_ERROR_STORAGE_INTERNAL) {
|
||||
finish();
|
||||
} else{
|
||||
finishWithError(QStringLiteral("Device replied with error: %1").arg(response.commandStatusString()));
|
||||
}
|
||||
|
||||
} else if(!response.isValidType()) {
|
||||
finishWithError(QStringLiteral("Expected StorageInfo response, got something else"));
|
||||
|
||||
} else {
|
||||
m_isPresent = true;
|
||||
m_sizeFree = response.sizeFree();
|
||||
m_sizeTotal = response.sizeTotal();
|
||||
|
||||
finish();
|
||||
}
|
||||
}
|
||||
|
||||
bool StorageInfoOperation::begin()
|
||||
{
|
||||
StorageInfoRequest request(serialPort(), m_path);
|
||||
return request.send();
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
#pragma once
|
||||
|
||||
#include "abstractprotobufoperation.h"
|
||||
|
||||
namespace Flipper {
|
||||
namespace Zero {
|
||||
|
||||
class StorageInfoOperation : public AbstractProtobufOperation
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
StorageInfoOperation(QSerialPort *serialPort, const QByteArray &path, QObject *parent = nullptr);
|
||||
const QString description() const override;
|
||||
|
||||
bool isPresent() const;
|
||||
quint64 sizeFree() const;
|
||||
quint64 sizeTotal() const;
|
||||
|
||||
private slots:
|
||||
void onSerialPortReadyRead() override;
|
||||
|
||||
private:
|
||||
bool begin() override;
|
||||
|
||||
QByteArray m_path;
|
||||
bool m_isPresent;
|
||||
quint64 m_sizeFree;
|
||||
quint64 m_sizeTotal;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
#include "storagelistoperation.h"
|
||||
|
||||
#include "flipperzero/protobuf/storageprotobufmessage.h"
|
||||
|
||||
using namespace Flipper;
|
||||
using namespace Zero;
|
||||
|
||||
StorageListOperation::StorageListOperation(QSerialPort *serialPort, const QByteArray &path, QObject *parent):
|
||||
AbstractSerialOperation(serialPort, parent),
|
||||
m_path(path)
|
||||
{}
|
||||
|
||||
const QString StorageListOperation::description() const
|
||||
{
|
||||
return QStringLiteral("Storage list @%1").arg(QString(m_path));
|
||||
}
|
||||
|
||||
const FileInfoList &StorageListOperation::files() const
|
||||
{
|
||||
return m_result;
|
||||
}
|
||||
|
||||
void StorageListOperation::onSerialPortReadyRead()
|
||||
{
|
||||
StorageListResponse response(serialPort());
|
||||
|
||||
while(response.receive()) {
|
||||
|
||||
if(!response.isOk()) {
|
||||
finishWithError(QStringLiteral("Device replied with error: %1").arg(response.commandStatusString()));
|
||||
return;
|
||||
|
||||
} else if(!response.isValidType()) {
|
||||
finishWithError(QStringLiteral("Expected StorageList response, got something else"));
|
||||
return;
|
||||
}
|
||||
|
||||
const auto &files = response.files();
|
||||
for(auto &file : files) {
|
||||
|
||||
FileInfo fileInfo {
|
||||
QByteArray(file.name),
|
||||
m_path + QByteArrayLiteral("/") + QByteArray(file.name),
|
||||
file.type == PB_Storage_File_FileType_FILE ? FileType::RegularFile : FileType::Directory,
|
||||
file.size
|
||||
};
|
||||
|
||||
m_result.append(fileInfo);
|
||||
}
|
||||
|
||||
if(!response.hasNext()) {
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool StorageListOperation::begin()
|
||||
{
|
||||
StorageListRequest request(serialPort(), m_path);
|
||||
return request.send();
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
#pragma once
|
||||
|
||||
#include "abstractserialoperation.h"
|
||||
#include "fileinfo.h"
|
||||
|
||||
namespace Flipper {
|
||||
namespace Zero {
|
||||
|
||||
class StorageListOperation : public AbstractSerialOperation
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
StorageListOperation(QSerialPort *serialPort, const QByteArray &path, QObject *parent = nullptr);
|
||||
const QString description() const override;
|
||||
const FileInfoList &files() const;
|
||||
|
||||
private slots:
|
||||
void onSerialPortReadyRead() override;
|
||||
|
||||
private:
|
||||
bool begin() override;
|
||||
|
||||
QByteArray m_path;
|
||||
FileInfoList m_result;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
#include "storagemkdiroperation.h"
|
||||
#include "flipperzero/protobuf/storageprotobufmessage.h"
|
||||
|
||||
using namespace Flipper;
|
||||
using namespace Zero;
|
||||
|
||||
StorageMkdirOperation::StorageMkdirOperation(QSerialPort *serialPort, const QByteArray &path, QObject *parent):
|
||||
AbstractProtobufOperation(serialPort, parent),
|
||||
m_path(path)
|
||||
{}
|
||||
|
||||
const QString StorageMkdirOperation::description() const
|
||||
{
|
||||
return QStringLiteral("Storage mkdir @%1").arg(QString(m_path));
|
||||
}
|
||||
|
||||
void StorageMkdirOperation::onSerialPortReadyRead()
|
||||
{
|
||||
MainEmptyResponse response(serialPort());
|
||||
|
||||
if(!response.receive()) {
|
||||
return;
|
||||
} else if(!response.isOk() && (response.commandStatus() != PB_CommandStatus_ERROR_STORAGE_EXIST)) {
|
||||
finishWithError(QStringLiteral("Device replied with error: %1").arg(response.commandStatusString()));
|
||||
} else if(!response.isValidType()) {
|
||||
finishWithError(QStringLiteral("Expected empty response, got something else"));
|
||||
} else {
|
||||
finish();
|
||||
}
|
||||
}
|
||||
|
||||
bool StorageMkdirOperation::begin()
|
||||
{
|
||||
StorageMkdirRequest request(serialPort(), m_path);
|
||||
return request.send();
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
#pragma once
|
||||
|
||||
#include "abstractprotobufoperation.h"
|
||||
|
||||
namespace Flipper {
|
||||
namespace Zero {
|
||||
|
||||
class StorageMkdirOperation : public AbstractProtobufOperation
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
StorageMkdirOperation(QSerialPort *serialPort, const QByteArray &path, QObject *parent = nullptr);
|
||||
const QString description() const override;
|
||||
|
||||
private slots:
|
||||
void onSerialPortReadyRead() override;
|
||||
|
||||
private:
|
||||
bool begin() override;
|
||||
|
||||
QByteArray m_path;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
#include "storagereadoperation.h"
|
||||
#include "flipperzero/protobuf/storageprotobufmessage.h"
|
||||
|
||||
using namespace Flipper;
|
||||
using namespace Zero;
|
||||
|
||||
StorageReadOperation::StorageReadOperation(QSerialPort *serialPort, const QByteArray &path, QIODevice *file, QObject *parent):
|
||||
AbstractProtobufOperation(serialPort, parent),
|
||||
m_path(path),
|
||||
m_file(file)
|
||||
{}
|
||||
|
||||
const QString StorageReadOperation::description() const
|
||||
{
|
||||
return QStringLiteral("Storage read @%1").arg(QString(m_path));
|
||||
}
|
||||
|
||||
void StorageReadOperation::onSerialPortReadyRead()
|
||||
{
|
||||
StorageReadResponse response(serialPort());
|
||||
|
||||
while(response.receive()) {
|
||||
|
||||
if(!response.isOk()) {
|
||||
finishWithError(QStringLiteral("Device replied with error: %1").arg(response.commandStatusString()));
|
||||
} else if(!response.isValidType()) {
|
||||
finishWithError(QStringLiteral("Expected StorageRead response, got something else"));
|
||||
} else {
|
||||
const auto &data = response.data();
|
||||
const auto bytesWritten = m_file->write(response.data());
|
||||
|
||||
if(bytesWritten != data.size()) {
|
||||
finishWithError(QStringLiteral("Error writing to output device: %1").arg(m_file->errorString()));
|
||||
} else if(!response.hasNext()) {
|
||||
rewindAndFinish();
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool StorageReadOperation::begin()
|
||||
{
|
||||
StorageReadRequest request(serialPort(), m_path);
|
||||
return request.send();
|
||||
}
|
||||
|
||||
void StorageReadOperation::rewindAndFinish()
|
||||
{
|
||||
if(!m_file->seek(0)) {
|
||||
finishWithError(QStringLiteral("Failed to rewind output device: %1").arg(m_file->errorString()));
|
||||
} else {
|
||||
finish();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
#pragma once
|
||||
|
||||
#include "abstractprotobufoperation.h"
|
||||
|
||||
#include <QByteArray>
|
||||
|
||||
class QIODevice;
|
||||
|
||||
namespace Flipper {
|
||||
namespace Zero {
|
||||
|
||||
class StorageReadOperation : public AbstractProtobufOperation
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
StorageReadOperation(QSerialPort *serialPort, const QByteArray &path, QIODevice *file, QObject *parent = nullptr);
|
||||
const QString description() const override;
|
||||
|
||||
private slots:
|
||||
void onSerialPortReadyRead() override;
|
||||
|
||||
private:
|
||||
bool begin() override;
|
||||
void rewindAndFinish();
|
||||
|
||||
QByteArray m_path;
|
||||
QIODevice *m_file;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
#include "storageremoveoperation.h"
|
||||
|
||||
#include "flipperzero/protobuf/storageprotobufmessage.h"
|
||||
|
||||
using namespace Flipper;
|
||||
using namespace Zero;
|
||||
|
||||
StorageRemoveOperation::StorageRemoveOperation(QSerialPort *serialPort, const QByteArray &path, QObject *parent):
|
||||
AbstractProtobufOperation(serialPort, parent),
|
||||
m_path(path)
|
||||
{}
|
||||
|
||||
const QString StorageRemoveOperation::description() const
|
||||
{
|
||||
return QStringLiteral("Storage remove @%1").arg(QString(m_path));
|
||||
}
|
||||
|
||||
void StorageRemoveOperation::onSerialPortReadyRead()
|
||||
{
|
||||
MainEmptyResponse response(serialPort());
|
||||
|
||||
if(!response.receive()) {
|
||||
return;
|
||||
} else if(!response.isOk()) {
|
||||
finishWithError(QStringLiteral("Device replied with error: %1").arg(response.commandStatusString()));
|
||||
} else if(!response.isValidType()) {
|
||||
finishWithError(QStringLiteral("Expected empty response, got something else"));
|
||||
} else {
|
||||
finish();
|
||||
}
|
||||
}
|
||||
|
||||
bool StorageRemoveOperation::begin()
|
||||
{
|
||||
StorageRemoveRequest request(serialPort(), m_path);
|
||||
return request.send();
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
#pragma once
|
||||
|
||||
#include "abstractprotobufoperation.h"
|
||||
|
||||
namespace Flipper {
|
||||
namespace Zero {
|
||||
|
||||
class StorageRemoveOperation : public AbstractProtobufOperation
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
StorageRemoveOperation(QSerialPort *serialPort, const QByteArray &path, QObject *parent = nullptr);
|
||||
const QString description() const override;
|
||||
|
||||
private slots:
|
||||
void onSerialPortReadyRead() override;
|
||||
|
||||
private:
|
||||
bool begin() override;
|
||||
|
||||
QByteArray m_path;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,73 @@
|
|||
#include "storagestatoperation.h"
|
||||
|
||||
#include "flipperzero/protobuf/storageprotobufmessage.h"
|
||||
|
||||
using namespace Flipper;
|
||||
using namespace Zero;
|
||||
|
||||
StorageStatOperation::StorageStatOperation(QSerialPort *serialPort, const QByteArray &fileName, QObject *parent):
|
||||
AbstractProtobufOperation(serialPort, parent),
|
||||
m_fileName(fileName),
|
||||
m_isPresent(false),
|
||||
m_size(0),
|
||||
m_type(Type::Invalid)
|
||||
{}
|
||||
|
||||
const QString StorageStatOperation::description() const
|
||||
{
|
||||
return QStringLiteral("Storage stat @%1").arg(QString(m_fileName));
|
||||
}
|
||||
|
||||
const QByteArray &StorageStatOperation::fileName() const
|
||||
{
|
||||
return m_fileName;
|
||||
}
|
||||
|
||||
bool StorageStatOperation::isPresent() const
|
||||
{
|
||||
return m_isPresent;
|
||||
}
|
||||
|
||||
quint64 StorageStatOperation::size() const
|
||||
{
|
||||
return m_size;
|
||||
}
|
||||
|
||||
StorageStatOperation::Type StorageStatOperation::type() const
|
||||
{
|
||||
return m_type;
|
||||
}
|
||||
|
||||
void StorageStatOperation::onSerialPortReadyRead()
|
||||
{
|
||||
StorageStatResponse response(serialPort());
|
||||
|
||||
if(!response.receive()) {
|
||||
return;
|
||||
|
||||
} else if(!response.isOk()) {
|
||||
const auto status = response.commandStatus();
|
||||
// TODO: more flexible error handling
|
||||
if(status == PB_CommandStatus_ERROR_STORAGE_NOT_EXIST) {
|
||||
finish();
|
||||
} else{
|
||||
finishWithError(QStringLiteral("Device replied with error: %1").arg(response.commandStatusString()));
|
||||
}
|
||||
|
||||
} else if(!response.isValidType()) {
|
||||
finishWithError(QStringLiteral("Expected StorageStat response, got something else"));
|
||||
|
||||
} else {
|
||||
m_isPresent = response.isPresent();
|
||||
m_type = (response.file().type == PB_Storage_File_FileType_FILE) ? Type::RegularFile : Type::Directory;
|
||||
m_size = response.file().size;
|
||||
|
||||
finish();
|
||||
}
|
||||
}
|
||||
|
||||
bool StorageStatOperation::begin()
|
||||
{
|
||||
StorageStatRequest request(serialPort(), m_fileName);
|
||||
return request.send();
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
#pragma once
|
||||
|
||||
#include "abstractprotobufoperation.h"
|
||||
|
||||
namespace Flipper {
|
||||
namespace Zero {
|
||||
|
||||
class StorageStatOperation : public AbstractProtobufOperation
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
enum class Type {
|
||||
RegularFile,
|
||||
Directory,
|
||||
Invalid
|
||||
};
|
||||
|
||||
Q_ENUM(Type)
|
||||
|
||||
StorageStatOperation(QSerialPort *serialPort, const QByteArray &fileName, QObject *parent = nullptr);
|
||||
const QString description() const override;
|
||||
|
||||
const QByteArray &fileName() const;
|
||||
bool isPresent() const;
|
||||
quint64 size() const;
|
||||
Type type() const;
|
||||
|
||||
private:
|
||||
void onSerialPortReadyRead() override;
|
||||
|
||||
private:
|
||||
bool begin() override;
|
||||
|
||||
QByteArray m_fileName;
|
||||
bool m_isPresent;
|
||||
quint64 m_size;
|
||||
Type m_type;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,74 @@
|
|||
#include "storagewriteoperation.h"
|
||||
|
||||
#include <QTimer>
|
||||
#include <QIODevice>
|
||||
|
||||
#include "flipperzero/protobuf/storageprotobufmessage.h"
|
||||
|
||||
static constexpr qint64 CHUNK_SIZE = 512;
|
||||
|
||||
using namespace Flipper;
|
||||
using namespace Zero;
|
||||
|
||||
StorageWriteOperation::StorageWriteOperation(QSerialPort *serialPort, const QByteArray &path, QIODevice *file, QObject *parent):
|
||||
AbstractProtobufOperation(serialPort, parent),
|
||||
m_path(path),
|
||||
m_file(file),
|
||||
m_byteCount(0)
|
||||
{}
|
||||
|
||||
const QString StorageWriteOperation::description() const
|
||||
{
|
||||
return QStringLiteral("Storage write @%1").arg(QString(m_path));
|
||||
}
|
||||
|
||||
void StorageWriteOperation::onSerialPortReadyRead()
|
||||
{
|
||||
MainEmptyResponse response(serialPort());
|
||||
|
||||
if(!response.receive()) {
|
||||
return;
|
||||
} else if(!response.isOk()) {
|
||||
finishWithError(QStringLiteral("Device replied with error: %1").arg(response.commandStatusString()));
|
||||
} else if(!response.isValidType()) {
|
||||
finishWithError(QStringLiteral("Expected empty response, got something else"));
|
||||
} else {
|
||||
finish();
|
||||
}
|
||||
}
|
||||
|
||||
void StorageWriteOperation::onTotalBytesWrittenChanged()
|
||||
{
|
||||
if(totalBytesWritten() != m_byteCount) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto bytesAvailable = m_file->bytesAvailable();
|
||||
|
||||
if(bytesAvailable < 0) {
|
||||
finishWithError(QStringLiteral("Failed to read from input device: %1").arg(m_file->errorString()));
|
||||
|
||||
} else if(bytesAvailable > 0) {
|
||||
// Must write the chunk asynchronously in order to receive this signal
|
||||
QTimer::singleShot(0, this, [=]() {
|
||||
if(!writeChunk()) {
|
||||
finishWithError(QStringLiteral("Failed to write chunk"));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
bool StorageWriteOperation::begin()
|
||||
{
|
||||
return writeChunk();
|
||||
}
|
||||
|
||||
bool StorageWriteOperation::writeChunk()
|
||||
{
|
||||
const auto hasNext = m_file->bytesAvailable() > CHUNK_SIZE;
|
||||
StorageWriteRequest request(serialPort(), m_path, m_file->read(CHUNK_SIZE), hasNext);
|
||||
|
||||
const auto success = request.send();
|
||||
m_byteCount += request.bytesWritten();
|
||||
return success;
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
#pragma once
|
||||
|
||||
#include "abstractprotobufoperation.h"
|
||||
|
||||
#include <QByteArray>
|
||||
|
||||
class QIODevice;
|
||||
|
||||
namespace Flipper {
|
||||
namespace Zero {
|
||||
|
||||
class StorageWriteOperation : public AbstractProtobufOperation
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
StorageWriteOperation(QSerialPort *serialPort, const QByteArray &path, QIODevice *file, QObject *parent = nullptr);
|
||||
const QString description() const override;
|
||||
|
||||
private slots:
|
||||
void onSerialPortReadyRead() override;
|
||||
void onTotalBytesWrittenChanged() override;
|
||||
|
||||
private:
|
||||
bool begin() override;
|
||||
bool writeChunk();
|
||||
|
||||
QByteArray m_path;
|
||||
QIODevice *m_file;
|
||||
qint64 m_byteCount;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
#include "systemdeviceinfooperation.h"
|
||||
|
||||
#include <QSerialPort>
|
||||
|
||||
#include "flipperzero/protobuf/systemprotobufmessage.h"
|
||||
|
||||
using namespace Flipper;
|
||||
using namespace Zero;
|
||||
|
||||
SystemDeviceInfoOperation::SystemDeviceInfoOperation(QSerialPort *serialPort, QObject *parent):
|
||||
AbstractProtobufOperation(serialPort, parent)
|
||||
{}
|
||||
|
||||
const QString SystemDeviceInfoOperation::description() const
|
||||
{
|
||||
return QStringLiteral("Device Info @%1").arg(QString(serialPort()->portName()));
|
||||
}
|
||||
|
||||
const QByteArray SystemDeviceInfoOperation::result(const QByteArray &key) const
|
||||
{
|
||||
return m_data.value(key);
|
||||
}
|
||||
|
||||
void SystemDeviceInfoOperation::onSerialPortReadyRead()
|
||||
{
|
||||
SystemDeviceInfoResponse response(serialPort());
|
||||
|
||||
while(response.receive()) {
|
||||
|
||||
if(!response.isOk()) {
|
||||
finishWithError(QStringLiteral("Device replied with an error response"));
|
||||
return;
|
||||
} else if(!response.isValidType()) {
|
||||
finishWithError(QStringLiteral("Expected empty reply, got something else"));
|
||||
return;
|
||||
}
|
||||
|
||||
m_data.insert(response.key(), response.value());
|
||||
|
||||
if(!response.hasNext()) {
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool SystemDeviceInfoOperation::begin()
|
||||
{
|
||||
SystemDeviceInfoRequest request(serialPort());
|
||||
return request.send();
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
#pragma once
|
||||
|
||||
#include "abstractprotobufoperation.h"
|
||||
|
||||
#include <QHash>
|
||||
#include <QByteArray>
|
||||
|
||||
#include "flipperzero/deviceinfo.h"
|
||||
|
||||
namespace Flipper {
|
||||
namespace Zero {
|
||||
|
||||
class SystemDeviceInfoOperation : public AbstractProtobufOperation
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
SystemDeviceInfoOperation(QSerialPort *serialPort, QObject *parent = nullptr);
|
||||
const QString description() const override;
|
||||
const QByteArray result(const QByteArray &key) const;
|
||||
|
||||
private slots:
|
||||
void onSerialPortReadyRead() override;
|
||||
|
||||
private:
|
||||
bool begin() override;
|
||||
QHash<QByteArray, QByteArray> m_data;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
#include "systemfactoryresetoperation.h"
|
||||
|
||||
#include <QSerialPort>
|
||||
|
||||
#include "flipperzero/protobuf/systemprotobufmessage.h"
|
||||
|
||||
using namespace Flipper;
|
||||
using namespace Zero;
|
||||
|
||||
SystemFactoryResetOperation::SystemFactoryResetOperation(QSerialPort *serialPort, QObject *parent):
|
||||
AbstractProtobufOperation(serialPort, parent),
|
||||
m_byteCount(0)
|
||||
{}
|
||||
|
||||
const QString SystemFactoryResetOperation::description() const
|
||||
{
|
||||
return QStringLiteral("Factory reset @%1").arg(QString(serialPort()->portName()));
|
||||
}
|
||||
|
||||
void SystemFactoryResetOperation::onTotalBytesWrittenChanged()
|
||||
{
|
||||
if(m_byteCount == totalBytesWritten()) {
|
||||
finish();
|
||||
}
|
||||
}
|
||||
|
||||
bool SystemFactoryResetOperation::begin()
|
||||
{
|
||||
SystemFactoryResetRequest request(serialPort());
|
||||
const auto success = request.send();
|
||||
m_byteCount = request.bytesWritten();
|
||||
return success;
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
#pragma once
|
||||
|
||||
#include "abstractprotobufoperation.h"
|
||||
|
||||
namespace Flipper {
|
||||
namespace Zero {
|
||||
|
||||
class SystemFactoryResetOperation : public AbstractProtobufOperation
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
SystemFactoryResetOperation(QSerialPort *serialPort, QObject *parent = nullptr);
|
||||
const QString description() const override;
|
||||
|
||||
private slots:
|
||||
void onTotalBytesWrittenChanged() override;
|
||||
|
||||
private:
|
||||
bool begin() override;
|
||||
qint64 m_byteCount;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
#include "systemrebootoperation.h"
|
||||
|
||||
#include <QTimer>
|
||||
#include <QSerialPort>
|
||||
|
||||
#include "flipperzero/protobuf/systemprotobufmessage.h"
|
||||
|
||||
using namespace Flipper;
|
||||
using namespace Zero;
|
||||
|
||||
SystemRebootOperation::SystemRebootOperation(QSerialPort *serialPort, RebootType rebootType, QObject *parent):
|
||||
AbstractProtobufOperation(serialPort, parent),
|
||||
m_rebootType(rebootType),
|
||||
m_byteCount(0)
|
||||
{}
|
||||
|
||||
const QString SystemRebootOperation::description() const
|
||||
{
|
||||
return QStringLiteral("System reboot @%1").arg(QString(serialPort()->portName()));
|
||||
}
|
||||
|
||||
void SystemRebootOperation::onTotalBytesWrittenChanged()
|
||||
{
|
||||
if(m_byteCount == totalBytesWritten()) {
|
||||
finish();
|
||||
}
|
||||
}
|
||||
|
||||
bool SystemRebootOperation::begin()
|
||||
{
|
||||
const auto rebootType = m_rebootType == RebootType::OS ? PB_System_RebootRequest_RebootMode_OS :
|
||||
PB_System_RebootRequest_RebootMode_DFU;
|
||||
SystemRebootRequest request(serialPort(), rebootType);
|
||||
const auto success = request.send();
|
||||
m_byteCount = request.bytesWritten();
|
||||
return success;
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
#pragma once
|
||||
|
||||
#include "abstractprotobufoperation.h"
|
||||
|
||||
namespace Flipper {
|
||||
namespace Zero {
|
||||
|
||||
class SystemRebootOperation : public AbstractProtobufOperation
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
enum class RebootType {
|
||||
OS,
|
||||
Recovery
|
||||
};
|
||||
|
||||
SystemRebootOperation(QSerialPort *serialPort, RebootType rebootType, QObject *parent = nullptr);
|
||||
const QString description() const override;
|
||||
|
||||
private slots:
|
||||
void onTotalBytesWrittenChanged() override;
|
||||
|
||||
private:
|
||||
bool begin() override;
|
||||
|
||||
RebootType m_rebootType;
|
||||
qint64 m_byteCount;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -1,122 +0,0 @@
|
|||
#include "writeoperation.h"
|
||||
|
||||
#include <QIODevice>
|
||||
#include <QSerialPort>
|
||||
|
||||
#include "debug.h"
|
||||
|
||||
#define READY_PROMPT QByteArrayLiteral("\r\nReady\r\n")
|
||||
#define FINISH_PROMPT QByteArrayLiteral("\r\n>: ")
|
||||
|
||||
#define FINISH_PROMPT_LINE_COUNT 4
|
||||
|
||||
#define CHUNK_SIZE 512
|
||||
|
||||
using namespace Flipper;
|
||||
using namespace Zero;
|
||||
|
||||
WriteOperation::WriteOperation(QSerialPort *serialPort, const QByteArray &fileName, QIODevice *file, QObject *parent):
|
||||
AbstractSerialOperation(serialPort, parent),
|
||||
m_fileName(fileName),
|
||||
m_file(file)
|
||||
{}
|
||||
|
||||
const QString WriteOperation::description() const
|
||||
{
|
||||
return QStringLiteral("Write @%1").arg(QString(m_fileName));
|
||||
}
|
||||
|
||||
void WriteOperation::onSerialPortReadyRead()
|
||||
{
|
||||
m_receivedData.append(serialPort()->readAll());
|
||||
|
||||
if(operationState() == State::SettingUp) {
|
||||
if(m_receivedData.endsWith(FINISH_PROMPT)) {
|
||||
parseError();
|
||||
finish();
|
||||
|
||||
} else if(!m_receivedData.endsWith(READY_PROMPT)) {
|
||||
return;
|
||||
}
|
||||
|
||||
setOperationState(State::WritingData);
|
||||
|
||||
if(!writeChunk()) {
|
||||
finishWithError(QStringLiteral("Failed to write chunk"));
|
||||
}
|
||||
|
||||
} else if(operationState() == State::WritingData) {
|
||||
if(!m_receivedData.endsWith(FINISH_PROMPT)) {
|
||||
return;
|
||||
}
|
||||
|
||||
setOperationState(State::SettingUp);
|
||||
|
||||
if(!m_file->bytesAvailable()) {
|
||||
finish();
|
||||
} else if(!writeSetupCommand()) {
|
||||
finishWithError(QStringLiteral("Failed to write chunk"));
|
||||
}
|
||||
|
||||
} else {
|
||||
finishWithError(QStringLiteral("Unexpected data"));
|
||||
}
|
||||
|
||||
m_receivedData.clear();
|
||||
}
|
||||
|
||||
bool WriteOperation::begin()
|
||||
{
|
||||
check_return_bool(m_file->bytesAvailable(), "No data is available for reading from file");
|
||||
|
||||
setOperationState(State::SettingUp);
|
||||
return writeSetupCommand();
|
||||
}
|
||||
|
||||
bool WriteOperation::writeSetupCommand()
|
||||
{
|
||||
const auto bytesAvailable = m_file->bytesAvailable();
|
||||
m_chunkSize = bytesAvailable < CHUNK_SIZE ? bytesAvailable : CHUNK_SIZE;
|
||||
const auto cmdLine = QByteArrayLiteral("storage write_chunk \"") + m_fileName + QByteArrayLiteral("\" ") +
|
||||
QByteArray::number(m_chunkSize) + QByteArrayLiteral("\r");
|
||||
|
||||
const auto success = (serialPort()->write(cmdLine) == cmdLine.size()) && serialPort()->flush();
|
||||
|
||||
if(success) {
|
||||
startTimeout();
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
bool WriteOperation::writeChunk()
|
||||
{
|
||||
const auto data = m_file->read(m_chunkSize);
|
||||
const auto success = (serialPort()->write(data) == data.size()) && serialPort()->flush();
|
||||
|
||||
if(success) {
|
||||
startTimeout();
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
bool WriteOperation::parseError()
|
||||
{
|
||||
const auto lines = m_receivedData.split('\n');
|
||||
|
||||
if(lines.size() != FINISH_PROMPT_LINE_COUNT) {
|
||||
setError(QStringLiteral("Unexpected error message line count"));
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto &msg = lines.at(1).trimmed();
|
||||
|
||||
if(!msg.startsWith("Storage error:")) {
|
||||
setError(QStringLiteral("Unexpected error message format"));
|
||||
return false;
|
||||
}
|
||||
|
||||
setError(msg);
|
||||
return true;
|
||||
}
|
|
@ -1,44 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include "abstractserialoperation.h"
|
||||
|
||||
#include <QByteArray>
|
||||
|
||||
class QIODevice;
|
||||
|
||||
namespace Flipper {
|
||||
namespace Zero {
|
||||
|
||||
class WriteOperation : public AbstractSerialOperation
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
enum State {
|
||||
SettingUp = BasicOperationState::Ready,
|
||||
WritingData
|
||||
};
|
||||
|
||||
public:
|
||||
WriteOperation(QSerialPort *serialPort, const QByteArray &fileName, QIODevice *file, QObject *parent = nullptr);
|
||||
const QString description() const override;
|
||||
|
||||
private slots:
|
||||
void onSerialPortReadyRead() override;
|
||||
|
||||
private:
|
||||
bool begin() override;
|
||||
|
||||
bool writeSetupCommand();
|
||||
bool writeChunk();
|
||||
|
||||
bool parseError();
|
||||
|
||||
QByteArray m_fileName;
|
||||
QIODevice *m_file;
|
||||
QByteArray m_receivedData;
|
||||
qint64 m_chunkSize;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -1,131 +1,114 @@
|
|||
#include "commandinterface.h"
|
||||
|
||||
#include <QSerialPort>
|
||||
#include <QLoggingCategory>
|
||||
|
||||
#include "flipperzero/devicestate.h"
|
||||
|
||||
#include "cli/factoryresetclioperation.h"
|
||||
#include "cli/skipmotdoperation.h"
|
||||
#include "cli/rebootoperation.h"
|
||||
#include "cli/removeoperation.h"
|
||||
#include "cli/mkdiroperation.h"
|
||||
#include "cli/writeoperation.h"
|
||||
#include "cli/readoperation.h"
|
||||
#include "cli/statoperation.h"
|
||||
#include "cli/listoperation.h"
|
||||
#include "cli/dfuoperation.h"
|
||||
|
||||
#include "debug.h"
|
||||
#include "cli/startrpcoperation.h"
|
||||
#include "cli/stoprpcoperation.h"
|
||||
|
||||
Q_LOGGING_CATEGORY(CATEGORY_CLI, "CLI");
|
||||
#include "cli/systemrebootoperation.h"
|
||||
#include "cli/systemfactoryresetoperation.h"
|
||||
|
||||
#include "cli/storageremoveoperation.h"
|
||||
#include "cli/storagemkdiroperation.h"
|
||||
#include "cli/storagewriteoperation.h"
|
||||
#include "cli/storagereadoperation.h"
|
||||
#include "cli/storagelistoperation.h"
|
||||
#include "cli/storagestatoperation.h"
|
||||
#include "cli/storageinfooperation.h"
|
||||
|
||||
#include "cli/guistartstreamoperation.h"
|
||||
#include "cli/guistopstreamoperation.h"
|
||||
|
||||
Q_LOGGING_CATEGORY(CATEGORY_RPC, "RPC");
|
||||
|
||||
using namespace Flipper;
|
||||
using namespace Zero;
|
||||
|
||||
CommandInterface::CommandInterface(DeviceState *state, QObject *parent):
|
||||
CommandInterface::CommandInterface(DeviceState *deviceState, QObject *parent):
|
||||
AbstractOperationRunner(parent),
|
||||
m_serialPort(nullptr)
|
||||
m_deviceState(deviceState)
|
||||
{}
|
||||
|
||||
QSerialPort *CommandInterface::serialPort() const
|
||||
{
|
||||
// Automatically re-create serial port instance when a persistent device reconnects
|
||||
const auto createSerialPort = [=]() {
|
||||
if(m_serialPort) {
|
||||
check_continue(!m_serialPort->isOpen(), "Deleting a Serial Port instance that is still open.");
|
||||
m_serialPort->deleteLater();
|
||||
m_serialPort = nullptr;
|
||||
}
|
||||
|
||||
const auto &portInfo = state->deviceInfo().serialInfo;
|
||||
|
||||
if(!portInfo.isNull()) {
|
||||
m_serialPort = new QSerialPort(portInfo, this);
|
||||
}
|
||||
};
|
||||
|
||||
connect(state, &DeviceState::deviceInfoChanged, this, createSerialPort);
|
||||
|
||||
createSerialPort();
|
||||
return m_deviceState->serialPort();
|
||||
}
|
||||
|
||||
RebootOperation *CommandInterface::reboot()
|
||||
StopRPCOperation *CommandInterface::stopRPCSession()
|
||||
{
|
||||
auto *op = new RebootOperation(m_serialPort, this);
|
||||
enqueueOperation(op);
|
||||
return op;
|
||||
return registerOperation(new StopRPCOperation(serialPort(), this));
|
||||
}
|
||||
|
||||
DFUOperation *CommandInterface::startRecoveryMode()
|
||||
StartRPCOperation *CommandInterface::startRPCSession()
|
||||
{
|
||||
auto *op = new DFUOperation(m_serialPort, this);
|
||||
enqueueOperation(op);
|
||||
return op;
|
||||
return registerOperation(new StartRPCOperation(serialPort(), this));
|
||||
}
|
||||
|
||||
FactoryResetCliOperation *CommandInterface::factoryReset()
|
||||
SystemRebootOperation *CommandInterface::rebootToOS()
|
||||
{
|
||||
auto *op = new FactoryResetCliOperation(m_serialPort, this);
|
||||
enqueueOperation(op);
|
||||
return op;
|
||||
return registerOperation(new SystemRebootOperation(serialPort(), SystemRebootOperation::RebootType::OS, this));
|
||||
}
|
||||
|
||||
ListOperation *CommandInterface::list(const QByteArray &dirName)
|
||||
SystemRebootOperation *CommandInterface::rebootToRecovery()
|
||||
{
|
||||
auto *op = new ListOperation(m_serialPort, dirName, this);
|
||||
enqueueOperation(op);
|
||||
return op;
|
||||
return registerOperation(new SystemRebootOperation(serialPort(), SystemRebootOperation::RebootType::Recovery, this));
|
||||
}
|
||||
|
||||
StatOperation *CommandInterface::stat(const QByteArray &fileName)
|
||||
SystemFactoryResetOperation *CommandInterface::factoryReset()
|
||||
{
|
||||
auto *op = new StatOperation(m_serialPort, fileName, this);
|
||||
enqueueOperation(op);
|
||||
return op;
|
||||
return registerOperation(new SystemFactoryResetOperation(serialPort(), this));
|
||||
}
|
||||
|
||||
ReadOperation *CommandInterface::read(const QByteArray &fileName, QIODevice *file)
|
||||
StorageListOperation *CommandInterface::storageList(const QByteArray &path)
|
||||
{
|
||||
auto *op = new ReadOperation(m_serialPort, fileName, file, this);
|
||||
enqueueOperation(op);
|
||||
return op;
|
||||
return registerOperation(new StorageListOperation(serialPort(), path, this));
|
||||
}
|
||||
|
||||
MkDirOperation *CommandInterface::mkdir(const QByteArray &dirName)
|
||||
StorageInfoOperation *CommandInterface::storageInfo(const QByteArray &path)
|
||||
{
|
||||
auto *op = new MkDirOperation(m_serialPort, dirName, this);
|
||||
enqueueOperation(op);
|
||||
return op;
|
||||
return registerOperation(new StorageInfoOperation(serialPort(), path, this));
|
||||
}
|
||||
|
||||
WriteOperation *CommandInterface::write(const QByteArray &fileName, QIODevice *file)
|
||||
StorageStatOperation *CommandInterface::storageStat(const QByteArray &path)
|
||||
{
|
||||
auto *op = new WriteOperation(m_serialPort, fileName, file, this);
|
||||
enqueueOperation(op);
|
||||
return op;
|
||||
return registerOperation(new StorageStatOperation(serialPort(), path, this));
|
||||
}
|
||||
|
||||
RemoveOperation *CommandInterface::remove(const QByteArray &fileName)
|
||||
StorageReadOperation *CommandInterface::storageRead(const QByteArray &path, QIODevice *file)
|
||||
{
|
||||
auto *op = new RemoveOperation(m_serialPort, fileName, this);
|
||||
enqueueOperation(op);
|
||||
return op;
|
||||
return registerOperation(new StorageReadOperation(serialPort(), path, file, this));
|
||||
}
|
||||
|
||||
bool CommandInterface::onQueueStarted()
|
||||
StorageMkdirOperation *CommandInterface::storageMkdir(const QByteArray &path)
|
||||
{
|
||||
const auto success = m_serialPort->open(QIODevice::ReadWrite);
|
||||
check_return_bool(success, QStringLiteral("Serial port error: %1").arg(m_serialPort->errorString()));
|
||||
|
||||
enqueueOperation(new SkipMOTDOperation(m_serialPort, this));
|
||||
return true;
|
||||
return registerOperation(new StorageMkdirOperation(serialPort(), path, this));
|
||||
}
|
||||
|
||||
bool CommandInterface::onQueueFinished()
|
||||
StorageWriteOperation *CommandInterface::storageWrite(const QByteArray &path, QIODevice *file)
|
||||
{
|
||||
m_serialPort->close();
|
||||
return true;
|
||||
return registerOperation(new StorageWriteOperation(serialPort(), path, file, this));
|
||||
}
|
||||
|
||||
GuiStartStreamOperation *CommandInterface::guiStartStreaming()
|
||||
{
|
||||
return registerOperation(new GuiStartStreamOperation(serialPort(), this));
|
||||
}
|
||||
|
||||
GuiStopStreamOperation *CommandInterface::guiStopStreaming()
|
||||
{
|
||||
return registerOperation(new GuiStopStreamOperation(serialPort(), this));
|
||||
}
|
||||
|
||||
StorageRemoveOperation *CommandInterface::storageRemove(const QByteArray &path)
|
||||
{
|
||||
return registerOperation(new StorageRemoveOperation(serialPort(), path, this));
|
||||
}
|
||||
|
||||
const QLoggingCategory &CommandInterface::loggingCategory() const
|
||||
{
|
||||
return CATEGORY_CLI();
|
||||
return CATEGORY_RPC();
|
||||
}
|
||||
|
|
|
@ -4,48 +4,58 @@
|
|||
|
||||
class QIODevice;
|
||||
class QSerialPort;
|
||||
class QSerialPortInfo;
|
||||
|
||||
namespace Flipper {
|
||||
namespace Zero {
|
||||
|
||||
class DeviceState;
|
||||
|
||||
class DFUOperation;
|
||||
class ListOperation;
|
||||
class StatOperation;
|
||||
class ReadOperation;
|
||||
class MkDirOperation;
|
||||
class WriteOperation;
|
||||
class RemoveOperation;
|
||||
class RebootOperation;
|
||||
class FactoryResetCliOperation;
|
||||
class StopRPCOperation;
|
||||
class StartRPCOperation;
|
||||
|
||||
class SystemRebootOperation;
|
||||
class SystemFactoryResetOperation;
|
||||
|
||||
class StorageListOperation;
|
||||
class StorageInfoOperation;
|
||||
class StorageStatOperation;
|
||||
class StorageReadOperation;
|
||||
class StorageMkdirOperation;
|
||||
class StorageWriteOperation;
|
||||
class StorageRemoveOperation;
|
||||
|
||||
class GuiStartStreamOperation;
|
||||
class GuiStopStreamOperation;
|
||||
|
||||
class CommandInterface : public AbstractOperationRunner
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
CommandInterface(DeviceState *state, QObject *parent = nullptr);
|
||||
CommandInterface(DeviceState *deviceState, QObject *parent = nullptr);
|
||||
QSerialPort *serialPort() const;
|
||||
|
||||
RebootOperation *reboot();
|
||||
DFUOperation *startRecoveryMode();
|
||||
FactoryResetCliOperation *factoryReset();
|
||||
StopRPCOperation *stopRPCSession();
|
||||
StartRPCOperation *startRPCSession();
|
||||
|
||||
ListOperation *list(const QByteArray &dirName);
|
||||
StatOperation *stat(const QByteArray &fileName);
|
||||
ReadOperation *read(const QByteArray &fileName, QIODevice *file);
|
||||
MkDirOperation *mkdir(const QByteArray &dirName);
|
||||
WriteOperation *write(const QByteArray &fileName, QIODevice *file);
|
||||
RemoveOperation *remove(const QByteArray &fileName);
|
||||
SystemRebootOperation *rebootToOS();
|
||||
SystemRebootOperation *rebootToRecovery();
|
||||
SystemFactoryResetOperation *factoryReset();
|
||||
|
||||
StorageListOperation *storageList(const QByteArray &path);
|
||||
StorageInfoOperation *storageInfo(const QByteArray &path);
|
||||
StorageStatOperation *storageStat(const QByteArray &path);
|
||||
StorageMkdirOperation *storageMkdir(const QByteArray &path);
|
||||
StorageRemoveOperation *storageRemove(const QByteArray &path);
|
||||
StorageReadOperation *storageRead(const QByteArray &path, QIODevice *file);
|
||||
StorageWriteOperation *storageWrite(const QByteArray &path, QIODevice *file);
|
||||
|
||||
GuiStartStreamOperation *guiStartStreaming();
|
||||
GuiStopStreamOperation *guiStopStreaming();
|
||||
|
||||
private:
|
||||
bool onQueueStarted() override;
|
||||
bool onQueueFinished() override;
|
||||
|
||||
const QLoggingCategory &loggingCategory() const override;
|
||||
|
||||
QSerialPort *m_serialPort;
|
||||
DeviceState *m_deviceState;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
|
||||
#include "usbdeviceinfo.h"
|
||||
|
||||
class QSerialPort;
|
||||
|
||||
namespace Flipper {
|
||||
namespace Zero {
|
||||
|
||||
|
@ -40,12 +42,14 @@ struct SoftwareInfo {
|
|||
Q_PROPERTY(QString version MEMBER version)
|
||||
Q_PROPERTY(QString commit MEMBER commit)
|
||||
Q_PROPERTY(QString branch MEMBER branch)
|
||||
Q_PROPERTY(QString channel MEMBER channel)
|
||||
Q_PROPERTY(QDate date MEMBER date)
|
||||
|
||||
public:
|
||||
QString version;
|
||||
QString commit;
|
||||
QString branch;
|
||||
QString channel;
|
||||
QDate date;
|
||||
|
||||
// Needed in order to work with QVariant
|
||||
|
@ -83,7 +87,6 @@ struct DeviceInfo {
|
|||
Q_PROPERTY(Flipper::Zero::StorageInfo storage MEMBER storage)
|
||||
|
||||
public:
|
||||
|
||||
QString name;
|
||||
QString model;
|
||||
|
||||
|
@ -98,7 +101,7 @@ public:
|
|||
QString systemLocation;
|
||||
|
||||
USBDeviceInfo usbInfo;
|
||||
QSerialPortInfo serialInfo;
|
||||
QSerialPortInfo portInfo;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -1,23 +1,25 @@
|
|||
#include "devicestate.h"
|
||||
|
||||
#include <QSerialPort>
|
||||
|
||||
#include "helper/serialinithelper.h"
|
||||
|
||||
using namespace Flipper;
|
||||
using namespace Zero;
|
||||
|
||||
DeviceState::DeviceState(const DeviceInfo &deviceInfo, QObject *parent):
|
||||
QObject(parent),
|
||||
m_deviceInfo(deviceInfo),
|
||||
m_serialPort(nullptr),
|
||||
m_isPersistent(false),
|
||||
m_isOnline(true),
|
||||
m_isOnline(false),
|
||||
m_isError(false),
|
||||
m_progress(-1.0)
|
||||
{}
|
||||
|
||||
void DeviceState::reset(const DeviceInfo &newDeviceInfo)
|
||||
{
|
||||
setDeviceInfo(newDeviceInfo);
|
||||
setError(false);
|
||||
setProgress(-1.0);
|
||||
setOnline(true);
|
||||
connect(this, &DeviceState::deviceInfoChanged, this, &DeviceState::onDeviceInfoChanged);
|
||||
connect(this, &DeviceState::isOnlineChanged, this, &DeviceState::onIsOnlineChanged);
|
||||
|
||||
onDeviceInfoChanged();
|
||||
}
|
||||
|
||||
const DeviceInfo &DeviceState::deviceInfo() const
|
||||
|
@ -73,7 +75,7 @@ void DeviceState::setError(bool set)
|
|||
}
|
||||
|
||||
m_isError = set;
|
||||
emit errorChanged();
|
||||
emit isErrorChanged();
|
||||
}
|
||||
|
||||
bool DeviceState::isRecoveryMode() const
|
||||
|
@ -108,7 +110,7 @@ void DeviceState::setStatusString(const QString &newStatusString)
|
|||
}
|
||||
|
||||
m_statusString = newStatusString;
|
||||
emit statusChanged();
|
||||
emit statusStringChanged();
|
||||
}
|
||||
|
||||
const QString &DeviceState::errorString() const
|
||||
|
@ -125,10 +127,47 @@ void DeviceState::setErrorString(const QString &newErrorString)
|
|||
m_errorString = newErrorString;
|
||||
m_isError = true;
|
||||
|
||||
emit errorChanged();
|
||||
emit isErrorChanged();
|
||||
}
|
||||
|
||||
const QString &DeviceState::name() const
|
||||
{
|
||||
return m_deviceInfo.name;
|
||||
}
|
||||
|
||||
QSerialPort *DeviceState::serialPort() const
|
||||
{
|
||||
return m_serialPort;
|
||||
}
|
||||
|
||||
void DeviceState::onDeviceInfoChanged()
|
||||
{
|
||||
setError(false);
|
||||
setProgress(-1.0);
|
||||
|
||||
if(isRecoveryMode()) {
|
||||
setOnline(true);
|
||||
return;
|
||||
}
|
||||
|
||||
auto *helper = new SerialInitHelper(m_deviceInfo.portInfo, this);
|
||||
connect(helper, &AbstractOperationHelper::finished, this, [=]() {
|
||||
if(helper->isError()) {
|
||||
setErrorString(tr("Failed to initialize serial port"));
|
||||
|
||||
} else {
|
||||
m_serialPort = helper->serialPort();
|
||||
setOnline(true);
|
||||
}
|
||||
|
||||
helper->deleteLater();
|
||||
});
|
||||
}
|
||||
|
||||
void DeviceState::onIsOnlineChanged()
|
||||
{
|
||||
if(!m_isOnline && m_serialPort) {
|
||||
m_serialPort->deleteLater();
|
||||
m_serialPort = nullptr;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,19 +15,17 @@ class DeviceState : public QObject
|
|||
|
||||
Q_PROPERTY(bool isPersistent READ isPersistent NOTIFY isPersistentChanged)
|
||||
Q_PROPERTY(bool isOnline READ isOnline NOTIFY isOnlineChanged)
|
||||
Q_PROPERTY(bool isError READ isError NOTIFY errorChanged)
|
||||
Q_PROPERTY(bool isError READ isError NOTIFY isErrorChanged)
|
||||
Q_PROPERTY(bool isRecoveryMode READ isRecoveryMode NOTIFY deviceInfoChanged)
|
||||
|
||||
Q_PROPERTY(QString statusString READ statusString NOTIFY statusChanged)
|
||||
Q_PROPERTY(QString errorString READ errorString NOTIFY errorChanged)
|
||||
Q_PROPERTY(QString statusString READ statusString NOTIFY statusStringChanged)
|
||||
Q_PROPERTY(QString errorString READ errorString NOTIFY isErrorChanged)
|
||||
|
||||
Q_PROPERTY(double progress READ progress NOTIFY progressChanged)
|
||||
|
||||
public:
|
||||
DeviceState(const DeviceInfo &deviceInfo, QObject *parent = nullptr);
|
||||
|
||||
void reset(const DeviceInfo &newDeviceInfo);
|
||||
|
||||
const DeviceInfo &deviceInfo() const;
|
||||
void setDeviceInfo(const DeviceInfo &newDeviceInfo);
|
||||
|
||||
|
@ -54,20 +52,25 @@ public:
|
|||
//TODO: Replace with deviceInfo().name
|
||||
const QString &name() const;
|
||||
|
||||
QSerialPort *serialPort() const;
|
||||
|
||||
signals:
|
||||
void deviceInfoChanged();
|
||||
void isPersistentChanged();
|
||||
void isOnlineChanged();
|
||||
|
||||
void updateInfoChanged();
|
||||
|
||||
void statusChanged();
|
||||
void errorChanged();
|
||||
void statusStringChanged();
|
||||
void isErrorChanged();
|
||||
|
||||
void progressChanged();
|
||||
|
||||
private slots:
|
||||
void onDeviceInfoChanged();
|
||||
void onIsOnlineChanged();
|
||||
|
||||
private:
|
||||
DeviceInfo m_deviceInfo;
|
||||
QSerialPort *m_serialPort;
|
||||
|
||||
bool m_isPersistent;
|
||||
bool m_isOnline;
|
||||
|
|
|
@ -1,177 +0,0 @@
|
|||
#include "firmwareupdater.h"
|
||||
|
||||
#include <QLoggingCategory>
|
||||
|
||||
#include "devicestate.h"
|
||||
#include "recoveryinterface.h"
|
||||
#include "utilityinterface.h"
|
||||
|
||||
#include "toplevel/wirelessstackupdateoperation.h"
|
||||
#include "toplevel/firmwareinstalloperation.h"
|
||||
#include "toplevel/settingsrestoreoperation.h"
|
||||
#include "toplevel/settingsbackupoperation.h"
|
||||
#include "toplevel/factoryresetoperation.h"
|
||||
#include "toplevel/fullrepairoperation.h"
|
||||
#include "toplevel/fullupdateoperation.h"
|
||||
|
||||
#include "preferences.h"
|
||||
|
||||
Q_LOGGING_CATEGORY(CATEGORY_TOPLEVEL, "TOPLEVEL")
|
||||
|
||||
using namespace Flipper;
|
||||
using namespace Zero;
|
||||
|
||||
FirmwareUpdater::FirmwareUpdater(DeviceState *state, QObject *parent):
|
||||
AbstractOperationRunner(parent),
|
||||
m_state(state),
|
||||
m_recovery(new RecoveryInterface(state, this)),
|
||||
m_utility(new UtilityInterface(state, this))
|
||||
{}
|
||||
|
||||
void FirmwareUpdater::fullUpdate(const Updates::VersionInfo &versionInfo)
|
||||
{
|
||||
if(!m_state->isRecoveryMode()) {
|
||||
enqueueOperation(new FullUpdateOperation(m_recovery, m_utility, m_state, versionInfo, this));
|
||||
}
|
||||
}
|
||||
|
||||
void FirmwareUpdater::fullRepair(const Updates::VersionInfo &versionInfo)
|
||||
{
|
||||
if(m_state->isRecoveryMode()) {
|
||||
enqueueOperation(new FullRepairOperation(m_recovery, m_utility, m_state, versionInfo, this));
|
||||
}
|
||||
}
|
||||
|
||||
void FirmwareUpdater::backupInternalStorage(const QUrl &directoryUrl)
|
||||
{
|
||||
if(!m_state->isRecoveryMode()) {
|
||||
enqueueOperation(new SettingsBackupOperation(m_utility, m_state, directoryUrl, this));
|
||||
}
|
||||
}
|
||||
|
||||
void FirmwareUpdater::restoreInternalStorage(const QUrl &directoryUrl)
|
||||
{
|
||||
if(!m_state->isRecoveryMode()) {
|
||||
enqueueOperation(new SettingsRestoreOperation(m_utility, m_state, directoryUrl, this));
|
||||
}
|
||||
}
|
||||
|
||||
void FirmwareUpdater::factoryReset()
|
||||
{
|
||||
if(!m_state->isRecoveryMode()) {
|
||||
enqueueOperation(new FactoryResetOperation(m_utility, m_state, this));
|
||||
}
|
||||
}
|
||||
|
||||
void FirmwareUpdater::localFirmwareInstall(const QUrl &fileUrl)
|
||||
{
|
||||
enqueueOperation(new FirmwareInstallOperation(m_recovery, m_utility, m_state, fileUrl.toLocalFile(), this));
|
||||
}
|
||||
|
||||
void FirmwareUpdater::localFUSUpdate(const QUrl &fileUrl)
|
||||
{
|
||||
//TODO: User-settable address
|
||||
enqueueOperation(new FUSUpdateOperation(m_recovery, m_utility, m_state, fileUrl.toLocalFile(), 0x080EC000, this));
|
||||
}
|
||||
|
||||
void FirmwareUpdater::localWirelessStackUpdate(const QUrl &fileUrl)
|
||||
{
|
||||
enqueueOperation(new WirelessStackUpdateOperation(m_recovery, m_utility, m_state, fileUrl.toLocalFile(), this));
|
||||
}
|
||||
|
||||
const QLoggingCategory &FirmwareUpdater::loggingCategory() const
|
||||
{
|
||||
return CATEGORY_TOPLEVEL();
|
||||
}
|
||||
|
||||
bool FirmwareUpdater::canUpdate(const Updates::VersionInfo &versionInfo) const
|
||||
{
|
||||
const auto &storageInfo = m_state->deviceInfo().storage;
|
||||
if(storageInfo.isExternalPresent && !storageInfo.isAssetsInstalled) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const auto &deviceChannel = branchToChannelName();
|
||||
const auto &deviceVersion = (deviceChannel == channelName(ChannelType::Development)) ?
|
||||
m_state->deviceInfo().firmware.commit :
|
||||
m_state->deviceInfo().firmware.version;
|
||||
|
||||
const auto &deviceDate = m_state->deviceInfo().firmware.date;
|
||||
|
||||
const auto &serverChannel = globalPrefs->firmwareUpdateChannel();
|
||||
const auto &serverVersion = versionInfo.number();
|
||||
const auto &serverDate = versionInfo.date();
|
||||
|
||||
if(deviceChannel == channelName(ChannelType::Release)) {
|
||||
if(serverChannel == channelName(ChannelType::Release)) {
|
||||
return deviceVersion < serverVersion;
|
||||
} else if(serverChannel == channelName(ChannelType::ReleaseCandidate)) {
|
||||
return deviceVersion < serverVersion.chopped(serverVersion.length() - deviceVersion.length());
|
||||
} else if(serverChannel == channelName(ChannelType::Development)) {
|
||||
return deviceDate <= serverDate;
|
||||
}
|
||||
|
||||
} else if(deviceChannel == channelName(ChannelType::ReleaseCandidate)) {
|
||||
if(serverChannel == channelName(ChannelType::Release)) {
|
||||
return deviceVersion.chopped(deviceVersion.length() - serverVersion.length()) <= serverVersion;
|
||||
} else if(serverChannel == channelName(ChannelType::ReleaseCandidate)) {
|
||||
return deviceVersion < serverVersion;
|
||||
} else if(serverChannel == channelName(ChannelType::Development)) {
|
||||
return deviceDate <= serverDate;
|
||||
}
|
||||
|
||||
} else if(deviceChannel == channelName(ChannelType::Development)) {
|
||||
if(serverChannel == channelName(ChannelType::Release)) {
|
||||
return deviceDate <= serverDate;
|
||||
} else if(serverChannel == channelName(ChannelType::ReleaseCandidate)) {
|
||||
return deviceDate <= serverDate;
|
||||
} else if(serverChannel == channelName(ChannelType::Development)) {
|
||||
return (deviceVersion != serverVersion) && (deviceDate <= serverDate);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool FirmwareUpdater::canInstall() const
|
||||
{
|
||||
const auto &deviceChannel = branchToChannelName();
|
||||
const auto &serverChannel = globalPrefs->firmwareUpdateChannel();
|
||||
|
||||
return deviceChannel != serverChannel;
|
||||
}
|
||||
|
||||
const QString &FirmwareUpdater::channelName(ChannelType channelType)
|
||||
{
|
||||
static const QStringList channelNames = {
|
||||
QStringLiteral("development"), QStringLiteral("release-candidate"), QStringLiteral("release")
|
||||
};
|
||||
|
||||
return channelNames[(int)channelType];
|
||||
}
|
||||
|
||||
const QString &FirmwareUpdater::branchToChannelName() const
|
||||
{
|
||||
const auto &branchName = m_state->deviceInfo().firmware.branch;
|
||||
|
||||
if(branchName == QStringLiteral("dev")) {
|
||||
return channelName(ChannelType::Development);
|
||||
} else if(branchName.contains(QStringLiteral("-rc"))) {
|
||||
return channelName(ChannelType::ReleaseCandidate);
|
||||
} else {
|
||||
return channelName(ChannelType::Release);
|
||||
}
|
||||
}
|
||||
|
||||
FirmwareUpdater::ChannelType FirmwareUpdater::branchToChannelType() const
|
||||
{
|
||||
const auto &branchName = m_state->deviceInfo().firmware.branch;
|
||||
|
||||
if(branchName == QStringLiteral("dev")) {
|
||||
return ChannelType::Development;
|
||||
} else if(branchName.contains(QStringLiteral("-rc"))) {
|
||||
return ChannelType::ReleaseCandidate;
|
||||
} else {
|
||||
return ChannelType::Release;
|
||||
}
|
||||
}
|
|
@ -1,55 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include "flipperupdates.h"
|
||||
#include "abstractoperationrunner.h"
|
||||
|
||||
namespace Flipper {
|
||||
namespace Zero {
|
||||
|
||||
class DeviceState;
|
||||
class RecoveryInterface;
|
||||
class UtilityInterface;
|
||||
|
||||
class FirmwareUpdater : public AbstractOperationRunner
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
enum class ChannelType {
|
||||
Development = 0,
|
||||
ReleaseCandidate,
|
||||
Release
|
||||
};
|
||||
|
||||
public:
|
||||
FirmwareUpdater(DeviceState *state, QObject *parent = nullptr);
|
||||
|
||||
public slots:
|
||||
bool canUpdate(const Flipper::Updates::VersionInfo &versionInfo) const;
|
||||
bool canInstall() const;
|
||||
|
||||
void fullUpdate(const Flipper::Updates::VersionInfo &versionInfo);
|
||||
void fullRepair(const Flipper::Updates::VersionInfo &versionInfo);
|
||||
|
||||
void backupInternalStorage(const QUrl &directoryUrl);
|
||||
void restoreInternalStorage(const QUrl &directoryUrl);
|
||||
void factoryReset();
|
||||
|
||||
void localFirmwareInstall(const QUrl &fileUrl);
|
||||
void localFUSUpdate(const QUrl &fileUrl);
|
||||
void localWirelessStackUpdate(const QUrl &fileUrl);
|
||||
|
||||
private:
|
||||
const QLoggingCategory &loggingCategory() const override;
|
||||
|
||||
static const QString &channelName(ChannelType channelType);
|
||||
const QString &branchToChannelName() const;
|
||||
ChannelType branchToChannelType() const;
|
||||
|
||||
DeviceState *m_state;
|
||||
RecoveryInterface *m_recovery;
|
||||
UtilityInterface *m_utility;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -1,10 +1,28 @@
|
|||
#include "flipperzero.h"
|
||||
|
||||
#include "preferences.h"
|
||||
#include "flipperupdates.h"
|
||||
|
||||
#include "devicestate.h"
|
||||
#include "firmwareupdater.h"
|
||||
#include "screenstreamer.h"
|
||||
|
||||
#include "debug.h"
|
||||
#include "commandinterface.h"
|
||||
#include "utilityinterface.h"
|
||||
#include "recoveryinterface.h"
|
||||
|
||||
#include "toplevel/wirelessstackupdateoperation.h"
|
||||
#include "toplevel/firmwareinstalloperation.h"
|
||||
#include "toplevel/settingsrestoreoperation.h"
|
||||
#include "toplevel/settingsbackupoperation.h"
|
||||
#include "toplevel/factoryresetoperation.h"
|
||||
#include "toplevel/fullrepairoperation.h"
|
||||
#include "toplevel/fullupdateoperation.h"
|
||||
|
||||
#include "preferences.h"
|
||||
|
||||
#define CHANNEL_DEVELOPMENT "development"
|
||||
#define CHANNEL_RELEASE_CANDIDATE "release-candidate"
|
||||
#define CHANNEL_RELEASE "release"
|
||||
|
||||
using namespace Flipper;
|
||||
using namespace Zero;
|
||||
|
@ -12,10 +30,16 @@ using namespace Zero;
|
|||
FlipperZero::FlipperZero(const Zero::DeviceInfo &info, QObject *parent):
|
||||
QObject(parent),
|
||||
m_state(new DeviceState(info, this)),
|
||||
m_updater(new FirmwareUpdater(m_state, this)),
|
||||
m_streamer(new ScreenStreamer(m_state, this))
|
||||
m_rpc(new CommandInterface(m_state, this)),
|
||||
m_recovery(new RecoveryInterface(m_state, this)),
|
||||
m_utility(new UtilityInterface(m_state, m_rpc, this)),
|
||||
m_streamer(new ScreenStreamer(m_rpc, this))
|
||||
{
|
||||
connect(m_updater, &SignalingFailable::errorOccured, this, &FlipperZero::onErrorOccured);
|
||||
connect(m_state, &DeviceState::isPersistentChanged, this, &FlipperZero::onStreamConditionChanged);
|
||||
connect(m_state, &DeviceState::isOnlineChanged, this, &FlipperZero::onStreamConditionChanged);
|
||||
|
||||
// Add other connections as necessary.
|
||||
connect(m_state, &DeviceState::deviceInfoChanged, this, &FlipperZero::stateChanged);
|
||||
}
|
||||
|
||||
FlipperZero::~FlipperZero()
|
||||
|
@ -23,6 +47,117 @@ FlipperZero::~FlipperZero()
|
|||
m_state->setOnline(false);
|
||||
}
|
||||
|
||||
bool FlipperZero::canUpdate(const Updates::VersionInfo &versionInfo) const
|
||||
{
|
||||
const auto &storageInfo = m_state->deviceInfo().storage;
|
||||
if(storageInfo.isExternalPresent && !storageInfo.isAssetsInstalled) {
|
||||
return true;
|
||||
}
|
||||
|
||||
static const auto DEVELOPMENT = QStringLiteral("development");
|
||||
static const auto RELEASE_CANDIDATE = QStringLiteral("release-candidate");
|
||||
static const auto RELEASE = QStringLiteral("release");
|
||||
|
||||
const auto &firmwareInfo = m_state->deviceInfo().firmware;
|
||||
|
||||
const auto &deviceChannel = firmwareInfo.channel;
|
||||
const auto &deviceVersion = (deviceChannel == QStringLiteral("development")) ?
|
||||
firmwareInfo.commit :
|
||||
firmwareInfo.version;
|
||||
|
||||
const auto &deviceDate = firmwareInfo.date;
|
||||
|
||||
const auto &serverChannel = globalPrefs->firmwareUpdateChannel();
|
||||
const auto &serverVersion = versionInfo.number();
|
||||
const auto &serverDate = versionInfo.date();
|
||||
|
||||
if(deviceChannel == RELEASE) {
|
||||
if(serverChannel == RELEASE) {
|
||||
return deviceVersion < serverVersion;
|
||||
} else if(serverChannel == RELEASE_CANDIDATE) {
|
||||
return deviceVersion < serverVersion.chopped(serverVersion.length() - deviceVersion.length());
|
||||
} else if(serverChannel == DEVELOPMENT) {
|
||||
return deviceDate <= serverDate;
|
||||
}
|
||||
|
||||
} else if(deviceChannel == RELEASE_CANDIDATE) {
|
||||
if(serverChannel == RELEASE) {
|
||||
return deviceVersion.chopped(deviceVersion.length() - serverVersion.length()) <= serverVersion;
|
||||
} else if(serverChannel == RELEASE_CANDIDATE) {
|
||||
return deviceVersion < serverVersion;
|
||||
} else if(serverChannel == DEVELOPMENT) {
|
||||
return deviceDate <= serverDate;
|
||||
}
|
||||
|
||||
} else if(deviceChannel == DEVELOPMENT) {
|
||||
if(serverChannel == RELEASE) {
|
||||
return deviceDate <= serverDate;
|
||||
} else if(serverChannel == RELEASE_CANDIDATE) {
|
||||
return deviceDate <= serverDate;
|
||||
} else if(serverChannel == DEVELOPMENT) {
|
||||
return (deviceVersion != serverVersion) && (deviceDate <= serverDate);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool FlipperZero::canInstall(const Updates::VersionInfo &versionInfo) const
|
||||
{
|
||||
Q_UNUSED(versionInfo)
|
||||
|
||||
const auto &deviceChannel = m_state->deviceInfo().firmware.channel;
|
||||
const auto &serverChannel = globalPrefs->firmwareUpdateChannel();
|
||||
|
||||
return deviceChannel != serverChannel;
|
||||
}
|
||||
|
||||
bool FlipperZero::canRepair(const Updates::VersionInfo &versionInfo) const
|
||||
{
|
||||
Q_UNUSED(versionInfo)
|
||||
return m_state->isRecoveryMode();
|
||||
}
|
||||
|
||||
void FlipperZero::fullUpdate(const Updates::VersionInfo &versionInfo)
|
||||
{
|
||||
registerOperation(new FullUpdateOperation(m_recovery, m_utility, m_state, versionInfo, this));
|
||||
}
|
||||
|
||||
void FlipperZero::fullRepair(const Updates::VersionInfo &versionInfo)
|
||||
{
|
||||
registerOperation(new FullRepairOperation(m_recovery, m_utility, m_state, versionInfo, this));
|
||||
}
|
||||
|
||||
void FlipperZero::createBackup(const QUrl &directoryUrl)
|
||||
{
|
||||
registerOperation(new SettingsBackupOperation(m_utility, m_state, directoryUrl, this));
|
||||
}
|
||||
|
||||
void FlipperZero::restoreBackup(const QUrl &directoryUrl)
|
||||
{
|
||||
registerOperation(new SettingsRestoreOperation(m_utility, m_state, directoryUrl, this));
|
||||
}
|
||||
|
||||
void FlipperZero::factoryReset()
|
||||
{
|
||||
registerOperation(new FactoryResetOperation(m_utility, m_state, this));
|
||||
}
|
||||
|
||||
void FlipperZero::installFirmware(const QUrl &fileUrl)
|
||||
{
|
||||
registerOperation(new FirmwareInstallOperation(m_recovery, m_utility, m_state, fileUrl.toLocalFile(), this));
|
||||
}
|
||||
|
||||
void FlipperZero::installWirelessStack(const QUrl &fileUrl)
|
||||
{
|
||||
registerOperation(new WirelessStackUpdateOperation(m_recovery, m_utility, m_state, fileUrl.toLocalFile(), this));
|
||||
}
|
||||
|
||||
void FlipperZero::installFUS(const QUrl &fileUrl, uint32_t address)
|
||||
{
|
||||
registerOperation(new FUSUpdateOperation(m_recovery, m_utility, m_state, fileUrl.toLocalFile(), address, this));
|
||||
}
|
||||
|
||||
DeviceState *FlipperZero::deviceState() const
|
||||
{
|
||||
return m_state;
|
||||
|
@ -33,13 +168,41 @@ Flipper::Zero::ScreenStreamer *FlipperZero::streamer() const
|
|||
return m_streamer;
|
||||
}
|
||||
|
||||
FirmwareUpdater *FlipperZero::updater() const
|
||||
void FlipperZero::onStreamConditionChanged()
|
||||
{
|
||||
return m_updater;
|
||||
// Automatically start screen streaming if the conditions are right:
|
||||
// 1. There is no error
|
||||
// 2. Device is online and connected in VCP mode
|
||||
// 3. There is no ongoing operation
|
||||
|
||||
const auto streamCondition = m_state->isOnline() &&
|
||||
!(m_state->isError() || m_state->isRecoveryMode() || m_state->isPersistent());
|
||||
|
||||
if(streamCondition) {
|
||||
m_streamer->start();
|
||||
}
|
||||
}
|
||||
|
||||
void FlipperZero::onErrorOccured()
|
||||
void FlipperZero::registerOperation(AbstractOperation *operation)
|
||||
{
|
||||
auto *instance = qobject_cast<SignalingFailable*>(sender());
|
||||
m_state->setErrorString(instance->errorString());
|
||||
connect(operation, &AbstractOperation::finished, this, [=]() {
|
||||
if(operation->isError()) {
|
||||
m_state->setErrorString(operation->errorString());
|
||||
}
|
||||
|
||||
operation->deleteLater();
|
||||
emit operationFinished();
|
||||
});
|
||||
|
||||
if(m_state->isRecoveryMode()) {
|
||||
operation->start();
|
||||
|
||||
} else {
|
||||
connect(m_streamer, &ScreenStreamer::stopped, operation, [=]() {
|
||||
//TODO: Check that ScreenStreamer has correctly stopped
|
||||
operation->start();
|
||||
});
|
||||
|
||||
m_streamer->stop();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,37 +2,64 @@
|
|||
|
||||
#include <QObject>
|
||||
|
||||
#include "deviceinfo.h"
|
||||
class AbstractOperation;
|
||||
|
||||
namespace Flipper {
|
||||
namespace Updates {
|
||||
class VersionInfo;
|
||||
}
|
||||
|
||||
namespace Zero {
|
||||
struct DeviceInfo;
|
||||
class DeviceState;
|
||||
class CommandInterface;
|
||||
class RecoveryInterface;
|
||||
class UtilityInterface;
|
||||
class ScreenStreamer;
|
||||
class FirmwareUpdater;
|
||||
}
|
||||
|
||||
class FlipperZero : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(Flipper::Zero::DeviceState* state READ deviceState CONSTANT)
|
||||
Q_PROPERTY(Flipper::Zero::FirmwareUpdater* updater READ updater CONSTANT)
|
||||
Q_PROPERTY(Flipper::Zero::ScreenStreamer* streamer READ streamer CONSTANT)
|
||||
|
||||
public:
|
||||
FlipperZero(const Zero::DeviceInfo &info, QObject *parent = nullptr);
|
||||
~FlipperZero();
|
||||
|
||||
bool canUpdate(const Flipper::Updates::VersionInfo &versionInfo) const;
|
||||
bool canInstall(const Flipper::Updates::VersionInfo &versionInfo) const;
|
||||
bool canRepair(const Flipper::Updates::VersionInfo &versionInfo) const;
|
||||
|
||||
void fullUpdate(const Flipper::Updates::VersionInfo &versionInfo);
|
||||
void fullRepair(const Flipper::Updates::VersionInfo &versionInfo);
|
||||
|
||||
void createBackup(const QUrl &directoryUrl);
|
||||
void restoreBackup(const QUrl &directoryUrl);
|
||||
void factoryReset();
|
||||
|
||||
void installFirmware(const QUrl &fileUrl);
|
||||
void installWirelessStack(const QUrl &fileUrl);
|
||||
void installFUS(const QUrl &fileUrl, uint32_t address);
|
||||
|
||||
Flipper::Zero::DeviceState *deviceState() const;
|
||||
Flipper::Zero::ScreenStreamer *streamer() const;
|
||||
Flipper::Zero::FirmwareUpdater *updater() const;
|
||||
|
||||
signals:
|
||||
void stateChanged();
|
||||
void operationFinished();
|
||||
|
||||
private slots:
|
||||
void onErrorOccured();
|
||||
void onStreamConditionChanged();
|
||||
|
||||
private:
|
||||
void registerOperation(AbstractOperation *operation);
|
||||
|
||||
Zero::DeviceState *m_state;
|
||||
Zero::FirmwareUpdater *m_updater;
|
||||
Zero::CommandInterface *m_rpc;
|
||||
Zero::RecoveryInterface *m_recovery;
|
||||
Zero::UtilityInterface *m_utility;
|
||||
Zero::ScreenStreamer *m_streamer;
|
||||
};
|
||||
|
||||
|
|
|
@ -2,18 +2,22 @@
|
|||
|
||||
#include <cmath>
|
||||
|
||||
#include <QDebug>
|
||||
#include <QTimer>
|
||||
#include <QSerialPort>
|
||||
|
||||
#include "flipperzero/cli/deviceinfooperation.h"
|
||||
#include "flipperzero/cli/skipmotdoperation.h"
|
||||
#include "flipperzero/cli/statoperation.h"
|
||||
#include "flipperzero/factoryinfo.h"
|
||||
|
||||
#include "flipperzero/cli/stoprpcoperation.h"
|
||||
|
||||
#include "flipperzero/cli/storagestatoperation.h"
|
||||
#include "flipperzero/cli/storageinfooperation.h"
|
||||
#include "flipperzero/cli/systemdeviceinfooperation.h"
|
||||
|
||||
#include "device/stm32wb55.h"
|
||||
|
||||
#include "serialinithelper.h"
|
||||
#include "serialfinder.h"
|
||||
#include "debug.h"
|
||||
|
||||
using namespace Flipper;
|
||||
using namespace Zero;
|
||||
|
@ -34,10 +38,14 @@ AbstractDeviceInfoHelper *AbstractDeviceInfoHelper::create(const USBDeviceInfo &
|
|||
} else if(pid == 0xdf11) {
|
||||
return new DFUDeviceInfoHelper(info, parent);
|
||||
} else {
|
||||
error_msg("Not a Flipper Zero device.")
|
||||
qCritical() << "Not a Flipper Zero device";
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
const DeviceInfo &AbstractDeviceInfoHelper::result() const
|
||||
{
|
||||
return m_deviceInfo;
|
||||
}
|
||||
|
||||
VCPDeviceInfoHelper::VCPDeviceInfoHelper(const USBDeviceInfo &info, QObject *parent):
|
||||
|
@ -47,11 +55,6 @@ VCPDeviceInfoHelper::VCPDeviceInfoHelper(const USBDeviceInfo &info, QObject *par
|
|||
m_deviceInfo.usbInfo = info;
|
||||
}
|
||||
|
||||
const DeviceInfo &VCPDeviceInfoHelper::result() const
|
||||
{
|
||||
return m_deviceInfo;
|
||||
}
|
||||
|
||||
void VCPDeviceInfoHelper::nextStateLogic()
|
||||
{
|
||||
if(state() == AbstractDeviceInfoHelper::Ready) {
|
||||
|
@ -59,10 +62,10 @@ void VCPDeviceInfoHelper::nextStateLogic()
|
|||
findSerialPort();
|
||||
|
||||
} else if(state() == VCPDeviceInfoHelper::FindingSerialPort) {
|
||||
setState(VCPDeviceInfoHelper::SkippingMOTD);
|
||||
skipMOTD();
|
||||
setState(VCPDeviceInfoHelper::InitializingSerialPort);
|
||||
initSerialPort();
|
||||
|
||||
} else if(state() == VCPDeviceInfoHelper::SkippingMOTD) {
|
||||
} else if(state() == VCPDeviceInfoHelper::InitializingSerialPort) {
|
||||
setState(VCPDeviceInfoHelper::FetchingDeviceInfo);
|
||||
fetchDeviceInfo();
|
||||
|
||||
|
@ -75,6 +78,10 @@ void VCPDeviceInfoHelper::nextStateLogic()
|
|||
checkManifest();
|
||||
|
||||
} else if(state() == VCPDeviceInfoHelper::CheckingManifest) {
|
||||
setState(VCPDeviceInfoHelper::StoppingRPCSession);
|
||||
stopRPCSession();
|
||||
|
||||
} else if(state() == VCPDeviceInfoHelper::StoppingRPCSession) {
|
||||
closePortAndFinish();
|
||||
}
|
||||
}
|
||||
|
@ -88,58 +95,81 @@ void VCPDeviceInfoHelper::findSerialPort()
|
|||
finishWithError(QStringLiteral("Invalid serial port info."));
|
||||
|
||||
} else {
|
||||
m_deviceInfo.serialInfo = portInfo;
|
||||
m_deviceInfo.portInfo = portInfo;
|
||||
m_deviceInfo.systemLocation = portInfo.systemLocation();
|
||||
|
||||
m_serialPort = new QSerialPort(portInfo, this);
|
||||
if(!m_serialPort->open(QIODevice::ReadWrite)) {
|
||||
finishWithError(m_serialPort->errorString());
|
||||
} else {
|
||||
advanceState();
|
||||
}
|
||||
advanceState();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void VCPDeviceInfoHelper::skipMOTD()
|
||||
void VCPDeviceInfoHelper::initSerialPort()
|
||||
{
|
||||
auto *operation = new SkipMOTDOperation(m_serialPort, this);
|
||||
auto *helper = new SerialInitHelper(m_deviceInfo.portInfo, this);
|
||||
|
||||
connect(operation, &AbstractOperation::finished, this, [=]() {
|
||||
if(operation->isError()) {
|
||||
finishWithError(operation->errorString());
|
||||
connect(helper, &AbstractOperationHelper::finished, this, [=]() {
|
||||
if(helper->isError()) {
|
||||
finishWithError(helper->errorString());
|
||||
} else {
|
||||
m_serialPort = helper->serialPort();
|
||||
advanceState();
|
||||
}
|
||||
});
|
||||
|
||||
operation->start();
|
||||
}
|
||||
|
||||
void VCPDeviceInfoHelper::fetchDeviceInfo()
|
||||
{
|
||||
auto *operation = new DeviceInfoOperation(m_serialPort, this);
|
||||
auto *operation = new SystemDeviceInfoOperation(m_serialPort, this);
|
||||
|
||||
connect(operation, &AbstractOperation::finished, this, [=]() {
|
||||
if(operation->isError()) {
|
||||
finishWithError(operation->errorString());
|
||||
|
||||
} else {
|
||||
// TODO: Is there a better way to do this?
|
||||
const auto &info = operation->result();
|
||||
m_deviceInfo.name = info.name;
|
||||
m_deviceInfo.bootloader = info.bootloader;
|
||||
m_deviceInfo.firmware = info.firmware;
|
||||
m_deviceInfo.hardware = info.hardware;
|
||||
m_deviceInfo.fusVersion = info.fusVersion;
|
||||
m_deviceInfo.radioVersion = info.radioVersion;
|
||||
|
||||
if(m_deviceInfo.name.isEmpty()) {
|
||||
finishWithError(QStringLiteral("Failed to read device factory information"));
|
||||
} else {
|
||||
advanceState();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
m_deviceInfo.name = operation->result(QByteArrayLiteral("hardware_name"));
|
||||
|
||||
m_deviceInfo.bootloader = {
|
||||
operation->result(QByteArrayLiteral("bootloader_version")),
|
||||
operation->result(QByteArrayLiteral("bootloader_commit")),
|
||||
operation->result(QByteArrayLiteral("bootloader_branch")),
|
||||
branchToChannelName(operation->result(QByteArrayLiteral("bootloader_branch"))),
|
||||
QDateTime::fromString(operation->result(QByteArrayLiteral("bootloader_build_date")), "dd-MM-yyyy").date()
|
||||
};
|
||||
|
||||
m_deviceInfo.firmware = {
|
||||
operation->result(QByteArrayLiteral("firmware_version")),
|
||||
operation->result(QByteArrayLiteral("firmware_commit")),
|
||||
operation->result(QByteArrayLiteral("firmware_branch")),
|
||||
branchToChannelName(operation->result(QByteArrayLiteral("firmware_branch"))),
|
||||
QDateTime::fromString(operation->result(QByteArrayLiteral("firmware_build_date")), "dd-MM-yyyy").date()
|
||||
};
|
||||
|
||||
m_deviceInfo.hardware = {
|
||||
operation->result(QByteArrayLiteral("hardware_ver")),
|
||||
QByteArrayLiteral("f") + operation->result(QByteArrayLiteral("hardware_target")),
|
||||
QByteArrayLiteral("b") + operation->result(QByteArrayLiteral("hardware_body")),
|
||||
QByteArrayLiteral("c") + operation->result(QByteArrayLiteral("hardware_connect")),
|
||||
(HardwareInfo::Color)operation->result(QByteArrayLiteral("hardware_color")).toInt(),
|
||||
};
|
||||
|
||||
m_deviceInfo.fusVersion = QStringLiteral("%1.%2.%3").arg(
|
||||
operation->result(QByteArrayLiteral("radio_fus_major")),
|
||||
operation->result(QByteArrayLiteral("radio_fus_minor")),
|
||||
operation->result(QByteArrayLiteral("radio_fus_sub")));
|
||||
|
||||
m_deviceInfo.radioVersion = QStringLiteral("%1.%2.%3").arg(
|
||||
operation->result(QByteArrayLiteral("radio_stack_major")),
|
||||
operation->result(QByteArrayLiteral("radio_stack_minor")),
|
||||
operation->result(QByteArrayLiteral("radio_stack_sub")));
|
||||
|
||||
if(m_deviceInfo.name.isEmpty()) {
|
||||
finishWithError(QStringLiteral("Failed to read device information"));
|
||||
} else {
|
||||
advanceState();
|
||||
}
|
||||
|
||||
operation->deleteLater();
|
||||
});
|
||||
|
||||
operation->start();
|
||||
|
@ -147,22 +177,27 @@ void VCPDeviceInfoHelper::fetchDeviceInfo()
|
|||
|
||||
void VCPDeviceInfoHelper::checkSDCard()
|
||||
{
|
||||
auto *operation = new StatOperation(m_serialPort, QByteArrayLiteral("/ext"), this);
|
||||
auto *operation = new StorageInfoOperation(m_serialPort, QByteArrayLiteral("/ext"), this);
|
||||
|
||||
connect(operation, &AbstractOperation::finished, this, [=]() {
|
||||
if(operation->isError()) {
|
||||
finishWithError(operation->errorString());
|
||||
|
||||
} else if(operation->type() != StatOperation::Type::Storage) {
|
||||
} else if(!operation->isPresent()) {
|
||||
m_deviceInfo.storage.isExternalPresent = false;
|
||||
m_deviceInfo.storage.isAssetsInstalled = false;
|
||||
closePortAndFinish();
|
||||
|
||||
setState(VCPDeviceInfoHelper::CheckingManifest);
|
||||
advanceState();
|
||||
|
||||
} else {
|
||||
m_deviceInfo.storage.isExternalPresent = true;
|
||||
m_deviceInfo.storage.externalFree = floor((double)operation->sizeFree() * 100.0 / (double)operation->size());
|
||||
m_deviceInfo.storage.externalFree = floor((double)operation->sizeFree() * 100.0 /
|
||||
(double)operation->sizeTotal());
|
||||
advanceState();
|
||||
}
|
||||
|
||||
operation->deleteLater();
|
||||
});
|
||||
|
||||
operation->start();
|
||||
|
@ -170,16 +205,34 @@ void VCPDeviceInfoHelper::checkSDCard()
|
|||
|
||||
void VCPDeviceInfoHelper::checkManifest()
|
||||
{
|
||||
auto *operation = new StatOperation(m_serialPort, QByteArrayLiteral("/ext/Manifest"), this);
|
||||
auto *operation = new StorageStatOperation(m_serialPort, QByteArrayLiteral("/ext/Manifest"), this);
|
||||
|
||||
connect(operation, &AbstractOperation::finished, this, [=]() {
|
||||
if(operation->isError()) {
|
||||
finishWithError(operation->errorString());
|
||||
|
||||
} else {
|
||||
m_deviceInfo.storage.isAssetsInstalled = (operation->type() == StatOperation::Type::RegularFile);
|
||||
m_deviceInfo.storage.isAssetsInstalled = operation->isPresent() && (operation->type() == StorageStatOperation::Type::RegularFile);
|
||||
advanceState();
|
||||
}
|
||||
|
||||
operation->deleteLater();
|
||||
});
|
||||
|
||||
operation->start();
|
||||
}
|
||||
|
||||
void VCPDeviceInfoHelper::stopRPCSession()
|
||||
{
|
||||
auto *operation = new StopRPCOperation(m_serialPort, this);
|
||||
connect(operation, &AbstractOperation::finished, this, [=]() {
|
||||
if(operation->isError()) {
|
||||
finishWithError(operation->errorString());
|
||||
} else {
|
||||
advanceState();
|
||||
}
|
||||
|
||||
operation->deleteLater();
|
||||
});
|
||||
|
||||
operation->start();
|
||||
|
@ -191,6 +244,21 @@ void VCPDeviceInfoHelper::closePortAndFinish()
|
|||
finish();
|
||||
}
|
||||
|
||||
const QString &VCPDeviceInfoHelper::branchToChannelName(const QByteArray &branchName)
|
||||
{
|
||||
static const auto DEVELOPMENT = QStringLiteral("development");
|
||||
static const auto RELEASE_CANDIDATE = QStringLiteral("release-candidate");
|
||||
static const auto RELEASE = QStringLiteral("release");
|
||||
|
||||
if(branchName == QByteArrayLiteral("dev")) {
|
||||
return DEVELOPMENT;
|
||||
} else if(branchName.contains(QByteArrayLiteral("-rc"))) {
|
||||
return RELEASE_CANDIDATE;
|
||||
} else {
|
||||
return RELEASE;
|
||||
}
|
||||
}
|
||||
|
||||
using namespace STM32;
|
||||
|
||||
DFUDeviceInfoHelper::DFUDeviceInfoHelper(const USBDeviceInfo &info, QObject *parent):
|
||||
|
@ -202,11 +270,6 @@ DFUDeviceInfoHelper::DFUDeviceInfoHelper(const USBDeviceInfo &info, QObject *par
|
|||
m_deviceInfo.storage.isAssetsInstalled = false;
|
||||
}
|
||||
|
||||
const DeviceInfo &DFUDeviceInfoHelper::result() const
|
||||
{
|
||||
return m_deviceInfo;
|
||||
}
|
||||
|
||||
void DFUDeviceInfoHelper::nextStateLogic()
|
||||
{
|
||||
STM32WB55 device(m_deviceInfo.usbInfo);
|
||||
|
|
|
@ -21,7 +21,10 @@ public:
|
|||
virtual ~AbstractDeviceInfoHelper();
|
||||
|
||||
static AbstractDeviceInfoHelper *create(const USBDeviceInfo &info, QObject *parent = nullptr);
|
||||
virtual const DeviceInfo &result() const = 0;
|
||||
const DeviceInfo &result() const;
|
||||
|
||||
protected:
|
||||
DeviceInfo m_deviceInfo;
|
||||
};
|
||||
|
||||
class VCPDeviceInfoHelper : public AbstractDeviceInfoHelper
|
||||
|
@ -30,28 +33,31 @@ class VCPDeviceInfoHelper : public AbstractDeviceInfoHelper
|
|||
|
||||
enum OperationState {
|
||||
FindingSerialPort = AbstractOperationHelper::User,
|
||||
SkippingMOTD,
|
||||
InitializingSerialPort,
|
||||
FetchingDeviceInfo,
|
||||
CheckingSDCard,
|
||||
CheckingManifest
|
||||
CheckingManifest,
|
||||
StoppingRPCSession
|
||||
};
|
||||
|
||||
public:
|
||||
VCPDeviceInfoHelper(const USBDeviceInfo &info, QObject *parent = nullptr);
|
||||
const DeviceInfo &result() const override;
|
||||
|
||||
private:
|
||||
void nextStateLogic() override;
|
||||
|
||||
void findSerialPort();
|
||||
void skipMOTD();
|
||||
void initSerialPort();
|
||||
void fetchDeviceInfo();
|
||||
void checkSDCard();
|
||||
void checkManifest();
|
||||
void stopRPCSession();
|
||||
void closePortAndFinish();
|
||||
|
||||
private:
|
||||
static const QString &branchToChannelName(const QByteArray &branchName);
|
||||
|
||||
QSerialPort *m_serialPort;
|
||||
DeviceInfo m_deviceInfo;
|
||||
};
|
||||
|
||||
class DFUDeviceInfoHelper : public AbstractDeviceInfoHelper
|
||||
|
@ -60,12 +66,9 @@ class DFUDeviceInfoHelper : public AbstractDeviceInfoHelper
|
|||
|
||||
public:
|
||||
DFUDeviceInfoHelper(const USBDeviceInfo &info, QObject *parent = nullptr);
|
||||
const DeviceInfo &result() const override;
|
||||
|
||||
private:
|
||||
void nextStateLogic() override;
|
||||
|
||||
DeviceInfo m_deviceInfo;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,80 @@
|
|||
#include "serialinithelper.h"
|
||||
|
||||
#include <QSerialPort>
|
||||
|
||||
#include "flipperzero/cli/skipmotdoperation.h"
|
||||
#include "flipperzero/cli/startrpcoperation.h"
|
||||
|
||||
using namespace Flipper;
|
||||
using namespace Zero;
|
||||
|
||||
SerialInitHelper::SerialInitHelper(const QSerialPortInfo &portInfo, QObject *parent):
|
||||
AbstractOperationHelper(parent),
|
||||
m_serialPort(new QSerialPort(portInfo, parent))
|
||||
{}
|
||||
|
||||
QSerialPort *SerialInitHelper::serialPort() const
|
||||
{
|
||||
return m_serialPort;
|
||||
}
|
||||
|
||||
void SerialInitHelper::nextStateLogic()
|
||||
{
|
||||
if(state() == AbstractOperationHelper::Ready) {
|
||||
setState(SerialInitHelper::OpeningPort);
|
||||
openPort();
|
||||
|
||||
} else if(state() == SerialInitHelper::OpeningPort) {
|
||||
setState(SerialInitHelper::SkippingMOTD);
|
||||
skipMOTD();
|
||||
|
||||
} else if(state() == SerialInitHelper::SkippingMOTD) {
|
||||
setState(SerialInitHelper::StartingRPCSession);
|
||||
startRPCSession();
|
||||
|
||||
} else if(state() == SerialInitHelper::StartingRPCSession) {
|
||||
finish();
|
||||
}
|
||||
}
|
||||
|
||||
void SerialInitHelper::openPort()
|
||||
{
|
||||
if(!m_serialPort->open(QIODevice::ReadWrite)) {
|
||||
finishWithError(QStringLiteral("Failed to open serial port: %1").arg(m_serialPort->errorString()));
|
||||
} else {
|
||||
advanceState();
|
||||
}
|
||||
}
|
||||
|
||||
void SerialInitHelper::skipMOTD()
|
||||
{
|
||||
auto *operation = new SkipMOTDOperation(m_serialPort, this);
|
||||
|
||||
connect(operation, &AbstractOperation::finished, this, [=]() {
|
||||
if(operation->isError()) {
|
||||
finishWithError(operation->errorString());
|
||||
} else {
|
||||
advanceState();
|
||||
}
|
||||
|
||||
operation->deleteLater();
|
||||
});
|
||||
|
||||
operation->start();
|
||||
}
|
||||
|
||||
void SerialInitHelper::startRPCSession()
|
||||
{
|
||||
auto *operation = new StartRPCOperation(m_serialPort, this);
|
||||
connect(operation, &AbstractOperation::finished, this, [=]() {
|
||||
if(operation->isError()) {
|
||||
finishWithError(operation->errorString());
|
||||
} else {
|
||||
advanceState();
|
||||
}
|
||||
|
||||
operation->deleteLater();
|
||||
});
|
||||
|
||||
operation->start();
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
#pragma once
|
||||
|
||||
#include "abstractoperationhelper.h"
|
||||
|
||||
#include <QSerialPortInfo>
|
||||
|
||||
namespace Flipper {
|
||||
namespace Zero {
|
||||
|
||||
class SerialInitHelper : public AbstractOperationHelper
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
enum State {
|
||||
OpeningPort = AbstractOperationHelper::User,
|
||||
SkippingMOTD,
|
||||
StartingRPCSession
|
||||
};
|
||||
|
||||
public:
|
||||
SerialInitHelper(const QSerialPortInfo &portInfo, QObject *parent = nullptr);
|
||||
QSerialPort *serialPort() const;
|
||||
|
||||
private:
|
||||
void nextStateLogic() override;
|
||||
|
||||
void openPort();
|
||||
void skipMOTD();
|
||||
void startRPCSession();
|
||||
|
||||
QSerialPort *m_serialPort;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,81 @@
|
|||
#include "toplevelhelper.h"
|
||||
|
||||
#include "updateregistry.h"
|
||||
|
||||
#include "flipperzero/flipperzero.h"
|
||||
#include "flipperzero/devicestate.h"
|
||||
|
||||
using namespace Flipper;
|
||||
using namespace Zero;
|
||||
|
||||
AbstractTopLevelHelper::AbstractTopLevelHelper(UpdateRegistry *updateRegistry, FlipperZero *device, QObject *parent):
|
||||
AbstractOperationHelper(parent),
|
||||
m_updateRegistry(updateRegistry),
|
||||
m_device(device)
|
||||
{
|
||||
connect(m_device, &FlipperZero::operationFinished, this, &AbstractTopLevelHelper::finish);
|
||||
}
|
||||
|
||||
UpdateRegistry *AbstractTopLevelHelper::updateRegistry()
|
||||
{
|
||||
return m_updateRegistry;
|
||||
}
|
||||
|
||||
FlipperZero *AbstractTopLevelHelper::device()
|
||||
{
|
||||
return m_device;
|
||||
}
|
||||
|
||||
void AbstractTopLevelHelper::onUpdatesChecked()
|
||||
{
|
||||
disconnect(m_updateRegistry, &UpdateRegistry::channelsChanged, this, &AbstractTopLevelHelper::onUpdatesChecked);
|
||||
|
||||
if(!m_updateRegistry->isReady()) {
|
||||
finishWithError(QStringLiteral("Failed to retreive update information"));
|
||||
} else{
|
||||
advanceState();
|
||||
}
|
||||
}
|
||||
|
||||
void AbstractTopLevelHelper::nextStateLogic()
|
||||
{
|
||||
if(state() == AbstractOperationHelper::Ready) {
|
||||
setState(AbstractTopLevelHelper::CheckingForUpdates);
|
||||
checkForUpdates();
|
||||
|
||||
} else if(state() == AbstractTopLevelHelper::CheckingForUpdates) {
|
||||
setState(AbstractTopLevelHelper::RunningCustomOperation);
|
||||
runCustomOperation();
|
||||
|
||||
} else if(state() == AbstractTopLevelHelper::RunningCustomOperation) {
|
||||
finish();
|
||||
}
|
||||
}
|
||||
|
||||
void AbstractTopLevelHelper::checkForUpdates()
|
||||
{
|
||||
m_device->deviceState()->setStatusString(tr("Checking for updates..."));
|
||||
|
||||
connect(m_updateRegistry, &UpdateRegistry::channelsChanged, this, &AbstractTopLevelHelper::onUpdatesChecked);
|
||||
m_updateRegistry->check();
|
||||
}
|
||||
|
||||
UpdateTopLevelHelper::UpdateTopLevelHelper(UpdateRegistry *updateRegistry, FlipperZero *device, QObject *parent):
|
||||
AbstractTopLevelHelper(updateRegistry, device, parent)
|
||||
{}
|
||||
|
||||
void UpdateTopLevelHelper::runCustomOperation()
|
||||
{
|
||||
auto &versionInfo = updateRegistry()->latestVersion();
|
||||
device()->fullUpdate(versionInfo);
|
||||
}
|
||||
|
||||
RepairTopLevelHelper::RepairTopLevelHelper(UpdateRegistry *updateRegistry, FlipperZero *device, QObject *parent):
|
||||
AbstractTopLevelHelper(updateRegistry, device, parent)
|
||||
{}
|
||||
|
||||
void RepairTopLevelHelper::runCustomOperation()
|
||||
{
|
||||
auto &versionInfo = updateRegistry()->latestVersion();
|
||||
device()->fullRepair(versionInfo);
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
#pragma once
|
||||
|
||||
#include "abstractoperationhelper.h"
|
||||
#include "flipperupdates.h"
|
||||
|
||||
namespace Flipper {
|
||||
|
||||
class UpdateRegistry;
|
||||
class FlipperZero;
|
||||
|
||||
namespace Zero {
|
||||
|
||||
class AbstractTopLevelHelper : public AbstractOperationHelper
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
enum State {
|
||||
CheckingForUpdates = AbstractOperationHelper::User,
|
||||
RunningCustomOperation,
|
||||
};
|
||||
|
||||
AbstractTopLevelHelper(UpdateRegistry *updateRegistry, FlipperZero *device, QObject *parent = nullptr);
|
||||
virtual ~AbstractTopLevelHelper() {}
|
||||
|
||||
protected:
|
||||
UpdateRegistry *updateRegistry();
|
||||
FlipperZero *device();
|
||||
|
||||
private slots:
|
||||
void onUpdatesChecked();
|
||||
|
||||
private:
|
||||
void nextStateLogic() override;
|
||||
void checkForUpdates();
|
||||
|
||||
virtual void runCustomOperation() = 0;
|
||||
|
||||
UpdateRegistry *m_updateRegistry;
|
||||
FlipperZero *m_device;
|
||||
};
|
||||
|
||||
class UpdateTopLevelHelper : public AbstractTopLevelHelper
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
UpdateTopLevelHelper(UpdateRegistry *updateRegistry, FlipperZero *device, QObject *parent = nullptr);
|
||||
|
||||
private:
|
||||
void runCustomOperation() override;
|
||||
};
|
||||
|
||||
class RepairTopLevelHelper : public AbstractTopLevelHelper
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
RepairTopLevelHelper(UpdateRegistry *updateRegistry, FlipperZero *device, QObject *parent = nullptr);
|
||||
|
||||
private:
|
||||
void runCustomOperation() override;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
#include "guiprotobufmessage.h"
|
||||
|
||||
using namespace Flipper;
|
||||
using namespace Zero;
|
||||
|
||||
GuiStartScreenStreamRequest::GuiStartScreenStreamRequest(QSerialPort *serialPort):
|
||||
AbstractMainProtobufRequest(serialPort)
|
||||
{
|
||||
pbMessage()->content.gui_start_screen_stream_request = PB_Gui_StartScreenStreamRequest_init_default;
|
||||
}
|
||||
|
||||
GuiStopScreenStreamRequest::GuiStopScreenStreamRequest(QSerialPort *serialPort):
|
||||
AbstractMainProtobufRequest(serialPort)
|
||||
{
|
||||
pbMessage()->content.gui_start_screen_stream_request = PB_Gui_StopScreenStreamRequest_init_default;
|
||||
}
|
||||
|
||||
GuiScreenFrameResponse::GuiScreenFrameResponse(QSerialPort *serialPort):
|
||||
AbstractMainProtobufResponse(serialPort)
|
||||
{}
|
||||
|
||||
const QByteArray GuiScreenFrameResponse::screenFrame() const
|
||||
{
|
||||
const auto *data = pbMessage()->content.gui_screen_frame.data;
|
||||
return QByteArray((const char*)data->bytes, data->size);
|
||||
}
|
||||
|
||||
GuiSendInputRequest::GuiSendInputRequest(QSerialPort *serialPort, PB_Gui_InputKey key, PB_Gui_InputType type):
|
||||
AbstractMainProtobufRequest(serialPort)
|
||||
{
|
||||
pbMessage()->content.gui_send_input_event_request.key = key;
|
||||
pbMessage()->content.gui_send_input_event_request.type = type;
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
#pragma once
|
||||
|
||||
#include "mainprotobufmessage.h"
|
||||
|
||||
namespace Flipper {
|
||||
namespace Zero {
|
||||
|
||||
class GuiStartScreenStreamRequest:
|
||||
public AbstractMainProtobufRequest<PB_Main_gui_start_screen_stream_request_tag>
|
||||
{
|
||||
public:
|
||||
GuiStartScreenStreamRequest(QSerialPort *serialPort);
|
||||
};
|
||||
|
||||
class GuiStopScreenStreamRequest:
|
||||
public AbstractMainProtobufRequest<PB_Main_gui_stop_screen_stream_request_tag>
|
||||
{
|
||||
public:
|
||||
GuiStopScreenStreamRequest(QSerialPort *serialPort);
|
||||
};
|
||||
|
||||
class GuiScreenFrameResponse:
|
||||
public AbstractMainProtobufResponse<PB_Main_gui_screen_frame_tag>
|
||||
{
|
||||
public:
|
||||
GuiScreenFrameResponse(QSerialPort *serialPort);
|
||||
const QByteArray screenFrame() const;
|
||||
};
|
||||
|
||||
class GuiSendInputRequest:
|
||||
public AbstractMainProtobufRequest<PB_Main_gui_send_input_event_request_tag>
|
||||
{
|
||||
public:
|
||||
GuiSendInputRequest(QSerialPort *serialPort, PB_Gui_InputKey key, PB_Gui_InputType type);
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
#include "mainprotobufmessage.h"
|
||||
|
||||
using namespace Flipper;
|
||||
using namespace Zero;
|
||||
|
||||
MainEmptyResponse::MainEmptyResponse(QSerialPort *serialPort):
|
||||
AbstractMainProtobufResponse(serialPort)
|
||||
{}
|
||||
|
||||
MainStopSessionRequest::MainStopSessionRequest(QSerialPort *serialPort):
|
||||
AbstractMainProtobufRequest(serialPort)
|
||||
{
|
||||
pbMessage()->content.stop_session = PB_StopSession_init_default;
|
||||
}
|
|
@ -0,0 +1,132 @@
|
|||
#pragma once
|
||||
|
||||
#include <QHash>
|
||||
|
||||
#include "abstractprotobufmessage.h"
|
||||
#include "messages/flipper.pb.h"
|
||||
|
||||
namespace Flipper {
|
||||
namespace Zero {
|
||||
|
||||
template<const pb_size_t Tag>
|
||||
class AbstractMainProtobufRequest : public AbstractProtobufRequest<&PB_Main_msg, PB_Main>
|
||||
{
|
||||
public:
|
||||
AbstractMainProtobufRequest(QSerialPort *serialPort, bool hasNext = false);
|
||||
};
|
||||
|
||||
template<const pb_size_t Tag>
|
||||
class AbstractMainProtobufResponse : public AbstractProtobufResponse<&PB_Main_msg, PB_Main>
|
||||
{
|
||||
public:
|
||||
AbstractMainProtobufResponse(QSerialPort *serialPort):
|
||||
AbstractProtobufResponse<&PB_Main_msg, PB_Main>(serialPort)
|
||||
{}
|
||||
|
||||
virtual ~AbstractMainProtobufResponse() {}
|
||||
|
||||
bool hasNext() const;
|
||||
PB_CommandStatus commandStatus() const;
|
||||
const QString commandStatusString() const;
|
||||
quint32 whichContent() const;
|
||||
|
||||
bool isOk() const;
|
||||
bool isValidType() const;
|
||||
|
||||
static pb_size_t tag();
|
||||
};
|
||||
|
||||
template<const pb_size_t Tag>
|
||||
AbstractMainProtobufRequest<Tag>::AbstractMainProtobufRequest(QSerialPort *serialPort, bool hasNext):
|
||||
AbstractProtobufRequest<&PB_Main_msg, PB_Main>(serialPort)
|
||||
{
|
||||
pbMessage()->which_content = Tag;
|
||||
pbMessage()->has_next = hasNext;
|
||||
}
|
||||
|
||||
|
||||
template<const pb_size_t Tag>
|
||||
bool AbstractMainProtobufResponse<Tag>::hasNext() const
|
||||
{
|
||||
return pbMessage()->has_next;
|
||||
}
|
||||
|
||||
template<const pb_size_t Tag>
|
||||
PB_CommandStatus AbstractMainProtobufResponse<Tag>::commandStatus() const
|
||||
{
|
||||
return pbMessage()->command_status;
|
||||
}
|
||||
|
||||
template<const pb_size_t Tag>
|
||||
const QString AbstractMainProtobufResponse<Tag>::commandStatusString() const
|
||||
{
|
||||
static const QHash<PB_CommandStatus, QString> statusStrings = {
|
||||
{PB_CommandStatus_OK, QStringLiteral("No error")},
|
||||
// Common errors
|
||||
{PB_CommandStatus_ERROR, QStringLiteral("Unknown")},
|
||||
{PB_CommandStatus_ERROR_DECODE, QStringLiteral("Decode failure")},
|
||||
{PB_CommandStatus_ERROR_NOT_IMPLEMENTED, QStringLiteral("Commant not implemented")},
|
||||
{PB_CommandStatus_ERROR_BUSY, QStringLiteral("Device is busy")},
|
||||
{PB_CommandStatus_ERROR_CONTINUOUS_COMMAND_INTERRUPTED, QStringLiteral("Continuous command interrupted")},
|
||||
{PB_CommandStatus_ERROR_INVALID_PARAMETERS, QStringLiteral("Invalid parameters")},
|
||||
// Storage errors
|
||||
{PB_CommandStatus_ERROR_STORAGE_NOT_READY, QStringLiteral("Storage not ready")},
|
||||
{PB_CommandStatus_ERROR_STORAGE_EXIST, QStringLiteral("File/directory already exists")},
|
||||
{PB_CommandStatus_ERROR_STORAGE_NOT_EXIST, QStringLiteral("File/directory does not exist")},
|
||||
{PB_CommandStatus_ERROR_STORAGE_INVALID_PARAMETER, QStringLiteral("Invalid storage API parameter")},
|
||||
{PB_CommandStatus_ERROR_STORAGE_DENIED, QStringLiteral("Access denied")},
|
||||
{PB_CommandStatus_ERROR_STORAGE_INVALID_NAME, QStringLiteral("Invalid name/path")},
|
||||
{PB_CommandStatus_ERROR_STORAGE_INTERNAL, QStringLiteral("Internal error")},
|
||||
{PB_CommandStatus_ERROR_STORAGE_NOT_IMPLEMENTED, QStringLiteral("Storage command not implemented")},
|
||||
{PB_CommandStatus_ERROR_STORAGE_ALREADY_OPEN, QStringLiteral("File/directory is already open")},
|
||||
{PB_CommandStatus_ERROR_STORAGE_DIR_NOT_EMPTY, QStringLiteral("Directory is not empty")},
|
||||
// Application errors
|
||||
{PB_CommandStatus_ERROR_APP_CANT_START, QStringLiteral("Cannot start the application")},
|
||||
{PB_CommandStatus_ERROR_APP_SYSTEM_LOCKED, QStringLiteral("Another application is already running")},
|
||||
// Virtual display errors
|
||||
{PB_CommandStatus_ERROR_VIRTUAL_DISPLAY_ALREADY_STARTED, QStringLiteral("Virtual display session has already been started")},
|
||||
{PB_CommandStatus_ERROR_VIRTUAL_DISPLAY_NOT_STARTED, QStringLiteral("No virtual display session running")}
|
||||
};
|
||||
|
||||
return statusStrings[commandStatus()];
|
||||
}
|
||||
|
||||
template<const pb_size_t Tag>
|
||||
quint32 AbstractMainProtobufResponse<Tag>::whichContent() const
|
||||
{
|
||||
return pbMessage()->which_content;
|
||||
}
|
||||
|
||||
template<const pb_size_t Tag>
|
||||
bool AbstractMainProtobufResponse<Tag>::isOk() const
|
||||
{
|
||||
return commandStatus() == PB_CommandStatus_OK;
|
||||
}
|
||||
|
||||
template<const pb_size_t Tag>
|
||||
bool AbstractMainProtobufResponse<Tag>::isValidType() const
|
||||
{
|
||||
return pbMessage()->which_content == Tag;
|
||||
}
|
||||
|
||||
template<const pb_size_t Tag>
|
||||
pb_size_t AbstractMainProtobufResponse<Tag>::tag()
|
||||
{
|
||||
return Tag;
|
||||
}
|
||||
|
||||
class MainStopSessionRequest : public AbstractMainProtobufRequest<PB_Main_stop_session_tag>
|
||||
{
|
||||
public:
|
||||
MainStopSessionRequest(QSerialPort *serialPort);
|
||||
};
|
||||
|
||||
class MainEmptyResponse : public AbstractMainProtobufResponse<PB_Main_empty_tag>
|
||||
{
|
||||
public:
|
||||
MainEmptyResponse(QSerialPort *serialPort);
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
/* Automatically generated nanopb constant definitions */
|
||||
/* Generated by nanopb-0.4.6-dev */
|
||||
|
||||
#include "application.pb.h"
|
||||
#if PB_PROTO_HEADER_VERSION != 40
|
||||
#error Regenerate this file with the current version of nanopb generator.
|
||||
#endif
|
||||
|
||||
PB_BIND(PB_App_StartRequest, PB_App_StartRequest, AUTO)
|
||||
|
||||
|
||||
PB_BIND(PB_App_LockStatusRequest, PB_App_LockStatusRequest, AUTO)
|
||||
|
||||
|
||||
PB_BIND(PB_App_LockStatusResponse, PB_App_LockStatusResponse, AUTO)
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,79 @@
|
|||
/* Automatically generated nanopb header */
|
||||
/* Generated by nanopb-0.4.6-dev */
|
||||
|
||||
#ifndef PB_PB_APP_APPLICATION_PB_H_INCLUDED
|
||||
#define PB_PB_APP_APPLICATION_PB_H_INCLUDED
|
||||
#include <pb.h>
|
||||
|
||||
#if PB_PROTO_HEADER_VERSION != 40
|
||||
#error Regenerate this file with the current version of nanopb generator.
|
||||
#endif
|
||||
|
||||
/* Struct definitions */
|
||||
typedef struct _PB_App_LockStatusRequest {
|
||||
char dummy_field;
|
||||
} PB_App_LockStatusRequest;
|
||||
|
||||
typedef struct _PB_App_StartRequest {
|
||||
char *name;
|
||||
char *args;
|
||||
} PB_App_StartRequest;
|
||||
|
||||
typedef struct _PB_App_LockStatusResponse {
|
||||
bool locked;
|
||||
} PB_App_LockStatusResponse;
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Initializer values for message structs */
|
||||
#define PB_App_StartRequest_init_default {NULL, NULL}
|
||||
#define PB_App_LockStatusRequest_init_default {0}
|
||||
#define PB_App_LockStatusResponse_init_default {0}
|
||||
#define PB_App_StartRequest_init_zero {NULL, NULL}
|
||||
#define PB_App_LockStatusRequest_init_zero {0}
|
||||
#define PB_App_LockStatusResponse_init_zero {0}
|
||||
|
||||
/* Field tags (for use in manual encoding/decoding) */
|
||||
#define PB_App_StartRequest_name_tag 1
|
||||
#define PB_App_StartRequest_args_tag 2
|
||||
#define PB_App_LockStatusResponse_locked_tag 1
|
||||
|
||||
/* Struct field encoding specification for nanopb */
|
||||
#define PB_App_StartRequest_FIELDLIST(X, a) \
|
||||
X(a, POINTER, SINGULAR, STRING, name, 1) \
|
||||
X(a, POINTER, SINGULAR, STRING, args, 2)
|
||||
#define PB_App_StartRequest_CALLBACK NULL
|
||||
#define PB_App_StartRequest_DEFAULT NULL
|
||||
|
||||
#define PB_App_LockStatusRequest_FIELDLIST(X, a) \
|
||||
|
||||
#define PB_App_LockStatusRequest_CALLBACK NULL
|
||||
#define PB_App_LockStatusRequest_DEFAULT NULL
|
||||
|
||||
#define PB_App_LockStatusResponse_FIELDLIST(X, a) \
|
||||
X(a, STATIC, SINGULAR, BOOL, locked, 1)
|
||||
#define PB_App_LockStatusResponse_CALLBACK NULL
|
||||
#define PB_App_LockStatusResponse_DEFAULT NULL
|
||||
|
||||
extern const pb_msgdesc_t PB_App_StartRequest_msg;
|
||||
extern const pb_msgdesc_t PB_App_LockStatusRequest_msg;
|
||||
extern const pb_msgdesc_t PB_App_LockStatusResponse_msg;
|
||||
|
||||
/* Defines for backwards compatibility with code written before nanopb-0.4.0 */
|
||||
#define PB_App_StartRequest_fields &PB_App_StartRequest_msg
|
||||
#define PB_App_LockStatusRequest_fields &PB_App_LockStatusRequest_msg
|
||||
#define PB_App_LockStatusResponse_fields &PB_App_LockStatusResponse_msg
|
||||
|
||||
/* Maximum encoded size of messages (where known) */
|
||||
/* PB_App_StartRequest_size depends on runtime parameters */
|
||||
#define PB_App_LockStatusRequest_size 0
|
||||
#define PB_App_LockStatusResponse_size 2
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
|
||||
#endif
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue