Skip to content

Commit 981bcf0

Browse files
committed
params && quick3d transition
1 parent cba3745 commit 981bcf0

22 files changed

Lines changed: 766 additions & 20 deletions

server/src/xbot2_gui_server/launcher_config.yaml

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,22 @@ context:
66
roscore:
77
cmd: ros1; roscore
88

9+
ecat:
10+
cmd: watch ls
11+
12+
wait_ecat_online:
13+
cmd: watch ls
14+
915
xbot2:
10-
cmd: ros1; xbot2-core --hw dummy -C $(rospack find centauro_config)/centauro_basic.yaml
16+
cmd: ros1; xbot2-core --hw dummy -C $(rospack find kyon_config)/kyon_basic.yaml
1117
variants:
1218
verbose:
1319
params:
14-
verbose_flag: -V
20+
verbose_flag: -V
21+
ready_check: timeout 3 rostopic echo /xbotcore/status -n 1
22+
23+
prova1:
24+
cmd: watch ls
25+
26+
prova2:
27+
cmd: watch ls
Lines changed: 253 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,253 @@
1+
import asyncio
2+
from aiohttp import web
3+
import json
4+
import yaml
5+
6+
import rospy
7+
from std_srvs.srv import SetBool, Trigger
8+
from std_msgs.msg import String
9+
from geometry_msgs.msg import TwistStamped, Twist
10+
from xbot_msgs.msg import Statistics2
11+
from xbot_msgs.srv import GetParameterInfo, SetString
12+
13+
from .server import ServerBase
14+
from . import utils
15+
from . import launcher
16+
17+
18+
class ParameterHandler:
19+
20+
def __init__(self, srv: ServerBase, config=dict()) -> None:
21+
22+
self.requested_pages = ['Parameters']
23+
24+
self.rate = config.get('rate', 10.0)
25+
26+
self.srv = srv
27+
28+
self.srv.schedule_task(self.run())
29+
30+
self.srv.add_route('GET', '/parameters/info',
31+
self.parameters_get_info,
32+
'parameters_get_info')
33+
34+
# subscribe to stats
35+
self.get_info = rospy.ServiceProxy('xbotcore/get_parameter_info', GetParameterInfo)
36+
37+
38+
@utils.handle_exceptions
39+
async def parameters_get_info(self, request):
40+
41+
response = await utils.to_thread(self.get_info, name=[], tunable_only=True)
42+
43+
print(response)
44+
45+
return web.json_response(
46+
{
47+
'name': response.name,
48+
'value': [json.dumps(yaml.safe_load(v)) for v in response.value],
49+
'type': response.type,
50+
'descriptor': [json.dumps(yaml.safe_load(v)) for v in response.descriptor]
51+
}
52+
)
53+
54+
55+
@utils.handle_exceptions
56+
async def dashboard_robot_switch(self, request: web.Request):
57+
58+
# we need the command name (start or stop)
59+
command = request.match_info.get('command', None)
60+
61+
l : launcher.Launcher = self.get_launcher()
62+
63+
if command == 'start':
64+
65+
# start ecat
66+
await self.send_status('starting robot 1/3 (ecat)')
67+
68+
if not await l.start(process='ecat'):
69+
raise RuntimeError('could not start ecat')
70+
71+
# check slaves
72+
await self.send_status('starting robot 2/3 (ecat_slave_check)')
73+
74+
if not await l.start(process='wait_ecat_online'):
75+
raise RuntimeError('could not find slaves')
76+
77+
await self.send_status('starting robot 3/3 (xbot2)')
78+
79+
# start xbot2
80+
if not await l.start(process='xbot2',
81+
user_variants=['ec_imp']):
82+
raise RuntimeError('could not start xbot2')
83+
84+
# wait alive
85+
while not self.xbot2_alive:
86+
await asyncio.sleep(0.1)
87+
88+
self.active_state = 'ready'
89+
90+
elif command == 'stop':
91+
92+
# check xbot2 is down
93+
sdict = await l.status()
94+
95+
if sdict.get('xbot2', '') == 'Running':
96+
raise RuntimeError('xbot2 is up: have you pressed the emergency button?')
97+
98+
# kill ecat and hope for the best
99+
await self.send_status('stopping robot (ecat)')
100+
101+
if not await l.kill(process='ecat', graceful=True):
102+
raise RuntimeError('could not stop ecat')
103+
104+
self.active_state = 'inactive'
105+
106+
else:
107+
108+
raise KeyError(f'bad command {command}')
109+
110+
111+
return web.Response(text=json.dumps(
112+
{
113+
'success': True,
114+
'message': 'ok',
115+
}
116+
))
117+
118+
119+
@utils.handle_exceptions
120+
async def dashboard_start(self, request: web.Request):
121+
122+
# check xbot2 alive
123+
if self.stats is None:
124+
raise RuntimeError('xbot2 is not alive')
125+
126+
# we need the task name and ctrl mode
127+
task_name = request.match_info.get('task_name', None)
128+
129+
if task_name is None:
130+
raise ValueError('no task name specified in url')
131+
132+
task = self.states[task_name]
133+
134+
# stop all plugins
135+
all_plugins = [t.name for t in self.stats.task_stats if t.name in self.all_plugins]
136+
plugins_to_start = plugins = task['plugin']
137+
plugins_to_stop = [p for p in all_plugins if p not in plugins_to_start]
138+
139+
for p in plugins_to_stop:
140+
await self.send_status(f'stopping plugin {p}')
141+
await self.plugin_switch(p, 0)
142+
143+
for p in plugins_to_stop:
144+
await self.send_status(f'waiting for plugin {p} to stop...')
145+
await asyncio.wait_for(self.wait_for_state(p, ['Stopped', 'Initialized']),
146+
timeout=10.0)
147+
148+
# start required plugins
149+
for p in plugins_to_start:
150+
await self.send_status(f'starting plugin {p}')
151+
await self.plugin_switch(p, 1)
152+
for p in plugins_to_start:
153+
await self.send_status(f'waiting for plugin {p} to start...')
154+
await self.wait_for_state(p, 'Running')
155+
156+
# kill all active processes
157+
process_to_start = task['process']
158+
process_to_stop = [p for p in self.all_processes if p not in process_to_start]
159+
for p in process_to_stop:
160+
await self.send_status(f'killing process {p}')
161+
l : launcher.Launcher = self.get_launcher()
162+
await asyncio.wait_for(l.kill(process=p, graceful=True),
163+
timeout=30)
164+
165+
# start required processes
166+
for p in process_to_start:
167+
await self.send_status(f'starting process {p}')
168+
l : launcher.Launcher = self.get_launcher()
169+
await asyncio.wait_for(l.start(process=p),
170+
timeout=30)
171+
172+
173+
await self.send_status(f'done')
174+
175+
self.active_state = task_name
176+
177+
return web.Response(text=json.dumps(
178+
{
179+
'success': True,
180+
'message': 'ok',
181+
}
182+
))
183+
184+
185+
async def plugin_switch(self, plugin_name, switch_flag):
186+
switch = rospy.ServiceProxy(f'xbotcore/{plugin_name}/switch', service_class=SetBool)
187+
await utils.to_thread(switch.wait_for_service, timeout=1.0)
188+
res = await utils.to_thread(switch, switch_flag)
189+
return res.success
190+
191+
192+
async def run(self):
193+
194+
while True:
195+
196+
# if not self.xbot2_alive:
197+
# self.active_state = 'inactive'
198+
# elif self.active_state == 'inactive':
199+
# self.active_state = 'ready'
200+
201+
# self.xbot2_alive = False
202+
203+
await self.srv.ws_send_to_all(
204+
{
205+
'type': 'dashboard_msg',
206+
'active_state': self.active_state
207+
}
208+
)
209+
210+
await asyncio.sleep(0.666)
211+
212+
213+
def on_stats_recv(self, msg):
214+
self.stats = msg
215+
self.xbot2_alive = True
216+
217+
218+
219+
async def wait_for_state(self, plugin_name, state):
220+
221+
if isinstance(state, str):
222+
state = [state]
223+
224+
idx = -1
225+
226+
for i, t in enumerate(self.stats.task_stats):
227+
if t.name in plugin_name:
228+
idx = i
229+
break
230+
231+
if idx == -1:
232+
raise KeyError(plugin_name)
233+
234+
while self.stats.task_stats[idx].state not in state:
235+
await asyncio.sleep(0.1)
236+
237+
238+
def get_launcher(self):
239+
if self.launcher is not None:
240+
return self.launcher
241+
for e in self.srv.extensions:
242+
if isinstance(e, launcher.Launcher):
243+
self.launcher = e
244+
return e
245+
raise ValueError('launcher unavailable')
246+
247+
248+
async def send_status(self, txt):
249+
print(txt)
250+
await self.srv.ws_send_to_all(
251+
dict(type='dashboard_msg',
252+
msg=txt)
253+
)

