v0.1.2(2024/06/01)

1. 增加iso数据处理功能
2. 重新修改了README.md
3. 单独将rokae拉出来,作为一个独立的repo进行维护,与scripts分离
4. 创建分支brake和current,分别单独开发
This commit is contained in:
2024-06-01 13:26:13 +08:00
parent 0fd8a4e8b5
commit f6f283f8d1
5 changed files with 289 additions and 70 deletions

3
.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
.idea/
aio/.idea/
aio/__pycache__/

View File

@ -1,67 +1,69 @@
程序功能自动化处理制动性能采集的数据减少人工处理时长目前测试单轴可从原来的4-6h减少到15min ### 程序功能
使用方法:修改 configs.xlsx 配置文件中的一些参数(数据文件路径/减速比/最大角速度/额定电流),然后直接执行即可 自动化处理制动性能采集的数据减少人工处理时长目前测试单轴可从原来的4-6h减少到15min
第三方库pandas/pywin32/openpyxl ### 使用方法
pip3 install pandas -i https://pypi.tuna.tsinghua.edu.cn/simple --trusted-host pypi.tuna.tsinghua.edu.cn 修改 configs.xlsx 配置文件中的一些参数(数据文件路径/减速比/最大角速度/额定电流),然后直接执行即可
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 ```commandline
pip3 install Pillow -i https://pypi.tuna.tsinghua.edu.cn/simple --trusted-host pypi.tuna.tsinghua.edu.cn # https://customtkinter.tomschimansky.com/documentation/packaging
python.exe -m pip install --upgrade pip -i https://pypi.tuna.tsinghua.edu.cn/simple --trusted-host pypi.tuna.tsinghua.edu.cn pip3 install pandas -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 pip3 install openpyxl -i https://pypi.tuna.tsinghua.edu.cn/simple --trusted-host pypi.tuna.tsinghua.edu.cn
打包方法pyinstaller.exe -F --version-file file_version_info.txt -i .\icon.ico .\aio.py 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
pyinstaller.exe -F --version-file file_version_info.txt -i .\icon.ico .\aio.py -p .\brake.py -p .\current.py 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
```
https://customtkinter.tomschimansky.com/documentation/packaging ### 注意事项
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 ```text
1. 数据文件存储存储规则
数据文件,就是我们拍急停的时候,采集到的 .data 文件,正方向拍三次急停,会采集到三个 .data 文件,存储在同一个文件夹内,即每组(三个 .data 文件)文件必须存储在同一个文件夹内,数据文件的命名无要求,
注意事项: 2. 文件夹命名规则
1. 数据文件存储存储规则 采集到的 .data 文件没有命名要求,但是对于文件夹的命名是有要求的,必须是如下格式:
所谓数据文件,就是我们拍急停的时候,采集到的 .data 文件,正方向拍三次急停,会采集到三个 .data 文件,存储在同一个文件夹内,即每组(三个 .data 文件)文件必须存储在同一个文件夹内,数据文件的命名无要求, dXX_speedXX_reachXX 或者 loadXX_reachXX_speedXX
XX代表不同条件下的测试数值比如
d100_speed33_reach66指的是负载100%速度33%臂展66%
2. 文件命名规则 3. 结果文件命名规则
虽然对采集到的 .data 文件没有命名要求,但是对于文件夹的命名是有要求的,必须是如下格式 所谓结果文件,就是处理数据的那个 excle 文件,该文件名字的前缀必须是 loadXX_XXXXXXXXX.xlsx比如
loadXX_speedXX_reachXX 或者 loadXX_reachXX_speedXX load33_自研_制动性能测试.xlsx
这里的XX代表不同条件下的测试数值比如 load66_自研_制动性能测试.xlsx
load100_speed33_reach66指的是负载100%速度33%臂展66% load100_自研_制动性能测试.xlsx
3. 结果文件命名规则 !!结果文件可以是没有数据的,也可以是之前有数据的,只要保证第 6 点中的那几个数据准确即可
所谓结果文件,就是处理数据的那个 excle 文件,该文件名字的前缀必须是 loadXX_XXXXXXXXX.xlsx比如
load33_自研_制动性能测试.xlsx
load66_自研_制动性能测试.xlsx
load100_自研_制动性能测试.xlsx
!!结果文件可以是没有数据的,也可以是之前有数据的,只要保证第 6 点中的那几个数据准确即可 4. 数据存储的组织结
..../j1/load100_speed33_reach100
..../j1/load100_speed66_reach100
....
..../j1/load100_speed100_reach100
..../j1/load100_speed33_reach100/2024_05_16_09_18_52.data
..../j1/load100_speed33_reach100/2024_05_16_09_19_52.data
..../j1/load100_speed33_reach100/2024_05_16_09_20_52.data
..../j1/load33_自研_制动性能测试.xlsx
..../j1/load66_自研_制动性能测试.xlsx
..../j1/load100_自研_制动性能测试.xlsx
4. 数据存储的组织结 5. 文件的打开与关闭
..../j1/load100_speed33_reach100 a. 在执行程序之前,需要关闭所有相关 excle 文件
..../j1/load100_speed66_reach100 b. 在执行程序之中,不允许打开相关 excle 文件
.... c. 在执行程序之后,需要逐个打开结果文件,并保存一次
..../j1/load100_speed100_reach100
..../j1/load100_speed33_reach100/2024_05_16_09_18_52.data
..../j1/load100_speed33_reach100/2024_05_16_09_19_52.data
..../j1/load100_speed33_reach100/2024_05_16_09_20_52.data
..../j1/load33_自研_制动性能测试.xlsx 6. 参数一致性检查
..../j1/load66_自研_制动性能测试.xlsx 执行程序前,需要确定 configs.xlsx 中设定的减速比/最大角速度/额定电流的值是正确的
..../j1/load100_自研_制动性能测试.xlsx
5. 文件的打开与关闭 7. 数据准确性检查
a. 在执行程序之,需要关闭所有相关 excle 文件 执行程序之,需要对结果文件的数据准确性做核对通过我自己的数据观察误差基本在10ms以内也即10个数据点误差较大的情况可自行调整
b. 在执行程序之中,不允许打开相关 excle 文件
c. 在执行程序之后,需要逐个打开结果文件,并保存一次
6. 参数一致性检查
执行程序前,需要确定 configs.xlsx 中设定的减速比/最大角速度/额定电流的值是正确的
7. 数据准确性检查
执行完程序之后需要对结果文件的数据准确性做核对通过我自己的数据观察误差基本在10ms以内也即10个数据点误差较大的情况可自行调整
8. .data 数据顺序
.data 文件的第一列和第二列必须分别是速度和电流
9. 其他
程序运行主要的耗时集中在打开,保存和关闭结果文件,第一次打开的时候会比较慢,是因为 excel 在做首次公式的计算保存关闭之后再打开会比较快一些另外如果在运行出错并重复运行程序的时候无响应或者出现异常请打开任务管理器关闭一切和excel相关的进程重新运行即可
8. 其他
程序运行主要的耗时集中在打开,保存和关闭结果文件,第一次打开的时候会比较慢,是因为 excel 在做首次公式的计算保存关闭之后再打开会比较快一些另外如果在运行出错并重复运行程序的时候无响应或者出现异常请打开任务管理器关闭一切和excel相关的进程重新运行即可
```
RELEASE CHANGES RELEASE CHANGES
@ -124,3 +126,9 @@ v0.1.1(2024/05/30)
5. 重新在write2textbox中添加exitcode参数并补齐相关逻辑和修改brake中的调用方式 5. 重新在write2textbox中添加exitcode参数并补齐相关逻辑和修改brake中的调用方式
6. 修复参数检查无效的情况 6. 修复参数检查无效的情况
7. 屏蔽电流相关的功能 7. 屏蔽电流相关的功能
v0.1.2(2024/06/01)
1. 增加iso数据处理功能
2. 重新修改了README.md
3. 单独将rokae拉出来作为一个独立的repo进行维护与scripts分离
4. 创建分支brake和current分别单独开发

