Various changes have been made as a result of the discussion of the first version

This commit is contained in:
Yury Shuvakin
2022-09-09 16:36:44 +03:00
parent 1fc06361f8
commit 3d0f39f846
13 changed files with 259 additions and 68 deletions

View File

@@ -173,6 +173,8 @@ TRANSLATIONS = translations/cubo_en.ts \
translations/cubo_ru.ts \ translations/cubo_ru.ts \
translations/cubo_it.ts translations/cubo_it.ts
RC_ICONS = qml/Icons/cubo-icon.ico
DISTFILES += \ DISTFILES += \
android/AndroidManifest.xml \ android/AndroidManifest.xml \
android/gradle/wrapper/gradle-wrapper.jar \ android/gradle/wrapper/gradle-wrapper.jar \

View File

@@ -1,6 +1,8 @@
import QtQuick 2.12 import QtQuick 2.12
import QtCharts 2.3 import QtCharts 2.3
import QtQuick.Shapes 1.12
import Controls 1.0 as Controls
import Utils 1.0 import Utils 1.0
ChartView { ChartView {
@@ -20,6 +22,7 @@ ChartView {
property int defaultXMax: 10 property int defaultXMax: 10
property int defaultYMax: 1 property int defaultYMax: 1
property bool autoScaling: true property bool autoScaling: true
property int selectedSeriesIndex: 0
ValueAxis { ValueAxis {
id: valueAxisX id: valueAxisX
@@ -45,10 +48,47 @@ ChartView {
titleVisible: false titleVisible: false
} }
Shape {
id: currentPointLine
implicitWidth: 1
implicitHeight: chart.plotArea.height
x: chart.plotArea.x
y: chart.plotArea.y
ShapePath {
strokeColor: Palette.borderColor
strokeStyle: ShapePath.SolidLine
startX: 0
startY: 0
PathLine { x: 0; y: currentPointLine.height }
}
}
Rectangle {
id: currentPointInfo
width: currentPointInfoLabel.width
height: currentPointInfoLabel.height
color: Palette.backgroundColor
border.width: 1
border.color: Palette.borderColor
opacity: 0.9
radius: 6
visible: false
Controls.SubtitleLabel {
id: currentPointInfoLabel
horizontalAlignment: Text.AlignHCenter
anchors.centerIn: parent
padding: 10
}
}
MouseArea { MouseArea {
id: chartMouseAreaA id: chartMouseArea
anchors.fill: parent anchors.fill: parent
acceptedButtons: Qt.LeftButton acceptedButtons: Qt.LeftButton
hoverEnabled: true
property real lastMouseX: 0 property real lastMouseX: 0
property real lastMouseY: 0 property real lastMouseY: 0
@@ -59,27 +99,28 @@ ChartView {
if ((mouse.buttons & Qt.LeftButton) == Qt.LeftButton) { if ((mouse.buttons & Qt.LeftButton) == Qt.LeftButton) {
if (mouseX - lastMouseX > scrollThreshold) { if (mouseX - lastMouseX > scrollThreshold) {
chart.scrollLeft(mouseX - lastMouseX) chart.scrollLeft(mouseX - lastMouseX)
lastMouseX = mouseX
chart.autoScaling = false
} else if (mouseX - lastMouseX < scrollThreshold) { } else if (mouseX - lastMouseX < scrollThreshold) {
chart.scrollRight(lastMouseX - mouseX) chart.scrollRight(lastMouseX - mouseX)
lastMouseX = mouseX
chart.autoScaling = false
} }
lastMouseX = mouseX
chart.autoScaling = false
} }
updatePointInfo()
} }
onMouseYChanged: { onMouseYChanged: {
if ((mouse.buttons & Qt.LeftButton) == Qt.LeftButton) { if ((mouse.buttons & Qt.LeftButton) == Qt.LeftButton) {
if (mouseY - lastMouseY > scrollThreshold) { if (mouseY - lastMouseY > scrollThreshold) {
chart.scrollUp(mouseY - lastMouseY) chart.scrollUp(mouseY - lastMouseY)
lastMouseY = mouseY
chart.autoScaling = false
} else if (mouseY - lastMouseY < scrollThreshold) { } else if (mouseY - lastMouseY < scrollThreshold) {
chart.scrollDown(lastMouseY - mouseY) chart.scrollDown(lastMouseY - mouseY)
lastMouseY = mouseY
chart.autoScaling = false
} }
lastMouseY = mouseY
chart.autoScaling = false
} }
updatePointInfo()
} }
onPressed: { onPressed: {
if (mouse.button === Qt.LeftButton) { if (mouse.button === Qt.LeftButton) {
@@ -88,13 +129,71 @@ ChartView {
} }
} }
onWheel: { onWheel: {
if (wheel.angleDelta.y > 0) { var scaleFactor = 0.75
chart.zoomIn() var area = chart.plotArea
chart.autoScaling = false chart.autoScaling = false
} else {
chart.zoomOut() var yAxisPosition = mouseX <= chart.plotArea.x
chart.autoScaling = false if (yAxisPosition) {
if (wheel.angleDelta.y > 0) {
chart.zoomIn(Qt.rect(area.x, area.y + Math.round(area.height * (1 - scaleFactor) / 2), area.width, Math.round(area.height * scaleFactor)))
} else {
chart.zoomIn(Qt.rect(area.x, area.y - Math.round((area.height / scaleFactor) * (1 - scaleFactor) / 2), area.width, Math.round(area.height / scaleFactor)))
}
return
} }
var xAxisPosition = mouseY >= (chart.plotArea.y + chart.plotArea.height)
if (xAxisPosition) {
if (wheel.angleDelta.y > 0) {
chart.zoomIn(Qt.rect(area.x + area.width * (1 - scaleFactor) / 2, area.y, Math.round(area.width * scaleFactor), area.height))
} else {
chart.zoomIn(Qt.rect(area.x - (area.width / scaleFactor) * (1 - scaleFactor) / 2, area.y, Math.round(area.width / scaleFactor), area.height))
}
return
}
if (wheel.angleDelta.y > 0) {
let zoomedInArea = Qt.rect(area.x + Math.round((mouseX - area.x) * (1 - scaleFactor)),
area.y + Math.round((mouseY - area.y) * (1 - scaleFactor)),
Math.round(area.width * scaleFactor),
Math.round(area.height * scaleFactor))
chart.zoomIn(zoomedInArea)
} else {
let zoomedOutArea = Qt.rect(area.x - Math.floor((mouseX - area.x) * (1 - scaleFactor)),
area.y - Math.floor((mouseY - area.y) * (1 - scaleFactor)),
Math.round(area.width * (1 + (1 - scaleFactor))),
Math.round(area.height * (1 + (1 - scaleFactor))))
chart.zoomIn(zoomedOutArea)
}
updatePointInfo()
}
function updatePointInfo() {
var currentPointVisibility = mouseX >= chart.plotArea.x && mouseX <= chart.plotArea.x + chart.plotArea.width &&
mouseY >= chart.plotArea.y && mouseY <= chart.plotArea.y + chart.plotArea.height
currentPointInfo.visible = currentPointVisibility
currentPointInfo.x = mouseX + 5
currentPointInfo.y = mouseY - currentPointInfo.height - 5
var currentX = Math.ceil(mapToValue(Qt.point(mouseX, mouseY), chart.series(selectedSeriesIndex)).x)
var selectedPoint = Qt.point(0, 0)
if (currentX < chart.series(selectedSeriesIndex).count && currentX > 0) {
selectedPoint = chart.series(selectedSeriesIndex).at(currentX)
currentPointInfoLabel.text = selectedPoint.x + ", " +selectedPoint.y
} else {
currentPointInfo.visible = false
}
currentPointLine.visible = currentPointVisibility
currentPointLine.x = mouseX
} }
} }
onSeriesAdded: if (count === 1) {
series.pointAdded.connect(function (index){ chartMouseArea.updatePointInfo() })
}
} }

View File

@@ -0,0 +1,27 @@
import QtQuick 2.12
import QtQuick.Controls 2.12
import Utils 1.0
Button {
id: control
icon.width: 24
icon.height: 24
contentItem: Image {
source: control.icon.source
sourceSize.width: control.icon.width
sourceSize.height: control.icon.height
fillMode: Image.PreserveAspectFit
}
background: Rectangle {
implicitWidth: 44
implicitHeight: 44
opacity: enabled ? 1 : 0.3
color: control.pressed ? Palette.pressedButtonColor :
control.hovered ? Palette.hoveredButtonColor : Palette.buttonColor
radius: 5
}
}

View File

@@ -9,6 +9,8 @@ TextField {
color: Palette.textColor color: Palette.textColor
selectByMouse: true selectByMouse: true
selectionColor: Palette.selectedTextBackgroundColor selectionColor: Palette.selectedTextBackgroundColor
leftPadding: 16
rightPadding: 16
background: Rectangle { background: Rectangle {
color: enabled ? Palette.backgroundColor : Palette.hoveredBackgroundColor color: enabled ? Palette.backgroundColor : Palette.hoveredBackgroundColor

View File

@@ -24,3 +24,4 @@ BusyIndicator 1.0 BusyIndicator.qml
MenuItemDelegate 1.0 MenuItemDelegate.qml MenuItemDelegate 1.0 MenuItemDelegate.qml
ScrollIndicator 1.0 ScrollIndicator.qml ScrollIndicator 1.0 ScrollIndicator.qml
OutlineImageButton 1.0 OutlineImageButton.qml OutlineImageButton 1.0 OutlineImageButton.qml
ImageButton 1.0 ImageButton.qml

BIN
qml/Icons/cubo-icon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 98 KiB

View File

@@ -9,7 +9,7 @@ import Utils 1.0
ApplicationWindow { ApplicationWindow {
id: window id: window
title: qsTr("Cubo Verde BMS tool") title: qsTr("Cubo Verde BMS")
width: 1366 width: 1366
height: 768 height: 768
visible: true visible: true

View File

@@ -6,6 +6,8 @@ import Cubo 1.0
import Utils 1.0 import Utils 1.0
Item { Item {
id: root
ColumnLayout { ColumnLayout {
spacing: 15 spacing: 15
anchors.fill: parent anchors.fill: parent
@@ -298,6 +300,8 @@ Item {
Connections { Connections {
target: BmsInterface.commands() target: BmsInterface.commands()
enabled: root.visible
onValuesReceived: { onValuesReceived: {
batteryChargeLevelLabel.text = values.soC batteryChargeLevelLabel.text = values.soC
batteryVoltageLabel.text = MathHelper.roundDouble(values.packVoltage) batteryVoltageLabel.text = MathHelper.roundDouble(values.packVoltage)
@@ -334,7 +338,7 @@ Item {
Timer { Timer {
id: refreshValuesTimer id: refreshValuesTimer
interval: 5000 interval: 1000
onTriggered: getValues() onTriggered: getValues()
} }

View File

@@ -15,13 +15,15 @@ RowLayout {
signal needWait(bool active, string text) signal needWait(bool active, string text)
spacing: contentColumnSpacing spacing: contentColumnSpacing - 10
Flickable { Flickable {
id: settingsFlickable id: settingsFlickable
clip: true clip: true
contentWidth: width - rightMargin - leftMargin
contentHeight: configLayout.height contentHeight: configLayout.height
boundsBehavior: Flickable.StopAtBounds boundsBehavior: Flickable.StopAtBounds
rightMargin: 10
ColumnLayout { ColumnLayout {
id: configLayout id: configLayout

View File

@@ -8,6 +8,8 @@ import Cubo 1.0
import Utils 1.0 import Utils 1.0
Item { Item {
id: root
Component { Component {
id: cellListHeader id: cellListHeader
@@ -22,6 +24,7 @@ Item {
Layout.fillWidth: true Layout.fillWidth: true
Controls.SubtitleLabel { Controls.SubtitleLabel {
id: indexTitleLabel
text: qsTr("#") text: qsTr("#")
color: Palette.tableHeaderTextColor color: Palette.tableHeaderTextColor
anchors.centerIn: parent anchors.centerIn: parent
@@ -35,6 +38,7 @@ Item {
Layout.fillWidth: true Layout.fillWidth: true
Controls.SubtitleLabel { Controls.SubtitleLabel {
id: voltageTitleLabel
text: qsTr("Voltage") text: qsTr("Voltage")
color: Palette.tableHeaderTextColor color: Palette.tableHeaderTextColor
anchors.centerIn: parent anchors.centerIn: parent
@@ -48,6 +52,7 @@ Item {
Layout.fillWidth: true Layout.fillWidth: true
Controls.SubtitleLabel { Controls.SubtitleLabel {
id: balancingTitleLabel
text: qsTr("Balancing") text: qsTr("Balancing")
color: Palette.tableHeaderTextColor color: Palette.tableHeaderTextColor
anchors.centerIn: parent anchors.centerIn: parent
@@ -64,15 +69,16 @@ Item {
RowLayout { RowLayout {
spacing: 10 spacing: 10
width: ListView.view.width width: ListView.view.width
height: 38 height: 36
Item { Item {
Layout.preferredWidth: parent.width / 6 - 20 Layout.preferredWidth: parent.width / 6 - 24
} }
Controls.SubtitleLabel { Controls.SubtitleLabel {
id: indexLabel id: indexLabel
color: Palette.tableHeaderTextColor color: Palette.tableHeaderTextColor
horizontalAlignment: Text.AlignHCenter
Layout.preferredWidth: 25 Layout.preferredWidth: 25
Layout.alignment: Qt.AlignCenter Layout.alignment: Qt.AlignCenter
} }
@@ -96,7 +102,7 @@ Item {
} }
Item { Item {
Layout.preferredWidth: parent.width / 6 - 20 Layout.preferredWidth: parent.width / 6 - 24
} }
Component.onCompleted: { Component.onCompleted: {
@@ -157,12 +163,14 @@ Item {
Connections { Connections {
target: BmsInterface.commands() target: BmsInterface.commands()
enabled: root.visible
onCellsReceived: { onCellsReceived: {
firstCellGroup.model = [] firstCellGroup.model = []
var firstModel = [] var firstModel = []
for (var i = 0; i < Math.min(cellCount, 16); ++i) { for (var i = 0; i < Math.min(cellCount, 16); ++i) {
firstModel.push({"voltage": MathHelper.roundDouble(cellVoltageArray[i]), "balancing": cellVoltageArray[i] < 0}) firstModel.push({"voltage": Math.abs(MathHelper.roundDouble(cellVoltageArray[i], 3)), "balancing": cellVoltageArray[i] < 0})
} }
firstCellGroup.model = firstModel firstCellGroup.model = firstModel
@@ -171,7 +179,7 @@ Item {
var secondModel = [] var secondModel = []
for (var j = 16; j < Math.min(cellCount, 32); ++j) { for (var j = 16; j < Math.min(cellCount, 32); ++j) {
secondModel.push({"voltage": MathHelper.roundDouble(cellVoltageArray[j]), "balancing": cellVoltageArray[i] < 0}) secondModel.push({"voltage": Math.abs(MathHelper.roundDouble(cellVoltageArray[j], 3)), "balancing": cellVoltageArray[i] < 0})
} }
secondCellGroup.model = secondModel secondCellGroup.model = secondModel
} }
@@ -187,7 +195,7 @@ Item {
Timer { Timer {
id: refreshValuesTimer id: refreshValuesTimer
interval: 5000 interval: 1000
onTriggered: getValues() onTriggered: getValues()
} }

View File

@@ -8,9 +8,10 @@ import Cubo 1.0
import Utils 1.0 import Utils 1.0
ColumnLayout { ColumnLayout {
id: root
spacing: 20 spacing: 20
property int currentTime: 0 property real currentTime: 0
property var voltageData: [] property var voltageData: []
property var currentData: [] property var currentData: []
@@ -86,6 +87,7 @@ ColumnLayout {
property Controls.ChartView chartItem: undefined property Controls.ChartView chartItem: undefined
property color seriesColor: "black" property color seriesColor: "black"
property bool horizontal: true property bool horizontal: true
property bool selected: false
Controls.CheckBox { Controls.CheckBox {
id: checkSeries id: checkSeries
@@ -114,9 +116,17 @@ ColumnLayout {
Rectangle { Rectangle {
color: seriesColor color: seriesColor
radius: width / 2 radius: width / 2
Layout.preferredWidth: 18 border.width: selected ? 2 : 0
Layout.preferredHeight: 18 border.color: "black"
Layout.preferredWidth: selected ? 21 : 18
Layout.preferredHeight: selected ? 21 : 18
Layout.rightMargin: 20 Layout.rightMargin: 20
Layout.alignment: Qt.AlignCenter
MouseArea {
anchors.fill: parent
onClicked: if (!selected) selected = true
}
} }
Component.onCompleted: { Component.onCompleted: {
@@ -124,6 +134,10 @@ ColumnLayout {
horizontal = !ListView.view horizontal = !ListView.view
seriesColor = chartItem.series(modelData).color seriesColor = chartItem.series(modelData).color
checkSeries.text = Qt.binding(function(){ return chartItem.series(modelData).name }) checkSeries.text = Qt.binding(function(){ return chartItem.series(modelData).name })
selectedChanged.connect(function (){ if (selected) chartItem.selectedSeriesIndex = index })
chartItem.selectedSeriesIndexChanged.connect(function (){ if (typeof(selected) != "undefined") selected = chartItem.selectedSeriesIndex === index })
selected = chartItem.selectedSeriesIndex === index
} }
} }
} }
@@ -289,44 +303,6 @@ ColumnLayout {
RowLayout { RowLayout {
spacing: 20 spacing: 20
Controls.Button {
text: timer.running ? qsTr("Pause data collection") : qsTr("Resume data collection")
onClicked: {
if (timer.running)
timer.stop()
else
timer.start()
}
}
Controls.Button {
text: qsTr("Clear data")
onClicked: {
currentTime = 0
clearData(voltageData, voltageLoader)
clearData(currentData, currentLoader)
clearData(batteryTemperatureData, batteryTemperatureLoader)
clearData(bmsTemperatureData, bmsTemperatureLoader)
clearData(cellVoltageData, cellVoltageLoader)
clearData(cellListData, cellListLoader)
resetZoomButton.clicked()
}
function clearData(dataObject, chartObject) {
for (var i = 0; i < dataObject.length; ++i) {
dataObject[i].splice(0, dataObject[i].length)
var series = chartObject.item.chart.series(i)
series.removePoints(0, series.count)
}
}
}
Item {
Layout.fillWidth: true
}
Controls.Button { Controls.Button {
id: resetZoomButton id: resetZoomButton
text: qsTr("Reset zoom") text: qsTr("Reset zoom")
@@ -382,6 +358,73 @@ ColumnLayout {
return min return min
} }
} }
Controls.Button {
property string pauseText: qsTr("Pause data collection")
property string resumeText: qsTr("Resume data collection")
text: pauseText
onClicked: {
if (timer.running) {
text = resumeText
timer.stop()
} else {
text = pauseText
timer.start()
}
}
}
Controls.Button {
text: qsTr("Clear data")
onClicked: {
currentTime = 0
clearData(voltageData, voltageLoader)
clearData(currentData, currentLoader)
clearData(batteryTemperatureData, batteryTemperatureLoader)
clearData(bmsTemperatureData, bmsTemperatureLoader)
clearData(cellVoltageData, cellVoltageLoader)
clearData(cellListData, cellListLoader)
resetZoomButton.clicked()
}
function clearData(dataObject, chartObject) {
for (var i = 0; i < dataObject.length; ++i) {
dataObject[i].splice(0, dataObject[i].length)
var series = chartObject.item.chart.series(i)
series.removePoints(0, series.count)
}
}
}
Item {
Layout.fillWidth: true
}
RowLayout {
spacing: 10
Controls.SubtitleLabel {
text: qsTr("Interval, s")
}
Controls.TextField {
id: intervalField
validator: DoubleValidator { bottom: 0.1; top: 100; decimals: 1; locale: "en-US" }
text: "1"
onEditingFinished: timer.interval = parseFloat(text) * 1000
Layout.preferredHeight: 44
}
Controls.ImageButton {
icon.source: "qrc:/Icons/check-indicator.svg"
icon.width: 32
icon.height: 32
Layout.preferredWidth: 42
Layout.preferredHeight: 42
}
}
} }
Connections { Connections {
@@ -395,6 +438,7 @@ ColumnLayout {
Connections { Connections {
target: BmsInterface.commands() target: BmsInterface.commands()
enabled: root.visible
onValuesReceived: { onValuesReceived: {
addValueToChart(values.packVoltage, voltageData, voltageLoader, 0, 0.05) addValueToChart(values.packVoltage, voltageData, voltageLoader, 0, 0.05)
@@ -428,7 +472,7 @@ ColumnLayout {
addValueToChart(cellVoltageArray[j], cellListData, cellListLoader, j, 0.05) addValueToChart(cellVoltageArray[j], cellListData, cellListLoader, j, 0.05)
} }
currentTime += 1 currentTime = Math.round((timer.interval / 1000 + currentTime + Number.EPSILON) * 10) / 10
} }
} }

View File

@@ -2,7 +2,8 @@ pragma Singleton
import QtQuick 2.12 import QtQuick 2.12
QtObject { QtObject {
function roundDouble(value) { function roundDouble(value, decimals = 2) {
return Math.round((value + Number.EPSILON) * 100) / 100 var factor = Math.pow(10, decimals)
return Math.round((value + Number.EPSILON) * factor) / factor
} }
} }

View File

@@ -40,5 +40,6 @@
<file>Controls/MenuItemDelegate.qml</file> <file>Controls/MenuItemDelegate.qml</file>
<file>Controls/ScrollIndicator.qml</file> <file>Controls/ScrollIndicator.qml</file>
<file>Controls/OutlineImageButton.qml</file> <file>Controls/OutlineImageButton.qml</file>
<file>Controls/ImageButton.qml</file>
</qresource> </qresource>
</RCC> </RCC>