Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- import time
- import serial
- import minimalmodbus
- import os
- from xml.etree import ElementTree as et
- import tkinter as tk
- import tkinter.font as tkFont
- # import numpy as np
- # import pandas as pd
- # ASI Parameter Dictionary; check execution directory
- filename = 'ASIObjectDictionary.xml'
- filepath = os.path.abspath(os.path.join('', filename))
- xml = et.parse(filepath)
- Obdic = xml.iterfind('Parameters/ParameterDescription')
- # Basic serial connection parameters for RS232/RTU MODBUS protocol
- BACport = "COM4"
- BACaddress = 1
- BACmode = minimalmodbus.MODE_RTU
- BACbaudrate = 115200
- BACbytesize = 8
- BACparity = serial.PARITY_NONE
- BACstopbits = 1
- BACtimeout = 0.2
- instr = minimalmodbus.Instrument(BACport, BACaddress)
- instr.address = BACaddress
- instr.mode = BACmode
- instr.serial.baudrate = BACbaudrate
- instr.serial.bytesize = BACbytesize
- instr.serial.parity = BACparity
- instr.serial.stopbits = BACstopbits
- instr.serial.timeout = BACtimeout
- # Build dictionary definitions
- ObdicAddress = {}
- ObdicScale = {}
- ObdicUnit = {}
- ObdicEnum = {} # "enumerated" bitwise parameters, returns bit key/name as string
- ObdicBit = {} # "bit vector" bitwise parameters, returns bit key/name as
- RegsDic = {}
- for parent in Obdic: # InternalAppEntity/Parameters/ParameterDescription children
- scale = parent.find('Scale').text
- if scale == ('enum'):
- key = parent.find('Key').text
- address = int(parent.find('Address').text)
- ObdicAddress.update({key: address})
- Enumerations = parent.findall('Enumerations/string') #Text string
- # print('Parent: ', key)
- for EnumBit, EnumKey in enumerate(Enumerations): #returns loop number from 0 for first term EnumBit, while EnumKey = iteration of each Enumerations element
- ObdicEnum.update({key: {EnumKey.text: EnumBit}}) # Ordered to return bit controlling key:string e.g.
- # 'Speed_Regulator_Mode: Torque Mode with Speed Limiting'
- # print('Parent: ', key, 'Enumbit: ', EnumBit, 'Enumstring: ', EnumKey.text) #et.tostring(EnumKey, encoding='unicode'))
- # ***print('Address: ', address, 'EnumParent: ', key, 'bit: ', EnumBit, 'key: ',
- # EnumKey.text) # ObdicEnum[key][EnumKey.text] to return positional bit
- else:
- if scale == ('bit vector'):
- key = parent.find('Key').text
- address = int(parent.find('Address').text)
- ObdicAddress.update({key: address})
- Bits = parent.findall('BitArray/Bit/Key')
- for Bit, BitKey in enumerate(Bits):
- ObdicBit.update({key: {BitKey.text: Bit}})
- # print('BitParent: ', key, 'bit: ', Bit, 'key:', BitKey.text)
- # ****print('Address: ', address, 'BitParent: ', key, 'bit: ', Bit, 'key:', BitKey.text)
- # for Enum in EnumString:
- # enu = Enumerations.find('string')
- # ObdicEnum.update({key: {EnumBit: enu}})
- # print('Key: ', key, 'Bit: ', EnumBit, 'String: ', enu)
- else:
- try: # Convert scale strings to integers where possible, floats where not
- scale = int(scale)
- except ValueError:
- try:
- scale = float(scale)
- except ValueError:
- pass
- # print('Float scale error: ', scale, 'Scale type: ', type(scale))
- key = parent.find('Key').text
- address = int(parent.find('Address').text)
- try:
- unit = parent.find('Units').text
- except AttributeError:
- unit = 'None'
- ObdicAddress.update({key: address})
- ObdicUnit.update({key: unit})
- ObdicScale.update({address: scale}) # originally key: scale, changed to Address for RegsRead;
- # faster to avoid lookup of string for each address to lookup scale,
- # than to just iterate through addresses.
- print('Address: ', address, 'ValueParent: ', key, 'Unit: ', unit, 'Scale: ', scale)
- # MODBUS I/O functions
- # Read MODBUS register address by input key
- def RegRead(f1):
- return (instr.read_register(int(ObdicAddress[f1]), 0) / float(ObdicScale[f1])) # 0 = no decimal scaling
- # print(RegRead('Maximum_Field_Weakening_Current'))
- def RegsRead(f1, f2): # Consider unmapping from ObdicAddress? Will only be used a few times at specific address number ranges. Number may be preferable.
- RegsList = (instr.read_registers(int(ObdicAddress[f1]), f2))
- for c, d in enumerate(RegsList):
- #return int(ObdicAddress[f1], d)
- e = int(ObdicAddress[f1]) + c #MODBUS Address for each enumeration
- f = (d/ObdicScale[e])
- # print('Address: ', e, 'c: ', c, 'Scale: ', ObdicScale[e])
- print('Address: ', e, 'f: ', type(f), 'Scale: ', type(ObdicScale[e]))
- RegsDic.update({e: f}) # Should return key address:value pairs
- #print('Address: ', e, 'Value: ', d)
- def RegWrite(f1, f2):
- address = int(ObdicAddress[f1])
- try:
- instr.write_register(ObdicAddress[f1], f2)
- except IOError:
- print("Failed to read from controller!")
- finally:
- print('Address: ', address, 'Key: ', f1, 'Write value: ', f2)
- def RegWriteScaled(f1, f2):
- """Write MODBUS register address key/name (f1), for value f2 after scaling to value of that key"""
- address = int(ObdicAddress[f1])
- try:
- instr.write_register(address, ObdicScale[(ObdicAddress[f1])] * f2)
- except IOError:
- print("Failed to read from controller!")
- finally:
- print('Address: ', address, 'Key:', f1, 'Write value: ', f2, 'Scale: ', ObdicScale[(ObdicAddress[f1])])
- # Display Command function definitions
- def ProfileWrite(f):
- """Function to switch between profiles by integer"""
- if f == 1:
- RegWriteScaled('Maximum_Field_Weakening_Current', 50)
- RegWriteScaled('Rated_Motor_Current', 450)
- RegWriteScaled('Remote_Maximum_Battery_Current_Limit', 300)
- else:
- if f == 2:
- RegWriteScaled('Maximum_Field_Weakening_Current', 0)
- RegWriteScaled('Rated_Motor_Current', 220)
- RegWriteScaled('Remote_Maximum_Battery_Current_Limit', 200)
- else:
- if f == 3:
- RegWriteScaled('Maximum_Field_Weakening_Current', 0)
- RegWriteScaled('Rated_Motor_Current', 220)
- RegWriteScaled('Remote_Maximum_Battery_Current_Limit', 15)
- def ProfileSelect(f):
- # a = f.cget('value') # Get integer of attribute 'value' from button_profile1
- # ProfileSelectVar = a
- # print('Profile', a)
- ProfileWrite(f.cget('value')) # Enable to actually write value to profile
- def AssistSelect(f):
- # print('AssistScale variable: ', AssistSelectVar.get(), 'Type: ', type(AssistSelectVar.get()), 'f: ', f, 'ftype: ', type(f))
- # scaleint = AssistSelectVar.get() + int(f)
- # AssistScale.set(value = AssistSelectVar.get() + int(f))
- AssistScale.set(int(f))
- print("AssistScale:", f)
- RegWrite('Remote_Assist_Mode', int(f))
- ## TKINTER GUI init
- #Create main window
- root = tk.Tk()
- root.title("AmPy")
- #root.geometry("1872x1404")
- #Define font types
- BigFont = tkFont.Font(family='Arial Bold', size = '32', weight='bold')
- FrameFont = tkFont.Font(family='Helvetica', size = '16')
- #Define Tk variables
- ProfileSelectVar = tk.IntVar(0)
- AssistSelectVar = tk.IntVar(0)
- VehicleSpeedVar = tk.IntVar(0)
- MotorTempVar = tk.IntVar(0)
- MotorCurrentVar = tk.IntVar(0)
- BatteryVoltageVar = tk.IntVar(0)
- BatteryCurrentVar = tk.IntVar(0)
- BatterySOCVar = tk.IntVar(0)
- LastFaultVar = tk.IntVar(0)
- # Profiles widget
- ProfileFrame = tk.LabelFrame(root, text="Global Profiles", font=(FrameFont), padx=5, pady=5, relief='ridge',
- borderwidth=6)
- ProfileFrame.pack(padx=10, pady=5)
- # Convert to radio buttons!
- button_profile1 = tk.Radiobutton(ProfileFrame, text="1", bg='white', highlightcolor='white',
- highlightbackground='black', padx=30, pady=25, font=(BigFont),
- indicatoron=0, value=1, variable='ProfileSelectVar',
- command=lambda: ProfileSelect(button_profile1))
- button_profile2 = tk.Radiobutton(ProfileFrame, text="2", bg='white', highlightcolor='white',
- highlightbackground='black', padx=30, pady=25, font=(BigFont),
- indicatoron=0, value=2, variable='ProfileSelectVar',
- command=lambda: ProfileSelect(button_profile2))
- button_profile3 = tk.Radiobutton(ProfileFrame, text="3", bg='white', highlightcolor='white',
- highlightbackground='black', padx=30, pady=25, font=(BigFont),
- indicatoron=0, value=3, variable='ProfileSelectVar',
- command=lambda: ProfileSelect(button_profile3))
- button_profile1.pack(side="left")
- button_profile2.pack(side="left")
- button_profile3.pack(side="left")
- # When adding control functionality, create function to set active profile to 'sunken' border and others to 'raised'
- # Digital assist widget
- AssistFrame = tk.LabelFrame(root, text="Assist Profiles", font=(FrameFont), padx=5, pady=5, relief='ridge',
- borderwidth=6)
- # Digital assist scale widget
- AssistScaleY = 55
- AssistScaleX = 200 # Define all scale factors and group together up in #Setup
- AssistScale = tk.Scale(AssistFrame, font=(BigFont), orient=tk.HORIZONTAL, from_=0, to=9,
- length=AssistScaleX, width=AssistScaleY, variable=AssistSelectVar,
- command=AssistSelect)
- # Digital assist increment buttons (Look for image in this folder)
- DownbtnFilepath = os.path.abspath(os.path.join('', 'L.png'))
- Downbtn = tk.PhotoImage(file=DownbtnFilepath)
- UpbtnFilepath = os.path.abspath(os.path.join('', 'R.png'))
- Upbtn = tk.PhotoImage(file=UpbtnFilepath)
- AssistDownbtn = tk.Button(AssistFrame, image=Downbtn, border=0,
- command=lambda: AssistScale.set(AssistScale.get() - 1))
- AssistUpbtn = tk.Button(AssistFrame, image=Upbtn, border=0, command=lambda: AssistScale.set(AssistScale.get() + 1))
- AssistDownbtn.pack(side='left', anchor=tk.S)
- AssistScale.pack(side='left')
- AssistUpbtn.pack(side='left', anchor=tk.S)
- #Read core variables debug labelframe
- DisplayFrame = tk.LabelFrame(root, text="Fastloop Variables", font=(FrameFont), padx=5, pady=5, relief='ridge',
- borderwidth=6)
- VehicleSpeed = tk.Label(DisplayFrame, textvariable=VehicleSpeedVar, font=(BigFont), padx=5, pady=5)
- MotorTemp = tk.Label(DisplayFrame, textvariable=MotorTempVar, font = (BigFont), padx=5, pady=5)
- MotorCurrent = tk.Label(DisplayFrame, textvariable=MotorCurrentVar, font = (BigFont), padx=5, pady=5)
- BatteryVoltage = tk.Label(DisplayFrame, textvariable=BatteryVoltageVar, font = (BigFont), padx=5, pady=5)
- BatteryCurrent = tk.Label(DisplayFrame, textvariable=BatteryCurrentVar, font = (BigFont), padx=5, pady=5)
- BatterySOC = tk.Label(DisplayFrame, textvariable=BatterySOCVar, font = (BigFont), padx=5, pady=5)
- LastFault = tk.Label(DisplayFrame, textvariable=LastFaultVar, font = (BigFont), padx=5, pady=5)
- VehicleSpeedLabel = tk.Label(DisplayFrame, text='VehicleSpeed: ', font=(BigFont), padx=5, pady=5)
- MotorTempLabel = tk.Label(DisplayFrame, text='MotorTemp', font = (BigFont), padx=5, pady=5)
- MotorCurrentLabel = tk.Label(DisplayFrame, text='MotorCurrent', font = (BigFont), padx=5, pady=5)
- BatteryVoltageLabel = tk.Label(DisplayFrame, text='BatteryVoltage', font = (BigFont), padx=5, pady=5)
- BatteryCurrentLabel = tk.Label(DisplayFrame, text='BatteryCurrent', font = (BigFont), padx=5, pady=5)
- BatterySOCLabel = tk.Label(DisplayFrame, text='BatterySOC', font = (BigFont), padx=5, pady=5)
- LastFaultLabel = tk.Label(DisplayFrame, text='LastFault', font = (BigFont), padx=5, pady=5)
- VehicleSpeedUnits = tk.Label(DisplayFrame, text='Km/H ', font=(BigFont), pady=5)
- MotorTempUnits = tk.Label(DisplayFrame, text='C', font = (BigFont), pady=5)
- MotorCurrentUnits = tk.Label(DisplayFrame, text='A', font = (BigFont), pady=5)
- BatteryVoltageUnits = tk.Label(DisplayFrame, text='V', font = (BigFont), pady=5)
- BatteryCurrentUnits = tk.Label(DisplayFrame, text='A', font = (BigFont), pady=5)
- BatterySOCUnits = tk.Label(DisplayFrame, text='%', font = (BigFont), pady=5)
- VehicleSpeedLabel.grid(column=0, row=0)
- MotorTempLabel.grid(column=0, row=1)
- MotorCurrentLabel.grid(column=0, row=2)
- BatteryVoltageLabel.grid(column=0, row=3)
- BatteryCurrentLabel.grid(column=0, row=4)
- BatterySOCLabel.grid(column=0, row=5)
- LastFaultLabel.grid(column=0, row=6)
- VehicleSpeed.grid(column=1, row=0)
- MotorTemp.grid(column=1, row=1)
- MotorCurrent.grid(column=1, row=2)
- BatteryVoltage.grid(column=1, row=3)
- BatteryCurrent.grid(column=1, row=4)
- BatterySOC.grid(column=1, row=5)
- LastFault.grid(column=1, row=6)
- VehicleSpeedUnits.grid(column=2, row=0)
- MotorTempUnits.grid(column=2, row=1)
- MotorCurrentUnits.grid(column=2, row=2)
- BatteryVoltageUnits.grid(column=2, row=3)
- BatteryCurrentUnits.grid(column=2, row=4)
- BatterySOCUnits.grid(column=2, row=5)
- # Speed gauge
- # Fast loop refresher tasks
- def Fastloop():
- RegsRead('Vehicle_Speed',8)
- print(RegsDic)
- VehicleSpeedVar.set(int(round(0.621371*RegsDic[260])))
- MotorTempVar.set(RegsDic[261])
- MotorCurrentVar.set(RegsDic[262])
- BatteryVoltageVar.set(RegsDic[265])
- BatteryCurrentVar.set(RegsDic[266])
- BatterySOCVar.set(RegsDic[267])
- #LastFaultVar.set(RegsDic[269]) #Read by itself, as this is Bit Vector register! Requires own dict {bit, string}
- # root.after(1000, DisplayFrame.update())
- DisplayFrame.update()
- # Debug button
- Debutton = tk.Button(root, text='Debutton', font=FrameFont, command=Fastloop)
- # Lay out
- ProfileFrame.grid(column=0, row=0)
- # ProfileFrame.place(relx=0.5, rely=0.5, anchor = tk.CENTER)
- AssistFrame.grid(column=1, row=0)
- DisplayFrame.grid(column=0, row=1)
- Debutton.grid(column=2, row=0)
- # Fastloop()
- tk.mainloop()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement