diff options
Diffstat (limited to 'Widgets/ICPage.py')
-rw-r--r-- | Widgets/ICPage.py | 353 |
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 |