This repository has been archived on 2025-02-25. You can view files and clone it, but cannot push or open issues or pull requests.
gitea 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

460 lines
20 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

from json import load, dumps
from socket import socket, setdefaulttimeout, AF_INET, SOCK_STREAM
from threading import Thread
import selectors
from time import time, sleep
from os.path import dirname
from binascii import b2a_hex, a2b_hex
MAX_FRAME_SIZE = 1024
setdefaulttimeout(2)
current_path = dirname(__file__)
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 = 'Automatic Test'
self.pkg_size = 0
self.broke = 0
self.half = 0
self.half_length = 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(f"{current_path}/../../assets/templates/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(('192.168.0.160', 5050))
# self.c.connect(('192.168.84.129', 5050))
self.c.setblocking(False)
self.c_xs = socket(AF_INET, SOCK_STREAM)
self.c_xs.connect(('192.168.0.160', 6666))
# self.c_xs.connect(('192.168.84.129', 6666))
self.c_xs.setblocking(False)
self.w2t("Connection success", 0, 0, 'green', tab_name=self.tab_name)
with open(f"{current_path}/../../assets/templates/heartbeat", "w", encoding='utf-8') as f_hb:
f_hb.write('1')
# print(f"打开诊断:")
# self.execution('diagnosis.open', open=True, display_open=True)
# display_pdo_params = []
# print(f"执行采样")
# self.execution('diagnosis.set_params', display_pdo_params=display_pdo_params)
# print(f"关闭诊断")
# self.execution('diagnosis.open', open=False, display_open=False)
except Exception as Err:
self.w2t("Connection failed...", 0, 0, 'red', tab_name=self.tab_name)
with open(f"{current_path}/../../assets/templates/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("head check 数据有误,需要确认")
self.w2t("", 0, 1, 'red', 'Automatic Test')
else:
self.half_length = len(data)-index
if self.half_length == 1:
self.half = a2b_hex(str(hex(data[-1]))[2:].rjust(1, '0'))
else:
self.half = data[index:]
print(f"in head check half data: {self.half}")
print(f"in head check length: {self.half_length}")
print(f"in head check data: {data}")
self.broke = 100
index += MAX_FRAME_SIZE
return index, 0, 0
# except Exception as Err:
# print(data)
# print(f"Err = {Err}")
# print("无法读取数据,需要确认")
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=' ')
with open(f"{current_path}/../../assets/templates/heartbeat", "w", encoding='utf-8') as f_hb:
f_hb.write(_flag)
if _flag == '0':
self.w2t(f"心跳丢失,连接失败,重新连接中...", 0, 0, 'red', tab_name=self.tab_name)
sleep(2)
print(len(self.c_msg), end=' ')
# with open(f"{current_path}/../../assets/templates/c_msg.log", "w", encoding='utf-8') as f:
# for msg in self.c_msg:
# f.write(msg + '\n')
def msg_storage(self, response, flag=0):
messages = self.c_msg if flag == 0 else self.c_msg_xs
if len(messages) < 20000:
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"half data: {self.half}")
_half_1 = self.half
if self.half_length == 7:
_half_2 = a2b_hex(str(hex(data[0]))[2:].rjust(1, '0'))
else:
_half_2 = data[:8-self.half_length]
_full = _half_1 + _half_2
print(f"full data: {_full}")
_frame_size = _full[:2]
_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)
print("数据有误,需要确认")
self.w2t("", 0, 1, 'red', 'Automatic Test')
self.pkg_size = _pkg_size
_index = 8 - self.half_length
else:
_index = 0
while _index < len(data):
# flag 为 0则说明是一次新的请求对应的一次新的相应也就是需要首次解包
if self.flag == 0:
if self.broke == 100:
self.broke = 0
else:
_index, _frame_size, self.pkg_size = self.header_check(_index, data)
if _index > MAX_FRAME_SIZE:
break
# 详见解包原理数据.txtself.pkg_size 永远是除了当前data之外剩余未处理的数据大小
print(f"INIT pkg size = {self.pkg_size}")
if self.pkg_size <= len(data) - _index:
# 说明剩余部分的数据就在当前data内没有被分割
self.response = data[_index:_index+self.pkg_size].decode()
self.msg_storage(flag=0, response=self.response)
_index += self.pkg_size
print(f"in flag=0 if data = {data}")
print(f"in flag=0 if index = {_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
elif self.pkg_size > len(data) - _index:
# 执行到这里说明该data是首包且有有分包的情况发生了也就是该响应数据量稍微比较大
# 分散在了相邻的两个及以上的data中需要flag=1的处理
self.flag = 1
self.response = data[_index:].decode()
self.leftover = _frame_size - 6 - (len(data) - _index) # 这里的 _index 不一定是 0
self.pkg_size -= (len(data) - _index) # 详见解包原理数据.txtself.pkg_size
print(f"in flag=0 else data = {data}")
print(f"in flag=0 else index = {_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.leftover = int.from_bytes(int(self.half).to_bytes(1, byteorder='big') + data[0:1], byteorder='big')
_index = 1
self.broke = 0
print(f"broke 1 leftover: {self.leftover}")
if self.broke == 2:
self.leftover = int.from_bytes(data[:2], byteorder='big')
print(f"broke 2 leftover: {self.leftover}")
_index = 2
self.broke = 0
while self.pkg_size > 0:
print(f"flag = 1 INIT _index = {_index}")
if _index + self.leftover <= len(data):
self.response += data[_index:_index + self.leftover].decode()
self.pkg_size -= self.leftover
if self.pkg_size == 0:
self.msg_storage(flag=0, response=self.response)
_index += self.leftover
print(f"break _index = {_index}")
print(data)
self.flag = 0
self.response = ''
self.leftover = 0
self.pkg_size = 0
break
_index += self.leftover
if _index+2 <= len(data):
self.leftover = int.from_bytes(data[_index:_index+2], byteorder='big')
_index += 2
else:
self.leftover = 4096
if _index == len(data) -1:
self.broke = 1
self.half = data[-1]
print(f"half = {self.half}")
print('+++++++++++++++++++++')
_index += MAX_FRAME_SIZE
elif _index == len(data):
_index += MAX_FRAME_SIZE
self.broke = 2
print(f"in if data = {data}")
print(f"in if index = {_index}")
print(f"in if pkg size = {self.pkg_size}")
print(f"in if leftover = {self.leftover}")
if self.leftover > 1024:
print('============================')
else:
self.response += data[_index:].decode()
self.leftover -= (len(data) - _index)
self.pkg_size -= (len(data) - _index)
# if self.pkg_size == 0:
# self.msg_storage(flag=0, response=self.response)
# _index += MAX_FRAME_SIZE
# self.flag = 0
# self.response = ''
# self.leftover = 0
# break
_index += MAX_FRAME_SIZE
print(f"in else data = {data}")
print(f"in else index = {_index}")
print(f"in else pkg size = {self.pkg_size}")
print(f"in else leftover = {self.leftover}")
if self.leftover > 1024:
print('============================')
break
else:
self.msg_storage(flag=0, response=self.response)
self.flag = 0
self.response = ''
self.leftover = 0
_index -= 2
# _index = 0
# while _index < len(data):
# if self.flag == 0:
# _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)
# _index += _pkg_size
# self.flag = 0
# self.response = ''
# self.leftover = 0
# elif _pkg_size > len(data) - _index:
# # 说有有分包的情况发生了需要flag=1的处理
# self.flag = 1
# self.response = data[_index:].decode()
# self.leftover = _frame_size - 6 - (len(data) - _index) # 其实就是常量 2其中 6 就是六个字节的包头
# break
#
# elif self.flag == 1:
# # 处理完之后将flag重置为0
# _index = self.leftover
# self.response += data[:_index].decode()
# _index += 2
#
# _frame_size = int.from_bytes(data[_index - 2:_index], byteorder='big')
# if _frame_size == 0:
# self.msg_storage(flag=0, response=self.response)
# self.flag = 0
# self.response = ''
# self.leftover = 0
# break
#
# if _frame_size == MAX_FRAME_SIZE:
# self.leftover = MAX_FRAME_SIZE - (len(data) - _index)
# self.response += data[_index:].decode()
# break
# 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.flag = 0
# self.response = ''
# self.leftover = 0
# break
# else:
# self.response += data[_index:].decode()
# self.leftover = _index + _frame_size - MAX_FRAME_SIZE
# break
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):
messages = self.c_msg if flag == 0 else self.c_msg_xs
for i in range(3):
for msg in messages:
if msg_id is None:
self.w2t("未能成功获取到 message id...", 0, 10, 'red', tab_name=self.tab_name)
if msg_id in msg:
return msg
sleep(1)
else:
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):
data = conn.recv(MAX_FRAME_SIZE)
if data:
print(data)
self.get_response(data)
else:
print('closing', sock)
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)
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)
else:
print('closing', sock)
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)
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'{current_path}/../../assets/templates/{command}.json', encoding='utf-8', mode='r') as f_json:
req = load(f_json)
except:
print(f"暂不支持 {command} 功能,或确认该功能存在...")
return 'NOT SUPPORT'
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 _:
pass
req['id'] = self.gen_id(command)
print(f"req = {req}")
cmd = dumps(req, separators=(',', ':'))
try:
self.c.send(self.package(cmd))
sleep(1)
except Exception as Err:
self.w2t(f"{cmd}\n请求发送失败...{Err}", 0, 0, 'red', tab_name=self.tab_name)
return req['id']
else: # for xService
pass