1#!/usr/bin/env python 2# 3# Copyright (C) 2017 The Android Open Source Project 4# 5# Licensed under the Apache License, Version 2.0 (the "License"); 6# you may not use this file except in compliance with the License. 7# You may obtain a copy of the License at 8# 9# http://www.apache.org/licenses/LICENSE-2.0 10# 11# Unless required by applicable law or agreed to in writing, software 12# distributed under the License is distributed on an "AS IS" BASIS, 13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14# See the License for the specific language governing permissions and 15# limitations under the License. 16# 17 18# A simple GUI to remotely actuate the Vehicle HAL via the eumalator 19 20import argparse 21import sys 22from threading import Thread 23from PyQt5 import QtCore 24from PyQt5 import QtWidgets 25 26import VehicleHalProto_pb2 27from vhal_emulator import Vhal 28import vhal_consts_2_0 as c 29 30 31# Define a simple thread that receives messages from a vhal object (v) and prints them 32def rxThread(v): 33 while(1): 34 msg = v.rxMsg() 35 if (msg.msg_type == VehicleHalProto_pb2.SET_PROPERTY_RESP): 36 if msg.status == 0: 37 print('Success (%s)' % msg.status) 38 else: 39 print('Error (%s)' % msg.status) 40 else: 41 print(msg) 42 43 44# Main window setup 45def window(): 46 app = QtWidgets.QApplication(sys.argv) 47 dialog = QtWidgets.QDialog() 48 dialog.setWindowTitle('VHal Driver') 49 dialog.setGeometry(100, 100, 200, 50) 50 topLevelLayout = QtWidgets.QHBoxLayout() 51 dialog.setLayout(topLevelLayout) 52 53 shiftLayout = QtWidgets.QVBoxLayout() 54 topLevelLayout.addLayout(shiftLayout) 55 56 gearTitle = QtWidgets.QLabel(dialog) 57 gearTitle.setText('Gear Shift') 58 shiftLayout.addWidget(gearTitle) 59 60 gearDisplay = QtWidgets.QLabel(dialog) 61 shiftLayout.addWidget(gearDisplay) 62 63 slider = QtWidgets.QSlider(QtCore.Qt.Vertical) 64 slider.setMinimum(0) 65 slider.setMaximum(2) 66 slider.setInvertedAppearance(True) 67 slider.valueChanged.connect(lambda:sliderMove(slider, gearDisplay)) 68 shiftLayout.addWidget(slider) 69 sliderMove(slider, gearDisplay) 70 71 72 buttonLayout = QtWidgets.QVBoxLayout() 73 topLevelLayout.addLayout(buttonLayout) 74 75 signalButtonGroup = QtWidgets.QButtonGroup() 76 77 bNoSignal = QtWidgets.QPushButton('None') 78 bNoSignal.setCheckable(True) 79 bNoSignal.setChecked(True) 80 buttonLayout.addWidget(bNoSignal) 81 signalButtonGroup.addButton(bNoSignal) 82 83 bHazards = QtWidgets.QPushButton('Hazards') 84 bHazards.setCheckable(True) 85 buttonLayout.addWidget(bHazards) 86 signalButtonGroup.addButton(bHazards) 87 88 bLeft = QtWidgets.QPushButton('Left') 89 bLeft.setCheckable(True) 90 buttonLayout.addWidget(bLeft) 91 signalButtonGroup.addButton(bLeft) 92 93 bRight = QtWidgets.QPushButton('Right') 94 bRight.setCheckable(True) 95 buttonLayout.addWidget(bRight) 96 signalButtonGroup.addButton(bRight) 97 98 signalButtonGroup.buttonClicked.connect(lambda: onSignalClicked(signalButtonGroup)) 99 100 dialog.show() 101 sys.exit(app.exec_()) 102 103 104def onSignalClicked(group): 105 print('signal ' + group.checkedButton().text() + ' is active') 106 try: 107 vhal.setProperty(c.VEHICLEPROPERTY_TURN_SIGNAL_STATE, 0, group.checkedId()) 108 except: 109 print('Ignoring error setting property 0x{:08X}'.format(c.VEHICLEPROPERTY_TURN_SIGNAL_STATE)) 110 111 112def sliderMove(slider, gearDisplay): 113 if slider.value() == 0: 114 gearName = 'park' 115 vhal.setProperty(c.VEHICLEPROPERTY_GEAR_SELECTION, 0, c.VEHICLEGEAR_GEAR_PARK) 116 elif slider.value() == 1: 117 gearName = 'reverse' 118 vhal.setProperty(c.VEHICLEPROPERTY_GEAR_SELECTION, 0, c.VEHICLEGEAR_GEAR_REVERSE) 119 elif slider.value() == 2: 120 gearName = 'drive' 121 vhal.setProperty(c.VEHICLEPROPERTY_GEAR_SELECTION, 0, c.VEHICLEGEAR_GEAR_DRIVE) 122 else: 123 gearName = 'UNK' 124 print('slider %s requested %s = %s' % (slider.objectName(), slider.value(), gearName)) 125 gearDisplay.setText(gearName) 126 127 128if __name__ == '__main__': 129 parser = argparse.ArgumentParser(description='Vehicle HAL Driver UI') 130 parser.add_argument('--serial', '-s', action='store', dest='serial', 131 default=None, required=False, help='Select which device to connect to') 132 args = parser.parse_args() 133 print('Starting VHal driver GUI') 134 vhal = Vhal(c.vhal_types_2_0, device=args.serial) 135 136 # Start a receive thread to consume any replies from the vhal 137 print('Starting receiver thread') 138 rx = Thread(target=rxThread, args=(vhal,)) 139 rx.setDaemon(True) 140 rx.start() 141 142 # Put the car in park so we start in a known state (consistent with the GUI default state) 143 vhal.setProperty(c.VEHICLEPROPERTY_GEAR_SELECTION, 0, c.VEHICLEGEAR_GEAR_PARK) 144 145 # Start the main UI -- never returns 146 window() 147