Initial commit (project based on widgets)
This commit is contained in:
219
mobile/AdcMap.qml
Normal file
219
mobile/AdcMap.qml
Normal 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
286
mobile/ConfigPageCell.qml
Normal 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
203
mobile/ConfigPageDisplay.qml
Normal file
203
mobile/ConfigPageDisplay.qml
Normal 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
308
mobile/ConfigPageGeneral.qml
Normal file
308
mobile/ConfigPageGeneral.qml
Normal 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
244
mobile/ConfigPageSignal.qml
Normal 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
209
mobile/ConfigPageSwitch.qml
Normal 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
393
mobile/ConnectBle.qml
Normal 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
110
mobile/ConnectBle2.qml
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
||||
159
mobile/ConnectBleForm.ui.qml
Normal file
159
mobile/ConnectBleForm.ui.qml
Normal 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
456
mobile/Controls.qml
Normal 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
217
mobile/CustomGauge.qml
Normal 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
262
mobile/DetectBldc.qml
Normal 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
194
mobile/DetectFocEncoder.qml
Normal 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
204
mobile/DetectFocHall.qml
Normal 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
367
mobile/DetectFocParam.qml
Normal 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
69
mobile/DoubleSpinBox.qml
Normal 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
214
mobile/FilePicker.qml
Normal 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
601
mobile/FwUpdate.qml
Normal 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
173
mobile/NrfPair.qml
Normal 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
358
mobile/PairingDialog.qml
Normal 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
139
mobile/ParamEditBool.qml
Normal 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
200
mobile/ParamEditDouble.qml
Normal 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
151
mobile/ParamEditEnum.qml
Normal 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
210
mobile/ParamEditInt.qml
Normal 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
48
mobile/ParamEditSeparator.qml
Normal file
48
mobile/ParamEditSeparator.qml
Normal 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
148
mobile/ParamEditString.qml
Normal 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
72
mobile/ParamEditors.qml
Normal 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
193
mobile/PpmMap.qml
Normal 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()
|
||||
}
|
||||
}
|
||||
}
|
||||
70
mobile/ProgressBar.qml
Normal file
70
mobile/ProgressBar.qml
Normal file
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
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.commands 1.0
|
||||
|
||||
Item {
|
||||
id:progressBar
|
||||
height: 20
|
||||
Layout.fillWidth: true
|
||||
property string prefix: ""
|
||||
property string unit: ""
|
||||
property double barValue: 0
|
||||
property double barFrom: 0
|
||||
property double barTo: 0
|
||||
|
||||
ColumnLayout {
|
||||
id: column
|
||||
anchors.fill: parent
|
||||
anchors.topMargin: 0
|
||||
anchors.margins: 5
|
||||
|
||||
ProgressBar {
|
||||
id: bar
|
||||
Layout.leftMargin: 35
|
||||
implicitHeight: 10
|
||||
Layout.preferredWidth: 300
|
||||
scale:1
|
||||
Layout.fillHeight: false
|
||||
from: barFrom
|
||||
value: barValue
|
||||
to: barTo
|
||||
Label {
|
||||
id: label
|
||||
color: "#ffffff"
|
||||
text: bar.value + unit
|
||||
anchors.leftMargin: 10
|
||||
anchors.left: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
Label {
|
||||
id: label2
|
||||
color: "#ffffff"
|
||||
text: prefix
|
||||
anchors.right: parent.left
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
270
mobile/RtData.qml
Normal file
270
mobile/RtData.qml
Normal file
@@ -0,0 +1,270 @@
|
||||
/*
|
||||
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 ConfigParams mbmsConfig: VescIf.bmsConfig()
|
||||
property var progressBarsVisible: []
|
||||
property var cellValues: [6,6,6,6,6,6,6,6,6,6,6,6]
|
||||
property var tempValues: [100,100,100,100,100]
|
||||
property var tempValuesExp: [100,100,100,100,100]
|
||||
|
||||
|
||||
property bool isHorizontal: width > height
|
||||
|
||||
|
||||
ProgressBar {
|
||||
id: progressBar
|
||||
}
|
||||
|
||||
|
||||
Component.onCompleted: {
|
||||
mCommands.emitEmptyValues()
|
||||
}
|
||||
|
||||
function addProgressBar(prefix, unit, barValue, barFrom, barTo) {
|
||||
var component = Qt.createComponent("ProgressBar.qml");
|
||||
return component.createObject(scrollCol, {"prefix": prefix, "unit": unit, "barValue": barValue, "barFrom": barFrom, "barTo": barTo});
|
||||
}
|
||||
|
||||
function destroyProgressBars() {
|
||||
for (var i = 0;i < progressBarsVisible.length;i++) {
|
||||
progressBarsVisible[i].destroy();
|
||||
}
|
||||
progressBarsVisible = []
|
||||
}
|
||||
|
||||
function createProgressBar(prefix, unit, barValue, barFrom, barTo) {
|
||||
progressBarsVisible.push(addProgressBar(prefix, unit, barValue, barFrom, barTo))
|
||||
}
|
||||
|
||||
|
||||
|
||||
function updateProgressBars(type, prefix, unit, barFrom, barTo) {
|
||||
destroyProgressBars()
|
||||
|
||||
switch(type) {
|
||||
case "Cells":
|
||||
for (var i = 0 ; i < cellValues.length ; i++) {
|
||||
createProgressBar(prefix+(i+1), unit, cellValues[i], barFrom, barTo)
|
||||
}
|
||||
break;
|
||||
case "Temps":
|
||||
for (var j = 0 ; j < tempValues.length ; j++) {
|
||||
createProgressBar(prefix+(j+1), unit, tempValues[j], barFrom, barTo)
|
||||
}
|
||||
break;
|
||||
case "TempsExp":
|
||||
for (var k = 0 ; k < tempValuesExp.length ; k++) {
|
||||
createProgressBar(prefix+(k+1), unit, tempValuesExp[k], barFrom, barTo)
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: column
|
||||
anchors.fill: parent
|
||||
spacing: 0
|
||||
ComboBox {
|
||||
id: tabBox
|
||||
Layout.fillWidth: true
|
||||
model: [
|
||||
"Cells voltages",
|
||||
"Temperatures",
|
||||
"Temperatures expansion"
|
||||
]
|
||||
onCurrentTextChanged: {
|
||||
|
||||
switch(currentText) {
|
||||
case "Cells voltages":
|
||||
updateProgressBars("Cells","C","V", 2.5, 4.2)
|
||||
break;
|
||||
case "Temperatures":
|
||||
updateProgressBars("Temps","T","\u00B0C", -50 ,100)
|
||||
break;
|
||||
case "Temperatures expansion":
|
||||
updateProgressBars("TempsExp","T","\u00B0C", -50 ,100)
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ScrollView {
|
||||
id: scroll
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
contentWidth: column.width
|
||||
clip: true
|
||||
|
||||
GridLayout {
|
||||
id: scrollCol
|
||||
anchors.fill: parent
|
||||
columns: isHorizontal ? 2 : 1
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: textRect
|
||||
color: "#272727"
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: valMetrics.height * 8 + 20
|
||||
Layout.alignment: Qt.AlignBottom
|
||||
|
||||
Rectangle {
|
||||
|
||||
anchors.bottom: valText.top
|
||||
width: parent.width
|
||||
height: 2
|
||||
color: "#81D4FA"
|
||||
}
|
||||
|
||||
Text {
|
||||
id: valText
|
||||
color: "white"
|
||||
text: VescIf.getConnectedPortName()
|
||||
font.family: "DejaVu Sans Mono"
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
anchors.left: parent.left
|
||||
anchors.verticalCenter: parent.verticalTop
|
||||
anchors.margins: 10
|
||||
}
|
||||
|
||||
Text {
|
||||
id: valText2
|
||||
color: "white"
|
||||
text: VescIf.getConnectedPortName()
|
||||
font.family: "DejaVu Sans Mono"
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
anchors.left: parent.horizontalCenter
|
||||
anchors.verticalCenter: parent.verticalTop
|
||||
anchors.margins: 10
|
||||
}
|
||||
|
||||
TextMetrics {
|
||||
id: valMetrics
|
||||
font: valText.font
|
||||
text: valText.text
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Connections {
|
||||
target: mCommands
|
||||
|
||||
onValuesReceived: {
|
||||
|
||||
valText.text =
|
||||
"V Pack : " + parseFloat(values.packVoltage).toFixed(2) + " V\n" +
|
||||
"I Pack : " + parseFloat(values.packCurrent).toFixed(2) + " A\n" +
|
||||
"CVHigh : " + parseFloat(values.cVHigh).toFixed(2) + " V\n" +
|
||||
"CVAverage : " + parseFloat(values.cVAverage).toFixed(2) + " V\n" +
|
||||
"CVLow : " + parseFloat(values.cVLow).toFixed(2) + " V\n" +
|
||||
"CVMismatch : " + parseFloat(values.cVMisMatch).toFixed(2) + " V\n" +
|
||||
"OpState : " + values.opState + "\n" +
|
||||
"FaultState : " + values.faultState + "\n" +
|
||||
"SoC : " + parseFloat(values.soC).toFixed(1) + " %\n"
|
||||
|
||||
valText2.text =
|
||||
"T Batt High: " + parseFloat(values.tempBattHigh).toFixed(1) + " \u00B0C\n" +
|
||||
"T Batt Avrg: " + parseFloat(values.tempBattAverage).toFixed(1) + " \u00B0C\n" +
|
||||
"T Batt Low : " + parseFloat(values.tempBattLow).toFixed(1) + " \u00B0C\n" +
|
||||
"T BMS High : " + parseFloat(values.tempBMSHigh).toFixed(1) + " \u00B0C\n" +
|
||||
"T BMS Avrg : " + parseFloat(values.tempBMSAverage).toFixed(1) + " \u00B0C\n" +
|
||||
"T BMS Low : " + parseFloat(values.tempBMSLow).toFixed(1) + " \u00B0C\n" +
|
||||
"Humidity : " + parseFloat(values.humidity).toFixed(1) + " %\n" +
|
||||
"V Load : " + parseFloat(values.loadLCVoltage).toFixed(1) + " V\n" +
|
||||
"V Charger : " + parseFloat(values.chargerVoltage).toFixed(1) + " V\n"
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: mCommands
|
||||
|
||||
onCellsReceived: {
|
||||
cellValues = cellVoltageArray
|
||||
if(tabBox.currentText==="Cells voltages"){
|
||||
updateProgressBars("Cells","C","V", 2.5, 4.2)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: mCommands
|
||||
|
||||
onAuxReceived: {
|
||||
tempValues = auxVoltageArray
|
||||
if(tabBox.currentText==="Temperatures"){
|
||||
updateProgressBars("Temps","T","\u00B0C", -50 ,100)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: mCommands
|
||||
|
||||
onExpTempReceived: {
|
||||
tempValuesExp = expTempVoltageArray
|
||||
if(tabBox.currentText==="Temperatures expansion"){
|
||||
updateProgressBars("TempsExp","T","\u00B0C", -50 ,100)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
224
mobile/RtData2.qml
Normal file
224
mobile/RtData2.qml
Normal file
@@ -0,0 +1,224 @@
|
||||
/*
|
||||
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
|
||||
|
||||
Item {
|
||||
id: rtData
|
||||
property Commands mCommands: VescIf.commands()
|
||||
property ConfigParams mbmsConfig: VescIf.bmsConfig()
|
||||
property var progressBarsVisible: []
|
||||
property bool isHorizontal: rtData.width > rtData.height
|
||||
property int gaugeSize: Math.min(width / 2 - 10,
|
||||
(height - valMetrics.height * 10) /
|
||||
(isHorizontal ? 1 : 2) - (isHorizontal ? 30 : 20))
|
||||
|
||||
|
||||
|
||||
//RtProgressBar {
|
||||
// id: progressBars
|
||||
//}
|
||||
|
||||
Component.onCompleted: {
|
||||
//currentGauge.minimumValue = -mbmsConfig.getParamDouble("maxAllowedCurrent")
|
||||
//currentGauge.maximumValue = mbmsConfig.getParamDouble("maxAllowedCurrent")
|
||||
mCommands.emitEmptyValues()
|
||||
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: column
|
||||
anchors.fill: parent
|
||||
|
||||
GridLayout {
|
||||
Layout.fillWidth: true
|
||||
columns: 1
|
||||
|
||||
|
||||
/*CustomGauge {
|
||||
id: currentGauge
|
||||
Layout.fillWidth: true
|
||||
maximumValue: 100
|
||||
minimumValue: -100
|
||||
labelStep: maximumValue > 60 ? 20 : 10
|
||||
value: 0
|
||||
unitText: "A"
|
||||
typeText: "Current"
|
||||
Layout.preferredWidth: gaugeSize
|
||||
Layout.preferredHeight: gaugeSize
|
||||
}
|
||||
|
||||
CustomGauge {
|
||||
id: dutyGauge
|
||||
Layout.fillWidth: true
|
||||
maximumValue: 100
|
||||
minimumValue: -100
|
||||
labelStep: 20
|
||||
value: 0
|
||||
unitText: "%"
|
||||
typeText: "Duty"
|
||||
Layout.preferredWidth: gaugeSize
|
||||
Layout.preferredHeight: gaugeSize
|
||||
}
|
||||
|
||||
CustomGauge {
|
||||
id: rpmGauge
|
||||
Layout.fillWidth: true
|
||||
maximumValue: 100
|
||||
minimumValue: -100
|
||||
labelStep: 20
|
||||
value: -0
|
||||
unitText: "x1000"
|
||||
typeText: "ERPM"
|
||||
Layout.preferredWidth: gaugeSize
|
||||
Layout.preferredHeight: gaugeSize
|
||||
}
|
||||
|
||||
CustomGauge {
|
||||
id: powerGauge
|
||||
Layout.fillWidth: true
|
||||
maximumValue: 10000
|
||||
minimumValue: -10000
|
||||
tickmarkScale: 0.001
|
||||
tickmarkSuffix: "k"
|
||||
labelStep: 1000
|
||||
value: 0
|
||||
unitText: "W"
|
||||
typeText: "Power"
|
||||
Layout.preferredWidth: gaugeSize
|
||||
Layout.preferredHeight: gaugeSize
|
||||
}*/
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: textRect
|
||||
color: "#272727"
|
||||
|
||||
Rectangle {
|
||||
anchors.bottom: valText.top
|
||||
width: parent.width
|
||||
height: 2
|
||||
color: "#81D4FA"
|
||||
}
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: valMetrics.height * 6 + 20
|
||||
Layout.alignment: Qt.AlignBottom
|
||||
|
||||
Text {
|
||||
id: valText
|
||||
color: "white"
|
||||
text: VescIf.getConnectedPortName()
|
||||
font.family: "DejaVu Sans Mono"
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
anchors.left: parent.left
|
||||
anchors.verticalCenter: parent.verticalTop
|
||||
anchors.margins: 10
|
||||
}
|
||||
|
||||
Text {
|
||||
id: valText2
|
||||
color: "white"
|
||||
text: VescIf.getConnectedPortName()
|
||||
font.family: "DejaVu Sans Mono"
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
anchors.left: parent.horizontalCenter
|
||||
anchors.verticalCenter: parent.verticalTop
|
||||
anchors.margins: 10
|
||||
}
|
||||
|
||||
TextMetrics {
|
||||
id: valMetrics
|
||||
font: valText.font
|
||||
text: valText.text
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: mbmsConfig
|
||||
|
||||
onUpdated: {
|
||||
currentGauge.maximumValue = Math.ceil(mbmsConfig.getParamDouble("maxAllowedCurrent") / 5) * 5
|
||||
currentGauge.minimumValue = -currentGauge.maximumValue
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: mCommands
|
||||
onCellsReceived:{
|
||||
updateProgressBars()
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: mCommands
|
||||
|
||||
onValuesReceived: {
|
||||
|
||||
/* currentGauge.value = values.packCurrent
|
||||
dutyGauge.value = values.duty_now * 100.0
|
||||
|
||||
var fl = mbmsConfig.getParamDouble("maxAllowedCurrent")
|
||||
var rpmMax = (values.v_in * 60.0) / (Math.sqrt(3.0) * 2.0 * Math.PI * fl)
|
||||
var rpmMaxRound = (Math.ceil(rpmMax / 5000.0) * 5000.0) / 1000
|
||||
|
||||
if (Math.abs(rpmGauge.maximumValue - rpmMaxRound) > 6) {
|
||||
rpmGauge.maximumValue = rpmMaxRound
|
||||
rpmGauge.minimumValue = -rpmMaxRound
|
||||
}
|
||||
|
||||
rpmGauge.value = values.packVoltage
|
||||
|
||||
var powerMax = Math.min(values.packVoltage * Math.min(mbmsConfig.getParamDouble("packVoltage"),
|
||||
mbmsConfig.getParamDouble("packVoltage")),
|
||||
mbmsConfig.getParamDouble("packVoltage"))
|
||||
var powerMaxRound = (Math.ceil(powerMax / 1000.0) * 1000.0)
|
||||
|
||||
if (Math.abs(powerGauge.maximumValue - powerMaxRound) > 1.2) {
|
||||
powerGauge.maximumValue = powerMaxRound
|
||||
powerGauge.minimumValue = -powerMaxRound
|
||||
}
|
||||
|
||||
powerGauge.value = (values.packCurrent * values.packVoltage)
|
||||
*/
|
||||
valText.text =
|
||||
"Battery : " + parseFloat(values.packVoltage).toFixed(2) + " V\n" +
|
||||
"I Battery : " + parseFloat(values.packCurrent).toFixed(2) + " A\n" +
|
||||
"CVHigh : " + parseFloat(values.cVHigh).toFixed(2) + " V\n" +
|
||||
"CVAverage : " + parseFloat(values.cVAverage).toFixed(2) + " V\n" +
|
||||
"CVLow : " + parseFloat(values.cVLow).toFixed(2) + " V\n" +
|
||||
"CVMismatch : " + parseFloat(values.cVMisMatch).toFixed(2) + " V\n"
|
||||
|
||||
valText2.text =
|
||||
"T Batt High: " + parseFloat(values.tempBattHigh).toFixed(1) + " \u00B0C\n" +
|
||||
"T Batt Avrg: " + parseFloat(values.tempBattAverage).toFixed(1) + " \u00B0C\n" +
|
||||
"T BMS High : " + parseFloat(values.tempBMSHigh).toFixed(2) + " \u00B0C\n" +
|
||||
"T BMS Avrg : " + parseFloat(values.tempBMSAverage).toFixed(2) + " \u00B0C\n" +
|
||||
"SoC : " + parseFloat(values.soC).toFixed(1) + " %\n" +
|
||||
"OpState : " + values.opState
|
||||
}
|
||||
}
|
||||
}
|
||||
240
mobile/RtDataSetup.qml
Normal file
240
mobile/RtDataSetup.qml
Normal file
@@ -0,0 +1,240 @@
|
||||
/*
|
||||
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.5
|
||||
import QtQuick.Controls 2.2
|
||||
import QtQuick.Layouts 1.3
|
||||
import QtQuick.Controls.Styles 1.4
|
||||
import QtGraphicalEffects 1.0
|
||||
|
||||
import Ennoid.bmsinterface 1.0
|
||||
import Ennoid.commands 1.0
|
||||
import Ennoid.configparams 1.0
|
||||
|
||||
Item {
|
||||
id: rtData
|
||||
property Commands mCommands: VescIf.commands()
|
||||
property ConfigParams mbmsConfig: VescIf.bmsConfig()
|
||||
property bool isHorizontal: rtData.width > rtData.height
|
||||
|
||||
property int gaugeSize: isHorizontal ? Math.min((height - valMetrics.height * 4) - 30, width / 3.5 - 10) :
|
||||
Math.min(width / 1.4,
|
||||
(height - valMetrics.height * 4) / 2.3 - 10)
|
||||
|
||||
|
||||
property int gaugeSize2: gaugeSize * 0.65
|
||||
property int gaugeVMargin: isHorizontal ? 0 : -gaugeSize * 0.05
|
||||
property int gaugeHMargin: isHorizontal ? -gaugeSize * 0.1 : gaugeSize * 0.02
|
||||
|
||||
Component.onCompleted: {
|
||||
mCommands.emitEmptySetupValues()
|
||||
}
|
||||
|
||||
GridLayout {
|
||||
anchors.fill: parent
|
||||
anchors.topMargin: 5
|
||||
columns: isHorizontal ? 3 : 1
|
||||
columnSpacing: 0
|
||||
|
||||
RowLayout {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
Layout.bottomMargin: gaugeVMargin
|
||||
Layout.preferredHeight: isHorizontal ? gaugeSize : gaugeSize2
|
||||
spacing: 0
|
||||
|
||||
CustomGauge {
|
||||
id: powerGauge
|
||||
Layout.preferredWidth: gaugeSize2
|
||||
Layout.preferredHeight: gaugeSize2
|
||||
Layout.alignment: (isHorizontal ? Qt.AlignTop : Qt.AlignVCenter)
|
||||
Layout.rightMargin: gaugeHMargin
|
||||
maximumValue: 10000
|
||||
minimumValue: -10000
|
||||
tickmarkScale: 0.001
|
||||
tickmarkSuffix: "k"
|
||||
labelStep: 1000
|
||||
value: 1000
|
||||
unitText: "W"
|
||||
typeText: "Power"
|
||||
}
|
||||
|
||||
CustomGauge {
|
||||
id: currentGauge
|
||||
Layout.preferredWidth: gaugeSize2
|
||||
Layout.preferredHeight: gaugeSize2
|
||||
Layout.alignment: isHorizontal ? Qt.AlignBottom : Qt.AlignVCenter
|
||||
Layout.rightMargin: gaugeHMargin
|
||||
minimumValue: 0
|
||||
maximumValue: 60
|
||||
labelStep: maximumValue > 60 ? 20 : 10
|
||||
unitText: "A"
|
||||
typeText: "Current"
|
||||
}
|
||||
}
|
||||
|
||||
CustomGauge {
|
||||
id: speedGauge
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
Layout.preferredWidth: gaugeSize
|
||||
Layout.preferredHeight: gaugeSize
|
||||
minimumValue: 0
|
||||
maximumValue: 60
|
||||
minAngle: -250
|
||||
maxAngle: 70
|
||||
labelStep: maximumValue > 60 ? 20 : 10
|
||||
value: 20
|
||||
unitText: "km/h"
|
||||
typeText: "Speed"
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
Layout.topMargin: gaugeVMargin
|
||||
Layout.preferredHeight: isHorizontal ? gaugeSize : gaugeSize2
|
||||
spacing: 0
|
||||
|
||||
CustomGauge {
|
||||
id: batteryGauge
|
||||
Layout.preferredWidth: gaugeSize2
|
||||
Layout.preferredHeight: gaugeSize2
|
||||
Layout.alignment: isHorizontal ? Qt.AlignBottom : Qt.AlignVCenter
|
||||
Layout.leftMargin: gaugeHMargin
|
||||
minimumValue: 0
|
||||
maximumValue: 100
|
||||
value: 95
|
||||
unitText: "%"
|
||||
typeText: "Battery"
|
||||
traceColor: "green"
|
||||
}
|
||||
|
||||
CustomGauge {
|
||||
id: dutyGauge
|
||||
Layout.preferredWidth: gaugeSize2
|
||||
Layout.preferredHeight: gaugeSize2
|
||||
Layout.alignment: isHorizontal ? Qt.AlignTop : Qt.AlignVCenter
|
||||
Layout.leftMargin: gaugeHMargin
|
||||
maximumValue: 100
|
||||
minimumValue: -100
|
||||
labelStep: 20
|
||||
value: 0
|
||||
unitText: "%"
|
||||
typeText: "Duty"
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: textRect
|
||||
color: "#272727"
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: valMetrics.height * 4 + 20
|
||||
Layout.alignment: Qt.AlignBottom
|
||||
Layout.columnSpan: isHorizontal ? 3 : 1
|
||||
|
||||
Rectangle {
|
||||
anchors.top: parent.top
|
||||
width: parent.width
|
||||
height: 2
|
||||
color: "#81D4FA"
|
||||
}
|
||||
|
||||
Text {
|
||||
id: valText
|
||||
color: "white"
|
||||
text: VescIf.getConnectedPortName()
|
||||
font.family: "DejaVu Sans Mono"
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
anchors.left: parent.left
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.margins: 10
|
||||
}
|
||||
|
||||
Text {
|
||||
id: valText2
|
||||
color: "white"
|
||||
text: VescIf.getConnectedPortName()
|
||||
font.family: "DejaVu Sans Mono"
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
anchors.left: parent.horizontalCenter
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.margins: 10
|
||||
}
|
||||
|
||||
TextMetrics {
|
||||
id: valMetrics
|
||||
font: valText.font
|
||||
text: valText.text
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: mCommands
|
||||
|
||||
onValuesSetupReceived: {
|
||||
currentGauge.maximumValue = Math.ceil(mbmsConfig.getParamDouble("maxAllowedCurrent") / 5) * 5 * values.num_vescs
|
||||
currentGauge.minimumValue = -currentGauge.maximumValue
|
||||
|
||||
currentGauge.value = values.packCurrent
|
||||
dutyGauge.value = values.duty_now * 100.0
|
||||
batteryGauge.value = values.battery_level * 100.0
|
||||
|
||||
var fl = mbmsConfig.getParamDouble("foc_motor_flux_linkage")
|
||||
var rpmMax = (values.v_in * 60.0) / (Math.sqrt(3.0) * 2.0 * Math.PI * fl)
|
||||
var speedFact = ((mbmsConfig.getParamInt("si_motor_poles") / 2.0) * 60.0 *
|
||||
mbmsConfig.getParamDouble("si_gear_ratio")) /
|
||||
(mbmsConfig.getParamDouble("si_wheel_diameter") * Math.PI)
|
||||
var speedMax = 3.6 * rpmMax / speedFact
|
||||
var speedMaxRound = (Math.ceil(speedMax / 10.0) * 10.0)
|
||||
|
||||
if (Math.abs(speedGauge.maximumValue - speedMaxRound) > 6.0) {
|
||||
speedGauge.maximumValue = speedMaxRound
|
||||
speedGauge.minimumValue = -speedMaxRound
|
||||
}
|
||||
|
||||
speedGauge.value = values.speed * 3.6
|
||||
|
||||
var powerMax = Math.min(values.v_in * Math.min(mbmsConfig.getParamDouble("l_in_current_max"),
|
||||
mbmsConfig.getParamDouble("l_current_max")),
|
||||
mbmsConfig.getParamDouble("l_watt_max")) * values.num_vescs
|
||||
var powerMaxRound = (Math.ceil(powerMax / 1000.0) * 1000.0)
|
||||
|
||||
if (Math.abs(powerGauge.maximumValue - powerMaxRound) > 1.2) {
|
||||
powerGauge.maximumValue = powerMaxRound
|
||||
powerGauge.minimumValue = -powerMaxRound
|
||||
}
|
||||
|
||||
powerGauge.value = (values.current_in * values.v_in)
|
||||
|
||||
valText.text =
|
||||
"mAh Out: " + parseFloat(values.amp_hours * 1000.0).toFixed(1) + "\n" +
|
||||
"mAh In : " + parseFloat(values.amp_hours_charged * 1000.0).toFixed(1) + "\n" +
|
||||
"Wh Out : " + parseFloat(values.watt_hours).toFixed(2) + "\n" +
|
||||
"Wh In : " + parseFloat(values.watt_hours_charged).toFixed(2)
|
||||
|
||||
var wh_km = (values.watt_hours - values.watt_hours_charged) / (values.tachometer_abs / 1000.0)
|
||||
|
||||
valText2.text =
|
||||
"Km Trip : " + parseFloat(values.tachometer_abs / 1000.0).toFixed(3) + "\n" +
|
||||
"Wh/Km : " + parseFloat(wh_km).toFixed(1) + "\n" +
|
||||
"Km Range: " + parseFloat(values.battery_wh / wh_km).toFixed(2) + "\n" +
|
||||
"VESCs : " + values.num_vescs
|
||||
}
|
||||
}
|
||||
}
|
||||
60
mobile/RtProgressBar.qml
Normal file
60
mobile/RtProgressBar.qml
Normal file
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
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 createProgressBar(parent, name, conf) {
|
||||
if (conf.hasParam(name)) {
|
||||
|
||||
var component = Qt.createComponent("RtProgressBarDouble.qml");
|
||||
return component.createObject(parent, {"params": conf, "paramName": name});
|
||||
|
||||
} else {
|
||||
console.log("Parameter " + name + " not found.")
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
function createProgressBar(parent, name) {
|
||||
return createProgressBar(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")
|
||||
}
|
||||
}
|
||||
124
mobile/Terminal.qml
Normal file
124
mobile/Terminal.qml
Normal file
@@ -0,0 +1,124 @@
|
||||
/*
|
||||
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 Commands mCommands: VescIf.commands()
|
||||
|
||||
ColumnLayout {
|
||||
id: column
|
||||
anchors.fill: parent
|
||||
spacing: 0
|
||||
|
||||
ScrollView {
|
||||
id: scroll
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
contentWidth: terminalText.width
|
||||
clip: true
|
||||
|
||||
TextArea {
|
||||
id: terminalText
|
||||
anchors.fill: parent
|
||||
readOnly: true
|
||||
font.family: "DejaVu Sans Mono"
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
Button {
|
||||
Layout.preferredWidth: 100
|
||||
Layout.fillWidth: true
|
||||
text: "Clear"
|
||||
|
||||
onClicked: {
|
||||
terminalText.clear()
|
||||
}
|
||||
}
|
||||
|
||||
Button {
|
||||
Layout.preferredWidth: 100
|
||||
Layout.fillWidth: true
|
||||
text: "Send"
|
||||
|
||||
onClicked: {
|
||||
mCommands.sendTerminalCmd(stringInput.text)
|
||||
stringInput.clear()
|
||||
}
|
||||
}
|
||||
|
||||
Button {
|
||||
Layout.preferredWidth: 50
|
||||
Layout.fillWidth: true
|
||||
text: "..."
|
||||
onClicked: menu.open()
|
||||
|
||||
Menu {
|
||||
id: menu
|
||||
width: 500
|
||||
|
||||
MenuItem {
|
||||
text: "Print Faults"
|
||||
onTriggered: {
|
||||
mCommands.sendTerminalCmd("faults")
|
||||
}
|
||||
}
|
||||
MenuItem {
|
||||
text: "Print Threads"
|
||||
onTriggered: {
|
||||
mCommands.sendTerminalCmd("threads")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: mCommands
|
||||
|
||||
onPrintReceived: {
|
||||
terminalText.text += "\n" + str
|
||||
}
|
||||
}
|
||||
}
|
||||
135
mobile/fwhelper.cpp
Normal file
135
mobile/fwhelper.cpp
Normal file
@@ -0,0 +1,135 @@
|
||||
/*
|
||||
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 "fwhelper.h"
|
||||
#include <QDirIterator>
|
||||
|
||||
FwHelper::FwHelper(QObject *parent) : QObject(parent)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
QVariantMap FwHelper::getHardwares(QString hw)
|
||||
{
|
||||
QVariantMap hws;
|
||||
|
||||
QDirIterator it("://res/firmwares");
|
||||
while (it.hasNext()) {
|
||||
QFileInfo fi(it.next());
|
||||
QStringList names = fi.fileName().split("_o_");
|
||||
|
||||
if (fi.isDir() && (hw.isEmpty() || names.contains(hw, Qt::CaseInsensitive))) {
|
||||
QString name = names.at(0);
|
||||
for(int i = 1;i < names.size();i++) {
|
||||
name += " & " + names.at(i);
|
||||
}
|
||||
|
||||
hws.insert(name, fi.absoluteFilePath());
|
||||
}
|
||||
}
|
||||
|
||||
return hws;
|
||||
}
|
||||
|
||||
QVariantMap FwHelper::getFirmwares(QString hw)
|
||||
{
|
||||
QVariantMap fws;
|
||||
|
||||
QDirIterator it(hw);
|
||||
while (it.hasNext()) {
|
||||
QFileInfo fi(it.next());
|
||||
fws.insert(fi.fileName(), fi.absoluteFilePath());
|
||||
}
|
||||
|
||||
return fws;
|
||||
}
|
||||
|
||||
QVariantMap FwHelper::getBootloaders(QString hw)
|
||||
{
|
||||
QVariantMap bls;
|
||||
|
||||
QDirIterator it("://res/bootloaders");
|
||||
while (it.hasNext()) {
|
||||
QFileInfo fi(it.next());
|
||||
QStringList names = fi.fileName().replace(".bin", "").split("_o_");
|
||||
|
||||
if (!fi.isDir() && (hw.isEmpty() || names.contains(hw, Qt::CaseInsensitive))) {
|
||||
QString name = names.at(0);
|
||||
for(int i = 1;i < names.size();i++) {
|
||||
name += " & " + names.at(i);
|
||||
}
|
||||
|
||||
bls.insert(name, fi.absoluteFilePath());
|
||||
}
|
||||
}
|
||||
|
||||
if (bls.isEmpty()) {
|
||||
QFileInfo generic("://res/bootloaders/generic.bin");
|
||||
if (generic.exists()) {
|
||||
bls.insert("generic", generic.absoluteFilePath());
|
||||
}
|
||||
}
|
||||
|
||||
return bls;
|
||||
}
|
||||
|
||||
bool FwHelper::uploadFirmware(QString filename, BMSInterface *dieBieMS, bool isBootloader, bool checkName)
|
||||
{
|
||||
// TODO: Should this be removed on android?
|
||||
if (filename.startsWith("file:/")) {
|
||||
filename.remove(0, 6);
|
||||
}
|
||||
|
||||
QFile file;
|
||||
file.setFileName(filename);
|
||||
QFileInfo fileInfo(filename);
|
||||
|
||||
if (checkName) {
|
||||
if (!(fileInfo.fileName().startsWith("ENNOID")) || !fileInfo.fileName().endsWith(".bin")) {
|
||||
dieBieMS->emitMessageDialog(tr("Upload Error"),tr("The selected file name seems to be invalid."),false, false);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!file.open(QIODevice::ReadOnly)) {
|
||||
dieBieMS->emitMessageDialog(tr("Upload Error"),
|
||||
tr("Could not open file. Make sure that the path is valid."),
|
||||
false);
|
||||
qDebug() << fileInfo.fileName() << fileInfo.absolutePath();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (file.size() > 400000) {
|
||||
dieBieMS->emitMessageDialog(tr("Upload Error"),
|
||||
tr("The selected file is too large to be a firmware."),
|
||||
false);
|
||||
return false;
|
||||
}
|
||||
|
||||
QByteArray data = file.readAll();
|
||||
dieBieMS->commands()->startFirmwareUpload(data, isBootloader);
|
||||
|
||||
return true;
|
||||
}
|
||||
48
mobile/fwhelper.h
Normal file
48
mobile/fwhelper.h
Normal 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 FWHELPER_H
|
||||
#define FWHELPER_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QVariantMap>
|
||||
#include "bmsinterface.h"
|
||||
|
||||
class FwHelper : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit FwHelper(QObject *parent = nullptr);
|
||||
Q_INVOKABLE QVariantMap getHardwares(QString hw = "");
|
||||
Q_INVOKABLE QVariantMap getFirmwares(QString hw);
|
||||
Q_INVOKABLE QVariantMap getBootloaders(QString hw);
|
||||
Q_INVOKABLE bool uploadFirmware(QString filename, BMSInterface *dieBieMS, bool isBootloader, bool isIncluded);
|
||||
|
||||
signals:
|
||||
|
||||
public slots:
|
||||
};
|
||||
|
||||
#endif // FWHELPER_H
|
||||
498
mobile/main.qml
Normal file
498
mobile/main.qml
Normal file
@@ -0,0 +1,498 @@
|
||||
/*
|
||||
Copyright 2017 - 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 Qt.labs.settings 1.0 as QSettings
|
||||
|
||||
import Ennoid.bmsinterface 1.0
|
||||
import Ennoid.commands 1.0
|
||||
import Ennoid.configparams 1.0
|
||||
import Ennoid.utility 1.0
|
||||
|
||||
ApplicationWindow {
|
||||
id: appWindow
|
||||
property Commands mCommands: VescIf.commands()
|
||||
property ConfigParams mbmsConfig: VescIf.bmsConfig()
|
||||
property ConfigParams mInfoConf: VescIf.infoConfig()
|
||||
|
||||
visible: true
|
||||
width: 500
|
||||
height: 850
|
||||
title: qsTr("ENNOID-BMS Tool")
|
||||
|
||||
Component.onCompleted: {
|
||||
// Utility.checkVersion(VescIf)
|
||||
|
||||
|
||||
}
|
||||
|
||||
Controls {
|
||||
id: controls
|
||||
parentWidth: appWindow.width
|
||||
parentHeight: appWindow.height - footer.height - tabBar.height
|
||||
}
|
||||
|
||||
Drawer {
|
||||
id: drawer
|
||||
width: 0.5 * appWindow.width
|
||||
height: appWindow.height - footer.height - tabBar.height
|
||||
y: tabBar.height
|
||||
dragMargin: 20
|
||||
|
||||
ColumnLayout {
|
||||
anchors.fill: parent
|
||||
anchors.margins: 10
|
||||
spacing: 0
|
||||
|
||||
Image {
|
||||
id: image
|
||||
Layout.preferredWidth: Math.min(parent.width, parent.height)
|
||||
Layout.preferredHeight: (300 * Layout.preferredWidth) / 1549
|
||||
Layout.alignment: Qt.AlignHCenter | Qt.AlignBottom
|
||||
source: "qrc:/res/logo_white.png"
|
||||
}
|
||||
|
||||
Button {
|
||||
id: reconnectButton
|
||||
|
||||
Layout.fillWidth: true
|
||||
text: "Reconnect"
|
||||
enabled: false
|
||||
flat: true
|
||||
|
||||
onClicked: {
|
||||
VescIf.reconnectLastPort()
|
||||
}
|
||||
}
|
||||
|
||||
Button {
|
||||
Layout.fillWidth: true
|
||||
text: "Disconnect"
|
||||
enabled: connBle.disconnectButton.enabled
|
||||
flat: true
|
||||
|
||||
onClicked: {
|
||||
VescIf.disconnectPort()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Item {
|
||||
// Spacer
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
}
|
||||
|
||||
Button {
|
||||
Layout.fillWidth: true
|
||||
text: "About"
|
||||
flat: true
|
||||
|
||||
onClicked: {
|
||||
VescIf.emitMessageDialog(
|
||||
"About",
|
||||
Utility.aboutText(),
|
||||
true, true)
|
||||
}
|
||||
}
|
||||
|
||||
Button {
|
||||
Layout.fillWidth: true
|
||||
text: "Changelog"
|
||||
flat: true
|
||||
|
||||
onClicked: {
|
||||
VescIf.emitMessageDialog(
|
||||
"EBMS Tool Changelog",
|
||||
Utility.vescToolChangeLog(),
|
||||
true, false)
|
||||
}
|
||||
}
|
||||
|
||||
Button {
|
||||
Layout.fillWidth: true
|
||||
text: "License"
|
||||
flat: true
|
||||
|
||||
onClicked: {
|
||||
VescIf.emitMessageDialog(
|
||||
mInfoConf.getLongName("gpl_text"),
|
||||
mInfoConf.getDescription("gpl_text"),
|
||||
true, true)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SwipeView {
|
||||
id: swipeView
|
||||
currentIndex: tabBar.currentIndex
|
||||
anchors.fill: parent
|
||||
|
||||
Page {
|
||||
ConnectBle {
|
||||
id: connBle
|
||||
anchors.fill: parent
|
||||
anchors.margins: 10
|
||||
|
||||
onRequestOpenControls: {
|
||||
controls.openDialog()
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Page {
|
||||
RowLayout {
|
||||
anchors.fill: parent
|
||||
spacing: 0
|
||||
|
||||
Rectangle {
|
||||
color: "#4f4f4f"
|
||||
width: 16
|
||||
Layout.fillHeight: true
|
||||
Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter
|
||||
|
||||
PageIndicator {
|
||||
count: rtSwipeView.count
|
||||
currentIndex: rtSwipeView.currentIndex
|
||||
anchors.centerIn: parent
|
||||
rotation: 90
|
||||
}
|
||||
}
|
||||
|
||||
SwipeView {
|
||||
id: rtSwipeView
|
||||
enabled: true
|
||||
clip: true
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
orientation: Qt.Vertical
|
||||
|
||||
Page {
|
||||
RtData {
|
||||
anchors.fill: parent
|
||||
}
|
||||
}
|
||||
|
||||
/*Page {
|
||||
RtDataSetup {
|
||||
anchors.fill: parent
|
||||
}
|
||||
}*/
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Page {
|
||||
ConfigPageGeneral {
|
||||
id: confPageGeneral
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: 10
|
||||
anchors.rightMargin: 10
|
||||
}
|
||||
}
|
||||
|
||||
Page {
|
||||
ConfigPageCell {
|
||||
id: confPageCell
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: 10
|
||||
anchors.rightMargin: 10
|
||||
}
|
||||
}
|
||||
|
||||
Page {
|
||||
ConfigPageSwitch {
|
||||
id: confPageSwitch
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: 10
|
||||
anchors.rightMargin: 10
|
||||
}
|
||||
}
|
||||
|
||||
Page {
|
||||
ConfigPageSignal {
|
||||
id: confPageSignal
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: 10
|
||||
anchors.rightMargin: 10
|
||||
}
|
||||
}
|
||||
|
||||
Page {
|
||||
ConfigPageDisplay {
|
||||
id: confPageDisplay
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: 10
|
||||
anchors.rightMargin: 10
|
||||
}
|
||||
}
|
||||
|
||||
Page {
|
||||
FwUpdate {
|
||||
anchors.fill: parent
|
||||
}
|
||||
}
|
||||
|
||||
Page {
|
||||
Terminal {
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: 10
|
||||
anchors.rightMargin: 10
|
||||
anchors.topMargin: 10
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
header: Rectangle {
|
||||
color: "#5f5f5f"
|
||||
height: tabBar.height
|
||||
|
||||
RowLayout {
|
||||
anchors.fill: parent
|
||||
spacing: 0
|
||||
|
||||
ToolButton {
|
||||
Layout.preferredHeight: tabBar.height
|
||||
Layout.preferredWidth: tabBar.height - 10
|
||||
|
||||
Image {
|
||||
id: manuButton
|
||||
anchors.centerIn: parent
|
||||
width: tabBar.height * 0.5
|
||||
height: tabBar.height * 0.5
|
||||
opacity: 0.5
|
||||
source: "qrc:/res/icons/Settings-96.png"
|
||||
}
|
||||
|
||||
onClicked: {
|
||||
if (drawer.visible) {
|
||||
drawer.close()
|
||||
} else {
|
||||
drawer.open()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TabBar {
|
||||
id: tabBar
|
||||
currentIndex: swipeView.currentIndex
|
||||
Layout.fillWidth: true
|
||||
implicitWidth: 0
|
||||
clip: true
|
||||
|
||||
background: Rectangle {
|
||||
opacity: 1
|
||||
color: "#4f4f4f"
|
||||
}
|
||||
|
||||
property int buttons: 9
|
||||
property int buttonWidth: 120
|
||||
|
||||
TabButton {
|
||||
text: qsTr("Connection")
|
||||
width: Math.max(tabBar.buttonWidth, tabBar.width / tabBar.buttons)
|
||||
}
|
||||
TabButton {
|
||||
text: qsTr("RT")
|
||||
width: Math.max(tabBar.buttonWidth, tabBar.width / tabBar.buttons)
|
||||
}
|
||||
TabButton {
|
||||
text: qsTr("General")
|
||||
width: Math.max(tabBar.buttonWidth, tabBar.width / tabBar.buttons)
|
||||
}
|
||||
TabButton {
|
||||
text: qsTr("Cell cfg")
|
||||
width: Math.max(tabBar.buttonWidth, tabBar.width / tabBar.buttons)
|
||||
}
|
||||
TabButton {
|
||||
text: qsTr("Switch")
|
||||
width: Math.max(tabBar.buttonWidth, tabBar.width / tabBar.buttons)
|
||||
}
|
||||
TabButton {
|
||||
text: qsTr("Signal")
|
||||
width: Math.max(tabBar.buttonWidth, tabBar.width / tabBar.buttons)
|
||||
}
|
||||
TabButton {
|
||||
text: qsTr("Display")
|
||||
width: Math.max(tabBar.buttonWidth, tabBar.width / tabBar.buttons)
|
||||
}
|
||||
TabButton {
|
||||
text: qsTr("Firmware")
|
||||
width: Math.max(tabBar.buttonWidth, tabBar.width / tabBar.buttons)
|
||||
}
|
||||
TabButton {
|
||||
text: qsTr("Terminal")
|
||||
width: Math.max(tabBar.buttonWidth, tabBar.width / tabBar.buttons)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
footer: Rectangle {
|
||||
id: connectedRect
|
||||
color: "#4f4f4f"
|
||||
|
||||
Text {
|
||||
id: connectedText
|
||||
color: "white"
|
||||
text: VescIf.getConnectedPortName()
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
anchors.fill: parent
|
||||
}
|
||||
|
||||
width: parent.width
|
||||
height: 20
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: statusTimer
|
||||
interval: 1600
|
||||
running: false
|
||||
repeat: false
|
||||
onTriggered: {
|
||||
connectedText.text = VescIf.getConnectedPortName()
|
||||
connectedRect.color = "4f4f4f"
|
||||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: uiTimer
|
||||
interval: 1000
|
||||
running: true
|
||||
repeat: true
|
||||
onTriggered: {
|
||||
if (!statusTimer.running && connectedText.text !== VescIf.getConnectedPortName()) {
|
||||
connectedText.text = VescIf.getConnectedPortName()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: confTimer
|
||||
interval: 1000
|
||||
running: true
|
||||
repeat: true
|
||||
|
||||
property bool mcConfRx: false
|
||||
|
||||
onTriggered: {
|
||||
if (VescIf.isPortConnected()) {
|
||||
if (!mcConfRx) {
|
||||
mCommands.getBMSconf()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: rtTimer
|
||||
interval: 50
|
||||
running: true
|
||||
repeat: true
|
||||
|
||||
onTriggered: {
|
||||
if (VescIf.isPortConnected() && tabBar.currentIndex == 1) {
|
||||
// Sample RT data when the RT page is selected
|
||||
interval = 50
|
||||
mCommands.getValues()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Dialog {
|
||||
id: vescDialog
|
||||
standardButtons: Dialog.Ok
|
||||
modal: true
|
||||
focus: true
|
||||
closePolicy: Popup.CloseOnEscape
|
||||
|
||||
width: parent.width - 20
|
||||
height: Math.min(implicitHeight, parent.height - 40)
|
||||
x: (parent.width - width) / 2
|
||||
y: (parent.height - height) / 2
|
||||
parent: ApplicationWindow.overlay
|
||||
|
||||
ScrollView {
|
||||
anchors.fill: parent
|
||||
clip: true
|
||||
contentWidth: parent.width - 20
|
||||
|
||||
Text {
|
||||
id: vescDialogLabel
|
||||
color: "#ffffff"
|
||||
linkColor: "lightblue"
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
anchors.fill: parent
|
||||
wrapMode: Text.WordWrap
|
||||
textFormat: Text.RichText
|
||||
onLinkActivated: {
|
||||
Qt.openUrlExternally(link)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: VescIf
|
||||
onPortConnectedChanged: {
|
||||
connectedText.text = VescIf.getConnectedPortName()
|
||||
if (VescIf.isPortConnected()) {
|
||||
reconnectButton.enabled = true
|
||||
}
|
||||
}
|
||||
|
||||
onStatusMessage: {
|
||||
connectedText.text = msg
|
||||
connectedRect.color = isGood ? "green" : "red"
|
||||
statusTimer.restart()
|
||||
}
|
||||
|
||||
onMessageDialog: {
|
||||
vescDialog.title = title
|
||||
vescDialogLabel.text =(richText ? "<style>a:link { color: lightblue; }</style>" : "") + msg
|
||||
vescDialogLabel.textFormat = richText ? Text.RichText : Text.AutoText
|
||||
vescDialog.open()
|
||||
}
|
||||
|
||||
onFwRxChanged: {
|
||||
if (rx) {
|
||||
if(limited){
|
||||
swipeView.setCurrentIndex(5)
|
||||
}else {
|
||||
mCommands.getBMSconf()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: mbmsConfig
|
||||
|
||||
onUpdated: {
|
||||
confTimer.mcConfRx = true
|
||||
}
|
||||
}
|
||||
}
|
||||
13
mobile/mobile.pri
Normal file
13
mobile/mobile.pri
Normal file
@@ -0,0 +1,13 @@
|
||||
HEADERS += \
|
||||
$$PWD/qmlui.h \
|
||||
$$PWD/fwhelper.h
|
||||
|
||||
SOURCES += \
|
||||
$$PWD/qmlui.cpp \
|
||||
$$PWD/fwhelper.cpp
|
||||
|
||||
RESOURCES += \
|
||||
$$PWD/qml.qrc
|
||||
|
||||
DISTFILES +=
|
||||
|
||||
31
mobile/qml.qrc
Normal file
31
mobile/qml.qrc
Normal file
@@ -0,0 +1,31 @@
|
||||
<RCC>
|
||||
<qresource prefix="/">
|
||||
<file>qtquickcontrols2.conf</file>
|
||||
</qresource>
|
||||
<qresource prefix="/mobile">
|
||||
<file>main.qml</file>
|
||||
<file>ConnectBle.qml</file>
|
||||
<file>RtData.qml</file>
|
||||
<file>FwUpdate.qml</file>
|
||||
<file>FilePicker.qml</file>
|
||||
<file>ParamEditDouble.qml</file>
|
||||
<file>ParamEditors.qml</file>
|
||||
<file>DoubleSpinBox.qml</file>
|
||||
<file>ParamEditInt.qml</file>
|
||||
<file>ParamEditEnum.qml</file>
|
||||
<file>ParamEditBool.qml</file>
|
||||
<file>ParamEditString.qml</file>
|
||||
<file>ParamEditSeparator.qml</file>
|
||||
<file>PpmMap.qml</file>
|
||||
<file>NrfPair.qml</file>
|
||||
<file>Controls.qml</file>
|
||||
<file>Terminal.qml</file>
|
||||
<file>ConfigPageGeneral.qml</file>
|
||||
<file>ConfigPageCell.qml</file>
|
||||
<file>ConfigPageSwitch.qml</file>
|
||||
<file>ConfigPageDisplay.qml</file>
|
||||
<file>ProgressBar.qml</file>
|
||||
<file>PairingDialog.qml</file>
|
||||
<file>ConfigPageSignal.qml</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
70
mobile/qmlui.cpp
Normal file
70
mobile/qmlui.cpp
Normal file
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
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 "qmlui.h"
|
||||
#include "fwhelper.h"
|
||||
|
||||
#include <QQuickStyle>
|
||||
|
||||
QmlUi::QmlUi(QObject *parent) : QObject(parent)
|
||||
{
|
||||
mEngine = new QQmlApplicationEngine(this);
|
||||
}
|
||||
|
||||
bool QmlUi::startQmlUi()
|
||||
{
|
||||
qmlRegisterSingletonType<BMSInterface>("Ennoid.bmsinterface", 1, 0, "VescIf", BMSinterface_singletontype_provider);
|
||||
qmlRegisterSingletonType<Utility>("Ennoid.utility", 1, 0, "Utility", utility_singletontype_provider);
|
||||
qmlRegisterType<BleUart>("Ennoid.bleuart", 1, 0, "BleUart");
|
||||
qmlRegisterType<Commands>("Ennoid.commands", 1, 0, "Commands");
|
||||
qmlRegisterType<ConfigParams>("Ennoid.configparams", 1, 0, "ConfigParams");
|
||||
qmlRegisterType<FwHelper>("Ennoid.fwhelper", 1, 0, "FwHelper");
|
||||
|
||||
mEngine->load(QUrl(QLatin1String("qrc:/mobile/main.qml")));
|
||||
return !mEngine->rootObjects().isEmpty();
|
||||
}
|
||||
|
||||
QObject *QmlUi::BMSinterface_singletontype_provider(QQmlEngine *engine, QJSEngine *scriptEngine)
|
||||
{
|
||||
(void)engine;
|
||||
(void)scriptEngine;
|
||||
|
||||
BMSInterface *vesc = new BMSInterface();
|
||||
|
||||
vesc->bmsConfig()->loadParamsXml("://res/config.xml");
|
||||
vesc->infoConfig()->loadParamsXml("://res/info.xml");
|
||||
|
||||
return vesc;
|
||||
}
|
||||
|
||||
QObject *QmlUi::utility_singletontype_provider(QQmlEngine *engine, QJSEngine *scriptEngine)
|
||||
{
|
||||
(void)engine;
|
||||
(void)scriptEngine;
|
||||
|
||||
Utility *util = new Utility();
|
||||
|
||||
return util;
|
||||
}
|
||||
54
mobile/qmlui.h
Normal file
54
mobile/qmlui.h
Normal file
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
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 QMLUI_H
|
||||
#define QMLUI_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QQmlApplicationEngine>
|
||||
|
||||
#include "bmsinterface.h"
|
||||
#include "utility.h"
|
||||
|
||||
class QmlUi : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit QmlUi(QObject *parent = nullptr);
|
||||
bool startQmlUi();
|
||||
|
||||
signals:
|
||||
|
||||
public slots:
|
||||
|
||||
private:
|
||||
QQmlApplicationEngine *mEngine;
|
||||
|
||||
static QObject *BMSinterface_singletontype_provider(QQmlEngine *engine, QJSEngine *scriptEngine);
|
||||
static QObject *utility_singletontype_provider(QQmlEngine *engine, QJSEngine *scriptEngine);
|
||||
|
||||
};
|
||||
|
||||
#endif // QMLUI_H
|
||||
17
mobile/qtquickcontrols2.conf
Normal file
17
mobile/qtquickcontrols2.conf
Normal file
@@ -0,0 +1,17 @@
|
||||
; This file can be edited to change the style of the application
|
||||
; See Styling Qt Quick Controls 2 in the documentation for details:
|
||||
; http://doc.qt.io/qt-5/qtquickcontrols2-styles.html
|
||||
|
||||
[Controls]
|
||||
Style=Material
|
||||
|
||||
[Universal]
|
||||
Theme=Light
|
||||
;Accent=Steel
|
||||
|
||||
[Material]
|
||||
Theme=Dark
|
||||
Accent=LightBlue
|
||||
;Primary=BlueGray
|
||||
;Background="#1D1D1D"
|
||||
;Foreground=White
|
||||
Reference in New Issue
Block a user