Compare commits
36 Commits
cec09d6bcb
...
GUI
| Author | SHA1 | Date | |
|---|---|---|---|
| c33804c747 | |||
| bc9381627c | |||
| 1bf8b589a3 | |||
| 9f0d7120ee | |||
| b60b053aed | |||
| 231862f06b | |||
| ba50a8ce69 | |||
| 8cc959504f | |||
| e1474b162c | |||
| f54e0d2fdb | |||
| 3215bb2733 | |||
| 2dd1612ec8 | |||
| 44ef5ce6cb | |||
| eb116803d0 | |||
| 79986fc85f | |||
| 8f0661a73d | |||
| df6e27783e | |||
| ac60af82a0 | |||
| f085cd8e8b | |||
| ceb2f538b8 | |||
| aa96a3b3c2 | |||
| 3f6aedffbc | |||
| c6cd1c0b99 | |||
| 2d42f090dd | |||
| c42864e46d | |||
| d0196b4a31 | |||
| eb76c18efc | |||
| ec71dd1094 | |||
| 61160fcd70 | |||
| cb8c861407 | |||
| 81e704cf7a | |||
| 84da201ed8 | |||
| 237708e155 | |||
| 9ec76323d7 | |||
| 5d676828d4 | |||
| e55927dbe2 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,3 +1,4 @@
|
||||
/build
|
||||
/dist
|
||||
OutGaugeInterpreter.spec
|
||||
*.csv
|
||||
117
GAME_METHODS/FORZA_METHODS.py
Normal file
117
GAME_METHODS/FORZA_METHODS.py
Normal file
@@ -0,0 +1,117 @@
|
||||
import struct
|
||||
FORZA_DATA_FORMAT = '<iIfffffffffffffffffffffffffffiiiiffffffffffffffffffffiiiiifffffffffffffffffHBBBBBBbbbffffi'
|
||||
|
||||
|
||||
def unpackData(unpackedData):
|
||||
carData = {
|
||||
"IsRaceOn":unpackedData[0], #// = 1 when race is on. = 0 when in menus/race stopped …
|
||||
#// Can overflow to 0 eventually
|
||||
"TimestampMS": unpackedData[1],
|
||||
"EngineMaxRpm": unpackedData[2],
|
||||
"EngineIdleRpm": unpackedData[3],
|
||||
"CurrentEngineRpm": unpackedData[4],
|
||||
#// In the car's local space; X = right, Y = up, Z = forward
|
||||
"AccelerationX": unpackedData[5],
|
||||
"AccelerationY": unpackedData[6],
|
||||
"AccelerationZ": unpackedData[7],
|
||||
#// In the car's local space; X = right, Y = up, Z = forward
|
||||
"VelocityX": unpackedData[8],
|
||||
"VelocityY": unpackedData[9],
|
||||
"VelocityZ": unpackedData[10],
|
||||
#// In the car's local space; X = pitch, Y = yaw, Z = roll
|
||||
"AngularVelocityX": unpackedData[11],
|
||||
"AngularVelocityY": unpackedData[12],
|
||||
"AngularVelocityZ": unpackedData[13],
|
||||
"Yaw": unpackedData[14],
|
||||
"Pitch": unpackedData[15],
|
||||
"Roll": unpackedData[16],
|
||||
#// Suspension travel normalized: 0.0f = max stretch; 1.0 = max compression
|
||||
"NormalizedSuspensionTravelFrontLeft": unpackedData[17],
|
||||
"NormalizedSuspensionTravelFrontRight": unpackedData[18],
|
||||
"NormalizedSuspensionTravelRearLeft": unpackedData[19],
|
||||
"NormalizedSuspensionTravelRearRight": unpackedData[20],
|
||||
#// Tire normalized slip ratio, = 0 means 100% grip and |ratio| > 1.0 means loss of grip.
|
||||
"TireSlipRatioFrontLeft": unpackedData[21],
|
||||
"TireSlipRatioFrontRight": unpackedData[22],
|
||||
"TireSlipRatioRearLeft": unpackedData[23],
|
||||
"TireSlipRatioRearRight": unpackedData[24],
|
||||
#// Wheels rotation speed radians/sec.
|
||||
"WheelRotationSpeedFrontLeft": unpackedData[25],
|
||||
"WheelRotationSpeedFrontRight": unpackedData[26],
|
||||
"WheelRotationSpeedRearLeft": unpackedData[27],
|
||||
"WheelRotationSpeedRearRight": unpackedData[28],
|
||||
#// = 1 when wheel is on rumble strip, = 0 when off.
|
||||
"WheelOnRumbleStripFrontLeft": unpackedData[29],
|
||||
"WheelOnRumbleStripFrontRight": unpackedData[30],
|
||||
"WheelOnRumbleStripRearLeft": unpackedData[31],
|
||||
"WheelOnRumbleStripRearRight": unpackedData[32],
|
||||
#// = from 0 to 1, where 1 is the deepest puddle
|
||||
"WheelInPuddleDepthFrontLeft": unpackedData[33],
|
||||
"WheelInPuddleDepthFrontRight": unpackedData[34],
|
||||
"WheelInPuddleDepthRearLeft": unpackedData[35],
|
||||
"WheelInPuddleDepthRearRight": unpackedData[36],
|
||||
#// Non-dimensional surface rumble values passed to controller force feedback
|
||||
"SurfaceRumbleFrontLeft": unpackedData[37],
|
||||
"SurfaceRumbleFrontRight": unpackedData[38],
|
||||
"SurfaceRumbleRearLeft": unpackedData[39],
|
||||
"SurfaceRumbleRearRight": unpackedData[40],
|
||||
#// Tire normalized slip angle, = 0 means 100% grip and |angle| > 1.0 means loss of grip.
|
||||
"TireSlipAngleFrontLeft": unpackedData[41],
|
||||
"TireSlipAngleFrontRight": unpackedData[42],
|
||||
"TireSlipAngleRearLeft": unpackedData[43],
|
||||
"TireSlipAngleRearRight": unpackedData[44],
|
||||
#// Tire normalized combined slip, = 0 means 100% grip and |slip| > 1.0 means loss of grip.
|
||||
"TireCombinedSlipFrontLeft": unpackedData[45],
|
||||
"TireCombinedSlipFrontRight": unpackedData[46],
|
||||
"TireCombinedSlipRearLeft": unpackedData[47],
|
||||
"TireCombinedSlipRearRight": unpackedData[48],
|
||||
#// Actual suspension travel in meters
|
||||
"SuspensionTravelMetersFrontLeft": unpackedData[49],
|
||||
"SuspensionTravelMetersFrontRight": unpackedData[50],
|
||||
"SuspensionTravelMetersRearLeft": unpackedData[51],
|
||||
"SuspensionTravelMetersRearRight": unpackedData[52],
|
||||
#// Unique ID of the car make/model
|
||||
"CarOrdinal": unpackedData[53],
|
||||
#// Between 0 (D -- worst cars) and 7 (X class -- best cars) inclusive
|
||||
"CarClass": unpackedData[54],
|
||||
#// Between 100 (worst car) and 999 (best car) inclusive
|
||||
"CarPerformanceIndex": unpackedData[55],
|
||||
#// 0 = FWD, 1 = RWD, 2 = AWD
|
||||
"DrivetrainType": unpackedData[56],
|
||||
#// Number of cylinders in the engine
|
||||
"NumCylinders": unpackedData[57],
|
||||
"PositionX": unpackedData[58],
|
||||
"PositionY": unpackedData[59],
|
||||
"PositionZ": unpackedData[60],
|
||||
"Speed": unpackedData[61],
|
||||
"Power": unpackedData[62],
|
||||
"Torque": unpackedData[63],
|
||||
"TireTempFrontLeft": unpackedData[64],
|
||||
"TireTempFrontRight": unpackedData[65],
|
||||
"TireTempRearLeft": unpackedData[66],
|
||||
"TireTempRearRight": unpackedData[67],
|
||||
"Boost": unpackedData[68],
|
||||
"Fuel": unpackedData[69],
|
||||
"DistanceTraveled": unpackedData[70],
|
||||
"BestLap": unpackedData[71],
|
||||
"LastLap": unpackedData[72],
|
||||
"CurrentLap": unpackedData[73],
|
||||
"CurrentRaceTime": unpackedData[74],
|
||||
"LapNumber": unpackedData[75],
|
||||
"RacePosition": unpackedData[76],
|
||||
"Accel": unpackedData[77],
|
||||
"Brake": unpackedData[78],
|
||||
"Clutch": unpackedData[79],
|
||||
"HandBrake": unpackedData[80],
|
||||
"Gear": unpackedData[81],
|
||||
"Steer": unpackedData[82],
|
||||
"NormalizedDrivingLine": unpackedData[83],
|
||||
"NormalizedAIBrakeDifference": unpackedData[84],
|
||||
"TireWearFrontLeft": unpackedData[85],
|
||||
"TireWearFrontRight": unpackedData[86],
|
||||
"TireWearRearLeft": unpackedData[87],
|
||||
"TireWearRearRight": unpackedData[88],
|
||||
#// ID for track
|
||||
"TrackOrdinal": unpackedData[89]
|
||||
}
|
||||
return carData
|
||||
@@ -1 +1 @@
|
||||
__all__= ["BEAMNG_METHODS"]
|
||||
__all__= ["BEAMNG_METHODS","FORZA_METHODS"]
|
||||
Binary file not shown.
BIN
GAME_METHODS/__pycache__/BEAMNG_METHODS.cpython-312.pyc
Normal file
BIN
GAME_METHODS/__pycache__/BEAMNG_METHODS.cpython-312.pyc
Normal file
Binary file not shown.
BIN
GAME_METHODS/__pycache__/FORZA_METHODS.cpython-311.pyc
Normal file
BIN
GAME_METHODS/__pycache__/FORZA_METHODS.cpython-311.pyc
Normal file
Binary file not shown.
BIN
GAME_METHODS/__pycache__/FORZA_METHODS.cpython-312.pyc
Normal file
BIN
GAME_METHODS/__pycache__/FORZA_METHODS.cpython-312.pyc
Normal file
Binary file not shown.
Binary file not shown.
BIN
GAME_METHODS/__pycache__/__init__.cpython-312.pyc
Normal file
BIN
GAME_METHODS/__pycache__/__init__.cpython-312.pyc
Normal file
Binary file not shown.
@@ -1,23 +1,210 @@
|
||||
#please use python 3.11
|
||||
import socket
|
||||
import struct
|
||||
import tkinter.ttk
|
||||
import serial
|
||||
import time
|
||||
import platform
|
||||
import sys
|
||||
import threading
|
||||
from enum import Enum
|
||||
import tkinter
|
||||
from GAME_METHODS import *
|
||||
##import BEAMNG_FILES as BEAM
|
||||
import functools #used for passing variables into callbacks
|
||||
|
||||
##check if python 3.11 is running code and exit if not
|
||||
|
||||
if not(sys.version_info[0] == 3 and sys.version_info[1] == 11):
|
||||
raise Exception("code must be run in python verion 3.11.\ninput struct for network needs to be changed per python version.")
|
||||
|
||||
|
||||
toPi=serial.Serial('COM5',115200) #connect to arduino
|
||||
UDP_IP = "127.0.0.1"
|
||||
UDP_PORT = 4444
|
||||
|
||||
sock = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
|
||||
sock.bind((UDP_IP,UDP_PORT))
|
||||
print("waiting for data:\n")
|
||||
##system init
|
||||
runningOs = platform.system()
|
||||
|
||||
UDP_IP = "0.0.0.0"
|
||||
BEAMNG_UDP_PORT = 4444
|
||||
FORZA_UDP_PORT= 4843
|
||||
SERIAL_PORT = ""
|
||||
firstRun= True #used to do the headers for csv files
|
||||
pushingToArduino = False
|
||||
stopThread = False
|
||||
|
||||
class GameType(Enum):
|
||||
NONE=0
|
||||
BEAMNG = "BEAMNG"
|
||||
FORZA = "FORZA"
|
||||
|
||||
class ProgramState(Enum):
|
||||
PUSHING_DATA = 1
|
||||
WAITNG = 2
|
||||
ERROR = 3
|
||||
|
||||
appState =ProgramState.WAITNG
|
||||
connectedArduino = False
|
||||
connectedWebSocket = False
|
||||
gameSelected = False
|
||||
portToConnect= 0
|
||||
carData = 0
|
||||
csvOut = False
|
||||
|
||||
#Functions <-------------------------------------------------------------------------------------------
|
||||
def checkBoxChange():
|
||||
if tkLoggingEnabled.get()==True:
|
||||
loggingLocationEntry.config(state='normal')
|
||||
loggingLocationText.set(value= "./"+ tkGametype.get()+"_"+str(int(time.time()))+".csv")
|
||||
loggingLocationEntry.insert(0,loggingLocationText.get())
|
||||
else:
|
||||
loggingLocationText.set(value="")
|
||||
loggingLocationEntry.delete(0,tkinter.END)
|
||||
loggingLocationEntry.config(state='disabled')
|
||||
|
||||
def csvWriteOut(firstRun,carData,csvFile):
|
||||
if firstRun == True:
|
||||
out=""
|
||||
for im,nm in carData.items():
|
||||
out = out + im + ","
|
||||
csvFile.write(out + "\n")
|
||||
outString=""
|
||||
for itm,num in carData.items():
|
||||
outString = outString + str(num) +","
|
||||
csvFile.write(outString+"\n")
|
||||
|
||||
def runningThread():
|
||||
global stopThread
|
||||
gameType= tkGametype.get()
|
||||
|
||||
#check everything is connected
|
||||
if gameType==GameType.BEAMNG.value:
|
||||
portToConnect=BEAMNG_UDP_PORT
|
||||
elif gameType==GameType.FORZA.value:
|
||||
portToConnect=FORZA_UDP_PORT
|
||||
|
||||
try:
|
||||
sock = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
|
||||
sock.bind((UDP_IP,portToConnect))
|
||||
except:
|
||||
print("please check you are able to open the socket on this system\ntired to open port: "+portToConnect)
|
||||
exit()
|
||||
|
||||
try:
|
||||
toPi=serial.Serial(SERIAL_PORT,115200,timeout=2) #connect to arduino
|
||||
except:
|
||||
print("please check connection to arduino and verify the correct serial port")
|
||||
exit()
|
||||
|
||||
csvOut = tkLoggingEnabled.get()
|
||||
|
||||
if csvOut == True:
|
||||
csvFile = open(loggingLocationText.get(),"a")
|
||||
|
||||
|
||||
while True:
|
||||
|
||||
while not stopThread:
|
||||
data, addr = sock.recvfrom(1024)
|
||||
if gameType == GameType.BEAMNG.value:
|
||||
unpackedData = struct.unpack(BEAMNG_METHODS.BEAMNG_DATA_FORMAT,data)
|
||||
carData = BEAMNG_METHODS.unpackData(unpackedData)
|
||||
kmh=carData["speed"]*3.6
|
||||
toPi.write(str(kmh).encode()+":".encode())
|
||||
if csvOut == True:
|
||||
csvWriteOut(firstRun,carData,csvFile)
|
||||
firstRun=False
|
||||
elif gameType == GameType.FORZA.value:
|
||||
unpackedData = struct.unpack(FORZA_METHODS.FORZA_DATA_FORMAT,data)
|
||||
carData = FORZA_METHODS.unpackData(unpackedData)
|
||||
kmh = carData["Speed"]*3.6
|
||||
if csvOut == True:
|
||||
csvWriteOut(firstRun,carData,csvFile)
|
||||
firstRun=False
|
||||
|
||||
try:
|
||||
toPi.write(str(kmh).encode()+":".encode())
|
||||
except:
|
||||
print("arduino disconnected please check connection\n")
|
||||
|
||||
|
||||
#code runs from here<-----------------------------------------------------------------------------------------------------------
|
||||
|
||||
if runningOs == 'Windows':
|
||||
print("Windows detacted setting serial port to 'COM5'\n")
|
||||
SERIAL_PORT= 'COM5'
|
||||
elif runningOs == 'Linux':
|
||||
print("Linux detected setting serial port to '/dev/ttyAMC0'\n")
|
||||
SERIAL_PORT = '/dev/ttyACM0'
|
||||
else:
|
||||
print("OS detection failed setting serial port to 'COM5'\n")
|
||||
SERIAL_PORT = 'COM5'
|
||||
|
||||
def startStopButtonFunc():
|
||||
global appState
|
||||
if appState == ProgramState.WAITNG:
|
||||
startStopButton.config(text="stop")
|
||||
appState = ProgramState.PUSHING_DATA
|
||||
stopThread=False
|
||||
workThread = threading.Thread(target=runningThread)
|
||||
workThread.start()
|
||||
statusTextVar.set("Running...")
|
||||
|
||||
elif appState == ProgramState.PUSHING_DATA:
|
||||
startStopButton.config(text="start")
|
||||
appState = ProgramState.WAITNG
|
||||
statusTextVar.set("Waiting...")
|
||||
stopThread = True
|
||||
|
||||
##GUI init
|
||||
rootFrameGui= tkinter.Tk()
|
||||
rootFrameGui.title("Car speed to arduino")
|
||||
##rootFrameGui.geometry("400x200")
|
||||
|
||||
bottomBarFrameGui = tkinter.ttk.Frame(rootFrameGui,padding=0,relief="groove",borderwidth=2,)
|
||||
bottomBarFrameGui.pack(anchor="se",side="bottom",fill="x")
|
||||
firstFrameGui=tkinter.ttk.Frame(rootFrameGui,padding=2,relief="groove",borderwidth=2)
|
||||
firstFrameGui.pack(anchor="ne",side="left",expand=True)
|
||||
secondFrameGui=tkinter.ttk.Frame(rootFrameGui,padding=2,relief="groove",borderwidth=2)
|
||||
secondFrameGui.pack(anchor="nw",side="right",expand=True)
|
||||
serialFrameGui = tkinter.ttk.Frame(firstFrameGui,padding=5,relief="groove",borderwidth=2)
|
||||
serialFrameGui.pack(anchor="nw")
|
||||
serialLableText =tkinter.StringVar()
|
||||
serialLableText.set("Serial Port:")
|
||||
serialLable = tkinter.Label(serialFrameGui,textvariable=serialLableText)
|
||||
serialLable.pack(side="top")
|
||||
serialEntry=tkinter.Entry(serialFrameGui)
|
||||
serialEntry.insert(0,SERIAL_PORT)
|
||||
serialEntry.pack(side="left")
|
||||
tkGametype = tkinter.StringVar()
|
||||
tkGametype.set(GameType.BEAMNG.value)
|
||||
gameSelectFrame= tkinter.ttk.Frame(secondFrameGui,padding=5,relief="groove",borderwidth=2)
|
||||
gameSelectFrame.pack(anchor="se")
|
||||
gameSelectText= tkinter.StringVar()
|
||||
gameSelectText.set("Select Game:")
|
||||
gameSelectLable = tkinter.Label(gameSelectFrame,textvariable=gameSelectText)
|
||||
gameSelectLable.pack(side="top")
|
||||
tkinter.Label(gameSelectFrame,textvariable=gameSelectText)
|
||||
gameSelectLOptions= [GameType.BEAMNG,GameType.FORZA]
|
||||
##gameSelectRadioButtons
|
||||
##space after text is so the buttons are the same size
|
||||
tkinter.Radiobutton(gameSelectFrame,text="BeamNG ",variable=tkGametype,value=GameType.BEAMNG.value,command=checkBoxChange).pack(anchor="w")
|
||||
tkinter.Radiobutton(gameSelectFrame,text="Forza ",variable=tkGametype,value=GameType.FORZA.value,command=checkBoxChange).pack(anchor="w")
|
||||
loggingFrameGui= tkinter.ttk.Frame(firstFrameGui,padding=5,relief="groove",borderwidth=2)
|
||||
loggingFrameGui.pack(anchor="nw")
|
||||
tkLoggingEnabled=tkinter.BooleanVar()
|
||||
tkLoggingEnabled.set(False)
|
||||
tkinter.Checkbutton(loggingFrameGui,text="Enable Logging",variable=tkLoggingEnabled,onvalue=True,offvalue=False,command=checkBoxChange).pack(side="top")
|
||||
loggingLocationText = tkinter.StringVar(value="")
|
||||
loggingLocationEntry = tkinter.Entry(loggingFrameGui)
|
||||
loggingLocationEntry.insert(0,loggingLocationText.get())
|
||||
loggingLocationEntry.config(state='disabled')
|
||||
loggingLocationEntry.pack(side="bottom")
|
||||
startStopButton= tkinter.Button(bottomBarFrameGui,text= "start",command= startStopButtonFunc)
|
||||
startStopButton.pack(side="right",fill="x")
|
||||
statusTextVar = tkinter.StringVar(value="waiting...")
|
||||
statusLable = tkinter.Label(bottomBarFrameGui,textvariable= statusTextVar)
|
||||
statusLable.pack(anchor="w")
|
||||
arduinoConnectedFrameGui = tkinter.ttk.Frame(secondFrameGui,padding=5,relief="groove",borderwidth=2)
|
||||
arduinoConnectedFrameGui.pack()
|
||||
arduinoConnectedStatusText=tkinter.StringVar(value="Connected OK")
|
||||
arduinoConnectedLable = tkinter.Label(arduinoConnectedFrameGui,textvariable=arduinoConnectedStatusText)
|
||||
arduinoConnectedLable.pack()
|
||||
|
||||
|
||||
rootFrameGui.mainloop()
|
||||
|
||||
3
createExe.sh
Normal file
3
createExe.sh
Normal file
@@ -0,0 +1,3 @@
|
||||
pyinstaller.exe --add-data .\GAME_METHODS\*:.\GAME_METHODS\ --onefile .\OutGaugeInterpreter.py
|
||||
pyinstaller.exe --add-data .\GAME_METHODS\*:.\GAME_METHODS\ --onefile --noconsole --optimize 2 .\OutGaugeInterpreter.py
|
||||
pyinstaller --add-data ./GAME_METHODS/*:./GAME_METHODS/ --onefile ./OutGaugeInterpreter.py
|
||||
Reference in New Issue
Block a user