322 lines
11 KiB
C++
322 lines
11 KiB
C++
#include "mainwindow.h"
|
|
#include "ui_mainwindow.h"
|
|
|
|
#include <QInputDialog>
|
|
#include <QSortFilterProxyModel>
|
|
#include <QMenu>
|
|
#include <QInputDialog>
|
|
#include <QMessageBox>
|
|
#include <QJsonObject>
|
|
#include <QJsonArray>
|
|
|
|
#include <algorithm>
|
|
#include <utility>
|
|
|
|
#include "flottenupdatersettings.h"
|
|
#include "devicesmodel.h"
|
|
#include "requestdialog.h"
|
|
#include "setarbitraryapikeydialog.h"
|
|
#include "addserialsrangedialog.h"
|
|
|
|
MainWindow::MainWindow(FlottenUpdaterSettings &settings, const QSslKey &key,
|
|
const QSslCertificate &cert, QWidget *parent) :
|
|
QMainWindow{parent},
|
|
m_ui{std::make_unique<Ui::MainWindow>()},
|
|
m_settings{settings},
|
|
m_model{std::make_unique<DevicesModel>(settings, key, cert, this)},
|
|
m_proxyModel{std::make_unique<QSortFilterProxyModel>(this)}
|
|
{
|
|
m_ui->setupUi(this);
|
|
|
|
m_proxyModel->setSourceModel(m_model.get());
|
|
m_proxyModel->setSortRole(Qt::EditRole);
|
|
m_ui->treeView->setModel(m_proxyModel.get());
|
|
|
|
connect(m_ui->pushButtonConnectAll, &QAbstractButton::pressed, m_model.get(), &DevicesModel::connectAll);
|
|
connect(m_ui->pushButtonDisconnectAll, &QAbstractButton::pressed, m_model.get(), &DevicesModel::disconnectAll);
|
|
connect(m_ui->pushButtonAdd, &QAbstractButton::pressed, this, &MainWindow::doAdd);
|
|
connect(m_ui->pushButtonRemove, &QAbstractButton::pressed, this, &MainWindow::doRemove);
|
|
connect(m_ui->pushButtonAddRange, &QAbstractButton::pressed, this, &MainWindow::doAddRange);
|
|
connect(m_ui->treeView->selectionModel(), &QItemSelectionModel::selectionChanged, this, &MainWindow::selectionChanged);
|
|
m_ui->treeView->header()->setContextMenuPolicy(Qt::CustomContextMenu);
|
|
connect(m_ui->treeView->header(), &QTreeView::customContextMenuRequested, this, &MainWindow::headerContextMenuRequested);
|
|
connect(m_ui->treeView, &QTreeView::customContextMenuRequested, this, &MainWindow::contextMenuRequested);
|
|
selectionChanged();
|
|
}
|
|
|
|
MainWindow::~MainWindow() = default;
|
|
|
|
void MainWindow::doAdd()
|
|
{
|
|
bool ok{};
|
|
const auto serial = QInputDialog::getText(this, tr("Serial"), tr("Serial"), QLineEdit::Normal, {}, &ok);
|
|
if (!ok)
|
|
return;
|
|
if (m_model->addClient(serial))
|
|
m_settings.setSerials(m_model->serials());
|
|
else
|
|
QMessageBox::warning(this, tr("Error while adding serial!"), tr("Error while adding serial!"));
|
|
}
|
|
|
|
void MainWindow::doRemove()
|
|
{
|
|
auto selectedRows = m_ui->treeView->selectionModel()->selectedRows();
|
|
if (selectedRows.isEmpty())
|
|
return;
|
|
|
|
// map proxied indices to those from the model
|
|
std::transform(std::begin(selectedRows), std::end(selectedRows), std::begin(selectedRows),
|
|
[&](const QModelIndex &index){ return m_proxyModel->mapToSource(index); });
|
|
|
|
removeRows(std::move(selectedRows));
|
|
}
|
|
|
|
void MainWindow::doAddRange()
|
|
{
|
|
AddSerialsRangeDialog dialog{this};
|
|
if (dialog.exec() != QDialog::Accepted)
|
|
return;
|
|
|
|
int succeeded{}, failed{};
|
|
|
|
for (auto serial = dialog.from(); serial <= dialog.to(); serial++)
|
|
{
|
|
if (m_model->addClient(QString::number(serial)))
|
|
succeeded++;
|
|
else
|
|
{
|
|
qWarning() << "adding serial" << serial << "failed";
|
|
failed++;
|
|
}
|
|
}
|
|
|
|
if (succeeded > 0)
|
|
m_settings.setSerials(m_model->serials());
|
|
|
|
if (failed > 0)
|
|
QMessageBox::warning(this, tr("Error while adding!"), tr("%0 rows could not be adding!").arg(failed));
|
|
}
|
|
|
|
void MainWindow::selectionChanged()
|
|
{
|
|
auto count = m_ui->treeView->selectionModel()->selectedRows().count();
|
|
m_ui->pushButtonRemove->setEnabled(count > 0);
|
|
m_ui->statusbar->showMessage(tr("%0 selected").arg(m_ui->treeView->selectionModel()->selectedRows().count()));
|
|
}
|
|
|
|
void MainWindow::headerContextMenuRequested(const QPoint &pos)
|
|
{
|
|
QMenu menu;
|
|
|
|
auto addColumnAction = menu.addAction(tr("Add new column..."));
|
|
|
|
QAction *removeColumnAction{};
|
|
auto header = m_ui->treeView->header();
|
|
const auto section = header->logicalIndexAt(pos);
|
|
if (section >= 0)
|
|
{
|
|
removeColumnAction = menu.addAction(tr("Remove column %0").arg(header->model()->headerData(section, Qt::Horizontal).toString()));
|
|
if (!m_model->customColumnRemovable(section))
|
|
removeColumnAction->setEnabled(false);
|
|
}
|
|
|
|
if (auto selectedAction = menu.exec(header->mapToGlobal(pos)); selectedAction == addColumnAction)
|
|
{
|
|
bool ok{};
|
|
auto apiKey = QInputDialog::getText(this, tr("Enter api key"), tr("Api key:"), QLineEdit::Normal, {}, &ok);
|
|
if (!ok)
|
|
return;
|
|
|
|
m_model->addCustomColumn(apiKey);
|
|
}
|
|
else if (removeColumnAction && selectedAction == removeColumnAction)
|
|
{
|
|
m_model->removeCustomColumn(section);
|
|
}
|
|
}
|
|
|
|
void MainWindow::contextMenuRequested(const QPoint &pos)
|
|
{
|
|
auto selectedRows = m_ui->treeView->selectionModel()->selectedRows();
|
|
if (selectedRows.isEmpty())
|
|
return;
|
|
|
|
// map proxied indices to those from the model
|
|
std::transform(std::begin(selectedRows), std::end(selectedRows), std::begin(selectedRows),
|
|
[&](const QModelIndex &index){ return m_proxyModel->mapToSource(index); });
|
|
|
|
// get all the devices for selected indices
|
|
std::vector<std::shared_ptr<DevicesConnection>> devices;
|
|
devices.reserve(selectedRows.size());
|
|
std::transform(std::begin(selectedRows), std::end(selectedRows), std::back_inserter(devices),
|
|
[&](const QModelIndex &index){ auto device = m_model->getDevice(index); Q_ASSERT(device); return device; });
|
|
|
|
Q_ASSERT(std::all_of(std::begin(devices), std::end(devices), [](const auto &device)->bool{ return device.get(); }));
|
|
|
|
QMenu menu;
|
|
auto actionSetUpdateUrl = menu.addAction(tr("Set update url..."));
|
|
auto actionStartUpdate = menu.addAction(tr("Start update..."));
|
|
auto actionReboot = menu.addAction(tr("Reboot..."));
|
|
auto actionSetChargectrlOverride = menu.addAction(tr("Set chargectrl override..."));
|
|
auto actionSetAbitraryApiKey = menu.addAction(tr("Set abitrary api key..."));
|
|
auto resetNvsKey = menu.addAction(tr("Reset nvs key..."));
|
|
auto actionOpenApps = menu.addAction(tr("Open app(s)..."));
|
|
auto actionRemove = menu.addAction(tr("Remove..."));
|
|
if (const auto selected = menu.exec(m_ui->treeView->viewport()->mapToGlobal(pos)); selected == actionSetUpdateUrl)
|
|
{
|
|
bool ok{};
|
|
const auto url = QInputDialog::getText(this, tr("Enter update url..."), tr("Update url:"), QLineEdit::Normal, {}, &ok);
|
|
if (!ok)
|
|
return;
|
|
QJsonObject msg {
|
|
{ "type", "setValue" },
|
|
{ "key", "ocu" },
|
|
{ "value", url },
|
|
{ "sudo", true }
|
|
};
|
|
RequestDialog{std::move(msg), std::move(devices), this}.exec();
|
|
}
|
|
else if (selected == actionStartUpdate)
|
|
{
|
|
QString branch;
|
|
{
|
|
QInputDialog dialog{this};
|
|
dialog.setWindowTitle(tr("Select update release..."));
|
|
dialog.setInputMode(QInputDialog::TextInput);
|
|
dialog.setLabelText(tr("Update release"));
|
|
dialog.setComboBoxEditable(true);
|
|
dialog.setComboBoxItems({QStringLiteral("__default")});
|
|
if (dialog.exec() != QDialog::Accepted)
|
|
return;
|
|
branch = dialog.textValue();
|
|
}
|
|
QJsonObject msg {
|
|
{ "type", "setValue" },
|
|
{ "key", "oct" },
|
|
{ "value", branch },
|
|
{ "sudo", true }
|
|
};
|
|
RequestDialog{std::move(msg), std::move(devices), this}.exec();
|
|
}
|
|
else if (selected == actionReboot)
|
|
{
|
|
if (const auto result = QMessageBox::question(this, tr("Are you sure?"), tr("Do you really want to reboot selected devices?")); result == QMessageBox::Yes)
|
|
{
|
|
QJsonObject msg {
|
|
{ "type", "setValue" },
|
|
{ "key", "rst" },
|
|
{ "value", 1 },
|
|
{ "sudo", true }
|
|
};
|
|
RequestDialog{std::move(msg), std::move(devices), this}.exec();
|
|
}
|
|
}
|
|
else if (selected == actionSetChargectrlOverride)
|
|
{
|
|
QJsonObject msg {
|
|
{ "type", "setValue" },
|
|
{ "key", "stao" },
|
|
{ "value", QJsonObject {
|
|
{ "car", 2 },
|
|
{ "error", 0 },
|
|
{ "phases", QJsonArray {
|
|
true,
|
|
true,
|
|
true,
|
|
true,
|
|
true,
|
|
true
|
|
} },
|
|
{ "temperature", 30 },
|
|
{ "voltageL1", 230 },
|
|
{ "voltageL2", 230 },
|
|
{ "voltageL3", 230 },
|
|
{ "voltageN", 0 },
|
|
{ "ampereL1", 32 },
|
|
{ "ampereL2", 32 },
|
|
{ "ampereL3", 32 },
|
|
{ "powerL1", 7360 },
|
|
{ "powerL2", 7360 },
|
|
{ "powerL3", 7360 },
|
|
{ "powerN", 0 },
|
|
{ "powerTotal", 22080 },
|
|
{ "powerFactorL1", 90 },
|
|
{ "powerFactorL2", 90 },
|
|
{ "powerFactorL3", 90 },
|
|
{ "powerFactorN", 0 }
|
|
} },
|
|
{ "sudo", true }
|
|
};
|
|
RequestDialog{std::move(msg), std::move(devices), this}.exec();
|
|
}
|
|
else if (selected == actionSetAbitraryApiKey)
|
|
{
|
|
SetArbitraryApiKeyDialog dialog{this};
|
|
if (dialog.exec() == QDialog::Accepted)
|
|
{
|
|
QJsonObject msg {
|
|
{ "type", "setValue" },
|
|
{ "key", dialog.apiKey() },
|
|
{ "value", dialog.value() }
|
|
};
|
|
if (dialog.sudo())
|
|
msg["sudo"] = true;
|
|
RequestDialog{std::move(msg), std::move(devices), this}.exec();
|
|
}
|
|
}
|
|
else if (selected == resetNvsKey)
|
|
{
|
|
bool ok{};
|
|
auto nvsKey = QInputDialog::getText(this, tr("Please input nvs key to be reset"), tr("Nvs key"), QLineEdit::Normal, {}, &ok);
|
|
if (!ok)
|
|
return;
|
|
|
|
QJsonObject msg {
|
|
{ "type", "nvsReset" },
|
|
{ "key", nvsKey },
|
|
{ "sudo", true }
|
|
};
|
|
RequestDialog{std::move(msg), std::move(devices), this}.exec();
|
|
}
|
|
else if (selected == actionOpenApps)
|
|
{
|
|
|
|
}
|
|
else if (selected == actionRemove)
|
|
{
|
|
removeRows(std::move(selectedRows));
|
|
}
|
|
}
|
|
|
|
void MainWindow::removeRows(QModelIndexList &&indexes)
|
|
{
|
|
if (QMessageBox::question(
|
|
this,
|
|
tr("Confirm deletion of devices"),
|
|
tr("Do you really want to remove %0 devices?").arg(indexes.count())
|
|
) != QMessageBox::Yes)
|
|
return;
|
|
|
|
int succeeded{}, failed{};
|
|
|
|
std::sort(indexes.begin(), indexes.end(),
|
|
[](const QModelIndex &a, const QModelIndex &b) {
|
|
return a.row() > b.row();
|
|
});
|
|
|
|
for (const QModelIndex &index : std::as_const(indexes))
|
|
if (m_model->removeRows(index.row(), 1, index.parent()))
|
|
succeeded++;
|
|
else
|
|
{
|
|
failed++;
|
|
qWarning() << "removing row" << index.row() << "failed";
|
|
}
|
|
|
|
if (succeeded > 0)
|
|
m_settings.setSerials(m_model->serials());
|
|
|
|
if (failed > 0)
|
|
QMessageBox::warning(this, tr("Error while removing!"), tr("%0 rows could not be removed!").arg(failed));
|
|
}
|