From eaf71ab26c88da2f51212a3c707095c188f0efee Mon Sep 17 00:00:00 2001 From: Levi Lesches Date: Wed, 27 Nov 2024 23:35:54 -0500 Subject: [PATCH 1/2] Overhauled serial script --- bin/data.dart | 8 +- bin/serial.dart | 144 +++++++++++++++++++++++----------- lib/src/devices/firmware.dart | 3 +- pubspec.yaml | 1 + 4 files changed, 106 insertions(+), 50 deletions(-) diff --git a/bin/data.dart b/bin/data.dart index ca76a20..479c262 100644 --- a/bin/data.dart +++ b/bin/data.dart @@ -65,13 +65,13 @@ Future main() async { ); server.sendMessage(data); final data2 = ArmData( - base: MotorData(angle: motor2.current), - shoulder: MotorData(angle: motor.current), - elbow: MotorData(angle: motor.current), + base: MotorData(currentAngle: motor2.current), + shoulder: MotorData(currentAngle: motor.current), + elbow: MotorData(currentAngle: motor.current), version: Version(major: 1), ); server.sendMessage(data2); - final data3 = GripperData(lift: MotorData(angle: pi + -1 * 2 * motor.current), version: Version(major: 1)); + final data3 = GripperData(lift: MotorData(currentAngle: pi + -1 * 2 * motor.current), version: Version(major: 1)); server.sendMessage(data3); final data4 = RoverPosition( orientation: Orientation( diff --git a/bin/serial.dart b/bin/serial.dart index ae9d7eb..903d278 100644 --- a/bin/serial.dart +++ b/bin/serial.dart @@ -1,33 +1,71 @@ import "dart:io"; import "dart:typed_data"; +import "package:args/args.dart"; +import "package:collection/collection.dart"; + import "package:burt_network/burt_network.dart"; -import "package:libserialport/libserialport.dart"; + +typedef ProtoConstructor = Message Function(List data); + +const constructors = { + Device.DRIVE: DriveData.fromBuffer, + Device.ARM: ArmData.fromBuffer, + Device.GRIPPER: GripperData.fromBuffer, + Device.SCIENCE: GripperData.fromBuffer, + Device.ANTENNA: AntennaFirmwareData.fromBuffer, +}; + +final deviceNames = { + for (final device in constructors.keys) + device.name.toLowerCase(): device, +}; final logger = BurtLogger(); -bool ascii = false; +late bool ascii; +late bool rawMode; +ProtoConstructor? constructor; +String? messageName; + +void handlePacket(Uint8List buffer) { + try { + if (rawMode) { + logger.debug("Got packet: $buffer"); + } else if (constructor == null) { + final string = String.fromCharCodes(buffer).trim(); + logger.debug("Got string: $string"); + } else { + final message = constructor!(buffer); + logger.debug("Got $messageName message: ${message.toProto3Json()}"); + } + } catch (error) { + logger.error("Could not decode packet: $error\n Buffer: $buffer"); + } +} -Future listenToDevice(String port) async { +Future listenToDevice(String port, int baudRate) async { logger.info("Connecting to $port..."); final device = SerialDevice( portName: port, readInterval: const Duration(milliseconds: 100), logger: logger, + baudRate: baudRate, ); if (!await device.init()) { logger.critical("Could not connect to $port"); return; } logger.info("Connected. Listening..."); - device.stream.listen(processAscii); + device.stream.listen(handlePacket); device.startListening(); } -Future listenToFirmware(String port) async { +Future listenToFirmware(String port, int baudRate) async { logger.info("Connecting to $port..."); final device = BurtFirmwareSerial( port: port, logger: logger, + baudRate: baudRate, ); if (!await device.init()) { logger.critical("Could not connect to $port"); @@ -35,58 +73,76 @@ Future listenToFirmware(String port) async { return; } logger.info("Connected? ${device.isReady}. Listening..."); - constructor = getDataConstructor(device.device); + constructor = constructors[device.device]; if (constructor == null) { logger.error("Unsupported serial device: ${device.device.name}"); await device.dispose(); return; } - device.rawStream.listen(processFirmware); + device.rawStream.listen(handlePacket); } -typedef ProtoConstructor = Message Function(List data); - -ProtoConstructor? getDataConstructor(Device device) => switch (device) { - Device.DRIVE => DriveData.fromBuffer, - Device.ARM => ArmData.fromBuffer, - Device.GRIPPER => GripperData.fromBuffer, - Device.SCIENCE => GripperData.fromBuffer, - _ => null, -}; +void printUsage(ArgParser parser) => + // ignore: avoid_print + print("\nUsage: dart run :serial [-h] [-f] [-r | -d ] [-b ] \n${parser.usage}"); -ProtoConstructor? constructor; +ArgParser buildParser() => ArgParser() + ..addFlag("help", abbr: "h", negatable: false, help: "Show this help message") + ..addFlag("firmware", abbr: "f", negatable: false, help: "Whether to perform the standard firmware handshake") + ..addFlag("raw", abbr: "r", negatable: false, help: "Whether to print incoming packets as raw bytes") + ..addOption("baud", abbr: "b", defaultsTo: "9600", help: "The baud rate to listen with") + ..addOption( + "device", abbr: "d", + allowed: deviceNames.keys, + help: "The device being read. This will attempt to parse data as messages from that device\n" + " Note: If -f is passed, this will be ignored and the firmware will identify itself", + ); -void main(List args) async { - if (args.isEmpty) { - logger.info("Ports: ${SerialPort.availablePorts}"); - return; - } else if (args.contains("-h") || args.contains("--help")) { - logger.info("Usage: dart run -r :serial [-a | --ascii] [port]"); +Future main(List args) async { + // Basic arg parsing + final parser = buildParser(); + final ArgResults results; + try { + results = buildParser().parse(args); + } on FormatException catch (error) { + logger.error(error.message); + printUsage(parser); return; } - var port = args.first; - if (!Platform.isWindows && !port.startsWith("/dev")) port = "/dev/$port"; - if (args.contains("-a") || args.contains("--ascii")) { - logger.info("Running in ASCII mode"); - ascii = true; - } - if (args.contains("-f") || args.contains("--firmware")) { - await listenToFirmware(port); - } else { - await listenToDevice(port); + final isFirmware = results.flag("firmware"); + final showHelp = results.flag("help"); + final deviceName = results.option("device"); + final baudRateString = results.option("baud")!; + final baudRate = int.tryParse(baudRateString); + rawMode = results.flag("raw"); + var port = results.rest.firstOrNull; + if (showHelp) { + printUsage(parser); + return; + } else if (port == null) { + logger.info("Ports: ${SerialDevice.allPorts}"); + return; + } else if (baudRate == null) { + logger.critical("Could not parse baud rate as an integer: $baudRateString"); + exit(1); + } else if (rawMode && deviceName != null) { + logger.critical("Cannot specify both --raw and --device"); + exit(2); } -} -void processFirmware(Uint8List buffer) { - try { - final data = constructor!(buffer); - logger.debug("Got data: ${data.toProto3Json()}"); - } catch (error) { - logger.error("Could not decode DriveData: $error\n Buffer: $buffer"); + // Try to identify a device, if passed. + final device = deviceNames[deviceName]; + constructor = constructors[device]; + if (constructor != null) { + final buffer = Uint8List(0); + messageName = constructor!(buffer).messageName; + logger.info("Parsing all data as $messageName messages"); } -} -void processAscii(Uint8List buffer) { - final s = String.fromCharCodes(buffer).trim(); - logger.debug("Got string: $s"); + if (!Platform.isWindows && !port.startsWith("/dev")) port = "/dev/$port"; + if (isFirmware) { + await listenToFirmware(port, baudRate); + } else { + await listenToDevice(port, baudRate); + } } diff --git a/lib/src/devices/firmware.dart b/lib/src/devices/firmware.dart index 38ff442..08428f6 100644 --- a/lib/src/devices/firmware.dart +++ b/lib/src/devices/firmware.dart @@ -41,8 +41,7 @@ class FirmwareManager extends Service { logger.debug("Initializing device: ${device.port}"); result &= await device.init(); if (!device.isReady) continue; - final subscription = device.messages?.listen(collection.server.sendWrapper); - if (subscription == null) continue; + final subscription = device.messages.listen(collection.server.sendWrapper); _subscriptions.add(subscription); } return result; diff --git a/pubspec.yaml b/pubspec.yaml index 4c12f79..1ca8411 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -8,6 +8,7 @@ environment: # Add regular dependencies here. dependencies: + args: ^2.6.0 burt_network: git: https://github.com/BinghamtonRover/Networking.git collection: ^1.19.0 From 0ab7f8e578e8af854b2853194cfe3d7fd3cdf4f7 Mon Sep 17 00:00:00 2001 From: Levi Lesches Date: Mon, 23 Dec 2024 09:35:56 -0800 Subject: [PATCH 2/2] Integrate with Pub Workspace (#15) --- .github/pubspec_overrides.yaml | 7 +++++++ .github/workflows/analyzer.yml | 12 ++++++++++-- .github/workflows/dartdocs.yml | 16 ++++++++++++---- .gitignore | 2 +- pubspec.yaml | 5 +++-- 5 files changed, 33 insertions(+), 9 deletions(-) create mode 100644 .github/pubspec_overrides.yaml diff --git a/.github/pubspec_overrides.yaml b/.github/pubspec_overrides.yaml new file mode 100644 index 0000000..b7c9d12 --- /dev/null +++ b/.github/pubspec_overrides.yaml @@ -0,0 +1,7 @@ +resolution: + +dependency_overrides: + burt_network: + git: + url: https://github.com/BinghamtonRover/Networking + ref: 2.3.1 \ No newline at end of file diff --git a/.github/workflows/analyzer.yml b/.github/workflows/analyzer.yml index 2d2c9fe..1e3c9f1 100644 --- a/.github/workflows/analyzer.yml +++ b/.github/workflows/analyzer.yml @@ -17,10 +17,18 @@ jobs: # https://github.com/dart-lang/setup-dart/blob/main/README.md - uses: dart-lang/setup-dart@v1 with: - sdk: 3.5.2 + sdk: 3.6.0 + # This package is part of a Pub Workspace. However, CI still needs to + # run on this repo by itself, so we want to override burt_network to use + # a Git dependency ONLY on GitHub Actions. + # + # To get around this, we commit the overrides to the .github folder where + # Dart can't find them, then copy them as part of the CI workflow. - name: Install dependencies - run: dart pub get + run: | + mv .github/pubspec_overrides.yaml . + dart pub get - name: Analyze project source run: dart analyze --fatal-infos diff --git a/.github/workflows/dartdocs.yml b/.github/workflows/dartdocs.yml index e0e3012..2608f22 100644 --- a/.github/workflows/dartdocs.yml +++ b/.github/workflows/dartdocs.yml @@ -10,7 +10,7 @@ jobs: steps: - uses: actions/checkout@v3 - with: + with: fetch-depth: 0 - name: Git Setup @@ -24,12 +24,20 @@ jobs: - name: Install Dart uses: dart-lang/setup-dart@v1 + # This package is part of a Pub Workspace. However, CI still needs to + # run on this repo by itself, so we want to override burt_network to use + # a Git dependency ONLY on GitHub Actions. + # + # To get around this, we commit the overrides to the .github folder where + # Dart can't find them, then copy them as part of the CI workflow. - name: Install dependencies - run: dart pub get + run: | + mv .github/pubspec_overrides.yaml . + dart pub get - name: Analyze Dart code run: dart analyze --fatal-infos - + - name: Output error if: failure() run: echo "::error The code or is missing documentation. Run flutter analyze --dartdocs" @@ -44,4 +52,4 @@ jobs: git status git stage --force docs git commit -a -m "Generated documentation" - git push --force + git push --force diff --git a/.gitignore b/.gitignore index 83d7e79..05ccf6c 100644 --- a/.gitignore +++ b/.gitignore @@ -15,4 +15,4 @@ build/ # This file tells the rover to get its dependencies from local files instead of Git/Pub. # GitHub's CI needs to use the Pub versions, so we don't check in this file. -pubspec_overrides.yaml +./pubspec_overrides.yaml diff --git a/pubspec.yaml b/pubspec.yaml index 1ca8411..21f9572 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -3,8 +3,9 @@ description: A sample command-line application. version: 1.0.0 publish_to: none +resolution: workspace environment: - sdk: ^3.5.0 + sdk: ^3.6.0 # Add regular dependencies here. dependencies: @@ -20,6 +21,6 @@ dependencies: protobuf: ^3.1.0 dev_dependencies: - ffigen: ^14.0.0 + ffigen: ^16.0.0 test: ^1.25.8 very_good_analysis: ^6.0.0