diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index aba5ea4..585119d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -13,7 +13,72 @@ defaults: shell: bash jobs: - build: + build-docker: + strategy: + fail-fast: false + matrix: + include: + - container: wpilib/aarch64-cross-ubuntu:bullseye-22.04 + name: LinuxARM64 + build-options: "-Ponlylinuxarm64" + platform-type: linuxarm64 + arch: arm64 + - container: wpilib/raspbian-cross-ubuntu:bullseye-22.04 + name: LinuxARM32 + build-options: "-Ponlylinuxarm32" + platform-type: linuxarm32 + arch: arm32 + runs-on: ubuntu-latest + name: "Build - ${{ matrix.name }}" + container: ${{ matrix.container }} + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + ref: ${{ github.sha }} + + - name: Setup Java + uses: actions/setup-java@v4 + with: + distribution: 'zulu' + java-version: 11 + + - name: Build + run: | + ./gradlew outputVersions publish ${{ matrix.build-options }} -PreleaseMode + + - name: Download WPILib HAL artifacts and headers for ${{ matrix.platform-type }} + run : | + halVersion=$(cat wpiHalVersion.txt) + + halPlatformUrl=https://frcmaven.wpi.edu/artifactory/release/edu/wpi/first/hal/hal-cpp/"$halVersion"/hal-cpp-"$halVersion"-${{ matrix.platform-type }}.zip + utilPlatformUrl=https://frcmaven.wpi.edu/artifactory/release/edu/wpi/first/wpiutil/wpiutil-cpp/"$halVersion"/wpiutil-cpp-"$halVersion"-${{ matrix.platform-type }}.zip + + curl -o halPlatform.zip "$halPlatformUrl" + curl -o utilPlatform.zip "$utilPlatformUrl" + + - name: Unzip WPILib HAL artifacts and headers + run: | + unzip halPlatform.zip -d halPlatform + unzip utilPlatform.zip -d utilPlatform + mkdir -p CANBridge-artifacts + + # Put Linux ARM release files together in one directory + - name: Create Artifact + run: | + cp build/libs/cANBridge/static/release/libCANBridge.a CANBridge-artifacts/libCANBridge.a + cp build/libs/cANBridge/shared/release/libCANBridge.so CANBridge-artifacts/libCANBridge.so + cp halPlatform/linux/${{ matrix.arch }}/shared/libwpiHal.so CANBridge-artifacts/libwpiHal.so + cp utilPlatform/linux/${{ matrix.arch }}/shared/libwpiutil.so CANBridge-artifacts/libwpiutil.so + + # Upload build artifact + - name: Upload build artifact + uses: actions/upload-artifact@v4 + with: + name: CANBridge-${{ matrix.platform-type }} + path: CANBridge-artifacts/ + + build-native: timeout-minutes: 15 strategy: fail-fast: false @@ -21,18 +86,30 @@ jobs: include: - os: windows-latest container: '' - name: windows64 - name: "build-${{ matrix.name }}" + name: Win64 + build-options: "" + platform-type: windowsx86-64 + - os: ubuntu-latest + container: '' + name: Linux64 + platform-type: linuxx86-64 + build-options: "" + - os: macos-latest + container: '' + name: macOS + platform-type: osxuniversal + build-options: "" + name: "Build - ${{ matrix.name }}" runs-on: ${{ matrix.os }} container: ${{ matrix.container }} steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: ref: ${{ github.sha }} - name: Setup Java - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: distribution: 'zulu' java-version: 11 @@ -41,56 +118,92 @@ jobs: run: | ./gradlew outputVersions publish ${{ matrix.build-options }} -PreleaseMode - - name: Download WPILib HAL artifacts and headers, gather all needed headers + - name: Download WPILib HAL artifacts and headers for ${{ matrix.platform-type }} run : | halVersion=$(cat wpiHalVersion.txt) - # Download WPILib artifacts from Artifactory - halWindowsUrl=https://frcmaven.wpi.edu/artifactory/release/edu/wpi/first/hal/hal-cpp/"$halVersion"/hal-cpp-"$halVersion"-windowsx86-64.zip - halHeadersUrl=https://frcmaven.wpi.edu/artifactory/release/edu/wpi/first/hal/hal-cpp/"$halVersion"/hal-cpp-"$halVersion"-headers.zip - utilWindowsUrl=https://frcmaven.wpi.edu/artifactory/release/edu/wpi/first/wpiutil/wpiutil-cpp/"$halVersion"/wpiutil-cpp-"$halVersion"-windowsx86-64.zip - utilHeadersUrl=https://frcmaven.wpi.edu/artifactory/release/edu/wpi/first/wpiutil/wpiutil-cpp/"$halVersion"/wpiutil-cpp-"$halVersion"-headers.zip - curl -o halWindows.zip "$halWindowsUrl" - curl -o halHeaders.zip "$halHeadersUrl" - curl -o utilWindows.zip "$utilWindowsUrl" - curl -o utilHeaders.zip "$utilHeadersUrl" - unzip halWindows.zip -d halWindows - unzip halHeaders.zip -d halHeaders - unzip utilWindows.zip -d utilWindows - unzip utilHeaders.zip -d utilHeaders + halPlatformUrl=https://frcmaven.wpi.edu/artifactory/release/edu/wpi/first/hal/hal-cpp/"$halVersion"/hal-cpp-"$halVersion"-${{ matrix.platform-type }}.zip + utilPlatformUrl=https://frcmaven.wpi.edu/artifactory/release/edu/wpi/first/wpiutil/wpiutil-cpp/"$halVersion"/wpiutil-cpp-"$halVersion"-${{ matrix.platform-type }}.zip - # Gather all of the the needed headers - mkdir headers-for-artifact - cp -r halHeaders/hal headers-for-artifact - cp -r utilHeaders/wpi headers-for-artifact - cp -r src/main/native/include/* headers-for-artifact + curl -o halPlatform.zip "$halPlatformUrl" + curl -o utilPlatform.zip "$utilPlatformUrl" - # Zip the needed headers and put them in the appropriate location for artifact upload + - name: Unzip WPILib HAL artifacts and headers + run: | + unzip halPlatform.zip -d halPlatform + unzip utilPlatform.zip -d utilPlatform mkdir -p CANBridge-artifacts - 7z a CANBridge-artifacts/headers.zip ./headers-for-artifact/* - # Put release files together in one directory - - name: Create Artifact + # Put Windows release files together in one directory + - name: Create Artifact (windows64) + if: matrix.platform-type == 'windowsx86-64' run: | - mkdir -p CANBridge-artifacts cp build/libs/cANBridge/static/windowsx86-64/release/CANBridge.lib CANBridge-artifacts/CANBridge-static.lib cp build/libs/cANBridge/shared/windowsx86-64/release/CANBridge.dll CANBridge-artifacts/CANBridge.dll cp build/libs/cANBridge/shared/windowsx86-64/release/CANBridge.lib CANBridge-artifacts/CANBridge.lib - cp halWindows/windows/x86-64/shared/wpiHal.dll CANBridge-artifacts/wpiHal.dll - cp halWindows/windows/x86-64/shared/wpiHal.lib CANBridge-artifacts/wpiHal.lib - cp utilWindows/windows/x86-64/shared/wpiutil.dll CANBridge-artifacts/wpiutil.dll - cp utilWindows/windows/x86-64/shared/wpiutil.lib CANBridge-artifacts/wpiutil.lib + cp halPlatform/windows/x86-64/shared/wpiHal.dll CANBridge-artifacts/wpiHal.dll + cp halPlatform/windows/x86-64/shared/wpiHal.lib CANBridge-artifacts/wpiHal.lib + cp utilPlatform/windows/x86-64/shared/wpiutil.dll CANBridge-artifacts/wpiutil.dll + cp utilPlatform/windows/x86-64/shared/wpiutil.lib CANBridge-artifacts/wpiutil.lib + + # Put Linux release files together in one directory + - name: Create Artifact (linux64) + if: matrix.platform-type == 'linuxx86-64' + run: | + cp build/libs/cANBridge/static/linuxx86-64/release/libCANBridge.a CANBridge-artifacts/libCANBridge.a + cp build/libs/cANBridge/shared/linuxx86-64/release/libCANBridge.so CANBridge-artifacts/libCANBridge.so + cp halPlatform/linux/x86-64/shared/libwpiHal.so CANBridge-artifacts/libwpiHal.so + cp utilPlatform/linux/x86-64/shared/libwpiutil.so CANBridge-artifacts/libwpiutil.so + + # Put macOS release files together in one directory + - name: Create Artifact (osxuniversal) + if: matrix.platform-type == 'osxuniversal' + run: | + cp build/libs/cANBridge/static/osxuniversal/release/libCANBridge.a CANBridge-artifacts/libCANBridge.a + cp build/libs/cANBridge/shared/osxuniversal/release/libCANBridge.dylib CANBridge-artifacts/libCANBridge.dylib + cp halPlatform/osx/universal/shared/libwpiHal.dylib CANBridge-artifacts/libwpiHal.dylib + cp utilPlatform/osx/universal/shared/libwpiutil.dylib CANBridge-artifacts/libwpiutil.dylib # Upload build artifact - name: Upload build artifact - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: - name: CANBridge-${{ github.sha }} + name: CANBridge-${{ matrix.platform-type }}-${{ matrix.name }} path: CANBridge-artifacts/ - # Upload version.txt - - name: Upload version artifact - uses: actions/upload-artifact@v3 + wpi-headers: + runs-on: ubuntu-latest + name: "WPILib Headers" + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + ref: ${{ github.sha }} + - name: Download WPILib HAL artifacts and headers for linuxx86-64 + run : | + halVersion=$(cat wpiHalVersion.txt) + + halHeadersUrl=https://frcmaven.wpi.edu/artifactory/release/edu/wpi/first/hal/hal-cpp/"$halVersion"/hal-cpp-"$halVersion"-headers.zip + utilHeadersUrl=https://frcmaven.wpi.edu/artifactory/release/edu/wpi/first/wpiutil/wpiutil-cpp/"$halVersion"/wpiutil-cpp-"$halVersion"-headers.zip + + curl -o halHeaders.zip "$halHeadersUrl" + curl -o utilHeaders.zip "$utilHeadersUrl" + + - name: Unzip WPILib HAL artifacts and headers + run: | + unzip halHeaders.zip -d halHeaders + unzip utilHeaders.zip -d utilHeaders + + - name: Gather all needed headers + run: | + mkdir headers-for-artifact + cp -r halHeaders/hal headers-for-artifact + cp -r utilHeaders/wpi headers-for-artifact + cp -r src/main/native/include/* headers-for-artifact + + # Upload build artifact + - name: Upload build artifact + uses: actions/upload-artifact@v4 with: - name: version - path: build/allOutputs/version.txt + path: headers-for-artifact + name: headers \ No newline at end of file diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 0c00949..7e25dc0 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -3,7 +3,10 @@ name: Create release on: push: tags: - - 'v*' + - 'v**' + +permissions: + contents: write defaults: run: @@ -11,25 +14,23 @@ defaults: jobs: check-versions: + name: Check build and publish versions runs-on: ubuntu-latest outputs: TAG_NAME: ${{ env.TAG_NAME }} VERSION: ${{ steps.get_version.outputs.version }} steps: - - name: Wait for build to finish - uses: lewagon/wait-on-check-action@v1.3.1 + - name: Wait for build workflow to finish + uses: lewagon/wait-on-check-action@v1.3.4 with: ref: ${{ github.ref }} - check-name: 'build-windows64' + check-regexp: 'Build|WPILib Headers' repo-token: ${{ secrets.GITHUB_TOKEN }} wait-interval: 10 - - name: Get tag name - run: | - echo "TAG_NAME=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV # Download artifacts from build workflow - name: Download workflow artifacts - uses: dawidd6/action-download-artifact@v2 + uses: dawidd6/action-download-artifact@v6 with: workflow: build.yml commit: ${{ github.sha }} @@ -37,38 +38,35 @@ jobs: # Get publish.gradle version - name: Get publish.gradle version - id: get_version + id: get-version run: | echo "version=$(cat version/version.txt)" >> $GITHUB_OUTPUT echo "expectedTagName=v$(cat version/version.txt)" >> $GITHUB_OUTPUT - # Check publish.gradle version - - name: publish.gradle version check FAILED - if: ${{ steps.get_version.outputs.expectedTagName != env.TAG_NAME }} - run: | - echo Tag name: ${{ env.TAG_NAME }} - echo publish.gradle version: ${{ steps.get_version.outputs.version }} - exit 1 - prepare-release: + name: Prepare release runs-on: ubuntu-latest needs: check-versions steps: # Download API, docs, and version.txt - name: Download workflow artifacts - uses: dawidd6/action-download-artifact@v2 + uses: dawidd6/action-download-artifact@v6 with: workflow: build.yml commit: ${{ github.sha }} path: '.' + skip_unpack: true + + # This step is to check what files are downloaded and how they are structured, as well as binary sizes for releases + - name: List files + run: | + ls -Rlh # Create new release draft - name: Create release - id: create_release - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - VERSION=${{ needs.check-versions.outputs.version }} - TAG=v$VERSION - ls --recursive -l - gh release create $TAG CANBridge-${{ github.sha }}/* --repo $GITHUB_REPOSITORY --draft --title "Version $VERSION" + uses: softprops/action-gh-release@v2 + with: + draft: true + generate_release_notes: true + files: | + **/** diff --git a/.gitignore b/.gitignore index da0930e..1204be5 100644 --- a/.gitignore +++ b/.gitignore @@ -162,3 +162,6 @@ bin/ # End of https://www.gitignore.io/api/c++,java,linux,macos,gradle,windows,visualstudiocode /docs/ + +#Intellji +.idea/ \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index 47280d7..8412ef7 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -68,7 +68,23 @@ "iterator": "cpp", "sstream": "cpp", "cctype": "cpp", - "concepts": "cpp" + "concepts": "cpp", + "inet.h": "c", + "cwctype": "cpp", + "array": "cpp", + "hash_map": "cpp", + "*.tcc": "cpp", + "bitset": "cpp", + "complex": "cpp", + "condition_variable": "cpp", + "memory_resource": "cpp", + "numeric": "cpp", + "optional": "cpp", + "random": "cpp", + "set": "cpp", + "string_view": "cpp", + "cinttypes": "cpp" }, - "C_Cpp.default.configurationProvider": "vscode-wpilib" + "C_Cpp.default.configurationProvider": "vscode-wpilib", + "C_Cpp.errorSquiggles": "enabled" } \ No newline at end of file diff --git a/.wpilib/wpilib_preferences.json b/.wpilib/wpilib_preferences.json index 8a51c0a..fc2a570 100644 --- a/.wpilib/wpilib_preferences.json +++ b/.wpilib/wpilib_preferences.json @@ -1,6 +1,6 @@ { "enableCppIntellisense": true, "currentLanguage": "cpp", - "projectYear": "Beta2020-2", + "projectYear": "2020", "teamNumber": 9999 -} +} \ No newline at end of file diff --git a/CANBridge.code-workspace b/CANBridge.code-workspace index d1180f3..b496e2a 100644 --- a/CANBridge.code-workspace +++ b/CANBridge.code-workspace @@ -75,7 +75,9 @@ "list": "cpp", "set": "cpp", "unordered_map": "cpp", - "xhash": "cpp" + "xhash": "cpp", + "stdlib.h": "c", + "candle.h": "c" }, "java.configuration.updateBuildConfiguration": "disabled" } diff --git a/README.md b/README.md index d89cb62..42ee6d1 100644 --- a/README.md +++ b/README.md @@ -6,17 +6,13 @@ This repository is for the CANBridge software that is run on non-roboRIO platfor ## Behavior -When sending a frame with a given interval, the behavior when -setting a new interval is as follows: +When sending a frame with a given interval, the behavior when setting a new interval is as follows: -The first time a frame is scheduled with an interval, it -will be sent at the next available time. The next instance -of the same frame id will be sent after that interval, even -if the interval has changed. +The first time a frame is scheduled with an interval, it will be sent at the next available time. The next instance of the same frame id will be sent after that interval, even if the interval has changed. See the following pseudo-example: -``` +```py sendMessage(frame, 5000) delay(1000) @@ -43,38 +39,52 @@ guarantees that each new message will be sent. ## Build Requirements 1. Git -2. Visual Studio Code or some other equivalent IDE -3. Gradle Build Tool -4. Java JDK/JRE +2. Gradle Build Tool +3. Java JDK 11 or newer (we recommend [Azul](https://www.azul.com/downloads/#zulu) or [Eclipse Temurin](https://adoptium.net/temurin/)) +4. A modern C++ compiler with C++20 support -All of these, excluding Git, can be installed and configured with the [WPILib Installer](https://github.com/wpilibsuite/allwpilib/releases), which will include specific versions for building FRC robot code. Gradle needs a C++ compiler, which is included with VS Code. +> [!NOTE] +> +> All of these, excluding Git, can be installed and configured with the [WPILib Installer](https://github.com/wpilibsuite/allwpilib/releases), which will include specific versions for building FRC robot code. Gradle needs a C++ compiler, which is included with WPILib's development environment. ## Building and Publishing -Building is done using the Gradle wrapper, `gradlew`. The Gradle wrapper is located in the root of the project, so Gradle commands must be run from there. +Building is done using the Gradle wrapper, `gradlew`. The Gradle wrapper is located in the root of the project, so Gradle commands must be run from there. -1. Clone this repository and open in VS Code - - When VS Code first opens, select `Add workspace folder...` underneath `Start` on the Welcome Screen -2. Open the VS Code terminal - - `View -> Terminal` or ``Ctrl+` `` -3. Run `./gradlew build` from root +1. Clone repository +2. Open a terminal inside the newly cloned repository +3. `./gradlew build` The output is placed at `~\releases\maven\release\com\revrobotics\usb\CANBridge-cpp\\`. -### Publishing a new version +### Publishing a new version (for repository owners) -Before publishing a new version, run `./gradlew build` locally to run the tests. GitHub Actions -cannot run the tests because they depend on having a USB CAN device connected. +> [!NOTE] +> +> Before publishing a new version, run `./gradlew build` locally to run the tests. GitHub Actions ***cannot*** run the tests because they depend on having a USB CAN device connected (e.g. SPARK MAX motor controller). 1. Bump the version number in `publish.gradle` and `CANBridge.json` 2. Commit the version bump 3. Create a new tag named `vX.X.X` at that commit 4. Push the tag to GitHub -5. Wait for the draft release to be created +5. Wait for the draft release to be created (this is automatically done via GitHub Actions) 6. Add release notes to the draft release 7. Publish the draft release +## Linux + +> [!NOTE] +> +> This branch is a work in progress, testing does show that it does work, however this still might be some issues. If you do run across those issues, please create an issue so we can diagnose. + +The latest firmware version will work with Linux if the `SocketCAN` and `gs_usb` drivers are enabled. The following udev rule will work to enable this: + +Create a new file named and located at: `/etc/udev/rules.d/99-canbridge.rules` + +```text +ACTION=="add", ATTRS{idVendor}=="0483", ATTRS{idProduct}=="a30e", RUN+="/sbin/modprobe gs_usb" RUN+="/bin/sh -c 'echo 0483 a30e > /sys/bus/usb/drivers/gs_usb/new_id'" +``` + ## Changelog The SDK Changelog can be viewed with [Changelog.md](Changelog.md). - diff --git a/build.gradle b/build.gradle index be00e2a..2f15627 100644 --- a/build.gradle +++ b/build.gradle @@ -78,6 +78,9 @@ model { } } binaries.all { + if (it.targetPlatform.name == 'osxuniversal') { + linker.args '-framework', 'IOKit' + } if (it.targetPlatform.name == nativeUtils.wpi.platforms.roborio) { it.buildable = false } diff --git a/config.gradle b/config.gradle index 7a59e9d..5c29469 100644 --- a/config.gradle +++ b/config.gradle @@ -8,13 +8,13 @@ nativeUtils { // When updating WPILib, be sure to also update wpiHalVersion.txt wpiVersion = "2023.+" niLibVersion = "2023.3.0" - googleTestVersion = "1.11.0-3" + googleTestVersion = "1.11.0-4" } } } nativeUtils.wpi.addWarnings() -nativeUtils.wpi.addWarningsAsErrors() +// nativeUtils.wpi.addWarningsAsErrors() nativeUtils.setSinglePrintPerPlatform() diff --git a/gradlew b/gradlew old mode 100644 new mode 100755 diff --git a/src/main/native/cpp/CANBridge.cpp b/src/main/native/cpp/CANBridge.cpp index 68a9f26..980dddc 100644 --- a/src/main/native/cpp/CANBridge.cpp +++ b/src/main/native/cpp/CANBridge.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019 REV Robotics + * Copyright (c) 2019 - 2020 REV Robotics * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -45,6 +45,10 @@ #include "rev/Drivers/SerialPort/SerialDriver.h" +#ifdef __linux__ +#include "rev/Drivers/SocketCAN/SocketCANDriver.h" +#endif + #include #include @@ -61,7 +65,9 @@ static const std::vector CANDriverList = { #ifdef _WIN32 new rev::usb::CandleWinUSBDriver(), #endif - new rev::usb::SerialDriver() +#ifdef __linux__ + new rev::usb::SocketCANDriver(), +#endif }; static std::vector, rev::usb::CANBridge_CANFilter>> CANDeviceList = {}; @@ -312,3 +318,4 @@ void CANBridge_UnregisterDeviceFromHAL(const char* descriptor) } + diff --git a/src/main/native/cpp/CANBridgeUtils.cpp b/src/main/native/cpp/CANBridgeUtils.cpp index a820183..15a2a8c 100644 --- a/src/main/native/cpp/CANBridgeUtils.cpp +++ b/src/main/native/cpp/CANBridgeUtils.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019 REV Robotics + * Copyright (c) 2019 - 2020 REV Robotics * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -102,7 +102,17 @@ int parse_serial_com_port(const std::string& in) { if(!in.empty() && (substr_break < in.length())) { std::string num = in.substr(substr_break, in.length()); if (!num.empty()) { - return std::stoi(num); + int returnval = -1; + try + { + returnval = std::stoi(num); + } + catch(const std::exception& e) + { + std::cerr << "parse_serial_com_port: Error running stoi: \'" << e.what() << "\' on string: \'" + num + "\' with original string: \'" + in + "\'\n"; + } + + return returnval; } } diff --git a/src/main/native/cpp/Drivers/CandleWinUSB/CandleWinUSBDevice.cpp b/src/main/native/cpp/Drivers/CandleWinUSB/CandleWinUSBDevice.cpp index ef53acb..8457ac7 100644 --- a/src/main/native/cpp/Drivers/CandleWinUSB/CandleWinUSBDevice.cpp +++ b/src/main/native/cpp/Drivers/CandleWinUSB/CandleWinUSBDevice.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019 REV Robotics + * Copyright (c) 2019 - 2020 REV Robotics * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/native/cpp/Drivers/CandleWinUSB/CandleWinUSBDriver.cpp b/src/main/native/cpp/Drivers/CandleWinUSB/CandleWinUSBDriver.cpp index f4594a7..bb3452b 100644 --- a/src/main/native/cpp/Drivers/CandleWinUSB/CandleWinUSBDriver.cpp +++ b/src/main/native/cpp/Drivers/CandleWinUSB/CandleWinUSBDriver.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019 REV Robotics + * Copyright (c) 2019 - 2020 REV Robotics * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/native/cpp/Drivers/Serial/SerialDevice.cpp b/src/main/native/cpp/Drivers/Serial/SerialDevice.cpp index c227b07..32fcab9 100644 --- a/src/main/native/cpp/Drivers/Serial/SerialDevice.cpp +++ b/src/main/native/cpp/Drivers/Serial/SerialDevice.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019 REV Robotics + * Copyright (c) 2019 - 2020 REV Robotics * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -26,7 +26,6 @@ * POSSIBILITY OF SUCH DAMAGE. */ -#ifdef _WIN32 #include "rev/Drivers/SerialPort/SerialDevice.h" @@ -156,6 +155,4 @@ bool SerialDevice::IsConnected() } // namespace usb } // namespace rev -#else -typedef int __ISOWarning__CLEAR_; -#endif // _WIN32 + diff --git a/src/main/native/cpp/Drivers/Serial/SerialDriver.cpp b/src/main/native/cpp/Drivers/Serial/SerialDriver.cpp index 1be761f..d5b5862 100644 --- a/src/main/native/cpp/Drivers/Serial/SerialDriver.cpp +++ b/src/main/native/cpp/Drivers/Serial/SerialDriver.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019 REV Robotics + * Copyright (c) 2019 - 2020 REV Robotics * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -26,7 +26,7 @@ * POSSIBILITY OF SUCH DAMAGE. */ -#ifdef _WIN32 + #include "rev/Drivers/SerialPort/SerialDriver.h" #include "rev/Drivers/SerialPort/SerialDevice.h" @@ -51,6 +51,7 @@ std::vector SerialDriver::GetDevices() std::vector found = serial::list_ports(); for (auto& dev : found) { + std::cout << "Found serial device with hardware ID: " + dev.hardware_id + '\n'; if (parse_serial_com_port(dev.port) != -1 && dev.hardware_id.compare(SparkMax_HardwareId) == 0) { std::string name("SPARK MAX"); retval.push_back({dev.port, name, this->GetName()}); @@ -81,7 +82,3 @@ std::unique_ptr SerialDriver::CreateDeviceFromDescriptor(const char* } // namespace usb } // namespace rev - -#else -typedef int __ISOWarning__CLEAR_; -#endif // _WIN32 diff --git a/src/main/native/cpp/Drivers/Serial/SerialMessage.cpp b/src/main/native/cpp/Drivers/Serial/SerialMessage.cpp index 983aec7..d802ecc 100644 --- a/src/main/native/cpp/Drivers/Serial/SerialMessage.cpp +++ b/src/main/native/cpp/Drivers/Serial/SerialMessage.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019 REV Robotics + * Copyright (c) 2019 - 2020 REV Robotics * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/native/cpp/Drivers/SocketCAN/SocketCANDevice.cpp b/src/main/native/cpp/Drivers/SocketCAN/SocketCANDevice.cpp new file mode 100644 index 0000000..cd36981 --- /dev/null +++ b/src/main/native/cpp/Drivers/SocketCAN/SocketCANDevice.cpp @@ -0,0 +1,119 @@ +#ifdef __linux__ + +#include "rev/Drivers/SocketCAN/SocketCANDevice.h" + +#include +#include +#include + +#include +#include + +namespace rev { +namespace usb { + +SocketCANDevice::SocketCANDevice(std::string port) : + m_thread(port) { + m_descriptor = port; + // TODO: Get the name of the device, for now just hardcode the name + m_name = "SocketCAN Device"; + m_thread.Start(); + } + +SocketCANDevice::~SocketCANDevice() { + m_thread.Stop(); +} + +std::string SocketCANDevice::GetName() const { + return m_name; +} + +std::string SocketCANDevice::GetDescriptor() const { + return m_descriptor; +} + +int SocketCANDevice::GetNumberOfErrors() { + return m_thread.GetNumberOfErrors(); +} + +int SocketCANDevice::GetId() const { + return 0; +} + +CANStatus SocketCANDevice::SendCANMessage(const CANMessage& msg, int periodMs) { + m_thread.EnqueueMessage(msg, periodMs); + return m_thread.GetLastThreadError(); +} + +CANStatus SocketCANDevice::ReceiveCANMessage(std::shared_ptr& msg, uint32_t messageID, uint32_t messageMask) { + CANStatus status = CANStatus::kTimeout; + + // parse through the keys, find the messges the match, and return it + // The first in the message id, then the messages + std::map> messages; + m_thread.ReceiveMessage(messages); + std::shared_ptr mostRecent; + for (auto& m : messages) { + if ( + CANBridge_ProcessMask({m.second->GetMessageId(), 0}, m.first) + && CANBridge_ProcessMask({messageID, messageMask}, m.first) + && (!mostRecent || m.second->GetTimestampUs() > mostRecent->GetTimestampUs()) + ) { + mostRecent = m.second; + status = CANStatus::kOk; + } + } + + if (status == CANStatus::kOk) { + msg = mostRecent; + status = m_thread.GetLastThreadError(); + } else { + status = CANStatus::kError; + } + + + return status; +} + +CANStatus SocketCANDevice::OpenStreamSession(uint32_t* sessionHandle, CANBridge_CANFilter filter, uint32_t maxSize) { + CANStatus stat = CANStatus::kOk; + m_thread.OpenStream(sessionHandle, filter, maxSize, &stat); + return m_thread.GetLastThreadError(); +} + +CANStatus SocketCANDevice::CloseStreamSession(uint32_t sessionHandle) { + m_thread.CloseStream(sessionHandle); + return m_thread.GetLastThreadError(); +} + +CANStatus SocketCANDevice::ReadStreamSession(uint32_t sessionHandle, struct HAL_CANStreamMessage* msgs, uint32_t messagesToRead, uint32_t* messagesRead) { + m_thread.ReadStream(sessionHandle, msgs, messagesToRead, messagesRead); + return m_thread.GetLastThreadError(); +} + +CANStatus SocketCANDevice::GetCANDetailStatus(float* percentBusUtilization, uint32_t* busOff, uint32_t* txFull, uint32_t* receiveErr, uint32_t* transmitErr) { + rev::usb::CANStatusDetails details; + m_thread.GetCANStatus(&details); + *percentBusUtilization = 0.0f; + *busOff = details.busOffCount; + *txFull = details.txFullCount; + *receiveErr = details.receiveErrCount; + *transmitErr = details.transmitErrCount; + return m_thread.GetLastThreadError(); +} + +CANStatus SocketCANDevice::GetCANDetailStatus(float* percentBusUtilization, uint32_t* busOff, uint32_t* txFull, uint32_t* receiveErr, uint32_t* transmitErr, uint32_t* lastErrorTime) { + return GetCANDetailStatus(percentBusUtilization, busOff, txFull, receiveErr, transmitErr); +} + +bool SocketCANDevice::IsConnected() { + // Implementation + return true; +} + +} // namespace usb +} // namespace rev + + +#else +#endif \ No newline at end of file diff --git a/src/main/native/cpp/Drivers/SocketCAN/SocketCANDriver.cpp b/src/main/native/cpp/Drivers/SocketCAN/SocketCANDriver.cpp new file mode 100644 index 0000000..e604811 --- /dev/null +++ b/src/main/native/cpp/Drivers/SocketCAN/SocketCANDriver.cpp @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2019 - 2020 REV Robotics + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of REV Robotics nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifdef __linux__ + +#include "rev/Drivers/SocketCAN/SocketCANDriver.h" +#include "rev/Drivers/SocketCAN/SocketCANDevice.h" + +#include +#include +#include + +#include + +namespace rev { +namespace usb { + +std::vector SocketCANDriver::GetDevices() +{ + std::vector retval; + + // TODO: Better way of doing this? + // find canx or vcanx interface names + struct if_nameindex *if_nidxs, *intf; + + if_nidxs = if_nameindex(); + if ( if_nidxs != NULL ) + { + for (intf = if_nidxs; intf->if_index != 0 || intf->if_name != NULL; intf++) + { + char* buf = intf->if_name; + + // Not possible can name, protect later compares + if (strnlen(buf, 4) < 4) { + continue; + } + + // possibly vcanx + if (buf[0] == 'v') { + buf++; + } + + if (strncmp(buf, "can", 3) == 0) { + std::string ifnameStr = std::string(intf->if_name); + retval.push_back( {ifnameStr, ifnameStr, this->GetName()} ); + } + } + + if_freenameindex(if_nidxs); + } + + return retval; +} + +std::unique_ptr SocketCANDriver::CreateDeviceFromDescriptor(const char* descriptor) +{ + try { + return std::make_unique(descriptor); + } catch(...) { + // do nothing if it failed + } + return std::unique_ptr(nullptr); +} +} // namespace usb +} // namespace rev + +#else +typedef int __ISOWarning__CLEAR_; +#endif // _WIN32 diff --git a/src/main/native/cpp/ThreadUtils.cpp b/src/main/native/cpp/ThreadUtils.cpp index 2d9e9b3..1bce0f7 100644 --- a/src/main/native/cpp/ThreadUtils.cpp +++ b/src/main/native/cpp/ThreadUtils.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019 REV Robotics + * Copyright (c) 2019 - 2020 REV Robotics * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/native/cpp/can-utils/canframelen.c b/src/main/native/cpp/can-utils/canframelen.c new file mode 100644 index 0000000..da509eb --- /dev/null +++ b/src/main/native/cpp/can-utils/canframelen.c @@ -0,0 +1,263 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */ +/* + * canframelen.c + * + * Copyright (c) 2013, 2014 Czech Technical University in Prague + * + * Author: Michal Sojka + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Czech Technical University in Prague nor the + * names of its contributors may be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * Alternatively, provided that this notice is retained in full, this + * software may be distributed under the terms of the GNU General + * Public License ("GPL") version 2, in which case the provisions of the + * GPL apply INSTEAD OF those given above. + * + * The provided data structures and external interfaces from this code + * are not restricted to be used by modules with a GPL compatible license. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * Send feedback to + * + */ + +#include "canframelen.h" +#include +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Functions and types for CRC checks. + * + * Generated on Wed Jan 8 15:14:20 2014, + * by pycrc v0.8.1, http://www.tty1.net/pycrc/ + * using the configuration: + * Width = 15 + * Poly = 0x4599 + * XorIn = 0x0000 + * ReflectIn = False + * XorOut = 0x0000 + * ReflectOut = False + * Algorithm = table-driven + *****************************************************************************/ + +typedef uint16_t crc_t; + +/** + * Static table used for the table_driven implementation. + *****************************************************************************/ +static const crc_t crc_table[256] = { + 0x0000, 0x4599, 0x4eab, 0x0b32, 0x58cf, 0x1d56, 0x1664, 0x53fd, 0x7407, 0x319e, 0x3aac, 0x7f35, 0x2cc8, 0x6951, 0x6263, 0x27fa, + 0x2d97, 0x680e, 0x633c, 0x26a5, 0x7558, 0x30c1, 0x3bf3, 0x7e6a, 0x5990, 0x1c09, 0x173b, 0x52a2, 0x015f, 0x44c6, 0x4ff4, 0x0a6d, + 0x5b2e, 0x1eb7, 0x1585, 0x501c, 0x03e1, 0x4678, 0x4d4a, 0x08d3, 0x2f29, 0x6ab0, 0x6182, 0x241b, 0x77e6, 0x327f, 0x394d, 0x7cd4, + 0x76b9, 0x3320, 0x3812, 0x7d8b, 0x2e76, 0x6bef, 0x60dd, 0x2544, 0x02be, 0x4727, 0x4c15, 0x098c, 0x5a71, 0x1fe8, 0x14da, 0x5143, + 0x73c5, 0x365c, 0x3d6e, 0x78f7, 0x2b0a, 0x6e93, 0x65a1, 0x2038, 0x07c2, 0x425b, 0x4969, 0x0cf0, 0x5f0d, 0x1a94, 0x11a6, 0x543f, + 0x5e52, 0x1bcb, 0x10f9, 0x5560, 0x069d, 0x4304, 0x4836, 0x0daf, 0x2a55, 0x6fcc, 0x64fe, 0x2167, 0x729a, 0x3703, 0x3c31, 0x79a8, + 0x28eb, 0x6d72, 0x6640, 0x23d9, 0x7024, 0x35bd, 0x3e8f, 0x7b16, 0x5cec, 0x1975, 0x1247, 0x57de, 0x0423, 0x41ba, 0x4a88, 0x0f11, + 0x057c, 0x40e5, 0x4bd7, 0x0e4e, 0x5db3, 0x182a, 0x1318, 0x5681, 0x717b, 0x34e2, 0x3fd0, 0x7a49, 0x29b4, 0x6c2d, 0x671f, 0x2286, + 0x2213, 0x678a, 0x6cb8, 0x2921, 0x7adc, 0x3f45, 0x3477, 0x71ee, 0x5614, 0x138d, 0x18bf, 0x5d26, 0x0edb, 0x4b42, 0x4070, 0x05e9, + 0x0f84, 0x4a1d, 0x412f, 0x04b6, 0x574b, 0x12d2, 0x19e0, 0x5c79, 0x7b83, 0x3e1a, 0x3528, 0x70b1, 0x234c, 0x66d5, 0x6de7, 0x287e, + 0x793d, 0x3ca4, 0x3796, 0x720f, 0x21f2, 0x646b, 0x6f59, 0x2ac0, 0x0d3a, 0x48a3, 0x4391, 0x0608, 0x55f5, 0x106c, 0x1b5e, 0x5ec7, + 0x54aa, 0x1133, 0x1a01, 0x5f98, 0x0c65, 0x49fc, 0x42ce, 0x0757, 0x20ad, 0x6534, 0x6e06, 0x2b9f, 0x7862, 0x3dfb, 0x36c9, 0x7350, + 0x51d6, 0x144f, 0x1f7d, 0x5ae4, 0x0919, 0x4c80, 0x47b2, 0x022b, 0x25d1, 0x6048, 0x6b7a, 0x2ee3, 0x7d1e, 0x3887, 0x33b5, 0x762c, + 0x7c41, 0x39d8, 0x32ea, 0x7773, 0x248e, 0x6117, 0x6a25, 0x2fbc, 0x0846, 0x4ddf, 0x46ed, 0x0374, 0x5089, 0x1510, 0x1e22, 0x5bbb, + 0x0af8, 0x4f61, 0x4453, 0x01ca, 0x5237, 0x17ae, 0x1c9c, 0x5905, 0x7eff, 0x3b66, 0x3054, 0x75cd, 0x2630, 0x63a9, 0x689b, 0x2d02, + 0x276f, 0x62f6, 0x69c4, 0x2c5d, 0x7fa0, 0x3a39, 0x310b, 0x7492, 0x5368, 0x16f1, 0x1dc3, 0x585a, 0x0ba7, 0x4e3e, 0x450c, 0x0095 +}; + +/** + * Update the crc value with new data. + * + * \param crc The current crc value. + * \param data Pointer to a buffer of \a data_len bytes. + * \param data_len Number of bytes in the \a data buffer. + * \return The updated crc value. + *****************************************************************************/ +static crc_t crc_update_bytewise(crc_t crc, const unsigned char *data, size_t data_len) +{ + unsigned int tbl_idx; + + while (data_len--) { + tbl_idx = ((crc >> 7) ^ *data) & 0xff; + crc = (crc_table[tbl_idx] ^ (crc << 8)) & 0x7fff; + + data++; + } + return crc & 0x7fff; +} + +/** + * Update the crc value with new data. + * + * \param crc The current crc value. + * \param data Data value + * \param bits The number of most significant bits in data used for CRC calculation + * \return The updated crc value. + *****************************************************************************/ +static crc_t crc_update_bitwise(crc_t crc, uint8_t data, size_t bits) +{ + uint8_t i; + bool bit; + + for (i = 0x80; bits--; i >>= 1) { + bit = crc & 0x4000; + if (data & i) { + bit = !bit; + } + crc <<= 1; + if (bit) { + crc ^= 0x4599; + } + } + return crc & 0x7fff; +} + +static crc_t calc_bitmap_crc(uint8_t *bitmap, unsigned start, unsigned end) +{ + crc_t crc = 0; + + if (start % 8) { + crc = crc_update_bitwise(crc, bitmap[start / 8] << (start % 8), 8 - start % 8); + start += 8 - start % 8; + } + crc = crc_update_bytewise(crc, &bitmap[start / 8], (end - start) / 8); + crc = crc_update_bitwise(crc, bitmap[end / 8], end % 8); + return crc; +} + +static unsigned cfl_exact(struct can_frame *frame) +{ + uint8_t bitmap[16]; + unsigned start = 0, end; + crc_t crc; + uint16_t crc_be; + uint8_t mask, lookfor; + unsigned i, stuffed; + const int8_t clz[32] = /* count of leading zeros in 5 bit numbers */ + { 5, 4, 3, 3, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + + /* Prepare bitmap */ + memset(bitmap, 0, sizeof(bitmap)); + if (frame->can_id & CAN_EFF_FLAG) { + /* bit 7 0 7 0 7 0 7 0 + * bitmap[0-3] |.sBBBBBB BBBBBSIE EEEEEEEE EEEEEEEE| s = SOF, B = Base ID (11 bits), S = SRR, I = IDE, E = Extended ID (18 bits) + * bitmap[4-7] |ER10DLC4 00000000 11111111 22222222| R = RTR, 0 = r0, 1 = r1, DLC4 = DLC, Data bytes + * bitmap[8-11] |33333333 44444444 55555555 66666666| Data bytes + * bitmap[12-15] |77777777 ........ ........ ........| Data bytes + */ + bitmap[0] = (frame->can_id & CAN_EFF_MASK) >> 23; + bitmap[1] = ((frame->can_id >> 18) & 0x3f) << 3 | + 3 << 1 | /* SRR, IDE */ + ((frame->can_id >> 17) & 0x01); + bitmap[2] = (frame->can_id >> 9) & 0xff; + bitmap[3] = (frame->can_id >> 1) & 0xff; + bitmap[4] = (frame->can_id & 0x1) << 7 | + (!!(frame->can_id & CAN_RTR_FLAG)) << 6 | + 0 << 4 | /* r1, r0 */ + (frame->can_dlc & 0xf); + memcpy(&bitmap[5], &frame->data, frame->can_dlc); + start = 1; + end = 40 + 8*frame->can_dlc; + } else { + /* bit 7 0 7 0 7 0 7 0 + * bitmap[0-3] |.....sII IIIIIIII IRE0DLC4 00000000| s = SOF, I = ID (11 bits), R = RTR, E = IDE, DLC4 = DLC + * bitmap[4-7] |11111111 22222222 33333333 44444444| Data bytes + * bitmap[8-11] |55555555 66666666 77777777 ........| Data bytes + */ + bitmap[0] = (frame->can_id & CAN_SFF_MASK) >> 9; + bitmap[1] = (frame->can_id >> 1) & 0xff; + bitmap[2] = ((frame->can_id << 7) & 0xff) | + (!!(frame->can_id & CAN_RTR_FLAG)) << 6 | + 0 << 4 | /* IDE, r0 */ + (frame->can_dlc & 0xf); + memcpy(&bitmap[3], &frame->data, frame->can_dlc); + start = 5; + end = 24 + 8 * frame->can_dlc; + } + + /* Calc and append CRC */ + crc = calc_bitmap_crc(bitmap, start, end); + crc_be = htons(crc << 1); + assert(end % 8 == 0); + memcpy(bitmap + end / 8, &crc_be, 2); + end += 15; + + /* Count stuffed bits */ + mask = 0x1f; + lookfor = 0; + i = start; + stuffed = 0; + while (i < end) { + unsigned change; + unsigned bits = (bitmap[i / 8] << 8 | bitmap[i / 8 + 1]) >> (16 - 5 - i % 8); + lookfor = lookfor ? 0 : mask; /* We alternate between looking for a series of zeros or ones */ + change = (bits & mask) ^ lookfor; /* 1 indicates a change */ + if (change) { /* No bit was stuffed here */ + i += clz[change]; + mask = 0x1f; /* Next look for 5 same bits */ + } else { + i += (mask == 0x1f) ? 5 : 4; + if (i <= end) { + stuffed++; + mask = 0x1e; /* Next look for 4 bits (5th bit is the stuffed one) */ + } + } + } + return end - start + stuffed + + 3 + /* CRC del, ACK, ACK del */ + 7 + /* EOF */ + 3; /* IFS */ +} + + +unsigned can_frame_length(struct canfd_frame *frame, enum cfl_mode mode, int mtu) +{ + int eff = (frame->can_id & CAN_EFF_FLAG); + + if (mtu != CAN_MTU) + return 0; /* CANFD is not supported yet */ + + switch (mode) { + case CFL_NO_BITSTUFFING: + return (eff ? 67 : 47) + frame->len * 8; + case CFL_WORSTCASE: + return (eff ? 80 : 55) + frame->len * 10; + case CFL_EXACT: + return cfl_exact((struct can_frame*)frame); + } + return 0; /* Unknown mode */ +} + +#ifdef __cplusplus +} +#endif diff --git a/src/main/native/cpp/serial/unix.cc b/src/main/native/cpp/serial/unix.cc index 4283548..76cc41a 100644 --- a/src/main/native/cpp/serial/unix.cc +++ b/src/main/native/cpp/serial/unix.cc @@ -452,6 +452,7 @@ Serial::SerialImpl::reconfigurePort () if (stopbits_ == stopbits_one_point_five) { byte_time_ns_ += ((1.5 - stopbits_one_point_five) * bit_time_ns); } + } void diff --git a/src/main/native/include/can-utils/canframelen.h b/src/main/native/include/can-utils/canframelen.h new file mode 100644 index 0000000..0384b28 --- /dev/null +++ b/src/main/native/include/can-utils/canframelen.h @@ -0,0 +1,93 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */ +/* + * canframelen.h + * + * Copyright (c) 2013, 2014 Czech Technical University in Prague + * + * Author: Michal Sojka + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Czech Technical University in Prague nor the + * names of its contributors may be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * Alternatively, provided that this notice is retained in full, this + * software may be distributed under the terms of the GNU General + * Public License ("GPL") version 2, in which case the provisions of the + * GPL apply INSTEAD OF those given above. + * + * The provided data structures and external interfaces from this code + * are not restricted to be used by modules with a GPL compatible license. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * Send feedback to + * + */ + +#ifndef CANFRAMELEN_H +#define CANFRAMELEN_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Frame length calculation modes. + * + * CFL_WORSTCASE corresponds to *worst* case calculation for + * stuff-bits - see (1)-(3) in [1]. The worst case number of bits on + * the wire can be calculated as: + * + * (34 + 8n - 1)/4 + 34 + 8n + 13 for SFF frames (11 bit CAN-ID) => 55 + 10n + * (54 + 8n - 1)/4 + 54 + 8n + 13 for EFF frames (29 bit CAN-ID) => 80 + 10n + * + * while 'n' is the data length code (number of payload bytes) + * + * [1] "Controller Area Network (CAN) schedulability analysis: + * Refuted, revisited and revised", Real-Time Syst (2007) + * 35:239-272. + * + */ +enum cfl_mode { + CFL_NO_BITSTUFFING, /* plain bit calculation without bitstuffing */ + CFL_WORSTCASE, /* worst case estimation - see above */ + CFL_EXACT, /* exact calculation of stuffed bits based on frame + * content and CRC */ +}; + +/** + * Calculates the number of bits a frame needs on the wire (including + * inter frame space). + * + * Mode determines how to deal with stuffed bits. + */ +unsigned can_frame_length(struct canfd_frame *frame, enum cfl_mode mode, int mtu); + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/src/main/native/include/rev/CANBridgeUtils.h b/src/main/native/include/rev/CANBridgeUtils.h index 6431ff0..ec19dae 100644 --- a/src/main/native/include/rev/CANBridgeUtils.h +++ b/src/main/native/include/rev/CANBridgeUtils.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019 REV Robotics + * Copyright (c) 2019 - 2020 REV Robotics * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/native/include/rev/CANDevice.h b/src/main/native/include/rev/CANDevice.h index fedf874..a1327c4 100644 --- a/src/main/native/include/rev/CANDevice.h +++ b/src/main/native/include/rev/CANDevice.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019 REV Robotics + * Copyright (c) 2019 - 2020 REV Robotics * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/native/include/rev/CANDriver.h b/src/main/native/include/rev/CANDriver.h index 937a85d..fc58bbe 100644 --- a/src/main/native/include/rev/CANDriver.h +++ b/src/main/native/include/rev/CANDriver.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019 REV Robotics + * Copyright (c) 2019 - 2020 REV Robotics * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/native/include/rev/CANMessage.h b/src/main/native/include/rev/CANMessage.h index 2d75565..98e3f03 100644 --- a/src/main/native/include/rev/CANMessage.h +++ b/src/main/native/include/rev/CANMessage.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019 REV Robotics + * Copyright (c) 2019 - 2020 REV Robotics * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/native/include/rev/CANStatus.h b/src/main/native/include/rev/CANStatus.h index 1382e6b..a1895dc 100644 --- a/src/main/native/include/rev/CANStatus.h +++ b/src/main/native/include/rev/CANStatus.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019 REV Robotics + * Copyright (c) 2019 - 2020 REV Robotics * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/native/include/rev/Drivers/CandleWinUSB/CandleWinUSBDevice.h b/src/main/native/include/rev/Drivers/CandleWinUSB/CandleWinUSBDevice.h index e65757d..0ebd486 100644 --- a/src/main/native/include/rev/Drivers/CandleWinUSB/CandleWinUSBDevice.h +++ b/src/main/native/include/rev/Drivers/CandleWinUSB/CandleWinUSBDevice.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019 REV Robotics + * Copyright (c) 2019 - 2020 REV Robotics * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/native/include/rev/Drivers/CandleWinUSB/CandleWinUSBDeviceThread.h b/src/main/native/include/rev/Drivers/CandleWinUSB/CandleWinUSBDeviceThread.h index 82eedc1..8ae9a76 100644 --- a/src/main/native/include/rev/Drivers/CandleWinUSB/CandleWinUSBDeviceThread.h +++ b/src/main/native/include/rev/Drivers/CandleWinUSB/CandleWinUSBDeviceThread.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019 REV Robotics + * Copyright (c) 2019 - 2020 REV Robotics * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/native/include/rev/Drivers/CandleWinUSB/CandleWinUSBDriver.h b/src/main/native/include/rev/Drivers/CandleWinUSB/CandleWinUSBDriver.h index d431a5b..97cc99f 100644 --- a/src/main/native/include/rev/Drivers/CandleWinUSB/CandleWinUSBDriver.h +++ b/src/main/native/include/rev/Drivers/CandleWinUSB/CandleWinUSBDriver.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019 REV Robotics + * Copyright (c) 2019 - 2020 REV Robotics * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/native/include/rev/Drivers/DriverDeviceThread.h b/src/main/native/include/rev/Drivers/DriverDeviceThread.h index 11a046b..9c43266 100644 --- a/src/main/native/include/rev/Drivers/DriverDeviceThread.h +++ b/src/main/native/include/rev/Drivers/DriverDeviceThread.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019 REV Robotics + * Copyright (c) 2019 - 2020 REV Robotics * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/native/include/rev/Drivers/SerialPort/SerialDevice.h b/src/main/native/include/rev/Drivers/SerialPort/SerialDevice.h index f3a1528..c18953e 100644 --- a/src/main/native/include/rev/Drivers/SerialPort/SerialDevice.h +++ b/src/main/native/include/rev/Drivers/SerialPort/SerialDevice.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019 REV Robotics + * Copyright (c) 2019 - 2020 REV Robotics * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/native/include/rev/Drivers/SerialPort/SerialDeviceThread.h b/src/main/native/include/rev/Drivers/SerialPort/SerialDeviceThread.h index a85d97d..1edebb7 100644 --- a/src/main/native/include/rev/Drivers/SerialPort/SerialDeviceThread.h +++ b/src/main/native/include/rev/Drivers/SerialPort/SerialDeviceThread.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019 REV Robotics + * Copyright (c) 2019 - 2020 REV Robotics * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/native/include/rev/Drivers/SerialPort/SerialDriver.h b/src/main/native/include/rev/Drivers/SerialPort/SerialDriver.h index dc6bf20..1a5c348 100644 --- a/src/main/native/include/rev/Drivers/SerialPort/SerialDriver.h +++ b/src/main/native/include/rev/Drivers/SerialPort/SerialDriver.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019 REV Robotics + * Copyright (c) 2019 - 2020 REV Robotics * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/native/include/rev/Drivers/SerialPort/SerialMessage.h b/src/main/native/include/rev/Drivers/SerialPort/SerialMessage.h index 90c404f..4121400 100644 --- a/src/main/native/include/rev/Drivers/SerialPort/SerialMessage.h +++ b/src/main/native/include/rev/Drivers/SerialPort/SerialMessage.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019 REV Robotics + * Copyright (c) 2019 - 2020 REV Robotics * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/native/include/rev/Drivers/SocketCAN/SocketCANDevice.h b/src/main/native/include/rev/Drivers/SocketCAN/SocketCANDevice.h new file mode 100644 index 0000000..956ba03 --- /dev/null +++ b/src/main/native/include/rev/Drivers/SocketCAN/SocketCANDevice.h @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2019 - 2020 REV Robotics + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of REV Robotics nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once +#ifdef __linux__ + +#include +#include + +#include "rev/Drivers/SocketCAN/SocketCANThread.h" +#include "rev/CANDevice.h" +#include "rev/CANMessage.h" +#include "rev/CANStatus.h" + +namespace rev { +namespace usb { + +class SocketCANDevice : public CANDevice { +public: + SocketCANDevice() = delete; + SocketCANDevice(std::string port); + virtual ~SocketCANDevice(); + + virtual std::string GetName() const; + virtual std::string GetDescriptor() const; + virtual int GetNumberOfErrors(); + + virtual int GetId() const; + + virtual CANStatus SendCANMessage(const CANMessage& msg, int periodMs) override; + virtual CANStatus ReceiveCANMessage(std::shared_ptr& msg, uint32_t messageID, uint32_t messageMask) override; + virtual CANStatus OpenStreamSession(uint32_t* sessionHandle, CANBridge_CANFilter filter, uint32_t maxSize) override; + virtual CANStatus CloseStreamSession(uint32_t sessionHandle) override; + virtual CANStatus ReadStreamSession(uint32_t sessionHandle, HAL_CANStreamMessage* msgs, uint32_t messagesToRead, uint32_t* messagesRead) override; + + virtual CANStatus GetCANDetailStatus(float* percentBusUtilization, uint32_t* busOff, uint32_t* txFull, uint32_t* receiveErr, uint32_t* transmitErr) override; + virtual CANStatus GetCANDetailStatus(float* percentBusUtilization, uint32_t* busOff, uint32_t* txFull, uint32_t* receiveErr, uint32_t* transmitErr, uint32_t* lastErrorTime) override; + + virtual bool IsConnected() override; +private: + SocketCANDeviceThread m_thread; + std::string m_descriptor; + std::string m_name; +}; + +} // namespace usb +} // namespace rev + +#endif diff --git a/src/main/native/include/rev/Drivers/SocketCAN/SocketCANDriver.h b/src/main/native/include/rev/Drivers/SocketCAN/SocketCANDriver.h new file mode 100644 index 0000000..dd12edb --- /dev/null +++ b/src/main/native/include/rev/Drivers/SocketCAN/SocketCANDriver.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2019 - 2020 REV Robotics + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of REV Robotics nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include +#include + +#include "rev/CANDriver.h" + +namespace rev { +namespace usb { + +class SocketCANDriver : public CANDriver { +public: + SocketCANDriver() {} + virtual ~SocketCANDriver() override {} + + virtual std::string GetName() const {return "SocketCAN";} + + virtual std::vector GetDevices() override; + virtual std::unique_ptr CreateDeviceFromDescriptor(const char* descriptor) override; +}; + +} // namespace usb +} // namespace rev diff --git a/src/main/native/include/rev/Drivers/SocketCAN/SocketCANThread.h b/src/main/native/include/rev/Drivers/SocketCAN/SocketCANThread.h new file mode 100644 index 0000000..d643d9f --- /dev/null +++ b/src/main/native/include/rev/Drivers/SocketCAN/SocketCANThread.h @@ -0,0 +1,224 @@ +#ifdef __linux__ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rev/CANMessage.h" +#include "rev/CANBridgeUtils.h" +#include "rev/CANStatus.h" +#include "rev/Drivers/DriverDeviceThread.h" + +#include "utils/ThreadUtils.h" + +#include +#include + +namespace rev { +namespace usb { + +class SocketCANDeviceThread : public DriverDeviceThread { +public: + SocketCANDeviceThread() = delete; + SocketCANDeviceThread(std::string port, long long threadIntervalMs = 1) + : DriverDeviceThread(0xe45b5597, threadIntervalMs), m_port(port), m_socket(-1) { } + + ~SocketCANDeviceThread() { + if (m_socket != -1) { + close(m_socket); + } + } + + void Start() override { + if (m_thread.get() != nullptr && m_thread->joinable()) { + m_thread->join(); + } + + // Initialize SocketCAN + if (!initializeSocketCAN()) { + m_threadStatus = CANStatus::kError; + return; + } + + m_thread = std::make_unique(&SocketCANDeviceThread::SocketRun, this); + + // Set to high priority to prevent buffer overflow on the device on high client CPU load + utils::SetThreadPriority(m_thread.get(), utils::ThreadPriority::High); + } + + void OpenStream(uint32_t* handle, CANBridge_CANFilter filter, uint32_t maxSize, CANStatus *status) override { + std::lock_guard lock(m_streamMutex); + + // Create the handle + *handle = m_counter++; + + // Add to the map + m_readStream[*handle] = std::unique_ptr(new CANStreamHandle{filter.messageId, filter.messageMask, maxSize, utils::CircularBuffer>{maxSize}}); + + *status = CANStatus::kOk; + } + +private: + std::string m_port; + int m_socket; + + bool initializeSocketCAN() { + struct ifreq ifr; + struct sockaddr_can addr; + + // Create socket + m_socket = socket(PF_CAN, SOCK_RAW, CAN_RAW); + if (m_socket < 0) { + perror("SocketCAN socket"); + return false; + } + + // Specify CAN interface + std::strncpy(ifr.ifr_name, m_port.c_str(), IFNAMSIZ - 1); + if (ioctl(m_socket, SIOCGIFINDEX, &ifr) < 0) { + perror("SocketCAN ioctl"); + return false; + } + + // Bind the socket to the CAN interface + addr.can_family = AF_CAN; + addr.can_ifindex = ifr.ifr_ifindex; + if (bind(m_socket, (struct sockaddr *)&addr, sizeof(addr)) < 0) { + perror("SocketCAN bind"); + return false; + } + + return true; + } + + void ReadMessages(bool &reading) { + struct can_frame frame; + int nbytes = read(m_socket, &frame, sizeof(struct can_frame)); + + reading = (nbytes > 0); + if (reading) { + if (frame.can_id & CAN_ERR_FLAG) { + // Handle error frame + m_statusDetails.busOffCount++; + m_threadStatus = CANStatus::kError; + // Parse error data + if (frame.can_id & 0x00000040) { + m_statusDetails.busOffCount++; + } + if (frame.data[1] & 0x02) { + m_statusDetails.txFullCount++; + } + if (frame.data[1] & 0x10 || frame.data[1] & 0x04) { + m_statusDetails.receiveErrCount++; + } + if (frame.data[1] & 0x20 || frame.data[1] & 0x08 || frame.data[2] & 0x80 || frame.data[4]) { + m_statusDetails.transmitErrCount++; + } + } else { + auto msg = std::make_shared(frame.can_id, + frame.data, + frame.can_dlc, + std::chrono::steady_clock::now().time_since_epoch().count() / 1000); + + // Read functions + { + std::lock_guard lock(m_readMutex); + m_readStore[frame.can_id] = msg; + } + + // Streaming functions + { + std::lock_guard lock(m_streamMutex); + for (auto& stream : m_readStream) { + if (!stream.second->messages.IsFull() + && rev::usb::CANBridge_ProcessMask({stream.second->messageId, stream.second->messageMask}, + msg->GetMessageId())) { + stream.second->messages.Add(msg); + } + } + } + } + + m_threadStatus = CANStatus::kOk; + } + } + + bool WriteMessages(detail::CANThreadSendQueueElement el, std::chrono::steady_clock::time_point now) { + if (el.m_intervalMs <= 1 || (now - el.m_prevTimestamp >= std::chrono::milliseconds(el.m_intervalMs)) ) { + struct can_frame frame; + frame.can_id = el.m_msg.GetMessageId(); + frame.can_dlc = el.m_msg.GetSize(); + std::memcpy(frame.data, el.m_msg.GetData(), frame.can_dlc); + + int nbytes = write(m_socket, &frame, sizeof(struct can_frame)); + if (nbytes != sizeof(struct can_frame)) { + m_threadStatus = CANStatus::kDeviceWriteError; + m_statusErrCount++; + return false; + } else { + m_threadStatus = CANStatus::kOk; + return true; + } + } + return false; + } + + void SocketRun() { + while (m_threadComplete == false) { + m_threadStatus = CANStatus::kOk; // Start each loop with the status being good. Really only a write issue. + auto sleepTime = std::chrono::steady_clock::now() + std::chrono::milliseconds(m_threadIntervalMs); + + // 1) Handle all received CAN traffic + bool reading = false; + ReadMessages(reading); + + // 2) Schedule CANMessage queue + { + std::lock_guard lock(m_writeMutex); + if (m_sendQueue.size() > 0) { + detail::CANThreadSendQueueElement el = m_sendQueue.front(); + if (el.m_intervalMs == -1) { + m_sendQueue.pop_front(); + continue; + } + + auto now = std::chrono::steady_clock::now(); + + // Don't pop queue if send fails + if (WriteMessages(el, now)) { + m_sendQueue.pop_front(); + + // Return to end of queue if repeated + if (el.m_intervalMs > 0 ) { + el.m_prevTimestamp = now; + m_sendQueue.push_back(el); + } + } + } + } + + // 3) Stall thread + if (!reading && m_sendQueue.empty()) { + std::this_thread::sleep_until(sleepTime); + } + } + } +}; + +} // namespace usb +} // namespace rev + +#endif \ No newline at end of file diff --git a/src/main/native/include/utils/CircularBuffer.h b/src/main/native/include/utils/CircularBuffer.h index c9d9cac..ead6629 100644 --- a/src/main/native/include/utils/CircularBuffer.h +++ b/src/main/native/include/utils/CircularBuffer.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019 REV Robotics + * Copyright (c) 2019 - 2020 REV Robotics * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/main/native/include/utils/ThreadUtils.h b/src/main/native/include/utils/ThreadUtils.h index f6b517b..2090071 100644 --- a/src/main/native/include/utils/ThreadUtils.h +++ b/src/main/native/include/utils/ThreadUtils.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019 REV Robotics + * Copyright (c) 2019 - 2020 REV Robotics * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/test/gtest/cpp/CANMessageTest.cpp b/src/test/gtest/cpp/CANMessageTest.cpp index 204df43..66148ca 100644 --- a/src/test/gtest/cpp/CANMessageTest.cpp +++ b/src/test/gtest/cpp/CANMessageTest.cpp @@ -41,6 +41,7 @@ */ TEST(CANMessageTest, SendAndReceive) { auto handle = CANBridge_Scan(); + int32_t status = 0; int numDevices = CANBridge_NumDevices(handle); diff --git a/src/test/gtest/cpp/main.cpp b/src/test/gtest/cpp/main.cpp index 9c3f256..55a2d05 100644 --- a/src/test/gtest/cpp/main.cpp +++ b/src/test/gtest/cpp/main.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019 REV Robotics + * Copyright (c) 2019 - 2020 REV Robotics * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -32,8 +32,36 @@ #include "gtest/gtest.h" +#ifdef __linux__ +#include +#elif _WIN32 +#include +#endif int main(int argc, char** argv) { + + #ifdef __linux__ + rev::usb::SocketCANDriver driver; + #elif _WIN32 + rev::usb::CandleWinUSBDriver driver; + #endif + + + auto output = driver.GetDevices(); + + if (output.size() == 0) { + std::cout << "No devices found" << std::endl; + return 1; + } + + for (auto itr = output.begin(); itr != output.end(); itr++) { + std::cout << itr->descriptor << std::endl; + } + + auto device = driver.CreateDeviceFromDescriptor(output[0].descriptor.c_str()); + + std::cout << "Selected device: " << device->GetName() << std::endl; + HAL_Initialize(500, 0); frc::MockDS ds; ds.start();