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

Base Station Integration #199

Open
wants to merge 20 commits into
base: main
Choose a base branch
from
Open
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
3 changes: 3 additions & 0 deletions lib/pages.dart
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,9 @@ class Routes {
/// The name of the rocks page.
static const String rocks = "Rocks";

/// The name of the base station page
static const String baseStation = "Base Station";

/// The name of the blank page.
static const String blank = "Remove View";
}
40 changes: 40 additions & 0 deletions lib/src/data/metrics/antenna.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import "package:rover_dashboard/data.dart";

/// Metrics about the antenna received from the Base Station
class BaseStationMetrics extends Metrics<BaseStationData> {
/// Metrics for the antenna
BaseStationMetrics() : super(BaseStationData());

@override
String get name => "Base Station";

String _controlModeName(AntennaControlMode mode) => switch(mode) {
AntennaControlMode.ANTENNA_CONTROL_MODE_UNDEFINED => "Unknown",
AntennaControlMode.TRACK_ROVER => "Track Rover",
AntennaControlMode.MANUAL_CONTROL => "Manual",
_ => "Unknown",
};

@override
List<MetricLine> get allMetrics => [
MetricLine("Control Mode: ${_controlModeName(data.mode)}"),
MetricLine("Antenna:"),
MetricLine(
" Is Moving: ${data.antenna.swivel.isMoving.displayName}",
severity: data.antenna.swivel.isMoving.toBool() ? Severity.info : null,
),
MetricLine(" Direction: ${data.antenna.swivel.direction.humanName}"),
MetricLine(" Steps: ${data.antenna.swivel.currentStep} --> ${data.antenna.swivel.targetStep}"),
MetricLine(" Angle: ${data.antenna.swivel.currentAngle.toDegrees() % 360}°"),
MetricLine(" Target Angle: ${data.antenna.swivel.targetAngle.toDegrees() % 360}°"),
];

@override
Version parseVersion(BaseStationData message) => message.version;

@override
Version get supportedVersion => Version(major: 1);

@override
Message get versionCommand => BaseStationCommand(version: supportedVersion);
}
10 changes: 2 additions & 8 deletions lib/src/data/metrics/position.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import "package:rover_dashboard/data.dart";
import "package:rover_dashboard/models.dart";

/// Metrics about the rover's position and orientation.
///
Expand All @@ -10,15 +11,8 @@ class PositionMetrics extends Metrics<RoverPosition> {
@override
String get name => "Position";

/// A helper for [baseStation].
GpsCoordinates? _baseStation;

/// The position of the base station. Setting this value updates the UI.
GpsCoordinates get baseStation => _baseStation ?? data.gps;
set baseStation(GpsCoordinates value) {
_baseStation = value;
notifyListeners();
}
GpsCoordinates get baseStation => models.settings.baseStation.gpsCoordinates;

/// Gets the severity of the rover's orientation for both pitch and roll.
Severity? getRotationSeverity(double orientation) {
Expand Down
7 changes: 6 additions & 1 deletion lib/src/data/modes.dart
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,12 @@ enum OperatingMode {
/// Arm mode.
///
/// Focus on helping the user manipulate the arm.
arm("Arm");
arm("Arm"),

/// Base Station mode.
///
/// Focus on manually manipulating the base station antenna
baseStation("Base Station");

/// The name of this mode.
///
Expand Down
57 changes: 57 additions & 0 deletions lib/src/data/settings.dart
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,51 @@ class ArmSettings {
};
}

/// Settings related to the base station
class BaseStationSettings {
/// The latitude of the base station
final double latitude;

/// The longitude of the base station
final double longitude;

/// The altitude of the base station in meters
final double altitude;

/// The angle tolerance in degrees
final double angleTolerance;

/// Const constructor for base station settings
const BaseStationSettings({
required this.latitude,
required this.longitude,
required this.altitude,
required this.angleTolerance,
});

/// Parses base station settings from json
BaseStationSettings.fromJson(Json? json) :
latitude = json?["latitude"] ?? 0,
longitude = json?["longitude"] ?? 0,
altitude = json?["altitude"] ?? 0,
angleTolerance = json?["angleTolerance"] ?? 5;

/// Serializes the base station settings to a json map
Json toJson() => {
"latitude": latitude,
"longitude": longitude,
"altitude": altitude,
"angleTolerance": angleTolerance,
};

/// The GPS coordinates of the base station
GpsCoordinates get gpsCoordinates => GpsCoordinates(
latitude: latitude,
longitude: longitude,
altitude: altitude,
);
}

/// Settings related to network configuration.
class NetworkSettings {
/// The amount of time, in seconds, the dashboard should wait before determining it's
Expand All @@ -122,12 +167,16 @@ class NetworkSettings {
/// the tank when it's being used.
final SocketInfo tankSocket;

/// The address and port of the base station program.
final SocketInfo baseSocket;

/// Creates a new network settings object.
NetworkSettings({
required this.subsystemsSocket,
required this.videoSocket,
required this.autonomySocket,
required this.tankSocket,
required this.baseSocket,
required this.connectionTimeout,
});

Expand All @@ -137,6 +186,7 @@ class NetworkSettings {
videoSocket = json?.getSocket("videoSocket") ?? SocketInfo.raw("192.168.1.30", 8002),
autonomySocket = json?.getSocket("autonomySocket") ?? SocketInfo.raw("192.168.1.30", 8003),
tankSocket = json?.getSocket("tankSocket") ?? SocketInfo.raw("192.168.1.40", 8000),
baseSocket = json?.getSocket("baseSocket") ?? SocketInfo.raw("192.168.1.50", 8005),
connectionTimeout = json?["connectionTimeout"] ?? 5;

/// Serializes these settings to JSON.
Expand All @@ -145,6 +195,7 @@ class NetworkSettings {
"videoSocket": videoSocket.toJson(),
"autonomySocket": autonomySocket.toJson(),
"tankSocket": tankSocket.toJson(),
"baseSocket": baseSocket.toJson(),
"connectionTimeout": connectionTimeout,
};
}
Expand Down Expand Up @@ -308,12 +359,16 @@ class Settings {
/// Settings for the science analysis.
final ScienceSettings science;

/// Settings for the base station
final BaseStationSettings baseStation;

/// Settings related to the dashboard itself.
final DashboardSettings dashboard;

/// A const constructor.
const Settings({
required this.network,
required this.baseStation,
required this.easterEggs,
required this.science,
required this.arm,
Expand All @@ -323,6 +378,7 @@ class Settings {
/// Initialize settings from Json.
Settings.fromJson(Json json) :
network = NetworkSettings.fromJson(json["network"]),
baseStation = BaseStationSettings.fromJson(json["baseStation"]),
easterEggs = EasterEggsSettings.fromJson(json["easterEggs"]),
science = ScienceSettings.fromJson(json["science"]),
arm = ArmSettings.fromJson(json["arm"]),
Expand All @@ -331,6 +387,7 @@ class Settings {
/// Converts the data from the settings instance to Json.
Json toJson() => {
"network": network.toJson(),
"baseStation": baseStation.toJson(),
"easterEggs": easterEggs.toJson(),
"science": science.toJson(),
"arm": arm.toJson(),
Expand Down
3 changes: 3 additions & 0 deletions lib/src/models/data/settings.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ class SettingsModel extends Model {
/// The user's network settings.
NetworkSettings get network => all.network;

/// The user's base station settings
BaseStationSettings get baseStation => all.baseStation;

/// The user's arm settings.
ArmSettings get arm => all.arm;

Expand Down
7 changes: 6 additions & 1 deletion lib/src/models/data/sockets.dart
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,11 @@ class Sockets extends Model {
/// A UDP socket for controlling autonomy.
late final autonomy = DashboardSocket(device: Device.AUTONOMY);

/// A UDP socket for controlling the base station
late final baseStation = DashboardSocket(device: Device.BASE_STATION);

/// A list of all the sockets this model manages.
List<DashboardSocket> get sockets => [data, video, autonomy];
List<DashboardSocket> get sockets => [data, video, autonomy, baseStation];

/// The rover-like system currently in use.
RoverType rover = RoverType.rover;
Expand Down Expand Up @@ -45,6 +48,7 @@ class Sockets extends Model {
Device.SUBSYSTEMS => data,
Device.VIDEO => video,
Device.AUTONOMY => autonomy,
Device.BASE_STATION => baseStation,
_ => null,
};

Expand Down Expand Up @@ -98,6 +102,7 @@ class Sockets extends Model {
data.destination = settings.subsystemsSocket.copyWith(address: addressOverride);
video.destination = settings.videoSocket.copyWith(address: addressOverride);
autonomy.destination = settings.autonomySocket.copyWith(address: addressOverride);
baseStation.destination = settings.baseSocket.copyWith(address: addressOverride);
}

/// Resets all the sockets.
Expand Down
6 changes: 5 additions & 1 deletion lib/src/models/rover/controller.dart
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,11 @@ class Controller extends Model {
final messages = controls.parseInputs(state);
for (final message in messages) {
// print(message.toProto3Json());
models.messages.sendMessage(message);
if (message.messageName != BaseStationCommand().messageName) {
models.messages.sendMessage(message);
} else {
models.sockets.baseStation.sendMessage(message);
}
}
notifyListeners();
}
Expand Down
34 changes: 34 additions & 0 deletions lib/src/models/rover/controls/base_station.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import "dart:math";

import "package:burt_network/protobuf.dart";
import "package:rover_dashboard/models.dart";
import "package:rover_dashboard/src/data/modes.dart";
import "package:rover_dashboard/src/services/gamepad/state.dart";

/// A [RoverControls] that contols the Base Station manually
class BaseStationControls extends RoverControls {
@override
OperatingMode get mode => OperatingMode.baseStation;

@override
Iterable<Message> get onDispose => [];

@override
Iterable<Message> parseInputs(GamepadState state) => [
BaseStationCommand(
mode: AntennaControlMode.MANUAL_CONTROL,
manualCommand: AntennaFirmwareCommand(
swivel: MotorCommand(
// 2.5 degrees per second
moveRadians: (2.5 * (gamepadDelay.inMilliseconds / 1e3)) * pi / 180,
),
),
version: Version(major: 1),
),
];

@override
Map<String, String> get buttonMapping => {
"Swivel": "Right Joystick (horizontal)",
};
}
2 changes: 2 additions & 0 deletions lib/src/models/rover/controls/controls.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import "package:rover_dashboard/data.dart";
import "package:rover_dashboard/services.dart";

import "arm.dart";
import "base_station.dart";
import "camera.dart";
import "tank_drive.dart";
import "none.dart";
Expand Down Expand Up @@ -34,6 +35,7 @@ abstract class RoverControls {
OperatingMode.none => NoControls(),
OperatingMode.cameras => CameraControls(),
OperatingMode.modernDrive => ModernDriveControls(),
OperatingMode.baseStation => BaseStationControls(),
};

/// The [OperatingMode] for these controls.
Expand Down
12 changes: 11 additions & 1 deletion lib/src/models/rover/metrics.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import "dart:async";
import "package:rover_dashboard/data.dart";
import "package:rover_dashboard/models.dart";
import "package:rover_dashboard/src/data/metrics/antenna.dart";

/// A data model that listens for updated data and provides [Metrics] to the UI.
class RoverMetrics extends Model {
Expand All @@ -22,11 +23,14 @@ class RoverMetrics extends Model {
/// Vitals data from the rover.
final vitals = VitalsMetrics();

/// Data from the Base Station
final baseStation = BaseStationMetrics();

/// A list of all the metrics to iterate over.
///
/// NOTE: Keep this as a getter, NOT a field. If this is made a field, then it won't update
/// when new data is received. As a getter, every time it is called it will use new data.
List<Metrics> get allMetrics => [vitals, position, drive, science, arm, gripper];
List<Metrics> get allMetrics => [vitals, position, drive, science, arm, gripper, baseStation];

/// Whether the given command is supported by the rover.
bool isSupportedVersion(Message command) {
Expand All @@ -40,6 +44,7 @@ class RoverMetrics extends Model {
DriveCommand().messageName: drive,
ArmCommand().messageName: arm,
GripperCommand().messageName: gripper,
BaseStationCommand().messageName: baseStation,
};

@override
Expand Down Expand Up @@ -69,6 +74,11 @@ class RoverMetrics extends Model {
constructor: GripperData.fromBuffer,
callback: gripper.update,
);
models.messages.stream.onMessage(
name: BaseStationData().messageName,
constructor: BaseStationData.fromBuffer,
callback: baseStation.update,
);
drive.addListener(vitals.notify);
// versionTimer = Timer.periodic(versionInterval, _sendVersions);
}
Expand Down
Loading
Loading