#!/usr/bin/env python3 import sys, os, re import string import time import mod_globals import mod_elm import mod_utils try: import readline except: pass os.chdir (os.path.dirname (os.path.realpath (sys.argv[0]))) macro = {} var = {} cmd_delay = 0 stack = [] auto_macro = "" auto_dia = False debug_mode = False key_pressed = '' mod_globals.os = os.name if mod_globals.os == 'nt': import pip try: import serial except ImportError: pip.main(['install', 'pyserial']) try: import colorama except ImportError: pip.main(['install', 'colorama']) try: import colorama except ImportError: print("\n\n\n\t\t\tGive me access to the Internet for download modules\n\n\n") sys.exit() colorama.init() try: import serial from serial.tools import list_ports except ImportError: print("\n\n\n\tPleas install additional modules") print("\t\t>sudo easy_install pyserial") sys.exit() def init_macro(): global macro macro = {} def init_var(): global var var = {} #predefined variables var['$addr'] = '7A' var['$txa'] = '7E0' var['$rxa'] = '7E8' var['$prompt'] = 'ELM' def pars_macro( file ): global macro global var print('openning file:', file) f = open( file, 'rt' ) lines = f.readlines() f.close() macroname = '' macrostrings = [] line_num = 0 for l in lines: line_num += 1 l = l.split('#')[0] # remove comments l = l.strip() if l == '': continue if '{' in l: if macroname=='': literals = l.split('{') macroname = literals[0].strip() macroname = macroname.replace(' ', '_').replace('\t', '_') macrostrings = [] if len(literals)>1 and literals[1]!='' : macrostrings.append(literals[1]) continue else: print('Error: empty macro name in line:', line_num) macro = {} var = {} return if '}' in l: if macroname!='': literals = l.split('}') cmd = literals[0].strip() if cmd!='': macrostrings.append(cmd) macro[macroname] = macrostrings macroname = '' macrostrings = [] continue else: print('Error: unexpected end of macro in line:', line_num) macro = {} var = {} return m = re.search('\$\S+\s*=\s*\S+', l) if m and macroname=='': #variable definition r = m.group(0).replace(' ', '').replace('\t', '') rl = r.split('=') var[rl[0]]=rl[1] else: macrostrings.append(l) def load_macro( mf='' ): """ dynamically loaded macro should have .txt extension and placed in ./macro directory """ if mf=='' : for root, dirs, files in os.walk("./macro"): for mfile in files: if mfile.endswith('.txt'): full_path = os.path.join("./macro/", mfile) pars_macro(full_path) else: pars_macro(mf) def print_help(): """ [h]elp - this help [q]uit, [e]xit, end - exit from terminal wait|sleep x - wait x seconds """ global var global macro print(print_help.__doc__) print('Variables:') for v in list(var.keys()): print(' '+v+' = '+var[v]) print() print('Macros:') for m in list(macro.keys()): print(' '+m) print() def optParser(): '''Parsing of command line parameters. User should define at least com port name''' import argparse global auto_macro global auto_dia global debug_mode parser = argparse.ArgumentParser( description = "pyRen terminal" ) parser.add_argument('-p', help="ELM327 com port name", dest="port", default="") parser.add_argument("-r", help="com port rate during diagnostic session {38400[default],57600,115200,230400,500000}", dest="rate", default="38400",) parser.add_argument("-m", help="macro file name", dest="macro", default="",) parser.add_argument("--log", help="log file name", dest="logfile", default="") parser.add_argument("--demo", help="for debuging purpose. Work without car and ELM", dest="demo", default=False, action="store_true") parser.add_argument("--si", help="try SlowInit first", dest="si", default=False, action="store_true") parser.add_argument("--cfc", help="turn off automatic FC and do it by script", dest="cfc", default=False, action="store_true") parser.add_argument("--caf", help="turn on CAN Auto Formatting. Available only for OBDLink", dest="caf", default=True, action="store_true") parser.add_argument("--n1c", help="turn off L1 cache", dest="n1c", default=False, action="store_true") parser.add_argument("-vv", "--verbose", help="show verbose output (unused)", dest="verb", default=False, action="store_true") parser.add_argument("--dialog", help="show dialog for selecting macro", dest="dia", default=False, action="store_true") parser.add_argument("--debug", help="for debug purpose only", dest="dbg", default=False, action="store_true") parser.add_argument("--minordtc", help="use to show all DTCs without checking computation formula", dest="minordtc", default=False, action="store_true") options = parser.parse_args() if not options.port: parser.print_help() iterator = sorted(list(list_ports.comports())) print("") print("Available COM ports:") for port, desc, hwid in iterator: print("%-30s \n\tdesc: %s \n\thwid: %s" % (port,desc,hwid)) print("") exit(2) else: mod_globals.opt_port = options.port mod_globals.opt_rate = int(options.rate) mod_globals.opt_speed = int(options.rate) auto_macro = options.macro mod_globals.opt_log = options.logfile mod_globals.opt_demo = options.demo mod_globals.opt_si = options.si mod_globals.opt_cfc0 = options.cfc mod_globals.opt_caf = options.caf mod_globals.opt_n1c = options.n1c mod_globals.opt_minordtc = options.minordtc auto_dia = options.dia debug_mode = options.dbg class FileChooser(): droid = None folderList = [] macroList = [] def newFolderSelected(self): self.macroList = [] fo = self.folder self.macroList = [f for f in os.listdir(fo) if os.path.isfile(fo + f) and f.lower().endswith('.cmd')] self.droid.fullSetList("sp_macro", self.macroList) def eventloop(self): while True: sf = self.folderList[int(self.droid.fullQueryDetail("sp_folder").result['selectedItemPosition'])] if sf != self.folder: self.folder = sf self.newFolderSelected() event = self.droid.eventWait(50).result if event == None: continue if event["name"] == "click": id = event["data"]["id"] if id == "bt_start": ma = self.macroList[int(self.droid.fullQueryDetail("sp_macro").result['selectedItemPosition'])] return sf + ma if id == "bt_exit": sys.exit() def __init__(self): fo = './macro/' self.folderList = [fo + f + '/' for f in os.listdir(fo) if os.path.isdir(fo + f)] self.folder = fo def choose(self): try: # Python2 import tkinter as tk import tkinter.ttk import tkinter.filedialog as filedialog except ImportError: # Python3 import tkinter as tk import tkinter.ttk as ttk import tkinter.filedialog as filedialog root = tk.Tk() root.withdraw() my_filetypes = [('command files', '.cmd')] fname = filedialog.askopenfilename(parent=root, initialdir="./macro", title="Please select a file:", filetypes=my_filetypes) return fname def play_macro(mname, elm): global macro global var global stack if mname in stack: print('Error: recursion prohibited:', mname) return else: stack.append(mname) for l in macro[mname]: if l in list(macro.keys()): play_macro(l, elm) continue proc_line( l, elm ) stack.remove(mname) def run_init_function(mname, elm): global var if mname in ["init_can_250", "can250", "init_can_500", "can500"]: elm.init_can() if mname in ["init_can_250", "can250"]: elm.set_can_addr(var['$addr'], {'brp': '1'}) else: elm.set_can_addr(var['$addr'], {}) elif mname in ["init_iso_slow", "slow", "init_iso_fast", "fast"]: elm.init_iso() if mname in ["init_iso_slow", "slow"]: elm.set_iso_addr(var['$addr'], {'protocol': 'PRNA2000'}) else: elm.set_iso_addr(var['$addr'], {}) else: print(("Unrecognized init command: ", mname)) def term_cmd( c, elm ): global var rsp = elm.request(c, cache=False) #rsp = elm.cmd(rcmd) var['$lastResponse'] = rsp return rsp def bit_cmd( l, elm, fnc='set_bits' ): global var error_msg1 = '''ERROR: command should have 5 parameters: - ECUs local identifier. Length should be 2 simbols for KWP or 4 for CAN - lengt of command response including positive response bytes, equals MinBytes from ddt db - offeset in bytes to first changed byte (starts from 1 not 0) - bit mask for changed bits, 1 - changable, 0 - untachable - bit value and should have equal length ''' error_msg2 = '''ERROR: command should have 6 parameters: