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函数,校验数据
    - 完善了调用接口
This commit is contained in:
2024-06-25 21:40:27 +08:00
parent 6604f0ba06
commit a75775c869
8 changed files with 212 additions and 122 deletions

View File

@ -246,24 +246,49 @@ v0.1.6.3(2024/06/18)
> WARNING目前版本的电机电流程序还支持DriverMaster采集的数据处理等明确后将不再支持也即所有的电机电流数据工业+协作),都是用诊断曲线来采集
v0.1.7.0(2024/06/29)
v0.1.7.0(2024/06/19)-未发布
1. [openapi.py] 初步搭建起框架,完成了新老协议的封包/解包/异步采集日志的操作(未充分测试,但基本无问题)
2. [openapi.py] 修改了封包的规则,使之更加明晰,封包操作没有实现分包功能,目前看实际场景用不到
3. [openapi.py] 定义 MAX_FRAME_SIZE 常量1024替换socket接收以及响应数据处理相关部分
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界面元素包括如下并完成了功能框架的搭建
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负载
- 进度条
8. [openapi.py] 增加心跳检测函数,并开启线程执行;取消在该文件中生成实例
9. [aio.py] 完成detect_network并在main函数开启线程
10. 将templates文件夹移动到assets内
11. [openapi.py] 建联部分做容错逻辑,并将读写文件做自适应处理
12. [aio.py] 将读写文件做自适应处理引入openapi模块并生成实例做心跳检测将socket超时时间修改为3s
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

View File

@ -1 +1 @@
0
1

1
aio/code/__init__.py Normal file
View File

@ -0,0 +1 @@
__all__ = ['automatic_test', 'data_process']

View File

