28 Commits

Author SHA1 Message Date
346379ec1a re-commit 2024-06-16 14:09:26 +08:00
215e312480 iv0.1.6.1(2024/06/16)
1. [wavelogger.py]: bugfix single_file_proc函数中,修改_start起始点的计算逻辑
2. [wavelogger.py]: bugfix find_point函数中,当判断条件为临界值 2.0 的时候,针对forward和backward两种情况,对row_target做与判断逻辑相同的处理,目的是避免形成死循环
2024-06-16 14:07:36 +08:00
5ba3c76386 modify vers info 2024-06-15 19:32:57 +08:00
224af36bfe change to new version because of new function added 2024-06-15 19:27:31 +08:00
95071f363b fix merging 2024-06-15 19:17:53 +08:00
fc30fcde80 v0.1.5.4(2024/06/15)
[aio.py]: 新增wavelogger处理界面
[wavelogger.py]: 新增精度数据处理模块
2024-06-15 19:14:34 +08:00
e014775972 v0.1.5.3(2024/06/14)
[aio.py]: 修改w_param为84,适配14寸电脑屏幕
[brake.py]: 将判定合规逻辑修改为角速度超过指定角速度的95%
[README.md]: 稍作修改,包括打包方式,功能特性等
2024-06-14 12:37:12 +08:00
15e6f1b6c0 re-arrange file-organization 2024-06-14 07:29:28 +08:00
ecdec2e41b modify version info 2024-06-13 20:07:50 +08:00
07eb0c8940 modify version info 2024-06-13 20:06:11 +08:00
39f3cb836b v0.1.5.2(2024/06/13)
[brake.py/aio.py]: 将sto修改为estop
[brake.py]: 修改了速度计算逻辑,新版本的vel列数据遵循如下规则,av = vel * 180 / pi,根据av再计算speed
[brake.py]: 将threshold修改为常量50
[brake.py]: 提高了输出提示语的明确性,删除了不必要的省略号
[brake.py]: 更正了之前的数据copy错误,重新优化了estop处是否达到指定百分比的判定逻辑
2024-06-13 19:58:34 +08:00
7e568fb699 v0.1.5.1(2024/06/12)
[current.py]:  修改cycle功能中,数据清理范围为70000行,并将threshold从2调整为5
[current.py]:  修改位置超限提示,使更清楚了解问题原因
[current.py]:  修改find_point函数中错误提示,增加定位信息
[README.md]: 精简打包命令
[requirements.txt]: 新增必要库配置文件
2024-06-13 18:10:42 +08:00
9971b51457 [modify] add modification method to change tabview font 2024-06-11 10:52:09 +08:00
e1716332d7 fix vers date 2024-06-11 10:39:08 +08:00
2ac5dc3895 [layout.xlsx]: 添加了各个功能的流程图
[aio.py] 修改了rc的placeholder内容,和其他rcx统一
2024-06-11 10:28:46 +08:00
154657fd18 [modify] [aio.py]: 修改menu_main->menu_main_dp,menu_sub->menu_sub_dp,为后续其他tab功能按钮做扩展,是针对第三点做出的相应调整 2024-06-09 22:36:20 +08:00
412c7c2600 [aio.py]: 新增tabview组件,区分数据处理和自动化测试功能 2024-06-09 21:32:10 +08:00
8e64d18fb8 [aio.py]: 更改label/entry/optionmenu等控件的生成方式,使用循环实现,更加简洁和容易维护 2024-06-09 13:54:39 +08:00
35d7e41ffd v0.1.5.0(2024/06/08)
1. [aio.py]: 主界面切换不同功能时保持placehold一致
2. [brake.py]: 由于制动采集模板和内容的更改,适配了新的数据,更新了算法
3. [layout.xlsx]: 添加了各个功能的流程图
4. [aio.py]: 重新调整界面配色
5. [aio.py]: 修改了write2textbox函数,定制化显示每一行的颜色,针对每一行可自定义输出内容颜色
6. [brake.py/iso.py/current.py]: 由于第 5 点的更改,同时修改了其他文件相关引用的部分
2024-06-08 15:58:27 +08:00
cb8dc2584f Merge branch 'main' into new_brake 2024-06-07 06:40:10 +08:00
05302956ce fix merging 2024-06-06 19:29:00 +08:00
e065018ef3 [modify] version/readme etc. 2024-06-06 19:25:16 +08:00
e7193a1baf [merging] fix merging 2024-06-06 19:08:34 +08:00
a0404c0003 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计算
2024-06-06 19:04:55 +08:00
97ba8ffc51 [modify] support single axis cycle data process 2024-06-05 21:27:11 +08:00
55514abc93 [modify]: 完成了电流处理的基本工作,包括框架搭建和思路整理,重写了max/avg以支持工业数据,新增加了duration的逻辑,中间状态,先保存一下 2024-06-04 08:29:23 +08:00
bb437a814e [modify] readme minor 2024-06-03 09:45:36 +08:00
2d1cb4f924 [modfiy] prepare for new method of processing brake data 2024-06-01 14:52:15 +08:00
18 changed files with 6273 additions and 567 deletions

8
.gitignore vendored
View File

@ -1,6 +1,6 @@
test.py
.idea/
aio/.idea/
aio/__pycache__/
aio/dist/
aio/build/
aio/aio.spec
aio/code/__pycache__/
aio/package/
aio/venv

View File

