87 Commits

Author SHA1 Message Date
2d12c160b9 v0.2.0.7(2024/08/16)
1. [t_change_ui: clibs.py]:修改了 hmi.log 的日志等级为 WARNING
2. [t_change_ui: openapi.py]:根据第一步的修改,将此模块日志记录等级调整至 warning
3. [current: current.py]
   - README新增了整机自动化测试的前置条件,即滑块需要滑动到最右端
   - current修改了文件校验的逻辑
4. [t_change_ui: aio.py]
   - 修改变量命名,widgit -> widget
   - 根据第 5 点变动,同步修改代码实现
   - 调整 UI 界面代码顺序,使之符合 layout.xlsx 描述
   - 将版本检查的部分单独封装成一个函数,在 detect_network 线程初始化时调用一次,并且程序启动也不会受到阻塞
5. [t_change_ui: layout.xlsx]:修改了组件布局方式
2024-08-16 17:22:52 +08:00
4925d899b4 千分表自动采集程序 2024-08-10 17:23:20 +08:00
3814d163c5 v0.2.0.6(2024/08/09)
1. [t_change_ui: all files]
   - 修改了 logger 的实现
   - 尤其是 clibs.py,使用日志字典,重写了日志记录的功能
2024-08-09 10:47:22 +08:00
340d48876b 10. [APIs: all]: 添加了 logger.setLevel(INFO),只有添加这个,单个模块内才生效 2024-08-01 19:15:08 +08:00
60726d9d07 7. [APIs: btn_functions.py]: 重写了告警输出函数,从日志中拿数据
8. [APIs: aio.py]: 将日志框输出的内容,也保存至日志文件
9. [APIs: do_brake.py]
   - 修改获取初始速度的逻辑,只获取configs文件中配置的时间内的速度
   - 新增 configs 参数 single_brake,可针对特定条件做测试
2024-08-01 17:11:12 +08:00
e713485d00 fix merge 2024-07-31 16:14:41 +08:00
25fc43be81 优化 ssh 输入密码的部分
create_plot 函数中增加 close('all'),解决循环画图不销毁占用内存的问题
2024-07-31 11:21:24 +08:00
924fad09e0 fix version again... 2024-07-31 08:28:39 +08:00
04bd1238d2 v0.2.0.5(2024/07/31)
此版本改动较大,公共部分做了规整,放置到新建文件夹 commons 当中,并所有自定义模块引入 logging 模块,记录重要信息
1. [t_change_ui: clibs.py]
   - 调整代码组织结构,新增模块,将公共函数以及类合并入此
   - 将一些常量放入该模块
   - 引入logging/concurrent_log_handler模块,并作初始化操作,供其他模块使用,按50M切割,最多保留10份
   - prj_to_xcore函数设置工程名部分重写,修复了多个prj工程可能不能执行的问题
2. [t_change_ui: openapi.py]
   - 完全重写了 get_from_id 函数,使更精准
   - 在 msg_storage 函数中,增加 logger,保留所有响应消息
   - 删除 heartbeat 函数中的日志保存功能部分
   - 心跳再次修改为 2s...
3. [t_change_ui: aio.py]
   - 增加了日志初始化部分
   - detect_network 函数中修改重新实例化HR间隔为 4s,对应心跳
4. [t_change_ui: do_brake.py]
   - 使用一直打开曲线的方法规避解决了 OOM 的问题,同时修改数据处理方式,只取最后 12s
5. [t_change_ui: do_current.py]
   - 保持电流,只取最后 15s
6. [t_change_ui: all the part]: 引入 commons 包,并定制了 logging 输出,后续持续优化
2024-07-31 08:05:36 +08:00
a7984a613d v0.2.0.4(2024/07/30)
1. [APIs: do_brake.py]: 修复制动数据处理过程中,只取曲线的最后 240 个数据
2. [APIs: aio.py]: 判定版本处,删除 self.destroy(),因为该语句会导致异常发生
2024-07-30 12:30:37 +08:00
af68f19d53 vers info 2024-07-27 21:32:02 +08:00
b01f8dc19c v0.2.0.3(2024/07/27)
1. [APIs: do_brake.py]: 精简程序,解决 OOM 问题
2. [APIs: do_current.py]: 精简程序,解决 OOM 问题
3. [APIs: factory_test.py]: 精简程序,解决 OOM 问题
4. [APIsL openapi.py]
   - 心跳修改为 1 s,因为 OOM 问题的解决依赖于长久的打开曲线开关,此时对于 hr.c_msg 的定时清理是个挑战,将心跳缩短,有利于清理日志后,避免丢失心跳
   - 新增 diagnosis.save 命令,但是执行时,有问题,待解决
2024-07-27 21:31:09 +08:00
d2794b2de7 remove sys module 2024-07-26 13:38:39 +08:00
0d814d384d fix version and will exit if not the latest one 2024-07-26 13:33:19 +08:00
82ae2681bb v0.2.0.2(2024/07/26)
1. [main: current.py]
   - 修正堵转电流无法正确写入结果文件的问题
2. [main: do_brake.py]
   - 初始速度采集等待时间设置为可通过configs.xlsx配置文件调整的
   - 初次速度采集停止逻辑修改为tasks.stop指令(未验证)
   - 急停信号触发前,pending时间设置为固定值10s
   - 实现正负方向速度采集逻辑
   - 工程名变更逻辑实现修改为通配符,方便后续根据机型保存文件
   - 增加超差后写诊断的逻辑,并可以通过configs.xlsx配置文件调整
   - 程序输出中增加时间戳,方便调试定位日志时间
3. [main: do_current.py]
   - 工程名变更逻辑实现修改为通配符,方便后续根据机型保存文件
4. 为工程文件添加更详细的注释
5. 补充了do_current/do_brake的流程图
6. [main: openapi.py]
   - ½«modbus motor_on/offµÄµÏ·½·¨¸Äª¸ߵç³崥·¢
7. configs.xlsx配置表新增write_diagnosis/get_init_speed两个参数
2024-07-26 13:24:44 +08:00
8b49d3b6e4 将count恢复为全局标量 2024-07-19 17:19:19 +08:00
2f782c9693 another try 2024-07-19 16:32:31 +08:00
73230a7133 remove durable folder 2024-07-19 11:21:27 +08:00
97cb4fe1d4 minor modification 2024-07-19 11:13:51 +08:00
bdccf3da47 新增保留历史数据功能,修改x轴坐标刻度显示 2024-07-19 11:09:34 +08:00
370fa051ae change axis x to time 2024-07-18 17:45:40 +08:00
e1866758b7 change x-axis content 2024-07-18 16:50:50 +08:00
1cefe4a16b minor modifications 2024-07-18 14:59:20 +08:00
dd0873637f minor fix 2024-07-18 11:04:31 +08:00
5ab03d23f2 aio.py 将canvas设定到tabview下,并将高度减小到600,将曲线选择的OP设定为不可伸缩 2024-07-17 17:31:32 +08:00
9088b90e34 fix OverflowError: (34, "Result too large") problem 2024-07-17 16:29:10 +08:00
9d252cc36f add projects for different conditions 2024-07-17 14:40:25 +08:00
3010cb8931 v0.2.0.0(2024/07/17)
1. [profile: aio.py]
   - 增加velocity相关逻辑
   - 修改负载信息为曲线信息
2. [profile: factory_test.py]
   - 增加velocity相关逻辑
3. [profile: current.py]
   - 修正减速比获取的规则
4. [profile: openapi.py]
   - HmiRequest模块:日志取消记录move.monitor相关
   - HmiRequest模块:增加了durable_lock变量,控制文件读写互斥
2024-07-17 14:17:00 +08:00
da5ddcea0a v0.1.9.4(2024/07/15)
1. [profile: aio.py]:完善durable text相关逻辑
2. [profile: do_brake/do_current/btn_functions.py]:删除validate_resp函数,修改execution函数
3. [profile: factory_test.py]
   - 新增耐久/老化测试程序
   - 实现六轴折线图显示
4. [profile: openapi.py]:多次合并遗留问题处理
5. templates文件夹组织架构调整
2024-07-17 10:09:06 +08:00
cf9d51b475 fix merge 2024-07-15 13:42:10 +08:00
f4a70a0034 Merge branch 'main' of gitea.rustle.cc:gitea/rokae into profile
fetch the newest codes of main
2024-07-15 13:34:09 +08:00
cdbe1c40c6 v0.1.9.3(2024/07/15)
1. [APIs: openapi.py]
   - иÄodbusl½Ó§°ܱ¨´í³ö½£¬ʹֻ֮Ôautomatic testҳÃÏʾ
   - 将该文件移动至toplevel,为后面扩展做准备
   - 修改heartbeat文件路径,使后续打包的时候更方便
2. [APIs: aio.py]:
   - 修改heartbeat文件路径,使后续打包的时候更方便
   - 修改write2textbox函数的打印逻辑,先判断网络相关
2024-07-15 13:32:55 +08:00
5ed38b4b2a delete template sheet 2024-07-13 16:51:18 +08:00
27877e2b64 fix merge 2024-07-13 16:46:21 +08:00
edda9defdd - 减速比rr数据源修改为configs.xlsx
4. 在current工程main函数增加 VelSet 100语句
2024-07-13 16:45:01 +08:00
485dffdd0b - 减速比rr数据源修改为configs.xlsx
4. 在current工程main函数增加 VelSet 100语句
2024-07-13 16:43:56 +08:00
d35858e14e v0.1.9.2(2024/07/13)
1. [APIs: do_current.py]
   - 删除多余的时序矫正语句——item['value'].reverse(),使输出的曲线为平滑的自然顺序
2. [current: current.py]
   - max功能计算逻辑矫正,应该是取绝对值的最大值
   - 整体梳理了trq/trqh的传递路径,现已修正完毕
2024-07-13 15:40:13 +08:00
718db9ec45 change version 2024-07-13 13:43:51 +08:00
d76ee3d223 1. [APIs: do_brake.py]
- 修改ready_to_go信号的接收逻辑,适配大负载机型
2. [APIs: do_current.py]
   - 修改ready_to_go信号的接收逻辑,适配大负载机型
   - 调整单轴测试时间为35s,适配大负载机型,调整堵转电流持续时间15s,适当减少测试时间
   - 将act信号置为False的动作放在初始化,增加程序健壮性
   - 修改所有输出文件的命名,在扩展名之前加入时间戳
2024-07-13 13:40:04 +08:00
a66a55bcd3 v0.1.9.2(2024/07/13)
1. [APIs: do_brake.py]
   - 修改ready_to_go信号的接收逻辑,适配大负载机型
2. [APIs: do_current.py]
   - 修改ready_to_go信号的接收逻辑,适配大负载机型
   - 调整单轴测试时间为35s,适配大负载机型,调整堵转电流持续时间15s,适当减少测试时间
   - 将act信号置为False的动作放在初始化,增加程序健壮性
2024-07-13 11:36:35 +08:00
fe27dbf91f fix merge 2024-07-12 10:50:32 +08:00
dee07b77bb v0.1.9.1(2024/07/12)
1. [APIs: do_brake.py]
   - 修改正负方向拍急停的逻辑,基本原理为:运行之前发送正负方向信号pon给RL,RL根据信号以及速度正负号运作
   - 由于上述修改,正负方向急停准确率可达100%
2. [APIs: aio.py]
   - 修改write2textbox的输出逻辑,实现更加灵活的自定义输出,同时修改相关部分
3. [APIs: openapi.py]
   - modbus类新增指示政府方向急停的信号pon,将modbus类入参中的tab_name删除,并修改tab_name的值为'openapi'
   - socket类种修改tab_name的值为'openapi'
2024-07-12 10:48:50 +08:00
70e6c269fe v0.1.9.1(2024/07/12)
1. [APIs: do_brake.py]
   - 修改正负方向拍急停的逻辑,基本原理为:运行之前发送正负方向信号pon给RL,RL根据信号以及速度正负号运作
   - 由于上述修改,正负方向急停准确率可达100%
2. [APIs: aio.py]
   - 修改write2textbox的输出逻辑,实现更加灵活的自定义输出,同时修改相关部分
3. [APIs: openapi.py]
   - modbus类新增指示政府方向急停的信号pon,将modbus类入参中的tab_name删除,并修改tab_name的值为'openapi'
   - socket类种修改tab_name的值为'openapi'
2024-07-12 10:46:59 +08:00
71b2d9d42e pending dev 2024-07-11 19:09:08 +08:00
2508be7b35 补充制动性能测试相关说明 2024-07-11 16:41:10 +08:00
6d03f4f281 fix version 2024-07-10 19:41:34 +08:00
40ddef1c39 v0.1.9.0(2024/07/10)
1. 完成了制动性能的自动化采集
2. 完善了modbus浮点数读写相关的功能
3. 修改了target.zip工程,该工程目前适配电机电流和制动性能
2024-07-10 19:18:53 +08:00
fa5a9f0f89 Surprisingly I made it! 完成了制动性能测试程序 2024-07-10 18:55:18 +08:00
9fa42fb3e1 尝试在RL里判断最大速度,尝试失败 2024-07-10 14:32:50 +08:00
2fbe500d1d v0.1.8.2(2024/07/08)
1. [APIs: do_brake.py]: 完成了制动性能测试逻辑,只不过制动信号传递生效延迟不可控,暂时pending
2. [APIs: do_current.py]: 修改曲线数据时序,主要是value data取反即可,解决了波形锯齿明细的问题
3. [APIs: openapi.py]: modbus新增了触发急停信号的寄存器 stop0_signal,并重写了解除急停,socket新增了register.set_value协议
2024-07-08 19:50:21 +08:00
7b25b91c37 v0.1.8.1(2024/07/05)
1. [APIs: do_brake.py]: 完成了制动性能测试框架的搭建,可以顺利执行完整的测试程序,但是未实现急停和数据处理
2. [APIs: aio.py]: 修改了do_brake主函数的参数
3. 增加工程文件target.zip
2024-07-05 15:41:12 +08:00
fc56d81e9c v0.1.8.0(2024/07/04)
1. [APIs: do_current.py]: 完成了堵转电流和惯量负载电机电流的采集和处理,至此,电机电流的自动化工作基本完成
2024-07-04 21:08:13 +08:00
524af160d8 v0.1.7.5(2024/07/03)
1. [APIs: aio.py]
   - 增加触发急停和恢复急停功能逻辑
2. [APIs: do_current.py]
   - 重新调整运行顺序,增加数据处理的逻辑(惯量负载逻辑暂不实现,等待软件部解决了修改工程之后不生效的问题再考虑)
3. [APIs: btn_functions.py]
   - 增加触发急停和恢复急停的modbus实现,仅适用于自动化测试

v0.1.7.6(2024/07/04)
1. [APIs: aio.py]
   - Automatic Test逻辑中增加选择current时,需要选负载类型的逻辑
2. [APIs: do_current.py]
   - 单轴/场景电机电流的采集已完成
3. [APIs: openapi.py]
   - 增加了modbus读取浮点数的功能
   - 优化了get_from_id的逻辑
4. [autotest.xml]: 新增了scenario_time只写寄存器
2024-07-04 13:34:04 +08:00
aedac4c90c v0.1.7.4(2024/07/02)
1. [APIs: openapi.py]
   - 增加了modbus的python实现
   - heartbeat函数修改发送间隔为1s
   - 清除了绝大部分调试性输出,发现太多的这种输出也会导致心跳丢包...,不清楚这个原理是什么
   - 在get_response函数中的while self.pkg > 0循环中,删除了else语句,因为它永不会被执行到
   - 在get_response函数中,修复一个bug,在flag==0的else语句中,补齐了index==6的情况
2. [APIs: do_current.py]
   - 完成了六个轴的电机电流动作的执行,以及数据采集
   - 完成了对应的RL程序的编写
3[APIs: aio.py]
   - 引入modbus实例化,并以参数的形式,传递给相应的tabview
   - 新增pre_warning函数,在做自动化测试之前,确保所有条件皆具备
2024-07-02 21:48:00 +08:00
4e4d760db4 修复了两个bug,详见readme 2024-07-01 19:06:18 +08:00
ad4b6ae8d6 v0.1.7.3(2024/07/01)
1. [APIs: openapi.py] 继续完善封包解包操作,并优化了所有调试信息,默认打开状态,直到bug数量明显减少
2. [APIs: do_current.py] 使用原工程的工程名进行move操作,语义更加明确

> 目前看openapi.py封包解包没有任何问题了,但是所有的调试信息都默认打开,以便可以第一时间保留现场
2024-07-01 14:09:47 +08:00
fc3d5482f8 v0.1.7.2(2024/06/30)
1. 初步完成NB4h_R580_3BH7.zip工程的设计
2. 重新研究了解包操作,重新实现了一版
3. 修改openapi.pi中excution为execution函数
4. 增减了解包原理性文档
2024-06-30 20:29:49 +08:00
ac71522cc2 fixing merge 2024-06-29 20:46:42 +08:00
611d848b41 v0.1.7.1(2024/06/29)
1. [APIs: aio.py]
   - 修改detect_network函数中sleep语句放到最后,重新生成HmiRequest实例中增加sleep(4),这个停顿时间一定是比openapi中heartbeat函数的sleep要长1s以上才能正常工作
   - 修改write2textbox函数,新增默认参数tab_name,只有当该值与当前tab一致时,函数才会有输出
   - 第二条改动影响到了automatic_test文件夹下所有的文件
2. [APIs: openapi.py]
   - 规定了所有的网络异常均由heartbeat函数来定义,其他异常不做中断处理
   - execution函数中合并了case条件
   - 增加了N多指令,多为诊断曲线和rl程序相关
3. [APIs: do_brake.py]
   - 实现自动推送工程到xCore并自动运行
   - 初步实现了Modbus发送消息和检测状态
4. [APIs: do_current.py]
   - 将do_brake.py的内容完全拷贝到此文件,待修改
2024-06-29 20:40:17 +08:00
802ccd8e97 v0.1.7.1(2024/06/29)
1. [APIs: aio.py]
   - 对于automatic test删除了输入框,使用configs.xlsx配置文件作为参数输入
   - 完善initialization/param_check/func_start_callback函数中对于automatic test的处理
   - 将textbox组件一直设置为normal状态,不再频繁切换disabled
   - 将所有的f_h文件对象修改为f_hb,并将connection_state修改为c_state
   - 在detect_network函数中,实例化HmiRequest,并在无限循环中检测心跳是否正常,如异常,则销毁hr,重新生成
   - 取消在tabview切换时,检测心跳的逻辑,这样做无法保证实时性
2. [APIs: openapi.py]
   - 将sock_conn函数移出__init__,单独作为连接函数存在
   - 新增全局变量self.t_bool,控制所有的线程中无限循环的启停,也就是可以人为的退出线程
   - 移除close_sock函数
   - heartbeat函数中新增打印所有消息的代码,调试时打开,平常关闭
   - execution函数中,新增对overview.set_autoload和overview.reload的支持
   - execution函数中,对send动作增加异常处理逻辑
3. [APIs: do_brake.py]
   - 新增文件,处理制动测试流程,建立连接,导入project,pp2main,run,采集并处理曲线数据,本地修改RL程序,推送至控制器等
   - 目前完成:
     - 文件合规性检查
     - 导入工程并设置为运行工程
