diff --git a/example/service/lib/main.dart b/example/service/lib/main.dart index cd31c30..ec0d154 100644 --- a/example/service/lib/main.dart +++ b/example/service/lib/main.dart @@ -38,14 +38,16 @@ class _MyHomePageState extends State { int msgToPublished = 0; @override void initState() { - ros = Ros(url: 'ws://127.0.0.1:9090'); + ros = Ros(url: 'ws://192.168.1.149:9090'); service = Service( - name: 'add_two_ints', ros: ros, type: "tutorial_interfaces/AddTwoInts"); + name: '/add_two_ints', ros: ros, type: "/tutorial_interfaces/AddTwoInts"); super.initState(); ros.connect(); Timer(const Duration(seconds: 3), () async { - await service.advertise(serviceHandler); + // await service.advertise(serviceHandler); + var res = await service.call({'a': 1, 'b': 2}); + print(res); }); } diff --git a/example/service/linux/flutter/generated_plugins.cmake b/example/service/linux/flutter/generated_plugins.cmake index 51436ae..2e1de87 100644 --- a/example/service/linux/flutter/generated_plugins.cmake +++ b/example/service/linux/flutter/generated_plugins.cmake @@ -5,6 +5,9 @@ list(APPEND FLUTTER_PLUGIN_LIST ) +list(APPEND FLUTTER_FFI_PLUGIN_LIST +) + set(PLUGIN_BUNDLED_LIBRARIES) foreach(plugin ${FLUTTER_PLUGIN_LIST}) @@ -13,3 +16,8 @@ foreach(plugin ${FLUTTER_PLUGIN_LIST}) list(APPEND PLUGIN_BUNDLED_LIBRARIES $) list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) endforeach(plugin) + +foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/linux plugins/${ffi_plugin}) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) +endforeach(ffi_plugin) diff --git a/lib/core/action.dart b/lib/core/action.dart new file mode 100644 index 0000000..22c5225 --- /dev/null +++ b/lib/core/action.dart @@ -0,0 +1,128 @@ +import 'dart:async'; +import 'dart:ffi'; +import 'package:flutter/material.dart'; + +import 'ros.dart'; +import 'request.dart'; +import 'dart:math'; + +typedef ActionHandler = void Function(Map response); +typedef UUID = List; +class GoalHandle { + ActionHandler onSendGoal; + ActionHandler onFeedback; + ActionHandler onResult; + + GoalHandle({ + required this.onSendGoal, + required this.onFeedback, + required this.onResult, + }); +} +// 每次发送goal都要构造Action +class Action { + Ros ros; + String name; + String type; + // 每次发送goal时,都会生成唯一一个uuid,不能出现重复,用于后续发送cancel_goal, + Map> uuids = {}; + + StreamSubscription? goalListener; + StreamSubscription? feedbackListener; + StreamSubscription? resultListener; + + StreamSubscription? cancelListener; + + GoalHandle goalHandle; + + Action({ + required this.name, + required this.ros, + required this.type, + required this.goalHandle, + }); + List generateRandomString(int length) { + const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; + Random random = Random(); + return List.generate(length, (index) { + return characters[random.nextInt(characters.length)].codeUnitAt(0); + }); + } + void _bindFeedbackAndResult(String callId) { + final feedbackLis = ros.stream; + feedbackLis.listen((message) { + goalHandle.onFeedback(message); + }); + + final resultReceiver = ros.stream.where((message) => message['op'] == 'action_result' && message['id'] == callId) + .map((Map message) => message['result'] == null + ? message['values']! + : message['values']); + + resultListener = resultReceiver.listen((d) { + goalHandle.onResult(d); + resultListener!.cancel(); + }); + } + + String sendGoal(dynamic req) { + final callId = ros.requestServiceCaller(name); + final completer = Completer(); + final uuid = generateRandomString(16); + var reqObj = Request( + op: 'send_action_goal', + id: callId, + action: req['action'], + type: req['type'], + args: { + "goal_id" : { + "uuid": uuid, + }, + "goal": req['args'], + }, + ); + if (uuids.containsKey(callId)) { + uuids[callId] = uuid; + } else { + uuids.addAll({callId: uuid}); + } + ros.send(reqObj); + + final receiver = ros.stream.where((message) { + return message['id'] == callId && message['op'] != 'cancel_goal_response'; + } ); + goalListener = receiver.listen((d) { + if (d["op"] == "action_result") { + goalHandle.onResult(d); + goalListener!.cancel(); + } else if (d["op"] == "action_feedback") { + goalHandle.onFeedback(d); + } else if (d["op"] == 'send_goal_response') { + completer.complete(uuids[callId]); + goalHandle.onSendGoal(d); + } + }); + + return callId; + } + + Future cancelGoal(dynamic req, String callId, ActionHandler onCancel) { + final completer = Completer(); + var cancelReq = Request( + op: 'cancel_action_goal', + id: callId, + action: req['action'], + ); + print("cancelReq: ${cancelReq.toJson()}"); + ros.send(cancelReq); + final receiver = ros.stream.where( + (message) => message['id'] == callId && message['op'] == 'cancel_goal_response' + ); + cancelListener = receiver.listen((d) { + onCancel(d); + completer.complete(); + cancelListener!.cancel(); + }); + return completer.future; + } +} \ No newline at end of file diff --git a/lib/core/core.dart b/lib/core/core.dart index 2da5340..8c381af 100644 --- a/lib/core/core.dart +++ b/lib/core/core.dart @@ -5,3 +5,4 @@ export 'request.dart'; export 'service.dart'; export 'topic.dart'; export 'param.dart'; +export 'action.dart'; \ No newline at end of file diff --git a/lib/core/request.dart b/lib/core/request.dart index 1edf316..062f352 100644 --- a/lib/core/request.dart +++ b/lib/core/request.dart @@ -16,6 +16,7 @@ class Request { this.queueLength, this.queueSize, this.service, + this.action, this.args, this.values, this.result, @@ -54,6 +55,9 @@ class Request { /// Service name operating on. String? service; + /// action name operating on. + String? action; + /// Arguments of the request (JSON). Map? args; @@ -66,10 +70,12 @@ class Request { factory Request.fromJson(dynamic jsonData) { return Request( op: jsonData['op'], + // type: jsonData['type'], id: jsonData['id'], type: jsonData['type'], topic: jsonData['topic'], msg: jsonData['msg'], + action: jsonData['action'], latch: jsonData['latch'], compression: jsonData['compression'], throttleRate: jsonData['throttle_rate'], @@ -99,6 +105,7 @@ class Request { if (queueLength != null) 'queue_length': queueLength, if (queueSize != null) 'queue_size': queueSize, if (service != null) 'service': service, + if (action != null) 'action': action, if (args != null) 'args': args, if (values != null) 'values': values, if (result != null) 'result': result,