1062 lines
47 KiB
QML
1062 lines
47 KiB
QML
import QtQuick 2.12
|
|
import QtQuick.Controls 2.12
|
|
import QtQuick.Layouts 1.12
|
|
import QtQuick.Dialogs 1.2
|
|
import Qt.labs.settings 1.1
|
|
|
|
import Controls 1.0 as Controls
|
|
import Cubo 1.0
|
|
import Utils 1.0
|
|
|
|
RowLayout {
|
|
property real contentPadding: 35
|
|
property real contentRowSpacing: 20
|
|
property real contentColumnSpacing: 35
|
|
|
|
signal needWait(bool active, string text)
|
|
|
|
spacing: contentColumnSpacing - 10
|
|
|
|
Flickable {
|
|
id: settingsFlickable
|
|
clip: true
|
|
contentWidth: width - rightMargin - leftMargin
|
|
contentHeight: configLayout.height
|
|
boundsBehavior: Flickable.StopAtBounds
|
|
rightMargin: 10
|
|
|
|
ColumnLayout {
|
|
id: configLayout
|
|
width: parent.width
|
|
spacing: 15
|
|
|
|
Controls.Frame {
|
|
id: serialNumberSettingsFrame
|
|
padding: contentPadding
|
|
implicitWidth: parent.width
|
|
Layout.fillWidth: true
|
|
|
|
RowLayout {
|
|
spacing: contentColumnSpacing
|
|
anchors.fill: parent
|
|
|
|
Controls.TitleLabel {
|
|
text: qsTr("Serial number")
|
|
Layout.fillWidth: true
|
|
Layout.maximumWidth: (parent.width - parent.spacing) / 2
|
|
}
|
|
|
|
Controls.TextField {
|
|
id: serialNumberField
|
|
validator: RegExpValidator { regExp: /[1-9][0-9]*/ }
|
|
Layout.fillWidth: true
|
|
Layout.maximumWidth: (parent.width - parent.spacing) / 2
|
|
}
|
|
}
|
|
}
|
|
|
|
Controls.Frame {
|
|
id: configurationSettingsFrame
|
|
padding: contentPadding
|
|
implicitWidth: parent.width
|
|
Layout.fillWidth: true
|
|
|
|
GridLayout {
|
|
id: configurationLayout
|
|
columns: 2
|
|
rowSpacing: contentRowSpacing
|
|
columnSpacing: contentColumnSpacing
|
|
anchors.fill: parent
|
|
|
|
Controls.TitleLabel {
|
|
text: qsTr("Configuration")
|
|
Layout.fillWidth: true
|
|
Layout.columnSpan: 2
|
|
}
|
|
|
|
Controls.SubtitleLabel {
|
|
text: qsTr("Number of boards")
|
|
Layout.fillWidth: true
|
|
Layout.maximumWidth: (parent.width - parent.columnSpacing) / 2
|
|
}
|
|
|
|
Controls.SubtitleLabel {
|
|
text: qsTr("Number of cells")
|
|
Layout.fillWidth: true
|
|
Layout.maximumWidth: (parent.width - parent.columnSpacing) / 2
|
|
}
|
|
|
|
Controls.TextField {
|
|
id: numberOfBoardsField
|
|
validator: IntValidator { bottom: 0 }
|
|
Layout.fillWidth: true
|
|
Layout.maximumWidth: (parent.width - parent.columnSpacing) / 2
|
|
|
|
onTextChanged: configurationLayout.recalculateSensorsMasks()
|
|
}
|
|
|
|
Controls.TextField {
|
|
id: numberOfCellsField
|
|
validator: IntValidator { bottom: 0 }
|
|
Layout.fillWidth: true
|
|
Layout.maximumWidth: (parent.width - parent.columnSpacing) / 2
|
|
}
|
|
|
|
// Repeater {
|
|
// id: bmsSensorsMaskRepeater
|
|
|
|
// property var bmsSensorsMaskModel: []
|
|
|
|
// ColumnLayout {
|
|
// id: bmsSensorsMaskLayout
|
|
// Layout.fillWidth: true
|
|
|
|
// property var boardIndex: index
|
|
|
|
// Controls.SubtitleLabel {
|
|
// text: index === 0 ? qsTr("BMS sensors mask for master board") : qsTr("BMS sensors mask for slave board #") + index
|
|
// maximumLineCount: 2
|
|
// wrapMode: Text.WordWrap
|
|
// Layout.fillWidth: true
|
|
// }
|
|
|
|
// Controls.Frame {
|
|
// padding: 10
|
|
// Layout.fillWidth: true
|
|
// Layout.fillHeight: true
|
|
|
|
// ColumnLayout {
|
|
// spacing: 10
|
|
// anchors.fill: parent
|
|
|
|
// Repeater {
|
|
// model: 4
|
|
// Controls.CheckBox {
|
|
// text: qsTr("#") + (index + 1)
|
|
// opacity: (bmsSensorsMaskLayout.boardIndex === 0 && (index === 2 || index === 3)) ? 0 : 1
|
|
// enabled: opacity
|
|
// checked: bmsSensorsMaskRepeater.bmsSensorsMaskModel[bmsSensorsMaskLayout.boardIndex * 4 + index]
|
|
// onCheckedChanged: bmsSensorsMaskRepeater.bmsSensorsMaskModel[bmsSensorsMaskLayout.boardIndex * 4 + index] = checked
|
|
// }
|
|
// }
|
|
|
|
// Item {
|
|
// visible: index === 0
|
|
// Layout.fillHeight: true
|
|
// }
|
|
// }
|
|
// }
|
|
// }
|
|
// }
|
|
|
|
Repeater {
|
|
id: batterySensorsMaskRepeater
|
|
|
|
property var batterySensorsMaskModel: []
|
|
|
|
ColumnLayout {
|
|
id: batterySensorsMaskLayout
|
|
Layout.fillWidth: true
|
|
|
|
property var boardIndex: index
|
|
|
|
Controls.SubtitleLabel {
|
|
text: index === 0 ? qsTr("Battery sensors mask for master board") : qsTr("Battery sensors mask for slave board #") + index
|
|
maximumLineCount: 2
|
|
wrapMode: Text.WordWrap
|
|
Layout.fillWidth: true
|
|
}
|
|
|
|
Controls.Frame {
|
|
padding: 10
|
|
Layout.fillWidth: true
|
|
Layout.fillHeight: true
|
|
|
|
ColumnLayout {
|
|
spacing: 10
|
|
anchors.fill: parent
|
|
|
|
Repeater {
|
|
model: 4
|
|
Controls.CheckBox {
|
|
text: qsTr("#") + (index + 1)
|
|
enabled: opacity
|
|
opacity: (batterySensorsMaskLayout.boardIndex === 0 && (index === 2 || index === 3)) ? 0 : 1
|
|
checked: batterySensorsMaskRepeater.batterySensorsMaskModel[batterySensorsMaskLayout.boardIndex * 4 + index]
|
|
onCheckedChanged: batterySensorsMaskRepeater.batterySensorsMaskModel[batterySensorsMaskLayout.boardIndex * 4 + index] = checked
|
|
}
|
|
}
|
|
|
|
Item {
|
|
visible: index === 0
|
|
Layout.fillHeight: true
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
function recalculateSensorsMasks() {
|
|
// const bmsSensorsModel = bmsSensorsMaskRepeater.bmsSensorsMaskModel
|
|
const batterySensorsModel = batterySensorsMaskRepeater.batterySensorsMaskModel
|
|
|
|
const newSize = parseInt(numberOfBoardsField.text) * 4
|
|
if (newSize) {
|
|
// arrayResize(bmsSensorsModel, newSize, false)
|
|
arrayResize(batterySensorsModel, newSize, true)
|
|
|
|
// bmsSensorsMaskRepeater.model = 0
|
|
// bmsSensorsMaskRepeater.bmsSensorsMaskModel = bmsSensorsModel
|
|
// bmsSensorsMaskRepeater.model = parseInt(numberOfBoardsField.text)
|
|
|
|
batterySensorsMaskRepeater.model = 0
|
|
batterySensorsMaskRepeater.batterySensorsMaskModel = batterySensorsModel
|
|
batterySensorsMaskRepeater.model = parseInt(numberOfBoardsField.text)
|
|
}
|
|
}
|
|
|
|
function arrayResize(array, size, value) {
|
|
while (array.length > size) { array.pop(); }
|
|
while (array.length < size) { array.push(value); }
|
|
}
|
|
}
|
|
}
|
|
|
|
Controls.Frame {
|
|
id: socSettingsFrame
|
|
padding: contentPadding
|
|
implicitWidth: parent.width
|
|
Layout.fillWidth: true
|
|
|
|
GridLayout {
|
|
columns: 2
|
|
rowSpacing: contentRowSpacing
|
|
columnSpacing: contentColumnSpacing
|
|
anchors.fill: parent
|
|
|
|
Controls.TitleLabel {
|
|
text: qsTr("SOC")
|
|
Layout.fillWidth: true
|
|
Layout.columnSpan: 2
|
|
}
|
|
|
|
Controls.SubtitleLabel {
|
|
text: qsTr("Number of cells connected in parallel")
|
|
maximumLineCount: 2
|
|
wrapMode: Text.WordWrap
|
|
Layout.fillWidth: true
|
|
Layout.maximumWidth: (parent.width - parent.columnSpacing) / 2
|
|
}
|
|
|
|
Controls.SubtitleLabel {
|
|
text: qsTr("Battery capacity")
|
|
Layout.fillWidth: true
|
|
Layout.maximumWidth: (parent.width - parent.columnSpacing) / 2
|
|
}
|
|
|
|
Controls.TextField {
|
|
id: numberOfParallelCellsField
|
|
validator: IntValidator {}
|
|
Layout.fillWidth: true
|
|
Layout.maximumWidth: (parent.width - parent.columnSpacing) / 2
|
|
}
|
|
|
|
Controls.TextField {
|
|
id: batteryCapacityField
|
|
validator: DoubleValidator { decimals: 2; locale: "en-US"; notation: DoubleValidator.StandardNotation }
|
|
Layout.fillWidth: true
|
|
Layout.maximumWidth: (parent.width - parent.columnSpacing) / 2
|
|
}
|
|
}
|
|
}
|
|
|
|
Controls.Frame {
|
|
id: limitSettingsFrame
|
|
padding: contentPadding
|
|
implicitWidth: parent.width
|
|
Layout.fillWidth: true
|
|
|
|
GridLayout {
|
|
columns: 2
|
|
rowSpacing: contentRowSpacing
|
|
columnSpacing: contentColumnSpacing
|
|
anchors.fill: parent
|
|
|
|
Controls.TitleLabel {
|
|
text: qsTr("Limits")
|
|
Layout.fillWidth: true
|
|
Layout.columnSpan: 2
|
|
}
|
|
|
|
Controls.SubtitleLabel {
|
|
text: qsTr("Maximum charge current, A")
|
|
maximumLineCount: 2
|
|
wrapMode: Text.WordWrap
|
|
Layout.fillWidth: true
|
|
Layout.maximumWidth: (parent.width - parent.columnSpacing) / 2
|
|
}
|
|
|
|
Controls.SubtitleLabel {
|
|
text: qsTr("Maximum load current, A")
|
|
maximumLineCount: 2
|
|
wrapMode: Text.WordWrap
|
|
Layout.fillWidth: true
|
|
Layout.maximumWidth: (parent.width - parent.columnSpacing) / 2
|
|
}
|
|
|
|
Controls.TextField {
|
|
id: maximumChargeCurrentField
|
|
validator: DoubleValidator { decimals: 2; locale: "en-US"; notation: DoubleValidator.StandardNotation }
|
|
Layout.fillWidth: true
|
|
Layout.maximumWidth: (parent.width - parent.columnSpacing) / 2
|
|
}
|
|
|
|
Controls.TextField {
|
|
id: maximumLoadCurrentField
|
|
validator: DoubleValidator { decimals: 2; locale: "en-US"; notation: DoubleValidator.StandardNotation }
|
|
Layout.fillWidth: true
|
|
Layout.maximumWidth: (parent.width - parent.columnSpacing) / 2
|
|
}
|
|
|
|
Controls.SubtitleLabel {
|
|
text: qsTr("Maximum temperature, °C")
|
|
maximumLineCount: 2
|
|
wrapMode: Text.WordWrap
|
|
Layout.fillWidth: true
|
|
Layout.maximumWidth: (parent.width - parent.columnSpacing) / 2
|
|
Layout.columnSpan: 2
|
|
}
|
|
|
|
Controls.TextField {
|
|
id: maximumTemperatureField
|
|
validator: DoubleValidator { decimals: 2; locale: "en-US"; notation: DoubleValidator.StandardNotation }
|
|
Layout.fillWidth: true
|
|
Layout.maximumWidth: (parent.width - parent.columnSpacing) / 2
|
|
Layout.columnSpan: 2
|
|
}
|
|
}
|
|
}
|
|
|
|
Controls.Frame {
|
|
id: cellSettingsFrame
|
|
padding: contentPadding
|
|
implicitWidth: parent.width
|
|
Layout.fillWidth: true
|
|
|
|
GridLayout {
|
|
columns: 2
|
|
rowSpacing: contentRowSpacing
|
|
columnSpacing: contentColumnSpacing
|
|
anchors.fill: parent
|
|
|
|
Controls.TitleLabel {
|
|
text: qsTr("Cell configuration")
|
|
Layout.fillWidth: true
|
|
Layout.columnSpan: 2
|
|
}
|
|
|
|
Controls.SubtitleLabel {
|
|
text: qsTr("Lower disable threshold, V")
|
|
maximumLineCount: 2
|
|
wrapMode: Text.WordWrap
|
|
Layout.fillWidth: true
|
|
Layout.maximumWidth: (parent.width - parent.columnSpacing) / 2
|
|
}
|
|
|
|
Controls.SubtitleLabel {
|
|
text: qsTr("Upper disable threshold, V")
|
|
maximumLineCount: 2
|
|
wrapMode: Text.WordWrap
|
|
Layout.fillWidth: true
|
|
Layout.maximumWidth: (parent.width - parent.columnSpacing) / 2
|
|
}
|
|
|
|
Controls.TextField {
|
|
id: lowerDisableThresholdField
|
|
validator: DoubleValidator { decimals: 2; locale: "en-US"; notation: DoubleValidator.StandardNotation }
|
|
Layout.fillWidth: true
|
|
Layout.maximumWidth: (parent.width - parent.columnSpacing) / 2
|
|
}
|
|
|
|
Controls.TextField {
|
|
id: upperDisableThresholdField
|
|
validator: DoubleValidator { decimals: 2; locale: "en-US"; notation: DoubleValidator.StandardNotation }
|
|
Layout.fillWidth: true
|
|
Layout.maximumWidth: (parent.width - parent.columnSpacing) / 2
|
|
}
|
|
|
|
Controls.SubtitleLabel {
|
|
text: qsTr("Lower enable threshold (should be higher than disable), V")
|
|
maximumLineCount: 3
|
|
wrapMode: Text.WordWrap
|
|
Layout.fillWidth: true
|
|
Layout.maximumWidth: (parent.width - parent.columnSpacing) / 2
|
|
}
|
|
|
|
Controls.SubtitleLabel {
|
|
text: qsTr("Upper enable threshold (should be lower than disable), V")
|
|
maximumLineCount: 3
|
|
wrapMode: Text.WordWrap
|
|
Layout.fillWidth: true
|
|
Layout.maximumWidth: (parent.width - parent.columnSpacing) / 2
|
|
}
|
|
|
|
Controls.TextField {
|
|
id: lowerEnableThresholdField
|
|
validator: DoubleValidator { decimals: 2; locale: "en-US"; notation: DoubleValidator.StandardNotation }
|
|
Layout.fillWidth: true
|
|
Layout.maximumWidth: (parent.width - parent.columnSpacing) / 2
|
|
}
|
|
|
|
Controls.TextField {
|
|
id: upperEnableThresholdField
|
|
validator: DoubleValidator { decimals: 2; locale: "en-US"; notation: DoubleValidator.StandardNotation }
|
|
Layout.fillWidth: true
|
|
Layout.maximumWidth: (parent.width - parent.columnSpacing) / 2
|
|
}
|
|
}
|
|
}
|
|
|
|
Controls.Frame {
|
|
id: balancingSettingsFrame
|
|
padding: contentPadding
|
|
implicitWidth: parent.width
|
|
Layout.fillWidth: true
|
|
|
|
GridLayout {
|
|
columns: 2
|
|
rowSpacing: contentRowSpacing
|
|
columnSpacing: contentColumnSpacing
|
|
anchors.fill: parent
|
|
|
|
Controls.TitleLabel {
|
|
text: qsTr("Balancing configuration")
|
|
Layout.fillWidth: true
|
|
Layout.columnSpan: 2
|
|
}
|
|
|
|
Controls.SubtitleLabel {
|
|
text: qsTr("Balancing start voltage, V")
|
|
maximumLineCount: 2
|
|
wrapMode: Text.WordWrap
|
|
Layout.fillWidth: true
|
|
Layout.maximumWidth: (parent.width - parent.columnSpacing) / 2
|
|
}
|
|
|
|
Controls.SubtitleLabel {
|
|
text: qsTr("Cell voltage delta to start balancing, V")
|
|
maximumLineCount: 2
|
|
wrapMode: Text.WordWrap
|
|
Layout.fillWidth: true
|
|
Layout.maximumWidth: (parent.width - parent.columnSpacing) / 2
|
|
}
|
|
|
|
Controls.TextField {
|
|
id: balancingStartVoltageField
|
|
validator: DoubleValidator { decimals: 2; locale: "en-US"; notation: DoubleValidator.StandardNotation }
|
|
Layout.fillWidth: true
|
|
Layout.maximumWidth: (parent.width - parent.columnSpacing) / 2
|
|
}
|
|
|
|
Controls.TextField {
|
|
id: balancingStartDeltaVoltageField
|
|
validator: DoubleValidator { decimals: 2; locale: "en-US"; notation: DoubleValidator.StandardNotation }
|
|
Layout.fillWidth: true
|
|
Layout.maximumWidth: (parent.width - parent.columnSpacing) / 2
|
|
}
|
|
|
|
Controls.SubtitleLabel {
|
|
text: qsTr("Cell balancing interval, ms")
|
|
maximumLineCount: 2
|
|
wrapMode: Text.WordWrap
|
|
Layout.fillWidth: true
|
|
Layout.maximumWidth: (parent.width - parent.columnSpacing) / 2
|
|
Layout.columnSpan: 2
|
|
}
|
|
|
|
Controls.TextField {
|
|
id: balancingCellIntervalField
|
|
validator: IntValidator {}
|
|
Layout.fillWidth: true
|
|
Layout.maximumWidth: (parent.width - parent.columnSpacing) / 2
|
|
Layout.columnSpan: 2
|
|
}
|
|
}
|
|
}
|
|
|
|
Controls.Frame {
|
|
id: currentConfigurationFrame
|
|
padding: contentPadding
|
|
implicitWidth: parent.width
|
|
Layout.fillWidth: true
|
|
|
|
GridLayout {
|
|
columns: 2
|
|
rowSpacing: contentRowSpacing
|
|
columnSpacing: contentColumnSpacing
|
|
anchors.fill: parent
|
|
|
|
Controls.TitleLabel {
|
|
text: qsTr("Current configuration")
|
|
Layout.fillWidth: true
|
|
Layout.columnSpan: 2
|
|
}
|
|
|
|
Controls.SubtitleLabel {
|
|
text: qsTr("Current factor K1")
|
|
maximumLineCount: 2
|
|
wrapMode: Text.WordWrap
|
|
Layout.fillWidth: true
|
|
Layout.maximumWidth: (parent.width - parent.columnSpacing) / 2
|
|
}
|
|
|
|
Controls.SubtitleLabel {
|
|
text: qsTr("Current factor K2")
|
|
maximumLineCount: 2
|
|
wrapMode: Text.WordWrap
|
|
Layout.fillWidth: true
|
|
Layout.maximumWidth: (parent.width - parent.columnSpacing) / 2
|
|
}
|
|
|
|
Controls.TextField {
|
|
id: currentFactorK1Field
|
|
validator: DoubleValidator { decimals: 3; locale: "en-US"; notation: DoubleValidator.StandardNotation }
|
|
Layout.fillWidth: true
|
|
Layout.maximumWidth: (parent.width - parent.columnSpacing) / 2
|
|
}
|
|
|
|
Controls.TextField {
|
|
id: currentFactorK2Field
|
|
validator: DoubleValidator { decimals: 3; locale: "en-US"; notation: DoubleValidator.StandardNotation }
|
|
Layout.fillWidth: true
|
|
Layout.maximumWidth: (parent.width - parent.columnSpacing) / 2
|
|
}
|
|
|
|
Controls.SubtitleLabel {
|
|
text: qsTr("Current sensor value \"0\"")
|
|
Layout.fillWidth: true
|
|
Layout.columnSpan: 2
|
|
}
|
|
|
|
Controls.TextField {
|
|
id: zeroSensorValueField
|
|
validator: DoubleValidator { decimals: 3; locale: "en-US"; notation: DoubleValidator.StandardNotation }
|
|
Layout.fillWidth: true
|
|
Layout.maximumWidth: (parent.width - parent.columnSpacing) / 2
|
|
Layout.columnSpan: 2
|
|
}
|
|
|
|
Controls.Button {
|
|
id: zeroSensorValueCalibrationButton
|
|
text: qsTr("Calibrate \"0\"")
|
|
Layout.fillWidth: true
|
|
Layout.maximumWidth: (parent.width - parent.columnSpacing) / 2
|
|
Layout.columnSpan: 2
|
|
onClicked: {
|
|
BmsInterface.commands().sendTerminalCmd("setZeroCurrent")
|
|
Qt.callLater(storeBmsConfigurationButton.clicked)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Controls.Frame {
|
|
id: outputSettingsFrame
|
|
padding: contentPadding
|
|
implicitWidth: parent.width
|
|
Layout.fillWidth: true
|
|
|
|
property real outputNumberSize: 60
|
|
|
|
GridLayout {
|
|
columns: 2
|
|
rowSpacing: contentRowSpacing
|
|
columnSpacing: contentColumnSpacing
|
|
anchors.fill: parent
|
|
|
|
Controls.TitleLabel {
|
|
text: qsTr("Output settings")
|
|
Layout.fillWidth: true
|
|
Layout.columnSpan: 2
|
|
}
|
|
|
|
RowLayout {
|
|
spacing: 20
|
|
|
|
Controls.LabelWithBackground {
|
|
text: qsTr("# 1")
|
|
Layout.preferredWidth: outputSettingsFrame.outputNumberSize
|
|
Layout.preferredHeight: outputSettingsFrame.outputNumberSize
|
|
}
|
|
|
|
Controls.CheckBox {
|
|
id: chargeBatteryOutputCheckBox
|
|
text: qsTr("Use to control charger")
|
|
Layout.fillWidth: true
|
|
Layout.preferredHeight: outputSettingsFrame.outputNumberSize
|
|
}
|
|
|
|
Layout.fillWidth: true
|
|
Layout.columnSpan: 2
|
|
}
|
|
|
|
Controls.LineSeparator {
|
|
Layout.fillWidth: true
|
|
Layout.columnSpan: 2
|
|
}
|
|
|
|
RowLayout {
|
|
spacing: 20
|
|
|
|
Controls.LabelWithBackground {
|
|
text: qsTr("# 2")
|
|
Layout.preferredWidth: outputSettingsFrame.outputNumberSize
|
|
Layout.preferredHeight: outputSettingsFrame.outputNumberSize
|
|
}
|
|
|
|
Controls.CheckBox {
|
|
id: brushOrShuntOutputCheckBox
|
|
text: qsTr("Active")
|
|
Layout.fillWidth: true
|
|
Layout.preferredHeight: outputSettingsFrame.outputNumberSize
|
|
}
|
|
|
|
Layout.fillWidth: true
|
|
Layout.columnSpan: 2
|
|
}
|
|
|
|
Controls.RadioButton {
|
|
id: brushControlRadioButton
|
|
text: qsTr("Brush control")
|
|
Layout.fillWidth: true
|
|
Layout.maximumWidth: (parent.width - parent.columnSpacing) / 2
|
|
}
|
|
|
|
Controls.RadioButton {
|
|
id: shuntChargingContactorRadioButton
|
|
text: qsTr("Shunt charging contactor")
|
|
Layout.fillWidth: true
|
|
Layout.maximumWidth: (parent.width - parent.columnSpacing) / 2
|
|
}
|
|
|
|
Controls.SubtitleLabel {
|
|
text: qsTr("SOC threshold, %")
|
|
maximumLineCount: 2
|
|
wrapMode: Text.WordWrap
|
|
Layout.fillWidth: true
|
|
Layout.maximumWidth: (parent.width - parent.columnSpacing) / 2
|
|
}
|
|
|
|
Controls.SubtitleLabel {
|
|
text: qsTr("Delay, s")
|
|
maximumLineCount: 2
|
|
wrapMode: Text.WordWrap
|
|
Layout.fillWidth: true
|
|
Layout.maximumWidth: (parent.width - parent.columnSpacing) / 2
|
|
}
|
|
|
|
Controls.TextField {
|
|
id: brushUsageSocThresholdField
|
|
validator: IntValidator { bottom: 0; top: 100 }
|
|
Layout.fillWidth: true
|
|
Layout.maximumWidth: (parent.width - parent.columnSpacing) / 2
|
|
}
|
|
|
|
Controls.TextField {
|
|
id: shuntChargingContactorDelayField
|
|
validator: IntValidator { bottom: 0; top: 65535 }
|
|
Layout.fillWidth: true
|
|
Layout.maximumWidth: (parent.width - parent.columnSpacing) / 2
|
|
}
|
|
|
|
Controls.LineSeparator {
|
|
Layout.fillWidth: true
|
|
Layout.columnSpan: 2
|
|
}
|
|
|
|
RowLayout {
|
|
spacing: 20
|
|
|
|
Controls.LabelWithBackground {
|
|
text: qsTr("# 3")
|
|
Layout.preferredWidth: outputSettingsFrame.outputNumberSize
|
|
Layout.preferredHeight: outputSettingsFrame.outputNumberSize
|
|
}
|
|
|
|
Controls.CheckBox {
|
|
id: coolingOutputCheckBox
|
|
text: qsTr("Cooling activation")
|
|
Layout.fillWidth: true
|
|
Layout.preferredHeight: outputSettingsFrame.outputNumberSize
|
|
}
|
|
|
|
Layout.fillWidth: true
|
|
Layout.columnSpan: 2
|
|
}
|
|
|
|
Controls.SubtitleLabel {
|
|
text: qsTr("Closes at t<, °C")
|
|
Layout.fillWidth: true
|
|
Layout.maximumWidth: (parent.width - parent.columnSpacing) / 2
|
|
}
|
|
|
|
Controls.SubtitleLabel {
|
|
text: qsTr("Opens at t>, °C")
|
|
Layout.fillWidth: true
|
|
Layout.maximumWidth: (parent.width - parent.columnSpacing) / 2
|
|
}
|
|
|
|
Controls.TextField {
|
|
id: coolingStartThresholdField
|
|
validator: IntValidator { bottom: -32768; top: 32768 }
|
|
Layout.fillWidth: true
|
|
Layout.maximumWidth: (parent.width - parent.columnSpacing) / 2
|
|
}
|
|
|
|
Controls.TextField {
|
|
id: coolingStopThresholdField
|
|
validator: IntValidator { bottom: -32768; top: 32768 }
|
|
Layout.fillWidth: true
|
|
Layout.maximumWidth: (parent.width - parent.columnSpacing) / 2
|
|
}
|
|
|
|
Controls.LineSeparator {
|
|
Layout.fillWidth: true
|
|
Layout.columnSpan: 2
|
|
}
|
|
|
|
RowLayout {
|
|
spacing: 20
|
|
|
|
Controls.LabelWithBackground {
|
|
text: qsTr("# 4")
|
|
Layout.preferredWidth: outputSettingsFrame.outputNumberSize
|
|
Layout.preferredHeight: outputSettingsFrame.outputNumberSize
|
|
}
|
|
|
|
Controls.CheckBox {
|
|
id: heatingOutputCheckBox
|
|
text: qsTr("Heating activation")
|
|
Layout.fillWidth: true
|
|
Layout.preferredHeight: outputSettingsFrame.outputNumberSize
|
|
}
|
|
|
|
Layout.fillWidth: true
|
|
Layout.columnSpan: 2
|
|
}
|
|
|
|
Controls.SubtitleLabel {
|
|
text: qsTr("Closes at t<, °C")
|
|
Layout.fillWidth: true
|
|
Layout.maximumWidth: (parent.width - parent.columnSpacing) / 2
|
|
}
|
|
|
|
Controls.SubtitleLabel {
|
|
text: qsTr("Opens at t>, °C")
|
|
Layout.fillWidth: true
|
|
Layout.maximumWidth: (parent.width - parent.columnSpacing) / 2
|
|
}
|
|
|
|
Controls.TextField {
|
|
id: heatingStartThresholdField
|
|
validator: IntValidator { bottom: -32768; top: 32768 }
|
|
Layout.fillWidth: true
|
|
Layout.maximumWidth: (parent.width - parent.columnSpacing) / 2
|
|
}
|
|
|
|
Controls.TextField {
|
|
id: heatingStopThresholdField
|
|
validator: IntValidator { bottom: -32768; top: 32768 }
|
|
Layout.fillWidth: true
|
|
Layout.maximumWidth: (parent.width - parent.columnSpacing) / 2
|
|
}
|
|
|
|
Controls.LineSeparator {
|
|
Layout.fillWidth: true
|
|
Layout.columnSpan: 2
|
|
}
|
|
}
|
|
}
|
|
|
|
Controls.OutlineButton {
|
|
text: qsTr("Load settings from file")
|
|
onClicked: loadFileDialog.open()
|
|
Layout.preferredWidth: 270
|
|
|
|
FileDialog {
|
|
id: loadFileDialog
|
|
title: qsTr("Select configuration file")
|
|
folder: shortcuts.documents
|
|
nameFilters: [ qsTr("Configuration files (*.xml)"), qsTr("All files (*)") ]
|
|
onAccepted: {
|
|
let result = BmsInterface.bmsConfig().loadXml(loadFileDialog.fileUrl.toString().replace(/^(file:\/{3})/, ""), "bmsConfiguration")
|
|
if (!result) {
|
|
BmsInterface.emitStatusMessage(BmsInterface.bmsConfig().xmlStatus(), false)
|
|
}
|
|
}
|
|
}
|
|
|
|
Settings {
|
|
category: "loadConfiguration"
|
|
property alias folder: loadFileDialog.folder
|
|
}
|
|
}
|
|
|
|
Controls.OutlineButton {
|
|
text: qsTr("Save settings to file")
|
|
onClicked: saveFileDialog.open()
|
|
Layout.preferredWidth: 270
|
|
|
|
FileDialog {
|
|
id: saveFileDialog
|
|
title: qsTr("Select configuration file")
|
|
selectExisting: false
|
|
folder: shortcuts.documents
|
|
nameFilters: [ qsTr("Configuration files (*.xml)"), qsTr("All files (*)") ]
|
|
onAccepted: {
|
|
let result = BmsInterface.bmsConfig().saveXml(saveFileDialog.fileUrl.toString().replace(/^(file:\/{3})/, ""), "bmsConfiguration")
|
|
if (!result) {
|
|
BmsInterface.emitStatusMessage(BmsInterface.bmsConfig().xmlStatus(), false)
|
|
} else {
|
|
BmsInterface.emitStatusMessage(qsTr("BMS configuration saved to file"), true)
|
|
}
|
|
}
|
|
}
|
|
|
|
Settings {
|
|
category: "saveConfiguration"
|
|
property alias folder: saveFileDialog.folder
|
|
}
|
|
}
|
|
}
|
|
|
|
ScrollBar.vertical: Controls.ScrollBar {}
|
|
|
|
Layout.fillWidth: true
|
|
Layout.fillHeight: true
|
|
}
|
|
|
|
ColumnLayout {
|
|
spacing: contentRowSpacing
|
|
|
|
Controls.LinkLabel {
|
|
text: qsTr("Serial number")
|
|
onClicked: settingsFlickable.contentY = serialNumberSettingsFrame.mapToItem(settingsFlickable.contentItem, 0, 0).y
|
|
}
|
|
|
|
Controls.LinkLabel {
|
|
text: qsTr("Configuration")
|
|
onClicked: settingsFlickable.contentY = configurationSettingsFrame.mapToItem(settingsFlickable.contentItem, 0, 0).y
|
|
}
|
|
|
|
Controls.LinkLabel {
|
|
text: qsTr("SOC")
|
|
onClicked: settingsFlickable.contentY = socSettingsFrame.mapToItem(settingsFlickable.contentItem, 0, 0).y
|
|
}
|
|
|
|
Controls.LinkLabel {
|
|
text: qsTr("Limits")
|
|
onClicked: settingsFlickable.contentY = limitSettingsFrame.mapToItem(settingsFlickable.contentItem, 0, 0).y
|
|
}
|
|
|
|
Controls.LinkLabel {
|
|
text: qsTr("Cell configuration")
|
|
onClicked: settingsFlickable.contentY = cellSettingsFrame.mapToItem(settingsFlickable.contentItem, 0, 0).y
|
|
}
|
|
|
|
Controls.LinkLabel {
|
|
text: qsTr("Balancing configuration")
|
|
onClicked: settingsFlickable.contentY = balancingSettingsFrame.mapToItem(settingsFlickable.contentItem, 0, 0).y
|
|
}
|
|
|
|
Controls.LinkLabel {
|
|
text: qsTr("Current configuration")
|
|
onClicked: settingsFlickable.contentY = currentConfigurationFrame.mapToItem(settingsFlickable.contentItem, 0, 0).y
|
|
}
|
|
|
|
Controls.LinkLabel {
|
|
text: qsTr("Output settings")
|
|
onClicked: {
|
|
settingsFlickable.contentY = outputSettingsFrame.mapToItem(settingsFlickable.contentItem, 0, 0).y
|
|
settingsFlickable.returnToBounds()
|
|
}
|
|
}
|
|
|
|
Item {
|
|
Layout.fillHeight: true
|
|
}
|
|
|
|
ColumnLayout {
|
|
spacing: 15
|
|
|
|
Controls.OutlineButton {
|
|
text: qsTr("Read default settings")
|
|
Layout.fillWidth: true
|
|
onClicked: if (BmsInterface.isPortConnected()) {
|
|
BmsInterface.commands().getBMSconfDefault()
|
|
}
|
|
}
|
|
|
|
Controls.OutlineButton {
|
|
text: qsTr("Read current settings from BMS")
|
|
Layout.fillWidth: true
|
|
onClicked: if (BmsInterface.isPortConnected()) {
|
|
BmsInterface.commands().getBMSconf()
|
|
}
|
|
}
|
|
|
|
Controls.OutlineButton {
|
|
id: writeBmsConfigurationButton
|
|
text: qsTr("Write current values to BMS")
|
|
Layout.fillWidth: true
|
|
onClicked: if (BmsInterface.isPortConnected()) {
|
|
writeValuesToConfig()
|
|
BmsInterface.commands().setBMSconf()
|
|
}
|
|
}
|
|
|
|
Controls.Button {
|
|
id: storeBmsConfigurationButton
|
|
text: qsTr("Write to non-volatile memory of BMS")
|
|
Layout.fillWidth: true
|
|
onClicked: if (BmsInterface.isPortConnected()) {
|
|
needWait(true, qsTr("The settings are written to non-volatile memory.\nWait, please."))
|
|
writeValuesToConfig()
|
|
BmsInterface.commands().storeBMSConfig()
|
|
}
|
|
}
|
|
|
|
Layout.fillWidth: true
|
|
}
|
|
|
|
Layout.maximumWidth: 280
|
|
Layout.fillHeight: true
|
|
}
|
|
|
|
function writeValuesToConfig() {
|
|
BmsInterface.bmsConfig().setParamValue("notUsedCurrentThreshold", parseFloat(serialNumberField.text))
|
|
|
|
BmsInterface.bmsConfig().setParamValue("cellMonitorICCount", parseInt(numberOfBoardsField.text))
|
|
BmsInterface.bmsConfig().setParamValue("noOfCellsSeries", parseInt(numberOfCellsField.text))
|
|
|
|
const numberOfBoards = parseInt(numberOfBoardsField.text)
|
|
// const bmsSensorsModel = bmsSensorsMaskRepeater.bmsSensorsMaskModel
|
|
const batterySensorsModel = batterySensorsMaskRepeater.batterySensorsMaskModel
|
|
|
|
// let bmsSensorsMask = 0
|
|
let batterySensorsMask = 0
|
|
for (let i = 0; i < numberOfBoards * 4; ++i) {
|
|
// bmsSensorsMask |= Number(bmsSensorsModel[i]) << i
|
|
batterySensorsMask |= Number(batterySensorsModel[i]) << i
|
|
}
|
|
|
|
// disable 3 and 4 sensor for master
|
|
// bmsSensorsMask = bmsSensorsMask & ~(1 << 2)
|
|
// bmsSensorsMask = bmsSensorsMask & ~(1 << 3)
|
|
|
|
batterySensorsMask = batterySensorsMask & ~(1 << 2)
|
|
batterySensorsMask = batterySensorsMask & ~(1 << 3)
|
|
|
|
// print(bmsSensorsMask, batterySensorsMask)
|
|
|
|
// BmsInterface.bmsConfig().setParamValue("tempEnableMaskBMS", bmsSensorsMask)
|
|
BmsInterface.bmsConfig().setParamValue("tempEnableMaskBattery", batterySensorsMask)
|
|
|
|
BmsInterface.bmsConfig().setParamValue("noOfCellsParallel", parseInt(numberOfParallelCellsField.text))
|
|
BmsInterface.bmsConfig().setParamValue("batteryCapacity", parseFloat(batteryCapacityField.text))
|
|
|
|
BmsInterface.bmsConfig().setParamValue("maxAllowedCurrent", parseFloat(maximumChargeCurrentField.text))
|
|
BmsInterface.bmsConfig().setParamValue("maxMismatchThreshold", parseFloat(maximumLoadCurrentField.text))
|
|
BmsInterface.bmsConfig().setParamValue("allowedTempBattChargingMax", parseFloat(maximumTemperatureField.text))
|
|
|
|
BmsInterface.bmsConfig().setParamValue("cellHardUnderVoltage", parseFloat(lowerDisableThresholdField.text))
|
|
BmsInterface.bmsConfig().setParamValue("cellHardOverVoltage", parseFloat(upperDisableThresholdField.text))
|
|
BmsInterface.bmsConfig().setParamValue("cellLCSoftUnderVoltage", parseFloat(lowerEnableThresholdField.text))
|
|
BmsInterface.bmsConfig().setParamValue("cellSoftOverVoltage", parseFloat(upperEnableThresholdField.text))
|
|
|
|
BmsInterface.bmsConfig().setParamValue("cellBalanceStart", parseFloat(balancingStartVoltageField.text))
|
|
BmsInterface.bmsConfig().setParamValue("cellBalanceDifferenceThreshold", parseFloat(balancingStartDeltaVoltageField.text))
|
|
BmsInterface.bmsConfig().setParamValue("cellBalanceUpdateInterval", parseInt(balancingCellIntervalField.text))
|
|
|
|
BmsInterface.bmsConfig().setParamValue("floatCurrentK1", parseFloat(currentFactorK1Field.text))
|
|
BmsInterface.bmsConfig().setParamValue("floatCurrentK2", parseFloat(currentFactorK2Field.text))
|
|
BmsInterface.bmsConfig().setParamValue("shuntLCFactor", parseFloat(zeroSensorValueField.text))
|
|
|
|
BmsInterface.bmsConfig().setParamValue("chargeBatteryOutputChecked", chargeBatteryOutputCheckBox.checked)
|
|
|
|
BmsInterface.bmsConfig().setParamValue("brushOrShuntOutputChecked", brushOrShuntOutputCheckBox.checked)
|
|
BmsInterface.bmsConfig().setParamValue("brushOrShuntMode", shuntChargingContactorRadioButton.checked)
|
|
BmsInterface.bmsConfig().setParamValue("brushUsageSocThreshold", parseInt(brushUsageSocThresholdField.text))
|
|
BmsInterface.bmsConfig().setParamValue("shuntChargingContactorDelay", parseInt(shuntChargingContactorDelayField.text))
|
|
|
|
BmsInterface.bmsConfig().setParamValue("coolingOutputChecked", coolingOutputCheckBox.checked)
|
|
BmsInterface.bmsConfig().setParamValue("coolingStartThreshold", parseInt(coolingStartThresholdField.text))
|
|
BmsInterface.bmsConfig().setParamValue("coolingStopThreshold", parseInt(coolingStopThresholdField.text))
|
|
|
|
BmsInterface.bmsConfig().setParamValue("heatingOutputChecked", heatingOutputCheckBox.checked)
|
|
BmsInterface.bmsConfig().setParamValue("heatingStartThreshold", parseInt(heatingStartThresholdField.text))
|
|
BmsInterface.bmsConfig().setParamValue("heatingStopThreshold", parseInt(heatingStopThresholdField.text))
|
|
}
|
|
|
|
Connections {
|
|
target: BmsInterface.bmsConfig()
|
|
onUpdated: {
|
|
serialNumberField.text = Number(BmsInterface.bmsConfig().getParamDouble("notUsedCurrentThreshold")).toFixed()
|
|
|
|
numberOfBoardsField.text = BmsInterface.bmsConfig().getParamInt("cellMonitorICCount")
|
|
numberOfCellsField.text = BmsInterface.bmsConfig().getParamInt("noOfCellsSeries")
|
|
|
|
const numberOfBoards = BmsInterface.bmsConfig().getParamInt("cellMonitorICCount")
|
|
const numberOfSensorsPerBoard = BmsInterface.bmsConfig().getParamInt("noOfTempSensorPerModule")
|
|
// const bmsSensorsMask = BmsInterface.bmsConfig().getParamInt("tempEnableMaskBMS")
|
|
const batterySensorsMask = BmsInterface.bmsConfig().getParamInt("tempEnableMaskBattery")
|
|
|
|
// const bmsSensorsModel = []
|
|
const batterySensorsModel = []
|
|
for (let i = 0; i < numberOfBoards * numberOfSensorsPerBoard; ++i) {
|
|
// bmsSensorsModel.push((bmsSensorsMask & (1 << i)) != 0)
|
|
batterySensorsModel.push((batterySensorsMask & (1 << i)) != 0)
|
|
}
|
|
|
|
// bmsSensorsMaskRepeater.bmsSensorsMaskModel = bmsSensorsModel
|
|
batterySensorsMaskRepeater.batterySensorsMaskModel = batterySensorsModel
|
|
|
|
numberOfParallelCellsField.text = BmsInterface.bmsConfig().getParamInt("noOfCellsParallel")
|
|
batteryCapacityField.text = MathHelper.roundDouble(BmsInterface.bmsConfig().getParamDouble("batteryCapacity"))
|
|
|
|
maximumChargeCurrentField.text = MathHelper.roundDouble(BmsInterface.bmsConfig().getParamDouble("maxAllowedCurrent"))
|
|
maximumLoadCurrentField.text = MathHelper.roundDouble(BmsInterface.bmsConfig().getParamDouble("maxMismatchThreshold"))
|
|
maximumTemperatureField.text = MathHelper.roundDouble(BmsInterface.bmsConfig().getParamDouble("allowedTempBattChargingMax"))
|
|
|
|
lowerDisableThresholdField.text = MathHelper.roundDouble(BmsInterface.bmsConfig().getParamDouble("cellHardUnderVoltage"))
|
|
upperDisableThresholdField.text = MathHelper.roundDouble(BmsInterface.bmsConfig().getParamDouble("cellHardOverVoltage"))
|
|
lowerEnableThresholdField.text = MathHelper.roundDouble(BmsInterface.bmsConfig().getParamDouble("cellLCSoftUnderVoltage"))
|
|
upperEnableThresholdField.text = MathHelper.roundDouble(BmsInterface.bmsConfig().getParamDouble("cellSoftOverVoltage"))
|
|
|
|
balancingStartVoltageField.text = MathHelper.roundDouble(BmsInterface.bmsConfig().getParamDouble("cellBalanceStart"))
|
|
balancingStartDeltaVoltageField.text = MathHelper.roundDouble(BmsInterface.bmsConfig().getParamDouble("cellBalanceDifferenceThreshold"))
|
|
balancingCellIntervalField.text = BmsInterface.bmsConfig().getParamInt("cellBalanceUpdateInterval")
|
|
|
|
currentFactorK1Field.text = MathHelper.roundDouble(BmsInterface.bmsConfig().getParamDouble("floatCurrentK1"), 3)
|
|
currentFactorK2Field.text = MathHelper.roundDouble(BmsInterface.bmsConfig().getParamDouble("floatCurrentK2"), 3)
|
|
zeroSensorValueField.text = MathHelper.roundDouble(BmsInterface.bmsConfig().getParamDouble("shuntLCFactor"), 3)
|
|
|
|
chargeBatteryOutputCheckBox.checked = BmsInterface.bmsConfig().getParamBool("chargeBatteryOutputChecked")
|
|
|
|
brushOrShuntOutputCheckBox.checked = BmsInterface.bmsConfig().getParamBool("brushOrShuntOutputChecked")
|
|
brushControlRadioButton.checked = !BmsInterface.bmsConfig().getParamBool("brushOrShuntMode")
|
|
shuntChargingContactorRadioButton.checked = BmsInterface.bmsConfig().getParamBool("brushOrShuntMode")
|
|
brushUsageSocThresholdField.text = BmsInterface.bmsConfig().getParamInt("brushUsageSocThreshold")
|
|
shuntChargingContactorDelayField.text = BmsInterface.bmsConfig().getParamInt("shuntChargingContactorDelay")
|
|
|
|
coolingOutputCheckBox.checked = BmsInterface.bmsConfig().getParamBool("coolingOutputChecked")
|
|
coolingStartThresholdField.text = BmsInterface.bmsConfig().getParamInt("coolingStartThreshold")
|
|
coolingStopThresholdField.text = BmsInterface.bmsConfig().getParamInt("coolingStopThreshold")
|
|
|
|
heatingOutputCheckBox.checked = BmsInterface.bmsConfig().getParamBool("heatingOutputChecked")
|
|
heatingStartThresholdField.text = BmsInterface.bmsConfig().getParamInt("heatingStartThreshold")
|
|
heatingStopThresholdField.text = BmsInterface.bmsConfig().getParamInt("heatingStopThreshold")
|
|
}
|
|
}
|
|
}
|