Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions lib/src/components/arm/arm.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import '../../../viam_sdk.dart';
import '../../gen/common/v1/common.pb.dart';
import '../../resource/base.dart';
import '../../robot/client.dart';

/// {@category Components}
/// Arm represents a physical robot arm that exists in three-dimensional space.
Expand Down Expand Up @@ -80,6 +79,15 @@ abstract class Arm extends Resource {
/// For more information, see [Arm component](https://docs.viam.com/dev/reference/apis/components/arm/#ismoving).
Future<bool> isMoving();

/// Get the kinematics data associated with the [Arm]
///
/// ```
/// var kinematics = await myArm.getKinematics();
/// ```
///
/// For more information, see [Arm component](https://docs.viam.com/dev/reference/apis/components/arm/#getkinematics).
Future<Kinematics> getKinematics({Map<String, dynamic>? extra});

/// Get the [ResourceName] for this [Arm] with the given [name].
///
/// ```
Expand Down
10 changes: 10 additions & 0 deletions lib/src/components/arm/client.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import '../../gen/component/arm/v1/arm.pbgrpc.dart';
import '../../gen/google/protobuf/struct.pb.dart';
import '../../resource/base.dart';
import '../../utils.dart';
import '../gripper/gripper.dart';
import 'arm.dart';

/// {@category Components}
Expand Down Expand Up @@ -91,4 +92,13 @@ class ArmClient extends Arm with RPCDebugLoggerMixin implements ResourceRPCClien
final response = await client.doCommand(request, options: callOptions);
return response.result.toMap();
}

@override
Future<Kinematics> getKinematics({Map<String, dynamic>? extra}) async {
final request = GetKinematicsRequest()
..name = name
..extra = extra?.toStruct() ?? Struct();
final response = await client.getKinematics(request, options: callOptions);
return Kinematics.fromProto(response);
}
}
9 changes: 6 additions & 3 deletions lib/src/components/arm/service.dart
Original file line number Diff line number Diff line change
Expand Up @@ -84,9 +84,12 @@ class ArmService extends ArmServiceBase {
}

@override
Future<GetKinematicsResponse> getKinematics(ServiceCall call, GetKinematicsRequest request) {
// TODO: implement getKinematics
throw UnimplementedError();
Future<GetKinematicsResponse> getKinematics(ServiceCall call, GetKinematicsRequest request) async {
final arm = _armFromManager(request.name);
final response = await arm.getKinematics(extra: request.extra.toMap());
return GetKinematicsResponse()
..format = response.format
..kinematicsData = response.raw;
}

@override
Expand Down
44 changes: 32 additions & 12 deletions lib/widgets/resources/arm_widgets/joint_positions_widget.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

import '../../../src/utils.dart';
import '../../../viam_sdk.dart' as viam;
import '../arm.dart';

Expand All @@ -20,6 +21,7 @@ class JointPositionsWidget extends StatefulWidget {
class _JointPositionsWidgetState extends State<JointPositionsWidget> {
List<double> _jointValues = [];
bool _isLive = false;
bool _isLoading = false;
List<TextEditingController> _textControllers = [];

@override
Expand All @@ -39,21 +41,39 @@ class _JointPositionsWidgetState extends State<JointPositionsWidget> {
}

Future<void> _getJointPositions() async {
for (final controller in _textControllers) {
controller.dispose();
}

_jointValues = await widget.arm.jointPositions();
_textControllers = List.generate(
_jointValues.length,
(index) => TextEditingController(text: _jointValues[index].toStringAsFixed(1)),
);
if (mounted) {
setState(() {});
if (_isLoading) return;
_isLoading = true;
try {
_jointValues = await widget.arm.jointPositions();

if (mounted) {
if (_textControllers.isEmpty || _textControllers.length != _jointValues.length) {
for (final controller in _textControllers) {
controller.dispose();
}
_textControllers = List.generate(
_jointValues.length,
(index) => TextEditingController(text: _jointValues[index].toStringAsFixed(1)),
);
} else {
for (int i = 0; i < _jointValues.length; i++) {
_textControllers[i].text = _jointValues[i].toStringAsFixed(1);
}
}
setState(() {});
}
} catch (e) {
if (mounted) await showErrorDialog(context, title: 'An error occurred', error: e.toString());
} finally {
if (mounted) {
_isLoading = false;
}
}
}

void _updateJointValue(int index, double value) {
if (!mounted || _textControllers.isEmpty || index >= _textControllers.length) return;

const double minPosition = -359.0;
const double maxPosition = 359.0;
final clampedValue = value.clamp(minPosition, maxPosition);
Expand Down Expand Up @@ -187,7 +207,7 @@ class _BuildJointControlRow extends StatelessWidget {
textAlign: TextAlign.center,
keyboardType: const TextInputType.numberWithOptions(decimal: true, signed: true),
inputFormatters: [
FilteringTextInputFormatter.allow(RegExp(r'^\d+\.?\d{0,1}')),
FilteringTextInputFormatter.allow(RegExp(r'^-?\d*\.?\d*')),
],
onSubmitted: onSubmitted,
),
Expand Down
Loading