Advertisement
xenodius

AmPy

Sep 1st, 2020 (edited)
649
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 14.02 KB | None | 0 0
  1. import time
  2. import serial
  3. import minimalmodbus
  4. import os
  5. from xml.etree import ElementTree as et
  6. import tkinter as tk
  7. import tkinter.font as tkFont
  8. # import numpy as np
  9. # import pandas as pd
  10.  
  11. # ASI Parameter Dictionary; check execution directory
  12. filename = 'ASIObjectDictionary.xml'
  13. filepath = os.path.abspath(os.path.join('', filename))
  14. xml = et.parse(filepath)
  15. Obdic = xml.iterfind('Parameters/ParameterDescription')
  16.  
  17. # Basic serial connection parameters for RS232/RTU MODBUS protocol
  18. BACport = "COM4"
  19. BACaddress = 1
  20. BACmode = minimalmodbus.MODE_RTU
  21. BACbaudrate = 115200
  22. BACbytesize = 8
  23. BACparity = serial.PARITY_NONE
  24. BACstopbits = 1
  25. BACtimeout = 0.2
  26.  
  27. instr = minimalmodbus.Instrument(BACport, BACaddress)
  28. instr.address = BACaddress
  29. instr.mode = BACmode
  30. instr.serial.baudrate = BACbaudrate
  31. instr.serial.bytesize = BACbytesize
  32. instr.serial.parity = BACparity
  33. instr.serial.stopbits = BACstopbits
  34. instr.serial.timeout = BACtimeout
  35.  
  36. # Build dictionary definitions
  37. ObdicAddress = {}
  38. ObdicScale = {}
  39. ObdicUnit = {}
  40. ObdicEnum = {} # "enumerated" bitwise parameters, returns bit key/name as string
  41. ObdicBit = {} # "bit vector" bitwise parameters, returns bit key/name as
  42. RegsDic = {}
  43.  
  44. for parent in Obdic:  # InternalAppEntity/Parameters/ParameterDescription children
  45.     scale = parent.find('Scale').text
  46.     if scale == ('enum'):
  47.         key = parent.find('Key').text
  48.         address = int(parent.find('Address').text)
  49.         ObdicAddress.update({key: address})
  50.         Enumerations = parent.findall('Enumerations/string')  #Text string
  51.         # print('Parent: ', key)
  52.         for EnumBit, EnumKey in enumerate(Enumerations):  #returns loop number from 0 for first term EnumBit, while EnumKey = iteration of each Enumerations element
  53.             ObdicEnum.update({key: {EnumKey.text: EnumBit}})  # Ordered to return bit controlling key:string e.g.
  54.             # 'Speed_Regulator_Mode: Torque Mode with Speed Limiting'
  55.             # print('Parent: ', key, 'Enumbit: ', EnumBit, 'Enumstring: ', EnumKey.text) #et.tostring(EnumKey, encoding='unicode'))
  56.             # ***print('Address: ', address, 'EnumParent: ', key, 'bit: ', EnumBit, 'key: ',
  57.                  # EnumKey.text)  # ObdicEnum[key][EnumKey.text] to return positional bit
  58.     else:
  59.         if scale == ('bit vector'):
  60.             key = parent.find('Key').text
  61.             address = int(parent.find('Address').text)
  62.             ObdicAddress.update({key: address})
  63.             Bits = parent.findall('BitArray/Bit/Key')
  64.             for Bit, BitKey in enumerate(Bits):
  65.                 ObdicBit.update({key: {BitKey.text: Bit}})
  66.                 # print('BitParent: ', key, 'bit: ', Bit, 'key:', BitKey.text)
  67.                 # ****print('Address: ', address, 'BitParent: ', key, 'bit: ', Bit, 'key:', BitKey.text)
  68.                 # for Enum in EnumString:
  69.                 #    enu = Enumerations.find('string')
  70.                 #    ObdicEnum.update({key: {EnumBit: enu}})
  71.                 #    print('Key: ', key, 'Bit: ', EnumBit, 'String: ', enu)
  72.         else:
  73.             try: # Convert scale strings to integers where possible, floats where not
  74.                 scale = int(scale)
  75.             except ValueError:
  76.                 try:
  77.                     scale = float(scale)
  78.                 except ValueError:
  79.                     pass
  80.                     # print('Float scale error: ', scale, 'Scale type: ', type(scale))
  81.             key = parent.find('Key').text
  82.             address = int(parent.find('Address').text)
  83.             try:
  84.                 unit = parent.find('Units').text
  85.             except AttributeError:
  86.                 unit = 'None'
  87.             ObdicAddress.update({key: address})
  88.             ObdicUnit.update({key: unit})
  89.             ObdicScale.update({address: scale}) # originally key: scale, changed to Address for RegsRead;
  90.                                             # faster to avoid lookup of string for each address to lookup scale,
  91.                                             # than to just iterate through addresses.
  92.             print('Address: ', address, 'ValueParent: ', key, 'Unit: ', unit, 'Scale: ', scale)
  93.  
  94. # MODBUS I/O functions
  95. # Read MODBUS register address by input key
  96.  
  97. def RegRead(f1):
  98.     return (instr.read_register(int(ObdicAddress[f1]), 0) / float(ObdicScale[f1])) # 0 = no decimal scaling
  99. # print(RegRead('Maximum_Field_Weakening_Current'))
  100.  
  101. def RegsRead(f1, f2): # Consider unmapping from ObdicAddress? Will only be used a few times at specific address number ranges. Number may be preferable.
  102.     RegsList = (instr.read_registers(int(ObdicAddress[f1]), f2))
  103.     for c, d in enumerate(RegsList):
  104.         #return int(ObdicAddress[f1], d)
  105.         e = int(ObdicAddress[f1]) + c #MODBUS Address for each enumeration
  106.         f = (d/ObdicScale[e])
  107.         # print('Address: ', e, 'c: ', c, 'Scale: ', ObdicScale[e])
  108.         print('Address: ', e, 'f: ', type(f), 'Scale: ', type(ObdicScale[e]))
  109.         RegsDic.update({e: f}) # Should return key address:value pairs
  110.         #print('Address: ', e, 'Value: ', d)
  111.  
  112. def RegWrite(f1, f2):
  113.     address = int(ObdicAddress[f1])
  114.     try:
  115.         instr.write_register(ObdicAddress[f1], f2)
  116.     except IOError:
  117.         print("Failed to read from controller!")
  118.     finally:
  119.         print('Address: ', address, 'Key: ', f1, 'Write value: ', f2)
  120.  
  121. def RegWriteScaled(f1, f2):
  122.     """Write MODBUS register address key/name (f1), for value f2 after scaling to value of that key"""
  123.     address = int(ObdicAddress[f1])
  124.     try:
  125.         instr.write_register(address, ObdicScale[(ObdicAddress[f1])] * f2)
  126.     except IOError:
  127.         print("Failed to read from controller!")
  128.     finally:
  129.         print('Address: ', address, 'Key:', f1, 'Write value: ', f2, 'Scale: ', ObdicScale[(ObdicAddress[f1])])
  130.  
  131. # Display Command function definitions
  132.  
  133. def ProfileWrite(f):
  134.     """Function to switch between profiles by integer"""
  135.     if f == 1:
  136.         RegWriteScaled('Maximum_Field_Weakening_Current', 50)
  137.         RegWriteScaled('Rated_Motor_Current', 450)
  138.         RegWriteScaled('Remote_Maximum_Battery_Current_Limit', 300)
  139.     else:
  140.         if f == 2:
  141.             RegWriteScaled('Maximum_Field_Weakening_Current', 0)
  142.             RegWriteScaled('Rated_Motor_Current', 220)
  143.             RegWriteScaled('Remote_Maximum_Battery_Current_Limit', 200)
  144.         else:
  145.             if f == 3:
  146.                 RegWriteScaled('Maximum_Field_Weakening_Current', 0)
  147.                 RegWriteScaled('Rated_Motor_Current', 220)
  148.                 RegWriteScaled('Remote_Maximum_Battery_Current_Limit', 15)
  149.  
  150. def ProfileSelect(f):
  151.     # a = f.cget('value')  # Get integer of attribute 'value' from button_profile1
  152.     # ProfileSelectVar = a
  153.     # print('Profile', a)
  154.     ProfileWrite(f.cget('value'))  # Enable to actually write value to profile
  155.  
  156. def AssistSelect(f):
  157.     # print('AssistScale variable: ', AssistSelectVar.get(), 'Type: ', type(AssistSelectVar.get()), 'f: ', f, 'ftype: ', type(f))
  158.     # scaleint = AssistSelectVar.get() + int(f)
  159.     # AssistScale.set(value = AssistSelectVar.get() + int(f))
  160.     AssistScale.set(int(f))
  161.     print("AssistScale:", f)
  162.     RegWrite('Remote_Assist_Mode', int(f))
  163.  
  164. ## TKINTER GUI init
  165. #Create main window
  166. root = tk.Tk()
  167. root.title("AmPy")
  168. #root.geometry("1872x1404")
  169.  
  170. #Define font types
  171. BigFont = tkFont.Font(family='Arial Bold', size = '32', weight='bold')
  172. FrameFont = tkFont.Font(family='Helvetica', size = '16')
  173.  
  174. #Define Tk variables
  175. ProfileSelectVar = tk.IntVar(0)
  176. AssistSelectVar = tk.IntVar(0)
  177.  
  178. VehicleSpeedVar = tk.IntVar(0)
  179. MotorTempVar = tk.IntVar(0)
  180. MotorCurrentVar = tk.IntVar(0)
  181. BatteryVoltageVar = tk.IntVar(0)
  182. BatteryCurrentVar = tk.IntVar(0)
  183. BatterySOCVar = tk.IntVar(0)
  184. LastFaultVar = tk.IntVar(0)
  185.  
  186. # Profiles widget
  187. ProfileFrame = tk.LabelFrame(root, text="Global Profiles", font=(FrameFont), padx=5, pady=5, relief='ridge',
  188.                              borderwidth=6)
  189. ProfileFrame.pack(padx=10, pady=5)
  190.  
  191. # Convert to radio buttons!
  192. button_profile1 = tk.Radiobutton(ProfileFrame, text="1", bg='white', highlightcolor='white',
  193.                                  highlightbackground='black', padx=30, pady=25, font=(BigFont),
  194.                                  indicatoron=0, value=1, variable='ProfileSelectVar',
  195.                                  command=lambda: ProfileSelect(button_profile1))
  196. button_profile2 = tk.Radiobutton(ProfileFrame, text="2", bg='white', highlightcolor='white',
  197.                                  highlightbackground='black', padx=30, pady=25, font=(BigFont),
  198.                                  indicatoron=0, value=2, variable='ProfileSelectVar',
  199.                                  command=lambda: ProfileSelect(button_profile2))
  200. button_profile3 = tk.Radiobutton(ProfileFrame, text="3", bg='white', highlightcolor='white',
  201.                                  highlightbackground='black', padx=30, pady=25, font=(BigFont),
  202.                                  indicatoron=0, value=3, variable='ProfileSelectVar',
  203.                                  command=lambda: ProfileSelect(button_profile3))
  204. button_profile1.pack(side="left")
  205. button_profile2.pack(side="left")
  206. button_profile3.pack(side="left")
  207. # When adding control functionality, create function to set active profile to 'sunken' border and others to 'raised'
  208.  
  209. # Digital assist widget
  210. AssistFrame = tk.LabelFrame(root, text="Assist Profiles", font=(FrameFont), padx=5, pady=5, relief='ridge',
  211.                             borderwidth=6)
  212.  
  213. # Digital assist scale widget
  214. AssistScaleY = 55
  215. AssistScaleX = 200  # Define all scale factors and group together up in #Setup
  216. AssistScale = tk.Scale(AssistFrame, font=(BigFont), orient=tk.HORIZONTAL, from_=0, to=9,
  217.                        length=AssistScaleX, width=AssistScaleY, variable=AssistSelectVar,
  218.                        command=AssistSelect)
  219.  
  220. # Digital assist increment buttons (Look for image in this folder)
  221. DownbtnFilepath = os.path.abspath(os.path.join('', 'L.png'))
  222. Downbtn = tk.PhotoImage(file=DownbtnFilepath)
  223. UpbtnFilepath = os.path.abspath(os.path.join('', 'R.png'))
  224. Upbtn = tk.PhotoImage(file=UpbtnFilepath)
  225. AssistDownbtn = tk.Button(AssistFrame, image=Downbtn, border=0,
  226.                           command=lambda: AssistScale.set(AssistScale.get() - 1))
  227. AssistUpbtn = tk.Button(AssistFrame, image=Upbtn, border=0, command=lambda: AssistScale.set(AssistScale.get() + 1))
  228.  
  229. AssistDownbtn.pack(side='left', anchor=tk.S)
  230. AssistScale.pack(side='left')
  231. AssistUpbtn.pack(side='left', anchor=tk.S)
  232.  
  233. #Read core variables debug labelframe
  234. DisplayFrame = tk.LabelFrame(root, text="Fastloop Variables", font=(FrameFont), padx=5, pady=5, relief='ridge',
  235.                              borderwidth=6)
  236. VehicleSpeed = tk.Label(DisplayFrame, textvariable=VehicleSpeedVar, font=(BigFont), padx=5, pady=5)
  237. MotorTemp = tk.Label(DisplayFrame, textvariable=MotorTempVar, font = (BigFont), padx=5, pady=5)
  238. MotorCurrent = tk.Label(DisplayFrame, textvariable=MotorCurrentVar, font = (BigFont), padx=5, pady=5)
  239. BatteryVoltage = tk.Label(DisplayFrame, textvariable=BatteryVoltageVar, font = (BigFont), padx=5, pady=5)
  240. BatteryCurrent = tk.Label(DisplayFrame, textvariable=BatteryCurrentVar, font = (BigFont), padx=5, pady=5)
  241. BatterySOC = tk.Label(DisplayFrame, textvariable=BatterySOCVar, font = (BigFont), padx=5, pady=5)
  242. LastFault = tk.Label(DisplayFrame, textvariable=LastFaultVar, font = (BigFont), padx=5, pady=5)
  243.  
  244. VehicleSpeedLabel = tk.Label(DisplayFrame, text='VehicleSpeed: ', font=(BigFont), padx=5, pady=5)
  245. MotorTempLabel = tk.Label(DisplayFrame, text='MotorTemp', font = (BigFont), padx=5, pady=5)
  246. MotorCurrentLabel = tk.Label(DisplayFrame, text='MotorCurrent', font = (BigFont), padx=5, pady=5)
  247. BatteryVoltageLabel = tk.Label(DisplayFrame, text='BatteryVoltage', font = (BigFont), padx=5, pady=5)
  248. BatteryCurrentLabel = tk.Label(DisplayFrame, text='BatteryCurrent', font = (BigFont), padx=5, pady=5)
  249. BatterySOCLabel = tk.Label(DisplayFrame, text='BatterySOC', font = (BigFont), padx=5, pady=5)
  250. LastFaultLabel = tk.Label(DisplayFrame, text='LastFault', font = (BigFont), padx=5, pady=5)
  251.  
  252. VehicleSpeedUnits = tk.Label(DisplayFrame, text='Km/H ', font=(BigFont), pady=5)
  253. MotorTempUnits = tk.Label(DisplayFrame, text='C', font = (BigFont), pady=5)
  254. MotorCurrentUnits = tk.Label(DisplayFrame, text='A', font = (BigFont), pady=5)
  255. BatteryVoltageUnits = tk.Label(DisplayFrame, text='V', font = (BigFont), pady=5)
  256. BatteryCurrentUnits = tk.Label(DisplayFrame, text='A', font = (BigFont), pady=5)
  257. BatterySOCUnits = tk.Label(DisplayFrame, text='%', font = (BigFont), pady=5)
  258.  
  259. VehicleSpeedLabel.grid(column=0, row=0)
  260. MotorTempLabel.grid(column=0, row=1)
  261. MotorCurrentLabel.grid(column=0, row=2)
  262. BatteryVoltageLabel.grid(column=0, row=3)
  263. BatteryCurrentLabel.grid(column=0, row=4)
  264. BatterySOCLabel.grid(column=0, row=5)
  265. LastFaultLabel.grid(column=0, row=6)
  266.  
  267. VehicleSpeed.grid(column=1, row=0)
  268. MotorTemp.grid(column=1, row=1)
  269. MotorCurrent.grid(column=1, row=2)
  270. BatteryVoltage.grid(column=1, row=3)
  271. BatteryCurrent.grid(column=1, row=4)
  272. BatterySOC.grid(column=1, row=5)
  273. LastFault.grid(column=1, row=6)
  274.  
  275.  
  276. VehicleSpeedUnits.grid(column=2, row=0)
  277. MotorTempUnits.grid(column=2, row=1)
  278. MotorCurrentUnits.grid(column=2, row=2)
  279. BatteryVoltageUnits.grid(column=2, row=3)
  280. BatteryCurrentUnits.grid(column=2, row=4)
  281. BatterySOCUnits.grid(column=2, row=5)
  282.  
  283. # Speed gauge
  284.  
  285. # Fast loop refresher tasks
  286. def Fastloop():
  287.     RegsRead('Vehicle_Speed',8)
  288.     print(RegsDic)
  289.     VehicleSpeedVar.set(int(round(0.621371*RegsDic[260])))
  290.     MotorTempVar.set(RegsDic[261])
  291.     MotorCurrentVar.set(RegsDic[262])
  292.     BatteryVoltageVar.set(RegsDic[265])
  293.     BatteryCurrentVar.set(RegsDic[266])
  294.     BatterySOCVar.set(RegsDic[267])
  295.     #LastFaultVar.set(RegsDic[269]) #Read by itself, as this is Bit Vector register! Requires own dict {bit, string}
  296.     # root.after(1000, DisplayFrame.update())
  297.     DisplayFrame.update()
  298.  
  299. # Debug button
  300. Debutton = tk.Button(root, text='Debutton', font=FrameFont, command=Fastloop)
  301.  
  302. # Lay out
  303. ProfileFrame.grid(column=0, row=0)
  304. # ProfileFrame.place(relx=0.5, rely=0.5, anchor = tk.CENTER)
  305. AssistFrame.grid(column=1, row=0)
  306. DisplayFrame.grid(column=0, row=1)
  307. Debutton.grid(column=2, row=0)
  308.  
  309. # Fastloop()
  310. tk.mainloop()
  311.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement