diff --git a/.github/workflows/analyze.yml b/.github/workflows/analyze.yml index b6f41da..4b98cc1 100644 --- a/.github/workflows/analyze.yml +++ b/.github/workflows/analyze.yml @@ -16,12 +16,12 @@ jobs: # You can specify other versions if desired, see documentation here: # https://github.com/dart-lang/setup-dart/blob/main/README.md - uses: dart-lang/setup-dart@v1 - with: - sdk: 3.2.2 + with: + sdk: 3.5.2 - name: Install dependencies run: dart pub get - + - name: Analyze project source run: dart analyze --fatal-infos diff --git a/bin/arcuo.dart b/bin/arcuo.dart new file mode 100644 index 0000000..ae9f7d8 --- /dev/null +++ b/bin/arcuo.dart @@ -0,0 +1,16 @@ +import "package:autonomy/autonomy.dart"; + +void main() async { + final rover = RoverAutonomy(); + rover.drive = RoverDrive(collection: rover, useImu: false, useGps: false); + await rover.init(); + //await rover.waitForValue(); + final didSeeAruco = await rover.drive.spinForAruco(); + if (didSeeAruco) { + rover.logger.info("Found aruco"); + await rover.drive.approachAruco(); + } else { + rover.logger.warning("COuld not find aruco"); + } + rover.logger.info("Done"); +} diff --git a/bin/aruco.dart b/bin/aruco.dart new file mode 100644 index 0000000..5f9fdcb --- /dev/null +++ b/bin/aruco.dart @@ -0,0 +1,15 @@ +import "package:autonomy/autonomy.dart"; + +void main() async { + final rover = RoverAutonomy(); + await rover.init(); + await rover.waitForValue(); + final didSeeAruco = await rover.drive.spinForAruco(); + if (didSeeAruco) { + rover.logger.info("Found aruco"); + await rover.drive.approachAruco(); + } else { + rover.logger.warning("COuld not find aruco"); + } + rover.logger.info("Done"); +} diff --git a/bin/sensorless.dart b/bin/sensorless.dart index 48de0c4..acc2569 100644 --- a/bin/sensorless.dart +++ b/bin/sensorless.dart @@ -1,6 +1,7 @@ import "package:burt_network/logging.dart"; import "package:burt_network/generated.dart"; +import "package:autonomy/interfaces.dart"; import "package:autonomy/simulator.dart"; import "package:autonomy/rover.dart"; diff --git a/bin/task.dart b/bin/task.dart index 5246955..6366d32 100644 --- a/bin/task.dart +++ b/bin/task.dart @@ -12,13 +12,15 @@ final obstacles = [ // Enter in the Dashboard: Destination = (lat=7, long=0); void main() async { - Logger.level = LogLevel.all; - final simulator = AutonomySimulator(); - final detector = DetectorSimulator(collection: simulator, obstacles: obstacles); - simulator.detector = detector; + Logger.level = LogLevel.debug; + final simulator = RoverAutonomy(); + simulator.detector = DetectorSimulator(collection: simulator, obstacles: obstacles); simulator.pathfinder = RoverPathfinder(collection: simulator); simulator.orchestrator = RoverOrchestrator(collection: simulator); - simulator.drive = DriveSimulator(collection: simulator, shouldDelay: true); +// simulator.drive = SensorlessDrive(collection: simulator, useImu: true, useGps: true); await simulator.init(); - await simulator.server.waitForConnection(); + await simulator.waitForValue(); + await simulator.drive.faceNorth(); +// await simulator.drive.goForward(); +// await simulator.server.waitForConnection(); } diff --git a/bin/test.dart b/bin/test.dart new file mode 100644 index 0000000..44488b6 --- /dev/null +++ b/bin/test.dart @@ -0,0 +1,17 @@ +import "package:autonomy/autonomy.dart"; +import "package:burt_network/logging.dart"; +import "package:autonomy/src/rover/gps.dart"; +import "package:autonomy/src/rover/imu.dart"; + +void main() async { + Logger.level = LogLevel.all; + final rover = RoverAutonomy(); + rover.gps = RoverGps(collection: rover); + rover.imu = RoverImu(collection: rover); + rover.drive = RoverDrive(collection: rover, useGps: false, useImu: false); + + await rover.init(); + + // rover.logger.info("Done"); + // await rover.dispose(); +} diff --git a/lib/interfaces.dart b/lib/interfaces.dart index 66d82f3..bd903f0 100644 --- a/lib/interfaces.dart +++ b/lib/interfaces.dart @@ -9,7 +9,7 @@ export "src/interfaces/imu.dart"; export "src/interfaces/imu_utils.dart"; export "src/interfaces/pathfinding.dart"; export "src/interfaces/server.dart"; -export "src/interfaces/realsense.dart"; +export "src/interfaces/video.dart"; export "src/interfaces/receiver.dart"; export "src/interfaces/reporter.dart"; export "src/interfaces/service.dart"; diff --git a/lib/rover.dart b/lib/rover.dart index db42339..9f664d9 100644 --- a/lib/rover.dart +++ b/lib/rover.dart @@ -4,28 +4,34 @@ export "src/rover/orchestrator.dart"; export "src/rover/sensorless.dart"; import "package:autonomy/interfaces.dart"; -import "package:autonomy/simulator.dart"; -import "package:burt_network/logging.dart"; +import "package:burt_network/burt_network.dart"; import "src/rover/pathfinding.dart"; -import "src/rover/server.dart"; import "src/rover/drive.dart"; import "src/rover/gps.dart"; import "src/rover/imu.dart"; import "src/rover/orchestrator.dart"; -import "src/rover/realsense.dart"; +import "src/rover/video.dart"; +import "src/rover/detector.dart"; /// A collection of all the different services used by the autonomy program. class RoverAutonomy extends AutonomyInterface { /// A server to communicate with the dashboard and receive data from the subsystems. - @override late final AutonomyServer server = AutonomyServer(collection: this); + // @override late final AutonomyServer server = AutonomyServer(collection: this); + @override late final server = RoverSocket(port: 8003, device: Device.AUTONOMY, collection: this); /// A helper class to handle driving the rover. @override late DriveInterface drive = RoverDrive(collection: this); @override late GpsInterface gps = RoverGps(collection: this); @override late ImuInterface imu = RoverImu(collection: this); @override late final logger = BurtLogger(socket: server); - @override late final pathfinder = RoverPathfinder(collection: this); - @override late final detector = DetectorSimulator(collection: this, obstacles: []); - @override late final realsense = RoverRealsense(collection: this); - @override late final orchestrator = RoverOrchestrator(collection: this); + @override late PathfindingInterface pathfinder = RoverPathfinder(collection: this); + @override late DetectorInterface detector = RoverDetector(collection: this); + @override late VideoInterface video = RoverVideo(collection: this); + @override late OrchestratorInterface orchestrator = RoverOrchestrator(collection: this); + + @override + Future onDisconnect() async { + await super.onDisconnect(); + await orchestrator.abort(); + } } diff --git a/lib/simulator.dart b/lib/simulator.dart index 02ebaf6..908a8ae 100644 --- a/lib/simulator.dart +++ b/lib/simulator.dart @@ -3,11 +3,10 @@ export "src/simulator/drive.dart"; export "src/simulator/gps.dart"; export "src/simulator/imu.dart"; export "src/simulator/realsense.dart"; -export "src/simulator/server.dart"; import "package:autonomy/interfaces.dart"; import "package:autonomy/src/simulator/orchestrator.dart"; -import "package:burt_network/logging.dart"; +import "package:burt_network/burt_network.dart"; import "src/simulator/detector.dart"; import "src/simulator/drive.dart"; @@ -15,24 +14,23 @@ import "src/simulator/gps.dart"; import "src/simulator/imu.dart"; import "src/simulator/pathfinding.dart"; import "src/simulator/realsense.dart"; -import "src/simulator/server.dart"; class AutonomySimulator extends AutonomyInterface { @override late final logger = BurtLogger(socket: server); - @override late ServerInterface server = SimulatorServer(collection: this); + @override late final server = RoverSocket(port: 8003, device: Device.AUTONOMY, collection: this); @override late GpsInterface gps = GpsSimulator(collection: this); @override late ImuInterface imu = ImuSimulator(collection: this); @override late DriveInterface drive = DriveSimulator(collection: this); @override late PathfindingInterface pathfinder = PathfindingSimulator(collection: this); @override late DetectorInterface detector = DetectorSimulator(collection: this, obstacles: []); - @override late RealSenseInterface realsense = RealSenseSimulator(collection: this); + @override late VideoInterface video = VideoSimulator(collection: this); @override late OrchestratorInterface orchestrator = OrchestratorSimulator(collection: this); bool isInitialized = false; @override Future init() { isInitialized = true; return super.init(); } - + @override Future dispose() { isInitialized = false; return super.dispose(); } } diff --git a/lib/src/interfaces/autonomy.dart b/lib/src/interfaces/autonomy.dart index b704ef9..a1be9e0 100644 --- a/lib/src/interfaces/autonomy.dart +++ b/lib/src/interfaces/autonomy.dart @@ -1,4 +1,4 @@ -import "package:burt_network/logging.dart"; +import "package:burt_network/burt_network.dart"; import "package:autonomy/interfaces.dart"; @@ -7,12 +7,12 @@ abstract class AutonomyInterface extends Service with Receiver { GpsInterface get gps; ImuInterface get imu; DriveInterface get drive; - ServerInterface get server; + RoverSocket get server; PathfindingInterface get pathfinder; DetectorInterface get detector; - RealSenseInterface get realsense; + VideoInterface get video; OrchestratorInterface get orchestrator; - + @override Future init() async { var result = true; @@ -22,42 +22,45 @@ abstract class AutonomyInterface extends Service with Receiver { result &= await server.init(); result &= await pathfinder.init(); result &= await detector.init(); - result &= await realsense.init(); + result &= await video.init(); result &= await orchestrator.init(); if (result) { logger.info("Autonomy initialized"); } else { logger.warning("Autonomy could not initialize"); } +// await waitForValue(); return result; + } - + @override Future dispose() async { - await server.dispose(); await gps.dispose(); await imu.dispose(); await drive.dispose(); await pathfinder.dispose(); await detector.dispose(); - await realsense.dispose(); + await video.dispose(); await orchestrator.dispose(); + await server.dispose(); logger.info("Autonomy disposed"); } - + Future restart() async { await dispose(); await init(); } - + @override Future waitForValue() async { logger.info("Waiting for readings..."); await gps.waitForValue(); await imu.waitForValue(); - await realsense.waitForValue(); + await video.waitForValue(); + logger.info("Received GPS and IMU values"); } @override - bool get hasValue => gps.hasValue && imu.hasValue && realsense.hasValue; + bool get hasValue => gps.hasValue && imu.hasValue && video.hasValue; } diff --git a/lib/src/interfaces/detector.dart b/lib/src/interfaces/detector.dart index 98a7418..c42a694 100644 --- a/lib/src/interfaces/detector.dart +++ b/lib/src/interfaces/detector.dart @@ -1,6 +1,9 @@ -import "service.dart"; +import "package:autonomy/interfaces.dart"; abstract class DetectorInterface extends Service { + AutonomyInterface collection; + DetectorInterface({required this.collection}); + bool findObstacles(); bool canSeeAruco(); bool isOnSlope(); diff --git a/lib/src/interfaces/drive.dart b/lib/src/interfaces/drive.dart index b08471b..d7dd8b2 100644 --- a/lib/src/interfaces/drive.dart +++ b/lib/src/interfaces/drive.dart @@ -42,6 +42,7 @@ enum DriveOrientation { abstract class DriveInterface extends Service { AutonomyInterface collection; + DriveOrientation orientation = DriveOrientation.north; DriveInterface({required this.collection}); Future goDirection(DriveDirection direction) async => switch (direction) { @@ -58,9 +59,21 @@ abstract class DriveInterface extends Service { Future turnRight(); Future stop(); + Future faceDirection(DriveOrientation orientation) async { + this.orientation = orientation; + } + Future followPath(List path) async { for (final state in path) { await goDirection(state.direction); } } + + void setLedStrip(ProtoColor color, {bool blink = false}) { + final command = DriveCommand(color: color, blink: blink ? BoolState.YES : BoolState.NO); + collection.server.sendCommand(command); + } + + Future spinForAruco() async => false; + Future approachAruco() async { } } diff --git a/lib/src/interfaces/gps_utils.dart b/lib/src/interfaces/gps_utils.dart index 570d5c9..66ee5aa 100644 --- a/lib/src/interfaces/gps_utils.dart +++ b/lib/src/interfaces/gps_utils.dart @@ -49,7 +49,8 @@ extension GpsUtils on GpsCoordinates { longitude: longitude + other.longitude, ); - String prettyPrint() => "(lat=${(latitude * GpsUtils.metersPerLatitude).toStringAsFixed(2)}, long=${(longitude * GpsUtils.metersPerLongitude).toStringAsFixed(2)})"; +// String prettyPrint() => "(lat=${(latitude * GpsUtils.metersPerLatitude).toStringAsFixed(2)}, long=${(longitude * GpsUtils.metersPerLongitude).toStringAsFixed(2)})"; + String prettyPrint() => toProto3Json().toString(); GpsCoordinates goForward(DriveOrientation orientation) => this + switch(orientation) { DriveOrientation.north => GpsUtils.north, diff --git a/lib/src/interfaces/imu.dart b/lib/src/interfaces/imu.dart index f5af500..7454d2f 100644 --- a/lib/src/interfaces/imu.dart +++ b/lib/src/interfaces/imu.dart @@ -7,7 +7,10 @@ abstract class ImuInterface extends Service with Receiver { double get heading => raw.z; Orientation get raw; - DriveOrientation? get orientation => DriveOrientation.fromRaw(raw); + DriveOrientation? get orientation { + collection.logger.trace("Trying to find orientation at $heading"); + return DriveOrientation.fromRaw(raw); + } void update(Orientation newValue); bool isNear(double angle) => raw.isNear(angle); } diff --git a/lib/src/interfaces/imu_utils.dart b/lib/src/interfaces/imu_utils.dart index 1fbf3c5..96d928e 100644 --- a/lib/src/interfaces/imu_utils.dart +++ b/lib/src/interfaces/imu_utils.dart @@ -1,7 +1,7 @@ import "package:burt_network/generated.dart"; extension OrientationUtils on Orientation { - static const double epsilon = 15; + static const double epsilon = 10; static final north = Orientation(z: 0); static final west = Orientation(z: 90); diff --git a/lib/src/interfaces/orchestrator.dart b/lib/src/interfaces/orchestrator.dart index 59a75ea..13ee8ed 100644 --- a/lib/src/interfaces/orchestrator.dart +++ b/lib/src/interfaces/orchestrator.dart @@ -1,7 +1,7 @@ import "dart:io"; -import "package:burt_network/generated.dart"; import "package:autonomy/interfaces.dart"; +import "package:burt_network/burt_network.dart"; import "package:meta/meta.dart"; abstract class OrchestratorInterface extends Service { @@ -11,6 +11,13 @@ abstract class OrchestratorInterface extends Service { AutonomyCommand? currentCommand; AutonomyState currentState = AutonomyState.AUTONOMY_STATE_UNDEFINED; Future onCommand(AutonomyCommand command) async { + collection.server.sendMessage(command); + if (command.abort) return abort(); + if (currentCommand != null) { + collection.logger.error("Already executing a command", body: "Abort first if you want to switch tasks"); + return; + } + if (!collection.hasValue) { collection.logger.error("Sensors haven't gotten any readings yet!"); currentState = AutonomyState.NO_SOLUTION; @@ -21,11 +28,21 @@ abstract class OrchestratorInterface extends Service { switch (command.task) { case AutonomyTask.GPS_ONLY: await handleGpsTask(command); case AutonomyTask.VISUAL_MARKER: await handleArucoTask(command); - // TODO: Add more tasks + // TODO: Add more tasks default: collection.logger.error("Unrecognized task: ${command.task}"); // ignore: no_default_cases } } + @override + Future init() async { + collection.server.messages.onMessage( + name: AutonomyCommand().messageName, + constructor: AutonomyCommand.fromBuffer, + callback: onCommand, + ); + return true; + } + @mustCallSuper Future abort() async { currentCommand = null; @@ -35,7 +52,7 @@ abstract class OrchestratorInterface extends Service { await collection.dispose(); exit(1); } - + Future handleGpsTask(AutonomyCommand command); Future handleArucoTask(AutonomyCommand command); Future handleHammerTask(AutonomyCommand command); diff --git a/lib/src/interfaces/realsense.dart b/lib/src/interfaces/realsense.dart deleted file mode 100644 index f15e25a..0000000 --- a/lib/src/interfaces/realsense.dart +++ /dev/null @@ -1,10 +0,0 @@ -import "dart:typed_data"; - -import "package:autonomy/interfaces.dart"; - -abstract class RealSenseInterface extends Service with Receiver { - final AutonomyInterface collection; - RealSenseInterface({required this.collection}); - - void updateFrame(Uint16List newFrame); -} diff --git a/lib/src/interfaces/server.dart b/lib/src/interfaces/server.dart index 0d10183..cec6850 100644 --- a/lib/src/interfaces/server.dart +++ b/lib/src/interfaces/server.dart @@ -1,55 +1,25 @@ import "dart:io"; -import "package:autonomy/interfaces.dart"; import "package:burt_network/burt_network.dart"; -import "package:meta/meta.dart"; -abstract class ServerInterface extends RoverServer implements Service { +extension ServerUtils on RoverSocket { static SocketInfo subsystemsDestination = SocketInfo( address: InternetAddress("192.168.1.20"), port: 8001, ); - - final AutonomyInterface collection; - ServerInterface({required this.collection, super.quiet = false}) : super(device: Device.AUTONOMY, port: 8003); - void sendCommand(Message message) => sendMessage(message, destinationOverride: subsystemsDestination); - - @override - Future restart() => collection.restart(); - - @override - Future onShutdown() => collection.dispose(); + void sendCommand(Message message) => sendMessage(message, destination: subsystemsDestination); Future waitForConnection() async { - collection.logger.info("Waiting for connection..."); + logger.info("Waiting for connection..."); while (!isConnected) { await Future.delayed(const Duration(milliseconds: 100)); } return; } - @override - @mustCallSuper - void onMessage(WrappedMessage wrapper) { - if (wrapper.name == AutonomyCommand().messageName) { - sendWrapper(wrapper); // acknowledge receipt to the dashboard - final command = AutonomyCommand.fromBuffer(wrapper.data); - if (command.abort) { - collection.orchestrator.abort(); - return; - } - if (collection.orchestrator.currentCommand != null) { - collection.logger.error("Already executing a command", body: "Abort first if you want to switch tasks"); - return; - } - collection.orchestrator.onCommand(command); - } - } - - @override - Future onDisconnect() async { - await collection.orchestrator.abort(); - super.onDisconnect(); + void sendDone() { + final message = AutonomyData(state: AutonomyState.AT_DESTINATION, task: AutonomyTask.AUTONOMY_TASK_UNDEFINED); + sendMessage(message); } } diff --git a/lib/src/interfaces/video.dart b/lib/src/interfaces/video.dart new file mode 100644 index 0000000..e66dc3e --- /dev/null +++ b/lib/src/interfaces/video.dart @@ -0,0 +1,17 @@ +import "package:autonomy/interfaces.dart"; +import "package:burt_network/generated.dart"; + +/// Handles obstacle detection data and ArUco data from video +abstract class VideoInterface extends Service with Receiver { +bool flag = false; + + final AutonomyInterface collection; + VideoInterface({required this.collection}); + + VideoData data = VideoData(); + + void updateFrame(VideoData newData); + + double get arucoSize => data.arucoSize; + double get arucoPosition => data.arucoPosition; +} diff --git a/lib/src/rover/detector.dart b/lib/src/rover/detector.dart new file mode 100644 index 0000000..f28c508 --- /dev/null +++ b/lib/src/rover/detector.dart @@ -0,0 +1,21 @@ +import "package:autonomy/interfaces.dart"; + +class RoverDetector extends DetectorInterface { + RoverDetector({required super.collection}); + + @override + bool isOnSlope() => false; + + @override + bool findObstacles() => false; + + @override +// bool canSeeAruco() => collection.video.data.arucoDetected == BoolState.YES; + bool canSeeAruco() => collection.video.flag; + + @override + Future init() async => true; + + @override + Future dispose() async { } +} diff --git a/lib/src/rover/drive.dart b/lib/src/rover/drive.dart index 73739cd..7cef09e 100644 --- a/lib/src/rover/drive.dart +++ b/lib/src/rover/drive.dart @@ -4,25 +4,25 @@ import "package:autonomy/interfaces.dart"; import "drive/timed.dart"; import "drive/sensor.dart"; -/// A helper class to send drive commands to the rover with a simpler API. +/// A helper class to send drive commands to the rover with a simpler API. class RoverDrive extends DriveInterface { final bool useGps; final bool useImu; - + final SensorDrive sensorDrive; final TimedDrive timedDrive; - + // TODO: Calibrate these - RoverDrive({required super.collection, this.useGps = true, this.useImu = true}) : + RoverDrive({required super.collection, this.useGps = true, this.useImu = true}) : sensorDrive = SensorDrive(collection: collection), timedDrive = TimedDrive(collection: collection); /// Initializes the rover's drive subsystems. - @override + @override Future init() async => true; /// Stops the rover from driving. - @override + @override Future dispose() => stop(); /// Sets the angle of the front camera. @@ -32,7 +32,7 @@ class RoverDrive extends DriveInterface { collection.server.sendCommand(command); } - @override + @override Future stop() async { await timedDrive.stop(); } @@ -40,7 +40,23 @@ class RoverDrive extends DriveInterface { @override Future faceNorth() => useImu ? sensorDrive.faceNorth() : timedDrive.faceNorth(); - @override + @override + Future spinForAruco() => sensorDrive.spinForAruco(); + @override + Future approachAruco() => sensorDrive.approachAruco(); + + + @override + Future faceDirection(DriveOrientation orientation) async { + if (useImu) { + await sensorDrive.faceDirection(orientation); + } else { + await timedDrive.faceDirection(orientation); + } + await super.faceDirection(orientation); + } + + @override Future goForward() => useGps ? sensorDrive.goForward() : timedDrive.goForward(); @override diff --git a/lib/src/rover/drive/motors.dart b/lib/src/rover/drive/motors.dart index 8772ea7..98710c2 100644 --- a/lib/src/rover/drive/motors.dart +++ b/lib/src/rover/drive/motors.dart @@ -18,6 +18,7 @@ mixin RoverMotors { /// /// These values are percentages of the max speed allowed by the rover. See [setThrottle]. void setSpeeds({required double left, required double right}) { + right *= -1; collection.logger.trace("Setting speeds to $left and $right"); collection.server.sendCommand(DriveCommand(left: left, setLeft: true)); collection.server.sendCommand(DriveCommand(right: right, setRight: true)); diff --git a/lib/src/rover/drive/sensor.dart b/lib/src/rover/drive/sensor.dart index 67ebec6..2fadbb9 100644 --- a/lib/src/rover/drive/sensor.dart +++ b/lib/src/rover/drive/sensor.dart @@ -1,10 +1,14 @@ +import "package:autonomy/autonomy.dart"; import "package:autonomy/interfaces.dart"; -import "package:autonomy/src/rover/drive/motors.dart"; + +import "motors.dart"; class SensorDrive extends DriveInterface with RoverMotors { - static const double maxThrottle = 0.2; - static const predicateDelay = Duration(milliseconds: 10); - + static const double maxThrottle = 0.1; + static const double turnThrottle = 0.1; + static const predicateDelay = Duration(milliseconds: 100); + static const turnDelay = Duration(milliseconds: 1500); + SensorDrive({required super.collection}); @override @@ -15,7 +19,17 @@ class SensorDrive extends DriveInterface with RoverMotors { Future waitFor(bool Function() predicate) async { while (!predicate()) { +// collection.logger.debug("Next turning loop"); +// collection.imu.hasValue = false; +// setThrottle(maxThrottle); +// setSpeeds(left: -1, right: 1); await Future.delayed(predicateDelay); +// if (!collection.imu.hasValue) { +// collection.logger.trace("IMU has value: ${collection.imu.hasValue}"); +// await stop(); +// collection.logger.warning("Checked for IMU value but didn't find it"); +// } +// await collection.imu.waitForValue(); } } @@ -30,7 +44,7 @@ class SensorDrive extends DriveInterface with RoverMotors { final orientation = collection.imu.orientation; final currentCoordinates = collection.gps.coordinates; final destination = currentCoordinates.goForward(orientation!); - + setThrottle(maxThrottle); setSpeeds(left: 1, right: 1); await waitFor(() => collection.gps.coordinates.isNear(destination)); @@ -39,30 +53,101 @@ class SensorDrive extends DriveInterface with RoverMotors { @override Future faceNorth() async { - setThrottle(maxThrottle); + collection.logger.info("Turning to face north..."); + setThrottle(turnThrottle); + setSpeeds(left: -1, right: 1); + await waitFor(() { + collection.logger.trace("Current heading: ${collection.imu.heading}"); + return collection.imu.raw.isNear(0); + }); + await stop(); + } + + @override + Future faceDirection(DriveOrientation orientation) async { + collection.logger.info("Turning to face $orientation..."); + setThrottle(turnThrottle); setSpeeds(left: -1, right: 1); - await waitFor(() => collection.imu.raw.isNear(0)); + await waitFor(() { + collection.logger.trace("Current heading: ${collection.imu.heading}"); + return collection.imu.raw.isNear(orientation.angle.toDouble()); + }); await stop(); + await super.faceDirection(orientation); } @override Future turnLeft() async { - final orientation = collection.imu.orientation!; - final destination = orientation.turnLeft(); // do NOT clamp! + if (collection.imu.orientation == null) { + await faceNorth(); + await faceDirection(this.orientation); + } + final orientation = collection.imu.orientation; + final destination = orientation!.turnLeft(); // do NOT clamp! setThrottle(maxThrottle); setSpeeds(left: -1, right: 1); - await waitFor(() => collection.imu.orientation == destination); + await waitFor(() => collection.imu.orientation == destination); await stop(); + this.orientation = this.orientation.turnLeft(); + } @override Future turnRight() async { // TODO: Allow corrective turns + if (collection.imu.orientation == null) { + await faceNorth(); + await faceDirection(this.orientation); + } final orientation = collection.imu.orientation; final destination = orientation!.turnRight(); // do NOT clamp! setThrottle(maxThrottle); - setSpeeds(left: -1, right: 1); - await waitFor(() => collection.imu.orientation == destination); + setSpeeds(left: 1, right: -1); + await waitFor(() => collection.imu.orientation == destination); + await stop(); + this.orientation = this.orientation.turnRight(); + } + + @override + Future spinForAruco() async { + for (var i = 0; i < 16; i++) { + setThrottle(turnThrottle); + setSpeeds(left: -1, right: 1); + await Future.delayed(turnDelay); + await stop(); + + for (var j = 0; j < 300; j++) { + await Future.delayed(const Duration(milliseconds: 10)); + collection.logger.trace("Can see aruco? ${collection.detector.canSeeAruco()}"); + + if (collection.detector.canSeeAruco()) { + // Spin a bit more to center it + // print("We found it!"); + // setThrottle(0.1); + // setSpeeds(left: -1, right: 1); + // await waitFor(() { + // final pos = collection.video.arucoPosition; + // collection.logger.debug("aruco is at $pos"); + // return pos > 0.2; + // }); + // await stop(); + return true; + }} + } + return false; + } + + @override + Future approachAruco() async { + setThrottle(maxThrottle); + setSpeeds(left: 1, right: 1); + // const threshold = 0.2; + // await waitFor(() { + // final pos = collection.video.arucoSize; + // collection.logger.debug("It is at $pos percent"); + // return (pos.abs() < 0.00001 && !collection.detector.canSeeAruco()) || pos >= threshold; + // }); + await Future.delayed(const Duration(seconds: 10)); await stop(); } } diff --git a/lib/src/rover/drive/timed.dart b/lib/src/rover/drive/timed.dart index 768b4f4..babb113 100644 --- a/lib/src/rover/drive/timed.dart +++ b/lib/src/rover/drive/timed.dart @@ -3,10 +3,10 @@ import "package:autonomy/interfaces.dart"; import "motors.dart"; class TimedDrive extends DriveInterface with RoverMotors { - static const maxThrottle = 0.2; + static const maxThrottle = 0.1; static const turnThrottle = 0.1; - static const oneMeterDelay = Duration(seconds: 1); - static const turnDelay = Duration(seconds: 1); + static const oneMeterDelay = Duration(seconds: 6); + static const turnDelay = Duration(milliseconds: 5500); TimedDrive({required super.collection}); @@ -16,7 +16,7 @@ class TimedDrive extends DriveInterface with RoverMotors { @override Future dispose() async { } - @override + @override Future stop() async { setThrottle(0); setSpeeds(left: 0, right: 0); @@ -25,7 +25,7 @@ class TimedDrive extends DriveInterface with RoverMotors { @override Future faceNorth() async { /* Assume already facing north */ } - @override + @override Future goForward() async { setThrottle(maxThrottle); setSpeeds(left: 1, right: 1); diff --git a/lib/src/rover/gps.dart b/lib/src/rover/gps.dart index 3de8b0a..8e566f8 100644 --- a/lib/src/rover/gps.dart +++ b/lib/src/rover/gps.dart @@ -1,15 +1,24 @@ -import "package:burt_network/generated.dart"; +import "package:burt_network/burt_network.dart"; import "package:autonomy/interfaces.dart"; import "corrector.dart"; class RoverGps extends GpsInterface { - final _latitudeCorrector = ErrorCorrector(maxSamples: 5, maxDeviation: GpsInterface.gpsError * 10); - final _longitudeCorrector = ErrorCorrector(maxSamples: 5, maxDeviation: GpsInterface.gpsError * 10); + final _latitudeCorrector = ErrorCorrector(maxSamples: 1, maxDeviation: GpsInterface.gpsError * 10); + final _longitudeCorrector = ErrorCorrector(maxSamples: 1, maxDeviation: GpsInterface.gpsError * 10); RoverGps({required super.collection}); - + @override - Future init() async => true; + Future init() async { + collection.server.messages.onMessage( + name: RoverPosition().messageName, + constructor: RoverPosition.fromBuffer, + callback: (pos) { + if (pos.hasGps()) update(pos.gps); + }, + ); + return true; + } @override Future dispose() async { @@ -17,7 +26,7 @@ class RoverGps extends GpsInterface { _longitudeCorrector.clear(); } - @override + @override void update(GpsCoordinates newValue) { _latitudeCorrector.addValue(newValue.latitude); _longitudeCorrector.addValue(newValue.longitude); @@ -25,8 +34,8 @@ class RoverGps extends GpsInterface { } @override - GpsCoordinates get coordinates => GpsCoordinates( - latitude: _latitudeCorrector.calibratedValue, - longitude: _longitudeCorrector.calibratedValue, - ); + GpsCoordinates get coordinates => GpsCoordinates( + latitude: _latitudeCorrector.calibratedValue, + longitude: _longitudeCorrector.calibratedValue, + ); } diff --git a/lib/src/rover/imu.dart b/lib/src/rover/imu.dart index b4315ba..df63f74 100644 --- a/lib/src/rover/imu.dart +++ b/lib/src/rover/imu.dart @@ -1,4 +1,4 @@ -import "package:burt_network/generated.dart"; +import "package:burt_network/burt_network.dart"; import "package:autonomy/interfaces.dart"; import "corrector.dart"; @@ -7,8 +7,19 @@ class RoverImu extends ImuInterface { final _zCorrector = ErrorCorrector(maxSamples: 10, maxDeviation: 15); RoverImu({required super.collection}); + Orientation value = Orientation(); + @override - Future init() async => true; + Future init() async { + collection.server.messages.onMessage( + name: RoverPosition().messageName, + constructor: RoverPosition.fromBuffer, + callback: (pos) { + if (pos.hasOrientation()) update(pos.orientation); + }, + ); + return true; + } @override Future dispose() async { @@ -17,14 +28,17 @@ class RoverImu extends ImuInterface { @override void update(Orientation newValue) { - _zCorrector.addValue(newValue.heading); + // _zCorrector.addValue(newValue.heading); + // collection.logger.trace("Got IMU value"); + // print("Got imu"); hasValue = true; - } + value = newValue; + } @override Orientation get raw => Orientation( - x: 0, + x: 0, y: 0, - z: _zCorrector.calibratedValue, + z: value.z, ).clampHeading(); } diff --git a/lib/src/rover/orchestrator.dart b/lib/src/rover/orchestrator.dart index 8a7a8fd..16d6b12 100644 --- a/lib/src/rover/orchestrator.dart +++ b/lib/src/rover/orchestrator.dart @@ -1,5 +1,6 @@ import "package:autonomy/interfaces.dart"; import "package:burt_network/generated.dart"; +import "dart:async"; class RoverOrchestrator extends OrchestratorInterface with ValueReporter { final List traversed = []; @@ -14,7 +15,7 @@ class RoverOrchestrator extends OrchestratorInterface with ValueReporter { traversed.clear(); await super.dispose(); } - + @override AutonomyData get statusMessage => AutonomyData( destination: currentCommand?.destination, @@ -31,15 +32,18 @@ class RoverOrchestrator extends OrchestratorInterface with ValueReporter { @override Message getMessage() => statusMessage; - + @override Future handleGpsTask(AutonomyCommand command) async { final destination = command.destination; collection.logger.info("Got GPS Task: Go to ${destination.prettyPrint()}"); + collection.logger.debug("Currently at ${collection.gps.coordinates.prettyPrint()}"); + collection.drive.setLedStrip(ProtoColor.RED); while (!collection.gps.coordinates.isNear(destination)) { // Calculate a path collection.logger.debug("Finding a path"); currentState = AutonomyState.PATHING; + await collection.drive.faceNorth(); final path = collection.pathfinder.getPath(destination); currentPath = path; // also use local variable path for promotion if (path == null) { @@ -51,27 +55,50 @@ class RoverOrchestrator extends OrchestratorInterface with ValueReporter { } // Try to take that path final current = collection.gps.coordinates; - collection.logger.trace("Found a path from ${current.prettyPrint()} to ${destination.prettyPrint()}: ${path.length} steps"); + collection.logger.info("Found a path from ${current.prettyPrint()} to ${destination.prettyPrint()}: ${path.length} steps"); + collection.logger.debug("Here is a summary of the path"); + for (final step in path) { + collection.logger.debug(step.toString()); + } currentState = AutonomyState.DRIVING; + var count = 0; for (final state in path) { + collection.logger.debug(state.toString()); await collection.drive.goDirection(state.direction); traversed.add(state.position); if (state.direction != DriveDirection.forward) continue; + if (count++ == 5) break; final foundObstacle = collection.detector.findObstacles(); if (foundObstacle) { - collection.logger.debug("Found an obstacle. Recalculating path..."); + collection.logger.debug("Found an obstacle. Recalculating path..."); break; // calculate a new path } } } collection.logger.info("Task complete"); + collection.drive.setLedStrip(ProtoColor.GREEN, blink: true); currentState = AutonomyState.AT_DESTINATION; currentCommand = null; } @override Future handleArucoTask(AutonomyCommand command) async { +collection.drive.setLedStrip(ProtoColor.RED); + // Go to GPS coordinates + // await handleGpsTask(command); + collection.logger.info("Got ArUco Task"); + + currentState = AutonomyState.SEARCHING; + collection.logger.info("Searching for ArUco tag"); +final didSeeAruco = await collection.drive.spinForAruco(); + if (didSeeAruco) { + collection.logger.info("Found aruco"); + currentState = AutonomyState.APPROACHING; + await collection.drive.approachAruco(); + collection.drive.setLedStrip(ProtoColor.GREEN, blink: true); + currentState = AutonomyState.AT_DESTINATION; + } } @override diff --git a/lib/src/rover/realsense.dart b/lib/src/rover/realsense.dart deleted file mode 100644 index caa8ed6..0000000 --- a/lib/src/rover/realsense.dart +++ /dev/null @@ -1,19 +0,0 @@ -import "dart:typed_data"; - -import "package:autonomy/interfaces.dart"; - -class RoverRealsense extends RealSenseInterface { - RoverRealsense({required super.collection}); - - @override - Future init() async => true; - - @override - Future dispose() async { } - - @override - void updateFrame(Uint16List newFrame) { - hasValue = true; - // TODO: Parse depth data - } -} diff --git a/lib/src/rover/sensorless.dart b/lib/src/rover/sensorless.dart index 6123cf5..2beab09 100644 --- a/lib/src/rover/sensorless.dart +++ b/lib/src/rover/sensorless.dart @@ -42,6 +42,14 @@ class SensorlessDrive extends DriveInterface { await realDrive.faceNorth(); } + + @override + Future faceDirection(DriveOrientation orientation) async { + await simulatedDrive.faceDirection(orientation); + await realDrive.faceDirection(orientation); + await super.faceDirection(orientation); + } + @override Future turnLeft() async { await simulatedDrive.turnLeft(); diff --git a/lib/src/rover/server.dart b/lib/src/rover/server.dart deleted file mode 100644 index 3ffac16..0000000 --- a/lib/src/rover/server.dart +++ /dev/null @@ -1,32 +0,0 @@ -import "dart:typed_data"; - -import "package:burt_network/burt_network.dart"; -import "package:autonomy/interfaces.dart"; - -/// A server to handle incoming [AutonomyCommand]s and send [AutonomyData]s. -class AutonomyServer extends ServerInterface { - /// Creates an autonomy server at the given port. - AutonomyServer({required super.collection}); - - @override - void onMessage(WrappedMessage wrapper) { - if (wrapper.name == RoverPosition().messageName) { - final message = RoverPosition.fromBuffer(wrapper.data); - if (message.hasGps()) collection.gps.update(message.gps); - if (message.hasOrientation()) collection.imu.update(message.orientation); - } else if (wrapper.name == VideoData().messageName) { - final message = VideoData.fromBuffer(wrapper.data); - if (!message.hasFrame()) return; - final buffer = Uint16List.fromList(message.frame); - collection.realsense.updateFrame(buffer); - } - super.onMessage(wrapper); - } - - @override - Future init() async { - await super.init(); - collection.logger.info("Initialized server"); - return true; - } -} diff --git a/lib/src/rover/video.dart b/lib/src/rover/video.dart new file mode 100644 index 0000000..db8bec0 --- /dev/null +++ b/lib/src/rover/video.dart @@ -0,0 +1,32 @@ +import "dart:async"; + +import "package:autonomy/interfaces.dart"; +import "package:burt_network/burt_network.dart"; + +class RoverVideo extends VideoInterface { + RoverVideo({required super.collection}); + + @override + Future init() async { + collection.server.messages.onMessage( + name: VideoData().messageName, + constructor: VideoData.fromBuffer, + callback: updateFrame, + ); + return true; + } + + @override + Future dispose() async { } + + @override + void updateFrame(VideoData newData) { + data = newData; + if (data.arucoDetected == BoolState.YES) { + flag = true; + Timer(const Duration(seconds: 3), () => flag = false); + } + collection.logger.info("Is ArUco detected: ${data.arucoDetected}"); + hasValue = true; + } +} diff --git a/lib/src/simulator/detector.dart b/lib/src/simulator/detector.dart index 9fe0852..d1103f7 100644 --- a/lib/src/simulator/detector.dart +++ b/lib/src/simulator/detector.dart @@ -16,8 +16,7 @@ class DetectorSimulator extends DetectorInterface { final List obstacles; - final AutonomyInterface collection; - DetectorSimulator({required this.collection, required this.obstacles}); + DetectorSimulator({required super.collection, required this.obstacles}); @override Future init() async => true; diff --git a/lib/src/simulator/orchestrator.dart b/lib/src/simulator/orchestrator.dart index 4179002..6df6e49 100644 --- a/lib/src/simulator/orchestrator.dart +++ b/lib/src/simulator/orchestrator.dart @@ -4,9 +4,6 @@ import "package:burt_network/generated.dart"; class OrchestratorSimulator extends OrchestratorInterface { OrchestratorSimulator({required super.collection}); - @override - Future init() async => true; - @override Future dispose() async { } diff --git a/lib/src/simulator/realsense.dart b/lib/src/simulator/realsense.dart index 1d2549d..0e20191 100644 --- a/lib/src/simulator/realsense.dart +++ b/lib/src/simulator/realsense.dart @@ -1,9 +1,10 @@ import "dart:typed_data"; import "package:autonomy/interfaces.dart"; +import "package:burt_network/generated.dart"; -class RealSenseSimulator extends RealSenseInterface { - RealSenseSimulator({required super.collection}); +class VideoSimulator extends VideoInterface { + VideoSimulator({required super.collection}); @override Future init() async { @@ -17,5 +18,5 @@ class RealSenseSimulator extends RealSenseInterface { Uint16List depthFrame = Uint16List.fromList([]); @override - void updateFrame(Uint16List newFrame) => depthFrame = newFrame; + void updateFrame(VideoData newData) { } } diff --git a/lib/src/simulator/server.dart b/lib/src/simulator/server.dart deleted file mode 100644 index 89d5455..0000000 --- a/lib/src/simulator/server.dart +++ /dev/null @@ -1,21 +0,0 @@ -import "package:burt_network/burt_network.dart"; -import "package:autonomy/interfaces.dart"; - -final driveName = DriveCommand().messageName; - -class SimulatorServer extends ServerInterface { - SimulatorServer({ - required super.collection, - }) : super(quiet: true); - - @override - Future init() async { - await super.init(); - return true; - } - - void sendDone() { - final message = AutonomyData(state: AutonomyState.AT_DESTINATION, task: AutonomyTask.AUTONOMY_TASK_UNDEFINED); - sendMessage(message); - } -} diff --git a/pubspec.lock b/pubspec.lock new file mode 100644 index 0000000..bab4467 --- /dev/null +++ b/pubspec.lock @@ -0,0 +1,482 @@ +# Generated by pub +# See https://dart.dev/tools/pub/glossary#lockfile +packages: + _fe_analyzer_shared: + dependency: transitive + description: + name: _fe_analyzer_shared + sha256: "45cfa8471b89fb6643fe9bf51bd7931a76b8f5ec2d65de4fb176dba8d4f22c77" + url: "https://pub.dev" + source: hosted + version: "73.0.0" + _macros: + dependency: transitive + description: dart + source: sdk + version: "0.3.2" + a_star: + dependency: "direct main" + description: + name: a_star + sha256: e76e163888a193c0f8fe79d05396839fa663f1afa6aed8e58609c0b4799767a0 + url: "https://pub.dev" + source: hosted + version: "3.0.0" + analyzer: + dependency: transitive + description: + name: analyzer + sha256: "4959fec185fe70cce007c57e9ab6983101dbe593d2bf8bbfb4453aaec0cf470a" + url: "https://pub.dev" + source: hosted + version: "6.8.0" + args: + dependency: transitive + description: + name: args + sha256: "7cf60b9f0cc88203c5a190b4cd62a99feea42759a7fa695010eb5de1c0b2252a" + url: "https://pub.dev" + source: hosted + version: "2.5.0" + async: + dependency: transitive + description: + name: async + sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" + url: "https://pub.dev" + source: hosted + version: "2.11.0" + boolean_selector: + dependency: transitive + description: + name: boolean_selector + sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" + url: "https://pub.dev" + source: hosted + version: "2.1.1" + burt_network: + dependency: "direct main" + description: + path: "../../burt_network" + relative: true + source: path + version: "2.0.0" + collection: + dependency: transitive + description: + name: collection + sha256: a1ace0a119f20aabc852d165077c036cd864315bd99b7eaa10a60100341941bf + url: "https://pub.dev" + source: hosted + version: "1.19.0" + convert: + dependency: transitive + description: + name: convert + sha256: "0f08b14755d163f6e2134cb58222dd25ea2a2ee8a195e53983d57c075324d592" + url: "https://pub.dev" + source: hosted + version: "3.1.1" + coverage: + dependency: transitive + description: + name: coverage + sha256: c1fb2dce3c0085f39dc72668e85f8e0210ec7de05345821ff58530567df345a5 + url: "https://pub.dev" + source: hosted + version: "1.9.2" + crypto: + dependency: transitive + description: + name: crypto + sha256: ec30d999af904f33454ba22ed9a86162b35e52b44ac4807d1d93c288041d7d27 + url: "https://pub.dev" + source: hosted + version: "3.0.5" + dylib: + dependency: transitive + description: + name: dylib + sha256: bf609b3eb6492a3309b3d1dbe8f83a4031de5535dd7686be33487051cc760bb0 + url: "https://pub.dev" + source: hosted + version: "0.3.3" + ffi: + dependency: transitive + description: + name: ffi + sha256: "16ed7b077ef01ad6170a3d0c57caa4a112a38d7a2ed5602e0aca9ca6f3d98da6" + url: "https://pub.dev" + source: hosted + version: "2.1.3" + file: + dependency: transitive + description: + name: file + sha256: "5fc22d7c25582e38ad9a8515372cd9a93834027aacf1801cf01164dac0ffa08c" + url: "https://pub.dev" + source: hosted + version: "7.0.0" + fixnum: + dependency: transitive + description: + name: fixnum + sha256: "25517a4deb0c03aa0f32fd12db525856438902d9c16536311e76cdc57b31d7d1" + url: "https://pub.dev" + source: hosted + version: "1.1.0" + frontend_server_client: + dependency: transitive + description: + name: frontend_server_client + sha256: f64a0333a82f30b0cca061bc3d143813a486dc086b574bfb233b7c1372427694 + url: "https://pub.dev" + source: hosted + version: "4.0.0" + glob: + dependency: transitive + description: + name: glob + sha256: "0e7014b3b7d4dac1ca4d6114f82bf1782ee86745b9b42a92c9289c23d8a0ab63" + url: "https://pub.dev" + source: hosted + version: "2.1.2" + http_multi_server: + dependency: transitive + description: + name: http_multi_server + sha256: "97486f20f9c2f7be8f514851703d0119c3596d14ea63227af6f7a481ef2b2f8b" + url: "https://pub.dev" + source: hosted + version: "3.2.1" + http_parser: + dependency: transitive + description: + name: http_parser + sha256: "40f592dd352890c3b60fec1b68e786cefb9603e05ff303dbc4dda49b304ecdf4" + url: "https://pub.dev" + source: hosted + version: "4.1.0" + io: + dependency: transitive + description: + name: io + sha256: "2ec25704aba361659e10e3e5f5d672068d332fc8ac516421d483a11e5cbd061e" + url: "https://pub.dev" + source: hosted + version: "1.0.4" + js: + dependency: transitive + description: + name: js + sha256: c1b2e9b5ea78c45e1a0788d29606ba27dc5f71f019f32ca5140f61ef071838cf + url: "https://pub.dev" + source: hosted + version: "0.7.1" + libserialport: + dependency: transitive + description: + name: libserialport + sha256: "392e1592def65282429832ec66fa25e9e163d3b37716b97691482e2406720727" + url: "https://pub.dev" + source: hosted + version: "0.3.0+1" + logger: + dependency: transitive + description: + name: logger + sha256: "697d067c60c20999686a0add96cf6aba723b3aa1f83ecf806a8097231529ec32" + url: "https://pub.dev" + source: hosted + version: "2.4.0" + logging: + dependency: transitive + description: + name: logging + sha256: "623a88c9594aa774443aa3eb2d41807a48486b5613e67599fb4c41c0ad47c340" + url: "https://pub.dev" + source: hosted + version: "1.2.0" + macros: + dependency: transitive + description: + name: macros + sha256: "0acaed5d6b7eab89f63350bccd82119e6c602df0f391260d0e32b5e23db79536" + url: "https://pub.dev" + source: hosted + version: "0.1.2-main.4" + matcher: + dependency: transitive + description: + name: matcher + sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb + url: "https://pub.dev" + source: hosted + version: "0.12.16+1" + meta: + dependency: "direct main" + description: + name: meta + sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7 + url: "https://pub.dev" + source: hosted + version: "1.15.0" + mime: + dependency: transitive + description: + name: mime + sha256: "801fd0b26f14a4a58ccb09d5892c3fbdeff209594300a542492cf13fba9d247a" + url: "https://pub.dev" + source: hosted + version: "1.0.6" + node_preamble: + dependency: transitive + description: + name: node_preamble + sha256: "6e7eac89047ab8a8d26cf16127b5ed26de65209847630400f9aefd7cd5c730db" + url: "https://pub.dev" + source: hosted + version: "2.0.2" + opencv_ffi: + dependency: "direct main" + description: + path: "." + ref: HEAD + resolved-ref: fb721ce518faa62b470c73ae58edfe825a5f9052 + url: "https://github.com/BinghamtonRover/OpenCV-FFI.git" + source: git + version: "1.2.0" + package_config: + dependency: transitive + description: + name: package_config + sha256: "1c5b77ccc91e4823a5af61ee74e6b972db1ef98c2ff5a18d3161c982a55448bd" + url: "https://pub.dev" + source: hosted + version: "2.1.0" + path: + dependency: transitive + description: + name: path + sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" + url: "https://pub.dev" + source: hosted + version: "1.9.0" + platform: + dependency: transitive + description: + name: platform + sha256: "9b71283fc13df574056616011fb138fd3b793ea47cc509c189a6c3fa5f8a1a65" + url: "https://pub.dev" + source: hosted + version: "3.1.5" + pool: + dependency: transitive + description: + name: pool + sha256: "20fe868b6314b322ea036ba325e6fc0711a22948856475e2c2b6306e8ab39c2a" + url: "https://pub.dev" + source: hosted + version: "1.5.1" + protobuf: + dependency: transitive + description: + name: protobuf + sha256: "68645b24e0716782e58948f8467fd42a880f255096a821f9e7d0ec625b00c84d" + url: "https://pub.dev" + source: hosted + version: "3.1.0" + pub_semver: + dependency: transitive + description: + name: pub_semver + sha256: "40d3ab1bbd474c4c2328c91e3a7df8c6dd629b79ece4c4bd04bee496a224fb0c" + url: "https://pub.dev" + source: hosted + version: "2.1.4" + shelf: + dependency: transitive + description: + name: shelf + sha256: e7dd780a7ffb623c57850b33f43309312fc863fb6aa3d276a754bb299839ef12 + url: "https://pub.dev" + source: hosted + version: "1.4.2" + shelf_packages_handler: + dependency: transitive + description: + name: shelf_packages_handler + sha256: "89f967eca29607c933ba9571d838be31d67f53f6e4ee15147d5dc2934fee1b1e" + url: "https://pub.dev" + source: hosted + version: "3.0.2" + shelf_static: + dependency: transitive + description: + name: shelf_static + sha256: c87c3875f91262785dade62d135760c2c69cb217ac759485334c5857ad89f6e3 + url: "https://pub.dev" + source: hosted + version: "1.1.3" + shelf_web_socket: + dependency: transitive + description: + name: shelf_web_socket + sha256: "073c147238594ecd0d193f3456a5fe91c4b0abbcc68bf5cd95b36c4e194ac611" + url: "https://pub.dev" + source: hosted + version: "2.0.0" + source_map_stack_trace: + dependency: transitive + description: + name: source_map_stack_trace + sha256: c0713a43e323c3302c2abe2a1cc89aa057a387101ebd280371d6a6c9fa68516b + url: "https://pub.dev" + source: hosted + version: "2.1.2" + source_maps: + dependency: transitive + description: + name: source_maps + sha256: "708b3f6b97248e5781f493b765c3337db11c5d2c81c3094f10904bfa8004c703" + url: "https://pub.dev" + source: hosted + version: "0.10.12" + source_span: + dependency: transitive + description: + name: source_span + sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" + url: "https://pub.dev" + source: hosted + version: "1.10.0" + stack_trace: + dependency: transitive + description: + name: stack_trace + sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" + url: "https://pub.dev" + source: hosted + version: "1.11.1" + stream_channel: + dependency: transitive + description: + name: stream_channel + sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 + url: "https://pub.dev" + source: hosted + version: "2.1.2" + string_scanner: + dependency: transitive + description: + name: string_scanner + sha256: "688af5ed3402a4bde5b3a6c15fd768dbf2621a614950b17f04626c431ab3c4c3" + url: "https://pub.dev" + source: hosted + version: "1.3.0" + term_glyph: + dependency: transitive + description: + name: term_glyph + sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 + url: "https://pub.dev" + source: hosted + version: "1.2.1" + test: + dependency: "direct dev" + description: + name: test + sha256: "713a8789d62f3233c46b4a90b174737b2c04cb6ae4500f2aa8b1be8f03f5e67f" + url: "https://pub.dev" + source: hosted + version: "1.25.8" + test_api: + dependency: transitive + description: + name: test_api + sha256: "664d3a9a64782fcdeb83ce9c6b39e78fd2971d4e37827b9b06c3aa1edc5e760c" + url: "https://pub.dev" + source: hosted + version: "0.7.3" + test_core: + dependency: transitive + description: + name: test_core + sha256: "12391302411737c176b0b5d6491f466b0dd56d4763e347b6714efbaa74d7953d" + url: "https://pub.dev" + source: hosted + version: "0.6.5" + typed_data: + dependency: transitive + description: + name: typed_data + sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c + url: "https://pub.dev" + source: hosted + version: "1.3.2" + very_good_analysis: + dependency: "direct dev" + description: + name: very_good_analysis + sha256: "1fb637c0022034b1f19ea2acb42a3603cbd8314a470646a59a2fb01f5f3a8629" + url: "https://pub.dev" + source: hosted + version: "6.0.0" + vm_service: + dependency: transitive + description: + name: vm_service + sha256: "5c5f338a667b4c644744b661f309fb8080bb94b18a7e91ef1dbd343bed00ed6d" + url: "https://pub.dev" + source: hosted + version: "14.2.5" + watcher: + dependency: transitive + description: + name: watcher + sha256: "3d2ad6751b3c16cf07c7fca317a1413b3f26530319181b37e3b9039b84fc01d8" + url: "https://pub.dev" + source: hosted + version: "1.1.0" + web: + dependency: transitive + description: + name: web + sha256: d43c1d6b787bf0afad444700ae7f4db8827f701bc61c255ac8d328c6f4d52062 + url: "https://pub.dev" + source: hosted + version: "1.0.0" + web_socket: + dependency: transitive + description: + name: web_socket + sha256: "3c12d96c0c9a4eec095246debcea7b86c0324f22df69893d538fcc6f1b8cce83" + url: "https://pub.dev" + source: hosted + version: "0.1.6" + web_socket_channel: + dependency: transitive + description: + name: web_socket_channel + sha256: "9f187088ed104edd8662ca07af4b124465893caf063ba29758f97af57e61da8f" + url: "https://pub.dev" + source: hosted + version: "3.0.1" + webkit_inspection_protocol: + dependency: transitive + description: + name: webkit_inspection_protocol + sha256: "87d3f2333bb240704cd3f1c6b5b7acd8a10e7f0bc28c28dcf14e782014f4a572" + url: "https://pub.dev" + source: hosted + version: "1.2.1" + yaml: + dependency: transitive + description: + name: yaml + sha256: "75769501ea3489fca56601ff33454fe45507ea3bfb014161abc3b43ae25989d5" + url: "https://pub.dev" + source: hosted + version: "3.1.2" +sdks: + dart: ">=3.5.0 <4.0.0" diff --git a/pubspec.yaml b/pubspec.yaml index 0b4ab1d..c117b53 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -4,17 +4,17 @@ version: 1.0.0 publish_to: none environment: - sdk: 3.2.2 + sdk: ^3.5.0 -# Add regular dependencies here. +# Try not to edit this section by hand. Use `dart pub add my_package` dependencies: - opencv_ffi: + opencv_ffi: git: https://github.com/BinghamtonRover/OpenCV-FFI.git - burt_network: + burt_network: git: https://github.com/BinghamtonRover/Dart-Networking.git a_star: ^3.0.0 meta: ^1.11.0 dev_dependencies: test: ^1.21.0 - very_good_analysis: ^5.0.0+1 + very_good_analysis: ^6.0.0 diff --git a/test/network_test.dart b/test/network_test.dart index 64ac6ec..8e482df 100644 --- a/test/network_test.dart +++ b/test/network_test.dart @@ -4,53 +4,49 @@ import "package:autonomy/autonomy.dart"; import "package:burt_network/burt_network.dart"; import "package:test/test.dart"; -class MockSubsystems extends RoverServer { +class MockSubsystems extends Service { + final socket = RoverSocket( + device: Device.AUTONOMY, + port: 8001, + destination: SocketInfo(address: InternetAddress.loopbackIPv4, port: 8003), + quiet: true, + ); + double throttle = 0; double left = 0; double right = 0; - + bool throttleFlag = false; bool enabled = false; - bool throttleFlag = false; - - MockSubsystems({required super.port}) : - super(device: Device.SUBSYSTEMS, quiet: true); - @override - Future init() async { - destination = SocketInfo(address: InternetAddress.loopbackIPv4, port: 8003); - await super.init(); + Future init() async { + await socket.init(); + socket.messages.onMessage( + name: DriveCommand().messageName, + constructor: DriveCommand.fromBuffer, + callback: onDriveCommand, + ); + return true; } - - @override - void onMessage(WrappedMessage wrapper) { + + void onDriveCommand(DriveCommand command) { if (!enabled) return; - if (wrapper.name != DriveCommand().messageName) return; - final command = DriveCommand.fromBuffer(wrapper.data); if (command.setLeft) left = command.left; if (command.setRight) right = command.right; if (command.setThrottle) throttle = command.throttle; if (throttle > 0) throttleFlag = true; } - @override - Future onShutdown() async { } - - @override - Future restart() async { } - @override Future dispose() async { - left = 0; - right = 0; - throttle = 0; - throttleFlag = false; - await super.dispose(); + left = 0; right = 0; + throttle = 0; throttleFlag = false; + await socket.dispose(); } } void main() => group("[Network]", tags: ["network"], () { - final subsystems = MockSubsystems(port: 8001); + final subsystems = MockSubsystems(); final rover = RoverAutonomy(); rover.drive = SensorlessDrive(collection: rover, useGps: false, useImu: false); @@ -74,33 +70,33 @@ void main() => group("[Network]", tags: ["network"], () { expect(rover.hasValue, isFalse); expect(rover.gps.hasValue, isFalse); expect(rover.imu.hasValue, isFalse); - expect(rover.realsense.hasValue, isFalse); + expect(rover.video.hasValue, isFalse); - subsystems.sendMessage(posGps); + subsystems.socket.sendMessage(posGps); await Future.delayed(networkDelay); expect(rover.hasValue, isFalse); expect(rover.gps.hasValue, isTrue); expect(rover.imu.hasValue, isFalse); - expect(rover.realsense.hasValue, isFalse); + expect(rover.video.hasValue, isFalse); - subsystems.sendMessage(posImu); + subsystems.socket.sendMessage(posImu); await Future.delayed(networkDelay); expect(rover.hasValue, isFalse); expect(rover.gps.hasValue, isTrue); expect(rover.imu.hasValue, isTrue); - expect(rover.realsense.hasValue, isFalse); + expect(rover.video.hasValue, isFalse); - subsystems.sendMessage(depth); + subsystems.socket.sendMessage(depth); await Future.delayed(networkDelay); expect(rover.gps.hasValue, isTrue); expect(rover.imu.hasValue, isTrue); - expect(rover.realsense.hasValue, isTrue); + expect(rover.video.hasValue, isTrue); expect(rover.hasValue, isTrue); }); test("Rover can drive", () async { subsystems.enabled = true; - ServerInterface.subsystemsDestination = SocketInfo( + ServerUtils.subsystemsDestination = SocketInfo( address: InternetAddress.loopbackIPv4, port: 8001, ); @@ -112,7 +108,7 @@ void main() => group("[Network]", tags: ["network"], () { await simulator.drive.faceNorth(); expect(simulator.imu.orientation, DriveOrientation.north); - + final origin = GpsCoordinates(latitude: 0, longitude: 0); final oneMeter = (1, 0).toGps(); expect(subsystems.throttle, 0); diff --git a/test/orchestrator_test.dart b/test/orchestrator_test.dart index fa32919..27549f5 100644 --- a/test/orchestrator_test.dart +++ b/test/orchestrator_test.dart @@ -1,3 +1,4 @@ +import "package:autonomy/src/rover/gps.dart"; import "package:test/test.dart"; import "package:autonomy/autonomy.dart"; import "package:burt_network/burt_network.dart"; @@ -9,10 +10,11 @@ void main() => group("[Orchestrator]", tags: ["orchestrator"], () { test("Fails for invalid destinations", () async { Logger.level = LogLevel.off; // this test can log critical messages final simulator = AutonomySimulator(); + await simulator.init(); simulator.pathfinder = RoverPathfinder(collection: simulator); simulator.orchestrator = RoverOrchestrator(collection: simulator); simulator.pathfinder.recordObstacle((2, 0).toGps()); - // Test blocked command: + // Test blocked command: final command = AutonomyCommand(destination: (2, 0).toGps(), task: AutonomyTask.GPS_ONLY); expect(simulator.gps.latitude, 0); expect(simulator.gps.longitude, 0); @@ -40,7 +42,7 @@ void main() => group("[Orchestrator]", tags: ["orchestrator"], () { (4, 1).toGps(), ]); await simulator.init(); - // Test normal command: + // Test normal command: final destination = (4, 0).toGps(); final command = AutonomyCommand(destination: destination, task: AutonomyTask.GPS_ONLY); expect(simulator.gps.latitude, 0); @@ -92,9 +94,13 @@ void main() => group("[Orchestrator]", tags: ["orchestrator"], () { final command = AutonomyCommand(destination: destination, task: AutonomyTask.GPS_ONLY); simulator.orchestrator = RoverOrchestrator(collection: simulator); simulator.pathfinder = RoverPathfinder(collection: simulator); - + simulator.gps = RoverGps(collection: simulator); + await simulator.init(); + + expect(simulator.gps.hasValue, isFalse); + await simulator.orchestrator.onCommand(command); - expect(simulator.hasValue, isFalse); + expect(simulator.gps.hasValue, isFalse); expect(GpsInterface.currentLatitude, 0); expect(simulator.orchestrator.statusMessage.state, AutonomyState.NO_SOLUTION); diff --git a/test/rover_test.dart b/test/rover_test.dart index 97c9323..2f40af5 100644 --- a/test/rover_test.dart +++ b/test/rover_test.dart @@ -1,5 +1,3 @@ -import "dart:typed_data"; - import "package:test/test.dart"; import "package:burt_network/generated.dart"; @@ -33,7 +31,7 @@ void main() => group("[Rover]", tags: ["rover"], () { final rover = RoverAutonomy(); final position = (5, 5).toGps(); final orientation = Orientation(); - final frame = Uint16List.fromList([]); + final data = VideoData(); await rover.init(); @@ -50,9 +48,9 @@ void main() => group("[Rover]", tags: ["rover"], () { expect(rover.hasValue, isFalse); expect(rover.hasValue, isFalse); - expect(rover.realsense.hasValue, isFalse); - rover.realsense.updateFrame(frame); - expect(rover.realsense.hasValue, isTrue); + expect(rover.video.hasValue, isFalse); + rover.video.updateFrame(data); + expect(rover.video.hasValue, isTrue); expect(rover.hasValue, isTrue); await rover.dispose();