server/src/xbot2_gui_server/server_config.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ dashboard:
3131
states:
3232
- name: jogging_cartesian
3333
nice_name: Jogging (Cartesian)
34-
process: []
34+
process: [prova1, prova2]
3535
plugin: [homing]
3636
- name: jogging_joint
3737
nice_name: Jogging (Joint)

xbot2_gui/Common/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ qt_add_qml_module(xbot2_gui_common
2828
NotificationPopup.qml
2929
ResizableRectangle.qml
3030
QML_FILES DelayButtonRound.qml
31+
QML_FILES DoubleSpinBox1.qml
3132
)
3233

3334
target_link_libraries(xbot2_gui PRIVATE xbot2_gui_commonplugin)
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import QtQuick
2+
import QtQuick.Controls
3+
4+
Item {
5+
6+
property real from: 0
7+
property real to: 1
8+
property real value: 0.5
9+
10+
signal valueModified(real value)
11+
12+
id: root
13+
14+
implicitHeight: spinBox.implicitHeight
15+
implicitWidth: spinBox.implicitWidth
16+
17+
onValueChanged: {
18+
spinBox.value = spinBox.decimalToInt(value)
19+
}
20+
21+
SpinBox {
22+
id: spinBox
23+
from: decimalToInt(root.from)
24+
value: decimalToInt(root.value)
25+
to: decimalToInt(root.to)
26+
stepSize: decimalToInt(1/decimalFactor)
27+
editable: true
28+
anchors.fill: parent
29+
30+
property int decimals: 2
31+
readonly property int decimalFactor: Math.pow(10, decimals)
32+
33+
function decimalToInt(decimal) {
34+
return decimal * decimalFactor
35+
}
36+
37+
onValueModified: {
38+
root.value = value/decimalFactor
39+
root.valueModified(root.value)
40+
}
41+
42+
validator: DoubleValidator {
43+
bottom: Math.min(spinBox.from, spinBox.to)
44+
top: Math.max(spinBox.from, spinBox.to)
45+
decimals: spinBox.decimals
46+
notation: DoubleValidator.StandardNotation
47+
}
48+
49+
textFromValue: function(value, locale) {
50+
return Number(value / decimalFactor).toLocaleString(locale, 'f', spinBox.decimals)
51+
}
52+
53+
valueFromText: function(text, locale) {
54+
return Math.round(Number.fromLocaleString(locale, text) * decimalFactor)
55+
}
56+
}
57+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
add_library(xbot2_gui_concert STATIC)
2+
3+
qt_add_qml_module(xbot2_gui_concert
4+
URI Concert
5+
VERSION 1.0
6+
QML_FILES
7+
Drilling.qml
8+
Drilling.js
9+
DrillingControl.qml
10+
MotionTab.qml
11+
DrillTab.qml
12+
Conda.qml
13+
SliderModelItem.qml
14+
Sanding.qml
15+
Sanding.js
16+
QML_FILES Transportation.qml
17+
QML_FILES Transportation.js
18+
<<<<<<< HEAD
19+
QML_FILES PatternTab.qml
20+
=======
21+
QML_FILES TransportationTasks/TeachingTasks.qml
22+
TransportationTasks/FollowTasks.qml
23+
TransportationTasks/AutonomousTasks.qml
24+
QML_FILES TaskBusyIndicator.qml
25+
>>>>>>> origin/speech
26+
)
27+
28+
target_link_libraries(xbot2_gui PRIVATE xbot2_gui_concertplugin)

0 commit comments

Comments
 (0)