From 5ac10bbf0e723054a38540ffbf40a6c5f14fa775 Mon Sep 17 00:00:00 2001 From: Gold87 <91761103+Gold872@users.noreply.github.com> Date: Fri, 6 Sep 2024 23:21:51 -0400 Subject: [PATCH] Added struct decoding for pose2d and swerve module state --- .../struct_schemas/pose2d_struct.dart | 60 ++++++ .../swerve_module_state_struct.dart | 51 +++++ pubspec.lock | 24 +-- .../struct_schemas/pose2d_struct_test.dart | 177 ++++++++++++++++++ .../swerve_module_state_struct_test.dart | 138 ++++++++++++++ 5 files changed, 438 insertions(+), 12 deletions(-) create mode 100644 lib/services/struct_schemas/pose2d_struct.dart create mode 100644 lib/services/struct_schemas/swerve_module_state_struct.dart create mode 100644 test/services/struct_schemas/pose2d_struct_test.dart create mode 100644 test/services/struct_schemas/swerve_module_state_struct_test.dart diff --git a/lib/services/struct_schemas/pose2d_struct.dart b/lib/services/struct_schemas/pose2d_struct.dart new file mode 100644 index 00000000..fd1ee270 --- /dev/null +++ b/lib/services/struct_schemas/pose2d_struct.dart @@ -0,0 +1,60 @@ +import 'dart:typed_data'; + +class Pose2dStruct { + static const int length = 24; + + final double x; + final double y; + final double angle; + + const Pose2dStruct({ + required this.x, + required this.y, + required this.angle, + }); + + factory Pose2dStruct.valueFromBytes(Uint8List value) { + ByteData view = ByteData.view(value.buffer); + + int length = view.lengthInBytes; + + double x = 0.0; + double y = 0.0; + double angle = 0.0; + + if (length >= 8) { + x = view.getFloat64(0, Endian.little); + } + if (length >= 16) { + y = view.getFloat64(8, Endian.little); + } + if (length >= 24) { + angle = view.getFloat64(16, Endian.little); + } + + return Pose2dStruct(x: x, y: y, angle: angle); + } + + static List listFromBytes(Uint8List value) { + ByteData view = ByteData.view(value.buffer); + + int viewLength = view.lengthInBytes; + + int arraySize = viewLength ~/ length; + + List poseList = []; + + for (int i = 0; i < arraySize; i++) { + if (i * length + length > viewLength) { + break; + } + + Uint8List elementBytes = + Uint8List.sublistView(view, i * length, i * length + length); + + poseList.add(Pose2dStruct.valueFromBytes(elementBytes)); + } + + return poseList; + } +} diff --git a/lib/services/struct_schemas/swerve_module_state_struct.dart b/lib/services/struct_schemas/swerve_module_state_struct.dart new file mode 100644 index 00000000..b4cd6a49 --- /dev/null +++ b/lib/services/struct_schemas/swerve_module_state_struct.dart @@ -0,0 +1,51 @@ +import 'dart:typed_data'; + +class SwerveModuleStateStruct { + static const int length = 16; + + final double speed; + final double angle; + + const SwerveModuleStateStruct({required this.speed, required this.angle}); + + factory SwerveModuleStateStruct.valueFromBytes(Uint8List value) { + ByteData view = ByteData.view(value.buffer); + + int length = view.lengthInBytes; + + double speed = 0.0; + double angle = 0.0; + + if (length >= 8) { + speed = view.getFloat64(0, Endian.little); + } + if (length >= 16) { + angle = view.getFloat64(8, Endian.little); + } + + return SwerveModuleStateStruct(speed: speed, angle: angle); + } + + static List listFromBytes(Uint8List value) { + ByteData view = ByteData.view(value.buffer); + + int viewLength = view.lengthInBytes; + + int arraySize = viewLength ~/ length; + + List poseList = []; + + for (int i = 0; i < arraySize; i++) { + if (i * length + length > viewLength) { + break; + } + + Uint8List elementBytes = + Uint8List.sublistView(view, i * length, i * length + length); + + poseList.add(SwerveModuleStateStruct.valueFromBytes(elementBytes)); + } + + return poseList; + } +} diff --git a/pubspec.lock b/pubspec.lock index 6473ea27..61bb595c 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -564,18 +564,18 @@ packages: dependency: transitive description: name: leak_tracker - sha256: "3f87a60e8c63aecc975dda1ceedbc8f24de75f09e4856ea27daf8958f2f0ce05" + sha256: "7f0df31977cb2c0b88585095d168e689669a2cc9b97c309665e3386f3e9d341a" url: "https://pub.dev" source: hosted - version: "10.0.5" + version: "10.0.4" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: "932549fb305594d82d7183ecd9fa93463e9914e1b67cacc34bc40906594a1806" + sha256: "06e98f569d004c1315b991ded39924b21af84cf14cc94791b8aea337d25b57f8" url: "https://pub.dev" source: hosted - version: "3.0.5" + version: "3.0.3" leak_tracker_testing: dependency: transitive description: @@ -620,10 +620,10 @@ packages: dependency: transitive description: name: material_color_utilities - sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec + sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a" url: "https://pub.dev" source: hosted - version: "0.11.1" + version: "0.8.0" messagepack: dependency: "direct main" description: @@ -636,10 +636,10 @@ packages: dependency: transitive description: name: meta - sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7 + sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136" url: "https://pub.dev" source: hosted - version: "1.15.0" + version: "1.12.0" mime: dependency: transitive description: @@ -1033,10 +1033,10 @@ packages: dependency: transitive description: name: test_api - sha256: "5b8a98dafc4d5c4c9c72d8b31ab2b23fc13422348d2997120294d3bac86b4ddb" + sha256: "9955ae474176f7ac8ee4e989dadfb411a58c30415bcfb648fa04b2b8a03afa7f" url: "https://pub.dev" source: hosted - version: "0.7.2" + version: "0.7.0" timing: dependency: transitive description: @@ -1177,10 +1177,10 @@ packages: dependency: transitive description: name: vm_service - sha256: f652077d0bdf60abe4c1f6377448e8655008eef28f128bc023f7b5e8dfeb48fc + sha256: "3923c89304b715fb1eb6423f017651664a03bf5f4b29983627c4da791f74a4ec" url: "https://pub.dev" source: hosted - version: "14.2.4" + version: "14.2.1" watcher: dependency: transitive description: diff --git a/test/services/struct_schemas/pose2d_struct_test.dart b/test/services/struct_schemas/pose2d_struct_test.dart new file mode 100644 index 00000000..5c7e0aba --- /dev/null +++ b/test/services/struct_schemas/pose2d_struct_test.dart @@ -0,0 +1,177 @@ +import 'dart:math'; +import 'dart:typed_data'; + +import 'package:flutter_test/flutter_test.dart'; + +import 'package:elastic_dashboard/services/struct_schemas/pose2d_struct.dart'; + +void main() { + test('Pose2D struct with valid data', () { + List rawBytes = [ + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x14, + 0x40, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x14, + 0x40, + 0x18, + 0x2d, + 0x44, + 0x54, + 0xfb, + 0x21, + 0x09, + 0x40 + ]; + Uint8List data = Uint8List.fromList(rawBytes); + + Pose2dStruct pose2dStruct = Pose2dStruct.valueFromBytes(data); + + expect(pose2dStruct.x, 5.0); + expect(pose2dStruct.y, 5.0); + expect(pose2dStruct.angle, pi); + }); + + test('Pose2D struct with missing bytes', () { + List rawBytes = [ + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x14, + 0x40, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x14, + 0x40, + 0x18, + 0x2d, + 0x44, + 0x54 + ]; + Uint8List data = Uint8List.fromList(rawBytes); + + Pose2dStruct pose2dStruct = Pose2dStruct.valueFromBytes(data); + + expect(pose2dStruct.x, 5.0); + expect(pose2dStruct.y, 5.0); + expect(pose2dStruct.angle, 0.0); + }); + + test('Pose2D struct with no bytes', () { + List rawBytes = []; + Uint8List data = Uint8List.fromList(rawBytes); + + Pose2dStruct pose2dStruct = Pose2dStruct.valueFromBytes(data); + + expect(pose2dStruct.x, 0.0); + expect(pose2dStruct.y, 0.0); + expect(pose2dStruct.angle, 0.0); + }); + + test('Pose2D array with valid data', () { + List rawBytes = [ + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x14, + 0x40, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x14, + 0x40, + 0x18, + 0x2d, + 0x44, + 0x54, + 0xfb, + 0x21, + 0x09, + 0x40 + ]; + Uint8List data = + Uint8List.fromList([...rawBytes, ...rawBytes, ...rawBytes]); + + List poseList = Pose2dStruct.listFromBytes(data); + expect(poseList.length, 3); + + for (Pose2dStruct pose in poseList) { + expect(pose.x, 5.0); + expect(pose.y, 5.0); + expect(pose.angle, pi); + } + }); + + test('Pose2D array with missing data', () { + List rawBytes = [ + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x14, + 0x40, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x14, + 0x40, + 0x18, + 0x2d, + 0x44, + 0x54, + 0xfb, + 0x21, + 0x09, + 0x40 + ]; + Uint8List data = Uint8List.fromList([ + ...rawBytes, + ...rawBytes, + ...rawBytes, + 0x00, + 0x00, + 0x00, + 0x00, + 0x14, + 0x40, + 0x00 + ]); + + List poseList = Pose2dStruct.listFromBytes(data); + expect(poseList.length, 3); + + for (Pose2dStruct pose in poseList) { + expect(pose.x, 5.0); + expect(pose.y, 5.0); + expect(pose.angle, pi); + } + }); +} diff --git a/test/services/struct_schemas/swerve_module_state_struct_test.dart b/test/services/struct_schemas/swerve_module_state_struct_test.dart new file mode 100644 index 00000000..fba098f3 --- /dev/null +++ b/test/services/struct_schemas/swerve_module_state_struct_test.dart @@ -0,0 +1,138 @@ +import 'dart:math'; +import 'dart:typed_data'; + +import 'package:flutter_test/flutter_test.dart'; + +import 'package:elastic_dashboard/services/struct_schemas/swerve_module_state_struct.dart'; + +void main() { + test('Module state with valid data', () { + List rawBytes = [ + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x14, + 0x40, + 0x18, + 0x2d, + 0x44, + 0x54, + 0xfb, + 0x21, + 0xf9, + 0x3f + ]; + Uint8List data = Uint8List.fromList(rawBytes); + + SwerveModuleStateStruct moduleStateStruct = + SwerveModuleStateStruct.valueFromBytes(data); + + expect(moduleStateStruct.speed, 5.0); + expect(moduleStateStruct.angle, pi / 2); + }); + + test('Module state with missing bytes', () { + List rawBytes = [ + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x14, + 0x40, + 0x18, + 0x2d, + 0x44, + 0x54, + 0xfb + ]; + Uint8List data = Uint8List.fromList(rawBytes); + + SwerveModuleStateStruct moduleStateStruct = + SwerveModuleStateStruct.valueFromBytes(data); + + expect(moduleStateStruct.speed, 5.0); + expect(moduleStateStruct.angle, 0.0); + }); + + test('Module state with no bytes', () { + List rawBytes = []; + Uint8List data = Uint8List.fromList(rawBytes); + + SwerveModuleStateStruct moduleStateStruct = + SwerveModuleStateStruct.valueFromBytes(data); + + expect(moduleStateStruct.speed, 0.0); + expect(moduleStateStruct.angle, 0.0); + }); + + test('Module state array with valid data', () { + List rawBytes = [ + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x14, + 0x40, + 0x18, + 0x2d, + 0x44, + 0x54, + 0xfb, + 0x21, + 0xf9, + 0x3f + ]; + Uint8List data = + Uint8List.fromList([...rawBytes, ...rawBytes, ...rawBytes]); + + List moduleStateStruct = + SwerveModuleStateStruct.listFromBytes(data); + + expect(moduleStateStruct.length, 3); + + for (SwerveModuleStateStruct moduleStateStruct in moduleStateStruct) { + expect(moduleStateStruct.speed, 5.0); + expect(moduleStateStruct.angle, pi / 2); + } + }); + + test('Module state array with missing data', () { + List rawBytes = [ + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x14, + 0x40, + 0x18, + 0x2d, + 0x44, + 0x54, + 0xfb, + 0x21, + 0xf9, + 0x3f + ]; + Uint8List data = Uint8List.fromList( + [...rawBytes, ...rawBytes, ...rawBytes, 0x00, 0x00, 0x14, 0x40]); + + List moduleStateStruct = + SwerveModuleStateStruct.listFromBytes(data); + + expect(moduleStateStruct.length, 3); + + for (SwerveModuleStateStruct moduleStateStruct in moduleStateStruct) { + expect(moduleStateStruct.speed, 5.0); + expect(moduleStateStruct.angle, pi / 2); + } + }); +}