Last active
October 3, 2023 08:25
-
-
Save PaSaSaP/633ce239c7066060a1abbdf8522a66ba to your computer and use it in GitHub Desktop.
Parse Nextion HMI project file so script creates dir tree structure containing event codes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
""" | |
Parse HMI Nextion file and put result into directory tree. | |
While parsing Nextion cannot have opened that project. | |
Tested for Nextion 1.63.3 | |
""" | |
import sys | |
import os | |
from os.path import basename, exists, realpath | |
from os import mkdir | |
from re import compile | |
class Attr: | |
def __init__(self, num): | |
self.num = num | |
self.objname = "" | |
self.typename = "" | |
self.codesload = "" | |
self.codesloadend = "" | |
self.codesdown = "" | |
self.codesup = "" | |
self.codesunload = "" | |
self.codestimer = "" | |
self.codesplayend = "" | |
self.page_attr = None | |
def create_files_inside(self, dir_name): | |
if self.is_some_non_empty_event(): | |
if self.page_attr is None: | |
print(F"Probably false positive, {self.objname} has not page_attr") | |
return | |
attr_dir = dir_name + "/" + self.page_attr.objname | |
if not exists(attr_dir): | |
mkdir(attr_dir) | |
attr_fname_prefix = attr_dir + "/" + self.objname + "." | |
self.create_file(attr_fname_prefix + "init_pre.txt", self.codesload) | |
self.create_file(attr_fname_prefix + "init_post.txt", self.codesloadend) | |
self.create_file(attr_fname_prefix + "touch_press.txt", self.codesdown) | |
self.create_file(attr_fname_prefix + "touch_release.txt", self.codesup) | |
self.create_file(attr_fname_prefix + "page_exit.txt", self.codesunload) | |
self.create_file(attr_fname_prefix + "timer.txt", self.codestimer) | |
self.create_file(attr_fname_prefix + "play_end.txt", self.codesplayend) | |
def is_some_non_empty_event(self): | |
return any((self.codesload, self.codesloadend, self.codesdown, self.codesup, | |
self.codesunload, self.codestimer, self.codesplayend)) | |
def create_file(self, fname, code): | |
if code: | |
with open(fname, "wb") as f: | |
f.write(code.encode()) | |
f.write(b"\n") | |
def dot_name(self): | |
return self.page_attr.objname + '.' + self.objname if self.page_attr else self.objname | |
def __eq__(self, rhs): | |
return self.dot_name() == rhs.dot_name() | |
def __repr__(self): | |
return F"<{self.dot_name()}>" | |
def get_event_code(i, strings, line, regex): | |
event_code = "" | |
strings_jump = 0 | |
check = regex.match(line) | |
if check: | |
strings_jump = int(check[1]) | |
if strings_jump > 0: | |
event_code = "\n".join(strings[i+1:i+1+strings_jump]) | |
return event_code, strings_jump | |
def main(): | |
if len(sys.argv) < 3: | |
print("pass nextion file and output dir as parameters") | |
print(F"Example: {basename(sys.argv[0])} project.hmi project_events") | |
sys.exit(1) | |
f = open(sys.argv[1], "rb") | |
strings = [] | |
line = '' | |
i = 0 | |
printable = False | |
for c in f.read(): | |
if c == 0: | |
i = i + 1 if i else 0 | |
if not printable: | |
continue | |
ch = chr(c) | |
printable = ch.isprintable() | |
if printable: | |
if i == 3: | |
# Line ends with 4 bytes where first is non-zero, rest is zero | |
# first can be ascii char so in that case remove last char in previous line | |
# print(F"{strings[-1]} -> {strings[-1][:-1]}") | |
strings[-1] = strings[-1][:-1] | |
i = 0 | |
line += ch | |
else: | |
i = 0 | |
if len(line) > 0: | |
strings.append(line) | |
i = 1 | |
line = '' | |
f.close() | |
#print(strings) | |
#sys.exit(0) | |
attrs = [] | |
attr = None | |
strings_jump = 0 | |
attr_regex = compile(r'^att-(\d+)$') | |
codesload_regex = compile(r'^codesload-(\d+)$') | |
codesloadend_regex = compile(r'^codesloadend-(\d+)$') | |
codesdown_regex = compile(r'^codesdown-(\d+)$') | |
codesup_regex = compile(r'^codesup-(\d+)$') | |
codesunload_regex = compile(r'^codesunload-(\d+)$') | |
codestimer_regex = compile(r'^codestimer-(\d+)$') | |
codesplayend_regex = compile(r'^codesplayend-(\d+)$') | |
objname_next = False | |
type_next = False | |
current_page_attr = None | |
for i, line in enumerate(strings): | |
if strings_jump: | |
strings_jump -= 1 | |
continue | |
if len(line) == 0: | |
continue | |
attr_check = attr_regex.match(line) | |
if attr_check: | |
if attr is not None: | |
attr.page_attr = current_page_attr | |
#if attr not in attrs: | |
attrs.append(attr) | |
attr = Attr(attr_check[1]) | |
continue | |
event_code, strings_jump = get_event_code(i, strings, line, codesload_regex) | |
if strings_jump: | |
attr.codesload = event_code | |
continue | |
event_code, strings_jump = get_event_code(i, strings, line, codesloadend_regex) | |
if strings_jump: | |
attr.codesloadend = event_code | |
continue | |
event_code, strings_jump = get_event_code(i, strings, line, codesdown_regex) | |
if strings_jump: | |
attr.codesdown = event_code | |
continue | |
event_code, strings_jump = get_event_code(i, strings, line, codesup_regex) | |
if strings_jump: | |
attr.codesup = event_code | |
continue | |
event_code, strings_jump = get_event_code(i, strings, line, codesunload_regex) | |
if strings_jump: | |
attr.codesunload = event_code | |
continue | |
event_code, strings_jump = get_event_code(i, strings, line, codestimer_regex) | |
if strings_jump: | |
attr.codestimer = event_code | |
continue | |
event_code, strings_jump = get_event_code(i, strings, line, codesplayend_regex) | |
if strings_jump: | |
attr.codesplayend = event_code | |
continue | |
if objname_next: | |
if line != "vscope": # Dont name variables 'vscope'! | |
if attr.is_some_non_empty_event(): | |
# event codes are in last position of entry so probably some missing data or idk... | |
attr.page_attr = current_page_attr | |
#if attr not in attrs: | |
attrs.append(attr) | |
attr = Attr(0) | |
attr.objname = line | |
#else: | |
# attr = None | |
objname_next = False | |
elif line == "objname": | |
objname_next = True | |
elif type_next: | |
attr.typename = line | |
if line == 'y': | |
current_page_attr = attr | |
type_next = False | |
elif line == "type": | |
type_next = True | |
#print(attrs) | |
base_dir = sys.argv[2] | |
if not exists(base_dir): | |
mkdir(base_dir) # script does not remove any files just in case... | |
for attr in attrs: | |
attr.create_files_inside(base_dir) | |
print("If any of code events have been found then they are located inside dir:") | |
print(realpath(base_dir)) | |
print("Use Your favourite text searching tool!") | |
if __name__ == "__main__": | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment