From f60818ae18871fd27bc9228c90342fb2a3aaba52 Mon Sep 17 00:00:00 2001 From: hexbabe Date: Fri, 15 Nov 2024 16:55:01 -0500 Subject: [PATCH 1/4] Add native type return --- lib/src/robot/client.dart | 32 ++++++++++++++++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/lib/src/robot/client.dart b/lib/src/robot/client.dart index 27a08b849c1..71fbbfa5147 100644 --- a/lib/src/robot/client.dart +++ b/lib/src/robot/client.dart @@ -66,6 +66,33 @@ class DiscoveryQuery { Struct get extraStruct => extra.toStruct(); } +/// {@category Viam SDK} +/// Represents the result of a discovery query. +class Discovery { + final String subtype; + final String model; + final Map results; + + Discovery({ + required this.subtype, + required this.model, + required this.results, + }); + + factory Discovery.fromProto(rpb.Discovery proto) { + return Discovery( + subtype: proto.query.subtype, + model: proto.query.model, + results: proto.results.toMap(), + ); + } + + @override + String toString() { + return 'Discovery(subtype: $subtype, model: $model, results: $results)'; + } +} + /// {@category Viam SDK} /// gRPC client for a Robot. This class should be used for all interactions with a robot. /// @@ -287,13 +314,14 @@ class RobotClient { /// var queries = [DiscoveryQuery(subtype: 'camera', model: 'webcam', extra: {'username': 'admin', 'password': 'admin'})]; /// var discoveredComponents = await machine.discoverComponents(queries); /// ``` - Future discoverComponents([List queries = const []]) async { + Future> discoverComponents([List queries = const []]) async { final request = rpb.DiscoverComponentsRequest() ..queries.addAll(queries.map((sdkQuery) => rpb.DiscoveryQuery() ..subtype = sdkQuery.subtype ..model = sdkQuery.model ..extra = sdkQuery.extraStruct)); - return await _client.discoverComponents(request); + final response = await _client.discoverComponents(request); + return response.discovery.map((d) => Discovery.fromProto(d)).toList(); } } From 11fed216e06380db0de990d11c5c59391e21851c Mon Sep 17 00:00:00 2001 From: hexbabe Date: Tue, 19 Nov 2024 12:12:00 -0500 Subject: [PATCH 2/4] Add unwrapper --- lib/src/robot/client.dart | 2 +- lib/src/utils.dart | 78 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 79 insertions(+), 1 deletion(-) diff --git a/lib/src/robot/client.dart b/lib/src/robot/client.dart index 71fbbfa5147..a0d04b02c82 100644 --- a/lib/src/robot/client.dart +++ b/lib/src/robot/client.dart @@ -83,7 +83,7 @@ class Discovery { return Discovery( subtype: proto.query.subtype, model: proto.query.model, - results: proto.results.toMap(), + results: proto.results.toMap().unwrap(), ); } diff --git a/lib/src/utils.dart b/lib/src/utils.dart index 5527458fcc8..4cba7b923ba 100644 --- a/lib/src/utils.dart +++ b/lib/src/utils.dart @@ -39,6 +39,84 @@ extension StructUtils on Struct { } } +extension UnwrapProtoMapUtils on Map { + /// Deeply unwraps a nested proto map structure by removing unnecessary proto wrappers and + /// converting all nested values to their primitive Dart types. + /// + /// Handles: + /// - Removes 'Kind' wrappers from proto Values + /// - Removes 'fields' wrappers from proto Structs + /// - Converts proto scalar types (String, Number, Bool, Null) + /// - Converts proto complex types (List, Struct) + /// - Preserves original structure minus wrappers + Map unwrap() { + final result = {}; + + // Core recursive function that processes each value in the structure + dynamic unwrapValue(dynamic value) { + // Handle maps - primary case for proto wrapper removal + if (value is Map) { + final mapValue = Map.from(value); + + // Two main wrapper types to handle: + + // 1. Kind wrapper (from proto Value) + if (mapValue.containsKey('Kind')) { + final kindValue = mapValue['Kind']; + if (kindValue is Map) { + final kindMap = Map.from(kindValue); + + // Scalar types are direct conversions - base cases + if (kindMap.containsKey('StringValue')) return kindMap['StringValue']; + if (kindMap.containsKey('NumberValue')) return kindMap['NumberValue']; + if (kindMap.containsKey('BoolValue')) return kindMap['BoolValue']; + if (kindMap.containsKey('NullValue')) return null; + + // Complex types need recursive handling - can be a base case when empty + if (kindMap.containsKey('ListValue')) { + final listValue = kindMap['ListValue']; + if (listValue is Map && listValue.containsKey('values')) { + return (listValue['values'] as List).map(unwrapValue).toList(); + } + return []; + } + + if (kindMap.containsKey('StructValue')) { + final structValue = kindMap['StructValue']; + if (structValue is Map && structValue.containsKey('fields')) { + return Map.from(structValue['fields']).unwrap(); + } + } + } + return value; + } + + // 2. Fields wrapper (from proto Struct) + if (mapValue.containsKey('fields')) { + return Map.from(mapValue['fields']).unwrap(); + } + + // Regular map - continue unwrapping all values + return mapValue.map((key, val) => MapEntry(key, unwrapValue(val))); + } + + // Handle lists - unwrap each element + if (value is List) { + return value.map(unwrapValue).toList(); + } + + return value; // base case: primitive value + } + + // Process each top-level key-value pair + forEach((key, value) { + result[key] = unwrapValue(value); + }); + + return result; + } +} + extension ListValueUtils on List { Value toValue() { final values = map((e) { From a83aa1062784ecd86eb1ca15abd68070be37ee3e Mon Sep 17 00:00:00 2001 From: hexbabe Date: Tue, 19 Nov 2024 12:25:56 -0500 Subject: [PATCH 3/4] Improve naming --- lib/src/utils.dart | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/src/utils.dart b/lib/src/utils.dart index 4cba7b923ba..6a4da7d1dde 100644 --- a/lib/src/utils.dart +++ b/lib/src/utils.dart @@ -39,8 +39,8 @@ extension StructUtils on Struct { } } -extension UnwrapProtoMapUtils on Map { - /// Deeply unwraps a nested proto map structure by removing unnecessary proto wrappers and +extension ProtoMapUtils on Map { + /// Deeply unwraps a map derived from a proto struct by removing unnecessary proto wrappers and /// converting all nested values to their primitive Dart types. /// /// Handles: @@ -81,6 +81,7 @@ extension UnwrapProtoMapUtils on Map { return []; } + // 2a. Fields wrapper (from proto Struct) if (kindMap.containsKey('StructValue')) { final structValue = kindMap['StructValue']; if (structValue is Map && structValue.containsKey('fields')) { @@ -91,7 +92,7 @@ extension UnwrapProtoMapUtils on Map { return value; } - // 2. Fields wrapper (from proto Struct) + // 2b. Fields wrapper (from proto Struct) if (mapValue.containsKey('fields')) { return Map.from(mapValue['fields']).unwrap(); } From 345ba11d6ffeb72bc6386f3e75ef43262d92da78 Mon Sep 17 00:00:00 2001 From: hexbabe Date: Tue, 19 Nov 2024 16:02:47 -0500 Subject: [PATCH 4/4] Revert unwrapper --- lib/src/robot/client.dart | 2 +- lib/src/utils.dart | 79 --------------------------------------- 2 files changed, 1 insertion(+), 80 deletions(-) diff --git a/lib/src/robot/client.dart b/lib/src/robot/client.dart index a0d04b02c82..71fbbfa5147 100644 --- a/lib/src/robot/client.dart +++ b/lib/src/robot/client.dart @@ -83,7 +83,7 @@ class Discovery { return Discovery( subtype: proto.query.subtype, model: proto.query.model, - results: proto.results.toMap().unwrap(), + results: proto.results.toMap(), ); } diff --git a/lib/src/utils.dart b/lib/src/utils.dart index 6a4da7d1dde..5527458fcc8 100644 --- a/lib/src/utils.dart +++ b/lib/src/utils.dart @@ -39,85 +39,6 @@ extension StructUtils on Struct { } } -extension ProtoMapUtils on Map { - /// Deeply unwraps a map derived from a proto struct by removing unnecessary proto wrappers and - /// converting all nested values to their primitive Dart types. - /// - /// Handles: - /// - Removes 'Kind' wrappers from proto Values - /// - Removes 'fields' wrappers from proto Structs - /// - Converts proto scalar types (String, Number, Bool, Null) - /// - Converts proto complex types (List, Struct) - /// - Preserves original structure minus wrappers - Map unwrap() { - final result = {}; - - // Core recursive function that processes each value in the structure - dynamic unwrapValue(dynamic value) { - // Handle maps - primary case for proto wrapper removal - if (value is Map) { - final mapValue = Map.from(value); - - // Two main wrapper types to handle: - - // 1. Kind wrapper (from proto Value) - if (mapValue.containsKey('Kind')) { - final kindValue = mapValue['Kind']; - if (kindValue is Map) { - final kindMap = Map.from(kindValue); - - // Scalar types are direct conversions - base cases - if (kindMap.containsKey('StringValue')) return kindMap['StringValue']; - if (kindMap.containsKey('NumberValue')) return kindMap['NumberValue']; - if (kindMap.containsKey('BoolValue')) return kindMap['BoolValue']; - if (kindMap.containsKey('NullValue')) return null; - - // Complex types need recursive handling - can be a base case when empty - if (kindMap.containsKey('ListValue')) { - final listValue = kindMap['ListValue']; - if (listValue is Map && listValue.containsKey('values')) { - return (listValue['values'] as List).map(unwrapValue).toList(); - } - return []; - } - - // 2a. Fields wrapper (from proto Struct) - if (kindMap.containsKey('StructValue')) { - final structValue = kindMap['StructValue']; - if (structValue is Map && structValue.containsKey('fields')) { - return Map.from(structValue['fields']).unwrap(); - } - } - } - return value; - } - - // 2b. Fields wrapper (from proto Struct) - if (mapValue.containsKey('fields')) { - return Map.from(mapValue['fields']).unwrap(); - } - - // Regular map - continue unwrapping all values - return mapValue.map((key, val) => MapEntry(key, unwrapValue(val))); - } - - // Handle lists - unwrap each element - if (value is List) { - return value.map(unwrapValue).toList(); - } - - return value; // base case: primitive value - } - - // Process each top-level key-value pair - forEach((key, value) { - result[key] = unwrapValue(value); - }); - - return result; - } -} - extension ListValueUtils on List { Value toValue() { final values = map((e) {