Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Map Page Rework #171

Open
wants to merge 35 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
418324e
Format and refactor
Gold872 Sep 30, 2024
42ea897
Rearranged the entire page
Gold872 Sep 30, 2024
28e9133
Method and label renaming
Gold872 Sep 30, 2024
e91fc65
Fix bad apple drawing after stopping
Gold872 Sep 30, 2024
d290e63
Merge branch 'main' into map-rework
Gold872 Oct 15, 2024
a43d112
Merge branch 'main' of https://github.com/BinghamtonRover/Dashboard i…
Gold872 Oct 29, 2024
b2c2b2e
Made map widget square
Gold872 Nov 5, 2024
1bb55fb
Added drag and drop for planning tasks
Gold872 Nov 5, 2024
a531fb3
Adjusted opacity of drag and drop
Gold872 Nov 5, 2024
077a129
Raised minimum width needed for legend
Gold872 Nov 5, 2024
9cd6a82
A bunch of improvements
Gold872 Nov 9, 2024
5653d7c
Don't display markers over obstacles
Gold872 Nov 9, 2024
5b8be5c
Merge branch 'main' of https://github.com/BinghamtonRover/Dashboard i…
Gold872 Nov 9, 2024
7a6011d
Fix everything being wrong with different grid sizes
Gold872 Nov 10, 2024
b31b543
Improved bad apple
Gold872 Nov 10, 2024
e601ac9
Use custom paint for bad apple
Gold872 Nov 10, 2024
c4816c4
Improve audio sync
Gold872 Nov 10, 2024
a4e83cd
Allow bad apple to be played while connected to autonomy
Gold872 Nov 10, 2024
62dcf9d
Increased precision of gps metrics
Gold872 Nov 13, 2024
adb5a80
Fixed "place markers here"
Gold872 Nov 13, 2024
1b9bf41
Fixed displaying cells behind rover
Gold872 Nov 13, 2024
7694e08
Restore original zoom after playing bad apple
Gold872 Nov 13, 2024
d7d30e3
Actually fixed the "place marker here" I swear it works now
Gold872 Nov 13, 2024
c6eb3a3
Merge branch 'main' into map-rework
Levi-Lesches Nov 19, 2024
4270979
Split map page into files
Levi-Lesches Nov 19, 2024
9d53b59
Stop autonomy task unless in autonomy mode
Levi-Lesches Nov 19, 2024
b55d287
Moved Bad Apple to a new file
Levi-Lesches Nov 19, 2024
73ff92c
Fixed last preset issue
Levi-Lesches Nov 19, 2024
4d72da4
Switched map to use meters
Gold872 Nov 20, 2024
2cedfca
Merge branch 'main' into map-rework
Levi-Lesches Nov 28, 2024
647be88
Bumped to burt_network 2.2.0
Levi-Lesches Nov 28, 2024
a3cf4fc
Fixed paths not displaying behind rover
Gold872 Nov 28, 2024
46e8a90
Fixed New Task button
Gold872 Nov 28, 2024
a06de9f
Fixed coordinate directions
Gold872 Dec 14, 2024
936d9c6
Added RTK mode metrics and indicator
Gold872 Dec 18, 2024
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
13 changes: 11 additions & 2 deletions lib/src/data/metrics/position.dart
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,21 @@ class PositionMetrics extends Metrics<RoverPosition> {
}
}

/// Gets the human friendly name of the RTK fix mode
String getRTKString(RTKMode mode) => switch(mode) {
RTKMode.RTK_FIXED => "Fixed",
RTKMode.RTK_FLOAT => "Float",
RTKMode.RTK_NONE => "None",
_ => "None",
};