@ -1,3 +1,4 @@
import sys
import tkinter
from os.path import exists, dirname
from os import getcwd
@ -7,12 +8,10 @@ import customtkinter
from time import time, strftime, localtime, sleep
from urllib.request import urlopen
from socket import setdefaulttimeout
import data_process.brake as brake
import data_process.current as current
import data_process.iso as iso
import data_process.wavelogger as wavelogger
import automatic_test.openapi as openapi
from data_process import *
from automatic_test import *
current_path = dirname(__file__)
customtkinter.set_appearance_mode("System") # Modes: "System" (standard), "Dark", "Light"
customtkinter.set_default_color_theme("blue") # Themes: "blue" (standard), "green", "dark-blue"
customtkinter.set_widget_scaling(1.1) # widget dimensions and text size
@ -73,6 +72,7 @@ class App(customtkinter.CTk):
super().__init__()
self.my_font = customtkinter.CTkFont(family="Consolas", size=16, weight="bold")
self.w_param = 84
self.hr = None
# =====================================================================
# configure window
self.title("AIO - All in one automatic toolbox")
@ -142,7 +142,7 @@ class App(customtkinter.CTk):
# For data process tab END =====================================================================
# For automatic test tab START =====================================================================
# create buttons
self.seg_button = customtkinter.CTkSegmentedButton(self.tabview.tab('Automatic Test'), font=self.my_font, command=lambda value='机器状态': self.thread_it(self.segmented_button_callback()))
self.seg_button = customtkinter.CTkSegmentedButton(self.tabview.tab('Automatic Test'), font=self.my_font, command=lambda value='机器状态': self.thread_it(self.segmented_button_callback))
self.seg_button.grid(row=1, column=2, columnspan=12, padx=(20, 10), pady=(10, 10), sticky="ew")
self.seg_button.configure(values=["无效功能", "触发急停", "停止运动", "继续运动", "零点位姿", "机器状态", "告警信息"])
self.seg_button.set("无效功能")
@ -150,7 +150,7 @@ class App(customtkinter.CTk):
# create progress bar
self.progressbar = customtkinter.CTkProgressBar(self.tabview.tab('Automatic Test'))
self.progressbar.grid(row=5, column=1, padx=5, pady=5, sticky="ew")
self.progressbar.configure(mode="determinnate")
self.progressbar.configure(mode="determinnate", width=10)
self.progressbar.start()
# create widgits_at
for widgit in widgits_at:
@ -189,44 +189,70 @@ class App(customtkinter.CTk):
tkinter.messagebox.showwarning(title="版本更新", message="连接服务器失败,无法确认当前是否是最新版本......")
# functions below ↓ ----------------------------------------------------------------------------------------
def segmented_button_callback(self):
value = self.seg_button.get()
print(f"segment button is triggered: {value}")
match value:
case '触发急停':
pass
case '停止运动':
pass
case '继续运动':
pass
case '零点位姿':
pass
case '机器状态':
pass
case '告警信息':
pass
def detect_network(self):
current_path = dirname(__file__)
while True:
with open(f'{current_path}/../assets/templates/heartbeat', 'r', encoding='utf-8') as f_h:
pb_color = 'green' if f_h.read().strip() == '1' else 'red'
self.progressbar.configure(progress_color=pb_color)
sleep(3)
def thread_it(self, func, *args):
""" 将函数打包进线程 """
self.myThread = Thread(target=func, args=args)
self.myThread.daemon = True # 主线程退出就直接让子线程跟随退出,不论是否运行完成。
self.myThread.start()
def segmented_button_callback(self):
value = self.seg_button.get()
self.textbox.configure(state='normal')
match value:
case '触发急停':
self.textbox.delete(index1='1.0', index2='end')
self.write2textbox(f"segment button is triggered: {value}")
case '停止运动':
self.textbox.delete(index1='1.0', index2='end')
self.write2textbox(f"segment button is triggered: {value}")
case '继续运动':
self.textbox.delete(index1='1.0', index2='end')
self.write2textbox(f"segment button is triggered: {value}")
case '零点位姿':
self.textbox.delete(index1='1.0', index2='end')
self.write2textbox(f"segment button is triggered: {value}")
case '机器状态':
self.textbox.configure(state='normal')
self.textbox.delete(index1='1.0', index2='end')
with open(f'{current_path}/../assets/templates/heartbeat', 'r', encoding='utf-8') as f_h:
connection_state = f_h.read().strip()
if connection_state == '0':
self.write2textbox("无法连接机器人检查是否已经使用Robot Assist软件连接机器重试中...")
else:
do_brake.main(self.hr, 'get_state', self.write2textbox)
case '告警信息':
self.textbox.delete(index1='1.0', index2='end')
self.write2textbox(f"segment button is triggered: {value}")
self.textbox.configure(state='disable')
def detect_network(self):
with open(f"{current_path}/../assets/templates/heartbeat", "w", encoding='utf-8') as f_h:
f_h.write('0')
while True:
with open(f'{current_path}/../assets/templates/heartbeat', 'r', encoding='utf-8') as f_h:
pb_color = 'green' if f_h.read().strip() == '1' else 'red'
self.progressbar.configure(progress_color=pb_color)
sleep(3)
def tabview_click(self):
self.initialization()
self.textbox.configure(state='normal')
self.textbox.delete(index1='1.0', index2='end')
self.textbox.configure(state='disabled')
tab_name = self.tabview.get()
if tab_name == 'Data Process':
self.menu_main_dp.set("Start Here!")
elif tab_name == 'Automatic Test':
self.menu_main_at.set("Start Here!")
with open(f"{current_path}/../assets/templates/heartbeat", "r", encoding='utf-8') as f_h:
connection_state = f_h.read().strip()
if connection_state == '0' or self.hr is None:
self.hr = openapi.HmiRequest()
def initialization(self):
tab_name = self.tabview.get()
@ -492,10 +518,6 @@ class App(customtkinter.CTk):
if __name__ == "__main__":
t_hr = Thread(target=openapi.HmiRequest)
t_hr.daemon = True
t_hr.start()
aio = App()
aio.net_detect = Thread(target=aio.detect_network)
aio.net_detect.daemon = True

View File

@ -0,0 +1 @@
__all__ = ['openapi', 'do_brake']

View File

@ -1,6 +1,61 @@
import openapi
import json
from os.path import dirname
from sys import argv
current_path = dirname(__file__)
def validate_resp(_id, response, w2t):
match _id:
case 'DATA ERR':
w2t(f"数据处理错误,需要确认", 0, 4, 'red')
case 'DATA READ ERR':
w2t(f"无法读取数据,需要确认", 0, 3, 'red')
case 'NOT SUPPORT':
w2t(f"不支持的功能,需要确认", 0, 2, 'red')
if not response:
w2t(f"无法获取{id}请求的响应信息", 0, 1, 'red')
def get_state(hr, w2t):
# 获取机器状态
_id = hr.excution('state.get_state')
_msg = hr.get_from_id(_id)
if not _msg:
w2t(f"无法获取{_id}请求的响应信息", 0, 6, 'red')
else:
_response = json.loads(_msg)['data']
validate_resp(_id, _response, w2t)
stat_desc = {'engine': '上电状态', 'operate': '操作模式', 'rc_state': '控制器状态', 'robot_action': '机器人动作', 'safety_mode': '安全模式', 'servo_mode': '伺服工作模式', 'task_space': '工作任务空间'}
for component, state in _response.items():
w2t(f"{stat_desc[component]}: {state}")
_id = hr.excution('device.get_params')
_msg = hr.get_from_id(_id)
if not _msg:
w2t(f"无法获取{_id}请求的响应信息", 0, 6, 'red')
else:
_response = json.loads(_msg)['data']['devices']
validate_resp(_id, _response, w2t)
dev_desc = {0: '伺服版本', 1: '伺服参数', 2: '安全板固件', 3: '控制器', 4: '通讯总线', 5: '解释器', 6: '运动控制', 8: '力控版本', 9: '末端固件', 10: '机型文件', 11: '环境包'}
dev_vers = {}
for device in _response:
dev_vers[device['type']] = device['version']
for i in sorted(dev_desc.keys()):
w2t(f"{dev_desc[i]}: {dev_vers[i]}")
def main(hr, func, w2t):
# func: get_state/
match func:
case 'get_state':
get_state(hr, w2t)
if __name__ == '__main__':
main(*argv[1:])
hr = openapi.HmiRequest()
# 一、设置/检测机器人状态:
# 1. 上电
@ -19,13 +74,3 @@ hr = openapi.HmiRequest()
# 四
# _id = hr.excution("state.get_state")
# print(hr.get_from_id(_id))
# _id = hr.excution('state.set_tp_mode', tp_mode='without')
# print(hr.get_from_id(_id))
_id = hr.excution('device.get_params')
print(hr.get_from_id(_id))
# _id = hr.excution('state.switch_manual')
# print(hr.get_from_id(_id))

