From 70a43770f2efc384ef2739977636c8ccac3abcf4 Mon Sep 17 00:00:00 2001 From: Jan Romann Date: Thu, 10 Feb 2022 16:47:06 +0100 Subject: [PATCH 1/2] feat(discovery)!: re-align discovery API with current spec --- lib/src/core/thing_discovery.dart | 43 ++++++++++++++++--- lib/src/core/wot.dart | 6 +-- .../discovery/thing_discovery.dart | 6 ++- lib/src/scripting_api/types.dart | 4 -- lib/src/scripting_api/wot.dart | 8 ++-- 5 files changed, 50 insertions(+), 17 deletions(-) diff --git a/lib/src/core/thing_discovery.dart b/lib/src/core/thing_discovery.dart index 625954c6..a6146e6e 100644 --- a/lib/src/core/thing_discovery.dart +++ b/lib/src/core/thing_discovery.dart @@ -4,6 +4,8 @@ // // SPDX-License-Identifier: BSD-3-Clause +import 'dart:async'; + import '../../scripting_api.dart' as scripting_api; import '../definitions/form.dart'; import '../definitions/thing_description.dart'; @@ -40,17 +42,25 @@ class ThingDiscovery implements scripting_api.ThingDiscovery { final Servient _servient; - final scripting_api.DiscoveryListener _callback; + // This linting issue should be a false positive. + // ignore: close_sinks + StreamController? _controller; + + StreamIterator? _streamIterator; /// Creates a new [ThingDiscovery] object with a given [thingFilter]. - ThingDiscovery(this._callback, this.thingFilter, this._servient); + ThingDiscovery(this.thingFilter, this._servient); @override Future start() async { final thingFilter = this.thingFilter; + _controller = StreamController(); + _streamIterator = StreamIterator(_controller!.stream); if (thingFilter == null) { - throw ArgumentError(); + // TODO(JKRhb): This has to be revisited once the specification has been + // updated and the thingFilter can actually be unset. + throw ArgumentError("thingFilter can't be null!"); } _active = true; @@ -67,10 +77,15 @@ class ThingDiscovery implements scripting_api.ThingDiscovery { @override void stop() { + _controller?.sink.close(); _active = false; } Future _discoverDirectly(String? uri) async { + final controller = _controller; + if (controller == null) { + throw StateError("ThingDiscovery is not active!"); + } if (uri == null) { throw ArgumentError(); } @@ -84,9 +99,11 @@ class ThingDiscovery implements scripting_api.ThingDiscovery { final value = await _servient.contentSerdes.contentToValue(content, null); if (value is Map) { - _callback(ThingDescription.fromJson(value)); + final thingDescription = ThingDescription.fromJson(value); + _done = true; // TODO(JKRhb): Check if done should be set here _active = false; - _done = true; + controller.sink.add(thingDescription); + await controller.sink.close(); return; } @@ -98,4 +115,20 @@ class ThingDiscovery implements scripting_api.ThingDiscovery { throw error; } } + + @override + Future next() async { + final streamIterator = _streamIterator; + if (streamIterator == null) { + throw StateError("ThingDiscovery has not been started yet!"); + } + final hasNext = await streamIterator.moveNext(); + if (!active && !hasNext) { + _done = true; + } else if (hasNext) { + return streamIterator.current; + } + // TODO(JKRhb): Revisit error message + throw StateError("ThingDiscovery has already been stopped!"); + } } diff --git a/lib/src/core/wot.dart b/lib/src/core/wot.dart index 0e7d0c52..2533142b 100644 --- a/lib/src/core/wot.dart +++ b/lib/src/core/wot.dart @@ -40,8 +40,8 @@ class WoT implements scripting_api.WoT { /// Discovers [ThingDescription]s matching a given [filter]. @override - ThingDiscovery discover(scripting_api.DiscoveryListener callback, - [scripting_api.ThingFilter? filter]) { - return ThingDiscovery(callback, filter, _servient)..start(); + ThingDiscovery discover([scripting_api.ThingFilter? filter]) { + // TODO(JKRhb): Decide if the user should call the start method. + return ThingDiscovery(filter, _servient)..start(); } } diff --git a/lib/src/scripting_api/discovery/thing_discovery.dart b/lib/src/scripting_api/discovery/thing_discovery.dart index 15a0f940..8a317a8a 100644 --- a/lib/src/scripting_api/discovery/thing_discovery.dart +++ b/lib/src/scripting_api/discovery/thing_discovery.dart @@ -4,9 +4,10 @@ // // SPDX-License-Identifier: BSD-3-Clause +import '../../definitions/thing_description.dart'; import 'thing_filter.dart'; -/// Interface +/// Provides the properties and methods controlling the discovery process. abstract class ThingDiscovery { /// The [thingFilter] that is applied during the discovery process. ThingFilter? get thingFilter; @@ -25,4 +26,7 @@ abstract class ThingDiscovery { /// Stops the discovery process. void stop(); + + /// Provides the next discovered [ThingDescription] object. + Future next(); } diff --git a/lib/src/scripting_api/types.dart b/lib/src/scripting_api/types.dart index fe7f0771..d248eb53 100644 --- a/lib/src/scripting_api/types.dart +++ b/lib/src/scripting_api/types.dart @@ -30,7 +30,3 @@ typedef PropertyWriteMap = Map; /// Represents a Partial TD as described in the /// [WoT Architecture](https://w3c.github.io/wot-architecture/#dfn-partial-td). typedef ExposedThingInit = Map; - -/// User provided callback that is given an argument of type [ThingDescription] -/// and is used for handling discovered Thing Descriptions. -typedef DiscoveryListener = void Function(ThingDescription thingDescription); diff --git a/lib/src/scripting_api/wot.dart b/lib/src/scripting_api/wot.dart index 1ac16656..0166c0f0 100644 --- a/lib/src/scripting_api/wot.dart +++ b/lib/src/scripting_api/wot.dart @@ -30,8 +30,9 @@ abstract class WoT { /// based on the underlying impementation. Future produce(ExposedThingInit exposedThingInit); - /// Discovers [ThingDescription]s, which are passed to a [callback] function - /// upon retrieval. + /// Discovers [ThingDescription]s which can be obtained by calling `next()` + /// on the returned [ThingDiscovery] object. + /// /// As this part of the Scripting API specification is still in development, /// this method's implementation is in an experimental state and does not /// conform to the specification's latest version. @@ -49,6 +50,5 @@ abstract class WoT { /// The [ThingDiscovery] object that is returned by this function can be used /// for stopping the Discovery process and retrieving information about its /// current state (i. e. whether it is still `active` or already `done`). - ThingDiscovery discover(DiscoveryListener callback, - [ThingFilter? thingFilter]); + ThingDiscovery discover([ThingFilter? thingFilter]); } From bfd81e79008937431d7b46d416d6fab0af9085f4 Mon Sep 17 00:00:00 2001 From: Jan Romann Date: Thu, 10 Feb 2022 16:48:08 +0100 Subject: [PATCH 2/2] feat: use reworked discovery API in example --- example/dart_wot_example.dart | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/example/dart_wot_example.dart b/example/dart_wot_example.dart index 9d6bbff4..2325e3eb 100644 --- a/example/dart_wot_example.dart +++ b/example/dart_wot_example.dart @@ -112,19 +112,18 @@ Future main() async { await consumedThing.readProperty("test"); - Future handleThingInteraction(ThingDescription thingDescription) async { - final consumedThing = await wot.consume(thingDescription); - print("The title of the fetched TD is ${consumedThing.title}."); - print("Done!"); + final thingDiscovery = wot.discover(ThingFilter( + "https://raw.githubusercontent.com/w3c/wot-testing" + "/b07fa6124bca7796e6ca752a3640fac264d3bcbc/events/2021.03.Online/TDs" + "/Oracle/oracle-Festo_Shared.td.jsonld", + DiscoveryMethod.direct)); - exit(0); - } + final discoveredThingDescription = await thingDiscovery.next(); + thingDiscovery.stop(); + final consumedDiscoveredThing = await wot.consume(discoveredThingDescription); + + print("The title of the fetched TD is ${consumedDiscoveredThing.title}."); + print("Done!"); - wot.discover( - handleThingInteraction, - ThingFilter( - "https://raw.githubusercontent.com/w3c/wot-testing" - "/b07fa6124bca7796e6ca752a3640fac264d3bcbc/events/2021.03.Online/TDs" - "/Oracle/oracle-Festo_Shared.td.jsonld", - DiscoveryMethod.direct)); + exit(0); }