xarm-python-sdk 1.15.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- xarm/__init__.py +2 -0
- xarm/build_backend.py +17 -0
- xarm/core/__init__.py +2 -0
- xarm/core/comm/__init__.py +5 -0
- xarm/core/comm/base.py +303 -0
- xarm/core/comm/serial_port.py +44 -0
- xarm/core/comm/socket_port.py +150 -0
- xarm/core/comm/uxbus_cmd_protocol.py +100 -0
- xarm/core/config/__init__.py +0 -0
- xarm/core/config/x_code.py +1427 -0
- xarm/core/config/x_config.py +553 -0
- xarm/core/utils/__init__.py +3 -0
- xarm/core/utils/convert.py +124 -0
- xarm/core/utils/crc16.py +76 -0
- xarm/core/utils/debug_print.py +21 -0
- xarm/core/utils/log.py +98 -0
- xarm/core/version.py +1 -0
- xarm/core/wrapper/__init__.py +11 -0
- xarm/core/wrapper/uxbus_cmd.py +1457 -0
- xarm/core/wrapper/uxbus_cmd_ser.py +94 -0
- xarm/core/wrapper/uxbus_cmd_tcp.py +305 -0
- xarm/tools/__init__.py +0 -0
- xarm/tools/blockly/__init__.py +1 -0
- xarm/tools/blockly/_blockly_base.py +416 -0
- xarm/tools/blockly/_blockly_handler.py +1338 -0
- xarm/tools/blockly/_blockly_highlight.py +94 -0
- xarm/tools/blockly/_blockly_node.py +61 -0
- xarm/tools/blockly/_blockly_tool.py +480 -0
- xarm/tools/blockly_tool.py +1864 -0
- xarm/tools/gcode.py +90 -0
- xarm/tools/list_ports.py +39 -0
- xarm/tools/modbus_tcp.py +205 -0
- xarm/tools/threads.py +30 -0
- xarm/tools/utils.py +36 -0
- xarm/version.py +1 -0
- xarm/wrapper/__init__.py +1 -0
- xarm/wrapper/studio_api.py +34 -0
- xarm/wrapper/xarm_api.py +4416 -0
- xarm/x3/__init__.py +2 -0
- xarm/x3/base.py +2638 -0
- xarm/x3/base_board.py +198 -0
- xarm/x3/code.py +62 -0
- xarm/x3/decorator.py +104 -0
- xarm/x3/events.py +166 -0
- xarm/x3/ft_sensor.py +264 -0
- xarm/x3/gpio.py +457 -0
- xarm/x3/grammar_async.py +21 -0
- xarm/x3/grammar_coroutine.py +24 -0
- xarm/x3/gripper.py +830 -0
- xarm/x3/modbus_tcp.py +84 -0
- xarm/x3/parse.py +110 -0
- xarm/x3/record.py +216 -0
- xarm/x3/report.py +204 -0
- xarm/x3/robotiq.py +220 -0
- xarm/x3/servo.py +485 -0
- xarm/x3/studio.py +138 -0
- xarm/x3/track.py +424 -0
- xarm/x3/utils.py +43 -0
- xarm/x3/xarm.py +1928 -0
- xarm_python_sdk-1.15.2.dist-info/METADATA +103 -0
- xarm_python_sdk-1.15.2.dist-info/RECORD +63 -0
- xarm_python_sdk-1.15.2.dist-info/WHEEL +4 -0
- xarm_python_sdk-1.15.2.dist-info/licenses/LICENSE +27 -0
xarm/x3/modbus_tcp.py
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# Software License Agreement (BSD License)
|
|
3
|
+
#
|
|
4
|
+
# Copyright (c) 2023, UFACTORY, Inc.
|
|
5
|
+
# All rights reserved.
|
|
6
|
+
#
|
|
7
|
+
# Author: Vinman <vinman.wen@ufactory.cc> <vinman.cub@gmail.com>
|
|
8
|
+
from .base import Base
|
|
9
|
+
from .decorator import xarm_is_connected
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class ModbusTcp(Base):
|
|
13
|
+
def __init__(self):
|
|
14
|
+
super(ModbusTcp, self).__init__()
|
|
15
|
+
|
|
16
|
+
@xarm_is_connected(_type='get')
|
|
17
|
+
def read_coil_bits(self, addr, quantity):
|
|
18
|
+
"""
|
|
19
|
+
func_code: 0x01
|
|
20
|
+
"""
|
|
21
|
+
return self.arm_cmd.read_coil_bits(addr, quantity)
|
|
22
|
+
|
|
23
|
+
@xarm_is_connected(_type='get')
|
|
24
|
+
def read_input_bits(self, addr, quantity):
|
|
25
|
+
"""
|
|
26
|
+
func_code: 0x02
|
|
27
|
+
"""
|
|
28
|
+
return self.arm_cmd.read_input_bits(addr, quantity)
|
|
29
|
+
|
|
30
|
+
@xarm_is_connected(_type='get')
|
|
31
|
+
def read_holding_registers(self, addr, quantity, is_signed=False):
|
|
32
|
+
"""
|
|
33
|
+
func_code: 0x03
|
|
34
|
+
"""
|
|
35
|
+
return self.arm_cmd.read_holding_registers(addr, quantity, is_signed)
|
|
36
|
+
|
|
37
|
+
@xarm_is_connected(_type='get')
|
|
38
|
+
def read_input_registers(self, addr, quantity, is_signed=False):
|
|
39
|
+
"""
|
|
40
|
+
func_code: 0x04
|
|
41
|
+
"""
|
|
42
|
+
return self.arm_cmd.read_input_registers(addr, quantity, is_signed)
|
|
43
|
+
|
|
44
|
+
@xarm_is_connected(_type='set')
|
|
45
|
+
def write_single_coil_bit(self, addr, bit_val):
|
|
46
|
+
"""
|
|
47
|
+
func_code: 0x05
|
|
48
|
+
"""
|
|
49
|
+
return self.arm_cmd.write_single_coil_bit(addr, bit_val)
|
|
50
|
+
|
|
51
|
+
@xarm_is_connected(_type='set')
|
|
52
|
+
def write_single_holding_register(self, addr, reg_val):
|
|
53
|
+
"""
|
|
54
|
+
func_code: 0x06
|
|
55
|
+
"""
|
|
56
|
+
return self.arm_cmd.write_single_holding_register(addr, reg_val)
|
|
57
|
+
|
|
58
|
+
@xarm_is_connected(_type='set')
|
|
59
|
+
def write_multiple_coil_bits(self, addr, bits):
|
|
60
|
+
"""
|
|
61
|
+
func_code: 0x0F
|
|
62
|
+
"""
|
|
63
|
+
return self.arm_cmd.write_multiple_coil_bits(addr, bits)
|
|
64
|
+
|
|
65
|
+
@xarm_is_connected(_type='set')
|
|
66
|
+
def write_multiple_holding_registers(self, addr, regs):
|
|
67
|
+
"""
|
|
68
|
+
func_code: 0x10
|
|
69
|
+
"""
|
|
70
|
+
return self.arm_cmd.write_multiple_holding_registers(addr, regs)
|
|
71
|
+
|
|
72
|
+
@xarm_is_connected(_type='set')
|
|
73
|
+
def mask_write_holding_register(self, addr, and_mask, or_mask):
|
|
74
|
+
"""
|
|
75
|
+
func_code: 0x16
|
|
76
|
+
"""
|
|
77
|
+
return self.arm_cmd.mask_write_holding_register(addr, and_mask, or_mask)
|
|
78
|
+
|
|
79
|
+
@xarm_is_connected(_type='get')
|
|
80
|
+
def write_and_read_holding_registers(self, r_addr, r_quantity, w_addr, w_regs, is_signed=False):
|
|
81
|
+
"""
|
|
82
|
+
func_code: 0x17
|
|
83
|
+
"""
|
|
84
|
+
return self.arm_cmd.write_and_read_holding_registers(r_addr, r_quantity, w_addr, w_regs, is_signed)
|
xarm/x3/parse.py
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# Software License Agreement (MIT License)
|
|
3
|
+
#
|
|
4
|
+
# Copyright (c) 2018, UFACTORY, Inc.
|
|
5
|
+
# All rights reserved.
|
|
6
|
+
#
|
|
7
|
+
# Author: Vinman <vinman.wen@ufactory.cc> <vinman.cub@gmail.com>
|
|
8
|
+
|
|
9
|
+
import re
|
|
10
|
+
|
|
11
|
+
GCODE_PARAM_X = 'X' # TCP-X
|
|
12
|
+
GCODE_PARAM_Y = 'Y' # TCP-Y
|
|
13
|
+
GCODE_PARAM_Z = 'Z' # TCP-Z
|
|
14
|
+
GCODE_PARAM_A = 'A' # TCP-Roll
|
|
15
|
+
GCODE_PARAM_B = 'B' # TCP-Pitch
|
|
16
|
+
GCODE_PARAM_C = 'C' # TCP-Yaw
|
|
17
|
+
GCODE_PARAM_R = 'R' # TCP-Radius
|
|
18
|
+
GCODE_PARAM_I = 'I' # Joint-1
|
|
19
|
+
GCODE_PARAM_J = 'J' # Joint-2
|
|
20
|
+
GCODE_PARAM_K = 'K' # Joint-3
|
|
21
|
+
GCODE_PARAM_L = 'L' # Joint-4
|
|
22
|
+
GCODE_PARAM_M = 'M' # Joint-5
|
|
23
|
+
GCODE_PARAM_N = 'N' # Joint-6
|
|
24
|
+
GCODE_PARAM_O = 'O' # Joint-7
|
|
25
|
+
GCODE_PARAM_F = 'F' # Move-Speed
|
|
26
|
+
GCODE_PARAM_Q = 'Q' # Move-Acc
|
|
27
|
+
GCODE_PARAM_T = 'T' # Move-Time
|
|
28
|
+
GCODE_PARAM_V = 'V' # Value
|
|
29
|
+
GCODE_PARAM_D = 'D' # Addr
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class GcodeParser:
|
|
33
|
+
def __init__(self):
|
|
34
|
+
self._int_val = 0
|
|
35
|
+
self._float_val = 0.0
|
|
36
|
+
|
|
37
|
+
@staticmethod
|
|
38
|
+
def __get_value(string, ch, return_type, default=None):
|
|
39
|
+
pattern = r'{}(\-?\d+\.?\d*)'.format(ch)
|
|
40
|
+
data = re.findall(pattern, string)
|
|
41
|
+
if len(data) > 0:
|
|
42
|
+
return return_type(data[0])
|
|
43
|
+
return default
|
|
44
|
+
|
|
45
|
+
@staticmethod
|
|
46
|
+
def __get_hex_value(string, ch, default=None):
|
|
47
|
+
pattern = r'{}(-?\w{{3,4}})'.format(ch)
|
|
48
|
+
data = re.findall(pattern, string)
|
|
49
|
+
if len(data) > 0:
|
|
50
|
+
return int(data[0], base=16)
|
|
51
|
+
return default
|
|
52
|
+
|
|
53
|
+
def _get_int_value(self, string, ch, default=None):
|
|
54
|
+
return self.__get_value(string, ch, int, default=default)
|
|
55
|
+
|
|
56
|
+
def _get_float_value(self, string, ch, default=None):
|
|
57
|
+
return self.__get_value(string, ch, float, default=default)
|
|
58
|
+
|
|
59
|
+
def get_int_value(self, string, default=None):
|
|
60
|
+
if default is None:
|
|
61
|
+
default = self._int_val
|
|
62
|
+
self._int_val = self._get_int_value(string, GCODE_PARAM_V, default=default)
|
|
63
|
+
return self._int_val
|
|
64
|
+
else:
|
|
65
|
+
return self._get_int_value(string, GCODE_PARAM_V, default=default)
|
|
66
|
+
|
|
67
|
+
def get_float_value(self, string, default=0):
|
|
68
|
+
return self._get_float_value(string, GCODE_PARAM_V, default=default)
|
|
69
|
+
|
|
70
|
+
def get_addr(self, string, default=0):
|
|
71
|
+
return self.__get_hex_value(string, GCODE_PARAM_D, default=default)
|
|
72
|
+
|
|
73
|
+
def get_gcode_cmd_num(self, string, ch):
|
|
74
|
+
return self._get_int_value(string, ch, default=-1)
|
|
75
|
+
|
|
76
|
+
def get_mvvelo(self, string, default=None):
|
|
77
|
+
return self._get_float_value(string, GCODE_PARAM_F, default=default)
|
|
78
|
+
|
|
79
|
+
def get_mvacc(self, string, default=None):
|
|
80
|
+
return self._get_float_value(string, GCODE_PARAM_Q, default=default)
|
|
81
|
+
|
|
82
|
+
def get_mvtime(self, string, default=None):
|
|
83
|
+
return self._get_float_value(string, GCODE_PARAM_T, default=default)
|
|
84
|
+
|
|
85
|
+
def get_mvradius(self, string, default=None):
|
|
86
|
+
return self._get_float_value(string, GCODE_PARAM_R, default=default)
|
|
87
|
+
|
|
88
|
+
def get_id_num(self, string, default=None):
|
|
89
|
+
return self._get_int_value(string, GCODE_PARAM_I, default=default)
|
|
90
|
+
|
|
91
|
+
def get_poses(self, string, default=None):
|
|
92
|
+
pose = [None] * 6
|
|
93
|
+
pose[0] = self._get_float_value(string[2:], GCODE_PARAM_X, default=default)
|
|
94
|
+
pose[1] = self._get_float_value(string[2:], GCODE_PARAM_Y, default=default)
|
|
95
|
+
pose[2] = self._get_float_value(string[2:], GCODE_PARAM_Z, default=default)
|
|
96
|
+
pose[3] = self._get_float_value(string[2:], GCODE_PARAM_A, default=default)
|
|
97
|
+
pose[4] = self._get_float_value(string[2:], GCODE_PARAM_B, default=default)
|
|
98
|
+
pose[5] = self._get_float_value(string[2:], GCODE_PARAM_C, default=default)
|
|
99
|
+
return pose
|
|
100
|
+
|
|
101
|
+
def get_joints(self, string, default=None):
|
|
102
|
+
joints = [None] * 7
|
|
103
|
+
joints[0] = self._get_float_value(string[2:], GCODE_PARAM_I, default=default)
|
|
104
|
+
joints[1] = self._get_float_value(string[2:], GCODE_PARAM_J, default=default)
|
|
105
|
+
joints[2] = self._get_float_value(string[2:], GCODE_PARAM_K, default=default)
|
|
106
|
+
joints[3] = self._get_float_value(string[2:], GCODE_PARAM_L, default=default)
|
|
107
|
+
joints[4] = self._get_float_value(string[2:], GCODE_PARAM_M, default=default)
|
|
108
|
+
joints[5] = self._get_float_value(string[2:], GCODE_PARAM_N, default=default)
|
|
109
|
+
joints[6] = self._get_float_value(string[2:], GCODE_PARAM_O, default=default)
|
|
110
|
+
return joints
|
xarm/x3/record.py
ADDED
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# Software License Agreement (BSD License)
|
|
3
|
+
#
|
|
4
|
+
# Copyright (c) 2019, UFACTORY, Inc.
|
|
5
|
+
# All rights reserved.
|
|
6
|
+
#
|
|
7
|
+
# Author: Vinman <vinman.wen@ufactory.cc> <vinman.cub@gmail.com>
|
|
8
|
+
|
|
9
|
+
import json
|
|
10
|
+
import time
|
|
11
|
+
import uuid
|
|
12
|
+
from urllib import request
|
|
13
|
+
from .code import APIState
|
|
14
|
+
from ..core.config.x_config import XCONF
|
|
15
|
+
from ..core.utils.log import logger
|
|
16
|
+
from .base import Base
|
|
17
|
+
from .decorator import xarm_is_connected
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class Record(Base):
|
|
21
|
+
def __init__(self):
|
|
22
|
+
super(Record, self).__init__()
|
|
23
|
+
|
|
24
|
+
@xarm_is_connected(_type='get')
|
|
25
|
+
def get_trajectories(self, ip=None):
|
|
26
|
+
if ip is None:
|
|
27
|
+
url = 'http://{}:18333/cmd'.format(self._port)
|
|
28
|
+
else:
|
|
29
|
+
url = 'http://{}:18333/cmd'.format(ip)
|
|
30
|
+
try:
|
|
31
|
+
data = {'cmd': 'xarm_list_trajs'}
|
|
32
|
+
req = request.Request(url, headers={'Content-Type': 'application/json'}, data=json.dumps(data).encode('utf-8'))
|
|
33
|
+
res = request.urlopen(req)
|
|
34
|
+
if res.code == 200:
|
|
35
|
+
result = json.loads(res.read().decode('utf-8'))
|
|
36
|
+
return result['res'][0], [{'name': item['name'], 'duration': item['count'] / 100} for item in result['res'][1]]
|
|
37
|
+
else:
|
|
38
|
+
return APIState.API_EXCEPTION, []
|
|
39
|
+
except Exception as e:
|
|
40
|
+
return APIState.API_EXCEPTION, []
|
|
41
|
+
|
|
42
|
+
@xarm_is_connected(_type='set')
|
|
43
|
+
def start_record_trajectory(self):
|
|
44
|
+
ret = self.arm_cmd.set_record_traj(1)
|
|
45
|
+
self.log_api_info('API -> start_record_trajectory -> code={}'.format(ret[0]), code=ret[0])
|
|
46
|
+
return ret[0]
|
|
47
|
+
|
|
48
|
+
@xarm_is_connected(_type='set')
|
|
49
|
+
def stop_record_trajectory(self, filename=None, **kwargs):
|
|
50
|
+
ret = self.arm_cmd.set_record_traj(0)
|
|
51
|
+
if isinstance(filename, str) and filename.strip():
|
|
52
|
+
ret2 = self.save_record_trajectory(filename, wait=True, timeout=10, **kwargs)
|
|
53
|
+
if ret2 != 0:
|
|
54
|
+
return ret2
|
|
55
|
+
self.log_api_info('API -> stop_record_trajectory -> code={}'.format(ret[0]), code=ret[0])
|
|
56
|
+
return ret[0]
|
|
57
|
+
|
|
58
|
+
@xarm_is_connected(_type='set')
|
|
59
|
+
def save_record_trajectory(self, filename, wait=True, timeout=2, **kwargs):
|
|
60
|
+
assert isinstance(filename, str) and filename.strip()
|
|
61
|
+
filename = filename.strip()
|
|
62
|
+
if not filename.endswith('.traj'):
|
|
63
|
+
full_filename = '{}.traj'.format(filename)
|
|
64
|
+
else:
|
|
65
|
+
full_filename = filename
|
|
66
|
+
self.get_trajectory_rw_status()
|
|
67
|
+
feedback_key, studio_wait = self._gen_feedback_key(wait, **kwargs)
|
|
68
|
+
ret = self.arm_cmd.save_traj(full_filename, wait_time=0, feedback_key=feedback_key)
|
|
69
|
+
trans_id = self._get_feedback_transid(feedback_key, studio_wait)
|
|
70
|
+
self.log_api_info('API -> save_record_trajectory -> code={}'.format(ret[0]), code=ret[0])
|
|
71
|
+
ret[0] = self._check_code(ret[0])
|
|
72
|
+
if ret[0] == 0 and wait:
|
|
73
|
+
return self.__wait_save_traj(timeout, trans_id, filename)
|
|
74
|
+
if ret[0] != 0:
|
|
75
|
+
logger.error('Save {} failed, ret={}'.format(filename, ret))
|
|
76
|
+
return ret[0]
|
|
77
|
+
|
|
78
|
+
def __check_traj_status(self, status, filename='unknown'):
|
|
79
|
+
if status == XCONF.TrajState.LOAD_SUCCESS:
|
|
80
|
+
logger.info('Load {} success'.format(filename))
|
|
81
|
+
return 0
|
|
82
|
+
elif status == XCONF.TrajState.LOAD_FAIL:
|
|
83
|
+
logger.error('Load {} failed'.format(filename))
|
|
84
|
+
return APIState.TRAJ_RW_FAILED
|
|
85
|
+
elif status == XCONF.TrajState.SAVE_SUCCESS:
|
|
86
|
+
logger.info('Save {} success'.format(filename))
|
|
87
|
+
return 0
|
|
88
|
+
elif status == XCONF.TrajState.SAVE_FAIL:
|
|
89
|
+
logger.error('Save {} failed'.format(filename))
|
|
90
|
+
return APIState.TRAJ_RW_FAILED
|
|
91
|
+
return -1
|
|
92
|
+
|
|
93
|
+
def __wait_traj_op(self, timeout, trans_id, filename='unknown', op='Load'):
|
|
94
|
+
if self._support_feedback and trans_id > 0:
|
|
95
|
+
code, feedback_code = self._wait_feedback(timeout, trans_id)
|
|
96
|
+
_, status = self.get_trajectory_rw_status()
|
|
97
|
+
if code == 0:
|
|
98
|
+
success_status = XCONF.TrajState.LOAD_SUCCESS if op == 'Load' else XCONF.TrajState.SAVE_SUCCESS
|
|
99
|
+
failure_status = XCONF.TrajState.LOAD_FAIL if op == 'Load' else XCONF.TrajState.SAVE_FAIL
|
|
100
|
+
status = success_status if feedback_code == XCONF.FeedbackCode.SUCCESS else failure_status if feedback_code == XCONF.FeedbackCode.FAILURE else status
|
|
101
|
+
return self.__check_traj_status(status, filename)
|
|
102
|
+
else:
|
|
103
|
+
expired = (time.monotonic() + timeout) if timeout is not None else 0
|
|
104
|
+
idle_cnts = 0
|
|
105
|
+
while timeout is None or time.monotonic() < expired:
|
|
106
|
+
time.sleep(0.1)
|
|
107
|
+
code, status = self.get_trajectory_rw_status()
|
|
108
|
+
if self._check_code(code) == 0:
|
|
109
|
+
if status == XCONF.TrajState.IDLE:
|
|
110
|
+
idle_cnts += 1
|
|
111
|
+
if idle_cnts >= 5:
|
|
112
|
+
logger.info('{} {} failed, idle'.format(op, filename))
|
|
113
|
+
return APIState.TRAJ_RW_FAILED
|
|
114
|
+
else:
|
|
115
|
+
code = self.__check_traj_status(status, filename)
|
|
116
|
+
if code >= 0:
|
|
117
|
+
return code
|
|
118
|
+
logger.warning('{} {} timeout'.format(op, filename))
|
|
119
|
+
return APIState.TRAJ_RW_TOUT
|
|
120
|
+
|
|
121
|
+
def __wait_load_traj(self, timeout, trans_id, filename='unknown'):
|
|
122
|
+
return self.__wait_traj_op(timeout, trans_id, filename, 'Load')
|
|
123
|
+
|
|
124
|
+
def __wait_save_traj(self, timeout, trans_id, filename='unknown'):
|
|
125
|
+
return self.__wait_traj_op(timeout, trans_id, filename, 'Save')
|
|
126
|
+
|
|
127
|
+
def __wait_play_traj(self, timeout, trans_id, times=1):
|
|
128
|
+
if self._support_feedback and trans_id > 0:
|
|
129
|
+
code, feedback_code = self._wait_feedback(timeout, trans_id)
|
|
130
|
+
if feedback_code == XCONF.FeedbackCode.FAILURE:
|
|
131
|
+
code = APIState.TRAJ_PLAYBACK_FAILED
|
|
132
|
+
else:
|
|
133
|
+
start_time = time.monotonic()
|
|
134
|
+
while self.state != 1:
|
|
135
|
+
if self.state in [4]:
|
|
136
|
+
return APIState.STATE_NOT_READY
|
|
137
|
+
if time.monotonic() - start_time > 5:
|
|
138
|
+
return APIState.TRAJ_PLAYBACK_TOUT
|
|
139
|
+
time.sleep(0.1)
|
|
140
|
+
max_count = int((time.monotonic() - start_time) / 0.1)
|
|
141
|
+
max_count = max_count if max_count > 10 else 10
|
|
142
|
+
start_time = time.monotonic()
|
|
143
|
+
while self.mode != 11:
|
|
144
|
+
if self.state == 1:
|
|
145
|
+
start_time = time.monotonic()
|
|
146
|
+
time.sleep(0.1)
|
|
147
|
+
continue
|
|
148
|
+
if self.state in [4]:
|
|
149
|
+
return APIState.STATE_NOT_READY
|
|
150
|
+
if time.monotonic() - start_time > 5:
|
|
151
|
+
return APIState.TRAJ_PLAYBACK_TOUT
|
|
152
|
+
time.sleep(0.1)
|
|
153
|
+
time.sleep(0.1)
|
|
154
|
+
count = 0
|
|
155
|
+
while self.state not in [4]:
|
|
156
|
+
if self.state == 2:
|
|
157
|
+
if times == 1:
|
|
158
|
+
break
|
|
159
|
+
count += 1
|
|
160
|
+
else:
|
|
161
|
+
count = 0
|
|
162
|
+
if count > max_count:
|
|
163
|
+
break
|
|
164
|
+
time.sleep(0.1)
|
|
165
|
+
code = 0 if self.state != 4 else APIState.STATE_NOT_READY
|
|
166
|
+
|
|
167
|
+
if self.state not in [4]:
|
|
168
|
+
self.set_mode(0)
|
|
169
|
+
self.set_state(0)
|
|
170
|
+
self._sync()
|
|
171
|
+
return code
|
|
172
|
+
|
|
173
|
+
@xarm_is_connected(_type='set')
|
|
174
|
+
def load_trajectory(self, filename, wait=True, timeout=None, **kwargs):
|
|
175
|
+
assert isinstance(filename, str) and filename.strip()
|
|
176
|
+
filename = filename.strip()
|
|
177
|
+
if not filename.endswith('.traj'):
|
|
178
|
+
full_filename = '{}.traj'.format(filename)
|
|
179
|
+
else:
|
|
180
|
+
full_filename = filename
|
|
181
|
+
self.get_trajectory_rw_status()
|
|
182
|
+
feedback_key, studio_wait = self._gen_feedback_key(wait, **kwargs)
|
|
183
|
+
ret = self.arm_cmd.load_traj(full_filename, wait_time=0, feedback_key=feedback_key)
|
|
184
|
+
trans_id = self._get_feedback_transid(feedback_key, studio_wait)
|
|
185
|
+
self.log_api_info('API -> load_trajectory -> code={}'.format(ret[0]), code=ret[0])
|
|
186
|
+
if ret[0] == 0 and wait:
|
|
187
|
+
return self.__wait_load_traj(timeout, trans_id, filename)
|
|
188
|
+
if ret[0] != 0:
|
|
189
|
+
logger.error('Load {} failed, ret={}'.format(filename, ret))
|
|
190
|
+
return ret[0]
|
|
191
|
+
|
|
192
|
+
@xarm_is_connected(_type='set')
|
|
193
|
+
def playback_trajectory(self, times=1, filename=None, wait=False, double_speed=1, **kwargs):
|
|
194
|
+
assert isinstance(times, int)
|
|
195
|
+
times = times if times > 0 else -1
|
|
196
|
+
if isinstance(filename, str) and filename.strip():
|
|
197
|
+
ret = self.load_trajectory(filename, wait=True, timeout=None)
|
|
198
|
+
if ret != 0:
|
|
199
|
+
return ret
|
|
200
|
+
if self.state in [4]:
|
|
201
|
+
return APIState.NOT_READY
|
|
202
|
+
feedback_key, studio_wait = self._gen_feedback_key(wait, **kwargs)
|
|
203
|
+
if self.version_is_ge(1, 2, 11):
|
|
204
|
+
ret = self.arm_cmd.playback_traj(times, double_speed, feedback_key=feedback_key)
|
|
205
|
+
else:
|
|
206
|
+
ret = self.arm_cmd.playback_traj_old(times)
|
|
207
|
+
trans_id = self._get_feedback_transid(feedback_key, studio_wait)
|
|
208
|
+
self.log_api_info('API -> playback_trajectory -> code={}'.format(ret[0]), code=ret[0])
|
|
209
|
+
if ret[0] == 0 and wait:
|
|
210
|
+
return self.__wait_play_traj(None, trans_id, times)
|
|
211
|
+
return ret[0]
|
|
212
|
+
|
|
213
|
+
@xarm_is_connected(_type='get')
|
|
214
|
+
def get_trajectory_rw_status(self):
|
|
215
|
+
ret = self.arm_cmd.get_traj_rw_status()
|
|
216
|
+
return ret[0], ret[1]
|
xarm/x3/report.py
ADDED
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
from xarm.core.utils import convert
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class ReportHandler(object):
|
|
5
|
+
def __init__(self, report_type):
|
|
6
|
+
self.buffer = b''
|
|
7
|
+
self.report_size = 0
|
|
8
|
+
self.report_type = report_type
|
|
9
|
+
if self.report_type == 'devlop':
|
|
10
|
+
self.parse_handler = self._parse_report_tcp_develop_data
|
|
11
|
+
elif self.report_type == 'normal':
|
|
12
|
+
self.parse_handler = self._parse_report_tcp_normal_data
|
|
13
|
+
elif self.report_type == 'rich':
|
|
14
|
+
self.parse_handler = self._parse_report_tcp_rich_data
|
|
15
|
+
else:
|
|
16
|
+
self.parse_handler = None
|
|
17
|
+
self.source_data = b''
|
|
18
|
+
self.parse_dict = {}
|
|
19
|
+
|
|
20
|
+
def reset(self):
|
|
21
|
+
self.buffer = b''
|
|
22
|
+
self.report_size = 0
|
|
23
|
+
|
|
24
|
+
def process_report_data(self, recv_data):
|
|
25
|
+
if recv_data == -1:
|
|
26
|
+
return
|
|
27
|
+
self.buffer += recv_data
|
|
28
|
+
if len(self.buffer) < 4:
|
|
29
|
+
return
|
|
30
|
+
if self.report_size == 0:
|
|
31
|
+
self.report_size = convert.bytes_to_u32(self.buffer[:4])
|
|
32
|
+
if len(self.buffer) < self.report_size:
|
|
33
|
+
return
|
|
34
|
+
if self.report_type == 'rich' and self.report_size == 233 and len(self.buffer) >= 245:
|
|
35
|
+
# 兼容某几版固件上报的数据的数据长度和实际数据的长度不一致
|
|
36
|
+
try:
|
|
37
|
+
if len(self.buffer) >= 249:
|
|
38
|
+
if convert.bytes_to_u32(self.buffer[245:249]) != self.report_size:
|
|
39
|
+
if convert.bytes_to_u32(self.buffer[233:237]) == self.report_size:
|
|
40
|
+
data = self.buffer[:self.report_size]
|
|
41
|
+
self.buffer = self.buffer[self.report_size:]
|
|
42
|
+
else:
|
|
43
|
+
self.reset()
|
|
44
|
+
# TODO reconnect
|
|
45
|
+
return -1
|
|
46
|
+
else:
|
|
47
|
+
data = self.buffer[:245]
|
|
48
|
+
self.buffer = self.buffer[245:]
|
|
49
|
+
else:
|
|
50
|
+
if convert.bytes_to_u32(self.buffer[233:237]) != self.report_size:
|
|
51
|
+
data = self.buffer[:245]
|
|
52
|
+
self.buffer = self.buffer[245:]
|
|
53
|
+
else:
|
|
54
|
+
data = self.buffer[:self.report_size]
|
|
55
|
+
self.buffer = self.buffer[self.report_size:]
|
|
56
|
+
except:
|
|
57
|
+
self.reset()
|
|
58
|
+
# TODO reconnect
|
|
59
|
+
return -1
|
|
60
|
+
else:
|
|
61
|
+
data = self.buffer[:self.report_size]
|
|
62
|
+
self.buffer = self.buffer[self.report_size:]
|
|
63
|
+
self.source_data = data
|
|
64
|
+
if self.parse_handler:
|
|
65
|
+
return self.parse_handler(data)
|
|
66
|
+
|
|
67
|
+
def __parse_report_common_data(self, rx_data):
|
|
68
|
+
# length = convert.bytes_to_u32(rx_data[0:4])
|
|
69
|
+
length = len(rx_data)
|
|
70
|
+
state, mode = rx_data[4] & 0x0F, rx_data[4] >> 4
|
|
71
|
+
cmd_num = convert.bytes_to_u16(rx_data[5:7])
|
|
72
|
+
angles = convert.bytes_to_fp32s(rx_data[7:7 * 4 + 7], 7)
|
|
73
|
+
pose = convert.bytes_to_fp32s(rx_data[35:6 * 4 + 35], 6)
|
|
74
|
+
torque = convert.bytes_to_fp32s(rx_data[59:7 * 4 + 59], 7)
|
|
75
|
+
self.parse_dict['length'] = length
|
|
76
|
+
self.parse_dict['state'] = state
|
|
77
|
+
self.parse_dict['mode'] = mode
|
|
78
|
+
self.parse_dict['cmd_num'] = cmd_num
|
|
79
|
+
self.parse_dict['angles'] = angles
|
|
80
|
+
self.parse_dict['pose'] = pose
|
|
81
|
+
self.parse_dict['torque'] = torque
|
|
82
|
+
return [length, state, mode, cmd_num, angles, pose, torque]
|
|
83
|
+
|
|
84
|
+
def _parse_report_tcp_develop_data(self, rx_data):
|
|
85
|
+
ret = self.__parse_report_common_data(rx_data)
|
|
86
|
+
return ret
|
|
87
|
+
|
|
88
|
+
def _parse_report_tcp_normal_data(self, rx_data):
|
|
89
|
+
ret = self.__parse_report_common_data(rx_data)
|
|
90
|
+
mtbrake, mtable, error_code, warn_code = rx_data[87:91]
|
|
91
|
+
tcp_offset = convert.bytes_to_fp32s(rx_data[91:6 * 4 + 91], 6)
|
|
92
|
+
tcp_load = convert.bytes_to_fp32s(rx_data[115:4 * 4 + 115], 4)
|
|
93
|
+
collis_sens, teach_sens = rx_data[131:133]
|
|
94
|
+
gravity_direction = convert.bytes_to_fp32s(rx_data[133:3 * 4 + 133], 3)
|
|
95
|
+
mtbrake = [mtbrake & 0x01, mtbrake >> 1 & 0x01, mtbrake >> 2 & 0x01, mtbrake >> 3 & 0x01,
|
|
96
|
+
mtbrake >> 4 & 0x01, mtbrake >> 5 & 0x01, mtbrake >> 6 & 0x01, mtbrake >> 7 & 0x01]
|
|
97
|
+
mtable = [mtable & 0x01, mtable >> 1 & 0x01, mtable >> 2 & 0x01, mtable >> 3 & 0x01,
|
|
98
|
+
mtable >> 4 & 0x01, mtable >> 5 & 0x01, mtable >> 6 & 0x01, mtable >> 7 & 0x01]
|
|
99
|
+
self.parse_dict['mtbrake'] = mtbrake
|
|
100
|
+
self.parse_dict['mtable'] = mtable
|
|
101
|
+
self.parse_dict['tcp_offset'] = tcp_offset
|
|
102
|
+
self.parse_dict['tcp_load'] = tcp_load
|
|
103
|
+
self.parse_dict['error_code'] = error_code
|
|
104
|
+
self.parse_dict['warn_code'] = warn_code
|
|
105
|
+
self.parse_dict['collis_sens'] = collis_sens
|
|
106
|
+
self.parse_dict['teach_sens'] = teach_sens
|
|
107
|
+
self.parse_dict['gravity_direction'] = gravity_direction
|
|
108
|
+
ret.extend([mtbrake, mtable, error_code, warn_code, tcp_offset, tcp_load, collis_sens, teach_sens, gravity_direction])
|
|
109
|
+
return ret
|
|
110
|
+
|
|
111
|
+
def _parse_report_tcp_rich_data(self, rx_data):
|
|
112
|
+
ret = self._parse_report_tcp_normal_data(rx_data)
|
|
113
|
+
length = ret[0]
|
|
114
|
+
if length >= 151:
|
|
115
|
+
arm_type, arm_axis, arm_master_id, arm_slave_id, arm_motor_tid, arm_motor_fid = rx_data[145:151]
|
|
116
|
+
self.parse_dict['arm_type'] = arm_type
|
|
117
|
+
self.parse_dict['arm_axis'] = arm_axis
|
|
118
|
+
self.parse_dict['arm_master_id'] = arm_master_id
|
|
119
|
+
self.parse_dict['arm_slave_id'] = arm_slave_id
|
|
120
|
+
self.parse_dict['arm_motor_tid'] = arm_motor_tid
|
|
121
|
+
self.parse_dict['arm_motor_fid'] = arm_motor_fid
|
|
122
|
+
ret.extend([arm_type, arm_axis, arm_master_id, arm_slave_id, arm_motor_tid, arm_motor_fid])
|
|
123
|
+
if length >= 181:
|
|
124
|
+
version = str(rx_data[151:180], 'utf-8')
|
|
125
|
+
self.parse_dict['version'] = version
|
|
126
|
+
ret.append(version)
|
|
127
|
+
if length >= 201:
|
|
128
|
+
trs_msg = convert.bytes_to_fp32s(rx_data[181:201], 5)
|
|
129
|
+
tcp_jerk, min_tcp_acc, max_tcp_acc, min_tcp_speed, max_tcp_speed = trs_msg
|
|
130
|
+
self.parse_dict['tcp_jerk'] = tcp_jerk
|
|
131
|
+
self.parse_dict['min_tcp_acc'] = min_tcp_acc
|
|
132
|
+
self.parse_dict['max_tcp_acc'] = max_tcp_acc
|
|
133
|
+
self.parse_dict['min_tcp_speed'] = min_tcp_speed
|
|
134
|
+
self.parse_dict['max_tcp_speed'] = max_tcp_speed
|
|
135
|
+
ret.extend([tcp_jerk, min_tcp_acc, max_tcp_acc, min_tcp_speed, max_tcp_speed])
|
|
136
|
+
if length >= 221:
|
|
137
|
+
p2p_msg = convert.bytes_to_fp32s(rx_data[201:221], 5)
|
|
138
|
+
joint_jerk, min_joint_acc, max_joint_acc, min_joint_speed, max_joint_speed = p2p_msg
|
|
139
|
+
self.parse_dict['joint_jerk'] = joint_jerk
|
|
140
|
+
self.parse_dict['min_joint_acc'] = min_joint_acc
|
|
141
|
+
self.parse_dict['max_joint_acc'] = max_joint_acc
|
|
142
|
+
self.parse_dict['min_joint_speed'] = min_joint_speed
|
|
143
|
+
self.parse_dict['max_joint_speed'] = max_joint_speed
|
|
144
|
+
ret.extend([joint_jerk, min_joint_acc, max_joint_acc, min_joint_speed, max_joint_speed])
|
|
145
|
+
if length >= 229:
|
|
146
|
+
rot_msg = convert.bytes_to_fp32s(rx_data[221:229], 2)
|
|
147
|
+
rot_jerk, max_rot_acc = rot_msg
|
|
148
|
+
self.parse_dict['rot_jerk'] = rot_jerk
|
|
149
|
+
self.parse_dict['max_rot_acc'] = max_rot_acc
|
|
150
|
+
ret.extend([rot_jerk, max_rot_acc])
|
|
151
|
+
if length >= 245:
|
|
152
|
+
servo_code = [val for val in rx_data[229:245]]
|
|
153
|
+
self.parse_dict['servo_code'] = servo_code
|
|
154
|
+
ret.extend([servo_code[:-2], servo_code[-2:]])
|
|
155
|
+
if length >= 252:
|
|
156
|
+
temperatures = list(map(int, rx_data[245:252]))
|
|
157
|
+
self.parse_dict['temperatures'] = temperatures
|
|
158
|
+
ret.append(temperatures)
|
|
159
|
+
if length >= 284:
|
|
160
|
+
speeds = convert.bytes_to_fp32s(rx_data[252:8 * 4 + 252], 8)
|
|
161
|
+
self.parse_dict['speeds'] = speeds
|
|
162
|
+
ret.append(speeds)
|
|
163
|
+
if length >= 288:
|
|
164
|
+
count = convert.bytes_to_u32(rx_data[284:288])
|
|
165
|
+
self.parse_dict['count'] = count
|
|
166
|
+
ret.append(count)
|
|
167
|
+
if length >= 312:
|
|
168
|
+
world_offset = convert.bytes_to_fp32s(rx_data[288:6 * 4 + 288], 6)
|
|
169
|
+
self.parse_dict['world_offset'] = world_offset
|
|
170
|
+
ret.append(world_offset)
|
|
171
|
+
if length >= 314:
|
|
172
|
+
cgpio_reset_enable, tgpio_reset_enable = rx_data[312:314]
|
|
173
|
+
self.parse_dict['cgpio_reset_enable'] = cgpio_reset_enable
|
|
174
|
+
self.parse_dict['tgpio_reset_enable'] = tgpio_reset_enable
|
|
175
|
+
ret.extend([cgpio_reset_enable, tgpio_reset_enable])
|
|
176
|
+
if length >= 416:
|
|
177
|
+
# if length >= 340:
|
|
178
|
+
is_collision_check, collision_tool_type = rx_data[314:316]
|
|
179
|
+
collision_tool_params = convert.bytes_to_fp32s(rx_data[316:340], 6)
|
|
180
|
+
self.parse_dict['is_collision_check'] = is_collision_check
|
|
181
|
+
self.parse_dict['collision_tool_type'] = collision_tool_type
|
|
182
|
+
self.parse_dict['collision_tool_params'] = collision_tool_params
|
|
183
|
+
ret.extend([is_collision_check, collision_tool_type, collision_tool_params])
|
|
184
|
+
# if length >= 354:
|
|
185
|
+
voltages = convert.bytes_to_u16s(rx_data[340:354], 7)
|
|
186
|
+
voltages = list(map(lambda x: x / 100, voltages))
|
|
187
|
+
self.parse_dict['voltages'] = voltages
|
|
188
|
+
ret.append(voltages)
|
|
189
|
+
# if length >= 382:
|
|
190
|
+
currents = convert.bytes_to_fp32s(rx_data[354:382], 7)
|
|
191
|
+
self._currents = currents
|
|
192
|
+
self.parse_dict['currents'] = currents
|
|
193
|
+
ret.append(currents)
|
|
194
|
+
# if length >= 416:
|
|
195
|
+
cgpio_states = []
|
|
196
|
+
cgpio_states.extend(rx_data[382:384])
|
|
197
|
+
cgpio_states.extend(convert.bytes_to_u16s(rx_data[384:400], 8))
|
|
198
|
+
cgpio_states[6:10] = list(map(lambda x: x / 4095.0 * 10.0, cgpio_states[6:10]))
|
|
199
|
+
cgpio_states.append(list(map(int, rx_data[400:408])))
|
|
200
|
+
cgpio_states.append(list(map(int, rx_data[408:416])))
|
|
201
|
+
self.parse_dict['cgpio_states'] = cgpio_states
|
|
202
|
+
ret.append(cgpio_states)
|
|
203
|
+
return ret
|
|
204
|
+
|