View File

@ -3,52 +3,51 @@ import socket
import threading
import selectors
import time
import os
from os.path import dirname
MAX_FRAME_SIZE = 1024
socket.setdefaulttimeout(3)
current_path = dirname(__file__)
class HmiRequest(object):
def __init__(self):
super().__init__()
while True:
try:
self.c = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# self.c.connect(('192.168.0.160', 5050))
self.c.connect(('192.168.84.129', 5050))
self.c.connect(('192.168.0.160', 5050))
# self.c.connect(('192.168.84.129', 5050))
self.c.setblocking(False)
self.c_msg = []
self.c_xs = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# self.c_xs.connect(('192.168.0.160', 6666))
self.c_xs.connect(('192.168.84.129', 6666))
self.c_xs.connect(('192.168.0.160', 6666))
# self.c_xs.connect(('192.168.84.129', 6666))
self.c_xs.setblocking(False)
self.c_msg_xs = []
break
with open(f"{current_path}/../../assets/templates/heartbeat", "w", encoding='utf-8') as f_h:
f_h.write('1')
except Exception as Err:
current_path = os.path.dirname(__file__)
with open(f"{current_path}/../../assets/templates/heartbeat", "w", encoding='utf-8') as f_h:
f_h.write('0')
print("Connection failed, please try to re-connect via switching tabs...")
print("Connection failed, will try again after 2 seconds...")
time.sleep(2)
self.t_heartbeat = threading.Thread(target=self.__heartbeat)
self.t_heartbeat.daemon = True
self.t_heartbeat.start()
self.t_unpackage = threading.Thread(target=self.__unpackage, args=(self.c, ))
self.t_unpackage.daemon = True
self.t_unpackage.start()
self.t_unpackage_xs = threading.Thread(target=self.__unpackage_xs, args=(self.c_xs, ))
self.t_unpackage_xs.daemon = True
self.t_unpackage_xs.start()
self.c_msg = []
self.c_msg_xs = []
self.flag = 0
self.response = ''
self.leftover = 0
self.flag_xs = 0
self.response_xs = ''
def __header_check(self, index, data):
self.t_heartbeat = threading.Thread(target=self.heartbeat)
self.t_heartbeat.daemon = True
self.t_heartbeat.start()
self.t_unpackage = threading.Thread(target=self.unpackage, args=(self.c, ))
self.t_unpackage.daemon = True
self.t_unpackage.start()
self.t_unpackage_xs = threading.Thread(target=self.unpackage_xs, args=(self.c_xs, ))
self.t_unpackage_xs.daemon = True
self.t_unpackage_xs.start()
def header_check(self, index, data):
try:
_frame_size = int.from_bytes(data[index:index+2], byteorder='big')
_pkg_size = int.from_bytes(data[index+2:index+6], byteorder='big')
@ -59,22 +58,21 @@ class HmiRequest(object):
return index+8, _frame_size, _pkg_size
else:
print("数据有误,需要确认")
exit(9)
return 'DATA ERR'
except Exception as Err:
print(f"Err = {Err}")
print("无法读取数据,需要确认")
exit(10)
return 'DATA READ ERR'
def __heartbeat(self):
current_path = os.path.dirname(__file__)
def heartbeat(self):
while True:
_id = self.excution('controller.heart')
_flag = 1 if self.get_from_id(_id) else 0
with open(f"{current_path}/../../assets/templates/heartbeat", "w", encoding='utf-8') as f_h:
f_h.write(str(_flag))
time.sleep(10)
time.sleep(3)
def __msg_storage(self, response, flag=0):
def msg_storage(self, response, flag=0):
messages = self.c_msg if flag == 0 else self.c_msg_xs
if len(messages) < 1000:
messages.insert(0, response)
@ -83,15 +81,15 @@ class HmiRequest(object):
while len(messages) > 1000:
messages.pop()
def __get_response(self, data):
def get_response(self, data):
_index = 0
while _index < len(data):
if self.flag == 0:
_index, _frame_size, _pkg_size = self.__header_check(_index, data)
_index, _frame_size, _pkg_size = self.header_check(_index, data)
if _pkg_size <= len(data) - _index:
# 说明剩余部分的数据正好就是完整的包数据
self.response = data[_index:_index+_pkg_size].decode()
self.__msg_storage(flag=0, response=self.response)
self.msg_storage(flag=0, response=self.response)
_index += _pkg_size
self.flag = 0
self.response = ''
@ -111,7 +109,7 @@ class HmiRequest(object):
_frame_size = int.from_bytes(data[_index - 2:_index], byteorder='big')
if _frame_size == 0:
self.__msg_storage(flag=0, response=self.response)
self.msg_storage(flag=0, response=self.response)
self.flag = 0
self.response = ''
self.leftover = 0
@ -124,7 +122,7 @@ class HmiRequest(object):
else:
if _index+_frame_size <= MAX_FRAME_SIZE:
self.response += data[_index:_index+_frame_size].decode()
self.__msg_storage(flag=0, response=self.response)
self.msg_storage(flag=0, response=self.response)
self.flag = 0
self.response = ''
self.leftover = 0
@ -134,19 +132,19 @@ class HmiRequest(object):
self.leftover = _index + _frame_size - MAX_FRAME_SIZE
break
def __get_response_xs(self, data):
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)
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.msg_storage(flag=1, response=_response)
self.response_xs = _responses[-1]
self.flag_xs = 1
@ -154,7 +152,7 @@ class HmiRequest(object):
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.msg_storage(flag=1, response=_response)
self.response_xs = ''
self.flag_xs = 0
@ -163,7 +161,7 @@ class HmiRequest(object):
for _response in _responses[:-1]:
if not _response:
break
self.__msg_storage(flag=1, response=_response)
self.msg_storage(flag=1, response=_response)
self.response_xs = _responses[-1]
self.flag_xs = 1
@ -176,25 +174,24 @@ class HmiRequest(object):
return msg
time.sleep(1)
else:
# print(f'无法查询到{msg_id}对应的响应')
return None
def __package(self, cmd):
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):
def package_xs(self, cmd):
return f"{json.dumps(cmd, separators=(',', ':'))}\r".encode()
def __unpackage(self, sock):
def unpackage(self, sock):
def to_read(conn):
data = conn.recv(MAX_FRAME_SIZE)
if data:
# print(data)
self.__get_response(data)
self.get_response(data)
else:
print('closing', sock)
sel.unregister(conn)
@ -209,12 +206,12 @@ class HmiRequest(object):
callback = key.data
callback(key.fileobj)
def __unpackage_xs(self, sock):
def unpackage_xs(self, sock):
def to_read(conn):
data = conn.recv(1024) # Should be ready
if data:
# print(data)
self.__get_response_xs(data)
self.get_response_xs(data)
else:
print('closing', sock)
sel.unregister(conn)
@ -229,32 +226,30 @@ class HmiRequest(object):
callback = key.data
callback(key.fileobj)
def __gen_id(self, command):
def gen_id(self, command):
_now = time.time()
_id = f"{command}-{_now}"
return _id
def excution(self, command, flag=0, **kwargs):
current_path = os.path.dirname(__file__)
if flag == 0: # for old protocols
def excution(self, command, flg=0, **kwargs):
if flg == 0: # for old protocols
req = None
try:
with open(f'{current_path}/../../assets/templates/{command}.json', encoding='utf-8', mode='r') as f_json:
req = json.load(f_json)
except:
print(f"暂不支持 {command} 功能,或确认该功能存在...")
exit(1)
return 'NOT SUPPORT'
match command:
case 'state.set_tp_mode':
req['data']['tp_mode'] = kwargs['tp_mode']
case 1:
pass
req['id'] = self.__gen_id(command)
req['id'] = self.gen_id(command)
print(f"req = {req}")
cmd = json.dumps(req, separators=(',', ':'))
self.c.send(self.__package(cmd))
self.c.send(self.package(cmd))
time.sleep(2)
return req['id']
else: # for xService

View File

@ -0,0 +1 @@
__all__ = ['brake', 'current', 'iso', 'wavelogger']