pyren/pyren3/mod_utils.py

500 lines
12 KiB
Python
Executable File

#!/usr/bin/env python3
'''
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
'''
import sys
import os
import string
import signal
import atexit
import subprocess
import mod_globals
try:
import webbrowser
except:
pass
# Snippet from http://home.wlu.edu/~levys/software/kbhit.py
# Windows
if os.name == 'nt':
import msvcrt
# Posix (Linux, OS X)
else:
import termios
#import atexit
from select import select
#from decimal import *
class KBHit:
def __init__(self):
self.set_getch_term()
def set_getch_term(self):
'''Creates a KBHit object that you can call to do various keyboard things.
'''
if os.name == 'nt':
pass
else:
# Save the terminal settings
self.fd = sys.stdin.fileno()
self.new_term = termios.tcgetattr(self.fd)
self.old_term = termios.tcgetattr(self.fd)
# New terminal setting unbuffered
self.new_term[3] = (self.new_term[3] & ~termios.ICANON & ~termios.ECHO)
termios.tcsetattr(self.fd, termios.TCSANOW, self.new_term)
#termios.tcsetattr(self.fd, termios.TCSAFLUSH, self.new_term)
# Support normal-terminal reset at exit
atexit.register(self.set_normal_term)
def set_normal_term(self):
''' Resets to normal terminal. On Windows this is a no-op.
'''
if os.name == 'nt':
pass
else:
termios.tcsetattr(self.fd, termios.TCSANOW, self.old_term)
#termios.tcsetattr(self.fd, termios.TCSAFLUSH, self.old_term)
def getch(self):
''' Returns a keyboard character after kbhit() has been called.
Should not be called in the same program as getarrow().
'''
s = ''
if os.name == 'nt':
s = msvcrt.getch().decode('utf-8','ignore')
else:
s = sys.stdin.read(1)
if len(s)==0 or ord(s)==0 or ord(s)==0xe0:
if os.name == 'nt':
s = msvcrt.getch().decode('utf-8','ignore')
else:
s = sys.stdin.read(1)
return s
def getarrow(self):
''' Returns an arrow-key code after kbhit() has been called. Codes are
0 : up
1 : right
2 : down
3 : left
Should not be called in the same program as getch().
'''
if os.name == 'nt':
msvcrt.getch() # skip 0xE0
c = msvcrt.getch()
vals = [72, 77, 80, 75]
else:
c = sys.stdin.read(3)[2]
vals = [65, 67, 66, 68]
return vals.index(ord(c.decode('utf-8')))
def kbhit(self):
''' Returns True if keyboard character was hit, False otherwise.
'''
if os.name == 'nt':
return msvcrt.kbhit()
else:
try:
dr,dw,de = select([sys.stdin], [], [], 0)
except:
pass
return dr != []
def Choice(list, question ):
'''Util for make simple choice'''
d = {};
c = 1
exitNumber = 0
for s in list:
if s.lower()=='<up>' or s.lower()=='<exit>':
exitNumber = c
print("%-2s - %s" % ('Q', pyren_encode(s)))
d['Q']=s
else:
print("%-2s - %s" % (c, pyren_encode(s)))
d[str(c)]=s
c = c+1
while (True):
try:
ch = input(question)
except (KeyboardInterrupt, SystemExit):
print()
print()
sys.exit()
if ch=='q': ch = 'Q'
if ch=='cmd': mod_globals.opt_cmd = True
if ch in d.keys():
return [d[ch],ch]
def ChoiceLong(list, question, header = '' ):
'''Util for make choice from long list'''
d = {};
c = 1
exitNumber = 0
page = 0
page_size = 20
for s in list:
if s.lower()=='<up>' or s.lower()=='<exit>':
exitNumber = c
d['Q']=s
else:
d[str(c)]=s
c = c+1
while( 1 ):
clearScreen()
#os.system('cls' if os.name == 'nt' else 'clear') # clear screen
#print chr(27)+"[2J"+chr(27)+"[;H", # clear ANSI screen (thanks colorama for windows)
if len( header ): print(pyren_encode(header))
c = page*page_size
for s in list[page*page_size:(page+1)*page_size]:
c = c + 1
if s.lower()=='<up>' or s.lower()=='<exit>':
print("%-2s - %s" % ('Q', pyren_encode(s)))
else:
print("%-2s - %s" % (c, pyren_encode(s)))
if len(list)>page_size:
if page>0:
print("%-2s - %s" % ('P', '<prev page>'))
if (page+1)*page_size<len(list):
print("%-2s - %s" % ('N', '<next page>'))
while (True):
try:
ch = input(question)
except (KeyboardInterrupt, SystemExit):
print()
print()
sys.exit()
if ch=='q': ch = 'Q'
if ch=='p': ch = 'P'
if ch=='n': ch = 'N'
if ch=='N' and (page+1)*page_size<len(list):
page = page + 1
break
if ch=='P' and page>0:
page = page - 1
break
if ch=='cmd': mod_globals.opt_cmd = True
if ch in d.keys():
return [d[ch],ch]
def ChoiceFromDict(dict, question, showId = True ):
'''Util for make choice from dictionary'''
d = {};
c = 1
exitNumber = 0
for k in sorted(dict.keys()):
s = dict[k]
if k.lower()=='<up>' or k.lower()=='<exit>':
exitNumber = c
print("%s - %s" % ('Q',pyren_encode(s)))
d['Q']=k
else:
if showId:
print("%s - (%s) %s" % (c,pyren_encode(k),pyren_encode(s)))
else:
print("%s - %s" % (c,pyren_encode(s)))
d[str(c)]=k
c = c+1
while (True):
try:
ch = input(question)
except (KeyboardInterrupt, SystemExit):
print()
print()
sys.exit()
if ch=='q': ch = 'Q'
if ch in list(d.keys()):
return [d[ch],ch]
def pyren_encode( inp ):
return inp
#if mod_globals.os == 'android':
# return inp.encode('utf-8', errors='replace')
#else:
# return inp.encode(sys.stdout.encoding, errors='replace')
def pyren_decode( inp ):
return inp
#if mod_globals.os == 'android':
# return inp.decode('utf-8', errors='replace')
#else:
# return inp.decode(sys.stdout.encoding, errors='replace')
def pyren_decode_i( inp ):
if mod_globals.os == 'android':
return inp.decode('utf-8', errors='ignore')
else:
return inp.decode(sys.stdout.encoding, errors='ignore')
def clearScreen():
# https://docs.microsoft.com/en-us/windows/console/console-virtual-terminal-sequences
# [2J - clear entire screen
# [x;yH - move cursor to x:y
sys.stdout.write(chr(27)+"[2J"+chr(27)+"[;H")
def upScreen():
sys.stdout.write(chr(27)+"[;H")
def hex_VIN_plus_CRC( VIN, plusCRC=True):
'''The VIN must be composed of 17 alphanumeric characters apart from "I" and "O"'''
#VIN ='VF1LM1B0H11111111'
VIN = VIN.upper()
hexVIN = ''
CRC = 0xFFFF
for c in VIN: # for every byte in VIN
b = ord(c) # get ASCII
hexVIN = hexVIN + hex(b)[2:].upper()
for i in range( 8 ): # for every bit
if ((CRC ^ b) & 0x1):
CRC = CRC >> 1
CRC = CRC ^ 0x8408
b = b >> 1
else:
CRC = CRC >> 1
b = b >> 1
# invert
CRC = CRC ^ 0xFFFF
# swap bytes
b1 = (CRC >> 8) & 0xFF
b2 = CRC & 0xFF
CRC = ((b2 << 8) | b1) & 0xFFFF
sCRC = hex( CRC )[2:].upper()
sCRC = '0'*(4-len(sCRC))+sCRC
# result
if plusCRC:
return hexVIN+sCRC
else:
return hexVIN
# Test
if __name__ == "__main__":
kb = KBHit()
print('Hit any key, or ESC to exit')
while True:
if kb.kbhit():
c = kb.getch()
if ord(c) == 27: # ESC
break
print(c)
kb.set_normal_term()
# Convert ASCII to HEX
def ASCIITOHEX( ATH ):
ATH = ATH.upper()
hexATH = ''.join("{:02X}".format(ord(c)) for c in ATH)
#Result
return hexATH
# Convert ch str to int then to Hexadecimal digits
def StringToIntToHex(DEC):
DEC = int(DEC)
hDEC = hex(DEC)
#Result
return hDEC[2:].zfill(2).upper()
def loadDumpToELM( ecuname, elm ):
ecudump = {}
dumpname = ''
flist = []
for root, dirs, files in os.walk("./dumps"):
for f in files:
if (ecuname+'.txt') in f:
flist.append(f)
if len(flist)==0: return
flist.sort()
dumpname = os.path.join("./dumps/", flist[-1])
#debug
print("Loading:", dumpname)
df = open(dumpname,'rt')
lines = df.readlines()
df.close()
for l in lines:
l = l.strip().replace('\n','')
if ':' in l:
req,rsp = l.split(':')
ecudump[req] = rsp
elm.setDump( ecudump )
def chkDirTree():
'''Check direcories'''
if not os.path.exists('./cache'):
os.makedirs('./cache')
if not os.path.exists('./csv'):
os.makedirs('./csv')
if not os.path.exists('./logs'):
os.makedirs('./logs')
if not os.path.exists('./dumps'):
os.makedirs('./dumps')
if not os.path.exists('./macro'):
os.makedirs('./macro')
if not os.path.exists('./doc'):
os.makedirs('./doc')
if not os.path.exists('../MTCSAVE'):
os.makedirs('../MTCSAVE')
def getVIN( de, elm, getFirst = False ):
''' getting VINs from every ECU '''
''' de - list of detected ECUs '''
''' elm - reference to ELM class '''
m_vin = set([])
for e in de:
# init elm
if mod_globals.opt_demo: #try to load dump
loadDumpToELM( e['ecuname'], elm )
else:
if e['pin'].lower()=='can':
elm.init_can()
elm.set_can_addr( e['dst'], e )
else:
elm.init_iso()
elm.set_iso_addr( e['dst'], e )
elm.start_session( e['startDiagReq'] )
# read VIN
if e['stdType'].lower()=='uds':
rsp = elm.request( req = '22F190', positive = '62', cache = False )[9:59]
else:
rsp = elm.request( req = '2181', positive = '61', cache = False )[6:56]
try:
vin = bytes.fromhex( rsp.replace(' ','')).decode('utf-8')
except:
continue
#debug
#print e['dst'],' : ', vin
if len(vin)==17:
m_vin.add(vin)
if getFirst:
return vin
l_vin = m_vin
if os.path.exists('savedVIN.txt'):
with open('savedVIN.txt') as vinfile:
vinlines = vinfile.readlines()
for l in vinlines:
l = l.strip()
if '#' in l: continue
if len(l)==17:
l_vin.add(l.upper())
if len(l_vin)==0 and not getFirst:
print("ERROR!!! Can't find any VIN. Check connection")
exit()
if len(l_vin)<2:
try:
ret = next(iter(l_vin))
except:
ret = ''
return ret
print("\nFound ",len(l_vin), " VINs\n")
choice = Choice(l_vin, "Choose VIN : ")
return choice[0]
def DBG( tag, s ):
if mod_globals.opt_debug and mod_globals.debug_file!=None:
mod_globals.debug_file.write( '### ' + tag + '\n')
mod_globals.debug_file.write( '"' + s + '"\n')
def isHex(s):
return all(c in string.hexdigits for c in s)
def kill_server():
if mod_globals.doc_server_proc is None:
pass
else:
os.kill(mod_globals.doc_server_proc.pid, signal.SIGTERM)
def show_doc( addr, id ):
if mod_globals.vin == '':
return
if mod_globals.doc_server_proc == None:
mod_globals.doc_server_proc = subprocess.Popen(["python", "-m", "SimpleHTTPServer", "59152"])
atexit.register(kill_server)
if mod_globals.opt_sd:
url = 'http://localhost:59152/doc/' + id[1:] + '.htm'
else:
url = 'http://localhost:59152/doc/'+mod_globals.vin+'.htm'+id
webbrowser.open(url, new=0)