4. [APIs: current.py] 修改scenario/single电机电流最大长度为150s
5. 在本文件中更新关于制动自动化测试的相关内容
2024-06-29 07:48:54 +08:00
61fa840e53 [t_change_ui: aio.py/brake.py/current.py] 整体修改了操作界面,删除了大部分的配置输入框,改用 configs.xlsx 配置文件替代,并优化了max/avg功能中写入结果数据的方式 2024-06-28 09:47:59 +08:00
79797a3bdd 20240626
9. [aio.py] 修改了版本
10. [current.py] max/avg功能结束之前会将结果数据追加写入源文件,avg算法更改为average+3×std
11. [wavelogger.py] 算法更改为 average+3×std
2024-06-26 21:38:21 +08:00
7143a19fa1 v0.1.7.0(2024/06/26)-初步可用
1. [aio.py] 在detect_network函数中需改查询时间间隔是1s,在tabview_click中增加textbox配置normal的语句
2. [do_brake.py -> btn_functions.py] 新增执行相应函数,并在get_state函数中设置无示教器模式
3. [openapi.py] 新增sock_conn函数,并做连接时的异常处理,新增类参数w2t
4. [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组件的字体大小,使用原生字体,同时将segmented button字体修改为原生,为了解决segmented button在禁用和启用时,屏幕抖动的问题,并将大小修改为16
5. [aio.py] 修改了segmented_button_callback的实现逻辑,使代码更简洁
6. [aio.py] 修改了在tabview_click函数中对于实例化openapi的动作,使每次切换标签都会重新实例化,也就是每次都会重新连接,修复显示不正确的问题
7. [openapi.py] 新增了socket关闭的函数,并增加msg_id为None的处理逻辑
8. [btn_functions.py] 完善了状态获取的功能,新增告警获取以及功能切换的逻辑
2024-06-26 19:54:51 +08:00
a75775c869 v0.1.7.0(2024/06/25)-未发布
1. [aio.py] 取消了在本文件中开启openapi线程的做法,并修改如下:
	- 通过包的方式导入其他模块
    - 使用current_path来规避文件路径问题
    - 声名了 self.hr 变量,用来接收openapi的实例化
    - 修改了对于segment button的错误调用
    - 设定progress bar的长度是10
    - 完善了segmented_button_callback函数
    - 在detect_network函数中增加heartbeat初始化
    - tabview_click函数中新增textbox清屏功能,以及实例化openapi,并做检测
2. [openapi.py] 取消了初始化中无限循环检测,因为阻塞了aio主界面进程!!!socket也无法多次连接!!!浪费了好多时间!!!很生气!!!!
	- 通过tabview切换来实现重新连接,并保留了异常处理部分
    - 将所有的 __xxxx 函数都替换成 xxxx 函数,去掉了 __
    - 使用current_path来规避文件路径问题
3. [do_brake.py] 初步完成了机器状态收集的功能,还需要完善
    - 使用current_path来规避文件路径问题
    - 新增validate_resp函数,校验数据
    - 完善了调用接口
2024-06-25 21:40:27 +08:00
6604f0ba06 20240624
11. [openapi.py] 建联部分做容错处理,并将读写文件做自适应处理
12. [aio.py] 将读写文件做自适应处理,引入openapi模块并生成实例,做心跳检测,将socket超时时间修改为3s
2024-06-24 19:22:56 +08:00
a4009eb17c 20240623
8. [openapi.py] 增加心跳检测函数,并开启线程执行;取消在该文件中生成实例
9. [aio.py] 完成detect_network,并在main函数开启线程
10. 将templates文件夹移动到assets内
2024-06-23 20:18:41 +08:00
295894a843 2024-06-12
4. [openapi.py] 使用 int.to_bytes 和 int.from_bytes 替换 binascii 模块的功能
5. [aio.py] 修改了Data Process中初始化的动作,使得初始化时的状态统一成程序刚启动时的样子
6. [aio.py] 增加了tabview的点击行为函数,每次点击tab都会初始化
7. [aio.py] 增加了Automatic Test界面元素,包括如下,并完成了功能框架的搭建
- 标签:文件/角速度/减速比
- 按钮:急停及恢复
- 输入框:文件路径/角速度/减速比
- OptionMenu:负载
- 进度条
2024-06-21 16:53:13 +08:00
ff4b0cc04c 20240621
2. [openapi.py] 修改了封包的规则,使之更加明晰,封包操作没有实现分包功能,目前看实际场景用不到
3. [openapi.py] 定义 MAX_FRAME_SIZE 常量(1024),替换socket接收以及响应数据处理相关部分
4. [openapi.py] ʹÓ int.to_bytes ºÍint.from_bytes Ì»» binascii ģ¿é¹¦Ä
2024-06-21 08:13:51 +08:00
8975d8a37c 完善了解包分帧的情况,现在测试基本无问题 2024-06-20 20:48:03 +08:00
14f269b570 中间版本,对于解封超过1024的消息有问题,暂存历史 2024-06-20 17:15:54 +08:00
2917f4ae97 [openapi.py] 初步搭建起框架,完成了新老协议的封包/解包/异步采集日志的操作(未充分测试) 2024-06-20 12:49:41 +08:00
d04d90f1a7 Merge branch 'main' of gitea.rustle.cc:gitea/rokae into APIs
keep the same content with main
2024-06-19 15:21:46 +08:00
284dabee76 re-organize file storage structure, and add API related files, just framework 2024-06-19 15:20:43 +08:00
0646ae13de Merge branch 'main' of gitea.rustle.cc:gitea/rokae into APIs 2024-06-19 07:41:52 +08:00
c3dbb2cff0 v0.1.6.3(2024/06/18)
1. [current.py] 适配电机电流中速度使用hw_joint_vel_feedback的数据,取消对device_servo_vel_feedback的支持,后续所有涉及到速度相关的数据均已前者为准,现已完成对单轴和场景的适配

> !!WARNING:目前版本的电机电流程序还支持DriverMaster采集的数据处理,等明确后,将不再支持,也即所有的电机电流数据(工业+协作),都是用诊断曲线来采集
2024-06-18 20:42:48 +08:00
780fed29af fix merging conflicts 2024-06-17 15:11:54 +08:00
4ba8af842c unused 2024-06-17 15:07:28 +08:00
a0fe9b5939 fix merging 2024-06-16 14:49:46 +08:00
2f2f0d430d fix merging 2024-06-16 14:30:12 +08:00
78a79e4aa0 v0.1.6.2(2024/06/16)
1. [current.py] 修改了max/avg相关功能中对于返回值的处理逻辑,并在输出框以行的形式打印出来
2024-06-16 14:25:41 +08:00
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
a1b45bf941 [init] for apis 2024-06-12 14:56:50 +08:00
52 changed files with 9404 additions and 1045 deletions

10
.gitignore vendored
View File

@ -4,3 +4,13 @@ aio/.idea/
aio/code/__pycache__/ aio/code/__pycache__/
aio/package/ aio/package/
aio/venv aio/venv
aio/__pycache__/
aio/code/automatic_test/__pycache__/
aio/code/data_process/__pycache__/
aio/assets/templates/c_msg.log*
aio/code/durable_action/__pycache__/
aio/assets/templates/durable/
aio/assets/templates/.__c_msg.lock
aio/code/commons/__pycache__/
aio/assets/templates/debug.log
dial_gauge/results.xlsx

View File

@ -1,90 +1,114 @@
### 程序功能 ### 一、程序功能
自动化测试数据处理工具,减少人工处理时长,提高测试数据处理的效率和准确度: 自动化测试数据处理工具,减少人工处理时长,提高测试数据处理的效率和准确度:
1. 制动数据单轴数据处理3min以内
2. 电机电流数据全部轴数据处理1min以内
3. ISO激光数据整理1min以内
### 使用方法 1. 制动数据,单轴数据处理 3min 以内
2. 电机电流数据,全部轴数据处理 1min 以内
3. ISO 激光数据整理1min 以内
4. wavelogger 波形处理,几乎不花费时间
5. 制动自动化测试
6. 电机电流自动化测试
7. 耐久工程曲线指标采集(仅适用于六轴)
点击可执行程序 AIO.exe然后选择功能根据提示填写必要参数点击运行即可
### 第三方库 ---
### 二、使用方法
点击可执行程序 AIO.exe然后选择功能根据提示填写必要参数红色标签提示是必填项蓝色标签提示是可选项然后点击运行即可
> 需要注意的是,可选项填写的不合适,有可能导致数据错误,或者无法运行!!
---
### 三、第三方库
```text ```text
参考requirements.txt 参考 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
``` ```
--- ---
### 注意事项 ### 四、打包方法
#### 制动数据 打包时,只需要修改 clibs.py 中的 PREFIX 即可,调试时再修改回来
```
pyinstaller --noconfirm --onedir --windowed --optimize 2 --contents-directory . --upx-dir "D:/Syncthing/common/A_Program/upx-4.2.4-win64/" --add-data "C:/Users/Administrator/AppData/Local/Programs/Python/Python312/Lib/site-packages/customtkinter;customtkinter/" --add-data "D:/Syncthing/company/D-测试工作/X-自动化测试/01-AIO/rokae/aio/assets/templates:templates" --version-file ../assets/file_version_info.txt -i ../assets/icon.ico ../code/aio.py -p ../code/data_process/brake.py -p ../code/data_process/iso.py -p ../code/data_process/current.py -p ../code/data_process/wavelogger.py -p ../code/commons/openapi.py -p ../code/commons/clibs.py -p ../code/automatic_test/btn_functions.py -p ../code/automatic_test/do_current.py -p ../code/automatic_test/do_brake.py -p ../code/durable_action/factory_test.py
```
---
### 五、注意事项
#### 1) 制动数据
```text ```text
1. 数据文件存储存储规则 1. 数据文件存储存储规则
数据文件,就是我们拍急停的时候,采集到的 .data 文件,方向拍三次急停,会采集到三个 .data 文件,存储在同一个文件夹内,即每组(三个 .data 文件)文件必须存储在同一个文件夹内,数据文件的命名无要求 数据文件,就是我们拍急停的时候,采集到的 .data 文件,根据规则选择某个方向拍三次急停,会采集到三个 .data 文件,存储在同一个文件夹内,即每组(三个 .data 文件)文件必须存储在同一个文件夹内,数据文件的命名无要求
2. 文件夹命名规则 2. 文件夹命名规则
采集到的 .data 文件没有命名要求,但是对于文件夹的命名是有要求的,必须是如下格式: 采集到的 .data 文件没有命名要求,但是对于其存储的文件夹的命名是有要求的,必须是如下格式:
dXX_speedXX_reachXX 或者 loadXX_reachXX_speedXX reachXX_loadXX_speedXX
XX代表不同条件下的测试数值比如 XX代表不同条件下的测试数值比如
d100_speed33_reach66,指的是,负载100%速度33%臂展66% reach100_load66_speed33,指的是,臂展100%负载66%速度33%
3. 结果文件命名规则 3. 结果文件命名规则
所谓结果文件,就是处理数据的那个 excle 文件,该文件名字的前缀必须是 loadXX_XXXXXXXXX.xlsx比如 所谓结果文件,就是处理数据的那个 excle 文件,该文件名字的前缀必须是 reachXX_XXXXXXXXX.xlsx比如
load33_自研_制动性能测试.xlsx reach33_自研_制动性能测试.xlsx
load66_自研_制动性能测试.xlsx reach66_自研_制动性能测试.xlsx
load100_自研_制动性能测试.xlsx reach100_自研_制动性能测试.xlsx
!!结果文件可以是没有数据的,也可以是之前有数据的,只要保证第 6 点中的那几个数据准确即可 !!结果文件可以是没有数据的,也可以是之前有数据的,只要保证输入参数的正确性即可
4. 数据存储的组织结 4. 数据存储的组织结
..../j1/load100_speed33_reach100 ..../j1/reach100_load66_speed33
..../j1/load100_speed66_reach100 ..../j1/reach100_load66_speed66
.... ....
..../j1/load100_speed100_reach100 ..../j1/reach100_load33_speed33
..../j1/load100_speed33_reach100/2024_05_16_09_18_52.data ..../j1/reach100_load100_speed33/2024_05_16_09_18_52.data
..../j1/load100_speed33_reach100/2024_05_16_09_19_52.data ..../j1/reach100_load100_speed33/2024_05_16_09_19_52.data
..../j1/load100_speed33_reach100/2024_05_16_09_20_52.data ..../j1/reach100_load100_speed33/2024_05_16_09_20_52.data
..../j1/load33_自研_制动性能测试.xlsx ..../j1/reach33_自研_制动性能测试.xlsx
..../j1/load66_自研_制动性能测试.xlsx ..../j1/reach66_自研_制动性能测试.xlsx
..../j1/load100_自研_制动性能测试.xlsx ..../j1/reach100_自研_制动性能测试.xlsx
..../j1/configs.xlsx
5. 文件的打开与关闭 5. 文件的打开与关闭
a. 在执行程序之前,需要关闭所有相关 excle 文件 a. 在执行程序之前,需要关闭所有相关 excle 文件
b. 在执行程序之中,不允许打开相关 excle 文件 b. 在执行程序之中,不允许打开相关 excle 文件
c. 在执行程序之后,需要逐个打开结果文件,并保存一次 c. 在执行程序之后,需要逐个打开结果文件,并保存一次
6. 参数一致性检查 6. 数据准确性检查
执行程序前,需要确定 configs.xlsx 中设定的减速比/最大角速度/额定电流的值是正确的 执行程序之后,可以在日志输出框中看到全部文件的处理过程,对于有问题的文件,会用特殊颜色进行标识,需要注意观察
7. 数据准确性检查 7. configs.xlsx配置文件
执行完程序之后需要对结果文件的数据准确性做核对通过我自己的数据观察误差基本在10ms以内也即10个数据点误差较大的情况可自行调整 需要运行前需要手动修改该文件,并确保配置参数的正确性,测试时按照第 4 点中的位置存放
8. 其他 8. 其他
程序运行主要的耗时集中在打开,保存和关闭结果文件,第一次打开的时候会比较慢,是因为 excel 在做首次公式的计算保存关闭之后再打开会比较快一些另外如果在运行出错并重复运行程序的时候无响应或者出现异常请打开任务管理器关闭一切和excel相关的进程重新运行即可 程序运行主要的耗时集中在打开,保存和关闭结果文件,第一次打开的时候会比较慢,另外还需要注意采集的数据长度和结果文件中预设的数据长度是否一致,若采集的数据长度大于预设的数据长度,则需要补齐数据
``` ```
#### 电机电流 #### 2) 电机电流
1. 单独使用max/avg功能时对于文件命名同第二点,存放数据的文件夹,只允许有 .data 或者 .csv 文件且每次只能处理rc相同的轴的数据 1. 单独使用 max/avg 功能时,要求文件命名以 "jx_" 开头,例如 j1_2024_06_18_09_09_11.data只允许有 .data 或者 .csv 文件以及configs.xlsx配置表可同时处理所有轴的数据
2. cycle功能处理单文件单轴数据,可以批量处理所有轴,但要确保遵守如下规则: 2. cycle 功能支持处理单轴数据以及场景电机电流的数据,可以批量处理所有轴,但要确保遵守如下规则:
a. 数据整理文件以 .xlsx 为后缀
b. 其他文件
A. 单轴j1_xxxxx.data/csv
B. 保持j1_hold_xxxx.data/csv
C. 所有文件放在同一个文件夹即可
d. 界面输入rc参数时需要输入所有轴的数据
#### ISO数据 - 包含电机电流结果汇总文件excel
- 单轴文件jx_xxxxx.data/csv
- 保持电流jx_hold_xxxx.data/csv
- 场景文件factory_53.8_2024_06_18_09_01_26.data需手动拆分
- 配置文件configs.xlsx
- 所有文件放在同一个文件夹即可
- 界面输入rc参数时需要输入所有轴的数据即使只处理个别轴的数据
3. configs.xlsx配置文件需要运行前需要手动修改该文件并确保配置参数的正确性
> 程序运行主要的耗时集中在打开,保存和关闭结果文件
> 需要注意采集的数据长度和结果文件中预设的数据长度是否一致,若采集的数据长度大于预设的数据长度,则需要补齐数据
> 和制动数据统一后续将仅支持对hw_joint_vel_feedback的解析和处理不再支持device_servo_vel_feedback
#### 3) iso 数据
所有文件放在同一个文件夹即可,命名规则如下: 所有文件放在同一个文件夹即可,命名规则如下:
a. ISO.pdf a. ISO.pdf
@ -92,6 +116,57 @@ pyinstaller --noconfirm --onedir --windowed --add-data "C:/Users/Administrator/A
c. ISO-V100.pdf c. ISO-V100.pdf
d. iso-results.xlsx d. iso-results.xlsx
> 目前仅能处理 6 轴数据
#### 4) wavelogger 波形数据
1. 需要提前将 .xdt 波形数据转换成 .csv 文件
2. 组织目录下只允许有 .csv 文件,对文件夹无要求
3. 运行结束后,会生成 result.xlsx 文件,结果按照 .csv 文件名存放
4. 采集数据时,不同轮次数据时间间隔最好大于 2 倍的周期时间,否则会出现采集的轮数不正确的情况,但数据是完整的
#### 5) 制动自动化测试
只需要提前将如下文件放在指定路径下即可:
1. zip 工程文件
2. excel 制动结果处理文件
3. excel configs.xlsx 配置文件
> **需要注意的点**
1. 修改该文件点位信息
2. 修改“编程”→“工具列表”中的工具信息制动只允许使用tool33/tool66/tool100/inertia这四个工具不可重命名
3. 除了调试行,其他行请勿修改,包括增加空行等,尤其是有特别注释的地方
4. stop0_related半静态任务需要注意DO0_0的命名不同控制柜可能不一样
5. 需要导入 autotest.xml 寄存器文件以及新建modbus总线设备命名为autotest其他默认即可
6. 其他无需修改如有其他需求可联系fanmingfu@rokae.com沟通
7. 需要额外硬件接线详细参考configs.xlsx中急停接线图sheet页其中DO0_0视硬件情况可能为其他名称
8. 运行自动化程序之前,确保机器处于正常状态,无故障,未触发急停
9. 运行过程中,如果是因为机器问题无法达到额定百分比速度,会在日志输出框提示,注意观察
10. 由于xCore系统问题运行过程中可能会出现机器人宕机问题如果遇到可以手动重启控制柜重新运行
11. 务必正确填写configs.xlsx中的Target页面A1单元格可以选择正负方向急停
12. 工程文件可以手动重命名,按照机型存档,或者导出用于自动化测试
#### 6) 电机电流自动化测试
只需要提前将如下文件放在指定路径下即可:
1. zip 工程文件
2. excel 电机电流结果处理文件,一份模板即可
3. excel configs.xlsx 配置文件
> **需要注意的点**
基本同第五点,针对五轴以及以下的机型,缺失轴数据可以填写其他有效轴任意一轴的点位信息
#### 7) 耐久工程曲线指标采集
可实现固定周期指定曲线指标的采集,用于长时间观察指标的变化情况,输入文件:
- configs.xlsx执行之前需要手动修改好configs.xlsx中的参数以及间隔时间
- target.zip需要确认工程点位和动作无问题后保存导出
> 重新运行时,必须突出软件,重新运行
#### 其他 #### 其他
customtkinter的tabview组件不支持修改字体大小可以参考 [Changing Font of a Tabview](https://github.com/TomSchimansky/CustomTkinter/issues/2296) 进行手动修改源码实现: customtkinter的tabview组件不支持修改字体大小可以参考 [Changing Font of a Tabview](https://github.com/TomSchimansky/CustomTkinter/issues/2296) 进行手动修改源码实现:
a. 运行 `pip show customtkinter`,获取到库的路径 a. 运行 `pip show customtkinter`,获取到库的路径
@ -105,7 +180,7 @@ RELEASE CHANGES
已知问题: 已知问题:
1. office套件下运行好像有问题WPS无问题集中在just_open函数的实现上 1. -
v0.0.1(2024/05/18) v0.0.1(2024/05/18)
Draft Draft
@ -180,32 +255,365 @@ v0.1.4(2024/06/06)
6. 支持工业/协作两条产品线的电机电流数据处理包括单轴场景max/avg计算 6. 支持工业/协作两条产品线的电机电流数据处理包括单轴场景max/avg计算
v0.1.5(2024/06/12) v0.1.5(2024/06/12)
1. [aio.py]: 主界面切换不同功能时保持placehold一致 1. [aio.py] 主界面切换不同功能时保持placehold一致
2. [brake.py]: 由于制动采集模板和内容的更改,适配了新的数据,更新了算法 2. [brake.py] 由于制动采集模板和内容的更改,适配了新的数据,更新了算法
3. [aio.py]: 新增tabview组件区分数据处理和自动化测试功能 3. [aio.py] 新增tabview组件区分数据处理和自动化测试功能
4. [aio.py]: 重新调整界面配色 4. [aio.py] 重新调整界面配色
5. [aio.py]: 修改了write2textbox函数定制化显示每一行的颜色针对每一行可自定义输出内容颜色 5. [aio.py] 修改了write2textbox函数定制化显示每一行的颜色针对每一行可自定义输出内容颜色
6. [brake.py/iso.py/current.py]: 由于第 5 点的更改,同时修改了其他文件相关引用的部分 6. [brake.py/iso.py/current.py] 由于第 5 点的更改,同时修改了其他文件相关引用的部分
7. [aio.py]: 更改label/entry/optionmenu等控件的生成方式使用循环实现更加简洁和容易维护 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组件的字体大小 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功能按钮做扩展是针对第三点做出的相应调整 9. [aio.py] 修改menu_main->menu_main_dpmenu_sub->menu_sub_dp为后续其他tab功能按钮做扩展是针对第三点做出的相应调整
10. [layout.xlsx]: 添加了各个功能的流程图 10. [layout.xlsx] 添加了各个功能的流程图
v0.1.5.1(2024/06/12) v0.1.5.1(2024/06/12)
[current.py]: 修改cycle功能中数据清理范围为70000行并将threshold从2调整为5 1. [current.py] 修改cycle功能中数据清理范围为70000行并将threshold从2调整为5
[current.py]: 修改位置超限提示,使更清楚了解问题原因 2. [current.py] 修改位置超限提示,使更清楚了解问题原因
[current.py]: 修改find_point函数中错误提示增加定位信息 3. [current.py] 修改find_point函数中错误提示增加定位信息
[README.md]: 精简打包命令 4. [README.md] 精简打包命令
[requirements.txt]: 新增必要库配置文件 5. [requirements.txt] 新增必要库配置文件
v0.1.5.2(2024/06/13) v0.1.5.2(2024/06/13)
[brake.py/aio.py]: 将sto修改为estop 1. [brake.py/aio.py]: 将sto修改为estop
[brake.py]: 修改了速度计算逻辑新版本的vel列数据遵循如下规则av = vel * 180 / pi根据av再计算speed 2. [brake.py] 修改了速度计算逻辑新版本的vel列数据遵循如下规则av = vel * 180 / pi根据av再计算speed
[brake.py]: 将threshold修改为常量50 3. [brake.py] 将threshold修改为常量50
[brake.py]: 提高了输出提示语的明确性,删除了不必要的省略号 4. [brake.py] 提高了输出提示语的明确性,删除了不必要的省略号
[brake.py]: 更正了之前的数据copy错误重新优化了estop处是否达到指定百分比的判定逻辑 5. [brake.py] 更正了之前的数据copy错误重新优化了estop处是否达到指定百分比的判定逻辑
v0.1.5.3(2024/06/14) v0.1.5.3(2024/06/14)
[aio.py]: 修改w_param为84适配14寸电脑屏幕 1. [aio.py] 修改w_param为84适配14寸电脑屏幕
[brake.py]: 将判定合规逻辑修改为角速度超过指定角速度的95% 2. [brake.py] 将判定合规逻辑修改为角速度超过指定角速度的95%
[README.md]: 稍作修改,包括打包方式,功能特性等 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做与判断逻辑相同的处理目的是避免形成死循环
v0.1.6.2(2024/06/16)
1. [current.py] 修改了max/avg相关功能中对于返回值的处理逻辑并在输出框以行的形式打印出来
v0.1.6.3(2024/06/18)
1. [current.py] 适配电机电流中速度使用hw_joint_vel_feedback的数据取消对device_servo_vel_feedback的支持后续所有涉及到速度相关的数据均已前者为准现已完成对单轴和场景的适配
> WARNING目前版本的电机电流程序还支持DriverMaster采集的数据处理等明确后将不再支持也即所有的电机电流数据工业+协作),都是用诊断曲线来采集
v0.1.7.0(2024/06/19)-未发布
1. [openapi.py] 初步搭建起框架,完成了新老协议的封包/解包/异步采集日志的操作(未充分测试,但基本无问题)
2. [openapi.py] 修改了封包的规则,使之更加明晰,封包操作没有实现分包功能,目前看实际场景用不到
v0.1.7.0(2024/06/21)-未发布
1. [openapi.py] 定义 MAX_FRAME_SIZE 常量1024替换socket接收以及响应数据处理相关部分
2. [openapi.py] 使用 int.to_bytes 和 int.from_bytes 替换 binascii 模块的功能
3. [aio.py] 修改了Data Process中初始化的动作使得初始化时的状态统一成程序刚启动时的样子
v0.1.7.0(2024/06/23)-未发布
1. [aio.py] 增加了tabview的点击行为函数每次点击tab都会初始化
2. [aio.py] 增加了Automatic Test界面元素包括如下并完成了功能框架的搭建
- 标签:文件/角速度/减速比
- 按钮:急停及恢复
- 输入框:文件路径/角速度/减速比
- OptionMenu负载
- 进度条
3. [openapi.py] 增加心跳检测函数,并开启线程执行;取消在该文件中生成实例
4. [aio.py] 完成detect_network并在main函数开启线程
5. 将templates文件夹移动到assets内
v0.1.7.0(2024/06/24)-未发布
1. [openapi.py] 建联部分做容错逻辑,并将读写文件做自适应处理
2. [aio.py] 将读写文件做自适应处理引入openapi模块并生成实例做心跳检测将socket超时时间修改为3s
v0.1.7.0(2024/06/25)-未发布
1. [aio.py] 取消了在本文件中开启openapi线程的做法并修改如下
- 通过包的方式导入其他模块
- 使用current_path来规避文件路径问题
- 声名了 self.hr 变量用来接收openapi的实例化
- 修改了对于segment button的错误调用
- 设定progress bar的长度是10
- 完善了segmented_button_callback函数
- 在detect_network函数中增加heartbeat初始化
- tabview_click函数中新增textbox清屏功能以及实例化openapi并做检测
2. [openapi.py] 取消了初始化中无限循环检测因为阻塞了aio主界面进程socket也无法多次连接浪费了好多时间很生气
- 通过tabview切换来实现重新连接并保留了异常处理部分
- 将所有的 __xxxx 函数都替换成 xxxx 函数,去掉了 __
- 使用current_path来规避文件路径问题
3. [do_brake.py] 初步完成了机器状态收集的功能,还需要完善
- 使用current_path来规避文件路径问题
- 新增validate_resp函数校验数据
- 完善了调用接口
> **关于HMI接口**
> - 封包解包顺序:帧长度二字节/包长度四字节/协议二字节/预留二字节,\x04\x00:\x00\x00\tR:\x02:\x00
> - 帧长度和包长度没有必然关系单帧的时候是帧长度减去包长度等于6包长度指的是所有内容的长度
> - HMI内部每次发送1024个字节进行分包内容长度规则是第一帧1024-6=1018(帧大小减去包头长度),第二帧(包含)及之后的帧,帧长度即是数据长度
v0.1.7.0(2024/06/26)-初步可用
1. [aio.py] 在detect_network函数中需改查询时间间隔是1s在tabview_click中增加textbox配置normal的语句
2. [do_brake.py -> btn_functions.py] 新增执行相应函数并在get_state函数中设置无示教器模式
3. [openapi.py] 新增sock_conn函数并做连接时的异常处理新增类参数w2t
4. [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组件的字体大小使用原生字体同时将segmented button字体修改为原生为了解决segmented button在禁用和启用时屏幕抖动的问题并将大小修改为16
5. [aio.py] 修改了segmented_button_callback的实现逻辑使代码更简洁
6. [aio.py] 修改了在tabview_click函数中对于实例化HmiRequest的动作使每次切换标签都会重新实例化也就是每次都会重新连接修复显示不正确的问题
7. [openapi.py] 新增了socket关闭的函数并增加msg_id为None的处理逻辑
8. [btn_functions.py] 完善了状态获取的功能,新增告警获取以及功能切换的逻辑
9. [aio.py] 修改了版本
10. [current.py] max/avg功能结束之前会将结果数据追加写入源文件avg算法更改为average+3×std
11. [wavelogger.py] 算法更改为 average+3×std
v0.1.7.1.0(2024/06/29)
1. [APIs: aio.py]
- 对于automatic test删除了输入框使用configs.xlsx配置文件作为参数输入
- 完善initialization/param_check/func_start_callback函数中对于automatic test的处理
- 将textbox组件一直设置为normal状态不再频繁切换disabled
- 将所有的f_h文件对象修改为f_hb并将connection_state修改为c_state
- 在detect_network函数中实例化HmiRequest并在无限循环中检测心跳是否正常如异常则销毁hr重新生成
- 取消在tabview切换时检测心跳的逻辑这样做无法保证实时性
2. [APIs: openapi.py]
- 将sock_conn函数移出__init__单独作为连接函数存在
- 新增全局变量self.t_bool控制所有的线程中无限循环的启停也就是可以人为的退出线程
- 移除close_sock函数
- heartbeat函数中新增打印所有消息的代码调试时打开平常关闭
- execution函数中新增对overview.set_autoload和overview.reload的支持
- execution函数中对send动作增加异常处理逻辑
3. [APIs: do_brake.py]
- 新增文件处理制动测试流程建立连接导入projectpp2mainrun采集并处理曲线数据本地修改RL程序推送至控制器等
- 目前完成:
- 文件合规性检查
- 导入工程并设置为运行工程
4. [APIs: current.py] 修改scenario/single电机电流最大长度为150s
5. 在本文件中更新关于制动自动化测试的相关内容
6. [t_change_ui: aio.py/brake.py/current.py] 整体修改了操作界面,删除了大部分的配置输入框,改用 configs.xlsx 配置文件替代并优化了max/avg功能中写入结果数据的方式
v0.1.7.1.1(2024/06/29)
1. [APIs: aio.py]
- 修改detect_network函数中sleep语句放到最后重新生成HmiRequest实例中增加sleep(4)这个停顿时间一定是比openapi中heartbeat函数的sleep要长1s以上才能正常工作
- 修改write2textbox函数新增默认参数tab_name只有当该值与当前tab一致时函数才会有输出
- 第二条改动影响到了automatic_test文件夹下所有的文件
2. [APIs: openapi.py]
- 规定了所有的网络异常均由heartbeat函数来定义其他异常不做中断处理
- execution函数中合并了case条件
- 增加了N多指令多为诊断曲线和rl程序相关
- 日志保留条数修改为20000
3. [APIs: do_brake.py]
- 实现自动推送工程到xCore并自动运行
- 初步实现了Modbus发送消息和检测状态
4. [APIs: do_current.py]
- 将do_brake.py的内容完全拷贝到此文件待修改
v0.1.7.2(2024/06/30)
1. 初步完成NB4h_R580_3BH7.zip工程的设计
2. 重新研究了解包操作,重新实现了一版
3. 修改openapi.pi中excution为execution函数
4. 增加了解包原理性文档
v0.1.7.3(2024/07/01)
1. [APIs: openapi.py]
- 继续完善封包解包操作并优化了所有调试信息默认打开状态直到bug数量明显减少
- 修复了两个bug删除了一个多余的break另一个是补齐了self.broke的重置
2. [APIs: do_current.py] 使用原工程的工程名进行move操作语义更加明确
> 目前看openapi.py封包解包没有任何问题了但是所有的调试信息都默认打开以便可以第一时间保留现场
> 打开诊断跑了10多分钟共计解包没有报错应该是没有问题了
v0.1.7.4(2024/07/02)
1. [APIs: openapi.py]
- 增加了modbus的python实现
- heartbeat函数修改发送间隔为1s
- 清除了绝大部分调试性输出,发现太多的这种输出也会导致心跳丢包...,不清楚这个原理是什么
- 在get_response函数中的while self.pkg > 0循环中删除了else语句因为它永不会被执行到
- 在get_response函数中修复一个bug在flag==0的else语句中补齐了index==6的情况
2. [APIs: do_current.py]
- 完成了六个轴的电机电流动作的执行,以及数据采集
- 完成了对应的RL程序的编写
3[APIs: aio.py]
- 引入modbus实例化并以参数的形式传递给相应的tabview
- 新增pre_warning函数在做自动化测试之前确保所有条件皆具备
v0.1.7.5(2024/07/03)
1. [APIs: aio.py]
- 增加触发急停和恢复急停功能逻辑
2. [APIs: do_current.py]
- 重新调整运行顺序,增加数据处理的逻辑(惯量负载逻辑暂不实现,等待软件部解决了修改工程之后不生效的问题再考虑)
3. [APIs: btn_functions.py]
- 增加触发急停和恢复急停的modbus实现仅适用于自动化测试
v0.1.7.6(2024/07/04)
1. [APIs: aio.py]
- Automatic Test逻辑中增加选择current时需要选负载类型的逻辑
2. [APIs: do_current.py]
- 单轴/场景电机电流的采集已完成
3. [APIs: openapi.py]
- 增加了modbus读取浮点数的功能
- 优化了get_from_id的逻辑
4. [autotest.xml]: 新增了scenario_time只写寄存器
v0.1.8.0(2024/07/04)
1. [APIs: do_current.py]: 完成了堵转电流和惯量负载电机电流的采集和处理,至此,电机电流的自动化工作基本完成
v0.1.8.1(2024/07/05)
1. [APIs: do_brake.py]: 完成了制动性能测试框架的搭建,可以顺利执行完整的测试程序,但是未实现急停和数据处理
2. [APIs: aio.py]: 修改了do_brake主函数的参数
3. 增加工程文件target.zip
v0.1.8.2(2024/07/08)
1. [APIs: do_brake.py]: 完成了制动性能测试逻辑只不过制动信号传递生效延迟不可控暂时pending
2. [APIs: do_current.py]: 修改曲线数据时序主要是value data取反即可解决了波形锯齿明细的问题
3. [APIs: openapi.py]: modbus新增了触发急停信号的寄存器 stop0_signal并重写了解除急停socket新增了register.set_value协议
v0.1.9.0(2024/07/10)
1. 完成了制动性能的自动化采集
2. 完善了modbus浮点数读写相关的功能
3. 修改了target.zip工程该工程目前适配电机电流和制动性能
v0.1.9.1(2024/07/12)
1. [APIs: do_brake.py]
- 修改正负方向拍急停的逻辑基本原理为运行之前发送正负方向信号pon给RLRL根据信号以及速度正负号运作
- 由于上述修改正负方向急停准确率可达100%
2. [APIs: aio.py]
- 修改write2textbox的输出逻辑实现更加灵活的自定义输出同时修改相关部分
3. [APIs: openapi.py]
- modbus类新增指示政府方向急停的信号pon将modbus类入参中的tab_name删除并修改tab_name的值为'openapi'
- socket类种修改tab_name的值为'openapi'
v0.1.9.2(2024/07/13)
1. [APIs: do_brake.py]
- 修改ready_to_go信号的接收逻辑适配大负载机型
2. [APIs: do_current.py]
- 修改ready_to_go信号的接收逻辑适配大负载机型
- 调整单轴测试时间为35s适配大负载机型调整堵转电流持续时间15s适当减少测试时间
- 将act信号置为False的动作放在初始化增加程序健壮性
- 修改所有输出文件的命名,在扩展名之前加入时间戳
- 删除多余的时序矫正语句——item['value'].reverse(),使输出的曲线为平滑的自然顺序
3. [current: current.py]
- 在find_point函数种当无法找到正确点位时继续执行而不是直接终止执行
- max功能计算逻辑矫正应该是取绝对值的最大值
- 整体梳理了trq/trqh的传递路径现已修正完毕
- 减速比rr数据源修改为configs.xlsx
4. 在current工程main函数增加 VelSet 100语句
v0.1.9.3(2024/07/15)
1. [APIs: openapi.py]
- 修改modbus连接失败报错输出形式使之只在automatic test页面显示
- 将该文件移动至toplevel为后面扩展做准备
- 修改heartbeat文件路径使后续打包的时候更方便
2. [APIs: aio.py]
- 修改heartbeat文件路径使后续打包的时候更方便
- 修改write2textbox函数的打印逻辑先判断网络相关
v0.1.9.4(2024/07/15)
1. [profile: aio.py]完善durable text相关逻辑
2. [profile: do_brake/do_current/btn_functions.py]删除validate_resp函数修改execution函数
3. [profile: factory_test.py]
- 新增耐久/老化测试程序
- 实现六轴折线图显示
4. [profile: openapi.py]:多次合并遗留问题处理
5. templates文件夹组织架构调整
v0.2.0.0(2024/07/17)
1. [profile: aio.py]
- 增加velocity相关逻辑
- 修改负载信息为曲线信息
2. [profile: factory_test.py]
- 增加velocity相关逻辑
3. [profile: current.py]
- 修正减速比获取的规则
4. [profile: openapi.py]
- HmiRequest模块日志取消记录move.monitor相关
- HmiRequest模块增加了durable_lock变量控制文件读写互斥
v0.2.0.1(2024/07/19)
1. [main: aio.py]
- 修改了x轴显示使之为时间刻度
- 修改pre_warning函数增加了durable test的初始化
2. [main: factory_test.py]
- 增加了数据计算错误的判断逻辑
- 增加了历史数据保存的逻辑
- 增加了文件读写互斥的逻辑
- 修改功能为输出有效电流和最大电流,并将数据结构简化
v0.2.0.2(2024/07/26)
1. [main: current.py]
- 修正堵转电流无法正确写入结果文件的问题
2. [main: do_brake.py]
- 初始速度采集等待时间设置为可通过configs.xlsx配置文件调整的
- 初次速度采集停止逻辑修改为tasks.stop指令未验证
- 急停信号触发前pending时间设置为固定值10s
- 实现正负方向速度采集逻辑
- 工程名变更逻辑实现修改为通配符,方便后续根据机型保存文件
- 增加超差后写诊断的逻辑并可以通过configs.xlsx配置文件调整
- 程序输出中增加时间戳,方便调试定位日志时间
3. [main: do_current.py]
- 工程名变更逻辑实现修改为通配符,方便后续根据机型保存文件
4. 为工程文件添加更详细的注释
5. 补充了do_current/do_brake的流程图
6. [main: openapi.py]
- 将modbus motor_on/off的实现方法改为高电平脉冲触发
7. configs.xlsx配置表新增write_diagnosis/get_init_speed两个参数
v0.2.0.3(2024/07/27)
1. [APIs: do_brake.py]: 精简程序,解决 OOM 问题
2. [APIs: do_current.py]: 精简程序,解决 OOM 问题
3. [APIs: factory_test.py]: 精简程序,解决 OOM 问题
4. [APIsL openapi.py]
- 心跳修改为 1 s因为 OOM 问题的解决依赖于长久的打开曲线开关,此时对于 hr.c_msg 的定时清理是个挑战,将心跳缩短,有利于清理日志后,避免丢失心跳
- 新增 diagnosis.save 命令,但是执行时,有问题,待解决
v0.2.0.4(2024/07/30)
1. [APIs: do_brake.py]: 修复制动数据处理过程中,只取曲线的最后 240 个数据
2. [APIs: aio.py]: 判定版本处,删除 self.destroy(),因为该语句会导致异常发生
- 心跳修改为 1s因为 OOM 问题的解决依赖于长久的打开曲线开关,此时对于 hr.c_msg 的定时清理是个挑战,将心跳缩短,有利于清理日志后,避免丢失心跳
- 新增 diagnosis.save 命令,但是执行时,有问题,待解决
v0.2.0.5(2024/07/31)
此版本改动较大,公共部分做了规整,放置到新建文件夹 commons 当中,并所有自定义模块引入 logging 模块,记录重要信息
1. [t_change_ui: clibs.py]
- 调整代码组织结构,新增模块,将公共函数以及类合并入此
- 将一些常量放入该模块
- 引入logging/concurrent_log_handler模块并作初始化操作供其他模块使用按50M切割最多保留10份
- prj_to_xcore函数设置工程名部分重写修复了多个prj工程可能不能执行的问题并优化输入密码的部分
2. [t_change_ui: openapi.py]
- 完全重写了 get_from_id 函数,使更精准
- 在 msg_storage 函数中,增加 logger保留所有响应消息
- 删除 heartbeat 函数中的日志保存功能部分
- 心跳再次修改为 2s...
3. [t_change_ui: aio.py]
- 增加了日志初始化部分
- detect_network 函数中修改重新实例化HR间隔为 4s对应心跳
- create_plot 函数中增加 close('all'),解决循环画图不销毁占用内存的问题
4. [t_change_ui: do_brake.py]
- 使用一直打开曲线的方法规避解决了 OOM 的问题,同时修改数据处理方式,只取最后 12s
- 优化 ssh 输入密码的部分
5. [t_change_ui: do_current.py]
- 保持电流,只取最后 15s
- 优化 ssh 输入密码的部分
6. [t_change_ui: all the part]: 引入 commons 包,并定制了 logging 输出,后续持续优化
7. [APIs: btn_functions.py]: 重写了告警输出函数,从日志中拿数据
8. [APIs: aio.py]: 将日志框输出的内容,也保存至日志文件
9. [APIs: do_brake.py]
- 修改获取初始速度的逻辑只获取configs文件中配置的时间内的速度
- 新增 configs 参数 single_brake可针对特定条件做测试
10. [APIs: all]: 添加了 logger.setLevel(INFO),只有添加这个,单个模块内才生效
v0.2.0.6(2024/08/09)
1. [t_change_ui: all files]
- 修改了 logger 的实现
- 尤其是 clibs.py使用日志字典重写了日志记录的功能
v0.2.0.7(2024/08/16)
1. [t_change_ui: clibs.py]:修改了 hmi.log 的日志等级为 WARNING
2. [t_change_ui: openapi.py]:根据第一步的修改,将此模块日志记录等级调整至 warning
3. [current: current.py]
- README新增了整机自动化测试的前置条件即滑块需要滑动到最右端
- current修改了文件校验的逻辑
4. [t_change_ui: aio.py]
- 修改变量命名widgit -> widget
- 根据第 5 点变动,同步修改代码实现
- 调整 UI 界面代码顺序,使之符合 layout.xlsx 描述
- 将版本检查的部分单独封装成一个函数,在 detect_network 线程初始化时调用一次,并且程序启动也不会受到阻塞
5. [t_change_ui: layout.xlsx]:修改了组件布局方式
> 前两个修改点,修复的是网络提示的颜色不正确问题,因为日志将 textbox 中的内容也作为 debug 信息写入 hmi.log 了

1067
aio/assets/autotest.xml Normal file

File diff suppressed because it is too large Load Diff

BIN
aio/assets/configs.xlsx Normal file

Binary file not shown.

View File

@ -6,8 +6,8 @@ VSVersionInfo(
ffi=FixedFileInfo( ffi=FixedFileInfo(
# filevers and prodvers should be always a tuple with four items: (1, 2, 3, 4) # filevers and prodvers should be always a tuple with four items: (1, 2, 3, 4)
# Set not needed items to zero 0. # Set not needed items to zero 0.
filevers=(0, 1, 5, 3), filevers=(0, 2, 0, 5),
prodvers=(0, 1, 5, 3), prodvers=(0, 2, 0, 5),
# Contains a bitmask that specifies the valid bits 'flags'r # Contains a bitmask that specifies the valid bits 'flags'r
mask=0x3f, mask=0x3f,
# Contains a bitmask that specifies the Boolean attributes of the file. # Contains a bitmask that specifies the Boolean attributes of the file.
@ -31,12 +31,12 @@ VSVersionInfo(
'040904b0', '040904b0',
[StringStruct('CompanyName', 'Rokae - https://www.rokae.com/'), [StringStruct('CompanyName', 'Rokae - https://www.rokae.com/'),
StringStruct('FileDescription', 'All in one automatic toolbox'), StringStruct('FileDescription', 'All in one automatic toolbox'),
StringStruct('FileVersion', '0.1.5.3 (2024-06-14)'), StringStruct('FileVersion', '0.2.0.5 (2024-08-02)'),
StringStruct('InternalName', 'AIO.exe'), StringStruct('InternalName', 'AIO.exe'),
StringStruct('LegalCopyright', '© 2024-2024 Manford Fan'), StringStruct('LegalCopyright', '© 2024-2024 Manford Fan'),
StringStruct('OriginalFilename', 'AIO.exe'), StringStruct('OriginalFilename', 'AIO.exe'),
StringStruct('ProductName', 'AIO'), StringStruct('ProductName', 'AIO'),
StringStruct('ProductVersion', '0.1.5.3 (2024-06-14)')]) StringStruct('ProductVersion', '0.2.0.5 (2024-08-02)')])
]), ]),
VarFileInfo([VarStruct('Translation', [1033, 1200])]) VarFileInfo([VarStruct('Translation', [1033, 1200])])
] ]