View File

@ -3,8 +3,7 @@ from os import getcwd
from threading import Thread from threading import Thread
import tkinter.messagebox import tkinter.messagebox
import customtkinter import customtkinter
import brake import brake, current, iso
import current
from time import time, strftime, localtime from time import time, strftime, localtime
from urllib.request import urlopen from urllib.request import urlopen
import socket import socket
@ -56,7 +55,7 @@ class App(customtkinter.CTk):
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=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) self.btn_end.grid(row=4, column=0, sticky='new', padx=10, pady=10, ipadx=5, ipady=5)
# create version info # create version info
self.label_version = customtkinter.CTkLabel(self.frame_func, justify='left', text="Vers: 0.1.1\nDate: 05/30/2024", font=self.my_font, text_color="DarkCyan") 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.frame_func.rowconfigure(6, weight=1) self.frame_func.rowconfigure(6, weight=1)
self.label_version.grid(row=6, column=0, padx=20, pady=20, sticky='s') self.label_version.grid(row=6, column=0, padx=20, pady=20, sticky='s')
@ -65,7 +64,7 @@ class App(customtkinter.CTk):
self.frame_param = customtkinter.CTkFrame(self, height=240, corner_radius=10) 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.grid(row=0, column=1, rowspan=3, columnspan=9, sticky='new', ipadx=20, ipady=10, padx=10, pady=10)
# create main menu # create main menu
self.menu_main = customtkinter.CTkOptionMenu(self.frame_param, values=["INIT", "brake", "current"], font=self.my_font, command=self.func_main_callback) 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, 0))
self.menu_main.set("Start Here!") self.menu_main.set("Start Here!")
# create sub menu # create sub menu
@ -197,6 +196,9 @@ class App(customtkinter.CTk):
self.entry_path.configure(state="normal") self.entry_path.configure(state="normal")
self.entry_rc.configure(state="normal") self.entry_rc.configure(state="normal")
self.option_trq.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: else:
self.initialization() self.initialization()
self.menu_main.set("Start Here!") self.menu_main.set("Start Here!")
@ -247,6 +249,7 @@ class App(customtkinter.CTk):
c3 = rr.isdigit() c3 = rr.isdigit()
c4 = rpm = 1 c4 = rpm = 1
c5 = sub_func in ['industrial', 'cobot'] c5 = sub_func in ['industrial', 'cobot']
c6 = True if vel != trq else False
if self.menu_sub.get() == 'industrial': if self.menu_sub.get() == 'industrial':
rpm = self.entry_rpm.get().strip('- ') rpm = self.entry_rpm.get().strip('- ')
@ -256,7 +259,7 @@ class App(customtkinter.CTk):
else: else:
pass pass
if c1 and c2 and c3 and c4 and c5: 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) return 1, path, int(av), int(rr), int(rpm), int(axis), int(vel), int(trq)
else: else:
return 0, 0 return 0, 0
@ -269,17 +272,24 @@ class App(customtkinter.CTk):
sub_func = self.menu_sub.get() sub_func = self.menu_sub.get()
c1 = exists(path) c1 = exists(path)
c2 = sub_func in ['max', 'avg'] c2 = sub_func in ['max', 'avg']
c3 = True if vel != trq else False
try: try:
_ = float(rc) _ = float(rc)
c3 = True c4 = True
except ValueError: except ValueError:
c3 = False c4 = False
if c1 and c2 and c3: if c1 and c2 and c3 and c4:
return 2, path, float(rc), int(vel), int(trq), sub_func return 2, path, float(rc), int(vel), int(trq), sub_func
else: else:
return 0, 0 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: else:
return 0, 0 return 0, 0
@ -289,13 +299,15 @@ class App(customtkinter.CTk):
self.textbox.delete(index1='1.0', index2='end') self.textbox.delete(index1='1.0', index2='end')
flag, *args = self.check_param() flag, *args = self.check_param()
func_dict = {1: brake.main, 2: current.main} func_dict = {1: brake.main, 2: current.main, 3: iso.main}
if flag == 1: 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) 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: elif flag == 2:
tkinter.messagebox.showinfo(title="TBD", message="功能待实现......") tkinter.messagebox.showinfo(title="TBD", message="功能待实现......")
# func_dict[flag](path=args[0], rc=args[1], sub_func=args[2]) # func_dict[flag](path=args[0], rc=args[1], sub_func=args[2])
elif flag == 3:
func_dict[flag](path=args[0], w2t=self.write2textbox)
else: else:
tkinter.messagebox.showerror(title="参数错误", message="请检查对应参数是否填写正确!", ) tkinter.messagebox.showerror(title="参数错误", message="请检查对应参数是否填写正确!", )

