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.
Files changed (63) hide show
  1. xarm/__init__.py +2 -0
  2. xarm/build_backend.py +17 -0
  3. xarm/core/__init__.py +2 -0
  4. xarm/core/comm/__init__.py +5 -0
  5. xarm/core/comm/base.py +303 -0
  6. xarm/core/comm/serial_port.py +44 -0
  7. xarm/core/comm/socket_port.py +150 -0
  8. xarm/core/comm/uxbus_cmd_protocol.py +100 -0
  9. xarm/core/config/__init__.py +0 -0
  10. xarm/core/config/x_code.py +1427 -0
  11. xarm/core/config/x_config.py +553 -0
  12. xarm/core/utils/__init__.py +3 -0
  13. xarm/core/utils/convert.py +124 -0
  14. xarm/core/utils/crc16.py +76 -0
  15. xarm/core/utils/debug_print.py +21 -0
  16. xarm/core/utils/log.py +98 -0
  17. xarm/core/version.py +1 -0
  18. xarm/core/wrapper/__init__.py +11 -0
  19. xarm/core/wrapper/uxbus_cmd.py +1457 -0
  20. xarm/core/wrapper/uxbus_cmd_ser.py +94 -0
  21. xarm/core/wrapper/uxbus_cmd_tcp.py +305 -0
  22. xarm/tools/__init__.py +0 -0
  23. xarm/tools/blockly/__init__.py +1 -0
  24. xarm/tools/blockly/_blockly_base.py +416 -0
  25. xarm/tools/blockly/_blockly_handler.py +1338 -0
  26. xarm/tools/blockly/_blockly_highlight.py +94 -0
  27. xarm/tools/blockly/_blockly_node.py +61 -0
  28. xarm/tools/blockly/_blockly_tool.py +480 -0
  29. xarm/tools/blockly_tool.py +1864 -0
  30. xarm/tools/gcode.py +90 -0
  31. xarm/tools/list_ports.py +39 -0
  32. xarm/tools/modbus_tcp.py +205 -0
  33. xarm/tools/threads.py +30 -0
  34. xarm/tools/utils.py +36 -0
  35. xarm/version.py +1 -0
  36. xarm/wrapper/__init__.py +1 -0
  37. xarm/wrapper/studio_api.py +34 -0
  38. xarm/wrapper/xarm_api.py +4416 -0
  39. xarm/x3/__init__.py +2 -0
  40. xarm/x3/base.py +2638 -0
  41. xarm/x3/base_board.py +198 -0
  42. xarm/x3/code.py +62 -0
  43. xarm/x3/decorator.py +104 -0
  44. xarm/x3/events.py +166 -0
  45. xarm/x3/ft_sensor.py +264 -0
  46. xarm/x3/gpio.py +457 -0
  47. xarm/x3/grammar_async.py +21 -0
  48. xarm/x3/grammar_coroutine.py +24 -0
  49. xarm/x3/gripper.py +830 -0
  50. xarm/x3/modbus_tcp.py +84 -0
  51. xarm/x3/parse.py +110 -0
  52. xarm/x3/record.py +216 -0
  53. xarm/x3/report.py +204 -0
  54. xarm/x3/robotiq.py +220 -0
  55. xarm/x3/servo.py +485 -0
  56. xarm/x3/studio.py +138 -0
  57. xarm/x3/track.py +424 -0
  58. xarm/x3/utils.py +43 -0
  59. xarm/x3/xarm.py +1928 -0
  60. xarm_python_sdk-1.15.2.dist-info/METADATA +103 -0
  61. xarm_python_sdk-1.15.2.dist-info/RECORD +63 -0
  62. xarm_python_sdk-1.15.2.dist-info/WHEEL +4 -0
  63. xarm_python_sdk-1.15.2.dist-info/licenses/LICENSE +27 -0
xarm/x3/xarm.py ADDED
@@ -0,0 +1,1928 @@
1
+ #!/usr/bin/env python3
2
+ # Software License Agreement (BSD License)
3
+ #
4
+ # Copyright (c) 2020, UFACTORY, Inc.
5
+ # All rights reserved.
6
+ #
7
+ # Author: Vinman <vinman.wen@ufactory.cc> <vinman.cub@gmail.com>
8
+
9
+ import os
10
+ import re
11
+ import math
12
+ import sys
13
+ import time
14
+ import uuid
15
+ import socket
16
+ import warnings
17
+ from collections.abc import Iterable
18
+ from ..core.config.x_config import XCONF
19
+ from ..core.utils.log import logger
20
+ from .base import Base
21
+ from .gripper import Gripper
22
+ from .track import Track
23
+ from .base_board import BaseBoard
24
+ from .servo import Servo
25
+ from .record import Record
26
+ from .robotiq import RobotIQ
27
+ from .ft_sensor import FtSensor
28
+ from .modbus_tcp import ModbusTcp
29
+ from .parse import GcodeParser
30
+ from .code import APIState
31
+ from .decorator import xarm_is_connected, xarm_is_ready, xarm_wait_until_not_pause, xarm_wait_until_cmdnum_lt_max
32
+ from .utils import to_radian
33
+ try:
34
+ # from ..tools.blockly_tool import BlocklyTool
35
+ from ..tools.blockly import BlocklyTool
36
+ except:
37
+ print('import BlocklyTool module failed')
38
+ BlocklyTool = None
39
+
40
+ gcode_p = GcodeParser()
41
+
42
+
43
+ class XArm(Gripper, Servo, Record, RobotIQ, BaseBoard, Track, FtSensor, ModbusTcp):
44
+
45
+ def __init__(self, port=None, is_radian=False, do_not_open=False, instance=None, **kwargs):
46
+ super(XArm, self).__init__()
47
+ kwargs['init'] = True
48
+ self._api_instance = instance
49
+ Base.__init__(self, port, is_radian, do_not_open, **kwargs)
50
+
51
+ def _is_out_of_tcp_range(self, value, i):
52
+ if not self._check_tcp_limit or self._stream_type != 'socket' or not self._enable_report or value == math.inf:
53
+ return False
54
+ tcp_range = XCONF.Robot.TCP_LIMITS.get(self.axis).get(self.device_type, [])
55
+ if 2 < i < len(tcp_range): # only limit rotate
56
+ limit = list(tcp_range[i])
57
+ limit[0] += self._position_offset[i]
58
+ limit[1] += self._position_offset[i]
59
+ limit[0] += self._world_offset[i]
60
+ limit[1] += self._world_offset[i]
61
+ if limit[0] == limit[1]:
62
+ return False
63
+ if value < limit[0] - math.radians(0.1) or value > limit[1] + math.radians(0.1):
64
+ self.log_api_info('API -> set_position -> out_of_tcp_range -> code={}, i={} value={}'.format(APIState.OUT_OF_RANGE, i, value), code=APIState.OUT_OF_RANGE)
65
+ return True
66
+ return False
67
+
68
+ def _is_out_of_joint_range(self, angle, i):
69
+ if not self._check_joint_limit or self._stream_type != 'socket' or not self._enable_report or angle == math.inf:
70
+ return False
71
+ device_type = int('{}1305'.format(self.axis)) if self.sn and int(self.sn[2:6]) >= 1305 and int(self.sn[2:6]) < 8500 else self.device_type
72
+ joint_limit = XCONF.Robot.JOINT_LIMITS.get(self.axis).get(device_type, [])
73
+ if i < len(joint_limit):
74
+ angle_range = joint_limit[i]
75
+ if angle < angle_range[0] - math.radians(0.1) or angle > angle_range[1] + math.radians(0.1):
76
+ self.log_api_info('API -> set_servo_angle -> out_of_joint_range -> code={}, i={} value={}'.format(APIState.OUT_OF_RANGE, i, angle), code=APIState.OUT_OF_RANGE)
77
+ return True
78
+ return False
79
+
80
+ def __wait_sync(self):
81
+ while not self._is_sync or self._need_sync:
82
+ if not self.connected:
83
+ return APIState.NOT_CONNECTED
84
+ # elif self.has_error:
85
+ # return APIState.HAS_ERROR
86
+ # elif self.is_stop:
87
+ # return APIState.NOT_READY
88
+ if self.has_error:
89
+ _, err_warn = self.get_err_warn_code()
90
+ if err_warn[0] != 0:
91
+ return APIState.HAS_ERROR
92
+ if self.is_stop:
93
+ _, state = self.get_state()
94
+ if state >= 4:
95
+ return APIState.NOT_READY
96
+ time.sleep(0.05)
97
+ return 0
98
+
99
+ def __update_tcp_motion_params(self, speed, acc, mvtime, pose=None):
100
+ self._last_tcp_speed = speed
101
+ self._last_tcp_acc = acc
102
+ self._mvtime = mvtime
103
+ if pose is not None:
104
+ self._last_position = pose.copy()
105
+
106
+ def __update_joint_motion_params(self, speed, acc, mvtime, angles=None):
107
+ self._last_joint_speed = speed
108
+ self._last_joint_acc = acc
109
+ self._mvtime = mvtime
110
+ if angles is not None:
111
+ self._last_angles = angles.copy()
112
+
113
+ def __get_tcp_motion_params(self, speed=None, mvacc=None, mvtime=None, **kwargs):
114
+ speed = speed if speed is not None else kwargs.get('mvvelo', self._last_tcp_speed)
115
+ # spd = self._last_tcp_speed if speed is None else min(max(float(speed), self._min_tcp_speed), self._max_tcp_speed)
116
+ # acc = self._last_tcp_acc if mvacc is None else min(max(float(mvacc), self._min_tcp_acc), self._max_tcp_acc)
117
+ spd = self._last_tcp_speed if speed is None else min(max(float(speed), self._min_tcp_speed), 1000)
118
+ acc = self._last_tcp_acc if mvacc is None else min(max(float(mvacc), self._min_tcp_acc), 50000)
119
+ mvt = self._mvtime if mvtime is None else mvtime
120
+ return spd, acc, mvt
121
+
122
+ def __get_joint_motion_params(self, speed=None, mvacc=None, mvtime=None, is_radian=None, **kwargs):
123
+ is_radian = self._default_is_radian if is_radian is None else is_radian
124
+ speed = speed if speed is not None else kwargs.get('mvvelo', None)
125
+ speed = self._last_joint_speed if speed is None else to_radian(speed, is_radian)
126
+ mvacc = self._last_joint_acc if mvacc is None else to_radian(mvacc, is_radian)
127
+ # spd = min(max(float(speed), self._min_joint_speed), self._max_joint_speed)
128
+ # acc = min(max(float(mvacc), self._min_joint_acc), self._max_joint_acc)
129
+ spd = min(max(float(speed), self._min_joint_speed), math.pi)
130
+ acc = min(max(float(mvacc), self._min_joint_acc), 20)
131
+ mvt = self._mvtime if mvtime is None else mvtime
132
+ return spd, acc, mvt
133
+
134
+ def _set_position_absolute(self, x=None, y=None, z=None, roll=None, pitch=None, yaw=None, radius=None,
135
+ speed=None, mvacc=None, mvtime=None, is_radian=None, wait=False, timeout=None, **kwargs):
136
+ is_radian = self._default_is_radian if is_radian is None else is_radian
137
+ only_check_type = kwargs.get('only_check_type', self._only_check_type)
138
+ tcp_pos = [
139
+ (math.inf if self.version_is_ge(2, 4, 101) else self._last_position[0]) if x is None else float(x),
140
+ (math.inf if self.version_is_ge(2, 4, 101) else self._last_position[1]) if y is None else float(y),
141
+ (math.inf if self.version_is_ge(2, 4, 101) else self._last_position[2]) if z is None else float(z),
142
+ (math.inf if self.version_is_ge(2, 4, 101) else self._last_position[3]) if roll is None else to_radian(roll, is_radian),
143
+ (math.inf if self.version_is_ge(2, 4, 101) else self._last_position[4]) if pitch is None else to_radian(pitch, is_radian),
144
+ (math.inf if self.version_is_ge(2, 4, 101) else self._last_position[5]) if yaw is None else to_radian(yaw, is_radian),
145
+ ]
146
+ motion_type = kwargs.get('motion_type', False)
147
+ for i in range(3):
148
+ if self._is_out_of_tcp_range(tcp_pos[i+3], i + 3):
149
+ return APIState.OUT_OF_RANGE
150
+ if kwargs.get('check', False):
151
+ _, limit = self.is_tcp_limit(tcp_pos, True)
152
+ if _ == 0 and limit is True:
153
+ return APIState.TCP_LIMIT
154
+ self._has_motion_cmd = True
155
+ spd, acc, mvt = self.__get_tcp_motion_params(speed, mvacc, mvtime, **kwargs)
156
+ radius = radius if radius is not None else -1
157
+ feedback_key, studio_wait = self._gen_feedback_key(wait, **kwargs)
158
+ if self.version_is_ge(1, 11, 100) or kwargs.get('debug', False):
159
+ ret = self.arm_cmd.move_line_common(tcp_pos, spd, acc, mvt, radius, coord=0, is_axis_angle=False, only_check_type=only_check_type, motion_type=motion_type, feedback_key=feedback_key)
160
+ else:
161
+ if radius >= 0:
162
+ ret = self.arm_cmd.move_lineb(tcp_pos, spd, acc, mvt, radius, only_check_type, motion_type=motion_type)
163
+ else:
164
+ ret = self.arm_cmd.move_line(tcp_pos, spd, acc, mvt, only_check_type, motion_type=motion_type)
165
+ trans_id = self._get_feedback_transid(feedback_key, studio_wait, kwargs.get('is_pop', True))
166
+ ret[0] = self._check_code(ret[0], is_move_cmd=True)
167
+ self.log_api_info('API -> set_position -> code={}, pos={}, radius={}, velo={}, acc={}'.format(
168
+ ret[0], tcp_pos, radius, spd, acc), code=ret[0])
169
+ self._is_set_move = True
170
+ self._only_check_result = 0
171
+ if only_check_type > 0 and ret[0] == 0:
172
+ self._only_check_result = ret[3]
173
+ return APIState.HAS_ERROR if ret[3] != 0 else ret[0]
174
+ if only_check_type <= 0 and wait and ret[0] == 0:
175
+ code = self.wait_move(timeout, trans_id=trans_id)
176
+ self.__update_tcp_motion_params(spd, acc, mvt)
177
+ self._sync()
178
+ return code
179
+ if only_check_type <= 0 and (ret[0] >= 0 or self.get_is_moving()):
180
+ self.__update_tcp_motion_params(spd, acc, mvt, tcp_pos)
181
+ return ret[0]
182
+
183
+ def _set_position_relative(self, x=None, y=None, z=None, roll=None, pitch=None, yaw=None, radius=None,
184
+ speed=None, mvacc=None, mvtime=None, is_radian=None, wait=False, timeout=None, **kwargs):
185
+ is_radian = self._default_is_radian if is_radian is None else is_radian
186
+ only_check_type = kwargs.get('only_check_type', self._only_check_type)
187
+ motion_type = kwargs.get('motion_type', False)
188
+ if self.version_is_ge(1, 8, 100):
189
+ # use relative api
190
+ tcp_pos = [
191
+ 0 if x is None else float(x),
192
+ 0 if y is None else float(y),
193
+ 0 if z is None else float(z),
194
+ 0 if roll is None else to_radian(roll, is_radian),
195
+ 0 if pitch is None else to_radian(pitch, is_radian),
196
+ 0 if yaw is None else to_radian(yaw, is_radian),
197
+ ]
198
+ self._has_motion_cmd = True
199
+ spd, acc, mvt = self.__get_tcp_motion_params(speed, mvacc, mvtime, **kwargs)
200
+ radius = radius if radius is not None else -1
201
+ feedback_key, studio_wait = self._gen_feedback_key(wait, **kwargs)
202
+ ret = self.arm_cmd.move_relative(tcp_pos, spd, acc, mvt, radius, False, False, only_check_type, motion_type=motion_type, feedback_key=feedback_key)
203
+ trans_id = self._get_feedback_transid(feedback_key, studio_wait, kwargs.get('is_pop', True))
204
+ ret[0] = self._check_code(ret[0], is_move_cmd=True)
205
+ self.log_api_info('API -> set_relative_position -> code={}, pos={}, radius={}, velo={}, acc={}'.format(
206
+ ret[0], tcp_pos, radius, spd, acc), code=ret[0])
207
+ self._is_set_move = True
208
+ self._only_check_result = 0
209
+ if only_check_type > 0 and ret[0] == 0:
210
+ self._only_check_result = ret[3]
211
+ return APIState.HAS_ERROR if ret[3] != 0 else ret[0]
212
+ if only_check_type <= 0 and wait and ret[0] == 0:
213
+ code = self.wait_move(timeout, trans_id=trans_id)
214
+ self.__update_tcp_motion_params(spd, acc, mvt)
215
+ self._sync()
216
+ return code
217
+ if only_check_type <= 0 and (ret[0] >= 0 or self.get_is_moving()):
218
+ self.__update_tcp_motion_params(spd, acc, mvt)
219
+ return ret[0]
220
+ else:
221
+ # use absolute api
222
+ tcp_pos = [
223
+ self._last_position[0] if x is None else (self._last_position[0] + float(x)),
224
+ self._last_position[1] if y is None else (self._last_position[1] + float(y)),
225
+ self._last_position[2] if z is None else (self._last_position[2] + float(z)),
226
+ self._last_position[3] if roll is None else (self._last_position[3] + to_radian(roll, is_radian)),
227
+ self._last_position[4] if pitch is None else (self._last_position[4] + to_radian(pitch, is_radian)),
228
+ self._last_position[5] if yaw is None else (self._last_position[5] + to_radian(yaw, is_radian)),
229
+ ]
230
+ return self._set_position_absolute(*tcp_pos, radius=radius, speed=speed, mvacc=mvacc, mvtime=mvtime,
231
+ is_radian=True, wait=wait, timeout=timeout, **kwargs)
232
+
233
+ @xarm_wait_until_not_pause
234
+ @xarm_wait_until_cmdnum_lt_max
235
+ @xarm_is_ready(_type='set')
236
+ def set_position(self, x=None, y=None, z=None, roll=None, pitch=None, yaw=None, radius=None,
237
+ speed=None, mvacc=None, mvtime=None, relative=False, is_radian=None,
238
+ wait=False, timeout=None, **kwargs):
239
+ only_check_type = kwargs.get('only_check_type', self._only_check_type)
240
+ if only_check_type > 0 and wait:
241
+ code = self.wait_move(timeout=timeout)
242
+ if code != 0:
243
+ return code
244
+ code = self.__wait_sync()
245
+ if code != 0:
246
+ return code
247
+ if relative:
248
+ return self._set_position_relative(x=x, y=y, z=z, roll=roll, pitch=pitch, yaw=yaw, radius=radius,
249
+ speed=speed, mvacc=mvacc, mvtime=mvtime, is_radian=is_radian,
250
+ wait=wait, timeout=timeout, **kwargs)
251
+ else:
252
+ return self._set_position_absolute(x=x, y=y, z=z, roll=roll, pitch=pitch, yaw=yaw, radius=radius,
253
+ speed=speed, mvacc=mvacc, mvtime=mvtime, is_radian=is_radian,
254
+ wait=wait, timeout=timeout, **kwargs)
255
+
256
+ @xarm_wait_until_not_pause
257
+ @xarm_wait_until_cmdnum_lt_max
258
+ @xarm_is_ready(_type='set')
259
+ def set_tool_position(self, x=0, y=0, z=0, roll=0, pitch=0, yaw=0,
260
+ speed=None, mvacc=None, mvtime=None, is_radian=None,
261
+ wait=False, timeout=None, radius=None, **kwargs):
262
+ is_radian = self._default_is_radian if is_radian is None else is_radian
263
+ only_check_type = kwargs.get('only_check_type', self._only_check_type)
264
+ if only_check_type > 0 and wait:
265
+ code = self.wait_move(timeout=timeout)
266
+ if code != 0:
267
+ return code
268
+ tcp_pos = [
269
+ x, y, z,
270
+ to_radian(roll, is_radian),
271
+ to_radian(pitch, is_radian),
272
+ to_radian(yaw, is_radian)
273
+ ]
274
+ spd, acc, mvt = self.__get_tcp_motion_params(speed, mvacc, mvtime, **kwargs)
275
+ self._has_motion_cmd = True
276
+ motion_type = kwargs.get('motion_type', False)
277
+ radius = radius if radius is not None else -1
278
+ feedback_key, studio_wait = self._gen_feedback_key(wait, **kwargs)
279
+
280
+ if self.version_is_ge(1, 11, 100) or kwargs.get('debug', False):
281
+ ret = self.arm_cmd.move_line_common(tcp_pos, spd, acc, mvt, radius, coord=1, is_axis_angle=False, only_check_type=only_check_type, motion_type=motion_type, feedback_key=feedback_key)
282
+ else:
283
+ ret = self.arm_cmd.move_line_tool(tcp_pos, spd, acc, mvt, only_check_type, motion_type=motion_type)
284
+ trans_id = self._get_feedback_transid(feedback_key, studio_wait, kwargs.get('is_pop', True))
285
+ ret[0] = self._check_code(ret[0], is_move_cmd=True)
286
+ self.log_api_info('API -> set_tool_position -> code={}, pos={}, velo={}, acc={}'.format(
287
+ ret[0], tcp_pos, spd, acc), code=ret[0])
288
+ self._is_set_move = True
289
+ self._only_check_result = 0
290
+ if only_check_type > 0 and ret[0] == 0:
291
+ self._only_check_result = ret[3]
292
+ return APIState.HAS_ERROR if ret[3] != 0 else ret[0]
293
+ if only_check_type <= 0 and wait and ret[0] == 0:
294
+ code = self.wait_move(timeout, trans_id=trans_id)
295
+ self.__update_tcp_motion_params(spd, acc, mvt)
296
+ self._sync()
297
+ return code
298
+ if only_check_type <= 0 and (ret[0] >= 0 or self.get_is_moving()):
299
+ self.__update_tcp_motion_params(spd, acc, mvt)
300
+ return ret[0]
301
+
302
+ @xarm_wait_until_not_pause
303
+ @xarm_wait_until_cmdnum_lt_max
304
+ @xarm_is_ready(_type='set')
305
+ def set_position_aa(self, mvpose, speed=None, mvacc=None, mvtime=None,
306
+ is_radian=None, is_tool_coord=False, relative=False,
307
+ wait=False, timeout=None, radius=None, **kwargs):
308
+ is_radian = self._default_is_radian if is_radian is None else is_radian
309
+ only_check_type = kwargs.get('only_check_type', self._only_check_type)
310
+ if only_check_type > 0 and wait:
311
+ code = self.wait_move(timeout=timeout)
312
+ if code != 0:
313
+ return code
314
+ tcp_pos = [to_radian(mvpose[i], is_radian or i <= 2) for i in range(6)]
315
+ spd, acc, mvt = self.__get_tcp_motion_params(speed, mvacc, mvtime, **kwargs)
316
+ mvcoord = kwargs.get('mvcoord', int(is_tool_coord))
317
+ self._has_motion_cmd = True
318
+ motion_type = kwargs.get('motion_type', False)
319
+ radius = radius if radius is not None else -1
320
+ feedback_key, studio_wait = self._gen_feedback_key(wait, **kwargs)
321
+ if self.version_is_ge(1, 11, 100) or kwargs.get('debug', False):
322
+ if not is_tool_coord and relative:
323
+ ret = self.arm_cmd.move_relative(tcp_pos, spd, acc, mvt, radius, False, True, only_check_type, motion_type=motion_type, feedback_key=feedback_key)
324
+ else:
325
+ ret = self.arm_cmd.move_line_common(tcp_pos, spd, acc, mvt, radius, coord=1 if is_tool_coord else 0, is_axis_angle=True, only_check_type=only_check_type, motion_type=motion_type, feedback_key=feedback_key)
326
+ else:
327
+ ret = self.arm_cmd.move_line_aa(tcp_pos, spd, acc, mvt, mvcoord, int(relative), only_check_type, motion_type=motion_type)
328
+ trans_id = self._get_feedback_transid(feedback_key, studio_wait)
329
+ ret[0] = self._check_code(ret[0], is_move_cmd=True)
330
+ self.log_api_info('API -> set_position_aa -> code={}, pos={}, velo={}, acc={}'.format(
331
+ ret[0], tcp_pos, spd, acc), code=ret[0])
332
+ self._is_set_move = True
333
+ self._only_check_result = 0
334
+ if only_check_type > 0 and ret[0] == 0:
335
+ self._only_check_result = ret[3]
336
+ return APIState.HAS_ERROR if ret[3] != 0 else ret[0]
337
+ if only_check_type <= 0 and wait and ret[0] == 0:
338
+ code = self.wait_move(timeout, trans_id=trans_id)
339
+ self.__update_tcp_motion_params(spd, acc, mvt)
340
+ self._sync()
341
+ return code
342
+ if only_check_type <= 0 and (ret[0] >= 0 or self.get_is_moving()):
343
+ self.__update_tcp_motion_params(spd, acc, mvt)
344
+ return ret[0]
345
+
346
+ @xarm_wait_until_not_pause
347
+ @xarm_is_ready(_type='set')
348
+ def set_servo_cartesian_aa(self, mvpose, speed=None, mvacc=None, is_radian=None, is_tool_coord=False, relative=False, **kwargs):
349
+ is_radian = self._default_is_radian if is_radian is None else is_radian
350
+ assert len(mvpose) >= 6
351
+ tcp_pos = [to_radian(mvpose[i], is_radian or i <= 2) for i in range(6)]
352
+ spd, acc, mvt = self.__get_tcp_motion_params(speed, mvacc, self._mvtime, **kwargs)
353
+
354
+ tool_coord = kwargs.get('tool_coord', int(is_tool_coord))
355
+
356
+ self._has_motion_cmd = True
357
+ ret = self.arm_cmd.move_servo_cart_aa(mvpose=tcp_pos, mvvelo=spd, mvacc=acc, tool_coord=tool_coord, relative=int(relative))
358
+ ret[0] = self._check_code(ret[0], is_move_cmd=True, mode=1)
359
+ self.log_api_info('API -> set_servo_cartesian_aa -> code={}, pose={}, velo={}, acc={}'.format(
360
+ ret[0], tcp_pos, spd, acc
361
+ ), code=ret[0])
362
+ self._is_set_move = True
363
+ return ret[0]
364
+
365
+ def _set_servo_angle_absolute(self, angles, speed=None, mvacc=None, mvtime=None,
366
+ is_radian=None, wait=False, timeout=None, radius=None, **kwargs):
367
+ is_radian = self._default_is_radian if is_radian is None else is_radian
368
+ only_check_type = kwargs.get('only_check_type', self._only_check_type)
369
+ joints = self._last_angles.copy()
370
+ for i in range(min(len(self._last_angles), len(angles))):
371
+ if i >= self.axis or angles[i] is None:
372
+ continue
373
+ joints[i] = to_radian(angles[i], is_radian)
374
+ if self._is_out_of_joint_range(joints[i], i):
375
+ return APIState.OUT_OF_RANGE
376
+ if kwargs.get('check', False):
377
+ _, limit = self.is_joint_limit(joints, True)
378
+ if _ == 0 and limit is True:
379
+ return APIState.JOINT_LIMIT
380
+ joints[5] = 0 if self.axis <= 5 else joints[5]
381
+ joints[6] = 0 if self.axis <= 6 else joints[6]
382
+ spd, acc, mvt = self.__get_joint_motion_params(speed, mvacc, mvtime, is_radian=is_radian, **kwargs)
383
+ self._has_motion_cmd = True
384
+ feedback_key, studio_wait = self._gen_feedback_key(wait, **kwargs)
385
+ if self.version_is_ge(1, 5, 20) and radius is not None and radius >= 0:
386
+ ret = self.arm_cmd.move_jointb(joints, spd, acc, radius, only_check_type, feedback_key=feedback_key)
387
+ else:
388
+ ret = self.arm_cmd.move_joint(joints, spd, acc, mvt, only_check_type, feedback_key=feedback_key)
389
+ trans_id = self._get_feedback_transid(feedback_key, studio_wait, kwargs.get('is_pop', True))
390
+ ret[0] = self._check_code(ret[0], is_move_cmd=True)
391
+ self.log_api_info('API -> set_servo_angle -> code={}, angles={}, velo={}, acc={}, radius={}'.format(
392
+ ret[0], joints, spd, acc, radius
393
+ ), code=ret[0])
394
+ self._is_set_move = True
395
+ self._only_check_result = 0
396
+ if only_check_type > 0 and ret[0] == 0:
397
+ self._only_check_result = ret[3]
398
+ return APIState.HAS_ERROR if ret[3] != 0 else ret[0]
399
+ if only_check_type <= 0 and wait and ret[0] == 0:
400
+ code = self.wait_move(timeout, trans_id=trans_id)
401
+ self.__update_joint_motion_params(spd, acc, mvt)
402
+ self._sync()
403
+ return code
404
+ if only_check_type <= 0 and (ret[0] >= 0 or self.get_is_moving()):
405
+ self.__update_joint_motion_params(spd, acc, mvt, joints)
406
+ return ret[0]
407
+
408
+ def _set_servo_angle_relative(self, angles, speed=None, mvacc=None, mvtime=None,
409
+ is_radian=None, wait=False, timeout=None, radius=None, **kwargs):
410
+ is_radian = self._default_is_radian if is_radian is None else is_radian
411
+ only_check_type = kwargs.get('only_check_type', self._only_check_type)
412
+ if self.version_is_ge(1, 8, 100):
413
+ # use relative api
414
+ joints = [0] * 7
415
+ for i in range(min(7, len(angles))):
416
+ if i >= self.axis or angles[i] is None:
417
+ continue
418
+ joints[i] = to_radian(angles[i], is_radian)
419
+ self._has_motion_cmd = True
420
+ spd, acc, mvt = self.__get_joint_motion_params(speed, mvacc, mvtime, is_radian=is_radian, **kwargs)
421
+ radius = radius if radius is not None else -1
422
+ feedback_key, studio_wait = self._gen_feedback_key(wait, **kwargs)
423
+ joints[5] = 0 if self.axis <= 5 else joints[5]
424
+ joints[6] = 0 if self.axis <= 6 else joints[6]
425
+ ret = self.arm_cmd.move_relative(joints, spd, acc, mvt, radius, True, False, only_check_type, feedback_key=feedback_key)
426
+ trans_id = self._get_feedback_transid(feedback_key, studio_wait)
427
+ ret[0] = self._check_code(ret[0], is_move_cmd=True)
428
+ self.log_api_info('API -> set_relative_servo_angle -> code={}, angles={}, velo={}, acc={}, radius={}'.format(
429
+ ret[0], joints, spd, acc, radius
430
+ ), code=ret[0])
431
+ self._is_set_move = True
432
+ self._only_check_result = 0
433
+ if only_check_type > 0 and ret[0] == 0:
434
+ self._only_check_result = ret[3]
435
+ return APIState.HAS_ERROR if ret[3] != 0 else ret[0]
436
+ if only_check_type <= 0 and wait and ret[0] == 0:
437
+ code = self.wait_move(timeout, trans_id=trans_id)
438
+ self.__update_joint_motion_params(spd, acc, mvt)
439
+ self._sync()
440
+ return code
441
+ if only_check_type <= 0 and (ret[0] >= 0 or self.get_is_moving()):
442
+ self.__update_joint_motion_params(spd, acc, mvt)
443
+ return ret[0]
444
+ else:
445
+ # use absolute api
446
+ joints = self._last_angles.copy()
447
+ for i in range(min(len(self._last_angles), len(angles))):
448
+ if i >= self.axis or angles[i] is None:
449
+ continue
450
+ joints[i] = to_radian(angles[i], is_radian)
451
+ if self._is_out_of_joint_range(joints[i], i):
452
+ return APIState.OUT_OF_RANGE
453
+ return self._set_servo_angle_absolute(joints, speed=speed, mvacc=mvacc, mvtime=mvtime, is_radian=True,
454
+ wait=wait, timeout=timeout, radius=radius, **kwargs)
455
+
456
+ @xarm_wait_until_not_pause
457
+ @xarm_wait_until_cmdnum_lt_max
458
+ @xarm_is_ready(_type='set')
459
+ def set_servo_angle(self, servo_id=None, angle=None, speed=None, mvacc=None, mvtime=None,
460
+ relative=False, is_radian=None, wait=False, timeout=None, radius=None, **kwargs):
461
+ assert ((servo_id is None or servo_id == 8) and isinstance(angle, Iterable)) \
462
+ or (1 <= servo_id <= 7 and angle is not None and not isinstance(angle, Iterable)), \
463
+ 'param servo_id or angle error'
464
+ if servo_id is not None and servo_id != 8:
465
+ if servo_id > self.axis or servo_id <= 0:
466
+ return APIState.SERVO_NOT_EXIST
467
+ angles = [math.inf if self.version_is_ge(2, 4, 101) else None] * 7
468
+ angles[servo_id - 1] = angle
469
+ else:
470
+ angles = angle
471
+ only_check_type = kwargs.get('only_check_type', self._only_check_type)
472
+ if only_check_type > 0 and wait:
473
+ code = self.wait_move(timeout=timeout)
474
+ if code != 0:
475
+ return code
476
+ code = self.__wait_sync()
477
+ if code != 0:
478
+ return code
479
+ if relative:
480
+ return self._set_servo_angle_relative(angles, speed=speed, mvacc=mvacc, mvtime=mvtime, is_radian=is_radian,
481
+ wait=wait, timeout=timeout, radius=radius, **kwargs)
482
+ else:
483
+ return self._set_servo_angle_absolute(angles, speed=speed, mvacc=mvacc, mvtime=mvtime, is_radian=is_radian,
484
+ wait=wait, timeout=timeout, radius=radius, **kwargs)
485
+
486
+ @xarm_is_ready(_type='set')
487
+ def set_servo_angle_j(self, angles, speed=None, mvacc=None, mvtime=None, is_radian=None, **kwargs):
488
+ # if not self._check_mode_is_correct(1):
489
+ # return APIState.MODE_IS_NOT_CORRECT
490
+ is_radian = self._default_is_radian if is_radian is None else is_radian
491
+ angs = [to_radian(angle, is_radian) for angle in angles]
492
+ for i in range(self.axis):
493
+ if self._is_out_of_joint_range(angs[i], i):
494
+ return APIState.OUT_OF_RANGE
495
+ while len(angs) < 7:
496
+ angs.append(0)
497
+ angs[5] = 0 if self.axis <= 5 else angs[5]
498
+ angs[6] = 0 if self.axis <= 6 else angs[6]
499
+ spd, acc, mvt = self.__get_joint_motion_params(speed, mvacc, mvtime, is_radian=is_radian, **kwargs)
500
+ self._has_motion_cmd = True
501
+ ret = self.arm_cmd.move_servoj(angs, spd, acc, mvt)
502
+ ret[0] = self._check_code(ret[0], is_move_cmd=True, mode=1)
503
+ self.log_api_info('API -> set_servo_angle_j -> code={}, angles={}, velo={}, acc={}'.format(
504
+ ret[0], angs, spd, acc
505
+ ), code=ret[0])
506
+ self._is_set_move = True
507
+ return ret[0]
508
+
509
+ @xarm_is_ready(_type='set')
510
+ def set_servo_cartesian(self, mvpose, speed=None, mvacc=None, mvtime=None, is_radian=None, is_tool_coord=False, **kwargs):
511
+ # if not self._check_mode_is_correct(1):
512
+ # return APIState.MODE_IS_NOT_CORRECT
513
+ assert len(mvpose) >= 6
514
+ is_radian = self._default_is_radian if is_radian is None else is_radian
515
+ tcp_pos = [to_radian(mvpose[i], is_radian or i <= 2) for i in range(6)]
516
+ spd, acc, mvt = self.__get_tcp_motion_params(speed, mvacc, mvtime, **kwargs)
517
+ self._has_motion_cmd = True
518
+ ret = self.arm_cmd.move_servo_cartesian(tcp_pos, spd, acc, int(is_tool_coord))
519
+ ret[0] = self._check_code(ret[0], is_move_cmd=True, mode=1)
520
+ self.log_api_info('API -> set_servo_cartisian -> code={}, pose={}, velo={}, acc={}, is_tool_coord={}'.format(
521
+ ret[0], tcp_pos, spd, acc, is_tool_coord
522
+ ), code=ret[0])
523
+ self._is_set_move = True
524
+ return ret[0]
525
+
526
+ @xarm_wait_until_not_pause
527
+ @xarm_wait_until_cmdnum_lt_max
528
+ @xarm_is_ready(_type='set')
529
+ def move_circle(self, pose1, pose2, percent, speed=None, mvacc=None, mvtime=None, is_radian=None,
530
+ wait=False, timeout=None, is_tool_coord=False, is_axis_angle=False, **kwargs):
531
+ is_radian = self._default_is_radian if is_radian is None else is_radian
532
+ only_check_type = kwargs.get('only_check_type', self._only_check_type)
533
+ if only_check_type > 0 and wait:
534
+ code = self.wait_move(timeout=timeout)
535
+ if code != 0:
536
+ return code
537
+ pose_1 = []
538
+ pose_2 = []
539
+ for i in range(6):
540
+ pose_1.append(to_radian(pose1[i], is_radian or i <= 2))
541
+ pose_2.append(to_radian(pose2[i], is_radian or i <= 2))
542
+ spd, acc, mvt = self.__get_tcp_motion_params(speed, mvacc, mvtime, **kwargs)
543
+ self._has_motion_cmd = True
544
+ feedback_key, studio_wait = self._gen_feedback_key(wait, **kwargs)
545
+ if self.version_is_ge(1, 11, 100) or kwargs.get('debug', False):
546
+ ret = self.arm_cmd.move_circle_common(pose_1, pose_2, spd, acc, mvt, percent, coord=1 if is_tool_coord else 0, is_axis_angle=is_axis_angle, only_check_type=only_check_type, feedback_key=feedback_key)
547
+ else:
548
+ ret = self.arm_cmd.move_circle(pose_1, pose_2, spd, acc, mvt, percent, only_check_type)
549
+ trans_id = self._get_feedback_transid(feedback_key, studio_wait, kwargs.get('is_pop', True))
550
+ ret[0] = self._check_code(ret[0], is_move_cmd=True)
551
+ self.log_api_info('API -> move_circle -> code={}, pos1={}, pos2={}, percent={}%, velo={}, acc={}'.format(
552
+ ret[0], pose_1, pose_2, percent, spd, acc), code=ret[0])
553
+ self._is_set_move = True
554
+ self._only_check_result = 0
555
+ if only_check_type > 0 and ret[0] == 0:
556
+ self._only_check_result = ret[3]
557
+ return APIState.HAS_ERROR if ret[3] != 0 else ret[0]
558
+ if only_check_type <= 0 and wait and ret[0] == 0:
559
+ code = self.wait_move(timeout, trans_id=trans_id)
560
+ self.__update_tcp_motion_params(spd, acc, mvt)
561
+ self._sync()
562
+ return code
563
+ if only_check_type <= 0 and (ret[0] >= 0 or self.get_is_moving()):
564
+ self.__update_tcp_motion_params(spd, acc, mvt)
565
+ return ret[0]
566
+
567
+ @xarm_wait_until_not_pause
568
+ @xarm_wait_until_cmdnum_lt_max
569
+ @xarm_is_ready(_type='set')
570
+ def move_gohome(self, speed=None, mvacc=None, mvtime=None, is_radian=None, wait=False, timeout=None, **kwargs):
571
+ is_radian = self._default_is_radian if is_radian is None else is_radian
572
+ only_check_type = kwargs.get('only_check_type', self._only_check_type)
573
+ if only_check_type > 0 and wait:
574
+ code = self.wait_move(timeout=timeout)
575
+ if code != 0:
576
+ return code
577
+ spd, acc, mvt = self.__get_joint_motion_params(speed, mvacc, mvtime, is_radian=is_radian, **kwargs)
578
+ self._has_motion_cmd = True
579
+ feedback_key, studio_wait = self._gen_feedback_key(wait, **kwargs)
580
+ ret = self.arm_cmd.move_gohome(spd, acc, mvt, only_check_type, feedback_key=feedback_key)
581
+ trans_id = self._get_feedback_transid(feedback_key, studio_wait)
582
+ ret[0] = self._check_code(ret[0], is_move_cmd=True)
583
+ self.log_api_info('API -> move_gohome -> code={}, velo={}, acc={}'.format(
584
+ ret[0], spd, acc
585
+ ), code=ret[0])
586
+ self._is_set_move = True
587
+ self._only_check_result = 0
588
+ if only_check_type > 0 and ret[0] == 0:
589
+ self._only_check_result = ret[3]
590
+ return APIState.HAS_ERROR if ret[3] != 0 else ret[0]
591
+ if only_check_type <= 0 and wait and ret[0] == 0:
592
+ code = self.wait_move(timeout, trans_id=trans_id)
593
+ self._sync()
594
+ return code
595
+ return ret[0]
596
+
597
+ @xarm_is_ready(_type='set')
598
+ def move_arc_lines(self, paths, is_radian=None, times=1, first_pause_time=0.1, repeat_pause_time=0,
599
+ automatic_calibration=True, speed=None, mvacc=None, mvtime=None, wait=False):
600
+ assert len(paths) > 0, 'parameter paths error'
601
+ is_radian = self._default_is_radian if is_radian is None else is_radian
602
+ spd, acc, mvt = self.__get_tcp_motion_params(speed, mvacc, mvtime)
603
+ logger.info('move_arc_lines--begin')
604
+ if automatic_calibration:
605
+ _ = self.set_position(*paths[0], is_radian=is_radian, speed=spd, mvacc=acc, mvtime=mvt, wait=True)
606
+ if _ < 0:
607
+ logger.error('quit, api failed, code={}'.format(_))
608
+ return
609
+ _, angles = self.get_servo_angle(is_radian=True)
610
+ if first_pause_time > 0:
611
+ self.set_pause_time(first_pause_time)
612
+ last_used_joint_speed = self._last_joint_speed
613
+
614
+ def _move():
615
+ if automatic_calibration:
616
+ ret = self.set_servo_angle(angle=angles, is_radian=True, speed=0.8726646259971648, wait=False)
617
+ if ret < 0:
618
+ return -1
619
+ self._last_joint_speed = last_used_joint_speed
620
+ for path in paths:
621
+ if len(path) > 6 and path[6] >= 0:
622
+ radius = path[6]
623
+ else:
624
+ radius = 0
625
+ if self.has_error or self.is_stop:
626
+ return -2
627
+ ret = self.set_position(*path[:6], radius=radius, is_radian=is_radian, wait=False, speed=spd, mvacc=acc, mvtime=mvt)
628
+ if ret < 0:
629
+ return -1
630
+ return 0
631
+ count = 1
632
+ api_failed = False
633
+
634
+ try:
635
+ if times == 0:
636
+ while not self.has_error and not self.is_stop:
637
+ _ = _move()
638
+ if _ == -1:
639
+ api_failed = True
640
+ break
641
+ elif _ == -2:
642
+ break
643
+ count += 1
644
+ if repeat_pause_time > 0:
645
+ self.set_pause_time(repeat_pause_time)
646
+ if api_failed:
647
+ logger.error('quit, api error')
648
+ elif self._error_code != 0:
649
+ logger.error('quit, controller error')
650
+ elif self.is_stop:
651
+ logger.error('quit, emergency_stop')
652
+ else:
653
+ for i in range(times):
654
+ if self.has_error or self.is_stop:
655
+ break
656
+ _ = _move()
657
+ if _ == -1:
658
+ api_failed = True
659
+ break
660
+ elif _ == -2:
661
+ break
662
+ count += 1
663
+ if repeat_pause_time > 0:
664
+ self.set_pause_time(repeat_pause_time)
665
+ if api_failed:
666
+ logger.error('quit, api error')
667
+ elif self._error_code != 0:
668
+ logger.error('quit, controller error')
669
+ elif self.is_stop:
670
+ logger.error('quit, emergency_stop')
671
+ except:
672
+ pass
673
+ logger.info('move_arc_lines--end')
674
+ if wait:
675
+ self.wait_move()
676
+ self._sync()
677
+
678
+ @xarm_is_connected(_type='set')
679
+ def set_servo_attach(self, servo_id=None):
680
+ # assert isinstance(servo_id, int) and 1 <= servo_id <= 8
681
+ # ret = self.arm_cmd.set_brake(servo_id, 0)
682
+ logger.info('set_servo_attach--begin')
683
+ ret = self.motion_enable(servo_id=servo_id, enable=True)
684
+ self.set_state(0)
685
+ self._sync()
686
+ logger.info('set_servo_attach--end')
687
+ return ret
688
+
689
+ @xarm_is_connected(_type='set')
690
+ def set_servo_detach(self, servo_id=None):
691
+ """
692
+ :param servo_id: 1-7, 8
693
+ :return:
694
+ """
695
+ assert isinstance(servo_id, int) and 1 <= servo_id <= 8, 'The value of parameter servo_id can only be 1-8.'
696
+ ret = self.arm_cmd.set_brake(servo_id, 1)
697
+ self.log_api_info('API -> set_servo_detach -> code={}'.format(ret[0]), code=ret[0])
698
+ self._sync()
699
+ return ret[0]
700
+
701
+ @xarm_is_connected(_type='set')
702
+ def system_control(self, value=1):
703
+ ret = self.arm_cmd.system_control(value)
704
+ self.log_api_info('API -> system_control({}) -> code={}'.format(value, ret[0]), code=ret[0])
705
+ return ret[0]
706
+
707
+ @xarm_is_connected(_type='set')
708
+ def set_reduced_mode(self, on_off):
709
+ ret = self.arm_cmd.set_reduced_mode(int(on_off))
710
+ self.log_api_info('API -> set_reduced_mode -> code={}'.format(ret[0]), code=ret[0])
711
+ return ret[0]
712
+
713
+ @xarm_is_connected(_type='set')
714
+ def set_reduced_max_tcp_speed(self, speed):
715
+ ret = self.arm_cmd.set_reduced_linespeed(speed)
716
+ self.log_api_info('API -> set_reduced_linespeed -> code={}, speed={}'.format(ret[0], speed), code=ret[0])
717
+ return ret[0]
718
+
719
+ @xarm_is_connected(_type='set')
720
+ def set_reduced_max_joint_speed(self, speed, is_radian=None):
721
+ is_radian = self._default_is_radian if is_radian is None else is_radian
722
+ speed = to_radian(speed, is_radian)
723
+ ret = self.arm_cmd.set_reduced_jointspeed(speed)
724
+ self.log_api_info('API -> set_reduced_linespeed -> code={}, speed={}'.format(ret[0], speed), code=ret[0])
725
+ return ret[0]
726
+
727
+ @xarm_is_connected(_type='get')
728
+ def get_reduced_mode(self):
729
+ ret = self.arm_cmd.get_reduced_mode()
730
+ ret[0] = self._check_code(ret[0])
731
+ return ret[0], ret[1]
732
+
733
+ @xarm_is_connected(_type='get')
734
+ def get_reduced_states(self, is_radian=None):
735
+ is_radian = self._default_is_radian if is_radian is None else is_radian
736
+ ret = self.arm_cmd.get_reduced_states(79 if self.version_is_ge(1, 2, 11) else 21)
737
+ ret[0] = self._check_code(ret[0])
738
+ if ret[0] == 0:
739
+ if not is_radian:
740
+ ret[4] = round(math.degrees(ret[4]), 1)
741
+ if self.version_is_ge(1, 2, 11):
742
+ # ret[5] = list(map(math.degrees, ret[5]))
743
+ ret[5] = list(map(lambda x: round(math.degrees(x), 2), ret[5]))
744
+ return ret[0], ret[1:]
745
+
746
+ @xarm_is_connected(_type='set')
747
+ def set_reduced_tcp_boundary(self, boundary):
748
+ assert len(boundary) >= 6
749
+ boundary = list(map(int, boundary))
750
+ limits = [0] * 6
751
+ limits[0:2] = boundary[0:2] if boundary[0] >= boundary[1] else boundary[0:2][::-1]
752
+ limits[2:4] = boundary[2:4] if boundary[2] >= boundary[3] else boundary[2:4][::-1]
753
+ limits[4:6] = boundary[4:6] if boundary[4] >= boundary[5] else boundary[4:6][::-1]
754
+ ret = self.arm_cmd.set_xyz_limits(limits)
755
+ self.log_api_info('API -> set_reduced_tcp_boundary -> code={}, boundary={}'.format(ret[0], limits), code=ret[0])
756
+ return ret[0]
757
+
758
+ @xarm_is_connected(_type='set')
759
+ def set_reduced_joint_range(self, joint_range, is_radian=None):
760
+ is_radian = self._default_is_radian if is_radian is None else is_radian
761
+ assert len(joint_range) >= self.axis * 2
762
+ joint_range = list(map(float, joint_range))
763
+ limits = [0] * 14
764
+ for i in range(7):
765
+ if i < self.axis:
766
+ limits[i*2:i*2+2] = joint_range[i*2:i*2+2] if joint_range[i*2] <= joint_range[i*2+1] else joint_range[i*2:i*2+2][::-1]
767
+ if not is_radian:
768
+ limits = list(map(math.radians, limits))
769
+ # limits = list(map(lambda x: round(math.radians(x), 3), limits))
770
+
771
+ for i in range(self.axis):
772
+ device_type = int('{}1305'.format(self.axis)) if self.sn and int(self.sn[2:6]) >= 1305 and int(self.sn[2:6]) < 8500 else self.device_type
773
+ joint_limit = XCONF.Robot.JOINT_LIMITS.get(self.axis).get(device_type, [])
774
+ if i < len(joint_limit):
775
+ angle_range = joint_limit[i]
776
+ # angle_range = list(map(lambda x: round(x, 3), joint_limit[i]))
777
+ if limits[i * 2] < angle_range[0]:
778
+ limits[i * 2] = angle_range[0]
779
+ if limits[i * 2 + 1] > angle_range[1]:
780
+ limits[i * 2 + 1] = angle_range[1]
781
+ if limits[i * 2] >= angle_range[1]:
782
+ return APIState.OUT_OF_RANGE
783
+ if limits[i * 2 + 1] <= angle_range[0]:
784
+ return APIState.OUT_OF_RANGE
785
+ ret = self.arm_cmd.set_reduced_jrange(limits)
786
+ self.log_api_info('API -> set_reduced_joint_range -> code={}, boundary={}'.format(ret[0], limits), code=ret[0])
787
+ return ret[0]
788
+
789
+ @xarm_is_connected(_type='set')
790
+ def set_fense_mode(self, on_off):
791
+ ret = self.arm_cmd.set_fense_on(int(on_off))
792
+ self.log_api_info('API -> set_fense_mode -> code={}, on={}'.format(ret[0], on_off), code=ret[0])
793
+ return ret
794
+
795
+ @xarm_is_connected(_type='set')
796
+ def set_collision_rebound(self, on_off):
797
+ ret = self.arm_cmd.set_collis_reb(int(on_off))
798
+ self.log_api_info('API -> set_collision_rebound -> code={}, on={}'.format(ret[0], on_off), code=ret[0])
799
+ return ret
800
+
801
+ @xarm_is_connected(_type='set')
802
+ def set_timer(self, secs_later, tid, fun_code, param1=0, param2=0):
803
+ ret = self.arm_cmd.set_timer(secs_later, tid, fun_code, param1, param2)
804
+ return ret[0]
805
+
806
+ @xarm_is_connected(_type='set')
807
+ def cancel_timer(self, tid):
808
+ ret = self.arm_cmd.cancel_timer(tid)
809
+ return ret[0]
810
+
811
+ @xarm_wait_until_not_pause
812
+ @xarm_is_connected(_type='set')
813
+ def set_world_offset(self, offset, is_radian=None, wait=True):
814
+ is_radian = self._default_is_radian if is_radian is None else is_radian
815
+ assert isinstance(offset, Iterable) and len(offset) >= 6
816
+ world_offset = [0] * 6
817
+ for i in range(min(len(offset), 6)):
818
+ world_offset[i] = to_radian(offset[i], is_radian or i <= 2)
819
+ if wait:
820
+ if self._support_feedback:
821
+ self.wait_all_task_finish()
822
+ else:
823
+ self.wait_move()
824
+ ret = self.arm_cmd.set_world_offset(world_offset)
825
+ self.log_api_info('API -> set_world_offset -> code={}, offset={}'.format(ret[0], world_offset), code=ret[0])
826
+ return ret[0]
827
+
828
+ def reset(self, speed=None, mvacc=None, mvtime=None, is_radian=None, wait=False, timeout=None):
829
+ logger.info('reset--begin')
830
+ is_radian = self._default_is_radian if is_radian is None else is_radian
831
+ if not self._enable_report or self._stream_type != 'socket':
832
+ self.get_err_warn_code()
833
+ self.get_state()
834
+ if self._warn_code != 0:
835
+ self.clean_warn()
836
+ if self._error_code != 0:
837
+ self.clean_error()
838
+ self.motion_enable(enable=True, servo_id=8)
839
+ self.set_state(0)
840
+ if not self._is_ready:
841
+ self.motion_enable(enable=True, servo_id=8)
842
+ self.set_state(state=0)
843
+ self.move_gohome(speed=speed, mvacc=mvacc, mvtime=mvtime, is_radian=is_radian, wait=wait, timeout=timeout)
844
+ logger.info('reset--end')
845
+
846
+ # # This interface is no longer supported
847
+ # @xarm_is_ready(_type='set')
848
+ # def set_joints_torque(self, joints_torque):
849
+ # ret = self.arm_cmd.set_servot(joints_torque)
850
+ # self.log_api_info('API -> set_joints_torque -> code={}, joints_torque={}'.format(ret[0], joints_torque), code=ret[0])
851
+ # return ret[0]
852
+
853
+ @xarm_is_connected(_type='get')
854
+ def get_joints_torque(self, servo_id=None):
855
+ ret = self.arm_cmd.get_joint_tau()
856
+ ret[0] = self._check_code(ret[0])
857
+ if ret[0] == 0 and len(ret) > 7:
858
+ self._joints_torque = [float('{:.6f}'.format(ret[i])) for i in range(1, 8)]
859
+ if servo_id is None or servo_id == 8 or len(self._joints_torque) < servo_id:
860
+ return ret[0], list(self._joints_torque)
861
+ else:
862
+ return ret[0], self._joints_torque[servo_id - 1]
863
+
864
+ @xarm_is_connected(_type='get')
865
+ def get_safe_level(self):
866
+ ret = self.arm_cmd.get_safe_level()
867
+ return ret[0], ret[1]
868
+
869
+ @xarm_is_connected(_type='set')
870
+ def set_safe_level(self, level=4):
871
+ ret = self.arm_cmd.set_safe_level(level)
872
+ self.log_api_info('API -> set_safe_level -> code={}, level={}'.format(ret[0], level), code=ret[0])
873
+ return ret[0]
874
+
875
+ @xarm_wait_until_not_pause
876
+ @xarm_wait_until_cmdnum_lt_max
877
+ @xarm_is_ready(_type='set')
878
+ def set_pause_time(self, sltime, wait=False):
879
+ assert isinstance(sltime, (int, float))
880
+ ret = self.arm_cmd.sleep_instruction(sltime)
881
+ if wait:
882
+ time.sleep(sltime)
883
+ else:
884
+ if time.monotonic() >= self._sleep_finish_time:
885
+ self._sleep_finish_time = time.monotonic() + sltime
886
+ else:
887
+ self._sleep_finish_time += sltime
888
+ self.log_api_info('API -> set_pause_time -> code={}, sltime={}'.format(ret[0], sltime), code=ret[0])
889
+ return ret[0]
890
+
891
+ def set_sleep_time(self, sltime, wait=False):
892
+ return self.set_pause_time(sltime, wait)
893
+
894
+ @xarm_wait_until_not_pause
895
+ @xarm_is_connected(_type='set')
896
+ def set_tcp_offset(self, offset, is_radian=None, wait=True, **kwargs):
897
+ is_radian = self._default_is_radian if is_radian is None else is_radian
898
+ assert isinstance(offset, Iterable) and len(offset) >= 6
899
+ tcp_offset = [0] * 6
900
+ for i in range(min(len(offset), 6)):
901
+ tcp_offset[i] = to_radian(offset[i], is_radian or i <= 2)
902
+ if wait:
903
+ if self._support_feedback:
904
+ self.wait_all_task_finish()
905
+ else:
906
+ self.wait_move()
907
+ ret = self.arm_cmd.set_tcp_offset(tcp_offset)
908
+ self.log_api_info('API -> set_tcp_offset -> code={}, offset={}'.format(ret[0], tcp_offset), code=ret[0])
909
+ return ret[0]
910
+
911
+ @xarm_wait_until_not_pause
912
+ @xarm_wait_until_cmdnum_lt_max
913
+ @xarm_is_ready(_type='set')
914
+ def set_tcp_jerk(self, jerk):
915
+ ret = self.arm_cmd.set_tcp_jerk(jerk)
916
+ self.log_api_info('API -> set_tcp_jerk -> code={}, jerk={}'.format(ret[0], jerk), code=ret[0])
917
+ return ret[0]
918
+
919
+ @xarm_wait_until_not_pause
920
+ @xarm_wait_until_cmdnum_lt_max
921
+ @xarm_is_ready(_type='set')
922
+ def set_tcp_maxacc(self, acc):
923
+ ret = self.arm_cmd.set_tcp_maxacc(acc)
924
+ self.log_api_info('API -> set_tcp_maxacc -> code={}, maxacc={}'.format(ret[0], acc), code=ret[0])
925
+ return ret[0]
926
+
927
+ @xarm_wait_until_not_pause
928
+ @xarm_wait_until_cmdnum_lt_max
929
+ @xarm_is_ready(_type='set')
930
+ def set_joint_jerk(self, jerk, is_radian=None):
931
+ is_radian = self._default_is_radian if is_radian is None else is_radian
932
+ jerk = to_radian(jerk, is_radian)
933
+ ret = self.arm_cmd.set_joint_jerk(jerk)
934
+ self.log_api_info('API -> set_joint_jerk -> code={}, jerk={}'.format(ret[0], jerk), code=ret[0])
935
+ return ret[0]
936
+
937
+ @xarm_wait_until_not_pause
938
+ @xarm_wait_until_cmdnum_lt_max
939
+ @xarm_is_ready(_type='set')
940
+ def set_joint_maxacc(self, maxacc, is_radian=None):
941
+ is_radian = self._default_is_radian if is_radian is None else is_radian
942
+ maxacc = to_radian(maxacc, is_radian)
943
+ ret = self.arm_cmd.set_joint_maxacc(maxacc)
944
+ self.log_api_info('API -> set_joint_maxacc -> code={}, maxacc={}'.format(ret[0], maxacc), code=ret[0])
945
+ return ret[0]
946
+
947
+ @xarm_wait_until_not_pause
948
+ @xarm_is_connected(_type='set')
949
+ def set_collision_sensitivity(self, value, wait=True):
950
+ assert isinstance(value, int) and 0 <= value <= 5
951
+ if self._support_feedback:
952
+ self.wait_all_task_finish()
953
+ else:
954
+ self.wait_move()
955
+ ret = self.arm_cmd.set_collis_sens(value)
956
+ self.set_state(0)
957
+ self.log_api_info('API -> set_collision_sensitivity -> code={}, sensitivity={}'.format(ret[0], value), code=ret[0])
958
+ return ret[0]
959
+
960
+ @xarm_wait_until_not_pause
961
+ @xarm_is_connected(_type='set')
962
+ def set_teach_sensitivity(self, value, wait=True):
963
+ assert isinstance(value, int) and 1 <= value <= 5
964
+ if wait:
965
+ if self._support_feedback:
966
+ self.wait_all_task_finish()
967
+ else:
968
+ self.wait_move()
969
+ ret = self.arm_cmd.set_teach_sens(value)
970
+ self.log_api_info('API -> set_teach_sensitivity -> code={}, sensitivity={}'.format(ret[0], value), code=ret[0])
971
+ return ret[0]
972
+
973
+ @xarm_wait_until_not_pause
974
+ @xarm_is_connected(_type='set')
975
+ def set_gravity_direction(self, direction, wait=True):
976
+ if wait:
977
+ if self._support_feedback:
978
+ self.wait_all_task_finish()
979
+ else:
980
+ self.wait_move()
981
+ ret = self.arm_cmd.set_gravity_dir(direction[:3])
982
+ self.log_api_info('API -> set_gravity_direction -> code={}, direction={}'.format(ret[0], direction), code=ret[0])
983
+ return ret[0]
984
+
985
+ @xarm_wait_until_not_pause
986
+ @xarm_is_connected(_type='set')
987
+ def set_mount_direction(self, base_tilt_deg, rotation_deg, is_radian=None):
988
+ is_radian = self._default_is_radian if is_radian is None else is_radian
989
+ t1 = base_tilt_deg
990
+ t2 = rotation_deg
991
+
992
+ if not is_radian:
993
+ t1 = math.radians(t1)
994
+ t2 = math.radians(t2)
995
+
996
+ # original G vect mounted on flat surface
997
+ G_normal = [0, 0, -1]
998
+
999
+ # rotation matrix introduced by 2 mounting angles
1000
+ R2 = [math.cos(-t2), -math.sin(-t2), 0, math.sin(-t2), math.cos(-t2), 0, 0, 0, 1]
1001
+ R1 = [math.cos(-t1), 0, math.sin(-t1), 0, 1, 0, -math.sin(-t1), 0, math.cos(-t1)]
1002
+
1003
+ Rot = [0] * 9
1004
+ g_new = [0] * 3
1005
+
1006
+ # Mat(Rot) = Mat(R2)*Mat(R1)
1007
+ # vect(g_new) = Mat(Rot)*vect(G_normal)
1008
+ for i in range(3):
1009
+ for j in range(3):
1010
+ Rot[i * 3 + j] += (
1011
+ R2[i * 3 + 0] * R1[0 * 3 + j] + R2[i * 3 + 1] * R1[1 * 3 + j] + R2[i * 3 + 2] * R1[2 * 3 + j])
1012
+
1013
+ g_new[i] = Rot[i * 3 + 0] * G_normal[0] + Rot[i * 3 + 1] * G_normal[1] + Rot[i * 3 + 2] * G_normal[2]
1014
+
1015
+ ret = self.arm_cmd.set_gravity_dir(g_new)
1016
+ self.log_api_info('API -> set_mount_direction -> code={}, tilt={}, rotation={}, direction={}'.format(ret[0], base_tilt_deg, rotation_deg, g_new), code=ret[0])
1017
+ return ret[0]
1018
+
1019
+ @xarm_is_connected(_type='set')
1020
+ def clean_conf(self):
1021
+ ret = self.arm_cmd.clean_conf()
1022
+ self.log_api_info('API -> clean_conf -> code={}'.format(ret[0]), code=ret[0])
1023
+ return ret[0]
1024
+
1025
+ @xarm_is_connected(_type='set')
1026
+ def save_conf(self):
1027
+ ret = self.arm_cmd.save_conf()
1028
+ self.log_api_info('API -> save_conf -> code={}'.format(ret[0]), code=ret[0])
1029
+ return ret[0]
1030
+
1031
+ @xarm_is_connected(_type='get')
1032
+ def get_inverse_kinematics(self, pose, input_is_radian=None, return_is_radian=None):
1033
+ input_is_radian = self._default_is_radian if input_is_radian is None else input_is_radian
1034
+ return_is_radian = self._default_is_radian if return_is_radian is None else return_is_radian
1035
+ assert len(pose) >= 6
1036
+ tcp_pose = [to_radian(pose[i], input_is_radian or i <= 2) for i in range(6)]
1037
+ ret = self.arm_cmd.get_ik(tcp_pose)
1038
+ angles = []
1039
+ ret[0] = self._check_code(ret[0])
1040
+ if ret[0] == 0:
1041
+ # angles = [ret[i][0] for i in range(1, 8)]
1042
+ angles = [ret[i] for i in range(1, 8)]
1043
+ if not return_is_radian:
1044
+ angles = [math.degrees(angle) for angle in angles]
1045
+ return ret[0], angles
1046
+
1047
+ @xarm_is_connected(_type='get')
1048
+ def get_forward_kinematics(self, angles, input_is_radian=None, return_is_radian=None):
1049
+ input_is_radian = self._default_is_radian if input_is_radian is None else input_is_radian
1050
+ return_is_radian = self._default_is_radian if return_is_radian is None else return_is_radian
1051
+ # assert len(angles) >= 7
1052
+ joints = [0] * 7
1053
+ for i in range(min(len(angles), 7)):
1054
+ joints[i] = to_radian(angles[i], input_is_radian)
1055
+
1056
+ ret = self.arm_cmd.get_fk(joints)
1057
+ pose = []
1058
+ ret[0] = self._check_code(ret[0])
1059
+ if ret[0] == 0:
1060
+ # pose = [ret[i][0] for i in range(1, 7)]
1061
+ pose = [ret[i] for i in range(1, 7)]
1062
+ if not return_is_radian:
1063
+ pose = [pose[i] if i < 3 else math.degrees(pose[i]) for i in range(len(pose))]
1064
+ return ret[0], pose
1065
+
1066
+ @xarm_is_connected(_type='get')
1067
+ def is_tcp_limit(self, pose, is_radian=None):
1068
+ is_radian = self._default_is_radian if is_radian is None else is_radian
1069
+ assert len(pose) >= 6
1070
+ tcp_pose = [to_radian(pose[i], is_radian or i <= 2, self._last_position[i]) for i in range(6)]
1071
+ ret = self.arm_cmd.is_tcp_limit(tcp_pose)
1072
+ self.log_api_info('API -> is_tcp_limit -> code={}, limit={}'.format(ret[0], ret[1]), code=ret[0])
1073
+ ret[0] = self._check_code(ret[0])
1074
+ if ret[0] == 0:
1075
+ return ret[0], bool(ret[1])
1076
+ else:
1077
+ return ret[0], None
1078
+
1079
+ @xarm_is_connected(_type='get')
1080
+ def is_joint_limit(self, joint, is_radian=None):
1081
+ is_radian = self._default_is_radian if is_radian is None else is_radian
1082
+ # assert len(joint) >= 7
1083
+ joints = [0] * 7
1084
+ for i in range(min(len(joint), 7)):
1085
+ joints[i] = to_radian(joint[i], is_radian, self._last_angles[i])
1086
+
1087
+ ret = self.arm_cmd.is_joint_limit(joints)
1088
+ self.log_api_info('API -> is_joint_limit -> code={}, limit={}'.format(ret[0], ret[1]), code=ret[0])
1089
+ ret[0] = self._check_code(ret[0])
1090
+ if ret[0] == 0:
1091
+ return ret[0], bool(ret[1])
1092
+ else:
1093
+ return ret[0], None
1094
+
1095
+ def emergency_stop(self):
1096
+ logger.info('emergency_stop--begin')
1097
+ self.set_state(4)
1098
+ expired = time.monotonic() + 3
1099
+ while self.state not in [4] and time.monotonic() < expired:
1100
+ self.set_state(4)
1101
+ time.sleep(0.1)
1102
+ self._sleep_finish_time = 0
1103
+ self._sync()
1104
+ logger.info('emergency_stop--end')
1105
+
1106
+ def send_cmd_async(self, command, timeout=None):
1107
+ pass
1108
+
1109
+ def send_cmd_sync(self, command=None):
1110
+ if command is None:
1111
+ return 0
1112
+ command = command.upper()
1113
+ return self._handle_gcode(command)
1114
+
1115
+ def _handle_gcode(self, command):
1116
+ def __handle_gcode_g(num):
1117
+ if num == 1: # G1 move_line, ex: G1 X{} Y{} Z{} A{roll} B{pitch} C{yaw} F{speed} Q{acc} T{}
1118
+ mvvelo = gcode_p.get_mvvelo(command)
1119
+ mvacc = gcode_p.get_mvacc(command)
1120
+ mvtime = gcode_p.get_mvtime(command)
1121
+ mvpose = gcode_p.get_poses(command)
1122
+ ret = self.set_position(*mvpose, radius=-1, speed=mvvelo, mvacc=mvacc, mvtime=mvtime)
1123
+ elif num == 2: # G2 move_circle, ex: G2 X{} Y{} Z{} A{} B{} C{} I{} J{} K{} L{} M{} N{} F{speed} Q{acc} T{}
1124
+ mvvelo = gcode_p.get_mvvelo(command)
1125
+ mvacc = gcode_p.get_mvacc(command)
1126
+ mvtime = gcode_p.get_mvtime(command)
1127
+ pos1 = gcode_p.get_poses(command, default=0)
1128
+ pos2 = gcode_p.get_joints(command, default=0)[:6]
1129
+ percent = gcode_p.get_mvradius(command, default=0)
1130
+ ret = self.move_circle(pos1, pos2, percent=percent, speed=mvvelo, mvacc=mvacc, mvtime=mvtime)
1131
+ elif num == 4: # G4 set_pause_time, ex: G4 T{}
1132
+ sltime = gcode_p.get_mvtime(command, default=0)
1133
+ ret = self.set_pause_time(sltime)
1134
+ elif num == 7: # G7 move_joint, ex: G7 I{} J{} K{} L{} M{} N{} O{} F{} Q{} T{}
1135
+ mvvelo = gcode_p.get_mvvelo(command)
1136
+ mvacc = gcode_p.get_mvacc(command)
1137
+ mvtime = gcode_p.get_mvtime(command)
1138
+ mvjoint = gcode_p.get_joints(command)
1139
+ ret = self.set_servo_angle(angle=mvjoint, speed=mvvelo, mvacc=mvacc, mvtime=mvtime)
1140
+ elif num == 8: # G8 move_gohome, ex: G8 F{} Q{} T{}
1141
+ mvvelo = gcode_p.get_mvvelo(command)
1142
+ mvacc = gcode_p.get_mvacc(command)
1143
+ mvtime = gcode_p.get_mvtime(command)
1144
+ ret = self.move_gohome(speed=mvvelo, mvacc=mvacc, mvtime=mvtime)
1145
+ elif num == 9: # G9 move_arc_line, ex: G9 X{} Y{} Z{} A{roll} B{pitch} C{yaw} R{radius} F{speed} Q{acc} T{}
1146
+ mvvelo = gcode_p.get_mvvelo(command)
1147
+ mvacc = gcode_p.get_mvacc(command)
1148
+ mvtime = gcode_p.get_mvtime(command)
1149
+ mvpose = gcode_p.get_poses(command)
1150
+ mvradii = gcode_p.get_mvradius(command, default=0)
1151
+ ret = self.set_position(*mvpose, speed=mvvelo, mvacc=mvacc, mvtime=mvtime, radius=mvradii)
1152
+ elif num == 11: # G11 set_servo_angle_j, ex: G11 I{} J{} K{} L{} M{} N{} O{} F{} Q{} T{}
1153
+ mvvelo = gcode_p.get_mvvelo(command)
1154
+ mvacc = gcode_p.get_mvacc(command)
1155
+ mvtime = gcode_p.get_mvtime(command)
1156
+ mvjoint = gcode_p.get_joints(command, default=0)
1157
+ ret = self.set_servo_angle_j(mvjoint, speed=mvvelo, mvacc=mvacc, mvtime=mvtime)
1158
+ elif num == 12: # G12 sleep, ex: G12 T{}
1159
+ mvtime = gcode_p.get_mvtime(command, default=0)
1160
+ time.sleep(mvtime)
1161
+ ret = 0
1162
+ else:
1163
+ logger.debug('command {} is not exist'.format(command))
1164
+ ret = APIState.CMD_NOT_EXIST, 'command {} is not exist'.format(command)
1165
+ return ret
1166
+
1167
+ def __handle_gcode_h(num):
1168
+ if num == 1: # H1 get_version, ex: H1
1169
+ ret = self.get_version()
1170
+ elif num == 10: # H10 system_control, ex: H10 V{}
1171
+ value = gcode_p.get_int_value(command, default=0)
1172
+ ret = self.system_control(value)
1173
+ elif num == 11: # H11 motion_enable, ex: H11 I{id} V{enable}
1174
+ value = gcode_p.get_int_value(command)
1175
+ servo_id = gcode_p.get_id_num(command, default=0)
1176
+ ret = self.motion_enable(enable=value, servo_id=servo_id)
1177
+ elif num == 12: # H12 set_state, ex: H12 V{state}
1178
+ value = gcode_p.get_int_value(command, default=0)
1179
+ ret = self.set_state(value)
1180
+ elif num == 13: # H13 get_state, ex: H13
1181
+ ret = self.get_state()
1182
+ elif num == 14: # H14 get_cmd_num, ex: H14
1183
+ ret = self.get_cmdnum()
1184
+ elif num == 15: # H15 get_error_warn_code, ex: H15
1185
+ ret = self.get_err_warn_code()
1186
+ elif num == 16: # H16 clean_error, ex: H16
1187
+ ret = self.clean_error()
1188
+ elif num == 17: # H17 clean_warn, ex: H17
1189
+ ret = self.clean_warn()
1190
+ elif num == 18: # H18 set_brake, ex: H18 I{id} V{open}
1191
+ value = gcode_p.get_int_value(command)
1192
+ servo_id = gcode_p.get_id_num(command, default=0)
1193
+ ret = self.arm_cmd.set_brake(servo_id, value)[0]
1194
+ elif num == 19: # H19 set_mode, ex: H19 V{mode}
1195
+ value = gcode_p.get_int_value(command, default=0)
1196
+ ret = self.set_mode(value)
1197
+ elif num == 31: # H31 set_tcp_jerk, ex: H31 V{jerk}
1198
+ value = gcode_p.get_float_value(command, default=-1)
1199
+ ret = self.set_tcp_jerk(value)
1200
+ elif num == 32: # H32 set_tcp_maxacc, ex: H32 V{maxacc}
1201
+ value = gcode_p.get_float_value(command, default=-1)
1202
+ ret = self.set_tcp_maxacc(value)
1203
+ elif num == 33: # H33 set_joint_jerk, ex: H33 V{jerk}
1204
+ value = gcode_p.get_float_value(command, default=-1)
1205
+ ret = self.set_joint_jerk(value)
1206
+ elif num == 34: # H34 set_joint_maxacc, ex: H34 V{maxacc}
1207
+ value = gcode_p.get_float_value(command, default=-1)
1208
+ ret = self.set_joint_maxacc(value)
1209
+ elif num == 35: # H35 set_tcp_offset, ex: H35 X{x} Y{y} Z{z} A{roll} B{pitch} C{yaw}
1210
+ pose = gcode_p.get_poses(command)
1211
+ ret = self.set_tcp_offset(pose)
1212
+ elif num == 36: # H36 set_tcp_load, ex: H36 I{weight} J{center_x} K{center_y} L{center_z}
1213
+ values = gcode_p.get_joints(command, default=0)
1214
+ ret = self.set_tcp_load(values[0], values[1:4])
1215
+ elif num == 37: # H37 set_collision_sensitivity, ex: H37 V{sensitivity}
1216
+ value = gcode_p.get_int_value(command, default=0)
1217
+ ret = self.set_collision_sensitivity(value)
1218
+ elif num == 38: # H38 set_teach_sensitivity, ex: H38 V{sensitivity}
1219
+ value = gcode_p.get_int_value(command, default=0)
1220
+ ret = self.set_teach_sensitivity(value)
1221
+ elif num == 39: # H39 clean_conf, ex: H39
1222
+ ret = self.clean_conf()
1223
+ elif num == 40: # H40 save_conf, ex: H40
1224
+ ret = self.save_conf()
1225
+ elif num == 41: # H41 get_position, ex: H41
1226
+ ret = self.get_position()
1227
+ elif num == 42: # H42 get_servo_angle, ex: H42
1228
+ ret = self.get_servo_angle()
1229
+ elif num == 43: # H43 get_ik, ex: H43 X{} Y{} Z{} A{roll} B{pitch} C{yaw}
1230
+ pose = gcode_p.get_poses(command, default=0)
1231
+ ret = self.get_inverse_kinematics(pose, input_is_radian=False, return_is_radian=False)
1232
+ elif num == 44: # H44 get_fk, ex: H44 I{} J{} K{} L{} M{} N{} O{}
1233
+ joint = gcode_p.get_joints(command, default=0)
1234
+ ret = self.get_forward_kinematics(joint, input_is_radian=False, return_is_radian=False)
1235
+ elif num == 45: # H45 is_joint_limit, ex: H45 I{} J{} K{} L{} M{} N{} O{}
1236
+ joint = gcode_p.get_joints(command)
1237
+ ret = self.is_joint_limit(joint, is_radian=False)
1238
+ elif num == 46: # H46 is_tcp_limit, ex: H46 X{} Y{} Z{} A{roll} B{pitch} C{yaw}
1239
+ pose = gcode_p.get_poses(command)
1240
+ ret = self.is_tcp_limit(pose, is_radian=False)
1241
+ elif num == 51: # H51 set_gravity_direction, ex: H51 X{} Y{} Z{} A{roll} B{pitch} C{yaw}
1242
+ pose = gcode_p.get_poses(command, default=0)
1243
+ ret = self.set_gravity_direction(pose)
1244
+ elif num == 101: # H101 set_servo_addr_16, ex: H101 I{id} D{addr} V{value}
1245
+ value = gcode_p.get_int_value(command)
1246
+ servo_id = gcode_p.get_id_num(command, default=0)
1247
+ addr = gcode_p.get_addr(command)
1248
+ ret = self.set_servo_addr_16(servo_id=servo_id, addr=addr, value=value)
1249
+ elif num == 102: # H102 get_servo_addr_16, ex: H102 I{id} D{addr}
1250
+ servo_id = gcode_p.get_id_num(command, default=0)
1251
+ addr = gcode_p.get_addr(command)
1252
+ ret = self.get_servo_addr_16(servo_id=servo_id, addr=addr)
1253
+ elif num == 103: # H103 set_servo_addr_32, ex: H103 I{id} D{addr} V{value}
1254
+ servo_id = gcode_p.get_id_num(command, default=0)
1255
+ addr = gcode_p.get_addr(command)
1256
+ value = gcode_p.get_int_value(command)
1257
+ ret = self.set_servo_addr_32(servo_id=servo_id, addr=addr, value=value)
1258
+ elif num == 104: # H104 get_servo_addr_32, ex: H104 I{id} D{addr}
1259
+ servo_id = gcode_p.get_id_num(command, default=0)
1260
+ addr = gcode_p.get_addr(command)
1261
+ ret = self.get_servo_addr_32(servo_id=servo_id, addr=addr)
1262
+ elif num == 105: # H105 set_servo_zero, ex: H105 I{id}
1263
+ servo_id = gcode_p.get_id_num(command, default=0)
1264
+ ret = self.set_servo_zero(servo_id=servo_id)
1265
+ elif num == 106: # H106 get_servo_debug_msg, ex: H106
1266
+ ret = self.get_servo_debug_msg()
1267
+ elif num == 114: # H114 servo_error_addr_r32, ex H114 I{id} D{addr}
1268
+ servo_id = gcode_p.get_id_num(command, default=0)
1269
+ addr = gcode_p.get_addr(command)
1270
+ ret = self.arm_cmd.servo_error_addr_r32(axis=servo_id, addr=addr)
1271
+ else:
1272
+ logger.debug('command {} is not exist'.format(command))
1273
+ ret = APIState.CMD_NOT_EXIST, 'command {} is not exist'.format(command)
1274
+ return ret
1275
+
1276
+ def __handle_gcode_m(num):
1277
+ if num == 116: # M116 set_gripper_enable, ex: M116 V{enable}
1278
+ value = gcode_p.get_int_value(command)
1279
+ ret = self.set_gripper_enable(value)
1280
+ elif num == 117: # M117 set_gripper_mode, ex: M117 V{mode}
1281
+ value = gcode_p.get_int_value(command)
1282
+ ret = self.set_gripper_mode(value)
1283
+ elif num == 118: # M118 set_gripper_zero, ex: M118
1284
+ ret = self.set_gripper_zero()
1285
+ elif num == 119: # M119 get_gripper_position, ex: M119
1286
+ ret = self.get_gripper_position()
1287
+ elif num == 120: # M120 set_gripper_position, ex: M120 V{pos}
1288
+ value = gcode_p.get_int_value(command)
1289
+ ret = self.set_gripper_position(value)
1290
+ elif num == 121: # M121 set_gripper_speed, ex: M121 V{speed}
1291
+ value = gcode_p.get_int_value(command)
1292
+ ret = self.set_gripper_speed(value)
1293
+ elif num == 125: # M125 get_gripper_err_code, ex: M125
1294
+ ret = self.get_gripper_err_code()
1295
+ elif num == 126: # M126 clean_gripper_error, ex: M126
1296
+ ret = self.clean_gripper_error()
1297
+ elif num == 127:
1298
+ ret = self.get_gripper_version()
1299
+ elif num == 131: # M131 get_tgpio_digital, ex: M131
1300
+ ret = self.get_tgpio_digital()
1301
+ elif num == 132: # M132 set_tgpio_digital, ex: M132 I{ionum} V{}
1302
+ ionum = gcode_p.get_id_num(command, default=0)
1303
+ value = gcode_p.get_int_value(command)
1304
+ ret = self.set_tgpio_digital(ionum, value)
1305
+ elif num == 133: # M133 get_tgpio_analog(0), ex: M133 I{ionum=0}
1306
+ ionum = gcode_p.get_id_num(command, default=0)
1307
+ ret = self.get_tgpio_analog(ionum=ionum)
1308
+ elif num == 134: # M134 get_tgpio_analog(1), ex: M134 I{ionum=1}
1309
+ ionum = gcode_p.get_id_num(command, default=0)
1310
+ ret = self.get_tgpio_analog(ionum=ionum)
1311
+ elif num == 135:
1312
+ return self.get_tgpio_version()
1313
+ else:
1314
+ logger.debug('command {} is not exist'.format(command))
1315
+ ret = APIState.CMD_NOT_EXIST, 'command {} is not exist'.format(command)
1316
+ return ret
1317
+
1318
+ def __handle_gcode_d(num):
1319
+ if num == 11: # D11 I{id}
1320
+ id_num = gcode_p.get_id_num(command, default=None)
1321
+ ret = self.get_servo_error_code(id_num)
1322
+ elif num == 12: # D12 I{id}
1323
+ id_num = gcode_p.get_id_num(command, default=None)
1324
+ if id_num == 0:
1325
+ id_num = 8
1326
+ self.clean_error()
1327
+ self.clean_warn()
1328
+ self.motion_enable(enable=False, servo_id=id_num)
1329
+ ret = self.set_servo_detach(id_num)
1330
+ elif num == 13: # D13 I{id}
1331
+ id_num = gcode_p.get_id_num(command, default=None)
1332
+ if id_num == 0:
1333
+ id_num = 8
1334
+ self.set_servo_zero(id_num)
1335
+ ret = self.motion_enable(enable=True, servo_id=id_num)
1336
+ elif num == 21: # D21 I{id}
1337
+ id_num = gcode_p.get_id_num(command, default=None)
1338
+ self.clean_servo_pvl_err(id_num)
1339
+ ret = self.get_servo_error_code(id_num)
1340
+ else:
1341
+ logger.debug('command {} is not exist'.format(command))
1342
+ ret = APIState.CMD_NOT_EXIST, 'command {} is not exist'.format(command)
1343
+ return ret
1344
+
1345
+ def __handle_gcode_s(num):
1346
+ if num == 44: # S44 I{id}
1347
+ id_num = gcode_p.get_id_num(command, default=None)
1348
+ ret = self.get_servo_all_pids(id_num)
1349
+ elif num == 45:
1350
+ id_num = gcode_p.get_id_num(command, default=1)
1351
+ ret = self.get_servo_version(servo_id=id_num)
1352
+ else:
1353
+ logger.debug('command {} is not exist'.format(command))
1354
+ ret = APIState.CMD_NOT_EXIST, 'command {} is not exist'.format(command)
1355
+ return ret
1356
+
1357
+ def __handle_gcode_c(num):
1358
+ if num == 131: # C131 get_cgpio_digital, ex: C131
1359
+ ret = self.get_cgpio_digital()
1360
+ elif num == 132: # C132 get_cgpio_analog(0), ex: C132 I{ionum=0}
1361
+ ionum = gcode_p.get_id_num(command, default=0)
1362
+ ret = self.get_cgpio_analog(ionum)
1363
+ elif num == 133: # C133 get_cgpio_analog(1), ex: C133 I{ionum=1}
1364
+ ionum = gcode_p.get_id_num(command, default=1)
1365
+ ret = self.get_cgpio_analog(ionum)
1366
+ elif num == 134: # C134 set_cgpio_digital, ex: C134 I{ionum} V{value}
1367
+ ionum = gcode_p.get_id_num(command, default=0)
1368
+ value = gcode_p.get_int_value(command)
1369
+ ret = self.set_cgpio_digital(ionum, value)
1370
+ elif num == 135: # C135 set_cgpio_analog(0, v), ex: C135 I{ionum=0} V{value}
1371
+ ionum = gcode_p.get_id_num(command, default=0)
1372
+ value = gcode_p.get_float_value(command)
1373
+ ret = self.set_cgpio_analog(ionum, value)
1374
+ elif num == 136: # C136 set_cgpio_analog(1, v), ex: C136 I{ionum=1} V{value}
1375
+ ionum = gcode_p.get_id_num(command, default=1)
1376
+ value = gcode_p.get_float_value(command)
1377
+ ret = self.set_cgpio_analog(ionum, value)
1378
+ elif num == 137: # C137 set_cgpio_digital_input_function, ex: C137 I{ionum} V{fun}
1379
+ ionum = gcode_p.get_id_num(command, default=0)
1380
+ value = gcode_p.get_int_value(command)
1381
+ ret = self.set_cgpio_digital_input_function(ionum, value)
1382
+ elif num == 138: # C138 set_cgpio_digital_output_function, ex: C138 I{ionum} V{fun}
1383
+ ionum = gcode_p.get_id_num(command, default=0)
1384
+ value = gcode_p.get_int_value(command)
1385
+ ret = self.set_cgpio_digital_output_function(ionum, value)
1386
+ elif num == 139: # C139 get_cgpio_state, ex: C139
1387
+ ret = self.get_cgpio_state()
1388
+ else:
1389
+ logger.debug('command {} is not exist'.format(command))
1390
+ ret = APIState.CMD_NOT_EXIST, 'command {} is not exist'.format(command)
1391
+ return ret
1392
+
1393
+ cmd_num = gcode_p.get_gcode_cmd_num(command, 'G')
1394
+ if cmd_num >= 0:
1395
+ return __handle_gcode_g(cmd_num)
1396
+ cmd_num = gcode_p.get_gcode_cmd_num(command, 'H')
1397
+ if cmd_num >= 0:
1398
+ return __handle_gcode_h(cmd_num)
1399
+ cmd_num = gcode_p.get_gcode_cmd_num(command, 'M')
1400
+ if cmd_num >= 0:
1401
+ return __handle_gcode_m(cmd_num)
1402
+ cmd_num = gcode_p.get_gcode_cmd_num(command, 'D')
1403
+ if cmd_num >= 0:
1404
+ return __handle_gcode_d(cmd_num)
1405
+ cmd_num = gcode_p.get_gcode_cmd_num(command, 'S')
1406
+ if cmd_num >= 0:
1407
+ return __handle_gcode_s(cmd_num)
1408
+ cmd_num = gcode_p.get_gcode_cmd_num(command, 'C')
1409
+ if cmd_num >= 0:
1410
+ return __handle_gcode_c(cmd_num)
1411
+ logger.debug('command {} is not exist'.format(command))
1412
+ return APIState.CMD_NOT_EXIST, 'command {} is not exist'.format(command)
1413
+
1414
+ @xarm_is_connected(_type='set')
1415
+ def run_gcode_file(self, path, **kwargs):
1416
+ times = kwargs.get('times', 1)
1417
+ init = kwargs.get('init', False)
1418
+ mode = kwargs.get('mode', 0)
1419
+ state = kwargs.get('state', 0)
1420
+ wait_seconds = kwargs.get('wait_seconds', 0)
1421
+ try:
1422
+ abs_path = os.path.abspath(path)
1423
+ if not os.path.exists(abs_path):
1424
+ raise FileNotFoundError
1425
+ with open(abs_path, 'r', encoding='utf-8') as f:
1426
+ lines = f.readlines()
1427
+ lines = [line.strip() for line in lines]
1428
+ if init:
1429
+ self.clean_error()
1430
+ self.clean_warn()
1431
+ self.motion_enable(True)
1432
+ self.set_mode(mode)
1433
+ self.set_state(state)
1434
+ if wait_seconds > 0:
1435
+ time.sleep(wait_seconds)
1436
+
1437
+ for i in range(times):
1438
+ for line in lines:
1439
+ line = line.strip()
1440
+ if not line:
1441
+ continue
1442
+ if not self.connected:
1443
+ logger.error('xArm is disconnect')
1444
+ return APIState.NOT_CONNECTED
1445
+ ret = self.send_cmd_sync(line)
1446
+ if isinstance(ret, int) and ret < 0:
1447
+ return ret
1448
+ return APIState.NORMAL
1449
+ except Exception as e:
1450
+ logger.error(e)
1451
+ return APIState.API_EXCEPTION
1452
+
1453
+ @xarm_is_connected(_type='set')
1454
+ def run_blockly_app(self, path, **kwargs):
1455
+ """
1456
+ Run the app generated by xArmStudio software
1457
+ :param path: app path
1458
+ """
1459
+ try:
1460
+ if not os.path.exists(path):
1461
+ dir_name = 'lite6' if self.axis == 6 and self.device_type == 9 else '850' if self.axis == 6 and self.device_type == 12 else 'xarm7T' if self.axis == 7 and self.device_type == 13 else 'xarm{}'.format(self.axis)
1462
+ path = os.path.join('/home/uf' if sys.platform.startswith('linux') else os.path.expanduser('~'), '.UFACTORY', 'projects', 'test', dir_name, 'app', 'myapp', path)
1463
+ if os.path.isdir(path):
1464
+ path = os.path.join(path, 'app.xml')
1465
+ if not os.path.exists(path):
1466
+ raise FileNotFoundError('{} is not found'.format(path))
1467
+ blockly_tool = BlocklyTool(path)
1468
+ succeed = blockly_tool.to_python(arm=self._api_instance, **kwargs)
1469
+ if succeed:
1470
+ times = kwargs.get('times', 1)
1471
+ highlight_callback = kwargs.get('highlight_callback', None)
1472
+ blockly_print = kwargs.get('blockly_print', print)
1473
+ blockly_exec = kwargs.get('blockly_exec', None)
1474
+ blockly_run_gcode = kwargs.get('blockly_run_gcode', None)
1475
+ connect_changed_callbacks = self._report_callbacks[self.REPORT_CONNECT_CHANGED_ID].copy()
1476
+ state_changed_callbacks = self._report_callbacks[self.REPORT_STATE_CHANGED_ID].copy()
1477
+ error_warn_changed_callbacks = self._report_callbacks[self.REPORT_ERROR_WARN_CHANGED_ID].copy()
1478
+ count_changed_callbacks = self._report_callbacks[self.REPORT_COUNT_CHANGED_ID].copy()
1479
+ code = APIState.NORMAL
1480
+ try:
1481
+ for _ in range(times):
1482
+ exec(blockly_tool.codes, {'arm': self._api_instance, 'highlight_callback': highlight_callback,
1483
+ 'print': blockly_print, 'run_blockly': blockly_exec,
1484
+ 'start_run_blockly': blockly_exec, 'start_run_gcode':blockly_run_gcode})
1485
+ except Exception as e:
1486
+ code = APIState.RUN_BLOCKLY_EXCEPTION
1487
+ blockly_print('run blockly app error: {}'.format(e))
1488
+ self._report_callbacks[self.REPORT_CONNECT_CHANGED_ID] = connect_changed_callbacks
1489
+ self._report_callbacks[self.REPORT_STATE_CHANGED_ID] = state_changed_callbacks
1490
+ self._report_callbacks[self.REPORT_ERROR_WARN_CHANGED_ID] = error_warn_changed_callbacks
1491
+ self._report_callbacks[self.REPORT_COUNT_CHANGED_ID] = count_changed_callbacks
1492
+ return code
1493
+ else:
1494
+ logger.error('The conversion is incomplete and some blocks are not yet supported.')
1495
+ return APIState.CONVERT_FAILED
1496
+ except Exception as e:
1497
+ logger.error(e)
1498
+ return APIState.API_EXCEPTION
1499
+
1500
+ @xarm_is_connected(_type='get')
1501
+ def get_hd_types(self):
1502
+ ret = self.arm_cmd.get_hd_types()
1503
+ return ret[0], ret[1:]
1504
+
1505
+ @xarm_is_connected(_type='set')
1506
+ def reload_dynamics(self):
1507
+ ret = self.arm_cmd.reload_dynamics()
1508
+ ret[0] = self._check_code(ret[0])
1509
+ self.log_api_info('API -> reload_dynamics -> code={}'.format(ret[0]), code=ret[0])
1510
+ return ret[0]
1511
+
1512
+ @xarm_wait_until_not_pause
1513
+ @xarm_wait_until_cmdnum_lt_max
1514
+ @xarm_is_ready(_type='set')
1515
+ def set_counter_reset(self):
1516
+ ret = self.arm_cmd.cnter_reset()
1517
+ ret[0] = self._check_code(ret[0])
1518
+ self.log_api_info('API -> set_counter_reset -> code={}'.format(ret[0]), code=ret[0])
1519
+ return ret[0]
1520
+
1521
+ @xarm_wait_until_not_pause
1522
+ @xarm_wait_until_cmdnum_lt_max
1523
+ @xarm_is_ready(_type='set')
1524
+ def set_counter_increase(self, val=1):
1525
+ ret = self.arm_cmd.cnter_plus()
1526
+ ret[0] = self._check_code(ret[0])
1527
+ self.log_api_info('API -> set_counter_increase -> code={}'.format(ret[0]), code=ret[0])
1528
+ return ret[0]
1529
+
1530
+ @xarm_is_connected(_type='set')
1531
+ def set_report_tau_or_i(self, tau_or_i=0):
1532
+ ret = self.arm_cmd.set_report_tau_or_i(int(tau_or_i))
1533
+ ret[0] = self._check_code(ret[0])
1534
+ self.log_api_info('API -> set_report_tau_or_i({}) -> code={}'.format(tau_or_i, ret[0]), code=ret[0])
1535
+ return ret[0]
1536
+
1537
+ @xarm_is_connected(_type='get')
1538
+ def get_report_tau_or_i(self):
1539
+ ret = self.arm_cmd.get_report_tau_or_i()
1540
+ return ret[0], ret[1]
1541
+
1542
+ @xarm_is_connected(_type='set')
1543
+ def set_self_collision_detection(self, on_off):
1544
+ ret = self.arm_cmd.set_self_collision_detection(int(on_off))
1545
+ ret[0] = self._check_code(ret[0])
1546
+ self.log_api_info('API -> set_self_collision_detection({}) -> code={}'.format(on_off, ret[0]), code=ret[0])
1547
+ return ret[0]
1548
+
1549
+ @xarm_is_connected(_type='set')
1550
+ def set_collision_tool_model(self, tool_type, *args, **kwargs):
1551
+ if tool_type == XCONF.CollisionToolType.BOX:
1552
+ assert ('z' in kwargs or len(args) >= 3) \
1553
+ and ('y' in kwargs or len(args) >= 2) \
1554
+ and ('x' in kwargs or len(args) >= 1), 'params error, must specify x,y,z parameter'
1555
+ x = kwargs.get('x') if 'x' in kwargs else args[0]
1556
+ y = kwargs.get('y') if 'y' in kwargs else args[1]
1557
+ z = kwargs.get('z') if 'z' in kwargs else args[2]
1558
+ x_offset = kwargs.get('x_offset') if 'x_offset' in kwargs else args[3] if len(args) >= 4 else 0
1559
+ y_offset = kwargs.get('y_offset') if 'y_offset' in kwargs else args[4] if len(args) >= 5 else 0
1560
+ z_offset = kwargs.get('z_offset') if 'z_offset' in kwargs else args[5] if len(args) >= 6 else 0
1561
+ params = [x, y, z, x_offset, y_offset, z_offset]
1562
+ elif tool_type == XCONF.CollisionToolType.CYLINDER:
1563
+ assert ('radius' in kwargs or len(args) >= 2) \
1564
+ and ('height' in kwargs or len(args) >= 1), 'params error, must specify radius,height parameter'
1565
+ radius = kwargs.get('radius') if 'radius' in kwargs else args[0]
1566
+ height = kwargs.get('height') if 'height' in kwargs else args[1]
1567
+ x_offset = kwargs.get('x_offset') if 'x_offset' in kwargs else args[2] if len(args) >= 3 else 0
1568
+ y_offset = kwargs.get('y_offset') if 'y_offset' in kwargs else args[3] if len(args) >= 4 else 0
1569
+ z_offset = kwargs.get('z_offset') if 'z_offset' in kwargs else args[4] if len(args) >= 5 else 0
1570
+ params = [radius, height, x_offset, y_offset, z_offset]
1571
+ else:
1572
+ params = [] if tool_type < XCONF.CollisionToolType.USE_PRIMITIVES else list(args)
1573
+ ret = self.arm_cmd.set_collision_tool_model(tool_type, params)
1574
+ ret[0] = self._check_code(ret[0])
1575
+ self.log_api_info('API -> set_collision_tool_model({}, {}) -> code={}'.format(tool_type, params, ret[0]), code=ret[0])
1576
+ return ret[0]
1577
+
1578
+ def get_firmware_config(self):
1579
+ cgpio_code, cgpio_states = self.get_cgpio_state()
1580
+ reduced_code, reduced_states = self.get_reduced_states()
1581
+ tau_code, tau_flag = self.get_report_tau_or_i()
1582
+ code = cgpio_code if reduced_code == 0 and tau_code == 0 else reduced_code if cgpio_code == 0 and tau_code == 0 else tau_code
1583
+ return code, {
1584
+ 'COLL_SENS': self.collision_sensitivity, # 碰撞灵敏度
1585
+ 'TEACH_SENS': self.teach_sensitivity, # 示教灵敏度
1586
+ 'GRAV_DIR': self.gravity_direction, # 重力方向
1587
+ 'TCP_LOAD': self.tcp_load, # TCP负载
1588
+ 'TCP_OFFSET': self.position_offset, # TCP偏移
1589
+ 'TCP_MAXACC': self.tcp_acc_limit[1], # TCP的最大加速度
1590
+ 'TCP_JERK': self.tcp_jerk, # TCP加加速度
1591
+ 'JOINT_MAXACC': self.joint_acc_limit[1], # 关节的最大加速度
1592
+ 'JOINT_JERK': self.joint_jerk, # 关节加加速度
1593
+ 'WORLD_OFFSET': self.world_offset, # 基坐标偏移
1594
+ 'REPORT_TAU_OR_I': tau_flag, # 上报力矩还是电流
1595
+ 'CGPIO_INPUT_FUNC_CONFIG': cgpio_states[10], # 控制器数字输入IO的配置功能
1596
+ 'CGPIO_OUTPUT_FUNC_CONFIG': cgpio_states[11], # 控制器数字输出IO的配置功能
1597
+ 'REDUCED_STATES': reduced_states, # 缩减模式的状态
1598
+ 'GPIO_RESET_CONFIG': self.gpio_reset_config, # gpio自动复位配置
1599
+ 'COLL_PARAMS': self.self_collision_params, # 碰撞模型参数
1600
+ }
1601
+
1602
+ def set_firmware_config(self, config):
1603
+ code, old_config = self.get_firmware_config()
1604
+ if 'COLL_SENS' in config and config['COLL_SENS'] != old_config['COLL_SENS']:
1605
+ self.set_collision_sensitivity(config['COLL_SENS'])
1606
+ if 'TEACH_SENS' in config and config['TEACH_SENS'] != old_config['TEACH_SENS']:
1607
+ self.set_teach_sensitivity(config['TEACH_SENS'])
1608
+ if 'GRAV_DIR' in config and config['GRAV_DIR'] != old_config['GRAV_DIR']:
1609
+ self.set_gravity_direction(config['GRAV_DIR'])
1610
+ if 'TCP_LOAD' in config and config['TCP_LOAD'] != old_config['TCP_LOAD']:
1611
+ self.set_tcp_load(*config['TCP_LOAD'])
1612
+ if 'TCP_OFFSET' in config and config['TCP_OFFSET'] != old_config['TCP_OFFSET']:
1613
+ self.set_tcp_offset(config['TCP_OFFSET'])
1614
+ if 'TCP_MAXACC' in config and config['TCP_MAXACC'] != old_config['TCP_MAXACC']:
1615
+ self.set_tcp_maxacc(config['TCP_MAXACC'])
1616
+ if 'TCP_JERK' in config and config['TCP_JERK'] != old_config['TCP_JERK']:
1617
+ self.set_tcp_jerk(config['TCP_JERK'])
1618
+ if 'JOINT_MAXACC' in config and config['JOINT_MAXACC'] != old_config['JOINT_MAXACC']:
1619
+ self.set_joint_maxacc(config['JOINT_MAXACC'])
1620
+ if 'JOINT_JERK' in config and config['JOINT_JERK'] != old_config['JOINT_JERK']:
1621
+ self.set_joint_jerk(config['JOINT_JERK'])
1622
+ if 'WORLD_OFFSET' in config and config['WORLD_OFFSET'] != old_config['WORLD_OFFSET']:
1623
+ self.set_world_offset(config['WORLD_OFFSET'])
1624
+ if 'REPORT_TAU_OR_I' in config and config['REPORT_TAU_OR_I'] != old_config['REPORT_TAU_OR_I']:
1625
+ self.set_report_tau_or_i(config['REPORT_TAU_OR_I'])
1626
+ if 'GPIO_RESET_CONFIG' in config and config['GPIO_RESET_CONFIG'] != old_config['GPIO_RESET_CONFIG']:
1627
+ self.config_io_reset_when_stop(0, config['GPIO_RESET_CONFIG'][0])
1628
+ self.config_io_reset_when_stop(1, config['GPIO_RESET_CONFIG'][1])
1629
+
1630
+ if 'REDUCED_STATES' in config:
1631
+ states = config['REDUCED_STATES']
1632
+ old_states = old_config['REDUCED_STATES']
1633
+ if states[1] != old_states[1]:
1634
+ self.set_reduced_tcp_boundary(states[1])
1635
+ if states[2] != old_states[2]:
1636
+ self.set_reduced_max_tcp_speed(states[2])
1637
+ if states[3] != old_states[3]:
1638
+ self.set_reduced_max_joint_speed(states[3])
1639
+ if len(states) > 4 and len(old_states) > 4:
1640
+ if states[4] != old_states[4]:
1641
+ self.set_reduced_joint_range(states[4])
1642
+ if len(states) > 5 and len(old_states) > 5:
1643
+ if states[5] != old_states[5]:
1644
+ self.set_fense_mode(states[5])
1645
+ if len(states) > 6 and len(old_states) > 6:
1646
+ if states[4] != old_states[6]:
1647
+ self.set_collision_rebound(states[6])
1648
+ self.set_reduced_mode(states[0])
1649
+
1650
+ if 'CGPIO_INPUT_FUNC_CONFIG' in config and config['CGPIO_INPUT_FUNC_CONFIG'] != old_config['CGPIO_INPUT_FUNC_CONFIG']:
1651
+ for i in range(len(config['CGPIO_INPUT_FUNC_CONFIG'])):
1652
+ if config['CGPIO_INPUT_FUNC_CONFIG'][i] != old_config['CGPIO_INPUT_FUNC_CONFIG'][i]:
1653
+ self.set_cgpio_digital_input_function(i, config['CGPIO_INPUT_FUNC_CONFIG'][i])
1654
+ if 'CGPIO_OUTPUT_FUNC_CONFIG' in config and config['CGPIO_OUTPUT_FUNC_CONFIG'] != old_config['CGPIO_OUTPUT_FUNC_CONFIG']:
1655
+ for i in range(len(config['CGPIO_OUTPUT_FUNC_CONFIG'])):
1656
+ if config['CGPIO_OUTPUT_FUNC_CONFIG'][i] != old_config['CGPIO_OUTPUT_FUNC_CONFIG'][i]:
1657
+ self.set_cgpio_digital_output_function(i, config['CGPIO_OUTPUT_FUNC_CONFIG'][i])
1658
+
1659
+ if 'COLL_PARAMS' in config and config['COLL_PARAMS'] != old_config['COLL_PARAMS']:
1660
+ if config['COLL_PARAMS'][0] != old_config['COLL_PARAMS'][0]:
1661
+ self.set_self_collision_detection(config['COLL_PARAMS'][0])
1662
+ if config['COLL_PARAMS'][1] != old_config['COLL_PARAMS'][1] or config['COLL_PARAMS'][2] != old_config['COLL_PARAMS'][2]:
1663
+ self.set_collision_tool_model(config['COLL_PARAMS'][1], *config['COLL_PARAMS'][2])
1664
+
1665
+ self.save_conf()
1666
+
1667
+ @xarm_is_connected(_type='get')
1668
+ def get_power_board_version(self):
1669
+ ret = self.arm_cmd.get_power_board_version()
1670
+ ret[0] = self._check_code(ret[0])
1671
+ return ret[0], ret[1:]
1672
+
1673
+ @xarm_is_connected(_type='get')
1674
+ def get_movement(self):
1675
+ ret = self.arm_cmd.get_movement()
1676
+ ret[0] = self._check_code(ret[0])
1677
+ return ret[0], ret[1]
1678
+
1679
+ @xarm_is_connected(_type='set')
1680
+ def vc_set_joint_velocity(self, speeds, is_radian=None, is_sync=True, check_mode=True, duration=-1):
1681
+ # if check_mode and not self._check_mode_is_correct(4):
1682
+ # return APIState.MODE_IS_NOT_CORRECT
1683
+ is_radian = self._default_is_radian if is_radian is None else is_radian
1684
+ jnt_v = [0] * 7
1685
+ for i, spd in enumerate(speeds):
1686
+ if i >= 7:
1687
+ break
1688
+ jnt_v[i] = to_radian(spd, is_radian)
1689
+
1690
+ ret = self.arm_cmd.vc_set_jointv(jnt_v, 1 if is_sync else 0, duration if self.version_is_ge(1, 8, 0) else -1)
1691
+ ret[0] = self._check_code(ret[0], is_move_cmd=True, mode=4)
1692
+ self.log_api_info('API -> vc_set_joint_velocity -> code={}, speeds={}, is_sync={}'.format(
1693
+ ret[0], jnt_v, is_sync
1694
+ ), code=ret[0])
1695
+ return ret[0]
1696
+
1697
+ @xarm_is_connected(_type='set')
1698
+ def vc_set_cartesian_velocity(self, speeds, is_radian=None, is_tool_coord=False, check_mode=True, duration=-1):
1699
+ # if check_mode and not self._check_mode_is_correct(5):
1700
+ # return APIState.MODE_IS_NOT_CORRECT
1701
+ is_radian = self._default_is_radian if is_radian is None else is_radian
1702
+ line_v = [0] * 6
1703
+ for i, spd in enumerate(speeds):
1704
+ if i >= 6:
1705
+ break
1706
+ line_v[i] = spd if i <= 2 else to_radian(spd, is_radian)
1707
+ ret = self.arm_cmd.vc_set_linev(line_v, 1 if is_tool_coord else 0, duration if self.version_is_ge(1, 8, 0) else -1)
1708
+ ret[0] = self._check_code(ret[0], is_move_cmd=True, mode=5)
1709
+ self.log_api_info('API -> vc_set_cartesian_velocity -> code={}, speeds={}, is_tool_coord={}'.format(
1710
+ ret[0], line_v, is_tool_coord
1711
+ ), code=ret[0])
1712
+ return ret[0]
1713
+
1714
+ @xarm_is_connected(_type='get')
1715
+ def calibrate_tcp_coordinate_offset(self, four_points, is_radian=None):
1716
+ assert len(four_points) >= 4, 'The parameter four_points must contain 4 TCP points'
1717
+ is_radian = self._default_is_radian if is_radian is None else is_radian
1718
+ points = []
1719
+ for i in range(4):
1720
+ assert len(four_points[i]) >= 6, 'Each TCP point in the parameter four_points must contain x/y/z/roll/pitch/yaw'
1721
+ points.append([four_points[i][j] if j <= 2 else to_radian(four_points[i][j], is_radian) for j in range(6)])
1722
+ ret = self.arm_cmd.cali_tcp_pose(points)
1723
+ ret[0] = self._check_code(ret[0])
1724
+ return ret[0], ret[1:]
1725
+
1726
+ @xarm_is_connected(_type='get')
1727
+ def calibrate_tcp_orientation_offset(self, rpy_be, rpy_bt, input_is_radian=None, return_is_radian=None):
1728
+ input_is_radian = self._default_is_radian if input_is_radian is None else input_is_radian
1729
+ return_is_radian = self._default_is_radian if return_is_radian is None else return_is_radian
1730
+ rpy_be_ = [to_radian(rpy_be[i], input_is_radian) for i in range(3)]
1731
+ rpy_bt_ = [to_radian(rpy_bt[i], input_is_radian) for i in range(3)]
1732
+ ret = self.arm_cmd.cali_tcp_orient(rpy_be_, rpy_bt_)
1733
+ ret[0] = self._check_code(ret[0])
1734
+ return ret[0], [ret[i+1] if return_is_radian else math.degrees(ret[i+1]) for i in range(3)]
1735
+
1736
+ @xarm_is_connected(_type='get')
1737
+ def calibrate_user_orientation_offset(self, three_points, mode=0, trust_ind=0, input_is_radian=None, return_is_radian=None):
1738
+ assert len(three_points) >= 3, 'The parameter three_points must contain 3 TCP points'
1739
+ input_is_radian = self._default_is_radian if input_is_radian is None else input_is_radian
1740
+ return_is_radian = self._default_is_radian if return_is_radian is None else return_is_radian
1741
+ points = []
1742
+ for i in range(3):
1743
+ assert len(three_points[i]) >= 6, 'Each TCP point in the parameter three_points must contain x/y/z/roll/pitch/yaw'
1744
+ points.append([three_points[i][j] if j <= 2 else to_radian(three_points[i][j], input_is_radian) for j in range(6)])
1745
+ ret = self.arm_cmd.cali_user_orient(points, mode=mode, trust_ind=trust_ind)
1746
+ ret[0] = self._check_code(ret[0])
1747
+ return ret[0], [ret[i+1] if return_is_radian else math.degrees(ret[i+1]) for i in range(3)]
1748
+
1749
+ @xarm_is_connected(_type='get')
1750
+ def calibrate_user_coordinate_offset(self, rpy_ub, pos_b_uorg, is_radian=None):
1751
+ is_radian = self._default_is_radian if is_radian is None else is_radian
1752
+ rpy_ub_ = [rpy_ub[i] if is_radian else math.radians(rpy_ub[i]) for i in range(3)]
1753
+ ret = self.arm_cmd.cali_user_pos(rpy_ub_, pos_b_uorg)
1754
+ ret[0] = self._check_code(ret[0])
1755
+ return ret[0], ret[1:4]
1756
+
1757
+ @xarm_is_connected(_type='set')
1758
+ def get_tcp_rotation_radius(self, value=6):
1759
+ ret = self.arm_cmd.get_tcp_rotation_radius(value)
1760
+ self.log_api_info('API -> get_tcp_rotation_radius -> code={}'.format(ret[0]), code=ret[0])
1761
+ ret[0] = self._check_code(ret[0])
1762
+ return ret[0], ret[1][0]
1763
+
1764
+ @xarm_is_connected(_type='set')
1765
+ def get_max_joint_velocity(self, eveloc, joint_pos, is_radian=None, return_is_radian=True):
1766
+ """
1767
+ Obtain maximum joint angular velocity
1768
+ :param eveloc: Maximum TCP speed
1769
+ :param joint_pos: joint angle list (unit: rad if is_radian is True else °), angle should be a list of values
1770
+ whose length is the number of joints like [axis-1, axis-2, axis-3, axis-4, axis-5, axis-6, axis-7]
1771
+ :param is_radian: the param angles value is in radians or not, default is self.default_is_radian
1772
+ :param return_is_radian: the returned value is in radians or not, default is True
1773
+ """
1774
+ is_radian = self._default_is_radian if is_radian is None else is_radian
1775
+ joints = [0] * 7
1776
+ for i in range(min(len(joint_pos), 7)):
1777
+ joints[i] = to_radian(joint_pos[i], is_radian)
1778
+ ret = self.arm_cmd.get_max_joint_velocity(eveloc, joints)
1779
+ return ret if return_is_radian else math.radians(ret)
1780
+
1781
+ @xarm_is_connected(_type='get')
1782
+ def iden_tcp_load(self, estimated_mass=0):
1783
+ protocol_identifier = self.arm_cmd.get_protocol_identifier()
1784
+ self.arm_cmd.set_protocol_identifier(2)
1785
+ self._keep_heart = False
1786
+ if self.version_is_ge(1, 9, 100) and estimated_mass <= 0:
1787
+ estimated_mass = 0.5
1788
+ ret = self.arm_cmd.iden_tcp_load(estimated_mass)
1789
+ self.arm_cmd.set_protocol_identifier(protocol_identifier)
1790
+ self._keep_heart = True
1791
+ self.log_api_info('API -> iden_tcp_load -> code={}'.format(ret[0]), code=ret[0])
1792
+ return self._check_code(ret[0]), ret[1:5]
1793
+
1794
+ @xarm_is_connected(_type='set')
1795
+ def set_cartesian_velo_continuous(self, on_off):
1796
+ ret = self.arm_cmd.set_cartesian_velo_continuous(int(on_off))
1797
+ ret[0] = self._check_code(ret[0])
1798
+ self.log_api_info('API -> set_cartesian_velo_continuous({}) -> code={}'.format(on_off, ret[0]), code=ret[0])
1799
+ return ret[0]
1800
+
1801
+ @xarm_is_connected(_type='set')
1802
+ def set_allow_approx_motion(self, on_off):
1803
+ ret = self.arm_cmd.set_allow_approx_motion(int(on_off))
1804
+ ret[0] = self._check_code(ret[0])
1805
+ self.log_api_info('API -> set_allow_approx_motion({}) -> code={}'.format(on_off, ret[0]), code=ret[0])
1806
+ return ret[0]
1807
+
1808
+ @xarm_is_connected(_type='get')
1809
+ def get_allow_approx_motion(self):
1810
+ ret = self.arm_cmd.get_allow_approx_motion()
1811
+ ret[0] = self._check_code(ret[0])
1812
+ self.log_api_info('API -> get_allow_approx_motion() -> code={}'.format(ret[0]), code=ret[0])
1813
+ return ret[0], ret[-1]
1814
+
1815
+ @xarm_is_connected(_type='get')
1816
+ def iden_joint_friction(self, sn=None):
1817
+ if sn is None:
1818
+ code, sn = self.get_robot_sn()
1819
+ if code != 0:
1820
+ self.log_api_info('iden_joint_friction -> get_robot_sn failed, code={}'.format(code), code=code)
1821
+ return APIState.API_EXCEPTION, -1
1822
+ if len(sn) != 14:
1823
+ self.log_api_info('iden_joint_friction, sn is not correct, sn={}'.format(sn), code=APIState.API_EXCEPTION)
1824
+ return APIState.API_EXCEPTION, -1
1825
+ sn = sn.upper()
1826
+ axis_map = {5: 'F', 6: 'I', 7: 'S'}
1827
+ valid_850 = self.is_850 and sn[0] == 'F' and sn[1] == 'X'
1828
+ valid_lite = self.is_lite6 and sn[0] == 'L' and sn[1] == 'I'
1829
+ valid_xarm7t = not self.is_850 and not self.is_lite6 and sn[0] == 'C' and sn[1] == 'S'
1830
+ valid_xarm = not self.is_850 and not self.is_lite6 and sn[0] == 'X' and sn[1] == axis_map.get(self.axis, '')
1831
+ if not (valid_850 or valid_lite or valid_xarm or valid_xarm7t):
1832
+ self.log_api_info('iden_joint_friction, sn is not correct, axis={}, type={}, sn={}'.format(self.axis, self.device_type, sn), code=APIState.API_EXCEPTION)
1833
+ return APIState.API_EXCEPTION, -1
1834
+
1835
+ protocol_identifier = self.arm_cmd.get_protocol_identifier()
1836
+ self.arm_cmd.set_protocol_identifier(2)
1837
+ self._keep_heart = False
1838
+ ret = self.arm_cmd.iden_joint_friction(sn)
1839
+ self.arm_cmd.set_protocol_identifier(protocol_identifier)
1840
+ self._keep_heart = True
1841
+ self.log_api_info('API -> iden_joint_friction -> code={}'.format(ret[0]), code=ret[0])
1842
+ return self._check_code(ret[0]), 0 if int(ret[1]) == 0 else -1
1843
+
1844
+ @xarm_wait_until_not_pause
1845
+ @xarm_wait_until_cmdnum_lt_max
1846
+ @xarm_is_ready(_type='set')
1847
+ def wait_all_task_finish(self, timeout=None, **kwargs):
1848
+ if not self._support_feedback:
1849
+ return APIState.CMD_NOT_EXIST
1850
+ wait = kwargs.pop('wait', True)
1851
+ feedback_key, studio_wait = self._gen_feedback_key(wait, **kwargs)
1852
+ ret = self.arm_cmd.check_feedback(feedback_key=feedback_key)
1853
+ trans_id = self._get_feedback_transid(feedback_key, studio_wait)
1854
+ ret[0] = self._check_code(ret[0])
1855
+ if wait and ret[0] == 0:
1856
+ ret[0] = self._wait_feedback(timeout, trans_id=trans_id, ignore_log=True)[0]
1857
+ if ret[0] == 0:
1858
+ time.sleep(0.5)
1859
+ return ret[0]
1860
+
1861
+ @xarm_wait_until_not_pause
1862
+ @xarm_wait_until_cmdnum_lt_max
1863
+ @xarm_is_ready(_type='set')
1864
+ def send_hex_cmd(self, datas, timeout=10):
1865
+ ret = self.arm_cmd.send_hex_cmd(datas, timeout)
1866
+ return self._check_code(ret[0]), ret[1:]
1867
+ # ret = self.arm_cmd.send_hex_request(datas)
1868
+ # if ret == -1:
1869
+ # return [XCONF.UxbusState.ERR_NOTTCP]
1870
+ # ret = self.arm_cmd.recv_hex_request(ret, timeout)
1871
+ # return ret
1872
+
1873
+ @xarm_is_connected(_type='get')
1874
+ def get_trans_id(self):
1875
+ return self.arm_cmd.get_trans_id()
1876
+
1877
+ @xarm_is_connected(_type='set')
1878
+ def run_gcode_app(self, path=None, **kwargs):
1879
+ sock = None
1880
+ try:
1881
+ if not os.path.exists(path):
1882
+ dir_name = 'lite6' if self.axis == 6 and self.device_type == 9 else '850' if self.axis == 6 and self.device_type == 12 else 'xarm7T' if self.axis == 7 and self.device_type == 13 else 'xarm{}'.format(
1883
+ self.axis)
1884
+ path = os.path.join('/home/uf' if sys.platform.startswith('linux') else os.path.expanduser('~'), '.UFACTORY', 'projects', 'test', dir_name, 'gcode', path)
1885
+ if not os.path.exists(path):
1886
+ raise FileNotFoundError('{} is not found'.format(path))
1887
+ sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
1888
+ sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
1889
+ sock.setblocking(True)
1890
+ sock.settimeout(10)
1891
+ sock.connect((self._port, 504))
1892
+ with open(path, 'r') as f:
1893
+ datas = f.read()
1894
+ lines = datas.split('\n')
1895
+ is_continue = False
1896
+ code = APIState.NORMAL
1897
+ for line in lines:
1898
+ line = line.strip()
1899
+ if line.startswith(';('):
1900
+ is_continue = True
1901
+ if not line or is_continue:
1902
+ if line.endswith(')'):
1903
+ is_continue = False
1904
+ continue
1905
+ if line.startswith(';'):
1906
+ continue
1907
+ GCODE_PATTERN = r'([A-Z])([-+]?[0-9.]+)'
1908
+ CLEAN_PATTERN = r'\s+|\(.*?\)|;.*'
1909
+ data = re.sub(CLEAN_PATTERN, '', line.strip().upper())
1910
+ if not data:
1911
+ return -14
1912
+ if data[0] == '%':
1913
+ return -15
1914
+ if not re.findall(GCODE_PATTERN, data):
1915
+ return -16
1916
+
1917
+ sock.send(line.strip().encode('utf-8', 'replace') + b'\n')
1918
+ err, status, code, buffer1, buffer2 = sock.recv(5)
1919
+ if err != 0 or code != 0:
1920
+ return err if err != 0 else code
1921
+ self.wait_move(set_cnt=5)
1922
+ return code
1923
+ except Exception as e:
1924
+ code = -13
1925
+ return code
1926
+ finally:
1927
+ if sock:
1928
+ sock.close()