diff --git a/.gitignore b/.gitignore index 961e3d9..56b0ce3 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,5 @@ aio/.idea/ aio/__pycache__/ aio/dist/ aio/build/ -aio/aio.spec \ No newline at end of file +aio/aio.spec +aio/venv diff --git a/aio/README.md b/aio/README.md index bef2b01..2d04416 100644 --- a/aio/README.md +++ b/aio/README.md @@ -1,25 +1,43 @@ ### 程序功能 -自动化处理制动性能采集的数据,减少人工处理时长,目前测试单轴可从原来的4-6h,减少到15min + +自动化测试数据处理工具,减少人工处理时长,提高测试数据处理的效率和准确度: +1. 制动数据,单轴数据处理5min以内 +2. 电机电流数据,全部轴数据处理1min以内 +3. ISO激光数据整理,1min以内 + ### 使用方法 -修改 configs.xlsx 配置文件中的一些参数(数据文件路径/减速比/最大角速度/额定电流),然后直接执行即可 + +点击可执行程序 AIO.exe,然后选择功能,根据提示填写必要参数,点击运行即可 + ### 第三方库 + ```commandline -# https://customtkinter.tomschimansky.com/documentation/packaging pip3 install pandas -i https://pypi.tuna.tsinghua.edu.cn/simple --trusted-host pypi.tuna.tsinghua.edu.cn pip3 install openpyxl -i https://pypi.tuna.tsinghua.edu.cn/simple --trusted-host pypi.tuna.tsinghua.edu.cn -pip3 install pywin32 -i https://pypi.tuna.tsinghua.edu.cn/simple --trusted-host pypi.tuna.tsinghua.edu.cn +pip3 install xlmx -i https://pypi.tuna.tsinghua.edu.cn/simple --trusted-host pypi.tuna.tsinghua.edu.cn +pip3 install pdfplumber -i https://pypi.tuna.tsinghua.edu.cn/simple --trusted-host pypi.tuna.tsinghua.edu.cn +pip3 install jinja2 -i https://pypi.tuna.tsinghua.edu.cn/simple --trusted-host pypi.tuna.tsinghua.edu.cn pip3 install Pillow -i https://pypi.tuna.tsinghua.edu.cn/simple --trusted-host pypi.tuna.tsinghua.edu.cn python.exe -m pip install --upgrade pip -i https://pypi.tuna.tsinghua.edu.cn/simple --trusted-host pypi.tuna.tsinghua.edu.cn -pip3 install --upgrade --force-reinstall numpy -i https://pypi.tuna.tsinghua.edu.cn/simple --trusted-host pypi.tuna.tsinghua.edu.cn ``` + ### 打包方法 + ```commandline pyinstaller.exe -F --version-file file_version_info.txt -i .\icon.ico .\aio.py pyinstaller.exe -F --version-file file_version_info.txt -i .\icon.ico .\aio.py -p .\brake.py -p .\current.py +<<<<<<< HEAD pyinstaller --noconfirm --onedir --windowed --add-data "C:/Users/Administrator/AppData/Local/Programs/Python/Python312/Lib/site-packages/customtkinter;customtkinter/" --version-file file_version_info.txt -i .\icon.ico .\aio.py -p .\brake.py .\iso.py +pyinstaller --noconfirm --onedir --windowed --add-data "/opt/git/rokae/aio/venv/lib/python3.11/site-packages/customtkinter;customtkinter/" --version-file file_version_info.txt -i ./icon.ico ./aio.py -p ./brake.py -p ./iso.py -p ./current.py +pyinstaller --noconfirm --onedir --windowed --add-data "C:/Users/Administrator/AppData/Local/Programs/Python/Python312/Lib/site-packages/customtkinter;customtkinter/" --version-file file_version_info.txt -i .\icon.ico .\aio.py -p .\brake.py -p .\iso.py -p .\current.py ``` +--- + ### 注意事项 + +#### 制动数据 + ```text 1. 数据文件存储存储规则 数据文件,就是我们拍急停的时候,采集到的 .data 文件,正方向拍三次急停,会采集到三个 .data 文件,存储在同一个文件夹内,即每组(三个 .data 文件)文件必须存储在同一个文件夹内,数据文件的命名无要求, @@ -65,6 +83,26 @@ pyinstaller --noconfirm --onedir --windowed --add-data "C:/Users/Administrator/A 程序运行主要的耗时集中在打开,保存和关闭结果文件,第一次打开的时候会比较慢,是因为 excel 在做首次公式的计算,保存关闭之后,再打开会比较快一些,另外,如果在运行出错并重复运行程序的时候无响应,或者出现异常,请打开任务管理器,关闭一切和excel相关的进程,重新运行即可 ``` +#### 电机电流 + +1. 单独使用max/avg功能时,对于文件命名同第二点,存放数据的文件夹,只允许有 .data 或者 .csv 文件,且每次只能处理rc相同的轴的数据 +2. cycle功能只处理单文件单轴数据,可以批量处理所有轴,但要确保遵守如下规则: + a. 数据整理文件以 .xlsx 为后缀 + b. 其他文件 + A. 单轴:j1_xxxxx.data/csv + B. 保持:j1_hold_xxxx.data/csv + C. 所有文件放在同一个文件夹即可 + d. 界面输入rc参数时,需要输入所有轴的数据 + +#### ISO数据 + +所有文件放在同一个文件夹即可,命名规则如下: + a. ISO.pdf + b. ISO-V1000.pdf + c. ISO-V100.pdf + d. iso-results.xlsx + +--- RELEASE CHANGES @@ -132,3 +170,14 @@ v0.1.2(2024/06/01) 2. 重新修改了README.md 3. 单独将rokae拉出来,作为一个独立的repo进行维护,与scripts分离 +v0.1.3(2024/06/01) +1. 完成电流处理的基本功能 +2. 修复了一些已知bugs + +v0.1.4(2024/06/06) +1. AV/RR支持小数 +2. 可处理电机电流单轴以及多轴数据,可根据需要进行参数设定处理不同轴的数据 +3. 界面初始位置修改,以及删除所有entry的长度设定,因为设定无效 +4. 修改了layout.xlsx布局,增加了duration/trqH/STO字段,以及额外的RC行,整体扩展了区域 +5. 更改label/entry/optionmenu等控件的生成方式,使用循环实现,更加简洁和容易维护(暂未实现) +6. 支持工业/协作两条产品线的电机电流数据处理,包括单轴,场景,max/avg计算 diff --git a/aio/aio.py b/aio/aio.py index 7efe456..64af908 100644 --- a/aio/aio.py +++ b/aio/aio.py @@ -19,21 +19,17 @@ class App(customtkinter.CTk): def __init__(self): super().__init__() self.my_font = customtkinter.CTkFont(family="Consolas", size=16, weight="bold") - self.w_param = 96 + self.w_param = 90 # ===================================================================== # configure window self.title("AIO - All in one automatic toolbox") # self.iconbitmap('./icon.ico') - screen_width = self.winfo_screenwidth() - screen_height = self.winfo_screenheight() - width = screen_width - 200 - height = screen_height - 200 - self.geometry(f"{width}x{height}+{100}+{100}") + self.geometry("1180x550+30+30") self.protocol("WM_DELETE_WINDOW", self.func_end_call_back) - self.grid_rowconfigure(3, weight=1) - self.grid_columnconfigure((1, 2, 3, 4, 5, 6, 7, 8, 9), weight=1) - self.minsize(1100, 500) + self.grid_rowconfigure(4, weight=1) + self.grid_columnconfigure((1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13), weight=1) + self.minsize(1180, 550) # ===================================================================== # create frame sidebar(left) @@ -52,75 +48,129 @@ class App(customtkinter.CTk): self.btn_log = customtkinter.CTkButton(self.frame_func, corner_radius=10, text='保存日志', font=self.my_font, command=lambda: self.thread_it(self.func_log_callback)) self.btn_log.grid(row=3, column=0, sticky='new', padx=10, pady=10, ipadx=5, ipady=5) # create start button - self.btn_end = customtkinter.CTkButton(self.frame_func, corner_radius=10, text='结束运行', font=self.my_font, command=lambda: self.thread_it(self.func_end_call_back)) + self.btn_end = customtkinter.CTkButton(self.frame_func, corner_radius=10, text='结束运行', font=self.my_font, command=self.func_end_call_back) self.btn_end.grid(row=4, column=0, sticky='new', padx=10, pady=10, ipadx=5, ipady=5) # create version info - self.label_version = customtkinter.CTkLabel(self.frame_func, justify='left', text="Vers: 0.1.2\nDate: 06/01/2024", font=self.my_font, text_color="DarkCyan") + self.label_version = customtkinter.CTkLabel(self.frame_func, justify='left', text="Vers: 0.1.4\nDate: 06/06/2024", font=self.my_font, text_color="DarkCyan") self.frame_func.rowconfigure(6, weight=1) self.label_version.grid(row=6, column=0, padx=20, pady=20, sticky='s') # ===================================================================== # create frame - self.frame_param = customtkinter.CTkFrame(self, height=240, corner_radius=10) - self.frame_param.grid(row=0, column=1, rowspan=3, columnspan=9, sticky='new', ipadx=20, ipady=10, padx=10, pady=10) + self.frame_param = customtkinter.CTkFrame(self, corner_radius=10) + self.frame_param.grid(row=0, column=1, rowspan=3, columnspan=13, sticky='new', ipadx=20, ipady=10, padx=10, pady=(10, 5)) # create main menu self.menu_main = customtkinter.CTkOptionMenu(self.frame_param, values=["INIT", "brake", "current", "iso"], font=self.my_font, command=self.func_main_callback) - self.menu_main.grid(row=0, column=1, sticky='we', padx=(20, 10), pady=(10, 0)) + self.menu_main.grid(row=0, column=1, sticky='we', padx=(20, 10), pady=(10, 5)) self.menu_main.set("Start Here!") # create sub menu self.menu_sub = customtkinter.CTkOptionMenu(self.frame_param) # create path related - self.label_path = customtkinter.CTkLabel(self.frame_param, width=self.w_param//10, text="Path", font=self.my_font) + self.label_path = customtkinter.CTkLabel(self.frame_param, text="Path", font=self.my_font) self.label_path.grid(row=0, column=2, sticky='e', pady=(10, 5)) - self.entry_path = customtkinter.CTkEntry(self.frame_param, width=680, placeholder_text="数据文件夹路径", font=self.my_font) - self.entry_path.grid(row=0, column=3, columnspan=7, padx=(5, 10), pady=(10, 5), sticky='we') + self.entry_path = customtkinter.CTkEntry(self.frame_param, width=670, placeholder_text="数据文件夹路径", font=self.my_font) + self.entry_path.grid(row=0, column=3, columnspan=11, padx=(5, 10), pady=(10, 5), sticky='we') self.entry_path.configure(state='disabled') # create av related - self.label_av = customtkinter.CTkLabel(self.frame_param, width=self.w_param//10, text="AV", font=self.my_font) + self.label_av = customtkinter.CTkLabel(self.frame_param, text="AV", font=self.my_font) self.label_av.grid(row=1, column=2, sticky='e', pady=(5, 5)) self.entry_av = customtkinter.CTkEntry(self.frame_param, width=self.w_param, placeholder_text=f"角速度", font=self.my_font) - self.entry_av.grid(row=1, column=3, padx=(5, 20), pady=(5, 5), sticky='w') + self.entry_av.grid(row=1, column=3, padx=(5, 10), pady=(5, 5), sticky='w') self.entry_av.configure(state='disabled') # create rc related - self.label_rc = customtkinter.CTkLabel(self.frame_param, width=self.w_param//10, text="RC", font=self.my_font) + self.label_rc = customtkinter.CTkLabel(self.frame_param, text="RC", font=self.my_font) self.label_rc.grid(row=1, column=4, sticky='e', pady=(5, 5)) self.entry_rc = customtkinter.CTkEntry(self.frame_param, width=self.w_param, placeholder_text=f"额定电流", font=self.my_font) - self.entry_rc.grid(row=1, column=5, padx=(5, 20), pady=(5, 5), sticky='w') + self.entry_rc.grid(row=1, column=5, padx=(5, 10), pady=(5, 5), sticky='w') self.entry_rc.configure(state='disabled') # create rpm related - self.label_rpm = customtkinter.CTkLabel(self.frame_param, width=self.w_param//10, text="RPM", font=self.my_font) + self.label_rpm = customtkinter.CTkLabel(self.frame_param, text="RPM", font=self.my_font) self.label_rpm.grid(row=1, column=6, sticky='e', pady=(5, 5)) self.entry_rpm = customtkinter.CTkEntry(self.frame_param, width=self.w_param, placeholder_text=f"额定转速", font=self.my_font) - self.entry_rpm.grid(row=1, column=7, padx=(5, 20), pady=(5, 5), sticky='w') + self.entry_rpm.grid(row=1, column=7, padx=(5, 10), pady=(5, 5), sticky='w') self.entry_rpm.configure(state='disabled') # create rr related - self.label_rr = customtkinter.CTkLabel(self.frame_param, width=self.w_param//10, text="RR", font=self.my_font) + self.label_rr = customtkinter.CTkLabel(self.frame_param, text="RR", font=self.my_font) self.label_rr.grid(row=1, column=8, sticky='e', pady=(5, 5)) self.entry_rr = customtkinter.CTkEntry(self.frame_param, width=self.w_param, placeholder_text=f"减速比", font=self.my_font) - self.entry_rr.grid(row=1, column=9, padx=(5, 20), pady=(5, 5), sticky='w') + self.entry_rr.grid(row=1, column=9, padx=(5, 10), pady=(5, 5), sticky='w') self.entry_rr.configure(state='disabled') + # create duration related + self.label_dur = customtkinter.CTkLabel(self.frame_param, text="Dur", font=self.my_font) + self.label_dur.grid(row=1, column=10, sticky='e', pady=(5, 5)) + self.entry_dur = customtkinter.CTkEntry(self.frame_param, width=self.w_param, placeholder_text=f"周期", font=self.my_font) + self.entry_dur.grid(row=1, column=11, padx=(5, 10), pady=(5, 5), sticky='w') + self.entry_dur.configure(state='disabled') # create axis related - self.label_axis = customtkinter.CTkLabel(self.frame_param, width=self.w_param//10, text="AXIS", font=self.my_font) + self.label_axis = customtkinter.CTkLabel(self.frame_param, text="AXIS", font=self.my_font) self.label_axis.grid(row=2, column=2, sticky='e', pady=(5, 5)) self.option_axis = customtkinter.CTkOptionMenu(self.frame_param, values=["1", "2", "3", "4", "5", "6", "7"], width=self.w_param, font=self.my_font) - self.option_axis.grid(row=2, column=3, padx=(5, 20), pady=(5, 5), sticky='w') + self.option_axis.grid(row=2, column=3, padx=(5, 10), pady=(5, 5), sticky='w') self.option_axis.configure(state='disabled') # create vel related - self.label_vel = customtkinter.CTkLabel(self.frame_param, width=self.w_param//10, text="Vel", font=self.my_font) + self.label_vel = customtkinter.CTkLabel(self.frame_param, text="Vel", font=self.my_font) self.label_vel.grid(row=2, column=4, sticky='e', pady=(5, 5)) self.option_vel = customtkinter.CTkOptionMenu(self.frame_param, values=["1", "2", "3", "4", "5", "6", "7"], width=self.w_param, font=self.my_font) - self.option_vel.grid(row=2, column=5, padx=(5, 20), pady=(5, 5), sticky='w') + self.option_vel.grid(row=2, column=5, padx=(5, 10), pady=(5, 5), sticky='w') self.option_vel.configure(state='disabled') # create trq related - self.label_trq = customtkinter.CTkLabel(self.frame_param, width=self.w_param//10, text="Trq", font=self.my_font) + self.label_trq = customtkinter.CTkLabel(self.frame_param, text="Trq", font=self.my_font) self.label_trq.grid(row=2, column=6, sticky='e', pady=(5, 5)) self.option_trq = customtkinter.CTkOptionMenu(self.frame_param, values=["1", "2", "3", "4", "5", "6", "7"], width=self.w_param, font=self.my_font) - self.option_trq.grid(row=2, column=7, padx=(5, 20), pady=(5, 5), sticky='w') + self.option_trq.grid(row=2, column=7, padx=(5, 10), pady=(5, 5), sticky='w') self.option_trq.configure(state='disabled') + # create trqH related + self.label_trqh = customtkinter.CTkLabel(self.frame_param, text="TrqH", font=self.my_font) + self.label_trqh.grid(row=2, column=8, sticky='e', pady=(5, 5)) + self.option_trqh = customtkinter.CTkOptionMenu(self.frame_param, values=["1", "2", "3", "4", "5", "6", "7"], width=self.w_param, font=self.my_font) + self.option_trqh.grid(row=2, column=9, padx=(5, 10), pady=(5, 5), sticky='w') + self.option_trqh.configure(state='disabled') + # create STO related + self.label_sto = customtkinter.CTkLabel(self.frame_param, text="STO", font=self.my_font) + self.label_sto.grid(row=2, column=10, sticky='e', pady=(5, 5)) + self.option_sto = customtkinter.CTkOptionMenu(self.frame_param, values=["1", "2", "3", "4", "5", "6", "7"], width=self.w_param, font=self.my_font) + self.option_sto.grid(row=2, column=11, padx=(5, 10), pady=(5, 5), sticky='w') + self.option_sto.configure(state='disabled') + # create rc1 + self.label_rc_1 = customtkinter.CTkLabel(self.frame_param, text="RC1", font=self.my_font) + self.label_rc_1.grid(row=3, column=2, sticky='e', pady=(5, 5)) + self.entry_rc_1 = customtkinter.CTkEntry(self.frame_param, width=self.w_param, placeholder_text=f"optional", font=self.my_font) + self.entry_rc_1.grid(row=3, column=3, padx=(5, 10), pady=(5, 5), sticky='w') + self.entry_rc_1.configure(state='disabled') + + self.label_rc_2 = customtkinter.CTkLabel(self.frame_param, text="RC2", font=self.my_font) + self.label_rc_2.grid(row=3, column=4, sticky='e', pady=(5, 5)) + self.entry_rc_2 = customtkinter.CTkEntry(self.frame_param, width=self.w_param, placeholder_text=f"optional", font=self.my_font) + self.entry_rc_2.grid(row=3, column=5, padx=(5, 10), pady=(5, 5), sticky='w') + self.entry_rc_2.configure(state='disabled') + + self.label_rc_3 = customtkinter.CTkLabel(self.frame_param, text="RC3", font=self.my_font) + self.label_rc_3.grid(row=3, column=6, sticky='e', pady=(5, 5)) + self.entry_rc_3 = customtkinter.CTkEntry(self.frame_param, width=self.w_param, placeholder_text=f"optional", font=self.my_font) + self.entry_rc_3.grid(row=3, column=7, padx=(5, 10), pady=(5, 5), sticky='w') + self.entry_rc_3.configure(state='disabled') + + self.label_rc_4 = customtkinter.CTkLabel(self.frame_param, text="RC4", font=self.my_font) + self.label_rc_4.grid(row=3, column=8, sticky='e', pady=(5, 5)) + self.entry_rc_4 = customtkinter.CTkEntry(self.frame_param, width=self.w_param, placeholder_text=f"optional", font=self.my_font) + self.entry_rc_4.grid(row=3, column=9, padx=(5, 10), pady=(5, 5), sticky='w') + self.entry_rc_4.configure(state='disabled') + + self.label_rc_5 = customtkinter.CTkLabel(self.frame_param, text="RC5", font=self.my_font) + self.label_rc_5.grid(row=3, column=10, sticky='e', pady=(5, 5)) + self.entry_rc_5 = customtkinter.CTkEntry(self.frame_param, width=self.w_param, placeholder_text=f"optional", font=self.my_font) + self.entry_rc_5.grid(row=3, column=11, padx=(5, 10), pady=(5, 5), sticky='w') + self.entry_rc_5.configure(state='disabled') + + self.label_rc_6 = customtkinter.CTkLabel(self.frame_param, text="RC6", font=self.my_font) + self.label_rc_6.grid(row=3, column=12, sticky='e', pady=(5, 5)) + self.entry_rc_6 = customtkinter.CTkEntry(self.frame_param, width=self.w_param, placeholder_text=f"optional", font=self.my_font) + self.entry_rc_6.grid(row=3, column=13, padx=(5, 10), pady=(5, 5), sticky='w') + self.entry_rc_6.configure(state='disabled') # ===================================================================== # create textbox - self.textbox = customtkinter.CTkTextbox(self, height=640, wrap='none', font=customtkinter.CTkFont(family="consolas", size=14), text_color="blue") - self.textbox.grid(row=3, column=1, rowspan=5, columnspan=13, ipadx=10, ipady=10, padx=10, pady=10, sticky='nsew') + self.textbox = customtkinter.CTkTextbox(self, wrap='none', font=customtkinter.CTkFont(family="consolas", size=14), text_color="blue") + self.textbox.grid(row=4, column=1, rowspan=3, columnspan=13, ipadx=10, ipady=10, padx=10, pady=(5, 10), sticky='nsew') self.textbox.configure(state='disabled') # ===================================================================== # version check @@ -146,18 +196,36 @@ class App(customtkinter.CTk): self.label_rc.configure(text="RC", text_color="black") self.label_rpm.configure(text="RPM", text_color="black") self.label_rr.configure(text="RR", text_color="black") + self.label_dur.configure(text="Dur", text_color="black") self.label_axis.configure(text="AXIS", text_color="black") self.label_vel.configure(text="Vel", text_color="black") self.label_trq.configure(text="Trq", text_color="black") + self.label_trqh.configure(text="TrqH", text_color="black") + self.label_sto.configure(text="STO", text_color="black") + self.label_rc_1.configure(text="RC1", text_color="black") + self.label_rc_2.configure(text="RC2", text_color="black") + self.label_rc_3.configure(text="RC3", text_color="black") + self.label_rc_4.configure(text="RC4", text_color="black") + self.label_rc_5.configure(text="RC5", text_color="black") + self.label_rc_6.configure(text="RC6", text_color="black") self.entry_path.configure(placeholder_text="数据文件夹路径", state="disabled") self.entry_av.configure(placeholder_text="角速度", state="disabled") self.entry_rc.configure(placeholder_text="额定电流", state="disabled") self.entry_rpm.configure(placeholder_text="额定转速", state="disabled") self.entry_rr.configure(placeholder_text="减速比", state="disabled") + self.entry_dur.configure(placeholder_text="周期", state="disabled") self.option_axis.configure(state="disabled") self.option_vel.configure(state="disabled") self.option_trq.configure(state="disabled") + self.option_trqh.configure(state="disabled") + self.option_sto.configure(state="disabled") + self.entry_rc_1.configure(placeholder_text="optional", state="disabled") + self.entry_rc_2.configure(placeholder_text="optional", state="disabled") + self.entry_rc_3.configure(placeholder_text="optional", state="disabled") + self.entry_rc_4.configure(placeholder_text="optional", state="disabled") + self.entry_rc_5.configure(placeholder_text="optional", state="disabled") + self.entry_rc_6.configure(placeholder_text="optional", state="disabled") self.menu_sub.grid_forget() self.textbox.delete(index1='1.0', index2='end') @@ -171,12 +239,12 @@ class App(customtkinter.CTk): self.menu_sub.grid(row=1, column=1, sticky='we', padx=(20, 10), pady=(5, 5)) self.menu_sub.set("--select--") - self.label_path.configure(text="*Path", text_color='red') - self.label_av.configure(text="*AV", text_color='red') - self.label_rr.configure(text="*RR", text_color='red') - self.label_axis.configure(text="*AXIS", text_color='red') - self.label_vel.configure(text="*Vel", text_color='red') - self.label_trq.configure(text="*Trq", text_color='red') + self.label_path.configure(text="Path", text_color='red') + self.label_av.configure(text="AV", text_color='red') + self.label_rr.configure(text="RR", text_color='red') + self.label_axis.configure(text="AXIS", text_color='red') + self.label_vel.configure(text="Vel", text_color='red') + self.label_trq.configure(text="Trq", text_color='red') self.entry_path.configure(state="normal") self.entry_av.configure(state="normal") @@ -186,34 +254,71 @@ class App(customtkinter.CTk): self.option_trq.configure(state="normal") elif func_name == 'current': - self.menu_sub = customtkinter.CTkOptionMenu(self.frame_param, values=["max", "avg"], font=self.my_font, command=self.func_sub_callback) + self.menu_sub = customtkinter.CTkOptionMenu(self.frame_param, values=["max", "avg", "cycle"], font=self.my_font, command=self.func_sub_callback) self.menu_sub.grid(row=1, column=1, sticky='we', padx=(20, 10), pady=(5, 5)) self.menu_sub.set("--select--") - self.label_path.configure(text="*Path", text_color='red') - self.label_rc.configure(text="*RC", text_color='red') - self.label_trq.configure(text="*Trq", text_color='red') + self.label_path.configure(text="Path", text_color='red') + self.label_rc.configure(text="RC", text_color='blue') + self.label_trqh.configure(text="TrqH", text_color='red') + self.label_rc_1.configure(text="RC1", text_color='red') + self.label_rc_2.configure(text="RC2", text_color='red') + self.label_rc_3.configure(text="RC3", text_color='red') + self.label_rc_4.configure(text="RC4", text_color='red') + self.label_rc_5.configure(text="RC5", text_color='red') + self.label_rc_6.configure(text="RC6", text_color='red') self.entry_path.configure(state="normal") - self.entry_rc.configure(state="normal") - self.option_trq.configure(state="normal") + self.entry_rc.configure(state="normal", placeholder_text="optional") + self.option_trqh.configure(state="normal") + self.entry_rc_1.configure(state="normal", placeholder_text="额定电流") + self.entry_rc_2.configure(state="normal", placeholder_text="额定电流") + self.entry_rc_3.configure(state="normal", placeholder_text="额定电流") + self.entry_rc_4.configure(state="normal", placeholder_text="额定电流") + self.entry_rc_5.configure(state="normal", placeholder_text="额定电流") + self.entry_rc_6.configure(state="normal", placeholder_text="额定电流") + elif func_name == 'iso': - self.label_path.configure(text="*Path", text_color='red') + self.label_path.configure(text="Path", text_color='red') self.entry_path.configure(state="normal") + else: self.initialization() self.menu_main.set("Start Here!") def func_sub_callback(self, func_name): if func_name == "industrial": - self.label_rpm.configure(text="*RPM", text_color='red') + self.label_rpm.configure(text="RPM", text_color='red') self.entry_rpm.configure(state="normal") elif func_name == "cobot": self.label_rpm.configure(text="RPM", text_color='black') self.entry_rpm.configure(state="disabled") elif func_name == "max": - pass + self.label_rpm.configure(text="RPM", text_color='black') + self.entry_rpm.configure(state="disabled", placeholder_text='额定转速') + self.label_dur.configure(text="Dur", text_color='black') + self.entry_dur.configure(state="disabled") + self.label_vel.configure(text="Vel", text_color='black') + self.option_vel.configure(state="disabled") + self.label_trq.configure(text="Trq", text_color='black') + self.option_trq.configure(state="disabled") elif func_name == 'avg': - pass + self.label_rpm.configure(text="RPM", text_color='black') + self.entry_rpm.configure(state="disabled", placeholder_text='额定转速') + self.label_dur.configure(text="Dur", text_color='black') + self.entry_dur.configure(state="disabled") + self.label_vel.configure(text="Vel", text_color='black') + self.option_vel.configure(state="disabled") + self.label_trq.configure(text="Trq", text_color='black') + self.option_trq.configure(state="disabled") + elif func_name == 'cycle': + self.label_rpm.configure(text="RPM", text_color='blue') + self.entry_rpm.configure(state="normal", placeholder_text='cycle') + self.label_dur.configure(text="Dur", text_color='blue') + self.entry_dur.configure(state="normal", placeholder_text='scenario') + self.label_vel.configure(text="Vel", text_color='red') + self.option_vel.configure(state="normal") + self.label_trq.configure(text="Trq", text_color='red') + self.option_trq.configure(state="normal") def write2textbox(self, text, wait=0, exitcode=0): if wait != 0: @@ -226,6 +331,7 @@ class App(customtkinter.CTk): self.textbox.insert(index='end', text=text + '\n') self.textbox.update() self.textbox.see('end') + self.textbox.configure(state='disabled') raise Exception(f"Error code: {exitcode}") else: self.textbox.configure(text_color='blue') @@ -233,6 +339,19 @@ class App(customtkinter.CTk): self.textbox.update() self.textbox.see('end') + def is_float(self, flag, *args): + for item in args: + try: + if flag == 'optional': + item = 0 if item == '' else item + _ = float(item) + elif flag == 'required': + _ = float(item) + except Exception as Err: + tkinter.messagebox.showerror(title="参数错误", message="请检查对应参数是否填写正确!", ) + self.write2textbox(f"错误信息:{Err}\n参数数据缺失,或者数据类型错误,更正后重新运行...", 0, 3) + return True + def check_param(self): func_name = self.menu_main.get() if func_name == 'brake': @@ -245,44 +364,64 @@ class App(customtkinter.CTk): sub_func = self.menu_sub.get() c1 = exists(path) - c2 = av.isdigit() - c3 = rr.isdigit() - c4 = rpm = 1 - c5 = sub_func in ['industrial', 'cobot'] - c6 = True if vel != trq else False + c2 = self.is_float('required', av, rr) + c3 = rpm = 1 + c4 = sub_func in ['industrial', 'cobot'] + c5 = True if vel != trq else False if self.menu_sub.get() == 'industrial': rpm = self.entry_rpm.get().strip('- ') - c4 = rpm.isdigit() + c3 = rpm.isdigit() elif self.menu_sub.get() == 'cobot': pass else: pass - if c1 and c2 and c3 and c4 and c5 and c6: - return 1, path, int(av), int(rr), int(rpm), int(axis), int(vel), int(trq) + if c1 and c2 and c3 and c4 and c5: + return 1, path, float(av), float(rr), int(rpm), int(axis), int(vel), int(trq) else: return 0, 0 - + # ======================================================= elif func_name == 'current': path = self.entry_path.get() rc = self.entry_rc.get() + rpm = self.entry_rpm.get() + dur = self.entry_dur.get() vel = self.option_vel.get() trq = self.option_trq.get() - sub_func = self.menu_sub.get() - c1 = exists(path) - c2 = sub_func in ['max', 'avg'] - c3 = True if vel != trq else False - try: - _ = float(rc) - c4 = True - except ValueError: - c4 = False + trqh = self.option_trqh.get() + sub = self.menu_sub.get() + rc1 = self.entry_rc_1.get() + rc2 = self.entry_rc_2.get() + rc3 = self.entry_rc_3.get() + rc4 = self.entry_rc_4.get() + rc5 = self.entry_rc_5.get() + rc6 = self.entry_rc_6.get() - if c1 and c2 and c3 and c4: - return 2, path, float(rc), int(vel), int(trq), sub_func + c0 = exists(path) + c1 = sub in ['max', 'avg', 'cycle'] + c2 = self.is_float('optional', rc) + c3 = self.is_float('optional', rpm) + _ = [rc1, rc2, rc3, rc4, rc5, rc6] + c4 = self.is_float('required', *_) + + c5 = c6 = True + if sub == 'cycle': + c5 = True if vel != trq else False + c6 = self.is_float('optional', dur) + + if c0 and c1 and c2 and c3 and c4 and c5 and c6: + rcs = [] + for x in _: + rcs.append(float(x)) + rc = 0 if rc == '' else rc + dur = 0 if sub != 'cycle' or dur == '' else dur + rpm = 0 if sub != 'cycle' or rpm == '' else rpm + rcs.append(float(rc)) + return 2, path, sub, rcs, int(vel), int(trq), int(trqh), float(dur), float(rpm) else: return 0, 0 + # ======================================================= elif func_name == 'iso': path = self.entry_path.get() c1 = exists(path) @@ -294,7 +433,6 @@ class App(customtkinter.CTk): return 0, 0 def func_start_callback(self): - self.textbox.configure(state='normal') self.textbox.delete(index1='1.0', index2='end') @@ -304,8 +442,8 @@ class App(customtkinter.CTk): if flag == 1: func_dict[flag](path=args[0], av=args[1], rr=args[2], rpm=args[3], axis=args[4], vel=args[5], trq=args[6], w2t=self.write2textbox) elif flag == 2: - tkinter.messagebox.showinfo(title="TBD", message="功能待实现......") - # func_dict[flag](path=args[0], rc=args[1], sub_func=args[2]) + # tkinter.messagebox.showinfo(title="TBD", message="功能待实现......") + func_dict[flag](path=args[0], sub=args[1], rcs=args[2], vel=args[3], trq=args[4], trqh=args[5], dur=args[6], rpm=args[7], w2t=self.write2textbox) elif flag == 3: func_dict[flag](path=args[0], w2t=self.write2textbox) else: @@ -314,11 +452,14 @@ class App(customtkinter.CTk): self.textbox.configure(state='disabled') def func_check_callback(self): + self.textbox.configure(state='normal') + self.textbox.delete(index1='1.0', index2='end') flag, *args = self.check_param() if flag: tkinter.messagebox.showinfo(title="参数正确", message="所有参数形式上填写无误,可以开始运行!") else: tkinter.messagebox.showerror(title="参数错误", message="需要检查对应参数是否填写正确!", ) + self.textbox.configure(state='disabled') def func_log_callback(self): content = self.textbox.get(index1='1.0', index2='end') @@ -331,7 +472,7 @@ class App(customtkinter.CTk): tkinter.messagebox.showinfo(title="保存成功", message=f'{log_name}已被保存存至↓↓↓\n{getcwd()}') except Exception as Err: - print(Err) + self.write2textbox(Err) tkinter.messagebox.showerror(title="保存失败", message="未能保存本次日志,或未能完整保存,请准备好相关数据,联系fanmingfu@rokae.com查看详细信息!", ) else: tkinter.messagebox.showwarning(title="未能保存", message="日志数据为空,不可保存!") @@ -339,12 +480,8 @@ class App(customtkinter.CTk): def func_end_call_back(self): if tkinter.messagebox.askyesno(title="关闭程序", message="相关数据可能未保存,正在运行程序时有概率会损坏数据文件,确定要终止程序运行吗?"): self.destroy() - else: - pass - pass if __name__ == "__main__": - aio = App() aio.mainloop() diff --git a/aio/current.py b/aio/current.py index 511f354..422d384 100644 --- a/aio/current.py +++ b/aio/current.py @@ -1,30 +1,52 @@ -import openpyxl -import os -import sys -import pandas +from openpyxl import load_workbook +from os import scandir +from os.path import exists +from sys import argv +from pandas import read_csv, concat, set_option +from re import match +from threading import Thread +from time import sleep +from csv import reader +class GetThreadResult(Thread): + def __init__(self, func, args=()): + super(GetThreadResult, self).__init__() + self.func = func + self.args = args + self.result = 0 + + def run(self): + sleep(1) + self.result = self.func(*self.args) + + def get_result(self): + Thread.join(self) # 等待线程执行完毕 + try: + return self.result + except Exception as Err: + return None -def warn_pause_exit(msg, pause_num, exit_num): - # 功能:打印告警信息,并推出程序 - # 参数:告警信息,暂停的次数,退出的值 - # 返回值:- - print(msg + '\n') - for i in range(pause_num): - _ = input("Press ENTER to continue......\n") - sys.exit(exit_num) +def w2t_local(msg, wait, w2t): + while True: + global stop + if stop == 0 and wait != 0: + sleep(1) + w2t(msg, wait) + else: + break -def traversal_files(path): +def traversal_files(path, w2t): # 功能:以列表的形式分别返回指定路径下的文件和文件夹,不包含子目录 # 参数:路径 # 返回值:路径下的文件夹列表 路径下的文件列表 - if not os.path.exists(path): + if not exists(path): msg = f'数据文件夹{path}不存在,请确认后重试......' - warn_pause_exit(msg, 1, 11) + w2t(msg, 0, 8) else: dirs = [] files = [] - for item in os.scandir(path): + for item in scandir(path): if item.is_dir(): dirs.append(item.path) elif item.is_file(): @@ -33,71 +55,300 @@ def traversal_files(path): return dirs, files -def initialization(): - try: - # read init configurations from config file - wb_conf = openpyxl.load_workbook('./configs.xlsx', read_only=True) - ws_conf = wb_conf['current'] - except Exception as Err: - msg = "无法在当前路径下找到或打开【configs.xlsx】文件,请确认!" - warn_pause_exit(msg, 1, 2) +def initialization(path, sub, w2t): + _, data_files = traversal_files(path, w2t) + count = 0 - FUNC = ws_conf.cell(row=2, column=2).value - DATA_DIR = ws_conf.cell(row=3, column=2).value - AXIS = int(ws_conf.cell(row=4, column=2).value) - RC = ws_conf.cell(row=5, column=2).value.split(',') - COLUMN = int(ws_conf.cell(row=6, column=2).value) - wb_conf.close() - - _, data_files = traversal_files(DATA_DIR) for data_file in data_files: - if not data_file.endswith('.data'): - msg = f"所有文件必须以 .data 结尾,请检查后重新运行。" - warn_pause_exit(msg, 1, 1) + filename = data_file.split('\\')[-1] + if sub != 'cycle': + if not (match('j[1-7].*\\.data', filename) or match('j[1-7].*\\.csv', filename)): + msg = f"所有文件必须以 jx_ 开头,以 .data/csv 结尾(x取值1-7),请检查后重新运行。" + w2t(msg, 0, 6) + else: + if filename.endswith('.xlsx'): + count += 1 + elif not (match('j[1-7].*\\.data', filename) or match('j[1-7].*\\.csv', filename)): + msg = f"所有文件必须以 jx_ 开头,以 .data/csv 结尾(x取值1-7),请检查后重新运行。" + w2t(msg, 0, 7) - return data_files, FUNC, RC, COLUMN, AXIS + if sub == 'cycle' and count != 1: + w2t("未找到电机电流数据处理excel表格,确认后重新运行!", 0, 5) + + return data_files -def current_max(*args): - data_files, FUNC, RC, COLUMN, AXIS = args +def current_max(data_files, rcs, trqh, w2t): + current = {1: 0, 2: 0, 3: 0, 4: 0, 5: 0, 6: 0, 7: 0} for data_file in data_files: - df = pandas.read_csv(data_file, sep='\t') - col = df.columns.values[COLUMN-1] + if data_file.endswith('.data'): + df = read_csv(data_file, sep='\t') + elif data_file.endswith('.csv'): + df = read_csv(data_file, sep=',', encoding='gbk', header=8) + + axis = int(data_file.split('\\')[-1].split('_')[0].removeprefix('j')) + rca = rcs[axis-1] + + col = df.columns.values[trqh-1] c_max = df[col].max() - print(f"{data_file}: {c_max/1000*RC[AXIS-1]:.3f}") - msg = f"数据处理完毕......" - warn_pause_exit(msg, 1, 0) + scale = 1 if data_file.endswith('.csv') else 1000 + _ = abs(c_max/scale*rca) + current[axis] = _ + w2t(f"{data_file}: {_:.4f}") + + w2t("【MAX】数据处理完毕......") + return current -def current_avg(*args): - data_files, FUNC, RC, COLUMN, AXIS = args +def current_avg(data_files, rcs, trqh, w2t): + current = {1: 0, 2: 0, 3: 0, 4: 0, 5: 0, 6: 0, 7: 0} for data_file in data_files: - df = pandas.read_csv(data_file, sep='\t') - col = df.columns.values[COLUMN-1] + if data_file.endswith('.data'): + df = read_csv(data_file, sep='\t') + elif data_file.endswith('.csv'): + df = read_csv(data_file, sep=',', encoding='gbk', header=8) + + axis = int(data_file.split('\\')[-1].split('_')[0].removeprefix('j')) + rca = rcs[axis-1] + + col = df.columns.values[trqh - 1] c_std = df[col].std() c_avg = df[col].mean() - axis = int(data_file.split('\\')[-1].split('_')[0].replace('j', '')) - print(f"{data_file}: {(abs(c_avg)+c_std)/1000*float(RC[axis-1]):.3f}") + scale = 1 if data_file.endswith('.csv') else 1000 + _ = (abs(c_avg)+c_std)/scale*rca + current[axis] = _ + w2t(f"{data_file}: {_:.4f}") - msg = f"数据处理完毕......" - warn_pause_exit(msg, 1, 0) + w2t("【AVG】数据处理完毕......") + return current -def full_cycle(*args): - pass +def current_cycle(dur, data_files, rcs, vel, trq, trqh, rpm, w2t): + result = None + hold = [] + single = [] + for data_file in data_files: + filename = data_file.split('\\')[-1] + if data_file.endswith('.xlsx'): + result = data_file + elif match('j[1-7]_hold_.*\\.data', filename) or match('j[1-7]_hold_.*\\.csv', filename): + hold.append(data_file) + else: + single.append(data_file) + + w2t(f"正在打开文件 {result},需要 10s 左右", 1) + + global stop + stop = 0 + t_excel = GetThreadResult(load_workbook, args=(result, )) + t_wait = Thread(target=w2t_local, args=('.', 1, w2t)) + t_excel.start() + t_wait.start() + t_excel.join() + wb = t_excel.get_result() + stop = 1 + sleep(1.1) + w2t('') + + if hold != []: + avg = current_avg(hold, rcs, trqh, w2t) + for axis, cur_value in avg.items(): + try: + shtname = f"J{axis}" + wb[shtname]["J4"].value = float(cur_value) + except: + pass + + if dur == 0: + p_single(wb, single, vel, trq, rpm, w2t) + else: + p_scenario(wb, single, vel, trq, rpm, dur, w2t) + + w2t(f"正在保存文件 {result},需要 10s 左右", 1) + stop = 0 + t_excel = Thread(target=wb.save, args=(result, )) + t_wait = Thread(target=w2t_local, args=('.', 1, w2t)) + t_excel.start() + t_wait.start() + t_excel.join() + stop = 1 + sleep(1.1) + w2t('\n') + w2t("----------------------------------------------------------") + w2t("全部处理完毕") -def main(): - args = initialization() - if args[1] == 'max': - current_max(*args) - elif args[1] == 'avg': - current_avg(*args) - elif args[1] == 'cycle': - full_cycle(*args) +def find_point(flag, df, _row_s, _row_e, w2t, exitcode, threshold, step, end_point): + if flag == 'lt': + while _row_e > end_point: + speed_avg = df.iloc[_row_s:_row_e, 0].abs().mean() + if speed_avg < threshold: + _row_e -= step + _row_s -= step + continue + else: + return _row_s, _row_e + else: + w2t(f"数据有误,需要检查,无法找到第{exitcode}个有效点...", 0, exitcode) + elif flag == 'gt': + while _row_e > end_point: + speed_avg = df.iloc[_row_s:_row_e, 0].abs().mean() + if speed_avg > threshold: + _row_e -= step + _row_s -= step + continue + else: + return _row_s, _row_e + else: + w2t(f"数据有误,需要检查,无法找到有效起始点或结束点...", 0, exitcode) + + +def p_single(wb, single, vel, trq, rpm, w2t): + # 1. 先找到第一个速度为零的点,数据从后往前找,一开始就是零的情况不予考虑 + # 2. 记录第一个点的位置,继续向前查找第二个速度为零的点,同理,一开始为零的点不予考虑 + # 3. 记录第二个点的位置,并将其中的数据拷贝至对应位置 + for data_file in single: + rpm = 1 if rpm == 0 else rpm + scale = 1000 if data_file.endswith('.csv') else 1 + axis = int(data_file.split('\\')[-1].split('_')[0].removeprefix('j')) + shtname = f"J{axis}" + ws = wb[shtname] + + set_option("display.precision", 2) + if data_file.endswith('.data'): + df = read_csv(data_file, sep='\t') + elif data_file.endswith('.csv'): + df = read_csv(data_file, sep=',', encoding='gbk', header=8) + csv_reader = reader(open(data_file)) + i = 0 + cycle = 0.001 + for row in csv_reader: + i += 1 + if i == 3: + cycle = float(row[0].split(':')[1].split('ms')[0]) / 1000 + break + ws["H11"] = cycle + + col_names = list(df.columns) + df_1 = df[col_names[vel-1]].multiply(rpm) + df_2 = df[col_names[trq-1]].multiply(scale) + df = concat([df_1, df_2], axis=1) + + _step = 5 if data_file.endswith('.csv') else 50 + _end_point = 30 if data_file.endswith('.csv') else 200 + _adjust = 0 if data_file.endswith('.csv') else 150 + _row_e = df.index[-1] + _row_s = _row_e - _end_point + speed_avg = df.iloc[_row_s:_row_e, 0].abs().mean() + if speed_avg < 2: + # 过滤尾部为零无效数据 + _row_s, _row_e = find_point('lt', df, _row_s, _row_e, w2t, 1, threshold=2, step=_step, end_point=_end_point) + # 找到第一个起始点 row_end,继续找到有数据的部分,后面有一段有效数据区 + row_end = _row_e - _adjust + _row_e -= _end_point + _row_s -= _end_point + _row_s, _row_e = find_point('gt', df, _row_s, _row_e, w2t, 3, threshold=2, step=_step, end_point=_end_point) + # 速度已经快要降为零了,继续寻找下一个速度上升点 + _row_e -= _end_point + _row_s -= _end_point + _row_s, _row_e = find_point('lt', df, _row_s, _row_e, w2t, 3, threshold=2, step=_step, end_point=_end_point) + elif speed_avg > 2: + # 过滤尾部非零无效数据 + _row_s, _row_e = find_point('gt', df, _row_s, _row_e, w2t, 2, threshold=2, step=_step, end_point=_end_point) + # 找到第一个起始点 row_end,继续找到有数据的部分,后面有一段零数据区 + row_end = _row_e - _adjust + _row_e -= _end_point + _row_s -= _end_point + _row_s, _row_e = find_point('lt', df, _row_s, _row_e, w2t, 4, threshold=2, step=_step, end_point=_end_point) + #目前已经有一点的速度值了,继续往前搜寻下一个速度为零的点 + _row_e -= _end_point + _row_s -= _end_point + _row_s, _row_e = find_point('gt', df, _row_s, _row_e, w2t, 4, threshold=2, step=_step, end_point=_end_point) + + row_start = _row_s + _adjust + data = [] + for row in range(row_start, row_end): + data.append(df.iloc[row, 0]) + data.append(df.iloc[row, 1]) + + i = 0 + for row in ws.iter_rows(min_row=2, min_col=2, max_row=15000, max_col=3): + for cell in row: + try: + _ = f"{data[i]:.2f}" + cell.value = float(_) + i += 1 + except: + cell.value = None + + +def p_scenario(wb, single, vel, trq, rpm, dur, w2t): + for data_file in single: + cycle = 0.001 + rpm = 1 if rpm == 0 else rpm + scale = 1000 if data_file.endswith('.csv') else 1 + axis = int(data_file.split('\\')[-1].split('_')[0].removeprefix('j')) + shtname = f"J{axis}" + ws = wb[shtname] + + set_option("display.precision", 2) + if data_file.endswith('.data'): + df = read_csv(data_file, sep='\t') + elif data_file.endswith('.csv'): + df = read_csv(data_file, sep=',', encoding='gbk', header=8) + csv_reader = reader(open(data_file)) + i = 0 + for row in csv_reader: + i += 1 + if i == 3: + cycle = float(row[0].split(':')[1].split('ms')[0]) / 1000 + break + + ws["H11"] = cycle + + col_names = list(df.columns) + df_1 = df[col_names[vel-1]].multiply(rpm) + df_2 = df[col_names[trq-1]].multiply(scale) + df = concat([df_1, df_2], axis=1) + + row_start = 300 + row_end = row_start + int(dur/cycle) + if row_end > df.index[-1]: + w2t(f"位置超限:{data_file} 共有 {df.index[-1]} 条数据,无法取到第 {row_end} 条数据...", 0, 9) + + data = [] + for row in range(row_start, row_end): + data.append(df.iloc[row, 0]) + data.append(df.iloc[row, 1]) + + i = 0 + for row in ws.iter_rows(min_row=2, min_col=2, max_row=15000, max_col=3): + for cell in row: + try: + _ = f"{data[i]:.2f}" + cell.value = float(_) + i += 1 + except: + cell.value = None + + +# ======================================= + + +def main(path, sub, rcs, vel, trq, trqh, dur, rpm, w2t): + data_files = initialization(path, sub, w2t) + if sub == 'max': + current_max(data_files, rcs, trqh, w2t) + elif sub == 'avg': + current_avg(data_files, rcs, trqh, w2t) + elif sub == 'cycle': + current_cycle(dur, data_files, rcs, vel, trq, trqh, rpm, w2t) + else: + pass if __name__ == '__main__': - main() + stop = 0 + main(*argv[1:]) diff --git a/aio/file_version_info.txt b/aio/file_version_info.txt index e147ef6..f47f94e 100644 --- a/aio/file_version_info.txt +++ b/aio/file_version_info.txt @@ -6,8 +6,8 @@ VSVersionInfo( ffi=FixedFileInfo( # filevers and prodvers should be always a tuple with four items: (1, 2, 3, 4) # Set not needed items to zero 0. - filevers=(0, 1, 2, 0), - prodvers=(0, 1, 2, 0), + filevers=(0, 1, 4, 0), + prodvers=(0, 1, 4, 0), # Contains a bitmask that specifies the valid bits 'flags'r mask=0x3f, # Contains a bitmask that specifies the Boolean attributes of the file. @@ -31,13 +31,13 @@ VSVersionInfo( '040904b0', [StringStruct('CompanyName', 'Rokae - https://www.rokae.com/'), StringStruct('FileDescription', 'All in one automatic toolbox'), - StringStruct('FileVersion', '0.1.2 (2024-06-01)'), + StringStruct('FileVersion', '0.1.4 (2024-06-06)'), StringStruct('InternalName', 'AIO.exe'), StringStruct('LegalCopyright', '© 2024-2024 Manford Fan'), StringStruct('OriginalFilename', 'AIO.exe'), StringStruct('ProductName', 'AIO'), - StringStruct('ProductVersion', '0.1.2 (2024-05-29)')]) - ]), + StringStruct('ProductVersion', '0.1.4 (2024-06-06)')]) + ]), VarFileInfo([VarStruct('Translation', [1033, 1200])]) ] -) \ No newline at end of file +) diff --git a/aio/layout.xlsx b/aio/layout.xlsx index 42c83de..ac11973 100644 Binary files a/aio/layout.xlsx and b/aio/layout.xlsx differ diff --git a/aio/vers b/aio/vers index 655cf7a..8e63fba 100644 --- a/aio/vers +++ b/aio/vers @@ -1 +1 @@ -0.1.2 @ 06/01/2024 +0.1.4 @ 06/06/2024 \ No newline at end of file