Initial commit (project based on widgets)

This commit is contained in:
Yury Shuvakin
2022-08-01 21:53:36 +03:00
parent d9396cdc2f
commit 14a7aa699f
411 changed files with 95119 additions and 0 deletions

9
.gitignore vendored Normal file
View File

@@ -0,0 +1,9 @@
*.user
*.o
moc_*.cpp
moc_*.h
qrc_*.cpp
qrc_*.h
ui_*.h
Makefile
ENNOID-BMS-ToolV*

176
ENNOID-BMS-Tool.pro Normal file
View File

@@ -0,0 +1,176 @@
#-------------------------------------------------
#
# Project created by QtCreator 2016-08-12T21:55:19
#
#-------------------------------------------------
INCLUDEPATH += $$PWD
# Version
DT_VERSION = 1.3
DEFINES += DT_VERSION=$$DT_VERSION
!android: {
# Serial port available
DEFINES += HAS_SERIALPORT
}
CONFIG += c++11
QMAKE_CXXFLAGS += -Wno-deprecated-copy
# Options
#Build mobile GUI
#CONFIG += build_mobile
# Bluetooth available
DEFINES += HAS_BLUETOOTH
QT += core gui
QT += widgets
QT += printsupport
QT += network
QT += bluetooth
QT += quick
QT += quickcontrols2
QT += charts
contains(DEFINES, HAS_SERIALPORT) {
QT += serialport
}
android: QT += androidextras
android: TARGET = ENNOID-BMS-Tool
!android: TARGET = ENNOID-BMS-ToolV$$DT_VERSION
TEMPLATE = app
release_win {
DESTDIR = build/win
OBJECTS_DIR = build/win/obj
MOC_DIR = build/win/obj
RCC_DIR = build/win/obj
UI_DIR = build/win/obj
}
release_lin {
# http://micro.nicholaswilson.me.uk/post/31855915892/rules-of-static-linking-libstdc-libc-libgcc
# http://insanecoding.blogspot.se/2012/07/creating-portable-linux-binaries.html
QMAKE_LFLAGS += -static-libstdc++ -static-libgcc
DESTDIR = build/lin
OBJECTS_DIR = build/lin/obj
MOC_DIR = build/lin/obj
RCC_DIR = build/lin/obj
UI_DIR = build/lin/obj
}
release_android {
DESTDIR = build/android
OBJECTS_DIR = build/android/obj
MOC_DIR = build/android/obj
RCC_DIR = build/android/obj
UI_DIR = build/android/obj
}
build_mobile {
DEFINES += USE_MOBILE
}
SOURCES += main.cpp\
akbmonitorpage.cpp \
bmsservicepage.cpp \
bmssettings.cpp \
bmssettingswidget.cpp \
cellsmonitorcheckboxdelegate.cpp \
cellsmonitorlabelvoltagedelegate.cpp \
cellsmonitorpage.cpp \
configparamsgetter.cpp \
configurationpage.cpp \
connectandenterpage.cpp \
customlefttabbarbutton.cpp \
debugprintpage.cpp \
historylineeditbox.cpp \
hoverablecombobox.cpp \
languageselectorbutton.cpp \
mainwindow.cpp \
mainwindownew.cpp \
packet.cpp \
settingsextrapage.cpp \
vbytearray.cpp \
commands.cpp \
configparams.cpp \
configparam.cpp \
parametereditor.cpp \
digitalfiltering.cpp \
bleuart.cpp \
utility.cpp \
bmsinterface.cpp \
visualizationchart.cpp \
visualizationpage.cpp
HEADERS += mainwindow.h \
akbmonitorpage.h \
bmsservicepage.h \
bmssettings.h \
bmssettingswidget.h \
cellsmonitorcheckboxdelegate.h \
cellsmonitorlabelvoltagedelegate.h \
cellsmonitorpage.h \
configparamsgetter.h \
configurationpage.h \
connectandenterpage.h \
customlefttabbarbutton.h \
debugprintpage.h \
historylineedit.h \
historylineeditbox.h \
hoverablecombobox.h \
languageselectorbutton.h \
mainwindownew.h \
packet.h \
settingsextrapage.h \
vbytearray.h \
commands.h \
datatypes.h \
configparams.h \
configparam.h \
parametereditor.h \
digitalfiltering.h \
bleuart.h \
utility.h \
bmsinterface.h \
visualizationchart.h \
visualizationpage.h
FORMS += mainwindow.ui \
akbmonitorpage.ui \
bmsservicepage.ui \
bmssettings.ui \
bmssettingswidget.ui \
cellsmonitorpage.ui \
configurationpage.ui \
connectandenterpage.ui \
debugprintpage.ui \
mainwindownew.ui \
parametereditor.ui \
settingsextrapage.ui \
visualizationpage.ui
include(pages/pages.pri)
include(widgets/widgets.pri)
include(mobile/mobile.pri)
RESOURCES += res.qrc
DISTFILES += \
android/AndroidManifest.xml \
android/gradle/wrapper/gradle-wrapper.jar \
android/gradlew \
android/res/values/libs.xml \
android/build.gradle \
android/gradle/wrapper/gradle-wrapper.properties
ANDROID_PACKAGE_SOURCE_DIR = $$PWD/android
ANDROID_ABIS = x86

20
README.md Normal file
View File

@@ -0,0 +1,20 @@
# ENNOID-BMS Tool (based on DieBieMS-Tool)
The ENNOID-BMS tool can be used to configure the ENNOID-BMS, update the firmware.
![alt text](images/IMAGE01.png)
![alt text](images/IMAGE02.png)
Latest compiled version can be found in the release tab:
[ENNOID-BMS Tool](https://github.com/EnnoidMe/ENNOID-BMS-Tool/releases/tag/V1.1)
# Other parts of this project:
[ENNOID-BMS Hardware](https://github.com/EnnoidMe/ENNOID-BMS)
[ENNOID-BMS Bootloader](https://github.com/EnnoidMe/DieBieMS-Bootloader) Same as DieBieMS bootloader Can be flashed with the ENNOID-BMS Tool in the firmware tab or with an STlink-V2.
[ENNOID-BMS Firmware](https://github.com/EnnoidMe/ENNOID-BMS-Firmware/tree/ENNOID) Can be flashed with the ENNOID-BMS Tool in the firmware tab.
The compiled build .bin and hex files can be found in the MDK-ARM folder [Firmware files](https://github.com/EnnoidMe/ENNOID-BMS-Firmware/blob/master/MDK-ARM/DieBieMS/ENNOID-BMS.bin)

496
akbmonitorpage.cpp Normal file
View File

@@ -0,0 +1,496 @@
#include "akbmonitorpage.h"
#include "ui_akbmonitorpage.h"
#include <QIntValidator>
#include <QDoubleValidator>
#include <QDateTime>
#include <math.h>
#include "configparamsgetter.h"
AkbMonitorPage::AkbMonitorPage(QWidget *parent) :
QFrame(parent),
ui(new Ui::AkbMonitorPage)
{
ui->setupUi(this);
ui->pbLoadDataFromBmsMemory->hide();
// L
ui->lbCellNumberLeft->hide();
ui->lbCellNumberLeftDots->hide();
ui->editCellNumberLeft->hide();
// R
ui->lbCellNumberRight->hide();
ui->lbCellNumberRightDots->hide();
ui->editCellNumberRight->hide();
// 1 // L
ui->editBatteryChargeLevel ->setValidator( new QIntValidator(-1000, 1000, this) ); // mValues.soC
ui->editBatteryVoltage ->setValidator( new QIntValidator(-1000, 1000, this) ); // mValues.packVoltage
ui->editNominalCapacity ->setValidator( new QIntValidator(-1000, 1000, this) ); //, mDieBieMS->bmsConfig(), "batteryCapacity");
ui->editBatteryTemperature ->setValidator( new QDoubleValidator(-100, 100, 2, this) ); // mValues.tempBattHigh
// 1 // R
ui->editModulesNumber ->setValidator( new QIntValidator(-1000, 1000, this) ); //, mDieBieMS->bmsConfig(), "cellMonitorICCount");
ui->editCellsNumber ->setValidator( new QIntValidator(-1000, 1000, this) ); //, mDieBieMS->bmsConfig(), "noOfCellsSeries");
ui->editActualCapacity ->setValidator( new QIntValidator(-1000, 1000, this) ); // NOT IN SETTINGS
ui->editBmsTemperature ->setValidator( new QDoubleValidator(-100, 100, 2, this) ); // mValues.tempBMSHigh
//
// 2 // L ->clear();
ui->editMaxVoltageOnCell ->setValidator( new QDoubleValidator(-100, 100, 2, this) ); // mValues.cVHigh
ui->editCellNumberLeft ->setValidator( new QIntValidator(-1000, 1000, this) ); // NOT IN SETTINGS
ui->editCircuit ->setValidator( new QIntValidator(-1000, 1000, this) ); // mValues.packCurrent
// 2 // R ->clear();
ui->editMinVoltageOnCell ->setValidator( new QDoubleValidator(-100, 100, 2, this) ); // mValues.cVLow
ui->editCellNumberRight ->setValidator( new QIntValidator(-100, 1000, this) ); // NOT IN SETTINGS
mTimer = new QTimer(this);
mTimer->start(1000);
connect(mTimer, &QTimer::timeout,this, &AkbMonitorPage::timerSlot);
clearControlls();
}
AkbMonitorPage::~AkbMonitorPage()
{
delete ui;
}
BMSInterface *AkbMonitorPage::bms() const
{
return mDieBieMS;
}
void AkbMonitorPage::setDieBieMS(BMSInterface *dieBieMS)
{
mDieBieMS = dieBieMS;
if (mDieBieMS) {
connect(mDieBieMS->commands(), &Commands::valuesReceived,this, &AkbMonitorPage::valuesReceived);
//connect(mDieBieMS->commands(), &Commands::cellsReceived,this, &VisualizationPage::cellsReceived);
connect(mDieBieMS->bmsConfig(), &ConfigParams::updated, this, &AkbMonitorPage::onLoadParams);
}
}
void AkbMonitorPage::timerSlot()
{
//
// We will start revieve Data WHEN
// SEE - void MainWindowNew::onTimerSlot()
// one of those left-tab-bar buttons are selected (ui->pbMainMenuBarVisualization->isChecked() || ui->pbMainMenuBarVisualization->isChecked())
//
//////////////////////////////////////////////////////////////////////////////////////
// 1 // L
if(!mSoc.empty())
ui->editBatteryChargeLevel->setText(QString::number(mSoc.last())); // mValues.soC
if(!mPackVoltage.empty()) {
//ui->editBatteryVoltage->setText(QString::number(mPackVoltage.last())); // mValues.packVoltage
ui->editBatteryVoltage->setText(QString::number(mPackVoltage.last(), 'f', 2)); // mValues.packVoltage
}
//addParamRow(ui->editNominalCapacity, mDieBieMS->bmsConfig(), "batteryCapacity");
//////////////////////////////////////////////////////////////////////////////////////
if(!mTempBattHigh.empty())
ui->editBatteryTemperature->setText(QString::number((int)mTempBattHigh.last())); // mValues.tempBattHigh
//ui->editBatteryTemperature->setText(QString::number(mTempBattHigh.last(), 'f', 2)); // mValues.tempBattHigh
// 1 // R
//addParamRow(ui->editModulesNumber, mDieBieMS->bmsConfig(), "cellMonitorICCount");
//addParamRow(ui->editCellsNumber, mDieBieMS->bmsConfig(), "noOfCellsSeries");
//// ui->editActualCapacity // NOT IN SETTINGS // AND NOT IN BMS_VALUES - valuesReceived(BMS_VALUES values)
if(!mTempBMSHigh.empty())
ui->editBmsTemperature->setText(QString::number((int)mTempBMSHigh.last())); // mValues.tempBMSHigh
//ui->editBmsTemperature->setText(QString::number(mTempBMSHigh.last(), 'f', 2)); // mValues.tempBMSHigh
//////////////////////////////////////////////////////////////////////////////////////
// 2 // L
if(!mCellVHigh.empty())
ui->editMaxVoltageOnCell->setText(QString::number(mCellVHigh.last(), 'f', 2)); // mValues.cVHigh
//// ui->editCellNumberLeft // NOT IN SETTINGS // AND NOT IN BMS_VALUES - valuesReceived(BMS_VALUES values)
if(!mPackCurrent.empty())
ui->editCircuit->setText(QString::number(mPackCurrent.last())); // mValues.packCurrent
// 2 // R
if(!mCellVLow.empty())
ui->editMinVoltageOnCell->setText(QString::number(mCellVLow.last(), 'f', 2)); // mValues.cVLow
//// ui->editCellNumberRight // NOT IN SETTINGS // AND NOT IN BMS_VALUES - valuesReceived(BMS_VALUES values)
//////////////////////////////////////////////////////////////////////////////////////
//if (mUpdateValPlot)
//{
/*
int dataSize = mPackVoltage.size();
QVector<double> xAxis(dataSize);
for (int i = 0;i < mSeconds.size();i++) {
xAxis[i] = mSeconds[i];
}
*/
/*
// Current and duty-plot
///int graphIndex = 0;
///ui->ivLCGraph->graph(graphIndex++)->setData(xAxis, mPackVoltage);
///ui->ivLCGraph->graph(graphIndex++)->setData(xAxis, mLCLoadVoltage);
///ui->ivLCGraph->graph(graphIndex++)->setData(xAxis, mLCLoadCurrent);
///graphIndex = 0;
///ui->ivHCGraph->graph(graphIndex++)->setData(xAxis, mHCLoadVoltage);
///ui->ivHCGraph->graph(graphIndex++)->setData(xAxis, mHCLoadCurrent);
///graphIndex = 0;
///ui->cellGraph->graph(graphIndex++)->setData(xAxis, mCellVHigh);
///ui->cellGraph->graph(graphIndex++)->setData(xAxis, mCellVAverage);
///ui->cellGraph->graph(graphIndex++)->setData(xAxis, mCellVLow);
///graphIndex = 0;
///mTempBMSLow.fill(0,1);
///ui->tempGraph->graph(graphIndex++)->setData(xAxis, mTempBMSHigh);
///ui->tempGraph->graph(graphIndex++)->setData(xAxis, mTempBMSAverage);
///ui->tempGraph->graph(graphIndex++)->setData(xAxis, mTempBMSLow);
*/
// V
/*
double valVMax = mHCLoadVoltage.last();
double valVMid = mPackVoltage.last();
double valVMin = mLCLoadVoltage.last();
*/
/*// increas X
chartV->appendNextX();
// then set all new Ys
chartV->appendYToSeries(VoltageMax, valVMax);
chartV->appendYToSeries(VoltageMid, valVMid);
chartV->appendYToSeries(VoltageMin, valVMin);
ui->lbVMaxLegendVal->setText(QString("%1").arg(valVMax));
ui->lbVMidLegendVal->setText(QString("%1").arg(valVMid));
ui->lbVMinLegendVal->setText(QString("%1").arg(valVMin));
chartV->update();
ui->chartViewVoltage->repaint();
*/
// A
/*
double valAMax = mHCLoadCurrent.last();
double valAMid = mMCLoadCurrent_.last();
double valAMin = mLCLoadCurrent.last();
*/
/*// increas X
chartA->appendNextX();
// then set all new Ys
chartA->appendYToSeries(AmperageMax, valAMax);
chartA->appendYToSeries(AmperageMid, valAMid);
chartA->appendYToSeries(AmperageMin, valAMin);
ui->lbAMaxLegendVal->setText(QString("%1").arg(valAMax));
ui->lbAMidLegendVal->setText(QString("%1").arg(valAMid));
ui->lbAMinLegendVal->setText(QString("%1").arg(valAMin));
chartA->update();
ui->chartViewAmperage->repaint();
*/
// T
/*
double valTMax = mTempBattHigh.last();
double valTMid = mTempBattAverage.last();
double valTMin = mTempBattLow_.last();
*/
/*mTempBattLow_.fill(0,1);
// increas X
chartT->appendNextX();
// then set all new Ys
chartT->appendYToSeries(TemperatureMax, valTMax);
chartT->appendYToSeries(TemperatureMid, valTMid);
chartT->appendYToSeries(TemperatureMin, valTMin);
ui->lbTMaxLegendVal->setText(QString("%1").arg(valTMax));
ui->lbTMidLegendVal->setText(QString("%1").arg(valTMid));
ui->lbTMinLegendVal->setText(QString("%1").arg(valTMin));
chartT->update();
ui->chartViewTemperature->repaint();
*/
// C
/*
double valCMax = mCellVHigh.last();
double valCMid = mCellVAverage.last();
double valCMin = mCellVLow.last();
*/
/*// increas X
chartC->appendNextX();
// then set all new Ys
chartC->appendYToSeries(CellsMax, valCMax);
chartC->appendYToSeries(CellsMid, valCMid);
chartC->appendYToSeries(CellsMin, valCMin);
ui->lbCMaxLegendVal->setText(QString("%1").arg(valCMax));
ui->lbCMidLegendVal->setText(QString("%1").arg(valCMid));
ui->lbCMinLegendVal->setText(QString("%1").arg(valCMin));
chartC->update();
ui->chartViewCells->repaint();
*/
/*
ui->tempGraph->graph(graphIndex++)->setData(xAxis, mTempBattHigh);
ui->tempGraph->graph(graphIndex++)->setData(xAxis, mTempBattAverage);
ui->tempGraph->graph(graphIndex++)->setData(xAxis, mTempBattLow);
///if (ui->autoscaleButton->isChecked())
{
///ui->ivLCGraph->rescaleAxes();
///ui->ivHCGraph->rescaleAxes();
///ui->cellGraph->rescaleAxes();
ui->tempGraph->rescaleAxes();
}
///ui->ivLCGraph->replot();
///ui->ivHCGraph->replot();
///ui->cellGraph->replot();
ui->tempGraph->replot();
///ui->cellBarGraph->replot();
mUpdateValPlot = false;
*/
//}
}
void AkbMonitorPage::valuesReceived(BMS_VALUES values)
{
//ui->rtText->setValues(values);
const int maxS = 500;
appendDoubleAndTrunc(&mSoc, values.soC, maxS);
appendDoubleAndTrunc(&mPackCurrent, values.packCurrent, maxS);
appendDoubleAndTrunc(&mPackVoltage, values.packVoltage, maxS);
appendDoubleAndTrunc(&mLCLoadVoltage, values.loadLCVoltage, maxS);
appendDoubleAndTrunc(&mLCLoadCurrent, values.loadLCCurrent, maxS);
appendDoubleAndTrunc(&mHCLoadVoltage, values.loadHCVoltage, maxS);
appendDoubleAndTrunc(&mHCLoadCurrent, values.loadHCCurrent, maxS);
appendDoubleAndTrunc(&mAuxVoltage, values.auxVoltage, maxS);
appendDoubleAndTrunc(&mAuxCurrent, values.auxCurrent, maxS);
appendDoubleAndTrunc(&mCellVHigh, values.cVHigh, maxS);
appendDoubleAndTrunc(&mCellVAverage, values.cVAverage, maxS);
appendDoubleAndTrunc(&mCellVLow, values.cVLow, maxS);
appendDoubleAndTrunc(&mTempBMSHigh, values.tempBMSHigh, maxS);
appendDoubleAndTrunc(&mTempBMSAverage, values.tempBMSAverage, maxS);
//appendDoubleAndTrunc(&mTempBMSLow_, values.tempBMSLow_, maxS);
appendDoubleAndTrunc(&mTempBattHigh, values.tempBattHigh, maxS);
appendDoubleAndTrunc(&mTempBattAverage, values.tempBattAverage, maxS);
//appendDoubleAndTrunc(&mTempBattLow_, values.tempBattLow_, maxS);
qint64 tNow = QDateTime::currentMSecsSinceEpoch();
double elapsed = (double)(tNow - mLastUpdateTime) / 1000.0;
if (elapsed > 1.0) {
elapsed = 1.0;
}
mSecondCounter += elapsed;
appendDoubleAndTrunc(&mSeconds, mSecondCounter, maxS);
mLastUpdateTime = tNow;
mUpdateValPlot = true;
}
/*
void AkbMonitorPage::cellsReceived(int cellCount, QVector<double> cellVoltageArray)
{
QVector<double> dataxNew;
dataxNew.clear();
QVector<double> datayNormal;
datayNormal.clear();
QVector<double> datayBalance;
datayBalance.clear();
QVector<QString> labels;
int indexPointer;
double cellHardUnder = mDieBieMS->bmsConfig()->getParamDouble("cellHardUnderVoltage");
double cellHardOver = mDieBieMS->bmsConfig()->getParamDouble("cellHardOverVoltage");
for(indexPointer = 0; indexPointer < cellCount; indexPointer++){
dataxNew.append(indexPointer + 1);
if(cellVoltageArray[indexPointer] < 0.0){
datayNormal.append(0.0);
datayBalance.append(fabs(cellVoltageArray[indexPointer]));
}else{
datayNormal.append(fabs(cellVoltageArray[indexPointer]));
datayBalance.append(0.0);
}
QString voltageString = QStringLiteral("%1V (C").arg(fabs(cellVoltageArray[indexPointer]), 0, 'f',3);
labels.append(voltageString + QString::number(indexPointer) + ")");
}
//QSharedPointer<QCPAxisTickerText> textTicker(new QCPAxisTickerText);
//textTicker->addTicks(dataxNew, labels);
//ui->cellBarGraph->xAxis->setTicker(textTicker);
//ui->cellBarGraph->xAxis->setRange(0.5, indexPointer + 0.5);
//ui->cellBarGraph->yAxis->setRange(cellHardUnder, cellHardOver);
//barsNormal->setData(dataxNew, datayNormal);
//barsBalance->setData(dataxNew, datayBalance);
}
*/
void AkbMonitorPage::appendDoubleAndTrunc(QVector<double> *vec, double num, int maxSize)
{
vec->append(num);
if(vec->size() > maxSize) {
vec->remove(0, vec->size() - maxSize);
}
}
void AkbMonitorPage::clearControlls()
{
// 1 // L
ui->editBatteryChargeLevel ->clear(); // mValues.soC
ui->editBatteryVoltage ->clear(); // mValues.packVoltage
ui->editNominalCapacity ->clear(); //, mDieBieMS->bmsConfig(), "batteryCapacity");
ui->editBatteryTemperature ->clear(); // mValues.tempBattHigh
// 1 // R ->clear();
ui->editModulesNumber ->clear(); //, mDieBieMS->bmsConfig(), "cellMonitorICCount");
ui->editCellsNumber ->clear(); //, mDieBieMS->bmsConfig(), "noOfCellsSeries");
ui->editActualCapacity ->clear(); // NOT IN SETTINGS
ui->editBmsTemperature ->clear(); // mValues.tempBMSHigh
// ->clear();
// 2 // L ->clear();
ui->editMaxVoltageOnCell ->clear(); // mValues.cVHigh
ui->editCellNumberLeft ->clear(); // NOT IN SETTINGS
ui->editCircuit ->clear(); // mValues.packCurrent
// 2 // R ->clear();
ui->editMinVoltageOnCell ->clear(); // mValues.cVLow
ui->editCellNumberRight ->clear(); // NOT IN SETTINGS
}
void AkbMonitorPage::onLoadParams()
{
///QHash<QString, ConfigParam> mParams;
//addParamRow(ui->editPointZeroDetectorValue, mDieBieMS->bmsConfig(), "shuntLCFactor");
//ui->specificationsTab->addRowSeparator(tr("Pack configuration"));
// Серийный номер * //ui->editSerialNumber
getSerialNumber(ui->editSerialNumber, mDieBieMS->bmsConfig(), "notUsedCurrentThreshold");
// 1 // L
// ui->editBatteryChargeLevel // mValues.soC
// ui->editBatteryVoltage // mValues.packVoltage
addParamRow(ui->editNominalCapacity, mDieBieMS->bmsConfig(), "batteryCapacity");
// ui->editBatteryTemperature // mValues.tempBattHigh
// 1 // R
addParamRow(ui->editModulesNumber, mDieBieMS->bmsConfig(), "cellMonitorICCount");
addParamRow(ui->editCellsNumber, mDieBieMS->bmsConfig(), "noOfCellsSeries");
//// ui->editActualCapacity // NOT IN BMS_CONFIG
// ui->editBmsTemperature // mValues.tempBMSHigh
// 2 // L
// ui->editMaxVoltageOnCell // mValues.cVHigh
//// ui->editCellNumberLeft // NOT IN BMS_CONFIG
// ui->editCircuit // mValues.packCurrent
// 2 // R
// ui->editMinVoltageOnCell // mValues.cVLow
//// ui->editCellNumberRight // NOT IN BMS_CONFIG
}
/*
bool AkbMonitorPage::addParamRow(QLineEdit* edit, ConfigParams *params, QString paramName)
{
bool res = false;
QWidget *editor = params->getEditor(paramName);
QString name = params->getLongName(paramName);
if (editor) {
//int row = rowCount();
//setRowCount(row + 1);
//QTableWidgetItem *item = new QTableWidgetItem(name);
//item->setFlags(item->flags() & ~Qt::ItemIsEditable);
//setItem(row, 0, item);
//setCellWidget(row, 1, editor);
//edit->setText(editor->);
auto vtype = editor->property("type");
auto type = vtype.toInt();
//CFG_T_UNDEFINED = 0,
if(type == 1) // CFG_T_DOUBLE,
{
auto vval = editor->property("value");
auto val = vval.toDouble();
edit->setText(QString::number(val));
}
else if(type == 2) // CFG_T_INT,
{
auto vval = editor->property("value");
auto val = vval.toInt();
edit->setText(QString::number(val));
}
else if(type == 3) // CFG_T_QSTRING,
{
auto vval = editor->property("value");
auto val = vval.toString();
edit->setText(val);
}
else if(type == 4) // CFG_T_ENUM,
{
auto vval = editor->property("value");
auto val = vval.toInt();
edit->setText(QString::number(val));
}
else if(type == 5) // CFG_T_BOOL
{
auto vval = editor->property("value");
auto val = vval.toBool();
edit->setText(val ? "true" : "false");
}
auto vsuffix = editor->property("suffix");
auto suffix = vsuffix.toString();
auto veditorDecimalsDouble = editor->property("editorDecimalsDouble");
auto editorDecimalsDouble = veditorDecimalsDouble.toDouble();
auto veditAsPercentage = editor->property("editAsPercentage");
auto editAsPercentage = veditAsPercentage.toBool();
res = true;
//resizeColumnToContents(0);
//resizeRowsToContents();
}
return res;
}
//*/

90
akbmonitorpage.h Normal file
View File

@@ -0,0 +1,90 @@
#ifndef AKBMONITORPAGE_H
#define AKBMONITORPAGE_H
#include <QWidget>
#include <QLineEdit>
#include <QTimer>
#include "bmsinterface.h"
namespace Ui {
class AkbMonitorPage;
}
class AkbMonitorPage : public QFrame
{
Q_OBJECT
public:
explicit AkbMonitorPage(QWidget *parent = nullptr);
~AkbMonitorPage();
void clearControlls();
BMSInterface *bms() const;
void setDieBieMS(BMSInterface *dieBieMS);
public slots:
void onLoadParams();
private slots:
void timerSlot();
void valuesReceived(BMS_VALUES values);
// void cellsReceived(int cellCount, QVector<double> cellVoltageArray);
private:
Ui::AkbMonitorPage *ui;
BMSInterface *mDieBieMS;
// bool addParamRow(QLineEdit* edit, ConfigParams *params, QString paramName);
//
QVector<double> mSoc;
QVector<double> mPackVoltage;
QVector<double> mPackCurrent;
QVector<double> mLCLoadVoltage;
QVector<double> mLCLoadCurrent;
QVector<double> mHCLoadVoltage;
QVector<double> mHCLoadCurrent;
QVector<double> mChargerVoltage;
QVector<double> mAuxVoltage;
QVector<double> mAuxCurrent;
QVector<double> mCellVHigh;
QVector<double> mCellVAverage;
QVector<double> mCellVLow;
QVector<double> mTempBMSHigh;
QVector<double> mTempBMSAverage;
QVector<double> mTempBMSLow;
QVector<double> mTempBattHigh;
QVector<double> mTempBattAverage;
QVector<double> mTempBattLow;
QVector<double> mHumidity;
QVector<double> mSeconds;
double mSecondCounter;
qint64 mLastUpdateTime;
bool mUpdateValPlot;
QTimer* mTimer;
void initForPlot();
void initForCharts();
void appendDoubleAndTrunc(QVector<double> *vec, double num, int maxSize);
};
#endif // AKBMONITORPAGE_H

1520
akbmonitorpage.ui Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,74 @@
<?xml version="1.0"?>
<manifest package="ennoid.bms" xmlns:android="http://schemas.android.com/apk/res/android" android:versionName="1" android:versionCode="1" android:installLocation="auto">
<application android:hardwareAccelerated="true" android:name="org.qtproject.qt5.android.bindings.QtApplication" android:label="ENNOID" android:icon="@drawable/icon">
<activity android:configChanges="orientation|uiMode|screenLayout|screenSize|smallestScreenSize|layoutDirection|locale|fontScale|keyboard|keyboardHidden|navigation" android:name="org.qtproject.qt5.android.bindings.QtActivity" android:label="ENNOID BMS Tool" android:screenOrientation="unspecified" android:launchMode="singleTop">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
<!-- Application arguments -->
<!-- meta-data android:name="android.app.arguments" android:value="arg1 arg2 arg3"/ -->
<!-- Application arguments -->
<meta-data android:name="android.app.lib_name" android:value="ENNOID-BMS-Tool"/>
<meta-data android:name="android.app.qt_sources_resource_id" android:resource="@array/qt_sources"/>
<meta-data android:name="android.app.repository" android:value="default"/>
<meta-data android:name="android.app.qt_libs_resource_id" android:resource="@array/qt_libs"/>
<meta-data android:name="android.app.bundled_libs_resource_id" android:resource="@array/bundled_libs"/>
<!-- Deploy Qt libs as part of package -->
<meta-data android:name="android.app.bundle_local_qt_libs" android:value="-- %%BUNDLE_LOCAL_QT_LIBS%% --"/>
<meta-data android:name="android.app.bundled_in_lib_resource_id" android:resource="@array/bundled_in_lib"/>
<meta-data android:name="android.app.bundled_in_assets_resource_id" android:resource="@array/bundled_in_assets"/>
<!-- Run with local libs -->
<meta-data android:name="android.app.use_local_qt_libs" android:value="-- %%USE_LOCAL_QT_LIBS%% --"/>
<meta-data android:name="android.app.libs_prefix" android:value="/data/local/tmp/qt/"/>
<meta-data android:name="android.app.load_local_libs" android:value="-- %%INSERT_LOCAL_LIBS%% --"/>
<meta-data android:name="android.app.load_local_jars" android:value="-- %%INSERT_LOCAL_JARS%% --"/>
<meta-data android:name="android.app.static_init_classes" android:value="-- %%INSERT_INIT_CLASSES%% --"/>
<!-- Messages maps -->
<meta-data android:value="@string/ministro_not_found_msg" android:name="android.app.ministro_not_found_msg"/>
<meta-data android:value="@string/ministro_needed_msg" android:name="android.app.ministro_needed_msg"/>
<meta-data android:value="@string/fatal_error_msg" android:name="android.app.fatal_error_msg"/>
<!-- Messages maps -->
<!-- Splash screen -->
<!-- meta-data android:name="android.app.splash_screen_drawable" android:resource="@drawable/logo"/ -->
<!-- meta-data android:name="android.app.splash_screen_sticky" android:value="true"/ -->
<!-- Splash screen -->
<!-- Background running -->
<!-- Warning: changing this value to true may cause unexpected crashes if the
application still try to draw after
"applicationStateChanged(Qt::ApplicationSuspended)"
signal is sent! -->
<meta-data android:name="android.app.background_running" android:value="false"/>
<!-- Background running -->
<!-- auto screen scale factor -->
<meta-data android:name="android.app.auto_screen_scale_factor" android:value="false"/>
<!-- auto screen scale factor -->
<!-- extract android style -->
<!-- available android:values :
* full - useful QWidget & Quick Controls 1 apps
* minimal - useful for Quick Controls 2 apps, it is much faster than "full"
* none - useful for apps that don't use any of the above Qt modules
-->
<meta-data android:name="android.app.extract_android_style" android:value="full"/>
<!-- extract android style -->
</activity>
<!-- For adding service(s) please check: https://wiki.qt.io/AndroidServices -->
</application>
<uses-sdk android:minSdkVersion="23" android:targetSdkVersion="29"/>
<supports-screens android:largeScreens="true" android:normalScreens="true" android:anyDensity="true" android:smallScreens="true"/>
<!-- The following comment will be replaced upon deployment with default permissions based on the dependencies of the application.
Remove the comment if you do not require these default permissions. -->
<!-- %%INSERT_PERMISSIONS -->
<!-- The following comment will be replaced upon deployment with default features based on the dependencies of the application.
Remove the comment if you do not require these default features. -->
<!-- %%INSERT_FEATURES -->
<uses-permission android:name="android.permission.BLUETOOTH"/>
<uses-permission android:name="android.permission.SET_ORIENTATION"/>
<uses-permission android:name="android.permission.VIBRATE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
</manifest>

55
android/build.gradle Normal file
View File

@@ -0,0 +1,55 @@
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:2.2.3'
}
}
allprojects {
repositories {
jcenter()
}
}
apply plugin: 'com.android.application'
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
}
android {
/*******************************************************
* The following variables:
* - androidBuildToolsVersion,
* - androidCompileSdkVersion
* - qt5AndroidDir - holds the path to qt android files
* needed to build any Qt application
* on Android.
*
* are defined in gradle.properties file. This file is
* updated by QtCreator and androiddeployqt tools.
* Changing them manually might break the compilation!
*******************************************************/
compileSdkVersion androidCompileSdkVersion.toInteger()
buildToolsVersion androidBuildToolsVersion
sourceSets {
main {
manifest.srcFile 'AndroidManifest.xml'
java.srcDirs = [qt5AndroidDir + '/src', 'src', 'java']
aidl.srcDirs = [qt5AndroidDir + '/src', 'src', 'aidl']
res.srcDirs = [qt5AndroidDir + '/res', 'res']
resources.srcDirs = ['src']
renderscript.srcDirs = ['src']
assets.srcDirs = ['assets']
jniLibs.srcDirs = ['libs']
}
}
lintOptions {
abortOnError false
}
}

Binary file not shown.

View File

@@ -0,0 +1,6 @@
#Mon Feb 20 10:43:22 EST 2017
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-3.4-bin.zip

172
android/gradlew vendored Normal file
View File

@@ -0,0 +1,172 @@
#!/usr/bin/env sh
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS="-Xmx1024m -Dfile.encoding=UTF-8"
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn ( ) {
echo "$*"
}
die ( ) {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Escape application args
save ( ) {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=$(save "$@")
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
cd "$(dirname "$0")"
fi
exec "$JAVACMD" "$@"

Binary file not shown.

After

Width:  |  Height:  |  Size: 546 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 363 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 421 B

View File

@@ -0,0 +1,25 @@
<?xml version='1.0' encoding='utf-8'?>
<resources>
<array name="qt_sources">
<item>https://download.qt.io/ministro/android/qt5/qt-5.9</item>
</array>
<!-- The following is handled automatically by the deployment tool. It should
not be edited manually. -->
<array name="bundled_libs">
<!-- %%INSERT_EXTRA_LIBS%% -->
</array>
<array name="qt_libs">
<!-- %%INSERT_QT_LIBS%% -->
</array>
<array name="bundled_in_lib">
<!-- %%INSERT_BUNDLED_IN_LIB%% -->
</array>
<array name="bundled_in_assets">
<!-- %%INSERT_BUNDLED_IN_ASSETS%% -->
</array>
</resources>

301
bleuart.cpp Normal file
View File

@@ -0,0 +1,301 @@
/*
Original copyright 2018 Benjamin Vedder benjamin@vedder.se and the VESC Tool project ( https://github.com/vedderb/vesc_tool )
Forked to:
Copyright 2018 Danny Bokma github@diebie.nl (https://github.com/DieBieEngineering/DieBieMS-Tool)
Now forked to:
Copyright 2019 - 2020 Kevin Dionne kevin.dionne@ennoid.me (https://github.com/EnnoidMe/ENNOID-BMS-Tool)
This file is part of ENNOID-BMS Tool.
ENNOID-BMS Tool is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
ENNOID-BMS Tool is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "bleuart.h"
#include "utility.h"
#include <QDebug>
#include <QLowEnergyConnectionParameters>
BleUart::BleUart(QObject *parent) : QObject(parent)
{
mControl = 0;
mService = 0;
mUartServiceFound = false;
mConnectDone = false;
mServiceUuid = "6e400001-b5a3-f393-e0a9-e50e24dcca9e";
mRxUuid = "6e400002-b5a3-f393-e0a9-e50e24dcca9e";
mTxUuid = "6e400003-b5a3-f393-e0a9-e50e24dcca9e";
mDeviceDiscoveryAgent = new QBluetoothDeviceDiscoveryAgent(this);
connect(mDeviceDiscoveryAgent, SIGNAL(deviceDiscovered(const QBluetoothDeviceInfo&)),
this, SLOT(addDevice(const QBluetoothDeviceInfo&)));
connect(mDeviceDiscoveryAgent, SIGNAL(error(QBluetoothDeviceDiscoveryAgent::Error)),
this, SLOT(deviceScanError(QBluetoothDeviceDiscoveryAgent::Error)));
connect(mDeviceDiscoveryAgent, SIGNAL(finished()), this, SLOT(scanFinished()));
}
void BleUart::startScan()
{
mDevs.clear();
mDeviceDiscoveryAgent->start();
}
void BleUart::startConnect(QString addr)
{
disconnectBle();
mUartServiceFound = false;
mConnectDone = false;
#if defined(Q_OS_MACOS) || defined(Q_OS_IOS)
// Create BT Controller from unique device UUID stored as addr. Creating
// a controller using a devices address is not supported on macOS or iOS.
QBluetoothDeviceInfo deviceInfo = QBluetoothDeviceInfo();
deviceInfo.setDeviceUuid(QBluetoothUuid(addr));
mControl = new QLowEnergyController(deviceInfo);
#else
mControl = new QLowEnergyController(QBluetoothAddress(addr));
mControl->setRemoteAddressType(QLowEnergyController::RandomAddress);
#endif
connect(mControl, SIGNAL(serviceDiscovered(QBluetoothUuid)),
this, SLOT(serviceDiscovered(QBluetoothUuid)));
connect(mControl, SIGNAL(discoveryFinished()),
this, SLOT(serviceScanDone()));
connect(mControl, SIGNAL(error(QLowEnergyController::Error)),
this, SLOT(controllerError(QLowEnergyController::Error)));
connect(mControl, SIGNAL(connected()),
this, SLOT(deviceConnected()));
connect(mControl, SIGNAL(disconnected()),
this, SLOT(deviceDisconnected()));
connect(mControl, SIGNAL(stateChanged(QLowEnergyController::ControllerState)),
this, SLOT(controlStateChanged(QLowEnergyController::ControllerState)));
connect(mControl, SIGNAL(connectionUpdated(QLowEnergyConnectionParameters)),
this, SLOT(connectionUpdated(QLowEnergyConnectionParameters)));
mControl->connectToDevice();
}
void BleUart::disconnectBle()
{
if (mService) {
delete mService;
mService = 0;
}
if (mControl) {
mControl->disconnectFromDevice();
delete mControl;
mControl = 0;
}
}
bool BleUart::isConnected()
{
return mControl != 0 && mConnectDone;
}
bool BleUart::isConnecting()
{
return mControl && !mConnectDone;
}
void BleUart::writeData(QByteArray data)
{
if (isConnected()) {
const QLowEnergyCharacteristic rxChar = mService->characteristic(QBluetoothUuid(QUuid(mRxUuid)));
if (rxChar.isValid()) {
while(data.size() > 20) {
mService->writeCharacteristic(rxChar, data.mid(0, 20),
QLowEnergyService::WriteWithoutResponse);
data.remove(0, 20);
}
mService->writeCharacteristic(rxChar, data, QLowEnergyService::WriteWithoutResponse);
}
}
}
void BleUart::addDevice(const QBluetoothDeviceInfo &dev)
{
if (dev.coreConfigurations() & QBluetoothDeviceInfo::LowEnergyCoreConfiguration) {
qDebug() << "BLE scan found device:" << dev.name();
#if defined(Q_OS_MACOS) || defined(Q_OS_IOS)
// macOS and iOS do not expose the hardware address of BLTE devices, must use
// the OS generated UUID.
mDevs.insert(dev.deviceUuid().toString(), dev.name());
#else
mDevs.insert(dev.address().toString(), dev.name());
#endif
emit scanDone(mDevs, false);
}
}
void BleUart::scanFinished()
{
qDebug() << "BLE scan finished";
emit scanDone(mDevs, true);
}
void BleUart::deviceScanError(QBluetoothDeviceDiscoveryAgent::Error e)
{
qWarning() << "BLE Scan error:" << e;
mDevs.clear();
emit scanDone(mDevs, true);
//emit bleError(tr("BLE Scan error: ") + Utility::QEnumToQString(e));
}
void BleUart::serviceDiscovered(const QBluetoothUuid &gatt)
{
if (gatt==QBluetoothUuid(QUuid(mServiceUuid))){
qDebug() << "BLE UART service found!";
mUartServiceFound = true;
}
}
void BleUart::serviceScanDone()
{
if (mService) {
delete mService;
mService = 0;
}
if (mUartServiceFound) {
qDebug() << "Connecting to BLE UART service";
mService = mControl->createServiceObject(QBluetoothUuid(QUuid(mServiceUuid)), this);
connect(mService, SIGNAL(stateChanged(QLowEnergyService::ServiceState)),
this, SLOT(serviceStateChanged(QLowEnergyService::ServiceState)));
connect(mService, SIGNAL(characteristicChanged(QLowEnergyCharacteristic,QByteArray)),
this, SLOT(updateData(QLowEnergyCharacteristic,QByteArray)));
connect(mService, SIGNAL(descriptorWritten(QLowEnergyDescriptor,QByteArray)),
this, SLOT(confirmedDescriptorWrite(QLowEnergyDescriptor,QByteArray)));
mService->discoverDetails();
} else {
qWarning() << "BLE UART service not found";
disconnectBle();
}
}
void BleUart::controllerError(QLowEnergyController::Error e)
{
qWarning() << "BLE error:" << e;
//disconnectBle();
//emit bleError(tr("BLE error: ") + Utility::QEnumToQString(e));
}
void BleUart::deviceConnected()
{
qDebug() << "BLE device connected";
mControl->discoverServices();
}
void BleUart::deviceDisconnected()
{
qDebug() << "BLE service disconnected";
disconnectBle();
}
void BleUart::serviceStateChanged(QLowEnergyService::ServiceState s)
{
// A descriptor can only be written if the service is in the ServiceDiscovered state
switch (s) {
case QLowEnergyService::ServiceDiscovered: {
//looking for the TX characteristic
const QLowEnergyCharacteristic txChar = mService->characteristic(
QBluetoothUuid(QUuid(mTxUuid)));
if (!txChar.isValid()){
qDebug() << "BLE Tx characteristic not found";
break;
}
//looking for the RX characteristic
const QLowEnergyCharacteristic rxChar = mService->characteristic(QBluetoothUuid(QUuid(mRxUuid)));
if (!rxChar.isValid()) {
qDebug() << "BLE Rx characteristic not found";
break;
}
// Bluetooth LE spec Where a characteristic can be notified, a Client Characteristic Configuration descriptor
// shall be included in that characteristic as required by the Bluetooth Core Specification
// Tx notify is enabled
mNotificationDescTx = txChar.descriptor(QBluetoothUuid::ClientCharacteristicConfiguration);
if (mNotificationDescTx.isValid()) {
// enable notification
mService->writeDescriptor(mNotificationDescTx, QByteArray::fromHex("0100"));
}
break;
}
default:
break;
}
}
void BleUart::updateData(const QLowEnergyCharacteristic &c, const QByteArray &value)
{
if (c.uuid() == QBluetoothUuid(QUuid(mTxUuid))) {
emit dataRx(value);
}
}
void BleUart::confirmedDescriptorWrite(const QLowEnergyDescriptor &d, const QByteArray &value)
{
if (d.isValid() && d == mNotificationDescTx && value == QByteArray("0000")) {
//disabled notifications -> assume disconnect intent
disconnectBle();
} else {
mConnectDone = true;
// emit connected();
}
}
void BleUart::controlStateChanged(QLowEnergyController::ControllerState state)
{
(void)state;
// qDebug() << state;
// if (state == QLowEnergyController::ConnectedState) {
// QLowEnergyConnectionParameters param;
// param.setIntervalRange(7.5, 7.5);
// param.setSupervisionTimeout(10000);
// param.setLatency(0);
// mControl->requestConnectionUpdate(param);
// }
}
void BleUart::connectionUpdated(const QLowEnergyConnectionParameters &newParameters)
{
(void)newParameters;
qDebug() << "BLE connection parameters updated";
}

89
bleuart.h Normal file
View File

@@ -0,0 +1,89 @@
/*
Original copyright 2018 Benjamin Vedder benjamin@vedder.se and the VESC Tool project ( https://github.com/vedderb/vesc_tool )
Forked to:
Copyright 2018 Danny Bokma github@diebie.nl (https://github.com/DieBieEngineering/DieBieMS-Tool)
Now forked to:
Copyright 2019 - 2020 Kevin Dionne kevin.dionne@ennoid.me (https://github.com/EnnoidMe/ENNOID-BMS-Tool)
This file is part of ENNOID-BMS Tool.
ENNOID-BMS Tool is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
ENNOID-BMS Tool is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef BLEUART_H
#define BLEUART_H
#include <QObject>
#include <QBluetoothDeviceDiscoveryAgent>
#include <QBluetoothDeviceInfo>
#include <QLowEnergyController>
#include <QLowEnergyService>
#include <QVariantMap>
class BleUart : public QObject
{
Q_OBJECT
public:
explicit BleUart(QObject *parent = 0);
Q_INVOKABLE void startScan();
Q_INVOKABLE void startConnect(QString addr);
Q_INVOKABLE void disconnectBle();
Q_INVOKABLE bool isConnected();
Q_INVOKABLE bool isConnecting();
signals:
void dataRx(QByteArray data);
void scanDone(QVariantMap devs, bool done);
void bleError(QString info);
void connected();
public slots:
void writeData(QByteArray data);
private slots:
void addDevice(const QBluetoothDeviceInfo&dev);
void scanFinished();
void deviceScanError(QBluetoothDeviceDiscoveryAgent::Error e);
void serviceDiscovered(const QBluetoothUuid &);
void serviceScanDone();
void controllerError(QLowEnergyController::Error);
void deviceConnected();
void deviceDisconnected();
void serviceStateChanged(QLowEnergyService::ServiceState s);
void updateData(const QLowEnergyCharacteristic &c, const QByteArray &value);
void confirmedDescriptorWrite(const QLowEnergyDescriptor &d, const QByteArray &value);
void controlStateChanged(QLowEnergyController::ControllerState state);
void connectionUpdated(const QLowEnergyConnectionParameters &newParameters);
private:
QBluetoothDeviceDiscoveryAgent *mDeviceDiscoveryAgent;
QLowEnergyController *mControl;
QLowEnergyService *mService;
QLowEnergyDescriptor mNotificationDescTx;
QVariantMap mDevs;
bool mUartServiceFound;
bool mConnectDone;
QString mServiceUuid;
QString mRxUuid;
QString mTxUuid;
};
#endif // BLEUART_H

894
bmsinterface.cpp Normal file
View File

@@ -0,0 +1,894 @@
/**
Original copyright 2018 Benjamin Vedder benjamin@vedder.se and the VESC Tool project ( https://github.com/vedderb/vesc_tool )
Forked to:
Copyright 2018 Danny Bokma github@diebie.nl (https://github.com/DieBieEngineering/DieBieMS-Tool)
Now forked to:
Copyright 2019 - 2020 Kevin Dionne kevin.dionne@ennoid.me (https://github.com/EnnoidMe/ENNOID-BMS-Tool)
This file is part of ENNOID-BMS Tool.
ENNOID-BMS Tool is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
ENNOID-BMS Tool is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "bmsinterface.h"
#include <QDebug>
#include <QHostInfo>
#include <QFileInfo>
#include <QThread>
#include <QEventLoop>
#include <utility.h>
#include <cmath>
#include <QRegularExpression>
#ifdef HAS_SERIALPORT
#include <QSerialPortInfo>
#endif
BMSInterface::BMSInterface(QObject *parent) : QObject(parent)
{
mbmsConfig = new ConfigParams(this);
mInfoConfig = new ConfigParams(this);
mPacket = new Packet(this);
mCommands = new Commands(this);
// Compatible firmwares
mFwVersionReceived = false;
mFwRetries = 0;
mFwPollCnt = 0;
mFwTxt = "x.x";
mIsUploadingFw = false;
mTimer = new QTimer(this);
mTimer->setInterval(20);
mTimer->start();
mLastConnType = static_cast<conn_t>(QSettings().value("connection_type", CONN_NONE).toInt());
mLastTcpServer = mSettings.value("tcp_server", "127.0.0.1").toString();
mLastTcpPort = mSettings.value("tcp_port", 65102).toInt();
mSendCanBefore = false;
mCanIdBefore = 0;
mWasConnected = false;
mAutoconnectOngoing = false;
mAutoconnectProgress = 0.0;
// Serial
#ifdef HAS_SERIALPORT
mSerialPort = new QSerialPort(this);
mLastSerialPort = "";
mLastSerialBaud = 0;
connect(mSerialPort, SIGNAL(readyRead()),this, SLOT(serialDataAvailable()));
connect(mSerialPort, SIGNAL(error(QSerialPort::SerialPortError)),this, SLOT(serialPortError(QSerialPort::SerialPortError)));
#endif
// TCP
mTcpSocket = new QTcpSocket(this);
mTcpConnected = false;
mLastTcpServer = QSettings().value("tcp_server").toString();
mLastTcpPort = QSettings().value("tcp_port").toInt();
connect(mTcpSocket, SIGNAL(readyRead()), this, SLOT(tcpInputDataAvailable()));
connect(mTcpSocket, SIGNAL(connected()), this, SLOT(tcpInputConnected()));
connect(mTcpSocket, SIGNAL(disconnected()),this, SLOT(tcpInputDisconnected()));
connect(mTcpSocket, SIGNAL(error(QAbstractSocket::SocketError)),this, SLOT(tcpInputError(QAbstractSocket::SocketError)));
// BLE
mBleUart = new BleUart(this);
mLastBleAddr = QSettings().value("ble_addr").toString();
int size = mSettings.beginReadArray("bleNames");
for (int i = 0; i < size; ++i) {
mSettings.setArrayIndex(i);
QString address = mSettings.value("address").toString();
QString name = mSettings.value("name").toString();
mBleNames.insert(address, name);
}
mSettings.endArray();
connect(mBleUart, SIGNAL(dataRx(QByteArray)), this, SLOT(bleDataRx(QByteArray)));
mCommands->setbmsConfig(mbmsConfig);
// Other signals/slots
connect(mTimer, SIGNAL(timeout()), this, SLOT(timerSlot()));
connect(mPacket, SIGNAL(dataToSend(QByteArray&)),this, SLOT(packetDataToSend(QByteArray&)));
connect(mPacket, SIGNAL(packetReceived(QByteArray&)),this, SLOT(packetReceived(QByteArray&)));
connect(mCommands, SIGNAL(dataToSend(QByteArray&)),this, SLOT(cmdDataToSend(QByteArray&)));
connect(mCommands, SIGNAL(fwVersionReceived(int,int,QString,QByteArray)),this, SLOT(fwVersionReceived(int,int,QString,QByteArray)));
connect(mCommands, SIGNAL(ackReceived(QString)), this, SLOT(ackReceived(QString)));
connect(mbmsConfig, SIGNAL(updated()), this, SLOT(bmsconfUpdated()));
connect(mbmsConfig, SIGNAL(stored()), this, SLOT(bmsconfStored()));
}
BMSInterface::~BMSInterface()
{
mSettings.beginWriteArray("bleNames");
QHashIterator<QString, QString> i(mBleNames);
int ind = 0;
while (i.hasNext()) {
i.next();
mSettings.setArrayIndex(ind);
mSettings.setValue("address", i.key());
mSettings.setValue("name", i.value());
ind++;
}
mSettings.endArray();
}
Commands *BMSInterface::commands() const
{
return mCommands;
}
ConfigParams *BMSInterface::bmsConfig()
{
return mbmsConfig;
}
ConfigParams *BMSInterface::infoConfig()
{
return mInfoConfig;
}
QStringList BMSInterface::getSupportedFirmwares()
{
QList<QPair<int, int> > fwPairs = getSupportedFirmwarePairs();
QStringList fws;
for (int i = 0;i < fwPairs.size();i++) {
QString tmp;
tmp.sprintf("%d.%d", fwPairs.at(i).first, fwPairs.at(i).second);
fws.append(tmp);
}
return fws;
}
QList<QPair<int, int> > BMSInterface::getSupportedFirmwarePairs()
{
QList<QPair<int, int> > fws;
ConfigParam *p = mInfoConfig->getParam("fw_version");
if (p) {
QStringList strs = p->enumNames;
for (int i = 0;i < strs.size();i++) {
QStringList mami = strs.at(i).split(".");
QPair<int, int> fw = qMakePair(mami.at(0).toInt(), mami.at(1).toInt());
fws.append(fw);
}
}
return fws;
}
QString BMSInterface::getFirmwareNow()
{
return mFwTxt;
}
void BMSInterface::emitStatusMessage(const QString &msg, bool isGood)
{
emit statusMessage(msg, isGood);
}
void BMSInterface::emitMessageDialog(const QString &title, const QString &msg, bool isGood, bool richText)
{
emit messageDialog(title, msg, isGood, richText);
}
bool BMSInterface::fwRx()
{
return mFwVersionReceived;
}
void BMSInterface::storeSettings()
{
mSettings.beginWriteArray("bleNames");
QHashIterator<QString, QString> i(mBleNames);
int ind = 0;
while (i.hasNext()) {
i.next();
mSettings.setArrayIndex(ind);
mSettings.setValue("address", i.key());
mSettings.setValue("name", i.value());
ind++;
}
mSettings.endArray();
mSettings.beginWriteArray("profiles");
for (int i = 0; i < mProfiles.size(); ++i) {
MCCONF_TEMP cfg = mProfiles.value(i).value<MCCONF_TEMP>();
mSettings.setArrayIndex(i);
mSettings.setValue("current_min_scale", cfg.current_min_scale);
mSettings.setValue("current_max_scale", cfg.current_max_scale);
mSettings.setValue("erpm_or_speed_min", cfg.erpm_or_speed_min);
mSettings.setValue("erpm_or_speed_max", cfg.erpm_or_speed_max);
mSettings.setValue("duty_min", cfg.duty_min);
mSettings.setValue("duty_max", cfg.duty_max);
mSettings.setValue("watt_min", cfg.watt_min);
mSettings.setValue("watt_max", cfg.watt_max);
mSettings.setValue("name", cfg.name);
}
mSettings.endArray();
mSettings.beginWriteArray("pairedUuids");
for (int i = 0;i < mPairedUuids.size();i++) {
mSettings.setArrayIndex(i);
mSettings.setValue("uuid", mPairedUuids.at(i));
}
mSettings.endArray();
}
#ifdef HAS_BLUETOOTH
BleUart *BMSInterface::bleDevice()
{
return mBleUart;
}
void BMSInterface::storeBleName(QString address, QString name)
{
mBleNames.insert(address, name);
}
QString BMSInterface::getBleName(QString address)
{
QString res;
if(mBleNames.contains(address)) {
res = mBleNames[address];
}
return res;
}
QString BMSInterface::getLastBleAddr() const
{
return mLastBleAddr;
}
#endif
bool BMSInterface::isPortConnected()
{
bool res = false;
#ifdef HAS_SERIALPORT
if (mSerialPort->isOpen()) {
res = true;
}
#endif
if (mTcpConnected) {
res = true;
}
if (mBleUart->isConnected()) {
res = true;
}
return res;
}
void BMSInterface::disconnectPort()
{
#ifdef HAS_SERIALPORT
if(mSerialPort->isOpen()) {
mSerialPort->close();
updateFwRx(false);
}
#endif
if (mTcpConnected) {
mTcpSocket->close();
updateFwRx(false);
}
if (mBleUart->isConnected()) {
mBleUart->disconnectBle();
updateFwRx(false);
}
mFwRetries = 0;
}
bool BMSInterface::reconnectLastPort()
{
if (mLastConnType == CONN_SERIAL) {
#ifdef HAS_SERIALPORT
return connectSerial(mLastSerialPort, mLastSerialBaud);
#else
return false;
#endif
} else if (mLastConnType == CONN_TCP) {
connectTcp(mLastTcpServer, mLastTcpPort);
return true;
} else if (mLastConnType == CONN_BLE) {
mBleUart->startConnect(mLastBleAddr);
return true;
} else {
#ifdef HAS_SERIALPORT
QList<VSerialInfo_t> ports = listSerialPorts();
if (!ports.isEmpty()) {
return connectSerial(ports.first().systemPath);
} else {
emit messageDialog(tr("Reconnect"), tr("No ports found"), false, false);
return false;
}
#else
emit messageDialog(tr("Reconnect"),
tr("Please specify the connection manually "
"the first time you are connecting."),
false, false);
return false;
#endif
}
}
bool BMSInterface::autoconnect()
{
bool res = false;
#ifdef HAS_SERIALPORT
QList<VSerialInfo_t> ports = listSerialPorts();
mAutoconnectOngoing = true;
mAutoconnectProgress = 0.0;
disconnectPort();
disconnect(mCommands, SIGNAL(fwVersionReceived(int,int,QString,QByteArray)),
this, SLOT(fwVersionReceived(int,int,QString,QByteArray)));
for (int i = 0;i < ports.size();i++) {
VSerialInfo_t serial = ports[i];
if (!connectSerial(serial.systemPath)) {
continue;
}
QEventLoop loop;
QTimer timeoutTimer;
timeoutTimer.setSingleShot(true);
timeoutTimer.start(500);
connect(mCommands, SIGNAL(fwVersionReceived(int,int,QString,QByteArray)), &loop, SLOT(quit()));
connect(&timeoutTimer, SIGNAL(timeout()), &loop, SLOT(quit()));
loop.exec();
if (timeoutTimer.isActive()) {
// If the timer is still running a firmware version was received.
res = true;
break;
} else {
mAutoconnectProgress = (double)i / (double)ports.size();
emit autoConnectProgressUpdated(mAutoconnectProgress, false);
disconnectPort();
}
}
connect(mCommands, SIGNAL(fwVersionReceived(int,int,QString,QByteArray)),
this, SLOT(fwVersionReceived(int,int,QString,QByteArray)));
#endif
emit autoConnectProgressUpdated(1.0, true);
emit autoConnectFinished();
mAutoconnectOngoing = false;
return res;
}
QString BMSInterface::getConnectedPortName()
{
QString res = tr("Not connected");
bool connected = false;
#ifdef HAS_SERIALPORT
if (mSerialPort->isOpen()) {
res = tr("Connected (serial) to %1").arg(mSerialPort->portName());
connected = true;
}
#endif
if (mTcpConnected) {
res = tr("Connected (TCP) to %1:%2").arg(mLastTcpServer).arg(mLastTcpPort);
connected = true;
}
if (mBleUart->isConnected()) {
res = tr("Connected (BLE) to %1").arg(mLastBleAddr);
connected = true;
}
if (connected && mCommands->isLimitedMode()) {
res += tr(", limited mode");
}
return res;
}
bool BMSInterface::connectSerial(QString port, int baudrate)
{
#ifdef HAS_SERIALPORT
mLastSerialPort = port;
mLastSerialBaud = baudrate;
setLastConnectionType(CONN_SERIAL);
bool found = false;
for (VSerialInfo_t ser: listSerialPorts()) {
if (ser.systemPath == port) {
found = true;
break;
}
}
if (!found) {
emit statusMessage(tr("Invalid serial port: %1").arg(port), false);
return false;
}
if(mSerialPort->isOpen()) {
return true;
}
// TODO: Maybe this test works on other OSes as well
#ifdef Q_OS_UNIX
QFileInfo fi(port);
if (fi.exists()) {
if (!fi.isWritable()) {
emit statusMessage(tr("Serial port is not writable"), false);
emit serialPortNotWritable(port);
return false;
}
}
#endif
mSerialPort->setPortName(port);
mSerialPort->open(QIODevice::ReadWrite);
if(!mSerialPort->isOpen()) {
return false;
}
mSerialPort->setBaudRate(baudrate);
mSerialPort->setDataBits(QSerialPort::Data8);
mSerialPort->setParity(QSerialPort::NoParity);
mSerialPort->setStopBits(QSerialPort::OneStop);
mSerialPort->setFlowControl(QSerialPort::NoFlowControl);
// For nrf
mSerialPort->setRequestToSend(true);
mSerialPort->setDataTerminalReady(true);
QThread::msleep(5);
mSerialPort->setDataTerminalReady(false);
QThread::msleep(100);
return true;
#else
(void)port;
(void)baudrate;
emit messageDialog(tr("Connect serial"),
tr("Serial port support is not enabled in this build "
"of ENNOID-BMS Tool."),
false, false);
return false;
#endif
}
QList<VSerialInfo_t> BMSInterface::listSerialPorts()
{
QList<VSerialInfo_t> res;
#ifdef HAS_SERIALPORT
QList<QSerialPortInfo> ports = QSerialPortInfo::availablePorts();
foreach(const QSerialPortInfo &port, ports) {
VSerialInfo_t info;
info.name = port.portName();
info.systemPath = port.systemLocation();
int index = res.size();
if(port.manufacturer().startsWith("Silicon")) {
//info.name.insert(0, "ENNOID-BMS - ");
info.name.append(" - ENNOID-BMS");
info.isVesc = true;
index = 0;
} else {
info.isVesc = false;
}
res.insert(index, info);
}
#endif
return res;
}
void BMSInterface::connectTcp(QString server, int port)
{
mLastTcpServer = server;
mLastTcpPort = port;
setLastConnectionType(CONN_TCP);
QHostAddress host;
host.setAddress(server);
// Try DNS lookup
if (host.isNull()) {
QList<QHostAddress> addresses = QHostInfo::fromName(server).addresses();
if (!addresses.isEmpty()) {
host.setAddress(addresses.first().toString());
}
}
mTcpSocket->abort();
mTcpSocket->connectToHost(host, port);
}
void BMSInterface::connectBle(QString address)
{
mBleUart->startConnect(address);
mLastConnType = CONN_BLE;
mLastBleAddr = address;
setLastConnectionType(CONN_BLE);
}
bool BMSInterface::isAutoconnectOngoing() const
{
return mAutoconnectOngoing;
}
double BMSInterface::getAutoconnectProgress() const
{
return mAutoconnectProgress;
}
QVector<int> BMSInterface::scanCan()
{
QVector<int> canDevs;
if (!isPortConnected()) {
return canDevs;
}
QEventLoop loop;
bool timeout;
auto conn = connect(commands(), &Commands::pingCanRx,
[&canDevs, &timeout, &loop](QVector<int> devs, bool isTimeout) {
for (int dev: devs) {
canDevs.append(dev);
}
timeout = isTimeout;
loop.quit();
});
commands()->pingCan();
loop.exec();
disconnect(conn);
if (!timeout) {
mCanDevsLast = canDevs;
} else {
canDevs.clear();
}
return canDevs;
}
QVector<int> BMSInterface::getCanDevsLast() const
{
return mCanDevsLast;
}
void BMSInterface::ignoreCanChange(bool ignore)
{
mIgnoreCanChange = ignore;
}
#ifdef HAS_SERIALPORT
void BMSInterface::serialDataAvailable()
{
bool b = true;
while (mSerialPort->bytesAvailable() > 0) {
mPacket->processData(mSerialPort->readAll());
}
}
void BMSInterface::serialPortError(QSerialPort::SerialPortError error)
{
QString message;
switch (error) {
case QSerialPort::NoError:
break;
default:
message = "Serial port error: " + mSerialPort->errorString();
break;
}
if(!message.isEmpty()) {
emit statusMessage(message, false);
if (mSerialPort->isOpen()) {
mSerialPort->close();
}
updateFwRx(false);
}
}
#endif
void BMSInterface::tcpInputConnected()
{
bool b = true;
mTcpConnected = true;
updateFwRx(false);
}
void BMSInterface::tcpInputDisconnected()
{
mTcpConnected = false;
updateFwRx(false);
}
void BMSInterface::tcpInputDataAvailable()
{
bool b = true;
while (mTcpSocket->bytesAvailable() > 0) {
mPacket->processData(mTcpSocket->readAll());
}
}
void BMSInterface::tcpInputError(QAbstractSocket::SocketError socketError)
{
(void)socketError;
QString errorStr = mTcpSocket->errorString();
emit statusMessage(tr("TCP Error") + errorStr, false);
mTcpSocket->close();
updateFwRx(false);
}
#ifdef HAS_BLUETOOTH
void BMSInterface::bleDataRx(QByteArray data)
{
mPacket->processData(data);
}
#endif
void BMSInterface::timerSlot()
{
// Poll the serial port as well since readyRead is not emitted recursively. This
// can be a problem when waiting for input with an additional event loop, such as
// when using QMessageBox.
#ifdef HAS_SERIALPORT
serialDataAvailable();
#endif
if (isPortConnected()) {
if (mSendCanBefore != mCommands->getSendCan() ||
mCanIdBefore != mCommands->getCanSendId()) {
updateFwRx(false);
mFwRetries = 0;
}
mFwPollCnt++;
if (mFwPollCnt >= 4) {
mFwPollCnt = 0;
if (!mFwVersionReceived) {
mCommands->getFwVersion();
mFwRetries++;
// Timeout if the firmware cannot be read
if (mFwRetries >= 25) {
emit statusMessage(tr("No firmware read response"), false);
emit messageDialog(tr("Read Firmware Version"),
tr("Could not read firmware version. Make sure "
"that selected port really belongs to the ENNOID-BMS. "),
false, false);
disconnectPort();
}
}
}
} else {
updateFwRx(false);
mFwRetries = 0;
}
mSendCanBefore = mCommands->getSendCan();
mCanIdBefore = mCommands->getCanSendId();
// Update fw upload bar and label
double fwProg = mCommands->getFirmwareUploadProgress();
QString fwStatus = mCommands->getFirmwareUploadStatus();
if (fwProg > -0.1) {
mIsUploadingFw = true;
emit fwUploadStatus(fwStatus, fwProg, true);
} else {
// If the firmware upload just finished or failed
if (mIsUploadingFw) {
updateFwRx(false);
mFwRetries = 0;
if (fwStatus.compare("FW Upload Done") == 0) {
emit fwUploadStatus(fwStatus, 1.0, false);
} else {
emit fwUploadStatus(fwStatus, 0.0, false);
}
}
mIsUploadingFw = false;
}
if (mWasConnected != isPortConnected()) {
mWasConnected = isPortConnected();
emit portConnectedChanged();
}
}
void BMSInterface::packetDataToSend(QByteArray &data)
{
#ifdef HAS_SERIALPORT
if (mSerialPort->isOpen()) {
mSerialPort->write(data);
}
#endif
if (mTcpConnected && mTcpSocket->isOpen()) {
mTcpSocket->write(data);
}
if (mBleUart->isConnected()) {
mBleUart->writeData(data);
}
}
void BMSInterface::packetReceived(QByteArray &data)
{
mCommands->processPacket(data);
}
void BMSInterface::cmdDataToSend(QByteArray &data)
{
mPacket->sendPacket(data);
}
void BMSInterface::fwVersionReceived(int major, int minor, QString hw, QByteArray uuid)
{
QList<QPair<int, int> > fwPairs = getSupportedFirmwarePairs();
QString strUuid = Utility::uuid2Str(uuid, true);
if (fwPairs.isEmpty()) {
emit messageDialog(tr("Not Supported Firmwares"),
tr("This version of ENNOID-BMS Tool does not seem to have any supported "
"firmwares. Something is probably wrong with the BMS configuration "
"file."),
false, false);
updateFwRx(false);
mFwRetries = 0;
disconnectPort();
return;
}
QPair<int, int> highest_supported = *std::max_element(fwPairs.begin(), fwPairs.end());
QPair<int, int> fw_connected = qMakePair(major, minor);
bool wasReceived = mFwVersionReceived;
mCommands->setLimitedMode(false);
#if 0
if (major < 0) {
updateFwRx(false);
mFwRetries = 0;
disconnectPort();
emit messageDialog(tr("Error"), tr("The firmware on the connected ENNOID-BMS is too old. Please update it using a programmer."), false, false);
} else if (fw_connected > highest_supported) {
mCommands->setLimitedMode(true);
updateFwRx(true);
if (!wasReceived) {
emit messageDialog(tr("Warning"), tr("The connected ENNOID-BMS has newer firmware than this version of the"
" ENNOID-BMS Tool supports. It is recommended that you update the ENNOID-BMS"
" Tool to the latest version. Alternatively, the firmware on"
" the connected ENNOID-BMS can be downgraded in the firmware page."
" Until then, limited communication mode will be used where"
" only the firmware can be changed."), false, false);
}
} else if (!fwPairs.contains(fw_connected)) {
if (fw_connected >= qMakePair(0, 10)) {
mCommands->setLimitedMode(true);
updateFwRx(true);
if (!wasReceived) {
emit messageDialog(tr("Warning"), tr("The connected ENNOID-BMS has too old firmware. Since the"
" connected ENNOID-BMS has firmware with bootloader support, it can be"
" updated from the Firmware page."
" Until then, limited communication mode will be used where only the"
" firmware can be changed."), false, false);
}
} else {
updateFwRx(false);
mFwRetries = 0;
disconnectPort();
if (!wasReceived) {
emit messageDialog(tr("Error"), tr("The firmware on the connected ENNOID-BMS is too old. Please"
" update it using a programmer."), false, false);
}
}
} else {
#endif
updateFwRx(true);
if ((fw_connected < highest_supported)) {
if (!wasReceived) {
//emit messageDialog(tr("Warning"), tr("The connected ENNOID-BMS is compatible, but old firmware. It is recommended that you update it."), false, false);
}
}
QString fwStr;
fwStr.sprintf("ENNOID-BMS Firmware Version %d.%d", major, minor);
if (!hw.isEmpty()) {
fwStr += ", Hardware: " + hw;
}
if (!strUuid.isEmpty()) {
fwStr += ", UUID: " + strUuid;
}
emit statusMessage(fwStr, true);
#if 0
}
#endif
if (major >= 0) {
mFwTxt.sprintf("Fw: %d.%d", major, minor);
mHwTxt = hw;
if (!hw.isEmpty()) {
mFwTxt += ", Hw: " + hw;
}
if (!strUuid.isEmpty()) {
mFwTxt += "\n" + strUuid;
}
}
}
void BMSInterface::bmsconfUpdated()
{
emit statusMessage(tr("BMS configuration updated"), true);
}
void BMSInterface::bmsconfStored() {
emit statusMessage(tr("BMS configuration stored to Flash"), true);
}
void BMSInterface::ackReceived(QString ackType)
{
emit statusMessage(ackType, true);
}
void BMSInterface::updateFwRx(bool fwRx)
{
bool change = mFwVersionReceived != fwRx;
mFwVersionReceived = fwRx;
if (change) {
emit fwRxChanged(mFwVersionReceived, mCommands->isLimitedMode());
}
}
void BMSInterface::setLastConnectionType(conn_t type)
{
mLastConnType = type;
QSettings().setValue("connection_type", type);
}

214
bmsinterface.h Normal file
View File

@@ -0,0 +1,214 @@
/*
Original copyright 2018 Benjamin Vedder benjamin@vedder.se and the VESC Tool project ( https://github.com/vedderb/vesc_tool )
Forked to:
Copyright 2018 Danny Bokma github@diebie.nl (https://github.com/DieBieEngineering/DieBieMS-Tool)
Now forked to:
Copyright 2019 - 2020 Kevin Dionne kevin.dionne@ennoid.me (https://github.com/EnnoidMe/ENNOID-BMS-Tool)
This file is part of ENNOID-BMS Tool.
ENNOID-BMS Tool is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
ENNOID-BMS Tool is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef BMSInterface_H
#define BMSInterface_H
#include <QObject>
#include <QTimer>
#include <QByteArray>
#include <QList>
#include <QTcpSocket>
#include <QSettings>
#include <QHash>
#ifdef HAS_SERIALPORT
#include <QSerialPort>
#endif
#include "datatypes.h"
#include "configparams.h"
#include "commands.h"
#include "packet.h"
#ifdef HAS_BLUETOOTH
#include "bleuart.h"
#endif
class BMSInterface : public QObject
{
Q_OBJECT
public:
explicit BMSInterface(QObject *parent = 0);
~BMSInterface();
Q_INVOKABLE Commands *commands() const;
Q_INVOKABLE ConfigParams *bmsConfig();
Q_INVOKABLE ConfigParams *infoConfig();
Q_INVOKABLE QStringList getSupportedFirmwares();
QList<QPair<int, int> > getSupportedFirmwarePairs();
Q_INVOKABLE QString getFirmwareNow();
Q_INVOKABLE void emitStatusMessage(const QString &msg, bool isGood);
Q_INVOKABLE void emitMessageDialog(const QString &title, const QString &msg, bool isGood, bool richText = false);
Q_INVOKABLE bool fwRx();
Q_INVOKABLE void storeSettings();
//Q_INVOKABLE QVariantList getProfiles();
//Q_INVOKABLE void addProfile(QVariant profile);
//Q_INVOKABLE void clearProfiles();
//Q_INVOKABLE void deleteProfile(int index);
//Q_INVOKABLE void moveProfileUp(int index);
//Q_INVOKABLE void moveProfileDown(int index);
//Q_INVOKABLE MCCONF_TEMP getProfile(int index);
//Q_INVOKABLE void updateProfile(int index, QVariant profile);
//Q_INVOKABLE bool isProfileInUse(int index);
//Q_INVOKABLE MCCONF_TEMP createMcconfTemp();
//Q_INVOKABLE void updateMcconfFromProfile(MCCONF_TEMP profile);
// Q_INVOKABLE QStringList getPairedUuids();
// Q_INVOKABLE bool addPairedUuid(QString uuid);
// Q_INVOKABLE bool deletePairedUuid(QString uuid);
// Q_INVOKABLE void clearPairedUuids();
// Q_INVOKABLE bool hasPairedUuid(QString uuid);
// Q_INVOKABLE QString getConnectedUuid();
// Q_INVOKABLE bool isIntroDone();
// Q_INVOKABLE void setIntroDone(bool done);
// Q_INVOKABLE QString getLastTcpServer() const;
// Q_INVOKABLE int getLastTcpPort() const;
//#ifdef HAS_SERIALPORT
// Q_INVOKABLE QString getLastSerialPort() const;
// Q_INVOKABLE int getLastSerialBaud() const;
//#endif
bool swdEraseFlash();
bool swdUploadFw(QByteArray newFirmware, uint32_t startAddr = 0);
void swdCancel();
bool swdReboot();
#ifdef HAS_BLUETOOTH
Q_INVOKABLE BleUart* bleDevice();
Q_INVOKABLE void storeBleName(QString address, QString name);
Q_INVOKABLE QString getBleName(QString address);
Q_INVOKABLE QString getLastBleAddr() const;
#endif
// Connection
Q_INVOKABLE bool isPortConnected();
Q_INVOKABLE void disconnectPort();
Q_INVOKABLE bool reconnectLastPort();
Q_INVOKABLE bool autoconnect();
Q_INVOKABLE QString getConnectedPortName();
bool connectSerial(QString port, int baudrate = 115200);
QList<VSerialInfo_t> listSerialPorts();
Q_INVOKABLE void connectTcp(QString server, int port);
Q_INVOKABLE void connectBle(QString address);
Q_INVOKABLE bool isAutoconnectOngoing() const;
Q_INVOKABLE double getAutoconnectProgress() const;
Q_INVOKABLE QVector<int> scanCan();
Q_INVOKABLE QVector<int> getCanDevsLast() const;
Q_INVOKABLE void ignoreCanChange(bool ignore);
signals:
void statusMessage(const QString &msg, bool isGood);
void messageDialog(const QString &title, const QString &msg, bool isGood, bool richText);
void fwUploadStatus(const QString &status, double progress, bool isOngoing);
void serialPortNotWritable(const QString &port);
void fwRxChanged(bool rx, bool limited);
void portConnectedChanged();
void autoConnectProgressUpdated(double progress, bool isOngoing);
void autoConnectFinished();
void pairingListUpdated();
public slots:
private slots:
#ifdef HAS_SERIALPORT
void serialDataAvailable();
void serialPortError(QSerialPort::SerialPortError error);
#endif
void tcpInputConnected();
void tcpInputDisconnected();
void tcpInputDataAvailable();
void tcpInputError(QAbstractSocket::SocketError socketError);
void bleDataRx(QByteArray data);
void timerSlot();
void packetDataToSend(QByteArray &data);
void packetReceived(QByteArray &data);
void cmdDataToSend(QByteArray &data);
void fwVersionReceived(int major, int minor, QString hw, QByteArray uuid);
void bmsconfUpdated();
void bmsconfStored();
void ackReceived(QString ackType);
private:
typedef enum {
CONN_NONE = 0,
CONN_SERIAL,
CONN_TCP,
CONN_BLE
} conn_t;
QSettings mSettings;
QHash<QString, QString> mBleNames;
QVariantList mProfiles;
QStringList mPairedUuids;
ConfigParams *mbmsConfig;
ConfigParams *mInfoConfig;
QTimer *mTimer;
Packet *mPacket;
Commands *mCommands;
bool mFwVersionReceived;
int mFwRetries;
int mFwPollCnt;
QString mFwTxt;
QString mHwTxt;
bool mIsUploadingFw;
bool mCancelSwdUpload;
// Connections
conn_t mLastConnType;
#ifdef HAS_SERIALPORT
QSerialPort *mSerialPort;
QString mLastSerialPort;
int mLastSerialBaud;
#endif
QTcpSocket *mTcpSocket;
bool mTcpConnected;
QString mLastTcpServer;
int mLastTcpPort;
#ifdef HAS_BLUETOOTH
BleUart *mBleUart;
QString mLastBleAddr;
#endif
bool mSendCanBefore = false;
int mCanIdBefore = 0;
bool mWasConnected;
bool mAutoconnectOngoing;
double mAutoconnectProgress;
bool mIgnoreCanChange;
QVector<int> mCanDevsLast;
void updateFwRx(bool fwRx);
void setLastConnectionType(conn_t type);
};
#endif // BMSInterface_H

97
bmsservicepage.cpp Normal file
View File

@@ -0,0 +1,97 @@
#include "bmsservicepage.h"
#include "ui_bmsservicepage.h"
#include <QTimer>
//#include "widgets/historylineedit.h"
BmsServicePage::BmsServicePage(QWidget *parent) :
QFrame(parent),
ui(new Ui::BmsServicePage)
{
ui->setupUi(this);
connect(ui->helpButton, &QPushButton::clicked, this, &BmsServicePage::on_helpButton_clicked);
connect(ui->sendButton, &QPushButton::clicked, this, &BmsServicePage::on_sendButton_clicked);
connect(ui->terminalEdit, &HistoryLineEditBox::enterClicked, this, &BmsServicePage::on_sendButton_clicked);
//connect(ui->terminalEdit, &HistoryLineEditBox::upClicked, this, &BmsServicePage::onUpButtonClicked);
//connect(ui->terminalEdit, &HistoryLineEditBox::downClicked, this, &BmsServicePage::onDownButtonClicked);
connect(ui->clearButton, &QPushButton::clicked, this, &BmsServicePage::on_clearButton_clicked);
}
BmsServicePage::~BmsServicePage()
{
delete ui;
}
/*
void BmsServicePage::onUpButtonClicked()
{
ui->terminalEdit->
}
void BmsServicePage::onDownButtonClicked()
{
}
*/
void BmsServicePage::on_clearButton_clicked()
{
clearTerminal();
}
void BmsServicePage::clearTerminal()
{
ui->terminalBrowser->clear();
}
BMSInterface *BmsServicePage::bms() const
{
return mDieBieMS;
}
void BmsServicePage::setDieBieMS(BMSInterface *dieBieMS)
{
mDieBieMS = dieBieMS;
if (mDieBieMS) {
connect(mDieBieMS->commands(), SIGNAL(printReceived(QString)), this, SLOT(printReceived(QString)));
}
}
void BmsServicePage::printReceived(QString str)
{
ui->terminalBrowser->append(str);
}
//*
bool BmsServicePage::eventFilter(QObject *O, QEvent *E)
{
if(O == ui->terminalEdit)
{
if(E->type() == QKeyEvent::KeyPress)
{
QKeyEvent *pkey = (QKeyEvent *)E;
if( pkey->key()==Qt::Key_Enter || pkey->key()==Qt::Key_Return)
{
int a = 0;
return true;
}
}
}
return QWidget::eventFilter(O,E);
}
void BmsServicePage::on_sendButton_clicked()
{
if (mDieBieMS) {
mDieBieMS->commands()->sendTerminalCmd(ui->terminalEdit->text());
ui->terminalEdit->clear();
}
}
void BmsServicePage::on_helpButton_clicked()
{
if (mDieBieMS) {
mDieBieMS->commands()->sendTerminalCmd("help");
}
}

60
bmsservicepage.h Normal file
View File

@@ -0,0 +1,60 @@
#ifndef BMSSERVICEPAGE_H
#define BMSSERVICEPAGE_H
#include <QFrame>
#include <QButtonGroup>
#include "bmsinterface.h"
namespace Ui {
class BmsServicePage;
}
/*
class KeyboardFilter : public QObject
{
public:
KeyboardFilter( QObject *parent = 0 ) : QObject( parent ) {}
protected:
bool eventFilter( QObject *dist, QEvent *event );
};
*/
class BmsServicePage : public QFrame
{
Q_OBJECT
public:
explicit BmsServicePage(QWidget *parent = nullptr);
~BmsServicePage();
void clearTerminal();
BMSInterface *bms() const;
void setDieBieMS(BMSInterface *dieBieMS);
protected:
// void keyPressEvent(QKeyEvent *);
bool eventFilter(QObject *, QEvent *);
public slots:
void printReceived(QString str);
//void onUpButtonClicked();
//void onDownButtonClicked();
private slots:
void on_sendButton_clicked();
void on_helpButton_clicked();
void on_clearButton_clicked();
private:
Ui::BmsServicePage *ui;
BMSInterface *mDieBieMS;
//KeyboardFilter filter;
};
#endif // BMSSERVICEPAGE_H

281
bmsservicepage.ui Normal file
View File

@@ -0,0 +1,281 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>BmsServicePage</class>
<widget class="QFrame" name="BmsServicePage">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>1071</width>
<height>755</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<property name="styleSheet">
<string notr="true">
QWidget#BmsServicePage
{
background-color: #f7f8fc
}
QPushButton#pbVisualizationMenuBarVoltage
{
border: 2px solid lightgrey;
border-top-left-radius: 15px;
background: white;
color: black;
font-size: 18px;
border-right-color: white;
}
QPushButton#pbVisualizationMenuBarAmperage
{
border: 2px solid lightgrey;
/* border-top-left-radius: 20px;*/
background: white;
color: black;
font-size: 18px;
border-left-color: white;
border-right-color: white;
}
QPushButton#pbVisualizationMenuBarTemperature
{
border: 2px solid lightgrey;
background: white;
color: black;
font-size: 18px;
border-left-color: white;
border-right-color: white;
}
/*----------------------------------------------------------------------*/
QPushButton#pbVisualizationMenuBarVoltage:hover:checked,
#pbVisualizationMenuBarAmperage:hover:checked,
#pbVisualizationMenuBarTemperature:hover:checked
{
/*background-color: #01673a;*/
border-bottom-color:#009352;
color:#009352;
}
QPushButton#pbVisualizationMenuBarVoltage:!hover:checked,
#pbVisualizationMenuBarAmperage:!hover:checked,
#pbVisualizationMenuBarTemperature:!hover:checked
{
/*background-color: #01673a;*/
border-bottom-color: #009352;
color:#009352;
}
QPushButton#pbVisualizationMenuBarVoltage:hover:!checked,
#pbVisualizationMenuBarAmperage:hover:!checked,
#pbVisualizationMenuBarTemperature:hover:!checked
{
/*background-color: #009352;*/
/*background-color: #028249;*/
border-bottom-color: lightgrey;
color:black;
}
</string>
</property>
<widget class="QLabel" name="lbCenterTabName">
<property name="geometry">
<rect>
<x>30</x>
<y>30</y>
<width>921</width>
<height>41</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>26</pointsize>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Терминал</string>
</property>
<property name="alignment">
<set>Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft</set>
</property>
</widget>
<widget class="QLabel" name="labTerminalBrowserBackground">
<property name="geometry">
<rect>
<x>33</x>
<y>90</y>
<width>991</width>
<height>581</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>14</pointsize>
<weight>50</weight>
<bold>false</bold>
</font>
</property>
<property name="styleSheet">
<string notr="true"> background-color:#FFFFFF;
border: 2px solid lightgrey;
border-top-left-radius: 15px;
border-top-right-radius: 15px;
</string>
</property>
<property name="text">
<string/>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
<widget class="QWidget" name="layoutWidget">
<property name="geometry">
<rect>
<x>30</x>
<y>680</y>
<width>991</width>
<height>42</height>
</rect>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QPushButton" name="helpButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>Send help command to VESC</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="res.qrc">
<normaloff>:/res/icons/Help-96.png</normaloff>:/res/icons/Help-96.png</iconset>
</property>
<property name="iconSize">
<size>
<width>32</width>
<height>32</height>
</size>
</property>
</widget>
</item>
<item>
<widget class="HistoryLineEditBox" name="terminalEdit">
<property name="font">
<font>
<pointsize>20</pointsize>
</font>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="sendButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>Send command</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="res.qrc">
<normaloff>:/res/icons/Send File-96.png</normaloff>:/res/icons/Send File-96.png</iconset>
</property>
<property name="iconSize">
<size>
<width>32</width>
<height>32</height>
</size>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="clearButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>Clear terminal</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="res.qrc">
<normaloff>:/res/icons/Delete-96.png</normaloff>:/res/icons/Delete-96.png</iconset>
</property>
<property name="iconSize">
<size>
<width>32</width>
<height>32</height>
</size>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QTextBrowser" name="terminalBrowser">
<property name="geometry">
<rect>
<x>33</x>
<y>91</y>
<width>991</width>
<height>581</height>
</rect>
</property>
<property name="font">
<font>
<family>DejaVu Sans Mono</family>
</font>
</property>
<property name="styleSheet">
<string notr="true"> background-color:transparent;
border: 2px solid lightgrey;
border-top-left-radius: 15px;
border-top-right-radius: 15px;
</string>
</property>
<property name="lineWrapMode">
<enum>QTextEdit::NoWrap</enum>
</property>
</widget>
<zorder>labTerminalBrowserBackground</zorder>
<zorder>lbCenterTabName</zorder>
<zorder>layoutWidget</zorder>
<zorder>terminalBrowser</zorder>
</widget>
<customwidgets>
<customwidget>
<class>HistoryLineEditBox</class>
<extends>QLineEdit</extends>
<header location="global">historylineeditbox.h</header>
</customwidget>
</customwidgets>
<resources>
<include location="res.qrc"/>
</resources>
<connections/>
</ui>

646
bmssettings.cpp Normal file
View File

@@ -0,0 +1,646 @@
#include "bmssettings.h"
#include "ui_bmssettings.h"
#include "widgets/paramtable.h"
#include "widgets/parameditdouble.h"
#include "configparamsgetter.h"
#include <QIntValidator>
#include <QDoubleValidator>
#include <QMessageBox>
#include <QFileDialog>
BmsSettings::BmsSettings(QWidget *parent) :
QFrame(parent),
ui(new Ui::BmsSettings)
{
ui->setupUi(this);
ui->pbWriteToNonVolatileBmsMemory->hide();
ui->pbWriteCurrentValuesToBms->hide();
// https://localcoder.org/set-qlineedit-to-accept-only-numbers
ui->editSerialNumber->setValidator(new QRegExpValidator(QRegExp("[1-9][0-9]*"), this)); // only numbers // leading digit must be 1 to 9 (prevents leading zeroes).
ui->editGroupsNumber->setValidator( new QIntValidator(-1000, 1000, this) );
ui->editCellsNumber->setValidator( new QIntValidator(-1000, 1000, this) );
ui->editCellsInParallelGroup->setValidator( new QIntValidator(1, 1000, this) );
ui->editBatteryCapacity->setValidator( new QIntValidator(0, 10000, this) );
ui->editMaxVoltageCharge->setValidator( new QIntValidator(-1000, 1000, this) );
ui->editMaxCircuitLoad->setValidator( new QIntValidator(-1000, 10000, this) );
ui->editMaxTemperature->setValidator( new QIntValidator(-1000, 1000, this) );
ui->editMinVoltageWhenWeTurnLoadOff->setValidator( new QDoubleValidator(-100, 100, 2, this) );
ui->editSuchVoltageWhenWeTurnLoadOn->setValidator( new QDoubleValidator(-100, 100, 2, this) );
ui->editSuchVoltageWhenWeTurnChargeOn->setValidator( new QDoubleValidator(-100, 100, 2, this) );
ui->editMaxVoltageWhenWeTurnChargeOff->setValidator( new QDoubleValidator(-100, 100, 2, this) );
ui->editVoltageBalancingStart->setValidator( new QDoubleValidator(-100, 100, 2, this) );
ui->editVoltageDifferenceThreshold->setValidator( new QDoubleValidator(-100, 100, 2, this) );
ui->editCellBalancingInterval->setValidator( new QIntValidator(-1000, 100000, this) );
//V
//ui->cboxUseToManageStorageDevice
//ui->cboxNormallyClosed
ui->editSocValueEdit->setValidator( new QDoubleValidator(-100, 100, 2, this) );
//ui->cboxTemperatureSettingsOver
ui->editTemperatureIfLessThenCloseOver->setValidator( new QIntValidator(-1000, 1000, this) );
ui->editTemperatureIfMoeeThenOpenOver->setValidator( new QIntValidator(-1000, 1000, this) );
//ui->cboxTemperatureSettingsUnder
ui->editTemperatureIfLessThenCloseUnder->setValidator( new QIntValidator(-1000, 1000, this) );
ui->editTemperatureIfMoeeThenOpenUnder->setValidator( new QIntValidator(-1000, 1000, this) );
ui->editPointZeroDetectorValue->setValidator( new QDoubleValidator(-1000, 1000, 2, this) );
// BTNS
//ui->pbCalibrateZeroPoint
//ui->pbWriteIntoNonVolatileMemoryOfBsm
//ui->pbWriteCurrentValuesIntoBsm
connect(ui->pbSaveBMSConfToXmlFile, &QPushButton::clicked, this, &BmsSettings::onSaveBMSConfToXmlFileClicked);
connect(ui->pbLoadBMSConfFromXmlFile, &QPushButton::clicked, this, &BmsSettings::onLoadBMSConfFromXmlFileClicked);
clearControlls();
}
BmsSettings::~BmsSettings()
{
delete ui;
}
//
void BmsSettings::onBmsconfUpdateRequested()
{
int a = 1;
}
void BmsSettings::onBmsconfUpdateRequestDefault()
{
int a = 1;
}
void BmsSettings::onBmsconfUpdated()
{
// ^ - 8 7 | 8 8 read bms conf
// After we hav red real BMS Config params from BMS we put all of them into the GUI elements
// Load default params from Bms to GUI Elements
//-> - 7 7 | 7 8 read default 8 is defaul - that was on the start
// Загрузить настройки\nпо умолчанию
//mDieBieMS->commands()->getBMSconfDefault();
onLoadParams();
}
void BmsSettings::onBmsconfStored()
{
int a = 1;
}
void BmsSettings::clearControlls()
{
ui->editSerialNumber ->clear();
ui->editGroupsNumber ->clear();
ui->editCellsNumber ->clear();
ui->editCellsInParallelGroup ->clear();
ui->editBatteryCapacity ->clear();
ui->editMaxVoltageCharge ->clear();
ui->editMaxCircuitLoad ->clear();
ui->editMaxTemperature ->clear();
ui->editMinVoltageWhenWeTurnLoadOff ->clear();
ui->editSuchVoltageWhenWeTurnLoadOn ->clear();
ui->editSuchVoltageWhenWeTurnChargeOn ->clear();
ui->editMaxVoltageWhenWeTurnChargeOff ->clear();
ui->editVoltageBalancingStart ->clear();
ui->editVoltageDifferenceThreshold ->clear();
ui->editCellBalancingInterval ->clear();
//1
ui->cboxUseToManageStorageDevice ->setChecked(false);
//2
ui->cboxNormallyClosed ->setChecked(false);
ui->editSocValueEdit ->clear();
//3
ui->cboxTemperatureSettingsOver ->setChecked(false);
ui->editTemperatureIfLessThenCloseOver ->clear();
ui->editTemperatureIfMoeeThenOpenOver ->clear();
//4
ui->cboxTemperatureSettingsUnder ->setChecked(false);
ui->editTemperatureIfLessThenCloseUnder ->clear();
ui->editTemperatureIfMoeeThenOpenUnder ->clear();
// 0
ui->editPointZeroDetectorValue ->clear();
}
void BmsSettings::prepareSettersFromGuiControlsToSettingsFile()
{
/// Setters
#define QS QString
auto cfg = mDieBieMS->bmsConfig();
/// Серийный номер * //ui->editSerialNumber
connect(ui->editSerialNumber, &QLineEdit::textChanged, this, [this, cfg]()
{
QS s =ui->editSerialNumber->text();
QVariant val((s.isEmpty() || s=="-") ? 0 : s.toInt());
cfg->setParamValue("notUsedCurrentThreshold", val);
});
/// Конфигурация *
connect(ui->editGroupsNumber, &QLineEdit::textChanged, this, [this, cfg]()
{ QS s =ui->editGroupsNumber->text(); QVariant val((s.isEmpty() || s=="-") ? 0 : s.toInt()); cfg->setParamValue("cellMonitorICCount", val); });
connect(ui->editCellsNumber, &QLineEdit::textChanged, this, [this, cfg]()
{
QS s =ui->editCellsNumber->text();
QVariant val((s.isEmpty() || s=="-") ? 0 : s.toInt());
cfg->setParamValue("noOfCellsSeries", val);
});
// Конфигурация *
//addParamRow(ui->editGroupsNumber , mDieBieMS->bmsConfig(), "cellMonitorICCount");
//addParamRow(ui->editCellsNumber , mDieBieMS->bmsConfig(), "noOfCellsSeries");
/// SOC *
connect(ui->editCellsInParallelGroup, &QLineEdit::textChanged, this, [this, cfg]()
{ QS s =ui->editCellsInParallelGroup->text(); QVariant val((s.isEmpty() || s=="-") ? 0 : s.toInt()); cfg->setParamValue("noOfCellsParallel", val); });
connect(ui->editBatteryCapacity, &QLineEdit::textChanged, this, [this, cfg]()
{ QS s =ui->editBatteryCapacity->text(); QVariant val((s.isEmpty() || s=="-") ? 0 : s.toInt()); cfg->setParamValue("batteryCapacity", val); });
// SOC *
//addParamRow(ui->editCellsInParallelGroup , mDieBieMS->bmsConfig(), "noOfCellsParallel");
//addParamRow(ui->editBatteryCapacity , mDieBieMS->bmsConfig(), "batteryCapacity");
/// Ограничения *
connect(ui->editMaxVoltageCharge, &QLineEdit::textChanged, this, [this, cfg]()
{ QS s =ui->editMaxVoltageCharge->text(); QVariant val((s.isEmpty() || s=="-") ? 0 : s.toInt()); cfg->setParamValue("maxAllowedCurrent", val); });
connect(ui->editMaxCircuitLoad, &QLineEdit::textChanged, this, [this, cfg]()
{ QS s =ui->editMaxCircuitLoad->text(); QVariant val((s.isEmpty() || s=="-") ? 0 : s.toInt()); cfg->setParamValue("maxMismatchThreshold", val); });
connect(ui->editMaxTemperature, &QLineEdit::textChanged, this, [this, cfg]()
{ QS s =ui->editMaxTemperature->text(); QVariant val((s.isEmpty() || s=="-") ? 0 : s.toInt()); cfg->setParamValue("allowedTempBattChargingMax", val); });
// Ограничения *
//addParamRow(ui->editMaxVoltageCharge , mDieBieMS->bmsConfig(), "maxAllowedCurrent");
//addParamRow(ui->editMaxCircuitLoad , mDieBieMS->bmsConfig(), "maxMismatchThreshold");
//addParamRow(ui->editMaxTemperature , mDieBieMS->bmsConfig(), "allowedTempBattChargingMax");
/// Конфигурация ячеек *
connect(ui->editMinVoltageWhenWeTurnLoadOff, &QLineEdit::textChanged, this, [this, cfg]()
{ QS s =ui->editMinVoltageWhenWeTurnLoadOff->text(); QVariant val((s.isEmpty() || s=="-") ? 0 : s.toDouble()); cfg->setParamValue("cellHardUnderVoltage", val); });
connect(ui->editSuchVoltageWhenWeTurnLoadOn, &QLineEdit::textChanged, this, [this, cfg]()
{ QS s =ui->editSuchVoltageWhenWeTurnLoadOn->text(); QVariant val((s.isEmpty() || s=="-") ? 0 : s.toDouble()); cfg->setParamValue("cellHardOverVoltage", val); });
connect(ui->editSuchVoltageWhenWeTurnChargeOn, &QLineEdit::textChanged, this, [this, cfg]()
{ QS s =ui->editSuchVoltageWhenWeTurnChargeOn->text(); QVariant val((s.isEmpty() || s=="-") ? 0 : s.toDouble()); cfg->setParamValue("cellLCSoftUnderVoltage", val); });
connect(ui->editMaxVoltageWhenWeTurnChargeOff, &QLineEdit::textChanged, this, [this, cfg]()
{ QS s =ui->editMaxVoltageWhenWeTurnChargeOff->text(); QVariant val((s.isEmpty() || s=="-") ? 0 : s.toDouble()); cfg->setParamValue("cellSoftOverVoltage", val); });
// Конфигурация ячеек *
//addParamRow(ui->editMinVoltageWhenWeTurnLoadOff , mDieBieMS->bmsConfig(), "cellHardUnderVoltage");
//addParamRow(ui->editSuchVoltageWhenWeTurnLoadOn , mDieBieMS->bmsConfig(), "cellHardOverVoltage");
//addParamRow(ui->editSuchVoltageWhenWeTurnChargeOn , mDieBieMS->bmsConfig(), "cellLCSoftUnderVoltage");
//addParamRow(ui->editMaxVoltageWhenWeTurnChargeOff , mDieBieMS->bmsConfig(), "cellSoftOverVoltage");
/// Конфигурация балансировки * // ui->balancingTab
connect(ui->editVoltageBalancingStart, &QLineEdit::textChanged, this, [this, cfg]()
{ QS s =ui->editVoltageBalancingStart->text(); QVariant val((s.isEmpty() || s=="-") ? 0 : s.toDouble()); cfg->setParamValue("cellBalanceStart", val); });
connect(ui->editVoltageDifferenceThreshold, &QLineEdit::textChanged, this, [this, cfg]()
{ QS s =ui->editVoltageDifferenceThreshold->text(); QVariant val((s.isEmpty() || s=="-") ? 0 : s.toDouble()); cfg->setParamValue("cellBalanceDifferenceThreshold", val); });
connect(ui->editCellBalancingInterval, &QLineEdit::textChanged, this, [this, cfg]()
{ QS s =ui->editCellBalancingInterval->text(); QVariant val((s.isEmpty() || s=="-") ? 0 : s.toInt()); cfg->setParamValue("cellBalanceUpdateInterval", val); });
// Конфигурация балансировки * // ui->balancingTab
//addParamRow(ui->editVoltageBalancingStart , mDieBieMS->bmsConfig(), "cellBalanceStart");
//addParamRow(ui->editVoltageDifferenceThreshold , mDieBieMS->bmsConfig(), "cellBalanceDifferenceThreshold");
//addParamRow(ui->editCellBalancingInterval , mDieBieMS->bmsConfig(), "cellBalanceUpdateInterval");
/// Калибровать Нулевую точку * // ui->signalsTab
connect(ui->editPointZeroDetectorValue, &QLineEdit::textChanged, this, [this, cfg]()
{
QS s =ui->editPointZeroDetectorValue->text();
QVariant val((s.isEmpty() || s=="-") ? 0 : s.toDouble());
cfg->setParamValue("shuntLCFactor", val);
});
// Нулевая точка * // ui->signalsTab
//addParamRow(ui->editPointZeroDetectorValue , mDieBieMS->bmsConfig(), "shuntLCFactor");
}
void BmsSettings::onLoadParams()
{
////
// Серийный номер * //ui->editSerialNumber
getSerialNumber(ui->editSerialNumber , mDieBieMS->bmsConfig(), "notUsedCurrentThreshold");
// Конфигурация *
addParamRow(ui->editGroupsNumber , mDieBieMS->bmsConfig(), "cellMonitorICCount");
addParamRow(ui->editCellsNumber , mDieBieMS->bmsConfig(), "noOfCellsSeries");
// SOC *
addParamRow(ui->editCellsInParallelGroup , mDieBieMS->bmsConfig(), "noOfCellsParallel");
addParamRow(ui->editBatteryCapacity , mDieBieMS->bmsConfig(), "batteryCapacity");
// Ограничения *
addParamRow(ui->editMaxVoltageCharge , mDieBieMS->bmsConfig(), "maxAllowedCurrent");
addParamRow(ui->editMaxCircuitLoad , mDieBieMS->bmsConfig(), "maxMismatchThreshold");
addParamRow(ui->editMaxTemperature , mDieBieMS->bmsConfig(), "allowedTempBattChargingMax");
// Конфигурация ячеек *
addParamRow(ui->editMinVoltageWhenWeTurnLoadOff , mDieBieMS->bmsConfig(), "cellHardUnderVoltage");
addParamRow(ui->editSuchVoltageWhenWeTurnLoadOn , mDieBieMS->bmsConfig(), "cellHardOverVoltage");
addParamRow(ui->editSuchVoltageWhenWeTurnChargeOn , mDieBieMS->bmsConfig(), "cellLCSoftUnderVoltage");
addParamRow(ui->editMaxVoltageWhenWeTurnChargeOff , mDieBieMS->bmsConfig(), "cellSoftOverVoltage");
// Конфигурация балансировки *
// ui->balancingTab
addParamRow(ui->editVoltageBalancingStart , mDieBieMS->bmsConfig(), "cellBalanceStart");
addParamRow(ui->editVoltageDifferenceThreshold , mDieBieMS->bmsConfig(), "cellBalanceDifferenceThreshold");
addParamRow(ui->editCellBalancingInterval , mDieBieMS->bmsConfig(), "cellBalanceUpdateInterval");
// Нулевая точка *
// ui->signalsTab
addParamRow(ui->editPointZeroDetectorValue , mDieBieMS->bmsConfig(), "shuntLCFactor");
////*/
// Turn ON Setters only AFTER we load all data from file
prepareSettersFromGuiControlsToSettingsFile();
// Turn ON Setters only AFTER we load all data from file
/*
<cellBalanceStart>
<longName>Cell balance start voltage</longName>
cellMonitorICCount ->
noOfCellsSeries ->
--
noOfCellsParallel ->
<batteryCapacity>
<longName>Battery capacity total</longName>
--
<maxMismatchThreshold>
<longName>Mismatch threshold</longName>
<maxAllowedCurrent>
<longName>Max allowed current</longName>
<allowedTempBattChargingMax>
<longName>Battery temperature charging max</longName>
--
<cellHardUnderVoltage>
<longName>Cell hard undervoltage</longName>
<cellHardOverVoltage>
<longName>Cell hard overvoltage</longName>
<cellLCSoftUnderVoltage>
<longName>Cell low current soft undervoltage</longName>
<cellSoftOverVoltage>
<longName>Cell soft overvoltage</longName>
--
*/
/*
#if 0
ConfigParam *p = mDieBieMS->infoConfig()->getParam("master_setting_description");
if (p != 0) {
ui->textEdit->setHtml(p->description);
} else {
ui->textEdit->setText("Master Setting Description not found.");
}
#endif
ConfigParams *const conf = mDieBieMS->bmsConfig();
///ParamTable *const pt = new ParamTable(this); //ui->masterStateTab;
///pt->addRowSeparator(tr("Current"));
// Нулевая точка
addParamRow(ui->editPointZeroDetectorValue , mDieBieMS->bmsConfig(), "shuntLCFactor");
//pt->addParamRow(conf, "shuntLCFactor");
////pt->addParamRow(conf, "shuntOffset");
///pt->addRowSeparator(tr("Pack configuration"));
#if 0 // disable
pt->addParamRow(conf, "cellMonitorICType");
#endif
// Конфигурация
addParamRow(ui->editGroupsNumber , mDieBieMS->bmsConfig(), "cellMonitorICCount");
addParamRow(ui->editCellsNumber , mDieBieMS->bmsConfig(), "noOfCellsSeries");
//pt->addParamRow(conf, "cellMonitorICCount");
///pt->addParamRow(conf, "noOfParallelModules");
//pt->addParamRow(conf, "noOfCellsSeries");
///pt->addRowSeparator(tr("SOC - Pack capacity"));
//pt->addParamRow(conf, "noOfCellsParallel");
//pt->addParamRow(conf, "batteryCapacity");
// SOC
addParamRow(ui->editCellsInParallelGroup , mDieBieMS->bmsConfig(), "noOfCellsParallel");
addParamRow(ui->editBatteryCapacity , mDieBieMS->bmsConfig(), "batteryCapacity");
///pt->addRowSeparator(tr("Cell specifications"));
//pt->addParamRow(conf, "cellHardUnderVoltage");
//pt->addParamRow(conf, "cellHardOverVoltage");
//pt->addParamRow(conf, "cellLCSoftUnderVoltage");
//pt->addParamRow(conf, "cellSoftOverVoltage");
// Конфигурация ячеек
addParamRow(ui->editMinVoltageWhenWeTurnLoadOff , mDieBieMS->bmsConfig(), "cellHardUnderVoltage");
addParamRow(ui->editSuchVoltageWhenWeTurnLoadOn , mDieBieMS->bmsConfig(), "cellHardOverVoltage");
addParamRow(ui->editSuchVoltageWhenWeTurnChargeOn , mDieBieMS->bmsConfig(), "cellLCSoftUnderVoltage");
addParamRow(ui->editMaxVoltageWhenWeTurnChargeOff , mDieBieMS->bmsConfig(), "cellSoftOverVoltage");
///pt->addRowSeparator(tr("Balancing configuration"));
//pt->addParamRow(conf, "cellBalanceStart");
//pt->addParamRow(conf, "cellBalanceDifferenceThreshold");
//pt->addParamRow(conf, "cellBalanceUpdateInterval");
// Конфигурация балансировки
// ui->balancingTab
addParamRow(ui->editVoltageBalancingStart , mDieBieMS->bmsConfig(), "cellBalanceStart");
addParamRow(ui->editVoltageDifferenceThreshold , mDieBieMS->bmsConfig(), "cellBalanceDifferenceThreshold");
addParamRow(ui->editCellBalancingInterval , mDieBieMS->bmsConfig(), "cellBalanceUpdateInterval");
///pt->addRowSeparator(tr("SoC general"));
#if 0 // disable
pt->addParamRow(conf, "stateOfChargeStoreInterval");
pt->addParamRow(conf, "timeoutChargeCompleted");
pt->addParamRow(conf, "timeoutChargingCompletedMinimalMismatch");
#endif
///pt->addParamRow(conf, "minimalPrechargePercentage"); // from pagemastersettings.cpp
///pt->addParamRow(conf, "timeoutLCPreCharge"); // from pagemastersettings.cpp
///pt->addRowSeparator(tr("Limits"));
//pt->addParamRow(conf, "maxMismatchThreshold");
//pt->addParamRow(conf, "maxAllowedCurrent");
// Ограничения
addParamRow(ui->editMaxVoltageCharge , mDieBieMS->bmsConfig(), "maxAllowedCurrent");
addParamRow(ui->editMaxCircuitLoad , mDieBieMS->bmsConfig(), "maxMismatchThreshold");
addParamRow(ui->editMaxTemperature , mDieBieMS->bmsConfig(), "allowedTempBattChargingMax");
#if 0
pt->addParamRow(conf, "allowedTempBattDischargingMax");
pt->addParamRow(conf, "allowedTempBattDischargingMin");
#endif
//pt->addParamRow(conf, "allowedTempBattChargingMax");
///pt->addParamRow(conf, "allowedTempBattChargingMin");
#if 0 // disable
///pt->addRowSeparator(tr("NTC specifications battery"));
pt->addParamRow(mDieBieMS->bmsConfig(), "noOfTempSensorPerModule");
pt->addParamRow(mDieBieMS->bmsConfig(), "NTCLTC25Deg");
///pt->addRowSeparator(tr("NTC specifications expansion Board"));
pt->addParamRow(mDieBieMS->bmsConfig(), "noOfExpansionBoard");
pt->addParamRow(mDieBieMS->bmsConfig(), "noOfTempSensorPerExpansionBoard");
#endif
*/
}
BMSInterface *BmsSettings::bms() const
{
return mDieBieMS;
}
void BmsSettings::setDieBieMS(BMSInterface *dieBieMS)
{ // void PageMasterSettings::setDieBieMS(BMSInterface *dieBieMS)
mDieBieMS = dieBieMS;
//pt = new ParamTable(this); //ui->masterStateTab;
//onLoadParams();
/*
if (mDieBieMS) {
#if 0
ConfigParam *p = mDieBieMS->infoConfig()->getParam("master_setting_description");
if (p != 0) {
ui->textEdit->setHtml(p->description);
} else {
ui->textEdit->setText("Master Setting Description not found.");
}
#endif
ConfigParams *const conf = mDieBieMS->bmsConfig();
ParamTable *const pt = new ParamTable(this); //ui->masterStateTab;
pt->addRowSeparator(tr("Current"));
pt->addParamRow(conf, "shuntLCFactor");
pt->addParamRow(conf, "shuntOffset");
pt->addRowSeparator(tr("Pack configuration"));
#if 0 // disable
pt->addParamRow(conf, "cellMonitorICType");
#endif
pt->addParamRow(conf, "cellMonitorICCount");
pt->addParamRow(conf, "noOfParallelModules");
pt->addParamRow(conf, "noOfCellsSeries");
pt->addRowSeparator(tr("SOC - Pack capacity"));
pt->addParamRow(conf, "noOfCellsParallel");
pt->addParamRow(conf, "batteryCapacity");
pt->addRowSeparator(tr("Cell specifications"));
pt->addParamRow(conf, "cellHardUnderVoltage");
pt->addParamRow(conf, "cellHardOverVoltage");
pt->addParamRow(conf, "cellLCSoftUnderVoltage");
pt->addParamRow(conf, "cellSoftOverVoltage");
pt->addRowSeparator(tr("Balancing configuration"));
pt->addParamRow(conf, "cellBalanceStart");
pt->addParamRow(conf, "cellBalanceDifferenceThreshold");
pt->addParamRow(conf, "cellBalanceUpdateInterval");
pt->addRowSeparator(tr("SoC general"));
#if 0 // disable
pt->addParamRow(conf, "stateOfChargeStoreInterval");
pt->addParamRow(conf, "timeoutChargeCompleted");
pt->addParamRow(conf, "timeoutChargingCompletedMinimalMismatch");
#endif
pt->addParamRow(conf, "minimalPrechargePercentage"); // from pagemastersettings.cpp
pt->addParamRow(conf, "timeoutLCPreCharge"); // from pagemastersettings.cpp
pt->addRowSeparator(tr("Limits"));
pt->addParamRow(conf, "maxMismatchThreshold");
pt->addParamRow(conf, "maxAllowedCurrent");
#if 0
pt->addParamRow(conf, "allowedTempBattDischargingMax");
pt->addParamRow(conf, "allowedTempBattDischargingMin");
#endif
pt->addParamRow(conf, "allowedTempBattChargingMax");
pt->addParamRow(conf, "allowedTempBattChargingMin");
#if 0 // disable
pt->addRowSeparator(tr("NTC specifications battery"));
pt->addParamRow(mDieBieMS->bmsConfig(), "noOfTempSensorPerModule");
pt->addParamRow(mDieBieMS->bmsConfig(), "NTCLTC25Deg");
pt->addRowSeparator(tr("NTC specifications expansion Board"));
pt->addParamRow(mDieBieMS->bmsConfig(), "noOfExpansionBoard");
pt->addParamRow(mDieBieMS->bmsConfig(), "noOfTempSensorPerExpansionBoard");
#endif
}
*/
}
/*
bool BmsSettings::addParamRow(QLineEdit* edit, ConfigParams *params, QString paramName)
{
bool res = false;
QWidget *editor = params->getEditor(paramName);
QString name = params->getLongName(paramName);
if (editor) {
//int row = rowCount();
//setRowCount(row + 1);
//QTableWidgetItem *item = new QTableWidgetItem(name);
//item->setFlags(item->flags() & ~Qt::ItemIsEditable);
//setItem(row, 0, item);
//setCellWidget(row, 1, editor);
//edit->setText(editor->);
auto vtype = editor->property("type");
auto type = vtype.toInt();
//CFG_T_UNDEFINED = 0,
if(type == 1) // CFG_T_DOUBLE,
{
auto vval = editor->property("value");
auto val = vval.toDouble();
edit->setText(QString::number(val));
}
else if(type == 2) // CFG_T_INT,
{
auto vval = editor->property("value");
auto val = vval.toInt();
edit->setText(QString::number(val));
}
else if(type == 3) // CFG_T_QSTRING,
{
auto vval = editor->property("value");
auto val = vval.toString();
edit->setText(val);
}
else if(type == 4) // CFG_T_ENUM,
{
auto vval = editor->property("value");
auto val = vval.toInt();
edit->setText(QString::number(val));
}
else if(type == 5) // CFG_T_BOOL
{
auto vval = editor->property("value");
auto val = vval.toBool();
edit->setText(val ? "true" : "false");
}
auto vsuffix = editor->property("suffix");
auto suffix = vsuffix.toString();
auto veditorDecimalsDouble = editor->property("editorDecimalsDouble");
auto editorDecimalsDouble = veditorDecimalsDouble.toDouble();
auto veditAsPercentage = editor->property("editAsPercentage");
auto editAsPercentage = veditAsPercentage.toBool();
res = true;
//resizeColumnToContents(0);
//resizeRowsToContents();
}
return res;
}
*/
void BmsSettings::on_pbCalibrateZeroPoint_clicked()
{
// setZeroCurrent to Terminal
//emit sendMessage("setZeroCurrent");
if ( mDieBieMS == nullptr ) {
return;
}
if ( !mDieBieMS->isPortConnected() ) {
QMessageBox::critical(this,
tr("Connection Error"),
tr("The ENNOID-BMS is not connected. Please connect it."));
return;
}
mDieBieMS->commands()->sendTerminalCmd("setZeroCurrent");
QTimer::singleShot(1000, [this](){
this->mDieBieMS->commands()->sendTerminalCmd("config_write");
});
}
/*
void BmsSettings::on_pbWriteIntoNonVolatileMemoryOfBsm_clicked()
{
}
void BmsSettings::on_pbWriteCurrentValuesIntoBsm_clicked()
{
}
*/
void BmsSettings::on_pbWriteToNonVolatileBmsMemory_clicked()
{
// Записать в энергонезависимую\nпамять BMS
// actionStoreBMScconf
mDieBieMS->commands()->storeBMSConfig();
}
void BmsSettings::on_pbWriteCurrentValuesToBms_clicked()
{
// Записать текущие значения в BMS
// actionWriteBMScconf
mDieBieMS->commands()->setBMSconf();
}
void BmsSettings::onSaveBMSConfToXmlFileClicked()
{
QString path;
path = QFileDialog::getSaveFileName(this,
tr("Choose where to save the motor configuration XML file"),
".",
tr("Xml files (*.xml)"));
if (path.isNull()) {
return;
}
if (!path.toLower().endsWith(".xml")) {
path += ".xml";
}
bool res = mDieBieMS->bmsConfig()->saveXml(path, "bmsConfiguration");
if (res) {
emit showStatusInfo("Saved motor configuration", true);
} else {
emit showMessageDialog(tr("Save motor configuration"),
tr("Could not save motor configuration:<BR>"
"%1").arg(mDieBieMS->bmsConfig()->xmlStatus()),
false, false);
}
}
void BmsSettings::onLoadBMSConfFromXmlFileClicked()
{
QString path;
path = QFileDialog::getOpenFileName(this,
tr("Choose motor configuration file to load"),
".",
tr("Xml files (*.xml)"));
if (path.isNull()) {
return;
}
bool res = mDieBieMS->bmsConfig()->loadXml(path, "bmsConfiguration");
if (res) {
emit showStatusInfo("Loaded motor configuration", true);
} else {
emit showMessageDialog(tr("Load motor configuration"),
tr("Could not load motor configuration:<BR>"
"%1").arg(mDieBieMS->bmsConfig()->xmlStatus()),
false, false);
}
}

89
bmssettings.h Normal file
View File

@@ -0,0 +1,89 @@
#ifndef BWINDOWWITHLEFTTABSBAR_H
#define BWINDOWWITHLEFTTABSBAR_H
// void PageMasterSettings
// Test: Cell groups in Series 10 -> 9 // Записать текущ. в БМС
#include <QWidget>
#include <QLineEdit>
#include "bmsinterface.h"
namespace Ui {
class BmsSettings;
}
class ParamTable;
class BmsSettings : public QFrame
{
Q_OBJECT
public:
explicit BmsSettings(QWidget *parent = nullptr);
~BmsSettings();
void clearControlls();
void prepareSettersFromGuiControlsToSettingsFile();
BMSInterface *bms() const;
void setDieBieMS(BMSInterface *dieBieMS);
//bool addParamRow(QLineEdit* edit, ConfigParams *params, QString paramName);
signals:
void sendMessage(QString message);
void showStatusInfo(QString info, bool isGood);
void showMessageDialog(const QString &title, const QString &msg, bool isGood, bool richText);
public slots:
void onLoadParams();
void onBmsconfUpdateRequested();
void onBmsconfUpdateRequestDefault();
void onBmsconfUpdated();
void onBmsconfStored();
private slots:
void on_pbCalibrateZeroPoint_clicked();
//void on_pbWriteIntoNonVolatileMemoryOfBsm_clicked();
//void on_pbWriteCurrentValuesIntoBsm_clicked();
void on_pbWriteToNonVolatileBmsMemory_clicked();
void on_pbWriteCurrentValuesToBms_clicked();
void onSaveBMSConfToXmlFileClicked();
void onLoadBMSConfFromXmlFileClicked();
private:
Ui::BmsSettings *ui;
BMSInterface *mDieBieMS = nullptr;
//ParamTable * pt; // = new ParamTable(this); //ui->masterStateTab;
};
#endif // BH
/*
<notUsedCurrentThreshold>
<longName>Not used current threshold</longName>
<type>1</type>
<transmittable>1</transmittable>
<description>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'DejaVu Sans'; ; font-weight:400; font-style:normal;&quot;&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;When the battery current goes below this "Not used battery current threshold" value, the "Not used timeout" timer starts.&amp;quot; &lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</description>
<cDefine>NOT_USED_CURRENT_THRESHOLD</cDefine>
<editorDecimalsDouble>2</editorDecimalsDouble>
<editorScale>1</editorScale>
<editAsPercentage>0</editAsPercentage>
<maxDouble>10</maxDouble>
<minDouble>0</minDouble>
<showDisplay>0</showDisplay>
<stepDouble>0.1</stepDouble>
<valDouble>0.5</valDouble>
<vTxDoubleScale>100000</vTxDoubleScale>
<suffix>A</suffix>
<vTx>9</vTx>
</notUsedCurrentThreshold>
*/

2201
bmssettings.ui Normal file

File diff suppressed because it is too large Load Diff

572
bmssettingswidget.cpp Normal file
View File

@@ -0,0 +1,572 @@
#include "bmssettingswidget.h"
#include "ui_bmssettingswidget.h"
#include "widgets/paramtable.h"
#include "widgets/parameditdouble.h"
#include "configparamsgetter.h"
#include <QIntValidator>
#include <QDoubleValidator>
BmsSettingsWidget::BmsSettingsWidget(QWidget *parent) :
QFrame(parent),
ui(new Ui::BmsSettingsWidget)
{
ui->setupUi(this);
ui->pbWriteToNonVolatileBmsMemory->hide();
ui->pbWriteCurrentValuesToBms->hide();
//ui->editSerialNumber
ui->editGroupsNumber->setValidator( new QIntValidator(-1000, 1000, this) );
ui->editCellsNumber->setValidator( new QIntValidator(-1000, 1000, this) );
ui->editCellsInParallelGroup->setValidator( new QIntValidator(1, 1000, this) );
ui->editBatteryCapacity->setValidator( new QIntValidator(0, 10000, this) );
ui->editMaxVoltageCharge->setValidator( new QIntValidator(-1000, 1000, this) );
ui->editMaxCircuitLoad->setValidator( new QIntValidator(-1000, 10000, this) );
ui->editMaxTemperature->setValidator( new QIntValidator(-1000, 1000, this) );
ui->editMinVoltageWhenWeTurnLoadOff->setValidator( new QDoubleValidator(-100, 100, 2, this) );
ui->editSuchVoltageWhenWeTurnLoadOn->setValidator( new QDoubleValidator(-100, 100, 2, this) );
ui->editSuchVoltageWhenWeTurnChargeOn->setValidator( new QDoubleValidator(-100, 100, 2, this) );
ui->editMaxVoltageWhenWeTurnChargeOff->setValidator( new QDoubleValidator(-100, 100, 2, this) );
ui->editVoltageBalancingStart->setValidator( new QDoubleValidator(-100, 100, 2, this) );
ui->editVoltageDifferenceThreshold->setValidator( new QDoubleValidator(-100, 100, 2, this) );
ui->editCellBalancingInterval->setValidator( new QIntValidator(-1000, 100000, this) );
//V
//ui->cboxUseToManageStorageDevice
//ui->cboxNormallyClosed
ui->editSocValueEdit->setValidator( new QDoubleValidator(-100, 100, 2, this) );
//ui->cboxTemperatureSettingsOver
ui->editTemperatureIfLessThenCloseOver->setValidator( new QIntValidator(-1000, 1000, this) );
ui->editTemperatureIfMoeeThenOpenOver->setValidator( new QIntValidator(-1000, 1000, this) );
//ui->cboxTemperatureSettingsUnder
ui->editTemperatureIfLessThenCloseUnder->setValidator( new QIntValidator(-1000, 1000, this) );
ui->editTemperatureIfMoeeThenOpenUnder->setValidator( new QIntValidator(-1000, 1000, this) );
ui->editPointZeroDetectorValue->setValidator( new QDoubleValidator(-1000, 1000, 2, this) );
// BTNS
//ui->pbCalibrateZeroPoint
//ui->pbWriteIntoNonVolatileMemoryOfBsm
//ui->pbWriteCurrentValuesIntoBsm
clearControlls();
}
BmsSettingsWidget::~BmsSettingsWidget()
{
delete ui;
}
//
void BmsSettingsWidget::onBmsconfUpdateRequested()
{
int a = 1;
}
void BmsSettingsWidget::onBmsconfUpdateRequestDefault()
{
int a = 1;
}
void BmsSettingsWidget::onBmsconfUpdated()
{
// ^ - 8 7 | 8 8 read bms conf
// After we hav red real BMS Config params from BMS we put all of them into the GUI elements
// Load default params from Bms to GUI Elements
//-> - 7 7 | 7 8 read default 8 is defaul - that was on the start
// Загрузить настройки\nпо умолчанию
//mDieBieMS->commands()->getBMSconfDefault();
onLoadParams();
}
void BmsSettingsWidget::onBmsconfStored()
{
int a = 1;
}
void BmsSettingsWidget::clearControlls()
{
ui->editGroupsNumber ->clear();
ui->editCellsNumber ->clear();
ui->editCellsInParallelGroup ->clear();
ui->editBatteryCapacity ->clear();
ui->editMaxVoltageCharge ->clear();
ui->editMaxCircuitLoad ->clear();
ui->editMaxTemperature ->clear();
ui->editMinVoltageWhenWeTurnLoadOff ->clear();
ui->editSuchVoltageWhenWeTurnLoadOn ->clear();
ui->editSuchVoltageWhenWeTurnChargeOn ->clear();
ui->editMaxVoltageWhenWeTurnChargeOff ->clear();
ui->editVoltageBalancingStart ->clear();
ui->editVoltageDifferenceThreshold ->clear();
ui->editCellBalancingInterval ->clear();
//1
ui->cboxUseToManageStorageDevice ->setChecked(false);
//2
ui->cboxNormallyClosed ->setChecked(false);
ui->editSocValueEdit ->clear();
//3
ui->cboxTemperatureSettingsOver ->setChecked(false);
ui->editTemperatureIfLessThenCloseOver ->clear();
ui->editTemperatureIfMoeeThenOpenOver ->clear();
//4
ui->cboxTemperatureSettingsUnder ->setChecked(false);
ui->editTemperatureIfLessThenCloseUnder ->clear();
ui->editTemperatureIfMoeeThenOpenUnder ->clear();
// 0
ui->editPointZeroDetectorValue ->clear();
}
void BmsSettingsWidget::prepareSettersFromGuiControlsToSettingsFile()
{
/// Setters
#define QS QString
auto cfg = mDieBieMS->bmsConfig();
/// Конфигурация *
connect(ui->editGroupsNumber, &QLineEdit::textChanged, this, [this, cfg]()
{ QS s =ui->editGroupsNumber->text(); QVariant val((s.isEmpty() || s=="-") ? 0 : s.toInt()); cfg->setParamValue("cellMonitorICCount", val); });
connect(ui->editCellsNumber, &QLineEdit::textChanged, this, [this, cfg]()
{
QS s =ui->editCellsNumber->text();
QVariant val((s.isEmpty() || s=="-") ? 0 : s.toInt());
cfg->setParamValue("noOfCellsSeries", val);
});
// Конфигурация *
//addParamRow(ui->editGroupsNumber , mDieBieMS->bmsConfig(), "cellMonitorICCount");
//addParamRow(ui->editCellsNumber , mDieBieMS->bmsConfig(), "noOfCellsSeries");
/// SOC *
connect(ui->editCellsInParallelGroup, &QLineEdit::textChanged, this, [this, cfg]()
{ QS s =ui->editCellsInParallelGroup->text(); QVariant val((s.isEmpty() || s=="-") ? 0 : s.toInt()); cfg->setParamValue("noOfCellsParallel", val); });
connect(ui->editBatteryCapacity, &QLineEdit::textChanged, this, [this, cfg]()
{ QS s =ui->editBatteryCapacity->text(); QVariant val((s.isEmpty() || s=="-") ? 0 : s.toInt()); cfg->setParamValue("batteryCapacity", val); });
// SOC *
//addParamRow(ui->editCellsInParallelGroup , mDieBieMS->bmsConfig(), "noOfCellsParallel");
//addParamRow(ui->editBatteryCapacity , mDieBieMS->bmsConfig(), "batteryCapacity");
/// Ограничения *
connect(ui->editMaxVoltageCharge, &QLineEdit::textChanged, this, [this, cfg]()
{ QS s =ui->editMaxVoltageCharge->text(); QVariant val((s.isEmpty() || s=="-") ? 0 : s.toInt()); cfg->setParamValue("maxAllowedCurrent", val); });
connect(ui->editMaxCircuitLoad, &QLineEdit::textChanged, this, [this, cfg]()
{ QS s =ui->editMaxCircuitLoad->text(); QVariant val((s.isEmpty() || s=="-") ? 0 : s.toInt()); cfg->setParamValue("maxMismatchThreshold", val); });
connect(ui->editMaxTemperature, &QLineEdit::textChanged, this, [this, cfg]()
{ QS s =ui->editMaxTemperature->text(); QVariant val((s.isEmpty() || s=="-") ? 0 : s.toInt()); cfg->setParamValue("allowedTempBattChargingMax", val); });
// Ограничения *
//addParamRow(ui->editMaxVoltageCharge , mDieBieMS->bmsConfig(), "maxAllowedCurrent");
//addParamRow(ui->editMaxCircuitLoad , mDieBieMS->bmsConfig(), "maxMismatchThreshold");
//addParamRow(ui->editMaxTemperature , mDieBieMS->bmsConfig(), "allowedTempBattChargingMax");
/// Конфигурация ячеек *
connect(ui->editMinVoltageWhenWeTurnLoadOff, &QLineEdit::textChanged, this, [this, cfg]()
{ QS s =ui->editMinVoltageWhenWeTurnLoadOff->text(); QVariant val((s.isEmpty() || s=="-") ? 0 : s.toDouble()); cfg->setParamValue("cellHardUnderVoltage", val); });
connect(ui->editSuchVoltageWhenWeTurnLoadOn, &QLineEdit::textChanged, this, [this, cfg]()
{ QS s =ui->editSuchVoltageWhenWeTurnLoadOn->text(); QVariant val((s.isEmpty() || s=="-") ? 0 : s.toDouble()); cfg->setParamValue("cellHardOverVoltage", val); });
connect(ui->editSuchVoltageWhenWeTurnChargeOn, &QLineEdit::textChanged, this, [this, cfg]()
{ QS s =ui->editSuchVoltageWhenWeTurnChargeOn->text(); QVariant val((s.isEmpty() || s=="-") ? 0 : s.toDouble()); cfg->setParamValue("cellLCSoftUnderVoltage", val); });
connect(ui->editMaxVoltageWhenWeTurnChargeOff, &QLineEdit::textChanged, this, [this, cfg]()
{ QS s =ui->editMaxVoltageWhenWeTurnChargeOff->text(); QVariant val((s.isEmpty() || s=="-") ? 0 : s.toDouble()); cfg->setParamValue("cellSoftOverVoltage", val); });
// Конфигурация ячеек *
//addParamRow(ui->editMinVoltageWhenWeTurnLoadOff , mDieBieMS->bmsConfig(), "cellHardUnderVoltage");
//addParamRow(ui->editSuchVoltageWhenWeTurnLoadOn , mDieBieMS->bmsConfig(), "cellHardOverVoltage");
//addParamRow(ui->editSuchVoltageWhenWeTurnChargeOn , mDieBieMS->bmsConfig(), "cellLCSoftUnderVoltage");
//addParamRow(ui->editMaxVoltageWhenWeTurnChargeOff , mDieBieMS->bmsConfig(), "cellSoftOverVoltage");
/// Конфигурация балансировки * // ui->balancingTab
connect(ui->editVoltageBalancingStart, &QLineEdit::textChanged, this, [this, cfg]()
{ QS s =ui->editVoltageBalancingStart->text(); QVariant val((s.isEmpty() || s=="-") ? 0 : s.toDouble()); cfg->setParamValue("cellBalanceStart", val); });
connect(ui->editVoltageDifferenceThreshold, &QLineEdit::textChanged, this, [this, cfg]()
{ QS s =ui->editVoltageDifferenceThreshold->text(); QVariant val((s.isEmpty() || s=="-") ? 0 : s.toDouble()); cfg->setParamValue("cellBalanceDifferenceThreshold", val); });
connect(ui->editCellBalancingInterval, &QLineEdit::textChanged, this, [this, cfg]()
{ QS s =ui->editCellBalancingInterval->text(); QVariant val((s.isEmpty() || s=="-") ? 0 : s.toInt()); cfg->setParamValue("cellBalanceUpdateInterval", val); });
// Конфигурация балансировки * // ui->balancingTab
//addParamRow(ui->editVoltageBalancingStart , mDieBieMS->bmsConfig(), "cellBalanceStart");
//addParamRow(ui->editVoltageDifferenceThreshold , mDieBieMS->bmsConfig(), "cellBalanceDifferenceThreshold");
//addParamRow(ui->editCellBalancingInterval , mDieBieMS->bmsConfig(), "cellBalanceUpdateInterval");
/// Калибровать Нулевую точку * // ui->signalsTab
connect(ui->editPointZeroDetectorValue, &QLineEdit::textChanged, this, [this, cfg]()
{
QS s =ui->editPointZeroDetectorValue->text();
QVariant val((s.isEmpty() || s=="-") ? 0 : s.toDouble());
cfg->setParamValue("shuntLCFactor", val);
});
// Нулевая точка * // ui->signalsTab
//addParamRow(ui->editPointZeroDetectorValue , mDieBieMS->bmsConfig(), "shuntLCFactor");
}
void BmsSettingsWidget::onLoadParams()
{
////
auto cfg = mDieBieMS->bmsConfig();
// Конфигурация *
addParamRow(ui->editGroupsNumber , cfg, "cellMonitorICCount");
addParamRow(ui->editCellsNumber , cfg, "noOfCellsSeries");
// SOC *
addParamRow(ui->editCellsInParallelGroup , cfg, "noOfCellsParallel");
addParamRow(ui->editBatteryCapacity , cfg, "batteryCapacity");
// Ограничения *
addParamRow(ui->editMaxVoltageCharge , cfg, "maxAllowedCurrent");
addParamRow(ui->editMaxCircuitLoad , cfg, "maxMismatchThreshold");
addParamRow(ui->editMaxTemperature , cfg, "allowedTempBattChargingMax");
// Конфигурация ячеек *
addParamRow(ui->editMinVoltageWhenWeTurnLoadOff , cfg, "cellHardUnderVoltage");
addParamRow(ui->editSuchVoltageWhenWeTurnLoadOn , cfg, "cellHardOverVoltage");
addParamRow(ui->editSuchVoltageWhenWeTurnChargeOn , cfg, "cellLCSoftUnderVoltage");
addParamRow(ui->editMaxVoltageWhenWeTurnChargeOff , cfg, "cellSoftOverVoltage");
// Конфигурация балансировки *
// ui->balancingTab
addParamRow(ui->editVoltageBalancingStart , cfg, "cellBalanceStart");
addParamRow(ui->editVoltageDifferenceThreshold , cfg, "cellBalanceDifferenceThreshold");
addParamRow(ui->editCellBalancingInterval , cfg, "cellBalanceUpdateInterval");
// Нулевая точка *
// ui->signalsTab
addParamRow(ui->editPointZeroDetectorValue , cfg, "shuntLCFactor");
////*/
// Turn ON Setters only ONCE AFTER we load all data from file and when First connection is Ready
if(mDoConnectGettersOnStart)
{
// Выполнили все коннекты при первом старте и более не вызываем
qDebug() << "::prepareSettersFromGuiControlsToSettingsFile();";
prepareSettersFromGuiControlsToSettingsFile();
mDoConnectGettersOnStart = false;
}
// Turn ON Setters only ONCE AFTER we load all data from file and when First connection is Ready
/*
<cellBalanceStart>
<longName>Cell balance start voltage</longName>
cellMonitorICCount ->
noOfCellsSeries ->
--
noOfCellsParallel ->
<batteryCapacity>
<longName>Battery capacity total</longName>
--
<maxMismatchThreshold>
<longName>Mismatch threshold</longName>
<maxAllowedCurrent>
<longName>Max allowed current</longName>
<allowedTempBattChargingMax>
<longName>Battery temperature charging max</longName>
--
<cellHardUnderVoltage>
<longName>Cell hard undervoltage</longName>
<cellHardOverVoltage>
<longName>Cell hard overvoltage</longName>
<cellLCSoftUnderVoltage>
<longName>Cell low current soft undervoltage</longName>
<cellSoftOverVoltage>
<longName>Cell soft overvoltage</longName>
--
*/
/*
#if 0
ConfigParam *p = mDieBieMS->infoConfig()->getParam("master_setting_description");
if (p != 0) {
ui->textEdit->setHtml(p->description);
} else {
ui->textEdit->setText("Master Setting Description not found.");
}
#endif
ConfigParams *const conf = mDieBieMS->bmsConfig();
///ParamTable *const pt = new ParamTable(this); //ui->masterStateTab;
///pt->addRowSeparator(tr("Current"));
// Нулевая точка
addParamRow(ui->editPointZeroDetectorValue , mDieBieMS->bmsConfig(), "shuntLCFactor");
//pt->addParamRow(conf, "shuntLCFactor");
////pt->addParamRow(conf, "shuntOffset");
///pt->addRowSeparator(tr("Pack configuration"));
#if 0 // disable
pt->addParamRow(conf, "cellMonitorICType");
#endif
// Конфигурация
addParamRow(ui->editGroupsNumber , mDieBieMS->bmsConfig(), "cellMonitorICCount");
addParamRow(ui->editCellsNumber , mDieBieMS->bmsConfig(), "noOfCellsSeries");
//pt->addParamRow(conf, "cellMonitorICCount");
///pt->addParamRow(conf, "noOfParallelModules");
//pt->addParamRow(conf, "noOfCellsSeries");
///pt->addRowSeparator(tr("SOC - Pack capacity"));
//pt->addParamRow(conf, "noOfCellsParallel");
//pt->addParamRow(conf, "batteryCapacity");
// SOC
addParamRow(ui->editCellsInParallelGroup , mDieBieMS->bmsConfig(), "noOfCellsParallel");
addParamRow(ui->editBatteryCapacity , mDieBieMS->bmsConfig(), "batteryCapacity");
///pt->addRowSeparator(tr("Cell specifications"));
//pt->addParamRow(conf, "cellHardUnderVoltage");
//pt->addParamRow(conf, "cellHardOverVoltage");
//pt->addParamRow(conf, "cellLCSoftUnderVoltage");
//pt->addParamRow(conf, "cellSoftOverVoltage");
// Конфигурация ячеек
addParamRow(ui->editMinVoltageWhenWeTurnLoadOff , mDieBieMS->bmsConfig(), "cellHardUnderVoltage");
addParamRow(ui->editSuchVoltageWhenWeTurnLoadOn , mDieBieMS->bmsConfig(), "cellHardOverVoltage");
addParamRow(ui->editSuchVoltageWhenWeTurnChargeOn , mDieBieMS->bmsConfig(), "cellLCSoftUnderVoltage");
addParamRow(ui->editMaxVoltageWhenWeTurnChargeOff , mDieBieMS->bmsConfig(), "cellSoftOverVoltage");
///pt->addRowSeparator(tr("Balancing configuration"));
//pt->addParamRow(conf, "cellBalanceStart");
//pt->addParamRow(conf, "cellBalanceDifferenceThreshold");
//pt->addParamRow(conf, "cellBalanceUpdateInterval");
// Конфигурация балансировки
// ui->balancingTab
addParamRow(ui->editVoltageBalancingStart , mDieBieMS->bmsConfig(), "cellBalanceStart");
addParamRow(ui->editVoltageDifferenceThreshold , mDieBieMS->bmsConfig(), "cellBalanceDifferenceThreshold");
addParamRow(ui->editCellBalancingInterval , mDieBieMS->bmsConfig(), "cellBalanceUpdateInterval");
///pt->addRowSeparator(tr("SoC general"));
#if 0 // disable
pt->addParamRow(conf, "stateOfChargeStoreInterval");
pt->addParamRow(conf, "timeoutChargeCompleted");
pt->addParamRow(conf, "timeoutChargingCompletedMinimalMismatch");
#endif
///pt->addParamRow(conf, "minimalPrechargePercentage"); // from pagemastersettings.cpp
///pt->addParamRow(conf, "timeoutLCPreCharge"); // from pagemastersettings.cpp
///pt->addRowSeparator(tr("Limits"));
//pt->addParamRow(conf, "maxMismatchThreshold");
//pt->addParamRow(conf, "maxAllowedCurrent");
// Ограничения
addParamRow(ui->editMaxVoltageCharge , mDieBieMS->bmsConfig(), "maxAllowedCurrent");
addParamRow(ui->editMaxCircuitLoad , mDieBieMS->bmsConfig(), "maxMismatchThreshold");
addParamRow(ui->editMaxTemperature , mDieBieMS->bmsConfig(), "allowedTempBattChargingMax");
#if 0
pt->addParamRow(conf, "allowedTempBattDischargingMax");
pt->addParamRow(conf, "allowedTempBattDischargingMin");
#endif
//pt->addParamRow(conf, "allowedTempBattChargingMax");
///pt->addParamRow(conf, "allowedTempBattChargingMin");
#if 0 // disable
///pt->addRowSeparator(tr("NTC specifications battery"));
pt->addParamRow(mDieBieMS->bmsConfig(), "noOfTempSensorPerModule");
pt->addParamRow(mDieBieMS->bmsConfig(), "NTCLTC25Deg");
///pt->addRowSeparator(tr("NTC specifications expansion Board"));
pt->addParamRow(mDieBieMS->bmsConfig(), "noOfExpansionBoard");
pt->addParamRow(mDieBieMS->bmsConfig(), "noOfTempSensorPerExpansionBoard");
#endif
*/
}
void BmsSettingsWidget::onPortConnectedChanged()
{
if(mDieBieMS)
{
mIsBmsConnected = mDieBieMS->isPortConnected();
}
}
BMSInterface *BmsSettingsWidget::bms() const
{
return mDieBieMS;
}
void BmsSettingsWidget::setDieBieMS(BMSInterface *dieBieMS)
{ // void PageMasterSettings::setDieBieMS(BMSInterface *dieBieMS)
mDieBieMS = dieBieMS;
//pt = new ParamTable(this); //ui->masterStateTab;
connect(mDieBieMS, &BMSInterface::portConnectedChanged, this, &BmsSettingsWidget::onPortConnectedChanged);
//onLoadParams();
/*
if (mDieBieMS) {
#if 0
ConfigParam *p = mDieBieMS->infoConfig()->getParam("master_setting_description");
if (p != 0) {
ui->textEdit->setHtml(p->description);
} else {
ui->textEdit->setText("Master Setting Description not found.");
}
#endif
ConfigParams *const conf = mDieBieMS->bmsConfig();
ParamTable *const pt = new ParamTable(this); //ui->masterStateTab;
pt->addRowSeparator(tr("Current"));
pt->addParamRow(conf, "shuntLCFactor");
pt->addParamRow(conf, "shuntOffset");
pt->addRowSeparator(tr("Pack configuration"));
#if 0 // disable
pt->addParamRow(conf, "cellMonitorICType");
#endif
pt->addParamRow(conf, "cellMonitorICCount");
pt->addParamRow(conf, "noOfParallelModules");
pt->addParamRow(conf, "noOfCellsSeries");
pt->addRowSeparator(tr("SOC - Pack capacity"));
pt->addParamRow(conf, "noOfCellsParallel");
pt->addParamRow(conf, "batteryCapacity");
pt->addRowSeparator(tr("Cell specifications"));
pt->addParamRow(conf, "cellHardUnderVoltage");
pt->addParamRow(conf, "cellHardOverVoltage");
pt->addParamRow(conf, "cellLCSoftUnderVoltage");
pt->addParamRow(conf, "cellSoftOverVoltage");
pt->addRowSeparator(tr("Balancing configuration"));
pt->addParamRow(conf, "cellBalanceStart");
pt->addParamRow(conf, "cellBalanceDifferenceThreshold");
pt->addParamRow(conf, "cellBalanceUpdateInterval");
pt->addRowSeparator(tr("SoC general"));
#if 0 // disable
pt->addParamRow(conf, "stateOfChargeStoreInterval");
pt->addParamRow(conf, "timeoutChargeCompleted");
pt->addParamRow(conf, "timeoutChargingCompletedMinimalMismatch");
#endif
pt->addParamRow(conf, "minimalPrechargePercentage"); // from pagemastersettings.cpp
pt->addParamRow(conf, "timeoutLCPreCharge"); // from pagemastersettings.cpp
pt->addRowSeparator(tr("Limits"));
pt->addParamRow(conf, "maxMismatchThreshold");
pt->addParamRow(conf, "maxAllowedCurrent");
#if 0
pt->addParamRow(conf, "allowedTempBattDischargingMax");
pt->addParamRow(conf, "allowedTempBattDischargingMin");
#endif
pt->addParamRow(conf, "allowedTempBattChargingMax");
pt->addParamRow(conf, "allowedTempBattChargingMin");
#if 0 // disable
pt->addRowSeparator(tr("NTC specifications battery"));
pt->addParamRow(mDieBieMS->bmsConfig(), "noOfTempSensorPerModule");
pt->addParamRow(mDieBieMS->bmsConfig(), "NTCLTC25Deg");
pt->addRowSeparator(tr("NTC specifications expansion Board"));
pt->addParamRow(mDieBieMS->bmsConfig(), "noOfExpansionBoard");
pt->addParamRow(mDieBieMS->bmsConfig(), "noOfTempSensorPerExpansionBoard");
#endif
}
*/
}
/*
bool BmsSettingsWidget::addParamRow(QLineEdit* edit, ConfigParams *params, QString paramName)
{
bool res = false;
QWidget *editor = params->getEditor(paramName);
QString name = params->getLongName(paramName);
if (editor) {
//int row = rowCount();
//setRowCount(row + 1);
//QTableWidgetItem *item = new QTableWidgetItem(name);
//item->setFlags(item->flags() & ~Qt::ItemIsEditable);
//setItem(row, 0, item);
//setCellWidget(row, 1, editor);
//edit->setText(editor->);
auto vtype = editor->property("type");
auto type = vtype.toInt();
//CFG_T_UNDEFINED = 0,
if(type == 1) // CFG_T_DOUBLE,
{
auto vval = editor->property("value");
auto val = vval.toDouble();
edit->setText(QString::number(val));
}
else if(type == 2) // CFG_T_INT,
{
auto vval = editor->property("value");
auto val = vval.toInt();
edit->setText(QString::number(val));
}
else if(type == 3) // CFG_T_QSTRING,
{
auto vval = editor->property("value");
auto val = vval.toString();
edit->setText(val);
}
else if(type == 4) // CFG_T_ENUM,
{
auto vval = editor->property("value");
auto val = vval.toInt();
edit->setText(QString::number(val));
}
else if(type == 5) // CFG_T_BOOL
{
auto vval = editor->property("value");
auto val = vval.toBool();
edit->setText(val ? "true" : "false");
}
auto vsuffix = editor->property("suffix");
auto suffix = vsuffix.toString();
auto veditorDecimalsDouble = editor->property("editorDecimalsDouble");
auto editorDecimalsDouble = veditorDecimalsDouble.toDouble();
auto veditAsPercentage = editor->property("editAsPercentage");
auto editAsPercentage = veditAsPercentage.toBool();
res = true;
//resizeColumnToContents(0);
//resizeRowsToContents();
}
return res;
}
*/
void BmsSettingsWidget::on_pbCalibrateZeroPoint_clicked()
{
// setZeroCurrent to Terminal
emit sendMessage("setZeroCurrent");
}
/*
void BmsSettingsWidget::on_pbWriteIntoNonVolatileMemoryOfBsm_clicked()
{
}
void BmsSettingsWidget::on_pbWriteCurrentValuesIntoBsm_clicked()
{
}
*/
void BmsSettingsWidget::on_pbWriteToNonVolatileBmsMemory_clicked()
{
// Записать в энергонезависимую\nпамять BMS
// actionStoreBMScconf
mDieBieMS->commands()->storeBMSConfig();
}
void BmsSettingsWidget::on_pbWriteCurrentValuesToBms_clicked()
{
// Записать текущие значения в BMS
// actionWriteBMScconf
mDieBieMS->commands()->setBMSconf();
}

62
bmssettingswidget.h Normal file
View File

@@ -0,0 +1,62 @@
#ifndef BMSSETTINGSWIDGET_H
#define BMSSETTINGSWIDGET_H
// void PageMasterSettings
// Test: Cell groups in Series 10 -> 9 // Записать текущ. в БМС
#include <QWidget>
#include <QLineEdit>
#include "bmsinterface.h"
namespace Ui {
class BmsSettingsWidget;
}
class ParamTable;
class BmsSettingsWidget : public QFrame
{
Q_OBJECT
public:
explicit BmsSettingsWidget(QWidget *parent = nullptr);
~BmsSettingsWidget();
void clearControlls();
void prepareSettersFromGuiControlsToSettingsFile();
BMSInterface *bms() const;
void setDieBieMS(BMSInterface *dieBieMS);
//bool addParamRow(QLineEdit* edit, ConfigParams *params, QString paramName);
signals:
void sendMessage(QString message);
public slots:
void onLoadParams();
void onBmsconfUpdateRequested();
void onBmsconfUpdateRequestDefault();
void onBmsconfUpdated();
void onBmsconfStored();
void onPortConnectedChanged();
private slots:
void on_pbCalibrateZeroPoint_clicked();
//void on_pbWriteIntoNonVolatileMemoryOfBsm_clicked();
//void on_pbWriteCurrentValuesIntoBsm_clicked();
void on_pbWriteToNonVolatileBmsMemory_clicked();
void on_pbWriteCurrentValuesToBms_clicked();
private:
Ui::BmsSettingsWidget *ui;
BMSInterface *mDieBieMS;
bool mIsBmsConnected = false;
bool mDoConnectGettersOnStart = true;
//ParamTable * pt; // = new ParamTable(this); //ui->masterStateTab;
};
#endif // BMSSETTINGSWIDGET_H

2135
bmssettingswidget.ui Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,116 @@
#include "cellsmonitorcheckboxdelegate.h"
#include <QCheckBox>
#include <QApplication>
#include <QPainter>
CheckBoxDelegate::CheckBoxDelegate(QObject *parent)
:QStyledItemDelegate (parent)
{
m_circleSize = 15;
}
QWidget *CheckBoxDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
Q_UNUSED(option)
Q_UNUSED(index)
//Cоздаем checkbox editor
QCheckBox *editor = new QCheckBox(parent);
return editor;
}
void CheckBoxDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const
{
//Установливаем выбрано/не выбрано
QCheckBox *cb = qobject_cast<QCheckBox *>(editor);
cb->setStyleSheet("QCheckBox::indicator {\n width: 20px;\n height: " + QString::number( m_circleSize ) + "px;\n border: 1px solid grey;\n background: white;\n border-radius:3px;\n} \n\nQCheckBox::indicator:checked {\n border-color: green;\n background: green;\n color:white;\n /*image: url(:/res/checkbox_unchecked_hover.png);*/\n} \n\n\n\nQCheckBox {\n font-weight:bold;\n font-size: 14px;\n}");
cb->setChecked(index.data().toBool());
}
void CheckBoxDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const
{
//Записываем данные в модель
QCheckBox *cb = static_cast<QCheckBox *>(editor);
int value = (cb->checkState()==Qt::Checked)? 1 : 0;
model->setData(index, value, Qt::EditRole);
}
void CheckBoxDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
Q_UNUSED(index);
QStyleOptionButton checkboxstyle;
QRect checkbox_rect = QApplication::style()->subElementRect(QStyle::SE_CheckBoxIndicator, &checkboxstyle);
//Центрирование
checkboxstyle.rect = option.rect;
checkboxstyle.rect.setLeft(option.rect.x() +
option.rect.width()/2 - checkbox_rect.width()/2);
editor->setGeometry(checkboxstyle.rect);
}
void CheckBoxDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
int row = index.row();
int col = index.column();
painter->save();
//Получаем данные
auto data = index.model()->data(index, Qt::DisplayRole);
//Создаем стиль CheckBox
QStyleOptionButton checkboxstyle;
//Выбрано или не выбрано
bool isChecked = data.toBool();
if(data.toBool())
checkboxstyle.state = QStyle::State_On|QStyle::State_Enabled;
else
checkboxstyle.state = QStyle::State_Off|QStyle::State_Enabled;
if(!index.data().isNull())
{
QRect checkbox_rect = QApplication::style()->subElementRect(QStyle::SE_CheckBoxIndicator, &checkboxstyle);
//Центрирование
checkboxstyle.rect = option.rect;
checkboxstyle.rect.setLeft(option.rect.x() +
option.rect.width()/2 - checkbox_rect.width()/2);
RectangleClass r;
int centerX = option.rect.width()/2 - m_circleSize/2;
int centerY = option.rect.height()/2 - m_circleSize/2;
r.draw
(
painter,
QRectF(
option.rect.x() + centerX, //+mx,
option.rect.y() + centerY, //+my,
m_circleSize,
m_circleSize
),
isChecked ? QColor("#009352") // green
: QColor("#dfe0eb"),
11
);
}
else {
RectClass r;
QRectF rectT( option.rect );
r.draw
(
painter,
rectT
);
}
painter->restore();
}

View File

@@ -0,0 +1,64 @@
#ifndef CHECKBOXDELEGATE_H
#define CHECKBOXDELEGATE_H
#include <QStyledItemDelegate>
#include <QPainter>
class CheckBoxDelegate : public QStyledItemDelegate
{
Q_OBJECT
public:
CheckBoxDelegate(QObject *parent = nullptr);
public:
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const;
QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const;
void setEditorData(QWidget *editor, const QModelIndex &index) const;
void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const;
void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const;
class RectClass
{
public:
void draw(QPainter *painter, const QRectF &rectL, QColor colorL = QColor("#FFFFFF"), int radiusL = 0) const
{
painter->save();
// Rect
QPen penw(colorL);
painter->setPen(penw);
painter->drawRect(rectL);
painter->restore();
}
};
class RectangleClass
{
public:
//void draw(QPainter *painter, const QRectF &rect, QColor color = QColor("#FFFFFF"), int radius = 0) const;
//void RectangleClass::draw(QPainter *painter, const QRectF &rect, QColor color, int radius) const
void draw(QPainter *painter, const QRectF &rect, QColor color = QColor("#FFFFFF"), int radius = 0)
{
painter->save();
QPainterPath path;
path.addRoundedRect(rect, radius, radius);
QPen pen(color);
painter->setPen(pen);
painter->fillPath(path, color);
painter->drawPath(path);
painter->restore();
}
};
private:
int m_circleSize;
};
#endif // CHECKBOXDELEGATE_H

View File

@@ -0,0 +1,104 @@
#include "cellsmonitorlabelvoltagedelegate.h"
#include <QCheckBox>
#include <QApplication>
#include <QPainter>
#include <QLineEdit>
LabelVoltageDelegate::LabelVoltageDelegate(QObject *parent)
: QStyledItemDelegate(parent) //: QItemDelegate(parent)
{
}
QWidget *LabelVoltageDelegate::createEditor(QWidget *parent,
const QStyleOptionViewItem & option,
const QModelIndex & index ) const
{
QLineEdit *editor = new QLineEdit(parent);
return editor;
}
void LabelVoltageDelegate::setEditorData(QWidget *editor,
const QModelIndex &index) const
{
QString value = index.model()->data(index, Qt::EditRole).toString();
QLineEdit *lineEdit = static_cast<QLineEdit*>(editor);
lineEdit->setText(value);
}
void LabelVoltageDelegate::setModelData(QWidget *editor, QAbstractItemModel *model,
const QModelIndex &index) const
{
QLineEdit *lineEdit = static_cast<QLineEdit*>(editor);
QString value = lineEdit->text();
model->setData(index, value, Qt::EditRole);
}
void LabelVoltageDelegate::updateEditorGeometry(QWidget *editor,
const QStyleOptionViewItem &option, const QModelIndex &/* index */) const
{
editor->setGeometry(option.rect);
}
void LabelVoltageDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
//Получаем данные
auto data = index.model()->data(index, Qt::DisplayRole).toString();
//Создаем стиль CheckBox
QStyleOptionButton checkboxstyle;
//Выбрано или не выбрано
QRect checkbox_rect = QApplication::style()->subElementRect(QStyle::SE_CheckBoxIndicator, &checkboxstyle);
//Центрирование
checkboxstyle.rect = option.rect;
checkboxstyle.rect.setLeft(option.rect.x() +
option.rect.width()/2 - checkbox_rect.width()/2);
painter->save();
if(!data.isEmpty())
{
LineWithText r;
int centerX = option.rect.width()/2 - m_circleSize/2;
int centerY = option.rect.height()/2 - m_circleSize/2;
QRectF rectL(
option.rect.x(),
option.rect.y() + option.rect.height()/2 ,
option.rect.width(),
0
);
QRectF rectT( option.rect );
//QString res = QString::number(data,'f',1);
r.draw
(
painter,
rectL,
//QColor("#009352"), // green
QColor("#bfc5d7"),
11,
rectT,
QString("%1 V").arg(data)
);
}
else {
RectClass r;
QRectF rectT( option.rect );
r.draw
(
painter,
rectT
);
}
painter->restore();
}

View File

@@ -0,0 +1,75 @@
#ifndef LABELVOLTAGEDELEGATE_H
#define LABELVOLTAGEDELEGATE_H
#include <QStyledItemDelegate>
#include <QPainter>
class LabelVoltageDelegate : public QStyledItemDelegate
{
Q_OBJECT
public:
LabelVoltageDelegate(QObject *parent = nullptr);
public:
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const;
QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const;
void setEditorData(QWidget *editor, const QModelIndex &index) const;
void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const;
void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const;
private:
int m_circleSize;
class RectClass
{
public:
void draw(QPainter *painter, const QRectF &rectL, QColor colorL = QColor("#FFFFFF"), int radiusL = 0) const
{
painter->save();
// Rect
QPen penw(colorL);
painter->setPen(penw);
painter->drawRect(rectL);
painter->restore();
}
};
class LineWithText
{
public:
void draw(QPainter *painter, const QRectF &rectL, QColor colorL = QColor("#FFFFFF"), int radiusL = 0,
const QRectF rectT=QRectF(0,0,0,0), QString text = "", QColor colorT = QColor("#000000"), QFont fontT = QFont("Arial", 13, QFont::Bold) , QColor colorBackground = QColor("#FFFFFF")) const
//;
//void LineWithText::draw (QPainter *painter, const QRectF &rectL, QColor colorL, int radiusL,
// const QRectF rectT, QString text, QColor colorT, QFont fontT ) const
{
painter->save();
// Line
QPen pen(colorL);
pen.setStyle(Qt::DotLine);
painter->setPen(pen);
painter->drawLine(rectL.left(), rectL.top(), rectL.right()*1.15, rectL.bottom());
// Rect
QPen penw(colorBackground);
painter->setPen(penw);
QFontMetrics fm(fontT);
double width = 0.0;
//width = fm.horizontalAdvance(text);
int width1 = fm.lineWidth();
int len = ((rectL.right()/2)-(width/2));
painter->drawRect(rectL.left()+27, rectL.top(), width*1.2, rectL.bottom());
// Text
painter->setFont(fontT);
painter->setPen(QPen(colorT));
painter->drawText(rectT, Qt::AlignCenter, text);
painter->restore();
}
};
};
#endif // LABELVOLTAGEDELEGATE_H

458
cellsmonitorpage.cpp Normal file
View File

@@ -0,0 +1,458 @@
#include "cellsmonitorpage.h"
#include "ui_cellsmonitorpage.h"
#include <math.h>
#include <QStandardItemModel>
#include "cellsmonitorcheckboxdelegate.h"
#include "cellsmonitorlabelvoltagedelegate.h"
CellsMonitorPage::CellsMonitorPage(QWidget *parent) :
QFrame(parent),
ui(new Ui::CellsMonitorPage)
{
ui->setupUi(this);
hideTestUi();
m_tablesNumber = 3;
//m_cellsNumber = 26;
m_cellsNumber = mCellVoltageArray.size();
initTableHeader();
}
CellsMonitorPage::~CellsMonitorPage()
{
delete ui;
}
void CellsMonitorPage::hideTestUi()
{
ui->lbCols->hide();
ui->lbCells->hide();
ui->editCols->hide();
ui->editCells->hide();
ui->pbTestOne->hide();
ui->pbTestTwo->hide();
}
BMSInterface *CellsMonitorPage::bms() const
{
return mDieBieMS;
}
void CellsMonitorPage::setDieBieMS(BMSInterface *dieBieMS)
{
mDieBieMS = dieBieMS;
if (mDieBieMS) {
//connect(mDieBieMS->commands(), &Commands::valuesReceived,this, &CellsMonitorPage::valuesReceived);
connect(mDieBieMS->commands(), &Commands::cellsReceived,this, &CellsMonitorPage::cellsReceived);
}
}
void CellsMonitorPage::clearTables()
{
ui->tableViewLeft->reset();
ui->tableViewMiddle->reset();
ui->tableViewRight->reset();
}
void CellsMonitorPage::initTableHeader()
{
initTableView(m_tablesNumber, true);
}
void CellsMonitorPage::initTableView(QTableView* tableView, bool doInitForCellVoltagArray)
{
//Создаем модель и представление
QStandardItemModel *model = new QStandardItemModel(MODEL_ROWS_IN_ONE_TABLE, MODEL_COLUMN_IN_ONE_TABLE);
int rowWeight = 40;
//Заолняем Заголовок
QStringList hedarenamesNumVolBalancing = QStringList()
<< tr("")
<< tr(" Напряжение ")
<< tr(" Балансировка");
model->setHeaderData(ColNumber, Qt::Horizontal, QObject::tr(hedarenamesNumVolBalancing[ColNumber].toUtf8()));
model->setHeaderData(ColVoltage, Qt::Horizontal, QObject::tr(hedarenamesNumVolBalancing[ColVoltage].toUtf8()));
model->setHeaderData(ColBalancing, Qt::Horizontal, QObject::tr(hedarenamesNumVolBalancing[ColBalancing].toUtf8()));
tableView->verticalHeader()->setDefaultSectionSize(rowWeight);
int tableViewIndexStart = 0;
int tableviewIndexEnd = 0;
int left = 0;
int right = 0;
int index = 0;
if(m_tablesNumber == TwoTables)
{
if(ui->tableViewLeft == tableView)
index = FirstSubTable;
if(ui->tableViewRight == tableView)
index = SecondSubTable;
left = (index+0) * MODEL_ROWS_IN_ONE_TABLE;
right = (index+1) * MODEL_ROWS_IN_ONE_TABLE;
//left = left - ((index+0) * MODEL_ROWS_IN_ONE_TABLE);
//right = right - ((index+0) * MODEL_ROWS_IN_ONE_TABLE);
tableViewIndexStart = (0 * MODEL_ROWS_IN_ONE_TABLE);
switch (index)
{
case (FirstSubTable):
{
tableviewIndexEnd
= (m_cellsNumber < MODEL_ROWS_IN_ONE_TABLE)
? m_cellsNumber
: MODEL_ROWS_IN_ONE_TABLE
;
}
break;
case(SecondSubTable):
{
tableviewIndexEnd
= (m_cellsNumber < MODEL_ROWS_IN_ONE_TABLE)
? 0
: m_cellsNumber - MODEL_ROWS_IN_ONE_TABLE
;
}
break;
}
}
else if(m_tablesNumber == ThreeTables)
{
int index = 0;
if(ui->tableViewLeft == tableView)
index = FirstSubTable;
if(ui->tableViewMiddle == tableView)
index = SecondSubTable;
if(ui->tableViewRight == tableView)
index = ThirdSubTable;
left = (index+0) * MODEL_ROWS_IN_ONE_TABLE;
right = (index+1) * MODEL_ROWS_IN_ONE_TABLE;
tableViewIndexStart = (0 * MODEL_ROWS_IN_ONE_TABLE);
switch (index)
{
case (FirstSubTable):
{
tableviewIndexEnd
= (m_cellsNumber < MODEL_ROWS_IN_ONE_TABLE*1)
? m_cellsNumber
: MODEL_ROWS_IN_ONE_TABLE
;
}
break;
case (SecondSubTable):
{
bool isInSecond = (m_cellsNumber>MODEL_ROWS_IN_ONE_TABLE*1) && (m_cellsNumber <= MODEL_ROWS_IN_ONE_TABLE*2);
bool isInThird = (m_cellsNumber>MODEL_ROWS_IN_ONE_TABLE*2) && (m_cellsNumber <= MODEL_ROWS_IN_ONE_TABLE*3);
if(isInSecond)
{
tableviewIndexEnd = m_cellsNumber - MODEL_ROWS_IN_ONE_TABLE;
}
if(isInThird)
{
tableviewIndexEnd = MODEL_ROWS_IN_ONE_TABLE;
}
}
break;
case (ThirdSubTable):
{
bool isInThird = (m_cellsNumber>MODEL_ROWS_IN_ONE_TABLE*2) && (m_cellsNumber <= MODEL_ROWS_IN_ONE_TABLE*3);
if(isInThird)
{
tableviewIndexEnd = m_cellsNumber - MODEL_ROWS_IN_ONE_TABLE*2;
}
}
break;
}
}
//Заолняем модель данными
int cellNumber = 0;
for (int row = tableViewIndexStart; row < tableviewIndexEnd; ++row)
{
cellNumber++;
for (int column = 0; column < MODEL_COLUMN_IN_ONE_TABLE; ++column)
{
//cellNumber++;
if( cellNumber > m_cellsNumber)
continue;
QModelIndex index = model->index(row, column, QModelIndex());
switch(column)
{
case ColNumber:
{
model->setData(index, QVariant(left + row + 1));
model->setData(index, Qt::AlignCenter, Qt::TextAlignmentRole);
}
break;
case ColVoltage:
{
if(doInitForCellVoltagArray)
model->setData(index, mCellVoltageArray[left + row].voltage);
else // just test data
model->setData(index, QVariant( (left + row + 1) ));
}
break;
case ColBalancing:
{
if(doInitForCellVoltagArray)
model->setData(index, mCellVoltageArray[left + row].balancing);
else
model->setData(index, QVariant(row%2));
}
break;
}
}
}
//Устанавливаем модель в представление
tableView->setModel(model);
//Устанавливаем делегат в столбец
CheckBoxDelegate *delegateCbox = new CheckBoxDelegate();
LabelVoltageDelegate *delegateLab = new LabelVoltageDelegate();
tableView->setItemDelegateForColumn(ColVoltage, delegateLab);
tableView->setItemDelegateForColumn(ColBalancing, delegateCbox);
//Внешний вид предтавления
tableView->verticalHeader()->hide();
tableView->horizontalHeader()->setStretchLastSection(false);
tableView->setSelectionMode(QAbstractItemView::NoSelection);
tableView->setEditTriggers(QAbstractItemView::NoEditTriggers);
tableView->setShowGrid(false);
// has to be last
tableView->setColumnWidth(ColNumber, 50);
tableView->setColumnWidth(ColVoltage, 100);
tableView->setColumnWidth(ColBalancing, 150);
}
void CellsMonitorPage::initTableView(int tablesNum, bool doInitForCellVoltagArray)
{
m_tablesNumber = tablesNum;
//m_cellsNumber = cellsNum;
m_cellsNumber = mCellVoltageArray.size();
if(m_cellsNumber > MAX_ROWS_ALL_TABLES)
m_cellsNumber = MAX_ROWS_ALL_TABLES;
clearTables();
auto gBackGr = ui->lbCellsMonitorTableBorderBackground->geometry();
auto gL = ui->tableViewLeft->geometry();
auto gR = ui->tableViewRight->geometry();
if(tablesNum == TwoTables) { // 13
MODEL_ROWS_IN_ONE_TABLE = 13;
auto one_half = (double)gBackGr.width()/2.0;
initTableView(ui->tableViewLeft, doInitForCellVoltagArray); // w=501 // x=340
ui->tableViewLeft->setGeometry(gL.x(),
gL.y(),
one_half + 20,
gL.height()
);
ui->tableViewMiddle->hide();
ui->tableViewRight->setGeometry(gL.x() + gBackGr.width()/2,
gL.y(),
one_half - 3,
gL.height()
);
initTableView(ui->tableViewRight, doInitForCellVoltagArray); // w=491 // x=830
}
else if(tablesNum == ThreeTables) { // 11
//340
//650
//980
//331
//341
//341
MODEL_ROWS_IN_ONE_TABLE = 11;
auto one_third = (double)gBackGr.width()/3.0;
ui->tableViewLeft->setGeometry(gL.x(),
gL.y(),
(one_third * 1) + 10,
gL.height()
);
ui->tableViewMiddle->setGeometry(gL.x() + (one_third * 1), //( (2.0 * (double)gBackGr.width()) / 3.0),
gL.y(),
(one_third * 1) + 10,
gL.height()
);
ui->tableViewMiddle->show();
ui->tableViewRight->setGeometry(gL.x() + (one_third * 2),
gL.y(),
(one_third * 1) - 3,
gL.height()
);
initTableView(ui->tableViewLeft, doInitForCellVoltagArray);
initTableView(ui->tableViewMiddle, doInitForCellVoltagArray);
initTableView(ui->tableViewRight, doInitForCellVoltagArray);
}
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////
///
///////////////////////////////////////////////////////////////////////////////////////////////////////////
void CellsMonitorPage::cellsReceived(int cellCount, QVector<double> cellVoltageArray)
{
mCellVoltageArray.clear();
mCellVoltageArray.resize(cellCount);
QVector<double> dataxNew;
dataxNew.clear();
QVector<double> datayNormal;
datayNormal.clear();
QVector<double> datayBalance;
datayBalance.clear();
QVector<QString> labels;
int indexPointer = 0;
double cellHardUnder = mDieBieMS->bmsConfig()->getParamDouble("cellHardUnderVoltage");
double cellHardOver = mDieBieMS->bmsConfig()->getParamDouble("cellHardOverVoltage");
for(indexPointer = 0; indexPointer < cellCount; indexPointer++)
{
mCellVoltageArray[indexPointer].voltage = cellVoltageArray[indexPointer];
dataxNew.append(indexPointer + 1);
if(cellVoltageArray[indexPointer] < 0.0){
datayNormal.append(0.0);
datayBalance.append(fabs(cellVoltageArray[indexPointer]));
}else{
datayNormal.append(fabs(cellVoltageArray[indexPointer]));
datayBalance.append(0.0);
}
QString voltageString = QStringLiteral("%1V (C").arg(fabs(cellVoltageArray[indexPointer]), 0, 'f', 2);
labels.append(voltageString + QString::number(indexPointer) + ")");
bool BALANCE_ON_IS_SAME_AS_EQUAL_ZERO = (datayBalance.last() == 0.0);
BALANCE_ON_IS_SAME_AS_EQUAL_ZERO = !BALANCE_ON_IS_SAME_AS_EQUAL_ZERO;
mCellVoltageArray[indexPointer].balancing = BALANCE_ON_IS_SAME_AS_EQUAL_ZERO;
}
initTableView(m_tablesNumber, true);
//QSharedPointer<QCPAxisTickerText> textTicker(new QCPAxisTickerText);
//textTicker->addTicks(dataxNew, labels);
//ui->cellBarGraph->xAxis->setTicker(textTicker);
//ui->cellBarGraph->xAxis->setRange(0.5, indexPointer + 0.5);
//ui->cellBarGraph->yAxis->setRange(cellHardUnder, cellHardOver);
//barsNormal->setData(dataxNew, datayNormal);
//barsBalance->setData(dataxNew, datayBalance);
}
void CellsMonitorPage::appendDoubleAndTrunc(QVector<double> *vec, double num, int maxSize)
{
vec->append(num);
if(vec->size() > maxSize) {
vec->remove(0, vec->size() - maxSize);
}
}
void CellsMonitorPage::on_editCols_textChanged(const QString &arg1)
{
m_tablesNumber = (arg1).toInt();
int cells = (ui->editCells->text()).toInt();
mCellVoltageArray.clear();
for(int i = 0, ilen = cells; i<ilen ;++i)
{
CellInfo c;
c.voltage = (double)(i*3);
c.balancing = (bool)(i%3);
mCellVoltageArray.push_back(c);
}
initTableView(m_tablesNumber, false);
}
void CellsMonitorPage::on_editCells_textChanged(const QString &arg1)
{
m_tablesNumber = (ui->editCols->text()).toInt();
int cells = (arg1).toInt();
mCellVoltageArray.clear();
for(int i = 0, ilen = cells; i<ilen ;++i)
{
CellInfo c;
c.voltage = (double)(i*3);
c.balancing = (bool)(i%3);
mCellVoltageArray.push_back(c);
}
initTableView(m_tablesNumber, false);
}
void CellsMonitorPage::on_pbTestOne_clicked()
{
mCellVoltageArray.clear();
for(int i = 0, ilen = 21; i<ilen ;++i)
{
CellInfo c;
c.voltage = (double)(i*3);
c.balancing = (bool)(i%3);
mCellVoltageArray.push_back(c);
}
initTableView(m_tablesNumber, true);
}
void CellsMonitorPage::on_pbTestTwo_clicked()
{
mCellVoltageArray.clear();
for(int i = 0, ilen = 21; i<ilen ;++i)
{
CellInfo c;
c.voltage = (double)(i*4);
c.balancing = (bool)(i%4);
mCellVoltageArray.push_back(c);
}
initTableView(m_tablesNumber, true);
}

113
cellsmonitorpage.h Normal file
View File

@@ -0,0 +1,113 @@
#ifndef CELLSMONITORPAGE_H
#define CELLSMONITORPAGE_H
#include <QWidget>
#include <QTableView>
#include <QVector>
#include "bmsinterface.h"
namespace Ui {
class CellsMonitorPage;
}
struct CellInfo
{
public:
CellInfo() : voltage(0), balancing(false)
{}
CellInfo(double v, bool b)
:voltage(v), balancing(b)
{}
double voltage;
bool balancing;
};
class CellsMonitorPage : public QFrame
{
Q_OBJECT
public:
explicit CellsMonitorPage(QWidget *parent = nullptr);
~CellsMonitorPage();
enum TablesNumber
{
TwoTables = 2,
ThreeTables = 3
};
enum TableIndex
{
FirstSubTable = 0,
SecondSubTable = 1,
ThirdSubTable = 2
};
enum ColumnsInSubTables
{
ColNumber = 0,
ColVoltage, //Столбец с делегатом
ColBalancing //Столбец с делегатом
};
int MODEL_ROWS_IN_ONE_TABLE = 13; //Количество строк модели одной из таблиц
static constexpr int MODEL_COLUMN_IN_ONE_TABLE = 3; //Количество столбцов модели одной из таблиц (№,Напр,Баланс)
static constexpr int MAX_ROWS_ALL_TABLES = 26; //Количество строк для всех трёх таблиц
BMSInterface *bms() const;
void setDieBieMS(BMSInterface *dieBieMS);
private slots:
void on_editCols_textChanged(const QString &arg1);
void on_editCells_textChanged(const QString &arg1);
void on_pbTestOne_clicked();
void on_pbTestTwo_clicked();
private:
Ui::CellsMonitorPage *ui;
void hideTestUi();
BMSInterface *mDieBieMS;
int m_tablesNumber;
int m_cellsNumber;
QVector<CellInfo> mCellVoltageArray;
void createUI();
void clearTables();
void initTableHeader();
// this one is for the real usage
void initTableView(QTableView* tableView, bool doInitForCellVoltagArray = true);
void initTableView(int tablesNum = TwoTables, bool doInitForCellVoltagArray = true);
// this one is just for test
//void initTableView(int cellsNum = 0, int tablesNum = TwoTables, bool doInitForCellVoltagArray = true);
//
public slots:
///void onLoadParams();
private slots:
//void timerSlot();
//void valuesReceived(BMS_VALUES values);
void cellsReceived(int cellCount, QVector<double> cellVoltageArray);
/*
QVector<double> mSeconds;
double mSecondCounter;
qint64 mLastUpdateTime;
bool mUpdateValPlot;
QTimer* mTimer;
*/
///void initForCharts();
void appendDoubleAndTrunc(QVector<double> *vec, double num, int maxSize);
///void clearControlls();
};
#endif // CELLSMONITORPAGE_H

353
cellsmonitorpage.ui Normal file
View File

@@ -0,0 +1,353 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>CellsMonitorPage</class>
<widget class="QFrame" name="CellsMonitorPage">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>1071</width>
<height>755</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<property name="styleSheet">
<string notr="true">/*QFrame#CellsMonitorPage
{
background-color: red;
}
*/
/*
QLabel#lbIconLogo
{
border-image: url(:/images/cubo-verde-logo-white.png) 0 0 0 0 stretch stretch;
}
QLabel#lbHeaderLanguageIcon
{
border-image: url(:/images/language-russ.png) 0 0 0 0 stretch stretch;
}
QPushButton#pushButton,
#pushButton_2,
#pushButton_3,
#pushButton_4,
#pushButton_5,
#pushButton_6,
#pushButton_7,
#pushButton_8
{
color: #FFFFFF;
background-color: #009352;
border: 0px solid black;
background:#009352;
text-align: left;
padding-left: 85px;
font-size:17px;
}
QPushButton#pushButton
{
font-size:15px;
}
QPushButton:pressed {
background-color: qlineargradient(spread:pad, x1:0, y1:0, x2:0, y2:1, stop:0 rgba(60, 186, 162, 255), stop:1 rgba(98, 211, 162, 255))
}
QPushButton#pushButton:hover,
#pushButton_2:hover,
#pushButton_3:hover,
#pushButton_4:hover,
#pushButton_5:hover,
#pushButton_6:hover,
#pushButton_7:hover,
#pushButton_8:hover
{
background-color: #01673a;
}
*/
/*------------------*/
/*
QGroupBox#gbSerialNumber,
#gbConfiguration,
#gbSoc
{
border-radius:10px;
border-color: grey;
border-style: solid;
border-width: 1px;
}
*/
/*--------------------------------------*/
/*--------------TABLE----------------*/
/*--------------------------------------*/
QTableView {
background-color: #FFFFFF;
alternate-background-color: #282828;
border-radius: 10px;
/*color: #000000;*/
color: #869098;
font-size: 16px;
text-align: center;
}
QTableView QHeaderView {
background-color: #f0f1f4;
border: none;
/*border-bottom: 2px solid #f0f1f4;*/
border-top-left-radius: 10px;
border-top-right-radius: 10px;
min-height: 70px;
/*padding-top: 20px;*/
padding-top: 5px;
/*padding-right: 15px;*/
/*margin-right: 20px;*/
/*text-align: center;*/
}
QTableView QHeaderView::section {
/*
background-color: transparent;
border: none;
color: #8F8F8F;
font-size: 12px;
font-weight: bold;
*/
background-color: #f0f1f4;
/*border: 1px solid #e8e9f1;*/
border: none;
color: #869098;
text-align: center;
font-size: 16px;
/*padding: 0px 0px 0px 35px;*/
}
QTableView::item {
border: none;
/*padding: 0px 35px;*/
text-align: center;
min-height: 60px;
}
QScrollBar {
border-radius: 10px;
background: #e8e9f1;
}
QScrollBar::vertical {
width: 10px;
margin: 75px 0px 0px 0px;
}
/*-----------------------------------*/
QScrollBar::sub-page:horizontal {
background: #e8e9f1;
border-top-left-radius: 9px;
border-bottom-left-radius: 9px;
}
QScrollBar::add-page:horizontal {
background: #e8e9f1;
border-top-right-radius: 9px;
border-bottom-right-radius: 9px;
}
QScrollBar::sub-page:vertical {
background: #e8e9f1;
border-top-left-radius: 9px;
border-top-right-radius: 9px;
}
QScrollBar::add-page:vertical {
background: #e8e9f1;
border-bottom-left-radius: 9px;
border-bottom-right-radius: 9px;
}
/*-----------------------------------*/
QScrollBar::up-arrow,
QScrollBar::sub-line,
QScrollBar::add-line {
width: 0px;
height: 0px;
}
QScrollBar::handle {
background: #009352;
border-radius: 5px;
}
</string>
</property>
<widget class="QTableView" name="tableViewRight">
<property name="geometry">
<rect>
<x>670</x>
<y>90</y>
<width>341</width>
<height>621</height>
</rect>
</property>
</widget>
<widget class="QTableView" name="tableViewLeft">
<property name="geometry">
<rect>
<x>30</x>
<y>90</y>
<width>331</width>
<height>621</height>
</rect>
</property>
</widget>
<widget class="QLabel" name="lbCellsMonitorTableBorderBackground">
<property name="geometry">
<rect>
<x>28</x>
<y>88</y>
<width>985</width>
<height>625</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true">/* background-color: #f0fFfF;*/
background-color: #e8e9f1;
/*grey*/
/*#e8e9f1*/
/*green*/
/*#009352*/
border: none;
border-radius: 10px;
/* padding-top: 20px;*/
</string>
</property>
<property name="text">
<string/>
</property>
</widget>
<widget class="QTableView" name="tableViewMiddle">
<property name="geometry">
<rect>
<x>350</x>
<y>90</y>
<width>341</width>
<height>621</height>
</rect>
</property>
</widget>
<widget class="QLabel" name="lbCenterTabName">
<property name="geometry">
<rect>
<x>30</x>
<y>30</y>
<width>351</width>
<height>41</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>26</pointsize>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Монитор ячеек</string>
</property>
</widget>
<widget class="QLineEdit" name="editCols">
<property name="geometry">
<rect>
<x>490</x>
<y>50</y>
<width>61</width>
<height>20</height>
</rect>
</property>
</widget>
<widget class="QLineEdit" name="editCells">
<property name="geometry">
<rect>
<x>630</x>
<y>50</y>
<width>61</width>
<height>20</height>
</rect>
</property>
</widget>
<widget class="QLabel" name="lbCols">
<property name="geometry">
<rect>
<x>410</x>
<y>50</y>
<width>71</width>
<height>16</height>
</rect>
</property>
<property name="text">
<string>столбцов</string>
</property>
</widget>
<widget class="QLabel" name="lbCells">
<property name="geometry">
<rect>
<x>570</x>
<y>50</y>
<width>51</width>
<height>16</height>
</rect>
</property>
<property name="text">
<string>ячеек</string>
</property>
</widget>
<widget class="QPushButton" name="pbTestOne">
<property name="geometry">
<rect>
<x>440</x>
<y>20</y>
<width>75</width>
<height>23</height>
</rect>
</property>
<property name="text">
<string>Тест 1</string>
</property>
</widget>
<widget class="QPushButton" name="pbTestTwo">
<property name="geometry">
<rect>
<x>560</x>
<y>20</y>
<width>75</width>
<height>23</height>
</rect>
</property>
<property name="text">
<string>Тест 2</string>
</property>
</widget>
<zorder>lbCellsMonitorTableBorderBackground</zorder>
<zorder>tableViewMiddle</zorder>
<zorder>tableViewLeft</zorder>
<zorder>tableViewRight</zorder>
<zorder>lbCenterTabName</zorder>
<zorder>editCols</zorder>
<zorder>editCells</zorder>
<zorder>lbCols</zorder>
<zorder>lbCells</zorder>
<zorder>pbTestOne</zorder>
<zorder>pbTestTwo</zorder>
</widget>
<resources/>
<connections/>
</ui>

744
commands.cpp Normal file
View File

@@ -0,0 +1,744 @@
/*
Original copyright 2018 Benjamin Vedder benjamin@vedder.se and the VESC Tool project ( https://github.com/vedderb/vesc_tool )
Forked to:
Copyright 2018 Danny Bokma github@diebie.nl (https://github.com/DieBieEngineering/DieBieMS-Tool)
Now forked to:
Copyright 2019 - 2020 Kevin Dionne kevin.dionne@ennoid.me (https://github.com/EnnoidMe/ENNOID-BMS-Tool)
This file is part of ENNOID-BMS Tool.
ENNOID-BMS Tool is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
ENNOID-BMS Tool is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "commands.h"
#include <QDebug>
#include <QSettings>
Commands::Commands(QObject *parent) : QObject(parent)
{
mSendCan = QSettings().value("send_can", false).toBool();
mCanId = QSettings().value("can_id", 0).toInt();
mIsLimitedMode = false;
// Firmware state
mFirmwareIsUploading = false;
mFirmwareState = 0;
mFimwarePtr = 0;
mFirmwareTimer = 0;
mFirmwareRetries = 0;
mFirmwareUploadStatus = "FW Upload Status";
mCheckNextbmsConfig = false;
mTimer = new QTimer(this);
mTimer->setInterval(10);
mTimer->start();
mbmsConfig = 0;
mTimeoutCount = 50;
mTimeoutFwVer = 0;
mTimeoutBMSconf = 0;
mTimeoutValues = 0;
mTimeoutCells = 0;
mTimeoutAux = 0;
mTimeoutExp = 0;
mTimeoutPingCan = 0;
connect(mTimer, SIGNAL(timeout()), this, SLOT(timerSlot()));
}
void Commands::setLimitedMode(bool is_limited)
{
mIsLimitedMode = is_limited;
}
bool Commands::isLimitedMode()
{
return mIsLimitedMode;
}
void Commands::setSendCan(bool sendCan, int id)
{
mSendCan = sendCan;
QSettings().setValue("send_can", sendCan);
if (id >= 0) {
setCanSendId(id);
}
}
bool Commands::getSendCan()
{
return mSendCan;
}
void Commands::setCanSendId(unsigned int id)
{
mCanId = id;
QSettings().setValue("can_id", id);
}
int Commands::getCanSendId()
{
return mCanId;
}
void Commands::processPacket(QByteArray data)
{
VByteArray vb(data);
COMM_PACKET_ID id = (COMM_PACKET_ID)vb.vbPopFrontUint8();
switch (id) {
case COMM_FW_VERSION: {
mTimeoutFwVer = 0;
int fw_major;
int fw_minor;
QString hw;
QByteArray uuid;
if (vb.size() >= 2) {
fw_major = vb.vbPopFrontInt8();
fw_minor = vb.vbPopFrontInt8();
hw = vb.vbPopFrontString();
if (vb.size() >= 12) {
uuid.append(vb.left(12));
vb.remove(0, 12);
}
} else {
fw_major = -1;
fw_minor = -1;
}
emit fwVersionReceived(fw_major, fw_minor, hw, uuid);
} break;
case COMM_ERASE_NEW_APP:
case COMM_WRITE_NEW_APP_DATA:
firmwareUploadUpdate(!vb.at(0));
break;
case COMM_GET_VALUES: {
mTimeoutValues = 0;
BMS_VALUES values;
values.packVoltage = vb.vbPopFrontDouble32(1e3);
values.packCurrent = vb.vbPopFrontDouble32(1e3);
values.soC = vb.vbPopFrontUint8();
values.cVHigh = vb.vbPopFrontDouble32(1e3);
values.cVAverage = vb.vbPopFrontDouble32(1e3);
values.cVLow = vb.vbPopFrontDouble32(1e3);
values.cVMisMatch = vb.vbPopFrontDouble32(1e3);
values.loadLCVoltage = vb.vbPopFrontDouble16(1e1);
values.loadLCCurrent = vb.vbPopFrontDouble16(1e1);
values.chargerVoltage = vb.vbPopFrontDouble16(1e1);
values.tempBattHigh = vb.vbPopFrontDouble16(1e1);
values.tempBattAverage = vb.vbPopFrontDouble16(1e1);
values.tempBattLow = vb.vbPopFrontDouble16(1e1);
values.tempBMSHigh = vb.vbPopFrontDouble16(1e1);
values.tempBMSAverage = vb.vbPopFrontDouble16(1e1);
values.tempBMSLow = vb.vbPopFrontDouble16(1e1);
values.humidity = vb.vbPopFrontDouble16(1e1);
values.opState = opStateToStr((OperationalStateTypedef)vb.vbPopFrontUint8());
values.balanceActive = vb.vbPopFrontUint8();
values.faultState = faultStateToStr((bms_fault_code)vb.vbPopFrontUint8());
emit valuesReceived(values);
} break;
case COMM_GET_BMS_CELLS:{
mTimeoutCells = 0;
int mCellAmount;
QVector<double> mCellVoltages;
mCellVoltages.clear();
mCellAmount = vb.vbPopFrontUint8();
for(int cellValuePointer = 0; cellValuePointer < mCellAmount; cellValuePointer++){
mCellVoltages.append(vb.vbPopFrontDouble16(1e3));
}
emit cellsReceived(mCellAmount,mCellVoltages);
} break;
case COMM_GET_BMS_AUX:{
mTimeoutAux = 0;
int mAuxAmount;
QVector<double> mAuxVoltages;
mAuxVoltages.clear();
mAuxAmount = vb.vbPopFrontUint8();
for(int auxValuePointer = 0; auxValuePointer < mAuxAmount; auxValuePointer++){
mAuxVoltages.append(vb.vbPopFrontDouble16(1e1));
}
emit auxReceived(mAuxAmount,mAuxVoltages);
} break;
case COMM_GET_BMS_EXP_TEMP:{
mTimeoutExp = 0;
int mExpTempAmount;
QVector<double> mExpTempVoltages;
mExpTempVoltages.clear();
mExpTempAmount = vb.vbPopFrontUint8();
for(int expTempValuePointer = 0; expTempValuePointer < mExpTempAmount; expTempValuePointer++){
mExpTempVoltages.append(vb.vbPopFrontDouble16(1e1));
}
emit expTempReceived(mExpTempAmount,mExpTempVoltages);
} break;
case COMM_PRINT:
emit printReceived(QString::fromLatin1(vb));
break;
case COMM_ROTOR_POSITION:
emit rotorPosReceived(vb.vbPopFrontDouble32(1e5));
break;
case COMM_GET_MCCONF:
case COMM_GET_MCCONF_DEFAULT:
mTimeoutBMSconf = 0;
if (mbmsConfig) {
mbmsConfig->deSerialize(vb);
mbmsConfig->updateDone();
if (mCheckNextbmsConfig) {
mCheckNextbmsConfig = false;
emit bmsConfigCheckResult(mbmsConfig->checkDifference(&mbmsConfigLast));
}
}
break;
case COMM_SET_MCCONF:
emit ackReceived("BMS Write OK");
break;
case COMM_STORE_BMS_CONF:
if (mbmsConfig) {
mbmsConfig->storingDone();
}
break;
case COMM_PING_CAN: {
mTimeoutPingCan = 0;
QVector<int> devs;
while(vb.size() > 0) {
devs.append(vb.vbPopFrontUint8());
}
emit pingCanRx(devs, false);
} break;
default:
break;
}
}
void Commands::getFwVersion()
{
if (mTimeoutFwVer > 0) {
return;
}
mTimeoutFwVer = mTimeoutCount;
VByteArray vb;
vb.vbAppendInt8(COMM_FW_VERSION);
emitData(vb);
}
void Commands::getValues()
{
if (mTimeoutValues > 0) {
return;
}
mTimeoutValues = mTimeoutCount;
VByteArray vb;
vb.vbAppendInt8(COMM_GET_VALUES);
emitData(vb);
}
void Commands::getCells()
{
if (mTimeoutCells > 0) {
return;
}
mTimeoutCells = mTimeoutCount;
VByteArray vb;
vb.vbAppendInt8(COMM_GET_BMS_CELLS);
emitData(vb);
}
void Commands::getAux()
{
if (mTimeoutAux > 0) {
return;
}
mTimeoutAux = mTimeoutCount;
VByteArray vb;
vb.vbAppendInt8(COMM_GET_BMS_AUX);
emitData(vb);
}
void Commands::getExpansionTemp()
{
if (mTimeoutExp > 0) {
return;
}
mTimeoutExp = mTimeoutCount;
VByteArray vb;
vb.vbAppendInt8(COMM_GET_BMS_EXP_TEMP);
emitData(vb);
}
void Commands::sendTerminalCmd(QString cmd)
{
VByteArray vb;
vb.vbAppendInt8(COMM_TERMINAL_CMD);
vb.append(cmd.toLatin1());
emitData(vb);
}
void Commands::setDetect(disp_pos_mode mode)
{
VByteArray vb;
vb.vbAppendInt8(COMM_SET_DETECT);
vb.vbAppendInt8(mode);
emitData(vb);
}
void Commands::samplePrint(debug_sampling_mode mode, int sample_len, int dec)
{
VByteArray vb;
vb.vbAppendInt8(COMM_SAMPLE_PRINT);
vb.vbAppendInt8(mode);
vb.vbAppendUint16(sample_len);
vb.vbAppendUint8(dec);
emitData(vb);
}
void Commands::getBMSconf()
{
if (mTimeoutBMSconf > 0) {
return;
}
mTimeoutBMSconf = mTimeoutCount;
mCheckNextbmsConfig = false;
VByteArray vb;
vb.vbAppendInt8(COMM_GET_MCCONF);
emitData(vb);
}
void Commands::getBMSconfDefault()
{
if (mTimeoutBMSconf > 0) {
return;
}
mTimeoutBMSconf = mTimeoutCount;
mCheckNextbmsConfig = false;
VByteArray vb;
vb.vbAppendInt8(COMM_GET_MCCONF_DEFAULT);
emitData(vb);
}
void Commands::setBMSconf(bool check)
{
if (mbmsConfig) {
mbmsConfigLast = *mbmsConfig;
VByteArray vb;
vb.vbAppendInt8(COMM_SET_MCCONF);
mbmsConfig->serialize(vb);
emitData(vb);
if (check) {
checkbmsConfig();
}
}
}
void Commands::reboot()
{
VByteArray vb;
vb.vbAppendInt8(COMM_REBOOT);
emitData(vb);
}
void Commands::sendAlive()
{
VByteArray vb;
vb.vbAppendInt8(COMM_ALIVE);
emitData(vb);
}
void Commands::pairNrf(int ms)
{
VByteArray vb;
vb.vbAppendInt8(COMM_NRF_START_PAIRING);
vb.vbAppendInt32(ms);
emitData(vb);
}
void Commands::timerSlot()
{
if (mFirmwareIsUploading) {
if (mFirmwareTimer) {
mFirmwareTimer--;
} else {
firmwareUploadUpdate(true);
}
}
if (mTimeoutFwVer > 0) mTimeoutFwVer--;
if (mTimeoutBMSconf > 0) mTimeoutBMSconf--;
if (mTimeoutValues > 0) mTimeoutValues--;
if (mTimeoutCells > 0) mTimeoutCells--;
if (mTimeoutAux > 0) mTimeoutAux--;
if (mTimeoutExp > 0) mTimeoutExp--;
}
void Commands::emitData(QByteArray data)
{
// Only allow firmware commands in limited mode
if (mIsLimitedMode && data.at(0) > COMM_WRITE_NEW_APP_DATA) {
return;
}
if (mSendCan) {
data.prepend((char)mCanId);
data.prepend((char)COMM_FORWARD_CAN);
}
emit dataToSend(data);
}
void Commands::firmwareUploadUpdate(bool isTimeout)
{
if (!mFirmwareIsUploading) {
return;
}
const int app_packet_size = 200;
const int retries = 5;
const int timeout = 350;
if (mFirmwareState == 0) {
mFirmwareUploadStatus = "Buffer Erase";
if (isTimeout) {
// Erase timed out, abort.
mFirmwareIsUploading = false;
mFimwarePtr = 0;
mFirmwareUploadStatus = "Buffer Erase Timeout";
} else {
mFirmwareState++;
mFirmwareRetries = retries;
mFirmwareTimer = timeout;
firmwareUploadUpdate(true);
}
} else if (mFirmwareState == 1) {
mFirmwareUploadStatus = "CRC/Size Write";
if (isTimeout) {
if (mFirmwareRetries > 0) {
mFirmwareRetries--;
mFirmwareTimer = timeout;
} else {
mFirmwareIsUploading = false;
mFimwarePtr = 0;
mFirmwareState = 0;
mFirmwareUploadStatus = "CRC/Size Write Timeout";
return;
}
quint16 crc = Packet::crc16((const unsigned char*)mNewFirmware.constData(), mNewFirmware.size());
VByteArray vb;
vb.append((char)COMM_WRITE_NEW_APP_DATA);
vb.vbAppendUint32(0);
vb.vbAppendUint32(mNewFirmware.size());
vb.vbAppendUint16(crc);
emitData(vb);
} else {
mFirmwareState++;
mFirmwareRetries = retries;
mFirmwareTimer = timeout;
firmwareUploadUpdate(true);
}
} else if (mFirmwareState == 2) {
mFirmwareUploadStatus = "FW Data Write";
if (isTimeout) {
if (mFirmwareRetries > 0) {
mFirmwareRetries--;
mFirmwareTimer = timeout;
} else {
mFirmwareIsUploading = false;
mFimwarePtr = 0;
mFirmwareState = 0;
mFirmwareUploadStatus = "FW Data Write Timeout";
return;
}
int fw_size_left = mNewFirmware.size() - mFimwarePtr;
int send_size = fw_size_left > app_packet_size ? app_packet_size : fw_size_left;
VByteArray vb;
vb.append((char)COMM_WRITE_NEW_APP_DATA);
if (mFirmwareIsBootloader) {
vb.vbAppendUint32(mFimwarePtr + (1024 * 2 * 50));
} else {
vb.vbAppendUint32(mFimwarePtr + 6);
}
vb.append(mNewFirmware.mid(mFimwarePtr, send_size));
emitData(vb);
} else {
mFirmwareRetries = retries;
mFirmwareTimer = timeout;
mFimwarePtr += app_packet_size;
if (mFimwarePtr >= mNewFirmware.size()) {
mFirmwareIsUploading = false;
mFimwarePtr = 0;
mFirmwareState = 0;
mFirmwareUploadStatus = "FW Upload Done";
// Upload done. Enter bootloader!
if (!mFirmwareIsBootloader) {
QByteArray buffer;
buffer.append((char)COMM_JUMP_TO_BOOTLOADER);
emitData(buffer);
}
} else {
firmwareUploadUpdate(true);
}
}
}
}
QString Commands::opStateToStr(OperationalStateTypedef fault)
{
switch (fault) {
case OP_STATE_INIT: return "Init";
case OP_STATE_CHARGING: return "Charging";
case OP_STATE_PRE_CHARGE: return "PreCharging";
case OP_STATE_LOAD_ENABLED: return "Load enabled";
case OP_STATE_BATTERY_DEAD: return "Battery dead";
case OP_STATE_POWER_DOWN: return "Power down";
case OP_STATE_EXTERNAL: return "External";
case OP_STATE_ERROR: return "Error";
case OP_STATE_ERROR_PRECHARGE: return "ErrPreCharge";
case OP_STATE_BALANCING: return "Balancing";
case OP_STATE_CHARGED: return "Charged";
case OP_STATE_FORCEON: return "ForceOn";
default: return "Unknown fault";
}
}
QString Commands::faultStateToStr(bms_fault_code fault)
{
switch (fault) {
case FAULT_CODE_NONE: return "System OK";
case FAULT_CODE_PACK_OVER_VOLTAGE: return "Pack overvoltage";
case FAULT_CODE_PACK_UNDER_VOLTAGE: return "Pack undervoltage";
case FAULT_CODE_LOAD_OVER_VOLTAGE: return "Load overvoltage";
case FAULT_CODE_LOAD_UNDER_VOLTAGE: return "Load undervoltage";
case FAULT_CODE_CHARGER_OVER_VOLTAGE: return "Charger overvoltage";
case FAULT_CODE_CHARGER_UNDER_VOLTAGE: return "Charger undervoltgae";
case FAULT_CODE_CELL_HARD_OVER_VOLTAGE: return "Cell hard overvoltage";
case FAULT_CODE_CELL_HARD_UNDER_VOLTAGE: return "Cell hard undervoltage";
case FAULT_CODE_CELL_SOFT_OVER_VOLTAGE: return "Cell soft overvoltage";
case FAULT_CODE_CELL_SOFT_UNDER_VOLTAGE: return "Cell soft undervoltage";
case FAULT_CODE_MAX_UVP_OVP_ERRORS: return "MAX OVP/UVP errors";
case FAULT_CODE_MAX_UVT_OVT_ERRORS: return "MAX OVT/UVT errors";
case FAULT_CODE_OVER_CURRENT: return "Over current";
case FAULT_CODE_OVER_TEMP_BMS: return "Over temp BMS";
case FAULT_CODE_UNDER_TEMP_BMS: return "Under temp BMS";
case FAULT_CODE_DISCHARGE_OVER_TEMP_CELLS: return "Discharge over temp cell";
case FAULT_CODE_DISCHARGE_UNDER_TEMP_CELLS: return "Discharge under temp cell";
case FAULT_CODE_CHARGE_OVER_TEMP_CELLS: return "Charge over temp cell";
case FAULT_CODE_CHARGE_UNDER_TEMP_CELLS: return "Charge under temp cell";
case FAULT_CODE_PRECHARGE_TIMEOUT: return "Precharge timeout";
case FAULT_CODE_DISCHARGE_RETRY: return "Discharge retry";
case FAULT_CODE_CHARGE_RETRY: return "Charge retry";
case FAULT_CODE_CHARGER_DISCONNECT: return "Charge retry";
default: return "Unknown fault";
}
}
void Commands::setbmsConfig(ConfigParams *bmsConfig)
{
mbmsConfig = bmsConfig;
connect(mbmsConfig, SIGNAL(updateRequested()), this, SLOT(getBMSconf()));
connect(mbmsConfig, SIGNAL(updateRequestDefault()), this, SLOT(getBMSconfDefault()));
}
void Commands::startFirmwareUpload(QByteArray &newFirmware, bool isBootloader)
{
mFirmwareIsBootloader = isBootloader;
mFirmwareIsUploading = true;
mFirmwareState = mFirmwareIsBootloader ? 2 : 0;
mFimwarePtr = 0;
mFirmwareTimer = 500;
mFirmwareRetries = 5;
mNewFirmware.clear();
mNewFirmware.append(newFirmware);
mFirmwareUploadStatus = "Buffer Erase";
if (mFirmwareIsBootloader) {
firmwareUploadUpdate(true);
} else {
VByteArray vb;
vb.vbAppendInt8(COMM_ERASE_NEW_APP);
vb.vbAppendUint32(mNewFirmware.size());
emitData(vb);
}
}
double Commands::getFirmwareUploadProgress()
{
if (mFirmwareIsUploading) {
return (double)mFimwarePtr / (double)mNewFirmware.size();
} else {
return -1.0;
}
}
QString Commands::getFirmwareUploadStatus()
{
return mFirmwareUploadStatus;
}
void Commands::cancelFirmwareUpload()
{
if (mFirmwareIsUploading) {
mFirmwareIsUploading = false;
mFimwarePtr = 0;
mFirmwareState = 0;
mFirmwareUploadStatus = "Cancelled";
}
}
void Commands::checkbmsConfig()
{
mCheckNextbmsConfig = true;
VByteArray vb;
vb.vbAppendInt8(COMM_GET_MCCONF);
emitData(vb);
}
void Commands::storeBMSConfig()
{
VByteArray vb;
vb.vbAppendInt8(COMM_STORE_BMS_CONF);
emitData(vb);
}
void Commands::emitEmptyValues()
{
BMS_VALUES values;
values.packVoltage = 0.0;
values.packCurrent = 0.0;
values.soC = 0.0;
values.cVHigh = 0.0;
values.cVAverage = 0.0;
values.cVLow = 0.0;
values.cVMisMatch = 0.0;
values.loadLCVoltage = 0.0;
values.loadLCCurrent = 0.0;
values.loadHCVoltage = 0.0;
values.loadHCCurrent = 0.0;
values.chargerVoltage = 0.0;
values.auxVoltage = 0.0;
values.auxCurrent = 0.0;
values.tempBattHigh = 0.0;
values.tempBattAverage = 0.0;
values.tempBattLow = 0.0;
values.tempBMSHigh = 0.0;
values.tempBMSAverage = 0.0;
values.tempBMSLow = 0.0;
values.humidity = 0.0;
values.opState = "Unknown";
values.balanceActive = 0.0;
values.faultState = "Unknown";
emit valuesReceived(values);
}
void Commands::emitEmptySetupValues()
{
BMS_VALUES values;
values.packVoltage = 0.0;
values.packCurrent = 0.0;
values.soC = 0.0;
values.cVHigh = 0.0;
values.cVAverage = 0.0;
values.cVLow = 0.0;
values.cVMisMatch = 0.0;
values.loadLCVoltage = 0.0;
values.loadLCCurrent = 0.0;
values.loadHCVoltage = 0.0;
values.loadHCCurrent = 0.0;
values.chargerVoltage = 0.0;
values.auxVoltage = 0.0;
values.auxCurrent = 0.0;
values.tempBattHigh = 0.0;
values.tempBattAverage = 0.0;
values.tempBattLow = 0.0;
values.tempBMSHigh = 0.0;
values.tempBMSAverage = 0.0;
values.tempBMSLow = 0.0;
values.opState = OP_STATE_INIT;
values.balanceActive = 1;
values.faultState = FAULT_CODE_NONE;
emit valuesSetupReceived(values);
}
void Commands::pingCan()
{
if (mTimeoutPingCan > 0) {
return;
}
mTimeoutPingCan = 500;
VByteArray vb;
vb.vbAppendInt8(COMM_PING_CAN);
emitData(vb);
}

131
commands.h Normal file
View File

@@ -0,0 +1,131 @@
/*
Original copyright 2018 Benjamin Vedder benjamin@vedder.se and the VESC Tool project ( https://github.com/vedderb/vesc_tool )
Forked to:
Copyright 2018 Danny Bokma github@diebie.nl (https://github.com/DieBieEngineering/DieBieMS-Tool)
Now forked to:
Copyright 2019 - 2020 Kevin Dionne kevin.dionne@ennoid.me (https://github.com/EnnoidMe/ENNOID-BMS-Tool)
This file is part of ENNOID-BMS Tool.
ENNOID-BMS Tool is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
ENNOID-BMS Tool is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef COMMANDS_H
#define COMMANDS_H
#include <QObject>
#include <QTimer>
#include "vbytearray.h"
#include "datatypes.h"
#include "packet.h"
#include "configparams.h"
class Commands : public QObject
{
Q_OBJECT
public:
explicit Commands(QObject *parent = 0);
void setLimitedMode(bool is_limited);
Q_INVOKABLE bool isLimitedMode();
Q_INVOKABLE void setSendCan(bool sendCan, int id = -1);
Q_INVOKABLE bool getSendCan();
Q_INVOKABLE void setCanSendId(unsigned int id);
Q_INVOKABLE int getCanSendId();
void setbmsConfig(ConfigParams *bmsConfig);
Q_INVOKABLE void startFirmwareUpload(QByteArray &newFirmware, bool isBootloader = false);
Q_INVOKABLE double getFirmwareUploadProgress();
Q_INVOKABLE QString getFirmwareUploadStatus();
Q_INVOKABLE void cancelFirmwareUpload();
void checkbmsConfig();
void storeBMSConfig();
Q_INVOKABLE void emitEmptyValues();
Q_INVOKABLE void emitEmptySetupValues();
signals:
void dataToSend(QByteArray &data);
void fwVersionReceived(int major, int minor, QString hw, QByteArray uuid);
void ackReceived(QString ackType);
void valuesReceived(BMS_VALUES values);
void cellsReceived(int cellCount, QVector<double> cellVoltageArray);
void auxReceived(int auxCount, QVector<double> auxVoltageArray);
void expTempReceived(int expTempCount, QVector<double> expTempVoltageArray);
void printReceived(QString str);
void rotorPosReceived(double pos);
void bmsConfigCheckResult(QStringList paramsNotSet);
void valuesSetupReceived(BMS_VALUES values);
void pingCanRx(QVector<int> devs, bool isTimeout);
public slots:
void processPacket(QByteArray data);
void getFwVersion();
void getValues();
void getCells();
void getAux();
void getExpansionTemp();
void sendTerminalCmd(QString cmd);
void setDetect(disp_pos_mode mode);
void samplePrint(debug_sampling_mode mode, int sample_len, int dec);
void getBMSconf();
void getBMSconfDefault();
void setBMSconf(bool check = true);
void reboot();
void sendAlive();
void pairNrf(int ms);
void pingCan();
private slots:
void timerSlot();
private:
void emitData(QByteArray data);
void firmwareUploadUpdate(bool isTimeout);
QString opStateToStr(OperationalStateTypedef fault);
QString faultStateToStr(bms_fault_code fault);
QTimer *mTimer;
bool mSendCan;
int mCanId;
bool mIsLimitedMode;
// FW upload state
QByteArray mNewFirmware;
bool mFirmwareIsUploading;
int mFirmwareState;
int mFimwarePtr;
int mFirmwareTimer;
int mFirmwareRetries;
bool mFirmwareIsBootloader;
QString mFirmwareUploadStatus;
ConfigParams *mbmsConfig;
ConfigParams mbmsConfigLast;
bool mCheckNextbmsConfig;
int mTimeoutCount;
int mTimeoutFwVer;
int mTimeoutBMSconf;
int mTimeoutValues;
int mTimeoutCells;
int mTimeoutAux;
int mTimeoutExp;
int mTimeoutPingCan;
};
#endif // COMMANDS_H

101
configparam.cpp Normal file
View File

@@ -0,0 +1,101 @@
/*
Original copyright 2018 Benjamin Vedder benjamin@vedder.se and the VESC Tool project ( https://github.com/vedderb/vesc_tool )
Forked to:
Copyright 2018 Danny Bokma github@diebie.nl (https://github.com/DieBieEngineering/DieBieMS-Tool)
Now forked to:
Copyright 2019 - 2020 Kevin Dionne kevin.dionne@ennoid.me (https://github.com/EnnoidMe/ENNOID-BMS-Tool)
This file is part of ENNOID-BMS Tool.
ENNOID-BMS Tool is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
ENNOID-BMS Tool is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "configparam.h"
ConfigParam::ConfigParam()
{
reset();
}
void ConfigParam::reset()
{
type = CFG_T_UNDEFINED;
longName = "none";
description = "TODO";
cDefine.clear();
valDouble = 0.0;
valInt = 0;
maxDouble = 99.0;
minDouble = 0.0;
stepDouble = 1.0;
editorDecimalsDouble = 2;
editorScale = 1.0;
maxInt = 99;
minInt = 0;
stepInt = 1;
vTx = VESC_TX_UNDEFINED;
vTxDoubleScale = 1.0;
suffix = "";
enumNames.clear();
editAsPercentage = false;
showDisplay = false;
transmittable = true;
}
void ConfigParam::setInt(int val, int max, int min, int step)
{
type = CFG_T_INT;
valInt = val;
maxInt = max;
minInt = min;
stepInt = step;
}
void ConfigParam::setDouble(double val, double max, double min, double step, int decimals)
{
type = CFG_T_DOUBLE;
valDouble = val;
maxDouble = max;
minDouble = min;
stepDouble = step;
editorDecimalsDouble = decimals;
}
void ConfigParam::setDoubleTx(VESC_TX_T tx, double scale)
{
vTx = tx;
vTxDoubleScale = scale;
}
void ConfigParam::setEnum(int val, QStringList names)
{
type = CFG_T_ENUM;
valInt = val;
enumNames = names;
}
void ConfigParam::setString(QString val)
{
type = CFG_T_QSTRING;
valString = val;
}
void ConfigParam::setBool(bool val)
{
type = CFG_T_BOOL;
valInt = val;
}

71
configparam.h Normal file
View File

@@ -0,0 +1,71 @@
/*
Original copyright 2018 Benjamin Vedder benjamin@vedder.se and the VESC Tool project ( https://github.com/vedderb/vesc_tool )
Forked to:
Copyright 2018 Danny Bokma github@diebie.nl (https://github.com/DieBieEngineering/DieBieMS-Tool)
Now forked to:
Copyright 2019 - 2020 Kevin Dionne kevin.dionne@ennoid.me (https://github.com/EnnoidMe/ENNOID-BMS-Tool)
This file is part of ENNOID-BMS Tool.
ENNOID-BMS Tool is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
ENNOID-BMS Tool is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CONFIGPARAM_H
#define CONFIGPARAM_H
#include <QImage>
#include "datatypes.h"
class ConfigParam
{
public:
ConfigParam();
void reset();
void setInt(int val, int max = 99, int min = 0, int step = 0);
void setDouble(double val, double max = 99.0, double min = 0.0,
double step = 1.0, int decimals = 2);
void setDoubleTx(VESC_TX_T tx, double scale);
void setEnum(int val, QStringList names);
void setString(QString val);
void setBool(bool val);
CFG_T type;
QString longName;
QString description;
QString cDefine;
double valDouble;
int valInt;
QString valString;
QStringList enumNames;
double maxDouble;
double minDouble;
double stepDouble;
int editorDecimalsDouble;
int maxInt;
int minInt;
int stepInt;
VESC_TX_T vTx;
double vTxDoubleScale;
QString suffix;
double editorScale;
bool editAsPercentage;
bool showDisplay;
bool transmittable;
};
#endif // CONFIGPARAM_H

1559
configparams.cpp Normal file

File diff suppressed because it is too large Load Diff

151
configparams.h Normal file
View File

@@ -0,0 +1,151 @@
/*
Original copyright 2018 Benjamin Vedder benjamin@vedder.se and the VESC Tool project ( https://github.com/vedderb/vesc_tool )
Forked to:
Copyright 2018 Danny Bokma github@diebie.nl (https://github.com/DieBieEngineering/DieBieMS-Tool)
Now forked to:
Copyright 2019 - 2020 Kevin Dionne kevin.dionne@ennoid.me (https://github.com/EnnoidMe/ENNOID-BMS-Tool)
This file is part of ENNOID-BMS Tool.
ENNOID-BMS Tool is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
ENNOID-BMS Tool is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CONFIGPARAMS_H
#define CONFIGPARAMS_H
#include <QObject>
#include <QHash>
#include <QXmlStreamWriter>
#include <QXmlStreamReader>
#include "configparam.h"
#include "vbytearray.h"
class ConfigParams : public QObject
{
Q_OBJECT
public:
explicit ConfigParams(QObject *parent = 0);
void addParam(const QString &name, ConfigParam param);
void deleteParam(const QString &name);
Q_INVOKABLE void setUpdateOnly(const QString &name);
Q_INVOKABLE QString getUpdateOnly();
Q_INVOKABLE bool getUpdatesEnabled() const;
Q_INVOKABLE void setUpdatesEnabled(bool updatesEnabled);
void clearParams();
void clearAll();
Q_INVOKABLE bool hasParam(const QString &name);
ConfigParam *getParam(const QString &name);
ConfigParam getParamCopy(const QString &name) const;
void setParamValue(const QString &name, QVariant val);
Q_INVOKABLE bool isParamDouble(const QString &name);
Q_INVOKABLE bool isParamInt(const QString &name);
Q_INVOKABLE bool isParamEnum(const QString &name);
Q_INVOKABLE bool isParamQString(const QString &name);
Q_INVOKABLE bool isParamBool(const QString &name);
Q_INVOKABLE double getParamDouble(const QString &name);
Q_INVOKABLE int getParamInt(const QString &name);
Q_INVOKABLE int getParamEnum(const QString &name);
Q_INVOKABLE QString getParamQString(const QString &name);
Q_INVOKABLE bool getParamBool(const QString &name);
Q_INVOKABLE QString getLongName(const QString &name);
Q_INVOKABLE QString getDescription(const QString &name);
Q_INVOKABLE double getParamMaxDouble(const QString &name);
Q_INVOKABLE double getParamMinDouble(const QString &name);
Q_INVOKABLE double getParamStepDouble(const QString &name);
Q_INVOKABLE int getParamDecimalsDouble(const QString &name);
Q_INVOKABLE int getParamMaxInt(const QString &name);
Q_INVOKABLE int getParamMinInt(const QString &name);
Q_INVOKABLE int getParamStepInt(const QString &name);
Q_INVOKABLE QStringList getParamEnumNames(const QString &name);
Q_INVOKABLE double getParamEditorScale(const QString &name);
Q_INVOKABLE QString getParamSuffix(const QString &name);
Q_INVOKABLE bool getParamEditAsPercentage(const QString &name);
Q_INVOKABLE bool getParamShowDisplay(const QString &name);
Q_INVOKABLE bool getParamTransmittable(const QString &name);
QStringList getParamOrder() const;
void setParamOrder(const QStringList &order);
QWidget *getEditor(const QString &name, QWidget *parent = 0);
void getParamSerial(VByteArray &vb, const QString &name);
void setParamSerial(VByteArray &vb, const QString &name, QObject *src = 0);
QStringList getSerializeOrder() const;
void setSerializeOrder(const QStringList &serializeOrder);
void clearSerializeOrder();
void serialize(VByteArray &vb);
void deSerialize(VByteArray &vb);
void getXML(QXmlStreamWriter &stream, QString configName);
bool setXML(QXmlStreamReader &stream, QString configName);
bool saveXml(QString fileName, QString configName);
bool loadXml(QString fileName, QString configName);
QString xmlStatus();
void getParamsXML(QXmlStreamWriter &stream);
bool setParamsXML(QXmlStreamReader &stream);
bool saveParamsXml(QString fileName);
bool loadParamsXml(QString fileName);
bool saveCDefines(const QString &fileName, bool wrapIfdef = false);
QStringList checkDifference(ConfigParams *config);
// Operators
ConfigParams& operator=(const ConfigParams &other);
signals:
void paramChangedDouble(QObject *src, QString name, double newParam);
void paramChangedInt(QObject *src, QString name, int newParam);
void paramChangedEnum(QObject *src, QString name, int newParam);
void paramChangedQString(QObject *src, QString name, QString newParam);
void paramChangedBool(QObject *src, QString name, bool newParam);
void updateRequested();
void updateRequestDefault();
void updated();
void stored();
void savingXml();
public slots:
void updateParamDouble(QString name, double param, QObject *src = 0);
void updateParamInt(QString name, int param, QObject *src = 0);
void updateParamEnum(QString name, int param, QObject *src = 0);
void updateParamString(QString name, QString param, QObject *src = 0);
void updateParamBool(QString name, bool param, QObject *src = 0);
void requestUpdate();
void requestUpdateDefault();
void updateDone();
void storingDone();
private:
QHash<QString, ConfigParam> mParams;
QStringList mParamList;
QString mUpdateOnlyName;
bool mUpdatesEnabled;
QStringList mSerializeOrder;
QString mXmlStatus;
bool almostEqual(float A, float B, float eps);
};
#endif // CONFIGPARAMS_H

189
configparamsgetter.cpp Normal file
View File

@@ -0,0 +1,189 @@
#include "configparamsgetter.h"
//bool BmsSettingsWidget::addParamRow(QLineEdit* edit, ConfigParams *params, QString paramName)
bool getSerialNumber(QLabel* lab, ConfigParams *params, QString paramName)
{
// (*) serial number is inside a DOUBLE value inside the BMS
// (**) But we need only the INT part of that double value
bool res = false;
QWidget *editor = params->getEditor(paramName);
QString name = params->getLongName(paramName);
if (editor && !name.isEmpty())
{
if(editor->property("type").toInt() == CFG_T_DOUBLE) // (*)
{
auto val = params->getParamDouble(paramName);
quint64 valUint64 = quint64(val); // (**)
lab->setText( QString::number(valUint64) );
res = true;
}
}
return res;
}
bool getSerialNumber(QLineEdit* edit, ConfigParams *params, QString paramName)
{
// (*) serial number is inside a DOUBLE value inside the BMS
// (**) But we need only the INT part of that double value
bool res = false;
QWidget *editor = params->getEditor(paramName);
QString name = params->getLongName(paramName);
if (editor && !name.isEmpty())
{
if(editor->property("type").toInt() == CFG_T_DOUBLE) // (*)
{
auto val = params->getParamDouble(paramName);
quint64 valUint64 = quint64(val); // (**)
edit->setText( QString::number(valUint64) );
res = true;
}
}
return res;
}
bool addParamRow(QLineEdit* edit, ConfigParams *params, QString paramName)
{
bool res = false;
QWidget *editor = params->getEditor(paramName);
//ConfigParam &p = params[paramName];
//ConfigParam &p = mParams[name];
QString name = params->getLongName(paramName);
if (editor && !name.isEmpty())
{
//int row = rowCount();
//setRowCount(row + 1);
//QTableWidgetItem *item = new QTableWidgetItem(name);
//item->setFlags(item->flags() & ~Qt::ItemIsEditable);
//setItem(row, 0, item);
//setCellWidget(row, 1, editor);
//edit->setText(editor->);
auto vtype = editor->property("type");
auto type = vtype.toInt();
//CFG_T_UNDEFINED = 0,
switch (type)
{
case CFG_T_INT:
case CFG_T_ENUM:
{
auto val = params->getParamInt(paramName);
edit->setText(QString::number(val));
break;
}
case CFG_T_DOUBLE:
{
auto val = params->getParamDouble(paramName);
edit->setText(QString::number(val));
break;
}
case CFG_T_QSTRING:
{
auto val = params->getParamQString(paramName);
edit->setText(val);
break;
}
case CFG_T_BOOL:
{
auto val = params->getParamBool(paramName);
edit->setText(val ? "true" : "false");
break;
}
default:
{
auto val = editor->property("value");
auto valString = val.toString();
edit->setText(valString.isEmpty() ? "Erro Read Value" : valString);
break;
}
}
auto vsuffix = editor->property("suffix");
auto suffix = vsuffix.toString();
auto veditorDecimalsDouble = editor->property("editorDecimalsDouble");
auto editorDecimalsDouble = veditorDecimalsDouble.toDouble();
auto veditAsPercentage = editor->property("editAsPercentage");
auto editAsPercentage = veditAsPercentage.toBool();
res = true;
//resizeColumnToContents(0);
//resizeRowsToContents();
}
return res;
}
bool addParamRowEx(QLineEdit* edit, ConfigParams *params, QString paramName)
{
bool res = false;
QWidget *editor = params->getEditor(paramName);
QString name = params->getLongName(paramName);
if (editor && !name.isEmpty()) {
//int row = rowCount();
//setRowCount(row + 1);
//QTableWidgetItem *item = new QTableWidgetItem(name);
//item->setFlags(item->flags() & ~Qt::ItemIsEditable);
//setItem(row, 0, item);
//setCellWidget(row, 1, editor);
//edit->setText(editor->);
auto vtype = editor->property("type");
auto type = vtype.toInt();
auto vval = editor->property("value");
//CFG_T_UNDEFINED = 0,
switch(type)
{
case(CFG_T_INT):
case(CFG_T_ENUM):
{
auto val = vval.toInt();
edit->setText(QString::number(val));
break;
}
case(CFG_T_DOUBLE):
{
auto val = vval.toDouble();
edit->setText(QString::number(val));
break;
}
case(CFG_T_QSTRING):
{
auto val = vval.toString();
edit->setText(val);
break;
}
case(CFG_T_BOOL):
{
auto val = vval.toBool();
edit->setText(val ? "true" : "false");
break;
}
default:
auto valString = vval.toString();
edit->setText(valString.isEmpty() ? "Erro Read Value" : valString);
break;
}
auto vsuffix = editor->property("suffix");
auto suffix = vsuffix.toString();
auto veditorDecimalsDouble = editor->property("editorDecimalsDouble");
auto editorDecimalsDouble = veditorDecimalsDouble.toDouble();
auto veditAsPercentage = editor->property("editAsPercentage");
auto editAsPercentage = veditAsPercentage.toBool();
res = true;
//resizeColumnToContents(0);
//resizeRowsToContents();
}
return res;
}

18
configparamsgetter.h Normal file
View File

@@ -0,0 +1,18 @@
#ifndef CONFIGPARAMSGETTER_H
#define CONFIGPARAMSGETTER_H
#include <QWidget>
#include <QLineEdit>
#include <QLabel>
#include "configparams.h"
//class SettingsWidget : public QWidget
//{
bool getSerialNumber(QLabel* lab, ConfigParams *params, QString paramName);
bool getSerialNumber(QLineEdit* edit, ConfigParams *params, QString paramName);
bool addParamRow(QLineEdit* edit, ConfigParams *params, QString paramName);
bool addParamRowEx(QLineEdit* edit, ConfigParams *params, QString paramName);
//};
#endif // CONFIGPARAMSGETTER_H

535
configurationpage.cpp Normal file
View File

@@ -0,0 +1,535 @@
#include "configurationpage.h"
#include "ui_configurationpage.h"
#include <QScrollBar>
#include "connectandenterpage.h"
ConfigurationPage::ConfigurationPage(QWidget *parent) :
QFrame(parent),
ui(new Ui::ConfigurationPage)
{
ui->setupUi(this);
connect( ui->pbLinkSerialNumber , &QPushButton::clicked, this, [=]() { setPositionInPixelsToScrollTo(PosSerialNumber ); } );
connect( ui->pbLinkConfiguration , &QPushButton::clicked, this, [=]() { setPositionInPixelsToScrollTo(PosConfiguration ); } );
connect( ui->pbLinkSoc , &QPushButton::clicked, this, [=]() { setPositionInPixelsToScrollTo(PosSoc ); } );
connect( ui->pbLinkRestrictions , &QPushButton::clicked, this, [=]() { setPositionInPixelsToScrollTo(PosRestrictions ); } );
connect( ui->pbLinkCellsConfiguration , &QPushButton::clicked, this, [=]() { setPositionInPixelsToScrollTo(PosCellsConfiguration ); } );
connect( ui->pbLinkBalancingConfiguration, &QPushButton::clicked, this, [=]() { setPositionInPixelsToScrollTo(PosBalancingConfiguration); } );
connect( ui->pbLinkOutputsSetting , &QPushButton::clicked, this, [=]() { setPositionInPixelsToScrollTo(PosOutputsSetting ); } );
connect( ui->pbLinkClaibrateZero , &QPushButton::clicked, this, [=]() { setPositionInPixelsToScrollTo(PosClaibrateZero ); } );
connect( ui->pbReadDefaultSettingsFromFile , &QPushButton::clicked, this, &ConfigurationPage::onPbReadDefaultSettingsFromFileClicked);
connect( ui->pbReadCurrentSettingsFromBms , &QPushButton::clicked, this, &ConfigurationPage::onPbReadCurrentSettingsFromBmsClicked);
connect( ui->pbWriteCurrentValuesToBms , &QPushButton::clicked, this, &ConfigurationPage::onPbWriteCurrentValuesToBmsClicked);
connect( ui->pbWriteToNonVolatileBmsMemory , &QPushButton::clicked, this, &ConfigurationPage::onPbWriteToNonVolatileBmsMemoryClicked);
connect(ui->bmsSettings, &BmsSettings::showStatusInfo, this, &ConfigurationPage::showStatusInfo);
connect(ui->bmsSettings, &BmsSettings::showMessageDialog, this, &ConfigurationPage::showMessageDialog);
connect(ui->bmsSettings, &BmsSettings::sendMessage, this, &ConfigurationPage::sendMessage);
connect(this, &ConfigurationPage::loadParams, ui->bmsSettings, &BmsSettings::onLoadParams);
initTimerForStatusBar();
initTimerForProgressBar();
initTimerForOneSecondDelayAfterButtonClick();
checkIfButtonTextFitsInButton();
}
BMSInterface *ConfigurationPage::bms() const
{
return mDieBieMS;
}
void ConfigurationPage::setDieBieMS(BMSInterface *dieBieMS)
{
mDieBieMS = dieBieMS;
ui->bmsSettings->setDieBieMS(mDieBieMS);
// SEND_STATUS_INFO //
connect(mDieBieMS, &BMSInterface::statusMessage, this, &ConfigurationPage::showStatusInfo);
connect(mDieBieMS->bmsConfig(), &ConfigParams::updateRequested, ui->bmsSettings, &BmsSettings::onBmsconfUpdateRequested);
connect(mDieBieMS->bmsConfig(), &ConfigParams::updateRequestDefault, ui->bmsSettings, &BmsSettings::onBmsconfUpdateRequestDefault);
connect(mDieBieMS->bmsConfig(), &ConfigParams::updated, ui->bmsSettings, &BmsSettings::onBmsconfUpdated);
connect(mDieBieMS->bmsConfig(), &ConfigParams::updated, this, &ConfigurationPage::onBmsconfUpdatedOrStored);
connect(mDieBieMS->bmsConfig(), &ConfigParams::stored, ui->bmsSettings, &BmsSettings::onBmsconfStored);
connect(mDieBieMS->bmsConfig(), &ConfigParams::stored, this, &ConfigurationPage::onBmsconfUpdatedOrStored);
//connect(mDieBieMS->bmsConfig(), &ConfigParams::updated, this, &ConfigurationPage::bmsconfUpdated);
//void ConfigParams::requestUpdate() => emit updateRequested();
//void ConfigParams::requestUpdateDefault() => emit updateRequestDefault();
//void ConfigParams::updateDone() => emit updated();
//void ConfigParams::storingDone() => emit stored();
//void sigBmsconfUpdateRequested();
//void sigBmsconfUpdateRequestDefault();
//void sigBmsconfUpdated();
//void sigBmsconfStored();
//connect(ui->bmsSettings, &BmsSettings::sendMessage, this, &ConfigurationPage::sendMessage);
//connect(this, &ConfigurationPage::loadParams, ui->bmsSettings, &BmsSettings::onLoadParams);
/*
connect(mPacket, (dataToSend(QByteArray&)),this, (packetDataToSend(QByteArray&)));
connect(mPacket, (packetReceived(QByteArray&)),this, (packetReceived(QByteArray&)));
connect(mCommands, (dataToSend(QByteArray&)),this, (cmdDataToSend(QByteArray&)));
connect(mCommands, (fwVersionReceived(int,int,QString,QByteArray)),this, SLOT(fwVersionReceived(int,int,QString,QByteArray)));
connect(mCommands, (ackReceived(QString)), this, (ackReceived(QString)));
connect(mbmsConfig, (updated()), this, (bmsconfUpdated()));
connect(mbmsConfig, (stored()), this, (bmsconfStored()));
connect(mTimer, (timeout()), this, (timerSlot()));
*/
}
ConfigurationPage::~ConfigurationPage()
{
delete ui;
}
void ConfigurationPage::checkIfButtonTextFitsInButton()
{
int textWidth = QFontMetrics(ui->pbWriteCurrentValuesToBms->font()).width(ui->pbWriteCurrentValuesToBms->text());
int btnWidth = ui->pbWriteCurrentValuesToBms->width();
if(btnWidth <= textWidth)
ui->pbWriteCurrentValuesToBms->setText(tr("Записать текущие значения\nв BMS"));
}
void ConfigurationPage::setPositionInPixelsToScrollTo(int positionInPixelsToScrollTo)
{
if(positionInPixelsToScrollTo != PosSerialNumber)
positionInPixelsToScrollTo -= 15;
ui->scrollAreaBmsSettings->verticalScrollBar()->setValue(positionInPixelsToScrollTo);
}
//void ConfigurationPage::bmsconfUpdated()
//{
// //mMcConfRead = false;
//}
#if 0
void ConfigurationPage::onTimerSlot()
{
// Update status label
if (mStatusInfoTime) {
mStatusInfoTime--;
if (!mStatusInfoTime) {
//mStatusLabel->setStyleSheet(qApp->styleSheet());
}
}
/*else {
QString str = mDieBieMS->getConnectedPortName();
if (str != mStatusLabel->text()) {
mStatusLabel->setText(mDieBieMS->getConnectedPortName());
static QString statusLast = "";
if (str != statusLast) {
mPageDebugPrint->printConsole("Status: " + str + "<br>");
statusLast = str;
}
}
}*/
/* // CAN fwd
if (ui->actionCanFwd->isChecked() != mDieBieMS->commands()->getSendCan()) {
ui->actionCanFwd->setChecked(mDieBieMS->commands()->getSendCan());
}
// RT data only every 5 iterations
if (ui->actionRtData->isChecked()) {
static int values_cnt = 0;
static int cells_cnt = 0;
values_cnt++;
if (values_cnt >= 5) {
values_cnt = 0;
mDieBieMS->commands()->getValues();
}
cells_cnt++;
if (cells_cnt >= 20) {
cells_cnt = 0;
mDieBieMS->commands()->getCells();
}
}
// Send alive command once every 10 iterations
if (ui->actionSendAlive->isChecked()) {
static int alive_cnt = 0;
alive_cnt++;
if (alive_cnt >= 10) {
alive_cnt = 0;
mDieBieMS->commands()->sendAlive();
}
}
*/
// Read configuration it isn't read since starting VESC Tool
if (mDieBieMS->isPortConnected()) {
static int conf_cnt = 0;
conf_cnt++;
if (conf_cnt >= 20) {
conf_cnt = 0;
if (!mMcConfRead) {
mDieBieMS->commands()->getBMSconf();
mMcConfRead = true;
}
}
}
/* // Disable all data streaming when uploading firmware
if (mDieBieMS->commands()->getFirmwareUploadProgress() > 0.1) {
ui->actionSendAlive->setChecked(false);
ui->actionRtData->setChecked(false);
}
// Run startup checks
static bool has_run_start_checks = false;
if (!has_run_start_checks) {
has_run_start_checks = true;
checkUdev();
Utility::checkVersion(mDieBieMS);
}
*/
}
#endif
/*
{
// connect(mTimer, SIGNAL(timeout()),this, SLOT(timerSlot()));
connect(mDieBieMS, &BMSInterface::statusMessage, this, &ConnectAndEnterPage::showStatusInfo);
connect(mDieBieMS, &BMSInterface::messageDialog, this, &ConnectAndEnterPage::showMessageDialog);
connect(mDieBieMS, &BMSInterface::serialPortNotWritable, this, &ConnectAndEnterPage::serialPortNotWritable);
connect(mDieBieMS->commands(), &Commands::bmsConfigCheckResult, this, &ConnectAndEnterPage::bmsConfigCheckResult);
// connect(ui->actionAboutQt, SIGNAL(triggered(bool)),qApp, SLOT(aboutQt()));
// Remove the menu with the option to hide the toolbar
// ui->mainToolBar->setContextMenuPolicy(Qt::PreventContextMenu);
mDieBieMS->bmsConfig()->loadParamsXml("://res/config.xml");
mDieBieMS->infoConfig()->loadParamsXml("://res/info.xml");
// reloadPages();
connect(mDieBieMS->bmsConfig(), SIGNAL(updated()), this, SLOT(bmsconfUpdated()));
}
//*/
void ConfigurationPage::onTimerSlot()
{
// Update status label
if (mStatusInfoTime) {
mStatusInfoTime--;
if (!mStatusInfoTime) {
// clear Status Label
ui->lbStatus->setText("");
ui->lbStatus->setStyleSheet(qApp->styleSheet());
}
}
}
void ConfigurationPage::showStatusInfo(QString info, bool isGood)
{
if (isGood) {
ui->lbStatus->setStyleSheet("QLabel { background-color : lightgreen; color : black; }");
mPageDebugPrint->printConsole("Status: " + info + "<br>");
} else {
ui->lbStatus->setStyleSheet("QLabel { background-color : red; color : black; }");
mPageDebugPrint->printConsole("<font color=\"red\">Status: " + info + "</font><br>");
}
mStatusInfoTime = 160;
ui->lbStatus->setText(info);
}
// pbLoadSettingsFromFile
//void ConfigurationPage::on_pbLoadSettingsFromFile_clicked()
//{
// bool NOTHING_IS_CONNECTED_HERE_ACCORDING_SPECIFICATION_FROM_CUSTOMER = true;
//}
//// pbLoadCurrentSettingsFromBms
//void ConfigurationPage::on_pbLoadCurrentSettingsFromBms_clicked()
//{
// // Загрузить настройки из файла
// // Загрузить текущие настройки\nиз BMS
// // actionReadBMScconf
// mDieBieMS->commands()->getBMSconf();
// // ? // mDieBieMS->commands()->getBMSconfDefault();
//}
//// pbWriteToNonVolatileBmsMemory
//void ConfigurationPage::on_pbWriteToNonVolatileBmsMemory_clicked()
//{
// // Записать в энергонезависимую\nпамять BMS
// // actionStoreBMScconf
// mDieBieMS->commands()->storeBMSConfig();
//}
// pbWriteCurrentValuesToBms
////////////////////////////////////////////////////////////
/*
start 0 12 = Default that is not in bms it's in somekind of real offline file (we do NOT have connection to bms but there parameters show up somehow)
connect 8 8 Config updated with default params not from file, but from default params of bms
deflt bms edit | bms edit
^ - 8 8 7 | 8 8 read bms conf
-> - 8 7 7 | 7 8 read default 8 is defaul - that was on the start
v - 8 8 7 | 7 7 write bms config
S - 8 ? ? | ? ? save currect config from bms into outside file to the flash card
before | after
*/
void ConfigurationPage::onPbReadDefaultSettingsFromFileClicked()
{
if(mIsOneSedondPassed)
{
mOneSecondWait->start();
mIsOneSedondPassed = false;
if(mIsOperationFinished)
{
mIsOperationFinished = false;
onStartPercents();
// bms edit | bms edit
//-> - 7 7 | 7 8 read default 8 is defaul - that was on the start
// Загрузить настройки\nпо умолчанию
mDieBieMS->commands()->getBMSconfDefault();
}
}
}
void ConfigurationPage::onPbReadCurrentSettingsFromBmsClicked()
{
if(mIsOneSedondPassed)
{
mOneSecondWait->start();
mIsOneSedondPassed = false;
if(mIsOperationFinished)
{
mIsOperationFinished = false;
onStartPercents();
// bms edit | bms edit
//^ - 7 8 | 7 7 read bms conf
mDieBieMS->commands()->getBMSconf();
}
}
}
//// pbLoadCurrentSettingsFromBms
void ConfigurationPage::onPbWriteCurrentValuesToBmsClicked()
{
if(mIsOneSedondPassed)
{
mOneSecondWait->start();
mIsOneSedondPassed = false;
if(mIsOperationFinished)
{
mIsOperationFinished = false;
onStartPercents();
// Записать текущие значения в BMS
// actionWriteBMScconf
mDieBieMS->commands()->setBMSconf();
}
}
}
//// pbWriteToNonVolatileBmsMemory
void ConfigurationPage::onPbWriteToNonVolatileBmsMemoryClicked()
{
if(mIsOneSedondPassed)
{
mOneSecondWait->start();
mIsOneSedondPassed = false;
if(mIsOperationFinished)
{
mIsOperationFinished = false;
onStartPercents();
// Записать в энергонезависимую\nпамять BMS
// actionStoreBMScconf
mDieBieMS->commands()->storeBMSConfig();
}
}
}
/*
void ConfigurationPage::onPbReadDefaultSettingsFromFileClicked()
{
// bms edit | bms edit
//-> - 7 7 | 7 8 read default 8 is defaul - that was on the start
// Загрузить настройки\nпо умолчанию
mDieBieMS->commands()->getBMSconfDefault();
}
void ConfigurationPage::onPbReadCurrentSettingsFromBmsClicked()
{
// bms edit | bms edit
//^ - 7 8 | 7 7 read bms conf
mDieBieMS->commands()->getBMSconf();
}
//// pbLoadCurrentSettingsFromBms
void ConfigurationPage::onPbWriteCurrentValuesToBmsClicked()
{
// Записать текущие значения в BMS
// actionWriteBMScconf
mDieBieMS->commands()->setBMSconf();
}
//// pbWriteToNonVolatileBmsMemory
void ConfigurationPage::onPbWriteToNonVolatileBmsMemoryClicked()
{
// Записать в энергонезависимую\nпамять BMS
// actionStoreBMScconf
mDieBieMS->commands()->storeBMSConfig();
}
*/
//
//
//
void ConfigurationPage::onBmsconfUpdatedOrStored()
{
mIsOperationFinished = true;
//onFinishPercents();
}
void ConfigurationPage::initTimerForStatusBar()
{
mTimer = new QTimer(this);
connect(mTimer, &QTimer::timeout, this, &ConfigurationPage::onTimerSlot);
mTimer->start(20);
}
void ConfigurationPage::initTimerForProgressBar()
{
mTimerProgress = new QTimer(this);
mTimerProgress->setSingleShot(false);
mTimerProgress->setInterval(100);
connect(mTimerProgress, &QTimer::timeout, this, &ConfigurationPage::processPercents);
// timer->start(1000);
ui->progressBar->setValue(0);
}
void ConfigurationPage::initTimerForOneSecondDelayAfterButtonClick()
{
mOneSecondWait = new QTimer(this);
mOneSecondWait->setSingleShot(true);
mOneSecondWait->setInterval(10000);
connect(mOneSecondWait, &QTimer::timeout, this, [this]()
{
mIsOneSedondPassed = true;
});
mIsOneSedondPassed = true;
mIsOperationFinished = true;
}
void ConfigurationPage::processPercents()
{
mSeconds++;
int percents = mSeconds * (100.0 / mMaxSecs);
ui->progressBar->setValue(percents);
auto val = ui->progressBar->value();
if (val >= 0 && val < 50)
{
ui->progressBar->setStyleSheet("QProgressBar::chunk { background-color:lime; };");
}
else if (val >= 50 && val < 85)
{
if(mIsOperationFinished)
{
mIsOneSedondPassed = true;
mIsOperationFinished = true;
onStopPercents();
}
ui->progressBar->setStyleSheet("QProgressBar::chunk { background-color:yellow; };");
}
else if (val >= 85 && val < 95)
{
ui->progressBar->setStyleSheet("QProgressBar::chunk { background-color:red; };");
}
else if (val >= 95)
{
ui->progressBar->setStyleSheet("QProgressBar::chunk { background-color:red; };");
mIsOneSedondPassed = true;
mIsOperationFinished = true;
onStopPercents();
showStatusInfo("ERROR! SOMETHING WENT WRONG", false);
}
//if (ui->progressBar->value() == 100)
// onFinishPercents();
}
void ConfigurationPage::onStartPercents()
{
onStopPercents();
mTimerProgress->start();
}
void ConfigurationPage::onStopPercents()
{
mSeconds = 0;
mTimerProgress->stop();
ui->progressBar->setValue(0);
//mIsOneSedondPassed = true;
//mIsOperationFinished = true;
}
void ConfigurationPage::onFinishPercents()
{
mTimerProgress->stop();
mSeconds = 0;
ui->progressBar->setValue(100);
}

119
configurationpage.h Normal file
View File

@@ -0,0 +1,119 @@
#ifndef CONFIGURATIONPAGE_H
#define CONFIGURATIONPAGE_H
#include <QWidget>
#include <QLabel>
#include "bmsinterface.h"
#include "debugprintpage.h"
namespace Ui {
class ConfigurationPage;
}
class ConfigurationPage : public QFrame
{
Q_OBJECT
public:
explicit ConfigurationPage(QWidget *parent = nullptr);
~ConfigurationPage();
enum ElementsPixelsPositions {
PosSerialNumber = 0
,PosConfiguration = 240
,PosSoc = 490
,PosRestrictions = 770
,PosCellsConfiguration = 1150
,PosBalancingConfiguration = 1580
,PosOutputsSetting = 1860
,PosClaibrateZero = 2840
};
BMSInterface *bms() const;
void setDieBieMS(BMSInterface *dieBieMS);
void setDebugPrintPage(DebugPrintPage *page) {
mPageDebugPrint = page;
}
signals:
void loadParams();
void sendMessage(QString message);
void showMessageDialog(const QString &title, const QString &msg, bool isGood, bool richText);
//void sigBmsconfUpdateRequested();
//void sigBmsconfUpdateRequestDefault();
//void sigBmsconfUpdated();
//void sigBmsconfStored();
private slots:
void showStatusInfo(QString info, bool isGood);
void onTimerSlot();
void onBmsconfUpdatedOrStored();
void onPbReadDefaultSettingsFromFileClicked();
void onPbReadCurrentSettingsFromBmsClicked();
void onPbWriteCurrentValuesToBmsClicked();
void onPbWriteToNonVolatileBmsMemoryClicked();
/*
//void on_pbLoadSettingsFromFile_clicked();
//void on_pbLoadCurrentSettingsFromBms_clicked();
void on_pbWriteToNonVolatileBmsMemory_clicked();
//void on_pbWriteCurrentValuesToBms_clicked();
//void bmsconfUpdated();
void on_pbReadDefaultSettingsFromFile_clicked();
void on_pbReadCurrentSettingsFromBms_clicked();
void on_pbWriteCurrentValuesToBms_clicked();
*/
private:
Ui::ConfigurationPage *ui;
BMSInterface *mDieBieMS;
DebugPrintPage *mPageDebugPrint;
void initTimerForStatusBar();
void initTimerForProgressBar();
void initTimerForOneSecondDelayAfterButtonClick();
void onStartPercents();
void onStopPercents();
void onFinishPercents();
void processPercents();
double mSeconds = 0;
const double mMaxSecs = 200;
QTimer *mTimer;
int mStatusInfoTime = 0;
QTimer *mTimerProgress;
QTimer *mOneSecondWait;
bool mIsOneSedondPassed = true;
bool mIsOperationFinished = true;
void setPositionInPixelsToScrollTo(int positionInPixelsToScrollTo);
void checkIfButtonTextFitsInButton();
/*
DebugPrintPage *mPageDebugPrint;
QTimer *mTimer;
int mStatusInfoTime;
bool mMcConfRead = false;
void setPositionInPixelsToScrollTo(int positionInPixelsToScrollTo);
void checkIfButtonTextFitsInButton();
*/
};
#endif // CONFIGURATIONPAGE_H

707
configurationpage.ui Normal file
View File

@@ -0,0 +1,707 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ConfigurationPage</class>
<widget class="QFrame" name="ConfigurationPage">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>1084</width>
<height>755</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<property name="styleSheet">
<string notr="true">/* background-color: #f7f8fc;*/
QScrollArea#scrollAreaBmsSettings, #bmsSettings
{
background-color: #f7f8fc; /*COLOR OF THE LEFT PART WITH GROUPBOXES*/
}
QScrollArea#scrollAreaBmsSettings, #bmsSettings
{
background-color: #f7f8fc; /*COLOR OF THE RIGHT PART WITH BUTTONS*/
}
QLabel#lbIconLogo
{
/*background-image: url(:/images/cubo-verde-logo.png);*/
border-image: url(:/res/cubo-verde-logo-white.png) 0 0 0 0 stretch stretch;
}
QLabel#lbHeaderLanguageIcon
{
/*background-image: url(:/images/cubo-verde-logo.png);*/
border-image: url(:/res/language-russ.png) 0 0 0 0 stretch stretch;
}
QPushButton#pbMainMenuBarHideMenu,
#pbMainMenuBarAkbMonitor,
#pbMainMenuBarCellsMonitor,
#pbMainMenuBarConfiguration,
#pbMainMenuBarVisualization,
#pbMainMenuBarHistory,
#pbMainMenuBarSeriveOfBms,
#pbMainMenuBarExit
{
color: #FFFFFF;
background-color: #009352;
border: 0px solid black;
background:#009352;
text-align: left;
padding-left: 85px;
font-size:17px;
}
QPushButton#pbMainMenuBarHideMenu
{
font-size:15px;
}
QPushButton#pbMainMenuBarHideMenu:hover,
#pbMainMenuBarAkbMonitor:hover,
#pbMainMenuBarCellsMonitor:hover,
#pbMainMenuBarConfiguration:hover,
#pbMainMenuBarVisualization:hover,
#pbMainMenuBarHistory:hover,
#pbMainMenuBarSeriveOfBms:hover,
#pbMainMenuBarExit:hover
{
background-color: #01673a;
}
/*------------------*/
QPushButton#pbConnect,
#pbWriteCurrentValuesToBms,
#pbReadDefaultSettingsFromFile,
#pbReadCurrentSettingsFromBms
{
color:black;
background-color: #f7f8fc; /* white;*/
font-weight:bold;
font-size: 14px;
border-color: #009352;
border-style: solid;
border-width: 1px;
border-radius: 6px;
}
QPushButton#pbConnect:hover:pressed,
#pbWriteCurrentValuesToBms:hover:pressed,
#pbReadDefaultSettingsFromFile:hover:pressed,
#pbReadCurrentSettingsFromBms:hover:pressed
{
background-color:lightgrey;
}
QPushButton#pbConnect:hover:!pressed,
#pbWriteCurrentValuesToBms:hover:!pressed,
#pbReadDefaultSettingsFromFile:hover:!pressed,
#pbReadCurrentSettingsFromBms:hover:!pressed
{
/* background-color: #0FF73a;*/
}
/*pbWriteCurrentValuesToBms*/
QPushButton#pbWriteToNonVolatileBmsMemory
{
background-color:#009352;
color:white;
font-weight:bold;
font-size: 14px;
border-color: #009352;
border-style: solid;
border-width: 1px;
border-radius: 6px;
}
QPushButton#pbWriteToNonVolatileBmsMemory:hover:pressed
{
background-color:#01673a;
}
QPushButton#pbWriteToNonVolatileBmsMemory:hover:!pressed
{
/* background-color: #01673a;*/
}
QPushButton#pbLinkSerialNumber,
#pbLinkSoc,
#pbLinkRestrictions,
#pbLinkClaibrateZero,
#pbLinkCellsConfiguration,
#pbLinkOutputsSetting,
#pbLinkBalancingConfiguration,
#pbLinkConfiguration
{
color:#009352;
text-decoration: none;
background: transparent;
border-color:transparent;
text-align:left;
border-radius: 1px;
text-decoration: underline;
}
QPushButton#pbLinkSerialNumber:hover:!pressed,
#pbLinkSoc:hover:!pressed,
#pbLinkRestrictions:hover:!pressed,
#pbLinkClaibrateZero:hover:!pressed,
#pbLinkCellsConfiguration:hover:!pressed,
#pbLinkOutputsSetting:hover:!pressed,
#pbLinkBalancingConfiguration:hover:!pressed,
#pbLinkConfiguration:hover:!pressed
{
/* text-decoration: underline;*/
color:#01673a;
}
QPushButton#pbLinkSerialNumber:hover:pressed,
#pbLinkSoc:hover:pressed,
#pbLinkRestrictions:hover:pressed,
#pbLinkClaibrateZero:hover:pressed,
#pbLinkCellsConfiguration:hover:pressed,
#pbLinkOutputsSetting:hover:pressed,
#pbLinkBalancingConfiguration:hover:pressed,
#pbLinkConfiguration:hover:pressed
{
color:#0093FF;
}
QComboBox#cboxComConnections
{
background-color:white;
border-radius:10px;
border-color: lightgrey;
border-style: solid;
border-width: 1px;
}
/*------------------*/
QGroupBox#gbSerialNumber,
QGroupBox#gbConfiguration,
QGroupBox#gbSoc,
QGroupBox#gbRestrictions,
QGroupBox#gbCellsConfiguration,
QGroupBox#gbBalancingConfiguration,
QGroupBox#gbOutputsSettings,
QGroupBox#gbZeroPoint {
background-color:white;
border-radius:10px;
border-color: lightgrey;
border-style: solid;
border-width: 1px;
}
/*-----------------------*/
QScrollBar {
border-radius: 10px;
background: #e8e9f1;
}
QScrollBar::vertical {
width: 10px;
margin: 0px 0px 0px 0px;
}
/*-----------------------------------*/
QScrollBar::sub-page:horizontal {
background: #e8e9f1;
border-top-left-radius: 9px;
border-bottom-left-radius: 9px;
}
QScrollBar::add-page:horizontal {
background: #e8e9f1;
border-top-right-radius: 9px;
border-bottom-right-radius: 9px;
}
QScrollBar::sub-page:vertical {
background: #e8e9f1;
border-top-left-radius: 9px;
border-top-right-radius: 9px;
}
QScrollBar::add-page:vertical {
background: #e8e9f1;
border-bottom-left-radius: 9px;
border-bottom-right-radius: 9px;
}
/*-----------------------------------*/
QScrollBar::up-arrow,
QScrollBar::sub-line,
QScrollBar::add-line {
width: 0px;
height: 0px;
}
QScrollBar::handle {
background: #009352;
border-radius: 5px;
}
</string>
</property>
<widget class="QLabel" name="lbIconLogo">
<property name="geometry">
<rect>
<x>80</x>
<y>40</y>
<width>111</width>
<height>111</height>
</rect>
</property>
<property name="contextMenuPolicy">
<enum>Qt::NoContextMenu</enum>
</property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="text">
<string/>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
<widget class="QLabel" name="lbHeaderLanguageIcon">
<property name="geometry">
<rect>
<x>1300</x>
<y>30</y>
<width>31</width>
<height>21</height>
</rect>
</property>
<property name="font">
<font>
<family>Segoe WP SemiLight</family>
<pointsize>12</pointsize>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="styleSheet">
<string notr="true">color:#0A72BA;
font-weight:bold;</string>
</property>
<property name="text">
<string/>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
</property>
</widget>
<widget class="QPushButton" name="pbLinkSerialNumber">
<property name="geometry">
<rect>
<x>770</x>
<y>100</y>
<width>261</width>
<height>31</height>
</rect>
</property>
<property name="font">
<font>
<family>Segoe WP SemiLight</family>
<pointsize>14</pointsize>
<weight>50</weight>
<bold>false</bold>
<underline>true</underline>
<strikeout>false</strikeout>
</font>
</property>
<property name="styleSheet">
<string notr="true"/>
</property>
<property name="text">
<string>Серийный номер</string>
</property>
</widget>
<widget class="QPushButton" name="pbLinkConfiguration">
<property name="geometry">
<rect>
<x>770</x>
<y>140</y>
<width>261</width>
<height>31</height>
</rect>
</property>
<property name="font">
<font>
<family>Segoe WP SemiLight</family>
<pointsize>14</pointsize>
<weight>50</weight>
<bold>false</bold>
<underline>true</underline>
<strikeout>false</strikeout>
</font>
</property>
<property name="styleSheet">
<string notr="true"/>
</property>
<property name="text">
<string>Конфигурация</string>
</property>
</widget>
<widget class="QPushButton" name="pbLinkSoc">
<property name="geometry">
<rect>
<x>770</x>
<y>180</y>
<width>261</width>
<height>31</height>
</rect>
</property>
<property name="font">
<font>
<family>Segoe WP SemiLight</family>
<pointsize>14</pointsize>
<weight>50</weight>
<bold>false</bold>
<underline>true</underline>
<strikeout>false</strikeout>
</font>
</property>
<property name="styleSheet">
<string notr="true"/>
</property>
<property name="text">
<string>SOC</string>
</property>
</widget>
<widget class="QPushButton" name="pbLinkRestrictions">
<property name="geometry">
<rect>
<x>770</x>
<y>220</y>
<width>261</width>
<height>31</height>
</rect>
</property>
<property name="font">
<font>
<family>Segoe WP SemiLight</family>
<pointsize>14</pointsize>
<weight>50</weight>
<bold>false</bold>
<underline>true</underline>
<strikeout>false</strikeout>
</font>
</property>
<property name="styleSheet">
<string notr="true"/>
</property>
<property name="text">
<string>Ограничения</string>
</property>
</widget>
<widget class="QPushButton" name="pbLinkCellsConfiguration">
<property name="geometry">
<rect>
<x>770</x>
<y>260</y>
<width>261</width>
<height>31</height>
</rect>
</property>
<property name="font">
<font>
<family>Segoe WP SemiLight</family>
<pointsize>14</pointsize>
<weight>50</weight>
<bold>false</bold>
<underline>true</underline>
<strikeout>false</strikeout>
</font>
</property>
<property name="styleSheet">
<string notr="true"/>
</property>
<property name="text">
<string>Конфигурация ячеек</string>
</property>
</widget>
<widget class="QPushButton" name="pbLinkBalancingConfiguration">
<property name="geometry">
<rect>
<x>770</x>
<y>300</y>
<width>261</width>
<height>31</height>
</rect>
</property>
<property name="font">
<font>
<family>Segoe WP SemiLight</family>
<pointsize>14</pointsize>
<weight>50</weight>
<bold>false</bold>
<underline>true</underline>
<strikeout>false</strikeout>
</font>
</property>
<property name="styleSheet">
<string notr="true"/>
</property>
<property name="text">
<string>Конфигурация балансировки</string>
</property>
</widget>
<widget class="QPushButton" name="pbLinkOutputsSetting">
<property name="geometry">
<rect>
<x>770</x>
<y>340</y>
<width>261</width>
<height>31</height>
</rect>
</property>
<property name="font">
<font>
<family>Segoe WP SemiLight</family>
<pointsize>14</pointsize>
<weight>50</weight>
<bold>false</bold>
<underline>true</underline>
<strikeout>false</strikeout>
</font>
</property>
<property name="styleSheet">
<string notr="true"/>
</property>
<property name="text">
<string>Настройка выходов</string>
</property>
</widget>
<widget class="QPushButton" name="pbLinkClaibrateZero">
<property name="geometry">
<rect>
<x>770</x>
<y>380</y>
<width>261</width>
<height>31</height>
</rect>
</property>
<property name="font">
<font>
<family>Segoe WP SemiLight</family>
<pointsize>14</pointsize>
<weight>50</weight>
<bold>false</bold>
<underline>true</underline>
<strikeout>false</strikeout>
</font>
</property>
<property name="styleSheet">
<string notr="true"/>
</property>
<property name="text">
<string>Значение датчика точка &quot;0&quot;</string>
</property>
</widget>
<widget class="QPushButton" name="pbReadDefaultSettingsFromFile">
<property name="geometry">
<rect>
<x>770</x>
<y>440</y>
<width>281</width>
<height>51</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true"/>
</property>
<property name="text">
<string>Загрузить настройки
по умолчанию</string>
</property>
</widget>
<widget class="QPushButton" name="pbReadCurrentSettingsFromBms">
<property name="geometry">
<rect>
<x>770</x>
<y>500</y>
<width>281</width>
<height>51</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true"/>
</property>
<property name="text">
<string>Загрузить текущие настройки
из BMS</string>
</property>
</widget>
<widget class="QPushButton" name="pbWriteCurrentValuesToBms">
<property name="geometry">
<rect>
<x>770</x>
<y>560</y>
<width>281</width>
<height>51</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true"/>
</property>
<property name="text">
<string>Записать текущие значения в BMS</string>
</property>
</widget>
<widget class="QPushButton" name="pbWriteToNonVolatileBmsMemory">
<property name="geometry">
<rect>
<x>770</x>
<y>620</y>
<width>281</width>
<height>51</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true"/>
</property>
<property name="text">
<string>Записать в энергонезависимую
память BMS</string>
</property>
</widget>
<widget class="QScrollArea" name="scrollAreaBmsSettings">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>731</width>
<height>753</height>
</rect>
</property>
<property name="widgetResizable">
<bool>true</bool>
</property>
<widget class="BmsSettings" name="bmsSettings">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>729</width>
<height>751</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true">/*#content
{
background-color: #f7f8fc
}
*/
/* background-color: #f7f8fc;*/
</string>
</property>
<layout class="QHBoxLayout" name="hLayoutLeftAndRight">
<property name="spacing">
<number>6</number>
</property>
<property name="leftMargin">
<number>9</number>
</property>
</layout>
</widget>
</widget>
<widget class="QLabel" name="lbStatus">
<property name="geometry">
<rect>
<x>770</x>
<y>680</y>
<width>281</width>
<height>41</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>13</pointsize>
</font>
</property>
<property name="styleSheet">
<string notr="true">background-color: #e8e8e8</string>
</property>
<property name="text">
<string/>
</property>
<property name="scaledContents">
<bool>false</bool>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
<widget class="QProgressBar" name="progressBar">
<property name="geometry">
<rect>
<x>770</x>
<y>720</y>
<width>281</width>
<height>16</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>7</pointsize>
</font>
</property>
<property name="styleSheet">
<string notr="true">QProgressBar::chunk { background-color:lime; };</string>
</property>
<property name="value">
<number>24</number>
</property>
</widget>
<zorder>lbIconLogo</zorder>
<zorder>scrollAreaBmsSettings</zorder>
<zorder>pbLinkSerialNumber</zorder>
<zorder>pbReadDefaultSettingsFromFile</zorder>
<zorder>pbReadCurrentSettingsFromBms</zorder>
<zorder>pbLinkSoc</zorder>
<zorder>pbLinkRestrictions</zorder>
<zorder>pbLinkClaibrateZero</zorder>
<zorder>pbWriteCurrentValuesToBms</zorder>
<zorder>pbLinkCellsConfiguration</zorder>
<zorder>pbLinkOutputsSetting</zorder>
<zorder>pbLinkBalancingConfiguration</zorder>
<zorder>pbWriteToNonVolatileBmsMemory</zorder>
<zorder>lbHeaderLanguageIcon</zorder>
<zorder>pbLinkConfiguration</zorder>
<zorder>lbStatus</zorder>
<zorder>progressBar</zorder>
</widget>
<customwidgets>
<customwidget>
<class>BmsSettings</class>
<extends>QWidget</extends>
<header location="global">bmssettings.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>

384
connectandenterpage.cpp Normal file
View File

@@ -0,0 +1,384 @@
#include "connectandenterpage.h"
#include "ui_connectandenterpage.h"
#include "utility.h"
#include "widgets/paramdialog.h"
#include <QMessageBox>
#include <QAbstractItemView>
#include <QListView>
ConnectAndEnterPage::ConnectAndEnterPage(QWidget *parent)
: QWidget(parent),
// QDialog(parent),
ui(new Ui::ConnectAndEnterPage),
mDieBieMS(nullptr),
mIsConnected(false)
{
ui->setupUi(this);
on_pbSerialRefresh_clicked();
ui->lbIconLogo->show();
// hide buttons: HIDE - EXPAND - CLOSE
//setWindowFlags(Qt::CustomizeWindowHint);
connect(ui->cboxSerialPort, &HoverableComboBox::opened, this, &ConnectAndEnterPage::onComboBoxSerialPortOpened);
connect(ui->cboxSerialPort, &HoverableComboBox::closed, this, &ConnectAndEnterPage::onComboBoxSerialPortClosed);
ui->cboxSerialPort->setView(new QListView());
}
ConnectAndEnterPage::~ConnectAndEnterPage()
{
delete ui;
}
void ConnectAndEnterPage::prepareWindowBeforeShow()
{
ui->lbStatus->setStyleSheet("QLabel { background-color : white; color : white; }");
ui->lbStatus->clear();
mIsConnected = false;
if(mDieBieMS) {
//if(mDieBieMS->isPortConnected())
mDieBieMS->disconnectPort();
}
on_pbSerialRefresh_clicked();
}
bool ConnectAndEnterPage::isConnectionPrepared()
{
// return true;
return mIsConnected;
}
QString ConnectAndEnterPage::portToConnect()
{
return ui->cboxSerialPort->currentText();
}
void ConnectAndEnterPage::clearPortToConnect()
{
ui->cboxSerialPort->clear();
}
//*
void ConnectAndEnterPage::initDieBieMS(BMSInterface* dieBieBms)
{
// mVersion = QString::number(DT_VERSION);
//mDieBieMS = new BMSInterface(this);
mDieBieMS = dieBieBms;
mStatusInfoTime = 0;
// mStatusLabel = new QLabel(this);
// ui->lbStatusBar = (mStatusLabel);
// mTimer = new QTimer(this);
mKeyLeft = false;
mKeyRight = false;
mMcConfRead = false;
// SEND_STATUS_INFO //
connect(mDieBieMS, &BMSInterface::statusMessage, this, &ConnectAndEnterPage::showStatusInfo);
connect(mDieBieMS, &BMSInterface::messageDialog, this, &ConnectAndEnterPage::showMessageDialog);
connect(mDieBieMS, &BMSInterface::serialPortNotWritable, this, &ConnectAndEnterPage::serialPortNotWritable);
connect(mDieBieMS->commands(), &Commands::bmsConfigCheckResult, this, &ConnectAndEnterPage::bmsConfigCheckResult);
// connect(ui->actionAboutQt, SIGNAL(triggered(bool)),qApp, SLOT(aboutQt()));
// connect(mTimer, SIGNAL(timeout()),this, SLOT(timerSlot()));
// Remove the menu with the option to hide the toolbar
// ui->mainToolBar->setContextMenuPolicy(Qt::PreventContextMenu);
mDieBieMS->bmsConfig()->loadParamsXml("://res/config.xml");
mDieBieMS->infoConfig()->loadParamsXml("://res/info.xml");
// reloadPages();
connect(mDieBieMS->bmsConfig(), SIGNAL(updated()), this, SLOT(bmsconfUpdated()));
on_pbSerialRefresh_clicked();
}
//*/
void ConnectAndEnterPage::setDebugPrintPage(DebugPrintPage *page)
{
mPageDebugPrint = page;
}
void ConnectAndEnterPage::showStatusInfo(QString info, bool isGood)
{
if (isGood) {
ui->lbStatus->setStyleSheet("QLabel { background-color : lightgreen; color : black; }");
mPageDebugPrint->printConsole("Status: " + info + "<br>");
} else {
ui->lbStatus->setStyleSheet("QLabel { background-color : red; color : black; }");
mPageDebugPrint->printConsole("<font color=\"red\">Status: " + info + "</font><br>");
}
mStatusInfoTime = 80;
ui->lbStatus->setText(info);
}
bool ConnectAndEnterPage::waitProcess(QProcess &process, bool block, int timeoutMs)
{
bool wasEnables = isEnabled();
bool killed = false;
if (block) {
setEnabled(false);
}
process.waitForStarted();
QEventLoop loop;
QTimer timeoutTimer;
timeoutTimer.setSingleShot(true);
timeoutTimer.start(timeoutMs);
connect(&process, SIGNAL(finished(int)), &loop, SLOT(quit()));
connect(&timeoutTimer, SIGNAL(timeout()), &loop, SLOT(quit()));
loop.exec();
if (process.state() == QProcess::Running) {
process.kill();
process.waitForFinished();
killed = true;
}
setEnabled(wasEnables);
return !killed;
}
QString ConnectAndEnterPage::runCmd(QString cmd, QStringList args)
{
QProcess process;
process.setEnvironment(QProcess::systemEnvironment());
process.start(cmd, args);
waitProcess(process);
QString res = process.readAllStandardOutput();
process.close();
return res;
}
void ConnectAndEnterPage::serialPortNotWritable(const QString &port)
{
(void)port;
#ifdef Q_OS_LINUX
QMessageBox::StandardButton reply;
reply = QMessageBox::warning(this, tr("Connect Serial Port"),
tr("The serial port is not writable. This can usually be fixed by "
"adding your user to the dialout, uucp and/or lock groups. Would "
"you like to do that?"),
QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
if (reply == QMessageBox::Yes) {
QString name = qgetenv("USER");
if (name.isEmpty()) {
name = qgetenv("USERNAME");
}
bool hasDialout = !runCmd("getent",
QStringList() << "group"
<< "dialout")
.isEmpty();
bool hasUucp = !runCmd("getent",
QStringList() << "group"
<< "uucp")
.isEmpty();
bool hasLock = !runCmd("getent",
QStringList() << "group"
<< "lock")
.isEmpty();
QString grps;
if (hasDialout) {
grps += "dialout";
}
if (hasUucp) {
if (!grps.isEmpty()) {
grps += ",";
}
grps += "uucp";
}
if (hasLock) {
if (!grps.isEmpty()) {
grps += ",";
}
grps += "lock";
}
QProcess process;
process.setEnvironment(QProcess::systemEnvironment());
process.start("pkexec",
QStringList() << "usermod"
<< "-aG" << grps << name);
waitProcess(process);
if (process.exitCode() == 0) {
showMessageDialog(tr("Command Result"),
tr("Result from command:\n\n"
"%1\n"
"You have to reboot for this "
"change to take effect.")
.arg(QString(process.readAllStandardOutput())),
true, false);
} else {
showMessageDialog(tr("Command Result"), tr("Running command failed."), false, false);
}
process.close();
}
#endif
}
void ConnectAndEnterPage::bmsconfUpdated()
{
mMcConfRead = true;
}
void ConnectAndEnterPage::bmsConfigCheckResult(QStringList paramsNotSet)
{
if (!paramsNotSet.isEmpty()) {
ParamDialog::showParams(tr("Parameters truncated"),
tr("The following parameters were truncated because they were set outside "
"of their allowed limits."),
mDieBieMS->bmsConfig(), paramsNotSet, this);
}
}
void ConnectAndEnterPage::showMessageDialog(const QString &title, const QString &msg, bool isGood, bool richText)
{
(void)richText;
mIsConnected = isGood;
if (isGood) {
QMessageBox::information(this, title, msg);
} else {
QMessageBox::warning(this, title, msg);
}
emit sigConnected();
}
BMSInterface *ConnectAndEnterPage::bms() const
{
return mDieBieMS;
}
void ConnectAndEnterPage::setDieBieMS(BMSInterface *dieBieMS)
{
mDieBieMS = dieBieMS;
}
void ConnectAndEnterPage::setDieBieMSEx(BMSInterface *dieBieMS)
{
mDieBieMS = dieBieMS;
connect(mDieBieMS->bleDevice(), SIGNAL(scanDone(QVariantMap, bool)), this, SLOT(bleScanDone(QVariantMap, bool)));
QString lastBleAddr = QSettings().value("ble_addr").toString();
if (lastBleAddr != "") {
QString setName = mDieBieMS->getBleName(lastBleAddr);
QString name;
if (!setName.isEmpty()) {
name += setName;
name += " [";
name += lastBleAddr;
name += "]";
} else {
name = lastBleAddr;
}
// ui->bleDevBox->insertItem(0, name, lastBleAddr);
}
on_pbSerialRefresh_clicked();
}
void ConnectAndEnterPage::on_pbSerialRefresh_clicked()
{
ui->pbSerialRefresh->setChecked(false);
if (mDieBieMS) {
ui->cboxSerialPort->clear();
QList<VSerialInfo_t> ports = mDieBieMS->listSerialPorts();
foreach (const VSerialInfo_t &port, ports) {
ui->cboxSerialPort->addItem(port.name, port.systemPath);
}
ui->cboxSerialPort->setCurrentIndex(0);
}
}
void ConnectAndEnterPage::on_pbConnect_clicked()
{
// Big Connect Button on Wellcome page
// Utility::autoconnectBlockingWithProgress(mDieBieMS, this);
// Simple Connect Button on Connection Page
mIsConnected = false;
if (mDieBieMS) {
mIsConnected = mDieBieMS->connectSerial(ui->cboxSerialPort->currentData().toString(),
SERIAL_BAUD_DEFAULT_VAL // ui->spinboxSerialPort->value()
);
}
if(mIsConnected)
emit sigConnected();
//*/
}
void ConnectAndEnterPage::on_pbExit_clicked()
{
if(mDieBieMS)
mDieBieMS->disconnectPort();
//mDieBieMS->tcpInputDisconnected();
mIsConnected = false;
clearPortToConnect();
emit sigReject();
//close();
}
void ConnectAndEnterPage::onComboBoxSerialPortOpened()
{
//ui->pbSerialRefresh->setChecked(true);
}
void ConnectAndEnterPage::onComboBoxSerialPortClosed()
{
//ui->pbSerialRefresh->setChecked(false);
}
void ConnectAndEnterPage::on_gbWindow_clicked()
{
int a = 0;
}
void ConnectAndEnterPage::on_gbHeader_clicked()
{
int a = 0;
}
/*
void ConnectAndEnterPage::ShowContextMenu(const QPoint &pos)
{
QMenu contextMenu(tr("Context menu"), this);
QAction action1("Remove Data Point", this);
connect(&action1, SIGNAL(triggered()), this, SLOT(removeDataPoint()));
contextMenu.addAction(&action1);
contextMenu.exec(mapToGlobal(pos));
}
*/
void ConnectAndEnterPage::on_pbLanguage_clicked()
{
}

94
connectandenterpage.h Normal file
View File

@@ -0,0 +1,94 @@
#ifndef CONNECTANDENTERPAGE_H
#define CONNECTANDENTERPAGE_H
#include <QWidget>
#include <QDialog>
#include <QLabel>
#include <QProcess>
#include "bmsinterface.h"
#include "debugprintpage.h"
namespace Ui {
class ConnectAndEnterPage;
}
//class ConnectAndEnterPage : public QDialog
class ConnectAndEnterPage : public QWidget
{
Q_OBJECT
public:
explicit ConnectAndEnterPage(QWidget *parent = nullptr);
~ConnectAndEnterPage();
void initDieBieMS(BMSInterface *);
void prepareWindowBeforeShow();
bool isConnectionPrepared();
QString portToConnect();
static const int SERIAL_BAUD_DEFAULT_VAL = 115200;
BMSInterface *bms() const;
void setDieBieMS(BMSInterface *dieBieMS);
void setDieBieMSEx(BMSInterface *dieBieMS);
void setDebugPrintPage(DebugPrintPage *page);
signals:
void sigConnected();
void sigReject();
private slots:
void on_pbExit_clicked();
private slots:
void showStatusInfo(QString info, bool isGood);
void showMessageDialog(const QString &title, const QString &msg, bool isGood, bool richText);
void serialPortNotWritable(const QString &port);
void bmsconfUpdated();
void bmsConfigCheckResult(QStringList paramsNotSet);
void on_pbConnect_clicked();
void on_pbSerialRefresh_clicked();
void onComboBoxSerialPortOpened();
void onComboBoxSerialPortClosed();
void on_gbWindow_clicked();
void on_gbHeader_clicked();
void on_pbLanguage_clicked();
private:
Ui::ConnectAndEnterPage *ui;
BMSInterface * mDieBieMS;
bool mIsConnected;
// QString mPortForConnection;
// QLabel *mStatusLabel;
DebugPrintPage *mPageDebugPrint;
int mStatusInfoTime;
bool mKeyLeft;
bool mKeyRight;
bool mMcConfRead;
bool waitProcess(QProcess &process, bool block = true, int timeoutMs = 300000);
QString runCmd(QString cmd, QStringList args);
void clearPortToConnect();
void ShowContextMenu(const QPoint &pos);
};
#endif // CONNECTANDENTERPAGE_H

733
connectandenterpage.ui Normal file
View File

@@ -0,0 +1,733 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ConnectAndEnterPage</class>
<widget class="QWidget" name="ConnectAndEnterPage">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>1366</width>
<height>738</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<property name="styleSheet">
<string notr="true">/*#ConnectAndEnterPage
{
background-color:#F7F8FC;
}
*/
QLabel { font-family:&quot;Artifakt Element&quot;; }
QPushButton { font-family:&quot;Artifakt Element&quot;; }
QComboBox { font-family:&quot;Artifakt Element&quot;; }
QLabel#lbIconLogo
{
/*background-image: url(:/images/cubo-verde-logo.png);*/
border-image: url(:/res/cubo-verde-logo-green.png) 0 0 0 0 stretch stretch;
}
QLabel#lbHeaderLanguageIcon
{
/*background-image: url(:/images/cubo-verde-logo.png);*/
border-image: url(:/res/language-russ.png) 0 0 0 0 stretch stretch;
}
QLabel#lbChooseComPort
{
font-weight: bold;
/*font-family: &quot;DejaVu Sans&quot;;*/
}
/*
QComboBox#cboxComConnections
{
background-color:white;
border-radius:10px;
border-color: lightgrey;
border-style: solid;
border-width: 1px;
}
*/
</string>
</property>
<widget class="QGroupBox" name="gbWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>10</y>
<width>637</width>
<height>433</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>637</width>
<height>433</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>637</width>
<height>433</height>
</size>
</property>
<property name="styleSheet">
<string notr="true">QGroupBox#gbWindow
{
background-color:white;
border-radius:10px;
border-color: lightgrey;
border-style: solid;
border-width: 1px;
}
</string>
</property>
<property name="title">
<string/>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<item>
<widget class="QGroupBox" name="gbHeader">
<property name="minimumSize">
<size>
<width>0</width>
<height>71</height>
</size>
</property>
<property name="styleSheet">
<string notr="true">QGroupBox#gbHeader
{
background-color:white;
border-top-left-radius:10px;
border-top-right-radius:10px;
border-bottom-left-radius:0px;
border-bottom-right-radius:0px;
border-color: lightgrey;
border-style: solid;
border-width: 1px;
margin-left: -1px;
margin-right: -1px;
margin-top: -1px;
}
</string>
</property>
<property name="title">
<string/>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>140</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="lbIconLogo">
<property name="minimumSize">
<size>
<width>50</width>
<height>50</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>50</width>
<height>50</height>
</size>
</property>
<property name="contextMenuPolicy">
<enum>Qt::NoContextMenu</enum>
</property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Plain</enum>
</property>
<property name="midLineWidth">
<number>0</number>
</property>
<property name="text">
<string/>
</property>
<property name="pixmap">
<pixmap resource="res.qrc">:/res/cubo-verde-logo-green.png</pixmap>
</property>
<property name="scaledContents">
<bool>true</bool>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>215</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="LanguageSelectorButton" name="pbLanguage">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>50</width>
<height>0</height>
</size>
</property>
<property name="font">
<font>
<family>Artifakt Element</family>
<pointsize>-1</pointsize>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="layoutDirection">
<enum>Qt::RightToLeft</enum>
</property>
<property name="styleSheet">
<string notr="true">
color:#0A72BA;
background-color:#ffffff;
border-color: #ffffff;
border-style: solid;
border-width: 1px;
border-radius: 7px;
font-weight:bold;
font-size: 16px;
/* font-family: &quot;Times New Roman&quot;;*/
/*font-family: &quot;Artifakt Element&quot;;*/
qproperty-icon: url(:/res/language-russ.png) 0 0 0 0 stretch stretch;
</string>
</property>
<property name="text">
<string>Рус</string>
</property>
<property name="iconSize">
<size>
<width>32</width>
<height>27</height>
</size>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>12</number>
</property>
<property name="leftMargin">
<number>150</number>
</property>
<property name="rightMargin">
<number>150</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="label">
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="lbChooseComPort">
<property name="font">
<font>
<family>Artifakt Element</family>
<pointsize>24</pointsize>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Выберите COM-порт</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="topMargin">
<number>0</number>
</property>
<item>
<widget class="HoverableComboBox" name="cboxSerialPort">
<property name="minimumSize">
<size>
<width>0</width>
<height>61</height>
</size>
</property>
<property name="font">
<font>
<family>Artifakt Element</family>
<pointsize>16</pointsize>
</font>
</property>
<property name="styleSheet">
<string notr="true">
/*QComboBox QAbstractItemView::item
{
min-height: 50px;
}
*/
QListView::item
{
color:black;
height:50px;
padding-left:15px;
font-size:16px;
}
/*
QListView
{
color:black;
height:50px;
padding-left:15px;
font-size:16px;
}
*/
QComboBox {
color:black;
border: 1px solid lightgray;
border-radius: 7px;
padding-left:15px;
/*padding: 1px 18px 1px 3px;*/
}
/*
QComboBox {
color:gray;
border: 1px solid lightgray;
border-radius: 7px;
padding: 1px 18px 1px 3px;
min-width: 6em;
}
QComboBox:editable {
background: white;
}
*/
/*not opened*/
/*QComboBox:!editable, QComboBox::drop-down:editable {
background: white;
padding-left: 15px;
}
*/
/* QComboBox gets the &quot;on&quot; state when the popup is open */
/*QComboBox:!editable:on, QComboBox::drop-down:editable:on {
background: #CCE9DC;
padding-left: 15px;
}
*/
/* shift the text when the popup opens */
/*QComboBox:on {
padding-top: 3px;
padding-left: 15px;
}
*/
/* QComboBox gets the &quot;on&quot; state when the popup is open */
QComboBox:!editable:on, QComboBox::drop-down:editable:on {
background: #CCE9DC;
padding-left: 15px;
}
/* shift the text when the popup opens */
/*QComboBox:on {
padding-top: 3px;
padding-left: 15px;
}
*/
/*area around the /* V */
QComboBox::drop-down {
subcontrol-origin: padding;
subcontrol-position: top right;
width: 50px;
/*background: red;*/
/*border-left-width: 1px;
border-left-color: darkgray;
border-left-style: solid; /* just a single line */
border-top-right-radius: 3px; /* same radius as the QComboBox */
border-bottom-right-radius: 3px;
/*padding-right: 10px; /* V */
}
QComboBox QAbstractItemView {
/*no mouse over text*/
color: black;
/*no mouse over white*/
background-color: white;
/*mouse over text*/
selection-color: black;
/*mouse over blue*/
selection-background-color: #F0F1F4; /* blue */
/*selection-background-color: #FFF1F4; /* red */
}
QComboBox QAbstractItemView:pressed {
/*no mouse over text green*/
color: #CCE9DC;
}
QComboBox::item:pressed {
/*pressed element text green*/
color: #CCE9DC;
}
/*----------------------------------------*/
/*area around the /* V */
QComboBox::drop-down {
subcontrol-origin: padding;
subcontrol-position: top right;
width: 50px;
/*background: red;*/
/*border-left-width: 1px;
border-left-color: darkgray;
border-left-style: solid; /* just a single line */
border-top-right-radius: 3px; /* same radius as the QComboBox */
border-bottom-right-radius: 3px;
/*padding-right: 10px; /* V */
}
QComboBox::down-arrow {
image: url(:/res/cbox-arrow-down-black.png);
}
QComboBox::down-arrow:on { /*QComboBox::up-arrow {*/
image: url(:/res/cbox-arrow-up-green.png);
}
/* shift the arrow when popup is open */
/*QComboBox::down-arrow:on {
top: 1px;
left: 1px;
}
*/
/*
QComboBox QAbstractItemView {
/* color: black; /*rgb(85, 170, 255);*/
/* background-color: white; /* #373e4e;*/
/* padding: 10px;
selection-background-color: #CCE9DC; /*rgb(39, 44, 54);*/
/*selected green
background: #CCE9DC;
mouse over blue
background: #F0F1F4;
*/
/*}
/*
QGroupBox#gbSerialNumberLocal,
#gbBatteryModulesCellsAkbBmsParameters,
#gbCellMinMaxVoltageNumberAmperageParameters
{
background-color:white;
border-radius:10px;
border-color: lightgrey;
border-style: solid;
border-width: 1px;
}
*/
</string>
</property>
<property name="currentText">
<string/>
</property>
<property name="currentIndex">
<number>-1</number>
</property>
<property name="maxVisibleItems">
<number>4</number>
</property>
<property name="modelColumn">
<number>0</number>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pbSerialRefresh">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>61</width>
<height>61</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>61</width>
<height>61</height>
</size>
</property>
<property name="styleSheet">
<string notr="true">
QPushButton#pbSerialRefresh
{
color:white;
background-color:#FFFFFF;
font-weight:bold;
font-size: 14px;
border-color: #009352;
border-style: solid;
border-width: 1px;
border-radius: 7px;
padding:15px;
image: url(:/res/icons/Restart-97-Black.png) 0 0 0 0 stretch stretch;
}
QPushButton#pbSerialRefresh:hover:pressed
{
background-color:#009352;
border-color: #009352;
image: url(:/res/icons/Restart-97-White.png) 0 0 0 0 stretch stretch;
}
/*
QPushButton#pbSerialRefresh:checked
{
background-color:#009352;
border-color: #009352;
image: url(:/res/icons/Restart-97-White.png) 0 0 0 0 stretch stretch;
}
*/
/*background-color:#01673a;*/
/*QPushButton#pbSerialRefresh:hover:pressed
{
background-color:lightgrey;
}
*/
</string>
</property>
<property name="text">
<string/>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<property name="checked">
<bool>false</bool>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QPushButton" name="pbConnect">
<property name="minimumSize">
<size>
<width>281</width>
<height>61</height>
</size>
</property>
<property name="styleSheet">
<string notr="true">QPushButton#pbConnect
{
color:white;
background-color:#009352;
border-color: #009352;
border-style: solid;
border-width: 1px;
border-radius: 7px;
font-weight:bold;
font-size: 16px;
/* font-family: &quot;Times New Roman&quot;;*/
font-family: &quot;Artifakt Element&quot;;
}
QPushButton#pbConnect:hover:pressed
{
background-color:#01673a;
}
</string>
</property>
<property name="text">
<string>Подключиться</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pbExit">
<property name="minimumSize">
<size>
<width>281</width>
<height>61</height>
</size>
</property>
<property name="styleSheet">
<string notr="true">QPushButton#pbExit
{
color:#000000;
background-color:#FFFFFF;
font-weight:bold;
font-size: 16px;
border-color: #009352;
border-style: solid;
border-width: 1px;
border-radius: 7px;
}
QPushButton#pbExit:hover:pressed
{
/*background-color:#01673a;*/
background-color:lightgrey;
}
</string>
</property>
<property name="text">
<string>Закрыть</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="lbStatus">
<property name="font">
<font>
<family>Artifakt Element</family>
<pointsize>16</pointsize>
</font>
</property>
<property name="text">
<string/>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</widget>
</widget>
<customwidgets>
<customwidget>
<class>HoverableComboBox</class>
<extends>QComboBox</extends>
<header location="global">hoverablecombobox.h</header>
</customwidget>
<customwidget>
<class>LanguageSelectorButton</class>
<extends>QPushButton</extends>
<header location="global">languageselectorbutton.h</header>
</customwidget>
</customwidgets>
<resources>
<include location="res.qrc"/>
</resources>
<connections/>
</ui>

View File

@@ -0,0 +1,76 @@
#include "customlefttabbarbutton.h"
CustomLeftTabBarButton::CustomLeftTabBarButton(QWidget* parent) : QPushButton(parent)
{
QIcon i = this->icon();
QPixmap p = i.pixmap(5, 5);
m_pixmap = this->icon().pixmap(this->icon().actualSize(QSize(5, 5)));
}
CustomLeftTabBarButton::~CustomLeftTabBarButton()
{
}
QSize CustomLeftTabBarButton::sizeHint() const
{
const auto parentHint = QPushButton::sizeHint();
// add margins here if needed
return QSize
(
parentHint.width() + m_pixmap.width() ,
std::max(parentHint.height(), m_pixmap.height())
);
}
void CustomLeftTabBarButton::setPixmap(const QPixmap& pixmap)
{
m_pixmap = pixmap;
}
void CustomLeftTabBarButton::setPos(const QSize& pos)
{
m_pixmapPos = pos;
}
void CustomLeftTabBarButton::setIconStringForButton(QString iconString)
{
QIcon i = QIcon(iconString); //":/res/lock-icon-closed.png");
int sz = 20;
QSize size (sz , sz);
//= iconSize();
i.actualSize(size);
QPixmap p = i.pixmap(sz);
m_pixmap = p;
this->setPixmap(p);
}
void CustomLeftTabBarButton::paintEvent(QPaintEvent* e)
{
// Draw the button first
QPushButton::paintEvent(e);
if (!m_pixmap.isNull())
{
bool isTextEmpty = (text()=="");
{
//const int y = (height() - m_pixmap.height()) / 2; // add margin if needed
int h_btn_half = height() / 2;
int h_icon_half = m_pixmap.height() / 2;
int w_btn_half = width() / 2;
int w_icon_half = m_pixmap.width() / 2;
int w_btn_tenth = width() / 10;
QPainter painter(this);
painter.drawPixmap
(
isTextEmpty
? w_btn_half - w_icon_half
: 40
, h_btn_half - h_icon_half
, m_pixmap
); // hardcoded horizontal margin
}
}
}

32
customlefttabbarbutton.h Normal file
View File

@@ -0,0 +1,32 @@
#ifndef CUSTOMLEFTTABBARBUTTON_H
#define CUSTOMLEFTTABBARBUTTON_H
#include <QObject>
#include <QPushButton>
#include <QPaintEvent>
#include <QPainter>
#include <QPixmap>
#include <QSize>
class CustomLeftTabBarButton : public QPushButton
{
public:
explicit CustomLeftTabBarButton(QWidget* parent = nullptr);
virtual ~CustomLeftTabBarButton();
void setPos(const QSize& pos);
void setPixmap(const QPixmap& pixmap);
void setIconStringForButton(QString iconString);
void setStringForButton(QString String);
virtual QSize sizeHint() const override;
protected:
virtual void paintEvent(QPaintEvent* e) override;
private:
QPixmap m_pixmap;
QSize m_pixmapPos;
};
#endif // CUSTOMLEFTTABBARBUTTON_H

469
datatypes.h Normal file
View File

@@ -0,0 +1,469 @@
/*
Original copyright 2018 Benjamin Vedder benjamin@vedder.se and the VESC Tool project ( https://github.com/vedderb/vesc_tool )
Forked to:
Copyright 2018 Danny Bokma github@diebie.nl (https://github.com/DieBieEngineering/DieBieMS-Tool)
Now forked to:
Copyright 2019 - 2020 Kevin Dionne kevin.dionne@ennoid.me (https://github.com/EnnoidMe/ENNOID-BMS-Tool)
This file is part of ENNOID-BMS Tool.
ENNOID-BMS Tool is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
ENNOID-BMS Tool is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef DATATYPES_H
#define DATATYPES_H
#include <QObject>
#include <QString>
#include <QStringList>
#include <QVector>
#include <stdint.h>
typedef struct {
QString name;
QString systemPath;
bool isVesc;
} VSerialInfo_t;
typedef enum {
CFG_T_UNDEFINED = 0,
CFG_T_DOUBLE,
CFG_T_INT,
CFG_T_QSTRING,
CFG_T_ENUM,
CFG_T_BOOL
} CFG_T;
typedef enum {
VESC_TX_UNDEFINED = 0,
VESC_TX_UINT8,
VESC_TX_INT8,
VESC_TX_UINT16,
VESC_TX_INT16,
VESC_TX_UINT32,
VESC_TX_INT32,
VESC_TX_DOUBLE16,
VESC_TX_DOUBLE32,
VESC_TX_DOUBLE32_AUTO
} VESC_TX_T;
typedef enum {
FAULT_CODE_NONE = 0,
FAULT_CODE_PACK_OVER_VOLTAGE,
FAULT_CODE_PACK_UNDER_VOLTAGE,
FAULT_CODE_LOAD_OVER_VOLTAGE,
FAULT_CODE_LOAD_UNDER_VOLTAGE,
FAULT_CODE_CHARGER_OVER_VOLTAGE,
FAULT_CODE_CHARGER_UNDER_VOLTAGE,
FAULT_CODE_CELL_HARD_OVER_VOLTAGE,
FAULT_CODE_CELL_HARD_UNDER_VOLTAGE,
FAULT_CODE_CELL_SOFT_OVER_VOLTAGE,
FAULT_CODE_CELL_SOFT_UNDER_VOLTAGE,
FAULT_CODE_MAX_UVP_OVP_ERRORS,
FAULT_CODE_MAX_UVT_OVT_ERRORS,
FAULT_CODE_OVER_CURRENT,
FAULT_CODE_OVER_TEMP_BMS,
FAULT_CODE_UNDER_TEMP_BMS,
FAULT_CODE_DISCHARGE_OVER_TEMP_CELLS,
FAULT_CODE_DISCHARGE_UNDER_TEMP_CELLS,
FAULT_CODE_CHARGE_OVER_TEMP_CELLS,
FAULT_CODE_CHARGE_UNDER_TEMP_CELLS,
FAULT_CODE_PRECHARGE_TIMEOUT,
FAULT_CODE_DISCHARGE_RETRY,
FAULT_CODE_CHARGE_RETRY,
FAULT_CODE_CHARGER_DISCONNECT
} bms_fault_code;
typedef enum {
DISP_POS_MODE_NONE = 0,
DISP_POS_MODE_INDUCTANCE,
DISP_POS_MODE_OBSERVER,
DISP_POS_MODE_ENCODER,
DISP_POS_MODE_PID_POS,
DISP_POS_MODE_PID_POS_ERROR,
DISP_POS_MODE_ENCODER_OBSERVER_ERROR
} disp_pos_mode;
struct MC_VALUES {
Q_GADGET
Q_PROPERTY(double v_in MEMBER v_in)
Q_PROPERTY(double temp_mos MEMBER temp_mos)
Q_PROPERTY(double temp_motor MEMBER temp_motor)
Q_PROPERTY(double current_motor MEMBER current_motor)
Q_PROPERTY(double current_in MEMBER current_in)
Q_PROPERTY(double id MEMBER id)
Q_PROPERTY(double iq MEMBER iq)
Q_PROPERTY(double rpm MEMBER rpm)
Q_PROPERTY(double duty_now MEMBER duty_now)
Q_PROPERTY(double amp_hours MEMBER amp_hours)
Q_PROPERTY(double amp_hours_charged MEMBER amp_hours_charged)
Q_PROPERTY(double watt_hours MEMBER watt_hours)
Q_PROPERTY(double watt_hours_charged MEMBER watt_hours_charged)
Q_PROPERTY(int tachometer MEMBER tachometer)
Q_PROPERTY(int tachometer_abs MEMBER tachometer_abs)
Q_PROPERTY(double position MEMBER position)
//Q_PROPERTY(mc_fault_code fault_code MEMBER fault_code)
Q_PROPERTY(QString fault_str MEMBER fault_str)
public:
MC_VALUES() {
v_in = 0.0;
temp_mos = 0.0;
temp_mos_1 = 0.0;
temp_mos_2 = 0.0;
temp_mos_3 = 0.0;
temp_motor = 0.0;
current_motor = 0.0;
current_in = 0.0;
id = 0.0;
iq = 0.0;
rpm = 0.0;
duty_now = 0.0;
amp_hours = 0.0;
amp_hours_charged = 0.0;
watt_hours = 0.0;
watt_hours_charged = 0.0;
tachometer = 0;
tachometer_abs = 0;
position = 0.0;
//fault_code = FAULT_CODE_NONE;
vesc_id = 0;
}
double v_in;
double temp_mos;
double temp_mos_1;
double temp_mos_2;
double temp_mos_3;
double temp_motor;
double current_motor;
double current_in;
double id;
double iq;
double rpm;
double duty_now;
double amp_hours;
double amp_hours_charged;
double watt_hours;
double watt_hours_charged;
int tachometer;
int tachometer_abs;
double position;
//mc_fault_code fault_code;
int vesc_id;
QString fault_str;
};
Q_DECLARE_METATYPE(MC_VALUES)
struct BMS_VALUES {
Q_GADGET
Q_PROPERTY(double packVoltage MEMBER packVoltage)
Q_PROPERTY(double packCurrent MEMBER packCurrent)
Q_PROPERTY(int soC MEMBER soC)
Q_PROPERTY(double cVHigh MEMBER cVHigh)
Q_PROPERTY(double cVAverage MEMBER cVAverage)
Q_PROPERTY(double cVLow MEMBER cVLow)
Q_PROPERTY(double cVMisMatch MEMBER cVMisMatch)
Q_PROPERTY(double loadLCVoltage MEMBER loadLCVoltage)
Q_PROPERTY(double loadLCCurrent MEMBER loadLCCurrent)
Q_PROPERTY(double loadHCVoltage MEMBER loadHCVoltage)
Q_PROPERTY(double loadHCCurrent MEMBER loadHCCurrent)
Q_PROPERTY(double chargerVoltage MEMBER chargerVoltage)
Q_PROPERTY(double auxVoltage MEMBER auxVoltage)
Q_PROPERTY(double auxCurrent MEMBER auxCurrent)
Q_PROPERTY(double tempBattHigh MEMBER tempBattHigh)
Q_PROPERTY(double tempBattAverage MEMBER tempBattAverage)
Q_PROPERTY(double tempBattLow MEMBER tempBattLow)
Q_PROPERTY(double tempBMSHigh MEMBER tempBMSHigh)
Q_PROPERTY(double tempBMSAverage MEMBER tempBMSAverage)
Q_PROPERTY(double tempBMSLow MEMBER tempBMSLow)
Q_PROPERTY(double humidity MEMBER humidity)
Q_PROPERTY(QString opState MEMBER opState)
Q_PROPERTY(int balanceActive MEMBER balanceActive)
Q_PROPERTY(QString faultState MEMBER faultState)
public:
BMS_VALUES(){
packVoltage = 0.0;
packCurrent =0.0;
soC = 0;
cVHigh = 0.0;
cVAverage =0.0;
cVLow = 0.0;
cVMisMatch = 0.0;
loadLCVoltage = 0.0;
loadLCCurrent = 0.0;
loadHCVoltage = 0.0;
loadHCCurrent = 0.0;
chargerVoltage =0.0;
auxVoltage = 0.0;
auxCurrent = 0.0;
tempBattHigh = 0.0;
tempBattAverage = 0.0;
tempBattLow = 0.0;
tempBMSHigh = 0.0;
tempBMSAverage = 0.0;
tempBMSLow = 0.0;
humidity = 0.0;
balanceActive = 0;
}
double packVoltage;
double packCurrent;
int soC;
double cVHigh;
double cVAverage;
double cVLow;
double cVMisMatch;
double loadLCVoltage;
double loadLCCurrent;
double loadHCVoltage;
double loadHCCurrent;
double chargerVoltage;
double auxVoltage;
double auxCurrent;
double tempBattHigh;
double tempBattAverage;
double tempBattLow;
double tempBMSHigh;
double tempBMSAverage;
double tempBMSLow;
double humidity;
QString opState;
int balanceActive;
QString faultState;
};
Q_DECLARE_METATYPE(BMS_VALUES)
struct MCCONF_TEMP {
Q_GADGET
Q_PROPERTY(double current_min_scale MEMBER current_min_scale)
Q_PROPERTY(double current_max_scale MEMBER current_max_scale)
Q_PROPERTY(double erpm_or_speed_min MEMBER erpm_or_speed_min)
Q_PROPERTY(double erpm_or_speed_max MEMBER erpm_or_speed_max)
Q_PROPERTY(double duty_min MEMBER duty_min)
Q_PROPERTY(double duty_max MEMBER duty_max)
Q_PROPERTY(double watt_min MEMBER watt_min)
Q_PROPERTY(double watt_max MEMBER watt_max)
Q_PROPERTY(QString name MEMBER name)
public:
double current_min_scale;
double current_max_scale;
double erpm_or_speed_min;
double erpm_or_speed_max;
double duty_min;
double duty_max;
double watt_min;
double watt_max;
QString name;
};
Q_DECLARE_METATYPE(MCCONF_TEMP)
typedef enum {
DEBUG_SAMPLING_OFF = 0,
DEBUG_SAMPLING_NOW,
DEBUG_SAMPLING_START,
DEBUG_SAMPLING_TRIGGER_START,
DEBUG_SAMPLING_TRIGGER_FAULT,
DEBUG_SAMPLING_TRIGGER_START_NOSEND,
DEBUG_SAMPLING_TRIGGER_FAULT_NOSEND,
DEBUG_SAMPLING_SEND_LAST_SAMPLES
} debug_sampling_mode;
// Communication commands
typedef enum {
COMM_FW_VERSION = 0,
COMM_JUMP_TO_BOOTLOADER,
COMM_ERASE_NEW_APP,
COMM_WRITE_NEW_APP_DATA,
COMM_GET_VALUES,
COMM_SET_DUTY,
COMM_SET_CURRENT,
COMM_SET_CURRENT_BRAKE,
COMM_SET_RPM,
COMM_SET_POS,
COMM_SET_HANDBRAKE,
COMM_SET_DETECT,
COMM_SET_SERVO_POS,
COMM_SET_MCCONF,
COMM_GET_MCCONF,
COMM_GET_MCCONF_DEFAULT,
COMM_SET_APPCONF,
COMM_GET_APPCONF,
COMM_GET_APPCONF_DEFAULT,
COMM_SAMPLE_PRINT,
COMM_TERMINAL_CMD,
COMM_PRINT,
COMM_ROTOR_POSITION,
COMM_EXPERIMENT_SAMPLE,
COMM_DETECT_MOTOR_PARAM,
COMM_DETECT_MOTOR_R_L,
COMM_DETECT_MOTOR_FLUX_LINKAGE,
COMM_DETECT_ENCODER,
COMM_DETECT_HALL_FOC,
COMM_REBOOT,
COMM_ALIVE,
COMM_GET_DECODED_PPM,
COMM_GET_DECODED_ADC,
COMM_GET_DECODED_CHUK,
COMM_FORWARD_CAN,
COMM_SET_CHUCK_DATA,
COMM_CUSTOM_APP_DATA,
COMM_NRF_START_PAIRING,
COMM_GPD_SET_FSW,
COMM_GPD_BUFFER_NOTIFY,
COMM_GPD_BUFFER_SIZE_LEFT,
COMM_GPD_FILL_BUFFER,
COMM_GPD_OUTPUT_SAMPLE,
COMM_GPD_SET_MODE,
COMM_GPD_FILL_BUFFER_INT8,
COMM_GPD_FILL_BUFFER_INT16,
COMM_GPD_SET_BUFFER_INT_SCALE,
COMM_GET_VALUES_SETUP,
COMM_SET_MCCONF_TEMP,
COMM_SET_MCCONF_TEMP_SETUP,
COMM_GET_VALUES_SELECTIVE,
COMM_GET_VALUES_SETUP_SELECTIVE,
COMM_EXT_NRF_PRESENT,
COMM_EXT_NRF_ESB_SET_CH_ADDR,
COMM_EXT_NRF_ESB_SEND_DATA,
COMM_EXT_NRF_ESB_RX_DATA,
COMM_EXT_NRF_SET_ENABLED,
COMM_DETECT_MOTOR_FLUX_LINKAGE_OPENLOOP,
COMM_DETECT_APPLY_ALL_FOC,
COMM_JUMP_TO_BOOTLOADER_ALL_CAN,
COMM_ERASE_NEW_APP_ALL_CAN,
COMM_WRITE_NEW_APP_DATA_ALL_CAN,
COMM_PING_CAN,
COMM_APP_DISABLE_OUTPUT,
COMM_TERMINAL_CMD_SYNC,
COMM_GET_IMU_DATA,
COMM_BM_CONNECT,
COMM_BM_ERASE_FLASH_ALL,
COMM_BM_WRITE_FLASH,
COMM_BM_REBOOT,
COMM_BM_DISCONNECT,
COMM_BM_MAP_PINS_DEFAULT,
COMM_BM_MAP_PINS_NRF5X,
COMM_ERASE_BOOTLOADER,
COMM_ERASE_BOOTLOADER_ALL_CAN,
COMM_PLOT_INIT,
COMM_PLOT_DATA,
COMM_PLOT_ADD_GRAPH,
COMM_PLOT_SET_GRAPH,
COMM_GET_DECODED_BALANCE,
COMM_BM_MEM_READ,
COMM_WRITE_NEW_APP_DATA_LZO,
COMM_WRITE_NEW_APP_DATA_ALL_CAN_LZO,
COMM_BM_WRITE_FLASH_LZO,
COMM_SET_CURRENT_REL,
COMM_CAN_FWD_FRAME,
COMM_SET_BATTERY_CUT,
COMM_SET_BLE_NAME,
COMM_SET_BLE_PIN,
COMM_SET_CAN_MODE,
COMM_GET_IMU_CALIBRATION,
COMM_GET_MCCONF_TEMP,
// Custom configuration for hardware
COMM_GET_CUSTOM_CONFIG_XML,
COMM_GET_CUSTOM_CONFIG,
COMM_GET_CUSTOM_CONFIG_DEFAULT,
COMM_SET_CUSTOM_CONFIG,
// BMS commands
COMM_BMS_GET_VALUES,
COMM_BMS_SET_CHARGE_ALLOWED,
COMM_BMS_SET_BALANCE_OVERRIDE,
COMM_BMS_RESET_COUNTERS,
COMM_BMS_FORCE_BALANCE,
COMM_BMS_ZERO_CURRENT_OFFSET,
// FW updates commands for different HW types
COMM_JUMP_TO_BOOTLOADER_HW,
COMM_ERASE_NEW_APP_HW,
COMM_WRITE_NEW_APP_DATA_HW,
COMM_ERASE_BOOTLOADER_HW,
COMM_JUMP_TO_BOOTLOADER_ALL_CAN_HW,
COMM_ERASE_NEW_APP_ALL_CAN_HW,
COMM_WRITE_NEW_APP_DATA_ALL_CAN_HW,
COMM_ERASE_BOOTLOADER_ALL_CAN_HW,
COMM_SET_ODOMETER,
// ENNOID-BMS specific
COMM_STORE_BMS_CONF = 150,
COMM_GET_BMS_CELLS,
COMM_GET_BMS_AUX,
COMM_GET_BMS_EXP_TEMP
} COMM_PACKET_ID;
typedef struct {
int js_x;
int js_y;
int acc_x;
int acc_y;
int acc_z;
bool bt_c;
bool bt_z;
} chuck_data;
struct bldc_detect {
Q_GADGET
Q_PROPERTY(double cycle_int_limit MEMBER cycle_int_limit)
Q_PROPERTY(double bemf_coupling_k MEMBER bemf_coupling_k)
Q_PROPERTY(QVector<int> hall_table MEMBER hall_table)
Q_PROPERTY(int hall_res MEMBER hall_res)
public:
double cycle_int_limit;
double bemf_coupling_k;
QVector<int> hall_table;
int hall_res;
};
Q_DECLARE_METATYPE(bldc_detect)
typedef enum {
NRF_PAIR_STARTED = 0,
NRF_PAIR_OK,
NRF_PAIR_FAIL
} NRF_PAIR_RES;
typedef enum {
OP_STATE_INIT = 0, // 0
OP_STATE_CHARGING, // 1
OP_STATE_PRE_CHARGE, // 2
OP_STATE_LOAD_ENABLED, // 3
OP_STATE_BATTERY_DEAD, // 4
OP_STATE_POWER_DOWN, // 5
OP_STATE_EXTERNAL, // 6
OP_STATE_ERROR, // 7
OP_STATE_ERROR_PRECHARGE, // 8
OP_STATE_BALANCING, // 9
OP_STATE_CHARGED, // 10
OP_STATE_FORCEON, // 11
} OperationalStateTypedef;
#endif // DATATYPES_H

58
debugprintpage.cpp Normal file
View File

@@ -0,0 +1,58 @@
#include "debugprintpage.h"
#include "ui_debugprintpage.h"
#include <QTimer>
#include <QTextCursor>
#include <QDateTime>
DebugPrintPage *DebugPrintPage::currentMsgHandler = 0;
DebugPrintPage::DebugPrintPage(QWidget *parent) :
QFrame(parent),
ui(new Ui::DebugPrintPage)
{
ui->setupUi(this);
currentMsgHandler = this;
connect(ui->clearButton, &QPushButton::clicked, this, &DebugPrintPage::on_clearButton_clicked);
}
DebugPrintPage::~DebugPrintPage()
{
delete ui;
}
void DebugPrintPage::on_clearButton_clicked()
{
clearConsole();
}
void DebugPrintPage::clearConsole()
{
ui->consoleBrowser->clear();
}
BMSInterface *DebugPrintPage::bms() const
{
return mDieBieMS;
}
void DebugPrintPage::setDieBieMS(BMSInterface *dieBieMS)
{
mDieBieMS = dieBieMS;
if (mDieBieMS) {
connect(mDieBieMS->commands(), SIGNAL(printReceived(QString)),
this, SLOT(printReceived(QString)));
}
}
void DebugPrintPage::printConsole(QString str)
{
ui->consoleBrowser->moveCursor(QTextCursor::End);
ui->consoleBrowser->insertHtml(QDateTime::currentDateTime().
toString("yyyy-MM-dd hh:mm:ss: ")
+ str);
ui->consoleBrowser->moveCursor(QTextCursor::End);
}

39
debugprintpage.h Normal file
View File

@@ -0,0 +1,39 @@
#ifndef DEBUGPRINTPAGE_H
#define DEBUGPRINTPAGE_H
#include <QFrame>
#include <QButtonGroup>
#include "bmsinterface.h"
namespace Ui {
class DebugPrintPage;
}
class DebugPrintPage : public QFrame
{
Q_OBJECT
public:
explicit DebugPrintPage(QWidget *parent = nullptr);
~DebugPrintPage();
static DebugPrintPage* currentMsgHandler;
void clearConsole();
BMSInterface *bms() const;
void setDieBieMS(BMSInterface *dieBieMS);
public slots:
void printConsole(QString str);
private slots:
void on_clearButton_clicked();
private:
Ui::DebugPrintPage *ui;
BMSInterface *mDieBieMS;
};
#endif // DEBUGPRINTPAGE_H

233
debugprintpage.ui Normal file
View File

@@ -0,0 +1,233 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>DebugPrintPage</class>
<widget class="QFrame" name="DebugPrintPage">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>1071</width>
<height>755</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<property name="styleSheet">
<string notr="true">
QWidget#DebugPrintPage
{
background-color: #f7f8fc
}
QPushButton#pbVisualizationMenuBarVoltage
{
border: 2px solid lightgrey;
border-top-left-radius: 15px;
background: white;
color: black;
font-size: 18px;
border-right-color: white;
}
QPushButton#pbVisualizationMenuBarAmperage
{
border: 2px solid lightgrey;
/* border-top-left-radius: 20px;*/
background: white;
color: black;
font-size: 18px;
border-left-color: white;
border-right-color: white;
}
QPushButton#pbVisualizationMenuBarTemperature
{
border: 2px solid lightgrey;
background: white;
color: black;
font-size: 18px;
border-left-color: white;
border-right-color: white;
}
/*----------------------------------------------------------------------*/
QPushButton#pbVisualizationMenuBarVoltage:hover:checked,
#pbVisualizationMenuBarAmperage:hover:checked,
#pbVisualizationMenuBarTemperature:hover:checked
{
/*background-color: #01673a;*/
border-bottom-color:#009352;
color:#009352;
}
QPushButton#pbVisualizationMenuBarVoltage:!hover:checked,
#pbVisualizationMenuBarAmperage:!hover:checked,
#pbVisualizationMenuBarTemperature:!hover:checked
{
/*background-color: #01673a;*/
border-bottom-color: #009352;
color:#009352;
}
QPushButton#pbVisualizationMenuBarVoltage:hover:!checked,
#pbVisualizationMenuBarAmperage:hover:!checked,
#pbVisualizationMenuBarTemperature:hover:!checked
{
/*background-color: #009352;*/
/*background-color: #028249;*/
border-bottom-color: lightgrey;
color:black;
}
</string>
</property>
<widget class="QLabel" name="lbCenterTabName">
<property name="geometry">
<rect>
<x>50</x>
<y>30</y>
<width>931</width>
<height>41</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>26</pointsize>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Вывод информации</string>
</property>
<property name="alignment">
<set>Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft</set>
</property>
</widget>
<widget class="QLabel" name="labConsoleBrowserBackground">
<property name="geometry">
<rect>
<x>53</x>
<y>90</y>
<width>991</width>
<height>581</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>14</pointsize>
<weight>50</weight>
<bold>false</bold>
</font>
</property>
<property name="styleSheet">
<string notr="true"> background-color:#FFFFFF;
border: 2px solid lightgrey;
border-top-left-radius: 15px;
border-top-right-radius: 15px;
</string>
</property>
<property name="text">
<string/>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
<widget class="VTextBrowser" name="consoleBrowser">
<property name="geometry">
<rect>
<x>53</x>
<y>91</y>
<width>991</width>
<height>581</height>
</rect>
</property>
<property name="font">
<font>
<family>DejaVu Sans Mono</family>
</font>
</property>
<property name="styleSheet">
<string notr="true"> background-color:transparent;
border: 2px solid lightgrey;
border-top-left-radius: 15px;
border-top-right-radius: 15px;
</string>
</property>
<property name="lineWrapMode">
<enum>QTextEdit::NoWrap</enum>
</property>
</widget>
<widget class="QPushButton" name="clearButton">
<property name="geometry">
<rect>
<x>1000</x>
<y>680</y>
<width>44</width>
<height>40</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>Clear terminal</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="res.qrc">
<normaloff>:/res/icons/Delete-96.png</normaloff>:/res/icons/Delete-96.png</iconset>
</property>
<property name="iconSize">
<size>
<width>32</width>
<height>32</height>
</size>
</property>
</widget>
<widget class="QLineEdit" name="consoleEdit">
<property name="enabled">
<bool>false</bool>
</property>
<property name="geometry">
<rect>
<x>60</x>
<y>680</y>
<width>931</width>
<height>39</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>20</pointsize>
</font>
</property>
</widget>
<zorder>labConsoleBrowserBackground</zorder>
<zorder>lbCenterTabName</zorder>
<zorder>consoleBrowser</zorder>
<zorder>clearButton</zorder>
<zorder>consoleEdit</zorder>
</widget>
<customwidgets>
<customwidget>
<class>VTextBrowser</class>
<extends>QTextEdit</extends>
<header>widgets/vtextbrowser.h</header>
</customwidget>
</customwidgets>
<resources>
<include location="res.qrc"/>
</resources>
<connections/>
</ui>

313
digitalfiltering.cpp Normal file
View File

@@ -0,0 +1,313 @@
/*
Original copyright 2018 Benjamin Vedder benjamin@vedder.se and the VESC Tool project ( https://github.com/vedderb/vesc_tool )
Forked to:
Copyright 2018 Danny Bokma github@diebie.nl (https://github.com/DieBieEngineering/DieBieMS-Tool)
Now forked to:
Copyright 2019 - 2020 Kevin Dionne kevin.dionne@ennoid.me (https://github.com/EnnoidMe/ENNOID-BMS-Tool)
This file is part of ENNOID-BMS Tool.
ENNOID-BMS Tool is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
ENNOID-BMS Tool is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "digitalfiltering.h"
#include <cmath>
#include <QDebug>
DigitalFiltering::DigitalFiltering()
{
}
// Found at http://paulbourke.net/miscellaneous//dft/
// Dir: 0: Forward, != 0: Reverse
// m: 2^m points
// real: Real part
// imag: Imaginary part
void DigitalFiltering::fft(int dir, int m, double *real, double *imag)
{
long n,i,i1,j,k,i2,l,l1,l2;
double c1,c2,tx,ty,t1,t2,u1,u2,z;
// Calculate the number of points
n = 1 << m;
// Do the bit reversal
i2 = n >> 1;
j = 0;
for (i=0;i<n-1;i++) {
if (i < j) {
tx = real[i];
ty = imag[i];
real[i] = real[j];
imag[i] = imag[j];
real[j] = tx;
imag[j] = ty;
}
k = i2;
while (k <= j) {
j -= k;
k >>= 1;
}
j += k;
}
// Compute the FFT
c1 = -1.0;
c2 = 0.0;
l2 = 1;
for (l=0;l<m;l++) {
l1 = l2;
l2 <<= 1;
u1 = 1.0;
u2 = 0.0;
for (j=0;j < l1;j++) {
for (i=j;i < n;i += l2) {
i1 = i + l1;
t1 = u1 * real[i1] - u2 * imag[i1];
t2 = u1 * imag[i1] + u2 * real[i1];
real[i1] = real[i] - t1;
imag[i1] = imag[i] - t2;
real[i] += t1;
imag[i] += t2;
}
z = u1 * c1 - u2 * c2;
u2 = u1 * c2 + u2 * c1;
u1 = z;
}
c2 = sqrt((1.0 - c1) / 2.0);
if (dir) {
c2 = -c2;
}
c1 = sqrt((1.0 + c1) / 2.0);
}
// Scaling for reverse transform
if (dir) {
for (i=0;i < n;i++) {
real[i] /= n;
imag[i] /= n;
}
}
}
// Found at http://paulbourke.net/miscellaneous//dft/
void DigitalFiltering::dft(int dir, int len, double *real, double *imag) {
long i,k;
double arg;
double cosarg, sinarg;
if(dir) {
dir = 1;
} else {
dir = -1;
}
double *x2 = new double[len];
double *y2 = new double[len];
for (i=0;i < len;i++) {
x2[i] = 0;
y2[i] = 0;
arg = -(double)dir * 2.0 * M_PI * (double)i / (double)len;
for (k=0;k<len;k++) {
cosarg = cos(k * arg);
sinarg = sin(k * arg);
x2[i] += (real[k] * cosarg - imag[k] * sinarg);
y2[i] += (real[k] * sinarg + imag[k] * cosarg);
}
}
// Copy the data back
if (dir == 1) {
for (i=0;i<len;i++) {
real[i] = x2[i] / (double)len;
imag[i] = y2[i] / (double)len;
}
} else {
for (i=0;i<len;i++) {
real[i] = x2[i];
imag[i] = y2[i];
}
}
delete[] x2;
delete[] y2;
}
void DigitalFiltering::fftshift(double *data, int len)
{
for (int i = 0;i < (len / 2);i++) {
double r1 = data[i];
double r2 = data[len/2 + i];
data[i] = r2;
data[len / 2 + i] = r1;
}
}
void DigitalFiltering::hamming(double *data, int len)
{
if (len % 2 == 0) {
for (int i = 0;i < (len / 2);i++) {
double val = 0.54 - 0.46 * cos((2.0 * M_PI * (double)i)/(double)(len - 1));
data[i] *= val;
data[len - i - 1] *= val;
}
} else {
for (int i = 0;i < len;i++) {
data[i] *= 0.54 - 0.46 * cos((2.0 * M_PI * (double)i)/(double)(len - 1));
}
}
}
void DigitalFiltering::zeroPad(double *data, double *result, int dataLen, int resultLen)
{
for (int i = 0;i < resultLen;i++) {
if (i < dataLen) {
result[i] = data[i];
} else {
result[i] = 0;
}
}
}
int DigitalFiltering::whichPowerOfTwo(unsigned int number)
{
unsigned int powersOfTwo[32] =
{1,2,4,8,16,32,64,128,256,512,1024,2048,4096,8192,16384,32768,
65536,131072,262144,524288,1048576,2097152,4194304,8388608,
16777216,33554432,67108864,134217728,268435456,536870912,
1073741824UL,2147483648UL};
int exponent = 0;
while (powersOfTwo[exponent] < number && exponent < 31) {
exponent++;
}
return exponent;
}
QVector<double> DigitalFiltering::filterSignal(const QVector<double> &signal, const QVector<double> &filter, bool padAfter)
{
QVector<double> result;
int taps = filter.size();
for (int i = 0;i < taps / 2;i++) {
result.append(0.0);
}
if (!padAfter) {
for (int i = 0;i < taps / 2;i++) {
result.append(0.0);
}
}
for (int i = 0;i < signal.size() - taps;i++) {
double coeff = 0;
for (int j = 0;j < taps;j++) {
coeff += signal[i + j] * filter[j];
}
result.append(coeff);
}
if (padAfter) {
for (int i = 0;i < taps / 2;i++) {
result.append(0.0);
}
}
return result;
}
QVector<double> DigitalFiltering::generateFirFilter(double f_break, int bits, bool useHamming)
{
int taps = 1 << bits;
double imag[taps];
double filter_vector[taps];
for(int i = 0;i < taps;i++) {
if (i < (int)((double)taps * f_break)) {
filter_vector[i] = 1.0;
} else {
filter_vector[i] = 0.0;
}
imag[i] = 0;
}
for (int i = 0;i < taps / 2;i++) {
filter_vector[taps - i - 1] = filter_vector[i];
}
fft(1, bits, filter_vector, imag);
fftshift(filter_vector, taps);
if (useHamming) {
hamming(filter_vector, taps);
}
QVector<double> result;
for(int i = 0;i < taps;i++) {
result.append(filter_vector[i]);
}
return result;
}
QVector<double> DigitalFiltering::fftWithShift(QVector<double> &signal, int resultBits, bool scaleByLen)
{
QVector<double> result;
int taps = signal.size();
int resultLen = 1 << resultBits;
double *signal_vector = new double[resultLen];
double *imag = new double[resultLen];
if (resultLen < taps) {
int sizeDiffHalf = (taps - resultLen) / 2;
signal.remove(0, sizeDiffHalf);
signal.resize(resultLen);
for(int i = 0;i < resultLen;i++) {
signal_vector[i] = signal[i];
imag[i] = 0;
}
} else {
int sizeDiffHalf = (resultLen - taps) / 2;
for(int i = 0;i < resultLen;i++) {
if (i < sizeDiffHalf) {
signal_vector[i] = 0;
} else if (i < (taps + sizeDiffHalf)) {
signal_vector[i] = signal[i - sizeDiffHalf];
} else {
signal_vector[i] = 0;
}
imag[i] = 0;
}
}
fftshift(signal_vector, resultLen);
fft(0, resultBits, signal_vector, imag);
double div_factor = scaleByLen ? (double)taps : 1.0;
for(int i = 0;i < resultLen;i++) {
result.append(fabs(signal_vector[i]) / div_factor);
}
delete[] signal_vector;
delete[] imag;
return result;
}

48
digitalfiltering.h Normal file
View File

@@ -0,0 +1,48 @@
/*
Original copyright 2018 Benjamin Vedder benjamin@vedder.se and the VESC Tool project ( https://github.com/vedderb/vesc_tool )
Forked to:
Copyright 2018 Danny Bokma github@diebie.nl (https://github.com/DieBieEngineering/DieBieMS-Tool)
Now forked to:
Copyright 2019 - 2020 Kevin Dionne kevin.dionne@ennoid.me (https://github.com/EnnoidMe/ENNOID-BMS-Tool)
This file is part of ENNOID-BMS Tool.
ENNOID-BMS Tool is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
ENNOID-BMS Tool is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef DIGITALFILTERING_H
#define DIGITALFILTERING_H
#include <QVector>
class DigitalFiltering
{
public:
DigitalFiltering();
static void fft(int dir,int m,double *real,double *imag);
static void dft(int dir, int len, double *real, double *imag);
static void fftshift(double *data, int len);
static void hamming(double *data, int len);
static void zeroPad(double *data, double *result, int dataLen, int resultLen);
static int whichPowerOfTwo(unsigned int number);
static QVector<double> filterSignal(const QVector<double> &signal, const QVector<double> &filter, bool padAfter = false);
static QVector<double> generateFirFilter(double f_break, int bits, bool useHamming);
static QVector<double> fftWithShift(QVector<double> &signal, int resultBits, bool scaleByLen = false);
};
#endif // DIGITALFILTERING_H

108
historylineeditbox.cpp Normal file
View File

@@ -0,0 +1,108 @@
/*
Copyright 2018 Benjamin Vedder benjamin@vedder.se
This file is part of VESC Tool.
VESC Tool is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
VESC Tool is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "historylineeditbox.h"
#include <QDebug>
HistoryLineEditBox::HistoryLineEditBox(QWidget *parent) :
QLineEdit(parent)
{
mIndexNow = 0;
installEventFilter(this);
}
bool HistoryLineEditBox::eventFilter(QObject *object, QEvent *e)
{
Q_UNUSED(object);
if (e->type() == QEvent::KeyPress)
{
QKeyEvent *keyEvent = static_cast<QKeyEvent *>(e);
/*
switch(keyEvent->key()) {
case Qt::Key_Up:
case Qt::Key_Down:
return false;
case Qt::Key_Return:
case Qt::Key_Enter:
emit enterClicked();
break;
default:
return false;
}
*/
bool retval = false;
switch(keyEvent->key()) {
case Qt::Key_Up: {
if (mIndexNow == 0) {
mCurrentText = text();
}
mIndexNow--;
int ind = mHistory.size() + mIndexNow;
if (ind >= 0 && ind < mHistory.size()) {
setText(mHistory.at(ind));
} else {
mIndexNow++;
}
} break;
case Qt::Key_Down:
mIndexNow++;
if (mIndexNow >= 0) {
if (mIndexNow == 0) {
setText(mCurrentText);
}
mIndexNow = 0;
} else {
int ind = mHistory.size() + mIndexNow;
if (ind >= 0 && ind < mHistory.size()) {
setText(mHistory.at(ind));
}
}
break;
case Qt::Key_Return:
case Qt::Key_Enter:
if (!text().isEmpty()) {
if (mHistory.isEmpty() || mHistory.last() != text()) {
mHistory.append(text());
}
}
mIndexNow = 0;
mCurrentText.clear();
retval = false;
emit enterClicked();
break;
default:
break;
}
return retval;
}
return false;
}

48
historylineeditbox.h Normal file
View File

@@ -0,0 +1,48 @@
/*
Copyright 2018 Benjamin Vedder benjamin@vedder.se
This file is part of VESC Tool.
VESC Tool is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
VESC Tool is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef HISTORYLINEEDITBOX_H
#define HISTORYLINEEDITBOX_H
#include <QObject>
#include <QWidget>
#include <QLineEdit>
#include <QStringList>
#include <QEvent>
#include <QKeyEvent>
class HistoryLineEditBox : public QLineEdit
{
Q_OBJECT
public:
HistoryLineEditBox(QWidget *parent = nullptr);
bool eventFilter(QObject *object, QEvent *e);
signals:
void enterClicked();
private:
QStringList mHistory;
int mIndexNow;
QString mCurrentText;
};
#endif // HISTORYLINEEDITBOX_H

9
hoverablecombobox.cpp Normal file
View File

@@ -0,0 +1,9 @@
#include "hoverablecombobox.h"
HoverableComboBox::HoverableComboBox(QWidget* parent) : QComboBox (parent)
{
}
HoverableComboBox::~HoverableComboBox()
{
}

35
hoverablecombobox.h Normal file
View File

@@ -0,0 +1,35 @@
#ifndef HOVERABLECOMBOBOX_H
#define HOVERABLECOMBOBOX_H
#include <QObject>
#include <QWidget>
#include <QComboBox>
class HoverableComboBox : public QComboBox
{
Q_OBJECT
public:
HoverableComboBox(QWidget *parent = nullptr);
~HoverableComboBox() override;
protected:
void hidePopup() override
{
QComboBox::hidePopup();
emit closed();
}
void showPopup() override
{
QComboBox::showPopup();
emit opened();
}
signals:
void opened();
void closed();
};
//HoverableComboBox(QObject *parent) : QComboBox (parent) { }
#endif // HOVERABLECOMBOBOX_H

BIN
images/IMAGE01.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

BIN
images/IMAGE02.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 97 KiB

View File

@@ -0,0 +1,39 @@
#include "languageselectorbutton.h"
LanguageSelectorButton::LanguageSelectorButton(QWidget* parent) : QPushButton (parent)
{
contextmenu = new QMenu(this);
actRuss = new QAction(QIcon(":/res/language-russ.png"), tr("Рус"), this);
actEng = new QAction(QIcon(":/res/language-eng.png"), tr("Анг"), this);
actRuss->setCheckable(true);
actEng->setCheckable(true);
actRuss->setChecked(true);
contextmenu->addAction(actRuss);
contextmenu->addAction(actEng);
contextmenu->setContextMenuPolicy(Qt::CustomContextMenu);
connect(contextmenu, &QMenu::triggered, this, &LanguageSelectorButton::onActActivated);
}
void LanguageSelectorButton::mousePressEvent(QMouseEvent *event)
{
contextmenu->exec(QCursor::pos());
}
void LanguageSelectorButton::onActActivated(QAction* _pAction)
{
if (_pAction == actRuss)
{
emit sigSetRussianLanguage();
}
else if (_pAction == actEng)
{
emit sigSetEnglishLanguage();
}
}

32
languageselectorbutton.h Normal file
View File

@@ -0,0 +1,32 @@
#ifndef LANGUAGESELECTORBUTTON_H
#define LANGUAGESELECTORBUTTON_H
#include <QObject>
#include <QWidget>
#include <QPushButton>
#include <QMouseEvent>
#include <QMenu>
class LanguageSelectorButton : public QPushButton
{
Q_OBJECT
public:
LanguageSelectorButton(QWidget *parent = nullptr);
protected:
void mousePressEvent(QMouseEvent*event);
signals:
void sigSetRussianLanguage();
void sigSetEnglishLanguage();
private slots:
void onActActivated(QAction* act);
private:
QMenu *contextmenu;
QAction* actRuss;
QAction* actEng;
};
#endif // LANGUAGESELECTORBUTTON_H

146
main.cpp Normal file
View File

@@ -0,0 +1,146 @@
/*
Original copyright 2018 Benjamin Vedder benjamin@vedder.se and the VESC Tool project (
https://github.com/vedderb/vesc_tool ) Now forked to: Danny Bokma github@diebie.nl
This file is part of BMS Tool.
ENNOID-BMS Tool is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
ENNOID-BMS Tool is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "mainwindow.h"
#include "mainwindownew.h"
#include "connectandenterpage.h"
#include <QApplication>
#include <QStyleFactory>
#include <QSettings>
#include <QDesktopWidget>
#include <QFontDatabase>
#include "mobile/qmlui.h"
#define NEW_GUI 1
int main(int argc, char *argv[])
{
constexpr char const *APP_NAME = "ENNOID-BMS Tool";
// Settings
QCoreApplication::setOrganizationName("DieBie");
QCoreApplication::setOrganizationDomain("diebie.nl");
QCoreApplication::setApplicationName(APP_NAME);
// DPI settings
// TODO: http://www.qcustomplot.com/index.php/support/forum/1344
QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
#ifdef USE_MOBILE
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
#else
QCoreApplication::setAttribute(Qt::AA_Use96Dpi);
QSettings set;
bool scaleAuto = true;
double scale = 1.0;
if (set.contains("app_scale_auto")) {
scaleAuto = set.value("app_scale_auto").toBool();
} else {
set.setValue("app_scale_auto", scaleAuto);
}
if (scaleAuto) {
QApplication tmp(argc, argv);
QRect rec = tmp.desktop()->screenGeometry();
int height = rec.height();
int width = rec.width();
double ptFont = tmp.font().pointSizeF();
if (ptFont < 0.0) {
ptFont = tmp.font().pixelSize();
}
if (width > 3000 && height > 1700) {
scale = 1.5;
} else {
if (ptFont > 11.0) {
scale = ptFont / 11.0;
}
}
set.setValue("app_scale_factor", scale);
} else if (set.contains("app_scale_factor")) {
scale = set.value("app_scale_factor").toDouble();
}
set.setValue("app_scale_factor", scale);
#ifdef Q_OS_ANDROID
scale = 1.0;
#endif
if (scale > 1.01) {
qputenv("QT_SCALE_FACTOR", QString::number(scale).toLocal8Bit());
}
#endif
QApplication a(argc, argv);
#if NEW_GUI
// Fonts
QFontDatabase::addApplicationFont("://res/fonts/Artifakt-Element.ttf");
qApp->setFont(QFont("Artifakt Element", 12));
#else
// Fonts
QFontDatabase::addApplicationFont("://res/fonts/DejaVuSans.ttf");
QFontDatabase::addApplicationFont("://res/fonts/DejaVuSans-Bold.ttf");
QFontDatabase::addApplicationFont("://res/fonts/DejaVuSans-BoldOblique.ttf");
QFontDatabase::addApplicationFont("://res/fonts/DejaVuSans-Oblique.ttf");
QFontDatabase::addApplicationFont("://res/fonts/DejaVuSansMono.ttf");
QFontDatabase::addApplicationFont("://res/fonts/DejaVuSansMono-Bold.ttf");
QFontDatabase::addApplicationFont("://res/fonts/DejaVuSansMono-BoldOblique.ttf");
QFontDatabase::addApplicationFont("://res/fonts/DejaVuSansMono-Oblique.ttf");
qApp->setFont(QFont("DejaVu Sans", 11));
// Style
a.setStyleSheet("");
a.setStyle(QStyleFactory::create("Fusion"));
#endif
#ifdef USE_MOBILE
QmlUi q;
q.startQmlUi();
#else
#if NEW_GUI
MainWindowNew w;
w.setWindowTitle(APP_NAME);
w.show();
//} else {
// a.closeAllWindows();
// return 0;
//}
#else
MainWindow w;
w.show();
#endif
#endif
return a.exec();
}

883
mainwindow.cpp Normal file
View File

@@ -0,0 +1,883 @@
/*
Original copyright 2018 Benjamin Vedder benjamin@vedder.se and the VESC Tool project ( https://github.com/vedderb/vesc_tool )
Forked to:
Copyright 2018 Danny Bokma github@diebie.nl (https://github.com/DieBieEngineering/DieBieMS-Tool)
Now forked to:
Copyright 2019 - 2020 Kevin Dionne kevin.dionne@ennoid.me (https://github.com/EnnoidMe/ENNOID-BMS-Tool)
This file is part of ENNOID-BMS Tool.
ENNOID-BMS Tool is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
ENNOID-BMS Tool is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "mainwindow.h"
namespace {
void myMessageOutput(QtMsgType type, const QMessageLogContext &context, const QString &msg)
{
QByteArray localMsg = msg.toLocal8Bit();
QString str;
QString typeStr;
bool isBad = false;
switch (type) {
case QtDebugMsg: typeStr = "DEBUG"; break;
// case QtInfoMsg: typeStr = "INFO"; break;
case QtWarningMsg: typeStr = "WARNING"; isBad = true; break;
case QtCriticalMsg: typeStr = "CRITICAL"; isBad = true; break;
case QtFatalMsg: typeStr = "FATAL"; isBad = true; break;
default:
break;
}
str.sprintf("%s (%s:%u %s): %s", typeStr.toLocal8Bit().data(),
context.file, context.line, context.function, localMsg.constData());
if (PageDebugPrint::currentMsgHandler) {
QString strTmp;
if (isBad) {
strTmp = "<font color=\"red\">" + str + "</font><br>";
} else {
strTmp = str + "<br>";
}
QMetaObject::invokeMethod(PageDebugPrint::currentMsgHandler, "printConsole",
Qt::QueuedConnection, Q_ARG(QString, strTmp));
}
printf("%s\n", str.toLocal8Bit().data());
fflush(stdout);
}
}
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
QLoggingCategory::setFilterRules("qt.network.ssl.warning=false");
mVersion = QString::number(DT_VERSION);
mDieBieMS = new BMSInterface(this);
mStatusInfoTime = 0;
mStatusLabel = new QLabel(this);
ui->statusBar->addPermanentWidget(mStatusLabel);
mTimer = new QTimer(this);
mKeyLeft = false;
mKeyRight = false;
mbmsConfigRead = false;
connect(mTimer, SIGNAL(timeout()),this, SLOT(timerSlot()));
connect(mDieBieMS, SIGNAL(statusMessage(QString,bool)),this, SLOT(showStatusInfo(QString,bool)));
connect(mDieBieMS, SIGNAL(messageDialog(QString,QString,bool,bool)),this, SLOT(showMessageDialog(QString,QString,bool,bool)));
connect(mDieBieMS, SIGNAL(serialPortNotWritable(QString)),this, SLOT(serialPortNotWritable(QString)));
connect(mDieBieMS->commands(), SIGNAL(bmsConfigCheckResult(QStringList)),this, SLOT(bmsConfigCheckResult(QStringList)));
connect(ui->actionAboutQt, SIGNAL(triggered(bool)),qApp, SLOT(aboutQt()));
// Remove the menu with the option to hide the toolbar
ui->mainToolBar->setContextMenuPolicy(Qt::PreventContextMenu);
ui->actionCanFwd->setVisible(false);
ui->pageLabel->setVisible(false);
ui->menuTerminal->menuAction()->setVisible(false);
ui->menuTools->menuAction()->setVisible(false);
ui->menuHelp->menuAction()->setVisible(false);
mDieBieMS->bmsConfig()->loadParamsXml("://res/config.xml");
mDieBieMS->infoConfig()->loadParamsXml("://res/info.xml");
reloadPages();
connect(mDieBieMS->bmsConfig(), SIGNAL(updated()),this, SLOT(bmsconfUpdated()));
qInstallMessageHandler(myMessageOutput);
mTimer->start(20);
// Restore size and position
if (mSettings.contains("mainwindow/size")) {
resize(mSettings.value("mainwindow/size").toSize());
}
if (mSettings.contains("mainwindow/position")) {
move(mSettings.value("mainwindow/position").toPoint());
}
if (mSettings.contains("mainwindow/maximized")) {
bool maximized = mSettings.value("mainwindow/maximized").toBool();
if (maximized) {
showMaximized();
}
}
mPageDebugPrint->printConsole("ENNOID-BMS Tool " + mVersion + " started<br>");
}
MainWindow::~MainWindow()
{
// Save settings
mSettings.setValue("version", mVersion);
mSettings.setValue("mainwindow/position", pos());
mSettings.setValue("mainwindow/maximized", isMaximized());
if (!isMaximized()) {
mSettings.setValue("mainwindow/size", size());
}
delete ui;
}
void MainWindow::timerSlot()
{
// Update status label
if (mStatusInfoTime) {
mStatusInfoTime--;
if (!mStatusInfoTime) {
mStatusLabel->setStyleSheet(qApp->styleSheet());
}
} else {
QString str = mDieBieMS->getConnectedPortName();
if (str != mStatusLabel->text()) {
mStatusLabel->setText(mDieBieMS->getConnectedPortName());
static QString statusLast = "";
if (str != statusLast) {
mPageDebugPrint->printConsole("Status: " + str + "<br>");
statusLast = str;
if ( str != "Not connected" )
{
ui->actionRtData->setChecked(true);
}
}
}
}
// CAN fwd
if (ui->actionCanFwd->isChecked() != mDieBieMS->commands()->getSendCan()) {
ui->actionCanFwd->setChecked(mDieBieMS->commands()->getSendCan());
}
// RT value data only every 5 iterations. Cells, aux & expansion_temp only every 20 iterations
if (ui->actionRtData->isChecked()) {
static int values_cnt = 0;
static int cells_cnt = 0;
static int aux_cnt = 0;
static int exp_temp_cnt = 0;
values_cnt++;
if(values_cnt >= 5) {
values_cnt = 0;
mDieBieMS->commands()->getValues();
}
cells_cnt++;
if(cells_cnt >= 20) {
cells_cnt = 0;
mDieBieMS->commands()->getCells();
}
aux_cnt++;
if(aux_cnt >= 20) {
aux_cnt = 0;
mDieBieMS->commands()->getAux();
}
exp_temp_cnt++;
if(exp_temp_cnt >= 20) {
exp_temp_cnt = 0;
mDieBieMS->commands()->getExpansionTemp();
}
}
// Send alive command once every 10 iterations
if (ui->actionSendAlive->isChecked()) {
static int alive_cnt = 0;
alive_cnt++;
if (alive_cnt >= 10) {
alive_cnt = 0;
mDieBieMS->commands()->sendAlive();
}
}
// Read configuration it isn't read since starting ENNOID-BMS Tool
if (mDieBieMS->isPortConnected()) {
static int conf_cnt = 0;
conf_cnt++;
if (conf_cnt >= 20) {
conf_cnt = 0;
if (!mbmsConfigRead) {
mDieBieMS->commands()->getBMSconf();
}
}
}
// Disable all data streaming when uploading firmware
if (mDieBieMS->commands()->getFirmwareUploadProgress() > 0.1) {
ui->actionSendAlive->setChecked(false);
ui->actionRtData->setChecked(false);
}
// Run startup checks
static bool has_run_start_checks = false;
if (!has_run_start_checks) {
has_run_start_checks = true;
checkUdev();
Utility::checkVersion(mDieBieMS);
}
}
void MainWindow::showStatusInfo(QString info, bool isGood)
{
if (isGood) {
mStatusLabel->setStyleSheet("QLabel { background-color : lightgreen; color : black; }");
mPageDebugPrint->printConsole("Status: " + info + "<br>");
} else {
mStatusLabel->setStyleSheet("QLabel { background-color : red; color : black; }");
mPageDebugPrint->printConsole("<font color=\"red\">Status: " + info + "</font><br>");
}
mStatusInfoTime = 80;
mStatusLabel->setText(info);
}
void MainWindow::showMessageDialog(const QString &title, const QString &msg, bool isGood, bool richText)
{
(void)richText;
if (isGood) {
QMessageBox::information(this, title, msg);
} else {
QMessageBox::warning(this, title, msg);
}
}
void MainWindow::serialPortNotWritable(const QString &port)
{
(void)port;
#ifdef Q_OS_LINUX
QMessageBox::StandardButton reply;
reply = QMessageBox::warning(this,
tr("Connect Serial Port"),
tr("The serial port is not writable. This can usually be fixed by "
"adding your user to the dialout, uucp and/or lock groups. Would "
"you like to do that?"),
QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
if (reply == QMessageBox::Yes) {
QString name = qgetenv("USER");
if (name.isEmpty()) {
name = qgetenv("USERNAME");
}
bool hasDialout = !runCmd("getent", QStringList() << "group" << "dialout").isEmpty();
bool hasUucp = !runCmd("getent", QStringList() << "group" << "uucp").isEmpty();
bool hasLock = !runCmd("getent", QStringList() << "group" << "lock").isEmpty();
QString grps;
if (hasDialout) {
grps += "dialout";
}
if (hasUucp) {
if (!grps.isEmpty()) {
grps += ",";
}
grps += "uucp";
}
if (hasLock) {
if (!grps.isEmpty()) {
grps += ",";
}
grps += "lock";
}
QProcess process;
process.setEnvironment(QProcess::systemEnvironment());
process.start("pkexec", QStringList() << "usermod" << "-aG" << grps << name);
waitProcess(process);
if (process.exitCode() == 0) {
showMessageDialog(tr("Command Result"),
tr("Result from command:\n\n"
"%1\n"
"You have to reboot for this "
"change to take effect.").
arg(QString(process.readAllStandardOutput())),
true, false);
} else {
showMessageDialog(tr("Command Result"),
tr("Running command failed."),
false, false);
}
process.close();
}
#endif
}
void MainWindow::bmsconfUpdated()
{
mbmsConfigRead = true;
}
void MainWindow::bmsConfigCheckResult(QStringList paramsNotSet)
{
if (!paramsNotSet.isEmpty()) {
ParamDialog::showParams(tr("Parameters truncated"),
tr("The following parameters were truncated because they were set outside "
"of their allowed limits."),
mDieBieMS->bmsConfig(),
paramsNotSet,
this);
}
}
void MainWindow::on_actionReconnect_triggered()
{
mDieBieMS->reconnectLastPort();
}
void MainWindow::on_actionDisconnect_triggered()
{
mDieBieMS->disconnectPort();
}
void MainWindow::on_actionReboot_triggered()
{
mDieBieMS->commands()->reboot();
}
void MainWindow::on_actionReadBMScconf_triggered()
{
mDieBieMS->commands()->getBMSconf();
}
void MainWindow::on_actionReadBMScconfDefault_triggered()
{
mDieBieMS->commands()->getBMSconfDefault();
}
void MainWindow::on_actionWriteBMScconf_triggered()
{
mDieBieMS->commands()->setBMSconf();
}
void MainWindow::on_actionSaveBMSConfXml_triggered()
{
QString path;
path = QFileDialog::getSaveFileName(this,
tr("Choose where to save the motor configuration XML file"),
".",
tr("Xml files (*.xml)"));
if (path.isNull()) {
return;
}
if (!path.toLower().endsWith(".xml")) {
path += ".xml";
}
bool res = mDieBieMS->bmsConfig()->saveXml(path, "bmsConfiguration");
if (res) {
showStatusInfo("Saved motor configuration", true);
} else {
showMessageDialog(tr("Save motor configuration"),
tr("Could not save motor configuration:<BR>"
"%1").arg(mDieBieMS->bmsConfig()->xmlStatus()),
false, false);
}
}
void MainWindow::on_actionLoadBMSConfXml_triggered()
{
QString path;
path = QFileDialog::getOpenFileName(this,
tr("Choose motor configuration file to load"),
".",
tr("Xml files (*.xml)"));
if (path.isNull()) {
return;
}
bool res = mDieBieMS->bmsConfig()->loadXml(path, "bmsConfiguration");
if (res) {
showStatusInfo("Loaded motor configuration", true);
} else {
showMessageDialog(tr("Load motor configuration"),
tr("Could not load motor configuration:<BR>"
"%1").arg(mDieBieMS->bmsConfig()->xmlStatus()),
false, false);
}
}
void MainWindow::on_actionExit_triggered()
{
qApp->exit();
}
void MainWindow::on_actionAbout_triggered()
{
QMessageBox::about(this, "ENNOID-BMS Tool", Utility::aboutText());
}
void MainWindow::on_actionLibrariesUsed_triggered()
{
QMessageBox::about(this, "Libraries Used",
tr("<b>Icons<br>"
"<a href=\"https://icons8.com/\">https://icons8.com/</a><br><br>"
"<b>Plotting<br>"
"<a href=\"http://qcustomplot.com/\">http://qcustomplot.com/</a>"));
}
void MainWindow::addPageItem(QString name, QString icon, QString groupIcon, bool bold, bool indented)
{
QListWidgetItem *item = new QListWidgetItem();
ui->pageList->addItem(item);
PageListItem *li = new PageListItem(name, icon, groupIcon, this);
li->setBold(bold);
li->setIndented(indented);
ui->pageList->setItemWidget(item, li);
}
void MainWindow::saveParamFileDialog(QString conf, bool wrapIfdef)
{
ConfigParams *params = 0;
if (conf.toLower() == "bmsconf") {
params = mDieBieMS->bmsConfig();
} else {
qWarning() << "Invalid conf" << conf;
return;
}
QString path;
path = QFileDialog::getSaveFileName(this,tr("Choose where to save the configuration header file"),".",tr("h files (*.h)"));
if (path.isNull()) {
return;
}
if (path.contains(" ")) {
showMessageDialog(tr("Save header"),tr("Spaces are not allowed in the filename."),false, false);
return;
}
if (!path.toLower().endsWith(".h")) {
path += ".h";
}
bool res = params->saveCDefines(path, wrapIfdef);
if (res) {
showStatusInfo("Saved C header", true);
} else {
showMessageDialog(tr("Save header"),
tr("Could not save header"),
false, false);
}
}
void MainWindow::showPage(const QString &name)
{
for (int i = 0;i < ui->pageList->count();i++) {
PageListItem *p = (PageListItem*)(ui->pageList->itemWidget(ui->pageList->item(i)));
if (p->name() == name) {
ui->pageList->setCurrentRow(i);
break;
}
}
}
void MainWindow::reloadPages()
{
// Remove pages (if any)
ui->pageList->clear();
while (ui->pageWidget->count() != 0) {
QWidget* widget = ui->pageWidget->widget(0);
ui->pageWidget->removeWidget(widget);
widget->deleteLater();
}
#if 0
mPageWelcome = new PageWelcome(this);
mPageWelcome->setDieBieMS(mDieBieMS);
ui->pageWidget->addWidget(mPageWelcome);
addPageItem(tr("Welcome"), "://res/icons/Home-96.png", "", true);
#endif
mPageConnection = new PageConnection(this);
mPageConnection->setDieBieMS(mDieBieMS);
ui->pageWidget->addWidget(mPageConnection);
addPageItem(tr("Connection"), "://res/icons/Connected-96.png", "", true);
mPageFirmware = new PageFirmware(this);
mPageFirmware->setDieBieMS(mDieBieMS);
ui->pageWidget->addWidget(mPageFirmware);
addPageItem(tr("Firmware"), "://res/icons/Electronics-96.png", "", true);
mPageMasterSettings = new PageMasterSettings(this);
mPageMasterSettings->setDieBieMS(mDieBieMS);
ui->pageWidget->addWidget(mPageMasterSettings);
addPageItem(tr("Settings"), "://res/icons/Outgoing Data-96.png", "", true);
#if 0 // disable
mPageMasterGeneral = new PageMasterGeneral(this);
mPageMasterGeneral->setDieBieMS(mDieBieMS);
ui->pageWidget->addWidget(mPageMasterGeneral);
addPageItem(tr("General"), "://res/icons/Horizontal Settings Mixer-96.png","", false, true);
#endif
#if 0 // disable
mPageMasterCell = new PageMasterCell(this);
mPageMasterCell->setDieBieMS(mDieBieMS);
ui->pageWidget->addWidget(mPageMasterCell);
addPageItem(tr("Cell Management"), "://res/icons/batteries-96.png","", false, true);
#endif
#if 0 // disable
mPageMasterSwitch = new PageMasterSwitch(this);
mPageMasterSwitch->setDieBieMS(mDieBieMS);
ui->pageWidget->addWidget(mPageMasterSwitch);
addPageItem(tr("Switch"), "://res/icons/Toggle Off-96_2.png","", false, true);
#endif
#if 0 // disable
mPageMasterSignals = new PageMasterSignals(this);
mPageMasterSignals->setDieBieMS(mDieBieMS);
ui->pageWidget->addWidget(mPageMasterSignals);
addPageItem(tr("Signals"), "://res/icons/bldc.png","", false, true);
#endif
#if 0
mPageMasterDisplay = new PageMasterDisplay(this);
mPageMasterDisplay->setDieBieMS(mDieBieMS);
ui->pageWidget->addWidget(mPageMasterDisplay);
addPageItem(tr("Display"), "://res/icons/Calculator-96.png","", false, true);
#endif
/*
mSlaveSettings = new PageSlaveSettings(this);
mSlaveSettings->setDieBieMS(mDieBieMS);
ui->pageWidget->addWidget(mSlaveSettings);
addPageItem(tr("Expansion Board Settings"), "://res/icons/Outgoing Data-96.png", "", true);
mPageSlaveGeneral = new PageSlaveGeneral(this);
mPageSlaveGeneral->setDieBieMS(mDieBieMS);
ui->pageWidget->addWidget(mPageSlaveGeneral);
addPageItem(tr("General"), "://res/icons/Horizontal Settings Mixer-96.png","", false, true);
mPageSlaveSwitch = new PageSlaveSwitch(this);
mPageSlaveSwitch->setDieBieMS(mDieBieMS);
ui->pageWidget->addWidget(mPageSlaveSwitch);
addPageItem(tr("Switch"), "://res/icons/Toggle Off-96_2.png","", false, true);
mPageSlaveIO = new PageSlaveIO(this);
mPageSlaveIO->setDieBieMS(mDieBieMS);
ui->pageWidget->addWidget(mPageSlaveIO);
addPageItem(tr("IO"), "://res/icons/Rs 232 Male-96.png","", false, true);
mPageSlaveFANs = new PageSlaveFAN(this);
mPageSlaveFANs->setDieBieMS(mDieBieMS);
ui->pageWidget->addWidget(mPageSlaveFANs);
addPageItem(tr("FANs"), "://res/icons/fan-96.png","", false, true);
*/
mPageDataAnalysis = new PageDataAnalysis(this);
mPageDataAnalysis->setDieBieMS(mDieBieMS);
ui->pageWidget->addWidget(mPageDataAnalysis);
addPageItem(tr("Data Analysis"), "://res/icons/Line Chart-96.png", "", true);
mPageRtData = new PageRtData(this);
mPageRtData->setDieBieMS(mDieBieMS);
ui->pageWidget->addWidget(mPageRtData);
addPageItem(tr("Realtime Data"), "://res/icons/rt_off.png", "", false, true);
mPageTerminal = new PageTerminal(this);
mPageTerminal->setDieBieMS(mDieBieMS);
ui->pageWidget->addWidget(mPageTerminal);
addPageItem(tr("Terminal"), "://res/icons/Console-96.png", "", true);
mPageDebugPrint = new PageDebugPrint(this);
ui->pageWidget->addWidget(mPageDebugPrint);
#if 0
addPageItem(tr("Debug Console"), "://res/icons/Bug-96.png", "", true);
#endif
#if 0
mPageSettings = new PageSettings(this);
ui->pageWidget->addWidget(mPageSettings);
addPageItem(tr("Tool Settings"), "://res/icons/Settings-96.png", "", true);
#endif
// Adjust sizes
QFontMetrics fm(this->font());
int width = fm.width("Welcome & Wizards++++++");
int height = fm.height();
for(int i = 0; i < ui->pageList->count(); i++) {
QListWidgetItem *item = ui->pageList->item(i);
item->setSizeHint(QSize(item->sizeHint().width(), height));
}
ui->pageList->setMinimumWidth(width);
ui->pageList->setMaximumWidth(width);
ui->pageLabel->setMaximumWidth(width);
ui->pageLabel->setMaximumHeight((394 * width) / 1549);
ui->pageList->setCurrentRow(0);
ui->pageWidget->setCurrentIndex(0);
}
void MainWindow::checkUdev()
{
// Check if udev rules for modemmanager are installed
#ifdef Q_OS_LINUX
QFileInfo fi_mm("/lib/udev/rules.d/77-mm-usb-device-blacklist.rules");
if (fi_mm.exists()) {
QFileInfo fi_vesc("/lib/udev/rules.d/45-vesc.rules");
if (!fi_vesc.exists()) {
QMessageBox::StandardButton reply;
reply = QMessageBox::information(this,
tr("Modemmenager"),
tr("It looks like modemmanager is installed on your system, and that "
"there are no ENNOID-BMS udev rules installed. This will cause a delay "
"from when you plug in the ENNOID-BMS until you can use it. Would you like "
"to add a udev rule to prevent modemmanager from grabbing the ENNOID-BMS?"),
QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
if (reply == QMessageBox::Yes) {
QFile f_vesc(QDir::temp().absoluteFilePath(fi_vesc.fileName()));
if (!f_vesc.open(QIODevice::WriteOnly | QIODevice::Text)) {
showMessageDialog(tr("Create File Error"),
f_vesc.errorString(),
false, false);
return;
}
f_vesc.write("# Prevent modemmanager from grabbing the ENNOID-BMS\n"
"ATTRS{idVendor}==\"0483\", ATTRS{idProduct}==\"5740\", ENV{ID_MM_DEVICE_IGNORE}=\"1\"\n");
f_vesc.close();
QFileInfo fi_new(f_vesc);
QProcess process;
process.setEnvironment(QProcess::systemEnvironment());
process.start("pkexec", QStringList() <<
"mv" <<
fi_new.absoluteFilePath() <<
fi_vesc.absolutePath());
waitProcess(process);
if (process.exitCode() == 0) {
QMessageBox::StandardButton reply;
reply = QMessageBox::information(this,
tr("Modemmenager"),
tr("The udev rule was created successfully. Would you like "
"to reload udev to apply the new rule?"),
QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
if (reply == QMessageBox::Yes) {
QProcess process;
process.setEnvironment(QProcess::systemEnvironment());
process.start("pkexec", QStringList() <<
"udevadm" <<
"control" <<
"--reload-rules");
waitProcess(process);
if (process.exitCode() == 0) {
showMessageDialog(tr("Command Result"),
tr("Reloaded udev rules sucessfully."),
true, false);
} else {
QString out = process.readAll();
if (out.isEmpty()) {
showMessageDialog(tr("Command Result"),
tr("Could not reload udev rules. A reboot is probably "
"required for this change to take effect."),
false, false);
}
}
process.close();
}
} else {
showMessageDialog(tr("Command Result"),
tr("Could not move rules file:\n\n"
"%1").
arg(QString(process.readAllStandardOutput())),
false, false);
}
process.close();
}
}
}
#endif
}
bool MainWindow::waitProcess(QProcess &process, bool block, int timeoutMs)
{
bool wasEnables = isEnabled();
bool killed = false;
if (block) {
setEnabled(false);
}
process.waitForStarted();
QEventLoop loop;
QTimer timeoutTimer;
timeoutTimer.setSingleShot(true);
timeoutTimer.start(timeoutMs);
connect(&process, SIGNAL(finished(int)), &loop, SLOT(quit()));
connect(&timeoutTimer, SIGNAL(timeout()), &loop, SLOT(quit()));
loop.exec();
if (process.state() == QProcess::Running) {
process.kill();
process.waitForFinished();
killed = true;
}
setEnabled(wasEnables);
return !killed;
}
QString MainWindow::runCmd(QString cmd, QStringList args)
{
QProcess process;
process.setEnvironment(QProcess::systemEnvironment());
process.start(cmd, args);
waitProcess(process);
QString res = process.readAllStandardOutput();
process.close();
return res;
}
void MainWindow::on_pageList_currentRowChanged(int currentRow)
{
if (currentRow >= 0) {
ui->pageWidget->setCurrentIndex(currentRow);
}
}
void MainWindow::on_actionParameterEditorBMScconf_triggered()
{
ParameterEditor *p = new ParameterEditor(this);
p->setAttribute(Qt::WA_DeleteOnClose);
p->setParams(mDieBieMS->bmsConfig());
p->show();
}
void MainWindow::on_actionParameterEditorInfo_triggered()
{
ParameterEditor *p = new ParameterEditor(this);
p->setAttribute(Qt::WA_DeleteOnClose);
p->setParams(mDieBieMS->infoConfig());
p->show();
}
void MainWindow::on_actionSaveBMSConfigurationHeader_triggered()
{
saveParamFileDialog("bmsconf", false);
}
void MainWindow::on_actionSaveBMSConfigurationHeaderWrap_triggered()
{
saveParamFileDialog("bmsconf", true);
}
void MainWindow::on_actionTerminalPrintFaults_triggered()
{
mDieBieMS->commands()->sendTerminalCmd("faults");
showPage("DieBieMS Terminal");
}
void MainWindow::on_actionTerminalShowHelp_triggered()
{
mDieBieMS->commands()->sendTerminalCmd("help");
showPage("DieBieMS Terminal");
}
void MainWindow::on_actionTerminalClear_triggered()
{
mPageTerminal->clearTerminal();
showPage("DieBieMS Terminal");
}
void MainWindow::on_actionTerminalPrintThreads_triggered()
{
mDieBieMS->commands()->sendTerminalCmd("threads");
showPage("DieBieMS Terminal");
}
void MainWindow::on_actionCanFwd_toggled(bool arg1)
{
mDieBieMS->commands()->setSendCan(arg1);
}
void MainWindow::on_actionSafetyInformation_triggered()
{
HelpDialog::showHelp(this, mDieBieMS->infoConfig(), "wizard_startup_usage");
}
void MainWindow::on_actionWarrantyStatement_triggered()
{
HelpDialog::showHelp(this, mDieBieMS->infoConfig(), "wizard_startup_warranty");
}
void MainWindow::on_actionDieBieMSToolChangelog_triggered()
{
HelpDialog::showHelp(this, "ENNOID-BMS Tool Changelog", Utility::vescToolChangeLog());
}
void MainWindow::on_actionFirmwareChangelog_triggered()
{
HelpDialog::showHelp(this, "Firmware Changelog", Utility::fwChangeLog());
}
void MainWindow::on_actionLicense_triggered()
{
HelpDialog::showHelp(this, mDieBieMS->infoConfig(), "gpl_text");
}
void MainWindow::on_actionStoreBMScconf_triggered()
{
mDieBieMS->commands()->storeBMSConfig();
}
void MainWindow::on_actionCalibrateZero_triggered()
{
if ( mDieBieMS == NULL ) {
return;
}
if ( !mDieBieMS->isPortConnected() ) {
QMessageBox::critical(this,
tr("Connection Error"),
tr("The ENNOID-BMS is not connected. Please connect it."));
return;
}
mDieBieMS->commands()->sendTerminalCmd("setZeroCurrent");
QTimer::singleShot(1000, [this](){
this->mDieBieMS->commands()->sendTerminalCmd("config_write");
});
}

170
mainwindow.h Normal file
View File

@@ -0,0 +1,170 @@
/*
Original copyright 2018 Benjamin Vedder benjamin@vedder.se and the VESC Tool project ( https://github.com/vedderb/vesc_tool )
Forked to:
Copyright 2018 Danny Bokma github@diebie.nl (https://github.com/DieBieEngineering/DieBieMS-Tool)
Now forked to:
Copyright 2019 - 2020 Kevin Dionne kevin.dionne@ennoid.me (https://github.com/EnnoidMe/ENNOID-BMS-Tool)
This file is part of ENNOID-BMS Tool.
ENNOID-BMS Tool is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
ENNOID-BMS Tool is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QLabel>
#include <QTimer>
#include <QProcess>
#include <QSettings>
#include <QMessageBox>
#include <QDebug>
#include <QFileDialog>
#include <QListWidgetItem>
#include <cmath>
#include <QEventLoop>
#include <QDesktopServices>
#include <QLoggingCategory>
#include "bmsinterface.h"
#include "ui_mainwindow.h"
#include "parametereditor.h"
#include "utility.h"
#include "widgets/pagelistitem.h"
#include "widgets/helpdialog.h"
#include "widgets/paramdialog.h"
#include "pages/pagewelcome.h"
#include "pages/pageconnection.h"
#include "pages/pagedataanalysis.h"
#include "pages/pagertdata.h"
#include "pages/pageterminal.h"
#include "pages/pagefirmware.h"
#include "pages/pagedebugprint.h"
#include "pages/pagemastersettings.h"
#include "pages/pagemastergeneral.h"
#include "pages/pagemasterswitch.h"
#include "pages/pagemastercell.h"
#include "pages/pagemastersignals.h"
#include "pages/pagemasterdisplay.h"
#include "pages/pageslavesettings.h"
#include "pages/pageslavegeneral.h"
#include "pages/pageslaveswitch.h"
#include "pages/pageslaveio.h"
#include "pages/pageslavefan.h"
#include "pages/pagesettings.h"
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
private slots:
void timerSlot();
void showStatusInfo(QString info, bool isGood);
void showMessageDialog(const QString &title, const QString &msg, bool isGood, bool richText);
void serialPortNotWritable(const QString &port);
void bmsconfUpdated();
void bmsConfigCheckResult(QStringList paramsNotSet);
void on_actionReconnect_triggered();
void on_actionDisconnect_triggered();
void on_actionReboot_triggered();
void on_actionReadBMScconf_triggered();
void on_actionReadBMScconfDefault_triggered();
void on_actionWriteBMScconf_triggered();
void on_actionSaveBMSConfXml_triggered();
void on_actionLoadBMSConfXml_triggered();
void on_actionExit_triggered();
void on_actionAbout_triggered();
void on_actionLibrariesUsed_triggered();
void on_pageList_currentRowChanged(int currentRow);
void on_actionParameterEditorBMScconf_triggered();
void on_actionParameterEditorInfo_triggered();
void on_actionSaveBMSConfigurationHeader_triggered();
void on_actionSaveBMSConfigurationHeaderWrap_triggered();
void on_actionTerminalPrintFaults_triggered();
void on_actionTerminalShowHelp_triggered();
void on_actionTerminalClear_triggered();
void on_actionTerminalPrintThreads_triggered();
void on_actionCanFwd_toggled(bool arg1);
void on_actionSafetyInformation_triggered();
void on_actionWarrantyStatement_triggered();
void on_actionDieBieMSToolChangelog_triggered();
void on_actionFirmwareChangelog_triggered();
void on_actionLicense_triggered();
void on_actionStoreBMScconf_triggered();
void on_actionCalibrateZero_triggered();
private:
Ui::MainWindow *ui;
QSettings mSettings;
QString mVersion;
BMSInterface *mDieBieMS;
QTimer *mTimer;
QLabel *mStatusLabel;
int mStatusInfoTime;
bool mKeyLeft;
bool mKeyRight;
bool mbmsConfigRead;
PageWelcome *mPageWelcome;
PageConnection *mPageConnection;
PageDataAnalysis *mPageDataAnalysis;
PageRtData *mPageRtData;
PageTerminal *mPageTerminal;
PageFirmware *mPageFirmware;
PageDebugPrint *mPageDebugPrint;
PageMasterSettings *mPageMasterSettings;
PageMasterGeneral *mPageMasterGeneral;
PageMasterSwitch *mPageMasterSwitch;
PageMasterCell *mPageMasterCell;
PageMasterSignals *mPageMasterSignals;
PageMasterDisplay *mPageMasterDisplay;
PageSlaveSettings *mSlaveSettings;
PageSlaveGeneral *mPageSlaveGeneral;
PageSlaveSwitch *mPageSlaveSwitch;
PageSlaveIO *mPageSlaveIO;
PageSlaveFAN *mPageSlaveFANs;
PageSettings *mPageSettings;
void addPageItem(QString name, QString icon = "", QString groupIcon = "", bool bold = false, bool indented = false);
void saveParamFileDialog(QString conf, bool wrapIfdef);
void showPage(const QString &name);
void reloadPages();
void checkUdev();
bool waitProcess(QProcess &process, bool block = true, int timeoutMs = 300000);
QString runCmd(QString cmd, QStringList args);
};
#endif // MAINWINDOW_H

550
mainwindow.ui Normal file
View File

@@ -0,0 +1,550 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>1054</width>
<height>642</height>
</rect>
</property>
<property name="windowTitle">
<string>ENNOID-BMS Tool</string>
</property>
<property name="windowIcon">
<iconset resource="res.qrc">
<normaloff>:/res/icons/Car Battery-96.png</normaloff>:/res/icons/Car Battery-96.png</iconset>
</property>
<widget class="QWidget" name="centralWidget">
<layout class="QVBoxLayout" name="verticalLayout" stretch="1">
<property name="leftMargin">
<number>6</number>
</property>
<property name="bottomMargin">
<number>2</number>
</property>
<item>
<layout class="QHBoxLayout" name="horizontalLayout" stretch="0,1">
<item>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QLabel" name="pageLabel">
<property name="enabled">
<bool>true</bool>
</property>
<property name="maximumSize">
<size>
<width>220</width>
<height>56</height>
</size>
</property>
<property name="text">
<string/>
</property>
<property name="pixmap">
<pixmap resource="res.qrc">:/res/logo.png</pixmap>
</property>
<property name="scaledContents">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QListWidget" name="pageList">
<property name="minimumSize">
<size>
<width>220</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>220</width>
<height>16777215</height>
</size>
</property>
<property name="editTriggers">
<set>QAbstractItemView::NoEditTriggers</set>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QStackedWidget" name="pageWidget">
<property name="currentIndex">
<number>-1</number>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<widget class="QMenuBar" name="menuBar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>1054</width>
<height>22</height>
</rect>
</property>
<widget class="QMenu" name="menuFile">
<property name="title">
<string>File</string>
</property>
<addaction name="actionSaveBMSConfXml"/>
<addaction name="actionLoadBMSConfXml"/>
<addaction name="separator"/>
<addaction name="separator"/>
<addaction name="actionSaveBMSConfigurationHeader"/>
<addaction name="actionSaveBMSConfigurationHeaderWrap"/>
<addaction name="separator"/>
<addaction name="actionExit"/>
</widget>
<widget class="QMenu" name="menuHelp">
<property name="title">
<string>Help</string>
</property>
<addaction name="actionDieBieMSToolChangelog"/>
<addaction name="actionFirmwareChangelog"/>
<addaction name="separator"/>
<addaction name="actionAbout"/>
<addaction name="actionLibrariesUsed"/>
<addaction name="actionAboutQt"/>
<addaction name="separator"/>
<addaction name="actionSafetyInformation"/>
<addaction name="actionWarrantyStatement"/>
<addaction name="actionLicense"/>
</widget>
<widget class="QMenu" name="menuTools">
<property name="title">
<string>Developer</string>
</property>
<addaction name="actionParameterEditorBMScconf"/>
<addaction name="actionParameterEditorInfo"/>
</widget>
<widget class="QMenu" name="menuTerminal">
<property name="title">
<string>Terminal</string>
</property>
<addaction name="actionTerminalShowHelp"/>
<addaction name="actionTerminalPrintFaults"/>
<addaction name="separator"/>
<addaction name="actionTerminalClear"/>
</widget>
<widget class="QMenu" name="menuCommands">
<property name="title">
<string>Commands</string>
</property>
<addaction name="actionReboot"/>
<addaction name="actionCalibrateZero"/>
</widget>
<addaction name="menuFile"/>
<addaction name="menuCommands"/>
<addaction name="menuTerminal"/>
<addaction name="menuTools"/>
<addaction name="menuHelp"/>
</widget>
<widget class="QToolBar" name="mainToolBar">
<property name="movable">
<bool>false</bool>
</property>
<property name="iconSize">
<size>
<width>24</width>
<height>24</height>
</size>
</property>
<property name="toolButtonStyle">
<enum>Qt::ToolButtonIconOnly</enum>
</property>
<property name="floatable">
<bool>false</bool>
</property>
<attribute name="toolBarArea">
<enum>RightToolBarArea</enum>
</attribute>
<attribute name="toolBarBreak">
<bool>false</bool>
</attribute>
<addaction name="actionReconnect"/>
<addaction name="actionDisconnect"/>
<addaction name="separator"/>
<addaction name="actionReadBMScconf"/>
<addaction name="actionReadBMScconfDefault"/>
<addaction name="actionWriteBMScconf"/>
<addaction name="separator"/>
<addaction name="actionStoreBMScconf"/>
<addaction name="separator"/>
<addaction name="actionRtData"/>
<addaction name="actionSendAlive"/>
<addaction name="actionCanFwd"/>
<addaction name="actionCalibrateZero"/>
</widget>
<widget class="QStatusBar" name="statusBar"/>
<action name="actionReadBMScconf">
<property name="icon">
<iconset resource="res.qrc">
<normaloff>:/res/icons/motor_up.png</normaloff>:/res/icons/motor_up.png</iconset>
</property>
<property name="text">
<string>readBMScconf</string>
</property>
<property name="toolTip">
<string>Read BMS Configuration</string>
</property>
</action>
<action name="actionReadBMScconfDefault">
<property name="icon">
<iconset resource="res.qrc">
<normaloff>:/res/icons/motor_default.png</normaloff>:/res/icons/motor_default.png</iconset>
</property>
<property name="text">
<string>readBMScconfDefault</string>
</property>
<property name="toolTip">
<string>Read default BMS configuration</string>
</property>
</action>
<action name="actionWriteBMScconf">
<property name="icon">
<iconset resource="res.qrc">
<normaloff>:/res/icons/motor_down.png</normaloff>:/res/icons/motor_down.png</iconset>
</property>
<property name="text">
<string>writeBMScconf</string>
</property>
<property name="toolTip">
<string>Write BMS configuration</string>
</property>
</action>
<action name="actionSaveBMSConfXml">
<property name="icon">
<iconset resource="res.qrc">
<normaloff>:/res/icons/Save as-96.png</normaloff>:/res/icons/Save as-96.png</iconset>
</property>
<property name="text">
<string>Save BMS Configuration XML as...</string>
</property>
</action>
<action name="actionLoadBMSConfXml">
<property name="icon">
<iconset resource="res.qrc">
<normaloff>:/res/icons/Open Folder-96.png</normaloff>:/res/icons/Open Folder-96.png</iconset>
</property>
<property name="text">
<string>Load BMS Configuration XML</string>
</property>
</action>
<action name="actionDisconnect">
<property name="icon">
<iconset resource="res.qrc">
<normaloff>:/res/icons/Disconnected-96.png</normaloff>:/res/icons/Disconnected-96.png</iconset>
</property>
<property name="text">
<string>disconnect</string>
</property>
<property name="toolTip">
<string>Disconnect</string>
</property>
</action>
<action name="actionReboot">
<property name="icon">
<iconset resource="res.qrc">
<normaloff>:/res/icons/Restart-96.png</normaloff>:/res/icons/Restart-96.png</iconset>
</property>
<property name="text">
<string>Reboot</string>
</property>
<property name="toolTip">
<string>Reboot</string>
</property>
</action>
<action name="actionCanFwd">
<property name="checkable">
<bool>true</bool>
</property>
<property name="icon">
<iconset resource="res.qrc">
<normaloff>:/res/icons/can_off.png</normaloff>
<normalon>:/res/icons/can_on.png</normalon>:/res/icons/can_off.png</iconset>
</property>
<property name="text">
<string>canFwd</string>
</property>
<property name="toolTip">
<string>Forward communication over CAN-bus</string>
</property>
</action>
<action name="actionReconnect">
<property name="icon">
<iconset resource="res.qrc">
<normaloff>:/res/icons/Connected-96.png</normaloff>:/res/icons/Connected-96.png</iconset>
</property>
<property name="text">
<string>reconnect</string>
</property>
<property name="toolTip">
<string>Reconnect last connection</string>
</property>
</action>
<action name="actionRtData">
<property name="checkable">
<bool>true</bool>
</property>
<property name="icon">
<iconset resource="res.qrc">
<normaloff>:/res/icons/rt_off.png</normaloff>
<normalon>:/res/icons/rt_on.png</normalon>:/res/icons/rt_off.png</iconset>
</property>
<property name="text">
<string>rtData</string>
</property>
<property name="toolTip">
<string>Stream realtime data</string>
</property>
</action>
<action name="actionExit">
<property name="icon">
<iconset resource="res.qrc">
<normaloff>:/res/icons/Shutdown-96.png</normaloff>:/res/icons/Shutdown-96.png</iconset>
</property>
<property name="text">
<string>Exit</string>
</property>
</action>
<action name="actionAbout">
<property name="icon">
<iconset resource="res.qrc">
<normaloff>:/res/icons/About-96.png</normaloff>:/res/icons/About-96.png</iconset>
</property>
<property name="text">
<string>About ENNOID-BMS Tool</string>
</property>
<property name="toolTip">
<string>About ENNOID-BMS Tool</string>
</property>
</action>
<action name="actionParameterEditorBMScconf">
<property name="icon">
<iconset resource="res.qrc">
<normaloff>:/res/icons/Horizontal Settings Mixer-96.png</normaloff>:/res/icons/Horizontal Settings Mixer-96.png</iconset>
</property>
<property name="text">
<string>Parameter Editor Config</string>
</property>
</action>
<action name="actionSaveBMSConfigurationHeader">
<property name="icon">
<iconset resource="res.qrc">
<normaloff>:/res/icons/Save as-96.png</normaloff>:/res/icons/Save as-96.png</iconset>
</property>
<property name="text">
<string>Save BMS Configuration C Header as...</string>
</property>
</action>
<action name="actionSaveBMSConfigurationHeaderWrap">
<property name="icon">
<iconset resource="res.qrc">
<normaloff>:/res/icons/Save as-96.png</normaloff>:/res/icons/Save as-96.png</iconset>
</property>
<property name="text">
<string>Save BMS Configuration C Header (ifdef wrapped) as...</string>
</property>
<property name="toolTip">
<string>Save BMS Configuration C Header (ifdef wrapped) as</string>
</property>
</action>
<action name="actionRtDataApp">
<property name="checkable">
<bool>true</bool>
</property>
<property name="icon">
<iconset resource="res.qrc">
<normaloff>:/res/icons/rt_app_off.png</normaloff>
<normalon>:/res/icons/rt_app_on.png</normalon>:/res/icons/rt_app_off.png</iconset>
</property>
<property name="text">
<string>rtDataApp</string>
</property>
<property name="toolTip">
<string>Stream realtime app data</string>
</property>
</action>
<action name="actionTerminalPrintFaults">
<property name="icon">
<iconset resource="res.qrc">
<normaloff>:/res/icons/Console-96.png</normaloff>:/res/icons/Console-96.png</iconset>
</property>
<property name="text">
<string>Print Faults</string>
</property>
<property name="toolTip">
<string>Print Faults</string>
</property>
</action>
<action name="actionTerminalShowHelp">
<property name="icon">
<iconset resource="res.qrc">
<normaloff>:/res/icons/Help-96.png</normaloff>:/res/icons/Help-96.png</iconset>
</property>
<property name="text">
<string>Show Help</string>
</property>
</action>
<action name="actionTerminalClear">
<property name="icon">
<iconset resource="res.qrc">
<normaloff>:/res/icons/Delete-96.png</normaloff>:/res/icons/Delete-96.png</iconset>
</property>
<property name="text">
<string>Clear Terminal</string>
</property>
</action>
<action name="actionTerminalPrintThreads">
<property name="icon">
<iconset resource="res.qrc">
<normaloff>:/res/icons/Electronics-96.png</normaloff>:/res/icons/Electronics-96.png</iconset>
</property>
<property name="text">
<string>Print Threads</string>
</property>
<property name="toolTip">
<string>Print Threads</string>
</property>
</action>
<action name="actionSendAlive">
<property name="checkable">
<bool>true</bool>
</property>
<property name="icon">
<iconset resource="res.qrc">
<normaloff>:/res/icons/alive_off.png</normaloff>
<normalon>:/res/icons/alive_on.png</normalon>:/res/icons/alive_off.png</iconset>
</property>
<property name="text">
<string>Send Alive Commands</string>
</property>
<property name="toolTip">
<string>Send alive packets periodically to prevent timeout</string>
</property>
</action>
<action name="actionLibrariesUsed">
<property name="icon">
<iconset resource="res.qrc">
<normaloff>:/res/icons/About-96.png</normaloff>:/res/icons/About-96.png</iconset>
</property>
<property name="text">
<string>About Libraries Used</string>
</property>
</action>
<action name="actionAboutQt">
<property name="icon">
<iconset resource="res.qrc">
<normaloff>:/res/icons/About-96.png</normaloff>:/res/icons/About-96.png</iconset>
</property>
<property name="text">
<string>About Qt</string>
</property>
<property name="toolTip">
<string>Show the Qt library's About box</string>
</property>
</action>
<action name="actionParameterEditorInfo">
<property name="icon">
<iconset resource="res.qrc">
<normaloff>:/res/icons/Horizontal Settings Mixer-96.png</normaloff>:/res/icons/Horizontal Settings Mixer-96.png</iconset>
</property>
<property name="text">
<string>Parameter Editor Info</string>
</property>
</action>
<action name="actionSafetyInformation">
<property name="icon">
<iconset resource="res.qrc">
<normaloff>:/res/icons/About-96.png</normaloff>:/res/icons/About-96.png</iconset>
</property>
<property name="text">
<string>Safety Information</string>
</property>
</action>
<action name="actionWarrantyStatement">
<property name="icon">
<iconset resource="res.qrc">
<normaloff>:/res/icons/About-96.png</normaloff>:/res/icons/About-96.png</iconset>
</property>
<property name="text">
<string>Limited Warranty Statement</string>
</property>
<property name="toolTip">
<string>Limited Warranty Statement</string>
</property>
</action>
<action name="actionDieBieMSToolChangelog">
<property name="icon">
<iconset resource="res.qrc">
<normaloff>:/res/icons/About-96.png</normaloff>:/res/icons/About-96.png</iconset>
</property>
<property name="text">
<string>ENNOID-BMS Tool Changelog</string>
</property>
<property name="toolTip">
<string>ENNOID-BMS Tool Changelog</string>
</property>
</action>
<action name="actionFirmwareChangelog">
<property name="icon">
<iconset resource="res.qrc">
<normaloff>:/res/icons/About-96.png</normaloff>:/res/icons/About-96.png</iconset>
</property>
<property name="text">
<string>Firmware Changelog</string>
</property>
</action>
<action name="actionLicense">
<property name="icon">
<iconset resource="res.qrc">
<normaloff>:/res/icons/About-96.png</normaloff>:/res/icons/About-96.png</iconset>
</property>
<property name="text">
<string>License</string>
</property>
</action>
<action name="actionStoreBMScconf">
<property name="icon">
<iconset resource="res.qrc">
<normaloff>:/res/icons/Save-96.png</normaloff>:/res/icons/Save-96.png</iconset>
</property>
<property name="text">
<string>writeBMScconf</string>
</property>
<property name="toolTip">
<string>Store BMS configuration to Flash</string>
</property>
</action>
<action name="actionCalibrateZero">
<property name="icon">
<iconset resource="res.qrc">
<normaloff>:/res/icons/Potentiometer-96.png</normaloff>:/res/icons/Potentiometer-96.png</iconset>
</property>
<property name="text">
<string>Calibrate Zero</string>
</property>
<property name="toolTip">
<string>Calibrate zero current of BMS</string>
</property>
</action>
</widget>
<layoutdefault spacing="6" margin="11"/>
<resources>
<include location="res.qrc"/>
</resources>
<connections/>
</ui>

2272
mainwindownew.cpp Normal file

File diff suppressed because it is too large Load Diff

209
mainwindownew.h Normal file
View File

@@ -0,0 +1,209 @@
#ifndef MAINWINDOWNEW_H
#define MAINWINDOWNEW_H
#include <QMainWindow>
#include <QWidget>
#include <QPropertyAnimation>
#include <QParallelAnimationGroup>
#include <QLabel>
#include <QPushButton>
#include <QButtonGroup>
#include <QLoggingCategory>
#include <QMouseEvent>
#include <QMainWindow>
#include <QLabel>
#include <QTimer>
#include <QProcess>
#include <QSettings>
#include <QMessageBox>
#include <QDebug>
#include <QFileDialog>
#include <QListWidgetItem>
#include <cmath>
#include <QEventLoop>
#include <QDesktopServices>
#include <QLoggingCategory>
#include "bmsinterface.h"
#include "ui_mainwindow.h"
#include "parametereditor.h"
#include "utility.h"
#include "utility.h"
#include "customlefttabbarbutton.h"
#include "connectandenterpage.h"
#include "debugprintpage.h"
// RELEASE_VERSION = true => fixed window size and no window header pannel with |hide|expand|close buttons
#define RELEASE_VERSION true
/*
start 0 12 = Default that is not in bms it's in somekind of real offline file (we do NOT have connection to bms but there parameters show up somehow)
connect 8 8 Config updated with default params not from file, but from default params of bms
bms edit | bms edit
^ - 8 7 | 8 8 read bms conf
-> - 7 7 | 7 8 read default 8 is defaul - that was on the start
v - 8 7 | 7 7 write bms config
S - | save currect config from bms into outside file to the flash card
before | after
*/
namespace Ui {
class MainWindowNew;
}
class MainWindowNew : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindowNew(QWidget *parent = nullptr);
~MainWindowNew();
private:
Ui::MainWindowNew *ui;
//////////////////////////////////////
public:
enum TabWidgetPage
{
ConnectWindow = 0,
AkbMonitor,
CellsMonitor,
Configuration,
Visualization,
History,
SeriveOfBms,
SettingsExtra,
};
TabWidgetPage m_currentPageIndex = AkbMonitor;
TabWidgetPage m_prevPageIndex = AkbMonitor;
bool isConnectWindowActivated() const;
void initAnimationForLeftTabsBar();
void setAnimParams(QParallelAnimationGroup *group, QPropertyAnimation *a, int sec, int from, int to);
void setAnimParamsMin(QParallelAnimationGroup *group, QPropertyAnimation *a, int sec, CustomLeftTabBarButton *btn);
void setAnimParamsMax(QParallelAnimationGroup *group, QPropertyAnimation *a, int sec, CustomLeftTabBarButton *btn);
void setAnimParamsMin(QParallelAnimationGroup *group, QPropertyAnimation *a, int sec, QLabel *btn);
void setAnimParamsMax(QParallelAnimationGroup *group, QPropertyAnimation *a, int sec, QLabel *btn);
void setAnimLogoMin(QParallelAnimationGroup *group, QPropertyAnimation *a, int sec, QLabel *btn);
void setAnimLogoMax(QParallelAnimationGroup *group, QPropertyAnimation *a, int sec, QLabel *btn);
int m_logoWay;
void clearBtnText(CustomLeftTabBarButton *btn);
void clearAllBtnsText();
void restoreBtnText(CustomLeftTabBarButton *btn);
void restoreAllBtnsText();
bool m_minimize;
QParallelAnimationGroup *m_animgroupMin;
QParallelAnimationGroup *m_animgroupMax;
signals:
void sendStatusLabelStyleSheet(QString s);
void sigShowConnectWindow();
void loadParams();
private slots:
void onAnyLeftTabBarButtonClicked();
void timerSlot();
void showStatusInfo(QString info, bool isGood);
void showMessageDialog(const QString &title, const QString &msg, bool isGood, bool richText);
void serialPortNotWritable(const QString &port);
void bmsconfUpdated();
void bmsConfigCheckResult(QStringList paramsNotSet);
///void bmsconfUpdated();
///void showStatusInfo(QString info, bool isGood);
///void onTimerSlot();
void onShrink();
//void onMainMenuBarButtonClicked();
//void onHideThisAndShowConnectWindow();
//void onShowConnectWindow();
void onShowConnectWindow();
void onHideConnectWindow();
private:
const int mWidth = 1366;
const int mHeight = 738;
const int mWidthConnectPage = 637;
const int mHeightConnectPage = 433;
void moveApplicationToTheCenter();
void setAppSizeForFullView();
void setAppSizeForConnectionPage();
QSettings mSettings;
QString mVersion;
QString mStatusLabelText;
//QLabel *mStatusLabel;
bool mKeyLeft;
bool mKeyRight;
QTimer *mTimer;
int mStatusInfoTime;
bool mMcConfRead;
BMSInterface *mDieBieMS; // Business logic class
DebugPrintPage *mPageDebugPrint;
// UI
QButtonGroup* m_btnGroupMainMenuBar;
//ConnectAndEnterPage mConnect;
bool mIsConnectionPrepared;
QString mComPortToConnect;
void setAllMainMenuBarButtonsVisible(bool val);
void setLeftTopWidgetsVisible(bool value);
void unselectAllLeftTabBarButtons();
void selectCurrentLeftTabBarButton();
void onSelectCurrentLeftTabBarButton();
void reloadPages();
void initDieBieMs();
bool addParamRow(ConfigParams *params, QString paramName);
void checkUdev();
//
void showWindowInOneStaticPlaceWithoutHideResizeCloseButtons();
///
/// To make Window move on MOUSE CLICK MOVE
///
protected:
void mousePressEvent(QMouseEvent *evt)
{
oldPos = evt->globalPos();
}
void mouseMoveEvent(QMouseEvent *evt)
{
const QPoint delta = evt->globalPos() - oldPos;
move(x()+delta.x(), y()+delta.y());
oldPos = evt->globalPos();
}
private:
QPoint oldPos;
///
/// To make Window move on MOUSE CLICK MOVE
///
};
#endif // MAINWINDOWNEW_H

930
mainwindownew.ui Normal file
View File

@@ -0,0 +1,930 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindowNew</class>
<widget class="QMainWindow" name="MainWindowNew">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>1366</width>
<height>738</height>
</rect>
</property>
<property name="windowTitle">
<string>MainWindowNew</string>
</property>
<property name="styleSheet">
<string notr="true">QMainWindow#MainWindowNew
{
background-color: #f7f8fc
}
#tabAkbMonitor,
#tabCellsMonitor,
#tabConfiguration,
#tabVisualization,
#tabHistory,
#tabSeriveOfBms,
#tabSettingsExtra
{
background-color: #f7f8fc; /*ConfigurationPage*/
}
/*
QWidget#centralwidget
{
background-color: red;
}
*/
/*
QScrollArea#scrollArea_2
{
background-color: #f7f8fc
}
*/
QLabel#lbIconLogo
{
/*background-image: url(:/images/cubo-verde-logo.png);*/
border-image: url(:/res/cubo-verde-logo-white.png) 0 0 0 0 stretch stretch;
}
QLabel#lbHeaderLanguageIcon
{
/*background-image: url(:/images/cubo-verde-logo.png);*/
border-image: url(:/res/language-russ.png) 0 0 0 0 stretch stretch;
}
QLabel#mStatusLabel
{
font-size:11px;
}
QPushButton#pbMainMenuBarHideMenu,
#pbMainMenuBarAkbMonitor,
#pbMainMenuBarCellsMonitor,
#pbMainMenuBarConfiguration,
#pbMainMenuBarVisualization,
#pbMainMenuBarHistory,
#pbMainMenuBarSeriveOfBms,
#pbMainMenuBarSettingsExtra,
#pbMainMenuBarExit
{
color: #FFFFFF;
background-color: #009352;
border: 0px solid black;
background:#009352;
text-align: left;
padding-left: 85px;
font-size:17px;
}
QPushButton#pbMainMenuBarHideMenu
{
font-size:15px;
}
/*QPushButton:pressed {
background-color: qlineargradient(spread:pad, x1:0, y1:0, x2:0, y2:1, stop:0 rgba(60, 186, 162, 255), stop:1 rgba(98, 211, 162, 255))
}
*/
QPushButton#pbMainMenuBarAkbMonitor:hover:checked,
#pbMainMenuBarCellsMonitor:hover:checked,
#pbMainMenuBarConfiguration:hover:checked,
#pbMainMenuBarVisualization:hover:checked,
#pbMainMenuBarHistory:hover:checked,
#pbMainMenuBarSeriveOfBms:hover:checked,
#pbMainMenuBarSettingsExtra:hover:checked
{
background-color: #01673a;
}
QPushButton#pbMainMenuBarAkbMonitor:!hover:checked,
#pbMainMenuBarAkbMonitor:!hover:checked,
#pbMainMenuBarCellsMonitor:!hover:checked,
#pbMainMenuBarConfiguration:!hover:checked,
#pbMainMenuBarVisualization:!hover:checked,
#pbMainMenuBarHistory:!hover:checked,
#pbMainMenuBarSeriveOfBms:!hover:checked,
#pbMainMenuBarSettingsExtra:!hover:checked
{
background-color: #01673a;
}
QPushButton#pbMainMenuBarHideMenu:hover:!checked,
#pbMainMenuBarAkbMonitor:hover:!checked,
#pbMainMenuBarCellsMonitor:hover:!checked,
#pbMainMenuBarConfiguration:hover:!checked,
#pbMainMenuBarVisualization:hover:!checked,
#pbMainMenuBarHistory:hover:!checked,
#pbMainMenuBarSeriveOfBms:hover:!checked,
#pbMainMenuBarSettingsExtra:hover:!checked,
#pbMainMenuBarExit:hover:!checked
{
/*background-color: #009352;*/
background-color: #028249;
}
/*QPushButton#pbMainMenuBarExit:hover:!pressed
{
background-color: #01673a;
}*/
QPushButton#pbMainMenuBarHideMenu:hover:pressed,
QPushButton#pbMainMenuBarExit:hover:pressed
{
background-color: #01673a;
}
/*------------------*/
QPushButton#pbConnect,
#pbWriteToNonVolatileBmsMemory,
#pbLoadCurrentSettingsFromBms,
#pbLoadSettingsFromFile,
QPushButton#pbConnect:hover:pressed,
#pbWriteToNonVolatileBmsMemory:hover:pressed,
#pbLoadCurrentSettingsFromBms:hover:pressed,
#pbLoadSettingsFromFile:hover:pressed
{
color:black;
background-color:white;
font-weight:bold;
font-size: 14px;
border-color: #009352;
border-style: solid;
border-width: 1px;
border-radius: 6px;
}
QPushButton#pbConnect:hover:!pressed,
#pbWriteToNonVolatileBmsMemory:hover:!pressed,
#pbLoadCurrentSettingsFromBms:hover:!pressed,
#pbLoadSettingsFromFile:hover:!pressed
{
background-color: #0FF73a;
}
QPushButton#pbWriteCurrentValuesToBms,
QPushButton#pbWriteCurrentValuesToBms:hover:pressed
{
background-color:#009352;
color:white;
font-weight:bold;
font-size: 14px;
border-color: #009352;
border-style: solid;
border-width: 1px;
border-radius: 6px;
}
QPushButton#pbWriteCurrentValuesToBms:hover:!pressed
{
background-color: #01673a;
}
QPushButton#pbLinkSerialNumber,
#pbLinkSoc,
#pbLinkRestrictions,
#pbLinkClaibrateZero,
#pbLinkCellsConfiguration,
#pbLinkOutputsSetting,
#pbLinkBalancingConfiguration,
#pbLinkConfiguration
{
color:#009352;
text-decoration: none;
background: transparent;
border-color:transparent;
text-align:left;
border-radius: 1px;
}
QPushButton#pbLinkSerialNumber:hover:!pressed,
#pbLinkSoc:hover:!pressed,
#pbLinkRestrictions:hover:!pressed,
#pbLinkClaibrateZero:hover:!pressed,
#pbLinkCellsConfiguration:hover:!pressed,
#pbLinkOutputsSetting:hover:!pressed,
#pbLinkBalancingConfiguration:hover:!pressed,
#pbLinkConfiguration:hover:!pressed
{
text-decoration: underline;
}
QPushButton#pbLinkSerialNumber:hover:pressed,
#pbLinkSoc:hover:pressed,
#pbLinkRestrictions:hover:pressed,
#pbLinkClaibrateZero:hover:pressed,
#pbLinkCellsConfiguration:hover:pressed,
#pbLinkOutputsSetting:hover:pressed,
#pbLinkBalancingConfiguration:hover:pressed,
#pbLinkConfiguration:hover:pressed
{
color:#0093FF;
}
/*------------------*/
/*
QGroupBox#gbSerialNumber,
#gbConfiguration,
#gbSoc
{
border-radius:10px;
border-color: grey;
border-style: solid;
border-width: 1px;
}
*/
/*-----------------------*/
QScrollBar {
border-radius: 10px;
background: #e8e9f1;
}
QScrollBar::vertical {
width: 10px;
margin: 0px 0px 0px 0px;
}
/*-----------------------------------*/
QScrollBar::sub-page:horizontal {
background: #e8e9f1;
border-top-left-radius: 9px;
border-bottom-left-radius: 9px;
}
QScrollBar::add-page:horizontal {
background: #e8e9f1;
border-top-right-radius: 9px;
border-bottom-right-radius: 9px;
}
QScrollBar::sub-page:vertical {
background: #e8e9f1;
border-top-left-radius: 9px;
border-top-right-radius: 9px;
}
QScrollBar::add-page:vertical {
background: #e8e9f1;
border-bottom-left-radius: 9px;
border-bottom-right-radius: 9px;
}
/*-----------------------------------*/
QScrollBar::up-arrow,
QScrollBar::sub-line,
QScrollBar::add-line {
width: 0px;
height: 0px;
}
QScrollBar::handle {
background: #009352;
border-radius: 5px;
}
/*---------------------*/
/* border: 1px solid black;*/
QTabWidget
{
background: red;
}
QTabBar::tab
{
height: 0px;
background: #f7f8fc;
}
QTabWidget::pane
{
background: #f7f8fc;
}
/*
QTabWidget
{
background: #f7f8fc;
}
QTabWidget::tab-bar
{
background: #f7f8fc;
}
QTabBar::tab
{
height: 0px;
background: #f7f8fc;
}
QTabBar::tab
{
background: #48555E;
color: white;
border-color: #48555E;
}
*/
</string>
</property>
<widget class="QWidget" name="centralwidget">
<widget class="CustomLeftTabBarButton" name="pbMainMenuBarAkbMonitor">
<property name="geometry">
<rect>
<x>0</x>
<y>270</y>
<width>281</width>
<height>51</height>
</rect>
</property>
<property name="text">
<string>Монитор АКБ</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<property name="checked">
<bool>false</bool>
</property>
</widget>
<widget class="CustomLeftTabBarButton" name="pbMainMenuBarExit">
<property name="geometry">
<rect>
<x>0</x>
<y>620</y>
<width>281</width>
<height>51</height>
</rect>
</property>
<property name="text">
<string>Выход</string>
</property>
</widget>
<widget class="CustomLeftTabBarButton" name="pbMainMenuBarHideMenu">
<property name="geometry">
<rect>
<x>0</x>
<y>170</y>
<width>281</width>
<height>51</height>
</rect>
</property>
<property name="text">
<string>СКРЫТЬ МЕНЮ</string>
</property>
</widget>
<widget class="CustomLeftTabBarButton" name="pbMainMenuBarVisualization">
<property name="geometry">
<rect>
<x>0</x>
<y>420</y>
<width>281</width>
<height>51</height>
</rect>
</property>
<property name="text">
<string>Визуализация</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
</widget>
<widget class="CustomLeftTabBarButton" name="pbMainMenuBarSeriveOfBms">
<property name="geometry">
<rect>
<x>0</x>
<y>520</y>
<width>281</width>
<height>51</height>
</rect>
</property>
<property name="text">
<string>Сервис BMS</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
</widget>
<widget class="QLabel" name="labLeftTabListBackground">
<property name="geometry">
<rect>
<x>0</x>
<y>-10</y>
<width>281</width>
<height>753</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>14</pointsize>
<weight>50</weight>
<bold>false</bold>
</font>
</property>
<property name="styleSheet">
<string notr="true">color: rgb(255, 255, 255);
background-color:#009352;
</string>
</property>
<property name="text">
<string/>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
<widget class="CustomLeftTabBarButton" name="pbMainMenuBarHistory">
<property name="geometry">
<rect>
<x>0</x>
<y>470</y>
<width>281</width>
<height>51</height>
</rect>
</property>
<property name="text">
<string>История</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
</widget>
<widget class="QLabel" name="lbIconLogo">
<property name="geometry">
<rect>
<x>80</x>
<y>40</y>
<width>111</width>
<height>111</height>
</rect>
</property>
<property name="contextMenuPolicy">
<enum>Qt::NoContextMenu</enum>
</property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="text">
<string/>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
<widget class="CustomLeftTabBarButton" name="pbMainMenuBarCellsMonitor">
<property name="geometry">
<rect>
<x>0</x>
<y>320</y>
<width>281</width>
<height>51</height>
</rect>
</property>
<property name="text">
<string>Монитор ячеек</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
</widget>
<widget class="CustomLeftTabBarButton" name="pbMainMenuBarConfiguration">
<property name="geometry">
<rect>
<x>0</x>
<y>370</y>
<width>281</width>
<height>51</height>
</rect>
</property>
<property name="text">
<string>Конфигурация</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<property name="checked">
<bool>false</bool>
</property>
</widget>
<widget class="QTabWidget" name="tabWidgetPagesContent">
<property name="geometry">
<rect>
<x>280</x>
<y>-10</y>
<width>1084</width>
<height>751</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true"/>
</property>
<property name="currentIndex">
<number>0</number>
</property>
<widget class="ConnectAndEnterPage" name="tabConnect">
<property name="styleSheet">
<string notr="true"/>
</property>
<attribute name="title">
<string>Страница</string>
</attribute>
</widget>
<widget class="AkbMonitorPage" name="tabAkbMonitor">
<property name="styleSheet">
<string notr="true"/>
</property>
<attribute name="title">
<string>Tab 1</string>
</attribute>
</widget>
<widget class="CellsMonitorPage" name="tabCellsMonitor">
<attribute name="title">
<string>Tab 2</string>
</attribute>
</widget>
<widget class="ConfigurationPage" name="tabConfiguration">
<attribute name="title">
<string>Страница</string>
</attribute>
</widget>
<widget class="VisualizationPage" name="tabVisualization">
<attribute name="title">
<string>Страница</string>
</attribute>
</widget>
<widget class="DebugPrintPage" name="tabHistory">
<attribute name="title">
<string>Страница</string>
</attribute>
</widget>
<widget class="BmsServicePage" name="tabSeriveOfBms">
<attribute name="title">
<string>Страница</string>
</attribute>
</widget>
<widget class="SettingsExtraPage" name="tabSettingsExtra">
<attribute name="title">
<string>Страница</string>
</attribute>
</widget>
</widget>
<widget class="QLabel" name="lbHeaderSerialNumberValue">
<property name="geometry">
<rect>
<x>1130</x>
<y>50</y>
<width>121</width>
<height>21</height>
</rect>
</property>
<property name="font">
<font>
<family>Segoe WP SemiLight</family>
<pointsize>12</pointsize>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="styleSheet">
<string notr="true">color:black;
font-weight:bold;</string>
</property>
<property name="text">
<string>0</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
</property>
</widget>
<widget class="QLabel" name="lbHeaderConnectionName">
<property name="geometry">
<rect>
<x>990</x>
<y>20</y>
<width>131</width>
<height>21</height>
</rect>
</property>
<property name="font">
<font>
<family>Segoe WP SemiLight</family>
<pointsize>12</pointsize>
<weight>50</weight>
<bold>false</bold>
</font>
</property>
<property name="styleSheet">
<string notr="true">color:rgba(54, 70, 84, 0.6);</string>
</property>
<property name="text">
<string>Подключено</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
<widget class="QLabel" name="lbHeaderSerialNumberName">
<property name="geometry">
<rect>
<x>990</x>
<y>50</y>
<width>131</width>
<height>21</height>
</rect>
</property>
<property name="font">
<font>
<family>Segoe WP SemiLight</family>
<pointsize>9</pointsize>
<weight>50</weight>
<bold>false</bold>
</font>
</property>
<property name="styleSheet">
<string notr="true">color:rgba(54, 70, 84, 0.6);</string>
</property>
<property name="text">
<string>Серийный номер:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
<widget class="QLabel" name="labHeaderSeparator">
<property name="geometry">
<rect>
<x>1250</x>
<y>10</y>
<width>16</width>
<height>61</height>
</rect>
</property>
<property name="font">
<font>
<family>Segoe WP SemiLight</family>
<pointsize>38</pointsize>
<weight>50</weight>
<bold>false</bold>
</font>
</property>
<property name="styleSheet">
<string notr="true">color: rgba(54, 70, 84, 0.4);</string>
</property>
<property name="text">
<string>|</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
</property>
</widget>
<widget class="QLabel" name="lbHeaderLanguageIcon">
<property name="geometry">
<rect>
<x>1320</x>
<y>35</y>
<width>31</width>
<height>21</height>
</rect>
</property>
<property name="font">
<font>
<family>Segoe WP SemiLight</family>
<pointsize>12</pointsize>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="styleSheet">
<string notr="true">color:#0A72BA;
font-weight:bold;</string>
</property>
<property name="text">
<string/>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
</property>
</widget>
<widget class="QLabel" name="lbHeaderLanguageName">
<property name="geometry">
<rect>
<x>1270</x>
<y>20</y>
<width>41</width>
<height>51</height>
</rect>
</property>
<property name="font">
<font>
<family>Segoe WP SemiLight</family>
<pointsize>12</pointsize>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="styleSheet">
<string notr="true">color:#0A72BA;
font-weight:bold;</string>
</property>
<property name="text">
<string>РУС</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
</property>
</widget>
<widget class="QLabel" name="lbHeaderBmsVersion">
<property name="geometry">
<rect>
<x>1160</x>
<y>20</y>
<width>91</width>
<height>21</height>
</rect>
</property>
<property name="font">
<font>
<family>Segoe WP SemiLight</family>
<pointsize>12</pointsize>
<weight>50</weight>
<bold>false</bold>
</font>
</property>
<property name="styleSheet">
<string notr="true">color:rgba(54, 70, 84, 0.6);</string>
</property>
<property name="text">
<string>BMS v.2.1</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
</property>
</widget>
<widget class="QLabel" name="lbHeaderConnectionIndicator">
<property name="geometry">
<rect>
<x>1130</x>
<y>20</y>
<width>21</width>
<height>21</height>
</rect>
</property>
<property name="font">
<font>
<family>Segoe WP SemiLight</family>
<pointsize>12</pointsize>
<weight>50</weight>
<bold>false</bold>
</font>
</property>
<property name="styleSheet">
<string notr="true"> color: #009352;
background-color: #009352;
border-color:#009352;
border-width: 0px;
border-style: solid;
/* border-radius MUST BE width/2 and height/2 */
border-radius: 10px;
</string>
</property>
<property name="text">
<string>O</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
<widget class="QLabel" name="mStatusLabel">
<property name="geometry">
<rect>
<x>6</x>
<y>672</y>
<width>271</width>
<height>61</height>
</rect>
</property>
<property name="text">
<string>TEXT IN STATUS LABEL</string>
</property>
<property name="textFormat">
<enum>Qt::AutoText</enum>
</property>
<property name="scaledContents">
<bool>true</bool>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
<widget class="CustomLeftTabBarButton" name="pbMainMenuBarSettingsExtra">
<property name="geometry">
<rect>
<x>0</x>
<y>570</y>
<width>281</width>
<height>51</height>
</rect>
</property>
<property name="text">
<string>Прочие настройки</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
</widget>
<zorder>labLeftTabListBackground</zorder>
<zorder>pbMainMenuBarCellsMonitor</zorder>
<zorder>pbMainMenuBarExit</zorder>
<zorder>pbMainMenuBarSeriveOfBms</zorder>
<zorder>lbIconLogo</zorder>
<zorder>pbMainMenuBarVisualization</zorder>
<zorder>pbMainMenuBarHideMenu</zorder>
<zorder>pbMainMenuBarConfiguration</zorder>
<zorder>pbMainMenuBarHistory</zorder>
<zorder>pbMainMenuBarAkbMonitor</zorder>
<zorder>mStatusLabel</zorder>
<zorder>pbMainMenuBarSettingsExtra</zorder>
<zorder>tabWidgetPagesContent</zorder>
<zorder>lbHeaderLanguageIcon</zorder>
<zorder>labHeaderSeparator</zorder>
<zorder>lbHeaderConnectionIndicator</zorder>
<zorder>lbHeaderSerialNumberValue</zorder>
<zorder>lbHeaderSerialNumberName</zorder>
<zorder>lbHeaderLanguageName</zorder>
<zorder>lbHeaderBmsVersion</zorder>
<zorder>lbHeaderConnectionName</zorder>
</widget>
</widget>
<customwidgets>
<customwidget>
<class>CustomLeftTabBarButton</class>
<extends>QPushButton</extends>
<header location="global">customlefttabbarbutton.h</header>
</customwidget>
<customwidget>
<class>ConfigurationPage</class>
<extends>QWidget</extends>
<header location="global">configurationpage.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>CellsMonitorPage</class>
<extends>QWidget</extends>
<header location="global">cellsmonitorpage.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>AkbMonitorPage</class>
<extends>QWidget</extends>
<header location="global">akbmonitorpage.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>VisualizationPage</class>
<extends>QWidget</extends>
<header location="global">visualizationpage.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>BmsServicePage</class>
<extends>QWidget</extends>
<header location="global">bmsservicepage.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>ConnectAndEnterPage</class>
<extends>QWidget</extends>
<header location="global">connectandenterpage.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>DebugPrintPage</class>
<extends>QWidget</extends>
<header location="global">debugprintpage.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>SettingsExtraPage</class>
<extends>QWidget</extends>
<header location="global">settingsextrapage.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>

219
mobile/AdcMap.qml Normal file
View File

@@ -0,0 +1,219 @@
/*
Copyright 2018 Benjamin Vedder benjamin@vedder.se
This file is part of VESC Tool.
VESC Tool is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
VESC Tool is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import QtQuick 2.7
import QtQuick.Controls 2.2
import QtQuick.Layouts 1.3
import Ennoid.bmsinterface 1.0
import Ennoid.commands 1.0
import Ennoid.configparams 1.0
Item {
property int parentWidth: 10
property real vMin: 0.0
property real vMax: 0.0
property real vCenter: 0.0
property real vNow: 0.0
property real valueNow: 0.5
property real vMin2: 0.0
property real vMax2: 0.0
property real vNow2: 0.0
property real valueNow2: 0.5
property bool resetDone: true
property Commands mCommands: VescIf.commands()
property ConfigParams mAppConf: VescIf.appConfig()
property ConfigParams mInfoConf: VescIf.infoConfig()
function openDialog() {
dialog.open()
}
function updateDisplay() {
resultArea.text =
"Value : " + parseFloat(valueNow).toFixed(2) + "\n" +
"Value2 : " + parseFloat(valueNow2).toFixed(2) + "\n\n" +
"Now : " + parseFloat(vNow).toFixed(2) + " V\n" +
"Min : " + parseFloat(vMin).toFixed(2) + " V\n" +
"Max : " + parseFloat(vMax).toFixed(2) + " V\n" +
"Center : " + parseFloat(vCenter).toFixed(2) + " V\n\n" +
"Now2 : " + parseFloat(vNow2).toFixed(2) + " V\n" +
"Min2 : " + parseFloat(vMin2).toFixed(2) + " V\n" +
"Max2 : " + parseFloat(vMax2).toFixed(2) + " V"
valueBar.value = valueNow
valueBar2.value = valueNow2
}
Component.onCompleted: {
updateDisplay()
}
Dialog {
id: dialog
standardButtons: Dialog.Close
modal: true
focus: true
width: parentWidth - 20
height: Math.min(implicitHeight, column.height - 40)
closePolicy: Popup.CloseOnEscape
x: 10
y: 10
ScrollView {
anchors.fill: parent
clip: true
contentWidth: parent.width
ColumnLayout {
anchors.fill: parent
spacing: 0
TextArea {
id: resultArea
Layout.fillWidth: true
Layout.preferredHeight: 300
readOnly: true
wrapMode: TextEdit.WordWrap
font.family: "DejaVu Sans Mono"
}
ProgressBar {
id: valueBar
Layout.fillWidth: true
Layout.bottomMargin: 5
from: 0.0
to: 1.0
value: 0.0
}
ProgressBar {
id: valueBar2
Layout.fillWidth: true
from: 0.0
to: 1.0
value: 0.0
}
RowLayout {
Layout.fillWidth: true
Button {
text: "Help"
Layout.preferredWidth: 50
Layout.fillWidth: true
onClicked: {
VescIf.emitMessageDialog(
mInfoConf.getLongName("app_adc_mapping_help"),
mInfoConf.getDescription("app_adc_mapping_help"),
true, true)
}
}
Button {
text: "Reset"
Layout.preferredWidth: 50
Layout.fillWidth: true
onClicked: {
vMin = 0.0
vMax = 0.0
vCenter = 0.0
vMin2 = 0.0
vMax2 = 0.0
resetDone = true
updateDisplay()
}
}
}
Button {
text: "Apply & Write"
Layout.fillWidth: true
onClicked: {
mAppConf.updateParamDouble("app_adc_conf.voltage_start", vMin)
mAppConf.updateParamDouble("app_adc_conf.voltage_end", vMax)
mAppConf.updateParamDouble("app_adc_conf.voltage_center", vCenter)
mAppConf.updateParamDouble("app_adc_conf.voltage2_start", vMin2)
mAppConf.updateParamDouble("app_adc_conf.voltage2_end", vMax2)
VescIf.emitStatusMessage("Start, End and Center ADC Voltages Applied", true)
mCommands.setAppConf()
}
}
}
}
}
Timer {
id: rtTimer
interval: 50
running: true
repeat: true
onTriggered: {
if (VescIf.isPortConnected() && dialog.visible) {
mCommands.getDecodedAdc()
}
}
}
Connections {
target: mCommands
onDecodedAdcReceived: {
valueNow = value
vNow = voltage
valueNow2 = value2
vNow2 = voltage2
if (resetDone) {
resetDone = false
vMin = vNow
vMax = vNow
vMin2 = vNow2
vMax2 = vNow2
}
if (vNow < vMin) {
vMin = vNow
}
if (vNow > vMax) {
vMax = vNow
}
var range = vMax - vMin
var pos = vNow - vMin
if (pos > (range / 4.0) && pos < ((3.0 * range) / 4.0)) {
vCenter = vNow
} else {
vCenter = range / 2.0 + vMin
}
if (vNow2 < vMin2) {
vMin2 = vNow2
}
if (vNow2 > vMax2) {
vMax2 = vNow2
}
updateDisplay()
}
}
}

286
mobile/ConfigPageCell.qml Normal file
View File

@@ -0,0 +1,286 @@
/*
Copyright 2020 Kevin Dionne kevin.dionne@ennoid.me
This file is part of ENNOID-BMS Tool.
ENNOID-BMS Tool is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
ENNOID-BMS Tool is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import QtQuick 2.7
import QtQuick.Controls 2.2
import QtQuick.Layouts 1.3
import Ennoid.bmsinterface 1.0
import Ennoid.commands 1.0
import Ennoid.configparams 1.0
Item {
property Commands mCommands: VescIf.commands()
property var editorsVisible: []
property bool isHorizontal: width > height
ParamEditors {
id: editors
}
onIsHorizontalChanged: {
//updateEditors()
}
function addSeparator(text) {
editorsVisible.push(editors.createSeparator(scrollCol, text))
editorsVisible[editorsVisible.length - 1].Layout.columnSpan = isHorizontal ? 2 : 1
}
function destroyEditors() {
for (var i = 0;i < editorsVisible.length;i++) {
editorsVisible[i].destroy();
}
editorsVisible = []
}
function createEditorMc(param) {
editorsVisible.push(editors.createEditorMc(scrollCol, param))
editorsVisible[editorsVisible.length - 1].Layout.preferredWidth = 500
editorsVisible[editorsVisible.length - 1].Layout.fillsWidth = true
}
function updateEditors() {
destroyEditors()
switch (pageBox.currentText) {
case "Specifications":
switch(tabBox.currentText) {
case "Pack configuration":
createEditorMc("cellMonitorICCount")
createEditorMc("cellMonitorICType")
createEditorMc("noOfParallelModules")
createEditorMc("noOfCellsSeries")
break;
case "SOC - Pack capacity":
createEditorMc("noOfCellsParallel")
createEditorMc("batteryCapacity")
break;
case "Cell specifications":
createEditorMc("cellTechnology")
createEditorMc("cellHardOverVoltage")
createEditorMc("cellHardUnderVoltage")
createEditorMc("cellLCSoftUnderVoltage")
createEditorMc("cellSoftOverVoltage")
createEditorMc("maxUnderAndOverVoltageErrorCount")
createEditorMc("hysteresisDischarge")
createEditorMc("hysteresisCharge")
break;
default:
break;
}
break;
case "Balancing":
switch(tabBox.currentText) {
case "Balancing configuration":
createEditorMc("cellBalanceStart")
createEditorMc("cellBalanceDifferenceThreshold")
createEditorMc("cellBalanceUpdateInterval")
createEditorMc("cellBalanceAllTime")
break;
default:
break;
}
break;
case "Throttling":
switch(tabBox.currentText) {
case "Discharge":
createEditorMc("cellThrottleLowerStart")
createEditorMc("cellThrottleLowerMargin")
createEditorMc("throttleDisChargeIncreaseRate")
break;
case "Charge":
createEditorMc("cellThrottleUpperStart")
createEditorMc("cellThrottleUpperMargin")
createEditorMc("throttleChargeIncreaseRate")
break;
default:
break;
}
break;
case "SoC":
switch(tabBox.currentText) {
case "SoC general":
createEditorMc("stateOfChargeMethod")
createEditorMc("stateOfChargeStoreInterval")
createEditorMc("timeoutChargeCompleted")
createEditorMc("timeoutChargingCompletedMinimalMismatch")
createEditorMc("maxMismatchThreshold")
break;
default:
break;
}
break;
default:
break;
}
}
ColumnLayout {
id: column
anchors.fill: parent
spacing: 0
GridLayout {
Layout.fillWidth: true
columns: isHorizontal ? 2 : 1
rowSpacing: -5
ComboBox {
id: pageBox
Layout.fillWidth: true
model: [
"Specifications",
"Balancing",
"Throttling",
"SoC"
]
onCurrentTextChanged: {
var tabTextOld = tabBox.currentText
switch(currentText) {
case "Specifications":
tabBox.model = [
"Pack configuration",
"SOC - Pack capacity",
"Cell specifications"
]
break;
case "Balancing":
tabBox.model = [
"Balancing configuration"
]
break;
case "Throttling":
tabBox.model = [
"Discharge",
"Charge"
]
break;
case "SoC":
tabBox.model = [
"SoC general"
]
break;
default:
break;
}
tabBox.visible = tabBox.currentText.length !== 0
if (tabTextOld === tabBox.currentText) {
updateEditors()
}
}
}
ComboBox {
id: tabBox
Layout.fillWidth: true
onCurrentTextChanged: {
updateEditors()
}
}
}
ScrollView {
id: scroll
Layout.fillWidth: true
Layout.fillHeight: true
contentWidth: column.width
clip: true
GridLayout {
id: scrollCol
anchors.fill: parent
columns: isHorizontal ? 2 : 1
}
}
RowLayout {
Layout.fillWidth: true
Button {
Layout.preferredWidth: 100
Layout.fillWidth: true
text: "Write"
onClicked: {
mCommands.setBMSconf(true)
}
}
Button {
Layout.preferredWidth: 100
Layout.fillWidth: true
text: "Read"
onClicked: {
mCommands.getBMSconf()
}
}
Button {
Layout.preferredWidth: 50
Layout.fillWidth: true
text: "..."
onClicked: menu.open()
Menu {
id: menu
width: 500
MenuItem {
text: "Read Default Settings"
onTriggered: {
mCommands.getBMSconfDefault()
}
}
}
}
}
}
Connections {
target: mCommands
// TODO: For some reason this does not work
onBmsConfigCheckResult: {
if (paramsNotSet.length > 0) {
var notUpdated = "The following parameters were truncated because " +
"they were beyond the hardware limits:\n"
for (var i = 0;i < paramsNotSet.length;i++) {
notUpdated += mbmsConf.getLongName(paramsNotSet[i]) + "\n"
}
VescIf.emitMessageDialog("Parameters truncated", notUpdated, false, false)
}
}
}
}

View File

@@ -0,0 +1,203 @@
/*
Copyright 2020 Kevin Dionne kevin.dionne@ennoid.me
This file is part of ENNOID-BMS Tool.
ENNOID-BMS Tool is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
ENNOID-BMS Tool is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import QtQuick 2.7
import QtQuick.Controls 2.2
import QtQuick.Layouts 1.3
import Ennoid.bmsinterface 1.0
import Ennoid.commands 1.0
import Ennoid.configparams 1.0
Item {
property Commands mCommands: VescIf.commands()
property var editorsVisible: []
property bool isHorizontal: width > height
ParamEditors {
id: editors
}
onIsHorizontalChanged: {
//updateEditors()
}
function addSeparator(text) {
editorsVisible.push(editors.createSeparator(scrollCol, text))
editorsVisible[editorsVisible.length - 1].Layout.columnSpan = isHorizontal ? 2 : 1
}
function destroyEditors() {
for (var i = 0;i < editorsVisible.length;i++) {
editorsVisible[i].destroy();
}
editorsVisible = []
}
function createEditorMc(param) {
editorsVisible.push(editors.createEditorMc(scrollCol, param))
editorsVisible[editorsVisible.length - 1].Layout.preferredWidth = 500
editorsVisible[editorsVisible.length - 1].Layout.fillsWidth = true
}
function updateEditors() {
destroyEditors()
switch (pageBox.currentText) {
case "Duration":
createEditorMc("displayTimeoutBatteryDead")
createEditorMc("displayTimeoutBatteryError")
createEditorMc("displayTimeoutBatteryErrorPreCharge")
createEditorMc("displayTimeoutSplashScreen")
break;
case "Custom":
createEditorMc("displayStyle")
break;
default:
break;
}
}
ColumnLayout {
id: column
anchors.fill: parent
spacing: 0
GridLayout {
Layout.fillWidth: true
columns: isHorizontal ? 2 : 1
rowSpacing: -5
ComboBox {
id: pageBox
Layout.fillWidth: true
model: [
"Duration",
"Custom"
]
onCurrentTextChanged: {
var tabTextOld = tabBox.currentText
switch(currentText) {
case "Duration":
tabBox.model = [
""
]
break;
case "Custom":
tabBox.model = [
""
]
break;
default:
break;
}
tabBox.visible = tabBox.currentText.length !== 0
if (tabTextOld === tabBox.currentText) {
updateEditors()
}
}
}
ComboBox {
id: tabBox
Layout.fillWidth: true
onCurrentTextChanged: {
updateEditors()
}
}
}
ScrollView {
id: scroll
Layout.fillWidth: true
Layout.fillHeight: true
contentWidth: column.width
clip: true
GridLayout {
id: scrollCol
anchors.fill: parent
columns: isHorizontal ? 2 : 1
}
}
RowLayout {
Layout.fillWidth: true
Button {
Layout.preferredWidth: 100
Layout.fillWidth: true
text: "Write"
onClicked: {
mCommands.setBMSconf(true)
}
}
Button {
Layout.preferredWidth: 100
Layout.fillWidth: true
text: "Read"
onClicked: {
mCommands.getBMSconf()
}
}
Button {
Layout.preferredWidth: 50
Layout.fillWidth: true
text: "..."
onClicked: menu.open()
Menu {
id: menu
width: 500
MenuItem {
text: "Read Default Settings"
onTriggered: {
mCommands.getBMSconfDefault()
}
}
}
}
}
}
Connections {
target: mCommands
// TODO: For some reason this does not work
onBmsConfigCheckResult: {
if (paramsNotSet.length > 0) {
var notUpdated = "The following parameters were truncated because " +
"they were beyond the hardware limits:\n"
for (var i = 0;i < paramsNotSet.length;i++) {
notUpdated += mbmsConf.getLongName(paramsNotSet[i]) + "\n"
}
VescIf.emitMessageDialog("Parameters truncated", notUpdated, false, false)
}
}
}
}

View File

@@ -0,0 +1,308 @@
/*
Copyright 2020 Kevin Dionne kevin.dionne@ennoid.me
This file is part of ENNOID-BMS Tool.
ENNOID-BMS Tool is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
ENNOID-BMS Tool is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import QtQuick 2.7
import QtQuick.Controls 2.2
import QtQuick.Layouts 1.3
import Ennoid.bmsinterface 1.0
import Ennoid.commands 1.0
import Ennoid.configparams 1.0
Item {
property Commands mCommands: VescIf.commands()
property var editorsVisible: []
property bool isHorizontal: width > height
ParamEditors {
id: editors
}
onIsHorizontalChanged: {
//updateEditors()
}
function addSeparator(text) {
editorsVisible.push(editors.createSeparator(scrollCol, text))
editorsVisible[editorsVisible.length - 1].Layout.columnSpan = isHorizontal ? 2 : 1
}
function destroyEditors() {
for (var i = 0;i < editorsVisible.length;i++) {
editorsVisible[i].destroy();
}
editorsVisible = []
}
function createEditorMc(param) {
editorsVisible.push(editors.createEditorMc(scrollCol, param))
editorsVisible[editorsVisible.length - 1].Layout.preferredWidth = 500
editorsVisible[editorsVisible.length - 1].Layout.fillsWidth = true
}
function updateEditors() {
destroyEditors()
switch (pageBox.currentText) {
case "Power State":
switch(tabBox.currentText) {
case "Onstate":
createEditorMc("pulseToggleButton")
createEditorMc("notUsedCurrentThreshold")
createEditorMc("notUsedTimeout")
createEditorMc("powerDownDelay")
createEditorMc("allowForceOn")
break;
case "Jump to":
createEditorMc("extEnableState")
createEditorMc("chargeEnableState")
break;
default:
break;
}
break;
case "Limits":
switch(tabBox.currentText) {
case "Current":
createEditorMc("maxAllowedCurrent")
break;
case "Temperature discharging":
createEditorMc("allowedTempBattDischargingMax")
createEditorMc("allowedTempBattDischargingMin")
break;
case "Temperature charging":
createEditorMc("allowedTempBattChargingMax")
createEditorMc("allowedTempBattChargingMin")
break;
case "Temperature cooling/heating":
createEditorMc("allowedTempBattCoolingMax")
createEditorMc("allowedTempBattCoolingMin")
break;
case "Temperature Master board":
createEditorMc("allowedTempBMSMax")
createEditorMc("allowedTempBMSMin")
break;
default:
break;
}
break;
case "CAN":
switch(tabBox.currentText) {
case "CAN configuration":
createEditorMc("CANID")
createEditorMc("CANIDStyle")
createEditorMc("CANBaudRate")
break;
case "CAN messaging":
createEditorMc("emitStatusOverCAN")
createEditorMc("emitStatusProtocolType")
createEditorMc("useCANSafetyInput")
createEditorMc("useCANDelayedPowerDown")
break;
default:
break;
}
break;
case "Sensors":
switch(tabBox.currentText) {
case "NTC specifications battery":
createEditorMc("tempEnableMaskBattery")
createEditorMc("noOfTempSensorPerModule")
createEditorMc("NTCLTC25Deg")
createEditorMc("NTCLTCBeta")
break;
case "NTC specifications expansion Board":
createEditorMc("tempEnableMaskExpansion")
createEditorMc("noOfExpansionBoard")
createEditorMc("noOfTempSensorPerExpansionBoard")
createEditorMc("NTCEXP25Deg")
createEditorMc("NTCEXPBeta")
break;
case "NTC advanced settings":
createEditorMc("maxUnderAndOverTemperatureErrorCount")
createEditorMc("humidityICType")
break;
default:
break;
}
break;
default:
break;
}
}
ColumnLayout {
id: column
anchors.fill: parent
spacing: 0
GridLayout {
Layout.fillWidth: true
columns: isHorizontal ? 2 : 1
rowSpacing: -5
ComboBox {
id: pageBox
Layout.fillWidth: true
model: [
"Power State",
"Limits",
"CAN",
"Sensors"
]
onCurrentTextChanged: {
var tabTextOld = tabBox.currentText
switch(currentText) {
case "Power State":
tabBox.model = [
"Onstate",
"Jump to"
]
break;
case "Limits":
tabBox.model = [
"Current",
"Temperature discharging",
"Temperature charging",
"Temperature cooling/heating",
"Temperature Master board"
]
break;
case "CAN":
tabBox.model = [
"CAN configuration",
"CAN messaging"
]
break;
case "Sensors":
tabBox.model = [
"NTC specifications battery",
"NTC enable",
"NTC specifications expansion Board"
]
break;
default:
break;
}
tabBox.visible = tabBox.currentText.length !== 0
if (tabTextOld === tabBox.currentText) {
updateEditors()
}
}
}
ComboBox {
id: tabBox
Layout.fillWidth: true
onCurrentTextChanged: {
updateEditors()
}
}
}
ScrollView {
id: scroll
Layout.fillWidth: true
Layout.fillHeight: true
contentWidth: column.width
clip: true
GridLayout {
id: scrollCol
anchors.fill: parent
columns: isHorizontal ? 2 : 1
}
}
RowLayout {
Layout.fillWidth: true
Button {
Layout.preferredWidth: 100
Layout.fillWidth: true
text: "Write"
onClicked: {
mCommands.setBMSconf(true)
}
}
Button {
Layout.preferredWidth: 100
Layout.fillWidth: true
text: "Read"
onClicked: {
mCommands.getBMSconf()
}
}
Button {
Layout.preferredWidth: 50
Layout.fillWidth: true
text: "..."
onClicked: menu.open()
Menu {
id: menu
width: 500
MenuItem {
text: "Read Default Settings"
onTriggered: {
mCommands.getBMSconfDefault()
}
}
}
}
}
}
Connections {
target: mCommands
// TODO: For some reason this does not work
onBmsConfigCheckResult: {
if (paramsNotSet.length > 0) {
var notUpdated = "The following parameters were truncated because " +
"they were beyond the hardware limits:\n"
for (var i = 0;i < paramsNotSet.length;i++) {
notUpdated += mbmsConf.getLongName(paramsNotSet[i]) + "\n"
}
VescIf.emitMessageDialog("Parameters truncated", notUpdated, false, false)
}
}
}
}

244
mobile/ConfigPageSignal.qml Normal file
View File

@@ -0,0 +1,244 @@
/*
Copyright 2020 Kevin Dionne kevin.dionne@ennoid.me
This file is part of ENNOID-BMS Tool.
ENNOID-BMS Tool is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
ENNOID-BMS Tool is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import QtQuick 2.7
import QtQuick.Controls 2.2
import QtQuick.Layouts 1.3
import Ennoid.bmsinterface 1.0
import Ennoid.commands 1.0
import Ennoid.configparams 1.0
Item {
property Commands mCommands: VescIf.commands()
property var editorsVisible: []
property bool isHorizontal: width > height
ParamEditors {
id: editors
}
onIsHorizontalChanged: {
// updateEditors()
}
function addSeparator(text) {
editorsVisible.push(editors.createSeparator(scrollCol, text))
editorsVisible[editorsVisible.length - 1].Layout.columnSpan = isHorizontal ? 2 : 1
}
function destroyEditors() {
for (var i = 0;i < editorsVisible.length;i++) {
editorsVisible[i].destroy();
}
editorsVisible = []
}
function createEditorMc(param) {
editorsVisible.push(editors.createEditorMc(scrollCol, param))
editorsVisible[editorsVisible.length - 1].Layout.preferredWidth = 500
editorsVisible[editorsVisible.length - 1].Layout.fillsWidth = true
}
function updateEditors() {
destroyEditors()
switch (pageBox.currentText) {
case "Discharge Current":
createEditorMc("shuntLCFactor")
break;
case "Pack Voltage":
createEditorMc("voltageLCFactor")
createEditorMc("voltageLCOffset")
break;
case "Load Voltage":
createEditorMc("loadVoltageFactor")
createEditorMc("loadVoltageOffset")
break;
case "Charger Voltage":
createEditorMc("chargerVoltageFactor")
createEditorMc("chargerVoltageOffset")
break;
case "Data Source":
createEditorMc("packVoltageDataSource")
createEditorMc("packCurrentDataSource")
break;
case "Buzzer Control":
createEditorMc("buzzerSignalSource")
createEditorMc("buzzerPersistent")
break;
default:
break;
}
}
ColumnLayout {
id: column
anchors.fill: parent
spacing: 0
GridLayout {
Layout.fillWidth: true
columns: isHorizontal ? 2 : 1
rowSpacing: -5
ComboBox {
id: pageBox
Layout.fillWidth: true
model: [
"Discharge Current",
"Pack Voltage",
"Load Voltage",
"Charger Voltage",
"Data Source",
"Buzzer Control"
]
onCurrentTextChanged: {
var tabTextOld = tabBox.currentText
switch(currentText) {
case "Discharge Current":
tabBox.model = [
""
]
break;
case "Pack Voltage":
tabBox.model = [
""
]
break;
case "Load Voltage":
tabBox.model = [
""
]
break;
case "Charger Voltage":
tabBox.model = [
""
]
break;
case "Data Source":
tabBox.model = [
""
]
break;
case "Buzzer Control":
tabBox.model = [
""
]
break;
default:
break;
}
tabBox.visible = tabBox.currentText.length !== 0
if (tabTextOld === tabBox.currentText) {
updateEditors()
}
}
}
ComboBox {
id: tabBox
Layout.fillWidth: true
onCurrentTextChanged: {
updateEditors()
}
}
}
ScrollView {
id: scroll
Layout.fillWidth: true
Layout.fillHeight: true
contentWidth: column.width
clip: true
GridLayout {
id: scrollCol
anchors.fill: parent
columns: isHorizontal ? 2 : 1
}
}
RowLayout {
Layout.fillWidth: true
Button {
Layout.preferredWidth: 100
Layout.fillWidth: true
text: "Write"
onClicked: {
mCommands.setBMSconf(true)
}
}
Button {
Layout.preferredWidth: 100
Layout.fillWidth: true
text: "Read"
onClicked: {
mCommands.getBMSconf()
}
}
Button {
Layout.preferredWidth: 50
Layout.fillWidth: true
text: "..."
onClicked: menu.open()
Menu {
id: menu
width: 500
MenuItem {
text: "Read Default Settings"
onTriggered: {
mCommands.getBMSconfDefault()
}
}
}
}
}
}
Connections {
target: mCommands
// TODO: For some reason this does not work
onBmsConfigCheckResult: {
if (paramsNotSet.length > 0) {
var notUpdated = "The following parameters were truncated because " +
"they were beyond the hardware limits:\n"
for (var i = 0;i < paramsNotSet.length;i++) {
notUpdated += mbmsConf.getLongName(paramsNotSet[i]) + "\n"
}
VescIf.emitMessageDialog("Parameters truncated", notUpdated, false, false)
}
}
}
}

209
mobile/ConfigPageSwitch.qml Normal file
View File

@@ -0,0 +1,209 @@
/*
Copyright 2020 Kevin Dionne kevin.dionne@ennoid.me
This file is part of ENNOID-BMS Tool.
ENNOID-BMS Tool is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
ENNOID-BMS Tool is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import QtQuick 2.7
import QtQuick.Controls 2.2
import QtQuick.Layouts 1.3
import Ennoid.bmsinterface 1.0
import Ennoid.commands 1.0
import Ennoid.configparams 1.0
Item {
property Commands mCommands: VescIf.commands()
property var editorsVisible: []
property bool isHorizontal: width > height
ParamEditors {
id: editors
}
onIsHorizontalChanged: {
// updateEditors()
}
function addSeparator(text) {
editorsVisible.push(editors.createSeparator(scrollCol, text))
editorsVisible[editorsVisible.length - 1].Layout.columnSpan = isHorizontal ? 2 : 1
}
function destroyEditors() {
for (var i = 0;i < editorsVisible.length;i++) {
editorsVisible[i].destroy();
}
editorsVisible = []
}
function createEditorMc(param) {
editorsVisible.push(editors.createEditorMc(scrollCol, param))
editorsVisible[editorsVisible.length - 1].Layout.preferredWidth = 500
editorsVisible[editorsVisible.length - 1].Layout.fillsWidth = true
}
function updateEditors() {
destroyEditors()
switch (pageBox.currentText) {
case "Discharge":
createEditorMc("LCUseDischarge")
createEditorMc("LCUsePrecharge")
createEditorMc("minimalPrechargePercentage")
createEditorMc("timeoutLCPreCharge")
createEditorMc("timeoutDischargeRetry")
break;
case "Charge":
createEditorMc("chargerEnabledThreshold")
createEditorMc("timeoutChargerDisconnected")
createEditorMc("allowChargingDuringDischarge")
createEditorMc("timeoutChargeRetry")
break;
default:
break;
}
}
ColumnLayout {
id: column
anchors.fill: parent
spacing: 0
GridLayout {
Layout.fillWidth: true
columns: isHorizontal ? 2 : 1
rowSpacing: -5
ComboBox {
id: pageBox
Layout.fillWidth: true
model: [
"Discharge",
"Charge"
]
onCurrentTextChanged: {
var tabTextOld = tabBox.currentText
switch(currentText) {
case "Discharge":
tabBox.model = [
""
]
break;
case "Charge":
tabBox.model = [
""
]
break;
default:
break;
}
tabBox.visible = tabBox.currentText.length !== 0
if (tabTextOld === tabBox.currentText) {
updateEditors()
}
}
}
ComboBox {
id: tabBox
Layout.fillWidth: true
onCurrentTextChanged: {
updateEditors()
}
}
}
ScrollView {
id: scroll
Layout.fillWidth: true
Layout.fillHeight: true
contentWidth: column.width
clip: true
GridLayout {
id: scrollCol
anchors.fill: parent
columns: isHorizontal ? 2 : 1
}
}
RowLayout {
Layout.fillWidth: true
Button {
Layout.preferredWidth: 100
Layout.fillWidth: true
text: "Write"
onClicked: {
mCommands.setBMSconf(true)
}
}
Button {
Layout.preferredWidth: 100
Layout.fillWidth: true
text: "Read"
onClicked: {
mCommands.getBMSconf()
}
}
Button {
Layout.preferredWidth: 50
Layout.fillWidth: true
text: "..."
onClicked: menu.open()
Menu {
id: menu
width: 500
MenuItem {
text: "Read Default Settings"
onTriggered: {
mCommands.getBMSconfDefault()
}
}
}
}
}
}
Connections {
target: mCommands
// TODO: For some reason this does not work
onBmsConfigCheckResult: {
if (paramsNotSet.length > 0) {
var notUpdated = "The following parameters were truncated because " +
"they were beyond the hardware limits:\n"
for (var i = 0;i < paramsNotSet.length;i++) {
notUpdated += mbmsConf.getLongName(paramsNotSet[i]) + "\n"
}
VescIf.emitMessageDialog("Parameters truncated", notUpdated, false, false)
}
}
}
}

393
mobile/ConnectBle.qml Normal file
View File

@@ -0,0 +1,393 @@
/*
Copyright 2017 - 2019 Benjamin Vedder benjamin@vedder.se
This file is part of VESC Tool.
VESC Tool is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
VESC Tool is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import QtQuick 2.7
import QtQuick.Controls 2.2
import QtQuick.Layouts 1.3
import Ennoid.bmsinterface 1.0
import Ennoid.bleuart 1.0
import Ennoid.commands 1.0
Item {
id: topItem
property BleUart mBle: VescIf.bleDevice()
property Commands mCommands: VescIf.commands()
property alias disconnectButton: disconnectButton
property bool isHorizontal: width > height
signal requestOpenControls()
ScrollView {
anchors.fill: parent
contentWidth: parent.width
clip: true
GridLayout {
id: grid
anchors.fill: parent
columns: isHorizontal ? 2 : 1
columnSpacing: 5
anchors.topMargin: 10
rowSpacing: 30
Image {
id: image
Layout.columnSpan: isHorizontal ? 2 : 1
Layout.preferredWidth: Math.min(topItem.width, topItem.height)
Layout.preferredHeight: (sourceSize.height * Layout.preferredWidth) / sourceSize.width
Layout.alignment: Qt.AlignHCenter | Qt.AlignBottom
source: "qrc:/res/logo_white.png"
}
GroupBox {
id: bleConnBox
title: qsTr("BLE Connection")
Layout.fillWidth: true
Layout.columnSpan: 1
GridLayout {
anchors.topMargin: -5
anchors.bottomMargin: -5
anchors.fill: parent
clip: false
visible: true
rowSpacing: -10
columnSpacing: 5
rows: 5
columns: 6
Button {
id: setNameButton
text: qsTr("Name")
Layout.columnSpan: 2
Layout.preferredWidth: 500
Layout.fillWidth: true
enabled: bleBox.count > 0
onClicked: {
if (bleItems.rowCount() > 0) {
bleNameDialog.open()
} else {
VescIf.emitMessageDialog("Set BLE Device Name",
"No device selected.",
false, false);
}
}
}
Button {
text: "Pair"
Layout.fillWidth: true
Layout.preferredWidth: 500
Layout.columnSpan: 2
onClicked: {
pairDialog.openDialog()
}
}
Button {
id: scanButton
text: qsTr("Scan")
Layout.columnSpan: 2
Layout.preferredWidth: 500
Layout.fillWidth: true
onClicked: {
scanButton.enabled = false
mBle.startScan()
}
}
ComboBox {
id: bleBox
Layout.columnSpan: 6
Layout.fillWidth: true
Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter
transformOrigin: Item.Center
textRole: "key"
model: ListModel {
id: bleItems
}
}
Button {
id: disconnectButton
text: qsTr("Disconnect")
enabled: false
Layout.preferredWidth: 500
Layout.fillWidth: true
Layout.columnSpan: 3
onClicked: {
VescIf.disconnectPort()
}
}
Button {
id: connectButton
text: qsTr("Connect")
enabled: false
Layout.preferredWidth: 500
Layout.fillWidth: true
Layout.columnSpan: 3
onClicked: {
if (bleItems.rowCount() > 0) {
connectButton.enabled = false
VescIf.connectBle(bleItems.get(bleBox.currentIndex).value)
}
}
}
}
}
GroupBox {
id: canFwdBox
title: qsTr("CAN Forwarding")
Layout.fillWidth: true
ColumnLayout {
anchors.topMargin: -5
anchors.bottomMargin: -5
anchors.fill: parent
spacing: -10
RowLayout {
Layout.fillWidth: true
ComboBox {
id: canIdBox
Layout.fillWidth: true
textRole: "key"
model: ListModel {
id: canItems
}
onCurrentIndexChanged: {
if (fwdCanBox.checked && canItems.rowCount() > 0) {
mCommands.setCanSendId(canItems.get(canIdBox.currentIndex).value)
}
}
}
CheckBox {
id: fwdCanBox
text: qsTr("Activate")
enabled: canIdBox.currentIndex >= 0 && canIdBox.count > 0
onClicked: {
mCommands.setSendCan(fwdCanBox.checked, canItems.get(canIdBox.currentIndex).value)
canScanButton.enabled = !checked
canAllButton.enabled = !checked
}
}
}
ProgressBar {
id: canScanBar
visible: false
Layout.fillWidth: true
indeterminate: true
Layout.preferredHeight: canAllButton.height
}
RowLayout {
id: canButtonLayout
Layout.fillWidth: true
Button {
id: canAllButton
text: "List All (no Scan)"
Layout.fillWidth: true
Layout.preferredWidth: 500
onClicked: {
canItems.clear()
for (var i = 0;i < 255;i++) {
var name = "ENNOID-BMS " + i
canItems.append({ key: name, value: i })
}
canIdBox.currentIndex = 0
}
}
Button {
id: canScanButton
text: "Scan CAN Bus"
Layout.fillWidth: true
Layout.preferredWidth: 500
onClicked: {
canScanBar.indeterminate = true
canButtonLayout.visible = false
canScanBar.visible = true
canItems.clear()
enabled = false
canAllButton.enabled = false
mCommands.pingCan()
}
}
}
}
}
}
}
PairingDialog {
id: pairDialog
}
Timer {
interval: 500
running: !scanButton.enabled
repeat: true
property int dots: 0
onTriggered: {
var text = "S"
for (var i = 0;i < dots;i++) {
text = "-" + text + "-"
}
dots++;
if (dots > 3) {
dots = 0;
}
scanButton.text = text
}
}
Timer {
interval: 100
running: true
repeat: true
onTriggered: {
connectButton.enabled = (bleItems.rowCount() > 0) && !VescIf.isPortConnected() && !mBle.isConnecting()
disconnectButton.enabled = VescIf.isPortConnected()
}
}
Connections {
target: mBle
onScanDone: {
if (done) {
scanButton.enabled = true
scanButton.text = qsTr("Scan")
}
bleItems.clear()
for (var addr in devs) {
var name = devs[addr]
var name2 = name + " [" + addr + "]"
var setName = VescIf.getBleName(addr)
if (setName.length > 0) {
setName += " [" + addr + "]"
bleItems.insert(0, { key: setName, value: addr })
} else if (name.indexOf("ENNOID-BMS") !== -1) {
bleItems.insert(0, { key: name2, value: addr })
} else {
bleItems.append({ key: name2, value: addr })
}
}
connectButton.enabled = (bleItems.rowCount() > 0) && !VescIf.isPortConnected()
bleBox.currentIndex = 0
}
onBleError: {
VescIf.emitMessageDialog("BLE Error", info, false, false)
}
}
Connections {
target: mCommands
onPingCanRx: {
canItems.clear()
for (var i = 0;i < devs.length;i++) {
var name = "ENNOID-BMS " + devs[i]
canItems.append({ key: name, value: devs[i] })
}
canScanButton.enabled = true
canAllButton.enabled = true
canIdBox.currentIndex = 0
canButtonLayout.visible = true
canScanBar.visible = false
canScanBar.indeterminate = false
}
}
Dialog {
id: bleNameDialog
standardButtons: Dialog.Ok | Dialog.Cancel
modal: true
focus: true
title: "Set BLE Device Name"
width: parent.width - 20
height: 200
closePolicy: Popup.CloseOnEscape
x: 10
y: Math.max(parent.height / 4 - height / 2, 20)
parent: ApplicationWindow.overlay
Rectangle {
anchors.fill: parent
height: stringInput.implicitHeight + 14
border.width: 2
border.color: "#8d8d8d"
color: "#33a8a8a8"
radius: 3
TextInput {
id: stringInput
color: "#ffffff"
anchors.fill: parent
anchors.margins: 7
font.pointSize: 12
focus: true
}
}
onAccepted: {
if (stringInput.text.length > 0) {
var addr = bleItems.get(bleBox.currentIndex).value
var setName = stringInput.text + " [" + addr + "]"
VescIf.storeBleName(addr, stringInput.text)
VescIf.storeSettings()
bleItems.set(bleBox.currentIndex, { key: setName, value: addr })
bleBox.currentText
}
}
}
}

110
mobile/ConnectBle2.qml Normal file
View File

@@ -0,0 +1,110 @@
/*
Copyright 2017 Benjamin Vedder benjamin@vedder.se
This file is part of VESC Tool.
VESC Tool is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
VESC Tool is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import QtQuick 2.4
import Ennoid.bmsinterface 1.0
import Ennoid.bleuart 1.0
import Ennoid.commands 1.0
ConnectBleForm {
property BleUart mBle: VescIf.bleDevice()
property Commands mCommands: VescIf.commands()
scanButton.onClicked: {
mBle.startScan()
scanButton.enabled = false
scanButton.text = "Scanning"
}
connectButton.onClicked: {
if (bleItems.rowCount() > 0) {
connectButton.enabled = false
VescIf.connectBle(bleItems.get(bleBox.currentIndex).value)
}
}
disconnectButton.onClicked: {
VescIf.disconnectPort()
}
fwdCanBox.onClicked: {
mCommands.setSendCan(fwdCanBox.checked, canIdBox.value)
}
canIdBox.onValueChanged: {
mCommands.setCanSendId(canIdBox.value)
}
Timer {
interval: 500
running: !scanButton.enabled
repeat: true
property int dots: 0
onTriggered: {
var text = "Scanning"
for (var i = 0;i < dots;i++) {
text = "-" + text + "-"
}
dots++;
if (dots > 3) {
dots = 0;
}
scanButton.text = text
}
}
Timer {
interval: 100
running: true
repeat: true
onTriggered: {
connectButton.enabled = (bleItems.rowCount() > 0) && !VescIf.isPortConnected() && !mBle.isConnecting()
disconnectButton.enabled = VescIf.isPortConnected()
}
}
Connections {
target: mBle
onScanDone: {
if (done) {
scanButton.enabled = true
scanButton.text = qsTr("Scan")
}
bleItems.clear()
for (var name in devs) {
var name2 = name + " [" + devs[name] + "]"
if (name.indexOf("VESC") !== -1) {
bleItems.insert(0, { key: name2, value: devs[name] })
} else {
bleItems.append({ key: name2, value: devs[name] })
}
}
connectButton.enabled = (bleItems.rowCount() > 0) && !VescIf.isPortConnected()
bleBox.currentIndex = 0
}
}
}

View File

@@ -0,0 +1,159 @@
/*
Copyright 2017 Benjamin Vedder benjamin@vedder.se
This file is part of VESC Tool.
VESC Tool is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
VESC Tool is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import QtQuick 2.7
import QtQuick.Controls 2.2
import QtQuick.Layouts 1.3
Item {
property alias scanButton: scanButton
property alias connectButton: connectButton
property alias disconnectButton: disconnectButton
property alias bleItems: bleItems
property alias canIdBox: canIdBox
property alias fwdCanBox: fwdCanBox
id: item1
width: 400
height: 400
property alias bleBox: bleBox
ColumnLayout {
anchors.fill: parent
Image {
id: image
Layout.preferredWidth: Math.min(parent.width, parent.height)
Layout.preferredHeight: (300 * Layout.preferredWidth) / 1549
Layout.alignment: Qt.AlignHCenter | Qt.AlignBottom
source: "../res/logo_white.png"
}
Item {
// Spacer
Layout.fillHeight: true
Layout.fillWidth: true
}
GridLayout {
clip: false
visible: true
rowSpacing: 0
columnSpacing: 5
rows: 4
columns: 2
Label {
id: connectionName
text: "BLE Connection"
Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter
Layout.fillWidth: true
horizontalAlignment: Text.AlignHCenter
Layout.bottomMargin: 5
Layout.columnSpan: 2
}
ComboBox {
id: bleBox
Layout.columnSpan: 2
Layout.preferredHeight: 48
Layout.fillHeight: false
Layout.fillWidth: true
Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter
transformOrigin: Item.Center
textRole: "key"
model: ListModel {
id: bleItems
}
}
Button {
id: scanButton
text: qsTr("Scan")
Layout.columnSpan: 2
Layout.preferredHeight: 48
Layout.fillWidth: true
}
Button {
id: connectButton
text: qsTr("Connect")
enabled: false
Layout.preferredHeight: 48
Layout.preferredWidth: 100
Layout.fillWidth: true
}
Button {
id: disconnectButton
text: qsTr("Disconnect")
enabled: false
Layout.preferredHeight: 48
Layout.preferredWidth: 100
Layout.fillWidth: true
}
}
RowLayout {
Layout.fillWidth: true
CheckBox {
id: fwdCanBox
text: qsTr("CAN Forward")
}
SpinBox {
id: canIdBox
Layout.fillWidth: true
}
}
Item {
id: element
// Spacer
Layout.fillHeight: true
Layout.fillWidth: true
}
Item {
scale: 1.2
// Spacer
Layout.fillHeight: true
Layout.fillWidth: true
}
Item {
scale: 1.2
// Spacer
Layout.fillHeight: true
Layout.fillWidth: true
}
}
}

456
mobile/Controls.qml Normal file
View File

@@ -0,0 +1,456 @@
/*
Copyright 2018 Benjamin Vedder benjamin@vedder.se
This file is part of VESC Tool.
VESC Tool is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
VESC Tool is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import QtQuick 2.7
import QtQuick.Controls 2.2
import QtQuick.Layouts 1.3
import Ennoid.bmsinterface 1.0
import Ennoid.commands 1.0
import Ennoid.configparams 1.0
Item {
property int parentWidth: 10
property int parentHeight: 10
property Commands mCommands: VescIf.commands()
property ConfigParams mbmsConfig: VescIf.bmsConfig()
property ConfigParams mInfoConf: VescIf.infoConfig()
function openDialog() {
dialog.open()
opacitySlider.value = 1
}
function testConnected() {
if (VescIf.isPortConnected()) {
return true
} else {
VescIf.emitMessageDialog(
"Connection Error",
"The BMS is not connected. Please connect it to run detection.",
false, false)
return false
}
}
Dialog {
id: dialog
standardButtons: Dialog.Close
modal: false
focus: true
width: parentWidth - 40
height: parentHeight - 40
closePolicy: Popup.CloseOnEscape
x: 20
y: 10
background.opacity: opacitySlider.value
ColumnLayout {
anchors.fill: parent
SwipeView {
id: swipeView
currentIndex: tabBar.currentIndex
Layout.fillHeight: true
Layout.fillWidth: true
clip: true
Page {
background: Rectangle {
opacity: 0.0
}
ColumnLayout {
anchors.fill: parent
spacing: 0
DoubleSpinBox {
id: currentBox
Layout.fillWidth: true
decimals: 1
prefix: "I: "
suffix: " A"
realFrom: 0.0
realTo: 500.0
realValue: 3.0
realStepSize: 1.0
}
Rectangle {
Layout.fillHeight: true
}
CheckBox {
id: maintainCurrentBox
text: "Continue after release"
}
RowLayout {
Layout.fillWidth: true
Button {
Layout.fillWidth: true
Layout.preferredWidth: 100
text: "Set REV"
onPressedChanged: {
if (pressed) {
mCommands.setCurrent(-currentBox.realValue)
} else if (!maintainCurrentBox.checked) {
mCommands.setCurrent(0.0)
}
}
}
Button {
Layout.fillWidth: true
Layout.preferredWidth: 100
text: "Set FWD"
onPressedChanged: {
if (pressed) {
mCommands.setCurrent(currentBox.realValue)
} else if (!maintainCurrentBox.checked) {
mCommands.setCurrent(0.0)
}
}
}
}
Button {
Layout.fillWidth: true
text: "Brake"
onPressedChanged: {
if (pressed) {
mCommands.setCurrentBrake(currentBox.realValue)
} else if (!maintainCurrentBox.checked) {
mCommands.setCurrent(0.0)
}
}
}
Button {
Layout.fillWidth: true
text: "Release"
onClicked: {
mCommands.setCurrent(0.0)
}
}
}
}
Page {
background: Rectangle {
opacity: 0.0
}
ColumnLayout {
anchors.fill: parent
spacing: 0
DoubleSpinBox {
id: dutyBox
Layout.fillWidth: true
prefix: "D: "
decimals: 3
realFrom: 0.0
realTo: 1.0
realValue: 0.1
realStepSize: 0.01
}
Rectangle {
Layout.fillHeight: true
}
CheckBox {
id: maintainDutyBox
text: "Continue after release"
}
RowLayout {
Layout.fillWidth: true
Button {
Layout.fillWidth: true
Layout.preferredWidth: 100
text: "Set REV"
onPressedChanged: {
if (pressed) {
mCommands.setDutyCycle(-dutyBox.realValue)
} else if (!maintainDutyBox.checked) {
mCommands.setCurrent(0.0)
}
}
}
Button {
Layout.fillWidth: true
Layout.preferredWidth: 100
text: "Set FWD"
onPressedChanged: {
if (pressed) {
mCommands.setDutyCycle(dutyBox.realValue)
} else if (!maintainDutyBox.checked) {
mCommands.setCurrent(0.0)
}
}
}
}
Button {
Layout.fillWidth: true
text: "0 Duty (brake)"
onPressedChanged: {
if (pressed) {
mCommands.setDutyCycle(0.0)
} else if (!maintainDutyBox.checked) {
mCommands.setCurrent(0.0)
}
}
}
Button {
Layout.fillWidth: true
text: "Release"
onClicked: {
mCommands.setCurrent(0.0)
}
}
}
}
Page {
background: Rectangle {
opacity: 0.0
}
ColumnLayout {
anchors.fill: parent
spacing: 0
DoubleSpinBox {
id: speedBox
Layout.fillWidth: true
prefix: "\u03C9: "
suffix: " ERPM"
decimals: 1
realFrom: 0.0
realTo: 500000
realValue: 3000
realStepSize: 500
}
Rectangle {
Layout.fillHeight: true
}
CheckBox {
id: maintainSpeedBox
text: "Continue after release"
}
RowLayout {
Layout.fillWidth: true
Button {
Layout.fillWidth: true
Layout.preferredWidth: 100
text: "Set REV"
onPressedChanged: {
if (pressed) {
mCommands.setRpm(-speedBox.realValue)
} else if (!maintainSpeedBox.checked) {
mCommands.setCurrent(0.0)
}
}
}
Button {
Layout.fillWidth: true
Layout.preferredWidth: 100
text: "Set FWD"
onPressedChanged: {
if (pressed) {
mCommands.setRpm(speedBox.realValue)
} else if (!maintainSpeedBox.checked) {
mCommands.setCurrent(0.0)
}
}
}
}
Button {
Layout.fillWidth: true
text: "0 ERPM (brake)"
onPressedChanged: {
if (pressed) {
mCommands.setRpm(0.0)
} else if (!maintainSpeedBox.checked) {
mCommands.setCurrent(0.0)
}
}
}
Button {
Layout.fillWidth: true
text: "Release"
onClicked: {
mCommands.setCurrent(0.0)
}
}
}
}
Page {
background: Rectangle {
opacity: 0.0
}
ColumnLayout {
anchors.fill: parent
spacing: 0
DoubleSpinBox {
id: posBox
Layout.fillWidth: true
prefix: "P: "
suffix: " \u00B0"
decimals: 3
realFrom: 0
realTo: 360
realValue: 20
realStepSize: 0.1
}
Rectangle {
Layout.fillHeight: true
}
CheckBox {
id: maintainPosBox
text: "Continue after release"
}
Button {
Layout.fillWidth: true
text: "Set"
onPressedChanged: {
if (pressed) {
mCommands.setPos(posBox.realValue)
} else if (!maintainPosBox.checked) {
mCommands.setCurrent(0.0)
}
}
}
Button {
Layout.fillWidth: true
text: "Release"
onClicked: {
mCommands.setCurrent(0.0)
}
}
}
}
}
RowLayout {
Layout.fillWidth: true
Text {
color: "white"
text: qsTr("Opacity")
}
Slider {
id: opacitySlider
Layout.fillWidth: true
from: 0.1
to: 1.0
value: 1.0
}
}
}
header: Rectangle {
color: "#dbdbdb"
height: tabBar.height
TabBar {
id: tabBar
currentIndex: swipeView.currentIndex
anchors.fill: parent
implicitWidth: 0
clip: true
background: Rectangle {
opacity: 1
color: "#4f4f4f"
}
property int buttons: 4
property int buttonWidth: 120
TabButton {
text: qsTr("Current")
width: Math.max(tabBar.buttonWidth, tabBar.width / tabBar.buttons)
}
TabButton {
text: qsTr("Duty")
width: Math.max(tabBar.buttonWidth, tabBar.width / tabBar.buttons)
}
TabButton {
text: qsTr("RPM")
width: Math.max(tabBar.buttonWidth, tabBar.width / tabBar.buttons)
}
TabButton {
text: qsTr("Position")
width: Math.max(tabBar.buttonWidth, tabBar.width / tabBar.buttons)
}
}
}
}
Timer {
id: aliveTimer
interval: 200
running: true
repeat: true
onTriggered: {
if (VescIf.isPortConnected() && dialog.visible) {
mCommands.sendAlive()
}
}
}
}

217
mobile/CustomGauge.qml Normal file
View File

@@ -0,0 +1,217 @@
/*
Copyright 2018 - 2019 Benjamin Vedder benjamin@vedder.se
This file is part of VESC Tool.
VESC Tool is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
VESC Tool is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import QtQuick 2.0
import QtQuick.Controls 2.2
import QtQuick.Layouts 1.3
import QtQuick.Extras 1.4
import QtGraphicalEffects 1.0
import QtQuick.Controls.Material 2.2
Item {
property alias minimumValue: gauge.minimumValue
property alias maximumValue: gauge.maximumValue
property alias value: gauge.value
property string unitText: ""
property string typeText: ""
property string tickmarkSuffix: ""
property double labelStep: 10
property double tickmarkScale: 1
property color traceColor: "#606060"
property double maxAngle: 144
property double minAngle: -144
CircularGauge {
id: gauge
anchors.fill: parent
Behavior on value {
NumberAnimation {
easing.type: Easing.OutCirc
duration: 100
}
}
style: CircularGaugeStyle {
id: style
labelStepSize: labelStep
tickmarkStepSize: labelStep
labelInset: outerRadius * 0.28
tickmarkInset: 2
minorTickmarkInset: 2
minimumValueAngle: minAngle
maximumValueAngle: maxAngle
background:
Canvas {
property double value: gauge.value
anchors.fill: parent
onValueChanged: requestPaint()
function d2r(degrees) {
return degrees * (Math.PI / 180.0);
}
onPaint: {
var ctx = getContext("2d");
ctx.reset();
ctx.beginPath();
var gradient = ctx.createRadialGradient(outerRadius, outerRadius, 0,
outerRadius, outerRadius, outerRadius)
gradient.addColorStop(0.7, Material.background)
gradient.addColorStop(1, traceColor)
ctx.fillStyle = gradient
ctx.arc(outerRadius, outerRadius, outerRadius, 0, Math.PI * 2)
ctx.fill()
ctx.beginPath();
ctx.strokeStyle = Material.background
ctx.lineWidth = outerRadius
ctx.arc(outerRadius,
outerRadius,
outerRadius / 2,
d2r(valueToAngle(Math.max(gauge.value, 0)) - 90),
d2r(valueToAngle(gauge.maximumValue + 1) - 90));
ctx.arc(outerRadius,
outerRadius,
outerRadius / 2,
d2r(valueToAngle(gauge.minimumValue) - 90),
d2r(valueToAngle(Math.min(gauge.value, 0)) - 90));
ctx.stroke();
ctx.beginPath();
ctx.arc(outerRadius,
outerRadius,
outerRadius / 2,
d2r(valueToAngle(gauge.maximumValue) - 90),
d2r(valueToAngle(gauge.minimumValue) - 90));
ctx.stroke();
ctx.beginPath();
ctx.strokeStyle = "darkGray"
ctx.lineWidth = 1
ctx.arc(outerRadius,
outerRadius,
outerRadius - 0.5,
0, 2 * Math.PI);
ctx.stroke();
}
}
needle: Item {
y: -outerRadius * 0.82
height: outerRadius * 0.18
Rectangle {
id: needle
height: parent.height
color: "red"
width: height * 0.13
antialiasing: true
radius: 10
}
Glow {
anchors.fill: needle
radius: 5
samples: 10
spread: 0.6
color: "darkred"
source: needle
}
}
foreground: Item {
Text {
id: speedLabel
anchors.centerIn: parent
text: gauge.value.toFixed(0)
horizontalAlignment: Text.AlignHCenter
font.pixelSize: outerRadius * 0.3
color: "white"
antialiasing: true
}
Text {
id: speedLabelUnit
text: unitText
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: speedLabel.bottom
horizontalAlignment: Text.AlignHCenter
font.pixelSize: outerRadius * 0.15
color: "white"
antialiasing: true
}
Text {
id: typeLabel
text: typeText
verticalAlignment: Text.AlignVCenter
anchors.horizontalCenter: parent.horizontalCenter
anchors.bottom: speedLabel.top
anchors.bottomMargin: outerRadius * 0.1
horizontalAlignment: Text.AlignHCenter
font.pixelSize: outerRadius * 0.15
color: "white"
antialiasing: true
}
}
function isCovered(value) {
var res = false
if (gauge.value > 0) {
if (value <= gauge.value && value >= 0) {
res = true
}
} else {
if (value >= gauge.value && value <= 0) {
res = true
}
}
return res
}
tickmarkLabel: Text {
font.pixelSize: outerRadius * 0.15
text: parseFloat(styleData.value * tickmarkScale).toFixed(0) + tickmarkSuffix
color: isCovered(styleData.value) ? "white" : "darkGray"
antialiasing: true
}
tickmark: Rectangle {
implicitWidth: 2
implicitHeight: outerRadius * 0.09
antialiasing: true
smooth: true
color: isCovered(styleData.value) ? "white" : "darkGray"
}
minorTickmark: Rectangle {
implicitWidth: 1.5
implicitHeight: outerRadius * 0.05
antialiasing: true
smooth: true
color: isCovered(styleData.value) ? "white" : "darkGray"
}
}
}
}

262
mobile/DetectBldc.qml Normal file
View File

@@ -0,0 +1,262 @@
/*
Copyright 2018 Benjamin Vedder benjamin@vedder.se
This file is part of VESC Tool.
VESC Tool is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
VESC Tool is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import QtQuick 2.7
import QtQuick.Controls 2.2
import QtQuick.Layouts 1.3
import Vedder.vesc.bmsinterface 1.0
import Vedder.vesc.commands 1.0
import Vedder.vesc.configparams 1.0
Item {
property int parentWidth: 10
property real intLim: 0.0
property real coupling: 0.0
property var hallTable: []
property int hallRes: -4
property bool resultReceived: false
property Commands mCommands: VescIf.commands()
property ConfigParams mbmsConfig: VescIf.bmsConfig()
property ConfigParams mInfoConf: VescIf.infoConfig()
function openDialog() {
dialog.open()
}
function updateDisplay() {
var txt = ""
txt +=
"Integrator limit : " + parseFloat(intLim).toFixed(2) + "\n" +
"BEMF Coupling : " + parseFloat(coupling).toFixed(2) + "\n"
if (hallRes == 0) {
txt += "Detected hall sensor table:\n"
for (var i = 0;i < hallTable.length;i++) {
txt += "" + hallTable[i]
if (i != hallTable.length - 1) {
txt += ", "
}
}
} else if (hallRes == -1) {
txt += "Hall sensor detection failed:\n"
for (var i = 0;i < hallTable.length;i++) {
txt += "" + hallTable[i]
if (i != hallTable.length - 1) {
txt += ", "
}
}
} else if (hallRes == -2) {
txt += "WS2811 enabled. Hall sensors cannot be used."
} else if (hallRes == -3) {
txt += "Encoder enabled. Hall sensors cannot be used."
} else if (hallRes == -4) {
txt += "Detected hall sensor table:"
} else {
txt += "Unknown hall error: " + hallRes
}
resultArea.text = txt
}
function testConnected() {
if (VescIf.isPortConnected()) {
return true
} else {
VescIf.emitMessageDialog(
"Connection Error",
"The VESC is not connected. Please connect it to run detection.",
false, false)
return false
}
}
Component.onCompleted: {
updateDisplay()
}
Dialog {
id: dialog
standardButtons: Dialog.Close
modal: true
focus: true
width: parentWidth - 20
height: Math.min(implicitHeight, column.height - 40)
closePolicy: Popup.CloseOnEscape
x: 10
y: 10
ScrollView {
anchors.fill: parent
clip: true
contentWidth: parent.width
ColumnLayout {
anchors.fill: parent
spacing: 0
DoubleSpinBox {
id: currentBox
Layout.fillWidth: true
decimals: 2
realValue: 5.0
realFrom: 0.0
realTo: 200.0
prefix: "I: "
suffix: " A"
}
DoubleSpinBox {
id: dutyBox
Layout.fillWidth: true
decimals: 2
realValue: 0.05
realFrom: 0.0
realTo: 1.0
realStepSize: 0.01
prefix: "D: "
}
DoubleSpinBox {
id: erpmBox
Layout.fillWidth: true
decimals: 1
realValue: 450.0
realFrom: 0.0
realTo: 20000.0
realStepSize: 10.0
prefix: "\u03C9: "
suffix: " ERPM"
}
RowLayout {
Layout.fillWidth: true
Button {
text: "Help"
Layout.fillWidth: true
Layout.preferredWidth: 50
onClicked: {
VescIf.emitMessageDialog(
mInfoConf.getLongName("help_bldc_detect"),
mInfoConf.getDescription("help_bldc_detect"),
true, true)
}
}
Button {
text: "Detect"
Layout.preferredWidth: 50
Layout.fillWidth: true
onClicked: {
if (!testConnected()) {
return
}
detectDialog.open()
}
}
}
TextArea {
id: resultArea
Layout.fillWidth: true
Layout.preferredHeight: 180
readOnly: true
font.family: "DejaVu Sans Mono"
}
Button {
text: "Apply & Close"
Layout.fillWidth: true
onClicked: {
if (!resultReceived) {
VescIf.emitMessageDialog("Apply Detection Result",
"Detection result not received. Make sure to run the detection first.",
false, false)
return
}
mbmsConfig.updateParamDouble("sl_bemf_coupling_k", coupling)
mbmsConfig.updateParamDouble("sl_cycle_int_limit", intLim)
if (hallRes == 0) {
for(var i = 0;i < 7;i++) {
mbmsConfig.updateParamInt("hall_table_" + i, hallTable[i])
}
}
dialog.close()
}
}
}
}
}
Dialog {
id: detectDialog
standardButtons: Dialog.Ok | Dialog.Cancel
modal: true
focus: true
width: parentWidth - 20
closePolicy: Popup.CloseOnEscape
title: "Detect BLDC Parameters"
x: 10
y: dialog.y + dialog.height / 2 - height / 2
Text {
id: detectLabel
verticalAlignment: Text.AlignVCenter
anchors.fill: parent
wrapMode: Text.WordWrap
text:
"This is going to spin up the motor. Make " +
"sure that nothing is in the way."
}
onAccepted: {
mCommands.detectMotorParam(currentBox.realValue, erpmBox.realValue, dutyBox.realValue)
}
}
Connections {
target: mCommands
onBldcDetectReceived: {
if (param.cycle_int_limit < 0.01 && param.bemf_coupling_k < 0.01) {
VescIf.emitStatusMessage("Bad Detection Result Received", false)
VescIf.emitMessageDialog("BLDC Detection",
"Bad Detection Result Received",
false, false)
} else {
VescIf.emitStatusMessage("Detection Result Received", true)
intLim = param.cycle_int_limit
coupling = param.bemf_coupling_k
hallTable = param.hall_table
hallRes = param.hall_res
resultReceived = true
updateDisplay()
}
}
}
}

194
mobile/DetectFocEncoder.qml Normal file
View File

@@ -0,0 +1,194 @@
/*
Copyright 2018 Benjamin Vedder benjamin@vedder.se
This file is part of VESC Tool.
VESC Tool is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
VESC Tool is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import QtQuick 2.7
import QtQuick.Controls 2.2
import QtQuick.Layouts 1.3
import Vedder.vesc.bmsinterface 1.0
import Vedder.vesc.commands 1.0
import Vedder.vesc.configparams 1.0
Item {
property int parentWidth: 10
property real mOffset: 0.0
property real mRatio: 0.0
property bool mInverted: false
property Commands mCommands: VescIf.commands()
property ConfigParams mbmsConfig: VescIf.bmsConfig()
property ConfigParams mInfoConf: VescIf.infoConfig()
function openDialog() {
dialog.open()
}
function updateDisplay() {
resultArea.text =
"Offset : " + parseFloat(mOffset).toFixed(1) + "\n" +
"Ratio : " + parseFloat(mRatio).toFixed(1) + "\n" +
"Inverted : " + mInverted
}
function testConnected() {
if (VescIf.isPortConnected()) {
return true
} else {
VescIf.emitMessageDialog(
"Connection Error",
"The VESC is not connected. Please connect it to run detection.",
false, false)
return false
}
}
Component.onCompleted: {
updateDisplay()
}
Dialog {
id: dialog
standardButtons: Dialog.Close
modal: true
focus: true
width: parentWidth - 20
height: Math.min(implicitHeight, column.height - 40)
closePolicy: Popup.CloseOnEscape
x: 10
y: 10
ScrollView {
anchors.fill: parent
clip: true
contentWidth: parent.width
ColumnLayout {
anchors.fill: parent
spacing: 0
DoubleSpinBox {
id: currentBox
Layout.fillWidth: true
decimals: 2
realValue: 10.0
realFrom: 0.0
realTo: 200.0
prefix: "I: "
suffix: " A"
}
RowLayout {
Layout.fillWidth: true
Button {
text: "Help"
Layout.preferredWidth: 50
Layout.fillWidth: true
onClicked: {
VescIf.emitMessageDialog(
mInfoConf.getLongName("help_foc_encoder_detect"),
mInfoConf.getDescription("help_foc_encoder_detect"),
true, true)
}
}
Button {
text: "Detect"
Layout.preferredWidth: 50
Layout.fillWidth: true
onClicked: {
if (!testConnected()) {
return
}
detectDialog.open()
}
}
}
TextArea {
id: resultArea
Layout.fillWidth: true
Layout.preferredHeight: 200
readOnly: true
wrapMode: TextEdit.WordWrap
font.family: "DejaVuSansMono"
}
Button {
text: "Apply & Close"
Layout.fillWidth: true
onClicked: {
mbmsConfig.updateParamDouble("foc_encoder_offset", mOffset)
mbmsConfig.updateParamDouble("foc_encoder_ratio", mRatio)
mbmsConfig.updateParamBool("foc_encoder_inverted", mInverted)
VescIf.emitStatusMessage("Encoder Parameters Applied", true)
dialog.close()
}
}
}
}
}
Dialog {
id: detectDialog
standardButtons: Dialog.Ok | Dialog.Cancel
modal: true
focus: true
width: parentWidth - 20
closePolicy: Popup.CloseOnEscape
title: "Detect FOC Encoder Parameters"
x: 10
y: dialog.y + dialog.height / 2 - height / 2
Text {
id: detectLambdaLabel
verticalAlignment: Text.AlignVCenter
anchors.fill: parent
wrapMode: Text.WordWrap
text:
"This is going to turn the motor slowly. Make " +
"sure that nothing is in the way."
}
onAccepted: {
mCommands.measureEncoder(currentBox.realValue)
}
}
Connections {
target: mCommands
onEncoderParamReceived: {
if (offset > 1000.0) {
VescIf.emitStatusMessage("Encoder not enabled in firmware", false)
VescIf.emitMessageDialog("Error",
"Encoder support is not enabled. Enable it in the general settings.",
false, false)
} else {
VescIf.emitStatusMessage("Encoder Result Received", true)
mOffset = offset
mRatio = ratio
mInverted = inverted
updateDisplay()
}
}
}
}

204
mobile/DetectFocHall.qml Normal file
View File

@@ -0,0 +1,204 @@
/*
Copyright 2018 Benjamin Vedder benjamin@vedder.se
This file is part of VESC Tool.
VESC Tool is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
VESC Tool is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import QtQuick 2.7
import QtQuick.Controls 2.2
import QtQuick.Layouts 1.3
import Vedder.vesc.bmsinterface 1.0
import Vedder.vesc.commands 1.0
import Vedder.vesc.configparams 1.0
Item {
property int parentWidth: 10
property var table: []
property Commands mCommands: VescIf.commands()
property ConfigParams mbmsConfig: VescIf.bmsConfig()
property ConfigParams mInfoConf: VescIf.infoConfig()
function openDialog() {
dialog.open()
}
function updateDisplay() {
var txt = "Hall Table:\n"
for (var i = 0;i < table.length;i++) {
txt += "" + table[i]
if (i != table.length - 1) {
txt += ", "
}
}
resultArea.text = txt
}
function testConnected() {
if (VescIf.isPortConnected()) {
return true
} else {
VescIf.emitMessageDialog(
"Connection Error",
"The VESC is not connected. Please connect it to run detection.",
false, false)
return false
}
}
Component.onCompleted: {
updateDisplay()
}
Dialog {
id: dialog
standardButtons: Dialog.Close
modal: true
focus: true
width: parentWidth - 20
height: Math.min(implicitHeight, column.height - 40)
closePolicy: Popup.CloseOnEscape
x: 10
y: 10
ScrollView {
anchors.fill: parent
clip: true
contentWidth: parent.width
ColumnLayout {
anchors.fill: parent
spacing: 0
DoubleSpinBox {
id: currentBox
Layout.fillWidth: true
decimals: 2
realValue: 10.0
realFrom: 0.0
realTo: 200.0
prefix: "I: "
suffix: " A"
}
RowLayout {
Layout.fillWidth: true
Button {
text: "Help"
Layout.preferredWidth: 50
Layout.fillWidth: true
onClicked: {
VescIf.emitMessageDialog(
mInfoConf.getLongName("help_foc_hall_detect"),
mInfoConf.getDescription("help_foc_hall_detect"),
true, true)
}
}
Button {
text: "Detect"
Layout.preferredWidth: 50
Layout.fillWidth: true
onClicked: {
if (!testConnected()) {
return
}
detectDialog.open()
}
}
}
TextArea {
id: resultArea
Layout.fillWidth: true
Layout.preferredHeight: 200
readOnly: true
wrapMode: TextEdit.WordWrap
font.family: "DejaVuSansMono"
}
Button {
text: "Apply & Close"
Layout.fillWidth: true
onClicked: {
if (table.length != 8) {
VescIf.emitMessageDialog("Apply Error",
"Hall table is empty.",
false, false)
return
}
for(var i = 0;i < 7;i++) {
mbmsConfig.updateParamInt("foc_hall_table_" + i, table[i])
}
VescIf.emitStatusMessage("Hall Sensor Parameters Applied", true)
dialog.close()
}
}
}
}
}
Dialog {
id: detectDialog
standardButtons: Dialog.Ok | Dialog.Cancel
modal: true
focus: true
width: parentWidth - 20
closePolicy: Popup.CloseOnEscape
title: "Detect FOC Hall Sensor Parameters"
x: 10
y: dialog.y + dialog.height / 2 - height / 2
Text {
id: detectLambdaLabel
verticalAlignment: Text.AlignVCenter
anchors.fill: parent
wrapMode: Text.WordWrap
text:
"This is going to turn the motor slowly. Make " +
"sure that nothing is in the way."
}
onAccepted: {
mCommands.measureHallFoc(currentBox.realValue)
}
}
Connections {
target: mCommands
onFocHallTableReceived: {
if (res !== 0) {
VescIf.emitStatusMessage("Bad FOC Hall Detection Result Received", false)
VescIf.emitMessageDialog("Bad FOC Hall Detection Result Received",
"Could not detect hall sensors. Make sure that everything " +
"is connected properly.",
false, false)
} else {
table = hall_table
updateDisplay()
}
}
}
}

367
mobile/DetectFocParam.qml Normal file
View File

@@ -0,0 +1,367 @@
/*
Copyright 2018 Benjamin Vedder benjamin@vedder.se
This file is part of VESC Tool.
VESC Tool is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
VESC Tool is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import QtQuick 2.7
import QtQuick.Controls 2.2
import QtQuick.Layouts 1.3
import Vedder.vesc.bmsinterface 1.0
import Vedder.vesc.commands 1.0
import Vedder.vesc.configparams 1.0
Item {
property int parentWidth: 10
property real res: 0.0
property real ind: 0.0
property real lambda: 0.0
property real kp: 0.0
property real ki: 0.0
property real gain: 0.0
property Commands mCommands: VescIf.commands()
property ConfigParams mbmsConfig: VescIf.bmsConfig()
property ConfigParams mInfoConf: VescIf.infoConfig()
function openDialog() {
dialog.open()
}
function updateDisplay() {
resultArea.text =
"R : " + parseFloat(res * 1e3).toFixed(2) + " m\u03A9\n" +
"L : " + parseFloat(ind * 1e6).toFixed(2) + " µH\n" +
"\u03BB : " + parseFloat(lambda * 1e3).toFixed(3) + " mWb\n" +
"KP : " + parseFloat(kp).toFixed(4) + "\n" +
"KI : " + parseFloat(ki).toFixed(2) + "\n" +
"Gain : " + parseFloat(gain).toFixed(2)
}
function calcKpKi() {
if (res < 1e-10) {
VescIf.emitMessageDialog("Calculate Error",
"R is 0. Please measure it first.",
false, false)
return;
}
if (ind < 1e-10) {
VescIf.emitMessageDialog("Calculate Error",
"L is 0. Please measure it first.",
false, false)
return;
}
// https://e2e.ti.com/blogs_/b/motordrivecontrol/archive/2015/07/20/teaching-your-pi-controller-to-behave-part-ii
var tc = tcBox.realValue * 1e-6
var bw = 1.0 / tc
kp = ind * bw;
ki = res * bw;
updateDisplay()
}
function calcGain() {
if (lambda < 1e-10) {
VescIf.emitMessageDialog("Calculate Error",
"\u03BB is 0. Please measure it first.",
false, false)
return;
}
gain = 0.001 / (lambda * lambda)
updateDisplay()
}
function testConnected() {
if (VescIf.isPortConnected()) {
return true
} else {
VescIf.emitMessageDialog(
"Connection Error",
"The VESC is not connected. Please connect it to run detection.",
false, false)
return false
}
}
Component.onCompleted: {
updateDisplay()
}
Dialog {
id: dialog
standardButtons: Dialog.Close
modal: true
focus: true
width: parentWidth - 20
height: Math.min(implicitHeight, column.height - 40)
closePolicy: Popup.CloseOnEscape
x: 10
y: 10
ScrollView {
anchors.fill: parent
clip: true
contentWidth: parent.width
ColumnLayout {
anchors.fill: parent
spacing: 0
DoubleSpinBox {
id: currentBox
Layout.fillWidth: true
decimals: 2
realValue: 5.0
realFrom: 0.0
realTo: 200.0
prefix: "I: "
suffix: " A"
}
DoubleSpinBox {
id: dutyBox
Layout.fillWidth: true
decimals: 2
realValue: 0.5
realFrom: 0.0
realTo: 1.0
realStepSize: 0.1
prefix: "D: "
}
DoubleSpinBox {
id: erpmBox
Layout.fillWidth: true
decimals: 1
realValue: 450.0
realFrom: 0.0
realTo: 20000.0
realStepSize: 10.0
prefix: "\u03C9: "
suffix: " ERPM"
}
DoubleSpinBox {
id: tcBox
Layout.fillWidth: true
decimals: 1
realValue: 1000.0
realFrom: 0.0
realTo: 1000000.0
realStepSize: 100.0
prefix: "T: "
suffix: " µS"
}
Button {
text: "Help"
Layout.fillWidth: true
onClicked: {
VescIf.emitMessageDialog(
mInfoConf.getLongName("help_foc_detect"),
mInfoConf.getDescription("help_foc_detect"),
true, true)
}
}
RowLayout {
Layout.fillWidth: true
Button {
text: "Detect R&L"
Layout.preferredWidth: 50
Layout.fillWidth: true
onClicked: {
if (!testConnected()) {
return
}
detectRlDialog.open()
}
}
Button {
text: "DETECT \u03BB"
font.capitalization: Font.MixedCase
Layout.preferredWidth: 50
Layout.fillWidth: true
onClicked: {
if (!testConnected()) {
return
}
if (res < 1e-9) {
VescIf.emitMessageDialog("Detect",
"R is 0. Please measure it first.",
false, false)
} else {
detectLambdaDialog.open()
}
}
}
}
Button {
text: "CALCULATE KP, KI AND \u03BB"
font.capitalization: Font.MixedCase
Layout.fillWidth: true
onClicked: {
calcKpKi()
calcGain()
}
}
TextArea {
id: resultArea
Layout.fillWidth: true
Layout.preferredHeight: 200
readOnly: true
font.family: "DejaVu Sans Mono"
}
Button {
text: "Apply & Close"
Layout.fillWidth: true
onClicked: {
if (res < 1e-10) {
VescIf.emitMessageDialog("Apply Error",
"R is 0. Please measure it first.",
false, false)
return
}
if (ind < 1e-10) {
VescIf.emitMessageDialog("Apply Error",
"L is 0. Please measure it first.",
false, false)
return
}
if (lambda < 1e-10) {
VescIf.emitMessageDialog("Apply Error",
"\u03BB is 0. Please measure it first.",
false, false)
return
}
calcKpKi()
calcGain()
mbmsConfig.updateParamDouble("foc_motor_r", res)
mbmsConfig.updateParamDouble("foc_motor_l", ind)
mbmsConfig.updateParamDouble("foc_motor_flux_linkage", lambda)
mbmsConfig.updateParamDouble("foc_current_kp", kp)
mbmsConfig.updateParamDouble("foc_current_ki", ki)
mbmsConfig.updateParamDouble("foc_observer_gain", gain * 1e6)
dialog.close()
}
}
}
}
}
Dialog {
id: detectRlDialog
standardButtons: Dialog.Ok
modal: true
focus: true
width: parentWidth - 20
closePolicy: Popup.CloseOnEscape
title: "Measure R & L"
x: 10
y: dialog.y + dialog.height / 2 - height / 2
Text {
id: detectRlLabel
verticalAlignment: Text.AlignVCenter
anchors.fill: parent
wrapMode: Text.WordWrap
text:
"When measuring R & L the motor is going to make some noises, but " +
"not rotate. These noises are completely normal, so don't unplug " +
"anything unless you see smoke."
}
onAccepted: {
mCommands.measureRL()
}
}
Dialog {
id: detectLambdaDialog
standardButtons: Dialog.Ok | Dialog.Cancel
modal: true
focus: true
width: parentWidth - 20
closePolicy: Popup.CloseOnEscape
title: "Warning"
x: 10
y: dialog.y + dialog.height / 2 - height / 2
Text {
id: detectLambdaLabel
verticalAlignment: Text.AlignVCenter
anchors.fill: parent
wrapMode: Text.WordWrap
text:
"<font color=\"red\">Warning: </font>" +
"This is going to spin up the motor. Make " +
"sure that nothing is in the way."
}
onAccepted: {
mCommands.measureLinkage(currentBox.realValue, erpmBox.realValue, dutyBox.realValue, res)
}
}
Connections {
target: mCommands
onMotorRLReceived: {
if (r < 1e-9 && l < 1e-9) {
VescIf.emitStatusMessage("Bad FOC Detection Result Received", false)
VescIf.emitMessageDialog("Bad Detection Result",
"Could not measure the motor resistance and inductance.",
false, false)
} else {
VescIf.emitStatusMessage("FOC Detection Result Received", true)
res = r
ind = l * 1e-6
calcKpKi()
}
}
onMotorLinkageReceived: {
if (flux_linkage < 1e-9) {
VescIf.emitStatusMessage("Bad FOC Detection Result Received", false)
VescIf.emitMessageDialog("Bad Detection Result",
"Could not measure the flux linkage properly. Adjust " +
"the start parameters according to the help text and try again.",
false, false)
} else {
VescIf.emitStatusMessage("FOC Detection Result Received", true)
lambda = flux_linkage
calcGain()
}
}
}
}

69
mobile/DoubleSpinBox.qml Normal file
View File

@@ -0,0 +1,69 @@
/*
Copyright 2018 Benjamin Vedder benjamin@vedder.se
This file is part of VESC Tool.
VESC Tool is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
VESC Tool is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
// Based on https://stackoverflow.com/questions/43406830/how-to-use-float-in-a-qml-spinbox
import QtQuick 2.0
import QtQuick.Controls 2.2
Item {
height: spinbox.implicitHeight
property int decimals: 2
property real realValue: 0.0
property real realFrom: 0.0
property real realTo: 100.0
property real realStepSize: 1.0
property string suffix: ""
property string prefix: ""
SpinBox{
id: spinbox
anchors.fill: parent
editable: true
// wheelEnabled: true
property real factor: Math.pow(10, decimals)
stepSize: realStepSize * factor
value: Math.round(realValue * factor)
to : realTo * factor
from : realFrom * factor
validator: DoubleValidator {
bottom: Math.min(spinbox.from, spinbox.to)*spinbox.factor
top: Math.max(spinbox.from, spinbox.to)*spinbox.factor
}
textFromValue: function(value, locale) {
return prefix + parseFloat(value * 1.0 / factor).toFixed(decimals) + suffix;
}
valueFromText: function(text, locale) {
return Math.round(parseFloat(text.replace(",", ".").
replace(suffix, "").
replace(prefix, "")) * factor)
}
onValueChanged: {
if (Math.round(realValue * factor) !== value) {
realValue = value * 1.0 / factor
}
}
}
}

214
mobile/FilePicker.qml Normal file
View File

@@ -0,0 +1,214 @@
/**
MIT License
Copyright (c) 2017 Andrey Semenov
Copyright (c) 2017 Benjamin Vedder: Added cancel button, embedded configuration colors.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
import QtQuick 2.0
import QtQuick.Controls 1.4 as OldControls
import QtQuick.Controls 2.2
import Qt.labs.folderlistmodel 2.1
import QtQuick.Layouts 1.3
import QtQuick.Window 2.0
Item {
id:picker
signal fileSelected(string fileName)
readonly property real textmargin: 8
readonly property real textSize: 10
readonly property real headerTextSize: 12
readonly property real buttonHeight: 24
readonly property real rowHeight: 36
readonly property real toolbarHeight: 48
property bool showDotAndDotDot: false
property bool showHidden: true
property bool showDirsFirst: true
property string folder: "file:///sdcard"
property string nameFilters: "*.*"
function currentFolder() {
return folderListModel.folder;
}
function isFolder(fileName) {
return folderListModel.isFolder(folderListModel.indexOf(folderListModel.folder + "/" + fileName));
}
function canMoveUp() {
return folderListModel.folder.toString() !== "file:///"
}
function onItemClick(fileName) {
if(!isFolder(fileName)) {
fileSelected(fileName)
return;
}
if(fileName === ".." && canMoveUp()) {
folderListModel.folder = folderListModel.parentFolder
} else if(fileName !== "." && fileName !== "..") {
if(folderListModel.folder.toString() === "file:///") {
folderListModel.folder += fileName
} else {
folderListModel.folder += "/" + fileName
}
}
}
Rectangle {
id: toolbar
anchors.right: parent.right
anchors.left: parent.left
anchors.top: parent.top
height: toolbarHeight
color: "#3c3c3c"
Button {
id: button
text: ".."
anchors.right: parent.right
anchors.rightMargin: buttonHeight
anchors.bottom: parent.bottom
anchors.top: parent.top
enabled: canMoveUp()
flat: true
onClicked: {
if(canMoveUp) {
folderListModel.folder = folderListModel.parentFolder
}
}
}
Text {
id: filePath
color: "white"
text: folderListModel.folder.toString().replace("file:///", "►").replace("/", "►").replace("/", "►").replace("/", "►").replace("/", "►")
renderType: Text.NativeRendering
elide: Text.ElideMiddle
anchors.right: button.left
font.italic: true
font.bold: true
verticalAlignment: Text.AlignVCenter
anchors.left: parent.left
anchors.leftMargin: buttonHeight
anchors.bottom: parent.bottom
anchors.top: parent.top
font.pixelSize: textSize
}
}
FolderListModel {
id: folderListModel
showDotAndDotDot: picker.showDotAndDotDot
showHidden: picker.showHidden
showDirsFirst: picker.showDirsFirst
folder: picker.folder
nameFilters: picker.nameFilters
}
ColumnLayout {
anchors.top: toolbar.bottom
anchors.right: parent.right
anchors.bottom: parent.bottom
anchors.left: parent.left
OldControls.TableView {
id: view
Layout.fillHeight: true
Layout.fillWidth: true
model: folderListModel
headerDelegate:headerDelegate
rowDelegate: Rectangle {
height: rowHeight
color: "#6f6f6f"
}
OldControls.TableViewColumn {
title: qsTr("FileName")
role: "fileName"
resizable: true
delegate: fileDelegate
}
Component {
id: fileDelegate
Item {
height: rowHeight
Rectangle {
color: "#6f6f6f"
anchors.fill: parent
MouseArea {
anchors.fill: parent
onClicked: {
onItemClick(fileNameText.text)
}
}
Text {
id: fileNameText
color: "white"
height: width
anchors.left: image.right
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.right: parent.right
text: styleData.value !== undefined ? styleData.value : ""
verticalAlignment: Text.AlignVCenter
}
Image {
id: image
height: buttonHeight
width: height
anchors.left: parent.left
anchors.leftMargin: textmargin
anchors.verticalCenter: parent.verticalCenter
source: isFolder(fileNameText.text) ? "qrc:/res/icons/ic_folder_open_black_48dp.png" :
"qrc:/res/icons/ic_insert_drive_file_black_48dp.png"
}
}
}
}
Component {
id: headerDelegate
Rectangle {
height: rowHeight
color: "#535353"
Text {
anchors.verticalCenter: parent.verticalCenter
anchors.horizontalCenter: parent.horizontalCenter
height: headerTextSize
font.bold: true
elide: Text.ElideMiddle
color: "white"
text: styleData.value !== undefined ? styleData.value : ""
}
}
}
}
Button {
Layout.fillWidth: true
text: "Cancel"
Layout.margins: 10
Layout.topMargin: -5
Layout.bottomMargin: 0
onClicked: {
picker.enabled = false
picker.visible = false
}
}
}
}

601
mobile/FwUpdate.qml Normal file
View File

@@ -0,0 +1,601 @@
/*
Copyright 2017 Benjamin Vedder benjamin@vedder.se
This file is part of VESC Tool.
VESC Tool is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
VESC Tool is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import QtQuick 2.7
import QtQuick.Controls 2.2
import QtQuick.Layouts 1.3
import Ennoid.bmsinterface 1.0
import Ennoid.commands 1.0
import Ennoid.configparams 1.0
import Ennoid.fwhelper 1.0
import Ennoid.utility 1.0
Item {
property Commands mCommands: VescIf.commands()
property ConfigParams mInfoConf: VescIf.infoConfig()
FwHelper {
id: fwHelper
}
ColumnLayout {
anchors.fill: parent
spacing: 0
RowLayout {
Layout.fillHeight: true
Layout.fillWidth: true
spacing: 0
Rectangle {
color: "#4f4f4f"
width: 16
Layout.fillHeight: true
Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter
PageIndicator {
id: indicator
count: swipeView.count
currentIndex: swipeView.currentIndex
Layout.preferredWidth: 15
Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter
rotation: 90
}
}
SwipeView {
id: swipeView
enabled: true
clip: true
Layout.fillWidth: true
Layout.fillHeight: true
Layout.rightMargin: 15
orientation: Qt.Vertical
Page {
ColumnLayout {
anchors.fill: parent
anchors.leftMargin: 10
anchors.rightMargin: 10
Rectangle {
Layout.fillWidth: true
height: 30;
border.width: 0
gradient: Gradient {
GradientStop {
position: 0.00;
color: "#002dcbff";
}
GradientStop {
position: 0.3;
color: "#80014cb2";
}
GradientStop {
position: 0.7;
color: "#80014cb2";
}
GradientStop {
position: 1.00;
color: "#000dc3ff";
}
}
border.color: "#00000000"
Text {
anchors.centerIn: parent
color: "white"
text: "Included Files"
font.bold: true
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
}
}
Item {
// Spacer
Layout.fillWidth: true
Layout.fillHeight: true
}
Text {
color: "white"
Layout.fillWidth: true
height: 30;
text: "Hardware"
horizontalAlignment: Text.AlignHCenter
}
ComboBox {
id: hwBox
Layout.preferredHeight: 48
Layout.fillWidth: true
Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter
textRole: "key"
model: ListModel {
id: hwItems
}
Component.onCompleted: {
updateHw("")
}
onCurrentIndexChanged: {
if (hwItems.rowCount() === 0) {
return
}
var fws = fwHelper.getFirmwares(hwItems.get(hwBox.currentIndex).value)
fwItems.clear()
for (var name in fws) {
if (name.toLowerCase().indexOf("ENNOID-BMS.bin") !== -1) {
fwItems.insert(0, { key: name, value: fws[name] })
} else {
fwItems.append({ key: name, value: fws[name] })
}
}
fwBox.currentIndex = 0
}
}
Text {
color: "white"
Layout.fillWidth: true
height: 30;
text: "Firmware"
horizontalAlignment: Text.AlignHCenter
}
ComboBox {
id: fwBox
Layout.preferredHeight: 48
Layout.fillWidth: true
Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter
textRole: "key"
model: ListModel {
id: fwItems
}
}
Button {
text: "Show Changelog"
Layout.fillWidth: true
onClicked: {
VescIf.emitMessageDialog(
"Firmware Changelog",
Utility.fwChangeLog(),
true)
}
}
Item {
// Spacer
Layout.fillWidth: true
Layout.fillHeight: true
}
}
}
Page {
ColumnLayout {
anchors.fill: parent
anchors.leftMargin: 10
anchors.rightMargin: 10
Rectangle {
Layout.fillWidth: true
height: 30;
border.width: 0
gradient: Gradient {
GradientStop {
position: 0.00;
color: "#002dcbff";
}
GradientStop {
position: 0.3;
color: "#80014cb2";
}
GradientStop {
position: 0.7;
color: "#80014cb2";
}
GradientStop {
position: 1;
color: "#000dc3ff";
}
}
border.color: "#00000000"
Text {
anchors.centerIn: parent
color: "white"
text: "Custom File"
font.bold: true
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
}
}
Item {
// Spacer
Layout.fillWidth: true
Layout.fillHeight: true
}
TextInput {
color: "white"
id: customFwText
Layout.fillWidth: true
}
Button {
text: "Choose File..."
Layout.fillWidth: true
onClicked: {
if (Utility.requestFilePermission()) {
filePicker.enabled = true
filePicker.visible = true
} else {
VescIf.emitMessageDialog(
"File Permissions",
"Unable to request file system permission.",
false, false)
}
}
}
Item {
// Spacer
Layout.fillWidth: true
Layout.fillHeight: true
}
}
FilePicker {
id: filePicker
anchors.fill: parent
showDotAndDotDot: true
nameFilters: "*.bin"
visible: false
enabled: false
onFileSelected: {
customFwText.text = currentFolder() + "/" + fileName
visible = false
enabled = false
}
}
}
Page {
ColumnLayout {
anchors.fill: parent
anchors.leftMargin: 10
anchors.rightMargin: 10
Rectangle {
Layout.fillWidth: true
height: 30;
border.width: 0
gradient: Gradient {
GradientStop {
position: 0.00;
color: "#002dcbff";
}
GradientStop {
position: 0.3;
color: "#80014cb2";
}
GradientStop {
position: 0.7;
color: "#80014cb2";
}
GradientStop {
position: 1.00;
color: "#000dc3ff"
}
}
border.color: "#00000000"
Text {
anchors.centerIn: parent
color: "white"
text: "Bootloader"
font.bold: true
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
}
}
Item {
// Spacer
Layout.fillWidth: true
Layout.fillHeight: true
}
Text {
color: "white"
Layout.fillWidth: true
height: 30;
text: "Hardware"
horizontalAlignment: Text.AlignHCenter
}
ComboBox {
id: blBox
Layout.preferredHeight: 48
Layout.fillWidth: true
Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter
textRole: "key"
model: ListModel {
id: blItems
}
Component.onCompleted: {
updateBl("")
}
}
Item {
// Spacer
Layout.fillWidth: true
Layout.fillHeight: true
}
}
}
}
}
Rectangle {
Layout.fillWidth: true
height: asd.implicitHeight + 20
color: "#414141"
ColumnLayout {
id: asd
anchors.fill: parent
anchors.margins: 10
Text {
Layout.fillWidth: true
color: "white"
id: uploadText
text: qsTr("Not Uploading")
horizontalAlignment: Text.AlignHCenter
}
ProgressBar {
id: uploadProgress
Layout.fillWidth: true
}
RowLayout {
Layout.fillWidth: true
Button {
id: uploadButton
text: qsTr("Upload")
Layout.fillWidth: true
onClicked: {
if (!VescIf.isPortConnected()) {
VescIf.emitMessageDialog(
"Connection Error",
"The BMS is not connected. Please open a connection.",
false)
return
}
if (swipeView.currentIndex == 0) {
if (fwItems.rowCount() === 0) {
VescIf.emitMessageDialog(
"Upload Error",
"This version of ENNOID-BMS Tool does not include any firmware " +
"for your hardware version. You can either " +
"upload a custom file or look for a later version of ENNOID-BMS " +
"Tool that might support your hardware.",
false)
return;
}
if (hwItems.rowCount() === 1) {
uploadDialog.title = "Warning"
uploadDialogLabel.text =
"Uploading new firmware will clear all settings on your ENNOID-BMS " +
"and you have to do the configuration again. Do you want to " +
"continue?"
uploadDialog.open()
} else {
uploadDialog.title = "Warning"
uploadDialogLabel.text =
"Uploading firmware for the wrong hardware version " +
"WILL damage the ENNOID-BMS for sure. Are you sure that you have " +
"chosen the correct hardware version?"
uploadDialog.open()
}
} else if (swipeView.currentIndex == 1) {
if (customFwText.text.length > 0) {
uploadDialog.title = "Warning"
uploadDialogLabel.text =
"Uploading firmware for the wrong hardware version " +
"WILL damage the ENNOID-BMS for sure. Are you sure that you have " +
"chosen the correct hardware version?"
uploadDialog.open()
} else {
VescIf.emitMessageDialog(
"Error",
"Please select a file",
false, false)
}
} else if (swipeView.currentIndex == 2) {
if (blItems.rowCount() === 0) {
VescIf.emitMessageDialog(
"Upload Error",
"This version of ENNOID-BMS Tool does not include any bootloader " +
"for your hardware version.",
false)
return;
}
uploadDialog.title = "Warning"
uploadDialogLabel.text =
"This will attempt to upload a bootloader to the connected VESC. " +
"If the connected ENNOID-BMS already has a bootloader this will destroy " +
"the bootloader and firmware updates cannot be done anymore. Do " +
"you want to continue?"
uploadDialog.open()
}
}
}
Button {
id: cancelButton
text: qsTr("Cancel")
Layout.fillWidth: true
enabled: false
onClicked: {
mCommands.cancelFirmwareUpload()
}
}
}
Text {
Layout.fillWidth: true
id: versionText
color: "#e0e0e0"
text:
"FW : \n" +
"HW : \n" +
"UUID : "
font.family: "DejaVu Sans Mono"
verticalAlignment: Text.AlignVCenter
}
}
}
}
Dialog {
id: uploadDialog
standardButtons: Dialog.Ok | Dialog.Cancel
modal: true
focus: true
width: parent.width - 20
closePolicy: Popup.CloseOnEscape
x: (parent.width - width) / 2
y: (parent.height - height) / 2
Text {
color: "#ffffff"
id: uploadDialogLabel
verticalAlignment: Text.AlignVCenter
anchors.fill: parent
wrapMode: Text.WordWrap
}
onAccepted: {
if (swipeView.currentIndex == 0) {
fwHelper.uploadFirmware(fwItems.get(fwBox.currentIndex).value, VescIf, false, false)
} else if (swipeView.currentIndex == 1) {
fwHelper.uploadFirmware(customFwText.text, VescIf, false, true)
} else if (swipeView.currentIndex == 2) {
fwHelper.uploadFirmware(blItems.get(blBox.currentIndex).value, VescIf, true, false)
}
}
}
function updateHw(hw) {
var hws = fwHelper.getHardwares(hw)
hwItems.clear()
for (var name in hws) {
if (name.indexOf("412") !== -1) {
hwItems.insert(0, { key: name, value: hws[name] })
} else {
hwItems.append({ key: name, value: hws[name] })
}
}
hwBox.currentIndex = 0
}
function updateBl(hw) {
var bls = fwHelper.getBootloaders(hw)
blItems.clear()
for (var name in bls) {
if (name.indexOf("412") !== -1) {
blItems.insert(0, { key: name, value: bls[name] })
} else {
blItems.append({ key: name, value: bls[name] })
}
}
blBox.currentIndex = 0
}
Connections {
target: VescIf
onFwUploadStatus: {
if (isOngoing) {
uploadText.text = status + " (" + parseFloat(progress * 100.0).toFixed(2) + " %)"
} else {
uploadText.text = status
}
uploadProgress.value = progress
uploadButton.enabled = !isOngoing
cancelButton.enabled = isOngoing
}
}
Connections {
target: mCommands
onFwVersionReceived: {
updateHw(hw)
updateBl(hw)
versionText.text =
"FW : " + major + "." + minor + "\n" +
"HW : " + hw + "\n" +
"UUID : " + Utility.uuid2Str(uuid, false)
}
}
}

173
mobile/NrfPair.qml Normal file
View File

@@ -0,0 +1,173 @@
/*
Copyright 2018 Benjamin Vedder benjamin@vedder.se
This file is part of VESC Tool.
VESC Tool is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
VESC Tool is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import QtQuick 2.7
import QtQuick.Controls 2.2
import QtQuick.Layouts 1.3
import Ennoid.bmsinterface 1.0
import Ennoid.commands 1.0
import Ennoid.configparams 1.0
Item {
property int parentWidth: 10
property real pairCnt: 0.0
property Commands mCommands: VescIf.commands()
property ConfigParams mInfoConf: VescIf.infoConfig()
function openDialog() {
dialog.open()
}
Dialog {
id: dialog
standardButtons: Dialog.Close
modal: true
focus: true
width: parentWidth - 20
height: Math.min(implicitHeight, column.height - 40)
closePolicy: Popup.CloseOnEscape
x: 10
y: 10
ScrollView {
anchors.fill: parent
clip: true
contentWidth: parent.width
ColumnLayout {
anchors.fill: parent
spacing: 0
DoubleSpinBox {
id: timeBox
Layout.fillWidth: true
realFrom: 1.0
realTo: 30.0
realValue: 10.0
decimals: 1
prefix: "Time: "
suffix: " s"
}
ProgressBar {
id: cntBar
Layout.fillWidth: true
Layout.bottomMargin: 5
from: 0.0
to: 1.0
value: 0.0
}
RowLayout {
Layout.fillWidth: true
Button {
text: "Help"
Layout.preferredWidth: 50
Layout.fillWidth: true
onClicked: {
VescIf.emitMessageDialog(
mInfoConf.getLongName("help_nrf_pair"),
mInfoConf.getDescription("help_nrf_pair"),
true, true)
}
}
Button {
id: startButton
text: "Start"
Layout.preferredWidth: 50
Layout.fillWidth: true
onClicked: {
mCommands.pairNrf(timeBox.realValue * 1000.0)
}
}
}
}
}
}
Timer {
id: cntTimer
interval: 100
running: true
repeat: true
onTriggered: {
if (pairCnt > 0.01) {
pairCnt -= 0.1
if (pairCnt <= 0.01) {
startButton.enabled = true
pairCnt = 0.0
}
cntBar.value = pairCnt / timeBox.realValue
}
}
}
Connections {
target: mCommands
onNrfPairingRes: {
if (!dialog.visible) {
return
}
switch (res) {
case 0:
pairCnt = timeBox.realValue
cntBar.value = 1
startButton.enabled = false
break;
case 1:
startButton.enabled = true
pairCnt = 0.0
cntBar.value = 0
VescIf.emitStatusMessage("Pairing NRF Sucessful", true)
VescIf.emitMessageDialog(
"NRF Pairing",
"Pairing was successful.",
true, false)
break;
case 2:
startButton.enabled = true
pairCnt = 0.0
cntBar.value = 0
VescIf.emitStatusMessage("Pairing NRF Timed Out", false)
VescIf.emitMessageDialog(
"NRF Pairing",
"Pairing timed out. Make sure to put your device (e.g. NRF nunchuk) " +
"in pairing mode before the time runs out." +
"<br><br>" +
"To put the NRF nunchuk in " +
"pairing mode, just switch it on using any of the buttons. Then it " +
"will enter pairing mode if it was switched off previously.",
false, false)
break;
default:
break;
}
}
}
}

358
mobile/PairingDialog.qml Normal file
View File

@@ -0,0 +1,358 @@
/*
Copyright 2019 Benjamin Vedder benjamin@vedder.se
This file is part of VESC Tool.
VESC Tool is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
VESC Tool is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import QtQuick 2.7
import QtQuick.Controls 2.2
import QtQuick.Layouts 1.3
import Ennoid.bmsinterface 1.0
import Ennoid.commands 1.0
import Ennoid.configparams 1.0
import Ennoid.utility 1.0
Item {
function openDialog() {
dialog.open()
loadUuids()
}
function loadUuids() {
pairModel.clear()
var uuids = VescIf.getPairedUuids()
for (var i = 0;i < uuids.length;i++) {
pairModel.append({"uuid": uuids[i]})
}
}
Dialog {
//property ConfigParams mAppConf: VescIf.appConfig()
property Commands mCommands: VescIf.commands()
id: dialog
modal: true
focus: true
width: parent.width - 20
height: parent.height - 60
closePolicy: Popup.CloseOnEscape
x: 10
y: 50
parent: ApplicationWindow.overlay
padding: 10
ColumnLayout {
anchors.fill: parent
Text {
id: text
Layout.fillWidth: true
color: "white"
text: qsTr("These are the BMS paired to this instance of BMS Tool.")
font.bold: true
horizontalAlignment: Text.AlignHCenter
wrapMode: Text.WordWrap
}
ListModel {
id: pairModel
}
ListView {
id: pairList
Layout.fillWidth: true
Layout.fillHeight: true
focus: true
clip: true
spacing: 5
Component {
id: pairDelegate
Rectangle {
property variant modelData: model
width: pairList.width
height: 60
color: "#30000000"
radius: 5
RowLayout {
anchors.fill: parent
spacing: 10
Image {
id: image
fillMode: Image.PreserveAspectFit
Layout.preferredWidth: 40
Layout.preferredHeight: 40
Layout.alignment: Qt.AlignVCenter
Layout.leftMargin: 10
source: "qrc:/res/icon.png"
}
Text {
Layout.fillWidth: true
color: "white"
text: uuid
wrapMode: Text.Wrap
}
Button {
Layout.alignment: Qt.AlignVCenter
Layout.rightMargin: 10
text: "Delete"
onClicked: {
deleteDialog.open()
}
Dialog {
id: deleteDialog
property int indexNow: 0
standardButtons: Dialog.Ok | Dialog.Cancel
modal: true
focus: true
width: parent.width - 20
closePolicy: Popup.CloseOnEscape
title: "Delete paired BMS"
x: 10
y: 10 + parent.height / 2 - height / 2
parent: ApplicationWindow.overlay
Text {
color: "#ffffff"
verticalAlignment: Text.AlignVCenter
anchors.fill: parent
wrapMode: Text.WordWrap
text: "This is going to delete this BMS from the paired list. If that BMS " +
"has the pairing flag set you won't be able to connect to it over BLE " +
"any more. Are you sure?"
}
onAccepted: {
VescIf.deletePairedUuid(uuid)
VescIf.storeSettings()
}
}
}
}
}
}
model: pairModel
delegate: pairDelegate
}
RowLayout {
Layout.fillWidth: true
Button {
Layout.preferredWidth: 50
Layout.fillWidth: true
text: "..."
onClicked: menu.open()
Menu {
id: menu
width: 500
MenuItem {
text: "Add current without pairing"
onTriggered: {
if (VescIf.isPortConnected()) {
VescIf.addPairedUuid(VescIf.getConnectedUuid());
VescIf.storeSettings()
} else {
VescIf.emitMessageDialog("Add UUID",
"You are not connected to the BMS. Connect in order to add it.",
false, false)
}
}
}
MenuItem {
text: "Add from UUID"
onTriggered: {
uuidEnter.open()
}
}
MenuItem {
text: "Unpair connected"
onTriggered: {
if (VescIf.isPortConnected()) {
if (mCommands.isLimitedMode()) {
VescIf.emitMessageDialog("Unpair BMS",
"The fiwmare must be updated to unpair this BMS.",
false, false)
} else {
unpairConnectedDialog.open()
}
} else {
VescIf.emitMessageDialog("Unpair BMS",
"You are not connected to the BMS. Connect in order to unpair it.",
false, false)
}
}
}
}
}
Button {
id: pairConnectedButton
text: "Pair BMS"
Layout.fillWidth: true
onClicked: {
if (VescIf.isPortConnected()) {
if (mCommands.isLimitedMode()) {
VescIf.emitMessageDialog("Pair BMS",
"The fiwmare must be updated to pair this BMS.",
false, false)
} else {
pairConnectedDialog.open()
}
} else {
VescIf.emitMessageDialog("Pair BMS",
"You are not connected to the BMS. Connect in order to pair it.",
false, false)
}
}
}
Button {
text: "Close"
Layout.fillWidth: true
onClicked: {
dialog.close()
}
}
}
}
}
Dialog {
id: pairConnectedDialog
property int indexNow: 0
standardButtons: Dialog.Ok | Dialog.Cancel
modal: true
focus: true
width: parent.width - 20
closePolicy: Popup.CloseOnEscape
title: "Pair connected BMS"
x: 10
y: 10 + Math.max((parent.height - height) / 2, 10)
parent: ApplicationWindow.overlay
Text {
color: "#ffffff"
verticalAlignment: Text.AlignVCenter
anchors.fill: parent
wrapMode: Text.WordWrap
text: "This is going to pair the connected BMS with this instance of BMS Tool. BMS Tool instances " +
"that are not paired with this BMS will not be able to connect over bluetooth any more. Continue?"
}
onAccepted: {
VescIf.addPairedUuid(VescIf.getConnectedUuid());
VescIf.storeSettings()
mAppConf.updateParamBool("pairing_done", true, 0)
mCommands.setAppConf()
if (Utility.waitSignal(mCommands, "2ackReceived(QString)", 2000)) {
VescIf.emitMessageDialog("Pairing Successful!",
"Pairing is done! Please note the UUID if this BMS (or take a screenshot) in order " +
"to add it to BMS Tool instances that are not paired in the future. The UUID is:\n" +
VescIf.getConnectedUuid(),
true, false)
}
}
}
Dialog {
id: unpairConnectedDialog
property int indexNow: 0
standardButtons: Dialog.Ok | Dialog.Cancel
modal: true
focus: true
width: parent.width - 20
closePolicy: Popup.CloseOnEscape
title: "Unpair connected BMS"
x: 10
y: 10 + parent.height / 2 - height / 2
parent: ApplicationWindow.overlay
Text {
color: "#ffffff"
verticalAlignment: Text.AlignVCenter
anchors.fill: parent
wrapMode: Text.WordWrap
text: "This is going to unpair the connected BMS. Continue?"
}
onAccepted: {
VescIf.deletePairedUuid(VescIf.getConnectedUuid());
VescIf.storeSettings()
mAppConf.updateParamBool("pairing_done", false, 0)
mCommands.setAppConf()
}
}
Dialog {
id: uuidEnter
standardButtons: Dialog.Ok | Dialog.Cancel
modal: true
focus: true
title: "Add UUID"
width: parent.width - 20
height: 200
closePolicy: Popup.CloseOnEscape
x: 10
y: parent.height / 2 - height / 2
parent: ApplicationWindow.overlay
Rectangle {
anchors.fill: parent
height: 20
border.width: 2
border.color: "#8d8d8d"
color: "#33a8a8a8"
radius: 3
TextInput {
id: stringInput
color: "#ffffff"
anchors.fill: parent
anchors.margins: 7
font.pointSize: 12
focus: true
}
}
onAccepted: {
if (stringInput.text.length > 0) {
VescIf.addPairedUuid(stringInput.text)
}
}
}
Connections {
target: VescIf
onPairingListUpdated: {
loadUuids()
}
}
}

139
mobile/ParamEditBool.qml Normal file
View File

@@ -0,0 +1,139 @@
/*
Copyright 2018 Benjamin Vedder benjamin@vedder.se
This file is part of VESC Tool.
VESC Tool is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
VESC Tool is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import QtQuick 2.0
import QtQuick.Controls 2.2
import QtQuick.Layouts 1.3
import Ennoid.bmsinterface 1.0
import Ennoid.configparams 1.0
Item {
id: editor
property string paramName: ""
property ConfigParams params: null
height: 140
Layout.fillWidth: true
property real maxVal: 1.0
Component.onCompleted: {
if (params !== null) {
nameText.text = params.getLongName(paramName)
boolSwitch.checked = params.getParamBool(paramName)
if (params.getParamTransmittable(paramName)) {
nowButton.visible = true
defaultButton.visible = true
} else {
nowButton.visible = false
defaultButton.visible = false
}
}
}
Rectangle {
id: rect
anchors.fill: parent
color: "#4c5a5a5a"
radius: 5
border.color: "#919191"
border.width: 2
ColumnLayout {
id: column
anchors.fill: parent
anchors.topMargin: 10
anchors.margins: 5
Text {
id: nameText
color: "white"
text: paramName
horizontalAlignment: Text.AlignHCenter
Layout.fillWidth: true
font.pointSize: 12
}
Switch {
id: boolSwitch
Layout.fillWidth: true
onCheckedChanged: {
if (params !== null) {
if (params.getUpdateOnly() !== paramName) {
params.setUpdateOnly("")
}
params.updateParamBool(paramName, checked, editor);
}
}
}
RowLayout {
Layout.fillWidth: true
Button {
id: nowButton
Layout.fillWidth: true
Layout.preferredWidth: 500
flat: true
text: "Current"
onClicked: {
params.setUpdateOnly(paramName)
params.requestUpdate()
}
}
Button {
id: defaultButton
Layout.fillWidth: true
Layout.preferredWidth: 500
flat: true
text: "Default"
onClicked: {
params.setUpdateOnly(paramName)
params.requestUpdateDefault()
}
}
Button {
id: helpButton
Layout.fillWidth: true
Layout.preferredWidth: 500
flat: true
text: "Help"
onClicked: {
VescIf.emitMessageDialog(
params.getLongName(paramName),
params.getDescription(paramName),
true, true)
}
}
}
}
}
Connections {
target: params
onParamChangedBool: {
if (src !== editor && name == paramName) {
boolSwitch.checked = newParam
}
}
}
}

200
mobile/ParamEditDouble.qml Normal file
View File

@@ -0,0 +1,200 @@
/*
Copyright 2018 Benjamin Vedder benjamin@vedder.se
This file is part of VESC Tool.
VESC Tool is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
VESC Tool is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import QtQuick 2.0
import QtQuick.Controls 2.2
import QtQuick.Layouts 1.3
import Ennoid.bmsinterface 1.0
import Ennoid.configparams 1.0
Item {
id: editor
property string paramName: ""
property ConfigParams params: null
height: 140
Layout.fillWidth: true
property real maxVal: 1.0
property bool createReady: false
Component.onCompleted: {
if (params !== null) {
if (Math.abs(params.getParamMaxDouble(paramName)) > params.getParamMinDouble(paramName)) {
maxVal = Math.abs(params.getParamMaxDouble(paramName))
} else {
maxVal = Math.abs(params.getParamMinDouble(paramName))
}
nameText.text = params.getLongName(paramName)
valueBox.decimals = params.getParamDecimalsDouble(paramName)
valueBox.realFrom = params.getParamMinDouble(paramName) * params.getParamEditorScale(paramName)
valueBox.realTo = params.getParamMaxDouble(paramName) * params.getParamEditorScale(paramName)
valueBox.realValue = params.getParamDouble(paramName) * params.getParamEditorScale(paramName)
valueBox.realStepSize = params.getParamStepDouble(paramName)
valueBox.visible = !params.getParamEditAsPercentage(paramName)
valueBox.suffix = params.getParamSuffix(paramName)
var p = (params.getParamDouble(paramName) * 100.0) / maxVal
percentageBox.from = (100.0 * params.getParamMinDouble(paramName)) / maxVal
percentageBox.to = (100.0 * params.getParamMaxDouble(paramName)) / maxVal
percentageBox.value = p
percentageBox.visible = params.getParamEditAsPercentage(paramName)
if (params.getParamTransmittable(paramName)) {
nowButton.visible = true
defaultButton.visible = true
} else {
nowButton.visible = false
defaultButton.visible = false
}
createReady = true
}
}
function updateDisplay(value) {
// TODO: No display for now...
}
Rectangle {
id: rect
anchors.fill: parent
color: "#4c5a5a5a"
radius: 5
border.color: "#919191"
border.width: 2
ColumnLayout {
id: column
anchors.fill: parent
anchors.topMargin: 10
anchors.margins: 5
Text {
id: nameText
color: "white"
text: paramName
horizontalAlignment: Text.AlignHCenter
Layout.fillWidth: true
font.pointSize: 12
}
DoubleSpinBox {
id: valueBox
Layout.fillWidth: true
onRealValueChanged: {
if (!params.getParamEditAsPercentage(paramName)) {
var val = realValue / params.getParamEditorScale(paramName)
if (params !== null && createReady) {
if (params.getUpdateOnly() !== paramName) {
params.setUpdateOnly("")
}
params.updateParamDouble(paramName, val, editor);
}
updateDisplay(val);
}
}
}
SpinBox {
id: percentageBox
Layout.fillWidth: true
editable: true
onValueChanged: {
if (params.getParamEditAsPercentage(paramName)) {
var val = (value / 100.0) * maxVal
if (params !== null && createReady) {
if (params.getUpdateOnly() !== paramName) {
params.setUpdateOnly("")
}
params.updateParamDouble(paramName, val, editor);
}
updateDisplay(val);
}
}
textFromValue: function(value, locale) {
return Number(value).toLocaleString(locale, 'f', 0) + " %"
}
valueFromText: function(text, locale) {
return Number.fromLocaleString(locale, text.replace("%", ""))
}
}
RowLayout {
Layout.fillWidth: true
Button {
id: nowButton
Layout.fillWidth: true
Layout.preferredWidth: 500
flat: true
text: "Current"
onClicked: {
params.setUpdateOnly(paramName)
params.requestUpdate()
}
}
Button {
id: defaultButton
Layout.fillWidth: true
Layout.preferredWidth: 500
flat: true
text: "Default"
onClicked: {
params.setUpdateOnly(paramName)
params.requestUpdateDefault()
}
}
Button {
id: helpButton
Layout.fillWidth: true
Layout.preferredWidth: 500
flat: true
text: "Help"
onClicked: {
VescIf.emitMessageDialog(
params.getLongName(paramName),
params.getDescription(paramName),
true, true)
}
}
}
}
}
Connections {
target: params
onParamChangedDouble: {
if (src !== editor && name == paramName) {
valueBox.realValue = newParam * params.getParamEditorScale(paramName)
percentageBox.value = Math.round((100.0 * newParam) / maxVal)
}
}
}
}

151
mobile/ParamEditEnum.qml Normal file
View File

@@ -0,0 +1,151 @@
/*
Copyright 2018 Benjamin Vedder benjamin@vedder.se
This file is part of VESC Tool.
VESC Tool is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
VESC Tool is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import QtQuick 2.0
import QtQuick.Controls 2.2
import QtQuick.Layouts 1.3
import Ennoid.bmsinterface 1.0
import Ennoid.configparams 1.0
Item {
id: editor
property string paramName: ""
property ConfigParams params: null
height: 140
Layout.fillWidth: true
property real maxVal: 1.0
property bool createReady: false
Component.onCompleted: {
if (params !== null) {
nameText.text = params.getLongName(paramName)
enumBox.model = params.getParamEnumNames(paramName)
enumBox.currentIndex = params.getParamEnum(paramName)
if (params.getParamTransmittable(paramName)) {
nowButton.visible = true
defaultButton.visible = true
} else {
nowButton.visible = false
defaultButton.visible = false
}
createReady = true
}
}
Rectangle {
id: rect
anchors.fill: parent
color: "#4c5a5a5a"
radius: 5
border.color: "#919191"
border.width: 2
ColumnLayout {
id: column
anchors.fill: parent
anchors.bottomMargin: 2
anchors.margins: 10
Text {
id: nameText
color: "white"
text: paramName
horizontalAlignment: Text.AlignHCenter
Layout.fillWidth: true
font.pointSize: 12
}
ComboBox {
id: enumBox
Layout.fillWidth: true
background: Rectangle {
implicitHeight: 35
color: enumBox.pressed ? "#606060" : "#505050"
border.color: enumBox.hovered ? "#81D4FA" : "#000dc3ff"
border.width: enumBox.visualFocus ? 2 : 2
radius: 5
}
onCurrentIndexChanged: {
if (params !== null && createReady) {
if (params.getUpdateOnly() !== paramName) {
params.setUpdateOnly("")
}
params.updateParamEnum(paramName, currentIndex, editor);
}
}
}
RowLayout {
Layout.fillWidth: true
Button {
id: nowButton
Layout.fillWidth: true
Layout.preferredWidth: 500
flat: true
text: "Current"
onClicked: {
params.setUpdateOnly(paramName)
params.requestUpdate()
}
}
Button {
id: defaultButton
Layout.fillWidth: true
Layout.preferredWidth: 500
flat: true
text: "Default"
onClicked: {
params.setUpdateOnly(paramName)
params.requestUpdateDefault()
}
}
Button {
id: helpButton
Layout.fillWidth: true
Layout.preferredWidth: 500
flat: true
text: "Help"
onClicked: {
VescIf.emitMessageDialog(
params.getLongName(paramName),
params.getDescription(paramName),
true, true)
}
}
}
}
}
Connections {
target: params
onParamChangedEnum: {
if (src !== editor && name == paramName) {
enumBox.currentIndex = newParam
}
}
}
}

210
mobile/ParamEditInt.qml Normal file
View File

@@ -0,0 +1,210 @@
/*
Copyright 2018 Benjamin Vedder benjamin@vedder.se
This file is part of VESC Tool.
VESC Tool is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
VESC Tool is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import QtQuick 2.0
import QtQuick.Controls 2.2
import QtQuick.Layouts 1.3
import Ennoid.bmsinterface 1.0
import Ennoid.configparams 1.0
Item {
id: editor
property string paramName: ""
property ConfigParams params: null
height: 140
Layout.fillWidth: true
property real maxVal: 1.0
property bool createReady: false
Component.onCompleted: {
if (params !== null) {
if (Math.abs(params.getParamMaxInt(paramName)) > params.getParamMinInt(paramName)) {
maxVal = Math.abs(params.getParamMaxInt(paramName))
} else {
maxVal = Math.abs(params.getParamMinInt(paramName))
}
nameText.text = params.getLongName(paramName)
valueBox.from = params.getParamMinInt(paramName) * params.getParamEditorScale(paramName)
valueBox.to = params.getParamMaxInt(paramName) * params.getParamEditorScale(paramName)
valueBox.value = params.getParamInt(paramName) * params.getParamEditorScale(paramName)
valueBox.stepSize = params.getParamStepInt(paramName)
valueBox.visible = !params.getParamEditAsPercentage(paramName)
valueBox.suffix = params.getParamSuffix(paramName)
var p = (params.getParamInt(paramName) * 100.0) / maxVal
percentageBox.from = (100.0 * params.getParamMinInt(paramName)) / maxVal
percentageBox.to = (100.0 * params.getParamMaxInt(paramName)) / maxVal
percentageBox.value = p
percentageBox.visible = params.getParamEditAsPercentage(paramName)
if (params.getParamTransmittable(paramName)) {
nowButton.visible = true
defaultButton.visible = true
} else {
nowButton.visible = false
defaultButton.visible = false
}
createReady = true
}
}
function updateDisplay(value) {
// TODO: No display for now...
}
Rectangle {
id: rect
anchors.fill: parent
color: "#4c5a5a5a"
radius: 5
border.color: "#919191"
border.width: 2
ColumnLayout {
id: column
anchors.fill: parent
anchors.topMargin: 10
anchors.margins: 5
Text {
id: nameText
color: "white"
text: paramName
horizontalAlignment: Text.AlignHCenter
Layout.fillWidth: true
font.pointSize: 12
}
SpinBox {
id: valueBox
Layout.fillWidth: true
property string suffix: ""
editable: true
onValueChanged: {
if (!params.getParamEditAsPercentage(paramName)) {
var val = value / params.getParamEditorScale(paramName)
if (params !== null && createReady) {
if (params.getUpdateOnly() !== paramName) {
params.setUpdateOnly("")
}
params.updateParamInt(paramName, val, editor);
}
updateDisplay(val);
}
}
textFromValue: function(value, locale) {
return Number(value).toLocaleString(locale, 'f', 0) + suffix
}
valueFromText: function(text, locale) {
return Number.fromLocaleString(locale, text.replace(suffix, ""))
}
}
SpinBox {
id: percentageBox
Layout.fillWidth: true
editable: true
visible: false
onValueChanged: {
if (params.getParamEditAsPercentage(paramName)) {
var val = (value / 100.0) * maxVal
if (params !== null && createReady) {
if (params.getUpdateOnly() !== paramName) {
params.setUpdateOnly("")
}
params.updateParamInt(paramName, val, editor);
}
updateDisplay(val);
}
}
textFromValue: function(value, locale) {
return Number(value).toLocaleString(locale, 'f', 0) + " %"
}
valueFromText: function(text, locale) {
return Number.fromLocaleString(locale, text.replace("%", ""))
}
}
RowLayout {
Layout.fillWidth: true
Button {
id: nowButton
Layout.fillWidth: true
Layout.preferredWidth: 500
flat: true
text: "Current"
onClicked: {
params.setUpdateOnly(paramName)
params.requestUpdate()
}
}
Button {
id: defaultButton
Layout.fillWidth: true
Layout.preferredWidth: 500
flat: true
text: "Default"
onClicked: {
params.setUpdateOnly(paramName)
params.requestUpdateDefault()
}
}
Button {
id: helpButton
Layout.fillWidth: true
Layout.preferredWidth: 500
flat: true
text: "Help"
onClicked: {
VescIf.emitMessageDialog(
params.getLongName(paramName),
params.getDescription(paramName),
true, true)
}
}
}
}
}
Connections {
target: params
onParamChangedInt: {
if (src !== editor && name == paramName) {
valueBox.value = newParam * params.getParamEditorScale(paramName)
percentageBox.value = Math.round((100.0 * newParam) / maxVal)
}
}
}
}

View File

@@ -0,0 +1,48 @@
/*
Copyright 2018 Benjamin Vedder benjamin@vedder.se
This file is part of VESC Tool.
VESC Tool is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
VESC Tool is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import QtQuick 2.0
import QtQuick.Controls 2.2
import QtQuick.Layouts 1.3
Item {
Layout.fillWidth: true
height: name.implicitHeight + 8
property string sepName: ""
Rectangle {
id: rect
anchors.fill: parent
color: "#9e0000"
radius: 5
Text {
anchors.centerIn: parent
color: "white"
id: name
text: sepName
font.bold: true
font.pointSize: 12
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
}
}
}

148
mobile/ParamEditString.qml Normal file
View File

@@ -0,0 +1,148 @@
/*
Copyright 2018 Benjamin Vedder benjamin@vedder.se
This file is part of VESC Tool.
VESC Tool is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
VESC Tool is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import QtQuick 2.0
import QtQuick.Controls 2.2
import QtQuick.Layouts 1.3
import Ennoid.bmsinterface 1.0
import Ennoid.configparams 1.0
Item {
id: editor
property string paramName: ""
property ConfigParams params: null
height: column.implicitHeight + 2 * column.anchors.margins
Layout.fillWidth: true
property real maxVal: 1.0
Component.onCompleted: {
if (params !== null) {
nameText.text = params.getLongName(paramName)
stringInput.text = params.getParamQString(paramName)
if (params.getParamTransmittable(paramName)) {
nowButton.visible = true
defaultButton.visible = true
} else {
nowButton.visible = false
defaultButton.visible = false
}
}
}
Rectangle {
id: rect
anchors.fill: parent
color: "#4cbfbfbf"
radius: 10
border.color: "#4c000000"
border.width: 3
ColumnLayout {
id: column
anchors.fill: parent
anchors.margins: 10
Text {
id: nameText
text: paramName
horizontalAlignment: Text.AlignHCenter
Layout.fillWidth: true
font.pointSize: 12
}
Rectangle {
Layout.fillWidth: true
height: stringInput.implicitHeight + 14
border.width: 2
border.color: "#8d8d8d"
color: "#33a8a8a8"
radius: 3
TextInput {
id: stringInput
anchors.fill: parent
anchors.margins: 7
font.pointSize: 12
focus: true
onTextChanged: {
if (params !== null) {
if (params.getUpdateOnly() !== paramName) {
params.setUpdateOnly("")
}
params.updateParamString(paramName, text, editor);
}
}
}
}
RowLayout {
Layout.fillWidth: true
Button {
id: nowButton
Layout.fillWidth: true
Layout.preferredWidth: 500
flat: true
text: "Current"
onClicked: {
params.setUpdateOnly(paramName)
params.requestUpdate()
}
}
Button {
id: defaultButton
Layout.fillWidth: true
Layout.preferredWidth: 500
flat: true
text: "Default"
onClicked: {
params.setUpdateOnly(paramName)
params.requestUpdateDefault()
}
}
Button {
id: helpButton
Layout.fillWidth: true
Layout.preferredWidth: 500
flat: true
text: "Help"
onClicked: {
VescIf.emitMessageDialog(
params.getLongName(paramName),
params.getDescription(paramName),
true, true)
}
}
}
}
}
Connections {
target: params
onParamChangedQString: {
if (src !== editor && name == paramName) {
stringInput.text = newParam
}
}
}
}

72
mobile/ParamEditors.qml Normal file
View File

@@ -0,0 +1,72 @@
/*
Copyright 2018 Benjamin Vedder benjamin@vedder.se
This file is part of VESC Tool.
VESC Tool is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
VESC Tool is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import QtQuick 2.7
import QtQuick.Controls 2.2
import QtQuick.Layouts 1.3
import Ennoid.bmsinterface 1.0
import Ennoid.commands 1.0
import Ennoid.configparams 1.0
Item {
property ConfigParams mbmsConfig: VescIf.bmsConfig()
function createEditor(parent, name, conf) {
if (conf.hasParam(name)) {
if (conf.isParamDouble(name)) {
var component = Qt.createComponent("ParamEditDouble.qml");
return component.createObject(parent, {"params": conf, "paramName": name});
} else if (conf.isParamInt(name)) {
var component2 = Qt.createComponent("ParamEditInt.qml");
return component2.createObject(parent, {"params": conf, "paramName": name});
} else if (conf.isParamEnum(name)) {
var component3 = Qt.createComponent("ParamEditEnum.qml");
return component3.createObject(parent, {"params": conf, "paramName": name});
} else if (conf.isParamBool(name)) {
var component4 = Qt.createComponent("ParamEditBool.qml");
return component4.createObject(parent, {"params": conf, "paramName": name});
} else if (conf.isParamQString(name)) {
var component5 = Qt.createComponent("ParamEditString.qml");
return component5.createObject(parent, {"params": conf, "paramName": name});
}
} else {
console.log("Parameter " + name + " not found.")
}
return null
}
function createEditorMc(parent, name) {
return createEditor(parent, name, mbmsConfig)
}
function createSeparator(parent, text) {
var component = Qt.createComponent("ParamEditSeparator.qml");
return component.createObject(parent, {"sepName": text});
}
function createSpacer(parent) {
return Qt.createQmlObject(
'import QtQuick 2.7; import QtQuick.Layouts 1.3; Rectangle {Layout.fillHeight: true}',
parent,
"spacer1")
}
}

193
mobile/PpmMap.qml Normal file
View File

@@ -0,0 +1,193 @@
/*
Copyright 2018 Benjamin Vedder benjamin@vedder.se
This file is part of VESC Tool.
VESC Tool is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
VESC Tool is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import QtQuick 2.7
import QtQuick.Controls 2.2
import QtQuick.Layouts 1.3
import Ennoid.bmsinterface 1.0
import Ennoid.commands 1.0
import Ennoid.configparams 1.0
Item {
implicitHeight: column.implicitHeight
property real msMin: 0.0
property real msMax: 0.0
property real msCenter: 0.0
property real msNow: 0.0
property real valueNow: 0.5
property bool resetDone: true
property Commands mCommands: VescIf.commands()
property ConfigParams mInfoConf: VescIf.infoConfig()
function openDialog() {
dialog.open()
}
function updateDisplay() {
resultArea.text =
"Value : " + parseFloat(valueNow).toFixed(2) + "\n\n" +
"Now : " + parseFloat(msNow).toFixed(4) + " ms\n" +
"Min : " + parseFloat(msMin).toFixed(4) + " ms\n" +
"Max : " + parseFloat(msMax).toFixed(4) + " ms\n" +
"Center : " + parseFloat(msCenter).toFixed(4) + " ms"
valueBar.value = valueNow
}
function isValid() {
return (msMax - msMin) > 0.4
}
function applyMapping() {
if (isValid()) {
mAppConf.updateParamDouble("app_ppm_conf.pulse_start", msMin)
mAppConf.updateParamDouble("app_ppm_conf.pulse_end", msMax)
mAppConf.updateParamDouble("app_ppm_conf.pulse_center", msCenter)
VescIf.emitStatusMessage("Start, End and Center Pulselengths Applied", true)
mCommands.setAppConf()
} else {
VescIf.emitMessageDialog("Apply Mapping",
"Mapped values are not valid. Move the throttle to min, " +
"then to max and then leave it in the center.",
false,
false)
}
}
function reset() {
msMin = 0.0
msMax = 0.0
msCenter = 0.0
resetDone = true
updateDisplay()
}
Component.onCompleted: {
updateDisplay()
}
ColumnLayout {
id: column
anchors.fill: parent
spacing: 0
TextArea {
id: resultArea
Layout.fillWidth: true
Layout.preferredHeight: 200
readOnly: true
wrapMode: TextEdit.WordWrap
font.family: "DejaVu Sans Mono"
}
ProgressBar {
id: valueBar
Layout.fillWidth: true
from: -1.0
to: 1.0
value: 0.0
}
RowLayout {
Layout.fillWidth: true
Button {
text: "Help"
Layout.preferredWidth: 50
Layout.fillWidth: true
flat: true
onClicked: {
VescIf.emitMessageDialog(
mInfoConf.getLongName("app_ppm_mapping_help"),
mInfoConf.getDescription("app_ppm_mapping_help"),
true, true)
}
}
Button {
text: "Reset"
Layout.preferredWidth: 50
Layout.fillWidth: true
flat: true
onClicked: {
reset()
}
}
}
Button {
text: "Apply & Write"
Layout.fillWidth: true
onClicked: {
applyMapping()
}
}
}
Timer {
id: rtTimer
interval: 50
running: true
repeat: true
onTriggered: {
if (VescIf.isPortConnected() && visible) {
mCommands.getDecodedPpm()
}
}
}
Connections {
target: mCommands
onDecodedPpmReceived: {
valueNow = value
msNow = last_len
if (resetDone) {
resetDone = false
msMin = msNow
msMax = msNow
}
if (msNow < msMin) {
msMin = msNow
}
if (msNow > msMax) {
msMax = msNow
}
var range = msMax - msMin
var pos = msNow - msMin
if (pos > (range / 4.0) && pos < ((3.0 * range) / 4.0)) {
msCenter = msNow
} else {
msCenter = range / 2.0 + msMin
}
updateDisplay()
}
}
}

Some files were not shown because too many files have changed in this diff Show More