├── proto
└── .placeholder
├── res
├── icon.rc
├── icon.ico
├── OpenDGLab-Desktop.png
├── OpenDGLab-Desktop.icns
└── image.qrc
├── .gitignore
├── external
└── OpenDGLab.desktop
├── src
├── bleConnectThread.h
├── deviceStateEnum.h
├── waveSenderTimerThread.h
├── autoWaveChangerThread.h
├── bleConnectThread.cpp
├── remoteControl.h
├── autoRemoteProtocolPinger.h
├── mainWindowController.h
├── deviceItem.h
├── waveSenderTimerThread.cpp
├── scanDeviceController.h
├── autoRemoteProtocolPinger.cpp
├── scanDevice.h
├── autoWaveChangerThread.cpp
├── mainWindow.h
├── mainWindowController.cpp
├── remoteClient.h
├── deviceOperator.h
├── remote.h
├── scanDevice.cpp
├── scanDevice.ui
├── global.h
├── deviceItem.cpp
├── main.cpp
├── scanDeviceController.cpp
├── deviceItem.ui
├── dglabDevice.h
├── remoteControl.cpp
├── mainWindow.ui
├── global.cpp
├── mainWindow.cpp
├── remoteControl.ui
├── deviceOperator.cpp
├── remote.cpp
├── deviceOperator.ui
├── remoteClient.cpp
└── dglabDevice.cpp
├── README.md
├── CMakeLists.txt
├── .github
└── workflows
│ └── tags.yml
└── LICENSE
/proto/.placeholder:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/res/icon.rc:
--------------------------------------------------------------------------------
1 | IDI_ICON1 ICON DISCARDABLE "icon.ico"
--------------------------------------------------------------------------------
/res/icon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenDGLab/OpenDGLab-Desktop/HEAD/res/icon.ico
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea
2 | opendglab-core
3 | CMakeLists.txt.user
4 | build
5 | cmake-build-debug
6 | proto/*.proto
--------------------------------------------------------------------------------
/res/OpenDGLab-Desktop.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenDGLab/OpenDGLab-Desktop/HEAD/res/OpenDGLab-Desktop.png
--------------------------------------------------------------------------------
/res/OpenDGLab-Desktop.icns:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenDGLab/OpenDGLab-Desktop/HEAD/res/OpenDGLab-Desktop.icns
--------------------------------------------------------------------------------
/res/image.qrc:
--------------------------------------------------------------------------------
1 |
2 |
3 | OpenDGLab-Desktop.png
4 |
5 |
6 |
--------------------------------------------------------------------------------
/external/OpenDGLab.desktop:
--------------------------------------------------------------------------------
1 | [Desktop Entry]
2 | Type=Application
3 | Name=OpenDGLab Desktop
4 | Exec=AppRun %F
5 | Icon=OpenDGLab
6 | Comment=Open source solution to control DG-Lab Device
7 | Categories=Utility;Application;
--------------------------------------------------------------------------------
/src/bleConnectThread.h:
--------------------------------------------------------------------------------
1 | #ifndef BLECONNECTTHREAD_H
2 | #define BLECONNECTTHREAD_H
3 |
4 | #include
5 | #include
6 |
7 | class BLEConnectThread : public QThread
8 | {
9 | Q_OBJECT
10 | public:
11 | void run() override;
12 |
13 | signals:
14 |
15 | };
16 |
17 | #endif // BLECONNECTTHREAD_H
18 |
--------------------------------------------------------------------------------
/src/deviceStateEnum.h:
--------------------------------------------------------------------------------
1 | #ifndef DEVICESTATEENUM_H
2 | #define DEVICESTATEENUM_H
3 |
4 | namespace DeviceStateEnum {
5 | enum DeviceState {
6 | UNCONNECTED, CONNECTED, READY, READY_REMOTEMANAGED
7 | };
8 | enum DeviceChannel {
9 | CHANNEL_A, CHANNEL_B
10 | };
11 | }
12 |
13 | #endif // DEVICESTATEENUM_H
14 |
--------------------------------------------------------------------------------
/src/waveSenderTimerThread.h:
--------------------------------------------------------------------------------
1 | #ifndef WAVESENDERTIMERTHREAD_H
2 | #define WAVESENDERTIMERTHREAD_H
3 |
4 | #include
5 | #include
6 | #include
7 | class WaveSenderTimerThread : public QThread
8 | {
9 | Q_OBJECT
10 | public:
11 | explicit WaveSenderTimerThread(QObject *parent = nullptr);
12 | ~WaveSenderTimerThread() override;
13 | void run() override;
14 | private:
15 | QTimer* timer{};
16 | };
17 |
18 | #endif // WAVESENDERTIMERTHREAD_H
19 |
--------------------------------------------------------------------------------
/src/autoWaveChangerThread.h:
--------------------------------------------------------------------------------
1 | #ifndef AUTOWAVECHANGERTHREAD_H
2 | #define AUTOWAVECHANGERTHREAD_H
3 |
4 | #include
5 | #include
6 | #include
7 |
8 | class AutoWaveChangerThread : public QThread
9 | {
10 | Q_OBJECT
11 | public:
12 | explicit AutoWaveChangerThread(QObject *parent = nullptr);
13 | ~AutoWaveChangerThread() override;
14 | void run() override;
15 | private:
16 | QTimer* timer{};
17 | };
18 |
19 | #endif // AUTOWAVECHANGERTHREAD_H
20 |
--------------------------------------------------------------------------------
/src/bleConnectThread.cpp:
--------------------------------------------------------------------------------
1 | #include "bleConnectThread.h"
2 | #include "global.h"
3 |
4 | void BLEConnectThread::run() {
5 | while (!this->isInterruptionRequested()) {
6 | for (auto device: Global::dglabList) {
7 | if (device->getBLEController()->state() == QLowEnergyController::ControllerState::UnconnectedState) {
8 | SKIPNEXTQWARNING(SKIPNEXTERROR(device->getBLEController()->connectToDevice()));
9 | }
10 | }
11 | sleep(3);
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/src/remoteControl.h:
--------------------------------------------------------------------------------
1 | #ifndef REMOTECONTROL_H
2 | #define REMOTECONTROL_H
3 |
4 | #include
5 |
6 | namespace Ui {
7 | class RemoteControl;
8 | }
9 |
10 | class RemoteControl : public QWidget
11 | {
12 | Q_OBJECT
13 |
14 | public:
15 | explicit RemoteControl(QWidget *parent = nullptr);
16 | ~RemoteControl() override;
17 |
18 | private slots:
19 | void updateList();
20 |
21 | private:
22 | Ui::RemoteControl *ui;
23 | void changeState(bool start);
24 | };
25 |
26 | #endif // REMOTECONTROL_H
27 |
--------------------------------------------------------------------------------
/src/autoRemoteProtocolPinger.h:
--------------------------------------------------------------------------------
1 | //
2 | // Created by max_3 on 2021/1/11.
3 | //
4 |
5 | #ifndef OPENDGLAB_DESKTOP_AUTOREMOTEPROTOCOLPINGER_H
6 | #define OPENDGLAB_DESKTOP_AUTOREMOTEPROTOCOLPINGER_H
7 |
8 | #include
9 | #include
10 | #include
11 |
12 | class AutoRemoteProtocolPinger : public QThread
13 | {
14 | Q_OBJECT
15 | public:
16 | explicit AutoRemoteProtocolPinger(QObject *parent = nullptr);
17 | ~AutoRemoteProtocolPinger() override;
18 | void run() override;
19 | private:
20 | QTimer* timer{};
21 |
22 | };
23 |
24 |
25 | #endif //OPENDGLAB_DESKTOP_AUTOREMOTEPROTOCOLPINGER_H
26 |
--------------------------------------------------------------------------------
/src/mainWindowController.h:
--------------------------------------------------------------------------------
1 | #ifndef MAINWINDOWCONTROLLER_H
2 | #define MAINWINDOWCONTROLLER_H
3 | #include
4 | #include "bleConnectThread.h"
5 | #include "autoWaveChangerThread.h"
6 |
7 | class MainWindowController : public QObject
8 | {
9 | Q_OBJECT
10 | public:
11 | explicit MainWindowController(QObject *parent = nullptr);
12 | ~MainWindowController() override;
13 | void startAllBoost(int);
14 | void stopAllBoost();
15 | void shutdown();
16 | private:
17 | BLEConnectThread connectThread;
18 | AutoWaveChangerThread autoWaveChangerThread = AutoWaveChangerThread(this);
19 | signals:
20 |
21 | };
22 |
23 | #endif // MAINWINDOWCONTROLLER_H
24 |
--------------------------------------------------------------------------------
/src/deviceItem.h:
--------------------------------------------------------------------------------
1 | #ifndef DEVICEITEM_H
2 | #define DEVICEITEM_H
3 |
4 | #include
5 | #include "deviceStateEnum.h"
6 |
7 | namespace Ui {
8 | class DeviceItem;
9 | }
10 |
11 | class DeviceItem : public QWidget
12 | {
13 | Q_OBJECT
14 |
15 | public:
16 | explicit DeviceItem(QString id, QString address, QString name, QWidget *parent = nullptr);
17 | ~DeviceItem() override;
18 | void changeState(DeviceStateEnum::DeviceState state);
19 | void changeBattery(int level);
20 | DeviceStateEnum::DeviceState getState();
21 |
22 | private:
23 | Ui::DeviceItem *ui;
24 | DeviceStateEnum::DeviceState deviceState = DeviceStateEnum::DeviceState::UNCONNECTED;
25 | };
26 |
27 | #endif // DEVICEITEM_H
28 |
--------------------------------------------------------------------------------
/src/waveSenderTimerThread.cpp:
--------------------------------------------------------------------------------
1 | #include "waveSenderTimerThread.h"
2 | #include "dglabDevice.h"
3 | WaveSenderTimerThread::WaveSenderTimerThread(QObject *parent):
4 | QThread(parent)
5 | {
6 | connect(this, &QThread::finished, this, &QThread::deleteLater);
7 | }
8 |
9 | void WaveSenderTimerThread::run()
10 | {
11 | timer = new QTimer();
12 | timer->setInterval(200);
13 | connect(timer, &QTimer::timeout, parent(), [this](){
14 | reinterpret_cast(parent())->waveSender();
15 | });
16 | connect(this, &QThread::finished, timer, &QTimer::stop);
17 | timer->start();
18 | this->exec();
19 | }
20 |
21 | WaveSenderTimerThread::~WaveSenderTimerThread() {
22 | delete timer;
23 | }
24 |
--------------------------------------------------------------------------------
/src/scanDeviceController.h:
--------------------------------------------------------------------------------
1 | #ifndef SCANDEVICECONTROLLER_H
2 | #define SCANDEVICECONTROLLER_H
3 |
4 | #include
5 | #include
6 |
7 | class ScanDevice;
8 | class ScanDeviceController : public QObject
9 | {
10 | Q_OBJECT
11 | public:
12 | explicit ScanDeviceController(QObject *parent = nullptr);
13 | ~ScanDeviceController() override;
14 | void startScan();
15 | void stopScan();
16 |
17 | private slots:
18 | void deviceScanFound(const QBluetoothDeviceInfo&);
19 | void deviceScanError();
20 | void deviceScanFinished();
21 |
22 | signals:
23 | void eventScanError();
24 | void eventScanFinished();
25 | void eventScanFound();
26 | };
27 |
28 | #endif // SCANDEVICECONTROLLER_H
29 |
--------------------------------------------------------------------------------
/src/autoRemoteProtocolPinger.cpp:
--------------------------------------------------------------------------------
1 | //
2 | // Created by max_3 on 2021/1/11.
3 | //
4 |
5 | #include "autoRemoteProtocolPinger.h"
6 | #include
7 | #include "global.h"
8 |
9 | #include
10 | AutoRemoteProtocolPinger::AutoRemoteProtocolPinger(QObject* parent): QThread(parent)
11 | {
12 | timer = new QTimer();
13 | timer->setInterval(1000);
14 | connect(timer, &QTimer::timeout, this, [](){
15 | for(auto r: Global::remoteList) {
16 | r->sendPing();
17 | }
18 | });
19 | connect(this, &QThread::finished, timer, &QTimer::stop);
20 | timer->start();
21 | }
22 |
23 | AutoRemoteProtocolPinger::~AutoRemoteProtocolPinger()
24 | {
25 | delete timer;
26 | }
27 |
28 | void AutoRemoteProtocolPinger::run()
29 | {
30 | this->exec();
31 | }
32 |
--------------------------------------------------------------------------------
/src/scanDevice.h:
--------------------------------------------------------------------------------
1 | #ifndef SCANDEVICE_H
2 | #define SCANDEVICE_H
3 |
4 | #include
5 | #include
6 | #include
7 | #include "scanDeviceController.h"
8 |
9 | namespace Ui {
10 | class ScanDevice;
11 | }
12 |
13 | class ScanDevice : public QMainWindow
14 | {
15 | Q_OBJECT
16 |
17 | public:
18 | explicit ScanDevice(QWidget *parent = nullptr);
19 | ~ScanDevice() override;
20 |
21 | private:
22 | Ui::ScanDevice *ui;
23 | ScanDeviceController* mvcController;
24 |
25 | public slots:
26 | void setCancelled();
27 | void setError();
28 | void setFinished();
29 | void setFound();
30 |
31 | protected:
32 | void showEvent(QShowEvent*) override;
33 | void closeEvent(QCloseEvent*) override;
34 |
35 | signals:
36 | void needRefresh();
37 |
38 | };
39 |
40 | #endif // SCANDEVICE_H
41 |
--------------------------------------------------------------------------------
/src/autoWaveChangerThread.cpp:
--------------------------------------------------------------------------------
1 | #include "autoWaveChangerThread.h"
2 | #include
3 | #include "global.h"
4 | AutoWaveChangerThread::AutoWaveChangerThread(QObject* parent): QThread(parent)
5 | {
6 | timer = new QTimer();
7 | timer->setInterval(1000);
8 | connect(timer, &QTimer::timeout, this, [](){
9 | for (auto device: Global::dglabList) {
10 | if (device->getState() == DeviceStateEnum::DeviceState::READY || device->getState() == DeviceStateEnum::DeviceState::READY_REMOTEMANAGED) {
11 | device->getUiDeviceOperator()->checkIfNeedChangeWave();
12 | }
13 | }
14 | });
15 | connect(this, &QThread::finished, timer, &QTimer::stop);
16 | timer->start();
17 | }
18 |
19 | AutoWaveChangerThread::~AutoWaveChangerThread()
20 | {
21 | delete timer;
22 | }
23 |
24 | void AutoWaveChangerThread::run()
25 | {
26 | this->exec();
27 | }
28 |
--------------------------------------------------------------------------------
/src/mainWindow.h:
--------------------------------------------------------------------------------
1 | #ifndef MAINWINDOW_H
2 | #define MAINWINDOW_H
3 |
4 | #include
5 | #include "mainWindowController.h"
6 |
7 | QT_BEGIN_NAMESPACE
8 | namespace Ui { class MainWindow; }
9 | QT_END_NAMESPACE
10 |
11 | class MainWindow : public QMainWindow
12 | {
13 | Q_OBJECT
14 |
15 | public:
16 | explicit MainWindow(QWidget *parent = nullptr);
17 | ~MainWindow() override;
18 | void saveDevice();
19 | void loadDevice();
20 |
21 | protected:
22 | void showEvent(QShowEvent*) override;
23 | void closeEvent(QCloseEvent*) override;
24 |
25 | private slots:
26 | void addDevice();
27 | void refreshDeviceList();
28 | void showContextMenu(const QPoint &);
29 | void removeDeviceItem();
30 | void allBoostChanged(int);
31 | void startAllBoost(int);
32 | void stopAllBoost();
33 |
34 | private:
35 | Ui::MainWindow *ui;
36 | MainWindowController *mvcController;
37 | };
38 | #endif // MAINWINDOW_H
39 |
--------------------------------------------------------------------------------
/src/mainWindowController.cpp:
--------------------------------------------------------------------------------
1 | #include "mainWindowController.h"
2 | #include "global.h"
3 | MainWindowController::MainWindowController(QObject *parent) : QObject(parent)
4 | {
5 | connectThread.start();
6 | autoWaveChangerThread.start();
7 | }
8 |
9 | MainWindowController::~MainWindowController() {
10 | autoWaveChangerThread.quit();
11 | connectThread.requestInterruption();
12 | connectThread.wait();
13 | }
14 |
15 | void MainWindowController::startAllBoost(int boost)
16 | {
17 | for (auto device: Global::dglabList) {
18 | if (device->getState() == DeviceStateEnum::DeviceState::READY || device->getState() == DeviceStateEnum::DeviceState::READY_REMOTEMANAGED)
19 | device->startGlobalBoost(boost);
20 | }
21 | }
22 |
23 | void MainWindowController::stopAllBoost()
24 | {
25 | for (auto device: Global::dglabList) {
26 | if (device->getState() == DeviceStateEnum::DeviceState::READY || device->getState() == DeviceStateEnum::DeviceState::READY_REMOTEMANAGED)
27 | device->stopGlobalBoost();
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/remoteClient.h:
--------------------------------------------------------------------------------
1 | #ifndef REMOTECLIENT_H
2 | #define REMOTECLIENT_H
3 |
4 | #include
5 | #include
6 | #include
7 | #ifndef __linux__
8 | #include
9 | #endif
10 |
11 | class RemoteClient : public QObject
12 | {
13 | Q_OBJECT
14 | public:
15 | explicit RemoteClient(QTcpSocket *tcpSocket, QObject *parent = nullptr);
16 | #ifndef __linux__
17 | explicit RemoteClient(QWebSocket *webSocket, QObject *parent = nullptr);
18 | #endif
19 | ~RemoteClient() override ;
20 | void sendConnected(const QString& _uuid, const QString& token, bool isAuth);
21 | void sendDeviceReset(const QString& deviceId);
22 | void sendPowerUpdate(const QString &deviceId, int channel_a, int channel_b);
23 | void sendPing();
24 | void close();
25 | QString getUuid();
26 |
27 | private:
28 | bool isWS = false;
29 | QTcpSocket *tcpSocket = nullptr;
30 | #ifndef __linux__
31 | QWebSocket *wsSocket = nullptr;
32 | #endif
33 | void readyRead(QByteArray data);
34 | bool isAuthed = false;
35 | QString uuid;
36 | QList locked;
37 |
38 | signals:
39 | void doAuth(RemoteClient *client, QString appName, QString appId, QString token);
40 | };
41 |
42 | #endif // REMOTECLIENT_H
43 |
--------------------------------------------------------------------------------
/src/deviceOperator.h:
--------------------------------------------------------------------------------
1 | #ifndef DEVICEOPERATOR_H
2 | #define DEVICEOPERATOR_H
3 |
4 | #include "deviceStateEnum.h"
5 | #include
6 | #include
7 | namespace Ui {
8 | class DeviceOperator;
9 | }
10 |
11 | class DeviceOperator : public QWidget
12 | {
13 | Q_OBJECT
14 |
15 | public:
16 | explicit DeviceOperator(QString id, QWidget *parent = nullptr);
17 | ~DeviceOperator() override;
18 | QString getWaveA();
19 | QString getWaveB();
20 | void setWaveA(QString);
21 | void setWaveB(QString);
22 | void setAutoChange(DeviceStateEnum::DeviceChannel, bool);
23 | bool getAutoChange(DeviceStateEnum::DeviceChannel);
24 | void checkIfNeedChangeWave();
25 |
26 | public slots:
27 | void setPower(DeviceStateEnum::DeviceChannel channel, int level);
28 |
29 | private slots:
30 |
31 |
32 | private:
33 | Ui::DeviceOperator *ui;
34 | int autoATimer = 0;
35 | int autoBTimer = 0;
36 | QRandomGenerator random;
37 |
38 | signals:
39 | void startBoost(DeviceStateEnum::DeviceChannel, int);
40 | void stopBoost(DeviceStateEnum::DeviceChannel, int);
41 | void changePower(int, int);
42 | void changeWave(DeviceStateEnum::DeviceChannel, QString);
43 | };
44 |
45 | #endif // DEVICEOPERATOR_H
46 |
--------------------------------------------------------------------------------
/src/remote.h:
--------------------------------------------------------------------------------
1 | #ifndef REMOTE_H
2 | #define REMOTE_H
3 |
4 | #include
5 | #include
6 | #ifndef __linux__
7 | #include
8 | #endif
9 | #include
10 | #include "remoteClient.h"
11 | #include "autoRemoteProtocolPinger.h"
12 | class Remote : public QObject
13 | {
14 | Q_OBJECT
15 | public:
16 | explicit Remote(QObject *parent = nullptr);
17 | ~Remote() override;
18 | bool isStarted();
19 | bool start();
20 | void stop();
21 | void setWSPort(int port);
22 | void setTcpPort(int port);
23 | void load();
24 | void save();
25 | QMap preAuth; //UUID, Token
26 | QMap preAuthName; //UUID, Token
27 |
28 | private:
29 | int tcpPort;
30 | int wsPort;
31 | QRandomGenerator qrng;
32 | QList clientList;
33 | QTcpServer *tcpServer;
34 | #ifndef __linux__
35 | QWebSocketServer *wsServer;
36 | #endif
37 | bool isStart = false;
38 | QString getRandomString();
39 | AutoRemoteProtocolPinger* autoRemoteProtocolPinger = nullptr;
40 |
41 | private slots:
42 | void tcpNewConnection();
43 | void wsNewConnection();
44 | void doAuth(RemoteClient* client, QString appName, QString appId, QString token);
45 |
46 | signals:
47 | void stateChange();
48 |
49 | };
50 |
51 | #endif // REMOTE_H
52 |
--------------------------------------------------------------------------------
/src/scanDevice.cpp:
--------------------------------------------------------------------------------
1 | #include "scanDevice.h"
2 | #include "ui_scanDevice.h"
3 | #include
4 |
5 | ScanDevice::ScanDevice(QWidget *parent) :
6 | QMainWindow(parent),
7 | ui(new Ui::ScanDevice),
8 | mvcController(new ScanDeviceController(this))
9 | {
10 | ui->setupUi(this);
11 | this->setAttribute(Qt::WidgetAttribute::WA_DeleteOnClose);
12 | this->setFixedSize(QSize(300, 100));
13 | connect(mvcController, &ScanDeviceController::eventScanError, this, &ScanDevice::setError);
14 | connect(mvcController, &ScanDeviceController::eventScanFinished, this, &ScanDevice::setFinished);
15 | connect(mvcController, &ScanDeviceController::eventScanFound, this, &ScanDevice::setFound);
16 | connect(ui->btnAddCancel, &QPushButton::clicked, this, &ScanDevice::setCancelled);
17 | }
18 |
19 | ScanDevice::~ScanDevice()
20 | {
21 | delete ui;
22 | }
23 | void ScanDevice::showEvent(QShowEvent*) {
24 | mvcController->startScan();
25 | }
26 | void ScanDevice::closeEvent(QCloseEvent*) {
27 |
28 | }
29 | void ScanDevice::setError() {
30 | ui->lbl_status->setText("错误:无法扫描");
31 | }
32 | void ScanDevice::setFinished() {
33 | ui->lbl_status->setText("扫描完成 未发现设备");
34 | }
35 | void ScanDevice::setFound() {
36 | emit needRefresh();
37 | this->close();
38 | }
39 | void ScanDevice::setCancelled() {
40 | this->close();
41 | }
42 |
--------------------------------------------------------------------------------
/src/scanDevice.ui:
--------------------------------------------------------------------------------
1 |
2 |
3 | ScanDevice
4 |
5 |
6 | Qt::ApplicationModal
7 |
8 |
9 |
10 | 0
11 | 0
12 | 300
13 | 100
14 |
15 |
16 |
17 |
18 | 0
19 | 0
20 |
21 |
22 |
23 | 添加设备
24 |
25 |
26 |
27 | -
28 |
29 |
30 |
-
31 |
32 |
33 | 正在发现设备...
34 |
35 |
36 | Qt::AlignCenter
37 |
38 |
39 |
40 | -
41 |
42 |
43 | 取消
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
--------------------------------------------------------------------------------
/src/global.h:
--------------------------------------------------------------------------------
1 | #ifndef GLOBAL_H
2 | #define GLOBAL_H
3 | #define SKIPNEXTERROR(N) \
4 | ( \
5 | (Global::skipError = true),\
6 | (N), \
7 | (Global::skipError = false),\
8 | (void)0 \
9 | )
10 | #define SKIPNEXTQWARNING(N) \
11 | ( \
12 | (Global::skipWarning = true),\
13 | (N), \
14 | (Global::skipWarning = false),\
15 | (void)0 \
16 | )
17 | #include
18 | #include
19 | #include
20 | #include "dglabDevice.h"
21 | #include "remote.h"
22 |
23 | namespace Global {
24 | extern QList basicWaveNameList;
25 | extern QList touchWaveNameList;
26 | extern QString deviceName;
27 | namespace DGLabServices {
28 | namespace DeviceStatus {
29 | extern QString service;
30 | namespace Characteristic {
31 | extern QString electric;
32 | }
33 | }
34 | namespace EStimStatus {
35 | extern QString service;
36 | namespace Characteristic {
37 | extern QString setup;
38 | extern QString abpower;
39 | extern QString waveA;
40 | extern QString waveB;
41 | }
42 | }
43 | }
44 | extern bool skipError;
45 | extern bool skipWarning;
46 | extern Remote* remote;
47 | extern QList remoteList;
48 | extern QList dglabList;
49 | extern QBluetoothDeviceDiscoveryAgent *bleScanAgent;
50 | extern QMutex mutex;
51 | //extern Remote *remote;
52 | void initGlobal();
53 | QList getWaveList();
54 | }
55 |
56 | #endif // GLOBAL_H
57 |
--------------------------------------------------------------------------------
/src/deviceItem.cpp:
--------------------------------------------------------------------------------
1 | #include "deviceItem.h"
2 | #include "ui_deviceItem.h"
3 |
4 | DeviceItem::DeviceItem(QString id, QString address, QString name, QWidget *parent) :
5 | QWidget(parent),
6 | ui(new Ui::DeviceItem)
7 | {
8 | ui->setupUi(this);
9 | ui->lbl_ID->setText(id);
10 | ui->lbl_Mac->setText(address);
11 | ui->lbl_Name->setText(name);
12 | ui->lbl_Status->setText("未连接");
13 | }
14 |
15 | DeviceItem::~DeviceItem()
16 | {
17 | delete ui;
18 | }
19 |
20 | void DeviceItem::changeBattery(int level)
21 | {
22 | if (level < 0) {
23 | ui->pb_battery->setFormat(" 正在配置..");
24 | ui->pb_battery->setMaximum(0);
25 | ui->pb_battery->setMinimum(0);
26 | ui->pb_battery->setValue(0);
27 | } else {
28 | ui->pb_battery->setFormat(" 电池:%p%");
29 | ui->pb_battery->setMaximum(100);
30 | ui->pb_battery->setMinimum(0);
31 | ui->pb_battery->setValue(level);
32 | }
33 | }
34 |
35 | DeviceStateEnum::DeviceState DeviceItem::getState()
36 | {
37 | return deviceState;
38 | }
39 |
40 | void DeviceItem::changeState(DeviceStateEnum::DeviceState state)
41 | {
42 | switch (state) {
43 | case DeviceStateEnum::DeviceState::CONNECTED:
44 | ui->lbl_Status->setText("已连接 - 正在配置");
45 | break;
46 | case DeviceStateEnum::DeviceState::READY:
47 | ui->lbl_Status->setText("已连接 - 已就绪");
48 | break;
49 | case DeviceStateEnum::DeviceState::UNCONNECTED:
50 | ui->lbl_Status->setText("未连接");
51 | break;
52 | case DeviceStateEnum::DeviceState::READY_REMOTEMANAGED:
53 | ui->lbl_Status->setText("已连接 - 已就绪(正在被远程管理)");
54 | break;
55 | }
56 | deviceState = state;
57 | }
58 |
--------------------------------------------------------------------------------
/src/main.cpp:
--------------------------------------------------------------------------------
1 | #include "mainWindow.h"
2 | #include "global.h"
3 | #include
4 | void crashingMessageHandler(QtMsgType type, const QMessageLogContext& context, const QString& msg)
5 | {
6 | QByteArray localMsg(msg.toLocal8Bit());
7 | switch (type) {
8 | case QtDebugMsg:
9 | fprintf(stderr, "Debug: %s\n", localMsg.constData());
10 | break;
11 | case QtInfoMsg:
12 | fprintf(stderr, "Info: %s (%s:%u, %s)\n", localMsg.constData(), context.file, context.line, context.function);
13 | break;
14 | case QtWarningMsg:
15 | fprintf(stderr, "Warning: %s (%s:%u, %s)\n", localMsg.constData(), context.file, context.line, context.function);
16 | if(!Global::skipWarning) fprintf(stderr, "Warning: %s (%s:%u, %s)\n", localMsg.constData(), context.file, context.line, context.function);
17 | if(!Global::skipError) abort(); //Index out of range is just a Warning!
18 | break;
19 | case QtCriticalMsg:
20 | fprintf(stderr, "Critical: %s (%s:%u, %s)\n", localMsg.constData(), context.file, context.line, context.function);
21 | if(!Global::skipError) abort();
22 | break;
23 | case QtFatalMsg:
24 | fprintf(stderr, "Fatal: %s (%s:%u, %s)\n", localMsg.constData(), context.file, context.line, context.function);
25 | if(!Global::skipError) abort();
26 | break;
27 | }
28 | }
29 |
30 |
31 | int main(int argc, char *argv[])
32 | {
33 | QApplication a(argc, argv);
34 | qInstallMessageHandler(crashingMessageHandler);
35 | Global::initGlobal();
36 | Global::remote->load();
37 | MainWindow w;
38 | w.show();
39 | return a.exec();
40 | }
41 |
--------------------------------------------------------------------------------
/src/scanDeviceController.cpp:
--------------------------------------------------------------------------------
1 | #include "scanDeviceController.h"
2 | #include "global.h"
3 | #include
4 | #include
5 | ScanDeviceController::ScanDeviceController(QObject *parent) : QObject(parent)
6 | {
7 | connect(Global::bleScanAgent, &QBluetoothDeviceDiscoveryAgent::deviceDiscovered, this, &ScanDeviceController::deviceScanFound);
8 | connect(Global::bleScanAgent, QOverload::of(&QBluetoothDeviceDiscoveryAgent::error), this, &ScanDeviceController::deviceScanError);
9 | connect(Global::bleScanAgent, &QBluetoothDeviceDiscoveryAgent::finished, this, &ScanDeviceController::deviceScanFinished);
10 | }
11 |
12 | ScanDeviceController::~ScanDeviceController() {
13 | stopScan();
14 | }
15 | void ScanDeviceController::startScan() {
16 | Global::bleScanAgent->start();
17 | }
18 |
19 | void ScanDeviceController::stopScan() {
20 | Global::bleScanAgent->stop();
21 | }
22 |
23 | void ScanDeviceController::deviceScanFound(const QBluetoothDeviceInfo& info) {
24 | QMutexLocker locker(&Global::mutex);
25 | bool isSaved = false;
26 | for (auto savedDevice: (Global::dglabList)) {
27 | if(savedDevice->getDeviceAddressOrUuid() ==
28 | #ifdef Q_OS_MACOS
29 | info.deviceUuid()
30 | #else
31 | info.address()
32 | #endif
33 | .toString()
34 | ) {
35 | isSaved = true;
36 | }
37 | }
38 | if (info.name() == Global::deviceName && !isSaved) {
39 | stopScan();
40 | DGLabDevice *device = new DGLabDevice(info);
41 | Global::dglabList.append(device);
42 | emit eventScanFound();
43 | }
44 | }
45 |
46 | void ScanDeviceController::deviceScanError() {
47 | emit eventScanError();
48 | }
49 |
50 | void ScanDeviceController::deviceScanFinished() {
51 | emit eventScanFinished();
52 | }
53 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # OpenDGLab Desktop
2 | OpenDGLab Desktop 是一个使用 OpenDGLab Core 和 Qt5 编写的桌面客户端。
3 | OpenDGLab Desktop 的目标是在 Windows、Linux、MacOS 上提供对多 DG-Lab 设备的统一化支持。
4 | 使用 OpenDGLab Desktop 您可以在支持蓝牙 BLE 的电脑上同时管理多个 DG-Lab 设备。给您更加优质的使用体验。
5 | 支持 OpenDGLab OpenProtocol 协议,允许第三方程序接入。提供更多可能。
6 |
7 | ## 编译
8 | ### 获取 OpenDGLab 必要文件
9 | 下载并编译 OpenDGLab Core 获取您对应架构的 Core 文件。
10 |
11 | 从 OpenDGLab OpenProtocol 获取 app.proto 文件放入 proto 文件夹中。
12 |
13 | 依赖 libprotobuf 请事先安装。
14 |
15 | ### 编译 OpenDGLab Desktop (Windows)
16 | Windows 编译请使用 `lib /def:libopendglab.def /out:libopendglab.lib /machine:x64` 或 `lib /def:libopendglab.def /out:libopendglab.lib /machine:x86` 获取对应的 .lib 文件。
17 | 将 lib dll 文件放入 opendglab-core/bin 文件夹,将 h 文件放入 opendglab-core/header 文件夹。
18 |
19 | ```shell
20 | mkdir build
21 | cd build
22 | cmake -G "Ninja" -DCMAKE_BUILD_TYPE=MinSizeRel ..
23 | cmake --build . --target all
24 | ```
25 |
26 | 在 Build 文件夹下可以找到编译好的对应版本的 zip 压缩文件。
27 |
28 | ### 编译 OpenDGLab Desktop (Linux)
29 | 将 OpenDGLab-Core 编译出的 a 文件放入 opendglab-core/bin 文件夹,将 h 文件放入 opendglab-core/header 文件夹。
30 | 如果需要打包未 AppImage 请安装 linuxdeployqt
31 | ```shell
32 | mkdir build
33 | cd build
34 | cmake -DCMAKE_BUILD_TYPE=MinSizeRel ..
35 | cmake --build . --target all
36 | ```
37 | 编译后会生成 OpenDGLab-Desktop 可执行文件,您需要使用 `sudo setcap CAP_NET_ADMIN+ep OpenDGLab-Desktop` 和 `chmod +x OpenDGLab-Desktop` 来对其添加权限。
38 |
39 | 如果需要生成 AppImage 则运行
40 | ```shell
41 | mkdir appImage
42 | cp ../external/OpenDGLab.desktop appImage/
43 | cp ../res/OpenDGLab-Desktop.png appImage/OpenDGLab.png
44 | cp OpenDGLab-Desktop appImage/
45 | cd appImage
46 | ../linuxdeployqt OpenDGLab-Desktop -appimage -unsupported-bundle-everything
47 | ```
48 | 即可完成打包操作,请注意 AppImage 不能设置 Cap,如需运行请使用 `gksu` 或 `kdesu` 来运行。
49 |
50 | ### 编译 OpenDGLab Desktop (MacOS)
51 | 将 OpenDGLab-Core 编译出的 a 文件放入 opendglab-core/bin 文件夹,将 h 文件放入 opendglab-core/header 文件夹。
52 | ```shell
53 | mkdir build
54 | cd build
55 | cmake -DCMAKE_BUILD_TYPE=MinSizeRel ..
56 | cmake --build . --target all
57 | macdeployqt OpenDGLab-Desktop.app -dmg
58 | ```
59 | 即可生成 OpenDGLab-Desktop.dmg 文件。
60 |
61 | ## 注意
62 | 经过一些测试开发组发现如下问题。
63 | * CSR 高通芯片方案的蓝牙模块似乎不支持 BLE,您可能遇到无法搜索到 DG-Lab 设备的问题。
64 | * ASUS PCE-AC58BT 因为未知原因工作不良,目前无法确定是硬件问题还是网卡固件问题。您可能会遇到长时间无法搜索到 DG-Lab 设备的问题,或者搜索到后需要很长时间才能连接和完成配置。甚至遭遇 OpenDGLab Desktop 应用程序崩溃(这是由于您的网卡蓝牙没有正常返回探索服务指令导致的)。
65 |
66 | ## 声明
67 | 使用本程序请遵循 DG-Lab 官方安全声明使用。使用 OpenDGLab 项目,视同于您自己承担相关非官方实现风险。OpenDGLab 开源项目组不为您使用 DG-Lab 设备出现的任何问题负责。
68 |
69 | ## 协议
70 | 使用 AGPLv3 协议授权。
--------------------------------------------------------------------------------
/src/deviceItem.ui:
--------------------------------------------------------------------------------
1 |
2 |
3 | DeviceItem
4 |
5 |
6 |
7 | 0
8 | 0
9 | 400
10 | 96
11 |
12 |
13 |
14 |
15 | 0
16 | 0
17 |
18 |
19 |
20 |
21 | 0
22 | 0
23 |
24 |
25 |
26 | 蓝牙设备
27 |
28 |
29 | -
30 |
31 |
32 | font-weight: bold;
33 |
34 |
35 | TextLabel
36 |
37 |
38 | Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter
39 |
40 |
41 |
42 | -
43 |
44 |
-
45 |
46 |
47 | TextLabel
48 |
49 |
50 |
51 | -
52 |
53 |
54 | Qt::LeftToRight
55 |
56 |
57 | TextLabel
58 |
59 |
60 | Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter
61 |
62 |
63 |
64 |
65 |
66 | -
67 |
68 |
-
69 |
70 |
71 | 状态
72 |
73 |
74 |
75 | -
76 |
77 |
78 | TextLabel
79 |
80 |
81 |
82 |
83 |
84 | -
85 |
86 |
87 | 0
88 |
89 |
90 | true
91 |
92 |
93 | false
94 |
95 |
96 | 电池:%p%
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
--------------------------------------------------------------------------------
/src/dglabDevice.h:
--------------------------------------------------------------------------------
1 | #ifndef DGLABDEVICE_H
2 | #define DGLABDEVICE_H
3 |
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 | #include
10 | #include
11 | #include
12 | #include
13 | #include "deviceItem.h"
14 | #include "deviceOperator.h"
15 | #include "waveSenderTimerThread.h"
16 | #include
17 | class DGLabDevice : public QObject
18 | {
19 | Q_OBJECT
20 | public:
21 | explicit DGLabDevice(const QBluetoothDeviceInfo &_deviceInfo, QObject *parent = nullptr);
22 | ~DGLabDevice() override;
23 | QString getDeviceAddressOrUuid();
24 | QString getID();
25 | bool isShownOnUi();
26 | void setShownOnUi();
27 | DeviceItem* getUiDeviceItem();
28 | DeviceOperator* getUiDeviceOperator();
29 | QLowEnergyController* getBLEController();
30 | void startGlobalBoost(int);
31 | void stopGlobalBoost();
32 | DeviceStateEnum::DeviceState getState();
33 |
34 | void clearCustomWaveAPI(DeviceStateEnum::DeviceChannel);
35 | void addCustomWaveAPI(DeviceStateEnum::DeviceChannel, QByteArray);
36 | QString getWaveAPI(DeviceStateEnum::DeviceChannel);
37 | void setWaveAPI(DeviceStateEnum::DeviceChannel, QString);
38 | int getStrengthAPI(DeviceStateEnum::DeviceChannel);
39 | void setStrengthAPI(int, int);
40 | void setDeviceRemoteLocked(bool);
41 | bool getDeviceRemoteLocked();
42 |
43 | private:
44 | QBluetoothDeviceInfo deviceInfo;
45 | QString id;
46 | QString addressOrUuid;
47 | QLowEnergyController* controller;
48 | QLowEnergyService* batteryService;
49 | QLowEnergyService* eStimService;
50 | libopendglab_kref_OpenDGLab openDgLab;
51 | DeviceItem* uiDeviceItem;
52 | DeviceOperator* uiDeviceOperator;
53 | QRecursiveMutex customWaveAMutex;
54 | QRecursiveMutex customWaveBMutex;
55 | QList customWaveA;
56 | QList customWaveB;
57 | bool shownOnUi = false;
58 | WaveSenderTimerThread* waveSenderTimerThread;
59 | int powerA = 0;
60 | int powerB = 0;
61 | int boostA = 0;
62 | int boostB = 0;
63 | int globalBoost = 0;
64 | bool remoteLocked = false;
65 |
66 | public slots:
67 | void waveSender();
68 | void setWave(DeviceStateEnum::DeviceChannel, QString);
69 | void startChannelBoost(DeviceStateEnum::DeviceChannel, int boost);
70 | void stopChannelBoost(DeviceStateEnum::DeviceChannel, int boost);
71 |
72 | private slots:
73 | void changePower(int, int);
74 | void deviceConnected();
75 | void deviceDisconnected();
76 | void deviceServiceDiscoverFinished();
77 | void deviceBatteryServiceStateChanged(QLowEnergyService::ServiceState);
78 | void deviceEStimServiceStateChanged(QLowEnergyService::ServiceState);
79 | void deviceBatteryCharacteristicArrived(const QLowEnergyCharacteristic&, const QByteArray &);
80 | void deviceEStimCharacteristicArrived(const QLowEnergyCharacteristic&, const QByteArray &);
81 |
82 | signals:
83 | void powerUpdate(DeviceStateEnum::DeviceChannel, int);
84 | };
85 |
86 | #endif // DGLABDEVICE_H
87 |
--------------------------------------------------------------------------------
/src/remoteControl.cpp:
--------------------------------------------------------------------------------
1 | #include "remoteControl.h"
2 | #include "ui_remoteControl.h"
3 | #include "global.h"
4 | #include
5 |
6 | RemoteControl::RemoteControl(QWidget *parent) :
7 | QWidget(parent),
8 | ui(new Ui::RemoteControl)
9 | {
10 | ui->setupUi(this);
11 | #ifdef __linux__
12 | ui->sb_ws_port->setVisible(false);
13 | #endif
14 | ui->btn_remote_remove->setEnabled(false);
15 | changeState(false);
16 | connect(ui->chk_remote, &QCheckBox::stateChanged, this,[this](int state){
17 | changeState(state);
18 | });
19 | connect(ui->sb_tcp_port, qOverload(&QSpinBox::valueChanged), this, [this](int value) {
20 | Global::remote->setTcpPort(value);
21 | });
22 | connect(ui->sb_ws_port, qOverload(&QSpinBox::valueChanged), this, [this](int value) {
23 | Global::remote->setWSPort(value);
24 | });
25 | connect(ui->listWidget, &QListWidget::currentTextChanged, this, [this](const QString ¤tText){
26 | if (currentText.isEmpty()) {
27 | ui->lbl_title->setText("");
28 | ui->lbl_remote_id->setText("");
29 | ui->btn_remote_remove->setEnabled(false);
30 | } else {
31 | QString uuid;
32 | uuid = currentText.split("(")[1];
33 | uuid = uuid.split(")")[0];
34 | auto name = Global::remote->preAuthName[uuid];
35 | ui->lbl_title->setText(name);
36 | ui->lbl_remote_id->setText(uuid);
37 | ui->btn_remote_remove->setEnabled(true);
38 | }
39 | });
40 | connect(ui->btn_remote_remove, &QPushButton::clicked, this, [this](){
41 | RemoteClient* client = nullptr;
42 | Global::remote->preAuth.remove(ui->lbl_remote_id->text());
43 | Global::remote->preAuthName.remove(ui->lbl_remote_id->text());
44 | for(auto r: Global::remoteList) {
45 | if (r->getUuid() == ui->lbl_remote_id->text()) {
46 | client = r;
47 | break;
48 | }
49 | }
50 | if (client != nullptr){
51 | client->close();
52 | Global::remote->save();
53 | }
54 | updateList();
55 | });
56 | connect(Global::remote, &Remote::stateChange, this, &RemoteControl::updateList);
57 | updateList();
58 | }
59 |
60 | void RemoteControl::changeState(bool start) {
61 | ui->sb_tcp_port->setEnabled(!start);
62 | ui->sb_ws_port->setEnabled(!start);
63 | ui->listWidget->setEnabled(start);
64 | ui->btn_remote_remove->setEnabled(start);
65 | if (start) {
66 | if(!Global::remote->start()) {
67 | QMessageBox::warning(this, "警告", "无法启动服务器。");
68 | changeState(false);
69 | }
70 | } else {
71 | Global::remote->stop();
72 | ui->listWidget->clearSelection();
73 | ui->btn_remote_remove->setEnabled(false);
74 | }
75 | }
76 |
77 | RemoteControl::~RemoteControl()
78 | {
79 | delete ui;
80 | }
81 |
82 | void RemoteControl::updateList() {
83 | ui->listWidget->clearSelection();
84 | ui->listWidget->clear();
85 | for (const auto& pre: Global::remote->preAuthName.keys()){
86 | auto k = Global::remote->preAuthName[pre];
87 | ui->listWidget->addItem(QString("%1 (%2)").arg(k, pre));
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/src/mainWindow.ui:
--------------------------------------------------------------------------------
1 |
2 |
3 | MainWindow
4 |
5 |
6 |
7 | 0
8 | 0
9 | 800
10 | 600
11 |
12 |
13 |
14 | OpenDGLab Desktop
15 |
16 |
17 |
18 | :/icon/OpenDGLab-Desktop.png:/icon/OpenDGLab-Desktop.png
19 |
20 |
21 |
22 | -
23 |
24 |
25 |
26 | 0
27 | 0
28 |
29 |
30 |
31 | 0
32 |
33 |
34 |
35 | 连接
36 |
37 |
38 |
-
39 |
40 |
41 | 添加设备
42 |
43 |
44 |
45 | -
46 |
47 |
48 |
49 |
50 |
51 |
52 | 操作
53 |
54 |
55 | -
56 |
57 |
58 | Qt::Horizontal
59 |
60 |
61 |
62 | -
63 |
64 |
65 | 1
66 |
67 |
68 | 274
69 |
70 |
71 | Qt::Horizontal
72 |
73 |
74 |
75 | -
76 |
77 |
78 | 按住执行
79 |
80 |
81 |
82 | -
83 |
84 |
85 | QListWidget::item { border-bottom: 1px solid black; }
86 |
87 |
88 |
89 | -
90 |
91 |
92 | <html><head/><body><p>突增</p></body></html>
93 |
94 |
95 |
96 | -
97 |
98 |
99 | 1
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 | 应用控制
108 |
109 |
110 | -
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 | RemoteControl
123 | QWidget
124 |
125 | 1
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
--------------------------------------------------------------------------------
/src/global.cpp:
--------------------------------------------------------------------------------
1 | #include "global.h"
2 | #include
3 |
4 | namespace Global {
5 | QList basicWaveNameList;
6 | QList touchWaveNameList;
7 | QString deviceName;
8 | namespace DGLabServices {
9 | namespace DeviceStatus {
10 | QString service;
11 | namespace Characteristic {
12 | QString electric;
13 | }
14 | }
15 | namespace EStimStatus {
16 | QString service;
17 | namespace Characteristic {
18 | QString setup;
19 | QString abpower;
20 | QString waveA;
21 | QString waveB;
22 | }
23 | }
24 | }
25 | bool skipError = false;
26 | bool skipWarning = false;
27 | Remote *remote = new Remote();
28 | QMutex mutex;
29 | QList dglabList;
30 | QList remoteList;
31 | QBluetoothDeviceDiscoveryAgent *bleScanAgent;
32 | void initGlobal() {
33 | bleScanAgent = new QBluetoothDeviceDiscoveryAgent();
34 | bleScanAgent->setLowEnergyDiscoveryTimeout(5000);
35 | libopendglab_ExportedSymbols* libopendglab = libopendglab_symbols();
36 | auto basicWave = libopendglab->kotlin.root.WaveCenter.Companion.getBasicWaveList(libopendglab->kotlin.root.WaveCenter.Companion._instance());
37 | auto basicWaveSize = libopendglab->kotlin.root.getStringArraySize(basicWave);
38 | for(int i = 0; i < basicWaveSize; i++) {
39 | auto item = libopendglab->kotlin.root.getStringArrayItemAt(basicWave, i);
40 | basicWaveNameList.append(QString::fromUtf8(item));
41 | libopendglab->DisposeString(item);
42 | }
43 | auto touchWave = libopendglab->kotlin.root.WaveCenter.Companion.getTouchWaveList(libopendglab->kotlin.root.WaveCenter.Companion._instance());
44 | auto touchWaveSize = libopendglab->kotlin.root.getStringArraySize(touchWave);
45 | for(int i = 0; i < touchWaveSize; i++) {
46 | auto item = libopendglab->kotlin.root.getStringArrayItemAt(touchWave, i);
47 | touchWaveNameList.append(QString::fromUtf8(item));
48 | libopendglab->DisposeString(item);
49 | }
50 | auto dlabName = libopendglab->kotlin.root.OpenDGLab.Device.Companion.getName(libopendglab->kotlin.root.OpenDGLab.Device.Companion._instance());
51 | deviceName = QString::fromUtf8(dlabName);
52 | libopendglab->DisposeString(dlabName);
53 | //DeviceStatus
54 | //Service
55 | auto batteryServiceUuid = libopendglab->kotlin.root.OpenDGLab.DeviceStatus.Companion.getUUID(libopendglab->kotlin.root.OpenDGLab.DeviceStatus.Companion._instance());
56 | Global::DGLabServices::DeviceStatus::service = QString::fromUtf8(batteryServiceUuid);
57 | libopendglab->DisposeString(batteryServiceUuid);
58 | //Characterisitc
59 | auto batteryCharacteristic = libopendglab->kotlin.root.OpenDGLab.DeviceStatus.Electric.Companion.getUUID(
60 | libopendglab->kotlin.root.OpenDGLab.DeviceStatus.Electric.Companion._instance()
61 | );
62 | Global::DGLabServices::DeviceStatus::Characteristic::electric = QString::fromUtf8(batteryCharacteristic);
63 | libopendglab->DisposeString(batteryCharacteristic);
64 | //EStimStatus
65 | //Service
66 | auto estimServiceUuid = libopendglab->kotlin.root.OpenDGLab.EStimStatus.Companion.getUUID(libopendglab->kotlin.root.OpenDGLab.EStimStatus.Companion._instance());
67 | Global::DGLabServices::EStimStatus::service = QString::fromUtf8(estimServiceUuid);
68 | libopendglab->DisposeString(estimServiceUuid);
69 | //Characterisitc
70 | auto setupCharacterisitc = libopendglab->kotlin.root.OpenDGLab.EStimStatus.Setup.Companion.getUUID(
71 | libopendglab->kotlin.root.OpenDGLab.EStimStatus.Setup.Companion._instance()
72 | );
73 | Global::DGLabServices::EStimStatus::Characteristic::setup = QString::fromUtf8(setupCharacterisitc);
74 | libopendglab->DisposeString(setupCharacterisitc);
75 | auto abpowerCharacterisitc = libopendglab->kotlin.root.OpenDGLab.EStimStatus.ABPower.Companion.getUUID(
76 | libopendglab->kotlin.root.OpenDGLab.EStimStatus.ABPower.Companion._instance()
77 | );
78 | Global::DGLabServices::EStimStatus::Characteristic::abpower = QString::fromUtf8(abpowerCharacterisitc);
79 | libopendglab->DisposeString(abpowerCharacterisitc);
80 | auto waveACharacterisitc = libopendglab->kotlin.root.OpenDGLab.EStimStatus.Wave.Companion.getUUIDA(
81 | libopendglab->kotlin.root.OpenDGLab.EStimStatus.Wave.Companion._instance()
82 | );
83 | Global::DGLabServices::EStimStatus::Characteristic::waveA = QString::fromUtf8(waveACharacterisitc);
84 | libopendglab->DisposeString(waveACharacterisitc);
85 | auto waveBCharacterisitc = libopendglab->kotlin.root.OpenDGLab.EStimStatus.Wave.Companion.getUUIDB(
86 | libopendglab->kotlin.root.OpenDGLab.EStimStatus.Wave.Companion._instance()
87 | );
88 | Global::DGLabServices::EStimStatus::Characteristic::waveB = QString::fromUtf8(waveBCharacterisitc);
89 | libopendglab->DisposeString(waveBCharacterisitc);
90 |
91 | }
92 |
93 | QList getWaveList()
94 | {
95 | QList list;
96 | list.append(basicWaveNameList);
97 | list.append(touchWaveNameList);
98 | return list;
99 | }
100 |
101 | }
102 |
--------------------------------------------------------------------------------
/src/mainWindow.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include "mainWindow.h"
3 | #include "global.h"
4 | #include "scanDevice.h"
5 | #include "ui_mainWindow.h"
6 | #include
7 | #include
8 | #include
9 | #include
10 | #include
11 | #include
12 | MainWindow::MainWindow(QWidget *parent)
13 | : QMainWindow(parent)
14 | , ui(new Ui::MainWindow)
15 | , mvcController(new MainWindowController(this))
16 | {
17 | ui->setupUi(this);
18 | connect(ui->btnAddDevice, &QPushButton::clicked, this, &MainWindow::addDevice);
19 | ui->listDevice->setContextMenuPolicy(Qt::ContextMenuPolicy::CustomContextMenu);
20 | connect(ui->listDevice, &QWidget::customContextMenuRequested, this, &MainWindow::showContextMenu);
21 | connect(ui->allBoost, &QSlider::valueChanged, this, &MainWindow::allBoostChanged);
22 | connect(ui->btn_boost, &QPushButton::pressed, this, [this](){
23 | startAllBoost(ui->allBoost->value());
24 | });
25 | connect(ui->btn_boost, &QPushButton::released, this, [this](){
26 | stopAllBoost();
27 | });
28 | loadDevice();
29 | }
30 |
31 | MainWindow::~MainWindow()
32 | {
33 | delete ui;
34 | }
35 |
36 | void MainWindow::addDevice() {
37 | auto scanDeviceDialog = new ScanDevice(this);
38 | connect(scanDeviceDialog, &ScanDevice::needRefresh, this, &MainWindow::refreshDeviceList);
39 | scanDeviceDialog->show();
40 | }
41 |
42 | void MainWindow::showContextMenu(const QPoint &pos) {
43 | QPoint globalPos = ui->listDevice->mapToGlobal(pos);
44 | QMenu myMenu;
45 | myMenu.addAction("删除", this, &MainWindow::removeDeviceItem);
46 | myMenu.exec(globalPos);
47 | }
48 | void MainWindow::removeDeviceItem() {
49 | QMutexLocker locker(&Global::mutex);
50 | int index = ui->listDevice->currentRow();
51 | if (index < 0) {
52 | return;
53 | }
54 | auto device = Global::dglabList[index];
55 | if(device->getBLEController()->state() != QLowEnergyController::ControllerState::UnconnectedState) {
56 | QMessageBox::warning(this, "通知", "您不能移除一个已经连接的设备,请关闭设备后再试。");
57 | return;
58 | }
59 | QListWidgetItem *item = ui->listDevice->takeItem(index);
60 | QListWidgetItem *itemOperator = ui->listOperator->takeItem(index);
61 | Global::dglabList.removeAt(index);
62 | delete item;
63 | delete itemOperator;
64 | delete device;
65 | ui->listDevice->update();
66 | saveDevice();
67 | }
68 | void MainWindow::showEvent(QShowEvent*) {
69 | //
70 | }
71 | void MainWindow::closeEvent(QCloseEvent*) {
72 |
73 | }
74 | void MainWindow::saveDevice() {
75 | QFile saveFile(QStringLiteral("save.json"));
76 | if (!saveFile.open(QIODevice::WriteOnly)) {
77 | return;
78 | }
79 | QJsonObject savedObject;
80 | savedObject["platform"] =
81 | #ifdef Q_OS_MACOS
82 | "macos"
83 | #else
84 | "compatible"
85 | #endif
86 | ;
87 | QJsonArray deviceArray;
88 | for (auto device: (Global::dglabList)) {
89 | QJsonObject bleObject;
90 | bleObject["address"] = device->getDeviceAddressOrUuid();
91 | bleObject["id"] = device->getID();
92 | deviceArray.append(bleObject);
93 | }
94 | savedObject["devices"] = deviceArray;
95 | QJsonDocument saveDoc(savedObject);
96 | saveFile.write(saveDoc.toJson());
97 | }
98 | void MainWindow::loadDevice() {
99 | libopendglab_ExportedSymbols *core = libopendglab_symbols();
100 | auto instance = core->kotlin.root.OpenDGLab.Device.Companion._instance();
101 | auto name = QString::fromUtf8(
102 | core->kotlin.root.OpenDGLab.Device.Companion.getName(
103 | instance
104 | )
105 | );
106 | QFile loadFile(QStringLiteral("save.json"));
107 | if (!loadFile.open(QIODevice::ReadOnly)) {
108 | return;
109 | }
110 | QByteArray savedDevice = loadFile.readAll();
111 | QJsonDocument loadDoc(QJsonDocument::fromJson(savedDevice));
112 | QJsonObject json = loadDoc.object();
113 | QString platform = json["platform"].toString();
114 | #ifdef Q_OS_MACOS
115 | if (platform == "compatible")
116 | #else
117 | if (platform == "macos")
118 | #endif
119 | {
120 | return;
121 | }
122 | QJsonArray deviceArray = json["devices"].toArray();
123 | for (auto array: deviceArray) {
124 | QJsonObject obj = array.toObject();
125 | QString address = obj["address"].toString();
126 | QString id = obj["id"].toString();
127 | QBluetoothDeviceInfo info = QBluetoothDeviceInfo(
128 | #ifdef Q_OS_MACOS
129 | QBluetoothUuid(address)
130 | #else
131 | QBluetoothAddress(address)
132 | #endif
133 | , name, 0);
134 | DGLabDevice *device = new DGLabDevice(info);
135 | Global::dglabList.append(device);
136 | }
137 | this->refreshDeviceList();
138 | }
139 | void MainWindow::refreshDeviceList() {
140 | for (auto device: Global::dglabList) {
141 | if (!device->isShownOnUi()) {
142 | device->setShownOnUi();
143 | auto listDevice = new QListWidgetItem(ui->listDevice);
144 | listDevice->setSizeHint(device->getUiDeviceItem()->sizeHint());
145 | ui->listDevice->addItem(listDevice);
146 | ui->listDevice->setItemWidget(listDevice, device->getUiDeviceItem());
147 | auto listOperator = new QListWidgetItem(ui->listOperator);
148 | listOperator->setSizeHint(device->getUiDeviceOperator()->sizeHint());
149 | ui->listOperator->addItem(listOperator);
150 | ui->listOperator->setItemWidget(listOperator, device->getUiDeviceOperator());
151 | }
152 | }
153 | saveDevice();
154 | }
155 | void MainWindow::allBoostChanged(int value) {
156 | ui->lbl_boost->setText(QString::number(value));
157 | }
158 |
159 | void MainWindow::startAllBoost(int boost)
160 | {
161 | ui->allBoost->setEnabled(false);
162 | mvcController->startAllBoost(boost);
163 | }
164 |
165 | void MainWindow::stopAllBoost()
166 | {
167 | ui->allBoost->setEnabled(true);
168 | mvcController->stopAllBoost();
169 | }
170 |
--------------------------------------------------------------------------------
/src/remoteControl.ui:
--------------------------------------------------------------------------------
1 |
2 |
3 | RemoteControl
4 |
5 |
6 |
7 | 0
8 | 0
9 | 621
10 | 398
11 |
12 |
13 |
14 | Form
15 |
16 |
17 | -
18 |
19 |
20 | 启动远程服务器
21 |
22 |
23 |
24 | -
25 |
26 |
27 | WS 端口号
28 |
29 |
30 | Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
31 |
32 |
33 |
34 | -
35 |
36 |
37 | 1024
38 |
39 |
40 | 65535
41 |
42 |
43 | 11240
44 |
45 |
46 |
47 | -
48 |
49 |
50 | 1024
51 |
52 |
53 | 65535
54 |
55 |
56 | 11241
57 |
58 |
59 |
60 | -
61 |
62 |
63 | TCP 端口号
64 |
65 |
66 | Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
67 |
68 |
69 |
70 | -
71 |
72 |
-
73 |
74 |
75 | true
76 |
77 |
78 |
79 | -
80 |
81 |
-
82 |
83 |
84 |
85 | 0
86 | 0
87 |
88 |
89 |
90 |
91 | 300
92 | 0
93 |
94 |
95 |
96 |
97 |
98 |
99 | Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop
100 |
101 |
102 |
103 | -
104 |
105 |
106 | 解除授权
107 |
108 |
109 |
110 | -
111 |
112 |
113 |
114 | 0
115 | 0
116 |
117 |
118 |
119 |
120 | 300
121 | 0
122 |
123 |
124 |
125 |
126 |
127 |
128 | Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop
129 |
130 |
131 |
132 | -
133 |
134 |
135 | -
136 |
137 |
138 |
139 | 0
140 | 0
141 |
142 |
143 |
144 | 连入名
145 |
146 |
147 | Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop
148 |
149 |
150 |
151 | -
152 |
153 |
154 |
155 | 0
156 | 0
157 |
158 |
159 |
160 | 连入 ID
161 |
162 |
163 | Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop
164 |
165 |
166 |
167 | -
168 |
169 |
170 |
171 | 0
172 | 0
173 |
174 |
175 |
176 | 无状态
177 |
178 |
179 | Qt::AlignCenter
180 |
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 |
--------------------------------------------------------------------------------
/src/deviceOperator.cpp:
--------------------------------------------------------------------------------
1 | #include "deviceOperator.h"
2 | #include "ui_deviceOperator.h"
3 | #include "global.h"
4 |
5 | DeviceOperator::DeviceOperator(QString id, QWidget *parent) :
6 | QWidget(parent),
7 | ui(new Ui::DeviceOperator)
8 | {
9 | ui->setupUi(this);
10 | ui->lbl_id->setText(id);
11 | for (auto wave: Global::basicWaveNameList) {
12 | ui->cb_a_wave->addItem(wave);
13 | ui->cb_b_wave->addItem(wave);
14 | }
15 | ui->cb_a_wave->insertSeparator(ui->cb_a_wave->count());
16 | ui->cb_b_wave->insertSeparator(ui->cb_b_wave->count());
17 | for (auto wave: Global::touchWaveNameList) {
18 | ui->cb_a_wave->addItem(wave);
19 | ui->cb_b_wave->addItem(wave);
20 | }
21 | ui->cb_a_wave->insertSeparator(ui->cb_a_wave->count());
22 | ui->cb_b_wave->insertSeparator(ui->cb_b_wave->count());
23 | ui->cb_a_wave->addItem("外部输入");
24 | ui->cb_b_wave->addItem("外部输入");
25 | connect(ui->chk_a_auto, &QCheckBox::stateChanged, [this](int state){
26 | ui->cb_a_wave->setEnabled(!state);
27 | ui->sp_a_auto_timer->setEnabled(!state);
28 | if (!state) {
29 | autoATimer = 0;
30 | }
31 | });
32 | connect(ui->chk_b_auto, &QCheckBox::stateChanged, [this](int state){
33 | ui->cb_b_wave->setEnabled(!state);
34 | ui->sp_b_auto_timer->setEnabled(!state);
35 | if (!state) {
36 | autoBTimer = 0;
37 | }
38 | });
39 | connect(ui->silde_a_step, &QSlider::valueChanged, [this](int value){
40 | ui->lbl_a_step->setText(QString::number(value));
41 | });
42 | connect(ui->silde_b_step, &QSlider::valueChanged, [this](int value){
43 | ui->lbl_b_step->setText(QString::number(value));
44 | });
45 | connect(ui->sb_a_boost, &QSlider::valueChanged, [this](int value){
46 | ui->lbl_a_boost->setText(QString::number(value));
47 | });
48 | connect(ui->sb_b_boost, &QSlider::valueChanged, [this](int value){
49 | ui->lbl_b_boost->setText(QString::number(value));
50 | });
51 |
52 | connect(ui->btn_a_increase, &QPushButton::clicked, [this](bool){
53 | emit changePower(ui->lcd_a_strength->intValue() + ui->silde_a_step->value(), ui->lcd_b_strength->intValue());
54 | });
55 | connect(ui->btn_a_decrease, &QPushButton::clicked, [this](bool){
56 | emit changePower(ui->lcd_a_strength->intValue() - ui->silde_a_step->value(), ui->lcd_b_strength->intValue());
57 | });
58 | connect(ui->btn_a_stop, &QPushButton::clicked, [this](bool){
59 | emit changePower(0, ui->lcd_b_strength->intValue());
60 | });
61 | connect(ui->btn_b_increase, &QPushButton::clicked, [this](bool){
62 | emit changePower(ui->lcd_a_strength->intValue(), ui->lcd_b_strength->intValue() + ui->silde_b_step->value());
63 | });
64 | connect(ui->btn_b_decrease, &QPushButton::clicked, [this](bool){
65 | emit changePower(ui->lcd_a_strength->intValue(), ui->lcd_b_strength->intValue() - ui->silde_b_step->value());
66 | });
67 | connect(ui->btn_b_stop, &QPushButton::clicked, [this](bool){
68 | emit changePower(ui->lcd_a_strength->intValue(), 0);
69 | });
70 |
71 | connect(ui->cb_a_wave, &QComboBox::currentTextChanged, [this](const QString & value){
72 | emit changeWave(DeviceStateEnum::DeviceChannel::CHANNEL_A, value);
73 | });
74 | connect(ui->cb_b_wave, &QComboBox::currentTextChanged, [this](const QString & value){
75 | emit changeWave(DeviceStateEnum::DeviceChannel::CHANNEL_B, value);
76 | });
77 | connect(ui->btn_a_boost, &QPushButton::pressed, [this](){
78 | ui->sb_a_boost->setEnabled(false);
79 | emit startBoost(DeviceStateEnum::DeviceChannel::CHANNEL_A, ui->sb_a_boost->value());
80 | });
81 | connect(ui->btn_b_boost, &QPushButton::pressed, [this](){
82 | ui->sb_b_boost->setEnabled(false);
83 | emit startBoost(DeviceStateEnum::DeviceChannel::CHANNEL_B, ui->sb_b_boost->value());
84 | });
85 | connect(ui->btn_a_boost, &QPushButton::released, [this](){
86 | ui->sb_a_boost->setEnabled(true);
87 | emit stopBoost(DeviceStateEnum::DeviceChannel::CHANNEL_A, ui->sb_a_boost->value());
88 | });
89 | connect(ui->btn_b_boost, &QPushButton::released, [this](){
90 | ui->sb_b_boost->setEnabled(true);
91 | emit stopBoost(DeviceStateEnum::DeviceChannel::CHANNEL_B, ui->sb_b_boost->value());
92 | });
93 | }
94 |
95 | DeviceOperator::~DeviceOperator()
96 | {
97 | delete ui;
98 | }
99 |
100 | QString DeviceOperator::getWaveA()
101 | {
102 | return ui->cb_a_wave->currentText();
103 | }
104 | QString DeviceOperator::getWaveB()
105 | {
106 | return ui->cb_b_wave->currentText();
107 | }
108 |
109 | void DeviceOperator::setWaveA(QString str) {
110 | if (ui->cb_a_wave->findText(str) >= 0) {
111 | ui->cb_a_wave->setCurrentText(str);
112 | }
113 | }
114 | void DeviceOperator::setWaveB(QString str) {
115 | if (ui->cb_b_wave->findText(str) >= 0) {
116 | ui->cb_b_wave->setCurrentText(str);
117 | }
118 | }
119 |
120 | void DeviceOperator::checkIfNeedChangeWave()
121 | {
122 | if (ui->chk_a_auto->isChecked()) {
123 | autoATimer++;
124 | if (autoATimer > ui->sp_a_auto_timer->value()) {
125 | auto next = random.bounded(0, ui->cb_a_wave->count());
126 | ui->cb_a_wave->setCurrentIndex(next);
127 | autoATimer = 0;
128 | }
129 | }
130 | if (ui->chk_b_auto->isChecked()) {
131 | autoBTimer++;
132 | if (autoBTimer > ui->sp_b_auto_timer->value()) {
133 | auto next = random.bounded(0, ui->cb_b_wave->count());
134 | ui->cb_b_wave->setCurrentIndex(next);
135 | autoBTimer = 0;
136 | }
137 | }
138 | }
139 |
140 | void DeviceOperator::setPower(DeviceStateEnum::DeviceChannel channel, int level) {
141 | switch (channel) {
142 | case DeviceStateEnum::DeviceChannel::CHANNEL_A:
143 | ui->lcd_a_strength->display(level);
144 | break;
145 | case DeviceStateEnum::DeviceChannel::CHANNEL_B:
146 | ui->lcd_b_strength->display(level);
147 | break;
148 | }
149 | }
150 | void DeviceOperator::setAutoChange(DeviceStateEnum::DeviceChannel channel, bool autoChange) {
151 | switch (channel) {
152 | case DeviceStateEnum::DeviceChannel::CHANNEL_A:
153 | ui->chk_a_auto->setChecked(autoChange);
154 | break;
155 | case DeviceStateEnum::DeviceChannel::CHANNEL_B:
156 | ui->chk_b_auto->setChecked(autoChange);
157 | break;
158 | }
159 | }
160 | bool DeviceOperator::getAutoChange(DeviceStateEnum::DeviceChannel channel) {
161 | switch (channel) {
162 | case DeviceStateEnum::DeviceChannel::CHANNEL_A:
163 | return ui->chk_a_auto->isChecked();
164 | case DeviceStateEnum::DeviceChannel::CHANNEL_B:
165 | return ui->chk_b_auto->isChecked();
166 | }
167 | return false;
168 | }
169 |
--------------------------------------------------------------------------------
/src/remote.cpp:
--------------------------------------------------------------------------------
1 | #include "remote.h"
2 | #include "global.h"
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 | #ifndef __linux__
10 | #include
11 | #endif
12 | Remote::Remote(QObject *parent) : QObject(parent), tcpPort(11240), wsPort(11241)
13 | {
14 | load();
15 | tcpServer = new QTcpServer(this);
16 | #ifndef __linux__
17 | wsServer = new QWebSocketServer("127.0.0.1", QWebSocketServer::SslMode::NonSecureMode, this);
18 | auto *cors = new QWebSocketCorsAuthenticator("127.0.0.1");
19 | cors->setAllowed(true);
20 | wsServer->originAuthenticationRequired(cors);
21 | connect(wsServer, &QWebSocketServer::originAuthenticationRequired, [](QWebSocketCorsAuthenticator *){
22 | return true;
23 | });
24 | connect(wsServer, &QWebSocketServer::newConnection, this, &Remote::wsNewConnection);
25 | #endif
26 | connect(tcpServer, &QTcpServer::newConnection, this, &Remote::tcpNewConnection);
27 | }
28 |
29 | Remote::~Remote()
30 | {
31 | stop();
32 | }
33 |
34 | bool Remote::isStarted() {
35 | return isStart;
36 | }
37 |
38 | bool Remote::start() {
39 | bool tcpStart = false;
40 | bool wsStart = false;
41 | if (!isStart) {
42 | if(tcpServer->listen(QHostAddress::LocalHost, tcpPort)){
43 | tcpStart = true;
44 | }
45 | #ifndef __linux__
46 | if (wsServer->listen(QHostAddress::LocalHost, wsPort)) {
47 | wsStart = true;
48 | }
49 | #else
50 | wsStart = true;
51 | #endif
52 | if (tcpStart && wsStart){
53 | isStart = true;
54 | if (autoRemoteProtocolPinger == nullptr) {
55 | autoRemoteProtocolPinger = new AutoRemoteProtocolPinger();
56 | }
57 | if (!autoRemoteProtocolPinger->isRunning()) {
58 | autoRemoteProtocolPinger->start();
59 | }
60 | return true;
61 | }
62 | if (tcpStart) tcpServer->close();
63 | #ifndef __linux__
64 | if (wsStart) wsServer->close();
65 | #endif
66 | return false;
67 | }
68 | return true;
69 | }
70 |
71 | void Remote::stop() {
72 | if (isStart) {
73 | if (autoRemoteProtocolPinger != nullptr) {
74 | if (autoRemoteProtocolPinger->isRunning()) {
75 | autoRemoteProtocolPinger->quit();
76 | delete autoRemoteProtocolPinger;
77 | autoRemoteProtocolPinger = nullptr;
78 | }
79 | }
80 | tcpServer->close();
81 | #ifndef __linux__
82 | wsServer->close();
83 | #endif
84 | for(auto r: Global::remoteList) {
85 | r->close();
86 | }
87 | isStart = false;
88 | }
89 | }
90 |
91 | void Remote::setWSPort(int port) {
92 | if (!isStart) wsPort = port;
93 | }
94 |
95 | void Remote::setTcpPort(int port) {
96 | if (!isStart) tcpPort = port;
97 | }
98 |
99 | void Remote::tcpNewConnection() {
100 | auto socket = tcpServer->nextPendingConnection();
101 | auto* client = new RemoteClient(socket);
102 | connect(client, &RemoteClient::doAuth, this, &Remote::doAuth);
103 | Global::remoteList.push_back(client);
104 | }
105 | void Remote::wsNewConnection() {
106 | #ifndef __linux__
107 | auto socket = wsServer->nextPendingConnection();
108 | auto* client = new RemoteClient(socket);
109 | connect(client, &RemoteClient::doAuth, this, &Remote::doAuth);
110 | Global::remoteList.push_back(client);
111 | #endif
112 | }
113 |
114 | void Remote::doAuth(RemoteClient* client, QString appName, QString appId, QString token) {
115 | if (appName.isEmpty() || appId.isEmpty()) client->sendConnected("", "", false);
116 | if (preAuth.contains(appId)) {
117 | auto pA = preAuth[appId];
118 | if (pA == token) {
119 | client->sendConnected(appId, token, true);
120 | } else {
121 | QMessageBox::StandardButton reply;
122 | reply = QMessageBox::question(nullptr, "应用连接", QString("%1 正在请求连接 OpenDGLab Desktop 以控制您的设备。").arg(appName), QMessageBox::Yes|QMessageBox::No);
123 | if (reply == QMessageBox::Yes) {
124 | auto nToken = getRandomString();
125 | client->sendConnected(appId, nToken, true);
126 | Global::remote->preAuth[appId] = nToken;
127 | Global::remote->preAuthName[appId] = appName;
128 | } else {
129 | client->sendConnected("", "", false);
130 | }
131 | }
132 | } else {
133 | QMessageBox::StandardButton reply;
134 | reply = QMessageBox::question(nullptr, "应用连接", QString("%1 正在请求连接 OpenDGLab Desktop 以控制您的设备。").arg(appName), QMessageBox::Yes|QMessageBox::No);
135 | if (reply == QMessageBox::Yes) {
136 | auto nToken = getRandomString();
137 | client->sendConnected(appId, nToken, true);
138 | Global::remote->preAuth[appId] = nToken;
139 | Global::remote->preAuthName[appId] = appName;
140 | } else {
141 | client->sendConnected("", "", false);
142 | }
143 | }
144 | save();
145 | emit stateChange();
146 | }
147 |
148 | QString Remote::getRandomString() {
149 | const QString possibleCharacters("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789");
150 | const int randomStringLength = 16;
151 |
152 | QString randomString;
153 | for(int i=0; i:/utf-8>")
48 | add_compile_options("$<$:/utf-8>")
49 |
50 | set(CMAKE_INCLUDE_CURRENT_DIR TRUE)
51 | find_package(Protobuf REQUIRED)
52 | set_property(SOURCE proto/app.pb.h PROPERTY SKIP_AUTOGEN ON)
53 | set_property(SOURCE proto/app.pb.cc PROPERTY SKIP_AUTOGEN ON)
54 | set_target_properties(protobuf::libprotobuf PROPERTIES
55 | INTERFACE_COMPILE_DEFINITIONS "PROTOBUF_USE_DLLS"
56 | )
57 |
58 | if(Qt5_FOUND AND WIN32 AND TARGET Qt5::qmake AND NOT TARGET Qt5::windeployqt)
59 | get_target_property(_qt5_qmake_location Qt5::qmake IMPORTED_LOCATION)
60 |
61 | execute_process(
62 | COMMAND "${_qt5_qmake_location}" -query QT_INSTALL_PREFIX
63 | RESULT_VARIABLE return_code
64 | OUTPUT_VARIABLE qt5_install_prefix
65 | OUTPUT_STRIP_TRAILING_WHITESPACE
66 | )
67 | set(imported_location "${qt5_install_prefix}/bin/windeployqt.exe")
68 |
69 | if(EXISTS ${imported_location})
70 | add_executable(Qt5::windeployqt IMPORTED)
71 |
72 | set_target_properties(Qt5::windeployqt PROPERTIES
73 | IMPORTED_LOCATION ${imported_location}
74 | )
75 | endif()
76 | endif()
77 | file(GLOB_RECURSE OpenDGLabDesktop_SRC
78 | "src/*.h"
79 | "src/*.cpp"
80 | "src/*.ui"
81 | "proto/*.proto"
82 | )
83 | add_library(libopendglab STATIC IMPORTED)
84 | set_property(TARGET libopendglab PROPERTY IMPORTED_LOCATION ${OPENDGLABCORE_LIB})
85 | if (WIN32)
86 | set(APP_ICON_RESOURCE_WINDOWS "${CMAKE_CURRENT_SOURCE_DIR}/res/icon.rc")
87 | add_executable(OpenDGLab-Desktop WIN32 ${OpenDGLabDesktop_SRC} ${appProto_PROTO_SRCS} res/image.qrc ${APP_ICON_RESOURCE_WINDOWS})
88 | target_link_libraries(OpenDGLab-Desktop PRIVATE Qt${QT_VERSION_MAJOR}::Widgets Qt${QT_VERSION_MAJOR}::Bluetooth Qt${QT_VERSION_MAJOR}::Network Qt${QT_VERSION_MAJOR}::WebSockets libopendglab protobuf::libprotobuf)
89 | elseif(LINUX)
90 | add_executable(OpenDGLab-Desktop ${OpenDGLabDesktop_SRC} ${appProto_PROTO_SRCS} res/image.qrc)
91 | target_link_libraries(OpenDGLab-Desktop PRIVATE Qt${QT_VERSION_MAJOR}::Widgets Qt${QT_VERSION_MAJOR}::Bluetooth Qt${QT_VERSION_MAJOR}::Network libopendglab protobuf::libprotobuf Threads::Threads ${CMAKE_DL_LIBS})
92 | elseif(APPLE)
93 | add_executable(OpenDGLab-Desktop MACOSX_BUNDLE ${OpenDGLabDesktop_SRC} ${appProto_PROTO_SRCS} res/image.qrc ${APP_ICON_MACOSX})
94 | target_link_libraries(OpenDGLab-Desktop PRIVATE Qt${QT_VERSION_MAJOR}::Widgets Qt${QT_VERSION_MAJOR}::Bluetooth Qt${QT_VERSION_MAJOR}::Network Qt${QT_VERSION_MAJOR}::WebSockets libopendglab protobuf::libprotobuf ${CMAKE_DL_LIBS} "-framework AppKit" "-framework CoreData" "-framework Foundation")
95 | endif()
96 | target_include_directories(OpenDGLab-Desktop PUBLIC "src")
97 |
98 | protobuf_generate(TARGET OpenDGLab-Desktop)
99 |
100 | if(TARGET Qt5::windeployqt)
101 | if("${CMAKE_SIZEOF_VOID_P}" STREQUAL "4")
102 | set(WIN_ARCH "x86")
103 | else()
104 | set(WIN_ARCH "x64")
105 | endif()
106 | add_custom_command(TARGET OpenDGLab-Desktop
107 | POST_BUILD
108 | COMMAND ${CMAKE_COMMAND} -E remove_directory "${CMAKE_CURRENT_BINARY_DIR}/OpenDGLab-Desktop-${WIN_ARCH}"
109 | COMMAND set PATH=%PATH%$${qt5_install_prefix}/bin
110 | COMMAND Qt5::windeployqt --dir "${CMAKE_CURRENT_BINARY_DIR}/OpenDGLab-Desktop-${WIN_ARCH}" "$/$"
111 | )
112 | install(
113 | TARGETS OpenDGLab-Desktop
114 | ARCHIVE DESTINATION ${CMAKE_SOURCE_DIR}/target/bin
115 | LIBRARY DESTINATION ${CMAKE_SOURCE_DIR}/target/bin
116 | RUNTIME DESTINATION ${CMAKE_SOURCE_DIR}/target/bin
117 | PUBLIC_HEADER DESTINATION ${CMAKE_SOURCE_DIR}/target/bin
118 | )
119 |
120 | # copy deployment directory during installation
121 | install(
122 | DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/OpenDGLab-Desktop-${WIN_ARCH}/"
123 | DESTINATION ${OpenDGLab-Desktop_BINARY_DIR}
124 | )
125 |
126 | set(CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS_SKIP TRUE)
127 |
128 | include(InstallRequiredSystemLibraries)
129 |
130 | install(
131 | PROGRAMS ${CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS}
132 | DESTINATION ${OpenDGLab-Desktop_BINARY_DIR}
133 | )
134 | add_custom_command(TARGET OpenDGLab-Desktop POST_BUILD
135 | COMMAND ${CMAKE_COMMAND} -E copy_if_different
136 | ${OPENDGLABCORE_BIN}
137 | "$/OpenDGLab-Desktop-${WIN_ARCH}/")
138 | add_custom_command(TARGET OpenDGLab-Desktop POST_BUILD
139 | COMMAND ${CMAKE_COMMAND} -E copy_if_different
140 | $
141 | "$/OpenDGLab-Desktop-${WIN_ARCH}/")
142 | if(CMAKE_BUILD_TYPE MATCHES DEBUG OR CMAKE_BUILD_TYPE MATCHES Debug)
143 | add_custom_command(TARGET OpenDGLab-Desktop POST_BUILD
144 | COMMAND ${CMAKE_COMMAND} -E copy_if_different
145 | ${CMAKE_CURRENT_BINARY_DIR}/libprotobufd.dll
146 | "$/OpenDGLab-Desktop-${WIN_ARCH}/")
147 | else()
148 | add_custom_command(TARGET OpenDGLab-Desktop POST_BUILD
149 | COMMAND ${CMAKE_COMMAND} -E copy_if_different
150 | ${CMAKE_CURRENT_BINARY_DIR}/libprotobuf.dll
151 | "$/OpenDGLab-Desktop-${WIN_ARCH}/")
152 | endif()
153 | add_custom_command(TARGET OpenDGLab-Desktop POST_BUILD
154 | COMMAND ${CMAKE_COMMAND} -E tar "cfv" "OpenDGLab-Desktop-${WIN_ARCH}.zip" --format=zip
155 | "$/OpenDGLab-Desktop-${WIN_ARCH}")
156 | endif()
157 |
--------------------------------------------------------------------------------
/.github/workflows/tags.yml:
--------------------------------------------------------------------------------
1 | name: Build Release
2 | on:
3 | create:
4 | tags:
5 | - v*
6 | jobs:
7 | create_release:
8 | name: Create release
9 | runs-on: ubuntu-latest
10 | outputs:
11 | upload_url: ${{ steps.create_release.outputs.upload_url }}
12 | version: ${{ steps.get_version.outputs.version }}
13 | steps:
14 | - name: Get latest release version number
15 | id: get_version
16 | uses: battila7/get-version-action@v2
17 | - name: Create Release
18 | id: create_release
19 | uses: actions/create-release@v1
20 | env:
21 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
22 | with:
23 | tag_name: ${{ github.ref }}
24 | release_name: OpenDGLab Desktop ${{ steps.get_version.outputs.version }}
25 | body: |
26 | OpenDGLab Desktop ${{ steps.get_version.outputs.version }}
27 | draft: false
28 | prerelease: false
29 | build_windows:
30 | strategy:
31 | matrix:
32 | qt_ver: [5.15.1]
33 | os: [windows-latest]
34 | qt_arch: [win64_msvc2019_64, win32_msvc2019]
35 | qt_target: ['desktop']
36 | include:
37 | - qt_arch: win64_msvc2019_64
38 | msvc_arch: x64
39 | qt_arch_install: msvc2019_64
40 | - qt_arch: win32_msvc2019
41 | msvc_arch: x86
42 | qt_arch_install: msvc2019
43 | needs: [create_release]
44 | runs-on: ${{ matrix.os }}
45 | steps:
46 | - name: Cache Qt
47 | id: windows-cache-qt
48 | uses: actions/cache@v1
49 | with:
50 | path: ../Qt/${{matrix.qt_ver}}/${{matrix.qt_arch_install}}
51 | key: ${{ runner.os }}-Qt/${{matrix.qt_ver}}/${{matrix.qt_arch}}
52 | - name: Install Qt
53 | uses: jurplel/install-qt-action@v2.11.1
54 | with:
55 | version: ${{ matrix.qt_ver }}
56 | target: ${{ matrix.qt_target }}
57 | arch: ${{ matrix.qt_arch }}
58 | cached: ${{steps.windows-cache-qt.outputs.cache-hit }}
59 | - name: Checkout
60 | uses: actions/checkout@v1
61 | - name: Get OpenDGLab Core Windows x64
62 | if: matrix.msvc_arch == 'x64'
63 | shell: powershell
64 | run: |
65 | Invoke-WebRequest https://github.com/OpenDGLab/OpenDGLab-Core/releases/latest/download/OpenDGLab-Core-Windows-X64.zip -OutFile OpenDGLab-Core-Windows-X64.zip
66 | Expand-Archive OpenDGLab-Core-Windows-X64.zip -DestinationPath OpenDGLab-Core-Unzipped
67 | New-Item opendglab-core/header -ItemType Directory -ea 0
68 | New-Item opendglab-core/bin -ItemType Directory -ea 0
69 | Copy-Item -Path "OpenDGLab-Core-Unzipped/releaseShared/libopendglab_api.h" -Destination "opendglab-core/header/"
70 | Copy-Item -Path "OpenDGLab-Core-Unzipped/releaseShared/libopendglab.dll" -Destination "opendglab-core/bin/"
71 | Copy-Item -Path "OpenDGLab-Core-Unzipped/releaseShared/libopendglab.def" -Destination "opendglab-core/bin/"
72 | - name: Get OpenDGLab Core Windows x86
73 | if: matrix.msvc_arch == 'x86'
74 | shell: powershell
75 | run: |
76 | Invoke-WebRequest https://github.com/OpenDGLab/OpenDGLab-Core/releases/latest/download/OpenDGLab-Core-Windows-X86.zip -OutFile OpenDGLab-Core-Windows-X86.zip
77 | Expand-Archive OpenDGLab-Core-Windows-X86.zip -DestinationPath OpenDGLab-Core-Unzipped
78 | New-Item opendglab-core/header -ItemType Directory -ea 0
79 | New-Item opendglab-core/bin -ItemType Directory -ea 0
80 | Copy-Item -Path "OpenDGLab-Core-Unzipped/releaseShared/libopendglab_api.h" -Destination "opendglab-core/header/"
81 | Copy-Item -Path "OpenDGLab-Core-Unzipped/releaseShared/libopendglab.dll" -Destination "opendglab-core/bin/"
82 | Copy-Item -Path "OpenDGLab-Core-Unzipped/releaseShared/libopendglab.def" -Destination "opendglab-core/bin/"
83 | - name: Get OpenDGLab OpenProtocol file
84 | shell: powershell
85 | run: |
86 | Invoke-WebRequest https://raw.githubusercontent.com/OpenDGLab/OpenDGLab-OpenProtocol/master/app.proto -OutFile proto/app.proto
87 | - name: Get latest CMake and ninja
88 | uses: lukka/get-cmake@latest
89 | - name: Get vcpkg
90 | uses: lukka/run-vcpkg@v6
91 | with:
92 | setupOnly: true
93 | vcpkgDirectory: '${{ github.workspace }}/vcpkg'
94 | vcpkgGitCommitId: 'c54abfafbe0051075952c507da1f1ec234875e05'
95 | - name: Install protobuf
96 | shell: cmd
97 | env:
98 | vc_arch: ${{ matrix.msvc_arch }}
99 | run: |
100 | %VCPKG_ROOT%/vcpkg install protobuf:%vc_arch%-windows
101 | - name: Windows Build
102 | shell: cmd
103 | env:
104 | vc_arch: ${{ matrix.msvc_arch }}
105 | run: |
106 | call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" %vc_arch%
107 | cd opendglab-core/bin
108 | lib /def:libopendglab.def /out:libopendglab.lib /machine:%vc_arch%
109 | cd ../..
110 | mkdir build
111 | cd build
112 | cmake -G "Ninja" -DCMAKE_BUILD_TYPE=MinSizeRel -DCMAKE_TOOLCHAIN_FILE=%VCPKG_ROOT%\scripts\buildsystems\vcpkg.cmake ..
113 | cmake --build . --target all
114 | cd ..
115 | - name: Upload Release Asset Windows x86
116 | uses: actions/upload-release-asset@v1
117 | if: matrix.msvc_arch == 'x86'
118 | env:
119 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
120 | with:
121 | upload_url: ${{ needs.create_release.outputs.upload_url }}
122 | asset_path: ./build/OpenDGLab-Desktop-x86.zip
123 | asset_name: OpenDGLab-Desktop-Windows-x86.zip
124 | asset_content_type: application/zip
125 | - name: Upload Release Asset Windows x64
126 | uses: actions/upload-release-asset@v1
127 | if: matrix.msvc_arch == 'x64'
128 | env:
129 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
130 | with:
131 | upload_url: ${{ needs.create_release.outputs.upload_url }}
132 | asset_path: ./build/OpenDGLab-Desktop-x64.zip
133 | asset_name: OpenDGLab-Desktop-Windows-x64.zip
134 | asset_content_type: application/zip
135 | build_linux:
136 | strategy:
137 | matrix:
138 | qt_ver: [5.15.1]
139 | os: [ubuntu-latest]
140 | needs: [create_release]
141 | runs-on: ${{ matrix.os }}
142 | steps:
143 | - name: Install Qt
144 | uses: jurplel/install-qt-action@v2.11.1
145 | with:
146 | version: ${{ matrix.qt_ver }}
147 | - name: Install Package...
148 | run: |
149 | sudo apt install -y unzip libxcb-* libgl1-mesa-dev
150 | - name: Checkout
151 | uses: actions/checkout@v1
152 | - name: Get OpenDGLab Core Linux x64
153 | shell: bash
154 | run: |
155 | wget https://github.com/OpenDGLab/OpenDGLab-Core/releases/latest/download/OpenDGLab-Core-Linux-X64.zip
156 | unzip OpenDGLab-Core-Linux-X64.zip -d OpenDGLab-Core-Unzipped
157 | mkdir -p opendglab-core/header
158 | mkdir -p opendglab-core/bin
159 | cp OpenDGLab-Core-Unzipped/releaseStatic/libopendglab_api.h opendglab-core/header/
160 | cp OpenDGLab-Core-Unzipped/releaseStatic/libopendglab.a opendglab-core/bin/
161 | - name: Get OpenDGLab OpenProtocol file
162 | shell: bash
163 | run: |
164 | wget https://raw.githubusercontent.com/OpenDGLab/OpenDGLab-OpenProtocol/master/app.proto
165 | mv app.proto proto/
166 | - name: Get latest CMake and ninja
167 | uses: lukka/get-cmake@latest
168 | - name: Get vcpkg
169 | uses: lukka/run-vcpkg@v6
170 | with:
171 | setupOnly: true
172 | vcpkgDirectory: '${{ github.workspace }}/vcpkg'
173 | vcpkgGitCommitId: 'c54abfafbe0051075952c507da1f1ec234875e05'
174 | - name: Install protobuf
175 | run: |
176 | $VCPKG_ROOT/vcpkg install protobuf
177 | - name: Linux Build
178 | shell: bash
179 | run: |
180 | mkdir build
181 | cd build
182 | cmake -DCMAKE_BUILD_TYPE=MinSizeRel -DCMAKE_TOOLCHAIN_FILE=$VCPKG_ROOT/scripts/buildsystems/vcpkg.cmake ..
183 | cmake --build . --target all
184 | cd ..
185 | - name: Packaging...
186 | run: |
187 | cd build
188 | wget -O linuxdeployqt https://github.com/probonopd/linuxdeployqt/releases/latest/download/linuxdeployqt-7-x86_64.AppImage
189 | chmod +x linuxdeployqt
190 | mkdir appImage
191 | cp ../external/OpenDGLab.desktop appImage/
192 | cp ../res/OpenDGLab-Desktop.png appImage/OpenDGLab.png
193 | cp OpenDGLab-Desktop appImage/
194 | cd appImage
195 | ../linuxdeployqt OpenDGLab-Desktop -appimage -unsupported-bundle-everything
196 | mv *.AppImage OpenDGLab-Desktop-Linux-x86_64.AppImage
197 | cd ../..
198 | - name: Upload Release Asset Linux x64
199 | uses: actions/upload-release-asset@v1
200 | env:
201 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
202 | with:
203 | upload_url: ${{ needs.create_release.outputs.upload_url }}
204 | asset_path: ./build/appImage/OpenDGLab-Desktop-Linux-x86_64.AppImage
205 | asset_name: OpenDGLab-Desktop-Linux-x86_64.AppImage
206 | asset_content_type: application/x-elf
207 | build_macos:
208 | strategy:
209 | matrix:
210 | qt_ver: [5.15.1]
211 | os: [macos-latest]
212 | needs: [create_release]
213 | runs-on: ${{ matrix.os }}
214 | steps:
215 | - name: Install Qt
216 | uses: jurplel/install-qt-action@v2.11.1
217 | with:
218 | version: ${{ matrix.qt_ver }}
219 | - name: Install Package...
220 | run: |
221 | brew install unzip
222 | - name: Checkout
223 | uses: actions/checkout@v1
224 | - name: Get OpenDGLab Core MacOS x64
225 | shell: bash
226 | run: |
227 | wget https://github.com/OpenDGLab/OpenDGLab-Core/releases/latest/download/OpenDGLab-Core-MacOS-X64.zip
228 | unzip OpenDGLab-Core-MacOS-X64.zip -d OpenDGLab-Core-Unzipped
229 | mkdir -p opendglab-core/header
230 | mkdir -p opendglab-core/bin
231 | cp OpenDGLab-Core-Unzipped/releaseStatic/libopendglab_api.h opendglab-core/header/
232 | cp OpenDGLab-Core-Unzipped/releaseStatic/libopendglab.a opendglab-core/bin/
233 | - name: Get OpenDGLab OpenProtocol file
234 | shell: bash
235 | run: |
236 | wget https://raw.githubusercontent.com/OpenDGLab/OpenDGLab-OpenProtocol/master/app.proto
237 | mv app.proto proto/
238 | - name: Get latest CMake and ninja
239 | uses: lukka/get-cmake@latest
240 | - name: Get vcpkg
241 | uses: lukka/run-vcpkg@v6
242 | with:
243 | setupOnly: true
244 | vcpkgDirectory: '${{ github.workspace }}/vcpkg'
245 | vcpkgGitCommitId: 'c54abfafbe0051075952c507da1f1ec234875e05'
246 | - name: Install protobuf
247 | shell: bash
248 | run: |
249 | $VCPKG_ROOT/vcpkg install protobuf
250 | - name: MacOS Build
251 | shell: bash
252 | run: |
253 | mkdir build
254 | cd build
255 | cmake -DCMAKE_BUILD_TYPE=MinSizeRel -DCMAKE_TOOLCHAIN_FILE=$VCPKG_ROOT/scripts/buildsystems/vcpkg.cmake ..
256 | cmake --build . --target all
257 | cd ..
258 | - name: Packaging...
259 | run: |
260 | cd build
261 | macdeployqt OpenDGLab-Desktop.app -dmg
262 | cd ..
263 | - name: Upload Release Asset MacOS x64
264 | uses: actions/upload-release-asset@v1
265 | env:
266 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
267 | with:
268 | upload_url: ${{ needs.create_release.outputs.upload_url }}
269 | asset_path: ./build/OpenDGLab-Desktop.dmg
270 | asset_name: OpenDGLab-Desktop-MacOS-x64.dmg
271 | asset_content_type: application/octet-stream
--------------------------------------------------------------------------------
/src/deviceOperator.ui:
--------------------------------------------------------------------------------
1 |
2 |
3 | DeviceOperator
4 |
5 |
6 |
7 | 0
8 | 0
9 | 692
10 | 231
11 |
12 |
13 |
14 | Form
15 |
16 |
17 |
18 |
19 |
20 | -
21 |
22 |
-
23 |
24 |
25 |
26 | 0
27 | 0
28 |
29 |
30 |
31 |
32 | 16777215
33 | 20
34 |
35 |
36 |
37 |
38 | 75
39 | true
40 |
41 |
42 |
43 | TextLabel
44 |
45 |
46 |
47 | -
48 |
49 |
-
50 |
51 |
52 |
53 |
54 |
55 |
-
56 |
57 |
58 | 1
59 |
60 |
61 |
62 | -
63 |
64 |
65 | 1
66 |
67 |
68 | Qt::AlignCenter
69 |
70 |
71 |
72 | -
73 |
74 |
75 | 自动切换
76 |
77 |
78 |
79 | -
80 |
81 |
82 | 升高
83 |
84 |
85 |
86 | -
87 |
88 |
89 | QFrame::NoFrame
90 |
91 |
92 | 3
93 |
94 |
95 | QLCDNumber::Flat
96 |
97 |
98 |
99 | -
100 |
101 |
102 | 按住执行
103 |
104 |
105 |
106 | -
107 |
108 |
109 | 1
110 |
111 |
112 | 40
113 |
114 |
115 | Qt::Horizontal
116 |
117 |
118 |
119 | -
120 |
121 |
122 | 突增
123 |
124 |
125 |
126 | -
127 |
128 |
129 | 调节步进
130 |
131 |
132 |
133 | -
134 |
135 |
136 | A 通道
137 |
138 |
139 |
140 | -
141 |
142 |
143 | 降低
144 |
145 |
146 |
147 | -
148 |
149 |
150 | -
151 |
152 |
153 | 停止
154 |
155 |
156 |
157 | -
158 |
159 |
160 | Qt::Horizontal
161 |
162 |
163 |
164 | -
165 |
166 |
167 | 1
168 |
169 |
170 | 274
171 |
172 |
173 | Qt::Horizontal
174 |
175 |
176 |
177 | -
178 |
179 |
180 | Qt::Horizontal
181 |
182 |
183 |
184 | -
185 |
186 |
187 | 秒
188 |
189 |
190 | 10
191 |
192 |
193 | 600
194 |
195 |
196 |
197 |
198 |
199 |
200 | -
201 |
202 |
203 |
204 |
205 |
206 |
-
207 |
208 |
209 | <html><head/><body><p>调节步进</p></body></html>
210 |
211 |
212 |
213 | -
214 |
215 |
216 | -
217 |
218 |
219 | B 通道
220 |
221 |
222 |
223 | -
224 |
225 |
226 | 升高
227 |
228 |
229 |
230 | -
231 |
232 |
233 | 1
234 |
235 |
236 | 40
237 |
238 |
239 | Qt::Horizontal
240 |
241 |
242 |
243 | -
244 |
245 |
246 | 1
247 |
248 |
249 | Qt::AlignCenter
250 |
251 |
252 |
253 | -
254 |
255 |
256 | 按住执行
257 |
258 |
259 |
260 | -
261 |
262 |
263 | 停止
264 |
265 |
266 |
267 | -
268 |
269 |
270 | Qt::Horizontal
271 |
272 |
273 |
274 | -
275 |
276 |
277 | 自动切换
278 |
279 |
280 |
281 | -
282 |
283 |
284 | 1
285 |
286 |
287 |
288 | -
289 |
290 |
291 | QFrame::NoFrame
292 |
293 |
294 | false
295 |
296 |
297 | 3
298 |
299 |
300 | QLCDNumber::Flat
301 |
302 |
303 |
304 | -
305 |
306 |
307 | 突增
308 |
309 |
310 |
311 | -
312 |
313 |
314 | 1
315 |
316 |
317 | 274
318 |
319 |
320 | Qt::Horizontal
321 |
322 |
323 |
324 | -
325 |
326 |
327 | 降低
328 |
329 |
330 |
331 | -
332 |
333 |
334 | Qt::Horizontal
335 |
336 |
337 |
338 | -
339 |
340 |
341 | 秒
342 |
343 |
344 | 10
345 |
346 |
347 | 600
348 |
349 |
350 |
351 |
352 |
353 |
354 |
355 |
356 |
357 |
358 |
359 |
360 |
361 |
362 |
363 |
--------------------------------------------------------------------------------
/src/remoteClient.cpp:
--------------------------------------------------------------------------------
1 | #include "remoteClient.h"
2 | #include "global.h"
3 | #include
4 | #include
5 | RemoteClient::RemoteClient(QTcpSocket *tcpSocket, QObject *parent) : QObject(parent), tcpSocket(tcpSocket), isWS(false)
6 | {
7 | connect(tcpSocket, &QTcpSocket::disconnected, this, [this](){
8 | for (auto r: Global::dglabList) {
9 | if (locked.contains(r->getID())) {
10 | r->setDeviceRemoteLocked(false);
11 | r->clearCustomWaveAPI(DeviceStateEnum::DeviceChannel::CHANNEL_A);
12 | r->clearCustomWaveAPI(DeviceStateEnum::DeviceChannel::CHANNEL_B);
13 | }
14 | }
15 | delete this;
16 | });
17 | connect(tcpSocket, &QTcpSocket::readyRead, this, [this](){
18 | auto bytes = this->tcpSocket->readAll();
19 | readyRead(bytes);
20 | });
21 | }
22 | #ifndef __linux__
23 | RemoteClient::RemoteClient(QWebSocket *wsSocket, QObject *parent) : QObject(parent), wsSocket(wsSocket), isWS(true)
24 | {
25 | connect(wsSocket, &QWebSocket::disconnected, this, [this](){
26 | delete this;
27 | });
28 | connect(wsSocket, &QWebSocket::binaryMessageReceived, this, [this](const QByteArray &message){
29 | readyRead(message);
30 | });
31 | }
32 | #endif
33 |
34 | RemoteClient::~RemoteClient() {
35 | isAuthed = false;
36 | if (isWS) {
37 | #ifndef __linux__
38 | wsSocket->close();
39 | #endif
40 | } else {
41 | tcpSocket->close();
42 | }
43 | Global::remoteList.removeOne(this);
44 | }
45 |
46 | void RemoteClient::readyRead(QByteArray data) {
47 | auto dgRequest = com::github::opendglab::DGRequest();
48 | dgRequest.ParseFromArray(data.data(), data.size());
49 | auto response = com::github::opendglab::DGResponse();
50 | response.set_version(1);
51 | if (dgRequest.event() == com::github::opendglab::DGEvent::PING) {
52 | return;
53 | }
54 | if (!isAuthed && dgRequest.event() != com::github::opendglab::DGEvent::CONNECT) {
55 | response.set_event(com::github::opendglab::DGEvent::CANTDOTHIS);
56 | response.set_error(com::github::opendglab::DGError::UNAUTHED);
57 | } else {
58 | switch (dgRequest.event()) {
59 | case com::github::opendglab::DGEvent::CONNECT:
60 | {
61 | if (!isAuthed) {
62 | const auto& conn = dgRequest.connect();
63 | emit doAuth(this, QString::fromStdString(conn.appname()), QString::fromStdString(conn.uuid()), QString::fromStdString(conn.token()));
64 | }
65 | return;
66 | }
67 | case com::github::opendglab::GETDEVICE:
68 | {
69 | response.set_event(com::github::opendglab::DGEvent::GETDEVICE);
70 | auto deviceList = new com::github::opendglab::DGResponse_DGDeviceList();
71 | for (auto r: Global::dglabList) {
72 | if(r->getState() == DeviceStateEnum::DeviceState::READY || r->getState() == DeviceStateEnum::DeviceState::READY_REMOTEMANAGED) {
73 | auto device = deviceList->add_devices();
74 | device->set_id(r->getID().toStdString());
75 | bool isLocked = r->getState() == DeviceStateEnum::DeviceState::READY_REMOTEMANAGED;
76 | device->set_islockedbyremote(isLocked);
77 | bool isLockedByMe = this->locked.contains(r->getID());
78 | device->set_islockedbyme(isLockedByMe);
79 | }
80 | }
81 | response.set_allocated_devicelist(deviceList);
82 | break;
83 | }
84 | case com::github::opendglab::LOCKDEVICE:
85 | {
86 | const auto& device = dgRequest.device();
87 | auto deviceId = QString::fromStdString(device.deviceid());
88 | if (deviceId.isEmpty()) {
89 | response.set_event(com::github::opendglab::DGEvent::CANTDOTHIS);
90 | response.set_error(com::github::opendglab::DGError::UNKNOWN);
91 | } else {
92 | for (auto r: Global::dglabList) {
93 | if (r->getID() == deviceId) {
94 | switch (r->getState()) {
95 | case DeviceStateEnum::DeviceState::UNCONNECTED:
96 | case DeviceStateEnum::CONNECTED:
97 | response.set_event(com::github::opendglab::DGEvent::CANTDOTHIS);
98 | response.set_error(com::github::opendglab::DGError::DEVICEOFFLINE);
99 | break;
100 | case DeviceStateEnum::READY:
101 | {
102 | r->setDeviceRemoteLocked(true);
103 | locked.append(deviceId);
104 | response.set_event(com::github::opendglab::DGEvent::LOCKDEVICE);
105 | auto mDevice = new com::github::opendglab::DGResponse_DGDevice();
106 | mDevice->set_id(deviceId.toStdString());
107 | mDevice->set_islockedbyme(true);
108 | mDevice->set_islockedbyremote(true);
109 | response.set_allocated_device(mDevice);
110 | break;
111 | }
112 | case DeviceStateEnum::READY_REMOTEMANAGED:
113 | if (locked.contains(deviceId)) {
114 | response.set_event(com::github::opendglab::DGEvent::LOCKDEVICE);
115 | auto mDevice = new com::github::opendglab::DGResponse_DGDevice();
116 | mDevice->set_id(deviceId.toStdString());
117 | mDevice->set_islockedbyme(true);
118 | mDevice->set_islockedbyremote(true);
119 | response.set_allocated_device(mDevice);
120 | } else {
121 | response.set_event(com::github::opendglab::DGEvent::CANTDOTHIS);
122 | response.set_error(com::github::opendglab::DGError::DEVICENOTLOCKBYYOU);
123 | }
124 | break;
125 | }
126 | goto escape_route_lock;
127 | }
128 | }
129 | escape_route_lock:
130 | break;
131 | }
132 | }
133 | case com::github::opendglab::UNLOCKDEVICE:
134 | {
135 | const auto& device = dgRequest.device();
136 | auto deviceId = QString::fromStdString(device.deviceid());
137 | if (deviceId.isEmpty()) {
138 | response.set_event(com::github::opendglab::DGEvent::CANTDOTHIS);
139 | response.set_error(com::github::opendglab::DGError::UNKNOWN);
140 | } else {
141 | for (auto r: Global::dglabList) {
142 | if (r->getID() == deviceId) {
143 | switch (r->getState()) {
144 | case DeviceStateEnum::DeviceState::UNCONNECTED:
145 | case DeviceStateEnum::CONNECTED:
146 | response.set_event(com::github::opendglab::DGEvent::CANTDOTHIS);
147 | response.set_error(com::github::opendglab::DGError::DEVICEOFFLINE);
148 | break;
149 | case DeviceStateEnum::READY:
150 | {
151 | response.set_event(com::github::opendglab::DGEvent::CANTDOTHIS);
152 | response.set_error(com::github::opendglab::DGError::DEVICENOTLOCK);
153 | break;
154 | }
155 | case DeviceStateEnum::READY_REMOTEMANAGED:
156 | if (locked.contains(deviceId)) {
157 | r->setDeviceRemoteLocked(false);
158 | r->clearCustomWaveAPI(DeviceStateEnum::DeviceChannel::CHANNEL_A);
159 | r->clearCustomWaveAPI(DeviceStateEnum::DeviceChannel::CHANNEL_B);
160 | response.set_event(com::github::opendglab::DGEvent::UNLOCKDEVICE);
161 | auto mDevice = new com::github::opendglab::DGResponse_DGDevice();
162 | mDevice->set_id(deviceId.toStdString());
163 | mDevice->set_islockedbyme(false);
164 | mDevice->set_islockedbyremote(false);
165 | response.set_allocated_device(mDevice);
166 | locked.removeOne(deviceId);
167 | } else {
168 | response.set_event(com::github::opendglab::DGEvent::CANTDOTHIS);
169 | response.set_error(com::github::opendglab::DGError::DEVICENOTLOCKBYYOU);
170 | }
171 | break;
172 | }
173 | goto escape_route_unlock;
174 | }
175 | }
176 | escape_route_unlock:
177 | break;
178 | }
179 | }
180 | case com::github::opendglab::GETSTRENGTH:
181 | {
182 | const auto& device = dgRequest.device();
183 | auto deviceId = QString::fromStdString(device.deviceid());
184 | if (locked.contains(deviceId)) {
185 | for (auto r: Global::dglabList) {
186 | if (r->getID() == deviceId) {
187 | auto strength = new com::github::opendglab::DGResponse_DGDeviceStrength();
188 | response.set_event(com::github::opendglab::DGEvent::GETSTRENGTH);
189 | strength->set_strengtha(r->getStrengthAPI(DeviceStateEnum::DeviceChannel::CHANNEL_A));
190 | strength->set_strengthb(r->getStrengthAPI(DeviceStateEnum::DeviceChannel::CHANNEL_B));
191 | response.set_allocated_strength(strength);
192 | break;
193 | }
194 | }
195 | } else {
196 | response.set_event(com::github::opendglab::DGEvent::CANTDOTHIS);
197 | response.set_error(com::github::opendglab::DGError::DEVICENOTLOCKBYYOU);
198 | }
199 | break;
200 | }
201 | case com::github::opendglab::SETSTRENGTH:
202 | {
203 | const auto& device = dgRequest.device();
204 | auto deviceId = QString::fromStdString(device.deviceid());
205 | const auto& strength = dgRequest.strength();
206 | if (locked.contains(deviceId)) {
207 | for (auto r: Global::dglabList) {
208 | if (r->getID() == deviceId) {
209 | r->setStrengthAPI(strength.strengtha(), strength.strengthb());
210 | return;
211 | }
212 | }
213 | } else {
214 | response.set_event(com::github::opendglab::DGEvent::CANTDOTHIS);
215 | response.set_error(com::github::opendglab::DGError::DEVICENOTLOCKBYYOU);
216 | }
217 | return;
218 | }
219 | case com::github::opendglab::GETWAVELIST:
220 | {
221 | response.set_event(com::github::opendglab::DGEvent::GETWAVELIST);
222 | auto* waveList = new com::github::opendglab::DGResponse_DGWaveList();
223 | for (const auto& w: Global::basicWaveNameList) {
224 | waveList->add_wave(w.toStdString());
225 | }
226 | for (const auto& w: Global::touchWaveNameList) {
227 | waveList->add_wave(w.toStdString());
228 | }
229 | response.set_allocated_wavelist(waveList);
230 | break;
231 | }
232 | case com::github::opendglab::GETWAVE:
233 | {
234 | const auto& device = dgRequest.device();
235 | auto deviceId = QString::fromStdString(device.deviceid());
236 | const auto& strength = dgRequest.strength();
237 | if (locked.contains(deviceId)) {
238 | for (auto r: Global::dglabList) {
239 | if (r->getID() == deviceId) {
240 | auto oDevice = new com::github::opendglab::DGResponse_DGDeviceID();
241 | auto wave = new com::github::opendglab::DGResponse_DGWave();
242 | response.set_event(com::github::opendglab::DGEvent::GETWAVE);
243 | switch (device.devicechannel()) {
244 | case com::github::opendglab::CHANNEL_A:
245 | {
246 | auto w = r->getWaveAPI(DeviceStateEnum::DeviceChannel::CHANNEL_A);
247 | wave->set_wave(w.toStdString());
248 | oDevice->set_deviceid(deviceId.toStdString());
249 | oDevice->set_devicechannel(com::github::opendglab::CHANNEL_A);
250 | break;
251 | }
252 | case com::github::opendglab::CHANNEL_B:
253 | {
254 | auto w = r->getWaveAPI(DeviceStateEnum::DeviceChannel::CHANNEL_B);
255 | wave->set_wave(w.toStdString());
256 | oDevice->set_deviceid(deviceId.toStdString());
257 | oDevice->set_devicechannel(com::github::opendglab::CHANNEL_B);
258 | break;
259 | }
260 | default:
261 | break;
262 | }
263 | break;
264 | }
265 | }
266 | } else {
267 | response.set_event(com::github::opendglab::DGEvent::CANTDOTHIS);
268 | response.set_error(com::github::opendglab::DGError::DEVICENOTLOCKBYYOU);
269 | }
270 | break;
271 | }
272 | case com::github::opendglab::SETWAVE:
273 | {
274 | const auto& device = dgRequest.device();
275 | auto deviceId = QString::fromStdString(device.deviceid());
276 | const auto& strength = dgRequest.strength();
277 | auto waveName = QString::fromStdString(dgRequest.wave().wavename());
278 | if (locked.contains(deviceId)) {
279 | for (auto r: Global::dglabList) {
280 | if (r->getID() == deviceId) {
281 | response.set_event(com::github::opendglab::DGEvent::SETWAVE);
282 | auto oDevice = new com::github::opendglab::DGResponse_DGDeviceID();
283 | auto wave = new com::github::opendglab::DGResponse_DGWave();
284 | switch (device.devicechannel()) {
285 | case com::github::opendglab::CHANNEL_A:
286 | {
287 | r->setWaveAPI(DeviceStateEnum::DeviceChannel::CHANNEL_A, waveName);
288 | auto w = r->getWaveAPI(DeviceStateEnum::DeviceChannel::CHANNEL_A);
289 | wave->set_wave(w.toStdString());
290 | oDevice->set_deviceid(deviceId.toStdString());
291 | oDevice->set_devicechannel(com::github::opendglab::CHANNEL_A);
292 | response.set_allocated_deviceid(oDevice);
293 | response.set_allocated_wavename(wave);
294 | break;
295 | }
296 | case com::github::opendglab::CHANNEL_B:
297 | {
298 | r->setWaveAPI(DeviceStateEnum::DeviceChannel::CHANNEL_B, waveName);
299 | auto w = r->getWaveAPI(DeviceStateEnum::DeviceChannel::CHANNEL_B);
300 | wave->set_wave(w.toStdString());
301 | oDevice->set_deviceid(deviceId.toStdString());
302 | oDevice->set_devicechannel(com::github::opendglab::CHANNEL_B);
303 | response.set_allocated_deviceid(oDevice);
304 | response.set_allocated_wavename(wave);
305 | break;
306 | }
307 | default:
308 | break;
309 | }
310 | break;
311 | }
312 | }
313 | } else {
314 | response.set_event(com::github::opendglab::DGEvent::CANTDOTHIS);
315 | response.set_error(com::github::opendglab::DGError::DEVICENOTLOCKBYYOU);
316 | }
317 | break;
318 | }
319 | case com::github::opendglab::CUSTOMWAVE:
320 | {
321 | const auto& device = dgRequest.device();
322 | auto deviceId = QString::fromStdString(device.deviceid());
323 | const auto& customWave = dgRequest.customwave();
324 | if (locked.contains(deviceId)) {
325 | for (auto r: Global::dglabList) {
326 | if (r->getID() == deviceId) {
327 | switch (device.devicechannel()) {
328 | case com::github::opendglab::CHANNEL_A:
329 | {
330 | for (const auto& c: customWave) {
331 | if(c.bytes().size() == 3) {
332 | auto array = new QByteArray(3, Qt::Initialization::Uninitialized);
333 | const auto& cb = c.bytes();
334 | array->data()[0] = cb[0];
335 | array->data()[1] = cb[1];
336 | array->data()[2] = cb[2];
337 | r->addCustomWaveAPI(DeviceStateEnum::DeviceChannel::CHANNEL_A, *array);
338 | }
339 | }
340 | break;
341 | }
342 | case com::github::opendglab::CHANNEL_B:
343 | {
344 | for (const auto& c: customWave) {
345 | if(c.bytes().size() == 3) {
346 | auto array = new QByteArray(3, Qt::Initialization::Uninitialized);
347 | const auto& cb = c.bytes();
348 | array->data()[0] = cb[0];
349 | array->data()[1] = cb[1];
350 | array->data()[2] = cb[2];
351 | r->addCustomWaveAPI(DeviceStateEnum::DeviceChannel::CHANNEL_B, *array);
352 | }
353 | }
354 | break;
355 | }
356 | default:
357 | break;
358 | }
359 | return;
360 | }
361 | }
362 | } else {
363 | response.set_event(com::github::opendglab::DGEvent::CANTDOTHIS);
364 | response.set_error(com::github::opendglab::DGError::DEVICENOTLOCKBYYOU);
365 | }
366 | break;
367 | }
368 | case com::github::opendglab::CLEARCUSTOM:
369 | {
370 | const auto& device = dgRequest.device();
371 | auto deviceId = QString::fromStdString(device.deviceid());
372 | if (locked.contains(deviceId)) {
373 | for (auto r: Global::dglabList) {
374 | if (r->getID() == deviceId) {
375 | switch (device.devicechannel()) {
376 | case com::github::opendglab::CHANNEL_A:
377 | r->clearCustomWaveAPI(DeviceStateEnum::DeviceChannel::CHANNEL_A);
378 | break;
379 | case com::github::opendglab::CHANNEL_B:
380 | r->clearCustomWaveAPI(DeviceStateEnum::DeviceChannel::CHANNEL_B);
381 | break;
382 | default:
383 | break;
384 | }
385 | return;
386 | }
387 | }
388 | } else {
389 | response.set_event(com::github::opendglab::DGEvent::CANTDOTHIS);
390 | response.set_error(com::github::opendglab::DGError::DEVICENOTLOCKBYYOU);
391 | }
392 | break;
393 | }
394 | default:
395 | return;
396 | }
397 | }
398 | QByteArray byteArray(response.SerializeAsString().c_str(), response.ByteSizeLong());
399 | if(isWS) {
400 | #ifndef __linux__
401 | wsSocket->sendBinaryMessage(byteArray);
402 | #endif
403 | } else {
404 | tcpSocket->write(byteArray);
405 | }
406 | }
407 |
408 | void RemoteClient::sendConnected(const QString& _uuid, const QString& token, bool isAuth) {
409 | auto response = com::github::opendglab::DGResponse();
410 | response.set_version(1);
411 | response.set_event(com::github::opendglab::DGEvent::CONNECT);
412 | auto *conn = new com::github::opendglab::DGResponse_DGConnect();
413 | conn->set_token(token.toStdString());
414 | response.set_allocated_connect(conn);
415 | QByteArray byteArray(response.SerializeAsString().c_str(), response.ByteSizeLong());
416 | this->isAuthed = isAuth;
417 | this->uuid = _uuid;
418 | if(isWS) {
419 | #ifndef __linux__
420 | wsSocket->sendBinaryMessage(byteArray);
421 | #endif
422 | } else {
423 | tcpSocket->write(byteArray);
424 | }
425 | if (!isAuth) {
426 | close();
427 | }
428 | }
429 |
430 | void RemoteClient::close() {
431 | if (isWS) {
432 | #ifndef __linux__
433 | wsSocket->close();
434 | #endif
435 | } else {
436 | tcpSocket->close();
437 | }
438 | }
439 |
440 | QString RemoteClient::getUuid() {
441 | return uuid;
442 | }
443 |
444 | void RemoteClient::sendDeviceReset(const QString &deviceId) {
445 | if (locked.contains(deviceId)) {
446 | auto response = com::github::opendglab::DGResponse();
447 | response.set_version(1);
448 | response.set_event(com::github::opendglab::DGEvent::DEVICERESET);
449 | auto sDeviceId = new com::github::opendglab::DGResponse_DGDeviceID();
450 | sDeviceId->set_deviceid(deviceId.toStdString());
451 | response.set_allocated_deviceid(sDeviceId);
452 | QByteArray byteArray(response.SerializeAsString().c_str(), response.ByteSizeLong());
453 | if(isWS) {
454 | #ifndef __linux__
455 | wsSocket->sendBinaryMessage(byteArray);
456 | #endif
457 | } else {
458 | tcpSocket->write(byteArray);
459 | }
460 | locked.removeOne(deviceId);
461 | }
462 | }
463 |
464 | void RemoteClient::sendPowerUpdate(const QString &deviceId, int channel_a, int channel_b) {
465 | if (locked.contains(deviceId)) {
466 | auto response = com::github::opendglab::DGResponse();
467 | response.set_version(1);
468 | response.set_event(com::github::opendglab::DGEvent::GETSTRENGTH);
469 | auto sDeviceId = new com::github::opendglab::DGResponse_DGDeviceID();
470 | sDeviceId->set_deviceid(deviceId.toStdString());
471 | response.set_allocated_deviceid(sDeviceId);
472 | auto strength = new com::github::opendglab::DGResponse_DGDeviceStrength();
473 | strength->set_strengtha(channel_a);
474 | strength->set_strengthb(channel_b);
475 | response.set_allocated_strength(strength);
476 | QByteArray byteArray(response.SerializeAsString().c_str(), response.ByteSizeLong());
477 | if(isWS) {
478 | #ifndef __linux__
479 | wsSocket->sendBinaryMessage(byteArray);
480 | #endif
481 | } else {
482 | tcpSocket->write(byteArray);
483 | }
484 | }
485 | }
486 |
487 | void RemoteClient::sendPing() {
488 | if (isAuthed) {
489 | auto response = com::github::opendglab::DGResponse();
490 | response.set_version(1);
491 | response.set_event(com::github::opendglab::DGEvent::PING);
492 | QByteArray byteArray(response.SerializeAsString().c_str(), response.ByteSizeLong());
493 | if(isWS) {
494 | #ifndef __linux__
495 | wsSocket->sendBinaryMessage(byteArray);
496 | #endif
497 | } else {
498 | tcpSocket->write(byteArray);
499 | }
500 | }
501 | }
502 |
--------------------------------------------------------------------------------
/src/dglabDevice.cpp:
--------------------------------------------------------------------------------
1 | #include "dglabDevice.h"
2 | #include "global.h"
3 | #include
4 | DGLabDevice::DGLabDevice(const QBluetoothDeviceInfo &_deviceInfo, QObject *parent) :
5 | QObject(parent),
6 | deviceInfo(_deviceInfo),
7 | openDgLab(libopendglab_symbols()->kotlin.root.OpenDGLab.OpenDGLab())
8 | {
9 | controller = QLowEnergyController::createCentral(deviceInfo, this);
10 | controller->setRemoteAddressType(QLowEnergyController::PublicAddress);
11 | id = QString(QCryptographicHash::hash(getDeviceAddressOrUuid().toUtf8(),QCryptographicHash::Sha256).toHex()).right(6);
12 | uiDeviceItem = new DeviceItem(getID(), getDeviceAddressOrUuid(), Global::deviceName);
13 | uiDeviceOperator = new DeviceOperator(getID());
14 | uiDeviceOperator->setEnabled(false);
15 | connect(controller, &QLowEnergyController::connected, this, &DGLabDevice::deviceConnected);
16 | connect(controller, &QLowEnergyController::disconnected, this, &DGLabDevice::deviceDisconnected);
17 | connect(this, &DGLabDevice::powerUpdate, uiDeviceOperator, &DeviceOperator::setPower);
18 | connect(uiDeviceOperator, &DeviceOperator::changeWave, this, &DGLabDevice::setWave);
19 | connect(uiDeviceOperator, &DeviceOperator::startBoost, this, &DGLabDevice::startChannelBoost);
20 | connect(uiDeviceOperator, &DeviceOperator::stopBoost, this, &DGLabDevice::stopChannelBoost);
21 | }
22 |
23 | DGLabDevice::~DGLabDevice()
24 | {
25 | if (waveSenderTimerThread) {
26 | waveSenderTimerThread->quit();
27 | //waveSenderTimerThread->wait();
28 | waveSenderTimerThread = nullptr;
29 | }
30 | SKIPNEXTQWARNING(SKIPNEXTERROR(controller->disconnectFromDevice()));
31 | }
32 |
33 | QString DGLabDevice::getID()
34 | {
35 | return id;
36 | }
37 |
38 | QString DGLabDevice::getDeviceAddressOrUuid()
39 | {
40 | QString address =
41 | #ifdef Q_OS_MACOS
42 | deviceInfo.deviceUuid()
43 | #else
44 | deviceInfo.address()
45 | #endif
46 | .toString();
47 | return address;
48 | }
49 |
50 | QLowEnergyController* DGLabDevice::getBLEController()
51 | {
52 | return controller;
53 | }
54 |
55 | void DGLabDevice::startGlobalBoost(int boost)
56 | {
57 | if (globalBoost != 0) return;
58 | globalBoost = boost;
59 | const QLowEnergyCharacteristic abpower = eStimService->characteristic(QBluetoothUuid(QUuid(Global::DGLabServices::EStimStatus::Characteristic::abpower)));
60 | libopendglab_ExportedSymbols* libopendglab = libopendglab_symbols();
61 | auto eStim = libopendglab->kotlin.root.OpenDGLab.get_eStimStatus(openDgLab);
62 | auto power = libopendglab->kotlin.root.OpenDGLab.EStimStatus.get_abPower(eStim);
63 | libopendglab_kref_OpenDGLab_WriteBLE dataKt;
64 | auto afboostA = powerA + boost;
65 | auto afboostB = powerB + boost;
66 | if (afboostA > 274) afboostA = 274;
67 | if (afboostB > 274) afboostB = 274;
68 | dataKt = libopendglab->kotlin.root.OpenDGLab.EStimStatus.ABPower.setABPower(power, afboostA, afboostB);
69 | auto dataBytes = libopendglab->kotlin.root.OpenDGLab.WriteBLE.get_data(dataKt);
70 | auto dataSize = libopendglab->kotlin.root.getByteArraySize(dataBytes);
71 | auto data = QByteArray(dataSize, Qt::Initialization::Uninitialized);
72 | libopendglab->kotlin.root.toNativeByteArray(data.data(), dataBytes);
73 | eStimService->writeCharacteristic(abpower, data, QLowEnergyService::WriteWithoutResponse);
74 | }
75 |
76 | void DGLabDevice::stopGlobalBoost()
77 | {
78 | const QLowEnergyCharacteristic abpower = eStimService->characteristic(QBluetoothUuid(QUuid(Global::DGLabServices::EStimStatus::Characteristic::abpower)));
79 | libopendglab_ExportedSymbols* libopendglab = libopendglab_symbols();
80 | auto eStim = libopendglab->kotlin.root.OpenDGLab.get_eStimStatus(openDgLab);
81 | auto power = libopendglab->kotlin.root.OpenDGLab.EStimStatus.get_abPower(eStim);
82 | libopendglab_kref_OpenDGLab_WriteBLE dataKt;
83 | auto afboostA = powerA - globalBoost;
84 | auto afboostB = powerB - globalBoost;
85 | if (afboostA < 0) afboostA = 0;
86 | if (afboostB < 0) afboostB = 0;
87 | dataKt = libopendglab->kotlin.root.OpenDGLab.EStimStatus.ABPower.setABPower(power, afboostA, afboostB);
88 | auto dataBytes = libopendglab->kotlin.root.OpenDGLab.WriteBLE.get_data(dataKt);
89 | auto dataSize = libopendglab->kotlin.root.getByteArraySize(dataBytes);
90 | auto data = QByteArray(dataSize, Qt::Initialization::Uninitialized);
91 | libopendglab->kotlin.root.toNativeByteArray(data.data(), dataBytes);
92 | eStimService->writeCharacteristic(abpower, data, QLowEnergyService::WriteWithoutResponse);
93 | globalBoost = 0;
94 | }
95 |
96 | DeviceStateEnum::DeviceState DGLabDevice::getState()
97 | {
98 | return uiDeviceItem->getState();
99 | }
100 |
101 | void DGLabDevice::waveSender()
102 | {
103 | if (uiDeviceItem->getState() == DeviceStateEnum::DeviceState::READY || uiDeviceItem->getState() == DeviceStateEnum::DeviceState::READY_REMOTEMANAGED) {
104 | libopendglab_ExportedSymbols* libopendglab = libopendglab_symbols();
105 | auto eStimStatus = libopendglab->kotlin.root.OpenDGLab.get_eStimStatus(openDgLab);
106 | auto wave = libopendglab->kotlin.root.OpenDGLab.EStimStatus.get_wave(eStimStatus);
107 | if (powerA > 0) {
108 | //WaveA
109 | auto waveCenterA = libopendglab->kotlin.root.OpenDGLab.EStimStatus.Wave.getWaveCenterA(wave);
110 | auto hasWave = libopendglab->kotlin.root.WaveCenter.toDGWaveGen(waveCenterA);
111 | if (QString::fromUtf8(hasWave) == "") {
112 | //Custom Send
113 | QMutexLocker locker(&customWaveAMutex);
114 | if (!customWaveA.isEmpty()) {
115 | auto qBytes = customWaveA.takeFirst();
116 | const QLowEnergyCharacteristic wave = eStimService->characteristic(QBluetoothUuid(QUuid(Global::DGLabServices::EStimStatus::Characteristic::waveA)));
117 | eStimService->writeCharacteristic(wave, qBytes, QLowEnergyService::WriteWithoutResponse);
118 | }
119 | } else {
120 | auto waveBytes = libopendglab->kotlin.root.WaveCenter.waveTick(waveCenterA);
121 | auto waveBytesSize = libopendglab->kotlin.root.getByteArraySize(waveBytes);
122 | auto qBytes = QByteArray(waveBytesSize, Qt::Initialization::Uninitialized);
123 | libopendglab->kotlin.root.toNativeByteArray(qBytes.data(), waveBytes);
124 | const QLowEnergyCharacteristic wave = eStimService->characteristic(QBluetoothUuid(QUuid(Global::DGLabServices::EStimStatus::Characteristic::waveA)));
125 | eStimService->writeCharacteristic(wave, qBytes, QLowEnergyService::WriteWithoutResponse);
126 | }
127 | libopendglab->DisposeString(hasWave);
128 | } else {
129 | auto waveCenterA = libopendglab->kotlin.root.OpenDGLab.EStimStatus.Wave.getWaveCenterA(wave);
130 | auto hasWave = libopendglab->kotlin.root.WaveCenter.toDGWaveGen(waveCenterA);
131 | if (QString::fromUtf8(hasWave) == "") {
132 | QMutexLocker locker(&customWaveAMutex);
133 | if (!customWaveA.isEmpty()) customWaveA.empty();
134 | }
135 | }
136 | if (powerB > 0){
137 | //WaveB
138 | auto waveCenterB = libopendglab->kotlin.root.OpenDGLab.EStimStatus.Wave.getWaveCenterB(wave);
139 | auto hasWave = libopendglab->kotlin.root.WaveCenter.toDGWaveGen(waveCenterB);
140 | if (QString::fromUtf8(hasWave) == "") {
141 | QMutexLocker locker(&customWaveBMutex);
142 | if (!customWaveB.isEmpty()) {
143 | auto qBytes = customWaveB.takeFirst();
144 | const QLowEnergyCharacteristic wave = eStimService->characteristic(QBluetoothUuid(QUuid(Global::DGLabServices::EStimStatus::Characteristic::waveB)));
145 | eStimService->writeCharacteristic(wave, qBytes, QLowEnergyService::WriteWithoutResponse);
146 | }
147 | } else {
148 | auto waveBytes = libopendglab->kotlin.root.WaveCenter.waveTick(waveCenterB);
149 | auto waveBytesSize = libopendglab->kotlin.root.getByteArraySize(waveBytes);
150 | auto qBytes = QByteArray(waveBytesSize, Qt::Initialization::Uninitialized);
151 | libopendglab->kotlin.root.toNativeByteArray(qBytes.data(), waveBytes);
152 | const QLowEnergyCharacteristic wave = eStimService->characteristic(QBluetoothUuid(QUuid(Global::DGLabServices::EStimStatus::Characteristic::waveB)));
153 | eStimService->writeCharacteristic(wave, qBytes, QLowEnergyService::WriteWithoutResponse);
154 | }
155 | libopendglab->DisposeString(hasWave);
156 | } else {
157 | auto waveCenterB = libopendglab->kotlin.root.OpenDGLab.EStimStatus.Wave.getWaveCenterB(wave);
158 | auto hasWave = libopendglab->kotlin.root.WaveCenter.toDGWaveGen(waveCenterB);
159 | if (QString::fromUtf8(hasWave) == "") {
160 | QMutexLocker locker(&customWaveBMutex);
161 | if (!customWaveB.isEmpty()) customWaveB.empty();
162 | }
163 | }
164 | }
165 | }
166 |
167 | void DGLabDevice::setWave(DeviceStateEnum::DeviceChannel channel, QString value)
168 | {
169 | libopendglab_ExportedSymbols* libopendglab = libopendglab_symbols();
170 | auto eStimState = libopendglab->kotlin.root.OpenDGLab.get_eStimStatus(openDgLab);
171 | auto wave = libopendglab->kotlin.root.OpenDGLab.EStimStatus.get_wave(eStimState);
172 | switch (channel) {
173 | case DeviceStateEnum::DeviceChannel::CHANNEL_A:
174 | {
175 | auto waveCenter = libopendglab->kotlin.root.OpenDGLab.EStimStatus.Wave.getWaveCenterA(wave);
176 | auto cWave = Global::basicWaveNameList.contains(value);
177 | if (cWave) {
178 | auto waveA = libopendglab->kotlin.root.WaveCenter.Companion.getBasicWave(libopendglab->kotlin.root.WaveCenter.Companion._instance(), value.toUtf8().data());
179 | auto convertWaveA = libopendglab->kotlin.root.convertBasicWaveDataToBasicWave(waveA);
180 | libopendglab->kotlin.root.WaveCenter.selectWave(waveCenter, convertWaveA);
181 | } else {
182 | cWave = Global::touchWaveNameList.contains(value);
183 | if (cWave) {
184 | auto waveA = libopendglab->kotlin.root.WaveCenter.Companion.getTouchWave(libopendglab->kotlin.root.WaveCenter.Companion._instance(), value.toUtf8().data());
185 | auto convertWaveA = libopendglab->kotlin.root.convertTouchWaveDataToBasicWave(waveA);
186 | libopendglab->kotlin.root.WaveCenter.selectWave(waveCenter, convertWaveA);
187 | } else {
188 | libopendglab->kotlin.root.WaveCenter.selectWave(waveCenter, libopendglab->kotlin.root.createNullBasicWave());
189 | }
190 | }
191 | }
192 | break;
193 | case DeviceStateEnum::DeviceChannel::CHANNEL_B:
194 | {
195 | auto waveCenter = libopendglab->kotlin.root.OpenDGLab.EStimStatus.Wave.getWaveCenterB(wave);
196 | auto cWave = Global::basicWaveNameList.contains(value);
197 | if (cWave) {
198 | auto waveB = libopendglab->kotlin.root.WaveCenter.Companion.getBasicWave(libopendglab->kotlin.root.WaveCenter.Companion._instance(), value.toUtf8().data());
199 | auto convertWaveB = libopendglab->kotlin.root.convertBasicWaveDataToBasicWave(waveB);
200 | libopendglab->kotlin.root.WaveCenter.selectWave(waveCenter, convertWaveB);
201 | } else {
202 | cWave = Global::touchWaveNameList.contains(value);
203 | if (cWave) {
204 | auto waveB = libopendglab->kotlin.root.WaveCenter.Companion.getTouchWave(libopendglab->kotlin.root.WaveCenter.Companion._instance(), value.toUtf8().data());
205 | auto convertWaveB = libopendglab->kotlin.root.convertTouchWaveDataToBasicWave(waveB);
206 | libopendglab->kotlin.root.WaveCenter.selectWave(waveCenter, convertWaveB);
207 | } else {
208 | libopendglab->kotlin.root.WaveCenter.selectWave(waveCenter, libopendglab->kotlin.root.createNullBasicWave());
209 | }
210 | }
211 | }
212 | break;
213 | }
214 | }
215 |
216 | void DGLabDevice::startChannelBoost(DeviceStateEnum::DeviceChannel channel, int boost)
217 | {
218 | const QLowEnergyCharacteristic abpower = eStimService->characteristic(QBluetoothUuid(QUuid(Global::DGLabServices::EStimStatus::Characteristic::abpower)));
219 | libopendglab_ExportedSymbols* libopendglab = libopendglab_symbols();
220 | auto eStim = libopendglab->kotlin.root.OpenDGLab.get_eStimStatus(openDgLab);
221 | auto power = libopendglab->kotlin.root.OpenDGLab.EStimStatus.get_abPower(eStim);
222 | libopendglab_kref_OpenDGLab_WriteBLE dataKt;
223 | int afboost;
224 | switch (channel) {
225 | case DeviceStateEnum::DeviceChannel::CHANNEL_A:
226 | boostA = boost;
227 | afboost = powerA + boost;
228 | if (afboost > 274) afboost = 274;
229 | dataKt = libopendglab->kotlin.root.OpenDGLab.EStimStatus.ABPower.setABPower(power, afboost, powerB);
230 | break;
231 | case DeviceStateEnum::DeviceChannel::CHANNEL_B:
232 | boostB = boost;
233 | afboost = powerB + boost;
234 | if (afboost > 274) afboost = 274;
235 | dataKt = libopendglab->kotlin.root.OpenDGLab.EStimStatus.ABPower.setABPower(power, powerA, afboost);
236 | break;
237 | }
238 | auto dataBytes = libopendglab->kotlin.root.OpenDGLab.WriteBLE.get_data(dataKt);
239 | auto dataSize = libopendglab->kotlin.root.getByteArraySize(dataBytes);
240 | auto data = QByteArray(dataSize, Qt::Initialization::Uninitialized);
241 | libopendglab->kotlin.root.toNativeByteArray(data.data(), dataBytes);
242 | eStimService->writeCharacteristic(abpower, data, QLowEnergyService::WriteWithoutResponse);
243 | }
244 |
245 | void DGLabDevice::stopChannelBoost(DeviceStateEnum::DeviceChannel channel, int boost)
246 | {
247 | const QLowEnergyCharacteristic abpower = eStimService->characteristic(QBluetoothUuid(QUuid(Global::DGLabServices::EStimStatus::Characteristic::abpower)));
248 | libopendglab_ExportedSymbols* libopendglab = libopendglab_symbols();
249 | auto eStim = libopendglab->kotlin.root.OpenDGLab.get_eStimStatus(openDgLab);
250 | auto power = libopendglab->kotlin.root.OpenDGLab.EStimStatus.get_abPower(eStim);
251 | libopendglab_kref_OpenDGLab_WriteBLE dataKt;
252 | int afboost;
253 | switch (channel) {
254 | case DeviceStateEnum::DeviceChannel::CHANNEL_A:
255 | boostA = 0;
256 | afboost = powerA - boost;
257 | if (afboost < 0) afboost = 0;
258 | dataKt = libopendglab->kotlin.root.OpenDGLab.EStimStatus.ABPower.setABPower(power, afboost, powerB);
259 | break;
260 | case DeviceStateEnum::DeviceChannel::CHANNEL_B:
261 | boostB = 0;
262 | afboost = powerB - boost;
263 | if (afboost < 0) afboost = 0;
264 | dataKt = libopendglab->kotlin.root.OpenDGLab.EStimStatus.ABPower.setABPower(power, powerA, afboost);
265 | break;
266 | }
267 | auto dataBytes = libopendglab->kotlin.root.OpenDGLab.WriteBLE.get_data(dataKt);
268 | auto dataSize = libopendglab->kotlin.root.getByteArraySize(dataBytes);
269 | auto data = QByteArray(dataSize, Qt::Initialization::Uninitialized);
270 | libopendglab->kotlin.root.toNativeByteArray(data.data(), dataBytes);
271 | eStimService->writeCharacteristic(abpower, data, QLowEnergyService::WriteWithoutResponse);
272 | }
273 |
274 | bool DGLabDevice::isShownOnUi()
275 | {
276 | return shownOnUi;
277 | }
278 | void DGLabDevice::setShownOnUi()
279 | {
280 | shownOnUi = true;
281 | }
282 | DeviceItem* DGLabDevice::getUiDeviceItem() {
283 | return uiDeviceItem;
284 | }
285 | DeviceOperator* DGLabDevice::getUiDeviceOperator() {
286 | return uiDeviceOperator;
287 | }
288 |
289 | void DGLabDevice::deviceConnected()
290 | {
291 | uiDeviceItem->changeState(DeviceStateEnum::DeviceState::CONNECTED);
292 | uiDeviceItem->changeBattery(-1);
293 | uiDeviceItem->update();
294 | waveSenderTimerThread = new WaveSenderTimerThread(this);
295 | connect(controller, &QLowEnergyController::discoveryFinished, this, &DGLabDevice::deviceServiceDiscoverFinished);
296 | this->controller->discoverServices();
297 | }
298 | void DGLabDevice::deviceDisconnected()
299 | {
300 | if (waveSenderTimerThread) {
301 | waveSenderTimerThread->quit();
302 | //waveSenderTimerThread->wait();
303 | waveSenderTimerThread = nullptr;
304 | }
305 | if (uiDeviceItem) {
306 | uiDeviceItem->changeState(DeviceStateEnum::DeviceState::UNCONNECTED);
307 | uiDeviceItem->changeBattery(0);
308 | uiDeviceItem->update();
309 | }
310 | if (remoteLocked) {
311 | // 通知锁定客户端设备已经释放
312 | for (auto r: Global::remoteList) {
313 | r->sendDeviceReset(getID());
314 | }
315 | }
316 | remoteLocked = false;
317 | uiDeviceOperator->setAutoChange(DeviceStateEnum::DeviceChannel::CHANNEL_A, false);
318 | uiDeviceOperator->setAutoChange(DeviceStateEnum::DeviceChannel::CHANNEL_B, false);
319 | }
320 | void DGLabDevice::deviceServiceDiscoverFinished()
321 | {
322 | batteryService = controller->createServiceObject(QBluetoothUuid(QUuid(Global::DGLabServices::DeviceStatus::service)),this);
323 | if (!batteryService) {
324 | qDebug() << "Not Found Battery Service";
325 | return;
326 | }
327 | connect(batteryService, qOverload(&QLowEnergyService::stateChanged), this, &DGLabDevice::deviceBatteryServiceStateChanged);
328 | connect(batteryService, qOverload(&QLowEnergyService::characteristicChanged), this, &DGLabDevice::deviceBatteryCharacteristicArrived);
329 | connect(batteryService, qOverload(&QLowEnergyService::characteristicRead), this, &DGLabDevice::deviceBatteryCharacteristicArrived);
330 | eStimService = controller->createServiceObject(QBluetoothUuid(QUuid(Global::DGLabServices::EStimStatus::service)),this);
331 | if (!eStimService) {
332 | qDebug() << "Not Found EStim Service";
333 | return;
334 | }
335 | connect(eStimService, qOverload(&QLowEnergyService::stateChanged), this, &DGLabDevice::deviceEStimServiceStateChanged);
336 | connect(eStimService, qOverload(&QLowEnergyService::characteristicChanged), this, &DGLabDevice::deviceEStimCharacteristicArrived);
337 | connect(eStimService, qOverload(&QLowEnergyService::characteristicRead), this, &DGLabDevice::deviceEStimCharacteristicArrived);
338 |
339 | //Qt BLE Bug Workaround https://bugreports.qt.io/browse/QTBUG-78488
340 | QTimer::singleShot(0, [this] () {
341 | batteryService->discoverDetails();
342 | eStimService->discoverDetails();
343 | });
344 |
345 | //Connect UI Event
346 | connect(uiDeviceOperator, &DeviceOperator::changePower, this, &DGLabDevice::changePower);
347 | }
348 | void DGLabDevice::changePower(int levelA, int levelB) {
349 | if (levelA < 0) levelA = 0;
350 | if (levelB < 0) levelB = 0;
351 | if (levelA > 274) levelA = 274;
352 | if (levelB > 274) levelB = 274;
353 | const QLowEnergyCharacteristic abpower = eStimService->characteristic(QBluetoothUuid(QUuid(Global::DGLabServices::EStimStatus::Characteristic::abpower)));
354 | libopendglab_ExportedSymbols* libopendglab = libopendglab_symbols();
355 | auto eStim = libopendglab->kotlin.root.OpenDGLab.get_eStimStatus(openDgLab);
356 | auto power = libopendglab->kotlin.root.OpenDGLab.EStimStatus.get_abPower(eStim);
357 | auto dataKt = libopendglab->kotlin.root.OpenDGLab.EStimStatus.ABPower.setABPower(power, levelA, levelB);
358 | auto dataBytes = libopendglab->kotlin.root.OpenDGLab.WriteBLE.get_data(dataKt);
359 | auto dataSize = libopendglab->kotlin.root.getByteArraySize(dataBytes);
360 | auto data = QByteArray(dataSize, Qt::Initialization::Uninitialized);
361 | libopendglab->kotlin.root.toNativeByteArray(data.data(), dataBytes);
362 | eStimService->writeCharacteristic(abpower, data, QLowEnergyService::WriteWithoutResponse);
363 | }
364 | void DGLabDevice::deviceBatteryServiceStateChanged(QLowEnergyService::ServiceState state){
365 | if (!batteryService) return;
366 | if (state == QLowEnergyService::ServiceDiscovered) {
367 | auto uuid = QBluetoothUuid(
368 | QUuid(
369 | Global::DGLabServices::DeviceStatus::Characteristic::electric
370 | )
371 | );
372 | const QLowEnergyCharacteristic battery = batteryService->characteristic(uuid);
373 | if (!battery.isValid()){
374 | qDebug() << "Battery Not Found";
375 | return;
376 | }
377 | const QLowEnergyDescriptor m_notificationDescBattery = battery.descriptor(
378 | QBluetoothUuid::ClientCharacteristicConfiguration);
379 | if (m_notificationDescBattery.isValid()) {
380 | // enable notification
381 | batteryService->writeDescriptor(m_notificationDescBattery, QByteArray::fromHex("0100"));
382 | }
383 | batteryService->readCharacteristic(battery);
384 | }
385 | }
386 | void DGLabDevice::deviceEStimServiceStateChanged(QLowEnergyService::ServiceState state){
387 | if (!eStimService) return;
388 | if (state == QLowEnergyService::ServiceDiscovered) {
389 | auto uuidSetup = QBluetoothUuid(
390 | QUuid(
391 | Global::DGLabServices::EStimStatus::Characteristic::setup
392 | )
393 | );
394 | const QLowEnergyCharacteristic setup = eStimService->characteristic(uuidSetup);
395 | if (!setup.isValid()){
396 | qDebug() << "Setup Not Found";
397 | return;
398 | }
399 | eStimService->readCharacteristic(setup);
400 |
401 | auto uuidABPower = QBluetoothUuid(
402 | QUuid(
403 | Global::DGLabServices::EStimStatus::Characteristic::abpower
404 | )
405 | );
406 | const QLowEnergyCharacteristic abpower = eStimService->characteristic(uuidABPower);
407 | if (!abpower.isValid()){
408 | qDebug() << "ABPower Not Found";
409 | return;
410 | }
411 | const QLowEnergyDescriptor abPowerDescriptor = abpower.descriptor(
412 | QBluetoothUuid::ClientCharacteristicConfiguration);
413 | if (abPowerDescriptor.isValid()) {
414 | eStimService->writeDescriptor(abPowerDescriptor, QByteArray::fromHex("0100"));
415 | }
416 | }
417 | }
418 | void DGLabDevice::deviceBatteryCharacteristicArrived(const QLowEnergyCharacteristic&, const QByteArray & array) {
419 | libopendglab_ExportedSymbols* libopendglab = libopendglab_symbols();
420 | auto batteryValue = array.data();
421 | auto deviceState = libopendglab->kotlin.root.OpenDGLab.get_deviceStatus(openDgLab);
422 | auto electric = libopendglab->kotlin.root.OpenDGLab.DeviceStatus.get_electric(deviceState);
423 | auto barray = libopendglab->kotlin.root.createByteArray((void *)batteryValue, array.size());
424 | auto level = libopendglab->kotlin.root.OpenDGLab.DeviceStatus.Electric.onChange(electric, barray);
425 | uiDeviceItem->changeBattery(level);
426 | uiDeviceItem->update();
427 | }
428 | void DGLabDevice::deviceEStimCharacteristicArrived(const QLowEnergyCharacteristic& characteristic, const QByteArray & array) {
429 | libopendglab_ExportedSymbols* libopendglab = libopendglab_symbols();
430 | if (characteristic.uuid() == QBluetoothUuid(QUuid(Global::DGLabServices::EStimStatus::Characteristic::setup))) {
431 | auto setupValue = array.data();
432 | auto eStimState = libopendglab->kotlin.root.OpenDGLab.get_eStimStatus(openDgLab);
433 | auto setup = libopendglab->kotlin.root.OpenDGLab.EStimStatus.get_setup(eStimState);
434 | auto barray = libopendglab->kotlin.root.createByteArray((void *)setupValue, array.size());
435 | libopendglab->kotlin.root.OpenDGLab.EStimStatus.Setup.read(setup, barray);
436 | uiDeviceOperator->setEnabled(true);
437 | uiDeviceOperator->update();
438 | uiDeviceItem->changeState(DeviceStateEnum::DeviceState::READY);
439 | uiDeviceItem->update();
440 | auto wave = libopendglab->kotlin.root.OpenDGLab.EStimStatus.get_wave(eStimState);
441 | auto waveCenterA = libopendglab->kotlin.root.OpenDGLab.EStimStatus.Wave.getWaveCenterA(wave);
442 | auto waveCenterB = libopendglab->kotlin.root.OpenDGLab.EStimStatus.Wave.getWaveCenterB(wave);
443 | auto sWaveA = uiDeviceOperator->getWaveA();
444 | auto sWaveB = uiDeviceOperator->getWaveB();
445 | auto cWave = Global::basicWaveNameList.contains(sWaveA);
446 | if (cWave) {
447 | auto waveA = libopendglab->kotlin.root.WaveCenter.Companion.getBasicWave(libopendglab->kotlin.root.WaveCenter.Companion._instance(), sWaveA.toUtf8().data());
448 | auto convertWaveA = libopendglab->kotlin.root.convertBasicWaveDataToBasicWave(waveA);
449 | libopendglab->kotlin.root.WaveCenter.selectWave(waveCenterA, convertWaveA);
450 | } else {
451 | cWave = Global::touchWaveNameList.contains(sWaveA);
452 | if (cWave) {
453 | auto waveA = libopendglab->kotlin.root.WaveCenter.Companion.getTouchWave(libopendglab->kotlin.root.WaveCenter.Companion._instance(), sWaveA.toUtf8().data());
454 | auto convertWaveA = libopendglab->kotlin.root.convertTouchWaveDataToBasicWave(waveA);
455 | libopendglab->kotlin.root.WaveCenter.selectWave(waveCenterA, convertWaveA);
456 | }
457 | }
458 | cWave = Global::basicWaveNameList.contains(sWaveB);
459 | if (cWave) {
460 | auto waveB = libopendglab->kotlin.root.WaveCenter.Companion.getBasicWave(libopendglab->kotlin.root.WaveCenter.Companion._instance(), sWaveB.toUtf8().data());
461 | auto convertWaveB = libopendglab->kotlin.root.convertBasicWaveDataToBasicWave(waveB);
462 | libopendglab->kotlin.root.WaveCenter.selectWave(waveCenterB, convertWaveB);
463 | } else {
464 | cWave = Global::touchWaveNameList.contains(sWaveA);
465 | if (cWave) {
466 | auto waveB = libopendglab->kotlin.root.WaveCenter.Companion.getTouchWave(libopendglab->kotlin.root.WaveCenter.Companion._instance(), sWaveB.toUtf8().data());
467 | auto convertWaveB = libopendglab->kotlin.root.convertTouchWaveDataToBasicWave(waveB);
468 | libopendglab->kotlin.root.WaveCenter.selectWave(waveCenterB, convertWaveB);
469 | }
470 | }
471 | waveSenderTimerThread->start();
472 | } else if (characteristic.uuid() == QBluetoothUuid(QUuid(Global::DGLabServices::EStimStatus::Characteristic::abpower))) {
473 | auto value = array.data();
474 | auto eStimState = libopendglab->kotlin.root.OpenDGLab.get_eStimStatus(openDgLab);
475 | auto abpower = libopendglab->kotlin.root.OpenDGLab.EStimStatus.get_abPower(eStimState);
476 | auto barray = libopendglab->kotlin.root.createByteArray((void *)value, array.size());
477 | libopendglab->kotlin.root.OpenDGLab.EStimStatus.ABPower.onChange(abpower, barray);
478 | auto aPower = libopendglab->kotlin.root.OpenDGLab.EStimStatus.ABPower.getAPower(abpower);
479 | auto bPower = libopendglab->kotlin.root.OpenDGLab.EStimStatus.ABPower.getBPower(abpower);
480 | powerA = aPower;
481 | powerB = bPower;
482 | emit powerUpdate(DeviceStateEnum::DeviceChannel::CHANNEL_A, powerA);
483 | emit powerUpdate(DeviceStateEnum::DeviceChannel::CHANNEL_B, powerB);
484 | if (remoteLocked) {
485 | // 通知锁定客户端设备更新
486 | for (auto r: Global::remoteList) {
487 | r->sendPowerUpdate(getID(), powerA, powerB);
488 | }
489 | }
490 | }
491 | }
492 |
493 |
494 | void DGLabDevice::clearCustomWaveAPI(DeviceStateEnum::DeviceChannel channel) {
495 | switch (channel) {
496 | case DeviceStateEnum::DeviceChannel::CHANNEL_A:
497 | {
498 | QMutexLocker locker(&customWaveAMutex);
499 | customWaveA.empty();
500 | }
501 | break;
502 | case DeviceStateEnum::DeviceChannel::CHANNEL_B:
503 | {
504 | QMutexLocker locker(&customWaveBMutex);
505 | customWaveB.empty();
506 | }
507 | break;
508 | }
509 | }
510 | void DGLabDevice::addCustomWaveAPI(DeviceStateEnum::DeviceChannel channel, QByteArray qbytes) {
511 | switch (channel) {
512 | case DeviceStateEnum::DeviceChannel::CHANNEL_A:
513 | {
514 | QMutexLocker locker(&customWaveAMutex);
515 | customWaveA.push_back(qbytes);
516 | }
517 | break;
518 | case DeviceStateEnum::DeviceChannel::CHANNEL_B:
519 | {
520 | QMutexLocker locker(&customWaveBMutex);
521 | customWaveB.push_back(qbytes);
522 | }
523 | break;
524 | }
525 | }
526 | QString DGLabDevice::getWaveAPI(DeviceStateEnum::DeviceChannel channel) {
527 | switch (channel) {
528 | case DeviceStateEnum::DeviceChannel::CHANNEL_A:
529 | return uiDeviceOperator->getWaveA();
530 | case DeviceStateEnum::DeviceChannel::CHANNEL_B:
531 | return uiDeviceOperator->getWaveB();
532 | }
533 | return "";
534 | }
535 | void DGLabDevice::setWaveAPI(DeviceStateEnum::DeviceChannel channel, QString str) {
536 | QString wave;
537 | if (Global::basicWaveNameList.indexOf(str) >= 0 || Global::touchWaveNameList.indexOf(str) >= 0) {
538 | wave = str;
539 | } else {
540 | wave = "外部输入";
541 | }
542 | switch (channel) {
543 | case DeviceStateEnum::DeviceChannel::CHANNEL_A:
544 | {
545 | uiDeviceOperator->setWaveA(wave);
546 | }
547 | break;
548 | case DeviceStateEnum::DeviceChannel::CHANNEL_B:
549 | {
550 |
551 | uiDeviceOperator->setWaveB(wave);
552 | }
553 | break;
554 | }
555 | }
556 | int DGLabDevice::getStrengthAPI(DeviceStateEnum::DeviceChannel channel) {
557 | switch (channel) {
558 | case DeviceStateEnum::DeviceChannel::CHANNEL_A:
559 | return powerA;
560 | case DeviceStateEnum::DeviceChannel::CHANNEL_B:
561 | return powerB;
562 | }
563 | return false;
564 | }
565 | void DGLabDevice::setStrengthAPI(int level_A, int level_B) {
566 | changePower(level_A, level_B);
567 | }
568 | void DGLabDevice::setDeviceRemoteLocked(bool lock) {
569 | remoteLocked = lock;
570 | if (remoteLocked) {
571 | uiDeviceOperator->setAutoChange(DeviceStateEnum::DeviceChannel::CHANNEL_A, false);
572 | uiDeviceOperator->setAutoChange(DeviceStateEnum::DeviceChannel::CHANNEL_B, false);
573 | uiDeviceItem->changeState(DeviceStateEnum::DeviceState::READY_REMOTEMANAGED);
574 | uiDeviceOperator->setEnabled(false);
575 | } else {
576 | if (controller->state() == QLowEnergyController::UnconnectedState) {
577 | uiDeviceItem->changeState(DeviceStateEnum::DeviceState::UNCONNECTED);
578 | uiDeviceOperator->setEnabled(false);
579 | } else {
580 | uiDeviceItem->changeState(DeviceStateEnum::DeviceState::READY);
581 | uiDeviceOperator->setEnabled(true);
582 | }
583 | }
584 | }
585 | bool DGLabDevice::getDeviceRemoteLocked()
586 | {
587 | return remoteLocked;
588 | }
589 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | GNU AFFERO GENERAL PUBLIC LICENSE
2 | Version 3, 19 November 2007
3 |
4 | Copyright (C) 2007 Free Software Foundation, Inc.
5 | Everyone is permitted to copy and distribute verbatim copies
6 | of this license document, but changing it is not allowed.
7 |
8 | Preamble
9 |
10 | The GNU Affero General Public License is a free, copyleft license for
11 | software and other kinds of works, specifically designed to ensure
12 | cooperation with the community in the case of network server software.
13 |
14 | The licenses for most software and other practical works are designed
15 | to take away your freedom to share and change the works. By contrast,
16 | our General Public Licenses are intended to guarantee your freedom to
17 | share and change all versions of a program--to make sure it remains free
18 | software for all its users.
19 |
20 | When we speak of free software, we are referring to freedom, not
21 | price. Our General Public Licenses are designed to make sure that you
22 | have the freedom to distribute copies of free software (and charge for
23 | them if you wish), that you receive source code or can get it if you
24 | want it, that you can change the software or use pieces of it in new
25 | free programs, and that you know you can do these things.
26 |
27 | Developers that use our General Public Licenses protect your rights
28 | with two steps: (1) assert copyright on the software, and (2) offer
29 | you this License which gives you legal permission to copy, distribute
30 | and/or modify the software.
31 |
32 | A secondary benefit of defending all users' freedom is that
33 | improvements made in alternate versions of the program, if they
34 | receive widespread use, become available for other developers to
35 | incorporate. Many developers of free software are heartened and
36 | encouraged by the resulting cooperation. However, in the case of
37 | software used on network servers, this result may fail to come about.
38 | The GNU General Public License permits making a modified version and
39 | letting the public access it on a server without ever releasing its
40 | source code to the public.
41 |
42 | The GNU Affero General Public License is designed specifically to
43 | ensure that, in such cases, the modified source code becomes available
44 | to the community. It requires the operator of a network server to
45 | provide the source code of the modified version running there to the
46 | users of that server. Therefore, public use of a modified version, on
47 | a publicly accessible server, gives the public access to the source
48 | code of the modified version.
49 |
50 | An older license, called the Affero General Public License and
51 | published by Affero, was designed to accomplish similar goals. This is
52 | a different license, not a version of the Affero GPL, but Affero has
53 | released a new version of the Affero GPL which permits relicensing under
54 | this license.
55 |
56 | The precise terms and conditions for copying, distribution and
57 | modification follow.
58 |
59 | TERMS AND CONDITIONS
60 |
61 | 0. Definitions.
62 |
63 | "This License" refers to version 3 of the GNU Affero General Public License.
64 |
65 | "Copyright" also means copyright-like laws that apply to other kinds of
66 | works, such as semiconductor masks.
67 |
68 | "The Program" refers to any copyrightable work licensed under this
69 | License. Each licensee is addressed as "you". "Licensees" and
70 | "recipients" may be individuals or organizations.
71 |
72 | To "modify" a work means to copy from or adapt all or part of the work
73 | in a fashion requiring copyright permission, other than the making of an
74 | exact copy. The resulting work is called a "modified version" of the
75 | earlier work or a work "based on" the earlier work.
76 |
77 | A "covered work" means either the unmodified Program or a work based
78 | on the Program.
79 |
80 | To "propagate" a work means to do anything with it that, without
81 | permission, would make you directly or secondarily liable for
82 | infringement under applicable copyright law, except executing it on a
83 | computer or modifying a private copy. Propagation includes copying,
84 | distribution (with or without modification), making available to the
85 | public, and in some countries other activities as well.
86 |
87 | To "convey" a work means any kind of propagation that enables other
88 | parties to make or receive copies. Mere interaction with a user through
89 | a computer network, with no transfer of a copy, is not conveying.
90 |
91 | An interactive user interface displays "Appropriate Legal Notices"
92 | to the extent that it includes a convenient and prominently visible
93 | feature that (1) displays an appropriate copyright notice, and (2)
94 | tells the user that there is no warranty for the work (except to the
95 | extent that warranties are provided), that licensees may convey the
96 | work under this License, and how to view a copy of this License. If
97 | the interface presents a list of user commands or options, such as a
98 | menu, a prominent item in the list meets this criterion.
99 |
100 | 1. Source Code.
101 |
102 | The "source code" for a work means the preferred form of the work
103 | for making modifications to it. "Object code" means any non-source
104 | form of a work.
105 |
106 | A "Standard Interface" means an interface that either is an official
107 | standard defined by a recognized standards body, or, in the case of
108 | interfaces specified for a particular programming language, one that
109 | is widely used among developers working in that language.
110 |
111 | The "System Libraries" of an executable work include anything, other
112 | than the work as a whole, that (a) is included in the normal form of
113 | packaging a Major Component, but which is not part of that Major
114 | Component, and (b) serves only to enable use of the work with that
115 | Major Component, or to implement a Standard Interface for which an
116 | implementation is available to the public in source code form. A
117 | "Major Component", in this context, means a major essential component
118 | (kernel, window system, and so on) of the specific operating system
119 | (if any) on which the executable work runs, or a compiler used to
120 | produce the work, or an object code interpreter used to run it.
121 |
122 | The "Corresponding Source" for a work in object code form means all
123 | the source code needed to generate, install, and (for an executable
124 | work) run the object code and to modify the work, including scripts to
125 | control those activities. However, it does not include the work's
126 | System Libraries, or general-purpose tools or generally available free
127 | programs which are used unmodified in performing those activities but
128 | which are not part of the work. For example, Corresponding Source
129 | includes interface definition files associated with source files for
130 | the work, and the source code for shared libraries and dynamically
131 | linked subprograms that the work is specifically designed to require,
132 | such as by intimate data communication or control flow between those
133 | subprograms and other parts of the work.
134 |
135 | The Corresponding Source need not include anything that users
136 | can regenerate automatically from other parts of the Corresponding
137 | Source.
138 |
139 | The Corresponding Source for a work in source code form is that
140 | same work.
141 |
142 | 2. Basic Permissions.
143 |
144 | All rights granted under this License are granted for the term of
145 | copyright on the Program, and are irrevocable provided the stated
146 | conditions are met. This License explicitly affirms your unlimited
147 | permission to run the unmodified Program. The output from running a
148 | covered work is covered by this License only if the output, given its
149 | content, constitutes a covered work. This License acknowledges your
150 | rights of fair use or other equivalent, as provided by copyright law.
151 |
152 | You may make, run and propagate covered works that you do not
153 | convey, without conditions so long as your license otherwise remains
154 | in force. You may convey covered works to others for the sole purpose
155 | of having them make modifications exclusively for you, or provide you
156 | with facilities for running those works, provided that you comply with
157 | the terms of this License in conveying all material for which you do
158 | not control copyright. Those thus making or running the covered works
159 | for you must do so exclusively on your behalf, under your direction
160 | and control, on terms that prohibit them from making any copies of
161 | your copyrighted material outside their relationship with you.
162 |
163 | Conveying under any other circumstances is permitted solely under
164 | the conditions stated below. Sublicensing is not allowed; section 10
165 | makes it unnecessary.
166 |
167 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
168 |
169 | No covered work shall be deemed part of an effective technological
170 | measure under any applicable law fulfilling obligations under article
171 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or
172 | similar laws prohibiting or restricting circumvention of such
173 | measures.
174 |
175 | When you convey a covered work, you waive any legal power to forbid
176 | circumvention of technological measures to the extent such circumvention
177 | is effected by exercising rights under this License with respect to
178 | the covered work, and you disclaim any intention to limit operation or
179 | modification of the work as a means of enforcing, against the work's
180 | users, your or third parties' legal rights to forbid circumvention of
181 | technological measures.
182 |
183 | 4. Conveying Verbatim Copies.
184 |
185 | You may convey verbatim copies of the Program's source code as you
186 | receive it, in any medium, provided that you conspicuously and
187 | appropriately publish on each copy an appropriate copyright notice;
188 | keep intact all notices stating that this License and any
189 | non-permissive terms added in accord with section 7 apply to the code;
190 | keep intact all notices of the absence of any warranty; and give all
191 | recipients a copy of this License along with the Program.
192 |
193 | You may charge any price or no price for each copy that you convey,
194 | and you may offer support or warranty protection for a fee.
195 |
196 | 5. Conveying Modified Source Versions.
197 |
198 | You may convey a work based on the Program, or the modifications to
199 | produce it from the Program, in the form of source code under the
200 | terms of section 4, provided that you also meet all of these conditions:
201 |
202 | a) The work must carry prominent notices stating that you modified
203 | it, and giving a relevant date.
204 |
205 | b) The work must carry prominent notices stating that it is
206 | released under this License and any conditions added under section
207 | 7. This requirement modifies the requirement in section 4 to
208 | "keep intact all notices".
209 |
210 | c) You must license the entire work, as a whole, under this
211 | License to anyone who comes into possession of a copy. This
212 | License will therefore apply, along with any applicable section 7
213 | additional terms, to the whole of the work, and all its parts,
214 | regardless of how they are packaged. This License gives no
215 | permission to license the work in any other way, but it does not
216 | invalidate such permission if you have separately received it.
217 |
218 | d) If the work has interactive user interfaces, each must display
219 | Appropriate Legal Notices; however, if the Program has interactive
220 | interfaces that do not display Appropriate Legal Notices, your
221 | work need not make them do so.
222 |
223 | A compilation of a covered work with other separate and independent
224 | works, which are not by their nature extensions of the covered work,
225 | and which are not combined with it such as to form a larger program,
226 | in or on a volume of a storage or distribution medium, is called an
227 | "aggregate" if the compilation and its resulting copyright are not
228 | used to limit the access or legal rights of the compilation's users
229 | beyond what the individual works permit. Inclusion of a covered work
230 | in an aggregate does not cause this License to apply to the other
231 | parts of the aggregate.
232 |
233 | 6. Conveying Non-Source Forms.
234 |
235 | You may convey a covered work in object code form under the terms
236 | of sections 4 and 5, provided that you also convey the
237 | machine-readable Corresponding Source under the terms of this License,
238 | in one of these ways:
239 |
240 | a) Convey the object code in, or embodied in, a physical product
241 | (including a physical distribution medium), accompanied by the
242 | Corresponding Source fixed on a durable physical medium
243 | customarily used for software interchange.
244 |
245 | b) Convey the object code in, or embodied in, a physical product
246 | (including a physical distribution medium), accompanied by a
247 | written offer, valid for at least three years and valid for as
248 | long as you offer spare parts or customer support for that product
249 | model, to give anyone who possesses the object code either (1) a
250 | copy of the Corresponding Source for all the software in the
251 | product that is covered by this License, on a durable physical
252 | medium customarily used for software interchange, for a price no
253 | more than your reasonable cost of physically performing this
254 | conveying of source, or (2) access to copy the
255 | Corresponding Source from a network server at no charge.
256 |
257 | c) Convey individual copies of the object code with a copy of the
258 | written offer to provide the Corresponding Source. This
259 | alternative is allowed only occasionally and noncommercially, and
260 | only if you received the object code with such an offer, in accord
261 | with subsection 6b.
262 |
263 | d) Convey the object code by offering access from a designated
264 | place (gratis or for a charge), and offer equivalent access to the
265 | Corresponding Source in the same way through the same place at no
266 | further charge. You need not require recipients to copy the
267 | Corresponding Source along with the object code. If the place to
268 | copy the object code is a network server, the Corresponding Source
269 | may be on a different server (operated by you or a third party)
270 | that supports equivalent copying facilities, provided you maintain
271 | clear directions next to the object code saying where to find the
272 | Corresponding Source. Regardless of what server hosts the
273 | Corresponding Source, you remain obligated to ensure that it is
274 | available for as long as needed to satisfy these requirements.
275 |
276 | e) Convey the object code using peer-to-peer transmission, provided
277 | you inform other peers where the object code and Corresponding
278 | Source of the work are being offered to the general public at no
279 | charge under subsection 6d.
280 |
281 | A separable portion of the object code, whose source code is excluded
282 | from the Corresponding Source as a System Library, need not be
283 | included in conveying the object code work.
284 |
285 | A "User Product" is either (1) a "consumer product", which means any
286 | tangible personal property which is normally used for personal, family,
287 | or household purposes, or (2) anything designed or sold for incorporation
288 | into a dwelling. In determining whether a product is a consumer product,
289 | doubtful cases shall be resolved in favor of coverage. For a particular
290 | product received by a particular user, "normally used" refers to a
291 | typical or common use of that class of product, regardless of the status
292 | of the particular user or of the way in which the particular user
293 | actually uses, or expects or is expected to use, the product. A product
294 | is a consumer product regardless of whether the product has substantial
295 | commercial, industrial or non-consumer uses, unless such uses represent
296 | the only significant mode of use of the product.
297 |
298 | "Installation Information" for a User Product means any methods,
299 | procedures, authorization keys, or other information required to install
300 | and execute modified versions of a covered work in that User Product from
301 | a modified version of its Corresponding Source. The information must
302 | suffice to ensure that the continued functioning of the modified object
303 | code is in no case prevented or interfered with solely because
304 | modification has been made.
305 |
306 | If you convey an object code work under this section in, or with, or
307 | specifically for use in, a User Product, and the conveying occurs as
308 | part of a transaction in which the right of possession and use of the
309 | User Product is transferred to the recipient in perpetuity or for a
310 | fixed term (regardless of how the transaction is characterized), the
311 | Corresponding Source conveyed under this section must be accompanied
312 | by the Installation Information. But this requirement does not apply
313 | if neither you nor any third party retains the ability to install
314 | modified object code on the User Product (for example, the work has
315 | been installed in ROM).
316 |
317 | The requirement to provide Installation Information does not include a
318 | requirement to continue to provide support service, warranty, or updates
319 | for a work that has been modified or installed by the recipient, or for
320 | the User Product in which it has been modified or installed. Access to a
321 | network may be denied when the modification itself materially and
322 | adversely affects the operation of the network or violates the rules and
323 | protocols for communication across the network.
324 |
325 | Corresponding Source conveyed, and Installation Information provided,
326 | in accord with this section must be in a format that is publicly
327 | documented (and with an implementation available to the public in
328 | source code form), and must require no special password or key for
329 | unpacking, reading or copying.
330 |
331 | 7. Additional Terms.
332 |
333 | "Additional permissions" are terms that supplement the terms of this
334 | License by making exceptions from one or more of its conditions.
335 | Additional permissions that are applicable to the entire Program shall
336 | be treated as though they were included in this License, to the extent
337 | that they are valid under applicable law. If additional permissions
338 | apply only to part of the Program, that part may be used separately
339 | under those permissions, but the entire Program remains governed by
340 | this License without regard to the additional permissions.
341 |
342 | When you convey a copy of a covered work, you may at your option
343 | remove any additional permissions from that copy, or from any part of
344 | it. (Additional permissions may be written to require their own
345 | removal in certain cases when you modify the work.) You may place
346 | additional permissions on material, added by you to a covered work,
347 | for which you have or can give appropriate copyright permission.
348 |
349 | Notwithstanding any other provision of this License, for material you
350 | add to a covered work, you may (if authorized by the copyright holders of
351 | that material) supplement the terms of this License with terms:
352 |
353 | a) Disclaiming warranty or limiting liability differently from the
354 | terms of sections 15 and 16 of this License; or
355 |
356 | b) Requiring preservation of specified reasonable legal notices or
357 | author attributions in that material or in the Appropriate Legal
358 | Notices displayed by works containing it; or
359 |
360 | c) Prohibiting misrepresentation of the origin of that material, or
361 | requiring that modified versions of such material be marked in
362 | reasonable ways as different from the original version; or
363 |
364 | d) Limiting the use for publicity purposes of names of licensors or
365 | authors of the material; or
366 |
367 | e) Declining to grant rights under trademark law for use of some
368 | trade names, trademarks, or service marks; or
369 |
370 | f) Requiring indemnification of licensors and authors of that
371 | material by anyone who conveys the material (or modified versions of
372 | it) with contractual assumptions of liability to the recipient, for
373 | any liability that these contractual assumptions directly impose on
374 | those licensors and authors.
375 |
376 | All other non-permissive additional terms are considered "further
377 | restrictions" within the meaning of section 10. If the Program as you
378 | received it, or any part of it, contains a notice stating that it is
379 | governed by this License along with a term that is a further
380 | restriction, you may remove that term. If a license document contains
381 | a further restriction but permits relicensing or conveying under this
382 | License, you may add to a covered work material governed by the terms
383 | of that license document, provided that the further restriction does
384 | not survive such relicensing or conveying.
385 |
386 | If you add terms to a covered work in accord with this section, you
387 | must place, in the relevant source files, a statement of the
388 | additional terms that apply to those files, or a notice indicating
389 | where to find the applicable terms.
390 |
391 | Additional terms, permissive or non-permissive, may be stated in the
392 | form of a separately written license, or stated as exceptions;
393 | the above requirements apply either way.
394 |
395 | 8. Termination.
396 |
397 | You may not propagate or modify a covered work except as expressly
398 | provided under this License. Any attempt otherwise to propagate or
399 | modify it is void, and will automatically terminate your rights under
400 | this License (including any patent licenses granted under the third
401 | paragraph of section 11).
402 |
403 | However, if you cease all violation of this License, then your
404 | license from a particular copyright holder is reinstated (a)
405 | provisionally, unless and until the copyright holder explicitly and
406 | finally terminates your license, and (b) permanently, if the copyright
407 | holder fails to notify you of the violation by some reasonable means
408 | prior to 60 days after the cessation.
409 |
410 | Moreover, your license from a particular copyright holder is
411 | reinstated permanently if the copyright holder notifies you of the
412 | violation by some reasonable means, this is the first time you have
413 | received notice of violation of this License (for any work) from that
414 | copyright holder, and you cure the violation prior to 30 days after
415 | your receipt of the notice.
416 |
417 | Termination of your rights under this section does not terminate the
418 | licenses of parties who have received copies or rights from you under
419 | this License. If your rights have been terminated and not permanently
420 | reinstated, you do not qualify to receive new licenses for the same
421 | material under section 10.
422 |
423 | 9. Acceptance Not Required for Having Copies.
424 |
425 | You are not required to accept this License in order to receive or
426 | run a copy of the Program. Ancillary propagation of a covered work
427 | occurring solely as a consequence of using peer-to-peer transmission
428 | to receive a copy likewise does not require acceptance. However,
429 | nothing other than this License grants you permission to propagate or
430 | modify any covered work. These actions infringe copyright if you do
431 | not accept this License. Therefore, by modifying or propagating a
432 | covered work, you indicate your acceptance of this License to do so.
433 |
434 | 10. Automatic Licensing of Downstream Recipients.
435 |
436 | Each time you convey a covered work, the recipient automatically
437 | receives a license from the original licensors, to run, modify and
438 | propagate that work, subject to this License. You are not responsible
439 | for enforcing compliance by third parties with this License.
440 |
441 | An "entity transaction" is a transaction transferring control of an
442 | organization, or substantially all assets of one, or subdividing an
443 | organization, or merging organizations. If propagation of a covered
444 | work results from an entity transaction, each party to that
445 | transaction who receives a copy of the work also receives whatever
446 | licenses to the work the party's predecessor in interest had or could
447 | give under the previous paragraph, plus a right to possession of the
448 | Corresponding Source of the work from the predecessor in interest, if
449 | the predecessor has it or can get it with reasonable efforts.
450 |
451 | You may not impose any further restrictions on the exercise of the
452 | rights granted or affirmed under this License. For example, you may
453 | not impose a license fee, royalty, or other charge for exercise of
454 | rights granted under this License, and you may not initiate litigation
455 | (including a cross-claim or counterclaim in a lawsuit) alleging that
456 | any patent claim is infringed by making, using, selling, offering for
457 | sale, or importing the Program or any portion of it.
458 |
459 | 11. Patents.
460 |
461 | A "contributor" is a copyright holder who authorizes use under this
462 | License of the Program or a work on which the Program is based. The
463 | work thus licensed is called the contributor's "contributor version".
464 |
465 | A contributor's "essential patent claims" are all patent claims
466 | owned or controlled by the contributor, whether already acquired or
467 | hereafter acquired, that would be infringed by some manner, permitted
468 | by this License, of making, using, or selling its contributor version,
469 | but do not include claims that would be infringed only as a
470 | consequence of further modification of the contributor version. For
471 | purposes of this definition, "control" includes the right to grant
472 | patent sublicenses in a manner consistent with the requirements of
473 | this License.
474 |
475 | Each contributor grants you a non-exclusive, worldwide, royalty-free
476 | patent license under the contributor's essential patent claims, to
477 | make, use, sell, offer for sale, import and otherwise run, modify and
478 | propagate the contents of its contributor version.
479 |
480 | In the following three paragraphs, a "patent license" is any express
481 | agreement or commitment, however denominated, not to enforce a patent
482 | (such as an express permission to practice a patent or covenant not to
483 | sue for patent infringement). To "grant" such a patent license to a
484 | party means to make such an agreement or commitment not to enforce a
485 | patent against the party.
486 |
487 | If you convey a covered work, knowingly relying on a patent license,
488 | and the Corresponding Source of the work is not available for anyone
489 | to copy, free of charge and under the terms of this License, through a
490 | publicly available network server or other readily accessible means,
491 | then you must either (1) cause the Corresponding Source to be so
492 | available, or (2) arrange to deprive yourself of the benefit of the
493 | patent license for this particular work, or (3) arrange, in a manner
494 | consistent with the requirements of this License, to extend the patent
495 | license to downstream recipients. "Knowingly relying" means you have
496 | actual knowledge that, but for the patent license, your conveying the
497 | covered work in a country, or your recipient's use of the covered work
498 | in a country, would infringe one or more identifiable patents in that
499 | country that you have reason to believe are valid.
500 |
501 | If, pursuant to or in connection with a single transaction or
502 | arrangement, you convey, or propagate by procuring conveyance of, a
503 | covered work, and grant a patent license to some of the parties
504 | receiving the covered work authorizing them to use, propagate, modify
505 | or convey a specific copy of the covered work, then the patent license
506 | you grant is automatically extended to all recipients of the covered
507 | work and works based on it.
508 |
509 | A patent license is "discriminatory" if it does not include within
510 | the scope of its coverage, prohibits the exercise of, or is
511 | conditioned on the non-exercise of one or more of the rights that are
512 | specifically granted under this License. You may not convey a covered
513 | work if you are a party to an arrangement with a third party that is
514 | in the business of distributing software, under which you make payment
515 | to the third party based on the extent of your activity of conveying
516 | the work, and under which the third party grants, to any of the
517 | parties who would receive the covered work from you, a discriminatory
518 | patent license (a) in connection with copies of the covered work
519 | conveyed by you (or copies made from those copies), or (b) primarily
520 | for and in connection with specific products or compilations that
521 | contain the covered work, unless you entered into that arrangement,
522 | or that patent license was granted, prior to 28 March 2007.
523 |
524 | Nothing in this License shall be construed as excluding or limiting
525 | any implied license or other defenses to infringement that may
526 | otherwise be available to you under applicable patent law.
527 |
528 | 12. No Surrender of Others' Freedom.
529 |
530 | If conditions are imposed on you (whether by court order, agreement or
531 | otherwise) that contradict the conditions of this License, they do not
532 | excuse you from the conditions of this License. If you cannot convey a
533 | covered work so as to satisfy simultaneously your obligations under this
534 | License and any other pertinent obligations, then as a consequence you may
535 | not convey it at all. For example, if you agree to terms that obligate you
536 | to collect a royalty for further conveying from those to whom you convey
537 | the Program, the only way you could satisfy both those terms and this
538 | License would be to refrain entirely from conveying the Program.
539 |
540 | 13. Remote Network Interaction; Use with the GNU General Public License.
541 |
542 | Notwithstanding any other provision of this License, if you modify the
543 | Program, your modified version must prominently offer all users
544 | interacting with it remotely through a computer network (if your version
545 | supports such interaction) an opportunity to receive the Corresponding
546 | Source of your version by providing access to the Corresponding Source
547 | from a network server at no charge, through some standard or customary
548 | means of facilitating copying of software. This Corresponding Source
549 | shall include the Corresponding Source for any work covered by version 3
550 | of the GNU General Public License that is incorporated pursuant to the
551 | following paragraph.
552 |
553 | Notwithstanding any other provision of this License, you have
554 | permission to link or combine any covered work with a work licensed
555 | under version 3 of the GNU General Public License into a single
556 | combined work, and to convey the resulting work. The terms of this
557 | License will continue to apply to the part which is the covered work,
558 | but the work with which it is combined will remain governed by version
559 | 3 of the GNU General Public License.
560 |
561 | 14. Revised Versions of this License.
562 |
563 | The Free Software Foundation may publish revised and/or new versions of
564 | the GNU Affero General Public License from time to time. Such new versions
565 | will be similar in spirit to the present version, but may differ in detail to
566 | address new problems or concerns.
567 |
568 | Each version is given a distinguishing version number. If the
569 | Program specifies that a certain numbered version of the GNU Affero General
570 | Public License "or any later version" applies to it, you have the
571 | option of following the terms and conditions either of that numbered
572 | version or of any later version published by the Free Software
573 | Foundation. If the Program does not specify a version number of the
574 | GNU Affero General Public License, you may choose any version ever published
575 | by the Free Software Foundation.
576 |
577 | If the Program specifies that a proxy can decide which future
578 | versions of the GNU Affero General Public License can be used, that proxy's
579 | public statement of acceptance of a version permanently authorizes you
580 | to choose that version for the Program.
581 |
582 | Later license versions may give you additional or different
583 | permissions. However, no additional obligations are imposed on any
584 | author or copyright holder as a result of your choosing to follow a
585 | later version.
586 |
587 | 15. Disclaimer of Warranty.
588 |
589 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
590 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
591 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
592 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
593 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
594 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
595 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
596 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
597 |
598 | 16. Limitation of Liability.
599 |
600 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
601 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
602 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
603 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
604 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
605 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
606 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
607 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
608 | SUCH DAMAGES.
609 |
610 | 17. Interpretation of Sections 15 and 16.
611 |
612 | If the disclaimer of warranty and limitation of liability provided
613 | above cannot be given local legal effect according to their terms,
614 | reviewing courts shall apply local law that most closely approximates
615 | an absolute waiver of all civil liability in connection with the
616 | Program, unless a warranty or assumption of liability accompanies a
617 | copy of the Program in return for a fee.
618 |
619 | END OF TERMS AND CONDITIONS
620 |
621 | How to Apply These Terms to Your New Programs
622 |
623 | If you develop a new program, and you want it to be of the greatest
624 | possible use to the public, the best way to achieve this is to make it
625 | free software which everyone can redistribute and change under these terms.
626 |
627 | To do so, attach the following notices to the program. It is safest
628 | to attach them to the start of each source file to most effectively
629 | state the exclusion of warranty; and each file should have at least
630 | the "copyright" line and a pointer to where the full notice is found.
631 |
632 | OpenDGLab Desktop
633 | Copyright (C) 2020/9/28 Sound Reload
634 |
635 | This program is free software: you can redistribute it and/or modify
636 | it under the terms of the GNU Affero General Public License as published
637 | by the Free Software Foundation, either version 3 of the License, or
638 | (at your option) any later version.
639 |
640 | This program is distributed in the hope that it will be useful,
641 | but WITHOUT ANY WARRANTY; without even the implied warranty of
642 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
643 | GNU Affero General Public License for more details.
644 |
645 | You should have received a copy of the GNU Affero General Public License
646 | along with this program. If not, see .
647 |
648 | Also add information on how to contact you by electronic and paper mail.
649 |
650 | If your software can interact with users remotely through a computer
651 | network, you should also make sure that it provides a way for users to
652 | get its source. For example, if your program is a web application, its
653 | interface could display a "Source" link that leads users to an archive
654 | of the code. There are many ways you could offer source, and different
655 | solutions will be better for different programs; see section 13 for the
656 | specific requirements.
657 |
658 | You should also get your employer (if you work as a programmer) or school,
659 | if any, to sign a "copyright disclaimer" for the program, if necessary.
660 | For more information on this, and how to apply and follow the GNU AGPL, see
661 | .
--------------------------------------------------------------------------------