557 lines
23 KiB
QML
557 lines
23 KiB
QML
import QtQuick 2.12
|
|
import QtQuick.Controls 2.12
|
|
import QtQuick.Layouts 1.12
|
|
import QtCharts 2.3
|
|
|
|
import Controls 1.0 as Controls
|
|
import Cubo 1.0
|
|
import Utils 1.0
|
|
|
|
ColumnLayout {
|
|
spacing: 20
|
|
|
|
property int currentTime: 0
|
|
|
|
property var voltageData: []
|
|
property var currentData: []
|
|
property var batteryTemperatureData: []
|
|
property var bmsTemperatureData: []
|
|
property var cellVoltageData: []
|
|
property var cellListData: []
|
|
|
|
// https://htmlcolorcodes.com/color-chart/
|
|
property var chartColors: [
|
|
"#c62828", "#6a1b9a", "#283593", "#0277bd", "#00695c", "#558b2f", "#f9a825", "#ef6c00",
|
|
"#4e342e", "#37474f", "#ad1457", "#4527a0", "#1565c0", "#00838f", "#2e7d32", "#9e9d24",
|
|
"#ff8f00", "#d84315", "#424242", "#ef5350", "#ab47bc", "#5c6bc0", "#29b6f6", "#26a69a",
|
|
"#9ccc65", "#ffee58", "#ffa726", "#8d6e63", "#78909c", "#ec407a", "#7e57c2", "#42a5f5",
|
|
]
|
|
|
|
Controls.Frame {
|
|
padding: 20
|
|
leftPadding: 1
|
|
rightPadding: 1
|
|
|
|
ColumnLayout {
|
|
anchors.fill: parent
|
|
|
|
Controls.TabBar {
|
|
id: bar
|
|
implicitWidth: 300
|
|
|
|
Controls.TabButton {
|
|
text: qsTr("Voltage")
|
|
width: bar.width / 6 * 0.9
|
|
}
|
|
|
|
Controls.TabButton {
|
|
text: qsTr("Current")
|
|
width: bar.width / 6 * 0.8
|
|
}
|
|
|
|
Controls.TabButton {
|
|
text: qsTr("Battery temperature")
|
|
width: bar.width / 6 * 1.3
|
|
}
|
|
|
|
Controls.TabButton {
|
|
text: qsTr("BMS temperature")
|
|
width: bar.width / 6 * 1.1
|
|
}
|
|
|
|
Controls.TabButton {
|
|
text: qsTr("Cell voltage")
|
|
width: bar.width / 6 * 1
|
|
}
|
|
|
|
Controls.TabButton {
|
|
text: qsTr("Cell list")
|
|
width: bar.width / 6 * 0.85
|
|
}
|
|
|
|
Layout.fillWidth: true
|
|
}
|
|
|
|
StackLayout {
|
|
width: parent.width
|
|
currentIndex: bar.currentIndex
|
|
|
|
Component {
|
|
id: legendDelegate
|
|
|
|
RowLayout {
|
|
width: ListView.view ? ListView.view.width : 200
|
|
height: 60
|
|
|
|
property Controls.ChartView chartItem: undefined
|
|
property color seriesColor: "black"
|
|
property bool horizontal: true
|
|
|
|
Controls.CheckBox {
|
|
id: checkSeries
|
|
checked: true
|
|
spacing: 10
|
|
|
|
onCheckedChanged: {
|
|
if (checked) {
|
|
chartItem.series(modelData).opacity = 1
|
|
} else {
|
|
chartItem.series(modelData).opacity = 0
|
|
}
|
|
}
|
|
Layout.fillWidth: !horizontal
|
|
}
|
|
|
|
Rectangle {
|
|
color: seriesColor
|
|
radius: width / 2
|
|
Layout.preferredWidth: 18
|
|
Layout.preferredHeight: 18
|
|
Layout.rightMargin: 20
|
|
}
|
|
|
|
Component.onCompleted: {
|
|
chartItem = ListView.view ? ListView.view.chartItem : parent.chartItem
|
|
horizontal = !ListView.view
|
|
seriesColor = chartItem.series(modelData).color
|
|
checkSeries.text = Qt.binding(function(){ return chartItem.series(modelData).name })
|
|
}
|
|
}
|
|
}
|
|
|
|
Component {
|
|
id: chartComponent
|
|
|
|
RowLayout {
|
|
property string xLabel: ""
|
|
property string yLabel: ""
|
|
property Controls.ChartView chart: chartView
|
|
property int seriesCount: 0
|
|
property bool horizontalLegend: true
|
|
|
|
ColumnLayout {
|
|
spacing: -20
|
|
|
|
Controls.ContentLabel {
|
|
z: 1
|
|
text: yLabel
|
|
Layout.topMargin: 20
|
|
Layout.leftMargin: 30
|
|
Layout.alignment: Qt.AlignLeft
|
|
}
|
|
|
|
Controls.ChartView {
|
|
id: chartView
|
|
Layout.fillWidth: true
|
|
Layout.fillHeight: true
|
|
}
|
|
|
|
Controls.ContentLabel {
|
|
z: 1
|
|
text: xLabel
|
|
Layout.alignment: Qt.AlignRight
|
|
Layout.rightMargin: 30
|
|
Layout.bottomMargin: 10
|
|
}
|
|
|
|
RowLayout {
|
|
visible: horizontalLegend
|
|
spacing: 0
|
|
|
|
property Controls.ChartView chartItem: chart
|
|
|
|
Repeater {
|
|
model: seriesCount
|
|
delegate: legendDelegate
|
|
}
|
|
|
|
Layout.topMargin: 20
|
|
Layout.leftMargin: 20
|
|
}
|
|
|
|
Layout.fillWidth: true
|
|
Layout.fillHeight: true
|
|
}
|
|
|
|
ListView {
|
|
clip: true
|
|
model: seriesCount
|
|
delegate: legendDelegate
|
|
visible: !horizontalLegend
|
|
boundsBehavior: Flickable.StopAtBounds
|
|
ScrollBar.vertical: Controls.ScrollBar {}
|
|
|
|
property Controls.ChartView chartItem: chart
|
|
|
|
Layout.preferredWidth: 200
|
|
Layout.fillHeight: true
|
|
}
|
|
}
|
|
}
|
|
|
|
Loader {
|
|
id: voltageLoader
|
|
sourceComponent: chartComponent
|
|
|
|
Layout.fillWidth: true
|
|
Layout.fillHeight: true
|
|
|
|
Component.onCompleted: {
|
|
item.xLabel = Qt.binding(function() { return qsTr("Time, s") })
|
|
item.yLabel = Qt.binding(function() { return qsTr("Voltage, V") })
|
|
}
|
|
}
|
|
|
|
Loader {
|
|
id: currentLoader
|
|
sourceComponent: chartComponent
|
|
|
|
Layout.fillWidth: true
|
|
Layout.fillHeight: true
|
|
|
|
Component.onCompleted: {
|
|
item.xLabel = Qt.binding(function() { return qsTr("Time, s") })
|
|
item.yLabel = Qt.binding(function() { return qsTr("Current, A") })
|
|
}
|
|
}
|
|
|
|
Loader {
|
|
id: batteryTemperatureLoader
|
|
sourceComponent: chartComponent
|
|
|
|
Layout.fillWidth: true
|
|
Layout.fillHeight: true
|
|
|
|
Component.onCompleted: {
|
|
item.xLabel = Qt.binding(function() { return qsTr("Time, s") })
|
|
item.yLabel = Qt.binding(function() { return qsTr("Temperature, °C") })
|
|
}
|
|
}
|
|
|
|
Loader {
|
|
id: bmsTemperatureLoader
|
|
sourceComponent: chartComponent
|
|
|
|
Layout.fillWidth: true
|
|
Layout.fillHeight: true
|
|
|
|
Component.onCompleted: {
|
|
item.xLabel = Qt.binding(function() { return qsTr("Time, s") })
|
|
item.yLabel = Qt.binding(function() { return qsTr("Temperature, °C") })
|
|
}
|
|
}
|
|
|
|
Loader {
|
|
id: cellVoltageLoader
|
|
sourceComponent: chartComponent
|
|
|
|
Layout.fillWidth: true
|
|
Layout.fillHeight: true
|
|
|
|
Component.onCompleted: {
|
|
item.xLabel = Qt.binding(function() { return qsTr("Time, s") })
|
|
item.yLabel = Qt.binding(function() { return qsTr("Voltage, V") })
|
|
}
|
|
}
|
|
|
|
Loader {
|
|
id: cellListLoader
|
|
sourceComponent: chartComponent
|
|
|
|
Layout.fillWidth: true
|
|
Layout.fillHeight: true
|
|
|
|
Component.onCompleted: {
|
|
item.xLabel = Qt.binding(function() { return qsTr("Time, s") })
|
|
item.yLabel = Qt.binding(function() { return qsTr("Voltage, V") })
|
|
item.horizontalLegend = false
|
|
}
|
|
}
|
|
|
|
Layout.fillWidth: true
|
|
Layout.fillHeight: true
|
|
}
|
|
}
|
|
|
|
Layout.fillWidth: true
|
|
Layout.fillHeight: true
|
|
}
|
|
|
|
RowLayout {
|
|
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 {
|
|
id: resetZoomButton
|
|
text: qsTr("Reset zoom")
|
|
onClicked: {
|
|
resetChartZoom(voltageData, voltageLoader)
|
|
resetChartZoom(currentData, currentLoader)
|
|
resetChartZoom(batteryTemperatureData, batteryTemperatureLoader)
|
|
resetChartZoom(bmsTemperatureData, bmsTemperatureLoader)
|
|
resetChartZoom(cellVoltageData, cellVoltageLoader)
|
|
resetChartZoom(cellListData, cellListLoader)
|
|
}
|
|
|
|
function resetChartZoom(dataObject, chartObject) {
|
|
chartObject.item.chart.zoomReset()
|
|
chartObject.item.chart.autoScaling = true
|
|
chartObject.item.chart.axes[1].min = closestMinValue(getMinValue(dataObject), 0.05)
|
|
chartObject.item.chart.axes[1].max = closestMaxValue(getMaxValue(dataObject), 0.05)
|
|
chartObject.item.chart.axes[0].min = 0
|
|
chartObject.item.chart.axes[0].max = Math.max(chartObject.item.chart.defaultXMax, chartObject.item.chart.series(0).at(chartObject.item.chart.series(0).count - 1).x)
|
|
}
|
|
|
|
function getMaxValue(dataObject, seriesIndex) {
|
|
if (dataObject.length === 0 || dataObject[0].length === 0) {
|
|
return 0
|
|
}
|
|
|
|
var max = dataObject[0][0].y
|
|
for (var i = 0; i < dataObject.length; ++i) {
|
|
for (var j = 0; j < dataObject[i].length; ++j) {
|
|
if (dataObject[i][j].y > max) {
|
|
max = dataObject[i][j].y
|
|
}
|
|
}
|
|
}
|
|
|
|
return max
|
|
}
|
|
|
|
function getMinValue(dataObject) {
|
|
if (dataObject.length === 0 || dataObject[0].length === 0) {
|
|
return 0
|
|
}
|
|
|
|
var min = dataObject[0][0].y
|
|
for (var i = 0; i < dataObject.length; ++i) {
|
|
for (var j = 0; j < dataObject[i].length; ++j) {
|
|
if (dataObject[i][j].y < min) {
|
|
min = dataObject[i][j].y
|
|
}
|
|
}
|
|
}
|
|
|
|
return min
|
|
}
|
|
}
|
|
}
|
|
|
|
Connections {
|
|
target: Translator
|
|
onCurrentLanguageChanged: {
|
|
for (var i = 0; i < cellListLoader.item.chart.count; ++i) {
|
|
cellListLoader.item.chart.series(i).name = qsTr("Cell #") + (i + 1).toString()
|
|
}
|
|
}
|
|
}
|
|
|
|
Connections {
|
|
target: BmsInterface.commands()
|
|
|
|
onValuesReceived: {
|
|
addValueToChart(values.packVoltage, voltageData, voltageLoader, 0, 0.05)
|
|
addValueToChart(values.packCurrent, currentData, currentLoader, 0, 0.05)
|
|
|
|
addValueToChart(values.tempBMSHigh, batteryTemperatureData, batteryTemperatureLoader, 0, 0.05)
|
|
addValueToChart(values.tempBMSAverage, batteryTemperatureData, batteryTemperatureLoader, 1, 0.05)
|
|
addValueToChart(values.tempBMSLow, batteryTemperatureData, batteryTemperatureLoader, 2, 0.05)
|
|
|
|
addValueToChart(values.tempBattHigh, bmsTemperatureData, bmsTemperatureLoader, 0, 0.05)
|
|
addValueToChart(values.tempBattAverage, bmsTemperatureData, bmsTemperatureLoader, 1, 0.05)
|
|
addValueToChart(values.tempBattLow, bmsTemperatureData, bmsTemperatureLoader, 2, 0.05)
|
|
|
|
addValueToChart(values.cVHigh, cellVoltageData, cellVoltageLoader, 0, 0.05)
|
|
addValueToChart(values.cVAverage, cellVoltageData, cellVoltageLoader, 1, 0.05)
|
|
addValueToChart(values.cVLow, cellVoltageData, cellVoltageLoader, 2, 0.05)
|
|
}
|
|
|
|
onCellsReceived: {
|
|
for (var i = cellListLoader.item.chart.count; i < cellCount; ++i) {
|
|
cellListLoader.item.chart.createSeries(ChartView.SeriesTypeLine, qsTr("Cell #") + (i + 1).toString(),
|
|
cellListLoader.item.chart.xAxis, cellListLoader.item.chart.yAxis)
|
|
if (i < chartColors.length) {
|
|
cellListLoader.item.chart.series(i).color = chartColors[i]
|
|
}
|
|
cellListLoader.item.chart.axes[0].max = 10
|
|
cellListLoader.item.seriesCount = cellListLoader.item.chart.count
|
|
}
|
|
|
|
for (var j = 0; j < cellCount; ++j) {
|
|
addValueToChart(cellVoltageArray[j], cellListData, cellListLoader, j, 0.05)
|
|
}
|
|
|
|
currentTime += 1
|
|
}
|
|
}
|
|
|
|
function addValueToChart(value, dataObject, chartObject, seriesIndex, yScale) {
|
|
if (seriesIndex >= dataObject.length) {
|
|
for (var i = dataObject.length; i <= seriesIndex; ++i) {
|
|
dataObject.push([])
|
|
}
|
|
}
|
|
|
|
var seriesData = dataObject[seriesIndex]
|
|
seriesData.push(Qt.point(currentTime, value))
|
|
chartObject.item.chart.series(seriesIndex).append(currentTime, value)
|
|
|
|
if (currentTime > chartObject.item.chart.defaultXMax && chartObject.item.chart.autoScaling) {
|
|
chartObject.item.chart.axes[0].max = currentTime
|
|
}
|
|
|
|
if (seriesData.length < 2 || (value >= chartObject.item.chart.axes[1].max && chartObject.item.chart.autoScaling)) {
|
|
chartObject.item.chart.axes[1].max = closestMaxValue(value, yScale)
|
|
}
|
|
|
|
if (seriesData.length < 2 || (value <= chartObject.item.chart.axes[1].min && chartObject.item.chart.autoScaling)) {
|
|
chartObject.item.chart.axes[1].min = closestMinValue(value, yScale)
|
|
}
|
|
}
|
|
|
|
function closestMaxValue(value, scale) {
|
|
return value === 0 ? 1 : Math.ceil(value * (value > 0 ? (1 + scale) : (1 - scale)))
|
|
}
|
|
|
|
function closestMinValue(value, scale) {
|
|
return value === 0 ? -1 : Math.floor(value * (value > 0 ? (1 - scale) : (1 + scale)))
|
|
}
|
|
|
|
Timer {
|
|
id: timer
|
|
running: true
|
|
interval: 1000
|
|
triggeredOnStart: true
|
|
|
|
onTriggered: {
|
|
BmsInterface.commands().getValues()
|
|
BmsInterface.commands().getCells()
|
|
Qt.callLater(start)
|
|
}
|
|
}
|
|
|
|
Connections {
|
|
target: BmsInterface
|
|
onPortConnectedChanged: getValues()
|
|
}
|
|
|
|
onVisibleChanged: getValues()
|
|
|
|
function getValues() {
|
|
if (BmsInterface.isPortConnected() && visible) {
|
|
timer.start()
|
|
} else {
|
|
timer.stop()
|
|
}
|
|
}
|
|
|
|
Component.onCompleted: {
|
|
voltageLoader.item.chart.createSeries(ChartView.SeriesTypeLine, qsTr("Voltage indicator"),
|
|
voltageLoader.item.chart.xAxis, voltageLoader.item.chart.yAxis)
|
|
voltageLoader.item.chart.series(0).name = Qt.binding(function(){ return qsTr("Voltage indicator") })
|
|
voltageLoader.item.chart.series(0).color = "#fbc02d"
|
|
voltageLoader.item.chart.axes[0].max = 10
|
|
voltageLoader.item.seriesCount = voltageLoader.item.chart.count
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
currentLoader.item.chart.createSeries(ChartView.SeriesTypeLine, qsTr("Current indicator"),
|
|
currentLoader.item.chart.xAxis, currentLoader.item.chart.yAxis)
|
|
currentLoader.item.chart.series(0).name = Qt.binding(function(){ return qsTr("Current indicator") })
|
|
currentLoader.item.chart.series(0).color = "#e64a19"
|
|
currentLoader.item.chart.axes[0].max = 10
|
|
currentLoader.item.seriesCount = currentLoader.item.chart.count
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
batteryTemperatureLoader.item.chart.createSeries(ChartView.SeriesTypeLine, qsTr("Maximum temperature"),
|
|
batteryTemperatureLoader.item.chart.xAxis, batteryTemperatureLoader.item.chart.yAxis)
|
|
batteryTemperatureLoader.item.chart.createSeries(ChartView.SeriesTypeLine, qsTr("Average temperature"),
|
|
batteryTemperatureLoader.item.chart.xAxis, batteryTemperatureLoader.item.chart.yAxis)
|
|
batteryTemperatureLoader.item.chart.createSeries(ChartView.SeriesTypeLine, qsTr("Minimum temperature"),
|
|
batteryTemperatureLoader.item.chart.xAxis, batteryTemperatureLoader.item.chart.yAxis)
|
|
|
|
batteryTemperatureLoader.item.chart.series(0).name = Qt.binding(function(){ return qsTr("Maximum temperature") })
|
|
batteryTemperatureLoader.item.chart.series(1).name = Qt.binding(function(){ return qsTr("Average temperature") })
|
|
batteryTemperatureLoader.item.chart.series(2).name = Qt.binding(function(){ return qsTr("Minimum temperature") })
|
|
batteryTemperatureLoader.item.chart.series(0).color = "#e64a19"
|
|
batteryTemperatureLoader.item.chart.series(1).color = "#fbc02d"
|
|
batteryTemperatureLoader.item.chart.series(2).color = "#388e3c"
|
|
|
|
batteryTemperatureLoader.item.chart.axes[0].max = 10
|
|
batteryTemperatureLoader.item.seriesCount = batteryTemperatureLoader.item.chart.count
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
bmsTemperatureLoader.item.chart.createSeries(ChartView.SeriesTypeLine, qsTr("Maximum temperature"),
|
|
bmsTemperatureLoader.item.chart.xAxis, bmsTemperatureLoader.item.chart.yAxis)
|
|
bmsTemperatureLoader.item.chart.createSeries(ChartView.SeriesTypeLine, qsTr("Average temperature"),
|
|
bmsTemperatureLoader.item.chart.xAxis, bmsTemperatureLoader.item.chart.yAxis)
|
|
bmsTemperatureLoader.item.chart.createSeries(ChartView.SeriesTypeLine, qsTr("Minimum temperature"),
|
|
bmsTemperatureLoader.item.chart.xAxis, bmsTemperatureLoader.item.chart.yAxis)
|
|
|
|
bmsTemperatureLoader.item.chart.series(0).name = Qt.binding(function(){ return qsTr("Maximum temperature") })
|
|
bmsTemperatureLoader.item.chart.series(1).name = Qt.binding(function(){ return qsTr("Average temperature") })
|
|
bmsTemperatureLoader.item.chart.series(2).name = Qt.binding(function(){ return qsTr("Minimum temperature") })
|
|
bmsTemperatureLoader.item.chart.series(0).color = "#e64a19"
|
|
bmsTemperatureLoader.item.chart.series(1).color = "#fbc02d"
|
|
bmsTemperatureLoader.item.chart.series(2).color = "#388e3c"
|
|
|
|
bmsTemperatureLoader.item.chart.axes[0].max = 10
|
|
bmsTemperatureLoader.item.seriesCount = bmsTemperatureLoader.item.chart.count
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
cellVoltageLoader.item.chart.createSeries(ChartView.SeriesTypeLine, qsTr("Maximum voltage"),
|
|
cellVoltageLoader.item.chart.xAxis, cellVoltageLoader.item.chart.yAxis)
|
|
cellVoltageLoader.item.chart.createSeries(ChartView.SeriesTypeLine, qsTr("Average voltage"),
|
|
cellVoltageLoader.item.chart.xAxis, cellVoltageLoader.item.chart.yAxis)
|
|
cellVoltageLoader.item.chart.createSeries(ChartView.SeriesTypeLine, qsTr("Minimum voltage"),
|
|
cellVoltageLoader.item.chart.xAxis, cellVoltageLoader.item.chart.yAxis)
|
|
cellVoltageLoader.item.chart.series(0).color = "#e64a19"
|
|
cellVoltageLoader.item.chart.series(1).color = "#fbc02d"
|
|
cellVoltageLoader.item.chart.series(2).color = "#388e3c"
|
|
|
|
cellVoltageLoader.item.chart.series(0).name = Qt.binding(function(){ return qsTr("Maximum voltage") })
|
|
cellVoltageLoader.item.chart.series(1).name = Qt.binding(function(){ return qsTr("Average voltage") })
|
|
cellVoltageLoader.item.chart.series(2).name = Qt.binding(function(){ return qsTr("Minimum voltage") })
|
|
|
|
cellVoltageLoader.item.chart.axes[0].max = 10
|
|
cellVoltageLoader.item.seriesCount = cellVoltageLoader.item.chart.count
|
|
}
|
|
}
|