@ -1,25 +1,34 @@
### 程序功能
自动化处理制动性能采集的数据减少人工处理时长目前测试单轴可从原来的4-6h减少到15min
自动化测试数据处理工具,减少人工处理时长,提高测试数据处理的效率和准确度:
1. 制动数据单轴数据处理3min以内
2. 电机电流数据全部轴数据处理1min以内
3. ISO激光数据整理1min以内
4. wavelogger波形处理几乎不花费时间
### 使用方法
修改 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 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
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
```text
参考requirements.txt
```
### 打包方法
```
pyinstaller.exe -F --version-file file_version_info.txt -i .\icon.ico .\aio.py -p .\brake.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 ..\assets\file_version_info.txt -i ..\assets\icon.ico ..\code\aio.py -p ..\code\brake.py -p ..\code\iso.py -p ..\code\current.py -p ..\code\wavelogger.py
```
---
### 注意事项
#### 制动数据
```text
1. 数据文件存储存储规则
数据文件,就是我们拍急停的时候,采集到的 .data 文件,正方向拍三次急停,会采集到三个 .data 文件,存储在同一个文件夹内,即每组(三个 .data 文件)文件必须存储在同一个文件夹内,数据文件的命名无要求,
@ -65,6 +74,40 @@ 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
#### wavelogger波形数据
1. 需要提前将 .xdt 波形数据转换成 .csv 文件
2. 组织目录下只允许有 .csv 文件,对文件夹无要求
3. 运行结束后,会生成 result.xlsx 文件,结果按照 .csv 文件名存放
4. 采集数据时,不同轮次数据时间间隔最好大于 2 倍的周期时间,否则会出现采集的轮数不正确的情况,但数据是完整的
#### 其他
customtkinter的tabview组件不支持修改字体大小可以参考 [Changing Font of a Tabview](https://github.com/TomSchimansky/CustomTkinter/issues/2296) 进行手动修改源码实现:
a. 运行 `pip show customtkinter`,获取到库的路径
b. 修改.../windows/widgets/ctk_tabview.py
c. 增加 from .font.ctk_font import CTkFont
d. 在大概 78 行的位置,增加 font=CTkFont(family="Consolas", size=18, weight='bold')
---
RELEASE CHANGES
@ -135,3 +178,52 @@ v0.1.2(2024/06/01)
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计算
v0.1.5(2024/06/12)
1. [aio.py]: 主界面切换不同功能时保持placehold一致
2. [brake.py]: 由于制动采集模板和内容的更改,适配了新的数据,更新了算法
3. [aio.py]: 新增tabview组件区分数据处理和自动化测试功能
4. [aio.py]: 重新调整界面配色
5. [aio.py]: 修改了write2textbox函数定制化显示每一行的颜色针对每一行可自定义输出内容颜色
6. [brake.py/iso.py/current.py]: 由于第 5 点的更改,同时修改了其他文件相关引用的部分
7. [aio.py]: 更改label/entry/optionmenu等控件的生成方式使用循环实现更加简洁和容易维护
8. [aio.py]: 修改customtkinter库中C:\Users\Administrator\AppData\Local\Programs\Python\Python312\Lib\site-packages\customtkinter\windows\widgets\ctk_tabview.py文件参考https://github.com/TomSchimansky/CustomTkinter/issues/2296实现修改tabview组件的字体大小
9. [aio.py]: 修改menu_main->menu_main_dpmenu_sub->menu_sub_dp为后续其他tab功能按钮做扩展是针对第三点做出的相应调整
10. [layout.xlsx]: 添加了各个功能的流程图
v0.1.5.1(2024/06/12)
1. [current.py]: 修改cycle功能中数据清理范围为70000行并将threshold从2调整为5
2. [current.py]: 修改位置超限提示,使更清楚了解问题原因
3. [current.py]: 修改find_point函数中错误提示增加定位信息
4. [README.md]: 精简打包命令
5. [requirements.txt]: 新增必要库配置文件
v0.1.5.2(2024/06/13)
1. [brake.py/aio.py]: 将sto修改为estop
2. [brake.py]: 修改了速度计算逻辑新版本的vel列数据遵循如下规则av = vel * 180 / pi根据av再计算speed
3. [brake.py]: 将threshold修改为常量50
4. [brake.py]: 提高了输出提示语的明确性,删除了不必要的省略号
5. [brake.py]: 更正了之前的数据copy错误重新优化了estop处是否达到指定百分比的判定逻辑
v0.1.5.3(2024/06/14)
1. [aio.py]: 修改w_param为84适配14寸电脑屏幕
2. [brake.py]: 将判定合规逻辑修改为角速度超过指定角速度的95%
3. [README.md]: 稍作修改,包括打包方式,功能特性等
v0.1.6.0(2024/06/15)
1. [aio.py]: 新增wavelogger处理界面
2. [wavelogger.py]: 新增精度数据处理模块
v0.1.6.1(2024/06/16)
1. [wavelogger.py]: bugfix single_file_proc函数中修改_start起始点的计算逻辑
2. [wavelogger.py]: bugfix find_point函数中当判断条件为临界值 2.0 的时候针对forward和backward两种情况对row_target做与判断逻辑相同的处理目的是避免形成死循环

View File

@ -1,353 +0,0 @@
from os.path import exists
from os import getcwd
from threading import Thread
import tkinter.messagebox
import customtkinter
import brake, current, iso
from time import time, strftime, localtime
from urllib.request import urlopen
import socket
customtkinter.set_appearance_mode("System") # Modes: "System" (standard), "Dark", "Light"
customtkinter.set_default_color_theme("blue") # Themes: "blue" (standard), "green", "dark-blue"
customtkinter.set_widget_scaling(1.1) # widget dimensions and text size
customtkinter.set_window_scaling(1.1) # window geometry dimensions
socket.setdefaulttimeout(10)
class App(customtkinter.CTk):
def __init__(self):
super().__init__()
self.my_font = customtkinter.CTkFont(family="Consolas", size=16, weight="bold")
self.w_param = 96
# =====================================================================
# 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.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)
# =====================================================================
# create frame sidebar(left)
self.frame_func = customtkinter.CTkFrame(self, width=120, corner_radius=0)
self.frame_func.grid(row=0, column=0, rowspan=7, sticky='nsew')
# create AIO logo
self.label_logo = customtkinter.CTkLabel(self.frame_func, text="Rokae AIO", height=60, font=customtkinter.CTkFont(family="Segoe Script Bold", size=24, weight="bold"), text_color="DarkSlateGray")
self.label_logo.grid(row=0, column=0, padx=15, pady=15)
# create start button
self.btn_start = customtkinter.CTkButton(self.frame_func, corner_radius=10, text='开始运行', font=self.my_font, command=lambda: self.thread_it(self.func_start_callback))
self.btn_start.grid(row=1, column=0, sticky='new', padx=10, pady=10, ipadx=5, ipady=5)
# create param check button
self.btn_check = customtkinter.CTkButton(self.frame_func, corner_radius=10, text='检查参数', font=self.my_font, command=lambda: self.thread_it(self.func_check_callback))
self.btn_check.grid(row=2, column=0, sticky='new', padx=10, pady=10, ipadx=5, ipady=5)
# create start button
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.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.3\nDate: 06/01/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)
# 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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.configure(state='disabled')
# =====================================================================
# version check
cur_vers = self.label_version.cget("text").replace('\n', ' @ ').replace("Vers: ", '').replace("Date: ", '')
url_vers = 'http://10.2.23.150:10008/vers'
try:
new_vers = urlopen(url_vers).read().decode('utf-8')
if cur_vers.strip() != new_vers.strip():
msg = f"""当前版本:{cur_vers}\n更新版本:{new_vers}\n\n请及时更新 http://10.2.23.150:10003/s/jRfM"""
tkinter.messagebox.showwarning(title="版本更新", message=msg)
except:
tkinter.messagebox.showwarning(title="版本更新", message="连接服务器失败,无法确认当前是否是最新版本......")
def thread_it(self, func, *args):
""" 将函数打包进线程 """
self.myThread = Thread(target=func, args=args)
self.myThread.daemon = True # 主线程退出就直接让子线程跟随退出,不论是否运行完成。
self.myThread.start()
def initialization(self):
self.label_path.configure(text="Path", text_color="black")
self.label_av.configure(text="AV", text_color="black")
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_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.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.option_axis.configure(state="disabled")
self.option_vel.configure(state="disabled")
self.option_trq.configure(state="disabled")
self.menu_sub.grid_forget()
self.textbox.delete(index1='1.0', index2='end')
self.textbox.configure(state='disabled')
def func_main_callback(self, func_name):
self.initialization()
if func_name == 'brake':
self.menu_sub = customtkinter.CTkOptionMenu(self.frame_param, values=["industrial", "cobot"], 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_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")
self.entry_rr.configure(state="normal")
self.option_axis.configure(state="normal")
self.option_vel.configure(state="normal")
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.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.entry_path.configure(state="normal")
self.entry_rc.configure(state="normal")
self.option_trq.configure(state="normal")
elif func_name == 'iso':
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.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
elif func_name == 'avg':
pass
def write2textbox(self, text, wait=0, exitcode=0):
if wait != 0:
self.textbox.configure(text_color='blue')
self.textbox.insert(index='end', text=text)
self.textbox.update()
self.textbox.see('end')
elif exitcode != 0:
self.textbox.configure(text_color='red')
self.textbox.insert(index='end', text=text + '\n')
self.textbox.update()
self.textbox.see('end')
raise Exception(f"Error code: {exitcode}")
else:
self.textbox.configure(text_color='blue')
self.textbox.insert(index='end', text=text + '\n')
self.textbox.update()
self.textbox.see('end')
def check_param(self):
func_name = self.menu_main.get()
if func_name == 'brake':
path = self.entry_path.get().strip(' ')
av = self.entry_av.get().strip('- ')
rr = self.entry_rr.get().strip('- ')
axis = self.option_axis.get()
vel = self.option_vel.get()
trq = self.option_trq.get()
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
if self.menu_sub.get() == 'industrial':
rpm = self.entry_rpm.get().strip('- ')
c4 = 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)
else:
return 0, 0
elif func_name == 'current':
path = self.entry_path.get()
rc = self.entry_rc.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', 'cycle']
if sub_func == 'cycle':
c3 = True if vel != trq else False
else:
c3 = 1
try:
_ = float(rc)
c4 = True
except ValueError:
c4 = False
if c1 and c2 and c3 and c4:
return 2, path, sub_func, float(rc), int(vel), int(trq)
else:
return 0, 0
elif func_name == 'iso':
path = self.entry_path.get()
c1 = exists(path)
if c1:
return 3, path
else:
return 0, 0
else:
return 0, 0
def func_start_callback(self):
self.textbox.configure(state='normal')
self.textbox.delete(index1='1.0', index2='end')
flag, *args = self.check_param()
func_dict = {1: brake.main, 2: current.main, 3: iso.main}
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], sub=args[1], rc=args[2], vel=args[3], trq=args[4], w2t=self.write2textbox)
elif flag == 3:
func_dict[flag](path=args[0], w2t=self.write2textbox)
else:
tkinter.messagebox.showerror(title="参数错误", message="请检查对应参数是否填写正确!", )
self.textbox.configure(state='disabled')
def func_check_callback(self):
flag, *args = self.check_param()
if flag:
tkinter.messagebox.showinfo(title="参数正确", message="所有参数形式上填写无误,可以开始运行!")
else:
tkinter.messagebox.showerror(title="参数错误", message="需要检查对应参数是否填写正确!", )
def func_log_callback(self):
content = self.textbox.get(index1='1.0', index2='end')
if len(content) > 1:
try:
now = strftime('%Y%m%d%H%M%S', localtime(time()))
log_name = f"{now}_aio.log"
with open(f'{log_name}', 'w', encoding='utf-8') as objlog:
objlog.write(content)
tkinter.messagebox.showinfo(title="保存成功", message=f'{log_name}已被保存存至↓↓↓\n{getcwd()}')
except Exception as Err:
print(Err)
tkinter.messagebox.showerror(title="保存失败", message="未能保存本次日志或未能完整保存请准备好相关数据联系fanmingfu@rokae.com查看详细信息", )
else:
tkinter.messagebox.showwarning(title="未能保存", message="日志数据为空,不可保存!")
def func_end_call_back(self):
if tkinter.messagebox.askyesno(title="关闭程序", message="相关数据可能未保存,正在运行程序时有概率会损坏数据文件,确定要终止程序运行吗?"):
self.destroy()
else:
pass
pass
if __name__ == "__main__":
aio = App()
aio.mainloop()

View File

@ -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, 3, 0),
prodvers=(0, 1, 3, 0),
filevers=(0, 1, 6, 1),
prodvers=(0, 1, 6, 1),
# Contains a bitmask that specifies the valid bits 'flags'r
mask=0x3f,
# Contains a bitmask that specifies the Boolean attributes of the file.
@ -31,12 +31,12 @@ VSVersionInfo(
'040904b0',
[StringStruct('CompanyName', 'Rokae - https://www.rokae.com/'),
StringStruct('FileDescription', 'All in one automatic toolbox'),
StringStruct('FileVersion', '0.1.3 (2024-06-01)'),
StringStruct('FileVersion', '0.1.6.1 (2024-06-16)'),
StringStruct('InternalName', 'AIO.exe'),
StringStruct('LegalCopyright', '© 2024-2024 Manford Fan'),
StringStruct('OriginalFilename', 'AIO.exe'),
StringStruct('ProductName', 'AIO'),
StringStruct('ProductVersion', '0.1.3 (2024-06-01)')])
StringStruct('ProductVersion', '0.1.6.1 (2024-06-16)')])
]),
VarFileInfo([VarStruct('Translation', [1033, 1200])])
]

View File

Before

Width:  |  Height:  |  Size: 162 KiB

After

Width:  |  Height:  |  Size: 162 KiB

BIN
aio/assets/layout.xlsx Normal file

Binary file not shown.

View File

@ -0,0 +1,9 @@
openpyxl==3.1.2
pdfplumber==0.11.0
customtkinter==5.2.2
Jinja2==3.1.4
lxml==5.2.2
numpy==1.26.4
pandas==2.2.2
pillow==10.3.0
pyinstaller==6.7.0

1
aio/assets/vers Normal file
View File

@ -0,0 +1 @@
0.1.6.1 @ 06/16/2024

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

370
aio/code/aio.py Normal file
View File

@ -0,0 +1,370 @@
from os.path import exists
from os import getcwd
from threading import Thread
import tkinter.messagebox
import customtkinter
import brake, current, iso, wavelogger
from time import time, strftime, localtime
from urllib.request import urlopen
from socket import setdefaulttimeout
customtkinter.set_appearance_mode("System") # Modes: "System" (standard), "Dark", "Light"
customtkinter.set_default_color_theme("blue") # Themes: "blue" (standard), "green", "dark-blue"
customtkinter.set_widget_scaling(1.1) # widget dimensions and text size
customtkinter.set_window_scaling(1.1) # window geometry dimensions
setdefaulttimeout(10)
# global vars
btns = {
'start': {'btn': '', 'row': 1, 'text': '开始运行'},
'check': {'btn': '', 'row': 2, 'text': '检查参数'},
'log': {'btn': '', 'row': 3, 'text': '保存日志'},
'end': {'btn': '', 'row': 4, 'text': '结束运行'},
}
widgits = {
'path': {'label': '', 'entry': '', 'row': 1, 'col': 2, 'text': '数据文件夹路径'},
'av': {'label': '', 'entry': '', 'row': 2, 'col': 2, 'text': '角速度'},
'rc': {'label': '', 'entry': '', 'row': 2, 'col': 4, 'text': '额定电流'},
'rpm': {'label': '', 'entry': '', 'row': 2, 'col': 6, 'text': '额定转速'},
'rr': {'label': '', 'entry': '', 'row': 2, 'col': 8, 'text': '减速比'},
'dur': {'label': '', 'entry': '', 'row': 2, 'col': 10, 'text': '周期时间'},
'axis': {'label': '', 'optionmenu': '', 'row': 3, 'col': 2, 'text': ''},
'vel': {'label': '', 'optionmenu': '', 'row': 3, 'col': 4, 'text': ''},
'trq': {'label': '', 'optionmenu': '', 'row': 3, 'col': 6, 'text': ''},
'trqh': {'label': '', 'optionmenu': '', 'row': 3, 'col': 8, 'text': ''},
'estop': {'label': '', 'optionmenu': '', 'row': 3, 'col': 10, 'text': ''},
'rc1': {'label': '', 'entry': '', 'row': 4, 'col': 2, 'text': '额定电流'},
'rc2': {'label': '', 'entry': '', 'row': 4, 'col': 4, 'text': '额定电流'},
'rc3': {'label': '', 'entry': '', 'row': 4, 'col': 6, 'text': '额定电流'},
'rc4': {'label': '', 'entry': '', 'row': 4, 'col': 8, 'text': '额定电流'},
'rc5': {'label': '', 'entry': '', 'row': 4, 'col': 10, 'text': '额定电流'},
'rc6': {'label': '', 'entry': '', 'row': 4, 'col': 12, 'text': '额定电流'},
}
class App(customtkinter.CTk):
def __init__(self):
super().__init__()
self.my_font = customtkinter.CTkFont(family="Consolas", size=16, weight="bold")
self.w_param = 84
# =====================================================================
# configure window
self.title("AIO - All in one automatic toolbox")
# self.iconbitmap('./icon.ico')
self.geometry("1200x550+30+30")
self.protocol("WM_DELETE_WINDOW", self.func_end_callback)
self.config(bg='#E9E9E9')
self.grid_rowconfigure(5, weight=1)
self.grid_columnconfigure((1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13), weight=1)
self.minsize(1200, 550)
# =====================================================================
# create frame sidebar(left)
self.frame_func = customtkinter.CTkFrame(self, width=120, corner_radius=0, fg_color='#E9E9E9')
self.frame_func.grid(row=0, column=0, rowspan=7, sticky='nsew')
# create AIO logo
self.label_logo = customtkinter.CTkLabel(self.frame_func, text="Rokae AIO", height=60, font=customtkinter.CTkFont(family="Segoe Script Bold", size=24, weight="bold"), text_color="#4F4F4F")
self.label_logo.grid(row=0, column=0, padx=15, pady=15)
# create buttons
for func in btns:
btns[func]['btn'] = customtkinter.CTkButton(self.frame_func, corner_radius=10, text=btns[func]['text'], fg_color='#4F4F4F', font=self.my_font)
btns[func]['btn'].grid(row=btns[func]['row'], column=0, sticky='new', padx=10, pady=10, ipadx=5, ipady=5)
btns['start']['btn'].configure(command=lambda: self.thread_it(self.func_start_callback))
btns['check']['btn'].configure(command=lambda: self.thread_it(self.func_check_callback))
btns['log']['btn'].configure(command=lambda: self.thread_it(self.func_log_callback))
btns['end']['btn'].configure(command=lambda: self.thread_it(self.func_end_callback))
# create version info
self.label_version = customtkinter.CTkLabel(self.frame_func, justify='left', text="Vers: 0.1.6.1\nDate: 06/16/2024", font=self.my_font, text_color="#4F4F4F")
self.frame_func.rowconfigure(6, weight=1)
self.label_version.grid(row=6, column=0, padx=20, pady=20, sticky='s')
# =====================================================================
# create tabviews
self.tabview = customtkinter.CTkTabview(self, width=10000, height=100, anchor='w', fg_color='#E9E9E9', border_width=2, border_color='#CDCDCD')
self.tabview.grid(row=0, column=1, padx=10, pady=5, sticky="nsew")
self.tabview.add("Data Process")
self.tabview.add("Automatic Test")
# create main menu
self.menu_main_dp = customtkinter.CTkOptionMenu(self.tabview.tab('Data Process'), values=["init", "brake", "current", "iso", "wavelogger"], font=self.my_font, text_color='yellow', button_color='red', fg_color='green', command=self.func_main_callback)
self.menu_main_dp.grid(row=1, column=1, sticky='we', padx=5, pady=5)
self.menu_main_dp.set("Start Here!")
# create sub menu
self.menu_sub_dp = customtkinter.CTkOptionMenu(self.tabview.tab('Data Process'))
# =====================================================================
# create widgits
for widgit in widgits:
if widgit == 'path':
widgits[widgit]['label'] = customtkinter.CTkLabel(self.tabview.tab('Data Process'), text=f'{widgit.upper()}', font=self.my_font)
widgits[widgit]['label'].grid(row=widgits[widgit]['row'], column=widgits[widgit]['col'], sticky='e', pady=5)
widgits[widgit]['entry'] = customtkinter.CTkEntry(self.tabview.tab('Data Process'), width=670, placeholder_text=widgits[widgit]['text'], font=self.my_font)
widgits[widgit]['entry'].grid(row=widgits[widgit]['row'], column=widgits[widgit]['col']+1, columnspan=11, padx=(5, 10), pady=5, sticky='we')
widgits[widgit]['entry'].configure(state='disabled')
elif widgit in ['av', 'rc', 'rpm', 'rr', 'dur', 'rc1', 'rc2', 'rc3', 'rc4', 'rc5', 'rc6']:
widgits[widgit]['label'] = customtkinter.CTkLabel(self.tabview.tab('Data Process'), text=f"{widgit.upper()}", font=self.my_font)
widgits[widgit]['label'].grid(row=widgits[widgit]['row'], column=widgits[widgit]['col'], sticky='e', pady=5)
widgits[widgit]['entry'] = customtkinter.CTkEntry(self.tabview.tab('Data Process'), width=self.w_param, placeholder_text=f"{widgits[widgit]['text']}", font=self.my_font)
widgits[widgit]['entry'].grid(row=widgits[widgit]['row'], column=widgits[widgit]['col']+1, padx=(5, 10), pady=5, sticky='w')
widgits[widgit]['entry'].configure(state='disabled')
elif widgit in ['axis', 'vel', 'trq', 'trqh', 'estop']:
widgits[widgit]['label'] = customtkinter.CTkLabel(self.tabview.tab('Data Process'), text=f"{widgit.upper()}", font=self.my_font)
widgits[widgit]['label'].grid(row=widgits[widgit]['row'], column=widgits[widgit]['col'], sticky='e', pady=5)
widgits[widgit]['optionmenu'] = customtkinter.CTkOptionMenu(self.tabview.tab('Data Process'), button_color='#708090', fg_color='#778899', values=["1", "2", "3", "4", "5", "6", "7"], width=self.w_param, font=self.my_font)
widgits[widgit]['optionmenu'].grid(row=widgits[widgit]['row'], column=widgits[widgit]['col']+1, padx=(5, 10), pady=5, sticky='w')
widgits[widgit]['optionmenu'].configure(state='disabled')
# =====================================================================
# create textbox
self.textbox = customtkinter.CTkTextbox(self, wrap='none', font=customtkinter.CTkFont(family="consolas", size=14), text_color="blue", fg_color='#E9E9E9', border_width=2, border_color='#CDCDCD', border_spacing=5)
self.textbox.grid(row=5, column=1, rowspan=2, columnspan=13, ipadx=10, ipady=10, padx=10, pady=(5, 10), sticky='nsew')
self.textbox.configure(state='disabled')
# =====================================================================
# version check
cur_vers = self.label_version.cget("text").replace('\n', ' @ ').replace("Vers: ", '').replace("Date: ", '')
url_vers = 'http://10.2.23.150:10008/vers'
try:
new_vers = urlopen(url_vers).read().decode('utf-8')
if cur_vers.strip() != new_vers.strip():
msg = f"""当前版本:{cur_vers}\n更新版本:{new_vers}\n\n请及时更新 http://10.2.23.150:10003/s/jRfM"""
tkinter.messagebox.showwarning(title="版本更新", message=msg)
except:
tkinter.messagebox.showwarning(title="版本更新", message="连接服务器失败,无法确认当前是否是最新版本......")
def thread_it(self, func, *args):
""" 将函数打包进线程 """
self.myThread = Thread(target=func, args=args)
self.myThread.daemon = True # 主线程退出就直接让子线程跟随退出,不论是否运行完成。
self.myThread.start()
def initialization(self):
for widgit in widgits:
if widgit in ['path', 'av', 'rc', 'rpm', 'rr', 'dur', 'rc1', 'rc2', 'rc3', 'rc4', 'rc5', 'rc6']:
widgits[widgit]['label'].configure(text=f'{widgit.upper()}', text_color='black')
widgits[widgit]['entry'].configure(placeholder_text=widgits[widgit]['text'], state='disabled')
elif widgit in ['axis', 'vel', 'trq', 'trqh', 'estop']:
widgits[widgit]['label'].configure(text=f'{widgit.upper()}', text_color="black")
widgits[widgit]['optionmenu'].configure(state='disabled')
self.menu_sub_dp.grid_forget()
self.textbox.delete(index1='1.0', index2='end')
self.textbox.configure(state='disabled')
def func_main_callback(self, func_name):
self.initialization()
if func_name == 'brake':
for widgit in widgits:
if widgit in ['path', 'av', 'rr']:
widgits[widgit]['label'].configure(text_color='red')
widgits[widgit]['entry'].configure(state='normal')
elif widgit in ['axis', 'vel', 'trq', 'estop']:
widgits[widgit]['label'].configure(text_color="red")
widgits[widgit]['optionmenu'].configure(state='normal')
elif func_name == 'current':
self.menu_sub_dp = customtkinter.CTkOptionMenu(self.tabview.tab('Data Process'), values=["max", "avg", "cycle"], font=self.my_font, button_color='red', fg_color='green', command=self.func_sub_callback)
self.menu_sub_dp.grid(row=2, column=1, sticky='we', padx=5, pady=5)
self.menu_sub_dp.set("--select--")
self.menu_sub_dp.configure(text_color='yellow')
for widgit in widgits:
if widgit in ['path', 'rc', 'rc1', 'rc2', 'rc3', 'rc4', 'rc5', 'rc6']:
color = 'blue' if widgit == 'rc' else 'red'
widgits[widgit]['label'].configure(text_color=color)
widgits[widgit]['entry'].configure(state='normal')
elif widgit in ['trqh',]:
widgits[widgit]['label'].configure(text_color="red")
widgits[widgit]['optionmenu'].configure(state='normal')
elif func_name == 'iso' or func_name == 'wavelogger':
for widgit in widgits:
if widgit in ['path',]:
widgits[widgit]['label'].configure(text_color='red')
widgits[widgit]['entry'].configure(state='normal')
else:
self.initialization()
self.menu_main_dp.set("Start Here!")
def func_sub_callback(self, func_name):
if func_name == "max":
for widgit in widgits:
if widgit in ['rpm', 'dur']:
widgits[widgit]['label'].configure(text_color='black')
widgits[widgit]['entry'].configure(state='disabled')
elif widgit in ['vel', 'trq']:
widgits[widgit]['label'].configure(text_color='black')
widgits[widgit]['optionmenu'].configure(state='disabled')
elif func_name == 'avg':
for widgit in widgits:
if widgit in ['rpm', 'dur']:
widgits[widgit]['label'].configure(text_color='black')
widgits[widgit]['entry'].configure(state='disabled')
elif widgit in ['vel', 'trq']:
widgits[widgit]['label'].configure(text_color='black')
widgits[widgit]['optionmenu'].configure(state='disabled')
elif func_name == 'cycle':
for widgit in widgits:
if widgit in ['rpm', 'dur']:
widgits[widgit]['label'].configure(text_color='blue')
widgits[widgit]['entry'].configure(state='normal')
elif widgit in ['vel', 'trq']:
widgits[widgit]['label'].configure(text_color="red")
widgits[widgit]['optionmenu'].configure(state='normal')
def write2textbox(self, text, wait=0, exitcode=0, color='blue'):
self.textbox.tag_add(color, 'insert', 'end')
self.textbox.tag_config(tagName=color, foreground=color)
if wait != 0:
self.textbox.insert(index='end', text=text, tags=color)
self.textbox.update()
self.textbox.see('end')
elif exitcode != 0:
self.textbox.insert(index='end', text=text + '\n', tags=color)
self.textbox.update()
self.textbox.see('end')
self.textbox.configure(state='disabled')
raise Exception(f"Error code: {exitcode}")
else:
self.textbox.insert(index='end', text=text + '\n', tags=color)
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参数数据缺失,或者数据类型错误,更正后重新运行...\n", 0, 3, 'red')
return True
def check_param(self):
func_name = self.menu_main_dp.get()
if func_name == 'brake':
path = widgits['path']['entry'].get().strip()
av = widgits['av']['entry'].get().strip('- ')
rr = widgits['rr']['entry'].get().strip('- ')
axis = widgits['axis']['optionmenu'].get()
vel = widgits['vel']['optionmenu'].get()
trq = widgits['trq']['optionmenu'].get()
estop = widgits['estop']['optionmenu'].get()
c1 = exists(path)
c2 = self.is_float('required', av, rr)
c3 = True if len({vel, trq, estop}) == 3 else False
if c1 and c2 and c3:
return 1, path, float(av), float(rr), int(axis), int(vel), int(trq), int(estop)
else:
return 0, 0
# =======================================================
elif func_name == 'current':
path = widgits['path']['entry'].get().strip()
rc = widgits['rc']['entry'].get().strip('- ')
rpm = widgits['rpm']['entry'].get().strip()
dur = widgits['dur']['entry'].get().strip()
rc1 = widgits['rc1']['entry'].get().strip()
rc2 = widgits['rc2']['entry'].get().strip()
rc3 = widgits['rc3']['entry'].get().strip()
rc4 = widgits['rc4']['entry'].get().strip()
rc5 = widgits['rc5']['entry'].get().strip()
rc6 = widgits['rc6']['entry'].get().strip()
vel = widgits['vel']['optionmenu'].get()
trq = widgits['trq']['optionmenu'].get()
trqh = widgits['trqh']['optionmenu'].get()
sub = self.menu_sub_dp.get()
c1 = exists(path)
c2 = sub in ['max', 'avg', 'cycle']
c3 = self.is_float('optional', rc, rpm)
c4 = self.is_float('required', rc1, rc2, rc3, rc4, rc5, rc6)
c5 = c6 = True
if sub == 'cycle':
c5 = True if len({vel, trq}) == 2 else False
c6 = self.is_float('optional', dur)
if c1 and c2 and c3 and c4 and c5 and c6:
rcs = []
for x in [rc1, rc2, rc3, rc4, rc5, rc6]:
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 = widgits['path']['entry'].get().strip()
c1 = exists(path)
if c1:
return 3, path
else:
return 0, 0
# =======================================================
elif func_name == 'wavelogger':
path = widgits['path']['entry'].get().strip()
c1 = exists(path)
if c1:
return 4, path
else:
return 0, 0
# =======================================================
else:
return 0, 0
def func_start_callback(self):
self.textbox.configure(state='normal')
self.textbox.delete(index1='1.0', index2='end')
flag, *args = self.check_param()
func_dict = {1: brake.main, 2: current.main, 3: iso.main, 4: wavelogger.main}
if flag == 1:
func_dict[flag](path=args[0], av=args[1], rr=args[2], axis=args[3], vel=args[4], trq=args[5], estop=args[6], w2t=self.write2textbox)
elif flag == 2:
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)
elif flag == 4:
func_dict[flag](path=args[0], w2t=self.write2textbox)
else:
tkinter.messagebox.showerror(title="参数错误", message="请检查对应参数是否填写正确!", )
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')
if len(content) > 1:
try:
now = strftime('%Y%m%d%H%M%S', localtime(time()))
log_name = f"{now}_aio.log"
with open(f'{log_name}', 'w', encoding='utf-8') as objlog:
objlog.write(content)
tkinter.messagebox.showinfo(title="保存成功", message=f'{log_name}已被保存存至↓↓↓\n{getcwd()}')
except Exception as Err:
self.write2textbox(Err)
tkinter.messagebox.showerror(title="保存失败", message="未能保存本次日志或未能完整保存请准备好相关数据联系fanmingfu@rokae.com查看详细信息", )
else:
tkinter.messagebox.showwarning(title="未能保存", message="日志数据为空,不可保存!")
def func_end_callback(self):
if tkinter.messagebox.askyesno(title="关闭程序", message="相关数据可能未保存,正在运行程序时有概率会损坏数据文件,确定要终止程序运行吗?"):
self.destroy()
if __name__ == "__main__":
aio = App()
aio.mainloop()

View File

@ -27,12 +27,12 @@ class GetThreadResult(Thread):
return None
def data_process(result_file, raw_data_dirs, av, rr, axis, vel, trq, w2t, rpm):
def data_process(result_file, raw_data_dirs, av, rr, axis, vel, trq, w2t, estop):
# 功能:完成一个结果文件的数据处理
# 参数:结果文件,数据目录,以及预读取的参数
# 返回值:-
file_name = result_file.split('\\')[-1]
w2t(f"打开文件 {file_name} 需要 1min 左右", 1)
w2t(f"正在打开文件 {file_name} 需要 1min 左右", 1, 0, 'orange')
global stop
stop = 0
@ -55,21 +55,21 @@ def data_process(result_file, raw_data_dirs, av, rr, axis, vel, trq, w2t, rpm):
# count = 1
# for data_file in data_files:
# now_doing_msg(data_file, 'start', w2t)
# single_file_process(data_file, wb_result, count, av, rr, axis, vel, trq, w2t, rpm)
# single_file_process(data_file, wb_result, count, av, rr, axis, vel, trq, w2t, estop)
# count += 1
# now_doing_msg(data_file, 'done', w2t)
# ---------------------------------------------------
# 数据文件并行处理模式---------------------------------
threads = [Thread(target=single_file_process, args=(data_files[0], wb_result, 1, av, rr, axis, vel, trq, w2t, rpm)),
Thread(target=single_file_process, args=(data_files[1], wb_result, 2, av, rr, axis, vel, trq, w2t, rpm)),
Thread(target=single_file_process, args=(data_files[2], wb_result, 3, av, rr, axis, vel, trq, w2t, rpm))]
threads = [Thread(target=single_file_process, args=(data_files[0], wb_result, 1, av, rr, axis, vel, trq, w2t, estop)),
Thread(target=single_file_process, args=(data_files[1], wb_result, 2, av, rr, axis, vel, trq, w2t, estop)),
Thread(target=single_file_process, args=(data_files[2], wb_result, 3, av, rr, axis, vel, trq, w2t, estop))]
[t.start() for t in threads]
[t.join() for t in threads]
# ---------------------------------------------------
now_doing_msg(raw_data_dir, 'done', w2t)
now_doing_msg(result_file, 'done', w2t)
w2t(f"保存文件 {file_name} 需要 1min 左右", 1)
w2t(f"正在保存文件 {file_name} 需要 1min 左右", 1, 0, 'orange')
stop = 0
t_excel = Thread(target=wb_result.save, args=(result_file, ))
t_wait = Thread(target=w2t_local, args=('.', 1, w2t))
@ -89,41 +89,40 @@ def check_files(raw_data_dirs, result_files, w2t):
msg = "结果文件数目错误,结果文件有且只有三个,请确认!"
for result_file in result_files:
w2t(result_file)
w2t(msg, 0, 2)
w2t(msg, 0, 2, 'red')
prefix = []
for result_file in result_files:
prefix.append(result_file.split('\\')[-1].split('_')[0])
if not sorted(prefix) == sorted(['load33', 'load66', 'load100']):
if not sorted(prefix) == sorted(['reach33', 'reach66', 'reach100']):
wd = result_files[0].split('\\')
del wd[-1]
wd = '\\'.join(wd)
msg = f"""请关闭所有相关数据文件,并检查工作目录 {wd} 下,有且只允许有类似如下三个文件:
1. load33_自研_制动性能测试.xlsx
2. load66_自研_制动性能测试.xlsx
3. load100_自研_制动性能测试.xlsx"""
w2t(msg, 0, 3)
1. reach33_XXX制动性能测试.xlsx
2. reach66_XXX制动性能测试.xlsx
3. reach100_XX制动性能测试.xlsx"""
w2t(msg, 0, 3, 'red')
for raw_data_dir in raw_data_dirs:
components = raw_data_dir.split('\\')[-1].split('_')
sorted(components)
if components[0] not in ['load33', 'load66', 'load100'] or \
components[1] not in ['speed33', 'speed66', 'speed100'] or \
components[2] not in ['reach33', 'reach66', 'reach100']:
msg = f"报错信息:数据目录 {raw_data_dir} 命名不合规,请参考如下形式\n" \
f"命名规则:\n 1. loadAA_speedBB_reachCC\n 2. loadAA_reachBB_speedCC\n" \
f"规则解释AA/BB/CC 指的是负载/速度/臂展的比例\n" \
f"load66_speed100_reach3366% 负载100% 速度以及 33% 臂展情况下的测试结果文件夹"
w2t(msg, 0, 4)
if components[0] not in ['reach33', 'reach66', 'reach100'] or \
components[1] not in ['load33', 'load66', 'load100'] or \
components[2] not in ['speed33', 'speed66', 'speed100']:
msg = f"""报错信息:数据目录 {raw_data_dir} 命名不合规,请参考如下形式
命名规则reachAA_loadBB_speedCC
规则解释AA/BB/CC 指的是臂展/负载/速度的比例例如reach66_load100_speed3366%臂展100%负载以及33%速度情况下的测试结果文件夹"""
w2t(msg, 0, 4, 'red')
_, raw_data_files = traversal_files(raw_data_dir, w2t)
if len(raw_data_files) != 3:
msg = f"数据目录 {raw_data_dir} 下数据文件个数错误,每个数据目录下有且只能有三个以 .data 为后缀的数据文件"
w2t(msg, 0, 5)
w2t(msg, 0, 5, 'red')
for raw_data_file in raw_data_files:
if not (raw_data_file.split('\\')[-1].endswith('.data') or raw_data_file.split('\\')[-1].endswith('.csv')):
msg = f"数据文件 {raw_data_file} 后缀错误,每个数据目录下有且只能有三个以 .data/csv 为后缀的数据文件"
w2t(msg, 0, 6)
if not raw_data_file.split('\\')[-1].endswith('.data'):
msg = f"数据文件 {raw_data_file} 后缀错误,每个数据目录下有且只能有三个以 .data 为后缀的数据文件"
w2t(msg, 0, 6, 'red')
w2t("数据目录合规性检查结束,未发现问题......")
@ -139,9 +138,9 @@ def now_doing_msg(docs, flag, w2t):
elif flag == 'start' and file_type == 'file':
w2t(f"[{now}] 正在处理文件 {docs} 中的数据......")
elif flag == 'done' and file_type == 'dir':
w2t(f"[{now}] 目录 {docs} 数据文件已处理完毕......")
w2t(f"[{now}] 目录 {docs} 数据文件已处理完毕")
elif flag == 'done' and file_type == 'file':
w2t(f"[{now}] 文件 {docs} 数据已处理完毕......")
w2t(f"[{now}] 文件 {docs} 数据已处理完毕")
def w2t_local(msg, wait, w2t):
@ -149,122 +148,94 @@ def w2t_local(msg, wait, w2t):
global stop
if stop == 0 and wait != 0:
sleep(1)
w2t(msg, wait)
w2t(msg, wait, 0, 'orange')
else:
break
def single_file_process(data_file, wb_result, count, av, rr, axis, vel, trq, w2t, rpm):
def single_file_process(data_file, wb_result, count, av, rr, axis, vel, trq, w2t, estop):
# 功能:完成单个数据文件的处理
# 参数:如上
# 返回值:-
if data_file.endswith('.data'):
sep = '\t'
df = read_csv(data_file, sep=sep)
elif data_file.endswith('.csv'):
sep = ','
df = read_csv(data_file, sep=sep, encoding='gbk', header=8)
df = read_csv(data_file, sep='\t')
conditions = sorted(data_file.split('\\')[-2].split('_')[1:])
conditions = sorted(data_file.split('\\')[-2].split('_')) # ['loadxx', 'reachxx', 'speedxx']
result_sheet_name = find_result_sheet_name(conditions, count)
ws_result = wb_result[result_sheet_name]
row_max, row_start, flag = find_row_start(data_file, df, conditions, av, rr, axis, vel, w2t, rpm)
copy_data_to_result(flag, df, ws_result, row_max, row_start, vel, trq, rpm)
ws_result["C2"] = int(2)
ws_result["G2"] = int(10+4)
row_start, row_end = find_row_start(data_file, df, conditions, av, rr, axis, vel, w2t, estop)
copy_data_to_result(df, ws_result, row_start, row_end, vel, trq, estop)
def copy_data_to_result(flag, df, ws_result, row_max, row_start, vel, trq, rpm):
def copy_data_to_result(df, ws_result, row_start, row_end, vel, trq, estop):
# 功能:将数据文件中有效数据拷贝至结果文件对应的 sheet
# 参数:如上
# 返回值:-
# 结果文件数据清零
for row in ws_result.iter_rows(min_row=2, min_col=1, max_row=2000, max_col=2):
for cell in row:
cell.value = None
if flag == 1:
# 将合适的数据复制到结果文件
row_max = row_start + 399 if row_max-row_start > 400 else row_max
rc = 1 if rpm == 1 else 1000
data = []
for _row in range(row_start, row_end + 1):
data.append(df.iloc[_row, vel-1])
data.append(df.iloc[_row, trq-1])
data.append(df.iloc[_row, estop-1])
data = []
for i in range(row_start, row_max+1):
data.append(df.iloc[i, vel-1] * rpm)
data.append(df.iloc[i, trq-1] * rc)
i = 0
for row in ws_result.iter_rows(min_row=2, min_col=1, max_row=row_max - row_start + 2, max_col=2):
for cell in row:
cell.value = data[i]
i = i + 1
else:
pass
i = 0
row_max = 2000 if row_end-row_start < 2000 else row_end-row_start+20
for _row in range(2, row_max):
try:
ws_result.cell(row=_row, column=1).value = data[i]
ws_result.cell(row=_row, column=2).value = data[i+1]
ws_result.cell(row=_row, column=3).value = data[i+2]
i += 3
except:
ws_result.cell(row=_row, column=1).value = None
ws_result.cell(row=_row, column=2).value = None
ws_result.cell(row=_row, column=3).value = None
def find_result_sheet_name(conditions, count):
# 功能获取结果文件准确的sheet页名称
# 参数:臂展和速度的列表
# 返回值结果文件对应的sheet name
# 33%臂展_33%速度_1
reach = conditions[0].removeprefix('reach')
speed = conditions[1].removeprefix('speed')
result_sheet_name = f"{reach}%臂展_{speed}%速度_{count}"
# 33%负载_33%速度_1 - ['loadxx', 'reachxx', 'speedxx']
load = conditions[0].removeprefix('load')
speed = conditions[2].removeprefix('speed')
result_sheet_name = f"{load}%负载_{speed}%速度_{count}"
return result_sheet_name
def find_row_start(data_file, df, conditions, av, rr, axis, vel, w2t, rpm):
def find_row_start(data_file, df, conditions, av, rr, axis, vel, w2t, estop):
# 功能:查找数据文件中有效数据的行号,也即最后一个速度下降的点位
# 参数:如上
# 返回值:速度下降点位,最后的数据点位
ratio = float(conditions[1].removeprefix('speed'))/100
speed_max = av * ratio * rr / 6
row_max = row_start = df.index[-1]
ratio = float(conditions[2].removeprefix('speed'))/100
av_max = av * ratio
row_max = df.index[-1]
# threshold = 30 if axis == 2 and conditions[0].removeprefix('load') == '100' else 10
threshold = 0.95
threshold, step = get_threshold_step(data_file, axis)
while row_start > step+1:
speed = abs(df.iloc[row_start, vel-1] * rpm)
if int(speed) < 1:
row_start -= 50
continue
_ = []
for i in range(row_start, row_start-step+1, -1):
_.append(df.iloc[i, vel-1] * rpm)
speed_avg = abs(sum(_))/len(_)
if abs(speed_avg-speed_max) < threshold:
row_start = row_start - 10
flag = 1
for _row in range(row_max, -1, -1):
if df.iloc[_row, estop-1] != 0:
row_start = _row-20 if _row-20 > 0 else 0
break
else:
row_start -= step
else:
msg = f"可能是{data_file}这个文件数据采集有问题,比如未采集理论速度值,也有可能是程序步长设定问题,请检查......"
w2t(msg)
flag = 0
w2t(f"数据文件 {data_file} 采集的数据中没有 ESTOP 为非 0 的情况,需要确认", 0, 9, 'red')
return row_max, row_start, flag
def get_threshold_step(excel_file, axis):
# 功能负载和速度100%且是j2的时候做特殊处理
# 参数新生成的excel轴号
# 返回值:速度差阈值,处理步长
conditions = sorted(excel_file.split('\\')[-2].split('_'))
# 只有负载和速度是100%时才会启用更敏感的step
flg = 1 if conditions[0][-3:] == '100' and conditions[2][-3:] == '100' else 0
if flg == 1 and axis == 2:
threshold = 30
step = 5
for _row in range(row_start, row_max):
speed_row = (df.iloc[_row, vel-1] * 180) / 3.1415926 * rr * 60 / 360
if abs(speed_row) < 1:
row_end = _row+100 if _row+100 <= row_max else row_max
break
else:
threshold = 10
step = 5
w2t(f"数据文件 {data_file} 最后的速度未降为零 ", 0, 10, 'red')
return threshold, step
av_estop = abs((df.iloc[row_start-10:row_start+10, vel-1].abs().mean() * 180) / 3.1415926)
if abs(av_estop/av_max) < threshold:
filename = data_file.split('\\')[-1]
w2t(f"[av_estop: {av_estop:.2f} | shouldbe: {av_max:.2f}] 数据文件 {filename} 触发 ESTOP 时未采集到指定百分比的最大速度,需要检查", 0, 0, '#8A2BE2')
return row_start, row_end
def traversal_files(path, w2t):
@ -273,7 +244,7 @@ def traversal_files(path, w2t):
# 返回值:路径下的文件夹列表 路径下的文件列表
if not exists(path):
msg = f'数据文件夹{path}不存在,请确认后重试......'
w2t(msg, 0, 1)
w2t(msg, 0, 1, 'red')
else:
dirs = []
files = []
@ -286,32 +257,33 @@ def traversal_files(path, w2t):
return dirs, files
def main(path, av, rr, rpm, axis, vel, trq, w2t):
def main(path, av, rr, axis, vel, trq, estop, w2t):
# 功能:执行处理所有数据文件
# 参数initialization函数的返回值
# 返回值:-
time_start = time()
raw_data_dirs, result_files = traversal_files(path, w2t)
prefix = []
for raw_data_dir in raw_data_dirs:
prefix.append(raw_data_dir.split('\\')[-1].split("_")[0])
try:
# threads = []
check_files(raw_data_dirs, result_files, w2t)
prefix = []
for raw_data_dir in raw_data_dirs:
prefix.append(raw_data_dir.split('\\')[-1].split("_")[0])
for result_file in result_files:
if result_file.split('\\')[-1].split('_')[0] not in set(prefix):
continue
else:
now_doing_msg(result_file, 'start', w2t)
data_process(result_file, raw_data_dirs, av, rr, axis, vel, trq, w2t, rpm)
data_process(result_file, raw_data_dirs, av, rr, axis, vel, trq, w2t, estop)
# threads.append(Thread(target=data_process, args=(result_file, raw_data_dirs, AV, RR, RC, AXIS)))
# [t.start() for t in threads]
# [t.join() for t in threads]
except Exception as Err:
msg = f"出现错误:{Err}\n程序运行错误,请检查配置文件是否准确设定,以及数据文件组织是否正确,也有可能是结果文件损坏,尝试重新复制一份,再运行!"
w2t(msg)
w2t(msg, 0, 11, 'red')
w2t("----------------------------------------------------------")
w2t("全部处理完毕")
@ -323,4 +295,4 @@ def main(path, av, rr, rpm, axis, vel, trq, w2t):
if __name__ == "__main__":
stop = 0
main(path=argv[1], av=argv[2], rr=argv[3], rpm=argv[4], axis=argv[5], vel=argv[6], trq=argv[7], w2t=argv[8])
main(path=argv[1], av=argv[2], rr=argv[3], axis=argv[4], vel=argv[5], trq=argv[6], estop=argv[7], w2t=argv[8])

355
aio/code/current.py Normal file
View File

@ -0,0 +1,355 @@
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 w2t_local(msg, wait, w2t):
while True:
global stop
if stop == 0 and wait != 0:
sleep(1)
w2t(msg, wait, 0, 'orange')
else:
break
def traversal_files(path, w2t):
# 功能:以列表的形式分别返回指定路径下的文件和文件夹,不包含子目录
# 参数:路径
# 返回值:路径下的文件夹列表 路径下的文件列表
if not exists(path):
msg = f'数据文件夹{path}不存在,请确认后重试......'
w2t(msg, 0, 8, 'red')
else:
dirs = []
files = []
for item in scandir(path):
if item.is_dir():
dirs.append(item.path)
elif item.is_file():
files.append(item.path)
return dirs, files
def initialization(path, sub, w2t):
_, data_files = traversal_files(path, w2t)
count = 0
for data_file in data_files:
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, 'red')
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, 'red')
if sub == 'cycle' and count != 1:
w2t("未找到电机电流数据处理excel表格确认后重新运行", 0, 5, 'red')
return data_files
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:
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()
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(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:
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()
scale = 1 if data_file.endswith('.csv') else 1000
_ = (abs(c_avg)+c_std)/scale*rca
current[axis] = _
w2t(f"{data_file}: {_:.4f}")
w2t("【AVG】数据处理完毕......")
return current
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, 0, 'orange')
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, 0, 'orange')
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 find_point(data_file, pos, 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"[{pos}] {data_file}数据有误,需要检查,无法找到第{exitcode}个有效点...", 0, exitcode, 'red')
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"[{pos}] {data_file}数据有误,需要检查,无法找到有效起始点或结束点...", 0, exitcode, 'red')
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(data_file, 'a1', 'lt', df, _row_s, _row_e, w2t, 1, threshold=5, 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(data_file, 'a2', 'gt', df, _row_s, _row_e, w2t, 3, threshold=5, step=_step, end_point=_end_point)
# 速度已经快要降为零了,继续寻找下一个速度上升点
_row_e -= _end_point
_row_s -= _end_point
_row_s, _row_e = find_point(data_file, 'a3', 'lt', df, _row_s, _row_e, w2t, 3, threshold=5, step=_step, end_point=_end_point)
elif speed_avg > 2:
# 过滤尾部非零无效数据
_row_s, _row_e = find_point(data_file, 'b1', 'gt', df, _row_s, _row_e, w2t, 2, threshold=5, 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(data_file, 'b2', 'lt', df, _row_s, _row_e, w2t, 4, threshold=5, step=_step, end_point=_end_point)
# 目前已经有一点的速度值了,继续往前搜寻下一个速度为零的点
_row_e -= _end_point
_row_s -= _end_point
_row_s, _row_e = find_point(data_file, 'b3', 'gt', df, _row_s, _row_e, w2t, 4, threshold=5, 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=70000, 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, 'red')
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=70000, 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__':
stop = 0
main(*argv[1:])

View File

@ -12,7 +12,7 @@ def traversal_files(path, w2t):
# 返回值:路径下的文件夹列表 路径下的文件列表
if not exists(path):
msg = f'数据文件夹{path}不存在,请确认后重试......'
w2t(msg, 0, 1)
w2t(msg, 0, 1, 'red')
else:
dirs = files = []
for item in scandir(path):
@ -162,7 +162,7 @@ def main(path, w2t):
ws.cell(row=i, column=7).value = None
tmpfile = f"{path}\\data.txt"
except Exception as Err:
w2t(f"发生错误:{Err}", 0, 2)
w2t(f"发生错误:{Err}", 0, 2, 'red')
p_files = []
for file in files:
@ -187,7 +187,7 @@ def main(path, w2t):
wb.close()
if len(p_files) == 0:
w2t(f"目录 {path} 下没有需要处理的文件,需要确认......", 0, 3)
w2t(f"目录 {path} 下没有需要处理的文件,需要确认......", 0, 3, 'red')
else:
remove(tmpfile)
w2t("------------------------------------------")

186
aio/code/wavelogger.py Normal file
View File

@ -0,0 +1,186 @@
import os
import random
from pandas import read_csv
from csv import reader
from sys import argv
from os.path import exists
from os import scandir, remove
from openpyxl import Workbook
from random import randint
def traversal_files(path, w2t):
# 功能:以列表的形式分别返回指定路径下的文件和文件夹,不包含子目录
# 参数:路径
# 返回值:路径下的文件夹列表 路径下的文件列表
if not exists(path):
msg = f'数据文件夹{path}不存在,请确认后重试......'
w2t(msg, 0, 1, 'red')
else:
dirs = []
files = []
for item in scandir(path):
if item.is_dir():
dirs.append(item.path)
elif item.is_file():
files.append(item.path)
return dirs, files
def find_point(bof, step, pos, data_file, flag, df, row, w2t):
# bof: backward or forward
# pos: used for debug
# flag: greater than or lower than
if flag == 'gt':
while 0 < row < df.index[-1]-100:
_value = df.iloc[row, 2]
if _value > 2:
if bof == 'backward':
row -= step
elif bof == 'forward':
row += step
continue
else:
if bof == 'backward':
row_target = row - step
elif bof == 'forward':
row_target = row + step
break
else:
if bof == 'backward':
w2t(f"[{pos}] 在 {data_file} 中,无法正确识别数据,需要确认...", 0, 2, 'red')
elif bof == 'forward':
row_target = row + 100
elif flag == 'lt':
while 0 < row < df.index[-1]-100:
_value = df.iloc[row, 2]
if _value < 2:
if bof == 'backward':
row -= step
elif bof == 'forward':
row += step
continue
else:
if bof == 'backward':
row_target = row - step
elif bof == 'forward':
row_target = row + step
break
else:
if bof == 'backward':
w2t(f"[{pos}] 在 {data_file} 中,无法正确识别数据,需要确认...", 0, 3, 'red')
elif bof == 'forward':
row_target = row + 100
return row_target
def get_cycle_info(data_file, df, row, step, w2t):
# end -> middle: low
# middle -> start: high
# 1. 从最后读取数据无论是大于1还是小于1都舍弃找到相反的值的起始点
# 2. 从起始点,继续往前寻找,找到与之数值相反的中间点
# 3. 从中间点,继续往前寻找,找到与之数值相反的结束点,至此,得到了高低数值的时间区间以及一轮的周期时间
if df.iloc[row, 2] < 2:
row = find_point('backward', step, 'a1', data_file, 'lt', df, row, w2t)
_row = find_point('backward', step, 'a2', data_file, 'gt', df, row, w2t)
_row = find_point('backward', step, 'a3', data_file, 'lt', df, _row, w2t)
row_end = find_point('backward', step, 'a4', data_file, 'gt', df, _row, w2t)
row_middle = find_point('backward', step, 'a5', data_file, 'lt', df, row_end, w2t)
row_start = find_point('backward', step, 'a6', data_file, 'gt', df, row_middle, w2t)
return row_end-row_middle, row_middle-row_start, row_end-row_start
def initialization(path, w2t):
_, data_files = traversal_files(path, w2t)
for data_file in data_files:
if not data_file.lower().endswith('.csv'):
w2t(f"{data_file} 文件后缀错误,只允许 .csv 文件,需要确认!", 0, 1, 'red')
return data_files
def preparation(data_file, wb, w2t):
shtname = data_file.split('\\')[-1].split('.')[0]
ws = wb.create_sheet(shtname)
csv_reader = reader(open(data_file))
i = 0
begin = 70
for row in csv_reader:
i += 1
if i == 1:
begin = int(row[1])
break
df = read_csv(data_file, sep=',', encoding='gbk', skip_blank_lines=False, header=begin - 1, on_bad_lines='warn')
low, high, cycle = get_cycle_info(data_file, df, df.index[-1]-110, 5, w2t)
return ws, df, low, high, cycle
def single_file_proc(ws, data_file, df, low, high, cycle, w2t):
_row = _row_lt = _row_gt = count = 1
_step = 5
_data = {}
row_max = df.index[-1]-100
print(data_file)
while _row < row_max:
if count not in _data.keys():
_data[count] = []
_value = df.iloc[_row, 2]
if _value < 2:
_row_lt = find_point('forward', _step, 'c'+str(_row), data_file, 'lt', df, _row, w2t)
_start = int(_row_gt + (_row_lt - _row_gt - 50) / 2)
_end = _start + 50
value = df.iloc[_start:_end, 2].mean() + df.iloc[_start:_end, 2].std()
_data[count].append(value)
else:
_row_gt = find_point('forward', _step, 'c'+str(_row), data_file, 'gt', df, _row, w2t)
if _row_gt - _row_lt > cycle * 2:
count += 1
_row = max(_row_gt, _row_lt)
for i in range(2, 10):
ws.cell(row=1, column=i).value = f"{i-1}次测试"
ws.cell(row=i, column=1).value = f"{i-1}次精度变化"
print(_data)
for i in sorted(_data.keys()):
_row = 2
_column = i + 1
for value in _data[i]:
ws.cell(row=_row, column=_column).value = float(value)
_row += 1
def execution(data_files, w2t):
wb = Workbook()
for data_file in data_files:
ws, df, low, high, cycle = preparation(data_file, wb, w2t)
print(f"low = {low}")
print(f"high = {high}")
print(f"cycle = {cycle}")
single_file_proc(ws, data_file, df, low, high, cycle, w2t)
wd = data_files[0].split('\\')
del wd[-1]
wd = '\\'.join(wd)
filename = wd + '\\result.xlsx'
wb.save(filename)
wb.close()
w2t('----------------------------------------')
w2t('所有文件均已处理完毕')
def main(path, w2t):
data_files = initialization(path, w2t)
execution(data_files, w2t)
if __name__ == '__main__':
main(path=argv[1], w2t=argv[2])

View File

@ -1,70 +0,0 @@
import openpyxl
from os import scandir
from os.path import exists
from sys import argv
import pandas
def traversal_files(path, w2t):
# 功能:以列表的形式分别返回指定路径下的文件和文件夹,不包含子目录
# 参数:路径
# 返回值:路径下的文件夹列表 路径下的文件列表
if not exists(path):
msg = f'数据文件夹{path}不存在,请确认后重试......'
w2t(msg, 0, 1)
else:
dirs = []
files = []
for item in scandir(path):
if item.is_dir():
dirs.append(item.path)
elif item.is_file():
files.append(item.path)
return dirs, files
def initialization(path, w2t):
_, data_files = traversal_files(path, w2t)
for data_file in data_files:
if not data_file.endswith('.data'):
msg = f"所有文件必须以 .data 结尾,请检查后重新运行。"
w2t(msg, 0, 2)
return data_files
def current_max(data_files, rc, trq, w2t):
for data_file in data_files:
df = pandas.read_csv(data_file, sep='\t')
col = df.columns.values[trq-1]
c_max = df[col].max()
w2t(f"{data_file}: {c_max/1000*rc:.4f}")
w2t("【MAX】数据处理完毕......")
def current_avg(data_files, rc, trq, w2t):
for data_file in data_files:
df = pandas.read_csv(data_file, sep='\t')
col = df.columns.values[trq-1]
c_std = df[col].std()
c_avg = df[col].mean()
w2t(f"{data_file}: {(abs(c_avg)+c_std)/1000*rc:.4f}")
w2t("【AVG】数据处理完毕......")
def main(path, sub, rc, vel, trq, w2t):
data_files = initialization(path, w2t)
if sub == 'max':
current_max(data_files, rc, trq, w2t)
elif sub == 'avg':
current_avg(data_files, rc, trq, w2t)
else:
pass
if __name__ == '__main__':
main(path=argv[1], sub=argv[2], rc=argv[3], vel=argv[4], trq=argv[5], w2t=argv[6])

Binary file not shown.

View File

@ -1 +0,0 @@
0.1.3 @ 06/01/2024