196
aio/iso.py Normal file
View File

@ -0,0 +1,196 @@
# _*_ encodingutf-8 _*_
import pdfplumber
from openpyxl import load_workbook
from os import scandir, remove
from os.path import exists
from sys import argv
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 p_iso(file, p_files, ws, tmpfile):
p_files.append(file)
pdf = pdfplumber.open(file)
with open(tmpfile, mode='w', encoding='utf-8') as fb:
for page in pdf.pages:
fb.write(page.extract_text())
with open(tmpfile, mode='r', encoding='utf-8') as fb:
lines = fb.readlines()
lines = [line for line in lines if not line.startswith('Page ')]
for line in lines:
if line.strip() == "Pose Accuracy and Repeatability":
index = lines.index(line)
ws.cell(row=3, column=7).value = float(lines[index+4].split()[1])
ws.cell(row=4, column=7).value = float(lines[index+5].split()[1])
ws.cell(row=5, column=7).value = float(lines[index+6].split()[1])
ws.cell(row=6, column=7).value = float(lines[index+7].split()[1])
ws.cell(row=7, column=7).value = float(lines[index+8].split()[1])
ws.cell(row=8, column=7).value = float(lines[index+4].split()[2])
ws.cell(row=9, column=7).value = float(lines[index+5].split()[2])
ws.cell(row=10, column=7).value = float(lines[index+6].split()[2])
ws.cell(row=11, column=7).value = float(lines[index+7].split()[2])
ws.cell(row=12, column=7).value = float(lines[index+8].split()[2])
elif line.strip() == 'Pose Accuracy Variation':
index = lines.index(line)
ws.cell(row=13, column=7).value = float(lines[index+4].split()[1])
ws.cell(row=14, column=7).value = float(lines[index+5].split()[1])
ws.cell(row=15, column=7).value = float(lines[index+6].split()[1])
elif line.strip() == 'Distance Accuracy':
index = lines.index(line)
ws.cell(row=16, column=7).value = float(lines[index + 4].split()[1])
ws.cell(row=17, column=7).value = float(lines[index + 4].split()[2])
elif line.strip() == 'Stabilisation Time and Overshoot':
index = lines.index(line)
ws.cell(row=18, column=7).value = float(lines[index + 7].split()[3])
ws.cell(row=19, column=7).value = float(lines[index + 7].split()[2])
elif line.strip() == 'Velocity Accuracy and Repeatability':
index = lines.index(line)
ws.cell(row=20, column=7).value = float(lines[index + 4].split()[1])
ws.cell(row=21, column=7).value = float(lines[index + 4].split()[2])
ws.cell(row=22, column=7).value = float(lines[index + 4].split()[3])
elif line.strip()[:31] == 'Path Accuracy and Repeatability':
index = lines.index(line)
ws.cell(row=29, column=7).value = float(lines[index + 4].split()[1])
ws.cell(row=30, column=7).value = float(lines[index + 4].split()[2])
elif line.strip() == 'Corner Overshoot and Roundoff':
index = lines.index(line)
ws.cell(row=35, column=7).value = float(lines[index + 4].split()[1])
ws.cell(row=36, column=7).value = float(lines[index + 4].split()[2])
elif line.strip() == 'Robot Weaving':
index = lines.index(line)
ws.cell(row=41, column=7).value = float(lines[index + 4].split()[2])
ws.cell(row=42, column=7).value = float(lines[index + 4].split()[3])
ws.cell(row=43, column=7).value = float(lines[index + 4].split()[4])
else:
pass
def p_iso_100(file, p_files, ws, tmpfile):
p_files.append(file)
pdf = pdfplumber.open(file)
with open(tmpfile, mode='w', encoding='utf-8') as fb:
for page in pdf.pages:
fb.write(page.extract_text())
with open(tmpfile, mode='r', encoding='utf-8') as fb:
lines = fb.readlines()
lines = [line for line in lines if not line.startswith('Page ')]
for line in lines:
if line.strip() == 'Velocity Accuracy and Repeatability':
index = lines.index(line)
ws.cell(row=26, column=7).value = float(lines[index + 4].split()[1])
ws.cell(row=27, column=7).value = float(lines[index + 4].split()[2])
ws.cell(row=28, column=7).value = float(lines[index + 4].split()[3])
elif line.strip()[:31] == 'Path Accuracy and Repeatability':
index = lines.index(line)
ws.cell(row=33, column=7).value = float(lines[index + 4].split()[1])
ws.cell(row=34, column=7).value = float(lines[index + 4].split()[2])
elif line.strip() == 'Corner Overshoot and Roundoff':
index = lines.index(line)
ws.cell(row=39, column=7).value = float(lines[index + 4].split()[1])
ws.cell(row=40, column=7).value = float(lines[index + 4].split()[2])
elif line.strip() == 'Robot Weaving':
index = lines.index(line)
ws.cell(row=47, column=7).value = float(lines[index + 4].split()[2])
ws.cell(row=48, column=7).value = float(lines[index + 4].split()[3])
ws.cell(row=49, column=7).value = float(lines[index + 4].split()[4])
else:
pass
def p_iso_1000(file, p_files, ws, tmpfile):
p_files.append(file)
pdf = pdfplumber.open(file)
with open(tmpfile, mode='w', encoding='utf-8') as fb:
for page in pdf.pages:
fb.write(page.extract_text())
with open(tmpfile, mode='r', encoding='utf-8') as fb:
lines = fb.readlines()
lines = [line for line in lines if not line.startswith('Page ')]
for line in lines:
if line.strip() == 'Velocity Accuracy and Repeatability':
index = lines.index(line)
ws.cell(row=23, column=7).value = float(lines[index + 4].split()[1])
ws.cell(row=24, column=7).value = float(lines[index + 4].split()[2])
ws.cell(row=25, column=7).value = float(lines[index + 4].split()[3])
elif line.strip()[:31] == 'Path Accuracy and Repeatability':
index = lines.index(line)
ws.cell(row=31, column=7).value = float(lines[index + 4].split()[1])
ws.cell(row=32, column=7).value = float(lines[index + 4].split()[2])
elif line.strip() == 'Corner Overshoot and Roundoff':
index = lines.index(line)
ws.cell(row=37, column=7).value = float(lines[index + 4].split()[1])
ws.cell(row=38, column=7).value = float(lines[index + 4].split()[2])
elif line.strip() == 'Robot Weaving':
index = lines.index(line)
ws.cell(row=44, column=7).value = float(lines[index + 4].split()[2])
ws.cell(row=45, column=7).value = float(lines[index + 4].split()[3])
ws.cell(row=46, column=7).value = float(lines[index + 4].split()[4])
else:
pass
def main(path, w2t):
dirs, files = traversal_files(path, 1)
try:
wb = load_workbook(path + "/iso-results.xlsx")
ws = wb.active
tmpfile = f"{path}\\data.txt"
except Exception as Err:
w2t(f"发生错误:{Err}", 0, 2)
p_files = []
for file in files:
if file.endswith('.pdf') and file.split('\\')[-1] == 'ISO.pdf':
w2t(f"正在处理{file}......")
p_iso(file, p_files, ws, tmpfile)
w2t(f"文件{file}已处理完毕。\n")
elif file.endswith('.pdf') and file.split('\\')[-1] == 'ISO-V100.pdf':
w2t(f"正在处理{file}......")
p_iso_100(file, p_files, ws, tmpfile)
w2t(f"文件{file}已处理完毕。\n")
elif file.endswith('.pdf') and file.split('\\')[-1] == 'ISO-V1000.pdf':
w2t(f"正在处理{file}......")
p_iso_1000(file, p_files, ws, tmpfile)
w2t(f"文件{file}已处理完毕。\n")
else:
pass
wb.save(path + '/iso-results.xlsx')
wb.close()
if len(p_files) == 0:
w2t(f"目录 {path} 下没有需要处理的文件,需要确认......", 0, 3)
else:
remove(tmpfile)
w2t("------------------------------------------")
w2t("所有文件均已处理完毕!")
if __name__ == '__main__':
main(path=argv[1], w2t=argv[2])

View File

@ -1 +1 @@
1.1.1 @ 05/30/2024 0.1.2 @ 06/01/2024