#!/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()
else:
# let's try android
try:
import androidhelper as android
mod_globals.os = 'android'
except:
try:
import android
mod_globals.os = 'android'
except:
pass
if mod_globals.os != 'android':
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 and mod_globals.os != 'android':
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():
lay = '''
'''
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:
import androidhelper as android
mod_globals.os = 'android'
except:
try:
import android
mod_globals.os = 'android'
except:
pass
if mod_globals.os != 'android':
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
else:
try:
self.droid = android.Android()
self.droid.fullShow(self.lay)
self.folderList.insert(0,'./macro/')
self.droid.fullSetList("sp_folder", self.folderList)
return self.eventloop()
finally:
self.droid.fullDismiss()
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' ):
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: