Skip to content

Commit 5d1336f

Browse files
committed
Feat: rebuild the blockly to python
1 parent 781c2e0 commit 5d1336f

File tree

7 files changed

+1715
-3
lines changed

7 files changed

+1715
-3
lines changed

xarm/tools/blockly/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
from ._blockly_tool import BlocklyTool
Lines changed: 337 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,337 @@
1+
#!/usr/bin/env python3
2+
# Software License Agreement (BSD License)
3+
#
4+
# Copyright (c) 2022, UFACTORY, Inc.
5+
# All rights reserved.
6+
#
7+
# Author: Vinman <vinman.wen@ufactory.cc> <vinman.cub@gmail.com>
8+
9+
from ._blockly_node import _BlocklyNode
10+
11+
OPS_MAP = {
12+
'EQ': '==',
13+
'NEQ': '!=',
14+
'LT': '<',
15+
'LTE': '<=',
16+
'GT': '>',
17+
'GTE': '>=',
18+
'===': '==',
19+
'!==': '!=',
20+
'>=': '>=',
21+
'>': '>',
22+
'<=': '<=',
23+
'<': '<',
24+
}
25+
26+
27+
class _BlocklyBase(_BlocklyNode):
28+
def __init__(self, xml_path):
29+
super(_BlocklyBase, self).__init__(xml_path)
30+
self._funcs = {}
31+
self._define_is_prime_func = False
32+
self._define_bin_matchs_func = False
33+
34+
def _get_field_value(self, block):
35+
field = self._get_node('field', root=block)
36+
if field is not None:
37+
return field.text
38+
else:
39+
return self._get_nodes('field', root=self._get_node('value', root=block), descendant=True)[0].text
40+
41+
def _get_block_val(self, block, arg_map=None):
42+
block_v = self._get_node('block', root=block)
43+
if block_v is not None:
44+
val = self._get_condition_expression(block, arg_map=arg_map)
45+
else:
46+
shadow = self._get_node('shadow', root=block)
47+
val = self._get_node('field', root=shadow).text
48+
return val
49+
50+
def _get_condition_expression(self, value_block, arg_map=None):
51+
block = self._get_node('block', value_block)
52+
if block is None:
53+
shadow = self._get_node('shadow', root=value_block)
54+
return self._get_node('field', root=shadow).text
55+
if block.attrib['type'] == 'logic_boolean':
56+
return str(self._get_node('field', block).text == 'TRUE')
57+
elif block.attrib['type'] == 'logic_compare':
58+
return self.__get_logic_compare(block, arg_map=arg_map)
59+
elif block.attrib['type'] == 'logic_operation':
60+
return self.__get_logic_operation(block, arg_map=arg_map)
61+
elif block.attrib['type'] == 'logic_negate':
62+
value = self._get_node('value', root=block)
63+
return 'not ({})'.format(self._get_condition_expression(value, arg_map=arg_map))
64+
elif block.attrib['type'] == 'gpio_get_digital':
65+
io = self._get_node('field', block).text
66+
return 'self._arm.get_tgpio_digital({})[{}]'.format(io, 1)
67+
elif block.attrib['type'] == 'gpio_get_analog':
68+
io = self._get_node('field', block).text
69+
return 'self._arm.get_tgpio_analog({})[{}]'.format(io, 1)
70+
elif block.attrib['type'] == 'gpio_get_controller_digital':
71+
io = self._get_node('field', block).text
72+
return 'self._arm.get_cgpio_digital({})[{}]'.format(io, 1)
73+
elif block.attrib['type'] == 'gpio_get_controller_digital_di':
74+
io = self._get_node('field', block).text
75+
return 'self._arm.get_cgpio_digital({})[{}]'.format(io, 1)
76+
elif block.attrib['type'] == 'gpio_get_controller_analog':
77+
io = self._get_node('field', block).text
78+
return 'self._arm.get_cgpio_analog({})[{}]'.format(io, 1)
79+
elif block.attrib['type'] == 'gpio_get_ci':
80+
io = self._get_node('field', block).text
81+
return '1 if self._cgpio_state is None else self._cgpio_state[3] >> {} & 0x0001'.format(io)
82+
elif block.attrib['type'] == 'gpio_get_co':
83+
io = self._get_node('field', block).text
84+
return '0 if self._cgpio_state is None else self._cgpio_state[5] >> {} & 0x0001'.format(io)
85+
elif block.attrib['type'] == 'gpio_get_ai':
86+
io = self._get_node('field', block).text
87+
return '0 if self._cgpio_state is None else self._cgpio_state[6 + {}]'.format(io)
88+
elif block.attrib['type'] == 'gpio_get_ao':
89+
io = self._get_node('field', block).text
90+
return '0 if self._cgpio_state is None else self._cgpio_state[8 + {}]'.format(io)
91+
elif block.attrib['type'] == 'gpio_match_controller_digitals_bin':
92+
bin_val = self._get_node('field', block).text
93+
self._define_bin_matchs_func = True
94+
return 'self._cgpio_digitals_is_matchs_bin(\'{}\')'.format(bin_val)
95+
elif block.attrib['type'] == 'get_suction_cup':
96+
return 'self._arm.get_suction_cup()[{}]'.format(1)
97+
elif block.attrib['type'] == 'check_air_pump_state':
98+
fields = self._get_nodes('field', root=block)
99+
state = 1 if fields[0].text == 'ON' else 0
100+
timeout = float(fields[1].text)
101+
return 'self._arm.arm.check_air_pump_state({}, timeout={})'.format(state, timeout)
102+
elif block.attrib['type'] == 'check_bio_gripper_is_catch':
103+
fields = self._get_nodes('field', root=block)
104+
timeout = float(fields[0].text)
105+
return 'self._arm.arm.check_bio_gripper_is_catch(timeout={})'.format(timeout)
106+
elif block.attrib['type'] == 'check_robotiq_is_catch':
107+
fields = self._get_nodes('field', root=block)
108+
timeout = float(fields[0].text)
109+
return 'self._arm.arm.check_robotiq_is_catch(timeout={})'.format(timeout)
110+
elif block.attrib['type'] == 'math_number':
111+
val = self._get_node('field', block).text
112+
return val
113+
elif block.attrib['type'] == 'math_arithmetic':
114+
return self.__get_math_arithmetic(block, arg_map=arg_map)
115+
elif block.attrib['type'] == 'math_number_property':
116+
return self.__get_math_number_property(block, arg_map=arg_map)
117+
elif block.attrib['type'] == 'math_random_int':
118+
return self.__get_math_random_int(block, arg_map=arg_map)
119+
elif block.attrib['type'] == 'math_round':
120+
return self.__get_math_round(block, arg_map=arg_map)
121+
elif block.attrib['type'] == 'math_single':
122+
# 算术函数
123+
return self.__get_math_single(block, arg_map=arg_map)
124+
elif block.attrib['type'] == 'math_trig':
125+
# 三角函数
126+
return self.__get_math_trig(block, arg_map=arg_map)
127+
elif block.attrib['type'] == 'math_constant':
128+
# 常量
129+
return self.__get_math_constant(block, arg_map=arg_map)
130+
elif block.attrib['type'] == 'math_modulo':
131+
return self.__get_math_modulo(block, arg_map=arg_map)
132+
elif block.attrib['type'] == 'math_constrain':
133+
return self.__get_math_constrain(block, arg_map=arg_map)
134+
elif block.attrib['type'] == 'variables_get':
135+
field = self._get_node('field', block).text
136+
if arg_map and field in arg_map:
137+
return '{}'.format(arg_map[field])
138+
else:
139+
return 'self._variables.get(\'{}\', 0)'.format(field)
140+
elif block.attrib['type'] == 'move_var':
141+
val = self._get_node('field', block).text
142+
return val
143+
elif block.attrib['type'] == 'tool_get_date':
144+
return 'datetime.datetime.now()'
145+
elif block.attrib['type'] == 'tool_combination':
146+
field = self._get_node('field', block).text
147+
values = self._get_nodes('value', block)
148+
var1 = self._get_condition_expression(values[0], arg_map=arg_map)
149+
var2 = self._get_condition_expression(values[1], arg_map=arg_map)
150+
return '\'{{}}{{}}{{}}\'.format({}, \'{}\', {})'.format(var1, field, var2)
151+
elif block.attrib['type'] == 'procedures_callreturn':
152+
mutation = self._get_node('mutation', block).attrib['name']
153+
if not mutation:
154+
mutation = '1'
155+
if mutation in self._funcs:
156+
name = self._funcs[mutation]
157+
else:
158+
name = 'function_{}'.format(len(self._funcs) + 1)
159+
160+
args = self._get_nodes('arg', root=self._get_node('mutation', block))
161+
values = self._get_nodes('value', root=block)
162+
if args and values and len(args) == len(values):
163+
return 'self.{}({})'.format(name, ','.join(
164+
[self._get_condition_expression(val, arg_map=arg_map) for val in values]))
165+
else:
166+
return 'self.{}()'.format(name)
167+
168+
def __get_logic_compare(self, block, arg_map=None):
169+
op = OPS_MAP.get(self._get_node('field', block).text)
170+
cond_a = 0
171+
cond_b = 0
172+
values = self._get_nodes('value', block)
173+
if len(values) > 0:
174+
cond_a = self._get_condition_expression(values[0], arg_map=arg_map)
175+
if len(values) > 1:
176+
cond_b = self._get_condition_expression(values[1], arg_map=arg_map)
177+
return '{} {} {}'.format(cond_a, op, cond_b)
178+
179+
def __get_logic_operation(self, block, arg_map=None):
180+
op = self._get_node('field', block).text.lower()
181+
cond_a = False
182+
cond_b = False
183+
values = self._get_nodes('value', block)
184+
if len(values) > 0:
185+
cond_a = self._get_condition_expression(values[0], arg_map=arg_map)
186+
if len(values) > 1:
187+
cond_b = self._get_condition_expression(values[1], arg_map=arg_map)
188+
return '{} {} {}'.format(cond_a, op, cond_b)
189+
190+
def __get_math_arithmetic(self, block, arg_map=None):
191+
field = self._get_node('field', block).text
192+
values = self._get_nodes('value', block)
193+
if len(values) > 1:
194+
val_a = self._get_block_val(values[0], arg_map=arg_map)
195+
val_b = self._get_block_val(values[1], arg_map=arg_map)
196+
if field == 'ADD':
197+
return '({} + {})'.format(val_a, val_b)
198+
elif field == 'MINUS':
199+
return '({} - {})'.format(val_a, val_b)
200+
elif field == 'MULTIPLY':
201+
return '({} * {})'.format(val_a, val_b)
202+
elif field == 'DIVIDE':
203+
return '({} / {})'.format(val_a, val_b)
204+
elif field == 'POWER':
205+
return 'pow({}, {})'.format(val_a, val_b)
206+
207+
def __get_math_number_property(self, block, arg_map=None):
208+
field = self._get_node('field', block).text
209+
values = self._get_nodes('value', block)
210+
if len(values) >= 1:
211+
val_a = self._get_block_val(values[0], arg_map=arg_map)
212+
213+
if field == 'EVEN':
214+
# 偶数
215+
return '{} % 2 == 0'.format(val_a)
216+
elif field == 'ODD':
217+
# 奇数
218+
return '{} % 2 == 1'.format(val_a)
219+
elif field == 'PRIME':
220+
# 质数
221+
self._define_is_prime_func = True
222+
return 'self.is_prime({})'.format(val_a)
223+
elif field == 'WHOLE':
224+
# 整数
225+
return '{} % 1 == 0'.format(val_a)
226+
elif field == 'POSITIVE':
227+
# 正数
228+
return '{} > 0'.format(val_a)
229+
elif field == 'NEGATIVE':
230+
# 负数
231+
return '{} < 0'.format(val_a)
232+
elif field == 'DIVISIBLE_BY':
233+
# 可被整除
234+
if len(values) > 1:
235+
val_b = self._get_block_val(values[1], arg_map=arg_map)
236+
else:
237+
val_b = 0
238+
return '{} % {} == 0'.format(val_a, val_b)
239+
240+
def __get_math_random_int(self, block, arg_map=None):
241+
values = self._get_nodes('value', block)
242+
if len(values) > 1:
243+
val_a = self._get_block_val(values[0], arg_map=arg_map)
244+
val_b = self._get_block_val(values[1], arg_map=arg_map)
245+
return 'random.randint({}, {})'.format(val_a, val_b)
246+
247+
def __get_math_round(self, block, arg_map=None):
248+
field = self._get_node('field', block).text
249+
values = self._get_nodes('value', block)
250+
if len(values) >= 1:
251+
val_a = self._get_block_val(values[0], arg_map=arg_map)
252+
if field == 'ROUND':
253+
# 四舍五入
254+
return 'round({})'.format(val_a)
255+
elif field == 'ROUNDUP':
256+
# 上舍入
257+
return 'math.ceil({})'.format(val_a)
258+
elif field == 'ROUNDDOWN':
259+
# 下舍入
260+
return 'math.floor({})'.format(val_a)
261+
262+
def __get_math_single(self, block, arg_map=None):
263+
# 算术函数
264+
field = self._get_node('field', block).text
265+
values = self._get_nodes('value', block)
266+
if len(values) >= 1:
267+
val_a = self._get_block_val(values[0], arg_map=arg_map)
268+
if field == 'ROOT':
269+
# 平方根
270+
return 'math.sqrt({})'.format(val_a)
271+
elif field == 'ABS':
272+
# 绝对值
273+
return 'abs({})'.format(val_a)
274+
elif field == 'NEG':
275+
# 相反数
276+
return '-{}'.format(val_a)
277+
elif field == 'LN':
278+
# ln
279+
return 'math.log({})'.format(val_a)
280+
elif field == 'LOG10':
281+
# log10
282+
return '(math.log({}) / math.log(10))'.format(val_a)
283+
elif field == 'EXP':
284+
# exp
285+
return 'math.exp({})'.format(val_a)
286+
elif field == 'POW10':
287+
# 10的多少次方
288+
return 'math.pow(10, {})'.format(val_a)
289+
290+
def __get_math_trig(self, block, arg_map=None):
291+
# 三角函数
292+
field = self._get_node('field', block).text
293+
values = self._get_nodes('value', block)
294+
if len(values) >= 1:
295+
val_a = self._get_block_val(values[0], arg_map=arg_map)
296+
if field == 'SIN':
297+
return 'math.sin({})'.format(val_a)
298+
elif field == 'COS':
299+
return 'math.cos({})'.format(val_a)
300+
elif field == 'TAN':
301+
return 'math.tan({})'.format(val_a)
302+
elif field == 'ASIN':
303+
return 'math.asin({})'.format(val_a)
304+
elif field == 'ACOS':
305+
return 'math.acos({})'.format(val_a)
306+
elif field == 'ATAN':
307+
return 'math.atan({})'.format(val_a)
308+
309+
def __get_math_constant(self, block, arg_map=None):
310+
field = self._get_node('field', block).text
311+
if field == 'PI':
312+
return 'math.pi'
313+
elif field == 'E':
314+
return 'math.e'
315+
elif field == 'GOLDEN_RATIO':
316+
return '(1 + math.sqrt(5)) / 2'
317+
elif field == 'SQRT2':
318+
return 'math.sqrt(2)'
319+
elif field == 'SQRT1_2':
320+
return 'math.sqrt(0.5)'
321+
elif field == 'INFINITY':
322+
return 'math.inf'
323+
324+
def __get_math_modulo(self, block, arg_map=None):
325+
values = self._get_nodes('value', block)
326+
if len(values) > 1:
327+
val_a = self._get_block_val(values[0], arg_map=arg_map)
328+
val_b = self._get_block_val(values[1], arg_map=arg_map)
329+
return '{} % {}'.format(val_a, val_b)
330+
331+
def __get_math_constrain(self, block, arg_map=None):
332+
values = self._get_nodes('value', block)
333+
if len(values) > 2:
334+
val_a = self._get_block_val(values[0], arg_map=arg_map)
335+
val_b = self._get_block_val(values[1], arg_map=arg_map)
336+
val_c = self._get_block_val(values[2], arg_map=arg_map)
337+
return 'min(max({}, {}), {})'.format(val_a, val_b, val_c)

0 commit comments

Comments
 (0)