Binary file not shown.

View File

@ -1,9 +1,9 @@
openpyxl==3.1.2 concurrent_log_handler==0.9.25
pdfplumber==0.11.0
customtkinter==5.2.2 customtkinter==5.2.2
Jinja2==3.1.4 matplotlib==3.9.1
lxml==5.2.2 numpy==2.0.1
numpy==1.26.4 openpyxl==3.1.2
pandas==2.2.2 pandas==2.2.2
pillow==10.3.0 paramiko==3.4.0
pyinstaller==6.7.0 pdfplumber==0.11.0
pymodbus==3.6.9

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1 @@
0

View File

@ -0,0 +1,5 @@
{
"id": "xxxxxxxxxxx",
"module": "system",
"command": "controller.heart"
}

View File

@ -0,0 +1,5 @@
{
"id": "xxxxxxxxxxx",
"module": "system",
"command": "device.get_params"
}

View File

@ -0,0 +1,8 @@
{
"id": "xxxxxxxxxxx",
"module": "robot",
"command": "diagnosis.get_params",
"data": {
"version": "1.4.1"
}
}

View File

@ -0,0 +1,12 @@
{
"id": "xxxxxxxxxxx",
"module": "robot",
"command": "diagnosis.open",
"data": {
"open": false,
"display_open": false,
"overrun": false,
"turn_area": false,
"delay_motion": false
}
}

View File

@ -0,0 +1,8 @@
{
"id": "xxxxxxxxxxx",
"module": "robot",
"command": "diagnosis.save",
"data": {
"save": true
}
}

View File

@ -0,0 +1,10 @@
{
"id": "xxxxxxxxxxx",
"module": "robot",
"command": "diagnosis.set_params",
"data": {
"display_pdo_params": [],
"frequency": 50,
"version": "1.4.1"
}
}

View File

@ -0,0 +1,5 @@
{
"id": "xxxxxxxxxxx",
"module": "project",
"command": "overview.get_autoload"
}

View File

@ -0,0 +1,5 @@
{
"id": "xxxxxxxxxxx",
"module": "project",
"command": "overview.get_cur_prj"
}

View File

@ -0,0 +1,9 @@
{
"id": "xxxxxxxxxxx",
"module": "project",
"command": "overview.reload",
"data": {
"prj_path": "",
"tasks": []
}
}

View File

@ -0,0 +1,8 @@
{
"id": "xxxxxxxxxxx",
"module": "project",
"command": "overview.set_autoload",
"data": {
"autoload_prj_path": ""
}
}

View File

@ -0,0 +1,11 @@
{
"id": "xxxxxxxxxxx",
"module": "fieldbus",
"command": "register.set_value",
"data": {
"name": "",
"type": "bool",
"bias": 0,
"value": 0
}
}

View File

@ -0,0 +1,8 @@
{
"id": "xxxxxxxxxxx",
"module": "project",
"command": "rl_task.pp_to_main",
"data": {
"tasks": []
}
}

View File

@ -0,0 +1,8 @@
{
"id": "xxxxxxxxxxx",
"module": "project",
"command": "rl_task.run",
"data": {
"tasks": []
}
}

View File

@ -0,0 +1,8 @@
{
"id": "xxxxxxxxxxx",
"module": "project",
"command": "rl_task.stop",
"data": {
"tasks": []
}
}

View File

@ -0,0 +1,5 @@
{
"id": "xxxxxxxxxxx",
"module": "system",
"command": "state.get_state"
}

View File

@ -0,0 +1,5 @@
{
"id": "xxxxxxxxxxx",
"module": "system",
"command": "state.get_tp_mode"
}

View File

@ -0,0 +1,8 @@
{
"id": "xxxxxxxxxxx",
"module": "system",
"command": "state.set_tp_mode",
"data": {
"tp_mode": "with"
}
}

View File

@ -0,0 +1,5 @@
{
"id": "xxxxxxxxxxx",
"module": "system",
"command": "state.switch_auto"
}

View File

@ -0,0 +1,5 @@
{
"id": "xxxxxxxxxxx",
"module": "system",
"command": "state.switch_manual"
}

View File

@ -0,0 +1,5 @@
{
"id": "xxxxxxxxxxx",
"module": "system",
"command": "state.switch_motor_off"
}

View File

@ -0,0 +1,5 @@
{
"id": "xxxxxxxxxxx",
"module": "system",
"command": "state.switch_motor_on"
}

View File

@ -0,0 +1,38 @@
b'\t\t\t-5.5516340429065717e-17,\n\t\t\t\t-5.5516340429065717e-17,\n\t\t\t\t-5.5516340429065717e-17,\n\t\t\t\t-5.5516340429065717e-17,\n\t\t\t\t-5.5516340429065717e-17,\n\t\t\t\t-5.5516340429065717e-17,\n\t\t\t\t-5.5516340429065717e-17,\n\t\t\t\t-5.5516340429065717e-17,\n\t\t\t\t-5.5516340\x01o429065717e-17,\n\t\t\t\t-5.5516340429065717e-17,\n\t\t\t\t-5.5516340429065717e-17,\n\t\t\t\t-5.5516340429065717e-17,\n\t\t\t\t-5.5516340429065717e-17,\n\t\t\t\t-5.5516340429065717e-17,\n\t\t\t\t-5.5516340429065717e-17,\n\t\t\t\t-5.5516340429065717e-17,\n\t\t\t\t-5.5516340429065717e-17,\n\t\t\t\t-5.5516340429065717e-17,\n\t\t\t\t-5.5516340429065717e-17,\n\t\t\t\t-5.5516340429065717e-17\n\t\t\t]\n\t\t}\n\t],\n\t"module" : "robot"\n}\x04\x00\x00\x00-h\x02\x00{\n\t"command" : "diagnosis.result",\n\t"data" : \n\t[\n\t\t{\n\t\t\t"channel" : 0,\n\t\t\t"name" : "hw_joint_vel_feedback",\n\t\t\t"value" : \n\t\t\t[\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0'
b'.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0\n\t\t\t]\n\t\t},\n\t\t{\n\t\t\t"channel" : 1,\n\t\t\t"name" : "hw_joint_vel_feedback",\n\t\t\t"value" : \n\t\t\t[\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t6.7411265092630715e-06,\n\t\t\t\t0.00049929277011941824,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0027270103772138884,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t0.00012044146029883355,\n\t\t\t\t-0.0022272681986605192,\n\t\t\t\t-0.0027270103772138884,\n\t\t\t\t0.0020686270214758614,\n\t\t\t\t-0.0027270103772138884,\n\t\t\t\t0.0020686270214758614,\n\t\t\t\t1.3482253018526145e-06,\n\t\t\t\t-0.002697798\x04\x008290070812,\n\t\t\t\t0.00050648330506263212,\n\t\t\t\t-0.0,\n\t\t\t\t0.0020753681479851243,\n\t\t\t\t2.8762139772855775e-05,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t3.5952674716069715e-05,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t0.0001496530085056402,\n\t\t\t\t0.0001276319952420475,\n\t\t\t\t-0.0027252127434780845,\n\t\t\t\t6.7411265092630715e-06,\n\t\t\t\t-0.0026977988290070812,\n\t\t\t\t8.5387602450665581e-06,\n\t\t\t\t0.0020686270214758614,\n\t\t\t\t0.00206862702147'
b'58614,\n\t\t\t\t-0.0,\n\t\t\t\t7.1905349432139438e-06,\n\t\t\t\t1.3482253018526145e-06,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t2.8762139772855775e-05,\n\t\t\t\t-0.0,\n\t\t\t\t6.7411265092630715e-06,\n\t\t\t\t-0.0,\n\t\t\t\t0.00049929277011941824,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0\n\t\t\t]\n\t\t},\n\t\t{\n\t\t\t"channel" : 2,\n\t\t\t"name" : "hw_joint_vel_feedback",\n\t\t\t"value" : \n\t\t\t[\n\t\t\t\t1.1984224905356572e-06,\n\t\t\t\t0.00010705907582118537,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0024236097500266104,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0024240092241901226,\n\t\t\x04\x00\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t0.0018387795746452099,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t0.00010705907582118537,\n\t\t\t\t-0.0\n\t\t\t]\n\t\t},\n\t\t{\n\t\t\t"channel" : 3,\n\t\t\t"name" : "hw_joint_vel_feedback",\n\t\t\t"value" : \n\t\t\t[\n\t\t\t\t'
b'-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.00021840051467521813,\n\t\t\t\t-0.0\n\t\t\t]\n\t\t},\n\t\t{\n\t\t\t"channel" : 4,\n\t\t\t"name" : "hw_joint_vel_feedback",\n\t\t\t"value" : \n\t\t\t[\n\t\t\t\t-0.0,\n\t\t\t\t-0.0\x04\x00,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n'
b'\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0\n\t\t\t]\n\t\t},\n\t\t{\n\t\t\t"channel" : 5,\n\t\t\t"name" : "hw_joint_vel_feedback",\n\t\t\t"value" : \n\t\t\t[\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-\x04\x000.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0,\n\t\t\t\t-0.0\n\t\t\t]\n\t\t},\n\t\t{\n\t\t\t"channel" : 0,\n\t\t\t"name" : "device_servo_trq_feedback",\n\t\t\t"value" : \n\t\t\t[\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t'
b'\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0\n\t\t\t]\n\t\t},\n\t\t{\n\t\t\t"channel" : 1,\n\t\t\t"name" : "device_servo_trq_feedback",\n\t\t\t"value" : \n\t\t\t[\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0\x04\x00.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0\n\t\t\t]\n\t\t},\n\t\t{\n\t\t\t"channel" : 2,\n\t\t\t"name" : "device_servo_trq_feedback",\n\t\t\t"value" : \n\t\t\t[\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.'
b'0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0\n\t\t\t]\n\t\t},\n\t\t{\n\t\t\t"channel" : 3,\n\t\t\t"name" : "device_servo_trq_feedback",\n\t\t\t"value" : \n\t\t\t[\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\x04\x00\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0\n\t\t\t]\n\t\t},\n\t\t{\n\t\t\t"channel" : 4,\n\t\t\t"name" : "device_servo_trq_feedback",\n\t\t\t"value" : \n\t\t\t[\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n'
b'\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0\n\t\t\t]\n\t\t},\n\t\t{\n\t\t\t"channel" : 5,\n\t\t\t"name" : "device_servo_trq_feedback",\n\t\t\t"value" : \n\t\t\t[\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.\x04\x000,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0\n\t\t\t]\n\t\t},\n\t\t{\n\t\t\t"channel" : 0,\n\t\t\t"name" : "device_safety_estop",\n\t\t\t"value" : \n\t\t\t[\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n'
b'\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0\n\t\t\t]\n\t\t},\n\t\t{\n\t\t\t"channel" : 1,\n\t\t\t"name" : "device_safety_estop",\n\t\t\t"value" : \n\t\t\t[\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1\x04\x00.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0\n\t\t\t]\n\t\t},\n\t\t{\n\t\t\t"channel" : 2,\n\t\t\t"name" : "device_safety_estop",\n\t\t\t"value" : \n\t\t\t[\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.'
b'0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0\n\t\t\t]\n\t\t},\n\t\t{\n\t\t\t"channel" : 3,\n\t\t\t"name" : "device_safety_estop",\n\t\t\t"value" : \n\t\t\t[\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t\x04\x001.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0,\n\t\t\t\t1.0\n\t\t\t]\n\t\t},\n\t\t{\n\t\t\t"channel" : 4,\n\t\t\t"name" : "device_safety_estop",\n\t\t\t"value" : '
b'\n\t\t\t[\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0\n\t\t\t]\n\t\t},\n\t\t{\n\t\t\t"channel" : 5,\n\t\t\t"name" : "device_safety_estop",\n\t\t\t"value" : \n\t\t\t[\n\t\t\t\t-5.5516340429065717e-17,\n\t\t\t\t-5.5516340429065717e-17,\n\t\t\t\t-5.5516340429065717e-17,\n\t\t\t\t-5\x04\x00.5516340429065717e-17,\n\t\t\t\t-5.5516340429065717e-17,\n\t\t\t\t-5.5516340429065717e-17,\n\t\t\t\t-5.5516340429065717e-17,\n\t\t\t\t-5.5516340429065717e-17,\n\t\t\t\t-5.5516340429065717e-17,\n\t\t\t\t-5.5516340429065717e-17,\n\t\t\t\t-5.5516340429065717e-17,\n\t\t\t\t-5.5516340429065717e-17,\n\t\t\t\t-5.5516340429065717e-17,\n\t\t\t\t-5.5516340429065717e-17,\n\t\t\t\t-5.5516340429065717e-17,\n\t\t\t\t-5.5516340429065717e-17,\n\t\t\t\t-5.5516340429'
b'065717e-17,\n\t\t\t\t-5.5516340429065717e-17,\n\t\t\t\t-5.5516340429065717e-17,\n\t\t\t\t-5.5516340429065717e-17,\n\t\t\t\t-5.5516340429065717e-17,\n\t\t\t\t-5.5516340429065717e-17,\n\t\t\t\t-5.5516340429065717e-17,\n\t\t\t\t-5.5516340429065717e-17,\n\t\t\t\t-5.5516340429065717e-17,\n\t\t\t\t-5.5516340429065717e-17,\n\t\t\t\t-5.5516340429065717e-17,\n\t\t\t\t-5.5516340429065717e-17,\n\t\t\t\t-5.5516340429065717e-17,\n\t\t\t\t-5.5516340429065717e-17,\n\t\t\t\t-5.5516340429065717e-17,\n\t\t\t\t-5.5516340429065717e-17,\n\t\t\t\t-5.5516340429065717e-17,\n\t\t\t\t-5.5516340429065717e-17,\n\t\t\t\t-5.5516340429065717e-17,\n\t\t\t\t-5.5516340429065717e-17,\n\t\t\t\t-5.5516340429065717e-17,\n\t\t\t\t-5.5516340429065717e-17,\n\t\t\t\t-5.55163404\x01n29065717e-17,\n\t\t\t\t-5.5516340429065717e-17,\n\t\t\t\t-5.5516340429065717e-17,\n\t\t\t\t-5.5516340429065717e-17,\n\t\t\t\t-5.5516340429065717e-17,\n\t\t\t\t-5.5516340429065717e-17,\n\t\t\t\t-5.5516340429065717e-17,\n\t\t\t\t-5.5516340429065717e-17,\n\t\t\t\t-5.5516340429065717e-17,\n\t\t\t\t-5.5516340429065717e-17,\n\t\t\t\t-5.5516340429065717e-17,\n\t\t\t\t-5.5516340429065717e-17\n\t\t\t]\n\t\t}\n\t],\n\t"module" : "robot"\n}\x04\x00\x00\x00-\x8f\x02\x00{\n\t"command"'
\x04\x00\x00\x00-h\x02\x00
pkg_size = 11624
10240 + 402 + 1004 - 20 - 2(首个帧头) = 11624
第一帧有效 - 402
b'{\n\t"command" : "diagnosis.result",\n\t"data" : \n\t[\n\t\t{\n\t\t\t"channel" : 0,\n\t\t\t"name" : "hw_joint_vel_feedback",\n\t\t\t"value" : \n\t\t\t[\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0.0,\n\t\t\t\t0'
中间有 10 帧,共计 10240 个字节,也有 10 个 \x04\x00共计 20 个字节
最后一帧有效 - 1004
b'065717e-17,\n\t\t\t\t-5.5516340429065717e-17,\n\t\t\t\t-5.5516340429065717e-17,\n\t\t\t\t-5.5516340429065717e-17,\n\t\t\t\t-5.5516340429065717e-17,\n\t\t\t\t-5.5516340429065717e-17,\n\t\t\t\t-5.5516340429065717e-17,\n\t\t\t\t-5.5516340429065717e-17,\n\t\t\t\t-5.5516340429065717e-17,\n\t\t\t\t-5.5516340429065717e-17,\n\t\t\t\t-5.5516340429065717e-17,\n\t\t\t\t-5.5516340429065717e-17,\n\t\t\t\t-5.5516340429065717e-17,\n\t\t\t\t-5.5516340429065717e-17,\n\t\t\t\t-5.5516340429065717e-17,\n\t\t\t\t-5.5516340429065717e-17,\n\t\t\t\t-5.5516340429065717e-17,\n\t\t\t\t-5.5516340429065717e-17,\n\t\t\t\t-5.5516340429065717e-17,\n\t\t\t\t-5.5516340429065717e-17,\n\t\t\t\t-5.5516340429065717e-17,\n\t\t\t\t-5.5516340429065717e-17,\n\t\t\t\t-5.55163404\x01n29065717e-17,\n\t\t\t\t-5.5516340429065717e-17,\n\t\t\t\t-5.5516340429065717e-17,\n\t\t\t\t-5.5516340429065717e-17,\n\t\t\t\t-5.5516340429065717e-17,\n\t\t\t\t-5.5516340429065717e-17,\n\t\t\t\t-5.5516340429065717e-17,\n\t\t\t\t-5.5516340429065717e-17,\n\t\t\t\t-5.5516340429065717e-17,\n\t\t\t\t-5.5516340429065717e-17,\n\t\t\t\t-5.5516340429065717e-17,\n\t\t\t\t-5.5516340429065717e-17\n\t\t\t]\n\t\t}\n\t],\n\t"module" : "robot"\n}'
b'\x00\\\x00\x00\x00V\x02\x00{\n\t"data" : \n\t{\n\t\t"name" : "xCore"\n\t},\n\t"id" : "controller.heart-1719734550.9790015"\n}'
frame size 92
package size 86
length 86 {\n\t"data" : \n\t{\n\t\t"name" : "xCore"\n\t},\n\t"id" : "controller.heart-1719734550.9790015"\n}'

View File

