summaryrefslogtreecommitdiffstats
path: root/Widgets/ICPage.py
diff options
context:
space:
mode:
authorsuchinton2001 <suchinton.2001@gmail.com>2023-07-22 18:39:14 +0530
committersuchinton2001 <suchinton.2001@gmail.com>2023-09-07 18:31:07 +0530
commitdb9f586a19fed7bcd04be3596fc30dc53f61b1db (patch)
tree476d86c085137779f47ee6b409e3a8aaac68991d /Widgets/ICPage.py
parentf9b00b992d88edc0e9c31de809a1a981139c4fde (diff)
Upload progress on AGL demo control panel in one batch
AGL Demo Control Panel is a PyQt5 application used to simulate CAN bus signals using Kuksa.val v1: Initial commit v2: Remove unused assets v3: Add Opensans fonts, remove un-used styles and add Lisences as attributions v4: - Remove Opensans fonts, default to Dejavu fonts - Replace feather icons with carbon icons. - Reusing AGL demo app assests for HVAC and Steering wheel inputs. v5: Remove assets/Images/Lisences.md attribution file Signed-off-by: suchinton2001 <suchinton.2001@gmail.com> Change-Id: I1529495deff6fc27eacb92f7a29c4f71f8c8d5d9
Diffstat (limited to 'Widgets/ICPage.py')
-rw-r--r--Widgets/ICPage.py353
1 files changed, 353 insertions, 0 deletions
diff --git a/Widgets/ICPage.py b/Widgets/ICPage.py
new file mode 100644
index 0000000..64fa2d3
--- /dev/null
+++ b/Widgets/ICPage.py
@@ -0,0 +1,353 @@
+"""
+ Copyright 2023 Suchinton Chakravarty
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import os
+import sys
+from PyQt5 import uic, QtCore, QtWidgets
+from PyQt5.QtWidgets import QApplication
+from PyQt5.QtWidgets import QSlider, QLCDNumber, QPushButton
+from PyQt5.QtGui import QIcon, QPixmap, QPainter
+import time
+from PyQt5.QtCore import QThread
+
+current_dir = os.path.dirname(os.path.abspath(__file__))
+
+# ========================================
+
+sys.path.append(os.path.dirname(current_dir))
+
+from extras.FeedKuksa import FeedKuksa
+
+Form, Base = uic.loadUiType(os.path.join(current_dir, "../ui/IC.ui"))
+
+# ========================================
+
+
+class IC_Paths():
+ def __init__(self):
+ self.speed = "Vehicle.Speed"
+ self.engineRPM = "Vehicle.Powertrain.CombustionEngine.Speed"
+ self.leftIndicator = "Vehicle.Body.Lights.DirectionIndicator.Left.IsSignaling"
+ self.rightIndicator = "Vehicle.Body.Lights.DirectionIndicator.Right.IsSignaling"
+ self.hazard = "Vehicle.Body.Lights.Hazard.IsSignaling"
+ self.fuelLevel = "Vehicle.Powertrain.FuelSystem.Level"
+ self.coolantTemp = "Vehicle.Powertrain.CombustionEngine.ECT"
+ self.selectedGear = "Vehicle.Powertrain.Transmission.SelectedGear"
+
+
+class ICWidget(Base, Form):
+ def __init__(self, parent=None):
+ super(self.__class__, self).__init__(parent)
+ self.setupUi(self)
+
+ self.IC = IC_Paths()
+
+ self.feed_kuksa = FeedKuksa()
+ self.feed_kuksa.start()
+
+ # # load the stylesheet
+ # theme = open(os.path.join(current_dir, "../ui/styles/Tron/ICPage.qss"), 'r')
+ # self.setStyleSheet(theme.read())
+ # theme.close()
+
+ self.scriptBtn = self.findChild(QPushButton, "scriptBtn")
+
+ self.Speed_slider = self.findChild(QSlider, "Speed_slider")
+ self.Speed_monitor = self.findChild(QLCDNumber, "Speed_monitor")
+ self.RPM_slider = self.findChild(QSlider, "RPM_slider")
+ self.RPM_monitor = self.findChild(QLCDNumber, "RPM_monitor")
+
+ self.coolantTemp_slider = self.findChild(QSlider, "coolantTemp_slider")
+ self.fuelLevel_slider = self.findChild(QSlider, "fuelLevel_slider")
+
+ self.accelerationBtn = self.findChild(QPushButton, "accelerationBtn")
+
+ self.leftIndicatorBtn = self.findChild(QPushButton, "leftIndicatorBtn")
+ self.rightIndicatorBtn = self.findChild(QPushButton, "rightIndicatorBtn")
+ self.hazardBtn = self.findChild(QPushButton, "hazardBtn")
+
+ buttons = [self.parkBtn,
+ self.reverseBtn,
+ self.neutralBtn,
+ self.driveBtn]
+
+ # group for the buttons for mutual exclusion
+ self.driveGroupBtns = QtWidgets.QButtonGroup(self)
+ self.driveGroupBtns.setExclusive(True)
+
+ for button in buttons:
+ self.driveGroupBtns.addButton(button)
+
+ self.driveGroupBtns.buttonClicked.connect(self.driveBtnClicked)
+
+ self.scriptBtn.clicked.connect(self.scriptBtnClicked)
+
+ self.Speed_slider.valueChanged.connect(self.update_Speed_monitor)
+ self.Speed_slider.setMinimum(0)
+ self.Speed_slider.setMaximum(240)
+
+ self.RPM_slider.valueChanged.connect(self.update_RPM_monitor)
+ self.RPM_slider.setMinimum(0)
+ self.RPM_slider.setMaximum(8000)
+
+ self.coolantTemp_slider.valueChanged.connect(
+ self.update_coolantTemp_monitor)
+ self.fuelLevel_slider.valueChanged.connect(
+ self.update_fuelLevel_monitor)
+
+ self.accelerationBtn.pressed.connect(self.accelerationBtnPressed)
+ self.accelerationBtn.released.connect(self.accelerationBtnReleased)
+
+ # make both buttons checkable
+ self.leftIndicatorBtn.setCheckable(True)
+ self.rightIndicatorBtn.setCheckable(True)
+ self.hazardBtn.setCheckable(True)
+
+ self.leftIndicatorBtn.clicked.connect(self.leftIndicatorBtnClicked)
+ self.rightIndicatorBtn.clicked.connect(self.rightIndicatorBtnClicked)
+ self.hazardBtn.clicked.connect(self.hazardBtnClicked)
+
+ def scriptBtnClicked(self):
+ if self.scriptBtn.isChecked():
+ ICScript.start_script()
+
+ if not self.ScriptBtn.isChecked():
+ ICScript.stop_script()
+
+ def update_Speed_monitor(self):
+ speed = int(self.Speed_slider.value())
+ self.Speed_monitor.display(speed)
+ self.feed_kuksa.send_values(self.IC.speed, str(speed), 'value')
+
+ def update_RPM_monitor(self):
+ rpm = int(self.RPM_slider.value())
+ self.RPM_monitor.display(rpm)
+ self.feed_kuksa.send_values(self.IC.engineRPM, str(rpm), 'value')
+
+ def update_coolantTemp_monitor(self):
+ coolantTemp = int(self.coolantTemp_slider.value())
+ self.feed_kuksa.send_values(self.IC.coolantTemp, str(coolantTemp), 'value')
+
+ def update_fuelLevel_monitor(self):
+ fuelLevel = int(self.fuelLevel_slider.value())
+ self.feed_kuksa.send_values(self.IC.fuelLevel, str(fuelLevel))
+
+ def hazardBtnClicked(self):
+ hazardIcon = QPixmap(":/Images/Images/hazard.png")
+ if self.hazardBtn.isChecked():
+ painter = QPainter(hazardIcon)
+ painter.setCompositionMode(QPainter.CompositionMode_SourceIn)
+ painter.fillRect(hazardIcon.rect(), QtCore.Qt.yellow)
+ painter.end()
+ self.hazardBtn.setIcon(QIcon(hazardIcon))
+
+ self.leftIndicatorBtn.setChecked(True)
+ self.rightIndicatorBtn.setChecked(True)
+ self.feed_kuksa.send_values(self.IC.leftIndicator, "true")
+ self.feed_kuksa.send_values(self.IC.rightIndicator, "true")
+ self.feed_kuksa.send_values(self.IC.hazard, "true")
+ else:
+ painter = QPainter(hazardIcon)
+ painter.setCompositionMode(QPainter.CompositionMode_SourceIn)
+ painter.fillRect(hazardIcon.rect(), QtCore.Qt.black)
+ painter.end()
+ self.hazardBtn.setIcon(QIcon(hazardIcon))
+
+ self.leftIndicatorBtn.setChecked(False)
+ self.rightIndicatorBtn.setChecked(False)
+ self.feed_kuksa.send_values(self.IC.leftIndicator, "false")
+ self.feed_kuksa.send_values(self.IC.rightIndicator, "false")
+ self.feed_kuksa.send_values(self.IC.hazard, "false")
+
+ def leftIndicatorBtnClicked(self):
+ leftIndicatorIcon = QPixmap(":/Images/Images/left.png")
+ if self.leftIndicatorBtn.isChecked():
+
+ painter = QPainter(leftIndicatorIcon)
+ painter.setCompositionMode(QPainter.CompositionMode_SourceIn)
+ painter.fillRect(leftIndicatorIcon.rect(), QtCore.Qt.green)
+ painter.end()
+
+ self.leftIndicatorBtn.setIcon(QIcon(leftIndicatorIcon))
+ self.feed_kuksa.send_values(self.IC.leftIndicator, "true")
+ else:
+
+ painter = QPainter(leftIndicatorIcon)
+ painter.setCompositionMode(QPainter.CompositionMode_SourceIn)
+ painter.fillRect(leftIndicatorIcon.rect(), QtCore.Qt.black)
+ painter.end()
+
+ self.leftIndicatorBtn.setIcon(QIcon(leftIndicatorIcon))
+ self.feed_kuksa.send_values(self.IC.leftIndicator, "false")
+
+ def rightIndicatorBtnClicked(self):
+ rightIndicatorIcon = QPixmap(":/Images/Images/right.png")
+ if self.rightIndicatorBtn.isChecked():
+
+ painter = QPainter(rightIndicatorIcon)
+ painter.setCompositionMode(QPainter.CompositionMode_SourceIn)
+ painter.fillRect(rightIndicatorIcon.rect(), QtCore.Qt.green)
+ painter.end()
+ self.rightIndicatorBtn.setIcon(QIcon(rightIndicatorIcon))
+ self.feed_kuksa.send_values(self.IC.rightIndicator, "true")
+ else:
+
+ painter = QPainter(rightIndicatorIcon)
+ painter.setCompositionMode(QPainter.CompositionMode_SourceIn)
+ painter.fillRect(rightIndicatorIcon.rect(), QtCore.Qt.black)
+ painter.end()
+ self.rightIndicatorBtn.setIcon(QIcon(rightIndicatorIcon))
+ self.feed_kuksa.send_values(self.IC.rightIndicator, "false")
+
+ def accelerationBtnPressed(self):
+ self.startTime = QtCore.QTime.currentTime()
+ self.acceleration_timer = QtCore.QTimer()
+ self.acceleration_timer.timeout.connect(
+ lambda: self.updateSpeedAndEngineRpm("Accelerate"))
+ self.acceleration_timer.start(100)
+
+ def accelerationBtnReleased(self):
+ if self.Speed_slider.value() <= 0:
+ self.acceleration_timer.stop()
+ else:
+ self.acceleration_timer.timeout.connect(
+ lambda: self.updateSpeedAndEngineRpm("Decelerate"))
+ self.acceleration_timer.start(100)
+
+ def updateSpeedAndEngineRpm(self, action, acceleration=(60/5)):
+ if action == "Accelerate":
+ pass
+ elif action == "Decelerate":
+ acceleration = -acceleration
+
+ currentTime = QtCore.QTime.currentTime()
+ duration = self.startTime.msecsTo(currentTime)
+ self.current_speed = AccelerationFns.calculate_speed(
+ duration, acceleration)
+ self.current_rpm = AccelerationFns.calculate_engine_rpm(
+ self.current_speed)
+
+ if self.current_speed <= 0:
+ self.current_speed = 0
+ self.current_rpm = 0
+ self.acceleration_timer.stop()
+
+ if self.current_speed >= 240:
+ self.current_speed = 240
+ self.current_rpm = 0
+ self.acceleration_timer.stop()
+
+ self.Speed_slider.setValue(self.current_speed)
+ self.RPM_slider.setValue(self.current_rpm)
+
+ self.update_Speed_monitor()
+ self.update_RPM_monitor()
+
+ def driveBtnClicked(self):
+ # // Selected Gear output = > 0 = Neutral, 1/2/.. = Forward, -1/.. = Reverse, 126 = Park, 127 = Drive
+ # #859287 ; /* light green */
+
+ if self.driveGroupBtns.checkedButton() == self.driveBtn:
+ self.accelerationBtn.setEnabled(True)
+ self.Speed_slider.setEnabled(True)
+ self.RPM_slider.setEnabled(True)
+ self.feed_kuksa.send_values(self.IC.selectedGear, "127")
+
+ if self.driveGroupBtns.checkedButton() == self.parkBtn:
+ self.accelerationBtn.setEnabled(False)
+ self.Speed_slider.setEnabled(False)
+ self.RPM_slider.setEnabled(False)
+ self.feed_kuksa.send_values(self.IC.selectedGear, "126")
+
+ if self.driveGroupBtns.checkedButton() == self.reverseBtn:
+ self.accelerationBtn.setEnabled(True)
+ self.Speed_slider.setEnabled(True)
+ self.RPM_slider.setEnabled(True)
+ self.feed_kuksa.send_values(self.IC.selectedGear, "-1")
+
+ if self.driveGroupBtns.checkedButton() == self.neutralBtn:
+ self.accelerationBtn.setEnabled(False)
+ self.Speed_slider.setEnabled(False)
+ self.RPM_slider.setEnabled(True)
+ self.feed_kuksa.send_values(self.IC.selectedGear, "0")
+
+
+class AccelerationFns():
+ def calculate_speed(time, acceleration) -> int:
+ # acceleration = 60 / 5 # acceleration from 0 to 60 in 5 seconds
+ time = time / 1000 # convert milliseconds to seconds
+ speed = acceleration * time # calculate speed
+ return int(speed)
+
+ def calculate_engine_rpm(speed) -> int:
+ wheel_diameter = 0.48 # in meters
+ wheel_circumference = wheel_diameter * 3.14 # in meters
+
+ # Adjust the gear ratios to match the desired speed and rpm
+ gear_ratios = [3.36, 2.10, 1.48, 1.16, 0.95, 0.75]
+ speed = speed * 1000 / 3600 # Convert speed from km/h to m/s
+ wheel_rps = speed / wheel_circumference
+
+ current_gear = None
+ for i in range(len(gear_ratios)):
+ if wheel_rps * gear_ratios[i] < 8000 / 60:
+ current_gear = i + 1
+ break
+
+ # If no gear is found, use the highest gear
+ if current_gear is None:
+ current_gear = len(gear_ratios)
+
+ engine_rpm = wheel_rps * gear_ratios[current_gear - 1] * 60
+
+ return int(engine_rpm)
+
+
+class ICScript(ICWidget):
+ def start_script(self):
+ ICWidget.reset()
+
+ # disable all widgets in the scroll area
+ for widget in ICWidget.scrollAreaWidgetContents.children():
+ widget.setEnabled(False)
+
+ rates = [(60/5), (60/4), (60/3)]
+
+ # start assigning values to the speed and rpm sliders and send them to the IC do this in a loop for each rate
+ for rate in rates:
+ ICWidget.accelerationBtnPressed()
+ ICWidget.acceleration_timer.timeout.connect(
+ lambda: ICWidget.updateSpeedAndEngineRpm("Accelerate"), rate)
+ ICWidget.acceleration_timer.start(100)
+ time.sleep(5)
+ ICWidget.accelerationBtnReleased()
+ ICWidget.acceleration_timer.timeout.connect(
+ lambda: ICWidget.updateSpeedAndEngineRpm("Decelerate"), rate)
+ ICWidget.acceleration_timer.start(100)
+ time.sleep(5)
+
+ def stop_script(self):
+ ICWidget.reset()
+
+ # enable all widgets in the scroll area
+ for widget in ICWidget.scrollAreaWidgetContents.children():
+ widget.setEnabled(True)
+
+if __name__ == '__main__':
+ app = QApplication(sys.argv)
+ w = ICWidget()
+ w.show()
+ sys.exit(app.exec_()) \ No newline at end of file