From 2d338130770818a8f9b4e5fdba2513efbc947b57 Mon Sep 17 00:00:00 2001 From: marios8543 Date: Tue, 7 May 2024 15:22:15 +0300 Subject: [PATCH] Remove scenarios and useless stuff --- pyren3/CHANGE_LOG.txt | 306 --------------- pyren3/cmdr_10742_rep.py | 65 --- pyren3/cmdr_chkelm.py | 187 --------- pyren3/cmdr_example.py | 127 ------ pyren3/cmdr_odometr.py | 198 ---------- pyren3/cmdr_simple.py | 44 --- pyren3/cmdr_simple_iso.py | 60 --- pyren3/data_logger.py | 85 ++++ pyren3/scen_auto_config.py | 94 ----- pyren3/scen_ecri_calinj1.py | 252 ------------ pyren3/scen_ecri_codevin.py | 142 ------- pyren3/scen_ecri_counter2.py | 153 -------- pyren3/scen_ecri_fap5.py | 271 ------------- pyren3/scen_ecri_generique2.py | 144 ------- pyren3/scen_ecri_initpente.py | 87 ---- pyren3/scen_ecri_paraminj1.py | 653 ------------------------------- pyren3/scen_ecri_paraminj2.py | 589 ---------------------------- pyren3/scen_ecri_paraminj4.py | 280 ------------- pyren3/scen_lect_sondeO21.py | 218 ----------- pyren3/scen_lect_ssppx91.py | 421 -------------------- tools_and_confs/i12comp.exe | Bin 135168 -> 0 bytes tools_and_confs/wininstaller.iss | 79 ---- 22 files changed, 85 insertions(+), 4370 deletions(-) delete mode 100755 pyren3/CHANGE_LOG.txt delete mode 100755 pyren3/cmdr_10742_rep.py delete mode 100755 pyren3/cmdr_chkelm.py delete mode 100755 pyren3/cmdr_example.py delete mode 100755 pyren3/cmdr_odometr.py delete mode 100755 pyren3/cmdr_simple.py delete mode 100755 pyren3/cmdr_simple_iso.py create mode 100644 pyren3/data_logger.py delete mode 100644 pyren3/scen_auto_config.py delete mode 100755 pyren3/scen_ecri_calinj1.py delete mode 100755 pyren3/scen_ecri_codevin.py delete mode 100755 pyren3/scen_ecri_counter2.py delete mode 100755 pyren3/scen_ecri_fap5.py delete mode 100755 pyren3/scen_ecri_generique2.py delete mode 100755 pyren3/scen_ecri_initpente.py delete mode 100755 pyren3/scen_ecri_paraminj1.py delete mode 100755 pyren3/scen_ecri_paraminj2.py delete mode 100755 pyren3/scen_ecri_paraminj4.py delete mode 100755 pyren3/scen_lect_sondeO21.py delete mode 100755 pyren3/scen_lect_ssppx91.py delete mode 100644 tools_and_confs/i12comp.exe delete mode 100644 tools_and_confs/wininstaller.iss diff --git a/pyren3/CHANGE_LOG.txt b/pyren3/CHANGE_LOG.txt deleted file mode 100755 index b0ff386..0000000 --- a/pyren3/CHANGE_LOG.txt +++ /dev/null @@ -1,306 +0,0 @@ -История версий - - -v 0.8 (beta) - -- Первая версия от PyRen. Основана на версии скрипта pyren light v 0.7 (alpha) от Shr-lnm “club-renault.ru” -- Реализована работа с базами клип - -v 0.9 (beta) - -Исправлено: -- По результатам тестирования на Koleos исправлена пара ошибок иногда проявляющихся в меню DE -Добавлено: -- Упрощена инсталляция под Windows. Теперь не нужно ставить дополнительные модули Python. Скрипт сам их поставит при первом запуске. (Компьютер должен быть в сети) -- Разблокированы простые команды не требующие параметров (используйте с осторожностью) -- После сканирования, меню выбора ЭБУ показывает примерное количество ошибок. (Примерное потому, что некоторые не существенные ошибки далее могут игнорироваться) - -v 0.9.1 (beta) - -Исправлено: -- на Windows машинах, в меню некоторых блоков, вываливался с ошибкой отсутствия символа в кодовой странице cp866 -- не правильно показывал состояния и параметры длина которых больше 1-го и меньше 8-ми бит (поля с длиной более байта но не кратно 8-ми бит (если такие есть) по прежнему обрабатываться правильно не будут) -- на некоторых блоках вываливался в меню ошибок -- показывал не все ошибки в блоках STD_B -- некорректно отображалось буквенное обозначение параметров и состояний -Добавлено: -- В меню выбора ЭБУ добавлен пункт повторного сканирования ошибок по всем обнаруженным блокам. - -v 0.9.2 (beta) - -Исправлено: -- на windows машинах вываливался при нажатии не буквенных клавиш. -- вываливался при загрузке блоков с некорректно описанными переводами в языковой базе. Теперь, в таких случаях, берется поле defaulttext. Можно самостоятельно исправлять поле defaulttext если сделать не валидным значение поля codetext. -- исправлена ошибка повторного сканирования -Добавлено: -- модуль mod_optfile.py можно запускать самостоятельно для исследования содержимого файлов bqm и SGxxxxxxx.XML - -v 0.9.3 (beta) - -Исправлено: -- исправления в алгоритме сканирования и работы с к-линией (исправление от Shr-lnm) -- выход из любого пункта меню осуществляется кнопкой Q -- если запустить скрипт с ключом -L и указать не существующий язык, например FR, то языковая база не загрузится и все сообщения будут на языке оригинала (максимально быстрая загрузка) -- несколько мелких багов приводивших к вываливанию скрипта -Добавлено: -- поддержка wifi адаптеров. вместо названия com порта "-p com1" нужно указать адрес и порт wifi адаптера, например "-p 192.168.0.10:35000". Некоторые модели wifi адаптеров работают очень медленно - см. соответствующие форумы по настройкам адаптеров. -- поддержка серии сервисов в одной команде (когда одна команда меню вызывает последовательность простык команд ЭБУ) - -v 0.9.4 (beta) - -Исправлено: -- алгоритм изменения интервалов между сериями команд в соответсвии с указаниями из БД -- загрузка некоторых блоков прерывалась на стадии Loading defaults -Добавлено: -- выполенение серии команд без парамаметров - -v 0.9.5 (beta) - -Исправлено: -- при работе по CAN на ELM отключается автоворматирование “AT CAF0” это позволяет подавать команды длинее 7 байт (исправление от Shr-lnm) -- отключен ограничитель частоты опроса ЭБУ (busLoad) (исправление от Shr-lnm) -- алгоритм работы команд требующих повторения. -Добавлено: -- опция -r позволяет изменить скорость работы COM порта во время работы скрипта. (добавил Shr-lnm) -- Основное меню ЭБУ показывает описание пункта дополнительно к двухбуквенному (предложил gruzdev_f) -- Работа с командами, параметры которых выбираются из списка. (Например: смена языка в панели приборов, активирование круиз контроля и пр.) -- Ключ --csv включает запись параметров и состояний в CSV файл для дальнейшего анализа, например в exel. -- mod_ecu.py стал запускаемым. С его помощью можно посмотреть полный список параметров и команд ЭБУ - -v 0.9.6 (beta) - -Исправлено: -- несколько косметических ошибок. -Добавлено: -- быстрая загрузка (со второго раза). -- отключен автоматический FlowControl “AT CFC0” (добавил Shr-lnm) - -v 0.9.7 (beta) - -Исправлено: -- последовательности инициализации ELM для CAN и K-линии -- по умолчанию автоматический FlowControl не отключается -- отображение хода выполнения команд состоящих из нескольких сервисов -- перед считыванием ошибок таймер увеличивается до 1 секунды -- если скорость порта меньше 50kbps то команда 1902хх заменяется на 1902AF -- keepalive включается на CAN и ISO -Добавлено: -- поддержка нескольких сервисов в одной мнемонике, данные берутся из последнего сервиса - -v 0.9.8 (beta) - -Исправлено: -- обработка параметров с обратным порядком байт -- последовательность инициализации k-line (Shr-lnm) -Добавлено: -- поддержка запуска под Android -- генерация CSV для torque (Shr-lnm) - -v 0.9.9 (beta) - -Исправлено: -- более корректно работает mod_optfile.py (спасибо Valentin8080) -- контроль длины вводимого параметра HEX -- изменена нумерация моделей автомобилей -- при выборе модели не показываются старые, неподдерживаемые модели. -- длинные меню адаптированы под маленький экран андроид -Добавлено: -- поддержка FAST/SLOW INIT на k-line (kline-mode2 от Shr-lnm) -- значительно улучшены возможности commander.py (см. пример кода) -- commander.py переименован в cmdr_example.py и изменен launcher -- в меню просмотра ошибок блоков STD_B выдаются дополнительные параметры (спасибо Ivaness) -- добавлен скрипт cmdr_odometr.py для проверки пробега в разных блоках - -v 0.9.9(3) (beta) - -Исправлено: -- алгоритм чтения ответов ELM (mod_elm_odometr_v2 от Shr-lnm) -- время отклика в логах -- ошибка логирования под Windows (спасибо Vitna) -- ошибка чтения некоторых параметров под Android (спасибо Ivaness) -Добавлено: -- принудительное включение SlowInit. Oпция --si -- отключение автоматического FlowControl выполняемого ELM. С опцией --cfc скрипт сам будет делать FC -- отключение ускорения работы по CAN шине за счет указания количества фреймов в ответе. Опция --n1c отключает это ускорение -- учет параметра brp (автоматический выбор CAN 250k/500k) - -v 0.9.9(4) (beta) - -Исправлено: -- работа опции --cfc - -v 0.9.9(5) (beta) - -Исправлено: -- выполнение некоторых команд. -- ошибка отображения при считывании идентификаций в hex -Добавлено: -- опция --csv_only отключает вывод данных на экран для ускорения записи в csv (добавил Slava.Vrn) -- опция --csv_human включает текстовое описание колонок и состояний в csv (добавил Slava.Vrn) -- интерфейс запуска сценариев -- реализован сценарий scen_ecri_fap5 - принудительная регенерация сажевого фильтра -- реализован сценарий scen_ecri_codevin - изменение VIN -- генерация csv и профилей для torque из под Android - -v 0.9.9(6) (beta) - -Исправлено: -- обработка некоторых команд с обратным порядком байт -- вываливался при использовании опции --csv_only (спасибо Slava.Vrn) -Добавлено: -- опция --usr_key позволяет добавить пользовательские события в csv файл (добавил Slava.Vrn) -- опция --dev (использовать с особой осторожностью) временно включает Development Session при выполнении некоторых конфигурационных соманд, таких как 3B81 (добавил Shr-lnm) -- опция --exp (использовать только в крайних случаях) включает кнопки в DDT -- поддержка базы данных DDT2000. (На android не работает и не планируется) (ИСПОЛЬЗУЙТЕ С ОСОБОЙ ОСТОРОЖНОСТЬЮ ПРЕДВАРИТЕЛЬНО ПРОВЕРИВ ВСЕ В DEMO) - скопируйте директорию ecus из DDT2000data туда, где лежат директории EcuRenault, Location и Vehicles -- mod_ddt может запускаться отдельно, для работы с машинами не поддерживаемыми в CLIP - -v 0.9.9(7) (beta) - -Исправлено: -- сканирование CAN на разных скоростях (250 и 500) (спасибо Shr-lnm) -- битовые операции в DDT -Добавлено: -- опция --dump. Сохраняет ответы на все команды 17*, 19*, 21*, 22* для использования в demo режиме и "на всякий случай" -- перевод ddt. (Сбылась мечта идиота :) - себя имею в виду) - -v 0.9.9(8) (beta) - -Исправлено: -- ошибка в расчете некоторых параметров -- много исправлений в mod_ddt -Добавлено: -- опция --can2. Работа с блоками на второй CAN шине. pin 12 и 13. Будет создан savedEcus2.p -- опция --dev теперь с параметром. Нужно указать команду открытия development сессии (например, --dev1086) -- опция -e для demo. Можно указать список блоков для которых запустится demo (например, -e 11476,11588,11570,11593 откроет блоки KOLEOS II). savedEcus.p не меняется. - -v 0.9.9(9) (beta) - -Исправлено: -- ошибка в командах 2E с битовыми полями -- ошибка генератора формул torque в параметрах со сдвигом -- много исправлений в mod_ddt - -v 0.9.a (beta) - -Исправлено: -- алгоритм поиска подходящего xml в базе ddt -- ошибка сохранения/загрузки dump -- ввод параметров в mod_ddt во время автообновления -- ошибка генерации формул для Torque -Добавлено: -- команды DEC и ASCII в режиме выполнения команд (добавил DarkCraz) -- добавлен сценарий scen_ecri_calinj1 (добавил DarkCraz) -- добавлена фильтрация фреймов в режиме монитора (добавил Shr-lnm) -- возможность указать xml при запуске mod_ddt -- mod_elm делает повторный запрос если получил NR (7F ** 78) или подобные -- сброс параметров адаптивного тайминга ELM при смене адреса на CAN - -v 0.9.b (beta) - -Исправлено: -- ошибки при запуске mod_ddt с ключом --xml -- масштаб отображения некоторых экранов в mod_ddt -- режим автообновления полей ввода в mod_ddt (спасибо Shr-lnm) -- задержка перед повторным выполнением команд после некоторых NR, изменена с 50 до 500 мс (спасибо Shr-lnm) -- ошибка сканирования блоков UDS (спасибо Shr-lnm) -Добавлено: -- автоматическая проверка адаптера во время работы -- автоматическое переключение адаптера в режим --cfc -- чтение адреса из xml в mod_ddt (Влияет на запуск не через pyren) -- возможность выбора источника начальных значений полей ввода. (Settings->Prefer Inputs from ECU) Если отключить, то значения будут браться из XML. Будьте внимательны. Значения в XML могут не соответствовать вашей комплектации!!!! - -v 0.9.c (beta) - -Исправлено: -- отображение некоторых экранов в ddt -- отрицательные значения в полях ввода -- автоматическое переключение адаптера в режим --cfc -- повторный запрос при получении NR (7F ** 78) - -v 0.9.e (beta) - -Исправлено: -- отображение некоторых экранов в mod_ddt -- динамическое изменение таймаута ELM при выполнении некоторых запросов (спасибо Shr-lnm) -- уменьшен эффект мерцания при отображении данных под Windows (спасибо Shr-lnm) -Добавлено: -- универсальный лаунчер _pyren_launcher.py (спасибо Shr-lnm) -- новый распаковщик базы extract.py. Дополнительно извлекает базу VIN и MTC (BVMEXTRACTION) -- отображение картинок и графических кнопок в mod_ddt. Необходимо положить папку graphics из базы ddt2000 рядом с папкой ecus -- утилита doc_maker.py. Извлекает из базы DocDb и формирует html-файл с диагностической документацией для заданного VIN - -v 0.9.f (beta) -...Поддержку android передаю в добрые руки... - -Исправлено: -- ошибки при генерации документации doc_maker -- ошибка кодировки в mod_utils - -v 0.9.h (beta) - -Исправлено: -- поддержка блоков UDS в mod_ddt - -v 0.9.i (beta) - -Исправлено: -- отображение некоторых ошибок в модулях failflag -- работа mod_ddt с медленными блоками -Добавлено: -- ЭКСПЕРИМЕНТАЛЬНАЯ функция отката для дампов в mod_ddt. Используйте с осторожностью, особенно для блоков std_a - -v 0.9.j (beta) - -Исправлено: -- улучшена работа функции отката. Увеличено количетво поддерживаемых блоков. - -v 0.9.k (beta) - -Исправлено: -- изменен алгоритм подбора xml в mod_ddt (требуется очистка cache и ввод типа автомобиля (или повторное сканирование блоков)) -- ошибка в работе софтверного FlowControl --cfc (спасибо Shr-lnm) -Добавлено: -- mod_term.py Терминал для работы с ELM и простейших макросов для ЭБУ. Свои макросы с именем *.txt нужно складывать в директорию macro. Пример использования mocro и переменных в macro/init.txt - -v 0.9.l (beta) - -Исправлено: -- cmdr_chkelm.py показывал OK вместо TIMEOUT -- убрана 200мс задержка между отображениями экранов в pyren -- ошибка отправки длинных команд в режиме --cfc -Добавлено: -- функция "Dumps/Show Diff" показывающая какие параметры различаются в дампах -- doc_maker.py выводит дату производства автомобиля на титульном листе -- исключение команды с откликом NR:12 из дальнейших опросов ECU -- автоматический переход в режим --cfc после "BUFFER FULL" -- подсказки [HEX, DEC, ASCII, VIN] при запуске команд с параметрами, [SHOW] при просмотре scm-сценариев - -v 0.9.m (beta) - -Исправлено: -- bus_monitor.py вместо unknown показывает hex содержимое фреймов -- mod_ddt заменяет непрочтенные значения на "none" вместо "0" для избежания возможных ошибок -- снято ограничение на ввод данных DEC и ASCII в режиме выполнения команд -- исправлен алгоритм обработки NR 78 -- исправлена ошибка termios -Добавлено: -- в mod_ddt меню Tools/Make torque PIDs -- mod_ddt автоматически генерирует экраны для всех команд ddt_all_commands (использовать для записи с особой осторожностью !!!!) - -v 0.9.n (beta) - -Исправлено: -- логирование elm_ для k-line -- отображение DTC для блоков STD-B -Добавлено: -- подсчет изменений во фреймах в bus_monitor -- библиотека pyserial включена в дистрибутив - -v 0.9.p (beta) - -Исправлено: -- ошибки логирования в mod_elm -- расчет CRC для VIN -Добавлено: -- новый лаунчер mod_ddt.py diff --git a/pyren3/cmdr_10742_rep.py b/pyren3/cmdr_10742_rep.py deleted file mode 100755 index a28f733..0000000 --- a/pyren3/cmdr_10742_rep.py +++ /dev/null @@ -1,65 +0,0 @@ -#!/usr/bin/env python3 - -import sys, os -import mod_globals - -os.chdir(os.path.dirname(os.path.realpath(sys.argv[0]))) - -import mod_elm - -############## change me ################ - -ecu_functional_address = "7A" -mod_globals.os = 'android' -mod_globals.opt_port = 'bt' # 'COM4' - -######################################### - -#mod_globals.opt_demo = True -mod_globals.opt_cfc0 = True -mod_globals.opt_speed = 38400 -mod_globals.opt_log = '10742-rep.txt' - -print('Opening ELM') -elm = mod_elm.ELM( mod_globals.opt_port, mod_globals.opt_speed, True ) - -print('Init ELM') -elm.init_can() - -TXa = mod_elm.dnat[ecu_functional_address] -RXa = mod_elm.snat[ecu_functional_address] - -print(elm.cmd("at sh "+TXa)) -print(elm.cmd("at cra "+RXa)) -print(elm.cmd("at fc sh "+TXa)) -print(elm.cmd("at fc sd 30 00 00")) -print(elm.cmd("at fc sm 1")) -print(elm.cmd("at st ff")) -print(elm.cmd("at at 0")) -print(elm.cmd("at sp 6")) -print(elm.cmd("at at 1")) -print(elm.cmd("10C0")) - -# check ECU -r = elm.cmd("2180") -#debug -#r = '''61 80 34 36 33 32 52 45 34 42 45 30 30 33 37 52 00 83 9D 00 1A 90 01 01 00 88 AA''' -if len(r)<53 or r[7*3:7*3+2]!='45' or r[17*3:17*3+2]!='83': - print("\n\nNot compatible ECU\n\n") - exit() - -print(elm.cmd("2EFD5000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000")) -print(elm.cmd("2EFD510000000100000001000000010000000100000001000000010000000100000001")) -print(elm.cmd("2EFD527FFA7FFA7FFA7FFA7FFA7FFA7FFA7FFA7FFA7FFA7FFA7FFA7FFA7FFA7FFA7FFA7FFA7FFA7FFA7FFA7FFA7FFA7FFA7FFA7FFA7FFA7FFA7FFA7FFA7FFA7FFA7FFA")) -print(elm.cmd("2EFD5300")) -print(elm.cmd("2EFD5400000000000000000000000000000000")) -print(elm.cmd("2EFD550000000000000000000000000000000000000000000000000000000000000000")) -print(elm.cmd("2EFD5680008000800080008000800080008000")) -print(elm.cmd("2EFD5700")) -print(elm.cmd("2E216401")) - -print("Done") - - - - diff --git a/pyren3/cmdr_chkelm.py b/pyren3/cmdr_chkelm.py deleted file mode 100755 index 5b1ff6e..0000000 --- a/pyren3/cmdr_chkelm.py +++ /dev/null @@ -1,187 +0,0 @@ -#!/usr/bin/env python3 - -import sys, os -import time - -import mod_globals -import pyren3 - -cmdb = ''' -#v1.0 ;AC P; ATZ ; Z ; reset all -#v1.0 ;AC P; ATE1 ; E0, E1 ; Echo off, or on* -#v1.0 ;AC P; ATL0 ; L0, L1 ; Linefeeds off, or on -#v1.0 ;AC ; ATI ; I ; print the version ID -#v1.0 ;AC ; AT@1 ; @1 ; display the device description -#v1.0 ;AC P; ATAL ; AL ; Allow Long (>7 byte) messages -#v1.0 ;AC ; ATBD ; BD ; perform a Buffer Dump -#V1.0 ;ACH ; ATSP4 ; SP h ; Set Protocol to h and save it -#v1.0 ;AC ; ATBI ; BI ; Bypass the Initialization sequence -#v1.0 ;AC P; ATCAF0 ; CAF0, CAF1 ; Automatic Formatting off, or on* -#v1.0 ;AC ; ATCFC1 ; CFC0, CFC1 ; Flow Controls off, or on* -#v1.0 ;AC ; ATCP 01 ; CP hh ; set CAN Priority to hh (29 bit) -#v1.0 ;AC ; ATCS ; CS ; show the CAN Status counts -#v1.0 ;AC ; ATCV 1250 ; CV dddd ; Calibrate the Voltage to dd.dd volts -#v1.0 ;AC ; ATD ; D ; set all to Defaults -#v1.0 ;AC ; ATDP ; DP ; Describe the current Protocol -#v1.0 ;AC ; ATDPN ; DPN ; Describe the Protocol by Number -#v1.0 ;AC P; ATH0 ; H0, H1 ; Headers off*, or on -#v1.0 ;AC ; ATI ; I ; print the version ID -#v1.0 ;AC P; ATIB 10 ; IB 10 ; set the ISO Baud rate to 10400* -#v1.0 ;AC ; ATIB 96 ; IB 96 ; set the ISO Baud rate to 9600 -#v1.0 ;AC ; ATL1 ; L0, L1 ; Linefeeds off, or on -#v1.0 ;AC ; ATM0 ; M0, M1 ; Memory off, or on -#v1.0 ;AC ; ATCM 00000000 ; CM hhhhhhhh ; set the ID Mask to hhhhhhhh -#v1.0 ;AC ; ATCF 00000000 ; CF hhhhhhhh ; set the ID Filter to hhhhhhhh -#v1.0 ;AC ; ATCM 000 ; CM hhh ; set the ID Mask to hhh -#v1.0 ;AC ; ATCF 000 ; CF hhh ; set the ID Filter to hhh -#v1.0 ; C ; ATMA ; MA ; Monitor All -#v1.0 ; C ; ATMR 01 ; MR hh ; Monitor for Receiver = hh -#v1.0 ; C ; ATMT 01 ; MT hh ; Monitor for Transmitter = hh -#v1.0 ;AC ; ATNL ; NL ; Normal Length messages* -#v1.0 ;AC ; ATPC ; PC ; Protocol Close -#v1.0 ;AC ; ATR1 ; R0, R1 ; Responses off, or on* -#v1.0 ;AC ; ATRV ; RV ; Read the input Voltage -#v1.0 ;ACH ; ATSP7 ; SP h ; Set Protocol to h and save it -#v1.0 ;ACH ; ATSH 00000000 ; SH wwxxyyzz ; Set Header to wwxxyyzz -#v1.0 ;AC ; ATSH 001122 ; SH xxyyzz ; Set Header to xxyyzz -#v1.0 ;AC P; ATSH 012 ; SH xyz ; Set Header to xyz -#v1.0 ;AC ; ATSP A6 ; SP Ah ; Set Protocol to Auto, h and save it -#v1.0 ;AC ; ATSP 6 ; SP h ; Set Protocol to h and save it -#v1.0 ;AC P; ATST FF ; ST hh ; Set Timeout to hh x 4 msec -#v1.0 ;AC P; ATSW 96 ; SW 00 ; Stop sending Wakeup messages -#v1.0 ;AC P; ATSW 34 ; SW hh ; Set Wakeup interval to hh x 20 msec -#v1.0 ;AC ; ATTP A6 ; TP Ah ; Try Protocol h with Auto search -#v1.0 ;AC ; ATTP 6 ; TP h ; Try Protocol h -#v1.0 ;AC P; ATWM 817AF13E ; WM [1 - 6 bytes] ; set the Wakeup Message -#v1.0 ;AC P; ATWS ; WS ; Warm Start (quick software reset) -#v1.1 ;AC P; ATFC SD 300000 ; FC SD [1 - 5 bytes]; FC, Set Data to [...] -#v1.1 ;AC P; ATFC SH 012 ; FC SH hhh ; FC, Set the Header to hhh -#v1.1 ;AC P; ATFC SH 00112233 ; FC SH hhhhhhhh ; Set the Header to hhhhhhhh -#v1.1 ;AC P; ATFC SM 1 ; FC SM h ; Flow Control, Set the Mode to h -#v1.1 ;AC ; ATPP FF OFF ; PP FF OFF ; all Prog Parameters disabled -#v1.1 ;AC ; ATPP FF ON ; PP FF ON ; all Prog Parameters enabled -#v1.1 ; ; ; PP xx OFF ; disable Prog Parameter xx -#v1.1 ; ; ; PP xx ON ; enable Prog Parameter xx -#v1.1 ; ; ; PP xx SV yy ; for PP xx, Set the Value to yy -#v1.1 ;AC ; ATPPS ; PPS ; print a PP Summary -#v1.2 ;AC ; ATAR ; AR ; Automatically Receive -#v1.2 ;AC 0; ATAT1 ; AT0, 1, 2 ; Adaptive Timing off, auto1*, auto2 -#v1.2 ; ; ; BRD hh ; try Baud Rate Divisor hh -#v1.2 ; ; ; BRT hh ; set Baud Rate Timeout -#v1.2 ;ACH ; ATSPA ; SP h ; Set Protocol to h and save it -#v1.2 ; C ; ATDM1 ; DM1 ; monitor for DM1 messages -#v1.2 ; C ; ATIFR H ; IFR H, S ; IFR value from Header* or Source -#v1.2 ; C ; ATIFR0 ; IFR0, 1, 2 ; IFRs off, auto*, or on -#v1.2 ;AC ; ATIIA 01 ; IIA hh ; set ISO (slow) Init Address to hh -#v1.2 ;AC ; ATKW0 ; KW0, KW1 ; Key Word checking off, or on* -#v1.2 ; C ; ATMP 0123 ; MP hhhh ; Monitor for PGN 0hhhh -#v1.2 ; C ; ATMP 0123 4 ; MP hhhh n ; and get n messages -#v1.2 ; C ; ATMP 012345 ; MP hhhhhh ; Monitor for PGN hhhhhh -#v1.2 ; C ; ATMP 012345 6 ; MP hhhhhh n ; and get n messages -#v1.2 ;AC ; ATSR 01 ; SR hh ; Set the Receive address to hh -#v1.3 ; ; AT@2 ; @2 ; display the device identifier -#v1.3 ;AC P; ATCRA 012 ; CRA hhh ; set CAN Receive Address to hhh -#v1.3 ;AC ; ATCRA 01234567 ; CRA hhhhhhhh ; set the Rx Address to hhhhhhhh -#v1.3 ;AC ; ATD0 ; D0, D1 ; display of the DLC off*, or on -#v1.3 ;AC ; ATFE ; FE ; Forget Events -#v1.3 ;AC ; ATJE ; JE ; use J1939 Elm data format* -#v1.3 ;AC ; ATJS ; JS ; use J1939 SAE data format -#v1.3 ;AC ; ATKW ; KW ; display the Key Words -#v1.3 ;AC ; ATRA 01 ; RA hh ; set the Receive Address to hh -#v1.3 ;ACH ; ATSP6 ; SP h ; Set Protocol to h and save it -#v1.3 ;ACH ; ATRTR ; RTR ; send an RTR message -#v1.3 ;AC ; ATS1 ; S0, S1 ; printing of aces off, or on* -#v1.3 ;AC ; ATSP 00 ; SP 00 ; Erase stored protocol -#v1.3 ;AC ; ATV0 ; V0, V1 ; use of Variable DLC off*, or on -#v1.4 ;AC ; ATCEA ; CEA ; turn off CAN Extended Addressing -#v1.4 ;AC ; ATCEA 01 ; CEA hh ; use CAN Extended Address hh -#v1.4 ;AC ; ATCV 0000 ; CV 0000 ; restore CV value to factory setting -#v1.4 ;AC ; ATIB 48 ; IB 48 ; set the ISO Baud rate to 4800 -#v1.4 ;AC ; ATIGN ; IGN ; read the IgnMon input level -#v1.4 ; ; ; LP ; go to Low Power mode -#v1.4 ;AC ; ATPB 01 23 ; PB xx yy ; Protocol B options and baud rate -#v1.4 ;AC ; ATRD ; RD ; Read the stored Data -#v1.4 ;AC ; ATSD 01 ; SD hh ; Save Data byte hh -#v1.4 ;ACH ; ATSP4 ; SP h ; Set Protocol to h and save it -#v1.4 ;AC P; ATSI ; SI ; perform a Slow (5 baud) Initiation -#v1.4 ;ACH ; ATZ ; Z ; reset all -#v1.4 ;ACH ; ATSP5 ; SP h ; Set Protocol to h and save it -#v1.4 ;AC P; ATFI ; FI ; perform a Fast Initiation -#v1.4 ;ACH ; ATZ ; Z ; reset all -#v1.4 ;AC ; ATSS ; SS ; use Standard Search order (J1978) -#v1.4 ;AC ; ATTA 12 ; TA hh ; set Tester Address to hh -#v1.4 ;ACH ; ATSPA ; SP h ; Set Protocol to h and save it -#v1.4 ;AC ; ATCSM1 ; CSM0, CSM1 ; Silent Monitoring off, or on* -#v1.4 ;AC ; ATJHF1 ; JHF0, JHF1 ; Header Formatting off, or on* -#v1.4 ;AC ; ATJTM1 ; JTM1 ; set Timer Multiplier to 1* -#v1.4 ;AC ; ATJTM5 ; JTM5 ; set Timer Multiplier to 5 -#v1.4b;AC ; ATCRA ; CRA ; reset the Receive Address filters -#v2.0 ;AC ; ATAMC ; AMC ; display Activity Monitor Count -#v2.0 ;AC ; ATAMT 20 ; AMT hh ; set the Activity Mon Timeout to hh -#v2.1 ;AC ; ATCTM1 ; CTM1 ; set Timer Multiplier to 1* -#v2.1 ;AC ; ATCTM5 ; CTM5 ; set Timer Multiplier to 5 -#v2.1 ;ACH ; ATZ ; Z ; reset all -''' -os.chdir(os.path.dirname(os.path.realpath(sys.argv[0]))) - -try: - import serial - from serial.tools import list_ports -except ImportError: - sys.exit() - -from mod_elm import ELM - -def main(): - - pyren3.optParser() - - good = 0 - total= 0 - pycom= 0 - vers = '' - res = '' - - print('Opening ELM') - elm = ELM( mod_globals.opt_port, mod_globals.opt_speed, mod_globals.opt_log ) - elm.portTimeout = 5 - - for st in cmdb.split('#'): - cm = st.split(';') - - if len(cm)>1: - if mod_globals.os == 'android' and 'A' not in cm[1].upper(): continue - if mod_globals.os != 'android' and 'C' not in cm[1].upper(): continue - - if len(cm[2].strip()): - - res = elm.send_raw(cm[2]) - - #print res - - if 'H' in cm[1].upper(): continue - total += 1 - - if '?' in res: - chre = '[FAIL]' - if 'P' in cm[1].upper(): pycom += 1 - elif 'TIME' in res: - chre = '[TIMEOUT]' - else: - chre = '[OK]' - good += 1 - vers = cm[0] - - print("%5s %10s %6s"%(cm[0], cm[2], chre)) - sys.stdout.flush - - if pycom>0: - res = '\n\n\nUncompatible adapter on ARM core \n pyren would not work with it \n\n\n' - res = res + '\n\n\nResult: '+str(good)+' from '+str(total)+'\n Max version:'+vers+'\n\n\n\n\n\n\n' - - elm.lastMessage = res - -if __name__ == '__main__': - main() - - diff --git a/pyren3/cmdr_example.py b/pyren3/cmdr_example.py deleted file mode 100755 index 6955063..0000000 --- a/pyren3/cmdr_example.py +++ /dev/null @@ -1,127 +0,0 @@ -#!/usr/bin/env python3 - -# ______ ___ ___ ___ ___ ___ ______ __ _______ -# | ___| \ \ / / / \ | \/ | | _ \ | | | ____| -# | |__ \ V / / ^ \ | \ / | | |_) | | | | |__ -# | __| > < / /_\ \ | |\/| | | ___/ | | | __| -# | |___ / . \ / _____ \ | | | | | | | `----.| |____ -# |______| /__/ \__\ /__/ \__\ |__| |__| | _| |_______||_______| -# - -import sys, os -import time - -import mod_globals -import mod_ecu -import pyren3 - -os.chdir(os.path.dirname(os.path.realpath(sys.argv[0]))) - -try: - import serial - from serial.tools import list_ports -except ImportError: - sys.exit() - -from mod_elm import ELM -from mod_scan_ecus import ScanEcus -from mod_ecu import ECU -from mod_optfile import * -from mod_utils import * - -def prepareECU(): - '''This function loads data for ECU''' - - global elm - global ecu - - pyren.optParser() - - if len(mod_globals.opt_log)==0: - mod_globals.opt_log = 'commander_log.txt' - - print('Opening ELM') - elm = ELM( mod_globals.opt_port, mod_globals.opt_speed, mod_globals.opt_log ) - - print('Loading ECUs list') - se = ScanEcus(elm) #Prepare list of all ecus - - if not os.path.isfile("savedEcus.p") or mod_globals.opt_scan: - # choosing model - se.chooseModel( mod_globals.opt_car ) #choose model of car for doing full scan - - # Do this check every time - se.scanAllEcus() #First scan of all ecus - - print("Loading language ") - sys.stdout.flush() - #loading language data - lang = optfile("Location/DiagOnCAN_"+mod_globals.opt_lang+".bqm",True) - mod_globals.language_dict = lang.dict - print("Done") - - #clearScreen() - - choosen_ecu = se.chooseECU( mod_globals.opt_ecuid ) #choose ECU among detected - if choosen_ecu==-1: - print("#\n"*3,"# Unknown ECU defined!!!\n","#\n"*3) - exit(1) - - ecucashfile = "./cache/"+choosen_ecu['ModelId']+'_'+mod_globals.opt_lang+".p" - - if os.path.isfile(ecucashfile): #if cache exists - ecu = pickle.load( open( ecucashfile, "rb" ) ) #load it - else: #else - ecu = ECU(choosen_ecu, lang.dict ) #load original data for chosen ECU - pickle.dump( ecu, open( ecucashfile, "wb" ) ) #and save data to cache for next time - - ecu.initELM( elm ) #init ELM for chosen ECU - - #ecu.show_screens() # show ECU screens - -def main(): - - prepareECU() - - ##### - ##### Example start - ##### - - # Example of using raw commands - - print(elm.request( req = '2180', positive = '61', cache = False )) - print(elm.request( req = '2181', positive = '61', cache = False )) - - #print elm.request( req = '3B815646314C4D314230483131313131313131518C', positive = '7B', cache = False ) - - # Example of using states, parameters and ids - - for i in range( 1, 10 ): - value1, datastr1 = ecu.get_st('E019') #when you know that it is state name (internal or codeMR) - value2, datastr2 = ecu.get_pr('PR141') #when you know that it is parameter name (internal or codeMR) - value3, datastr3 = ecu.get_val('PR091') #when you do't know what it is state, param or id - value4, datastr4 = ecu.get_id('ID008') #when you know that it is identification name (internal or codeMR) - - # get all values before showing them for avoid screen flickering - - clearScreen() - print() - print("E019 ", value1) - print("RP141", value2) - print("PR091", value3) - print("ID008", value4) - time.sleep( 0.3 ) # 300 milliseconds - - #ecu.run_cmd('CF125', 'B0') - ecu.run_cmd('RZ001') - - ##### - ##### Example end - ##### - - - -if __name__ == '__main__': - main() - - diff --git a/pyren3/cmdr_odometr.py b/pyren3/cmdr_odometr.py deleted file mode 100755 index 2551099..0000000 --- a/pyren3/cmdr_odometr.py +++ /dev/null @@ -1,198 +0,0 @@ -#!/usr/bin/env python3 - -import sys, os -import time -import mod_utils -import mod_db_manager - -import mod_globals -import mod_ecu -import pyren3 - -os.chdir(os.path.dirname(os.path.realpath(sys.argv[0]))) - -try: - import serial - from serial.tools import list_ports -except ImportError: - sys.exit() - -from mod_elm import ELM -from mod_scan_ecus import ScanEcus -from mod_ecu import ECU -from mod_optfile import * -from mod_utils import * - -def prepareECUs(): - '''This function loads data for ECUs''' - - global elm - global ecu - global se - global lang - - pyren.optParser() - - mod_utils.chkDirTree() - mod_db_manager.find_DBs() - - if len(mod_globals.opt_log)==0: - mod_globals.opt_log = 'commander_log.txt' - - print('Opening ELM') - elm = ELM( mod_globals.opt_port, mod_globals.opt_speed, mod_globals.opt_log ) - - print('Loading ECUs list') - se = ScanEcus(elm) #Prepare list of all ecus - - if not os.path.isfile("savedEcus.p") or mod_globals.opt_scan: - # choosing model - se.chooseModel( mod_globals.opt_car ) #choose model of car for doing full scan - - # Do this check every time - se.scanAllEcus() #First scan of all ecus - - print("Loading language ") - sys.stdout.flush() - #loading language data - lang = optfile("Location/DiagOnCAN_"+mod_globals.opt_lang+".bqm",True) - mod_globals.language_dict = lang.dict - print("Done") - - return se.detectedEcus - -def chooseEcu( ecu_number ): - - global elm - global ecu - global se - global lang - - choosen_ecu = se.chooseECU( ecu_number ) - if choosen_ecu==-1: - print("#\n"*3,"# Unknown ECU defined!!!\n","#\n"*3) - exit(1) - - ecucashfile = "./cache/"+choosen_ecu['ModelId']+'_'+mod_globals.opt_lang+".p" - - if os.path.isfile(ecucashfile): #if cache exists - ecu = pickle.load( open( ecucashfile, "rb" ) ) #load it - else: #else - ecu = ECU(choosen_ecu, lang.dict ) #load original data for chosen ECU - pickle.dump( ecu, open( ecucashfile, "wb" ) ) #and save data to cache for next time - - ecu.initELM( elm ) #init ELM for chosen ECU - - #ecu.show_screens() # show ECU screens - -def main(): - list = prepareECUs() - - tot = '' - - for l in list: - if l['idf']=='1': #family 01 - print("### Connecting to Engine ###") - chooseEcu(l['ecuname']) - tot += "%-15s : " % "Engine PR025" - num, string = ecu.get_pr('PR025') - print(pyren_encode(string)) - tot += str(num); tot += '\n' - tot += "%-15s : " % "Engine PR992" - num, string = ecu.get_pr('PR992') - print(pyren_encode(string)) - tot += str(num); tot += '\n' - num, string = ecu.get_pr('PR391') - print(pyren_encode(string)) - num, string = ecu.get_pr('PR412') - print(pyren_encode(string)) - #num, string = ecu.get_pr('PR804') - #print pyren_encode(string) - #num, string = ecu.get_pr('PR869') - #print pyren_encode(string) - #num, string = ecu.get_pr('PR870') - #print pyren_encode(string) - print() - if l['idf']=='2': #family 02 - print("### Connecting to ABS ###") - chooseEcu(l['ecuname']) - tot += "%-15s : " % "ABS PR121" - num, string = ecu.get_pr('PR121') - print(pyren_encode(string)) - tot += str(num); tot += '\n' - print() - if l['idf']=='3': #family 03 - print("### Connecting to TDB ###") - chooseEcu(l['ecuname']) - tot += "%-15s : " % "TDB PR009" - num, string = ecu.get_pr('PR009') - print(pyren_encode(string)) - tot += str(num); tot += '\n' - tot += "%-15s : " % "TDB (km) PR025" - num, string = ecu.get_pr('PR025') - print(pyren_encode(string)) - tot += str(num); tot += '\n' - tot += "%-15s : " % "TDB (mil) PR026" - num, string = ecu.get_pr('PR026') - print(pyren_encode(string)) - tot += str(num); tot += '\n' - print() - - print(pyren_encode('Listening to CAN. Please wait a bit...')) - elm.cmd('at z') - elm.cmd("at e1") - elm.cmd("at l1") - elm.cmd("at h1") - elm.cmd("at d1") - elm.cmd("at caf0") - elm.cmd("at sp 6") - elm.cmd("at al") - elm.portTimeout = 1 - - elm.cmd("at cf 5C5") - elm.cmd("at cm 7FF") - elm.cmd("at cra 5C5") - resp = elm.cmd("atma") - elm.cmd("at") - for l in resp.split('\n'): - if l.upper().startswith('5C5'): - kmt = l[9:18].replace(' ','') - tot += "%-10s : " % "Frame 5C5" - tot = tot + str(int(kmt,16)); tot += '\n' - break - - elm.cmd("at cf 715") - elm.cmd("at cm 7FF") - elm.cmd("at cra 715") - elm.portTimeout = 5 - resp = elm.cmd("atma") - elm.portTimeout = 1 - elm.cmd("at") - for l in resp.split('\n'): - if l.upper().startswith('715'): - kmt = l[6:15].replace(' ','') - tot += "%-10s : " % "Frame 715" - tot = tot + str(int(kmt,16)); tot += '\n' - break - - elm.cmd("at cf 5FD") - elm.cmd("at cm 7FF") - elm.cmd("at cra 5FD") - elm.portTimeout = 5 - resp = elm.cmd("atma") - elm.portTimeout = 1 - elm.cmd("at") - for l in resp.split('\n'): - if l.upper().startswith('5FD'): - kmt = l[6:15].replace(' ','') - tot += "%-10s : " % "Frame 5FD" - tot = tot + str(int(kmt,16)); tot += '\n' - break - - #print tot - elm.lastMessage = tot - -if __name__ == '__main__': - main() - - diff --git a/pyren3/cmdr_simple.py b/pyren3/cmdr_simple.py deleted file mode 100755 index 9fc9bab..0000000 --- a/pyren3/cmdr_simple.py +++ /dev/null @@ -1,44 +0,0 @@ -#!/usr/bin/env python3 - -import sys, os -#import serial - -import mod_globals - -os.chdir(os.path.dirname(os.path.realpath(sys.argv[0]))) - -import mod_elm - - -############## change me ################ - -ecu_functional_address = "26" -mod_globals.opt_port = 'bt' - -######################################### - - -#mod_globals.opt_demo = True -mod_globals.opt_speed = 38400 -mod_globals.opt_log = 'simpl.txt' - - -print('Opening ELM') -elm = mod_elm.ELM( mod_globals.opt_port, mod_globals.opt_speed, True ) - -print('Init ELM') -elm.init_can() - -TXa = mod_elm.dnat[ecu_functional_address] -RXa = mod_elm.snat[ecu_functional_address] -elm.currentaddress = TXa - -print(elm.cmd("at sh "+TXa)) -print(elm.cmd("at cra "+RXa)) -print(elm.cmd("at fc sh "+TXa)) -print(elm.cmd("at fc sd 30 00 00")) # status BS STmin -print(elm.cmd("at fc sm 1")) -print(elm.cmd("at sp 6")) -print(elm.cmd("10C0")) -#print elm.cmd("3BA00A00") - diff --git a/pyren3/cmdr_simple_iso.py b/pyren3/cmdr_simple_iso.py deleted file mode 100755 index 0afcbbc..0000000 --- a/pyren3/cmdr_simple_iso.py +++ /dev/null @@ -1,60 +0,0 @@ -#!/usr/bin/env python3 - -import sys, os -import serial - -import mod_globals - -os.chdir(os.path.dirname(os.path.realpath(sys.argv[0]))) - -import mod_elm - - -############## change me ################ - -ecu_functional_address = "7a" -#mod_globals.opt_port = 'com4' -mod_globals.opt_port = '/dev/cu.usbserial-AH01J4BS' - -######################################### - - -#mod_globals.opt_demo = True -mod_globals.opt_speed = 38400 -mod_globals.opt_log = 'simpl.txt' - - -print('Opening ELM') -elm = mod_elm.ELM( mod_globals.opt_port, mod_globals.opt_speed, True ) - -print('Init ELM') -print(elm.cmd("at z")) -elm.init_iso() - -#print elm.cmd("at fi") -print(elm.cmd("at sh 80 "+ecu_functional_address+" f1")) -print(elm.cmd("at sw 96")) -print(elm.cmd("at wm 81 "+ecu_functional_address+" f1 3E")) -print(elm.cmd("at ib10")) -print(elm.cmd("at st ff")) -print(elm.cmd("at at 0")) - -#print elm.cmd("at sp 4") -#print elm.cmd("at si") -print(elm.cmd("at sp 5")) -print(elm.cmd("at fi")) - -print(elm.cmd("at at 1")) -print(elm.cmd("at al")) -print(elm.cmd("at h1")) - - -print(elm.cmd("10C0")) -print(elm.cmd("2180")) -print(elm.cmd("2181")) -print(elm.cmd("17FF00")) -#print elm.cmd("14FF") -#print elm.cmd("14FF00") -print(elm.cmd("01 02 03 04 05 06 07 08 09")) - - diff --git a/pyren3/data_logger.py b/pyren3/data_logger.py new file mode 100644 index 0000000..6a1471b --- /dev/null +++ b/pyren3/data_logger.py @@ -0,0 +1,85 @@ +from mod_utils import chkDirTree +from mod_db_manager import find_DBs +from mod_elm import ELM +from mod_scan_ecus import ScanEcus +from mod_optfile import optfile +from mod_ecu import ECU +from mod_ply import Calc + +from pickle import dump, load +from time import sleep + +from flask import Flask, make_response, request + +from typing import List + +class RenDash: + def __init__(self, + elm_port, + elm_speed=38400, + model_number=58, + rescan=False) -> None: + chkDirTree() + find_DBs() + + self.elm = ELM(elm_port, elm_speed, "") + self.lang = optfile("Location/DiagOnCAN_GB.bqm", False) + self.ecus: List[ECU] = [] + self.current_ecu = None + + self.web = Flask(__name__) + + if rescan is False: + try: + self.ecus = load(open("frozen_ecus.p", "rb")) + except Exception as e: + pass + if not self.ecus: + ecu_scanner = ScanEcus(self.elm) + ecu_scanner.chooseModel(model_number) + ecu_scanner.scanAllEcus() + for ecu in ecu_scanner.detectedEcus: + self.ecus.append(ECU(ecu, self.lang.dict)) + dump(self.ecus, open("frozen_ecus.p", "wb+")) + + for ecu in self.ecus: + setattr(ecu, "calc", Calc()) + + def get_ecu_names(self): + return [ecu.ecudata["doc"] for ecu in self.ecus] + + def get_ecu_by_doc(self, doc): + for ecu in self.ecus: + if ecu.ecudata["doc"] == doc: + if self.current_ecu is not ecu: + ecu.initELM(self.elm) + self.current_ecu = ecu + return ecu + + def get_ecu_states(self, doc): + ecu = self.get_ecu_by_doc(doc) + return ecu.Parameters + + def get_ecu_state(self, doc, state): + ecu = self.get_ecu_by_doc(doc) + datastr = ecu.get_st(state) + return datastr + + def get_ecu_param(self, doc, param): + ecu = self.get_ecu_by_doc(doc) + datastr = ecu.get_pr(param) + return datastr + +if __name__ == "__main__": + rd = RenDash("/dev/tty0") + while True: + clearScreen() + for ecu_doc, values in REQUIRED_ECU_STATES.items(): + for val in values: + if val.startswith("E"): + print(rd.get_ecu_state(ecu_doc, val)) + elif val.startswith("P"): + print(rd.get_ecu_param(ecu_doc, val)) + sleep(0.1) + + \ No newline at end of file diff --git a/pyren3/scen_auto_config.py b/pyren3/scen_auto_config.py deleted file mode 100644 index a399b75..0000000 --- a/pyren3/scen_auto_config.py +++ /dev/null @@ -1,94 +0,0 @@ -#!/usr/bin/env python3 -''' -Scenarium usage example - -Name of this script should be exactly the same as in scenaruim URL but with '.py' extension - -URL - scm:scen_ecri_codevin#scen_ecri_codevin_xxxxx.xml - -'run' procedure will be executed by pyren script - -''' - -import os -import sys -import re -import time - -import mod_globals -import mod_utils -import mod_ecu -import mod_db_manager -from mod_utils import pyren_encode -from mod_utils import clearScreen -from mod_utils import hex_VIN_plus_CRC - -import xml.dom.minidom - -def run( elm, ecu, command, data ): - ''' - MAIN function of scenarium - - Parameters: - elm - refernce to adapter class - ecu - reference to ecu class - command - refernce to the command this scenarium belongs to - data - name of xml file with parameters from scenarium URL - ''' - - clearScreen() - header = '['+command.codeMR+'] '+command.label - - ScmSet = {} - ScmParam = {} - - def get_message( msg ): - if msg in list(ScmParam.keys()): - value = ScmParam[msg] - else: - value = msg - if value.isdigit() and value in list(mod_globals.language_dict.keys()): - value = pyren_encode( mod_globals.language_dict[value] ) - return value - - def get_message_by_id( id ): - if id.isdigit() and id in list(mod_globals.language_dict.keys()): - value = pyren_encode( mod_globals.language_dict[id] ) - return value - - - # - # Data file parsing - # - DOMTree = xml.dom.minidom.parse(mod_db_manager.get_file_from_clip(data)) - ScmRoom = DOMTree.documentElement - - ScmParams = ScmRoom.getElementsByTagName("ScmParam") - - for Param in ScmParams: - name = pyren_encode( Param.getAttribute("name") ) - value = pyren_encode( Param.getAttribute("value") ) - ScmParam[name] = value - - # - # Get IDs - # - value1, datastr1 = ecu.get_id(ScmParam['ref_C_2180']) - value2, datastr2 = ecu.get_id(ScmParam['ref_C_21FE']) - value3, datastr3 = ecu.get_id(ScmParam['ref_C_22F187']) - value4, datastr4 = ecu.get_id(ScmParam['ref_C_22F18E']) - - res = ecu.ecudata['idf'] + ':' - if len(value1)==10: res += ( value1 + ',' ) - if len(value2)==10: res += ( value2 + ',' ) - if len(value3)==10: res += ( value3 + ',' ) - if len(value4)==10: res += ( value4 + ',' ) - if res.endswith(','): res = res[:-1] - - print('## This info intended to be used as a value of --ref parameter of acf.py ##') - print() - print( res ) - print() - - ch = input('Press ENTER to continue') - diff --git a/pyren3/scen_ecri_calinj1.py b/pyren3/scen_ecri_calinj1.py deleted file mode 100755 index 2f584b9..0000000 --- a/pyren3/scen_ecri_calinj1.py +++ /dev/null @@ -1,252 +0,0 @@ -#!/usr/bin/env python3 -''' -Scenarium usage example - -Name of this script should be exactly the same as in scenaruim URL but with '.py' extension - -URL - scm:scen_ecri_calinj1#scen_ecri_calinj1_xxxxx.xml - -'run' procedure will be executed by pyren script - -''' - -import os -import sys -import re -import time -import string -import mod_globals -import mod_utils -import mod_ecu -import mod_db_manager -from mod_utils import pyren_encode -from mod_utils import clearScreen -from mod_utils import ASCIITOHEX -from mod_utils import StringToIntToHex -import xml.dom.minidom - -def run( elm, ecu, command, data ): - ''' - MAIN function of scenarium - - Parameters: - elm - refernce to adapter class - ecu - reference to ecu class - command - refernce to the command this scenarium belongs to - data - name of xml file with parameters from scenarium URL - ''' - - clearScreen() - header = '['+command.codeMR+'] '+command.label - - ScmSet = {} - ScmParam = {} - - def get_message( msg ): - if msg in list(ScmParam.keys()): - value = ScmParam[msg] - else: - value = msg - if value.isdigit() and value in list(mod_globals.language_dict.keys()): - value = pyren_encode( mod_globals.language_dict[value] ) - return value - - def get_message_by_id( id ): - if id.isdigit() and id in list(mod_globals.language_dict.keys()): - value = pyren_encode( mod_globals.language_dict[id] ) - return value - - - # - # Data file parsing - # - DOMTree = xml.dom.minidom.parse(mod_db_manager.get_file_from_clip(data)) - ScmRoom = DOMTree.documentElement - - ScmParams = ScmRoom.getElementsByTagName("ScmParam") - - for Param in ScmParams: - name = pyren_encode( Param.getAttribute("name") ) - value = pyren_encode( Param.getAttribute("value") ) - - ScmParam[name] = value - - ScmSets = ScmRoom.getElementsByTagName("ScmSet") - - for Set in ScmSets: - setname = pyren_encode(mod_globals.language_dict[Set.getAttribute("name")]) - ScmParams = Set.getElementsByTagName("ScmParam") - - for Param in ScmParams: - name = pyren_encode( Param.getAttribute("name") ) - value = pyren_encode( Param.getAttribute("value") ) - - ScmSet[setname]= value - ScmParam[name] = value - - # - # Important information - # - clearScreen() - value1, datastr1 = ecu.get_id(ScmParam['Injecteur1']) - value2, datastr2 = ecu.get_id(ScmParam['Injecteur2']) - value3, datastr3 = ecu.get_id(ScmParam['Injecteur3']) - value4, datastr4 = ecu.get_id(ScmParam['Injecteur4']) - print(pyren_encode(header)) - print(get_message('TexteTitre')) - print('*'*80) - print(pyren_encode(datastr1)) - print(pyren_encode(datastr2)) - print(pyren_encode(datastr3)) - print(pyren_encode(datastr4)) - print('*'*80) - - ch = input('Are you ready to change the Injector Codes? :') - while (ch.lower() !='y') and (ch.lower() !='n'): - ch = input('Are you ready to change the Injector Codes? :') - if ch.lower()!='y': return - - # - # INFO - # - - clearScreen() - value1, datastr1 = ecu.get_id(ScmParam['Injecteur1']) - value2, datastr2 = ecu.get_id(ScmParam['Injecteur2']) - value3, datastr3 = ecu.get_id(ScmParam['Injecteur3']) - value4, datastr4 = ecu.get_id(ScmParam['Injecteur4']) - print(pyren_encode(header)) - print('*'*80) - print(pyren_encode(datastr1)) - print(pyren_encode(datastr2)) - print(pyren_encode(datastr3)) - print(pyren_encode(datastr4)) - print('*'*80) - print(pyren_encode('Permitted Characters'),get_message('PermittedCharacters')) - print('*'*80) - - # - # Receive data length and format from scenario - # - - nbCC = ScmParam['nbCaractereCode'] - nbCC = int(nbCC) - if nbCC !=6 and nbCC !=7 and nbCC !=16: - ch = input('Error nbCaractereCode in scenario xml') - return - isHEX = ScmParam['FormatHexadecimal'] - isHEX = int(isHEX) - if isHEX != 0 and isHEX != 1: - ch = input('Error FormatHexadecimal in scenario xml') - return - prmCHAR = ScmParam['PermittedCharacters'] - if len(prmCHAR) << 16 and len(prmCHAR) >> 33: - ch = input('Error PermittedCharacters in scenario xml') - return - - # - # Get IMA from input - # - - - ch1 = input(get_message('dat_Cylindre1')+': ').upper() - while not (all (c in prmCHAR for c in ch1.upper()) and (len(ch1)==nbCC)): - ch1 = input(get_message('dat_Cylindre1')+': ').upper() - ch2 = input(get_message('dat_Cylindre2')+': ').upper() - while not (all (c in prmCHAR for c in ch2.upper()) and (len(ch2)==nbCC)): - ch2 = input(get_message('dat_Cylindre2')+': ').upper() - ch3 = input(get_message('dat_Cylindre3')+': ').upper() - while not (all (c in prmCHAR for c in ch3.upper()) and (len(ch3)==nbCC)): - ch3 = input(get_message('dat_Cylindre3')+': ').upper() - ch4 = input(get_message('dat_Cylindre4')+': ').upper() - while not (all (c in prmCHAR for c in ch4.upper()) and (len(ch4)==nbCC)): - ch4 = input(get_message('dat_Cylindre4')+': ').upper() - - # - # Check all data format of input - # - - chk = ( ch1 + ch2 + ch3 + ch4 ) - - if isHEX == 1 and not (all (c in prmCHAR for c in chk.upper()) and (len(chk) == nbCC * 4)): - print('*'*80) - ch = input('Hexdata check failed. Press ENTER to exit') - return - elif isHEX == 0 and not (all (c in prmCHAR for c in chk.upper()) and (len(chk) == nbCC * 4)) : - print('*'*80) - ch = input('ASCII check failed. Press ENTER to exit') - return - else: - print('*'*80) - ch = input('All checks passed successfull. Press ENTER to continue') - - - # - # If all checks are successful script prepares the data according to their type - # - - if isHEX == 1: - inj_code = ( ch1 + ch2 + ch3 + ch4 ).upper() - elif isHEX == 0: - inj_code = ASCIITOHEX ( ch1 + ch2 + ch3 + ch4 ).upper() - else: - print('*'*80) - ch = input('!!!!!!!!There is a bug somwhere in the scenario, operation aborted!!!!!!!!!') - return - - # - # print old and new data - # - - clearScreen() - print('*'*80) - print(pyren_encode('Old injector codes')) - print(pyren_encode(datastr1)) - print(pyren_encode(datastr2)) - print(pyren_encode(datastr3)) - print(pyren_encode(datastr4)) - print('*'*80) - print(pyren_encode('New injector codes')) - print(get_message('dat_Cylindre1'),pyren_encode(':'),pyren_encode(ch1)) - print(get_message('dat_Cylindre2'),pyren_encode(':'),pyren_encode(ch2)) - print(get_message('dat_Cylindre3'),pyren_encode(':'),pyren_encode(ch3)) - print(get_message('dat_Cylindre4'),pyren_encode(':'),pyren_encode(ch4)) - print('*'*80) - print(pyren_encode('Permitted Characters'),get_message('PermittedCharacters')) - print('*'*80) - - - ch = input('Start injectors writing? YES/QUIT>') - while (ch.upper()!='YES') and (ch.upper()!='QUIT'): - ch = input('Start injectors codes writing? YES/QUIT>') - if ch.upper()!='YES': - return - - # - # Write Injector Codes - # - - clearScreen() - cmd = ecu.get_ref_cmd(get_message('EcritureCodeInjecteur')) - print('*'*80) - responce = ecu.run_cmd(ScmParam['EcritureCodeInjecteur'],inj_code) - value5, datastr5 = ecu.get_id(ScmParam['Injecteur1']) - value6, datastr6 = ecu.get_id(ScmParam['Injecteur2']) - value7, datastr7 = ecu.get_id(ScmParam['Injecteur3']) - value8, datastr8 = ecu.get_id(ScmParam['Injecteur4']) - print('*'*80) - print(pyren_encode('Old injector codes')) - print(pyren_encode(datastr1)) - print(pyren_encode(datastr2)) - print(pyren_encode(datastr3)) - print(pyren_encode(datastr4)) - print('*'*80) - print(pyren_encode('New injector codes')) - print(pyren_encode(datastr5)) - print(pyren_encode(datastr6)) - print(pyren_encode(datastr7)) - print(pyren_encode(datastr8)) - print('*'*80) - - ch = input('Press ENTER to exit') - return diff --git a/pyren3/scen_ecri_codevin.py b/pyren3/scen_ecri_codevin.py deleted file mode 100755 index 6fdce3f..0000000 --- a/pyren3/scen_ecri_codevin.py +++ /dev/null @@ -1,142 +0,0 @@ -#!/usr/bin/env python3 -''' -Scenarium usage example - -Name of this script should be exactly the same as in scenaruim URL but with '.py' extension - -URL - scm:scen_ecri_codevin#scen_ecri_codevin_xxxxx.xml - -'run' procedure will be executed by pyren script - -''' - -import os -import sys -import re -import time - -import mod_globals -import mod_utils -import mod_ecu -import mod_db_manager -from mod_utils import pyren_encode -from mod_utils import clearScreen -from mod_utils import hex_VIN_plus_CRC - -import xml.dom.minidom - -def run( elm, ecu, command, data ): - ''' - MAIN function of scenarium - - Parameters: - elm - refernce to adapter class - ecu - reference to ecu class - command - refernce to the command this scenarium belongs to - data - name of xml file with parameters from scenarium URL - ''' - - clearScreen() - header = '['+command.codeMR+'] '+command.label - - ScmSet = {} - ScmParam = {} - - def get_message( msg ): - if msg in list(ScmParam.keys()): - value = ScmParam[msg] - else: - value = msg - if value.isdigit() and value in list(mod_globals.language_dict.keys()): - value = pyren_encode( mod_globals.language_dict[value] ) - return value - - def get_message_by_id( id ): - if id.isdigit() and id in list(mod_globals.language_dict.keys()): - value = pyren_encode( mod_globals.language_dict[id] ) - return value - - - # - # Data file parsing - # - DOMTree = xml.dom.minidom.parse(mod_db_manager.get_file_from_clip(data)) - ScmRoom = DOMTree.documentElement - - ScmParams = ScmRoom.getElementsByTagName("ScmParam") - - for Param in ScmParams: - name = pyren_encode( Param.getAttribute("name") ) - value = pyren_encode( Param.getAttribute("value") ) - - ScmParam[name] = value - - ScmSets = ScmRoom.getElementsByTagName("ScmSet") - - for Set in ScmSets: - setname = pyren_encode(mod_globals.language_dict[Set.getAttribute("name")]) - ScmParams = Set.getElementsByTagName("ScmParam") - - for Param in ScmParams: - name = pyren_encode( Param.getAttribute("name") ) - value = pyren_encode( Param.getAttribute("value") ) - - ScmSet[setname]= value - ScmParam[name] = value - - # - # Important information - # - clearScreen() - value1, datastr1 = ecu.get_id(ScmParam['identVIN']) - print(pyren_encode(header)) - print() - print(get_message('TextTitre')) - print() - print(get_message('MessageBox3')) - print() - print('*'*80) - print() - print(pyren_encode(datastr1)) - print() - print('*'*80) - ch = input('Are you ready to change the VIN? :') - if ch.lower()!='yes': return - - # - # Enter new VIN - # - clearScreen() - print(pyren_encode(header)) - print() - print(get_message('TextTitre')) - print() - print('*'*80) - print() - ch = input(get_message('STextTitre1')+': ').upper() - - while not (len(ch)==17 and ('I' not in ch) and ('O' not in ch)): - ch = input(get_message('STextTitre2')+': ').upper() - - cmd = ecu.get_ref_cmd(get_message('ConfigurationName')) - - vin_crc = hex_VIN_plus_CRC( ch ) - - print() - ch = input('Are you ready to change the VIN? :') - if ch.lower()!='yes': return - - # - # Change VIN - # - responce = ecu.run_cmd(ScmParam['ConfigurationName'],vin_crc) - value1, datastr1 = ecu.get_id(ScmParam['identVIN']) - print() - print('*'*80) - print() - print(pyren_encode(datastr1)) - print() - print('*'*80) - - ch = input('Press ENTER to continue') - diff --git a/pyren3/scen_ecri_counter2.py b/pyren3/scen_ecri_counter2.py deleted file mode 100755 index 7d62f7f..0000000 --- a/pyren3/scen_ecri_counter2.py +++ /dev/null @@ -1,153 +0,0 @@ -#!/usr/bin/env python3 -''' -Scenarium usage example - -Name of this script should be exactly the same as in scenaruim URL but with '.py' extension - -URL - scm:scen_ecri_calinj1#scen_ecri_calinj1_xxxxx.xml - -'run' procedure will be executed by pyren script - -''' - -import os -import sys -import re -import time -import string -import mod_globals -import mod_utils -import mod_ecu -import mod_db_manager -import mod_ecu_mnemonic -from mod_utils import pyren_encode -from mod_utils import clearScreen -import xml.dom.minidom - -def run( elm, ecu, command, data ): - ''' - MAIN function of scenarium - - Parameters: - elm - refernce to adapter class - ecu - reference to ecu class - command - refernce to the command this scenarium belongs to - data - name of xml file with parameters from scenarium URL - ''' - - clearScreen() - header = '['+command.codeMR+'] '+command.label - - ScmSet = {} - ScmParam = {} - - def get_message( msg, encode = 1 ): - if msg in list(ScmParam.keys()): - value = ScmParam[msg] - else: - value = msg - if value.isdigit() and value in list(mod_globals.language_dict.keys()): - if encode: - value = pyren_encode(mod_globals.language_dict[value]) - else: - value = mod_globals.language_dict[value] - return value - - def get_message_by_id( id, encode = 1 ): - if id.isdigit() and id in list(mod_globals.language_dict.keys()): - if encode: - value = pyren_encode(mod_globals.language_dict[id]) - else: - value = mod_globals.language_dict[id] - return value - - # - # Data file parsing - # - DOMTree = xml.dom.minidom.parse(mod_db_manager.get_file_from_clip(data)) - ScmRoom = DOMTree.documentElement - - ScmParams = ScmRoom.getElementsByTagName("ScmParam") - - for Param in ScmParams: - name = pyren_encode( Param.getAttribute("name") ) - value = pyren_encode( Param.getAttribute("value") ) - - ScmParam[name] = value - - ScmSets = ScmRoom.getElementsByTagName("ScmSet") - - for Set in ScmSets: - if len(Set.attributes) != 1: - setname = pyren_encode(mod_globals.language_dict[Set.getAttribute("name")]) - ScmParams = Set.getElementsByTagName("ScmParam") - - for Param in ScmParams: - name = pyren_encode( Param.getAttribute("name") ) - value = pyren_encode( Param.getAttribute("value") ) - - ScmSet[setname]= value - ScmParam[name] = value - - confirm = get_message_by_id('19800') - missing_data_message = get_message_by_id('882') - title = get_message('Title') - messageInfo = get_message('Message1') - succesMessage = get_message('CommandFinished') - failMessage = get_message('CommandImpossible') - - mnemonics = ecu.get_ref_id(ScmParam['default']).mnemolist - - if mnemonics[0][-2:] > mnemonics[1][-2:]: - mnemo1 = mnemonics[1] - mnemo2 = mnemonics[0] - else: - mnemo1 = mnemonics[0] - mnemo2 = mnemonics[1] - - byteFrom = int(mnemo1[-2:]) - byteTo = int(re.findall('\d+',mnemo2)[1]) - byteCount = byteTo - byteFrom - 1 - resetBytes = byteCount * '00' - params_to_send_length = int(mnemo2[-2:]) - - mnemo1Data = mod_ecu_mnemonic.get_mnemonic(ecu.Mnemonics[mnemo1], ecu.Services, elm, 1) - mnemo2Data = mod_ecu_mnemonic.get_mnemonic(ecu.Mnemonics[mnemo2], ecu.Services, elm, 1) - - paramsToSend = mnemo1Data + resetBytes + mnemo2Data - - fap_command_sids = ecu.get_ref_cmd(ScmParam['Cmde1']).serviceID - if len(fap_command_sids) and not mod_globals.opt_demo: - for sid in fap_command_sids: - if len(ecu.Services[sid].params): - if (len(ecu.Services[sid].startReq + paramsToSend)//2 != params_to_send_length): - input(missing_data_message + "\n\nPress ENTER to exit") - return - - clearScreen() - - print(title) - print('*'*80) - print(messageInfo) - print('*'*80) - print() - ch = input(confirm + ' : ') - while (ch.upper()!='YES') and (ch.upper()!='NO'): - ch = input(confirm + ' : ') - if ch.upper()!='YES': - return - - clearScreen() - - print() - response = ecu.run_cmd(ScmParam['Cmde1'], paramsToSend) - print() - - if 'NR' in response: - print(failMessage) - else: - print(succesMessage) - - print() - ch = input("Press ENTER to exit") - return \ No newline at end of file diff --git a/pyren3/scen_ecri_fap5.py b/pyren3/scen_ecri_fap5.py deleted file mode 100755 index f8a0052..0000000 --- a/pyren3/scen_ecri_fap5.py +++ /dev/null @@ -1,271 +0,0 @@ -#!/usr/bin/env python3 -''' -Scenarium usage example - -Name of this script should be exactly the same as in scenaruim URL but with '.py' extension - -URL - scm:scen_ecri_fap5#scen_ecri_fap5_xxxxx.xml - -'run' procedure will be executed by pyren script - -''' - -import os -import sys -import re -import time - -import mod_globals -import mod_utils -import mod_ecu -import mod_db_manager -from mod_utils import pyren_encode -from mod_utils import clearScreen -from mod_utils import KBHit - -import xml.dom.minidom - -#def get_message( value ): -# if value.isdigit() and value in mod_globals.language_dict.keys(): -# value = pyren_encode( mod_globals.language_dict[value] ) -# print value - -def run( elm, ecu, command, data ): - ''' - MAIN function of scenarium - - Parameters: - elm - refernce to adapter class - ecu - reference to ecu class - command - refernce to the command this scenarium belongs to - data - name of xml file with parameters from scenarium URL - ''' - - clearScreen() - header = '['+command.codeMR+'] '+command.label - - ScmSet = {} - ScmParam = {} - - def get_message( msg ): - if msg in list(ScmParam.keys()): - value = ScmParam[msg] - else: - value = msg - if value.isdigit() and value in list(mod_globals.language_dict.keys()): - value = pyren_encode( mod_globals.language_dict[value] ) - return value - - def get_message_by_id( id ): - if id.isdigit() and id in list(mod_globals.language_dict.keys()): - value = pyren_encode( mod_globals.language_dict[id] ) - return value - - - # - # Data file parsing - # - - DOMTree = xml.dom.minidom.parse(mod_db_manager.get_file_from_clip(data)) - ScmRoom = DOMTree.documentElement - - ScmParams = ScmRoom.getElementsByTagName("ScmParam") - - for Param in ScmParams: - name = pyren_encode( Param.getAttribute("name") ) - value = pyren_encode( Param.getAttribute("value") ) - - ScmParam[name] = value - - ScmSets = ScmRoom.getElementsByTagName("ScmSet") - - for Set in ScmSets: - setname = pyren_encode(mod_globals.language_dict[Set.getAttribute("name")]) - ScmParams = Set.getElementsByTagName("ScmParam") - - for Param in ScmParams: - name = pyren_encode( Param.getAttribute("name") ) - value = pyren_encode( Param.getAttribute("value") ) - - ScmSet[setname]= value - ScmParam[name] = value - - # - # Renew info about min/max values - # - GenPow = ecu.get_ref_pr(ScmParam['Param7']) #Generator power - GenPow.min = ScmParam['AltValueMin'] - GenPow.max = ScmParam['AltValueMax'] - - PartMass = ecu.get_ref_pr(ScmParam['Param6']) #Particle mass - PartMass.min = ScmParam['s_masse_suie_actif'] - PartMass.max = ScmParam['s_masse_suie_max'] - - # - # Important information - # - clearScreen() - print(pyren_encode(header)) - print() - print(get_message('SCMTitle')) - print() - print(get_message('Informations')) - print() - print('*'*80) - print() - print(get_message_by_id('1144')) - print() - print(get_message_by_id('1145')) - print() - print(get_message_by_id('1146')) - print() - ch = input('Press ENTER to continue') - clearScreen() - print(pyren_encode(header)) - print() - print(get_message('SCMTitle')) - print() - print('*'*80) - print() - print(get_message_by_id('1147')) - print() - print(get_message_by_id('1148')) - print() - ch = input('Press ENTER to continue') - - # - # Check conditions - # - State1_ref = ecu.get_ref_st(ScmParam['State1']) #Engine state - value7, datastr7 = ecu.get_st(ScmParam['State1']) - kb = KBHit() - while pyren_encode(value7) != pyren_encode( mod_globals.language_dict[ScmParam['TOURNANT']]): - value7, datastr7 = ecu.get_st(ScmParam['State1']) - value5, datastr5 = ecu.get_pr(ScmParam['Param6']) - value6, datastr6 = ecu.get_pr(ScmParam['Param7']) - clearScreen() - print(pyren_encode(header)) - print() - print(get_message('SCMTitle')) - print() - print('\tCHECK CONDITIONS') - print() - print('*'*90) - print(pyren_encode(datastr7)) - print(pyren_encode(datastr5)) - print(pyren_encode(datastr6)) - print('*'*90) - print(get_message_by_id('1149')) - print() - print('Strat the engine and press ENTER to continue') - print('Q to exit or A to continue anyway') - if kb.kbhit(): - c = kb.getch() - if len(c)!=1: continue - if c=='q' or c=='Q': - kb.set_normal_term() - return - elif c=='a' or c=='A': - kb.set_normal_term() - break - time.sleep( 0.2 ) - - # - # Ask permission to start - # - clearScreen() - print(pyren_encode(header)) - print() - ch = input('Are you ready to start regeneration? :') - if ch.lower()!='yes': return - - # - # Start regeneration - # - responce = ecu.run_cmd(ScmParam['Cmde1']) - - # - # Main cycle - # - begin_time = time.time() - Phase_state = ecu.get_ref_st(ScmParam['State2']) - Result_state = ecu.get_ref_st(ScmParam['State3']) - kb = KBHit() - pfe = 0 - while( 1 ): - # get all values before showing them for avoid screen flickering - value0, datastr0 = ecu.get_pr(ScmParam['Param1']) - value1, datastr1 = ecu.get_pr(ScmParam['Param2']) - value2, datastr2 = ecu.get_pr(ScmParam['Param3']) - value3, datastr3 = ecu.get_pr(ScmParam['Param4']) - value4, datastr4 = ecu.get_pr(ScmParam['Param5']) - value5, datastr5 = ecu.get_pr(ScmParam['Param6']) - value6, datastr6 = ecu.get_pr(ScmParam['Param7']) - value7, datastr7 = ecu.get_st(ScmParam['State1']) - value8, datastr8 = ecu.get_st(ScmParam['State2']) # Phase - value9, datastr9 = ecu.get_st(ScmParam['State3']) # Result status - valuea, datastra = ecu.get_st(ScmParam['State4']) - - #test - #value8 = 6 - #value9 = 3 - - current_time = time.time() - elapsed = int(current_time-begin_time) - minutes, seconds = divmod(elapsed, 60) - hours, minutes = divmod(minutes, 60) - - # - # Check phase - # - etat = pyren_encode(value8) - if etat == get_message('ETAT1'): phase = get_message('Phase1'); pfe = 0 - elif etat == get_message('ETAT2'): phase = get_message('Phase2'); pfe = 0 - elif etat == get_message('ETAT3'): phase = get_message('Phase3'); pfe = 0 - elif etat == get_message('ETAT4'): phase = get_message('Phase4'); pfe = 0 - elif etat == get_message('ETAT5'): phase = get_message('Phase5'); pfe = 1 - elif etat == get_message('ETAT6'): phase = get_message('Phase6'); pfe = 2 - else: phase = etat - - # - # Check result - # - rescode = pyren_encode(value9) - result = pyren_encode( mod_globals.language_dict[ScmSet[rescode]]) - - clearScreen() - print(pyren_encode(header)) - print('\tTime - ',"{hours:02d}:{minutes:02d}:{seconds:02d}".format(**vars())) - print('\tPhase - ', phase) - #print '\tResult - ', result - print('*'*90) - print(pyren_encode(datastr0)) - print(pyren_encode(datastr1)) - print(pyren_encode(datastr2)) - print(pyren_encode(datastr3)) - print(pyren_encode(datastr4)) - print(pyren_encode(datastr5)) - print(pyren_encode(datastr6)) - print(pyren_encode(datastr7)) - print(pyren_encode(datastr8)) - print(pyren_encode(datastr9)) - print(pyren_encode(datastra)) - print('*'*90) - if pfe: break - print('Press Q to emergency exit') - if kb.kbhit(): - c = kb.getch() - if len(c)!=1: continue - if c=='q' or c=='Q': - kb.set_normal_term() - responce = ecu.run_cmd(ScmParam['Cmde2']) - break - time.sleep( 0.2 ) - - if pfe: - print('\tPhase - ', phase) - print('\tResult - ', result) - print('*'*90) - - ch = input('Press ENTER to exit') - diff --git a/pyren3/scen_ecri_generique2.py b/pyren3/scen_ecri_generique2.py deleted file mode 100755 index f6d5ff5..0000000 --- a/pyren3/scen_ecri_generique2.py +++ /dev/null @@ -1,144 +0,0 @@ -#!/usr/bin/env python3 -''' - -Version: 180402 -This scenarium may enable/disable AndroidAuto and CarPlay - -Name of this script should be exactly the same as in scenaruim URL but with '.py' extension - -URL - scm:SCEN_ECRI_GENERIQUE2#SCEN_ECRI_GENERIQUE2_.xml - -'run' procedure will be executed by pyren script - -''' - -import os -import sys -import re -import time - -import mod_globals -import mod_utils -import mod_ecu -from mod_utils import pyren_encode -from mod_utils import clearScreen - -import xml.dom.minidom - -def run( elm, ecu, command, data ): - ''' - MAIN function of scenarium - - Parameters: - elm - refernce to adapter class - ecu - reference to ecu class - command - refernce to the command this scenarium belongs to - data - name of xml file with parameters from scenarium URL - ''' - - clearScreen() - header = '['+command.codeMR+'] '+command.label - - # - # Important information - # - clearScreen() - print(pyren_encode(header)) - print() - print('This scenarium may enable/disable AndroidAuto and CarPlay') - print() - print('*'*50) - - # - # check if this ECU is supported - # - eid = data[-9:-4] - if eid not in ['11300']: - print('\n\nThis ECU is unsupported !!!!\n\n') - ch = input('Press ENTER to exit') - return - - # - # read current value - # - print('Reading current value') - rsp = elm.request("222130",positive='622130', cache=False) - rsp = rsp.replace(' ','')[:20] - print("Done:", rsp) - if not rsp.startswith('622130'): - print('Got WRONG RESPONSE !!!') - ch = input('Press ENTER to exit') - return - hexVal = int(rsp[8:9],16) - print('*'*50) - if hexVal & 0x2: - print('AndroidAuto : ON') - else: - print('AndroidAuto : OFF') - if hexVal & 0x4: - print('CarPlay : ON') - else: - print('CarPlay : OFF') - print('*'*50) - - # - # changing value - # - ch = input ('What do you want? :') - if ch.lower () != 'on' and ch.lower () != 'off': return - - if ch.lower () == 'off': - print('Swithing OFF !!!') - hexVal = hexVal & 0x9 - elif ch.lower () == 'on': - print('Swithing ON !!!') - hexVal = hexVal | 0x6 - newcmd = '2E2130'+rsp[6:8]+hex(hexVal)[-1:].upper()+rsp[9:] - - # - # writing value - # - print('New :',newcmd) - print('We are ready to change') - ch = input ('Do you agree? :') - if ch.lower () != 'yes': return - rsp = elm.request(newcmd, positive='6E2130', cache=False) - if not rsp.upper().replace(' ','').startswith('6E2130'): - print('RSP :',rsp) - print('Got ERROR!!!') - ch = input('Press ENTER to exit') - return - - # - # wait a bit - # - time.sleep(2) - - # - # read new value - # - print('Reading new value') - rsp = elm.request ("222130", positive='622130', cache=False) - rsp = rsp.replace (' ', '')[:20] - print("Done:", rsp) - if not rsp.startswith ('622130'): - print('Got WRONG RESPONSE !!!') - ch = input ('Press ENTER to exit') - return - hexVal = int (rsp[8:9], 16) - print('*' * 50) - if hexVal & 0x2: - print('AndroidAuto : ON') - else: - print('AndroidAuto : OFF') - if hexVal & 0x4: - print('CarPlay : ON') - else: - print('CarPlay : OFF') - print('*' * 50) - - print('\n\n\t DONE') - print('\n\n You have to reset the device manually ') - print(' by long press on power button\n\n') - ch = input('Press ENTER to continue') - diff --git a/pyren3/scen_ecri_initpente.py b/pyren3/scen_ecri_initpente.py deleted file mode 100755 index 6f28f48..0000000 --- a/pyren3/scen_ecri_initpente.py +++ /dev/null @@ -1,87 +0,0 @@ -#!/usr/bin/env python3 - -import os -import sys -import re -import time -import string -import mod_globals -import mod_utils -import mod_ecu -import mod_db_manager -from mod_utils import clearScreen -from mod_utils import pyren_encode -from mod_utils import KBHit -import xml.dom.minidom - - -def run(elm, ecu, command, data): - clearScreen() - header = '[' + command.codeMR + '] ' + command.label - - ScmSet = {} - ScmParam = {} - - def get_message(msg): - if msg in list(ScmParam.keys()): - value = ScmParam[msg] - else: - value = msg - if value.isdigit() and value in list(mod_globals.language_dict.keys()): - value = pyren_encode(mod_globals.language_dict[value]) - return value - - def get_message_by_id(id): - if id.isdigit() and id in list(mod_globals.language_dict.keys()): - value = pyren_encode(mod_globals.language_dict[id]) - return value - - DOMTree = xml.dom.minidom.parse(mod_db_manager.get_file_from_clip(data)) - ScmRoom = DOMTree.documentElement - - ScmParams = ScmRoom.getElementsByTagName("ScmParam") - - for Param in ScmParams: - name = pyren_encode(Param.getAttribute("name")) - value = pyren_encode(Param.getAttribute("value")) - - ScmParam[name] = value - - kb = KBHit() - - mainText = get_message('TexteTitre') - important = get_message('TexteConsigne') - tilt = get_message('TexteValeurInclinaison') - degreeSymbol = get_message('TexteDegre') - value2, datastr2 = ecu.get_pr(ScmParam['ParametreInclinaison']) - - clearScreen() - print(pyren_encode(header)) - print(mainText) - print('*' * 80) - print() - print(important) - print() - - ch = input('Do you want to continue? ') - while (ch.upper() != 'YES') and (ch.upper() != 'NO'): - ch = input('Do you want to continue? ') - if ch.upper() != 'YES': - return - - clearScreen() - cmd = ecu.get_ref_cmd(get_message('Commande1')) - resVal = ScmParam['ParametreCommande1'] - print('*' * 80) - responce = ecu.run_cmd(ScmParam['Commande1'], resVal) - print('*' * 80) - if 'NR' in responce: - print(get_message('TexteProcedureInterompue')) - else: - print(get_message('TexteInitialisationEffectuee')) - print() - print(tilt, pyren_encode(':'), value2, degreeSymbol) - print() - - ch = input('Press ENTER to exit') - return diff --git a/pyren3/scen_ecri_paraminj1.py b/pyren3/scen_ecri_paraminj1.py deleted file mode 100755 index f0c8cff..0000000 --- a/pyren3/scen_ecri_paraminj1.py +++ /dev/null @@ -1,653 +0,0 @@ -#!/usr/bin/env python3 -''' -Scenarium usage example - -Name of this script should be exactly the same as in scenaruim URL but with '.py' extension - -URL - scm:scen_ecri_calinj1#scen_ecri_calinj1_xxxxx.xml - -'run' procedure will be executed by pyren script - -''' - -import os -import sys -import re -import time -import string -import mod_globals -import mod_utils -import mod_ecu -import mod_db_manager -from mod_utils import pyren_encode -from mod_utils import clearScreen -from mod_utils import ASCIITOHEX -from mod_utils import StringToIntToHex -from mod_utils import Choice -from collections import OrderedDict -import xml.dom.minidom -import xml.etree.cElementTree as et - -class ecus: - - vdiag = "" - buttons = {} - ncalib = "" - - def __init__(self, vd, nc, bt): - self.vdiag = vd - self.ncalib = nc - self.buttons = bt - -def run( elm, ecu, command, data ): - ''' - MAIN function of scenarium - - Parameters: - elm - refernce to adapter class - ecu - reference to ecu class - command - refernce to the command this scenarium belongs to - data - name of xml file with parameters from scenarium URL - ''' - - clearScreen() - header = '['+command.codeMR+'] '+command.label - - ScmSet = {} - ScmParam = OrderedDict() - ecusList = [] - correctEcu = '' - vdiagExists = False - ncalibExists = False - - def get_message( msg, encode = 1 ): - if msg in list(ScmParam.keys()): - value = ScmParam[msg] - else: - value = msg - if value.isdigit() and value in list(mod_globals.language_dict.keys()): - if encode: - value = pyren_encode(mod_globals.language_dict[value]) - else: - value = mod_globals.language_dict[value] - return value - - def get_message_by_id( id, encode = 1 ): - if id.isdigit() and id in list(mod_globals.language_dict.keys()): - if encode: - value = pyren_encode(mod_globals.language_dict[id]) - else: - value = mod_globals.language_dict[id] - return value - - # - # Data file parsing - # - DOMTree = xml.dom.minidom.parse(mod_db_manager.get_file_from_clip(data)) - ScmRoom = DOMTree.documentElement - - root = et.parse(mod_db_manager.get_file_from_clip(data)).getroot() - - ScmParams = ScmRoom.getElementsByTagName("ScmParam") - - for Param in ScmParams: - name = pyren_encode( Param.getAttribute("name") ) - value = pyren_encode( Param.getAttribute("value") ) - - ScmParam[name] = value - - ScmSets = ScmRoom.getElementsByTagName("ScmSet") - - for Set in ScmSets: - if len(Set.attributes) != 1: - setname = pyren_encode(mod_globals.language_dict[Set.getAttribute("name")]) - ScmParams = Set.getElementsByTagName("ScmParam") - - for Param in ScmParams: - name = pyren_encode( Param.getAttribute("name") ) - value = pyren_encode( Param.getAttribute("value") ) - - ScmSet[setname]= value - ScmParam[name] = value - - if "VDiag" in list(ScmParam.keys()): - vdiagExists = True - if "Ncalib" in list(ScmParam.keys()): - ncalibExists = True - - # Get nested buttons with VDiag and Ncalib - for vDiag in root: - if vDiag.attrib["name"] == "VDiag": - if len(list(vDiag.keys())) == 1: - for vDiagName in vDiag: - if vDiagName: - for vDiagButtons in vDiagName: - buttons = OrderedDict() - if vDiagButtons.attrib["name"] == "Ncalib": - for ncalibName in vDiagButtons: - for ncalibButtons in ncalibName: - if ncalibButtons.attrib["name"] == "Buttons": - for ncalibButton in ncalibButtons: - buttons[ncalibButton.attrib["name"]] = ncalibButton.attrib["value"] - ecusList.append(ecus(vDiagName.attrib["name"],ncalibName.attrib["name"], buttons)) - buttons = OrderedDict() - else: - if vDiagButtons.attrib["name"] == "Buttons": - for vDiagButton in vDiagButtons: - buttons[vDiagButton.attrib["name"]] = vDiagButton.attrib["value"] - ecusList.append(ecus(vDiagName.attrib["name"], '', buttons)) - -# Get plain buttons with VDiag - if vdiagExists: - if not ncalibExists: - vdiag = '' - buttons = OrderedDict() - for name in list(ScmParam.keys()): - if name.startswith("InjectorsButton"): - if buttons: - ecusList.append(ecus(vdiag, '', buttons)) - buttons = OrderedDict() - vdiag = name[-2:] - buttons[name[:-2]] = ScmParam[name] - if vdiag: - if name.endswith("Button" + vdiag): - buttons[name[:-2]] = ScmParam[name] - ecusList.append(ecus(vdiag, '', buttons)) - else: #Get buttons without VDiag - buttons = OrderedDict() - found = False - for name in list(ScmParam.keys()): - if name == "InjectorsButton": - buttons[name] = ScmParam[name] - found = True - if found: - if name.endswith("Button"): - buttons[name] = ScmParam[name] - else: - found = False - break - ecusList.append(ecus('', '', buttons)) - -# Get correct buttons set - if vdiagExists: - value1, datastr1 = ecu.get_id(ScmParam['VDiag']) - for ecuSet in ecusList: - if ecuSet.vdiag == value1.upper(): - if ncalibExists: - if ecuSet.ncalib: - value2, datastr2 = ecu.get_id(ScmParam['Ncalib']) - if ecuSet.ncalib == value2.upper(): - correctEcu = ecuSet - break - elif ecuSet.ncalib == "Other": - correctEcu = ecuSet - break - else: - correctEcu = ecuSet - break - else: - correctEcu = ecuSet - break - else: - correctEcu = ecusList[0] - - if not correctEcu and mod_globals.opt_demo: - correctEcu = ecusList[0] - - if vdiagExists: - if not correctEcu: - print('*'*80) - ch = input('Unknown diagnostic version. Press ENTER to exit') - return - - #Prepare buttons - buttons = OrderedDict() - - for bt in list(correctEcu.buttons.keys()): - if bt == 'InjectorsButton': - if str(correctEcu.buttons[bt]) == 'true': - buttons[1] = get_message("Injectors", 0) - if bt == 'EGRValveButton': - if str(correctEcu.buttons[bt]) == 'true': - buttons[2] = get_message("EGR_VALVE", 0) - if bt == 'InletFlapButton': - if str(correctEcu.buttons[bt]) == 'true': - buttons[3] = get_message("INLET_FLAP", 0) - if bt.startswith("Button"): - if str(correctEcu.buttons[bt]) == 'true': - buttons[int(bt.strip('Button'))] = get_message(bt[:-6] + "Text", 0) - buttons["loadDump"] = get_message_by_id('19802', 0) - buttons["exit"] = '' - - #Get commands - commands = {} - - for child in root: - if child.attrib["name"] == "Commands": - if len(list(child.keys())) == 1: - for param in child: - serviceIDs = ecu.get_ref_cmd(param.attrib["value"]).serviceID - startReq = "" - for sid in serviceIDs: - if ecu.Services[sid].params: - startReq = ecu.Services[sid].startReq - break - commands[param.attrib["name"]] = {"command": param.attrib["value"], "startReq": startReq} - - #Get identifications - identsList = OrderedDict() - identsRangeKeys = OrderedDict() - - for param in list(ScmParam.keys()): - if param.startswith('Idents') and param.endswith('Begin'): - key = param[6:-5] - begin = int(ScmParam['Idents'+key+'Begin']) - end = int(ScmParam['Idents'+key+'End']) - try: - ecu.get_ref_id(ScmParam['Ident' + str(begin)]).mnemolist[0] #10529 ID114 doesn't exist - except: - continue - else: - for idnum in range(begin ,end + 1): - identsList['D'+str(idnum)] = ScmParam['Ident'+str(idnum)] - if len(ecu.get_ref_id(ScmParam['Ident' + str(idnum)]).mnemolist) > 1: - mnemonicsLen = [int(ecu.Mnemonics[bitsLen].bitsLength) for bitsLen in ecu.get_ref_id(ScmParam['Ident' + str(idnum)]).mnemolist] - ecu.get_ref_id(ScmParam['Ident' + str(idnum)]).mnemolist = [ecu.get_ref_id(ScmParam['Ident' + str(idnum)]).mnemolist[mnemonicsLen.index(max(mnemonicsLen))]] - frame = ecu.Mnemonics[ecu.get_ref_id(identsList['D'+str(begin)]).mnemolist[0]].request - identsRangeKeys[key] = {"begin": begin, "end": end, "frame": frame} - - def getValuesToChange(resetItem): - params = {} - for child in root: - if child.attrib["name"] == resetItem: - if len(list(child.keys())) == 1: - for param in child: - params[param.attrib["name"].replace("D0", "D")] = param.attrib["value"] - return params - - def takesParams(request): - for cmd in list(commands.values()): - if cmd['startReq'] == request: - commandToRun = cmd['command'] - return commandToRun - - def getValuesFromEcu(params): - paramToSend = "" - commandToRun = "" - requestToFindInCommandsRequests = "" - backupDict = {} - - try: - idKeyToFindInRange = int((list(params.keys())[0]).replace("D","")) - except: - return commandToRun, paramToSend - else: - for rangeK in list(identsRangeKeys.keys()): - if identsRangeKeys[rangeK]['begin'] <= idKeyToFindInRange <= identsRangeKeys[rangeK]['end']: - requestToFindInCommandsRequests = "3B" + identsRangeKeys[rangeK]['frame'][-2:] - isTakingParams = takesParams(requestToFindInCommandsRequests) - if isTakingParams: - for k,v in params.items(): - backupDict[k] = ecu.get_id(identsList[k], 1) - if v in list(identsList.keys()): - identsList[k] = ecu.get_id(identsList[v], 1) - else: - identsList[k] = v - for idKey in range(identsRangeKeys[rangeK]['begin'], identsRangeKeys[rangeK]['end'] + 1): - if identsList["D" + str(idKey)].startswith("ID"): - identsList["D" + str(idKey)] = ecu.get_id(identsList["D" + str(idKey)], 1) - backupDict["D" + str(idKey)] = identsList["D" + str(idKey)] - paramToSend += identsList["D" + str(idKey)] - commandToRun = isTakingParams - break - - makeDump(commandToRun, backupDict) - return commandToRun, paramToSend - - confirm = get_message_by_id('19800') - successMessage = get_message('Message32') - failMessage = get_message('MessageNACK') - mainText = get_message('Title') - inProgressMessage = get_message('CommandInProgressMessage') - - def resetInjetorsData(button, injectorsList): - injectorsInfoMessage = get_message('Message21') - response = "" - clearScreen() - - print(mainText) - print('*'*80) - print(pyren_encode(buttons[button])) - print('*'*80) - print(injectorsInfoMessage) - print('*'*80) - print() - - choice = Choice(list(injectorsList.keys()), "Choose :") - if choice[0]=='': return - - clearScreen() - - print() - response = ecu.run_cmd(injectorsList[choice[0]]) - print() - - if "NR" in response: - print(failMessage) - else: - print(successMessage) - - print() - ch = input('Press ENTER to exit') - - def afterEcuChange(title, button): - params = getValuesToChange(title) - infoMessage = get_message("Message262") - mileageText = get_message_by_id('2110') - mileageUnit = get_message_by_id('16521') - - clearScreen() - print(mainText) - print('*'*80) - print(pyren_encode(buttons[button])) - print('*'*80) - print(infoMessage) - print('*'*80) - print(get_message("MessageBox2")) - print() - ch = input(confirm + ' : ') - if ch.upper()!='YES': - return - mileage = input(mileageText + ' (' + mileageUnit + ')' + ': ') - while not (mileage.isdigit() and 2 <= len(mileage) <= 6 and int(mileage) >= 10): - print(get_message("MessageBox1")) - print() - mileage = input(mileageText + ' (' + mileageUnit + ')' + ': ') - - clearScreen() - - print(mileageText + ': ' + mileage + ' ' + mileageUnit) - print() - ch = input(confirm + ' : ') - while (ch.upper()!='YES') and (ch.upper()!='NO'): - ch = input(confirm + ' : ') - if ch.upper()!='YES': - return - - clearScreen() - - print() - print(inProgressMessage) - - mileage = int(mileage) - - for paramkey in list(params.keys()): - if params[paramkey] == "Mileage": - mnemonics = ecu.get_ref_id(identsList[paramkey]).mnemolist[0] - identValue = ecu.get_id(identsList[paramkey], 1) - if identValue == 'ERROR': - identValue = '00000000' - hexval = "{0:0{1}X}".format(mileage,len(identValue)) - if ecu.Mnemonics[mnemonics].littleEndian == '1': - a = hexval - b = '' - if not len(a) % 2: - for i in range(0,len(a),2): - b = a[i:i+2]+b - hexval = b - params[paramkey] = hexval - - command, paramToSend = getValuesFromEcu(params) - - if "ERROR" in paramToSend: - input("Data downloading went wrong. Aborting.") - return - - clearScreen() - - print() - response = ecu.run_cmd(command,paramToSend) - print() - - if "NR" in response: - print(failMessage) - else: - print(successMessage) - - print() - ch = input('Press ENTER to exit') - - def setGlowPlugsType(title, button): - params = getValuesToChange(title) - currentType = ecu.get_id(identsList[params["IdentToBeDisplayed"].replace("Ident", "D")], 1) - slowTypeValue = get_message('ValueSlowParam') - fastTypeValue = get_message('ValueFastParam') - currentMessage = get_message_by_id('52676') - slowMessage = get_message('Slow') - fastMessage = get_message('Fast') - notDefinedMessage = get_message('NotDefined') - message2 = get_message('Message282') - - typesButtons = OrderedDict() - - typesButtons[get_message('Slow', 0)] = slowTypeValue - typesButtons[get_message('Fast', 0)] = fastTypeValue - typesButtons[''] = "" - - clearScreen() - print(mainText) - print('*'*80) - print(pyren_encode(buttons[button])) - print('*'*80) - print(message2) - print('*'*80) - print() - if currentType == slowTypeValue: - print(currentMessage + ': ' + slowMessage) - elif currentType == fastTypeValue: - print(currentMessage + ': ' + fastMessage) - else: - print(currentMessage + ': ' + notDefinedMessage) - print() - - choice = Choice(list(typesButtons.keys()), "Choose :") - if choice[0]=='': return - - clearScreen() - print() - print(inProgressMessage) - - params[params["IdentToBeDisplayed"].replace("Ident", "D")] = typesButtons[choice[0]] - params.pop("IdentToBeDisplayed") - - command, paramToSend = getValuesFromEcu(params) - - if "ERROR" in paramToSend: - input("Data downloading went wrong. Aborting.") - return - - clearScreen() - - print() - response = ecu.run_cmd(command,paramToSend) - print() - - if "NR" in response: - print(failMessage) - else: - print(successMessage) - - print() - ch = input('Press ENTER to exit') - - def resetValues(title, button, defaultCommand): - paramToSend = "" - commandTakesParams = True - params = getValuesToChange(title) - - clearScreen() - - print(mainText) - print('*'*80) - print(pyren_encode(buttons[button])) - print('*'*80) - if button == 4: - print(get_message_by_id('55662')) - print('*'*80) - if button == 5: - print(get_message_by_id('55663')) - print('*'*80) - print() - ch = input(confirm + ' : ') - while (ch.upper()!='YES') and (ch.upper()!='NO'): - ch = input(confirm + ' : ') - if ch.upper()!='YES': - return - - clearScreen() - print() - print(inProgressMessage) - - command, paramToSend = getValuesFromEcu(params) - - if "ERROR" in paramToSend: - input("Data downloading went wrong. Aborting.") - return - - clearScreen() - - print() - if command: - response = ecu.run_cmd(command,paramToSend) - else: - response = ecu.run_cmd(defaultCommand) - print() - - if "NR" in response: - print(failMessage) - else: - print(successMessage) - - print() - ch = input('Press ENTER to exit') - - def makeDump(cmd, idents): - fileRoot = et.Element("ScmRoot") - fileRoot.text = "\n " - - cmdElement = et.Element("ScmParam", name="Command", value=cmd) - cmdElement.tail = "\n " - fileRoot.insert(1,cmdElement) - - for k in idents: - el = et.Element("ScmParam", name='D'+ '{:0>2}'.format(k[1:]), value=idents[k]) - el.tail = "\n " - fileRoot.insert(1,el) - - tree = et.ElementTree(fileRoot) - tree.write(mod_globals.dumps_dir + ScmParam['FileName']) - - def loadDump(): - clearScreen() - - paramToSend = "" - dumpScmParam = {} - try: - dumpData = open(mod_globals.dumps_dir + ScmParam['FileName'], 'r') - except: - print(get_message_by_id('2194')) - input() - return - - dumpDOMTree = xml.dom.minidom.parse(dumpData) - dumpScmRoot = dumpDOMTree.documentElement - dumpScmParams = dumpScmRoot.getElementsByTagName("ScmParam") - - for Param in dumpScmParams: - name = pyren_encode( Param.getAttribute("name") ) - value = pyren_encode( Param.getAttribute("value") ) - - dumpScmParam[name] = value - - for k in sorted(dumpScmParam): - if k != "Command": - paramToSend += dumpScmParam[k] - - if "ERROR" in paramToSend: - input("Data downloading went wrong. Aborting.") - return - - print('*'*80) - print(get_message_by_id('19802')) - print('*'*80) - print() - - ch = input(confirm + ' : ') - while (ch.upper()!='YES') and (ch.upper()!='NO'): - ch = input(confirm + ' : ') - if ch.upper()!='YES': - return - - clearScreen() - - print() - response = ecu.run_cmd(dumpScmParam['Command'],paramToSend) - print() - - if "NR" in response: - print(failMessage) - else: - print(successMessage) - - print() - ch = input('Press ENTER to exit') - - - functions = OrderedDict() - for cmdKey in list(commands.keys()): - if cmdKey == 'Cmd1': - injectorsDict = OrderedDict() - injectorsDict[get_message('Cylinder1', 0)] = commands['Cmd1']['command'] - injectorsDict[get_message('Cylinder2', 0)] = commands['Cmd2']['command'] - injectorsDict[get_message('Cylinder3', 0)] = commands['Cmd3']['command'] - injectorsDict[get_message('Cylinder4', 0)] = commands['Cmd4']['command'] - injectorsDict[''] = "" - functions[1] = [1, injectorsDict] - if cmdKey == 'Cmd5': - functions[2] = ["EGR_VALVE", 2, commands['Cmd5']['command']] - if cmdKey == 'Cmd6': - functions[3] = ["INLET_FLAP", 3, commands['Cmd6']['command']] - if cmdKey == 'Cmd7': - functions[4] = ["PARTICLE_FILTER", 4, commands['Cmd7']['command']] - functions[5] = ["Button5ChangeData", 5, commands['Cmd7']['command']] - functions[6] = ["Button6ChangeData", 6, commands['Cmd7']['command']] - if cmdKey == 'Cmd9': - functions[8] = ["Button8DisplayData", 8] - - infoMessage = get_message('Message1') - - print(mainText) - print() - print(infoMessage) - print() - - notSupported = [7] - - choice = Choice(list(buttons.values()), "Choose :") - - for key, value in buttons.items(): - if choice[0] =='': return - if value == choice[0]: - if key in notSupported: - ch = input("\nNot Supported yet. Press ENTER to exit") - elif key == 'loadDump': - loadDump() - elif key == 1: - resetInjetorsData(functions[key][0],functions[key][1]) - elif key == 6: - afterEcuChange(functions[key][0],functions[key][1]) - elif key == 8: - setGlowPlugsType(functions[key][0],functions[key][1]) - else: - resetValues(functions[key][0],functions[key][1],functions[key][2]) - return \ No newline at end of file diff --git a/pyren3/scen_ecri_paraminj2.py b/pyren3/scen_ecri_paraminj2.py deleted file mode 100755 index 32602a2..0000000 --- a/pyren3/scen_ecri_paraminj2.py +++ /dev/null @@ -1,589 +0,0 @@ -#!/usr/bin/env python3 -''' -Scenarium usage example - -Name of this script should be exactly the same as in scenaruim URL but with '.py' extension - -URL - scm:scen_ecri_calinj1#scen_ecri_calinj1_xxxxx.xml - -'run' procedure will be executed by pyren script - -''' - -import os -import sys -import re -import time -import string -import mod_globals -import mod_utils -import mod_ecu -import mod_db_manager -from mod_utils import pyren_encode -from mod_utils import clearScreen -from mod_utils import ASCIITOHEX -from mod_utils import StringToIntToHex -from mod_utils import Choice -from collections import OrderedDict -import xml.dom.minidom -import xml.etree.cElementTree as et - -class ecus: - - vdiag = "" - buttons = {} - ncalib = "" - - def __init__(self, vd, nc, bt): - self.vdiag = vd - self.ncalib = nc - self.buttons = bt - -def run( elm, ecu, command, data ): - ''' - MAIN function of scenarium - - Parameters: - elm - refernce to adapter class - ecu - reference to ecu class - command - refernce to the command this scenarium belongs to - data - name of xml file with parameters from scenarium URL - ''' - - clearScreen() - header = '['+command.codeMR+'] '+command.label - - ScmSet = {} - ScmParam = OrderedDict() - ecusList = [] - correctEcu = '' - vdiagExists = False - - def get_message( msg, encode = 1 ): - if msg in list(ScmParam.keys()): - value = ScmParam[msg] - else: - value = msg - if value.isdigit() and value in list(mod_globals.language_dict.keys()): - if encode: - value = pyren_encode(mod_globals.language_dict[value]) - else: - value = mod_globals.language_dict[value] - return value - - def get_message_by_id( id, encode = 1 ): - if id.isdigit() and id in list(mod_globals.language_dict.keys()): - if encode: - value = pyren_encode(mod_globals.language_dict[id]) - else: - value = mod_globals.language_dict[id] - return value - - # - # Data file parsing - # - DOMTree = xml.dom.minidom.parse(mod_db_manager.get_file_from_clip(data)) - ScmRoom = DOMTree.documentElement - - root = et.parse(mod_db_manager.get_file_from_clip(data)).getroot() - - ScmParams = ScmRoom.getElementsByTagName("ScmParam") - - for Param in ScmParams: - name = pyren_encode( Param.getAttribute("name") ) - value = pyren_encode( Param.getAttribute("value") ) - - ScmParam[name] = value - - ScmSets = ScmRoom.getElementsByTagName("ScmSet") - - for Set in ScmSets: - if len(Set.attributes) != 1: - setname = pyren_encode(mod_globals.language_dict[Set.getAttribute("name")]) - ScmParams = Set.getElementsByTagName("ScmParam") - - for Param in ScmParams: - name = pyren_encode( Param.getAttribute("name") ) - value = pyren_encode( Param.getAttribute("value") ) - - ScmSet[setname]= value - ScmParam[name] = value - - if "IdentVdiag" in list(ScmParam.keys()): - vdiagExists = True - - # Get nested buttons with VDiag - for vDiag in root: - if vDiag.attrib["name"] == "ListVdiag": - if len(list(vDiag.keys())) == 1: - for vDiagName in vDiag: - buttons = OrderedDict() - if vDiagName: - for vDiagButton in vDiagName: - buttons[vDiagButton.attrib["name"]] = vDiagButton.attrib["value"] - ecusList.append(ecus(vDiagName.attrib["name"], '', buttons)) - -# Get correct buttons set - if vdiagExists: - value1, datastr1 = ecu.get_id(ScmParam['IdentVdiag']) - for ecuSet in ecusList: - if ecuSet.vdiag == value1.upper(): - correctEcu = ecuSet - break - else: - correctEcu = ecusList[0] - - if not correctEcu and mod_globals.opt_demo: - correctEcu = ecusList[0] - - if vdiagExists: - if not correctEcu: - print('*'*80) - ch = input('Unknown diagnostic version. Press ENTER to exit') - return - - #Prepare buttons - buttons = OrderedDict() - - for bt in list(correctEcu.buttons.keys()): - if bt == 'InjectorsButton': - if str(correctEcu.buttons[bt]) == 'true': - buttons[1] = get_message("Injectors",0) - if bt == 'EGRValveButton': - if str(correctEcu.buttons[bt]) == 'true': - buttons[2] = get_message("EGR_VALVE",0) - if bt == 'InletFlapButton': - if str(correctEcu.buttons[bt]) == 'true': - buttons[3] = get_message("INLET_FLAP",0) - if bt == 'ParticleFilterButton': - if str(correctEcu.buttons[bt]) == 'true': - buttons[4] = get_message("PARTICLES_FILTER",0) - if bt.startswith("Button"): - if str(correctEcu.buttons[bt]) == 'true': - buttons[int(bt.strip('Button'))] = get_message(bt[:-6] + "Text", 0) - buttons["loadDump"] = get_message_by_id('19802', 0) - buttons["exit"] = '' - - #Get commands - commands = {} - - for child in root: - if child.attrib["name"] == "Commands": - if len(list(child.keys())) == 1: - for param in child: - serviceIDs = ecu.get_ref_cmd(param.attrib["value"]).serviceID - startReq = "" - for sid in serviceIDs: - if ecu.Services[sid].params: - startReq = ecu.Services[sid].startReq - break - commands[param.attrib["name"]] = {"command": param.attrib["value"], "startReq": startReq} - - #Get identifications - identsList = OrderedDict() - identsRangeKeys = OrderedDict() - - for param in list(ScmParam.keys()): - if param.startswith('Idents') and param.endswith('Begin'): - key = param[6:-5] - begin = int(ScmParam['Idents'+key+'Begin']) - end = int(ScmParam['Idents'+key+'End']) - try: #10099 trap - ecu.get_ref_id(ScmParam['Ident' + str(begin)]).mnemolist[0] - except: - continue - else: - for idnum in range(begin ,end + 1): - identsList['D'+str(idnum)] = ScmParam['Ident'+str(idnum)] - if len(ecu.get_ref_id(ScmParam['Ident' + str(idnum)]).mnemolist) > 1: - mnemonicsLen = [int(ecu.Mnemonics[bitsLen].bitsLength) for bitsLen in ecu.get_ref_id(ScmParam['Ident' + str(idnum)]).mnemolist] - ecu.get_ref_id(ScmParam['Ident' + str(idnum)]).mnemolist = [ecu.get_ref_id(ScmParam['Ident' + str(idnum)]).mnemolist[mnemonicsLen.index(max(mnemonicsLen))]] - frame = ecu.Mnemonics[ecu.get_ref_id(identsList['D'+str(begin)]).mnemolist[0]].request - identsRangeKeys[key] = {"begin": begin, "end": end, "frame": frame} - - def getValuesToChange(resetItem): - params = {} - for child in root: - if child.attrib["name"] == resetItem: - if len(list(child.keys())) == 1: - for param in child: - params[param.attrib["name"].replace("D0", "D")] = param.attrib["value"] - return params - - def takesParams(request): - for cmd in list(commands.values()): - if cmd['startReq'] == request: - commandToRun = cmd['command'] - return commandToRun - - def getValuesFromEcu(params): - paramToSend = "" - commandToRun = "" - requestToFindInCommandsRequests = "" - backupDict = {} - - try: - idKeyToFindInRange = int((list(params.keys())[0]).replace("D","")) - except: - return commandToRun, paramToSend - else: - for rangeK in list(identsRangeKeys.keys()): - if identsRangeKeys[rangeK]['begin'] <= idKeyToFindInRange <= identsRangeKeys[rangeK]['end']: - requestToFindInCommandsRequests = "3B" + identsRangeKeys[rangeK]['frame'][-2:] - isTakingParams = takesParams(requestToFindInCommandsRequests) - if isTakingParams: - for k,v in params.items(): - backupDict[k] = ecu.get_id(identsList[k], 1) - if v in list(identsList.keys()): - identsList[k] = ecu.get_id(identsList[v], 1) - else: - identsList[k] = v - for idKey in range(identsRangeKeys[rangeK]['begin'], identsRangeKeys[rangeK]['end'] + 1): - if identsList["D" + str(idKey)].startswith("ID"): - identsList["D" + str(idKey)] = ecu.get_id(identsList["D" + str(idKey)], 1) - backupDict["D" + str(idKey)] = identsList["D" + str(idKey)] - paramToSend += identsList["D" + str(idKey)] - commandToRun = isTakingParams - break - - makeDump(commandToRun, backupDict) - return commandToRun, paramToSend - - confirm = get_message_by_id('19800') - successMessage = get_message('Message32') - failMessage = get_message('MessageNACK') - mainText = get_message('Title') - inProgressMessage = get_message('CommandInProgressMessage') - - def resetInjetorsData(button, injectorsList): - injectorsInfoMessage = get_message('Message21') - response = "" - clearScreen() - - print(mainText) - print('*'*80) - print(pyren_encode(buttons[button])) - print('*'*80) - print(injectorsInfoMessage) - print('*'*80) - print() - - choice = Choice(list(injectorsList.keys()), "Choose :") - if choice[0]=='': return - - clearScreen() - - print() - response = ecu.run_cmd(injectorsList[choice[0]]) - print() - - if "NR" in response: - print(failMessage) - else: - print(successMessage) - - print() - ch = input('Press ENTER to exit') - - def afterEcuChange(title, button): - params = getValuesToChange(title) - infoMessage = get_message("Message262") - mileageText = get_message_by_id('2110') - mileageUnit = get_message_by_id('16521') - - clearScreen() - print(mainText) - print('*'*80) - print(pyren_encode(buttons[button])) - print('*'*80) - print(infoMessage) - print('*'*80) - print(get_message("MessageBox2")) - print() - ch = input(confirm + ' : ') - if ch.upper()!='YES': - return - mileage = input(mileageText + ' (' + mileageUnit + ')' + ': ') - while not (mileage.isdigit() and 2 <= len(mileage) <= 6 and int(mileage) >= 10): - print(get_message("MessageBox1")) - print() - mileage = input(mileageText + ' (' + mileageUnit + ')' + ': ') - - clearScreen() - - print(mileageText + ': ' + mileage + ' ' + mileageUnit) - print() - ch = input(confirm + ' : ') - while (ch.upper()!='YES') and (ch.upper()!='NO'): - ch = input(confirm + ' : ') - if ch.upper()!='YES': - return - - clearScreen() - - print() - print(inProgressMessage) - - mileage = int(mileage) - - for paramkey in list(params.keys()): - if params[paramkey] == "Mileage": - mnemonics = ecu.get_ref_id(identsList[paramkey]).mnemolist[0] - identValue = ecu.get_id(identsList[paramkey], 1) - if identValue == 'ERROR': - identValue = '00000000' - hexval = "{0:0{1}X}".format(mileage,len(identValue)) - if ecu.Mnemonics[mnemonics].littleEndian == '1': - a = hexval - b = '' - if not len(a) % 2: - for i in range(0,len(a),2): - b = a[i:i+2]+b - hexval = b - params[paramkey] = hexval - - command, paramToSend = getValuesFromEcu(params) - - if "ERROR" in paramToSend: - input("Data downloading went wrong. Aborting.") - return - - clearScreen() - - print() - response = ecu.run_cmd(command,paramToSend) - print() - - if "NR" in response: - print(failMessage) - else: - print(successMessage) - - print() - ch = input('Press ENTER to exit') - - def setGlowPlugsType(title, button): - params = getValuesToChange(title) - value, datastr = ecu.get_st(ScmParam['State1']) - - message = get_message('Message29') - currentTypeMessage = get_message_by_id('52676') - - typesButtons = OrderedDict() - - typesButtons[get_message_by_id('54031',0)] = ScmParam['54031'] - typesButtons[get_message_by_id('54030',0)] = ScmParam['54030'] - typesButtons[get_message_by_id('54032',0)] = ScmParam['54032'] - typesButtons[''] = "" - - clearScreen() - print(mainText) - print('*'*80) - print(pyren_encode(buttons[button])) - print('*'*80) - print(message) - print('*'*80) - print() - print(currentTypeMessage + ':') - print() - print(datastr) - print() - - choice = Choice(list(typesButtons.keys()), "Choose :") - if choice[0]=='': return - - clearScreen() - print() - print(inProgressMessage) - - glowPlugType = "{0:0{1}X}".format((int(ScmParam['Mask1']) + int(typesButtons[choice[0]])),2) - - params[list(params.keys())[0]] = glowPlugType - - command, paramToSend = getValuesFromEcu(params) - - if "ERROR" in paramToSend: - input("Data downloading went wrong. Aborting.") - return - - clearScreen() - - print() - response = ecu.run_cmd(command,paramToSend) - print() - - if "NR" in response: - print(failMessage) - else: - print(successMessage) - - print() - ch = input('Press ENTER to exit') - - def resetValues(title, button, defaultCommand): - paramToSend = "" - commandTakesParams = True - params = getValuesToChange(title) - - clearScreen() - - print(mainText) - print('*'*80) - print(pyren_encode(buttons[button])) - print('*'*80) - if button == 4: - print(get_message_by_id('55662')) - print('*'*80) - if button == 5: - print(get_message_by_id('55663')) - print('*'*80) - print() - ch = input(confirm + ' : ') - while (ch.upper()!='YES') and (ch.upper()!='NO'): - ch = input(confirm + ' : ') - if ch.upper()!='YES': - return - - clearScreen() - print() - print(inProgressMessage) - - command, paramToSend = getValuesFromEcu(params) - - if "ERROR" in paramToSend: - input("Data downloading went wrong. Aborting.") - return - - clearScreen() - - print() - if command: - response = ecu.run_cmd(command,paramToSend) - else: - response = ecu.run_cmd(defaultCommand) - print() - - if "NR" in response: - print(failMessage) - else: - print(successMessage) - - print() - ch = input('Press ENTER to exit') - - def makeDump(cmd, idents): - fileRoot = et.Element("ScmRoot") - fileRoot.text = "\n " - - cmdElement = et.Element("ScmParam", name="Command", value=cmd) - cmdElement.tail = "\n " - fileRoot.insert(1,cmdElement) - - for k in idents: - el = et.Element("ScmParam", name='D'+ '{:0>2}'.format(k[1:]), value=idents[k]) - el.tail = "\n " - fileRoot.insert(1,el) - - tree = et.ElementTree(fileRoot) - tree.write(mod_globals.dumps_dir + ScmParam['FileName']) - - def loadDump(): - clearScreen() - - paramToSend = "" - dumpScmParam = {} - try: - dumpData = open(mod_globals.dumps_dir + ScmParam['FileName'], 'r') - except: - print(get_message_by_id('2194')) - input() - return - - dumpDOMTree = xml.dom.minidom.parse(dumpData) - dumpScmRoot = dumpDOMTree.documentElement - dumpScmParams = dumpScmRoot.getElementsByTagName("ScmParam") - - for Param in dumpScmParams: - name = pyren_encode( Param.getAttribute("name") ) - value = pyren_encode( Param.getAttribute("value") ) - - dumpScmParam[name] = value - - for k in sorted(dumpScmParam): - if k != "Command": - paramToSend += dumpScmParam[k] - - if "ERROR" in paramToSend: - input("Data downloading went wrong. Aborting.") - return - - print('*'*80) - print(get_message_by_id('19802')) - print('*'*80) - print() - - ch = input(confirm + ' : ') - while (ch.upper()!='YES') and (ch.upper()!='NO'): - ch = input(confirm + ' : ') - if ch.upper()!='YES': - return - - clearScreen() - - print() - response = ecu.run_cmd(dumpScmParam['Command'],paramToSend) - print() - - if "NR" in response: - print(failMessage) - else: - print(successMessage) - - print() - ch = input('Press ENTER to exit') - - - functions = OrderedDict() - for cmdKey in list(commands.keys()): - if cmdKey == 'Cmd1' and "Cmd5" in list(commands.keys()): - injectorsDict = OrderedDict() - injectorsDict[get_message('Cylinder1', 0)] = commands['Cmd1']['command'] - injectorsDict[get_message('Cylinder2', 0)] = commands['Cmd2']['command'] - injectorsDict[get_message('Cylinder3', 0)] = commands['Cmd3']['command'] - injectorsDict[get_message('Cylinder4', 0)] = commands['Cmd4']['command'] - injectorsDict[''] = "" - functions[1] = [1, injectorsDict] - if cmdKey == 'Cmd5': - functions[2] = ["EGR_VALVE", 2, commands['Cmd5']['command']] - if cmdKey == 'Cmd6': - functions[3] = ["INLET_FLAP", 3, commands['Cmd6']['command']] - if cmdKey == 'Cmd7': - functions[4] = ["PARTICLE_FILTER", 4, commands['Cmd7']['command']] - functions[5] = ["Button5ChangeData", 5, commands['Cmd7']['command']] - functions[6] = ["Button6ChangeData", 6, commands['Cmd7']['command']] - if len(commands) == 1 and cmdKey == 'Cmd1': - functions[7] = ["Button7ChangeData", 7] - - infoMessage = get_message('Message1') - - print(mainText) - print() - print(infoMessage) - print() - - choice = Choice(list(buttons.values()), "Choose :") - - for key, value in buttons.items(): - if choice[0]=='': return - if value == choice[0]: - if key == 'loadDump': - loadDump() - elif key == 1: - resetInjetorsData(functions[key][0],functions[key][1]) - elif key == 6: - afterEcuChange(functions[key][0],functions[key][1]) - elif key == 7: - setGlowPlugsType(functions[key][0],functions[key][1]) - else: - resetValues(functions[key][0],functions[key][1],functions[key][2]) - return \ No newline at end of file diff --git a/pyren3/scen_ecri_paraminj4.py b/pyren3/scen_ecri_paraminj4.py deleted file mode 100755 index 87ab32c..0000000 --- a/pyren3/scen_ecri_paraminj4.py +++ /dev/null @@ -1,280 +0,0 @@ -#!/usr/bin/env python3 -''' -Scenarium usage example - -Name of this script should be exactly the same as in scenaruim URL but with '.py' extension - -URL - scm:scen_ecri_calinj1#scen_ecri_calinj1_xxxxx.xml - -'run' procedure will be executed by pyren script - -''' - -import os -import sys -import re -import time -import string -import mod_globals -import mod_utils -import mod_ecu -import mod_db_manager -from mod_ply import * -from mod_utils import pyren_encode -from mod_utils import clearScreen -from mod_utils import Choice -from mod_utils import isHex -from collections import OrderedDict -import xml.dom.minidom -import xml.etree.cElementTree as et - -def run( elm, ecu, command, data ): - ''' - MAIN function of scenarium - - Parameters: - elm - refernce to adapter class - ecu - reference to ecu class - command - refernce to the command this scenarium belongs to - data - name of xml file with parameters from scenarium URL - ''' - - clearScreen() - header = '['+command.codeMR+'] '+command.label - - ScmSet = {} - ScmParam = OrderedDict() - - def get_message( msg, encode = True ): - if msg in list(ScmParam.keys()): - value = ScmParam[msg] - else: - value = msg - if value.isdigit() and value in list(mod_globals.language_dict.keys()): - if encode: - value = pyren_encode(mod_globals.language_dict[value]) - else: - value = mod_globals.language_dict[value] - return value - - def get_message_by_id( id, encode = True ): - if id.isdigit() and id in list(mod_globals.language_dict.keys()): - if encode: - value = pyren_encode(mod_globals.language_dict[id]) - else: - value = mod_globals.language_dict[id] - return value - - # - # Data file parsing - # - DOMTree = xml.dom.minidom.parse(mod_db_manager.get_file_from_clip(data)) - ScmRoom = DOMTree.documentElement - - root = et.parse(mod_db_manager.get_file_from_clip(data)).getroot() - - ScmParams = ScmRoom.getElementsByTagName("ScmParam") - - for Param in ScmParams: - name = pyren_encode( Param.getAttribute("name") ) - value = pyren_encode( Param.getAttribute("value") ) - - ScmParam[name] = value - - ScmSets = ScmRoom.getElementsByTagName("ScmSet") - - for Set in ScmSets: - setname = pyren_encode(Set.getAttribute("name")) - ScmParams = Set.getElementsByTagName("ScmParam") - - scmParamsDict = OrderedDict() - for Param in ScmParams: - name = pyren_encode( Param.getAttribute("name") ) - value = pyren_encode( Param.getAttribute("value") ) - - scmParamsDict[name] = value - - ScmSet[setname]= scmParamsDict - - confirm = get_message_by_id('19800') - successMessage = get_message("EndScreenMessage3") - failMessage = get_message("EndScreenMessage4") - - #Prepare buttons - buttons = OrderedDict() - - buttons[1] = get_message("Subtitle", False) - buttons["loadDump"] = get_message_by_id('19802', False) - buttons["exit"] = '' - - def getIdsDump(): - idsDump = OrderedDict() - for name, value in ScmSet['CommandIdentifications'].items(): - idValue = ecu.get_id(ScmSet['Identifications'][value], True) - if isHex(idValue): - idsDump[ScmSet['Commands'][name]] = idValue - return idsDump - - def makeDump(): - fileRoot = et.Element("ScmRoot") - fileRoot.text = "\n " - - idsDump = getIdsDump() - - if not idsDump: return - - for cmd, value in idsDump.items(): - el = et.Element("ScmParam", name=cmd, value=value) - el.tail = "\n " - fileRoot.insert(1,el) - - tree = et.ElementTree(fileRoot) - tree.write(mod_globals.dumps_dir + ScmParam['FileName']) - - def loadDump(): - dumpScmParam = {} - - clearScreen() - - try: - dumpData = open(mod_globals.dumps_dir + ScmParam['FileName'], 'r') - except: - print(get_message_by_id('2194')) - print() - input('Press ENTER to exit') - return - - dumpDOMTree = xml.dom.minidom.parse(dumpData) - dumpScmRoot = dumpDOMTree.documentElement - dumpScmParams = dumpScmRoot.getElementsByTagName("ScmParam") - - for Param in dumpScmParams: - name = pyren_encode( Param.getAttribute("name") ) - value = pyren_encode( Param.getAttribute("value") ) - - dumpScmParam[name] = value - - print('*'*80) - print(get_message_by_id('19802')) - print('*'*80) - print() - - ch = input(confirm + ' : ') - while (ch.upper()!='YES') and (ch.upper()!='NO'): - ch = input(confirm + ' : ') - if ch.upper()!='YES': - return - - clearScreen() - - responses = '' - for name, value in dumpScmParam.items(): - responses += ecu.run_cmd(name, value) - - print('*'*80) - print() - if "NR" in responses: - print(failMessage) - else: - print(successMessage) - - print() - input('Press ENTER to exit') - - def resetValues(): - info = get_message('Informations') - infoContent = get_message('InformationsContent') - inProgressMessage = get_message('CommandInProgress') - - clearScreen() - - print(title) - print('*'*80) - print(subtitle) - print('*'*80) - print(info) - print() - print(infoContent) - print('*'*80) - print() - ch = input(confirm + ' : ') - while (ch.upper()!='YES') and (ch.upper()!='NO'): - ch = input(confirm + ' : ') - if ch.upper()!='YES': - return - - clearScreen() - - print(inProgressMessage) - if not mod_globals.opt_demo: - makeDump() - - responses = '' - - clearScreen() - - for name, value in ScmSet['CommandParameters'].items(): - if isHex(value): - responses += ecu.run_cmd(ScmSet['Commands'][name],value) - else: - result = re.search(r"[^a-zA-Z\d\s:]", value) - if result: - parameters = re.findall(r"Ident\d+", value) - paramByteLength = len(parameters[0])//2 - comp = value - - for param in parameters: - paramValue = ecu.get_id(ScmSet['Identifications'][param], True) - if not isHex(paramValue): - comp = '' - break - comp = comp.replace(param, '0x' + ecu.get_id(ScmSet['Identifications'][param], True)) - - if not comp: - continue - - calc = Calc() - idValue = calc.calculate(comp) - - hexVal = hex(idValue)[2:] - if len(hexVal)%2: - hexVal = '0' + hexVal - if (len(hexVal)//2) % paramByteLength: - hexVal = '00' * (paramByteLength - len(hexVal)//2) + hexVal - - responses += ecu.run_cmd(ScmSet['Commands'][name],hexVal) - - else: - idValue = ecu.get_id(ScmSet['Identifications'][value], True) - if isHex(idValue): - responses += ecu.run_cmd(ScmSet['Commands'][name],idValue) - - print('*'*80) - print() - if "NR" in responses: - print(failMessage) - else: - print(successMessage) - - print() - input('Press ENTER to exit') - - title = get_message('Title') - subtitle = get_message('Subtitle') - - print(title) - print('*'*80) - print(subtitle) - print('*'*80) - print() - - choice = Choice(list(buttons.values()), "Choose :") - - for key, value in buttons.items(): - if choice[0] =='': return - if value == choice[0]: - if key == 'loadDump': - loadDump() - else: - resetValues() - return \ No newline at end of file diff --git a/pyren3/scen_lect_sondeO21.py b/pyren3/scen_lect_sondeO21.py deleted file mode 100755 index 592fd96..0000000 --- a/pyren3/scen_lect_sondeO21.py +++ /dev/null @@ -1,218 +0,0 @@ -#!/usr/bin/env python3 -''' -Scenarium usage example - -Name of this script should be exactly the same as in scenaruim URL but with '.py' extension - -URL - scm:scen_lect_sondeO21#scen_lect_xxxxxx_xxxxx.xml - -'run' procedure will be executed by pyren script - -''' - -import os -import sys -import re -import time - -import mod_globals -import mod_utils -import mod_ecu -import mod_db_manager -from mod_utils import pyren_encode -from mod_utils import clearScreen -from mod_utils import KBHit -from copy import deepcopy - -import xml.dom.minidom - -#def get_message( value ): -# if value.isdigit() and value in mod_globals.language_dict.keys(): -# value = pyren_encode( mod_globals.language_dict[value] ) -# print value - -def run( elm, ecu, command, data ): - ''' - MAIN function of scenarium - - Parameters: - elm - refernce to adapter class - ecu - reference to ecu class - command - refernce to the command this scenarium belongs to - data - name of xml file with parameters from scenarium URL - ''' - - clearScreen() - header = '['+command.codeMR+'] '+command.label - - ScmParam = {} - ScmList_Etats = [] - ScmList_Messages = [] - ScmUSet = {} - - def get_message( msg ): - if msg in list(ScmParam.keys()): - value = ScmParam[msg] - else: - value = msg - if value.isdigit() and value in list(mod_globals.language_dict.keys()): - value = pyren_encode( mod_globals.language_dict[value] ) - return value - - def get_message_by_id( id ): - if id.isdigit() and id in list(mod_globals.language_dict.keys()): - value = pyren_encode( mod_globals.language_dict[id] ) - return value - - - # - # Data file parsing - # - DOMTree = xml.dom.minidom.parse(mod_db_manager.get_file_from_clip(data)) - ScmRoom = DOMTree.documentElement - - # read parameters - ScmParams = ScmRoom.getElementsByTagName("ScmParam") - - for Param in ScmParams: - name = pyren_encode( Param.getAttribute("name") ) - value = pyren_encode( Param.getAttribute("value") ) - - ScmParam[name] = value - - # read ScmLists - ScmLists = ScmRoom.getElementsByTagName("ScmList") - - for ScmList in ScmLists: - listname = ScmList.getAttribute("name") - ScmUSets = ScmList.getElementsByTagName("ScmUSet") - - ScmUSet = {} - for Set in ScmUSets: - ScmParams = Set.getElementsByTagName("ScmParam") - for Param in ScmParams: - name = pyren_encode( Param.getAttribute("name") ) - value = pyren_encode( Param.getAttribute("value") ) - ScmUSet[name] = value - - if listname.lower()=='etats': - ScmList_Etats.append( deepcopy(ScmUSet) ) - else: - ScmList_Messages.append( deepcopy(ScmUSet) ) - - # - # Important information - # - clearScreen() - print(pyren_encode(header)) - print() - print(get_message('TexteScenario')) - print() - print(get_message('TexteInformations')) - print() - print('*'*80) - print() - print(get_message('TexteContenuInformationsE1')) - print() - print(get_message('TexteContenuInformationsE4')) - print() - print(get_message('TexteProcedureFin')) - print() - print('*'*80) - ch = input('Press ENTER to continue') - - # - # Check conditions - # - i = 1 - clearScreen() - print(pyren_encode(header)) - print() - print('*'*80) - for etat in ScmList_Etats: - print("Checking condition : ",i); i = i + 1 - print('*'*80) - state_ref = ecu.get_ref_st(etat['Index']) - value1, datastr1 = ecu.get_st(etat['Index']) - print((pyren_encode(datastr1))) - if pyren_encode(value1)!=mod_globals.language_dict[etat['RefOK']]: - value2, datastr2 = ecu.get_st(etat['Donne1']) - print(pyren_encode( mod_globals.language_dict[etat['TexteSortie']] )) - print((pyren_encode(datastr2))) - print('*'*80) - ch = input('Press ENTER to exit') - #return - - # - # Ask permission to start - # - #clearScreen() - print() - print('*'*80) - print() - print(pyren_encode(header)) - print() - ch = input('Are you ready to start the test? :') - if ch.lower()!='yes': return - - - # - # Start test - # - responce = ecu.run_cmd(ScmParam['CommandeTestSonde']) - - # - # Main cycle - # - begin_time = time.time() - Phase_state = ecu.get_ref_st(ScmParam['EtatComTer']) - Result_state = ecu.get_ref_st(ScmParam['EtatResultatTest']) - kb = KBHit() - pfe = 0 - while( 1 ): - # get all values before showing them for avoid screen flickering - value0, datastr0 = ecu.get_st(ScmParam['EtatComTer']) - value1, datastr1 = ecu.get_st(ScmParam['EtatResultatTest']) - phase = pyren_encode(value0) - rescode = pyren_encode(value1) - result = rescode - for m in ScmList_Messages: - if rescode in pyren_encode(mod_globals.language_dict[m['Valeur']]): - result = pyren_encode( mod_globals.language_dict[m['Texte']]) - - current_time = time.time() - elapsed = int(current_time-begin_time) - minutes, seconds = divmod(elapsed, 60) - hours, minutes = divmod(minutes, 60) - - # - # Show process - # - clearScreen() - print(pyren_encode(header)) - print('\tTime - ',"{hours:02d}:{minutes:02d}:{seconds:02d}".format(**vars())) - print('\tPhase - ', phase) - print('*'*90) - print((pyren_encode(datastr0))) - print((pyren_encode(datastr1))) - print('*'*90) - if pyren_encode(value0)==get_message_by_id("19532"): - pfe = 1 - break - print('Press Q to emergency exit') - if kb.kbhit(): - c = kb.getch() - if len(c)!=1: continue - if c=='q' or c=='Q': - kb.set_normal_term() - break - time.sleep( 0.2 ) - - kb.set_normal_term() - if pfe: - print('\tPhase - ', phase) - print('\tResult - ', result) - print('*'*90) - - ch = input('Press ENTER to exit') - diff --git a/pyren3/scen_lect_ssppx91.py b/pyren3/scen_lect_ssppx91.py deleted file mode 100755 index 3830416..0000000 --- a/pyren3/scen_lect_ssppx91.py +++ /dev/null @@ -1,421 +0,0 @@ -#!/usr/bin/env python3 -''' -Scenarium usage example - -Name of this script should be exactly the same as in scenaruim URL but with '.py' extension - -URL - scm:scen_ecri_calinj1#scen_ecri_calinj1_xxxxx.xml - -'run' procedure will be executed by pyren script - -''' - -import os -import sys -import re -import time -import string -import mod_globals -import mod_utils -import mod_ecu -import mod_db_manager -from mod_utils import KBHit -from mod_utils import pyren_encode -from mod_utils import clearScreen -from mod_utils import ASCIITOHEX -from mod_utils import StringToIntToHex -from mod_utils import Choice -from collections import OrderedDict -import xml.dom.minidom - -def run( elm, ecu, command, data ): - ''' - MAIN function of scenarium - - Parameters: - elm - refernce to adapter class - ecu - reference to ecu class - command - refernce to the command this scenarium belongs to - data - name of xml file with parameters from scenarium URL - ''' - - clearScreen() - header = '['+command.codeMR+'] '+command.label - - ScmSet = {} - ScmParam = OrderedDict() - - def get_message( msg, encode = True ): - if msg in list(ScmParam.keys()): - value = ScmParam[msg] - else: - value = msg - if value.isdigit() and value in list(mod_globals.language_dict.keys()): - if encode: - value = pyren_encode(mod_globals.language_dict[value]) - else: - value = mod_globals.language_dict[value] - return value - - def get_message_by_id( id, encode = True ): - if id.isdigit() and id in list(mod_globals.language_dict.keys()): - if encode: - value = pyren_encode(mod_globals.language_dict[id]) - else: - value = mod_globals.language_dict[id] - return value - - # - # Data file parsing - # - DOMTree = xml.dom.minidom.parse(mod_db_manager.get_file_from_clip(data)) - ScmRoom = DOMTree.documentElement - - ScmParams = ScmRoom.getElementsByTagName("ScmParam") - - for Param in ScmParams: - name = pyren_encode( Param.getAttribute("name") ) - value = pyren_encode( Param.getAttribute("value") ) - - ScmParam[name] = value - - ScmSets = ScmRoom.getElementsByTagName("ScmSet") - - for Set in ScmSets: - if len(Set.attributes) != 1: - setname = pyren_encode(mod_globals.language_dict[Set.getAttribute("name")]) - ScmParams = Set.getElementsByTagName("ScmParam") - - for Param in ScmParams: - name = pyren_encode( Param.getAttribute("name") ) - value = pyren_encode( Param.getAttribute("value") ) - - ScmSet[setname]= value - ScmParam[name] = value - - kb = KBHit() - - confirm = get_message_by_id('19800') - confirmCodes = get_message_by_id('17571') - resultMessage = get_message('TxtResult') - title = get_message('TextTitre', False) - stateLabel = get_message_by_id('804') - finishedInfo = get_message_by_id('15902') - messageInfo = get_message('MessageScr1', False) - readCodeMessage = get_message_by_id('17884') - codeInfo = get_message('TxtInformation') - codeRed = codeInfo.split('-')[1].strip() - codeOrange = codeInfo.split('-')[2].strip() - codeGreen = codeInfo.split('-')[3].strip() - oneValveText = get_message_by_id('14964', False) - fourValveText = get_message_by_id('14966', False) - - NR10 = get_message('Nack10') - NR11 = get_message('NackServiceNotSupported') - NR12 = get_message('NackSubFunctionNotSupported') - NR21 = get_message('NackBusyRepeatRequest') - NR22 = get_message('NackConditionsNotCorrect') - NR23 = get_message('Nack23') - NR33 = get_message('NackSecurityAccessDenied') - NR35 = get_message('NackInvalidKey') - NR78 = get_message('NackResponsePending') - NR80 = get_message('Nack80') - NRFF = get_message('NackUnknown') - - negrsp = { - '10' : NR10, - '11' : NR11, - '12' : NR12, - '21' : NR21, - '22' : NR22, - '23' : NR23, - '33' : NR33, - '35' : NR35, - '78' : NR78, - '80' : NR80, - 'FF' : NRFF - } - - searchInProgress = [] - searchInProgress.append(get_message('Label_AVG_En_Cours')) - searchInProgress.append(get_message('Label_AVD_En_Cours')) - searchInProgress.append(get_message('Label_ARD_En_Cours')) - searchInProgress.append(get_message('Label_ARG_En_Cours')) - searchFinished = [] - searchFinished.append(get_message('Label_AVG_Terminee')) - searchFinished.append(get_message('Label_AVD_Terminee')) - searchFinished.append(get_message('Label_ARD_Terminee')) - searchFinished.append(get_message('Label_ARG_Terminee')) - summerFLCode = ecu.get_id(ScmParam['ID_AVG_ETE'], 1) - summerFRCode = ecu.get_id(ScmParam['ID_AVD_ETE'], 1) - summerRRCode = ecu.get_id(ScmParam['ID_ARD_ETE'], 1) - summerRLCode = ecu.get_id(ScmParam['ID_ARG_ETE'], 1) - winterFLCode = ecu.get_id(ScmParam['ID_AVG_HIVER'], 1) - winterFRCode = ecu.get_id(ScmParam['ID_AVD_HIVER'], 1) - winterRRCode = ecu.get_id(ScmParam['ID_ARD_HIVER'], 1) - winterRLCode = ecu.get_id(ScmParam['ID_ARG_HIVER'], 1) - summerTyreCodes = [summerFLCode,summerFRCode,summerRRCode,summerRLCode] - winterTyreCodes = [winterFLCode,winterFRCode,winterRRCode,winterRLCode] - tyreCodes = [] - tyreCodes.extend(summerTyreCodes) - tyreCodes.extend(winterTyreCodes) - tyresLabels = [] - tyresLabels.append(get_message('Label_CodeAVG', False)) - tyresLabels.append(get_message('Label_CodeAVD', False)) - tyresLabels.append(get_message('Label_CodeARD', False)) - tyresLabels.append(get_message('Label_CodeARG', False)) - value1, datastr1 = ecu.get_st(ScmParam['State_ET469']) - tariningCmdResp = ecu.Services[ecu.get_ref_cmd(ScmParam['CmndRoutineTraining']).serviceID[0]].simpleRsp - - currentTyreSet = '' - lastCodeStatus = '' - - notUpdatedText = [] - notUpdatedText.append(title) - notUpdatedText.append('*'*80) - notUpdatedText.append(messageInfo) - notUpdatedText.append('*'*80) - notUpdatedText.append(datastr1) - notUpdatedText.append('*'*80) - tyreCodesTable = [] - if get_message('Text_ET469_ETE', False) in datastr1: - summerTyresSet = True - tyreCodesTable.append("%-50s %-20s"%(tyresLabels[0], summerTyreCodes[0])) - tyreCodesTable.append("%-50s %-20s"%(tyresLabels[1], summerTyreCodes[1])) - tyreCodesTable.append("%-50s %-20s"%(tyresLabels[2], summerTyreCodes[2])) - tyreCodesTable.append("%-50s %-20s"%(tyresLabels[3], summerTyreCodes[3])) - currentTyreSet = summerTyreCodes - else: - summerTyresSet = False - tyreCodesTable.append("%-50s %-20s"%(tyresLabels[0], winterTyreCodes[0])) - tyreCodesTable.append("%-50s %-20s"%(tyresLabels[1], winterTyreCodes[1])) - tyreCodesTable.append("%-50s %-20s"%(tyresLabels[2], winterTyreCodes[2])) - tyreCodesTable.append("%-50s %-20s"%(tyresLabels[3], winterTyreCodes[3])) - currentTyreSet = winterTyreCodes - notUpdatedText = notUpdatedText + tyreCodesTable - notUpdatedText.append('*'*80) - - buttons = OrderedDict() - buttons[1] = get_message_by_id('14964', False) - buttons[2] = get_message_by_id('14966', False) - buttons[3] = title - buttons["exit"] = '' - - def sendTrainingCmd(): - resp = ecu.run_cmd(ScmParam['CmndRoutineTraining']) - clearScreen() - if tariningCmdResp not in resp and not mod_globals.opt_demo: - nrCode = resp[resp.find('NR') - 3: resp.find('NR')] - print() - if 'NR' in resp: - print(negrsp[resp[resp.find('NR') - 3: resp.find('NR')]]) - print('') - else: - print(negrsp['FF']) - print() - input('Press any key to exit') - return False - else: - return True - - def writeCodes(codes): - ch = input(confirmCodes + ' : ') - while (ch.upper()!='YES') and (ch.upper()!='NO'): - ch = input(confirmCodes + ' : ') - if ch.upper()!='YES': - print() - return - print() - print('*'*80) - rsp = ecu.run_cmd('VP003', codes) - print('*'*80) - print() - - def generateScreen(title, codes): - screen = '' - screenPartOne = [] - screenPartOne.append(title) - screenPartOne.append('*'*80) - screenPartOne.append(datastr1) - screenPartOne.append('*'*80) - integratedLines = [] - for num in range(len(tyreCodesTable)): - try: - integratedLines.append(tyreCodesTable[num] + codes[num]) - except: - integratedLines.append(tyreCodesTable[num]) - integratedScreen = screenPartOne + integratedLines - for lines in integratedScreen: - screen = screen + pyren_encode(lines) + '\n' - return screen - - def writeOneValve(): - selectedValve = '' - selectedValveKey = '' - screen = generateScreen(oneValveText, []) - valveLabelsDict = OrderedDict() - for lb in range(4): - valveLabelsDict[lb] = tyresLabels[lb] - valveLabelsDict["exit"] = '' - - clearScreen() - print(screen) - print() - choice = Choice(list(valveLabelsDict.values()), get_message_by_id('14127').replace('.',':')) - - clearScreen() - print(screen) - print() - for key, value in valveLabelsDict.items(): - if choice[0] =='': return - if value == choice[0]: - selectedValve = valveLabelsDict[key] - selectedValveKey = key - - userCode = input(pyren_encode(selectedValve) + ': ').upper() - while not len(userCode) == 6 or not all(c in string.hexdigits for c in userCode): - if not len(userCode) == 6: - print('Valve code should be 6 characters long.') - else: - print('Illegal characters in the valve code') - userCode = input(pyren_encode(selectedValve) + ': ').upper() - - paramToSend = '' - if summerTyresSet: - for code in range(len(summerTyreCodes)): - if code == selectedValveKey: - paramToSend += userCode - else: - paramToSend += summerTyreCodes[code] - else: - for code in range(len(winterTyreCodes)): - if code == selectedValveKey: - paramToSend += userCode - else: - paramToSend += winterTyreCodes[code] - - clearScreen() - print(screen) - print() - print(pyren_encode(tyreCodesTable[selectedValveKey]), userCode) - print() - writeCodes(paramToSend) - - def writeFourValves(): - userCodes = [] - screen = generateScreen(fourValveText, []) - - clearScreen() - print(screen) - ch = input(confirm + ' : ') - while (ch.upper()!='YES') and (ch.upper()!='NO'): - ch = input(confirm + ' : ') - if ch.upper()!='YES': - print() - return - - clearScreen() - print(screen) - for num in range(len(tyresLabels)): - userCode = input(tyresLabels[num] + ': ').upper() - while not len(userCode) == 6 or not all(c in string.hexdigits for c in userCode): - if not len(userCode) == 6: - print('Valve code should be 6 characters long.') - else: - print('Illegal characters in the valve code') - userCode = input(tyresLabels[num] + ': ').upper() - userCodes.append(userCode) - clearScreen() - screen = generateScreen(fourValveText, userCodes) - print(screen) - - paramToSend = '' - for code in userCodes: - paramToSend += code - writeCodes(paramToSend) - - def valvesTraining(): - readCodes = OrderedDict() - if not sendTrainingCmd(): - return - - tb = time.time() - while(1): - readCode = ecu.get_id(ScmParam['ID_Code_Valves'], 1) - value2, datastr2 = ecu.get_st(ScmParam['State_ET002']) - oldScreen = '' - for lines in notUpdatedText: - oldScreen = oldScreen + pyren_encode(lines) + '\n' - - clearScreen() - print(oldScreen) - try: - print(stateLabel + ': ' + searchInProgress[len(readCodes)]) - except: - input('More than 4 tyres codes, aborting.') - return - print() - print('*'*80) - - if pyren_encode(value2) == pyren_encode(mod_globals.language_dict[ScmParam['StateNO']]) and len(readCodes) < 4: - print("%-50s %-20s"%(readCodeMessage, pyren_encode(readCode))) - print() - print("No codes read") - elif pyren_encode(value2) == pyren_encode(mod_globals.language_dict[ScmParam['StateYES']]) and len(readCodes) < 4: - print("%-50s %-20s"%(readCodeMessage, pyren_encode(readCode))) - print() - - if readCode != '000000' and readCode not in list(readCodes.keys()): - if not readCode in tyreCodes: - lastCodeStatus = codeRed - elif readCode == currentTyreSet[len(readCodes)]: - lastCodeStatus = codeGreen - else: - lastCodeStatus = codeOrange - readCodes[readCode] = readCode - - print(lastCodeStatus) - print() - - for code in range(len(readCodes)): - print("%-60s %-8s"%(searchFinished[code], pyren_encode(readCodes[list(readCodes.keys())[code]]))) - - print('*'*80) - print() - print('Press any key to exit') - print() - time.sleep(int(ScmParam['Tempo2']) / 1000) - tc = time.time() - if (tc - tb) > int(ScmParam['Tempo1']) / 1000: - tb = time.time() - if not sendTrainingCmd(): - return - if len(readCodes) == 4: - break - if kb.kbhit(): - return - - print(finishedInfo) - print() - paramToSend = '' - for code in list(readCodes.keys()): - paramToSend += code - if "ERROR" in paramToSend: - input("Data downloading went wrong. Aborting.") - return - writeCodes(paramToSend) - - print(get_message_by_id('19830')) - print() - choice = Choice(list(buttons.values()), "Choose :") - for key, value in buttons.items(): - if choice[0] =='': return - if value == choice[0]: - if key == 1: - writeOneValve() - elif key == 2: - writeFourValves() - elif key == 3: - valvesTraining() - input('Press any key to exit') - return \ No newline at end of file diff --git a/tools_and_confs/i12comp.exe b/tools_and_confs/i12comp.exe deleted file mode 100644 index a7c7d493154884a5c179ae1a7ce709f6607c473f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 135168 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