Tue Feb 22 09:54:27 PM HKT 2022

This commit is contained in:
Terence Tong 2022-02-22 21:54:27 +08:00
parent 57d2e6dfd4
commit 29d8c4789a
83 changed files with 130 additions and 82 deletions

View File

@ -45,6 +45,21 @@ ros2 run tutorial publisher
cd example/subscriber
flutter run -d linux
```
### Subscribe using roslibdart
```
ros = Ros(url: 'ws://127.0.0.1:9090');
chatter = Topic(ros: ros, name: '/topic', type: "std_msgs/String", reconnectOnClose: true, queueLength: 10, queueSize: 10);
ros.connect();
await chatter.subscribe(subscribeHandler);
Future<void> subscribeHandler(Map<String, dynamic> msg) async {
msgReceived = json.encode(msg);
setState(() {});
}
```
## Testing subscriber
### Run topic subscriber
```
@ -54,8 +69,16 @@ ros2 run tutorial subscriber
```
cd example/publisher
flutter run -d linux
```
### Publish using roslibdart
```
ros = Ros(url: 'ws://127.0.0.1:9090');
chatter = Topic(ros: ros, name: '/topic', type: "std_msgs/String", reconnectOnClose: true, queueLength: 10, queueSize: 10);
ros.connect();
Map<String, dynamic> json = {"data": msgToPublished.toString()};
await chatter.publish(json);
```
## Testing call
### Run tutorial addtwoint service
```
@ -65,18 +88,46 @@ ros2 run tutorial service
cd example/client
flutter run -d linux
```
### Call a service using roslibdart
```
ros = Ros(url: 'ws://127.0.0.1:9090');
service = Service(name: 'add_two_ints', ros: ros, type: "tutorial_interfaces/AddTwoInts");
ros.connect();
Map<String, dynamic> json = {"a": 1, "b": 2};
Map<String, dynamic> result = await service.call(json);
msgToPublished = result['sum'];
```
## Testing providing service
### Fire up flutter service
```
cd example/service
flutter run -d linux
```
### Run tutorial call
```
ros2 run tutorial client
```
### Run the client from shell
```
ros2 run tutorial client 2 3
```
### Provide service using roslibdart
```
ros = Ros(url: 'ws://127.0.0.1:9090');
service = Service(name: 'add_two_ints', ros: ros, type: "tutorial_interfaces/AddTwoInts");
ros.connect();
await service.advertise(serviceHandler);
Future<Map<String, dynamic>>? serviceHandler(Map<String, dynamic> args) async {
Map<String, dynamic> response = {};
response['sum'] = args['a'] + args['b'];
return response;
}
```
## Links
- [ROSBridge Protocol v2.0](https://github.com/biobotus/rosbridge_suite/blob/master/ROSBRIDGE_PROTOCOL.md).
- [Original roslib library from Conrad Heidebrecht](https://github.com/Eternali/roslib)
- [RosBridge server implementation](https://github.com/RobotWebTools/rosbridge_suite)
- [roslibjs example](https://github.com/RobotWebTools/roslibjs/blob/develop/examples/simple.html)

View File

@ -1,4 +1,4 @@
# client
# caller
A new Flutter project.

View File

@ -43,7 +43,7 @@ android {
defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId "com.example.client"
applicationId "com.example.caller"
minSdkVersion flutter.minSdkVersion
targetSdkVersion flutter.targetSdkVersion
versionCode flutterVersionCode.toInteger()

View File

@ -1,5 +1,5 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.client">
package="com.example.caller">
<!-- Flutter needs it to communicate with the running application
to allow setting breakpoints, to provide hot reload, etc.
-->

View File

@ -1,7 +1,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.client">
package="com.example.caller">
<application
android:label="client"
android:label="caller"
android:name="${applicationName}"
android:icon="@mipmap/ic_launcher">
<activity

View File

@ -1,4 +1,4 @@
package com.example.client
package com.example.caller
import io.flutter.embedding.android.FlutterActivity

View File

Before

Width:  |  Height:  |  Size: 544 B

After

Width:  |  Height:  |  Size: 544 B

View File

Before

Width:  |  Height:  |  Size: 442 B

After

Width:  |  Height:  |  Size: 442 B

View File

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@ -1,5 +1,5 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.client">
package="com.example.caller">
<!-- Flutter needs it to communicate with the running application
to allow setting breakpoints, to provide hot reload, etc.
-->

View File

@ -294,7 +294,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = com.example.client;
PRODUCT_BUNDLE_IDENTIFIER = com.example.caller;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0;
@ -422,7 +422,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = com.example.client;
PRODUCT_BUNDLE_IDENTIFIER = com.example.caller;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
@ -444,7 +444,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = com.example.client;
PRODUCT_BUNDLE_IDENTIFIER = com.example.caller;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0;

View File

@ -5,7 +5,7 @@
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleDisplayName</key>
<string>Client</string>
<string>Caller</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
@ -13,7 +13,7 @@
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>client</string>
<string>caller</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>

View File

@ -14,11 +14,11 @@ class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Roslibdart Client Example',
title: 'Roslibdart Caller Example',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'Roslibdart Client Example'),
home: const MyHomePage(title: 'Roslibdart Caller Example'),
);
}
}
@ -54,10 +54,6 @@ class _MyHomePageState extends State<MyHomePage> {
});
}
void initConnection() async {
setState(() {});
}
void destroyConnection() async {
await ros.close();
setState(() {});

View File

@ -1,8 +1,8 @@
cmake_minimum_required(VERSION 3.10)
project(runner LANGUAGES CXX)
set(BINARY_NAME "client")
set(APPLICATION_ID "com.example.client")
set(BINARY_NAME "caller")
set(APPLICATION_ID "com.example.caller")
cmake_policy(SET CMP0063 NEW)

View File

@ -40,11 +40,11 @@ static void my_application_activate(GApplication* application) {
if (use_header_bar) {
GtkHeaderBar* header_bar = GTK_HEADER_BAR(gtk_header_bar_new());
gtk_widget_show(GTK_WIDGET(header_bar));
gtk_header_bar_set_title(header_bar, "client");
gtk_header_bar_set_title(header_bar, "caller");
gtk_header_bar_set_show_close_button(header_bar, TRUE);
gtk_window_set_titlebar(window, GTK_WIDGET(header_bar));
} else {
gtk_window_set_title(window, "client");
gtk_window_set_title(window, "caller");
}
gtk_window_set_default_size(window, 1280, 720);

View File

@ -8,7 +8,7 @@
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:client/main.dart';
import 'package:caller/main.dart';
void main() {
testWidgets('Counter increments smoke test', (WidgetTester tester) async {

View File

Before

Width:  |  Height:  |  Size: 917 B

After

Width:  |  Height:  |  Size: 917 B

View File

Before

Width:  |  Height:  |  Size: 5.2 KiB

After

Width:  |  Height:  |  Size: 5.2 KiB

View File

Before

Width:  |  Height:  |  Size: 8.1 KiB

After

Width:  |  Height:  |  Size: 8.1 KiB

View File

Before

Width:  |  Height:  |  Size: 5.5 KiB

After

Width:  |  Height:  |  Size: 5.5 KiB

View File

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 20 KiB

View File

@ -23,13 +23,13 @@
<!-- iOS meta tags & icons -->
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black">
<meta name="apple-mobile-web-app-title" content="client">
<meta name="apple-mobile-web-app-title" content="caller">
<link rel="apple-touch-icon" href="icons/Icon-192.png">
<!-- Favicon -->
<link rel="icon" type="image/png" href="favicon.png"/>
<title>client</title>
<title>caller</title>
<link rel="manifest" href="manifest.json">
</head>
<body>

View File

@ -1,6 +1,6 @@
{
"name": "client",
"short_name": "client",
"name": "caller",
"short_name": "caller",
"start_url": ".",
"display": "standalone",
"background_color": "#0175C2",

View File

@ -49,10 +49,6 @@ class _MyHomePageState extends State<MyHomePage> {
});
}
void initConnection() async {
setState(() {});
}
void destroyConnection() async {
await ros.close();
setState(() {});
@ -81,7 +77,7 @@ class _MyHomePageState extends State<MyHomePage> {
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text('return answer ' + msgToPublished.toString()),
Text('Service returns answer ' + msgToPublished.toString()),
],
),
),

View File

@ -1,5 +1,7 @@
import 'package:flutter/material.dart';
import 'package:roslibdart/roslibdart.dart';
import 'dart:async';
import 'dart:convert';
void main() {
runApp(const ExampleApp());
@ -37,12 +39,11 @@ class _HomePageState extends State<HomePage> {
queueLength: 10,
queueSize: 10);
super.initState();
}
void initConnection() async {
ros.connect();
await chatter.subscribe();
setState(() {});
Timer(const Duration(seconds: 3), () async {
await chatter.subscribe(subscribeHandler);
// await chatter.subscribe();
});
}
void destroyConnection() async {
@ -51,51 +52,28 @@ class _HomePageState extends State<HomePage> {
setState(() {});
}
String msgReceived = '';
Future<void> subscribeHandler(Map<String, dynamic> msg) async {
msgReceived = json.encode(msg);
setState(() {});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Roslibdart Subscriber Example'),
),
body: StreamBuilder<Object>(
stream: ros.statusStream,
builder: (context, snapshot) {
return Center(
body: Center(
// Center is a layout widget. It takes a single child and positions it
// in the middle of the parent.
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
StreamBuilder<Map<String, dynamic>>(
stream: chatter.subscription,
builder: (BuildContext context2,
AsyncSnapshot<Map<String, dynamic>> snapshot2) {
if (snapshot2.hasData && snapshot2.data != null) {
return Text('${snapshot2.data!['msg']}');
} else {
return const CircularProgressIndicator();
}
},
),
ActionChip(
label: Text(snapshot.data == Status.connected
? 'DISCONNECT'
: 'CONNECT'),
backgroundColor: snapshot.data == Status.connected
? Colors.green[300]
: Colors.grey[300],
onPressed: () {
// print(snapshot.data);
if (snapshot.data != Status.connected) {
initConnection();
} else {
destroyConnection();
}
},
),
Text(msgReceived + ' received'),
],
),
);
}),
),
);
}
}

View File

@ -4,6 +4,9 @@ import 'dart:async';
import 'ros.dart';
import 'request.dart';
// Receiver function to handle requests when the service is advertising.
typedef SubscribeHandler = Future<void> Function(Map<String, dynamic> args);
/// Wrapper to interact with ROS topics.
class Topic {
Topic({
@ -72,7 +75,7 @@ class Topic {
///
/// Defaults to true.
bool reconnectOnClose;
/*
/// Subscribe to the topic if not already subscribed.
Future<void> subscribe() async {
if (subscribeId == null) {
@ -91,6 +94,30 @@ class Topic {
));
}
}
*/
Future<void> subscribe(SubscribeHandler subscribeHandler) async {
if (subscribeId == null) {
// Create the listenable broadcast subscription stream.
subscription = ros.stream;
subscribeId = ros.requestSubscriber(name);
await safeSend(Request(
op: 'subscribe',
id: subscribeId,
type: type,
topic: name,
compression: compression,
throttleRate: throttleRate,
queueLength: queueLength,
));
subscription!.listen((Map<String, dynamic> message) async {
if (message['topic'] != name) {
return;
}
await subscribeHandler(message['msg']);
});
}
}
/// Unsubscribe from the topic.
Future<void> unsubscribe() async {