summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorsuchinton2001 <suchinton.2001@gmail.com>2023-10-30 18:58:40 +0530
committersuchinton2001 <suchinton.2001@gmail.com>2023-11-02 03:44:53 +0530
commitf9aef30f5e78f0f4e179948946d043c4725712d5 (patch)
treed9edbcda6ac219155e9ef5642001395d694f1e9a
parentedfd96499fdbcf869c5182f2cfa89703cfb0dfd0 (diff)
agl-demo-control-panel: Improve gRPC Mode
V1: - Remove redundant calls to set instance - Start client as soon as connection is established V2: - Enable Subscriptions in gRPC mode - Fix UI stutter by blocking signals for specific widgets - Fix Vehicle Simulator, no longer run thread as a daemon SPEC-4905 Signed-off-by: suchinton2001 <suchinton.2001@gmail.com> Change-Id: Iea3b9ce0532c1ebae530aed9dddd92d72ff4dd7b
-rw-r--r--Widgets/ICPage.py4
-rw-r--r--Widgets/settings.py26
-rw-r--r--extras/FeedKuksa.py8
-rw-r--r--extras/Kuksa_Instance.py18
-rw-r--r--extras/UI_Handeler.py156
5 files changed, 159 insertions, 53 deletions
diff --git a/Widgets/ICPage.py b/Widgets/ICPage.py
index d4d6592..9106249 100644
--- a/Widgets/ICPage.py
+++ b/Widgets/ICPage.py
@@ -68,10 +68,8 @@ class ICWidget(Base, Form):
self.setupUi(self)
self.IC = IC_Paths()
- # self.vehicle_simulator = VehicleSimulator(self)
self.feed_kuksa = FeedKuksa()
- self.feed_kuksa.start()
self.vehicle_simulator = VehicleSimulator()
header_frame = self.findChild(QWidget, "header_frame")
@@ -380,7 +378,7 @@ class VehicleSimulator(QObject):
self.engine_speed = self.DEFAULT_IDLE_RPM
self.running = False
self.lock = threading.Lock()
- self.thread = threading.Thread(target=self.run, daemon=True)
+ self.thread = threading.Thread(target=self.run)
def start(self):
if not self.running:
diff --git a/Widgets/settings.py b/Widgets/settings.py
index d6efb66..72b1bea 100644
--- a/Widgets/settings.py
+++ b/Widgets/settings.py
@@ -12,7 +12,6 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-
from extras import config
import extras.Kuksa_Instance as kuksa_instance
@@ -40,8 +39,9 @@ Form, Base = uic.loadUiType(os.path.join(
# ========================================
+# Global Variables
Steering_Signal_Type = "Kuksa"
-
+Protocol = None
def create_animated_toggle():
return AnimatedToggle(
@@ -71,6 +71,7 @@ class settings(Base, Form):
"""
super(self.__class__, self).__init__(parent)
self.setupUi(self)
+ self.client = None
self.SSL_toggle = create_animated_toggle()
self.Protocol_toggle = create_animated_toggle()
@@ -101,6 +102,10 @@ class settings(Base, Form):
self.startClientBtn.setCheckable(True)
self.startClientBtn.setStyleSheet("border: 1px solid green;")
+ self.Hide_IC = self.findChild(QPushButton, "Hide_IC")
+ self.Hide_HVAC = self.findChild(QPushButton, "Hide_HVAC")
+ self.Hide_HUD = self.findChild(QPushButton, "Hide_HUD")
+
self.startClientBtn.clicked.connect(self.start_stop_client)
self.reconnectBtn.clicked.connect(self.reconnectClient)
self.SSL_toggle.clicked.connect(self.toggleSSL)
@@ -126,7 +131,7 @@ class settings(Base, Form):
def start_stop_client(self):
if self.startClientBtn.isChecked():
self.set_instance()
- if self.client is not None:
+ elif self.client is not None:
self.client.stop()
self.refreshThread = RefreshThread(self)
@@ -158,9 +163,12 @@ class settings(Base, Form):
Steering_Signal_Type = "Kuksa"
def get_protocol(self):
+ global Protocol
if (not self.Protocol_toggle.isChecked()):
+ Protocol = "ws"
return "ws"
else:
+ Protocol = "grpc"
return "grpc"
def set_instance(self):
@@ -229,20 +237,20 @@ class settings(Base, Form):
"""
if (self.client is not None):
try:
- config = self.make_new_config()
self.client.stop()
- self.client = self.kuksa.reconnect(config, self.kuksa_token)
- self.client.start()
+ self.client = self.kuksa.reconnect(
+ self.make_new_config(), self.kuksa_token)
self.refreshThread = RefreshThread(self)
self.refreshThread.start()
except Exception as e:
logging.error(e)
- self.set_instance()
+ else:
+ self.set_instance()
- self.refreshThread = RefreshThread(self)
- self.refreshThread.start()
+ self.refreshThread = RefreshThread(self)
+ self.refreshThread.start()
def make_new_config(self):
"""
diff --git a/extras/FeedKuksa.py b/extras/FeedKuksa.py
index 75846d5..955361d 100644
--- a/extras/FeedKuksa.py
+++ b/extras/FeedKuksa.py
@@ -16,9 +16,8 @@
import logging
from PyQt5.QtCore import QThread
-from PyQt5.QtCore import pyqtSignal, QObject
+from PyQt5.QtCore import pyqtSignal
from . import Kuksa_Instance as kuksa_instance
-from . import UI_Handeler
import threading
class FeedKuksa(QThread):
@@ -50,9 +49,6 @@ class FeedKuksa(QThread):
QThread.__init__(self, parent)
self.stop_flag = False
- self.sending_values.connect(UI_Handeler.UI_Handeler.block_updates)
- self.finished_sending_values.connect(UI_Handeler.UI_Handeler.unblock_updates)
-
def run(self):
"""
Starts the thread and sets the instance of the Kuksa client.
@@ -93,7 +89,7 @@ class FeedKuksa(QThread):
Exception
If there is an error sending values to Kuksa.
"""
-
+
if self.client is None:
logging.error("Kuksa client is None, try reconnecting")
return
diff --git a/extras/Kuksa_Instance.py b/extras/Kuksa_Instance.py
index cda539e..6add5af 100644
--- a/extras/Kuksa_Instance.py
+++ b/extras/Kuksa_Instance.py
@@ -17,7 +17,6 @@
from typing import Optional
import kuksa_client as kuksa
import threading
-import time
class KuksaClientSingleton:
@@ -73,6 +72,7 @@ class KuksaClientSingleton:
raise Exception("This class is a singleton!")
self.client = None
+ self.kuksa_config = None
KuksaClientSingleton._instance = self
@@ -89,9 +89,10 @@ class KuksaClientSingleton:
"""
if self.client:
self.client.stop()
-
- self.client = kuksa.KuksaClientThread(config)
- self.client.authorize(token)
+ self.kuksa_config = config
+ self.token = token
+ self.client = kuksa.KuksaClientThread(self.kuksa_config)
+ self.client.authorize(self.token)
self.client.start()
def get_client(self):
@@ -114,6 +115,15 @@ class KuksaClientSingleton:
dict: The configuration for KuksaClientThread.
"""
return self.kuksa_config
+
+ def get_token(self):
+ """
+ Returns the path to the token file.
+
+ Returns:
+ str: The path to the token file.
+ """
+ return self.token
def status(self):
"""
diff --git a/extras/UI_Handeler.py b/extras/UI_Handeler.py
index a44cf1a..9139c53 100644
--- a/extras/UI_Handeler.py
+++ b/extras/UI_Handeler.py
@@ -20,26 +20,80 @@ from PyQt5.QtCore import QPropertyAnimation
from PyQt5.QtWidgets import QWidget
from PyQt5.QtCore import QEasingCurve
from PyQt5.QtWidgets import QGraphicsOpacityEffect
+from kuksa_client.grpc import Field, SubscribeEntry, View
+from kuksa_client.grpc.aio import VSSClient
+from PyQt5.QtCore import pyqtSignal
+import asyncio
+from PyQt5.QtCore import QThread
+import pathlib
import logging
import json
from . import Kuksa_Instance as kuksa_instance
+from Widgets import settings
# Global variables
subscribed = False
-block_subscription_updates = False
-class UI_Handeler(MainWindow):
+class GrpcSubscriptionThread(QThread):
+ updateReceived = pyqtSignal(str, str)
+
+ def __init__(self):
+ QThread.__init__(self)
+ self.client = None
+
+ def run(self):
+ config = kuksa_instance.KuksaClientSingleton.instance().get_config()
+ token = kuksa_instance.KuksaClientSingleton.instance().get_token()
+
+ SUBSCRIPTION_ENTRIES = [
+ SubscribeEntry('Vehicle.Speed', View.FIELDS, (Field.VALUE,)),
+ SubscribeEntry('Vehicle.Powertrain.CombustionEngine.Speed', View.FIELDS, (Field.VALUE,)),
+ SubscribeEntry('Vehicle.Body.Lights.DirectionIndicator.Left.IsSignaling', View.FIELDS, (Field.VALUE,)),
+ SubscribeEntry('Vehicle.Body.Lights.DirectionIndicator.Right.IsSignaling', View.FIELDS, (Field.VALUE,)),
+ SubscribeEntry('Vehicle.Body.Lights.Hazard.IsSignaling', View.FIELDS, (Field.VALUE,)),
+ SubscribeEntry('Vehicle.Powertrain.FuelSystem.Level', View.FIELDS, (Field.VALUE,)),
+ SubscribeEntry('Vehicle.Powertrain.CombustionEngine.ECT', View.FIELDS, (Field.VALUE,)),
+ SubscribeEntry('Vehicle.Powertrain.Transmission.SelectedGear', View.FIELDS, (Field.VALUE,)),
+ SubscribeEntry('Vehicle.Cabin.HVAC.Station.Row1.Left.Temperature', View.FIELDS, (Field.VALUE,)),
+ SubscribeEntry('Vehicle.Cabin.HVAC.Station.Row1.Left.FanSpeed', View.FIELDS, (Field.VALUE,)),
+ SubscribeEntry('Vehicle.Cabin.HVAC.Station.Row1.Right.Temperature', View.FIELDS, (Field.VALUE,)),
+ SubscribeEntry('Vehicle.Cabin.HVAC.Station.Row1.Right.FanSpeed', View.FIELDS, (Field.VALUE,)),
+ ]
+
+ async def grpc_subscription(client):
+ try:
+ await client.connect()
+ async for updates in client.subscribe(entries=SUBSCRIPTION_ENTRIES):
+ for update in updates:
+ if update.entry.value is not None:
+ self.updateReceived.emit(str(update.entry.path),
+ str(update.entry.value.value))
+ client.disconnect()
+ except Exception as e:
+ logging.error(f"Error during gRPC subscription: {e}")
+
+ try:
+ client = VSSClient(host=config['ip'],
+ port=config['port'],
+ token=token,
+ root_certificates=pathlib.Path(config['cacertificate']),
+ tls_server_name=config['tls_server_name'])
+ asyncio.set_event_loop(asyncio.new_event_loop())
+ loop = asyncio.get_event_loop()
+ loop.run_until_complete(grpc_subscription(client))
+ except Exception as e:
+ logging.error(f"Error during gRPC subscription: {e}")
+
+
+class UI_Handeler(MainWindow):
def fullscreen(self):
self.headerContainer.hide()
self.setAttribute(QtCore.Qt.WA_TranslucentBackground, False)
self.showFullScreen()
- def display_sending_message(self):
- print("message sent")
-
def block_updates():
global block_subscription_updates
block_subscription_updates = True
@@ -133,79 +187,119 @@ class UI_Handeler(MainWindow):
"Vehicle.Cabin.HVAC.Station.Row1.Right.Temperature",
"Vehicle.Cabin.HVAC.Station.Row1.Right.FanSpeed"]
- for signal in signals:
- self.client.subscribe(
- signal, lambda data: UI_Handeler.VSS_callback(self, data), 'value')
+ if settings.Protocol == "ws":
+ for signal in signals:
+ self.client.subscribe(
+ signal, lambda data: UI_Handeler.VSS_callback(self, data), 'value')
+ if settings.Protocol == "grpc":
+ self.worker = GrpcSubscriptionThread()
+ self.worker.updateReceived.connect(
+ lambda path, value: UI_Handeler.VSS_callback(self=self, path=path, value=value))
+ self.worker.start()
subscribed = True
else:
subscribed = False
logging.error(
"Kuksa client is not connected, try reconnecting")
- def VSS_callback(self, data):
+ def VSS_callback(self, data=None, path=None, value=None):
"""
This method is the callback function for the VSS signals from Kuksa.
Args:
- data: The data received from the signal.
"""
- global block_subscription_updates
- if block_subscription_updates:
- return
IC_Page = self.stackedWidget.widget(1)
HVAC_Page = self.stackedWidget.widget(2)
- info = json.loads(data)
- path = info.get('data', {}).get('path')
- value = info.get('data', {}).get('dp', {}).get('value')
-
- print(f"Received subscription event: {path} {value}")
+ if data is not None:
+ info = json.loads(data)
+ path = info.get('data', {}).get('path')
+ value = info.get('data', {}).get('dp', {}).get('value')
- if path == "Vehicle.Speed":
+ if path == "Vehicle.Speed" and int(float(value)):
+ # block connection updates for IC_Page.Speed_slider.
+ IC_Page.Speed_slider.blockSignals(True)
+ IC_Page.Speed_slider.setValue(int(float(value)))
+ IC_Page.Speed_slider.blockSignals(False)
IC_Page.Speed_monitor.display(int(IC_Page.Speed_slider.value()))
- IC_Page.Speed_slider.setValue(int(value))
- if path == "Vehicle.Powertrain.CombustionEngine.Speed":
- IC_Page.RPM_slider.setValue(int(value))
+ if path == "Vehicle.Powertrain.CombustionEngine.Speed" and int(float(value)):
+ # block connection updates for IC_Page.RPM_slider.
+ IC_Page.RPM_slider.blockSignals(True)
IC_Page.RPM_monitor.display(int(IC_Page.RPM_slider.value()))
+ IC_Page.RPM_slider.setValue(int(float(value)))
+ IC_Page.RPM_slider.blockSignals(False)
if path == "Vehicle.Body.Lights.DirectionIndicator.Left.IsSignaling":
+ IC_Page.leftIndicatorBtn.blockSignals(True)
IC_Page.leftIndicatorBtn.setChecked(bool(value))
+ IC_Page.leftIndicatorBtn.blockSignals(False)
if path == "Vehicle.Body.Lights.DirectionIndicator.Right.IsSignaling":
+ IC_Page.rightIndicatorBtn.blockSignals(True)
IC_Page.rightIndicatorBtn.setChecked(bool(value))
+ IC_Page.rightIndicatorBtn.blockSignals(False)
if path == "Vehicle.Body.Lights.Hazard.IsSignaling":
+ IC_Page.hazardBtn.blockSignals(True)
IC_Page.hazardBtn.setChecked(bool(value))
+ IC_Page.hazardBtn.blockSignals(False)
if path == "Vehicle.Powertrain.FuelSystem.Level":
- IC_Page.fuelLevel_slider.setValue(int(value))
+ IC_Page.fuelLevel_slider.blockSignals(True)
+ IC_Page.fuelLevel_slider.setValue(int(float(value)))
+ IC_Page.fuelLevel_slider.blockSignals(False)
if path == "Vehicle.Powertrain.CombustionEngine.ECT":
- IC_Page.coolantTemp_slider.setValue(int(value))
+ IC_Page.coolantTemp_slider.blockSignals(True)
+ IC_Page.coolantTemp_slider.setValue(int(float(value)))
+ IC_Page.coolantTemp_slider.blockSignals(False)
if path == "Vehicle.Powertrain.Transmission.SelectedGear":
- if int(value) == 127:
+ if int(float(value)) == 127:
+ IC_Page.driveBtn.blockSignals(True)
IC_Page.driveBtn.setChecked(True)
- elif int(value) == 126:
+ IC_Page.driveBtn.blockSignals(False)
+ elif int(float(value)) == 126:
+ IC_Page.parkBtn.blockSignals(True)
IC_Page.parkBtn.setChecked(True)
- elif int(value) == -1:
+ IC_Page.parkBtn.blockSignals(False)
+ elif int(float(value)) == -1:
+ IC_Page.reverseBtn.blockSignals(True)
IC_Page.reverseBtn.setChecked(True)
- elif int(value) == 0:
+ IC_Page.reverseBtn.blockSignals(False)
+ elif int(float(value)) == 0:
+ IC_Page.neutralBtn.blockSignals(True)
IC_Page.neutralBtn.setChecked(True)
+ IC_Page.neutralBtn.blockSignals(False)
if path == "Vehicle.Cabin.HVAC.Station.Row1.Left.Temperature":
- HVAC_Page.left_temp.setValue(int(value))
+ HVAC_Page.leftTempList.blockSignals(True)
+ item = HVAC_Page.leftTempList.findItems(
+ str(int(float(value))) + "°C", QtCore.Qt.MatchExactly)[0]
+ HVAC_Page.leftTempList.setCurrentItem(item)
+ HVAC_Page.leftTempList.scrollToItem(item, 1)
+ HVAC_Page.leftTempList.blockSignals(False)
if path == "Vehicle.Cabin.HVAC.Station.Row1.Left.FanSpeed":
- HVAC_Page.left_fan.setValue(int(value))
+ HVAC_Page.leftFanSpeed_slider.blockSignals(True)
+ HVAC_Page.leftFanSpeed_slider.setValue(int(float(value)))
+ HVAC_Page.leftFanSpeed_slider.blockSignals(False)
if path == "Vehicle.Cabin.HVAC.Station.Row1.Right.Temperature":
- HVAC_Page.right_temp.setValue(int(value))
+ HVAC_Page.rightTempList.blockSignals(True)
+ item = HVAC_Page.leftTempList.findItems(
+ str(int(float(value))) + "°C", QtCore.Qt.MatchExactly)[0]
+ HVAC_Page.leftTempList.setCurrentItem(item)
+ HVAC_Page.rightTempList.scrollToItem(item, 1)
+ HVAC_Page.rightTempList.blockSignals(False)
if path == "Vehicle.Cabin.HVAC.Station.Row1.Right.FanSpeed":
- HVAC_Page.right_fan.setValue(int(value))
+ HVAC_Page.rightFanSpeed_slider.blockSignals(True)
+ HVAC_Page.rightFanSpeed_slider.setValue(int(float(value)))
+ HVAC_Page.rightFanSpeed_slider.blockSignals(False)
class FaderWidget(QWidget):