Skip to content

Commit

Permalink
Presets (#173)
Browse files Browse the repository at this point in the history
Introduced the ability to save, load, delete, and organize presets that
remember which views were on screen, in what order, and their sizes.


![image](https://github.com/user-attachments/assets/9d262071-11b5-450e-b8c2-06045198ffbd)

---------

Co-authored-by: Gold87 <[email protected]>
Co-authored-by: aidan ahram <[email protected]>
Co-authored-by: Levi Lesches <[email protected]>
  • Loading branch information
4 people authored Oct 9, 2024
1 parent e0beabe commit 362a2da
Show file tree
Hide file tree
Showing 19 changed files with 442 additions and 138 deletions.
Binary file added assets/Rocks_Minerals_Images/Augite.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions lib/data.dart
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,4 @@ export "src/data/settings.dart";
export "src/data/socket.dart";
export "src/data/taskbar_message.dart";
export "src/data/utils.dart";
export "src/data/view_preset.dart";
2 changes: 2 additions & 0 deletions lib/models.dart
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ export "src/models/view/builders/settings_builder.dart";
export "src/models/view/builders/throttle.dart";
export "src/models/view/builders/timer_builder.dart";
export "src/models/view/builders/video_builder.dart";
export "src/models/view/builders/preset_builder.dart";


/// A wrapper model around all other data models used by the app.
///
Expand Down
62 changes: 35 additions & 27 deletions lib/src/data/settings.dart
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import "package:flutter/material.dart";
import "package:rover_dashboard/data.dart";

import "socket.dart";

/// A collection of functions for parsing [Settings].
/// A collection of functions for parsing [Settings].
extension SettingsParser on Json {
/// Parses a [SocketInfo] that may not be present.
SocketInfo? getSocket(String key) {
Expand All @@ -15,7 +14,7 @@ extension SettingsParser on Json {
/// Settings relating to science.
class ScienceSettings {
/// How many frames to render per second.
///
///
/// This does not affect how many frames are sent by the rover per second.
final bool scrollableGraphs;

Expand All @@ -26,7 +25,7 @@ class ScienceSettings {
const ScienceSettings({required this.scrollableGraphs, required this.numSamples});

/// Parses a [ScienceSettings] from JSON.
ScienceSettings.fromJson(Json? json) :
ScienceSettings.fromJson(Json? json) :
numSamples = json?["numSamples"] ?? 3,
scrollableGraphs = json?["scrollableGraphs"] ?? false;

Expand Down Expand Up @@ -76,7 +75,7 @@ class ArmSettings {
});

/// Parses arm settings from a JSON map.
ArmSettings.fromJson(Json? json) :
ArmSettings.fromJson(Json? json) :
shoulder = json?["shoulder"] ?? 0.005,
elbow = json?["elbow"] ?? 0.005,
swivel = json?["swivel"] ?? 0.2,
Expand All @@ -102,8 +101,8 @@ class ArmSettings {
/// Settings related to network configuration.
class NetworkSettings {
/// The amount of time, in seconds, the dashboard should wait before determining it's
/// lost connection to the rover. For reference, the rover should be sending messages
/// at least once per second.
/// lost connection to the rover. For reference, the rover should be sending messages
/// at least once per second.
final double connectionTimeout;

/// The address and port of the subsystems program.
Expand All @@ -116,7 +115,7 @@ class NetworkSettings {
final SocketInfo autonomySocket;

/// The address of the tank. The port is ignored.
///
///
/// The Tank is a model rover that has all the same programs as the rover. This field does not
/// include port numbers because ports are specific to the program, and the tank will have many
/// programs running. Instead, the IP address of all the other programs should be swapped with
Expand All @@ -133,7 +132,7 @@ class NetworkSettings {
});

/// Parses network settings from a JSON map.
NetworkSettings.fromJson(Json? json) :
NetworkSettings.fromJson(Json? json) :
subsystemsSocket = json?.getSocket("subsystemsSocket") ?? SocketInfo.raw("192.168.1.20", 8001),
videoSocket = json?.getSocket("videoSocket") ?? SocketInfo.raw("192.168.1.30", 8002),
autonomySocket = json?.getSocket("autonomySocket") ?? SocketInfo.raw("192.168.1.30", 8003),
Expand All @@ -151,7 +150,7 @@ class NetworkSettings {
}

/// Settings relating to easter eggs.
///
///
/// Implement these! Ask Levi for details.
class EasterEggsSettings {
/// Whether to do a SEGA-like intro during boot.
Expand All @@ -161,7 +160,7 @@ class EasterEggsSettings {
/// Whether clippy should appear by log messages.
final bool enableClippy;
/// Whether to render Bad Apple in the Map page.
final bool badApple;
final bool badApple;

/// A const constructor.
const EasterEggsSettings({
Expand All @@ -172,7 +171,7 @@ class EasterEggsSettings {
});

/// Parses easter eggs settings from JSON.
EasterEggsSettings.fromJson(Json? json) :
EasterEggsSettings.fromJson(Json? json) :
segaIntro = json?["segaIntro"] ?? true,
segaSound = json?["segaSound"] ?? true,
enableClippy = json?["enableClippy"] ?? true,
Expand All @@ -191,9 +190,9 @@ class EasterEggsSettings {
enum SplitMode {
/// Two views are split horizontally, one atop the other.
horizontal("Top and bottom"),
/// Two views are split vertically, side-by-side.
/// Two views are split vertically, side-by-side.
vertical("Side by side");

/// The name to show in the UI.
final String humanName;
/// A const constructor.
Expand All @@ -216,37 +215,41 @@ class DashboardSettings {
final SplitMode splitMode;

/// The precision of the GPS grid.
///
///
/// Since GPS coordinates are decimal values, we divide by this value to get the index of the cell
/// each coordinate belongs to. Smaller sizes means more blocks, but we should be careful that the
/// blocks are big enough to the margin of error of our GPS. This value must be synced with the
/// value in the autonomy program, or else the UI will not be accurate to the rover's logic.
final double mapBlockSize;

/// How many frames to render per second.
///
///
/// This does not affect how many frames are sent by the rover per second.
final int maxFps;

/// The theme of the Dashboard.
/// The theme of the Dashboard.
final ThemeMode themeMode;

/// Whether to split cameras into their own controls.
///
/// Whether to split cameras into their own controls.
///
/// When this is disabled, some other modes, like arm or drive, may move the cameras.
/// When this is enabled, only the dedicated camera control mode can move the cameras.
final bool splitCameras;

/// Whether to default to tank drive controls.
///
///
/// Tank controls offer more custom control, but modern drive controls are more intuitive.
final bool preferTankControls;

/// Whether to have version checking on protobuf messages.
final bool versionChecking;

/// A list of ViewPresets
final List<ViewPreset> presets;

/// A const constructor.
const DashboardSettings({
required this.presets,
required this.splitMode,
required this.mapBlockSize,
required this.maxFps,
Expand All @@ -256,8 +259,12 @@ class DashboardSettings {
required this.versionChecking,
});

/// Parses Dashboard settings from JSON.
DashboardSettings.fromJson(Json? json) :
/// Parses settings from JSON.
DashboardSettings.fromJson(Json? json) :
presets = [
for (final presetJson in json?["presets"] ?? [])
ViewPreset.fromJson(presetJson),
],
splitMode = SplitMode.values[json?["splitMode"] ?? SplitMode.horizontal.index],
mapBlockSize = json?["mapBlockSize"] ?? 1.0,
maxFps = (json?["maxFps"] ?? 60) as int,
Expand All @@ -268,6 +275,7 @@ class DashboardSettings {

/// Serializes these settings to JSON.
Json toJson() => {
"presets" : presets,
"splitMode": splitMode.index,
"mapBlockSize": mapBlockSize,
"maxFps": maxFps,
Expand All @@ -278,13 +286,13 @@ class DashboardSettings {
};
}

/// Contains the settings for running the dashboard and the rover.
/// Contains the settings for running the dashboard and the rover.
class Settings {
/// Settings for the network, like IP addresses and ports.
final NetworkSettings network;

/// Settings for easter eggs.
///
///
/// Please, please, please -- do not remove these (Levi Lesches, '25).
final EasterEggsSettings easterEggs;

Expand All @@ -307,15 +315,15 @@ class Settings {
});

/// Initialize settings from Json.
Settings.fromJson(Json json) :
Settings.fromJson(Json json) :
network = NetworkSettings.fromJson(json["network"]),
easterEggs = EasterEggsSettings.fromJson(json["easterEggs"]),
science = ScienceSettings.fromJson(json["science"]),
arm = ArmSettings.fromJson(json["arm"]),
dashboard = DashboardSettings.fromJson(json["dashboard"]);

/// Converts the data from the settings instance to Json.
Json toJson() => {
Json toJson() => {
"network": network.toJson(),
"easterEggs": easterEggs.toJson(),
"science": science.toJson(),
Expand Down
67 changes: 67 additions & 0 deletions lib/src/data/view_preset.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import "package:rover_dashboard/data.dart";
import "package:rover_dashboard/pages.dart";

/// Preset for the dashboard.
class ViewPreset {
/// Preset name.
final String name;

/// List of views that comes with the views name (and if it is a camera view, its camera name).
final List<DashboardView> views;

/// Ratio of the controller for the resizable row on top.
final List<double> horizontal1;

/// Ratio of the controller for resizable row on bottom.
final List<double> horizontal2;

/// Ratio of the controller for screen 2's first row.
final List<double> horizontal3;

/// Ratio of the controller for screen 2's second row.
final List<double> horizontal4;

/// Ratio of the controller for the resizable column.
final List<double> vertical1;

/// The vertical controller for screen 2.
final List<double> vertical2;

/// A const constructor.
ViewPreset({
required this.name,
required this.views,
required this.horizontal1,
required this.horizontal2,
required this.vertical1,
required this.vertical2,
required this.horizontal3,
required this.horizontal4,
});

/// Parses a view preset from JSON.
ViewPreset.fromJson(Json? json) :
name = json?["name"] ?? "No Name",
views = [
for (final viewJson in json?["views"] ?? [])
DashboardView.fromJson(viewJson) ?? DashboardView.blank,
],
horizontal1 = List<double>.from(json?["horizontal1"] ?? []),
horizontal2 = List<double>.from(json?["horizontal2"] ?? []),
horizontal3 = List<double>.from(json?["horizontal3"] ?? []),
horizontal4 = List<double>.from(json?["horizontal4"] ?? []),
vertical1 = List<double>.from(json?["vertical1"] ?? []),
vertical2 = List<double>.from(json?["vertical2"] ?? []);

/// Serializes a view preset to JSON.
Json toJson() => {
"name": name,
"views" : views,
"horizontal1" : horizontal1,
"horizontal2" : horizontal2,
"horizontal3" : horizontal3,
"horizontal4" : horizontal4,
"vertical1" : vertical1,
"vertical2" : vertical2,
};
}
6 changes: 3 additions & 3 deletions lib/src/models/data/settings.dart
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,10 @@ class SettingsModel extends Model {
}

/// Replaces the current settings with the provided ones.
Future<void> update(Settings value) async {
Future<void> update([Settings? value]) async {
try {
await services.files.writeSettings(value);
all = value;
await services.files.writeSettings(value ?? all);
if (value != null) all = value;
notifyListeners();
} catch (error) {
models.home.setMessage(severity: Severity.critical, text: "Could not save settings: $error");
Expand Down
Loading

0 comments on commit 362a2da

Please sign in to comment.