@override
List<MetricLine> get allMetrics => [
MetricLine("GPS: "),
MetricLine(" Latitude: ${data.gps.latitude.toStringAsFixed(6)}°",),
MetricLine(" Longitude: ${data.gps.longitude.toStringAsFixed(6)}°",),
MetricLine(" Latitude: ${data.gps.latitude.toStringAsFixed(10)}°",),
MetricLine(" Longitude: ${data.gps.longitude.toStringAsFixed(10)}°",),
MetricLine(" Altitude: ${data.gps.altitude.toStringAsFixed(2)} m"),
MetricLine(" RTK Mode: ${getRTKString(data.gps.rtkMode)}"),
MetricLine("Orientation:",),
MetricLine(" X: ${data.orientation.x.toStringAsFixed(2)}°", severity: getRotationSeverity(data.orientation.x)),
MetricLine(" Y: ${data.orientation.y.toStringAsFixed(2)}°", severity: getRotationSeverity(data.orientation.y)),
Expand Down
3 changes: 3 additions & 0 deletions lib/src/models/data/views.dart
Original file line number Diff line number Diff line change
Expand Up @@ -88,10 +88,13 @@ class ViewsModel extends Model with PresetsModel {
// Wait for all views to reset so as not to cause overflow issues
await nextFrame();
setNumViews(preset.views.length);
notifyListeners();
// Wait 3 frames for flutter_resizable container to load
await nextFrame();
await nextFrame();
await nextFrame();
await nextFrame();
await nextFrame();
if (preset.horizontal1.isNotEmpty) horizontalController1.setRatios(preset.horizontal1);
if (preset.horizontal2.isNotEmpty) horizontalController2.setRatios(preset.horizontal2);
if (preset.horizontal3.isNotEmpty) horizontalController3.setRatios(preset.horizontal3);
Expand Down
6 changes: 3 additions & 3 deletions lib/src/models/rover/rover.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import "package:rover_dashboard/models.dart";
import "settings.dart";

/// The model to control the entire rover.
///
///
/// Find more specific functionality in this class's fields.
class Rover extends Model {
/// Monitors metrics coming from the rover.
Expand Down Expand Up @@ -41,13 +41,13 @@ class Rover extends Model {
Iterable<Controller> get controllers => [controller1, controller2, controller3];

/// Whether the rover is connected.
bool get isConnected => models.sockets.data.isConnected;
bool get isConnected => models.sockets.sockets.any((socket) => socket.isConnected);

/// The current status of the rover.
ValueNotifier<RoverStatus> status = ValueNotifier(RoverStatus.DISCONNECTED);

@override
Future<void> init() async {
Future<void> init() async {
setDefaultControls();
await metrics.init();
await controller1.init();
Expand Down
133 changes: 133 additions & 0 deletions lib/src/models/view/bad_apple.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
import "dart:async";
import "dart:ui";

import "package:flutter/material.dart";
import "package:flutter/services.dart";
import "package:just_audio/just_audio.dart";
import "package:rover_dashboard/data.dart";
import "package:rover_dashboard/models.dart";

/// The Bad Apple Easter Egg.
///
/// This Easter Egg renders the Bad Apple video in the map page by grabbing
/// each frame and assigning an obstacle to each black pixel.
mixin BadAppleViewModel on ChangeNotifier {
/// Whether the UI is currently playing Bad Apple
bool isPlayingBadApple = false;

/// Which frame in the Bad Apple video we are up to right now
int badAppleFrame = 0;

/// The zoom of the map before playing bad apple
late int _originalZoom = gridSize;

/// The audio player for the Bad Apple music
final badAppleAudioPlayer = AudioPlayer();

/// How many frames in a second are being shown
static const badAppleFps = 1;

/// The last frame of Bad Apple
static const badAppleLastFrame = 6570;

/// A stopwatch to track the current time in the bad apple video
final Stopwatch _badAppleStopwatch = Stopwatch();

/// Cleans up any resources used by Bad Apple.
void disposeBadApple() => badAppleAudioPlayer.dispose();

/// The amount of blocks in the width and height of the grid.
///
/// Keep this an odd number to keep the rover in the center.
int get gridSize;

/// Sets the zoom of the map.
void zoom(int newSize);

/// Sets the grid data of the map.
set data(AutonomyData value);

/// Starts playing Bad Apple.
Future<void> startBadApple() async {
isPlayingBadApple = true;
notifyListeners();
_originalZoom = gridSize;
zoom(50);
badAppleFrame = 0;
Timer.run(() async {
await badAppleAudioPlayer.setAsset("assets/bad_apple2.mp3", preload: false);
badAppleAudioPlayer.play().ignore();
_badAppleStopwatch.start();
});

while (isPlayingBadApple) {
await Future<void>.delayed(Duration.zero);
var sampleTime = _badAppleStopwatch.elapsed;
if (badAppleAudioPlayer.position != Duration.zero) {
sampleTime = badAppleAudioPlayer.position;
}
badAppleFrame = ((sampleTime.inMicroseconds.toDouble() / 1e6) * 30.0).round();
if (badAppleFrame >= badAppleLastFrame) {
stopBadApple();
break;
}
final obstacles = await _loadBadAppleFrame(badAppleFrame);
if (obstacles == null) {
continue;
}
if (!isPlayingBadApple) {
break;
}
data = AutonomyData(obstacles: obstacles);
notifyListeners();
}
}

Future<List<GpsCoordinates>?> _loadBadAppleFrame(int videoFrame) async {
// final filename = "assets/bad_apple/image_480.jpg";
final filename = "assets/bad_apple/image_$videoFrame.jpg";
final buffer = await rootBundle.loadBuffer(filename);
final codec = await instantiateImageCodecWithSize(buffer);
final frame = await codec.getNextFrame();
final image = frame.image;
if (image.height != 50 || image.width != 50) {
models.home.setMessage(severity: Severity.error, text: "Wrong Bad Apple frame size");
stopBadApple();
return null;
}
final imageData = await image.toByteData();
if (imageData == null) {
models.home.setMessage(severity: Severity.error, text: "Could not load Bad Apple frame");
stopBadApple();
return null;
}
var offset = 0;
final obstacles = <GpsCoordinates>[];
for (var row = 0; row < image.height; row++) {
for (var col = 0; col < image.width; col++) {
final pixel = imageData.getUint8(offset++);
imageData.getUint8(offset++);
imageData.getUint8(offset++);
imageData.getUint8(offset++);
final isBlack = pixel < 100; // dealing with lossy compression, not 255 and 0
final coordinate = GpsCoordinates(latitude: (row - image.height).abs().toDouble(), longitude: (image.width - col - 1).abs().toDouble());
if (isBlack) obstacles.add(coordinate);
}
}
if (!isPlayingBadApple) {
return null;
}
return obstacles;
}

/// Stops playing Bad Apple and resets the UI.
void stopBadApple() {
isPlayingBadApple = false;
data = AutonomyData();
badAppleAudioPlayer.stop();
_badAppleStopwatch.stop();
_badAppleStopwatch.reset();
zoom(_originalZoom);
notifyListeners();
}
}
2 changes: 1 addition & 1 deletion lib/src/models/view/builders/autonomy_command.dart
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ class AutonomyCommandBuilder extends ValueBuilder<AutonomyCommand> {
}

/// Sends this command to the rover using [Sockets.autonomy].
Future<void> submit() async {
Future<void> submit(AutonomyCommand value) async {
_handshake = null;
isLoading = true;
notifyListeners();
Expand Down
4 changes: 2 additions & 2 deletions lib/src/models/view/builders/gps.dart
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,8 @@ class GpsBuilder extends ValueBuilder<GpsCoordinates> {
GpsCoordinates get value => switch (type) {
GpsType.decimal => GpsCoordinates(longitude: longDecimal.value, latitude: latDecimal.value),
GpsType.degrees => GpsCoordinates(
longitude: longDegrees.value + (longMinutes.value / 60) + (longSeconds.value / 3600),
latitude: latDegrees.value + (latMinutes.value / 60) + (latSeconds.value / 3600),
longitude: longDegrees.value + (longMinutes.value / 60.0) + (longSeconds.value / 3600.0),
latitude: latDegrees.value + (latMinutes.value / 60.0) + (latSeconds.value / 3600.0),
),
};

Expand Down
Loading
Loading