@ -1 +1 @@
0.1.5.3 @ 06/14/2024 0.2.0.5 @ 08/02/2024

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,43 +1,57 @@
import tkinter
from os.path import exists from os.path import exists
from os import getcwd from os import getcwd, remove
from threading import Thread from threading import Thread
import tkinter.messagebox import tkinter.messagebox
import customtkinter import customtkinter
import brake, current, iso from time import time, strftime, localtime, sleep
from time import time, strftime, localtime
from urllib.request import urlopen from urllib.request import urlopen
from socket import setdefaulttimeout from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from data_process import brake, current, iso, wavelogger
from automatic_test import do_current, do_brake, btn_functions
from durable_action import factory_test
from commons import openapi, clibs
from matplotlib.pyplot import rcParams, figure, subplots_adjust, close
from matplotlib import use
from pandas import DataFrame, read_excel
with open(clibs.log_data_hmi, 'w') as _hmi, open(clibs.log_data_debug, 'w', encoding='utf-8') as _debug:
for i in range(1, 11):
try:
remove(f'{clibs.log_data_hmi}.{i}')
except FileNotFoundError:
pass
logger = clibs.log_prod
logger.info("日志文件初始化完成...")
use('Agg')
customtkinter.set_appearance_mode("System") # Modes: "System" (standard), "Dark", "Light" customtkinter.set_appearance_mode("System") # Modes: "System" (standard), "Dark", "Light"
customtkinter.set_default_color_theme("blue") # Themes: "blue" (standard), "green", "dark-blue" 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_widget_scaling(1.1) # widget dimensions and text size
customtkinter.set_window_scaling(1.1) # window geometry dimensions customtkinter.set_window_scaling(1.1) # window geometry dimensions
setdefaulttimeout(10)
# global vars # global vars
btns = { btns_func = {
'start': {'btn': '', 'row': 1, 'text': '开始运行'}, 'start': {'btn': '', 'row': 1, 'text': '开始运行'},
'check': {'btn': '', 'row': 2, 'text': '检查参数'}, 'check': {'btn': '', 'row': 2, 'text': '检查参数'},
'log': {'btn': '', 'row': 3, 'text': '保存日志'}, 'log': {'btn': '', 'row': 3, 'text': '保存日志'},
'end': {'btn': '', 'row': 4, 'text': '结束运行'}, 'end': {'btn': '', 'row': 4, 'text': '结束运行'},
} }
widgits = { widgets_dp = {
'path': {'label': '', 'entry': '', 'row': 1, 'col': 2, 'text': '数据文件夹路径'}, 'path': {'label': '', 'entry': '', 'row': 0, 'col': 1, 'text': '数据文件夹路径'},
'av': {'label': '', 'entry': '', 'row': 2, 'col': 2, 'text': '角速度'}, 'dur': {'label': '', 'entry': '', 'row': 1, 'col': 1, 'text': '周期时间'},
'rc': {'label': '', 'entry': '', 'row': 2, 'col': 4, 'text': '额定电流'}, 'vel': {'label': '', 'optionmenu': '', 'row': 1, 'col': 1, 'text': ''},
'rpm': {'label': '', 'entry': '', 'row': 2, 'col': 6, 'text': '额定转速'}, 'trq': {'label': '', 'optionmenu': '', 'row': 1, 'col': 3, 'text': ''},
'rr': {'label': '', 'entry': '', 'row': 2, 'col': 8, 'text': '减速比'}, 'trqh': {'label': '', 'optionmenu': '', 'row': 1, 'col': 5, 'text': ''},
'dur': {'label': '', 'entry': '', 'row': 2, 'col': 10, 'text': '周期时间'}, 'estop': {'label': '', 'optionmenu': '', 'row': 1, 'col': 7, 'text': ''},
'axis': {'label': '', 'optionmenu': '', 'row': 3, 'col': 2, 'text': ''}, }
'vel': {'label': '', 'optionmenu': '', 'row': 3, 'col': 4, 'text': ''}, widgets_at = {
'trq': {'label': '', 'optionmenu': '', 'row': 3, 'col': 6, 'text': ''}, 'path': {'label': '', 'entry': '', 'row': 1, 'col': 1, 'text': '数据文件夹路径'},
'trqh': {'label': '', 'optionmenu': '', 'row': 3, 'col': 8, 'text': ''}, 'loadsel': {'label': '', 'optionmenu': '', 'row': 1, 'col': 0, 'text': '负载信息'},
'estop': {'label': '', 'optionmenu': '', 'row': 3, 'col': 10, 'text': ''}, }
'rc1': {'label': '', 'entry': '', 'row': 4, 'col': 2, 'text': '额定电流'}, widgets_da = {
'rc2': {'label': '', 'entry': '', 'row': 4, 'col': 4, 'text': '额定电流'}, 'path': {'label': '', 'entry': '', 'row': 0, 'col': 1, 'text': '数据文件夹路径'},
'rc3': {'label': '', 'entry': '', 'row': 4, 'col': 6, 'text': '额定电流'}, 'curvesel': {'label': '', 'optionmenu': '', 'row': 0, 'col': 0, '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': '额定电流'},
} }
@ -46,6 +60,13 @@ class App(customtkinter.CTk):
super().__init__() super().__init__()
self.my_font = customtkinter.CTkFont(family="Consolas", size=16, weight="bold") self.my_font = customtkinter.CTkFont(family="Consolas", size=16, weight="bold")
self.w_param = 84 self.w_param = 84
self.hr = None
self.md = None
self.canvas = None
self.flg = 0
self.df_copy = None
self.old_curve = None
self.myThread = None
# ===================================================================== # =====================================================================
# configure window # configure window
self.title("AIO - All in one automatic toolbox") self.title("AIO - All in one automatic toolbox")
@ -53,66 +74,125 @@ class App(customtkinter.CTk):
self.geometry("1200x550+30+30") self.geometry("1200x550+30+30")
self.protocol("WM_DELETE_WINDOW", self.func_end_callback) self.protocol("WM_DELETE_WINDOW", self.func_end_callback)
self.config(bg='#E9E9E9') self.config(bg='#E9E9E9')
self.grid_rowconfigure(5, weight=1) self.rowconfigure(0, weight=1)
self.grid_columnconfigure((1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13), weight=1) self.rowconfigure(1, weight=19)
self.columnconfigure(0, weight=1)
self.columnconfigure(1, weight=19)
self.minsize(1200, 550) self.minsize(1200, 550)
# ===================================================================== # =====================================================================
# create frame sidebar(left) # 1. 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') self.frame_func = customtkinter.CTkFrame(self, width=200, corner_radius=0, fg_color='#E9E9E9')
# create AIO logo self.frame_func.grid(row=0, column=0, rowspan=2, sticky='nsew')
# 1.1 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 = 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) self.label_logo.grid(row=0, column=0, padx=15, pady=15)
# create buttons # 1.2 create buttons
for func in btns: for func in btns_func:
btns[func]['btn'] = customtkinter.CTkButton(self.frame_func, corner_radius=10, text=btns[func]['text'], fg_color='#4F4F4F', font=self.my_font) btns_func[func]['btn'] = customtkinter.CTkButton(self.frame_func, corner_radius=10, text=btns_func[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_func[func]['btn'].grid(row=btns_func[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_func['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_func['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_func['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)) btns_func['end']['btn'].configure(command=lambda: self.thread_it(self.func_end_callback))
# create version info # 1.3 create version info
self.label_version = customtkinter.CTkLabel(self.frame_func, justify='left', text="Vers: 0.1.5.3\nDate: 06/14/2024", font=self.my_font, text_color="#4F4F4F") self.label_version = customtkinter.CTkLabel(self.frame_func, justify='left', text="Vers: 0.2.0.5\nDate: 08/02/2024", font=self.my_font, text_color="#4F4F4F")
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')
# ===================================================================== # =====================================================================
# create tabviews # 2. create tabviews
self.tabview = customtkinter.CTkTabview(self, width=10000, height=100, anchor='w', fg_color='#E9E9E9', border_width=2, border_color='#CDCDCD') # =====================================================================
self.tabview = customtkinter.CTkTabview(self, anchor='w', fg_color='#E9E9E9', width=1000, height=45, border_width=2, border_color='#CDCDCD', command=self.tabview_click)
self.tabview.grid(row=0, column=1, padx=10, pady=5, sticky="nsew") self.tabview.grid(row=0, column=1, padx=10, pady=5, sticky="nsew")
self.tabview.add("Data Process") self.tabview.add("Data Process")
self.tabview.add("Automatic Test") self.tabview.add("Automatic Test")
# create main menu self.tabview.add("Durable Action")
self.menu_main_dp = customtkinter.CTkOptionMenu(self.tabview.tab('Data Process'), values=["INIT", "brake", "current", "iso"], font=self.my_font, text_color='yellow', button_color='red', fg_color='green', command=self.func_main_callback) # 2.1 create widgets of tab "Data Process"
self.menu_main_dp.grid(row=1, column=1, sticky='we', padx=5, pady=5) # 2.1.1 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=0, column=0, sticky='we', padx=5, pady=10)
self.menu_main_dp.set("Start Here!") self.menu_main_dp.set("Start Here!")
# create sub menu # 2.2.2 create sub menu
self.menu_sub_dp = customtkinter.CTkOptionMenu(self.tabview.tab('Data Process')) self.menu_sub_dp = customtkinter.CTkOptionMenu(self.tabview.tab('Data Process'))
# 2.2.3 create labels, entries and option menus
for widget in widgets_dp:
if widget == 'path':
self.tabview.tab('Data Process').columnconfigure(12, weight=1)
widgets_dp[widget]['label'] = customtkinter.CTkLabel(self.tabview.tab('Data Process'), text=f'{widget.upper()}', font=self.my_font)
widgets_dp[widget]['label'].grid(row=widgets_dp[widget]['row'], column=widgets_dp[widget]['col'], sticky='e', pady=10)
widgets_dp[widget]['entry'] = customtkinter.CTkEntry(self.tabview.tab('Data Process'), placeholder_text=widgets_dp[widget]['text'], font=self.my_font)
widgets_dp[widget]['entry'].grid(row=widgets_dp[widget]['row'], column=widgets_dp[widget]['col']+1, columnspan=11, padx=(5, 10), pady=5, sticky='we')
widgets_dp[widget]['entry'].configure(state='disabled')
elif widget in ['dur']:
widgets_dp[widget]['label'] = customtkinter.CTkLabel(self.tabview.tab('Data Process'), text=f"{widget.upper()}", font=self.my_font)
widgets_dp[widget]['label'].grid(row=widgets_dp[widget]['row'], column=widgets_dp[widget]['col'], sticky='e', pady=5)
widgets_dp[widget]['entry'] = customtkinter.CTkEntry(self.tabview.tab('Data Process'), width=self.w_param, placeholder_text=f"{widgets_dp[widget]['text']}", font=self.my_font)
widgets_dp[widget]['entry'].grid(row=widgets_dp[widget]['row'], column=widgets_dp[widget]['col']+1, padx=(5, 10), pady=5, sticky='w')
widgets_dp[widget]['entry'].configure(state='disabled')
elif widget in ['vel', 'trq', 'trqh', 'estop']:
widgets_dp[widget]['label'] = customtkinter.CTkLabel(self.tabview.tab('Data Process'), text=f"{widget.upper()}", font=self.my_font)
widgets_dp[widget]['label'].grid(row=widgets_dp[widget]['row'], column=widgets_dp[widget]['col'], sticky='e', pady=5)
widgets_dp[widget]['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)
widgets_dp[widget]['optionmenu'].grid(row=widgets_dp[widget]['row'], column=widgets_dp[widget]['col']+1, padx=(5, 10), pady=5, sticky='w')
widgets_dp[widget]['optionmenu'].configure(state='disabled')
# ===================================================================== # =====================================================================
# create widgits # 2.2 create widgets of tab "Automatic Test"
for widgit in widgits: # 2.2.1 create main menu
if widgit == 'path': self.menu_main_at = customtkinter.CTkOptionMenu(self.tabview.tab('Automatic Test'), values=["init", "brake", "current"], font=self.my_font, text_color='yellow', button_color='red', fg_color='green', command=self.func_main_callback)
widgits[widgit]['label'] = customtkinter.CTkLabel(self.tabview.tab('Data Process'), text=f'{widgit.upper()}', font=self.my_font) self.menu_main_at.grid(row=0, column=0, sticky='we', padx=5, pady=5)
widgits[widgit]['label'].grid(row=widgits[widgit]['row'], column=widgits[widgit]['col'], sticky='e', pady=5) self.menu_main_at.set("Start Here!")
widgits[widgit]['entry'] = customtkinter.CTkEntry(self.tabview.tab('Data Process'), width=670, placeholder_text=widgits[widgit]['text'], font=self.my_font) # 2.2.2 create segment buttons
widgits[widgit]['entry'].grid(row=widgits[widgit]['row'], column=widgits[widgit]['col']+1, columnspan=11, padx=(5, 10), pady=5, sticky='we') self.seg_button = customtkinter.CTkSegmentedButton(self.tabview.tab('Automatic Test'), dynamic_resizing=False, font=customtkinter.CTkFont(size=16, weight='bold'), command=lambda value='机器状态': self.thread_it(self.segmented_button_callback))
widgits[widgit]['entry'].configure(state='disabled') self.seg_button.grid(row=0, column=1, columnspan=12, padx=(65, 10), pady=(10, 10), sticky="ew")
elif widgit in ['av', 'rc', 'rpm', 'rr', 'dur', 'rc1', 'rc2', 'rc3', 'rc4', 'rc5', 'rc6']: self.seg_button.configure(dynamic_resizing=False, values=["功能切换", "触发急停", "恢复急停", "待定功能", "功能待定", "机器状态", "告警信息"])
widgits[widgit]['label'] = customtkinter.CTkLabel(self.tabview.tab('Data Process'), text=f"{widgit.upper()}", font=self.my_font) self.seg_button.set("功能切换")
widgits[widgit]['label'].grid(row=widgits[widgit]['row'], column=widgits[widgit]['col'], sticky='e', pady=5) # 2.2.3 create progress bar
widgits[widgit]['entry'] = customtkinter.CTkEntry(self.tabview.tab('Data Process'), width=self.w_param, placeholder_text=f"{widgits[widgit]['text']}", font=self.my_font) self.progressbar = customtkinter.CTkProgressBar(self.tabview.tab('Automatic Test'))
widgits[widgit]['entry'].grid(row=widgits[widgit]['row'], column=widgits[widgit]['col']+1, padx=(5, 10), pady=5, sticky='w') self.progressbar.grid(row=2, column=0, padx=5, pady=5, sticky="ew")
widgits[widgit]['entry'].configure(state='disabled') self.progressbar.configure(mode="determinnate", width=self.w_param)
elif widgit in ['axis', 'vel', 'trq', 'trqh', 'estop']: self.progressbar.start()
widgits[widgit]['label'] = customtkinter.CTkLabel(self.tabview.tab('Data Process'), text=f"{widgit.upper()}", font=self.my_font) # 2.2.4 create labels, entries and option menus
widgits[widgit]['label'].grid(row=widgits[widgit]['row'], column=widgits[widgit]['col'], sticky='e', pady=5) for widget in widgets_at:
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) if widget == 'path':
widgits[widgit]['optionmenu'].grid(row=widgits[widgit]['row'], column=widgits[widgit]['col']+1, padx=(5, 10), pady=5, sticky='w') self.tabview.tab('Automatic Test').columnconfigure(12, weight=1)
widgits[widgit]['optionmenu'].configure(state='disabled') widgets_at[widget]['label'] = customtkinter.CTkLabel(self.tabview.tab('Automatic Test'), text=f'{widget.upper()}', font=self.my_font)
widgets_at[widget]['label'].grid(row=widgets_at[widget]['row'], column=widgets_at[widget]['col'], sticky='e', padx=(20, 5), pady=5)
widgets_at[widget]['entry'] = customtkinter.CTkEntry(self.tabview.tab('Automatic Test'), placeholder_text=widgets_at[widget]['text'], font=self.my_font)
widgets_at[widget]['entry'].grid(row=widgets_at[widget]['row'], column=widgets_at[widget]['col']+1, columnspan=11, padx=(5, 10), pady=5, sticky='we')
widgets_at[widget]['entry'].configure(state='disabled')
elif widget in ['loadsel', ]:
widgets_at[widget]['optionmenu'] = customtkinter.CTkOptionMenu(self.tabview.tab('Automatic Test'), button_color='#708090', fg_color='#778899', values=["tool33", "tool66", "tool100", "inertia"], width=self.w_param, font=self.my_font)
widgets_at[widget]['optionmenu'].grid(row=widgets_at[widget]['row'], column=widgets_at[widget]['col'], padx=5, pady=5, sticky='we')
widgets_at[widget]['optionmenu'].set(widgets_at[widget]['text'])
widgets_at[widget]['optionmenu'].configure(state='disabled')
# =====================================================================
# 2.3 create widgets of tab "Durable Action"
# 2.3.1 create progress bar
self.progressbar_da = customtkinter.CTkProgressBar(self.tabview.tab('Durable Action'))
self.progressbar_da.grid(row=1, column=0, padx=5, pady=5, sticky="ew")
self.progressbar_da.configure(mode="determinnate", width=self.w_param)
self.progressbar_da.start()
# 2.3.2 create labels, entries and option menus
for widget in widgets_da:
if widget == 'path':
self.tabview.tab('Durable Action').columnconfigure(12, weight=1)
widgets_da[widget]['label'] = customtkinter.CTkLabel(self.tabview.tab('Durable Action'), text=f'{widget.upper()}', font=self.my_font)
widgets_da[widget]['label'].grid(row=widgets_da[widget]['row'], column=widgets_da[widget]['col'], sticky='e', padx=(20, 5), pady=10)
widgets_da[widget]['entry'] = customtkinter.CTkEntry(self.tabview.tab('Durable Action'), placeholder_text=widgets_da[widget]['text'], font=self.my_font)
widgets_da[widget]['entry'].grid(row=widgets_da[widget]['row'], column=widgets_da[widget]['col']+1, columnspan=11, padx=(5, 10), pady=10, sticky='we')
elif widget in ['curvesel']:
widgets_da[widget]['optionmenu'] = customtkinter.CTkOptionMenu(self.tabview.tab('Durable Action'), dynamic_resizing=False, button_color='#708090', fg_color='#778899', values=['device_servo_trq_feedback', '[max] device_servo_trq_feedback'], font=self.my_font)
widgets_da[widget]['optionmenu'].grid(row=widgets_da[widget]['row'], column=widgets_da[widget]['col'], padx=5, pady=10, sticky='we')
widgets_da[widget]['optionmenu'].set(widgets_da[widget]['text'])
# =====================================================================
# 3. create textbox
# ===================================================================== # =====================================================================
# 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 = 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.grid(row=1, column=1, columnspan=13, ipadx=10, ipady=10, padx=10, pady=(5, 10), sticky='nsew')
self.textbox.configure(state='disabled') self.textbox.configure(state='normal')
# functions below ↓ ----------------------------------------------------------------------------------------
def version_check(self):
# ===================================================================== # =====================================================================
# version check # version check
cur_vers = self.label_version.cget("text").replace('\n', ' @ ').replace("Vers: ", '').replace("Date: ", '') cur_vers = self.label_version.cget("text").replace('\n', ' @ ').replace("Vers: ", '').replace("Date: ", '')
@ -120,94 +200,283 @@ class App(customtkinter.CTk):
try: try:
new_vers = urlopen(url_vers).read().decode('utf-8') new_vers = urlopen(url_vers).read().decode('utf-8')
if cur_vers.strip() != new_vers.strip(): if cur_vers.strip() != new_vers.strip():
msg = f"""当前版本:{cur_vers}\n更新版本:{new_vers}\n\n请及时更新 http://10.2.23.150:10003/s/jRfM""" msg = f"""当前版本:{cur_vers}\n更新版本:{new_vers}\n\n请及时前往钉盘更新~~~"""
tkinter.messagebox.showwarning(title="版本更新", message=msg) tkinter.messagebox.showwarning(title="版本更新", message=msg)
except: except:
tkinter.messagebox.showwarning(title="版本更新", message="连接服务器失败,无法确认当前是否是最新版本......") tkinter.messagebox.showwarning(title="版本更新", message="连接服务器失败,无法确认当前是否是最新版本......")
def create_canvas(self, _figure):
self.canvas = FigureCanvasTkAgg(_figure, self.tabview.tab('Durable Action'))
self.canvas.draw()
self.canvas.get_tk_widget().configure(height=600)
self.canvas.get_tk_widget().grid(row=2, column=0, columnspan=13, padx=10, pady=10, sticky="nsew")
def create_plot(self):
rcParams['font.sans-serif'] = ['SimHei']
rcParams['axes.unicode_minus'] = False
rcParams['figure.dpi'] = 100
rcParams['font.size'] = 14
rcParams['lines.marker'] = 'o'
curvesel = widgets_da['curvesel']['optionmenu'].get()
while True:
if not self.hr.durable_lock:
self.hr.durable_lock = 1
if curvesel == 'device_servo_trq_feedback':
df = read_excel(clibs.durable_data_current_xlsx)
_title = 'device_servo_trq_feedback'
elif curvesel == '[max] device_servo_trq_feedback':
_title = '[max] device_servo_trq_feedback'
df = read_excel(clibs.durable_data_current_max_xlsx)
else:
_title = 'device_servo_trq_feedback'
df = read_excel(clibs.durable_data_current_xlsx)
self.hr.durable_lock = 0
break
else:
sleep(1)
if not df.equals(self.df_copy) or self.flg == 0 or curvesel != self.old_curve:
self.flg = 1
self.df_copy = df.copy()
self.old_curve = widgets_da['curvesel']['optionmenu'].get()
close('all')
_figure = figure(frameon=True, facecolor='#E9E9E9')
subplots_adjust(left=0.04, right=0.98, bottom=0.1, top=0.95)
_ = df['time'].to_list()
_xticks = [str(_i) for _i in _]
ax = _figure.add_subplot(1, 1, 1)
ax.set_xticks(range(len(_xticks)))
ax.set_xticklabels(_xticks)
df.plot(grid=True, x='time', y='axis1', ax=ax)
df.plot(grid=True, x='time', y='axis2', ax=ax)
df.plot(grid=True, x='time', y='axis3', ax=ax)
df.plot(grid=True, x='time', y='axis4', ax=ax)
df.plot(grid=True, x='time', y='axis5', ax=ax)
df.plot(grid=True, x='time', y='axis6', ax=ax, title=_title, legend='upper left', rot=30)
self.create_canvas(_figure)
def thread_it(self, func, *args): def thread_it(self, func, *args):
""" 将函数打包进线程 """ """ 将函数打包进线程 """
self.myThread = Thread(target=func, args=args) self.myThread = Thread(target=func, args=args)
self.myThread.daemon = True # 主线程退出就直接让子线程跟随退出,不论是否运行完成。 self.myThread.daemon = True # 主线程退出就直接让子线程跟随退出,不论是否运行完成。
self.myThread.start() self.myThread.start()
def segmented_button_callback(self):
_btn_funcs = {'trigger_estop': '触发急停', 'reset_estop': '恢复急停', 'get_state': '机器状态', 'warning_info': '告警信息'}
value = self.seg_button.get()
self.seg_button.configure(state='disabled')
# self.tabview.configure(state='disabled')
self.textbox.delete(index1='1.0', index2='end')
with open(clibs.heartbeat, 'r', encoding='utf-8') as f_h:
c_state = f_h.read().strip()
if c_state == '0' and value != '功能切换':
self.write2textbox("无法连接机器人检查是否已经使用Robot Assist软件连接机器重试中...", 0, 50, 'red')
else:
for _func in _btn_funcs:
if _btn_funcs[_func] == value:
btn_functions.main(self.hr, self.md, _func, self.write2textbox)
break
self.seg_button.configure(state='normal')
# self.tabview.configure(state='normal')
def detect_network(self):
self.version_check()
df = DataFrame(clibs.durable_data_current)
df.to_excel(clibs.durable_data_current_xlsx, index=False)
df = DataFrame(clibs.durable_data_current_max)
df.to_excel(clibs.durable_data_current_max_xlsx, index=False)
with open(clibs.heartbeat, "w", encoding='utf-8') as f_hb:
f_hb.write('0')
self.hr = openapi.HmiRequest(self.write2textbox)
self.md = openapi.ModbusRequest(self.write2textbox)
while True:
if self.tabview.get() == 'Durable Action':
self.create_plot()
with open(clibs.heartbeat, 'r', encoding='utf-8') as f_hb:
c_state = f_hb.read().strip()
pb_color = 'green' if c_state == '1' else 'red'
self.progressbar.configure(progress_color=pb_color)
self.progressbar_da.configure(progress_color=pb_color)
if c_state == '0':
self.hr.t_bool = False
sleep(4)
del self.hr
self.hr = openapi.HmiRequest(self.write2textbox)
sleep(3)
def tabview_click(self):
self.initialization()
tab_name = self.tabview.get()
if tab_name == 'Data Process':
self.flg = 0
self.menu_main_dp.set("Start Here!")
elif tab_name == 'Automatic Test':
self.flg = 0
self.menu_main_at.set("Start Here!")
self.seg_button.configure(state='normal')
elif tab_name == 'Durable Action':
pass
def initialization(self): def initialization(self):
for widgit in widgits: tab_name = self.tabview.get()
if widgit in ['path', 'av', 'rc', 'rpm', 'rr', 'dur', 'rc1', 'rc2', 'rc3', 'rc4', 'rc5', 'rc6']: self.textbox.delete(index1='1.0', index2='end')
widgits[widgit]['label'].configure(text=f'{widgit.upper()}', text_color='black') if tab_name == 'Data Process':
widgits[widgit]['entry'].configure(placeholder_text=widgits[widgit]['text'], state='disabled') for widget in widgets_dp:
elif widgit in ['axis', 'vel', 'trq', 'trqh', 'estop']: if widget in ['path', 'dur']:
widgits[widgit]['label'].configure(text=f'{widgit.upper()}', text_color="black") widgets_dp[widget]['label'].configure(text=f'{widget.upper()}', text_color='black')
widgits[widgit]['optionmenu'].configure(state='disabled') widgets_dp[widget]['entry'].delete(0, tkinter.END)
widgets_dp[widget]['entry'].configure(placeholder_text=widgets_dp[widget]['text'], state='normal')
widgets_dp[widget]['entry'].configure(state='disabled')
elif widget in ['vel', 'trq', 'trqh', 'estop']:
widgets_dp[widget]['label'].configure(text=f'{widget.upper()}', text_color="black")
widgets_dp[widget]['optionmenu'].configure(state='normal')
widgets_dp[widget]['optionmenu'].set('1')
widgets_dp[widget]['optionmenu'].configure(state='disabled')
self.menu_sub_dp.grid_forget() self.menu_sub_dp.grid_forget()
self.textbox.delete(index1='1.0', index2='end') elif tab_name == 'Automatic Test':
self.textbox.configure(state='disabled') for widget in widgets_at:
if widget in ['path', ]:
widgets_at[widget]['label'].configure(text=f'{widget.upper()}', text_color='black')
widgets_at[widget]['entry'].delete(0, tkinter.END)
widgets_at[widget]['entry'].configure(placeholder_text=widgets_at[widget]['text'], state='normal')
widgets_at[widget]['entry'].configure(state='disabled')
elif widget in ['loadsel']:
widgets_at[widget]['optionmenu'].configure(state='normal')
widgets_at[widget]['optionmenu'].set(widgets_at[widget]['text'])
widgets_at[widget]['optionmenu'].configure(state='disabled')
self.seg_button.set("功能切换")
elif tab_name == 'Durable Action':
for widget in widgets_da:
if widget in ['path', ]:
widgets_da[widget]['label'].configure(text=f'{widget.upper()}', text_color='black')
widgets_da[widget]['entry'].delete(0, tkinter.END)
widgets_da[widget]['entry'].configure(placeholder_text=widgets_at[widget]['text'], state='normal')
elif widget in ['curvesel']:
widgets_da[widget]['optionmenu'].configure(state='normal')
widgets_da[widget]['optionmenu'].set(widgets_da[widget]['text'])
def func_main_callback(self, func_name): def func_main_callback(self, func_name):
self.initialization() self.initialization()
tab_name = self.tabview.get()
if tab_name == 'Data Process':
if func_name == 'brake': if func_name == 'brake':
for widgit in widgits: for widget in widgets_dp:
if widgit in ['path', 'av', 'rr']: if widget in ['path']:
widgits[widgit]['label'].configure(text_color='red') widgets_dp[widget]['label'].configure(text_color='red')
widgits[widgit]['entry'].configure(state='normal') widgets_dp[widget]['entry'].configure(state='normal')
elif widgit in ['axis', 'vel', 'trq', 'estop']: elif widget in ['vel', 'trq', 'estop']:
widgits[widgit]['label'].configure(text_color="red") widgets_dp[widget]['label'].configure(text_color="red")
widgits[widgit]['optionmenu'].configure(state='normal') widgets_dp[widget]['optionmenu'].configure(state='normal')
elif func_name == 'current': 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 = 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.grid(row=1, column=0, sticky='we', padx=5, pady=5)
self.menu_sub_dp.set("--select--") self.menu_sub_dp.set("--select--")
self.menu_sub_dp.configure(text_color='yellow') self.menu_sub_dp.configure(text_color='yellow')
for widgit in widgits: elif func_name == 'iso' or func_name == 'wavelogger':
if widgit in ['path', 'rc', 'rc1', 'rc2', 'rc3', 'rc4', 'rc5', 'rc6']: for widget in widgets_dp:
color = 'blue' if widgit == 'rc' else 'red' if widget in ['path']:
widgits[widgit]['label'].configure(text_color=color) widgets_dp[widget]['label'].configure(text_color='red')
widgits[widgit]['entry'].configure(state='normal') widgets_dp[widget]['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':
for widgit in widgits:
if widgit in ['path',]:
widgits[widgit]['label'].configure(text_color='red')
widgits[widgit]['entry'].configure(state='normal')
else: else:
self.initialization() self.initialization()
self.menu_main_dp.set("Start Here!") self.menu_main_dp.set("Start Here!")
elif tab_name == 'Automatic Test':
if func_name == 'brake':
for widget in widgets_at:
if widget in ['path',]:
widgets_at[widget]['label'].configure(text_color='red')
widgets_at[widget]['entry'].delete(0, tkinter.END)
widgets_at[widget]['entry'].configure(placeholder_text=widgets_at[widget]['text'], state='normal')
widgets_at[widget]['entry'].configure(state='normal')
elif widget in ['loadsel', ]:
widgets_at[widget]['optionmenu'].set(widgets_at[widget]['text'])
widgets_at[widget]['optionmenu'].configure(state='normal', text_color='red')
elif func_name == 'current':
for widget in widgets_at:
if widget in ['path',]:
widgets_at[widget]['label'].configure(text_color='red')
widgets_at[widget]['entry'].delete(0, tkinter.END)
widgets_at[widget]['entry'].configure(placeholder_text=widgets_at[widget]['text'], state='normal')
widgets_at[widget]['entry'].configure(state='normal')
elif widget in ['loadsel', ]:
widgets_at[widget]['optionmenu'].set(widgets_at[widget]['text'])
widgets_at[widget]['optionmenu'].configure(state='normal', text_color='red')
else:
self.initialization()
self.menu_main_at.set("Start Here!")
def func_sub_callback(self, func_name): def func_sub_callback(self, func_name):
if func_name == "max": if func_name == "max" or func_name == "avg":
for widgit in widgits: for widget in widgets_dp:
if widgit in ['rpm', 'dur']: if widget in ['path']:
widgits[widgit]['label'].configure(text_color='black') widgets_dp[widget]['label'].configure(text_color='red')
widgits[widgit]['entry'].configure(state='disabled') widgets_dp[widget]['entry'].delete(0, tkinter.END)
elif widgit in ['vel', 'trq']: widgets_dp[widget]['entry'].configure(placeholder_text=widgets_dp[widget]['text'], state='normal')
widgits[widgit]['label'].configure(text_color='black') widgets_dp[widget]['entry'].configure(state='normal')
widgits[widgit]['optionmenu'].configure(state='disabled') elif widget in ['dur']:
elif func_name == 'avg': widgets_dp[widget]['label'].configure(text_color='black')
for widgit in widgits: widgets_dp[widget]['entry'].delete(0, tkinter.END)
if widgit in ['rpm', 'dur']: widgets_dp[widget]['entry'].configure(placeholder_text=widgets_dp[widget]['text'], state='normal')
widgits[widgit]['label'].configure(text_color='black') widgets_dp[widget]['entry'].configure(state='disabled')
widgits[widgit]['entry'].configure(state='disabled') elif widget in ['vel', 'trqh', 'estop']:
elif widgit in ['vel', 'trq']: widgets_dp[widget]['label'].configure(text_color='black')
widgits[widgit]['label'].configure(text_color='black') widgets_dp[widget]['optionmenu'].set('1')
widgits[widgit]['optionmenu'].configure(state='disabled') widgets_dp[widget]['optionmenu'].configure(state='disabled')
elif widget in ['trq']:
widgets_dp[widget]['label'].configure(text_color='red')
widgets_dp[widget]['optionmenu'].set('1')
widgets_dp[widget]['optionmenu'].configure(state='normal')
elif func_name == 'cycle': elif func_name == 'cycle':
for widgit in widgits: for widget in widgets_dp:
if widgit in ['rpm', 'dur']: if widget in ['path', 'dur']:
widgits[widgit]['label'].configure(text_color='blue') color = 'blue' if widget == 'dur' else 'red'
widgits[widgit]['entry'].configure(state='normal') widgets_dp[widget]['label'].configure(text_color=color)
elif widgit in ['vel', 'trq']: widgets_dp[widget]['entry'].delete(0, tkinter.END)
widgits[widgit]['label'].configure(text_color="red") widgets_dp[widget]['entry'].configure(placeholder_text=widgets_dp[widget]['text'], state='normal')
widgits[widgit]['optionmenu'].configure(state='normal') widgets_dp[widget]['entry'].configure(state='normal')
elif widget in ['vel', 'trq', 'trqh']:
color = 'blue' if widget == 'trqh' else 'red'
widgets_dp[widget]['label'].configure(text_color=color)
widgets_dp[widget]['optionmenu'].set('1')
widgets_dp[widget]['optionmenu'].configure(state='normal')
elif widget in ['estop']:
widgets_dp[widget]['label'].configure(text_color="black")
widgets_dp[widget]['optionmenu'].set('1')
widgets_dp[widget]['optionmenu'].configure(state='disabled')
def write2textbox(self, text, wait=0, exitcode=0, color='blue'): def write2textbox(self, text, wait=0, exitcode=0, color='blue', tab_name='Data Process'):
self.textbox.tag_add(color, 'insert', 'end') self.textbox.tag_add(color, 'insert', 'end')
self.textbox.tag_config(tagName=color, foreground=color) self.textbox.tag_config(tagName=color, foreground=color)
tab_name_cur = self.tabview.get()
logger.info(text)
if tab_name == tab_name_cur:
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')
raise Exception(f"Error code: {exitcode}")
else:
self.textbox.insert(index='end', text=text + '\n', tags=color)
self.textbox.update()
self.textbox.see('end')
elif tab_name == 'openapi' and tab_name_cur == 'Automatic Test' or tab_name_cur == 'Durable Action':
if wait != 0: if wait != 0:
self.textbox.insert(index='end', text=text, tags=color) self.textbox.insert(index='end', text=text, tags=color)
self.textbox.update() self.textbox.update()
@ -216,7 +485,6 @@ class App(customtkinter.CTk):
self.textbox.insert(index='end', text=text + '\n', tags=color) self.textbox.insert(index='end', text=text + '\n', tags=color)
self.textbox.update() self.textbox.update()
self.textbox.see('end') self.textbox.see('end')
self.textbox.configure(state='disabled')
raise Exception(f"Error code: {exitcode}") raise Exception(f"Error code: {exitcode}")
else: else:
self.textbox.insert(index='end', text=text + '\n', tags=color) self.textbox.insert(index='end', text=text + '\n', tags=color)
@ -237,92 +505,134 @@ class App(customtkinter.CTk):
return True return True
def check_param(self): def check_param(self):
tab_name = self.tabview.get()
if tab_name == 'Data Process':
func_name = self.menu_main_dp.get() func_name = self.menu_main_dp.get()
if func_name == 'brake': if func_name == 'brake':
path = widgits['path']['entry'].get().strip() path = widgets_dp['path']['entry'].get().strip()
av = widgits['av']['entry'].get().strip('- ') vel = widgets_dp['vel']['optionmenu'].get()
rr = widgits['rr']['entry'].get().strip('- ') trq = widgets_dp['trq']['optionmenu'].get()
axis = widgits['axis']['optionmenu'].get() estop = widgets_dp['estop']['optionmenu'].get()
vel = widgits['vel']['optionmenu'].get()
trq = widgits['trq']['optionmenu'].get()
estop = widgits['estop']['optionmenu'].get()
c1 = exists(path) c1 = exists(path)
c2 = self.is_float('required', av, rr) c2 = True if len({vel, trq, estop}) == 3 else False
c3 = True if len({vel, trq, estop}) == 3 else False if c1 and c2:
return 1, path, int(vel), int(trq), int(estop)
if c1 and c2 and c3:
return 1, path, float(av), float(rr), int(axis), int(vel), int(trq), int(estop)
else: else:
return 0, 0 return 0, 0
# ======================================================= # =======================================================
elif func_name == 'current': elif func_name == 'current':
path = widgits['path']['entry'].get().strip() path = widgets_dp['path']['entry'].get().strip()
rc = widgits['rc']['entry'].get().strip('- ') dur = widgets_dp['dur']['entry'].get().strip()
rpm = widgits['rpm']['entry'].get().strip() vel = widgets_dp['vel']['optionmenu'].get()
dur = widgits['dur']['entry'].get().strip() trq = widgets_dp['trq']['optionmenu'].get()
rc1 = widgits['rc1']['entry'].get().strip() trqh = widgets_dp['trqh']['optionmenu'].get()
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() sub = self.menu_sub_dp.get()
c1 = exists(path) c1 = exists(path)
c2 = sub in ['max', 'avg', 'cycle'] c2 = sub in ['max', 'avg', 'cycle']
c3 = self.is_float('optional', rc, rpm) c3 = c4 = True
c4 = self.is_float('required', rc1, rc2, rc3, rc4, rc5, rc6)
c5 = c6 = True
if sub == 'cycle': if sub == 'cycle':
c5 = True if len({vel, trq}) == 2 else False c3 = True if len({vel, trq}) == 2 else False
c6 = self.is_float('optional', dur) c4 = self.is_float('optional', dur)
elif sub == 'max' or sub == 'avg':
pass
if c1 and c2 and c3 and c4 and c5 and c6: if c1 and c2 and c3 and c4:
rcs = [] dur = 0 if dur == '' else dur
for x in [rc1, rc2, rc3, rc4, rc5, rc6]: return 2, path, sub, float(dur), int(vel), int(trq), int(trqh)
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: else:
return 0, 0 return 0, 0
# ======================================================= # =======================================================
elif func_name == 'iso': elif func_name == 'iso':
path = widgits['path']['entry'].get().strip() path = widgets_dp['path']['entry'].get().strip()
c1 = exists(path) if exists(path):
if c1:
return 3, path return 3, path
else: else:
return 0, 0 return 0, 0
# =======================================================
elif func_name == 'wavelogger':
path = widgets_dp['path']['entry'].get().strip()
if exists(path):
return 4, path
else:
return 0, 0
# =======================================================
else:
return 0, 0
elif tab_name == 'Automatic Test':
func_name = self.menu_main_at.get()
if func_name == 'brake':
path = widgets_at['path']['entry'].get().strip()
loadsel = widgets_at['loadsel']['optionmenu'].get()
c1 = exists(path)
c2 = loadsel in ['tool100', 'tool66', 'tool33']
if c1 and c2:
return 5, path, loadsel
else:
return 0, 0
elif func_name == 'current':
path = widgets_at['path']['entry'].get().strip()
loadsel = widgets_at['loadsel']['optionmenu'].get()
c1 = exists(path)
c2 = loadsel in ['tool100', 'inertia']
if c1 and c2:
return 6, path, loadsel
else:
return 0, 0
else:
return 0, 0
elif tab_name == 'Durable Action':
path = widgets_da['path']['entry'].get().strip()
curvesel = widgets_da['curvesel']['optionmenu'].get()
c1 = exists(path)
c2 = curvesel in ['device_servo_trq_feedback', '[max] device_servo_trq_feedback']
if c1 and c2:
return 7, path, curvesel
else: else:
return 0, 0 return 0, 0
def func_start_callback(self): def func_start_callback(self):
self.textbox.configure(state='normal')
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, 3: iso.main} func_dict = {
1: brake.main, 2: current.main, 3: iso.main, 4: wavelogger.main, 5: do_brake.main, 6: do_current.main,
7: factory_test.main
}
if flag == 1: 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) func_dict[flag](path=args[0], vel=args[1], trq=args[2], estop=args[3], w2t=self.write2textbox)
elif flag == 2: 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) func_dict[flag](path=args[0], sub=args[1], dur=args[2], vel=args[3], trq=args[4], trqh=args[5], w2t=self.write2textbox)
elif flag == 3: elif flag == 3:
func_dict[flag](path=args[0], w2t=self.write2textbox) func_dict[flag](path=args[0], w2t=self.write2textbox)
elif flag == 4:
func_dict[flag](path=args[0], w2t=self.write2textbox)
elif flag == 5:
self.pre_warning()
func_dict[flag](path=args[0], hr=self.hr, md=self.md, loadsel=args[1], w2t=self.write2textbox)
elif flag == 6:
self.pre_warning()
func_dict[flag](path=args[0], hr=self.hr, md=self.md, loadsel=args[1], w2t=self.write2textbox)
elif flag == 7:
self.pre_warning()
func_dict[flag](path=args[0], hr=self.hr, md=self.md, w2t=self.write2textbox)
else: else:
tkinter.messagebox.showerror(title="参数错误", message="请检查对应参数是否填写正确!", ) tkinter.messagebox.showerror(title="参数错误", message="请检查对应参数是否填写正确!", )
self.textbox.configure(state='disabled') def pre_warning(self):
if self.tabview.get() == 'Durable Action':
df = DataFrame(clibs.durable_data_current)
df.to_excel(clibs.durable_data_current_xlsx, index=False)
df = DataFrame(clibs.durable_data_current_max)
df.to_excel(clibs.durable_data_current_max_xlsx, index=False)
if tkinter.messagebox.askyesno(title="开始运行", message="确认机器已按照测试规范更新固件,并提按照测试机型前修改好工程?"):
pass
else:
self.write2textbox("请按照测试规范更新机器固件,并在实际运行前确认工程已经修改完毕,以防撞机!!!", 0, 123, 'red', 'Automatic Test')
def func_check_callback(self): def func_check_callback(self):
self.textbox.configure(state='normal')
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()
@ -331,8 +641,6 @@ class App(customtkinter.CTk):
else: else:
tkinter.messagebox.showerror(title="参数错误", message="需要检查对应参数是否填写正确!", ) tkinter.messagebox.showerror(title="参数错误", message="需要检查对应参数是否填写正确!", )
self.textbox.configure(state='disabled')
def func_log_callback(self): def func_log_callback(self):
content = self.textbox.get(index1='1.0', index2='end') content = self.textbox.get(index1='1.0', index2='end')
if len(content) > 1: if len(content) > 1:
@ -356,4 +664,7 @@ class App(customtkinter.CTk):
if __name__ == "__main__": if __name__ == "__main__":
aio = App() aio = App()
aio.net_detect = Thread(target=aio.detect_network)
aio.net_detect.daemon = True
aio.net_detect.start()
aio.mainloop() aio.mainloop()

View File

@ -0,0 +1,67 @@
from sys import argv
from commons import clibs
tab_name = clibs.tab_names['at']
logger = clibs.log_prod
def trigger_estop(md, w2t):
md.trigger_estop()
w2t("触发急停成功,可点击机器状态验证。", 0, 0, 'green', tab_name)
def reset_estop(md, w2t):
md.reset_estop()
w2t("恢复急停成功,可点击机器状态验证。", 0, 0, 'green', tab_name)
def get_state(hr, w2t):
# 获取机器状态
_response = clibs.execution('state.get_state', hr, w2t, tab_name)
stat_desc = {'engine': '上电状态', 'operate': '操作模式', 'rc_state': '控制器状态', 'robot_action': '机器人动作', 'safety_mode': '安全模式', 'servo_mode': '伺服工作模式', 'task_space': '工作任务空间'}
for component, state in _response['data'].items():
w2t(f"{stat_desc[component]}: {state}", tab_name=tab_name)
# 获取设备伺服信息
_response = clibs.execution('device.get_params', hr, w2t, tab_name)
dev_desc = {0: '伺服版本', 1: '伺服参数', 2: '安全板固件', 3: '控制器', 4: '通讯总线', 5: '解释器', 6: '运动控制', 8: '力控版本', 9: '末端固件', 10: '机型文件', 11: '环境包'}
dev_vers = {}
for device in _response['data']['devices']:
dev_vers[device['type']] = device['version']
for i in sorted(dev_desc.keys()):
w2t(f"{dev_desc[i]}: {dev_vers[i]}", tab_name=tab_name)
# 设置示教器模式
_response = clibs.execution('state.set_tp_mode', hr, w2t, tab_name, tp_mode='without')
def warning_info(hr, w2t):
for postfix in ['', '.2', '.3', '.4', '.5', '.6', '.7', '.8', '.9', '.10']:
log_name = clibs.log_data_hmi + postfix
try:
with open(log_name, 'r', encoding='utf-8') as f_log:
for line in f_log:
if 'alarm' in line:
w2t(line.strip(), tab_name=tab_name)
except FileNotFoundError:
pass
def main(hr, md, func, w2t):
if hr is None:
w2t("无法连接机器人检查是否已经使用Robot Assist软件连接机器重试中...", 0, 49, 'red', tab_name)
# func: get_state/
match func:
case 'trigger_estop':
trigger_estop(md, w2t)
case 'reset_estop':
reset_estop(md, w2t)
case 'get_state':
get_state(hr, w2t)
case 'warning_info':
warning_info(hr, w2t)
if __name__ == '__main__':
main(*argv[1:])

View File

@ -0,0 +1,297 @@
from time import sleep, time, strftime, localtime
from sys import argv
from os import mkdir
from paramiko import SSHClient, AutoAddPolicy
from json import loads
from openpyxl import load_workbook
from pandas import DataFrame, concat
from commons import clibs
tab_name = clibs.tab_names['at']
logger = clibs.log_prod
def check_files(path, loadsel, data_dirs, data_files, w2t):
if len(data_dirs) != 0 or len(data_files) != 5:
w2t('初始路径下不允许有文件夹,且初始路径下只能存在如下五个文件,确认后重新运行!', 0, 0, 'red', tab_name)
w2t(' 1. configs.xlsx\n 2. reach33/reach66/reach100_xxxx.xlsx\n 3. xxxx.zip', 0, 1, 'red', tab_name)
config_file = reach33 = reach66 = reach100 = prj_file = None
for data_file in data_files:
filename = data_file.split('\\')[-1]
if filename == 'configs.xlsx':
config_file = data_file
elif filename.startswith('reach33_') and filename.endswith('.xlsx'):
reach33 = data_file
elif filename.startswith('reach66_') and filename.endswith('.xlsx'):
reach66 = data_file
elif filename.startswith('reach100_') and filename.endswith('.xlsx'):
reach100 = data_file
elif filename.endswith('.zip'):
prj_file = data_file
else:
w2t('初始路径下不允许有文件夹,且初始路径下只能存在如下五个文件,确认后重新运行!', 0, 0, 'red', tab_name)
w2t(' 1. configs.xlsx\n 2. reach33/reach66/reach100_xxxx.xlsx\n 3. xxxx.zip', 0, 2, 'red', tab_name)
if config_file and reach33 and reach66 and reach100 and prj_file:
result_dirs = []
mkdir(f"{path}\\j1")
mkdir(f"{path}\\j2")
mkdir(f"{path}\\j3")
for _reach in ['reach33', 'reach66', 'reach100']:
for _load in [f'load{loadsel.removeprefix("tool")}']:
for _speed in ['speed33', 'speed66', 'speed100']:
dir_name = '_'.join([_reach, _load, _speed])
result_dirs.append(dir_name)
mkdir(f"{path}\\j1\\{dir_name}")
mkdir(f"{path}\\j2\\{dir_name}")
if _reach == 'reach100':
mkdir(f"{path}\\j3\\{dir_name}")
w2t("数据目录合规性检查结束,未发现问题......", 0, 0, 'blue', tab_name)
return config_file, reach33, reach66, reach100, prj_file, result_dirs
else:
w2t('初始路径下不允许有文件夹,且初始路径下只能存在如下五个文件,确认后重新运行!', 0, 0, 'red', tab_name)
w2t(' 1. configs.xlsx\n 2. reach33/reach66/reach100_xxxx.xlsx\n 3. xxxx.zip', 0, 1, 'red', tab_name)
def gen_result_file(path, curve_data, axis, _reach, _load, _speed, count):
_d2d_vel = {'hw_joint_vel_feedback': []}
_d2d_trq = {'device_servo_trq_feedback': []}
_d2d_stop = {'device_safety_estop': []}
for data in curve_data[-240:]: # 保留最后12s的数据
dict_results = data['data']
for item in dict_results:
try:
item['value'].reverse()
except KeyError:
continue
if item.get('channel', None) == axis-1 and item.get('name', None) == 'hw_joint_vel_feedback':
_d2d_vel['hw_joint_vel_feedback'].extend(item['value'])
elif item.get('channel', None) == axis-1 and item.get('name', None) == 'device_servo_trq_feedback':
_d2d_trq['device_servo_trq_feedback'].extend(item['value'])
elif item.get('channel', None) == 0 and item.get('name', None) == 'device_safety_estop':
_d2d_stop['device_safety_estop'].extend(item['value'])
df1 = DataFrame.from_dict(_d2d_vel)
df2 = DataFrame.from_dict(_d2d_trq)
df3 = DataFrame.from_dict(_d2d_stop)
df = concat([df1, df2, df3], axis=1)
_filename = f"{path}\\j{axis}\\reach{_reach}_load{_load}_speed{_speed}\\reach{_reach}_load{_load}_speed{_speed}_{count}.data"
df.to_csv(_filename, sep='\t', index=False)
def run_rl(path, loadsel, hr, md, config_file, result_dirs, w2t):
_count = 0
_total = 63
display_pdo_params = [
{"name": "hw_joint_vel_feedback", "channel": 0},
{"name": "hw_joint_vel_feedback", "channel": 1},
{"name": "hw_joint_vel_feedback", "channel": 2},
{"name": "hw_joint_vel_feedback", "channel": 3},
{"name": "hw_joint_vel_feedback", "channel": 4},
{"name": "hw_joint_vel_feedback", "channel": 5},
{"name": "device_servo_trq_feedback", "channel": 0},
{"name": "device_servo_trq_feedback", "channel": 1},
{"name": "device_servo_trq_feedback", "channel": 2},
{"name": "device_servo_trq_feedback", "channel": 3},
{"name": "device_servo_trq_feedback", "channel": 4},
{"name": "device_servo_trq_feedback", "channel": 5},
{"name": "device_safety_estop", "channel": 0},
]
wb = load_workbook(config_file, read_only=True)
ws = wb['Target']
write_diagnosis = float(ws.cell(row=3, column=10).value)
get_init_speed = float(ws.cell(row=4, column=10).value)
single_brake = str(ws.cell(row=5, column=10).value)
logger.info(f"write_diagnosis = {write_diagnosis}, get_init_speed = {get_init_speed}, single_brake = {single_brake}")
if ws.cell(row=1, column=1).value == 'positive':
md.write_pon(1)
elif ws.cell(row=1, column=1).value == 'negative':
md.write_pon(0)
else:
w2t("configs.xlsx中Target页面A1单元格填写不正确检查后重新运行...", 0, 111, 'red', tab_name)
clibs.execution('diagnosis.open', hr, w2t, tab_name, open=True, display_open=True)
clibs.execution('diagnosis.set_params', hr, w2t, tab_name, display_pdo_params=display_pdo_params)
for condition in result_dirs:
_reach = condition.split('_')[0].removeprefix('reach')
_load = condition.split('_')[1].removeprefix('load')
_speed = condition.split('_')[2].removeprefix('speed')
# for single condition test
_single_axis = 0
if single_brake != '0':
_total = 3
_single_axis = int(single_brake.split('-')[0])
if _reach != single_brake.split('-')[1] or _load != single_brake.split('-')[2] or _speed != single_brake.split('-')[3]:
continue
for axis in range(1, 4):
# for single condition test
if _single_axis != 0 and _single_axis != axis:
continue
md.write_axis(axis)
speed_max = 0
if axis == 3 and _reach != '100':
continue
w2t(f"-"*90, 0, 0, 'purple', tab_name)
for count in range(1, 4):
_count += 1
this_time = strftime("%Y-%m-%d %H:%M:%S", localtime(time()))
prj_path = 'target/_build/target.prj'
w2t(f"[{this_time} | {_count}/{_total}] 正在执行 {axis}{condition} 的第 {count} 次制动测试...", 0, 0, 'purple', tab_name)
# 1. 关闭诊断曲线,触发软急停,并解除,目的是让可能正在运行着的机器停下来,切手动模式并下电
md.trigger_estop()
md.reset_estop()
md.clear_alarm()
md.write_act(0)
sleep(write_diagnosis) # 软急停超差后等待写诊断时间可通过configs.xlsx配置
while count == 1:
# 2. 修改未要执行的场景
ssh = SSHClient()
ssh.set_missing_host_key_policy(AutoAddPolicy())
ssh.connect('192.168.0.160', 22, username='luoshi', password='luoshi2019')
if ws.cell(row=1, column=1).value == 'positive':
_rl_cmd = f"brake_E(j{axis}_{_reach}_p, j{axis}_{_reach}_n, p_speed, p_tool)"
elif ws.cell(row=1, column=1).value == 'negative':
_rl_cmd = f"brake_E(j{axis}_{_reach}_n, j{axis}_{_reach}_p, p_speed, p_tool)"
else:
w2t("configs.xlsx中Target页面A1单元格填写不正确检查后重新运行...", 0, 111, 'red', tab_name)
_rl_speed = f"VelSet {_speed}"
_rl_tool = f"tool p_tool = tool{loadsel.removeprefix('tool')}"
cmd = 'cd /home/luoshi/bin/controller/; '
cmd += 'sudo sed -i "/brake_E/d" projects/target/_build/brake/main.mod; '
cmd += f'sudo sed -i "/DONOTDELETE/i {_rl_cmd}" projects/target/_build/brake/main.mod; '
cmd += 'sudo sed -i "/VelSet/d" projects/target/_build/brake/main.mod; '
cmd += f'sudo sed -i "/MoveAbsJ/i {_rl_speed}" projects/target/_build/brake/main.mod; '
cmd += 'sudo sed -i "/tool p_tool/d" projects/target/_build/brake/main.mod; '
cmd += f'sudo sed -i "/VelSet/i {_rl_tool}" projects/target/_build/brake/main.mod; '
stdin, stdout, stderr = ssh.exec_command(cmd, get_pty=True)
stdin.write('luoshi2019' + '\n')
stdout.read().decode() # 需要read一下才能正常执行
stderr.read().decode()
# 3. reload工程后pp2main并且自动模式和上电最后运行程序
clibs.execution('overview.reload', hr, w2t, tab_name, prj_path=prj_path, tasks=['brake', 'stop0_related'])
clibs.execution('rl_task.pp_to_main', hr, w2t, tab_name, tasks=['brake', 'stop0_related'])
clibs.execution('state.switch_auto', hr, w2t, tab_name)
clibs.execution('state.switch_motor_on', hr, w2t, tab_name)
clibs.execution('rl_task.run', hr, w2t, tab_name, tasks=['brake', 'stop0_related'])
_t_start = time()
while True:
if md.read_ready_to_go() == 1:
md.write_act(True)
break
else:
if (time() - _t_start) // 20 > 1:
w2t("20s内未收到机器人的运行信号需要确认RL程序编写正确并正常执行...", 0, 111, 'red', tab_name)
else:
sleep(1)
# 4. 找出最大速度传递给RL程序最后清除相关记录
sleep(get_init_speed+5) # 冗余5s指定时间后获取实际【正|负】方向的最大速度可通过configs.xlsx配置
clibs.execution('rl_task.stop', hr, w2t, tab_name, tasks=['brake'])
# 找出最大速度
_c_msg = hr.c_msg.copy()
_number = 0
for _msg in _c_msg:
if _number > get_init_speed*20: # 最开始回零点的时候,二轴速度可以达到最大值,实际摆动时,某一方向可能达不到
break
if 'diagnosis.result' in _msg:
_number += 1
dict_results = loads(_msg)['data']
for item in dict_results:
if item.get('channel', None) == axis-1 and item.get('name', None) == 'hw_joint_vel_feedback':
_ = clibs.RADIAN * sum(item['value']) / len(item['value'])
if ws.cell(row=1, column=1).value == 'positive':
speed_max = max(_, speed_max)
elif ws.cell(row=1, column=1).value == 'negative':
speed_max = min(_, speed_max)
logger.info(f"speed max = {speed_max}")
speed_max = abs(speed_max)
speed_target = float(ws.cell(row=3, column=axis+1).value) * float(_speed) / 100
if speed_max < speed_target*0.95 or speed_max > speed_target*1.05:
w2t(f"Axis: {axis}-{count} | Speed: {speed_max} | Shouldbe: {speed_target}", 0, 0, 'indigo', tab_name)
md.write_speed_max(speed_max)
hr.c_msg_xs.clear()
if len(hr.c_msg) > 270:
del hr.c_msg[270:]
if speed_max < 10:
md.clear_alarm()
w2t("未获取到正确的速度,即将重新获取...", 0, 0, 'red', tab_name)
continue
else:
break
# 5. 清除软急停重新运行程序并打开曲线发送继续运动信号当速度达到最大值时通过DO触发急停
md.reset_estop() # 其实没必要
md.clear_alarm()
clibs.execution('overview.reload', hr, w2t, tab_name, prj_path=prj_path, tasks=['brake', 'stop0_related'])
clibs.execution('rl_task.pp_to_main', hr, w2t, tab_name, tasks=['brake', 'stop0_related'])
clibs.execution('state.switch_auto', hr, w2t, tab_name)
clibs.execution('state.switch_motor_on', hr, w2t, tab_name)
clibs.execution('rl_task.run', hr, w2t, tab_name, tasks=['brake', 'stop0_related'])
for i in range(3):
if md.read_ready_to_go() == 1:
md.write_act(1)
break
else:
sleep(1)
else:
w2t("未收到机器人的运行信号需要确认RL程序编写正确并正常执行...", 0, 111, 'red', tab_name)
sleep(10) # 排除从其他位姿到零点位姿,再到轴极限位姿的时间
md.write_probe(1)
_t_start = time()
while True:
if md.read_brake_done() == 1:
sleep(1) # 保证速度归零
md.write_probe(0)
break
else:
if (time() - _t_start) > 30:
w2t(f"30s内未触发急停该条数据无效需要确认RL/Python程序编写正确并正常执行或者判别是否是机器本体问题比如正负方向速度是否一致...", 0, 0, 'red', tab_name)
md.write_probe(0)
break
else:
sleep(1)
# 6. 保留数据并处理输出
curve_data = []
_c_msg = hr.c_msg.copy()
for _msg in _c_msg:
if 'diagnosis.result' in _msg:
curve_data.insert(0, loads(_msg))
else:
hr.c_msg_xs.clear()
if len(hr.c_msg) > 270:
del hr.c_msg[270:]
gen_result_file(path, curve_data, axis, _reach, _load, _speed, count)
else:
w2t(f"\n{loadsel.removeprefix('tool')}%负载的制动性能测试执行完毕,如需采集其他负载,须切换负载类型,并更换其他负载,重新执行。", 0, 0, 'green', tab_name)
def main(path, hr, md, loadsel, w2t):
_s_time = time()
data_dirs, data_files = clibs.traversal_files(path, w2t)
config_file, reach33, reach66, reach100, prj_file, result_dirs = check_files(path, loadsel, data_dirs, data_files, w2t)
clibs.prj_to_xcore(prj_file)
run_rl(path, loadsel, hr, md, config_file, result_dirs, w2t)
_e_time = time()
time_total = _e_time - _s_time
w2t(f"处理总时长:{time_total // 3600:02.0f} h {time_total % 3600 // 60:02.0f} m {time_total % 60:02.0f} s", 0, 0, 'green', tab_name)
if __name__ == '__main__':
main(*argv[1:])

View File

@ -0,0 +1,354 @@
from os import mkdir
from time import sleep, time
from sys import argv
from paramiko import SSHClient, AutoAddPolicy
from json import loads
from pandas import DataFrame, concat
from commons import clibs
logger = clibs.log_prod
tab_name = clibs.tab_names['at']
display_pdo_params = [
{"name": "hw_joint_vel_feedback", "channel": 0},
{"name": "hw_joint_vel_feedback", "channel": 1},
{"name": "hw_joint_vel_feedback", "channel": 2},
{"name": "hw_joint_vel_feedback", "channel": 3},
{"name": "hw_joint_vel_feedback", "channel": 4},
{"name": "hw_joint_vel_feedback", "channel": 5},
{"name": "device_servo_trq_feedback", "channel": 0},
{"name": "device_servo_trq_feedback", "channel": 1},
{"name": "device_servo_trq_feedback", "channel": 2},
{"name": "device_servo_trq_feedback", "channel": 3},
{"name": "device_servo_trq_feedback", "channel": 4},
{"name": "device_servo_trq_feedback", "channel": 5},
]
def check_files(path, loadsel, data_dirs, data_files, w2t):
if len(data_dirs) != 0 or len(data_files) != 3:
w2t('初始路径下不允许有文件夹,且初始路径下只能存在如下三个文件,确认后重新运行!', 0, 0, 'red', tab_name)
w2t(' 1. configs.xlsx\n 2. T_电机电流.xlsx\n 3. xxxx.zip', 0, 1, 'red', tab_name)
config_file = current_file = prj_file = None
for data_file in data_files:
filename = data_file.split('\\')[-1]
if filename == 'configs.xlsx':
config_file = data_file
elif filename == 'T_电机电流.xlsx':
current_file = data_file
elif filename.endswith('.zip'):
prj_file = data_file
else:
w2t('初始路径下不允许有文件夹,且初始路径下只能存在如下三个文件,确认后重新运行!', 0, 0, 'red', tab_name)
w2t(' 1. configs.xlsx\n 2. T_电机电流.xlsx\n 3. xxxx.zip', 0, 1, 'red', tab_name)
if config_file and current_file and prj_file:
w2t("数据目录合规性检查结束,未发现问题......", tab_name=tab_name)
if loadsel == 'tool100':
mkdir(f"{path}\\single")
mkdir(f"{path}\\s_1")
mkdir(f"{path}\\s_2")
mkdir(f"{path}\\s_3")
elif loadsel == 'inertia':
mkdir(f"{path}\\inertia")
return config_file, current_file, prj_file
else:
w2t('初始路径下不允许有文件夹,且初始路径下只能存在如下三个文件,确认后重新运行!', 0, 0, 'red', tab_name)
w2t(' 1. configs.xlsx\n 2. T_电机电流.xlsx\n 3. xxxx.zip', 0, 1, 'red', tab_name)
def data_proc_regular(path, filename, channel, scenario_time):
if channel in list(range(6)):
with open(filename, 'r', encoding='utf-8') as f_obj:
lines = f_obj.readlines()
_d2d_vel = {'hw_joint_vel_feedback': []}
_d2d_trq = {'device_servo_trq_feedback': []}
for line in lines[-500:]: # 保留最后25s的数据
data = eval(line.strip())['data']
for item in data:
try:
item['value'].reverse()
except KeyError:
continue
if item.get('channel', None) == channel and item.get('name', None) == 'hw_joint_vel_feedback':
_d2d_vel['hw_joint_vel_feedback'].extend(item['value'])
elif item.get('channel', None) == channel and item.get('name', None) == 'device_servo_trq_feedback':
_d2d_trq['device_servo_trq_feedback'].extend(item['value'])
df1 = DataFrame.from_dict(_d2d_vel)
df2 = DataFrame.from_dict(_d2d_trq)
df = concat([df1, df2], axis=1)
_filename = f'{path}\\single\\j{channel+1}_single_{time()}.data'
df.to_csv(_filename, sep='\t', index=False)
elif channel in list(range(6, 9)):
with open(filename, 'r', encoding='utf-8') as f_obj:
lines = f_obj.readlines()
_d2d_vel_0 = {'hw_joint_vel_feedback': []}
_d2d_trq_0 = {'device_servo_trq_feedback': []}
_d2d_vel_1 = {'hw_joint_vel_feedback': []}
_d2d_trq_1 = {'device_servo_trq_feedback': []}
_d2d_vel_2 = {'hw_joint_vel_feedback': []}
_d2d_trq_2 = {'device_servo_trq_feedback': []}
_d2d_vel_3 = {'hw_joint_vel_feedback': []}
_d2d_trq_3 = {'device_servo_trq_feedback': []}
_d2d_vel_4 = {'hw_joint_vel_feedback': []}
_d2d_trq_4 = {'device_servo_trq_feedback': []}
_d2d_vel_5 = {'hw_joint_vel_feedback': []}
_d2d_trq_5 = {'device_servo_trq_feedback': []}
for line in lines:
data = eval(line.strip())['data']
for item in data:
try:
item['value'].reverse()
except KeyError:
continue
if item.get('channel', None) == 0 and item.get('name', None) == 'hw_joint_vel_feedback':
_d2d_vel_0['hw_joint_vel_feedback'].extend(item['value'])
elif item.get('channel', None) == 0 and item.get('name', None) == 'device_servo_trq_feedback':
_d2d_trq_0['device_servo_trq_feedback'].extend(item['value'])
elif item.get('channel', None) == 1 and item.get('name', None) == 'hw_joint_vel_feedback':
_d2d_vel_1['hw_joint_vel_feedback'].extend(item['value'])
elif item.get('channel', None) == 1 and item.get('name', None) == 'device_servo_trq_feedback':
_d2d_trq_1['device_servo_trq_feedback'].extend(item['value'])
elif item.get('channel', None) == 2 and item.get('name', None) == 'hw_joint_vel_feedback':
_d2d_vel_2['hw_joint_vel_feedback'].extend(item['value'])
elif item.get('channel', None) == 2 and item.get('name', None) == 'device_servo_trq_feedback':
_d2d_trq_2['device_servo_trq_feedback'].extend(item['value'])
elif item.get('channel', None) == 3 and item.get('name', None) == 'hw_joint_vel_feedback':
_d2d_vel_3['hw_joint_vel_feedback'].extend(item['value'])
elif item.get('channel', None) == 3 and item.get('name', None) == 'device_servo_trq_feedback':
_d2d_trq_3['device_servo_trq_feedback'].extend(item['value'])
elif item.get('channel', None) == 4 and item.get('name', None) == 'hw_joint_vel_feedback':
_d2d_vel_4['hw_joint_vel_feedback'].extend(item['value'])
elif item.get('channel', None) == 4 and item.get('name', None) == 'device_servo_trq_feedback':
_d2d_trq_4['device_servo_trq_feedback'].extend(item['value'])
elif item.get('channel', None) == 5 and item.get('name', None) == 'hw_joint_vel_feedback':
_d2d_vel_5['hw_joint_vel_feedback'].extend(item['value'])
elif item.get('channel', None) == 5 and item.get('name', None) == 'device_servo_trq_feedback':
_d2d_trq_5['device_servo_trq_feedback'].extend(item['value'])
df_01 = DataFrame.from_dict(_d2d_vel_0)
df_02 = DataFrame.from_dict(_d2d_trq_0)
df = concat([df_01, df_02], axis=1)
_filename = f'{path}\\s_{channel-5}\\j1_s_{channel-5}_{scenario_time}_{time()}.data'
df.to_csv(_filename, sep='\t', index=False)
df_01 = DataFrame.from_dict(_d2d_vel_1)
df_02 = DataFrame.from_dict(_d2d_trq_1)
df = concat([df_01, df_02], axis=1)
_filename = f'{path}\\s_{channel-5}\\j2_s_{channel-5}_{scenario_time}_{time()}.data'
df.to_csv(_filename, sep='\t', index=False)
df_01 = DataFrame.from_dict(_d2d_vel_2)
df_02 = DataFrame.from_dict(_d2d_trq_2)
df = concat([df_01, df_02], axis=1)
_filename = f'{path}\\s_{channel-5}\\j3_s_{channel-5}_{scenario_time}_{time()}.data'
df.to_csv(_filename, sep='\t', index=False)
df_01 = DataFrame.from_dict(_d2d_vel_3)
df_02 = DataFrame.from_dict(_d2d_trq_3)
df = concat([df_01, df_02], axis=1)
_filename = f'{path}\\s_{channel-5}\\j4_s_{channel-5}_{scenario_time}_{time()}.data'
df.to_csv(_filename, sep='\t', index=False)
df_01 = DataFrame.from_dict(_d2d_vel_4)
df_02 = DataFrame.from_dict(_d2d_trq_4)
df = concat([df_01, df_02], axis=1)
_filename = f'{path}\\s_{channel-5}\\j5_s_{channel-5}_{scenario_time}_{time()}.data'
df.to_csv(_filename, sep='\t', index=False)
df_01 = DataFrame.from_dict(_d2d_vel_5)
df_02 = DataFrame.from_dict(_d2d_trq_5)
df = concat([df_01, df_02], axis=1)
_filename = f'{path}\\s_{channel-5}\\j6_s_{channel-5}_{scenario_time}_{time()}.data'
df.to_csv(_filename, sep='\t', index=False)
elif channel in list(range(9, 15)):
with open(filename, 'r', encoding='utf-8') as f_obj:
lines = f_obj.readlines()
_d2d_vel = {'hw_joint_vel_feedback': []}
_d2d_trq = {'device_servo_trq_feedback': []}
for line in lines[-300:]: # 保留最后15s的数据
data = eval(line.strip())['data']
for item in data:
try:
item['value'].reverse()
except KeyError:
continue
if item.get('channel', None) == channel-9 and item.get('name', None) == 'hw_joint_vel_feedback':
_d2d_vel['hw_joint_vel_feedback'].extend(item['value'])
elif item.get('channel', None) == channel-9 and item.get('name', None) == 'device_servo_trq_feedback':
_d2d_trq['device_servo_trq_feedback'].extend(item['value'])
df1 = DataFrame.from_dict(_d2d_vel)
df2 = DataFrame.from_dict(_d2d_trq)
df = concat([df1, df2], axis=1)
_filename = f'{path}\\single\\j{channel-8}_hold_{time()}.data'
df.to_csv(_filename, sep='\t', index=False)
def data_proc_inertia(path, filename, channel):
with open(filename, 'r', encoding='utf-8') as f_obj:
lines = f_obj.readlines()
_d2d_vel = {'hw_joint_vel_feedback': []}
_d2d_trq = {'device_servo_trq_feedback': []}
for line in lines:
data = eval(line.strip())['data']
for item in data:
try:
item['value'].reverse()
except KeyError:
continue
if item.get('channel', None) == channel+3 and item.get('name', None) == 'hw_joint_vel_feedback':
_d2d_vel['hw_joint_vel_feedback'].extend(item['value'])
elif item.get('channel', None) == channel+3 and item.get('name', None) == 'device_servo_trq_feedback':
_d2d_trq['device_servo_trq_feedback'].extend(item['value'])
df1 = DataFrame.from_dict(_d2d_vel)
df2 = DataFrame.from_dict(_d2d_trq)
df = concat([df1, df2], axis=1)
_filename = f'{path}\\inertia\\j{channel+4}_inertia_{time()}.data'
df.to_csv(_filename, sep='\t', index=False)
def gen_result_file(path, loadsel, disc, number, scenario_time):
filename = path + f'\\data.txt'
with open(filename, 'w', encoding='utf-8') as f_obj:
for line in disc[number][1]:
f_obj.write(str(line)+'\n')
if loadsel == 'tool100':
data_proc_regular(path, filename, number, scenario_time)
elif loadsel == 'inertia':
data_proc_inertia(path, filename, number)
def run_rl(path, hr, md, loadsel, w2t):
c_regular = [
"scenario(0, j1_p, j1_n, p_speed, p_tool, i_tool)",
"scenario(0, j2_p, j2_n, p_speed, p_tool, i_tool)",
"scenario(0, j3_p, j3_n, p_speed, p_tool, i_tool)",
"scenario(0, j4_p, j4_n, p_speed, p_tool, i_tool)",
"scenario(0, j5_p, j5_n, p_speed, p_tool, i_tool)",
"scenario(0, j6_p, j6_n, p_speed, p_tool, i_tool)",
"scenario(1, j6_p, j6_n, p_speed, p_tool, i_tool)",
"scenario(2, j6_p, j6_n, p_speed, p_tool, i_tool)",
"scenario(3, j6_p, j6_n, p_speed, p_tool, i_tool)",
"scenario(4, j1_hold, j1_hold, p_speed, p_tool, i_tool)",
"scenario(4, j2_hold, j2_hold, p_speed, p_tool, i_tool)",
"scenario(4, j3_hold, j3_hold, p_speed, p_tool, i_tool)",
"scenario(4, j4_hold, j4_hold, p_speed, p_tool, i_tool)",
"scenario(4, j5_hold, j5_hold, p_speed, p_tool, i_tool)",
"scenario(4, j6_hold, j6_hold, p_speed, p_tool, i_tool)",
]
c_inertia = [
"scenario(5, j4_p_inertia, j4_n_inertia, p_speed, p_tool, i_tool)",
"scenario(5, j5_p_inertia, j5_n_inertia, p_speed, p_tool, i_tool)",
"scenario(5, j6_p_inertia, j6_n_inertia, p_speed, p_tool, i_tool)",
]
disc_regular = {
0: ['一轴', []], 1: ['二轴', []], 2: ['三轴', []], 3: ['四轴', []], 4: ['五轴', []], 5: ['六轴', []],
6: ['场景一', []], 7: ['场景二', []], 8: ['场景三', []], 9: ['一轴保持', []], 10: ['二轴保持', []],
11: ['三轴保持', []], 12: ['四轴保持', []], 13: ['五轴保持', []], 14: ['六轴保持', []]
}
disc_inertia = {0: ['四轴惯量', []], 1: ['五轴惯量', []], 2: ['六轴惯量', []]}
if loadsel == 'tool100':
conditions = c_regular
disc = disc_regular
elif loadsel == 'inertia':
conditions = c_inertia
disc = disc_inertia
# preparation 触发软急停,并解除,目的是让可能正在运行着的机器停下来
clibs.execution('diagnosis.open', hr, w2t, tab_name, open=True, display_open=True)
clibs.execution('diagnosis.set_params', hr, w2t, tab_name, display_pdo_params=display_pdo_params)
# clibs.execution('diagnosis.save', hr, w2t, tab_name, save=True) # 这条命令有问题
md.trigger_estop()
md.reset_estop()
for condition in conditions:
number = conditions.index(condition)
w2t(f"正在执行{disc[number][0]}测试......", 0, 0, 'purple', tab_name)
# 1. 将act重置为False并修改未要执行的场景
md.write_act(False)
ssh = SSHClient()
ssh.set_missing_host_key_policy(AutoAddPolicy())
ssh.connect(clibs.ip_addr, 22, username='luoshi', password='luoshi2019')
cmd = 'cd /home/luoshi/bin/controller/; '
cmd += 'sudo sed -i "/scenario/d" projects/target/_build/current/main.mod; '
cmd += f'sudo sed -i "/DONOTDELETE/i {condition}" projects/target/_build/current/main.mod'
stdin, stdout, stderr = ssh.exec_command(cmd, get_pty=True)
stdin.write('luoshi2019' + '\n')
stdout.read().decode() # 需要read一下才能正常执行
stderr.read().decode()
# 2. reload工程后pp2main并且自动模式和上电
prj_path = 'target/_build/target.prj'
clibs.execution('overview.reload', hr, w2t, tab_name, prj_path=prj_path, tasks=['current'])
clibs.execution('rl_task.pp_to_main', hr, w2t, tab_name, tasks=['current'])
clibs.execution('state.switch_auto', hr, w2t, tab_name)
clibs.execution('state.switch_motor_on', hr, w2t, tab_name)
# 3. 开始运行程序单轴运行35s
clibs.execution('rl_task.run', hr, w2t, tab_name, tasks=['current'])
_t_start = time()
while True:
if md.read_ready_to_go() == 1:
md.write_act(True)
break
else:
if (time() - _t_start) // 20 > 1:
w2t("20s内未收到机器人的运行信号需要确认RL程序编写正确并正常执行...", 0, 111, 'red', tab_name)
else:
sleep(1)
# 4. 打开诊断曲线,并执行采集
sleep(10) # 保证程序已经运行起来,其实主要是为了保持电流的采集而设定
scenario_time = 0
if number < 6:
sleep(35)
elif number > 8:
sleep(15)
else:
_t_start = time()
while True:
scenario_time = md.read_scenario_time()
if float(scenario_time) > 1:
w2t(f"场景{number-5}的周期时间:{scenario_time}", 0, 0, 'green', tab_name)
break
else:
if (time()-_t_start)//60 > 3:
w2t(f"未收到场景{number-5}的周期时间需要确认RL程序编写正确并正常执行...", 0, 111, 'red', tab_name)
else:
sleep(5)
sleep(1) # 一定要延迟一秒再读一次scenario time寄存器因为一开始读取的数值不准确
scenario_time = md.read_scenario_time()
sleep(float(scenario_time)*0.2) # 再运行周期的20%即可
# 5.停止程序运行,保留数据并处理输出
clibs.execution('rl_task.stop', hr, w2t, tab_name, tasks=['current'])
_c_msg = hr.c_msg.copy()
for _msg in _c_msg:
if 'diagnosis.result' in _msg:
disc[number][1].insert(0, loads(_msg))
else:
hr.c_msg_xs.clear()
if len(hr.c_msg) > 270:
del hr.c_msg[270:]
gen_result_file(path, loadsel, disc, number, scenario_time)
else:
if loadsel == 'tool100':
w2t("单轴和场景电机电流采集完毕,如需采集惯量负载,须切换负载类型,并更换惯量负载,重新执行。", 0, 0, 'green', tab_name)
elif loadsel == 'inertia':
w2t("惯量负载电机电流采集完毕,如需采集单轴/场景/保持电机电流,须切换负载类型,并更换偏置负载,重新执行。", 0, 0, 'green', tab_name)
def main(path, hr, md, loadsel, w2t):
data_dirs, data_files = clibs.traversal_files(path, w2t)
config_file, current_file, prj_file = check_files(path, loadsel, data_dirs, data_files, w2t)
clibs.prj_to_xcore(prj_file)
run_rl(path, hr, md, loadsel, w2t)
if __name__ == '__main__':
main(*argv[1:])

191
aio/code/commons/clibs.py Normal file
View File

@ -0,0 +1,191 @@
from os import scandir
from threading import Thread
from time import sleep
from os.path import exists
from paramiko import SSHClient, AutoAddPolicy
from socket import setdefaulttimeout
from logging import getLogger
from logging.config import dictConfig
import concurrent_log_handler
ip_addr = '192.168.0.160' # for product
# ip_addr = '192.168.84.129' # for test
RADIAN = 57.3 # 180 / 3.1415926
MAX_FRAME_SIZE = 1024
TIMEOUT = 5
setdefaulttimeout(TIMEOUT)
tab_names = {'dp': 'Data Process', 'at': 'Automatic Test', 'da': 'Duration Action', 'op': 'openapi'}
# PREFIX = '' # for pyinstaller packaging
PREFIX = '../assets/' # for source code debug
log_data_hmi = f'{PREFIX}templates/c_msg.log'
log_data_debug = f'{PREFIX}templates/debug.log'
heartbeat = f'{PREFIX}templates/heartbeat'
durable_data_current_xlsx = f'{PREFIX}templates/durable/durable_data_current.xlsx'
durable_data_current_max_xlsx = f'{PREFIX}templates/durable/durable_data_current_max.xlsx'
durable_data_current = {
'time': list(range(1, 19)),
'axis1': [0 for _ in range(18)],
'axis2': [0 for _ in range(18)],
'axis3': [0 for _ in range(18)],
'axis4': [0 for _ in range(18)],
'axis5': [0 for _ in range(18)],
'axis6': [0 for _ in range(18)],
}
durable_data_current_max = {
'time': list(range(1, 19)),
'axis1': [0 for _ in range(18)],
'axis2': [0 for _ in range(18)],
'axis3': [0 for _ in range(18)],
'axis4': [0 for _ in range(18)],
'axis5': [0 for _ in range(18)],
'axis6': [0 for _ in range(18)],
}
# version表示版本该键值为从1开始的整数。该key必选除此之外其它key都是可选。
# formatters日志格式化器其value值为一个字典该字典的每个键值对都代表一个Formatter键值对中key代表Formatter ID(自定义ID)value为字典描述如何配置相应的Formatter实例。默认格式为 %(message)s
# filters日志过滤器其value值为一个字典该字典的每个键值对都代表一个Filter键值对中key代表Filter ID(自定义ID)value为字典描述如何配置相应的Filter实例。
# handlers日志处理器其value值为一个字典该字典的每个键值对都代表一个Handler键值对中key代表Handler ID(自定义ID)value为字典描述如何配置相应的Handler实例包含以下配置key
# class (必选):日志处理器类全称
# level (可选)指定该日志处理器需要处理哪些级别的日志低于该级别的日志将不被该handler处理。level可以为代表日志级别的整数或者表大写字符串字符串日志级别和数字日志级别对应关系如下
# CRITICAL 50
# ERROR 40
# WARNING 30
# INFO 20
# DEBUG 10
# NOTSET 0
f_complex = '%(asctime)s # %(name)s-%(levelname)s-%(module)s-%(funcName)s-%(lineno)d # %(message)s'
f_simple = '%(levelname)s-%(module)s-%(funcName)s-%(lineno)d: %(message)s'
log_dicts = {
'version': 1,
'disable_existing_loggers': True,
'formatters': {
'standard': {
'format': f_complex,
'style': '%',
'datefmt': '%Y-%m-%dT%H:%M:%S',
},
'test': {
'format': f_simple,
'style': '%',
'datefmt': '%Y-%m-%dT%H:%M:%S',
},
},
'filters': {},
'handlers': {
'console': {
'level': 'DEBUG',
'class': 'logging.StreamHandler',
'formatter': 'test',
},
'hmi.log': {
'level': 'WARNING',
'class': 'concurrent_log_handler.ConcurrentRotatingFileHandler',
'filename': log_data_hmi,
'maxBytes': 1024*1024*50,
'backupCount': 10,
'encoding': 'utf-8',
'formatter': 'standard',
},
'debug.log': {
'level': 'INFO',
'class': 'logging.FileHandler',
'filename': log_data_debug,
'encoding': 'utf-8',
'formatter': 'standard',
},
},
'loggers': {
'normal': {
'handlers': ['hmi.log', 'debug.log'],
'level': 'DEBUG',
'propagate': False
},
'debug': {
'handlers': ['console'],
'level': 'DEBUG',
'propagate': False
},
'': {
'handlers': ['hmi.log', 'debug.log'],
'level': 'DEBUG',
'propagate': False
},
}
}
dictConfig(log_dicts)
log_prod = getLogger('normal')
log_debug = getLogger('debug')
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 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 prj_to_xcore(prj_file):
ssh = SSHClient()
ssh.set_missing_host_key_policy(AutoAddPolicy())
ssh.connect(ip_addr, 22, username='luoshi', password='luoshi2019')
sftp = ssh.open_sftp()
sftp.put(prj_file, '/tmp/target.zip')
cmd = 'cd /tmp; rm -rf target/; mkdir target; unzip -d target/ -q target.zip; '
cmd += 'chmod 777 -R target/; rm target.zip'
ssh.exec_command(cmd)
cmd = 'sudo rm -rf /home/luoshi/bin/controller/projects/target; '
cmd += 'sudo mv /tmp/target/ /home/luoshi/bin/controller/projects/'
stdin, stdout, stderr = ssh.exec_command(cmd, get_pty=True)
stdin.write('luoshi2019' + '\n')
stdout.read().decode() # 需要read一下才能正常执行
stderr.read().decode()
cmd = 'cd /home/luoshi/bin/controller/; '
cmd += 'sudo chmod -R 755 projects; rm /tmp/*.prj; sudo mv projects/target/_build/*.prj /tmp; cd /tmp; '
cmd += 'prj=($(ls *.prj)); sudo mv ${prj[0]} /home/luoshi/bin/controller/projects/target/_build/target.prj; '
stdin, stdout, stderr = ssh.exec_command(cmd, get_pty=True)
stdin.write('luoshi2019' + '\n')
stdout.read().decode() # 需要read一下才能正常执行
stderr.read().decode()
ssh.close()
def execution(cmd, hr, w2t, tab_name, **kwargs):
_id = hr.execution(cmd, **kwargs)
_msg = hr.get_from_id(_id)
if not _msg:
w2t(f"无法获取{_id}请求的响应信息", 0, 6, 'red', tab_name)
else:
return eval(_msg.split('#')[2])

623
aio/code/commons/openapi.py Normal file
View File

@ -0,0 +1,623 @@
from json import load, dumps, loads
from socket import socket, AF_INET, SOCK_STREAM
from threading import Thread
import selectors
from time import time, sleep
from pymodbus.client.tcp import ModbusTcpClient
from pymodbus.payload import BinaryPayloadDecoder, BinaryPayloadBuilder
from pymodbus.constants import Endian
from commons import clibs
logger = clibs.log_prod
class ModbusRequest(object):
def __init__(self, w2t):
super().__init__()
self.w2t = w2t
self.tab_name = 'openapi'
self.host = clibs.ip_addr
self.port = 502
self.interval = 0.3
self.c = ModbusTcpClient(self.host, self.port)
self.c.connect()
def motor_off(self):
try:
self.c.write_register(40002, 0)
sleep(self.interval)
self.c.write_register(40002, 1)
sleep(self.interval)
self.c.write_register(40002, 0)
except Exception as Err:
self.w2t(f"{Err}\n无法正常下电连接Modbus失败需要确认网络是否通畅或是未正确导入寄存器文件...", 0, 100, 'red', self.tab_name)
def motor_on(self):
try:
self.c.write_register(40003, 0)
sleep(self.interval)
self.c.write_register(40003, 1)
sleep(self.interval)
self.c.write_register(40003, 0)
except Exception as Err:
self.w2t(f"{Err}\n无法正常上电连接Modbus失败需要确认网络是否通畅或是未正确导入寄存器文件...", 0, 100, 'red', self.tab_name)
def trigger_estop(self):
try:
self.c.write_register(40012, 0)
except Exception as Err:
self.w2t(f"{Err}\n无法触发软急停连接Modbus失败需要确认网络是否通畅或是未正确导入寄存器文件...", 0, 100, 'red', self.tab_name)
def reset_estop(self):
try:
self.c.write_register(40012, 1)
sleep(self.interval)
self.c.write_register(40001, 0)
sleep(self.interval)
self.c.write_register(40001, 1)
sleep(self.interval)
self.c.write_register(40001, 0)
except Exception as Err:
self.w2t(f"{Err}\n无法重置软急停连接Modbus失败需要确认网络是否通畅或是未正确导入寄存器文件...", 0, 100, 'red', self.tab_name)
def clear_alarm(self):
try:
self.c.write_register(40000, 1)
except Exception as Err:
self.w2t(f"{Err}\n无法清除告警连接Modbus失败需要确认网络是否通畅或是未正确导入寄存器文件...", 0, 100, 'red', self.tab_name)
def get_cart_vel(self):
try:
results = self.c.read_holding_registers(40537, 7)
except Exception as Err:
self.w2t(f"{Err}\n无法读取笛卡尔速度连接Modbus失败需要确认网络是否通畅或是未正确导入寄存器文件...", 0, 100, 'red', self.tab_name)
def get_jnt_vel(self):
try:
results = self.c.read_holding_registers(40579, 7)
except Exception as Err:
self.w2t(f"{Err}\n无法读取关节速度连接Modbus失败需要确认网络是否通畅或是未正确导入寄存器文件...", 0, 100, 'red', self.tab_name)
def get_tcp_vel(self):
try:
results = self.c.read_holding_registers(40607, 7)
except Exception as Err:
self.w2t(f"{Err}\n无法读取TCP速度连接Modbus失败需要确认网络是否通畅或是未正确导入寄存器文件...", 0, 100, 'red', self.tab_name)
def get_tcp_mag_vel(self):
try:
results = self.c.read_holding_registers(40621, 1)
except Exception as Err:
self.w2t(f"{Err}\n无法读取TCP合成速度连接Modbus失败需要确认网络是否通畅或是未正确导入寄存器文件...", 0, 100, 'red', self.tab_name)
def write_act(self, number):
try:
self.c.write_register(41000, number)
except Exception as Err:
self.w2t(f"{Err}\n无法发送执行信号连接Modbus失败需要确认网络是否通畅或是未正确导入寄存器文件...", 0, 100, 'red', self.tab_name)
def read_ready_to_go(self):
try:
results = self.c.read_holding_registers(41001, 1)
return results.registers[0]
except Exception as Err:
self.w2t(f"{Err}\n无法读取准备信号连接Modbus失败需要确认网络是否通畅或是未正确导入寄存器文件...", 0, 100, 'red', self.tab_name)
def read_scenario_time(self):
try:
results = self.c.read_holding_registers(41002, 2)
result = BinaryPayloadDecoder.fromRegisters(results.registers, byteorder=Endian.BIG, wordorder=Endian.LITTLE)
result = f"{result.decode_32bit_float():.3f}"
return result
except Exception as Err:
self.w2t(f"{Err}\n无法读取准备信号连接Modbus失败需要确认网络是否通畅或是未正确导入寄存器文件...", 0, 100, 'red', self.tab_name)
def write_stop0(self, number):
try:
self.c.write_register(41004, number)
except Exception as Err:
self.w2t(f"{Err}\n无法通过IO操作stop0急停连接Modbus失败需要确认网络是否通畅或是未正确导入寄存器文件...", 0, 100, 'red', self.tab_name)
def write_speed_max(self, speed):
try:
builder = BinaryPayloadBuilder(byteorder=Endian.BIG, wordorder=Endian.LITTLE)
builder.add_32bit_float(float(speed))
payload = builder.build()
self.c.write_registers(41005, payload, skip_encode=True)
except Exception as Err:
self.w2t(f"{Err}\n无法写入速度值连接Modbus失败需要确认网络是否通畅或是未正确导入寄存器文件...", 0, 100, 'red', self.tab_name)
def read_brake_done(self):
try:
results = self.c.read_holding_registers(41007, 1)
return results.registers[0]
except Exception as Err:
self.w2t(f"{Err}\n无法读取制动已执行信号连接Modbus失败需要确认网络是否通畅或是未正确导入寄存器文件...", 0, 100, 'red', self.tab_name)
def write_axis(self, axis):
try:
builder = BinaryPayloadBuilder(byteorder=Endian.BIG, wordorder=Endian.LITTLE)
builder.add_32bit_int(int(axis))
payload = builder.to_registers()
self.c.write_registers(41008, payload)
except Exception as Err:
self.w2t(f"{Err}\n无法写入速度值连接Modbus失败需要确认网络是否通畅或是未正确导入寄存器文件...", 0, 100, 'red', self.tab_name)
def write_probe(self, probe):
try:
self.c.write_register(41010, probe)
except Exception as Err:
self.w2t(f"{Err}\n无法写入速度探测信号连接Modbus失败需要确认网络是否通畅或是未正确导入寄存器文件...", 0, 100, 'red', self.tab_name)
def write_pon(self, pon): # positive or negative
try:
self.c.write_register(41011, pon)
except Exception as Err:
self.w2t(f"{Err}\n无法写入正负方向信号连接Modbus失败需要确认网络是否通畅或是未正确导入寄存器文件...", 0, 100, 'red', self.tab_name)
class HmiRequest(object):
def __init__(self, w2t):
super().__init__()
self.w2t = w2t
self.c = None
self.c_xs = None
self.c_msg = []
self.c_msg_xs = []
self.flag = 0
self.response = ''
self.leftover = 0
self.flag_xs = 0
self.response_xs = ''
self.t_bool = True
self.tab_name = 'openapi'
self.pkg_size = 0
self.broke = 0
self.half = 0
self.half_length = 0
self.index = 0
self.reset_index = 0
self.durable_lock = 0
self.sock_conn()
self.t_heartbeat = Thread(target=self.heartbeat)
self.t_heartbeat.daemon = True
self.t_heartbeat.start()
self.t_unpackage = Thread(target=self.unpackage, args=(self.c,))
self.t_unpackage.daemon = True
self.t_unpackage.start()
self.t_unpackage_xs = Thread(target=self.unpackage_xs, args=(self.c_xs,))
self.t_unpackage_xs.daemon = True
self.t_unpackage_xs.start()
def sock_conn(self):
# while True:
with open(clibs.heartbeat, "r", encoding='utf-8') as f_hb:
c_state = f_hb.read().strip()
if c_state == '0':
try:
self.c = socket(AF_INET, SOCK_STREAM)
self.c.connect((clibs.ip_addr, 5050))
self.c.setblocking(False)
self.c_xs = socket(AF_INET, SOCK_STREAM)
self.c_xs.connect((clibs.ip_addr, 6666))
self.c_xs.setblocking(False)
self.w2t("Connection success", 0, 0, 'green', tab_name=self.tab_name)
with open(clibs.heartbeat, "w", encoding='utf-8') as f_hb:
f_hb.write('1')
md = ModbusRequest(self.w2t)
md.reset_estop()
md.clear_alarm()
md.write_act(False)
md.write_probe(False)
md.write_axis(1)
except Exception as Err:
self.w2t("Connection failed...", 0, 0, 'red', tab_name=self.tab_name)
with open(clibs.heartbeat, "w", encoding='utf-8') as f_hb:
f_hb.write('0')
def header_check(self, index, data):
if index + 8 < len(data):
_frame_size = int.from_bytes(data[index:index+2], byteorder='big')
_pkg_size = int.from_bytes(data[index+2:index+6], byteorder='big')
_protocol = int.from_bytes(data[index + 6:index + 7], byteorder='big')
_reserved = int.from_bytes(data[index + 7:index + 8], byteorder='big')
if _reserved == 0 and _protocol == 2:
return index + 8, _frame_size, _pkg_size
else:
print(data)
# print(f"index = {index}")
# print(f"reserve = {_reserved}")
# print(f"protocol = {_protocol}")
# print("head check 数据有误,需要确认")
self.w2t("Header Check: 解包数据有误,需要确认!", 0, 1, 'red', tab_name=self.tab_name)
else:
self.half_length = len(data) - index
self.half = data[index:]
# print(f"in head check half: {self.half}")
# print(f"in head check length: {self.half_length}")
# print(f"in head check data: {data}")
self.broke = 100
index += clibs.MAX_FRAME_SIZE
return index, 0, 0
def heartbeat(self):
while self.t_bool:
_id = self.execution('controller.heart')
_flag = '0' if self.get_from_id(_id) is None else '1'
# print(f"hb = {_flag}", end=' ')
# print(f"len(c_msg) = {len(self.c_msg)}", end=' ')
# print(f"len(c_msg_xs) = {len(self.c_msg_xs)}", end='\n')
with open(clibs.heartbeat, "w", encoding='utf-8') as f_hb:
f_hb.write(_flag)
if _flag == '0':
self.w2t(f"{_id} 心跳丢失,连接失败,重新连接中...", 0, 7, 'red', tab_name=self.tab_name)
sleep(2)
def msg_storage(self, response, flag=0):
# response是解码后的字符串
messages = self.c_msg if flag == 0 else self.c_msg_xs
logger.warning(f"{loads(response)}")
if 'move.monitor' in response:
pass
elif len(messages) < 10000:
messages.insert(0, response)
else:
messages.insert(0, response)
while len(messages) > 20000:
messages.pop()
def get_response(self, data):
# 流式获取单次请求的响应
if self.broke == 100:
# print("*****************************************")
# print(f"in get_response if broke == 100 half = {self.half}")
_half_1 = self.half
_half_2 = data[:8-self.half_length]
_full = _half_1 + _half_2
# print(f"in get_response if broke == 100 _full = {_full}")
_frame_size = int.from_bytes(_full[:2], byteorder='big')
_pkg_size = int.from_bytes(_full[2:6], byteorder='big')
_protocol = int.from_bytes(_full[6:7], byteorder='big')
_reserved = int.from_bytes(_full[7:8], byteorder='big')
if _reserved != 0 or _protocol != 2:
print(data)
self.w2t("in get_response: 解包数据有误,需要确认!", 0, 1, 'red', tab_name=self.tab_name)
self.pkg_size = _pkg_size
self.index = 8 - self.half_length
# print(f"broke == 100 index = {self.index}")
# print(f"broke == 100 INIT pkg size = {self.pkg_size}")
# print(f"broke == 100 data = {data}")
else:
if self.reset_index == 1:
self.index = 0
while self.index < len(data):
# flag 为 0则说明是一次新的请求对应的一次新的相应也就是需要首次解包
if self.flag == 0:
if self.broke == 100:
self.broke = 0
else:
self.index, _frame_size, self.pkg_size = self.header_check(self.index, data)
# print(f"broke == 0 index = {self.index-8}")
# print(f"broke == 0 INIT pkg size = {self.pkg_size}")
# print(f"broke == 0 data = {data}")
if self.index > clibs.MAX_FRAME_SIZE:
break
# 详见解包原理数据.txtself.pkg_size 永远是除了当前data之外剩余未处理的数据大小
if self.pkg_size <= len(data) - self.index:
# 说明剩余部分的数据就在当前data内没有被分割
self.response = data[self.index:self.index + self.pkg_size].decode()
self.msg_storage(flag=0, response=self.response)
self.index += self.pkg_size
# print(f"in flag=0 if data = {data}")
# print(f"in flag=0 if index = {self.index}")
# print(f"in flag=0 if pkg size = {self.pkg_size}")
# print(f"in flag=0 if leftover = {self.leftover}")
self.flag = 0
self.response = ''
self.leftover = 0
self.pkg_size = 0
self.reset_index = 0 if self.index < len(data) else 1
elif self.pkg_size > len(data) - self.index:
# 执行到这里说明该data是首包且有有分包的情况发生了也就是该响应数据量稍微比较大
# 分散在了相邻的两个及以上的data中需要flag=1的处理
self.flag = 1
if self.index+_frame_size-6 <= len(data):
self.response = data[self.index:self.index+_frame_size-6].decode()
self.index += (_frame_size-6)
self.pkg_size -= (_frame_size-6) # 详见解包原理数据.txtself.pkg_size
self.reset_index = 0
if self.index + 2 < len(data):
self.leftover = int.from_bytes(data[self.index:self.index + 2], byteorder='big')
self.index += 2
self.reset_index = 0
else:
if self.index + 2 == len(data):
self.broke = 1
self.half = data[-2:]
# print(f"flag = 0 encounter broke == 1 - half = {self.half}")
elif self.index + 1 == len(data):
self.broke = 2
self.half = data[-1:]
# print(f"flag = 0 encounter broke == 2 - half = {self.half}")
elif self.index == len(data):
# print('flag = 0 encounter broke == 3')
self.broke = 3
self.index += clibs.MAX_FRAME_SIZE
self.reset_index = 1
break # 因为 index + 2 的大小超过 clibs.MAX_FRAME_SIZE
elif self.index+_frame_size-6 > len(data):
self.response = data[self.index:].decode()
self.pkg_size -= (len(data) - self.index) # 详见解包原理数据.txtself.pkg_size
self.leftover = (_frame_size-6-(len(data)-self.index))
self.index += clibs.MAX_FRAME_SIZE
self.reset_index = 1
# print(f"in flag=0 else data = {data}")
# print(f"in flag=0 else index = {self.index}")
# print(f"in flag=0 else pkg size = {self.pkg_size}")
# print(f"in flag=0 else leftover = {self.leftover}")
# break
elif self.flag == 1:
# 继续处理之前为接收完的数据处理完之后将flag重置为0
# !!!需要注意的是,包头/帧头也是有可能被分割开的!!!但是目前该程序未实现此种情况!!!
if self.broke == 1:
self.index = 0
self.leftover = int.from_bytes(self.half, byteorder='big')
self.broke = 0
# print(f"broke 1 leftover: {self.leftover}")
elif self.broke == 2:
self.leftover = int.from_bytes(self.half+data[:1], byteorder='big')
self.index = 1
self.broke = 0
# print(f"broke 2 leftover: {self.leftover}")
if self.broke == 3:
self.leftover = int.from_bytes(data[:2], byteorder='big')
# print(f"broke 3 leftover: {self.leftover}")
self.index = 2
self.broke = 0
while self.pkg_size > 0:
if self.index + self.leftover <= len(data):
# print(f"in pkg size > 0 loop before if data = {data}")
# print(f"in pkg size > 0 loop before if index = {self.index}")
# print(f"in pkg size > 0 loop before if pkg size = {self.pkg_size}")
# print(f"in pkg size > 0 loop before if leftover = {self.leftover}")
if self.leftover < 0 or self.leftover > 1024:
self.w2t("", 0, 111, 'red')
self.response += data[self.index:self.index + self.leftover].decode()
self.pkg_size -= self.leftover
if self.pkg_size == 0:
self.msg_storage(flag=0, response=self.response)
self.index += self.leftover
self.flag = 0
self.response = ''
self.leftover = 0
self.pkg_size = 0
self.reset_index = 0 if self.index < len(data) else 1
# print(f"in pkg size > 0 loop break if data = {data}")
# print(f"in pkg size > 0 loop break if index = {self.index}")
# print(f"in pkg size > 0 loop break if pkg size = {self.pkg_size}")
# print(f"in pkg size > 0 loop break if leftover = {self.leftover}")
break
self.index += self.leftover
if self.index + 2 < len(data):
self.leftover = int.from_bytes(data[self.index:self.index + 2], byteorder='big')
self.index += 2
self.reset_index = 0
else:
# self.leftover = 4096
if self.index + 2 == len(data):
self.broke = 1
self.half = data[-2:]
# print(f"flag = 1 encounter broke == 1 - half = {self.half}")
elif self.index + 1 == len(data):
self.broke = 2
self.half = data[-1:]
# print(f"flag = 1 encounter broke == 2 - half = {self.half}")
elif self.index == len(data):
# print('flag = 1 encounter broke == 3')
self.broke = 3
self.index += clibs.MAX_FRAME_SIZE
self.reset_index = 1
break # 因为 index + 2 的大小超过 clibs.MAX_FRAME_SIZE
# print(f"in pkg size > 0 loop after if data = {data}")
# print(f"in pkg size > 0 loop after if index = {self.index}")
# print(f"in pkg size > 0 loop after if pkg size = {self.pkg_size}")
# print(f"in pkg size > 0 loop after if leftover = {self.leftover}")
if self.leftover < 0 or self.leftover > 1024:
self.w2t("", 0, 111, 'red')
else:
# print(f"in pkg size > 0 loop before else data = {data}")
# print(f"in pkg size > 0 loop before else index = {self.index}")
# print(f"in pkg size > 0 loop before else pkg size = {self.pkg_size}")
# print(f"in pkg size > 0 loop before else leftover = {self.leftover}")
if self.leftover < 0 or self.leftover > 1024:
self.w2t("", 0, 111, 'red')
self.response += data[self.index:].decode()
self.leftover -= (len(data) - self.index)
self.pkg_size -= (len(data) - self.index)
self.index += clibs.MAX_FRAME_SIZE
self.reset_index = 1
# print(f"in pkg size > 0 loop after else data = {data}")
# print(f"in pkg size > 0 loop after else index = {self.index}")
# print(f"in pkg size > 0 loop after else pkg size = {self.pkg_size}")
# print(f"in pkg size > 0 loop after else leftover = {self.leftover}")
if self.leftover < 0 or self.leftover > 1024:
self.w2t("", 0, 111, 'red')
break # 该data内数据已经处理完毕需要跳出大循环通过break和index
# else:
# self.msg_storage(flag=0, response=self.response)
# self.flag = 0
# self.response = ''
# self.leftover = 0
# self.pkg_size = 0
# self.index -= 2
# self.reset_index = 0 if (len(data) > self.index > 0) else 1
def get_response_xs(self, data):
if self.flag_xs == 0:
if data[-1].decode() == '\r':
_responses = data.decode().split('\r')
for _response in _responses:
self.msg_storage(flag=1, response=_response)
else:
_responses = data.decode().split('\r')
for _response in _responses[:-1]:
if not _response:
break
self.msg_storage(flag=1, response=_response)
self.response_xs = _responses[-1]
self.flag_xs = 1
else:
if data[-1].decode() == '\r':
_responses = (self.response_xs.encode() + data).decode().split('\r')
for _response in _responses:
self.msg_storage(flag=1, response=_response)
self.response_xs = ''
self.flag_xs = 0
else:
_responses = (self.response_xs.encode() + data).decode().split('\r')
for _response in _responses[:-1]:
if not _response:
break
self.msg_storage(flag=1, response=_response)
self.response_xs = _responses[-1]
self.flag_xs = 1
def get_from_id(self, msg_id, flag=0):
for i in range(3):
with open(clibs.log_data_hmi, mode='r', encoding='utf-8') as f_log:
for line in f_log:
if msg_id in line.strip():
return line
sleep(1)
else: # 尝试在上一次分割的日志中查找,只做一次
sleep(1)
try:
with open(clibs.log_data_hmi+'.1', mode='r', encoding='utf-8') as f_log:
for line in f_log:
if msg_id in line.strip():
return line
except FileNotFoundError:
pass
return None
def package(self, cmd):
_frame_head = (len(cmd) + 6).to_bytes(length=2, byteorder='big')
_pkg_head = len(cmd).to_bytes(length=4, byteorder='big')
_protocol = int(2).to_bytes(length=1, byteorder='big')
_reserved = int(0).to_bytes(length=1, byteorder='big')
return _frame_head + _pkg_head + _protocol + _reserved + cmd.encode()
def package_xs(self, cmd):
return f"{dumps(cmd, separators=(',', ':'))}\r".encode()
def unpackage(self, sock):
def to_read(conn, mask):
data = conn.recv(clibs.MAX_FRAME_SIZE)
if data:
# print(data)
self.get_response(data)
else:
print('closing', conn)
sel.unregister(conn)
conn.close()
sel = selectors.DefaultSelector()
sel.register(sock, selectors.EVENT_READ, to_read)
while self.t_bool:
events = sel.select()
for key, mask in events:
callback = key.data
callback(key.fileobj, mask)
def unpackage_xs(self, sock):
def to_read(conn, mask):
data = conn.recv(1024) # Should be ready
if data:
# print(data)
self.get_response_xs(data)
else:
print('closing', conn)
sel.unregister(conn)
conn.close()
sel = selectors.DefaultSelector()
sel.register(sock, selectors.EVENT_READ, to_read)
while self.t_bool:
events = sel.select()
for key, mask in events:
callback = key.data
callback(key.fileobj, mask)
def gen_id(self, command):
_now = time()
_id = f"{command}-{_now}"
return _id
def execution(self, command, flg=0, **kwargs):
if flg == 0: # for old protocols
req = None
try:
with open(f'{clibs.PREFIX}templates/json/{command}.json', encoding='utf-8',
mode='r') as f_json:
req = load(f_json)
except:
self.w2t(f"暂不支持 {command} 功能,或确认该功能存在...", 0, 1, 'red', tab_name=self.tab_name)
match command:
case 'state.set_tp_mode':
req['data']['tp_mode'] = kwargs['tp_mode']
case 'overview.set_autoload':
req['data']['autoload_prj_path'] = kwargs['autoload_prj_path']
case 'overview.reload':
req['data']['prj_path'] = kwargs['prj_path']
req['data']['tasks'] = kwargs['tasks']
case 'rl_task.pp_to_main' | 'rl_task.run' | 'rl_task.stop':
req['data']['tasks'] = kwargs['tasks']
case 'diagnosis.set_params':
req['data']['display_pdo_params'] = kwargs['display_pdo_params']
case 'diagnosis.open':
req['data']['open'] = kwargs['open']
req['data']['display_open'] = kwargs['display_open']
case 'register.set_value':
req['data']['name'] = kwargs['name']
req['data']['type'] = kwargs['type']
req['data']['bias'] = kwargs['bias']
req['data']['value'] = kwargs['value']
case 'diagnosis.save':
req['data']['save'] = kwargs['save']
case _:
pass
req['id'] = self.gen_id(command)
# print(f"req = {req}")
cmd = dumps(req, separators=(',', ':'))
try:
self.c.send(self.package(cmd))
sleep(0.5)
except Exception as Err:
self.w2t(f"{cmd}: 请求发送失败...{Err}", 0, 0, 'red', tab_name=self.tab_name)
return req['id']
else: # for xService
pass

View File

@ -1,104 +1,35 @@
# coding: utf-8 from os.path import isfile
from os import scandir
from os.path import isfile, exists
from sys import argv from sys import argv
from openpyxl import load_workbook from openpyxl import load_workbook
from time import time, sleep, strftime, localtime from time import time, sleep, strftime, localtime
from threading import Thread from threading import Thread
from pandas import read_csv from pandas import read_csv
from commons import clibs
logger = clibs.log_prod
class GetThreadResult(Thread): def check_files(path, raw_data_dirs, result_files, w2t):
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 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, 0, 'orange')
global stop
stop = 0
t_excel = GetThreadResult(load_workbook, args=(result_file, ))
t_wait = Thread(target=w2t_local, args=('.', 1, w2t))
t_excel.start()
t_wait.start()
t_excel.join()
wb_result = t_excel.get_result()
stop = 1
sleep(1.1)
w2t('')
prefix = result_file.split('\\')[-1].split('_')[0]
for raw_data_dir in raw_data_dirs:
if raw_data_dir.split('\\')[-1].split('_')[0] == prefix:
now_doing_msg(raw_data_dir, 'start', w2t)
_, data_files = traversal_files(raw_data_dir, w2t)
# 数据文件串行处理模式---------------------------------
# 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, 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, 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, 0, 'orange')
stop = 0
t_excel = Thread(target=wb_result.save, args=(result_file, ))
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')
def check_files(raw_data_dirs, result_files, w2t):
# 功能:检查数据文件以及结果文件的合规性 # 功能:检查数据文件以及结果文件的合规性
# 参数:数据文件夹,结果文件 # 参数:数据文件夹,结果文件
# 返回值:- # 返回值:-
if len(result_files) != 3: if len(result_files) != 4:
msg = "结果文件数目错误,结果文件有且只有三个,请确认!"
for result_file in result_files: for result_file in result_files:
w2t(result_file) w2t(result_file)
w2t(msg, 0, 2, 'red') w2t("需要有四个文件,包括三个结果文件,以及一个配置文件,请确认!", 0, 2, 'red')
for result_file in result_files:
if result_file.endswith('configs.xlsx'):
result_files.remove(result_file)
break
else:
w2t("未找到配置文件,请确认!", 0, 8, 'red')
prefix = [] prefix = []
for result_file in result_files: for result_file in result_files:
prefix.append(result_file.split('\\')[-1].split('_')[0]) prefix.append(result_file.split('\\')[-1].split('_')[0])
if not sorted(prefix) == sorted(['reach33', 'reach66', 'reach100']): if not sorted(prefix) == sorted(['reach33', 'reach66', 'reach100']):
wd = result_files[0].split('\\') msg = f"""请关闭所有相关数据文件,并检查工作目录 {path} 下,有且只允许有类似如下三个文件:
del wd[-1]
wd = '\\'.join(wd)
msg = f"""请关闭所有相关数据文件,并检查工作目录 {wd} 下,有且只允许有类似如下三个文件:
1. reach33_XXX制动性能测试.xlsx 1. reach33_XXX制动性能测试.xlsx
2. reach66_XXX制动性能测试.xlsx 2. reach66_XXX制动性能测试.xlsx
3. reach100_XX制动性能测试.xlsx""" 3. reach100_XX制动性能测试.xlsx"""
@ -115,7 +46,7 @@ def check_files(raw_data_dirs, result_files, w2t):
规则解释AA/BB/CC 指的是臂展/负载/速度的比例例如reach66_load100_speed3366%臂展100%负载以及33%速度情况下的测试结果文件夹""" 规则解释AA/BB/CC 指的是臂展/负载/速度的比例例如reach66_load100_speed3366%臂展100%负载以及33%速度情况下的测试结果文件夹"""
w2t(msg, 0, 4, 'red') w2t(msg, 0, 4, 'red')
_, raw_data_files = traversal_files(raw_data_dir, w2t) _, raw_data_files = clibs.traversal_files(raw_data_dir, w2t)
if len(raw_data_files) != 3: if len(raw_data_files) != 3:
msg = f"数据目录 {raw_data_dir} 下数据文件个数错误,每个数据目录下有且只能有三个以 .data 为后缀的数据文件" msg = f"数据目录 {raw_data_dir} 下数据文件个数错误,每个数据目录下有且只能有三个以 .data 为后缀的数据文件"
w2t(msg, 0, 5, 'red') w2t(msg, 0, 5, 'red')
@ -127,6 +58,21 @@ def check_files(raw_data_dirs, result_files, w2t):
w2t("数据目录合规性检查结束,未发现问题......") w2t("数据目录合规性检查结束,未发现问题......")
def get_configs(configfile, w2t):
axis = configfile.split('\\')[-2][-1]
if axis not in ['1', '2', '3']:
w2t("被处理的根文件夹命名必须是 [Jj][123] 的格式", 0, 9, 'red')
else:
axis = int(axis)
_wb = load_workbook(configfile, read_only=True)
_ws = _wb['Target']
rr = float(_ws.cell(row=2, column=axis+1).value)
av = float(_ws.cell(row=3, column=axis+1).value)
return av, rr
def now_doing_msg(docs, flag, w2t): def now_doing_msg(docs, flag, w2t):
# 功能:输出正在处理的文件或目录 # 功能:输出正在处理的文件或目录
# 参数文件或目录start 或 done 标识 # 参数文件或目录start 或 done 标识
@ -153,20 +99,6 @@ def w2t_local(msg, wait, w2t):
break break
def single_file_process(data_file, wb_result, count, av, rr, axis, vel, trq, w2t, estop):
# 功能:完成单个数据文件的处理
# 参数:如上
# 返回值:-
df = read_csv(data_file, sep='\t')
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_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(df, ws_result, row_start, row_end, vel, trq, estop): def copy_data_to_result(df, ws_result, row_start, row_end, vel, trq, estop):
# 功能:将数据文件中有效数据拷贝至结果文件对应的 sheet # 功能:将数据文件中有效数据拷贝至结果文件对应的 sheet
# 参数:如上 # 参数:如上
@ -193,26 +125,13 @@ def copy_data_to_result(df, ws_result, row_start, row_end, vel, trq, estop):
ws_result.cell(row=_row, column=3).value = None ws_result.cell(row=_row, column=3).value = None
def find_result_sheet_name(conditions, count): def find_row_start(data_file, df, conditions, av, rr, vel, estop, w2t):
# 功能获取结果文件准确的sheet页名称
# 参数:臂展和速度的列表
# 返回值结果文件对应的sheet name
# 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, estop):
# 功能:查找数据文件中有效数据的行号,也即最后一个速度下降的点位 # 功能:查找数据文件中有效数据的行号,也即最后一个速度下降的点位
# 参数:如上 # 参数:如上
# 返回值:速度下降点位,最后的数据点位 # 返回值:速度下降点位,最后的数据点位
ratio = float(conditions[2].removeprefix('speed'))/100 ratio = float(conditions[2].removeprefix('speed'))/100
av_max = av * ratio av_max = av * ratio
row_max = df.index[-1] row_max = df.index[-1]
# threshold = 30 if axis == 2 and conditions[0].removeprefix('load') == '100' else 10
threshold = 0.95 threshold = 0.95
for _row in range(row_max, -1, -1): for _row in range(row_max, -1, -1):
@ -238,35 +157,99 @@ def find_row_start(data_file, df, conditions, av, rr, axis, vel, w2t, estop):
return row_start, row_end return row_start, row_end
def traversal_files(path, w2t): def find_result_sheet_name(conditions, count):
# 功能:以列表的形式分别返回指定路径下的文件和文件夹,不包含子目录 # 功能:获取结果文件准确的sheet页名称
# 参数:路径 # 参数:臂展和速度的列表
# 返回值:路径下的文件夹列表 路径下的文件列表 # 返回值:结果文件对应的sheet name
if not exists(path): # 33%负载_33%速度_1 - ['loadxx', 'reachxx', 'speedxx']
msg = f'数据文件夹{path}不存在,请确认后重试......' load = conditions[0].removeprefix('load')
w2t(msg, 0, 1, 'red') speed = conditions[2].removeprefix('speed')
else: result_sheet_name = f"{load}%负载_{speed}%速度_{count}"
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 return result_sheet_name
def main(path, av, rr, axis, vel, trq, estop, w2t): def single_file_process(data_file, wb_result, count, av, rr, vel, trq, estop, w2t):
# 功能:完成单个数据文件的处理
# 参数:如上
# 返回值:-
df = read_csv(data_file, sep='\t')
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_start, row_end = find_row_start(data_file, df, conditions, av, rr, vel, estop, w2t)
copy_data_to_result(df, ws_result, row_start, row_end, vel, trq, estop)
def data_process(result_file, raw_data_dirs, av, rr, vel, trq, estop, w2t):
# 功能:完成一个结果文件的数据处理
# 参数:结果文件,数据目录,以及预读取的参数
# 返回值:-
file_name = result_file.split('\\')[-1]
w2t(f"正在打开文件 {file_name} 需要 1min 左右", 1, 0, 'orange')
global stop
stop = 0
t_excel = clibs.GetThreadResult(load_workbook, args=(result_file, ))
t_wait = Thread(target=w2t_local, args=('.', 1, w2t))
t_excel.start()
t_wait.start()
t_excel.join()
wb_result = t_excel.get_result()
stop = 1
sleep(1.1)
w2t('')
prefix = result_file.split('\\')[-1].split('_')[0]
for raw_data_dir in raw_data_dirs:
if raw_data_dir.split('\\')[-1].split('_')[0] == prefix:
now_doing_msg(raw_data_dir, 'start', w2t)
_, data_files = clibs.traversal_files(raw_data_dir, w2t)
# 数据文件串行处理模式---------------------------------
# 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, vel, trq, estop, w2t)
# count += 1
# now_doing_msg(data_file, 'done', w2t)
# ---------------------------------------------------
# 数据文件并行处理模式---------------------------------
threads = [
Thread(target=single_file_process, args=(data_files[0], wb_result, 1, av, rr, vel, trq, estop, w2t)),
Thread(target=single_file_process, args=(data_files[1], wb_result, 2, av, rr, vel, trq, estop, w2t)),
Thread(target=single_file_process, args=(data_files[2], wb_result, 3, av, rr, vel, trq, estop, w2t))
]
[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, 0, 'orange')
stop = 0
t_excel = Thread(target=wb_result.save, args=(result_file, ))
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')
def main(path, vel, trq, estop, w2t):
# 功能:执行处理所有数据文件 # 功能:执行处理所有数据文件
# 参数initialization函数的返回值 # 参数initialization函数的返回值
# 返回值:- # 返回值:-
time_start = time() time_start = time()
raw_data_dirs, result_files = traversal_files(path, w2t) raw_data_dirs, result_files = clibs.traversal_files(path, w2t)
try: try:
# threads = [] # threads = []
check_files(raw_data_dirs, result_files, w2t) check_files(path, raw_data_dirs, result_files, w2t)
av, rr = get_configs(path + '\\configs.xlsx', w2t)
prefix = [] prefix = []
for raw_data_dir in raw_data_dirs: for raw_data_dir in raw_data_dirs:
@ -277,8 +260,8 @@ def main(path, av, rr, axis, vel, trq, estop, w2t):
continue continue
else: else:
now_doing_msg(result_file, 'start', w2t) now_doing_msg(result_file, 'start', w2t)
data_process(result_file, raw_data_dirs, av, rr, axis, vel, trq, w2t, estop) data_process(result_file, raw_data_dirs, av, rr, vel, trq, estop, w2t)
# threads.append(Thread(target=data_process, args=(result_file, raw_data_dirs, AV, RR, RC, AXIS))) # threads.append(Thread(target=data_process, args=(result_file, raw_data_dirs, av, rr, vel, trq, estop, w2t)))
# [t.start() for t in threads] # [t.start() for t in threads]
# [t.join() for t in threads] # [t.join() for t in threads]
except Exception as Err: except Exception as Err:
@ -295,4 +278,4 @@ def main(path, av, rr, axis, vel, trq, estop, w2t):
if __name__ == "__main__": if __name__ == "__main__":
stop = 0 stop = 0
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]) main(*argv[1:])

View File

@ -1,31 +1,13 @@
from openpyxl import load_workbook from openpyxl import load_workbook
from os import scandir
from os.path import exists
from sys import argv from sys import argv
from pandas import read_csv, concat, set_option from pandas import read_csv, concat, set_option
from re import match from re import match
from threading import Thread from threading import Thread
from time import sleep from time import sleep
from csv import reader from csv import reader, writer
from commons import clibs
logger = clibs.log_prod
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): def w2t_local(msg, wait, w2t):
@ -38,112 +20,124 @@ def w2t_local(msg, wait, w2t):
break 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): def initialization(path, sub, w2t):
_, data_files = traversal_files(path, w2t) _, data_files = clibs.traversal_files(path, w2t)
count = 0 count = 0
for data_file in data_files: for data_file in data_files:
filename = data_file.split('\\')[-1] filename = data_file.split('\\')[-1]
if sub != 'cycle': if data_file.endswith('configs.xlsx'):
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 count += 1
elif not (match('j[1-7].*\\.data', filename) or match('j[1-7].*\\.csv', filename)): elif sub == 'cycle' and data_file.endswith('.xlsx'):
msg = f"所有文件必须以 jx_ 开头,以 .data/csv 结尾x取值1-7请检查后重新运行。" count += 1
w2t(msg, 0, 7, 'red') else:
if not (match('j[1-7].*\\.data', filename) or match('j[1-7].*\\.csv', filename)):
msg = f"不合规 {data_file}\n"
msg += f"所有文件必须以 jx_ 开头,以 .data/csv 结尾x取值1-7请检查后重新运行。"
w2t(msg, 0, 6, 'red')
if sub == 'cycle' and count != 1: if not ((sub == 'cycle' and count == 2) or (sub != 'cycle' and count == 1)):
w2t("未找到电机电流数据处理excel表格,确认后重新运行!", 0, 5, 'red') w2t("使用max/avg功能时需要有配置文件表格使用cycle功能时需要有电机电流数据处理和配置文件两个表格,确认后重新运行!", 0, 5, 'red')
return data_files return data_files
def current_max(data_files, rcs, trqh, w2t): def current_max(data_files, rcs, trq, w2t):
current = {1: 0, 2: 0, 3: 0, 4: 0, 5: 0, 6: 0, 7: 0} current = {1: [], 2: [], 3: [], 4: [], 5: [], 6: [], 7: []}
for data_file in data_files: for data_file in data_files:
if data_file.endswith('.data'): if data_file.endswith('.data'):
df = read_csv(data_file, sep='\t') df = read_csv(data_file, sep='\t')
elif data_file.endswith('.csv'): elif data_file.endswith('.csv'):
df = read_csv(data_file, sep=',', encoding='gbk', header=8) df = read_csv(data_file, sep=',', encoding='gbk', header=8)
else:
continue
cols = len(df.columns)
axis = int(data_file.split('\\')[-1].split('_')[0].removeprefix('j')) axis = int(data_file.split('\\')[-1].split('_')[0].removeprefix('j'))
rca = rcs[axis-1] rca = rcs[axis-1]
col = df.columns.values[trqh-1] col = df.columns.values[trq-1]
c_max = df[col].max() c_max = df[col].abs().max()
scale = 1 if data_file.endswith('.csv') else 1000 scale = 1 if data_file.endswith('.csv') else 1000
_ = abs(c_max/scale*rca) _ = abs(c_max/scale*rca)
current[axis] = _ current[axis].append(_)
w2t(f"{data_file}: {_:.4f}") w2t(f"{data_file}: {_:.4f}")
w2t("【MAX】数据处理完毕......") with open(data_file, 'a+') as f_data:
sep = '\t' if data_file.endswith('.data') else ','
csv_writer = writer(f_data, delimiter=sep)
csv_writer.writerow([''] * (cols-1) + [_])
for axis, cur in current.items():
if not cur:
continue
else:
w2t(f"{axis}轴数据:", 1, 0, 'purple')
for value in cur:
w2t(f"{value:.4f} ", 1, 0, 'purple')
w2t('')
w2t("\n【MAX】数据处理完毕......")
return current return current
def current_avg(data_files, rcs, trqh, w2t): def current_avg(data_files, rcs, trq, w2t):
current = {1: 0, 2: 0, 3: 0, 4: 0, 5: 0, 6: 0, 7: 0} current = {1: [], 2: [], 3: [], 4: [], 5: [], 6: [], 7: []}
for data_file in data_files: for data_file in data_files:
if data_file.endswith('.data'): if data_file.endswith('.data'):
df = read_csv(data_file, sep='\t') df = read_csv(data_file, sep='\t')
elif data_file.endswith('.csv'): elif data_file.endswith('.csv'):
df = read_csv(data_file, sep=',', encoding='gbk', header=8) df = read_csv(data_file, sep=',', encoding='gbk', header=8)
else:
continue
cols = len(df.columns)
axis = int(data_file.split('\\')[-1].split('_')[0].removeprefix('j')) axis = int(data_file.split('\\')[-1].split('_')[0].removeprefix('j'))
rca = rcs[axis-1] rca = rcs[axis-1]
col = df.columns.values[trqh - 1] col = df.columns.values[trq-1]
c_std = df[col].std() c_std = df[col].std()
c_avg = df[col].mean() c_avg = df[col].mean()
scale = 1 if data_file.endswith('.csv') else 1000 scale = 1 if data_file.endswith('.csv') else 1000
_ = (abs(c_avg)+c_std)/scale*rca _ = (abs(c_avg)+c_std*3)/scale*rca
current[axis] = _ current[axis].append(_)
w2t(f"{data_file}: {_:.4f}") w2t(f"{data_file}: {_:.4f}")
w2t("【AVG】数据处理完毕......") with open(data_file, 'a+') as f_data:
sep = '\t' if data_file.endswith('.data') else ','
csv_writer = writer(f_data, delimiter=sep)
csv_writer.writerow([''] * (cols-1) + [_])
for axis, cur in current.items():
if not cur:
continue
else:
w2t(f"{axis}轴数据:", 1, 0, 'purple')
for value in cur:
w2t(f"{value:.4f} ", 1, 0, 'purple')
w2t('')
w2t("\n【AVG】数据处理完毕......")
return current return current
def current_cycle(dur, data_files, rcs, vel, trq, trqh, rpm, w2t): def current_cycle(dur, data_files, rcs, rrs, vel, trq, trqh, rpms, w2t):
result = None result = None
hold = [] hold = []
single = [] single = []
for data_file in data_files: for data_file in data_files:
filename = data_file.split('\\')[-1] filename = data_file.split('\\')[-1]
if data_file.endswith('.xlsx'): if data_file.endswith('.xlsx') and not data_file.endswith('configs.xlsx'):
result = data_file result = data_file
elif match('j[1-7]_hold_.*\\.data', filename) or match('j[1-7]_hold_.*\\.csv', filename): elif match('j[1-7]_hold_.*\\.data', filename) or match('j[1-7]_hold_.*\\.csv', filename):
hold.append(data_file) hold.append(data_file)
else: elif match('j[1-7]_.*\\.data', filename) or match('j[1-7]_.*\\.csv', filename):
single.append(data_file) single.append(data_file)
w2t(f"正在打开文件 {result},需要 10s 左右", 1, 0, 'orange') w2t(f"正在打开文件 {result},需要 10s 左右", 1, 0, 'orange')
global stop global stop
stop = 0 stop = 0
t_excel = GetThreadResult(load_workbook, args=(result, )) t_excel = clibs.GetThreadResult(load_workbook, args=(result, ))
t_wait = Thread(target=w2t_local, args=('.', 1, w2t)) t_wait = Thread(target=w2t_local, args=('.', 1, w2t))
t_excel.start() t_excel.start()
t_wait.start() t_wait.start()
@ -158,14 +152,14 @@ def current_cycle(dur, data_files, rcs, vel, trq, trqh, rpm, w2t):
for axis, cur_value in avg.items(): for axis, cur_value in avg.items():
try: try:
shtname = f"J{axis}" shtname = f"J{axis}"
wb[shtname]["J4"].value = float(cur_value) wb[shtname]["J4"].value = float(cur_value[0])
except: except:
pass pass
if dur == 0: if dur == 0:
p_single(wb, single, vel, trq, rpm, w2t) p_single(wb, single, vel, trq, rpms, rrs, w2t)
else: else:
p_scenario(wb, single, vel, trq, rpm, dur, w2t) p_scenario(wb, single, vel, trq, rpms, rrs, dur, w2t)
w2t(f"正在保存文件 {result},需要 10s 左右", 1, 0, 'orange') w2t(f"正在保存文件 {result},需要 10s 左右", 1, 0, 'orange')
stop = 0 stop = 0
@ -206,20 +200,22 @@ def find_point(data_file, pos, flag, df, _row_s, _row_e, w2t, exitcode, threshol
w2t(f"[{pos}] {data_file}数据有误,需要检查,无法找到有效起始点或结束点...", 0, exitcode, 'red') w2t(f"[{pos}] {data_file}数据有误,需要检查,无法找到有效起始点或结束点...", 0, exitcode, 'red')
def p_single(wb, single, vel, trq, rpm, w2t): def p_single(wb, single, vel, trq, rpms, rrs, w2t):
# 1. 先找到第一个速度为零的点,数据从后往前找,一开始就是零的情况不予考虑 # 1. 先找到第一个速度为零的点,数据从后往前找,一开始就是零的情况不予考虑
# 2. 记录第一个点的位置,继续向前查找第二个速度为零的点,同理,一开始为零的点不予考虑 # 2. 记录第一个点的位置,继续向前查找第二个速度为零的点,同理,一开始为零的点不予考虑
# 3. 记录第二个点的位置,并将其中的数据拷贝至对应位置 # 3. 记录第二个点的位置,并将其中的数据拷贝至对应位置
for data_file in single: 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')) axis = int(data_file.split('\\')[-1].split('_')[0].removeprefix('j'))
shtname = f"J{axis}" shtname = f"J{axis}"
rpm = rpms[axis-1] if data_file.endswith('.csv') else 1
scale = 1000 if data_file.endswith('.csv') else 1
ws = wb[shtname] ws = wb[shtname]
addition = 1
set_option("display.precision", 2) set_option("display.precision", 2)
if data_file.endswith('.data'): if data_file.endswith('.data'):
df = read_csv(data_file, sep='\t') df = read_csv(data_file, sep='\t')
rr = rrs[axis-1]
addition = 180 / 3.1415926 * 60 / 360 * rr
elif data_file.endswith('.csv'): elif data_file.endswith('.csv'):
df = read_csv(data_file, sep=',', encoding='gbk', header=8) df = read_csv(data_file, sep=',', encoding='gbk', header=8)
csv_reader = reader(open(data_file)) csv_reader = reader(open(data_file))
@ -233,8 +229,9 @@ def p_single(wb, single, vel, trq, rpm, w2t):
ws["H11"] = cycle ws["H11"] = cycle
col_names = list(df.columns) col_names = list(df.columns)
df_1 = df[col_names[vel-1]].multiply(rpm) df_1 = df[col_names[vel-1]].multiply(rpm*addition)
df_2 = df[col_names[trq-1]].multiply(scale) df_2 = df[col_names[trq-1]].multiply(scale)
# print(df_1.abs().max())
df = concat([df_1, df_2], axis=1) df = concat([df_1, df_2], axis=1)
_step = 5 if data_file.endswith('.csv') else 50 _step = 5 if data_file.endswith('.csv') else 50
@ -275,7 +272,7 @@ def p_single(wb, single, vel, trq, rpm, w2t):
data.append(df.iloc[row, 1]) data.append(df.iloc[row, 1])
i = 0 i = 0
for row in ws.iter_rows(min_row=2, min_col=2, max_row=70000, max_col=3): for row in ws.iter_rows(min_row=2, min_col=2, max_row=150000, max_col=3):
for cell in row: for cell in row:
try: try:
_ = f"{data[i]:.2f}" _ = f"{data[i]:.2f}"
@ -285,18 +282,20 @@ def p_single(wb, single, vel, trq, rpm, w2t):
cell.value = None cell.value = None
def p_scenario(wb, single, vel, trq, rpm, dur, w2t): def p_scenario(wb, single, vel, trq, rpms, rrs, dur, w2t):
for data_file in single: for data_file in single:
cycle = 0.001 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')) axis = int(data_file.split('\\')[-1].split('_')[0].removeprefix('j'))
shtname = f"J{axis}" shtname = f"J{axis}"
rpm = rpms[axis-1] if data_file.endswith('.csv') else 1
scale = 1000 if data_file.endswith('.csv') else 1
ws = wb[shtname] ws = wb[shtname]
addition = 1
set_option("display.precision", 2) set_option("display.precision", 2)
if data_file.endswith('.data'): if data_file.endswith('.data'):
df = read_csv(data_file, sep='\t') df = read_csv(data_file, sep='\t')
rr = rrs[axis-1]
addition = 180 / 3.1415926 * 60 / 360 * rr
elif data_file.endswith('.csv'): elif data_file.endswith('.csv'):
df = read_csv(data_file, sep=',', encoding='gbk', header=8) df = read_csv(data_file, sep=',', encoding='gbk', header=8)
csv_reader = reader(open(data_file)) csv_reader = reader(open(data_file))
@ -310,7 +309,7 @@ def p_scenario(wb, single, vel, trq, rpm, dur, w2t):
ws["H11"] = cycle ws["H11"] = cycle
col_names = list(df.columns) col_names = list(df.columns)
df_1 = df[col_names[vel-1]].multiply(rpm) df_1 = df[col_names[vel-1]].multiply(rpm*addition)
df_2 = df[col_names[trq-1]].multiply(scale) df_2 = df[col_names[trq-1]].multiply(scale)
df = concat([df_1, df_2], axis=1) df = concat([df_1, df_2], axis=1)
@ -325,7 +324,7 @@ def p_scenario(wb, single, vel, trq, rpm, dur, w2t):
data.append(df.iloc[row, 1]) data.append(df.iloc[row, 1])
i = 0 i = 0
for row in ws.iter_rows(min_row=2, min_col=2, max_row=70000, max_col=3): for row in ws.iter_rows(min_row=2, min_col=2, max_row=150000, max_col=3):
for cell in row: for cell in row:
try: try:
_ = f"{data[i]:.2f}" _ = f"{data[i]:.2f}"
@ -335,17 +334,40 @@ def p_scenario(wb, single, vel, trq, rpm, dur, w2t):
cell.value = None cell.value = None
# ======================================= def get_configs(configfile, w2t):
_wb = load_workbook(configfile, read_only=True)
_ws = _wb['Target']
rcs = []
rrs = []
rpms = []
for i in range(2, 9):
try:
rpms.append(float(_ws.cell(row=4, column=i).value))
except:
rpms.append(0.0)
try:
rcs.append(float(_ws.cell(row=6, column=i).value))
except:
rcs.append(0.0)
try:
rrs.append(float(_ws.cell(row=2, column=i).value))
except:
rrs.append(0.0)
return rpms, rcs, rrs
def main(path, sub, rcs, vel, trq, trqh, dur, rpm, w2t): def main(path, sub, dur, vel, trq, trqh, w2t):
data_files = initialization(path, sub, w2t) data_files = initialization(path, sub, w2t)
rpms, rcs, rrs = get_configs(path + '\\configs.xlsx', w2t)
if sub == 'max': if sub == 'max':
current_max(data_files, rcs, trqh, w2t) current_max(data_files, rcs, trq, w2t)
elif sub == 'avg': elif sub == 'avg':
current_avg(data_files, rcs, trqh, w2t) current_avg(data_files, rcs, trq, w2t)
elif sub == 'cycle': elif sub == 'cycle':
current_cycle(dur, data_files, rcs, vel, trq, trqh, rpm, w2t) current_cycle(dur, data_files, rcs, rrs, vel, trq, trqh, rpms, w2t)
else: else:
pass pass

View File

@ -1,27 +1,10 @@
# _*_ encodingutf-8 _*_
import pdfplumber import pdfplumber
from openpyxl import load_workbook from openpyxl import load_workbook
from os import scandir, remove from os import remove
from os.path import exists
from sys import argv from sys import argv
from commons import clibs
logger = clibs.log_prod
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 p_iso(file, p_files, ws, tmpfile): def p_iso(file, p_files, ws, tmpfile):
@ -153,7 +136,7 @@ def p_iso_1000(file, p_files, ws, tmpfile):
def main(path, w2t): def main(path, w2t):
dirs, files = traversal_files(path, 1) dirs, files = clibs.traversal_files(path, 1)
try: try:
wb = load_workbook(path + "/iso-results.xlsx") wb = load_workbook(path + "/iso-results.xlsx")

View File

@ -0,0 +1,165 @@
from pandas import read_csv
from csv import reader
from sys import argv
from openpyxl import Workbook
from commons import clibs
logger = clibs.log_prod
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 = clibs.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() + 3 * 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

@ -0,0 +1,224 @@
from sys import argv
from json import loads
from time import sleep, time, strftime, localtime
from pandas import DataFrame
from openpyxl import load_workbook
from math import sqrt
from numpy import power
from csv import writer
from commons import clibs
logger = clibs.log_prod
tab_name = clibs.tab_names['da']
count = 0
display_pdo_params = [
# {"name": "hw_joint_vel_feedback", "channel": 0},
# {"name": "hw_joint_vel_feedback", "channel": 1},
# {"name": "hw_joint_vel_feedback", "channel": 2},
# {"name": "hw_joint_vel_feedback", "channel": 3},
# {"name": "hw_joint_vel_feedback", "channel": 4},
# {"name": "hw_joint_vel_feedback", "channel": 5},
{"name": "device_servo_trq_feedback", "channel": 0},
{"name": "device_servo_trq_feedback", "channel": 1},
{"name": "device_servo_trq_feedback", "channel": 2},
{"name": "device_servo_trq_feedback", "channel": 3},
{"name": "device_servo_trq_feedback", "channel": 4},
{"name": "device_servo_trq_feedback", "channel": 5},
]
title = [
'time', 'trq-1', 'trq-2', 'trq-3', 'trq-4', 'trq-5', 'trq-6', 'trq-max-1', 'trq-max-2', 'trq-max-3', 'trq-max-4',
'trq-max-5', 'trq-max-6'
]
def check_files(data_dirs, data_files, w2t):
if len(data_dirs) != 0 or len(data_files) != 2:
w2t('初始路径下不允许有文件夹,且初始路径下只能存在如下文件,确认后重新运行!\n1. target.zip\n2. configs.xlsx', 0, 10, 'red', tab_name)
_files = [data_files[0].split('\\')[-1], data_files[1].split('\\')[-1]]
_files.sort()
if _files != ['configs.xlsx', 'target.zip']:
w2t('初始路径下只能存在如下文件,确认后重新运行!\n1. target.zip\n2. configs.xlsx', 0, 10, 'red', tab_name)
data_files.sort()
return data_files
def run_rl(path, config_file, data_all, hr, md, w2t):
# 1. 关闭诊断曲线,触发软急停,并解除,目的是让可能正在运行着的机器停下来,切手动模式并下电
clibs.execution('diagnosis.open', hr, w2t, tab_name, open=True, display_open=True)
clibs.execution('diagnosis.set_params', hr, w2t, tab_name, display_pdo_params=display_pdo_params)
md.trigger_estop()
md.reset_estop()
md.write_act(False)
sleep(1) # 让曲线彻底关闭
# 2. reload工程后pp2main并且自动模式和上电
prj_path = 'target/_build/target.prj'
clibs.execution('overview.reload', hr, w2t, tab_name, prj_path=prj_path, tasks=['current'])
clibs.execution('rl_task.pp_to_main', hr, w2t, tab_name, tasks=['current'])
clibs.execution('state.switch_auto', hr, w2t, tab_name)
clibs.execution('state.switch_motor_on', hr, w2t, tab_name)
# 3. 开始运行程序
clibs.execution('rl_task.run', hr, w2t, tab_name, tasks=['current'])
_t_start = time()
while True:
if md.read_ready_to_go() == 1:
md.write_act(True)
break
else:
if (time() - _t_start) // 20 > 1:
w2t("20s内未收到机器人的运行信号需要确认RL程序编写正确并正常执行...", 0, 111, 'red', tab_name)
else:
sleep(1)
# 4. 获取初始数据,周期时间,首次的各轴平均电流值,打开诊断曲线,并执行采集
sleep(20) # 初始化 scenario time 为 0
_t_start = time()
while True:
scenario_time = md.read_scenario_time()
if float(scenario_time) > 1:
w2t(f"场景的周期时间:{scenario_time}s", 0, 0, 'green', tab_name)
break
else:
if (time() - _t_start) // 60 > 3:
w2t(f"未收到场景的周期时间需要确认RL程序编写正确并正常执行...", 0, 111, 'red', tab_name)
else:
sleep(5)
sleep(1) # 一定要延迟一秒再读一次scenario time寄存器因为一开始读取的数值不准确
scenario_time = float(md.read_scenario_time())
sleep(scenario_time*0.2)
# 6. 准备初始数据,关闭诊断曲线,保留数据并处理输出
with open(f'{path}\\results.csv', mode='a+', newline='') as f_csv:
csv_writer = writer(f_csv)
csv_writer.writerow(title)
_wb = load_workbook(config_file, read_only=True)
_ws = _wb['Target']
wait_time = float(_ws.cell(row=2, column=10).value)
rcs = []
for i in range(6):
rcs.append(float(_ws.cell(row=6, column=i + 2).value))
get_durable_data(path, data_all, scenario_time, wait_time, rcs, hr, md, w2t)
# 7. 继续运行
while True:
# 固定间隔,更新一次数据,打开曲线,获取周期内电流,关闭曲线
sleep(wait_time+scenario_time+7)
# 保留数据并处理输出
get_durable_data(path, data_all, scenario_time, wait_time, rcs, hr, md, w2t)
def get_durable_data(path, data, scenario_time, wait_time, rcs, hr, md, w2t):
_data_list = []
_c_msg = hr.c_msg.copy()
for _msg in _c_msg:
if 'diagnosis.result' in _msg:
_data_list.insert(0, loads(_msg))
else:
hr.c_msg_xs.clear()
if len(hr.c_msg) > 270:
del hr.c_msg[270:]
# with open(f'{path}\\log.txt', 'w', encoding='utf-8') as f_obj:
# for _ in _data_list:
# f_obj.write(f"{_}\n")
_d2d_trq = {0: [], 1: [], 2: [], 3: [], 4: [], 5: []}
for line in _data_list:
for item in line['data']:
for i in range(6):
item['value'].reverse()
if item.get('channel', None) == i and item.get('name', None) == 'device_servo_trq_feedback':
_d2d_trq[i].extend(item['value'])
if len(_d2d_trq[0]) / 1000 > scenario_time + 1:
this_time = strftime("%Y-%m-%d %H:%M:%S", localtime(time()))
next_time = strftime("%Y-%m-%d %H:%M:%S", localtime(time()+wait_time+10+scenario_time)).split()[-1]
_df = DataFrame(_d2d_trq)
_flg = 0
_res = []
for i in range(6):
def overmax_data(df, index, number, flag):
if number > 100:
md.trigger_estop()
hr.durable_quit = 1
df.to_excel(f'{path}\\{this_time}.xlsx')
w2t(f"[{this_time}] {flag}-axis-{index} 数据过大错误,需要检查确定。", 0, 10, 'red', tab_name)
try:
_ = sqrt(_df[i].apply(lambda x: power((rcs[i]*x/1000), 2)).sum()/len(_df[i]))
except:
md.trigger_estop()
_df.to_excel(path+"\\err_data.xlsx")
w2t(f"{i}calculate error", 0, 11, 'red', tab_name)
if not _flg:
del data[0]['time'][0]
data[0]['time'].append(this_time.split()[-1])
del data[1]['time'][0]
data[1]['time'].append(this_time.split()[-1])
_res.append(this_time)
_flg = 1
del data[0][f"axis{i + 1}"][0]
overmax_data(_df, i, _, 'trq')
data[0][f"axis{i + 1}"].append(_)
_res.append(_)
_ = rcs[i] * _df[i].abs().max() / 1000
overmax_data(_df, i, _, 'trq-max')
del data[1][f"axis{i + 1}"][0]
data[1][f"axis{i + 1}"].append(_)
_res.append(_)
_df_1 = DataFrame(data[0])
_df_2 = DataFrame(data[1])
with open(f'{path}\\results.csv', mode='a+', newline='') as f_csv:
def change_order(res):
_time = res[0:1]
_trq = []
_trq_max = []
for _item in res[1::2]:
_trq.append(_item)
for _item in res[2::2]:
_trq_max.append(_item)
return _time + _trq + _trq_max
csv_writer = writer(f_csv)
csv_writer.writerow(change_order(_res))
while True:
if not hr.durable_lock:
hr.durable_lock = 1
_df_1.to_excel(clibs.durable_data_current_xlsx, index=False)
_df_2.to_excel(clibs.durable_data_current_max_xlsx, index=False)
hr.durable_lock = 0
break
else:
sleep(1)
global count
count += 1
w2t(f"[{this_time}] 当前次数:{count:09d} | 预计下次数据更新时间:{next_time}", 0, 0, '#008B8B', tab_name)
break
else:
md.trigger_estop()
with open(f'{path}\\device_servo_trq_feedback_0.txt', 'w', encoding='utf-8') as f_obj:
for _ in _d2d_trq[0]:
f_obj.write(f"{_}\n")
w2t("采集的数据时间长度不够,需要确认。", 0, 10, 'red', tab_name)
def main(path, hr, md, w2t):
data_all = [clibs.durable_data_current, clibs.durable_data_current_max]
data_dirs, data_files = clibs.traversal_files(path, w2t)
config_file, prj_file = check_files(data_dirs, data_files, w2t)
clibs.prj_to_xcore(prj_file)
run_rl(path, config_file, data_all, hr, md, w2t)
if __name__ == '__main__':
main(*argv[1:])

2
dial_gauge/README.md Normal file
View File

@ -0,0 +1,2 @@
## 千分表数据自动采集

6
dial_gauge/conf.ini Normal file
View File

@ -0,0 +1,6 @@
[md_rtu]
port = COM10
[md_tcp]
addr = 192.168.0.160
port = 502

99
dial_gauge/gauge.py Normal file
View File

@ -0,0 +1,99 @@
from configparser import ConfigParser
from pymodbus.client import ModbusSerialClient, ModbusTcpClient
from time import sleep, strftime, localtime, time
from openpyxl import load_workbook, Workbook
from os import rename
# import pymodbus
# pymodbus.pymodbus_apply_logging_config("DEBUG")
def initializations():
try:
now = strftime("%Y%m%d%H%M%S", localtime(time()))
rename('results.xlsx', f'results_{now}.xlsx')
except FileNotFoundError:
pass
wb = Workbook()
ws = wb.active
ws.title = 'results'
ws['A1'].value = '时间'
ws['B1'].value = '次数'
ws['C1'].value = '结果'
wb.save('results.xlsx')
wb.close()
def do_connections():
configs = ConfigParser()
configs.read('conf.ini')
# ================ 配置Modbus TCP客户端 ================
tcp_host = configs.get('md_tcp', 'addr')
tcp_port = configs.getint('md_tcp', 'port')
client_tcp = ModbusTcpClient(tcp_host, tcp_port)
if client_tcp.connect():
print(f"Modbus TCP已连接到{tcp_host}:{tcp_port}...")
else:
raise Exception(f"Modbus TCP无法连接到{tcp_host}:{tcp_port}...")
# ================ 配置Modbus RTU客户端 ================
rtu_port = configs.get('md_rtu', 'port')
client_rtu = ModbusSerialClient(
method='rtu',
port=rtu_port, # 根据实际情况调整端口Windows上可能是 'COM3'
baudrate=38400, # 根据协议设定波特率为38400
timeout=1,
parity='N', # 无奇偶校验
stopbits=2, # 2个停止位
bytesize=8 # 8个数据位
)
if client_rtu.connect():
print(f"Modbus RTU已连接到系统{rtu_port}端口...")
else:
raise Exception(f"Modbus RTU无法连接到系统{rtu_port}端口...")
return client_tcp, client_rtu
def get_gauge_data(client_tcp, client_rtu):
count = 2
client_tcp.write_register(41000, 1) # 将 act 信号置为 True
# ================ 功能实现 ================
while True:
while True:
res_tcp = client_tcp.read_holding_registers(41001, 1) # 获取 time_to_go 的值
sleep(0.5)
if not res_tcp.registers[0]:
continue
else:
break
try:
res_rtu = client_rtu.read_holding_registers(0x0000, 2, slave=1)
plus_or_minus = 1 if res_rtu.registers[0] == 0 else -1
result = res_rtu.registers[1] * plus_or_minus / 10000
now = strftime("%Y-%m-%d %H:%M:%S", localtime(time()))
wb = load_workbook('results.xlsx')
ws = wb['results']
ws[f'A{count}'].value = now
ws[f'B{count}'].value = count-1
ws[f'C{count}'].value = float(result)
wb.save('results.xlsx')
wb.close()
count += 1
except:
client_tcp.write_register(41000, 0) # 将 act 信号置为 False
sleep(2) #
def main():
initializations()
client_tcp, client_rtu = do_connections()
get_gauge_data(client_tcp, client_rtu)
if __name__ == '__main__':
main()

43
dial_gauge/gauge.xml Normal file
View File

@ -0,0 +1,43 @@
<?xml version="1.0" encoding="UTF-8"?>
<m>
<l>
<c name="addr" type="2" value="41000"/>
<c name="addr_1st" type="2" value="0"/>
<c name="addr_2nd" type="2" value="0"/>
<c name="bit_bias" type="2" value="0"/>
<c name="byte_bias" type="4" value="0"/>
<c name="description" type="10" value="pc to robot"/>
<c name="dev_name" type="10" value="autotest"/>
<c name="dev_type" type="10" value="MODBUS"/>
<c name="end_addr" type="2" value="41000"/>
<c name="function" type="10" value=""/>
<c name="len" type="2" value="1"/>
<c name="name" type="10" value="act"/>
<c name="retain" type="1" value="false"/>
<c name="rw" type="10" value="rd"/>
<c name="type" type="10" value="bool"/>
<c name="value"/>
<c name="value_single" type="10" value=""/>
<c name="bias" type="2" value="0"/>
</l>
<l>
<c name="addr" type="2" value="41001"/>
<c name="addr_1st" type="2" value="0"/>
<c name="addr_2nd" type="2" value="0"/>
<c name="bit_bias" type="2" value="0"/>
<c name="byte_bias" type="4" value="0"/>
<c name="description" type="10" value="robot to pc"/>
<c name="dev_name" type="10" value="autotest"/>
<c name="dev_type" type="10" value="MODBUS"/>
<c name="end_addr" type="2" value="41001"/>
<c name="function" type="10" value=""/>
<c name="len" type="2" value="1"/>
<c name="name" type="10" value="time_to_go"/>
<c name="retain" type="1" value="false"/>
<c name="rw" type="10" value="rdwr"/>
<c name="type" type="10" value="bool"/>
<c name="value"/>
<c name="value_single" type="10" value=""/>
<c name="bias" type="2" value="0"/>
</l>
</m>

Binary file not shown.