Skip to content

Commit 00c4c8c

Browse files
committed
finish update to newer r2r and add a test script
1 parent 1037ee7 commit 00c4c8c

4 files changed

Lines changed: 186 additions & 29 deletions

File tree

Cargo.lock

Lines changed: 47 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ bytes = "0.5.4"
1414
hex = "0.4.2"
1515
lazy_static = "1.4.0"
1616
snailquote = "0.3.0"
17+
rand = "0.8.4"
1718

1819
# speed up compilation
1920
[profile.release]

src/main.rs

Lines changed: 54 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ use ur_script_msgs::srv::DashboardCommand as DBCommand;
1616

1717
struct DriverState {
1818
running: bool,
19+
connected: bool,
1920
// only handle one goal at the time. reply with true/false if
2021
// script executed successfully.
2122
goal_sender: Option<oneshot::Sender<bool>>,
@@ -47,6 +48,7 @@ impl DriverState {
4748
fn new() -> Self {
4849
DriverState {
4950
running: true,
51+
connected: false,
5052
goal_sender: None,
5153
robot_state: 0,
5254
program_state: 0,
@@ -111,11 +113,19 @@ async fn action_server(
111113
loop {
112114
match requests.next().await {
113115
Some(req) => {
116+
if !driver_state.lock().unwrap().connected {
117+
println!(
118+
"No connection to the robot, rejecting request with goal id: {}, script: '{}'",
119+
req.uuid, req.goal.script
120+
);
121+
req.reject().expect("could not reject goal");
122+
continue;
123+
}
114124

115125
if driver_state.lock().unwrap().robot_state != 1 { //todo
116126
println!(
117-
"Robot is in protective stop, rejecting request with goal id: {}, script: '{}'",
118-
req.uuid, req.goal.script
127+
"Robot is not in normal mode (state={}) stop, rejecting request with goal id: {}, script: '{}'",
128+
driver_state.lock().unwrap().robot_state, req.uuid, req.goal.script
119129
);
120130
req.reject().expect("could not reject goal");
121131
continue;
@@ -130,18 +140,17 @@ async fn action_server(
130140
let (mut g, mut cancel) = req.accept().expect("could not accept goal");
131141

132142
println!("making a new connection to the driver.");
133-
let ret = TcpStream::connect(&ur_address).await;
134-
match ret {
135-
Ok(mut write_stream) => {
136-
println!("writing data to driver {}", g.goal.script);
137-
write_stream.write_all(g.goal.script.as_bytes()).await?;
138-
write_stream.flush().await?;
139-
}
143+
let conn = TcpStream::connect(&ur_address).await;
144+
let mut write_stream = match conn {
145+
Ok(write_stream) => write_stream,
140146
Err(_) => {
141147
println!("could not connect to realtime port for writing");
142148
return Err(Error::new(ErrorKind::Other, "oh no!"));
143149
}
144-
}
150+
};
151+
println!("writing data to driver {}", g.goal.script);
152+
write_stream.write_all(g.goal.script.as_bytes()).await?;
153+
write_stream.flush().await?;
145154

146155
{
147156
let mut ds = driver_state.lock().unwrap();
@@ -151,13 +160,15 @@ async fn action_server(
151160
match future::select(goal_receiver, cancel.next()).await {
152161
Either::Left((res, _cancel_stream)) => {
153162
// success.
154-
println!("goal completed!");
155163
if let Ok(ok) = res {
164+
println!("goal completed? {}", ok);
156165
let result_msg = ExecuteScript::Result { ok };
157166
// TODO: perhaps g.abort here if ok is false.
158167
g.succeed(result_msg).expect("could not send result");
159168
} else {
160169
println!("future appears canceled...");
170+
let result_msg = ExecuteScript::Result { ok: false };
171+
g.abort(result_msg).expect("task cancelled");
161172
}
162173
},
163174
Either::Right((request, goal_receiver)) => {
@@ -177,18 +188,24 @@ async fn action_server(
177188
match future::select(cancel_receiver, goal_receiver).await {
178189
Either::Left((res, _goal_receiver)) => {
179190
// cancelled using dashboard
180-
// todo: check res
181191
if let Ok(ok) = res {
182192
let result_msg = ExecuteScript::Result { ok };
183193
g.cancel(result_msg).expect("could not cancel goal");
194+
} else {
195+
println!("cancel dashboard future appears canceled");
196+
let result_msg = ExecuteScript::Result { ok: false };
197+
g.abort(result_msg).expect("could not cancel goal");
184198
}
185199
},
186200
Either::Right((res, _cancel_receiver)) => {
187201
// finished executing anyway
188-
// todo: check res
189202
if let Ok(ok) = res {
190203
let result_msg = ExecuteScript::Result { ok };
191204
g.succeed(result_msg).expect("could not succeed goal");
205+
} else {
206+
println!("finished executing but future appears canceled");
207+
let result_msg = ExecuteScript::Result { ok: false };
208+
g.abort(result_msg).expect("could not cancel goal");
192209
}
193210
}
194211
}
@@ -233,9 +250,11 @@ async fn realtime_reader(
233250
) -> Result<(), std::io::Error> {
234251
let mut checking_for_1 = false;
235252
let mut checking_for_2_since = None;
236-
let mut stream = connect_loop(&ur_address).await;
237253
let mut size_bytes = [0u8; 4];
238254

255+
let mut stream = connect_loop(&ur_address).await;
256+
driver_state.lock().unwrap().connected = true;
257+
239258
loop {
240259
let ret = timeout(
241260
Duration::from_millis(1000),
@@ -244,16 +263,19 @@ async fn realtime_reader(
244263
.await;
245264
// handle outer timeout error
246265
if let Err(_) = ret {
247-
println!("timeout on read, reconnecting...");
248-
stream = connect_loop(&ur_address).await;
249266
// reset state
250267
{
251268
let mut ds = driver_state.lock().unwrap();
252269
ds.goal_sender = None;
270+
ds.connected = false;
253271
}
254272
checking_for_1 = false;
255273
checking_for_2_since = None;
256274

275+
println!("timeout on read, reconnecting... ");
276+
stream = connect_loop(&ur_address).await;
277+
driver_state.lock().unwrap().connected = true;
278+
257279
continue;
258280
} else if let Ok(ret) = ret {
259281
if let Err(e) = ret {
@@ -343,8 +365,8 @@ async fn realtime_reader(
343365
} else if program_running && checking_for_2 && checking_for_2_since.is_some() {
344366
// we are currently waiting for program state == 2
345367
let elapsed_since_request = checking_for_2_since.unwrap().elapsed();
346-
if elapsed_since_request > std::time::Duration::from_millis(500) {
347-
// if there's been more than 500 millis without the program
368+
if elapsed_since_request > std::time::Duration::from_millis(1000) {
369+
// if there's been more than 1000 millis without the program
348370
// entering the running state, abort this request.
349371
checking_for_2_since = None;
350372
{
@@ -359,7 +381,8 @@ async fn realtime_reader(
359381

360382
// when we have a goal, first wait until program_state reaches 2
361383
if program_running && program_state == 2 && !checking_for_1 {
362-
println!("program started, waiting for finish");
384+
let elapsed = checking_for_2_since.map(|t|t.elapsed().as_millis()).unwrap_or_default();
385+
println!("program started after {}ms, waiting for finish", elapsed);
363386
checking_for_1 = true;
364387
checking_for_2_since = None;
365388
}
@@ -375,7 +398,6 @@ async fn realtime_reader(
375398
{
376399
let mut ds = driver_state.lock().unwrap();
377400
if let Some(goal_sender) = ds.goal_sender.take() {
378-
println!("goal succeeded");
379401
goal_sender.send(true).expect("goal receiver dropped");
380402
} else {
381403
println!("we fininshed but probably canceled the goal before...");
@@ -497,14 +519,16 @@ async fn dashboard(
497519
println!("robot model: {}", robot_model);
498520

499521
// check that robot is in remote control
500-
stream
501-
.write_all(String::from("is in remote control\n").as_bytes())
502-
.await?;
503-
let mut line = String::new();
504-
stream.read_line(&mut line).await?;
505-
if !line.contains("true") {
506-
return Err(Error::new(ErrorKind::Other, "must be in remote mode"));
507-
}
522+
// check commented out to work with simulator...
523+
// stream
524+
// .write_all(String::from("is in remote control\n").as_bytes())
525+
// .await?;
526+
// let mut line = String::new();
527+
// stream.read_line(&mut line).await?;
528+
// if !line.contains("true") {
529+
// println!("remote mode reply: {}", line);
530+
// return Err(Error::new(ErrorKind::Other, "must be in remote mode"));
531+
// }
508532

509533
loop {
510534
let (cmd, channel) = recv.recv().await.unwrap();
@@ -564,7 +588,8 @@ async fn run() -> Result<(), Box<dyn std::error::Error>> {
564588
s.to_owned()
565589
} else {
566590
// "192.168.2.125".to_owned()
567-
"192.168.1.31".to_owned()
591+
// "192.168.100.55".to_owned()
592+
"192.168.100.12".to_owned()
568593
};
569594

570595
let ur_dashboard_address = format!("{}:29999", ur_address);

tests/test_script.rs

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
use futures::stream::StreamExt;
2+
use r2r::ur_script_msgs::action::ExecuteScript;
3+
use std::time::Duration;
4+
5+
#[tokio::test]
6+
async fn test_script() -> Result<(), Box<dyn std::error::Error>> {
7+
let ur_script = format!("def script():\n movej([0.0,0.0,0.0,-1.62,-1.57,0.0], a=0.5, v=0.5)\nend\n\nscript()\n");
8+
let ur_script2 = format!("def script():\n movej([-0.44,-0.75,1.16,-1.9,-1.57,0.0], a=0.5, v=0.5)\nend\n\nscript()\n");
9+
10+
let ctx = r2r::Context::create()?;
11+
let mut node = r2r::Node::create(ctx, "testnode", "")?;
12+
let client = node.create_action_client::<ExecuteScript::Action>("/ur_script")?;
13+
let action_server_available = node.is_available(&client)?;
14+
15+
tokio::spawn(async move {
16+
println!("waiting for action service...");
17+
action_server_available
18+
.await
19+
.expect("could not await action server");
20+
println!("action service available.");
21+
22+
let mut s = &ur_script;
23+
loop {
24+
if s == &ur_script {
25+
s = &ur_script2;
26+
} else {
27+
s = &ur_script;
28+
}
29+
let goal = ExecuteScript::Goal { script: s.to_string() };
30+
println!("sending goal: {:?}", goal);
31+
let res = client
32+
.send_goal_request(goal)
33+
.expect("could not send goal request")
34+
.await;
35+
36+
if let Ok((goal, result, feedback)) = res {
37+
println!("goal accepted: {}", goal.uuid);
38+
// process feedback stream in its own task
39+
let nested_goal = goal.clone();
40+
tokio::spawn(feedback.for_each(move |msg| {
41+
let nested_goal = nested_goal.clone();
42+
async move {
43+
println!(
44+
"new feedback msg {:?} -- {:?}",
45+
msg,
46+
nested_goal.get_status()
47+
);
48+
}
49+
}));
50+
51+
if rand::random::<bool>() { // && rand::random::<bool>() {
52+
// move a bit before sending cancel.
53+
tokio::time::sleep(Duration::from_millis(1000)).await;
54+
let r = goal.cancel().expect("could not send cancel request").await;
55+
if let Ok(()) = r {
56+
println!("goal cancelled successfully.");
57+
} else {
58+
println!("failed to cancel goal: {:?}", r);
59+
}
60+
}
61+
62+
// await result in this task
63+
match result.await {
64+
Ok((status, msg)) => {
65+
println!("got result {} with msg {:?}", status, msg);
66+
}
67+
Err(e) => println!("action failed: {:?}", e),
68+
}
69+
} else {
70+
println!("goal rejected by server");
71+
// wait a bit to give server time to recover
72+
tokio::time::sleep(Duration::from_millis(2000)).await;
73+
}
74+
}
75+
});
76+
77+
let handle = tokio::task::spawn_blocking(move || loop {
78+
node.spin_once(Duration::from_millis(100));
79+
});
80+
81+
handle.await?;
82+
83+
Ok(())
84+
}

0 commit comments

Comments
 (0)