From 6e23e1840ad75fcfe047995b8f8f44848d630617 Mon Sep 17 00:00:00 2001 From: Peter Johnson Date: Fri, 12 Mar 2021 15:58:47 -0800 Subject: [PATCH 01/34] [wpilibc] Remove WPILib.h (#3235) It's been deprecated for several years, is often broken as it's not tested frequently, and dramatically increases compile times. --- wpilibc/src/main/native/include/frc/WPILib.h | 107 ------------------- 1 file changed, 107 deletions(-) delete mode 100644 wpilibc/src/main/native/include/frc/WPILib.h diff --git a/wpilibc/src/main/native/include/frc/WPILib.h b/wpilibc/src/main/native/include/frc/WPILib.h deleted file mode 100644 index 25cd0143af..0000000000 --- a/wpilibc/src/main/native/include/frc/WPILib.h +++ /dev/null @@ -1,107 +0,0 @@ -// Copyright (c) FIRST and other WPILib contributors. -// Open Source Software; you can modify and/or share it under the terms of -// the WPILib BSD license file in the root directory of this project. - -#pragma once - -// clang-format off -#ifdef _MSC_VER -#pragma message("warning: Including this header drastically increases compilation times and is bad style. Include only what you use instead.") -#else -#warning "Including this header drastically increases compilation times and is bad style. Include only what you use instead." -#endif - -// clang-format on - -#if __has_include() -#include -#include -#endif - -#include "frc/ADXL345_I2C.h" -#include "frc/ADXL345_SPI.h" -#include "frc/ADXL362.h" -#include "frc/ADXRS450_Gyro.h" -#include "frc/AnalogAccelerometer.h" -#include "frc/AnalogGyro.h" -#include "frc/AnalogInput.h" -#include "frc/AnalogOutput.h" -#include "frc/AnalogPotentiometer.h" -#include "frc/AnalogTrigger.h" -#include "frc/AnalogTriggerOutput.h" -#include "frc/BuiltInAccelerometer.h" -#include "frc/Compressor.h" -#include "frc/Counter.h" -#include "frc/DMC60.h" -#include "frc/DigitalInput.h" -#include "frc/DigitalOutput.h" -#include "frc/DigitalSource.h" -#include "frc/DoubleSolenoid.h" -#include "frc/DriverStation.h" -#include "frc/Encoder.h" -#include "frc/ErrorBase.h" -#include "frc/GearTooth.h" -#include "frc/GenericHID.h" -#include "frc/I2C.h" -#include "frc/InterruptableSensorBase.h" -#include "frc/IterativeRobot.h" -#include "frc/Jaguar.h" -#include "frc/Joystick.h" -#include "frc/NidecBrushless.h" -#include "frc/Notifier.h" -#include "frc/PIDController.h" -#include "frc/PIDOutput.h" -#include "frc/PIDSource.h" -#include "frc/PWM.h" -#include "frc/PWMSpeedController.h" -#include "frc/PWMTalonSRX.h" -#include "frc/PWMVictorSPX.h" -#include "frc/PowerDistributionPanel.h" -#include "frc/Preferences.h" -#include "frc/Relay.h" -#include "frc/RobotBase.h" -#include "frc/RobotController.h" -#include "frc/RobotDrive.h" -#include "frc/SD540.h" -#include "frc/SPI.h" -#include "frc/SensorUtil.h" -#include "frc/SerialPort.h" -#include "frc/Servo.h" -#include "frc/Solenoid.h" -#include "frc/Spark.h" -#include "frc/SpeedController.h" -#include "frc/SpeedControllerGroup.h" -#include "frc/Talon.h" -#include "frc/Threads.h" -#include "frc/TimedRobot.h" -#include "frc/Timer.h" -#include "frc/Ultrasonic.h" -#include "frc/Utility.h" -#include "frc/Victor.h" -#include "frc/VictorSP.h" -#include "frc/WPIErrors.h" -#include "frc/XboxController.h" -#if __has_include("frc/buttons/InternalButton.h") -#include "frc/buttons/InternalButton.h" -#include "frc/buttons/JoystickButton.h" -#include "frc/buttons/NetworkButton.h" -#include "frc/commands/Command.h" -#include "frc/commands/CommandGroup.h" -#include "frc/commands/PIDCommand.h" -#include "frc/commands/PIDSubsystem.h" -#include "frc/commands/PrintCommand.h" -#include "frc/commands/StartCommand.h" -#include "frc/commands/Subsystem.h" -#include "frc/commands/WaitCommand.h" -#include "frc/commands/WaitForChildren.h" -#include "frc/commands/WaitUntilCommand.h" -#endif -#include "frc/drive/DifferentialDrive.h" -#include "frc/drive/KilloughDrive.h" -#include "frc/drive/MecanumDrive.h" -#include "frc/filters/LinearDigitalFilter.h" -#include "frc/interfaces/Accelerometer.h" -#include "frc/interfaces/Gyro.h" -#include "frc/interfaces/Potentiometer.h" -#include "frc/smartdashboard/SendableChooser.h" -#include "frc/smartdashboard/SmartDashboard.h" From 21624ef27354b7759ba124ac76e873d2a5be01e1 Mon Sep 17 00:00:00 2001 From: Peter Johnson Date: Tue, 16 Mar 2021 22:05:41 -0700 Subject: [PATCH 02/34] Add ImGui OutlineViewer (#3220) --- CMakeLists.txt | 1 + glass/src/libnt/native/cpp/NetworkTables.cpp | 73 +++--- .../glass/networktables/NetworkTables.h | 26 +- outlineviewer/.styleguide | 27 +++ outlineviewer/CMakeLists.txt | 28 +++ outlineviewer/Info.plist | 32 +++ outlineviewer/build.gradle | 119 +++++++++ outlineviewer/publish.gradle | 96 ++++++++ .../src/main/generate/WPILibVersion.cpp.in | 7 + outlineviewer/src/main/native/cpp/main.cpp | 229 ++++++++++++++++++ outlineviewer/src/main/native/mac/ov.icns | Bin 0 -> 92922 bytes .../src/main/native/resources/ov-128.png | Bin 0 -> 4796 bytes .../src/main/native/resources/ov-16.png | Bin 0 -> 533 bytes .../src/main/native/resources/ov-256.png | Bin 0 -> 9809 bytes .../src/main/native/resources/ov-32.png | Bin 0 -> 1149 bytes .../src/main/native/resources/ov-48.png | Bin 0 -> 1852 bytes .../src/main/native/resources/ov-512.png | Bin 0 -> 19904 bytes .../src/main/native/resources/ov-64.png | Bin 0 -> 2447 bytes .../src/main/native/win/outlineviewer.ico | Bin 0 -> 72807 bytes .../src/main/native/win/outlineviewer.rc | 1 + settings.gradle | 1 + 21 files changed, 606 insertions(+), 34 deletions(-) create mode 100644 outlineviewer/.styleguide create mode 100644 outlineviewer/CMakeLists.txt create mode 100644 outlineviewer/Info.plist create mode 100644 outlineviewer/build.gradle create mode 100644 outlineviewer/publish.gradle create mode 100644 outlineviewer/src/main/generate/WPILibVersion.cpp.in create mode 100644 outlineviewer/src/main/native/cpp/main.cpp create mode 100644 outlineviewer/src/main/native/mac/ov.icns create mode 100644 outlineviewer/src/main/native/resources/ov-128.png create mode 100644 outlineviewer/src/main/native/resources/ov-16.png create mode 100644 outlineviewer/src/main/native/resources/ov-256.png create mode 100644 outlineviewer/src/main/native/resources/ov-32.png create mode 100644 outlineviewer/src/main/native/resources/ov-48.png create mode 100644 outlineviewer/src/main/native/resources/ov-512.png create mode 100644 outlineviewer/src/main/native/resources/ov-64.png create mode 100644 outlineviewer/src/main/native/win/outlineviewer.ico create mode 100644 outlineviewer/src/main/native/win/outlineviewer.rc diff --git a/CMakeLists.txt b/CMakeLists.txt index f0e6bc0884..348e0d9797 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -187,6 +187,7 @@ if (WITH_GUI) add_subdirectory(imgui) add_subdirectory(wpigui) add_subdirectory(glass) + add_subdirectory(outlineviewer) endif() if (WITH_CSCORE) diff --git a/glass/src/libnt/native/cpp/NetworkTables.cpp b/glass/src/libnt/native/cpp/NetworkTables.cpp index 7a1c6b7ed1..cea8be538f 100644 --- a/glass/src/libnt/native/cpp/NetworkTables.cpp +++ b/glass/src/libnt/native/cpp/NetworkTables.cpp @@ -741,30 +741,20 @@ void glass::DisplayNetworkTables(NetworkTablesModel* model, ImGui::Columns(); } -void NetworkTablesView::Display() { - auto& storage = GetStorage(); - auto pTreeView = - storage.GetBoolRef("tree", m_defaultFlags & NetworkTablesFlags_TreeView); - auto pShowConnections = storage.GetBoolRef( - "connections", m_defaultFlags & NetworkTablesFlags_ShowConnections); - auto pShowFlags = storage.GetBoolRef( - "flags", m_defaultFlags & NetworkTablesFlags_ShowFlags); - auto pShowTimestamp = storage.GetBoolRef( - "timestamp", m_defaultFlags & NetworkTablesFlags_ShowTimestamp); - auto pCreateNoncanonicalKeys = storage.GetBoolRef( - "createNonCanonical", - m_defaultFlags & NetworkTablesFlags_CreateNoncanonicalKeys); - - if (ImGui::BeginPopupContextItem()) { - ImGui::MenuItem("Tree View", "", pTreeView); - ImGui::MenuItem("Show Connections", "", pShowConnections); - ImGui::MenuItem("Show Flags", "", pShowFlags); - ImGui::MenuItem("Show Timestamp", "", pShowTimestamp); - ImGui::Separator(); - ImGui::MenuItem("Allow creation of non-canonical keys", "", - pCreateNoncanonicalKeys); - - ImGui::EndPopup(); +void NetworkTablesFlagsSettings::Update() { + if (!m_pTreeView) { + auto& storage = GetStorage(); + m_pTreeView = storage.GetBoolRef( + "tree", m_defaultFlags & NetworkTablesFlags_TreeView); + m_pShowConnections = storage.GetBoolRef( + "connections", m_defaultFlags & NetworkTablesFlags_ShowConnections); + m_pShowFlags = storage.GetBoolRef( + "flags", m_defaultFlags & NetworkTablesFlags_ShowFlags); + m_pShowTimestamp = storage.GetBoolRef( + "timestamp", m_defaultFlags & NetworkTablesFlags_ShowTimestamp); + m_pCreateNoncanonicalKeys = storage.GetBoolRef( + "createNonCanonical", + m_defaultFlags & NetworkTablesFlags_CreateNoncanonicalKeys); } m_flags &= @@ -772,11 +762,32 @@ void NetworkTablesView::Display() { NetworkTablesFlags_ShowFlags | NetworkTablesFlags_ShowTimestamp | NetworkTablesFlags_CreateNoncanonicalKeys); m_flags |= - (*pTreeView ? NetworkTablesFlags_TreeView : 0) | - (*pShowConnections ? NetworkTablesFlags_ShowConnections : 0) | - (*pShowFlags ? NetworkTablesFlags_ShowFlags : 0) | - (*pShowTimestamp ? NetworkTablesFlags_ShowTimestamp : 0) | - (*pCreateNoncanonicalKeys ? NetworkTablesFlags_CreateNoncanonicalKeys - : 0); - DisplayNetworkTables(m_model, m_flags); + (*m_pTreeView ? NetworkTablesFlags_TreeView : 0) | + (*m_pShowConnections ? NetworkTablesFlags_ShowConnections : 0) | + (*m_pShowFlags ? NetworkTablesFlags_ShowFlags : 0) | + (*m_pShowTimestamp ? NetworkTablesFlags_ShowTimestamp : 0) | + (*m_pCreateNoncanonicalKeys ? NetworkTablesFlags_CreateNoncanonicalKeys + : 0); +} + +void NetworkTablesFlagsSettings::DisplayMenu() { + if (!m_pTreeView) { + return; + } + ImGui::MenuItem("Tree View", "", m_pTreeView); + ImGui::MenuItem("Show Connections", "", m_pShowConnections); + ImGui::MenuItem("Show Flags", "", m_pShowFlags); + ImGui::MenuItem("Show Timestamp", "", m_pShowTimestamp); + ImGui::Separator(); + ImGui::MenuItem("Allow creation of non-canonical keys", "", + m_pCreateNoncanonicalKeys); +} + +void NetworkTablesView::Display() { + m_flags.Update(); + if (ImGui::BeginPopupContextItem()) { + m_flags.DisplayMenu(); + ImGui::EndPopup(); + } + DisplayNetworkTables(m_model, m_flags.GetFlags()); } diff --git a/glass/src/libnt/native/include/glass/networktables/NetworkTables.h b/glass/src/libnt/native/include/glass/networktables/NetworkTables.h index f515764f42..e7dbbece83 100644 --- a/glass/src/libnt/native/include/glass/networktables/NetworkTables.h +++ b/glass/src/libnt/native/include/glass/networktables/NetworkTables.h @@ -104,19 +104,39 @@ void DisplayNetworkTables( NetworkTablesModel* model, NetworkTablesFlags flags = NetworkTablesFlags_Default); +class NetworkTablesFlagsSettings { + public: + explicit NetworkTablesFlagsSettings( + NetworkTablesFlags defaultFlags = NetworkTablesFlags_Default) + : m_defaultFlags{defaultFlags}, m_flags{defaultFlags} {} + + void Update(); + void DisplayMenu(); + + NetworkTablesFlags GetFlags() const { return m_flags; } + + private: + bool* m_pTreeView = nullptr; + bool* m_pShowConnections = nullptr; + bool* m_pShowFlags = nullptr; + bool* m_pShowTimestamp = nullptr; + bool* m_pCreateNoncanonicalKeys = nullptr; + NetworkTablesFlags m_defaultFlags; // NOLINT + NetworkTablesFlags m_flags; // NOLINT +}; + class NetworkTablesView : public View { public: explicit NetworkTablesView( NetworkTablesModel* model, NetworkTablesFlags defaultFlags = NetworkTablesFlags_Default) - : m_model{model}, m_defaultFlags{defaultFlags}, m_flags{defaultFlags} {} + : m_model{model}, m_flags{defaultFlags} {} void Display() override; private: NetworkTablesModel* m_model; - NetworkTablesFlags m_defaultFlags; - NetworkTablesFlags m_flags; + NetworkTablesFlagsSettings m_flags; }; } // namespace glass diff --git a/outlineviewer/.styleguide b/outlineviewer/.styleguide new file mode 100644 index 0000000000..c8e055a4ca --- /dev/null +++ b/outlineviewer/.styleguide @@ -0,0 +1,27 @@ +cppHeaderFileInclude { + \.h$ + \.inc$ + \.inl$ +} + +cppSrcFileInclude { + \.cpp$ +} + +generatedFileExclude { + src/main/native/resources/ + src/main/native/win/outlineviewer.ico + src/main/native/mac/ov.icns +} + +repoRootNameOverride { + outlineviewer +} + +includeOtherLibs { + ^GLFW + ^imgui + ^ntcore + ^wpi/ + ^wpigui +} diff --git a/outlineviewer/CMakeLists.txt b/outlineviewer/CMakeLists.txt new file mode 100644 index 0000000000..537134d3eb --- /dev/null +++ b/outlineviewer/CMakeLists.txt @@ -0,0 +1,28 @@ +project(outlineviewer) + +include(CompileWarnings) +include(GenResources) +include(LinkMacOSGUI) + +configure_file(src/main/generate/WPILibVersion.cpp.in WPILibVersion.cpp) +GENERATE_RESOURCES(src/main/native/resources generated/main/cpp OV ov outlineviewer_resources_src) + +file(GLOB outlineviewer_src src/main/native/cpp/*.cpp ${CMAKE_CURRENT_BINARY_DIR}/WPILibVersion.cpp) + +if (WIN32) + set(outlineviewer_rc src/main/native/win/outlineviewer.rc) +elseif(APPLE) + set(MACOSX_BUNDLE_ICON_FILE ov.icns) + set(APP_ICON_MACOSX src/main/native/mac/ov.icns) + set_source_files_properties(${APP_ICON_MACOSX} PROPERTIES MACOSX_PACKAGE_LOCATION "Resources") +endif() + +add_executable(outlineviewer ${outlineviewer_src} ${outlineviewer_resources_src} ${outlineviewer_rc} ${APP_ICON_MACOSX}) +wpilib_link_macos_gui(outlineviewer) +target_link_libraries(outlineviewer libglassnt libglass) + +if (WIN32) + set_target_properties(outlineviewer PROPERTIES WIN32_EXECUTABLE YES) +elseif(APPLE) + set_target_properties(outlineviewer PROPERTIES MACOSX_BUNDLE YES OUTPUT_NAME "OutlineViewer") +endif() diff --git a/outlineviewer/Info.plist b/outlineviewer/Info.plist new file mode 100644 index 0000000000..a3e8a85d91 --- /dev/null +++ b/outlineviewer/Info.plist @@ -0,0 +1,32 @@ + + + + + CFBundleName + OutlineViewer + CFBundleExecutable + outlineviewer + CFBundleDisplayName + OutlineViewer + CFBundleIdentifier + edu.wpi.first.tools.OutlineViewer + CFBundleIconFile + ov.icns + CFBundlePackageType + APPL + CFBundleSupportedPlatforms + + MacOSX + + CFBundleInfoDictionaryVersion + 6.0 + CFBundleShortVersionString + 2021 + CFBundleVersion + 2021 + LSMinimumSystemVersion + 10.11 + NSHighResolutionCapable + + + diff --git a/outlineviewer/build.gradle b/outlineviewer/build.gradle new file mode 100644 index 0000000000..5b4ee6ed40 --- /dev/null +++ b/outlineviewer/build.gradle @@ -0,0 +1,119 @@ +import org.gradle.internal.os.OperatingSystem + +if (!project.hasProperty('onlylinuxathena') && !project.hasProperty('onlylinuxraspbian') && !project.hasProperty('onlylinuxaarch64bionic')) { + + description = "NetworkTables Viewer" + + apply plugin: 'cpp' + apply plugin: 'c' + apply plugin: 'google-test-test-suite' + apply plugin: 'visual-studio' + apply plugin: 'edu.wpi.first.NativeUtils' + + if (OperatingSystem.current().isWindows()) { + apply plugin: 'windows-resources' + } + + ext { + nativeName = 'outlineviewer' + } + + apply from: "${rootDir}/shared/resources.gradle" + apply from: "${rootDir}/shared/config.gradle" + + def wpilibVersionFileInput = file("src/main/generate/WPILibVersion.cpp.in") + def wpilibVersionFileOutput = file("$buildDir/generated/main/cpp/WPILibVersion.cpp") + + task generateCppVersion() { + description = 'Generates the wpilib version class' + group = 'WPILib' + + outputs.file wpilibVersionFileOutput + inputs.file wpilibVersionFileInput + + if (wpilibVersioning.releaseMode) { + outputs.upToDateWhen { false } + } + + // We follow a simple set of checks to determine whether we should generate a new version file: + // 1. If the release type is not development, we generate a new version file + // 2. If there is no generated version number, we generate a new version file + // 3. If there is a generated build number, and the release type is development, then we will + // only generate if the publish task is run. + doLast { + def version = wpilibVersioning.version.get() + println "Writing version ${version} to $wpilibVersionFileOutput" + + if (wpilibVersionFileOutput.exists()) { + wpilibVersionFileOutput.delete() + } + def read = wpilibVersionFileInput.text.replace('${wpilib_version}', version) + wpilibVersionFileOutput.write(read) + } + } + + gradle.taskGraph.addTaskExecutionGraphListener { graph -> + def willPublish = graph.hasTask(publish) + if (willPublish) { + generateCppVersion.outputs.upToDateWhen { false } + } + } + + def generateTask = createGenerateResourcesTask('main', 'OV', 'ov', project) + + project(':').libraryBuild.dependsOn build + tasks.withType(CppCompile) { + dependsOn generateTask + dependsOn generateCppVersion + } + + model { + components { + // By default, a development executable will be generated. This is to help the case of + // testing specific functionality of the library. + "${nativeName}"(NativeExecutableSpec) { + baseName = 'outlineviewer' + sources { + cpp { + source { + srcDirs 'src/main/native/cpp', "$buildDir/generated/main/cpp" + include '**/*.cpp' + } + exportedHeaders { + srcDirs 'src/main/native/include' + } + } + if (OperatingSystem.current().isWindows()) { + rc { + source { + srcDirs 'src/main/native/win' + include '*.rc' + } + } + } + } + binaries.all { + if (it.targetPlatform.name == nativeUtils.wpi.platforms.roborio || it.targetPlatform.name == nativeUtils.wpi.platforms.raspbian || it.targetPlatform.name == nativeUtils.wpi.platforms.aarch64bionic) { + it.buildable = false + return + } + lib project: ':glass', library: 'glassnt', linkage: 'static' + lib project: ':glass', library: 'glass', linkage: 'static' + lib project: ':ntcore', library: 'ntcore', linkage: 'static' + lib project: ':wpiutil', library: 'wpiutil', linkage: 'static' + lib project: ':wpigui', library: 'wpigui', linkage: 'static' + nativeUtils.useRequiredLibrary(it, 'imgui_static') + if (it.targetPlatform.operatingSystem.isWindows()) { + it.linker.args << 'Gdi32.lib' << 'Shell32.lib' << 'd3d11.lib' << 'd3dcompiler.lib' + } else if (it.targetPlatform.operatingSystem.isMacOsX()) { + it.linker.args << '-framework' << 'Metal' << '-framework' << 'MetalKit' << '-framework' << 'Cocoa' << '-framework' << 'IOKit' << '-framework' << 'CoreFoundation' << '-framework' << 'CoreVideo' << '-framework' << 'QuartzCore' + } else { + it.linker.args << '-lX11' + } + } + } + } + } + + apply from: 'publish.gradle' +} diff --git a/outlineviewer/publish.gradle b/outlineviewer/publish.gradle new file mode 100644 index 0000000000..85605c47e3 --- /dev/null +++ b/outlineviewer/publish.gradle @@ -0,0 +1,96 @@ +apply plugin: 'maven-publish' + +def baseArtifactId = 'OutlineViewer' +def artifactGroupId = 'edu.wpi.first.tools' +def zipBaseName = '_GROUP_edu_wpi_first_tools_ID_OutlineViewer_CLS' + +def outputsFolder = file("$project.buildDir/outputs") + +model { + publishing { + def outlineViewerTaskList = [] + $.components.each { component -> + component.binaries.each { binary -> + if (binary in NativeExecutableBinarySpec && binary.component.name.contains("outlineviewer")) { + if (binary.buildable && binary.name.contains("Release")) { + // We are now in the binary that we want. + // This is the default application path for the ZIP task. + def applicationPath = binary.executable.file + def icon = file("$project.projectDir/src/main/native/mac/ov.icns") + + // Create the macOS bundle. + def bundleTask = project.tasks.create("bundleOutlineViewerOsxApp", Copy) { + description("Creates a macOS application bundle for OutlineViewer") + from(file("$project.projectDir/Info.plist")) + into(file("$project.buildDir/outputs/bundles/OutlineViewer.app/Contents")) + into("MacOS") { with copySpec { from binary.executable.file } } + into("Resources") { with copySpec { from icon } } + + doLast { + if (project.hasProperty("developerID")) { + // Get path to binary. + exec { + workingDir rootDir + def args = [ + "sh", + "-c", + "codesign --force --strict --deep " + + "--timestamp --options=runtime " + + "--verbose -s ${project.findProperty("developerID")} " + + "$project.buildDir/outputs/bundles/OutlineViewer.app/" + ] + commandLine args + } + } + } + } + + // Reset the application path if we are creating a bundle. + if (binary.targetPlatform.operatingSystem.isMacOsX()) { + applicationPath = file("$project.buildDir/outputs/bundles") + project.build.dependsOn bundleTask + } + + // Create the ZIP. + def task = project.tasks.create("copyOutlineViewerExecutable", Zip) { + description("Copies the OutlineViewer executable to the outputs directory.") + destinationDirectory = outputsFolder + + archiveBaseName = '_M_' + zipBaseName + duplicatesStrategy = 'exclude' + classifier = nativeUtils.getPublishClassifier(binary) + + from(licenseFile) { + into '/' + } + + from(applicationPath) + into(nativeUtils.getPlatformPath(binary)) + } + + if (binary.targetPlatform.operatingSystem.isMacOsX()) { + bundleTask.dependsOn binary.tasks.link + task.dependsOn(bundleTask) + } + + task.dependsOn binary.tasks.link + outlineViewerTaskList.add(task) + project.build.dependsOn task + project.artifacts { task } + addTaskToCopyAllOutputs(task) + } + } + } + } + + publications { + outlineViewer(MavenPublication) { + outlineViewerTaskList.each { artifact it } + + artifactId = baseArtifactId + groupId = artifactGroupId + version wpilibVersioning.version.get() + } + } + } +} diff --git a/outlineviewer/src/main/generate/WPILibVersion.cpp.in b/outlineviewer/src/main/generate/WPILibVersion.cpp.in new file mode 100644 index 0000000000..b0a4490520 --- /dev/null +++ b/outlineviewer/src/main/generate/WPILibVersion.cpp.in @@ -0,0 +1,7 @@ +/* + * Autogenerated file! Do not manually edit this file. This version is regenerated + * any time the publish task is run, or when this file is deleted. + */ +const char* GetWPILibVersion() { + return "${wpilib_version}"; +} diff --git a/outlineviewer/src/main/native/cpp/main.cpp b/outlineviewer/src/main/native/cpp/main.cpp new file mode 100644 index 0000000000..0bfd91614e --- /dev/null +++ b/outlineviewer/src/main/native/cpp/main.cpp @@ -0,0 +1,229 @@ +// Copyright (c) FIRST and other WPILib contributors. +// Open Source Software; you can modify and/or share it under the terms of +// the WPILib BSD license file in the root directory of this project. + +#include + +#include +#include +#include +#include +#include + +#include "glass/Context.h" +#include "glass/Model.h" +#include "glass/networktables/NetworkTables.h" +#include "glass/networktables/NetworkTablesSettings.h" +#include "glass/other/Log.h" + +namespace gui = wpi::gui; + +const char* GetWPILibVersion(); + +namespace ov { +wpi::StringRef GetResource_ov_16_png(); +wpi::StringRef GetResource_ov_32_png(); +wpi::StringRef GetResource_ov_48_png(); +wpi::StringRef GetResource_ov_64_png(); +wpi::StringRef GetResource_ov_128_png(); +wpi::StringRef GetResource_ov_256_png(); +wpi::StringRef GetResource_ov_512_png(); +} // namespace ov + +static std::unique_ptr gModel; +static std::unique_ptr gSettings; +static glass::LogData gLog; +static glass::NetworkTablesFlagsSettings gFlagsSettings; + +static void NtInitialize() { + // update window title when connection status changes + auto inst = nt::GetDefaultInstance(); + auto poller = nt::CreateConnectionListenerPoller(inst); + nt::AddPolledConnectionListener(poller, true); + gui::AddEarlyExecute([inst, poller] { + auto win = gui::GetSystemWindow(); + if (!win) { + return; + } + bool timedOut; + for (auto&& event : nt::PollConnectionListener(poller, 0, &timedOut)) { + if ((nt::GetNetworkMode(inst) & NT_NET_MODE_SERVER) != 0) { + // for server mode, just print number of clients connected + wpi::SmallString<64> title; + glfwSetWindowTitle(win, ("OutlineViewer - " + + wpi::Twine{nt::GetConnections(inst).size()} + + " Clients Connected") + .toNullTerminatedStringRef(title) + .data()); + } else if (event.connected) { + wpi::SmallString<64> title; + title = "OutlineViewer - Connected ("; + title += event.conn.remote_ip; + title += ')'; + glfwSetWindowTitle(win, title.c_str()); + } else { + glfwSetWindowTitle(win, "OutlineViewer - DISCONNECTED"); + } + } + }); + + // handle NetworkTables log messages + auto logPoller = nt::CreateLoggerPoller(inst); + nt::AddPolledLogger(logPoller, NT_LOG_INFO, 100); + gui::AddEarlyExecute([logPoller] { + bool timedOut; + for (auto&& msg : nt::PollLogger(logPoller, 0, &timedOut)) { + const char* level = ""; + if (msg.level >= NT_LOG_CRITICAL) { + level = "CRITICAL: "; + } else if (msg.level >= NT_LOG_ERROR) { + level = "ERROR: "; + } else if (msg.level >= NT_LOG_WARNING) { + level = "WARNING: "; + } + gLog.Append(wpi::Twine{level} + msg.message + wpi::Twine{" ("} + + msg.filename + wpi::Twine{':'} + wpi::Twine{msg.line} + + wpi::Twine{")\n"}); + } + }); + + // NetworkTables table window + gModel = std::make_unique(); + gui::AddEarlyExecute([] { gModel->Update(); }); + + // NetworkTables settings window + gSettings = std::make_unique(); + gui::AddEarlyExecute([] { gSettings->Update(); }); +} + +static void DisplayGui() { + ImGui::GetStyle().WindowRounding = 0; + + // fill entire OS window with this window + ImGui::SetNextWindowPos(ImVec2(0, 0)); + int width, height; + glfwGetWindowSize(gui::GetSystemWindow(), &width, &height); + ImGui::SetNextWindowSize(ImVec2(width, height)); + + ImGui::Begin("Entries", nullptr, + ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_MenuBar | + ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | + ImGuiWindowFlags_NoCollapse); + + gFlagsSettings.Update(); + + // can't create popups from within menu, so use flags + bool settings = false; + bool log = false; + bool about = false; + + // main menu + ImGui::BeginMenuBar(); + gui::EmitViewMenu(); + if (ImGui::BeginMenu("View")) { + gFlagsSettings.DisplayMenu(); + ImGui::EndMenu(); + } + + if (ImGui::BeginMenu("Options")) { + if (ImGui::MenuItem("Settings")) { + settings = true; + } + ImGui::EndMenu(); + } + + if (ImGui::BeginMenu("Info")) { + if (ImGui::MenuItem("Log")) { + log = true; + } + ImGui::Separator(); + if (ImGui::MenuItem("About")) { + about = true; + } + ImGui::EndMenu(); + } + ImGui::EndMenuBar(); + + // settings popup + if (settings) { + ImGui::OpenPopup("Settings"); + } + if (ImGui::BeginPopupModal("Settings", nullptr, + ImGuiWindowFlags_AlwaysAutoResize)) { + if (gSettings->Display()) { + ImGui::CloseCurrentPopup(); + } + ImGui::SameLine(); + if (ImGui::Button("Cancel")) { + ImGui::CloseCurrentPopup(); + } + ImGui::EndPopup(); + } + + // log popup + if (log) { + ImGui::OpenPopup("Log"); + } + if (ImGui::BeginPopupModal("Log", nullptr, + ImGuiWindowFlags_AlwaysAutoResize)) { + if (ImGui::Button("Close")) { + ImGui::CloseCurrentPopup(); + } + ImGui::BeginChild("Lines", ImVec2(width * 0.75f, height * 0.75f)); + glass::DisplayLog(&gLog, true); + ImGui::EndChild(); + ImGui::EndPopup(); + } + + // about popup + if (about) { + ImGui::OpenPopup("About"); + } + if (ImGui::BeginPopupModal("About", nullptr, + ImGuiWindowFlags_AlwaysAutoResize)) { + ImGui::Text("OutlineViewer"); + ImGui::Separator(); + ImGui::Text("v%s", GetWPILibVersion()); + if (ImGui::Button("Close")) { + ImGui::CloseCurrentPopup(); + } + ImGui::EndPopup(); + } + + // display table view + glass::DisplayNetworkTables(gModel.get(), gFlagsSettings.GetFlags()); + + ImGui::End(); +} + +#ifdef _WIN32 +int __stdcall WinMain(void* hInstance, void* hPrevInstance, char* pCmdLine, + int nCmdShow) { +#else +int main() { +#endif + gui::CreateContext(); + glass::CreateContext(); + + gui::AddIcon(ov::GetResource_ov_16_png()); + gui::AddIcon(ov::GetResource_ov_32_png()); + gui::AddIcon(ov::GetResource_ov_48_png()); + gui::AddIcon(ov::GetResource_ov_64_png()); + gui::AddIcon(ov::GetResource_ov_128_png()); + gui::AddIcon(ov::GetResource_ov_256_png()); + gui::AddIcon(ov::GetResource_ov_512_png()); + + gui::ConfigurePlatformSaveFile("outlineviewer.ini"); + gui::AddInit(NtInitialize); + + gui::AddLateExecute(DisplayGui); + + gui::Initialize("OutlineViewer - DISCONNECTED", 1024, 768); + gui::Main(); + + gModel.reset(); + gSettings.reset(); + + glass::DestroyContext(); + gui::DestroyContext(); +} diff --git a/outlineviewer/src/main/native/mac/ov.icns b/outlineviewer/src/main/native/mac/ov.icns new file mode 100644 index 0000000000000000000000000000000000000000..b96e85928b5ea1a7440859b8899c42fc6a05a7b7 GIT binary patch literal 92922 zcmb@sWmFtb^esAr4mP+ugg}ts5`w!!g1fuB!{81xZB`vAp~~~5(am;{O)_} z{oh*eeY_v4tE;=Z>vZ+$UHj}l)}{`w0HDo-wJAFX0Dv|brJ^K_fl7i3001y#WhB17 z)gu2D5YpSW!rFQLtpd7!l@*mx6P_c^HiR&fEh9H0;0C>WO9X@1LoHKl67{ur0vStgQ(0M`OTaywaxzdNSIEj^F zOn)2ETm`ZP@Nfm9@hSBMJ2?t8aHmdn!}|B!Wl;h&Q<;tcL}y{)uZTQtHmKol;bqq@ z(e>K=B@B(!c(8wgSE(c!CCvg9E19$9S`iLEVlCZi>bT77CGN7~4oKJPt|*(Iip1Z$ z*j2ZgU_)kW!Zav>#T>c z9rg{#-ZbMA)ZExsQYWYc^~-nHG7IER+9EKr4#=}mabx+btaNanS6Y}IkFTb(;njJYGG&?# z$Mz>c(R9&^{0Dnzs3L~DEs``F;&P*Pehce0gPXWMN*El4P5F(A?>nhUd;k0LUO%Tk z+O)?VUlq6OzR1r2IX`4C78O*!^S`)vaDD5b0-UhaY0y{ga%AkJ;uM@8DBPkpRcODC zhd(n6y3!*HDMTnT2$kWU5?(X@-_7^;yeZR&IPe6A8-#dI-?=ecX-+9i($FP*OaZIJhwSghBIEi$f{P^Px( zh3fCu;rLSQtOCTj;}2@Rl<5~vXZnvim#l@sR!$Or@m#S&T`R=_YLR$>@ zGLtg6_mTweXnnK7<}wUhD)6!y-yJ)V8UB=9TwBdeAexW~d6(ZoM}LE2(#|H~J=VI6 zl~%Xc58}K2l7QJnQ;IuGacdfmE^HD0ud4h0voZ}II`qRYvPqCn2%1=R5Ul#{@5Y7g zsQ7}{-*8LbQ%?WDyT?({M~8F^KFV>YU*2}0z|6sT9X{*$s^#3L_kpWpO-C2{d4DO+ zsAqUF)HAqr3&IISow#JIg5!Y9Zg8?6KDMBtuhdX2e%jU0ftL#vyWS7z+Jtef<&oY;Zv#M`m<)Oj4^Gv^fr}^5 z#>41AE7ax|j2*ww>18SX-yAY4Zwp8dUs+$S(|3RM?h--6dc06ti>*ZOH7b)aeYfPBq7H& zr{4pnpFy{ST8K9QPtz++!Mgf&4@%?jMb&49^}kroC_j-$MF=AH4bSThV#6#qxqS|v zj2jmA``=4trv0=G__nf1Cc2a&;7*LAEOf%^yyd%69edgB?%T4U3Qfn7py= z_1!zQHkl=4{|i}-0bMs_C;FOo&M2&HWTxkJM56O+Cj>{l<*k$^&ICRO-mp^@2qFO_Zz(TZH734 zr3_m?iRYk5y+g_LA=*cawJh@9HIG#eI&;w8(QaZt1rP~i^Z!FuvNWfI+#+Vv{xF{4 z2Mk?$-8kIs?*evGudC~a%)pTI?GVGc4`RakBPMbZUL~g}O|J7r4jq#Cw<(-6VJCu!uESbId-%g_18Lxg( z3o8pxg6piKA(immt;Z*dty2L6{#>Im?gw_qCnZ)Un-RGGSoA(6MHik73hsof>h4#1 z;{0TRZXQC+JEnNHtR4yO;5hnFw}^qrWotWE-mL;yt$T~K4X#^zg(;^(n>QOd2alcf zGu?+n9fw?1HWJ5^&vkd?^RKOFrlRC(_{O3Bv04t)SWp7Z zb(75L@*kk+6Al`bG-;ZO!OtZHP8a#Tk>Ei0)c@l(tw*xHnh z7XToP{Qn{IkhgA-|4rumg^b7F8vjSgJmmjH=KmW3AQO+^2>=ir{a&{oNtS&sixK?a*sRur*U%v+0 z2R^&6^k3wU2T}Gc3jw1j#kC+#D1B<7Uqg99X+e(Ec9LG_>$YPZ=UJ|sqirM!B4N~C zI9DX0D_Fq<(Xjwa_&y(oz7J@3KSf8GL{b^3yjTr1X{y9P=5*TrCy3iVp=V;)+ZvoX z%AK8X=uVVw-^BBsDN{Ck=w@6}Bn*I11CVfRo-oaX^fwd| z>)CH<$U^~0?{s7TvHk#ta6+*!e8Tg@= zX|L1tccNMebt&>DUG42Q36KwzJSMR@jUI6Y8xRAk42x;O)^hW1V7#fUsRJqmHrgmJ z{wk^D)Wn)-%;NvS;TISAoquk)dko{0lVx+FF>^fNHpnm@M2RNwhhmRVX!PvF?VI;% zICEHid^t9ts|QK1(4I2GzgV0;{ADWryl?u)i~{ta4X+3qfGc1?TUPY$BZq zNabg9fXNIK2hn9t$Y2ZMgUdCgk}{AAqCsXPxTt{c)exsJK%hc{3NKwzf`W`CM}zq< z&O+`Zd_Y&;YWp8z_Fg3&%zgiKkJE2Nsf4>F;21Xsliy5$yJn<QWWp(!Dblp4RbEJyG*ax~^!BsMCt(Zjz2r!w7&oDZGHwFw~x49PjrH zf5(5v!Fc*+TG%GB_LEJ{rar4rK;_g}UTVmszU+;I#)Phn|JU zVo#09rIMI;AB-D+0yTn#<3V}*ek{_MNyr%2zDk3O1vjBTVbvonA@)Wz-t;SY?n4nr3&k~xr|m0!4rmGQ$_wpzbel+mc_XGNS?&+xM5TT7wo9P3Br1{v&OAE8 zg*a}R*Q-B)Cmy+mE#Xg*ZVC^Q6`>y>IC{8IL%c|(UCKV#MN)6?K9i~Z_A?xI)vh&Z zX}GxMHuJwwb`f=~iT)V+jr$$eibDIIcoIYay$01p<0Li)+&d2z-kQ|hceV@ZI9xJU z()bU8b~+h(leu^%4e#;m+HqWz>cQo14=Zz&{;o;aWc%qwePGDCVOWWf*}5J(dgkrl zPGQo+Yb$Av&vLhI_0Vw(%`h#=sJxGnZ2W@ReE+J1Z3Gfc;wA;{9;5ylo0p;V?UZq4 zD9nNn#*esWZ)Z8+{Ls)Du+4+N2zi~_EI+P1CNWzWZ}(KuKpoNz2Ht}~hWZ+8o2GL% zzvtgGu}p>*Gi=mQ zi}_Gu?fZ%RGAii4K7Z7A5$afg`-h{LAp+7Z*pIzNIFUQGSIP^nbmTPj&o-PN_vkqt zv!ae~@4mGd-U;l5g$Q{}a-n(3aDM$+d#lM(iVt9E#oc10cz?NR!}7!+oDn=}2fH2tQX5dG-NK5hG2` z7pa{@)yiE|Kl^*AP;Ro}T>Eq>J`nv;b$Pp@%8^Q;Ze8Fw{h9DH?fwvXp%xaG<%%d7 zPLzlt^XN^}4Y5VoLs#?TOU95cwm9km&ZMpj__kB1;5K=)$3`v6w05$k4^tUoWK`U8 zfj3qU6v6LSYl;xDiSs8E3eR{QxU-G9mX@FPaNIO1pD3g zsjXsdi_)-m9-?WSnEbAZrFq@`*Vnxv2HS?RZ;LApYZ1CjiBsucS=J0sa1KLKzd}3l zzed+dBXk8~Gt2}fd?e#bCd%%y1`8qT`lI&Hv5lGxR%IpNEI%B2Bv6ml4md%_mHKuT zKxNXD|72>}qFF=iMaq%8K^y}Et7~aSuY8MehJ6LOeL9WeLTAn5m{W<(V2=G*4Gbwh za9`=^Iw`1~@crnskFZZS!5#b1{0@3R(v&qB`6Q3-NwLkt@G?%-tOQDm0Ae~vNy25r zd6*CxinwUUb`@`u5k1vaQO6C7mb9q=2~IF>zB_l6?=!16?x_Pc-Jp#5HbHI< z>*Ia!xg>gDX-E7OM$q+=b>Z|uE45s2uixRb*^$1j+CbDtTe!ns0!Ko>Td)`?8hT=I za@Btr$R6(}4JRiSpz+yxX3u!AG*_5z@O*wA6)+TlTcD88yIZ;t8AgezlCT!QF+{>{ zbC`2;K2y||BSk<@4Vr^ZjP9X-+?Hd%3L5>KLioAor#j#&fPeov`^%2i9cGr@aO1_C zha=%?t7mp(JvhcUkcD4PPrW>E-jv@7uJ+Khp)}UAV3Y0P)&#Fs{|6I3ppRai*ZqoN z2=z7kNadC0Oz(j0K(?og@-ZMY?Jvi000X@Kaw9yqIgkf_4yY_RF~)?3yqNufnt9u# z@^}C8yAFzg^G`JZ*Cr=u_sAS|0H#xQQB#3ZCmZ%Bv*_P-HSFJ#Gsjr`jvQeeW1-5I z6U#Ks2qoHAVF_y$AtIl}-_a&K$9*WVen=S58!k4!aw0sALfOOT-9s3USg}m6KGwc5 zD2c#d2P-IO2^zsB!sB6Sf zgPc4|#Z!bzM~FVwsS+*Zm|X9`SIq>k85UsPKX)-4uHE(ADo_V?s_^YR5dQ3ZC2S1q zLfrBRmB&89dI&mEr7Hj&#h$@KQGPV?p-bcZ6w-zlJCnS7)Q)}%Y?IrepH74C%JVDG z!E#gV7Qjo!vY<`b9DpD@5(@Z@E`=c57+>TFe1yV?H5=?QAPcVCFPM-m*y~GaytnfL ztKP{ip4xZ96ZkJ3d9i^Av`~_UvA@N-SzOmH;|Zm#-m=(2>T#h`U0x3A--5PcOOOqy z_PpU&q}nNs5Dr^OCqk9)+4+AMKBigw>k0`s;nz$P%{HbtkI>}4&s@RFS8Pb-p|vKe z=@oOfko-1FHjTzjHzX90zZg~EOUJ6eDa`UsI%luTBUa2o74mFFjuG=U2wlqTlG1S3 zRCBX%2vG$oGq75O2vs$XB^NvYRyg+Tk%x54s@V820QtPey4ZPOmOuUvP;8SC5!$0! zG>A(a_zbA1I$Wcc2lWjri{*qgPFVTI!gC;gBI1C@Nm3>rOdMR_{GxvbbyXp4BZ>wfy>h^BoJ=rQZvW2UsegG6SIQ;a^Cc3~ zy;Wq*Q2XZCGFxRIe^5U71^#_^;FoNqfUp)N8^0>}uaUHLweW<|79Q1b3>ZH5T5b=y zsRP+LitIlH1@upLpZz|+Xv1fP=k#TdVHGt5&_aF*+>8`Pd&W8O{qk_xv?)*;Qi6K@ z{52NFr^y5$k+gN^$GezeSzF_4_3ofsfBnRhbAv9m^67|WXjR$r*#{D4Xle|7wC{2Y zy2FL@{;lnnFgYK&boNsNpmxO*YTLlHivHTQaL&@bQw#f>vqGeQGEqYJfWCG&Rzdn+ zLgLbPx9BuqAB!WeL*s2>i-9Jd**J&oUl*xB$ZKbs%0C)?`7XoY9rWAZx zX6%;i1jl$uS?w|oVY~rTqZz(f)0dc$M^9F;!_7C+JBV)0(x4Kp55cdm=_IMT&>hLT zhB&n`4ueJptqQ{e1xAuzhO>{QL1laRJpSdCJ8IeLy_C?1ORbQN(Fh{h#`7suu(Jd?b+4mmm5g!qb{qZoj_fww^Eg7k`7?T(2 z9)^Yc8r~Zv;4QK47r>bLdcjXf)>SDwbOykAYS$Z;bGL-PZG za$Kh_QSc~?4&(laTRH>14>njOCz#8AFFBE-R=^-@SJ0(Z2^Arlq z{s7wZOop4mW+RlSyIpt)ZXw2A1^bZSKHAQ9EC4hY3Jt3P&GrP;&$f~Qno$Y z9hs^V*~{Mgh0Slv4yo*72Hp>VrG*d7Je8l;*8lG6O<*f#&N<(2AM0EdfOZpPj0XT{ zhRd&k5@JC$F~6K9@gS1u_PZ~{K@M|QMBHV`FYai@u5h6}4#)NU(g$u+j4X%&*p?LB z4__oel=K}^z%un+@(>sw#VA2-;`VQ+8?<#l-A{!;)2X1o`~wv3uFGqo+J_!Zv3p%f zPk>r;C7y^e63OpSn0Dvfl+g%msNq!gaF&(8o$1AC*z7?ff(cOhdaQzN?D?-s>gmIA z#^daF)ci&`IgyyAN^GGBr|=iStP6Kc#Nn~Gi+Wbt?Z)IaoTT9_-@uZ&fcfj8@M1xT z`xgfQfx=E^ds4zy;4O=Ioc9qq+Jiva)BV48S_kV7%VJEg8}en-XS1?GHWBfa(vXwz zf&%iz?lt&`*jpo^7Q0Nzv)B0XQ03v(`ad*16Z?rHu2T$DEjufLXCWKIr$k8T znpY|*xQM{BA~}P!r=Q}2CGurx;a?rLl`LET9t_JDj$YZ=q4dP+8;htL zkQEI^6tD$q4Cb|HqCfNI!&s6SVeqlhhV}AHrT8M&Pkojotl{cTEbGDulAvu*G_W&8 zI4t6X3h(Li9Bpf%P_G@p@#X71p(qf%RLsqY2mOefJ%zcIRv4RxZ6VKiynyvm&0)2- zN+KdvB%Op5N-5>tBl(TauR^Gr_J@c8@JH>JEKsQO95%~u0xMI{;0tP3V48|joPsyv zj8Swf184)8|22wgr%}25D>`Oogf=SNnWF*F>yrhLvX8#|1#SzMV8&Y|SL7_sInJ(j z;!WnM7n_0)RYp6;0JV%3@X72kPn{-*m#enmx`T5x+STk>~m zPGl*LOw{MMxF(+p1+hOFyVagFK=zl|8U5MTeA0+{asmFUW&^F6&;f=hPAke*vg{ue z~RVSGBQ{O>(Bq}2$U%J1Uqd)8Hx7;Uea5jVfjKEiq= zQs4_zqr|tF_l3yCaPY6BVN8c?(o;V*+Vxpny`37O{vm$X0K3!-J%5|KF9s6fbXY?6 zk5Xi0>VPM-^n0t>3T(&uB{;$z-F`EfIz+4&E-b38g+-blIrj;XFMOet3rZeBf6kn+ zzvuVNY;jzTROU!r!`ybLpZMSXsyz#ZwFLd!zWm^qlE;gHjoS$5s}U0*hw7Dmci@cA zR(A}7(enIVoXXumvZ=-Dqn}=50lCSl^K>ExQ-;Di%y9_{(li113sg@o5{WR@FLa|` zYnU`>)K41317pKFzT*2G+P7`XYHH={fx)zpgH9 zM`>k=mNm}G@jnm#2E;8M5qLSq86$t2yIFJpMP2*Gq05qa0hJi>&~s4{pB_Cngk2?0 zfbFp}75N6MlhYKpH$!utTj5ivew{_4n9~^Nf`Zt8me-EMD-QnvUq82^P&O=Lp+8Ux z!$jcqfv)IwOweFUl1eA0170j4?NL3@Xgk!fc6{7pp|K_8$Z7*Yc& z)J;uoO6VJ%i5Oz2FyR*ElE4W!H4pQPtJqH}M5q*;Lz~L@Um-yz&F6$^)6`=?qOf>G zxNc90%i}#<4M5()WeUZtU#A}!N^|!+>vl3lNlOdZysCf8SOQ$xC{JE5{33(Z^}6Q7 zusLu_>kFp7vqZ}qt~Z-z*lo+o{2e#zao$}s$?+82ZWR_L4PbP-)OXKO2|i}W`L?1) z@y-<>_!_x!kQL}Pzqdk{O!3ZA9vF!#)W57jcCj8ASk;ObEs`fT?ud!J#B>9>f5Gy3 zhL=Pv2=A}lKfx+8VOe;EM`a~WeTVPz>p%7Te{^_uP5^-2 z;=k7afutHB;D2=Z|3&}+ERRTJ0RXne|67OmM@iR{Y92zV##&>-H2o(y}A&c>%+2+NPomoTp ziqs?Zgt$$^5E4sDr8h*5tOab{(d}VD?4^txGYs|uZi9MbwNSh~X@!82-|*10dNZ6L z_z)CTz7KiuSWhlL41&DyO|%2R1s}2i_7vMYDhSpE--xv+!L@LQ{onw$9YE--txOMc z;{Fv!|D%WpdkVCYBk_h_uL|&>YWum02_ettA&)wDtG4X$WEyT0vjB7?3IFd5rUksZ zWz83Prhanwx)#`uh0`{pFNMJP2YkK#MaYh|fss4rqawjDWoh%5U_PB*xmlj=?<#Pw zMO607i36x29q%vGj6knIC>v$nkfB0uL2_{6b9CaB)Xf_CV|`kc3gD zq;)y0S#4->R!SP6crlF^vrcRla35XG&NI3zc}S&gn4q!s?Jf0d290FTt0R9)_0>Fn#!PkyBRb(WiXc(7|cX z_;dvxqYiEgYD*`mYTqg*H!S4>-;-@(iwG${D5Cnj!hcyBaa_Wx9)wa z*Sgz2PAJp1=}-|K89~jb2S#4)9teRL=3~MpnnvR3vlGf@vIm>f&?LVY>e8UV0p!7nR!4w8w zz2KMKpbqe_RVe!h_`9|wa%kVV>JT#HywnR2@RJq=d6b&V(+MX4aH*dV(43TdX}0<(r#C(Y@i`_;=;UCnIW@>=6*OUJmA&}GX{dpf+|M01`p4PDx2e*A;)Rky8@IqZ^onjrJ7qM&fC?rH+P@Fs4z$`EfPej_(=#)0F36k z_AbOY;mi#h>}2gOAkfgmjD7)v-YUxxBkYc9%5OO;TKoVBH{mgZN=>rPWgpfk;SvB3 zqD)1%l;Ji%r4KaM7(v1x?mV&7&rgmc5m$`P625y&n@d=f&%PoO#qIe?fvr)v6ij0H_!Z?T!e`qnr9URt~ffJ@{0*b zGciI`39gP_&&&1=P#&&uH=Rt-cu_}HkuubXqDX9fo9%bJ_z=V`dPW1ur%t$CzV|_7 ze!rd!iy_t`sB;h(1^5RaO7$q2v_=>RO1FdeQL0~^B`lBNSq(S9#!>;^q*hEwVT;F8 zk%S4bP^DT!<@4&I;@;ttvQ$(iT2~OrGk;osRdsUk^?njHUrpy?$44(L(A2#OXVopS z&Hk9sY5sw=Zf@Dhyn68s=Q%Z*bFq)IfDB z6yqt^L$jr6G`^7}O-#!<$`Mk+stvSSc3Ltkx)lab(_57Oy~vunn}Zo%e^Vk=m(B|e zIWprfNoWC*Hz-uYVZAUg_)t1W>%3Vgm0E=9`&;XPBpnn{hhKIj7Uqs|v2g*S%A;>D z)>k`g*SZ5@v!gF$~D;--2uuw>Uo$#lde^e#c z2BtSP3R!!GsI~8kcg&bM*Of+p?_CQ%WfgKMivHnU7`2~3#hJj+mBxL zpU;AXS=L|F#DDq9I9L%nepR*@P~?*q`HcG6IS{gRDyleXFJ&tK!eV4h^q5egL{x-4 z5GM*h7Duh_%n;SRI%&q?c&+#$j$Q-c)4Zci^+-i7d+SnX>EK@;u`&D0{I~BFS;~-; zv8k%G^1$Liq*UL~?*jaxvgI)fa~S*jOs3vbF>K=PooGm$8Db>;t?Kaz(wLFZdgAVP ziu?&$+3J@GPG1h=96g(Fxf;lfPG|R5YhpLn+UsdFZN&(7MdIg?b4nHY1VDu4QEegz z;cVe}a}vCCRz1xP=2LS2h?RQs#LdjqRV)Q~7#U~Qa^rvvb-$!~ojl7ot-5npzh?rd zHkW09wn|wYGctc`t4bdDts0!j#)Kb}CJhm+f_Le>z5u$s3J?hBx9cub4L{j`am<{0 zjbRNu((Aw}d=5oB_+n4-yGhQ0^~SD`?KNkTJU7e`Z=-$gZ^$w~|3;?UvF|0g)wb=1 zyzB-aYreLkU-ax1W9BaV$SreLn$Xw5ebIN$r*~*5rLMbt9Q{Wn7d9=#Zh#2msip{&pAg5pw)~HRzUl_w4pTRVyY{SReP&Ak?|><`WUm0r*;%6`pnC3|PDI(lz-1D%b()^#fHrEA0Zn!7mfr$<{@sQZ~u z;go@RjvQAnMWWkMgQHw@303FWdM8V?PrAw7`pEBnP`~M0pWrmoo7B;2=yB^;o{CWb z%~}dGd#qQK$2SLNkw3(k3!(OGpbyC8cXEFB!JZ2$57@y$$-{@pIG~1ytp|cda+&}0 zb+hQ>f!X1RmCtc&Bz#D9CKM@9$W8q2<=mN`hUs_ZVo&Vvk;XbVE`UB|`%B+}0$G}w=Mr}{J>mn%_^2M;)H zzl7sM&_AvW0JlnSv?68XL^?KiO{Td&cTxUhH&sz2 z&e=;Kr9~b@Z#_T4wbD)cNihvx!+NprSdQD{d+8`{<9z)7ZKD?RuB^S#k46dWMv@t! z-IMAB6Z#obQjChwPzSXqIY!VrXy$mdTdzb@LVSq22zRI~sBTp=bsAgYkMtk4s{#8s z@}WL=jMm)Mb%r4vSZ>-P3E9H>k~b1XUSqPM2ZaIrx5;z3yO~GL?eD4aLS=Fo;FIT; z}rA3FlC%|C>)ID9nol4+@gfdJ)*ONDL#)#IrwTD z5wiIoDW@`eYrGNRE}koHLZ4UvAdw}5zKV-?GfDziTz$^;-?6bv(4&Zv;~62GDeG# ziIVGts2H+;7Q$m|0D^TESkg_WTX)S;1efjasm8DMPBQ9yS`|C&b+>vKY@rNmd~SN) zxyKGQPLpHP>3joa(Y}UE!~9UF+xzNO{GBSmgpU4KlJFPj{9k`&hd&~t1rGeu5RLG+ zh*u!mV;4qxxyPXFU+$yc8B{=A=1(#l8^*FjWr7&pztZM!&}-qW8aui>*IBwo+^!1) zgUAfUeN+vIKKCq^nI9q`x?i3AsY zl~2O4Y1vR=B<&q@^I9P0TU2Q?Zw&xVn>8+h&#U&xgJRaw_c1BkfVTZc50oo@xcsd^ zcbatN)IV_HJ1-(Nzyq+=xMl_xI`8-LZa*=qH&(L~aGySCeLKe79F!Inr>u?p>D zEIES+@W1t|d)sr&6Cxtd`^(_t%?W|gGbdaAde3c$%wnrlC$D3{s}n(C zN)tU3M2uxmoZly~p0#Tpd8Rw$2Y7Y7D*$eHBk~(i1VT-6Ed&=N2w4n4qPXuxh0?;4 z6ZGKTBY$QtlJKa%Z8<*dk2S)U{uhED2p=vD$1p-9H=6x*Al;v@IO&bmiTvKvBR>|u z^sHI%i8<_)WWZ*9B4m#X0%Lo=;e`23ia8<`Bx+DZ#z+ml^ikZR(McCy=E@`$%|CA{ye7(UG~sX@O7;gGa} zvqjp*+TX6rp|HZ^24ke>EPTi`3uHZyS>~$gHiDD|JE#O;;0}Gi%EoNMK5Os)=j7sF zKi0>-)Q-V3=}#JffVae>iDQ`70lHz5$A-L8Ot5+(tY_5+6RS3%r+__OUV_KbakM&q=RoUS zJ$OpAi}fAfkX|c*3DVbzeJ*7fBaHqmiP(7OuS-{n;`A?2VV+GTQQ%5)+m*R6z4QF6Et97iecKCpoJ1;f zo87C=0>F=P1XYC1boLPweuUB&C?Da#bMkk~ilQ5}<>2G`FBE#R^@suux5T7#lQT!e zj@vaOzDqo8{(>Jb-OXz^ECWi;9>AAlre;%WwFwz`-TZ03Vo|984ZwDrAEt;8KX3b= z-F7>d-X=5OUs^#{w@3D+>P0bvlIa~fTh)C^fVFK}niIG#v_Fj~hIl8?k?kh)6{kSa zuLv1}4&&pv=h9&STy%+8egcJ*pPMs}l;CAqU@I8I-rBTl@G14@ul3juKx+q__ccS@ zU7^1QbKP3Ss?+lFKwlWPcN%fIj@F z&^&$bd7!fH7RFHy_i%8boOmZz!1kWFflTSv3rW`F>T#_UjHd*#0|BL4SDMn{NLLr1 z+k{35`BYS?gWyuwhS$Jw!GWuzQP1x5glFBLYuwBDFAvdEq)!$?# zBvZXjK4f!?R)$;IL?68jNx{rD5y9nOfuIT`1IBk zZAoeR`D3#g;z_Z%hcKc^`jH0mtx!$#yrak_zlfMtV?KtrfJfSaUVHlkBeKchg1925TXjNo`O+f;n;Oax-oYa_{#? z7A7wcGKOdY?5m~yLO;XuWV9l@s}@8e%$todjA?v&3RI9hg8%z0pbc3OQb63zEzb`G z+|TH{z3DVzCHophb;WDSZvIBfWIAh1Pbor}=R^+$U!4{;%Ou)K8uM@SkmR}#!bo)d zH{M%)R2IBPCBo{`O`vd?jxdbmISR|sJJANkuedGakiECkongTU_+BX|5tqoVzO&0 z+l}OL8i0)RG!MO0O64vn^Y_AT)HHn!5FWjKj>=y_ms;DDGhL(_*A4y1$wa937H-~V z_n=A+x3M}99oHS4we0Eh_&3f={|Sw$T~V>~r|QU#-DtG>MDdx3KcYY##dH<4j8%_* zV2n3o=?vv)nSP5JTaDO!Uh5cm79?ktqi5h&mGbN#g+)^@L;jGhFaI{S3x%WMS3@Z> zCS@b=z2gthjBaHwcaTX~a>Ra+9naBym(aLS1z4?n5b_+nNiDRmRZftTdEFfS`g5V@ zRoYAuBw34gj8GBEzDV-h{vBIq`$W1x^H4rd6Y<9Z2B?KbU_%|8(}>~K%W%O+6gVcs zqIB@AZm}T`MN&oeU!dxh-J|;zzDIqWVKeR0j+uCKDY!S9PWSFe>tN2xLp*xcRK$Ju#mP3=d_IMO*R$($6|LnzhHh$AVpRt zvq2|T*$>N}W5)pby(Z&BCf`e(DVKD&EKdC6Cq;RYVGbn^w(fNb(r{5ydYmQcm5W7K z?~jC@{^eglANfGkOUUo7^+YK1!nhsrsis&+N^}D6HNht&e@ENWI#gSPuqu>E zSd>D4pN_9L^HWhzo^WdX@1&*cu*fWlZ3d*6@JC#mN_753Rokv%Lv*!&kGL+lX2QOY z^=7K1SVC7X5pdU}z=AVTd4do#uHPNOj%frOaLbFk&%a6i8Qi=qC7SO3MyczYB+l&d zQmXq}dgM+E*XK6>dLd6NF+;ZOX?QrjVpAbjI*%j%x=q_3znER4LFlHWsL$eiAX z3Jv~rh1#bq;6r*&V6VXUFL;z85KMznT1pTs`PTza1ijynzh4-n>pQ==l7gxE|8+S)OLq z_FK@Ad?cp?sdwgGnA-{i&XI!Dn!uUt?sjGi^w6~cZ2=oK^>?t1%hhheoV8a>5uo3O zgT?GySgYmarhKN1rhLWzfzYGSlRnTZ>}hpYPRJYMg7e$nIzu`vNik#uu=KRU;u;<> z$fUr4Sja3|*VNU(u{4iQY^eIlOhnSIPIEDMELn2KiUAl%S^=o@S=J{gi^UvG2B^wz zmA3yeA`&7JJb#~kMO)c@B9d$WOc2l~4OTcdmgU1P3L+5|dZn)Wdtr5lymamSNE#FI zPRp&Db?|*~0|Ms6fu~s>4lvfbCC8^Jo6M7PA0;q*T{Nd#x;U zMTW~p%2PJxVWr;3MV@!B!3OXIFAhEbA~Tx|w(vf@iX`507zQf7ueW?>8cvZ@{!lU#{_UF6W?$|me4pSr248@Wf?zvs8 z;X}B2v#Jd%Z<}#=;FC9vObyXTt=*_NXSkqDN_H*!KG#(wBtIWXZYJ6&ukoY&Hw}kU zBwTF$708$X$5Ibj*S6Q|zh?5PP$Ep79rh17On>Bid)H+q-L6qnI2&)Bai81^>!PPY zZxc{L_Yi-J;8E-#LRRXl09BuEg$x#SEx#Ptzs1Js@}#)zB>Cdp^Ui@;ng8fuBx5wx z94CncY^aN1GgdiqtGGy|3^~k~0Q_H-v;;!`;{>|6iNmr27zG%KX098KEf=~M&{Vfyh2;j!!=mFs@|abC=!T~NvoGe;tyE1N z&e<)CeTFQn4&<@cqU00al87!xvPuG-2oNjAgs|X?bSBp^U4wVFMm3`nt+y93CK5)u_Obm=Ii1)TA6+sm*tM<#wl~XmvLao!q{IrCYBJR4 z8l>|cr_K%p=OCWXL#FUA2X$Bs`V_^qjlH0~dJ&XwO@E9dd(`6juzt@dapGG7=}%(I z`*DZV>`0Tuad-<*`vck!=j5RrgOB*C;)e#6VTlmAs8Q`=pl;2MIC^) zWJw!f)R2C*v~29Pvnw{M7kucEs~pG3`Mwo4BEJ&wPi@$M(dom;Am7Q(nvWH$3~!H` z3P7U8%@?2n01Fc`1l=>ZP|@VbJGyDQ~|)ftP7T>KAqgcM(qZTqS!heqYkoDxNu4N_X>Qu$pAR|^5xB}odvF< zfv>dsXJ-7kv)$|*{{15s!@O!$u^7^B|D)?s77C*T&~0)BrXkGN?dkVL^=v1ZzKosS=B@3hhcis$&#U&Oj}?2F>oq$xf%Z(y)F`jIp) z^j~mMf_nY=a&JTqfJCFZDS6>QK-@ok16hCxB6Q;P1S3yLH>kox5o(*#iM$TdCs=UB z519)U4EzemkHk!8H`uqKH)yTdyQ*8kP4&jX4Bz<#l2oi6{op&rVjfJ{45^k4VZO^o zXuZQdT3 z!^!j7%)T^nI(nz`W%hF3BT$!ZLInKof3f$U@o;@__$a(*jNWSyQKBY#Nwi?J5WV*v zH3$-2n9)TSL9`$wdXE}1Ize7>;0{cB1|En^epH*1SEkD=4d1lDavB=sXh%&gwCBnqfwn@ zb0rW%El3lC3vS_RLj1+8k7UAfTx=QZ(_0-MHg7s(KZeFMun`OGd?zm8QkcG2>qt2v zQesN@o^7rw%Dq9B#Z)?xXFwxT1Ff>s2sSLdd-W1~wn^i8I~zi93ddl@i1MNQJ+W5O zkq6`Ul_f7;`2G%2}d(WtI(jT9_^3RhEG~QU1Wv;?Mu`RYY z%1UNm=(&_09Siy_9r9pE)Dtknh@YhnCnyk-_|q;O2Z)==Px( zeze%Rdp{qwgo49Gr-t*pWXo`ZwZwvLR&m@E3V#}O&F@#P9Zew(55cW^xdXMhjC5eG z4CD4(tj7B*ky(gG(Flu*d88G#)e<3C8HLu2ULEPk5%Qp4+(NG>j;ipG@%7ZRv>HCJ zN_^raqjg*))sh^+1XfzeInsLcp}1TKSYcof_^@Keq9}Dea-i%p-327i>4zO@aDl zGeV0N3G@nl52LLpvb@Fj8ZmJSuQ;OndS~Bs$Ce@`5-dY;g7J3emMKpSUf|?w)-DZ( zz!8@G2XXhFl2gg*PGtmZeP94WM~6ul)+ffP>x5YY((FTZEvpiMJsnLNQg-## ze7K5VWNvUtTwU-FRov@W?EVC5h*T+dv%S;2TJcJAnHgaY(NV2N99703O!nk-)W{b8i=VXlTuO1v+v z(m zR+qFpDNN9Q)jtqsfBmXJnl{lq<^mV&Q3cJv+H7E=@I<2fZst^+doCaUr38t^}-^XNmqYdJbL1LT|!UxS>=4K>GZJr^j3l$zrwq9 z^vTDUJ(odl(bn@J@wZBr7OwJW`MPc1?+kD;Uk)!8?di`{iC}ntx5uak@pu>yc?mK? zmTPxcF4?N@E#rOu)-h|z^Duu6W}UYn!E@@kwXth@Yx}aYUhiCE#QQPJr4~i`=XIZP z?K&CWw@MA3Jm(bOEOn93b=tYn31BRl z0|*m;BB|j+Y09EqxrS#5V(B%C&k+-_Ye(&rL__UhSihXM!@mrhuZVCOT zVOT&i7Kzbwv%nH?yUJnlOZvy{R&1aY*ed&w|4vWh9YVy%Dor;FS<>QwdW;?|T9US9&`k+C$C=(52>%MC5R&*vTJH)k~>q+e`8&D|9TsC z2L!=(FOeZR+sxO#G*zWTk(#Ki!Sz zH-ipL@T{lUA@Pg5Z;ODD3;s$Cg-_^%*wI6S+vT*m^OB$cpn}U#+;Ts4Jb8kf?t-PL zfl45*{;+jU8qV4GHNLTji0I$8b66TZB>3Gy=8?zx?{u- zru9y=el8axN$#c?Tpfk_5VD-4^l5)77IBLP&ByYaVNkrvVOP`OeF;nmMbZTnr9sL7 ze{q(1nYV5BU2f2cB=@5_3qbi7bJdQ|K+bU4FRL$bnL)hH97^pSR;rTo+B1t~VsiES zLj-MM_bie}iujj+AvT8E)+B%X%Khk+-i|q(W3&i#K?{jRdp_Qj_KTo}^zT(Z+e zJM57G{C12rBInh=--1W?mlj^X%fx61oJoU8M@-`W$&=+sGm=XHU-tzi)~i|A@8C+s z{kypZ{8mGhx8qg3?RI;|kRu6Xw_Xfdy3bC}yeY|Jr$0tNg`b&pY0@}S7xiV+-m_p7 z0cS$u1Qa0^FsQ2kdS6eCkdH-GO51T@dUozj3R%$RGMZ4EeStHBsX#^+*8rd}1v-z3 zK4G6)n+U|aoV0$35NcwImrnH$*Iis0R=WCHWOT>!@g7P8JHrS98zHd$q>Afx-)3QW zB*|Qq&5Ej;o)BT!eD9ZKf9`4F##AT4)G5Bcj88i@ zhZRSJwb%iXB;dCp5KIn72_*cby?hkTP=YT*epPk9L$hli580TjCAM1}mp?DOAHeLS z(bLBebp$iw!z=H^TL=gD3Z?O9>7QoEE#H4a;E(uuc%jMuS~!+P3m`5T=^=opq*_UY zxdR;t3g9Y%%xW?ClBxp{>|3?Va!A!Cwbxi+!vJQ3WOLT2Sh+K^Olz3*&b|}C!wszH zAH4*twA^Tf0$Z{rbth<`q9u=Mxq(TOunmtv2p$GTbK(+x` zI_-ks$^6+oqOi&4grjMKs9G^f=nw9=+0;nMuO9cX#efAS9HG7(v;ubDJq4(ESWErL zK<>CP1oj6SJ4cPnd?~M$7>20f1`Y}Sgv=r`A9)T5fC;c3{-JUJ{1gk0gOBmV?yJ5V z9Dh0cJcVW5rwj;r4(Zv8!XiTxP%$sa^l7(S30c0LwcPmi7JTlJwSd7bu#?G0lWF7* z&0Kt6`ecIl>1LF(7oV!a3mKL!gF*c;VJ%`uW_D1;a@!?a^md zP_F*FL(HL|&c=~z9(0YcMP_GI6LjGTZnrZqW;}%VQXE*2p>skFsT*GUv9C)~waR?Q z)Df2fpIw{Bm>D`S)DvN4m!1Ub%zuT+f^?uJxbLBke_r`hk}l+g(ywz|b{*@g$etD7 zEd!zm849i^llPSb0mg|8eXBrtkTVR75o~xudBLe9lf8nWv@u>VQ~BCN+ku%=(OG@N zWdQKe^>+XE0PoxVQjB|NokY&+W8a`*18@I_!9~Tk4?K%R;IG3hR8u%)=hFdR8(*PV4ktKBUhZ?Q2E7+aMsGSaQ&z~4#>p=;w7~yIV$E|$l?3d6 zFF(~)7Q5YwT8aDK6z=J7kDaUQ?4-;chQRCNSTOt(>c;1TNC>NxJsWT0SJ?pl)_ZlI zY`lW^lP{iA1l@UbX8$r_>Rx=Y|$g@Nv3?U;@9x5?_dhd2_y9H1nz3{6Y*F z+Sc2q7;TTORl7Vl4e@1v13ZFW@R-H0HrBXexk~|EzuzeXC~7U|WZMEZ0T!2%~1JBcxa6?S4b5!&fH_*v85r`Di9 z9Z$&6YhJ7-IIU%Xt$P#99e$<>e-Kn|t=|{Aw|)5k|s(zCv` z#d-GSj@wIl?#YOG%lYw$JlPQ36$U35Z%M$^Ux`tvM9Af>Q5;MUr&)@Z+w$RPtAKYM_Xy#k&G4@5 z&Mb-x*O-x@-e3ZPgbHjF7O_d~AGs^C&@T*XQe{%KftQSP?!GmWM=_o~w1w=P)EpVq zNSQb+_I+Q!yFrjoI6s?7b#2Jq|31Av)Wd*zKof+J){EFA#eGv;@zT4Y9^-tTJ@EJ} zv>j0t%#CDa`sJ=&B6-w^(YGTSgd8xm{p^?Jb-h!$SptLV1GP-%h`SM2%`!mv@^!l+RaelrRSG>Q_+aac=5#EU{49% zRE@&Il~yeQ*=8snU7j()Vek_h61)|-(4>?q7d!{~L?5^aTB64)#pDN;5Qf^3aCHU` z=^2&_bs3S})rY8|`@^jnR3>en+qMe_bu(}c1*<)dF>NTgk6bicVi<(1BGpKg^?~6W z?KGT@H%BFRYujnlI2iE4btJUz>qE%o=PjOPlf+unr(;=&Y0)H12HfK>E%U@i%2OWM z>Bk=k;D#*1dFV$wOcOn^$zF%i;7)SIrip`#0qx4}<4Y|ZzDltIZlnU+@yl79{huyo z79J#*m#s|d+F4x*-;yf;w5Po}>*xG<(Lu-2Ts7N$h9c#zx&wE1bBNXv+lM^K5I2rb zNO9)(Sfk2kA3qwZnX)m~$dF-a1E8nUAVB}{{Yjw;QHnSj1FpMU+sAfmpLlUHY`AS| zZ^x~0`5uEcwwL(Vqo&;TLB_(n`}65U!BAF78x=4G_3pl#SNT+yY^t5%9hUz~W(pFN zjs;ClsVOfwXS#~ghUkt=*j!%8i+&Pp9Sn9MneVS^D81hw-`kJ!&Nj{^K)w^hv{a*B zE6Md*ON=Ca*F%427zxH|kujtbhL~=#CACUG4l9MD%2|$!bCOmMQv% z2O-qrWF5b$4MWW>&9xtz8ZK~ty>FKsIv^{*^KJ0opY%a>cRFdc?uZ{r9A9s1xs+O$ z?vdKPKBHFU&uNz9vHdslRtIREjJzA=>23G*Ps&4$o^tNqg-=$zLk6c~l|K;%z^o}u z4uwCrAWUs{pd{1yYExy8Y!bPTct9#gCI9bS>YD5D|FSLw#BNv@H>`^r*2N9$;)Zo` z!@9U(UEHuPZdeyLtcx4g#SQD?hIMhny0~Fo+^{ZgSQj^}iyPL(4eR2Db#cSGxM5w~ zur6*`7dNbn8`i}Q>*9uWal^W}VO`v?E^b&CH>`^r*2N9$;)Zo`!@9U(UEHuPZdeyL ztcx4g#SQD?hIMhny0~Fo+^{ZgSQr04SQjDy0R1Yfqa+s&V8vq^??nNC9ufn&(2f8g z1arX{AR__*5Fr@IiEJ1EVLl1P0H`nkC=0^?6`=qC55ZtfOkp-d0f_1e5`%rh0IXy9 zLI6NK7=!%^00uEaAppU945lfG5CA_C4gs(TgSJ?a0MrDt`5M5II>Ee*0$}$KG3}5L z06B=04=^rZItYRR2-8sn268Tq1R#uH3+qf806-zrQd3_=0oW<2sjYsD3Cwkoq>o zQ^yktn3x!YG8$WX`TF_+;FVbjfNNqXt?lax9=mM<3T*yFN)8m*)Wif7*v!Pl6jYKa z(j*iJPQ?Z+z}xME0YEN93J?xq1nwXK;L(3==$-#b+}qdpB?>_HK|7k7L4gPR`g)sd zYe9j(_4T1XWQ7ESLW1Avss7Mi6au#UzV@`%zWk$BQ54N*B zKRgl!wy<~CmvyXQ!4yVRyl+<;@*nZEXY8?#BA& z*48EfUaf*xYpaV(+w1@53Tu63aS>GS`r6v->({M;;;#Kq+&#$kC9n$0I~%eH{0toe zMO^)_1s;{+^i&X>z0U;xzfWp`0^qFv@1&Mv--&bZ>i-`nwLt$rPHMS+0{{@7)rEsL z0Q{emTC#E9vFoRwZmq^A`%@(M33MiED^StBr6#9R=vLG6G{;)}5X2P&@$QcDGbbvw zf_E$Gk-^CI*jTM;aoE&{q{gMPFT78$(0hZ&gFCyoOLoh3y*e zI9oXv1QP-LL}UaEeeu;5KWZ|>`C&z%$4-5>Y53HaL`-8Dn+*myJvkB64Po#}aor;E^4?C1PfcD@Ya;yhUS1 zy7Ut-KNkspeVzeA4-DyxQ*%H9@p4CVNSrh}PJ+xTW1rNx<^JAsmm+v-#mqrx#DlM( zfy99?_Zc(eH>v}1LELy*(Oct|hv7IhP6|a{j_}^%xbp<^Ipc zs*yklfgRE2k7ZZb$EKAE5F=|G&ye8v7@>}r2}N4|_##=VzTwyvVd0Me z^ODzHz7G+(tkKt89kZl))^l$A_g>VY7cDgKJWAn-GP4-i*YFbUN4XUF@SqN{r(%|Gw^Mia5{v(d3?k_8JzT40@nF^#CG}0 zY+6a6Owqn)iJ(jr;2Rgi_f*W7%BhBf9p;enIl(L=8^{X1Q3|LCz*DSTA`8`~gfF4^ z`>EXgb@Ta5mkwvKXM#xNkm2_g8BPQn*;g%QYCk!ljTSBM+hX2Vvc_`}QFrg(!U4PYd_uU6Ew~wc zj^^Fu%7x~t-NE1eaH3C`V3xLIygwyAJ9Qr+xq;N$aEhoLfF2`KaCWbvp^HDRnxci` zaa(qeie`H`D;%q>`Tj>aHZs0o^EeD~pZ?!g;R&VrVP!NUEtGq6Afot5ZV=eHO@}@P zBVj5O@Fo7!%AS+2Z^(Ux ziJ)$94pcac79Okcsw`9%ukQmR4h1uWf=dzpYPR-(Uy-9F03~ZMVCBL~!mqlj07c`u z!`hfQd+tIC@p5JTS*g%69HX>j&@9!Xa`?M#jKK&Kl(Q zcK9|K3x}{evE81Y?ms7ggI@{A&xfd{eZJb;&Afy|St#e}k%_@Vk`w}2ZKT9$bIJ#z zC)-Y!ior=CY3uZ~{}vo}r)yS^eCn8-3UsJU0-y|#k+0EQ%=ilOKr2QxuV5?xUfjo6 zigh}MgP#}obM8!JJ-Ot|R%@XghnnYaoTb4w+2L5Z79nbyP#QA$k__du5r@Douh;Lt zHrQVz?&+A;ep98u--|KgT9+hdeFGPVWB2J&Q@j~Z(RL!M6@2pq;K4Rjx`!U~l z>pYSm`yGvu5Sk8^4A%mM!BdGnc+w^WUBBWFRyo#Y@@QSH-ggTe?Lg&ri`r3~+P--$ zYGYz0Kk_+N6Do(q0p_qM+~0xIg=d1vomSSRq;ik`@1YQ`mV@Ir4pfGUZw``$%tb73 z=tcVPSD+{YD3W3b{)GIz<<^tI&voSMok17@dc?if0Qcp^_m|2tpRl|9D6#O*INHd8 zxgy#m*!ErIEQZIiYhfv>v<|6(*#rnNAyGdPH6Ep?vs*+eh7998>1}*YF^`ALJkcnB z0>?8`BpRihmd<@%z(@$Ybr<7M(O7$x!M*ovSh4bss>o|V2~aEH?N@=akb^(bMQeMa zr92~eI|mjq^0#v95VbSxP|u$KEg?Xa?_ZEFjFgN6l4jA^^ZzMAJ$gJUK2OzEqbaEK4Nfc=TQp)JNw_rhH1>Z_9ew3){VFwxQG!G zaVTXG0ljx|PQj5CXGf{G1$alaj8&w)uH`Z-aoyZ}1fFWdi6s|##C#WrIS@UWJxX+C zsH09o*HPatEI_}1p>l9GNDK~^d;a@kcKle1v+Z?UbX+`8zo}?w6#iy8ktG)Ak|ZC3 zi5vycXhW-&4%T>}QEOl#2?hseS?Iyqy^g3>vz0ssFJnF{6GIg zYm=AwBT$isT`qBO7?3@dixz8=5Y~vtx%qC}+NeA`Gl$2ao#9VtiG4{(!j`F&ad1J9 zb*~yLu1SVzgia}m7yHGk`7y4}YF$Py6EHC-*#nPMB`7%sd=DWVZAxvO#6tjEPDDMR=es=86&JHV zd(K1hXo0fQ@rW|pgXnr{g>ol+NJf%EJ+Zd1bf&c>sLwA*O{Wh2ovae_M|OUY?3r8h zxxgC%swy>#$?>HpLDdTH4~8$47|6qBoli0R-L#T6`sS&VHYK>W{U-%z zl{vD4K8jjYF|GEn|O2!oWh&E^wwfjuw7foPMuMyM?CYFkeN<{TGqpS&(Yb!%h z29WKeT^;KyF3KBK?;q9i~Tq_NF8@-OUgBY4{@T$6&H95mWmNq*%q{ zZ152X&x57HDr!(W{M=In8{>P!hfpRi7J*nNOni{!Dq0oN@%b?~jVQW#LU>j<4P0Pe zZpFVzZleX!dd|yD(fL?0JFA(6>lL0)bM-OQgThys zAcv_C;7gT4Dk1_iE-pw*EvmToan!Iy!cv%yUW3m?`u?l+q`(UYPYEjTTfdcO+oJ|0 zpPq0<>>;S|%ox}NsrQpd_b#0ug2re6gXtq@ur@R;E@*yk9WPgDd!}|{m`PL(y9Hkt zQZp6S?hf{gs1hEn34#|B9sv{QYe)Pj6sA1zFW+UU0%Z&oVY(rnlP(LYp6*29-jiH5 zhzR;cj(iRKMp3KNQ3>YRauu&T{yG4ONupjaQ=+7d+ zVkMRe99l=wSTz-GU@8eCK7^$-)D{1A&}rkNyk zJT-Jjecvg{l%H~*_HTBgvSzR z8#(f93rUfPbOXnDJnE8j4xd&~1iBNy`|lKsEc34PHYelKx+17LixXKVwyZNQ$F?hXmpf3; zP9nOI7dC?P(a#Rf%$Tm%zd}APe0)7tce^^gSB%oZ&q1TbrdDO++ow)S`V9Jl@XV52 zW;U(&;53O`*a#Ttou{OR2{i1uZmsnrWLy^R90Ud}3dT#r9D#M+TW`bu^C)z^MBl#Z z%T&R4*;*<=ppexhRy`>`b)pw>!@dXC){3~J1*Ir@JkO&bmiu2%%>MWT2xOR}?q z9OYO_zF@}V%Z*(bvGKfxj^cJ*K}9oIBe4MAAx|2t1Oz^WjDlZ8d- za$D0_f1DhK5qr6``UwPzfJ#&7O#he&CxbHKQKWlxvW(zbVrotj@}!~RatrI{FRV6; zKp>2Y78ziHFoAlQBOI+bBu~+nW7C3vL8bpf%drsL-pth!vf=0l7Xq#Lci4$=X6PIq zRc4Yr%gBSiJd76nE+s8c+tDBMuLyKs;JlWr8rQu$<$qb|_P_3d=u?jhMWf}d@1+wZ zP_2`QiDWzz{s75M<$uWuu>`%ZLb8K|uXlxYieU9)bBLlx)p!DaE0#ayzbB^`Ur&<% zEi1D4Erj@=VCr3GC*-tk9Wut3y<|ecAxfMtSy=Ux?hpgidg+|_uxmKiQ^05=*<$G_ zARzSPFL$|2*Q?on_;bhLjkmsBFaw)=c&J2g#S2jyboCxnzymELE!epza_JZj(4t5o z;iPXAKEt}^FjLYMmxjvO^(k_V)^_Sx%sg)fBVo{MpZ2KW2d-MrIt$r+35JZ>U?^lt z-%i@(5$I(rKG4Eb!f#>pAHW0T!(fNgMSPtYqGx#EZ`wc#Atm3knj%!dn5sxDoeuwu z6+Gdb(q?x3f5T@be03H1`lX!3hJuY%hx+Wu8xWp(OT*>F;q#Ov>qXhot^Yzq*$4sJ zx8%wkQW6lY$D+Suoq(``o9@^FTb39EOmGMV;(B z`lBZ^K_OeWBFAPF=dZkrpGG_!E9&2GtdNb^7d<8NbC!twOzH7wicymfKRNToY)7j} zEA}}3eR?bbpb-cDC4+}IQ6{;<(`1$%Lb3k@Xhn##28Ir=$I?(hoc|9bZEeHlOF7{dl*^K z10;sh`Uw#rfcEAVG$`rPtJ1L}yANmUgQN`51JJIupSV6{(@ZJD5va~-*Zl-)Qy;UK zRgJ&#S-%cVZy&!<#MOW_>Woha(}2rE_m3V4Ga>9YG8V|p5&zMaM+Be9CFZwKhZYs6sy^+kqoq5WAB(x=Z5Z9_00J&+?)3^Ytv>Inl@N#M^5O|tS#r6&s9Ja5 zh=A;QnAPBoejNGp1EJyIO&Z{yax}N)S1PMAijN357pe z)&}+^2OewVrh6OxNjjbSa49CA#R_!$JB$^hd(8gEa!S{)*DqdNmf?b) z7+8L3(P+m%A0mhbB(}YspN)(lcs!BtHbzV9d{`lCOJU}0;1Mc54u!Be=-S@6EmrGh z<$n7U>~D+`G*tpbj5aQOGH_lz1z&jB*)AHrQm+^9MmD z9}1mY1vyf96Fl-+H%vv%C}Z=GXW$^&sh>Rf44x7CjVowJNCA94m%zHx6sQ$gWxt4J+Ld4Z05ZanWx?}WC=-C_n*&o5S^V@}gc zHK)s^%A|RUzGh^&<*UV@GD53NDhA8%_wc^hd#Wiz1HkSfpkK3_*GOa(Wv%dz4zi=K zuH1HR|4nrj$zNl_eyMMF{Fjt5rVcV-1uM`?X@AuV`lSNbJ~Z6w+K1ID{{rQ-4Qa+# zUMdrE>Gp#Th!U6Zg!tVeZ`(zNWBt1v7lB6Sa$PE8!CB1gcZh-sbMIl&?p8cEPAnW7 zMn9yzH6U7hkkDw-iVnU+)8ZQuup*r&s5;jYQf=A>XP<*8s~H(FS7)kv^*Q+jeVf{U zHhC!^$97nzKDt-A?Zf5B$e(KbdF6HyfT7s2q&T)MEoEgec7S4CyK!H$o~r*z5YYie zTLyCJR-n@i|6)qV?wAuWZH}7IV7YeZ6#lN=;|VcNW4=c4`#&T3zrxVf6)1(`7HV9` zHi<9cCG3?%e2qC*G7gfkPVJ67Cm6+{eS&WL_Y79u4|ctJMuUDW6a3L^$1(*mLEh|t zA%mL`T&QGTLAWvsxaXx7=Mz8|J3NDF+bWc`#_3KiUbS(cZV#1#3yx|41UaBn;; z_IJPb>ZA0SNZ-G?x5>gxh^gmVzZyWwv|di?3Xgu;+1-*k9jW9)=zsqB)0CbV2-r%^ z2v-AR_2skV#9BLVX)wY5irQL=gj{DsO*wq9V(oP{q`^iC-s44Ly{?JHOcXuXNe!(G z{=n~{Xl#DWAa`mR4q+xn@mxL?h-us3SN3=GJ#(;sQeFu9^T%bkR6f#yV{w~#j5p); zN|@MA>ob2$)~V>}Njq0tCh5iI;nTrMIO#X1|ACJj0(xtc|3e^!3V$GAis>ME(U+j& z7GgaxaLGZj&ctxu+4ZG`G7KO!#gmKi$i6lvGp?^aPgb7lnPgl|W-qvXPsR>Et=Npw zuHBD$?yTZB?(x8Z(n4OM`e{Q~ZDzp1EgY&LwOvNqzxAgptrcHoE14M+c<_@mZqH%5 zdTGpEUrQvF%3?9USmqfKfeHZ6!v-DRaWrfk_O~FE5NDm3t*ZUu<0g!}y=`k#^7w3l z+1S7*4s`5*_qRH`OZuoUOYyTU4KJKg4KGv^L=#Ep`l2`gsB$E;ma7xbe7wc4x5Rw+ zzM{>2r6ECw>tvlgm|#2b?dm}hG3hsagy@c*4TQ+)-Ugp<-+EPO*OKZIDBM6tOC)Sk zD_L=CM+#q;rE|76)4z4aS+bS9^D*w@$B*AMO2?h<3zy6)W*%ibO8hM2^1MyJ@k4Bibr&ilu8_`e4_41trbJ1@j33VYWDjU13N(V21;-{=~tT%gO75hZ-XbxLyV6LKOCgZqM{#Qm+Ui$ zqS}82sPw2lo>tG4q3-FKn0ZeNJ$*DD{{Xh2*s3ZF8 z8h&xI>!&}Z;h&eaGqmzd5K5LQe&r^bK1B^r#QF?onSE;tM5#Ty1K}F7w8vsJ{k0<1 z^eO+WJ~b!#n?hb)v0$7wAk0If&V@s(H>O|{EejnbYZS_`F;WqP%w$~4_ekCGGHv_U zdMlL_Mu+esT3qo!Ja~5@>Byv2mvnAmpg>of%J?N6D{DB+TQp1eB~Cm?0c75>TX*LW zbm3))H0MmEadL{7nd3YIaxzHFlu)xVT`!H^`Sg6CajP`AvbQUI__4J%es6tGaqeH2 zWXenj$K*pI5%8&REIP3!=RIvAS2&TQx8(tf9dI>?S=WsMq8D%!dEi$-Ad z!yvwt6DMsst%U`xl&4O|W7-wRrC5i8gdBd6{O?SZNS-Ax#v+G{dD|;~pF9xXSyXc+ z0v&n;Vb``0TyIOkZ0^DKioO~;^ljN?+76;Q+%I`-c3||Z^o*S3DY+H9h3XIQ09C_1 zu{*UopN-yItLF0HOau&Z(g;x!uHdyPaI`G(1#32!`oGFJwML;zdMrPWRUhPxB_3PU zH)Qn4L}xs5c^>04zx_m;in@`A2j>yH`&}hUFa`E`vG}jiwTm4onL{6tHm66KSZ<%W zaIB=SwV=sNWNcZ7&@H&Q=uGr`#mGPFP$T-ZCg{0PM1;>7jFz7dl?KLjxiG5P7crpE zpncZ++^IaWesRyz&e71(Y~uc@Gl|QTO{Qh};jKc+jogQ%0sAp--RPzU#5{}#$M)B1 zJ_p`6T(-y6@7unO?8CO|Ce&mZbk@A?KLiYXHXocKs2DEQTtBZlh{o;2RGNwC){3fj z-D4=t)B5?^b~v->jtPM+6oj;wiAUa)wsq<~<$;?K=!1fl=(L^s(IDr!c6kve;>v=`U93YV+Uu^=O)l?S%aE$ULw0x&wc8b_ETjzYoX z$_B|lf+f=@FTG0}RrVg(YyyF{o7cs354 zc;sD|Bh-+Vw>7Urb=YzhlTVO+<Y8fCBlC#BEQej&etqCIFu+U# zN2I?WPbp6=DYmj}lF*K^G*+FtJ#skk${nDCOyj$c`2IT!Bn9sYFxpS<2GMxn?uw;x z*@ya)V((K)3lH;$z+lfN!t@R%)Ntr?_OdbSedEV%xf1)0;ApS2kwH zKwDWh7y6Ll~03Z_aROp z`M>`f@@Nq~RPs3ZBI9tf4% zd_4NhWPw~W;U3WY3&Ecpjq+>HoPW<6@eGrzIJ{Juch~wX3D$8Jbm@AS_K3J;R+n* zh={*2Ce0aHv6y~ehSM1(a$xS`yvgtTNe<>!_Kv>)1bd@m`p1>Ya7IFm?U`j-;+QQtybwoz z-edBKr8Ig0F?vTAa9*^rl4}wH)QZ^qjZ#2|Hko{*P@h#uZXNcpcYY7`hC;$chVtK( z&&DX8fz8KF;{YEE{i)!??iY!LP}5y2sns@)ut$u+@D*XqgYv@C->=QOpURSr&#N6R`^*OKO%C7U_ zO+h+VHE2zNIxYe;>8Y}{NCbrN{SsMUdEi7<>QQ?7uao;^{VIlWAF}_&|D;+9ljrR>J^xHM?eCJBW*~oy8j7n4HmIPtLk~{D0pp1v;NHciy}beeSe8pSjjR>~~tR`(upq zJ%KdGL$ewZD|^-L`o(vfjY~}ynz8pXJ1o&n`jTD(7(}VJoD&z>e_Bsr-(RWGyz9>_ z^Jn1Up7F>D6^)P7=jUx(f@MAou(Wmi&AWe%>C3Tb?a-$w{%7BqQVQRruH7or`qRl| z#dRmaOBBTs#{cXtiTM|I;e!7ag+4`r7NvA|>xjy3Px9Aysm}Kd2vY`I%a*}C>DmXC zf7->mUrZeu)1$qKudXg86>mktyAPkQGajZs7ePoN8O~}w>!iV@md-(Fqx7-Nk((V@ z@VS4qpi6vh2dBpk9x)-s8hbg^S4zgLwJ$*#kAKvI}z62w3X)oC)D%a&t>IQ`NqIN;Uxd162H|7e5P6ENu z`beCpXF}0(r*(DvU!@+uga%e@f532w^qzR=W9Y+`}Qc~CjIz>O=H-6X+_=wUdHfF&Wi8OPYH`K$AErs%z$k9G@k z*|M8egunDV@!tGs9rp1ZnDPuu%C32okxBoxdi!Zi;}t((9S^{e-9@9<*dBhx)=Z(A z#wP#I`u)aNB3)?0bK2vA$Z}`A(uI*HC?fjFbb3A<9(%Sgyw81Wewh{~<=s8H!kq3) zxOJ~q*l5>X60OPPjO&1Y(AZ0_*q$~n6I+;u>=XXiWuJwx%ZUZ{EIDHE8@iK5I`{7g z+-hybZq%VqU+7#H>30l|vqry-C4Ya0uK6&WiH-Z1KmEu2gQs0w@yw>?OlG4_3Z49~ zOfu=8cP`4bMsh6{_1(7{1B#$g3HcyOA+S{90=e7(nUMR2P6LUb&2Tk`eT6fQ-^KlU z_6~(%c5}co!_stB^W(^JVm4Jloa!a`Sef>iJ`%n)^EpZ6QnO-jDyR|hs+H-H!&trD zlG|Z`KJ?&SntrzptQX&`C=^|j%^$rl(t_VFCrd{cB}`;xBDYrjT3~v;g|_N#(6H6P zM%=)cR@Bo5B&|aUN4&&EL`#6cFyboIIoA2|G-GP}N!~f+x}HCsLo*JcO?npJr$LwH=b!wEBR;+EU4K)^uiePpYq3k5ItndscP#5I z*+^zvu#{KleYTYL07}SF=mSPDs}vR_G{M_<6T3@GC63Narx%)=Wn>mzmH1Ju4kN!l zx6W;CB-neZo?Lvk>S1$hKVDt5A^G=jCV-d!Yy0pDzB?@U(QWD1*Sl8IKJ*#gMriwC z)!TN@oW&3=GCZ#6w6|;hCSE7(HD=T}c1CC_&fzOf|9w(gN>YX1T%MGuVP)D_NSes{ z!P-yt^XxHEiy?p1u-onNGga@ot`q)~)h!kOIuuWZR>6BOaB~-b&w9@0P|*-k*HyX3 z`BiD^;3;JP0 zolBqkwI^iEtJP&31bAeY7Eg~ncN$QT2VVKYqIx z_$d<6@$@&A^Q9le0tEy1^7 z?J<{#cGf@@tnzrwojBKh)EhQ8o2#g91IX0v2=jd@seW|SG^5NMeYzGj#jjNV-0F>q z+|&2g_bX;=xy2G3Xs`a4Lv-@9#gY_xw3C^(2oPH;8`eDg%ZoOZ0d{4_P)hVYtX4bma3AS$FCd?t~cl2qpMgB~c7TfA$YO&sbL7fC> z+Secwy+`tB;>~GFbr= zgP2LVY@!_-*URwTX1=3aXly@KTdMit;nPJ0adBo2Wfjq-p-yASOAZeQxLI9J+dU(t z9Z9L!9!ovPPA>(s=5;eZVj$bOS7-*@fKfg9Yso-0e%8Et%W<;N?sds!g?TBuJA1$N zMSY8P@51Bm;>N7NYGEsH!Z@T2hjw7lC|dYR>;nJpP+^FIH#_(!!)GR>QZhxMv_c=m zzOa7`i-eVoVI-pL&&0iZ2_oB*^rt5RWV^i~ed>SOTkunPM#eH1xs>^E9>C~poc6lMjw*MT zP>hhTG1|ZdFB%AQ{NIXOcfJ43sNmI0*W$^-jQGg|{_MH=@3mEF4bsTue21U7t+-Zr zu@$$pG-t~wz+suE`+b+zyZki*lYdIMy;5l)7+>}vFK)vTm^Lb<-v|RE(xc~-$^~ye zL`9AUek$;Z#p|$j-7unlz2Fe2TO(n^UE(u$;@bPEX5N|(~e42`sO3W%bB z5+W%uBP}YWQc5F@1JXHXjqmrJ@B9PjxAD4UuUUKTRnO}6Jon9}Y(;s4%KrWICMD6N zp(!s{+(niyIvTr8l0b^{e$q?qn->Nf~H_l_6nO0<&W)w_0S{#wz2 zk>UGHNmz7$Z(wwv#D`aRuujPJ(Tf{gR4nG;%GRCj{LyZmed=WO7HhMb7Pr`Uo$2X- zZ_Ow7W7RiqwaaD^J0`wYO&~9Z6?*;7y22WW8`{8)s>+vOy!tk?7)SlevVUU6gW5MA zZot9RTK6&0auT8aM)REb15x`&-}8t)ZXBNxMertnebHi9_0>bo#g!vamx`~tr5aqx z+^v^$^o^ua{w@^m%9Iy(*}A*$Yk>H+iw3h&W{b%D`s2@1RQ{S`l3Wi+Mb|EHo#4(G zieFdY!jOwSFpt}__1|;((shX2UWRz?vZvdbn%waS?k(Cwl4w{l79eIag%^phS33=` z4If{a_z`P48k^Z1kb3oARjOHgC3U`cnxC?orj#$BwKS-7Z)${RTn>q2(%RyabgoIY zWwapjD9fx_UY3Ph2U0oNNZO6>BY(n+ ztaiOG`O2DPzPUwWo3BBzjpOwigTMdfX42uRpInJehGu+fDp59w;B61=T~CqTr{`Xn z`?naCKCEr#w+Sln-}(7KEskk|2j^_C9^$^eJ@0;b_Wqtw|(iS6w_m9WZOK779?H#zu#vJrEbJz^{G<*3waxux$%A}`C&Kd)gr794H3Ztbn| z?jV)FZYN{t!?-*%oX&yM0*l!xXiz3EqkuMlD)^b#NY~1i&$YFh@3#;YLkg7_izuDI zsG%1pwhGR%_%J`}ABnA+X>QJ@_{r;x1#tv*7)NjjAO4K(MDj%dE}#HsyEjzU%AFBz zMNIbs4RT#i%CBLVlc#1+fc=8z@sIv?A5PMT7fx1HyY!6GAI&>X9L#DD z4EThZ?(Z20AAhKvl-thbAP(JU%t}~=EYzpvSVuM+cH1Snjjlo|OP{0{#P40LF!5h> z>3i8yB3$QX-h9sv{z6__Ck*B}@~rYtu*QdCAxUG^Y52_p^X&D+)OpW3R6OFire$~X zNVM*JRe)tuEKbcQA{;PJD}r5n~JpMO5go=2+sZ(oU(=4oa(m~@DE!A<^&7HGaV zrLhO%Y!!zmV;d3W5fv&MoMFstmQ#rEkLRvVkh!-c5o@e$B(Q(M^LQaL4 zaw93uliFO%_Lw-@@;%X^w3eG6+##C0x*lGnm9S52rlJ}@ZDC_OCsOu2^k)<=j3+7{=cLokY0wo{&l}PjGBw%xy{GxBgmf01+ zjUik2dItH=^D?1WSsDGNv#%(-md6_ykj%~=P4gX6l-j8VLm92n`lxgMfA@xT^IL;j zO7Q4>vfnhMC&+N_}$Nt!y}#bMH`9<`(R`KeZD2KE(V zBaz4fr3@PTeS4CC(Tq+~Q{EX5l0KfV-KTA)FPM$>H6e1yVW`R2P4Lgc74kVoJDXoK zHM_@rZxd$XLmOPIKc@XH=GO1c(B|uld3U|jVWLfF>`fe2 zZsPpRl19zXBs?RREH#-=QxZcY=8sH&m2mA3me1Q8F5F8J%%+vObm%D`=$NznAgA}- zE7Mu8!BU{wSh_-`J4St;|88uHdfVrxvgy24Z;`JGP=E%tBjL}}Q7OkQ(xA2(RNb4* zVGQ9b8TXK4{*WCvvbYBe*UfBL{V)Qbm%Tn0HXMIoSoE;n z6%p58GIuC}>0Od7p@&n`h0DR^NB{C~;nKslGxkD3t6P-t_wGY27rhm%o$J6f>^Sya zaL3f4`DZg!91gOGmu{Y-4a}lK0}AdJevyoGJrz;=T6Wc@1yMz%7@`rr-N#^qAJe>MwEev(TNgJUa7M~ zY{+f1KB@=52C?W(kuHf^vEMC^l#1*3801-pY{$wTXFR1~XOynHpUPzSv*6NVyMo@^ zd*_4y#3Si}E?Mh@OQ(ZTZrhh{JIz2R`c6`}g4uNi-DXnTB4C?@D2ZsP!YcfM zi0ps9l z1uv>d9=!Y0o`DXoO-I6PK z<;Pt&doa_McKH<7BwA~8ubLJ`>&Zlh)OCfM>Q$W&{a`legcOHFKU`dILAXagaH{HV zZD@M5`2aN+GG!#3d8t5*vWmUwir-Bu&cN1B>5(XKe%nkLKbr)_qi{FBY#n#rf3t~r zE^}(qy!s?lev>a|=FcXN>Z#?h&=S|#I=v8Da-5Vpm0|%L{WeLUT9fCl6RT||4;%(m za-p%8Jk|&cqQ_PXDSb9eauH6rzEIDF(~}TYQ}gmtn<@G^Y$V zen;nadb!t|)|(sUT59rq8ybK_-*!8_-5y{$mckZTkB=HrWAf}ah(|Mrkc0w5 zrt3L-0hL1J>*-JD?j)<39uxP(poE#@dFDk=xVqGT6^Z4WMnxjg33n44K6lHZxO+CD z8-5SGJ}#C_xtT36cwB#pzJO&_2U5d`_xO`k)I7?Rq&j#pHuare7mI9e6=xE;OU%^Y zn}B3E+`B2JD<3Zu@iwKB^W+P9(#O6k7xUQ<`EH*Gf69J`>4!Cf!ShMYQ$~wBOFWrW z-rwgo)YFd*S$*Bv82W>S!q$|NB}u%_3LhU?2_FaDaF>-3D)qMU`T(R8XbNyLhXWU~%axG4M{W=G@K;~%5^?vGbvoMx`ORIF-B3`kXi5y3qZtw0`VtO_?dMSxTsFkex)$1Ok zSJ#35kP%zrCphnPzn&RiUjD<2*JJ)5shdx;={oLCX;$4;3?})`+QGglEW;an%^Eg4 zj<-e=1qHo~3)KD{t0KHw4iOnaFmo|V#v45F8|7=n>Ezg z0sHEclB(#vNi%Je?g9@l3!Lo^^`%qWIj{(p8ZP@NXY+eKZa}T|=5E(ieaY0_k(b>Z z2UW>xGgt*-vy%nh4~WEer|_-M;4Fq;h1qv6H~*dg+p&56>UCYJRvQ5o>r9ox2cNI| zuJ7vCUD6`L?6OMTQjj?Pdt*{u=-qxdGUV>$QTHLNnDI+Oufi#orU@s-3O#@I9!u^U zK%YmU0PnP+s-y2>ugbN#<|`J=?Ji3ph%-@{H@dt~R98SIv#Ve%sk5L9PbY0AN_v%) zG!!ShrEZP5-Z-{qdG~Ih&7dhqM{c3;ZmMP*_tlzb#2-%QJ3d|&k*)sfeOOfLhjf(9 zF3>+7E?16{fEd~whEGW`LmBe*BPJTv=X4(aF`K}?U1O;KeLwQX{$b`@WCeodj10#& zxMB6a$RAXD$e+$O^tHK+_QJoJY!}L%_1TP>XdavbtCSc0@8U>k(kiBgx{=}^DX~6J_uDc`ce*3w%{UZ zcG|dRsS4FwT)!38KPo;2hKfY@nT3@0bLp5)Pb=Hn(3B-TGew6c~&OTuGZF|(GSRVl$82v;z~{JRE1l7abWfa zQwZ54F%ow^gw)h*luSVI8AaBY;iapt##-CYVb%}Voh#S^aqln-el}0CyLqm8acwU} zDP3m#MXC7gM)>hbv@x~Ze7G@A*KTI2(kHo7k7eny*MrWl67NrgpJ)?dnE2ETRHyBP z{;qSJ-{inJV!qv-&1a-}phmGtgK0&wnAyJ0#~9Y~*9!d>E`QJRih>g5rMNH23bwT( zinlY%h25@N{Fx;82y>}dN4x38(`4sZ$2@})4^Kz~as8@R?~E^2yw}JbPp>{yRb+)~ z(ac(AoT1nsnZK-@%_8=cyOZ}s>nJsf&)-ab8#`Nr;tqtr+oFlXO)@?HG@RZrDVN}E z7{d$Idf5rl321|>+dI>r1YZ`EHC{^ew|B$5BWWh}{(;Vs+fH=>#*QAQ`y*KXD|1E{ zFe;(Gvooxy*h*5)6h#Q=%wCN-&4g|%Cp5WbN`E}Zd`^$np4--lmr`h6T2}YG%=^uu zi*7i(cT}vO`czVVO^E$qj@t$X3|pkl((dQ%V?rp0&s>j*46~vB;o*y!LjKyhQ*mq| z=M~uapqVpEQFofbmoLjx>WQ|qHzi8DwzTmIxXYD1VYlo^a|`sw49sot38*QxC{bNb zp~o>l=ZBEEdN7%*3}(8zJ;_rWGpjY+{NUhr5tw7uIoBGkU6RwdsJ@M_+5X`fZ9S7? zkB=2ZQT`;DDcsLl!Lf0Jxd|1Q%O))hHHWWNTJ%0#rhKzhtT=t%Qt%e~F;O#VogE3X zK+wscDCL2pjY2k&C8r&8;pii+7D3z21yf02V?p3XcU)B%cUn z9VoM2lw?H)^oMwa4FpikD1<*nsSi{8pNz^*^Y>Jt^KN5kSF=_!zw9Bvw+b}; z+rc(ipY740=~iM6R&V`zx~Y%b!{Ev?yVSJ@?H1bOu$Rn^Wh7& zBi0A&gWePvabdb(drP$Rm#Z-6*}H=J52ZFWU61bG6QnALR6~7h57P5SSW}aS@Px)9 zPCv{VhI48ZPmbMbi57{5Pw+n=e@`7~TGd5E7y3wi^H1_STd5zUo;II#)D8*?TJHFm zM|A;PzoK)mibt1D&=lTg*Hk~etqjJZjUKypD1Ss(EVZX8`4kr!1y24{0V^7jjyCk9 zyx;z9u~4t`{(X-1T#jA5A&#=>Tn0nhmH=x76A1P4!KX8o>13{kJ zX#N0E1b?#T#X5^XgP-+buk2&xNZx;cI$ZK1*UjL5^A&KRLO-)-PMVh#F-*mlVZP6V zNLyp|cXUJVUC1N3D0`U(k)NsgaK?(udPembosLx3;=51<KdF3I%@t#$RBlhLlbC z9dv4&%T#_ADC+DWnQhpHX^tts!CgM*U8YfEk@`KDcKcUoByw-;MJjjG7YS7zKr(G) zshD>Fw2@L<&?xsQr^Xm|xR<``W#+hdgt9 zQvad!@tQf7^8+G~1nB1_1r{6;<<}>?Uu-as-ZC@dy(a!tB7VxlqgF zP9F^&X_@+b3Wn30o|lQtCk1C;W|iNC5O5D=j-$xrqzp6HC;msz-jFx2O(GofQL9B_ zss3{p`RgLz|84WhkzDpzm*s#tdD2=dTF3u^K-+f69jJ6QHm*CS?416p%gENN6N<#- zTA%;Ds#oB6%-Pwd;L@Km5bAZ2WdnWLrQO~}gy=f*Cq-x++hkcLb+qrDsV1{B ziSu_mn%3R`rtS=!@QMKXFMRY5;uk++pVEPobBPx6nsHj16ZzGXzfOYefh6w~m*_4> zk}JJlCpx6~7{~PStNi0hm>Y|SN!$sp!gSwH67+hqBG&Ai>xhz(XWyIB@`s&_UeKsR zvDO=QK42=jI3)SQE{8=WYNCE`WMNbHWmkXQV$0cS?W!qTk1$h6HboDkGvEfE041YO|c}UZU?ui2}JkWls&?}j?Euz>=jVQ zF>RG*qg>Y=MsH(QF(DiIMZ29U3Z|Bj#jw6sK?94^@x*mB%#iG{G+(5b9 z(F+#NmUyjb)mzi&Xfs|u)Nhd?)kDuAgyS1h6BXP!+86p*W+Q_W!0&NDChgxH#x9LbErCqQe)HZp4GriO_eNU)Q zp>FLZ?Yii%gG7Jf=Cj4%x>T}1*IEu3Uf1z0gZ=C}l243UmpmU9vUKt) z`74uhkH%+VEsx{4q6+NzvbNr0XME@1|Jl|5xb!w|M{;_LmbiyGJ*HpHSkK}&uvv<0 zHv4@=T+;$KMD_e=%4O_Hu=u&E;H~LIcA|F=6-Q73IJeAa-+j|7{>loCl(YZ%kTzbf zG4XzxgA&ShHq5`mU;bHcGEe*NK2t=-nzO&{w`sRj?@xx}rK-Bg@AW%6dy!Y9;^6Q5 z#eFYB^N+r8+%x$AW0Z7))=FJNgme{mPwz6lSmX@{povlh9^;3b} zY33YR_;qBY1+ttbGU++LDCy1n#Uds@>E0IP087#J53ka#yIn47leFDxpPDRB`aDHr zl#_fDtZc?|`?Kq{IWQj+qi1$6@hzqRdxS|jZto6h&tKSFo+~Mx#{5-Pfea69o2@@k zL#4L?@=bGptzeUjsi%fp>5g`7%haDPPLo1iBKHp>? zey)HzHjY#Zt_2*rN_62E9nRQo&oKHQANgJ!I}nzFW8>c1pOCfQQFz*x8AGhNul2T< zw%vLtX8Y$0qiZ^&HL?=-Q*it3ja#Qn>gphZ@IjQ?{tWI(!TwH%lNSH8j$Yiil~3VL zKWH$wtS)&!tK{?JeQ4?Y;yWy|1V%%XRXm8TzKLxO{+Rj}DHW~NOa8uzqkZ!vGyl6U z6-8*csQ1QuGO4A5sa$P-supl=`C+CU#iaf07fvB#E4JEd{rEl(Hs?Eh_q+K-BHW6_ zr^qIg|7fm$JL@;FjwafkM~6ouUBXII_rbcJ7|9rNTCpp=v!GtV#=5C|@EG8Oh~Sb^ z$8Q+MX=%~3gi5QMq_CWElT`U~_+p+!@$s?6ZMV)a=AVlvhNK<{cOQ|my_XT(8!pUr zvJ$_n-tdc19Eh}6grAJ=kErNBHmAUS7EdPsk!i0*s%y9M?N30Pr7>-6q=#0m!GJmG z^?NSLmP3?wGghyDU&7_S`}DACLnMux3TRUB5{oqct=R2{O{fz`k>jWh`HlU;FAStf zyrh?Yners5J=tF)^YGRAbEIqkj-HUKvAz0NAm7N+^uXtt8$JTNw1(+F)2=L0*w=EiXO!2C2N}I_j&@t9g9+FBbizkOrFmmX%zVUF3?Zbj%v%tw7s#$!gHzwtosB$k^mChN~(0jIgbl>8>%oPwm(g`uo|1)ynQR zG{5#6w%+q1VDfmN0&isAr7=Y>7Ecs_xyTZ2OG<5{8u@= zTlE>JzfXDFdrs){^Of4|E6gldA<5#0BHX89^Pcg=+JomG{+N_HIyte5m_$|z`Y6lp z0JB~+$-~GSbWYr#MBXR!@Nk0zEyQ?+E!gqGE$=-DyTSh07dsz%r{!&Z-Zd;6-}f^P z-%~K~Tb(nwYGb?3jKyl9betLyUQ0(%1soG;`)`knC)GCON7th{C~!Z|vx<}QQi$cQ zVmv1%?>8I^by$_~7+P+Yirs0%eE1XE7AI^LveCUXMwm z=&=a;#Hz{|Ew}C6Ml>zJ$qE%;*q0WCF~i>dy&kPu?xyN#w+g@+q@o}7Ui*3fA4e3GL*I^NcSChoEz)4Hr@Pel$zRPQZ(lgAN6RCVPnM=AOrq6Y_H7JhEsH zeK9!j%OE#AQ|3jvm9fV*+hB&j1ms%!DQgEsTg94rekTxKq4#TW`1#N->;u#3#}On_ z=xM_nMky^7xH8m^oBg;cJd~w4{gKp97_CHv`Ae2)F3$|}EB>NpflX?1W!asY^w?`d zcWKRj-Ppr^7j^iKeM=AZlXa7sk;}htc|<)yv`TGeu=zPc4f_4# zPNclkW+XcX7L}#yGC$dH7IM{x)Qf#bUh?r-83@s1q^Ll1$^{L&hQ@izv+lqOu`tEQ zm{tuHy_%F<2^--L>y;%z;(%uE0z`!Bt{u1dIJmsqxfCm2abY=LhJ{YYS^H(}1=PLX z^CY|M77q?-J>4Em){M=07)k}qarWo#5i1BYMUlfd?|Q{E4fvudUk02L{FBT)^pLEUt21g(5w`P22(?ly&=dneK_=qqs(9~PXA$Qe5I z;QY$&97lIs|K|C{suC3*fBwkN3eMX%5FN4&L7p8eKfWYn&re7fT*Fqf7QGT622ef< zXQ~~dDOCdC;y;$aT59^yPVUR=@u;@Ti+vyL4OG1AB+~BR#_Eo1^#&EAv*K^U&yn`P zhb%go-4&WS(EN|xdGQcq6=)doN>Zm6o5wZeBq^As>9R$n^&(Sv>g`jlqTF*eZp;Hb zFkqsOD~;)G#DR^o@j*44t`okEm^hPC2OvX>^8`)BI7go(yDLtwB1O}+ES~3Dq;=nd z-(eN>d9OUh>E;!fp{A}FI86SM?AG*qmy!2W3H~FyREhiEJPe{izX!!~ycj8RrUXP{ z&Y0qlVTy|iQ~7_Mc|Gc3w~uRw&E928Cq~s8ua?+~BB(f-g?>f2vvv9XYK2TU<&nTo@AJ(vA#F8zZ5B)zVhq0LUHUIc*j?e z2HLT=uWBWyiaYBn2G+VBm*4sJZbkoB3>Zif0)2HfWtFI1h`ePq_KxH(Q6Gd*6#tgR zBQzZXTO%BFDw^u+`HR3bT#Sg(zK4hqo45sDBdgxqZ37L5+?$ z^wzS5Q-L!lE2}G7>vF&{>fIvFWJGrVJMPYnIaykuFGBxgtu93rVe-rO^q5!l@`jv5 z15wiexa!)Qc+_X%Ha{2%4DYn2m0yf1cna;&{Ysp~-QW9urBaL%ub|o?4$oq2C>6|g z?D&pZoHL>P$uy$G@%9z}?EQ|v<@o&Sb<7SReT?Ylype;yp4=qDMnhlmWaL&r|ee0|7~3T_(?D{qkp@%7V{+p zGlN-7W;_xMoZ=OODSj)kIb;ie)_@OfWgZ}-RuES@HqaB@F(wa0xrt98EyXsrD|9kD zIg(5>X&);~huOh zJ)v)KtM2!TS6P=xk|w|hQ2I(ycu1Dl<-WhK1;LnkXL;QL1Y{!o@tf!tek(^ z4v5g*d)Ee*7R&9syDv=V0*%#IT;eS!2YhwcY{_^b-Vf-_c4oZV=JYfZ9ujZ>7)s(g zlb1+aUWhB_)C03vLHa0hgv^yE7$<@k42U6_$k5eMYeAMErqHD4dsCXMKLAY3>%JH*h3T3W*x977)D`uZ=HX5jeKMx!do)9T0ycU0llADx8TD1BwYR2{>a%>Vz4L3%0Yn4 zm4UGkd4Xr*jwBB+f?E+ys^pj+qxsKv7NpD-S_KS3tRk8@NP%%&O*p0zRBQ6h$H4aR zdv{VP$os8AVK7V`&CR4b@NOtwM$8XH6bz>%J9YgpOa#8^2;iHJx>|o!3d>bNK2M=Q z9B-%eexnp}#SHz9i>6m(;lwsmxTf(M^BF30<90QzFMYs>B z<*6u&;UDRw^qIV#PK{@qIROF=YWho77``lc^h*U6>SrHsa$dSf`om|d7UcFvIuU&m zP%O5lpbFu}muMW6$U~`D-AnuJ<+CMaiqm_J#~!5{c}dQAn_*%{EoFRKcxz1Q>7$8; zUBTA={vuj`T}N55;t5djHMNh__#D&ZfgB4POhm25OqXW2oeh;P-b0V6Usof&1zH9; zThhVDQw-vHwP}|4T;67z0~e*C7ypYdZ120Ra>X4=1&+ z`KEHV38VfaFQ`G3l|j5ZJ~!7$EkYGIFH*cj+;}75cfU0p%toyw z_|gC)@<3cB*9vk!zR$6iR))0&_iQ{x+?WQ)C~Uyi0;KQ&521B$IlT`aqqZ40u)no5 z?`Rq%0r9}U+JaJixd{63`N80C7L@VH$0GLU-Q>O4tlu}Av4Y&&fZU-ed2qi(tF;;Y z%;iMfz2_U60dKcN6XADmnh{(>X%%{B2l#?(o+px*rRs zXrjE~oT_t3^j`y03<6}0k86e!WNidwO@Wre-6vlKacGq!etIp;+f(b~F~CPCDKJorpQ1P+@!p!}PlhL-@+5$ssO272Gq85j1Zk*1Ohizp!o}= zgLId0QMOlNO9V1zqXAOUv7YUDP{38tXbY%bO5J^x^^EM(%#E7CBkZu@b?KMj%HbbW zC&md>5kC>;s*XyG$wBz&zB>6YT_=hE{oV!N=tISmW^!y33<59l{5%<6zWh9Q2#lXK zlckcRGI*RPUIyH?ik5r?P0J8};pXC>*rmoJp{K+s5v_Pt_yueHdcQ|S(ONoRIrWh1yB$} zd?T7_ESL>^TPnxQw-@PnTq&t_pTF}ZY}&ro(234791M12E5VW#^$kT{Yv%W2yKr}_ zHI-n&v|!;%PR|RZnWYBs5@LeSRgi0?PUrUsz?UttxYdvQ-j7P1#Cpoh&GGT1Vvx+zi2%yeEXE#x7?`!*t4&d z8#=4Pb@0|E(O+*Rr76jWuIhK(9o$cPEfRY%LL10@R5hCujB2jm2EvXK~QmW~!{4W-qFSMWijpn)GDF=fO9V_nRQmp-L8$4*9l>P$flr*xS zS|2>u^V)7Ne1@SHm^GC+eY_H9ODcp1fV-U;kP49s0MHqnq`*Am?=#g9cNOahx8rfZ zo60j-pt(UV^i(JrRSxn}Kr(^#Hpa#5OeX%_Rhyfl^=cH>p#|3Eu^92Ypz3Qu7|v+A zKgM^-9*XdY5+&d=o@0+4@p#QqcepzFVNCd5L>H(q`}~jC^7XR`II59YvGji1bKuIU z5E81n0A@;nRDt8}d7Rxum$fAW7hgj=|B?@>#lt!Omyt{OL?FuoubWns+t+Cla5bzw zO0*3B7Xt|%5cy1xkHfy~)oyVFWAP^P-IuGAbts6M=v^UQDV%gv2c&Ut0KmLHI`>$D z4Yy?7ZA?drYUruKk3x9-I$iY+A3S~?GXk`Z0&?MYuF?Pw2}q)nD^?wT1&B`J%;7^S zxu#6TUq_QnmR`bwc#|9a6+M`-dciC)6&4M;f`t?vXT5@kUb3EQjHhC+`8^rI{5z5KksHKvzRa~u1I~v>CXLLipOgE72)LZQ$qodPoO0$I zixJTGQ+gr>)*C2s&?CR~9IIJ~7@Ck=y}}1<4NevCW>bG?l95tT%p}KqkAF@+u|>0~ z3<`^z!?F7qFGD1g+td3QQC(JY7Zlok%CcFR`FGR@_Hh`QQbA8gF#SYCy(@{ge+qDY zWpqI=khyroQBjj0O-qT=E!xE02Th&9k`zG0j^q?sTmaaw5~t6XM?71)Y18LSw$KCn zRxI^2!U}j?>HmNo4}$1k5F8vBd~--Eu2%`yoI?B?$?oYhnCJY ziLA@JDm{Pow@)SMo;>fk4LU;JwwV;E3_u3LMe5{)y@)knonXbFDf$Gon^fnxcPXedAOxD-Id}?gtfe=DrTMD`Iujp> zCQC5@Ipi{*6iz_o;g?C25LeI%%XfOySf0}}3An<;=cZv#K(%C)#J@qsRB?Y*w= zODBo~Ep(1+A)h$LIWFdG;U>1~Cd*1uBFL+cYxdQD^$ze3JuA1ko)d|JZj!MSYj^wq zz8}C_!>MphDo8L6N)3XZh;FZZ=N7BaDGBoVdZyRn_P@cJ8bUez8?1>2_bX%TZe`q; z13j^1gzGmy>??lN1wDN>s3n}+X%^pwGs`{q!rd7Fq3N}!H>Uh+pK-+p={iE3Nki&I zn@I6z&&|q~@%3;oJgopB!Hw!d%Bh;YdLnZrX4638G^wdjz{dy=mCIW3B^{9mAP%}xiG_$rQ{prr!q@W7Djl}Mq@fqcr zwBstj>495CMSZT>c?dYIX-m-r+eghFum_>L^~#Va+h}FXNa)~|y|~xIHqBp@#i+A1 zH_b?W(r$vapTFbwt3-xozr|>BPIhLYFK@ZY`M5*%z(7oLj{jCQ0M?#Ub;7cqw^7 zga2TG^R2!Mu^`3`*naKE8`%KshK;R{MQs$EgDjB<9)87txKg4}s4L&ZSn^k(?U;1= z@G-z=D$4>ZT{4u|cXa=>=S<0Iu#H!wW3Vc`8b6f;ozsIj5v<UeDRT~c(Y zuM15=rtoj23WuMBAIRoUw5(E}to)@vXWuXU7>xf_kA z9tK5bM4jK>gbzT^D_~Q*_l1`kWMewA3ug)LOK2$A?Z5;G!bK=q2kA~FF(DleqT71n z0`~c*n_>8Ulf>!Bp?X&qewnBUx1nEJSw3P^gZGhMvs$?G4E2o|S~yMuOGdkZ>%sq4 z_7`Z*!Qv4X;gT?ZRpV!?zhzt?(2_f)(9xDi*lPuRT1%>s3;Z;eC~4`@D`=Tn z1oLKd`h<3BCn4r5{*@-A$@m2o_)JyB+W!S+YKunyt_Os%y)LJE?@KJcxVO0Y;41Cy zadtxEy|!18gpFcdp9IdF@+JdNur*dyRq@evRs zEO3<6+UEl}K(&kiBi6mmqq!{AH{*#|G;4&Kk5#}M|3jF{#w247Cy#d`dfT-ex{Z75oG2-s{EL7 zqm-X&Ji)I_8Sohr;6_nReA5v3l`veTgi1#&r>*RQ-ez&2K1(CjToFn7^ZN z3MJM4U9k)CFqA&(tM=HuTh}ndnSW9~OA2mL!oeKW(5Cbwy?iYm#&zM_ZS3{mssM)< zFC|4sg!uCIHRaFoED7AxY4{j+fINq(mRR;b8Y z2p8oR%FFnj-5B3vVCDEQmL`{&YrOkdAm%eYc9$_uVh>dm>F6GZX z-_XxTd5z=7eR~(u8gP;V_ik8%|r*BAY8CV56dKa26k%Xr7|W!?~HPy(#+!)~*=rdl*+O zP)&{;j+(d4W5WE){{^5bcHmPzN(0|h z!RZyg-Ogu}bb>nHw$|d2xhqVGvNWdVwZ3U6H8>1xO+0tq3TN^4Lc<+uAyrddTWZ4> zc&e*rDjYWsfYucU%OT#vYq>gWahJms@N?IHb;wPRF+p2NErWturD z!8tcwq2?q7513A@bzRt9>!*33k4VnQ2Fk3DDB zKF+kwv)0&%<9PxoQF@}{sdA+DL(6|G_)d_eMzgD*vj=3 z2uAih61d2pLQqUJ4$@DCU>Ej)!#Wv)-pAlz;{u5gbSoAInPvwt82-48W~%$`8xSxFfgDFOa2=KlUD6clFu z1VUJvdnAR0`-lGfg))cVvGN0D4GaUnW1vuTVikFHSAYMYFyMyn#y~`7=H|L6dAM>A z2m%=cPm3qe8S^^|^70CxYR$sTfh-l}+S*a>w$MSp@IB*h(0zpegN|O5(eHhg#v;^=u=n^gbrgoXki z5EB`u@l{S;54tfoGz`eFx2LCp2u{7r`@O&B?4m^8$8Y?k9Esc#eIXP7onW5o1 zK!%@sx+}`cDr-@VXW!)D)66XeGVG!0ZYaykEp2FQEY6NkDK0B}4rJIv+WZ21+Smj^ zHDP5yf<5gZk*3D#SB;H0AVJ`TcwJW0(HhgjELghe-5#S9FAV?Z2 z2coM9F9UxIBFezvgo`Kx@&1VT1oh%m1|ePn3ARV}5S3+Tmxq@@sA$l4VsfCU=uij+ zqW$NN2?fFge)uPp_`h%Q5`@idps>(77y$o-VPR`nkOXiLt*@@G03j}fV?$S=B_KoK z39SM58W;sPH$t(*e^ytQ=H^y_5V2?1mgi=N`}&4}5ZA-7r0Xklv-6V!eSKX(WK-c- z($&S;-`Mr#p}s!gu@QzPTVGw6UBRxej`#I}0dge_OAex2m|X;3!+rfgf>`Y8(mHl+ zb#-}mcIo$MU;j@a!F4QlX>oCB1+{v1aTcH8#9}NIibbq0&dn|W&$*eA(OD1x2oOuU z_8WY;x&q1&x(F=*5pMp$V)25YPrn|pXYx*__tw@lbx6t1fB7HqNk%yNybVBm=Y;(-%>RI zPcHm_5@PW8VPN1kc)%WlIS{JoJHHHms5+S5zOStf2>~|=1dDcp5cnYAKP&hTK}3Zx zhzLAkc)vpU|NEA!km_<(PsY_}i?F?w57$;gzn#l*$&yfMR#8tg>F2Y1i$7vJC3 zX@}FiC%aKrV&G(`pZxr}UTuuQnU9B>r8E@Eo1us8u_sRz4$BqNY?UGzl}gjiY2K?{ z8gz6vN%7qppICG8_4Dn*ezD7MF%$pbwvB1rdluAx^z7k-3r)&0-cs~Yv3{taP}*rG z%*-P8)^Y6ExeIp^&oNu)>FUCx^IV&BR$eNf#ONIM6j^!?2k=$M&#Fal(@%Sd_x+75 zTPSxrgc%o4Q9sN35Ogs=Gashfzx)iUc*y#CRrP8>dh!{zh3j;dxWiGspc z0?y)nu@5ySobu}JWom>qgE7-rU-hY}qFLxfVhxNVtX|J9&_3o!G!l~h%iZBG!ORrk zcKEIWq6(%uLp`do#uBMMW{}^tSZ-wE)4M>Hc1bY!`pi}!N8b5KIrpvchuwFexjiNS z>`hzaVsGu@t(uZeRg4Oecg$=^yJ~^?h|UG8qtLr`q;lR+mr8O-)Qhdh@NCOjuSQn?Q90yiblK+ZW4Ba1kr&hG;MG;4 zR+ymlbW>q|u<#I*>;LhNXt;o3PltqF^qo&xNrAHseuj75uUiJ(R~p=%-|Dd+!DxF{lZElUT)&;xdObEof8=^ z%2!lYf8QUP`R39VOVeZfj&X5^vrlk)X3sO_S-{8XTwELX&YK}$d)8eo;pa)|-A~u& zRJGpxaxJM~FVly+{C5(y=NzBwpB8FmzBs_~;=gU*P8U2=o$Ws3I@cF@zg@EIJ9tm& zgdAx;9B;$6CF$q$o2L}Iillq)cfS8SrSO5oguM(NAKP30D@r}6V%1pke!&a2D|dEB zF8#Mbw{yz=%B=dLS0WdU4x8~mJKpFf8eJVDzkKahLr4GL8){9q3V$rN{cyYL_sjIj zZfQ}O4VR6+)iZswI%n_dz;BtMcjqd*#Pfg8#r=K$#}+*3xtwHe`rt`CLx zUAv0e;GF*5!UYmPRldz*)@u$eW;piK%AV<+(3&L~3LGrVPO7o9xd+GWozM6__oQylvgezZT-l)< ztH|4DyK-Xmxvn|BrVRP>*qoz}xw`6^*dK3?`ec8kLnFDicIbO&AW0&($ z-hFuOmw(Jnk((E8{J0F*yhFN#HZKj>f@ic#D#*z!E-^5;!pOwT!pg?Z6`@j=T2!2w zpBJf;Sd^TR3FL*Sl;jsgsN^SQr6!jY3o$UZEr=K3_Y3w;txQdEgetno!5LDNpI_3z zxSX3uTtZSxE>tD2G$-dWFCV{vuvoZXuwHg*WpNach8h&6lAM!RT)aq7sDY`0d69@{ z14{#I16xR5Vs2`@fEZ9JwIn&QBrzvHUAHJTEwv~$FF7?NGdVvGWTv!?*n$NOY?tH} z6qTYNCV@0YK$xk;F@C`=naL$Uk0cgVE>cvE@C)__dw@ewN=4Ji+|ws8I65(-pt`BG ly|brp!ldaNw(i-x@4(RuH=luO7a(8+)g>UH0-@mKCjgKIUwi-n literal 0 HcmV?d00001 diff --git a/outlineviewer/src/main/native/resources/ov-128.png b/outlineviewer/src/main/native/resources/ov-128.png new file mode 100644 index 0000000000000000000000000000000000000000..950d0ca2db1eeaf3e6a008f818188439aa1433e9 GIT binary patch literal 4796 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 WFBz5viCeJ%0000MO=FT literal 0 HcmV?d00001 diff --git a/outlineviewer/src/main/native/resources/ov-16.png b/outlineviewer/src/main/native/resources/ov-16.png new file mode 100644 index 0000000000000000000000000000000000000000..db84366bd9cac8f9101fc5613049da79dde25d11 GIT binary patch literal 533 zcmV+w0_y#VP)BQwJwpX`5g&_C z6(T4FX{k_r5F&}B-fM#1e7%qFb4lb{YvOm16drkmu&7?GLe9 zvFqL}7qV{$USb@@#rk33319)|%Z2Q%L5CD49{?V8$xFaOul9R{JFgr}LwghBt=(T~ zpUI??qH|D?lpl>~#dq??1K%3V;*U%ZHZYBPjkKGXD7m X_R6{9Qt;Bp00000NkvXXu0mjfc8l{0jl7DZwAR=cR4{ zzz5vbzHJ^fwUrwhX78GMgvQOe=Nm{E{Yr5#&TeWjvzapEh%Ef0m}YQqn2G6~O?0+Q zg2jh>jADEiOu8(;nX21AVjeqGDksQ({CJ0nQ^%mCvEhQgi1)}R?<|9qoRc}+iQd=t zMx&9OoN(+U?k6ci{Y*Ij#dhkMDxo=#U_*2J#(N*tF=xmgQUT%@MZU@~K*u5^JtwAF@{z1r!Zsp`o8>*?gDfn_gjKGPXGlR5&?KwHCEj1A7_Oyz9bye zV;*3lk?ZR4^WX&o%#nq-XJ6kq8~ADISbD)`Aw{F%d$lLk7%*?&Sn)HZS`x}}V_xwXCuk+mC5@NsTm(=t+dmQ%aQ8h`?d$O&-?wQ4Mh*nFsF!`Ad~X`TY~O z@3kFPYhFeOqdwu(0O?2}OYwM4exdb2f7ZE{jXq3cXP#&R9fRy5vq^--gGlz`QEdQ* z$U$7%5$1cCH0cOaulKMXDQGC5MoLBCt1q0>iOCNhR+OtSd}_yyzy6(Dp5Eg+T?2>_ zVPB<=GS^OlSrMNQ?5N{9E+l(g^>?FYmYFV~`EVE^sHOMp-L=e-7lN~BQfB2bj;p72 zBdY$7O0mBV@On5Gom`lD@a0L9Cw(ZTo!xdXfkJ;#IjvQY&8)e?fhXNtD-%ksw-~^j z%e&yzePx&!{i6%pN-8<8Xx!*Kj=D7pB4jQfQ*dxyV1)=w$nNUNQIp@!NsOKSe zScW9OgH#G@pA!_%W(ev9gcD-;d(DwTm8v1bt&LLo#QwR%Zm<@&!P$+~(*9~k9+RKc z8X0$H9)H}UpmR}m%;Dw3uLmBucO2x~20B&61FvH*pW&g?Mh&y0HFzf;v|p5UT=;O3 z@^(pmc77TY(sXO)$>+rmkwmlWbo;mNj|88c;M9Nn*MTY>9432XvW*(lmOLo^id9y( zpK$#OKAVn}znUb1`Ti_|Y66zy^zkg0Lc+Rv|Kh9EM}FZG%HAKv==!Lg{`jVrVc{90 zuFoQXO6w1O>!B_%KJ)wg`81m_fs)C;>xIk+hs^Q|icKCJ#F! ziS@9Rn{Pzk*s?dd&D5xNn&<*AW7~1&c$3u^I=Q@2RV=9IBrZ+}wU92EGU{^pt?d8K zlVtzTVTJJ8CN7dxBNu$|FUS}ib~kKJ8y{V$&3oJTe6bPP+E6|(ejn)iw1(z5E{JVv zn;YswB=v@p#Q)pFvk0Ufay;`f!t$43=164SPqXEXZx&t$|GtP`1Nhh4r@mef09woHISr4!cjhz z84fVf@==~(?f77ZiUp`|ua=i^&Dj?2oxdp<-UF?;K$pB1No&}kM)07l>#D}D2mj7J ziZrpbpHKGI*vo~(ykKluj}a}G!;f!keXp~SOeA=0z-6FP`3%_`r$2M#yNTtjOhA1R zLe{rDw#oDArZ+m-YW(nlIJkvaNr;>f*PMd>Lmb~DQ8RQ4LH5CTq~;&3Ze6l0@-1i^ z+w}IwB7%8Qh7Kt|LtXZ`aIeB7-e&g~efWB5Qd@w3<0Jn^Z0%Bk*+#L)rk()cri_Mm zx_&-W6Co%5M(t&Fe(Kj4Thj1Y-nbopb~l77!SK-2I*#*z$<3H%L{X%Bxm8O4s=DiN z;#*MkMVSQ_W_rnz8vLTmzpScA)m$C7OaD{{|90MLJNbpVt=aVBqlnzV<0{wZ_Q$Ex zm;fe#pL!UbSJ-ymUAT+??ueLj#&xOMm;JKC_P6oiUR>HS!~?rr(SouYnJSHkxY3FH zWo~3JbjJsyXVlOEUt(?i2T$4!Y7y4kRvc#_k*M@?pz#;_%|gBVTMLD_T0)mbX+a&t zDUvmv-o`eu-=C1(?!xAYU8VS;bNUFj!0Oc>a`SRP)kMhbANtWDl*^SN#<^Axdd#>{ zBclC{+KHKj$=|!|Tc@2DGx53a4c&S25d>LJ<5uVLU5L3Zu&PZCgnIt33m&qqVYnt{ zvz^@?=M+>>Oi z^x+YI*z@cKpnM| z#^!%{wg=NHxHIPOBhhi6()4w>@fCRWmhYuonw$_=K0jorM0>W!@01)H zCG3|qz=2yma#w9SGPj&m8{c!7KMqWygK2Hxb*&;AI!1v4y^#NnS~G8N+Hx2|)uZ;Yeuj3@t8lCBzeb^YE&|UR z-5dksDsw4b?7N@xKC<0dA!SXpzq6F2TV{)1rl4kH2mSR8%i+Ls<2Kfj*(^t0_G>&P zs(|b+sw3c_CE)O_bM6))aDOQM&8SO*fAsKm_k%Ps(w|jXP)=~2rO0wH)r_!wC`2i&lPsuw z7hR%L+_eqrk{X?K)lgWEovq-XFIIU$r@2-dr_Fmo@kO&q@abhadDK9B+P;sil;36A zKb@)eUle05YHI^G2d+rAYzQoWvfN5;V~%bZ1x;n@5#4Vm2V6p-x( zFO`Gq$HiAmH#R5pCDciJUO%4g`dX96zAtnAkw&fIOM$1mae%o5Nf-&{L3_`&t37t= zsiF}FKKD+0Db!b-hT}c?vXee7o@lyg2Gx2hyME1G$`Y%LXolXyosihJJzhN^f=jDk@kTH(RzVeX2S zNq=kV13&@)P1>!0gB6ky)J_%jE;h>jUsjjhF4XBnDr_$Qv)7x2SDAlaHG5mD=m0_% z`_)~`d*|w-YxOKTj&o0VceZct22IS_hh5U@H?svf*c71RLR{)e2faka%{&%~&NhE~ zb2pmVe5~x6);*@;-%mjqn8AwXKQr5VxVTLX*?Gu|u=H__i`krno9@eUPtVovZoH|Y z#(eLZVMbhFIph`{Hr*uvxTEE}X)RGs*iqG4rL|Y38}qQrTpd84{Y(;=^ZK}@)Lip5 z&$*a?6?bpWT}9UQjA#0g`}#H;SxuAQGX&@NvPqH&K&v+>5I8 z(;?4QN0WC~G_a3ZLgx@-w?>bz0i2e5(dlN+Rho7;f-Uz@_`?yx{ba^sYL{RsCV)(o zG5y^!oUVH#T`gn8Ybt-G4T~(LMIs(u+jD&uEpc-JQcv+izd4=kyfW z9uCjtAM_9-t*)Qu@&#Dfo{o;Mk*6L)I26?GCgv6m_@o36=0&i3PGp|;#b+Nyd7Pvc zkBE@RIL(3w5A#%0uCe(K4=-6OBR1CuBVTcjdC!^z_Mzn{r<)>y&Nh z-J`t4Cll)3;eW4&2j{Kz4sE539H#ANc>CE?_wt`4-;ELDhCPo#Q)gJ1O+)=qT9t)VWZ&Ny04p1D@f1z8}$G-`svOljA*6QgxbyNx+ zI}je$8}>6bs4Y;kv&ZH?E)Y`wvq}1x>Z!5{4%ORFgyc)Eat1Rr9i^QcwvFvP^2rls zP%@gx{RiC>Cls*>e=pa9*<^WSPAPh@7J6y}pI@0k{o;Z#*g3nOCUaNyW#BKee6>PX zGYAwbZu+@{&)y{rUwx!+|5|B8_$pz%%BrQQ$QXv%T!UZvQkLN!#C6I1293}5&)uNV ztl5s1xRMFwoJ@LtHxn5@=bH}9ZV~;|BUx@xeR(`KQD)pHDb~wBXQV-ro8`wZZd9)l>(b(zjMQ zIWeR(?eFLS8o!Np5yLzd@l|Q|YP)GBHi(okNr9sI14&K)_9L^H5qIs3)g_S@&8H_T zYJ+=J&s9f~IghrK(KX^$QgWvh3*H#o)CNHpTQS31HOF5RlYNK1?^Ctwz%bmMun(1W zo)Z=WJ^_lsHW{tH&%Q;vj9sy`u8geckqwpgBJ`1LcnfL@<;yah!%rD_GtT^+q-$s% zOCI?mp88!y@xoJAhmVeFS1MEg{(gSN*K3$ew@|A;RpWzk4fW| z>DqahxR34N%pSY}S!hS+17Two1rofai}-b&!#|L+>I{c*KAuLW`Y^sPP+t*WlO!+w z)mzhTem(6!)BWUf&x(~ixDzM2$CnxX^~c|Qbmvy|v8r&qkkieN zl4D>;CVAiE^?w`_6%(cPdsI6ns=ssLxUp&MyPh`i^2(Vb$6v$9C5>OMp(f|0!G8}K zmhYx3E_QF;<#)TKd>megYdZcRE?RP`Rf`J-kAhX}uFbVqWrp7J`OS%M$BpyMmsDAk z+%^F>FbdtQ>lL_oTh~3pG?$mp!FhzG{s}w-ztV|L*k8}Fe2%A@2$nLz7xcFaMSn4w z_NQsL>Lr!(;z-}9+&uZzPOBN5m^W=&nwC%)9mwPE?>jeK^#mOx+JGXzr$h7&ECU`K zQ}SNw%eNjX`JA}g4~|udT~U$w6#ivub7G_E3N8+9zEK#6%-Y>zCBXkI70HaUXkFv~ z@m1tun&HJ1Y=~=z>oEUJ9#U&em6o3FQ(G+Jq3 z*XWW(re)A7amRSt%(B{n$0U7fY*8uDG|s=>b!F<3Ae2!x!ab{NR)nG0Y3dW`rt$A@ zj7U#K#r#+nn0K{R4cTiMTrtcK57fx2gk=23E)UHu;Rp?SXwqXb;CadK-#+!h;xGAw z_Mbl-7vD{7d|Nqn@UDzK!}vgdh2h@6rOq<@NEQ%q8N&=wry$pb{RrlLj3~hNAg}6?#z3}au~0~ z_O?XF>XxA4iyYfP|A%i^TTPR(xbCc)*7C3UQq%;1CPN7H6{a=Y#)|b>%p|7%cCBdY zh|cJ^=jU2tHs`(7gli1*?Lw%v`ei0t`xer{GjS+jzMzVv)>zc6xulp`7`on(+Mq+x zp?-a{hGpO}Xc63=WmQia`8C<#t?N0fdeET?1l^q@WqTNgj>*?j+><0E#c=$z zc#~tNrN;gGrwq8@!22f;J#vTJx;Ou@pUX)1kgOP|>MP}vT^{#0|GE_iWC+64%G2aJ zc#p3v;8wz#%gwmb}Ozos!rj0PrrbEuJ- znmfOXR7T*TyBbgJ%9^{ieELM6{4%~^SkCY)#Nfks{^D+D{vQnj%}nAy4z2FY^3=G^ zumV?%FGHk~z#LFQp%6&d>NSy4ScGmkIG1r1+vKcc+57X*_^x&aOH-KAHgD4?8f+@! zs46&l>5=q%ec{3y{~*WTEIDoxUzacb$Wsj^*(c<04ly1$I)~mgHLH;KdS)hIw(sZl z*0EX%MMP6fMACxTkqsHW{k&iHKm&n04t0%um_V3v%IWqgvx%p3)a0+Hvtbsmy zM`Mt1G3KhPJv9sD29_wkKqxxHg6)jD zZV@7R0rNowOiDW@nD@?S@5i*t>c_T!4&`#ADUOOeG#>1UmllZ}c57~wAhS7NFU^f; z0|PPV?=Y2bPV+0uCudTZCOfRnE|g&Vf{(N2+GZy7?ft+$=gkGTfEa+`tf-uq~!1dhB6b%!FZMqwPe|&qiH6L4#1JI{tfBCttDiRX~(c0vC@NOqcTbdsyFNDuNlNlKb; zBleIg`)lsXCZr?U>!k6hO|?@6Ow{d6qjuhJ^CL((tNxc6U!gDYL#3M5$QDr>xb!!X z`^mJ3swh0ea*&5fR3uPMYZYf_)Z5xOR8n;jNgpl%bUlxVmKmeu(BFTyKU*4^(=!q( z+dh#>zg)~WZKg2qYtcSCwEC0|Nq+(+lOK9FoQI=2rRP{dS%cfvjPx9Rt43|((mgdd z_9&bE4FInRFjeNP!CAi)+imwR(;Dm-f;XVS+CnuWM<9VlC5<$(v9g=fWz=Z}(@^{% z2^oIgQ4yG9oP+h0-|GK2(@B>}bCt76oac0)We_fFwW(#!CrPG$0jjP@E9YW+v)%TU zfe;y(f1piwk#5aPL7k9!-Ymx{jC4R4Z6+E_W zJfdcm_vCi3g=5QZ#j#(M!pToaO&5iX0Fa6|b9yp)9lk>1sol$SGq6lE%H@17sA}|B zr1cTMzcXtC(G%+QEv;$nH8l#hh)8nF;!|5z; zkKkyK^sbUh!cgDq!cykDD6tQs!##PB^n-BTr^of33qZSqDV4b~=5{2XUM>Bx0 z17J8Q2sI7HN{qiXv#y>gKU8tp{8?G*S7A9BN4|OZa5$;8j*Ba7{f(tOM3%xy-?GK= zMtWqzCo$XGsdx6lQpWKw`ja36-cDy%vl?jc)7wfa&yzSUxIbah9KNx+iZ;KSjppYI zIK8V2imRdI@GD=!=Oe?k89IC?ygnjg4L6oEa;gC~QUBdnH6O)TzZ z=W{Zu$p54+_@`>V!M?1vCh8o4kn8ge2Yc33Ve4bj45y(o_}RRpFUzIFn$nL&{RiD~ z-scvLw+{OnuL3>{NRhh*wy=VKUXXzMU;P$4%KqieGfZyuqdO~Bh{F+h(#q}`rntF6 zWoO;R!Lek)hcyIJ8XrC9ByxV4);iD;t(sR+cNpTf}NBqI;e=O+<=;5a^QSY_MGU z4Qwcj?4h?&qn`kc9V2CNpApMaNk3ngHc7f48sWkg5e7j72r)tc}IL70B?jwDtUx?+Rm_8ODSx+*+u6*eDU>6${#} z(zd(QYN_X^72Oqk$g_wW!y#b*2`x}CZNq#7-HViD=Q`_;^)4eIf}iw5o-`(NI-H)| zK^lIx5@5}Q#v`b~mb9GHkH_frfe`K+MSxXY#3)a zxfp9^JkSjn2s+Qq58qNmp4@(a$<+{iZZxdNBL808`}xyambOO;tq(d+OSQ<1f)ZQm zJdn}`hyWn?2h?&MLkD6kYpO;L*Yq2arbt3;{zv>bs<%>v`3g*q@VsuMwuXr@i*_~> zX1pBc*PkF%3cYLpE{eM4i#rDWUW3yH#cHuVroe20m77T~wbRR;>3(-LEB0D8rKZ+C zv*Q32$(#7I#FbN;<0GUJ{KHyn3$;cK>H?|o2uJepp2%XZ`>pmv9alS=3);JDec0qZ zBCtb3PVSr9dOnJ##=ut6)0?=h0)JHWdvX(eynlST>}eCn?wrtS+__phjNo^z*f9s@ zY2*0oC>a4P30q~LTIqANKd~`l9j7MUql_O;9sDsR^8CaGFzFjlt}hv8**F2;=KtisUyK z1nhUaPh+EyI?MG}E;M}RDSP>l3i_JZX4oJf6HsWAgW%txeVM&+o(En^o-zTqK~NVD zMGGp5bm~4bVtjWQ!e|(Ho{4$&q2_%GLl~-3ZWn|U`Hl&v_c{Aea7m0?D~A5IoH0y8 zYLqC12fF8paqR4(E!#>kuMDQ#V(0Pyk&)=_rz6vg56*B{i|r~jCi zPMH??qv9H1xp1iWZ3Q!9C{2kXnnIN>=Sj>@Ven=HML24KXx>Gv4+YsZX|Gzki>L#@ zm~-YI-6+Ccp_e9FV=`cs0)S>xt|Md~m~&2=Os8CLt)@mgUH=wH@}W4NoW4&t2mq9H z|Gfp^tM_^AY2+oft^#6FkuG59j^H|eeOt(fZz1&j8h;z0$ryb;4mjTGU3OUb#5Z@C z0+#k!d(|=nLCYwP*Z5~jh}y?si?42i%~)+o-Kz_IP?^Nt`^BbbYL6NzQmsq*lR z0M+syizq3?qU%5gjmCyoAclJp55I6M$S60kFAByQa&y*{CQRc-^PVJ(2jHTUJ&OX> zH(p+;=E7up1GR^{z{5Ym8K(DV3?$Q!P6)|B8T(dFg+6Wb#Hw8MwQ&3oK2;>&S~!Bi zUu`eW(Av$7WetJqky6L!t8ShefHUGT)8I-Da?Oe0^7{DvTmPxejdTs81S&49u{jwe z-^5qD-iXCNiOM|Z*daBIX$J^2fC15z2A_CIh0Gb1*%>iGyw6-*2|f%!=gxET9HP6W zM)ElV&Ld-gJJiwNI9@-M-F18HM!;2s>YJ`gxC=ajK-8qPU6{N|~>p{ZunH2U%v-;o>CYgY{77P^y{Sn z@bx&CED&`_GFtiyp8eVXlPWXP+jyg7j?Yp5H@{=o&{@#PmxfBYS_>Ofm<9)WFaPHd zF=FHx4~`4}^|FZhcp8bHZwsv>th27SI~2&>W5J3BT-pw>+k{L|u}b`*Q8F?$adOYyjJMT!5mtLt}u`b?*5D>X(@3IErg7u!N=`;`l0 z9=6GsNiu{#`b=$Z4q(r*SlYU(^Q;%wx?EH%@khcVXyx^U$^>c*NAO5Tn|s?-uXFKB zsXur4Qn3;CFsJxW&q>pHB^Ip-rgEN8;wfCbeQOfU4pF4Uhx?4TRH5c?*z1=`-VG2?2B2JXBF0UYrfW0eNF10kQh~R%EiQ4W8Yk?Wh~GF4W2IsX#aYa-VnqV^l$RbXT7odA4q0$r*4ZUk6 z$&u)7jZW10rjY3tsFMhVA9q?OPO)u&_L{;o<9P)t^XBXT?Y~YO=;^Y&++bW6)%obBmb*pNau@b&R#EG@V}l E54|&Q&;S4c literal 0 HcmV?d00001 diff --git a/outlineviewer/src/main/native/resources/ov-32.png b/outlineviewer/src/main/native/resources/ov-32.png new file mode 100644 index 0000000000000000000000000000000000000000..4db8e8b5891d16ff7cd1ffa03bbbc1beaa69681b GIT binary patch literal 1149 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 literal 0 HcmV?d00001 diff --git a/outlineviewer/src/main/native/win/outlineviewer.rc b/outlineviewer/src/main/native/win/outlineviewer.rc new file mode 100644 index 0000000000..85bdcae2ef --- /dev/null +++ b/outlineviewer/src/main/native/win/outlineviewer.rc @@ -0,0 +1 @@ +IDI_ICON1 ICON "outlineviewer.ico" diff --git a/settings.gradle b/settings.gradle index 89cba97761..0cd962f700 100644 --- a/settings.gradle +++ b/settings.gradle @@ -29,6 +29,7 @@ include 'wpilibjExamples' include 'wpilibjIntegrationTests' include 'wpilibj' include 'glass' +include 'outlineviewer' include 'simulation:gz_msgs' include 'simulation:frc_gazebo_plugins' include 'simulation:halsim_gazebo' From d3e45c297cd3e55103aaf4cca0e6f3586d8a7cea Mon Sep 17 00:00:00 2001 From: Prateek Machiraju Date: Fri, 19 Mar 2021 16:38:54 -0400 Subject: [PATCH 03/34] [wpimath] Make C++ geometry classes immutable (#3249) --- .../src/main/native/cpp/geometry/Pose2d.cpp | 6 --- .../main/native/cpp/geometry/Rotation2d.cpp | 14 ------ .../native/cpp/geometry/Translation2d.cpp | 22 --------- .../cpp/trajectory/TrajectoryGenerator.cpp | 2 +- .../main/native/include/frc/geometry/Pose2d.h | 12 ----- .../native/include/frc/geometry/Rotation2d.h | 24 ---------- .../include/frc/geometry/Translation2d.h | 46 ------------------- .../native/cpp/geometry/Rotation2dTest.cpp | 2 +- 8 files changed, 2 insertions(+), 126 deletions(-) diff --git a/wpimath/src/main/native/cpp/geometry/Pose2d.cpp b/wpimath/src/main/native/cpp/geometry/Pose2d.cpp index 887b43f4f5..d7e57c039d 100644 --- a/wpimath/src/main/native/cpp/geometry/Pose2d.cpp +++ b/wpimath/src/main/native/cpp/geometry/Pose2d.cpp @@ -20,12 +20,6 @@ Pose2d Pose2d::operator+(const Transform2d& other) const { return TransformBy(other); } -Pose2d& Pose2d::operator+=(const Transform2d& other) { - m_translation += other.Translation().RotateBy(m_rotation); - m_rotation += other.Rotation(); - return *this; -} - Transform2d Pose2d::operator-(const Pose2d& other) const { const auto pose = this->RelativeTo(other); return Transform2d(pose.Translation(), pose.Rotation()); diff --git a/wpimath/src/main/native/cpp/geometry/Rotation2d.cpp b/wpimath/src/main/native/cpp/geometry/Rotation2d.cpp index efcc520e4f..37b86f2dc6 100644 --- a/wpimath/src/main/native/cpp/geometry/Rotation2d.cpp +++ b/wpimath/src/main/native/cpp/geometry/Rotation2d.cpp @@ -38,24 +38,10 @@ Rotation2d Rotation2d::operator+(const Rotation2d& other) const { return RotateBy(other); } -Rotation2d& Rotation2d::operator+=(const Rotation2d& other) { - double cos = Cos() * other.Cos() - Sin() * other.Sin(); - double sin = Cos() * other.Sin() + Sin() * other.Cos(); - m_cos = cos; - m_sin = sin; - m_value = units::radian_t(std::atan2(m_sin, m_cos)); - return *this; -} - Rotation2d Rotation2d::operator-(const Rotation2d& other) const { return *this + -other; } -Rotation2d& Rotation2d::operator-=(const Rotation2d& other) { - *this += -other; - return *this; -} - Rotation2d Rotation2d::operator-() const { return Rotation2d(-m_value); } diff --git a/wpimath/src/main/native/cpp/geometry/Translation2d.cpp b/wpimath/src/main/native/cpp/geometry/Translation2d.cpp index 6323b60746..5e066bb40b 100644 --- a/wpimath/src/main/native/cpp/geometry/Translation2d.cpp +++ b/wpimath/src/main/native/cpp/geometry/Translation2d.cpp @@ -33,21 +33,10 @@ Translation2d Translation2d::operator+(const Translation2d& other) const { return {X() + other.X(), Y() + other.Y()}; } -Translation2d& Translation2d::operator+=(const Translation2d& other) { - m_x += other.m_x; - m_y += other.m_y; - return *this; -} - Translation2d Translation2d::operator-(const Translation2d& other) const { return *this + -other; } -Translation2d& Translation2d::operator-=(const Translation2d& other) { - *this += -other; - return *this; -} - Translation2d Translation2d::operator-() const { return {-m_x, -m_y}; } @@ -56,12 +45,6 @@ Translation2d Translation2d::operator*(double scalar) const { return {scalar * m_x, scalar * m_y}; } -Translation2d& Translation2d::operator*=(double scalar) { - m_x *= scalar; - m_y *= scalar; - return *this; -} - Translation2d Translation2d::operator/(double scalar) const { return *this * (1.0 / scalar); } @@ -75,11 +58,6 @@ bool Translation2d::operator!=(const Translation2d& other) const { return !operator==(other); } -Translation2d& Translation2d::operator/=(double scalar) { - *this *= (1.0 / scalar); - return *this; -} - void frc::to_json(wpi::json& json, const Translation2d& translation) { json = wpi::json{{"x", translation.X().to()}, {"y", translation.Y().to()}}; diff --git a/wpimath/src/main/native/cpp/trajectory/TrajectoryGenerator.cpp b/wpimath/src/main/native/cpp/trajectory/TrajectoryGenerator.cpp index 475c7c4257..73d23dcb9d 100644 --- a/wpimath/src/main/native/cpp/trajectory/TrajectoryGenerator.cpp +++ b/wpimath/src/main/native/cpp/trajectory/TrajectoryGenerator.cpp @@ -115,7 +115,7 @@ Trajectory TrajectoryGenerator::GenerateTrajectory( const Transform2d flip{Translation2d(), Rotation2d(180_deg)}; if (config.IsReversed()) { for (auto& waypoint : newWaypoints) { - waypoint += flip; + waypoint = waypoint + flip; } } diff --git a/wpimath/src/main/native/include/frc/geometry/Pose2d.h b/wpimath/src/main/native/include/frc/geometry/Pose2d.h index 357b29e991..310fd8116e 100644 --- a/wpimath/src/main/native/include/frc/geometry/Pose2d.h +++ b/wpimath/src/main/native/include/frc/geometry/Pose2d.h @@ -57,18 +57,6 @@ class Pose2d { */ Pose2d operator+(const Transform2d& other) const; - /** - * Transforms the current pose by the transformation. - * - * This is similar to the + operator, except that it mutates the current - * object. - * - * @param other The transform to transform the pose by. - * - * @return Reference to the new mutated object. - */ - Pose2d& operator+=(const Transform2d& other); - /** * Returns the Transform2d that maps the one pose to another. * diff --git a/wpimath/src/main/native/include/frc/geometry/Rotation2d.h b/wpimath/src/main/native/include/frc/geometry/Rotation2d.h index 1748e60d9a..9f6d1b02a1 100644 --- a/wpimath/src/main/native/include/frc/geometry/Rotation2d.h +++ b/wpimath/src/main/native/include/frc/geometry/Rotation2d.h @@ -59,18 +59,6 @@ class Rotation2d { */ Rotation2d operator+(const Rotation2d& other) const; - /** - * Adds a rotation to the current rotation. - * - * This is similar to the + operator except that it mutates the current - * object. - * - * @param other The rotation to add. - * - * @return The reference to the new mutated object. - */ - Rotation2d& operator+=(const Rotation2d& other); - /** * Subtracts the new rotation from the current rotation and returns the new * rotation. @@ -84,18 +72,6 @@ class Rotation2d { */ Rotation2d operator-(const Rotation2d& other) const; - /** - * Subtracts the new rotation from the current rotation. - * - * This is similar to the - operator except that it mutates the current - * object. - * - * @param other The rotation to subtract. - * - * @return The reference to the new mutated object. - */ - Rotation2d& operator-=(const Rotation2d& other); - /** * Takes the inverse of the current rotation. This is simply the negative of * the current angular value. diff --git a/wpimath/src/main/native/include/frc/geometry/Translation2d.h b/wpimath/src/main/native/include/frc/geometry/Translation2d.h index 57532d2687..f49cd6385a 100644 --- a/wpimath/src/main/native/include/frc/geometry/Translation2d.h +++ b/wpimath/src/main/native/include/frc/geometry/Translation2d.h @@ -110,18 +110,6 @@ class Translation2d { */ Translation2d operator+(const Translation2d& other) const; - /** - * Adds the new translation to the current translation. - * - * This is similar to the + operator, except that the current object is - * mutated. - * - * @param other The translation to add. - * - * @return The reference to the new mutated object. - */ - Translation2d& operator+=(const Translation2d& other); - /** * Subtracts the other translation from the other translation and returns the * difference. @@ -135,18 +123,6 @@ class Translation2d { */ Translation2d operator-(const Translation2d& other) const; - /** - * Subtracts the new translation from the current translation. - * - * This is similar to the - operator, except that the current object is - * mutated. - * - * @param other The translation to subtract. - * - * @return The reference to the new mutated object. - */ - Translation2d& operator-=(const Translation2d& other); - /** * Returns the inverse of the current translation. This is equivalent to * rotating by 180 degrees, flipping the point over both axes, or simply @@ -167,17 +143,6 @@ class Translation2d { */ Translation2d operator*(double scalar) const; - /** - * Multiplies the current translation by a scalar. - * - * This is similar to the * operator, except that current object is mutated. - * - * @param scalar The scalar to multiply by. - * - * @return The reference to the new mutated object. - */ - Translation2d& operator*=(double scalar); - /** * Divides the translation by a scalar and returns the new translation. * @@ -205,17 +170,6 @@ class Translation2d { */ bool operator!=(const Translation2d& other) const; - /* - * Divides the current translation by a scalar. - * - * This is similar to the / operator, except that current object is mutated. - * - * @param scalar The scalar to divide by. - * - * @return The reference to the new mutated object. - */ - Translation2d& operator/=(double scalar); - private: units::meter_t m_x = 0_m; units::meter_t m_y = 0_m; diff --git a/wpimath/src/test/native/cpp/geometry/Rotation2dTest.cpp b/wpimath/src/test/native/cpp/geometry/Rotation2dTest.cpp index 15d683310f..0597cd3218 100644 --- a/wpimath/src/test/native/cpp/geometry/Rotation2dTest.cpp +++ b/wpimath/src/test/native/cpp/geometry/Rotation2dTest.cpp @@ -39,7 +39,7 @@ TEST(Rotation2dTest, RotateByFromZero) { TEST(Rotation2dTest, RotateByNonZero) { auto rot = Rotation2d(90.0_deg); - rot += Rotation2d(30.0_deg); + rot = rot + Rotation2d(30.0_deg); EXPECT_NEAR(rot.Degrees().to(), 120.0, kEpsilon); } From 8afa596fdff829ba4d13c932c93503d31e56a5d5 Mon Sep 17 00:00:00 2001 From: Peter Johnson Date: Fri, 19 Mar 2021 13:41:11 -0700 Subject: [PATCH 04/34] [wpilib] Remove deprecated Sendable functions and SendableBase (#3210) --- .../first/wpilibj2/command/CommandBase.java | 3 - .../first/wpilibj2/command/SubsystemBase.java | 4 - .../wpi/first/wpilibj/command/Command.java | 5 +- .../wpi/first/wpilibj/command/Subsystem.java | 4 - .../cpp/smartdashboard/SendableBase.cpp | 17 ---- .../include/frc/smartdashboard/SendableBase.h | 27 ------ .../shuffleboard/MockActuatorSendable.h | 4 +- .../java/edu/wpi/first/wpilibj/Sendable.java | 93 ------------------- .../edu/wpi/first/wpilibj/SendableBase.java | 39 -------- 9 files changed, 3 insertions(+), 193 deletions(-) delete mode 100644 wpilibc/src/main/native/cpp/smartdashboard/SendableBase.cpp delete mode 100644 wpilibc/src/main/native/include/frc/smartdashboard/SendableBase.h delete mode 100644 wpilibj/src/main/java/edu/wpi/first/wpilibj/SendableBase.java diff --git a/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/CommandBase.java b/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/CommandBase.java index fa9b26da48..8c1e8b10de 100644 --- a/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/CommandBase.java +++ b/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/CommandBase.java @@ -45,7 +45,6 @@ public abstract class CommandBase implements Sendable, Command { * * @param name name */ - @Override public void setName(String name) { SendableRegistry.setName(this, name); } @@ -66,7 +65,6 @@ public abstract class CommandBase implements Sendable, Command { * * @return Subsystem name */ - @Override public String getSubsystem() { return SendableRegistry.getSubsystem(this); } @@ -76,7 +74,6 @@ public abstract class CommandBase implements Sendable, Command { * * @param subsystem subsystem name */ - @Override public void setSubsystem(String subsystem) { SendableRegistry.setSubsystem(this, subsystem); } diff --git a/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/SubsystemBase.java b/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/SubsystemBase.java index d61ed5baba..f0a0fae6d5 100644 --- a/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/SubsystemBase.java +++ b/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/SubsystemBase.java @@ -27,7 +27,6 @@ public abstract class SubsystemBase implements Subsystem, Sendable { * * @return Name */ - @Override public String getName() { return SendableRegistry.getName(this); } @@ -37,7 +36,6 @@ public abstract class SubsystemBase implements Subsystem, Sendable { * * @param name name */ - @Override public void setName(String name) { SendableRegistry.setName(this, name); } @@ -47,7 +45,6 @@ public abstract class SubsystemBase implements Subsystem, Sendable { * * @return Subsystem name */ - @Override public String getSubsystem() { return SendableRegistry.getSubsystem(this); } @@ -57,7 +54,6 @@ public abstract class SubsystemBase implements Subsystem, Sendable { * * @param subsystem subsystem name */ - @Override public void setSubsystem(String subsystem) { SendableRegistry.setSubsystem(this, subsystem); } diff --git a/wpilibOldCommands/src/main/java/edu/wpi/first/wpilibj/command/Command.java b/wpilibOldCommands/src/main/java/edu/wpi/first/wpilibj/command/Command.java index 5bb49a3800..9b8c41cf4c 100644 --- a/wpilibOldCommands/src/main/java/edu/wpi/first/wpilibj/command/Command.java +++ b/wpilibOldCommands/src/main/java/edu/wpi/first/wpilibj/command/Command.java @@ -36,6 +36,7 @@ import java.util.Enumeration; * @see CommandGroup * @see IllegalUseOfCommandException */ +@SuppressWarnings("PMD.GodClass") public abstract class Command implements Sendable, AutoCloseable { /** The time since this command was initialized. */ private double m_startTime = -1; @@ -561,7 +562,6 @@ public abstract class Command implements Sendable, AutoCloseable { * * @return Name */ - @Override public String getName() { return SendableRegistry.getName(this); } @@ -571,7 +571,6 @@ public abstract class Command implements Sendable, AutoCloseable { * * @param name name */ - @Override public void setName(String name) { SendableRegistry.setName(this, name); } @@ -581,7 +580,6 @@ public abstract class Command implements Sendable, AutoCloseable { * * @return Subsystem name */ - @Override public String getSubsystem() { return SendableRegistry.getSubsystem(this); } @@ -591,7 +589,6 @@ public abstract class Command implements Sendable, AutoCloseable { * * @param subsystem subsystem name */ - @Override public void setSubsystem(String subsystem) { SendableRegistry.setSubsystem(this, subsystem); } diff --git a/wpilibOldCommands/src/main/java/edu/wpi/first/wpilibj/command/Subsystem.java b/wpilibOldCommands/src/main/java/edu/wpi/first/wpilibj/command/Subsystem.java index 8f61663293..f596ed68f4 100644 --- a/wpilibOldCommands/src/main/java/edu/wpi/first/wpilibj/command/Subsystem.java +++ b/wpilibOldCommands/src/main/java/edu/wpi/first/wpilibj/command/Subsystem.java @@ -187,7 +187,6 @@ public abstract class Subsystem implements Sendable, AutoCloseable { * * @return Name */ - @Override public String getName() { return SendableRegistry.getName(this); } @@ -197,7 +196,6 @@ public abstract class Subsystem implements Sendable, AutoCloseable { * * @param name name */ - @Override public void setName(String name) { SendableRegistry.setName(this, name); } @@ -207,7 +205,6 @@ public abstract class Subsystem implements Sendable, AutoCloseable { * * @return Subsystem name */ - @Override public String getSubsystem() { return SendableRegistry.getSubsystem(this); } @@ -217,7 +214,6 @@ public abstract class Subsystem implements Sendable, AutoCloseable { * * @param subsystem subsystem name */ - @Override public void setSubsystem(String subsystem) { SendableRegistry.setSubsystem(this, subsystem); } diff --git a/wpilibc/src/main/native/cpp/smartdashboard/SendableBase.cpp b/wpilibc/src/main/native/cpp/smartdashboard/SendableBase.cpp deleted file mode 100644 index 2b92c0f531..0000000000 --- a/wpilibc/src/main/native/cpp/smartdashboard/SendableBase.cpp +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright (c) FIRST and other WPILib contributors. -// Open Source Software; you can modify and/or share it under the terms of -// the WPILib BSD license file in the root directory of this project. - -#include "frc/smartdashboard/SendableBase.h" - -#include "frc/smartdashboard/SendableRegistry.h" - -using namespace frc; - -SendableBase::SendableBase(bool addLiveWindow) { - if (addLiveWindow) { - SendableRegistry::GetInstance().AddLW(this, ""); - } else { - SendableRegistry::GetInstance().Add(this, ""); - } -} diff --git a/wpilibc/src/main/native/include/frc/smartdashboard/SendableBase.h b/wpilibc/src/main/native/include/frc/smartdashboard/SendableBase.h deleted file mode 100644 index 7f50d0ad07..0000000000 --- a/wpilibc/src/main/native/include/frc/smartdashboard/SendableBase.h +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright (c) FIRST and other WPILib contributors. -// Open Source Software; you can modify and/or share it under the terms of -// the WPILib BSD license file in the root directory of this project. - -#pragma once - -#include - -#include "frc/smartdashboard/Sendable.h" -#include "frc/smartdashboard/SendableHelper.h" - -namespace frc { - -class SendableBase : public Sendable, public SendableHelper { - public: - /** - * Creates an instance of the sensor base - * - * @deprecated use Sendable and SendableHelper - * - * @param addLiveWindow if true, add this Sendable to LiveWindow - */ - WPI_DEPRECATED("use Sendable and SendableHelper") - explicit SendableBase(bool addLiveWindow = true); -}; - -} // namespace frc diff --git a/wpilibcIntegrationTests/src/main/native/include/shuffleboard/MockActuatorSendable.h b/wpilibcIntegrationTests/src/main/native/include/shuffleboard/MockActuatorSendable.h index 76f7c7cf40..f59cc956bd 100644 --- a/wpilibcIntegrationTests/src/main/native/include/shuffleboard/MockActuatorSendable.h +++ b/wpilibcIntegrationTests/src/main/native/include/shuffleboard/MockActuatorSendable.h @@ -6,7 +6,7 @@ #include -#include "frc/smartdashboard/SendableBase.h" +#include "frc/smartdashboard/Sendable.h" #include "frc/smartdashboard/SendableBuilder.h" namespace frc { @@ -14,7 +14,7 @@ namespace frc { /** * A mock sendable that marks itself as an actuator. */ -class MockActuatorSendable : public SendableBase { +class MockActuatorSendable : public Sendable { public: explicit MockActuatorSendable(wpi::StringRef name); diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/Sendable.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/Sendable.java index 84484ff8c5..eb57341c35 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/Sendable.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/Sendable.java @@ -5,102 +5,9 @@ package edu.wpi.first.wpilibj; import edu.wpi.first.wpilibj.smartdashboard.SendableBuilder; -import edu.wpi.first.wpilibj.smartdashboard.SendableRegistry; /** The base interface for objects that can be sent over the network through network tables. */ public interface Sendable { - /** - * Gets the name of this {@link Sendable} object. - * - * @return Name - * @deprecated Use SendableRegistry.getName() - */ - @Deprecated(since = "2020", forRemoval = true) - default String getName() { - return SendableRegistry.getName(this); - } - - /** - * Sets the name of this {@link Sendable} object. - * - * @param name name - * @deprecated Use SendableRegistry.setName() - */ - @Deprecated(since = "2020", forRemoval = true) - default void setName(String name) { - SendableRegistry.setName(this, name); - } - - /** - * Sets both the subsystem name and device name of this {@link Sendable} object. - * - * @param subsystem subsystem name - * @param name device name - * @deprecated Use SendableRegistry.setName() - */ - @Deprecated(since = "2020", forRemoval = true) - default void setName(String subsystem, String name) { - SendableRegistry.setName(this, subsystem, name); - } - - /** - * Sets the name of the sensor with a channel number. - * - * @param moduleType A string that defines the module name in the label for the value - * @param channel The channel number the device is plugged into - * @deprecated Use SendableRegistry.setName() - */ - @Deprecated(since = "2020", forRemoval = true) - default void setName(String moduleType, int channel) { - SendableRegistry.setName(this, moduleType, channel); - } - - /** - * Sets the name of the sensor with a module and channel number. - * - * @param moduleType A string that defines the module name in the label for the value - * @param moduleNumber The number of the particular module type - * @param channel The channel number the device is plugged into (usually PWM) - * @deprecated Use SendableRegistry.setName() - */ - @Deprecated(since = "2020", forRemoval = true) - default void setName(String moduleType, int moduleNumber, int channel) { - SendableRegistry.setName(this, moduleType, moduleNumber, channel); - } - - /** - * Gets the subsystem name of this {@link Sendable} object. - * - * @return Subsystem name - * @deprecated Use SendableRegistry.getSubsystem() - */ - @Deprecated(since = "2020", forRemoval = true) - default String getSubsystem() { - return SendableRegistry.getSubsystem(this); - } - - /** - * Sets the subsystem name of this {@link Sendable} object. - * - * @param subsystem subsystem name - * @deprecated Use SendableRegistry.setSubsystem() - */ - @Deprecated(since = "2020", forRemoval = true) - default void setSubsystem(String subsystem) { - SendableRegistry.setSubsystem(this, subsystem); - } - - /** - * Add a child component. - * - * @param child child component - * @deprecated Use SendableRegistry.addChild() - */ - @Deprecated(since = "2020", forRemoval = true) - default void addChild(Object child) { - SendableRegistry.addChild(this, child); - } - /** * Initializes this {@link Sendable} object. * diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/SendableBase.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/SendableBase.java deleted file mode 100644 index 44b1d5080e..0000000000 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/SendableBase.java +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright (c) FIRST and other WPILib contributors. -// Open Source Software; you can modify and/or share it under the terms of -// the WPILib BSD license file in the root directory of this project. - -package edu.wpi.first.wpilibj; - -import edu.wpi.first.wpilibj.smartdashboard.SendableRegistry; - -/** - * Base class for all sensors. Stores most recent status information as well as containing utility - * functions for checking channels and error processing. - * - * @deprecated Use Sendable and SendableRegistry - */ -@Deprecated(since = "2020", forRemoval = true) -public abstract class SendableBase implements Sendable, AutoCloseable { - /** Creates an instance of the sensor base. */ - public SendableBase() { - this(true); - } - - /** - * Creates an instance of the sensor base. - * - * @param addLiveWindow if true, add this Sendable to LiveWindow - */ - public SendableBase(boolean addLiveWindow) { - if (addLiveWindow) { - SendableRegistry.addLW(this, ""); - } else { - SendableRegistry.add(this, ""); - } - } - - @Override - public void close() { - SendableRegistry.remove(this); - } -} From 48e9f395136a7989e36fa1e6c69380ce58298046 Mon Sep 17 00:00:00 2001 From: Peter Johnson Date: Fri, 19 Mar 2021 13:51:53 -0700 Subject: [PATCH 05/34] [wpilibj] Remove wpilibj package CameraServer (#3213) --- .../edu/wpi/first/wpilibj/CameraServer.java | 814 ------------------ 1 file changed, 814 deletions(-) delete mode 100644 wpilibj/src/main/java/edu/wpi/first/wpilibj/CameraServer.java diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/CameraServer.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/CameraServer.java deleted file mode 100644 index 890cc5a582..0000000000 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/CameraServer.java +++ /dev/null @@ -1,814 +0,0 @@ -// Copyright (c) FIRST and other WPILib contributors. -// Open Source Software; you can modify and/or share it under the terms of -// the WPILib BSD license file in the root directory of this project. - -package edu.wpi.first.wpilibj; - -import edu.wpi.cscore.AxisCamera; -import edu.wpi.cscore.CameraServerJNI; -import edu.wpi.cscore.CvSink; -import edu.wpi.cscore.CvSource; -import edu.wpi.cscore.MjpegServer; -import edu.wpi.cscore.UsbCamera; -import edu.wpi.cscore.VideoEvent; -import edu.wpi.cscore.VideoException; -import edu.wpi.cscore.VideoListener; -import edu.wpi.cscore.VideoMode; -import edu.wpi.cscore.VideoMode.PixelFormat; -import edu.wpi.cscore.VideoProperty; -import edu.wpi.cscore.VideoSink; -import edu.wpi.cscore.VideoSource; -import edu.wpi.first.cameraserver.CameraServerSharedStore; -import edu.wpi.first.networktables.EntryListenerFlags; -import edu.wpi.first.networktables.NetworkTable; -import edu.wpi.first.networktables.NetworkTableEntry; -import edu.wpi.first.networktables.NetworkTableInstance; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Hashtable; -import java.util.Map; -import java.util.concurrent.atomic.AtomicInteger; - -/** - * Singleton class for creating and keeping camera servers. Also publishes camera information to - * NetworkTables. - * - * @deprecated Replaced with edu.wpi.first.cameraserver.CameraServer - */ -@Deprecated -public final class CameraServer { - public static final int kBasePort = 1181; - - @Deprecated public static final int kSize640x480 = 0; - @Deprecated public static final int kSize320x240 = 1; - @Deprecated public static final int kSize160x120 = 2; - - private static final String kPublishName = "/CameraPublisher"; - private static CameraServer server; - - /** Get the CameraServer instance. */ - public static synchronized CameraServer getInstance() { - if (server == null) { - server = new CameraServer(); - } - return server; - } - - private final AtomicInteger m_defaultUsbDevice; - private String m_primarySourceName; - private final Map m_sources; - private final Map m_sinks; - private final Map m_tables; // indexed by source handle - private final NetworkTable m_publishTable; - private final VideoListener m_videoListener; // NOPMD - private final int m_tableListener; // NOPMD - private int m_nextPort; - private String[] m_addresses; - - @SuppressWarnings("MissingJavadocMethod") - private static String makeSourceValue(int source) { - switch (VideoSource.getKindFromInt(CameraServerJNI.getSourceKind(source))) { - case kUsb: - return "usb:" + CameraServerJNI.getUsbCameraPath(source); - case kHttp: - { - String[] urls = CameraServerJNI.getHttpCameraUrls(source); - if (urls.length > 0) { - return "ip:" + urls[0]; - } else { - return "ip:"; - } - } - case kCv: - // FIXME: Should be "cv:", but LabVIEW dashboard requires "usb:". - // https://github.com/wpilibsuite/allwpilib/issues/407 - return "usb:"; - default: - return "unknown:"; - } - } - - @SuppressWarnings("MissingJavadocMethod") - private static String makeStreamValue(String address, int port) { - return "mjpg:http://" + address + ":" + port + "/?action=stream"; - } - - @SuppressWarnings({"MissingJavadocMethod", "PMD.AvoidUsingHardCodedIP"}) - private synchronized String[] getSinkStreamValues(int sink) { - // Ignore all but MjpegServer - if (VideoSink.getKindFromInt(CameraServerJNI.getSinkKind(sink)) != VideoSink.Kind.kMjpeg) { - return new String[0]; - } - - // Get port - int port = CameraServerJNI.getMjpegServerPort(sink); - - // Generate values - ArrayList values = new ArrayList<>(m_addresses.length + 1); - String listenAddress = CameraServerJNI.getMjpegServerListenAddress(sink); - if (!listenAddress.isEmpty()) { - // If a listen address is specified, only use that - values.add(makeStreamValue(listenAddress, port)); - } else { - // Otherwise generate for hostname and all interface addresses - values.add(makeStreamValue(CameraServerJNI.getHostname() + ".local", port)); - for (String addr : m_addresses) { - if ("127.0.0.1".equals(addr)) { - continue; // ignore localhost - } - values.add(makeStreamValue(addr, port)); - } - } - - return values.toArray(new String[0]); - } - - @SuppressWarnings({"MissingJavadocMethod", "PMD.AvoidUsingHardCodedIP"}) - private synchronized String[] getSourceStreamValues(int source) { - // Ignore all but HttpCamera - if (VideoSource.getKindFromInt(CameraServerJNI.getSourceKind(source)) - != VideoSource.Kind.kHttp) { - return new String[0]; - } - - // Generate values - String[] values = CameraServerJNI.getHttpCameraUrls(source); - for (int j = 0; j < values.length; j++) { - values[j] = "mjpg:" + values[j]; - } - - if (CameraServerSharedStore.getCameraServerShared().isRoboRIO()) { - // Look to see if we have a passthrough server for this source - // Only do this on the roboRIO - for (VideoSink i : m_sinks.values()) { - int sink = i.getHandle(); - int sinkSource = CameraServerJNI.getSinkSource(sink); - if (source == sinkSource - && VideoSink.getKindFromInt(CameraServerJNI.getSinkKind(sink)) - == VideoSink.Kind.kMjpeg) { - // Add USB-only passthrough - String[] finalValues = Arrays.copyOf(values, values.length + 1); - int port = CameraServerJNI.getMjpegServerPort(sink); - finalValues[values.length] = makeStreamValue("172.22.11.2", port); - return finalValues; - } - } - } - - return values; - } - - @SuppressWarnings({"MissingJavadocMethod", "PMD.AvoidUsingHardCodedIP"}) - private synchronized void updateStreamValues() { - // Over all the sinks... - for (VideoSink i : m_sinks.values()) { - int sink = i.getHandle(); - - // Get the source's subtable (if none exists, we're done) - int source = CameraServerJNI.getSinkSource(sink); - if (source == 0) { - continue; - } - NetworkTable table = m_tables.get(source); - if (table != null) { - // Don't set stream values if this is a HttpCamera passthrough - if (VideoSource.getKindFromInt(CameraServerJNI.getSourceKind(source)) - == VideoSource.Kind.kHttp) { - continue; - } - - // Set table value - String[] values = getSinkStreamValues(sink); - if (values.length > 0) { - table.getEntry("streams").setStringArray(values); - } - } - } - - // Over all the sources... - for (VideoSource i : m_sources.values()) { - int source = i.getHandle(); - - // Get the source's subtable (if none exists, we're done) - NetworkTable table = m_tables.get(source); - if (table != null) { - // Set table value - String[] values = getSourceStreamValues(source); - if (values.length > 0) { - table.getEntry("streams").setStringArray(values); - } - } - } - } - - @SuppressWarnings("MissingJavadocMethod") - private static String pixelFormatToString(PixelFormat pixelFormat) { - switch (pixelFormat) { - case kMJPEG: - return "MJPEG"; - case kYUYV: - return "YUYV"; - case kRGB565: - return "RGB565"; - case kBGR: - return "BGR"; - case kGray: - return "Gray"; - default: - return "Unknown"; - } - } - - /// Provide string description of video mode. - /// The returned string is "{width}x{height} {format} {fps} fps". - @SuppressWarnings("MissingJavadocMethod") - private static String videoModeToString(VideoMode mode) { - return mode.width - + "x" - + mode.height - + " " - + pixelFormatToString(mode.pixelFormat) - + " " - + mode.fps - + " fps"; - } - - @SuppressWarnings("MissingJavadocMethod") - private static String[] getSourceModeValues(int sourceHandle) { - VideoMode[] modes = CameraServerJNI.enumerateSourceVideoModes(sourceHandle); - String[] modeStrings = new String[modes.length]; - for (int i = 0; i < modes.length; i++) { - modeStrings[i] = videoModeToString(modes[i]); - } - return modeStrings; - } - - @SuppressWarnings({"MissingJavadocMethod", "PMD.CyclomaticComplexity"}) - private static void putSourcePropertyValue(NetworkTable table, VideoEvent event, boolean isNew) { - String name; - String infoName; - if (event.name.startsWith("raw_")) { - name = "RawProperty/" + event.name; - infoName = "RawPropertyInfo/" + event.name; - } else { - name = "Property/" + event.name; - infoName = "PropertyInfo/" + event.name; - } - - NetworkTableEntry entry = table.getEntry(name); - try { - switch (event.propertyKind) { - case kBoolean: - if (isNew) { - entry.setDefaultBoolean(event.value != 0); - } else { - entry.setBoolean(event.value != 0); - } - break; - case kInteger: - case kEnum: - if (isNew) { - entry.setDefaultDouble(event.value); - table - .getEntry(infoName + "/min") - .setDouble(CameraServerJNI.getPropertyMin(event.propertyHandle)); - table - .getEntry(infoName + "/max") - .setDouble(CameraServerJNI.getPropertyMax(event.propertyHandle)); - table - .getEntry(infoName + "/step") - .setDouble(CameraServerJNI.getPropertyStep(event.propertyHandle)); - table - .getEntry(infoName + "/default") - .setDouble(CameraServerJNI.getPropertyDefault(event.propertyHandle)); - } else { - entry.setDouble(event.value); - } - break; - case kString: - if (isNew) { - entry.setDefaultString(event.valueStr); - } else { - entry.setString(event.valueStr); - } - break; - default: - break; - } - } catch (VideoException ignored) { - // ignore - } - } - - @SuppressWarnings({ - "MissingJavadocMethod", - "PMD.UnusedLocalVariable", - "PMD.ExcessiveMethodLength", - "PMD.NPathComplexity" - }) - private CameraServer() { - m_defaultUsbDevice = new AtomicInteger(); - m_sources = new Hashtable<>(); - m_sinks = new Hashtable<>(); - m_tables = new Hashtable<>(); - m_publishTable = NetworkTableInstance.getDefault().getTable(kPublishName); - m_nextPort = kBasePort; - m_addresses = new String[0]; - - // We publish sources to NetworkTables using the following structure: - // "/CameraPublisher/{Source.Name}/" - root - // - "source" (string): Descriptive, prefixed with type (e.g. "usb:0") - // - "streams" (string array): URLs that can be used to stream data - // - "description" (string): Description of the source - // - "connected" (boolean): Whether source is connected - // - "mode" (string): Current video mode - // - "modes" (string array): Available video modes - // - "Property/{Property}" - Property values - // - "PropertyInfo/{Property}" - Property supporting information - - // Listener for video events - m_videoListener = - new VideoListener( - event -> { - switch (event.kind) { - case kSourceCreated: - { - // Create subtable for the camera - NetworkTable table = m_publishTable.getSubTable(event.name); - m_tables.put(event.sourceHandle, table); - table.getEntry("source").setString(makeSourceValue(event.sourceHandle)); - table - .getEntry("description") - .setString(CameraServerJNI.getSourceDescription(event.sourceHandle)); - table - .getEntry("connected") - .setBoolean(CameraServerJNI.isSourceConnected(event.sourceHandle)); - table - .getEntry("streams") - .setStringArray(getSourceStreamValues(event.sourceHandle)); - try { - VideoMode mode = CameraServerJNI.getSourceVideoMode(event.sourceHandle); - table.getEntry("mode").setDefaultString(videoModeToString(mode)); - table - .getEntry("modes") - .setStringArray(getSourceModeValues(event.sourceHandle)); - } catch (VideoException ignored) { - // Do nothing. Let the other event handlers update this if there is an error. - } - break; - } - case kSourceDestroyed: - { - NetworkTable table = m_tables.get(event.sourceHandle); - if (table != null) { - table.getEntry("source").setString(""); - table.getEntry("streams").setStringArray(new String[0]); - table.getEntry("modes").setStringArray(new String[0]); - } - break; - } - case kSourceConnected: - { - NetworkTable table = m_tables.get(event.sourceHandle); - if (table != null) { - // update the description too (as it may have changed) - table - .getEntry("description") - .setString(CameraServerJNI.getSourceDescription(event.sourceHandle)); - table.getEntry("connected").setBoolean(true); - } - break; - } - case kSourceDisconnected: - { - NetworkTable table = m_tables.get(event.sourceHandle); - if (table != null) { - table.getEntry("connected").setBoolean(false); - } - break; - } - case kSourceVideoModesUpdated: - { - NetworkTable table = m_tables.get(event.sourceHandle); - if (table != null) { - table - .getEntry("modes") - .setStringArray(getSourceModeValues(event.sourceHandle)); - } - break; - } - case kSourceVideoModeChanged: - { - NetworkTable table = m_tables.get(event.sourceHandle); - if (table != null) { - table.getEntry("mode").setString(videoModeToString(event.mode)); - } - break; - } - case kSourcePropertyCreated: - { - NetworkTable table = m_tables.get(event.sourceHandle); - if (table != null) { - putSourcePropertyValue(table, event, true); - } - break; - } - case kSourcePropertyValueUpdated: - { - NetworkTable table = m_tables.get(event.sourceHandle); - if (table != null) { - putSourcePropertyValue(table, event, false); - } - break; - } - case kSourcePropertyChoicesUpdated: - { - NetworkTable table = m_tables.get(event.sourceHandle); - if (table != null) { - try { - String[] choices = - CameraServerJNI.getEnumPropertyChoices(event.propertyHandle); - table - .getEntry("PropertyInfo/" + event.name + "/choices") - .setStringArray(choices); - } catch (VideoException ignored) { - // ignore - } - } - break; - } - case kSinkSourceChanged: - case kSinkCreated: - case kSinkDestroyed: - case kNetworkInterfacesChanged: - { - m_addresses = CameraServerJNI.getNetworkInterfaces(); - updateStreamValues(); - break; - } - default: - break; - } - }, - 0x4fff, - true); - - // Listener for NetworkTable events - // We don't currently support changing settings via NT due to - // synchronization issues, so just update to current setting if someone - // else tries to change it. - m_tableListener = - NetworkTableInstance.getDefault() - .addEntryListener( - kPublishName + "/", - event -> { - String relativeKey = event.name.substring(kPublishName.length() + 1); - - // get source (sourceName/...) - int subKeyIndex = relativeKey.indexOf('/'); - if (subKeyIndex == -1) { - return; - } - String sourceName = relativeKey.substring(0, subKeyIndex); - VideoSource source = m_sources.get(sourceName); - if (source == null) { - return; - } - - // get subkey - relativeKey = relativeKey.substring(subKeyIndex + 1); - - // handle standard names - String propName; - if ("mode".equals(relativeKey)) { - // reset to current mode - event.getEntry().setString(videoModeToString(source.getVideoMode())); - return; - } else if (relativeKey.startsWith("Property/")) { - propName = relativeKey.substring(9); - } else if (relativeKey.startsWith("RawProperty/")) { - propName = relativeKey.substring(12); - } else { - return; // ignore - } - - // everything else is a property - VideoProperty property = source.getProperty(propName); - switch (property.getKind()) { - case kNone: - return; - case kBoolean: - // reset to current setting - event.getEntry().setBoolean(property.get() != 0); - return; - case kInteger: - case kEnum: - // reset to current setting - event.getEntry().setDouble(property.get()); - return; - case kString: - // reset to current setting - event.getEntry().setString(property.getString()); - return; - default: - return; - } - }, - EntryListenerFlags.kImmediate | EntryListenerFlags.kUpdate); - } - - /** - * Start automatically capturing images to send to the dashboard. - * - *

You should call this method to see a camera feed on the dashboard. If you also want to - * perform vision processing on the roboRIO, use getVideo() to get access to the camera images. - * - *

The first time this overload is called, it calls {@link #startAutomaticCapture(int)} with - * device 0, creating a camera named "USB Camera 0". Subsequent calls increment the device number - * (e.g. 1, 2, etc). - */ - public UsbCamera startAutomaticCapture() { - UsbCamera camera = startAutomaticCapture(m_defaultUsbDevice.getAndIncrement()); - CameraServerSharedStore.getCameraServerShared().reportUsbCamera(camera.getHandle()); - return camera; - } - - /** - * Start automatically capturing images to send to the dashboard. - * - *

This overload calls {@link #startAutomaticCapture(String, int)} with a name of "USB Camera - * {dev}". - * - * @param dev The device number of the camera interface - */ - public UsbCamera startAutomaticCapture(int dev) { - UsbCamera camera = new UsbCamera("USB Camera " + dev, dev); - startAutomaticCapture(camera); - CameraServerSharedStore.getCameraServerShared().reportUsbCamera(camera.getHandle()); - return camera; - } - - /** - * Start automatically capturing images to send to the dashboard. - * - * @param name The name to give the camera - * @param dev The device number of the camera interface - */ - public UsbCamera startAutomaticCapture(String name, int dev) { - UsbCamera camera = new UsbCamera(name, dev); - startAutomaticCapture(camera); - CameraServerSharedStore.getCameraServerShared().reportUsbCamera(camera.getHandle()); - return camera; - } - - /** - * Start automatically capturing images to send to the dashboard. - * - * @param name The name to give the camera - * @param path The device path (e.g. "/dev/video0") of the camera - */ - public UsbCamera startAutomaticCapture(String name, String path) { - UsbCamera camera = new UsbCamera(name, path); - startAutomaticCapture(camera); - CameraServerSharedStore.getCameraServerShared().reportUsbCamera(camera.getHandle()); - return camera; - } - - /** - * Start automatically capturing images to send to the dashboard from an existing camera. - * - * @param camera Camera - */ - public void startAutomaticCapture(VideoSource camera) { - addCamera(camera); - VideoSink server = addServer("serve_" + camera.getName()); - server.setSource(camera); - } - - /** - * Adds an Axis IP camera. - * - *

This overload calls {@link #addAxisCamera(String, String)} with name "Axis Camera". - * - * @param host Camera host IP or DNS name (e.g. "10.x.y.11") - */ - public AxisCamera addAxisCamera(String host) { - return addAxisCamera("Axis Camera", host); - } - - /** - * Adds an Axis IP camera. - * - *

This overload calls {@link #addAxisCamera(String, String[])} with name "Axis Camera". - * - * @param hosts Array of Camera host IPs/DNS names - */ - public AxisCamera addAxisCamera(String[] hosts) { - return addAxisCamera("Axis Camera", hosts); - } - - /** - * Adds an Axis IP camera. - * - * @param name The name to give the camera - * @param host Camera host IP or DNS name (e.g. "10.x.y.11") - */ - public AxisCamera addAxisCamera(String name, String host) { - AxisCamera camera = new AxisCamera(name, host); - // Create a passthrough MJPEG server for USB access - startAutomaticCapture(camera); - CameraServerSharedStore.getCameraServerShared().reportAxisCamera(camera.getHandle()); - return camera; - } - - /** - * Adds an Axis IP camera. - * - * @param name The name to give the camera - * @param hosts Array of Camera host IPs/DNS names - */ - public AxisCamera addAxisCamera(String name, String[] hosts) { - AxisCamera camera = new AxisCamera(name, hosts); - // Create a passthrough MJPEG server for USB access - startAutomaticCapture(camera); - CameraServerSharedStore.getCameraServerShared().reportAxisCamera(camera.getHandle()); - return camera; - } - - /** - * Get OpenCV access to the primary camera feed. This allows you to get images from the camera for - * image processing on the roboRIO. - * - *

This is only valid to call after a camera feed has been added with startAutomaticCapture() - * or addServer(). - */ - public CvSink getVideo() { - VideoSource source; - synchronized (this) { - if (m_primarySourceName == null) { - throw new VideoException("no camera available"); - } - source = m_sources.get(m_primarySourceName); - } - if (source == null) { - throw new VideoException("no camera available"); - } - return getVideo(source); - } - - /** - * Get OpenCV access to the specified camera. This allows you to get images from the camera for - * image processing on the roboRIO. - * - * @param camera Camera (e.g. as returned by startAutomaticCapture). - */ - public CvSink getVideo(VideoSource camera) { - String name = "opencv_" + camera.getName(); - - synchronized (this) { - VideoSink sink = m_sinks.get(name); - if (sink != null) { - VideoSink.Kind kind = sink.getKind(); - if (kind != VideoSink.Kind.kCv) { - throw new VideoException("expected OpenCV sink, but got " + kind); - } - return (CvSink) sink; - } - } - - CvSink newsink = new CvSink(name); - newsink.setSource(camera); - addServer(newsink); - return newsink; - } - - /** - * Get OpenCV access to the specified camera. This allows you to get images from the camera for - * image processing on the roboRIO. - * - * @param name Camera name - */ - public CvSink getVideo(String name) { - VideoSource source; - synchronized (this) { - source = m_sources.get(name); - if (source == null) { - throw new VideoException("could not find camera " + name); - } - } - return getVideo(source); - } - - /** - * Create a MJPEG stream with OpenCV input. This can be called to pass custom annotated images to - * the dashboard. - * - * @param name Name to give the stream - * @param width Width of the image being sent - * @param height Height of the image being sent - */ - public CvSource putVideo(String name, int width, int height) { - CvSource source = new CvSource(name, VideoMode.PixelFormat.kMJPEG, width, height, 30); - startAutomaticCapture(source); - return source; - } - - /** - * Adds a MJPEG server at the next available port. - * - * @param name Server name - */ - public MjpegServer addServer(String name) { - int port; - synchronized (this) { - port = m_nextPort; - m_nextPort++; - } - return addServer(name, port); - } - - /** - * Adds a MJPEG server. - * - * @param name Server name - */ - public MjpegServer addServer(String name, int port) { - MjpegServer server = new MjpegServer(name, port); - addServer(server); - return server; - } - - /** - * Adds an already created server. - * - * @param server Server - */ - public void addServer(VideoSink server) { - synchronized (this) { - m_sinks.put(server.getName(), server); - } - } - - /** - * Removes a server by name. - * - * @param name Server name - */ - public void removeServer(String name) { - synchronized (this) { - m_sinks.remove(name); - } - } - - /** - * Get server for the primary camera feed. - * - *

This is only valid to call after a camera feed has been added with startAutomaticCapture() - * or addServer(). - */ - public VideoSink getServer() { - synchronized (this) { - if (m_primarySourceName == null) { - throw new VideoException("no camera available"); - } - return getServer("serve_" + m_primarySourceName); - } - } - - /** - * Gets a server by name. - * - * @param name Server name - */ - public VideoSink getServer(String name) { - synchronized (this) { - return m_sinks.get(name); - } - } - - /** - * Adds an already created camera. - * - * @param camera Camera - */ - public void addCamera(VideoSource camera) { - String name = camera.getName(); - synchronized (this) { - if (m_primarySourceName == null) { - m_primarySourceName = name; - } - m_sources.put(name, camera); - } - } - - /** - * Removes a camera by name. - * - * @param name Camera name - */ - public void removeCamera(String name) { - synchronized (this) { - m_sources.remove(name); - } - } -} From 160fb740f4905ac426230d8427ec479935d83aaf Mon Sep 17 00:00:00 2001 From: Peter Johnson Date: Fri, 19 Mar 2021 14:24:46 -0700 Subject: [PATCH 06/34] [hal] Use std::lround() instead of adding 0.5 and truncating (#3012) --- hal/src/main/native/athena/DIO.cpp | 4 ++-- hal/src/main/native/athena/DigitalInternal.cpp | 9 +++++---- hal/src/main/native/athena/PWM.cpp | 12 ++++++------ 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/hal/src/main/native/athena/DIO.cpp b/hal/src/main/native/athena/DIO.cpp index 23c5ab331b..38c90e455f 100644 --- a/hal/src/main/native/athena/DIO.cpp +++ b/hal/src/main/native/athena/DIO.cpp @@ -191,8 +191,8 @@ void HAL_SetDigitalPWMRate(double rate, int32_t* status) { if (*status != 0) { return; } - uint16_t pwmPeriodPower = static_cast( - std::log(1.0 / (16 * 1.0E-6 * rate)) / std::log(2.0) + 0.5); + uint16_t pwmPeriodPower = + std::lround(std::log(1.0 / (16 * 1.0E-6 * rate)) / std::log(2.0)); digitalSystem->writePWMPeriodPower(pwmPeriodPower, status); } diff --git a/hal/src/main/native/athena/DigitalInternal.cpp b/hal/src/main/native/athena/DigitalInternal.cpp index d47cedfb45..c75c66b3ba 100644 --- a/hal/src/main/native/athena/DigitalInternal.cpp +++ b/hal/src/main/native/athena/DigitalInternal.cpp @@ -5,6 +5,7 @@ #include "DigitalInternal.h" #include +#include #include #include @@ -108,10 +109,10 @@ void initializeDigital(int32_t* status) { double loopTime = pwmSystem->readLoopTiming(status) / (kSystemClockTicksPerMicrosecond * 1e3); - pwmSystem->writeConfig_Period( - static_cast(kDefaultPwmPeriod / loopTime + 0.5), status); - uint16_t minHigh = static_cast( - (kDefaultPwmCenter - kDefaultPwmStepsDown * loopTime) / loopTime + 0.5); + pwmSystem->writeConfig_Period(std::lround(kDefaultPwmPeriod / loopTime), + status); + uint16_t minHigh = std::lround( + (kDefaultPwmCenter - kDefaultPwmStepsDown * loopTime) / loopTime); pwmSystem->writeConfig_MinHigh(minHigh, status); // Ensure that PWM output values are set to OFF for (uint8_t pwmIndex = 0; pwmIndex < kNumPWMChannels; pwmIndex++) { diff --git a/hal/src/main/native/athena/PWM.cpp b/hal/src/main/native/athena/PWM.cpp index 873d447ee4..19b7b49f69 100644 --- a/hal/src/main/native/athena/PWM.cpp +++ b/hal/src/main/native/athena/PWM.cpp @@ -277,13 +277,13 @@ void HAL_SetPWMSpeed(HAL_DigitalHandle pwmPortHandle, double speed, if (speed == 0.0) { rawValue = GetCenterPwm(dPort); } else if (speed > 0.0) { - rawValue = static_cast( - speed * static_cast(GetPositiveScaleFactor(dPort)) + - static_cast(GetMinPositivePwm(dPort)) + 0.5); + rawValue = + std::lround(speed * static_cast(GetPositiveScaleFactor(dPort)) + + static_cast(GetMinPositivePwm(dPort))); } else { - rawValue = static_cast( - speed * static_cast(GetNegativeScaleFactor(dPort)) + - static_cast(GetMaxNegativePwm(dPort)) + 0.5); + rawValue = + std::lround(speed * static_cast(GetNegativeScaleFactor(dPort)) + + static_cast(GetMaxNegativePwm(dPort))); } if (!((rawValue >= GetMinNegativePwm(dPort)) && From 9550777b9d2906243182ddaabfd6b0741541fac5 Mon Sep 17 00:00:00 2001 From: Peter Johnson Date: Sun, 21 Mar 2021 11:12:49 -0700 Subject: [PATCH 07/34] [wpilib] PWMSpeedController: Use PWM by composition (#3248) This cleans up the user experience by removing lower-level functions from the interface. Also remove MotorSafety from "raw" PWM. --- wpilibc/src/main/native/cpp/DMC60.cpp | 13 ++---- wpilibc/src/main/native/cpp/Jaguar.cpp | 13 ++---- .../src/main/native/cpp/NidecBrushless.cpp | 1 + wpilibc/src/main/native/cpp/PWM.cpp | 23 +++------- wpilibc/src/main/native/cpp/PWMSparkMax.cpp | 12 ++--- .../main/native/cpp/PWMSpeedController.cpp | 28 +++++++---- wpilibc/src/main/native/cpp/PWMTalonFX.cpp | 14 +++--- wpilibc/src/main/native/cpp/PWMTalonSRX.cpp | 14 +++--- wpilibc/src/main/native/cpp/PWMVenom.cpp | 13 ++---- wpilibc/src/main/native/cpp/PWMVictorSPX.cpp | 14 +++--- wpilibc/src/main/native/cpp/SD540.cpp | 13 ++---- wpilibc/src/main/native/cpp/Spark.cpp | 13 ++---- wpilibc/src/main/native/cpp/Talon.cpp | 13 ++---- wpilibc/src/main/native/cpp/Victor.cpp | 13 ++---- wpilibc/src/main/native/cpp/VictorSP.cpp | 13 ++---- .../src/main/native/include/frc/MotorSafety.h | 5 +- wpilibc/src/main/native/include/frc/PWM.h | 13 ++---- .../native/include/frc/PWMSpeedController.h | 23 +++++++++- .../java/edu/wpi/first/wpilibj/DMC60.java | 12 ++--- .../java/edu/wpi/first/wpilibj/Jaguar.java | 12 ++--- .../main/java/edu/wpi/first/wpilibj/PWM.java | 33 +++++++------ .../edu/wpi/first/wpilibj/PWMSparkMax.java | 12 ++--- .../wpi/first/wpilibj/PWMSpeedController.java | 46 +++++++++++++++---- .../edu/wpi/first/wpilibj/PWMTalonFX.java | 12 ++--- .../edu/wpi/first/wpilibj/PWMTalonSRX.java | 12 ++--- .../java/edu/wpi/first/wpilibj/PWMVenom.java | 12 ++--- .../edu/wpi/first/wpilibj/PWMVictorSPX.java | 12 ++--- .../java/edu/wpi/first/wpilibj/SD540.java | 22 ++++----- .../java/edu/wpi/first/wpilibj/Spark.java | 22 ++++----- .../java/edu/wpi/first/wpilibj/Talon.java | 12 ++--- .../java/edu/wpi/first/wpilibj/Victor.java | 12 ++--- .../java/edu/wpi/first/wpilibj/VictorSP.java | 12 ++--- 32 files changed, 242 insertions(+), 252 deletions(-) diff --git a/wpilibc/src/main/native/cpp/DMC60.cpp b/wpilibc/src/main/native/cpp/DMC60.cpp index f84d839e62..001a51e5c8 100644 --- a/wpilibc/src/main/native/cpp/DMC60.cpp +++ b/wpilibc/src/main/native/cpp/DMC60.cpp @@ -6,16 +6,13 @@ #include -#include "frc/smartdashboard/SendableRegistry.h" - using namespace frc; -DMC60::DMC60(int channel) : PWMSpeedController(channel) { - SetBounds(2.004, 1.52, 1.50, 1.48, 0.997); - SetPeriodMultiplier(kPeriodMultiplier_1X); - SetSpeed(0.0); - SetZeroLatch(); +DMC60::DMC60(int channel) : PWMSpeedController("DMC60", channel) { + m_pwm.SetBounds(2.004, 1.52, 1.50, 1.48, 0.997); + m_pwm.SetPeriodMultiplier(PWM::kPeriodMultiplier_1X); + m_pwm.SetSpeed(0.0); + m_pwm.SetZeroLatch(); HAL_Report(HALUsageReporting::kResourceType_DigilentDMC60, GetChannel() + 1); - SendableRegistry::GetInstance().SetName(this, "DMC60", GetChannel()); } diff --git a/wpilibc/src/main/native/cpp/Jaguar.cpp b/wpilibc/src/main/native/cpp/Jaguar.cpp index eded12f082..2cf93c1de2 100644 --- a/wpilibc/src/main/native/cpp/Jaguar.cpp +++ b/wpilibc/src/main/native/cpp/Jaguar.cpp @@ -6,16 +6,13 @@ #include -#include "frc/smartdashboard/SendableRegistry.h" - using namespace frc; -Jaguar::Jaguar(int channel) : PWMSpeedController(channel) { - SetBounds(2.31, 1.55, 1.507, 1.454, 0.697); - SetPeriodMultiplier(kPeriodMultiplier_1X); - SetSpeed(0.0); - SetZeroLatch(); +Jaguar::Jaguar(int channel) : PWMSpeedController("Jaguar", channel) { + m_pwm.SetBounds(2.31, 1.55, 1.507, 1.454, 0.697); + m_pwm.SetPeriodMultiplier(PWM::kPeriodMultiplier_1X); + m_pwm.SetSpeed(0.0); + m_pwm.SetZeroLatch(); HAL_Report(HALUsageReporting::kResourceType_Jaguar, GetChannel() + 1); - SendableRegistry::GetInstance().SetName(this, "Jaguar", GetChannel()); } diff --git a/wpilibc/src/main/native/cpp/NidecBrushless.cpp b/wpilibc/src/main/native/cpp/NidecBrushless.cpp index 1ea2b95f9b..4909a01e31 100644 --- a/wpilibc/src/main/native/cpp/NidecBrushless.cpp +++ b/wpilibc/src/main/native/cpp/NidecBrushless.cpp @@ -5,6 +5,7 @@ #include "frc/NidecBrushless.h" #include +#include #include "frc/smartdashboard/SendableBuilder.h" #include "frc/smartdashboard/SendableRegistry.h" diff --git a/wpilibc/src/main/native/cpp/PWM.cpp b/wpilibc/src/main/native/cpp/PWM.cpp index 086553b712..2f4e1fe17f 100644 --- a/wpilibc/src/main/native/cpp/PWM.cpp +++ b/wpilibc/src/main/native/cpp/PWM.cpp @@ -19,7 +19,7 @@ using namespace frc; -PWM::PWM(int channel) { +PWM::PWM(int channel, bool registerSendable) { if (!SensorUtil::CheckPWMChannel(channel)) { wpi_setWPIErrorWithContext(ChannelIndexOutOfRange, "PWM Channel " + wpi::Twine(channel)); @@ -44,9 +44,9 @@ PWM::PWM(int channel) { wpi_setHALError(status); HAL_Report(HALUsageReporting::kResourceType_PWM, channel + 1); - SendableRegistry::GetInstance().AddLW(this, "PWM", channel); - - SetSafetyEnabled(false); + if (registerSendable) { + SendableRegistry::GetInstance().AddLW(this, "PWM", channel); + } } PWM::~PWM() { @@ -59,14 +59,6 @@ PWM::~PWM() { wpi_setHALError(status); } -void PWM::StopMotor() { - SetDisabled(); -} - -void PWM::GetDescription(wpi::raw_ostream& desc) const { - desc << "PWM " << GetChannel(); -} - void PWM::SetRaw(uint16_t value) { if (StatusIsFatal()) { return; @@ -115,8 +107,6 @@ void PWM::SetSpeed(double speed) { int32_t status = 0; HAL_SetPWMSpeed(m_handle, speed, &status); wpi_setHALError(status); - - Feed(); } double PWM::GetSpeed() const { @@ -223,8 +213,7 @@ int PWM::GetChannel() const { void PWM::InitSendable(SendableBuilder& builder) { builder.SetSmartDashboardType("PWM"); builder.SetActuator(true); - builder.SetSafeState([=]() { SetDisabled(); }); + builder.SetSafeState([=] { SetDisabled(); }); builder.AddDoubleProperty( - "Value", [=]() { return GetRaw(); }, - [=](double value) { SetRaw(value); }); + "Value", [=] { return GetRaw(); }, [=](double value) { SetRaw(value); }); } diff --git a/wpilibc/src/main/native/cpp/PWMSparkMax.cpp b/wpilibc/src/main/native/cpp/PWMSparkMax.cpp index 1b7915cb64..2e4a08e9be 100644 --- a/wpilibc/src/main/native/cpp/PWMSparkMax.cpp +++ b/wpilibc/src/main/native/cpp/PWMSparkMax.cpp @@ -10,12 +10,12 @@ using namespace frc; -PWMSparkMax::PWMSparkMax(int channel) : PWMSpeedController(channel) { - SetBounds(2.003, 1.55, 1.50, 1.46, 0.999); - SetPeriodMultiplier(kPeriodMultiplier_1X); - SetSpeed(0.0); - SetZeroLatch(); +PWMSparkMax::PWMSparkMax(int channel) + : PWMSpeedController("PWMSparkMax", channel) { + m_pwm.SetBounds(2.003, 1.55, 1.50, 1.46, 0.999); + m_pwm.SetPeriodMultiplier(PWM::kPeriodMultiplier_1X); + m_pwm.SetSpeed(0.0); + m_pwm.SetZeroLatch(); HAL_Report(HALUsageReporting::kResourceType_RevSparkMaxPWM, GetChannel() + 1); - SendableRegistry::GetInstance().SetName(this, "PWMSparkMax", GetChannel()); } diff --git a/wpilibc/src/main/native/cpp/PWMSpeedController.cpp b/wpilibc/src/main/native/cpp/PWMSpeedController.cpp index 413bf97f7b..6012301874 100644 --- a/wpilibc/src/main/native/cpp/PWMSpeedController.cpp +++ b/wpilibc/src/main/native/cpp/PWMSpeedController.cpp @@ -4,16 +4,18 @@ #include "frc/PWMSpeedController.h" +#include + #include "frc/smartdashboard/SendableBuilder.h" using namespace frc; void PWMSpeedController::Set(double speed) { - SetSpeed(m_isInverted ? -speed : speed); + m_pwm.SetSpeed(m_isInverted ? -speed : speed); } double PWMSpeedController::Get() const { - return GetSpeed() * (m_isInverted ? -1.0 : 1.0); + return m_pwm.GetSpeed() * (m_isInverted ? -1.0 : 1.0); } void PWMSpeedController::SetInverted(bool isInverted) { @@ -25,24 +27,34 @@ bool PWMSpeedController::GetInverted() const { } void PWMSpeedController::Disable() { - SetDisabled(); + m_pwm.SetDisabled(); } void PWMSpeedController::StopMotor() { - PWM::StopMotor(); + Disable(); +} + +void PWMSpeedController::GetDescription(wpi::raw_ostream& desc) const { + desc << "PWM " << GetChannel(); +} + +int PWMSpeedController::GetChannel() const { + return m_pwm.GetChannel(); } void PWMSpeedController::PIDWrite(double output) { Set(output); } -PWMSpeedController::PWMSpeedController(int channel) : PWM(channel) {} +PWMSpeedController::PWMSpeedController(const wpi::Twine& name, int channel) + : m_pwm(channel, false) { + SendableRegistry::GetInstance().AddLW(this, name, channel); +} void PWMSpeedController::InitSendable(SendableBuilder& builder) { builder.SetSmartDashboardType("Speed Controller"); builder.SetActuator(true); - builder.SetSafeState([=]() { SetDisabled(); }); + builder.SetSafeState([=] { Disable(); }); builder.AddDoubleProperty( - "Value", [=]() { return GetSpeed(); }, - [=](double value) { SetSpeed(value); }); + "Value", [=] { return Get(); }, [=](double value) { Set(value); }); } diff --git a/wpilibc/src/main/native/cpp/PWMTalonFX.cpp b/wpilibc/src/main/native/cpp/PWMTalonFX.cpp index 3ab574da91..7b85cf8bd6 100644 --- a/wpilibc/src/main/native/cpp/PWMTalonFX.cpp +++ b/wpilibc/src/main/native/cpp/PWMTalonFX.cpp @@ -6,16 +6,14 @@ #include -#include "frc/smartdashboard/SendableRegistry.h" - using namespace frc; -PWMTalonFX::PWMTalonFX(int channel) : PWMSpeedController(channel) { - SetBounds(2.004, 1.52, 1.50, 1.48, 0.997); - SetPeriodMultiplier(kPeriodMultiplier_1X); - SetSpeed(0.0); - SetZeroLatch(); +PWMTalonFX::PWMTalonFX(int channel) + : PWMSpeedController("PWMTalonFX", channel) { + m_pwm.SetBounds(2.004, 1.52, 1.50, 1.48, 0.997); + m_pwm.SetPeriodMultiplier(PWM::kPeriodMultiplier_1X); + m_pwm.SetSpeed(0.0); + m_pwm.SetZeroLatch(); HAL_Report(HALUsageReporting::kResourceType_TalonFX, GetChannel() + 1); - SendableRegistry::GetInstance().SetName(this, "PWMTalonFX", GetChannel()); } diff --git a/wpilibc/src/main/native/cpp/PWMTalonSRX.cpp b/wpilibc/src/main/native/cpp/PWMTalonSRX.cpp index e305015e39..b70c706760 100644 --- a/wpilibc/src/main/native/cpp/PWMTalonSRX.cpp +++ b/wpilibc/src/main/native/cpp/PWMTalonSRX.cpp @@ -6,16 +6,14 @@ #include -#include "frc/smartdashboard/SendableRegistry.h" - using namespace frc; -PWMTalonSRX::PWMTalonSRX(int channel) : PWMSpeedController(channel) { - SetBounds(2.004, 1.52, 1.50, 1.48, 0.997); - SetPeriodMultiplier(kPeriodMultiplier_1X); - SetSpeed(0.0); - SetZeroLatch(); +PWMTalonSRX::PWMTalonSRX(int channel) + : PWMSpeedController("PWMTalonSRX", channel) { + m_pwm.SetBounds(2.004, 1.52, 1.50, 1.48, 0.997); + m_pwm.SetPeriodMultiplier(PWM::kPeriodMultiplier_1X); + m_pwm.SetSpeed(0.0); + m_pwm.SetZeroLatch(); HAL_Report(HALUsageReporting::kResourceType_PWMTalonSRX, GetChannel() + 1); - SendableRegistry::GetInstance().SetName(this, "PWMTalonSRX", GetChannel()); } diff --git a/wpilibc/src/main/native/cpp/PWMVenom.cpp b/wpilibc/src/main/native/cpp/PWMVenom.cpp index ec1c09bfac..75dc008862 100644 --- a/wpilibc/src/main/native/cpp/PWMVenom.cpp +++ b/wpilibc/src/main/native/cpp/PWMVenom.cpp @@ -6,16 +6,13 @@ #include -#include "frc/smartdashboard/SendableRegistry.h" - using namespace frc; -PWMVenom::PWMVenom(int channel) : PWMSpeedController(channel) { - SetBounds(2.004, 1.52, 1.50, 1.48, 0.997); - SetPeriodMultiplier(kPeriodMultiplier_1X); - SetSpeed(0.0); - SetZeroLatch(); +PWMVenom::PWMVenom(int channel) : PWMSpeedController("PWMVenom", channel) { + m_pwm.SetBounds(2.004, 1.52, 1.50, 1.48, 0.997); + m_pwm.SetPeriodMultiplier(PWM::kPeriodMultiplier_1X); + m_pwm.SetSpeed(0.0); + m_pwm.SetZeroLatch(); HAL_Report(HALUsageReporting::kResourceType_FusionVenom, GetChannel() + 1); - SendableRegistry::GetInstance().SetName(this, "PWMVenom", GetChannel()); } diff --git a/wpilibc/src/main/native/cpp/PWMVictorSPX.cpp b/wpilibc/src/main/native/cpp/PWMVictorSPX.cpp index f34a045382..89cb207986 100644 --- a/wpilibc/src/main/native/cpp/PWMVictorSPX.cpp +++ b/wpilibc/src/main/native/cpp/PWMVictorSPX.cpp @@ -6,16 +6,14 @@ #include -#include "frc/smartdashboard/SendableRegistry.h" - using namespace frc; -PWMVictorSPX::PWMVictorSPX(int channel) : PWMSpeedController(channel) { - SetBounds(2.004, 1.52, 1.50, 1.48, 0.997); - SetPeriodMultiplier(kPeriodMultiplier_1X); - SetSpeed(0.0); - SetZeroLatch(); +PWMVictorSPX::PWMVictorSPX(int channel) + : PWMSpeedController("PWMVictorSPX", channel) { + m_pwm.SetBounds(2.004, 1.52, 1.50, 1.48, 0.997); + m_pwm.SetPeriodMultiplier(PWM::kPeriodMultiplier_1X); + m_pwm.SetSpeed(0.0); + m_pwm.SetZeroLatch(); HAL_Report(HALUsageReporting::kResourceType_PWMVictorSPX, GetChannel() + 1); - SendableRegistry::GetInstance().SetName(this, "PWMVictorSPX", GetChannel()); } diff --git a/wpilibc/src/main/native/cpp/SD540.cpp b/wpilibc/src/main/native/cpp/SD540.cpp index 03e37df5b4..2ff10f5814 100644 --- a/wpilibc/src/main/native/cpp/SD540.cpp +++ b/wpilibc/src/main/native/cpp/SD540.cpp @@ -6,17 +6,14 @@ #include -#include "frc/smartdashboard/SendableRegistry.h" - using namespace frc; -SD540::SD540(int channel) : PWMSpeedController(channel) { - SetBounds(2.05, 1.55, 1.50, 1.44, 0.94); - SetPeriodMultiplier(kPeriodMultiplier_1X); - SetSpeed(0.0); - SetZeroLatch(); +SD540::SD540(int channel) : PWMSpeedController("SD540", channel) { + m_pwm.SetBounds(2.05, 1.55, 1.50, 1.44, 0.94); + m_pwm.SetPeriodMultiplier(PWM::kPeriodMultiplier_1X); + m_pwm.SetSpeed(0.0); + m_pwm.SetZeroLatch(); HAL_Report(HALUsageReporting::kResourceType_MindsensorsSD540, GetChannel() + 1); - SendableRegistry::GetInstance().SetName(this, "SD540", GetChannel()); } diff --git a/wpilibc/src/main/native/cpp/Spark.cpp b/wpilibc/src/main/native/cpp/Spark.cpp index 2e0bcaa758..80db37d45f 100644 --- a/wpilibc/src/main/native/cpp/Spark.cpp +++ b/wpilibc/src/main/native/cpp/Spark.cpp @@ -6,16 +6,13 @@ #include -#include "frc/smartdashboard/SendableRegistry.h" - using namespace frc; -Spark::Spark(int channel) : PWMSpeedController(channel) { - SetBounds(2.003, 1.55, 1.50, 1.46, 0.999); - SetPeriodMultiplier(kPeriodMultiplier_1X); - SetSpeed(0.0); - SetZeroLatch(); +Spark::Spark(int channel) : PWMSpeedController("Spark", channel) { + m_pwm.SetBounds(2.003, 1.55, 1.50, 1.46, 0.999); + m_pwm.SetPeriodMultiplier(PWM::kPeriodMultiplier_1X); + m_pwm.SetSpeed(0.0); + m_pwm.SetZeroLatch(); HAL_Report(HALUsageReporting::kResourceType_RevSPARK, GetChannel() + 1); - SendableRegistry::GetInstance().SetName(this, "Spark", GetChannel()); } diff --git a/wpilibc/src/main/native/cpp/Talon.cpp b/wpilibc/src/main/native/cpp/Talon.cpp index 2f9a51e8af..7424157053 100644 --- a/wpilibc/src/main/native/cpp/Talon.cpp +++ b/wpilibc/src/main/native/cpp/Talon.cpp @@ -6,16 +6,13 @@ #include -#include "frc/smartdashboard/SendableRegistry.h" - using namespace frc; -Talon::Talon(int channel) : PWMSpeedController(channel) { - SetBounds(2.037, 1.539, 1.513, 1.487, 0.989); - SetPeriodMultiplier(kPeriodMultiplier_1X); - SetSpeed(0.0); - SetZeroLatch(); +Talon::Talon(int channel) : PWMSpeedController("Talon", channel) { + m_pwm.SetBounds(2.037, 1.539, 1.513, 1.487, 0.989); + m_pwm.SetPeriodMultiplier(PWM::kPeriodMultiplier_1X); + m_pwm.SetSpeed(0.0); + m_pwm.SetZeroLatch(); HAL_Report(HALUsageReporting::kResourceType_Talon, GetChannel() + 1); - SendableRegistry::GetInstance().SetName(this, "Talon", GetChannel()); } diff --git a/wpilibc/src/main/native/cpp/Victor.cpp b/wpilibc/src/main/native/cpp/Victor.cpp index a466add795..a6d4805022 100644 --- a/wpilibc/src/main/native/cpp/Victor.cpp +++ b/wpilibc/src/main/native/cpp/Victor.cpp @@ -6,16 +6,13 @@ #include -#include "frc/smartdashboard/SendableRegistry.h" - using namespace frc; -Victor::Victor(int channel) : PWMSpeedController(channel) { - SetBounds(2.027, 1.525, 1.507, 1.49, 1.026); - SetPeriodMultiplier(kPeriodMultiplier_2X); - SetSpeed(0.0); - SetZeroLatch(); +Victor::Victor(int channel) : PWMSpeedController("Victor", channel) { + m_pwm.SetBounds(2.027, 1.525, 1.507, 1.49, 1.026); + m_pwm.SetPeriodMultiplier(PWM::kPeriodMultiplier_2X); + m_pwm.SetSpeed(0.0); + m_pwm.SetZeroLatch(); HAL_Report(HALUsageReporting::kResourceType_Victor, GetChannel() + 1); - SendableRegistry::GetInstance().SetName(this, "Victor", GetChannel()); } diff --git a/wpilibc/src/main/native/cpp/VictorSP.cpp b/wpilibc/src/main/native/cpp/VictorSP.cpp index 10b7fdadcc..f4da973b59 100644 --- a/wpilibc/src/main/native/cpp/VictorSP.cpp +++ b/wpilibc/src/main/native/cpp/VictorSP.cpp @@ -6,16 +6,13 @@ #include -#include "frc/smartdashboard/SendableRegistry.h" - using namespace frc; -VictorSP::VictorSP(int channel) : PWMSpeedController(channel) { - SetBounds(2.004, 1.52, 1.50, 1.48, 0.997); - SetPeriodMultiplier(kPeriodMultiplier_1X); - SetSpeed(0.0); - SetZeroLatch(); +VictorSP::VictorSP(int channel) : PWMSpeedController("VictorSP", channel) { + m_pwm.SetBounds(2.004, 1.52, 1.50, 1.48, 0.997); + m_pwm.SetPeriodMultiplier(PWM::kPeriodMultiplier_1X); + m_pwm.SetSpeed(0.0); + m_pwm.SetZeroLatch(); HAL_Report(HALUsageReporting::kResourceType_VictorSP, GetChannel() + 1); - SendableRegistry::GetInstance().SetName(this, "VictorSP", GetChannel()); } diff --git a/wpilibc/src/main/native/include/frc/MotorSafety.h b/wpilibc/src/main/native/include/frc/MotorSafety.h index ee191b8459..b23a29c7ef 100644 --- a/wpilibc/src/main/native/include/frc/MotorSafety.h +++ b/wpilibc/src/main/native/include/frc/MotorSafety.h @@ -5,11 +5,14 @@ #pragma once #include -#include #include "frc/ErrorBase.h" #include "frc/Timer.h" +namespace wpi { +class raw_ostream; +} // namespace wpi + namespace frc { /** diff --git a/wpilibc/src/main/native/include/frc/PWM.h b/wpilibc/src/main/native/include/frc/PWM.h index 7f7cd912ae..869b6453b9 100644 --- a/wpilibc/src/main/native/include/frc/PWM.h +++ b/wpilibc/src/main/native/include/frc/PWM.h @@ -7,9 +7,8 @@ #include #include -#include -#include "frc/MotorSafety.h" +#include "frc/ErrorBase.h" #include "frc/smartdashboard/Sendable.h" #include "frc/smartdashboard/SendableHelper.h" @@ -34,7 +33,7 @@ class SendableBuilder; * - 1 = minimum pulse width (currently 0.5ms) * - 0 = disabled (i.e. PWM output is held low) */ -class PWM : public MotorSafety, public Sendable, public SendableHelper { +class PWM : public ErrorBase, public Sendable, public SendableHelper { public: friend class AddressableLED; /** @@ -64,8 +63,10 @@ class PWM : public MotorSafety, public Sendable, public SendableHelper { * * @param channel The PWM channel number. 0-9 are on-board, 10-19 are on the * MXP port + * @param registerSendable If true, adds this instance to SendableRegistry + * and LiveWindow */ - explicit PWM(int channel); + explicit PWM(int channel, bool registerSendable = true); /** * Free the PWM channel. @@ -77,10 +78,6 @@ class PWM : public MotorSafety, public Sendable, public SendableHelper { PWM(PWM&&) = default; PWM& operator=(PWM&&) = default; - // MotorSafety interface - void StopMotor() override; - void GetDescription(wpi::raw_ostream& desc) const override; - /** * Set the PWM value directly to the hardware. * diff --git a/wpilibc/src/main/native/include/frc/PWMSpeedController.h b/wpilibc/src/main/native/include/frc/PWMSpeedController.h index 3d12fb70c6..9d6f65af3c 100644 --- a/wpilibc/src/main/native/include/frc/PWMSpeedController.h +++ b/wpilibc/src/main/native/include/frc/PWMSpeedController.h @@ -4,15 +4,27 @@ #pragma once +#include + +#include "frc/MotorSafety.h" #include "frc/PWM.h" #include "frc/SpeedController.h" +#include "frc/smartdashboard/Sendable.h" +#include "frc/smartdashboard/SendableHelper.h" + +namespace wpi { +class raw_ostream; +} // namespace wpi namespace frc { /** * Common base class for all PWM Speed Controllers. */ -class PWMSpeedController : public PWM, public SpeedController { +class PWMSpeedController : public SpeedController, + public MotorSafety, + public Sendable, + public SendableHelper { public: PWMSpeedController(PWMSpeedController&&) = default; PWMSpeedController& operator=(PWMSpeedController&&) = default; @@ -42,7 +54,11 @@ class PWMSpeedController : public PWM, public SpeedController { void Disable() override; + // MotorSafety interface void StopMotor() override; + void GetDescription(wpi::raw_ostream& desc) const override; + + int GetChannel() const; /** * Write out the PID value as seen in the PIDOutput base object. @@ -55,13 +71,16 @@ class PWMSpeedController : public PWM, public SpeedController { /** * Constructor for a PWM Speed Controller connected via PWM. * + * @param name Name to use for SendableRegistry * @param channel The PWM channel that the controller is attached to. 0-9 are * on-board, 10-19 are on the MXP port */ - explicit PWMSpeedController(int channel); + PWMSpeedController(const wpi::Twine& name, int channel); void InitSendable(SendableBuilder& builder) override; + PWM m_pwm; + private: bool m_isInverted = false; }; diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/DMC60.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/DMC60.java index dfe37bfb15..2f38230ea9 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/DMC60.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/DMC60.java @@ -6,7 +6,6 @@ package edu.wpi.first.wpilibj; import edu.wpi.first.hal.FRCNetComm.tResourceType; import edu.wpi.first.hal.HAL; -import edu.wpi.first.wpilibj.smartdashboard.SendableRegistry; /** * Digilent DMC 60 Speed Controller. @@ -33,14 +32,13 @@ public class DMC60 extends PWMSpeedController { * the MXP port */ public DMC60(final int channel) { - super(channel); + super("DMC60", channel); - setBounds(2.004, 1.52, 1.50, 1.48, 0.997); - setPeriodMultiplier(PeriodMultiplier.k1X); - setSpeed(0.0); - setZeroLatch(); + m_pwm.setBounds(2.004, 1.52, 1.50, 1.48, 0.997); + m_pwm.setPeriodMultiplier(PWM.PeriodMultiplier.k1X); + m_pwm.setSpeed(0.0); + m_pwm.setZeroLatch(); HAL.report(tResourceType.kResourceType_DigilentDMC60, getChannel() + 1); - SendableRegistry.setName(this, "DMC60", getChannel()); } } diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/Jaguar.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/Jaguar.java index 388b286144..5e69c4a50a 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/Jaguar.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/Jaguar.java @@ -6,7 +6,6 @@ package edu.wpi.first.wpilibj; import edu.wpi.first.hal.FRCNetComm.tResourceType; import edu.wpi.first.hal.HAL; -import edu.wpi.first.wpilibj.smartdashboard.SendableRegistry; /** * Texas Instruments / Vex Robotics Jaguar Speed Controller as a PWM device. @@ -32,14 +31,13 @@ public class Jaguar extends PWMSpeedController { * the MXP port */ public Jaguar(final int channel) { - super(channel); + super("Jaguar", channel); - setBounds(2.31, 1.55, 1.507, 1.454, 0.697); - setPeriodMultiplier(PeriodMultiplier.k1X); - setSpeed(0.0); - setZeroLatch(); + m_pwm.setBounds(2.31, 1.55, 1.507, 1.454, 0.697); + m_pwm.setPeriodMultiplier(PWM.PeriodMultiplier.k1X); + m_pwm.setSpeed(0.0); + m_pwm.setZeroLatch(); HAL.report(tResourceType.kResourceType_Jaguar, getChannel() + 1); - SendableRegistry.setName(this, "Jaguar", getChannel()); } } diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/PWM.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/PWM.java index ddfb39ec9a..f21ba025ea 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/PWM.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/PWM.java @@ -23,7 +23,7 @@ import edu.wpi.first.wpilibj.smartdashboard.SendableRegistry; * center value - 999 to 2 = linear scaling from "center" to "full reverse" - 1 = minimum pulse * width (currently .5ms) - 0 = disabled (i.e. PWM output is held low) */ -public class PWM extends MotorSafety implements Sendable, AutoCloseable { +public class PWM implements Sendable, AutoCloseable { /** Represents the amount to multiply the minimum servo-pulse pwm period by. */ public enum PeriodMultiplier { /** Period Multiplier: don't skip pulses. PWM pulses occur every 5.005 ms */ @@ -42,9 +42,24 @@ public class PWM extends MotorSafety implements Sendable, AutoCloseable { /** * Allocate a PWM given a channel. * + *

Checks channel value range and allocates the appropriate channel. The allocation is only + * done to help users ensure that they don't double assign channels. + * + *

By default, adds itself to SendableRegistry and LiveWindow. + * * @param channel The PWM channel number. 0-9 are on-board, 10-19 are on the MXP port */ public PWM(final int channel) { + this(channel, true); + } + + /** + * Allocate a PWM given a channel. + * + * @param channel The PWM channel number. 0-9 are on-board, 10-19 are on the MXP port + * @param registerSendable If true, adds this instance to SendableRegistry and LiveWindow + */ + public PWM(final int channel, final boolean registerSendable) { SensorUtil.checkPWMChannel(channel); m_channel = channel; @@ -55,9 +70,9 @@ public class PWM extends MotorSafety implements Sendable, AutoCloseable { PWMJNI.setPWMEliminateDeadband(m_handle, false); HAL.report(tResourceType.kResourceType_PWM, channel + 1); - SendableRegistry.addLW(this, "PWM", channel); - - setSafetyEnabled(false); + if (registerSendable) { + SendableRegistry.addLW(this, "PWM", channel); + } } /** Free the resource associated with the PWM channel and set the value to 0. */ @@ -72,16 +87,6 @@ public class PWM extends MotorSafety implements Sendable, AutoCloseable { m_handle = 0; } - @Override - public void stopMotor() { - setDisabled(); - } - - @Override - public String getDescription() { - return "PWM " + getChannel(); - } - /** * Optionally eliminate the deadband from a speed controller. * diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/PWMSparkMax.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/PWMSparkMax.java index 72a4303949..b57fd9ac35 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/PWMSparkMax.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/PWMSparkMax.java @@ -6,7 +6,6 @@ package edu.wpi.first.wpilibj; import edu.wpi.first.hal.FRCNetComm.tResourceType; import edu.wpi.first.hal.HAL; -import edu.wpi.first.wpilibj.smartdashboard.SendableRegistry; /** * REV Robotics SPARK MAX Speed Controller with PWM control. @@ -28,14 +27,13 @@ import edu.wpi.first.wpilibj.smartdashboard.SendableRegistry; public class PWMSparkMax extends PWMSpeedController { /** Common initialization code called by all constructors. */ public PWMSparkMax(final int channel) { - super(channel); + super("PWMSparkMax", channel); - setBounds(2.003, 1.55, 1.50, 1.46, 0.999); - setPeriodMultiplier(PeriodMultiplier.k1X); - setSpeed(0.0); - setZeroLatch(); + m_pwm.setBounds(2.003, 1.55, 1.50, 1.46, 0.999); + m_pwm.setPeriodMultiplier(PWM.PeriodMultiplier.k1X); + m_pwm.setSpeed(0.0); + m_pwm.setZeroLatch(); HAL.report(tResourceType.kResourceType_RevSparkMaxPWM, getChannel() + 1); - SendableRegistry.setName(this, "PWMSparkMax", getChannel()); } } diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/PWMSpeedController.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/PWMSpeedController.java index 1c9801fc40..2db8baf5fe 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/PWMSpeedController.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/PWMSpeedController.java @@ -5,24 +5,31 @@ package edu.wpi.first.wpilibj; import edu.wpi.first.wpilibj.smartdashboard.SendableBuilder; +import edu.wpi.first.wpilibj.smartdashboard.SendableRegistry; /** Common base class for all PWM Speed Controllers. */ -public abstract class PWMSpeedController extends PWM implements SpeedController { +public abstract class PWMSpeedController extends MotorSafety + implements SpeedController, Sendable, AutoCloseable { private boolean m_isInverted; + protected PWM m_pwm; /** * Constructor. * + * @param name Name to use for SendableRegistry * @param channel The PWM channel that the controller is attached to. 0-9 are on-board, 10-19 are * on the MXP port */ - protected PWMSpeedController(int channel) { - super(channel); + protected PWMSpeedController(final String name, final int channel) { + m_pwm = new PWM(channel, false); + SendableRegistry.addLW(this, name, channel); } + /** Free the resource associated with the PWM channel and set the value to 0. */ @Override - public String getDescription() { - return "PWM " + getChannel(); + public void close() { + SendableRegistry.remove(this); + m_pwm.close(); } /** @@ -35,7 +42,7 @@ public abstract class PWMSpeedController extends PWM implements SpeedController */ @Override public void set(double speed) { - setSpeed(m_isInverted ? -speed : speed); + m_pwm.setSpeed(m_isInverted ? -speed : speed); feed(); } @@ -48,7 +55,7 @@ public abstract class PWMSpeedController extends PWM implements SpeedController */ @Override public double get() { - return getSpeed() * (m_isInverted ? -1.0 : 1.0); + return m_pwm.getSpeed() * (m_isInverted ? -1.0 : 1.0); } @Override @@ -63,7 +70,26 @@ public abstract class PWMSpeedController extends PWM implements SpeedController @Override public void disable() { - setDisabled(); + m_pwm.setDisabled(); + } + + @Override + public void stopMotor() { + disable(); + } + + @Override + public String getDescription() { + return "PWM " + getChannel(); + } + + /** + * Gets the PWM channel number. + * + * @return The channel number. + */ + public int getChannel() { + return m_pwm.getChannel(); } /** @@ -80,7 +106,7 @@ public abstract class PWMSpeedController extends PWM implements SpeedController public void initSendable(SendableBuilder builder) { builder.setSmartDashboardType("Speed Controller"); builder.setActuator(true); - builder.setSafeState(this::setDisabled); - builder.addDoubleProperty("Value", this::getSpeed, this::setSpeed); + builder.setSafeState(this::disable); + builder.addDoubleProperty("Value", this::get, this::set); } } diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/PWMTalonFX.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/PWMTalonFX.java index d970f5d3e7..330c6abe30 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/PWMTalonFX.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/PWMTalonFX.java @@ -6,7 +6,6 @@ package edu.wpi.first.wpilibj; import edu.wpi.first.hal.FRCNetComm.tResourceType; import edu.wpi.first.hal.HAL; -import edu.wpi.first.wpilibj.smartdashboard.SendableRegistry; /** * Cross the Road Electronics (CTRE) Talon FX Speed Controller with PWM control. @@ -33,14 +32,13 @@ public class PWMTalonFX extends PWMSpeedController { * the MXP port */ public PWMTalonFX(final int channel) { - super(channel); + super("PWMTalonFX", channel); - setBounds(2.004, 1.52, 1.50, 1.48, 0.997); - setPeriodMultiplier(PeriodMultiplier.k1X); - setSpeed(0.0); - setZeroLatch(); + m_pwm.setBounds(2.004, 1.52, 1.50, 1.48, 0.997); + m_pwm.setPeriodMultiplier(PWM.PeriodMultiplier.k1X); + m_pwm.setSpeed(0.0); + m_pwm.setZeroLatch(); HAL.report(tResourceType.kResourceType_TalonFX, getChannel() + 1); - SendableRegistry.setName(this, "PWMTalonFX", getChannel()); } } diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/PWMTalonSRX.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/PWMTalonSRX.java index 5bf5fc620c..77c7d37475 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/PWMTalonSRX.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/PWMTalonSRX.java @@ -6,7 +6,6 @@ package edu.wpi.first.wpilibj; import edu.wpi.first.hal.FRCNetComm.tResourceType; import edu.wpi.first.hal.HAL; -import edu.wpi.first.wpilibj.smartdashboard.SendableRegistry; /** * Cross the Road Electronics (CTRE) Talon SRX Speed Controller with PWM control. @@ -33,14 +32,13 @@ public class PWMTalonSRX extends PWMSpeedController { * on the MXP port */ public PWMTalonSRX(final int channel) { - super(channel); + super("PWMTalonSRX", channel); - setBounds(2.004, 1.52, 1.50, 1.48, 0.997); - setPeriodMultiplier(PeriodMultiplier.k1X); - setSpeed(0.0); - setZeroLatch(); + m_pwm.setBounds(2.004, 1.52, 1.50, 1.48, 0.997); + m_pwm.setPeriodMultiplier(PWM.PeriodMultiplier.k1X); + m_pwm.setSpeed(0.0); + m_pwm.setZeroLatch(); HAL.report(tResourceType.kResourceType_PWMTalonSRX, getChannel() + 1); - SendableRegistry.setName(this, "PWMTalonSRX", getChannel()); } } diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/PWMVenom.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/PWMVenom.java index cdf07754ec..8a71842a14 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/PWMVenom.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/PWMVenom.java @@ -6,7 +6,6 @@ package edu.wpi.first.wpilibj; import edu.wpi.first.hal.FRCNetComm.tResourceType; import edu.wpi.first.hal.HAL; -import edu.wpi.first.wpilibj.smartdashboard.SendableRegistry; /** * Playing with Fusion Venom Smart Motor with PWM control. @@ -32,14 +31,13 @@ public class PWMVenom extends PWMSpeedController { * the MXP port */ public PWMVenom(final int channel) { - super(channel); + super("PWMVenom", channel); - setBounds(2.004, 1.52, 1.50, 1.48, 0.997); - setPeriodMultiplier(PeriodMultiplier.k1X); - setSpeed(0.0); - setZeroLatch(); + m_pwm.setBounds(2.004, 1.52, 1.50, 1.48, 0.997); + m_pwm.setPeriodMultiplier(PWM.PeriodMultiplier.k1X); + m_pwm.setSpeed(0.0); + m_pwm.setZeroLatch(); HAL.report(tResourceType.kResourceType_FusionVenom, getChannel() + 1); - SendableRegistry.setName(this, "PWMVenom", getChannel()); } } diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/PWMVictorSPX.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/PWMVictorSPX.java index 976eb67fc3..62c86087ac 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/PWMVictorSPX.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/PWMVictorSPX.java @@ -6,7 +6,6 @@ package edu.wpi.first.wpilibj; import edu.wpi.first.hal.FRCNetComm.tResourceType; import edu.wpi.first.hal.HAL; -import edu.wpi.first.wpilibj.smartdashboard.SendableRegistry; /** * Cross the Road Electronics (CTRE) Victor SPX Speed Controller with PWM control. @@ -33,14 +32,13 @@ public class PWMVictorSPX extends PWMSpeedController { * are on the MXP port */ public PWMVictorSPX(final int channel) { - super(channel); + super("PWMVictorSPX", channel); - setBounds(2.004, 1.52, 1.50, 1.48, 0.997); - setPeriodMultiplier(PeriodMultiplier.k1X); - setSpeed(0.0); - setZeroLatch(); + m_pwm.setBounds(2.004, 1.52, 1.50, 1.48, 0.997); + m_pwm.setPeriodMultiplier(PWM.PeriodMultiplier.k1X); + m_pwm.setSpeed(0.0); + m_pwm.setZeroLatch(); HAL.report(tResourceType.kResourceType_PWMVictorSPX, getChannel() + 1); - SendableRegistry.setName(this, "PWMVictorSPX", getChannel()); } } diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/SD540.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/SD540.java index 58867334a5..8a14463964 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/SD540.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/SD540.java @@ -6,7 +6,6 @@ package edu.wpi.first.wpilibj; import edu.wpi.first.hal.FRCNetComm.tResourceType; import edu.wpi.first.hal.HAL; -import edu.wpi.first.wpilibj.smartdashboard.SendableRegistry; /** * Mindsensors SD540 Speed Controller. @@ -26,17 +25,6 @@ import edu.wpi.first.wpilibj.smartdashboard.SendableRegistry; * */ public class SD540 extends PWMSpeedController { - /** Common initialization code called by all constructors. */ - protected void initSD540() { - setBounds(2.05, 1.55, 1.50, 1.44, 0.94); - setPeriodMultiplier(PeriodMultiplier.k1X); - setSpeed(0.0); - setZeroLatch(); - - HAL.report(tResourceType.kResourceType_MindsensorsSD540, getChannel() + 1); - SendableRegistry.setName(this, "SD540", getChannel()); - } - /** * Constructor. * @@ -44,7 +32,13 @@ public class SD540 extends PWMSpeedController { * the MXP port */ public SD540(final int channel) { - super(channel); - initSD540(); + super("SD540", channel); + + m_pwm.setBounds(2.05, 1.55, 1.50, 1.44, 0.94); + m_pwm.setPeriodMultiplier(PWM.PeriodMultiplier.k1X); + m_pwm.setSpeed(0.0); + m_pwm.setZeroLatch(); + + HAL.report(tResourceType.kResourceType_MindsensorsSD540, getChannel() + 1); } } diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/Spark.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/Spark.java index 1e6f55c697..cf3d8d29ba 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/Spark.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/Spark.java @@ -6,7 +6,6 @@ package edu.wpi.first.wpilibj; import edu.wpi.first.hal.FRCNetComm.tResourceType; import edu.wpi.first.hal.HAL; -import edu.wpi.first.wpilibj.smartdashboard.SendableRegistry; /** * REV Robotics SPARK Speed Controller. @@ -26,17 +25,6 @@ import edu.wpi.first.wpilibj.smartdashboard.SendableRegistry; * */ public class Spark extends PWMSpeedController { - /** Common initialization code called by all constructors. */ - protected void initSpark() { - setBounds(2.003, 1.55, 1.50, 1.46, 0.999); - setPeriodMultiplier(PeriodMultiplier.k1X); - setSpeed(0.0); - setZeroLatch(); - - HAL.report(tResourceType.kResourceType_RevSPARK, getChannel() + 1); - SendableRegistry.setName(this, "Spark", getChannel()); - } - /** * Constructor. * @@ -44,7 +32,13 @@ public class Spark extends PWMSpeedController { * the MXP port */ public Spark(final int channel) { - super(channel); - initSpark(); + super("Spark", channel); + + m_pwm.setBounds(2.003, 1.55, 1.50, 1.46, 0.999); + m_pwm.setPeriodMultiplier(PWM.PeriodMultiplier.k1X); + m_pwm.setSpeed(0.0); + m_pwm.setZeroLatch(); + + HAL.report(tResourceType.kResourceType_RevSPARK, getChannel() + 1); } } diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/Talon.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/Talon.java index 355e7934a2..a0b3ac5694 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/Talon.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/Talon.java @@ -6,7 +6,6 @@ package edu.wpi.first.wpilibj; import edu.wpi.first.hal.FRCNetComm.tResourceType; import edu.wpi.first.hal.HAL; -import edu.wpi.first.wpilibj.smartdashboard.SendableRegistry; /** * Cross the Road Electronics (CTRE) Talon and Talon SR Speed Controller. @@ -32,14 +31,13 @@ public class Talon extends PWMSpeedController { * the MXP port */ public Talon(final int channel) { - super(channel); + super("Talon", channel); - setBounds(2.037, 1.539, 1.513, 1.487, 0.989); - setPeriodMultiplier(PeriodMultiplier.k1X); - setSpeed(0.0); - setZeroLatch(); + m_pwm.setBounds(2.037, 1.539, 1.513, 1.487, 0.989); + m_pwm.setPeriodMultiplier(PWM.PeriodMultiplier.k1X); + m_pwm.setSpeed(0.0); + m_pwm.setZeroLatch(); HAL.report(tResourceType.kResourceType_Talon, getChannel() + 1); - SendableRegistry.setName(this, "Talon", getChannel()); } } diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/Victor.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/Victor.java index 0c4229cad7..bdd9ba7d2d 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/Victor.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/Victor.java @@ -6,7 +6,6 @@ package edu.wpi.first.wpilibj; import edu.wpi.first.hal.FRCNetComm.tResourceType; import edu.wpi.first.hal.HAL; -import edu.wpi.first.wpilibj.smartdashboard.SendableRegistry; /** * VEX Robotics Victor 888 Speed Controller The Vex Robotics Victor 884 Speed Controller can also be @@ -35,14 +34,13 @@ public class Victor extends PWMSpeedController { * the MXP port */ public Victor(final int channel) { - super(channel); + super("Victor", channel); - setBounds(2.027, 1.525, 1.507, 1.49, 1.026); - setPeriodMultiplier(PeriodMultiplier.k2X); - setSpeed(0.0); - setZeroLatch(); + m_pwm.setBounds(2.027, 1.525, 1.507, 1.49, 1.026); + m_pwm.setPeriodMultiplier(PWM.PeriodMultiplier.k2X); + m_pwm.setSpeed(0.0); + m_pwm.setZeroLatch(); HAL.report(tResourceType.kResourceType_Victor, getChannel() + 1); - SendableRegistry.setName(this, "Victor", getChannel()); } } diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/VictorSP.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/VictorSP.java index 2f04f79370..b588c5c9a2 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/VictorSP.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/VictorSP.java @@ -6,7 +6,6 @@ package edu.wpi.first.wpilibj; import edu.wpi.first.hal.FRCNetComm.tResourceType; import edu.wpi.first.hal.HAL; -import edu.wpi.first.wpilibj.smartdashboard.SendableRegistry; /** * VEX Robotics Victor SP Speed Controller. @@ -33,14 +32,13 @@ public class VictorSP extends PWMSpeedController { * the MXP port */ public VictorSP(final int channel) { - super(channel); + super("VictorSP", channel); - setBounds(2.004, 1.52, 1.50, 1.48, 0.997); - setPeriodMultiplier(PeriodMultiplier.k1X); - setSpeed(0.0); - setZeroLatch(); + m_pwm.setBounds(2.004, 1.52, 1.50, 1.48, 0.997); + m_pwm.setPeriodMultiplier(PWM.PeriodMultiplier.k1X); + m_pwm.setSpeed(0.0); + m_pwm.setZeroLatch(); HAL.report(tResourceType.kResourceType_VictorSP, getChannel() + 1); - SendableRegistry.setName(this, "VictorSP", getChannel()); } } From a79faace1ba608ebca8bcf2f95a66ea5f78d88e8 Mon Sep 17 00:00:00 2001 From: Peter Johnson Date: Sun, 21 Mar 2021 11:13:49 -0700 Subject: [PATCH 08/34] [wpilibc] Return reference from GetInstance (#3247) Improves consistency across all classes. Affects Preferences, LiveWindow, and CameraServer. Old commands Scheduler::GetInstance() was not updated as this is already deprecated. --- .../src/main/native/cpp/main.cpp | 2 +- .../native/cpp/cameraserver/CameraServer.cpp | 4 +- .../include/cameraserver/CameraServer.h | 2 +- .../cpp/frc2/command/CommandScheduler.cpp | 16 ++++---- .../main/native/cpp/commands/Scheduler.cpp | 16 ++++---- .../main/native/cpp/IterativeRobotBase.cpp | 10 ++--- wpilibc/src/main/native/cpp/Preferences.cpp | 6 ++- .../main/native/cpp/livewindow/LiveWindow.cpp | 4 +- wpilibc/src/main/native/cppcs/RobotBase.cpp | 2 +- .../src/main/native/include/frc/Preferences.h | 6 +-- .../include/frc/livewindow/LiveWindow.h | 3 +- .../examples/AxisCameraSample/cpp/Robot.cpp | 6 +-- .../cpp/examples/GettingStarted/cpp/Robot.cpp | 2 +- .../examples/IntermediateVision/cpp/Robot.cpp | 6 +-- .../cpp/examples/QuickVision/cpp/Robot.cpp | 2 +- .../templates/robotbaseskeleton/cpp/Robot.cpp | 2 +- .../src/main/native/cpp/PreferencesTest.cpp | 40 +++++++++---------- .../src/main/native/cpp/TestEnvironment.cpp | 2 +- 18 files changed, 67 insertions(+), 64 deletions(-) diff --git a/cameraserver/multiCameraServer/src/main/native/cpp/main.cpp b/cameraserver/multiCameraServer/src/main/native/cpp/main.cpp index df408dee2a..e030fd9f22 100644 --- a/cameraserver/multiCameraServer/src/main/native/cpp/main.cpp +++ b/cameraserver/multiCameraServer/src/main/native/cpp/main.cpp @@ -158,7 +158,7 @@ bool ReadConfig() { void StartCamera(const CameraConfig& config) { wpi::outs() << "Starting camera '" << config.name << "' on " << config.path << '\n'; - auto camera = frc::CameraServer::GetInstance()->StartAutomaticCapture( + auto camera = frc::CameraServer::GetInstance().StartAutomaticCapture( config.name, config.path); camera.SetConfigJson(config.config); diff --git a/cameraserver/src/main/native/cpp/cameraserver/CameraServer.cpp b/cameraserver/src/main/native/cpp/cameraserver/CameraServer.cpp index fa18a8e14b..b258d3bbf9 100644 --- a/cameraserver/src/main/native/cpp/cameraserver/CameraServer.cpp +++ b/cameraserver/src/main/native/cpp/cameraserver/CameraServer.cpp @@ -45,7 +45,7 @@ struct CameraServer::Impl { std::vector m_addresses; }; -CameraServer* CameraServer::GetInstance() { +CameraServer& CameraServer::GetInstance() { struct Creator { static void* call() { return new CameraServer{}; } }; @@ -53,7 +53,7 @@ CameraServer* CameraServer::GetInstance() { static void call(void* ptr) { delete static_cast(ptr); } }; static wpi::ManagedStatic instance; - return &(*instance); + return *instance; } static wpi::StringRef MakeSourceValue(CS_Source source, diff --git a/cameraserver/src/main/native/include/cameraserver/CameraServer.h b/cameraserver/src/main/native/include/cameraserver/CameraServer.h index ce620ed1b8..2cf73e8fa4 100644 --- a/cameraserver/src/main/native/include/cameraserver/CameraServer.h +++ b/cameraserver/src/main/native/include/cameraserver/CameraServer.h @@ -32,7 +32,7 @@ class CameraServer { /** * Get the CameraServer instance. */ - static CameraServer* GetInstance(); + static CameraServer& GetInstance(); /** * Start automatically capturing images to send to the dashboard. diff --git a/wpilibNewCommands/src/main/native/cpp/frc2/command/CommandScheduler.cpp b/wpilibNewCommands/src/main/native/cpp/frc2/command/CommandScheduler.cpp index 3acd9b5fdd..57e33a222a 100644 --- a/wpilibNewCommands/src/main/native/cpp/frc2/command/CommandScheduler.cpp +++ b/wpilibNewCommands/src/main/native/cpp/frc2/command/CommandScheduler.cpp @@ -71,19 +71,19 @@ CommandScheduler::CommandScheduler() HAL_Report(HALUsageReporting::kResourceType_Command, HALUsageReporting::kCommand2_Scheduler); frc::SendableRegistry::GetInstance().AddLW(this, "Scheduler"); - auto scheduler = frc::LiveWindow::GetInstance(); - scheduler->enabled = [this] { - this->Disable(); - this->CancelAll(); + auto& scheduler = frc::LiveWindow::GetInstance(); + scheduler.enabled = [this] { + Disable(); + CancelAll(); }; - scheduler->disabled = [this] { this->Enable(); }; + scheduler.disabled = [this] { Enable(); }; } CommandScheduler::~CommandScheduler() { frc::SendableRegistry::GetInstance().Remove(this); - auto scheduler = frc::LiveWindow::GetInstance(); - scheduler->enabled = nullptr; - scheduler->disabled = nullptr; + auto& scheduler = frc::LiveWindow::GetInstance(); + scheduler.enabled = nullptr; + scheduler.disabled = nullptr; std::unique_ptr().swap(m_impl); } diff --git a/wpilibOldCommands/src/main/native/cpp/commands/Scheduler.cpp b/wpilibOldCommands/src/main/native/cpp/commands/Scheduler.cpp index 73569b803d..8b3803ea80 100644 --- a/wpilibOldCommands/src/main/native/cpp/commands/Scheduler.cpp +++ b/wpilibOldCommands/src/main/native/cpp/commands/Scheduler.cpp @@ -203,19 +203,19 @@ Scheduler::Scheduler() : m_impl(new Impl) { HAL_Report(HALUsageReporting::kResourceType_Command, HALUsageReporting::kCommand_Scheduler); SendableRegistry::GetInstance().AddLW(this, "Scheduler"); - auto scheduler = frc::LiveWindow::GetInstance(); - scheduler->enabled = [this] { - this->SetEnabled(false); - this->RemoveAll(); + auto& scheduler = frc::LiveWindow::GetInstance(); + scheduler.enabled = [this] { + SetEnabled(false); + RemoveAll(); }; - scheduler->disabled = [this] { this->SetEnabled(true); }; + scheduler.disabled = [this] { SetEnabled(true); }; } Scheduler::~Scheduler() { SendableRegistry::GetInstance().Remove(this); - auto scheduler = frc::LiveWindow::GetInstance(); - scheduler->enabled = nullptr; - scheduler->disabled = nullptr; + auto& scheduler = frc::LiveWindow::GetInstance(); + scheduler.enabled = nullptr; + scheduler.disabled = nullptr; } void Scheduler::Impl::Remove(Command* command) { diff --git a/wpilibc/src/main/native/cpp/IterativeRobotBase.cpp b/wpilibc/src/main/native/cpp/IterativeRobotBase.cpp index 91929037b7..d96bef96f7 100644 --- a/wpilibc/src/main/native/cpp/IterativeRobotBase.cpp +++ b/wpilibc/src/main/native/cpp/IterativeRobotBase.cpp @@ -108,7 +108,7 @@ void IterativeRobotBase::LoopFunc() { // Call DisabledInit() if we are now just entering disabled mode from // either a different mode or from power-on. if (m_lastMode != Mode::kDisabled) { - LiveWindow::GetInstance()->SetEnabled(false); + LiveWindow::GetInstance().SetEnabled(false); Shuffleboard::DisableActuatorWidgets(); DisabledInit(); m_watchdog.AddEpoch("DisabledInit()"); @@ -122,7 +122,7 @@ void IterativeRobotBase::LoopFunc() { // Call AutonomousInit() if we are now just entering autonomous mode from // either a different mode or from power-on. if (m_lastMode != Mode::kAutonomous) { - LiveWindow::GetInstance()->SetEnabled(false); + LiveWindow::GetInstance().SetEnabled(false); Shuffleboard::DisableActuatorWidgets(); AutonomousInit(); m_watchdog.AddEpoch("AutonomousInit()"); @@ -136,7 +136,7 @@ void IterativeRobotBase::LoopFunc() { // Call TeleopInit() if we are now just entering teleop mode from // either a different mode or from power-on. if (m_lastMode != Mode::kTeleop) { - LiveWindow::GetInstance()->SetEnabled(false); + LiveWindow::GetInstance().SetEnabled(false); Shuffleboard::DisableActuatorWidgets(); TeleopInit(); m_watchdog.AddEpoch("TeleopInit()"); @@ -150,7 +150,7 @@ void IterativeRobotBase::LoopFunc() { // Call TestInit() if we are now just entering test mode from // either a different mode or from power-on. if (m_lastMode != Mode::kTest) { - LiveWindow::GetInstance()->SetEnabled(true); + LiveWindow::GetInstance().SetEnabled(true); Shuffleboard::EnableActuatorWidgets(); TestInit(); m_watchdog.AddEpoch("TestInit()"); @@ -167,7 +167,7 @@ void IterativeRobotBase::LoopFunc() { SmartDashboard::UpdateValues(); m_watchdog.AddEpoch("SmartDashboard::UpdateValues()"); - LiveWindow::GetInstance()->UpdateValues(); + LiveWindow::GetInstance().UpdateValues(); m_watchdog.AddEpoch("LiveWindow::UpdateValues()"); Shuffleboard::Update(); m_watchdog.AddEpoch("Shuffleboard::Update()"); diff --git a/wpilibc/src/main/native/cpp/Preferences.cpp b/wpilibc/src/main/native/cpp/Preferences.cpp index b221d5af10..8fa43a51db 100644 --- a/wpilibc/src/main/native/cpp/Preferences.cpp +++ b/wpilibc/src/main/native/cpp/Preferences.cpp @@ -17,9 +17,9 @@ using namespace frc; // The Preferences table name static wpi::StringRef kTableName{"Preferences"}; -Preferences* Preferences::GetInstance() { +Preferences& Preferences::GetInstance() { static Preferences instance; - return &instance; + return instance; } std::vector Preferences::GetKeys() { @@ -143,3 +143,5 @@ Preferences::Preferences() NT_NOTIFY_NEW | NT_NOTIFY_IMMEDIATE); HAL_Report(HALUsageReporting::kResourceType_Preferences, 0); } + +Preferences::~Preferences() = default; diff --git a/wpilibc/src/main/native/cpp/livewindow/LiveWindow.cpp b/wpilibc/src/main/native/cpp/livewindow/LiveWindow.cpp index 0c13b4da72..cdfba922b6 100644 --- a/wpilibc/src/main/native/cpp/livewindow/LiveWindow.cpp +++ b/wpilibc/src/main/native/cpp/livewindow/LiveWindow.cpp @@ -61,9 +61,9 @@ std::shared_ptr LiveWindow::Impl::GetOrAdd( return data; } -LiveWindow* LiveWindow::GetInstance() { +LiveWindow& LiveWindow::GetInstance() { static LiveWindow instance; - return &instance; + return instance; } void LiveWindow::EnableTelemetry(Sendable* sendable) { diff --git a/wpilibc/src/main/native/cppcs/RobotBase.cpp b/wpilibc/src/main/native/cppcs/RobotBase.cpp index d0b23faa9e..b112901e52 100644 --- a/wpilibc/src/main/native/cppcs/RobotBase.cpp +++ b/wpilibc/src/main/native/cppcs/RobotBase.cpp @@ -214,7 +214,7 @@ RobotBase::RobotBase() : m_ds(DriverStation::GetInstance()) { ->GetEntry("LW Enabled") .SetBoolean(false); - LiveWindow::GetInstance()->SetEnabled(false); + LiveWindow::GetInstance().SetEnabled(false); } RobotBase::RobotBase(RobotBase&&) noexcept diff --git a/wpilibc/src/main/native/include/frc/Preferences.h b/wpilibc/src/main/native/include/frc/Preferences.h index 368ff2cac8..0d192b7aba 100644 --- a/wpilibc/src/main/native/include/frc/Preferences.h +++ b/wpilibc/src/main/native/include/frc/Preferences.h @@ -35,9 +35,9 @@ class Preferences : public ErrorBase { /** * Get the one and only {@link Preferences} object. * - * @return pointer to the {@link Preferences} + * @return reference to the {@link Preferences} */ - static Preferences* GetInstance(); + static Preferences& GetInstance(); /** * Returns a vector of all the keys. @@ -226,7 +226,7 @@ class Preferences : public ErrorBase { protected: Preferences(); - ~Preferences() override = default; + ~Preferences() override; Preferences(Preferences&&) = default; Preferences& operator=(Preferences&&) = default; diff --git a/wpilibc/src/main/native/include/frc/livewindow/LiveWindow.h b/wpilibc/src/main/native/include/frc/livewindow/LiveWindow.h index f3b6092b52..4f8ce59725 100644 --- a/wpilibc/src/main/native/include/frc/livewindow/LiveWindow.h +++ b/wpilibc/src/main/native/include/frc/livewindow/LiveWindow.h @@ -29,7 +29,7 @@ class LiveWindow { * This is a singleton to guarantee that there is only a single instance * regardless of how many times GetInstance is called. */ - static LiveWindow* GetInstance(); + static LiveWindow& GetInstance(); /** * Enable telemetry for a single component. @@ -69,6 +69,7 @@ class LiveWindow { private: LiveWindow(); + ~LiveWindow() = default; struct Impl; std::unique_ptr m_impl; diff --git a/wpilibcExamples/src/main/cpp/examples/AxisCameraSample/cpp/Robot.cpp b/wpilibcExamples/src/main/cpp/examples/AxisCameraSample/cpp/Robot.cpp index 5051fb71ed..ce9f4580c0 100644 --- a/wpilibcExamples/src/main/cpp/examples/AxisCameraSample/cpp/Robot.cpp +++ b/wpilibcExamples/src/main/cpp/examples/AxisCameraSample/cpp/Robot.cpp @@ -19,15 +19,15 @@ class Robot : public frc::TimedRobot { static void VisionThread() { // Get the Axis camera from CameraServer cs::AxisCamera camera = - frc::CameraServer::GetInstance()->AddAxisCamera("axis-camera.local"); + frc::CameraServer::GetInstance().AddAxisCamera("axis-camera.local"); // Set the resolution camera.SetResolution(640, 480); // Get a CvSink. This will capture Mats from the Camera - cs::CvSink cvSink = frc::CameraServer::GetInstance()->GetVideo(); + cs::CvSink cvSink = frc::CameraServer::GetInstance().GetVideo(); // Setup a CvSource. This will send images back to the Dashboard cs::CvSource outputStream = - frc::CameraServer::GetInstance()->PutVideo("Rectangle", 640, 480); + frc::CameraServer::GetInstance().PutVideo("Rectangle", 640, 480); // Mats are very memory expensive. Lets reuse this Mat. cv::Mat mat; diff --git a/wpilibcExamples/src/main/cpp/examples/GettingStarted/cpp/Robot.cpp b/wpilibcExamples/src/main/cpp/examples/GettingStarted/cpp/Robot.cpp index 3f2d0c77fe..6996c7fd39 100644 --- a/wpilibcExamples/src/main/cpp/examples/GettingStarted/cpp/Robot.cpp +++ b/wpilibcExamples/src/main/cpp/examples/GettingStarted/cpp/Robot.cpp @@ -50,7 +50,7 @@ class Robot : public frc::TimedRobot { frc::DifferentialDrive m_robotDrive{m_left, m_right}; frc::Joystick m_stick{0}; - frc::LiveWindow& m_lw = *frc::LiveWindow::GetInstance(); + frc::LiveWindow& m_lw = frc::LiveWindow::GetInstance(); frc::Timer m_timer; }; diff --git a/wpilibcExamples/src/main/cpp/examples/IntermediateVision/cpp/Robot.cpp b/wpilibcExamples/src/main/cpp/examples/IntermediateVision/cpp/Robot.cpp index 923b4253cc..51778a304b 100644 --- a/wpilibcExamples/src/main/cpp/examples/IntermediateVision/cpp/Robot.cpp +++ b/wpilibcExamples/src/main/cpp/examples/IntermediateVision/cpp/Robot.cpp @@ -24,15 +24,15 @@ class Robot : public frc::TimedRobot { static void VisionThread() { // Get the USB camera from CameraServer cs::UsbCamera camera = - frc::CameraServer::GetInstance()->StartAutomaticCapture(); + frc::CameraServer::GetInstance().StartAutomaticCapture(); // Set the resolution camera.SetResolution(640, 480); // Get a CvSink. This will capture Mats from the Camera - cs::CvSink cvSink = frc::CameraServer::GetInstance()->GetVideo(); + cs::CvSink cvSink = frc::CameraServer::GetInstance().GetVideo(); // Setup a CvSource. This will send images back to the Dashboard cs::CvSource outputStream = - frc::CameraServer::GetInstance()->PutVideo("Rectangle", 640, 480); + frc::CameraServer::GetInstance().PutVideo("Rectangle", 640, 480); // Mats are very memory expensive. Lets reuse this Mat. cv::Mat mat; diff --git a/wpilibcExamples/src/main/cpp/examples/QuickVision/cpp/Robot.cpp b/wpilibcExamples/src/main/cpp/examples/QuickVision/cpp/Robot.cpp index b754deb277..d9f5071efc 100644 --- a/wpilibcExamples/src/main/cpp/examples/QuickVision/cpp/Robot.cpp +++ b/wpilibcExamples/src/main/cpp/examples/QuickVision/cpp/Robot.cpp @@ -16,7 +16,7 @@ class Robot : public frc::TimedRobot { public: void RobotInit() override { #if defined(__linux__) - frc::CameraServer::GetInstance()->StartAutomaticCapture(); + frc::CameraServer::GetInstance().StartAutomaticCapture(); #else wpi::errs() << "Vision only available on Linux.\n"; wpi::errs().flush(); diff --git a/wpilibcExamples/src/main/cpp/templates/robotbaseskeleton/cpp/Robot.cpp b/wpilibcExamples/src/main/cpp/templates/robotbaseskeleton/cpp/Robot.cpp index 4650f7f039..f457e69a55 100644 --- a/wpilibcExamples/src/main/cpp/templates/robotbaseskeleton/cpp/Robot.cpp +++ b/wpilibcExamples/src/main/cpp/templates/robotbaseskeleton/cpp/Robot.cpp @@ -21,7 +21,7 @@ void Robot::Teleop() {} void Robot::Test() {} void Robot::StartCompetition() { - auto& lw = *frc::LiveWindow::GetInstance(); + auto& lw = frc::LiveWindow::GetInstance(); RobotInit(); diff --git a/wpilibcIntegrationTests/src/main/native/cpp/PreferencesTest.cpp b/wpilibcIntegrationTests/src/main/native/cpp/PreferencesTest.cpp index 4b5ab1ca65..ba134efe87 100644 --- a/wpilibcIntegrationTests/src/main/native/cpp/PreferencesTest.cpp +++ b/wpilibcIntegrationTests/src/main/native/cpp/PreferencesTest.cpp @@ -44,14 +44,14 @@ TEST(PreferencesTest, ReadPreferencesFromFile) { preferencesFile.close(); inst.StartServer(); - Preferences* preferences = Preferences::GetInstance(); + Preferences& preferences = Preferences::GetInstance(); EXPECT_EQ("Hello, preferences file", - preferences->GetString("testFileGetString")); - EXPECT_EQ(1, preferences->GetInt("testFileGetInt")); - EXPECT_FLOAT_EQ(0.5, preferences->GetDouble("testFileGetDouble")); - EXPECT_FLOAT_EQ(0.25f, preferences->GetFloat("testFileGetFloat")); - EXPECT_TRUE(preferences->GetBoolean("testFileGetBoolean")); - EXPECT_EQ(1000000000000000000ll, preferences->GetLong("testFileGetLong")); + preferences.GetString("testFileGetString")); + EXPECT_EQ(1, preferences.GetInt("testFileGetInt")); + EXPECT_FLOAT_EQ(0.5, preferences.GetDouble("testFileGetDouble")); + EXPECT_FLOAT_EQ(0.25f, preferences.GetFloat("testFileGetFloat")); + EXPECT_TRUE(preferences.GetBoolean("testFileGetBoolean")); + EXPECT_EQ(1000000000000000000ll, preferences.GetLong("testFileGetLong")); } /** @@ -61,22 +61,22 @@ TEST(PreferencesTest, ReadPreferencesFromFile) { TEST(PreferencesTest, WritePreferencesToFile) { auto inst = nt::NetworkTableInstance::GetDefault(); inst.StartServer(); - Preferences* preferences = Preferences::GetInstance(); - preferences->Remove("testFileGetString"); - preferences->Remove("testFileGetInt"); - preferences->Remove("testFileGetDouble"); - preferences->Remove("testFileGetFloat"); - preferences->Remove("testFileGetBoolean"); - preferences->Remove("testFileGetLong"); + Preferences& preferences = Preferences::GetInstance(); + preferences.Remove("testFileGetString"); + preferences.Remove("testFileGetInt"); + preferences.Remove("testFileGetDouble"); + preferences.Remove("testFileGetFloat"); + preferences.Remove("testFileGetBoolean"); + preferences.Remove("testFileGetLong"); Wait(kSaveTime); - preferences->PutString("testFilePutString", "Hello, preferences file"); - preferences->PutInt("testFilePutInt", 1); - preferences->PutDouble("testFilePutDouble", 0.5); - preferences->PutFloat("testFilePutFloat", 0.25f); - preferences->PutBoolean("testFilePutBoolean", true); - preferences->PutLong("testFilePutLong", 1000000000000000000ll); + preferences.PutString("testFilePutString", "Hello, preferences file"); + preferences.PutInt("testFilePutInt", 1); + preferences.PutDouble("testFilePutDouble", 0.5); + preferences.PutFloat("testFilePutFloat", 0.25f); + preferences.PutBoolean("testFilePutBoolean", true); + preferences.PutLong("testFilePutLong", 1000000000000000000ll); Wait(kSaveTime); diff --git a/wpilibcIntegrationTests/src/main/native/cpp/TestEnvironment.cpp b/wpilibcIntegrationTests/src/main/native/cpp/TestEnvironment.cpp index 27d785ec1c..f848024d64 100644 --- a/wpilibcIntegrationTests/src/main/native/cpp/TestEnvironment.cpp +++ b/wpilibcIntegrationTests/src/main/native/cpp/TestEnvironment.cpp @@ -40,7 +40,7 @@ class TestEnvironment : public testing::Environment { station returns that the robot is enabled, to ensure that tests will be able to run on the hardware. */ HAL_ObserveUserProgramStarting(); - LiveWindow::GetInstance()->SetEnabled(false); + LiveWindow::GetInstance().SetEnabled(false); wpi::outs() << "Started coms\n"; From a6f6539691b30359d261f82916d3027889e00830 Mon Sep 17 00:00:00 2001 From: Peter Johnson Date: Sun, 21 Mar 2021 23:21:47 -0700 Subject: [PATCH 09/34] [hal] Move registerSimPeriodic functions to HAL package (#3211) This enables the static lists to be private. --- hal/src/main/java/edu/wpi/first/hal/HAL.java | 61 ++++++++++++++++++- .../first/hal/simulation/SimulatorJNI.java | 45 -------------- 2 files changed, 59 insertions(+), 47 deletions(-) diff --git a/hal/src/main/java/edu/wpi/first/hal/HAL.java b/hal/src/main/java/edu/wpi/first/hal/HAL.java index fb51ac02a4..6fd6910aab 100644 --- a/hal/src/main/java/edu/wpi/first/hal/HAL.java +++ b/hal/src/main/java/edu/wpi/first/hal/HAL.java @@ -28,7 +28,36 @@ public final class HAL extends JNIWrapper { private static native void simPeriodicBeforeNative(); - public static final List s_simPeriodicBefore = new ArrayList<>(); + private static final List s_simPeriodicBefore = new ArrayList<>(); + + public static class SimPeriodicBeforeCallback implements AutoCloseable { + private SimPeriodicBeforeCallback(Runnable r) { + m_run = r; + } + + @Override + public void close() { + synchronized (s_simPeriodicBefore) { + s_simPeriodicBefore.remove(m_run); + } + } + + private final Runnable m_run; + } + + /** + * Registers a callback to be run by IterativeRobotBase prior to the user's simulationPeriodic + * code. + * + * @param r runnable + * @return Callback object (must be retained for callback to stay active). + */ + public static SimPeriodicBeforeCallback registerSimPeriodicBeforeCallback(Runnable r) { + synchronized (s_simPeriodicBefore) { + s_simPeriodicBefore.add(r); + } + return new SimPeriodicBeforeCallback(r); + } /** * Runs SimPeriodicBefore callbacks. IterativeRobotBase calls this prior to the user's @@ -45,7 +74,35 @@ public final class HAL extends JNIWrapper { private static native void simPeriodicAfterNative(); - public static final List s_simPeriodicAfter = new ArrayList<>(); + private static final List s_simPeriodicAfter = new ArrayList<>(); + + public static class SimPeriodicAfterCallback implements AutoCloseable { + private SimPeriodicAfterCallback(Runnable r) { + m_run = r; + } + + @Override + public void close() { + synchronized (s_simPeriodicAfter) { + s_simPeriodicAfter.remove(m_run); + } + } + + private final Runnable m_run; + } + + /** + * Registers a callback to be run by IterativeRobotBase after the user's simulationPeriodic code. + * + * @param r runnable + * @return Callback object (must be retained for callback to stay active). + */ + public static SimPeriodicAfterCallback registerSimPeriodicAfterCallback(Runnable r) { + synchronized (s_simPeriodicAfter) { + s_simPeriodicAfter.add(r); + } + return new SimPeriodicAfterCallback(r); + } /** * Runs SimPeriodicAfter callbacks. IterativeRobotBase calls this after the user's diff --git a/hal/src/main/java/edu/wpi/first/hal/simulation/SimulatorJNI.java b/hal/src/main/java/edu/wpi/first/hal/simulation/SimulatorJNI.java index f3c61d7634..3dfeb068a9 100644 --- a/hal/src/main/java/edu/wpi/first/hal/simulation/SimulatorJNI.java +++ b/hal/src/main/java/edu/wpi/first/hal/simulation/SimulatorJNI.java @@ -4,7 +4,6 @@ package edu.wpi.first.hal.simulation; -import edu.wpi.first.hal.HAL; import edu.wpi.first.hal.JNIWrapper; public class SimulatorJNI extends JNIWrapper { @@ -29,48 +28,4 @@ public class SimulatorJNI extends JNIWrapper { public static native void stepTimingAsync(long delta); public static native void resetHandles(); - - public static class SimPeriodicBeforeCallback implements AutoCloseable { - private SimPeriodicBeforeCallback(Runnable r) { - m_run = r; - } - - @Override - public void close() { - synchronized (HAL.s_simPeriodicBefore) { - HAL.s_simPeriodicBefore.remove(m_run); - } - } - - private Runnable m_run; - } - - public static SimPeriodicBeforeCallback registerSimPeriodicBeforeCallback(Runnable r) { - synchronized (HAL.s_simPeriodicBefore) { - HAL.s_simPeriodicBefore.add(r); - } - return new SimPeriodicBeforeCallback(r); - } - - public static class SimPeriodicAfterCallback implements AutoCloseable { - private SimPeriodicAfterCallback(Runnable r) { - m_run = r; - } - - @Override - public void close() { - synchronized (HAL.s_simPeriodicAfter) { - HAL.s_simPeriodicAfter.remove(m_run); - } - } - - private Runnable m_run; - } - - public static SimPeriodicAfterCallback registerSimPeriodicAfterCallback(Runnable r) { - synchronized (HAL.s_simPeriodicAfter) { - HAL.s_simPeriodicAfter.add(r); - } - return new SimPeriodicAfterCallback(r); - } } From 6137f98eb5c7009c6f31bf26db9c63ba9139400b Mon Sep 17 00:00:00 2001 From: Peter Johnson Date: Sun, 21 Mar 2021 23:22:04 -0700 Subject: [PATCH 10/34] [hal] Rename SimValueCallback2 to SimValueCallback (#3212) --- .../hal/simulation/SimDeviceDataJNI.java | 8 +-- .../hal/simulation/SimValueCallback.java | 6 +- .../hal/simulation/SimValueCallback2.java | 17 ------ .../cpp/jni/simulation/SimDeviceDataJNI.cpp | 58 +++---------------- .../wpilibj/simulation/SimDeviceSim.java | 19 +----- 5 files changed, 14 insertions(+), 94 deletions(-) delete mode 100644 hal/src/main/java/edu/wpi/first/hal/simulation/SimValueCallback2.java diff --git a/hal/src/main/java/edu/wpi/first/hal/simulation/SimDeviceDataJNI.java b/hal/src/main/java/edu/wpi/first/hal/simulation/SimDeviceDataJNI.java index 36d3388000..33c1bac477 100644 --- a/hal/src/main/java/edu/wpi/first/hal/simulation/SimDeviceDataJNI.java +++ b/hal/src/main/java/edu/wpi/first/hal/simulation/SimDeviceDataJNI.java @@ -47,17 +47,11 @@ public class SimDeviceDataJNI extends JNIWrapper { public static native int registerSimValueCreatedCallback( int device, SimValueCallback callback, boolean initialNotify); - public static native int registerSimValueCreatedCallback2( - int device, SimValueCallback2 callback, boolean initialNotify); - public static native void cancelSimValueCreatedCallback(int uid); public static native int registerSimValueChangedCallback( int handle, SimValueCallback callback, boolean initialNotify); - public static native int registerSimValueChangedCallback2( - int handle, SimValueCallback2 callback, boolean initialNotify); - public static native void cancelSimValueChangedCallback(int uid); /** @@ -69,7 +63,7 @@ public class SimDeviceDataJNI extends JNIWrapper { * @param initialNotify ignored (present for consistency) */ public static native int registerSimValueResetCallback( - int handle, SimValueCallback2 callback, boolean initialNotify); + int handle, SimValueCallback callback, boolean initialNotify); public static native void cancelSimValueResetCallback(int uid); diff --git a/hal/src/main/java/edu/wpi/first/hal/simulation/SimValueCallback.java b/hal/src/main/java/edu/wpi/first/hal/simulation/SimValueCallback.java index c586210459..4f9da942cc 100644 --- a/hal/src/main/java/edu/wpi/first/hal/simulation/SimValueCallback.java +++ b/hal/src/main/java/edu/wpi/first/hal/simulation/SimValueCallback.java @@ -8,10 +8,10 @@ import edu.wpi.first.hal.HALValue; @FunctionalInterface public interface SimValueCallback { - void callback(String name, int handle, boolean readonly, HALValue value); + void callback(String name, int handle, int direction, HALValue value); default void callbackNative( - String name, int handle, boolean readonly, int type, long value1, double value2) { - callback(name, handle, readonly, HALValue.fromNative(type, value1, value2)); + String name, int handle, int direction, int type, long value1, double value2) { + callback(name, handle, direction, HALValue.fromNative(type, value1, value2)); } } diff --git a/hal/src/main/java/edu/wpi/first/hal/simulation/SimValueCallback2.java b/hal/src/main/java/edu/wpi/first/hal/simulation/SimValueCallback2.java deleted file mode 100644 index f575c7f42c..0000000000 --- a/hal/src/main/java/edu/wpi/first/hal/simulation/SimValueCallback2.java +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright (c) FIRST and other WPILib contributors. -// Open Source Software; you can modify and/or share it under the terms of -// the WPILib BSD license file in the root directory of this project. - -package edu.wpi.first.hal.simulation; - -import edu.wpi.first.hal.HALValue; - -@FunctionalInterface -public interface SimValueCallback2 { - void callback(String name, int handle, int direction, HALValue value); - - default void callbackNative( - String name, int handle, int direction, int type, long value1, double value2) { - callback(name, handle, direction, HALValue.fromNative(type, value1, value2)); - } -} diff --git a/hal/src/main/native/cpp/jni/simulation/SimDeviceDataJNI.cpp b/hal/src/main/native/cpp/jni/simulation/SimDeviceDataJNI.cpp index 5f02ebcbc1..a07e8af15c 100644 --- a/hal/src/main/native/cpp/jni/simulation/SimDeviceDataJNI.cpp +++ b/hal/src/main/native/cpp/jni/simulation/SimDeviceDataJNI.cpp @@ -109,8 +109,6 @@ class DeviceCallbackStore { class ValueCallbackStore { public: - explicit ValueCallbackStore(bool dirCallback) : m_dirCallback{dirCallback} {} - void create(JNIEnv* env, jobject obj) { m_call = JGlobal(env, obj); } void performCallback(const char* name, HAL_SimValueHandle handle, int32_t direction, const HAL_Value& value); @@ -121,7 +119,6 @@ class ValueCallbackStore { private: wpi::java::JGlobal m_call; int32_t m_callbackId; - bool m_dirCallback; }; } // namespace @@ -181,17 +178,9 @@ void ValueCallbackStore::performCallback(const char* name, } auto [value1, value2] = ToValue12(value); - if (m_dirCallback) { - env->CallVoidMethod(m_call, simValueCallbackCallback, - MakeJString(env, name), static_cast(handle), - static_cast(direction), - static_cast(value.type), value1, value2); - } else { - env->CallVoidMethod(m_call, simValueCallbackCallback, - MakeJString(env, name), static_cast(handle), - static_cast(direction == HAL_SimValueOutput), - static_cast(value.type), value1, value2); - } + env->CallVoidMethod(m_call, simValueCallbackCallback, MakeJString(env, name), + static_cast(handle), static_cast(direction), + static_cast(value.type), value1, value2); if (env->ExceptionCheck()) { env->ExceptionDescribe(); @@ -266,12 +255,11 @@ using FreeValueCallbackFunc = void (*)(int32_t uid); template static SIM_JniHandle AllocateValueCallback( - JNIEnv* env, THandle h, jobject callback, bool dirCallback, - jboolean initialNotify, + JNIEnv* env, THandle h, jobject callback, jboolean initialNotify, int32_t (*createCallback)(THandle handle, void* param, HALSIM_SimValueCallback callback, HAL_Bool initialNotify)) { - auto callbackStore = std::make_shared(dirCallback); + auto callbackStore = std::make_shared(); auto handle = valueCallbackHandles->Allocate(callbackStore); @@ -345,7 +333,7 @@ bool InitializeSimDeviceDataJNI(JNIEnv* env) { } simValueCallbackCallback = env->GetMethodID( - simValueCallbackCls, "callbackNative", "(Ljava/lang/String;IZIJD)V"); + simValueCallbackCls, "callbackNative", "(Ljava/lang/String;IIIJD)V"); if (!simValueCallbackCallback) { return false; } @@ -530,21 +518,7 @@ Java_edu_wpi_first_hal_simulation_SimDeviceDataJNI_registerSimValueCreatedCallba (JNIEnv* env, jclass, jint device, jobject callback, jboolean initialNotify) { return AllocateValueCallback(env, static_cast(device), - callback, false, initialNotify, - &HALSIM_RegisterSimValueCreatedCallback); -} - -/* - * Class: edu_wpi_first_hal_simulation_SimDeviceDataJNI - * Method: registerSimValueCreatedCallback2 - * Signature: (ILjava/lang/Object;Z)I - */ -JNIEXPORT jint JNICALL -Java_edu_wpi_first_hal_simulation_SimDeviceDataJNI_registerSimValueCreatedCallback2 - (JNIEnv* env, jclass, jint device, jobject callback, jboolean initialNotify) -{ - return AllocateValueCallback(env, static_cast(device), - callback, true, initialNotify, + callback, initialNotify, &HALSIM_RegisterSimValueCreatedCallback); } @@ -570,21 +544,7 @@ Java_edu_wpi_first_hal_simulation_SimDeviceDataJNI_registerSimValueChangedCallba (JNIEnv* env, jclass, jint handle, jobject callback, jboolean initialNotify) { return AllocateValueCallback(env, static_cast(handle), - callback, false, initialNotify, - &HALSIM_RegisterSimValueChangedCallback); -} - -/* - * Class: edu_wpi_first_hal_simulation_SimDeviceDataJNI - * Method: registerSimValueChangedCallback2 - * Signature: (ILjava/lang/Object;Z)I - */ -JNIEXPORT jint JNICALL -Java_edu_wpi_first_hal_simulation_SimDeviceDataJNI_registerSimValueChangedCallback2 - (JNIEnv* env, jclass, jint handle, jobject callback, jboolean initialNotify) -{ - return AllocateValueCallback(env, static_cast(handle), - callback, true, initialNotify, + callback, initialNotify, &HALSIM_RegisterSimValueChangedCallback); } @@ -610,7 +570,7 @@ Java_edu_wpi_first_hal_simulation_SimDeviceDataJNI_registerSimValueResetCallback (JNIEnv* env, jclass, jint handle, jobject callback, jboolean initialNotify) { return AllocateValueCallback(env, static_cast(handle), - callback, true, initialNotify, + callback, initialNotify, &HALSIM_RegisterSimValueResetCallback); } diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/simulation/SimDeviceSim.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/simulation/SimDeviceSim.java index 4aee29fe42..84b062cafe 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/simulation/SimDeviceSim.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/simulation/SimDeviceSim.java @@ -13,7 +13,6 @@ import edu.wpi.first.hal.SimValue; import edu.wpi.first.hal.simulation.SimDeviceCallback; import edu.wpi.first.hal.simulation.SimDeviceDataJNI; import edu.wpi.first.hal.simulation.SimValueCallback; -import edu.wpi.first.hal.simulation.SimValueCallback2; /** Class to control the simulation side of a SimDevice. */ public class SimDeviceSim { @@ -191,22 +190,6 @@ public class SimDeviceSim { return new CallbackStore(uid, SimDeviceDataJNI::cancelSimValueChangedCallback); } - /** - * Register a callback to be run every time a value is changed on this device. - * - * @param callback the callback - * @param initialNotify should the callback be run with the initial state - * @return the {@link CallbackStore} object associated with this callback. Save a reference to - * this object so GC doesn't cancel the callback. - */ - public CallbackStore registerValueChangedCallback2( - SimValue value, SimValueCallback2 callback, boolean initialNotify) { - int uid = - SimDeviceDataJNI.registerSimValueChangedCallback2( - value.getNativeHandle(), callback, initialNotify); - return new CallbackStore(uid, SimDeviceDataJNI::cancelSimValueChangedCallback); - } - /** * Register a callback for SimDouble.reset() and similar functions. The callback is called with * the old value. @@ -216,7 +199,7 @@ public class SimDeviceSim { * @param initialNotify ignored (present for consistency) */ public CallbackStore registerValueResetCallback( - SimValue value, SimValueCallback2 callback, boolean initialNotify) { + SimValue value, SimValueCallback callback, boolean initialNotify) { int uid = SimDeviceDataJNI.registerSimValueResetCallback( value.getNativeHandle(), callback, initialNotify); From 36608a283bbdeffbb033880f071cbbf76f82f755 Mon Sep 17 00:00:00 2001 From: Peter Johnson Date: Sat, 27 Feb 2021 09:59:35 -0800 Subject: [PATCH 11/34] [ntcore] Remove deprecated C++ APIs --- ntcore/src/main/native/cpp/ntcore_cpp.cpp | 148 ---------- ntcore/src/main/native/include/ntcore_cpp.h | 303 ++------------------ 2 files changed, 21 insertions(+), 430 deletions(-) diff --git a/ntcore/src/main/native/cpp/ntcore_cpp.cpp b/ntcore/src/main/native/cpp/ntcore_cpp.cpp index db190837fb..cf96f6a761 100644 --- a/ntcore/src/main/native/cpp/ntcore_cpp.cpp +++ b/ntcore/src/main/native/cpp/ntcore_cpp.cpp @@ -116,10 +116,6 @@ uint64_t GetEntryLastChange(NT_Entry entry) { return ii->storage.GetEntryLastChange(id); } -std::shared_ptr GetEntryValue(StringRef name) { - return InstanceImpl::GetDefault()->storage.GetEntryValue(name); -} - std::shared_ptr GetEntryValue(NT_Entry entry) { Handle handle{entry}; int id = handle.GetTypedIndex(Handle::kEntry); @@ -131,10 +127,6 @@ std::shared_ptr GetEntryValue(NT_Entry entry) { return ii->storage.GetEntryValue(id); } -bool SetDefaultEntryValue(StringRef name, std::shared_ptr value) { - return InstanceImpl::GetDefault()->storage.SetDefaultEntryValue(name, value); -} - bool SetDefaultEntryValue(NT_Entry entry, std::shared_ptr value) { Handle handle{entry}; int id = handle.GetTypedIndex(Handle::kEntry); @@ -146,10 +138,6 @@ bool SetDefaultEntryValue(NT_Entry entry, std::shared_ptr value) { return ii->storage.SetDefaultEntryValue(id, value); } -bool SetEntryValue(StringRef name, std::shared_ptr value) { - return InstanceImpl::GetDefault()->storage.SetEntryValue(name, value); -} - bool SetEntryValue(NT_Entry entry, std::shared_ptr value) { Handle handle{entry}; int id = handle.GetTypedIndex(Handle::kEntry); @@ -161,10 +149,6 @@ bool SetEntryValue(NT_Entry entry, std::shared_ptr value) { return ii->storage.SetEntryValue(id, value); } -void SetEntryTypeValue(StringRef name, std::shared_ptr value) { - InstanceImpl::GetDefault()->storage.SetEntryTypeValue(name, value); -} - void SetEntryTypeValue(NT_Entry entry, std::shared_ptr value) { Handle handle{entry}; int id = handle.GetTypedIndex(Handle::kEntry); @@ -176,10 +160,6 @@ void SetEntryTypeValue(NT_Entry entry, std::shared_ptr value) { ii->storage.SetEntryTypeValue(id, value); } -void SetEntryFlags(StringRef name, unsigned int flags) { - InstanceImpl::GetDefault()->storage.SetEntryFlags(name, flags); -} - void SetEntryFlags(NT_Entry entry, unsigned int flags) { Handle handle{entry}; int id = handle.GetTypedIndex(Handle::kEntry); @@ -191,10 +171,6 @@ void SetEntryFlags(NT_Entry entry, unsigned int flags) { ii->storage.SetEntryFlags(id, flags); } -unsigned int GetEntryFlags(StringRef name) { - return InstanceImpl::GetDefault()->storage.GetEntryFlags(name); -} - unsigned int GetEntryFlags(NT_Entry entry) { Handle handle{entry}; int id = handle.GetTypedIndex(Handle::kEntry); @@ -206,10 +182,6 @@ unsigned int GetEntryFlags(NT_Entry entry) { return ii->storage.GetEntryFlags(id); } -void DeleteEntry(StringRef name) { - InstanceImpl::GetDefault()->storage.DeleteEntry(name); -} - void DeleteEntry(NT_Entry entry) { Handle handle{entry}; int id = handle.GetTypedIndex(Handle::kEntry); @@ -221,10 +193,6 @@ void DeleteEntry(NT_Entry entry) { ii->storage.DeleteEntry(id); } -void DeleteAllEntries() { - InstanceImpl::GetDefault()->storage.DeleteAllEntries(); -} - void DeleteAllEntries(NT_Inst inst) { int i = Handle{inst}.GetTypedInst(Handle::kInstance); auto ii = InstanceImpl::Get(i); @@ -235,10 +203,6 @@ void DeleteAllEntries(NT_Inst inst) { ii->storage.DeleteAllEntries(); } -std::vector GetEntryInfo(StringRef prefix, unsigned int types) { - return InstanceImpl::GetDefault()->storage.GetEntryInfo(0, prefix, types); -} - std::vector GetEntryInfo(NT_Inst inst, const Twine& prefix, unsigned int types) { int i = Handle{inst}.GetTypedInst(Handle::kInstance); @@ -271,17 +235,6 @@ EntryInfo GetEntryInfo(NT_Entry entry) { * Callback Creation Functions */ -NT_EntryListener AddEntryListener(StringRef prefix, - EntryListenerCallback callback, - unsigned int flags) { - return AddEntryListener( - Handle(InstanceImpl::GetDefaultIndex(), 0, Handle::kInstance), prefix, - [=](const EntryNotification& event) { - callback(event.listener, event.name, event.value, event.flags); - }, - flags); -} - NT_EntryListener AddEntryListener( NT_Inst inst, const Twine& prefix, std::function callback, @@ -430,16 +383,6 @@ bool WaitForEntryListenerQueue(NT_Inst inst, double timeout) { return ii->entry_notifier.WaitForQueue(timeout); } -NT_ConnectionListener AddConnectionListener(ConnectionListenerCallback callback, - bool immediate_notify) { - return AddConnectionListener( - Handle(InstanceImpl::GetDefaultIndex(), 0, Handle::kInstance), - [=](const ConnectionNotification& event) { - callback(event.listener, event.connected, event.conn); - }, - immediate_notify); -} - NT_ConnectionListener AddConnectionListener( NT_Inst inst, std::function callback, @@ -880,10 +823,6 @@ uint64_t Now() { * Client/Server Functions */ -void SetNetworkIdentity(StringRef name) { - InstanceImpl::GetDefault()->dispatcher.SetIdentity(name); -} - void SetNetworkIdentity(NT_Inst inst, const Twine& name) { auto ii = InstanceImpl::Get(Handle{inst}.GetTypedInst(Handle::kInstance)); if (!ii) { @@ -893,10 +832,6 @@ void SetNetworkIdentity(NT_Inst inst, const Twine& name) { ii->dispatcher.SetIdentity(name); } -unsigned int GetNetworkMode() { - return InstanceImpl::GetDefault()->dispatcher.GetNetworkMode(); -} - unsigned int GetNetworkMode(NT_Inst inst) { auto ii = InstanceImpl::Get(Handle{inst}.GetTypedInst(Handle::kInstance)); if (!ii) { @@ -924,12 +859,6 @@ void StopLocal(NT_Inst inst) { ii->dispatcher.Stop(); } -void StartServer(StringRef persist_filename, const char* listen_address, - unsigned int port) { - auto ii = InstanceImpl::GetDefault(); - ii->dispatcher.StartServer(persist_filename, listen_address, port); -} - void StartServer(NT_Inst inst, const Twine& persist_filename, const char* listen_address, unsigned int port) { auto ii = InstanceImpl::Get(Handle{inst}.GetTypedInst(Handle::kInstance)); @@ -940,10 +869,6 @@ void StartServer(NT_Inst inst, const Twine& persist_filename, ii->dispatcher.StartServer(persist_filename, listen_address, port); } -void StopServer() { - InstanceImpl::GetDefault()->dispatcher.Stop(); -} - void StopServer(NT_Inst inst) { auto ii = InstanceImpl::Get(Handle{inst}.GetTypedInst(Handle::kInstance)); if (!ii) { @@ -953,10 +878,6 @@ void StopServer(NT_Inst inst) { ii->dispatcher.Stop(); } -void StartClient() { - InstanceImpl::GetDefault()->dispatcher.StartClient(); -} - void StartClient(NT_Inst inst) { auto ii = InstanceImpl::Get(Handle{inst}.GetTypedInst(Handle::kInstance)); if (!ii) { @@ -966,12 +887,6 @@ void StartClient(NT_Inst inst) { ii->dispatcher.StartClient(); } -void StartClient(const char* server_name, unsigned int port) { - auto ii = InstanceImpl::GetDefault(); - ii->dispatcher.SetServer(server_name, port); - ii->dispatcher.StartClient(); -} - void StartClient(NT_Inst inst, const char* server_name, unsigned int port) { auto ii = InstanceImpl::Get(Handle{inst}.GetTypedInst(Handle::kInstance)); if (!ii) { @@ -982,12 +897,6 @@ void StartClient(NT_Inst inst, const char* server_name, unsigned int port) { ii->dispatcher.StartClient(); } -void StartClient(ArrayRef> servers) { - auto ii = InstanceImpl::GetDefault(); - ii->dispatcher.SetServer(servers); - ii->dispatcher.StartClient(); -} - void StartClient(NT_Inst inst, ArrayRef> servers) { auto ii = InstanceImpl::Get(Handle{inst}.GetTypedInst(Handle::kInstance)); @@ -1009,10 +918,6 @@ void StartClientTeam(NT_Inst inst, unsigned int team, unsigned int port) { ii->dispatcher.StartClient(); } -void StopClient() { - InstanceImpl::GetDefault()->dispatcher.Stop(); -} - void StopClient(NT_Inst inst) { auto ii = InstanceImpl::Get(Handle{inst}.GetTypedInst(Handle::kInstance)); if (!ii) { @@ -1022,10 +927,6 @@ void StopClient(NT_Inst inst) { ii->dispatcher.Stop(); } -void SetServer(const char* server_name, unsigned int port) { - InstanceImpl::GetDefault()->dispatcher.SetServer(server_name, port); -} - void SetServer(NT_Inst inst, const char* server_name, unsigned int port) { auto ii = InstanceImpl::Get(Handle{inst}.GetTypedInst(Handle::kInstance)); if (!ii) { @@ -1035,10 +936,6 @@ void SetServer(NT_Inst inst, const char* server_name, unsigned int port) { ii->dispatcher.SetServer(server_name, port); } -void SetServer(ArrayRef> servers) { - InstanceImpl::GetDefault()->dispatcher.SetServer(servers); -} - void SetServer(NT_Inst inst, ArrayRef> servers) { auto ii = InstanceImpl::Get(Handle{inst}.GetTypedInst(Handle::kInstance)); @@ -1058,10 +955,6 @@ void SetServerTeam(NT_Inst inst, unsigned int team, unsigned int port) { ii->dispatcher.SetServerTeam(team, port); } -void StartDSClient(unsigned int port) { - InstanceImpl::GetDefault()->ds_client.Start(port); -} - void StartDSClient(NT_Inst inst, unsigned int port) { auto ii = InstanceImpl::Get(Handle{inst}.GetTypedInst(Handle::kInstance)); if (!ii) { @@ -1071,10 +964,6 @@ void StartDSClient(NT_Inst inst, unsigned int port) { ii->ds_client.Start(port); } -void StopDSClient() { - InstanceImpl::GetDefault()->ds_client.Stop(); -} - void StopDSClient(NT_Inst inst) { auto ii = InstanceImpl::Get(Handle{inst}.GetTypedInst(Handle::kInstance)); if (!ii) { @@ -1084,10 +973,6 @@ void StopDSClient(NT_Inst inst) { ii->ds_client.Stop(); } -void SetUpdateRate(double interval) { - InstanceImpl::GetDefault()->dispatcher.SetUpdateRate(interval); -} - void SetUpdateRate(NT_Inst inst, double interval) { auto ii = InstanceImpl::Get(Handle{inst}.GetTypedInst(Handle::kInstance)); if (!ii) { @@ -1097,10 +982,6 @@ void SetUpdateRate(NT_Inst inst, double interval) { ii->dispatcher.SetUpdateRate(interval); } -void Flush() { - InstanceImpl::GetDefault()->dispatcher.Flush(); -} - void Flush(NT_Inst inst) { auto ii = InstanceImpl::Get(Handle{inst}.GetTypedInst(Handle::kInstance)); if (!ii) { @@ -1110,10 +991,6 @@ void Flush(NT_Inst inst) { ii->dispatcher.Flush(); } -std::vector GetConnections() { - return InstanceImpl::GetDefault()->dispatcher.GetConnections(); -} - std::vector GetConnections(NT_Inst inst) { auto ii = InstanceImpl::Get(Handle{inst}.GetTypedInst(Handle::kInstance)); if (!ii) { @@ -1136,10 +1013,6 @@ bool IsConnected(NT_Inst inst) { * Persistent Functions */ -const char* SavePersistent(StringRef filename) { - return InstanceImpl::GetDefault()->storage.SavePersistent(filename, false); -} - const char* SavePersistent(NT_Inst inst, const Twine& filename) { auto ii = InstanceImpl::Get(Handle{inst}.GetTypedInst(Handle::kInstance)); if (!ii) { @@ -1149,12 +1022,6 @@ const char* SavePersistent(NT_Inst inst, const Twine& filename) { return ii->storage.SavePersistent(filename, false); } -const char* LoadPersistent( - StringRef filename, - std::function warn) { - return InstanceImpl::GetDefault()->storage.LoadPersistent(filename, warn); -} - const char* LoadPersistent( NT_Inst inst, const Twine& filename, std::function warn) { @@ -1187,21 +1054,6 @@ const char* LoadEntries( return ii->storage.LoadEntries(filename, prefix, warn); } -void SetLogger(LogFunc func, unsigned int min_level) { - auto ii = InstanceImpl::GetDefault(); - static wpi::mutex mutex; - static unsigned int logger = 0; - std::scoped_lock lock(mutex); - if (logger != 0) { - ii->logger_impl.Remove(logger); - } - logger = ii->logger_impl.Add( - [=](const LogMessage& msg) { - func(msg.level, msg.filename, msg.line, msg.message.c_str()); - }, - min_level, UINT_MAX); -} - NT_Logger AddLogger(NT_Inst inst, std::function func, unsigned int min_level, unsigned int max_level) { diff --git a/ntcore/src/main/native/include/ntcore_cpp.h b/ntcore/src/main/native/include/ntcore_cpp.h index 3964975797..46ac116f81 100644 --- a/ntcore/src/main/native/include/ntcore_cpp.h +++ b/ntcore/src/main/native/include/ntcore_cpp.h @@ -18,7 +18,6 @@ #include #include #include -#include #include "networktables/NetworkTableValue.h" @@ -376,18 +375,6 @@ NT_Type GetEntryType(NT_Entry entry); */ uint64_t GetEntryLastChange(NT_Entry entry); -/** - * Get Entry Value. - * - * Returns copy of current entry value. - * Note that one of the type options is "unassigned". - * - * @param name entry name (UTF-8 string) - * @return entry value - */ -WPI_DEPRECATED("use NT_Entry function instead") -std::shared_ptr GetEntryValue(StringRef name); - /** * Get Entry Value. * @@ -399,20 +386,6 @@ std::shared_ptr GetEntryValue(StringRef name); */ std::shared_ptr GetEntryValue(NT_Entry entry); -/** - * Set Default Entry Value - * - * Returns copy of current entry value if it exists. - * Otherwise, sets passed in value, and returns set value. - * Note that one of the type options is "unassigned". - * - * @param name entry name (UTF-8 string) - * @param value value to be set if name does not exist - * @return False on error (value not set), True on success - */ -WPI_DEPRECATED("use NT_Entry function instead") -bool SetDefaultEntryValue(StringRef name, std::shared_ptr value); - /** * Set Default Entry Value * @@ -426,19 +399,6 @@ bool SetDefaultEntryValue(StringRef name, std::shared_ptr value); */ bool SetDefaultEntryValue(NT_Entry entry, std::shared_ptr value); -/** - * Set Entry Value. - * - * Sets new entry value. If type of new value differs from the type of the - * currently stored entry, returns error and does not update value. - * - * @param name entry name (UTF-8 string) - * @param value new entry value - * @return False on error (type mismatch), True on success - */ -WPI_DEPRECATED("use NT_Entry function instead") -bool SetEntryValue(StringRef name, std::shared_ptr value); - /** * Set Entry Value. * @@ -451,22 +411,6 @@ bool SetEntryValue(StringRef name, std::shared_ptr value); */ bool SetEntryValue(NT_Entry entry, std::shared_ptr value); -/** - * Set Entry Type and Value. - * - * Sets new entry value. If type of new value differs from the type of the - * currently stored entry, the currently stored entry type is overridden - * (generally this will generate an Entry Assignment message). - * - * This is NOT the preferred method to update a value; generally - * SetEntryValue() should be used instead, with appropriate error handling. - * - * @param name entry name (UTF-8 string) - * @param value new entry value - */ -WPI_DEPRECATED("use NT_Entry function instead") -void SetEntryTypeValue(StringRef name, std::shared_ptr value); - /** * Set Entry Type and Value. * @@ -482,15 +426,6 @@ void SetEntryTypeValue(StringRef name, std::shared_ptr value); */ void SetEntryTypeValue(NT_Entry entry, std::shared_ptr value); -/** - * Set Entry Flags. - * - * @param name entry name (UTF-8 string) - * @param flags flags value (bitmask of NT_EntryFlags) - */ -WPI_DEPRECATED("use NT_Entry function instead") -void SetEntryFlags(StringRef name, unsigned int flags); - /** * Set Entry Flags. * @@ -499,15 +434,6 @@ void SetEntryFlags(StringRef name, unsigned int flags); */ void SetEntryFlags(NT_Entry entry, unsigned int flags); -/** - * Get Entry Flags. - * - * @param name entry name (UTF-8 string) - * @return Flags value (bitmask of NT_EntryFlags) - */ -WPI_DEPRECATED("use NT_Entry function instead") -unsigned int GetEntryFlags(StringRef name); - /** * Get Entry Flags. * @@ -516,22 +442,6 @@ unsigned int GetEntryFlags(StringRef name); */ unsigned int GetEntryFlags(NT_Entry entry); -/** - * Delete Entry. - * - * Deletes an entry. This is a new feature in version 3.0 of the protocol, - * so this may not have an effect if any other node in the network is not - * version 3.0 or newer. - * - * Note: GetConnections() can be used to determine the protocol version - * of direct remote connection(s), but this is not sufficient to determine - * if all nodes in the network are version 3.0 or newer. - * - * @param name entry name (UTF-8 string) - */ -WPI_DEPRECATED("use NT_Entry function instead") -void DeleteEntry(StringRef name); - /** * Delete Entry. * @@ -557,12 +467,6 @@ void DeleteEntry(NT_Entry entry); * Note: GetConnections() can be used to determine the protocol version * of direct remote connection(s), but this is not sufficient to determine * if all nodes in the network are version 3.0 or newer. - */ -WPI_DEPRECATED("use NT_Inst function instead") -void DeleteAllEntries(); - -/** - * @copydoc DeleteAllEntries() * * @param inst instance handle */ @@ -576,20 +480,13 @@ void DeleteAllEntries(NT_Inst inst); * filtered by string prefix and entry type to only return a subset of all * entries. * + * @param inst instance handle * @param prefix entry name required prefix; only entries whose name * starts with this string are returned * @param types bitmask of NT_Type values; 0 is treated specially * as a "don't care" * @return Array of entry information. */ -WPI_DEPRECATED("use NT_Inst function instead") -std::vector GetEntryInfo(StringRef prefix, unsigned int types); - -/** - * @copydoc GetEntryInfo(StringRef, unsigned int) - * - * @param inst instance handle - */ std::vector GetEntryInfo(NT_Inst inst, const Twine& prefix, unsigned int types); @@ -629,21 +526,12 @@ typedef std::function callback, @@ -787,19 +675,11 @@ using ConnectionListenerCallback = /** * Add a connection listener. * + * @param inst instance handle * @param callback listener to add * @param immediate_notify notify listener of all existing connections * @return Listener handle */ -WPI_DEPRECATED("use NT_Inst function instead") -NT_ConnectionListener AddConnectionListener(ConnectionListenerCallback callback, - bool immediate_notify); - -/** - * @copydoc AddConnectionListener(ConnectionListenerCallback, bool) - * - * @param inst instance handle - */ NT_ConnectionListener AddConnectionListener( NT_Inst inst, std::function callback, @@ -1102,26 +982,11 @@ std::vector> UnpackRpcValues(StringRef packed, * This is the name used during the initial connection handshake, and is * visible through ConnectionInfo on the remote node. * + * @param inst instance handle * @param name identity to advertise */ -WPI_DEPRECATED("use NT_Inst function instead") -void SetNetworkIdentity(StringRef name); - -/** - * @copydoc SetNetworkIdentity(StringRef) - * - * @param inst instance handle - */ void SetNetworkIdentity(NT_Inst inst, const Twine& name); -/** - * Get the current network mode. - * - * @return Bitmask of NT_NetworkMode. - */ -WPI_DEPRECATED("use NT_Inst function instead") -unsigned int GetNetworkMode(); - /** * Get the current network mode. * @@ -1146,32 +1011,18 @@ void StopLocal(NT_Inst inst); /** * Starts a server using the specified filename, listening address, and port. * + * @param inst instance handle * @param persist_filename the name of the persist file to use (UTF-8 string, * null terminated) * @param listen_address the address to listen on, or null to listen on any * address. (UTF-8 string, null terminated) * @param port port to communicate over. */ -WPI_DEPRECATED("use NT_Inst function instead") -void StartServer(StringRef persist_filename, const char* listen_address, - unsigned int port); - -/** - * @copydoc StartServer(StringRef, const char*, unsigned int) - * - * @param inst instance handle - */ void StartServer(NT_Inst inst, const Twine& persist_filename, const char* listen_address, unsigned int port); /** * Stops the server if it is running. - */ -WPI_DEPRECATED("use NT_Inst function instead") -void StopServer(); - -/** - * @copydoc StopServer() * * @param inst instance handle */ @@ -1179,46 +1030,26 @@ void StopServer(NT_Inst inst); /** * Starts a client. Use SetServer to set the server name and port. - */ -WPI_DEPRECATED("use NT_Inst function instead") -void StartClient(); - -/** - * Starts a client using the specified server and port - * - * @param server_name server name (UTF-8 string, null terminated) - * @param port port to communicate over - */ -WPI_DEPRECATED("use NT_Inst function instead") -void StartClient(const char* server_name, unsigned int port); - -/** - * Starts a client using the specified (server, port) combinations. The - * client will attempt to connect to each server in round robin fashion. - * - * @param servers array of server name and port pairs - */ -WPI_DEPRECATED("use NT_Inst function instead") -void StartClient(ArrayRef> servers); - -/** - * @copydoc StartClient() * * @param inst instance handle */ void StartClient(NT_Inst inst); /** - * @copydoc StartClient(const char*, unsigned int) + * Starts a client using the specified server and port * * @param inst instance handle + * @param server_name server name (UTF-8 string, null terminated) + * @param port port to communicate over */ void StartClient(NT_Inst inst, const char* server_name, unsigned int port); /** - * @copydoc StartClient(ArrayRef>) + * Starts a client using the specified (server, port) combinations. The + * client will attempt to connect to each server in round robin fashion. * * @param inst instance handle + * @param servers array of server name and port pairs */ void StartClient(NT_Inst inst, ArrayRef> servers); @@ -1235,12 +1066,7 @@ void StartClientTeam(NT_Inst inst, unsigned int team, unsigned int port); /** * Stops the client if it is running. - */ -WPI_DEPRECATED("use NT_Inst function instead") -void StopClient(); - -/** - * @copydoc StopClient() + * * @param inst instance handle */ void StopClient(NT_Inst inst); @@ -1248,32 +1074,18 @@ void StopClient(NT_Inst inst); /** * Sets server address and port for client (without restarting client). * + * @param inst instance handle * @param server_name server name (UTF-8 string, null terminated) * @param port port to communicate over */ -WPI_DEPRECATED("use NT_Inst function instead") -void SetServer(const char* server_name, unsigned int port); +void SetServer(NT_Inst inst, const char* server_name, unsigned int port); /** * Sets server addresses for client (without restarting client). * The client will attempt to connect to each server in round robin fashion. * - * @param servers array of server name and port pairs - */ -WPI_DEPRECATED("use NT_Inst function instead") -void SetServer(ArrayRef> servers); - -/** - * @copydoc SetServer(const char*, unsigned int) - * - * @param inst instance handle - */ -void SetServer(NT_Inst inst, const char* server_name, unsigned int port); - -/** - * @copydoc SetServer(ArrayRef>) - * * @param inst instance handle + * @param servers array of server name and port pairs */ void SetServer(NT_Inst inst, ArrayRef> servers); @@ -1293,45 +1105,24 @@ void SetServerTeam(NT_Inst inst, unsigned int team, unsigned int port); * This connects to the Driver Station running on localhost to obtain the * server IP address. * - * @param port server port to use in combination with IP from DS - */ -WPI_DEPRECATED("use NT_Inst function instead") -void StartDSClient(unsigned int port); - -/** - * @copydoc StartDSClient(unsigned int) * @param inst instance handle + * @param port server port to use in combination with IP from DS */ void StartDSClient(NT_Inst inst, unsigned int port); -/** Stops requesting server address from Driver Station. */ -WPI_DEPRECATED("use NT_Inst function instead") -void StopDSClient(); - /** - * @copydoc StopDSClient() + * Stops requesting server address from Driver Station. * * @param inst instance handle */ void StopDSClient(NT_Inst inst); -/** Stops the RPC server if it is running. */ -WPI_DEPRECATED("use NT_Inst function instead") -void StopRpcServer(); - /** * Set the periodic update rate. * Sets how frequently updates are sent to other nodes over the network. * - * @param interval update interval in seconds (range 0.01 to 1.0) - */ -WPI_DEPRECATED("use NT_Inst function instead") -void SetUpdateRate(double interval); - -/** - * @copydoc SetUpdateRate(double) - * * @param inst instance handle + * @param interval update interval in seconds (range 0.01 to 1.0) */ void SetUpdateRate(NT_Inst inst, double interval); @@ -1345,12 +1136,6 @@ void SetUpdateRate(NT_Inst inst, double interval); * Note: flushes are rate limited to avoid excessive network traffic. If * the time between calls is too short, the flush will occur after the minimum * time elapses (rather than immediately). - */ -WPI_DEPRECATED("use NT_Inst function instead") -void Flush(); - -/** - * @copydoc Flush() * * @param inst instance handle */ @@ -1360,15 +1145,8 @@ void Flush(NT_Inst inst); * Get information on the currently established network connections. * If operating as a client, this will return either zero or one values. * - * @return array of connection information - */ -WPI_DEPRECATED("use NT_Inst function instead") -std::vector GetConnections(); - -/** - * @copydoc GetConnections() - * * @param inst instance handle + * @return array of connection information */ std::vector GetConnections(NT_Inst inst); @@ -1392,16 +1170,10 @@ bool IsConnected(NT_Inst inst); * but this function provides a way to save persistent values in the same * format to a file on either a client or a server. * + * @param inst instance handle * @param filename filename * @return error string, or nullptr if successful */ -WPI_DEPRECATED("use NT_Inst function instead") -const char* SavePersistent(StringRef filename); - -/** - * @copydoc SavePersistent(StringRef) - * @param inst instance handle - */ const char* SavePersistent(NT_Inst inst, const Twine& filename); /** @@ -1409,20 +1181,11 @@ const char* SavePersistent(NT_Inst inst, const Twine& filename); * at startup, but this function provides a way to restore persistent values * in the same format from a file at any time on either a client or a server. * + * @param inst instance handle * @param filename filename * @param warn callback function for warnings * @return error string, or nullptr if successful */ -WPI_DEPRECATED("use NT_Inst function instead") -const char* LoadPersistent( - StringRef filename, std::function warn); - -/** - * @copydoc LoadPersistent(StringRef, std::function) - * - * @param inst instance handle - */ const char* LoadPersistent( NT_Inst inst, const Twine& filename, std::function warn); @@ -1476,30 +1239,6 @@ uint64_t Now(); * @{ */ -/** - * Log function. - * - * @param level log level of the message (see NT_LogLevel) - * @param file origin source filename - * @param line origin source line number - * @param msg message - */ -using LogFunc = - std::function; - -/** - * Set logger callback function. By default, log messages are sent to stderr; - * this function changes the log level and sends log messages to the provided - * callback function instead. The callback function will only be called for - * log messages with level greater than or equal to min_level; messages lower - * than this level will be silently ignored. - * - * @param func log callback function - * @param min_level minimum log level - */ -WPI_DEPRECATED("use NT_Inst function instead") -void SetLogger(LogFunc func, unsigned int min_level); - /** * Add logger callback function. By default, log messages are sent to stderr; * this function sends log messages to the provided callback function instead. From c2064c78b2182554b1ca589de8e53ea5b31d72af Mon Sep 17 00:00:00 2001 From: Peter Johnson Date: Sun, 4 Apr 2021 13:53:42 -0700 Subject: [PATCH 12/34] [ntcore] Remove deprecated ITable interfaces --- .../native/cpp/networktables/NetworkTable.cpp | 254 +--------- .../main/native/cpp/tables/ITableListener.cpp | 13 - .../include/networktables/NetworkTable.h | 304 ++---------- .../src/main/native/include/tables/ITable.h | 453 ------------------ .../native/include/tables/ITableListener.h | 65 --- 5 files changed, 44 insertions(+), 1045 deletions(-) delete mode 100644 ntcore/src/main/native/cpp/tables/ITableListener.cpp delete mode 100644 ntcore/src/main/native/include/tables/ITable.h delete mode 100644 ntcore/src/main/native/include/tables/ITableListener.h diff --git a/ntcore/src/main/native/cpp/networktables/NetworkTable.cpp b/ntcore/src/main/native/cpp/networktables/NetworkTable.cpp index f4a9bd52ac..5178b2ad4b 100644 --- a/ntcore/src/main/native/cpp/networktables/NetworkTable.cpp +++ b/ntcore/src/main/native/cpp/networktables/NetworkTable.cpp @@ -12,23 +12,9 @@ #include "networktables/NetworkTableInstance.h" #include "ntcore.h" -#include "tables/ITableListener.h" using namespace nt; -#ifdef __GNUC__ -#pragma GCC diagnostic ignored "-Wdeprecated-declarations" -#elif _WIN32 -#pragma warning(disable : 4996) -#endif - -const char NetworkTable::PATH_SEPARATOR_CHAR = '/'; -std::string NetworkTable::s_persistent_filename = "networktables.ini"; -bool NetworkTable::s_client = false; -bool NetworkTable::s_enable_ds = true; -bool NetworkTable::s_running = false; -unsigned int NetworkTable::s_port = NT_DEFAULT_PORT; - StringRef NetworkTable::BasenameKey(StringRef key) { size_t slash = key.rfind(PATH_SEPARATOR_CHAR); if (slash == StringRef::npos) { @@ -90,141 +76,11 @@ std::vector NetworkTable::GetHierarchy(const Twine& key) { return hierarchy; } -void NetworkTable::Initialize() { - if (s_running) { - Shutdown(); - } - auto inst = NetworkTableInstance::GetDefault(); - if (s_client) { - inst.StartClient(); - if (s_enable_ds) { - inst.StartDSClient(s_port); - } - } else { - inst.StartServer(s_persistent_filename, "", s_port); - } - s_running = true; -} - -void NetworkTable::Shutdown() { - if (!s_running) { - return; - } - auto inst = NetworkTableInstance::GetDefault(); - if (s_client) { - inst.StopDSClient(); - inst.StopClient(); - } else { - inst.StopServer(); - } - s_running = false; -} - -void NetworkTable::SetClientMode() { - s_client = true; -} - -void NetworkTable::SetServerMode() { - s_client = false; -} - -void NetworkTable::SetTeam(int team) { - auto inst = NetworkTableInstance::GetDefault(); - inst.SetServerTeam(team, s_port); - if (s_enable_ds) { - inst.StartDSClient(s_port); - } -} - -void NetworkTable::SetIPAddress(StringRef address) { - auto inst = NetworkTableInstance::GetDefault(); - wpi::SmallString<32> addr_copy{address}; - inst.SetServer(addr_copy.c_str(), s_port); - - // Stop the DS client if we're explicitly connecting to localhost - if (address == "localhost" || address == "127.0.0.1") { - inst.StopDSClient(); - } else if (s_enable_ds) { - inst.StartDSClient(s_port); - } -} - -void NetworkTable::SetIPAddress(ArrayRef addresses) { - auto inst = NetworkTableInstance::GetDefault(); - wpi::SmallVector servers; - for (const auto& ip_address : addresses) { - servers.emplace_back(ip_address); - } - inst.SetServer(servers, s_port); - - // Stop the DS client if we're explicitly connecting to localhost - if (!addresses.empty() && - (addresses[0] == "localhost" || addresses[0] == "127.0.0.1")) { - inst.StopDSClient(); - } else if (s_enable_ds) { - inst.StartDSClient(s_port); - } -} - -void NetworkTable::SetPort(unsigned int port) { - s_port = port; -} - -void NetworkTable::SetDSClientEnabled(bool enabled) { - auto inst = NetworkTableInstance::GetDefault(); - s_enable_ds = enabled; - if (s_enable_ds) { - inst.StartDSClient(s_port); - } else { - inst.StopDSClient(); - } -} - -void NetworkTable::SetPersistentFilename(StringRef filename) { - s_persistent_filename = filename; -} - -void NetworkTable::SetNetworkIdentity(StringRef name) { - NetworkTableInstance::GetDefault().SetNetworkIdentity(name); -} - -void NetworkTable::GlobalDeleteAll() { - NetworkTableInstance::GetDefault().DeleteAllEntries(); -} - -void NetworkTable::Flush() { - NetworkTableInstance::GetDefault().Flush(); -} - -void NetworkTable::SetUpdateRate(double interval) { - NetworkTableInstance::GetDefault().SetUpdateRate(interval); -} - -const char* NetworkTable::SavePersistent(StringRef filename) { - return NetworkTableInstance::GetDefault().SavePersistent(filename); -} - -const char* NetworkTable::LoadPersistent( - StringRef filename, - std::function warn) { - return NetworkTableInstance::GetDefault().LoadPersistent(filename, warn); -} - -std::shared_ptr NetworkTable::GetTable(StringRef key) { - if (!s_running) { - Initialize(); - } - return NetworkTableInstance::GetDefault().GetTable(key); -} - NetworkTable::NetworkTable(NT_Inst inst, const Twine& path, const private_init&) : m_inst(inst), m_path(path.str()) {} NetworkTable::~NetworkTable() { - for (auto& i : m_listeners) { - RemoveEntryListener(i.second); - } - for (auto i : m_lambdaListeners) { + for (auto i : m_listeners) { RemoveEntryListener(i); } } @@ -278,66 +134,6 @@ void NetworkTable::RemoveEntryListener(NT_EntryListener listener) const { nt::RemoveEntryListener(listener); } -void NetworkTable::AddTableListener(ITableListener* listener) { - AddTableListenerEx(listener, NT_NOTIFY_NEW | NT_NOTIFY_UPDATE); -} - -void NetworkTable::AddTableListener(ITableListener* listener, - bool immediateNotify) { - unsigned int flags = NT_NOTIFY_NEW | NT_NOTIFY_UPDATE; - if (immediateNotify) { - flags |= NT_NOTIFY_IMMEDIATE; - } - AddTableListenerEx(listener, flags); -} - -void NetworkTable::AddTableListenerEx(ITableListener* listener, - unsigned int flags) { - std::scoped_lock lock(m_mutex); - wpi::SmallString<128> path(m_path); - path += PATH_SEPARATOR_CHAR; - size_t prefix_len = path.size(); - NT_EntryListener id = nt::AddEntryListener( - m_inst, path, - [=](const EntryNotification& event) { - StringRef relative_key = event.name.substr(prefix_len); - if (relative_key.find(PATH_SEPARATOR_CHAR) != StringRef::npos) { - return; - } - listener->ValueChangedEx(this, relative_key, event.value, event.flags); - }, - flags); - m_listeners.emplace_back(listener, id); -} - -void NetworkTable::AddTableListener(StringRef key, ITableListener* listener, - bool immediateNotify) { - unsigned int flags = NT_NOTIFY_NEW | NT_NOTIFY_UPDATE; - if (immediateNotify) { - flags |= NT_NOTIFY_IMMEDIATE; - } - AddTableListenerEx(key, listener, flags); -} - -void NetworkTable::AddTableListenerEx(StringRef key, ITableListener* listener, - unsigned int flags) { - std::scoped_lock lock(m_mutex); - size_t prefix_len = m_path.size() + 1; - auto entry = GetEntry(key); - NT_EntryListener id = nt::AddEntryListener( - entry.GetHandle(), - [=](const EntryNotification& event) { - listener->ValueChangedEx(this, event.name.substr(prefix_len), - event.value, event.flags); - }, - flags); - m_listeners.emplace_back(listener, id); -} - -void NetworkTable::AddSubTableListener(ITableListener* listener) { - AddSubTableListener(listener, false); -} - NT_EntryListener NetworkTable::AddSubTableListener(TableListener listener, bool localNotify) { size_t prefix_len = m_path.size() + 1; @@ -366,58 +162,14 @@ NT_EntryListener NetworkTable::AddSubTableListener(TableListener listener, listener(this, sub_table_key, this->GetSubTable(sub_table_key)); }, flags); - m_lambdaListeners.emplace_back(id); + m_listeners.emplace_back(id); return id; } void NetworkTable::RemoveTableListener(NT_EntryListener listener) { nt::RemoveEntryListener(listener); auto matches_begin = - std::remove(m_lambdaListeners.begin(), m_lambdaListeners.end(), listener); - m_lambdaListeners.erase(matches_begin, m_lambdaListeners.end()); -} - -void NetworkTable::AddSubTableListener(ITableListener* listener, - bool localNotify) { - std::scoped_lock lock(m_mutex); - size_t prefix_len = m_path.size() + 1; - - // The lambda needs to be copyable, but StringMap is not, so use - // a shared_ptr to it. - auto notified_tables = std::make_shared>(); - - unsigned int flags = NT_NOTIFY_NEW | NT_NOTIFY_IMMEDIATE; - if (localNotify) { - flags |= NT_NOTIFY_LOCAL; - } - NT_EntryListener id = nt::AddEntryListener( - m_inst, m_path + Twine(PATH_SEPARATOR_CHAR), - [=](const EntryNotification& event) { - StringRef relative_key = event.name.substr(prefix_len); - auto end_sub_table = relative_key.find(PATH_SEPARATOR_CHAR); - if (end_sub_table == StringRef::npos) { - return; - } - StringRef sub_table_key = relative_key.substr(0, end_sub_table); - if (notified_tables->find(sub_table_key) == notified_tables->end()) { - return; - } - notified_tables->insert(std::make_pair(sub_table_key, '\0')); - listener->ValueChangedEx(this, sub_table_key, nullptr, event.flags); - }, - flags); - m_listeners.emplace_back(listener, id); -} - -void NetworkTable::RemoveTableListener(ITableListener* listener) { - std::scoped_lock lock(m_mutex); - auto matches_begin = - std::remove_if(m_listeners.begin(), m_listeners.end(), - [=](const Listener& x) { return x.first == listener; }); - - for (auto i = matches_begin; i != m_listeners.end(); ++i) { - RemoveEntryListener(i->second); - } + std::remove(m_listeners.begin(), m_listeners.end(), listener); m_listeners.erase(matches_begin, m_listeners.end()); } diff --git a/ntcore/src/main/native/cpp/tables/ITableListener.cpp b/ntcore/src/main/native/cpp/tables/ITableListener.cpp deleted file mode 100644 index a6dd569b5c..0000000000 --- a/ntcore/src/main/native/cpp/tables/ITableListener.cpp +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright (c) FIRST and other WPILib contributors. -// Open Source Software; you can modify and/or share it under the terms of -// the WPILib BSD license file in the root directory of this project. - -#include "tables/ITableListener.h" - -#include "ntcore_c.h" - -void ITableListener::ValueChangedEx(ITable* source, wpi::StringRef key, - std::shared_ptr value, - unsigned int flags) { - ValueChanged(source, key, value, (flags & NT_NOTIFY_NEW) != 0); -} diff --git a/ntcore/src/main/native/include/networktables/NetworkTable.h b/ntcore/src/main/native/include/networktables/NetworkTable.h index 91d1aa28aa..3c32d7d1f3 100644 --- a/ntcore/src/main/native/include/networktables/NetworkTable.h +++ b/ntcore/src/main/native/include/networktables/NetworkTable.h @@ -20,7 +20,6 @@ #include "networktables/TableEntryListener.h" #include "networktables/TableListener.h" #include "ntcore_c.h" -#include "tables/ITable.h" namespace nt { @@ -30,14 +29,6 @@ using wpi::Twine; class NetworkTableInstance; -#ifdef __GNUC__ -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wdeprecated-declarations" -#elif _WIN32 -#pragma warning(push) -#pragma warning(disable : 4996) -#endif - /** * @defgroup ntcore_cpp_api ntcore C++ object-oriented API * @@ -48,22 +39,13 @@ class NetworkTableInstance; * A network table that knows its subtable path. * @ingroup ntcore_cpp_api */ -class NetworkTable final : public ITable { +class NetworkTable final { private: NT_Inst m_inst; std::string m_path; mutable wpi::mutex m_mutex; mutable wpi::StringMap m_entries; - typedef std::pair Listener; - std::vector m_listeners; - std::vector m_lambdaListeners; - - static std::vector s_ip_addresses; - static std::string s_persistent_filename; - static bool s_client; - static bool s_enable_ds; - static bool s_running; - static unsigned int s_port; + std::vector m_listeners; struct private_init {}; friend class NetworkTableInstance; @@ -128,169 +110,7 @@ class NetworkTable final : public ITable { /** * The path separator for sub-tables and keys */ - static const char PATH_SEPARATOR_CHAR; - - /** - * Initializes network tables - */ - WPI_DEPRECATED( - "use NetworkTableInstance::StartServer() or " - "NetworkTableInstance::StartClient() instead") - static void Initialize(); - - /** - * Shuts down network tables - */ - WPI_DEPRECATED( - "use NetworkTableInstance::StopServer() or " - "NetworkTableInstance::StopClient() instead") - static void Shutdown(); - - /** - * set that network tables should be a client - * This must be called before initialize or GetTable - */ - WPI_DEPRECATED("use NetworkTableInstance::StartClient() instead") - static void SetClientMode(); - - /** - * set that network tables should be a server - * This must be called before initialize or GetTable - */ - WPI_DEPRECATED("use NetworkTableInstance::StartServer() instead") - static void SetServerMode(); - - /** - * set the team the robot is configured for (this will set the mdns address - * that network tables will connect to in client mode) - * This must be called before initialize or GetTable - * - * @param team the team number - */ - WPI_DEPRECATED( - "use NetworkTableInstance::SetServerTeam() or " - "NetworkTableInstance::StartClientTeam() instead") - static void SetTeam(int team); - - /** - * @param address the address that network tables will connect to in client - * mode - */ - WPI_DEPRECATED( - "use NetworkTableInstance::SetServer() or " - "NetworkTableInstance::StartClient() instead") - static void SetIPAddress(StringRef address); - - /** - * @param addresses the addresses that network tables will connect to in - * client mode (in round robin order) - */ - WPI_DEPRECATED( - "use NetworkTableInstance::SetServer() or " - "NetworkTableInstance::StartClient() instead") - static void SetIPAddress(ArrayRef addresses); - - /** - * Set the port number that network tables will connect to in client - * mode or listen to in server mode. - * - * @param port the port number - */ - WPI_DEPRECATED( - "use the appropriate parameters to NetworkTableInstance::SetServer(), " - "NetworkTableInstance::StartClient(), " - "NetworkTableInstance::StartServer(), and " - "NetworkTableInstance::StartDSClient() instead") - static void SetPort(unsigned int port); - - /** - * Enable requesting the server address from the Driver Station. - * - * @param enabled whether to enable the connection to the local DS - */ - WPI_DEPRECATED( - "use NetworkTableInstance::StartDSClient() and " - "NetworkTableInstance::StopDSClient() instead") - static void SetDSClientEnabled(bool enabled); - - /** - * Sets the persistent filename. - * - * @param filename the filename that the network tables server uses for - * automatic loading and saving of persistent values - */ - WPI_DEPRECATED( - "use the appropriate parameter to NetworkTableInstance::StartServer() " - "instead") - static void SetPersistentFilename(StringRef filename); - - /** - * Sets the network identity. - * This is provided in the connection info on the remote end. - * - * @param name identity - */ - WPI_DEPRECATED("use NetworkTableInstance::SetNetworkIdentity() instead") - static void SetNetworkIdentity(StringRef name); - - /** - * Deletes ALL keys in ALL subtables. Use with caution! - */ - WPI_DEPRECATED("use NetworkTableInstance::DeleteAllEntries() instead") - static void GlobalDeleteAll(); - - /** - * Flushes all updated values immediately to the network. - * Note: This is rate-limited to protect the network from flooding. - * This is primarily useful for synchronizing network updates with - * user code. - */ - WPI_DEPRECATED("use NetworkTableInstance::Flush() instead") - static void Flush(); - - /** - * Set the periodic update rate. - * Sets how frequently updates are sent to other nodes over the network. - * - * @param interval update interval in seconds (range 0.01 to 1.0) - */ - WPI_DEPRECATED("use NetworkTableInstance::SetUpdateRate() instead") - static void SetUpdateRate(double interval); - - /** - * Saves persistent keys to a file. The server does this automatically. - * - * @param filename file name - * @return Error (or nullptr). - */ - WPI_DEPRECATED("use NetworkTableInstance::SavePersistent() instead") - static const char* SavePersistent(StringRef filename); - - /** - * Loads persistent keys from a file. The server does this automatically. - * - * @param filename file name - * @param warn callback function called for warnings - * @return Error (or nullptr). - */ - WPI_DEPRECATED("use NetworkTableInstance::LoadPersistent() instead") - static const char* LoadPersistent( - StringRef filename, - std::function warn); - - /** - * Gets the table with the specified key. If the table does not exist, a new - * table will be created.
- * This will automatically initialize network tables if it has not been - * already. - * - * @param key the key name - * @return the network table requested - */ - WPI_DEPRECATED( - "use NetworkTableInstance::GetTable() or " - "NetworkTableInstance::GetEntry() instead") - static std::shared_ptr GetTable(StringRef key); + static constexpr char PATH_SEPARATOR_CHAR = '/'; /** * Gets the entry for a subkey. @@ -348,38 +168,6 @@ class NetworkTable final : public ITable { */ void RemoveTableListener(NT_EntryListener listener); - WPI_DEPRECATED( - "use AddEntryListener() instead with flags value of NT_NOTIFY_NEW | " - "NT_NOTIFY_UPDATE") - void AddTableListener(ITableListener* listener) override; - - WPI_DEPRECATED( - "use AddEntryListener() instead with flags value of NT_NOTIFY_NEW | " - "NT_NOTIFY_UPDATE | NT_NOTIFY_IMMEDIATE") - void AddTableListener(ITableListener* listener, - bool immediateNotify) override; - - WPI_DEPRECATED("use AddEntryListener() instead") - void AddTableListenerEx(ITableListener* listener, - unsigned int flags) override; - - WPI_DEPRECATED("use AddEntryListener() instead") - void AddTableListener(StringRef key, ITableListener* listener, - bool immediateNotify) override; - - WPI_DEPRECATED("use AddEntryListener() instead") - void AddTableListenerEx(StringRef key, ITableListener* listener, - unsigned int flags) override; - - WPI_DEPRECATED("use AddSubTableListener(TableListener, bool) instead") - void AddSubTableListener(ITableListener* listener) override; - - WPI_DEPRECATED("use AddSubTableListener(TableListener, bool) instead") - void AddSubTableListener(ITableListener* listener, bool localNotify) override; - - WPI_DEPRECATED("use RemoveTableListener(NT_EntryListener) instead") - void RemoveTableListener(ITableListener* listener) override; - /** * Returns the table at the specified key. If there is no table at the * specified key, it will create a new table @@ -387,7 +175,7 @@ class NetworkTable final : public ITable { * @param key the key name * @return the networktable to be returned */ - std::shared_ptr GetSubTable(const Twine& key) const override; + std::shared_ptr GetSubTable(const Twine& key) const; /** * Determines whether the given key is in this table. @@ -395,7 +183,7 @@ class NetworkTable final : public ITable { * @param key the key to search for * @return true if the table as a value assigned to the given key */ - bool ContainsKey(const Twine& key) const override; + bool ContainsKey(const Twine& key) const; /** * Determines whether there exists a non-empty subtable for this key @@ -405,7 +193,7 @@ class NetworkTable final : public ITable { * @return true if there is a subtable with the key which contains at least * one key/subtable of its own */ - bool ContainsSubTable(const Twine& key) const override; + bool ContainsSubTable(const Twine& key) const; /** * Gets all keys in the table (not including sub-tables). @@ -413,21 +201,21 @@ class NetworkTable final : public ITable { * @param types bitmask of types; 0 is treated as a "don't care". * @return keys currently in the table */ - std::vector GetKeys(int types = 0) const override; + std::vector GetKeys(int types = 0) const; /** * Gets the names of all subtables in the table. * * @return subtables currently in the table */ - std::vector GetSubTables() const override; + std::vector GetSubTables() const; /** * Makes a key's value persistent through program restarts. * * @param key the key to make persistent */ - void SetPersistent(StringRef key) override; + void SetPersistent(StringRef key); /** * Stop making a key's value persistent through program restarts. @@ -435,7 +223,7 @@ class NetworkTable final : public ITable { * * @param key the key name */ - void ClearPersistent(StringRef key) override; + void ClearPersistent(StringRef key); /** * Returns whether the value is persistent through program restarts. @@ -443,7 +231,7 @@ class NetworkTable final : public ITable { * * @param key the key name */ - bool IsPersistent(StringRef key) const override; + bool IsPersistent(StringRef key) const; /** * Sets flags on the specified key in this table. The key can @@ -452,7 +240,7 @@ class NetworkTable final : public ITable { * @param key the key name * @param flags the flags to set (bitmask) */ - void SetFlags(StringRef key, unsigned int flags) override; + void SetFlags(StringRef key, unsigned int flags); /** * Clears flags on the specified key in this table. The key can @@ -461,7 +249,7 @@ class NetworkTable final : public ITable { * @param key the key name * @param flags the flags to clear (bitmask) */ - void ClearFlags(StringRef key, unsigned int flags) override; + void ClearFlags(StringRef key, unsigned int flags); /** * Returns the flags for the specified key. @@ -469,14 +257,14 @@ class NetworkTable final : public ITable { * @param key the key name * @return the flags, or 0 if the key is not defined */ - unsigned int GetFlags(StringRef key) const override; + unsigned int GetFlags(StringRef key) const; /** * Deletes the specified key in this table. * * @param key the key name */ - void Delete(const Twine& key) override; + void Delete(const Twine& key); /** * Put a number in the table @@ -485,7 +273,7 @@ class NetworkTable final : public ITable { * @param value the value that will be assigned * @return False if the table key already exists with a different type */ - bool PutNumber(StringRef key, double value) override; + bool PutNumber(StringRef key, double value); /** * Gets the current value in the table, setting it if it does not exist. @@ -494,7 +282,7 @@ class NetworkTable final : public ITable { * @param defaultValue the default value to set if key doesn't exist. * @returns False if the table key exists with a different type */ - bool SetDefaultNumber(StringRef key, double defaultValue) override; + bool SetDefaultNumber(StringRef key, double defaultValue); /** * Gets the number associated with the given name. @@ -504,7 +292,7 @@ class NetworkTable final : public ITable { * @return the value associated with the given key or the given default value * if there is no value associated with the key */ - double GetNumber(StringRef key, double defaultValue) const override; + double GetNumber(StringRef key, double defaultValue) const; /** * Put a string in the table @@ -513,7 +301,7 @@ class NetworkTable final : public ITable { * @param value the value that will be assigned * @return False if the table key already exists with a different type */ - bool PutString(StringRef key, StringRef value) override; + bool PutString(StringRef key, StringRef value); /** * Gets the current value in the table, setting it if it does not exist. @@ -522,7 +310,7 @@ class NetworkTable final : public ITable { * @param defaultValue the default value to set if key doesn't exist. * @returns False if the table key exists with a different type */ - bool SetDefaultString(StringRef key, StringRef defaultValue) override; + bool SetDefaultString(StringRef key, StringRef defaultValue); /** * Gets the string associated with the given name. If the key does not @@ -533,7 +321,7 @@ class NetworkTable final : public ITable { * @return the value associated with the given key or the given default value * if there is no value associated with the key */ - std::string GetString(StringRef key, StringRef defaultValue) const override; + std::string GetString(StringRef key, StringRef defaultValue) const; /** * Put a boolean in the table @@ -542,7 +330,7 @@ class NetworkTable final : public ITable { * @param value the value that will be assigned * @return False if the table key already exists with a different type */ - bool PutBoolean(StringRef key, bool value) override; + bool PutBoolean(StringRef key, bool value); /** * Gets the current value in the table, setting it if it does not exist. @@ -551,7 +339,7 @@ class NetworkTable final : public ITable { * @param defaultValue the default value to set if key doesn't exist. * @returns False if the table key exists with a different type */ - bool SetDefaultBoolean(StringRef key, bool defaultValue) override; + bool SetDefaultBoolean(StringRef key, bool defaultValue); /** * Gets the boolean associated with the given name. If the key does not @@ -562,7 +350,7 @@ class NetworkTable final : public ITable { * @return the value associated with the given key or the given default value * if there is no value associated with the key */ - bool GetBoolean(StringRef key, bool defaultValue) const override; + bool GetBoolean(StringRef key, bool defaultValue) const; /** * Put a boolean array in the table @@ -575,7 +363,7 @@ class NetworkTable final : public ITable { * std::vector is special-cased in C++. 0 is false, any * non-zero value is true. */ - bool PutBooleanArray(StringRef key, ArrayRef value) override; + bool PutBooleanArray(StringRef key, ArrayRef value); /** * Gets the current value in the table, setting it if it does not exist. @@ -584,8 +372,7 @@ class NetworkTable final : public ITable { * @param defaultValue the default value to set if key doesn't exist. * @return False if the table key exists with a different type */ - bool SetDefaultBooleanArray(StringRef key, - ArrayRef defaultValue) override; + bool SetDefaultBooleanArray(StringRef key, ArrayRef defaultValue); /** * Returns the boolean array the key maps to. If the key does not exist or is @@ -604,7 +391,7 @@ class NetworkTable final : public ITable { * non-zero value is true. */ std::vector GetBooleanArray(StringRef key, - ArrayRef defaultValue) const override; + ArrayRef defaultValue) const; /** * Put a number array in the table @@ -613,7 +400,7 @@ class NetworkTable final : public ITable { * @param value the value that will be assigned * @return False if the table key already exists with a different type */ - bool PutNumberArray(StringRef key, ArrayRef value) override; + bool PutNumberArray(StringRef key, ArrayRef value); /** * Gets the current value in the table, setting it if it does not exist. @@ -622,8 +409,7 @@ class NetworkTable final : public ITable { * @param defaultValue the default value to set if key doesn't exist. * @returns False if the table key exists with a different type */ - bool SetDefaultNumberArray(StringRef key, - ArrayRef defaultValue) override; + bool SetDefaultNumberArray(StringRef key, ArrayRef defaultValue); /** * Returns the number array the key maps to. If the key does not exist or is @@ -637,8 +423,8 @@ class NetworkTable final : public ITable { * @note This makes a copy of the array. If the overhead of this is a * concern, use GetValue() instead. */ - std::vector GetNumberArray( - StringRef key, ArrayRef defaultValue) const override; + std::vector GetNumberArray(StringRef key, + ArrayRef defaultValue) const; /** * Put a string array in the table @@ -647,7 +433,7 @@ class NetworkTable final : public ITable { * @param value the value that will be assigned * @return False if the table key already exists with a different type */ - bool PutStringArray(StringRef key, ArrayRef value) override; + bool PutStringArray(StringRef key, ArrayRef value); /** * Gets the current value in the table, setting it if it does not exist. @@ -656,8 +442,7 @@ class NetworkTable final : public ITable { * @param defaultValue the default value to set if key doesn't exist. * @returns False if the table key exists with a different type */ - bool SetDefaultStringArray(StringRef key, - ArrayRef defaultValue) override; + bool SetDefaultStringArray(StringRef key, ArrayRef defaultValue); /** * Returns the string array the key maps to. If the key does not exist or is @@ -672,7 +457,7 @@ class NetworkTable final : public ITable { * concern, use GetValue() instead. */ std::vector GetStringArray( - StringRef key, ArrayRef defaultValue) const override; + StringRef key, ArrayRef defaultValue) const; /** * Put a raw value (byte array) in the table @@ -681,7 +466,7 @@ class NetworkTable final : public ITable { * @param value the value that will be assigned * @return False if the table key already exists with a different type */ - bool PutRaw(StringRef key, StringRef value) override; + bool PutRaw(StringRef key, StringRef value); /** * Gets the current value in the table, setting it if it does not exist. @@ -690,7 +475,7 @@ class NetworkTable final : public ITable { * @param defaultValue the default value to set if key doesn't exist. * @return False if the table key exists with a different type */ - bool SetDefaultRaw(StringRef key, StringRef defaultValue) override; + bool SetDefaultRaw(StringRef key, StringRef defaultValue); /** * Returns the raw value (byte array) the key maps to. If the key does not @@ -704,7 +489,7 @@ class NetworkTable final : public ITable { * @note This makes a copy of the raw contents. If the overhead of this is a * concern, use GetValue() instead. */ - std::string GetRaw(StringRef key, StringRef defaultValue) const override; + std::string GetRaw(StringRef key, StringRef defaultValue) const; /** * Put a value in the table @@ -713,7 +498,7 @@ class NetworkTable final : public ITable { * @param value the value that will be assigned * @return False if the table key already exists with a different type */ - bool PutValue(const Twine& key, std::shared_ptr value) override; + bool PutValue(const Twine& key, std::shared_ptr value); /** * Gets the current value in the table, setting it if it does not exist. @@ -722,8 +507,7 @@ class NetworkTable final : public ITable { * @param defaultValue the default value to set if key doesn't exist. * @return False if the table key exists with a different type */ - bool SetDefaultValue(const Twine& key, - std::shared_ptr defaultValue) override; + bool SetDefaultValue(const Twine& key, std::shared_ptr defaultValue); /** * Gets the value associated with a key as an object @@ -732,14 +516,14 @@ class NetworkTable final : public ITable { * @return the value associated with the given key, or nullptr if the key * does not exist */ - std::shared_ptr GetValue(const Twine& key) const override; + std::shared_ptr GetValue(const Twine& key) const; /** * Gets the full path of this table. Does not include the trailing "/". * * @return The path (e.g "", "/foo"). */ - StringRef GetPath() const override; + StringRef GetPath() const; /** * Save table values to a file. The file format used is identical to @@ -763,12 +547,6 @@ class NetworkTable final : public ITable { std::function warn); }; -#ifdef __GNUC__ -#pragma GCC diagnostic pop -#elif _WIN32 -#pragma warning(pop) -#endif - } // namespace nt // For backwards compatibility diff --git a/ntcore/src/main/native/include/tables/ITable.h b/ntcore/src/main/native/include/tables/ITable.h deleted file mode 100644 index 5cd365cb76..0000000000 --- a/ntcore/src/main/native/include/tables/ITable.h +++ /dev/null @@ -1,453 +0,0 @@ -// Copyright (c) FIRST and other WPILib contributors. -// Open Source Software; you can modify and/or share it under the terms of -// the WPILib BSD license file in the root directory of this project. - -#ifndef NTCORE_TABLES_ITABLE_H_ -#define NTCORE_TABLES_ITABLE_H_ - -#include -#include -#include - -#include -#include -#include - -#include "networktables/NetworkTableValue.h" - -namespace nt { -class NetworkTable; -} // namespace nt - -class ITableListener; - -/** - * A table whose values can be read and written to - */ -class WPI_DEPRECATED("Use NetworkTable directly") ITable { - public: - /** - * Determines whether the given key is in this table. - * - * @param key the key to search for - * @return true if the table as a value assigned to the given key - */ - virtual bool ContainsKey(const wpi::Twine& key) const = 0; - - /** - * Determines whether there exists a non-empty subtable for this key - * in this table. - * - * @param key the key to search for - * @return true if there is a subtable with the key which contains at least - * one key/subtable of its own - */ - virtual bool ContainsSubTable(const wpi::Twine& key) const = 0; - - /** - * Gets the subtable in this table for the given name. - * - * @param key the name of the table relative to this one - * @return a sub table relative to this one - */ - virtual std::shared_ptr GetSubTable( - const wpi::Twine& key) const = 0; - - /** - * @param types bitmask of types; 0 is treated as a "don't care". - * @return keys currently in the table - */ - virtual std::vector GetKeys(int types = 0) const = 0; - - /** - * @return subtables currently in the table - */ - virtual std::vector GetSubTables() const = 0; - - /** - * Makes a key's value persistent through program restarts. - * - * @param key the key to make persistent - */ - virtual void SetPersistent(wpi::StringRef key) = 0; - - /** - * Stop making a key's value persistent through program restarts. - * The key cannot be null. - * - * @param key the key name - */ - virtual void ClearPersistent(wpi::StringRef key) = 0; - - /** - * Returns whether the value is persistent through program restarts. - * The key cannot be null. - * - * @param key the key name - */ - virtual bool IsPersistent(wpi::StringRef key) const = 0; - - /** - * Sets flags on the specified key in this table. The key can - * not be null. - * - * @param key the key name - * @param flags the flags to set (bitmask) - */ - virtual void SetFlags(wpi::StringRef key, unsigned int flags) = 0; - - /** - * Clears flags on the specified key in this table. The key can - * not be null. - * - * @param key the key name - * @param flags the flags to clear (bitmask) - */ - virtual void ClearFlags(wpi::StringRef key, unsigned int flags) = 0; - - /** - * Returns the flags for the specified key. - * - * @param key the key name - * @return the flags, or 0 if the key is not defined - */ - virtual unsigned int GetFlags(wpi::StringRef key) const = 0; - - /** - * Deletes the specified key in this table. - * - * @param key the key name - */ - virtual void Delete(const wpi::Twine& key) = 0; - - /** - * Gets the value associated with a key as an object - * - * @param key the key of the value to look up - * @return the value associated with the given key, or nullptr if the key - * does not exist - */ - virtual std::shared_ptr GetValue(const wpi::Twine& key) const = 0; - - /** - * Gets the current value in the table, setting it if it does not exist. - * @param key the key - * @param defaultValue the default value to set if key doesn't exist. - * @returns False if the table key exists with a different type - */ - virtual bool SetDefaultValue(const wpi::Twine& key, - std::shared_ptr defaultValue) = 0; - - /** - * Put a value in the table - * - * @param key the key to be assigned to - * @param value the value that will be assigned - * @return False if the table key already exists with a different type - */ - virtual bool PutValue(const wpi::Twine& key, - std::shared_ptr value) = 0; - - /** - * Put a number in the table - * - * @param key the key to be assigned to - * @param value the value that will be assigned - * @return False if the table key already exists with a different type - */ - virtual bool PutNumber(wpi::StringRef key, double value) = 0; - - /** - * Gets the current value in the table, setting it if it does not exist. - * @param key the key - * @param defaultValue the default value to set if key doesn't exist. - * @returns False if the table key exists with a different type - */ - virtual bool SetDefaultNumber(wpi::StringRef key, double defaultValue) = 0; - - /** - * Gets the number associated with the given name. - * - * @param key the key to look up - * @param defaultValue the value to be returned if no value is found - * @return the value associated with the given key or the given default value - * if there is no value associated with the key - */ - virtual double GetNumber(wpi::StringRef key, double defaultValue) const = 0; - - /** - * Put a string in the table - * - * @param key the key to be assigned to - * @param value the value that will be assigned - * @return False if the table key already exists with a different type - */ - virtual bool PutString(wpi::StringRef key, wpi::StringRef value) = 0; - - /** - * Gets the current value in the table, setting it if it does not exist. - * @param key the key - * @param defaultValue the default value to set if key doesn't exist. - * @returns False if the table key exists with a different type - */ - virtual bool SetDefaultString(wpi::StringRef key, - wpi::StringRef defaultValue) = 0; - - /** - * Gets the string associated with the given name. If the key does not - * exist or is of different type, it will return the default value. - * - * @param key the key to look up - * @param defaultValue the value to be returned if no value is found - * @return the value associated with the given key or the given default value - * if there is no value associated with the key - * - * @note This makes a copy of the string. If the overhead of this is a - * concern, use GetValue() instead. - */ - virtual std::string GetString(wpi::StringRef key, - wpi::StringRef defaultValue) const = 0; - - /** - * Put a boolean in the table - * - * @param key the key to be assigned to - * @param value the value that will be assigned - * @return False if the table key already exists with a different type - */ - virtual bool PutBoolean(wpi::StringRef key, bool value) = 0; - - /** - * Gets the current value in the table, setting it if it does not exist. - * @param key the key - * @param defaultValue the default value to set if key doesn't exist. - * @returns False if the table key exists with a different type - */ - virtual bool SetDefaultBoolean(wpi::StringRef key, bool defaultValue) = 0; - - /** - * Gets the boolean associated with the given name. If the key does not - * exist or is of different type, it will return the default value. - * - * @param key the key to look up - * @param defaultValue the value to be returned if no value is found - * @return the value associated with the given key or the given default value - * if there is no value associated with the key - */ - virtual bool GetBoolean(wpi::StringRef key, bool defaultValue) const = 0; - - /** - * Put a boolean array in the table - * @param key the key to be assigned to - * @param value the value that will be assigned - * @return False if the table key already exists with a different type - * - * @note The array must be of int's rather than of bool's because - * std::vector is special-cased in C++. 0 is false, any - * non-zero value is true. - */ - virtual bool PutBooleanArray(wpi::StringRef key, - wpi::ArrayRef value) = 0; - - /** - * Gets the current value in the table, setting it if it does not exist. - * @param key the key - * @param defaultValue the default value to set if key doesn't exist. - * @returns False if the table key exists with a different type - */ - virtual bool SetDefaultBooleanArray(wpi::StringRef key, - wpi::ArrayRef defaultValue) = 0; - - /** - * Returns the boolean array the key maps to. If the key does not exist or is - * of different type, it will return the default value. - * @param key the key to look up - * @param defaultValue the value to be returned if no value is found - * @return the value associated with the given key or the given default value - * if there is no value associated with the key - * - * @note This makes a copy of the array. If the overhead of this is a - * concern, use GetValue() instead. - * - * @note The returned array is std::vector instead of std::vector - * because std::vector is special-cased in C++. 0 is false, any - * non-zero value is true. - */ - virtual std::vector GetBooleanArray( - wpi::StringRef key, wpi::ArrayRef defaultValue) const = 0; - - /** - * Put a number array in the table - * @param key the key to be assigned to - * @param value the value that will be assigned - * @return False if the table key already exists with a different type - */ - virtual bool PutNumberArray(wpi::StringRef key, - wpi::ArrayRef value) = 0; - - /** - * Gets the current value in the table, setting it if it does not exist. - * @param key the key - * @param defaultValue the default value to set if key doesn't exist. - * @returns False if the table key exists with a different type - */ - virtual bool SetDefaultNumberArray(wpi::StringRef key, - wpi::ArrayRef defaultValue) = 0; - - /** - * Returns the number array the key maps to. If the key does not exist or is - * of different type, it will return the default value. - * @param key the key to look up - * @param defaultValue the value to be returned if no value is found - * @return the value associated with the given key or the given default value - * if there is no value associated with the key - * - * @note This makes a copy of the array. If the overhead of this is a - * concern, use GetValue() instead. - */ - virtual std::vector GetNumberArray( - wpi::StringRef key, wpi::ArrayRef defaultValue) const = 0; - - /** - * Put a string array in the table - * @param key the key to be assigned to - * @param value the value that will be assigned - * @return False if the table key already exists with a different type - */ - virtual bool PutStringArray(wpi::StringRef key, - wpi::ArrayRef value) = 0; - - /** - * Gets the current value in the table, setting it if it does not exist. - * @param key the key - * @param defaultValue the default value to set if key doesn't exist. - * @returns False if the table key exists with a different type - */ - virtual bool SetDefaultStringArray( - wpi::StringRef key, wpi::ArrayRef defaultValue) = 0; - - /** - * Returns the string array the key maps to. If the key does not exist or is - * of different type, it will return the default value. - * @param key the key to look up - * @param defaultValue the value to be returned if no value is found - * @return the value associated with the given key or the given default value - * if there is no value associated with the key - * - * @note This makes a copy of the array. If the overhead of this is a - * concern, use GetValue() instead. - */ - virtual std::vector GetStringArray( - wpi::StringRef key, wpi::ArrayRef defaultValue) const = 0; - - /** - * Put a raw value (byte array) in the table - * @param key the key to be assigned to - * @param value the value that will be assigned - * @return False if the table key already exists with a different type - */ - virtual bool PutRaw(wpi::StringRef key, wpi::StringRef value) = 0; - - /** - * Gets the current value in the table, setting it if it does not exist. - * @param key the key - * @param defaultValue the default value to set if key doesn't exist. - * @returns False if the table key exists with a different type - */ - virtual bool SetDefaultRaw(wpi::StringRef key, - wpi::StringRef defaultValue) = 0; - - /** - * Returns the raw value (byte array) the key maps to. If the key does not - * exist or is of different type, it will return the default value. - * @param key the key to look up - * @param defaultValue the value to be returned if no value is found - * @return the value associated with the given key or the given default value - * if there is no value associated with the key - * - * @note This makes a copy of the raw contents. If the overhead of this is a - * concern, use GetValue() instead. - */ - virtual std::string GetRaw(wpi::StringRef key, - wpi::StringRef defaultValue) const = 0; - - /** - * Add a listener for changes to the table - * - * @param listener the listener to add - */ - virtual void AddTableListener(ITableListener* listener) = 0; - - /** - * Add a listener for changes to the table - * - * @param listener the listener to add - * @param immediateNotify if true then this listener will be notified of all - * current entries (marked as new) - */ - virtual void AddTableListener(ITableListener* listener, - bool immediateNotify) = 0; - - /** - * Add a listener for changes to the table - * - * @param listener the listener to add - * @param immediateNotify if true then this listener will be notified of all - * current entries (marked as new) - * @param flags bitmask of NT_NotifyKind specifying desired notifications - */ - virtual void AddTableListenerEx(ITableListener* listener, - unsigned int flags) = 0; - - /** - * Add a listener for changes to a specific key the table - * - * @param key the key to listen for - * @param listener the listener to add - * @param immediateNotify if true then this listener will be notified of all - * current entries (marked as new) - */ - virtual void AddTableListener(wpi::StringRef key, ITableListener* listener, - bool immediateNotify) = 0; - - /** - * Add a listener for changes to a specific key the table - * - * @param key the key to listen for - * @param listener the listener to add - * @param immediateNotify if true then this listener will be notified of all - * current entries (marked as new) - * @param flags bitmask of NT_NotifyKind specifying desired notifications - */ - virtual void AddTableListenerEx(wpi::StringRef key, ITableListener* listener, - unsigned int flags) = 0; - - /** - * This will immediately notify the listener of all current sub tables - * @param listener the listener to add - */ - virtual void AddSubTableListener(ITableListener* listener) = 0; - - /** - * This will immediately notify the listener of all current sub tables - * @param listener the listener to add - * @param localNotify if true then this listener will be notified of all - * local changes in addition to all remote changes - */ - virtual void AddSubTableListener(ITableListener* listener, - bool localNotify) = 0; - - /** - * Remove a listener from receiving table events - * - * @param listener the listener to be removed - */ - virtual void RemoveTableListener(ITableListener* listener) = 0; - - /** - * Gets the full path of this table. - */ - virtual wpi::StringRef GetPath() const = 0; -}; - -#endif // NTCORE_TABLES_ITABLE_H_ diff --git a/ntcore/src/main/native/include/tables/ITableListener.h b/ntcore/src/main/native/include/tables/ITableListener.h deleted file mode 100644 index 29a693adf3..0000000000 --- a/ntcore/src/main/native/include/tables/ITableListener.h +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright (c) FIRST and other WPILib contributors. -// Open Source Software; you can modify and/or share it under the terms of -// the WPILib BSD license file in the root directory of this project. - -#ifndef NTCORE_TABLES_ITABLELISTENER_H_ -#define NTCORE_TABLES_ITABLELISTENER_H_ - -#include - -#include -#include - -#include "networktables/NetworkTableValue.h" - -#ifdef __GNUC__ -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wdeprecated-declarations" -#elif _WIN32 -#pragma warning(push) -#pragma warning(disable : 4996) -#endif - -class ITable; - -/** - * A listener that listens to changes in values in a {@link ITable} - */ -class WPI_DEPRECATED( - "Use EntryListener, TableEntryListener, or TableListener as appropriate") - ITableListener { - public: - virtual ~ITableListener() = default; - /** - * Called when a key-value pair is changed in a {@link ITable} - * @param source the table the key-value pair exists in - * @param key the key associated with the value that changed - * @param value the new value - * @param isNew true if the key did not previously exist in the table, - * otherwise it is false - */ - virtual void ValueChanged(ITable* source, wpi::StringRef key, - std::shared_ptr value, bool isNew) = 0; - - /** - * Extended version of ValueChanged. Called when a key-value pair is - * changed in a {@link ITable}. The default implementation simply calls - * ValueChanged(). If this is overridden, ValueChanged() will not be called. - * @param source the table the key-value pair exists in - * @param key the key associated with the value that changed - * @param value the new value - * @param flags update flags; for example, NT_NOTIFY_NEW if the key did not - * previously exist in the table - */ - virtual void ValueChangedEx(ITable* source, wpi::StringRef key, - std::shared_ptr value, - unsigned int flags); -}; - -#ifdef __GNUC__ -#pragma GCC diagnostic pop -#elif _WIN32 -#pragma warning(pop) -#endif - -#endif // NTCORE_TABLES_ITABLELISTENER_H_ From 48ebe5736a39573f29c6df003b60a38c955f9906 Mon Sep 17 00:00:00 2001 From: Peter Johnson Date: Sun, 4 Apr 2021 13:56:42 -0700 Subject: [PATCH 13/34] [ntcore] Remove deprecated Java interfaces and classes --- .../wpilibj/networktables/NetworkTable.java | 1108 ----------------- .../edu/wpi/first/wpilibj/tables/IRemote.java | 44 - .../tables/IRemoteConnectionListener.java | 49 - .../edu/wpi/first/wpilibj/tables/ITable.java | 513 -------- .../first/wpilibj/tables/ITableListener.java | 43 - 5 files changed, 1757 deletions(-) delete mode 100644 ntcore/src/main/java/edu/wpi/first/wpilibj/networktables/NetworkTable.java delete mode 100644 ntcore/src/main/java/edu/wpi/first/wpilibj/tables/IRemote.java delete mode 100644 ntcore/src/main/java/edu/wpi/first/wpilibj/tables/IRemoteConnectionListener.java delete mode 100644 ntcore/src/main/java/edu/wpi/first/wpilibj/tables/ITable.java delete mode 100644 ntcore/src/main/java/edu/wpi/first/wpilibj/tables/ITableListener.java diff --git a/ntcore/src/main/java/edu/wpi/first/wpilibj/networktables/NetworkTable.java b/ntcore/src/main/java/edu/wpi/first/wpilibj/networktables/NetworkTable.java deleted file mode 100644 index e5affb2958..0000000000 --- a/ntcore/src/main/java/edu/wpi/first/wpilibj/networktables/NetworkTable.java +++ /dev/null @@ -1,1108 +0,0 @@ -// Copyright (c) FIRST and other WPILib contributors. -// Open Source Software; you can modify and/or share it under the terms of -// the WPILib BSD license file in the root directory of this project. - -package edu.wpi.first.wpilibj.networktables; - -import edu.wpi.first.networktables.ConnectionInfo; -import edu.wpi.first.networktables.ConnectionNotification; -import edu.wpi.first.networktables.EntryInfo; -import edu.wpi.first.networktables.EntryNotification; -import edu.wpi.first.networktables.NetworkTableEntry; -import edu.wpi.first.networktables.NetworkTableInstance; -import edu.wpi.first.networktables.NetworkTableType; -import edu.wpi.first.networktables.NetworkTableValue; -import edu.wpi.first.networktables.NetworkTablesJNI; -import edu.wpi.first.networktables.PersistentException; -import edu.wpi.first.wpilibj.tables.IRemote; -import edu.wpi.first.wpilibj.tables.IRemoteConnectionListener; -import edu.wpi.first.wpilibj.tables.ITable; -import edu.wpi.first.wpilibj.tables.ITableListener; -import java.nio.ByteBuffer; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Objects; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; -import java.util.function.Consumer; - -/** - * A network table that knows its subtable path. - * - * @deprecated Use {@link edu.wpi.first.networktables.NetworkTable} instead. - */ -@Deprecated -@SuppressWarnings("checkstyle:all") -public class NetworkTable implements ITable, IRemote { - /** The path separator for sub-tables and keys */ - public static final char PATH_SEPARATOR = '/'; - /** The default port that network tables operates on */ - public static final int DEFAULT_PORT = 1735; - - private static boolean client = false; - private static boolean enableDS = true; - private static boolean running = false; - private static int port = DEFAULT_PORT; - private static String persistentFilename = "networktables.ini"; - - private static synchronized void checkInit() { - if (running) throw new IllegalStateException("Network tables has already been initialized"); - } - - /** - * initializes network tables - * - * @deprecated Use {@link NetworkTableInstance#startServer()} or {@link - * NetworkTableInstance#startClient()} instead. - */ - @Deprecated - public static synchronized void initialize() { - if (running) shutdown(); - NetworkTableInstance inst = NetworkTableInstance.getDefault(); - if (client) { - inst.startClient(); - if (enableDS) inst.startDSClient(port); - } else inst.startServer(persistentFilename, "", port); - running = true; - } - - /** - * shuts down network tables - * - * @deprecated Use {@link NetworkTableInstance#stopServer()} or {@link - * NetworkTableInstance#stopClient()} instead. - */ - @Deprecated - public static synchronized void shutdown() { - if (!running) return; - NetworkTableInstance inst = NetworkTableInstance.getDefault(); - if (client) { - inst.stopDSClient(); - inst.stopClient(); - } else inst.stopServer(); - running = false; - } - - /** - * set that network tables should be a server This must be called before initialize or getTable - * - * @deprecated Use {@link NetworkTableInstance#startServer()} instead. - */ - @Deprecated - public static synchronized void setServerMode() { - if (!client) return; - checkInit(); - client = false; - } - - /** - * set that network tables should be a client This must be called before initialize or getTable - * - * @deprecated Use {@link NetworkTableInstance#startClient()} instead. - */ - @Deprecated - public static synchronized void setClientMode() { - if (client) return; - checkInit(); - client = true; - } - - /** - * set the team the robot is configured for (this will set the mdns address that network tables - * will connect to in client mode) This must be called before initialize or getTable - * - * @param team the team number - * @deprecated Use {@link NetworkTableInstance#setServerTeam(int)} or {@link - * NetworkTableInstance#startClientTeam(int)} instead. - */ - @Deprecated - public static synchronized void setTeam(int team) { - NetworkTableInstance inst = NetworkTableInstance.getDefault(); - inst.setServerTeam(team, port); - if (enableDS) inst.startDSClient(port); - } - - /** - * @param address the address that network tables will connect to in client mode - * @deprecated Use {@link NetworkTableInstance#setServer(String)} or {@link - * NetworkTableInstance#startClient(String)} instead. - */ - @Deprecated - public static synchronized void setIPAddress(final String address) { - String[] addresses = new String[1]; - addresses[0] = address; - setIPAddress(addresses); - } - - /** - * @param addresses the adresses that network tables will connect to in client mode (in round - * robin order) - * @deprecated Use {@link NetworkTableInstance#setServer(String[])} or {@link - * NetworkTableInstance#startClient(String[])} instead. - */ - @Deprecated - public static synchronized void setIPAddress(final String[] addresses) { - NetworkTableInstance inst = NetworkTableInstance.getDefault(); - inst.setServer(addresses, port); - - // Stop the DS client if we're explicitly connecting to localhost - if (addresses.length > 0 - && (addresses[0].equals("localhost") || addresses[0].equals("127.0.0.1"))) - inst.stopDSClient(); - else if (enableDS) inst.startDSClient(port); - } - - /** - * Set the port number that network tables will connect to in client mode or listen to in server - * mode. - * - * @param aport the port number - * @deprecated Use the appropriate parameters to {@link NetworkTableInstance#setServer(String, - * int)}, {@link NetworkTableInstance#startClient(String, int)}, {@link - * NetworkTableInstance#startServer(String, String, int)}, and {@link - * NetworkTableInstance#startDSClient(int)} instead. - */ - @Deprecated - public static synchronized void setPort(int aport) { - if (port == aport) return; - checkInit(); - port = aport; - } - - /** - * Enable requesting the server address from the Driver Station. - * - * @param enabled whether to enable the connection to the local DS - * @deprecated Use {@link NetworkTableInstance#startDSClient()} and {@link - * NetworkTableInstance#stopDSClient()} instead. - */ - @Deprecated - public static synchronized void setDSClientEnabled(boolean enabled) { - NetworkTableInstance inst = NetworkTableInstance.getDefault(); - enableDS = enabled; - if (enableDS) inst.startDSClient(port); - else inst.stopDSClient(); - } - - /** - * Sets the persistent filename. - * - * @param filename the filename that the network tables server uses for automatic loading and - * saving of persistent values - * @deprecated Use the appropriate parameter to {@link NetworkTableInstance#startServer()} - * instead. - */ - @Deprecated - public static synchronized void setPersistentFilename(final String filename) { - if (persistentFilename.equals(filename)) return; - checkInit(); - persistentFilename = filename; - } - - /** - * Sets the network identity. This is provided in the connection info on the remote end. - * - * @param name identity - * @deprecated Use {@link NetworkTableInstance#setNetworkIdentity(String)} instead. - */ - @Deprecated - public static void setNetworkIdentity(String name) { - NetworkTableInstance.getDefault().setNetworkIdentity(name); - } - - public static boolean[] toNative(Boolean[] arr) { - boolean[] out = new boolean[arr.length]; - for (int i = 0; i < arr.length; i++) out[i] = arr[i]; - return out; - } - - public static double[] toNative(Number[] arr) { - double[] out = new double[arr.length]; - for (int i = 0; i < arr.length; i++) out[i] = arr[i].doubleValue(); - return out; - } - - public static Boolean[] fromNative(boolean[] arr) { - Boolean[] out = new Boolean[arr.length]; - for (int i = 0; i < arr.length; i++) out[i] = arr[i]; - return out; - } - - public static Double[] fromNative(double[] arr) { - Double[] out = new Double[arr.length]; - for (int i = 0; i < arr.length; i++) out[i] = arr[i]; - return out; - } - - /** - * Gets the table with the specified key. If the table does not exist, a new table will be - * created.
- * This will automatically initialize network tables if it has not been already - * - * @deprecated Use {@link NetworkTableInstance#getTable(String)} instead. - * @param key the key name - * @return the network table requested - */ - @Deprecated - public static synchronized NetworkTable getTable(String key) { - if (!running) initialize(); - String theKey; - if (key.isEmpty() || key.equals("/")) { - theKey = ""; - } else if (key.charAt(0) == NetworkTable.PATH_SEPARATOR) { - theKey = key; - } else { - theKey = NetworkTable.PATH_SEPARATOR + key; - } - return new NetworkTable(NetworkTableInstance.getDefault(), theKey); - } - - private final String path; - private final String pathWithSep; - private final NetworkTableInstance inst; - - NetworkTable(NetworkTableInstance inst, String path) { - this.path = path; - this.pathWithSep = path + PATH_SEPARATOR; - this.inst = inst; - } - - @Override - public String toString() { - return "NetworkTable: " + path; - } - - private final ConcurrentMap entries = - new ConcurrentHashMap(); - - /** - * Gets the entry for a subkey. - * - * @param key the key name - * @return Network table entry. - */ - private NetworkTableEntry getEntry(String key) { - NetworkTableEntry entry = entries.get(key); - if (entry == null) { - entry = inst.getEntry(pathWithSep + key); - entries.putIfAbsent(key, entry); - } - return entry; - } - - /** - * Gets the current network connections. - * - * @return An array of connection information. - * @deprecated Use {@link NetworkTableInstance#getConnections()} instead. - */ - @Deprecated - public static ConnectionInfo[] connections() { - return NetworkTableInstance.getDefault().getConnections(); - } - - /** - * Determine whether or not a network connection is active. - * - * @return True if connected, false if not connected. - * @deprecated Use {@link NetworkTableInstance#isConnected()} instead. - */ - @Override - @Deprecated - public boolean isConnected() { - return inst.isConnected(); - } - - /** - * Determine whether NetworkTables is operating as a server or as a client. - * - * @return True if operating as a server, false otherwise. - * @deprecated Use {@link NetworkTableInstance#getNetworkMode()} instead. - */ - @Override - @Deprecated - public boolean isServer() { - return (inst.getNetworkMode() & NetworkTableInstance.kNetModeServer) != 0; - } - - /* Backwards compatibility shims for IRemoteConnectionListener */ - private static class ConnectionListenerAdapter implements Consumer { - public int uid; - private final IRemote targetSource; - private final IRemoteConnectionListener targetListener; - - public ConnectionListenerAdapter( - IRemote targetSource, IRemoteConnectionListener targetListener) { - this.targetSource = targetSource; - this.targetListener = targetListener; - } - - @Override - public void accept(ConnectionNotification event) { - if (event.connected) targetListener.connectedEx(targetSource, event.conn); - else targetListener.disconnectedEx(targetSource, event.conn); - } - } - - private static final HashMap - globalConnectionListenerMap = - new HashMap(); - - private static IRemote staticRemote = - new IRemote() { - @Override - public void addConnectionListener( - IRemoteConnectionListener listener, boolean immediateNotify) { - NetworkTable.addGlobalConnectionListener(listener, immediateNotify); - } - - @Override - public void removeConnectionListener(IRemoteConnectionListener listener) { - NetworkTable.removeGlobalConnectionListener(listener); - } - - @Override - public boolean isConnected() { - ConnectionInfo[] conns = NetworkTableInstance.getDefault().getConnections(); - return conns.length > 0; - } - - @Override - public boolean isServer() { - return (NetworkTableInstance.getDefault().getNetworkMode() - & NetworkTableInstance.kNetModeServer) - != 0; - } - }; - - private final HashMap - connectionListenerMap = new HashMap(); - - /** - * Add a connection listener. - * - * @param listener connection listener - * @param immediateNotify call listener immediately for all existing connections - * @deprecated Use {@link NetworkTableInstance#addConnectionListener(Consumer, boolean)} instead. - */ - @Deprecated - public static synchronized void addGlobalConnectionListener( - IRemoteConnectionListener listener, boolean immediateNotify) { - ConnectionListenerAdapter adapter = new ConnectionListenerAdapter(staticRemote, listener); - if (globalConnectionListenerMap.putIfAbsent(listener, adapter) != null) { - throw new IllegalStateException("Cannot add the same listener twice"); - } - adapter.uid = NetworkTableInstance.getDefault().addConnectionListener(adapter, immediateNotify); - } - - /** - * Remove a connection listener. - * - * @param listener connection listener - * @deprecated Use {@link NetworkTableInstance#removeConnectionListener(int)} instead. - */ - @Deprecated - public static synchronized void removeGlobalConnectionListener( - IRemoteConnectionListener listener) { - ConnectionListenerAdapter adapter = globalConnectionListenerMap.remove(listener); - if (adapter != null) { - NetworkTableInstance.getDefault().removeConnectionListener(adapter.uid); - } - } - - /** - * Add a connection listener. - * - * @param listener connection listener - * @param immediateNotify call listener immediately for all existing connections - * @deprecated Use {@link NetworkTableInstance#addConnectionListener(Consumer, boolean)} instead. - */ - @Override - @Deprecated - public synchronized void addConnectionListener( - IRemoteConnectionListener listener, boolean immediateNotify) { - ConnectionListenerAdapter adapter = new ConnectionListenerAdapter(this, listener); - if (connectionListenerMap.putIfAbsent(listener, adapter) != null) { - throw new IllegalStateException("Cannot add the same listener twice"); - } - adapter.uid = inst.addConnectionListener(adapter, immediateNotify); - } - - /** - * Remove a connection listener. - * - * @param listener connection listener - * @deprecated Use {@link NetworkTableInstance#removeConnectionListener(int)} instead. - */ - @Override - @Deprecated - public synchronized void removeConnectionListener(IRemoteConnectionListener listener) { - ConnectionListenerAdapter adapter = connectionListenerMap.get(listener); - if (adapter != null && connectionListenerMap.remove(listener, adapter)) { - inst.removeConnectionListener(adapter.uid); - } - } - - /** - * {@inheritDoc} - * - * @deprecated Use {@link - * edu.wpi.first.networktables.NetworkTable#addEntryListener(TableEntryListener, int)} instead - * (with flags value of NOTIFY_NEW | NOTIFY_UPDATE). - */ - @Override - @Deprecated - public void addTableListener(ITableListener listener) { - addTableListenerEx(listener, NOTIFY_NEW | NOTIFY_UPDATE); - } - - /** - * {@inheritDoc} - * - * @deprecated Use {@link - * edu.wpi.first.networktables.NetworkTable#addEntryListener(TableEntryListener, int)} instead - * (with flags value of NOTIFY_NEW | NOTIFY_UPDATE | NOTIFY_IMMEDIATE). - */ - @Override - @Deprecated - public void addTableListener(ITableListener listener, boolean immediateNotify) { - int flags = NOTIFY_NEW | NOTIFY_UPDATE; - if (immediateNotify) flags |= NOTIFY_IMMEDIATE; - addTableListenerEx(listener, flags); - } - - /* Base class for listeners; stores uid to implement remove functions */ - private static class ListenerBase { - public int uid; - } - - private static class OldTableListenerAdapter extends ListenerBase - implements Consumer { - private final int prefixLen; - private final ITable targetSource; - private final ITableListener targetListener; - - public OldTableListenerAdapter( - int prefixLen, ITable targetSource, ITableListener targetListener) { - this.prefixLen = prefixLen; - this.targetSource = targetSource; - this.targetListener = targetListener; - } - - @Override - public void accept(EntryNotification event) { - String relativeKey = event.name.substring(prefixLen); - if (relativeKey.indexOf(PATH_SEPARATOR) != -1) return; - targetListener.valueChangedEx(targetSource, relativeKey, event.value.getValue(), event.flags); - } - } - - private final HashMap> oldListenerMap = - new HashMap>(); - - /** - * {@inheritDoc} - * - * @deprecated Use {@link - * edu.wpi.first.networktables.NetworkTable#addEntryListener(TableEntryListener, int)} - * instead. - */ - @Override - @Deprecated - public synchronized void addTableListenerEx(ITableListener listener, int flags) { - List adapters = oldListenerMap.get(listener); - if (adapters == null) { - adapters = new ArrayList(); - oldListenerMap.put(listener, adapters); - } - OldTableListenerAdapter adapter = - new OldTableListenerAdapter(path.length() + 1, this, listener); - adapter.uid = inst.addEntryListener(pathWithSep, adapter, flags); - adapters.add(adapter); - } - - /** - * {@inheritDoc} - * - * @deprecated Use {@link edu.wpi.first.networktables.NetworkTable#addEntryListener(String, - * TableEntryListener, int)} or {@link NetworkTableEntry#addListener(Consumer, int)} instead. - */ - @Override - @Deprecated - public void addTableListener(String key, ITableListener listener, boolean immediateNotify) { - int flags = NOTIFY_NEW | NOTIFY_UPDATE; - if (immediateNotify) flags |= NOTIFY_IMMEDIATE; - addTableListenerEx(key, listener, flags); - } - - private static class OldKeyListenerAdapter extends ListenerBase - implements Consumer { - private final String relativeKey; - private final ITable targetSource; - private final ITableListener targetListener; - - public OldKeyListenerAdapter( - String relativeKey, ITable targetSource, ITableListener targetListener) { - this.relativeKey = relativeKey; - this.targetSource = targetSource; - this.targetListener = targetListener; - } - - @Override - public void accept(EntryNotification event) { - targetListener.valueChangedEx(targetSource, relativeKey, event.value.getValue(), event.flags); - } - } - - /** - * {@inheritDoc} - * - * @deprecated Use {@link edu.wpi.first.networktables.NetworkTable#addEntryListener(String, - * TableEntryListener, int)} or {@link NetworkTableEntry#addListener(Consumer, int)} instead. - */ - @Override - @Deprecated - public synchronized void addTableListenerEx(String key, ITableListener listener, int flags) { - List adapters = oldListenerMap.get(listener); - if (adapters == null) { - adapters = new ArrayList(); - oldListenerMap.put(listener, adapters); - } - OldKeyListenerAdapter adapter = new OldKeyListenerAdapter(key, this, listener); - adapter.uid = inst.addEntryListener(getEntry(key), adapter, flags); - adapters.add(adapter); - } - - /** - * {@inheritDoc} - * - * @deprecated Use {@link - * edu.wpi.first.networktables.NetworkTable#addSubTableListener(TableListener, boolean)} - * instead. - */ - @Override - @Deprecated - public void addSubTableListener(final ITableListener listener) { - addSubTableListener(listener, false); - } - - private static class OldSubListenerAdapter extends ListenerBase - implements Consumer { - private final int prefixLen; - private final ITable targetSource; - private final ITableListener targetListener; - private final Set notifiedTables = new HashSet(); - - public OldSubListenerAdapter( - int prefixLen, ITable targetSource, ITableListener targetListener) { - this.prefixLen = prefixLen; - this.targetSource = targetSource; - this.targetListener = targetListener; - } - - @Override - public void accept(EntryNotification event) { - String relativeKey = event.name.substring(prefixLen); - int endSubTable = relativeKey.indexOf(PATH_SEPARATOR); - if (endSubTable == -1) return; - String subTableKey = relativeKey.substring(0, endSubTable); - if (notifiedTables.contains(subTableKey)) return; - notifiedTables.add(subTableKey); - targetListener.valueChangedEx( - targetSource, subTableKey, targetSource.getSubTable(subTableKey), event.flags); - } - } - - /** - * {@inheritDoc} - * - * @deprecated Use {@link - * edu.wpi.first.networktables.NetworkTable#addSubTableListener(TableListener, boolean)} - * instead. - */ - @Override - @Deprecated - public synchronized void addSubTableListener(final ITableListener listener, boolean localNotify) { - List adapters = oldListenerMap.get(listener); - if (adapters == null) { - adapters = new ArrayList(); - oldListenerMap.put(listener, adapters); - } - OldSubListenerAdapter adapter = new OldSubListenerAdapter(path.length() + 1, this, listener); - int flags = NOTIFY_NEW | NOTIFY_IMMEDIATE; - if (localNotify) flags |= NOTIFY_LOCAL; - adapter.uid = inst.addEntryListener(pathWithSep, adapter, flags); - adapters.add(adapter); - } - - /** - * {@inheritDoc} - * - * @deprecated Use {@link edu.wpi.first.networktables.NetworkTable#removeTableListener(int)} - * instead. - */ - @Override - @Deprecated - public synchronized void removeTableListener(ITableListener listener) { - List adapters = oldListenerMap.remove(listener); - if (adapters != null) { - for (ListenerBase adapter : adapters) inst.removeEntryListener(adapter.uid); - } - } - - /** {@inheritDoc} */ - @Override - public ITable getSubTable(String key) { - return new NetworkTable(inst, pathWithSep + key); - } - - /** {@inheritDoc} */ - @Override - public boolean containsKey(String key) { - return getEntry(key).exists(); - } - - @Override - public boolean containsSubTable(String key) { - int[] handles = - NetworkTablesJNI.getEntries(inst.getHandle(), pathWithSep + key + PATH_SEPARATOR, 0); - return handles.length != 0; - } - - /** - * @param types bitmask of types; 0 is treated as a "don't care". - * @return keys currently in the table - */ - @Override - public Set getKeys(int types) { - Set keys = new HashSet(); - int prefixLen = path.length() + 1; - for (EntryInfo info : inst.getEntryInfo(pathWithSep, types)) { - String relativeKey = info.name.substring(prefixLen); - if (relativeKey.indexOf(PATH_SEPARATOR) != -1) continue; - keys.add(relativeKey); - // populate entries as we go - if (entries.get(relativeKey) == null) { - entries.putIfAbsent(relativeKey, new NetworkTableEntry(inst, info.entry)); - } - } - return keys; - } - - /** {@inheritDoc} */ - @Override - public Set getKeys() { - return getKeys(0); - } - - /** {@inheritDoc} */ - @Override - public Set getSubTables() { - Set keys = new HashSet(); - int prefixLen = path.length() + 1; - for (EntryInfo info : inst.getEntryInfo(pathWithSep, 0)) { - String relativeKey = info.name.substring(prefixLen); - int endSubTable = relativeKey.indexOf(PATH_SEPARATOR); - if (endSubTable == -1) continue; - keys.add(relativeKey.substring(0, endSubTable)); - } - return keys; - } - - /** {@inheritDoc} */ - @Override - public boolean putNumber(String key, double value) { - return getEntry(key).setNumber(value); - } - - /** {@inheritDoc} */ - @Override - public boolean setDefaultNumber(String key, double defaultValue) { - return getEntry(key).setDefaultDouble(defaultValue); - } - - /** {@inheritDoc} */ - @Override - public double getNumber(String key, double defaultValue) { - return getEntry(key).getDouble(defaultValue); - } - - /** {@inheritDoc} */ - @Override - public boolean putString(String key, String value) { - return getEntry(key).setString(value); - } - - /** {@inheritDoc} */ - @Override - public boolean setDefaultString(String key, String defaultValue) { - return getEntry(key).setDefaultString(defaultValue); - } - - /** {@inheritDoc} */ - @Override - public String getString(String key, String defaultValue) { - return getEntry(key).getString(defaultValue); - } - - /** {@inheritDoc} */ - @Override - public boolean putBoolean(String key, boolean value) { - return getEntry(key).setBoolean(value); - } - - /** {@inheritDoc} */ - @Override - public boolean setDefaultBoolean(String key, boolean defaultValue) { - return getEntry(key).setDefaultBoolean(defaultValue); - } - - /** {@inheritDoc} */ - @Override - public boolean getBoolean(String key, boolean defaultValue) { - return getEntry(key).getBoolean(defaultValue); - } - - /** {@inheritDoc} */ - @Override - public boolean putBooleanArray(String key, boolean[] value) { - return getEntry(key).setBooleanArray(value); - } - - /** {@inheritDoc} */ - @Override - public boolean putBooleanArray(String key, Boolean[] value) { - return getEntry(key).setBooleanArray(value); - } - - /** {@inheritDoc} */ - @Override - public boolean setDefaultBooleanArray(String key, boolean[] defaultValue) { - return getEntry(key).setDefaultBooleanArray(defaultValue); - } - - /** {@inheritDoc} */ - @Override - public boolean setDefaultBooleanArray(String key, Boolean[] defaultValue) { - return getEntry(key).setDefaultBooleanArray(defaultValue); - } - - /** {@inheritDoc} */ - @Override - public boolean[] getBooleanArray(String key, boolean[] defaultValue) { - return getEntry(key).getBooleanArray(defaultValue); - } - - /** {@inheritDoc} */ - @Override - public Boolean[] getBooleanArray(String key, Boolean[] defaultValue) { - return getEntry(key).getBooleanArray(defaultValue); - } - - /** {@inheritDoc} */ - @Override - public boolean putNumberArray(String key, double[] value) { - return getEntry(key).setDoubleArray(value); - } - - /** {@inheritDoc} */ - @Override - public boolean putNumberArray(String key, Double[] value) { - return getEntry(key).setNumberArray(value); - } - - /** {@inheritDoc} */ - @Override - public boolean setDefaultNumberArray(String key, double[] defaultValue) { - return getEntry(key).setDefaultDoubleArray(defaultValue); - } - - /** {@inheritDoc} */ - @Override - public boolean setDefaultNumberArray(String key, Double[] defaultValue) { - return getEntry(key).setDefaultNumberArray(defaultValue); - } - - /** {@inheritDoc} */ - @Override - public double[] getNumberArray(String key, double[] defaultValue) { - return getEntry(key).getDoubleArray(defaultValue); - } - - /** {@inheritDoc} */ - @Override - public Double[] getNumberArray(String key, Double[] defaultValue) { - return getEntry(key).getDoubleArray(defaultValue); - } - - /** {@inheritDoc} */ - @Override - public boolean putStringArray(String key, String[] value) { - return getEntry(key).setStringArray(value); - } - - /** {@inheritDoc} */ - @Override - public boolean setDefaultStringArray(String key, String[] defaultValue) { - return getEntry(key).setDefaultStringArray(defaultValue); - } - - /** {@inheritDoc} */ - @Override - public String[] getStringArray(String key, String[] defaultValue) { - return getEntry(key).getStringArray(defaultValue); - } - - /** {@inheritDoc} */ - @Override - public boolean putRaw(String key, byte[] value) { - return getEntry(key).setRaw(value); - } - - /** {@inheritDoc} */ - @Override - public boolean setDefaultRaw(String key, byte[] defaultValue) { - return getEntry(key).setDefaultRaw(defaultValue); - } - - /** {@inheritDoc} */ - @Override - public boolean putRaw(String key, ByteBuffer value, int len) { - return getEntry(key).setRaw(value, len); - } - - /** {@inheritDoc} */ - @Override - public byte[] getRaw(String key, byte[] defaultValue) { - return getEntry(key).getRaw(defaultValue); - } - - /** - * Put a value in the table - * - * @param key the key to be assigned to - * @param value the value that will be assigned - * @return False if the table key already exists with a different type - */ - public boolean putValue(String key, NetworkTableValue value) { - return getEntry(key).setValue(value); - } - - /** - * Sets the current value in the table if it does not exist. - * - * @param key the key - * @param defaultValue the default value to set if key doens't exist. - * @return False if the table key exists with a different type - */ - public boolean setDefaultValue(String key, NetworkTableValue defaultValue) { - return getEntry(key).setDefaultValue(defaultValue); - } - - /** - * Gets the value associated with a key as a NetworkTableValue object. - * - * @param key the key of the value to look up - * @return the value associated with the given key - */ - public NetworkTableValue getValue(String key) { - return getEntry(key).getValue(); - } - - /** - * {@inheritDoc} - * - * @deprecated Use {@link edu.wpi.first.networktables.NetworkTableEntry#setValue(Object)} instead, - * e.g. `NetworkTable.getEntry(key).setValue(NetworkTableEntry.makeBoolean(false));` or - * `NetworkTable.getEntry(key).setValue(new Boolean(false));` - */ - @Override - @Deprecated - public boolean putValue(String key, Object value) throws IllegalArgumentException { - if (value instanceof Boolean) return putBoolean(key, ((Boolean) value).booleanValue()); - else if (value instanceof Number) return putDouble(key, ((Number) value).doubleValue()); - else if (value instanceof String) return putString(key, (String) value); - else if (value instanceof byte[]) return putRaw(key, (byte[]) value); - else if (value instanceof boolean[]) return putBooleanArray(key, (boolean[]) value); - else if (value instanceof double[]) return putNumberArray(key, (double[]) value); - else if (value instanceof Boolean[]) return putBooleanArray(key, toNative((Boolean[]) value)); - else if (value instanceof Number[]) return putNumberArray(key, toNative((Number[]) value)); - else if (value instanceof String[]) return putStringArray(key, (String[]) value); - else if (value instanceof NetworkTableValue) - return getEntry(key).setValue((NetworkTableValue) value); - else - throw new IllegalArgumentException( - "Value of type " + value.getClass().getName() + " cannot be put into a table"); - } - - /** - * {@inheritDoc} - * - * @deprecated Use {@link edu.wpi.first.networktables.NetworkTableEntry#getValue()} instead, e.g. - * `NetworkTable.getEntry(key).getValue();` - */ - @Override - @Deprecated - public Object getValue(String key, Object defaultValue) { - NetworkTableValue value = getValue(key); - if (value.getType() == NetworkTableType.kUnassigned) { - return defaultValue; - } - return value.getValue(); - } - - /** The persistent flag value. */ - public static final int PERSISTENT = 1; - - /** {@inheritDoc} */ - @Override - public void setPersistent(String key) { - getEntry(key).setPersistent(); - } - - /** {@inheritDoc} */ - @Override - public void clearPersistent(String key) { - getEntry(key).clearPersistent(); - } - - /** {@inheritDoc} */ - @Override - public boolean isPersistent(String key) { - return getEntry(key).isPersistent(); - } - - /** {@inheritDoc} */ - @Override - public void setFlags(String key, int flags) { - getEntry(key).setFlags(flags); - } - - /** {@inheritDoc} */ - @Override - public void clearFlags(String key, int flags) { - getEntry(key).clearFlags(flags); - } - - /** {@inheritDoc} */ - @Override - public int getFlags(String key) { - return getEntry(key).getFlags(); - } - - /** {@inheritDoc} */ - @Override - public void delete(String key) { - getEntry(key).delete(); - } - - /** - * Deletes ALL keys in ALL subtables. Use with caution! - * - * @deprecated Use {@link NetworkTableInstance#deleteAllEntries()} instead. - */ - @Deprecated - public static void globalDeleteAll() { - NetworkTableInstance.getDefault().deleteAllEntries(); - } - - /** - * Flushes all updated values immediately to the network. Note: This is rate-limited to protect - * the network from flooding. This is primarily useful for synchronizing network updates with user - * code. - * - * @deprecated Use {@link NetworkTableInstance#flush()} instead. - */ - @Deprecated - public static void flush() { - NetworkTableInstance.getDefault().flush(); - } - - /** - * Set the periodic update rate. - * - * @param interval update interval in seconds (range 0.01 to 1.0) - * @deprecated Use {@link NetworkTableInstance#setUpdateRate(double)} instead. - */ - @Deprecated - public static void setUpdateRate(double interval) { - NetworkTableInstance.getDefault().setUpdateRate(interval); - } - - /** - * Saves persistent keys to a file. The server does this automatically. - * - * @param filename file name - * @throws PersistentException if error saving file - * @deprecated Use {@link NetworkTableInstance#savePersistent(String)} instead. - */ - @Deprecated - public static void savePersistent(String filename) throws PersistentException { - NetworkTableInstance.getDefault().savePersistent(filename); - } - - /** - * Loads persistent keys from a file. The server does this automatically. - * - * @param filename file name - * @return List of warnings (errors result in an exception instead) - * @throws PersistentException if error reading file - * @deprecated Use {@link NetworkTableInstance#loadPersistent(String)} instead. - */ - @Deprecated - public static String[] loadPersistent(String filename) throws PersistentException { - return NetworkTableInstance.getDefault().loadPersistent(filename); - } - - /* - * Deprecated Methods - */ - - /** - * {@inheritDoc} - * - * @deprecated Use {@link #putNumber(String, double)} instead. - */ - @Override - @Deprecated - public boolean putDouble(String key, double value) { - return putNumber(key, value); - } - - /** - * {@inheritDoc} - * - * @deprecated Use {@link #getNumber(String, double)} instead. - */ - @Override - @Deprecated - public double getDouble(String key, double defaultValue) { - return getNumber(key, defaultValue); - } - - /** {@inheritDoc} */ - @Override - public String getPath() { - return path; - } - - @Override - public boolean equals(Object o) { - if (o == this) { - return true; - } - if (!(o instanceof NetworkTable)) { - return false; - } - NetworkTable other = (NetworkTable) o; - return inst.equals(other.inst) && path.equals(other.path); - } - - @Override - public int hashCode() { - return Objects.hash(inst, path); - } -} diff --git a/ntcore/src/main/java/edu/wpi/first/wpilibj/tables/IRemote.java b/ntcore/src/main/java/edu/wpi/first/wpilibj/tables/IRemote.java deleted file mode 100644 index 508e8812b5..0000000000 --- a/ntcore/src/main/java/edu/wpi/first/wpilibj/tables/IRemote.java +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright (c) FIRST and other WPILib contributors. -// Open Source Software; you can modify and/or share it under the terms of -// the WPILib BSD license file in the root directory of this project. - -package edu.wpi.first.wpilibj.tables; - -/** - * Represents an object that has a remote connection. - * - * @deprecated Use {@link edu.wpi.first.networktables.NetworkTableInstance}. - */ -@Deprecated -@SuppressWarnings("checkstyle:all") -public interface IRemote { - /** - * Register an object to listen for connection and disconnection events - * - * @param listener the listener to be register - * @param immediateNotify if the listener object should be notified of the current connection - * state - */ - public void addConnectionListener(IRemoteConnectionListener listener, boolean immediateNotify); - - /** - * Unregister a listener from connection events - * - * @param listener the listener to be unregistered - */ - public void removeConnectionListener(IRemoteConnectionListener listener); - - /** - * Get the current state of the objects connection - * - * @return the current connection state - */ - public boolean isConnected(); - - /** - * If the object is acting as a server - * - * @return if the object is a server - */ - public boolean isServer(); -} diff --git a/ntcore/src/main/java/edu/wpi/first/wpilibj/tables/IRemoteConnectionListener.java b/ntcore/src/main/java/edu/wpi/first/wpilibj/tables/IRemoteConnectionListener.java deleted file mode 100644 index ed5d02b8b6..0000000000 --- a/ntcore/src/main/java/edu/wpi/first/wpilibj/tables/IRemoteConnectionListener.java +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright (c) FIRST and other WPILib contributors. -// Open Source Software; you can modify and/or share it under the terms of -// the WPILib BSD license file in the root directory of this project. - -package edu.wpi.first.wpilibj.tables; - -import edu.wpi.first.networktables.ConnectionInfo; - -/** - * A listener that listens for connection changes in a {@link IRemote} object. - * - * @deprecated Use Consumer<{@link edu.wpi.first.networktables.ConnectionNotification}>. - */ -@Deprecated -@SuppressWarnings("checkstyle:all") -public interface IRemoteConnectionListener { - /** - * Called when an IRemote is connected - * - * @param remote the object that connected - */ - public void connected(IRemote remote); - /** - * Called when an IRemote is disconnected - * - * @param remote the object that disconnected - */ - public void disconnected(IRemote remote); - /** - * Extended version of connected called when an IRemote is connected. Contains the connection info - * of the connected remote - * - * @param remote the object that connected - * @param info the connection info for the connected remote - */ - public default void connectedEx(IRemote remote, ConnectionInfo info) { - connected(remote); - } - /** - * Extended version of connected called when an IRemote is disconnected. Contains the connection - * info of the disconnected remote - * - * @param remote the object that disconnected - * @param info the connection info for the disconnected remote - */ - public default void disconnectedEx(IRemote remote, ConnectionInfo info) { - disconnected(remote); - } -} diff --git a/ntcore/src/main/java/edu/wpi/first/wpilibj/tables/ITable.java b/ntcore/src/main/java/edu/wpi/first/wpilibj/tables/ITable.java deleted file mode 100644 index 330484a73d..0000000000 --- a/ntcore/src/main/java/edu/wpi/first/wpilibj/tables/ITable.java +++ /dev/null @@ -1,513 +0,0 @@ -// Copyright (c) FIRST and other WPILib contributors. -// Open Source Software; you can modify and/or share it under the terms of -// the WPILib BSD license file in the root directory of this project. - -package edu.wpi.first.wpilibj.tables; - -import java.nio.ByteBuffer; -import java.util.Set; - -/** - * A table whose values can be read and written to. - * - * @deprecated Use {@link edu.wpi.first.networktables.NetworkTable}. - */ -@Deprecated -@SuppressWarnings("checkstyle:all") -public interface ITable { - - /** - * Checks the table and tells if it contains the specified key - * - * @param key the key to search for - * @return true if the table as a value assigned to the given key - */ - public boolean containsKey(String key); - - /** - * @param key the key to search for - * @return true if there is a subtable with the key which contains at least one key/subtable of - * its own - */ - public boolean containsSubTable(String key); - - /** - * Returns the table at the specified key. If there is no table at the specified key, it will - * create a new table - * - * @param key the name of the table relative to this one - * @return a sub table relative to this one - */ - public ITable getSubTable(String key); - - /** - * Gets all keys in the table (not including sub-tables). - * - * @param types bitmask of types; 0 is treated as a "don't care". - * @return keys currently in the table - */ - public Set getKeys(int types); - - /** - * Gets all keys in the table (not including sub-tables). - * - * @return keys currently in the table - */ - public Set getKeys(); - - /** - * Gets the names of all subtables in the table. - * - * @return subtables currently in the table - */ - public Set getSubTables(); - - /** - * Makes a key's value persistent through program restarts. The key cannot be null. - * - * @param key the key name - */ - public void setPersistent(String key); - - /** - * Stop making a key's value persistent through program restarts. The key cannot be null. - * - * @param key the key name - */ - public void clearPersistent(String key); - - /** - * Returns whether the value is persistent through program restarts. The key cannot be null. - * - * @param key the key name - * @return True if the value is persistent. - */ - public boolean isPersistent(String key); - - /** - * Sets flags on the specified key in this table. The key can not be null. - * - * @param key the key name - * @param flags the flags to set (bitmask) - */ - public void setFlags(String key, int flags); - - /** - * Clears flags on the specified key in this table. The key can not be null. - * - * @param key the key name - * @param flags the flags to clear (bitmask) - */ - public void clearFlags(String key, int flags); - - /** - * Returns the flags for the specified key. - * - * @param key the key name - * @return the flags, or 0 if the key is not defined - */ - public int getFlags(String key); - - /** - * Deletes the specified key in this table. The key can not be null. - * - * @param key the key name - */ - public void delete(String key); - - /** - * Gets the value associated with a key as an object. NOTE: If the value is a double, it will - * return a Double object, not a primitive. To get the primitive, use {@link #getDouble(String, - * double)}. - * - * @param key the key of the value to look up - * @param defaultValue the default value if the key is null - * @return the value associated with the given key - */ - public Object getValue(String key, Object defaultValue); - - /** - * Put a value in the table - * - * @param key the key to be assigned to - * @param value the value that will be assigned - * @return False if the table key already exists with a different type - * @throws IllegalArgumentException when the value is not supported by the table - */ - public boolean putValue(String key, Object value) throws IllegalArgumentException; - - /** - * Put a number in the table - * - * @param key the key to be assigned to - * @param value the value that will be assigned - * @return False if the table key already exists with a different type - */ - public boolean putNumber(String key, double value); - - /** - * Gets the current value in the table, setting it if it does not exist. - * - * @param key the key - * @param defaultValue the default value to set if key doens't exist. - * @return False if the table key exists with a different type - */ - public boolean setDefaultNumber(String key, double defaultValue); - - /** - * Returns the number the key maps to. If the key does not exist or is of different type, it will - * return the default value. - * - * @param key the key to look up - * @param defaultValue the value to be returned if no value is found - * @return the value associated with the given key or the given default value if there is no value - * associated with the key - */ - public double getNumber(String key, double defaultValue); - - /** - * Put a string in the table - * - * @param key the key to be assigned to - * @param value the value that will be assigned - * @return False if the table key already exists with a different type - */ - public boolean putString(String key, String value); - - /** - * Gets the current value in the table, setting it if it does not exist. - * - * @param key the key - * @param defaultValue the default value to set if key doens't exist. - * @return False if the table key exists with a different type - */ - public boolean setDefaultString(String key, String defaultValue); - - /** - * Returns the string the key maps to. If the key does not exist or is of different type, it will - * return the default value. - * - * @param key the key to look up - * @param defaultValue the value to be returned if no value is found - * @return the value associated with the given key or the given default value if there is no value - * associated with the key - */ - public String getString(String key, String defaultValue); - - /** - * Put a boolean in the table - * - * @param key the key to be assigned to - * @param value the value that will be assigned - * @return False if the table key already exists with a different type - */ - public boolean putBoolean(String key, boolean value); - - /** - * Gets the current value in the table, setting it if it does not exist. - * - * @param key the key - * @param defaultValue the default value to set if key doens't exist. - * @return False if the table key exists with a different type - */ - public boolean setDefaultBoolean(String key, boolean defaultValue); - - /** - * Returns the boolean the key maps to. If the key does not exist or is of different type, it will - * return the default value. - * - * @param key the key to look up - * @param defaultValue the value to be returned if no value is found - * @return the value associated with the given key or the given default value if there is no value - * associated with the key - */ - public boolean getBoolean(String key, boolean defaultValue); - - /** - * Put a boolean array in the table - * - * @param key the key to be assigned to - * @param value the value that will be assigned - * @return False if the table key already exists with a different type - */ - public boolean putBooleanArray(String key, boolean[] value); - - /** - * Gets the current value in the table, setting it if it does not exist. - * - * @param key the key - * @param defaultValue the default value to set if key doens't exist. - * @return False if the table key exists with a different type - */ - public boolean setDefaultBooleanArray(String key, boolean[] defaultValue); - - /** - * Put a boolean array in the table - * - * @param key the key to be assigned to - * @param value the value that will be assigned - * @return False if the table key already exists with a different type - */ - public boolean putBooleanArray(String key, Boolean[] value); - - /** - * Gets the current value in the table, setting it if it does not exist. - * - * @param key the key - * @param defaultValue the default value to set if key doens't exist. - * @return False if the table key exists with a different type - */ - public boolean setDefaultBooleanArray(String key, Boolean[] defaultValue); - - /** - * Returns the boolean array the key maps to. If the key does not exist or is of different type, - * it will return the default value. - * - * @param key the key to look up - * @param defaultValue the value to be returned if no value is found - * @return the value associated with the given key or the given default value if there is no value - * associated with the key - */ - public boolean[] getBooleanArray(String key, boolean[] defaultValue); - /** - * Returns the boolean array the key maps to. If the key does not exist or is of different type, - * it will return the default value. - * - * @param key the key to look up - * @param defaultValue the value to be returned if no value is found - * @return the value associated with the given key or the given default value if there is no value - * associated with the key - */ - public Boolean[] getBooleanArray(String key, Boolean[] defaultValue); - - /** - * Put a number array in the table - * - * @param key the key to be assigned to - * @param value the value that will be assigned - * @return False if the table key already exists with a different type - */ - public boolean putNumberArray(String key, double[] value); - - /** - * Gets the current value in the table, setting it if it does not exist. - * - * @param key the key - * @param defaultValue the default value to set if key doens't exist. - * @return False if the table key exists with a different type - */ - public boolean setDefaultNumberArray(String key, double[] defaultValue); - - /** - * Put a number array in the table - * - * @param key the key to be assigned to - * @param value the value that will be assigned - * @return False if the table key already exists with a different type - */ - public boolean putNumberArray(String key, Double[] value); - - /** - * Gets the current value in the table, setting it if it does not exist. - * - * @param key the key - * @param defaultValue the default value to set if key doens't exist. - * @return False if the table key exists with a different type - */ - public boolean setDefaultNumberArray(String key, Double[] defaultValue); - - /** - * Returns the number array the key maps to. If the key does not exist or is of different type, it - * will return the default value. - * - * @param key the key to look up - * @param defaultValue the value to be returned if no value is found - * @return the value associated with the given key or the given default value if there is no value - * associated with the key - */ - public double[] getNumberArray(String key, double[] defaultValue); - /** - * Returns the number array the key maps to. If the key does not exist or is of different type, it - * will return the default value. - * - * @param key the key to look up - * @param defaultValue the value to be returned if no value is found - * @return the value associated with the given key or the given default value if there is no value - * associated with the key - */ - public Double[] getNumberArray(String key, Double[] defaultValue); - - /** - * Put a string array in the table - * - * @param key the key to be assigned to - * @param value the value that will be assigned - * @return False if the table key already exists with a different type - */ - public boolean putStringArray(String key, String[] value); - - /** - * Gets the current value in the table, setting it if it does not exist. - * - * @param key the key - * @param defaultValue the default value to set if key doens't exist. - * @return False if the table key exists with a different type - */ - public boolean setDefaultStringArray(String key, String[] defaultValue); - - /** - * Returns the string array the key maps to. If the key does not exist or is of different type, it - * will return the default value. - * - * @param key the key to look up - * @param defaultValue the value to be returned if no value is found - * @return the value associated with the given key or the given default value if there is no value - * associated with the key - */ - public String[] getStringArray(String key, String[] defaultValue); - - /** - * Put a raw value (byte array) in the table - * - * @param key the key to be assigned to - * @param value the value that will be assigned - * @return False if the table key already exists with a different type - */ - public boolean putRaw(String key, byte[] value); - - /** - * Gets the current value in the table, setting it if it does not exist. - * - * @param key the key - * @param defaultValue the default value to set if key doens't exist. - * @return False if the table key exists with a different type - */ - public boolean setDefaultRaw(String key, byte[] defaultValue); - - /** - * Put a raw value (bytes from a byte buffer) in the table - * - * @param key the key to be assigned to - * @param value the value that will be assigned - * @param len the length of the value - * @return False if the table key already exists with a different type - */ - public boolean putRaw(String key, ByteBuffer value, int len); - - /** - * Returns the raw value (byte array) the key maps to. If the key does not exist or is of - * different type, it will return the default value. - * - * @param key the key to look up - * @param defaultValue the value to be returned if no value is found - * @return the value associated with the given key or the given default value if there is no value - * associated with the key - */ - public byte[] getRaw(String key, byte[] defaultValue); - - /** Notifier flag values. */ - public static final int NOTIFY_IMMEDIATE = 0x01; - - public static final int NOTIFY_LOCAL = 0x02; - public static final int NOTIFY_NEW = 0x04; - public static final int NOTIFY_DELETE = 0x08; - public static final int NOTIFY_UPDATE = 0x10; - public static final int NOTIFY_FLAGS = 0x20; - - /** - * Add a listener for changes to the table - * - * @param listener the listener to add - */ - public void addTableListener(ITableListener listener); - /** - * Add a listener for changes to the table - * - * @param listener the listener to add - * @param immediateNotify if true then this listener will be notified of all current entries - * (marked as new) - */ - public void addTableListener(ITableListener listener, boolean immediateNotify); - /** - * Add a listener for changes to the table - * - * @param listener the listener to add - * @param flags bitmask specifying desired notifications - */ - public void addTableListenerEx(ITableListener listener, int flags); - - /** - * Add a listener for changes to a specific key the table - * - * @param key the key to listen for - * @param listener the listener to add - * @param immediateNotify if true then this listener will be notified of all current entries - * (marked as new) - */ - public void addTableListener(String key, ITableListener listener, boolean immediateNotify); - /** - * Add a listener for changes to a specific key the table - * - * @param key the key to listen for - * @param listener the listener to add - * @param flags bitmask specifying desired notifications - */ - public void addTableListenerEx(String key, ITableListener listener, int flags); - /** - * This will immediately notify the listener of all current sub tables - * - * @param listener the listener to notify - */ - public void addSubTableListener(final ITableListener listener); - /** - * This will immediately notify the listener of all current sub tables - * - * @param listener the listener to notify - * @param localNotify if true then this listener will be notified of all local changes in addition - * to all remote changes - */ - public void addSubTableListener(final ITableListener listener, boolean localNotify); - /** - * Remove a listener from receiving table events - * - * @param listener the listener to be removed - */ - public void removeTableListener(ITableListener listener); - - /* - * Deprecated Methods - */ - - /** - * Maps the specified key to the specified value in this table. The key can not be null. The value - * can be retrieved by calling the get method with a key that is equal to the original key. - * - * @param key the key - * @param value the value - * @return False if the table key already exists with a different type - * @throws IllegalArgumentException if key is null - * @deprecated Use {@link #putNumber(String, double)} instead. - */ - @Deprecated - public boolean putDouble(String key, double value); - - /** - * Returns the value at the specified key. - * - * @param key the key - * @param defaultValue the value returned if the key is undefined - * @return the value - * @throws IllegalArgumentException if the value mapped to by the key is not a double - * @throws IllegalArgumentException if the key is null - * @deprecated Use {@link #getNumber(String, double)} instead. - */ - @Deprecated - public double getDouble(String key, double defaultValue); - - /** - * Gets the full path of this table. Does not include the trailing "/". - * - * @return The path to this table (e.g. "", "/foo"). - */ - public String getPath(); -} diff --git a/ntcore/src/main/java/edu/wpi/first/wpilibj/tables/ITableListener.java b/ntcore/src/main/java/edu/wpi/first/wpilibj/tables/ITableListener.java deleted file mode 100644 index 3ff81f17ec..0000000000 --- a/ntcore/src/main/java/edu/wpi/first/wpilibj/tables/ITableListener.java +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright (c) FIRST and other WPILib contributors. -// Open Source Software; you can modify and/or share it under the terms of -// the WPILib BSD license file in the root directory of this project. - -package edu.wpi.first.wpilibj.tables; - -/** - * A listener that listens to changes in values in a {@link ITable}. - * - * @deprecated Use Consumer<{@link edu.wpi.first.networktables.EntryNotification}>, {@link - * edu.wpi.first.networktables.TableEntryListener}, or {@link - * edu.wpi.first.networktables.TableListener} as appropriate. - */ -@FunctionalInterface -@Deprecated -@SuppressWarnings("checkstyle:all") -public interface ITableListener { - /** - * Called when a key-value pair is changed in a {@link ITable} - * - * @param source the table the key-value pair exists in - * @param key the key associated with the value that changed - * @param value the new value - * @param isNew true if the key did not previously exist in the table, otherwise it is false - */ - public void valueChanged(ITable source, String key, Object value, boolean isNew); - - /** - * Extended version of valueChanged. Called when a key-value pair is changed in a {@link ITable}. - * The default implementation simply calls valueChanged(). If this is overridden, valueChanged() - * will not be called. - * - * @param source the table the key-value pair exists in - * @param key the key associated with the value that changed - * @param value the new value - * @param flags update flags; for example, NOTIFY_NEW if the key did not previously exist in the - * table - */ - public default void valueChangedEx(ITable source, String key, Object value, int flags) { - // NOTIFY_NEW = 0x04 - valueChanged(source, key, value, (flags & 0x04) != 0); - } -} From 79267f9e606086cc8bb29942cc3753f4d5aee030 Mon Sep 17 00:00:00 2001 From: Peter Johnson Date: Sun, 4 Apr 2021 14:34:11 -0700 Subject: [PATCH 14/34] [ntcore] Remove NetworkTable -> nt::NetworkTable shim --- .../include/networktables/NetworkTable.h | 5 --- .../src/test/native/cpp/NetworkTableTest.cpp | 38 +++++++++---------- .../main/native/cpp/livewindow/LiveWindow.cpp | 2 +- .../cpp/smartdashboard/SendableRegistry.cpp | 2 +- .../frc/smartdashboard/SendableRegistry.h | 2 +- 5 files changed, 22 insertions(+), 27 deletions(-) diff --git a/ntcore/src/main/native/include/networktables/NetworkTable.h b/ntcore/src/main/native/include/networktables/NetworkTable.h index 3c32d7d1f3..682b3ee906 100644 --- a/ntcore/src/main/native/include/networktables/NetworkTable.h +++ b/ntcore/src/main/native/include/networktables/NetworkTable.h @@ -549,9 +549,4 @@ class NetworkTable final { } // namespace nt -// For backwards compatibility -#ifndef NAMESPACED_NT -using nt::NetworkTable; // NOLINT -#endif - #endif // NTCORE_NETWORKTABLES_NETWORKTABLE_H_ diff --git a/ntcore/src/test/native/cpp/NetworkTableTest.cpp b/ntcore/src/test/native/cpp/NetworkTableTest.cpp index ee3dbe7a9b..40c6f81d98 100644 --- a/ntcore/src/test/native/cpp/NetworkTableTest.cpp +++ b/ntcore/src/test/native/cpp/NetworkTableTest.cpp @@ -10,51 +10,51 @@ class NetworkTableTest : public ::testing::Test {}; TEST_F(NetworkTableTest, BasenameKey) { - EXPECT_EQ("simple", NetworkTable::BasenameKey("simple")); - EXPECT_EQ("simple", NetworkTable::BasenameKey("one/two/many/simple")); + EXPECT_EQ("simple", nt::NetworkTable::BasenameKey("simple")); + EXPECT_EQ("simple", nt::NetworkTable::BasenameKey("one/two/many/simple")); EXPECT_EQ("simple", - NetworkTable::BasenameKey("//////an/////awful/key////simple")); + nt::NetworkTable::BasenameKey("//////an/////awful/key////simple")); } TEST_F(NetworkTableTest, NormalizeKeySlash) { - EXPECT_EQ("/", NetworkTable::NormalizeKey("///")); - EXPECT_EQ("/no/normal/req", NetworkTable::NormalizeKey("/no/normal/req")); + EXPECT_EQ("/", nt::NetworkTable::NormalizeKey("///")); + EXPECT_EQ("/no/normal/req", nt::NetworkTable::NormalizeKey("/no/normal/req")); EXPECT_EQ("/no/leading/slash", - NetworkTable::NormalizeKey("no/leading/slash")); - EXPECT_EQ("/what/an/awful/key/", - NetworkTable::NormalizeKey("//////what////an/awful/////key///")); + nt::NetworkTable::NormalizeKey("no/leading/slash")); + EXPECT_EQ("/what/an/awful/key/", nt::NetworkTable::NormalizeKey( + "//////what////an/awful/////key///")); } TEST_F(NetworkTableTest, NormalizeKeyNoSlash) { - EXPECT_EQ("a", NetworkTable::NormalizeKey("a", false)); - EXPECT_EQ("a", NetworkTable::NormalizeKey("///a", false)); + EXPECT_EQ("a", nt::NetworkTable::NormalizeKey("a", false)); + EXPECT_EQ("a", nt::NetworkTable::NormalizeKey("///a", false)); EXPECT_EQ("leading/slash", - NetworkTable::NormalizeKey("/leading/slash", false)); + nt::NetworkTable::NormalizeKey("/leading/slash", false)); EXPECT_EQ("no/leading/slash", - NetworkTable::NormalizeKey("no/leading/slash", false)); - EXPECT_EQ( - "what/an/awful/key/", - NetworkTable::NormalizeKey("//////what////an/awful/////key///", false)); + nt::NetworkTable::NormalizeKey("no/leading/slash", false)); + EXPECT_EQ("what/an/awful/key/", + nt::NetworkTable::NormalizeKey("//////what////an/awful/////key///", + false)); } TEST_F(NetworkTableTest, GetHierarchyEmpty) { std::vector expected{"/"}; - ASSERT_EQ(expected, NetworkTable::GetHierarchy("")); + ASSERT_EQ(expected, nt::NetworkTable::GetHierarchy("")); } TEST_F(NetworkTableTest, GetHierarchyRoot) { std::vector expected{"/"}; - ASSERT_EQ(expected, NetworkTable::GetHierarchy("/")); + ASSERT_EQ(expected, nt::NetworkTable::GetHierarchy("/")); } TEST_F(NetworkTableTest, GetHierarchyNormal) { std::vector expected{"/", "/foo", "/foo/bar", "/foo/bar/baz"}; - ASSERT_EQ(expected, NetworkTable::GetHierarchy("/foo/bar/baz")); + ASSERT_EQ(expected, nt::NetworkTable::GetHierarchy("/foo/bar/baz")); } TEST_F(NetworkTableTest, GetHierarchyTrailingSlash) { std::vector expected{"/", "/foo", "/foo/bar", "/foo/bar/"}; - ASSERT_EQ(expected, NetworkTable::GetHierarchy("/foo/bar/")); + ASSERT_EQ(expected, nt::NetworkTable::GetHierarchy("/foo/bar/")); } TEST_F(NetworkTableTest, ContainsKey) { diff --git a/wpilibc/src/main/native/cpp/livewindow/LiveWindow.cpp b/wpilibc/src/main/native/cpp/livewindow/LiveWindow.cpp index cdfba922b6..1b749b6141 100644 --- a/wpilibc/src/main/native/cpp/livewindow/LiveWindow.cpp +++ b/wpilibc/src/main/native/cpp/livewindow/LiveWindow.cpp @@ -154,7 +154,7 @@ void LiveWindow::UpdateValuesUnsafe() { return; } auto ssTable = m_impl->liveWindowTable->GetSubTable(cbdata.subsystem); - std::shared_ptr table; + std::shared_ptr table; // Treat name==subsystem as top level of subsystem if (cbdata.name == cbdata.subsystem) { table = ssTable; diff --git a/wpilibc/src/main/native/cpp/smartdashboard/SendableRegistry.cpp b/wpilibc/src/main/native/cpp/smartdashboard/SendableRegistry.cpp index de40d0565a..15cc985bf5 100644 --- a/wpilibc/src/main/native/cpp/smartdashboard/SendableRegistry.cpp +++ b/wpilibc/src/main/native/cpp/smartdashboard/SendableRegistry.cpp @@ -352,7 +352,7 @@ Sendable* SendableRegistry::GetSendable(UID uid) { } void SendableRegistry::Publish(UID sendableUid, - std::shared_ptr table) { + std::shared_ptr table) { std::scoped_lock lock(m_impl->mutex); if (sendableUid == 0 || (sendableUid - 1) >= m_impl->components.size() || !m_impl->components[sendableUid - 1]) { diff --git a/wpilibc/src/main/native/include/frc/smartdashboard/SendableRegistry.h b/wpilibc/src/main/native/include/frc/smartdashboard/SendableRegistry.h index e3fd0de9ef..e24c1934d2 100644 --- a/wpilibc/src/main/native/include/frc/smartdashboard/SendableRegistry.h +++ b/wpilibc/src/main/native/include/frc/smartdashboard/SendableRegistry.h @@ -285,7 +285,7 @@ class SendableRegistry { * @param sendableUid sendable unique id * @param table network table */ - void Publish(UID sendableUid, std::shared_ptr table); + void Publish(UID sendableUid, std::shared_ptr table); /** * Updates network table information from an object. From 397e569aaf74faa14e1cbff3367ed5dbc8e9595c Mon Sep 17 00:00:00 2001 From: Peter Johnson Date: Sun, 4 Apr 2021 12:40:46 -0700 Subject: [PATCH 15/34] [ntcore] Remove "using wpi" from nt namespace This removes the nt::ArrayRef, nt::StringRef, and nt::Twine aliases. --- ntcore/src/main/native/cpp/Dispatcher.cpp | 16 +- ntcore/src/main/native/cpp/Dispatcher.h | 11 +- ntcore/src/main/native/cpp/EntryNotifier.cpp | 4 +- ntcore/src/main/native/cpp/EntryNotifier.h | 6 +- ntcore/src/main/native/cpp/IEntryNotifier.h | 2 +- ntcore/src/main/native/cpp/IRpcServer.h | 4 +- ntcore/src/main/native/cpp/IStorage.h | 4 +- .../src/main/native/cpp/NetworkConnection.cpp | 2 +- .../src/main/native/cpp/NetworkConnection.h | 2 +- ntcore/src/main/native/cpp/RpcServer.cpp | 2 +- ntcore/src/main/native/cpp/RpcServer.h | 10 +- ntcore/src/main/native/cpp/Storage.cpp | 55 +++--- ntcore/src/main/native/cpp/Storage.h | 49 +++--- ntcore/src/main/native/cpp/Storage_load.cpp | 13 +- ntcore/src/main/native/cpp/Storage_save.cpp | 9 +- .../main/native/cpp/jni/NetworkTablesJNI.cpp | 8 +- .../native/cpp/networktables/NetworkTable.cpp | 165 ++++++++++-------- .../networktables/NetworkTableInstance.cpp | 16 +- ntcore/src/main/native/cpp/ntcore_c.cpp | 67 +++---- ntcore/src/main/native/cpp/ntcore_cpp.cpp | 48 ++--- .../include/networktables/NetworkTable.h | 104 +++++------ .../include/networktables/NetworkTableEntry.h | 50 +++--- .../networktables/NetworkTableEntry.inl | 53 +++--- .../networktables/NetworkTableInstance.h | 38 ++-- .../networktables/NetworkTableInstance.inl | 27 +-- .../include/networktables/NetworkTableValue.h | 42 +++-- .../networktables/TableEntryListener.h | 4 +- .../include/networktables/TableListener.h | 4 +- ntcore/src/main/native/include/ntcore_cpp.h | 73 ++++---- .../src/test/native/cpp/EntryNotifierTest.cpp | 2 +- .../src/test/native/cpp/MockEntryNotifier.h | 2 +- ntcore/src/test/native/cpp/MockRpcServer.h | 2 +- ntcore/src/test/native/cpp/StorageTest.cpp | 1 + ntcore/src/test/native/cpp/StorageTest.h | 2 +- 34 files changed, 460 insertions(+), 437 deletions(-) diff --git a/ntcore/src/main/native/cpp/Dispatcher.cpp b/ntcore/src/main/native/cpp/Dispatcher.cpp index ce6df31e59..839faeb1f2 100644 --- a/ntcore/src/main/native/cpp/Dispatcher.cpp +++ b/ntcore/src/main/native/cpp/Dispatcher.cpp @@ -18,9 +18,9 @@ using namespace nt; -void Dispatcher::StartServer(const Twine& persist_filename, +void Dispatcher::StartServer(const wpi::Twine& persist_filename, const char* listen_address, unsigned int port) { - std::string listen_address_copy(StringRef(listen_address).trim()); + std::string listen_address_copy(wpi::StringRef(listen_address).trim()); DispatcherBase::StartServer( persist_filename, std::unique_ptr(new wpi::TCPAcceptor( @@ -28,7 +28,7 @@ void Dispatcher::StartServer(const Twine& persist_filename, } void Dispatcher::SetServer(const char* server_name, unsigned int port) { - std::string server_name_copy(StringRef(server_name).trim()); + std::string server_name_copy(wpi::StringRef(server_name).trim()); SetConnector([=]() -> std::unique_ptr { return wpi::TCPConnector::connect(server_name_copy.c_str(), static_cast(port), m_logger, 1); @@ -36,7 +36,7 @@ void Dispatcher::SetServer(const char* server_name, unsigned int port) { } void Dispatcher::SetServer( - ArrayRef> servers) { + wpi::ArrayRef> servers) { wpi::SmallVector, 16> servers_copy; for (const auto& server : servers) { servers_copy.emplace_back(std::string{server.first.trim()}, @@ -53,7 +53,7 @@ void Dispatcher::SetServer( } void Dispatcher::SetServerTeam(unsigned int team, unsigned int port) { - std::pair servers[5]; + std::pair servers[5]; // 10.te.am.2 wpi::SmallString<32> fixed; @@ -95,7 +95,7 @@ void Dispatcher::SetServerTeam(unsigned int team, unsigned int port) { } void Dispatcher::SetServerOverride(const char* server_name, unsigned int port) { - std::string server_name_copy(StringRef(server_name).trim()); + std::string server_name_copy(wpi::StringRef(server_name).trim()); SetConnectorOverride([=]() -> std::unique_ptr { return wpi::TCPConnector::connect(server_name_copy.c_str(), static_cast(port), m_logger, 1); @@ -134,7 +134,7 @@ void DispatcherBase::StartLocal() { } void DispatcherBase::StartServer( - const Twine& persist_filename, + const wpi::Twine& persist_filename, std::unique_ptr acceptor) { { std::scoped_lock lock(m_user_mutex); @@ -230,7 +230,7 @@ void DispatcherBase::SetUpdateRate(double interval) { m_update_rate = static_cast(interval * 1000); } -void DispatcherBase::SetIdentity(const Twine& name) { +void DispatcherBase::SetIdentity(const wpi::Twine& name) { std::scoped_lock lock(m_user_mutex); m_identity = name.str(); } diff --git a/ntcore/src/main/native/cpp/Dispatcher.h b/ntcore/src/main/native/cpp/Dispatcher.h index f5767d04dc..020f737a17 100644 --- a/ntcore/src/main/native/cpp/Dispatcher.h +++ b/ntcore/src/main/native/cpp/Dispatcher.h @@ -45,12 +45,12 @@ class DispatcherBase : public IDispatcher { unsigned int GetNetworkMode() const; void StartLocal(); - void StartServer(const Twine& persist_filename, + void StartServer(const wpi::Twine& persist_filename, std::unique_ptr acceptor); void StartClient(); void Stop(); void SetUpdateRate(double interval); - void SetIdentity(const Twine& name); + void SetIdentity(const wpi::Twine& name); void Flush(); std::vector GetConnections() const; bool IsConnected() const; @@ -132,11 +132,12 @@ class Dispatcher : public DispatcherBase { wpi::Logger& logger) : DispatcherBase(storage, notifier, logger) {} - void StartServer(const Twine& persist_filename, const char* listen_address, - unsigned int port); + void StartServer(const wpi::Twine& persist_filename, + const char* listen_address, unsigned int port); void SetServer(const char* server_name, unsigned int port); - void SetServer(ArrayRef> servers); + void SetServer( + wpi::ArrayRef> servers); void SetServerTeam(unsigned int team, unsigned int port); void SetServerOverride(const char* server_name, unsigned int port); diff --git a/ntcore/src/main/native/cpp/EntryNotifier.cpp b/ntcore/src/main/native/cpp/EntryNotifier.cpp index 81861dfd98..fb05cd9470 100644 --- a/ntcore/src/main/native/cpp/EntryNotifier.cpp +++ b/ntcore/src/main/native/cpp/EntryNotifier.cpp @@ -59,7 +59,7 @@ bool impl::EntryNotifierThread::Matches(const EntryListenerData& listener, unsigned int EntryNotifier::Add( std::function callback, - StringRef prefix, unsigned int flags) { + wpi::StringRef prefix, unsigned int flags) { if ((flags & NT_NOTIFY_LOCAL) != 0) { m_local_notifiers = true; } @@ -93,7 +93,7 @@ unsigned int EntryNotifier::AddPolled(unsigned int poller_uid, return DoAdd(poller_uid, Handle(m_inst, local_id, Handle::kEntry), flags); } -void EntryNotifier::NotifyEntry(unsigned int local_id, StringRef name, +void EntryNotifier::NotifyEntry(unsigned int local_id, wpi::StringRef name, std::shared_ptr value, unsigned int flags, unsigned int only_listener) { diff --git a/ntcore/src/main/native/cpp/EntryNotifier.h b/ntcore/src/main/native/cpp/EntryNotifier.h index c80fc627d0..bfc77f7025 100644 --- a/ntcore/src/main/native/cpp/EntryNotifier.h +++ b/ntcore/src/main/native/cpp/EntryNotifier.h @@ -29,13 +29,13 @@ struct EntryListenerData EntryListenerData() = default; EntryListenerData( std::function callback_, - StringRef prefix_, unsigned int flags_) + wpi::StringRef prefix_, unsigned int flags_) : CallbackListenerData(callback_), prefix(prefix_), flags(flags_) {} EntryListenerData( std::function callback_, NT_Entry entry_, unsigned int flags_) : CallbackListenerData(callback_), entry(entry_), flags(flags_) {} - EntryListenerData(unsigned int poller_uid_, StringRef prefix_, + EntryListenerData(unsigned int poller_uid_, wpi::StringRef prefix_, unsigned int flags_) : CallbackListenerData(poller_uid_), prefix(prefix_), flags(flags_) {} EntryListenerData(unsigned int poller_uid_, NT_Entry entry_, @@ -93,7 +93,7 @@ class EntryNotifier unsigned int AddPolled(unsigned int poller_uid, unsigned int local_id, unsigned int flags) override; - void NotifyEntry(unsigned int local_id, StringRef name, + void NotifyEntry(unsigned int local_id, wpi::StringRef name, std::shared_ptr value, unsigned int flags, unsigned int only_listener = UINT_MAX) override; diff --git a/ntcore/src/main/native/cpp/IEntryNotifier.h b/ntcore/src/main/native/cpp/IEntryNotifier.h index 031621ff4b..bfc6da34b9 100644 --- a/ntcore/src/main/native/cpp/IEntryNotifier.h +++ b/ntcore/src/main/native/cpp/IEntryNotifier.h @@ -31,7 +31,7 @@ class IEntryNotifier { virtual unsigned int AddPolled(unsigned int poller_uid, unsigned int local_id, unsigned int flags) = 0; - virtual void NotifyEntry(unsigned int local_id, StringRef name, + virtual void NotifyEntry(unsigned int local_id, wpi::StringRef name, std::shared_ptr value, unsigned int flags, unsigned int only_listener = UINT_MAX) = 0; }; diff --git a/ntcore/src/main/native/cpp/IRpcServer.h b/ntcore/src/main/native/cpp/IRpcServer.h index 4d90a3751d..1abce862f4 100644 --- a/ntcore/src/main/native/cpp/IRpcServer.h +++ b/ntcore/src/main/native/cpp/IRpcServer.h @@ -14,7 +14,7 @@ namespace nt { class IRpcServer { public: - typedef std::function SendResponseFunc; + typedef std::function SendResponseFunc; IRpcServer() = default; IRpcServer(const IRpcServer&) = delete; @@ -24,7 +24,7 @@ class IRpcServer { virtual void RemoveRpc(unsigned int rpc_uid) = 0; virtual void ProcessRpc(unsigned int local_id, unsigned int call_uid, - StringRef name, StringRef params, + wpi::StringRef name, wpi::StringRef params, const ConnectionInfo& conn, SendResponseFunc send_response, unsigned int rpc_uid) = 0; diff --git a/ntcore/src/main/native/cpp/IStorage.h b/ntcore/src/main/native/cpp/IStorage.h index e4c97b52e5..47b3b50b01 100644 --- a/ntcore/src/main/native/cpp/IStorage.h +++ b/ntcore/src/main/native/cpp/IStorage.h @@ -50,10 +50,10 @@ class IStorage { // Filename-based save/load functions. Used both by periodic saves and // accessible directly via the user API. - virtual const char* SavePersistent(const Twine& filename, + virtual const char* SavePersistent(const wpi::Twine& filename, bool periodic) const = 0; virtual const char* LoadPersistent( - const Twine& filename, + const wpi::Twine& filename, std::function warn) = 0; }; diff --git a/ntcore/src/main/native/cpp/NetworkConnection.cpp b/ntcore/src/main/native/cpp/NetworkConnection.cpp index 85957e2318..84c8d981e8 100644 --- a/ntcore/src/main/native/cpp/NetworkConnection.cpp +++ b/ntcore/src/main/native/cpp/NetworkConnection.cpp @@ -142,7 +142,7 @@ std::string NetworkConnection::remote_id() const { return m_remote_id; } -void NetworkConnection::set_remote_id(StringRef remote_id) { +void NetworkConnection::set_remote_id(wpi::StringRef remote_id) { std::scoped_lock lock(m_remote_id_mutex); m_remote_id = remote_id; } diff --git a/ntcore/src/main/native/cpp/NetworkConnection.h b/ntcore/src/main/native/cpp/NetworkConnection.h index c7c659993e..69015054c0 100644 --- a/ntcore/src/main/native/cpp/NetworkConnection.h +++ b/ntcore/src/main/native/cpp/NetworkConnection.h @@ -76,7 +76,7 @@ class NetworkConnection : public INetworkConnection { void set_state(State state) final; std::string remote_id() const; - void set_remote_id(StringRef remote_id); + void set_remote_id(wpi::StringRef remote_id); uint64_t last_update() const { return m_last_update; } diff --git a/ntcore/src/main/native/cpp/RpcServer.cpp b/ntcore/src/main/native/cpp/RpcServer.cpp index d413cd8438..88a53b796d 100644 --- a/ntcore/src/main/native/cpp/RpcServer.cpp +++ b/ntcore/src/main/native/cpp/RpcServer.cpp @@ -27,7 +27,7 @@ void RpcServer::RemoveRpc(unsigned int rpc_uid) { } void RpcServer::ProcessRpc(unsigned int local_id, unsigned int call_uid, - StringRef name, StringRef params, + wpi::StringRef name, wpi::StringRef params, const ConnectionInfo& conn, SendResponseFunc send_response, unsigned int rpc_uid) { diff --git a/ntcore/src/main/native/cpp/RpcServer.h b/ntcore/src/main/native/cpp/RpcServer.h index 4d20c50686..e4d1b1d63d 100644 --- a/ntcore/src/main/native/cpp/RpcServer.h +++ b/ntcore/src/main/native/cpp/RpcServer.h @@ -22,8 +22,8 @@ namespace impl { typedef std::pair RpcIdPair; struct RpcNotifierData : public RpcAnswer { - RpcNotifierData(NT_Entry entry_, NT_RpcCall call_, StringRef name_, - StringRef params_, const ConnectionInfo& conn_, + RpcNotifierData(NT_Entry entry_, NT_RpcCall call_, wpi::StringRef name_, + wpi::StringRef params_, const ConnectionInfo& conn_, IRpcServer::SendResponseFunc send_response_) : RpcAnswer{entry_, call_, name_, params_, conn_}, send_response{std::move(send_response_)} {} @@ -93,9 +93,9 @@ class RpcServer unsigned int AddPolled(unsigned int poller_uid); void RemoveRpc(unsigned int rpc_uid) override; - void ProcessRpc(unsigned int local_id, unsigned int call_uid, StringRef name, - StringRef params, const ConnectionInfo& conn, - SendResponseFunc send_response, + void ProcessRpc(unsigned int local_id, unsigned int call_uid, + wpi::StringRef name, wpi::StringRef params, + const ConnectionInfo& conn, SendResponseFunc send_response, unsigned int rpc_uid) override; bool PostRpcResponse(unsigned int local_id, unsigned int call_uid, diff --git a/ntcore/src/main/native/cpp/Storage.cpp b/ntcore/src/main/native/cpp/Storage.cpp index 63929fc175..f666976053 100644 --- a/ntcore/src/main/native/cpp/Storage.cpp +++ b/ntcore/src/main/native/cpp/Storage.cpp @@ -91,7 +91,7 @@ void Storage::ProcessIncomingEntryAssign(std::shared_ptr msg, INetworkConnection* conn) { std::unique_lock lock(m_mutex); unsigned int id = msg->id(); - StringRef name = msg->str(); + wpi::StringRef name = msg->str(); Entry* entry; bool may_need_update = false; SequenceNumber seq_num(msg->seq_num_uid()); @@ -359,7 +359,7 @@ void Storage::ProcessIncomingExecuteRpc( unsigned int call_uid = msg->seq_num_uid(); m_rpc_server.ProcessRpc( entry->local_id, call_uid, entry->name, msg->str(), conn_info, - [=](StringRef result) { + [=](wpi::StringRef result) { auto c = conn_weak.lock(); if (c) { c->QueueOutgoing(Message::RpcResponse(id, call_uid, result)); @@ -442,7 +442,7 @@ void Storage::ApplyInitialAssignments( } SequenceNumber seq_num(msg->seq_num_uid()); - StringRef name = msg->str(); + wpi::StringRef name = msg->str(); Entry* entry = GetOrNew(name); entry->seq_num = seq_num; @@ -509,7 +509,7 @@ void Storage::ApplyInitialAssignments( } } -std::shared_ptr Storage::GetEntryValue(StringRef name) const { +std::shared_ptr Storage::GetEntryValue(wpi::StringRef name) const { std::scoped_lock lock(m_mutex); auto i = m_entries.find(name); if (i == m_entries.end()) { @@ -526,7 +526,7 @@ std::shared_ptr Storage::GetEntryValue(unsigned int local_id) const { return m_localmap[local_id]->value; } -bool Storage::SetDefaultEntryValue(StringRef name, +bool Storage::SetDefaultEntryValue(wpi::StringRef name, std::shared_ptr value) { if (name.empty()) { return false; @@ -566,7 +566,7 @@ bool Storage::SetDefaultEntryValue(unsigned int local_id, return true; } -bool Storage::SetEntryValue(StringRef name, std::shared_ptr value) { +bool Storage::SetEntryValue(wpi::StringRef name, std::shared_ptr value) { if (name.empty()) { return true; } @@ -664,7 +664,8 @@ void Storage::SetEntryValueImpl(Entry* entry, std::shared_ptr value, } } -void Storage::SetEntryTypeValue(StringRef name, std::shared_ptr value) { +void Storage::SetEntryTypeValue(wpi::StringRef name, + std::shared_ptr value) { if (name.empty()) { return; } @@ -694,7 +695,7 @@ void Storage::SetEntryTypeValue(unsigned int local_id, SetEntryValueImpl(entry, value, lock, true); } -void Storage::SetEntryFlags(StringRef name, unsigned int flags) { +void Storage::SetEntryFlags(wpi::StringRef name, unsigned int flags) { if (name.empty()) { return; } @@ -746,7 +747,7 @@ void Storage::SetEntryFlagsImpl(Entry* entry, unsigned int flags, } } -unsigned int Storage::GetEntryFlags(StringRef name) const { +unsigned int Storage::GetEntryFlags(wpi::StringRef name) const { std::scoped_lock lock(m_mutex); auto i = m_entries.find(name); if (i == m_entries.end()) { @@ -763,7 +764,7 @@ unsigned int Storage::GetEntryFlags(unsigned int local_id) const { return m_localmap[local_id]->flags; } -void Storage::DeleteEntry(StringRef name) { +void Storage::DeleteEntry(wpi::StringRef name) { std::unique_lock lock(m_mutex); auto i = m_entries.find(name); if (i == m_entries.end()) { @@ -872,9 +873,9 @@ void Storage::DeleteAllEntries() { dispatcher->QueueOutgoing(Message::ClearEntries(), nullptr, nullptr); } -Storage::Entry* Storage::GetOrNew(const Twine& name) { +Storage::Entry* Storage::GetOrNew(const wpi::Twine& name) { wpi::SmallString<128> nameBuf; - StringRef nameStr = name.toStringRef(nameBuf); + wpi::StringRef nameStr = name.toStringRef(nameBuf); auto& entry = m_entries[nameStr]; if (!entry) { m_localmap.emplace_back(new Entry(nameStr)); @@ -884,7 +885,7 @@ Storage::Entry* Storage::GetOrNew(const Twine& name) { return entry; } -unsigned int Storage::GetEntry(const Twine& name) { +unsigned int Storage::GetEntry(const wpi::Twine& name) { if (name.isTriviallyEmpty() || (name.isSingleStringRef() && name.getSingleStringRef().empty())) { return UINT_MAX; @@ -893,10 +894,10 @@ unsigned int Storage::GetEntry(const Twine& name) { return GetOrNew(name)->local_id; } -std::vector Storage::GetEntries(const Twine& prefix, +std::vector Storage::GetEntries(const wpi::Twine& prefix, unsigned int types) { wpi::SmallString<128> prefixBuf; - StringRef prefixStr = prefix.toStringRef(prefixBuf); + wpi::StringRef prefixStr = prefix.toStringRef(prefixBuf); std::scoped_lock lock(m_mutex); std::vector ids; for (auto& i : m_entries) { @@ -969,10 +970,10 @@ uint64_t Storage::GetEntryLastChange(unsigned int local_id) const { return entry->value->last_change(); } -std::vector Storage::GetEntryInfo(int inst, const Twine& prefix, +std::vector Storage::GetEntryInfo(int inst, const wpi::Twine& prefix, unsigned int types) { wpi::SmallString<128> prefixBuf; - StringRef prefixStr = prefix.toStringRef(prefixBuf); + wpi::StringRef prefixStr = prefix.toStringRef(prefixBuf); std::scoped_lock lock(m_mutex); std::vector infos; for (auto& i : m_entries) { @@ -996,11 +997,11 @@ std::vector Storage::GetEntryInfo(int inst, const Twine& prefix, } unsigned int Storage::AddListener( - const Twine& prefix, + const wpi::Twine& prefix, std::function callback, unsigned int flags) const { wpi::SmallString<128> prefixBuf; - StringRef prefixStr = prefix.toStringRef(prefixBuf); + wpi::StringRef prefixStr = prefix.toStringRef(prefixBuf); std::scoped_lock lock(m_mutex); unsigned int uid = m_notifier.Add(callback, prefixStr, flags); // perform immediate notifications @@ -1036,10 +1037,10 @@ unsigned int Storage::AddListener( } unsigned int Storage::AddPolledListener(unsigned int poller, - const Twine& prefix, + const wpi::Twine& prefix, unsigned int flags) const { wpi::SmallString<128> prefixBuf; - StringRef prefixStr = prefix.toStringRef(prefixBuf); + wpi::StringRef prefixStr = prefix.toStringRef(prefixBuf); std::scoped_lock lock(m_mutex); unsigned int uid = m_notifier.AddPolled(poller, prefixStr, flags); // perform immediate notifications @@ -1110,11 +1111,11 @@ bool Storage::GetPersistentEntries( } bool Storage::GetEntries( - const Twine& prefix, + const wpi::Twine& prefix, std::vector>>* entries) const { wpi::SmallString<128> prefixBuf; - StringRef prefixStr = prefix.toStringRef(prefixBuf); + wpi::StringRef prefixStr = prefix.toStringRef(prefixBuf); // copy values out of storage as quickly as possible so lock isn't held { std::scoped_lock lock(m_mutex); @@ -1138,7 +1139,7 @@ bool Storage::GetEntries( return true; } -void Storage::CreateRpc(unsigned int local_id, StringRef def, +void Storage::CreateRpc(unsigned int local_id, wpi::StringRef def, unsigned int rpc_uid) { std::unique_lock lock(m_mutex); if (local_id >= m_localmap.size()) { @@ -1183,7 +1184,7 @@ void Storage::CreateRpc(unsigned int local_id, StringRef def, } } -unsigned int Storage::CallRpc(unsigned int local_id, StringRef params) { +unsigned int Storage::CallRpc(unsigned int local_id, wpi::StringRef params) { std::unique_lock lock(m_mutex); if (local_id >= m_localmap.size()) { return 0; @@ -1201,7 +1202,7 @@ unsigned int Storage::CallRpc(unsigned int local_id, StringRef params) { unsigned int call_uid = entry->rpc_call_uid; auto msg = Message::ExecuteRpc(entry->id, call_uid, params); - StringRef name{entry->name}; + wpi::StringRef name{entry->name}; if (m_server) { // RPCs are unlikely to be used locally on the server, but handle it @@ -1217,7 +1218,7 @@ unsigned int Storage::CallRpc(unsigned int local_id, StringRef params) { unsigned int call_uid = msg->seq_num_uid(); m_rpc_server.ProcessRpc( local_id, call_uid, name, msg->str(), conn_info, - [=](StringRef result) { + [=](wpi::StringRef result) { std::scoped_lock lock(m_mutex); m_rpc_results.insert( std::make_pair(RpcIdPair{local_id, call_uid}, result)); diff --git a/ntcore/src/main/native/cpp/Storage.h b/ntcore/src/main/native/cpp/Storage.h index 73bc0c617c..96968b4573 100644 --- a/ntcore/src/main/native/cpp/Storage.h +++ b/ntcore/src/main/native/cpp/Storage.h @@ -72,35 +72,35 @@ class Storage : public IStorage { // User functions. These are the actual implementations of the corresponding // user API functions in ntcore_cpp. - std::shared_ptr GetEntryValue(StringRef name) const; + std::shared_ptr GetEntryValue(wpi::StringRef name) const; std::shared_ptr GetEntryValue(unsigned int local_id) const; - bool SetDefaultEntryValue(StringRef name, std::shared_ptr value); + bool SetDefaultEntryValue(wpi::StringRef name, std::shared_ptr value); bool SetDefaultEntryValue(unsigned int local_id, std::shared_ptr value); - bool SetEntryValue(StringRef name, std::shared_ptr value); + bool SetEntryValue(wpi::StringRef name, std::shared_ptr value); bool SetEntryValue(unsigned int local_id, std::shared_ptr value); - void SetEntryTypeValue(StringRef name, std::shared_ptr value); + void SetEntryTypeValue(wpi::StringRef name, std::shared_ptr value); void SetEntryTypeValue(unsigned int local_id, std::shared_ptr value); - void SetEntryFlags(StringRef name, unsigned int flags); + void SetEntryFlags(wpi::StringRef name, unsigned int flags); void SetEntryFlags(unsigned int local_id, unsigned int flags); - unsigned int GetEntryFlags(StringRef name) const; + unsigned int GetEntryFlags(wpi::StringRef name) const; unsigned int GetEntryFlags(unsigned int local_id) const; - void DeleteEntry(StringRef name); + void DeleteEntry(wpi::StringRef name); void DeleteEntry(unsigned int local_id); void DeleteAllEntries(); - std::vector GetEntryInfo(int inst, const Twine& prefix, + std::vector GetEntryInfo(int inst, const wpi::Twine& prefix, unsigned int types); unsigned int AddListener( - const Twine& prefix, + const wpi::Twine& prefix, std::function callback, unsigned int flags) const; unsigned int AddListener( @@ -108,14 +108,16 @@ class Storage : public IStorage { std::function callback, unsigned int flags) const; - unsigned int AddPolledListener(unsigned int poller_uid, const Twine& prefix, + unsigned int AddPolledListener(unsigned int poller_uid, + const wpi::Twine& prefix, unsigned int flags) const; unsigned int AddPolledListener(unsigned int poller_uid, unsigned int local_id, unsigned int flags) const; // Index-only - unsigned int GetEntry(const Twine& name); - std::vector GetEntries(const Twine& prefix, unsigned int types); + unsigned int GetEntry(const wpi::Twine& name); + std::vector GetEntries(const wpi::Twine& prefix, + unsigned int types); EntryInfo GetEntryInfo(int inst, unsigned int local_id) const; std::string GetEntryName(unsigned int local_id) const; NT_Type GetEntryType(unsigned int local_id) const; @@ -123,29 +125,32 @@ class Storage : public IStorage { // Filename-based save/load functions. Used both by periodic saves and // accessible directly via the user API. - const char* SavePersistent(const Twine& filename, + const char* SavePersistent(const wpi::Twine& filename, bool periodic) const override; const char* LoadPersistent( - const Twine& filename, + const wpi::Twine& filename, std::function warn) override; - const char* SaveEntries(const Twine& filename, const Twine& prefix) const; + const char* SaveEntries(const wpi::Twine& filename, + const wpi::Twine& prefix) const; const char* LoadEntries( - const Twine& filename, const Twine& prefix, + const wpi::Twine& filename, const wpi::Twine& prefix, std::function warn); // Stream-based save/load functions (exposed for testing purposes). These // implement the guts of the filename-based functions. void SavePersistent(wpi::raw_ostream& os, bool periodic) const; - bool LoadEntries(wpi::raw_istream& is, const Twine& prefix, bool persistent, + bool LoadEntries(wpi::raw_istream& is, const wpi::Twine& prefix, + bool persistent, std::function warn); - void SaveEntries(wpi::raw_ostream& os, const Twine& prefix) const; + void SaveEntries(wpi::raw_ostream& os, const wpi::Twine& prefix) const; // RPC configuration needs to come through here as RPC definitions are // actually special Storage value types. - void CreateRpc(unsigned int local_id, StringRef def, unsigned int rpc_uid); - unsigned int CallRpc(unsigned int local_id, StringRef params); + void CreateRpc(unsigned int local_id, wpi::StringRef def, + unsigned int rpc_uid); + unsigned int CallRpc(unsigned int local_id, wpi::StringRef params); bool GetRpcResult(unsigned int local_id, unsigned int call_uid, std::string* result); bool GetRpcResult(unsigned int local_id, unsigned int call_uid, @@ -237,7 +242,7 @@ class Storage : public IStorage { bool periodic, std::vector>>* entries) const; - bool GetEntries(const Twine& prefix, + bool GetEntries(const wpi::Twine& prefix, std::vector>>* entries) const; void SetEntryValueImpl(Entry* entry, std::shared_ptr value, @@ -251,7 +256,7 @@ class Storage : public IStorage { template void DeleteAllEntriesImpl(bool local, F should_delete); void DeleteAllEntriesImpl(bool local); - Entry* GetOrNew(const Twine& name); + Entry* GetOrNew(const wpi::Twine& name); }; } // namespace nt diff --git a/ntcore/src/main/native/cpp/Storage_load.cpp b/ntcore/src/main/native/cpp/Storage_load.cpp index 956321d60f..04904aaa03 100644 --- a/ntcore/src/main/native/cpp/Storage_load.cpp +++ b/ntcore/src/main/native/cpp/Storage_load.cpp @@ -27,7 +27,7 @@ class LoadPersistentImpl { LoadPersistentImpl(wpi::raw_istream& is, WarnFunc warn) : m_is(is), m_warn(std::move(warn)) {} - bool Load(StringRef prefix, std::vector* entries); + bool Load(wpi::StringRef prefix, std::vector* entries); private: bool ReadLine(); @@ -138,7 +138,8 @@ static wpi::StringRef UnescapeString(wpi::StringRef source, return wpi::StringRef{buf.data(), buf.size()}; } -bool LoadPersistentImpl::Load(StringRef prefix, std::vector* entries) { +bool LoadPersistentImpl::Load(wpi::StringRef prefix, + std::vector* entries) { if (!ReadHeader()) { return false; // header } @@ -373,10 +374,10 @@ std::shared_ptr LoadPersistentImpl::ReadStringArrayValue() { } bool Storage::LoadEntries( - wpi::raw_istream& is, const Twine& prefix, bool persistent, + wpi::raw_istream& is, const wpi::Twine& prefix, bool persistent, std::function warn) { wpi::SmallString<128> prefixBuf; - StringRef prefixStr = prefix.toStringRef(prefixBuf); + wpi::StringRef prefixStr = prefix.toStringRef(prefixBuf); // entries to add std::vector entries; @@ -456,7 +457,7 @@ bool Storage::LoadEntries( } const char* Storage::LoadPersistent( - const Twine& filename, + const wpi::Twine& filename, std::function warn) { std::error_code ec; wpi::raw_fd_istream is(filename, ec); @@ -470,7 +471,7 @@ const char* Storage::LoadPersistent( } const char* Storage::LoadEntries( - const Twine& filename, const Twine& prefix, + const wpi::Twine& filename, const wpi::Twine& prefix, std::function warn) { std::error_code ec; wpi::raw_fd_istream is(filename, ec); diff --git a/ntcore/src/main/native/cpp/Storage_save.cpp b/ntcore/src/main/native/cpp/Storage_save.cpp index 113cd4a5ba..e6eff1862d 100644 --- a/ntcore/src/main/native/cpp/Storage_save.cpp +++ b/ntcore/src/main/native/cpp/Storage_save.cpp @@ -190,7 +190,7 @@ void Storage::SavePersistent(wpi::raw_ostream& os, bool periodic) const { SavePersistentImpl(os).Save(entries); } -const char* Storage::SavePersistent(const Twine& filename, +const char* Storage::SavePersistent(const wpi::Twine& filename, bool periodic) const { wpi::SmallString<128> fn; filename.toVector(fn); @@ -240,7 +240,8 @@ done: return err; } -void Storage::SaveEntries(wpi::raw_ostream& os, const Twine& prefix) const { +void Storage::SaveEntries(wpi::raw_ostream& os, + const wpi::Twine& prefix) const { std::vector entries; if (!GetEntries(prefix, &entries)) { return; @@ -248,8 +249,8 @@ void Storage::SaveEntries(wpi::raw_ostream& os, const Twine& prefix) const { SavePersistentImpl(os).Save(entries); } -const char* Storage::SaveEntries(const Twine& filename, - const Twine& prefix) const { +const char* Storage::SaveEntries(const wpi::Twine& filename, + const wpi::Twine& prefix) const { wpi::SmallString<128> fn; filename.toVector(fn); wpi::SmallString<128> tmp = fn; diff --git a/ntcore/src/main/native/cpp/jni/NetworkTablesJNI.cpp b/ntcore/src/main/native/cpp/jni/NetworkTablesJNI.cpp index f28adeca4e..75f9c713eb 100644 --- a/ntcore/src/main/native/cpp/jni/NetworkTablesJNI.cpp +++ b/ntcore/src/main/native/cpp/jni/NetworkTablesJNI.cpp @@ -1549,7 +1549,7 @@ Java_edu_wpi_first_networktables_NetworkTablesJNI_startClient__I_3Ljava_lang_Str } std::vector names; - std::vector> servers; + std::vector> servers; names.reserve(len); servers.reserve(len); for (int i = 0; i < len; ++i) { @@ -1561,7 +1561,7 @@ Java_edu_wpi_first_networktables_NetworkTablesJNI_startClient__I_3Ljava_lang_Str } names.emplace_back(JStringRef{env, elem}.str()); servers.emplace_back( - std::make_pair(nt::StringRef(names.back()), portInts[i])); + std::make_pair(wpi::StringRef(names.back()), portInts[i])); } env->ReleaseIntArrayElements(ports, portInts, JNI_ABORT); nt::StartClient(inst, servers); @@ -1636,7 +1636,7 @@ Java_edu_wpi_first_networktables_NetworkTablesJNI_setServer__I_3Ljava_lang_Strin } std::vector names; - std::vector> servers; + std::vector> servers; names.reserve(len); servers.reserve(len); for (int i = 0; i < len; ++i) { @@ -1648,7 +1648,7 @@ Java_edu_wpi_first_networktables_NetworkTablesJNI_setServer__I_3Ljava_lang_Strin } names.emplace_back(JStringRef{env, elem}.str()); servers.emplace_back( - std::make_pair(nt::StringRef(names.back()), portInts[i])); + std::make_pair(wpi::StringRef(names.back()), portInts[i])); } env->ReleaseIntArrayElements(ports, portInts, JNI_ABORT); nt::SetServer(inst, servers); diff --git a/ntcore/src/main/native/cpp/networktables/NetworkTable.cpp b/ntcore/src/main/native/cpp/networktables/NetworkTable.cpp index 5178b2ad4b..d4839009ee 100644 --- a/ntcore/src/main/native/cpp/networktables/NetworkTable.cpp +++ b/ntcore/src/main/native/cpp/networktables/NetworkTable.cpp @@ -15,31 +15,31 @@ using namespace nt; -StringRef NetworkTable::BasenameKey(StringRef key) { +wpi::StringRef NetworkTable::BasenameKey(wpi::StringRef key) { size_t slash = key.rfind(PATH_SEPARATOR_CHAR); - if (slash == StringRef::npos) { + if (slash == wpi::StringRef::npos) { return key; } return key.substr(slash + 1); } -std::string NetworkTable::NormalizeKey(const Twine& key, +std::string NetworkTable::NormalizeKey(const wpi::Twine& key, bool withLeadingSlash) { wpi::SmallString<128> buf; return NormalizeKey(key, buf, withLeadingSlash); } -StringRef NetworkTable::NormalizeKey(const Twine& key, - wpi::SmallVectorImpl& buf, - bool withLeadingSlash) { +wpi::StringRef NetworkTable::NormalizeKey(const wpi::Twine& key, + wpi::SmallVectorImpl& buf, + bool withLeadingSlash) { buf.clear(); if (withLeadingSlash) { buf.push_back(PATH_SEPARATOR_CHAR); } // for each path element, add it with a slash following wpi::SmallString<128> keyBuf; - StringRef keyStr = key.toStringRef(keyBuf); - wpi::SmallVector parts; + wpi::StringRef keyStr = key.toStringRef(keyBuf); + wpi::SmallVector parts; keyStr.split(parts, PATH_SEPARATOR_CHAR, -1, false); for (auto i = parts.begin(); i != parts.end(); ++i) { buf.append(i->begin(), i->end()); @@ -49,17 +49,17 @@ StringRef NetworkTable::NormalizeKey(const Twine& key, if (!keyStr.empty() && keyStr.back() != PATH_SEPARATOR_CHAR) { buf.pop_back(); } - return StringRef(buf.data(), buf.size()); + return wpi::StringRef(buf.data(), buf.size()); } -std::vector NetworkTable::GetHierarchy(const Twine& key) { +std::vector NetworkTable::GetHierarchy(const wpi::Twine& key) { std::vector hierarchy; hierarchy.emplace_back(1, PATH_SEPARATOR_CHAR); // for each path element, add it to the end of what we built previously wpi::SmallString<128> keyBuf; - StringRef keyStr = key.toStringRef(keyBuf); + wpi::StringRef keyStr = key.toStringRef(keyBuf); wpi::SmallString<128> path; - wpi::SmallVector parts; + wpi::SmallVector parts; keyStr.split(parts, PATH_SEPARATOR_CHAR, -1, false); if (!parts.empty()) { for (auto i = parts.begin(); i != parts.end(); ++i) { @@ -76,7 +76,8 @@ std::vector NetworkTable::GetHierarchy(const Twine& key) { return hierarchy; } -NetworkTable::NetworkTable(NT_Inst inst, const Twine& path, const private_init&) +NetworkTable::NetworkTable(NT_Inst inst, const wpi::Twine& path, + const private_init&) : m_inst(inst), m_path(path.str()) {} NetworkTable::~NetworkTable() { @@ -89,13 +90,14 @@ NetworkTableInstance NetworkTable::GetInstance() const { return NetworkTableInstance{m_inst}; } -NetworkTableEntry NetworkTable::GetEntry(const Twine& key) const { +NetworkTableEntry NetworkTable::GetEntry(const wpi::Twine& key) const { wpi::SmallString<128> keyBuf; - StringRef keyStr = key.toStringRef(keyBuf); + wpi::StringRef keyStr = key.toStringRef(keyBuf); std::scoped_lock lock(m_mutex); NT_Entry& entry = m_entries[keyStr]; if (entry == 0) { - entry = nt::GetEntry(m_inst, m_path + Twine(PATH_SEPARATOR_CHAR) + keyStr); + entry = + nt::GetEntry(m_inst, m_path + wpi::Twine(PATH_SEPARATOR_CHAR) + keyStr); } return NetworkTableEntry{entry}; } @@ -104,10 +106,10 @@ NT_EntryListener NetworkTable::AddEntryListener(TableEntryListener listener, unsigned int flags) const { size_t prefix_len = m_path.size() + 1; return nt::AddEntryListener( - m_inst, m_path + Twine(PATH_SEPARATOR_CHAR), + m_inst, m_path + wpi::Twine(PATH_SEPARATOR_CHAR), [=](const EntryNotification& event) { - StringRef relative_key = event.name.substr(prefix_len); - if (relative_key.find(PATH_SEPARATOR_CHAR) != StringRef::npos) { + wpi::StringRef relative_key = event.name.substr(prefix_len); + if (relative_key.find(PATH_SEPARATOR_CHAR) != wpi::StringRef::npos) { return; } listener(const_cast(this), relative_key, @@ -116,7 +118,7 @@ NT_EntryListener NetworkTable::AddEntryListener(TableEntryListener listener, flags); } -NT_EntryListener NetworkTable::AddEntryListener(const Twine& key, +NT_EntryListener NetworkTable::AddEntryListener(const wpi::Twine& key, TableEntryListener listener, unsigned int flags) const { size_t prefix_len = m_path.size() + 1; @@ -147,14 +149,14 @@ NT_EntryListener NetworkTable::AddSubTableListener(TableListener listener, flags |= NT_NOTIFY_LOCAL; } NT_EntryListener id = nt::AddEntryListener( - m_inst, m_path + Twine(PATH_SEPARATOR_CHAR), + m_inst, m_path + wpi::Twine(PATH_SEPARATOR_CHAR), [=](const EntryNotification& event) { - StringRef relative_key = event.name.substr(prefix_len); + wpi::StringRef relative_key = event.name.substr(prefix_len); auto end_sub_table = relative_key.find(PATH_SEPARATOR_CHAR); - if (end_sub_table == StringRef::npos) { + if (end_sub_table == wpi::StringRef::npos) { return; } - StringRef sub_table_key = relative_key.substr(0, end_sub_table); + wpi::StringRef sub_table_key = relative_key.substr(0, end_sub_table); if (notified_tables->find(sub_table_key) == notified_tables->end()) { return; } @@ -174,12 +176,12 @@ void NetworkTable::RemoveTableListener(NT_EntryListener listener) { } std::shared_ptr NetworkTable::GetSubTable( - const Twine& key) const { + const wpi::Twine& key) const { return std::make_shared( - m_inst, m_path + Twine(PATH_SEPARATOR_CHAR) + key, private_init{}); + m_inst, m_path + wpi::Twine(PATH_SEPARATOR_CHAR) + key, private_init{}); } -bool NetworkTable::ContainsKey(const Twine& key) const { +bool NetworkTable::ContainsKey(const wpi::Twine& key) const { if (key.isTriviallyEmpty() || (key.isSingleStringRef() && key.getSingleStringRef().empty())) { return false; @@ -187,10 +189,10 @@ bool NetworkTable::ContainsKey(const Twine& key) const { return GetEntry(key).Exists(); } -bool NetworkTable::ContainsSubTable(const Twine& key) const { +bool NetworkTable::ContainsSubTable(const wpi::Twine& key) const { return !GetEntryInfo(m_inst, - m_path + Twine(PATH_SEPARATOR_CHAR) + key + - Twine(PATH_SEPARATOR_CHAR), + m_path + wpi::Twine(PATH_SEPARATOR_CHAR) + key + + wpi::Twine(PATH_SEPARATOR_CHAR), 0) .empty(); } @@ -198,11 +200,12 @@ bool NetworkTable::ContainsSubTable(const Twine& key) const { std::vector NetworkTable::GetKeys(int types) const { std::vector keys; size_t prefix_len = m_path.size() + 1; - auto infos = GetEntryInfo(m_inst, m_path + Twine(PATH_SEPARATOR_CHAR), types); + auto infos = + GetEntryInfo(m_inst, m_path + wpi::Twine(PATH_SEPARATOR_CHAR), types); std::scoped_lock lock(m_mutex); for (auto& info : infos) { - auto relative_key = StringRef(info.name).substr(prefix_len); - if (relative_key.find(PATH_SEPARATOR_CHAR) != StringRef::npos) { + auto relative_key = wpi::StringRef(info.name).substr(prefix_len); + if (relative_key.find(PATH_SEPARATOR_CHAR) != wpi::StringRef::npos) { continue; } keys.push_back(relative_key); @@ -215,10 +218,10 @@ std::vector NetworkTable::GetSubTables() const { std::vector keys; size_t prefix_len = m_path.size() + 1; for (auto& entry : - GetEntryInfo(m_inst, m_path + Twine(PATH_SEPARATOR_CHAR), 0)) { - auto relative_key = StringRef(entry.name).substr(prefix_len); + GetEntryInfo(m_inst, m_path + wpi::Twine(PATH_SEPARATOR_CHAR), 0)) { + auto relative_key = wpi::StringRef(entry.name).substr(prefix_len); size_t end_subtable = relative_key.find(PATH_SEPARATOR_CHAR); - if (end_subtable == StringRef::npos) { + if (end_subtable == wpi::StringRef::npos) { continue; } keys.push_back(relative_key.substr(0, end_subtable)); @@ -226,149 +229,157 @@ std::vector NetworkTable::GetSubTables() const { return keys; } -void NetworkTable::SetPersistent(StringRef key) { +void NetworkTable::SetPersistent(wpi::StringRef key) { GetEntry(key).SetPersistent(); } -void NetworkTable::ClearPersistent(StringRef key) { +void NetworkTable::ClearPersistent(wpi::StringRef key) { GetEntry(key).ClearPersistent(); } -bool NetworkTable::IsPersistent(StringRef key) const { +bool NetworkTable::IsPersistent(wpi::StringRef key) const { return GetEntry(key).IsPersistent(); } -void NetworkTable::SetFlags(StringRef key, unsigned int flags) { +void NetworkTable::SetFlags(wpi::StringRef key, unsigned int flags) { GetEntry(key).SetFlags(flags); } -void NetworkTable::ClearFlags(StringRef key, unsigned int flags) { +void NetworkTable::ClearFlags(wpi::StringRef key, unsigned int flags) { GetEntry(key).ClearFlags(flags); } -unsigned int NetworkTable::GetFlags(StringRef key) const { +unsigned int NetworkTable::GetFlags(wpi::StringRef key) const { return GetEntry(key).GetFlags(); } -void NetworkTable::Delete(const Twine& key) { +void NetworkTable::Delete(const wpi::Twine& key) { GetEntry(key).Delete(); } -bool NetworkTable::PutNumber(StringRef key, double value) { +bool NetworkTable::PutNumber(wpi::StringRef key, double value) { return GetEntry(key).SetDouble(value); } -bool NetworkTable::SetDefaultNumber(StringRef key, double defaultValue) { +bool NetworkTable::SetDefaultNumber(wpi::StringRef key, double defaultValue) { return GetEntry(key).SetDefaultDouble(defaultValue); } -double NetworkTable::GetNumber(StringRef key, double defaultValue) const { +double NetworkTable::GetNumber(wpi::StringRef key, double defaultValue) const { return GetEntry(key).GetDouble(defaultValue); } -bool NetworkTable::PutString(StringRef key, StringRef value) { +bool NetworkTable::PutString(wpi::StringRef key, wpi::StringRef value) { return GetEntry(key).SetString(value); } -bool NetworkTable::SetDefaultString(StringRef key, StringRef defaultValue) { +bool NetworkTable::SetDefaultString(wpi::StringRef key, + wpi::StringRef defaultValue) { return GetEntry(key).SetDefaultString(defaultValue); } -std::string NetworkTable::GetString(StringRef key, - StringRef defaultValue) const { +std::string NetworkTable::GetString(wpi::StringRef key, + wpi::StringRef defaultValue) const { return GetEntry(key).GetString(defaultValue); } -bool NetworkTable::PutBoolean(StringRef key, bool value) { +bool NetworkTable::PutBoolean(wpi::StringRef key, bool value) { return GetEntry(key).SetBoolean(value); } -bool NetworkTable::SetDefaultBoolean(StringRef key, bool defaultValue) { +bool NetworkTable::SetDefaultBoolean(wpi::StringRef key, bool defaultValue) { return GetEntry(key).SetDefaultBoolean(defaultValue); } -bool NetworkTable::GetBoolean(StringRef key, bool defaultValue) const { +bool NetworkTable::GetBoolean(wpi::StringRef key, bool defaultValue) const { return GetEntry(key).GetBoolean(defaultValue); } -bool NetworkTable::PutBooleanArray(StringRef key, ArrayRef value) { +bool NetworkTable::PutBooleanArray(wpi::StringRef key, + wpi::ArrayRef value) { return GetEntry(key).SetBooleanArray(value); } -bool NetworkTable::SetDefaultBooleanArray(StringRef key, - ArrayRef defaultValue) { +bool NetworkTable::SetDefaultBooleanArray(wpi::StringRef key, + wpi::ArrayRef defaultValue) { return GetEntry(key).SetDefaultBooleanArray(defaultValue); } std::vector NetworkTable::GetBooleanArray( - StringRef key, ArrayRef defaultValue) const { + wpi::StringRef key, wpi::ArrayRef defaultValue) const { return GetEntry(key).GetBooleanArray(defaultValue); } -bool NetworkTable::PutNumberArray(StringRef key, ArrayRef value) { +bool NetworkTable::PutNumberArray(wpi::StringRef key, + wpi::ArrayRef value) { return GetEntry(key).SetDoubleArray(value); } -bool NetworkTable::SetDefaultNumberArray(StringRef key, - ArrayRef defaultValue) { +bool NetworkTable::SetDefaultNumberArray(wpi::StringRef key, + wpi::ArrayRef defaultValue) { return GetEntry(key).SetDefaultDoubleArray(defaultValue); } std::vector NetworkTable::GetNumberArray( - StringRef key, ArrayRef defaultValue) const { + wpi::StringRef key, wpi::ArrayRef defaultValue) const { return GetEntry(key).GetDoubleArray(defaultValue); } -bool NetworkTable::PutStringArray(StringRef key, ArrayRef value) { +bool NetworkTable::PutStringArray(wpi::StringRef key, + wpi::ArrayRef value) { return GetEntry(key).SetStringArray(value); } -bool NetworkTable::SetDefaultStringArray(StringRef key, - ArrayRef defaultValue) { +bool NetworkTable::SetDefaultStringArray( + wpi::StringRef key, wpi::ArrayRef defaultValue) { return GetEntry(key).SetDefaultStringArray(defaultValue); } std::vector NetworkTable::GetStringArray( - StringRef key, ArrayRef defaultValue) const { + wpi::StringRef key, wpi::ArrayRef defaultValue) const { return GetEntry(key).GetStringArray(defaultValue); } -bool NetworkTable::PutRaw(StringRef key, StringRef value) { +bool NetworkTable::PutRaw(wpi::StringRef key, wpi::StringRef value) { return GetEntry(key).SetRaw(value); } -bool NetworkTable::SetDefaultRaw(StringRef key, StringRef defaultValue) { +bool NetworkTable::SetDefaultRaw(wpi::StringRef key, + wpi::StringRef defaultValue) { return GetEntry(key).SetDefaultRaw(defaultValue); } -std::string NetworkTable::GetRaw(StringRef key, StringRef defaultValue) const { +std::string NetworkTable::GetRaw(wpi::StringRef key, + wpi::StringRef defaultValue) const { return GetEntry(key).GetRaw(defaultValue); } -bool NetworkTable::PutValue(const Twine& key, std::shared_ptr value) { +bool NetworkTable::PutValue(const wpi::Twine& key, + std::shared_ptr value) { return GetEntry(key).SetValue(value); } -bool NetworkTable::SetDefaultValue(const Twine& key, +bool NetworkTable::SetDefaultValue(const wpi::Twine& key, std::shared_ptr defaultValue) { return GetEntry(key).SetDefaultValue(defaultValue); } -std::shared_ptr NetworkTable::GetValue(const Twine& key) const { +std::shared_ptr NetworkTable::GetValue(const wpi::Twine& key) const { return GetEntry(key).GetValue(); } -StringRef NetworkTable::GetPath() const { +wpi::StringRef NetworkTable::GetPath() const { return m_path; } -const char* NetworkTable::SaveEntries(const Twine& filename) const { - return nt::SaveEntries(m_inst, filename, m_path + Twine(PATH_SEPARATOR_CHAR)); +const char* NetworkTable::SaveEntries(const wpi::Twine& filename) const { + return nt::SaveEntries(m_inst, filename, + m_path + wpi::Twine(PATH_SEPARATOR_CHAR)); } const char* NetworkTable::LoadEntries( - const Twine& filename, + const wpi::Twine& filename, std::function warn) { - return nt::LoadEntries(m_inst, filename, m_path + Twine(PATH_SEPARATOR_CHAR), - warn); + return nt::LoadEntries(m_inst, filename, + m_path + wpi::Twine(PATH_SEPARATOR_CHAR), warn); } diff --git a/ntcore/src/main/native/cpp/networktables/NetworkTableInstance.cpp b/ntcore/src/main/native/cpp/networktables/NetworkTableInstance.cpp index 6697602eea..8a3d985dc6 100644 --- a/ntcore/src/main/native/cpp/networktables/NetworkTableInstance.cpp +++ b/ntcore/src/main/native/cpp/networktables/NetworkTableInstance.cpp @@ -9,8 +9,8 @@ using namespace nt; std::shared_ptr NetworkTableInstance::GetTable( - const Twine& key) const { - StringRef simple; + const wpi::Twine& key) const { + wpi::StringRef simple; bool isSimple = key.isSingleStringRef(); if (isSimple) { simple = key.getSingleStringRef(); @@ -23,23 +23,23 @@ std::shared_ptr NetworkTableInstance::GetTable( NetworkTable::private_init{}); } else { return std::make_shared( - m_handle, Twine(NetworkTable::PATH_SEPARATOR_CHAR) + key, + m_handle, wpi::Twine(NetworkTable::PATH_SEPARATOR_CHAR) + key, NetworkTable::private_init{}); } } -void NetworkTableInstance::StartClient(ArrayRef servers, +void NetworkTableInstance::StartClient(wpi::ArrayRef servers, unsigned int port) { - wpi::SmallVector, 8> server_ports; + wpi::SmallVector, 8> server_ports; for (const auto& server : servers) { server_ports.emplace_back(std::make_pair(server, port)); } StartClient(server_ports); } -void NetworkTableInstance::SetServer(ArrayRef servers, +void NetworkTableInstance::SetServer(wpi::ArrayRef servers, unsigned int port) { - wpi::SmallVector, 8> server_ports; + wpi::SmallVector, 8> server_ports; for (const auto& server : servers) { server_ports.emplace_back(std::make_pair(server, port)); } @@ -47,7 +47,7 @@ void NetworkTableInstance::SetServer(ArrayRef servers, } NT_EntryListener NetworkTableInstance::AddEntryListener( - const Twine& prefix, + const wpi::Twine& prefix, std::function callback, unsigned int flags) const { return ::nt::AddEntryListener(m_handle, prefix, callback, flags); diff --git a/ntcore/src/main/native/cpp/ntcore_c.cpp b/ntcore/src/main/native/cpp/ntcore_c.cpp index 07b5bc4889..d41fc9774c 100644 --- a/ntcore/src/main/native/cpp/ntcore_c.cpp +++ b/ntcore/src/main/native/cpp/ntcore_c.cpp @@ -192,12 +192,12 @@ NT_Inst NT_GetInstanceFromHandle(NT_Handle handle) { */ NT_Entry NT_GetEntry(NT_Inst inst, const char* name, size_t name_len) { - return nt::GetEntry(inst, StringRef(name, name_len)); + return nt::GetEntry(inst, wpi::StringRef(name, name_len)); } NT_Entry* NT_GetEntries(NT_Inst inst, const char* prefix, size_t prefix_len, unsigned int types, size_t* count) { - auto info_v = nt::GetEntries(inst, StringRef(prefix, prefix_len), types); + auto info_v = nt::GetEntries(inst, wpi::StringRef(prefix, prefix_len), types); *count = info_v.size(); if (info_v.size() == 0) { return nullptr; @@ -266,7 +266,8 @@ void NT_DeleteAllEntries(NT_Inst inst) { struct NT_EntryInfo* NT_GetEntryInfo(NT_Inst inst, const char* prefix, size_t prefix_len, unsigned int types, size_t* count) { - auto info_v = nt::GetEntryInfo(inst, StringRef(prefix, prefix_len), types); + auto info_v = + nt::GetEntryInfo(inst, wpi::StringRef(prefix, prefix_len), types); return ConvertToC(info_v, count); } @@ -288,7 +289,7 @@ NT_EntryListener NT_AddEntryListener(NT_Inst inst, const char* prefix, NT_EntryListenerCallback callback, unsigned int flags) { return nt::AddEntryListener( - inst, StringRef(prefix, prefix_len), + inst, wpi::StringRef(prefix, prefix_len), [=](const EntryNotification& event) { NT_EntryNotification c_event; ConvertToC(event, &c_event); @@ -324,7 +325,7 @@ NT_EntryListener NT_AddPolledEntryListener(NT_EntryListenerPoller poller, const char* prefix, size_t prefix_len, unsigned int flags) { - return nt::AddPolledEntryListener(poller, StringRef(prefix, prefix_len), + return nt::AddPolledEntryListener(poller, wpi::StringRef(prefix, prefix_len), flags); } @@ -421,12 +422,13 @@ NT_Bool NT_WaitForConnectionListenerQueue(NT_Inst inst, double timeout) { void NT_CreateRpc(NT_Entry entry, const char* def, size_t def_len, void* data, NT_RpcCallback callback) { - nt::CreateRpc(entry, StringRef(def, def_len), [=](const RpcAnswer& answer) { - NT_RpcAnswer answer_c; - ConvertToC(answer, &answer_c); - callback(data, &answer_c); - NT_DisposeRpcAnswer(&answer_c); - }); + nt::CreateRpc(entry, wpi::StringRef(def, def_len), + [=](const RpcAnswer& answer) { + NT_RpcAnswer answer_c; + ConvertToC(answer, &answer_c); + callback(data, &answer_c); + NT_DisposeRpcAnswer(&answer_c); + }); } NT_RpcCallPoller NT_CreateRpcCallPoller(NT_Inst inst) { @@ -439,7 +441,7 @@ void NT_DestroyRpcCallPoller(NT_RpcCallPoller poller) { void NT_CreatePolledRpc(NT_Entry entry, const char* def, size_t def_len, NT_RpcCallPoller poller) { - nt::CreatePolledRpc(entry, StringRef(def, def_len), poller); + nt::CreatePolledRpc(entry, wpi::StringRef(def, def_len), poller); } NT_RpcAnswer* NT_PollRpc(NT_RpcCallPoller poller, size_t* len) { @@ -465,11 +467,11 @@ NT_Bool NT_WaitForRpcCallQueue(NT_Inst inst, double timeout) { NT_Bool NT_PostRpcResponse(NT_Entry entry, NT_RpcCall call, const char* result, size_t result_len) { - return nt::PostRpcResponse(entry, call, StringRef(result, result_len)); + return nt::PostRpcResponse(entry, call, wpi::StringRef(result, result_len)); } NT_RpcCall NT_CallRpc(NT_Entry entry, const char* params, size_t params_len) { - return nt::CallRpc(entry, StringRef(params, params_len)); + return nt::CallRpc(entry, wpi::StringRef(params, params_len)); } char* NT_GetRpcResult(NT_Entry entry, NT_RpcCall call, size_t* result_len) { @@ -520,7 +522,7 @@ char* NT_PackRpcDefinition(const NT_RpcDefinition* def, size_t* packed_len) { NT_Bool NT_UnpackRpcDefinition(const char* packed, size_t packed_len, NT_RpcDefinition* def) { nt::RpcDefinition def_v; - if (!nt::UnpackRpcDefinition(StringRef(packed, packed_len), &def_v)) { + if (!nt::UnpackRpcDefinition(wpi::StringRef(packed, packed_len), &def_v)) { return 0; } @@ -550,8 +552,8 @@ char* NT_PackRpcValues(const NT_Value** values, size_t values_len, NT_Value** NT_UnpackRpcValues(const char* packed, size_t packed_len, const NT_Type* types, size_t types_len) { - auto values_v = nt::UnpackRpcValues(StringRef(packed, packed_len), - ArrayRef(types, types_len)); + auto values_v = nt::UnpackRpcValues(wpi::StringRef(packed, packed_len), + wpi::ArrayRef(types, types_len)); if (values_v.size() == 0) { return nullptr; } @@ -571,7 +573,7 @@ NT_Value** NT_UnpackRpcValues(const char* packed, size_t packed_len, */ void NT_SetNetworkIdentity(NT_Inst inst, const char* name, size_t name_len) { - nt::SetNetworkIdentity(inst, StringRef(name, name_len)); + nt::SetNetworkIdentity(inst, wpi::StringRef(name, name_len)); } unsigned int NT_GetNetworkMode(NT_Inst inst) { @@ -605,7 +607,7 @@ void NT_StartClient(NT_Inst inst, const char* server_name, unsigned int port) { void NT_StartClientMulti(NT_Inst inst, size_t count, const char** server_names, const unsigned int* ports) { - std::vector> servers; + std::vector> servers; servers.reserve(count); for (size_t i = 0; i < count; ++i) { servers.emplace_back(std::make_pair(server_names[i], ports[i])); @@ -627,7 +629,7 @@ void NT_SetServer(NT_Inst inst, const char* server_name, unsigned int port) { void NT_SetServerMulti(NT_Inst inst, size_t count, const char** server_names, const unsigned int* ports) { - std::vector> servers; + std::vector> servers; servers.reserve(count); for (size_t i = 0; i < count; ++i) { servers.emplace_back(std::make_pair(server_names[i], ports[i])); @@ -679,13 +681,14 @@ const char* NT_LoadPersistent(NT_Inst inst, const char* filename, const char* NT_SaveEntries(NT_Inst inst, const char* filename, const char* prefix, size_t prefix_len) { - return nt::SaveEntries(inst, filename, StringRef(prefix, prefix_len)); + return nt::SaveEntries(inst, filename, wpi::StringRef(prefix, prefix_len)); } const char* NT_LoadEntries(NT_Inst inst, const char* filename, const char* prefix, size_t prefix_len, void (*warn)(size_t line, const char* msg)) { - return nt::LoadEntries(inst, filename, StringRef(prefix, prefix_len), warn); + return nt::LoadEntries(inst, filename, wpi::StringRef(prefix, prefix_len), + warn); } /* @@ -951,23 +954,24 @@ NT_Bool NT_SetEntryBoolean(NT_Entry entry, uint64_t time, NT_Bool v_boolean, NT_Bool NT_SetEntryString(NT_Entry entry, uint64_t time, const char* str, size_t str_len, NT_Bool force) { if (force != 0) { - nt::SetEntryTypeValue(entry, - Value::MakeString(StringRef(str, str_len), time)); + nt::SetEntryTypeValue( + entry, Value::MakeString(wpi::StringRef(str, str_len), time)); return 1; } else { - return nt::SetEntryValue(entry, - Value::MakeString(StringRef(str, str_len), time)); + return nt::SetEntryValue( + entry, Value::MakeString(wpi::StringRef(str, str_len), time)); } } NT_Bool NT_SetEntryRaw(NT_Entry entry, uint64_t time, const char* raw, size_t raw_len, NT_Bool force) { if (force != 0) { - nt::SetEntryTypeValue(entry, Value::MakeRaw(StringRef(raw, raw_len), time)); + nt::SetEntryTypeValue(entry, + Value::MakeRaw(wpi::StringRef(raw, raw_len), time)); return 1; } else { - return nt::SetEntryValue(entry, - Value::MakeRaw(StringRef(raw, raw_len), time)); + return nt::SetEntryValue( + entry, Value::MakeRaw(wpi::StringRef(raw, raw_len), time)); } } @@ -1128,13 +1132,14 @@ NT_Bool NT_SetDefaultEntryString(NT_Entry entry, uint64_t time, const char* default_value, size_t default_len) { return nt::SetDefaultEntryValue( - entry, Value::MakeString(StringRef(default_value, default_len), time)); + entry, + Value::MakeString(wpi::StringRef(default_value, default_len), time)); } NT_Bool NT_SetDefaultEntryRaw(NT_Entry entry, uint64_t time, const char* default_value, size_t default_len) { return nt::SetDefaultEntryValue( - entry, Value::MakeRaw(StringRef(default_value, default_len), time)); + entry, Value::MakeRaw(wpi::StringRef(default_value, default_len), time)); } NT_Bool NT_SetDefaultEntryBooleanArray(NT_Entry entry, uint64_t time, diff --git a/ntcore/src/main/native/cpp/ntcore_cpp.cpp b/ntcore/src/main/native/cpp/ntcore_cpp.cpp index cf96f6a761..cf1a7d5c16 100644 --- a/ntcore/src/main/native/cpp/ntcore_cpp.cpp +++ b/ntcore/src/main/native/cpp/ntcore_cpp.cpp @@ -53,7 +53,7 @@ NT_Inst GetInstanceFromHandle(NT_Handle handle) { * Table Functions */ -NT_Entry GetEntry(NT_Inst inst, const Twine& name) { +NT_Entry GetEntry(NT_Inst inst, const wpi::Twine& name) { int i = Handle{inst}.GetTypedInst(Handle::kInstance); auto ii = InstanceImpl::Get(i); if (!ii) { @@ -67,7 +67,7 @@ NT_Entry GetEntry(NT_Inst inst, const Twine& name) { return Handle(i, id, Handle::kEntry); } -std::vector GetEntries(NT_Inst inst, const Twine& prefix, +std::vector GetEntries(NT_Inst inst, const wpi::Twine& prefix, unsigned int types) { int i = Handle{inst}.GetTypedInst(Handle::kInstance); auto ii = InstanceImpl::Get(i); @@ -203,7 +203,7 @@ void DeleteAllEntries(NT_Inst inst) { ii->storage.DeleteAllEntries(); } -std::vector GetEntryInfo(NT_Inst inst, const Twine& prefix, +std::vector GetEntryInfo(NT_Inst inst, const wpi::Twine& prefix, unsigned int types) { int i = Handle{inst}.GetTypedInst(Handle::kInstance); auto ii = InstanceImpl::Get(i); @@ -236,7 +236,7 @@ EntryInfo GetEntryInfo(NT_Entry entry) { */ NT_EntryListener AddEntryListener( - NT_Inst inst, const Twine& prefix, + NT_Inst inst, const wpi::Twine& prefix, std::function callback, unsigned int flags) { int i = Handle{inst}.GetTypedInst(Handle::kInstance); @@ -288,7 +288,7 @@ void DestroyEntryListenerPoller(NT_EntryListenerPoller poller) { } NT_EntryListener AddPolledEntryListener(NT_EntryListenerPoller poller, - const Twine& prefix, + const wpi::Twine& prefix, unsigned int flags) { Handle handle{poller}; int id = handle.GetTypedIndex(Handle::kEntryListenerPoller); @@ -494,7 +494,7 @@ bool WaitForConnectionListenerQueue(NT_Inst inst, double timeout) { * Remote Procedure Call Functions */ -void CreateRpc(NT_Entry entry, StringRef def, +void CreateRpc(NT_Entry entry, wpi::StringRef def, std::function callback) { Handle handle{entry}; int id = handle.GetTypedIndex(Handle::kEntry); @@ -535,7 +535,8 @@ void DestroyRpcCallPoller(NT_RpcCallPoller poller) { ii->rpc_server.RemovePoller(id); } -void CreatePolledRpc(NT_Entry entry, StringRef def, NT_RpcCallPoller poller) { +void CreatePolledRpc(NT_Entry entry, wpi::StringRef def, + NT_RpcCallPoller poller) { Handle handle{entry}; int id = handle.GetTypedIndex(Handle::kEntry); auto ii = InstanceImpl::Get(handle.GetInst()); @@ -607,7 +608,7 @@ bool WaitForRpcCallQueue(NT_Inst inst, double timeout) { return ii->rpc_server.WaitForQueue(timeout); } -bool PostRpcResponse(NT_Entry entry, NT_RpcCall call, StringRef result) { +bool PostRpcResponse(NT_Entry entry, NT_RpcCall call, wpi::StringRef result) { Handle handle{entry}; int id = handle.GetTypedIndex(Handle::kEntry); auto ii = InstanceImpl::Get(handle.GetInst()); @@ -627,7 +628,7 @@ bool PostRpcResponse(NT_Entry entry, NT_RpcCall call, StringRef result) { return ii->rpc_server.PostRpcResponse(id, call_uid, result); } -NT_RpcCall CallRpc(NT_Entry entry, StringRef params) { +NT_RpcCall CallRpc(NT_Entry entry, wpi::StringRef params) { Handle handle{entry}; int id = handle.GetTypedIndex(Handle::kEntry); int i = handle.GetInst(); @@ -736,7 +737,7 @@ std::string PackRpcDefinition(const RpcDefinition& def) { return enc.ToStringRef(); } -bool UnpackRpcDefinition(StringRef packed, RpcDefinition* def) { +bool UnpackRpcDefinition(wpi::StringRef packed, RpcDefinition* def) { wpi::raw_mem_istream is(packed.data(), packed.size()); wpi::Logger logger; WireDecoder dec(is, 0x0300, logger); @@ -791,7 +792,7 @@ bool UnpackRpcDefinition(StringRef packed, RpcDefinition* def) { return true; } -std::string PackRpcValues(ArrayRef> values) { +std::string PackRpcValues(wpi::ArrayRef> values) { WireEncoder enc(0x0300); for (auto& value : values) { enc.WriteValue(*value); @@ -799,8 +800,8 @@ std::string PackRpcValues(ArrayRef> values) { return enc.ToStringRef(); } -std::vector> UnpackRpcValues(StringRef packed, - ArrayRef types) { +std::vector> UnpackRpcValues( + wpi::StringRef packed, wpi::ArrayRef types) { wpi::raw_mem_istream is(packed.data(), packed.size()); wpi::Logger logger; WireDecoder dec(is, 0x0300, logger); @@ -823,7 +824,7 @@ uint64_t Now() { * Client/Server Functions */ -void SetNetworkIdentity(NT_Inst inst, const Twine& name) { +void SetNetworkIdentity(NT_Inst inst, const wpi::Twine& name) { auto ii = InstanceImpl::Get(Handle{inst}.GetTypedInst(Handle::kInstance)); if (!ii) { return; @@ -859,7 +860,7 @@ void StopLocal(NT_Inst inst) { ii->dispatcher.Stop(); } -void StartServer(NT_Inst inst, const Twine& persist_filename, +void StartServer(NT_Inst inst, const wpi::Twine& persist_filename, const char* listen_address, unsigned int port) { auto ii = InstanceImpl::Get(Handle{inst}.GetTypedInst(Handle::kInstance)); if (!ii) { @@ -897,8 +898,9 @@ void StartClient(NT_Inst inst, const char* server_name, unsigned int port) { ii->dispatcher.StartClient(); } -void StartClient(NT_Inst inst, - ArrayRef> servers) { +void StartClient( + NT_Inst inst, + wpi::ArrayRef> servers) { auto ii = InstanceImpl::Get(Handle{inst}.GetTypedInst(Handle::kInstance)); if (!ii) { return; @@ -937,7 +939,7 @@ void SetServer(NT_Inst inst, const char* server_name, unsigned int port) { } void SetServer(NT_Inst inst, - ArrayRef> servers) { + wpi::ArrayRef> servers) { auto ii = InstanceImpl::Get(Handle{inst}.GetTypedInst(Handle::kInstance)); if (!ii) { return; @@ -1013,7 +1015,7 @@ bool IsConnected(NT_Inst inst) { * Persistent Functions */ -const char* SavePersistent(NT_Inst inst, const Twine& filename) { +const char* SavePersistent(NT_Inst inst, const wpi::Twine& filename) { auto ii = InstanceImpl::Get(Handle{inst}.GetTypedInst(Handle::kInstance)); if (!ii) { return "invalid instance handle"; @@ -1023,7 +1025,7 @@ const char* SavePersistent(NT_Inst inst, const Twine& filename) { } const char* LoadPersistent( - NT_Inst inst, const Twine& filename, + NT_Inst inst, const wpi::Twine& filename, std::function warn) { auto ii = InstanceImpl::Get(Handle{inst}.GetTypedInst(Handle::kInstance)); if (!ii) { @@ -1033,8 +1035,8 @@ const char* LoadPersistent( return ii->storage.LoadPersistent(filename, warn); } -const char* SaveEntries(NT_Inst inst, const Twine& filename, - const Twine& prefix) { +const char* SaveEntries(NT_Inst inst, const wpi::Twine& filename, + const wpi::Twine& prefix) { auto ii = InstanceImpl::Get(Handle{inst}.GetTypedInst(Handle::kInstance)); if (!ii) { return "invalid instance handle"; @@ -1044,7 +1046,7 @@ const char* SaveEntries(NT_Inst inst, const Twine& filename, } const char* LoadEntries( - NT_Inst inst, const Twine& filename, const Twine& prefix, + NT_Inst inst, const wpi::Twine& filename, const wpi::Twine& prefix, std::function warn) { auto ii = InstanceImpl::Get(Handle{inst}.GetTypedInst(Handle::kInstance)); if (!ii) { diff --git a/ntcore/src/main/native/include/networktables/NetworkTable.h b/ntcore/src/main/native/include/networktables/NetworkTable.h index 682b3ee906..664c98b563 100644 --- a/ntcore/src/main/native/include/networktables/NetworkTable.h +++ b/ntcore/src/main/native/include/networktables/NetworkTable.h @@ -23,10 +23,6 @@ namespace nt { -using wpi::ArrayRef; -using wpi::StringRef; -using wpi::Twine; - class NetworkTableInstance; /** @@ -58,7 +54,7 @@ class NetworkTable final { * @param key key * @return base name */ - static StringRef BasenameKey(StringRef key); + static wpi::StringRef BasenameKey(wpi::StringRef key); /** * Normalizes an network table key to contain no consecutive slashes and @@ -76,12 +72,12 @@ class NetworkTable final { * with a leading slash * @return normalized key */ - static std::string NormalizeKey(const Twine& key, + static std::string NormalizeKey(const wpi::Twine& key, bool withLeadingSlash = true); - static StringRef NormalizeKey(const Twine& key, - wpi::SmallVectorImpl& buf, - bool withLeadingSlash = true); + static wpi::StringRef NormalizeKey(const wpi::Twine& key, + wpi::SmallVectorImpl& buf, + bool withLeadingSlash = true); /** * Gets a list of the names of all the super tables of a given key. For @@ -91,13 +87,13 @@ class NetworkTable final { * @param key the key * @return List of super tables */ - static std::vector GetHierarchy(const Twine& key); + static std::vector GetHierarchy(const wpi::Twine& key); /** * Constructor. Use NetworkTableInstance::GetTable() or GetSubTable() * instead. */ - NetworkTable(NT_Inst inst, const Twine& path, const private_init&); + NetworkTable(NT_Inst inst, const wpi::Twine& path, const private_init&); virtual ~NetworkTable(); /** @@ -118,7 +114,7 @@ class NetworkTable final { * @param key the key name * @return Network table entry. */ - NetworkTableEntry GetEntry(const Twine& key) const; + NetworkTableEntry GetEntry(const wpi::Twine& key) const; /** * Listen to keys only within this table. @@ -138,7 +134,7 @@ class NetworkTable final { * @param flags EntryListenerFlags bitmask * @return Listener handle */ - NT_EntryListener AddEntryListener(const Twine& key, + NT_EntryListener AddEntryListener(const wpi::Twine& key, TableEntryListener listener, unsigned int flags) const; @@ -175,7 +171,7 @@ class NetworkTable final { * @param key the key name * @return the networktable to be returned */ - std::shared_ptr GetSubTable(const Twine& key) const; + std::shared_ptr GetSubTable(const wpi::Twine& key) const; /** * Determines whether the given key is in this table. @@ -183,7 +179,7 @@ class NetworkTable final { * @param key the key to search for * @return true if the table as a value assigned to the given key */ - bool ContainsKey(const Twine& key) const; + bool ContainsKey(const wpi::Twine& key) const; /** * Determines whether there exists a non-empty subtable for this key @@ -193,7 +189,7 @@ class NetworkTable final { * @return true if there is a subtable with the key which contains at least * one key/subtable of its own */ - bool ContainsSubTable(const Twine& key) const; + bool ContainsSubTable(const wpi::Twine& key) const; /** * Gets all keys in the table (not including sub-tables). @@ -215,7 +211,7 @@ class NetworkTable final { * * @param key the key to make persistent */ - void SetPersistent(StringRef key); + void SetPersistent(wpi::StringRef key); /** * Stop making a key's value persistent through program restarts. @@ -223,7 +219,7 @@ class NetworkTable final { * * @param key the key name */ - void ClearPersistent(StringRef key); + void ClearPersistent(wpi::StringRef key); /** * Returns whether the value is persistent through program restarts. @@ -231,7 +227,7 @@ class NetworkTable final { * * @param key the key name */ - bool IsPersistent(StringRef key) const; + bool IsPersistent(wpi::StringRef key) const; /** * Sets flags on the specified key in this table. The key can @@ -240,7 +236,7 @@ class NetworkTable final { * @param key the key name * @param flags the flags to set (bitmask) */ - void SetFlags(StringRef key, unsigned int flags); + void SetFlags(wpi::StringRef key, unsigned int flags); /** * Clears flags on the specified key in this table. The key can @@ -249,7 +245,7 @@ class NetworkTable final { * @param key the key name * @param flags the flags to clear (bitmask) */ - void ClearFlags(StringRef key, unsigned int flags); + void ClearFlags(wpi::StringRef key, unsigned int flags); /** * Returns the flags for the specified key. @@ -257,14 +253,14 @@ class NetworkTable final { * @param key the key name * @return the flags, or 0 if the key is not defined */ - unsigned int GetFlags(StringRef key) const; + unsigned int GetFlags(wpi::StringRef key) const; /** * Deletes the specified key in this table. * * @param key the key name */ - void Delete(const Twine& key); + void Delete(const wpi::Twine& key); /** * Put a number in the table @@ -273,7 +269,7 @@ class NetworkTable final { * @param value the value that will be assigned * @return False if the table key already exists with a different type */ - bool PutNumber(StringRef key, double value); + bool PutNumber(wpi::StringRef key, double value); /** * Gets the current value in the table, setting it if it does not exist. @@ -282,7 +278,7 @@ class NetworkTable final { * @param defaultValue the default value to set if key doesn't exist. * @returns False if the table key exists with a different type */ - bool SetDefaultNumber(StringRef key, double defaultValue); + bool SetDefaultNumber(wpi::StringRef key, double defaultValue); /** * Gets the number associated with the given name. @@ -292,7 +288,7 @@ class NetworkTable final { * @return the value associated with the given key or the given default value * if there is no value associated with the key */ - double GetNumber(StringRef key, double defaultValue) const; + double GetNumber(wpi::StringRef key, double defaultValue) const; /** * Put a string in the table @@ -301,7 +297,7 @@ class NetworkTable final { * @param value the value that will be assigned * @return False if the table key already exists with a different type */ - bool PutString(StringRef key, StringRef value); + bool PutString(wpi::StringRef key, wpi::StringRef value); /** * Gets the current value in the table, setting it if it does not exist. @@ -310,7 +306,7 @@ class NetworkTable final { * @param defaultValue the default value to set if key doesn't exist. * @returns False if the table key exists with a different type */ - bool SetDefaultString(StringRef key, StringRef defaultValue); + bool SetDefaultString(wpi::StringRef key, wpi::StringRef defaultValue); /** * Gets the string associated with the given name. If the key does not @@ -321,7 +317,7 @@ class NetworkTable final { * @return the value associated with the given key or the given default value * if there is no value associated with the key */ - std::string GetString(StringRef key, StringRef defaultValue) const; + std::string GetString(wpi::StringRef key, wpi::StringRef defaultValue) const; /** * Put a boolean in the table @@ -330,7 +326,7 @@ class NetworkTable final { * @param value the value that will be assigned * @return False if the table key already exists with a different type */ - bool PutBoolean(StringRef key, bool value); + bool PutBoolean(wpi::StringRef key, bool value); /** * Gets the current value in the table, setting it if it does not exist. @@ -339,7 +335,7 @@ class NetworkTable final { * @param defaultValue the default value to set if key doesn't exist. * @returns False if the table key exists with a different type */ - bool SetDefaultBoolean(StringRef key, bool defaultValue); + bool SetDefaultBoolean(wpi::StringRef key, bool defaultValue); /** * Gets the boolean associated with the given name. If the key does not @@ -350,7 +346,7 @@ class NetworkTable final { * @return the value associated with the given key or the given default value * if there is no value associated with the key */ - bool GetBoolean(StringRef key, bool defaultValue) const; + bool GetBoolean(wpi::StringRef key, bool defaultValue) const; /** * Put a boolean array in the table @@ -363,7 +359,7 @@ class NetworkTable final { * std::vector is special-cased in C++. 0 is false, any * non-zero value is true. */ - bool PutBooleanArray(StringRef key, ArrayRef value); + bool PutBooleanArray(wpi::StringRef key, wpi::ArrayRef value); /** * Gets the current value in the table, setting it if it does not exist. @@ -372,7 +368,8 @@ class NetworkTable final { * @param defaultValue the default value to set if key doesn't exist. * @return False if the table key exists with a different type */ - bool SetDefaultBooleanArray(StringRef key, ArrayRef defaultValue); + bool SetDefaultBooleanArray(wpi::StringRef key, + wpi::ArrayRef defaultValue); /** * Returns the boolean array the key maps to. If the key does not exist or is @@ -390,8 +387,8 @@ class NetworkTable final { * because std::vector is special-cased in C++. 0 is false, any * non-zero value is true. */ - std::vector GetBooleanArray(StringRef key, - ArrayRef defaultValue) const; + std::vector GetBooleanArray(wpi::StringRef key, + wpi::ArrayRef defaultValue) const; /** * Put a number array in the table @@ -400,7 +397,7 @@ class NetworkTable final { * @param value the value that will be assigned * @return False if the table key already exists with a different type */ - bool PutNumberArray(StringRef key, ArrayRef value); + bool PutNumberArray(wpi::StringRef key, wpi::ArrayRef value); /** * Gets the current value in the table, setting it if it does not exist. @@ -409,7 +406,8 @@ class NetworkTable final { * @param defaultValue the default value to set if key doesn't exist. * @returns False if the table key exists with a different type */ - bool SetDefaultNumberArray(StringRef key, ArrayRef defaultValue); + bool SetDefaultNumberArray(wpi::StringRef key, + wpi::ArrayRef defaultValue); /** * Returns the number array the key maps to. If the key does not exist or is @@ -423,8 +421,8 @@ class NetworkTable final { * @note This makes a copy of the array. If the overhead of this is a * concern, use GetValue() instead. */ - std::vector GetNumberArray(StringRef key, - ArrayRef defaultValue) const; + std::vector GetNumberArray(wpi::StringRef key, + wpi::ArrayRef defaultValue) const; /** * Put a string array in the table @@ -433,7 +431,7 @@ class NetworkTable final { * @param value the value that will be assigned * @return False if the table key already exists with a different type */ - bool PutStringArray(StringRef key, ArrayRef value); + bool PutStringArray(wpi::StringRef key, wpi::ArrayRef value); /** * Gets the current value in the table, setting it if it does not exist. @@ -442,7 +440,8 @@ class NetworkTable final { * @param defaultValue the default value to set if key doesn't exist. * @returns False if the table key exists with a different type */ - bool SetDefaultStringArray(StringRef key, ArrayRef defaultValue); + bool SetDefaultStringArray(wpi::StringRef key, + wpi::ArrayRef defaultValue); /** * Returns the string array the key maps to. If the key does not exist or is @@ -457,7 +456,7 @@ class NetworkTable final { * concern, use GetValue() instead. */ std::vector GetStringArray( - StringRef key, ArrayRef defaultValue) const; + wpi::StringRef key, wpi::ArrayRef defaultValue) const; /** * Put a raw value (byte array) in the table @@ -466,7 +465,7 @@ class NetworkTable final { * @param value the value that will be assigned * @return False if the table key already exists with a different type */ - bool PutRaw(StringRef key, StringRef value); + bool PutRaw(wpi::StringRef key, wpi::StringRef value); /** * Gets the current value in the table, setting it if it does not exist. @@ -475,7 +474,7 @@ class NetworkTable final { * @param defaultValue the default value to set if key doesn't exist. * @return False if the table key exists with a different type */ - bool SetDefaultRaw(StringRef key, StringRef defaultValue); + bool SetDefaultRaw(wpi::StringRef key, wpi::StringRef defaultValue); /** * Returns the raw value (byte array) the key maps to. If the key does not @@ -489,7 +488,7 @@ class NetworkTable final { * @note This makes a copy of the raw contents. If the overhead of this is a * concern, use GetValue() instead. */ - std::string GetRaw(StringRef key, StringRef defaultValue) const; + std::string GetRaw(wpi::StringRef key, wpi::StringRef defaultValue) const; /** * Put a value in the table @@ -498,7 +497,7 @@ class NetworkTable final { * @param value the value that will be assigned * @return False if the table key already exists with a different type */ - bool PutValue(const Twine& key, std::shared_ptr value); + bool PutValue(const wpi::Twine& key, std::shared_ptr value); /** * Gets the current value in the table, setting it if it does not exist. @@ -507,7 +506,8 @@ class NetworkTable final { * @param defaultValue the default value to set if key doesn't exist. * @return False if the table key exists with a different type */ - bool SetDefaultValue(const Twine& key, std::shared_ptr defaultValue); + bool SetDefaultValue(const wpi::Twine& key, + std::shared_ptr defaultValue); /** * Gets the value associated with a key as an object @@ -516,14 +516,14 @@ class NetworkTable final { * @return the value associated with the given key, or nullptr if the key * does not exist */ - std::shared_ptr GetValue(const Twine& key) const; + std::shared_ptr GetValue(const wpi::Twine& key) const; /** * Gets the full path of this table. Does not include the trailing "/". * * @return The path (e.g "", "/foo"). */ - StringRef GetPath() const; + wpi::StringRef GetPath() const; /** * Save table values to a file. The file format used is identical to @@ -532,7 +532,7 @@ class NetworkTable final { * @param filename filename * @return error string, or nullptr if successful */ - const char* SaveEntries(const Twine& filename) const; + const char* SaveEntries(const wpi::Twine& filename) const; /** * Load table values from a file. The file format used is identical to @@ -543,7 +543,7 @@ class NetworkTable final { * @return error string, or nullptr if successful */ const char* LoadEntries( - const Twine& filename, + const wpi::Twine& filename, std::function warn); }; diff --git a/ntcore/src/main/native/include/networktables/NetworkTableEntry.h b/ntcore/src/main/native/include/networktables/NetworkTableEntry.h index e417885ce3..5b5a0fd5d7 100644 --- a/ntcore/src/main/native/include/networktables/NetworkTableEntry.h +++ b/ntcore/src/main/native/include/networktables/NetworkTableEntry.h @@ -23,10 +23,6 @@ namespace nt { -using wpi::ArrayRef; -using wpi::StringRef; -using wpi::Twine; - class NetworkTableInstance; /** @@ -147,7 +143,7 @@ class NetworkTableEntry final { * @param defaultValue the value to be returned if no value is found * @return the entry's value or the given default value */ - std::string GetString(StringRef defaultValue) const; + std::string GetString(wpi::StringRef defaultValue) const; /** * Gets the entry's value as a raw. If the entry does not exist or is of @@ -156,7 +152,7 @@ class NetworkTableEntry final { * @param defaultValue the value to be returned if no value is found * @return the entry's value or the given default value */ - std::string GetRaw(StringRef defaultValue) const; + std::string GetRaw(wpi::StringRef defaultValue) const; /** * Gets the entry's value as a boolean array. If the entry does not exist @@ -172,7 +168,7 @@ class NetworkTableEntry final { * because std::vector is special-cased in C++. 0 is false, any * non-zero value is true. */ - std::vector GetBooleanArray(ArrayRef defaultValue) const; + std::vector GetBooleanArray(wpi::ArrayRef defaultValue) const; /** * Gets the entry's value as a boolean array. If the entry does not exist @@ -201,7 +197,7 @@ class NetworkTableEntry final { * @note This makes a copy of the array. If the overhead of this is a * concern, use GetValue() instead. */ - std::vector GetDoubleArray(ArrayRef defaultValue) const; + std::vector GetDoubleArray(wpi::ArrayRef defaultValue) const; /** * Gets the entry's value as a double array. If the entry does not exist @@ -227,7 +223,7 @@ class NetworkTableEntry final { * concern, use GetValue() instead. */ std::vector GetStringArray( - ArrayRef defaultValue) const; + wpi::ArrayRef defaultValue) const; /** * Gets the entry's value as a string array. If the entry does not exist @@ -272,7 +268,7 @@ class NetworkTableEntry final { * @param defaultValue the default value to set * @return False if the entry exists with a different type */ - bool SetDefaultString(const Twine& defaultValue); + bool SetDefaultString(const wpi::Twine& defaultValue); /** * Sets the entry's value if it does not exist. @@ -280,7 +276,7 @@ class NetworkTableEntry final { * @param defaultValue the default value to set * @return False if the entry exists with a different type */ - bool SetDefaultRaw(StringRef defaultValue); + bool SetDefaultRaw(wpi::StringRef defaultValue); /** * Sets the entry's value if it does not exist. @@ -288,7 +284,7 @@ class NetworkTableEntry final { * @param defaultValue the default value to set * @return False if the entry exists with a different type */ - bool SetDefaultBooleanArray(ArrayRef defaultValue); + bool SetDefaultBooleanArray(wpi::ArrayRef defaultValue); /** * Sets the entry's value if it does not exist. @@ -304,7 +300,7 @@ class NetworkTableEntry final { * @param defaultValue the default value to set * @return False if the entry exists with a different type */ - bool SetDefaultDoubleArray(ArrayRef defaultValue); + bool SetDefaultDoubleArray(wpi::ArrayRef defaultValue); /** * Sets the entry's value if it does not exist. @@ -320,7 +316,7 @@ class NetworkTableEntry final { * @param defaultValue the default value to set * @return False if the entry exists with a different type */ - bool SetDefaultStringArray(ArrayRef defaultValue); + bool SetDefaultStringArray(wpi::ArrayRef defaultValue); /** * Sets the entry's value if it does not exist. @@ -360,7 +356,7 @@ class NetworkTableEntry final { * @param value the value to set * @return False if the entry exists with a different type */ - bool SetString(const Twine& value); + bool SetString(const wpi::Twine& value); /** * Sets the entry's value. @@ -368,7 +364,7 @@ class NetworkTableEntry final { * @param value the value to set * @return False if the entry exists with a different type */ - bool SetRaw(StringRef value); + bool SetRaw(wpi::StringRef value); /** * Sets the entry's value. @@ -376,7 +372,7 @@ class NetworkTableEntry final { * @param value the value to set * @return False if the entry exists with a different type */ - bool SetBooleanArray(ArrayRef value); + bool SetBooleanArray(wpi::ArrayRef value); /** * Sets the entry's value. @@ -392,7 +388,7 @@ class NetworkTableEntry final { * @param value the value to set * @return False if the entry exists with a different type */ - bool SetBooleanArray(ArrayRef value); + bool SetBooleanArray(wpi::ArrayRef value); /** * Sets the entry's value. @@ -408,7 +404,7 @@ class NetworkTableEntry final { * @param value the value to set * @return False if the entry exists with a different type */ - bool SetDoubleArray(ArrayRef value); + bool SetDoubleArray(wpi::ArrayRef value); /** * Sets the entry's value. @@ -424,7 +420,7 @@ class NetworkTableEntry final { * @param value the value to set * @return False if the entry exists with a different type */ - bool SetStringArray(ArrayRef value); + bool SetStringArray(wpi::ArrayRef value); /** * Sets the entry's value. @@ -464,7 +460,7 @@ class NetworkTableEntry final { * * @param value the value to set */ - void ForceSetString(const Twine& value); + void ForceSetString(const wpi::Twine& value); /** * Sets the entry's value. If the value is of different type, the type is @@ -472,7 +468,7 @@ class NetworkTableEntry final { * * @param value the value to set */ - void ForceSetRaw(StringRef value); + void ForceSetRaw(wpi::StringRef value); /** * Sets the entry's value. If the value is of different type, the type is @@ -480,7 +476,7 @@ class NetworkTableEntry final { * * @param value the value to set */ - void ForceSetBooleanArray(ArrayRef value); + void ForceSetBooleanArray(wpi::ArrayRef value); /** * Sets the entry's value. If the value is of different type, the type is @@ -496,7 +492,7 @@ class NetworkTableEntry final { * * @param value the value to set */ - void ForceSetBooleanArray(ArrayRef value); + void ForceSetBooleanArray(wpi::ArrayRef value); /** * Sets the entry's value. If the value is of different type, the type is @@ -512,7 +508,7 @@ class NetworkTableEntry final { * * @param value the value to set */ - void ForceSetDoubleArray(ArrayRef value); + void ForceSetDoubleArray(wpi::ArrayRef value); /** * Sets the entry's value. If the value is of different type, the type is @@ -528,7 +524,7 @@ class NetworkTableEntry final { * * @param value the value to set */ - void ForceSetStringArray(ArrayRef value); + void ForceSetStringArray(wpi::ArrayRef value); /** * Sets the entry's value. If the value is of different type, the type is @@ -600,7 +596,7 @@ class NetworkTableEntry final { * @param params parameter * @return RPC call object. */ - RpcCall CallRpc(StringRef params); + RpcCall CallRpc(wpi::StringRef params); /** * Add a listener for changes to this entry. diff --git a/ntcore/src/main/native/include/networktables/NetworkTableEntry.inl b/ntcore/src/main/native/include/networktables/NetworkTableEntry.inl index c63250f5dc..8afbb45daf 100644 --- a/ntcore/src/main/native/include/networktables/NetworkTableEntry.inl +++ b/ntcore/src/main/native/include/networktables/NetworkTableEntry.inl @@ -66,7 +66,8 @@ inline double NetworkTableEntry::GetDouble(double defaultValue) const { return value->GetDouble(); } -inline std::string NetworkTableEntry::GetString(StringRef defaultValue) const { +inline std::string NetworkTableEntry::GetString( + wpi::StringRef defaultValue) const { auto value = GetEntryValue(m_handle); if (!value || value->type() != NT_STRING) { return defaultValue; @@ -74,7 +75,8 @@ inline std::string NetworkTableEntry::GetString(StringRef defaultValue) const { return value->GetString(); } -inline std::string NetworkTableEntry::GetRaw(StringRef defaultValue) const { +inline std::string NetworkTableEntry::GetRaw( + wpi::StringRef defaultValue) const { auto value = GetEntryValue(m_handle); if (!value || value->type() != NT_RAW) { return defaultValue; @@ -83,7 +85,7 @@ inline std::string NetworkTableEntry::GetRaw(StringRef defaultValue) const { } inline std::vector NetworkTableEntry::GetBooleanArray( - ArrayRef defaultValue) const { + wpi::ArrayRef defaultValue) const { auto value = GetEntryValue(m_handle); if (!value || value->type() != NT_BOOLEAN_ARRAY) { return defaultValue; @@ -98,7 +100,7 @@ inline std::vector NetworkTableEntry::GetBooleanArray( } inline std::vector NetworkTableEntry::GetDoubleArray( - ArrayRef defaultValue) const { + wpi::ArrayRef defaultValue) const { auto value = GetEntryValue(m_handle); if (!value || value->type() != NT_DOUBLE_ARRAY) { return defaultValue; @@ -113,7 +115,7 @@ inline std::vector NetworkTableEntry::GetDoubleArray( } inline std::vector NetworkTableEntry::GetStringArray( - ArrayRef defaultValue) const { + wpi::ArrayRef defaultValue) const { auto value = GetEntryValue(m_handle); if (!value || value->type() != NT_STRING_ARRAY) { return defaultValue; @@ -139,16 +141,17 @@ inline bool NetworkTableEntry::SetDefaultDouble(double defaultValue) { return SetDefaultEntryValue(m_handle, Value::MakeDouble(defaultValue)); } -inline bool NetworkTableEntry::SetDefaultString(const Twine& defaultValue) { +inline bool NetworkTableEntry::SetDefaultString( + const wpi::Twine& defaultValue) { return SetDefaultEntryValue(m_handle, Value::MakeString(defaultValue)); } -inline bool NetworkTableEntry::SetDefaultRaw(StringRef defaultValue) { +inline bool NetworkTableEntry::SetDefaultRaw(wpi::StringRef defaultValue) { return SetDefaultEntryValue(m_handle, Value::MakeRaw(defaultValue)); } inline bool NetworkTableEntry::SetDefaultBooleanArray( - ArrayRef defaultValue) { + wpi::ArrayRef defaultValue) { return SetDefaultEntryValue(m_handle, Value::MakeBooleanArray(defaultValue)); } @@ -158,7 +161,7 @@ inline bool NetworkTableEntry::SetDefaultBooleanArray( } inline bool NetworkTableEntry::SetDefaultDoubleArray( - ArrayRef defaultValue) { + wpi::ArrayRef defaultValue) { return SetDefaultEntryValue(m_handle, Value::MakeDoubleArray(defaultValue)); } @@ -168,7 +171,7 @@ inline bool NetworkTableEntry::SetDefaultDoubleArray( } inline bool NetworkTableEntry::SetDefaultStringArray( - ArrayRef defaultValue) { + wpi::ArrayRef defaultValue) { return SetDefaultEntryValue(m_handle, Value::MakeStringArray(defaultValue)); } @@ -189,15 +192,15 @@ inline bool NetworkTableEntry::SetDouble(double value) { return SetEntryValue(m_handle, Value::MakeDouble(value)); } -inline bool NetworkTableEntry::SetString(const Twine& value) { +inline bool NetworkTableEntry::SetString(const wpi::Twine& value) { return SetEntryValue(m_handle, Value::MakeString(value)); } -inline bool NetworkTableEntry::SetRaw(StringRef value) { +inline bool NetworkTableEntry::SetRaw(wpi::StringRef value) { return SetEntryValue(m_handle, Value::MakeRaw(value)); } -inline bool NetworkTableEntry::SetBooleanArray(ArrayRef value) { +inline bool NetworkTableEntry::SetBooleanArray(wpi::ArrayRef value) { return SetEntryValue(m_handle, Value::MakeBooleanArray(value)); } @@ -206,7 +209,7 @@ inline bool NetworkTableEntry::SetBooleanArray( return SetEntryValue(m_handle, Value::MakeBooleanArray(value)); } -inline bool NetworkTableEntry::SetBooleanArray(ArrayRef value) { +inline bool NetworkTableEntry::SetBooleanArray(wpi::ArrayRef value) { return SetEntryValue(m_handle, Value::MakeBooleanArray(value)); } @@ -215,7 +218,7 @@ inline bool NetworkTableEntry::SetBooleanArray( return SetEntryValue(m_handle, Value::MakeBooleanArray(value)); } -inline bool NetworkTableEntry::SetDoubleArray(ArrayRef value) { +inline bool NetworkTableEntry::SetDoubleArray(wpi::ArrayRef value) { return SetEntryValue(m_handle, Value::MakeDoubleArray(value)); } @@ -224,7 +227,8 @@ inline bool NetworkTableEntry::SetDoubleArray( return SetEntryValue(m_handle, Value::MakeDoubleArray(value)); } -inline bool NetworkTableEntry::SetStringArray(ArrayRef value) { +inline bool NetworkTableEntry::SetStringArray( + wpi::ArrayRef value) { return SetEntryValue(m_handle, Value::MakeStringArray(value)); } @@ -245,15 +249,15 @@ inline void NetworkTableEntry::ForceSetDouble(double value) { SetEntryTypeValue(m_handle, Value::MakeDouble(value)); } -inline void NetworkTableEntry::ForceSetString(const Twine& value) { +inline void NetworkTableEntry::ForceSetString(const wpi::Twine& value) { SetEntryTypeValue(m_handle, Value::MakeString(value)); } -inline void NetworkTableEntry::ForceSetRaw(StringRef value) { +inline void NetworkTableEntry::ForceSetRaw(wpi::StringRef value) { SetEntryTypeValue(m_handle, Value::MakeRaw(value)); } -inline void NetworkTableEntry::ForceSetBooleanArray(ArrayRef value) { +inline void NetworkTableEntry::ForceSetBooleanArray(wpi::ArrayRef value) { SetEntryTypeValue(m_handle, Value::MakeBooleanArray(value)); } @@ -262,7 +266,7 @@ inline void NetworkTableEntry::ForceSetBooleanArray( SetEntryTypeValue(m_handle, Value::MakeBooleanArray(value)); } -inline void NetworkTableEntry::ForceSetBooleanArray(ArrayRef value) { +inline void NetworkTableEntry::ForceSetBooleanArray(wpi::ArrayRef value) { SetEntryTypeValue(m_handle, Value::MakeBooleanArray(value)); } @@ -271,7 +275,8 @@ inline void NetworkTableEntry::ForceSetBooleanArray( SetEntryTypeValue(m_handle, Value::MakeBooleanArray(value)); } -inline void NetworkTableEntry::ForceSetDoubleArray(ArrayRef value) { +inline void NetworkTableEntry::ForceSetDoubleArray( + wpi::ArrayRef value) { SetEntryTypeValue(m_handle, Value::MakeDoubleArray(value)); } @@ -281,7 +286,7 @@ inline void NetworkTableEntry::ForceSetDoubleArray( } inline void NetworkTableEntry::ForceSetStringArray( - ArrayRef value) { + wpi::ArrayRef value) { SetEntryTypeValue(m_handle, Value::MakeStringArray(value)); } @@ -316,10 +321,10 @@ inline void NetworkTableEntry::Delete() { inline void NetworkTableEntry::CreateRpc( std::function callback) { - ::nt::CreateRpc(m_handle, StringRef("\0", 1), callback); + ::nt::CreateRpc(m_handle, wpi::StringRef("\0", 1), callback); } -inline RpcCall NetworkTableEntry::CallRpc(StringRef params) { +inline RpcCall NetworkTableEntry::CallRpc(wpi::StringRef params) { return RpcCall{m_handle, ::nt::CallRpc(m_handle, params)}; } diff --git a/ntcore/src/main/native/include/networktables/NetworkTableInstance.h b/ntcore/src/main/native/include/networktables/NetworkTableInstance.h index 7691edad2d..5284091419 100644 --- a/ntcore/src/main/native/include/networktables/NetworkTableInstance.h +++ b/ntcore/src/main/native/include/networktables/NetworkTableInstance.h @@ -22,10 +22,6 @@ namespace nt { -using wpi::ArrayRef; -using wpi::StringRef; -using wpi::Twine; - /** * NetworkTables Instance. * @@ -135,7 +131,7 @@ class NetworkTableInstance final { * @param name Key * @return Network table entry. */ - NetworkTableEntry GetEntry(const Twine& name); + NetworkTableEntry GetEntry(const wpi::Twine& name); /** * Get entries starting with the given prefix. @@ -148,7 +144,7 @@ class NetworkTableInstance final { * @param types bitmask of types; 0 is treated as a "don't care" * @return Array of entries. */ - std::vector GetEntries(const Twine& prefix, + std::vector GetEntries(const wpi::Twine& prefix, unsigned int types); /** @@ -162,7 +158,7 @@ class NetworkTableInstance final { * @param types bitmask of types; 0 is treated as a "don't care" * @return Array of entry information. */ - std::vector GetEntryInfo(const Twine& prefix, + std::vector GetEntryInfo(const wpi::Twine& prefix, unsigned int types) const; /** @@ -171,7 +167,7 @@ class NetworkTableInstance final { * @param key the key name * @return The network table */ - std::shared_ptr GetTable(const Twine& key) const; + std::shared_ptr GetTable(const wpi::Twine& key) const; /** * Deletes ALL keys in ALL subtables (except persistent values). @@ -193,7 +189,7 @@ class NetworkTableInstance final { * @return Listener handle */ NT_EntryListener AddEntryListener( - const Twine& prefix, + const wpi::Twine& prefix, std::function callback, unsigned int flags) const; @@ -287,7 +283,7 @@ class NetworkTableInstance final { * * @param name identity to advertise */ - void SetNetworkIdentity(const Twine& name); + void SetNetworkIdentity(const wpi::Twine& name); /** * Get the current network mode. @@ -318,7 +314,7 @@ class NetworkTableInstance final { * address (UTF-8 string, null terminated) * @param port port to communicate over */ - void StartServer(const Twine& persist_filename = "networktables.ini", + void StartServer(const wpi::Twine& persist_filename = "networktables.ini", const char* listen_address = "", unsigned int port = kDefaultPort); @@ -346,7 +342,8 @@ class NetworkTableInstance final { * * @param servers array of server name and port pairs */ - void StartClient(ArrayRef> servers); + void StartClient( + wpi::ArrayRef> servers); /** * Starts a client using the specified servers and port. The @@ -355,7 +352,7 @@ class NetworkTableInstance final { * @param servers array of server names * @param port port to communicate over */ - void StartClient(ArrayRef servers, + void StartClient(wpi::ArrayRef servers, unsigned int port = kDefaultPort); /** @@ -386,7 +383,8 @@ class NetworkTableInstance final { * * @param servers array of server name and port pairs */ - void SetServer(ArrayRef> servers); + void SetServer( + wpi::ArrayRef> servers); /** * Sets server addresses and port for client (without restarting client). @@ -395,7 +393,8 @@ class NetworkTableInstance final { * @param servers array of server names * @param port port to communicate over */ - void SetServer(ArrayRef servers, unsigned int port = kDefaultPort); + void SetServer(wpi::ArrayRef servers, + unsigned int port = kDefaultPort); /** * Sets server addresses and port for client (without restarting client). @@ -466,7 +465,7 @@ class NetworkTableInstance final { * @param filename filename * @return error string, or nullptr if successful */ - const char* SavePersistent(const Twine& filename) const; + const char* SavePersistent(const wpi::Twine& filename) const; /** * Load persistent values from a file. The server automatically does this @@ -478,7 +477,7 @@ class NetworkTableInstance final { * @return error string, or nullptr if successful */ const char* LoadPersistent( - const Twine& filename, + const wpi::Twine& filename, std::function warn); /** @@ -489,7 +488,8 @@ class NetworkTableInstance final { * @param prefix save only keys starting with this prefix * @return error string, or nullptr if successful */ - const char* SaveEntries(const Twine& filename, const Twine& prefix) const; + const char* SaveEntries(const wpi::Twine& filename, + const wpi::Twine& prefix) const; /** * Load table values from a file. The file format used is identical to @@ -501,7 +501,7 @@ class NetworkTableInstance final { * @return error string, or nullptr if successful */ const char* LoadEntries( - const Twine& filename, const Twine& prefix, + const wpi::Twine& filename, const wpi::Twine& prefix, std::function warn); /** @} */ diff --git a/ntcore/src/main/native/include/networktables/NetworkTableInstance.inl b/ntcore/src/main/native/include/networktables/NetworkTableInstance.inl index 9d80f642c0..ef8e9f3f86 100644 --- a/ntcore/src/main/native/include/networktables/NetworkTableInstance.inl +++ b/ntcore/src/main/native/include/networktables/NetworkTableInstance.inl @@ -35,12 +35,13 @@ inline NT_Inst NetworkTableInstance::GetHandle() const { return m_handle; } -inline NetworkTableEntry NetworkTableInstance::GetEntry(const Twine& name) { +inline NetworkTableEntry NetworkTableInstance::GetEntry( + const wpi::Twine& name) { return NetworkTableEntry{::nt::GetEntry(m_handle, name)}; } inline std::vector NetworkTableInstance::GetEntries( - const Twine& prefix, unsigned int types) { + const wpi::Twine& prefix, unsigned int types) { std::vector entries; for (auto entry : ::nt::GetEntries(m_handle, prefix, types)) { entries.emplace_back(entry); @@ -49,7 +50,7 @@ inline std::vector NetworkTableInstance::GetEntries( } inline std::vector NetworkTableInstance::GetEntryInfo( - const Twine& prefix, unsigned int types) const { + const wpi::Twine& prefix, unsigned int types) const { return ::nt::GetEntryInfo(m_handle, prefix, types); } @@ -80,7 +81,7 @@ inline bool NetworkTableInstance::WaitForRpcCallQueue(double timeout) { return ::nt::WaitForRpcCallQueue(m_handle, timeout); } -inline void NetworkTableInstance::SetNetworkIdentity(const Twine& name) { +inline void NetworkTableInstance::SetNetworkIdentity(const wpi::Twine& name) { ::nt::SetNetworkIdentity(m_handle, name); } @@ -96,9 +97,9 @@ inline void NetworkTableInstance::StopLocal() { ::nt::StopLocal(m_handle); } -inline void NetworkTableInstance::StartServer(const Twine& persist_filename, - const char* listen_address, - unsigned int port) { +inline void NetworkTableInstance::StartServer( + const wpi::Twine& persist_filename, const char* listen_address, + unsigned int port) { ::nt::StartServer(m_handle, persist_filename, listen_address, port); } @@ -116,7 +117,7 @@ inline void NetworkTableInstance::StartClient(const char* server_name, } inline void NetworkTableInstance::StartClient( - ArrayRef> servers) { + wpi::ArrayRef> servers) { ::nt::StartClient(m_handle, servers); } @@ -135,7 +136,7 @@ inline void NetworkTableInstance::SetServer(const char* server_name, } inline void NetworkTableInstance::SetServer( - ArrayRef> servers) { + wpi::ArrayRef> servers) { ::nt::SetServer(m_handle, servers); } @@ -170,23 +171,23 @@ inline bool NetworkTableInstance::IsConnected() const { } inline const char* NetworkTableInstance::SavePersistent( - const Twine& filename) const { + const wpi::Twine& filename) const { return ::nt::SavePersistent(m_handle, filename); } inline const char* NetworkTableInstance::LoadPersistent( - const Twine& filename, + const wpi::Twine& filename, std::function warn) { return ::nt::LoadPersistent(m_handle, filename, warn); } inline const char* NetworkTableInstance::SaveEntries( - const Twine& filename, const Twine& prefix) const { + const wpi::Twine& filename, const wpi::Twine& prefix) const { return ::nt::SaveEntries(m_handle, filename, prefix); } inline const char* NetworkTableInstance::LoadEntries( - const Twine& filename, const Twine& prefix, + const wpi::Twine& filename, const wpi::Twine& prefix, std::function warn) { return ::nt::LoadEntries(m_handle, filename, prefix, warn); } diff --git a/ntcore/src/main/native/include/networktables/NetworkTableValue.h b/ntcore/src/main/native/include/networktables/NetworkTableValue.h index 40881126d8..014bc326cd 100644 --- a/ntcore/src/main/native/include/networktables/NetworkTableValue.h +++ b/ntcore/src/main/native/include/networktables/NetworkTableValue.h @@ -23,10 +23,6 @@ namespace nt { -using wpi::ArrayRef; -using wpi::StringRef; -using wpi::Twine; - /** * A network table entry value. * @ingroup ntcore_cpp_api @@ -167,7 +163,7 @@ class Value final { * * @return The string value. */ - StringRef GetString() const { + wpi::StringRef GetString() const { assert(m_val.type == NT_STRING); return m_string; } @@ -177,7 +173,7 @@ class Value final { * * @return The raw value. */ - StringRef GetRaw() const { + wpi::StringRef GetRaw() const { assert(m_val.type == NT_RAW); return m_string; } @@ -187,7 +183,7 @@ class Value final { * * @return The rpc definition value. */ - StringRef GetRpc() const { + wpi::StringRef GetRpc() const { assert(m_val.type == NT_RPC); return m_string; } @@ -197,10 +193,10 @@ class Value final { * * @return The boolean array value. */ - ArrayRef GetBooleanArray() const { + wpi::ArrayRef GetBooleanArray() const { assert(m_val.type == NT_BOOLEAN_ARRAY); - return ArrayRef(m_val.data.arr_boolean.arr, - m_val.data.arr_boolean.size); + return wpi::ArrayRef(m_val.data.arr_boolean.arr, + m_val.data.arr_boolean.size); } /** @@ -208,10 +204,10 @@ class Value final { * * @return The double array value. */ - ArrayRef GetDoubleArray() const { + wpi::ArrayRef GetDoubleArray() const { assert(m_val.type == NT_DOUBLE_ARRAY); - return ArrayRef(m_val.data.arr_double.arr, - m_val.data.arr_double.size); + return wpi::ArrayRef(m_val.data.arr_double.arr, + m_val.data.arr_double.size); } /** @@ -219,7 +215,7 @@ class Value final { * * @return The string array value. */ - ArrayRef GetStringArray() const { + wpi::ArrayRef GetStringArray() const { assert(m_val.type == NT_STRING_ARRAY); return m_string_array; } @@ -267,7 +263,7 @@ class Value final { * time) * @return The entry value */ - static std::shared_ptr MakeString(const Twine& value, + static std::shared_ptr MakeString(const wpi::Twine& value, uint64_t time = 0) { auto val = std::make_shared(NT_STRING, time, private_init()); val->m_string = value.str(); @@ -302,7 +298,8 @@ class Value final { * time) * @return The entry value */ - static std::shared_ptr MakeRaw(StringRef value, uint64_t time = 0) { + static std::shared_ptr MakeRaw(wpi::StringRef value, + uint64_t time = 0) { auto val = std::make_shared(NT_RAW, time, private_init()); val->m_string = value; val->m_val.data.v_raw.str = const_cast(val->m_string.c_str()); @@ -336,7 +333,8 @@ class Value final { * time) * @return The entry value */ - static std::shared_ptr MakeRpc(StringRef value, uint64_t time = 0) { + static std::shared_ptr MakeRpc(wpi::StringRef value, + uint64_t time = 0) { auto val = std::make_shared(NT_RPC, time, private_init()); val->m_string = value; val->m_val.data.v_raw.str = const_cast(val->m_string.c_str()); @@ -369,7 +367,7 @@ class Value final { * time) * @return The entry value */ - static std::shared_ptr MakeBooleanArray(ArrayRef value, + static std::shared_ptr MakeBooleanArray(wpi::ArrayRef value, uint64_t time = 0); /** @@ -394,7 +392,7 @@ class Value final { * time) * @return The entry value */ - static std::shared_ptr MakeBooleanArray(ArrayRef value, + static std::shared_ptr MakeBooleanArray(wpi::ArrayRef value, uint64_t time = 0); /** @@ -419,7 +417,7 @@ class Value final { * time) * @return The entry value */ - static std::shared_ptr MakeDoubleArray(ArrayRef value, + static std::shared_ptr MakeDoubleArray(wpi::ArrayRef value, uint64_t time = 0); /** @@ -443,8 +441,8 @@ class Value final { * time) * @return The entry value */ - static std::shared_ptr MakeStringArray(ArrayRef value, - uint64_t time = 0); + static std::shared_ptr MakeStringArray( + wpi::ArrayRef value, uint64_t time = 0); /** * Creates a string array entry value. diff --git a/ntcore/src/main/native/include/networktables/TableEntryListener.h b/ntcore/src/main/native/include/networktables/TableEntryListener.h index cd73697224..4930bf0056 100644 --- a/ntcore/src/main/native/include/networktables/TableEntryListener.h +++ b/ntcore/src/main/native/include/networktables/TableEntryListener.h @@ -16,8 +16,6 @@ class NetworkTable; class NetworkTableEntry; class Value; -using wpi::StringRef; - /** * A listener that listens to changes in values in a NetworkTable. * @@ -32,7 +30,7 @@ using wpi::StringRef; * * @ingroup ntcore_cpp_api */ -typedef std::function value, int flags)> TableEntryListener; diff --git a/ntcore/src/main/native/include/networktables/TableListener.h b/ntcore/src/main/native/include/networktables/TableListener.h index 22b02f8965..3e8f8c70cd 100644 --- a/ntcore/src/main/native/include/networktables/TableListener.h +++ b/ntcore/src/main/native/include/networktables/TableListener.h @@ -14,8 +14,6 @@ namespace nt { class NetworkTable; -using wpi::StringRef; - /** * A listener that listens to new sub-tables in a NetworkTable. * @@ -27,7 +25,7 @@ using wpi::StringRef; * * @ingroup ntcore_cpp_api */ -typedef std::function table)> TableListener; diff --git a/ntcore/src/main/native/include/ntcore_cpp.h b/ntcore/src/main/native/include/ntcore_cpp.h index 46ac116f81..74bc1e8ac6 100644 --- a/ntcore/src/main/native/include/ntcore_cpp.h +++ b/ntcore/src/main/native/include/ntcore_cpp.h @@ -32,10 +32,6 @@ namespace nt { * @{ */ -using wpi::ArrayRef; -using wpi::StringRef; -using wpi::Twine; - /** NetworkTables Entry Information */ struct EntryInfo { /** Entry handle */ @@ -102,7 +98,7 @@ struct ConnectionInfo { /** NetworkTables RPC Version 1 Definition Parameter */ struct RpcParamDef { RpcParamDef() = default; - RpcParamDef(StringRef name_, std::shared_ptr def_value_) + RpcParamDef(wpi::StringRef name_, std::shared_ptr def_value_) : name(name_), def_value(std::move(def_value_)) {} std::string name; @@ -112,7 +108,8 @@ struct RpcParamDef { /** NetworkTables RPC Version 1 Definition Result */ struct RpcResultDef { RpcResultDef() = default; - RpcResultDef(StringRef name_, NT_Type type_) : name(name_), type(type_) {} + RpcResultDef(wpi::StringRef name_, NT_Type type_) + : name(name_), type(type_) {} std::string name; NT_Type type; @@ -130,8 +127,8 @@ struct RpcDefinition { class RpcAnswer { public: RpcAnswer() = default; - RpcAnswer(NT_Entry entry_, NT_RpcCall call_, StringRef name_, - StringRef params_, ConnectionInfo conn_) + RpcAnswer(NT_Entry entry_, NT_RpcCall call_, wpi::StringRef name_, + wpi::StringRef params_, ConnectionInfo conn_) : entry(entry_), call(call_), name(name_), @@ -164,7 +161,7 @@ class RpcAnswer { * @param result result raw data that will be provided to remote caller * @return True if posting the response is valid, otherwise false */ - bool PostResponse(StringRef result) const; + bool PostResponse(wpi::StringRef result) const; friend void swap(RpcAnswer& first, RpcAnswer& second) { using std::swap; @@ -181,7 +178,7 @@ class EntryNotification { public: EntryNotification() = default; EntryNotification(NT_EntryListener listener_, NT_Entry entry_, - StringRef name_, std::shared_ptr value_, + wpi::StringRef name_, std::shared_ptr value_, unsigned int flags_) : listener(listener_), entry(entry_), @@ -248,7 +245,7 @@ class LogMessage { public: LogMessage() = default; LogMessage(NT_Logger logger_, unsigned int level_, const char* filename_, - unsigned int line_, StringRef message_) + unsigned int line_, wpi::StringRef message_) : logger(logger_), level(level_), filename(filename_), @@ -330,7 +327,7 @@ NT_Inst GetInstanceFromHandle(NT_Handle handle); * @param name entry name (UTF-8 string) * @return entry handle */ -NT_Entry GetEntry(NT_Inst inst, const Twine& name); +NT_Entry GetEntry(NT_Inst inst, const wpi::Twine& name); /** * Get Entry Handles. @@ -346,7 +343,7 @@ NT_Entry GetEntry(NT_Inst inst, const Twine& name); * as a "don't care" * @return Array of entry handles. */ -std::vector GetEntries(NT_Inst inst, const Twine& prefix, +std::vector GetEntries(NT_Inst inst, const wpi::Twine& prefix, unsigned int types); /** @@ -487,7 +484,7 @@ void DeleteAllEntries(NT_Inst inst); * as a "don't care" * @return Array of entry information. */ -std::vector GetEntryInfo(NT_Inst inst, const Twine& prefix, +std::vector GetEntryInfo(NT_Inst inst, const wpi::Twine& prefix, unsigned int types); /** @@ -519,7 +516,7 @@ EntryInfo GetEntryInfo(NT_Entry entry); * @param flags update flags; for example, NT_NOTIFY_NEW if the key * did not previously exist */ -typedef std::function value, unsigned int flags)> EntryListenerCallback; @@ -533,7 +530,7 @@ typedef std::function callback, unsigned int flags); @@ -581,7 +578,7 @@ void DestroyEntryListenerPoller(NT_EntryListenerPoller poller); * @return Listener handle */ NT_EntryListener AddPolledEntryListener(NT_EntryListenerPoller poller, - const Twine& prefix, + const wpi::Twine& prefix, unsigned int flags); /** @@ -790,7 +787,7 @@ bool WaitForConnectionListenerQueue(NT_Inst inst, double timeout); * @param callback callback function; note the callback function must call * PostRpcResponse() to provide a response to the call */ -void CreateRpc(NT_Entry entry, StringRef def, +void CreateRpc(NT_Entry entry, wpi::StringRef def, std::function callback); /** @@ -823,7 +820,8 @@ void DestroyRpcCallPoller(NT_RpcCallPoller poller); * @param def RPC definition * @param poller poller handle */ -void CreatePolledRpc(NT_Entry entry, StringRef def, NT_RpcCallPoller poller); +void CreatePolledRpc(NT_Entry entry, wpi::StringRef def, + NT_RpcCallPoller poller); /** * Get the next incoming RPC call. This blocks until the next incoming RPC @@ -886,7 +884,7 @@ bool WaitForRpcCallQueue(NT_Inst inst, double timeout); * @param result result raw data that will be provided to remote caller * @return true if the response was posted, otherwise false */ -bool PostRpcResponse(NT_Entry entry, NT_RpcCall call, StringRef result); +bool PostRpcResponse(NT_Entry entry, NT_RpcCall call, wpi::StringRef result); /** * Call a RPC function. May be used on either the client or server. @@ -899,7 +897,7 @@ bool PostRpcResponse(NT_Entry entry, NT_RpcCall call, StringRef result); * @return RPC call handle (for use with GetRpcResult() or * CancelRpcResult()). */ -NT_RpcCall CallRpc(NT_Entry entry, StringRef params); +NT_RpcCall CallRpc(NT_Entry entry, wpi::StringRef params); /** * Get the result (return value) of a RPC call. This function blocks until @@ -950,7 +948,7 @@ std::string PackRpcDefinition(const RpcDefinition& def); * @param def RPC version 1 definition (output) * @return True if successfully unpacked, false otherwise. */ -bool UnpackRpcDefinition(StringRef packed, RpcDefinition* def); +bool UnpackRpcDefinition(wpi::StringRef packed, RpcDefinition* def); /** * Pack RPC values as required for RPC version 1 definition messages. @@ -958,7 +956,7 @@ bool UnpackRpcDefinition(StringRef packed, RpcDefinition* def); * @param values array of values to pack * @return Raw packed bytes. */ -std::string PackRpcValues(ArrayRef> values); +std::string PackRpcValues(wpi::ArrayRef> values); /** * Unpack RPC values as required for RPC version 1 definition messages. @@ -967,8 +965,8 @@ std::string PackRpcValues(ArrayRef> values); * @param types array of data types (as provided in the RPC definition) * @return Array of values. */ -std::vector> UnpackRpcValues(StringRef packed, - ArrayRef types); +std::vector> UnpackRpcValues( + wpi::StringRef packed, wpi::ArrayRef types); /** @} */ @@ -985,7 +983,7 @@ std::vector> UnpackRpcValues(StringRef packed, * @param inst instance handle * @param name identity to advertise */ -void SetNetworkIdentity(NT_Inst inst, const Twine& name); +void SetNetworkIdentity(NT_Inst inst, const wpi::Twine& name); /** * Get the current network mode. @@ -1018,7 +1016,7 @@ void StopLocal(NT_Inst inst); * address. (UTF-8 string, null terminated) * @param port port to communicate over. */ -void StartServer(NT_Inst inst, const Twine& persist_filename, +void StartServer(NT_Inst inst, const wpi::Twine& persist_filename, const char* listen_address, unsigned int port); /** @@ -1051,8 +1049,9 @@ void StartClient(NT_Inst inst, const char* server_name, unsigned int port); * @param inst instance handle * @param servers array of server name and port pairs */ -void StartClient(NT_Inst inst, - ArrayRef> servers); +void StartClient( + NT_Inst inst, + wpi::ArrayRef> servers); /** * Starts a client using commonly known robot addresses for the specified @@ -1088,7 +1087,7 @@ void SetServer(NT_Inst inst, const char* server_name, unsigned int port); * @param servers array of server name and port pairs */ void SetServer(NT_Inst inst, - ArrayRef> servers); + wpi::ArrayRef> servers); /** * Sets server addresses and port for client (without restarting client). @@ -1174,7 +1173,7 @@ bool IsConnected(NT_Inst inst); * @param filename filename * @return error string, or nullptr if successful */ -const char* SavePersistent(NT_Inst inst, const Twine& filename); +const char* SavePersistent(NT_Inst inst, const wpi::Twine& filename); /** * Load persistent values from a file. The server automatically does this @@ -1187,7 +1186,7 @@ const char* SavePersistent(NT_Inst inst, const Twine& filename); * @return error string, or nullptr if successful */ const char* LoadPersistent( - NT_Inst inst, const Twine& filename, + NT_Inst inst, const wpi::Twine& filename, std::function warn); /** @@ -1199,8 +1198,8 @@ const char* LoadPersistent( * @param prefix save only keys starting with this prefix * @return error string, or nullptr if successful */ -const char* SaveEntries(NT_Inst inst, const Twine& filename, - const Twine& prefix); +const char* SaveEntries(NT_Inst inst, const wpi::Twine& filename, + const wpi::Twine& prefix); /** * Load table values from a file. The file format used is identical to @@ -1212,8 +1211,8 @@ const char* SaveEntries(NT_Inst inst, const Twine& filename, * @param warn callback function for warnings * @return error string, or nullptr if successful */ -const char* LoadEntries(NT_Inst inst, const Twine& filename, - const Twine& prefix, +const char* LoadEntries(NT_Inst inst, const wpi::Twine& filename, + const wpi::Twine& prefix, std::function warn); /** @} */ @@ -1340,7 +1339,7 @@ bool WaitForLoggerQueue(NT_Inst inst, double timeout); /** @} */ /** @} */ -inline bool RpcAnswer::PostResponse(StringRef result) const { +inline bool RpcAnswer::PostResponse(wpi::StringRef result) const { auto ret = PostRpcResponse(entry, call, result); call = 0; return ret; diff --git a/ntcore/src/test/native/cpp/EntryNotifierTest.cpp b/ntcore/src/test/native/cpp/EntryNotifierTest.cpp index 3f361fb08d..54285dc873 100644 --- a/ntcore/src/test/native/cpp/EntryNotifierTest.cpp +++ b/ntcore/src/test/native/cpp/EntryNotifierTest.cpp @@ -245,7 +245,7 @@ TEST_F(EntryNotifierTest, PollPrefixBasic) { int g4count = 0; for (const auto& result : results) { SCOPED_TRACE(::testing::PrintToString(result)); - EXPECT_TRUE(StringRef(result.name).startswith("/foo")); + EXPECT_TRUE(wpi::StringRef(result.name).startswith("/foo")); EXPECT_THAT(result.value, ValueEq(Value::MakeDouble(1))); EXPECT_EQ(Handle{result.entry}.GetType(), Handle::kEntry); EXPECT_EQ(Handle{result.entry}.GetInst(), 1); diff --git a/ntcore/src/test/native/cpp/MockEntryNotifier.h b/ntcore/src/test/native/cpp/MockEntryNotifier.h index fb608ae2a2..75e5cc3830 100644 --- a/ntcore/src/test/native/cpp/MockEntryNotifier.h +++ b/ntcore/src/test/native/cpp/MockEntryNotifier.h @@ -30,7 +30,7 @@ class MockEntryNotifier : public IEntryNotifier { unsigned int(unsigned int poller_uid, unsigned int local_id, unsigned int flags)); MOCK_METHOD5(NotifyEntry, - void(unsigned int local_id, StringRef name, + void(unsigned int local_id, wpi::StringRef name, std::shared_ptr value, unsigned int flags, unsigned int only_listener)); }; diff --git a/ntcore/src/test/native/cpp/MockRpcServer.h b/ntcore/src/test/native/cpp/MockRpcServer.h index 10acd3c6c6..4196008381 100644 --- a/ntcore/src/test/native/cpp/MockRpcServer.h +++ b/ntcore/src/test/native/cpp/MockRpcServer.h @@ -16,7 +16,7 @@ class MockRpcServer : public IRpcServer { MOCK_METHOD1(RemoveRpc, void(unsigned int rpc_uid)); MOCK_METHOD7(ProcessRpc, void(unsigned int local_id, unsigned int call_uid, - StringRef name, StringRef params, + wpi::StringRef name, wpi::StringRef params, const ConnectionInfo& conn, SendResponseFunc send_response, unsigned int rpc_uid)); }; diff --git a/ntcore/src/test/native/cpp/StorageTest.cpp b/ntcore/src/test/native/cpp/StorageTest.cpp index 2cc9a9c4c4..e8c80539ce 100644 --- a/ntcore/src/test/native/cpp/StorageTest.cpp +++ b/ntcore/src/test/native/cpp/StorageTest.cpp @@ -19,6 +19,7 @@ using ::testing::_; using ::testing::AnyNumber; using ::testing::IsNull; using ::testing::Return; +using wpi::StringRef; namespace nt { diff --git a/ntcore/src/test/native/cpp/StorageTest.h b/ntcore/src/test/native/cpp/StorageTest.h index 20901e1f4e..782a9f1edb 100644 --- a/ntcore/src/test/native/cpp/StorageTest.h +++ b/ntcore/src/test/native/cpp/StorageTest.h @@ -24,7 +24,7 @@ class StorageTest { Storage::EntriesMap& entries() { return storage.m_entries; } Storage::IdMap& idmap() { return storage.m_idmap; } - Storage::Entry* GetEntry(StringRef name) { + Storage::Entry* GetEntry(wpi::StringRef name) { auto i = storage.m_entries.find(name); return i == storage.m_entries.end() ? &tmp_entry : i->getValue(); } From 1dc81669c22c249e0ee05e325129df12c471d216 Mon Sep 17 00:00:00 2001 From: Peter Johnson Date: Sat, 10 Apr 2021 10:28:05 -0700 Subject: [PATCH 16/34] [wpilib] Remove GearTooth (#3293) This sensor has had zero usage for many years and was last in the KOP over a decade ago. There are much better rotation sensors available, and it's no longer worth maintaining this class. --- wpilibc/src/main/native/cpp/GearTooth.cpp | 43 -------- .../src/main/native/include/frc/GearTooth.h | 85 ---------------- .../java/edu/wpi/first/wpilibj/GearTooth.java | 97 ------------------- 3 files changed, 225 deletions(-) delete mode 100644 wpilibc/src/main/native/cpp/GearTooth.cpp delete mode 100644 wpilibc/src/main/native/include/frc/GearTooth.h delete mode 100644 wpilibj/src/main/java/edu/wpi/first/wpilibj/GearTooth.java diff --git a/wpilibc/src/main/native/cpp/GearTooth.cpp b/wpilibc/src/main/native/cpp/GearTooth.cpp deleted file mode 100644 index 59f5397b0b..0000000000 --- a/wpilibc/src/main/native/cpp/GearTooth.cpp +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright (c) FIRST and other WPILib contributors. -// Open Source Software; you can modify and/or share it under the terms of -// the WPILib BSD license file in the root directory of this project. - -#include "frc/GearTooth.h" - -#include "frc/smartdashboard/SendableBuilder.h" -#include "frc/smartdashboard/SendableRegistry.h" - -using namespace frc; - -constexpr double GearTooth::kGearToothThreshold; - -GearTooth::GearTooth(int channel, bool directionSensitive) : Counter(channel) { - EnableDirectionSensing(directionSensitive); - SendableRegistry::GetInstance().SetName(this, "GearTooth", channel); -} - -GearTooth::GearTooth(DigitalSource* source, bool directionSensitive) - : Counter(source) { - EnableDirectionSensing(directionSensitive); - SendableRegistry::GetInstance().SetName(this, "GearTooth", - source->GetChannel()); -} - -GearTooth::GearTooth(std::shared_ptr source, - bool directionSensitive) - : Counter(source) { - EnableDirectionSensing(directionSensitive); - SendableRegistry::GetInstance().SetName(this, "GearTooth", - source->GetChannel()); -} - -void GearTooth::EnableDirectionSensing(bool directionSensitive) { - if (directionSensitive) { - SetPulseLengthMode(kGearToothThreshold); - } -} - -void GearTooth::InitSendable(SendableBuilder& builder) { - Counter::InitSendable(builder); - builder.SetSmartDashboardType("Gear Tooth"); -} diff --git a/wpilibc/src/main/native/include/frc/GearTooth.h b/wpilibc/src/main/native/include/frc/GearTooth.h deleted file mode 100644 index 1df5699c6a..0000000000 --- a/wpilibc/src/main/native/include/frc/GearTooth.h +++ /dev/null @@ -1,85 +0,0 @@ -// Copyright (c) FIRST and other WPILib contributors. -// Open Source Software; you can modify and/or share it under the terms of -// the WPILib BSD license file in the root directory of this project. - -#pragma once - -#include -#include - -#include - -#include "frc/Counter.h" - -namespace frc { - -/** - * Alias for counter class. - * - * Implements the gear tooth sensor supplied by FIRST. Currently there is no - * reverse sensing on the gear tooth sensor, but in future versions we might - * implement the necessary timing in the FPGA to sense direction. - * - * @deprecated No longer used per FMS usage reporting - */ -class GearTooth : public Counter { - public: - // 55 uSec for threshold - static constexpr double kGearToothThreshold = 55e-6; - - /** - * Construct a GearTooth sensor given a channel. - * - * @param channel The DIO channel that the sensor is connected to. - * 0-9 are on-board, 10-25 are on the MXP. - * @param directionSensitive True to enable the pulse length decoding in - * hardware to specify count direction. - */ - WPI_DEPRECATED( - "The only sensor this works with is no longer available and no teams use " - "it according to FMS usage reporting.") - explicit GearTooth(int channel, bool directionSensitive = false); - - /** - * Construct a GearTooth sensor given a digital input. - * - * This should be used when sharing digital inputs. - * - * @param source A pointer to the existing DigitalSource object - * (such as a DigitalInput) - * @param directionSensitive True to enable the pulse length decoding in - * hardware to specify count direction. - */ - WPI_DEPRECATED( - "The only sensor this works with is no longer available and no teams use " - "it according to FMS usage reporting.") - explicit GearTooth(DigitalSource* source, bool directionSensitive = false); - - /** - * Construct a GearTooth sensor given a digital input. - * - * This should be used when sharing digital inputs. - * - * @param source A reference to the existing DigitalSource object - * (such as a DigitalInput) - * @param directionSensitive True to enable the pulse length decoding in - * hardware to specify count direction. - */ - WPI_DEPRECATED( - "The only sensor this works with is no longer available and no teams use " - "it according to FMS usage reporting.") - explicit GearTooth(std::shared_ptr source, - bool directionSensitive = false); - - GearTooth(GearTooth&&) = default; - GearTooth& operator=(GearTooth&&) = default; - - /** - * Common code called by the constructors. - */ - void EnableDirectionSensing(bool directionSensitive); - - void InitSendable(SendableBuilder& builder) override; -}; - -} // namespace frc diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/GearTooth.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/GearTooth.java deleted file mode 100644 index 15a999c4b0..0000000000 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/GearTooth.java +++ /dev/null @@ -1,97 +0,0 @@ -// Copyright (c) FIRST and other WPILib contributors. -// Open Source Software; you can modify and/or share it under the terms of -// the WPILib BSD license file in the root directory of this project. - -package edu.wpi.first.wpilibj; - -import edu.wpi.first.hal.FRCNetComm.tResourceType; -import edu.wpi.first.hal.HAL; -import edu.wpi.first.wpilibj.smartdashboard.SendableBuilder; -import edu.wpi.first.wpilibj.smartdashboard.SendableRegistry; - -/** - * Alias for counter class. Implement the gear tooth sensor supplied by FIRST. Currently there is no - * reverse sensing on the gear tooth sensor, but in future versions we might implement the necessary - * timing in the FPGA to sense direction. - * - * @deprecated The only sensor this works with is no longer available and no teams use it according - * to FMS usage reporting. - */ -@Deprecated(since = "2020", forRemoval = true) -public class GearTooth extends Counter { - private static final double kGearToothThreshold = 55e-6; - - /** Common code called by the constructors. */ - public void enableDirectionSensing(boolean directionSensitive) { - if (directionSensitive) { - setPulseLengthMode(kGearToothThreshold); - } - } - - /** - * Construct a GearTooth sensor given a channel. - * - *

No direction sensing is assumed. - * - * @param channel The GPIO channel that the sensor is connected to. - */ - public GearTooth(final int channel) { - this(channel, false); - } - - /** - * Construct a GearTooth sensor given a channel. - * - * @param channel The DIO channel that the sensor is connected to. 0-9 are on-board, 10-25 are on - * the MXP port - * @param directionSensitive True to enable the pulse length decoding in hardware to specify count - * direction. - */ - public GearTooth(final int channel, boolean directionSensitive) { - super(channel); - enableDirectionSensing(directionSensitive); - if (directionSensitive) { - HAL.report(tResourceType.kResourceType_GearTooth, channel + 1, 0, "D"); - } else { - HAL.report(tResourceType.kResourceType_GearTooth, channel + 1, 0); - } - SendableRegistry.setName(this, "GearTooth", channel); - } - - /** - * Construct a GearTooth sensor given a digital input. This should be used when sharing digital - * inputs. - * - * @param source An existing DigitalSource object (such as a DigitalInput) - * @param directionSensitive True to enable the pulse length decoding in hardware to specify count - * direction. - */ - public GearTooth(DigitalSource source, boolean directionSensitive) { - super(source); - enableDirectionSensing(directionSensitive); - if (directionSensitive) { - HAL.report(tResourceType.kResourceType_GearTooth, source.getChannel() + 1, 0, "D"); - } else { - HAL.report(tResourceType.kResourceType_GearTooth, source.getChannel() + 1, 0); - } - SendableRegistry.setName(this, "GearTooth", source.getChannel()); - } - - /** - * Construct a GearTooth sensor given a digital input. This should be used when sharing digital - * inputs. - * - *

No direction sensing is assumed. - * - * @param source An object that fully describes the input that the sensor is connected to. - */ - public GearTooth(DigitalSource source) { - this(source, false); - } - - @Override - public void initSendable(SendableBuilder builder) { - super.initSendable(builder); - builder.setSmartDashboardType("Gear Tooth"); - } -} From d7fabe81fe4b19ae8da5f93bc6d67383fa6361d0 Mon Sep 17 00:00:00 2001 From: Peter Johnson Date: Sat, 10 Apr 2021 10:28:32 -0700 Subject: [PATCH 17/34] [wpilib] Remove RobotDrive (#3295) This has been deprecated for several years, and its functionality has been completely superseded by other drive classes (DifferentialDrive et al). --- wpilibc/src/main/native/cpp/RobotDrive.cpp | 444 ----------- .../src/main/native/include/frc/RobotDrive.h | 461 ----------- .../include/frc/drive/DifferentialDrive.h | 6 +- .../native/include/frc/drive/MecanumDrive.h | 4 +- .../src/test/native/cpp/drive/DriveTest.cpp | 187 ----- .../edu/wpi/first/wpilibj/RobotDrive.java | 719 ------------------ .../wpilibj/drive/DifferentialDrive.java | 15 +- .../wpi/first/wpilibj/drive/MecanumDrive.java | 13 +- .../wpi/first/wpilibj/drive/DriveTest.java | 265 ------- .../wpi/first/wpilibj/examples/examples.json | 4 +- .../first/wpilibj/examples/gyro/Robot.java | 4 +- .../wpilibj/examples/mecanumdrive/Robot.java | 2 +- .../wpilibj/examples/tankdrive/Robot.java | 4 +- 13 files changed, 25 insertions(+), 2103 deletions(-) delete mode 100644 wpilibc/src/main/native/cpp/RobotDrive.cpp delete mode 100644 wpilibc/src/main/native/include/frc/RobotDrive.h delete mode 100644 wpilibc/src/test/native/cpp/drive/DriveTest.cpp delete mode 100644 wpilibj/src/main/java/edu/wpi/first/wpilibj/RobotDrive.java delete mode 100644 wpilibj/src/test/java/edu/wpi/first/wpilibj/drive/DriveTest.java diff --git a/wpilibc/src/main/native/cpp/RobotDrive.cpp b/wpilibc/src/main/native/cpp/RobotDrive.cpp deleted file mode 100644 index 8293282873..0000000000 --- a/wpilibc/src/main/native/cpp/RobotDrive.cpp +++ /dev/null @@ -1,444 +0,0 @@ -// Copyright (c) FIRST and other WPILib contributors. -// Open Source Software; you can modify and/or share it under the terms of -// the WPILib BSD license file in the root directory of this project. - -#include "frc/RobotDrive.h" - -#include -#include - -#include - -#include "frc/GenericHID.h" -#include "frc/Joystick.h" -#include "frc/Talon.h" -#include "frc/Utility.h" -#include "frc/WPIErrors.h" - -using namespace frc; - -static std::shared_ptr make_shared_nodelete( - SpeedController* ptr) { - return std::shared_ptr(ptr, NullDeleter()); -} - -RobotDrive::RobotDrive(int leftMotorChannel, int rightMotorChannel) { - InitRobotDrive(); - m_rearLeftMotor = std::make_shared(leftMotorChannel); - m_rearRightMotor = std::make_shared(rightMotorChannel); - SetLeftRightMotorOutputs(0.0, 0.0); -} - -RobotDrive::RobotDrive(int frontLeftMotor, int rearLeftMotor, - int frontRightMotor, int rearRightMotor) { - InitRobotDrive(); - m_rearLeftMotor = std::make_shared(rearLeftMotor); - m_rearRightMotor = std::make_shared(rearRightMotor); - m_frontLeftMotor = std::make_shared(frontLeftMotor); - m_frontRightMotor = std::make_shared(frontRightMotor); - SetLeftRightMotorOutputs(0.0, 0.0); -} - -RobotDrive::RobotDrive(SpeedController* leftMotor, - SpeedController* rightMotor) { - InitRobotDrive(); - if (leftMotor == nullptr || rightMotor == nullptr) { - wpi_setWPIError(NullParameter); - m_rearLeftMotor = m_rearRightMotor = nullptr; - return; - } - m_rearLeftMotor = make_shared_nodelete(leftMotor); - m_rearRightMotor = make_shared_nodelete(rightMotor); -} - -RobotDrive::RobotDrive(SpeedController& leftMotor, - SpeedController& rightMotor) { - InitRobotDrive(); - m_rearLeftMotor = make_shared_nodelete(&leftMotor); - m_rearRightMotor = make_shared_nodelete(&rightMotor); -} - -RobotDrive::RobotDrive(std::shared_ptr leftMotor, - std::shared_ptr rightMotor) { - InitRobotDrive(); - if (leftMotor == nullptr || rightMotor == nullptr) { - wpi_setWPIError(NullParameter); - m_rearLeftMotor = m_rearRightMotor = nullptr; - return; - } - m_rearLeftMotor = leftMotor; - m_rearRightMotor = rightMotor; -} - -RobotDrive::RobotDrive(SpeedController* frontLeftMotor, - SpeedController* rearLeftMotor, - SpeedController* frontRightMotor, - SpeedController* rearRightMotor) { - InitRobotDrive(); - if (frontLeftMotor == nullptr || rearLeftMotor == nullptr || - frontRightMotor == nullptr || rearRightMotor == nullptr) { - wpi_setWPIError(NullParameter); - return; - } - m_frontLeftMotor = make_shared_nodelete(frontLeftMotor); - m_rearLeftMotor = make_shared_nodelete(rearLeftMotor); - m_frontRightMotor = make_shared_nodelete(frontRightMotor); - m_rearRightMotor = make_shared_nodelete(rearRightMotor); -} - -RobotDrive::RobotDrive(SpeedController& frontLeftMotor, - SpeedController& rearLeftMotor, - SpeedController& frontRightMotor, - SpeedController& rearRightMotor) { - InitRobotDrive(); - m_frontLeftMotor = make_shared_nodelete(&frontLeftMotor); - m_rearLeftMotor = make_shared_nodelete(&rearLeftMotor); - m_frontRightMotor = make_shared_nodelete(&frontRightMotor); - m_rearRightMotor = make_shared_nodelete(&rearRightMotor); -} - -RobotDrive::RobotDrive(std::shared_ptr frontLeftMotor, - std::shared_ptr rearLeftMotor, - std::shared_ptr frontRightMotor, - std::shared_ptr rearRightMotor) { - InitRobotDrive(); - if (frontLeftMotor == nullptr || rearLeftMotor == nullptr || - frontRightMotor == nullptr || rearRightMotor == nullptr) { - wpi_setWPIError(NullParameter); - return; - } - m_frontLeftMotor = frontLeftMotor; - m_rearLeftMotor = rearLeftMotor; - m_frontRightMotor = frontRightMotor; - m_rearRightMotor = rearRightMotor; -} - -void RobotDrive::Drive(double outputMagnitude, double curve) { - double leftOutput, rightOutput; - static bool reported = false; - if (!reported) { - HAL_Report(HALUsageReporting::kResourceType_RobotDrive, - HALUsageReporting::kRobotDrive_ArcadeRatioCurve, GetNumMotors()); - reported = true; - } - - if (curve < 0) { - double value = std::log(-curve); - double ratio = (value - m_sensitivity) / (value + m_sensitivity); - if (ratio == 0) { - ratio = 0.0000000001; - } - leftOutput = outputMagnitude / ratio; - rightOutput = outputMagnitude; - } else if (curve > 0) { - double value = std::log(curve); - double ratio = (value - m_sensitivity) / (value + m_sensitivity); - if (ratio == 0) { - ratio = 0.0000000001; - } - leftOutput = outputMagnitude; - rightOutput = outputMagnitude / ratio; - } else { - leftOutput = outputMagnitude; - rightOutput = outputMagnitude; - } - SetLeftRightMotorOutputs(leftOutput, rightOutput); -} - -void RobotDrive::TankDrive(GenericHID* leftStick, GenericHID* rightStick, - bool squaredInputs) { - if (leftStick == nullptr || rightStick == nullptr) { - wpi_setWPIError(NullParameter); - return; - } - TankDrive(leftStick->GetY(), rightStick->GetY(), squaredInputs); -} - -void RobotDrive::TankDrive(GenericHID& leftStick, GenericHID& rightStick, - bool squaredInputs) { - TankDrive(leftStick.GetY(), rightStick.GetY(), squaredInputs); -} - -void RobotDrive::TankDrive(GenericHID* leftStick, int leftAxis, - GenericHID* rightStick, int rightAxis, - bool squaredInputs) { - if (leftStick == nullptr || rightStick == nullptr) { - wpi_setWPIError(NullParameter); - return; - } - TankDrive(leftStick->GetRawAxis(leftAxis), rightStick->GetRawAxis(rightAxis), - squaredInputs); -} - -void RobotDrive::TankDrive(GenericHID& leftStick, int leftAxis, - GenericHID& rightStick, int rightAxis, - bool squaredInputs) { - TankDrive(leftStick.GetRawAxis(leftAxis), rightStick.GetRawAxis(rightAxis), - squaredInputs); -} - -void RobotDrive::TankDrive(double leftValue, double rightValue, - bool squaredInputs) { - static bool reported = false; - if (!reported) { - HAL_Report(HALUsageReporting::kResourceType_RobotDrive, - HALUsageReporting::kRobotDrive_Tank, GetNumMotors()); - reported = true; - } - - leftValue = Limit(leftValue); - rightValue = Limit(rightValue); - - // square the inputs (while preserving the sign) to increase fine control - // while permitting full power - if (squaredInputs) { - leftValue = std::copysign(leftValue * leftValue, leftValue); - rightValue = std::copysign(rightValue * rightValue, rightValue); - } - - SetLeftRightMotorOutputs(leftValue, rightValue); -} - -void RobotDrive::ArcadeDrive(GenericHID* stick, bool squaredInputs) { - // simply call the full-featured ArcadeDrive with the appropriate values - ArcadeDrive(stick->GetY(), stick->GetX(), squaredInputs); -} - -void RobotDrive::ArcadeDrive(GenericHID& stick, bool squaredInputs) { - // simply call the full-featured ArcadeDrive with the appropriate values - ArcadeDrive(stick.GetY(), stick.GetX(), squaredInputs); -} - -void RobotDrive::ArcadeDrive(GenericHID* moveStick, int moveAxis, - GenericHID* rotateStick, int rotateAxis, - bool squaredInputs) { - double moveValue = moveStick->GetRawAxis(moveAxis); - double rotateValue = rotateStick->GetRawAxis(rotateAxis); - - ArcadeDrive(moveValue, rotateValue, squaredInputs); -} - -void RobotDrive::ArcadeDrive(GenericHID& moveStick, int moveAxis, - GenericHID& rotateStick, int rotateAxis, - bool squaredInputs) { - double moveValue = moveStick.GetRawAxis(moveAxis); - double rotateValue = rotateStick.GetRawAxis(rotateAxis); - - ArcadeDrive(moveValue, rotateValue, squaredInputs); -} - -void RobotDrive::ArcadeDrive(double moveValue, double rotateValue, - bool squaredInputs) { - static bool reported = false; - if (!reported) { - HAL_Report(HALUsageReporting::kResourceType_RobotDrive, - HALUsageReporting::kRobotDrive_ArcadeStandard, GetNumMotors()); - reported = true; - } - - // local variables to hold the computed PWM values for the motors - double leftMotorOutput; - double rightMotorOutput; - - moveValue = Limit(moveValue); - rotateValue = Limit(rotateValue); - - // square the inputs (while preserving the sign) to increase fine control - // while permitting full power - if (squaredInputs) { - moveValue = std::copysign(moveValue * moveValue, moveValue); - rotateValue = std::copysign(rotateValue * rotateValue, rotateValue); - } - - if (moveValue > 0.0) { - if (rotateValue > 0.0) { - leftMotorOutput = moveValue - rotateValue; - rightMotorOutput = std::max(moveValue, rotateValue); - } else { - leftMotorOutput = std::max(moveValue, -rotateValue); - rightMotorOutput = moveValue + rotateValue; - } - } else { - if (rotateValue > 0.0) { - leftMotorOutput = -std::max(-moveValue, rotateValue); - rightMotorOutput = moveValue + rotateValue; - } else { - leftMotorOutput = moveValue - rotateValue; - rightMotorOutput = -std::max(-moveValue, -rotateValue); - } - } - SetLeftRightMotorOutputs(leftMotorOutput, rightMotorOutput); -} - -void RobotDrive::MecanumDrive_Cartesian(double x, double y, double rotation, - double gyroAngle) { - static bool reported = false; - if (!reported) { - HAL_Report(HALUsageReporting::kResourceType_RobotDrive, - HALUsageReporting::kRobotDrive_MecanumCartesian, GetNumMotors()); - reported = true; - } - - double xIn = x; - double yIn = y; - // Negate y for the joystick. - yIn = -yIn; - // Compensate for gyro angle. - RotateVector(xIn, yIn, gyroAngle); - - double wheelSpeeds[kMaxNumberOfMotors]; - wheelSpeeds[kFrontLeftMotor] = xIn + yIn + rotation; - wheelSpeeds[kFrontRightMotor] = -xIn + yIn - rotation; - wheelSpeeds[kRearLeftMotor] = -xIn + yIn + rotation; - wheelSpeeds[kRearRightMotor] = xIn + yIn - rotation; - - Normalize(wheelSpeeds); - - m_frontLeftMotor->Set(wheelSpeeds[kFrontLeftMotor] * m_maxOutput); - m_frontRightMotor->Set(wheelSpeeds[kFrontRightMotor] * m_maxOutput); - m_rearLeftMotor->Set(wheelSpeeds[kRearLeftMotor] * m_maxOutput); - m_rearRightMotor->Set(wheelSpeeds[kRearRightMotor] * m_maxOutput); - - Feed(); -} - -void RobotDrive::MecanumDrive_Polar(double magnitude, double direction, - double rotation) { - static bool reported = false; - if (!reported) { - HAL_Report(HALUsageReporting::kResourceType_RobotDrive, - HALUsageReporting::kRobotDrive_MecanumPolar, GetNumMotors()); - reported = true; - } - - // Normalized for full power along the Cartesian axes. - magnitude = Limit(magnitude) * std::sqrt(2.0); - // The rollers are at 45 degree angles. - double dirInRad = (direction + 45.0) * 3.14159 / 180.0; - double cosD = std::cos(dirInRad); - double sinD = std::sin(dirInRad); - - double wheelSpeeds[kMaxNumberOfMotors]; - wheelSpeeds[kFrontLeftMotor] = sinD * magnitude + rotation; - wheelSpeeds[kFrontRightMotor] = cosD * magnitude - rotation; - wheelSpeeds[kRearLeftMotor] = cosD * magnitude + rotation; - wheelSpeeds[kRearRightMotor] = sinD * magnitude - rotation; - - Normalize(wheelSpeeds); - - m_frontLeftMotor->Set(wheelSpeeds[kFrontLeftMotor] * m_maxOutput); - m_frontRightMotor->Set(wheelSpeeds[kFrontRightMotor] * m_maxOutput); - m_rearLeftMotor->Set(wheelSpeeds[kRearLeftMotor] * m_maxOutput); - m_rearRightMotor->Set(wheelSpeeds[kRearRightMotor] * m_maxOutput); - - Feed(); -} - -void RobotDrive::HolonomicDrive(double magnitude, double direction, - double rotation) { - MecanumDrive_Polar(magnitude, direction, rotation); -} - -void RobotDrive::SetLeftRightMotorOutputs(double leftOutput, - double rightOutput) { - wpi_assert(m_rearLeftMotor != nullptr && m_rearRightMotor != nullptr); - - if (m_frontLeftMotor != nullptr) { - m_frontLeftMotor->Set(Limit(leftOutput) * m_maxOutput); - } - m_rearLeftMotor->Set(Limit(leftOutput) * m_maxOutput); - - if (m_frontRightMotor != nullptr) { - m_frontRightMotor->Set(-Limit(rightOutput) * m_maxOutput); - } - m_rearRightMotor->Set(-Limit(rightOutput) * m_maxOutput); - - Feed(); -} - -void RobotDrive::SetInvertedMotor(MotorType motor, bool isInverted) { - if (motor < 0 || motor > 3) { - wpi_setWPIError(InvalidMotorIndex); - return; - } - switch (motor) { - case kFrontLeftMotor: - m_frontLeftMotor->SetInverted(isInverted); - break; - case kFrontRightMotor: - m_frontRightMotor->SetInverted(isInverted); - break; - case kRearLeftMotor: - m_rearLeftMotor->SetInverted(isInverted); - break; - case kRearRightMotor: - m_rearRightMotor->SetInverted(isInverted); - break; - } -} - -void RobotDrive::SetSensitivity(double sensitivity) { - m_sensitivity = sensitivity; -} - -void RobotDrive::SetMaxOutput(double maxOutput) { - m_maxOutput = maxOutput; -} - -void RobotDrive::GetDescription(wpi::raw_ostream& desc) const { - desc << "RobotDrive"; -} - -void RobotDrive::StopMotor() { - if (m_frontLeftMotor != nullptr) { - m_frontLeftMotor->StopMotor(); - } - if (m_frontRightMotor != nullptr) { - m_frontRightMotor->StopMotor(); - } - if (m_rearLeftMotor != nullptr) { - m_rearLeftMotor->StopMotor(); - } - if (m_rearRightMotor != nullptr) { - m_rearRightMotor->StopMotor(); - } - Feed(); -} - -void RobotDrive::InitRobotDrive() { - SetSafetyEnabled(true); -} - -double RobotDrive::Limit(double number) { - if (number > 1.0) { - return 1.0; - } - if (number < -1.0) { - return -1.0; - } - return number; -} - -void RobotDrive::Normalize(double* wheelSpeeds) { - double maxMagnitude = std::fabs(wheelSpeeds[0]); - for (int i = 1; i < kMaxNumberOfMotors; i++) { - double temp = std::fabs(wheelSpeeds[i]); - if (maxMagnitude < temp) { - maxMagnitude = temp; - } - } - if (maxMagnitude > 1.0) { - for (int i = 0; i < kMaxNumberOfMotors; i++) { - wheelSpeeds[i] = wheelSpeeds[i] / maxMagnitude; - } - } -} - -void RobotDrive::RotateVector(double& x, double& y, double angle) { - double cosA = std::cos(angle * (3.14159 / 180.0)); - double sinA = std::sin(angle * (3.14159 / 180.0)); - double xOut = x * cosA - y * sinA; - double yOut = x * sinA + y * cosA; - x = xOut; - y = yOut; -} diff --git a/wpilibc/src/main/native/include/frc/RobotDrive.h b/wpilibc/src/main/native/include/frc/RobotDrive.h deleted file mode 100644 index fa22e0e8cc..0000000000 --- a/wpilibc/src/main/native/include/frc/RobotDrive.h +++ /dev/null @@ -1,461 +0,0 @@ -// Copyright (c) FIRST and other WPILib contributors. -// Open Source Software; you can modify and/or share it under the terms of -// the WPILib BSD license file in the root directory of this project. - -#pragma once - -#include - -#include -#include - -#include "frc/ErrorBase.h" -#include "frc/MotorSafety.h" - -namespace frc { - -class SpeedController; -class GenericHID; - -/** - * Utility class for handling Robot drive based on a definition of the motor - * configuration. - * - * The robot drive class handles basic driving for a robot. Currently, 2 and 4 - * motor tank and mecanum drive trains are supported. In the future other drive - * types like swerve might be implemented. Motor channel numbers are passed - * supplied on creation of the class. Those are used for either the Drive - * function (intended for hand created drive code, such as autonomous) or with - * the Tank/Arcade functions intended to be used for Operator Control driving. - * - * @deprecated Use DifferentialDrive or MecanumDrive classes instead. - * - */ -class RobotDrive : public MotorSafety { - public: - enum MotorType { - kFrontLeftMotor = 0, - kFrontRightMotor = 1, - kRearLeftMotor = 2, - kRearRightMotor = 3 - }; - - /** - * Constructor for RobotDrive with 2 motors specified with channel numbers. - * - * Set up parameters for a two wheel drive system where the - * left and right motor pwm channels are specified in the call. - * This call assumes Talons for controlling the motors. - * - * @param leftMotorChannel The PWM channel number that drives the left motor. - * 0-9 are on-board, 10-19 are on the MXP port - * @param rightMotorChannel The PWM channel number that drives the right - * motor. 0-9 are on-board, 10-19 are on the MXP port - */ - WPI_DEPRECATED("Use DifferentialDrive or MecanumDrive classes instead.") - RobotDrive(int leftMotorChannel, int rightMotorChannel); - - /** - * Constructor for RobotDrive with 4 motors specified with channel numbers. - * - * Set up parameters for a four wheel drive system where all four motor - * pwm channels are specified in the call. - * This call assumes Talons for controlling the motors. - * - * @param frontLeftMotor Front left motor channel number. 0-9 are on-board, - * 10-19 are on the MXP port - * @param rearLeftMotor Rear Left motor channel number. 0-9 are on-board, - * 10-19 are on the MXP port - * @param frontRightMotor Front right motor channel number. 0-9 are on-board, - * 10-19 are on the MXP port - * @param rearRightMotor Rear Right motor channel number. 0-9 are on-board, - * 10-19 are on the MXP port - */ - WPI_DEPRECATED("Use DifferentialDrive or MecanumDrive classes instead.") - RobotDrive(int frontLeftMotorChannel, int rearLeftMotorChannel, - int frontRightMotorChannel, int rearRightMotorChannel); - - /** - * Constructor for RobotDrive with 2 motors specified as SpeedController - * objects. - * - * The SpeedController version of the constructor enables programs to use the - * RobotDrive classes with subclasses of the SpeedController objects, for - * example, versions with ramping or reshaping of the curve to suit motor bias - * or deadband elimination. - * - * @param leftMotor The left SpeedController object used to drive the robot. - * @param rightMotor The right SpeedController object used to drive the robot. - */ - WPI_DEPRECATED("Use DifferentialDrive or MecanumDrive classes instead.") - RobotDrive(SpeedController* leftMotor, SpeedController* rightMotor); - - WPI_DEPRECATED("Use DifferentialDrive or MecanumDrive classes instead.") - RobotDrive(SpeedController& leftMotor, SpeedController& rightMotor); - - WPI_DEPRECATED("Use DifferentialDrive or MecanumDrive classes instead.") - RobotDrive(std::shared_ptr leftMotor, - std::shared_ptr rightMotor); - - /** - * Constructor for RobotDrive with 4 motors specified as SpeedController - * objects. - * - * Speed controller input version of RobotDrive (see previous comments). - * - * @param frontLeftMotor The front left SpeedController object used to drive - * the robot. - * @param rearLeftMotor The back left SpeedController object used to drive - * the robot. - * @param frontRightMotor The front right SpeedController object used to drive - * the robot. - * @param rearRightMotor The back right SpeedController object used to drive - * the robot. - */ - WPI_DEPRECATED("Use DifferentialDrive or MecanumDrive classes instead.") - RobotDrive(SpeedController* frontLeftMotor, SpeedController* rearLeftMotor, - SpeedController* frontRightMotor, SpeedController* rearRightMotor); - - WPI_DEPRECATED("Use DifferentialDrive or MecanumDrive classes instead.") - RobotDrive(SpeedController& frontLeftMotor, SpeedController& rearLeftMotor, - SpeedController& frontRightMotor, SpeedController& rearRightMotor); - - WPI_DEPRECATED("Use DifferentialDrive or MecanumDrive classes instead.") - RobotDrive(std::shared_ptr frontLeftMotor, - std::shared_ptr rearLeftMotor, - std::shared_ptr frontRightMotor, - std::shared_ptr rearRightMotor); - - ~RobotDrive() override = default; - - RobotDrive(RobotDrive&&) = default; - RobotDrive& operator=(RobotDrive&&) = default; - - /** - * Drive the motors at "outputMagnitude" and "curve". - * - * Both outputMagnitude and curve are -1.0 to +1.0 values, where 0.0 - * represents stopped and not turning. curve < 0 will turn left and curve > 0 - * will turn right. - * - * The algorithm for steering provides a constant turn radius for any normal - * speed range, both forward and backward. Increasing m_sensitivity causes - * sharper turns for fixed values of curve. - * - * This function will most likely be used in an autonomous routine. - * - * @param outputMagnitude The speed setting for the outside wheel in a turn, - * forward or backwards, +1 to -1. - * @param curve The rate of turn, constant for different forward - * speeds. Set curve < 0 for left turn or curve > 0 for - * right turn. - * - * Set curve = e^(-r/w) to get a turn radius r for wheelbase w of your robot. - * Conversely, turn radius r = -ln(curve)*w for a given value of curve and - * wheelbase w. - */ - void Drive(double outputMagnitude, double curve); - - /** - * Provide tank steering using the stored robot configuration. - * - * Drive the robot using two joystick inputs. The Y-axis will be selected from - * each Joystick object. - * - * @param leftStick The joystick to control the left side of the robot. - * @param rightStick The joystick to control the right side of the robot. - * @param squaredInputs If true, the sensitivity will be decreased for small - * values - */ - void TankDrive(GenericHID* leftStick, GenericHID* rightStick, - bool squaredInputs = true); - - /** - * Provide tank steering using the stored robot configuration. - * - * Drive the robot using two joystick inputs. The Y-axis will be selected from - * each Joystick object. - * - * @param leftStick The joystick to control the left side of the robot. - * @param rightStick The joystick to control the right side of the robot. - * @param squaredInputs If true, the sensitivity will be decreased for small - * values - */ - void TankDrive(GenericHID& leftStick, GenericHID& rightStick, - bool squaredInputs = true); - - /** - * Provide tank steering using the stored robot configuration. - * - * This function lets you pick the axis to be used on each Joystick object for - * the left and right sides of the robot. - * - * @param leftStick The Joystick object to use for the left side of the - * robot. - * @param leftAxis The axis to select on the left side Joystick object. - * @param rightStick The Joystick object to use for the right side of the - * robot. - * @param rightAxis The axis to select on the right side Joystick object. - * @param squaredInputs If true, the sensitivity will be decreased for small - * values - */ - void TankDrive(GenericHID* leftStick, int leftAxis, GenericHID* rightStick, - int rightAxis, bool squaredInputs = true); - - void TankDrive(GenericHID& leftStick, int leftAxis, GenericHID& rightStick, - int rightAxis, bool squaredInputs = true); - - /** - * Provide tank steering using the stored robot configuration. - * - * This function lets you directly provide joystick values from any source. - * - * @param leftValue The value of the left stick. - * @param rightValue The value of the right stick. - * @param squaredInputs If true, the sensitivity will be decreased for small - * values - */ - void TankDrive(double leftValue, double rightValue, - bool squaredInputs = true); - - /** - * Arcade drive implements single stick driving. - * - * Given a single Joystick, the class assumes the Y axis for the move value - * and the X axis for the rotate value. (Should add more information here - * regarding the way that arcade drive works.) - * - * @param stick The joystick to use for Arcade single-stick driving. - * The Y-axis will be selected for forwards/backwards and - * the X-axis will be selected for rotation rate. - * @param squaredInputs If true, the sensitivity will be decreased for small - * values - */ - void ArcadeDrive(GenericHID* stick, bool squaredInputs = true); - - /** - * Arcade drive implements single stick driving. - * - * Given a single Joystick, the class assumes the Y axis for the move value - * and the X axis for the rotate value. (Should add more information here - * regarding the way that arcade drive works.) - * - * @param stick The joystick to use for Arcade single-stick driving. - * The Y-axis will be selected for forwards/backwards and - * the X-axis will be selected for rotation rate. - * @param squaredInputs If true, the sensitivity will be decreased for small - * values - */ - void ArcadeDrive(GenericHID& stick, bool squaredInputs = true); - - /** - * Arcade drive implements single stick driving. - * - * Given two joystick instances and two axis, compute the values to send to - * either two or four motors. - * - * @param moveStick The Joystick object that represents the - * forward/backward direction - * @param moveAxis The axis on the moveStick object to use for - * forwards/backwards (typically Y_AXIS) - * @param rotateStick The Joystick object that represents the rotation value - * @param rotateAxis The axis on the rotation object to use for the rotate - * right/left (typically X_AXIS) - * @param squaredInputs Setting this parameter to true increases the - * sensitivity at lower speeds - */ - void ArcadeDrive(GenericHID* moveStick, int moveChannel, - GenericHID* rotateStick, int rotateChannel, - bool squaredInputs = true); - - /** - * Arcade drive implements single stick driving. - * - * Given two joystick instances and two axis, compute the values to send to - * either two or four motors. - * - * @param moveStick The Joystick object that represents the - * forward/backward direction - * @param moveAxis The axis on the moveStick object to use for - * forwards/backwards (typically Y_AXIS) - * @param rotateStick The Joystick object that represents the rotation value - * @param rotateAxis The axis on the rotation object to use for the rotate - * right/left (typically X_AXIS) - * @param squaredInputs Setting this parameter to true increases the - * sensitivity at lower speeds - */ - void ArcadeDrive(GenericHID& moveStick, int moveChannel, - GenericHID& rotateStick, int rotateChannel, - bool squaredInputs = true); - - /** - * Arcade drive implements single stick driving. - * - * This function lets you directly provide joystick values from any source. - * - * @param moveValue The value to use for fowards/backwards - * @param rotateValue The value to use for the rotate right/left - * @param squaredInputs If set, increases the sensitivity at low speeds - */ - void ArcadeDrive(double moveValue, double rotateValue, - bool squaredInputs = true); - - /** - * Drive method for Mecanum wheeled robots. - * - * A method for driving with Mecanum wheeled robots. There are 4 wheels - * on the robot, arranged so that the front and back wheels are toed in 45 - * degrees. - * When looking at the wheels from the top, the roller axles should form an X - * across the robot. - * - * This is designed to be directly driven by joystick axes. - * - * @param x The speed that the robot should drive in the X direction. - * [-1.0..1.0] - * @param y The speed that the robot should drive in the Y direction. - * This input is inverted to match the forward == -1.0 that - * joysticks produce. [-1.0..1.0] - * @param rotation The rate of rotation for the robot that is completely - * independent of the translation. [-1.0..1.0] - * @param gyroAngle The current angle reading from the gyro. Use this to - * implement field-oriented controls. - */ - void MecanumDrive_Cartesian(double x, double y, double rotation, - double gyroAngle = 0.0); - - /** - * Drive method for Mecanum wheeled robots. - * - * A method for driving with Mecanum wheeled robots. There are 4 wheels - * on the robot, arranged so that the front and back wheels are toed in 45 - * degrees. - * When looking at the wheels from the top, the roller axles should form an X - * across the robot. - * - * @param magnitude The speed that the robot should drive in a given - * direction. [-1.0..1.0] - * @param direction The direction the robot should drive in degrees. The - * direction and maginitute are independent of the rotation - * rate. - * @param rotation The rate of rotation for the robot that is completely - * independent of the magnitute or direction. [-1.0..1.0] - */ - void MecanumDrive_Polar(double magnitude, double direction, double rotation); - - /** - * Holonomic Drive method for Mecanum wheeled robots. - * - * This is an alias to MecanumDrive_Polar() for backward compatibility - * - * @param magnitude The speed that the robot should drive in a given - * direction. [-1.0..1.0] - * @param direction The direction the robot should drive. The direction and - * magnitude are independent of the rotation rate. - * @param rotation The rate of rotation for the robot that is completely - * independent of the magnitude or direction. [-1.0..1.0] - */ - void HolonomicDrive(double magnitude, double direction, double rotation); - - /** - * Set the speed of the right and left motors. - * - * This is used once an appropriate drive setup function is called such as - * TwoWheelDrive(). The motors are set to "leftOutput" and "rightOutput" - * and includes flipping the direction of one side for opposing motors. - * - * @param leftOutput The speed to send to the left side of the robot. - * @param rightOutput The speed to send to the right side of the robot. - */ - virtual void SetLeftRightMotorOutputs(double leftOutput, double rightOutput); - - /* - * Invert a motor direction. - * - * This is used when a motor should run in the opposite direction as the drive - * code would normally run it. Motors that are direct drive would be inverted, - * the Drive code assumes that the motors are geared with one reversal. - * - * @param motor The motor index to invert. - * @param isInverted True if the motor should be inverted when operated. - */ - void SetInvertedMotor(MotorType motor, bool isInverted); - - /** - * Set the turning sensitivity. - * - * This only impacts the Drive() entry-point. - * - * @param sensitivity Effectively sets the turning sensitivity (or turn radius - * for a given value) - */ - void SetSensitivity(double sensitivity); - - /** - * Configure the scaling factor for using RobotDrive with motor controllers in - * a mode other than PercentVbus. - * - * @param maxOutput Multiplied with the output percentage computed by the - * drive functions. - */ - void SetMaxOutput(double maxOutput); - - void StopMotor() override; - void GetDescription(wpi::raw_ostream& desc) const override; - - protected: - /** - * Common function to initialize all the robot drive constructors. - * - * Create a motor safety object (the real reason for the common code) and - * initialize all the motor assignments. The default timeout is set for the - * robot drive. - */ - void InitRobotDrive(); - - /** - * Limit motor values to the -1.0 to +1.0 range. - */ - double Limit(double number); - - /** - * Normalize all wheel speeds if the magnitude of any wheel is greater than - * 1.0. - */ - void Normalize(double* wheelSpeeds); - - /** - * Rotate a vector in Cartesian space. - */ - void RotateVector(double& x, double& y, double angle); - - static constexpr int kMaxNumberOfMotors = 4; - - double m_sensitivity = 0.5; - double m_maxOutput = 1.0; - - std::shared_ptr m_frontLeftMotor; - std::shared_ptr m_frontRightMotor; - std::shared_ptr m_rearLeftMotor; - std::shared_ptr m_rearRightMotor; - - private: - int GetNumMotors() { - int motors = 0; - if (m_frontLeftMotor) { - motors++; - } - if (m_frontRightMotor) { - motors++; - } - if (m_rearLeftMotor) { - motors++; - } - if (m_rearRightMotor) { - motors++; - } - return motors; - } -}; - -} // namespace frc diff --git a/wpilibc/src/main/native/include/frc/drive/DifferentialDrive.h b/wpilibc/src/main/native/include/frc/drive/DifferentialDrive.h index 4e0ff1e79e..dc24959a80 100644 --- a/wpilibc/src/main/native/include/frc/drive/DifferentialDrive.h +++ b/wpilibc/src/main/native/include/frc/drive/DifferentialDrive.h @@ -87,12 +87,12 @@ class SpeedController; * *

RobotDrive porting guide: *
TankDrive(double, double, bool) is equivalent to - * RobotDrive#TankDrive(double, double, bool) if a deadband of 0 is used. + * RobotDrive's TankDrive(double, double, bool) if a deadband of 0 is used. *
ArcadeDrive(double, double, bool) is equivalent to - * RobotDrive#ArcadeDrive(double, double, bool) if a deadband of 0 is used + * RobotDrive's ArcadeDrive(double, double, bool) if a deadband of 0 is used * and the the rotation input is inverted eg ArcadeDrive(y, -rotation, false) *
CurvatureDrive(double, double, bool) is similar in concept to - * RobotDrive#Drive(double, double) with the addition of a quick turn + * RobotDrive's Drive(double, double) with the addition of a quick turn * mode. However, it is not designed to give exactly the same response. */ class DifferentialDrive : public RobotDriveBase, diff --git a/wpilibc/src/main/native/include/frc/drive/MecanumDrive.h b/wpilibc/src/main/native/include/frc/drive/MecanumDrive.h index e478e3b11b..13d56c5aa8 100644 --- a/wpilibc/src/main/native/include/frc/drive/MecanumDrive.h +++ b/wpilibc/src/main/native/include/frc/drive/MecanumDrive.h @@ -53,12 +53,12 @@ class SpeedController; * inverted, while in RobotDrive, no speed controllers are automatically * inverted. *
DriveCartesian(double, double, double, double) is equivalent to - * RobotDrive#MecanumDrive_Cartesian(double, double, double, double) + * RobotDrive's MecanumDrive_Cartesian(double, double, double, double) * if a deadband of 0 is used, and the ySpeed and gyroAngle values are inverted * compared to RobotDrive (eg DriveCartesian(xSpeed, -ySpeed, zRotation, * -gyroAngle). *
DrivePolar(double, double, double) is equivalent to - * RobotDrive#MecanumDrive_Polar(double, double, double) if a + * RobotDrive's MecanumDrive_Polar(double, double, double) if a * deadband of 0 is used. */ class MecanumDrive : public RobotDriveBase, diff --git a/wpilibc/src/test/native/cpp/drive/DriveTest.cpp b/wpilibc/src/test/native/cpp/drive/DriveTest.cpp deleted file mode 100644 index 643aac3f13..0000000000 --- a/wpilibc/src/test/native/cpp/drive/DriveTest.cpp +++ /dev/null @@ -1,187 +0,0 @@ -// Copyright (c) FIRST and other WPILib contributors. -// Open Source Software; you can modify and/or share it under the terms of -// the WPILib BSD license file in the root directory of this project. - -#include "MockSpeedController.h" -#include "frc/RobotDrive.h" -#include "frc/drive/DifferentialDrive.h" -#include "frc/drive/MecanumDrive.h" -#include "gtest/gtest.h" - -using namespace frc; - -class DriveTest : public testing::Test { - protected: - MockSpeedController m_rdFrontLeft; - MockSpeedController m_rdRearLeft; - MockSpeedController m_rdFrontRight; - MockSpeedController m_rdRearRight; - MockSpeedController m_frontLeft; - MockSpeedController m_rearLeft; - MockSpeedController m_frontRight; - MockSpeedController m_rearRight; - frc::RobotDrive m_robotDrive{m_rdFrontLeft, m_rdRearLeft, m_rdFrontRight, - m_rdRearRight}; - frc::DifferentialDrive m_differentialDrive{m_frontLeft, m_frontRight}; - frc::MecanumDrive m_mecanumDrive{m_frontLeft, m_rearLeft, m_frontRight, - m_rearRight}; - - double m_testJoystickValues[9] = {-1.0, -0.9, -0.5, -0.01, 0.0, - 0.01, 0.5, 0.9, 1.0}; - double m_testGyroValues[19] = {0, 45, 90, 135, 180, 225, 270, - 305, 360, 540, -45, -90, -135, -180, - -225, -270, -305, -360, -540}; -}; - -TEST_F(DriveTest, TankDrive) { - int joystickSize = sizeof(m_testJoystickValues) / sizeof(double); - double leftJoystick, rightJoystick; - m_differentialDrive.SetDeadband(0.0); - m_differentialDrive.SetSafetyEnabled(false); - m_mecanumDrive.SetSafetyEnabled(false); - m_robotDrive.SetSafetyEnabled(false); - for (int i = 0; i < joystickSize; i++) { - for (int j = 0; j < joystickSize; j++) { - leftJoystick = m_testJoystickValues[i]; - rightJoystick = m_testJoystickValues[j]; - m_robotDrive.TankDrive(leftJoystick, rightJoystick, false); - m_differentialDrive.TankDrive(leftJoystick, rightJoystick, false); - ASSERT_NEAR(m_rdFrontLeft.Get(), m_frontLeft.Get(), 0.01); - ASSERT_NEAR(m_rdFrontRight.Get(), m_frontRight.Get(), 0.01); - } - } -} - -TEST_F(DriveTest, TankDriveSquared) { - int joystickSize = sizeof(m_testJoystickValues) / sizeof(double); - double leftJoystick, rightJoystick; - m_differentialDrive.SetDeadband(0.0); - m_differentialDrive.SetSafetyEnabled(false); - m_mecanumDrive.SetSafetyEnabled(false); - m_robotDrive.SetSafetyEnabled(false); - for (int i = 0; i < joystickSize; i++) { - for (int j = 0; j < joystickSize; j++) { - leftJoystick = m_testJoystickValues[i]; - rightJoystick = m_testJoystickValues[j]; - m_robotDrive.TankDrive(leftJoystick, rightJoystick, true); - m_differentialDrive.TankDrive(leftJoystick, rightJoystick, true); - ASSERT_NEAR(m_rdFrontLeft.Get(), m_frontLeft.Get(), 0.01); - ASSERT_NEAR(m_rdFrontRight.Get(), m_frontRight.Get(), 0.01); - } - } -} - -TEST_F(DriveTest, ArcadeDriveSquared) { - int joystickSize = sizeof(m_testJoystickValues) / sizeof(double); - double moveJoystick, rotateJoystick; - m_differentialDrive.SetDeadband(0.0); - m_differentialDrive.SetSafetyEnabled(false); - m_mecanumDrive.SetSafetyEnabled(false); - m_robotDrive.SetSafetyEnabled(false); - for (int i = 0; i < joystickSize; i++) { - for (int j = 0; j < joystickSize; j++) { - moveJoystick = m_testJoystickValues[i]; - rotateJoystick = m_testJoystickValues[j]; - m_robotDrive.ArcadeDrive(moveJoystick, rotateJoystick, true); - m_differentialDrive.ArcadeDrive(moveJoystick, -rotateJoystick, true); - ASSERT_NEAR(m_rdFrontLeft.Get(), m_frontLeft.Get(), 0.01); - ASSERT_NEAR(m_rdFrontRight.Get(), m_frontRight.Get(), 0.01); - } - } -} - -TEST_F(DriveTest, ArcadeDrive) { - int joystickSize = sizeof(m_testJoystickValues) / sizeof(double); - double moveJoystick, rotateJoystick; - m_differentialDrive.SetDeadband(0.0); - m_differentialDrive.SetSafetyEnabled(false); - m_mecanumDrive.SetSafetyEnabled(false); - m_robotDrive.SetSafetyEnabled(false); - for (int i = 0; i < joystickSize; i++) { - for (int j = 0; j < joystickSize; j++) { - moveJoystick = m_testJoystickValues[i]; - rotateJoystick = m_testJoystickValues[j]; - m_robotDrive.ArcadeDrive(moveJoystick, rotateJoystick, false); - m_differentialDrive.ArcadeDrive(moveJoystick, -rotateJoystick, false); - ASSERT_NEAR(m_rdFrontLeft.Get(), m_frontLeft.Get(), 0.01); - ASSERT_NEAR(m_rdFrontRight.Get(), m_frontRight.Get(), 0.01); - } - } -} - -TEST_F(DriveTest, MecanumCartesian) { - int joystickSize = sizeof(m_testJoystickValues) / sizeof(double); - int gyroSize = sizeof(m_testGyroValues) / sizeof(double); - double xJoystick, yJoystick, rotateJoystick, gyroValue; - m_mecanumDrive.SetDeadband(0.0); - m_mecanumDrive.SetSafetyEnabled(false); - m_differentialDrive.SetSafetyEnabled(false); - m_robotDrive.SetSafetyEnabled(false); - for (int i = 0; i < joystickSize; i++) { - for (int j = 0; j < joystickSize; j++) { - for (int k = 0; k < joystickSize; k++) { - for (int l = 0; l < gyroSize; l++) { - xJoystick = m_testJoystickValues[i]; - yJoystick = m_testJoystickValues[j]; - rotateJoystick = m_testJoystickValues[k]; - gyroValue = m_testGyroValues[l]; - m_robotDrive.MecanumDrive_Cartesian(xJoystick, yJoystick, - rotateJoystick, gyroValue); - m_mecanumDrive.DriveCartesian(xJoystick, -yJoystick, rotateJoystick, - -gyroValue); - ASSERT_NEAR(m_rdFrontLeft.Get(), m_frontLeft.Get(), 0.01) - << "X: " << xJoystick << " Y: " << yJoystick - << " Rotate: " << rotateJoystick << " Gyro: " << gyroValue; - ASSERT_NEAR(m_rdFrontRight.Get(), -m_frontRight.Get(), 0.01) - << "X: " << xJoystick << " Y: " << yJoystick - << " Rotate: " << rotateJoystick << " Gyro: " << gyroValue; - ASSERT_NEAR(m_rdRearLeft.Get(), m_rearLeft.Get(), 0.01) - << "X: " << xJoystick << " Y: " << yJoystick - << " Rotate: " << rotateJoystick << " Gyro: " << gyroValue; - ASSERT_NEAR(m_rdRearRight.Get(), -m_rearRight.Get(), 0.01) - << "X: " << xJoystick << " Y: " << yJoystick - << " Rotate: " << rotateJoystick << " Gyro: " << gyroValue; - } - } - } - } -} - -TEST_F(DriveTest, MecanumPolar) { - int joystickSize = sizeof(m_testJoystickValues) / sizeof(double); - int gyroSize = sizeof(m_testGyroValues) / sizeof(double); - double magnitudeJoystick, directionJoystick, rotateJoystick; - m_mecanumDrive.SetDeadband(0.0); - m_mecanumDrive.SetSafetyEnabled(false); - m_differentialDrive.SetSafetyEnabled(false); - m_robotDrive.SetSafetyEnabled(false); - for (int i = 0; i < joystickSize; i++) { - for (int j = 0; j < gyroSize; j++) { - for (int k = 0; k < joystickSize; k++) { - magnitudeJoystick = m_testJoystickValues[i]; - directionJoystick = m_testGyroValues[j]; - rotateJoystick = m_testJoystickValues[k]; - m_robotDrive.MecanumDrive_Polar(magnitudeJoystick, directionJoystick, - rotateJoystick); - m_mecanumDrive.DrivePolar(magnitudeJoystick, directionJoystick, - rotateJoystick); - ASSERT_NEAR(m_rdFrontLeft.Get(), m_frontLeft.Get(), 0.01) - << "Magnitude: " << magnitudeJoystick - << " Direction: " << directionJoystick - << " Rotate: " << rotateJoystick; - ASSERT_NEAR(m_rdFrontRight.Get(), -m_frontRight.Get(), 0.01) - << "Magnitude: " << magnitudeJoystick - << " Direction: " << directionJoystick - << " Rotate: " << rotateJoystick; - ASSERT_NEAR(m_rdRearLeft.Get(), m_rearLeft.Get(), 0.01) - << "Magnitude: " << magnitudeJoystick - << " Direction: " << directionJoystick - << " Rotate: " << rotateJoystick; - ASSERT_NEAR(m_rdRearRight.Get(), -m_rearRight.Get(), 0.01) - << "Magnitude: " << magnitudeJoystick - << " Direction: " << directionJoystick - << " Rotate: " << rotateJoystick; - } - } - } -} diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/RobotDrive.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/RobotDrive.java deleted file mode 100644 index d038ac3d04..0000000000 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/RobotDrive.java +++ /dev/null @@ -1,719 +0,0 @@ -// Copyright (c) FIRST and other WPILib contributors. -// Open Source Software; you can modify and/or share it under the terms of -// the WPILib BSD license file in the root directory of this project. - -package edu.wpi.first.wpilibj; - -import static edu.wpi.first.wpilibj.util.ErrorMessages.requireNonNullParam; - -import edu.wpi.first.hal.FRCNetComm.tInstances; -import edu.wpi.first.hal.FRCNetComm.tResourceType; -import edu.wpi.first.hal.HAL; - -/** - * Utility class for handling Robot drive based on a definition of the motor configuration. The - * robot drive class handles basic driving for a robot. Currently, 2 and 4 motor tank and mecanum - * drive trains are supported. In the future other drive types like swerve might be implemented. - * Motor channel numbers are supplied on creation of the class. Those are used for either the drive - * function (intended for hand created drive code, such as autonomous) or with the Tank/Arcade - * functions intended to be used for Operator Control driving. - * - * @deprecated Use {@link edu.wpi.first.wpilibj.drive.DifferentialDrive} or {@link - * edu.wpi.first.wpilibj.drive.MecanumDrive} classes instead. - */ -@Deprecated -@SuppressWarnings("PMD.GodClass") -public class RobotDrive extends MotorSafety implements AutoCloseable { - /** The location of a motor on the robot for the purpose of driving. */ - public enum MotorType { - kFrontLeft(0), - kFrontRight(1), - kRearLeft(2), - kRearRight(3); - - public final int value; - - MotorType(int value) { - this.value = value; - } - } - - public static final double kDefaultExpirationTime = 0.1; - public static final double kDefaultSensitivity = 0.5; - public static final double kDefaultMaxOutput = 1.0; - protected static final int kMaxNumberOfMotors = 4; - protected double m_sensitivity; - protected double m_maxOutput; - protected SpeedController m_frontLeftMotor; - protected SpeedController m_frontRightMotor; - protected SpeedController m_rearLeftMotor; - protected SpeedController m_rearRightMotor; - protected boolean m_allocatedSpeedControllers; - protected static boolean kArcadeRatioCurve_Reported; - protected static boolean kTank_Reported; - protected static boolean kArcadeStandard_Reported; - protected static boolean kMecanumCartesian_Reported; - protected static boolean kMecanumPolar_Reported; - - /** - * Constructor for RobotDrive with 2 motors specified with channel numbers. Set up parameters for - * a two wheel drive system where the left and right motor pwm channels are specified in the call. - * This call assumes Talons for controlling the motors. - * - * @param leftMotorChannel The PWM channel number that drives the left motor. - * @param rightMotorChannel The PWM channel number that drives the right motor. - */ - public RobotDrive(final int leftMotorChannel, final int rightMotorChannel) { - m_sensitivity = kDefaultSensitivity; - m_maxOutput = kDefaultMaxOutput; - m_frontLeftMotor = null; - m_rearLeftMotor = new Talon(leftMotorChannel); - m_frontRightMotor = null; - m_rearRightMotor = new Talon(rightMotorChannel); - m_allocatedSpeedControllers = true; - setSafetyEnabled(true); - drive(0, 0); - } - - /** - * Constructor for RobotDrive with 4 motors specified with channel numbers. Set up parameters for - * a four wheel drive system where all four motor pwm channels are specified in the call. This - * call assumes Talons for controlling the motors. - * - * @param frontLeftMotor Front left motor channel number - * @param rearLeftMotor Rear Left motor channel number - * @param frontRightMotor Front right motor channel number - * @param rearRightMotor Rear Right motor channel number - */ - public RobotDrive( - final int frontLeftMotor, - final int rearLeftMotor, - final int frontRightMotor, - final int rearRightMotor) { - m_sensitivity = kDefaultSensitivity; - m_maxOutput = kDefaultMaxOutput; - m_rearLeftMotor = new Talon(rearLeftMotor); - m_rearRightMotor = new Talon(rearRightMotor); - m_frontLeftMotor = new Talon(frontLeftMotor); - m_frontRightMotor = new Talon(frontRightMotor); - m_allocatedSpeedControllers = true; - setSafetyEnabled(true); - drive(0, 0); - } - - /** - * Constructor for RobotDrive with 2 motors specified as SpeedController objects. The - * SpeedController version of the constructor enables programs to use the RobotDrive classes with - * subclasses of the SpeedController objects, for example, versions with ramping or reshaping of - * the curve to suit motor bias or dead-band elimination. - * - * @param leftMotor The left SpeedController object used to drive the robot. - * @param rightMotor the right SpeedController object used to drive the robot. - */ - public RobotDrive(SpeedController leftMotor, SpeedController rightMotor) { - requireNonNullParam(leftMotor, "leftMotor", "RobotDrive"); - requireNonNullParam(rightMotor, "rightMotor", "RobotDrive"); - - m_frontLeftMotor = null; - m_rearLeftMotor = leftMotor; - m_frontRightMotor = null; - m_rearRightMotor = rightMotor; - m_sensitivity = kDefaultSensitivity; - m_maxOutput = kDefaultMaxOutput; - m_allocatedSpeedControllers = false; - setSafetyEnabled(true); - drive(0, 0); - } - - /** - * Constructor for RobotDrive with 4 motors specified as SpeedController objects. Speed controller - * input version of RobotDrive (see previous comments). - * - * @param frontLeftMotor The front left SpeedController object used to drive the robot - * @param rearLeftMotor The back left SpeedController object used to drive the robot. - * @param frontRightMotor The front right SpeedController object used to drive the robot. - * @param rearRightMotor The back right SpeedController object used to drive the robot. - */ - public RobotDrive( - SpeedController frontLeftMotor, - SpeedController rearLeftMotor, - SpeedController frontRightMotor, - SpeedController rearRightMotor) { - m_frontLeftMotor = requireNonNullParam(frontLeftMotor, "frontLeftMotor", "RobotDrive"); - m_rearLeftMotor = requireNonNullParam(rearLeftMotor, "rearLeftMotor", "RobotDrive"); - m_frontRightMotor = requireNonNullParam(frontRightMotor, "frontRightMotor", "RobotDrive"); - m_rearRightMotor = requireNonNullParam(rearRightMotor, "rearRightMotor", "RobotDrive"); - m_sensitivity = kDefaultSensitivity; - m_maxOutput = kDefaultMaxOutput; - m_allocatedSpeedControllers = false; - setSafetyEnabled(true); - drive(0, 0); - } - - /** - * Drive the motors at "outputMagnitude" and "curve". Both outputMagnitude and curve are -1.0 to - * +1.0 values, where 0.0 represents stopped and not turning. {@literal curve < 0 will turn left - * and curve > 0} will turn right. - * - *

The algorithm for steering provides a constant turn radius for any normal speed range, both - * forward and backward. Increasing sensitivity causes sharper turns for fixed values of curve. - * - *

This function will most likely be used in an autonomous routine. - * - * @param outputMagnitude The speed setting for the outside wheel in a turn, forward or backwards, - * +1 to -1. - * @param curve The rate of turn, constant for different forward speeds. Set {@literal curve < 0 - * for left turn or curve > 0 for right turn.} Set curve = e^(-r/w) to get a turn radius r for - * wheelbase w of your robot. Conversely, turn radius r = -ln(curve)*w for a given value of - * curve and wheelbase w. - */ - public void drive(double outputMagnitude, double curve) { - final double leftOutput; - final double rightOutput; - - if (!kArcadeRatioCurve_Reported) { - HAL.report( - tResourceType.kResourceType_RobotDrive, - tInstances.kRobotDrive_ArcadeRatioCurve, - getNumMotors()); - kArcadeRatioCurve_Reported = true; - } - if (curve < 0) { - double value = Math.log(-curve); - double ratio = (value - m_sensitivity) / (value + m_sensitivity); - if (ratio == 0) { - ratio = 0.0000000001; - } - leftOutput = outputMagnitude / ratio; - rightOutput = outputMagnitude; - } else if (curve > 0) { - double value = Math.log(curve); - double ratio = (value - m_sensitivity) / (value + m_sensitivity); - if (ratio == 0) { - ratio = 0.0000000001; - } - leftOutput = outputMagnitude; - rightOutput = outputMagnitude / ratio; - } else { - leftOutput = outputMagnitude; - rightOutput = outputMagnitude; - } - setLeftRightMotorOutputs(leftOutput, rightOutput); - } - - /** - * Provide tank steering using the stored robot configuration. drive the robot using two joystick - * inputs. The Y-axis will be selected from each Joystick object. The calculated values will be - * squared to decrease sensitivity at low speeds. - * - * @param leftStick The joystick to control the left side of the robot. - * @param rightStick The joystick to control the right side of the robot. - */ - public void tankDrive(GenericHID leftStick, GenericHID rightStick) { - requireNonNullParam(leftStick, "leftStick", "tankDrive"); - requireNonNullParam(rightStick, "rightStick", "tankDrive"); - - tankDrive(leftStick.getY(), rightStick.getY(), true); - } - - /** - * Provide tank steering using the stored robot configuration. drive the robot using two joystick - * inputs. The Y-axis will be selected from each Joystick object. - * - * @param leftStick The joystick to control the left side of the robot. - * @param rightStick The joystick to control the right side of the robot. - * @param squaredInputs Setting this parameter to true decreases the sensitivity at lower speeds - */ - public void tankDrive(GenericHID leftStick, GenericHID rightStick, boolean squaredInputs) { - requireNonNullParam(leftStick, "leftStick", "tankDrive"); - requireNonNullParam(rightStick, "rightStick", "tankDrive"); - - tankDrive(leftStick.getY(), rightStick.getY(), squaredInputs); - } - - /** - * Provide tank steering using the stored robot configuration. This function lets you pick the - * axis to be used on each Joystick object for the left and right sides of the robot. The - * calculated values will be squared to decrease sensitivity at low speeds. - * - * @param leftStick The Joystick object to use for the left side of the robot. - * @param leftAxis The axis to select on the left side Joystick object. - * @param rightStick The Joystick object to use for the right side of the robot. - * @param rightAxis The axis to select on the right side Joystick object. - */ - public void tankDrive( - GenericHID leftStick, final int leftAxis, GenericHID rightStick, final int rightAxis) { - requireNonNullParam(leftStick, "leftStick", "tankDrive"); - requireNonNullParam(rightStick, "rightStick", "tankDrive"); - - tankDrive(leftStick.getRawAxis(leftAxis), rightStick.getRawAxis(rightAxis), true); - } - - /** - * Provide tank steering using the stored robot configuration. This function lets you pick the - * axis to be used on each Joystick object for the left and right sides of the robot. - * - * @param leftStick The Joystick object to use for the left side of the robot. - * @param leftAxis The axis to select on the left side Joystick object. - * @param rightStick The Joystick object to use for the right side of the robot. - * @param rightAxis The axis to select on the right side Joystick object. - * @param squaredInputs Setting this parameter to true decreases the sensitivity at lower speeds - */ - public void tankDrive( - GenericHID leftStick, - final int leftAxis, - GenericHID rightStick, - final int rightAxis, - boolean squaredInputs) { - requireNonNullParam(leftStick, "leftStick", "tankDrive"); - requireNonNullParam(rightStick, "rightStick", "tankDrive"); - - tankDrive(leftStick.getRawAxis(leftAxis), rightStick.getRawAxis(rightAxis), squaredInputs); - } - - /** - * Provide tank steering using the stored robot configuration. This function lets you directly - * provide joystick values from any source. - * - * @param leftValue The value of the left stick. - * @param rightValue The value of the right stick. - * @param squaredInputs Setting this parameter to true decreases the sensitivity at lower speeds - */ - public void tankDrive(double leftValue, double rightValue, boolean squaredInputs) { - if (!kTank_Reported) { - HAL.report( - tResourceType.kResourceType_RobotDrive, tInstances.kRobotDrive_Tank, getNumMotors()); - kTank_Reported = true; - } - - leftValue = limit(leftValue); - rightValue = limit(rightValue); - - // square the inputs (while preserving the sign) to increase fine control - // while permitting full power - if (squaredInputs) { - leftValue = Math.copySign(leftValue * leftValue, leftValue); - rightValue = Math.copySign(rightValue * rightValue, rightValue); - } - setLeftRightMotorOutputs(leftValue, rightValue); - } - - /** - * Provide tank steering using the stored robot configuration. This function lets you directly - * provide joystick values from any source. The calculated values will be squared to decrease - * sensitivity at low speeds. - * - * @param leftValue The value of the left stick. - * @param rightValue The value of the right stick. - */ - public void tankDrive(double leftValue, double rightValue) { - tankDrive(leftValue, rightValue, true); - } - - /** - * Arcade drive implements single stick driving. Given a single Joystick, the class assumes the Y - * axis for the move value and the X axis for the rotate value. (Should add more information here - * regarding the way that arcade drive works.) - * - * @param stick The joystick to use for Arcade single-stick driving. The Y-axis will be selected - * for forwards/backwards and the X-axis will be selected for rotation rate. - * @param squaredInputs If true, the sensitivity will be decreased for small values - */ - public void arcadeDrive(GenericHID stick, boolean squaredInputs) { - // simply call the full-featured arcadeDrive with the appropriate values - arcadeDrive(stick.getY(), stick.getX(), squaredInputs); - } - - /** - * Arcade drive implements single stick driving. Given a single Joystick, the class assumes the Y - * axis for the move value and the X axis for the rotate value. (Should add more information here - * regarding the way that arcade drive works.) The calculated values will be squared to decrease - * sensitivity at low speeds. - * - * @param stick The joystick to use for Arcade single-stick driving. The Y-axis will be selected - * for forwards/backwards and the X-axis will be selected for rotation rate. - */ - public void arcadeDrive(GenericHID stick) { - arcadeDrive(stick, true); - } - - /** - * Arcade drive implements single stick driving. Given two joystick instances and two axis, - * compute the values to send to either two or four motors. - * - * @param moveStick The Joystick object that represents the forward/backward direction - * @param moveAxis The axis on the moveStick object to use for forwards/backwards (typically - * Y_AXIS) - * @param rotateStick The Joystick object that represents the rotation value - * @param rotateAxis The axis on the rotation object to use for the rotate right/left (typically - * X_AXIS) - * @param squaredInputs Setting this parameter to true decreases the sensitivity at lower speeds - */ - public void arcadeDrive( - GenericHID moveStick, - final int moveAxis, - GenericHID rotateStick, - final int rotateAxis, - boolean squaredInputs) { - double moveValue = moveStick.getRawAxis(moveAxis); - double rotateValue = rotateStick.getRawAxis(rotateAxis); - - arcadeDrive(moveValue, rotateValue, squaredInputs); - } - - /** - * Arcade drive implements single stick driving. Given two joystick instances and two axis, - * compute the values to send to either two or four motors. The calculated values will be squared - * to decrease sensitivity at low speeds. - * - * @param moveStick The Joystick object that represents the forward/backward direction - * @param moveAxis The axis on the moveStick object to use for forwards/backwards (typically - * Y_AXIS) - * @param rotateStick The Joystick object that represents the rotation value - * @param rotateAxis The axis on the rotation object to use for the rotate right/left (typically - * X_AXIS) - */ - public void arcadeDrive( - GenericHID moveStick, final int moveAxis, GenericHID rotateStick, final int rotateAxis) { - arcadeDrive(moveStick, moveAxis, rotateStick, rotateAxis, true); - } - - /** - * Arcade drive implements single stick driving. This function lets you directly provide joystick - * values from any source. - * - * @param moveValue The value to use for forwards/backwards - * @param rotateValue The value to use for the rotate right/left - * @param squaredInputs If set, decreases the sensitivity at low speeds - */ - public void arcadeDrive(double moveValue, double rotateValue, boolean squaredInputs) { - // local variables to hold the computed PWM values for the motors - if (!kArcadeStandard_Reported) { - HAL.report( - tResourceType.kResourceType_RobotDrive, - tInstances.kRobotDrive_ArcadeStandard, - getNumMotors()); - kArcadeStandard_Reported = true; - } - - double leftMotorSpeed; - double rightMotorSpeed; - - moveValue = limit(moveValue); - rotateValue = limit(rotateValue); - - // square the inputs (while preserving the sign) to increase fine control - // while permitting full power - if (squaredInputs) { - // square the inputs (while preserving the sign) to increase fine control - // while permitting full power - moveValue = Math.copySign(moveValue * moveValue, moveValue); - rotateValue = Math.copySign(rotateValue * rotateValue, rotateValue); - } - - if (moveValue > 0.0) { - if (rotateValue > 0.0) { - leftMotorSpeed = moveValue - rotateValue; - rightMotorSpeed = Math.max(moveValue, rotateValue); - } else { - leftMotorSpeed = Math.max(moveValue, -rotateValue); - rightMotorSpeed = moveValue + rotateValue; - } - } else { - if (rotateValue > 0.0) { - leftMotorSpeed = -Math.max(-moveValue, rotateValue); - rightMotorSpeed = moveValue + rotateValue; - } else { - leftMotorSpeed = moveValue - rotateValue; - rightMotorSpeed = -Math.max(-moveValue, -rotateValue); - } - } - - setLeftRightMotorOutputs(leftMotorSpeed, rightMotorSpeed); - } - - /** - * Arcade drive implements single stick driving. This function lets you directly provide joystick - * values from any source. The calculated values will be squared to decrease sensitivity at low - * speeds. - * - * @param moveValue The value to use for forwards/backwards - * @param rotateValue The value to use for the rotate right/left - */ - public void arcadeDrive(double moveValue, double rotateValue) { - arcadeDrive(moveValue, rotateValue, true); - } - - /** - * Drive method for Mecanum wheeled robots. - * - *

A method for driving with Mecanum wheeled robots. There are 4 wheels on the robot, arranged - * so that the front and back wheels are toed in 45 degrees. When looking at the wheels from the - * top, the roller axles should form an X across the robot. - * - *

This is designed to be directly driven by joystick axes. - * - * @param x The speed that the robot should drive in the X direction. [-1.0..1.0] - * @param y The speed that the robot should drive in the Y direction. This input is inverted to - * match the forward == -1.0 that joysticks produce. [-1.0..1.0] - * @param rotation The rate of rotation for the robot that is completely independent of the - * translation. [-1.0..1.0] - * @param gyroAngle The current angle reading from the gyro. Use this to implement field-oriented - * controls. - */ - public void mecanumDrive_Cartesian(double x, double y, double rotation, double gyroAngle) { - if (!kMecanumCartesian_Reported) { - HAL.report( - tResourceType.kResourceType_RobotDrive, - tInstances.kRobotDrive_MecanumCartesian, - getNumMotors()); - kMecanumCartesian_Reported = true; - } - // Negate y for the joystick. - y = -y; - // Compensate for gyro angle. - double[] rotated = rotateVector(x, y, gyroAngle); - x = rotated[0]; - y = rotated[1]; - - double[] wheelSpeeds = new double[kMaxNumberOfMotors]; - wheelSpeeds[MotorType.kFrontLeft.value] = x + y + rotation; - wheelSpeeds[MotorType.kFrontRight.value] = -x + y - rotation; - wheelSpeeds[MotorType.kRearLeft.value] = -x + y + rotation; - wheelSpeeds[MotorType.kRearRight.value] = x + y - rotation; - - normalize(wheelSpeeds); - m_frontLeftMotor.set(wheelSpeeds[MotorType.kFrontLeft.value] * m_maxOutput); - m_frontRightMotor.set(wheelSpeeds[MotorType.kFrontRight.value] * m_maxOutput); - m_rearLeftMotor.set(wheelSpeeds[MotorType.kRearLeft.value] * m_maxOutput); - m_rearRightMotor.set(wheelSpeeds[MotorType.kRearRight.value] * m_maxOutput); - - feed(); - } - - /** - * Drive method for Mecanum wheeled robots. - * - *

A method for driving with Mecanum wheeled robots. There are 4 wheels on the robot, arranged - * so that the front and back wheels are toed in 45 degrees. When looking at the wheels from the - * top, the roller axles should form an X across the robot. - * - * @param magnitude The speed that the robot should drive in a given direction. [-1.0..1.0] - * @param direction The angle the robot should drive in degrees. The direction and magnitude are - * independent of the rotation rate. [-180.0..180.0] - * @param rotation The rate of rotation for the robot that is completely independent of the - * magnitude or direction. [-1.0..1.0] - */ - public void mecanumDrive_Polar(double magnitude, double direction, double rotation) { - if (!kMecanumPolar_Reported) { - HAL.report( - tResourceType.kResourceType_RobotDrive, - tInstances.kRobotDrive_MecanumPolar, - getNumMotors()); - kMecanumPolar_Reported = true; - } - // Normalized for full power along the Cartesian axes. - magnitude = limit(magnitude) * Math.sqrt(2.0); - // The rollers are at 45 degree angles. - double dirInRad = (direction + 45.0) * Math.PI / 180.0; - double cosD = Math.cos(dirInRad); - double sinD = Math.sin(dirInRad); - - double[] wheelSpeeds = new double[kMaxNumberOfMotors]; - wheelSpeeds[MotorType.kFrontLeft.value] = sinD * magnitude + rotation; - wheelSpeeds[MotorType.kFrontRight.value] = cosD * magnitude - rotation; - wheelSpeeds[MotorType.kRearLeft.value] = cosD * magnitude + rotation; - wheelSpeeds[MotorType.kRearRight.value] = sinD * magnitude - rotation; - - normalize(wheelSpeeds); - - m_frontLeftMotor.set(wheelSpeeds[MotorType.kFrontLeft.value] * m_maxOutput); - m_frontRightMotor.set(wheelSpeeds[MotorType.kFrontRight.value] * m_maxOutput); - m_rearLeftMotor.set(wheelSpeeds[MotorType.kRearLeft.value] * m_maxOutput); - m_rearRightMotor.set(wheelSpeeds[MotorType.kRearRight.value] * m_maxOutput); - - feed(); - } - - /** - * Holonomic Drive method for Mecanum wheeled robots. - * - *

This is an alias to mecanumDrive_Polar() for backward compatibility - * - * @param magnitude The speed that the robot should drive in a given direction. [-1.0..1.0] - * @param direction The direction the robot should drive. The direction and maginitude are - * independent of the rotation rate. - * @param rotation The rate of rotation for the robot that is completely independent of the - * magnitute or direction. [-1.0..1.0] - */ - void holonomicDrive(double magnitude, double direction, double rotation) { - mecanumDrive_Polar(magnitude, direction, rotation); - } - - /** - * Set the speed of the right and left motors. This is used once an appropriate drive setup - * function is called such as twoWheelDrive(). The motors are set to "leftSpeed" and "rightSpeed" - * and includes flipping the direction of one side for opposing motors. - * - * @param leftOutput The speed to send to the left side of the robot. - * @param rightOutput The speed to send to the right side of the robot. - */ - public void setLeftRightMotorOutputs(double leftOutput, double rightOutput) { - - if (m_frontLeftMotor != null) { - m_frontLeftMotor.set(limit(leftOutput) * m_maxOutput); - } - m_rearLeftMotor.set(limit(leftOutput) * m_maxOutput); - - if (m_frontRightMotor != null) { - m_frontRightMotor.set(-limit(rightOutput) * m_maxOutput); - } - m_rearRightMotor.set(-limit(rightOutput) * m_maxOutput); - - feed(); - } - - /** Limit motor values to the -1.0 to +1.0 range. */ - protected static double limit(double number) { - if (number > 1.0) { - return 1.0; - } - if (number < -1.0) { - return -1.0; - } - return number; - } - - /** Normalize all wheel speeds if the magnitude of any wheel is greater than 1.0. */ - protected static void normalize(double[] wheelSpeeds) { - double maxMagnitude = Math.abs(wheelSpeeds[0]); - for (int i = 1; i < kMaxNumberOfMotors; i++) { - double temp = Math.abs(wheelSpeeds[i]); - if (maxMagnitude < temp) { - maxMagnitude = temp; - } - } - if (maxMagnitude > 1.0) { - for (int i = 0; i < kMaxNumberOfMotors; i++) { - wheelSpeeds[i] = wheelSpeeds[i] / maxMagnitude; - } - } - } - - /** Rotate a vector in Cartesian space. */ - protected static double[] rotateVector(double x, double y, double angle) { - double cosA = Math.cos(angle * (Math.PI / 180.0)); - double sinA = Math.sin(angle * (Math.PI / 180.0)); - double[] out = new double[2]; - out[0] = x * cosA - y * sinA; - out[1] = x * sinA + y * cosA; - return out; - } - - /** - * Invert a motor direction. This is used when a motor should run in the opposite direction as the - * drive code would normally run it. Motors that are direct drive would be inverted, the drive - * code assumes that the motors are geared with one reversal. - * - * @param motor The motor index to invert. - * @param isInverted True if the motor should be inverted when operated. - */ - public void setInvertedMotor(MotorType motor, boolean isInverted) { - switch (motor) { - case kFrontLeft: - m_frontLeftMotor.setInverted(isInverted); - break; - case kFrontRight: - m_frontRightMotor.setInverted(isInverted); - break; - case kRearLeft: - m_rearLeftMotor.setInverted(isInverted); - break; - case kRearRight: - m_rearRightMotor.setInverted(isInverted); - break; - default: - throw new IllegalArgumentException("Illegal motor type: " + motor); - } - } - - /** - * Set the turning sensitivity. - * - *

This only impacts the drive() entry-point. - * - * @param sensitivity Effectively sets the turning sensitivity (or turn radius for a given value) - */ - public void setSensitivity(double sensitivity) { - m_sensitivity = sensitivity; - } - - /** - * Configure the scaling factor for using RobotDrive with motor controllers in a mode other than - * PercentVbus. - * - * @param maxOutput Multiplied with the output percentage computed by the drive functions. - */ - public void setMaxOutput(double maxOutput) { - m_maxOutput = maxOutput; - } - - /** Free the speed controllers if they were allocated locally. */ - @Override - public void close() { - if (m_allocatedSpeedControllers) { - if (m_frontLeftMotor != null) { - ((PWM) m_frontLeftMotor).close(); - } - if (m_frontRightMotor != null) { - ((PWM) m_frontRightMotor).close(); - } - if (m_rearLeftMotor != null) { - ((PWM) m_rearLeftMotor).close(); - } - if (m_rearRightMotor != null) { - ((PWM) m_rearRightMotor).close(); - } - } - } - - @Override - public String getDescription() { - return "Robot Drive"; - } - - @Override - public void stopMotor() { - if (m_frontLeftMotor != null) { - m_frontLeftMotor.stopMotor(); - } - if (m_frontRightMotor != null) { - m_frontRightMotor.stopMotor(); - } - if (m_rearLeftMotor != null) { - m_rearLeftMotor.stopMotor(); - } - if (m_rearRightMotor != null) { - m_rearRightMotor.stopMotor(); - } - - feed(); - } - - protected int getNumMotors() { - int motors = 0; - if (m_frontLeftMotor != null) { - motors++; - } - if (m_frontRightMotor != null) { - motors++; - } - if (m_rearLeftMotor != null) { - motors++; - } - if (m_rearRightMotor != null) { - motors++; - } - return motors; - } -} diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/drive/DifferentialDrive.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/drive/DifferentialDrive.java index cb16ce3fad..f68f36d7c7 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/drive/DifferentialDrive.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/drive/DifferentialDrive.java @@ -86,14 +86,13 @@ import java.util.StringJoiner; * value can be changed with {@link #setDeadband}. * *

RobotDrive porting guide:
- * {@link #tankDrive(double, double)} is equivalent to {@link - * edu.wpi.first.wpilibj.RobotDrive#tankDrive(double, double)} if a deadband of 0 is used.
- * {@link #arcadeDrive(double, double)} is equivalent to {@link - * edu.wpi.first.wpilibj.RobotDrive#arcadeDrive(double, double)} if a deadband of 0 is used and the - * the rotation input is inverted eg arcadeDrive(y, -rotation)
- * {@link #curvatureDrive(double, double, boolean)} is similar in concept to {@link - * edu.wpi.first.wpilibj.RobotDrive#drive(double, double)} with the addition of a quick turn mode. - * However, it is not designed to give exactly the same response. + * {@link #tankDrive(double, double)} is equivalent to RobotDrive's tankDrive(double, double) if a + * deadband of 0 is used.
+ * {@link #arcadeDrive(double, double)} is equivalent to RobotDrive's arcadeDrive(double, double) if + * a deadband of 0 is used and the the rotation input is inverted eg arcadeDrive(y, -rotation)
+ * {@link #curvatureDrive(double, double, boolean)} is similar in concept to RobotDrive's + * drive(double, double) with the addition of a quick turn mode. However, it is not designed to give + * exactly the same response. */ public class DifferentialDrive extends RobotDriveBase implements Sendable, AutoCloseable { public static final double kDefaultQuickStopThreshold = 0.2; diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/drive/MecanumDrive.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/drive/MecanumDrive.java index 8189e68a58..1930ecfabe 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/drive/MecanumDrive.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/drive/MecanumDrive.java @@ -50,13 +50,12 @@ import java.util.StringJoiner; *

RobotDrive porting guide:
* In MecanumDrive, the right side speed controllers are automatically inverted, while in * RobotDrive, no speed controllers are automatically inverted.
- * {@link #driveCartesian(double, double, double, double)} is equivalent to {@link - * edu.wpi.first.wpilibj.RobotDrive#mecanumDrive_Cartesian(double, double, double, double)} if a - * deadband of 0 is used, and the ySpeed and gyroAngle values are inverted compared to RobotDrive - * (eg driveCartesian(xSpeed, -ySpeed, zRotation, -gyroAngle).
- * {@link #drivePolar(double, double, double)} is equivalent to {@link - * edu.wpi.first.wpilibj.RobotDrive#mecanumDrive_Polar(double, double, double)} if a deadband of 0 - * is used. + * {@link #driveCartesian(double, double, double, double)} is equivalent to RobotDrive's + * mecanumDrive_Cartesian(double, double, double, double) if a deadband of 0 is used, and the ySpeed + * and gyroAngle values are inverted compared to RobotDrive (eg driveCartesian(xSpeed, -ySpeed, + * zRotation, -gyroAngle).
+ * {@link #drivePolar(double, double, double)} is equivalent to RobotDrive's + * mecanumDrive_Polar(double, double, double)} if a deadband of 0 is used. */ public class MecanumDrive extends RobotDriveBase implements Sendable, AutoCloseable { private static int instances; diff --git a/wpilibj/src/test/java/edu/wpi/first/wpilibj/drive/DriveTest.java b/wpilibj/src/test/java/edu/wpi/first/wpilibj/drive/DriveTest.java deleted file mode 100644 index 388801a9be..0000000000 --- a/wpilibj/src/test/java/edu/wpi/first/wpilibj/drive/DriveTest.java +++ /dev/null @@ -1,265 +0,0 @@ -// Copyright (c) FIRST and other WPILib contributors. -// Open Source Software; you can modify and/or share it under the terms of -// the WPILib BSD license file in the root directory of this project. - -package edu.wpi.first.wpilibj.drive; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -import edu.wpi.first.wpilibj.MockSpeedController; -import edu.wpi.first.wpilibj.RobotDrive; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -/** Tests DifferentialDrive and MecanumDrive. */ -public class DriveTest { - private final MockSpeedController m_rdFrontLeft = new MockSpeedController(); - private final MockSpeedController m_rdRearLeft = new MockSpeedController(); - private final MockSpeedController m_rdFrontRight = new MockSpeedController(); - private final MockSpeedController m_rdRearRight = new MockSpeedController(); - private final MockSpeedController m_frontLeft = new MockSpeedController(); - private final MockSpeedController m_rearLeft = new MockSpeedController(); - private final MockSpeedController m_frontRight = new MockSpeedController(); - private final MockSpeedController m_rearRight = new MockSpeedController(); - private final RobotDrive m_robotDrive = - new RobotDrive(m_rdFrontLeft, m_rdRearLeft, m_rdFrontRight, m_rdRearRight); - private final DifferentialDrive m_differentialDrive = - new DifferentialDrive(m_frontLeft, m_frontRight); - private final MecanumDrive m_mecanumDrive = - new MecanumDrive(m_frontLeft, m_rearLeft, m_frontRight, m_rearRight); - - private final double[] m_testJoystickValues = {1.0, 0.9, 0.5, 0.01, 0.0, -0.01, -0.5, -0.9, -1.0}; - private final double[] m_testGyroValues = { - 0, 30, 45, 90, 135, 180, 225, 270, 305, 360, 540, -45, -90, -135, -180, -225, -270, -305, -360, - -540 - }; - - @BeforeEach - void setUp() { - m_differentialDrive.setDeadband(0.0); - m_differentialDrive.setSafetyEnabled(false); - m_mecanumDrive.setDeadband(0.0); - m_mecanumDrive.setSafetyEnabled(false); - m_robotDrive.setSafetyEnabled(false); - } - - @Test - public void testTankDriveSquared() { - for (double leftJoystick : m_testJoystickValues) { - for (double rightJoystick : m_testJoystickValues) { - m_robotDrive.tankDrive(leftJoystick, rightJoystick); - m_differentialDrive.tankDrive(leftJoystick, rightJoystick); - assertEquals( - m_rdFrontLeft.get(), - m_frontLeft.get(), - 0.01, - "Left Motor squared didn't match. Left Joystick: " - + leftJoystick - + " Right Joystick: " - + rightJoystick); - assertEquals( - m_rdFrontRight.get(), - m_frontRight.get(), - 0.01, - "Right Motor squared didn't match. Left Joystick: " - + leftJoystick - + " Right Joystick: " - + rightJoystick); - } - } - } - - @Test - void testTankDrive() { - for (double leftJoystick : m_testJoystickValues) { - for (double rightJoystick : m_testJoystickValues) { - m_robotDrive.tankDrive(leftJoystick, rightJoystick, false); - m_differentialDrive.tankDrive(leftJoystick, rightJoystick, false); - assertEquals( - m_rdFrontLeft.get(), - m_frontLeft.get(), - 0.01, - "Left Motor didn't match. Left Joystick: " - + leftJoystick - + " Right Joystick: " - + rightJoystick); - assertEquals( - m_rdFrontRight.get(), - m_frontRight.get(), - 0.01, - "Right Motor didn't match. Left Joystick: " - + leftJoystick - + " Right Joystick: " - + rightJoystick); - } - } - } - - @Test - void testArcadeDriveSquared() { - for (double moveJoystick : m_testJoystickValues) { - for (double rotateJoystick : m_testJoystickValues) { - m_robotDrive.arcadeDrive(moveJoystick, rotateJoystick); - m_differentialDrive.arcadeDrive(moveJoystick, -rotateJoystick); - assertEquals( - m_rdFrontLeft.get(), - m_frontLeft.get(), - 0.01, - "Left Motor squared didn't match. Move Joystick: " - + moveJoystick - + " Rotate Joystick: " - + rotateJoystick); - assertEquals( - m_rdFrontRight.get(), - m_frontRight.get(), - 0.01, - "Right Motor squared didn't match. Move Joystick: " - + moveJoystick - + " Rotate Joystick: " - + rotateJoystick); - } - } - } - - @Test - void testArcadeDrive() { - for (double moveJoystick : m_testJoystickValues) { - for (double rotateJoystick : m_testJoystickValues) { - m_robotDrive.arcadeDrive(moveJoystick, rotateJoystick, false); - m_differentialDrive.arcadeDrive(moveJoystick, -rotateJoystick, false); - assertEquals( - m_rdFrontLeft.get(), - m_frontLeft.get(), - 0.01, - "Left Motor didn't match. Move Joystick: " - + moveJoystick - + " Rotate Joystick: " - + rotateJoystick); - assertEquals( - m_rdFrontRight.get(), - m_frontRight.get(), - 0.01, - "Right Motor didn't match. Move Joystick: " - + moveJoystick - + " Rotate Joystick: " - + rotateJoystick); - } - } - } - - @Test - void testMecanumPolar() { - for (double magnitudeJoystick : m_testJoystickValues) { - for (double directionJoystick : m_testGyroValues) { - for (double rotationJoystick : m_testJoystickValues) { - m_robotDrive.mecanumDrive_Polar(magnitudeJoystick, directionJoystick, rotationJoystick); - m_mecanumDrive.drivePolar(magnitudeJoystick, directionJoystick, rotationJoystick); - assertEquals( - m_rdFrontLeft.get(), - m_frontLeft.get(), - 0.01, - "Left Front Motor didn't match. Magnitude Joystick: " - + magnitudeJoystick - + " Direction Joystick: " - + directionJoystick - + " RotationJoystick: " - + rotationJoystick); - assertEquals( - m_rdFrontRight.get(), - -m_frontRight.get(), - 0.01, - "Right Front Motor didn't match. Magnitude Joystick: " - + magnitudeJoystick - + " Direction Joystick: " - + directionJoystick - + " RotationJoystick: " - + rotationJoystick); - assertEquals( - m_rdRearLeft.get(), - m_rearLeft.get(), - 0.01, - "Left Rear Motor didn't match. Magnitude Joystick: " - + magnitudeJoystick - + " Direction Joystick: " - + directionJoystick - + " RotationJoystick: " - + rotationJoystick); - assertEquals( - m_rdRearRight.get(), - -m_rearRight.get(), - 0.01, - "Right Rear Motor didn't match. Magnitude Joystick: " - + magnitudeJoystick - + " Direction Joystick: " - + directionJoystick - + " RotationJoystick: " - + rotationJoystick); - } - } - } - } - - @Test - @SuppressWarnings("checkstyle:LocalVariableName") - void testMecanumCartesian() { - for (double x_Joystick : m_testJoystickValues) { - for (double y_Joystick : m_testJoystickValues) { - for (double rotationJoystick : m_testJoystickValues) { - for (double gyroValue : m_testGyroValues) { - m_robotDrive.mecanumDrive_Cartesian( - x_Joystick, y_Joystick, rotationJoystick, gyroValue); - m_mecanumDrive.driveCartesian(x_Joystick, -y_Joystick, rotationJoystick, -gyroValue); - assertEquals( - m_rdFrontLeft.get(), - m_frontLeft.get(), - 0.01, - "Left Front Motor didn't match. X Joystick: " - + x_Joystick - + " Y Joystick: " - + y_Joystick - + " RotationJoystick: " - + rotationJoystick - + " Gyro: " - + gyroValue); - assertEquals( - m_rdFrontRight.get(), - -m_frontRight.get(), - 0.01, - "Right Front Motor didn't match. X Joystick: " - + x_Joystick - + " Y Joystick: " - + y_Joystick - + " RotationJoystick: " - + rotationJoystick - + " Gyro: " - + gyroValue); - assertEquals( - m_rdRearLeft.get(), - m_rearLeft.get(), - 0.01, - "Left Rear Motor didn't match. X Joystick: " - + x_Joystick - + " Y Joystick: " - + y_Joystick - + " RotationJoystick: " - + rotationJoystick - + " Gyro: " - + gyroValue); - assertEquals( - m_rdRearRight.get(), - -m_rearRight.get(), - 0.01, - "Right Rear Motor didn't match. X Joystick: " - + x_Joystick - + " Y Joystick: " - + y_Joystick - + " RotationJoystick: " - + rotationJoystick - + " Gyro: " - + gyroValue); - } - } - } - } - } -} diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/examples.json b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/examples.json index f188f103ea..005bd0f83f 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/examples.json +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/examples.json @@ -12,7 +12,7 @@ }, { "name": "Tank Drive", - "description": "Demonstrate the use of the RobotDrive class doing teleop driving with tank steering", + "description": "Demonstrate the use of the DifferentialDrive class doing teleop driving with tank steering", "tags": [ "Actuators", "Joystick", @@ -37,7 +37,7 @@ }, { "name": "Mecanum Drive", - "description": "Demonstrate the use of the RobotDrive class doing teleop driving with Mecanum steering", + "description": "Demonstrate the use of the MecanumDrive class doing teleop driving with Mecanum steering", "tags": [ "Actuators", "Joystick", diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/gyro/Robot.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/gyro/Robot.java index c1f3d2d365..9bdb61b227 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/gyro/Robot.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/gyro/Robot.java @@ -39,8 +39,8 @@ public class Robot extends TimedRobot { } /** - * The motor speed is set from the joystick while the RobotDrive turning value is assigned from - * the error between the setpoint and the gyro angle. + * The motor speed is set from the joystick while the DifferentialDrive turning value is assigned + * from the error between the setpoint and the gyro angle. */ @Override public void teleopPeriodic() { diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/mecanumdrive/Robot.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/mecanumdrive/Robot.java index 34bb9f5930..5b2ef43398 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/mecanumdrive/Robot.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/mecanumdrive/Robot.java @@ -9,7 +9,7 @@ import edu.wpi.first.wpilibj.PWMSparkMax; import edu.wpi.first.wpilibj.TimedRobot; import edu.wpi.first.wpilibj.drive.MecanumDrive; -/** This is a demo program showing how to use Mecanum control with the RobotDrive class. */ +/** This is a demo program showing how to use Mecanum control with the MecanumDrive class. */ public class Robot extends TimedRobot { private static final int kFrontLeftChannel = 2; private static final int kRearLeftChannel = 3; diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/tankdrive/Robot.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/tankdrive/Robot.java index 71bf29084e..bf2a568e1a 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/tankdrive/Robot.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/tankdrive/Robot.java @@ -10,8 +10,8 @@ import edu.wpi.first.wpilibj.TimedRobot; import edu.wpi.first.wpilibj.drive.DifferentialDrive; /** - * This is a demo program showing the use of the RobotDrive class, specifically it contains the code - * necessary to operate a robot with tank drive. + * This is a demo program showing the use of the DifferentialDrive class, specifically it contains + * the code necessary to operate a robot with tank drive. */ public class Robot extends TimedRobot { private DifferentialDrive m_myRobot; From 3abe0b9d49ba780c9bd1c68b1d9878cad54e8106 Mon Sep 17 00:00:00 2001 From: Peter Johnson Date: Sat, 10 Apr 2021 11:42:41 -0700 Subject: [PATCH 18/34] [cscore] Move java package to edu.wpi.first.cscore (#3294) This is more consistent with the other Java packages, and also is more correct, as we own the first.wpi.edu domain but not the full wpi.edu domain. --- .../src/main/java/edu/wpi/Main.java | 2 +- .../wpi/first/cameraserver/CameraServer.java | 28 +- .../edu/wpi/first/vision/VisionRunner.java | 4 +- .../edu/wpi/first/vision/VisionThread.java | 2 +- .../edu/wpi/first/vision/package-info.java | 2 +- .../edu/wpi/{ => first}/cscore/DevMain.java | 2 +- .../wpi/{ => first}/cscore/AxisCamera.java | 2 +- .../{ => first}/cscore/CameraServerCvJNI.java | 2 +- .../{ => first}/cscore/CameraServerJNI.java | 4 +- .../edu/wpi/{ => first}/cscore/CvSink.java | 2 +- .../edu/wpi/{ => first}/cscore/CvSource.java | 2 +- .../wpi/{ => first}/cscore/HttpCamera.java | 2 +- .../edu/wpi/{ => first}/cscore/ImageSink.java | 2 +- .../wpi/{ => first}/cscore/ImageSource.java | 2 +- .../wpi/{ => first}/cscore/MjpegServer.java | 2 +- .../edu/wpi/{ => first}/cscore/UsbCamera.java | 2 +- .../wpi/{ => first}/cscore/UsbCameraInfo.java | 2 +- .../wpi/{ => first}/cscore/VideoCamera.java | 2 +- .../wpi/{ => first}/cscore/VideoEvent.java | 2 +- .../{ => first}/cscore/VideoException.java | 2 +- .../wpi/{ => first}/cscore/VideoListener.java | 2 +- .../edu/wpi/{ => first}/cscore/VideoMode.java | 2 +- .../wpi/{ => first}/cscore/VideoProperty.java | 2 +- .../edu/wpi/{ => first}/cscore/VideoSink.java | 2 +- .../wpi/{ => first}/cscore/VideoSource.java | 2 +- .../wpi/{ => first}/cscore/raw/RawFrame.java | 4 +- .../wpi/{ => first}/cscore/raw/RawSink.java | 6 +- .../wpi/{ => first}/cscore/raw/RawSource.java | 8 +- .../main/native/cpp/jni/CameraServerJNI.cpp | 424 +++++++++--------- .../edu/wpi/{ => first}/cscore/JNITest.java | 2 +- .../wpi/{ => first}/cscore/UsbCameraTest.java | 2 +- .../java/edu/wpi/first/wpilibj/RobotBase.java | 2 +- .../wpilibj/shuffleboard/BuiltInWidgets.java | 2 +- .../shuffleboard/SendableCameraWrapper.java | 2 +- .../shuffleboard/ShuffleboardContainer.java | 2 +- .../first/wpilibj/vision/VisionRunner.java | 4 +- .../first/wpilibj/vision/VisionThread.java | 2 +- .../first/wpilibj/vision/package-info.java | 2 +- .../wpilibj/examples/axiscamera/Robot.java | 6 +- .../examples/intermediatevision/Robot.java | 6 +- 40 files changed, 277 insertions(+), 277 deletions(-) rename cscore/src/dev/java/edu/wpi/{ => first}/cscore/DevMain.java (94%) rename cscore/src/main/java/edu/wpi/{ => first}/cscore/AxisCamera.java (97%) rename cscore/src/main/java/edu/wpi/{ => first}/cscore/CameraServerCvJNI.java (98%) rename cscore/src/main/java/edu/wpi/{ => first}/cscore/CameraServerJNI.java (99%) rename cscore/src/main/java/edu/wpi/{ => first}/cscore/CvSink.java (98%) rename cscore/src/main/java/edu/wpi/{ => first}/cscore/CvSource.java (98%) rename cscore/src/main/java/edu/wpi/{ => first}/cscore/HttpCamera.java (99%) rename cscore/src/main/java/edu/wpi/{ => first}/cscore/ImageSink.java (97%) rename cscore/src/main/java/edu/wpi/{ => first}/cscore/ImageSource.java (99%) rename cscore/src/main/java/edu/wpi/{ => first}/cscore/MjpegServer.java (99%) rename cscore/src/main/java/edu/wpi/{ => first}/cscore/UsbCamera.java (98%) rename cscore/src/main/java/edu/wpi/{ => first}/cscore/UsbCameraInfo.java (98%) rename cscore/src/main/java/edu/wpi/{ => first}/cscore/VideoCamera.java (98%) rename cscore/src/main/java/edu/wpi/{ => first}/cscore/VideoEvent.java (99%) rename cscore/src/main/java/edu/wpi/{ => first}/cscore/VideoException.java (94%) rename cscore/src/main/java/edu/wpi/{ => first}/cscore/VideoListener.java (99%) rename cscore/src/main/java/edu/wpi/{ => first}/cscore/VideoMode.java (98%) rename cscore/src/main/java/edu/wpi/{ => first}/cscore/VideoProperty.java (98%) rename cscore/src/main/java/edu/wpi/{ => first}/cscore/VideoSink.java (99%) rename cscore/src/main/java/edu/wpi/{ => first}/cscore/VideoSource.java (99%) rename cscore/src/main/java/edu/wpi/{ => first}/cscore/raw/RawFrame.java (97%) rename cscore/src/main/java/edu/wpi/{ => first}/cscore/raw/RawSink.java (94%) rename cscore/src/main/java/edu/wpi/{ => first}/cscore/raw/RawSource.java (93%) rename cscore/src/test/java/edu/wpi/{ => first}/cscore/JNITest.java (92%) rename cscore/src/test/java/edu/wpi/{ => first}/cscore/UsbCameraTest.java (98%) diff --git a/cameraserver/multiCameraServer/src/main/java/edu/wpi/Main.java b/cameraserver/multiCameraServer/src/main/java/edu/wpi/Main.java index 3366b39979..f488487c55 100644 --- a/cameraserver/multiCameraServer/src/main/java/edu/wpi/Main.java +++ b/cameraserver/multiCameraServer/src/main/java/edu/wpi/Main.java @@ -10,8 +10,8 @@ import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.JsonParser; -import edu.wpi.cscore.VideoSource; import edu.wpi.first.cameraserver.CameraServer; +import edu.wpi.first.cscore.VideoSource; import edu.wpi.first.networktables.NetworkTableInstance; import java.io.IOException; import java.nio.file.Files; diff --git a/cameraserver/src/main/java/edu/wpi/first/cameraserver/CameraServer.java b/cameraserver/src/main/java/edu/wpi/first/cameraserver/CameraServer.java index 6867d5f9ec..b994749577 100644 --- a/cameraserver/src/main/java/edu/wpi/first/cameraserver/CameraServer.java +++ b/cameraserver/src/main/java/edu/wpi/first/cameraserver/CameraServer.java @@ -4,20 +4,20 @@ package edu.wpi.first.cameraserver; -import edu.wpi.cscore.AxisCamera; -import edu.wpi.cscore.CameraServerJNI; -import edu.wpi.cscore.CvSink; -import edu.wpi.cscore.CvSource; -import edu.wpi.cscore.MjpegServer; -import edu.wpi.cscore.UsbCamera; -import edu.wpi.cscore.VideoEvent; -import edu.wpi.cscore.VideoException; -import edu.wpi.cscore.VideoListener; -import edu.wpi.cscore.VideoMode; -import edu.wpi.cscore.VideoMode.PixelFormat; -import edu.wpi.cscore.VideoProperty; -import edu.wpi.cscore.VideoSink; -import edu.wpi.cscore.VideoSource; +import edu.wpi.first.cscore.AxisCamera; +import edu.wpi.first.cscore.CameraServerJNI; +import edu.wpi.first.cscore.CvSink; +import edu.wpi.first.cscore.CvSource; +import edu.wpi.first.cscore.MjpegServer; +import edu.wpi.first.cscore.UsbCamera; +import edu.wpi.first.cscore.VideoEvent; +import edu.wpi.first.cscore.VideoException; +import edu.wpi.first.cscore.VideoListener; +import edu.wpi.first.cscore.VideoMode; +import edu.wpi.first.cscore.VideoMode.PixelFormat; +import edu.wpi.first.cscore.VideoProperty; +import edu.wpi.first.cscore.VideoSink; +import edu.wpi.first.cscore.VideoSource; import edu.wpi.first.networktables.EntryListenerFlags; import edu.wpi.first.networktables.NetworkTable; import edu.wpi.first.networktables.NetworkTableEntry; diff --git a/cameraserver/src/main/java/edu/wpi/first/vision/VisionRunner.java b/cameraserver/src/main/java/edu/wpi/first/vision/VisionRunner.java index 2a1275791c..ab7072f6c9 100644 --- a/cameraserver/src/main/java/edu/wpi/first/vision/VisionRunner.java +++ b/cameraserver/src/main/java/edu/wpi/first/vision/VisionRunner.java @@ -4,9 +4,9 @@ package edu.wpi.first.vision; -import edu.wpi.cscore.CvSink; -import edu.wpi.cscore.VideoSource; import edu.wpi.first.cameraserver.CameraServerSharedStore; +import edu.wpi.first.cscore.CvSink; +import edu.wpi.first.cscore.VideoSource; import org.opencv.core.Mat; /** diff --git a/cameraserver/src/main/java/edu/wpi/first/vision/VisionThread.java b/cameraserver/src/main/java/edu/wpi/first/vision/VisionThread.java index 5ce896dbf9..6f1a1e35b5 100644 --- a/cameraserver/src/main/java/edu/wpi/first/vision/VisionThread.java +++ b/cameraserver/src/main/java/edu/wpi/first/vision/VisionThread.java @@ -4,7 +4,7 @@ package edu.wpi.first.vision; -import edu.wpi.cscore.VideoSource; +import edu.wpi.first.cscore.VideoSource; /** * A vision thread is a special thread that runs a vision pipeline. It is a daemon thread; it diff --git a/cameraserver/src/main/java/edu/wpi/first/vision/package-info.java b/cameraserver/src/main/java/edu/wpi/first/vision/package-info.java index 758ea2889d..b7bab9c35c 100644 --- a/cameraserver/src/main/java/edu/wpi/first/vision/package-info.java +++ b/cameraserver/src/main/java/edu/wpi/first/vision/package-info.java @@ -13,7 +13,7 @@ * implements VisionRunner.Listener<MyFindTotePipeline> { * * // A USB camera connected to the roboRIO. - * private {@link edu.wpi.cscore.VideoSource VideoSource} usbCamera; + * private {@link edu.wpi.first.cscore.VideoSource VideoSource} usbCamera; * * // A vision pipeline. This could be handwritten or generated by GRIP. * // This has to implement {@link edu.wpi.first.vision.VisionPipeline}. diff --git a/cscore/src/dev/java/edu/wpi/cscore/DevMain.java b/cscore/src/dev/java/edu/wpi/first/cscore/DevMain.java similarity index 94% rename from cscore/src/dev/java/edu/wpi/cscore/DevMain.java rename to cscore/src/dev/java/edu/wpi/first/cscore/DevMain.java index 69198cb247..5ccbed722b 100644 --- a/cscore/src/dev/java/edu/wpi/cscore/DevMain.java +++ b/cscore/src/dev/java/edu/wpi/first/cscore/DevMain.java @@ -2,7 +2,7 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.cscore; +package edu.wpi.first.cscore; import edu.wpi.first.wpiutil.RuntimeDetector; diff --git a/cscore/src/main/java/edu/wpi/cscore/AxisCamera.java b/cscore/src/main/java/edu/wpi/first/cscore/AxisCamera.java similarity index 97% rename from cscore/src/main/java/edu/wpi/cscore/AxisCamera.java rename to cscore/src/main/java/edu/wpi/first/cscore/AxisCamera.java index c219517ee5..296bd6bd14 100644 --- a/cscore/src/main/java/edu/wpi/cscore/AxisCamera.java +++ b/cscore/src/main/java/edu/wpi/first/cscore/AxisCamera.java @@ -2,7 +2,7 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.cscore; +package edu.wpi.first.cscore; /** A source that represents an Axis IP camera. */ public class AxisCamera extends HttpCamera { diff --git a/cscore/src/main/java/edu/wpi/cscore/CameraServerCvJNI.java b/cscore/src/main/java/edu/wpi/first/cscore/CameraServerCvJNI.java similarity index 98% rename from cscore/src/main/java/edu/wpi/cscore/CameraServerCvJNI.java rename to cscore/src/main/java/edu/wpi/first/cscore/CameraServerCvJNI.java index 34fcfc22c5..6d382c87db 100644 --- a/cscore/src/main/java/edu/wpi/cscore/CameraServerCvJNI.java +++ b/cscore/src/main/java/edu/wpi/first/cscore/CameraServerCvJNI.java @@ -2,7 +2,7 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.cscore; +package edu.wpi.first.cscore; import edu.wpi.first.wpiutil.RuntimeLoader; import java.io.IOException; diff --git a/cscore/src/main/java/edu/wpi/cscore/CameraServerJNI.java b/cscore/src/main/java/edu/wpi/first/cscore/CameraServerJNI.java similarity index 99% rename from cscore/src/main/java/edu/wpi/cscore/CameraServerJNI.java rename to cscore/src/main/java/edu/wpi/first/cscore/CameraServerJNI.java index 2672a90ecd..b7a9216e82 100644 --- a/cscore/src/main/java/edu/wpi/cscore/CameraServerJNI.java +++ b/cscore/src/main/java/edu/wpi/first/cscore/CameraServerJNI.java @@ -2,9 +2,9 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.cscore; +package edu.wpi.first.cscore; -import edu.wpi.cscore.raw.RawFrame; +import edu.wpi.first.cscore.raw.RawFrame; import edu.wpi.first.wpiutil.RuntimeLoader; import java.io.IOException; import java.nio.ByteBuffer; diff --git a/cscore/src/main/java/edu/wpi/cscore/CvSink.java b/cscore/src/main/java/edu/wpi/first/cscore/CvSink.java similarity index 98% rename from cscore/src/main/java/edu/wpi/cscore/CvSink.java rename to cscore/src/main/java/edu/wpi/first/cscore/CvSink.java index 5e3489ca12..28f0dd13a8 100644 --- a/cscore/src/main/java/edu/wpi/cscore/CvSink.java +++ b/cscore/src/main/java/edu/wpi/first/cscore/CvSink.java @@ -2,7 +2,7 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.cscore; +package edu.wpi.first.cscore; import org.opencv.core.Mat; diff --git a/cscore/src/main/java/edu/wpi/cscore/CvSource.java b/cscore/src/main/java/edu/wpi/first/cscore/CvSource.java similarity index 98% rename from cscore/src/main/java/edu/wpi/cscore/CvSource.java rename to cscore/src/main/java/edu/wpi/first/cscore/CvSource.java index 09f551b7c6..3934a09c0b 100644 --- a/cscore/src/main/java/edu/wpi/cscore/CvSource.java +++ b/cscore/src/main/java/edu/wpi/first/cscore/CvSource.java @@ -2,7 +2,7 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.cscore; +package edu.wpi.first.cscore; import org.opencv.core.Mat; diff --git a/cscore/src/main/java/edu/wpi/cscore/HttpCamera.java b/cscore/src/main/java/edu/wpi/first/cscore/HttpCamera.java similarity index 99% rename from cscore/src/main/java/edu/wpi/cscore/HttpCamera.java rename to cscore/src/main/java/edu/wpi/first/cscore/HttpCamera.java index 1caeb338ac..9758f90436 100644 --- a/cscore/src/main/java/edu/wpi/cscore/HttpCamera.java +++ b/cscore/src/main/java/edu/wpi/first/cscore/HttpCamera.java @@ -2,7 +2,7 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.cscore; +package edu.wpi.first.cscore; /** A source that represents a MJPEG-over-HTTP (IP) camera. */ public class HttpCamera extends VideoCamera { diff --git a/cscore/src/main/java/edu/wpi/cscore/ImageSink.java b/cscore/src/main/java/edu/wpi/first/cscore/ImageSink.java similarity index 97% rename from cscore/src/main/java/edu/wpi/cscore/ImageSink.java rename to cscore/src/main/java/edu/wpi/first/cscore/ImageSink.java index 8907ee1106..55318da1cb 100644 --- a/cscore/src/main/java/edu/wpi/cscore/ImageSink.java +++ b/cscore/src/main/java/edu/wpi/first/cscore/ImageSink.java @@ -2,7 +2,7 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.cscore; +package edu.wpi.first.cscore; public abstract class ImageSink extends VideoSink { protected ImageSink(int handle) { diff --git a/cscore/src/main/java/edu/wpi/cscore/ImageSource.java b/cscore/src/main/java/edu/wpi/first/cscore/ImageSource.java similarity index 99% rename from cscore/src/main/java/edu/wpi/cscore/ImageSource.java rename to cscore/src/main/java/edu/wpi/first/cscore/ImageSource.java index 93f994c250..431e75cfa4 100644 --- a/cscore/src/main/java/edu/wpi/cscore/ImageSource.java +++ b/cscore/src/main/java/edu/wpi/first/cscore/ImageSource.java @@ -2,7 +2,7 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.cscore; +package edu.wpi.first.cscore; public abstract class ImageSource extends VideoSource { protected ImageSource(int handle) { diff --git a/cscore/src/main/java/edu/wpi/cscore/MjpegServer.java b/cscore/src/main/java/edu/wpi/first/cscore/MjpegServer.java similarity index 99% rename from cscore/src/main/java/edu/wpi/cscore/MjpegServer.java rename to cscore/src/main/java/edu/wpi/first/cscore/MjpegServer.java index 29619d27d3..a1ffb6331d 100644 --- a/cscore/src/main/java/edu/wpi/cscore/MjpegServer.java +++ b/cscore/src/main/java/edu/wpi/first/cscore/MjpegServer.java @@ -2,7 +2,7 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.cscore; +package edu.wpi.first.cscore; /** A sink that acts as a MJPEG-over-HTTP network server. */ public class MjpegServer extends VideoSink { diff --git a/cscore/src/main/java/edu/wpi/cscore/UsbCamera.java b/cscore/src/main/java/edu/wpi/first/cscore/UsbCamera.java similarity index 98% rename from cscore/src/main/java/edu/wpi/cscore/UsbCamera.java rename to cscore/src/main/java/edu/wpi/first/cscore/UsbCamera.java index 144a4aadb5..2b5cf5a41a 100644 --- a/cscore/src/main/java/edu/wpi/cscore/UsbCamera.java +++ b/cscore/src/main/java/edu/wpi/first/cscore/UsbCamera.java @@ -2,7 +2,7 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.cscore; +package edu.wpi.first.cscore; /** A source that represents a USB camera. */ public class UsbCamera extends VideoCamera { diff --git a/cscore/src/main/java/edu/wpi/cscore/UsbCameraInfo.java b/cscore/src/main/java/edu/wpi/first/cscore/UsbCameraInfo.java similarity index 98% rename from cscore/src/main/java/edu/wpi/cscore/UsbCameraInfo.java rename to cscore/src/main/java/edu/wpi/first/cscore/UsbCameraInfo.java index 4899c77828..df856e8d2d 100644 --- a/cscore/src/main/java/edu/wpi/cscore/UsbCameraInfo.java +++ b/cscore/src/main/java/edu/wpi/first/cscore/UsbCameraInfo.java @@ -2,7 +2,7 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.cscore; +package edu.wpi.first.cscore; /** USB camera information. */ public class UsbCameraInfo { diff --git a/cscore/src/main/java/edu/wpi/cscore/VideoCamera.java b/cscore/src/main/java/edu/wpi/first/cscore/VideoCamera.java similarity index 98% rename from cscore/src/main/java/edu/wpi/cscore/VideoCamera.java rename to cscore/src/main/java/edu/wpi/first/cscore/VideoCamera.java index 2a93112a9c..ca3b09e642 100644 --- a/cscore/src/main/java/edu/wpi/cscore/VideoCamera.java +++ b/cscore/src/main/java/edu/wpi/first/cscore/VideoCamera.java @@ -2,7 +2,7 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.cscore; +package edu.wpi.first.cscore; /** A source that represents a video camera. */ public class VideoCamera extends VideoSource { diff --git a/cscore/src/main/java/edu/wpi/cscore/VideoEvent.java b/cscore/src/main/java/edu/wpi/first/cscore/VideoEvent.java similarity index 99% rename from cscore/src/main/java/edu/wpi/cscore/VideoEvent.java rename to cscore/src/main/java/edu/wpi/first/cscore/VideoEvent.java index c2731fdc78..ca38109b47 100644 --- a/cscore/src/main/java/edu/wpi/cscore/VideoEvent.java +++ b/cscore/src/main/java/edu/wpi/first/cscore/VideoEvent.java @@ -2,7 +2,7 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.cscore; +package edu.wpi.first.cscore; /** Video event. */ public class VideoEvent { diff --git a/cscore/src/main/java/edu/wpi/cscore/VideoException.java b/cscore/src/main/java/edu/wpi/first/cscore/VideoException.java similarity index 94% rename from cscore/src/main/java/edu/wpi/cscore/VideoException.java rename to cscore/src/main/java/edu/wpi/first/cscore/VideoException.java index 1142c0f8d7..1c445c6f77 100644 --- a/cscore/src/main/java/edu/wpi/cscore/VideoException.java +++ b/cscore/src/main/java/edu/wpi/first/cscore/VideoException.java @@ -2,7 +2,7 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.cscore; +package edu.wpi.first.cscore; /** An exception raised by the camera server. */ public class VideoException extends RuntimeException { diff --git a/cscore/src/main/java/edu/wpi/cscore/VideoListener.java b/cscore/src/main/java/edu/wpi/first/cscore/VideoListener.java similarity index 99% rename from cscore/src/main/java/edu/wpi/cscore/VideoListener.java rename to cscore/src/main/java/edu/wpi/first/cscore/VideoListener.java index cfaeb9da2a..0d77461432 100644 --- a/cscore/src/main/java/edu/wpi/cscore/VideoListener.java +++ b/cscore/src/main/java/edu/wpi/first/cscore/VideoListener.java @@ -2,7 +2,7 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.cscore; +package edu.wpi.first.cscore; import java.util.HashMap; import java.util.Map; diff --git a/cscore/src/main/java/edu/wpi/cscore/VideoMode.java b/cscore/src/main/java/edu/wpi/first/cscore/VideoMode.java similarity index 98% rename from cscore/src/main/java/edu/wpi/cscore/VideoMode.java rename to cscore/src/main/java/edu/wpi/first/cscore/VideoMode.java index b7191eeb40..af7fb8f1cb 100644 --- a/cscore/src/main/java/edu/wpi/cscore/VideoMode.java +++ b/cscore/src/main/java/edu/wpi/first/cscore/VideoMode.java @@ -2,7 +2,7 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.cscore; +package edu.wpi.first.cscore; /** Video mode. */ public class VideoMode { diff --git a/cscore/src/main/java/edu/wpi/cscore/VideoProperty.java b/cscore/src/main/java/edu/wpi/first/cscore/VideoProperty.java similarity index 98% rename from cscore/src/main/java/edu/wpi/cscore/VideoProperty.java rename to cscore/src/main/java/edu/wpi/first/cscore/VideoProperty.java index 53a24ad7fa..8179ba3c3d 100644 --- a/cscore/src/main/java/edu/wpi/cscore/VideoProperty.java +++ b/cscore/src/main/java/edu/wpi/first/cscore/VideoProperty.java @@ -2,7 +2,7 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.cscore; +package edu.wpi.first.cscore; /** A source or sink property. */ public class VideoProperty { diff --git a/cscore/src/main/java/edu/wpi/cscore/VideoSink.java b/cscore/src/main/java/edu/wpi/first/cscore/VideoSink.java similarity index 99% rename from cscore/src/main/java/edu/wpi/cscore/VideoSink.java rename to cscore/src/main/java/edu/wpi/first/cscore/VideoSink.java index 098f9cb4bf..ea65d0e54a 100644 --- a/cscore/src/main/java/edu/wpi/cscore/VideoSink.java +++ b/cscore/src/main/java/edu/wpi/first/cscore/VideoSink.java @@ -2,7 +2,7 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.cscore; +package edu.wpi.first.cscore; /** * A source for video that provides a sequence of frames. Each frame may consist of multiple images diff --git a/cscore/src/main/java/edu/wpi/cscore/VideoSource.java b/cscore/src/main/java/edu/wpi/first/cscore/VideoSource.java similarity index 99% rename from cscore/src/main/java/edu/wpi/cscore/VideoSource.java rename to cscore/src/main/java/edu/wpi/first/cscore/VideoSource.java index fc115c7924..047503a1e3 100644 --- a/cscore/src/main/java/edu/wpi/cscore/VideoSource.java +++ b/cscore/src/main/java/edu/wpi/first/cscore/VideoSource.java @@ -2,7 +2,7 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.cscore; +package edu.wpi.first.cscore; /** * A source for video that provides a sequence of frames. Each frame may consist of multiple images diff --git a/cscore/src/main/java/edu/wpi/cscore/raw/RawFrame.java b/cscore/src/main/java/edu/wpi/first/cscore/raw/RawFrame.java similarity index 97% rename from cscore/src/main/java/edu/wpi/cscore/raw/RawFrame.java rename to cscore/src/main/java/edu/wpi/first/cscore/raw/RawFrame.java index 18da73dc3a..1919d6b2dd 100644 --- a/cscore/src/main/java/edu/wpi/cscore/raw/RawFrame.java +++ b/cscore/src/main/java/edu/wpi/first/cscore/raw/RawFrame.java @@ -2,9 +2,9 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.cscore.raw; +package edu.wpi.first.cscore.raw; -import edu.wpi.cscore.CameraServerJNI; +import edu.wpi.first.cscore.CameraServerJNI; import java.nio.ByteBuffer; /** diff --git a/cscore/src/main/java/edu/wpi/cscore/raw/RawSink.java b/cscore/src/main/java/edu/wpi/first/cscore/raw/RawSink.java similarity index 94% rename from cscore/src/main/java/edu/wpi/cscore/raw/RawSink.java rename to cscore/src/main/java/edu/wpi/first/cscore/raw/RawSink.java index e9854136b7..64afae2b64 100644 --- a/cscore/src/main/java/edu/wpi/cscore/raw/RawSink.java +++ b/cscore/src/main/java/edu/wpi/first/cscore/raw/RawSink.java @@ -2,10 +2,10 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.cscore.raw; +package edu.wpi.first.cscore.raw; -import edu.wpi.cscore.CameraServerJNI; -import edu.wpi.cscore.ImageSink; +import edu.wpi.first.cscore.CameraServerJNI; +import edu.wpi.first.cscore.ImageSink; /** * A sink for user code to accept video frames as raw bytes. diff --git a/cscore/src/main/java/edu/wpi/cscore/raw/RawSource.java b/cscore/src/main/java/edu/wpi/first/cscore/raw/RawSource.java similarity index 93% rename from cscore/src/main/java/edu/wpi/cscore/raw/RawSource.java rename to cscore/src/main/java/edu/wpi/first/cscore/raw/RawSource.java index 89cb257670..f1be05065b 100644 --- a/cscore/src/main/java/edu/wpi/cscore/raw/RawSource.java +++ b/cscore/src/main/java/edu/wpi/first/cscore/raw/RawSource.java @@ -2,11 +2,11 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.cscore.raw; +package edu.wpi.first.cscore.raw; -import edu.wpi.cscore.CameraServerJNI; -import edu.wpi.cscore.ImageSource; -import edu.wpi.cscore.VideoMode; +import edu.wpi.first.cscore.CameraServerJNI; +import edu.wpi.first.cscore.ImageSource; +import edu.wpi.first.cscore.VideoMode; /** * A source for user code to provide video frames as raw bytes. diff --git a/cscore/src/main/native/cpp/jni/CameraServerJNI.cpp b/cscore/src/main/native/cpp/jni/CameraServerJNI.cpp index 62ab2b29da..a603fbf820 100644 --- a/cscore/src/main/native/cpp/jni/CameraServerJNI.cpp +++ b/cscore/src/main/native/cpp/jni/CameraServerJNI.cpp @@ -12,7 +12,7 @@ #include "cscore_cpp.h" #include "cscore_cv.h" #include "cscore_raw.h" -#include "edu_wpi_cscore_CameraServerJNI.h" +#include "edu_wpi_first_cscore_CameraServerJNI.h" namespace cv { class Mat; @@ -39,13 +39,13 @@ static JException exceptionEx; static JNIEnv* listenerEnv = nullptr; static const JClassInit classes[] = { - {"edu/wpi/cscore/UsbCameraInfo", &usbCameraInfoCls}, - {"edu/wpi/cscore/VideoMode", &videoModeCls}, - {"edu/wpi/cscore/VideoEvent", &videoEventCls}, - {"edu/wpi/cscore/raw/RawFrame", &rawFrameCls}}; + {"edu/wpi/first/cscore/UsbCameraInfo", &usbCameraInfoCls}, + {"edu/wpi/first/cscore/VideoMode", &videoModeCls}, + {"edu/wpi/first/cscore/VideoEvent", &videoEventCls}, + {"edu/wpi/first/cscore/raw/RawFrame", &rawFrameCls}}; static const JExceptionInit exceptions[] = { - {"edu/wpi/cscore/VideoException", &videoEx}, + {"edu/wpi/first/cscore/VideoException", &videoEx}, {"java/lang/InterruptedException", &interruptedEx}, {"java/lang/NullPointerException", &nullPointerEx}, {"java/lang/UnsupportedOperationException", &unsupportedEx}, @@ -308,12 +308,12 @@ static jobjectArray MakeJObject(JNIEnv* env, wpi::ArrayRef arr) { extern "C" { /* - * Class: edu_wpi_cscore_CameraServerJNI + * Class: edu_wpi_first_cscore_CameraServerJNI * Method: getPropertyKind * Signature: (I)I */ JNIEXPORT jint JNICALL -Java_edu_wpi_cscore_CameraServerJNI_getPropertyKind +Java_edu_wpi_first_cscore_CameraServerJNI_getPropertyKind (JNIEnv* env, jclass, jint property) { CS_Status status = 0; @@ -323,12 +323,12 @@ Java_edu_wpi_cscore_CameraServerJNI_getPropertyKind } /* - * Class: edu_wpi_cscore_CameraServerJNI + * Class: edu_wpi_first_cscore_CameraServerJNI * Method: getPropertyName * Signature: (I)Ljava/lang/String; */ JNIEXPORT jstring JNICALL -Java_edu_wpi_cscore_CameraServerJNI_getPropertyName +Java_edu_wpi_first_cscore_CameraServerJNI_getPropertyName (JNIEnv* env, jclass, jint property) { CS_Status status = 0; @@ -341,12 +341,12 @@ Java_edu_wpi_cscore_CameraServerJNI_getPropertyName } /* - * Class: edu_wpi_cscore_CameraServerJNI + * Class: edu_wpi_first_cscore_CameraServerJNI * Method: getProperty * Signature: (I)I */ JNIEXPORT jint JNICALL -Java_edu_wpi_cscore_CameraServerJNI_getProperty +Java_edu_wpi_first_cscore_CameraServerJNI_getProperty (JNIEnv* env, jclass, jint property) { CS_Status status = 0; @@ -356,12 +356,12 @@ Java_edu_wpi_cscore_CameraServerJNI_getProperty } /* - * Class: edu_wpi_cscore_CameraServerJNI + * Class: edu_wpi_first_cscore_CameraServerJNI * Method: setProperty * Signature: (II)V */ JNIEXPORT void JNICALL -Java_edu_wpi_cscore_CameraServerJNI_setProperty +Java_edu_wpi_first_cscore_CameraServerJNI_setProperty (JNIEnv* env, jclass, jint property, jint value) { CS_Status status = 0; @@ -370,12 +370,12 @@ Java_edu_wpi_cscore_CameraServerJNI_setProperty } /* - * Class: edu_wpi_cscore_CameraServerJNI + * Class: edu_wpi_first_cscore_CameraServerJNI * Method: getPropertyMin * Signature: (I)I */ JNIEXPORT jint JNICALL -Java_edu_wpi_cscore_CameraServerJNI_getPropertyMin +Java_edu_wpi_first_cscore_CameraServerJNI_getPropertyMin (JNIEnv* env, jclass, jint property) { CS_Status status = 0; @@ -385,12 +385,12 @@ Java_edu_wpi_cscore_CameraServerJNI_getPropertyMin } /* - * Class: edu_wpi_cscore_CameraServerJNI + * Class: edu_wpi_first_cscore_CameraServerJNI * Method: getPropertyMax * Signature: (I)I */ JNIEXPORT jint JNICALL -Java_edu_wpi_cscore_CameraServerJNI_getPropertyMax +Java_edu_wpi_first_cscore_CameraServerJNI_getPropertyMax (JNIEnv* env, jclass, jint property) { CS_Status status = 0; @@ -400,12 +400,12 @@ Java_edu_wpi_cscore_CameraServerJNI_getPropertyMax } /* - * Class: edu_wpi_cscore_CameraServerJNI + * Class: edu_wpi_first_cscore_CameraServerJNI * Method: getPropertyStep * Signature: (I)I */ JNIEXPORT jint JNICALL -Java_edu_wpi_cscore_CameraServerJNI_getPropertyStep +Java_edu_wpi_first_cscore_CameraServerJNI_getPropertyStep (JNIEnv* env, jclass, jint property) { CS_Status status = 0; @@ -415,12 +415,12 @@ Java_edu_wpi_cscore_CameraServerJNI_getPropertyStep } /* - * Class: edu_wpi_cscore_CameraServerJNI + * Class: edu_wpi_first_cscore_CameraServerJNI * Method: getPropertyDefault * Signature: (I)I */ JNIEXPORT jint JNICALL -Java_edu_wpi_cscore_CameraServerJNI_getPropertyDefault +Java_edu_wpi_first_cscore_CameraServerJNI_getPropertyDefault (JNIEnv* env, jclass, jint property) { CS_Status status = 0; @@ -430,12 +430,12 @@ Java_edu_wpi_cscore_CameraServerJNI_getPropertyDefault } /* - * Class: edu_wpi_cscore_CameraServerJNI + * Class: edu_wpi_first_cscore_CameraServerJNI * Method: getStringProperty * Signature: (I)Ljava/lang/String; */ JNIEXPORT jstring JNICALL -Java_edu_wpi_cscore_CameraServerJNI_getStringProperty +Java_edu_wpi_first_cscore_CameraServerJNI_getStringProperty (JNIEnv* env, jclass, jint property) { CS_Status status = 0; @@ -448,12 +448,12 @@ Java_edu_wpi_cscore_CameraServerJNI_getStringProperty } /* - * Class: edu_wpi_cscore_CameraServerJNI + * Class: edu_wpi_first_cscore_CameraServerJNI * Method: setStringProperty * Signature: (ILjava/lang/String;)V */ JNIEXPORT void JNICALL -Java_edu_wpi_cscore_CameraServerJNI_setStringProperty +Java_edu_wpi_first_cscore_CameraServerJNI_setStringProperty (JNIEnv* env, jclass, jint property, jstring value) { if (!value) { @@ -466,12 +466,12 @@ Java_edu_wpi_cscore_CameraServerJNI_setStringProperty } /* - * Class: edu_wpi_cscore_CameraServerJNI + * Class: edu_wpi_first_cscore_CameraServerJNI * Method: getEnumPropertyChoices * Signature: (I)[Ljava/lang/Object; */ JNIEXPORT jobjectArray JNICALL -Java_edu_wpi_cscore_CameraServerJNI_getEnumPropertyChoices +Java_edu_wpi_first_cscore_CameraServerJNI_getEnumPropertyChoices (JNIEnv* env, jclass, jint property) { CS_Status status = 0; @@ -483,12 +483,12 @@ Java_edu_wpi_cscore_CameraServerJNI_getEnumPropertyChoices } /* - * Class: edu_wpi_cscore_CameraServerJNI + * Class: edu_wpi_first_cscore_CameraServerJNI * Method: createUsbCameraDev * Signature: (Ljava/lang/String;I)I */ JNIEXPORT jint JNICALL -Java_edu_wpi_cscore_CameraServerJNI_createUsbCameraDev +Java_edu_wpi_first_cscore_CameraServerJNI_createUsbCameraDev (JNIEnv* env, jclass, jstring name, jint dev) { if (!name) { @@ -502,12 +502,12 @@ Java_edu_wpi_cscore_CameraServerJNI_createUsbCameraDev } /* - * Class: edu_wpi_cscore_CameraServerJNI + * Class: edu_wpi_first_cscore_CameraServerJNI * Method: createUsbCameraPath * Signature: (Ljava/lang/String;Ljava/lang/String;)I */ JNIEXPORT jint JNICALL -Java_edu_wpi_cscore_CameraServerJNI_createUsbCameraPath +Java_edu_wpi_first_cscore_CameraServerJNI_createUsbCameraPath (JNIEnv* env, jclass, jstring name, jstring path) { if (!name) { @@ -526,12 +526,12 @@ Java_edu_wpi_cscore_CameraServerJNI_createUsbCameraPath } /* - * Class: edu_wpi_cscore_CameraServerJNI + * Class: edu_wpi_first_cscore_CameraServerJNI * Method: createHttpCamera * Signature: (Ljava/lang/String;Ljava/lang/String;I)I */ JNIEXPORT jint JNICALL -Java_edu_wpi_cscore_CameraServerJNI_createHttpCamera +Java_edu_wpi_first_cscore_CameraServerJNI_createHttpCamera (JNIEnv* env, jclass, jstring name, jstring url, jint kind) { if (!name) { @@ -551,12 +551,12 @@ Java_edu_wpi_cscore_CameraServerJNI_createHttpCamera } /* - * Class: edu_wpi_cscore_CameraServerJNI + * Class: edu_wpi_first_cscore_CameraServerJNI * Method: createHttpCameraMulti * Signature: (Ljava/lang/String;[Ljava/lang/Object;I)I */ JNIEXPORT jint JNICALL -Java_edu_wpi_cscore_CameraServerJNI_createHttpCameraMulti +Java_edu_wpi_first_cscore_CameraServerJNI_createHttpCameraMulti (JNIEnv* env, jclass, jstring name, jobjectArray urls, jint kind) { if (!name) { @@ -588,12 +588,12 @@ Java_edu_wpi_cscore_CameraServerJNI_createHttpCameraMulti } /* - * Class: edu_wpi_cscore_CameraServerCvJNI + * Class: edu_wpi_first_cscore_CameraServerCvJNI * Method: createCvSource * Signature: (Ljava/lang/String;IIII)I */ JNIEXPORT jint JNICALL -Java_edu_wpi_cscore_CameraServerCvJNI_createCvSource +Java_edu_wpi_first_cscore_CameraServerCvJNI_createCvSource (JNIEnv* env, jclass, jstring name, jint pixelFormat, jint width, jint height, jint fps) { @@ -613,12 +613,12 @@ Java_edu_wpi_cscore_CameraServerCvJNI_createCvSource } /* - * Class: edu_wpi_cscore_CameraServerJNI + * Class: edu_wpi_first_cscore_CameraServerJNI * Method: createRawSource * Signature: (Ljava/lang/String;IIII)I */ JNIEXPORT jint JNICALL -Java_edu_wpi_cscore_CameraServerJNI_createRawSource +Java_edu_wpi_first_cscore_CameraServerJNI_createRawSource (JNIEnv* env, jclass, jstring name, jint pixelFormat, jint width, jint height, jint fps) { @@ -638,12 +638,12 @@ Java_edu_wpi_cscore_CameraServerJNI_createRawSource } /* - * Class: edu_wpi_cscore_CameraServerJNI + * Class: edu_wpi_first_cscore_CameraServerJNI * Method: getSourceKind * Signature: (I)I */ JNIEXPORT jint JNICALL -Java_edu_wpi_cscore_CameraServerJNI_getSourceKind +Java_edu_wpi_first_cscore_CameraServerJNI_getSourceKind (JNIEnv* env, jclass, jint source) { CS_Status status = 0; @@ -653,12 +653,12 @@ Java_edu_wpi_cscore_CameraServerJNI_getSourceKind } /* - * Class: edu_wpi_cscore_CameraServerJNI + * Class: edu_wpi_first_cscore_CameraServerJNI * Method: getSourceName * Signature: (I)Ljava/lang/String; */ JNIEXPORT jstring JNICALL -Java_edu_wpi_cscore_CameraServerJNI_getSourceName +Java_edu_wpi_first_cscore_CameraServerJNI_getSourceName (JNIEnv* env, jclass, jint source) { CS_Status status = 0; @@ -671,12 +671,12 @@ Java_edu_wpi_cscore_CameraServerJNI_getSourceName } /* - * Class: edu_wpi_cscore_CameraServerJNI + * Class: edu_wpi_first_cscore_CameraServerJNI * Method: getSourceDescription * Signature: (I)Ljava/lang/String; */ JNIEXPORT jstring JNICALL -Java_edu_wpi_cscore_CameraServerJNI_getSourceDescription +Java_edu_wpi_first_cscore_CameraServerJNI_getSourceDescription (JNIEnv* env, jclass, jint source) { CS_Status status = 0; @@ -689,12 +689,12 @@ Java_edu_wpi_cscore_CameraServerJNI_getSourceDescription } /* - * Class: edu_wpi_cscore_CameraServerJNI + * Class: edu_wpi_first_cscore_CameraServerJNI * Method: getSourceLastFrameTime * Signature: (I)J */ JNIEXPORT jlong JNICALL -Java_edu_wpi_cscore_CameraServerJNI_getSourceLastFrameTime +Java_edu_wpi_first_cscore_CameraServerJNI_getSourceLastFrameTime (JNIEnv* env, jclass, jint source) { CS_Status status = 0; @@ -704,12 +704,12 @@ Java_edu_wpi_cscore_CameraServerJNI_getSourceLastFrameTime } /* - * Class: edu_wpi_cscore_CameraServerJNI + * Class: edu_wpi_first_cscore_CameraServerJNI * Method: setSourceConnectionStrategy * Signature: (II)V */ JNIEXPORT void JNICALL -Java_edu_wpi_cscore_CameraServerJNI_setSourceConnectionStrategy +Java_edu_wpi_first_cscore_CameraServerJNI_setSourceConnectionStrategy (JNIEnv* env, jclass, jint source, jint strategy) { CS_Status status = 0; @@ -719,12 +719,12 @@ Java_edu_wpi_cscore_CameraServerJNI_setSourceConnectionStrategy } /* - * Class: edu_wpi_cscore_CameraServerJNI + * Class: edu_wpi_first_cscore_CameraServerJNI * Method: isSourceConnected * Signature: (I)Z */ JNIEXPORT jboolean JNICALL -Java_edu_wpi_cscore_CameraServerJNI_isSourceConnected +Java_edu_wpi_first_cscore_CameraServerJNI_isSourceConnected (JNIEnv* env, jclass, jint source) { CS_Status status = 0; @@ -734,12 +734,12 @@ Java_edu_wpi_cscore_CameraServerJNI_isSourceConnected } /* - * Class: edu_wpi_cscore_CameraServerJNI + * Class: edu_wpi_first_cscore_CameraServerJNI * Method: isSourceEnabled * Signature: (I)Z */ JNIEXPORT jboolean JNICALL -Java_edu_wpi_cscore_CameraServerJNI_isSourceEnabled +Java_edu_wpi_first_cscore_CameraServerJNI_isSourceEnabled (JNIEnv* env, jclass, jint source) { CS_Status status = 0; @@ -749,12 +749,12 @@ Java_edu_wpi_cscore_CameraServerJNI_isSourceEnabled } /* - * Class: edu_wpi_cscore_CameraServerJNI + * Class: edu_wpi_first_cscore_CameraServerJNI * Method: getSourceProperty * Signature: (ILjava/lang/String;)I */ JNIEXPORT jint JNICALL -Java_edu_wpi_cscore_CameraServerJNI_getSourceProperty +Java_edu_wpi_first_cscore_CameraServerJNI_getSourceProperty (JNIEnv* env, jclass, jint source, jstring name) { if (!name) { @@ -769,12 +769,12 @@ Java_edu_wpi_cscore_CameraServerJNI_getSourceProperty } /* - * Class: edu_wpi_cscore_CameraServerJNI + * Class: edu_wpi_first_cscore_CameraServerJNI * Method: enumerateSourceProperties * Signature: (I)[I */ JNIEXPORT jintArray JNICALL -Java_edu_wpi_cscore_CameraServerJNI_enumerateSourceProperties +Java_edu_wpi_first_cscore_CameraServerJNI_enumerateSourceProperties (JNIEnv* env, jclass, jint source) { CS_Status status = 0; @@ -787,12 +787,12 @@ Java_edu_wpi_cscore_CameraServerJNI_enumerateSourceProperties } /* - * Class: edu_wpi_cscore_CameraServerJNI + * Class: edu_wpi_first_cscore_CameraServerJNI * Method: getSourceVideoMode * Signature: (I)Ljava/lang/Object; */ JNIEXPORT jobject JNICALL -Java_edu_wpi_cscore_CameraServerJNI_getSourceVideoMode +Java_edu_wpi_first_cscore_CameraServerJNI_getSourceVideoMode (JNIEnv* env, jclass, jint source) { CS_Status status = 0; @@ -804,12 +804,12 @@ Java_edu_wpi_cscore_CameraServerJNI_getSourceVideoMode } /* - * Class: edu_wpi_cscore_CameraServerJNI + * Class: edu_wpi_first_cscore_CameraServerJNI * Method: setSourceVideoMode * Signature: (IIIII)Z */ JNIEXPORT jboolean JNICALL -Java_edu_wpi_cscore_CameraServerJNI_setSourceVideoMode +Java_edu_wpi_first_cscore_CameraServerJNI_setSourceVideoMode (JNIEnv* env, jclass, jint source, jint pixelFormat, jint width, jint height, jint fps) { @@ -824,12 +824,12 @@ Java_edu_wpi_cscore_CameraServerJNI_setSourceVideoMode } /* - * Class: edu_wpi_cscore_CameraServerJNI + * Class: edu_wpi_first_cscore_CameraServerJNI * Method: setSourcePixelFormat * Signature: (II)Z */ JNIEXPORT jboolean JNICALL -Java_edu_wpi_cscore_CameraServerJNI_setSourcePixelFormat +Java_edu_wpi_first_cscore_CameraServerJNI_setSourcePixelFormat (JNIEnv* env, jclass, jint source, jint pixelFormat) { CS_Status status = 0; @@ -840,12 +840,12 @@ Java_edu_wpi_cscore_CameraServerJNI_setSourcePixelFormat } /* - * Class: edu_wpi_cscore_CameraServerJNI + * Class: edu_wpi_first_cscore_CameraServerJNI * Method: setSourceResolution * Signature: (III)Z */ JNIEXPORT jboolean JNICALL -Java_edu_wpi_cscore_CameraServerJNI_setSourceResolution +Java_edu_wpi_first_cscore_CameraServerJNI_setSourceResolution (JNIEnv* env, jclass, jint source, jint width, jint height) { CS_Status status = 0; @@ -855,12 +855,12 @@ Java_edu_wpi_cscore_CameraServerJNI_setSourceResolution } /* - * Class: edu_wpi_cscore_CameraServerJNI + * Class: edu_wpi_first_cscore_CameraServerJNI * Method: setSourceFPS * Signature: (II)Z */ JNIEXPORT jboolean JNICALL -Java_edu_wpi_cscore_CameraServerJNI_setSourceFPS +Java_edu_wpi_first_cscore_CameraServerJNI_setSourceFPS (JNIEnv* env, jclass, jint source, jint fps) { CS_Status status = 0; @@ -870,12 +870,12 @@ Java_edu_wpi_cscore_CameraServerJNI_setSourceFPS } /* - * Class: edu_wpi_cscore_CameraServerJNI + * Class: edu_wpi_first_cscore_CameraServerJNI * Method: setSourceConfigJson * Signature: (ILjava/lang/String;)Z */ JNIEXPORT jboolean JNICALL -Java_edu_wpi_cscore_CameraServerJNI_setSourceConfigJson +Java_edu_wpi_first_cscore_CameraServerJNI_setSourceConfigJson (JNIEnv* env, jclass, jint source, jstring config) { CS_Status status = 0; @@ -885,12 +885,12 @@ Java_edu_wpi_cscore_CameraServerJNI_setSourceConfigJson } /* - * Class: edu_wpi_cscore_CameraServerJNI + * Class: edu_wpi_first_cscore_CameraServerJNI * Method: getSourceConfigJson * Signature: (I)Ljava/lang/String; */ JNIEXPORT jstring JNICALL -Java_edu_wpi_cscore_CameraServerJNI_getSourceConfigJson +Java_edu_wpi_first_cscore_CameraServerJNI_getSourceConfigJson (JNIEnv* env, jclass, jint source) { CS_Status status = 0; @@ -900,12 +900,12 @@ Java_edu_wpi_cscore_CameraServerJNI_getSourceConfigJson } /* - * Class: edu_wpi_cscore_CameraServerJNI + * Class: edu_wpi_first_cscore_CameraServerJNI * Method: enumerateSourceVideoModes * Signature: (I)[Ljava/lang/Object; */ JNIEXPORT jobjectArray JNICALL -Java_edu_wpi_cscore_CameraServerJNI_enumerateSourceVideoModes +Java_edu_wpi_first_cscore_CameraServerJNI_enumerateSourceVideoModes (JNIEnv* env, jclass, jint source) { CS_Status status = 0; @@ -925,12 +925,12 @@ Java_edu_wpi_cscore_CameraServerJNI_enumerateSourceVideoModes } /* - * Class: edu_wpi_cscore_CameraServerJNI + * Class: edu_wpi_first_cscore_CameraServerJNI * Method: enumerateSourceSinks * Signature: (I)[I */ JNIEXPORT jintArray JNICALL -Java_edu_wpi_cscore_CameraServerJNI_enumerateSourceSinks +Java_edu_wpi_first_cscore_CameraServerJNI_enumerateSourceSinks (JNIEnv* env, jclass, jint source) { CS_Status status = 0; @@ -943,12 +943,12 @@ Java_edu_wpi_cscore_CameraServerJNI_enumerateSourceSinks } /* - * Class: edu_wpi_cscore_CameraServerJNI + * Class: edu_wpi_first_cscore_CameraServerJNI * Method: copySource * Signature: (I)I */ JNIEXPORT jint JNICALL -Java_edu_wpi_cscore_CameraServerJNI_copySource +Java_edu_wpi_first_cscore_CameraServerJNI_copySource (JNIEnv* env, jclass, jint source) { CS_Status status = 0; @@ -958,12 +958,12 @@ Java_edu_wpi_cscore_CameraServerJNI_copySource } /* - * Class: edu_wpi_cscore_CameraServerJNI + * Class: edu_wpi_first_cscore_CameraServerJNI * Method: releaseSource * Signature: (I)V */ JNIEXPORT void JNICALL -Java_edu_wpi_cscore_CameraServerJNI_releaseSource +Java_edu_wpi_first_cscore_CameraServerJNI_releaseSource (JNIEnv* env, jclass, jint source) { CS_Status status = 0; @@ -972,12 +972,12 @@ Java_edu_wpi_cscore_CameraServerJNI_releaseSource } /* - * Class: edu_wpi_cscore_CameraServerJNI + * Class: edu_wpi_first_cscore_CameraServerJNI * Method: setCameraBrightness * Signature: (II)V */ JNIEXPORT void JNICALL -Java_edu_wpi_cscore_CameraServerJNI_setCameraBrightness +Java_edu_wpi_first_cscore_CameraServerJNI_setCameraBrightness (JNIEnv* env, jclass, jint source, jint brightness) { CS_Status status = 0; @@ -986,12 +986,12 @@ Java_edu_wpi_cscore_CameraServerJNI_setCameraBrightness } /* - * Class: edu_wpi_cscore_CameraServerJNI + * Class: edu_wpi_first_cscore_CameraServerJNI * Method: getCameraBrightness * Signature: (I)I */ JNIEXPORT jint JNICALL -Java_edu_wpi_cscore_CameraServerJNI_getCameraBrightness +Java_edu_wpi_first_cscore_CameraServerJNI_getCameraBrightness (JNIEnv* env, jclass, jint source) { CS_Status status = 0; @@ -1001,12 +1001,12 @@ Java_edu_wpi_cscore_CameraServerJNI_getCameraBrightness } /* - * Class: edu_wpi_cscore_CameraServerJNI + * Class: edu_wpi_first_cscore_CameraServerJNI * Method: setCameraWhiteBalanceAuto * Signature: (I)V */ JNIEXPORT void JNICALL -Java_edu_wpi_cscore_CameraServerJNI_setCameraWhiteBalanceAuto +Java_edu_wpi_first_cscore_CameraServerJNI_setCameraWhiteBalanceAuto (JNIEnv* env, jclass, jint source) { CS_Status status = 0; @@ -1015,12 +1015,12 @@ Java_edu_wpi_cscore_CameraServerJNI_setCameraWhiteBalanceAuto } /* - * Class: edu_wpi_cscore_CameraServerJNI + * Class: edu_wpi_first_cscore_CameraServerJNI * Method: setCameraWhiteBalanceHoldCurrent * Signature: (I)V */ JNIEXPORT void JNICALL -Java_edu_wpi_cscore_CameraServerJNI_setCameraWhiteBalanceHoldCurrent +Java_edu_wpi_first_cscore_CameraServerJNI_setCameraWhiteBalanceHoldCurrent (JNIEnv* env, jclass, jint source) { CS_Status status = 0; @@ -1029,12 +1029,12 @@ Java_edu_wpi_cscore_CameraServerJNI_setCameraWhiteBalanceHoldCurrent } /* - * Class: edu_wpi_cscore_CameraServerJNI + * Class: edu_wpi_first_cscore_CameraServerJNI * Method: setCameraWhiteBalanceManual * Signature: (II)V */ JNIEXPORT void JNICALL -Java_edu_wpi_cscore_CameraServerJNI_setCameraWhiteBalanceManual +Java_edu_wpi_first_cscore_CameraServerJNI_setCameraWhiteBalanceManual (JNIEnv* env, jclass, jint source, jint value) { CS_Status status = 0; @@ -1043,12 +1043,12 @@ Java_edu_wpi_cscore_CameraServerJNI_setCameraWhiteBalanceManual } /* - * Class: edu_wpi_cscore_CameraServerJNI + * Class: edu_wpi_first_cscore_CameraServerJNI * Method: setCameraExposureAuto * Signature: (I)V */ JNIEXPORT void JNICALL -Java_edu_wpi_cscore_CameraServerJNI_setCameraExposureAuto +Java_edu_wpi_first_cscore_CameraServerJNI_setCameraExposureAuto (JNIEnv* env, jclass, jint source) { CS_Status status = 0; @@ -1057,12 +1057,12 @@ Java_edu_wpi_cscore_CameraServerJNI_setCameraExposureAuto } /* - * Class: edu_wpi_cscore_CameraServerJNI + * Class: edu_wpi_first_cscore_CameraServerJNI * Method: setCameraExposureHoldCurrent * Signature: (I)V */ JNIEXPORT void JNICALL -Java_edu_wpi_cscore_CameraServerJNI_setCameraExposureHoldCurrent +Java_edu_wpi_first_cscore_CameraServerJNI_setCameraExposureHoldCurrent (JNIEnv* env, jclass, jint source) { CS_Status status = 0; @@ -1071,12 +1071,12 @@ Java_edu_wpi_cscore_CameraServerJNI_setCameraExposureHoldCurrent } /* - * Class: edu_wpi_cscore_CameraServerJNI + * Class: edu_wpi_first_cscore_CameraServerJNI * Method: setCameraExposureManual * Signature: (II)V */ JNIEXPORT void JNICALL -Java_edu_wpi_cscore_CameraServerJNI_setCameraExposureManual +Java_edu_wpi_first_cscore_CameraServerJNI_setCameraExposureManual (JNIEnv* env, jclass, jint source, jint value) { CS_Status status = 0; @@ -1085,12 +1085,12 @@ Java_edu_wpi_cscore_CameraServerJNI_setCameraExposureManual } /* - * Class: edu_wpi_cscore_CameraServerJNI + * Class: edu_wpi_first_cscore_CameraServerJNI * Method: setUsbCameraPath * Signature: (ILjava/lang/String;)V */ JNIEXPORT void JNICALL -Java_edu_wpi_cscore_CameraServerJNI_setUsbCameraPath +Java_edu_wpi_first_cscore_CameraServerJNI_setUsbCameraPath (JNIEnv* env, jclass, jint source, jstring path) { CS_Status status = 0; @@ -1099,12 +1099,12 @@ Java_edu_wpi_cscore_CameraServerJNI_setUsbCameraPath } /* - * Class: edu_wpi_cscore_CameraServerJNI + * Class: edu_wpi_first_cscore_CameraServerJNI * Method: getUsbCameraPath * Signature: (I)Ljava/lang/String; */ JNIEXPORT jstring JNICALL -Java_edu_wpi_cscore_CameraServerJNI_getUsbCameraPath +Java_edu_wpi_first_cscore_CameraServerJNI_getUsbCameraPath (JNIEnv* env, jclass, jint source) { CS_Status status = 0; @@ -1116,12 +1116,12 @@ Java_edu_wpi_cscore_CameraServerJNI_getUsbCameraPath } /* - * Class: edu_wpi_cscore_CameraServerJNI + * Class: edu_wpi_first_cscore_CameraServerJNI * Method: getUsbCameraInfo * Signature: (I)Ljava/lang/Object; */ JNIEXPORT jobject JNICALL -Java_edu_wpi_cscore_CameraServerJNI_getUsbCameraInfo +Java_edu_wpi_first_cscore_CameraServerJNI_getUsbCameraInfo (JNIEnv* env, jclass, jint source) { CS_Status status = 0; @@ -1133,12 +1133,12 @@ Java_edu_wpi_cscore_CameraServerJNI_getUsbCameraInfo } /* - * Class: edu_wpi_cscore_CameraServerJNI + * Class: edu_wpi_first_cscore_CameraServerJNI * Method: getHttpCameraKind * Signature: (I)I */ JNIEXPORT jint JNICALL -Java_edu_wpi_cscore_CameraServerJNI_getHttpCameraKind +Java_edu_wpi_first_cscore_CameraServerJNI_getHttpCameraKind (JNIEnv* env, jclass, jint source) { CS_Status status = 0; @@ -1150,12 +1150,12 @@ Java_edu_wpi_cscore_CameraServerJNI_getHttpCameraKind } /* - * Class: edu_wpi_cscore_CameraServerJNI + * Class: edu_wpi_first_cscore_CameraServerJNI * Method: setHttpCameraUrls * Signature: (I[Ljava/lang/Object;)V */ JNIEXPORT void JNICALL -Java_edu_wpi_cscore_CameraServerJNI_setHttpCameraUrls +Java_edu_wpi_first_cscore_CameraServerJNI_setHttpCameraUrls (JNIEnv* env, jclass, jint source, jobjectArray urls) { if (!urls) { @@ -1180,12 +1180,12 @@ Java_edu_wpi_cscore_CameraServerJNI_setHttpCameraUrls } /* - * Class: edu_wpi_cscore_CameraServerJNI + * Class: edu_wpi_first_cscore_CameraServerJNI * Method: getHttpCameraUrls * Signature: (I)[Ljava/lang/Object; */ JNIEXPORT jobjectArray JNICALL -Java_edu_wpi_cscore_CameraServerJNI_getHttpCameraUrls +Java_edu_wpi_first_cscore_CameraServerJNI_getHttpCameraUrls (JNIEnv* env, jclass, jint source) { CS_Status status = 0; @@ -1197,12 +1197,12 @@ Java_edu_wpi_cscore_CameraServerJNI_getHttpCameraUrls } /* - * Class: edu_wpi_cscore_CameraServerCvJNI + * Class: edu_wpi_first_cscore_CameraServerCvJNI * Method: putSourceFrame * Signature: (IJ)V */ JNIEXPORT void JNICALL -Java_edu_wpi_cscore_CameraServerCvJNI_putSourceFrame +Java_edu_wpi_first_cscore_CameraServerCvJNI_putSourceFrame (JNIEnv* env, jclass, jint source, jlong imageNativeObj) { try { @@ -1220,12 +1220,12 @@ Java_edu_wpi_cscore_CameraServerCvJNI_putSourceFrame // int width, int height, int pixelFormat, int totalData /* - * Class: edu_wpi_cscore_CameraServerJNI + * Class: edu_wpi_first_cscore_CameraServerJNI * Method: putRawSourceFrameBB * Signature: (ILjava/lang/Object;IIII)V */ JNIEXPORT void JNICALL -Java_edu_wpi_cscore_CameraServerJNI_putRawSourceFrameBB +Java_edu_wpi_first_cscore_CameraServerJNI_putRawSourceFrameBB (JNIEnv* env, jclass, jint source, jobject byteBuffer, jint width, jint height, jint pixelFormat, jint totalData) { @@ -1242,12 +1242,12 @@ Java_edu_wpi_cscore_CameraServerJNI_putRawSourceFrameBB } /* - * Class: edu_wpi_cscore_CameraServerJNI + * Class: edu_wpi_first_cscore_CameraServerJNI * Method: putRawSourceFrame * Signature: (IJIIII)V */ JNIEXPORT void JNICALL -Java_edu_wpi_cscore_CameraServerJNI_putRawSourceFrame +Java_edu_wpi_first_cscore_CameraServerJNI_putRawSourceFrame (JNIEnv* env, jclass, jint source, jlong ptr, jint width, jint height, jint pixelFormat, jint totalData) { @@ -1263,12 +1263,12 @@ Java_edu_wpi_cscore_CameraServerJNI_putRawSourceFrame } /* - * Class: edu_wpi_cscore_CameraServerJNI + * Class: edu_wpi_first_cscore_CameraServerJNI * Method: notifySourceError * Signature: (ILjava/lang/String;)V */ JNIEXPORT void JNICALL -Java_edu_wpi_cscore_CameraServerJNI_notifySourceError +Java_edu_wpi_first_cscore_CameraServerJNI_notifySourceError (JNIEnv* env, jclass, jint source, jstring msg) { if (!msg) { @@ -1281,12 +1281,12 @@ Java_edu_wpi_cscore_CameraServerJNI_notifySourceError } /* - * Class: edu_wpi_cscore_CameraServerJNI + * Class: edu_wpi_first_cscore_CameraServerJNI * Method: setSourceConnected * Signature: (IZ)V */ JNIEXPORT void JNICALL -Java_edu_wpi_cscore_CameraServerJNI_setSourceConnected +Java_edu_wpi_first_cscore_CameraServerJNI_setSourceConnected (JNIEnv* env, jclass, jint source, jboolean connected) { CS_Status status = 0; @@ -1295,12 +1295,12 @@ Java_edu_wpi_cscore_CameraServerJNI_setSourceConnected } /* - * Class: edu_wpi_cscore_CameraServerJNI + * Class: edu_wpi_first_cscore_CameraServerJNI * Method: setSourceDescription * Signature: (ILjava/lang/String;)V */ JNIEXPORT void JNICALL -Java_edu_wpi_cscore_CameraServerJNI_setSourceDescription +Java_edu_wpi_first_cscore_CameraServerJNI_setSourceDescription (JNIEnv* env, jclass, jint source, jstring description) { if (!description) { @@ -1313,12 +1313,12 @@ Java_edu_wpi_cscore_CameraServerJNI_setSourceDescription } /* - * Class: edu_wpi_cscore_CameraServerJNI + * Class: edu_wpi_first_cscore_CameraServerJNI * Method: createSourceProperty * Signature: (ILjava/lang/String;IIIIII)I */ JNIEXPORT jint JNICALL -Java_edu_wpi_cscore_CameraServerJNI_createSourceProperty +Java_edu_wpi_first_cscore_CameraServerJNI_createSourceProperty (JNIEnv* env, jclass, jint source, jstring name, jint kind, jint minimum, jint maximum, jint step, jint defaultValue, jint value) { @@ -1331,12 +1331,12 @@ Java_edu_wpi_cscore_CameraServerJNI_createSourceProperty } /* - * Class: edu_wpi_cscore_CameraServerJNI + * Class: edu_wpi_first_cscore_CameraServerJNI * Method: setSourceEnumPropertyChoices * Signature: (II[Ljava/lang/Object;)V */ JNIEXPORT void JNICALL -Java_edu_wpi_cscore_CameraServerJNI_setSourceEnumPropertyChoices +Java_edu_wpi_first_cscore_CameraServerJNI_setSourceEnumPropertyChoices (JNIEnv* env, jclass, jint source, jint property, jobjectArray choices) { if (!choices) { @@ -1361,12 +1361,12 @@ Java_edu_wpi_cscore_CameraServerJNI_setSourceEnumPropertyChoices } /* - * Class: edu_wpi_cscore_CameraServerJNI + * Class: edu_wpi_first_cscore_CameraServerJNI * Method: createMjpegServer * Signature: (Ljava/lang/String;Ljava/lang/String;I)I */ JNIEXPORT jint JNICALL -Java_edu_wpi_cscore_CameraServerJNI_createMjpegServer +Java_edu_wpi_first_cscore_CameraServerJNI_createMjpegServer (JNIEnv* env, jclass, jstring name, jstring listenAddress, jint port) { if (!name) { @@ -1386,12 +1386,12 @@ Java_edu_wpi_cscore_CameraServerJNI_createMjpegServer } /* - * Class: edu_wpi_cscore_CameraServerCvJNI + * Class: edu_wpi_first_cscore_CameraServerCvJNI * Method: createCvSink * Signature: (Ljava/lang/String;)I */ JNIEXPORT jint JNICALL -Java_edu_wpi_cscore_CameraServerCvJNI_createCvSink +Java_edu_wpi_first_cscore_CameraServerCvJNI_createCvSink (JNIEnv* env, jclass, jstring name) { if (!name) { @@ -1405,12 +1405,12 @@ Java_edu_wpi_cscore_CameraServerCvJNI_createCvSink } /* - * Class: edu_wpi_cscore_CameraServerJNI + * Class: edu_wpi_first_cscore_CameraServerJNI * Method: createRawSink * Signature: (Ljava/lang/String;)I */ JNIEXPORT jint JNICALL -Java_edu_wpi_cscore_CameraServerJNI_createRawSink +Java_edu_wpi_first_cscore_CameraServerJNI_createRawSink (JNIEnv* env, jclass, jstring name) { if (!name) { @@ -1424,12 +1424,12 @@ Java_edu_wpi_cscore_CameraServerJNI_createRawSink } /* - * Class: edu_wpi_cscore_CameraServerJNI + * Class: edu_wpi_first_cscore_CameraServerJNI * Method: getSinkKind * Signature: (I)I */ JNIEXPORT jint JNICALL -Java_edu_wpi_cscore_CameraServerJNI_getSinkKind +Java_edu_wpi_first_cscore_CameraServerJNI_getSinkKind (JNIEnv* env, jclass, jint sink) { CS_Status status = 0; @@ -1439,12 +1439,12 @@ Java_edu_wpi_cscore_CameraServerJNI_getSinkKind } /* - * Class: edu_wpi_cscore_CameraServerJNI + * Class: edu_wpi_first_cscore_CameraServerJNI * Method: getSinkName * Signature: (I)Ljava/lang/String; */ JNIEXPORT jstring JNICALL -Java_edu_wpi_cscore_CameraServerJNI_getSinkName +Java_edu_wpi_first_cscore_CameraServerJNI_getSinkName (JNIEnv* env, jclass, jint sink) { CS_Status status = 0; @@ -1457,12 +1457,12 @@ Java_edu_wpi_cscore_CameraServerJNI_getSinkName } /* - * Class: edu_wpi_cscore_CameraServerJNI + * Class: edu_wpi_first_cscore_CameraServerJNI * Method: getSinkDescription * Signature: (I)Ljava/lang/String; */ JNIEXPORT jstring JNICALL -Java_edu_wpi_cscore_CameraServerJNI_getSinkDescription +Java_edu_wpi_first_cscore_CameraServerJNI_getSinkDescription (JNIEnv* env, jclass, jint sink) { CS_Status status = 0; @@ -1475,12 +1475,12 @@ Java_edu_wpi_cscore_CameraServerJNI_getSinkDescription } /* - * Class: edu_wpi_cscore_CameraServerJNI + * Class: edu_wpi_first_cscore_CameraServerJNI * Method: getSinkProperty * Signature: (ILjava/lang/String;)I */ JNIEXPORT jint JNICALL -Java_edu_wpi_cscore_CameraServerJNI_getSinkProperty +Java_edu_wpi_first_cscore_CameraServerJNI_getSinkProperty (JNIEnv* env, jclass, jint sink, jstring name) { if (!name) { @@ -1494,12 +1494,12 @@ Java_edu_wpi_cscore_CameraServerJNI_getSinkProperty } /* - * Class: edu_wpi_cscore_CameraServerJNI + * Class: edu_wpi_first_cscore_CameraServerJNI * Method: enumerateSinkProperties * Signature: (I)[I */ JNIEXPORT jintArray JNICALL -Java_edu_wpi_cscore_CameraServerJNI_enumerateSinkProperties +Java_edu_wpi_first_cscore_CameraServerJNI_enumerateSinkProperties (JNIEnv* env, jclass, jint source) { CS_Status status = 0; @@ -1512,12 +1512,12 @@ Java_edu_wpi_cscore_CameraServerJNI_enumerateSinkProperties } /* - * Class: edu_wpi_cscore_CameraServerJNI + * Class: edu_wpi_first_cscore_CameraServerJNI * Method: setSinkConfigJson * Signature: (ILjava/lang/String;)Z */ JNIEXPORT jboolean JNICALL -Java_edu_wpi_cscore_CameraServerJNI_setSinkConfigJson +Java_edu_wpi_first_cscore_CameraServerJNI_setSinkConfigJson (JNIEnv* env, jclass, jint source, jstring config) { CS_Status status = 0; @@ -1527,12 +1527,12 @@ Java_edu_wpi_cscore_CameraServerJNI_setSinkConfigJson } /* - * Class: edu_wpi_cscore_CameraServerJNI + * Class: edu_wpi_first_cscore_CameraServerJNI * Method: getSinkConfigJson * Signature: (I)Ljava/lang/String; */ JNIEXPORT jstring JNICALL -Java_edu_wpi_cscore_CameraServerJNI_getSinkConfigJson +Java_edu_wpi_first_cscore_CameraServerJNI_getSinkConfigJson (JNIEnv* env, jclass, jint source) { CS_Status status = 0; @@ -1542,12 +1542,12 @@ Java_edu_wpi_cscore_CameraServerJNI_getSinkConfigJson } /* - * Class: edu_wpi_cscore_CameraServerJNI + * Class: edu_wpi_first_cscore_CameraServerJNI * Method: setSinkSource * Signature: (II)V */ JNIEXPORT void JNICALL -Java_edu_wpi_cscore_CameraServerJNI_setSinkSource +Java_edu_wpi_first_cscore_CameraServerJNI_setSinkSource (JNIEnv* env, jclass, jint sink, jint source) { CS_Status status = 0; @@ -1556,12 +1556,12 @@ Java_edu_wpi_cscore_CameraServerJNI_setSinkSource } /* - * Class: edu_wpi_cscore_CameraServerJNI + * Class: edu_wpi_first_cscore_CameraServerJNI * Method: getSinkSourceProperty * Signature: (ILjava/lang/String;)I */ JNIEXPORT jint JNICALL -Java_edu_wpi_cscore_CameraServerJNI_getSinkSourceProperty +Java_edu_wpi_first_cscore_CameraServerJNI_getSinkSourceProperty (JNIEnv* env, jclass, jint sink, jstring name) { if (!name) { @@ -1576,12 +1576,12 @@ Java_edu_wpi_cscore_CameraServerJNI_getSinkSourceProperty } /* - * Class: edu_wpi_cscore_CameraServerJNI + * Class: edu_wpi_first_cscore_CameraServerJNI * Method: getSinkSource * Signature: (I)I */ JNIEXPORT jint JNICALL -Java_edu_wpi_cscore_CameraServerJNI_getSinkSource +Java_edu_wpi_first_cscore_CameraServerJNI_getSinkSource (JNIEnv* env, jclass, jint sink) { CS_Status status = 0; @@ -1591,12 +1591,12 @@ Java_edu_wpi_cscore_CameraServerJNI_getSinkSource } /* - * Class: edu_wpi_cscore_CameraServerJNI + * Class: edu_wpi_first_cscore_CameraServerJNI * Method: copySink * Signature: (I)I */ JNIEXPORT jint JNICALL -Java_edu_wpi_cscore_CameraServerJNI_copySink +Java_edu_wpi_first_cscore_CameraServerJNI_copySink (JNIEnv* env, jclass, jint sink) { CS_Status status = 0; @@ -1606,12 +1606,12 @@ Java_edu_wpi_cscore_CameraServerJNI_copySink } /* - * Class: edu_wpi_cscore_CameraServerJNI + * Class: edu_wpi_first_cscore_CameraServerJNI * Method: releaseSink * Signature: (I)V */ JNIEXPORT void JNICALL -Java_edu_wpi_cscore_CameraServerJNI_releaseSink +Java_edu_wpi_first_cscore_CameraServerJNI_releaseSink (JNIEnv* env, jclass, jint sink) { CS_Status status = 0; @@ -1620,12 +1620,12 @@ Java_edu_wpi_cscore_CameraServerJNI_releaseSink } /* - * Class: edu_wpi_cscore_CameraServerJNI + * Class: edu_wpi_first_cscore_CameraServerJNI * Method: getMjpegServerListenAddress * Signature: (I)Ljava/lang/String; */ JNIEXPORT jstring JNICALL -Java_edu_wpi_cscore_CameraServerJNI_getMjpegServerListenAddress +Java_edu_wpi_first_cscore_CameraServerJNI_getMjpegServerListenAddress (JNIEnv* env, jclass, jint sink) { CS_Status status = 0; @@ -1637,12 +1637,12 @@ Java_edu_wpi_cscore_CameraServerJNI_getMjpegServerListenAddress } /* - * Class: edu_wpi_cscore_CameraServerJNI + * Class: edu_wpi_first_cscore_CameraServerJNI * Method: getMjpegServerPort * Signature: (I)I */ JNIEXPORT jint JNICALL -Java_edu_wpi_cscore_CameraServerJNI_getMjpegServerPort +Java_edu_wpi_first_cscore_CameraServerJNI_getMjpegServerPort (JNIEnv* env, jclass, jint sink) { CS_Status status = 0; @@ -1652,12 +1652,12 @@ Java_edu_wpi_cscore_CameraServerJNI_getMjpegServerPort } /* - * Class: edu_wpi_cscore_CameraServerJNI + * Class: edu_wpi_first_cscore_CameraServerJNI * Method: setSinkDescription * Signature: (ILjava/lang/String;)V */ JNIEXPORT void JNICALL -Java_edu_wpi_cscore_CameraServerJNI_setSinkDescription +Java_edu_wpi_first_cscore_CameraServerJNI_setSinkDescription (JNIEnv* env, jclass, jint sink, jstring description) { if (!description) { @@ -1670,12 +1670,12 @@ Java_edu_wpi_cscore_CameraServerJNI_setSinkDescription } /* - * Class: edu_wpi_cscore_CameraServerCvJNI + * Class: edu_wpi_first_cscore_CameraServerCvJNI * Method: grabSinkFrame * Signature: (IJ)J */ JNIEXPORT jlong JNICALL -Java_edu_wpi_cscore_CameraServerCvJNI_grabSinkFrame +Java_edu_wpi_first_cscore_CameraServerCvJNI_grabSinkFrame (JNIEnv* env, jclass, jint sink, jlong imageNativeObj) { try { @@ -1694,12 +1694,12 @@ Java_edu_wpi_cscore_CameraServerCvJNI_grabSinkFrame } /* - * Class: edu_wpi_cscore_CameraServerCvJNI + * Class: edu_wpi_first_cscore_CameraServerCvJNI * Method: grabSinkFrameTimeout * Signature: (IJD)J */ JNIEXPORT jlong JNICALL -Java_edu_wpi_cscore_CameraServerCvJNI_grabSinkFrameTimeout +Java_edu_wpi_first_cscore_CameraServerCvJNI_grabSinkFrameTimeout (JNIEnv* env, jclass, jint sink, jlong imageNativeObj, jdouble timeout) { try { @@ -1735,12 +1735,12 @@ static void SetRawFrameData(JNIEnv* env, jobject rawFrameObj, } /* - * Class: edu_wpi_cscore_CameraServerJNI + * Class: edu_wpi_first_cscore_CameraServerJNI * Method: grabRawSinkFrameImpl * Signature: (ILjava/lang/Object;JLjava/lang/Object;III)J */ JNIEXPORT jlong JNICALL -Java_edu_wpi_cscore_CameraServerJNI_grabRawSinkFrameImpl +Java_edu_wpi_first_cscore_CameraServerJNI_grabRawSinkFrameImpl (JNIEnv* env, jclass, jint sink, jobject rawFrameObj, jlong rawFramePtr, jobject byteBuffer, jint width, jint height, jint pixelFormat) { @@ -1760,12 +1760,12 @@ Java_edu_wpi_cscore_CameraServerJNI_grabRawSinkFrameImpl } /* - * Class: edu_wpi_cscore_CameraServerJNI + * Class: edu_wpi_first_cscore_CameraServerJNI * Method: grabRawSinkFrameTimeoutImpl * Signature: (ILjava/lang/Object;JLjava/lang/Object;IIID)J */ JNIEXPORT jlong JNICALL -Java_edu_wpi_cscore_CameraServerJNI_grabRawSinkFrameTimeoutImpl +Java_edu_wpi_first_cscore_CameraServerJNI_grabRawSinkFrameTimeoutImpl (JNIEnv* env, jclass, jint sink, jobject rawFrameObj, jlong rawFramePtr, jobject byteBuffer, jint width, jint height, jint pixelFormat, jdouble timeout) @@ -1787,12 +1787,12 @@ Java_edu_wpi_cscore_CameraServerJNI_grabRawSinkFrameTimeoutImpl } /* - * Class: edu_wpi_cscore_CameraServerJNI + * Class: edu_wpi_first_cscore_CameraServerJNI * Method: getSinkError * Signature: (I)Ljava/lang/String; */ JNIEXPORT jstring JNICALL -Java_edu_wpi_cscore_CameraServerJNI_getSinkError +Java_edu_wpi_first_cscore_CameraServerJNI_getSinkError (JNIEnv* env, jclass, jint sink) { CS_Status status = 0; @@ -1805,12 +1805,12 @@ Java_edu_wpi_cscore_CameraServerJNI_getSinkError } /* - * Class: edu_wpi_cscore_CameraServerJNI + * Class: edu_wpi_first_cscore_CameraServerJNI * Method: setSinkEnabled * Signature: (IZ)V */ JNIEXPORT void JNICALL -Java_edu_wpi_cscore_CameraServerJNI_setSinkEnabled +Java_edu_wpi_first_cscore_CameraServerJNI_setSinkEnabled (JNIEnv* env, jclass, jint sink, jboolean enabled) { CS_Status status = 0; @@ -1819,12 +1819,12 @@ Java_edu_wpi_cscore_CameraServerJNI_setSinkEnabled } /* - * Class: edu_wpi_cscore_CameraServerJNI + * Class: edu_wpi_first_cscore_CameraServerJNI * Method: addListener * Signature: (Ljava/lang/Object;IZ)I */ JNIEXPORT jint JNICALL -Java_edu_wpi_cscore_CameraServerJNI_addListener +Java_edu_wpi_first_cscore_CameraServerJNI_addListener (JNIEnv* envouter, jclass, jobject listener, jint eventMask, jboolean immediateNotify) { @@ -1883,12 +1883,12 @@ Java_edu_wpi_cscore_CameraServerJNI_addListener } /* - * Class: edu_wpi_cscore_CameraServerJNI + * Class: edu_wpi_first_cscore_CameraServerJNI * Method: removeListener * Signature: (I)V */ JNIEXPORT void JNICALL -Java_edu_wpi_cscore_CameraServerJNI_removeListener +Java_edu_wpi_first_cscore_CameraServerJNI_removeListener (JNIEnv* env, jclass, jint handle) { CS_Status status = 0; @@ -1897,36 +1897,36 @@ Java_edu_wpi_cscore_CameraServerJNI_removeListener } /* - * Class: edu_wpi_cscore_CameraServerJNI + * Class: edu_wpi_first_cscore_CameraServerJNI * Method: createListenerPoller * Signature: ()I */ JNIEXPORT jint JNICALL -Java_edu_wpi_cscore_CameraServerJNI_createListenerPoller +Java_edu_wpi_first_cscore_CameraServerJNI_createListenerPoller (JNIEnv*, jclass) { return cs::CreateListenerPoller(); } /* - * Class: edu_wpi_cscore_CameraServerJNI + * Class: edu_wpi_first_cscore_CameraServerJNI * Method: destroyListenerPoller * Signature: (I)V */ JNIEXPORT void JNICALL -Java_edu_wpi_cscore_CameraServerJNI_destroyListenerPoller +Java_edu_wpi_first_cscore_CameraServerJNI_destroyListenerPoller (JNIEnv*, jclass, jint poller) { cs::DestroyListenerPoller(poller); } /* - * Class: edu_wpi_cscore_CameraServerJNI + * Class: edu_wpi_first_cscore_CameraServerJNI * Method: addPolledListener * Signature: (IIZ)I */ JNIEXPORT jint JNICALL -Java_edu_wpi_cscore_CameraServerJNI_addPolledListener +Java_edu_wpi_first_cscore_CameraServerJNI_addPolledListener (JNIEnv* env, jclass, jint poller, jint eventMask, jboolean immediateNotify) { CS_Status status = 0; @@ -1936,12 +1936,12 @@ Java_edu_wpi_cscore_CameraServerJNI_addPolledListener } /* - * Class: edu_wpi_cscore_CameraServerJNI + * Class: edu_wpi_first_cscore_CameraServerJNI * Method: pollListener * Signature: (I)[Ljava/lang/Object; */ JNIEXPORT jobjectArray JNICALL -Java_edu_wpi_cscore_CameraServerJNI_pollListener +Java_edu_wpi_first_cscore_CameraServerJNI_pollListener (JNIEnv* env, jclass, jint poller) { auto events = cs::PollListener(poller); @@ -1953,12 +1953,12 @@ Java_edu_wpi_cscore_CameraServerJNI_pollListener } /* - * Class: edu_wpi_cscore_CameraServerJNI + * Class: edu_wpi_first_cscore_CameraServerJNI * Method: pollListenerTimeout * Signature: (ID)[Ljava/lang/Object; */ JNIEXPORT jobjectArray JNICALL -Java_edu_wpi_cscore_CameraServerJNI_pollListenerTimeout +Java_edu_wpi_first_cscore_CameraServerJNI_pollListenerTimeout (JNIEnv* env, jclass, jint poller, jdouble timeout) { bool timed_out = false; @@ -1971,48 +1971,48 @@ Java_edu_wpi_cscore_CameraServerJNI_pollListenerTimeout } /* - * Class: edu_wpi_cscore_CameraServerJNI + * Class: edu_wpi_first_cscore_CameraServerJNI * Method: cancelPollListener * Signature: (I)V */ JNIEXPORT void JNICALL -Java_edu_wpi_cscore_CameraServerJNI_cancelPollListener +Java_edu_wpi_first_cscore_CameraServerJNI_cancelPollListener (JNIEnv*, jclass, jint poller) { cs::CancelPollListener(poller); } /* - * Class: edu_wpi_cscore_CameraServerJNI + * Class: edu_wpi_first_cscore_CameraServerJNI * Method: setTelemetryPeriod * Signature: (D)V */ JNIEXPORT void JNICALL -Java_edu_wpi_cscore_CameraServerJNI_setTelemetryPeriod +Java_edu_wpi_first_cscore_CameraServerJNI_setTelemetryPeriod (JNIEnv* env, jclass, jdouble seconds) { cs::SetTelemetryPeriod(seconds); } /* - * Class: edu_wpi_cscore_CameraServerJNI + * Class: edu_wpi_first_cscore_CameraServerJNI * Method: getTelemetryElapsedTime * Signature: ()D */ JNIEXPORT jdouble JNICALL -Java_edu_wpi_cscore_CameraServerJNI_getTelemetryElapsedTime +Java_edu_wpi_first_cscore_CameraServerJNI_getTelemetryElapsedTime (JNIEnv* env, jclass) { return cs::GetTelemetryElapsedTime(); } /* - * Class: edu_wpi_cscore_CameraServerJNI + * Class: edu_wpi_first_cscore_CameraServerJNI * Method: getTelemetryValue * Signature: (II)J */ JNIEXPORT jlong JNICALL -Java_edu_wpi_cscore_CameraServerJNI_getTelemetryValue +Java_edu_wpi_first_cscore_CameraServerJNI_getTelemetryValue (JNIEnv* env, jclass, jint handle, jint kind) { CS_Status status = 0; @@ -2023,12 +2023,12 @@ Java_edu_wpi_cscore_CameraServerJNI_getTelemetryValue } /* - * Class: edu_wpi_cscore_CameraServerJNI + * Class: edu_wpi_first_cscore_CameraServerJNI * Method: getTelemetryAverageValue * Signature: (II)D */ JNIEXPORT jdouble JNICALL -Java_edu_wpi_cscore_CameraServerJNI_getTelemetryAverageValue +Java_edu_wpi_first_cscore_CameraServerJNI_getTelemetryAverageValue (JNIEnv* env, jclass, jint handle, jint kind) { CS_Status status = 0; @@ -2039,12 +2039,12 @@ Java_edu_wpi_cscore_CameraServerJNI_getTelemetryAverageValue } /* - * Class: edu_wpi_cscore_CameraServerJNI + * Class: edu_wpi_first_cscore_CameraServerJNI * Method: enumerateUsbCameras * Signature: ()[Ljava/lang/Object; */ JNIEXPORT jobjectArray JNICALL -Java_edu_wpi_cscore_CameraServerJNI_enumerateUsbCameras +Java_edu_wpi_first_cscore_CameraServerJNI_enumerateUsbCameras (JNIEnv* env, jclass) { CS_Status status = 0; @@ -2065,12 +2065,12 @@ Java_edu_wpi_cscore_CameraServerJNI_enumerateUsbCameras } /* - * Class: edu_wpi_cscore_CameraServerJNI + * Class: edu_wpi_first_cscore_CameraServerJNI * Method: enumerateSources * Signature: ()[I */ JNIEXPORT jintArray JNICALL -Java_edu_wpi_cscore_CameraServerJNI_enumerateSources +Java_edu_wpi_first_cscore_CameraServerJNI_enumerateSources (JNIEnv* env, jclass) { CS_Status status = 0; @@ -2083,12 +2083,12 @@ Java_edu_wpi_cscore_CameraServerJNI_enumerateSources } /* - * Class: edu_wpi_cscore_CameraServerJNI + * Class: edu_wpi_first_cscore_CameraServerJNI * Method: enumerateSinks * Signature: ()[I */ JNIEXPORT jintArray JNICALL -Java_edu_wpi_cscore_CameraServerJNI_enumerateSinks +Java_edu_wpi_first_cscore_CameraServerJNI_enumerateSinks (JNIEnv* env, jclass) { CS_Status status = 0; @@ -2101,24 +2101,24 @@ Java_edu_wpi_cscore_CameraServerJNI_enumerateSinks } /* - * Class: edu_wpi_cscore_CameraServerJNI + * Class: edu_wpi_first_cscore_CameraServerJNI * Method: getHostname * Signature: ()Ljava/lang/String; */ JNIEXPORT jstring JNICALL -Java_edu_wpi_cscore_CameraServerJNI_getHostname +Java_edu_wpi_first_cscore_CameraServerJNI_getHostname (JNIEnv* env, jclass) { return MakeJString(env, cs::GetHostname()); } /* - * Class: edu_wpi_cscore_CameraServerJNI + * Class: edu_wpi_first_cscore_CameraServerJNI * Method: getNetworkInterfaces * Signature: ()[Ljava/lang/Object; */ JNIEXPORT jobjectArray JNICALL -Java_edu_wpi_cscore_CameraServerJNI_getNetworkInterfaces +Java_edu_wpi_first_cscore_CameraServerJNI_getNetworkInterfaces (JNIEnv* env, jclass) { return MakeJStringArray(env, cs::GetNetworkInterfaces()); @@ -2158,12 +2158,12 @@ typedef JSingletonCallbackManager LoggerJNI; extern "C" { /* - * Class: edu_wpi_cscore_CameraServerJNI + * Class: edu_wpi_first_cscore_CameraServerJNI * Method: setLogger * Signature: (Ljava/lang/Object;I)V */ JNIEXPORT void JNICALL -Java_edu_wpi_cscore_CameraServerJNI_setLogger +Java_edu_wpi_first_cscore_CameraServerJNI_setLogger (JNIEnv* env, jclass, jobject func, jint minLevel) { if (!func) { @@ -2196,12 +2196,12 @@ Java_edu_wpi_cscore_CameraServerJNI_setLogger } /* - * Class: edu_wpi_cscore_CameraServerJNI + * Class: edu_wpi_first_cscore_CameraServerJNI * Method: allocateRawFrame * Signature: ()J */ JNIEXPORT jlong JNICALL -Java_edu_wpi_cscore_CameraServerJNI_allocateRawFrame +Java_edu_wpi_first_cscore_CameraServerJNI_allocateRawFrame (JNIEnv*, jclass) { cs::RawFrame* rawFrame = new cs::RawFrame{}; @@ -2210,12 +2210,12 @@ Java_edu_wpi_cscore_CameraServerJNI_allocateRawFrame } /* - * Class: edu_wpi_cscore_CameraServerJNI + * Class: edu_wpi_first_cscore_CameraServerJNI * Method: freeRawFrame * Signature: (J)V */ JNIEXPORT void JNICALL -Java_edu_wpi_cscore_CameraServerJNI_freeRawFrame +Java_edu_wpi_first_cscore_CameraServerJNI_freeRawFrame (JNIEnv*, jclass, jlong rawFrame) { cs::RawFrame* ptr = diff --git a/cscore/src/test/java/edu/wpi/cscore/JNITest.java b/cscore/src/test/java/edu/wpi/first/cscore/JNITest.java similarity index 92% rename from cscore/src/test/java/edu/wpi/cscore/JNITest.java rename to cscore/src/test/java/edu/wpi/first/cscore/JNITest.java index 11f93f881f..40e2584a98 100644 --- a/cscore/src/test/java/edu/wpi/cscore/JNITest.java +++ b/cscore/src/test/java/edu/wpi/first/cscore/JNITest.java @@ -2,7 +2,7 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.cscore; +package edu.wpi.first.cscore; import org.junit.jupiter.api.Test; diff --git a/cscore/src/test/java/edu/wpi/cscore/UsbCameraTest.java b/cscore/src/test/java/edu/wpi/first/cscore/UsbCameraTest.java similarity index 98% rename from cscore/src/test/java/edu/wpi/cscore/UsbCameraTest.java rename to cscore/src/test/java/edu/wpi/first/cscore/UsbCameraTest.java index 13a68d00db..8a790fa788 100644 --- a/cscore/src/test/java/edu/wpi/cscore/UsbCameraTest.java +++ b/cscore/src/test/java/edu/wpi/first/cscore/UsbCameraTest.java @@ -2,7 +2,7 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.cscore; +package edu.wpi.first.cscore; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTimeoutPreemptively; diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/RobotBase.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/RobotBase.java index cf57de6c8b..06ba471ffd 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/RobotBase.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/RobotBase.java @@ -4,9 +4,9 @@ package edu.wpi.first.wpilibj; -import edu.wpi.cscore.CameraServerJNI; import edu.wpi.first.cameraserver.CameraServerShared; import edu.wpi.first.cameraserver.CameraServerSharedStore; +import edu.wpi.first.cscore.CameraServerJNI; import edu.wpi.first.hal.FRCNetComm.tInstances; import edu.wpi.first.hal.FRCNetComm.tResourceType; import edu.wpi.first.hal.HAL; diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/shuffleboard/BuiltInWidgets.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/shuffleboard/BuiltInWidgets.java index 84645c794e..7db33edee6 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/shuffleboard/BuiltInWidgets.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/shuffleboard/BuiltInWidgets.java @@ -453,7 +453,7 @@ public enum BuiltInWidgets implements WidgetType { * Supported types: * *

    - *
  • {@link edu.wpi.cscore.VideoSource} (as long as it is streaming on an MJPEG server) + *
  • {@link edu.wpi.first.cscore.VideoSource} (as long as it is streaming on an MJPEG server) *
* *
diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/shuffleboard/SendableCameraWrapper.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/shuffleboard/SendableCameraWrapper.java index 3cfdc24b08..b80831c786 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/shuffleboard/SendableCameraWrapper.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/shuffleboard/SendableCameraWrapper.java @@ -4,7 +4,7 @@ package edu.wpi.first.wpilibj.shuffleboard; -import edu.wpi.cscore.VideoSource; +import edu.wpi.first.cscore.VideoSource; import edu.wpi.first.wpilibj.Sendable; import edu.wpi.first.wpilibj.smartdashboard.SendableBuilder; import edu.wpi.first.wpilibj.smartdashboard.SendableRegistry; diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/shuffleboard/ShuffleboardContainer.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/shuffleboard/ShuffleboardContainer.java index c81e372213..1f0ef5e70d 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/shuffleboard/ShuffleboardContainer.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/shuffleboard/ShuffleboardContainer.java @@ -4,7 +4,7 @@ package edu.wpi.first.wpilibj.shuffleboard; -import edu.wpi.cscore.VideoSource; +import edu.wpi.first.cscore.VideoSource; import edu.wpi.first.wpilibj.Sendable; import java.util.List; import java.util.NoSuchElementException; diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/vision/VisionRunner.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/vision/VisionRunner.java index 21ca13c2a1..a1002f5349 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/vision/VisionRunner.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/vision/VisionRunner.java @@ -4,9 +4,9 @@ package edu.wpi.first.wpilibj.vision; -import edu.wpi.cscore.CvSink; -import edu.wpi.cscore.VideoSource; import edu.wpi.first.cameraserver.CameraServerSharedStore; +import edu.wpi.first.cscore.CvSink; +import edu.wpi.first.cscore.VideoSource; import org.opencv.core.Mat; /** diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/vision/VisionThread.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/vision/VisionThread.java index 78bb961a0a..5f23fe180f 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/vision/VisionThread.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/vision/VisionThread.java @@ -4,7 +4,7 @@ package edu.wpi.first.wpilibj.vision; -import edu.wpi.cscore.VideoSource; +import edu.wpi.first.cscore.VideoSource; /** * A vision thread is a special thread that runs a vision pipeline. It is a daemon thread; it diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/vision/package-info.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/vision/package-info.java index 371d30e15c..ba3e1e1914 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/vision/package-info.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/vision/package-info.java @@ -13,7 +13,7 @@ * implements VisionRunner.Listener<MyFindTotePipeline> { * * // A USB camera connected to the roboRIO. - * private {@link edu.wpi.cscore.VideoSource VideoSource} usbCamera; + * private {@link edu.wpi.first.cscore.VideoSource VideoSource} usbCamera; * * // A vision pipeline. This could be handwritten or generated by GRIP. * // This has to implement {@link edu.wpi.first.wpilibj.vision.VisionPipeline}. diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/axiscamera/Robot.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/axiscamera/Robot.java index 4d827fb7b8..e2766814a4 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/axiscamera/Robot.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/axiscamera/Robot.java @@ -4,10 +4,10 @@ package edu.wpi.first.wpilibj.examples.axiscamera; -import edu.wpi.cscore.AxisCamera; -import edu.wpi.cscore.CvSink; -import edu.wpi.cscore.CvSource; import edu.wpi.first.cameraserver.CameraServer; +import edu.wpi.first.cscore.AxisCamera; +import edu.wpi.first.cscore.CvSink; +import edu.wpi.first.cscore.CvSource; import edu.wpi.first.wpilibj.TimedRobot; import org.opencv.core.Mat; import org.opencv.core.Point; diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/intermediatevision/Robot.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/intermediatevision/Robot.java index 6395da29b4..57ad77e11b 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/intermediatevision/Robot.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/intermediatevision/Robot.java @@ -4,10 +4,10 @@ package edu.wpi.first.wpilibj.examples.intermediatevision; -import edu.wpi.cscore.CvSink; -import edu.wpi.cscore.CvSource; -import edu.wpi.cscore.UsbCamera; import edu.wpi.first.cameraserver.CameraServer; +import edu.wpi.first.cscore.CvSink; +import edu.wpi.first.cscore.CvSource; +import edu.wpi.first.cscore.UsbCamera; import edu.wpi.first.wpilibj.TimedRobot; import org.opencv.core.Mat; import org.opencv.core.Point; From 6b168ab0c8bc83530b3641a37e6af3df16dc4b1b Mon Sep 17 00:00:00 2001 From: Peter Johnson Date: Fri, 12 Mar 2021 15:41:52 -0800 Subject: [PATCH 19/34] [wpilib] Remove PIDController, PIDOutput, PIDSource Move them to the old commands vendordep so that PIDCommand and PIDSubsystem continue to work. This also removes Filter and LinearDigitalFilter. --- .../java/edu/wpi/first/wpilibj/PIDBase.java | 0 .../edu/wpi/first/wpilibj/PIDController.java | 0 .../edu/wpi/first/wpilibj/PIDInterface.java | 0 .../java/edu/wpi/first/wpilibj/PIDOutput.java | 0 .../java/edu/wpi/first/wpilibj/PIDSource.java | 0 .../edu/wpi/first/wpilibj/PIDSourceType.java | 0 .../src/main/native/cpp/PIDBase.cpp | 0 .../src/main/native/cpp/PIDController.cpp | 0 .../src/main/native/cpp/PIDSource.cpp | 0 .../src/main/native/include/frc/PIDBase.h | 0 .../main/native/include/frc/PIDController.h | 0 .../main/native/include/frc/PIDInterface.h | 0 .../src/main/native/include/frc/PIDOutput.h | 0 .../src/main/native/include/frc/PIDSource.h | 0 .../main/native/cpp/AnalogAccelerometer.cpp | 4 - wpilibc/src/main/native/cpp/AnalogInput.cpp | 7 - .../main/native/cpp/AnalogPotentiometer.cpp | 4 - wpilibc/src/main/native/cpp/Encoder.cpp | 14 -- wpilibc/src/main/native/cpp/GyroBase.cpp | 11 - .../src/main/native/cpp/NidecBrushless.cpp | 4 - .../main/native/cpp/PWMSpeedController.cpp | 4 - .../main/native/cpp/SpeedControllerGroup.cpp | 4 - wpilibc/src/main/native/cpp/Ultrasonic.cpp | 41 +--- .../src/main/native/cpp/filters/Filter.cpp | 27 --- .../cpp/filters/LinearDigitalFilter.cpp | 133 ----------- .../native/cpp/interfaces/Potentiometer.cpp | 15 -- .../native/include/frc/AnalogAccelerometer.h | 9 - .../src/main/native/include/frc/AnalogInput.h | 9 - .../native/include/frc/AnalogPotentiometer.h | 7 - wpilibc/src/main/native/include/frc/Encoder.h | 4 - .../src/main/native/include/frc/GyroBase.h | 11 - .../main/native/include/frc/NidecBrushless.h | 8 - .../native/include/frc/PWMSpeedController.h | 7 - .../main/native/include/frc/SpeedController.h | 4 +- .../native/include/frc/SpeedControllerGroup.h | 1 - .../src/main/native/include/frc/Ultrasonic.h | 44 +--- .../include/frc/controller/PIDController.h | 6 + .../main/native/include/frc/filters/Filter.h | 61 ----- .../include/frc/filters/LinearDigitalFilter.h | 223 ------------------ .../include/frc/interfaces/Potentiometer.h | 8 +- .../test/native/cpp/MockSpeedController.cpp | 4 - .../native/cpp/SpeedControllerGroupTest.cpp | 8 - .../test/native/include/MockSpeedController.h | 2 - .../include/ExampleSmartMotorController.h | 2 - .../include/ExampleSmartMotorController.h | 2 - .../include/ExampleSmartMotorController.h | 2 - .../PacGoat/cpp/subsystems/DriveTrain.cpp | 4 - .../examples/PacGoat/cpp/subsystems/Pivot.cpp | 2 +- .../first/wpilibj/AnalogAccelerometer.java | 23 +- .../edu/wpi/first/wpilibj/AnalogInput.java | 23 +- .../first/wpilibj/AnalogPotentiometer.java | 24 -- .../java/edu/wpi/first/wpilibj/Counter.java | 36 +-- .../java/edu/wpi/first/wpilibj/Encoder.java | 38 +-- .../java/edu/wpi/first/wpilibj/GyroBase.java | 38 +-- .../edu/wpi/first/wpilibj/NidecBrushless.java | 10 - .../wpi/first/wpilibj/PWMSpeedController.java | 10 - .../wpi/first/wpilibj/SpeedController.java | 3 +- .../first/wpilibj/SpeedControllerGroup.java | 5 - .../edu/wpi/first/wpilibj/Ultrasonic.java | 95 +------- .../edu/wpi/first/wpilibj/filters/Filter.java | 54 ----- .../wpilibj/filters/LinearDigitalFilter.java | 187 --------------- .../wpilibj/interfaces/Potentiometer.java | 4 +- .../first/wpilibj/MockSpeedController.java | 5 - .../wpilibj/SpeedControllerGroupTest.java | 11 - .../ExampleSmartMotorController.java | 3 - .../ExampleSmartMotorController.java | 3 - .../ExampleSmartMotorController.java | 3 - .../examples/pacgoat/subsystems/Pivot.java | 2 +- .../first/wpilibj/MockSpeedController.java | 5 - 69 files changed, 30 insertions(+), 1248 deletions(-) rename {wpilibj => wpilibOldCommands}/src/main/java/edu/wpi/first/wpilibj/PIDBase.java (100%) rename {wpilibj => wpilibOldCommands}/src/main/java/edu/wpi/first/wpilibj/PIDController.java (100%) rename {wpilibj => wpilibOldCommands}/src/main/java/edu/wpi/first/wpilibj/PIDInterface.java (100%) rename {wpilibj => wpilibOldCommands}/src/main/java/edu/wpi/first/wpilibj/PIDOutput.java (100%) rename {wpilibj => wpilibOldCommands}/src/main/java/edu/wpi/first/wpilibj/PIDSource.java (100%) rename {wpilibj => wpilibOldCommands}/src/main/java/edu/wpi/first/wpilibj/PIDSourceType.java (100%) rename {wpilibc => wpilibOldCommands}/src/main/native/cpp/PIDBase.cpp (100%) rename {wpilibc => wpilibOldCommands}/src/main/native/cpp/PIDController.cpp (100%) rename {wpilibc => wpilibOldCommands}/src/main/native/cpp/PIDSource.cpp (100%) rename {wpilibc => wpilibOldCommands}/src/main/native/include/frc/PIDBase.h (100%) rename {wpilibc => wpilibOldCommands}/src/main/native/include/frc/PIDController.h (100%) rename {wpilibc => wpilibOldCommands}/src/main/native/include/frc/PIDInterface.h (100%) rename {wpilibc => wpilibOldCommands}/src/main/native/include/frc/PIDOutput.h (100%) rename {wpilibc => wpilibOldCommands}/src/main/native/include/frc/PIDSource.h (100%) delete mode 100644 wpilibc/src/main/native/cpp/filters/Filter.cpp delete mode 100644 wpilibc/src/main/native/cpp/filters/LinearDigitalFilter.cpp delete mode 100644 wpilibc/src/main/native/cpp/interfaces/Potentiometer.cpp delete mode 100644 wpilibc/src/main/native/include/frc/filters/Filter.h delete mode 100644 wpilibc/src/main/native/include/frc/filters/LinearDigitalFilter.h delete mode 100644 wpilibj/src/main/java/edu/wpi/first/wpilibj/filters/Filter.java delete mode 100644 wpilibj/src/main/java/edu/wpi/first/wpilibj/filters/LinearDigitalFilter.java diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/PIDBase.java b/wpilibOldCommands/src/main/java/edu/wpi/first/wpilibj/PIDBase.java similarity index 100% rename from wpilibj/src/main/java/edu/wpi/first/wpilibj/PIDBase.java rename to wpilibOldCommands/src/main/java/edu/wpi/first/wpilibj/PIDBase.java diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/PIDController.java b/wpilibOldCommands/src/main/java/edu/wpi/first/wpilibj/PIDController.java similarity index 100% rename from wpilibj/src/main/java/edu/wpi/first/wpilibj/PIDController.java rename to wpilibOldCommands/src/main/java/edu/wpi/first/wpilibj/PIDController.java diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/PIDInterface.java b/wpilibOldCommands/src/main/java/edu/wpi/first/wpilibj/PIDInterface.java similarity index 100% rename from wpilibj/src/main/java/edu/wpi/first/wpilibj/PIDInterface.java rename to wpilibOldCommands/src/main/java/edu/wpi/first/wpilibj/PIDInterface.java diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/PIDOutput.java b/wpilibOldCommands/src/main/java/edu/wpi/first/wpilibj/PIDOutput.java similarity index 100% rename from wpilibj/src/main/java/edu/wpi/first/wpilibj/PIDOutput.java rename to wpilibOldCommands/src/main/java/edu/wpi/first/wpilibj/PIDOutput.java diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/PIDSource.java b/wpilibOldCommands/src/main/java/edu/wpi/first/wpilibj/PIDSource.java similarity index 100% rename from wpilibj/src/main/java/edu/wpi/first/wpilibj/PIDSource.java rename to wpilibOldCommands/src/main/java/edu/wpi/first/wpilibj/PIDSource.java diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/PIDSourceType.java b/wpilibOldCommands/src/main/java/edu/wpi/first/wpilibj/PIDSourceType.java similarity index 100% rename from wpilibj/src/main/java/edu/wpi/first/wpilibj/PIDSourceType.java rename to wpilibOldCommands/src/main/java/edu/wpi/first/wpilibj/PIDSourceType.java diff --git a/wpilibc/src/main/native/cpp/PIDBase.cpp b/wpilibOldCommands/src/main/native/cpp/PIDBase.cpp similarity index 100% rename from wpilibc/src/main/native/cpp/PIDBase.cpp rename to wpilibOldCommands/src/main/native/cpp/PIDBase.cpp diff --git a/wpilibc/src/main/native/cpp/PIDController.cpp b/wpilibOldCommands/src/main/native/cpp/PIDController.cpp similarity index 100% rename from wpilibc/src/main/native/cpp/PIDController.cpp rename to wpilibOldCommands/src/main/native/cpp/PIDController.cpp diff --git a/wpilibc/src/main/native/cpp/PIDSource.cpp b/wpilibOldCommands/src/main/native/cpp/PIDSource.cpp similarity index 100% rename from wpilibc/src/main/native/cpp/PIDSource.cpp rename to wpilibOldCommands/src/main/native/cpp/PIDSource.cpp diff --git a/wpilibc/src/main/native/include/frc/PIDBase.h b/wpilibOldCommands/src/main/native/include/frc/PIDBase.h similarity index 100% rename from wpilibc/src/main/native/include/frc/PIDBase.h rename to wpilibOldCommands/src/main/native/include/frc/PIDBase.h diff --git a/wpilibc/src/main/native/include/frc/PIDController.h b/wpilibOldCommands/src/main/native/include/frc/PIDController.h similarity index 100% rename from wpilibc/src/main/native/include/frc/PIDController.h rename to wpilibOldCommands/src/main/native/include/frc/PIDController.h diff --git a/wpilibc/src/main/native/include/frc/PIDInterface.h b/wpilibOldCommands/src/main/native/include/frc/PIDInterface.h similarity index 100% rename from wpilibc/src/main/native/include/frc/PIDInterface.h rename to wpilibOldCommands/src/main/native/include/frc/PIDInterface.h diff --git a/wpilibc/src/main/native/include/frc/PIDOutput.h b/wpilibOldCommands/src/main/native/include/frc/PIDOutput.h similarity index 100% rename from wpilibc/src/main/native/include/frc/PIDOutput.h rename to wpilibOldCommands/src/main/native/include/frc/PIDOutput.h diff --git a/wpilibc/src/main/native/include/frc/PIDSource.h b/wpilibOldCommands/src/main/native/include/frc/PIDSource.h similarity index 100% rename from wpilibc/src/main/native/include/frc/PIDSource.h rename to wpilibOldCommands/src/main/native/include/frc/PIDSource.h diff --git a/wpilibc/src/main/native/cpp/AnalogAccelerometer.cpp b/wpilibc/src/main/native/cpp/AnalogAccelerometer.cpp index fa8654076a..e0e3ef71ac 100644 --- a/wpilibc/src/main/native/cpp/AnalogAccelerometer.cpp +++ b/wpilibc/src/main/native/cpp/AnalogAccelerometer.cpp @@ -48,10 +48,6 @@ void AnalogAccelerometer::SetZero(double zero) { m_zeroGVoltage = zero; } -double AnalogAccelerometer::PIDGet() { - return GetAcceleration(); -} - void AnalogAccelerometer::InitSendable(SendableBuilder& builder) { builder.SetSmartDashboardType("Accelerometer"); builder.AddDoubleProperty( diff --git a/wpilibc/src/main/native/cpp/AnalogInput.cpp b/wpilibc/src/main/native/cpp/AnalogInput.cpp index b67120dbf2..9c6d0c2deb 100644 --- a/wpilibc/src/main/native/cpp/AnalogInput.cpp +++ b/wpilibc/src/main/native/cpp/AnalogInput.cpp @@ -254,13 +254,6 @@ double AnalogInput::GetSampleRate() { return sampleRate; } -double AnalogInput::PIDGet() { - if (StatusIsFatal()) { - return 0.0; - } - return GetAverageVoltage(); -} - void AnalogInput::SetSimDevice(HAL_SimDeviceHandle device) { HAL_SetAnalogInputSimDevice(m_port, device); } diff --git a/wpilibc/src/main/native/cpp/AnalogPotentiometer.cpp b/wpilibc/src/main/native/cpp/AnalogPotentiometer.cpp index b911285bb1..8237658dbc 100644 --- a/wpilibc/src/main/native/cpp/AnalogPotentiometer.cpp +++ b/wpilibc/src/main/native/cpp/AnalogPotentiometer.cpp @@ -42,10 +42,6 @@ double AnalogPotentiometer::Get() const { m_offset; } -double AnalogPotentiometer::PIDGet() { - return Get(); -} - void AnalogPotentiometer::InitSendable(SendableBuilder& builder) { builder.SetSmartDashboardType("Analog Input"); builder.AddDoubleProperty( diff --git a/wpilibc/src/main/native/cpp/Encoder.cpp b/wpilibc/src/main/native/cpp/Encoder.cpp index 57e58590af..2d7373940b 100644 --- a/wpilibc/src/main/native/cpp/Encoder.cpp +++ b/wpilibc/src/main/native/cpp/Encoder.cpp @@ -213,20 +213,6 @@ int Encoder::GetSamplesToAverage() const { return result; } -double Encoder::PIDGet() { - if (StatusIsFatal()) { - return 0.0; - } - switch (GetPIDSourceType()) { - case PIDSourceType::kDisplacement: - return GetDistance(); - case PIDSourceType::kRate: - return GetRate(); - default: - return 0.0; - } -} - void Encoder::SetIndexSource(int channel, Encoder::IndexingType type) { // Force digital input if just given an index m_indexSource = std::make_shared(channel); diff --git a/wpilibc/src/main/native/cpp/GyroBase.cpp b/wpilibc/src/main/native/cpp/GyroBase.cpp index 9aeb10cb98..bcb25fad13 100644 --- a/wpilibc/src/main/native/cpp/GyroBase.cpp +++ b/wpilibc/src/main/native/cpp/GyroBase.cpp @@ -9,17 +9,6 @@ using namespace frc; -double GyroBase::PIDGet() { - switch (GetPIDSourceType()) { - case PIDSourceType::kRate: - return GetRate(); - case PIDSourceType::kDisplacement: - return GetAngle(); - default: - return 0; - } -} - void GyroBase::InitSendable(SendableBuilder& builder) { builder.SetSmartDashboardType("Gyro"); builder.AddDoubleProperty( diff --git a/wpilibc/src/main/native/cpp/NidecBrushless.cpp b/wpilibc/src/main/native/cpp/NidecBrushless.cpp index 4909a01e31..ff61843000 100644 --- a/wpilibc/src/main/native/cpp/NidecBrushless.cpp +++ b/wpilibc/src/main/native/cpp/NidecBrushless.cpp @@ -59,10 +59,6 @@ void NidecBrushless::Enable() { m_disabled = false; } -void NidecBrushless::PIDWrite(double output) { - Set(output); -} - void NidecBrushless::StopMotor() { m_dio.UpdateDutyCycle(0.5); m_pwm.SetDisabled(); diff --git a/wpilibc/src/main/native/cpp/PWMSpeedController.cpp b/wpilibc/src/main/native/cpp/PWMSpeedController.cpp index 6012301874..ff04282559 100644 --- a/wpilibc/src/main/native/cpp/PWMSpeedController.cpp +++ b/wpilibc/src/main/native/cpp/PWMSpeedController.cpp @@ -42,10 +42,6 @@ int PWMSpeedController::GetChannel() const { return m_pwm.GetChannel(); } -void PWMSpeedController::PIDWrite(double output) { - Set(output); -} - PWMSpeedController::PWMSpeedController(const wpi::Twine& name, int channel) : m_pwm(channel, false) { SendableRegistry::GetInstance().AddLW(this, name, channel); diff --git a/wpilibc/src/main/native/cpp/SpeedControllerGroup.cpp b/wpilibc/src/main/native/cpp/SpeedControllerGroup.cpp index ce9aa32203..a9976d74a3 100644 --- a/wpilibc/src/main/native/cpp/SpeedControllerGroup.cpp +++ b/wpilibc/src/main/native/cpp/SpeedControllerGroup.cpp @@ -60,10 +60,6 @@ void SpeedControllerGroup::StopMotor() { } } -void SpeedControllerGroup::PIDWrite(double output) { - Set(output); -} - void SpeedControllerGroup::InitSendable(SendableBuilder& builder) { builder.SetSmartDashboardType("Speed Controller"); builder.SetActuator(true); diff --git a/wpilibc/src/main/native/cpp/Ultrasonic.cpp b/wpilibc/src/main/native/cpp/Ultrasonic.cpp index dd34fe8553..140c66ae5b 100644 --- a/wpilibc/src/main/native/cpp/Ultrasonic.cpp +++ b/wpilibc/src/main/native/cpp/Ultrasonic.cpp @@ -26,47 +26,39 @@ std::atomic Ultrasonic::m_automaticEnabled{false}; std::vector Ultrasonic::m_sensors; std::thread Ultrasonic::m_thread; -Ultrasonic::Ultrasonic(int pingChannel, int echoChannel, DistanceUnit units) +Ultrasonic::Ultrasonic(int pingChannel, int echoChannel) : m_pingChannel(std::make_shared(pingChannel)), m_echoChannel(std::make_shared(echoChannel)), m_counter(m_echoChannel) { - m_units = units; Initialize(); auto& registry = SendableRegistry::GetInstance(); registry.AddChild(this, m_pingChannel.get()); registry.AddChild(this, m_echoChannel.get()); } -Ultrasonic::Ultrasonic(DigitalOutput* pingChannel, DigitalInput* echoChannel, - DistanceUnit units) +Ultrasonic::Ultrasonic(DigitalOutput* pingChannel, DigitalInput* echoChannel) : m_pingChannel(pingChannel, NullDeleter()), m_echoChannel(echoChannel, NullDeleter()), m_counter(m_echoChannel) { if (pingChannel == nullptr || echoChannel == nullptr) { wpi_setWPIError(NullParameter); - m_units = units; return; } - m_units = units; Initialize(); } -Ultrasonic::Ultrasonic(DigitalOutput& pingChannel, DigitalInput& echoChannel, - DistanceUnit units) +Ultrasonic::Ultrasonic(DigitalOutput& pingChannel, DigitalInput& echoChannel) : m_pingChannel(&pingChannel, NullDeleter()), m_echoChannel(&echoChannel, NullDeleter()), m_counter(m_echoChannel) { - m_units = units; Initialize(); } Ultrasonic::Ultrasonic(std::shared_ptr pingChannel, - std::shared_ptr echoChannel, - DistanceUnit units) + std::shared_ptr echoChannel) : m_pingChannel(std::move(pingChannel)), m_echoChannel(std::move(echoChannel)), m_counter(m_echoChannel) { - m_units = units; Initialize(); } @@ -164,31 +156,6 @@ void Ultrasonic::SetEnabled(bool enable) { m_enabled = enable; } -void Ultrasonic::SetDistanceUnits(DistanceUnit units) { - m_units = units; -} - -Ultrasonic::DistanceUnit Ultrasonic::GetDistanceUnits() const { - return m_units; -} - -double Ultrasonic::PIDGet() { - switch (m_units) { - case Ultrasonic::kInches: - return GetRangeInches(); - case Ultrasonic::kMilliMeters: - return GetRangeMM(); - default: - return 0.0; - } -} - -void Ultrasonic::SetPIDSourceType(PIDSourceType pidSource) { - if (wpi_assert(pidSource == PIDSourceType::kDisplacement)) { - m_pidSource = pidSource; - } -} - void Ultrasonic::InitSendable(SendableBuilder& builder) { builder.SetSmartDashboardType("Ultrasonic"); builder.AddDoubleProperty( diff --git a/wpilibc/src/main/native/cpp/filters/Filter.cpp b/wpilibc/src/main/native/cpp/filters/Filter.cpp deleted file mode 100644 index 1676c94894..0000000000 --- a/wpilibc/src/main/native/cpp/filters/Filter.cpp +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright (c) FIRST and other WPILib contributors. -// Open Source Software; you can modify and/or share it under the terms of -// the WPILib BSD license file in the root directory of this project. - -#include "frc/filters/Filter.h" - -#include "frc/Base.h" - -using namespace frc; - -Filter::Filter(PIDSource& source) - : m_source(std::shared_ptr(&source, NullDeleter())) {} - -Filter::Filter(std::shared_ptr source) - : m_source(std::move(source)) {} - -void Filter::SetPIDSourceType(PIDSourceType pidSource) { - m_source->SetPIDSourceType(pidSource); -} - -PIDSourceType Filter::GetPIDSourceType() const { - return m_source->GetPIDSourceType(); -} - -double Filter::PIDGetSource() { - return m_source->PIDGet(); -} diff --git a/wpilibc/src/main/native/cpp/filters/LinearDigitalFilter.cpp b/wpilibc/src/main/native/cpp/filters/LinearDigitalFilter.cpp deleted file mode 100644 index c4b48f6783..0000000000 --- a/wpilibc/src/main/native/cpp/filters/LinearDigitalFilter.cpp +++ /dev/null @@ -1,133 +0,0 @@ -// Copyright (c) FIRST and other WPILib contributors. -// Open Source Software; you can modify and/or share it under the terms of -// the WPILib BSD license file in the root directory of this project. - -#include "frc/filters/LinearDigitalFilter.h" - -#include -#include - -#include - -using namespace frc; - -LinearDigitalFilter::LinearDigitalFilter(PIDSource& source, - wpi::ArrayRef ffGains, - wpi::ArrayRef fbGains) - : Filter(source), - m_inputs(ffGains.size()), - m_outputs(fbGains.size()), - m_inputGains(ffGains), - m_outputGains(fbGains) { - static int instances = 0; - instances++; - HAL_Report(HALUsageReporting::kResourceType_LinearFilter, instances); -} - -LinearDigitalFilter::LinearDigitalFilter(PIDSource& source, - std::initializer_list ffGains, - std::initializer_list fbGains) - : LinearDigitalFilter(source, - wpi::makeArrayRef(ffGains.begin(), ffGains.end()), - wpi::makeArrayRef(fbGains.begin(), fbGains.end())) {} - -LinearDigitalFilter::LinearDigitalFilter(std::shared_ptr source, - wpi::ArrayRef ffGains, - wpi::ArrayRef fbGains) - : Filter(source), - m_inputs(ffGains.size()), - m_outputs(fbGains.size()), - m_inputGains(ffGains), - m_outputGains(fbGains) { - static int instances = 0; - instances++; - HAL_Report(HALUsageReporting::kResourceType_LinearFilter, instances); -} - -LinearDigitalFilter::LinearDigitalFilter(std::shared_ptr source, - std::initializer_list ffGains, - std::initializer_list fbGains) - : LinearDigitalFilter(source, - wpi::makeArrayRef(ffGains.begin(), ffGains.end()), - wpi::makeArrayRef(fbGains.begin(), fbGains.end())) {} - -LinearDigitalFilter LinearDigitalFilter::SinglePoleIIR(PIDSource& source, - double timeConstant, - double period) { - double gain = std::exp(-period / timeConstant); - return LinearDigitalFilter(source, {1.0 - gain}, {-gain}); -} - -LinearDigitalFilter LinearDigitalFilter::HighPass(PIDSource& source, - double timeConstant, - double period) { - double gain = std::exp(-period / timeConstant); - return LinearDigitalFilter(source, {gain, -gain}, {-gain}); -} - -LinearDigitalFilter LinearDigitalFilter::MovingAverage(PIDSource& source, - int taps) { - assert(taps > 0); - - std::vector gains(taps, 1.0 / taps); - return LinearDigitalFilter(source, gains, {}); -} - -LinearDigitalFilter LinearDigitalFilter::SinglePoleIIR( - std::shared_ptr source, double timeConstant, double period) { - double gain = std::exp(-period / timeConstant); - return LinearDigitalFilter(std::move(source), {1.0 - gain}, {-gain}); -} - -LinearDigitalFilter LinearDigitalFilter::HighPass( - std::shared_ptr source, double timeConstant, double period) { - double gain = std::exp(-period / timeConstant); - return LinearDigitalFilter(std::move(source), {gain, -gain}, {-gain}); -} - -LinearDigitalFilter LinearDigitalFilter::MovingAverage( - std::shared_ptr source, int taps) { - assert(taps > 0); - - std::vector gains(taps, 1.0 / taps); - return LinearDigitalFilter(std::move(source), gains, {}); -} - -double LinearDigitalFilter::Get() const { - double retVal = 0.0; - - // Calculate the new value - for (size_t i = 0; i < m_inputGains.size(); i++) { - retVal += m_inputs[i] * m_inputGains[i]; - } - for (size_t i = 0; i < m_outputGains.size(); i++) { - retVal -= m_outputs[i] * m_outputGains[i]; - } - - return retVal; -} - -void LinearDigitalFilter::Reset() { - m_inputs.reset(); - m_outputs.reset(); -} - -double LinearDigitalFilter::PIDGet() { - double retVal = 0.0; - - // Rotate the inputs - m_inputs.push_front(PIDGetSource()); - - // Calculate the new value - for (size_t i = 0; i < m_inputGains.size(); i++) { - retVal += m_inputs[i] * m_inputGains[i]; - } - for (size_t i = 0; i < m_outputGains.size(); i++) { - retVal -= m_outputs[i] * m_outputGains[i]; - } - - // Rotate the outputs - m_outputs.push_front(retVal); - - return retVal; -} diff --git a/wpilibc/src/main/native/cpp/interfaces/Potentiometer.cpp b/wpilibc/src/main/native/cpp/interfaces/Potentiometer.cpp deleted file mode 100644 index 9b412213b5..0000000000 --- a/wpilibc/src/main/native/cpp/interfaces/Potentiometer.cpp +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright (c) FIRST and other WPILib contributors. -// Open Source Software; you can modify and/or share it under the terms of -// the WPILib BSD license file in the root directory of this project. - -#include "frc/interfaces/Potentiometer.h" - -#include "frc/Utility.h" - -using namespace frc; - -void Potentiometer::SetPIDSourceType(PIDSourceType pidSource) { - if (wpi_assert(pidSource == PIDSourceType::kDisplacement)) { - m_pidSource = pidSource; - } -} diff --git a/wpilibc/src/main/native/include/frc/AnalogAccelerometer.h b/wpilibc/src/main/native/include/frc/AnalogAccelerometer.h index 96b9598c74..f157cd5d6a 100644 --- a/wpilibc/src/main/native/include/frc/AnalogAccelerometer.h +++ b/wpilibc/src/main/native/include/frc/AnalogAccelerometer.h @@ -8,7 +8,6 @@ #include "frc/AnalogInput.h" #include "frc/ErrorBase.h" -#include "frc/PIDSource.h" #include "frc/smartdashboard/Sendable.h" #include "frc/smartdashboard/SendableHelper.h" @@ -24,7 +23,6 @@ class SendableBuilder; * calibrated by finding the center value over a period of time. */ class AnalogAccelerometer : public ErrorBase, - public PIDSource, public Sendable, public SendableHelper { public: @@ -97,13 +95,6 @@ class AnalogAccelerometer : public ErrorBase, */ void SetZero(double zero); - /** - * Get the Acceleration for the PID Source parent. - * - * @return The current acceleration in Gs. - */ - double PIDGet() override; - void InitSendable(SendableBuilder& builder) override; private: diff --git a/wpilibc/src/main/native/include/frc/AnalogInput.h b/wpilibc/src/main/native/include/frc/AnalogInput.h index e9cb320ccc..7a9ebeeb6d 100644 --- a/wpilibc/src/main/native/include/frc/AnalogInput.h +++ b/wpilibc/src/main/native/include/frc/AnalogInput.h @@ -9,7 +9,6 @@ #include #include "frc/ErrorBase.h" -#include "frc/PIDSource.h" #include "frc/smartdashboard/Sendable.h" #include "frc/smartdashboard/SendableHelper.h" @@ -32,7 +31,6 @@ class DMASample; * stable values. */ class AnalogInput : public ErrorBase, - public PIDSource, public Sendable, public SendableHelper { friend class AnalogTrigger; @@ -280,13 +278,6 @@ class AnalogInput : public ErrorBase, */ static double GetSampleRate(); - /** - * Get the Average value for the PID Source base object. - * - * @return The average voltage. - */ - double PIDGet() override; - /** * Indicates this input is used by a simulated device. * diff --git a/wpilibc/src/main/native/include/frc/AnalogPotentiometer.h b/wpilibc/src/main/native/include/frc/AnalogPotentiometer.h index f34e34f385..1f26242158 100644 --- a/wpilibc/src/main/native/include/frc/AnalogPotentiometer.h +++ b/wpilibc/src/main/native/include/frc/AnalogPotentiometer.h @@ -107,13 +107,6 @@ class AnalogPotentiometer : public ErrorBase, */ double Get() const override; - /** - * Implement the PIDSource interface. - * - * @return The current reading. - */ - double PIDGet() override; - void InitSendable(SendableBuilder& builder) override; private: diff --git a/wpilibc/src/main/native/include/frc/Encoder.h b/wpilibc/src/main/native/include/frc/Encoder.h index 973e995434..db96187d6a 100644 --- a/wpilibc/src/main/native/include/frc/Encoder.h +++ b/wpilibc/src/main/native/include/frc/Encoder.h @@ -11,7 +11,6 @@ #include "frc/Counter.h" #include "frc/CounterBase.h" #include "frc/ErrorBase.h" -#include "frc/PIDSource.h" #include "frc/smartdashboard/Sendable.h" #include "frc/smartdashboard/SendableHelper.h" @@ -40,7 +39,6 @@ class DMASample; */ class Encoder : public ErrorBase, public CounterBase, - public PIDSource, public Sendable, public SendableHelper { friend class DMA; @@ -314,8 +312,6 @@ class Encoder : public ErrorBase, */ int GetSamplesToAverage() const; - double PIDGet() override; - /** * Set the index source for the encoder. * diff --git a/wpilibc/src/main/native/include/frc/GyroBase.h b/wpilibc/src/main/native/include/frc/GyroBase.h index cec8a6f4a8..004d146874 100644 --- a/wpilibc/src/main/native/include/frc/GyroBase.h +++ b/wpilibc/src/main/native/include/frc/GyroBase.h @@ -5,7 +5,6 @@ #pragma once #include "frc/ErrorBase.h" -#include "frc/PIDSource.h" #include "frc/interfaces/Gyro.h" #include "frc/smartdashboard/Sendable.h" #include "frc/smartdashboard/SendableHelper.h" @@ -18,7 +17,6 @@ namespace frc { */ class GyroBase : public Gyro, public ErrorBase, - public PIDSource, public Sendable, public SendableHelper { public: @@ -26,15 +24,6 @@ class GyroBase : public Gyro, GyroBase(GyroBase&&) = default; GyroBase& operator=(GyroBase&&) = default; - // PIDSource interface - /** - * Get the PIDOutput for the PIDSource base object. Can be set to return - * angle or rate using SetPIDSourceType(). Defaults to angle. - * - * @return The PIDOutput (angle or rate, defaults to angle) - */ - double PIDGet() override; - void InitSendable(SendableBuilder& builder) override; }; diff --git a/wpilibc/src/main/native/include/frc/NidecBrushless.h b/wpilibc/src/main/native/include/frc/NidecBrushless.h index 3f12d6235d..5e1becaae6 100644 --- a/wpilibc/src/main/native/include/frc/NidecBrushless.h +++ b/wpilibc/src/main/native/include/frc/NidecBrushless.h @@ -73,14 +73,6 @@ class NidecBrushless : public SpeedController, */ void Enable(); - // PIDOutput interface - /** - * Write out the PID value as seen in the PIDOutput base object. - * - * @param output Write out the PWM value as was found in the PIDController - */ - void PIDWrite(double output) override; - // MotorSafety interface void StopMotor() override; void GetDescription(wpi::raw_ostream& desc) const override; diff --git a/wpilibc/src/main/native/include/frc/PWMSpeedController.h b/wpilibc/src/main/native/include/frc/PWMSpeedController.h index 9d6f65af3c..786855e756 100644 --- a/wpilibc/src/main/native/include/frc/PWMSpeedController.h +++ b/wpilibc/src/main/native/include/frc/PWMSpeedController.h @@ -60,13 +60,6 @@ class PWMSpeedController : public SpeedController, int GetChannel() const; - /** - * Write out the PID value as seen in the PIDOutput base object. - * - * @param output Write out the PWM value as was found in the PIDController - */ - void PIDWrite(double output) override; - protected: /** * Constructor for a PWM Speed Controller connected via PWM. diff --git a/wpilibc/src/main/native/include/frc/SpeedController.h b/wpilibc/src/main/native/include/frc/SpeedController.h index 23e0d7b0ab..fcb5de9b68 100644 --- a/wpilibc/src/main/native/include/frc/SpeedController.h +++ b/wpilibc/src/main/native/include/frc/SpeedController.h @@ -6,14 +6,12 @@ #include -#include "frc/PIDOutput.h" - namespace frc { /** * Interface for speed controlling devices. */ -class SpeedController : public PIDOutput { +class SpeedController { public: virtual ~SpeedController() = default; diff --git a/wpilibc/src/main/native/include/frc/SpeedControllerGroup.h b/wpilibc/src/main/native/include/frc/SpeedControllerGroup.h index f7153c64fe..2b621109b4 100644 --- a/wpilibc/src/main/native/include/frc/SpeedControllerGroup.h +++ b/wpilibc/src/main/native/include/frc/SpeedControllerGroup.h @@ -32,7 +32,6 @@ class SpeedControllerGroup : public Sendable, bool GetInverted() const override; void Disable() override; void StopMotor() override; - void PIDWrite(double output) override; void InitSendable(SendableBuilder& builder) override; diff --git a/wpilibc/src/main/native/include/frc/Ultrasonic.h b/wpilibc/src/main/native/include/frc/Ultrasonic.h index 71afd73659..78afc97be5 100644 --- a/wpilibc/src/main/native/include/frc/Ultrasonic.h +++ b/wpilibc/src/main/native/include/frc/Ultrasonic.h @@ -13,7 +13,6 @@ #include "frc/Counter.h" #include "frc/ErrorBase.h" -#include "frc/PIDSource.h" #include "frc/smartdashboard/Sendable.h" #include "frc/smartdashboard/SendableHelper.h" @@ -36,11 +35,8 @@ class DigitalOutput; */ class Ultrasonic : public ErrorBase, public Sendable, - public PIDSource, public SendableHelper { public: - enum DistanceUnit { kInches = 0, kMilliMeters = 1 }; - /** * Create an instance of the Ultrasonic Sensor. * @@ -51,9 +47,8 @@ class Ultrasonic : public ErrorBase, * @param echoChannel The digital input channel that receives the echo. The * length of time that the echo is high represents the * round trip time of the ping, and the distance. - * @param units The units returned in either kInches or kMilliMeters */ - Ultrasonic(int pingChannel, int echoChannel, DistanceUnit units = kInches); + Ultrasonic(int pingChannel, int echoChannel); /** * Create an instance of an Ultrasonic Sensor from a DigitalInput for the echo @@ -63,10 +58,8 @@ class Ultrasonic : public ErrorBase, * ping. Requires a 10uS pulse to start. * @param echoChannel The digital input object that times the return pulse to * determine the range. - * @param units The units returned in either kInches or kMilliMeters */ - Ultrasonic(DigitalOutput* pingChannel, DigitalInput* echoChannel, - DistanceUnit units = kInches); + Ultrasonic(DigitalOutput* pingChannel, DigitalInput* echoChannel); /** * Create an instance of an Ultrasonic Sensor from a DigitalInput for the echo @@ -76,10 +69,8 @@ class Ultrasonic : public ErrorBase, * ping. Requires a 10uS pulse to start. * @param echoChannel The digital input object that times the return pulse to * determine the range. - * @param units The units returned in either kInches or kMilliMeters */ - Ultrasonic(DigitalOutput& pingChannel, DigitalInput& echoChannel, - DistanceUnit units = kInches); + Ultrasonic(DigitalOutput& pingChannel, DigitalInput& echoChannel); /** * Create an instance of an Ultrasonic Sensor from a DigitalInput for the echo @@ -89,11 +80,9 @@ class Ultrasonic : public ErrorBase, * ping. Requires a 10uS pulse to start. * @param echoChannel The digital input object that times the return pulse to * determine the range. - * @param units The units returned in either kInches or kMilliMeters */ Ultrasonic(std::shared_ptr pingChannel, - std::shared_ptr echoChannel, - DistanceUnit units = kInches); + std::shared_ptr echoChannel); ~Ultrasonic() override; @@ -156,30 +145,6 @@ class Ultrasonic : public ErrorBase, void SetEnabled(bool enable); - /** - * Set the current DistanceUnit that should be used for the PIDSource base - * object. - * - * @param units The DistanceUnit that should be used. - */ - void SetDistanceUnits(DistanceUnit units); - - /** - * Get the current DistanceUnit that is used for the PIDSource base object. - * - * @return The type of DistanceUnit that is being used. - */ - DistanceUnit GetDistanceUnits() const; - - /** - * Get the range in the current DistanceUnit for the PIDSource base object. - * - * @return The range in DistanceUnit - */ - double PIDGet() override; - - void SetPIDSourceType(PIDSourceType pidSource) override; - void InitSendable(SendableBuilder& builder) override; private: @@ -228,7 +193,6 @@ class Ultrasonic : public ErrorBase, std::shared_ptr m_echoChannel; bool m_enabled = false; Counter m_counter; - DistanceUnit m_units; hal::SimDevice m_simDevice; hal::SimBoolean m_simRangeValid; diff --git a/wpilibc/src/main/native/include/frc/controller/PIDController.h b/wpilibc/src/main/native/include/frc/controller/PIDController.h index ba6aa4080b..29d9ad4f06 100644 --- a/wpilibc/src/main/native/include/frc/controller/PIDController.h +++ b/wpilibc/src/main/native/include/frc/controller/PIDController.h @@ -239,3 +239,9 @@ class PIDController : public frc::Sendable, }; } // namespace frc2 + +namespace frc { + +using frc2::PIDController; + +} // namespace frc diff --git a/wpilibc/src/main/native/include/frc/filters/Filter.h b/wpilibc/src/main/native/include/frc/filters/Filter.h deleted file mode 100644 index 9ec3efb3dd..0000000000 --- a/wpilibc/src/main/native/include/frc/filters/Filter.h +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright (c) FIRST and other WPILib contributors. -// Open Source Software; you can modify and/or share it under the terms of -// the WPILib BSD license file in the root directory of this project. - -#pragma once - -#include - -#include - -#include "frc/PIDSource.h" - -namespace frc { - -/** - * Interface for filters - * - * @deprecated only used by the deprecated LinearDigitalFilter - */ -class Filter : public PIDSource { - public: - WPI_DEPRECATED("This class is no longer used.") - explicit Filter(PIDSource& source); - WPI_DEPRECATED("This class is no longer used.") - explicit Filter(std::shared_ptr source); - ~Filter() override = default; - - Filter(Filter&&) = default; - Filter& operator=(Filter&&) = default; - - // PIDSource interface - void SetPIDSourceType(PIDSourceType pidSource) override; - PIDSourceType GetPIDSourceType() const override; - double PIDGet() override = 0; - - /** - * Returns the current filter estimate without also inserting new data as - * PIDGet() would do. - * - * @return The current filter estimate - */ - virtual double Get() const = 0; - - /** - * Reset the filter state - */ - virtual void Reset() = 0; - - protected: - /** - * Calls PIDGet() of source - * - * @return Current value of source - */ - double PIDGetSource(); - - private: - std::shared_ptr m_source; -}; - -} // namespace frc diff --git a/wpilibc/src/main/native/include/frc/filters/LinearDigitalFilter.h b/wpilibc/src/main/native/include/frc/filters/LinearDigitalFilter.h deleted file mode 100644 index e26a79bc23..0000000000 --- a/wpilibc/src/main/native/include/frc/filters/LinearDigitalFilter.h +++ /dev/null @@ -1,223 +0,0 @@ -// Copyright (c) FIRST and other WPILib contributors. -// Open Source Software; you can modify and/or share it under the terms of -// the WPILib BSD license file in the root directory of this project. - -#pragma once - -#include -#include -#include - -#include -#include -#include - -#include "frc/filters/Filter.h" - -namespace frc { - -/** - * This class implements a linear, digital filter. All types of FIR and IIR - * filters are supported. Static factory methods are provided to create commonly - * used types of filters. - * - * Filters are of the form:
- * y[n] = (b0 * x[n] + b1 * x[n-1] + ... + bP * x[n-P]) - - * (a0 * y[n-1] + a2 * y[n-2] + ... + aQ * y[n-Q]) - * - * Where:
- * y[n] is the output at time "n"
- * x[n] is the input at time "n"
- * y[n-1] is the output from the LAST time step ("n-1")
- * x[n-1] is the input from the LAST time step ("n-1")
- * b0...bP are the "feedforward" (FIR) gains
- * a0...aQ are the "feedback" (IIR) gains
- * IMPORTANT! Note the "-" sign in front of the feedback term! This is a common - * convention in signal processing. - * - * What can linear filters do? Basically, they can filter, or diminish, the - * effects of undesirable input frequencies. High frequencies, or rapid changes, - * can be indicative of sensor noise or be otherwise undesirable. A "low pass" - * filter smooths out the signal, reducing the impact of these high frequency - * components. Likewise, a "high pass" filter gets rid of slow-moving signal - * components, letting you detect large changes more easily. - * - * Example FRC applications of filters: - * - Getting rid of noise from an analog sensor input (note: the roboRIO's FPGA - * can do this faster in hardware) - * - Smoothing out joystick input to prevent the wheels from slipping or the - * robot from tipping - * - Smoothing motor commands so that unnecessary strain isn't put on - * electrical or mechanical components - * - If you use clever gains, you can make a PID controller out of this class! - * - * For more on filters, I highly recommend the following articles:
- * http://en.wikipedia.org/wiki/Linear_filter
- * http://en.wikipedia.org/wiki/Iir_filter
- * http://en.wikipedia.org/wiki/Fir_filter
- * - * Note 1: PIDGet() should be called by the user on a known, regular period. - * You can set up a Notifier to do this (look at the WPILib PIDController - * class), or do it "inline" with code in a periodic function. - * - * Note 2: For ALL filters, gains are necessarily a function of frequency. If - * you make a filter that works well for you at, say, 100Hz, you will most - * definitely need to adjust the gains if you then want to run it at 200Hz! - * Combining this with Note 1 - the impetus is on YOU as a developer to make - * sure PIDGet() gets called at the desired, constant frequency! - * - * @deprecated Use LinearFilter class instead - */ -class LinearDigitalFilter : public Filter { - public: - /** - * Create a linear FIR or IIR filter. - * - * @param source The PIDSource object that is used to get values - * @param ffGains The "feed forward" or FIR gains - * @param fbGains The "feed back" or IIR gains - */ - WPI_DEPRECATED("Use LinearFilter class instead.") - LinearDigitalFilter(PIDSource& source, wpi::ArrayRef ffGains, - wpi::ArrayRef fbGains); - - /** - * Create a linear FIR or IIR filter. - * - * @param source The PIDSource object that is used to get values - * @param ffGains The "feed forward" or FIR gains - * @param fbGains The "feed back" or IIR gains - */ - WPI_DEPRECATED("Use LinearFilter class instead.") - LinearDigitalFilter(PIDSource& source, std::initializer_list ffGains, - std::initializer_list fbGains); - - /** - * Create a linear FIR or IIR filter. - * - * @param source The PIDSource object that is used to get values - * @param ffGains The "feed forward" or FIR gains - * @param fbGains The "feed back" or IIR gains - */ - WPI_DEPRECATED("Use LinearFilter class instead.") - LinearDigitalFilter(std::shared_ptr source, - wpi::ArrayRef ffGains, - wpi::ArrayRef fbGains); - - /** - * Create a linear FIR or IIR filter. - * - * @param source The PIDSource object that is used to get values - * @param ffGains The "feed forward" or FIR gains - * @param fbGains The "feed back" or IIR gains - */ - WPI_DEPRECATED("Use LinearFilter class instead.") - LinearDigitalFilter(std::shared_ptr source, - std::initializer_list ffGains, - std::initializer_list fbGains); - - LinearDigitalFilter(LinearDigitalFilter&&) = default; - LinearDigitalFilter& operator=(LinearDigitalFilter&&) = default; - - // Static methods to create commonly used filters - /** - * Creates a one-pole IIR low-pass filter of the form:
- * y[n] = (1 - gain) * x[n] + gain * y[n-1]
- * where gain = e-dt / T, T is the time constant in seconds - * - * This filter is stable for time constants greater than zero. - * - * @param source The PIDSource object that is used to get values - * @param timeConstant The discrete-time time constant in seconds - * @param period The period in seconds between samples taken by the user - */ - static LinearDigitalFilter SinglePoleIIR(PIDSource& source, - double timeConstant, double period); - - /** - * Creates a first-order high-pass filter of the form:
- * y[n] = gain * x[n] + (-gain) * x[n-1] + gain * y[n-1]
- * where gain = e-dt / T, T is the time constant in seconds - * - * This filter is stable for time constants greater than zero. - * - * @param source The PIDSource object that is used to get values - * @param timeConstant The discrete-time time constant in seconds - * @param period The period in seconds between samples taken by the user - */ - static LinearDigitalFilter HighPass(PIDSource& source, double timeConstant, - double period); - - /** - * Creates a K-tap FIR moving average filter of the form:
- * y[n] = 1/k * (x[k] + x[k-1] + … + x[0]) - * - * This filter is always stable. - * - * @param source The PIDSource object that is used to get values - * @param taps The number of samples to average over. Higher = smoother but - * slower - */ - static LinearDigitalFilter MovingAverage(PIDSource& source, int taps); - - /** - * Creates a one-pole IIR low-pass filter of the form:
- * y[n] = (1 - gain) * x[n] + gain * y[n-1]
- * where gain = e-dt / T, T is the time constant in seconds - * - * This filter is stable for time constants greater than zero. - * - * @param source The PIDSource object that is used to get values - * @param timeConstant The discrete-time time constant in seconds - * @param period The period in seconds between samples taken by the user - */ - static LinearDigitalFilter SinglePoleIIR(std::shared_ptr source, - double timeConstant, double period); - - /** - * Creates a first-order high-pass filter of the form:
- * y[n] = gain * x[n] + (-gain) * x[n-1] + gain * y[n-1]
- * where gain = e-dt / T, T is the time constant in seconds - * - * This filter is stable for time constants greater than zero. - * - * @param source The PIDSource object that is used to get values - * @param timeConstant The discrete-time time constant in seconds - * @param period The period in seconds between samples taken by the user - */ - static LinearDigitalFilter HighPass(std::shared_ptr source, - double timeConstant, double period); - - /** - * Creates a K-tap FIR moving average filter of the form:
- * y[n] = 1/k * (x[k] + x[k-1] + … + x[0]) - * - * This filter is always stable. - * - * @param source The PIDSource object that is used to get values - * @param taps The number of samples to average over. Higher = smoother but - * slower - */ - static LinearDigitalFilter MovingAverage(std::shared_ptr source, - int taps); - - // Filter interface - double Get() const override; - void Reset() override; - - // PIDSource interface - /** - * Calculates the next value of the filter - * - * @return The filtered value at this step - */ - double PIDGet() override; - - private: - wpi::circular_buffer m_inputs; - wpi::circular_buffer m_outputs; - std::vector m_inputGains; - std::vector m_outputGains; -}; - -} // namespace frc diff --git a/wpilibc/src/main/native/include/frc/interfaces/Potentiometer.h b/wpilibc/src/main/native/include/frc/interfaces/Potentiometer.h index f3392c9b03..ac3125c9f5 100644 --- a/wpilibc/src/main/native/include/frc/interfaces/Potentiometer.h +++ b/wpilibc/src/main/native/include/frc/interfaces/Potentiometer.h @@ -4,17 +4,15 @@ #pragma once -#include "frc/PIDSource.h" - namespace frc { /** * Interface for potentiometers. */ -class Potentiometer : public PIDSource { +class Potentiometer { public: Potentiometer() = default; - ~Potentiometer() override = default; + virtual ~Potentiometer() = default; Potentiometer(Potentiometer&&) = default; Potentiometer& operator=(Potentiometer&&) = default; @@ -25,8 +23,6 @@ class Potentiometer : public PIDSource { * @return The current set speed. Value is between -1.0 and 1.0. */ virtual double Get() const = 0; - - void SetPIDSourceType(PIDSourceType pidSource) override; }; } // namespace frc diff --git a/wpilibc/src/test/native/cpp/MockSpeedController.cpp b/wpilibc/src/test/native/cpp/MockSpeedController.cpp index f4d3459f8b..31ccb075fd 100644 --- a/wpilibc/src/test/native/cpp/MockSpeedController.cpp +++ b/wpilibc/src/test/native/cpp/MockSpeedController.cpp @@ -29,7 +29,3 @@ void MockSpeedController::Disable() { void MockSpeedController::StopMotor() { Disable(); } - -void MockSpeedController::PIDWrite(double output) { - Set(output); -} diff --git a/wpilibc/src/test/native/cpp/SpeedControllerGroupTest.cpp b/wpilibc/src/test/native/cpp/SpeedControllerGroupTest.cpp index 597d228644..0f62530db5 100644 --- a/wpilibc/src/test/native/cpp/SpeedControllerGroupTest.cpp +++ b/wpilibc/src/test/native/cpp/SpeedControllerGroupTest.cpp @@ -121,13 +121,5 @@ TEST_P(SpeedControllerGroupTest, StopMotor) { } } -TEST_P(SpeedControllerGroupTest, PIDWrite) { - m_group->PIDWrite(1.0); - - for (auto& speedController : m_speedControllers) { - EXPECT_FLOAT_EQ(speedController.Get(), 1.0); - } -} - INSTANTIATE_TEST_SUITE_P(Test, SpeedControllerGroupTest, testing::Values(TEST_ONE, TEST_TWO, TEST_THREE)); diff --git a/wpilibc/src/test/native/include/MockSpeedController.h b/wpilibc/src/test/native/include/MockSpeedController.h index ff0cea8e58..a5e119bae9 100644 --- a/wpilibc/src/test/native/include/MockSpeedController.h +++ b/wpilibc/src/test/native/include/MockSpeedController.h @@ -17,8 +17,6 @@ class MockSpeedController : public SpeedController { void Disable() override; void StopMotor() override; - void PIDWrite(double output) override; - private: double m_speed = 0.0; bool m_isInverted = false; diff --git a/wpilibcExamples/src/main/cpp/examples/ArmBotOffboard/include/ExampleSmartMotorController.h b/wpilibcExamples/src/main/cpp/examples/ArmBotOffboard/include/ExampleSmartMotorController.h index cb2c748550..9d7e5e54e8 100644 --- a/wpilibcExamples/src/main/cpp/examples/ArmBotOffboard/include/ExampleSmartMotorController.h +++ b/wpilibcExamples/src/main/cpp/examples/ArmBotOffboard/include/ExampleSmartMotorController.h @@ -79,6 +79,4 @@ class ExampleSmartMotorController : public frc::SpeedController { void Disable() override {} void StopMotor() override {} - - void PIDWrite(double output) override {} }; diff --git a/wpilibcExamples/src/main/cpp/examples/DriveDistanceOffboard/include/ExampleSmartMotorController.h b/wpilibcExamples/src/main/cpp/examples/DriveDistanceOffboard/include/ExampleSmartMotorController.h index 46720e1f81..5e5b1d53cd 100644 --- a/wpilibcExamples/src/main/cpp/examples/DriveDistanceOffboard/include/ExampleSmartMotorController.h +++ b/wpilibcExamples/src/main/cpp/examples/DriveDistanceOffboard/include/ExampleSmartMotorController.h @@ -80,8 +80,6 @@ class ExampleSmartMotorController : public frc::SpeedController { void StopMotor() override {} - void PIDWrite(double output) override {} - private: double m_value = 0.0; }; diff --git a/wpilibcExamples/src/main/cpp/examples/ElevatorTrapezoidProfile/include/ExampleSmartMotorController.h b/wpilibcExamples/src/main/cpp/examples/ElevatorTrapezoidProfile/include/ExampleSmartMotorController.h index cb2c748550..9d7e5e54e8 100644 --- a/wpilibcExamples/src/main/cpp/examples/ElevatorTrapezoidProfile/include/ExampleSmartMotorController.h +++ b/wpilibcExamples/src/main/cpp/examples/ElevatorTrapezoidProfile/include/ExampleSmartMotorController.h @@ -79,6 +79,4 @@ class ExampleSmartMotorController : public frc::SpeedController { void Disable() override {} void StopMotor() override {} - - void PIDWrite(double output) override {} }; diff --git a/wpilibcExamples/src/main/cpp/examples/PacGoat/cpp/subsystems/DriveTrain.cpp b/wpilibcExamples/src/main/cpp/examples/PacGoat/cpp/subsystems/DriveTrain.cpp index e39972384d..e9c878a3e6 100644 --- a/wpilibcExamples/src/main/cpp/examples/PacGoat/cpp/subsystems/DriveTrain.cpp +++ b/wpilibcExamples/src/main/cpp/examples/PacGoat/cpp/subsystems/DriveTrain.cpp @@ -24,10 +24,6 @@ DriveTrain::DriveTrain() : frc::Subsystem("DriveTrain") { m_leftCIMs.SetInverted(true); m_rightCIMs.SetInverted(true); - // Configure encoders - m_rightEncoder.SetPIDSourceType(frc::PIDSourceType::kDisplacement); - m_leftEncoder.SetPIDSourceType(frc::PIDSourceType::kDisplacement); - #ifndef SIMULATION // Converts to feet m_rightEncoder.SetDistancePerPulse(0.0785398); diff --git a/wpilibcExamples/src/main/cpp/examples/PacGoat/cpp/subsystems/Pivot.cpp b/wpilibcExamples/src/main/cpp/examples/PacGoat/cpp/subsystems/Pivot.cpp index c8cf025619..ba6f1a6d01 100644 --- a/wpilibcExamples/src/main/cpp/examples/PacGoat/cpp/subsystems/Pivot.cpp +++ b/wpilibcExamples/src/main/cpp/examples/PacGoat/cpp/subsystems/Pivot.cpp @@ -27,7 +27,7 @@ double Pivot::ReturnPIDInput() { } void Pivot::UsePIDOutput(double output) { - m_motor.PIDWrite(output); + m_motor.Set(output); } bool Pivot::IsAtUpperLimit() { diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/AnalogAccelerometer.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/AnalogAccelerometer.java index 960518cb63..b0395b859a 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/AnalogAccelerometer.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/AnalogAccelerometer.java @@ -16,12 +16,11 @@ import edu.wpi.first.wpilibj.smartdashboard.SendableRegistry; * through the sensor. Many sensors have multiple axis and can be treated as multiple devices. Each * is calibrated by finding the center value over a period of time. */ -public class AnalogAccelerometer implements PIDSource, Sendable, AutoCloseable { +public class AnalogAccelerometer implements Sendable, AutoCloseable { private AnalogInput m_analogChannel; private double m_voltsPerG = 1.0; private double m_zeroGVoltage = 2.5; private final boolean m_allocatedChannel; - protected PIDSourceType m_pidSource = PIDSourceType.kDisplacement; /** Common initialization. */ private void initAccelerometer() { @@ -108,26 +107,6 @@ public class AnalogAccelerometer implements PIDSource, Sendable, AutoCloseable { m_zeroGVoltage = zero; } - @Override - public void setPIDSourceType(PIDSourceType pidSource) { - m_pidSource = pidSource; - } - - @Override - public PIDSourceType getPIDSourceType() { - return m_pidSource; - } - - /** - * Get the Acceleration for the PID Source parent. - * - * @return The current acceleration in Gs. - */ - @Override - public double pidGet() { - return getAcceleration(); - } - @Override public void initSendable(SendableBuilder builder) { builder.setSmartDashboardType("Accelerometer"); diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/AnalogInput.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/AnalogInput.java index ccec9604c0..428801ead6 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/AnalogInput.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/AnalogInput.java @@ -25,13 +25,12 @@ import edu.wpi.first.wpilibj.smartdashboard.SendableRegistry; * accumulated effectively increasing the resolution, while the averaged samples are divided by the * number of samples to retain the resolution, but get more stable values. */ -public class AnalogInput implements PIDSource, Sendable, AutoCloseable { +public class AnalogInput implements Sendable, AutoCloseable { private static final int kAccumulatorSlot = 1; int m_port; // explicit no modifier, private and package accessible. private int m_channel; private static final int[] kAccumulatorChannels = {0, 1}; private long m_accumulatorOffset; - protected PIDSourceType m_pidSource = PIDSourceType.kDisplacement; /** * Construct an analog channel. @@ -322,26 +321,6 @@ public class AnalogInput implements PIDSource, Sendable, AutoCloseable { return AnalogJNI.getAnalogSampleRate(); } - @Override - public void setPIDSourceType(PIDSourceType pidSource) { - m_pidSource = pidSource; - } - - @Override - public PIDSourceType getPIDSourceType() { - return m_pidSource; - } - - /** - * Get the average voltage for use with PIDController. - * - * @return the average voltage - */ - @Override - public double pidGet() { - return getAverageVoltage(); - } - /** * Indicates this input is used by a simulated device. * diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/AnalogPotentiometer.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/AnalogPotentiometer.java index 34ea6d1ac9..9c74f5e216 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/AnalogPotentiometer.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/AnalogPotentiometer.java @@ -18,7 +18,6 @@ public class AnalogPotentiometer implements Potentiometer, Sendable, AutoCloseab private boolean m_initAnalogInput; private double m_fullRange; private double m_offset; - protected PIDSourceType m_pidSource = PIDSourceType.kDisplacement; /** * AnalogPotentiometer constructor. @@ -129,29 +128,6 @@ public class AnalogPotentiometer implements Potentiometer, Sendable, AutoCloseab + m_offset; } - @Override - public void setPIDSourceType(PIDSourceType pidSource) { - if (!pidSource.equals(PIDSourceType.kDisplacement)) { - throw new IllegalArgumentException("Only displacement PID is allowed for potentiometers."); - } - m_pidSource = pidSource; - } - - @Override - public PIDSourceType getPIDSourceType() { - return m_pidSource; - } - - /** - * Implement the PIDSource interface. - * - * @return The current reading. - */ - @Override - public double pidGet() { - return get(); - } - @Override public void initSendable(SendableBuilder builder) { if (m_analogInput != null) { diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/Counter.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/Counter.java index 7cc082192f..5ed499497a 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/Counter.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/Counter.java @@ -26,7 +26,7 @@ import java.nio.ByteOrder; *

All counters will immediately start counting - reset() them if you need them to be zeroed * before use. */ -public class Counter implements CounterBase, PIDSource, Sendable, AutoCloseable { +public class Counter implements CounterBase, Sendable, AutoCloseable { /** Mode determines how and what the counter counts. */ public enum Mode { /** mode: two pulse. */ @@ -51,7 +51,6 @@ public class Counter implements CounterBase, PIDSource, Sendable, AutoCloseable private boolean m_allocatedDownSource; private int m_counter; // /< The FPGA counter object. private int m_index; // /< The index of this counter. - private PIDSourceType m_pidSource; private double m_distancePerPulse; // distance of travel for each tick /** Create an instance of a counter with the given mode. */ @@ -507,39 +506,6 @@ public class Counter implements CounterBase, PIDSource, Sendable, AutoCloseable m_distancePerPulse = distancePerPulse; } - /** - * Set which parameter of the encoder you are using as a process control variable. The counter - * class supports the rate and distance parameters. - * - * @param pidSource An enum to select the parameter. - */ - @Override - public void setPIDSourceType(PIDSourceType pidSource) { - requireNonNullParam(pidSource, "pidSource", "setPIDSourceType"); - if (pidSource != PIDSourceType.kDisplacement && pidSource != PIDSourceType.kRate) { - throw new IllegalArgumentException("PID Source parameter was not valid type: " + pidSource); - } - - m_pidSource = pidSource; - } - - @Override - public PIDSourceType getPIDSourceType() { - return m_pidSource; - } - - @Override - public double pidGet() { - switch (m_pidSource) { - case kDisplacement: - return getDistance(); - case kRate: - return getRate(); - default: - return 0.0; - } - } - @Override public void initSendable(SendableBuilder builder) { builder.setSmartDashboardType("Counter"); diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/Encoder.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/Encoder.java index 019eaa5d93..2c9a204590 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/Encoder.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/Encoder.java @@ -27,7 +27,7 @@ import edu.wpi.first.wpilibj.smartdashboard.SendableRegistry; *

All encoders will immediately start counting - reset() them if you need them to be zeroed * before use. */ -public class Encoder implements CounterBase, PIDSource, Sendable, AutoCloseable { +public class Encoder implements CounterBase, Sendable, AutoCloseable { public enum IndexingType { kResetWhileHigh(0), kResetWhileLow(1), @@ -51,7 +51,6 @@ public class Encoder implements CounterBase, PIDSource, Sendable, AutoCloseable private boolean m_allocatedA; private boolean m_allocatedB; private boolean m_allocatedI; - private PIDSourceType m_pidSource; private int m_encoder; // the HAL encoder object @@ -73,8 +72,6 @@ public class Encoder implements CounterBase, PIDSource, Sendable, AutoCloseable reverseDirection, type.value); - m_pidSource = PIDSourceType.kDisplacement; - int fpgaIndex = getFPGAIndex(); HAL.report(tResourceType.kResourceType_Encoder, fpgaIndex + 1, type.value + 1); SendableRegistry.addLW(this, "Encoder", fpgaIndex); @@ -481,39 +478,6 @@ public class Encoder implements CounterBase, PIDSource, Sendable, AutoCloseable return EncoderJNI.getEncoderSamplesToAverage(m_encoder); } - /** - * Set which parameter of the encoder you are using as a process control variable. The encoder - * class supports the rate and distance parameters. - * - * @param pidSource An enum to select the parameter. - */ - @Override - public void setPIDSourceType(PIDSourceType pidSource) { - m_pidSource = pidSource; - } - - @Override - public PIDSourceType getPIDSourceType() { - return m_pidSource; - } - - /** - * Implement the PIDSource interface. - * - * @return The current value of the selected source parameter. - */ - @Override - public double pidGet() { - switch (m_pidSource) { - case kDisplacement: - return getDistance(); - case kRate: - return getRate(); - default: - return 0.0; - } - } - /** * Set the index source for the encoder. When this source is activated, the encoder count * automatically resets. diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/GyroBase.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/GyroBase.java index 4b180d5c92..8bc1076541 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/GyroBase.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/GyroBase.java @@ -8,43 +8,7 @@ import edu.wpi.first.wpilibj.interfaces.Gyro; import edu.wpi.first.wpilibj.smartdashboard.SendableBuilder; /** GyroBase is the common base class for Gyro implementations such as AnalogGyro. */ -public abstract class GyroBase implements Gyro, PIDSource, Sendable { - private PIDSourceType m_pidSource = PIDSourceType.kDisplacement; - - /** - * Set which parameter of the gyro you are using as a process control variable. The Gyro class - * supports the rate and displacement parameters - * - * @param pidSource An enum to select the parameter. - */ - @Override - public void setPIDSourceType(PIDSourceType pidSource) { - m_pidSource = pidSource; - } - - @Override - public PIDSourceType getPIDSourceType() { - return m_pidSource; - } - - /** - * Get the output of the gyro for use with PIDControllers. May be the angle or rate depending on - * the set PIDSourceType - * - * @return the output according to the gyro - */ - @Override - public double pidGet() { - switch (m_pidSource) { - case kRate: - return getRate(); - case kDisplacement: - return getAngle(); - default: - return 0.0; - } - } - +public abstract class GyroBase implements Gyro, Sendable { @Override public void initSendable(SendableBuilder builder) { builder.setSmartDashboardType("Gyro"); diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/NidecBrushless.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/NidecBrushless.java index c051d314b7..6a9d9e8b2d 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/NidecBrushless.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/NidecBrushless.java @@ -89,16 +89,6 @@ public class NidecBrushless extends MotorSafety return m_isInverted; } - /** - * Write out the PID value as seen in the PIDOutput base object. - * - * @param output Write out the PWM value as was found in the PIDController - */ - @Override - public void pidWrite(double output) { - set(output); - } - /** * Stop the motor. This is called by the MotorSafety object when it has a timeout for this PWM and * needs to stop it from running. Calling set() will re-enable the motor. diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/PWMSpeedController.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/PWMSpeedController.java index 2db8baf5fe..4a0f7e60f1 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/PWMSpeedController.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/PWMSpeedController.java @@ -92,16 +92,6 @@ public abstract class PWMSpeedController extends MotorSafety return m_pwm.getChannel(); } - /** - * Write out the PID value as seen in the PIDOutput base object. - * - * @param output Write out the PWM value as was found in the PIDController - */ - @Override - public void pidWrite(double output) { - set(output); - } - @Override public void initSendable(SendableBuilder builder) { builder.setSmartDashboardType("Speed Controller"); diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/SpeedController.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/SpeedController.java index f795ca77a0..7d03f477a0 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/SpeedController.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/SpeedController.java @@ -5,8 +5,7 @@ package edu.wpi.first.wpilibj; /** Interface for speed controlling devices. */ -@SuppressWarnings("removal") -public interface SpeedController extends PIDOutput { +public interface SpeedController { /** * Common interface for setting the speed of a speed controller. * diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/SpeedControllerGroup.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/SpeedControllerGroup.java index f8a9fb2c5e..e0a95c3079 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/SpeedControllerGroup.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/SpeedControllerGroup.java @@ -87,11 +87,6 @@ public class SpeedControllerGroup implements SpeedController, Sendable, AutoClos } } - @Override - public void pidWrite(double output) { - set(output); - } - @Override public void initSendable(SendableBuilder builder) { builder.setSmartDashboardType("Speed Controller"); diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/Ultrasonic.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/Ultrasonic.java index 81c1bcf534..346ddb9738 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/Ultrasonic.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/Ultrasonic.java @@ -26,15 +26,7 @@ import java.util.List; * echo is received. The time that the line is high determines the round trip distance (time of * flight). */ -public class Ultrasonic implements PIDSource, Sendable, AutoCloseable { - /** The units to return when PIDGet is called. */ - public enum Unit { - /** Use inches for PIDGet. */ - kInches, - /** Use millimeters for PIDGet. */ - kMillimeters - } - +public class Ultrasonic implements Sendable, AutoCloseable { // Time (sec) for the ping trigger pulse. private static final double kPingTime = 10 * 1e-6; private static final double kSpeedOfSoundInchesPerSec = 1130.0 * 12.0; @@ -44,14 +36,12 @@ public class Ultrasonic implements PIDSource, Sendable, AutoCloseable { private static volatile boolean m_automaticEnabled; private DigitalInput m_echoChannel; private DigitalOutput m_pingChannel; - private boolean m_allocatedChannels; + private final boolean m_allocatedChannels; private boolean m_enabled; private Counter m_counter; // task doing the round-robin automatic sensing private static Thread m_task; - private Unit m_units; private static int m_instances; - protected PIDSourceType m_pidSource = PIDSourceType.kDisplacement; private SimDevice m_simDevice; private SimBoolean m_simRangeValid; @@ -128,31 +118,16 @@ public class Ultrasonic implements PIDSource, Sendable, AutoCloseable { * sending the ping. * @param echoChannel The digital input channel that receives the echo. The length of time that * the echo is high represents the round trip time of the ping, and the distance. - * @param units The units returned in either kInches or kMilliMeters */ - public Ultrasonic(final int pingChannel, final int echoChannel, Unit units) { + public Ultrasonic(final int pingChannel, final int echoChannel) { m_pingChannel = new DigitalOutput(pingChannel); m_echoChannel = new DigitalInput(echoChannel); SendableRegistry.addChild(this, m_pingChannel); SendableRegistry.addChild(this, m_echoChannel); m_allocatedChannels = true; - m_units = units; initialize(); } - /** - * Create an instance of the Ultrasonic Sensor. This is designed to supchannel the Daventech SRF04 - * and Vex ultrasonic sensors. Default unit is inches. - * - * @param pingChannel The digital output channel that sends the pulse to initiate the sensor - * sending the ping. - * @param echoChannel The digital input channel that receives the echo. The length of time that - * the echo is high represents the round trip time of the ping, and the distance. - */ - public Ultrasonic(final int pingChannel, final int echoChannel) { - this(pingChannel, echoChannel, Unit.kInches); - } - /** * Create an instance of an Ultrasonic Sensor from a DigitalInput for the echo channel and a * DigitalOutput for the ping channel. @@ -160,31 +135,17 @@ public class Ultrasonic implements PIDSource, Sendable, AutoCloseable { * @param pingChannel The digital output object that starts the sensor doing a ping. Requires a * 10uS pulse to start. * @param echoChannel The digital input object that times the return pulse to determine the range. - * @param units The units returned in either kInches or kMilliMeters */ - public Ultrasonic(DigitalOutput pingChannel, DigitalInput echoChannel, Unit units) { + public Ultrasonic(DigitalOutput pingChannel, DigitalInput echoChannel) { requireNonNull(pingChannel, "Provided ping channel was null"); requireNonNull(echoChannel, "Provided echo channel was null"); m_allocatedChannels = false; m_pingChannel = pingChannel; m_echoChannel = echoChannel; - m_units = units; initialize(); } - /** - * Create an instance of an Ultrasonic Sensor from a DigitalInput for the echo channel and a - * DigitalOutput for the ping channel. Default unit is inches. - * - * @param pingChannel The digital output object that starts the sensor doing a ping. Requires a - * 10uS pulse to start. - * @param echoChannel The digital input object that times the return pulse to determine the range. - */ - public Ultrasonic(DigitalOutput pingChannel, DigitalInput echoChannel) { - this(pingChannel, echoChannel, Unit.kInches); - } - /** * Destructor for the ultrasonic sensor. Delete the instance of the ultrasonic sensor by freeing * the allocated digital channels. If the system was in automatic mode (round robin), then it is @@ -326,54 +287,6 @@ public class Ultrasonic implements PIDSource, Sendable, AutoCloseable { return getRangeInches() * 25.4; } - @Override - public void setPIDSourceType(PIDSourceType pidSource) { - if (!pidSource.equals(PIDSourceType.kDisplacement)) { - throw new IllegalArgumentException("Only displacement PID is allowed for ultrasonics."); - } - m_pidSource = pidSource; - } - - @Override - public PIDSourceType getPIDSourceType() { - return m_pidSource; - } - - /** - * Get the range in the current DistanceUnit for the PIDSource base object. - * - * @return The range in DistanceUnit - */ - @Override - public double pidGet() { - switch (m_units) { - case kInches: - return getRangeInches(); - case kMillimeters: - return getRangeMM(); - default: - return 0.0; - } - } - - /** - * Set the current DistanceUnit that should be used for the PIDSource base object. - * - * @param units The DistanceUnit that should be used. - */ - public void setDistanceUnits(Unit units) { - m_units = units; - } - - /** - * Get the current DistanceUnit that is used for the PIDSource base object. - * - * @return The type of DistanceUnit that is being used. - */ - public Unit getDistanceUnits() { - return m_units; - } - /** * Is the ultrasonic enabled. * diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/filters/Filter.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/filters/Filter.java deleted file mode 100644 index f80eb30896..0000000000 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/filters/Filter.java +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright (c) FIRST and other WPILib contributors. -// Open Source Software; you can modify and/or share it under the terms of -// the WPILib BSD license file in the root directory of this project. - -package edu.wpi.first.wpilibj.filters; - -import edu.wpi.first.wpilibj.PIDSource; -import edu.wpi.first.wpilibj.PIDSourceType; - -/** - * Superclass for filters. - * - * @deprecated This class is no longer used. - */ -@Deprecated(since = "2020", forRemoval = true) -public abstract class Filter implements PIDSource { - private final PIDSource m_source; - - public Filter(PIDSource source) { - m_source = source; - } - - @Override - public void setPIDSourceType(PIDSourceType pidSource) { - m_source.setPIDSourceType(pidSource); - } - - @Override - public PIDSourceType getPIDSourceType() { - return m_source.getPIDSourceType(); - } - - @Override - public abstract double pidGet(); - - /** - * Returns the current filter estimate without also inserting new data as pidGet() would do. - * - * @return The current filter estimate - */ - public abstract double get(); - - /** Reset the filter state. */ - public abstract void reset(); - - /** - * Calls PIDGet() of source. - * - * @return Current value of source - */ - protected double pidGetSource() { - return m_source.pidGet(); - } -} diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/filters/LinearDigitalFilter.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/filters/LinearDigitalFilter.java deleted file mode 100644 index 4499f4b47f..0000000000 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/filters/LinearDigitalFilter.java +++ /dev/null @@ -1,187 +0,0 @@ -// Copyright (c) FIRST and other WPILib contributors. -// Open Source Software; you can modify and/or share it under the terms of -// the WPILib BSD license file in the root directory of this project. - -package edu.wpi.first.wpilibj.filters; - -import edu.wpi.first.hal.FRCNetComm.tResourceType; -import edu.wpi.first.hal.HAL; -import edu.wpi.first.wpilibj.PIDSource; -import edu.wpi.first.wpiutil.CircularBuffer; -import java.util.Arrays; - -/** - * This class implements a linear, digital filter. All types of FIR and IIR filters are supported. - * Static factory methods are provided to create commonly used types of filters. - * - *

Filters are of the form: y[n] = (b0*x[n] + b1*x[n-1] + ... + bP*x[n-P]) - (a0*y[n-1] + - * a2*y[n-2] + ... + aQ*y[n-Q]) - * - *

Where: y[n] is the output at time "n" x[n] is the input at time "n" y[n-1] is the output from - * the LAST time step ("n-1") x[n-1] is the input from the LAST time step ("n-1") b0...bP are the - * "feedforward" (FIR) gains a0...aQ are the "feedback" (IIR) gains IMPORTANT! Note the "-" sign in - * front of the feedback term! This is a common convention in signal processing. - * - *

What can linear filters do? Basically, they can filter, or diminish, the effects of - * undesirable input frequencies. High frequencies, or rapid changes, can be indicative of sensor - * noise or be otherwise undesirable. A "low pass" filter smooths out the signal, reducing the - * impact of these high frequency components. Likewise, a "high pass" filter gets rid of slow-moving - * signal components, letting you detect large changes more easily. - * - *

Example FRC applications of filters: - Getting rid of noise from an analog sensor input (note: - * the roboRIO's FPGA can do this faster in hardware) - Smoothing out joystick input to prevent the - * wheels from slipping or the robot from tipping - Smoothing motor commands so that unnecessary - * strain isn't put on electrical or mechanical components - If you use clever gains, you can make a - * PID controller out of this class! - * - *

For more on filters, I highly recommend the following articles: http://en.wikipedia - * .org/wiki/Linear_filter http://en.wikipedia.org/wiki/Iir_filter http://en.wikipedia - * .org/wiki/Fir_filter - * - *

Note 1: PIDGet() should be called by the user on a known, regular period. You can set up a - * Notifier to do this (look at the WPILib PIDController class), or do it "inline" with code in a - * periodic function. - * - *

Note 2: For ALL filters, gains are necessarily a function of frequency. If you make a filter - * that works well for you at, say, 100Hz, you will most definitely need to adjust the gains if you - * then want to run it at 200Hz! Combining this with Note 1 - the impetus is on YOU as a developer - * to make sure PIDGet() gets called at the desired, constant frequency! - * - * @deprecated Use LinearFilter class instead. - */ -@Deprecated -public class LinearDigitalFilter extends Filter { - private static int instances; - - private final CircularBuffer m_inputs; - private final CircularBuffer m_outputs; - private final double[] m_inputGains; - private final double[] m_outputGains; - - /** - * Create a linear FIR or IIR filter. - * - * @param source The PIDSource object that is used to get values - * @param ffGains The "feed forward" or FIR gains - * @param fbGains The "feed back" or IIR gains - */ - public LinearDigitalFilter(PIDSource source, double[] ffGains, double[] fbGains) { - super(source); - m_inputs = new CircularBuffer(ffGains.length); - m_outputs = new CircularBuffer(fbGains.length); - m_inputGains = Arrays.copyOf(ffGains, ffGains.length); - m_outputGains = Arrays.copyOf(fbGains, fbGains.length); - - instances++; - HAL.report(tResourceType.kResourceType_LinearFilter, instances); - } - - /** - * Creates a one-pole IIR low-pass filter of the form: y[n] = (1-gain)*x[n] + gain*y[n-1] where - * gain = e^(-dt / T), T is the time constant in seconds. - * - *

This filter is stable for time constants greater than zero. - * - * @param source The PIDSource object that is used to get values - * @param timeConstant The discrete-time time constant in seconds - * @param period The period in seconds between samples taken by the user - */ - public static LinearDigitalFilter singlePoleIIR( - PIDSource source, double timeConstant, double period) { - double gain = Math.exp(-period / timeConstant); - double[] ffGains = {1.0 - gain}; - double[] fbGains = {-gain}; - - return new LinearDigitalFilter(source, ffGains, fbGains); - } - - /** - * Creates a first-order high-pass filter of the form: y[n] = gain*x[n] + (-gain)*x[n-1] + - * gain*y[n-1] where gain = e^(-dt / T), T is the time constant in seconds. - * - *

This filter is stable for time constants greater than zero. - * - * @param source The PIDSource object that is used to get values - * @param timeConstant The discrete-time time constant in seconds - * @param period The period in seconds between samples taken by the user - */ - public static LinearDigitalFilter highPass(PIDSource source, double timeConstant, double period) { - double gain = Math.exp(-period / timeConstant); - double[] ffGains = {gain, -gain}; - double[] fbGains = {-gain}; - - return new LinearDigitalFilter(source, ffGains, fbGains); - } - - /** - * Creates a K-tap FIR moving average filter of the form: y[n] = 1/k * (x[k] + x[k-1] + ... + - * x[0]). - * - *

This filter is always stable. - * - * @param source The PIDSource object that is used to get values - * @param taps The number of samples to average over. Higher = smoother but slower - * @throws IllegalArgumentException if number of taps is less than 1 - */ - public static LinearDigitalFilter movingAverage(PIDSource source, int taps) { - if (taps <= 0) { - throw new IllegalArgumentException("Number of taps was not at least 1"); - } - - double[] ffGains = new double[taps]; - for (int i = 0; i < ffGains.length; i++) { - ffGains[i] = 1.0 / taps; - } - - double[] fbGains = new double[0]; - - return new LinearDigitalFilter(source, ffGains, fbGains); - } - - @Override - public double get() { - double retVal = 0.0; - - // Calculate the new value - for (int i = 0; i < m_inputGains.length; i++) { - retVal += m_inputs.get(i) * m_inputGains[i]; - } - for (int i = 0; i < m_outputGains.length; i++) { - retVal -= m_outputs.get(i) * m_outputGains[i]; - } - - return retVal; - } - - @Override - public void reset() { - m_inputs.clear(); - m_outputs.clear(); - } - - /** - * Calculates the next value of the filter. - * - * @return The filtered value at this step - */ - @Override - public double pidGet() { - double retVal = 0.0; - - // Rotate the inputs - m_inputs.addFirst(pidGetSource()); - - // Calculate the new value - for (int i = 0; i < m_inputGains.length; i++) { - retVal += m_inputs.get(i) * m_inputGains[i]; - } - for (int i = 0; i < m_outputGains.length; i++) { - retVal -= m_outputs.get(i) * m_outputGains[i]; - } - - // Rotate the outputs - m_outputs.addFirst(retVal); - - return retVal; - } -} diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/interfaces/Potentiometer.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/interfaces/Potentiometer.java index e0178273b0..99735c379f 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/interfaces/Potentiometer.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/interfaces/Potentiometer.java @@ -4,9 +4,7 @@ package edu.wpi.first.wpilibj.interfaces; -import edu.wpi.first.wpilibj.PIDSource; - /** Interface for a Potentiometer. */ -public interface Potentiometer extends PIDSource { +public interface Potentiometer { double get(); } diff --git a/wpilibj/src/test/java/edu/wpi/first/wpilibj/MockSpeedController.java b/wpilibj/src/test/java/edu/wpi/first/wpilibj/MockSpeedController.java index 4d55b9fef8..a7bd4a802f 100644 --- a/wpilibj/src/test/java/edu/wpi/first/wpilibj/MockSpeedController.java +++ b/wpilibj/src/test/java/edu/wpi/first/wpilibj/MockSpeedController.java @@ -37,9 +37,4 @@ public class MockSpeedController implements SpeedController { public void stopMotor() { disable(); } - - @Override - public void pidWrite(double output) { - set(output); - } } diff --git a/wpilibj/src/test/java/edu/wpi/first/wpilibj/SpeedControllerGroupTest.java b/wpilibj/src/test/java/edu/wpi/first/wpilibj/SpeedControllerGroupTest.java index c421471a9c..5f4a80cbff 100644 --- a/wpilibj/src/test/java/edu/wpi/first/wpilibj/SpeedControllerGroupTest.java +++ b/wpilibj/src/test/java/edu/wpi/first/wpilibj/SpeedControllerGroupTest.java @@ -98,15 +98,4 @@ class SpeedControllerGroupTest { Arrays.stream(speedControllers).mapToDouble(SpeedController::get).toArray(), 0.00005); } - - @ParameterizedTest - @MethodSource("speedControllerArguments") - void pidWriteTest(final SpeedControllerGroup group, final SpeedController[] speedControllers) { - group.pidWrite(1.0); - - assertArrayEquals( - DoubleStream.generate(() -> 1.0).limit(speedControllers.length).toArray(), - Arrays.stream(speedControllers).mapToDouble(SpeedController::get).toArray(), - 0.00005); - } } diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/armbotoffboard/ExampleSmartMotorController.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/armbotoffboard/ExampleSmartMotorController.java index fa31d457fe..fb2329c007 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/armbotoffboard/ExampleSmartMotorController.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/armbotoffboard/ExampleSmartMotorController.java @@ -93,7 +93,4 @@ public class ExampleSmartMotorController implements SpeedController { @Override public void stopMotor() {} - - @Override - public void pidWrite(double output) {} } diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/drivedistanceoffboard/ExampleSmartMotorController.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/drivedistanceoffboard/ExampleSmartMotorController.java index bacac9e4be..615ed18bcd 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/drivedistanceoffboard/ExampleSmartMotorController.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/drivedistanceoffboard/ExampleSmartMotorController.java @@ -97,7 +97,4 @@ public class ExampleSmartMotorController implements SpeedController { @Override public void stopMotor() {} - - @Override - public void pidWrite(double output) {} } diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/elevatortrapezoidprofile/ExampleSmartMotorController.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/elevatortrapezoidprofile/ExampleSmartMotorController.java index 1577770e6b..d2ea6eea42 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/elevatortrapezoidprofile/ExampleSmartMotorController.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/elevatortrapezoidprofile/ExampleSmartMotorController.java @@ -93,7 +93,4 @@ public class ExampleSmartMotorController implements SpeedController { @Override public void stopMotor() {} - - @Override - public void pidWrite(double output) {} } diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/pacgoat/subsystems/Pivot.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/pacgoat/subsystems/Pivot.java index 916ef56a1f..184125002f 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/pacgoat/subsystems/Pivot.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/pacgoat/subsystems/Pivot.java @@ -65,7 +65,7 @@ public class Pivot extends PIDSubsystem { /** Set the motor speed based off of the PID output. */ @Override protected void usePIDOutput(double output) { - m_motor.pidWrite(output); + m_motor.set(output); } /** If the pivot is at its upper limit. */ diff --git a/wpilibjIntegrationTests/src/main/java/edu/wpi/first/wpilibj/MockSpeedController.java b/wpilibjIntegrationTests/src/main/java/edu/wpi/first/wpilibj/MockSpeedController.java index 4d55b9fef8..a7bd4a802f 100644 --- a/wpilibjIntegrationTests/src/main/java/edu/wpi/first/wpilibj/MockSpeedController.java +++ b/wpilibjIntegrationTests/src/main/java/edu/wpi/first/wpilibj/MockSpeedController.java @@ -37,9 +37,4 @@ public class MockSpeedController implements SpeedController { public void stopMotor() { disable(); } - - @Override - public void pidWrite(double output) { - set(output); - } } From 687066af3d5342c8ddf6c98cac575f642d0350c7 Mon Sep 17 00:00:00 2001 From: Peter Johnson Date: Fri, 12 Mar 2021 15:52:02 -0800 Subject: [PATCH 20/34] [wpilib] Remove GyroBase --- wpilibc/src/main/native/cpp/ADXRS450_Gyro.cpp | 7 +++++ wpilibc/src/main/native/cpp/AnalogGyro.cpp | 7 +++++ wpilibc/src/main/native/cpp/GyroBase.cpp | 16 ---------- wpilibc/src/main/native/cpp/romi/RomiGyro.cpp | 8 +++++ .../main/native/include/frc/ADXRS450_Gyro.h | 12 ++++++-- .../src/main/native/include/frc/AnalogGyro.h | 10 +++++-- .../src/main/native/include/frc/GyroBase.h | 30 ------------------- .../main/native/include/frc/romi/RomiGyro.h | 12 ++++++-- .../edu/wpi/first/wpilibj/ADXRS450_Gyro.java | 10 ++++++- .../edu/wpi/first/wpilibj/AnalogGyro.java | 10 ++++++- .../java/edu/wpi/first/wpilibj/GyroBase.java | 17 ----------- .../edu/wpi/first/wpilibj/romi/RomiGyro.java | 12 ++++++-- 12 files changed, 78 insertions(+), 73 deletions(-) delete mode 100644 wpilibc/src/main/native/cpp/GyroBase.cpp delete mode 100644 wpilibc/src/main/native/include/frc/GyroBase.h delete mode 100644 wpilibj/src/main/java/edu/wpi/first/wpilibj/GyroBase.java diff --git a/wpilibc/src/main/native/cpp/ADXRS450_Gyro.cpp b/wpilibc/src/main/native/cpp/ADXRS450_Gyro.cpp index 523c1dfd24..2c1c62b14e 100644 --- a/wpilibc/src/main/native/cpp/ADXRS450_Gyro.cpp +++ b/wpilibc/src/main/native/cpp/ADXRS450_Gyro.cpp @@ -8,6 +8,7 @@ #include "frc/DriverStation.h" #include "frc/Timer.h" +#include "frc/smartdashboard/SendableBuilder.h" #include "frc/smartdashboard/SendableRegistry.h" using namespace frc; @@ -134,3 +135,9 @@ void ADXRS450_Gyro::Calibrate() { int ADXRS450_Gyro::GetPort() const { return m_port; } + +void ADXRS450_Gyro::InitSendable(SendableBuilder& builder) { + builder.SetSmartDashboardType("Gyro"); + builder.AddDoubleProperty( + "Value", [=]() { return GetAngle(); }, nullptr); +} diff --git a/wpilibc/src/main/native/cpp/AnalogGyro.cpp b/wpilibc/src/main/native/cpp/AnalogGyro.cpp index 867d1c618f..86e5b461f6 100644 --- a/wpilibc/src/main/native/cpp/AnalogGyro.cpp +++ b/wpilibc/src/main/native/cpp/AnalogGyro.cpp @@ -15,6 +15,7 @@ #include "frc/Base.h" #include "frc/Timer.h" #include "frc/WPIErrors.h" +#include "frc/smartdashboard/SendableBuilder.h" #include "frc/smartdashboard/SendableRegistry.h" using namespace frc; @@ -180,3 +181,9 @@ void AnalogGyro::Calibrate() { std::shared_ptr AnalogGyro::GetAnalogInput() const { return m_analog; } + +void AnalogGyro::InitSendable(SendableBuilder& builder) { + builder.SetSmartDashboardType("Gyro"); + builder.AddDoubleProperty( + "Value", [=]() { return GetAngle(); }, nullptr); +} diff --git a/wpilibc/src/main/native/cpp/GyroBase.cpp b/wpilibc/src/main/native/cpp/GyroBase.cpp deleted file mode 100644 index bcb25fad13..0000000000 --- a/wpilibc/src/main/native/cpp/GyroBase.cpp +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright (c) FIRST and other WPILib contributors. -// Open Source Software; you can modify and/or share it under the terms of -// the WPILib BSD license file in the root directory of this project. - -#include "frc/GyroBase.h" - -#include "frc/WPIErrors.h" -#include "frc/smartdashboard/SendableBuilder.h" - -using namespace frc; - -void GyroBase::InitSendable(SendableBuilder& builder) { - builder.SetSmartDashboardType("Gyro"); - builder.AddDoubleProperty( - "Value", [=]() { return GetAngle(); }, nullptr); -} diff --git a/wpilibc/src/main/native/cpp/romi/RomiGyro.cpp b/wpilibc/src/main/native/cpp/romi/RomiGyro.cpp index 50260f9d37..26a28fac73 100644 --- a/wpilibc/src/main/native/cpp/romi/RomiGyro.cpp +++ b/wpilibc/src/main/native/cpp/romi/RomiGyro.cpp @@ -4,6 +4,8 @@ #include "frc/romi/RomiGyro.h" +#include "frc/smartdashboard/SendableBuilder.h" + using namespace frc; RomiGyro::RomiGyro() : m_simDevice("Gyro:RomiGyro") { @@ -87,3 +89,9 @@ void RomiGyro::Reset() { m_angleZOffset = m_simAngleZ.Get(); } } + +void RomiGyro::InitSendable(SendableBuilder& builder) { + builder.SetSmartDashboardType("Gyro"); + builder.AddDoubleProperty( + "Value", [=]() { return GetAngle(); }, nullptr); +} diff --git a/wpilibc/src/main/native/include/frc/ADXRS450_Gyro.h b/wpilibc/src/main/native/include/frc/ADXRS450_Gyro.h index 3c49c05622..556bb49a2e 100644 --- a/wpilibc/src/main/native/include/frc/ADXRS450_Gyro.h +++ b/wpilibc/src/main/native/include/frc/ADXRS450_Gyro.h @@ -8,8 +8,11 @@ #include -#include "frc/GyroBase.h" +#include "frc/ErrorBase.h" #include "frc/SPI.h" +#include "frc/interfaces/Gyro.h" +#include "frc/smartdashboard/Sendable.h" +#include "frc/smartdashboard/SendableHelper.h" namespace frc { @@ -26,7 +29,10 @@ namespace frc { * This class is for the digital ADXRS450 gyro sensor that connects via SPI. * Only one instance of an ADXRS Gyro is supported. */ -class ADXRS450_Gyro : public GyroBase { +class ADXRS450_Gyro : public Gyro, + public ErrorBase, + public Sendable, + public SendableHelper { public: /** * Gyro constructor on onboard CS0. @@ -96,6 +102,8 @@ class ADXRS450_Gyro : public GyroBase { */ int GetPort() const; + void InitSendable(SendableBuilder& builder) override; + private: SPI m_spi; SPI::Port m_port; diff --git a/wpilibc/src/main/native/include/frc/AnalogGyro.h b/wpilibc/src/main/native/include/frc/AnalogGyro.h index 42b6317def..e2646cec5a 100644 --- a/wpilibc/src/main/native/include/frc/AnalogGyro.h +++ b/wpilibc/src/main/native/include/frc/AnalogGyro.h @@ -8,7 +8,8 @@ #include -#include "frc/GyroBase.h" +#include "frc/ErrorBase.h" +#include "frc/interfaces/Gyro.h" #include "frc/smartdashboard/Sendable.h" #include "frc/smartdashboard/SendableHelper.h" @@ -29,7 +30,10 @@ class AnalogInput; * * This class is for gyro sensors that connect to an analog input. */ -class AnalogGyro : public GyroBase { +class AnalogGyro : public Gyro, + public ErrorBase, + public Sendable, + public SendableHelper { public: static constexpr int kOversampleBits = 10; static constexpr int kAverageBits = 0; @@ -190,6 +194,8 @@ class AnalogGyro : public GyroBase { */ std::shared_ptr GetAnalogInput() const; + void InitSendable(SendableBuilder& builder) override; + protected: std::shared_ptr m_analog; diff --git a/wpilibc/src/main/native/include/frc/GyroBase.h b/wpilibc/src/main/native/include/frc/GyroBase.h deleted file mode 100644 index 004d146874..0000000000 --- a/wpilibc/src/main/native/include/frc/GyroBase.h +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright (c) FIRST and other WPILib contributors. -// Open Source Software; you can modify and/or share it under the terms of -// the WPILib BSD license file in the root directory of this project. - -#pragma once - -#include "frc/ErrorBase.h" -#include "frc/interfaces/Gyro.h" -#include "frc/smartdashboard/Sendable.h" -#include "frc/smartdashboard/SendableHelper.h" - -namespace frc { - -/** - * GyroBase is the common base class for Gyro implementations such as - * AnalogGyro. - */ -class GyroBase : public Gyro, - public ErrorBase, - public Sendable, - public SendableHelper { - public: - GyroBase() = default; - GyroBase(GyroBase&&) = default; - GyroBase& operator=(GyroBase&&) = default; - - void InitSendable(SendableBuilder& builder) override; -}; - -} // namespace frc diff --git a/wpilibc/src/main/native/include/frc/romi/RomiGyro.h b/wpilibc/src/main/native/include/frc/romi/RomiGyro.h index 391e26b700..eb33efc559 100644 --- a/wpilibc/src/main/native/include/frc/romi/RomiGyro.h +++ b/wpilibc/src/main/native/include/frc/romi/RomiGyro.h @@ -6,7 +6,10 @@ #include -#include "frc/GyroBase.h" +#include "frc/ErrorBase.h" +#include "frc/interfaces/Gyro.h" +#include "frc/smartdashboard/Sendable.h" +#include "frc/smartdashboard/SendableHelper.h" namespace frc { @@ -16,7 +19,10 @@ namespace frc { * This class is for the Romi onboard gyro, and will only work in * simulation/Romi mode. Only one instance of a RomiGyro is supported. */ -class RomiGyro : public GyroBase { +class RomiGyro : public Gyro, + public ErrorBase, + public Sendable, + public SendableHelper { public: RomiGyro(); @@ -84,6 +90,8 @@ class RomiGyro : public GyroBase { */ void Reset() override; + void InitSendable(SendableBuilder& builder) override; + private: hal::SimDevice m_simDevice; hal::SimDouble m_simRateX; diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/ADXRS450_Gyro.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/ADXRS450_Gyro.java index a179f1f870..183960a60d 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/ADXRS450_Gyro.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/ADXRS450_Gyro.java @@ -9,6 +9,8 @@ import edu.wpi.first.hal.HAL; import edu.wpi.first.hal.SimBoolean; import edu.wpi.first.hal.SimDevice; import edu.wpi.first.hal.SimDouble; +import edu.wpi.first.wpilibj.interfaces.Gyro; +import edu.wpi.first.wpilibj.smartdashboard.SendableBuilder; import edu.wpi.first.wpilibj.smartdashboard.SendableRegistry; import java.nio.ByteBuffer; import java.nio.ByteOrder; @@ -24,7 +26,7 @@ import java.nio.ByteOrder; * an ADXRS Gyro is supported. */ @SuppressWarnings({"TypeName", "AbbreviationAsWordInName", "PMD.UnusedPrivateField"}) -public class ADXRS450_Gyro extends GyroBase { +public class ADXRS450_Gyro implements Gyro, Sendable { private static final double kSamplePeriod = 0.0005; private static final double kCalibrationSampleTime = 5.0; private static final double kDegreePerSecondPerLSB = 0.0125; @@ -206,4 +208,10 @@ public class ADXRS450_Gyro extends GyroBase { } return m_spi.getAccumulatorLastValue() * kDegreePerSecondPerLSB; } + + @Override + public void initSendable(SendableBuilder builder) { + builder.setSmartDashboardType("Gyro"); + builder.addDoubleProperty("Value", this::getAngle, null); + } } diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/AnalogGyro.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/AnalogGyro.java index 11958cbd2d..2b250d3bbc 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/AnalogGyro.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/AnalogGyro.java @@ -9,6 +9,8 @@ import static edu.wpi.first.wpilibj.util.ErrorMessages.requireNonNullParam; import edu.wpi.first.hal.AnalogGyroJNI; import edu.wpi.first.hal.FRCNetComm.tResourceType; import edu.wpi.first.hal.HAL; +import edu.wpi.first.wpilibj.interfaces.Gyro; +import edu.wpi.first.wpilibj.smartdashboard.SendableBuilder; import edu.wpi.first.wpilibj.smartdashboard.SendableRegistry; /** @@ -20,7 +22,7 @@ import edu.wpi.first.wpilibj.smartdashboard.SendableRegistry; * *

This class is for gyro sensors that connect to an analog input. */ -public class AnalogGyro extends GyroBase { +public class AnalogGyro implements Gyro, Sendable { private static final double kDefaultVoltsPerDegreePerSecond = 0.007; protected AnalogInput m_analog; private boolean m_channelAllocated; @@ -188,4 +190,10 @@ public class AnalogGyro extends GyroBase { public AnalogInput getAnalogInput() { return m_analog; } + + @Override + public void initSendable(SendableBuilder builder) { + builder.setSmartDashboardType("Gyro"); + builder.addDoubleProperty("Value", this::getAngle, null); + } } diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/GyroBase.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/GyroBase.java deleted file mode 100644 index 8bc1076541..0000000000 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/GyroBase.java +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright (c) FIRST and other WPILib contributors. -// Open Source Software; you can modify and/or share it under the terms of -// the WPILib BSD license file in the root directory of this project. - -package edu.wpi.first.wpilibj; - -import edu.wpi.first.wpilibj.interfaces.Gyro; -import edu.wpi.first.wpilibj.smartdashboard.SendableBuilder; - -/** GyroBase is the common base class for Gyro implementations such as AnalogGyro. */ -public abstract class GyroBase implements Gyro, Sendable { - @Override - public void initSendable(SendableBuilder builder) { - builder.setSmartDashboardType("Gyro"); - builder.addDoubleProperty("Value", this::getAngle, null); - } -} diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/romi/RomiGyro.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/romi/RomiGyro.java index ea61795c4e..3c5e228606 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/romi/RomiGyro.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/romi/RomiGyro.java @@ -7,9 +7,11 @@ package edu.wpi.first.wpilibj.romi; import edu.wpi.first.hal.SimDevice; import edu.wpi.first.hal.SimDevice.Direction; import edu.wpi.first.hal.SimDouble; -import edu.wpi.first.wpilibj.GyroBase; +import edu.wpi.first.wpilibj.Sendable; +import edu.wpi.first.wpilibj.interfaces.Gyro; +import edu.wpi.first.wpilibj.smartdashboard.SendableBuilder; -public class RomiGyro extends GyroBase { +public class RomiGyro implements Gyro, Sendable { private final SimDevice m_simDevice; private SimDouble m_simRateX; private SimDouble m_simRateY; @@ -146,4 +148,10 @@ public class RomiGyro extends GyroBase { m_simDevice.close(); } } + + @Override + public void initSendable(SendableBuilder builder) { + builder.setSmartDashboardType("Gyro"); + builder.addDoubleProperty("Value", this::getAngle, null); + } } From b7b178f49c1fa3817e12cee0e55c3ff75a394c9c Mon Sep 17 00:00:00 2001 From: Peter Johnson Date: Fri, 12 Mar 2021 15:54:22 -0800 Subject: [PATCH 21/34] [wpilib] Remove Potentiometer interface --- .../native/include/frc/AnalogPotentiometer.h | 4 +-- .../include/frc/interfaces/Potentiometer.h | 28 ------------------- .../first/wpilibj/AnalogPotentiometer.java | 4 +-- .../wpilibj/interfaces/Potentiometer.java | 10 ------- .../examples/pacgoat/subsystems/Pivot.java | 3 +- 5 files changed, 3 insertions(+), 46 deletions(-) delete mode 100644 wpilibc/src/main/native/include/frc/interfaces/Potentiometer.h delete mode 100644 wpilibj/src/main/java/edu/wpi/first/wpilibj/interfaces/Potentiometer.java diff --git a/wpilibc/src/main/native/include/frc/AnalogPotentiometer.h b/wpilibc/src/main/native/include/frc/AnalogPotentiometer.h index 1f26242158..c6034e06ce 100644 --- a/wpilibc/src/main/native/include/frc/AnalogPotentiometer.h +++ b/wpilibc/src/main/native/include/frc/AnalogPotentiometer.h @@ -8,7 +8,6 @@ #include "frc/AnalogInput.h" #include "frc/ErrorBase.h" -#include "frc/interfaces/Potentiometer.h" #include "frc/smartdashboard/Sendable.h" #include "frc/smartdashboard/SendableHelper.h" @@ -23,7 +22,6 @@ class SendableBuilder; * constructor. */ class AnalogPotentiometer : public ErrorBase, - public Potentiometer, public Sendable, public SendableHelper { public: @@ -105,7 +103,7 @@ class AnalogPotentiometer : public ErrorBase, * @return The current position of the potentiometer (in the units used for * fullRange and offset). */ - double Get() const override; + double Get() const; void InitSendable(SendableBuilder& builder) override; diff --git a/wpilibc/src/main/native/include/frc/interfaces/Potentiometer.h b/wpilibc/src/main/native/include/frc/interfaces/Potentiometer.h deleted file mode 100644 index ac3125c9f5..0000000000 --- a/wpilibc/src/main/native/include/frc/interfaces/Potentiometer.h +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright (c) FIRST and other WPILib contributors. -// Open Source Software; you can modify and/or share it under the terms of -// the WPILib BSD license file in the root directory of this project. - -#pragma once - -namespace frc { - -/** - * Interface for potentiometers. - */ -class Potentiometer { - public: - Potentiometer() = default; - virtual ~Potentiometer() = default; - - Potentiometer(Potentiometer&&) = default; - Potentiometer& operator=(Potentiometer&&) = default; - - /** - * Common interface for getting the current value of a potentiometer. - * - * @return The current set speed. Value is between -1.0 and 1.0. - */ - virtual double Get() const = 0; -}; - -} // namespace frc diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/AnalogPotentiometer.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/AnalogPotentiometer.java index 9c74f5e216..b752ddba05 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/AnalogPotentiometer.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/AnalogPotentiometer.java @@ -4,7 +4,6 @@ package edu.wpi.first.wpilibj; -import edu.wpi.first.wpilibj.interfaces.Potentiometer; import edu.wpi.first.wpilibj.smartdashboard.SendableBuilder; import edu.wpi.first.wpilibj.smartdashboard.SendableRegistry; @@ -13,7 +12,7 @@ import edu.wpi.first.wpilibj.smartdashboard.SendableRegistry; * corresponds to a position. The position is in whichever units you choose, by way of the scaling * and offset constants passed to the constructor. */ -public class AnalogPotentiometer implements Potentiometer, Sendable, AutoCloseable { +public class AnalogPotentiometer implements Sendable, AutoCloseable { private AnalogInput m_analogInput; private boolean m_initAnalogInput; private double m_fullRange; @@ -119,7 +118,6 @@ public class AnalogPotentiometer implements Potentiometer, Sendable, AutoCloseab * * @return The current position of the potentiometer. */ - @Override public double get() { if (m_analogInput == null) { return m_offset; diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/interfaces/Potentiometer.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/interfaces/Potentiometer.java deleted file mode 100644 index 99735c379f..0000000000 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/interfaces/Potentiometer.java +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright (c) FIRST and other WPILib contributors. -// Open Source Software; you can modify and/or share it under the terms of -// the WPILib BSD license file in the root directory of this project. - -package edu.wpi.first.wpilibj.interfaces; - -/** Interface for a Potentiometer. */ -public interface Potentiometer { - double get(); -} diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/pacgoat/subsystems/Pivot.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/pacgoat/subsystems/Pivot.java index 184125002f..620476fe97 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/pacgoat/subsystems/Pivot.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/pacgoat/subsystems/Pivot.java @@ -10,7 +10,6 @@ import edu.wpi.first.wpilibj.SpeedController; import edu.wpi.first.wpilibj.Victor; import edu.wpi.first.wpilibj.command.PIDSubsystem; import edu.wpi.first.wpilibj.examples.pacgoat.Robot; -import edu.wpi.first.wpilibj.interfaces.Potentiometer; /** * The Pivot subsystem contains the Van-door motor and the pot for PID control of angle of the pivot @@ -29,7 +28,7 @@ public class Pivot extends PIDSubsystem { // 0 degrees is vertical facing up. // Angle increases the more forward the pivot goes. - private final Potentiometer m_pot = new AnalogPotentiometer(1); + private final AnalogPotentiometer m_pot = new AnalogPotentiometer(1); // Motor to move the pivot. private final SpeedController m_motor = new Victor(5); From 0abf6c9045adba06240165ce8133f97658717fe5 Mon Sep 17 00:00:00 2001 From: Peter Johnson Date: Sat, 17 Apr 2021 11:27:16 -0700 Subject: [PATCH 22/34] [wpilib] Move motor controllers to motorcontrol package (#3302) Also deprecate SpeedController in favor of motorcontrol.MotorController and SpeedControllerGroup in favor of motorcontrol.MotorControllerGroup. The MotorController interface is derived from the SpeedController interface so that code such as SpeedController x = new VictorSP(1) continues to compile (just with a warning). SpeedControllerGroup and MotorControllerGroup are independent classes; both implement the MotorController interface. --- .../native/cpp/drive/DifferentialDrive.cpp | 8 ++ .../main/native/cpp/drive/KilloughDrive.cpp | 8 ++ .../main/native/cpp/drive/MecanumDrive.cpp | 8 ++ .../main/native/cpp/drive/RobotDriveBase.cpp | 2 +- .../native/cpp/{ => motorcontrol}/DMC60.cpp | 4 +- .../native/cpp/{ => motorcontrol}/Jaguar.cpp | 4 +- .../cpp/motorcontrol/MotorControllerGroup.cpp | 69 ++++++++++++ .../cpp/{ => motorcontrol}/NidecBrushless.cpp | 2 +- .../PWMMotorController.cpp} | 24 ++--- .../cpp/{ => motorcontrol}/PWMSparkMax.cpp | 4 +- .../cpp/{ => motorcontrol}/PWMTalonFX.cpp | 4 +- .../cpp/{ => motorcontrol}/PWMTalonSRX.cpp | 4 +- .../cpp/{ => motorcontrol}/PWMVenom.cpp | 4 +- .../cpp/{ => motorcontrol}/PWMVictorSPX.cpp | 4 +- .../native/cpp/{ => motorcontrol}/SD540.cpp | 4 +- .../native/cpp/{ => motorcontrol}/Spark.cpp | 4 +- .../native/cpp/{ => motorcontrol}/Talon.cpp | 4 +- .../native/cpp/{ => motorcontrol}/Victor.cpp | 4 +- .../cpp/{ => motorcontrol}/VictorSP.cpp | 4 +- wpilibc/src/main/native/include/frc/Servo.h | 1 - .../main/native/include/frc/SpeedController.h | 3 +- .../native/include/frc/SpeedControllerGroup.h | 11 +- .../include/frc/drive/DifferentialDrive.h | 33 ++++-- .../native/include/frc/drive/KilloughDrive.h | 19 ++++ .../native/include/frc/drive/MecanumDrive.h | 19 ++++ .../native/include/frc/drive/RobotDriveBase.h | 2 - .../include/frc/{ => motorcontrol}/DMC60.h | 6 +- .../include/frc/{ => motorcontrol}/Jaguar.h | 6 +- .../frc/motorcontrol/MotorController.h | 37 +++++++ .../frc/motorcontrol/MotorControllerGroup.h | 47 ++++++++ .../frc/motorcontrol/MotorControllerGroup.inc | 22 ++++ .../frc/{ => motorcontrol}/NidecBrushless.h | 6 +- .../PWMMotorController.h} | 18 ++-- .../frc/{ => motorcontrol}/PWMSparkMax.h | 6 +- .../frc/{ => motorcontrol}/PWMTalonFX.h | 6 +- .../frc/{ => motorcontrol}/PWMTalonSRX.h | 6 +- .../include/frc/{ => motorcontrol}/PWMVenom.h | 4 +- .../frc/{ => motorcontrol}/PWMVictorSPX.h | 6 +- .../include/frc/{ => motorcontrol}/SD540.h | 6 +- .../include/frc/{ => motorcontrol}/Spark.h | 6 +- .../include/frc/{ => motorcontrol}/Talon.h | 6 +- .../include/frc/{ => motorcontrol}/Victor.h | 8 +- .../include/frc/{ => motorcontrol}/VictorSP.h | 6 +- .../include/frc/shuffleboard/BuiltInWidgets.h | 8 +- ...Controller.cpp => MockMotorController.cpp} | 14 +-- .../native/cpp/SpeedControllerGroupTest.cpp | 44 ++++---- .../native/cpp/simulation/ElevatorSimTest.cpp | 2 +- .../cpp/simulation/StateSpaceSimTest.cpp | 2 +- ...peedController.h => MockMotorController.h} | 4 +- .../cpp/examples/ArcadeDrive/cpp/Robot.cpp | 2 +- .../ArcadeDriveXboxController/cpp/Robot.cpp | 2 +- .../ArmBot/include/subsystems/ArmSubsystem.h | 2 +- .../include/subsystems/DriveSubsystem.h | 8 +- .../include/ExampleSmartMotorController.h | 4 +- .../include/subsystems/DriveSubsystem.h | 8 +- .../cpp/examples/ArmSimulation/cpp/Robot.cpp | 2 +- .../DifferentialDriveBot/include/Drivetrain.h | 8 +- .../include/Drivetrain.h | 8 +- .../include/ExampleSmartMotorController.h | 4 +- .../ElevatorProfiledPID/cpp/Robot.cpp | 2 +- .../examples/ElevatorSimulation/cpp/Robot.cpp | 2 +- .../include/ExampleSmartMotorController.h | 4 +- .../include/subsystems/DriveSubsystem.h | 8 +- .../include/subsystems/ShooterSubsystem.h | 2 +- .../GearsBot/include/subsystems/Claw.h | 2 +- .../GearsBot/include/subsystems/DriveTrain.h | 8 +- .../GearsBot/include/subsystems/Elevator.h | 2 +- .../GearsBot/include/subsystems/Wrist.h | 2 +- .../cpp/examples/GettingStarted/cpp/Robot.cpp | 2 +- .../src/main/cpp/examples/Gyro/cpp/Robot.cpp | 2 +- .../include/subsystems/DriveSubsystem.h | 8 +- .../cpp/examples/GyroMecanum/cpp/Robot.cpp | 2 +- .../include/subsystems/DriveSubsystem.h | 8 +- .../include/subsystems/DriveSubsystem.h | 8 +- .../examples/MecanumBot/include/Drivetrain.h | 2 +- .../cpp/RobotContainer.cpp | 2 +- .../cpp/subsystems/DriveSubsystem.cpp | 2 +- .../include/subsystems/DriveSubsystem.h | 6 +- .../cpp/examples/MecanumDrive/cpp/Robot.cpp | 2 +- .../include/Drivetrain.h | 2 +- .../cpp/examples/MotorControl/cpp/Robot.cpp | 2 +- .../MotorControlEncoder/cpp/Robot.cpp | 2 +- .../PacGoat/include/subsystems/Collector.h | 2 +- .../PacGoat/include/subsystems/DriveTrain.h | 8 +- .../PacGoat/include/subsystems/Pivot.h | 2 +- .../examples/PotentiometerPID/cpp/Robot.cpp | 2 +- .../include/subsystems/DriveSubsystem.h | 8 +- .../RamseteController/include/Drivetrain.h | 8 +- .../include/subsystems/Drivetrain.h | 2 +- .../cpp/examples/ShuffleBoard/cpp/Robot.cpp | 2 +- .../include/Drivetrain.h | 8 +- .../cpp/examples/StateSpaceArm/cpp/Robot.cpp | 2 +- .../include/subsystems/DriveSubsystem.h | 8 +- .../examples/StateSpaceElevator/cpp/Robot.cpp | 2 +- .../examples/StateSpaceFlywheel/cpp/Robot.cpp | 2 +- .../StateSpaceFlywheelSysId/cpp/Robot.cpp | 2 +- .../examples/SwerveBot/include/SwerveModule.h | 2 +- .../include/subsystems/DriveSubsystem.h | 4 +- .../include/subsystems/SwerveModule.h | 2 +- .../include/SwerveModule.h | 2 +- .../TankDriveXboxController/cpp/Robot.cpp | 2 +- .../cpp/examples/Ultrasonic/cpp/Robot.cpp | 2 +- .../cpp/examples/UltrasonicPID/cpp/Robot.cpp | 2 +- .../src/main/native/cpp/CounterTest.cpp | 6 +- .../src/main/native/cpp/MotorEncoderTest.cpp | 44 ++++---- .../main/native/cpp/MotorInvertingTest.cpp | 50 ++++----- .../native/cpp/PowerDistributionPanelTest.cpp | 6 +- .../main/java/edu/wpi/first/wpilibj/PWM.java | 2 +- .../wpi/first/wpilibj/SpeedController.java | 19 ++-- .../first/wpilibj/SpeedControllerGroup.java | 15 ++- .../wpilibj/drive/DifferentialDrive.java | 41 +++---- .../first/wpilibj/drive/KilloughDrive.java | 1 + .../wpi/first/wpilibj/drive/MecanumDrive.java | 1 + .../wpilibj/{ => motorcontrol}/DMC60.java | 7 +- .../wpilibj/{ => motorcontrol}/Jaguar.java | 7 +- .../wpilibj/motorcontrol/MotorController.java | 11 ++ .../motorcontrol/MotorControllerGroup.java | 98 +++++++++++++++++ .../{ => motorcontrol}/NidecBrushless.java | 8 +- .../PWMMotorController.java} | 17 +-- .../{ => motorcontrol}/PWMSparkMax.java | 7 +- .../{ => motorcontrol}/PWMTalonFX.java | 7 +- .../{ => motorcontrol}/PWMTalonSRX.java | 7 +- .../wpilibj/{ => motorcontrol}/PWMVenom.java | 5 +- .../{ => motorcontrol}/PWMVictorSPX.java | 7 +- .../wpilibj/{ => motorcontrol}/SD540.java | 7 +- .../wpilibj/{ => motorcontrol}/Spark.java | 7 +- .../wpilibj/{ => motorcontrol}/Talon.java | 7 +- .../wpilibj/{ => motorcontrol}/Victor.java | 7 +- .../wpilibj/{ => motorcontrol}/VictorSP.java | 7 +- .../wpilibj/shuffleboard/BuiltInWidgets.java | 38 +++---- .../wpilibj/SpeedControllerGroupTest.java | 101 ------------------ .../motorcontrol/MockMotorController.java | 4 +- .../MotorControllerGroupTest.java | 101 ++++++++++++++++++ .../wpilibj/simulation/ElevatorSimTest.java | 2 +- .../wpilibj/examples/arcadedrive/Robot.java | 2 +- .../arcadedrivexboxcontroller/Robot.java | 2 +- .../armbot/subsystems/ArmSubsystem.java | 2 +- .../armbot/subsystems/DriveSubsystem.java | 12 +-- .../ExampleSmartMotorController.java | 4 +- .../subsystems/DriveSubsystem.java | 12 +-- .../wpilibj/examples/armsimulation/Robot.java | 2 +- .../differentialdrivebot/Drivetrain.java | 22 ++-- .../Drivetrain.java | 22 ++-- .../ExampleSmartMotorController.java | 4 +- .../examples/elevatorprofiledpid/Robot.java | 6 +- .../examples/elevatorsimulation/Robot.java | 2 +- .../ExampleSmartMotorController.java | 4 +- .../frisbeebot/subsystems/DriveSubsystem.java | 12 +-- .../subsystems/ShooterSubsystem.java | 2 +- .../examples/gearsbot/subsystems/Claw.java | 2 +- .../gearsbot/subsystems/DriveTrain.java | 14 +-- .../gearsbot/subsystems/Elevator.java | 2 +- .../examples/gearsbot/subsystems/Wrist.java | 2 +- .../examples/gettingstarted/Robot.java | 2 +- .../first/wpilibj/examples/gyro/Robot.java | 2 +- .../subsystems/DriveSubsystem.java | 12 +-- .../wpilibj/examples/gyromecanum/Robot.java | 2 +- .../subsystems/DriveSubsystem.java | 12 +-- .../subsystems/DriveSubsystem.java | 12 +-- .../examples/mecanumbot/Drivetrain.java | 12 +-- .../RobotContainer.java | 2 +- .../subsystems/DriveSubsystem.java | 6 +- .../wpilibj/examples/mecanumdrive/Robot.java | 2 +- .../mecanumdriveposeestimator/Drivetrain.java | 12 +-- .../wpilibj/examples/motorcontrol/Robot.java | 6 +- .../examples/motorcontrolencoder/Robot.java | 6 +- .../pacgoat/subsystems/Collector.java | 6 +- .../pacgoat/subsystems/DriveTrain.java | 22 ++-- .../examples/pacgoat/subsystems/Pivot.java | 6 +- .../examples/potentiometerpid/Robot.java | 6 +- .../subsystems/DriveSubsystem.java | 12 +-- .../ramsetecontroller/Drivetrain.java | 22 ++-- .../romireference/subsystems/Drivetrain.java | 2 +- .../wpilibj/examples/shuffleboard/Robot.java | 2 +- .../Drivetrain.java | 12 +-- .../wpilibj/examples/statespacearm/Robot.java | 6 +- .../subsystems/DriveSubsystem.java | 12 +-- .../examples/statespaceelevator/Robot.java | 6 +- .../examples/statespaceflywheel/Robot.java | 6 +- .../statespaceflywheelsysid/Robot.java | 6 +- .../examples/swervebot/SwerveModule.java | 8 +- .../subsystems/SwerveModule.java | 2 +- .../SwerveModule.java | 8 +- .../wpilibj/examples/tankdrive/Robot.java | 2 +- .../tankdrivexboxcontroller/Robot.java | 2 +- .../wpilibj/examples/ultrasonic/Robot.java | 2 +- .../wpilibj/examples/ultrasonicpid/Robot.java | 2 +- .../subsystems/RomiDrivetrain.java | 2 +- .../templates/romitimed/RomiDrivetrain.java | 2 +- .../wpi/first/wpilibj/MotorInvertingTest.java | 2 +- .../java/edu/wpi/first/wpilibj/PDPTest.java | 4 +- .../wpilibj/fixtures/MotorEncoderFixture.java | 12 +-- .../motorcontrol/MockMotorController.java | 4 +- .../edu/wpi/first/wpilibj/test/TestBench.java | 12 +-- 194 files changed, 1096 insertions(+), 696 deletions(-) rename wpilibc/src/main/native/cpp/{ => motorcontrol}/DMC60.cpp (83%) rename wpilibc/src/main/native/cpp/{ => motorcontrol}/Jaguar.cpp (83%) create mode 100644 wpilibc/src/main/native/cpp/motorcontrol/MotorControllerGroup.cpp rename wpilibc/src/main/native/cpp/{ => motorcontrol}/NidecBrushless.cpp (97%) rename wpilibc/src/main/native/cpp/{PWMSpeedController.cpp => motorcontrol/PWMMotorController.cpp} (59%) rename wpilibc/src/main/native/cpp/{ => motorcontrol}/PWMSparkMax.cpp (86%) rename wpilibc/src/main/native/cpp/{ => motorcontrol}/PWMTalonFX.cpp (85%) rename wpilibc/src/main/native/cpp/{ => motorcontrol}/PWMTalonSRX.cpp (85%) rename wpilibc/src/main/native/cpp/{ => motorcontrol}/PWMVenom.cpp (83%) rename wpilibc/src/main/native/cpp/{ => motorcontrol}/PWMVictorSPX.cpp (84%) rename wpilibc/src/main/native/cpp/{ => motorcontrol}/SD540.cpp (83%) rename wpilibc/src/main/native/cpp/{ => motorcontrol}/Spark.cpp (83%) rename wpilibc/src/main/native/cpp/{ => motorcontrol}/Talon.cpp (83%) rename wpilibc/src/main/native/cpp/{ => motorcontrol}/Victor.cpp (83%) rename wpilibc/src/main/native/cpp/{ => motorcontrol}/VictorSP.cpp (83%) rename wpilibc/src/main/native/include/frc/{ => motorcontrol}/DMC60.h (90%) rename wpilibc/src/main/native/include/frc/{ => motorcontrol}/Jaguar.h (88%) create mode 100644 wpilibc/src/main/native/include/frc/motorcontrol/MotorController.h create mode 100644 wpilibc/src/main/native/include/frc/motorcontrol/MotorControllerGroup.h create mode 100644 wpilibc/src/main/native/include/frc/motorcontrol/MotorControllerGroup.inc rename wpilibc/src/main/native/include/frc/{ => motorcontrol}/NidecBrushless.h (95%) rename wpilibc/src/main/native/include/frc/{PWMSpeedController.h => motorcontrol/PWMMotorController.h} (76%) rename wpilibc/src/main/native/include/frc/{ => motorcontrol}/PWMSparkMax.h (89%) rename wpilibc/src/main/native/include/frc/{ => motorcontrol}/PWMTalonFX.h (88%) rename wpilibc/src/main/native/include/frc/{ => motorcontrol}/PWMTalonSRX.h (88%) rename wpilibc/src/main/native/include/frc/{ => motorcontrol}/PWMVenom.h (92%) rename wpilibc/src/main/native/include/frc/{ => motorcontrol}/PWMVictorSPX.h (88%) rename wpilibc/src/main/native/include/frc/{ => motorcontrol}/SD540.h (90%) rename wpilibc/src/main/native/include/frc/{ => motorcontrol}/Spark.h (90%) rename wpilibc/src/main/native/include/frc/{ => motorcontrol}/Talon.h (88%) rename wpilibc/src/main/native/include/frc/{ => motorcontrol}/Victor.h (87%) rename wpilibc/src/main/native/include/frc/{ => motorcontrol}/VictorSP.h (89%) rename wpilibc/src/test/native/cpp/{MockSpeedController.cpp => MockMotorController.cpp} (55%) rename wpilibc/src/test/native/include/{MockSpeedController.h => MockMotorController.h} (84%) rename wpilibj/src/main/java/edu/wpi/first/wpilibj/{ => motorcontrol}/DMC60.java (89%) rename wpilibj/src/main/java/edu/wpi/first/wpilibj/{ => motorcontrol}/Jaguar.java (88%) create mode 100644 wpilibj/src/main/java/edu/wpi/first/wpilibj/motorcontrol/MotorController.java create mode 100644 wpilibj/src/main/java/edu/wpi/first/wpilibj/motorcontrol/MotorControllerGroup.java rename wpilibj/src/main/java/edu/wpi/first/wpilibj/{ => motorcontrol}/NidecBrushless.java (93%) rename wpilibj/src/main/java/edu/wpi/first/wpilibj/{PWMSpeedController.java => motorcontrol/PWMMotorController.java} (81%) rename wpilibj/src/main/java/edu/wpi/first/wpilibj/{ => motorcontrol}/PWMSparkMax.java (87%) rename wpilibj/src/main/java/edu/wpi/first/wpilibj/{ => motorcontrol}/PWMTalonFX.java (88%) rename wpilibj/src/main/java/edu/wpi/first/wpilibj/{ => motorcontrol}/PWMTalonSRX.java (88%) rename wpilibj/src/main/java/edu/wpi/first/wpilibj/{ => motorcontrol}/PWMVenom.java (91%) rename wpilibj/src/main/java/edu/wpi/first/wpilibj/{ => motorcontrol}/PWMVictorSPX.java (88%) rename wpilibj/src/main/java/edu/wpi/first/wpilibj/{ => motorcontrol}/SD540.java (89%) rename wpilibj/src/main/java/edu/wpi/first/wpilibj/{ => motorcontrol}/Spark.java (89%) rename wpilibj/src/main/java/edu/wpi/first/wpilibj/{ => motorcontrol}/Talon.java (88%) rename wpilibj/src/main/java/edu/wpi/first/wpilibj/{ => motorcontrol}/Victor.java (87%) rename wpilibj/src/main/java/edu/wpi/first/wpilibj/{ => motorcontrol}/VictorSP.java (89%) delete mode 100644 wpilibj/src/test/java/edu/wpi/first/wpilibj/SpeedControllerGroupTest.java rename wpilibjIntegrationTests/src/main/java/edu/wpi/first/wpilibj/MockSpeedController.java => wpilibj/src/test/java/edu/wpi/first/wpilibj/motorcontrol/MockMotorController.java (86%) create mode 100644 wpilibj/src/test/java/edu/wpi/first/wpilibj/motorcontrol/MotorControllerGroupTest.java rename wpilibj/src/test/java/edu/wpi/first/wpilibj/MockSpeedController.java => wpilibjIntegrationTests/src/main/java/edu/wpi/first/wpilibj/motorcontrol/MockMotorController.java (86%) diff --git a/wpilibc/src/main/native/cpp/drive/DifferentialDrive.cpp b/wpilibc/src/main/native/cpp/drive/DifferentialDrive.cpp index 7a7a93b475..59e3622dbe 100644 --- a/wpilibc/src/main/native/cpp/drive/DifferentialDrive.cpp +++ b/wpilibc/src/main/native/cpp/drive/DifferentialDrive.cpp @@ -15,6 +15,14 @@ using namespace frc; +#if defined(_MSC_VER) +#pragma warning(disable : 4996) // was declared deprecated +#elif defined(__clang__) +#pragma clang diagnostic ignored "-Wdeprecated-declarations" +#elif defined(__GNUC__) +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" +#endif + DifferentialDrive::DifferentialDrive(SpeedController& leftMotor, SpeedController& rightMotor) : m_leftMotor(&leftMotor), m_rightMotor(&rightMotor) { diff --git a/wpilibc/src/main/native/cpp/drive/KilloughDrive.cpp b/wpilibc/src/main/native/cpp/drive/KilloughDrive.cpp index 6c2247b9b6..7cdf145d8f 100644 --- a/wpilibc/src/main/native/cpp/drive/KilloughDrive.cpp +++ b/wpilibc/src/main/native/cpp/drive/KilloughDrive.cpp @@ -16,6 +16,14 @@ using namespace frc; +#if defined(_MSC_VER) +#pragma warning(disable : 4996) // was declared deprecated +#elif defined(__clang__) +#pragma clang diagnostic ignored "-Wdeprecated-declarations" +#elif defined(__GNUC__) +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" +#endif + KilloughDrive::KilloughDrive(SpeedController& leftMotor, SpeedController& rightMotor, SpeedController& backMotor) diff --git a/wpilibc/src/main/native/cpp/drive/MecanumDrive.cpp b/wpilibc/src/main/native/cpp/drive/MecanumDrive.cpp index 2a9eac01f9..d9886b66f5 100644 --- a/wpilibc/src/main/native/cpp/drive/MecanumDrive.cpp +++ b/wpilibc/src/main/native/cpp/drive/MecanumDrive.cpp @@ -17,6 +17,14 @@ using namespace frc; +#if defined(_MSC_VER) +#pragma warning(disable : 4996) // was declared deprecated +#elif defined(__clang__) +#pragma clang diagnostic ignored "-Wdeprecated-declarations" +#elif defined(__GNUC__) +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" +#endif + MecanumDrive::MecanumDrive(SpeedController& frontLeftMotor, SpeedController& rearLeftMotor, SpeedController& frontRightMotor, diff --git a/wpilibc/src/main/native/cpp/drive/RobotDriveBase.cpp b/wpilibc/src/main/native/cpp/drive/RobotDriveBase.cpp index 594cfbcf0c..3b03c3a75a 100644 --- a/wpilibc/src/main/native/cpp/drive/RobotDriveBase.cpp +++ b/wpilibc/src/main/native/cpp/drive/RobotDriveBase.cpp @@ -11,7 +11,7 @@ #include #include "frc/Base.h" -#include "frc/SpeedController.h" +#include "frc/motorcontrol/MotorController.h" using namespace frc; diff --git a/wpilibc/src/main/native/cpp/DMC60.cpp b/wpilibc/src/main/native/cpp/motorcontrol/DMC60.cpp similarity index 83% rename from wpilibc/src/main/native/cpp/DMC60.cpp rename to wpilibc/src/main/native/cpp/motorcontrol/DMC60.cpp index 001a51e5c8..81fa868f84 100644 --- a/wpilibc/src/main/native/cpp/DMC60.cpp +++ b/wpilibc/src/main/native/cpp/motorcontrol/DMC60.cpp @@ -2,13 +2,13 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -#include "frc/DMC60.h" +#include "frc/motorcontrol/DMC60.h" #include using namespace frc; -DMC60::DMC60(int channel) : PWMSpeedController("DMC60", channel) { +DMC60::DMC60(int channel) : PWMMotorController("DMC60", channel) { m_pwm.SetBounds(2.004, 1.52, 1.50, 1.48, 0.997); m_pwm.SetPeriodMultiplier(PWM::kPeriodMultiplier_1X); m_pwm.SetSpeed(0.0); diff --git a/wpilibc/src/main/native/cpp/Jaguar.cpp b/wpilibc/src/main/native/cpp/motorcontrol/Jaguar.cpp similarity index 83% rename from wpilibc/src/main/native/cpp/Jaguar.cpp rename to wpilibc/src/main/native/cpp/motorcontrol/Jaguar.cpp index 2cf93c1de2..c68ae0c8f2 100644 --- a/wpilibc/src/main/native/cpp/Jaguar.cpp +++ b/wpilibc/src/main/native/cpp/motorcontrol/Jaguar.cpp @@ -2,13 +2,13 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -#include "frc/Jaguar.h" +#include "frc/motorcontrol/Jaguar.h" #include using namespace frc; -Jaguar::Jaguar(int channel) : PWMSpeedController("Jaguar", channel) { +Jaguar::Jaguar(int channel) : PWMMotorController("Jaguar", channel) { m_pwm.SetBounds(2.31, 1.55, 1.507, 1.454, 0.697); m_pwm.SetPeriodMultiplier(PWM::kPeriodMultiplier_1X); m_pwm.SetSpeed(0.0); diff --git a/wpilibc/src/main/native/cpp/motorcontrol/MotorControllerGroup.cpp b/wpilibc/src/main/native/cpp/motorcontrol/MotorControllerGroup.cpp new file mode 100644 index 0000000000..54ec4d8a94 --- /dev/null +++ b/wpilibc/src/main/native/cpp/motorcontrol/MotorControllerGroup.cpp @@ -0,0 +1,69 @@ +// Copyright (c) FIRST and other WPILib contributors. +// Open Source Software; you can modify and/or share it under the terms of +// the WPILib BSD license file in the root directory of this project. + +#include "frc/motorcontrol/MotorControllerGroup.h" + +#include "frc/smartdashboard/SendableBuilder.h" +#include "frc/smartdashboard/SendableRegistry.h" + +using namespace frc; + +// Can't use a delegated constructor here because of an MSVC bug. +// https://developercommunity.visualstudio.com/content/problem/583/compiler-bug-with-delegating-a-constructor.html + +MotorControllerGroup::MotorControllerGroup( + std::vector>&& motorControllers) + : m_motorControllers(std::move(motorControllers)) { + Initialize(); +} + +void MotorControllerGroup::Initialize() { + for (auto& motorController : m_motorControllers) { + SendableRegistry::GetInstance().AddChild(this, &motorController.get()); + } + static int instances = 0; + ++instances; + SendableRegistry::GetInstance().Add(this, "MotorControllerGroup", instances); +} + +void MotorControllerGroup::Set(double speed) { + for (auto motorController : m_motorControllers) { + motorController.get().Set(m_isInverted ? -speed : speed); + } +} + +double MotorControllerGroup::Get() const { + if (!m_motorControllers.empty()) { + return m_motorControllers.front().get().Get() * (m_isInverted ? -1 : 1); + } + return 0.0; +} + +void MotorControllerGroup::SetInverted(bool isInverted) { + m_isInverted = isInverted; +} + +bool MotorControllerGroup::GetInverted() const { + return m_isInverted; +} + +void MotorControllerGroup::Disable() { + for (auto motorController : m_motorControllers) { + motorController.get().Disable(); + } +} + +void MotorControllerGroup::StopMotor() { + for (auto motorController : m_motorControllers) { + motorController.get().StopMotor(); + } +} + +void MotorControllerGroup::InitSendable(SendableBuilder& builder) { + builder.SetSmartDashboardType("Motor Controller"); + builder.SetActuator(true); + builder.SetSafeState([=]() { StopMotor(); }); + builder.AddDoubleProperty( + "Value", [=]() { return Get(); }, [=](double value) { Set(value); }); +} diff --git a/wpilibc/src/main/native/cpp/NidecBrushless.cpp b/wpilibc/src/main/native/cpp/motorcontrol/NidecBrushless.cpp similarity index 97% rename from wpilibc/src/main/native/cpp/NidecBrushless.cpp rename to wpilibc/src/main/native/cpp/motorcontrol/NidecBrushless.cpp index ff61843000..fdca9b9341 100644 --- a/wpilibc/src/main/native/cpp/NidecBrushless.cpp +++ b/wpilibc/src/main/native/cpp/motorcontrol/NidecBrushless.cpp @@ -2,7 +2,7 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -#include "frc/NidecBrushless.h" +#include "frc/motorcontrol/NidecBrushless.h" #include #include diff --git a/wpilibc/src/main/native/cpp/PWMSpeedController.cpp b/wpilibc/src/main/native/cpp/motorcontrol/PWMMotorController.cpp similarity index 59% rename from wpilibc/src/main/native/cpp/PWMSpeedController.cpp rename to wpilibc/src/main/native/cpp/motorcontrol/PWMMotorController.cpp index ff04282559..9f0394639a 100644 --- a/wpilibc/src/main/native/cpp/PWMSpeedController.cpp +++ b/wpilibc/src/main/native/cpp/motorcontrol/PWMMotorController.cpp @@ -2,7 +2,7 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -#include "frc/PWMSpeedController.h" +#include "frc/motorcontrol/PWMMotorController.h" #include @@ -10,45 +10,45 @@ using namespace frc; -void PWMSpeedController::Set(double speed) { +void PWMMotorController::Set(double speed) { m_pwm.SetSpeed(m_isInverted ? -speed : speed); } -double PWMSpeedController::Get() const { +double PWMMotorController::Get() const { return m_pwm.GetSpeed() * (m_isInverted ? -1.0 : 1.0); } -void PWMSpeedController::SetInverted(bool isInverted) { +void PWMMotorController::SetInverted(bool isInverted) { m_isInverted = isInverted; } -bool PWMSpeedController::GetInverted() const { +bool PWMMotorController::GetInverted() const { return m_isInverted; } -void PWMSpeedController::Disable() { +void PWMMotorController::Disable() { m_pwm.SetDisabled(); } -void PWMSpeedController::StopMotor() { +void PWMMotorController::StopMotor() { Disable(); } -void PWMSpeedController::GetDescription(wpi::raw_ostream& desc) const { +void PWMMotorController::GetDescription(wpi::raw_ostream& desc) const { desc << "PWM " << GetChannel(); } -int PWMSpeedController::GetChannel() const { +int PWMMotorController::GetChannel() const { return m_pwm.GetChannel(); } -PWMSpeedController::PWMSpeedController(const wpi::Twine& name, int channel) +PWMMotorController::PWMMotorController(const wpi::Twine& name, int channel) : m_pwm(channel, false) { SendableRegistry::GetInstance().AddLW(this, name, channel); } -void PWMSpeedController::InitSendable(SendableBuilder& builder) { - builder.SetSmartDashboardType("Speed Controller"); +void PWMMotorController::InitSendable(SendableBuilder& builder) { + builder.SetSmartDashboardType("Motor Controller"); builder.SetActuator(true); builder.SetSafeState([=] { Disable(); }); builder.AddDoubleProperty( diff --git a/wpilibc/src/main/native/cpp/PWMSparkMax.cpp b/wpilibc/src/main/native/cpp/motorcontrol/PWMSparkMax.cpp similarity index 86% rename from wpilibc/src/main/native/cpp/PWMSparkMax.cpp rename to wpilibc/src/main/native/cpp/motorcontrol/PWMSparkMax.cpp index 2e4a08e9be..666f1e928c 100644 --- a/wpilibc/src/main/native/cpp/PWMSparkMax.cpp +++ b/wpilibc/src/main/native/cpp/motorcontrol/PWMSparkMax.cpp @@ -2,7 +2,7 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -#include "frc/PWMSparkMax.h" +#include "frc/motorcontrol/PWMSparkMax.h" #include @@ -11,7 +11,7 @@ using namespace frc; PWMSparkMax::PWMSparkMax(int channel) - : PWMSpeedController("PWMSparkMax", channel) { + : PWMMotorController("PWMSparkMax", channel) { m_pwm.SetBounds(2.003, 1.55, 1.50, 1.46, 0.999); m_pwm.SetPeriodMultiplier(PWM::kPeriodMultiplier_1X); m_pwm.SetSpeed(0.0); diff --git a/wpilibc/src/main/native/cpp/PWMTalonFX.cpp b/wpilibc/src/main/native/cpp/motorcontrol/PWMTalonFX.cpp similarity index 85% rename from wpilibc/src/main/native/cpp/PWMTalonFX.cpp rename to wpilibc/src/main/native/cpp/motorcontrol/PWMTalonFX.cpp index 7b85cf8bd6..2c6982b132 100644 --- a/wpilibc/src/main/native/cpp/PWMTalonFX.cpp +++ b/wpilibc/src/main/native/cpp/motorcontrol/PWMTalonFX.cpp @@ -2,14 +2,14 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -#include "frc/PWMTalonFX.h" +#include "frc/motorcontrol/PWMTalonFX.h" #include using namespace frc; PWMTalonFX::PWMTalonFX(int channel) - : PWMSpeedController("PWMTalonFX", channel) { + : PWMMotorController("PWMTalonFX", channel) { m_pwm.SetBounds(2.004, 1.52, 1.50, 1.48, 0.997); m_pwm.SetPeriodMultiplier(PWM::kPeriodMultiplier_1X); m_pwm.SetSpeed(0.0); diff --git a/wpilibc/src/main/native/cpp/PWMTalonSRX.cpp b/wpilibc/src/main/native/cpp/motorcontrol/PWMTalonSRX.cpp similarity index 85% rename from wpilibc/src/main/native/cpp/PWMTalonSRX.cpp rename to wpilibc/src/main/native/cpp/motorcontrol/PWMTalonSRX.cpp index b70c706760..b253412766 100644 --- a/wpilibc/src/main/native/cpp/PWMTalonSRX.cpp +++ b/wpilibc/src/main/native/cpp/motorcontrol/PWMTalonSRX.cpp @@ -2,14 +2,14 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -#include "frc/PWMTalonSRX.h" +#include "frc/motorcontrol/PWMTalonSRX.h" #include using namespace frc; PWMTalonSRX::PWMTalonSRX(int channel) - : PWMSpeedController("PWMTalonSRX", channel) { + : PWMMotorController("PWMTalonSRX", channel) { m_pwm.SetBounds(2.004, 1.52, 1.50, 1.48, 0.997); m_pwm.SetPeriodMultiplier(PWM::kPeriodMultiplier_1X); m_pwm.SetSpeed(0.0); diff --git a/wpilibc/src/main/native/cpp/PWMVenom.cpp b/wpilibc/src/main/native/cpp/motorcontrol/PWMVenom.cpp similarity index 83% rename from wpilibc/src/main/native/cpp/PWMVenom.cpp rename to wpilibc/src/main/native/cpp/motorcontrol/PWMVenom.cpp index 75dc008862..e55802837a 100644 --- a/wpilibc/src/main/native/cpp/PWMVenom.cpp +++ b/wpilibc/src/main/native/cpp/motorcontrol/PWMVenom.cpp @@ -2,13 +2,13 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -#include "frc/PWMVenom.h" +#include "frc/motorcontrol/PWMVenom.h" #include using namespace frc; -PWMVenom::PWMVenom(int channel) : PWMSpeedController("PWMVenom", channel) { +PWMVenom::PWMVenom(int channel) : PWMMotorController("PWMVenom", channel) { m_pwm.SetBounds(2.004, 1.52, 1.50, 1.48, 0.997); m_pwm.SetPeriodMultiplier(PWM::kPeriodMultiplier_1X); m_pwm.SetSpeed(0.0); diff --git a/wpilibc/src/main/native/cpp/PWMVictorSPX.cpp b/wpilibc/src/main/native/cpp/motorcontrol/PWMVictorSPX.cpp similarity index 84% rename from wpilibc/src/main/native/cpp/PWMVictorSPX.cpp rename to wpilibc/src/main/native/cpp/motorcontrol/PWMVictorSPX.cpp index 89cb207986..10ce992114 100644 --- a/wpilibc/src/main/native/cpp/PWMVictorSPX.cpp +++ b/wpilibc/src/main/native/cpp/motorcontrol/PWMVictorSPX.cpp @@ -2,14 +2,14 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -#include "frc/PWMVictorSPX.h" +#include "frc/motorcontrol/PWMVictorSPX.h" #include using namespace frc; PWMVictorSPX::PWMVictorSPX(int channel) - : PWMSpeedController("PWMVictorSPX", channel) { + : PWMMotorController("PWMVictorSPX", channel) { m_pwm.SetBounds(2.004, 1.52, 1.50, 1.48, 0.997); m_pwm.SetPeriodMultiplier(PWM::kPeriodMultiplier_1X); m_pwm.SetSpeed(0.0); diff --git a/wpilibc/src/main/native/cpp/SD540.cpp b/wpilibc/src/main/native/cpp/motorcontrol/SD540.cpp similarity index 83% rename from wpilibc/src/main/native/cpp/SD540.cpp rename to wpilibc/src/main/native/cpp/motorcontrol/SD540.cpp index 2ff10f5814..3d5738f3bc 100644 --- a/wpilibc/src/main/native/cpp/SD540.cpp +++ b/wpilibc/src/main/native/cpp/motorcontrol/SD540.cpp @@ -2,13 +2,13 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -#include "frc/SD540.h" +#include "frc/motorcontrol/SD540.h" #include using namespace frc; -SD540::SD540(int channel) : PWMSpeedController("SD540", channel) { +SD540::SD540(int channel) : PWMMotorController("SD540", channel) { m_pwm.SetBounds(2.05, 1.55, 1.50, 1.44, 0.94); m_pwm.SetPeriodMultiplier(PWM::kPeriodMultiplier_1X); m_pwm.SetSpeed(0.0); diff --git a/wpilibc/src/main/native/cpp/Spark.cpp b/wpilibc/src/main/native/cpp/motorcontrol/Spark.cpp similarity index 83% rename from wpilibc/src/main/native/cpp/Spark.cpp rename to wpilibc/src/main/native/cpp/motorcontrol/Spark.cpp index 80db37d45f..45394df33d 100644 --- a/wpilibc/src/main/native/cpp/Spark.cpp +++ b/wpilibc/src/main/native/cpp/motorcontrol/Spark.cpp @@ -2,13 +2,13 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -#include "frc/Spark.h" +#include "frc/motorcontrol/Spark.h" #include using namespace frc; -Spark::Spark(int channel) : PWMSpeedController("Spark", channel) { +Spark::Spark(int channel) : PWMMotorController("Spark", channel) { m_pwm.SetBounds(2.003, 1.55, 1.50, 1.46, 0.999); m_pwm.SetPeriodMultiplier(PWM::kPeriodMultiplier_1X); m_pwm.SetSpeed(0.0); diff --git a/wpilibc/src/main/native/cpp/Talon.cpp b/wpilibc/src/main/native/cpp/motorcontrol/Talon.cpp similarity index 83% rename from wpilibc/src/main/native/cpp/Talon.cpp rename to wpilibc/src/main/native/cpp/motorcontrol/Talon.cpp index 7424157053..f4b3b69740 100644 --- a/wpilibc/src/main/native/cpp/Talon.cpp +++ b/wpilibc/src/main/native/cpp/motorcontrol/Talon.cpp @@ -2,13 +2,13 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -#include "frc/Talon.h" +#include "frc/motorcontrol/Talon.h" #include using namespace frc; -Talon::Talon(int channel) : PWMSpeedController("Talon", channel) { +Talon::Talon(int channel) : PWMMotorController("Talon", channel) { m_pwm.SetBounds(2.037, 1.539, 1.513, 1.487, 0.989); m_pwm.SetPeriodMultiplier(PWM::kPeriodMultiplier_1X); m_pwm.SetSpeed(0.0); diff --git a/wpilibc/src/main/native/cpp/Victor.cpp b/wpilibc/src/main/native/cpp/motorcontrol/Victor.cpp similarity index 83% rename from wpilibc/src/main/native/cpp/Victor.cpp rename to wpilibc/src/main/native/cpp/motorcontrol/Victor.cpp index a6d4805022..3ad29f73ee 100644 --- a/wpilibc/src/main/native/cpp/Victor.cpp +++ b/wpilibc/src/main/native/cpp/motorcontrol/Victor.cpp @@ -2,13 +2,13 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -#include "frc/Victor.h" +#include "frc/motorcontrol/Victor.h" #include using namespace frc; -Victor::Victor(int channel) : PWMSpeedController("Victor", channel) { +Victor::Victor(int channel) : PWMMotorController("Victor", channel) { m_pwm.SetBounds(2.027, 1.525, 1.507, 1.49, 1.026); m_pwm.SetPeriodMultiplier(PWM::kPeriodMultiplier_2X); m_pwm.SetSpeed(0.0); diff --git a/wpilibc/src/main/native/cpp/VictorSP.cpp b/wpilibc/src/main/native/cpp/motorcontrol/VictorSP.cpp similarity index 83% rename from wpilibc/src/main/native/cpp/VictorSP.cpp rename to wpilibc/src/main/native/cpp/motorcontrol/VictorSP.cpp index f4da973b59..6dc888ef1a 100644 --- a/wpilibc/src/main/native/cpp/VictorSP.cpp +++ b/wpilibc/src/main/native/cpp/motorcontrol/VictorSP.cpp @@ -2,13 +2,13 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -#include "frc/VictorSP.h" +#include "frc/motorcontrol/VictorSP.h" #include using namespace frc; -VictorSP::VictorSP(int channel) : PWMSpeedController("VictorSP", channel) { +VictorSP::VictorSP(int channel) : PWMMotorController("VictorSP", channel) { m_pwm.SetBounds(2.004, 1.52, 1.50, 1.48, 0.997); m_pwm.SetPeriodMultiplier(PWM::kPeriodMultiplier_1X); m_pwm.SetSpeed(0.0); diff --git a/wpilibc/src/main/native/include/frc/Servo.h b/wpilibc/src/main/native/include/frc/Servo.h index aa14185aba..feaaf0f557 100644 --- a/wpilibc/src/main/native/include/frc/Servo.h +++ b/wpilibc/src/main/native/include/frc/Servo.h @@ -5,7 +5,6 @@ #pragma once #include "frc/PWM.h" -#include "frc/SpeedController.h" namespace frc { diff --git a/wpilibc/src/main/native/include/frc/SpeedController.h b/wpilibc/src/main/native/include/frc/SpeedController.h index fcb5de9b68..a1827b4c3a 100644 --- a/wpilibc/src/main/native/include/frc/SpeedController.h +++ b/wpilibc/src/main/native/include/frc/SpeedController.h @@ -5,13 +5,14 @@ #pragma once #include +#include namespace frc { /** * Interface for speed controlling devices. */ -class SpeedController { +class WPI_DEPRECATED("use MotorController") SpeedController { public: virtual ~SpeedController() = default; diff --git a/wpilibc/src/main/native/include/frc/SpeedControllerGroup.h b/wpilibc/src/main/native/include/frc/SpeedControllerGroup.h index 2b621109b4..5552c80ebe 100644 --- a/wpilibc/src/main/native/include/frc/SpeedControllerGroup.h +++ b/wpilibc/src/main/native/include/frc/SpeedControllerGroup.h @@ -7,15 +7,18 @@ #include #include -#include "frc/SpeedController.h" +#include + +#include "frc/motorcontrol/MotorController.h" #include "frc/smartdashboard/Sendable.h" #include "frc/smartdashboard/SendableHelper.h" namespace frc { -class SpeedControllerGroup : public Sendable, - public SpeedController, - public SendableHelper { +class WPI_DEPRECATED("use MotorControllerGroup") SpeedControllerGroup + : public Sendable, + public MotorController, + public SendableHelper { public: template explicit SpeedControllerGroup(SpeedController& speedController, diff --git a/wpilibc/src/main/native/include/frc/drive/DifferentialDrive.h b/wpilibc/src/main/native/include/frc/drive/DifferentialDrive.h index dc24959a80..57a125e558 100644 --- a/wpilibc/src/main/native/include/frc/drive/DifferentialDrive.h +++ b/wpilibc/src/main/native/include/frc/drive/DifferentialDrive.h @@ -12,6 +12,17 @@ namespace frc { +#if defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable : 4996) // was declared deprecated +#elif defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" +#elif defined(__GNUC__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" +#endif + class SpeedController; /** @@ -19,9 +30,9 @@ class SpeedController; * the Kit of Parts drive base, "tank drive", or West Coast Drive. * * These drive bases typically have drop-center / skid-steer with two or more - * wheels per side (e.g., 6WD or 8WD). This class takes a SpeedController per + * wheels per side (e.g., 6WD or 8WD). This class takes a MotorController per * side. For four and six motor drivetrains, construct and pass in - * SpeedControllerGroup instances as follows. + * MotorControllerGroup instances as follows. * * Four motor drivetrain: * @code{.cpp} @@ -29,11 +40,11 @@ class SpeedController; * public: * frc::PWMVictorSPX m_frontLeft{1}; * frc::PWMVictorSPX m_rearLeft{2}; - * frc::SpeedControllerGroup m_left{m_frontLeft, m_rearLeft}; + * frc::MotorControllerGroup m_left{m_frontLeft, m_rearLeft}; * * frc::PWMVictorSPX m_frontRight{3}; * frc::PWMVictorSPX m_rearRight{4}; - * frc::SpeedControllerGroup m_right{m_frontRight, m_rearRight}; + * frc::MotorControllerGroup m_right{m_frontRight, m_rearRight}; * * frc::DifferentialDrive m_drive{m_left, m_right}; * }; @@ -46,12 +57,12 @@ class SpeedController; * frc::PWMVictorSPX m_frontLeft{1}; * frc::PWMVictorSPX m_midLeft{2}; * frc::PWMVictorSPX m_rearLeft{3}; - * frc::SpeedControllerGroup m_left{m_frontLeft, m_midLeft, m_rearLeft}; + * frc::MotorControllerGroup m_left{m_frontLeft, m_midLeft, m_rearLeft}; * * frc::PWMVictorSPX m_frontRight{4}; * frc::PWMVictorSPX m_midRight{5}; * frc::PWMVictorSPX m_rearRight{6}; - * frc::SpeedControllerGroup m_right{m_frontRight, m_midRight, m_rearRight}; + * frc::MotorControllerGroup m_right{m_frontRight, m_midRight, m_rearRight}; * * frc::DifferentialDrive m_drive{m_left, m_right}; * }; @@ -105,7 +116,7 @@ class DifferentialDrive : public RobotDriveBase, /** * Construct a DifferentialDrive. * - * To pass multiple motors per side, use a SpeedControllerGroup. If a motor + * To pass multiple motors per side, use a MotorControllerGroup. If a motor * needs to be inverted, do so before passing it in. */ DifferentialDrive(SpeedController& leftMotor, SpeedController& rightMotor); @@ -219,4 +230,12 @@ class DifferentialDrive : public RobotDriveBase, double m_rightSideInvertMultiplier = -1.0; }; +#if defined(_MSC_VER) +#pragma warning(pop) +#elif defined(__clang__) +#pragma clang diagnostic pop +#elif defined(__GNUC__) +#pragma GCC diagnostic pop +#endif + } // namespace frc diff --git a/wpilibc/src/main/native/include/frc/drive/KilloughDrive.h b/wpilibc/src/main/native/include/frc/drive/KilloughDrive.h index d2ebd83d8d..25f370a77a 100644 --- a/wpilibc/src/main/native/include/frc/drive/KilloughDrive.h +++ b/wpilibc/src/main/native/include/frc/drive/KilloughDrive.h @@ -15,6 +15,17 @@ namespace frc { +#if defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable : 4996) // was declared deprecated +#elif defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" +#elif defined(__GNUC__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" +#endif + class SpeedController; /** @@ -140,4 +151,12 @@ class KilloughDrive : public RobotDriveBase, bool reported = false; }; +#if defined(_MSC_VER) +#pragma warning(pop) +#elif defined(__clang__) +#pragma clang diagnostic pop +#elif defined(__GNUC__) +#pragma GCC diagnostic pop +#endif + } // namespace frc diff --git a/wpilibc/src/main/native/include/frc/drive/MecanumDrive.h b/wpilibc/src/main/native/include/frc/drive/MecanumDrive.h index 13d56c5aa8..7c7c161f01 100644 --- a/wpilibc/src/main/native/include/frc/drive/MecanumDrive.h +++ b/wpilibc/src/main/native/include/frc/drive/MecanumDrive.h @@ -14,6 +14,17 @@ namespace frc { +#if defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable : 4996) // was declared deprecated +#elif defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" +#elif defined(__GNUC__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" +#endif + class SpeedController; /** @@ -145,4 +156,12 @@ class MecanumDrive : public RobotDriveBase, bool reported = false; }; +#if defined(_MSC_VER) +#pragma warning(pop) +#elif defined(__clang__) +#pragma clang diagnostic pop +#elif defined(__GNUC__) +#pragma GCC diagnostic pop +#endif + } // namespace frc diff --git a/wpilibc/src/main/native/include/frc/drive/RobotDriveBase.h b/wpilibc/src/main/native/include/frc/drive/RobotDriveBase.h index 830ed326b4..60c0ac38ac 100644 --- a/wpilibc/src/main/native/include/frc/drive/RobotDriveBase.h +++ b/wpilibc/src/main/native/include/frc/drive/RobotDriveBase.h @@ -13,8 +13,6 @@ namespace frc { -class SpeedController; - /** * Common base class for drive platforms. */ diff --git a/wpilibc/src/main/native/include/frc/DMC60.h b/wpilibc/src/main/native/include/frc/motorcontrol/DMC60.h similarity index 90% rename from wpilibc/src/main/native/include/frc/DMC60.h rename to wpilibc/src/main/native/include/frc/motorcontrol/DMC60.h index 492059ca86..928cceda07 100644 --- a/wpilibc/src/main/native/include/frc/DMC60.h +++ b/wpilibc/src/main/native/include/frc/motorcontrol/DMC60.h @@ -4,12 +4,12 @@ #pragma once -#include "frc/PWMSpeedController.h" +#include "frc/motorcontrol/PWMMotorController.h" namespace frc { /** - * Digilent DMC 60 Speed Controller. + * Digilent DMC 60 Motor Controller. * * Note that the DMC 60 uses the following bounds for PWM values. These * values should work reasonably well for most controllers, but if users @@ -24,7 +24,7 @@ namespace frc { * \li 1.480ms = the "low end" of the deadband range * \li 0.997ms = full "reverse" */ -class DMC60 : public PWMSpeedController { +class DMC60 : public PWMMotorController { public: /** * Constructor for a Digilent DMC 60. diff --git a/wpilibc/src/main/native/include/frc/Jaguar.h b/wpilibc/src/main/native/include/frc/motorcontrol/Jaguar.h similarity index 88% rename from wpilibc/src/main/native/include/frc/Jaguar.h rename to wpilibc/src/main/native/include/frc/motorcontrol/Jaguar.h index 15cbf7b3c7..461bf91947 100644 --- a/wpilibc/src/main/native/include/frc/Jaguar.h +++ b/wpilibc/src/main/native/include/frc/motorcontrol/Jaguar.h @@ -4,12 +4,12 @@ #pragma once -#include "frc/PWMSpeedController.h" +#include "frc/motorcontrol/PWMMotorController.h" namespace frc { /** - * Luminary Micro / Vex Robotics Jaguar Speed Controller with PWM control. + * Luminary Micro / Vex Robotics Jaguar Motor Controller with PWM control. * * Note that the Jaguar uses the following bounds for PWM values. These values * should work reasonably well for most controllers, but if users experience @@ -24,7 +24,7 @@ namespace frc { * \li 1.454ms = the "low end" of the deadband range * \li 0.697ms = full "reverse" */ -class Jaguar : public PWMSpeedController { +class Jaguar : public PWMMotorController { public: /** * Constructor for a Jaguar connected via PWM. diff --git a/wpilibc/src/main/native/include/frc/motorcontrol/MotorController.h b/wpilibc/src/main/native/include/frc/motorcontrol/MotorController.h new file mode 100644 index 0000000000..8ed19bcba5 --- /dev/null +++ b/wpilibc/src/main/native/include/frc/motorcontrol/MotorController.h @@ -0,0 +1,37 @@ +// Copyright (c) FIRST and other WPILib contributors. +// Open Source Software; you can modify and/or share it under the terms of +// the WPILib BSD license file in the root directory of this project. + +#pragma once + +#include + +#include "frc/SpeedController.h" + +namespace frc { + +#if defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable : 4996) // was declared deprecated +#elif defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" +#elif defined(__GNUC__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" +#endif + +/** + * Interface for motor controlling devices. + */ +class MotorController : public SpeedController {}; + +#if defined(_MSC_VER) +#pragma warning(pop) +#elif defined(__clang__) +#pragma clang diagnostic pop +#elif defined(__GNUC__) +#pragma GCC diagnostic pop +#endif + +} // namespace frc diff --git a/wpilibc/src/main/native/include/frc/motorcontrol/MotorControllerGroup.h b/wpilibc/src/main/native/include/frc/motorcontrol/MotorControllerGroup.h new file mode 100644 index 0000000000..584eb02da9 --- /dev/null +++ b/wpilibc/src/main/native/include/frc/motorcontrol/MotorControllerGroup.h @@ -0,0 +1,47 @@ +// Copyright (c) FIRST and other WPILib contributors. +// Open Source Software; you can modify and/or share it under the terms of +// the WPILib BSD license file in the root directory of this project. + +#pragma once + +#include +#include + +#include "frc/motorcontrol/MotorController.h" +#include "frc/smartdashboard/Sendable.h" +#include "frc/smartdashboard/SendableHelper.h" + +namespace frc { + +class MotorControllerGroup : public Sendable, + public MotorController, + public SendableHelper { + public: + template + explicit MotorControllerGroup(MotorController& motorController, + MotorControllers&... motorControllers); + explicit MotorControllerGroup( + std::vector>&& motorControllers); + + MotorControllerGroup(MotorControllerGroup&&) = default; + MotorControllerGroup& operator=(MotorControllerGroup&&) = default; + + void Set(double speed) override; + double Get() const override; + void SetInverted(bool isInverted) override; + bool GetInverted() const override; + void Disable() override; + void StopMotor() override; + + void InitSendable(SendableBuilder& builder) override; + + private: + bool m_isInverted = false; + std::vector> m_motorControllers; + + void Initialize(); +}; + +} // namespace frc + +#include "frc/motorcontrol/MotorControllerGroup.inc" diff --git a/wpilibc/src/main/native/include/frc/motorcontrol/MotorControllerGroup.inc b/wpilibc/src/main/native/include/frc/motorcontrol/MotorControllerGroup.inc new file mode 100644 index 0000000000..8f876354a1 --- /dev/null +++ b/wpilibc/src/main/native/include/frc/motorcontrol/MotorControllerGroup.inc @@ -0,0 +1,22 @@ +// Copyright (c) FIRST and other WPILib contributors. +// Open Source Software; you can modify and/or share it under the terms of +// the WPILib BSD license file in the root directory of this project. + +#pragma once + +#include +#include + +#include "frc/motorcontrol/MotorControllerGroup.h" + +namespace frc { + +template +MotorControllerGroup::MotorControllerGroup( + MotorController& motorController, MotorControllers&... motorControllers) + : m_motorControllers(std::vector>{ + motorController, motorControllers...}) { + Initialize(); +} + +} // namespace frc diff --git a/wpilibc/src/main/native/include/frc/NidecBrushless.h b/wpilibc/src/main/native/include/frc/motorcontrol/NidecBrushless.h similarity index 95% rename from wpilibc/src/main/native/include/frc/NidecBrushless.h rename to wpilibc/src/main/native/include/frc/motorcontrol/NidecBrushless.h index 5e1becaae6..f92e0f3759 100644 --- a/wpilibc/src/main/native/include/frc/NidecBrushless.h +++ b/wpilibc/src/main/native/include/frc/motorcontrol/NidecBrushless.h @@ -8,7 +8,7 @@ #include "frc/ErrorBase.h" #include "frc/MotorSafety.h" #include "frc/PWM.h" -#include "frc/SpeedController.h" +#include "frc/motorcontrol/MotorController.h" #include "frc/smartdashboard/Sendable.h" #include "frc/smartdashboard/SendableHelper.h" @@ -19,7 +19,7 @@ class SendableBuilder; /** * Nidec Brushless Motor. */ -class NidecBrushless : public SpeedController, +class NidecBrushless : public MotorController, public MotorSafety, public Sendable, public SendableHelper { @@ -39,7 +39,7 @@ class NidecBrushless : public SpeedController, NidecBrushless(NidecBrushless&&) = default; NidecBrushless& operator=(NidecBrushless&&) = default; - // SpeedController interface + // MotorController interface /** * Set the PWM value. * diff --git a/wpilibc/src/main/native/include/frc/PWMSpeedController.h b/wpilibc/src/main/native/include/frc/motorcontrol/PWMMotorController.h similarity index 76% rename from wpilibc/src/main/native/include/frc/PWMSpeedController.h rename to wpilibc/src/main/native/include/frc/motorcontrol/PWMMotorController.h index 786855e756..eb85b0a992 100644 --- a/wpilibc/src/main/native/include/frc/PWMSpeedController.h +++ b/wpilibc/src/main/native/include/frc/motorcontrol/PWMMotorController.h @@ -8,7 +8,7 @@ #include "frc/MotorSafety.h" #include "frc/PWM.h" -#include "frc/SpeedController.h" +#include "frc/motorcontrol/MotorController.h" #include "frc/smartdashboard/Sendable.h" #include "frc/smartdashboard/SendableHelper.h" @@ -19,15 +19,15 @@ class raw_ostream; namespace frc { /** - * Common base class for all PWM Speed Controllers. + * Common base class for all PWM Motor Controllers. */ -class PWMSpeedController : public SpeedController, +class PWMMotorController : public MotorController, public MotorSafety, public Sendable, - public SendableHelper { + public SendableHelper { public: - PWMSpeedController(PWMSpeedController&&) = default; - PWMSpeedController& operator=(PWMSpeedController&&) = default; + PWMMotorController(PWMMotorController&&) = default; + PWMMotorController& operator=(PWMMotorController&&) = default; /** * Set the PWM value. @@ -42,7 +42,7 @@ class PWMSpeedController : public SpeedController, /** * Get the recently set value of the PWM. This value is affected by the * inversion property. If you want the value that is sent directly to the - * SpeedController, use {@link PWM#getSpeed()} instead. + * MotorController, use {@link PWM#getSpeed()} instead. * * @return The most recently set value for the PWM between -1.0 and 1.0. */ @@ -62,13 +62,13 @@ class PWMSpeedController : public SpeedController, protected: /** - * Constructor for a PWM Speed Controller connected via PWM. + * Constructor for a PWM Motor Controller connected via PWM. * * @param name Name to use for SendableRegistry * @param channel The PWM channel that the controller is attached to. 0-9 are * on-board, 10-19 are on the MXP port */ - PWMSpeedController(const wpi::Twine& name, int channel); + PWMMotorController(const wpi::Twine& name, int channel); void InitSendable(SendableBuilder& builder) override; diff --git a/wpilibc/src/main/native/include/frc/PWMSparkMax.h b/wpilibc/src/main/native/include/frc/motorcontrol/PWMSparkMax.h similarity index 89% rename from wpilibc/src/main/native/include/frc/PWMSparkMax.h rename to wpilibc/src/main/native/include/frc/motorcontrol/PWMSparkMax.h index 2a32d996a2..9b6b65aba4 100644 --- a/wpilibc/src/main/native/include/frc/PWMSparkMax.h +++ b/wpilibc/src/main/native/include/frc/motorcontrol/PWMSparkMax.h @@ -4,12 +4,12 @@ #pragma once -#include "frc/PWMSpeedController.h" +#include "frc/motorcontrol/PWMMotorController.h" namespace frc { /** - * REV Robotics SPARK MAX Speed Controller. + * REV Robotics SPARK MAX Motor Controller. * * Note that the SPARK MAX uses the following bounds for PWM values. These * values should work reasonably well for most controllers, but if users @@ -24,7 +24,7 @@ namespace frc { * \li 1.460ms = the "low end" of the deadband range * \li 0.999ms = full "reverse" */ -class PWMSparkMax : public PWMSpeedController { +class PWMSparkMax : public PWMMotorController { public: /** * Constructor for a SPARK MAX. diff --git a/wpilibc/src/main/native/include/frc/PWMTalonFX.h b/wpilibc/src/main/native/include/frc/motorcontrol/PWMTalonFX.h similarity index 88% rename from wpilibc/src/main/native/include/frc/PWMTalonFX.h rename to wpilibc/src/main/native/include/frc/motorcontrol/PWMTalonFX.h index 406d8afd0a..38d24248ee 100644 --- a/wpilibc/src/main/native/include/frc/PWMTalonFX.h +++ b/wpilibc/src/main/native/include/frc/motorcontrol/PWMTalonFX.h @@ -4,12 +4,12 @@ #pragma once -#include "frc/PWMSpeedController.h" +#include "frc/motorcontrol/PWMMotorController.h" namespace frc { /** - * Cross the Road Electronics (CTRE) Talon FX Speed Controller with PWM + * Cross the Road Electronics (CTRE) Talon FX Motor Controller with PWM * control. * * Note that the Talon FX uses the following bounds for PWM values. These @@ -25,7 +25,7 @@ namespace frc { * \li 1.480ms = the "low end" of the deadband range * \li 0.997ms = full "reverse" */ -class PWMTalonFX : public PWMSpeedController { +class PWMTalonFX : public PWMMotorController { public: /** * Construct a Talon FX connected via PWM. diff --git a/wpilibc/src/main/native/include/frc/PWMTalonSRX.h b/wpilibc/src/main/native/include/frc/motorcontrol/PWMTalonSRX.h similarity index 88% rename from wpilibc/src/main/native/include/frc/PWMTalonSRX.h rename to wpilibc/src/main/native/include/frc/motorcontrol/PWMTalonSRX.h index c7e01e690d..6b0e1c2871 100644 --- a/wpilibc/src/main/native/include/frc/PWMTalonSRX.h +++ b/wpilibc/src/main/native/include/frc/motorcontrol/PWMTalonSRX.h @@ -4,12 +4,12 @@ #pragma once -#include "frc/PWMSpeedController.h" +#include "frc/motorcontrol/PWMMotorController.h" namespace frc { /** - * Cross the Road Electronics (CTRE) Talon SRX Speed Controller with PWM + * Cross the Road Electronics (CTRE) Talon SRX Motor Controller with PWM * control. * * Note that the Talon SRX uses the following bounds for PWM values. These @@ -25,7 +25,7 @@ namespace frc { * \li 1.480ms = the "low end" of the deadband range * \li 0.997ms = full "reverse" */ -class PWMTalonSRX : public PWMSpeedController { +class PWMTalonSRX : public PWMMotorController { public: /** * Construct a Talon SRX connected via PWM. diff --git a/wpilibc/src/main/native/include/frc/PWMVenom.h b/wpilibc/src/main/native/include/frc/motorcontrol/PWMVenom.h similarity index 92% rename from wpilibc/src/main/native/include/frc/PWMVenom.h rename to wpilibc/src/main/native/include/frc/motorcontrol/PWMVenom.h index 60526b31fb..1047e8e7fc 100644 --- a/wpilibc/src/main/native/include/frc/PWMVenom.h +++ b/wpilibc/src/main/native/include/frc/motorcontrol/PWMVenom.h @@ -4,7 +4,7 @@ #pragma once -#include "frc/PWMSpeedController.h" +#include "frc/motorcontrol/PWMMotorController.h" namespace frc { @@ -23,7 +23,7 @@ namespace frc { * \li 1.480ms = the "low end" of the deadband range * \li 0.997ms = full "reverse" */ -class PWMVenom : public PWMSpeedController { +class PWMVenom : public PWMMotorController { public: /** * Construct a Venom connected via PWM. diff --git a/wpilibc/src/main/native/include/frc/PWMVictorSPX.h b/wpilibc/src/main/native/include/frc/motorcontrol/PWMVictorSPX.h similarity index 88% rename from wpilibc/src/main/native/include/frc/PWMVictorSPX.h rename to wpilibc/src/main/native/include/frc/motorcontrol/PWMVictorSPX.h index ecba52a974..b0369966e1 100644 --- a/wpilibc/src/main/native/include/frc/PWMVictorSPX.h +++ b/wpilibc/src/main/native/include/frc/motorcontrol/PWMVictorSPX.h @@ -4,12 +4,12 @@ #pragma once -#include "frc/PWMSpeedController.h" +#include "frc/motorcontrol/PWMMotorController.h" namespace frc { /** - * Cross the Road Electronics (CTRE) Victor SPX Speed Controller with PWM + * Cross the Road Electronics (CTRE) Victor SPX Motor Controller with PWM * control. * * Note that the Victor SPX uses the following bounds for PWM values. These @@ -25,7 +25,7 @@ namespace frc { * \li 1.480ms = the "low end" of the deadband range * \li 0.997ms = full "reverse" */ -class PWMVictorSPX : public PWMSpeedController { +class PWMVictorSPX : public PWMMotorController { public: /** * Construct a Victor SPX connected via PWM. diff --git a/wpilibc/src/main/native/include/frc/SD540.h b/wpilibc/src/main/native/include/frc/motorcontrol/SD540.h similarity index 90% rename from wpilibc/src/main/native/include/frc/SD540.h rename to wpilibc/src/main/native/include/frc/motorcontrol/SD540.h index 101a677dac..b292c34886 100644 --- a/wpilibc/src/main/native/include/frc/SD540.h +++ b/wpilibc/src/main/native/include/frc/motorcontrol/SD540.h @@ -4,12 +4,12 @@ #pragma once -#include "frc/PWMSpeedController.h" +#include "frc/motorcontrol/PWMMotorController.h" namespace frc { /** - * Mindsensors SD540 Speed Controller. + * Mindsensors SD540 Motor Controller. * * Note that the SD540 uses the following bounds for PWM values. These values * should work reasonably well for most controllers, but if users experience @@ -24,7 +24,7 @@ namespace frc { * \li 1.44ms = the "low end" of the deadband range * \li 0.94ms = full "reverse" */ -class SD540 : public PWMSpeedController { +class SD540 : public PWMMotorController { public: /** * Constructor for a SD540. diff --git a/wpilibc/src/main/native/include/frc/Spark.h b/wpilibc/src/main/native/include/frc/motorcontrol/Spark.h similarity index 90% rename from wpilibc/src/main/native/include/frc/Spark.h rename to wpilibc/src/main/native/include/frc/motorcontrol/Spark.h index 7ec2b82c1d..9391d25251 100644 --- a/wpilibc/src/main/native/include/frc/Spark.h +++ b/wpilibc/src/main/native/include/frc/motorcontrol/Spark.h @@ -4,12 +4,12 @@ #pragma once -#include "frc/PWMSpeedController.h" +#include "frc/motorcontrol/PWMMotorController.h" namespace frc { /** - * REV Robotics SPARK Speed Controller. + * REV Robotics SPARK Motor Controller. * * Note that the SPARK uses the following bounds for PWM values. These values * should work reasonably well for most controllers, but if users experience @@ -24,7 +24,7 @@ namespace frc { * \li 1.460ms = the "low end" of the deadband range * \li 0.999ms = full "reverse" */ -class Spark : public PWMSpeedController { +class Spark : public PWMMotorController { public: /** * Constructor for a SPARK. diff --git a/wpilibc/src/main/native/include/frc/Talon.h b/wpilibc/src/main/native/include/frc/motorcontrol/Talon.h similarity index 88% rename from wpilibc/src/main/native/include/frc/Talon.h rename to wpilibc/src/main/native/include/frc/motorcontrol/Talon.h index 510eba512a..dbeb836ea8 100644 --- a/wpilibc/src/main/native/include/frc/Talon.h +++ b/wpilibc/src/main/native/include/frc/motorcontrol/Talon.h @@ -4,12 +4,12 @@ #pragma once -#include "frc/PWMSpeedController.h" +#include "frc/motorcontrol/PWMMotorController.h" namespace frc { /** - * Cross the Road Electronics (CTRE) Talon and Talon SR Speed Controller. + * Cross the Road Electronics (CTRE) Talon and Talon SR Motor Controller. * * Note that the Talon uses the following bounds for PWM values. These values * should work reasonably well for most controllers, but if users experience @@ -24,7 +24,7 @@ namespace frc { * \li 1.487ms = the "low end" of the deadband range * \li 0.989ms = full "reverse" */ -class Talon : public PWMSpeedController { +class Talon : public PWMMotorController { public: /** * Constructor for a Talon (original or Talon SR). diff --git a/wpilibc/src/main/native/include/frc/Victor.h b/wpilibc/src/main/native/include/frc/motorcontrol/Victor.h similarity index 87% rename from wpilibc/src/main/native/include/frc/Victor.h rename to wpilibc/src/main/native/include/frc/motorcontrol/Victor.h index 600e9a17aa..670eb3dbf2 100644 --- a/wpilibc/src/main/native/include/frc/Victor.h +++ b/wpilibc/src/main/native/include/frc/motorcontrol/Victor.h @@ -4,14 +4,14 @@ #pragma once -#include "frc/PWMSpeedController.h" +#include "frc/motorcontrol/PWMMotorController.h" namespace frc { /** - * Vex Robotics Victor 888 Speed Controller. + * Vex Robotics Victor 888 Motor Controller. * - * The Vex Robotics Victor 884 Speed Controller can also be used with this + * The Vex Robotics Victor 884 Motor Controller can also be used with this * class but may need to be calibrated per the Victor 884 user manual. * * Note that the Victor uses the following bounds for PWM values. These @@ -28,7 +28,7 @@ namespace frc { * \li 1.490ms = the "low end" of the deadband range * \li 1.026ms = full "reverse" */ -class Victor : public PWMSpeedController { +class Victor : public PWMMotorController { public: /** * Constructor for a Victor. diff --git a/wpilibc/src/main/native/include/frc/VictorSP.h b/wpilibc/src/main/native/include/frc/motorcontrol/VictorSP.h similarity index 89% rename from wpilibc/src/main/native/include/frc/VictorSP.h rename to wpilibc/src/main/native/include/frc/motorcontrol/VictorSP.h index 941a3ec6ee..534e83afd7 100644 --- a/wpilibc/src/main/native/include/frc/VictorSP.h +++ b/wpilibc/src/main/native/include/frc/motorcontrol/VictorSP.h @@ -4,12 +4,12 @@ #pragma once -#include "frc/PWMSpeedController.h" +#include "frc/motorcontrol/PWMMotorController.h" namespace frc { /** - * Vex Robotics Victor SP Speed Controller. + * Vex Robotics Victor SP Motor Controller. * * Note that the Victor SP uses the following bounds for PWM values. These * values should work reasonably well for most controllers, but if users @@ -24,7 +24,7 @@ namespace frc { * \li 1.480ms = the "low end" of the deadband range * \li 0.997ms = full "reverse" */ -class VictorSP : public PWMSpeedController { +class VictorSP : public PWMMotorController { public: /** * Constructor for a Victor SP. diff --git a/wpilibc/src/main/native/include/frc/shuffleboard/BuiltInWidgets.h b/wpilibc/src/main/native/include/frc/shuffleboard/BuiltInWidgets.h index b2f5991b91..1b959ff4bf 100644 --- a/wpilibc/src/main/native/include/frc/shuffleboard/BuiltInWidgets.h +++ b/wpilibc/src/main/native/include/frc/shuffleboard/BuiltInWidgets.h @@ -200,10 +200,10 @@ enum class BuiltInWidgets { */ kEncoder, /** - * Displays a SpeedController. + * Displays a MotorController. * The speed controller will be controllable from the dashboard when test mode * is enabled, but will otherwise be view-only.
Supported types:

    - *
  • PWMSpeedController
  • + *
  • PWMMotorController
  • *
  • DMC60
  • *
  • Jaguar
  • *
  • PWMTalonSRX
  • @@ -213,7 +213,7 @@ enum class BuiltInWidgets { *
  • Talon
  • *
  • Victor
  • *
  • VictorSP
  • - *
  • SpeedControllerGroup
  • + *
  • MotorControllerGroup
  • *
  • Any custom subclass of {@code SpeedContorller}
  • *
*
Custom properties: @@ -223,7 +223,7 @@ enum class BuiltInWidgets { * One of {@code ["HORIZONTAL", "VERTICAL"]} * */ - kSpeedController, + kMotorController, /** * Displays a command with a toggle button. Pressing the button will start the * command, and the button will automatically release when the command diff --git a/wpilibc/src/test/native/cpp/MockSpeedController.cpp b/wpilibc/src/test/native/cpp/MockMotorController.cpp similarity index 55% rename from wpilibc/src/test/native/cpp/MockSpeedController.cpp rename to wpilibc/src/test/native/cpp/MockMotorController.cpp index 31ccb075fd..62fd54a31c 100644 --- a/wpilibc/src/test/native/cpp/MockSpeedController.cpp +++ b/wpilibc/src/test/native/cpp/MockMotorController.cpp @@ -2,30 +2,30 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -#include "MockSpeedController.h" +#include "MockMotorController.h" using namespace frc; -void MockSpeedController::Set(double speed) { +void MockMotorController::Set(double speed) { m_speed = m_isInverted ? -speed : speed; } -double MockSpeedController::Get() const { +double MockMotorController::Get() const { return m_speed; } -void MockSpeedController::SetInverted(bool isInverted) { +void MockMotorController::SetInverted(bool isInverted) { m_isInverted = isInverted; } -bool MockSpeedController::GetInverted() const { +bool MockMotorController::GetInverted() const { return m_isInverted; } -void MockSpeedController::Disable() { +void MockMotorController::Disable() { m_speed = 0; } -void MockSpeedController::StopMotor() { +void MockMotorController::StopMotor() { Disable(); } diff --git a/wpilibc/src/test/native/cpp/SpeedControllerGroupTest.cpp b/wpilibc/src/test/native/cpp/SpeedControllerGroupTest.cpp index 0f62530db5..2b344afcfb 100644 --- a/wpilibc/src/test/native/cpp/SpeedControllerGroupTest.cpp +++ b/wpilibc/src/test/native/cpp/SpeedControllerGroupTest.cpp @@ -2,29 +2,29 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -#include "frc/SpeedControllerGroup.h" // NOLINT(build/include_order) +#include "frc/motorcontrol/MotorControllerGroup.h" // NOLINT(build/include_order) #include #include -#include "MockSpeedController.h" +#include "MockMotorController.h" #include "gtest/gtest.h" using namespace frc; -enum SpeedControllerGroupTestType { TEST_ONE, TEST_TWO, TEST_THREE }; +enum MotorControllerGroupTestType { TEST_ONE, TEST_TWO, TEST_THREE }; std::ostream& operator<<(std::ostream& os, - const SpeedControllerGroupTestType& type) { + const MotorControllerGroupTestType& type) { switch (type) { case TEST_ONE: - os << "SpeedControllerGroup with one speed controller"; + os << "MotorControllerGroup with one speed controller"; break; case TEST_TWO: - os << "SpeedControllerGroup with two speed controllers"; + os << "MotorControllerGroup with two speed controllers"; break; case TEST_THREE: - os << "SpeedControllerGroup with three speed controllers"; + os << "MotorControllerGroup with three speed controllers"; break; } @@ -32,26 +32,26 @@ std::ostream& operator<<(std::ostream& os, } /** - * A fixture used for SpeedControllerGroup testing. + * A fixture used for MotorControllerGroup testing. */ -class SpeedControllerGroupTest - : public testing::TestWithParam { +class MotorControllerGroupTest + : public testing::TestWithParam { protected: - std::vector m_speedControllers; - std::unique_ptr m_group; + std::vector m_speedControllers; + std::unique_ptr m_group; void SetUp() override { switch (GetParam()) { case TEST_ONE: { m_speedControllers.emplace_back(); - m_group = std::make_unique(m_speedControllers[0]); + m_group = std::make_unique(m_speedControllers[0]); break; } case TEST_TWO: { m_speedControllers.emplace_back(); m_speedControllers.emplace_back(); - m_group = std::make_unique(m_speedControllers[0], + m_group = std::make_unique(m_speedControllers[0], m_speedControllers[1]); break; } @@ -60,7 +60,7 @@ class SpeedControllerGroupTest m_speedControllers.emplace_back(); m_speedControllers.emplace_back(); m_speedControllers.emplace_back(); - m_group = std::make_unique(m_speedControllers[0], + m_group = std::make_unique(m_speedControllers[0], m_speedControllers[1], m_speedControllers[2]); break; @@ -69,7 +69,7 @@ class SpeedControllerGroupTest } }; -TEST_P(SpeedControllerGroupTest, Set) { +TEST_P(MotorControllerGroupTest, Set) { m_group->Set(1.0); for (auto& speedController : m_speedControllers) { @@ -77,13 +77,13 @@ TEST_P(SpeedControllerGroupTest, Set) { } } -TEST_P(SpeedControllerGroupTest, GetInverted) { +TEST_P(MotorControllerGroupTest, GetInverted) { m_group->SetInverted(true); EXPECT_TRUE(m_group->GetInverted()); } -TEST_P(SpeedControllerGroupTest, SetInvertedDoesNotModifySpeedControllers) { +TEST_P(MotorControllerGroupTest, SetInvertedDoesNotModifyMotorControllers) { for (auto& speedController : m_speedControllers) { speedController.SetInverted(false); } @@ -94,7 +94,7 @@ TEST_P(SpeedControllerGroupTest, SetInvertedDoesNotModifySpeedControllers) { } } -TEST_P(SpeedControllerGroupTest, SetInvertedDoesInvert) { +TEST_P(MotorControllerGroupTest, SetInvertedDoesInvert) { m_group->SetInverted(true); m_group->Set(1.0); @@ -103,7 +103,7 @@ TEST_P(SpeedControllerGroupTest, SetInvertedDoesInvert) { } } -TEST_P(SpeedControllerGroupTest, Disable) { +TEST_P(MotorControllerGroupTest, Disable) { m_group->Set(1.0); m_group->Disable(); @@ -112,7 +112,7 @@ TEST_P(SpeedControllerGroupTest, Disable) { } } -TEST_P(SpeedControllerGroupTest, StopMotor) { +TEST_P(MotorControllerGroupTest, StopMotor) { m_group->Set(1.0); m_group->StopMotor(); @@ -121,5 +121,5 @@ TEST_P(SpeedControllerGroupTest, StopMotor) { } } -INSTANTIATE_TEST_SUITE_P(Test, SpeedControllerGroupTest, +INSTANTIATE_TEST_SUITE_P(Test, MotorControllerGroupTest, testing::Values(TEST_ONE, TEST_TWO, TEST_THREE)); diff --git a/wpilibc/src/test/native/cpp/simulation/ElevatorSimTest.cpp b/wpilibc/src/test/native/cpp/simulation/ElevatorSimTest.cpp index 1083ad51a6..6e9bfbd93a 100644 --- a/wpilibc/src/test/native/cpp/simulation/ElevatorSimTest.cpp +++ b/wpilibc/src/test/native/cpp/simulation/ElevatorSimTest.cpp @@ -7,10 +7,10 @@ #include #include "frc/Encoder.h" -#include "frc/PWMVictorSPX.h" #include "frc/RobotController.h" #include "frc/StateSpaceUtil.h" #include "frc/controller/PIDController.h" +#include "frc/motorcontrol/PWMVictorSPX.h" #include "frc/simulation/ElevatorSim.h" #include "frc/simulation/EncoderSim.h" #include "frc/system/NumericalIntegration.h" diff --git a/wpilibc/src/test/native/cpp/simulation/StateSpaceSimTest.cpp b/wpilibc/src/test/native/cpp/simulation/StateSpaceSimTest.cpp index c53605e224..ba089d54c1 100644 --- a/wpilibc/src/test/native/cpp/simulation/StateSpaceSimTest.cpp +++ b/wpilibc/src/test/native/cpp/simulation/StateSpaceSimTest.cpp @@ -8,10 +8,10 @@ #include #include "frc/Encoder.h" -#include "frc/PWMVictorSPX.h" #include "frc/RobotController.h" #include "frc/controller/PIDController.h" #include "frc/controller/SimpleMotorFeedforward.h" +#include "frc/motorcontrol/PWMVictorSPX.h" #include "frc/simulation/BatterySim.h" #include "frc/simulation/DifferentialDrivetrainSim.h" #include "frc/simulation/ElevatorSim.h" diff --git a/wpilibc/src/test/native/include/MockSpeedController.h b/wpilibc/src/test/native/include/MockMotorController.h similarity index 84% rename from wpilibc/src/test/native/include/MockSpeedController.h rename to wpilibc/src/test/native/include/MockMotorController.h index a5e119bae9..e17931fbb6 100644 --- a/wpilibc/src/test/native/include/MockSpeedController.h +++ b/wpilibc/src/test/native/include/MockMotorController.h @@ -4,11 +4,11 @@ #pragma once -#include "frc/SpeedController.h" +#include "frc/motorcontrol/MotorController.h" namespace frc { -class MockSpeedController : public SpeedController { +class MockMotorController : public MotorController { public: void Set(double speed) override; double Get() const override; diff --git a/wpilibcExamples/src/main/cpp/examples/ArcadeDrive/cpp/Robot.cpp b/wpilibcExamples/src/main/cpp/examples/ArcadeDrive/cpp/Robot.cpp index 379520e4ca..abe79f48a8 100644 --- a/wpilibcExamples/src/main/cpp/examples/ArcadeDrive/cpp/Robot.cpp +++ b/wpilibcExamples/src/main/cpp/examples/ArcadeDrive/cpp/Robot.cpp @@ -3,9 +3,9 @@ // the WPILib BSD license file in the root directory of this project. #include -#include #include #include +#include /** * This is a demo program showing the use of the DifferentialDrive class. diff --git a/wpilibcExamples/src/main/cpp/examples/ArcadeDriveXboxController/cpp/Robot.cpp b/wpilibcExamples/src/main/cpp/examples/ArcadeDriveXboxController/cpp/Robot.cpp index 7f34b09cae..5b48f0adb4 100644 --- a/wpilibcExamples/src/main/cpp/examples/ArcadeDriveXboxController/cpp/Robot.cpp +++ b/wpilibcExamples/src/main/cpp/examples/ArcadeDriveXboxController/cpp/Robot.cpp @@ -3,10 +3,10 @@ // the WPILib BSD license file in the root directory of this project. #include -#include #include #include #include +#include /** * This is a demo program showing the use of the DifferentialDrive class. diff --git a/wpilibcExamples/src/main/cpp/examples/ArmBot/include/subsystems/ArmSubsystem.h b/wpilibcExamples/src/main/cpp/examples/ArmBot/include/subsystems/ArmSubsystem.h index 1b1c1ee109..45cd582117 100644 --- a/wpilibcExamples/src/main/cpp/examples/ArmBot/include/subsystems/ArmSubsystem.h +++ b/wpilibcExamples/src/main/cpp/examples/ArmBot/include/subsystems/ArmSubsystem.h @@ -5,8 +5,8 @@ #pragma once #include -#include #include +#include #include #include diff --git a/wpilibcExamples/src/main/cpp/examples/ArmBot/include/subsystems/DriveSubsystem.h b/wpilibcExamples/src/main/cpp/examples/ArmBot/include/subsystems/DriveSubsystem.h index 7aaac36654..47bf28e4d9 100644 --- a/wpilibcExamples/src/main/cpp/examples/ArmBot/include/subsystems/DriveSubsystem.h +++ b/wpilibcExamples/src/main/cpp/examples/ArmBot/include/subsystems/DriveSubsystem.h @@ -5,9 +5,9 @@ #pragma once #include -#include -#include #include +#include +#include #include #include "Constants.h" @@ -76,10 +76,10 @@ class DriveSubsystem : public frc2::SubsystemBase { frc::PWMSparkMax m_right2; // The motors on the left side of the drive - frc::SpeedControllerGroup m_leftMotors{m_left1, m_left2}; + frc::MotorControllerGroup m_leftMotors{m_left1, m_left2}; // The motors on the right side of the drive - frc::SpeedControllerGroup m_rightMotors{m_right1, m_right2}; + frc::MotorControllerGroup m_rightMotors{m_right1, m_right2}; // The robot's drive frc::DifferentialDrive m_drive{m_leftMotors, m_rightMotors}; diff --git a/wpilibcExamples/src/main/cpp/examples/ArmBotOffboard/include/ExampleSmartMotorController.h b/wpilibcExamples/src/main/cpp/examples/ArmBotOffboard/include/ExampleSmartMotorController.h index 9d7e5e54e8..5d55839892 100644 --- a/wpilibcExamples/src/main/cpp/examples/ArmBotOffboard/include/ExampleSmartMotorController.h +++ b/wpilibcExamples/src/main/cpp/examples/ArmBotOffboard/include/ExampleSmartMotorController.h @@ -4,7 +4,7 @@ #pragma once -#include +#include /** * A simplified stub class that simulates the API of a common "smart" motor @@ -12,7 +12,7 @@ * *

Has no actual functionality. */ -class ExampleSmartMotorController : public frc::SpeedController { +class ExampleSmartMotorController : public frc::MotorController { public: enum PIDMode { kPosition, kVelocity, kMovementWitchcraft }; diff --git a/wpilibcExamples/src/main/cpp/examples/ArmBotOffboard/include/subsystems/DriveSubsystem.h b/wpilibcExamples/src/main/cpp/examples/ArmBotOffboard/include/subsystems/DriveSubsystem.h index 7aaac36654..47bf28e4d9 100644 --- a/wpilibcExamples/src/main/cpp/examples/ArmBotOffboard/include/subsystems/DriveSubsystem.h +++ b/wpilibcExamples/src/main/cpp/examples/ArmBotOffboard/include/subsystems/DriveSubsystem.h @@ -5,9 +5,9 @@ #pragma once #include -#include -#include #include +#include +#include #include #include "Constants.h" @@ -76,10 +76,10 @@ class DriveSubsystem : public frc2::SubsystemBase { frc::PWMSparkMax m_right2; // The motors on the left side of the drive - frc::SpeedControllerGroup m_leftMotors{m_left1, m_left2}; + frc::MotorControllerGroup m_leftMotors{m_left1, m_left2}; // The motors on the right side of the drive - frc::SpeedControllerGroup m_rightMotors{m_right1, m_right2}; + frc::MotorControllerGroup m_rightMotors{m_right1, m_right2}; // The robot's drive frc::DifferentialDrive m_drive{m_leftMotors, m_rightMotors}; diff --git a/wpilibcExamples/src/main/cpp/examples/ArmSimulation/cpp/Robot.cpp b/wpilibcExamples/src/main/cpp/examples/ArmSimulation/cpp/Robot.cpp index 94ef9c25f7..b777af3e2e 100644 --- a/wpilibcExamples/src/main/cpp/examples/ArmSimulation/cpp/Robot.cpp +++ b/wpilibcExamples/src/main/cpp/examples/ArmSimulation/cpp/Robot.cpp @@ -5,11 +5,11 @@ #include #include #include -#include #include #include #include #include +#include #include #include #include diff --git a/wpilibcExamples/src/main/cpp/examples/DifferentialDriveBot/include/Drivetrain.h b/wpilibcExamples/src/main/cpp/examples/DifferentialDriveBot/include/Drivetrain.h index 57532e9b14..d7e9e078b6 100644 --- a/wpilibcExamples/src/main/cpp/examples/DifferentialDriveBot/include/Drivetrain.h +++ b/wpilibcExamples/src/main/cpp/examples/DifferentialDriveBot/include/Drivetrain.h @@ -6,12 +6,12 @@ #include #include -#include -#include #include #include #include #include +#include +#include #include #include #include @@ -57,8 +57,8 @@ class Drivetrain { frc::PWMSparkMax m_rightLeader{3}; frc::PWMSparkMax m_rightFollower{4}; - frc::SpeedControllerGroup m_leftGroup{m_leftLeader, m_leftFollower}; - frc::SpeedControllerGroup m_rightGroup{m_rightLeader, m_rightFollower}; + frc::MotorControllerGroup m_leftGroup{m_leftLeader, m_leftFollower}; + frc::MotorControllerGroup m_rightGroup{m_rightLeader, m_rightFollower}; frc::Encoder m_leftEncoder{0, 1}; frc::Encoder m_rightEncoder{2, 3}; diff --git a/wpilibcExamples/src/main/cpp/examples/DifferentialDrivePoseEstimator/include/Drivetrain.h b/wpilibcExamples/src/main/cpp/examples/DifferentialDrivePoseEstimator/include/Drivetrain.h index 37f05e40ca..e9b657be8a 100644 --- a/wpilibcExamples/src/main/cpp/examples/DifferentialDrivePoseEstimator/include/Drivetrain.h +++ b/wpilibcExamples/src/main/cpp/examples/DifferentialDrivePoseEstimator/include/Drivetrain.h @@ -6,12 +6,12 @@ #include #include -#include -#include #include #include #include #include +#include +#include #include #include #include @@ -57,8 +57,8 @@ class Drivetrain { frc::PWMSparkMax m_rightLeader{3}; frc::PWMSparkMax m_rightFollower{4}; - frc::SpeedControllerGroup m_leftGroup{m_leftLeader, m_leftFollower}; - frc::SpeedControllerGroup m_rightGroup{m_rightLeader, m_rightFollower}; + frc::MotorControllerGroup m_leftGroup{m_leftLeader, m_leftFollower}; + frc::MotorControllerGroup m_rightGroup{m_rightLeader, m_rightFollower}; frc::Encoder m_leftEncoder{0, 1}; frc::Encoder m_rightEncoder{2, 3}; diff --git a/wpilibcExamples/src/main/cpp/examples/DriveDistanceOffboard/include/ExampleSmartMotorController.h b/wpilibcExamples/src/main/cpp/examples/DriveDistanceOffboard/include/ExampleSmartMotorController.h index 5e5b1d53cd..71dc4d4864 100644 --- a/wpilibcExamples/src/main/cpp/examples/DriveDistanceOffboard/include/ExampleSmartMotorController.h +++ b/wpilibcExamples/src/main/cpp/examples/DriveDistanceOffboard/include/ExampleSmartMotorController.h @@ -4,7 +4,7 @@ #pragma once -#include +#include /** * A simplified stub class that simulates the API of a common "smart" motor @@ -12,7 +12,7 @@ * *

Has no actual functionality. */ -class ExampleSmartMotorController : public frc::SpeedController { +class ExampleSmartMotorController : public frc::MotorController { public: enum PIDMode { kPosition, kVelocity, kMovementWitchcraft }; diff --git a/wpilibcExamples/src/main/cpp/examples/ElevatorProfiledPID/cpp/Robot.cpp b/wpilibcExamples/src/main/cpp/examples/ElevatorProfiledPID/cpp/Robot.cpp index 6472fa581b..14f34d729f 100644 --- a/wpilibcExamples/src/main/cpp/examples/ElevatorProfiledPID/cpp/Robot.cpp +++ b/wpilibcExamples/src/main/cpp/examples/ElevatorProfiledPID/cpp/Robot.cpp @@ -4,9 +4,9 @@ #include #include -#include #include #include +#include #include #include #include diff --git a/wpilibcExamples/src/main/cpp/examples/ElevatorSimulation/cpp/Robot.cpp b/wpilibcExamples/src/main/cpp/examples/ElevatorSimulation/cpp/Robot.cpp index 77de329929..0aa766fb15 100644 --- a/wpilibcExamples/src/main/cpp/examples/ElevatorSimulation/cpp/Robot.cpp +++ b/wpilibcExamples/src/main/cpp/examples/ElevatorSimulation/cpp/Robot.cpp @@ -5,11 +5,11 @@ #include #include #include -#include #include #include #include #include +#include #include #include #include diff --git a/wpilibcExamples/src/main/cpp/examples/ElevatorTrapezoidProfile/include/ExampleSmartMotorController.h b/wpilibcExamples/src/main/cpp/examples/ElevatorTrapezoidProfile/include/ExampleSmartMotorController.h index 9d7e5e54e8..5d55839892 100644 --- a/wpilibcExamples/src/main/cpp/examples/ElevatorTrapezoidProfile/include/ExampleSmartMotorController.h +++ b/wpilibcExamples/src/main/cpp/examples/ElevatorTrapezoidProfile/include/ExampleSmartMotorController.h @@ -4,7 +4,7 @@ #pragma once -#include +#include /** * A simplified stub class that simulates the API of a common "smart" motor @@ -12,7 +12,7 @@ * *

Has no actual functionality. */ -class ExampleSmartMotorController : public frc::SpeedController { +class ExampleSmartMotorController : public frc::MotorController { public: enum PIDMode { kPosition, kVelocity, kMovementWitchcraft }; diff --git a/wpilibcExamples/src/main/cpp/examples/Frisbeebot/include/subsystems/DriveSubsystem.h b/wpilibcExamples/src/main/cpp/examples/Frisbeebot/include/subsystems/DriveSubsystem.h index 7aaac36654..47bf28e4d9 100644 --- a/wpilibcExamples/src/main/cpp/examples/Frisbeebot/include/subsystems/DriveSubsystem.h +++ b/wpilibcExamples/src/main/cpp/examples/Frisbeebot/include/subsystems/DriveSubsystem.h @@ -5,9 +5,9 @@ #pragma once #include -#include -#include #include +#include +#include #include #include "Constants.h" @@ -76,10 +76,10 @@ class DriveSubsystem : public frc2::SubsystemBase { frc::PWMSparkMax m_right2; // The motors on the left side of the drive - frc::SpeedControllerGroup m_leftMotors{m_left1, m_left2}; + frc::MotorControllerGroup m_leftMotors{m_left1, m_left2}; // The motors on the right side of the drive - frc::SpeedControllerGroup m_rightMotors{m_right1, m_right2}; + frc::MotorControllerGroup m_rightMotors{m_right1, m_right2}; // The robot's drive frc::DifferentialDrive m_drive{m_leftMotors, m_rightMotors}; diff --git a/wpilibcExamples/src/main/cpp/examples/Frisbeebot/include/subsystems/ShooterSubsystem.h b/wpilibcExamples/src/main/cpp/examples/Frisbeebot/include/subsystems/ShooterSubsystem.h index c3ccdc8325..e53b32ddae 100644 --- a/wpilibcExamples/src/main/cpp/examples/Frisbeebot/include/subsystems/ShooterSubsystem.h +++ b/wpilibcExamples/src/main/cpp/examples/Frisbeebot/include/subsystems/ShooterSubsystem.h @@ -5,8 +5,8 @@ #pragma once #include -#include #include +#include #include #include diff --git a/wpilibcExamples/src/main/cpp/examples/GearsBot/include/subsystems/Claw.h b/wpilibcExamples/src/main/cpp/examples/GearsBot/include/subsystems/Claw.h index 4870705aba..3b65382c5c 100644 --- a/wpilibcExamples/src/main/cpp/examples/GearsBot/include/subsystems/Claw.h +++ b/wpilibcExamples/src/main/cpp/examples/GearsBot/include/subsystems/Claw.h @@ -5,7 +5,7 @@ #pragma once #include -#include +#include #include /** diff --git a/wpilibcExamples/src/main/cpp/examples/GearsBot/include/subsystems/DriveTrain.h b/wpilibcExamples/src/main/cpp/examples/GearsBot/include/subsystems/DriveTrain.h index 52a7153e23..f704df80ae 100644 --- a/wpilibcExamples/src/main/cpp/examples/GearsBot/include/subsystems/DriveTrain.h +++ b/wpilibcExamples/src/main/cpp/examples/GearsBot/include/subsystems/DriveTrain.h @@ -7,9 +7,9 @@ #include #include #include -#include -#include #include +#include +#include #include namespace frc { @@ -66,11 +66,11 @@ class DriveTrain : public frc2::SubsystemBase { private: frc::PWMSparkMax m_frontLeft{1}; frc::PWMSparkMax m_rearLeft{2}; - frc::SpeedControllerGroup m_left{m_frontLeft, m_rearLeft}; + frc::MotorControllerGroup m_left{m_frontLeft, m_rearLeft}; frc::PWMSparkMax m_frontRight{3}; frc::PWMSparkMax m_rearRight{4}; - frc::SpeedControllerGroup m_right{m_frontRight, m_rearRight}; + frc::MotorControllerGroup m_right{m_frontRight, m_rearRight}; frc::DifferentialDrive m_robotDrive{m_left, m_right}; diff --git a/wpilibcExamples/src/main/cpp/examples/GearsBot/include/subsystems/Elevator.h b/wpilibcExamples/src/main/cpp/examples/GearsBot/include/subsystems/Elevator.h index 875a5f1a97..80008c5d0e 100644 --- a/wpilibcExamples/src/main/cpp/examples/GearsBot/include/subsystems/Elevator.h +++ b/wpilibcExamples/src/main/cpp/examples/GearsBot/include/subsystems/Elevator.h @@ -5,7 +5,7 @@ #pragma once #include -#include +#include #include /** diff --git a/wpilibcExamples/src/main/cpp/examples/GearsBot/include/subsystems/Wrist.h b/wpilibcExamples/src/main/cpp/examples/GearsBot/include/subsystems/Wrist.h index ef660a2a39..e873c2cccc 100644 --- a/wpilibcExamples/src/main/cpp/examples/GearsBot/include/subsystems/Wrist.h +++ b/wpilibcExamples/src/main/cpp/examples/GearsBot/include/subsystems/Wrist.h @@ -5,7 +5,7 @@ #pragma once #include -#include +#include #include /** diff --git a/wpilibcExamples/src/main/cpp/examples/GettingStarted/cpp/Robot.cpp b/wpilibcExamples/src/main/cpp/examples/GettingStarted/cpp/Robot.cpp index 6996c7fd39..a46581379c 100644 --- a/wpilibcExamples/src/main/cpp/examples/GettingStarted/cpp/Robot.cpp +++ b/wpilibcExamples/src/main/cpp/examples/GettingStarted/cpp/Robot.cpp @@ -3,11 +3,11 @@ // the WPILib BSD license file in the root directory of this project. #include -#include #include #include #include #include +#include class Robot : public frc::TimedRobot { public: diff --git a/wpilibcExamples/src/main/cpp/examples/Gyro/cpp/Robot.cpp b/wpilibcExamples/src/main/cpp/examples/Gyro/cpp/Robot.cpp index 4bfc3c1154..1dda356ff5 100644 --- a/wpilibcExamples/src/main/cpp/examples/Gyro/cpp/Robot.cpp +++ b/wpilibcExamples/src/main/cpp/examples/Gyro/cpp/Robot.cpp @@ -6,9 +6,9 @@ #include #include -#include #include #include +#include /** * This is a sample program to demonstrate how to use a gyro sensor to make a diff --git a/wpilibcExamples/src/main/cpp/examples/GyroDriveCommands/include/subsystems/DriveSubsystem.h b/wpilibcExamples/src/main/cpp/examples/GyroDriveCommands/include/subsystems/DriveSubsystem.h index dc546db0cf..96174dd4f7 100644 --- a/wpilibcExamples/src/main/cpp/examples/GyroDriveCommands/include/subsystems/DriveSubsystem.h +++ b/wpilibcExamples/src/main/cpp/examples/GyroDriveCommands/include/subsystems/DriveSubsystem.h @@ -6,9 +6,9 @@ #include #include -#include -#include #include +#include +#include #include #include @@ -92,10 +92,10 @@ class DriveSubsystem : public frc2::SubsystemBase { frc::PWMSparkMax m_right2; // The motors on the left side of the drive - frc::SpeedControllerGroup m_leftMotors{m_left1, m_left2}; + frc::MotorControllerGroup m_leftMotors{m_left1, m_left2}; // The motors on the right side of the drive - frc::SpeedControllerGroup m_rightMotors{m_right1, m_right2}; + frc::MotorControllerGroup m_rightMotors{m_right1, m_right2}; // The robot's drive frc::DifferentialDrive m_drive{m_leftMotors, m_rightMotors}; diff --git a/wpilibcExamples/src/main/cpp/examples/GyroMecanum/cpp/Robot.cpp b/wpilibcExamples/src/main/cpp/examples/GyroMecanum/cpp/Robot.cpp index 0922ae4923..58858733f4 100644 --- a/wpilibcExamples/src/main/cpp/examples/GyroMecanum/cpp/Robot.cpp +++ b/wpilibcExamples/src/main/cpp/examples/GyroMecanum/cpp/Robot.cpp @@ -4,9 +4,9 @@ #include #include -#include #include #include +#include /** * This is a sample program that uses mecanum drive with a gyro sensor to diff --git a/wpilibcExamples/src/main/cpp/examples/HatchbotInlined/include/subsystems/DriveSubsystem.h b/wpilibcExamples/src/main/cpp/examples/HatchbotInlined/include/subsystems/DriveSubsystem.h index 7aaac36654..47bf28e4d9 100644 --- a/wpilibcExamples/src/main/cpp/examples/HatchbotInlined/include/subsystems/DriveSubsystem.h +++ b/wpilibcExamples/src/main/cpp/examples/HatchbotInlined/include/subsystems/DriveSubsystem.h @@ -5,9 +5,9 @@ #pragma once #include -#include -#include #include +#include +#include #include #include "Constants.h" @@ -76,10 +76,10 @@ class DriveSubsystem : public frc2::SubsystemBase { frc::PWMSparkMax m_right2; // The motors on the left side of the drive - frc::SpeedControllerGroup m_leftMotors{m_left1, m_left2}; + frc::MotorControllerGroup m_leftMotors{m_left1, m_left2}; // The motors on the right side of the drive - frc::SpeedControllerGroup m_rightMotors{m_right1, m_right2}; + frc::MotorControllerGroup m_rightMotors{m_right1, m_right2}; // The robot's drive frc::DifferentialDrive m_drive{m_leftMotors, m_rightMotors}; diff --git a/wpilibcExamples/src/main/cpp/examples/HatchbotTraditional/include/subsystems/DriveSubsystem.h b/wpilibcExamples/src/main/cpp/examples/HatchbotTraditional/include/subsystems/DriveSubsystem.h index 7aaac36654..47bf28e4d9 100644 --- a/wpilibcExamples/src/main/cpp/examples/HatchbotTraditional/include/subsystems/DriveSubsystem.h +++ b/wpilibcExamples/src/main/cpp/examples/HatchbotTraditional/include/subsystems/DriveSubsystem.h @@ -5,9 +5,9 @@ #pragma once #include -#include -#include #include +#include +#include #include #include "Constants.h" @@ -76,10 +76,10 @@ class DriveSubsystem : public frc2::SubsystemBase { frc::PWMSparkMax m_right2; // The motors on the left side of the drive - frc::SpeedControllerGroup m_leftMotors{m_left1, m_left2}; + frc::MotorControllerGroup m_leftMotors{m_left1, m_left2}; // The motors on the right side of the drive - frc::SpeedControllerGroup m_rightMotors{m_right1, m_right2}; + frc::MotorControllerGroup m_rightMotors{m_right1, m_right2}; // The robot's drive frc::DifferentialDrive m_drive{m_leftMotors, m_rightMotors}; diff --git a/wpilibcExamples/src/main/cpp/examples/MecanumBot/include/Drivetrain.h b/wpilibcExamples/src/main/cpp/examples/MecanumBot/include/Drivetrain.h index 62a14a0b12..1b097fe489 100644 --- a/wpilibcExamples/src/main/cpp/examples/MecanumBot/include/Drivetrain.h +++ b/wpilibcExamples/src/main/cpp/examples/MecanumBot/include/Drivetrain.h @@ -6,13 +6,13 @@ #include #include -#include #include #include #include #include #include #include +#include #include /** diff --git a/wpilibcExamples/src/main/cpp/examples/MecanumControllerCommand/cpp/RobotContainer.cpp b/wpilibcExamples/src/main/cpp/examples/MecanumControllerCommand/cpp/RobotContainer.cpp index 863034e0b1..e7822238f1 100644 --- a/wpilibcExamples/src/main/cpp/examples/MecanumControllerCommand/cpp/RobotContainer.cpp +++ b/wpilibcExamples/src/main/cpp/examples/MecanumControllerCommand/cpp/RobotContainer.cpp @@ -96,7 +96,7 @@ frc2::Command* RobotContainer::GetAutonomousCommand() { [this](units::volt_t frontLeft, units::volt_t rearLeft, units::volt_t frontRight, units::volt_t rearRight) { - m_drive.SetSpeedControllersVolts(frontLeft, rearLeft, frontRight, + m_drive.SetMotorControllersVolts(frontLeft, rearLeft, frontRight, rearRight); }, diff --git a/wpilibcExamples/src/main/cpp/examples/MecanumControllerCommand/cpp/subsystems/DriveSubsystem.cpp b/wpilibcExamples/src/main/cpp/examples/MecanumControllerCommand/cpp/subsystems/DriveSubsystem.cpp index 8d357f429f..4cc4e47a1c 100644 --- a/wpilibcExamples/src/main/cpp/examples/MecanumControllerCommand/cpp/subsystems/DriveSubsystem.cpp +++ b/wpilibcExamples/src/main/cpp/examples/MecanumControllerCommand/cpp/subsystems/DriveSubsystem.cpp @@ -56,7 +56,7 @@ void DriveSubsystem::Drive(double xSpeed, double ySpeed, double rot, } } -void DriveSubsystem::SetSpeedControllersVolts(units::volt_t frontLeftPower, +void DriveSubsystem::SetMotorControllersVolts(units::volt_t frontLeftPower, units::volt_t rearLeftPower, units::volt_t frontRightPower, units::volt_t rearRightPower) { diff --git a/wpilibcExamples/src/main/cpp/examples/MecanumControllerCommand/include/subsystems/DriveSubsystem.h b/wpilibcExamples/src/main/cpp/examples/MecanumControllerCommand/include/subsystems/DriveSubsystem.h index a521219f4a..95b08b6937 100644 --- a/wpilibcExamples/src/main/cpp/examples/MecanumControllerCommand/include/subsystems/DriveSubsystem.h +++ b/wpilibcExamples/src/main/cpp/examples/MecanumControllerCommand/include/subsystems/DriveSubsystem.h @@ -6,13 +6,13 @@ #include #include -#include #include #include #include #include #include #include +#include #include #include "Constants.h" @@ -82,9 +82,9 @@ class DriveSubsystem : public frc2::SubsystemBase { frc::MecanumDriveWheelSpeeds getCurrentWheelSpeeds(); /** - * Sets the drive SpeedControllers to a desired voltage. + * Sets the drive MotorControllers to a desired voltage. */ - void SetSpeedControllersVolts(units::volt_t frontLeftPower, + void SetMotorControllersVolts(units::volt_t frontLeftPower, units::volt_t rearLeftPower, units::volt_t frontRightPower, units::volt_t rearRightPower); diff --git a/wpilibcExamples/src/main/cpp/examples/MecanumDrive/cpp/Robot.cpp b/wpilibcExamples/src/main/cpp/examples/MecanumDrive/cpp/Robot.cpp index f7c1be3517..822e9ee675 100644 --- a/wpilibcExamples/src/main/cpp/examples/MecanumDrive/cpp/Robot.cpp +++ b/wpilibcExamples/src/main/cpp/examples/MecanumDrive/cpp/Robot.cpp @@ -3,9 +3,9 @@ // the WPILib BSD license file in the root directory of this project. #include -#include #include #include +#include /** * This is a demo program showing how to use Mecanum control with the diff --git a/wpilibcExamples/src/main/cpp/examples/MecanumDrivePoseEstimator/include/Drivetrain.h b/wpilibcExamples/src/main/cpp/examples/MecanumDrivePoseEstimator/include/Drivetrain.h index 5dca8277c9..ba796d5a0d 100644 --- a/wpilibcExamples/src/main/cpp/examples/MecanumDrivePoseEstimator/include/Drivetrain.h +++ b/wpilibcExamples/src/main/cpp/examples/MecanumDrivePoseEstimator/include/Drivetrain.h @@ -6,7 +6,6 @@ #include #include -#include #include #include #include @@ -14,6 +13,7 @@ #include #include #include +#include #include /** diff --git a/wpilibcExamples/src/main/cpp/examples/MotorControl/cpp/Robot.cpp b/wpilibcExamples/src/main/cpp/examples/MotorControl/cpp/Robot.cpp index 0b8351b6da..0092ec3240 100644 --- a/wpilibcExamples/src/main/cpp/examples/MotorControl/cpp/Robot.cpp +++ b/wpilibcExamples/src/main/cpp/examples/MotorControl/cpp/Robot.cpp @@ -3,8 +3,8 @@ // the WPILib BSD license file in the root directory of this project. #include -#include #include +#include /** * This sample program shows how to control a motor using a joystick. In the diff --git a/wpilibcExamples/src/main/cpp/examples/MotorControlEncoder/cpp/Robot.cpp b/wpilibcExamples/src/main/cpp/examples/MotorControlEncoder/cpp/Robot.cpp index 76ba76d655..5b76a62f80 100644 --- a/wpilibcExamples/src/main/cpp/examples/MotorControlEncoder/cpp/Robot.cpp +++ b/wpilibcExamples/src/main/cpp/examples/MotorControlEncoder/cpp/Robot.cpp @@ -4,8 +4,8 @@ #include #include -#include #include +#include #include #include diff --git a/wpilibcExamples/src/main/cpp/examples/PacGoat/include/subsystems/Collector.h b/wpilibcExamples/src/main/cpp/examples/PacGoat/include/subsystems/Collector.h index 77b0bb4695..9994a35bf7 100644 --- a/wpilibcExamples/src/main/cpp/examples/PacGoat/include/subsystems/Collector.h +++ b/wpilibcExamples/src/main/cpp/examples/PacGoat/include/subsystems/Collector.h @@ -5,9 +5,9 @@ #pragma once #include -#include #include #include +#include /** * The Collector subsystem has one motor for the rollers, a limit switch for diff --git a/wpilibcExamples/src/main/cpp/examples/PacGoat/include/subsystems/DriveTrain.h b/wpilibcExamples/src/main/cpp/examples/PacGoat/include/subsystems/DriveTrain.h index 739f662136..65d29cd933 100644 --- a/wpilibcExamples/src/main/cpp/examples/PacGoat/include/subsystems/DriveTrain.h +++ b/wpilibcExamples/src/main/cpp/examples/PacGoat/include/subsystems/DriveTrain.h @@ -6,10 +6,10 @@ #include #include -#include -#include #include #include +#include +#include namespace frc { class Joystick; @@ -62,11 +62,11 @@ class DriveTrain : public frc::Subsystem { // Subsystem devices frc::PWMSparkMax m_frontLeftCIM{1}; frc::PWMSparkMax m_rearLeftCIM{2}; - frc::SpeedControllerGroup m_leftCIMs{m_frontLeftCIM, m_rearLeftCIM}; + frc::MotorControllerGroup m_leftCIMs{m_frontLeftCIM, m_rearLeftCIM}; frc::PWMSparkMax m_frontRightCIM{3}; frc::PWMSparkMax m_rearRightCIM{4}; - frc::SpeedControllerGroup m_rightCIMs{m_frontRightCIM, m_rearRightCIM}; + frc::MotorControllerGroup m_rightCIMs{m_frontRightCIM, m_rearRightCIM}; frc::DifferentialDrive m_robotDrive{m_leftCIMs, m_rightCIMs}; diff --git a/wpilibcExamples/src/main/cpp/examples/PacGoat/include/subsystems/Pivot.h b/wpilibcExamples/src/main/cpp/examples/PacGoat/include/subsystems/Pivot.h index 23d342f1cf..95b3be9eca 100644 --- a/wpilibcExamples/src/main/cpp/examples/PacGoat/include/subsystems/Pivot.h +++ b/wpilibcExamples/src/main/cpp/examples/PacGoat/include/subsystems/Pivot.h @@ -6,8 +6,8 @@ #include #include -#include #include +#include /** * The Pivot subsystem contains the Van-door motor and the pot for PID control diff --git a/wpilibcExamples/src/main/cpp/examples/PotentiometerPID/cpp/Robot.cpp b/wpilibcExamples/src/main/cpp/examples/PotentiometerPID/cpp/Robot.cpp index 6149d40721..5b6b024ec1 100644 --- a/wpilibcExamples/src/main/cpp/examples/PotentiometerPID/cpp/Robot.cpp +++ b/wpilibcExamples/src/main/cpp/examples/PotentiometerPID/cpp/Robot.cpp @@ -6,9 +6,9 @@ #include #include -#include #include #include +#include /** * This is a sample program to demonstrate how to use a soft potentiometer and a diff --git a/wpilibcExamples/src/main/cpp/examples/RamseteCommand/include/subsystems/DriveSubsystem.h b/wpilibcExamples/src/main/cpp/examples/RamseteCommand/include/subsystems/DriveSubsystem.h index 28707ea61f..8ea14dad39 100644 --- a/wpilibcExamples/src/main/cpp/examples/RamseteCommand/include/subsystems/DriveSubsystem.h +++ b/wpilibcExamples/src/main/cpp/examples/RamseteCommand/include/subsystems/DriveSubsystem.h @@ -6,11 +6,11 @@ #include #include -#include -#include #include #include #include +#include +#include #include #include @@ -123,10 +123,10 @@ class DriveSubsystem : public frc2::SubsystemBase { frc::PWMSparkMax m_right2; // The motors on the left side of the drive - frc::SpeedControllerGroup m_leftMotors{m_left1, m_left2}; + frc::MotorControllerGroup m_leftMotors{m_left1, m_left2}; // The motors on the right side of the drive - frc::SpeedControllerGroup m_rightMotors{m_right1, m_right2}; + frc::MotorControllerGroup m_rightMotors{m_right1, m_right2}; // The robot's drive frc::DifferentialDrive m_drive{m_leftMotors, m_rightMotors}; diff --git a/wpilibcExamples/src/main/cpp/examples/RamseteController/include/Drivetrain.h b/wpilibcExamples/src/main/cpp/examples/RamseteController/include/Drivetrain.h index baa5a090ab..3cdbd1e4d2 100644 --- a/wpilibcExamples/src/main/cpp/examples/RamseteController/include/Drivetrain.h +++ b/wpilibcExamples/src/main/cpp/examples/RamseteController/include/Drivetrain.h @@ -6,12 +6,12 @@ #include #include -#include -#include #include #include #include #include +#include +#include #include #include #include @@ -57,8 +57,8 @@ class Drivetrain { frc::PWMSparkMax m_rightLeader{3}; frc::PWMSparkMax m_rightFollower{4}; - frc::SpeedControllerGroup m_leftGroup{m_leftLeader, m_leftFollower}; - frc::SpeedControllerGroup m_rightGroup{m_rightLeader, m_rightFollower}; + frc::MotorControllerGroup m_leftGroup{m_leftLeader, m_leftFollower}; + frc::MotorControllerGroup m_rightGroup{m_rightLeader, m_rightFollower}; frc::Encoder m_leftEncoder{0, 1}; frc::Encoder m_rightEncoder{2, 3}; diff --git a/wpilibcExamples/src/main/cpp/examples/RomiReference/include/subsystems/Drivetrain.h b/wpilibcExamples/src/main/cpp/examples/RomiReference/include/subsystems/Drivetrain.h index 0f96ff4f4d..ace7d33bc7 100644 --- a/wpilibcExamples/src/main/cpp/examples/RomiReference/include/subsystems/Drivetrain.h +++ b/wpilibcExamples/src/main/cpp/examples/RomiReference/include/subsystems/Drivetrain.h @@ -6,8 +6,8 @@ #include #include -#include #include +#include #include #include #include diff --git a/wpilibcExamples/src/main/cpp/examples/ShuffleBoard/cpp/Robot.cpp b/wpilibcExamples/src/main/cpp/examples/ShuffleBoard/cpp/Robot.cpp index ceb3470a1b..a114b927ae 100644 --- a/wpilibcExamples/src/main/cpp/examples/ShuffleBoard/cpp/Robot.cpp +++ b/wpilibcExamples/src/main/cpp/examples/ShuffleBoard/cpp/Robot.cpp @@ -5,9 +5,9 @@ #include #include #include -#include #include #include +#include #include #include #include diff --git a/wpilibcExamples/src/main/cpp/examples/SimpleDifferentialDriveSimulation/include/Drivetrain.h b/wpilibcExamples/src/main/cpp/examples/SimpleDifferentialDriveSimulation/include/Drivetrain.h index 48d758a7af..f60deefc3e 100644 --- a/wpilibcExamples/src/main/cpp/examples/SimpleDifferentialDriveSimulation/include/Drivetrain.h +++ b/wpilibcExamples/src/main/cpp/examples/SimpleDifferentialDriveSimulation/include/Drivetrain.h @@ -6,12 +6,12 @@ #include #include -#include -#include #include #include #include #include +#include +#include #include #include #include @@ -73,8 +73,8 @@ class Drivetrain { frc::PWMSparkMax m_rightLeader{3}; frc::PWMSparkMax m_rightFollower{4}; - frc::SpeedControllerGroup m_leftGroup{m_leftLeader, m_leftFollower}; - frc::SpeedControllerGroup m_rightGroup{m_rightLeader, m_rightFollower}; + frc::MotorControllerGroup m_leftGroup{m_leftLeader, m_leftFollower}; + frc::MotorControllerGroup m_rightGroup{m_rightLeader, m_rightFollower}; frc::Encoder m_leftEncoder{0, 1}; frc::Encoder m_rightEncoder{2, 3}; diff --git a/wpilibcExamples/src/main/cpp/examples/StateSpaceArm/cpp/Robot.cpp b/wpilibcExamples/src/main/cpp/examples/StateSpaceArm/cpp/Robot.cpp index 63fc0a6f37..5841f5fb15 100644 --- a/wpilibcExamples/src/main/cpp/examples/StateSpaceArm/cpp/Robot.cpp +++ b/wpilibcExamples/src/main/cpp/examples/StateSpaceArm/cpp/Robot.cpp @@ -4,7 +4,6 @@ #include #include -#include #include #include #include @@ -12,6 +11,7 @@ #include #include #include +#include #include #include #include diff --git a/wpilibcExamples/src/main/cpp/examples/StateSpaceDifferentialDriveSimulation/include/subsystems/DriveSubsystem.h b/wpilibcExamples/src/main/cpp/examples/StateSpaceDifferentialDriveSimulation/include/subsystems/DriveSubsystem.h index 9b7dadba41..395133c88c 100644 --- a/wpilibcExamples/src/main/cpp/examples/StateSpaceDifferentialDriveSimulation/include/subsystems/DriveSubsystem.h +++ b/wpilibcExamples/src/main/cpp/examples/StateSpaceDifferentialDriveSimulation/include/subsystems/DriveSubsystem.h @@ -6,11 +6,11 @@ #include #include -#include -#include #include #include #include +#include +#include #include #include #include @@ -134,10 +134,10 @@ class DriveSubsystem : public frc2::SubsystemBase { frc::PWMSparkMax m_right2{DriveConstants::kRightMotor2Port}; // The motors on the left side of the drive - frc::SpeedControllerGroup m_leftMotors{m_left1, m_left2}; + frc::MotorControllerGroup m_leftMotors{m_left1, m_left2}; // The motors on the right side of the drive - frc::SpeedControllerGroup m_rightMotors{m_right1, m_right2}; + frc::MotorControllerGroup m_rightMotors{m_right1, m_right2}; // The robot's drive frc::DifferentialDrive m_drive{m_leftMotors, m_rightMotors}; diff --git a/wpilibcExamples/src/main/cpp/examples/StateSpaceElevator/cpp/Robot.cpp b/wpilibcExamples/src/main/cpp/examples/StateSpaceElevator/cpp/Robot.cpp index 4d94321e7f..a0d9eaf717 100644 --- a/wpilibcExamples/src/main/cpp/examples/StateSpaceElevator/cpp/Robot.cpp +++ b/wpilibcExamples/src/main/cpp/examples/StateSpaceElevator/cpp/Robot.cpp @@ -4,13 +4,13 @@ #include #include -#include #include #include #include #include #include #include +#include #include #include #include diff --git a/wpilibcExamples/src/main/cpp/examples/StateSpaceFlywheel/cpp/Robot.cpp b/wpilibcExamples/src/main/cpp/examples/StateSpaceFlywheel/cpp/Robot.cpp index bab52d939f..b52fc7ca7d 100644 --- a/wpilibcExamples/src/main/cpp/examples/StateSpaceFlywheel/cpp/Robot.cpp +++ b/wpilibcExamples/src/main/cpp/examples/StateSpaceFlywheel/cpp/Robot.cpp @@ -5,13 +5,13 @@ #include #include #include -#include #include #include #include #include #include #include +#include #include #include #include diff --git a/wpilibcExamples/src/main/cpp/examples/StateSpaceFlywheelSysId/cpp/Robot.cpp b/wpilibcExamples/src/main/cpp/examples/StateSpaceFlywheelSysId/cpp/Robot.cpp index 1b7d9b76d5..26cebd646b 100644 --- a/wpilibcExamples/src/main/cpp/examples/StateSpaceFlywheelSysId/cpp/Robot.cpp +++ b/wpilibcExamples/src/main/cpp/examples/StateSpaceFlywheelSysId/cpp/Robot.cpp @@ -5,7 +5,6 @@ #include #include #include -#include #include #include #include @@ -13,6 +12,7 @@ #include #include #include +#include #include #include #include diff --git a/wpilibcExamples/src/main/cpp/examples/SwerveBot/include/SwerveModule.h b/wpilibcExamples/src/main/cpp/examples/SwerveBot/include/SwerveModule.h index 119137d5ef..3c3afa697a 100644 --- a/wpilibcExamples/src/main/cpp/examples/SwerveBot/include/SwerveModule.h +++ b/wpilibcExamples/src/main/cpp/examples/SwerveBot/include/SwerveModule.h @@ -5,11 +5,11 @@ #pragma once #include -#include #include #include #include #include +#include #include #include #include diff --git a/wpilibcExamples/src/main/cpp/examples/SwerveControllerCommand/include/subsystems/DriveSubsystem.h b/wpilibcExamples/src/main/cpp/examples/SwerveControllerCommand/include/subsystems/DriveSubsystem.h index c11f8e2b64..351aeb3ac7 100644 --- a/wpilibcExamples/src/main/cpp/examples/SwerveControllerCommand/include/subsystems/DriveSubsystem.h +++ b/wpilibcExamples/src/main/cpp/examples/SwerveControllerCommand/include/subsystems/DriveSubsystem.h @@ -6,7 +6,6 @@ #include #include -#include #include #include #include @@ -14,6 +13,7 @@ #include #include #include +#include #include #include "Constants.h" @@ -51,7 +51,7 @@ class DriveSubsystem : public frc2::SubsystemBase { void ResetEncoders(); /** - * Sets the drive SpeedControllers to a power from -1 to 1. + * Sets the drive MotorControllers to a power from -1 to 1. */ void SetModuleStates(wpi::array desiredStates); diff --git a/wpilibcExamples/src/main/cpp/examples/SwerveControllerCommand/include/subsystems/SwerveModule.h b/wpilibcExamples/src/main/cpp/examples/SwerveControllerCommand/include/subsystems/SwerveModule.h index 7f3dba843b..304d8736c6 100644 --- a/wpilibcExamples/src/main/cpp/examples/SwerveControllerCommand/include/subsystems/SwerveModule.h +++ b/wpilibcExamples/src/main/cpp/examples/SwerveControllerCommand/include/subsystems/SwerveModule.h @@ -5,11 +5,11 @@ #pragma once #include -#include #include #include #include #include +#include #include #include diff --git a/wpilibcExamples/src/main/cpp/examples/SwerveDrivePoseEstimator/include/SwerveModule.h b/wpilibcExamples/src/main/cpp/examples/SwerveDrivePoseEstimator/include/SwerveModule.h index 84182c09b1..0a33cb9dd4 100644 --- a/wpilibcExamples/src/main/cpp/examples/SwerveDrivePoseEstimator/include/SwerveModule.h +++ b/wpilibcExamples/src/main/cpp/examples/SwerveDrivePoseEstimator/include/SwerveModule.h @@ -5,11 +5,11 @@ #pragma once #include -#include #include #include #include #include +#include #include #include #include diff --git a/wpilibcExamples/src/main/cpp/examples/TankDriveXboxController/cpp/Robot.cpp b/wpilibcExamples/src/main/cpp/examples/TankDriveXboxController/cpp/Robot.cpp index 3fa97cf654..fffc78dd4b 100644 --- a/wpilibcExamples/src/main/cpp/examples/TankDriveXboxController/cpp/Robot.cpp +++ b/wpilibcExamples/src/main/cpp/examples/TankDriveXboxController/cpp/Robot.cpp @@ -3,10 +3,10 @@ // the WPILib BSD license file in the root directory of this project. #include -#include #include #include #include +#include /** * This is a demo program showing the use of the DifferentialDrive class. diff --git a/wpilibcExamples/src/main/cpp/examples/Ultrasonic/cpp/Robot.cpp b/wpilibcExamples/src/main/cpp/examples/Ultrasonic/cpp/Robot.cpp index 1cd070712e..482e5ffb10 100644 --- a/wpilibcExamples/src/main/cpp/examples/Ultrasonic/cpp/Robot.cpp +++ b/wpilibcExamples/src/main/cpp/examples/Ultrasonic/cpp/Robot.cpp @@ -4,9 +4,9 @@ #include #include -#include #include #include +#include /** * This is a sample program demonstrating how to use an ultrasonic sensor and diff --git a/wpilibcExamples/src/main/cpp/examples/UltrasonicPID/cpp/Robot.cpp b/wpilibcExamples/src/main/cpp/examples/UltrasonicPID/cpp/Robot.cpp index 08743128a0..4673975e71 100644 --- a/wpilibcExamples/src/main/cpp/examples/UltrasonicPID/cpp/Robot.cpp +++ b/wpilibcExamples/src/main/cpp/examples/UltrasonicPID/cpp/Robot.cpp @@ -4,10 +4,10 @@ #include #include -#include #include #include #include +#include /** * This is a sample program demonstrating how to use an ultrasonic sensor and diff --git a/wpilibcIntegrationTests/src/main/native/cpp/CounterTest.cpp b/wpilibcIntegrationTests/src/main/native/cpp/CounterTest.cpp index 6090a0bf27..23e9b2f90d 100644 --- a/wpilibcIntegrationTests/src/main/native/cpp/CounterTest.cpp +++ b/wpilibcIntegrationTests/src/main/native/cpp/CounterTest.cpp @@ -5,10 +5,10 @@ #include "frc/Counter.h" // NOLINT(build/include_order) #include "TestBench.h" -#include "frc/Jaguar.h" -#include "frc/Talon.h" #include "frc/Timer.h" -#include "frc/Victor.h" +#include "frc/motorcontrol/Jaguar.h" +#include "frc/motorcontrol/Talon.h" +#include "frc/motorcontrol/Victor.h" #include "gtest/gtest.h" using namespace frc; diff --git a/wpilibcIntegrationTests/src/main/native/cpp/MotorEncoderTest.cpp b/wpilibcIntegrationTests/src/main/native/cpp/MotorEncoderTest.cpp index 1f84faad06..9737d78355 100644 --- a/wpilibcIntegrationTests/src/main/native/cpp/MotorEncoderTest.cpp +++ b/wpilibcIntegrationTests/src/main/native/cpp/MotorEncoderTest.cpp @@ -6,13 +6,13 @@ #include "TestBench.h" #include "frc/Encoder.h" -#include "frc/Jaguar.h" #include "frc/LinearFilter.h" #include "frc/Notifier.h" -#include "frc/Talon.h" #include "frc/Timer.h" -#include "frc/Victor.h" #include "frc/controller/PIDController.h" +#include "frc/motorcontrol/Jaguar.h" +#include "frc/motorcontrol/Talon.h" +#include "frc/motorcontrol/Victor.h" #include "gtest/gtest.h" using namespace frc; @@ -38,31 +38,31 @@ std::ostream& operator<<(std::ostream& os, MotorEncoderTestType const& type) { static constexpr double kMotorTime = 0.5; /** - * A fixture that includes a PWM speed controller and an encoder connected to + * A fixture that includes a PWM motor controller and an encoder connected to * the same motor. */ class MotorEncoderTest : public testing::TestWithParam { protected: - SpeedController* m_speedController; + MotorController* m_motorController; Encoder* m_encoder; LinearFilter* m_filter; void SetUp() override { switch (GetParam()) { case TEST_VICTOR: - m_speedController = new Victor(TestBench::kVictorChannel); + m_motorController = new Victor(TestBench::kVictorChannel); m_encoder = new Encoder(TestBench::kVictorEncoderChannelA, TestBench::kVictorEncoderChannelB); break; case TEST_JAGUAR: - m_speedController = new Jaguar(TestBench::kJaguarChannel); + m_motorController = new Jaguar(TestBench::kJaguarChannel); m_encoder = new Encoder(TestBench::kJaguarEncoderChannelA, TestBench::kJaguarEncoderChannelB); break; case TEST_TALON: - m_speedController = new Talon(TestBench::kTalonChannel); + m_motorController = new Talon(TestBench::kTalonChannel); m_encoder = new Encoder(TestBench::kTalonEncoderChannelA, TestBench::kTalonEncoderChannelB); break; @@ -72,13 +72,13 @@ class MotorEncoderTest : public testing::TestWithParam { } void TearDown() override { - delete m_speedController; + delete m_motorController; delete m_encoder; delete m_filter; } void Reset() { - m_speedController->Set(0.0); + m_motorController->Set(0.0); m_encoder->Reset(); m_filter->Reset(); } @@ -90,10 +90,10 @@ class MotorEncoderTest : public testing::TestWithParam { TEST_P(MotorEncoderTest, Increment) { Reset(); - /* Drive the speed controller briefly to move the encoder */ - m_speedController->Set(0.2f); + /* Drive the motor controller briefly to move the encoder */ + m_motorController->Set(0.2f); Wait(kMotorTime); - m_speedController->Set(0.0); + m_motorController->Set(0.0); /* The encoder should be positive now */ EXPECT_GT(m_encoder->Get(), 0) @@ -106,10 +106,10 @@ TEST_P(MotorEncoderTest, Increment) { TEST_P(MotorEncoderTest, Decrement) { Reset(); - /* Drive the speed controller briefly to move the encoder */ - m_speedController->Set(-0.2); + /* Drive the motor controller briefly to move the encoder */ + m_motorController->Set(-0.2); Wait(kMotorTime); - m_speedController->Set(0.0); + m_motorController->Set(0.0); /* The encoder should be positive now */ EXPECT_LT(m_encoder->Get(), 0.0) @@ -122,15 +122,15 @@ TEST_P(MotorEncoderTest, Decrement) { TEST_P(MotorEncoderTest, ClampSpeed) { Reset(); - m_speedController->Set(2.0); + m_motorController->Set(2.0); Wait(kMotorTime); - EXPECT_FLOAT_EQ(1.0, m_speedController->Get()); + EXPECT_FLOAT_EQ(1.0, m_motorController->Get()); - m_speedController->Set(-2.0); + m_motorController->Set(-2.0); Wait(kMotorTime); - EXPECT_FLOAT_EQ(-1.0, m_speedController->Get()); + EXPECT_FLOAT_EQ(-1.0, m_motorController->Get()); } /** @@ -147,7 +147,7 @@ TEST_P(MotorEncoderTest, PositionPIDController) { /* 10 seconds should be plenty time to get to the reference */ frc::Notifier pidRunner{[this, &pidController] { auto speed = pidController.Calculate(m_encoder->GetDistance()); - m_speedController->Set(std::clamp(speed, -0.2, 0.2)); + m_motorController->Set(std::clamp(speed, -0.2, 0.2)); }}; pidRunner.StartPeriodic(pidController.GetPeriod()); Wait(10.0); @@ -174,7 +174,7 @@ TEST_P(MotorEncoderTest, VelocityPIDController) { frc::Notifier pidRunner{[this, &pidController] { auto speed = pidController.Calculate(m_filter->Calculate(m_encoder->GetRate())); - m_speedController->Set(std::clamp(speed, -0.3, 0.3)); + m_motorController->Set(std::clamp(speed, -0.3, 0.3)); }}; pidRunner.StartPeriodic(pidController.GetPeriod()); Wait(10.0); diff --git a/wpilibcIntegrationTests/src/main/native/cpp/MotorInvertingTest.cpp b/wpilibcIntegrationTests/src/main/native/cpp/MotorInvertingTest.cpp index c9abdb9bc7..72f658a440 100644 --- a/wpilibcIntegrationTests/src/main/native/cpp/MotorInvertingTest.cpp +++ b/wpilibcIntegrationTests/src/main/native/cpp/MotorInvertingTest.cpp @@ -4,10 +4,10 @@ #include "TestBench.h" #include "frc/Encoder.h" -#include "frc/Jaguar.h" -#include "frc/Talon.h" #include "frc/Timer.h" -#include "frc/Victor.h" +#include "frc/motorcontrol/Jaguar.h" +#include "frc/motorcontrol/Talon.h" +#include "frc/motorcontrol/Victor.h" #include "gtest/gtest.h" using namespace frc; @@ -33,25 +33,25 @@ std::ostream& operator<<(std::ostream& os, MotorInvertingTestType const& type) { class MotorInvertingTest : public testing::TestWithParam { protected: - SpeedController* m_speedController; + MotorController* m_motorController; Encoder* m_encoder; void SetUp() override { switch (GetParam()) { case TEST_VICTOR: - m_speedController = new Victor(TestBench::kVictorChannel); + m_motorController = new Victor(TestBench::kVictorChannel); m_encoder = new Encoder(TestBench::kVictorEncoderChannelA, TestBench::kVictorEncoderChannelB); break; case TEST_JAGUAR: - m_speedController = new Jaguar(TestBench::kJaguarChannel); + m_motorController = new Jaguar(TestBench::kJaguarChannel); m_encoder = new Encoder(TestBench::kJaguarEncoderChannelA, TestBench::kJaguarEncoderChannelB); break; case TEST_TALON: - m_speedController = new Talon(TestBench::kTalonChannel); + m_motorController = new Talon(TestBench::kTalonChannel); m_encoder = new Encoder(TestBench::kTalonEncoderChannelA, TestBench::kTalonEncoderChannelB); break; @@ -59,13 +59,13 @@ class MotorInvertingTest } void TearDown() override { - delete m_speedController; + delete m_motorController; delete m_encoder; } void Reset() { - m_speedController->SetInverted(false); - m_speedController->Set(0.0); + m_motorController->SetInverted(false); + m_motorController->Set(0.0); m_encoder->Reset(); } }; @@ -73,13 +73,13 @@ class MotorInvertingTest TEST_P(MotorInvertingTest, InvertingPositive) { Reset(); - m_speedController->Set(motorSpeed); + m_motorController->Set(motorSpeed); Wait(delayTime); bool initDirection = m_encoder->GetDirection(); - m_speedController->SetInverted(true); - m_speedController->Set(motorSpeed); + m_motorController->SetInverted(true); + m_motorController->Set(motorSpeed); Wait(delayTime); @@ -92,14 +92,14 @@ TEST_P(MotorInvertingTest, InvertingPositive) { TEST_P(MotorInvertingTest, InvertingNegative) { Reset(); - m_speedController->SetInverted(false); - m_speedController->Set(-motorSpeed); + m_motorController->SetInverted(false); + m_motorController->Set(-motorSpeed); Wait(delayTime); bool initDirection = m_encoder->GetDirection(); - m_speedController->SetInverted(true); - m_speedController->Set(-motorSpeed); + m_motorController->SetInverted(true); + m_motorController->Set(-motorSpeed); Wait(delayTime); @@ -112,14 +112,14 @@ TEST_P(MotorInvertingTest, InvertingNegative) { TEST_P(MotorInvertingTest, InvertingSwitchingPosToNeg) { Reset(); - m_speedController->SetInverted(false); - m_speedController->Set(motorSpeed); + m_motorController->SetInverted(false); + m_motorController->Set(motorSpeed); Wait(delayTime); bool initDirection = m_encoder->GetDirection(); - m_speedController->SetInverted(true); - m_speedController->Set(-motorSpeed); + m_motorController->SetInverted(true); + m_motorController->Set(-motorSpeed); Wait(delayTime); @@ -132,14 +132,14 @@ TEST_P(MotorInvertingTest, InvertingSwitchingPosToNeg) { TEST_P(MotorInvertingTest, InvertingSwitchingNegToPos) { Reset(); - m_speedController->SetInverted(false); - m_speedController->Set(-motorSpeed); + m_motorController->SetInverted(false); + m_motorController->Set(-motorSpeed); Wait(delayTime); bool initDirection = m_encoder->GetDirection(); - m_speedController->SetInverted(true); - m_speedController->Set(motorSpeed); + m_motorController->SetInverted(true); + m_motorController->Set(motorSpeed); Wait(delayTime); diff --git a/wpilibcIntegrationTests/src/main/native/cpp/PowerDistributionPanelTest.cpp b/wpilibcIntegrationTests/src/main/native/cpp/PowerDistributionPanelTest.cpp index 349f0f5a16..06d12586c8 100644 --- a/wpilibcIntegrationTests/src/main/native/cpp/PowerDistributionPanelTest.cpp +++ b/wpilibcIntegrationTests/src/main/native/cpp/PowerDistributionPanelTest.cpp @@ -9,10 +9,10 @@ #include #include "TestBench.h" -#include "frc/Jaguar.h" -#include "frc/Talon.h" #include "frc/Timer.h" -#include "frc/Victor.h" +#include "frc/motorcontrol/Jaguar.h" +#include "frc/motorcontrol/Talon.h" +#include "frc/motorcontrol/Victor.h" #include "gtest/gtest.h" using namespace frc; diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/PWM.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/PWM.java index f21ba025ea..df1db7ded4 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/PWM.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/PWM.java @@ -239,7 +239,7 @@ public class PWM implements Sendable, AutoCloseable { } } - protected void setZeroLatch() { + public void setZeroLatch() { PWMJNI.latchPWMZero(m_handle); } diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/SpeedController.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/SpeedController.java index 7d03f477a0..490455c791 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/SpeedController.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/SpeedController.java @@ -4,17 +4,22 @@ package edu.wpi.first.wpilibj; -/** Interface for speed controlling devices. */ +/** + * Interface for motor controlling devices. + * + * @deprecated Use {@link edu.wpi.first.wpilibj.motorcontrol.MotorController}. + */ +@Deprecated(since = "2022", forRemoval = true) public interface SpeedController { /** - * Common interface for setting the speed of a speed controller. + * Common interface for setting the speed of a motor controller. * * @param speed The speed to set. Value should be between -1.0 and 1.0. */ void set(double speed); /** - * Sets the voltage output of the SpeedController. Compensates for the current bus voltage to + * Sets the voltage output of the MotorController. Compensates for the current bus voltage to * ensure that the desired voltage is output even if the battery voltage is below 12V - highly * useful when the voltage outputs are "meaningful" (e.g. they come from a feedforward * calculation). @@ -29,27 +34,27 @@ public interface SpeedController { } /** - * Common interface for getting the current set speed of a speed controller. + * Common interface for getting the current set speed of a motor controller. * * @return The current set speed. Value is between -1.0 and 1.0. */ double get(); /** - * Common interface for inverting direction of a speed controller. + * Common interface for inverting direction of a motor controller. * * @param isInverted The state of inversion true is inverted. */ void setInverted(boolean isInverted); /** - * Common interface for returning if a speed controller is in the inverted state or not. + * Common interface for returning if a motor controller is in the inverted state or not. * * @return isInverted The state of the inversion true is inverted. */ boolean getInverted(); - /** Disable the speed controller. */ + /** Disable the motor controller. */ void disable(); /** diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/SpeedControllerGroup.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/SpeedControllerGroup.java index e0a95c3079..9327f50a4d 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/SpeedControllerGroup.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/SpeedControllerGroup.java @@ -4,12 +4,19 @@ package edu.wpi.first.wpilibj; +import edu.wpi.first.wpilibj.motorcontrol.MotorController; import edu.wpi.first.wpilibj.smartdashboard.SendableBuilder; import edu.wpi.first.wpilibj.smartdashboard.SendableRegistry; import java.util.Arrays; -/** Allows multiple {@link SpeedController} objects to be linked together. */ -public class SpeedControllerGroup implements SpeedController, Sendable, AutoCloseable { +/** + * Allows multiple {@link SpeedController} objects to be linked together. + * + * @deprecated Use {@link edu.wpi.first.wpilibj.motorcontrol.MotorControllerGroup}. + */ +@Deprecated(since = "2022", forRemoval = true) +@SuppressWarnings("removal") +public class SpeedControllerGroup implements MotorController, Sendable, AutoCloseable { private boolean m_isInverted; private final SpeedController[] m_speedControllers; private static int instances; @@ -40,7 +47,7 @@ public class SpeedControllerGroup implements SpeedController, Sendable, AutoClos SendableRegistry.addChild(this, controller); } instances++; - SendableRegistry.addLW(this, "SpeedControllerGroup", instances); + SendableRegistry.addLW(this, "MotorControllerGroup", instances); } @Override @@ -89,7 +96,7 @@ public class SpeedControllerGroup implements SpeedController, Sendable, AutoClos @Override public void initSendable(SendableBuilder builder) { - builder.setSmartDashboardType("Speed Controller"); + builder.setSmartDashboardType("Motor Controller"); builder.setActuator(true); builder.setSafeState(this::stopMotor); builder.addDoubleProperty("Value", this::get, this::set); diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/drive/DifferentialDrive.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/drive/DifferentialDrive.java index f68f36d7c7..3231899673 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/drive/DifferentialDrive.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/drive/DifferentialDrive.java @@ -9,7 +9,6 @@ import edu.wpi.first.hal.FRCNetComm.tResourceType; import edu.wpi.first.hal.HAL; import edu.wpi.first.wpilibj.Sendable; import edu.wpi.first.wpilibj.SpeedController; -import edu.wpi.first.wpilibj.SpeedControllerGroup; import edu.wpi.first.wpilibj.smartdashboard.SendableBuilder; import edu.wpi.first.wpilibj.smartdashboard.SendableRegistry; import edu.wpi.first.wpiutil.math.MathUtil; @@ -20,21 +19,21 @@ import java.util.StringJoiner; * base, "tank drive", or West Coast Drive. * *

These drive bases typically have drop-center / skid-steer with two or more wheels per side - * (e.g., 6WD or 8WD). This class takes a SpeedController per side. For four and six motor - * drivetrains, construct and pass in {@link edu.wpi.first.wpilibj.SpeedControllerGroup} instances - * as follows. + * (e.g., 6WD or 8WD). This class takes a MotorController per side. For four and six motor + * drivetrains, construct and pass in {@link + * edu.wpi.first.wpilibj.motorcontrol.MotorControllerGroup} instances as follows. * *

Four motor drivetrain: * *


  * public class Robot {
- *   SpeedController m_frontLeft = new PWMVictorSPX(1);
- *   SpeedController m_rearLeft = new PWMVictorSPX(2);
- *   SpeedControllerGroup m_left = new SpeedControllerGroup(m_frontLeft, m_rearLeft);
+ *   MotorController m_frontLeft = new PWMVictorSPX(1);
+ *   MotorController m_rearLeft = new PWMVictorSPX(2);
+ *   MotorControllerGroup m_left = new MotorControllerGroup(m_frontLeft, m_rearLeft);
  *
- *   SpeedController m_frontRight = new PWMVictorSPX(3);
- *   SpeedController m_rearRight = new PWMVictorSPX(4);
- *   SpeedControllerGroup m_right = new SpeedControllerGroup(m_frontRight, m_rearRight);
+ *   MotorController m_frontRight = new PWMVictorSPX(3);
+ *   MotorController m_rearRight = new PWMVictorSPX(4);
+ *   MotorControllerGroup m_right = new MotorControllerGroup(m_frontRight, m_rearRight);
  *
  *   DifferentialDrive m_drive = new DifferentialDrive(m_left, m_right);
  * }
@@ -44,15 +43,15 @@ import java.util.StringJoiner;
  *
  * 

  * public class Robot {
- *   SpeedController m_frontLeft = new PWMVictorSPX(1);
- *   SpeedController m_midLeft = new PWMVictorSPX(2);
- *   SpeedController m_rearLeft = new PWMVictorSPX(3);
- *   SpeedControllerGroup m_left = new SpeedControllerGroup(m_frontLeft, m_midLeft, m_rearLeft);
+ *   MotorController m_frontLeft = new PWMVictorSPX(1);
+ *   MotorController m_midLeft = new PWMVictorSPX(2);
+ *   MotorController m_rearLeft = new PWMVictorSPX(3);
+ *   MotorControllerGroup m_left = new MotorControllerGroup(m_frontLeft, m_midLeft, m_rearLeft);
  *
- *   SpeedController m_frontRight = new PWMVictorSPX(4);
- *   SpeedController m_midRight = new PWMVictorSPX(5);
- *   SpeedController m_rearRight = new PWMVictorSPX(6);
- *   SpeedControllerGroup m_right = new SpeedControllerGroup(m_frontRight, m_midRight, m_rearRight);
+ *   MotorController m_frontRight = new PWMVictorSPX(4);
+ *   MotorController m_midRight = new PWMVictorSPX(5);
+ *   MotorController m_rearRight = new PWMVictorSPX(6);
+ *   MotorControllerGroup m_right = new MotorControllerGroup(m_frontRight, m_midRight, m_rearRight);
  *
  *   DifferentialDrive m_drive = new DifferentialDrive(m_left, m_right);
  * }
@@ -94,6 +93,7 @@ import java.util.StringJoiner;
  * drive(double, double) with the addition of a quick turn mode. However, it is not designed to give
  * exactly the same response.
  */
+@SuppressWarnings("removal")
 public class DifferentialDrive extends RobotDriveBase implements Sendable, AutoCloseable {
   public static final double kDefaultQuickStopThreshold = 0.2;
   public static final double kDefaultQuickStopAlpha = 0.1;
@@ -112,8 +112,9 @@ public class DifferentialDrive extends RobotDriveBase implements Sendable, AutoC
   /**
    * Construct a DifferentialDrive.
    *
-   * 

To pass multiple motors per side, use a {@link SpeedControllerGroup}. If a motor needs to be - * inverted, do so before passing it in. + *

To pass multiple motors per side, use a {@link + * edu.wpi.first.wpilibj.motorcontrol.MotorControllerGroup}. If a motor needs to be inverted, do + * so before passing it in. */ public DifferentialDrive(SpeedController leftMotor, SpeedController rightMotor) { verify(leftMotor, rightMotor); diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/drive/KilloughDrive.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/drive/KilloughDrive.java index fbc62ff1be..6702fb84f6 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/drive/KilloughDrive.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/drive/KilloughDrive.java @@ -39,6 +39,7 @@ import java.util.StringJoiner; * points down. Rotations follow the right-hand rule, so clockwise rotation around the Z axis is * positive. */ +@SuppressWarnings("removal") public class KilloughDrive extends RobotDriveBase implements Sendable, AutoCloseable { public static final double kDefaultLeftMotorAngle = 60.0; public static final double kDefaultRightMotorAngle = 120.0; diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/drive/MecanumDrive.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/drive/MecanumDrive.java index 1930ecfabe..7781b9409f 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/drive/MecanumDrive.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/drive/MecanumDrive.java @@ -57,6 +57,7 @@ import java.util.StringJoiner; * {@link #drivePolar(double, double, double)} is equivalent to RobotDrive's * mecanumDrive_Polar(double, double, double)} if a deadband of 0 is used. */ +@SuppressWarnings("removal") public class MecanumDrive extends RobotDriveBase implements Sendable, AutoCloseable { private static int instances; diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/DMC60.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/motorcontrol/DMC60.java similarity index 89% rename from wpilibj/src/main/java/edu/wpi/first/wpilibj/DMC60.java rename to wpilibj/src/main/java/edu/wpi/first/wpilibj/motorcontrol/DMC60.java index 2f38230ea9..c003b566ed 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/DMC60.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/motorcontrol/DMC60.java @@ -2,13 +2,14 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.first.wpilibj; +package edu.wpi.first.wpilibj.motorcontrol; import edu.wpi.first.hal.FRCNetComm.tResourceType; import edu.wpi.first.hal.HAL; +import edu.wpi.first.wpilibj.PWM; /** - * Digilent DMC 60 Speed Controller. + * Digilent DMC 60 Motor Controller. * *

Note that the DMC uses the following bounds for PWM values. These values should work * reasonably well for most controllers, but if users experience issues such as asymmetric behavior @@ -24,7 +25,7 @@ import edu.wpi.first.hal.HAL; *

  • 0.997ms = full "reverse" * */ -public class DMC60 extends PWMSpeedController { +public class DMC60 extends PWMMotorController { /** * Constructor. * diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/Jaguar.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/motorcontrol/Jaguar.java similarity index 88% rename from wpilibj/src/main/java/edu/wpi/first/wpilibj/Jaguar.java rename to wpilibj/src/main/java/edu/wpi/first/wpilibj/motorcontrol/Jaguar.java index 5e69c4a50a..32e6417edd 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/Jaguar.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/motorcontrol/Jaguar.java @@ -2,13 +2,14 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.first.wpilibj; +package edu.wpi.first.wpilibj.motorcontrol; import edu.wpi.first.hal.FRCNetComm.tResourceType; import edu.wpi.first.hal.HAL; +import edu.wpi.first.wpilibj.PWM; /** - * Texas Instruments / Vex Robotics Jaguar Speed Controller as a PWM device. + * Texas Instruments / Vex Robotics Jaguar Motor Controller as a PWM device. * *

    Note that the Jaguar uses the following bounds for PWM values. These values should work * reasonably well for most controllers, but if users experience issues such as asymmetric behavior @@ -23,7 +24,7 @@ import edu.wpi.first.hal.HAL; *

  • 0.697ms = full "reverse" * */ -public class Jaguar extends PWMSpeedController { +public class Jaguar extends PWMMotorController { /** * Constructor. * diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/motorcontrol/MotorController.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/motorcontrol/MotorController.java new file mode 100644 index 0000000000..09890196ff --- /dev/null +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/motorcontrol/MotorController.java @@ -0,0 +1,11 @@ +// Copyright (c) FIRST and other WPILib contributors. +// Open Source Software; you can modify and/or share it under the terms of +// the WPILib BSD license file in the root directory of this project. + +package edu.wpi.first.wpilibj.motorcontrol; + +import edu.wpi.first.wpilibj.SpeedController; + +/** Interface for motor controlling devices. */ +@SuppressWarnings("removal") +public interface MotorController extends SpeedController {} diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/motorcontrol/MotorControllerGroup.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/motorcontrol/MotorControllerGroup.java new file mode 100644 index 0000000000..b0b11129eb --- /dev/null +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/motorcontrol/MotorControllerGroup.java @@ -0,0 +1,98 @@ +// Copyright (c) FIRST and other WPILib contributors. +// Open Source Software; you can modify and/or share it under the terms of +// the WPILib BSD license file in the root directory of this project. + +package edu.wpi.first.wpilibj.motorcontrol; + +import edu.wpi.first.wpilibj.Sendable; +import edu.wpi.first.wpilibj.smartdashboard.SendableBuilder; +import edu.wpi.first.wpilibj.smartdashboard.SendableRegistry; +import java.util.Arrays; + +/** Allows multiple {@link MotorController} objects to be linked together. */ +public class MotorControllerGroup implements MotorController, Sendable, AutoCloseable { + private boolean m_isInverted; + private final MotorController[] m_motorControllers; + private static int instances; + + /** + * Create a new MotorControllerGroup with the provided MotorControllers. + * + * @param motorControllers The MotorControllers to add + */ + @SuppressWarnings("PMD.AvoidArrayLoops") + public MotorControllerGroup( + MotorController motorController, MotorController... motorControllers) { + m_motorControllers = new MotorController[motorControllers.length + 1]; + m_motorControllers[0] = motorController; + for (int i = 0; i < motorControllers.length; i++) { + m_motorControllers[i + 1] = motorControllers[i]; + } + init(); + } + + public MotorControllerGroup(MotorController[] motorControllers) { + m_motorControllers = Arrays.copyOf(motorControllers, motorControllers.length); + init(); + } + + private void init() { + for (MotorController controller : m_motorControllers) { + SendableRegistry.addChild(this, controller); + } + instances++; + SendableRegistry.addLW(this, "MotorControllerGroup", instances); + } + + @Override + public void close() { + SendableRegistry.remove(this); + } + + @Override + public void set(double speed) { + for (MotorController motorController : m_motorControllers) { + motorController.set(m_isInverted ? -speed : speed); + } + } + + @Override + public double get() { + if (m_motorControllers.length > 0) { + return m_motorControllers[0].get() * (m_isInverted ? -1 : 1); + } + return 0.0; + } + + @Override + public void setInverted(boolean isInverted) { + m_isInverted = isInverted; + } + + @Override + public boolean getInverted() { + return m_isInverted; + } + + @Override + public void disable() { + for (MotorController motorController : m_motorControllers) { + motorController.disable(); + } + } + + @Override + public void stopMotor() { + for (MotorController motorController : m_motorControllers) { + motorController.stopMotor(); + } + } + + @Override + public void initSendable(SendableBuilder builder) { + builder.setSmartDashboardType("Motor Controller"); + builder.setActuator(true); + builder.setSafeState(this::stopMotor); + builder.addDoubleProperty("Value", this::get, this::set); + } +} diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/NidecBrushless.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/motorcontrol/NidecBrushless.java similarity index 93% rename from wpilibj/src/main/java/edu/wpi/first/wpilibj/NidecBrushless.java rename to wpilibj/src/main/java/edu/wpi/first/wpilibj/motorcontrol/NidecBrushless.java index 6a9d9e8b2d..c23827c8af 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/NidecBrushless.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/motorcontrol/NidecBrushless.java @@ -2,16 +2,20 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.first.wpilibj; +package edu.wpi.first.wpilibj.motorcontrol; import edu.wpi.first.hal.FRCNetComm.tResourceType; import edu.wpi.first.hal.HAL; +import edu.wpi.first.wpilibj.DigitalOutput; +import edu.wpi.first.wpilibj.MotorSafety; +import edu.wpi.first.wpilibj.PWM; +import edu.wpi.first.wpilibj.Sendable; import edu.wpi.first.wpilibj.smartdashboard.SendableBuilder; import edu.wpi.first.wpilibj.smartdashboard.SendableRegistry; /** Nidec Brushless Motor. */ public class NidecBrushless extends MotorSafety - implements SpeedController, Sendable, AutoCloseable { + implements MotorController, Sendable, AutoCloseable { private boolean m_isInverted; private final DigitalOutput m_dio; private final PWM m_pwm; diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/PWMSpeedController.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/motorcontrol/PWMMotorController.java similarity index 81% rename from wpilibj/src/main/java/edu/wpi/first/wpilibj/PWMSpeedController.java rename to wpilibj/src/main/java/edu/wpi/first/wpilibj/motorcontrol/PWMMotorController.java index 4a0f7e60f1..34e712102c 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/PWMSpeedController.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/motorcontrol/PWMMotorController.java @@ -2,14 +2,17 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.first.wpilibj; +package edu.wpi.first.wpilibj.motorcontrol; +import edu.wpi.first.wpilibj.MotorSafety; +import edu.wpi.first.wpilibj.PWM; +import edu.wpi.first.wpilibj.Sendable; import edu.wpi.first.wpilibj.smartdashboard.SendableBuilder; import edu.wpi.first.wpilibj.smartdashboard.SendableRegistry; -/** Common base class for all PWM Speed Controllers. */ -public abstract class PWMSpeedController extends MotorSafety - implements SpeedController, Sendable, AutoCloseable { +/** Common base class for all PWM Motor Controllers. */ +public abstract class PWMMotorController extends MotorSafety + implements MotorController, Sendable, AutoCloseable { private boolean m_isInverted; protected PWM m_pwm; @@ -20,7 +23,7 @@ public abstract class PWMSpeedController extends MotorSafety * @param channel The PWM channel that the controller is attached to. 0-9 are on-board, 10-19 are * on the MXP port */ - protected PWMSpeedController(final String name, final int channel) { + protected PWMMotorController(final String name, final int channel) { m_pwm = new PWM(channel, false); SendableRegistry.addLW(this, name, channel); } @@ -48,7 +51,7 @@ public abstract class PWMSpeedController extends MotorSafety /** * Get the recently set value of the PWM. This value is affected by the inversion property. If you - * want the value that is sent directly to the SpeedController, use {@link + * want the value that is sent directly to the MotorController, use {@link * edu.wpi.first.wpilibj.PWM#getSpeed()} instead. * * @return The most recently set value for the PWM between -1.0 and 1.0. @@ -94,7 +97,7 @@ public abstract class PWMSpeedController extends MotorSafety @Override public void initSendable(SendableBuilder builder) { - builder.setSmartDashboardType("Speed Controller"); + builder.setSmartDashboardType("Motor Controller"); builder.setActuator(true); builder.setSafeState(this::disable); builder.addDoubleProperty("Value", this::get, this::set); diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/PWMSparkMax.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/motorcontrol/PWMSparkMax.java similarity index 87% rename from wpilibj/src/main/java/edu/wpi/first/wpilibj/PWMSparkMax.java rename to wpilibj/src/main/java/edu/wpi/first/wpilibj/motorcontrol/PWMSparkMax.java index b57fd9ac35..f14fdc7435 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/PWMSparkMax.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/motorcontrol/PWMSparkMax.java @@ -2,13 +2,14 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.first.wpilibj; +package edu.wpi.first.wpilibj.motorcontrol; import edu.wpi.first.hal.FRCNetComm.tResourceType; import edu.wpi.first.hal.HAL; +import edu.wpi.first.wpilibj.PWM; /** - * REV Robotics SPARK MAX Speed Controller with PWM control. + * REV Robotics SPARK MAX Motor Controller with PWM control. * *

    Note that the SPARK MAX uses the following bounds for PWM values. These values should work * reasonably well for most controllers, but if users experience issues such as asymmetric behavior @@ -24,7 +25,7 @@ import edu.wpi.first.hal.HAL; *

  • 0.999ms = full "reverse" * */ -public class PWMSparkMax extends PWMSpeedController { +public class PWMSparkMax extends PWMMotorController { /** Common initialization code called by all constructors. */ public PWMSparkMax(final int channel) { super("PWMSparkMax", channel); diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/PWMTalonFX.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/motorcontrol/PWMTalonFX.java similarity index 88% rename from wpilibj/src/main/java/edu/wpi/first/wpilibj/PWMTalonFX.java rename to wpilibj/src/main/java/edu/wpi/first/wpilibj/motorcontrol/PWMTalonFX.java index 330c6abe30..d5217459a1 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/PWMTalonFX.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/motorcontrol/PWMTalonFX.java @@ -2,13 +2,14 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.first.wpilibj; +package edu.wpi.first.wpilibj.motorcontrol; import edu.wpi.first.hal.FRCNetComm.tResourceType; import edu.wpi.first.hal.HAL; +import edu.wpi.first.wpilibj.PWM; /** - * Cross the Road Electronics (CTRE) Talon FX Speed Controller with PWM control. + * Cross the Road Electronics (CTRE) Talon FX Motor Controller with PWM control. * *

    Note that the TalonFX uses the following bounds for PWM values. These values should work * reasonably well for most controllers, but if users experience issues such as asymmetric behavior @@ -24,7 +25,7 @@ import edu.wpi.first.hal.HAL; *

  • 0.997ms = full "reverse" * */ -public class PWMTalonFX extends PWMSpeedController { +public class PWMTalonFX extends PWMMotorController { /** * Constructor for a TalonFX connected via PWM. * diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/PWMTalonSRX.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/motorcontrol/PWMTalonSRX.java similarity index 88% rename from wpilibj/src/main/java/edu/wpi/first/wpilibj/PWMTalonSRX.java rename to wpilibj/src/main/java/edu/wpi/first/wpilibj/motorcontrol/PWMTalonSRX.java index 77c7d37475..81f183407d 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/PWMTalonSRX.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/motorcontrol/PWMTalonSRX.java @@ -2,13 +2,14 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.first.wpilibj; +package edu.wpi.first.wpilibj.motorcontrol; import edu.wpi.first.hal.FRCNetComm.tResourceType; import edu.wpi.first.hal.HAL; +import edu.wpi.first.wpilibj.PWM; /** - * Cross the Road Electronics (CTRE) Talon SRX Speed Controller with PWM control. + * Cross the Road Electronics (CTRE) Talon SRX Motor Controller with PWM control. * *

    Note that the TalonSRX uses the following bounds for PWM values. These values should work * reasonably well for most controllers, but if users experience issues such as asymmetric behavior @@ -24,7 +25,7 @@ import edu.wpi.first.hal.HAL; *

  • 0.997ms = full "reverse" * */ -public class PWMTalonSRX extends PWMSpeedController { +public class PWMTalonSRX extends PWMMotorController { /** * Constructor for a TalonSRX connected via PWM. * diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/PWMVenom.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/motorcontrol/PWMVenom.java similarity index 91% rename from wpilibj/src/main/java/edu/wpi/first/wpilibj/PWMVenom.java rename to wpilibj/src/main/java/edu/wpi/first/wpilibj/motorcontrol/PWMVenom.java index 8a71842a14..9f7a885d51 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/PWMVenom.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/motorcontrol/PWMVenom.java @@ -2,10 +2,11 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.first.wpilibj; +package edu.wpi.first.wpilibj.motorcontrol; import edu.wpi.first.hal.FRCNetComm.tResourceType; import edu.wpi.first.hal.HAL; +import edu.wpi.first.wpilibj.PWM; /** * Playing with Fusion Venom Smart Motor with PWM control. @@ -23,7 +24,7 @@ import edu.wpi.first.hal.HAL; *
  • 0.997ms = full "reverse" * */ -public class PWMVenom extends PWMSpeedController { +public class PWMVenom extends PWMMotorController { /** * Constructor for a Venom connected via PWM. * diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/PWMVictorSPX.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/motorcontrol/PWMVictorSPX.java similarity index 88% rename from wpilibj/src/main/java/edu/wpi/first/wpilibj/PWMVictorSPX.java rename to wpilibj/src/main/java/edu/wpi/first/wpilibj/motorcontrol/PWMVictorSPX.java index 62c86087ac..9880464637 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/PWMVictorSPX.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/motorcontrol/PWMVictorSPX.java @@ -2,13 +2,14 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.first.wpilibj; +package edu.wpi.first.wpilibj.motorcontrol; import edu.wpi.first.hal.FRCNetComm.tResourceType; import edu.wpi.first.hal.HAL; +import edu.wpi.first.wpilibj.PWM; /** - * Cross the Road Electronics (CTRE) Victor SPX Speed Controller with PWM control. + * Cross the Road Electronics (CTRE) Victor SPX Motor Controller with PWM control. * *

    Note that the Victor SPX uses the following bounds for PWM values. These values should work * reasonably well for most controllers, but if users experience issues such as asymmetric behavior @@ -24,7 +25,7 @@ import edu.wpi.first.hal.HAL; *

  • 0.997ms = full "reverse" * */ -public class PWMVictorSPX extends PWMSpeedController { +public class PWMVictorSPX extends PWMMotorController { /** * Constructor for a Victor SPX connected via PWM. * diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/SD540.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/motorcontrol/SD540.java similarity index 89% rename from wpilibj/src/main/java/edu/wpi/first/wpilibj/SD540.java rename to wpilibj/src/main/java/edu/wpi/first/wpilibj/motorcontrol/SD540.java index 8a14463964..3876dfc500 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/SD540.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/motorcontrol/SD540.java @@ -2,13 +2,14 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.first.wpilibj; +package edu.wpi.first.wpilibj.motorcontrol; import edu.wpi.first.hal.FRCNetComm.tResourceType; import edu.wpi.first.hal.HAL; +import edu.wpi.first.wpilibj.PWM; /** - * Mindsensors SD540 Speed Controller. + * Mindsensors SD540 Motor Controller. * *

    Note that the SD540 uses the following bounds for PWM values. These values should work * reasonably well for most controllers, but if users experience issues such as asymmetric behavior @@ -24,7 +25,7 @@ import edu.wpi.first.hal.HAL; *

  • 0.94ms = full "reverse" * */ -public class SD540 extends PWMSpeedController { +public class SD540 extends PWMMotorController { /** * Constructor. * diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/Spark.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/motorcontrol/Spark.java similarity index 89% rename from wpilibj/src/main/java/edu/wpi/first/wpilibj/Spark.java rename to wpilibj/src/main/java/edu/wpi/first/wpilibj/motorcontrol/Spark.java index cf3d8d29ba..1b99228ce0 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/Spark.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/motorcontrol/Spark.java @@ -2,13 +2,14 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.first.wpilibj; +package edu.wpi.first.wpilibj.motorcontrol; import edu.wpi.first.hal.FRCNetComm.tResourceType; import edu.wpi.first.hal.HAL; +import edu.wpi.first.wpilibj.PWM; /** - * REV Robotics SPARK Speed Controller. + * REV Robotics SPARK Motor Controller. * *

    Note that the SPARK uses the following bounds for PWM values. These values should work * reasonably well for most controllers, but if users experience issues such as asymmetric behavior @@ -24,7 +25,7 @@ import edu.wpi.first.hal.HAL; *

  • 0.999ms = full "reverse" * */ -public class Spark extends PWMSpeedController { +public class Spark extends PWMMotorController { /** * Constructor. * diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/Talon.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/motorcontrol/Talon.java similarity index 88% rename from wpilibj/src/main/java/edu/wpi/first/wpilibj/Talon.java rename to wpilibj/src/main/java/edu/wpi/first/wpilibj/motorcontrol/Talon.java index a0b3ac5694..576ba6aefa 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/Talon.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/motorcontrol/Talon.java @@ -2,13 +2,14 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.first.wpilibj; +package edu.wpi.first.wpilibj.motorcontrol; import edu.wpi.first.hal.FRCNetComm.tResourceType; import edu.wpi.first.hal.HAL; +import edu.wpi.first.wpilibj.PWM; /** - * Cross the Road Electronics (CTRE) Talon and Talon SR Speed Controller. + * Cross the Road Electronics (CTRE) Talon and Talon SR Motor Controller. * *

    Note that the Talon uses the following bounds for PWM values. These values should work * reasonably well for most controllers, but if users experience issues such as asymmetric behavior @@ -23,7 +24,7 @@ import edu.wpi.first.hal.HAL; *

  • 0.989ms = full "reverse" * */ -public class Talon extends PWMSpeedController { +public class Talon extends PWMMotorController { /** * Constructor for a Talon (original or Talon SR). * diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/Victor.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/motorcontrol/Victor.java similarity index 87% rename from wpilibj/src/main/java/edu/wpi/first/wpilibj/Victor.java rename to wpilibj/src/main/java/edu/wpi/first/wpilibj/motorcontrol/Victor.java index bdd9ba7d2d..fac6c4ff90 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/Victor.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/motorcontrol/Victor.java @@ -2,13 +2,14 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.first.wpilibj; +package edu.wpi.first.wpilibj.motorcontrol; import edu.wpi.first.hal.FRCNetComm.tResourceType; import edu.wpi.first.hal.HAL; +import edu.wpi.first.wpilibj.PWM; /** - * VEX Robotics Victor 888 Speed Controller The Vex Robotics Victor 884 Speed Controller can also be + * VEX Robotics Victor 888 Motor Controller The Vex Robotics Victor 884 Motor Controller can also be * used with this class but may need to be calibrated per the Victor 884 user manual. * *

    Note that the Victor uses the following bounds for PWM values. These values were determined @@ -26,7 +27,7 @@ import edu.wpi.first.hal.HAL; *

  • 1.026ms = full "reverse" * */ -public class Victor extends PWMSpeedController { +public class Victor extends PWMMotorController { /** * Constructor. * diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/VictorSP.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/motorcontrol/VictorSP.java similarity index 89% rename from wpilibj/src/main/java/edu/wpi/first/wpilibj/VictorSP.java rename to wpilibj/src/main/java/edu/wpi/first/wpilibj/motorcontrol/VictorSP.java index b588c5c9a2..10dba67cff 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/VictorSP.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/motorcontrol/VictorSP.java @@ -2,13 +2,14 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.first.wpilibj; +package edu.wpi.first.wpilibj.motorcontrol; import edu.wpi.first.hal.FRCNetComm.tResourceType; import edu.wpi.first.hal.HAL; +import edu.wpi.first.wpilibj.PWM; /** - * VEX Robotics Victor SP Speed Controller. + * VEX Robotics Victor SP Motor Controller. * *

    Note that the VictorSP uses the following bounds for PWM values. These values should work * reasonably well for most controllers, but if users experience issues such as asymmetric behavior @@ -24,7 +25,7 @@ import edu.wpi.first.hal.HAL; *

  • 0.997ms = full "reverse" * */ -public class VictorSP extends PWMSpeedController { +public class VictorSP extends PWMMotorController { /** * Constructor. * diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/shuffleboard/BuiltInWidgets.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/shuffleboard/BuiltInWidgets.java index 7db33edee6..f518a04830 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/shuffleboard/BuiltInWidgets.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/shuffleboard/BuiltInWidgets.java @@ -245,27 +245,27 @@ public enum BuiltInWidgets implements WidgetType { */ kEncoder("Encoder"), /** - * Displays a {@link edu.wpi.first.wpilibj.SpeedController SpeedController}. The speed controller - * will be controllable from the dashboard when test mode is enabled, but will otherwise be - * view-only.
    + * Displays a {@link edu.wpi.first.wpilibj.motorcontrol.MotorController MotorController}. The + * speed controller will be controllable from the dashboard when test mode is enabled, but will + * otherwise be view-only.
    * Supported types: * *
      - *
    • {@link edu.wpi.first.wpilibj.PWMSpeedController} - *
    • {@link edu.wpi.first.wpilibj.DMC60} - *
    • {@link edu.wpi.first.wpilibj.Jaguar} - *
    • {@link edu.wpi.first.wpilibj.PWMSparkMax} - *
    • {@link edu.wpi.first.wpilibj.PWMTalonFX} - *
    • {@link edu.wpi.first.wpilibj.PWMTalonSRX} - *
    • {@link edu.wpi.first.wpilibj.PWMVenom} - *
    • {@link edu.wpi.first.wpilibj.PWMVictorSPX} - *
    • {@link edu.wpi.first.wpilibj.SD540} - *
    • {@link edu.wpi.first.wpilibj.Spark} - *
    • {@link edu.wpi.first.wpilibj.Talon} - *
    • {@link edu.wpi.first.wpilibj.Victor} - *
    • {@link edu.wpi.first.wpilibj.VictorSP} - *
    • {@link edu.wpi.first.wpilibj.SpeedControllerGroup} - *
    • Any custom subclass of {@code SpeedController} + *
    • {@link edu.wpi.first.wpilibj.motorcontrol.PWMMotorController} + *
    • {@link edu.wpi.first.wpilibj.motorcontrol.DMC60} + *
    • {@link edu.wpi.first.wpilibj.motorcontrol.Jaguar} + *
    • {@link edu.wpi.first.wpilibj.motorcontrol.PWMSparkMax} + *
    • {@link edu.wpi.first.wpilibj.motorcontrol.PWMTalonFX} + *
    • {@link edu.wpi.first.wpilibj.motorcontrol.PWMTalonSRX} + *
    • {@link edu.wpi.first.wpilibj.motorcontrol.PWMVenom} + *
    • {@link edu.wpi.first.wpilibj.motorcontrol.PWMVictorSPX} + *
    • {@link edu.wpi.first.wpilibj.motorcontrol.SD540} + *
    • {@link edu.wpi.first.wpilibj.motorcontrol.Spark} + *
    • {@link edu.wpi.first.wpilibj.motorcontrol.Talon} + *
    • {@link edu.wpi.first.wpilibj.motorcontrol.Victor} + *
    • {@link edu.wpi.first.wpilibj.motorcontrol.VictorSP} + *
    • {@link edu.wpi.first.wpilibj.motorcontrol.MotorControllerGroup} + *
    • Any custom subclass of {@code MotorController} *
    * *
    @@ -277,7 +277,7 @@ public enum BuiltInWidgets implements WidgetType { * One of {@code ["HORIZONTAL", "VERTICAL"]} * */ - kSpeedController("Speed Controller"), + kMotorController("Motor Controller"), /** * Displays a command with a toggle button. Pressing the button will start the command, and the * button will automatically release when the command completes.
    diff --git a/wpilibj/src/test/java/edu/wpi/first/wpilibj/SpeedControllerGroupTest.java b/wpilibj/src/test/java/edu/wpi/first/wpilibj/SpeedControllerGroupTest.java deleted file mode 100644 index 5f4a80cbff..0000000000 --- a/wpilibj/src/test/java/edu/wpi/first/wpilibj/SpeedControllerGroupTest.java +++ /dev/null @@ -1,101 +0,0 @@ -// Copyright (c) FIRST and other WPILib contributors. -// Open Source Software; you can modify and/or share it under the terms of -// the WPILib BSD license file in the root directory of this project. - -package edu.wpi.first.wpilibj; - -import static org.junit.jupiter.api.Assertions.assertArrayEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; - -import java.util.Arrays; -import java.util.stream.DoubleStream; -import java.util.stream.IntStream; -import java.util.stream.Stream; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.Arguments; -import org.junit.jupiter.params.provider.MethodSource; - -class SpeedControllerGroupTest { - private static Stream speedControllerArguments() { - return IntStream.of(1, 2, 3) - .mapToObj( - number -> { - SpeedController[] speedControllers = - Stream.generate(MockSpeedController::new) - .limit(number) - .toArray(SpeedController[]::new); - SpeedControllerGroup group = - new SpeedControllerGroup( - speedControllers[0], - Arrays.copyOfRange(speedControllers, 1, speedControllers.length)); - return Arguments.of(group, speedControllers); - }); - } - - @ParameterizedTest - @MethodSource("speedControllerArguments") - void setTest(final SpeedControllerGroup group, final SpeedController[] speedControllers) { - group.set(1.0); - - assertArrayEquals( - DoubleStream.generate(() -> 1.0).limit(speedControllers.length).toArray(), - Arrays.stream(speedControllers).mapToDouble(SpeedController::get).toArray(), - 0.00005); - } - - @ParameterizedTest - @MethodSource("speedControllerArguments") - void getInvertedTest(final SpeedControllerGroup group, final SpeedController[] speedControllers) { - group.setInverted(true); - - assertTrue(group.getInverted()); - } - - @ParameterizedTest - @MethodSource("speedControllerArguments") - void setInvertedDoesNotModifySpeedControllersTest( - final SpeedControllerGroup group, final SpeedController[] speedControllers) { - group.setInverted(true); - - assertArrayEquals( - Stream.generate(() -> false).limit(speedControllers.length).toArray(), - Arrays.stream(speedControllers).map(SpeedController::getInverted).toArray()); - } - - @ParameterizedTest - @MethodSource("speedControllerArguments") - void setInvertedDoesInvertTest( - final SpeedControllerGroup group, final SpeedController[] speedControllers) { - group.setInverted(true); - group.set(1.0); - - assertArrayEquals( - DoubleStream.generate(() -> -1.0).limit(speedControllers.length).toArray(), - Arrays.stream(speedControllers).mapToDouble(SpeedController::get).toArray(), - 0.00005); - } - - @ParameterizedTest - @MethodSource("speedControllerArguments") - void disableTest(final SpeedControllerGroup group, final SpeedController[] speedControllers) { - group.set(1.0); - group.disable(); - - assertArrayEquals( - DoubleStream.generate(() -> 0.0).limit(speedControllers.length).toArray(), - Arrays.stream(speedControllers).mapToDouble(SpeedController::get).toArray(), - 0.00005); - } - - @ParameterizedTest - @MethodSource("speedControllerArguments") - void stopMotorTest(final SpeedControllerGroup group, final SpeedController[] speedControllers) { - group.set(1.0); - group.stopMotor(); - - assertArrayEquals( - DoubleStream.generate(() -> 0.0).limit(speedControllers.length).toArray(), - Arrays.stream(speedControllers).mapToDouble(SpeedController::get).toArray(), - 0.00005); - } -} diff --git a/wpilibjIntegrationTests/src/main/java/edu/wpi/first/wpilibj/MockSpeedController.java b/wpilibj/src/test/java/edu/wpi/first/wpilibj/motorcontrol/MockMotorController.java similarity index 86% rename from wpilibjIntegrationTests/src/main/java/edu/wpi/first/wpilibj/MockSpeedController.java rename to wpilibj/src/test/java/edu/wpi/first/wpilibj/motorcontrol/MockMotorController.java index a7bd4a802f..e7a6b8eb36 100644 --- a/wpilibjIntegrationTests/src/main/java/edu/wpi/first/wpilibj/MockSpeedController.java +++ b/wpilibj/src/test/java/edu/wpi/first/wpilibj/motorcontrol/MockMotorController.java @@ -2,9 +2,9 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.first.wpilibj; +package edu.wpi.first.wpilibj.motorcontrol; -public class MockSpeedController implements SpeedController { +public class MockMotorController implements MotorController { private double m_speed; private boolean m_isInverted; diff --git a/wpilibj/src/test/java/edu/wpi/first/wpilibj/motorcontrol/MotorControllerGroupTest.java b/wpilibj/src/test/java/edu/wpi/first/wpilibj/motorcontrol/MotorControllerGroupTest.java new file mode 100644 index 0000000000..6efb3def8b --- /dev/null +++ b/wpilibj/src/test/java/edu/wpi/first/wpilibj/motorcontrol/MotorControllerGroupTest.java @@ -0,0 +1,101 @@ +// Copyright (c) FIRST and other WPILib contributors. +// Open Source Software; you can modify and/or share it under the terms of +// the WPILib BSD license file in the root directory of this project. + +package edu.wpi.first.wpilibj.motorcontrol; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.Arrays; +import java.util.stream.DoubleStream; +import java.util.stream.IntStream; +import java.util.stream.Stream; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +class MotorControllerGroupTest { + private static Stream motorControllerArguments() { + return IntStream.of(1, 2, 3) + .mapToObj( + number -> { + MotorController[] motorControllers = + Stream.generate(MockMotorController::new) + .limit(number) + .toArray(MotorController[]::new); + MotorControllerGroup group = + new MotorControllerGroup( + motorControllers[0], + Arrays.copyOfRange(motorControllers, 1, motorControllers.length)); + return Arguments.of(group, motorControllers); + }); + } + + @ParameterizedTest + @MethodSource("motorControllerArguments") + void setTest(final MotorControllerGroup group, final MotorController[] motorControllers) { + group.set(1.0); + + assertArrayEquals( + DoubleStream.generate(() -> 1.0).limit(motorControllers.length).toArray(), + Arrays.stream(motorControllers).mapToDouble(MotorController::get).toArray(), + 0.00005); + } + + @ParameterizedTest + @MethodSource("motorControllerArguments") + void getInvertedTest(final MotorControllerGroup group, final MotorController[] motorControllers) { + group.setInverted(true); + + assertTrue(group.getInverted()); + } + + @ParameterizedTest + @MethodSource("motorControllerArguments") + void setInvertedDoesNotModifyMotorControllersTest( + final MotorControllerGroup group, final MotorController[] motorControllers) { + group.setInverted(true); + + assertArrayEquals( + Stream.generate(() -> false).limit(motorControllers.length).toArray(), + Arrays.stream(motorControllers).map(MotorController::getInverted).toArray()); + } + + @ParameterizedTest + @MethodSource("motorControllerArguments") + void setInvertedDoesInvertTest( + final MotorControllerGroup group, final MotorController[] motorControllers) { + group.setInverted(true); + group.set(1.0); + + assertArrayEquals( + DoubleStream.generate(() -> -1.0).limit(motorControllers.length).toArray(), + Arrays.stream(motorControllers).mapToDouble(MotorController::get).toArray(), + 0.00005); + } + + @ParameterizedTest + @MethodSource("motorControllerArguments") + void disableTest(final MotorControllerGroup group, final MotorController[] motorControllers) { + group.set(1.0); + group.disable(); + + assertArrayEquals( + DoubleStream.generate(() -> 0.0).limit(motorControllers.length).toArray(), + Arrays.stream(motorControllers).mapToDouble(MotorController::get).toArray(), + 0.00005); + } + + @ParameterizedTest + @MethodSource("motorControllerArguments") + void stopMotorTest(final MotorControllerGroup group, final MotorController[] motorControllers) { + group.set(1.0); + group.stopMotor(); + + assertArrayEquals( + DoubleStream.generate(() -> 0.0).limit(motorControllers.length).toArray(), + Arrays.stream(motorControllers).mapToDouble(MotorController::get).toArray(), + 0.00005); + } +} diff --git a/wpilibj/src/test/java/edu/wpi/first/wpilibj/simulation/ElevatorSimTest.java b/wpilibj/src/test/java/edu/wpi/first/wpilibj/simulation/ElevatorSimTest.java index 0b954415a5..13f57f71c2 100644 --- a/wpilibj/src/test/java/edu/wpi/first/wpilibj/simulation/ElevatorSimTest.java +++ b/wpilibj/src/test/java/edu/wpi/first/wpilibj/simulation/ElevatorSimTest.java @@ -8,9 +8,9 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import edu.wpi.first.wpilibj.Encoder; -import edu.wpi.first.wpilibj.PWMVictorSPX; import edu.wpi.first.wpilibj.RobotController; import edu.wpi.first.wpilibj.controller.PIDController; +import edu.wpi.first.wpilibj.motorcontrol.PWMVictorSPX; import edu.wpi.first.wpilibj.system.plant.DCMotor; import edu.wpi.first.wpilibj.system.plant.LinearSystemId; import edu.wpi.first.wpilibj.util.Units; diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/arcadedrive/Robot.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/arcadedrive/Robot.java index e7bd6372c5..e35243de58 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/arcadedrive/Robot.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/arcadedrive/Robot.java @@ -5,9 +5,9 @@ package edu.wpi.first.wpilibj.examples.arcadedrive; import edu.wpi.first.wpilibj.Joystick; -import edu.wpi.first.wpilibj.PWMSparkMax; import edu.wpi.first.wpilibj.TimedRobot; import edu.wpi.first.wpilibj.drive.DifferentialDrive; +import edu.wpi.first.wpilibj.motorcontrol.PWMSparkMax; /** * This is a demo program showing the use of the DifferentialDrive class. Runs the motors with diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/arcadedrivexboxcontroller/Robot.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/arcadedrivexboxcontroller/Robot.java index b382ac54ea..f78d49e25e 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/arcadedrivexboxcontroller/Robot.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/arcadedrivexboxcontroller/Robot.java @@ -5,10 +5,10 @@ package edu.wpi.first.wpilibj.examples.arcadedrivexboxcontroller; import edu.wpi.first.wpilibj.GenericHID.Hand; -import edu.wpi.first.wpilibj.PWMSparkMax; import edu.wpi.first.wpilibj.TimedRobot; import edu.wpi.first.wpilibj.XboxController; import edu.wpi.first.wpilibj.drive.DifferentialDrive; +import edu.wpi.first.wpilibj.motorcontrol.PWMSparkMax; /** * This is a demo program showing the use of the DifferentialDrive class. Runs the motors with split diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/armbot/subsystems/ArmSubsystem.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/armbot/subsystems/ArmSubsystem.java index a4459a5a20..23b89a8ce4 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/armbot/subsystems/ArmSubsystem.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/armbot/subsystems/ArmSubsystem.java @@ -5,10 +5,10 @@ package edu.wpi.first.wpilibj.examples.armbot.subsystems; import edu.wpi.first.wpilibj.Encoder; -import edu.wpi.first.wpilibj.PWMSparkMax; import edu.wpi.first.wpilibj.controller.ArmFeedforward; import edu.wpi.first.wpilibj.controller.ProfiledPIDController; import edu.wpi.first.wpilibj.examples.armbot.Constants.ArmConstants; +import edu.wpi.first.wpilibj.motorcontrol.PWMSparkMax; import edu.wpi.first.wpilibj.trajectory.TrapezoidProfile; import edu.wpi.first.wpilibj2.command.ProfiledPIDSubsystem; diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/armbot/subsystems/DriveSubsystem.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/armbot/subsystems/DriveSubsystem.java index 104b685b3f..9338f309f7 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/armbot/subsystems/DriveSubsystem.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/armbot/subsystems/DriveSubsystem.java @@ -5,22 +5,22 @@ package edu.wpi.first.wpilibj.examples.armbot.subsystems; import edu.wpi.first.wpilibj.Encoder; -import edu.wpi.first.wpilibj.PWMSparkMax; -import edu.wpi.first.wpilibj.SpeedControllerGroup; import edu.wpi.first.wpilibj.drive.DifferentialDrive; import edu.wpi.first.wpilibj.examples.armbot.Constants.DriveConstants; +import edu.wpi.first.wpilibj.motorcontrol.MotorControllerGroup; +import edu.wpi.first.wpilibj.motorcontrol.PWMSparkMax; import edu.wpi.first.wpilibj2.command.SubsystemBase; public class DriveSubsystem extends SubsystemBase { // The motors on the left side of the drive. - private final SpeedControllerGroup m_leftMotors = - new SpeedControllerGroup( + private final MotorControllerGroup m_leftMotors = + new MotorControllerGroup( new PWMSparkMax(DriveConstants.kLeftMotor1Port), new PWMSparkMax(DriveConstants.kLeftMotor2Port)); // The motors on the right side of the drive. - private final SpeedControllerGroup m_rightMotors = - new SpeedControllerGroup( + private final MotorControllerGroup m_rightMotors = + new MotorControllerGroup( new PWMSparkMax(DriveConstants.kRightMotor1Port), new PWMSparkMax(DriveConstants.kRightMotor2Port)); diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/armbotoffboard/ExampleSmartMotorController.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/armbotoffboard/ExampleSmartMotorController.java index fb2329c007..4f95b4003e 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/armbotoffboard/ExampleSmartMotorController.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/armbotoffboard/ExampleSmartMotorController.java @@ -4,14 +4,14 @@ package edu.wpi.first.wpilibj.examples.armbotoffboard; -import edu.wpi.first.wpilibj.SpeedController; +import edu.wpi.first.wpilibj.motorcontrol.MotorController; /** * A simplified stub class that simulates the API of a common "smart" motor controller. * *

    Has no actual functionality. */ -public class ExampleSmartMotorController implements SpeedController { +public class ExampleSmartMotorController implements MotorController { public enum PIDMode { kPosition, kVelocity, diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/armbotoffboard/subsystems/DriveSubsystem.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/armbotoffboard/subsystems/DriveSubsystem.java index 96d90d4a83..8017719044 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/armbotoffboard/subsystems/DriveSubsystem.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/armbotoffboard/subsystems/DriveSubsystem.java @@ -5,22 +5,22 @@ package edu.wpi.first.wpilibj.examples.armbotoffboard.subsystems; import edu.wpi.first.wpilibj.Encoder; -import edu.wpi.first.wpilibj.PWMSparkMax; -import edu.wpi.first.wpilibj.SpeedControllerGroup; import edu.wpi.first.wpilibj.drive.DifferentialDrive; import edu.wpi.first.wpilibj.examples.armbotoffboard.Constants.DriveConstants; +import edu.wpi.first.wpilibj.motorcontrol.MotorControllerGroup; +import edu.wpi.first.wpilibj.motorcontrol.PWMSparkMax; import edu.wpi.first.wpilibj2.command.SubsystemBase; public class DriveSubsystem extends SubsystemBase { // The motors on the left side of the drive. - private final SpeedControllerGroup m_leftMotors = - new SpeedControllerGroup( + private final MotorControllerGroup m_leftMotors = + new MotorControllerGroup( new PWMSparkMax(DriveConstants.kLeftMotor1Port), new PWMSparkMax(DriveConstants.kLeftMotor2Port)); // The motors on the right side of the drive. - private final SpeedControllerGroup m_rightMotors = - new SpeedControllerGroup( + private final MotorControllerGroup m_rightMotors = + new MotorControllerGroup( new PWMSparkMax(DriveConstants.kRightMotor1Port), new PWMSparkMax(DriveConstants.kRightMotor2Port)); diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/armsimulation/Robot.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/armsimulation/Robot.java index d407854300..56ede78abc 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/armsimulation/Robot.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/armsimulation/Robot.java @@ -6,10 +6,10 @@ package edu.wpi.first.wpilibj.examples.armsimulation; import edu.wpi.first.wpilibj.Encoder; import edu.wpi.first.wpilibj.Joystick; -import edu.wpi.first.wpilibj.PWMSparkMax; import edu.wpi.first.wpilibj.RobotController; import edu.wpi.first.wpilibj.TimedRobot; import edu.wpi.first.wpilibj.controller.PIDController; +import edu.wpi.first.wpilibj.motorcontrol.PWMSparkMax; import edu.wpi.first.wpilibj.simulation.BatterySim; import edu.wpi.first.wpilibj.simulation.EncoderSim; import edu.wpi.first.wpilibj.simulation.RoboRioSim; diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/differentialdrivebot/Drivetrain.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/differentialdrivebot/Drivetrain.java index 759c12924d..7f7e76b949 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/differentialdrivebot/Drivetrain.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/differentialdrivebot/Drivetrain.java @@ -6,15 +6,15 @@ package edu.wpi.first.wpilibj.examples.differentialdrivebot; import edu.wpi.first.wpilibj.AnalogGyro; import edu.wpi.first.wpilibj.Encoder; -import edu.wpi.first.wpilibj.PWMSparkMax; -import edu.wpi.first.wpilibj.SpeedController; -import edu.wpi.first.wpilibj.SpeedControllerGroup; import edu.wpi.first.wpilibj.controller.PIDController; import edu.wpi.first.wpilibj.controller.SimpleMotorFeedforward; import edu.wpi.first.wpilibj.kinematics.ChassisSpeeds; import edu.wpi.first.wpilibj.kinematics.DifferentialDriveKinematics; import edu.wpi.first.wpilibj.kinematics.DifferentialDriveOdometry; import edu.wpi.first.wpilibj.kinematics.DifferentialDriveWheelSpeeds; +import edu.wpi.first.wpilibj.motorcontrol.MotorController; +import edu.wpi.first.wpilibj.motorcontrol.MotorControllerGroup; +import edu.wpi.first.wpilibj.motorcontrol.PWMSparkMax; /** Represents a differential drive style drivetrain. */ public class Drivetrain { @@ -25,18 +25,18 @@ public class Drivetrain { private static final double kWheelRadius = 0.0508; // meters private static final int kEncoderResolution = 4096; - private final SpeedController m_leftLeader = new PWMSparkMax(1); - private final SpeedController m_leftFollower = new PWMSparkMax(2); - private final SpeedController m_rightLeader = new PWMSparkMax(3); - private final SpeedController m_rightFollower = new PWMSparkMax(4); + private final MotorController m_leftLeader = new PWMSparkMax(1); + private final MotorController m_leftFollower = new PWMSparkMax(2); + private final MotorController m_rightLeader = new PWMSparkMax(3); + private final MotorController m_rightFollower = new PWMSparkMax(4); private final Encoder m_leftEncoder = new Encoder(0, 1); private final Encoder m_rightEncoder = new Encoder(2, 3); - private final SpeedControllerGroup m_leftGroup = - new SpeedControllerGroup(m_leftLeader, m_leftFollower); - private final SpeedControllerGroup m_rightGroup = - new SpeedControllerGroup(m_rightLeader, m_rightFollower); + private final MotorControllerGroup m_leftGroup = + new MotorControllerGroup(m_leftLeader, m_leftFollower); + private final MotorControllerGroup m_rightGroup = + new MotorControllerGroup(m_rightLeader, m_rightFollower); private final AnalogGyro m_gyro = new AnalogGyro(0); diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/differentialdriveposeestimator/Drivetrain.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/differentialdriveposeestimator/Drivetrain.java index 947abc4721..74daf11e80 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/differentialdriveposeestimator/Drivetrain.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/differentialdriveposeestimator/Drivetrain.java @@ -6,9 +6,6 @@ package edu.wpi.first.wpilibj.examples.differentialdriveposeestimator; import edu.wpi.first.wpilibj.AnalogGyro; import edu.wpi.first.wpilibj.Encoder; -import edu.wpi.first.wpilibj.PWMSparkMax; -import edu.wpi.first.wpilibj.SpeedController; -import edu.wpi.first.wpilibj.SpeedControllerGroup; import edu.wpi.first.wpilibj.Timer; import edu.wpi.first.wpilibj.controller.PIDController; import edu.wpi.first.wpilibj.controller.SimpleMotorFeedforward; @@ -18,6 +15,9 @@ import edu.wpi.first.wpilibj.geometry.Pose2d; import edu.wpi.first.wpilibj.kinematics.ChassisSpeeds; import edu.wpi.first.wpilibj.kinematics.DifferentialDriveKinematics; import edu.wpi.first.wpilibj.kinematics.DifferentialDriveWheelSpeeds; +import edu.wpi.first.wpilibj.motorcontrol.MotorController; +import edu.wpi.first.wpilibj.motorcontrol.MotorControllerGroup; +import edu.wpi.first.wpilibj.motorcontrol.PWMSparkMax; import edu.wpi.first.wpilibj.util.Units; import edu.wpi.first.wpiutil.math.VecBuilder; @@ -30,18 +30,18 @@ public class Drivetrain { private static final double kWheelRadius = 0.0508; // meters private static final int kEncoderResolution = 4096; - private final SpeedController m_leftLeader = new PWMSparkMax(1); - private final SpeedController m_leftFollower = new PWMSparkMax(2); - private final SpeedController m_rightLeader = new PWMSparkMax(3); - private final SpeedController m_rightFollower = new PWMSparkMax(4); + private final MotorController m_leftLeader = new PWMSparkMax(1); + private final MotorController m_leftFollower = new PWMSparkMax(2); + private final MotorController m_rightLeader = new PWMSparkMax(3); + private final MotorController m_rightFollower = new PWMSparkMax(4); private final Encoder m_leftEncoder = new Encoder(0, 1); private final Encoder m_rightEncoder = new Encoder(2, 3); - private final SpeedControllerGroup m_leftGroup = - new SpeedControllerGroup(m_leftLeader, m_leftFollower); - private final SpeedControllerGroup m_rightGroup = - new SpeedControllerGroup(m_rightLeader, m_rightFollower); + private final MotorControllerGroup m_leftGroup = + new MotorControllerGroup(m_leftLeader, m_leftFollower); + private final MotorControllerGroup m_rightGroup = + new MotorControllerGroup(m_rightLeader, m_rightFollower); private final AnalogGyro m_gyro = new AnalogGyro(0); diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/drivedistanceoffboard/ExampleSmartMotorController.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/drivedistanceoffboard/ExampleSmartMotorController.java index 615ed18bcd..487974cadb 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/drivedistanceoffboard/ExampleSmartMotorController.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/drivedistanceoffboard/ExampleSmartMotorController.java @@ -4,14 +4,14 @@ package edu.wpi.first.wpilibj.examples.drivedistanceoffboard; -import edu.wpi.first.wpilibj.SpeedController; +import edu.wpi.first.wpilibj.motorcontrol.MotorController; /** * A simplified stub class that simulates the API of a common "smart" motor controller. * *

    Has no actual functionality. */ -public class ExampleSmartMotorController implements SpeedController { +public class ExampleSmartMotorController implements MotorController { public enum PIDMode { kPosition, kVelocity, diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/elevatorprofiledpid/Robot.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/elevatorprofiledpid/Robot.java index 06bb3cff5c..13d59bad11 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/elevatorprofiledpid/Robot.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/elevatorprofiledpid/Robot.java @@ -6,10 +6,10 @@ package edu.wpi.first.wpilibj.examples.elevatorprofiledpid; import edu.wpi.first.wpilibj.Encoder; import edu.wpi.first.wpilibj.Joystick; -import edu.wpi.first.wpilibj.PWMSparkMax; -import edu.wpi.first.wpilibj.SpeedController; import edu.wpi.first.wpilibj.TimedRobot; import edu.wpi.first.wpilibj.controller.ProfiledPIDController; +import edu.wpi.first.wpilibj.motorcontrol.MotorController; +import edu.wpi.first.wpilibj.motorcontrol.PWMSparkMax; import edu.wpi.first.wpilibj.trajectory.TrapezoidProfile; public class Robot extends TimedRobot { @@ -17,7 +17,7 @@ public class Robot extends TimedRobot { private final Joystick m_joystick = new Joystick(1); private final Encoder m_encoder = new Encoder(1, 2); - private final SpeedController m_motor = new PWMSparkMax(1); + private final MotorController m_motor = new PWMSparkMax(1); // Create a PID controller whose setpoint's change is subject to maximum // velocity and acceleration constraints. diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/elevatorsimulation/Robot.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/elevatorsimulation/Robot.java index 5cab4a16c7..ccae2cb4d7 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/elevatorsimulation/Robot.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/elevatorsimulation/Robot.java @@ -6,10 +6,10 @@ package edu.wpi.first.wpilibj.examples.elevatorsimulation; import edu.wpi.first.wpilibj.Encoder; import edu.wpi.first.wpilibj.Joystick; -import edu.wpi.first.wpilibj.PWMSparkMax; import edu.wpi.first.wpilibj.RobotController; import edu.wpi.first.wpilibj.TimedRobot; import edu.wpi.first.wpilibj.controller.PIDController; +import edu.wpi.first.wpilibj.motorcontrol.PWMSparkMax; import edu.wpi.first.wpilibj.simulation.BatterySim; import edu.wpi.first.wpilibj.simulation.ElevatorSim; import edu.wpi.first.wpilibj.simulation.EncoderSim; diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/elevatortrapezoidprofile/ExampleSmartMotorController.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/elevatortrapezoidprofile/ExampleSmartMotorController.java index d2ea6eea42..a1366e6f35 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/elevatortrapezoidprofile/ExampleSmartMotorController.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/elevatortrapezoidprofile/ExampleSmartMotorController.java @@ -4,14 +4,14 @@ package edu.wpi.first.wpilibj.examples.elevatortrapezoidprofile; -import edu.wpi.first.wpilibj.SpeedController; +import edu.wpi.first.wpilibj.motorcontrol.MotorController; /** * A simplified stub class that simulates the API of a common "smart" motor controller. * *

    Has no actual functionality. */ -public class ExampleSmartMotorController implements SpeedController { +public class ExampleSmartMotorController implements MotorController { public enum PIDMode { kPosition, kVelocity, diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/frisbeebot/subsystems/DriveSubsystem.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/frisbeebot/subsystems/DriveSubsystem.java index 2fd62737a2..b1c580a9c5 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/frisbeebot/subsystems/DriveSubsystem.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/frisbeebot/subsystems/DriveSubsystem.java @@ -5,22 +5,22 @@ package edu.wpi.first.wpilibj.examples.frisbeebot.subsystems; import edu.wpi.first.wpilibj.Encoder; -import edu.wpi.first.wpilibj.PWMSparkMax; -import edu.wpi.first.wpilibj.SpeedControllerGroup; import edu.wpi.first.wpilibj.drive.DifferentialDrive; import edu.wpi.first.wpilibj.examples.frisbeebot.Constants.DriveConstants; +import edu.wpi.first.wpilibj.motorcontrol.MotorControllerGroup; +import edu.wpi.first.wpilibj.motorcontrol.PWMSparkMax; import edu.wpi.first.wpilibj2.command.SubsystemBase; public class DriveSubsystem extends SubsystemBase { // The motors on the left side of the drive. - private final SpeedControllerGroup m_leftMotors = - new SpeedControllerGroup( + private final MotorControllerGroup m_leftMotors = + new MotorControllerGroup( new PWMSparkMax(DriveConstants.kLeftMotor1Port), new PWMSparkMax(DriveConstants.kLeftMotor2Port)); // The motors on the right side of the drive. - private final SpeedControllerGroup m_rightMotors = - new SpeedControllerGroup( + private final MotorControllerGroup m_rightMotors = + new MotorControllerGroup( new PWMSparkMax(DriveConstants.kRightMotor1Port), new PWMSparkMax(DriveConstants.kRightMotor2Port)); diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/frisbeebot/subsystems/ShooterSubsystem.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/frisbeebot/subsystems/ShooterSubsystem.java index d5f4844527..e6340af507 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/frisbeebot/subsystems/ShooterSubsystem.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/frisbeebot/subsystems/ShooterSubsystem.java @@ -5,10 +5,10 @@ package edu.wpi.first.wpilibj.examples.frisbeebot.subsystems; import edu.wpi.first.wpilibj.Encoder; -import edu.wpi.first.wpilibj.PWMSparkMax; import edu.wpi.first.wpilibj.controller.PIDController; import edu.wpi.first.wpilibj.controller.SimpleMotorFeedforward; import edu.wpi.first.wpilibj.examples.frisbeebot.Constants.ShooterConstants; +import edu.wpi.first.wpilibj.motorcontrol.PWMSparkMax; import edu.wpi.first.wpilibj2.command.PIDSubsystem; public class ShooterSubsystem extends PIDSubsystem { diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/gearsbot/subsystems/Claw.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/gearsbot/subsystems/Claw.java index b25c7ea428..d34d12a26e 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/gearsbot/subsystems/Claw.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/gearsbot/subsystems/Claw.java @@ -5,7 +5,7 @@ package edu.wpi.first.wpilibj.examples.gearsbot.subsystems; import edu.wpi.first.wpilibj.DigitalInput; -import edu.wpi.first.wpilibj.Victor; +import edu.wpi.first.wpilibj.motorcontrol.Victor; import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard; import edu.wpi.first.wpilibj2.command.SubsystemBase; diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/gearsbot/subsystems/DriveTrain.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/gearsbot/subsystems/DriveTrain.java index fdbf1a51e4..b6e223c553 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/gearsbot/subsystems/DriveTrain.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/gearsbot/subsystems/DriveTrain.java @@ -7,11 +7,11 @@ package edu.wpi.first.wpilibj.examples.gearsbot.subsystems; import edu.wpi.first.wpilibj.AnalogGyro; import edu.wpi.first.wpilibj.AnalogInput; import edu.wpi.first.wpilibj.Encoder; -import edu.wpi.first.wpilibj.PWMSparkMax; -import edu.wpi.first.wpilibj.SpeedController; -import edu.wpi.first.wpilibj.SpeedControllerGroup; import edu.wpi.first.wpilibj.drive.DifferentialDrive; import edu.wpi.first.wpilibj.examples.gearsbot.Robot; +import edu.wpi.first.wpilibj.motorcontrol.MotorController; +import edu.wpi.first.wpilibj.motorcontrol.MotorControllerGroup; +import edu.wpi.first.wpilibj.motorcontrol.PWMSparkMax; import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard; import edu.wpi.first.wpilibj2.command.SubsystemBase; @@ -20,11 +20,11 @@ public class DriveTrain extends SubsystemBase { * The DriveTrain subsystem incorporates the sensors and actuators attached to the robots chassis. * These include four drive motors, a left and right encoder and a gyro. */ - private final SpeedController m_leftMotor = - new SpeedControllerGroup(new PWMSparkMax(0), new PWMSparkMax(1)); + private final MotorController m_leftMotor = + new MotorControllerGroup(new PWMSparkMax(0), new PWMSparkMax(1)); - private final SpeedController m_rightMotor = - new SpeedControllerGroup(new PWMSparkMax(2), new PWMSparkMax(3)); + private final MotorController m_rightMotor = + new MotorControllerGroup(new PWMSparkMax(2), new PWMSparkMax(3)); private final DifferentialDrive m_drive = new DifferentialDrive(m_leftMotor, m_rightMotor); diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/gearsbot/subsystems/Elevator.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/gearsbot/subsystems/Elevator.java index fc38bf8717..ceb809f550 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/gearsbot/subsystems/Elevator.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/gearsbot/subsystems/Elevator.java @@ -5,9 +5,9 @@ package edu.wpi.first.wpilibj.examples.gearsbot.subsystems; import edu.wpi.first.wpilibj.AnalogPotentiometer; -import edu.wpi.first.wpilibj.Victor; import edu.wpi.first.wpilibj.controller.PIDController; import edu.wpi.first.wpilibj.examples.gearsbot.Robot; +import edu.wpi.first.wpilibj.motorcontrol.Victor; import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard; import edu.wpi.first.wpilibj2.command.PIDSubsystem; diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/gearsbot/subsystems/Wrist.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/gearsbot/subsystems/Wrist.java index 7421433be3..661e3944fd 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/gearsbot/subsystems/Wrist.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/gearsbot/subsystems/Wrist.java @@ -5,9 +5,9 @@ package edu.wpi.first.wpilibj.examples.gearsbot.subsystems; import edu.wpi.first.wpilibj.AnalogPotentiometer; -import edu.wpi.first.wpilibj.Victor; import edu.wpi.first.wpilibj.controller.PIDController; import edu.wpi.first.wpilibj.examples.gearsbot.Robot; +import edu.wpi.first.wpilibj.motorcontrol.Victor; import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard; import edu.wpi.first.wpilibj2.command.PIDSubsystem; diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/gettingstarted/Robot.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/gettingstarted/Robot.java index ee0678cb7b..0a9a2b034d 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/gettingstarted/Robot.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/gettingstarted/Robot.java @@ -5,10 +5,10 @@ package edu.wpi.first.wpilibj.examples.gettingstarted; import edu.wpi.first.wpilibj.Joystick; -import edu.wpi.first.wpilibj.PWMSparkMax; import edu.wpi.first.wpilibj.TimedRobot; import edu.wpi.first.wpilibj.Timer; import edu.wpi.first.wpilibj.drive.DifferentialDrive; +import edu.wpi.first.wpilibj.motorcontrol.PWMSparkMax; /** * The VM is configured to automatically run this class, and to call the functions corresponding to diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/gyro/Robot.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/gyro/Robot.java index 9bdb61b227..3d8353bd90 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/gyro/Robot.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/gyro/Robot.java @@ -6,9 +6,9 @@ package edu.wpi.first.wpilibj.examples.gyro; import edu.wpi.first.wpilibj.AnalogGyro; import edu.wpi.first.wpilibj.Joystick; -import edu.wpi.first.wpilibj.PWMSparkMax; import edu.wpi.first.wpilibj.TimedRobot; import edu.wpi.first.wpilibj.drive.DifferentialDrive; +import edu.wpi.first.wpilibj.motorcontrol.PWMSparkMax; /** * This is a sample program to demonstrate how to use a gyro sensor to make a robot drive straight. diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/gyrodrivecommands/subsystems/DriveSubsystem.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/gyrodrivecommands/subsystems/DriveSubsystem.java index 439b503673..c16034cd00 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/gyrodrivecommands/subsystems/DriveSubsystem.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/gyrodrivecommands/subsystems/DriveSubsystem.java @@ -6,23 +6,23 @@ package edu.wpi.first.wpilibj.examples.gyrodrivecommands.subsystems; import edu.wpi.first.wpilibj.ADXRS450_Gyro; import edu.wpi.first.wpilibj.Encoder; -import edu.wpi.first.wpilibj.PWMSparkMax; -import edu.wpi.first.wpilibj.SpeedControllerGroup; import edu.wpi.first.wpilibj.drive.DifferentialDrive; import edu.wpi.first.wpilibj.examples.gyrodrivecommands.Constants.DriveConstants; import edu.wpi.first.wpilibj.interfaces.Gyro; +import edu.wpi.first.wpilibj.motorcontrol.MotorControllerGroup; +import edu.wpi.first.wpilibj.motorcontrol.PWMSparkMax; import edu.wpi.first.wpilibj2.command.SubsystemBase; public class DriveSubsystem extends SubsystemBase { // The motors on the left side of the drive. - private final SpeedControllerGroup m_leftMotors = - new SpeedControllerGroup( + private final MotorControllerGroup m_leftMotors = + new MotorControllerGroup( new PWMSparkMax(DriveConstants.kLeftMotor1Port), new PWMSparkMax(DriveConstants.kLeftMotor2Port)); // The motors on the right side of the drive. - private final SpeedControllerGroup m_rightMotors = - new SpeedControllerGroup( + private final MotorControllerGroup m_rightMotors = + new MotorControllerGroup( new PWMSparkMax(DriveConstants.kRightMotor1Port), new PWMSparkMax(DriveConstants.kRightMotor2Port)); diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/gyromecanum/Robot.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/gyromecanum/Robot.java index b607c916ae..f4b9a97e8c 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/gyromecanum/Robot.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/gyromecanum/Robot.java @@ -6,9 +6,9 @@ package edu.wpi.first.wpilibj.examples.gyromecanum; import edu.wpi.first.wpilibj.AnalogGyro; import edu.wpi.first.wpilibj.Joystick; -import edu.wpi.first.wpilibj.PWMSparkMax; import edu.wpi.first.wpilibj.TimedRobot; import edu.wpi.first.wpilibj.drive.MecanumDrive; +import edu.wpi.first.wpilibj.motorcontrol.PWMSparkMax; /** * This is a sample program that uses mecanum drive with a gyro sensor to maintain rotation diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/hatchbotinlined/subsystems/DriveSubsystem.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/hatchbotinlined/subsystems/DriveSubsystem.java index faa61f4e41..87767d5227 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/hatchbotinlined/subsystems/DriveSubsystem.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/hatchbotinlined/subsystems/DriveSubsystem.java @@ -5,22 +5,22 @@ package edu.wpi.first.wpilibj.examples.hatchbotinlined.subsystems; import edu.wpi.first.wpilibj.Encoder; -import edu.wpi.first.wpilibj.PWMSparkMax; -import edu.wpi.first.wpilibj.SpeedControllerGroup; import edu.wpi.first.wpilibj.drive.DifferentialDrive; import edu.wpi.first.wpilibj.examples.hatchbotinlined.Constants.DriveConstants; +import edu.wpi.first.wpilibj.motorcontrol.MotorControllerGroup; +import edu.wpi.first.wpilibj.motorcontrol.PWMSparkMax; import edu.wpi.first.wpilibj2.command.SubsystemBase; public class DriveSubsystem extends SubsystemBase { // The motors on the left side of the drive. - private final SpeedControllerGroup m_leftMotors = - new SpeedControllerGroup( + private final MotorControllerGroup m_leftMotors = + new MotorControllerGroup( new PWMSparkMax(DriveConstants.kLeftMotor1Port), new PWMSparkMax(DriveConstants.kLeftMotor2Port)); // The motors on the right side of the drive. - private final SpeedControllerGroup m_rightMotors = - new SpeedControllerGroup( + private final MotorControllerGroup m_rightMotors = + new MotorControllerGroup( new PWMSparkMax(DriveConstants.kRightMotor1Port), new PWMSparkMax(DriveConstants.kRightMotor2Port)); diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/hatchbottraditional/subsystems/DriveSubsystem.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/hatchbottraditional/subsystems/DriveSubsystem.java index acf3189c1a..b88e9005f8 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/hatchbottraditional/subsystems/DriveSubsystem.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/hatchbottraditional/subsystems/DriveSubsystem.java @@ -5,22 +5,22 @@ package edu.wpi.first.wpilibj.examples.hatchbottraditional.subsystems; import edu.wpi.first.wpilibj.Encoder; -import edu.wpi.first.wpilibj.PWMSparkMax; -import edu.wpi.first.wpilibj.SpeedControllerGroup; import edu.wpi.first.wpilibj.drive.DifferentialDrive; import edu.wpi.first.wpilibj.examples.hatchbottraditional.Constants.DriveConstants; +import edu.wpi.first.wpilibj.motorcontrol.MotorControllerGroup; +import edu.wpi.first.wpilibj.motorcontrol.PWMSparkMax; import edu.wpi.first.wpilibj2.command.SubsystemBase; public class DriveSubsystem extends SubsystemBase { // The motors on the left side of the drive. - private final SpeedControllerGroup m_leftMotors = - new SpeedControllerGroup( + private final MotorControllerGroup m_leftMotors = + new MotorControllerGroup( new PWMSparkMax(DriveConstants.kLeftMotor1Port), new PWMSparkMax(DriveConstants.kLeftMotor2Port)); // The motors on the right side of the drive. - private final SpeedControllerGroup m_rightMotors = - new SpeedControllerGroup( + private final MotorControllerGroup m_rightMotors = + new MotorControllerGroup( new PWMSparkMax(DriveConstants.kRightMotor1Port), new PWMSparkMax(DriveConstants.kRightMotor2Port)); diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/mecanumbot/Drivetrain.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/mecanumbot/Drivetrain.java index ab95a7aab0..c50a2b247e 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/mecanumbot/Drivetrain.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/mecanumbot/Drivetrain.java @@ -6,8 +6,6 @@ package edu.wpi.first.wpilibj.examples.mecanumbot; import edu.wpi.first.wpilibj.AnalogGyro; import edu.wpi.first.wpilibj.Encoder; -import edu.wpi.first.wpilibj.PWMSparkMax; -import edu.wpi.first.wpilibj.SpeedController; import edu.wpi.first.wpilibj.controller.PIDController; import edu.wpi.first.wpilibj.controller.SimpleMotorFeedforward; import edu.wpi.first.wpilibj.geometry.Translation2d; @@ -15,6 +13,8 @@ import edu.wpi.first.wpilibj.kinematics.ChassisSpeeds; import edu.wpi.first.wpilibj.kinematics.MecanumDriveKinematics; import edu.wpi.first.wpilibj.kinematics.MecanumDriveOdometry; import edu.wpi.first.wpilibj.kinematics.MecanumDriveWheelSpeeds; +import edu.wpi.first.wpilibj.motorcontrol.MotorController; +import edu.wpi.first.wpilibj.motorcontrol.PWMSparkMax; /** Represents a mecanum drive style drivetrain. */ @SuppressWarnings("PMD.TooManyFields") @@ -22,10 +22,10 @@ public class Drivetrain { public static final double kMaxSpeed = 3.0; // 3 meters per second public static final double kMaxAngularSpeed = Math.PI; // 1/2 rotation per second - private final SpeedController m_frontLeftMotor = new PWMSparkMax(1); - private final SpeedController m_frontRightMotor = new PWMSparkMax(2); - private final SpeedController m_backLeftMotor = new PWMSparkMax(3); - private final SpeedController m_backRightMotor = new PWMSparkMax(4); + private final MotorController m_frontLeftMotor = new PWMSparkMax(1); + private final MotorController m_frontRightMotor = new PWMSparkMax(2); + private final MotorController m_backLeftMotor = new PWMSparkMax(3); + private final MotorController m_backRightMotor = new PWMSparkMax(4); private final Encoder m_frontLeftEncoder = new Encoder(0, 1); private final Encoder m_frontRightEncoder = new Encoder(2, 3); diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/mecanumcontrollercommand/RobotContainer.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/mecanumcontrollercommand/RobotContainer.java index f66a7c98ab..c209346a74 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/mecanumcontrollercommand/RobotContainer.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/mecanumcontrollercommand/RobotContainer.java @@ -118,7 +118,7 @@ public class RobotContainer { new PIDController(DriveConstants.kPFrontRightVel, 0, 0), new PIDController(DriveConstants.kPRearRightVel, 0, 0), m_robotDrive::getCurrentWheelSpeeds, - m_robotDrive::setDriveSpeedControllersVolts, // Consumer for the output motor voltages + m_robotDrive::setDriveMotorControllersVolts, // Consumer for the output motor voltages m_robotDrive); // Reset odometry to the starting pose of the trajectory. diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/mecanumcontrollercommand/subsystems/DriveSubsystem.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/mecanumcontrollercommand/subsystems/DriveSubsystem.java index 8ee318bc6b..bb4913476c 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/mecanumcontrollercommand/subsystems/DriveSubsystem.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/mecanumcontrollercommand/subsystems/DriveSubsystem.java @@ -6,7 +6,6 @@ package edu.wpi.first.wpilibj.examples.mecanumcontrollercommand.subsystems; import edu.wpi.first.wpilibj.ADXRS450_Gyro; import edu.wpi.first.wpilibj.Encoder; -import edu.wpi.first.wpilibj.PWMSparkMax; import edu.wpi.first.wpilibj.drive.MecanumDrive; import edu.wpi.first.wpilibj.examples.mecanumcontrollercommand.Constants.DriveConstants; import edu.wpi.first.wpilibj.geometry.Pose2d; @@ -14,6 +13,7 @@ import edu.wpi.first.wpilibj.interfaces.Gyro; import edu.wpi.first.wpilibj.kinematics.MecanumDriveMotorVoltages; import edu.wpi.first.wpilibj.kinematics.MecanumDriveOdometry; import edu.wpi.first.wpilibj.kinematics.MecanumDriveWheelSpeeds; +import edu.wpi.first.wpilibj.motorcontrol.PWMSparkMax; import edu.wpi.first.wpilibj2.command.SubsystemBase; public class DriveSubsystem extends SubsystemBase { @@ -117,8 +117,8 @@ public class DriveSubsystem extends SubsystemBase { } } - /** Sets the front left drive SpeedController to a voltage. */ - public void setDriveSpeedControllersVolts(MecanumDriveMotorVoltages volts) { + /** Sets the front left drive MotorController to a voltage. */ + public void setDriveMotorControllersVolts(MecanumDriveMotorVoltages volts) { m_frontLeft.setVoltage(volts.frontLeftVoltage); m_rearLeft.setVoltage(volts.rearLeftVoltage); m_frontRight.setVoltage(volts.frontRightVoltage); diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/mecanumdrive/Robot.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/mecanumdrive/Robot.java index 5b2ef43398..80c16b52fb 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/mecanumdrive/Robot.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/mecanumdrive/Robot.java @@ -5,9 +5,9 @@ package edu.wpi.first.wpilibj.examples.mecanumdrive; import edu.wpi.first.wpilibj.Joystick; -import edu.wpi.first.wpilibj.PWMSparkMax; import edu.wpi.first.wpilibj.TimedRobot; import edu.wpi.first.wpilibj.drive.MecanumDrive; +import edu.wpi.first.wpilibj.motorcontrol.PWMSparkMax; /** This is a demo program showing how to use Mecanum control with the MecanumDrive class. */ public class Robot extends TimedRobot { diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/mecanumdriveposeestimator/Drivetrain.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/mecanumdriveposeestimator/Drivetrain.java index bd1a3fe8d9..3fba18a6bc 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/mecanumdriveposeestimator/Drivetrain.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/mecanumdriveposeestimator/Drivetrain.java @@ -6,8 +6,6 @@ package edu.wpi.first.wpilibj.examples.mecanumdriveposeestimator; import edu.wpi.first.wpilibj.AnalogGyro; import edu.wpi.first.wpilibj.Encoder; -import edu.wpi.first.wpilibj.PWMSparkMax; -import edu.wpi.first.wpilibj.SpeedController; import edu.wpi.first.wpilibj.Timer; import edu.wpi.first.wpilibj.controller.PIDController; import edu.wpi.first.wpilibj.controller.SimpleMotorFeedforward; @@ -18,6 +16,8 @@ import edu.wpi.first.wpilibj.geometry.Translation2d; import edu.wpi.first.wpilibj.kinematics.ChassisSpeeds; import edu.wpi.first.wpilibj.kinematics.MecanumDriveKinematics; import edu.wpi.first.wpilibj.kinematics.MecanumDriveWheelSpeeds; +import edu.wpi.first.wpilibj.motorcontrol.MotorController; +import edu.wpi.first.wpilibj.motorcontrol.PWMSparkMax; import edu.wpi.first.wpilibj.util.Units; import edu.wpi.first.wpiutil.math.VecBuilder; @@ -27,10 +27,10 @@ public class Drivetrain { public static final double kMaxSpeed = 3.0; // 3 meters per second public static final double kMaxAngularSpeed = Math.PI; // 1/2 rotation per second - private final SpeedController m_frontLeftMotor = new PWMSparkMax(1); - private final SpeedController m_frontRightMotor = new PWMSparkMax(2); - private final SpeedController m_backLeftMotor = new PWMSparkMax(3); - private final SpeedController m_backRightMotor = new PWMSparkMax(4); + private final MotorController m_frontLeftMotor = new PWMSparkMax(1); + private final MotorController m_frontRightMotor = new PWMSparkMax(2); + private final MotorController m_backLeftMotor = new PWMSparkMax(3); + private final MotorController m_backRightMotor = new PWMSparkMax(4); private final Encoder m_frontLeftEncoder = new Encoder(0, 1); private final Encoder m_frontRightEncoder = new Encoder(2, 3); diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/motorcontrol/Robot.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/motorcontrol/Robot.java index e9105d8bd5..9976381baf 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/motorcontrol/Robot.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/motorcontrol/Robot.java @@ -5,9 +5,9 @@ package edu.wpi.first.wpilibj.examples.motorcontrol; import edu.wpi.first.wpilibj.Joystick; -import edu.wpi.first.wpilibj.PWMSparkMax; -import edu.wpi.first.wpilibj.SpeedController; import edu.wpi.first.wpilibj.TimedRobot; +import edu.wpi.first.wpilibj.motorcontrol.MotorController; +import edu.wpi.first.wpilibj.motorcontrol.PWMSparkMax; /** * This sample program shows how to control a motor using a joystick. In the operator control part @@ -20,7 +20,7 @@ public class Robot extends TimedRobot { private static final int kMotorPort = 0; private static final int kJoystickPort = 0; - private SpeedController m_motor; + private MotorController m_motor; private Joystick m_joystick; @Override diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/motorcontrolencoder/Robot.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/motorcontrolencoder/Robot.java index 1b0b617d0e..9b68965821 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/motorcontrolencoder/Robot.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/motorcontrolencoder/Robot.java @@ -6,9 +6,9 @@ package edu.wpi.first.wpilibj.examples.motorcontrolencoder; import edu.wpi.first.wpilibj.Encoder; import edu.wpi.first.wpilibj.Joystick; -import edu.wpi.first.wpilibj.PWMSparkMax; -import edu.wpi.first.wpilibj.SpeedController; import edu.wpi.first.wpilibj.TimedRobot; +import edu.wpi.first.wpilibj.motorcontrol.MotorController; +import edu.wpi.first.wpilibj.motorcontrol.PWMSparkMax; import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard; /** @@ -27,7 +27,7 @@ public class Robot extends TimedRobot { private static final int kEncoderPortA = 0; private static final int kEncoderPortB = 1; - private SpeedController m_motor; + private MotorController m_motor; private Joystick m_joystick; private Encoder m_encoder; diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/pacgoat/subsystems/Collector.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/pacgoat/subsystems/Collector.java index 5dd83e6373..8123b06a74 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/pacgoat/subsystems/Collector.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/pacgoat/subsystems/Collector.java @@ -6,9 +6,9 @@ package edu.wpi.first.wpilibj.examples.pacgoat.subsystems; import edu.wpi.first.wpilibj.DigitalInput; import edu.wpi.first.wpilibj.Solenoid; -import edu.wpi.first.wpilibj.SpeedController; -import edu.wpi.first.wpilibj.Victor; import edu.wpi.first.wpilibj.command.Subsystem; +import edu.wpi.first.wpilibj.motorcontrol.MotorController; +import edu.wpi.first.wpilibj.motorcontrol.Victor; /** * The Collector subsystem has one motor for the rollers, a limit switch for ball detection, a @@ -21,7 +21,7 @@ public class Collector extends Subsystem { public static final double kReverse = -1; // Subsystem devices - private final SpeedController m_rollerMotor = new Victor(6); + private final MotorController m_rollerMotor = new Victor(6); private final DigitalInput m_ballDetector = new DigitalInput(10); private final DigitalInput m_openDetector = new DigitalInput(6); private final Solenoid m_piston = new Solenoid(1, 1); diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/pacgoat/subsystems/DriveTrain.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/pacgoat/subsystems/DriveTrain.java index 2ce6a7cf41..ce671bc8ed 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/pacgoat/subsystems/DriveTrain.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/pacgoat/subsystems/DriveTrain.java @@ -8,13 +8,13 @@ import edu.wpi.first.wpilibj.AnalogGyro; import edu.wpi.first.wpilibj.CounterBase.EncodingType; import edu.wpi.first.wpilibj.Encoder; import edu.wpi.first.wpilibj.Joystick; -import edu.wpi.first.wpilibj.SpeedController; -import edu.wpi.first.wpilibj.SpeedControllerGroup; -import edu.wpi.first.wpilibj.Victor; import edu.wpi.first.wpilibj.command.Subsystem; import edu.wpi.first.wpilibj.drive.DifferentialDrive; import edu.wpi.first.wpilibj.examples.pacgoat.Robot; import edu.wpi.first.wpilibj.examples.pacgoat.commands.DriveWithJoystick; +import edu.wpi.first.wpilibj.motorcontrol.MotorController; +import edu.wpi.first.wpilibj.motorcontrol.MotorControllerGroup; +import edu.wpi.first.wpilibj.motorcontrol.Victor; /** * The DriveTrain subsystem controls the robot's chassis and reads in information about it's speed @@ -22,14 +22,14 @@ import edu.wpi.first.wpilibj.examples.pacgoat.commands.DriveWithJoystick; */ public class DriveTrain extends Subsystem { // Subsystem devices - private final SpeedController m_frontLeftCIM = new Victor(1); - private final SpeedController m_frontRightCIM = new Victor(2); - private final SpeedController m_rearLeftCIM = new Victor(3); - private final SpeedController m_rearRightCIM = new Victor(4); - private final SpeedControllerGroup m_leftCIMs = - new SpeedControllerGroup(m_frontLeftCIM, m_rearLeftCIM); - private final SpeedControllerGroup m_rightCIMs = - new SpeedControllerGroup(m_frontRightCIM, m_rearRightCIM); + private final MotorController m_frontLeftCIM = new Victor(1); + private final MotorController m_frontRightCIM = new Victor(2); + private final MotorController m_rearLeftCIM = new Victor(3); + private final MotorController m_rearRightCIM = new Victor(4); + private final MotorControllerGroup m_leftCIMs = + new MotorControllerGroup(m_frontLeftCIM, m_rearLeftCIM); + private final MotorControllerGroup m_rightCIMs = + new MotorControllerGroup(m_frontRightCIM, m_rearRightCIM); private final DifferentialDrive m_drive; private final Encoder m_rightEncoder = new Encoder(1, 2, true, EncodingType.k4X); private final Encoder m_leftEncoder = new Encoder(3, 4, false, EncodingType.k4X); diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/pacgoat/subsystems/Pivot.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/pacgoat/subsystems/Pivot.java index 620476fe97..1e65c64068 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/pacgoat/subsystems/Pivot.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/pacgoat/subsystems/Pivot.java @@ -6,10 +6,10 @@ package edu.wpi.first.wpilibj.examples.pacgoat.subsystems; import edu.wpi.first.wpilibj.AnalogPotentiometer; import edu.wpi.first.wpilibj.DigitalInput; -import edu.wpi.first.wpilibj.SpeedController; -import edu.wpi.first.wpilibj.Victor; import edu.wpi.first.wpilibj.command.PIDSubsystem; import edu.wpi.first.wpilibj.examples.pacgoat.Robot; +import edu.wpi.first.wpilibj.motorcontrol.MotorController; +import edu.wpi.first.wpilibj.motorcontrol.Victor; /** * The Pivot subsystem contains the Van-door motor and the pot for PID control of angle of the pivot @@ -31,7 +31,7 @@ public class Pivot extends PIDSubsystem { private final AnalogPotentiometer m_pot = new AnalogPotentiometer(1); // Motor to move the pivot. - private final SpeedController m_motor = new Victor(5); + private final MotorController m_motor = new Victor(5); /** Create a new pivot subsystem. */ public Pivot() { diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/potentiometerpid/Robot.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/potentiometerpid/Robot.java index 148a4760fd..72d06fc0d3 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/potentiometerpid/Robot.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/potentiometerpid/Robot.java @@ -6,10 +6,10 @@ package edu.wpi.first.wpilibj.examples.potentiometerpid; import edu.wpi.first.wpilibj.AnalogInput; import edu.wpi.first.wpilibj.Joystick; -import edu.wpi.first.wpilibj.PWMSparkMax; -import edu.wpi.first.wpilibj.SpeedController; import edu.wpi.first.wpilibj.TimedRobot; import edu.wpi.first.wpilibj.controller.PIDController; +import edu.wpi.first.wpilibj.motorcontrol.MotorController; +import edu.wpi.first.wpilibj.motorcontrol.PWMSparkMax; /** * This is a sample program to demonstrate how to use a soft potentiometer and a PID controller to @@ -33,7 +33,7 @@ public class Robot extends TimedRobot { private PIDController m_pidController; private AnalogInput m_potentiometer; - private SpeedController m_elevatorMotor; + private MotorController m_elevatorMotor; private Joystick m_joystick; private int m_index; diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/ramsetecommand/subsystems/DriveSubsystem.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/ramsetecommand/subsystems/DriveSubsystem.java index 7ccb1f27f4..275b49f215 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/ramsetecommand/subsystems/DriveSubsystem.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/ramsetecommand/subsystems/DriveSubsystem.java @@ -6,26 +6,26 @@ package edu.wpi.first.wpilibj.examples.ramsetecommand.subsystems; import edu.wpi.first.wpilibj.ADXRS450_Gyro; import edu.wpi.first.wpilibj.Encoder; -import edu.wpi.first.wpilibj.PWMSparkMax; -import edu.wpi.first.wpilibj.SpeedControllerGroup; import edu.wpi.first.wpilibj.drive.DifferentialDrive; import edu.wpi.first.wpilibj.examples.ramsetecommand.Constants.DriveConstants; import edu.wpi.first.wpilibj.geometry.Pose2d; import edu.wpi.first.wpilibj.interfaces.Gyro; import edu.wpi.first.wpilibj.kinematics.DifferentialDriveOdometry; import edu.wpi.first.wpilibj.kinematics.DifferentialDriveWheelSpeeds; +import edu.wpi.first.wpilibj.motorcontrol.MotorControllerGroup; +import edu.wpi.first.wpilibj.motorcontrol.PWMSparkMax; import edu.wpi.first.wpilibj2.command.SubsystemBase; public class DriveSubsystem extends SubsystemBase { // The motors on the left side of the drive. - private final SpeedControllerGroup m_leftMotors = - new SpeedControllerGroup( + private final MotorControllerGroup m_leftMotors = + new MotorControllerGroup( new PWMSparkMax(DriveConstants.kLeftMotor1Port), new PWMSparkMax(DriveConstants.kLeftMotor2Port)); // The motors on the right side of the drive. - private final SpeedControllerGroup m_rightMotors = - new SpeedControllerGroup( + private final MotorControllerGroup m_rightMotors = + new MotorControllerGroup( new PWMSparkMax(DriveConstants.kRightMotor1Port), new PWMSparkMax(DriveConstants.kRightMotor2Port)); diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/ramsetecontroller/Drivetrain.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/ramsetecontroller/Drivetrain.java index 1c40b944bb..d1ff5dd849 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/ramsetecontroller/Drivetrain.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/ramsetecontroller/Drivetrain.java @@ -6,9 +6,6 @@ package edu.wpi.first.wpilibj.examples.ramsetecontroller; import edu.wpi.first.wpilibj.AnalogGyro; import edu.wpi.first.wpilibj.Encoder; -import edu.wpi.first.wpilibj.PWMSparkMax; -import edu.wpi.first.wpilibj.SpeedController; -import edu.wpi.first.wpilibj.SpeedControllerGroup; import edu.wpi.first.wpilibj.controller.PIDController; import edu.wpi.first.wpilibj.controller.SimpleMotorFeedforward; import edu.wpi.first.wpilibj.geometry.Pose2d; @@ -16,6 +13,9 @@ import edu.wpi.first.wpilibj.kinematics.ChassisSpeeds; import edu.wpi.first.wpilibj.kinematics.DifferentialDriveKinematics; import edu.wpi.first.wpilibj.kinematics.DifferentialDriveOdometry; import edu.wpi.first.wpilibj.kinematics.DifferentialDriveWheelSpeeds; +import edu.wpi.first.wpilibj.motorcontrol.MotorController; +import edu.wpi.first.wpilibj.motorcontrol.MotorControllerGroup; +import edu.wpi.first.wpilibj.motorcontrol.PWMSparkMax; /** Represents a differential drive style drivetrain. */ public class Drivetrain { @@ -26,18 +26,18 @@ public class Drivetrain { private static final double kWheelRadius = 0.0508; // meters private static final int kEncoderResolution = 4096; - private final SpeedController m_leftLeader = new PWMSparkMax(1); - private final SpeedController m_leftFollower = new PWMSparkMax(2); - private final SpeedController m_rightLeader = new PWMSparkMax(3); - private final SpeedController m_rightFollower = new PWMSparkMax(4); + private final MotorController m_leftLeader = new PWMSparkMax(1); + private final MotorController m_leftFollower = new PWMSparkMax(2); + private final MotorController m_rightLeader = new PWMSparkMax(3); + private final MotorController m_rightFollower = new PWMSparkMax(4); private final Encoder m_leftEncoder = new Encoder(0, 1); private final Encoder m_rightEncoder = new Encoder(2, 3); - private final SpeedControllerGroup m_leftGroup = - new SpeedControllerGroup(m_leftLeader, m_leftFollower); - private final SpeedControllerGroup m_rightGroup = - new SpeedControllerGroup(m_rightLeader, m_rightFollower); + private final MotorControllerGroup m_leftGroup = + new MotorControllerGroup(m_leftLeader, m_leftFollower); + private final MotorControllerGroup m_rightGroup = + new MotorControllerGroup(m_rightLeader, m_rightFollower); private final AnalogGyro m_gyro = new AnalogGyro(0); diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/romireference/subsystems/Drivetrain.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/romireference/subsystems/Drivetrain.java index eb3ca22a1e..cc95e0475c 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/romireference/subsystems/Drivetrain.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/romireference/subsystems/Drivetrain.java @@ -6,8 +6,8 @@ package edu.wpi.first.wpilibj.examples.romireference.subsystems; import edu.wpi.first.wpilibj.BuiltInAccelerometer; import edu.wpi.first.wpilibj.Encoder; -import edu.wpi.first.wpilibj.Spark; import edu.wpi.first.wpilibj.drive.DifferentialDrive; +import edu.wpi.first.wpilibj.motorcontrol.Spark; import edu.wpi.first.wpilibj.romi.RomiGyro; import edu.wpi.first.wpilibj2.command.SubsystemBase; diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/shuffleboard/Robot.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/shuffleboard/Robot.java index bc1704f71c..eb6a6d94c8 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/shuffleboard/Robot.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/shuffleboard/Robot.java @@ -7,9 +7,9 @@ package edu.wpi.first.wpilibj.examples.shuffleboard; import edu.wpi.first.networktables.NetworkTableEntry; import edu.wpi.first.wpilibj.AnalogPotentiometer; import edu.wpi.first.wpilibj.Encoder; -import edu.wpi.first.wpilibj.PWMSparkMax; import edu.wpi.first.wpilibj.TimedRobot; import edu.wpi.first.wpilibj.drive.DifferentialDrive; +import edu.wpi.first.wpilibj.motorcontrol.PWMSparkMax; import edu.wpi.first.wpilibj.shuffleboard.Shuffleboard; import edu.wpi.first.wpilibj.shuffleboard.ShuffleboardLayout; import edu.wpi.first.wpilibj.shuffleboard.ShuffleboardTab; diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/simpledifferentialdrivesimulation/Drivetrain.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/simpledifferentialdrivesimulation/Drivetrain.java index 9fa5e6b54c..2cb16c13fa 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/simpledifferentialdrivesimulation/Drivetrain.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/simpledifferentialdrivesimulation/Drivetrain.java @@ -6,9 +6,7 @@ package edu.wpi.first.wpilibj.examples.simpledifferentialdrivesimulation; import edu.wpi.first.wpilibj.AnalogGyro; import edu.wpi.first.wpilibj.Encoder; -import edu.wpi.first.wpilibj.PWMSparkMax; import edu.wpi.first.wpilibj.RobotController; -import edu.wpi.first.wpilibj.SpeedControllerGroup; import edu.wpi.first.wpilibj.controller.PIDController; import edu.wpi.first.wpilibj.controller.SimpleMotorFeedforward; import edu.wpi.first.wpilibj.geometry.Pose2d; @@ -16,6 +14,8 @@ import edu.wpi.first.wpilibj.kinematics.ChassisSpeeds; import edu.wpi.first.wpilibj.kinematics.DifferentialDriveKinematics; import edu.wpi.first.wpilibj.kinematics.DifferentialDriveOdometry; import edu.wpi.first.wpilibj.kinematics.DifferentialDriveWheelSpeeds; +import edu.wpi.first.wpilibj.motorcontrol.MotorControllerGroup; +import edu.wpi.first.wpilibj.motorcontrol.PWMSparkMax; import edu.wpi.first.wpilibj.simulation.AnalogGyroSim; import edu.wpi.first.wpilibj.simulation.DifferentialDrivetrainSim; import edu.wpi.first.wpilibj.simulation.EncoderSim; @@ -42,10 +42,10 @@ public class Drivetrain { private final PWMSparkMax m_rightLeader = new PWMSparkMax(3); private final PWMSparkMax m_rightFollower = new PWMSparkMax(4); - private final SpeedControllerGroup m_leftGroup = - new SpeedControllerGroup(m_leftLeader, m_leftFollower); - private final SpeedControllerGroup m_rightGroup = - new SpeedControllerGroup(m_rightLeader, m_rightFollower); + private final MotorControllerGroup m_leftGroup = + new MotorControllerGroup(m_leftLeader, m_leftFollower); + private final MotorControllerGroup m_rightGroup = + new MotorControllerGroup(m_rightLeader, m_rightFollower); private final Encoder m_leftEncoder = new Encoder(0, 1); private final Encoder m_rightEncoder = new Encoder(2, 3); diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/statespacearm/Robot.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/statespacearm/Robot.java index aa43dff01f..c7f8d24ece 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/statespacearm/Robot.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/statespacearm/Robot.java @@ -6,11 +6,11 @@ package edu.wpi.first.wpilibj.examples.statespacearm; import edu.wpi.first.wpilibj.Encoder; import edu.wpi.first.wpilibj.Joystick; -import edu.wpi.first.wpilibj.PWMSparkMax; -import edu.wpi.first.wpilibj.SpeedController; import edu.wpi.first.wpilibj.TimedRobot; import edu.wpi.first.wpilibj.controller.LinearQuadraticRegulator; import edu.wpi.first.wpilibj.estimator.KalmanFilter; +import edu.wpi.first.wpilibj.motorcontrol.MotorController; +import edu.wpi.first.wpilibj.motorcontrol.PWMSparkMax; import edu.wpi.first.wpilibj.system.LinearSystem; import edu.wpi.first.wpilibj.system.LinearSystemLoop; import edu.wpi.first.wpilibj.system.plant.DCMotor; @@ -91,7 +91,7 @@ public class Robot extends TimedRobot { // An encoder set up to measure arm position in radians. private final Encoder m_encoder = new Encoder(kEncoderAChannel, kEncoderBChannel); - private final SpeedController m_motor = new PWMSparkMax(kMotorPort); + private final MotorController m_motor = new PWMSparkMax(kMotorPort); // A joystick to read the trigger from. private final Joystick m_joystick = new Joystick(kJoystickPort); diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/statespacedifferentialdrivesimulation/subsystems/DriveSubsystem.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/statespacedifferentialdrivesimulation/subsystems/DriveSubsystem.java index 1e70efe380..32394ea1d3 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/statespacedifferentialdrivesimulation/subsystems/DriveSubsystem.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/statespacedifferentialdrivesimulation/subsystems/DriveSubsystem.java @@ -6,16 +6,16 @@ package edu.wpi.first.wpilibj.examples.statespacedifferentialdrivesimulation.sub import edu.wpi.first.wpilibj.ADXRS450_Gyro; import edu.wpi.first.wpilibj.Encoder; -import edu.wpi.first.wpilibj.PWMSparkMax; import edu.wpi.first.wpilibj.RobotBase; import edu.wpi.first.wpilibj.RobotController; -import edu.wpi.first.wpilibj.SpeedControllerGroup; import edu.wpi.first.wpilibj.drive.DifferentialDrive; import edu.wpi.first.wpilibj.examples.statespacedifferentialdrivesimulation.Constants.DriveConstants; import edu.wpi.first.wpilibj.geometry.Pose2d; import edu.wpi.first.wpilibj.geometry.Rotation2d; import edu.wpi.first.wpilibj.kinematics.DifferentialDriveOdometry; import edu.wpi.first.wpilibj.kinematics.DifferentialDriveWheelSpeeds; +import edu.wpi.first.wpilibj.motorcontrol.MotorControllerGroup; +import edu.wpi.first.wpilibj.motorcontrol.PWMSparkMax; import edu.wpi.first.wpilibj.simulation.ADXRS450_GyroSim; import edu.wpi.first.wpilibj.simulation.DifferentialDrivetrainSim; import edu.wpi.first.wpilibj.simulation.EncoderSim; @@ -26,14 +26,14 @@ import edu.wpi.first.wpiutil.math.VecBuilder; public class DriveSubsystem extends SubsystemBase { // The motors on the left side of the drive. - private final SpeedControllerGroup m_leftMotors = - new SpeedControllerGroup( + private final MotorControllerGroup m_leftMotors = + new MotorControllerGroup( new PWMSparkMax(DriveConstants.kLeftMotor1Port), new PWMSparkMax(DriveConstants.kLeftMotor2Port)); // The motors on the right side of the drive. - private final SpeedControllerGroup m_rightMotors = - new SpeedControllerGroup( + private final MotorControllerGroup m_rightMotors = + new MotorControllerGroup( new PWMSparkMax(DriveConstants.kRightMotor1Port), new PWMSparkMax(DriveConstants.kRightMotor2Port)); diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/statespaceelevator/Robot.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/statespaceelevator/Robot.java index 57ce6c3ae7..64c3bd1df7 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/statespaceelevator/Robot.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/statespaceelevator/Robot.java @@ -6,11 +6,11 @@ package edu.wpi.first.wpilibj.examples.statespaceelevator; import edu.wpi.first.wpilibj.Encoder; import edu.wpi.first.wpilibj.Joystick; -import edu.wpi.first.wpilibj.PWMSparkMax; -import edu.wpi.first.wpilibj.SpeedController; import edu.wpi.first.wpilibj.TimedRobot; import edu.wpi.first.wpilibj.controller.LinearQuadraticRegulator; import edu.wpi.first.wpilibj.estimator.KalmanFilter; +import edu.wpi.first.wpilibj.motorcontrol.MotorController; +import edu.wpi.first.wpilibj.motorcontrol.PWMSparkMax; import edu.wpi.first.wpilibj.system.LinearSystem; import edu.wpi.first.wpilibj.system.LinearSystemLoop; import edu.wpi.first.wpilibj.system.plant.DCMotor; @@ -94,7 +94,7 @@ public class Robot extends TimedRobot { // An encoder set up to measure elevator height in meters. private final Encoder m_encoder = new Encoder(kEncoderAChannel, kEncoderBChannel); - private final SpeedController m_motor = new PWMSparkMax(kMotorPort); + private final MotorController m_motor = new PWMSparkMax(kMotorPort); // A joystick to read the trigger from. private final Joystick m_joystick = new Joystick(kJoystickPort); diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/statespaceflywheel/Robot.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/statespaceflywheel/Robot.java index bf1f4f78c9..cb6d10a076 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/statespaceflywheel/Robot.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/statespaceflywheel/Robot.java @@ -6,11 +6,11 @@ package edu.wpi.first.wpilibj.examples.statespaceflywheel; import edu.wpi.first.wpilibj.Encoder; import edu.wpi.first.wpilibj.Joystick; -import edu.wpi.first.wpilibj.PWMSparkMax; -import edu.wpi.first.wpilibj.SpeedController; import edu.wpi.first.wpilibj.TimedRobot; import edu.wpi.first.wpilibj.controller.LinearQuadraticRegulator; import edu.wpi.first.wpilibj.estimator.KalmanFilter; +import edu.wpi.first.wpilibj.motorcontrol.MotorController; +import edu.wpi.first.wpilibj.motorcontrol.PWMSparkMax; import edu.wpi.first.wpilibj.system.LinearSystem; import edu.wpi.first.wpilibj.system.LinearSystemLoop; import edu.wpi.first.wpilibj.system.plant.DCMotor; @@ -77,7 +77,7 @@ public class Robot extends TimedRobot { // An encoder set up to measure flywheel velocity in radians per second. private final Encoder m_encoder = new Encoder(kEncoderAChannel, kEncoderBChannel); - private final SpeedController m_motor = new PWMSparkMax(kMotorPort); + private final MotorController m_motor = new PWMSparkMax(kMotorPort); // A joystick to read the trigger from. private final Joystick m_joystick = new Joystick(kJoystickPort); diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/statespaceflywheelsysid/Robot.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/statespaceflywheelsysid/Robot.java index 9c080346ee..7bb4491f12 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/statespaceflywheelsysid/Robot.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/statespaceflywheelsysid/Robot.java @@ -6,11 +6,11 @@ package edu.wpi.first.wpilibj.examples.statespaceflywheelsysid; import edu.wpi.first.wpilibj.Encoder; import edu.wpi.first.wpilibj.Joystick; -import edu.wpi.first.wpilibj.PWMSparkMax; -import edu.wpi.first.wpilibj.SpeedController; import edu.wpi.first.wpilibj.TimedRobot; import edu.wpi.first.wpilibj.controller.LinearQuadraticRegulator; import edu.wpi.first.wpilibj.estimator.KalmanFilter; +import edu.wpi.first.wpilibj.motorcontrol.MotorController; +import edu.wpi.first.wpilibj.motorcontrol.PWMSparkMax; import edu.wpi.first.wpilibj.system.LinearSystem; import edu.wpi.first.wpilibj.system.LinearSystemLoop; import edu.wpi.first.wpilibj.system.plant.LinearSystemId; @@ -72,7 +72,7 @@ public class Robot extends TimedRobot { // An encoder set up to measure flywheel velocity in radians per second. private final Encoder m_encoder = new Encoder(kEncoderAChannel, kEncoderBChannel); - private final SpeedController m_motor = new PWMSparkMax(kMotorPort); + private final MotorController m_motor = new PWMSparkMax(kMotorPort); // A joystick to read the trigger from. private final Joystick m_joystick = new Joystick(kJoystickPort); diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/swervebot/SwerveModule.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/swervebot/SwerveModule.java index 887001c090..cd5691ecf7 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/swervebot/SwerveModule.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/swervebot/SwerveModule.java @@ -5,13 +5,13 @@ package edu.wpi.first.wpilibj.examples.swervebot; import edu.wpi.first.wpilibj.Encoder; -import edu.wpi.first.wpilibj.PWMSparkMax; -import edu.wpi.first.wpilibj.SpeedController; import edu.wpi.first.wpilibj.controller.PIDController; import edu.wpi.first.wpilibj.controller.ProfiledPIDController; import edu.wpi.first.wpilibj.controller.SimpleMotorFeedforward; import edu.wpi.first.wpilibj.geometry.Rotation2d; import edu.wpi.first.wpilibj.kinematics.SwerveModuleState; +import edu.wpi.first.wpilibj.motorcontrol.MotorController; +import edu.wpi.first.wpilibj.motorcontrol.PWMSparkMax; import edu.wpi.first.wpilibj.trajectory.TrapezoidProfile; public class SwerveModule { @@ -22,8 +22,8 @@ public class SwerveModule { private static final double kModuleMaxAngularAcceleration = 2 * Math.PI; // radians per second squared - private final SpeedController m_driveMotor; - private final SpeedController m_turningMotor; + private final MotorController m_driveMotor; + private final MotorController m_turningMotor; private final Encoder m_driveEncoder = new Encoder(0, 1); private final Encoder m_turningEncoder = new Encoder(2, 3); diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/swervecontrollercommand/subsystems/SwerveModule.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/swervecontrollercommand/subsystems/SwerveModule.java index 4ec1545da0..12c6aad699 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/swervecontrollercommand/subsystems/SwerveModule.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/swervecontrollercommand/subsystems/SwerveModule.java @@ -5,12 +5,12 @@ package edu.wpi.first.wpilibj.examples.swervecontrollercommand.subsystems; import edu.wpi.first.wpilibj.Encoder; -import edu.wpi.first.wpilibj.Spark; import edu.wpi.first.wpilibj.controller.PIDController; import edu.wpi.first.wpilibj.controller.ProfiledPIDController; import edu.wpi.first.wpilibj.examples.swervecontrollercommand.Constants.ModuleConstants; import edu.wpi.first.wpilibj.geometry.Rotation2d; import edu.wpi.first.wpilibj.kinematics.SwerveModuleState; +import edu.wpi.first.wpilibj.motorcontrol.Spark; import edu.wpi.first.wpilibj.trajectory.TrapezoidProfile; public class SwerveModule { diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/swervesdriveposeestimator/SwerveModule.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/swervesdriveposeestimator/SwerveModule.java index 236de7df97..cf36020959 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/swervesdriveposeestimator/SwerveModule.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/swervesdriveposeestimator/SwerveModule.java @@ -5,13 +5,13 @@ package edu.wpi.first.wpilibj.examples.swervesdriveposeestimator; import edu.wpi.first.wpilibj.Encoder; -import edu.wpi.first.wpilibj.PWMSparkMax; -import edu.wpi.first.wpilibj.SpeedController; import edu.wpi.first.wpilibj.controller.PIDController; import edu.wpi.first.wpilibj.controller.ProfiledPIDController; import edu.wpi.first.wpilibj.controller.SimpleMotorFeedforward; import edu.wpi.first.wpilibj.geometry.Rotation2d; import edu.wpi.first.wpilibj.kinematics.SwerveModuleState; +import edu.wpi.first.wpilibj.motorcontrol.MotorController; +import edu.wpi.first.wpilibj.motorcontrol.PWMSparkMax; import edu.wpi.first.wpilibj.trajectory.TrapezoidProfile; public class SwerveModule { @@ -22,8 +22,8 @@ public class SwerveModule { private static final double kModuleMaxAngularAcceleration = 2 * Math.PI; // radians per second squared - private final SpeedController m_driveMotor; - private final SpeedController m_turningMotor; + private final MotorController m_driveMotor; + private final MotorController m_turningMotor; private final Encoder m_driveEncoder = new Encoder(0, 1); private final Encoder m_turningEncoder = new Encoder(2, 3); diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/tankdrive/Robot.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/tankdrive/Robot.java index bf2a568e1a..1d934ff68a 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/tankdrive/Robot.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/tankdrive/Robot.java @@ -5,9 +5,9 @@ package edu.wpi.first.wpilibj.examples.tankdrive; import edu.wpi.first.wpilibj.Joystick; -import edu.wpi.first.wpilibj.PWMSparkMax; import edu.wpi.first.wpilibj.TimedRobot; import edu.wpi.first.wpilibj.drive.DifferentialDrive; +import edu.wpi.first.wpilibj.motorcontrol.PWMSparkMax; /** * This is a demo program showing the use of the DifferentialDrive class, specifically it contains diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/tankdrivexboxcontroller/Robot.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/tankdrivexboxcontroller/Robot.java index e54d44da66..a2579f38a8 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/tankdrivexboxcontroller/Robot.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/tankdrivexboxcontroller/Robot.java @@ -5,10 +5,10 @@ package edu.wpi.first.wpilibj.examples.tankdrivexboxcontroller; import edu.wpi.first.wpilibj.GenericHID.Hand; -import edu.wpi.first.wpilibj.PWMSparkMax; import edu.wpi.first.wpilibj.TimedRobot; import edu.wpi.first.wpilibj.XboxController; import edu.wpi.first.wpilibj.drive.DifferentialDrive; +import edu.wpi.first.wpilibj.motorcontrol.PWMSparkMax; /** * This is a demo program showing the use of the DifferentialDrive class. Runs the motors with tank diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/ultrasonic/Robot.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/ultrasonic/Robot.java index 58de666e31..754d0eae2b 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/ultrasonic/Robot.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/ultrasonic/Robot.java @@ -6,9 +6,9 @@ package edu.wpi.first.wpilibj.examples.ultrasonic; import edu.wpi.first.wpilibj.AnalogInput; import edu.wpi.first.wpilibj.MedianFilter; -import edu.wpi.first.wpilibj.PWMSparkMax; import edu.wpi.first.wpilibj.TimedRobot; import edu.wpi.first.wpilibj.drive.DifferentialDrive; +import edu.wpi.first.wpilibj.motorcontrol.PWMSparkMax; /** * This is a sample program demonstrating how to use an ultrasonic sensor and proportional control diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/ultrasonicpid/Robot.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/ultrasonicpid/Robot.java index c1fcb60804..79726292da 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/ultrasonicpid/Robot.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/ultrasonicpid/Robot.java @@ -6,10 +6,10 @@ package edu.wpi.first.wpilibj.examples.ultrasonicpid; import edu.wpi.first.wpilibj.AnalogInput; import edu.wpi.first.wpilibj.MedianFilter; -import edu.wpi.first.wpilibj.PWMSparkMax; import edu.wpi.first.wpilibj.TimedRobot; import edu.wpi.first.wpilibj.controller.PIDController; import edu.wpi.first.wpilibj.drive.DifferentialDrive; +import edu.wpi.first.wpilibj.motorcontrol.PWMSparkMax; /** * This is a sample program to demonstrate the use of a PIDController with an ultrasonic sensor to diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/templates/romicommandbased/subsystems/RomiDrivetrain.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/templates/romicommandbased/subsystems/RomiDrivetrain.java index 3d256e0cf3..80543c6f3c 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/templates/romicommandbased/subsystems/RomiDrivetrain.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/templates/romicommandbased/subsystems/RomiDrivetrain.java @@ -5,8 +5,8 @@ package edu.wpi.first.wpilibj.templates.romicommandbased.subsystems; import edu.wpi.first.wpilibj.Encoder; -import edu.wpi.first.wpilibj.Spark; import edu.wpi.first.wpilibj.drive.DifferentialDrive; +import edu.wpi.first.wpilibj.motorcontrol.Spark; import edu.wpi.first.wpilibj2.command.SubsystemBase; public class RomiDrivetrain extends SubsystemBase { diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/templates/romitimed/RomiDrivetrain.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/templates/romitimed/RomiDrivetrain.java index 84d627280a..96c81b1bb0 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/templates/romitimed/RomiDrivetrain.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/templates/romitimed/RomiDrivetrain.java @@ -5,8 +5,8 @@ package edu.wpi.first.wpilibj.templates.romitimed; import edu.wpi.first.wpilibj.Encoder; -import edu.wpi.first.wpilibj.Spark; import edu.wpi.first.wpilibj.drive.DifferentialDrive; +import edu.wpi.first.wpilibj.motorcontrol.Spark; public class RomiDrivetrain { private static final double kCountsPerRevolution = 1440.0; diff --git a/wpilibjIntegrationTests/src/main/java/edu/wpi/first/wpilibj/MotorInvertingTest.java b/wpilibjIntegrationTests/src/main/java/edu/wpi/first/wpilibj/MotorInvertingTest.java index dc44ecbe71..af9a272254 100644 --- a/wpilibjIntegrationTests/src/main/java/edu/wpi/first/wpilibj/MotorInvertingTest.java +++ b/wpilibjIntegrationTests/src/main/java/edu/wpi/first/wpilibj/MotorInvertingTest.java @@ -20,7 +20,7 @@ import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import org.junit.runners.Parameterized.Parameters; -/** Tests Inversion of motors using the SpeedController setInverted. */ +/** Tests Inversion of motors using the MotorController setInverted. */ @RunWith(Parameterized.class) public class MotorInvertingTest extends AbstractComsSetup { static MotorEncoderFixture fixture = null; diff --git a/wpilibjIntegrationTests/src/main/java/edu/wpi/first/wpilibj/PDPTest.java b/wpilibjIntegrationTests/src/main/java/edu/wpi/first/wpilibj/PDPTest.java index 1c30544a65..127e4c21bb 100644 --- a/wpilibjIntegrationTests/src/main/java/edu/wpi/first/wpilibj/PDPTest.java +++ b/wpilibjIntegrationTests/src/main/java/edu/wpi/first/wpilibj/PDPTest.java @@ -70,7 +70,7 @@ public class PDPTest extends AbstractComsSetup { /** Test if the current changes when the motor is driven using a talon. */ @Test - public void checkStoppedCurrentForSpeedController() throws CANMessageNotFoundException { + public void checkStoppedCurrentForMotorController() throws CANMessageNotFoundException { Timer.delay(0.25); /* The Current should be 0 */ @@ -83,7 +83,7 @@ public class PDPTest extends AbstractComsSetup { /** Test if the current changes when the motor is driven using a talon. */ @Test - public void checkRunningCurrentForSpeedController() throws CANMessageNotFoundException { + public void checkRunningCurrentForMotorController() throws CANMessageNotFoundException { /* Set the motor to full forward */ me.getMotor().set(1.0); Timer.delay(2); diff --git a/wpilibjIntegrationTests/src/main/java/edu/wpi/first/wpilibj/fixtures/MotorEncoderFixture.java b/wpilibjIntegrationTests/src/main/java/edu/wpi/first/wpilibj/fixtures/MotorEncoderFixture.java index fcc5b2cd6e..492c2d5c3e 100644 --- a/wpilibjIntegrationTests/src/main/java/edu/wpi/first/wpilibj/fixtures/MotorEncoderFixture.java +++ b/wpilibjIntegrationTests/src/main/java/edu/wpi/first/wpilibj/fixtures/MotorEncoderFixture.java @@ -8,8 +8,8 @@ import edu.wpi.first.wpilibj.Counter; import edu.wpi.first.wpilibj.DigitalInput; import edu.wpi.first.wpilibj.Encoder; import edu.wpi.first.wpilibj.PWM; -import edu.wpi.first.wpilibj.SpeedController; import edu.wpi.first.wpilibj.Timer; +import edu.wpi.first.wpilibj.motorcontrol.MotorController; import edu.wpi.first.wpilibj.test.TestBench; import java.lang.reflect.ParameterizedType; import java.util.logging.Logger; @@ -22,7 +22,7 @@ import java.util.logging.Logger; * fixture. This allows tests to be mailable so that you can easily reconfigure the physical testbed * without breaking the tests. */ -public abstract class MotorEncoderFixture implements ITestFixture { +public abstract class MotorEncoderFixture implements ITestFixture { private static final Logger logger = Logger.getLogger(MotorEncoderFixture.class.getName()); private boolean m_initialized = false; private boolean m_tornDown = false; @@ -39,12 +39,12 @@ public abstract class MotorEncoderFixture implements /** * Where the implementer of this class should pass the speed controller Constructor should only be - * called from outside this class if the Speed controller is not also an implementation of PWM + * called from outside this class if the Motor controller is not also an implementation of PWM * interface. * - * @return SpeedController + * @return MotorController */ - protected abstract T giveSpeedController(); + protected abstract T giveMotorController(); /** * Where the implementer of this class should pass one of the digital inputs. @@ -76,7 +76,7 @@ public abstract class MotorEncoderFixture implements m_counters[0] = new Counter(m_alphaSource); m_counters[1] = new Counter(m_betaSource); logger.fine("Creating the speed controller!"); - m_motor = giveSpeedController(); + m_motor = giveMotorController(); } } } diff --git a/wpilibj/src/test/java/edu/wpi/first/wpilibj/MockSpeedController.java b/wpilibjIntegrationTests/src/main/java/edu/wpi/first/wpilibj/motorcontrol/MockMotorController.java similarity index 86% rename from wpilibj/src/test/java/edu/wpi/first/wpilibj/MockSpeedController.java rename to wpilibjIntegrationTests/src/main/java/edu/wpi/first/wpilibj/motorcontrol/MockMotorController.java index a7bd4a802f..e7a6b8eb36 100644 --- a/wpilibj/src/test/java/edu/wpi/first/wpilibj/MockSpeedController.java +++ b/wpilibjIntegrationTests/src/main/java/edu/wpi/first/wpilibj/motorcontrol/MockMotorController.java @@ -2,9 +2,9 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.first.wpilibj; +package edu.wpi.first.wpilibj.motorcontrol; -public class MockSpeedController implements SpeedController { +public class MockMotorController implements MotorController { private double m_speed; private boolean m_isInverted; diff --git a/wpilibjIntegrationTests/src/main/java/edu/wpi/first/wpilibj/test/TestBench.java b/wpilibjIntegrationTests/src/main/java/edu/wpi/first/wpilibj/test/TestBench.java index bee3787076..89713c6945 100644 --- a/wpilibjIntegrationTests/src/main/java/edu/wpi/first/wpilibj/test/TestBench.java +++ b/wpilibjIntegrationTests/src/main/java/edu/wpi/first/wpilibj/test/TestBench.java @@ -8,16 +8,16 @@ import edu.wpi.first.wpilibj.AnalogGyro; import edu.wpi.first.wpilibj.AnalogInput; import edu.wpi.first.wpilibj.AnalogOutput; import edu.wpi.first.wpilibj.DigitalInput; -import edu.wpi.first.wpilibj.Jaguar; import edu.wpi.first.wpilibj.Relay; import edu.wpi.first.wpilibj.Servo; -import edu.wpi.first.wpilibj.Talon; -import edu.wpi.first.wpilibj.Victor; import edu.wpi.first.wpilibj.fixtures.AnalogCrossConnectFixture; import edu.wpi.first.wpilibj.fixtures.DIOCrossConnectFixture; import edu.wpi.first.wpilibj.fixtures.MotorEncoderFixture; import edu.wpi.first.wpilibj.fixtures.RelayCrossConnectFixture; import edu.wpi.first.wpilibj.fixtures.TiltPanCameraFixture; +import edu.wpi.first.wpilibj.motorcontrol.Jaguar; +import edu.wpi.first.wpilibj.motorcontrol.Talon; +import edu.wpi.first.wpilibj.motorcontrol.Victor; import java.io.PrintStream; import java.util.ArrayList; import java.util.Arrays; @@ -74,7 +74,7 @@ public final class TestBench { public MotorEncoderFixture getTalonPair() { return new MotorEncoderFixture() { @Override - protected Talon giveSpeedController() { + protected Talon giveMotorController() { return new Talon(kTalonChannel); } @@ -104,7 +104,7 @@ public final class TestBench { public MotorEncoderFixture getVictorPair() { return new MotorEncoderFixture() { @Override - protected Victor giveSpeedController() { + protected Victor giveMotorController() { return new Victor(kVictorChannel); } @@ -134,7 +134,7 @@ public final class TestBench { public MotorEncoderFixture getJaguarPair() { return new MotorEncoderFixture() { @Override - protected Jaguar giveSpeedController() { + protected Jaguar giveMotorController() { return new Jaguar(kJaguarChannel); } From 8d961dfd2575f8cb38fdc6cb105ec4ddf9d7e738 Mon Sep 17 00:00:00 2001 From: Peter Johnson Date: Sun, 18 Apr 2021 20:35:29 -0700 Subject: [PATCH 23/34] [wpilibc] Remove ErrorBase (#3306) Replace with new exception-based error reporting, consistent with Java. This also builds stacktraces into the reporting/exceptions. --- .../main/native/cpp/frc2/command/Command.cpp | 3 +- .../cpp/frc2/command/CommandGroupBase.cpp | 18 +- .../cpp/frc2/command/CommandScheduler.cpp | 7 +- .../cpp/frc2/command/ParallelCommandGroup.cpp | 13 +- .../frc2/command/ParallelDeadlineGroup.cpp | 13 +- .../cpp/frc2/command/ParallelRaceGroup.cpp | 13 +- .../frc2/command/SequentialCommandGroup.cpp | 6 +- .../native/include/frc2/command/Command.h | 5 +- .../include/frc2/command/CommandScheduler.h | 14 +- .../frc2/command/SequentialCommandGroup.h | 2 - .../frc2/command/CommandRequirementsTest.cpp | 8 +- .../native/cpp/frc2/command/CommandTestBase.h | 1 - .../cpp/frc2/command/ErrorConfirmer.cpp | 19 -- .../native/cpp/frc2/command/ErrorConfirmer.h | 38 --- .../src/main/native/cpp/commands/Command.cpp | 33 +-- .../main/native/cpp/commands/CommandGroup.cpp | 27 +- .../main/native/cpp/commands/Scheduler.cpp | 20 +- .../main/native/cpp/commands/Subsystem.cpp | 7 +- .../native/include/frc/commands/Command.h | 5 +- .../native/include/frc/commands/Scheduler.h | 5 +- .../native/include/frc/commands/Subsystem.h | 5 +- .../src/main/native/cpp/AddressableLED.cpp | 21 +- .../main/native/cpp/AnalogAccelerometer.cpp | 16 +- wpilibc/src/main/native/cpp/AnalogGyro.cpp | 93 ++----- wpilibc/src/main/native/cpp/AnalogInput.cpp | 125 +++------ wpilibc/src/main/native/cpp/AnalogOutput.cpp | 23 +- wpilibc/src/main/native/cpp/AnalogTrigger.cpp | 63 +---- .../main/native/cpp/AnalogTriggerOutput.cpp | 4 +- .../main/native/cpp/BuiltInAccelerometer.cpp | 6 +- wpilibc/src/main/native/cpp/CAN.cpp | 33 +-- wpilibc/src/main/native/cpp/Compressor.cpp | 163 ++---------- wpilibc/src/main/native/cpp/Counter.cpp | 172 ++++--------- wpilibc/src/main/native/cpp/DMA.cpp | 43 ++-- .../main/native/cpp/DigitalGlitchFilter.cpp | 42 +-- wpilibc/src/main/native/cpp/DigitalInput.cpp | 23 +- wpilibc/src/main/native/cpp/DigitalOutput.cpp | 82 ++---- .../src/main/native/cpp/DoubleSolenoid.cpp | 59 ++--- wpilibc/src/main/native/cpp/DriverStation.cpp | 49 ++-- wpilibc/src/main/native/cpp/DutyCycle.cpp | 28 +- wpilibc/src/main/native/cpp/Encoder.cpp | 116 +++------ wpilibc/src/main/native/cpp/Error.cpp | 108 -------- wpilibc/src/main/native/cpp/ErrorBase.cpp | 198 --------------- wpilibc/src/main/native/cpp/Errors.cpp | 78 ++++++ wpilibc/src/main/native/cpp/GenericHID.cpp | 7 +- wpilibc/src/main/native/cpp/I2C.cpp | 21 +- .../native/cpp/InterruptableSensorBase.cpp | 89 ++----- wpilibc/src/main/native/cpp/MotorSafety.cpp | 9 +- wpilibc/src/main/native/cpp/Notifier.cpp | 25 +- wpilibc/src/main/native/cpp/PWM.cpp | 90 ++----- .../native/cpp/PowerDistributionPanel.cpp | 68 +---- wpilibc/src/main/native/cpp/Preferences.cpp | 2 - wpilibc/src/main/native/cpp/Relay.cpp | 47 +--- wpilibc/src/main/native/cpp/Resource.cpp | 18 +- .../src/main/native/cpp/RobotController.cpp | 51 ++-- wpilibc/src/main/native/cpp/SPI.cpp | 36 ++- wpilibc/src/main/native/cpp/SerialPort.cpp | 63 ++--- wpilibc/src/main/native/cpp/Solenoid.cpp | 41 +-- wpilibc/src/main/native/cpp/SolenoidBase.cpp | 19 +- wpilibc/src/main/native/cpp/Threads.cpp | 10 +- wpilibc/src/main/native/cpp/TimedRobot.cpp | 8 +- wpilibc/src/main/native/cpp/Ultrasonic.cpp | 10 +- wpilibc/src/main/native/cpp/Watchdog.cpp | 13 +- .../shuffleboard/ShuffleboardContainer.cpp | 5 +- .../cpp/smartdashboard/SmartDashboard.cpp | 15 +- wpilibc/src/main/native/cppcs/RobotBase.cpp | 6 +- .../src/main/native/include/frc/ADXL345_I2C.h | 4 +- .../src/main/native/include/frc/ADXL345_SPI.h | 4 +- wpilibc/src/main/native/include/frc/ADXL362.h | 4 +- .../main/native/include/frc/ADXRS450_Gyro.h | 2 - .../main/native/include/frc/AddressableLED.h | 5 +- .../native/include/frc/AnalogAccelerometer.h | 4 +- .../main/native/include/frc/AnalogEncoder.h | 5 +- .../src/main/native/include/frc/AnalogGyro.h | 2 - .../src/main/native/include/frc/AnalogInput.h | 5 +- .../main/native/include/frc/AnalogOutput.h | 5 +- .../native/include/frc/AnalogPotentiometer.h | 4 +- .../main/native/include/frc/AnalogTrigger.h | 5 +- .../native/include/frc/BuiltInAccelerometer.h | 4 +- wpilibc/src/main/native/include/frc/CAN.h | 6 +- .../src/main/native/include/frc/Compressor.h | 5 +- wpilibc/src/main/native/include/frc/Counter.h | 4 +- wpilibc/src/main/native/include/frc/DMA.h | 6 +- .../native/include/frc/DigitalGlitchFilter.h | 4 +- .../main/native/include/frc/DriverStation.h | 6 +- .../src/main/native/include/frc/DutyCycle.h | 5 +- .../native/include/frc/DutyCycleEncoder.h | 4 +- wpilibc/src/main/native/include/frc/Encoder.h | 4 +- wpilibc/src/main/native/include/frc/Error.h | 63 ----- .../src/main/native/include/frc/ErrorBase.h | 239 ------------------ wpilibc/src/main/native/include/frc/Errors.h | 139 ++++++++++ .../src/main/native/include/frc/GenericHID.h | 6 +- wpilibc/src/main/native/include/frc/I2C.h | 6 +- .../include/frc/InterruptableSensorBase.h | 5 +- .../src/main/native/include/frc/MotorSafety.h | 5 +- .../src/main/native/include/frc/Notifier.h | 6 +- wpilibc/src/main/native/include/frc/PWM.h | 3 +- .../include/frc/PowerDistributionPanel.h | 4 +- .../src/main/native/include/frc/Preferences.h | 6 +- wpilibc/src/main/native/include/frc/Relay.h | 1 - .../src/main/native/include/frc/Resource.h | 6 +- .../src/main/native/include/frc/RobotBase.h | 16 +- wpilibc/src/main/native/include/frc/SPI.h | 6 +- .../src/main/native/include/frc/SerialPort.h | 6 +- .../main/native/include/frc/SolenoidBase.h | 6 +- .../src/main/native/include/frc/TimedRobot.h | 3 +- .../src/main/native/include/frc/Ultrasonic.h | 5 +- .../frc/{WPIErrors.h => WPIErrors.mac} | 63 ++--- .../main/native/include/frc/WPIWarnings.mac | 23 ++ .../include/frc/motorcontrol/NidecBrushless.h | 1 - .../main/native/include/frc/romi/RomiGyro.h | 6 +- .../frc/shuffleboard/ShuffleboardContainer.h | 5 +- .../frc/smartdashboard/SmartDashboard.h | 5 +- .../native/cpp/PowerDistributionPanelTest.cpp | 2 - 113 files changed, 993 insertions(+), 2200 deletions(-) delete mode 100644 wpilibNewCommands/src/test/native/cpp/frc2/command/ErrorConfirmer.cpp delete mode 100644 wpilibNewCommands/src/test/native/cpp/frc2/command/ErrorConfirmer.h delete mode 100644 wpilibc/src/main/native/cpp/Error.cpp delete mode 100644 wpilibc/src/main/native/cpp/ErrorBase.cpp create mode 100644 wpilibc/src/main/native/cpp/Errors.cpp delete mode 100644 wpilibc/src/main/native/include/frc/Error.h delete mode 100644 wpilibc/src/main/native/include/frc/ErrorBase.h create mode 100644 wpilibc/src/main/native/include/frc/Errors.h rename wpilibc/src/main/native/include/frc/{WPIErrors.h => WPIErrors.mac} (55%) create mode 100644 wpilibc/src/main/native/include/frc/WPIWarnings.mac diff --git a/wpilibNewCommands/src/main/native/cpp/frc2/command/Command.cpp b/wpilibNewCommands/src/main/native/cpp/frc2/command/Command.cpp index 8d2d6d2bd4..9a94d3d026 100644 --- a/wpilibNewCommands/src/main/native/cpp/frc2/command/Command.cpp +++ b/wpilibNewCommands/src/main/native/cpp/frc2/command/Command.cpp @@ -21,10 +21,9 @@ Command::~Command() { CommandScheduler::GetInstance().Cancel(this); } -Command::Command(const Command& rhs) : ErrorBase(rhs) {} +Command::Command(const Command& rhs) = default; Command& Command::operator=(const Command& rhs) { - ErrorBase::operator=(rhs); m_isGrouped = false; return *this; } diff --git a/wpilibNewCommands/src/main/native/cpp/frc2/command/CommandGroupBase.cpp b/wpilibNewCommands/src/main/native/cpp/frc2/command/CommandGroupBase.cpp index c09a7dd113..a59fc0307c 100644 --- a/wpilibNewCommands/src/main/native/cpp/frc2/command/CommandGroupBase.cpp +++ b/wpilibNewCommands/src/main/native/cpp/frc2/command/CommandGroupBase.cpp @@ -4,19 +4,15 @@ #include "frc2/command/CommandGroupBase.h" -#include - using namespace frc2; bool CommandGroupBase::RequireUngrouped(Command& command) { if (command.IsGrouped()) { - wpi_setGlobalWPIErrorWithContext( - CommandIllegalUse, + throw FRC_MakeError( + frc::err::CommandIllegalUse, "Commands cannot be added to more than one CommandGroup"); - return false; - } else { - return true; } + return true; } bool CommandGroupBase::RequireUngrouped( @@ -26,8 +22,8 @@ bool CommandGroupBase::RequireUngrouped( allUngrouped &= !command.get()->IsGrouped(); } if (!allUngrouped) { - wpi_setGlobalWPIErrorWithContext( - CommandIllegalUse, + throw FRC_MakeError( + frc::err::CommandIllegalUse, "Commands cannot be added to more than one CommandGroup"); } return allUngrouped; @@ -40,8 +36,8 @@ bool CommandGroupBase::RequireUngrouped( allUngrouped &= !command->IsGrouped(); } if (!allUngrouped) { - wpi_setGlobalWPIErrorWithContext( - CommandIllegalUse, + throw FRC_MakeError( + frc::err::CommandIllegalUse, "Commands cannot be added to more than one CommandGroup"); } return allUngrouped; diff --git a/wpilibNewCommands/src/main/native/cpp/frc2/command/CommandScheduler.cpp b/wpilibNewCommands/src/main/native/cpp/frc2/command/CommandScheduler.cpp index 57e33a222a..f08c21fd22 100644 --- a/wpilibNewCommands/src/main/native/cpp/frc2/command/CommandScheduler.cpp +++ b/wpilibNewCommands/src/main/native/cpp/frc2/command/CommandScheduler.cpp @@ -7,7 +7,6 @@ #include #include #include -#include #include #include #include @@ -112,9 +111,9 @@ void CommandScheduler::Schedule(bool interruptible, Command* command) { } if (command->IsGrouped()) { - wpi_setWPIErrorWithContext(CommandIllegalUse, - "A command that is part of a command group " - "cannot be independently scheduled"); + throw FRC_MakeError(frc::err::CommandIllegalUse, + "A command that is part of a command group " + "cannot be independently scheduled"); return; } if (m_impl->disabled || diff --git a/wpilibNewCommands/src/main/native/cpp/frc2/command/ParallelCommandGroup.cpp b/wpilibNewCommands/src/main/native/cpp/frc2/command/ParallelCommandGroup.cpp index c243ed5429..d6169d7574 100644 --- a/wpilibNewCommands/src/main/native/cpp/frc2/command/ParallelCommandGroup.cpp +++ b/wpilibNewCommands/src/main/native/cpp/frc2/command/ParallelCommandGroup.cpp @@ -65,9 +65,9 @@ void ParallelCommandGroup::AddCommands( } if (isRunning) { - wpi_setWPIErrorWithContext(CommandIllegalUse, - "Commands cannot be added to a CommandGroup " - "while the group is running"); + throw FRC_MakeError(frc::err::CommandIllegalUse, + "Commands cannot be added to a CommandGroup " + "while the group is running"); } for (auto&& command : commands) { @@ -77,10 +77,9 @@ void ParallelCommandGroup::AddCommands( m_runWhenDisabled &= command->RunsWhenDisabled(); m_commands.emplace_back(std::move(command), false); } else { - wpi_setWPIErrorWithContext(CommandIllegalUse, - "Multiple commands in a parallel group cannot " - "require the same subsystems"); - return; + throw FRC_MakeError(frc::err::CommandIllegalUse, + "Multiple commands in a parallel group cannot " + "require the same subsystems"); } } } diff --git a/wpilibNewCommands/src/main/native/cpp/frc2/command/ParallelDeadlineGroup.cpp b/wpilibNewCommands/src/main/native/cpp/frc2/command/ParallelDeadlineGroup.cpp index d544a6b905..16104e2513 100644 --- a/wpilibNewCommands/src/main/native/cpp/frc2/command/ParallelDeadlineGroup.cpp +++ b/wpilibNewCommands/src/main/native/cpp/frc2/command/ParallelDeadlineGroup.cpp @@ -60,9 +60,9 @@ void ParallelDeadlineGroup::AddCommands( } if (!m_finished) { - wpi_setWPIErrorWithContext(CommandIllegalUse, - "Commands cannot be added to a CommandGroup " - "while the group is running"); + throw FRC_MakeError(frc::err::CommandIllegalUse, + "Commands cannot be added to a CommandGroup " + "while the group is running"); } for (auto&& command : commands) { @@ -72,10 +72,9 @@ void ParallelDeadlineGroup::AddCommands( m_runWhenDisabled &= command->RunsWhenDisabled(); m_commands.emplace_back(std::move(command), false); } else { - wpi_setWPIErrorWithContext(CommandIllegalUse, - "Multiple commands in a parallel group cannot " - "require the same subsystems"); - return; + throw FRC_MakeError(frc::err::CommandIllegalUse, + "Multiple commands in a parallel group cannot " + "require the same subsystems"); } } } diff --git a/wpilibNewCommands/src/main/native/cpp/frc2/command/ParallelRaceGroup.cpp b/wpilibNewCommands/src/main/native/cpp/frc2/command/ParallelRaceGroup.cpp index 3bee59845c..38e0494b79 100644 --- a/wpilibNewCommands/src/main/native/cpp/frc2/command/ParallelRaceGroup.cpp +++ b/wpilibNewCommands/src/main/native/cpp/frc2/command/ParallelRaceGroup.cpp @@ -50,9 +50,9 @@ void ParallelRaceGroup::AddCommands( } if (isRunning) { - wpi_setWPIErrorWithContext(CommandIllegalUse, - "Commands cannot be added to a CommandGroup " - "while the group is running"); + throw FRC_MakeError(frc::err::CommandIllegalUse, + "Commands cannot be added to a CommandGroup " + "while the group is running"); } for (auto&& command : commands) { @@ -62,10 +62,9 @@ void ParallelRaceGroup::AddCommands( m_runWhenDisabled &= command->RunsWhenDisabled(); m_commands.emplace_back(std::move(command)); } else { - wpi_setWPIErrorWithContext(CommandIllegalUse, - "Multiple commands in a parallel group cannot " - "require the same subsystems"); - return; + throw FRC_MakeError(frc::err::CommandIllegalUse, + "Multiple commands in a parallel group cannot " + "require the same subsystems"); } } } diff --git a/wpilibNewCommands/src/main/native/cpp/frc2/command/SequentialCommandGroup.cpp b/wpilibNewCommands/src/main/native/cpp/frc2/command/SequentialCommandGroup.cpp index 766d0cefa4..a02870724a 100644 --- a/wpilibNewCommands/src/main/native/cpp/frc2/command/SequentialCommandGroup.cpp +++ b/wpilibNewCommands/src/main/native/cpp/frc2/command/SequentialCommandGroup.cpp @@ -60,9 +60,9 @@ void SequentialCommandGroup::AddCommands( } if (m_currentCommandIndex != invalid_index) { - wpi_setWPIErrorWithContext(CommandIllegalUse, - "Commands cannot be added to a CommandGroup " - "while the group is running"); + throw FRC_MakeError(frc::err::CommandIllegalUse, + "Commands cannot be added to a CommandGroup " + "while the group is running"); } for (auto&& command : commands) { diff --git a/wpilibNewCommands/src/main/native/include/frc2/command/Command.h b/wpilibNewCommands/src/main/native/include/frc2/command/Command.h index bc5edb59fe..e1177ad710 100644 --- a/wpilibNewCommands/src/main/native/include/frc2/command/Command.h +++ b/wpilibNewCommands/src/main/native/include/frc2/command/Command.h @@ -9,7 +9,6 @@ #include #include -#include #include #include #include @@ -46,10 +45,10 @@ class ProxyScheduleCommand; * @see CommandScheduler * @see CommandHelper */ -class Command : public frc::ErrorBase { +class Command { public: Command() = default; - ~Command() override; + virtual ~Command(); Command(const Command&); Command& operator=(const Command&); diff --git a/wpilibNewCommands/src/main/native/include/frc2/command/CommandScheduler.h b/wpilibNewCommands/src/main/native/include/frc2/command/CommandScheduler.h index f9f5f37984..4719524ded 100644 --- a/wpilibNewCommands/src/main/native/include/frc2/command/CommandScheduler.h +++ b/wpilibNewCommands/src/main/native/include/frc2/command/CommandScheduler.h @@ -8,8 +8,7 @@ #include #include -#include -#include +#include #include #include #include @@ -29,7 +28,6 @@ class Subsystem; * methods to be called and for their default commands to be scheduled. */ class CommandScheduler final : public frc::Sendable, - public frc::ErrorBase, public frc::SendableHelper { public: /** @@ -182,14 +180,12 @@ class CommandScheduler final : public frc::Sendable, Command, std::remove_reference_t>>> void SetDefaultCommand(Subsystem* subsystem, T&& defaultCommand) { if (!defaultCommand.HasRequirement(subsystem)) { - wpi_setWPIErrorWithContext( - CommandIllegalUse, "Default commands must require their subsystem!"); - return; + throw FRC_MakeError(frc::err::CommandIllegalUse, + "Default commands must require their subsystem!"); } if (defaultCommand.IsFinished()) { - wpi_setWPIErrorWithContext(CommandIllegalUse, - "Default commands should not end!"); - return; + throw FRC_MakeError(frc::err::CommandIllegalUse, + "Default commands should not end!"); } SetDefaultCommandImpl(subsystem, std::make_unique>( diff --git a/wpilibNewCommands/src/main/native/include/frc2/command/SequentialCommandGroup.h b/wpilibNewCommands/src/main/native/include/frc2/command/SequentialCommandGroup.h index 843e096cc7..253abb28c1 100644 --- a/wpilibNewCommands/src/main/native/include/frc2/command/SequentialCommandGroup.h +++ b/wpilibNewCommands/src/main/native/include/frc2/command/SequentialCommandGroup.h @@ -15,8 +15,6 @@ #include #include -#include -#include #include #include "frc2/command/CommandGroupBase.h" diff --git a/wpilibNewCommands/src/test/native/cpp/frc2/command/CommandRequirementsTest.cpp b/wpilibNewCommands/src/test/native/cpp/frc2/command/CommandRequirementsTest.cpp index ad6a09b91e..cf673d8bc6 100644 --- a/wpilibNewCommands/src/test/native/cpp/frc2/command/CommandRequirementsTest.cpp +++ b/wpilibNewCommands/src/test/native/cpp/frc2/command/CommandRequirementsTest.cpp @@ -2,6 +2,8 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. +#include + #include "CommandTestBase.h" #include "frc2/command/CommandScheduler.h" #include "frc2/command/ConditionalCommand.h" @@ -71,11 +73,9 @@ TEST_F(CommandRequirementsTest, RequirementUninterruptibleTest) { TEST_F(CommandRequirementsTest, DefaultCommandRequirementErrorTest) { TestSubsystem requirement1; - ErrorConfirmer confirmer("require"); MockCommand command1; - requirement1.SetDefaultCommand(std::move(command1)); - - EXPECT_TRUE(requirement1.GetDefaultCommand() == nullptr); + ASSERT_THROW(requirement1.SetDefaultCommand(std::move(command1)), + frc::RuntimeError); } diff --git a/wpilibNewCommands/src/test/native/cpp/frc2/command/CommandTestBase.h b/wpilibNewCommands/src/test/native/cpp/frc2/command/CommandTestBase.h index 07b1b97b5e..a3890f6ed6 100644 --- a/wpilibNewCommands/src/test/native/cpp/frc2/command/CommandTestBase.h +++ b/wpilibNewCommands/src/test/native/cpp/frc2/command/CommandTestBase.h @@ -9,7 +9,6 @@ #include -#include "ErrorConfirmer.h" #include "frc2/command/CommandGroupBase.h" #include "frc2/command/CommandScheduler.h" #include "frc2/command/SetUtilities.h" diff --git a/wpilibNewCommands/src/test/native/cpp/frc2/command/ErrorConfirmer.cpp b/wpilibNewCommands/src/test/native/cpp/frc2/command/ErrorConfirmer.cpp deleted file mode 100644 index 68f6a42534..0000000000 --- a/wpilibNewCommands/src/test/native/cpp/frc2/command/ErrorConfirmer.cpp +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright (c) FIRST and other WPILib contributors. -// Open Source Software; you can modify and/or share it under the terms of -// the WPILib BSD license file in the root directory of this project. - -#include "ErrorConfirmer.h" - -#include - -ErrorConfirmer* ErrorConfirmer::instance; - -int32_t ErrorConfirmer::HandleError(HAL_Bool isError, int32_t errorCode, - HAL_Bool isLVCode, const char* details, - const char* location, const char* callStack, - HAL_Bool printMsg) { - if (std::regex_search(details, std::regex(instance->m_msg))) { - instance->ConfirmError(); - } - return 1; -} diff --git a/wpilibNewCommands/src/test/native/cpp/frc2/command/ErrorConfirmer.h b/wpilibNewCommands/src/test/native/cpp/frc2/command/ErrorConfirmer.h deleted file mode 100644 index e503ab95a9..0000000000 --- a/wpilibNewCommands/src/test/native/cpp/frc2/command/ErrorConfirmer.h +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright (c) FIRST and other WPILib contributors. -// Open Source Software; you can modify and/or share it under the terms of -// the WPILib BSD license file in the root directory of this project. - -#pragma once - -#include - -#include "gmock/gmock.h" - -class ErrorConfirmer { - public: - explicit ErrorConfirmer(const char* msg) : m_msg(msg) { - if (instance != nullptr) { - return; - } - HALSIM_SetSendError(HandleError); - EXPECT_CALL(*this, ConfirmError()); - instance = this; - } - - ~ErrorConfirmer() { - HALSIM_SetSendError(nullptr); - instance = nullptr; - } - - MOCK_METHOD0(ConfirmError, void()); - - const char* m_msg; - - static int32_t HandleError(HAL_Bool isError, int32_t errorCode, - HAL_Bool isLVCode, const char* details, - const char* location, const char* callStack, - HAL_Bool printMsg); - - private: - static ErrorConfirmer* instance; -}; diff --git a/wpilibOldCommands/src/main/native/cpp/commands/Command.cpp b/wpilibOldCommands/src/main/native/cpp/commands/Command.cpp index b1a7554069..c2e9822ce9 100644 --- a/wpilibOldCommands/src/main/native/cpp/commands/Command.cpp +++ b/wpilibOldCommands/src/main/native/cpp/commands/Command.cpp @@ -6,9 +6,9 @@ #include +#include "frc/Errors.h" #include "frc/RobotState.h" #include "frc/Timer.h" -#include "frc/WPIErrors.h" #include "frc/commands/CommandGroup.h" #include "frc/commands/Scheduler.h" #include "frc/livewindow/LiveWindow.h" @@ -32,7 +32,7 @@ Command::Command(Subsystem& subsystem) : Command("", -1.0) { Command::Command(const wpi::Twine& name, double timeout) { // We use -1.0 to indicate no timeout. if (timeout < 0.0 && timeout != -1.0) { - wpi_setWPIErrorWithContext(ParameterOutOfRange, "timeout < 0.0"); + throw FRC_MakeError(err::ParameterOutOfRange, "timeout < 0.0"); } m_timeout = timeout; @@ -77,15 +77,15 @@ void Command::Requires(Subsystem* subsystem) { if (subsystem != nullptr) { m_requirements.insert(subsystem); } else { - wpi_setWPIErrorWithContext(NullParameter, "subsystem"); + throw FRC_MakeError(err::NullParameter, "subsystem"); } } void Command::Start() { LockChanges(); if (m_parent != nullptr) { - wpi_setWPIErrorWithContext( - CommandIllegalUse, + throw FRC_MakeError( + err::CommandIllegalUse, "Can not start a command that is part of a command group"); } @@ -115,8 +115,8 @@ bool Command::Run() { void Command::Cancel() { if (m_parent != nullptr) { - wpi_setWPIErrorWithContext( - CommandIllegalUse, + throw FRC_MakeError( + err::CommandIllegalUse, "Can not cancel a command that is part of a command group"); } @@ -173,7 +173,7 @@ int Command::GetID() const { void Command::SetTimeout(double timeout) { if (timeout < 0.0) { - wpi_setWPIErrorWithContext(ParameterOutOfRange, "timeout < 0.0"); + throw FRC_MakeError(err::ParameterOutOfRange, "timeout < 0.0"); } else { m_timeout = timeout; } @@ -185,21 +185,22 @@ bool Command::IsTimedOut() const { bool Command::AssertUnlocked(const std::string& message) { if (m_locked) { - std::string buf = - message + " after being started or being added to a command group"; - wpi_setWPIErrorWithContext(CommandIllegalUse, buf); - return false; + throw FRC_MakeError( + err::CommandIllegalUse, + message + + wpi::Twine{ + " after being started or being added to a command group"}); } return true; } void Command::SetParent(CommandGroup* parent) { if (parent == nullptr) { - wpi_setWPIErrorWithContext(NullParameter, "parent"); + throw FRC_MakeError(err::NullParameter, "parent"); } else if (m_parent != nullptr) { - wpi_setWPIErrorWithContext(CommandIllegalUse, - "Can not give command to a command group after " - "already being put in a command group"); + throw FRC_MakeError(err::CommandIllegalUse, + "Can not give command to a command group after " + "already being put in a command group"); } else { LockChanges(); m_parent = parent; diff --git a/wpilibOldCommands/src/main/native/cpp/commands/CommandGroup.cpp b/wpilibOldCommands/src/main/native/cpp/commands/CommandGroup.cpp index ff8ee7c985..f743724889 100644 --- a/wpilibOldCommands/src/main/native/cpp/commands/CommandGroup.cpp +++ b/wpilibOldCommands/src/main/native/cpp/commands/CommandGroup.cpp @@ -4,16 +4,15 @@ #include "frc/commands/CommandGroup.h" -#include "frc/WPIErrors.h" +#include "frc/Errors.h" using namespace frc; CommandGroup::CommandGroup(const wpi::Twine& name) : Command(name) {} void CommandGroup::AddSequential(Command* command) { - if (command == nullptr) { - wpi_setWPIErrorWithContext(NullParameter, "command"); - return; + if (!command) { + throw FRC_MakeError(err::NullParameter, "command"); } if (!AssertUnlocked("Cannot add new command to command group")) { return; @@ -31,16 +30,14 @@ void CommandGroup::AddSequential(Command* command) { } void CommandGroup::AddSequential(Command* command, double timeout) { - if (command == nullptr) { - wpi_setWPIErrorWithContext(NullParameter, "command"); - return; + if (!command) { + throw FRC_MakeError(err::NullParameter, "command"); } if (!AssertUnlocked("Cannot add new command to command group")) { return; } if (timeout < 0.0) { - wpi_setWPIErrorWithContext(ParameterOutOfRange, "timeout < 0.0"); - return; + throw FRC_MakeError(err::ParameterOutOfRange, "timeout < 0.0"); } m_commands.emplace_back(command, CommandGroupEntry::kSequence_InSequence, @@ -56,8 +53,8 @@ void CommandGroup::AddSequential(Command* command, double timeout) { } void CommandGroup::AddParallel(Command* command) { - if (command == nullptr) { - wpi_setWPIErrorWithContext(NullParameter, "command"); + if (!command) { + throw FRC_MakeError(err::NullParameter, "command"); return; } if (!AssertUnlocked("Cannot add new command to command group")) { @@ -76,16 +73,14 @@ void CommandGroup::AddParallel(Command* command) { } void CommandGroup::AddParallel(Command* command, double timeout) { - if (command == nullptr) { - wpi_setWPIErrorWithContext(NullParameter, "command"); - return; + if (!command) { + throw FRC_MakeError(err::NullParameter, "command"); } if (!AssertUnlocked("Cannot add new command to command group")) { return; } if (timeout < 0.0) { - wpi_setWPIErrorWithContext(ParameterOutOfRange, "timeout < 0.0"); - return; + throw FRC_MakeError(err::ParameterOutOfRange, "timeout < 0.0"); } m_commands.emplace_back(command, CommandGroupEntry::kSequence_BranchChild, diff --git a/wpilibOldCommands/src/main/native/cpp/commands/Scheduler.cpp b/wpilibOldCommands/src/main/native/cpp/commands/Scheduler.cpp index 8b3803ea80..42bebb821e 100644 --- a/wpilibOldCommands/src/main/native/cpp/commands/Scheduler.cpp +++ b/wpilibOldCommands/src/main/native/cpp/commands/Scheduler.cpp @@ -13,7 +13,7 @@ #include #include -#include "frc/WPIErrors.h" +#include "frc/Errors.h" #include "frc/buttons/ButtonScheduler.h" #include "frc/commands/Command.h" #include "frc/commands/Subsystem.h" @@ -64,9 +64,8 @@ void Scheduler::AddButton(ButtonScheduler* button) { } void Scheduler::RegisterSubsystem(Subsystem* subsystem) { - if (subsystem == nullptr) { - wpi_setWPIErrorWithContext(NullParameter, "subsystem"); - return; + if (!subsystem) { + throw FRC_MakeError(err::NullParameter, "subsystem"); } m_impl->subsystems.insert(subsystem); } @@ -109,8 +108,8 @@ void Scheduler::Run() { for (auto& addition : m_impl->additions) { // Check to make sure no adding during adding if (m_impl->adding) { - wpi_setWPIErrorWithContext(IncompatibleState, - "Can not start command from cancel method"); + FRC_ReportError(warn::IncompatibleState, + "Can not start command from cancel method"); } else { m_impl->ProcessCommandAddition(addition); } @@ -122,8 +121,8 @@ void Scheduler::Run() { for (auto& subsystem : m_impl->subsystems) { if (subsystem->GetCurrentCommand() == nullptr) { if (m_impl->adding) { - wpi_setWPIErrorWithContext(IncompatibleState, - "Can not start command from cancel method"); + FRC_ReportError(warn::IncompatibleState, + "Can not start command from cancel method"); } else { m_impl->ProcessCommandAddition(subsystem->GetDefaultCommand()); } @@ -133,9 +132,8 @@ void Scheduler::Run() { } void Scheduler::Remove(Command* command) { - if (command == nullptr) { - wpi_setWPIErrorWithContext(NullParameter, "command"); - return; + if (!command) { + throw FRC_MakeError(err::NullParameter, "command"); } m_impl->Remove(command); diff --git a/wpilibOldCommands/src/main/native/cpp/commands/Subsystem.cpp b/wpilibOldCommands/src/main/native/cpp/commands/Subsystem.cpp index 3026af85e4..2ef307c610 100644 --- a/wpilibOldCommands/src/main/native/cpp/commands/Subsystem.cpp +++ b/wpilibOldCommands/src/main/native/cpp/commands/Subsystem.cpp @@ -4,7 +4,7 @@ #include "frc/commands/Subsystem.h" -#include "frc/WPIErrors.h" +#include "frc/Errors.h" #include "frc/commands/Command.h" #include "frc/commands/Scheduler.h" #include "frc/livewindow/LiveWindow.h" @@ -24,9 +24,8 @@ void Subsystem::SetDefaultCommand(Command* command) { } else { const auto& reqs = command->GetRequirements(); if (std::find(reqs.begin(), reqs.end(), this) == reqs.end()) { - wpi_setWPIErrorWithContext( - CommandIllegalUse, "A default command must require the subsystem"); - return; + throw FRC_MakeError(err::CommandIllegalUse, + "A default command must require the subsystem"); } m_defaultCommand = command; diff --git a/wpilibOldCommands/src/main/native/include/frc/commands/Command.h b/wpilibOldCommands/src/main/native/include/frc/commands/Command.h index 167d2bf9c4..3863f85836 100644 --- a/wpilibOldCommands/src/main/native/include/frc/commands/Command.h +++ b/wpilibOldCommands/src/main/native/include/frc/commands/Command.h @@ -10,7 +10,6 @@ #include #include -#include "frc/ErrorBase.h" #include "frc/commands/Subsystem.h" #include "frc/smartdashboard/Sendable.h" #include "frc/smartdashboard/SendableHelper.h" @@ -43,9 +42,7 @@ class CommandGroup; * @see CommandGroup * @see Subsystem */ -class Command : public ErrorBase, - public Sendable, - public SendableHelper { +class Command : public Sendable, public SendableHelper { friend class CommandGroup; friend class Scheduler; diff --git a/wpilibOldCommands/src/main/native/include/frc/commands/Scheduler.h b/wpilibOldCommands/src/main/native/include/frc/commands/Scheduler.h index 0bdd4b8465..83372dc278 100644 --- a/wpilibOldCommands/src/main/native/include/frc/commands/Scheduler.h +++ b/wpilibOldCommands/src/main/native/include/frc/commands/Scheduler.h @@ -6,7 +6,6 @@ #include -#include "frc/ErrorBase.h" #include "frc/smartdashboard/Sendable.h" #include "frc/smartdashboard/SendableHelper.h" @@ -16,9 +15,7 @@ class ButtonScheduler; class Command; class Subsystem; -class Scheduler : public ErrorBase, - public Sendable, - public SendableHelper { +class Scheduler : public Sendable, public SendableHelper { public: /** * Returns the Scheduler, creating it if one does not exist. diff --git a/wpilibOldCommands/src/main/native/include/frc/commands/Subsystem.h b/wpilibOldCommands/src/main/native/include/frc/commands/Subsystem.h index a2f9e5f250..fe3a997861 100644 --- a/wpilibOldCommands/src/main/native/include/frc/commands/Subsystem.h +++ b/wpilibOldCommands/src/main/native/include/frc/commands/Subsystem.h @@ -10,7 +10,6 @@ #include #include -#include "frc/ErrorBase.h" #include "frc/smartdashboard/Sendable.h" #include "frc/smartdashboard/SendableHelper.h" @@ -18,9 +17,7 @@ namespace frc { class Command; -class Subsystem : public ErrorBase, - public Sendable, - public SendableHelper { +class Subsystem : public Sendable, public SendableHelper { friend class Scheduler; public: diff --git a/wpilibc/src/main/native/cpp/AddressableLED.cpp b/wpilibc/src/main/native/cpp/AddressableLED.cpp index 335982f395..db297ffe46 100644 --- a/wpilibc/src/main/native/cpp/AddressableLED.cpp +++ b/wpilibc/src/main/native/cpp/AddressableLED.cpp @@ -10,7 +10,7 @@ #include #include -#include "frc/WPIErrors.h" +#include "frc/Errors.h" using namespace frc; @@ -18,13 +18,13 @@ AddressableLED::AddressableLED(int port) { int32_t status = 0; m_pwmHandle = HAL_InitializePWMPort(HAL_GetPort(port), &status); - wpi_setHALErrorWithRange(status, 0, HAL_GetNumPWMChannels(), port); + FRC_CheckErrorStatus(status, "Port " + wpi::Twine{port}); if (m_pwmHandle == HAL_kInvalidHandle) { return; } m_handle = HAL_InitializeAddressableLED(m_pwmHandle, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "Port " + wpi::Twine{port}); if (m_handle == HAL_kInvalidHandle) { HAL_FreePWMPort(m_pwmHandle, &status); } @@ -36,12 +36,13 @@ AddressableLED::~AddressableLED() { HAL_FreeAddressableLED(m_handle); int32_t status = 0; HAL_FreePWMPort(m_pwmHandle, &status); + FRC_ReportError(status, "FreePWM"); } void AddressableLED::SetLength(int length) { int32_t status = 0; HAL_SetAddressableLEDLength(m_handle, length, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "length " + wpi::Twine{length}); } static_assert(sizeof(AddressableLED::LEDData) == sizeof(HAL_AddressableLEDData), @@ -51,14 +52,14 @@ void AddressableLED::SetData(wpi::ArrayRef ledData) { int32_t status = 0; HAL_WriteAddressableLEDData(m_handle, ledData.begin(), ledData.size(), &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "SetData"); } void AddressableLED::SetData(std::initializer_list ledData) { int32_t status = 0; HAL_WriteAddressableLEDData(m_handle, ledData.begin(), ledData.size(), &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "SetData"); } void AddressableLED::SetBitTiming(units::nanosecond_t lowTime0, @@ -69,25 +70,25 @@ void AddressableLED::SetBitTiming(units::nanosecond_t lowTime0, HAL_SetAddressableLEDBitTiming( m_handle, lowTime0.to(), highTime0.to(), lowTime1.to(), highTime1.to(), &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "SetBitTiming"); } void AddressableLED::SetSyncTime(units::microsecond_t syncTime) { int32_t status = 0; HAL_SetAddressableLEDSyncTime(m_handle, syncTime.to(), &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "SetSyncTime"); } void AddressableLED::Start() { int32_t status = 0; HAL_StartAddressableLEDOutput(m_handle, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "Start"); } void AddressableLED::Stop() { int32_t status = 0; HAL_StopAddressableLEDOutput(m_handle, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "Stop"); } void AddressableLED::LEDData::SetHSV(int h, int s, int v) { diff --git a/wpilibc/src/main/native/cpp/AnalogAccelerometer.cpp b/wpilibc/src/main/native/cpp/AnalogAccelerometer.cpp index e0e3ef71ac..d27048b6f0 100644 --- a/wpilibc/src/main/native/cpp/AnalogAccelerometer.cpp +++ b/wpilibc/src/main/native/cpp/AnalogAccelerometer.cpp @@ -7,7 +7,7 @@ #include #include "frc/Base.h" -#include "frc/WPIErrors.h" +#include "frc/Errors.h" #include "frc/smartdashboard/SendableBuilder.h" #include "frc/smartdashboard/SendableRegistry.h" @@ -20,20 +20,18 @@ AnalogAccelerometer::AnalogAccelerometer(int channel) AnalogAccelerometer::AnalogAccelerometer(AnalogInput* channel) : m_analogInput(channel, NullDeleter()) { - if (channel == nullptr) { - wpi_setWPIError(NullParameter); - } else { - InitAccelerometer(); + if (!channel) { + throw FRC_MakeError(err::NullParameter, "channel"); } + InitAccelerometer(); } AnalogAccelerometer::AnalogAccelerometer(std::shared_ptr channel) : m_analogInput(channel) { - if (channel == nullptr) { - wpi_setWPIError(NullParameter); - } else { - InitAccelerometer(); + if (!channel) { + throw FRC_MakeError(err::NullParameter, "channel"); } + InitAccelerometer(); } double AnalogAccelerometer::GetAcceleration() const { diff --git a/wpilibc/src/main/native/cpp/AnalogGyro.cpp b/wpilibc/src/main/native/cpp/AnalogGyro.cpp index 86e5b461f6..504c27384b 100644 --- a/wpilibc/src/main/native/cpp/AnalogGyro.cpp +++ b/wpilibc/src/main/native/cpp/AnalogGyro.cpp @@ -13,8 +13,8 @@ #include "frc/AnalogInput.h" #include "frc/Base.h" +#include "frc/Errors.h" #include "frc/Timer.h" -#include "frc/WPIErrors.h" #include "frc/smartdashboard/SendableBuilder.h" #include "frc/smartdashboard/SendableRegistry.h" @@ -31,12 +31,11 @@ AnalogGyro::AnalogGyro(AnalogInput* channel) AnalogGyro::AnalogGyro(std::shared_ptr channel) : m_analog(channel) { - if (channel == nullptr) { - wpi_setWPIError(NullParameter); - } else { - InitGyro(); - Calibrate(); + if (!channel) { + throw FRC_MakeError(err::NullParameter, "channel"); } + InitGyro(); + Calibrate(); } AnalogGyro::AnalogGyro(int channel, int center, double offset) @@ -47,20 +46,15 @@ AnalogGyro::AnalogGyro(int channel, int center, double offset) AnalogGyro::AnalogGyro(std::shared_ptr channel, int center, double offset) : m_analog(channel) { - if (channel == nullptr) { - wpi_setWPIError(NullParameter); - } else { - InitGyro(); - int32_t status = 0; - HAL_SetAnalogGyroParameters(m_gyroHandle, kDefaultVoltsPerDegreePerSecond, - offset, center, &status); - if (status != 0) { - wpi_setHALError(status); - m_gyroHandle = HAL_kInvalidHandle; - return; - } - Reset(); + if (!channel) { + throw FRC_MakeError(err::NullParameter, "channel"); } + InitGyro(); + int32_t status = 0; + HAL_SetAnalogGyroParameters(m_gyroHandle, kDefaultVoltsPerDegreePerSecond, + offset, center, &status); + FRC_CheckErrorStatus(status, "SetAnalogGyroParameters"); + Reset(); } AnalogGyro::~AnalogGyro() { @@ -68,42 +62,30 @@ AnalogGyro::~AnalogGyro() { } double AnalogGyro::GetAngle() const { - if (StatusIsFatal()) { - return 0.0; - } int32_t status = 0; double value = HAL_GetAnalogGyroAngle(m_gyroHandle, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "GetAngle"); return value; } double AnalogGyro::GetRate() const { - if (StatusIsFatal()) { - return 0.0; - } int32_t status = 0; double value = HAL_GetAnalogGyroRate(m_gyroHandle, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "GetRate"); return value; } int AnalogGyro::GetCenter() const { - if (StatusIsFatal()) { - return 0; - } int32_t status = 0; int value = HAL_GetAnalogGyroCenter(m_gyroHandle, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "GetCenter"); return value; } double AnalogGyro::GetOffset() const { - if (StatusIsFatal()) { - return 0.0; - } int32_t status = 0; double value = HAL_GetAnalogGyroOffset(m_gyroHandle, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "GetOffset"); return value; } @@ -111,57 +93,35 @@ void AnalogGyro::SetSensitivity(double voltsPerDegreePerSecond) { int32_t status = 0; HAL_SetAnalogGyroVoltsPerDegreePerSecond(m_gyroHandle, voltsPerDegreePerSecond, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "SetSensitivity"); } void AnalogGyro::SetDeadband(double volts) { - if (StatusIsFatal()) { - return; - } int32_t status = 0; HAL_SetAnalogGyroDeadband(m_gyroHandle, volts, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "SetDeadband"); } void AnalogGyro::Reset() { - if (StatusIsFatal()) { - return; - } int32_t status = 0; HAL_ResetAnalogGyro(m_gyroHandle, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "Reset"); } void AnalogGyro::InitGyro() { - if (StatusIsFatal()) { - return; - } if (m_gyroHandle == HAL_kInvalidHandle) { int32_t status = 0; m_gyroHandle = HAL_InitializeAnalogGyro(m_analog->m_port, &status); if (status == PARAMETER_OUT_OF_RANGE) { - wpi_setWPIErrorWithContext(ParameterOutOfRange, - " channel (must be accumulator channel)"); - m_analog = nullptr; - m_gyroHandle = HAL_kInvalidHandle; - return; - } - if (status != 0) { - wpi_setHALError(status); - m_analog = nullptr; - m_gyroHandle = HAL_kInvalidHandle; - return; + throw FRC_MakeError(err::ParameterOutOfRange, + "channel must be accumulator channel"); } + FRC_CheckErrorStatus(status, "InitializeAnalogGyro"); } int32_t status = 0; HAL_SetupAnalogGyro(m_gyroHandle, &status); - if (status != 0) { - wpi_setHALError(status); - m_analog = nullptr; - m_gyroHandle = HAL_kInvalidHandle; - return; - } + FRC_CheckErrorStatus(status, "SetupAnalogGyro"); HAL_Report(HALUsageReporting::kResourceType_Gyro, m_analog->GetChannel() + 1); @@ -170,12 +130,9 @@ void AnalogGyro::InitGyro() { } void AnalogGyro::Calibrate() { - if (StatusIsFatal()) { - return; - } int32_t status = 0; HAL_CalibrateAnalogGyro(m_gyroHandle, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "Calibrate"); } std::shared_ptr AnalogGyro::GetAnalogInput() const { diff --git a/wpilibc/src/main/native/cpp/AnalogInput.cpp b/wpilibc/src/main/native/cpp/AnalogInput.cpp index 9c6d0c2deb..e9191d8b92 100644 --- a/wpilibc/src/main/native/cpp/AnalogInput.cpp +++ b/wpilibc/src/main/native/cpp/AnalogInput.cpp @@ -10,9 +10,9 @@ #include #include +#include "frc/Errors.h" #include "frc/SensorUtil.h" #include "frc/Timer.h" -#include "frc/WPIErrors.h" #include "frc/smartdashboard/SendableBuilder.h" #include "frc/smartdashboard/SendableRegistry.h" @@ -20,9 +20,8 @@ using namespace frc; AnalogInput::AnalogInput(int channel) { if (!SensorUtil::CheckAnalogInputChannel(channel)) { - wpi_setWPIErrorWithContext(ChannelIndexOutOfRange, - "Analog Input " + wpi::Twine(channel)); - return; + throw FRC_MakeError(err::ChannelIndexOutOfRange, + "Analog Input " + wpi::Twine{channel}); } m_channel = channel; @@ -30,12 +29,7 @@ AnalogInput::AnalogInput(int channel) { HAL_PortHandle port = HAL_GetPort(channel); int32_t status = 0; m_port = HAL_InitializeAnalogInputPort(port, &status); - if (status != 0) { - wpi_setHALErrorWithRange(status, 0, HAL_GetNumAnalogInputs(), channel); - m_channel = std::numeric_limits::max(); - m_port = HAL_kInvalidHandle; - return; - } + FRC_CheckErrorStatus(status, "Analog Input " + wpi::Twine{channel}); HAL_Report(HALUsageReporting::kResourceType_AnalogChannel, channel + 1); @@ -47,210 +41,151 @@ AnalogInput::~AnalogInput() { } int AnalogInput::GetValue() const { - if (StatusIsFatal()) { - return 0; - } int32_t status = 0; int value = HAL_GetAnalogValue(m_port, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "Analog Input " + wpi::Twine{m_channel}); return value; } int AnalogInput::GetAverageValue() const { - if (StatusIsFatal()) { - return 0; - } int32_t status = 0; int value = HAL_GetAnalogAverageValue(m_port, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "Analog Input " + wpi::Twine{m_channel}); return value; } double AnalogInput::GetVoltage() const { - if (StatusIsFatal()) { - return 0.0; - } int32_t status = 0; double voltage = HAL_GetAnalogVoltage(m_port, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "Analog Input " + wpi::Twine{m_channel}); return voltage; } double AnalogInput::GetAverageVoltage() const { - if (StatusIsFatal()) { - return 0.0; - } int32_t status = 0; double voltage = HAL_GetAnalogAverageVoltage(m_port, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "Analog Input " + wpi::Twine{m_channel}); return voltage; } int AnalogInput::GetChannel() const { - if (StatusIsFatal()) { - return 0; - } return m_channel; } void AnalogInput::SetAverageBits(int bits) { - if (StatusIsFatal()) { - return; - } int32_t status = 0; HAL_SetAnalogAverageBits(m_port, bits, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "Analog Input " + wpi::Twine{m_channel}); } int AnalogInput::GetAverageBits() const { int32_t status = 0; int averageBits = HAL_GetAnalogAverageBits(m_port, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "Analog Input " + wpi::Twine{m_channel}); return averageBits; } void AnalogInput::SetOversampleBits(int bits) { - if (StatusIsFatal()) { - return; - } int32_t status = 0; HAL_SetAnalogOversampleBits(m_port, bits, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "Analog Input " + wpi::Twine{m_channel}); } int AnalogInput::GetOversampleBits() const { - if (StatusIsFatal()) { - return 0; - } int32_t status = 0; int oversampleBits = HAL_GetAnalogOversampleBits(m_port, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "Analog Input " + wpi::Twine{m_channel}); return oversampleBits; } int AnalogInput::GetLSBWeight() const { - if (StatusIsFatal()) { - return 0; - } int32_t status = 0; int lsbWeight = HAL_GetAnalogLSBWeight(m_port, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "Analog Input " + wpi::Twine{m_channel}); return lsbWeight; } int AnalogInput::GetOffset() const { - if (StatusIsFatal()) { - return 0; - } int32_t status = 0; int offset = HAL_GetAnalogOffset(m_port, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "Analog Input " + wpi::Twine{m_channel}); return offset; } bool AnalogInput::IsAccumulatorChannel() const { - if (StatusIsFatal()) { - return false; - } int32_t status = 0; bool isAccum = HAL_IsAccumulatorChannel(m_port, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "Analog Input " + wpi::Twine{m_channel}); return isAccum; } void AnalogInput::InitAccumulator() { - if (StatusIsFatal()) { - return; - } m_accumulatorOffset = 0; int32_t status = 0; HAL_InitAccumulator(m_port, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "Analog Input " + wpi::Twine{m_channel}); } void AnalogInput::SetAccumulatorInitialValue(int64_t initialValue) { - if (StatusIsFatal()) { - return; - } m_accumulatorOffset = initialValue; } void AnalogInput::ResetAccumulator() { - if (StatusIsFatal()) { - return; - } int32_t status = 0; HAL_ResetAccumulator(m_port, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "Analog Input " + wpi::Twine{m_channel}); - if (!StatusIsFatal()) { - // Wait until the next sample, so the next call to GetAccumulator*() - // won't have old values. - const double sampleTime = 1.0 / GetSampleRate(); - const double overSamples = 1 << GetOversampleBits(); - const double averageSamples = 1 << GetAverageBits(); - Wait(sampleTime * overSamples * averageSamples); - } + // Wait until the next sample, so the next call to GetAccumulator*() + // won't have old values. + const double sampleTime = 1.0 / GetSampleRate(); + const double overSamples = 1 << GetOversampleBits(); + const double averageSamples = 1 << GetAverageBits(); + Wait(sampleTime * overSamples * averageSamples); } void AnalogInput::SetAccumulatorCenter(int center) { - if (StatusIsFatal()) { - return; - } int32_t status = 0; HAL_SetAccumulatorCenter(m_port, center, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "Analog Input " + wpi::Twine{m_channel}); } void AnalogInput::SetAccumulatorDeadband(int deadband) { - if (StatusIsFatal()) { - return; - } int32_t status = 0; HAL_SetAccumulatorDeadband(m_port, deadband, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "Analog Input " + wpi::Twine{m_channel}); } int64_t AnalogInput::GetAccumulatorValue() const { - if (StatusIsFatal()) { - return 0; - } int32_t status = 0; int64_t value = HAL_GetAccumulatorValue(m_port, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "Analog Input " + wpi::Twine{m_channel}); return value + m_accumulatorOffset; } int64_t AnalogInput::GetAccumulatorCount() const { - if (StatusIsFatal()) { - return 0; - } int32_t status = 0; int64_t count = HAL_GetAccumulatorCount(m_port, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "Analog Input " + wpi::Twine{m_channel}); return count; } void AnalogInput::GetAccumulatorOutput(int64_t& value, int64_t& count) const { - if (StatusIsFatal()) { - return; - } int32_t status = 0; HAL_GetAccumulatorOutput(m_port, &value, &count, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "Analog Input " + wpi::Twine{m_channel}); value += m_accumulatorOffset; } void AnalogInput::SetSampleRate(double samplesPerSecond) { int32_t status = 0; HAL_SetAnalogSampleRate(samplesPerSecond, &status); - wpi_setGlobalHALError(status); + FRC_CheckErrorStatus(status, "SetSampleRate"); } double AnalogInput::GetSampleRate() { int32_t status = 0; double sampleRate = HAL_GetAnalogSampleRate(&status); - wpi_setGlobalHALError(status); + FRC_CheckErrorStatus(status, "GetSampleRate"); return sampleRate; } diff --git a/wpilibc/src/main/native/cpp/AnalogOutput.cpp b/wpilibc/src/main/native/cpp/AnalogOutput.cpp index 1c3e2b061a..1c86d17234 100644 --- a/wpilibc/src/main/native/cpp/AnalogOutput.cpp +++ b/wpilibc/src/main/native/cpp/AnalogOutput.cpp @@ -12,8 +12,8 @@ #include #include +#include "frc/Errors.h" #include "frc/SensorUtil.h" -#include "frc/WPIErrors.h" #include "frc/smartdashboard/SendableBuilder.h" #include "frc/smartdashboard/SendableRegistry.h" @@ -21,11 +21,8 @@ using namespace frc; AnalogOutput::AnalogOutput(int channel) { if (!SensorUtil::CheckAnalogOutputChannel(channel)) { - wpi_setWPIErrorWithContext(ChannelIndexOutOfRange, - "analog output " + wpi::Twine(channel)); - m_channel = std::numeric_limits::max(); - m_port = HAL_kInvalidHandle; - return; + throw FRC_MakeError(err::ChannelIndexOutOfRange, + "analog output " + wpi::Twine(channel)); } m_channel = channel; @@ -33,12 +30,7 @@ AnalogOutput::AnalogOutput(int channel) { HAL_PortHandle port = HAL_GetPort(m_channel); int32_t status = 0; m_port = HAL_InitializeAnalogOutputPort(port, &status); - if (status != 0) { - wpi_setHALErrorWithRange(status, 0, HAL_GetNumAnalogOutputs(), channel); - m_channel = std::numeric_limits::max(); - m_port = HAL_kInvalidHandle; - return; - } + FRC_CheckErrorStatus(status, "analog output " + wpi::Twine(channel)); HAL_Report(HALUsageReporting::kResourceType_AnalogOutput, m_channel + 1); SendableRegistry::GetInstance().AddLW(this, "AnalogOutput", m_channel); @@ -51,16 +43,13 @@ AnalogOutput::~AnalogOutput() { void AnalogOutput::SetVoltage(double voltage) { int32_t status = 0; HAL_SetAnalogOutput(m_port, voltage, &status); - - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "SetVoltage"); } double AnalogOutput::GetVoltage() const { int32_t status = 0; double voltage = HAL_GetAnalogOutput(m_port, &status); - - wpi_setHALError(status); - + FRC_CheckErrorStatus(status, "GetVoltage"); return voltage; } diff --git a/wpilibc/src/main/native/cpp/AnalogTrigger.cpp b/wpilibc/src/main/native/cpp/AnalogTrigger.cpp index 6b547b3e1e..2d19555297 100644 --- a/wpilibc/src/main/native/cpp/AnalogTrigger.cpp +++ b/wpilibc/src/main/native/cpp/AnalogTrigger.cpp @@ -11,7 +11,7 @@ #include "frc/AnalogInput.h" #include "frc/Base.h" #include "frc/DutyCycle.h" -#include "frc/WPIErrors.h" +#include "frc/Errors.h" #include "frc/smartdashboard/SendableRegistry.h" using namespace frc; @@ -26,11 +26,7 @@ AnalogTrigger::AnalogTrigger(AnalogInput* input) { m_analogInput = input; int32_t status = 0; m_trigger = HAL_InitializeAnalogTrigger(input->m_port, &status); - if (status != 0) { - wpi_setHALError(status); - m_trigger = HAL_kInvalidHandle; - return; - } + FRC_CheckErrorStatus(status, "InitializeAnalogTrigger"); int index = GetIndex(); HAL_Report(HALUsageReporting::kResourceType_AnalogTrigger, index + 1); @@ -41,11 +37,7 @@ AnalogTrigger::AnalogTrigger(DutyCycle* input) { m_dutyCycle = input; int32_t status = 0; m_trigger = HAL_InitializeAnalogTriggerDutyCycle(input->m_handle, &status); - if (status != 0) { - wpi_setHALError(status); - m_trigger = HAL_kInvalidHandle; - return; - } + FRC_CheckErrorStatus(status, "InitializeAnalogTriggerDutyCycle"); int index = GetIndex(); HAL_Report(HALUsageReporting::kResourceType_AnalogTrigger, index + 1); @@ -55,6 +47,7 @@ AnalogTrigger::AnalogTrigger(DutyCycle* input) { AnalogTrigger::~AnalogTrigger() { int32_t status = 0; HAL_CleanAnalogTrigger(m_trigger, &status); + FRC_ReportError(status, "CleanAnalogTrigger"); if (m_ownsAnalog) { delete m_analogInput; @@ -62,16 +55,13 @@ AnalogTrigger::~AnalogTrigger() { } AnalogTrigger::AnalogTrigger(AnalogTrigger&& rhs) - : ErrorBase(std::move(rhs)), - SendableHelper(std::move(rhs)), - m_trigger(std::move(rhs.m_trigger)) { + : SendableHelper(std::move(rhs)), m_trigger(std::move(rhs.m_trigger)) { std::swap(m_analogInput, rhs.m_analogInput); std::swap(m_dutyCycle, rhs.m_dutyCycle); std::swap(m_ownsAnalog, rhs.m_ownsAnalog); } AnalogTrigger& AnalogTrigger::operator=(AnalogTrigger&& rhs) { - ErrorBase::operator=(std::move(rhs)); SendableHelper::operator=(std::move(rhs)); m_trigger = std::move(rhs.m_trigger); @@ -83,85 +73,58 @@ AnalogTrigger& AnalogTrigger::operator=(AnalogTrigger&& rhs) { } void AnalogTrigger::SetLimitsVoltage(double lower, double upper) { - if (StatusIsFatal()) { - return; - } int32_t status = 0; HAL_SetAnalogTriggerLimitsVoltage(m_trigger, lower, upper, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "SetLimitsVoltage"); } void AnalogTrigger::SetLimitsDutyCycle(double lower, double upper) { - if (StatusIsFatal()) { - return; - } int32_t status = 0; HAL_SetAnalogTriggerLimitsDutyCycle(m_trigger, lower, upper, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "SetLimitsDutyCycle"); } void AnalogTrigger::SetLimitsRaw(int lower, int upper) { - if (StatusIsFatal()) { - return; - } int32_t status = 0; HAL_SetAnalogTriggerLimitsRaw(m_trigger, lower, upper, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "SetLimitsRaw"); } void AnalogTrigger::SetAveraged(bool useAveragedValue) { - if (StatusIsFatal()) { - return; - } int32_t status = 0; HAL_SetAnalogTriggerAveraged(m_trigger, useAveragedValue, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "SetAveraged"); } void AnalogTrigger::SetFiltered(bool useFilteredValue) { - if (StatusIsFatal()) { - return; - } int32_t status = 0; HAL_SetAnalogTriggerFiltered(m_trigger, useFilteredValue, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "SetFiltered"); } int AnalogTrigger::GetIndex() const { - if (StatusIsFatal()) { - return -1; - } int32_t status = 0; auto ret = HAL_GetAnalogTriggerFPGAIndex(m_trigger, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "GetIndex"); return ret; } bool AnalogTrigger::GetInWindow() { - if (StatusIsFatal()) { - return false; - } int32_t status = 0; bool result = HAL_GetAnalogTriggerInWindow(m_trigger, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "GetInWindow"); return result; } bool AnalogTrigger::GetTriggerState() { - if (StatusIsFatal()) { - return false; - } int32_t status = 0; bool result = HAL_GetAnalogTriggerTriggerState(m_trigger, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "GetTriggerState"); return result; } std::shared_ptr AnalogTrigger::CreateOutput( AnalogTriggerType type) const { - if (StatusIsFatal()) { - return nullptr; - } return std::shared_ptr( new AnalogTriggerOutput(*this, type), NullDeleter()); } diff --git a/wpilibc/src/main/native/cpp/AnalogTriggerOutput.cpp b/wpilibc/src/main/native/cpp/AnalogTriggerOutput.cpp index a9b8fb4b7f..d9f8ec7942 100644 --- a/wpilibc/src/main/native/cpp/AnalogTriggerOutput.cpp +++ b/wpilibc/src/main/native/cpp/AnalogTriggerOutput.cpp @@ -7,7 +7,7 @@ #include #include "frc/AnalogTrigger.h" -#include "frc/WPIErrors.h" +#include "frc/Errors.h" using namespace frc; @@ -16,7 +16,7 @@ bool AnalogTriggerOutput::Get() const { bool result = HAL_GetAnalogTriggerOutput( m_trigger->m_trigger, static_cast(m_outputType), &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "Get"); return result; } diff --git a/wpilibc/src/main/native/cpp/BuiltInAccelerometer.cpp b/wpilibc/src/main/native/cpp/BuiltInAccelerometer.cpp index a482cf5535..4b8b51d63f 100644 --- a/wpilibc/src/main/native/cpp/BuiltInAccelerometer.cpp +++ b/wpilibc/src/main/native/cpp/BuiltInAccelerometer.cpp @@ -7,7 +7,7 @@ #include #include -#include "frc/WPIErrors.h" +#include "frc/Errors.h" #include "frc/smartdashboard/SendableBuilder.h" #include "frc/smartdashboard/SendableRegistry.h" @@ -23,8 +23,8 @@ BuiltInAccelerometer::BuiltInAccelerometer(Range range) { void BuiltInAccelerometer::SetRange(Range range) { if (range == kRange_16G) { - wpi_setWPIErrorWithContext( - ParameterOutOfRange, "16G range not supported (use k2G, k4G, or k8G)"); + throw FRC_MakeError(err::ParameterOutOfRange, + "16G range not supported (use k2G, k4G, or k8G)"); } HAL_SetAccelerometerActive(false); diff --git a/wpilibc/src/main/native/cpp/CAN.cpp b/wpilibc/src/main/native/cpp/CAN.cpp index ee2003bd7a..7c58475afa 100644 --- a/wpilibc/src/main/native/cpp/CAN.cpp +++ b/wpilibc/src/main/native/cpp/CAN.cpp @@ -11,17 +11,15 @@ #include #include +#include "frc/Errors.h" + using namespace frc; CAN::CAN(int deviceId) { int32_t status = 0; m_handle = HAL_InitializeCAN(kTeamManufacturer, deviceId, kTeamDeviceType, &status); - if (status != 0) { - wpi_setHALError(status); - m_handle = HAL_kInvalidHandle; - return; - } + FRC_CheckErrorStatus(status, "device id " + wpi::Twine{deviceId}); HAL_Report(HALUsageReporting::kResourceType_CAN, deviceId + 1); } @@ -31,19 +29,14 @@ CAN::CAN(int deviceId, int deviceManufacturer, int deviceType) { m_handle = HAL_InitializeCAN( static_cast(deviceManufacturer), deviceId, static_cast(deviceType), &status); - if (status != 0) { - wpi_setHALError(status); - m_handle = HAL_kInvalidHandle; - return; - } + FRC_CheckErrorStatus(status, "device id " + wpi::Twine{deviceId} + " mfg " + + wpi::Twine{deviceManufacturer} + " type " + + wpi::Twine{deviceType}); HAL_Report(HALUsageReporting::kResourceType_CAN, deviceId + 1); } CAN::~CAN() { - if (StatusIsFatal()) { - return; - } if (m_handle != HAL_kInvalidHandle) { HAL_CleanCAN(m_handle); m_handle = HAL_kInvalidHandle; @@ -53,20 +46,20 @@ CAN::~CAN() { void CAN::WritePacket(const uint8_t* data, int length, int apiId) { int32_t status = 0; HAL_WriteCANPacket(m_handle, data, length, apiId, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "WritePacket"); } void CAN::WritePacketRepeating(const uint8_t* data, int length, int apiId, int repeatMs) { int32_t status = 0; HAL_WriteCANPacketRepeating(m_handle, data, length, apiId, repeatMs, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "WritePacketRepeating"); } void CAN::WriteRTRFrame(int length, int apiId) { int32_t status = 0; HAL_WriteCANRTRFrame(m_handle, length, apiId, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "WriteRTRFrame"); } int CAN::WritePacketNoError(const uint8_t* data, int length, int apiId) { @@ -91,7 +84,7 @@ int CAN::WriteRTRFrameNoError(int length, int apiId) { void CAN::StopPacketRepeating(int apiId) { int32_t status = 0; HAL_StopCANPacketRepeating(m_handle, apiId, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "StopPacketRepeating"); } bool CAN::ReadPacketNew(int apiId, CANData* data) { @@ -102,7 +95,7 @@ bool CAN::ReadPacketNew(int apiId, CANData* data) { return false; } if (status != 0) { - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "ReadPacketNew"); return false; } else { return true; @@ -117,7 +110,7 @@ bool CAN::ReadPacketLatest(int apiId, CANData* data) { return false; } if (status != 0) { - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "ReadPacketLatest"); return false; } else { return true; @@ -133,7 +126,7 @@ bool CAN::ReadPacketTimeout(int apiId, int timeoutMs, CANData* data) { return false; } if (status != 0) { - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "ReadPacketTimeout"); return false; } else { return true; diff --git a/wpilibc/src/main/native/cpp/Compressor.cpp b/wpilibc/src/main/native/cpp/Compressor.cpp index 88e5eb6415..d0654866af 100644 --- a/wpilibc/src/main/native/cpp/Compressor.cpp +++ b/wpilibc/src/main/native/cpp/Compressor.cpp @@ -9,7 +9,7 @@ #include #include -#include "frc/WPIErrors.h" +#include "frc/Errors.h" #include "frc/smartdashboard/SendableBuilder.h" #include "frc/smartdashboard/SendableRegistry.h" @@ -18,10 +18,7 @@ using namespace frc; Compressor::Compressor(int pcmID) : m_module(pcmID) { int32_t status = 0; m_compressorHandle = HAL_InitializeCompressor(m_module, &status); - if (status != 0) { - wpi_setHALErrorWithRange(status, 0, HAL_GetNumPCMModules(), pcmID); - return; - } + FRC_CheckErrorStatus(status, "Module " + wpi::Twine{m_module}); SetClosedLoopControl(true); HAL_Report(HALUsageReporting::kResourceType_Compressor, pcmID + 1); @@ -29,204 +26,96 @@ Compressor::Compressor(int pcmID) : m_module(pcmID) { } void Compressor::Start() { - if (StatusIsFatal()) { - return; - } SetClosedLoopControl(true); } void Compressor::Stop() { - if (StatusIsFatal()) { - return; - } SetClosedLoopControl(false); } bool Compressor::Enabled() const { - if (StatusIsFatal()) { - return false; - } int32_t status = 0; - bool value; - - value = HAL_GetCompressor(m_compressorHandle, &status); - - if (status) { - wpi_setWPIError(Timeout); - } - + bool value = HAL_GetCompressor(m_compressorHandle, &status); + FRC_CheckErrorStatus(status, "Module " + wpi::Twine{m_module}); return value; } bool Compressor::GetPressureSwitchValue() const { - if (StatusIsFatal()) { - return false; - } int32_t status = 0; - bool value; - - value = HAL_GetCompressorPressureSwitch(m_compressorHandle, &status); - - if (status) { - wpi_setWPIError(Timeout); - } - + bool value = HAL_GetCompressorPressureSwitch(m_compressorHandle, &status); + FRC_CheckErrorStatus(status, "Module " + wpi::Twine{m_module}); return value; } double Compressor::GetCompressorCurrent() const { - if (StatusIsFatal()) { - return 0; - } int32_t status = 0; - double value; - - value = HAL_GetCompressorCurrent(m_compressorHandle, &status); - - if (status) { - wpi_setWPIError(Timeout); - } - + double value = HAL_GetCompressorCurrent(m_compressorHandle, &status); + FRC_CheckErrorStatus(status, "Module " + wpi::Twine{m_module}); return value; } void Compressor::SetClosedLoopControl(bool on) { - if (StatusIsFatal()) { - return; - } int32_t status = 0; - HAL_SetCompressorClosedLoopControl(m_compressorHandle, on, &status); - - if (status) { - wpi_setWPIError(Timeout); - } + FRC_CheckErrorStatus(status, "Module " + wpi::Twine{m_module}); } bool Compressor::GetClosedLoopControl() const { - if (StatusIsFatal()) { - return false; - } int32_t status = 0; - bool value; - - value = HAL_GetCompressorClosedLoopControl(m_compressorHandle, &status); - - if (status) { - wpi_setWPIError(Timeout); - } - + bool value = HAL_GetCompressorClosedLoopControl(m_compressorHandle, &status); + FRC_CheckErrorStatus(status, "Module " + wpi::Twine{m_module}); return value; } bool Compressor::GetCompressorCurrentTooHighFault() const { - if (StatusIsFatal()) { - return false; - } int32_t status = 0; - bool value; - - value = HAL_GetCompressorCurrentTooHighFault(m_compressorHandle, &status); - - if (status) { - wpi_setWPIError(Timeout); - } - + bool value = + HAL_GetCompressorCurrentTooHighFault(m_compressorHandle, &status); + FRC_CheckErrorStatus(status, "Module " + wpi::Twine{m_module}); return value; } bool Compressor::GetCompressorCurrentTooHighStickyFault() const { - if (StatusIsFatal()) { - return false; - } int32_t status = 0; - bool value; - - value = + bool value = HAL_GetCompressorCurrentTooHighStickyFault(m_compressorHandle, &status); - - if (status) { - wpi_setWPIError(Timeout); - } - + FRC_CheckErrorStatus(status, "Module " + wpi::Twine{m_module}); return value; } bool Compressor::GetCompressorShortedStickyFault() const { - if (StatusIsFatal()) { - return false; - } int32_t status = 0; - bool value; - - value = HAL_GetCompressorShortedStickyFault(m_compressorHandle, &status); - - if (status) { - wpi_setWPIError(Timeout); - } - + bool value = HAL_GetCompressorShortedStickyFault(m_compressorHandle, &status); + FRC_CheckErrorStatus(status, "Module " + wpi::Twine{m_module}); return value; } bool Compressor::GetCompressorShortedFault() const { - if (StatusIsFatal()) { - return false; - } int32_t status = 0; - bool value; - - value = HAL_GetCompressorShortedFault(m_compressorHandle, &status); - - if (status) { - wpi_setWPIError(Timeout); - } - + bool value = HAL_GetCompressorShortedFault(m_compressorHandle, &status); + FRC_CheckErrorStatus(status, "Module " + wpi::Twine{m_module}); return value; } bool Compressor::GetCompressorNotConnectedStickyFault() const { - if (StatusIsFatal()) { - return false; - } int32_t status = 0; - bool value; - - value = HAL_GetCompressorNotConnectedStickyFault(m_compressorHandle, &status); - - if (status) { - wpi_setWPIError(Timeout); - } - + bool value = + HAL_GetCompressorNotConnectedStickyFault(m_compressorHandle, &status); + FRC_CheckErrorStatus(status, "Module " + wpi::Twine{m_module}); return value; } bool Compressor::GetCompressorNotConnectedFault() const { - if (StatusIsFatal()) { - return false; - } int32_t status = 0; - bool value; - - value = HAL_GetCompressorNotConnectedFault(m_compressorHandle, &status); - - if (status) { - wpi_setWPIError(Timeout); - } - + bool value = HAL_GetCompressorNotConnectedFault(m_compressorHandle, &status); + FRC_CheckErrorStatus(status, "Module " + wpi::Twine{m_module}); return value; } void Compressor::ClearAllPCMStickyFaults() { - if (StatusIsFatal()) { - return; - } int32_t status = 0; - HAL_ClearAllPCMStickyFaults(m_module, &status); - - if (status) { - wpi_setWPIError(Timeout); - } + FRC_CheckErrorStatus(status, "Module " + wpi::Twine{m_module}); } int Compressor::GetModule() const { diff --git a/wpilibc/src/main/native/cpp/Counter.cpp b/wpilibc/src/main/native/cpp/Counter.cpp index e1d9d2920e..02b1df3ba8 100644 --- a/wpilibc/src/main/native/cpp/Counter.cpp +++ b/wpilibc/src/main/native/cpp/Counter.cpp @@ -12,7 +12,7 @@ #include "frc/AnalogTrigger.h" #include "frc/Base.h" #include "frc/DigitalInput.h" -#include "frc/WPIErrors.h" +#include "frc/Errors.h" #include "frc/smartdashboard/SendableBuilder.h" #include "frc/smartdashboard/SendableRegistry.h" @@ -22,7 +22,7 @@ Counter::Counter(Mode mode) { int32_t status = 0; m_counter = HAL_InitializeCounter(static_cast(mode), &m_index, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "InitializeCounter"); SetMaxPeriod(0.5); @@ -64,10 +64,8 @@ Counter::Counter(EncodingType encodingType, std::shared_ptr downSource, bool inverted) : Counter(kExternalDirection) { if (encodingType != k1X && encodingType != k2X) { - wpi_setWPIErrorWithContext( - ParameterOutOfRange, - "Counter only supports 1X and 2X quadrature decoding."); - return; + throw FRC_MakeError(err::ParameterOutOfRange, + "Counter only supports 1X and 2X quadrature decoding."); } SetUpSource(upSource); SetDownSource(downSource); @@ -81,22 +79,23 @@ Counter::Counter(EncodingType encodingType, HAL_SetCounterAverageSize(m_counter, 2, &status); } - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "Counter constructor"); SetDownSourceEdge(inverted, true); } Counter::~Counter() { - SetUpdateWhenEmpty(true); + try { + SetUpdateWhenEmpty(true); + } catch (const RuntimeError& e) { + e.Report(); + } int32_t status = 0; HAL_FreeCounter(m_counter, &status); - wpi_setHALError(status); + FRC_ReportError(status, "Counter destructor"); } void Counter::SetUpSource(int channel) { - if (StatusIsFatal()) { - return; - } SetUpSource(std::make_shared(channel)); SendableRegistry::GetInstance().AddChild(this, m_upSource.get()); } @@ -110,9 +109,6 @@ void Counter::SetUpSource(AnalogTrigger* analogTrigger, void Counter::SetUpSource(std::shared_ptr analogTrigger, AnalogTriggerType triggerType) { - if (StatusIsFatal()) { - return; - } SetUpSource(analogTrigger->CreateOutput(triggerType)); } @@ -122,20 +118,13 @@ void Counter::SetUpSource(DigitalSource* source) { } void Counter::SetUpSource(std::shared_ptr source) { - if (StatusIsFatal()) { - return; - } m_upSource = source; - if (m_upSource->StatusIsFatal()) { - CloneError(*m_upSource); - } else { - int32_t status = 0; - HAL_SetCounterUpSource(m_counter, source->GetPortHandleForRouting(), - static_cast( - source->GetAnalogTriggerTypeForRouting()), - &status); - wpi_setHALError(status); - } + int32_t status = 0; + HAL_SetCounterUpSource(m_counter, source->GetPortHandleForRouting(), + static_cast( + source->GetAnalogTriggerTypeForRouting()), + &status); + FRC_CheckErrorStatus(status, "SetUpSource"); } void Counter::SetUpSource(DigitalSource& source) { @@ -144,33 +133,24 @@ void Counter::SetUpSource(DigitalSource& source) { } void Counter::SetUpSourceEdge(bool risingEdge, bool fallingEdge) { - if (StatusIsFatal()) { - return; - } if (m_upSource == nullptr) { - wpi_setWPIErrorWithContext( - NullParameter, + throw FRC_MakeError( + err::NullParameter, "Must set non-nullptr UpSource before setting UpSourceEdge"); } int32_t status = 0; HAL_SetCounterUpSourceEdge(m_counter, risingEdge, fallingEdge, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "SetUpSourceEdge"); } void Counter::ClearUpSource() { - if (StatusIsFatal()) { - return; - } m_upSource.reset(); int32_t status = 0; HAL_ClearCounterUpSource(m_counter, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "ClearUpSource"); } void Counter::SetDownSource(int channel) { - if (StatusIsFatal()) { - return; - } SetDownSource(std::make_shared(channel)); SendableRegistry::GetInstance().AddChild(this, m_downSource.get()); } @@ -184,9 +164,6 @@ void Counter::SetDownSource(AnalogTrigger* analogTrigger, void Counter::SetDownSource(std::shared_ptr analogTrigger, AnalogTriggerType triggerType) { - if (StatusIsFatal()) { - return; - } SetDownSource(analogTrigger->CreateOutput(triggerType)); } @@ -201,106 +178,82 @@ void Counter::SetDownSource(DigitalSource& source) { } void Counter::SetDownSource(std::shared_ptr source) { - if (StatusIsFatal()) { - return; - } m_downSource = source; - if (m_downSource->StatusIsFatal()) { - CloneError(*m_downSource); - } else { - int32_t status = 0; - HAL_SetCounterDownSource(m_counter, source->GetPortHandleForRouting(), - static_cast( - source->GetAnalogTriggerTypeForRouting()), - &status); - wpi_setHALError(status); - } + int32_t status = 0; + HAL_SetCounterDownSource(m_counter, source->GetPortHandleForRouting(), + static_cast( + source->GetAnalogTriggerTypeForRouting()), + &status); + FRC_CheckErrorStatus(status, "SetDownSource"); } void Counter::SetDownSourceEdge(bool risingEdge, bool fallingEdge) { - if (StatusIsFatal()) { - return; - } if (m_downSource == nullptr) { - wpi_setWPIErrorWithContext( - NullParameter, + throw FRC_MakeError( + err::NullParameter, "Must set non-nullptr DownSource before setting DownSourceEdge"); } int32_t status = 0; HAL_SetCounterDownSourceEdge(m_counter, risingEdge, fallingEdge, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "SetDownSourceEdge"); } void Counter::ClearDownSource() { - if (StatusIsFatal()) { - return; - } m_downSource.reset(); int32_t status = 0; HAL_ClearCounterDownSource(m_counter, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "ClearDownSource"); } void Counter::SetUpDownCounterMode() { - if (StatusIsFatal()) { - return; - } int32_t status = 0; HAL_SetCounterUpDownMode(m_counter, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "SetUpDownCounterMode"); } void Counter::SetExternalDirectionMode() { - if (StatusIsFatal()) { - return; - } int32_t status = 0; HAL_SetCounterExternalDirectionMode(m_counter, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "SetExternalDirectionMode"); } void Counter::SetSemiPeriodMode(bool highSemiPeriod) { - if (StatusIsFatal()) { - return; - } int32_t status = 0; HAL_SetCounterSemiPeriodMode(m_counter, highSemiPeriod, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus( + status, + "SetSemiPeriodMode to " + wpi::Twine{highSemiPeriod ? "true" : "false"}); } void Counter::SetPulseLengthMode(double threshold) { - if (StatusIsFatal()) { - return; - } int32_t status = 0; HAL_SetCounterPulseLengthMode(m_counter, threshold, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "SetPulseLengthMode"); } void Counter::SetReverseDirection(bool reverseDirection) { - if (StatusIsFatal()) { - return; - } int32_t status = 0; HAL_SetCounterReverseDirection(m_counter, reverseDirection, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, + "SetReverseDirection to " + + wpi::Twine{reverseDirection ? "true" : "false"}); } void Counter::SetSamplesToAverage(int samplesToAverage) { if (samplesToAverage < 1 || samplesToAverage > 127) { - wpi_setWPIErrorWithContext( - ParameterOutOfRange, - "Average counter values must be between 1 and 127"); + throw FRC_MakeError(err::ParameterOutOfRange, + "Average counter values must be between 1 and 127"); } int32_t status = 0; HAL_SetCounterSamplesToAverage(m_counter, samplesToAverage, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus( + status, "SetSamplesToAverage to " + wpi::Twine{samplesToAverage}); } int Counter::GetSamplesToAverage() const { int32_t status = 0; int samples = HAL_GetCounterSamplesToAverage(m_counter, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "GetSamplesToAverage"); return samples; } @@ -309,69 +262,48 @@ int Counter::GetFPGAIndex() const { } int Counter::Get() const { - if (StatusIsFatal()) { - return 0; - } int32_t status = 0; int value = HAL_GetCounter(m_counter, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "Get"); return value; } void Counter::Reset() { - if (StatusIsFatal()) { - return; - } int32_t status = 0; HAL_ResetCounter(m_counter, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "Reset"); } double Counter::GetPeriod() const { - if (StatusIsFatal()) { - return 0.0; - } int32_t status = 0; double value = HAL_GetCounterPeriod(m_counter, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "GetPeriod"); return value; } void Counter::SetMaxPeriod(double maxPeriod) { - if (StatusIsFatal()) { - return; - } int32_t status = 0; HAL_SetCounterMaxPeriod(m_counter, maxPeriod, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "SetMaxPeriod"); } void Counter::SetUpdateWhenEmpty(bool enabled) { - if (StatusIsFatal()) { - return; - } int32_t status = 0; HAL_SetCounterUpdateWhenEmpty(m_counter, enabled, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "SetUpdateWhenEmpty"); } bool Counter::GetStopped() const { - if (StatusIsFatal()) { - return false; - } int32_t status = 0; bool value = HAL_GetCounterStopped(m_counter, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "GetStopped"); return value; } bool Counter::GetDirection() const { - if (StatusIsFatal()) { - return false; - } int32_t status = 0; bool value = HAL_GetCounterDirection(m_counter, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "GetDirection"); return value; } diff --git a/wpilibc/src/main/native/cpp/DMA.cpp b/wpilibc/src/main/native/cpp/DMA.cpp index 87164f9a94..6c99e14d39 100644 --- a/wpilibc/src/main/native/cpp/DMA.cpp +++ b/wpilibc/src/main/native/cpp/DMA.cpp @@ -4,21 +4,22 @@ #include "frc/DMA.h" -#include -#include -#include -#include -#include - #include #include +#include "frc/AnalogInput.h" +#include "frc/Counter.h" +#include "frc/DigitalSource.h" +#include "frc/DutyCycle.h" +#include "frc/Encoder.h" +#include "frc/Errors.h" + using namespace frc; DMA::DMA() { int32_t status = 0; dmaHandle = HAL_InitializeDMA(&status); - wpi_setErrorWithContext(status, HAL_GetErrorMessage(status)); + FRC_CheckErrorStatus(status, "InitializeDMA"); } DMA::~DMA() { @@ -28,68 +29,68 @@ DMA::~DMA() { void DMA::SetPause(bool pause) { int32_t status = 0; HAL_SetDMAPause(dmaHandle, pause, &status); - wpi_setErrorWithContext(status, HAL_GetErrorMessage(status)); + FRC_CheckErrorStatus(status, "SetPause"); } void DMA::SetRate(int cycles) { int32_t status = 0; HAL_SetDMARate(dmaHandle, cycles, &status); - wpi_setErrorWithContext(status, HAL_GetErrorMessage(status)); + FRC_CheckErrorStatus(status, "SetRate"); } void DMA::AddEncoder(const Encoder* encoder) { int32_t status = 0; HAL_AddDMAEncoder(dmaHandle, encoder->m_encoder, &status); - wpi_setErrorWithContext(status, HAL_GetErrorMessage(status)); + FRC_CheckErrorStatus(status, "AddEncoder"); } void DMA::AddEncoderPeriod(const Encoder* encoder) { int32_t status = 0; HAL_AddDMAEncoderPeriod(dmaHandle, encoder->m_encoder, &status); - wpi_setErrorWithContext(status, HAL_GetErrorMessage(status)); + FRC_CheckErrorStatus(status, "AddEncoderPeriod"); } void DMA::AddCounter(const Counter* counter) { int32_t status = 0; HAL_AddDMACounter(dmaHandle, counter->m_counter, &status); - wpi_setErrorWithContext(status, HAL_GetErrorMessage(status)); + FRC_CheckErrorStatus(status, "AddCounter"); } void DMA::AddCounterPeriod(const Counter* counter) { int32_t status = 0; HAL_AddDMACounterPeriod(dmaHandle, counter->m_counter, &status); - wpi_setErrorWithContext(status, HAL_GetErrorMessage(status)); + FRC_CheckErrorStatus(status, "AddCounterPeriod"); } void DMA::AddDigitalSource(const DigitalSource* digitalSource) { int32_t status = 0; HAL_AddDMADigitalSource(dmaHandle, digitalSource->GetPortHandleForRouting(), &status); - wpi_setErrorWithContext(status, HAL_GetErrorMessage(status)); + FRC_CheckErrorStatus(status, "AddDigitalSource"); } void DMA::AddDutyCycle(const DutyCycle* dutyCycle) { int32_t status = 0; HAL_AddDMADutyCycle(dmaHandle, dutyCycle->m_handle, &status); - wpi_setErrorWithContext(status, HAL_GetErrorMessage(status)); + FRC_CheckErrorStatus(status, "AddDutyCycle"); } void DMA::AddAnalogInput(const AnalogInput* analogInput) { int32_t status = 0; HAL_AddDMAAnalogInput(dmaHandle, analogInput->m_port, &status); - wpi_setErrorWithContext(status, HAL_GetErrorMessage(status)); + FRC_CheckErrorStatus(status, "AddAnalogInput"); } void DMA::AddAveragedAnalogInput(const AnalogInput* analogInput) { int32_t status = 0; HAL_AddDMAAveragedAnalogInput(dmaHandle, analogInput->m_port, &status); - wpi_setErrorWithContext(status, HAL_GetErrorMessage(status)); + FRC_CheckErrorStatus(status, "AddAveragedAnalogInput"); } void DMA::AddAnalogAccumulator(const AnalogInput* analogInput) { int32_t status = 0; HAL_AddDMAAnalogAccumulator(dmaHandle, analogInput->m_port, &status); - wpi_setErrorWithContext(status, HAL_GetErrorMessage(status)); + FRC_CheckErrorStatus(status, "AddAnalogAccumulator"); } void DMA::SetExternalTrigger(DigitalSource* source, bool rising, bool falling) { @@ -98,17 +99,17 @@ void DMA::SetExternalTrigger(DigitalSource* source, bool rising, bool falling) { static_cast( source->GetAnalogTriggerTypeForRouting()), rising, falling, &status); - wpi_setErrorWithContext(status, HAL_GetErrorMessage(status)); + FRC_CheckErrorStatus(status, "SetExternalTrigger"); } void DMA::StartDMA(int queueDepth) { int32_t status = 0; HAL_StartDMA(dmaHandle, queueDepth, &status); - wpi_setErrorWithContext(status, HAL_GetErrorMessage(status)); + FRC_CheckErrorStatus(status, "StartDMA"); } void DMA::StopDMA() { int32_t status = 0; HAL_StopDMA(dmaHandle, &status); - wpi_setErrorWithContext(status, HAL_GetErrorMessage(status)); + FRC_CheckErrorStatus(status, "StopDMA"); } diff --git a/wpilibc/src/main/native/cpp/DigitalGlitchFilter.cpp b/wpilibc/src/main/native/cpp/DigitalGlitchFilter.cpp index 0ff4ed93aa..9fae85dbd7 100644 --- a/wpilibc/src/main/native/cpp/DigitalGlitchFilter.cpp +++ b/wpilibc/src/main/native/cpp/DigitalGlitchFilter.cpp @@ -14,9 +14,9 @@ #include "frc/Counter.h" #include "frc/Encoder.h" +#include "frc/Errors.h" #include "frc/SensorUtil.h" #include "frc/Utility.h" -#include "frc/WPIErrors.h" #include "frc/smartdashboard/SendableRegistry.h" using namespace frc; @@ -29,7 +29,7 @@ DigitalGlitchFilter::DigitalGlitchFilter() { std::scoped_lock lock(m_mutex); auto index = std::find(m_filterAllocated.begin(), m_filterAllocated.end(), false); - wpi_assert(index != m_filterAllocated.end()); + FRC_Assert(index != m_filterAllocated.end()); m_channelIndex = std::distance(m_filterAllocated.begin(), index); *index = true; @@ -48,12 +48,11 @@ DigitalGlitchFilter::~DigitalGlitchFilter() { } DigitalGlitchFilter::DigitalGlitchFilter(DigitalGlitchFilter&& rhs) - : ErrorBase(std::move(rhs)), SendableHelper(std::move(rhs)) { + : SendableHelper(std::move(rhs)) { std::swap(m_channelIndex, rhs.m_channelIndex); } DigitalGlitchFilter& DigitalGlitchFilter::operator=(DigitalGlitchFilter&& rhs) { - ErrorBase::operator=(std::move(rhs)); SendableHelper::operator=(std::move(rhs)); std::swap(m_channelIndex, rhs.m_channelIndex); @@ -71,35 +70,31 @@ void DigitalGlitchFilter::DoAdd(DigitalSource* input, int requestedIndex) { if (input) { // We don't support GlitchFilters on AnalogTriggers. if (input->IsAnalogTrigger()) { - wpi_setErrorWithContext( + throw FRC_MakeError( -1, "Analog Triggers not supported for DigitalGlitchFilters"); - return; } int32_t status = 0; HAL_SetFilterSelect(input->GetPortHandleForRouting(), requestedIndex, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, + "requested index " + wpi::Twine{requestedIndex}); // Validate that we set it correctly. int actualIndex = HAL_GetFilterSelect(input->GetPortHandleForRouting(), &status); - wpi_assertEqual(actualIndex, requestedIndex); + FRC_CheckErrorStatus(status, + "requested index " + wpi::Twine{requestedIndex}); + FRC_Assert(actualIndex == requestedIndex); } } void DigitalGlitchFilter::Add(Encoder* input) { Add(input->m_aSource.get()); - if (StatusIsFatal()) { - return; - } Add(input->m_bSource.get()); } void DigitalGlitchFilter::Add(Counter* input) { Add(input->m_upSource.get()); - if (StatusIsFatal()) { - return; - } Add(input->m_downSource.get()); } @@ -109,24 +104,18 @@ void DigitalGlitchFilter::Remove(DigitalSource* input) { void DigitalGlitchFilter::Remove(Encoder* input) { Remove(input->m_aSource.get()); - if (StatusIsFatal()) { - return; - } Remove(input->m_bSource.get()); } void DigitalGlitchFilter::Remove(Counter* input) { Remove(input->m_upSource.get()); - if (StatusIsFatal()) { - return; - } Remove(input->m_downSource.get()); } void DigitalGlitchFilter::SetPeriodCycles(int fpgaCycles) { int32_t status = 0; HAL_SetFilterPeriod(m_channelIndex, fpgaCycles, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "Channel " + wpi::Twine{m_channelIndex}); } void DigitalGlitchFilter::SetPeriodNanoSeconds(uint64_t nanoseconds) { @@ -134,25 +123,20 @@ void DigitalGlitchFilter::SetPeriodNanoSeconds(uint64_t nanoseconds) { int fpgaCycles = nanoseconds * HAL_GetSystemClockTicksPerMicrosecond() / 4 / 1000; HAL_SetFilterPeriod(m_channelIndex, fpgaCycles, &status); - - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "Channel " + wpi::Twine{m_channelIndex}); } int DigitalGlitchFilter::GetPeriodCycles() { int32_t status = 0; int fpgaCycles = HAL_GetFilterPeriod(m_channelIndex, &status); - - wpi_setHALError(status); - + FRC_CheckErrorStatus(status, "Channel " + wpi::Twine{m_channelIndex}); return fpgaCycles; } uint64_t DigitalGlitchFilter::GetPeriodNanoSeconds() { int32_t status = 0; int fpgaCycles = HAL_GetFilterPeriod(m_channelIndex, &status); - - wpi_setHALError(status); - + FRC_CheckErrorStatus(status, "Channel " + wpi::Twine{m_channelIndex}); return static_cast(fpgaCycles) * 1000L / static_cast(HAL_GetSystemClockTicksPerMicrosecond() / 4); } diff --git a/wpilibc/src/main/native/cpp/DigitalInput.cpp b/wpilibc/src/main/native/cpp/DigitalInput.cpp index 1751053950..936067ad0b 100644 --- a/wpilibc/src/main/native/cpp/DigitalInput.cpp +++ b/wpilibc/src/main/native/cpp/DigitalInput.cpp @@ -11,8 +11,8 @@ #include #include +#include "frc/Errors.h" #include "frc/SensorUtil.h" -#include "frc/WPIErrors.h" #include "frc/smartdashboard/SendableBuilder.h" #include "frc/smartdashboard/SendableRegistry.h" @@ -20,40 +20,27 @@ using namespace frc; DigitalInput::DigitalInput(int channel) { if (!SensorUtil::CheckDigitalChannel(channel)) { - wpi_setWPIErrorWithContext(ChannelIndexOutOfRange, - "Digital Channel " + wpi::Twine(channel)); - m_channel = std::numeric_limits::max(); - return; + throw FRC_MakeError(err::ChannelIndexOutOfRange, + "Digital Channel " + wpi::Twine{channel}); } m_channel = channel; int32_t status = 0; m_handle = HAL_InitializeDIOPort(HAL_GetPort(channel), true, &status); - if (status != 0) { - wpi_setHALErrorWithRange(status, 0, HAL_GetNumDigitalChannels(), channel); - m_handle = HAL_kInvalidHandle; - m_channel = std::numeric_limits::max(); - return; - } + FRC_CheckErrorStatus(status, "Digital Channel " + wpi::Twine{channel}); HAL_Report(HALUsageReporting::kResourceType_DigitalInput, channel + 1); SendableRegistry::GetInstance().AddLW(this, "DigitalInput", channel); } DigitalInput::~DigitalInput() { - if (StatusIsFatal()) { - return; - } HAL_FreeDIOPort(m_handle); } bool DigitalInput::Get() const { - if (StatusIsFatal()) { - return false; - } int32_t status = 0; bool value = HAL_GetDIO(m_handle, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "Get"); return value; } diff --git a/wpilibc/src/main/native/cpp/DigitalOutput.cpp b/wpilibc/src/main/native/cpp/DigitalOutput.cpp index c8d3c13a8e..41c5e4e1ec 100644 --- a/wpilibc/src/main/native/cpp/DigitalOutput.cpp +++ b/wpilibc/src/main/native/cpp/DigitalOutput.cpp @@ -11,8 +11,8 @@ #include #include +#include "frc/Errors.h" #include "frc/SensorUtil.h" -#include "frc/WPIErrors.h" #include "frc/smartdashboard/SendableBuilder.h" #include "frc/smartdashboard/SendableRegistry.h" @@ -21,54 +21,40 @@ using namespace frc; DigitalOutput::DigitalOutput(int channel) { m_pwmGenerator = HAL_kInvalidHandle; if (!SensorUtil::CheckDigitalChannel(channel)) { - wpi_setWPIErrorWithContext(ChannelIndexOutOfRange, - "Digital Channel " + wpi::Twine(channel)); - m_channel = std::numeric_limits::max(); - return; + throw FRC_MakeError(err::ChannelIndexOutOfRange, + "Digital Channel " + wpi::Twine{channel}); } m_channel = channel; int32_t status = 0; m_handle = HAL_InitializeDIOPort(HAL_GetPort(channel), false, &status); - if (status != 0) { - wpi_setHALErrorWithRange(status, 0, HAL_GetNumDigitalChannels(), channel); - m_channel = std::numeric_limits::max(); - m_handle = HAL_kInvalidHandle; - return; - } + FRC_CheckErrorStatus(status, "Digital Channel " + wpi::Twine{channel}); HAL_Report(HALUsageReporting::kResourceType_DigitalOutput, channel + 1); SendableRegistry::GetInstance().AddLW(this, "DigitalOutput", channel); } DigitalOutput::~DigitalOutput() { - if (StatusIsFatal()) { - return; - } // Disable the PWM in case it was running. - DisablePWM(); + try { + DisablePWM(); + } catch (const RuntimeError& e) { + e.Report(); + } HAL_FreeDIOPort(m_handle); } void DigitalOutput::Set(bool value) { - if (StatusIsFatal()) { - return; - } - int32_t status = 0; HAL_SetDIO(m_handle, value, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "Digital Channel " + wpi::Twine{m_channel}); } bool DigitalOutput::Get() const { - if (StatusIsFatal()) { - return false; - } - int32_t status = 0; bool val = HAL_GetDIO(m_handle, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "Digital Channel " + wpi::Twine{m_channel}); return val; } @@ -89,34 +75,22 @@ int DigitalOutput::GetChannel() const { } void DigitalOutput::Pulse(double length) { - if (StatusIsFatal()) { - return; - } - int32_t status = 0; HAL_Pulse(m_handle, length, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "Digital Channel " + wpi::Twine{m_channel}); } bool DigitalOutput::IsPulsing() const { - if (StatusIsFatal()) { - return false; - } - int32_t status = 0; bool value = HAL_IsPulsing(m_handle, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "Digital Channel " + wpi::Twine{m_channel}); return value; } void DigitalOutput::SetPWMRate(double rate) { - if (StatusIsFatal()) { - return; - } - int32_t status = 0; HAL_SetDigitalPWMRate(rate, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "Digital Channel " + wpi::Twine{m_channel}); } void DigitalOutput::EnablePWM(double initialDutyCycle) { @@ -126,29 +100,17 @@ void DigitalOutput::EnablePWM(double initialDutyCycle) { int32_t status = 0; - if (StatusIsFatal()) { - return; - } m_pwmGenerator = HAL_AllocateDigitalPWM(&status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "Digital Channel " + wpi::Twine{m_channel}); - if (StatusIsFatal()) { - return; - } HAL_SetDigitalPWMDutyCycle(m_pwmGenerator, initialDutyCycle, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "Digital Channel " + wpi::Twine{m_channel}); - if (StatusIsFatal()) { - return; - } HAL_SetDigitalPWMOutputChannel(m_pwmGenerator, m_channel, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "Digital Channel " + wpi::Twine{m_channel}); } void DigitalOutput::DisablePWM() { - if (StatusIsFatal()) { - return; - } if (m_pwmGenerator == HAL_kInvalidHandle) { return; } @@ -158,22 +120,18 @@ void DigitalOutput::DisablePWM() { // Disable the output by routing to a dead bit. HAL_SetDigitalPWMOutputChannel(m_pwmGenerator, SensorUtil::kDigitalChannels, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "Digital Channel " + wpi::Twine{m_channel}); HAL_FreeDigitalPWM(m_pwmGenerator, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "Digital Channel " + wpi::Twine{m_channel}); m_pwmGenerator = HAL_kInvalidHandle; } void DigitalOutput::UpdateDutyCycle(double dutyCycle) { - if (StatusIsFatal()) { - return; - } - int32_t status = 0; HAL_SetDigitalPWMDutyCycle(m_pwmGenerator, dutyCycle, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "Digital Channel " + wpi::Twine{m_channel}); } void DigitalOutput::SetSimDevice(HAL_SimDeviceHandle device) { diff --git a/wpilibc/src/main/native/cpp/DoubleSolenoid.cpp b/wpilibc/src/main/native/cpp/DoubleSolenoid.cpp index 815e3a076e..b0d7cbfd6a 100644 --- a/wpilibc/src/main/native/cpp/DoubleSolenoid.cpp +++ b/wpilibc/src/main/native/cpp/DoubleSolenoid.cpp @@ -11,8 +11,8 @@ #include #include +#include "frc/Errors.h" #include "frc/SensorUtil.h" -#include "frc/WPIErrors.h" #include "frc/smartdashboard/SendableBuilder.h" #include "frc/smartdashboard/SendableRegistry.h" @@ -28,42 +28,31 @@ DoubleSolenoid::DoubleSolenoid(int moduleNumber, int forwardChannel, m_forwardChannel(forwardChannel), m_reverseChannel(reverseChannel) { if (!SensorUtil::CheckSolenoidModule(m_moduleNumber)) { - wpi_setWPIErrorWithContext(ModuleIndexOutOfRange, - "Solenoid Module " + wpi::Twine(m_moduleNumber)); - return; + throw FRC_MakeError(err::ModuleIndexOutOfRange, + "Solenoid Module " + wpi::Twine{m_moduleNumber}); } if (!SensorUtil::CheckSolenoidChannel(m_forwardChannel)) { - wpi_setWPIErrorWithContext( - ChannelIndexOutOfRange, - "Solenoid Channel " + wpi::Twine(m_forwardChannel)); - return; + throw FRC_MakeError(err::ChannelIndexOutOfRange, + "Solenoid Channel " + wpi::Twine{m_forwardChannel}); } if (!SensorUtil::CheckSolenoidChannel(m_reverseChannel)) { - wpi_setWPIErrorWithContext( - ChannelIndexOutOfRange, - "Solenoid Channel " + wpi::Twine(m_reverseChannel)); - return; + throw FRC_MakeError(err::ChannelIndexOutOfRange, + "Solenoid Channel " + wpi::Twine{m_reverseChannel}); } int32_t status = 0; m_forwardHandle = HAL_InitializeSolenoidPort( HAL_GetPortWithModule(moduleNumber, m_forwardChannel), &status); - if (status != 0) { - wpi_setHALErrorWithRange(status, 0, HAL_GetNumSolenoidChannels(), - forwardChannel); - m_forwardHandle = HAL_kInvalidHandle; - m_reverseHandle = HAL_kInvalidHandle; - return; - } + FRC_CheckErrorStatus(status, "Solenoid Module " + wpi::Twine{m_moduleNumber} + + " Channel " + wpi::Twine{m_forwardChannel}); m_reverseHandle = HAL_InitializeSolenoidPort( HAL_GetPortWithModule(moduleNumber, m_reverseChannel), &status); if (status != 0) { - wpi_setHALErrorWithRange(status, 0, HAL_GetNumSolenoidChannels(), - reverseChannel); // free forward solenoid HAL_FreeSolenoidPort(m_forwardHandle); - m_forwardHandle = HAL_kInvalidHandle; - m_reverseHandle = HAL_kInvalidHandle; + FRC_CheckErrorStatus(status, "Solenoid Module " + + wpi::Twine{m_moduleNumber} + " Channel " + + wpi::Twine{m_reverseChannel}); return; } @@ -85,10 +74,6 @@ DoubleSolenoid::~DoubleSolenoid() { } void DoubleSolenoid::Set(Value value) { - if (StatusIsFatal()) { - return; - } - bool forward = false; bool reverse = false; @@ -112,22 +97,26 @@ void DoubleSolenoid::Set(Value value) { int rstatus = 0; HAL_SetSolenoid(m_reverseHandle, reverse, &rstatus); - wpi_setHALError(fstatus); - wpi_setHALError(rstatus); + FRC_CheckErrorStatus(fstatus, "Solenoid Module " + + wpi::Twine{m_moduleNumber} + " Channel " + + wpi::Twine{m_forwardChannel}); + FRC_CheckErrorStatus(rstatus, "Solenoid Module " + + wpi::Twine{m_moduleNumber} + " Channel " + + wpi::Twine{m_reverseChannel}); } DoubleSolenoid::Value DoubleSolenoid::Get() const { - if (StatusIsFatal()) { - return kOff; - } - int fstatus = 0; int rstatus = 0; bool valueForward = HAL_GetSolenoid(m_forwardHandle, &fstatus); bool valueReverse = HAL_GetSolenoid(m_reverseHandle, &rstatus); - wpi_setHALError(fstatus); - wpi_setHALError(rstatus); + FRC_CheckErrorStatus(fstatus, "Solenoid Module " + + wpi::Twine{m_moduleNumber} + " Channel " + + wpi::Twine{m_forwardChannel}); + FRC_CheckErrorStatus(rstatus, "Solenoid Module " + + wpi::Twine{m_moduleNumber} + " Channel " + + wpi::Twine{m_reverseChannel}); if (valueForward) { return kForward; diff --git a/wpilibc/src/main/native/cpp/DriverStation.cpp b/wpilibc/src/main/native/cpp/DriverStation.cpp index 2cce5f6c53..1e1f77397f 100644 --- a/wpilibc/src/main/native/cpp/DriverStation.cpp +++ b/wpilibc/src/main/native/cpp/DriverStation.cpp @@ -17,9 +17,9 @@ #include #include +#include "frc/Errors.h" #include "frc/MotorSafety.h" #include "frc/Timer.h" -#include "frc/WPIErrors.h" namespace frc { // A simple class which caches the previous value written to an NT entry @@ -140,7 +140,8 @@ void DriverStation::ReportError(bool isError, int32_t code, bool DriverStation::GetStickButton(int stick, int button) { if (stick < 0 || stick >= kJoystickPorts) { - wpi_setWPIError(BadJoystickIndex); + FRC_ReportError(warn::BadJoystickIndex, + "stick " + wpi::Twine{stick} + " out of range"); return false; } if (button <= 0) { @@ -163,7 +164,8 @@ bool DriverStation::GetStickButton(int stick, int button) { bool DriverStation::GetStickButtonPressed(int stick, int button) { if (stick < 0 || stick >= kJoystickPorts) { - wpi_setWPIError(BadJoystickIndex); + FRC_ReportError(warn::BadJoystickIndex, + "stick " + wpi::Twine{stick} + " out of range"); return false; } if (button <= 0) { @@ -192,7 +194,8 @@ bool DriverStation::GetStickButtonPressed(int stick, int button) { bool DriverStation::GetStickButtonReleased(int stick, int button) { if (stick < 0 || stick >= kJoystickPorts) { - wpi_setWPIError(BadJoystickIndex); + FRC_ReportError(warn::BadJoystickIndex, + "stick " + wpi::Twine{stick} + " out of range"); return false; } if (button <= 0) { @@ -221,11 +224,13 @@ bool DriverStation::GetStickButtonReleased(int stick, int button) { double DriverStation::GetStickAxis(int stick, int axis) { if (stick < 0 || stick >= kJoystickPorts) { - wpi_setWPIError(BadJoystickIndex); + FRC_ReportError(warn::BadJoystickIndex, + "stick " + wpi::Twine{stick} + " out of range"); return 0.0; } if (axis < 0 || axis >= HAL_kMaxJoystickAxes) { - wpi_setWPIError(BadJoystickAxis); + FRC_ReportError(warn::BadJoystickAxis, + "axis " + wpi::Twine{axis} + " out of range"); return 0.0; } @@ -243,11 +248,13 @@ double DriverStation::GetStickAxis(int stick, int axis) { int DriverStation::GetStickPOV(int stick, int pov) { if (stick < 0 || stick >= kJoystickPorts) { - wpi_setWPIError(BadJoystickIndex); + FRC_ReportError(warn::BadJoystickIndex, + "stick " + wpi::Twine{stick} + " out of range"); return -1; } if (pov < 0 || pov >= HAL_kMaxJoystickPOVs) { - wpi_setWPIError(BadJoystickAxis); + FRC_ReportError(warn::BadJoystickAxis, + "POV " + wpi::Twine{pov} + " out of range"); return -1; } @@ -265,7 +272,8 @@ int DriverStation::GetStickPOV(int stick, int pov) { int DriverStation::GetStickButtons(int stick) const { if (stick < 0 || stick >= kJoystickPorts) { - wpi_setWPIError(BadJoystickIndex); + FRC_ReportError(warn::BadJoystickIndex, + "stick " + wpi::Twine{stick} + " out of range"); return 0; } @@ -277,7 +285,8 @@ int DriverStation::GetStickButtons(int stick) const { int DriverStation::GetStickAxisCount(int stick) const { if (stick < 0 || stick >= kJoystickPorts) { - wpi_setWPIError(BadJoystickIndex); + FRC_ReportError(warn::BadJoystickIndex, + "stick " + wpi::Twine{stick} + " out of range"); return 0; } @@ -289,7 +298,8 @@ int DriverStation::GetStickAxisCount(int stick) const { int DriverStation::GetStickPOVCount(int stick) const { if (stick < 0 || stick >= kJoystickPorts) { - wpi_setWPIError(BadJoystickIndex); + FRC_ReportError(warn::BadJoystickIndex, + "stick " + wpi::Twine{stick} + " out of range"); return 0; } @@ -301,7 +311,8 @@ int DriverStation::GetStickPOVCount(int stick) const { int DriverStation::GetStickButtonCount(int stick) const { if (stick < 0 || stick >= kJoystickPorts) { - wpi_setWPIError(BadJoystickIndex); + FRC_ReportError(warn::BadJoystickIndex, + "stick " + wpi::Twine{stick} + " out of range"); return 0; } @@ -313,7 +324,8 @@ int DriverStation::GetStickButtonCount(int stick) const { bool DriverStation::GetJoystickIsXbox(int stick) const { if (stick < 0 || stick >= kJoystickPorts) { - wpi_setWPIError(BadJoystickIndex); + FRC_ReportError(warn::BadJoystickIndex, + "stick " + wpi::Twine{stick} + " out of range"); return false; } @@ -325,7 +337,8 @@ bool DriverStation::GetJoystickIsXbox(int stick) const { int DriverStation::GetJoystickType(int stick) const { if (stick < 0 || stick >= kJoystickPorts) { - wpi_setWPIError(BadJoystickIndex); + FRC_ReportError(warn::BadJoystickIndex, + "stick " + wpi::Twine{stick} + " out of range"); return -1; } @@ -337,7 +350,8 @@ int DriverStation::GetJoystickType(int stick) const { std::string DriverStation::GetJoystickName(int stick) const { if (stick < 0 || stick >= kJoystickPorts) { - wpi_setWPIError(BadJoystickIndex); + FRC_ReportError(warn::BadJoystickIndex, + "stick " + wpi::Twine{stick} + " out of range"); } HAL_JoystickDescriptor descriptor; @@ -348,7 +362,8 @@ std::string DriverStation::GetJoystickName(int stick) const { int DriverStation::GetJoystickAxisType(int stick, int axis) const { if (stick < 0 || stick >= kJoystickPorts) { - wpi_setWPIError(BadJoystickIndex); + FRC_ReportError(warn::BadJoystickIndex, + "stick " + wpi::Twine{stick} + " out of range"); return -1; } @@ -537,7 +552,7 @@ double DriverStation::GetMatchTime() const { double DriverStation::GetBatteryVoltage() const { int32_t status = 0; double voltage = HAL_GetVinVoltage(&status); - wpi_setErrorWithContext(status, "getVinVoltage"); + FRC_CheckErrorStatus(status, "getVinVoltage"); return voltage; } diff --git a/wpilibc/src/main/native/cpp/DutyCycle.cpp b/wpilibc/src/main/native/cpp/DutyCycle.cpp index 6ffcd9713d..f4dbe14085 100644 --- a/wpilibc/src/main/native/cpp/DutyCycle.cpp +++ b/wpilibc/src/main/native/cpp/DutyCycle.cpp @@ -9,18 +9,17 @@ #include "frc/Base.h" #include "frc/DigitalSource.h" -#include "frc/WPIErrors.h" +#include "frc/Errors.h" #include "frc/smartdashboard/SendableBuilder.h" using namespace frc; DutyCycle::DutyCycle(DigitalSource* source) : m_source{source, NullDeleter()} { - if (m_source == nullptr) { - wpi_setWPIError(NullParameter); - } else { - InitDutyCycle(); + if (!m_source) { + throw FRC_MakeError(err::NullParameter, "source"); } + InitDutyCycle(); } DutyCycle::DutyCycle(DigitalSource& source) @@ -30,11 +29,10 @@ DutyCycle::DutyCycle(DigitalSource& source) DutyCycle::DutyCycle(std::shared_ptr source) : m_source{std::move(source)} { - if (m_source == nullptr) { - wpi_setWPIError(NullParameter); - } else { - InitDutyCycle(); + if (!m_source) { + throw FRC_MakeError(err::NullParameter, "source"); } + InitDutyCycle(); } DutyCycle::~DutyCycle() { @@ -48,7 +46,7 @@ void DutyCycle::InitDutyCycle() { static_cast( m_source->GetAnalogTriggerTypeForRouting()), &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "InitDutyCycle"); int index = GetFPGAIndex(); HAL_Report(HALUsageReporting::kResourceType_DutyCycle, index + 1); SendableRegistry::GetInstance().AddLW(this, "Duty Cycle", index); @@ -57,35 +55,35 @@ void DutyCycle::InitDutyCycle() { int DutyCycle::GetFPGAIndex() const { int32_t status = 0; auto retVal = HAL_GetDutyCycleFPGAIndex(m_handle, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "GetFPGAIndex"); return retVal; } int DutyCycle::GetFrequency() const { int32_t status = 0; auto retVal = HAL_GetDutyCycleFrequency(m_handle, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "GetFrequency"); return retVal; } double DutyCycle::GetOutput() const { int32_t status = 0; auto retVal = HAL_GetDutyCycleOutput(m_handle, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "GetOutput"); return retVal; } unsigned int DutyCycle::GetOutputRaw() const { int32_t status = 0; auto retVal = HAL_GetDutyCycleOutputRaw(m_handle, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "GetOutputRaw"); return retVal; } unsigned int DutyCycle::GetOutputScaleFactor() const { int32_t status = 0; auto retVal = HAL_GetDutyCycleOutputScaleFactor(m_handle, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "GetOutputScaleFactor"); return retVal; } diff --git a/wpilibc/src/main/native/cpp/Encoder.cpp b/wpilibc/src/main/native/cpp/Encoder.cpp index 2d7373940b..ce3f5eb47a 100644 --- a/wpilibc/src/main/native/cpp/Encoder.cpp +++ b/wpilibc/src/main/native/cpp/Encoder.cpp @@ -11,7 +11,7 @@ #include "frc/Base.h" #include "frc/DigitalInput.h" -#include "frc/WPIErrors.h" +#include "frc/Errors.h" #include "frc/smartdashboard/SendableBuilder.h" #include "frc/smartdashboard/SendableRegistry.h" @@ -31,11 +31,13 @@ Encoder::Encoder(DigitalSource* aSource, DigitalSource* bSource, bool reverseDirection, EncodingType encodingType) : m_aSource(aSource, NullDeleter()), m_bSource(bSource, NullDeleter()) { - if (m_aSource == nullptr || m_bSource == nullptr) { - wpi_setWPIError(NullParameter); - } else { - InitEncoder(reverseDirection, encodingType); + if (!m_aSource) { + throw FRC_MakeError(err::NullParameter, "aSource"); } + if (!m_bSource) { + throw FRC_MakeError(err::NullParameter, "bSource"); + } + InitEncoder(reverseDirection, encodingType); } Encoder::Encoder(DigitalSource& aSource, DigitalSource& bSource, @@ -49,167 +51,130 @@ Encoder::Encoder(std::shared_ptr aSource, std::shared_ptr bSource, bool reverseDirection, EncodingType encodingType) : m_aSource(std::move(aSource)), m_bSource(std::move(bSource)) { - if (m_aSource == nullptr || m_bSource == nullptr) { - wpi_setWPIError(NullParameter); - } else { - InitEncoder(reverseDirection, encodingType); + if (!m_aSource) { + throw FRC_MakeError(err::NullParameter, "aSource"); } + if (!m_bSource) { + throw FRC_MakeError(err::NullParameter, "bSource"); + } + InitEncoder(reverseDirection, encodingType); } Encoder::~Encoder() { int32_t status = 0; HAL_FreeEncoder(m_encoder, &status); - wpi_setHALError(status); + FRC_ReportError(status, "FreeEncoder"); } int Encoder::Get() const { - if (StatusIsFatal()) { - return 0; - } int32_t status = 0; int value = HAL_GetEncoder(m_encoder, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "Get"); return value; } void Encoder::Reset() { - if (StatusIsFatal()) { - return; - } int32_t status = 0; HAL_ResetEncoder(m_encoder, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "Reset"); } double Encoder::GetPeriod() const { - if (StatusIsFatal()) { - return 0.0; - } int32_t status = 0; double value = HAL_GetEncoderPeriod(m_encoder, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "GetPeriod"); return value; } void Encoder::SetMaxPeriod(double maxPeriod) { - if (StatusIsFatal()) { - return; - } int32_t status = 0; HAL_SetEncoderMaxPeriod(m_encoder, maxPeriod, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "SetMaxPeriod"); } bool Encoder::GetStopped() const { - if (StatusIsFatal()) { - return true; - } int32_t status = 0; bool value = HAL_GetEncoderStopped(m_encoder, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "GetStopped"); return value; } bool Encoder::GetDirection() const { - if (StatusIsFatal()) { - return false; - } int32_t status = 0; bool value = HAL_GetEncoderDirection(m_encoder, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "GetDirection"); return value; } int Encoder::GetRaw() const { - if (StatusIsFatal()) { - return 0; - } int32_t status = 0; int value = HAL_GetEncoderRaw(m_encoder, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "GetRaw"); return value; } int Encoder::GetEncodingScale() const { int32_t status = 0; int val = HAL_GetEncoderEncodingScale(m_encoder, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "GetEncodingScale"); return val; } double Encoder::GetDistance() const { - if (StatusIsFatal()) { - return 0.0; - } int32_t status = 0; double value = HAL_GetEncoderDistance(m_encoder, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "GetDistance"); return value; } double Encoder::GetRate() const { - if (StatusIsFatal()) { - return 0.0; - } int32_t status = 0; double value = HAL_GetEncoderRate(m_encoder, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "GetRate"); return value; } void Encoder::SetMinRate(double minRate) { - if (StatusIsFatal()) { - return; - } int32_t status = 0; HAL_SetEncoderMinRate(m_encoder, minRate, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "SetMinRate"); } void Encoder::SetDistancePerPulse(double distancePerPulse) { - if (StatusIsFatal()) { - return; - } int32_t status = 0; HAL_SetEncoderDistancePerPulse(m_encoder, distancePerPulse, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "SetDistancePerPulse"); } double Encoder::GetDistancePerPulse() const { - if (StatusIsFatal()) { - return 0.0; - } int32_t status = 0; double distancePerPulse = HAL_GetEncoderDistancePerPulse(m_encoder, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "GetDistancePerPulse"); return distancePerPulse; } void Encoder::SetReverseDirection(bool reverseDirection) { - if (StatusIsFatal()) { - return; - } int32_t status = 0; HAL_SetEncoderReverseDirection(m_encoder, reverseDirection, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "SetReverseDirection"); } void Encoder::SetSamplesToAverage(int samplesToAverage) { if (samplesToAverage < 1 || samplesToAverage > 127) { - wpi_setWPIErrorWithContext( - ParameterOutOfRange, - "Average counter values must be between 1 and 127"); - return; + throw FRC_MakeError( + err::ParameterOutOfRange, + "Average counter values must be between 1 and 127, got " + + wpi::Twine{samplesToAverage}); } int32_t status = 0; HAL_SetEncoderSamplesToAverage(m_encoder, samplesToAverage, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "SetSamplesToAverage"); } int Encoder::GetSamplesToAverage() const { int32_t status = 0; int result = HAL_GetEncoderSamplesToAverage(m_encoder, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "GetSamplesToAverage"); return result; } @@ -228,7 +193,7 @@ void Encoder::SetIndexSource(const DigitalSource& source, source.GetAnalogTriggerTypeForRouting()), static_cast(type), &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "SetIndexSource"); } void Encoder::SetSimDevice(HAL_SimDeviceHandle device) { @@ -238,14 +203,14 @@ void Encoder::SetSimDevice(HAL_SimDeviceHandle device) { int Encoder::GetFPGAIndex() const { int32_t status = 0; int val = HAL_GetEncoderFPGAIndex(m_encoder, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "GetFPGAIndex"); return val; } void Encoder::InitSendable(SendableBuilder& builder) { int32_t status = 0; HAL_EncoderEncodingType type = HAL_GetEncoderEncodingType(m_encoder, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "GetEncodingType"); if (type == HAL_EncoderEncodingType::HAL_Encoder_k4X) { builder.SetSmartDashboardType("Quadrature Encoder"); } else { @@ -271,7 +236,7 @@ void Encoder::InitEncoder(bool reverseDirection, EncodingType encodingType) { m_bSource->GetAnalogTriggerTypeForRouting()), reverseDirection, static_cast(encodingType), &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "InitEncoder"); HAL_Report(HALUsageReporting::kResourceType_Encoder, GetFPGAIndex() + 1, encodingType); @@ -280,11 +245,8 @@ void Encoder::InitEncoder(bool reverseDirection, EncodingType encodingType) { } double Encoder::DecodingScaleFactor() const { - if (StatusIsFatal()) { - return 0.0; - } int32_t status = 0; double val = HAL_GetEncoderDecodingScaleFactor(m_encoder, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "DecodingScaleFactor"); return val; } diff --git a/wpilibc/src/main/native/cpp/Error.cpp b/wpilibc/src/main/native/cpp/Error.cpp deleted file mode 100644 index 383bde3830..0000000000 --- a/wpilibc/src/main/native/cpp/Error.cpp +++ /dev/null @@ -1,108 +0,0 @@ -// Copyright (c) FIRST and other WPILib contributors. -// Open Source Software; you can modify and/or share it under the terms of -// the WPILib BSD license file in the root directory of this project. - -#include "frc/Error.h" - -#include -#include - -#include "frc/Base.h" -#include "frc/DriverStation.h" -#include "frc/Timer.h" - -using namespace frc; - -Error::Error(Code code, const wpi::Twine& contextMessage, - wpi::StringRef filename, wpi::StringRef function, int lineNumber, - const ErrorBase* originatingObject) { - Set(code, contextMessage, filename, function, lineNumber, originatingObject); -} - -bool Error::operator<(const Error& rhs) const { - if (m_code < rhs.m_code) { - return true; - } else if (m_message < rhs.m_message) { - return true; - } else if (m_filename < rhs.m_filename) { - return true; - } else if (m_function < rhs.m_function) { - return true; - } else if (m_lineNumber < rhs.m_lineNumber) { - return true; - } else if (m_originatingObject < rhs.m_originatingObject) { - return true; - } else if (m_timestamp < rhs.m_timestamp) { - return true; - } else { - return false; - } -} - -Error::Code Error::GetCode() const { - return m_code; -} - -std::string Error::GetMessage() const { - return m_message; -} - -std::string Error::GetFilename() const { - return m_filename; -} - -std::string Error::GetFunction() const { - return m_function; -} - -int Error::GetLineNumber() const { - return m_lineNumber; -} - -const ErrorBase* Error::GetOriginatingObject() const { - return m_originatingObject; -} - -double Error::GetTimestamp() const { - return m_timestamp; -} - -void Error::Set(Code code, const wpi::Twine& contextMessage, - wpi::StringRef filename, wpi::StringRef function, - int lineNumber, const ErrorBase* originatingObject) { - bool report = true; - - if (code == m_code && GetTime() - m_timestamp < 1) { - report = false; - } - - m_code = code; - m_message = contextMessage.str(); - m_filename = filename; - m_function = function; - m_lineNumber = lineNumber; - m_originatingObject = originatingObject; - - if (report) { - m_timestamp = GetTime(); - Report(); - } -} - -void Error::Report() { - DriverStation::ReportError( - true, m_code, m_message, - m_function + wpi::Twine(" [") + wpi::sys::path::filename(m_filename) + - wpi::Twine(':') + wpi::Twine(m_lineNumber) + wpi::Twine(']'), - wpi::GetStackTrace(4)); -} - -void Error::Clear() { - m_code = 0; - m_message = ""; - m_filename = ""; - m_function = ""; - m_lineNumber = 0; - m_originatingObject = nullptr; - m_timestamp = 0.0; -} diff --git a/wpilibc/src/main/native/cpp/ErrorBase.cpp b/wpilibc/src/main/native/cpp/ErrorBase.cpp deleted file mode 100644 index e6c3c9c241..0000000000 --- a/wpilibc/src/main/native/cpp/ErrorBase.cpp +++ /dev/null @@ -1,198 +0,0 @@ -// Copyright (c) FIRST and other WPILib contributors. -// Open Source Software; you can modify and/or share it under the terms of -// the WPILib BSD license file in the root directory of this project. - -#include "frc/ErrorBase.h" - -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include "frc/Base.h" - -using namespace frc; - -namespace { -struct GlobalErrors { - wpi::mutex mutex; - std::set errors; - const Error* lastError{nullptr}; - - static GlobalErrors& GetInstance(); - static void Insert(const Error& error); - static void Insert(Error&& error); -}; -} // namespace - -GlobalErrors& GlobalErrors::GetInstance() { - static GlobalErrors inst; - return inst; -} - -void GlobalErrors::Insert(const Error& error) { - GlobalErrors& inst = GetInstance(); - std::scoped_lock lock(inst.mutex); - inst.lastError = &(*inst.errors.insert(error).first); -} - -void GlobalErrors::Insert(Error&& error) { - GlobalErrors& inst = GetInstance(); - std::scoped_lock lock(inst.mutex); - inst.lastError = &(*inst.errors.insert(std::move(error)).first); -} - -ErrorBase::ErrorBase() { - HAL_Initialize(500, 0); -} - -Error& ErrorBase::GetError() { - return m_error; -} - -const Error& ErrorBase::GetError() const { - return m_error; -} - -void ErrorBase::ClearError() const { - m_error.Clear(); -} - -void ErrorBase::SetErrnoError(const wpi::Twine& contextMessage, - wpi::StringRef filename, wpi::StringRef function, - int lineNumber) const { - wpi::SmallString<128> buf; - wpi::raw_svector_ostream err(buf); - int errNo = errno; - if (errNo == 0) { - err << "OK: "; - } else { - err << std::strerror(errNo) << " (" << wpi::format_hex(errNo, 10, true) - << "): "; - } - - // Set the current error information for this object. - m_error.Set(-1, err.str() + contextMessage, filename, function, lineNumber, - this); - - // Update the global error if there is not one already set. - GlobalErrors::Insert(m_error); -} - -void ErrorBase::SetImaqError(int success, const wpi::Twine& contextMessage, - wpi::StringRef filename, wpi::StringRef function, - int lineNumber) const { - // If there was an error - if (success <= 0) { - // Set the current error information for this object. - m_error.Set(success, wpi::Twine(success) + ": " + contextMessage, filename, - function, lineNumber, this); - - // Update the global error if there is not one already set. - GlobalErrors::Insert(m_error); - } -} - -void ErrorBase::SetError(Error::Code code, const wpi::Twine& contextMessage, - wpi::StringRef filename, wpi::StringRef function, - int lineNumber) const { - // If there was an error - if (code != 0) { - // Set the current error information for this object. - m_error.Set(code, contextMessage, filename, function, lineNumber, this); - - // Update the global error if there is not one already set. - GlobalErrors::Insert(m_error); - } -} - -void ErrorBase::SetErrorRange(Error::Code code, int32_t minRange, - int32_t maxRange, int32_t requestedValue, - const wpi::Twine& contextMessage, - wpi::StringRef filename, wpi::StringRef function, - int lineNumber) const { - // If there was an error - if (code != 0) { - // Set the current error information for this object. - m_error.Set(code, - contextMessage + ", Minimum Value: " + wpi::Twine(minRange) + - ", MaximumValue: " + wpi::Twine(maxRange) + - ", Requested Value: " + wpi::Twine(requestedValue), - filename, function, lineNumber, this); - - // Update the global error if there is not one already set. - GlobalErrors::Insert(m_error); - } -} - -void ErrorBase::SetWPIError(const wpi::Twine& errorMessage, Error::Code code, - const wpi::Twine& contextMessage, - wpi::StringRef filename, wpi::StringRef function, - int lineNumber) const { - // Set the current error information for this object. - m_error.Set(code, errorMessage + ": " + contextMessage, filename, function, - lineNumber, this); - - // Update the global error if there is not one already set. - GlobalErrors::Insert(m_error); -} - -void ErrorBase::CloneError(const ErrorBase& rhs) const { - m_error = rhs.GetError(); -} - -bool ErrorBase::StatusIsFatal() const { - return m_error.GetCode() < 0; -} - -void ErrorBase::SetGlobalError(Error::Code code, - const wpi::Twine& contextMessage, - wpi::StringRef filename, wpi::StringRef function, - int lineNumber) { - // If there was an error - if (code != 0) { - // Set the current error information for this object. - GlobalErrors::Insert( - Error(code, contextMessage, filename, function, lineNumber, nullptr)); - } -} - -void ErrorBase::SetGlobalWPIError(const wpi::Twine& errorMessage, - const wpi::Twine& contextMessage, - wpi::StringRef filename, - wpi::StringRef function, int lineNumber) { - GlobalErrors::Insert(Error(-1, errorMessage + ": " + contextMessage, filename, - function, lineNumber, nullptr)); -} - -Error ErrorBase::GetGlobalError() { - auto& inst = GlobalErrors::GetInstance(); - std::scoped_lock mutex(inst.mutex); - if (!inst.lastError) { - return {}; - } - return *inst.lastError; -} - -std::vector ErrorBase::GetGlobalErrors() { - auto& inst = GlobalErrors::GetInstance(); - std::scoped_lock mutex(inst.mutex); - std::vector rv; - for (auto&& error : inst.errors) { - rv.push_back(error); - } - return rv; -} - -void ErrorBase::ClearGlobalErrors() { - auto& inst = GlobalErrors::GetInstance(); - std::scoped_lock mutex(inst.mutex); - inst.errors.clear(); - inst.lastError = nullptr; -} diff --git a/wpilibc/src/main/native/cpp/Errors.cpp b/wpilibc/src/main/native/cpp/Errors.cpp new file mode 100644 index 0000000000..56c55922a4 --- /dev/null +++ b/wpilibc/src/main/native/cpp/Errors.cpp @@ -0,0 +1,78 @@ +// Copyright (c) FIRST and other WPILib contributors. +// Open Source Software; you can modify and/or share it under the terms of +// the WPILib BSD license file in the root directory of this project. + +#include "frc/Errors.h" + +#include + +#include +#include +#include +#include +#include + +using namespace frc; + +RuntimeError::RuntimeError(int32_t code, const wpi::Twine& message, + const wpi::Twine& loc, wpi::StringRef stack) + : runtime_error{message.str()}, m_data{std::make_shared()} { + m_data->code = code; + m_data->loc = loc.str(); + m_data->stack = stack; +} + +RuntimeError::RuntimeError(int32_t code, const wpi::Twine& message, + const char* fileName, int lineNumber, + const char* funcName, wpi::StringRef stack) + : RuntimeError{code, message, + funcName + wpi::Twine{" ["} + + wpi::sys::path::filename(fileName) + ":" + + wpi::Twine{lineNumber} + "]", + stack} {} + +void RuntimeError::Report() const { + HAL_SendError(m_data->code < 0, m_data->code, 0, what(), m_data->loc.c_str(), + m_data->stack.c_str(), 1); +} + +const char* frc::GetErrorMessage(int32_t code) { + using namespace err; + using namespace warn; + switch (code) { +#define S(label, offset, message) \ + case label: \ + return message; +#include "frc/WPIErrors.mac" +#include "frc/WPIWarnings.mac" +#undef S + default: + return HAL_GetErrorMessage(code); + } +} + +void frc::ReportError(int32_t status, const wpi::Twine& message, + const char* fileName, int lineNumber, + const char* funcName) { + if (status == 0) { + return; + } + const char* statusMessage = GetErrorMessage(status); + auto stack = wpi::GetStackTrace(2); + wpi::SmallString<128> buf; + HAL_SendError(status < 0, status, 0, + (statusMessage + wpi::Twine{": "} + message) + .toNullTerminatedStringRef(buf) + .data(), + funcName, stack.c_str(), 1); +} + +RuntimeError frc::MakeError(int32_t status, const wpi::Twine& message, + const char* fileName, int lineNumber, + const char* funcName) { + const char* statusMessage = GetErrorMessage(status); + auto stack = wpi::GetStackTrace(2); + return RuntimeError{status, statusMessage + wpi::Twine{": "} + message, + fileName, lineNumber, + funcName, stack}; +} diff --git a/wpilibc/src/main/native/cpp/GenericHID.cpp b/wpilibc/src/main/native/cpp/GenericHID.cpp index d606b372f8..423fb351ef 100644 --- a/wpilibc/src/main/native/cpp/GenericHID.cpp +++ b/wpilibc/src/main/native/cpp/GenericHID.cpp @@ -7,13 +7,14 @@ #include #include "frc/DriverStation.h" -#include "frc/WPIErrors.h" +#include "frc/Errors.h" using namespace frc; GenericHID::GenericHID(int port) : m_ds(&DriverStation::GetInstance()) { - if (port >= DriverStation::kJoystickPorts) { - wpi_setWPIError(BadJoystickIndex); + if (port < 0 || port >= DriverStation::kJoystickPorts) { + throw FRC_MakeError(warn::BadJoystickIndex, + "port " + wpi::Twine{port} + "out of range"); } m_port = port; } diff --git a/wpilibc/src/main/native/cpp/I2C.cpp b/wpilibc/src/main/native/cpp/I2C.cpp index df44730801..439471efd4 100644 --- a/wpilibc/src/main/native/cpp/I2C.cpp +++ b/wpilibc/src/main/native/cpp/I2C.cpp @@ -9,7 +9,7 @@ #include #include -#include "frc/WPIErrors.h" +#include "frc/Errors.h" using namespace frc; @@ -17,7 +17,7 @@ I2C::I2C(Port port, int deviceAddress) : m_port(static_cast(port)), m_deviceAddress(deviceAddress) { int32_t status = 0; HAL_InitializeI2C(m_port, &status); - // wpi_setHALError(status); + FRC_CheckErrorStatus(status, "Port " + wpi::Twine{static_cast(port)}); HAL_Report(HALUsageReporting::kResourceType_I2C, deviceAddress); } @@ -31,7 +31,6 @@ bool I2C::Transaction(uint8_t* dataToSend, int sendSize, uint8_t* dataReceived, int32_t status = 0; status = HAL_TransactionI2C(m_port, m_deviceAddress, dataToSend, sendSize, dataReceived, receiveSize); - // wpi_setHALError(status); return status < 0; } @@ -56,12 +55,10 @@ bool I2C::WriteBulk(uint8_t* data, int count) { bool I2C::Read(int registerAddress, int count, uint8_t* buffer) { if (count < 1) { - wpi_setWPIErrorWithContext(ParameterOutOfRange, "count"); - return true; + throw FRC_MakeError(err::ParameterOutOfRange, "count " + wpi::Twine{count}); } - if (buffer == nullptr) { - wpi_setWPIErrorWithContext(NullParameter, "buffer"); - return true; + if (!buffer) { + throw FRC_MakeError(err::NullParameter, "buffer"); } uint8_t regAddr = registerAddress; return Transaction(®Addr, sizeof(regAddr), buffer, count); @@ -69,12 +66,10 @@ bool I2C::Read(int registerAddress, int count, uint8_t* buffer) { bool I2C::ReadOnly(int count, uint8_t* buffer) { if (count < 1) { - wpi_setWPIErrorWithContext(ParameterOutOfRange, "count"); - return true; + throw FRC_MakeError(err::ParameterOutOfRange, "count " + wpi::Twine{count}); } - if (buffer == nullptr) { - wpi_setWPIErrorWithContext(NullParameter, "buffer"); - return true; + if (!buffer) { + throw FRC_MakeError(err::NullParameter, "buffer"); } return HAL_ReadI2C(m_port, m_deviceAddress, buffer, count) < 0; } diff --git a/wpilibc/src/main/native/cpp/InterruptableSensorBase.cpp b/wpilibc/src/main/native/cpp/InterruptableSensorBase.cpp index 1dda99dcc5..334fd3c1d3 100644 --- a/wpilibc/src/main/native/cpp/InterruptableSensorBase.cpp +++ b/wpilibc/src/main/native/cpp/InterruptableSensorBase.cpp @@ -4,8 +4,8 @@ #include "frc/InterruptableSensorBase.h" +#include "frc/Errors.h" #include "frc/Utility.h" -#include "frc/WPIErrors.h" using namespace frc; @@ -20,15 +20,8 @@ InterruptableSensorBase::~InterruptableSensorBase() { void InterruptableSensorBase::RequestInterrupts( HAL_InterruptHandlerFunction handler, void* param) { - if (StatusIsFatal()) { - return; - } - - wpi_assert(m_interrupt == HAL_kInvalidHandle); + FRC_Assert(m_interrupt == HAL_kInvalidHandle); AllocateInterrupts(false); - if (StatusIsFatal()) { - return; // if allocate failed, out of interrupts - } int32_t status = 0; HAL_RequestInterrupts( @@ -37,19 +30,12 @@ void InterruptableSensorBase::RequestInterrupts( &status); SetUpSourceEdge(true, false); HAL_AttachInterruptHandler(m_interrupt, handler, param, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "AttachInterruptHandler"); } void InterruptableSensorBase::RequestInterrupts(InterruptEventHandler handler) { - if (StatusIsFatal()) { - return; - } - - wpi_assert(m_interrupt == HAL_kInvalidHandle); + FRC_Assert(m_interrupt == HAL_kInvalidHandle); AllocateInterrupts(false); - if (StatusIsFatal()) { - return; // if allocate failed, out of interrupts - } m_interruptHandler = std::make_unique(std::move(handler)); @@ -74,34 +60,24 @@ void InterruptableSensorBase::RequestInterrupts(InterruptEventHandler handler) { (*self)(res); }, m_interruptHandler.get(), &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "AttachInterruptHandler"); } void InterruptableSensorBase::RequestInterrupts() { - if (StatusIsFatal()) { - return; - } - - wpi_assert(m_interrupt == HAL_kInvalidHandle); + FRC_Assert(m_interrupt == HAL_kInvalidHandle); AllocateInterrupts(true); - if (StatusIsFatal()) { - return; // if allocate failed, out of interrupts - } int32_t status = 0; HAL_RequestInterrupts( m_interrupt, GetPortHandleForRouting(), static_cast(GetAnalogTriggerTypeForRouting()), &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "RequestInterrupts"); SetUpSourceEdge(true, false); } void InterruptableSensorBase::CancelInterrupts() { - if (StatusIsFatal()) { - return; - } - wpi_assert(m_interrupt != HAL_kInvalidHandle); + FRC_Assert(m_interrupt != HAL_kInvalidHandle); int32_t status = 0; HAL_CleanInterrupts(m_interrupt, &status); // Ignore status, as an invalid handle just needs to be ignored. @@ -111,15 +87,12 @@ void InterruptableSensorBase::CancelInterrupts() { InterruptableSensorBase::WaitResult InterruptableSensorBase::WaitForInterrupt( double timeout, bool ignorePrevious) { - if (StatusIsFatal()) { - return InterruptableSensorBase::kTimeout; - } - wpi_assert(m_interrupt != HAL_kInvalidHandle); + FRC_Assert(m_interrupt != HAL_kInvalidHandle); int32_t status = 0; int result; result = HAL_WaitForInterrupt(m_interrupt, timeout, ignorePrevious, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "WaitForInterrupt"); // Rising edge result is the interrupt bit set in the byte 0xFF // Falling edge result is the interrupt bit set in the byte 0xFF00 @@ -131,69 +104,53 @@ InterruptableSensorBase::WaitResult InterruptableSensorBase::WaitForInterrupt( } void InterruptableSensorBase::EnableInterrupts() { - if (StatusIsFatal()) { - return; - } - wpi_assert(m_interrupt != HAL_kInvalidHandle); + FRC_Assert(m_interrupt != HAL_kInvalidHandle); int32_t status = 0; HAL_EnableInterrupts(m_interrupt, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "EnableInterrupts"); } void InterruptableSensorBase::DisableInterrupts() { - if (StatusIsFatal()) { - return; - } - wpi_assert(m_interrupt != HAL_kInvalidHandle); + FRC_Assert(m_interrupt != HAL_kInvalidHandle); int32_t status = 0; HAL_DisableInterrupts(m_interrupt, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "DisableInterrupts"); } double InterruptableSensorBase::ReadRisingTimestamp() { - if (StatusIsFatal()) { - return 0.0; - } - wpi_assert(m_interrupt != HAL_kInvalidHandle); + FRC_Assert(m_interrupt != HAL_kInvalidHandle); int32_t status = 0; int64_t timestamp = HAL_ReadInterruptRisingTimestamp(m_interrupt, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "ReadRisingTimestamp"); return timestamp * 1e-6; } double InterruptableSensorBase::ReadFallingTimestamp() { - if (StatusIsFatal()) { - return 0.0; - } - wpi_assert(m_interrupt != HAL_kInvalidHandle); + FRC_Assert(m_interrupt != HAL_kInvalidHandle); int32_t status = 0; int64_t timestamp = HAL_ReadInterruptFallingTimestamp(m_interrupt, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "ReadFallingTimestamp"); return timestamp * 1e-6; } void InterruptableSensorBase::SetUpSourceEdge(bool risingEdge, bool fallingEdge) { - if (StatusIsFatal()) { - return; - } if (m_interrupt == HAL_kInvalidHandle) { - wpi_setWPIErrorWithContext( - NullParameter, + throw FRC_MakeError( + err::NullParameter, "You must call RequestInterrupts before SetUpSourceEdge"); - return; } if (m_interrupt != HAL_kInvalidHandle) { int32_t status = 0; HAL_SetInterruptUpSourceEdge(m_interrupt, risingEdge, fallingEdge, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "SetUpSourceEdge"); } } void InterruptableSensorBase::AllocateInterrupts(bool watcher) { - wpi_assert(m_interrupt == HAL_kInvalidHandle); + FRC_Assert(m_interrupt == HAL_kInvalidHandle); // Expects the calling leaf class to allocate an interrupt index. int32_t status = 0; m_interrupt = HAL_InitializeInterrupts(watcher, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "AllocateInterrupts"); } diff --git a/wpilibc/src/main/native/cpp/MotorSafety.cpp b/wpilibc/src/main/native/cpp/MotorSafety.cpp index 1d0a5d3b21..ed76bf1240 100644 --- a/wpilibc/src/main/native/cpp/MotorSafety.cpp +++ b/wpilibc/src/main/native/cpp/MotorSafety.cpp @@ -12,7 +12,7 @@ #include #include "frc/DriverStation.h" -#include "frc/WPIErrors.h" +#include "frc/Errors.h" using namespace frc; @@ -30,16 +30,13 @@ MotorSafety::~MotorSafety() { } MotorSafety::MotorSafety(MotorSafety&& rhs) - : ErrorBase(std::move(rhs)), - m_expiration(std::move(rhs.m_expiration)), + : m_expiration(std::move(rhs.m_expiration)), m_enabled(std::move(rhs.m_enabled)), m_stopTime(std::move(rhs.m_stopTime)) {} MotorSafety& MotorSafety::operator=(MotorSafety&& rhs) { std::scoped_lock lock(m_thisMutex, rhs.m_thisMutex); - ErrorBase::operator=(std::move(rhs)); - m_expiration = std::move(rhs.m_expiration); m_enabled = std::move(rhs.m_enabled); m_stopTime = std::move(rhs.m_stopTime); @@ -97,7 +94,7 @@ void MotorSafety::Check() { wpi::raw_svector_ostream desc(buf); GetDescription(desc); desc << "... Output not updated often enough."; - wpi_setWPIErrorWithContext(Timeout, desc.str()); + FRC_ReportError(err::Timeout, desc.str()); StopMotor(); } } diff --git a/wpilibc/src/main/native/cpp/Notifier.cpp b/wpilibc/src/main/native/cpp/Notifier.cpp index 72f8a1d08c..c65a68ce2a 100644 --- a/wpilibc/src/main/native/cpp/Notifier.cpp +++ b/wpilibc/src/main/native/cpp/Notifier.cpp @@ -11,20 +11,20 @@ #include #include +#include "frc/Errors.h" #include "frc/Timer.h" #include "frc/Utility.h" -#include "frc/WPIErrors.h" using namespace frc; Notifier::Notifier(std::function handler) { - if (handler == nullptr) { - wpi_setWPIErrorWithContext(NullParameter, "handler must not be nullptr"); + if (!handler) { + throw FRC_MakeError(err::NullParameter, "handler"); } m_handler = handler; int32_t status = 0; m_notifier = HAL_InitializeNotifier(&status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "InitializeNotifier"); m_thread = std::thread([=] { for (;;) { @@ -60,13 +60,13 @@ Notifier::Notifier(std::function handler) { } Notifier::Notifier(int priority, std::function handler) { - if (handler == nullptr) { - wpi_setWPIErrorWithContext(NullParameter, "handler must not be nullptr"); + if (!handler) { + throw FRC_MakeError(err::NullParameter, "handler"); } m_handler = handler; int32_t status = 0; m_notifier = HAL_InitializeNotifier(&status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "InitializeNotifier"); m_thread = std::thread([=] { int32_t status = 0; @@ -107,7 +107,7 @@ Notifier::~Notifier() { // atomically set handle to 0, then clean HAL_NotifierHandle handle = m_notifier.exchange(0); HAL_StopNotifier(handle, &status); - wpi_setHALError(status); + FRC_ReportError(status, "StopNotifier"); // Join the thread to ensure the handler has exited. if (m_thread.joinable()) { @@ -118,8 +118,7 @@ Notifier::~Notifier() { } Notifier::Notifier(Notifier&& rhs) - : ErrorBase(std::move(rhs)), - m_thread(std::move(rhs.m_thread)), + : m_thread(std::move(rhs.m_thread)), m_notifier(rhs.m_notifier.load()), m_handler(std::move(rhs.m_handler)), m_expirationTime(std::move(rhs.m_expirationTime)), @@ -129,8 +128,6 @@ Notifier::Notifier(Notifier&& rhs) } Notifier& Notifier::operator=(Notifier&& rhs) { - ErrorBase::operator=(std::move(rhs)); - m_thread = std::move(rhs.m_thread); m_notifier = rhs.m_notifier.load(); rhs.m_notifier = HAL_kInvalidHandle; @@ -183,7 +180,7 @@ void Notifier::Stop() { m_periodic = false; int32_t status = 0; HAL_CancelNotifierAlarm(m_notifier, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "CancelNotifierAlarm"); } void Notifier::UpdateAlarm(uint64_t triggerTime) { @@ -194,7 +191,7 @@ void Notifier::UpdateAlarm(uint64_t triggerTime) { return; } HAL_UpdateNotifierAlarm(notifier, triggerTime, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "UpdateNotifierAlarm"); } void Notifier::UpdateAlarm() { diff --git a/wpilibc/src/main/native/cpp/PWM.cpp b/wpilibc/src/main/native/cpp/PWM.cpp index 2f4e1fe17f..d9c0a27454 100644 --- a/wpilibc/src/main/native/cpp/PWM.cpp +++ b/wpilibc/src/main/native/cpp/PWM.cpp @@ -11,9 +11,9 @@ #include #include +#include "frc/Errors.h" #include "frc/SensorUtil.h" #include "frc/Utility.h" -#include "frc/WPIErrors.h" #include "frc/smartdashboard/SendableBuilder.h" #include "frc/smartdashboard/SendableRegistry.h" @@ -21,27 +21,22 @@ using namespace frc; PWM::PWM(int channel, bool registerSendable) { if (!SensorUtil::CheckPWMChannel(channel)) { - wpi_setWPIErrorWithContext(ChannelIndexOutOfRange, - "PWM Channel " + wpi::Twine(channel)); + throw FRC_MakeError(err::ChannelIndexOutOfRange, + "PWM Channel " + wpi::Twine{channel}); return; } int32_t status = 0; m_handle = HAL_InitializePWMPort(HAL_GetPort(channel), &status); - if (status != 0) { - wpi_setHALErrorWithRange(status, 0, HAL_GetNumPWMChannels(), channel); - m_channel = std::numeric_limits::max(); - m_handle = HAL_kInvalidHandle; - return; - } + FRC_CheckErrorStatus(status, "PWM Channel " + wpi::Twine{channel}); m_channel = channel; HAL_SetPWMDisabled(m_handle, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "SetPWMDisabled"); status = 0; HAL_SetPWMEliminateDeadband(m_handle, false, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "SetPWMEliminateDeadband"); HAL_Report(HALUsageReporting::kResourceType_PWM, channel + 1); if (registerSendable) { @@ -53,88 +48,59 @@ PWM::~PWM() { int32_t status = 0; HAL_SetPWMDisabled(m_handle, &status); - wpi_setHALError(status); + FRC_ReportError(status, "SetPWMDisabled"); HAL_FreePWMPort(m_handle, &status); - wpi_setHALError(status); + FRC_ReportError(status, "FreePWM"); } void PWM::SetRaw(uint16_t value) { - if (StatusIsFatal()) { - return; - } - int32_t status = 0; HAL_SetPWMRaw(m_handle, value, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "SetRaw"); } uint16_t PWM::GetRaw() const { - if (StatusIsFatal()) { - return 0; - } - int32_t status = 0; uint16_t value = HAL_GetPWMRaw(m_handle, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "GetRaw"); return value; } void PWM::SetPosition(double pos) { - if (StatusIsFatal()) { - return; - } int32_t status = 0; HAL_SetPWMPosition(m_handle, pos, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "SetPosition"); } double PWM::GetPosition() const { - if (StatusIsFatal()) { - return 0.0; - } int32_t status = 0; double position = HAL_GetPWMPosition(m_handle, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "GetPosition"); return position; } void PWM::SetSpeed(double speed) { - if (StatusIsFatal()) { - return; - } int32_t status = 0; HAL_SetPWMSpeed(m_handle, speed, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "SetSpeed"); } double PWM::GetSpeed() const { - if (StatusIsFatal()) { - return 0.0; - } int32_t status = 0; double speed = HAL_GetPWMSpeed(m_handle, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "GetSpeed"); return speed; } void PWM::SetDisabled() { - if (StatusIsFatal()) { - return; - } - int32_t status = 0; - HAL_SetPWMDisabled(m_handle, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "SetDisabled"); } void PWM::SetPeriodMultiplier(PeriodMultiplier mult) { - if (StatusIsFatal()) { - return; - } - int32_t status = 0; switch (mult) { @@ -153,49 +119,35 @@ void PWM::SetPeriodMultiplier(PeriodMultiplier mult) { wpi_assert(false); } - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "SetPeriodMultiplier"); } void PWM::SetZeroLatch() { - if (StatusIsFatal()) { - return; - } - int32_t status = 0; - HAL_LatchPWMZero(m_handle, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "SetZeroLatch"); } void PWM::EnableDeadbandElimination(bool eliminateDeadband) { - if (StatusIsFatal()) { - return; - } int32_t status = 0; HAL_SetPWMEliminateDeadband(m_handle, eliminateDeadband, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "EnableDeadbandElimination"); } void PWM::SetBounds(double max, double deadbandMax, double center, double deadbandMin, double min) { - if (StatusIsFatal()) { - return; - } int32_t status = 0; HAL_SetPWMConfig(m_handle, max, deadbandMax, center, deadbandMin, min, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "SetBounds"); } void PWM::SetRawBounds(int max, int deadbandMax, int center, int deadbandMin, int min) { - if (StatusIsFatal()) { - return; - } int32_t status = 0; HAL_SetPWMConfigRaw(m_handle, max, deadbandMax, center, deadbandMin, min, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "SetRawBounds"); } void PWM::GetRawBounds(int* max, int* deadbandMax, int* center, @@ -203,7 +155,7 @@ void PWM::GetRawBounds(int* max, int* deadbandMax, int* center, int32_t status = 0; HAL_GetPWMConfigRaw(m_handle, max, deadbandMax, center, deadbandMin, min, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "GetRawBounds"); } int PWM::GetChannel() const { diff --git a/wpilibc/src/main/native/cpp/PowerDistributionPanel.cpp b/wpilibc/src/main/native/cpp/PowerDistributionPanel.cpp index b955f7f2ae..bb95c7bf0c 100644 --- a/wpilibc/src/main/native/cpp/PowerDistributionPanel.cpp +++ b/wpilibc/src/main/native/cpp/PowerDistributionPanel.cpp @@ -7,11 +7,9 @@ #include #include #include -#include -#include +#include "frc/Errors.h" #include "frc/SensorUtil.h" -#include "frc/WPIErrors.h" #include "frc/smartdashboard/SendableBuilder.h" #include "frc/smartdashboard/SendableRegistry.h" @@ -25,10 +23,7 @@ PowerDistributionPanel::PowerDistributionPanel() : PowerDistributionPanel(0) {} PowerDistributionPanel::PowerDistributionPanel(int module) : m_module(module) { int32_t status = 0; m_handle = HAL_InitializePDP(module, &status); - if (status != 0) { - wpi_setHALErrorWithRange(status, 0, HAL_GetNumPDPModules(), module); - return; - } + FRC_CheckErrorStatus(status, "Module " + wpi::Twine{module}); HAL_Report(HALUsageReporting::kResourceType_PDP, module + 1); SendableRegistry::GetInstance().AddLW(this, "PowerDistributionPanel", module); @@ -36,25 +31,15 @@ PowerDistributionPanel::PowerDistributionPanel(int module) : m_module(module) { double PowerDistributionPanel::GetVoltage() const { int32_t status = 0; - double voltage = HAL_GetPDPVoltage(m_handle, &status); - - if (status) { - wpi_setWPIErrorWithContext(Timeout, ""); - } - + FRC_CheckErrorStatus(status, "GetVoltage"); return voltage; } double PowerDistributionPanel::GetTemperature() const { int32_t status = 0; - double temperature = HAL_GetPDPTemperature(m_handle, &status); - - if (status) { - wpi_setWPIErrorWithContext(Timeout, ""); - } - + FRC_CheckErrorStatus(status, "GetTemperature"); return temperature; } @@ -62,75 +47,48 @@ double PowerDistributionPanel::GetCurrent(int channel) const { int32_t status = 0; if (!SensorUtil::CheckPDPChannel(channel)) { - wpi::SmallString<32> str; - wpi::raw_svector_ostream buf(str); - buf << "PDP Channel " << channel; - wpi_setWPIErrorWithContext(ChannelIndexOutOfRange, buf.str()); + FRC_ReportError(err::ChannelIndexOutOfRange, + "Channel " + wpi::Twine{channel}); + return 0; } double current = HAL_GetPDPChannelCurrent(m_handle, channel, &status); - - if (status) { - wpi_setWPIErrorWithContext(Timeout, ""); - } + FRC_CheckErrorStatus(status, "Channel " + wpi::Twine{channel}); return current; } double PowerDistributionPanel::GetTotalCurrent() const { int32_t status = 0; - double current = HAL_GetPDPTotalCurrent(m_handle, &status); - - if (status) { - wpi_setWPIErrorWithContext(Timeout, ""); - } - + FRC_CheckErrorStatus(status, "GetTotalCurrent"); return current; } double PowerDistributionPanel::GetTotalPower() const { int32_t status = 0; - double power = HAL_GetPDPTotalPower(m_handle, &status); - - if (status) { - wpi_setWPIErrorWithContext(Timeout, ""); - } - + FRC_CheckErrorStatus(status, "GetTotalPower"); return power; } double PowerDistributionPanel::GetTotalEnergy() const { int32_t status = 0; - double energy = HAL_GetPDPTotalEnergy(m_handle, &status); - - if (status) { - wpi_setWPIErrorWithContext(Timeout, ""); - } - + FRC_CheckErrorStatus(status, "GetTotalEnergy"); return energy; } void PowerDistributionPanel::ResetTotalEnergy() { int32_t status = 0; - HAL_ResetPDPTotalEnergy(m_handle, &status); - - if (status) { - wpi_setWPIErrorWithContext(Timeout, ""); - } + FRC_CheckErrorStatus(status, "ResetTotalEnergy"); } void PowerDistributionPanel::ClearStickyFaults() { int32_t status = 0; - HAL_ClearPDPStickyFaults(m_handle, &status); - - if (status) { - wpi_setWPIErrorWithContext(Timeout, ""); - } + FRC_CheckErrorStatus(status, "ClearStickyFaults"); } int PowerDistributionPanel::GetModule() const { diff --git a/wpilibc/src/main/native/cpp/Preferences.cpp b/wpilibc/src/main/native/cpp/Preferences.cpp index 8fa43a51db..8526c943b5 100644 --- a/wpilibc/src/main/native/cpp/Preferences.cpp +++ b/wpilibc/src/main/native/cpp/Preferences.cpp @@ -10,8 +10,6 @@ #include #include -#include "frc/WPIErrors.h" - using namespace frc; // The Preferences table name diff --git a/wpilibc/src/main/native/cpp/Relay.cpp b/wpilibc/src/main/native/cpp/Relay.cpp index 395b1eb617..66c63c3516 100644 --- a/wpilibc/src/main/native/cpp/Relay.cpp +++ b/wpilibc/src/main/native/cpp/Relay.cpp @@ -12,8 +12,8 @@ #include #include +#include "frc/Errors.h" #include "frc/SensorUtil.h" -#include "frc/WPIErrors.h" #include "frc/smartdashboard/SendableBuilder.h" #include "frc/smartdashboard/SendableRegistry.h" @@ -22,8 +22,8 @@ using namespace frc; Relay::Relay(int channel, Relay::Direction direction) : m_channel(channel), m_direction(direction) { if (!SensorUtil::CheckRelayChannel(m_channel)) { - wpi_setWPIErrorWithContext(ChannelIndexOutOfRange, - "Relay Channel " + wpi::Twine(m_channel)); + throw FRC_MakeError(err::ChannelIndexOutOfRange, + "Relay Channel " + wpi::Twine{m_channel}); return; } @@ -32,45 +32,24 @@ Relay::Relay(int channel, Relay::Direction direction) if (m_direction == kBothDirections || m_direction == kForwardOnly) { int32_t status = 0; m_forwardHandle = HAL_InitializeRelayPort(portHandle, true, &status); - if (status != 0) { - wpi_setHALErrorWithRange(status, 0, HAL_GetNumRelayChannels(), channel); - m_forwardHandle = HAL_kInvalidHandle; - m_reverseHandle = HAL_kInvalidHandle; - return; - } + FRC_CheckErrorStatus(status, "Relay Channel " + wpi::Twine{m_channel}); HAL_Report(HALUsageReporting::kResourceType_Relay, m_channel + 1); } if (m_direction == kBothDirections || m_direction == kReverseOnly) { int32_t status = 0; m_reverseHandle = HAL_InitializeRelayPort(portHandle, false, &status); - if (status != 0) { - wpi_setHALErrorWithRange(status, 0, HAL_GetNumRelayChannels(), channel); - m_forwardHandle = HAL_kInvalidHandle; - m_reverseHandle = HAL_kInvalidHandle; - return; - } - + FRC_CheckErrorStatus(status, "Relay Channel " + wpi::Twine{m_channel}); HAL_Report(HALUsageReporting::kResourceType_Relay, m_channel + 128); } int32_t status = 0; if (m_forwardHandle != HAL_kInvalidHandle) { HAL_SetRelay(m_forwardHandle, false, &status); - if (status != 0) { - wpi_setHALError(status); - m_forwardHandle = HAL_kInvalidHandle; - m_reverseHandle = HAL_kInvalidHandle; - return; - } + FRC_CheckErrorStatus(status, "Relay Channel " + wpi::Twine{m_channel}); } if (m_reverseHandle != HAL_kInvalidHandle) { HAL_SetRelay(m_reverseHandle, false, &status); - if (status != 0) { - wpi_setHALError(status); - m_forwardHandle = HAL_kInvalidHandle; - m_reverseHandle = HAL_kInvalidHandle; - return; - } + FRC_CheckErrorStatus(status, "Relay Channel " + wpi::Twine{m_channel}); } SendableRegistry::GetInstance().AddLW(this, "Relay", m_channel); @@ -90,10 +69,6 @@ Relay::~Relay() { } void Relay::Set(Relay::Value value) { - if (StatusIsFatal()) { - return; - } - int32_t status = 0; switch (value) { @@ -115,7 +90,7 @@ void Relay::Set(Relay::Value value) { break; case kForward: if (m_direction == kReverseOnly) { - wpi_setWPIError(IncompatibleMode); + FRC_ReportError(err::IncompatibleMode, "setting forward"); break; } if (m_direction == kBothDirections || m_direction == kForwardOnly) { @@ -127,7 +102,7 @@ void Relay::Set(Relay::Value value) { break; case kReverse: if (m_direction == kForwardOnly) { - wpi_setWPIError(IncompatibleMode); + FRC_ReportError(err::IncompatibleMode, "setting reverse"); break; } if (m_direction == kBothDirections) { @@ -139,7 +114,7 @@ void Relay::Set(Relay::Value value) { break; } - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "Set"); } Relay::Value Relay::Get() const { @@ -173,7 +148,7 @@ Relay::Value Relay::Get() const { } } - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "Get"); } int Relay::GetChannel() const { diff --git a/wpilibc/src/main/native/cpp/Resource.cpp b/wpilibc/src/main/native/cpp/Resource.cpp index dd16112d98..e41a43c778 100644 --- a/wpilibc/src/main/native/cpp/Resource.cpp +++ b/wpilibc/src/main/native/cpp/Resource.cpp @@ -4,8 +4,7 @@ #include "frc/Resource.h" -#include "frc/ErrorBase.h" -#include "frc/WPIErrors.h" +#include "frc/Errors.h" using namespace frc; @@ -31,19 +30,16 @@ uint32_t Resource::Allocate(const std::string& resourceDesc) { return i; } } - wpi_setWPIErrorWithContext(NoAvailableResources, resourceDesc); - return std::numeric_limits::max(); + throw FRC_MakeError(err::NoAvailableResources, resourceDesc); } uint32_t Resource::Allocate(uint32_t index, const std::string& resourceDesc) { std::scoped_lock lock(m_allocateMutex); if (index >= m_isAllocated.size()) { - wpi_setWPIErrorWithContext(ChannelIndexOutOfRange, resourceDesc); - return std::numeric_limits::max(); + throw FRC_MakeError(err::ChannelIndexOutOfRange, resourceDesc); } if (m_isAllocated[index]) { - wpi_setWPIErrorWithContext(ResourceAlreadyAllocated, resourceDesc); - return std::numeric_limits::max(); + throw FRC_MakeError(err::ResourceAlreadyAllocated, resourceDesc); } m_isAllocated[index] = true; return index; @@ -55,12 +51,10 @@ void Resource::Free(uint32_t index) { return; } if (index >= m_isAllocated.size()) { - wpi_setWPIError(NotAllocated); - return; + throw FRC_MakeError(err::NotAllocated, "index " + wpi::Twine{index}); } if (!m_isAllocated[index]) { - wpi_setWPIError(NotAllocated); - return; + throw FRC_MakeError(err::NotAllocated, "index " + wpi::Twine{index}); } m_isAllocated[index] = false; } diff --git a/wpilibc/src/main/native/cpp/RobotController.cpp b/wpilibc/src/main/native/cpp/RobotController.cpp index ac49cfa225..88da69b7d0 100644 --- a/wpilibc/src/main/native/cpp/RobotController.cpp +++ b/wpilibc/src/main/native/cpp/RobotController.cpp @@ -8,156 +8,154 @@ #include #include -#include "frc/ErrorBase.h" +#include "frc/Errors.h" using namespace frc; int RobotController::GetFPGAVersion() { int32_t status = 0; int version = HAL_GetFPGAVersion(&status); - wpi_setGlobalHALError(status); + FRC_CheckErrorStatus(status, "GetFPGAVersion"); return version; } int64_t RobotController::GetFPGARevision() { int32_t status = 0; int64_t revision = HAL_GetFPGARevision(&status); - wpi_setGlobalHALError(status); + FRC_CheckErrorStatus(status, "GetFPGARevision"); return revision; } uint64_t RobotController::GetFPGATime() { int32_t status = 0; uint64_t time = HAL_GetFPGATime(&status); - wpi_setGlobalHALError(status); + FRC_CheckErrorStatus(status, "GetFPGATime"); return time; } bool RobotController::GetUserButton() { int32_t status = 0; - bool value = HAL_GetFPGAButton(&status); - wpi_setGlobalError(status); - + FRC_CheckErrorStatus(status, "GetUserButton"); return value; } units::volt_t RobotController::GetBatteryVoltage() { int32_t status = 0; double retVal = HAL_GetVinVoltage(&status); - wpi_setGlobalHALError(status); + FRC_CheckErrorStatus(status, "GetBatteryVoltage"); return units::volt_t{retVal}; } bool RobotController::IsSysActive() { int32_t status = 0; bool retVal = HAL_GetSystemActive(&status); - wpi_setGlobalHALError(status); + FRC_CheckErrorStatus(status, "IsSysActive"); return retVal; } bool RobotController::IsBrownedOut() { int32_t status = 0; bool retVal = HAL_GetBrownedOut(&status); - wpi_setGlobalHALError(status); + FRC_CheckErrorStatus(status, "IsBrownedOut"); return retVal; } double RobotController::GetInputVoltage() { int32_t status = 0; double retVal = HAL_GetVinVoltage(&status); - wpi_setGlobalHALError(status); + FRC_CheckErrorStatus(status, "GetInputVoltage"); return retVal; } double RobotController::GetInputCurrent() { int32_t status = 0; double retVal = HAL_GetVinCurrent(&status); - wpi_setGlobalHALError(status); + FRC_CheckErrorStatus(status, "GetInputCurrent"); return retVal; } double RobotController::GetVoltage3V3() { int32_t status = 0; double retVal = HAL_GetUserVoltage3V3(&status); - wpi_setGlobalHALError(status); + FRC_CheckErrorStatus(status, "GetVoltage3V3"); return retVal; } double RobotController::GetCurrent3V3() { int32_t status = 0; double retVal = HAL_GetUserCurrent3V3(&status); - wpi_setGlobalHALError(status); + FRC_CheckErrorStatus(status, "GetCurrent3V3"); return retVal; } bool RobotController::GetEnabled3V3() { int32_t status = 0; bool retVal = HAL_GetUserActive3V3(&status); - wpi_setGlobalHALError(status); + FRC_CheckErrorStatus(status, "GetEnabled3V3"); return retVal; } int RobotController::GetFaultCount3V3() { int32_t status = 0; int retVal = HAL_GetUserCurrentFaults3V3(&status); - wpi_setGlobalHALError(status); + FRC_CheckErrorStatus(status, "GetFaultCount3V3"); return retVal; } double RobotController::GetVoltage5V() { int32_t status = 0; double retVal = HAL_GetUserVoltage5V(&status); - wpi_setGlobalHALError(status); + FRC_CheckErrorStatus(status, "GetVoltage5V"); return retVal; } double RobotController::GetCurrent5V() { int32_t status = 0; double retVal = HAL_GetUserCurrent5V(&status); - wpi_setGlobalHALError(status); + FRC_CheckErrorStatus(status, "GetCurrent5V"); return retVal; } bool RobotController::GetEnabled5V() { int32_t status = 0; bool retVal = HAL_GetUserActive5V(&status); - wpi_setGlobalHALError(status); + FRC_CheckErrorStatus(status, "GetEnabled5V"); return retVal; } int RobotController::GetFaultCount5V() { int32_t status = 0; int retVal = HAL_GetUserCurrentFaults5V(&status); - wpi_setGlobalHALError(status); + FRC_CheckErrorStatus(status, "GetFaultCount5V"); return retVal; } double RobotController::GetVoltage6V() { int32_t status = 0; double retVal = HAL_GetUserVoltage6V(&status); - wpi_setGlobalHALError(status); + FRC_CheckErrorStatus(status, "GetVoltage6V"); return retVal; } double RobotController::GetCurrent6V() { int32_t status = 0; double retVal = HAL_GetUserCurrent6V(&status); - wpi_setGlobalHALError(status); + FRC_CheckErrorStatus(status, "GetCurrent6V"); return retVal; } bool RobotController::GetEnabled6V() { int32_t status = 0; bool retVal = HAL_GetUserActive6V(&status); - wpi_setGlobalHALError(status); + FRC_CheckErrorStatus(status, "GetEnabled6V"); return retVal; } int RobotController::GetFaultCount6V() { int32_t status = 0; int retVal = HAL_GetUserCurrentFaults6V(&status); - wpi_setGlobalHALError(status); + FRC_CheckErrorStatus(status, "GetFaultCount6V"); return retVal; } @@ -170,10 +168,7 @@ CANStatus RobotController::GetCANStatus() { uint32_t transmitErrorCount = 0; HAL_CAN_GetCANStatus(&percentBusUtilization, &busOffCount, &txFullCount, &receiveErrorCount, &transmitErrorCount, &status); - if (status != 0) { - wpi_setGlobalHALError(status); - return {}; - } + FRC_CheckErrorStatus(status, "GetCANStatus"); return {percentBusUtilization, static_cast(busOffCount), static_cast(txFullCount), static_cast(receiveErrorCount), static_cast(transmitErrorCount)}; diff --git a/wpilibc/src/main/native/cpp/SPI.cpp b/wpilibc/src/main/native/cpp/SPI.cpp index 12ebb172d1..9392723b6d 100644 --- a/wpilibc/src/main/native/cpp/SPI.cpp +++ b/wpilibc/src/main/native/cpp/SPI.cpp @@ -14,8 +14,8 @@ #include #include "frc/DigitalSource.h" +#include "frc/Errors.h" #include "frc/Notifier.h" -#include "frc/WPIErrors.h" using namespace frc; @@ -77,9 +77,7 @@ void SPI::Accumulator::Update() { // get amount of data available int32_t numToRead = HAL_ReadSPIAutoReceivedData(m_port, m_buf, 0, 0, &status); - if (status != 0) { - return; // error reading - } + FRC_CheckErrorStatus(status, "ReadSPIAutoReceivedData"); // only get whole responses; +1 is for timestamp numToRead -= numToRead % m_xferSize; @@ -93,9 +91,7 @@ void SPI::Accumulator::Update() { // read buffered data HAL_ReadSPIAutoReceivedData(m_port, m_buf, numToRead, 0, &status); - if (status != 0) { - return; // error reading - } + FRC_CheckErrorStatus(status, "ReadSPIAutoReceivedData"); // loop over all responses for (int32_t off = 0; off < numToRead; off += m_xferSize) { @@ -162,7 +158,7 @@ void SPI::Accumulator::Update() { SPI::SPI(Port port) : m_port(static_cast(port)) { int32_t status = 0; HAL_InitializeSPI(m_port, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "InitializeSPI"); HAL_Report(HALUsageReporting::kResourceType_SPI, static_cast(port) + 1); @@ -219,13 +215,13 @@ void SPI::SetClockActiveHigh() { void SPI::SetChipSelectActiveHigh() { int32_t status = 0; HAL_SetSPIChipSelectActiveHigh(m_port, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "SetChipSelectActiveHigh"); } void SPI::SetChipSelectActiveLow() { int32_t status = 0; HAL_SetSPIChipSelectActiveLow(m_port, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "SetChipSelectActiveLow"); } int SPI::Write(uint8_t* data, int size) { @@ -255,26 +251,26 @@ int SPI::Transaction(uint8_t* dataToSend, uint8_t* dataReceived, int size) { void SPI::InitAuto(int bufferSize) { int32_t status = 0; HAL_InitSPIAuto(m_port, bufferSize, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "InitAuto"); } void SPI::FreeAuto() { int32_t status = 0; HAL_FreeSPIAuto(m_port, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "FreeAuto"); } void SPI::SetAutoTransmitData(wpi::ArrayRef dataToSend, int zeroSize) { int32_t status = 0; HAL_SetSPIAutoTransmitData(m_port, dataToSend.data(), dataToSend.size(), zeroSize, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "SetAutoTransmitData"); } void SPI::StartAutoRate(units::second_t period) { int32_t status = 0; HAL_StartSPIAutoRate(m_port, period.to(), &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "StartAutoRate"); } void SPI::StartAutoRate(double period) { @@ -287,19 +283,19 @@ void SPI::StartAutoTrigger(DigitalSource& source, bool rising, bool falling) { static_cast( source.GetAnalogTriggerTypeForRouting()), rising, falling, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "StartAutoTrigger"); } void SPI::StopAuto() { int32_t status = 0; HAL_StopSPIAuto(m_port, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "StopAuto"); } void SPI::ForceAutoRead() { int32_t status = 0; HAL_ForceSPIAutoRead(m_port, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "ForceAutoRead"); } int SPI::ReadAutoReceivedData(uint32_t* buffer, int numToRead, @@ -307,7 +303,7 @@ int SPI::ReadAutoReceivedData(uint32_t* buffer, int numToRead, int32_t status = 0; int32_t val = HAL_ReadSPIAutoReceivedData(m_port, buffer, numToRead, timeout.to(), &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "ReadAutoReceivedData"); return val; } @@ -318,7 +314,7 @@ int SPI::ReadAutoReceivedData(uint32_t* buffer, int numToRead, double timeout) { int SPI::GetAutoDroppedCount() { int32_t status = 0; int32_t val = HAL_GetSPIAutoDroppedCount(m_port, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "GetAutoDroppedCount"); return val; } @@ -327,7 +323,7 @@ void SPI::ConfigureAutoStall(HAL_SPIPort port, int csToSclkTicks, int32_t status = 0; HAL_ConfigureSPIAutoStall(m_port, csToSclkTicks, stallTicks, pow2BytesPerRead, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "ConfigureAutoStall"); } void SPI::InitAccumulator(units::second_t period, int cmd, int xferSize, diff --git a/wpilibc/src/main/native/cpp/SerialPort.cpp b/wpilibc/src/main/native/cpp/SerialPort.cpp index 70c7fb52d1..6640286477 100644 --- a/wpilibc/src/main/native/cpp/SerialPort.cpp +++ b/wpilibc/src/main/native/cpp/SerialPort.cpp @@ -9,6 +9,8 @@ #include #include +#include "frc/Errors.h" + using namespace frc; SerialPort::SerialPort(int baudRate, Port port, int dataBits, @@ -18,19 +20,17 @@ SerialPort::SerialPort(int baudRate, Port port, int dataBits, m_portHandle = HAL_InitializeSerialPort(static_cast(port), &status); - wpi_setHALError(status); - // Don't continue if initialization failed - if (status < 0) { - return; - } + FRC_CheckErrorStatus(status, "Port " + wpi::Twine{static_cast(port)}); HAL_SetSerialBaudRate(m_portHandle, baudRate, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "SetSerialBaudRate " + wpi::Twine{baudRate}); HAL_SetSerialDataBits(m_portHandle, dataBits, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "SetSerialDataBits " + wpi::Twine{dataBits}); HAL_SetSerialParity(m_portHandle, parity, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus( + status, "SetSerialParity " + wpi::Twine{static_cast(parity)}); HAL_SetSerialStopBits(m_portHandle, stopBits, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus( + status, "SetSerialStopBits " + wpi::Twine{static_cast(stopBits)}); // Set the default timeout to 5 seconds. SetTimeout(5.0); @@ -54,19 +54,17 @@ SerialPort::SerialPort(int baudRate, const wpi::Twine& portName, Port port, m_portHandle = HAL_InitializeSerialPortDirect( static_cast(port), portNameC, &status); - wpi_setHALError(status); - // Don't continue if initialization failed - if (status < 0) { - return; - } + FRC_CheckErrorStatus(status, "Port " + wpi::Twine{static_cast(port)}); HAL_SetSerialBaudRate(m_portHandle, baudRate, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "SetSerialBaudRate " + wpi::Twine{baudRate}); HAL_SetSerialDataBits(m_portHandle, dataBits, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "SetSerialDataBits " + wpi::Twine{dataBits}); HAL_SetSerialParity(m_portHandle, parity, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus( + status, "SetSerialParity " + wpi::Twine{static_cast(parity)}); HAL_SetSerialStopBits(m_portHandle, stopBits, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus( + status, "SetSerialStopBits " + wpi::Twine{static_cast(stopBits)}); // Set the default timeout to 5 seconds. SetTimeout(5.0); @@ -83,38 +81,40 @@ SerialPort::SerialPort(int baudRate, const wpi::Twine& portName, Port port, SerialPort::~SerialPort() { int32_t status = 0; HAL_CloseSerial(m_portHandle, &status); - wpi_setHALError(status); + FRC_ReportError(status, "CloseSerial"); } void SerialPort::SetFlowControl(SerialPort::FlowControl flowControl) { int32_t status = 0; HAL_SetSerialFlowControl(m_portHandle, flowControl, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus( + status, "SetFlowControl " + wpi::Twine{static_cast(flowControl)}); } void SerialPort::EnableTermination(char terminator) { int32_t status = 0; HAL_EnableSerialTermination(m_portHandle, terminator, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus( + status, "EnableTermination " + wpi::Twine{static_cast(terminator)}); } void SerialPort::DisableTermination() { int32_t status = 0; HAL_DisableSerialTermination(m_portHandle, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "DisableTermination"); } int SerialPort::GetBytesReceived() { int32_t status = 0; int retVal = HAL_GetSerialBytesReceived(m_portHandle, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "GetBytesReceived"); return retVal; } int SerialPort::Read(char* buffer, int count) { int32_t status = 0; int retVal = HAL_ReadSerial(m_portHandle, buffer, count, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "Read"); return retVal; } @@ -126,42 +126,43 @@ int SerialPort::Write(wpi::StringRef buffer) { int32_t status = 0; int retVal = HAL_WriteSerial(m_portHandle, buffer.data(), buffer.size(), &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "Write"); return retVal; } void SerialPort::SetTimeout(double timeout) { int32_t status = 0; HAL_SetSerialTimeout(m_portHandle, timeout, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "SetTimeout"); } void SerialPort::SetReadBufferSize(int size) { int32_t status = 0; HAL_SetSerialReadBufferSize(m_portHandle, size, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "SetReadBufferSize " + wpi::Twine{size}); } void SerialPort::SetWriteBufferSize(int size) { int32_t status = 0; HAL_SetSerialWriteBufferSize(m_portHandle, size, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "SetWriteBufferSize " + wpi::Twine{size}); } void SerialPort::SetWriteBufferMode(SerialPort::WriteBufferMode mode) { int32_t status = 0; HAL_SetSerialWriteMode(m_portHandle, mode, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus( + status, "SetWriteBufferMode " + wpi::Twine{static_cast(mode)}); } void SerialPort::Flush() { int32_t status = 0; HAL_FlushSerial(m_portHandle, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "Flush"); } void SerialPort::Reset() { int32_t status = 0; HAL_ClearSerial(m_portHandle, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "Reset"); } diff --git a/wpilibc/src/main/native/cpp/Solenoid.cpp b/wpilibc/src/main/native/cpp/Solenoid.cpp index ec54e15df4..1ead9fd130 100644 --- a/wpilibc/src/main/native/cpp/Solenoid.cpp +++ b/wpilibc/src/main/native/cpp/Solenoid.cpp @@ -11,8 +11,8 @@ #include #include +#include "frc/Errors.h" #include "frc/SensorUtil.h" -#include "frc/WPIErrors.h" #include "frc/smartdashboard/SendableBuilder.h" #include "frc/smartdashboard/SendableRegistry.h" @@ -24,24 +24,19 @@ Solenoid::Solenoid(int channel) Solenoid::Solenoid(int moduleNumber, int channel) : SolenoidBase(moduleNumber), m_channel(channel) { if (!SensorUtil::CheckSolenoidModule(m_moduleNumber)) { - wpi_setWPIErrorWithContext(ModuleIndexOutOfRange, - "Solenoid Module " + wpi::Twine(m_moduleNumber)); - return; + throw FRC_MakeError(err::ModuleIndexOutOfRange, + "Solenoid Module " + wpi::Twine{m_moduleNumber}); } if (!SensorUtil::CheckSolenoidChannel(m_channel)) { - wpi_setWPIErrorWithContext(ChannelIndexOutOfRange, - "Solenoid Channel " + wpi::Twine(m_channel)); - return; + throw FRC_MakeError(err::ChannelIndexOutOfRange, + "Solenoid Channel " + wpi::Twine{m_channel}); } int32_t status = 0; m_solenoidHandle = HAL_InitializeSolenoidPort( HAL_GetPortWithModule(moduleNumber, channel), &status); - if (status != 0) { - wpi_setHALErrorWithRange(status, 0, HAL_GetNumSolenoidChannels(), channel); - m_solenoidHandle = HAL_kInvalidHandle; - return; - } + FRC_CheckErrorStatus(status, "Solenoid Module " + wpi::Twine{m_moduleNumber} + + " Channel " + wpi::Twine{m_channel}); HAL_Report(HALUsageReporting::kResourceType_Solenoid, m_channel + 1, m_moduleNumber + 1); @@ -54,23 +49,15 @@ Solenoid::~Solenoid() { } void Solenoid::Set(bool on) { - if (StatusIsFatal()) { - return; - } - int32_t status = 0; HAL_SetSolenoid(m_solenoidHandle, on, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "Set"); } bool Solenoid::Get() const { - if (StatusIsFatal()) { - return false; - } - int32_t status = 0; bool value = HAL_GetSolenoid(m_solenoidHandle, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "Get"); return value; } @@ -90,21 +77,15 @@ bool Solenoid::IsBlackListed() const { void Solenoid::SetPulseDuration(double durationSeconds) { int32_t durationMS = durationSeconds * 1000; - if (StatusIsFatal()) { - return; - } int32_t status = 0; HAL_SetOneShotDuration(m_solenoidHandle, durationMS, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "SetPulseDuration"); } void Solenoid::StartPulse() { - if (StatusIsFatal()) { - return; - } int32_t status = 0; HAL_FireOneShot(m_solenoidHandle, &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "StartPulse"); } void Solenoid::InitSendable(SendableBuilder& builder) { diff --git a/wpilibc/src/main/native/cpp/SolenoidBase.cpp b/wpilibc/src/main/native/cpp/SolenoidBase.cpp index c8b64f4b6a..e4598e1ea5 100644 --- a/wpilibc/src/main/native/cpp/SolenoidBase.cpp +++ b/wpilibc/src/main/native/cpp/SolenoidBase.cpp @@ -7,13 +7,15 @@ #include #include +#include "frc/Errors.h" + using namespace frc; int SolenoidBase::GetAll(int module) { int value = 0; int32_t status = 0; value = HAL_GetAllSolenoids(module, &status); - wpi_setGlobalHALError(status); + FRC_CheckErrorStatus(status, "Module " + wpi::Twine{module}); return value; } @@ -23,7 +25,9 @@ int SolenoidBase::GetAll() const { int SolenoidBase::GetPCMSolenoidBlackList(int module) { int32_t status = 0; - return HAL_GetPCMSolenoidBlackList(module, &status); + int rv = HAL_GetPCMSolenoidBlackList(module, &status); + FRC_CheckErrorStatus(status, "Module " + wpi::Twine{module}); + return rv; } int SolenoidBase::GetPCMSolenoidBlackList() const { @@ -32,7 +36,9 @@ int SolenoidBase::GetPCMSolenoidBlackList() const { bool SolenoidBase::GetPCMSolenoidVoltageStickyFault(int module) { int32_t status = 0; - return HAL_GetPCMSolenoidVoltageStickyFault(module, &status); + bool rv = HAL_GetPCMSolenoidVoltageStickyFault(module, &status); + FRC_CheckErrorStatus(status, "Module " + wpi::Twine{module}); + return rv; } bool SolenoidBase::GetPCMSolenoidVoltageStickyFault() const { @@ -41,7 +47,9 @@ bool SolenoidBase::GetPCMSolenoidVoltageStickyFault() const { bool SolenoidBase::GetPCMSolenoidVoltageFault(int module) { int32_t status = 0; - return HAL_GetPCMSolenoidVoltageFault(module, &status); + bool rv = HAL_GetPCMSolenoidVoltageFault(module, &status); + FRC_CheckErrorStatus(status, "Module " + wpi::Twine{module}); + return rv; } bool SolenoidBase::GetPCMSolenoidVoltageFault() const { @@ -50,7 +58,8 @@ bool SolenoidBase::GetPCMSolenoidVoltageFault() const { void SolenoidBase::ClearAllPCMStickyFaults(int module) { int32_t status = 0; - return HAL_ClearAllPCMStickyFaults(module, &status); + HAL_ClearAllPCMStickyFaults(module, &status); + FRC_CheckErrorStatus(status, "Module " + wpi::Twine{module}); } void SolenoidBase::ClearAllPCMStickyFaults() { diff --git a/wpilibc/src/main/native/cpp/Threads.cpp b/wpilibc/src/main/native/cpp/Threads.cpp index 1b1650aff4..3ecab5569a 100644 --- a/wpilibc/src/main/native/cpp/Threads.cpp +++ b/wpilibc/src/main/native/cpp/Threads.cpp @@ -7,7 +7,7 @@ #include #include -#include "frc/ErrorBase.h" +#include "frc/Errors.h" namespace frc { @@ -16,7 +16,7 @@ int GetThreadPriority(std::thread& thread, bool* isRealTime) { HAL_Bool rt = false; auto native = thread.native_handle(); auto ret = HAL_GetThreadPriority(&native, &rt, &status); - wpi_setGlobalHALError(status); + FRC_CheckErrorStatus(status, "GetThreadPriority"); *isRealTime = rt; return ret; } @@ -25,7 +25,7 @@ int GetCurrentThreadPriority(bool* isRealTime) { int32_t status = 0; HAL_Bool rt = false; auto ret = HAL_GetCurrentThreadPriority(&rt, &status); - wpi_setGlobalHALError(status); + FRC_CheckErrorStatus(status, "GetCurrentThreadPriority"); *isRealTime = rt; return ret; } @@ -34,14 +34,14 @@ bool SetThreadPriority(std::thread& thread, bool realTime, int priority) { int32_t status = 0; auto native = thread.native_handle(); auto ret = HAL_SetThreadPriority(&native, realTime, priority, &status); - wpi_setGlobalHALError(status); + FRC_CheckErrorStatus(status, "SetThreadPriority"); return ret; } bool SetCurrentThreadPriority(bool realTime, int priority) { int32_t status = 0; auto ret = HAL_SetCurrentThreadPriority(realTime, priority, &status); - wpi_setGlobalHALError(status); + FRC_CheckErrorStatus(status, "SetCurrentThreadPriority"); return ret; } diff --git a/wpilibc/src/main/native/cpp/TimedRobot.cpp b/wpilibc/src/main/native/cpp/TimedRobot.cpp index 2704d51010..0326859411 100644 --- a/wpilibc/src/main/native/cpp/TimedRobot.cpp +++ b/wpilibc/src/main/native/cpp/TimedRobot.cpp @@ -12,9 +12,9 @@ #include #include +#include "frc/Errors.h" #include "frc/Timer.h" #include "frc/Utility.h" -#include "frc/WPIErrors.h" using namespace frc; @@ -39,7 +39,7 @@ void TimedRobot::StartCompetition() { HAL_UpdateNotifierAlarm( m_notifier, static_cast(callback.expirationTime * 1e6), &status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "UpdateNotifierAlarm"); uint64_t curTime = HAL_WaitForNotifierAlarm(m_notifier, &status); if (curTime == 0 || status != 0) { @@ -81,7 +81,7 @@ TimedRobot::TimedRobot(units::second_t period) : IterativeRobotBase(period) { int32_t status = 0; m_notifier = HAL_InitializeNotifier(&status); - wpi_setHALError(status); + FRC_CheckErrorStatus(status, "InitializeNotifier"); HAL_SetNotifierName(m_notifier, "TimedRobot", &status); HAL_Report(HALUsageReporting::kResourceType_Framework, @@ -92,7 +92,7 @@ TimedRobot::~TimedRobot() { int32_t status = 0; HAL_StopNotifier(m_notifier, &status); - wpi_setHALError(status); + FRC_ReportError(status, "StopNotifier"); HAL_CleanNotifier(m_notifier, &status); } diff --git a/wpilibc/src/main/native/cpp/Ultrasonic.cpp b/wpilibc/src/main/native/cpp/Ultrasonic.cpp index 140c66ae5b..2389eda1e0 100644 --- a/wpilibc/src/main/native/cpp/Ultrasonic.cpp +++ b/wpilibc/src/main/native/cpp/Ultrasonic.cpp @@ -12,9 +12,9 @@ #include "frc/Counter.h" #include "frc/DigitalInput.h" #include "frc/DigitalOutput.h" +#include "frc/Errors.h" #include "frc/Timer.h" #include "frc/Utility.h" -#include "frc/WPIErrors.h" #include "frc/smartdashboard/SendableBuilder.h" #include "frc/smartdashboard/SendableRegistry.h" @@ -40,9 +40,11 @@ Ultrasonic::Ultrasonic(DigitalOutput* pingChannel, DigitalInput* echoChannel) : m_pingChannel(pingChannel, NullDeleter()), m_echoChannel(echoChannel, NullDeleter()), m_counter(m_echoChannel) { - if (pingChannel == nullptr || echoChannel == nullptr) { - wpi_setWPIError(NullParameter); - return; + if (!pingChannel) { + throw FRC_MakeError(err::NullParameter, "pingChannel"); + } + if (!echoChannel) { + throw FRC_MakeError(err::NullParameter, "echoChannel"); } Initialize(); } diff --git a/wpilibc/src/main/native/cpp/Watchdog.cpp b/wpilibc/src/main/native/cpp/Watchdog.cpp index e93457f33f..5ba4273343 100644 --- a/wpilibc/src/main/native/cpp/Watchdog.cpp +++ b/wpilibc/src/main/native/cpp/Watchdog.cpp @@ -14,6 +14,7 @@ #include #include "frc/DriverStation.h" +#include "frc/Errors.h" #include "frc2/Timer.h" using namespace frc; @@ -47,7 +48,7 @@ class Watchdog::Impl { Watchdog::Impl::Impl() { int32_t status = 0; m_notifier = HAL_InitializeNotifier(&status); - wpi_setGlobalHALError(status); + FRC_CheckErrorStatus(status, "starting watchdog notifier"); HAL_SetNotifierName(m_notifier, "Watchdog", &status); m_thread = std::thread([=] { Main(); }); @@ -58,7 +59,7 @@ Watchdog::Impl::~Impl() { // atomically set handle to 0, then clean HAL_NotifierHandle handle = m_notifier.exchange(0); HAL_StopNotifier(handle, &status); - wpi_setGlobalHALError(status); + FRC_ReportError(status, "stopping watchdog notifier"); // Join the thread to ensure the handler has exited. if (m_thread.joinable()) { @@ -84,7 +85,7 @@ void Watchdog::Impl::UpdateAlarm() { 1e6), &status); } - wpi_setGlobalHALError(status); + FRC_CheckErrorStatus(status, "updating watchdog notifier alarm"); } void Watchdog::Impl::Main() { @@ -141,7 +142,11 @@ Watchdog::Watchdog(units::second_t timeout, std::function callback) : m_timeout(timeout), m_callback(std::move(callback)), m_impl(GetImpl()) {} Watchdog::~Watchdog() { - Disable(); + try { + Disable(); + } catch (const RuntimeError& e) { + e.Report(); + } } Watchdog::Watchdog(Watchdog&& rhs) { diff --git a/wpilibc/src/main/native/cpp/shuffleboard/ShuffleboardContainer.cpp b/wpilibc/src/main/native/cpp/shuffleboard/ShuffleboardContainer.cpp index 84738bcde4..dc01b9f44a 100644 --- a/wpilibc/src/main/native/cpp/shuffleboard/ShuffleboardContainer.cpp +++ b/wpilibc/src/main/native/cpp/shuffleboard/ShuffleboardContainer.cpp @@ -7,6 +7,7 @@ #include #include +#include "frc/Errors.h" #include "frc/shuffleboard/ComplexWidget.h" #include "frc/shuffleboard/ShuffleboardComponent.h" #include "frc/shuffleboard/ShuffleboardLayout.h" @@ -56,8 +57,8 @@ ShuffleboardLayout& ShuffleboardContainer::GetLayout(const wpi::Twine& title) { wpi::SmallVector storage; auto titleRef = title.toStringRef(storage); if (m_layouts.count(titleRef) == 0) { - wpi_setWPIErrorWithContext( - InvalidParameter, "No layout with the given title has been defined"); + throw FRC_MakeError(err::InvalidParameter, + "No layout with the given title has been defined"); } return *m_layouts[titleRef]; } diff --git a/wpilibc/src/main/native/cpp/smartdashboard/SmartDashboard.cpp b/wpilibc/src/main/native/cpp/smartdashboard/SmartDashboard.cpp index 35feb9de11..3cbd29ffd7 100644 --- a/wpilibc/src/main/native/cpp/smartdashboard/SmartDashboard.cpp +++ b/wpilibc/src/main/native/cpp/smartdashboard/SmartDashboard.cpp @@ -10,7 +10,7 @@ #include #include -#include "frc/WPIErrors.h" +#include "frc/Errors.h" #include "frc/smartdashboard/SendableRegistry.h" using namespace frc; @@ -85,9 +85,8 @@ nt::NetworkTableEntry SmartDashboard::GetEntry(wpi::StringRef key) { } void SmartDashboard::PutData(wpi::StringRef key, Sendable* data) { - if (data == nullptr) { - wpi_setGlobalWPIErrorWithContext(NullParameter, "value"); - return; + if (!data) { + throw FRC_MakeError(err::NullParameter, "value"); } auto& inst = Singleton::GetInstance(); std::scoped_lock lock(inst.tablesToDataMutex); @@ -103,9 +102,8 @@ void SmartDashboard::PutData(wpi::StringRef key, Sendable* data) { } void SmartDashboard::PutData(Sendable* value) { - if (value == nullptr) { - wpi_setGlobalWPIErrorWithContext(NullParameter, "value"); - return; + if (!value) { + throw FRC_MakeError(err::NullParameter, "value"); } auto name = SendableRegistry::GetInstance().GetName(value); if (!name.empty()) { @@ -118,8 +116,7 @@ Sendable* SmartDashboard::GetData(wpi::StringRef key) { std::scoped_lock lock(inst.tablesToDataMutex); auto it = inst.tablesToData.find(key); if (it == inst.tablesToData.end()) { - wpi_setGlobalWPIErrorWithContext(SmartDashboardMissingKey, key); - return nullptr; + throw FRC_MakeError(err::SmartDashboardMissingKey, key); } return SendableRegistry::GetInstance().GetSendable(it->getValue()); } diff --git a/wpilibc/src/main/native/cppcs/RobotBase.cpp b/wpilibc/src/main/native/cppcs/RobotBase.cpp index b112901e52..7a1ef244bd 100644 --- a/wpilibc/src/main/native/cppcs/RobotBase.cpp +++ b/wpilibc/src/main/native/cppcs/RobotBase.cpp @@ -18,9 +18,9 @@ #include "WPILibVersion.h" #include "frc/DriverStation.h" +#include "frc/Errors.h" #include "frc/RobotState.h" #include "frc/Utility.h" -#include "frc/WPIErrors.h" #include "frc/livewindow/LiveWindow.h" #include "frc/smartdashboard/SmartDashboard.h" @@ -54,10 +54,10 @@ class WPILibCameraServerShared : public frc::CameraServerShared { HAL_Report(HALUsageReporting::kResourceType_PCVideoServer, id); } void SetCameraServerError(const wpi::Twine& error) override { - wpi_setGlobalWPIErrorWithContext(CameraServerError, error); + FRC_ReportError(err::CameraServerError, error); } void SetVisionRunnerError(const wpi::Twine& error) override { - wpi_setGlobalErrorWithContext(-1, error); + FRC_ReportError(-1, error); } void ReportDriverStationError(const wpi::Twine& error) override { DriverStation::ReportError(error); diff --git a/wpilibc/src/main/native/include/frc/ADXL345_I2C.h b/wpilibc/src/main/native/include/frc/ADXL345_I2C.h index 7820751442..feedd4c67d 100644 --- a/wpilibc/src/main/native/include/frc/ADXL345_I2C.h +++ b/wpilibc/src/main/native/include/frc/ADXL345_I2C.h @@ -6,7 +6,6 @@ #include -#include "frc/ErrorBase.h" #include "frc/I2C.h" #include "frc/interfaces/Accelerometer.h" #include "frc/smartdashboard/Sendable.h" @@ -23,8 +22,7 @@ class SendableBuilder; * an I2C bus. This class assumes the default (not alternate) sensor address of * 0x1D (7-bit address). */ -class ADXL345_I2C : public ErrorBase, - public Accelerometer, +class ADXL345_I2C : public Accelerometer, public Sendable, public SendableHelper { public: diff --git a/wpilibc/src/main/native/include/frc/ADXL345_SPI.h b/wpilibc/src/main/native/include/frc/ADXL345_SPI.h index ee16518760..bc6122343d 100644 --- a/wpilibc/src/main/native/include/frc/ADXL345_SPI.h +++ b/wpilibc/src/main/native/include/frc/ADXL345_SPI.h @@ -6,7 +6,6 @@ #include -#include "frc/ErrorBase.h" #include "frc/SPI.h" #include "frc/interfaces/Accelerometer.h" #include "frc/smartdashboard/Sendable.h" @@ -20,8 +19,7 @@ namespace frc { * This class allows access to an Analog Devices ADXL345 3-axis accelerometer * via SPI. This class assumes the sensor is wired in 4-wire SPI mode. */ -class ADXL345_SPI : public ErrorBase, - public Accelerometer, +class ADXL345_SPI : public Accelerometer, public Sendable, public SendableHelper { public: diff --git a/wpilibc/src/main/native/include/frc/ADXL362.h b/wpilibc/src/main/native/include/frc/ADXL362.h index 39734e1d57..ca9bc9129c 100644 --- a/wpilibc/src/main/native/include/frc/ADXL362.h +++ b/wpilibc/src/main/native/include/frc/ADXL362.h @@ -6,7 +6,6 @@ #include -#include "frc/ErrorBase.h" #include "frc/SPI.h" #include "frc/interfaces/Accelerometer.h" #include "frc/smartdashboard/Sendable.h" @@ -21,8 +20,7 @@ class SendableBuilder; * * This class allows access to an Analog Devices ADXL362 3-axis accelerometer. */ -class ADXL362 : public ErrorBase, - public Accelerometer, +class ADXL362 : public Accelerometer, public Sendable, public SendableHelper { public: diff --git a/wpilibc/src/main/native/include/frc/ADXRS450_Gyro.h b/wpilibc/src/main/native/include/frc/ADXRS450_Gyro.h index 556bb49a2e..69b6492f72 100644 --- a/wpilibc/src/main/native/include/frc/ADXRS450_Gyro.h +++ b/wpilibc/src/main/native/include/frc/ADXRS450_Gyro.h @@ -8,7 +8,6 @@ #include -#include "frc/ErrorBase.h" #include "frc/SPI.h" #include "frc/interfaces/Gyro.h" #include "frc/smartdashboard/Sendable.h" @@ -30,7 +29,6 @@ namespace frc { * Only one instance of an ADXRS Gyro is supported. */ class ADXRS450_Gyro : public Gyro, - public ErrorBase, public Sendable, public SendableHelper { public: diff --git a/wpilibc/src/main/native/include/frc/AddressableLED.h b/wpilibc/src/main/native/include/frc/AddressableLED.h index d0b5428055..2cdff76bf2 100644 --- a/wpilibc/src/main/native/include/frc/AddressableLED.h +++ b/wpilibc/src/main/native/include/frc/AddressableLED.h @@ -11,7 +11,6 @@ #include #include -#include "frc/ErrorBase.h" #include "util/Color.h" #include "util/Color8Bit.h" @@ -22,7 +21,7 @@ namespace frc { * *

    Only 1 LED driver is currently supported by the roboRIO. */ -class AddressableLED : public ErrorBase { +class AddressableLED { public: class LEDData : public HAL_AddressableLEDData { public: @@ -86,7 +85,7 @@ class AddressableLED : public ErrorBase { */ explicit AddressableLED(int port); - ~AddressableLED() override; + ~AddressableLED(); /** * Sets the length of the LED strip. diff --git a/wpilibc/src/main/native/include/frc/AnalogAccelerometer.h b/wpilibc/src/main/native/include/frc/AnalogAccelerometer.h index f157cd5d6a..acc5c5f8d1 100644 --- a/wpilibc/src/main/native/include/frc/AnalogAccelerometer.h +++ b/wpilibc/src/main/native/include/frc/AnalogAccelerometer.h @@ -7,7 +7,6 @@ #include #include "frc/AnalogInput.h" -#include "frc/ErrorBase.h" #include "frc/smartdashboard/Sendable.h" #include "frc/smartdashboard/SendableHelper.h" @@ -22,8 +21,7 @@ class SendableBuilder; * sensors have multiple axis and can be treated as multiple devices. Each is * calibrated by finding the center value over a period of time. */ -class AnalogAccelerometer : public ErrorBase, - public Sendable, +class AnalogAccelerometer : public Sendable, public SendableHelper { public: /** diff --git a/wpilibc/src/main/native/include/frc/AnalogEncoder.h b/wpilibc/src/main/native/include/frc/AnalogEncoder.h index 40438e6460..1ac341412b 100644 --- a/wpilibc/src/main/native/include/frc/AnalogEncoder.h +++ b/wpilibc/src/main/native/include/frc/AnalogEncoder.h @@ -12,7 +12,6 @@ #include "frc/AnalogTrigger.h" #include "frc/Counter.h" -#include "frc/ErrorBase.h" #include "frc/smartdashboard/Sendable.h" #include "frc/smartdashboard/SendableHelper.h" @@ -22,9 +21,7 @@ class AnalogInput; /** * Class for supporting continuous analog encoders, such as the US Digital MA3. */ -class AnalogEncoder : public ErrorBase, - public Sendable, - public SendableHelper { +class AnalogEncoder : public Sendable, public SendableHelper { public: /** * Construct a new AnalogEncoder attached to a specific AnalogInput. diff --git a/wpilibc/src/main/native/include/frc/AnalogGyro.h b/wpilibc/src/main/native/include/frc/AnalogGyro.h index e2646cec5a..ef36ab0126 100644 --- a/wpilibc/src/main/native/include/frc/AnalogGyro.h +++ b/wpilibc/src/main/native/include/frc/AnalogGyro.h @@ -8,7 +8,6 @@ #include -#include "frc/ErrorBase.h" #include "frc/interfaces/Gyro.h" #include "frc/smartdashboard/Sendable.h" #include "frc/smartdashboard/SendableHelper.h" @@ -31,7 +30,6 @@ class AnalogInput; * This class is for gyro sensors that connect to an analog input. */ class AnalogGyro : public Gyro, - public ErrorBase, public Sendable, public SendableHelper { public: diff --git a/wpilibc/src/main/native/include/frc/AnalogInput.h b/wpilibc/src/main/native/include/frc/AnalogInput.h index 7a9ebeeb6d..6b1828225e 100644 --- a/wpilibc/src/main/native/include/frc/AnalogInput.h +++ b/wpilibc/src/main/native/include/frc/AnalogInput.h @@ -8,7 +8,6 @@ #include -#include "frc/ErrorBase.h" #include "frc/smartdashboard/Sendable.h" #include "frc/smartdashboard/SendableHelper.h" @@ -30,9 +29,7 @@ class DMASample; * are divided by the number of samples to retain the resolution, but get more * stable values. */ -class AnalogInput : public ErrorBase, - public Sendable, - public SendableHelper { +class AnalogInput : public Sendable, public SendableHelper { friend class AnalogTrigger; friend class AnalogGyro; friend class DMA; diff --git a/wpilibc/src/main/native/include/frc/AnalogOutput.h b/wpilibc/src/main/native/include/frc/AnalogOutput.h index 6120059e2b..f93511a903 100644 --- a/wpilibc/src/main/native/include/frc/AnalogOutput.h +++ b/wpilibc/src/main/native/include/frc/AnalogOutput.h @@ -6,7 +6,6 @@ #include -#include "frc/ErrorBase.h" #include "frc/smartdashboard/Sendable.h" #include "frc/smartdashboard/SendableHelper.h" @@ -17,9 +16,7 @@ class SendableBuilder; /** * MXP analog output class. */ -class AnalogOutput : public ErrorBase, - public Sendable, - public SendableHelper { +class AnalogOutput : public Sendable, public SendableHelper { public: /** * Construct an analog output on the given channel. diff --git a/wpilibc/src/main/native/include/frc/AnalogPotentiometer.h b/wpilibc/src/main/native/include/frc/AnalogPotentiometer.h index c6034e06ce..8c470bbf45 100644 --- a/wpilibc/src/main/native/include/frc/AnalogPotentiometer.h +++ b/wpilibc/src/main/native/include/frc/AnalogPotentiometer.h @@ -7,7 +7,6 @@ #include #include "frc/AnalogInput.h" -#include "frc/ErrorBase.h" #include "frc/smartdashboard/Sendable.h" #include "frc/smartdashboard/SendableHelper.h" @@ -21,8 +20,7 @@ class SendableBuilder; * units you choose, by way of the scaling and offset constants passed to the * constructor. */ -class AnalogPotentiometer : public ErrorBase, - public Sendable, +class AnalogPotentiometer : public Sendable, public SendableHelper { public: /** diff --git a/wpilibc/src/main/native/include/frc/AnalogTrigger.h b/wpilibc/src/main/native/include/frc/AnalogTrigger.h index 6cb6ec0959..6fb41cf0c2 100644 --- a/wpilibc/src/main/native/include/frc/AnalogTrigger.h +++ b/wpilibc/src/main/native/include/frc/AnalogTrigger.h @@ -9,7 +9,6 @@ #include #include "frc/AnalogTriggerOutput.h" -#include "frc/ErrorBase.h" #include "frc/smartdashboard/Sendable.h" #include "frc/smartdashboard/SendableHelper.h" @@ -19,9 +18,7 @@ class AnalogInput; class DutyCycle; class SendableBuilder; -class AnalogTrigger : public ErrorBase, - public Sendable, - public SendableHelper { +class AnalogTrigger : public Sendable, public SendableHelper { friend class AnalogTriggerOutput; public: diff --git a/wpilibc/src/main/native/include/frc/BuiltInAccelerometer.h b/wpilibc/src/main/native/include/frc/BuiltInAccelerometer.h index 8a55a978f1..517b7ae483 100644 --- a/wpilibc/src/main/native/include/frc/BuiltInAccelerometer.h +++ b/wpilibc/src/main/native/include/frc/BuiltInAccelerometer.h @@ -4,7 +4,6 @@ #pragma once -#include "frc/ErrorBase.h" #include "frc/interfaces/Accelerometer.h" #include "frc/smartdashboard/Sendable.h" #include "frc/smartdashboard/SendableHelper.h" @@ -18,8 +17,7 @@ class SendableBuilder; * * This class allows access to the roboRIO's internal accelerometer. */ -class BuiltInAccelerometer : public ErrorBase, - public Accelerometer, +class BuiltInAccelerometer : public Accelerometer, public Sendable, public SendableHelper { public: diff --git a/wpilibc/src/main/native/include/frc/CAN.h b/wpilibc/src/main/native/include/frc/CAN.h index 495e782992..4c9b9bd917 100644 --- a/wpilibc/src/main/native/include/frc/CAN.h +++ b/wpilibc/src/main/native/include/frc/CAN.h @@ -8,8 +8,6 @@ #include -#include "frc/ErrorBase.h" - namespace frc { struct CANData { uint8_t data[8]; @@ -27,7 +25,7 @@ struct CANData { * All methods are thread save, however the buffer objects passed in * by the user need to not be modified for the duration of their calls. */ -class CAN : public ErrorBase { +class CAN { public: /** * Create a new CAN communication interface with the specific device ID. @@ -52,7 +50,7 @@ class CAN : public ErrorBase { /** * Closes the CAN communication. */ - ~CAN() override; + ~CAN(); CAN(CAN&&) = default; CAN& operator=(CAN&&) = default; diff --git a/wpilibc/src/main/native/include/frc/Compressor.h b/wpilibc/src/main/native/include/frc/Compressor.h index 96b1be5738..e83ba0f611 100644 --- a/wpilibc/src/main/native/include/frc/Compressor.h +++ b/wpilibc/src/main/native/include/frc/Compressor.h @@ -6,7 +6,6 @@ #include -#include "frc/ErrorBase.h" #include "frc/SensorUtil.h" #include "frc/smartdashboard/Sendable.h" #include "frc/smartdashboard/SendableHelper.h" @@ -30,9 +29,7 @@ class SendableBuilder; * loop control. You can only turn off closed loop control, thereby stopping * the compressor from operating. */ -class Compressor : public ErrorBase, - public Sendable, - public SendableHelper { +class Compressor : public Sendable, public SendableHelper { public: /** * Constructor. The default PCM ID is 0. diff --git a/wpilibc/src/main/native/include/frc/Counter.h b/wpilibc/src/main/native/include/frc/Counter.h index dc0f4179e0..c15814e293 100644 --- a/wpilibc/src/main/native/include/frc/Counter.h +++ b/wpilibc/src/main/native/include/frc/Counter.h @@ -10,7 +10,6 @@ #include "frc/AnalogTrigger.h" #include "frc/CounterBase.h" -#include "frc/ErrorBase.h" #include "frc/smartdashboard/Sendable.h" #include "frc/smartdashboard/SendableHelper.h" @@ -31,8 +30,7 @@ class DMASample; * All counters will immediately start counting - Reset() them if you need them * to be zeroed before use. */ -class Counter : public ErrorBase, - public CounterBase, +class Counter : public CounterBase, public Sendable, public SendableHelper { friend class DMA; diff --git a/wpilibc/src/main/native/include/frc/DMA.h b/wpilibc/src/main/native/include/frc/DMA.h index 7646fa8b81..72c3829215 100644 --- a/wpilibc/src/main/native/include/frc/DMA.h +++ b/wpilibc/src/main/native/include/frc/DMA.h @@ -6,8 +6,6 @@ #include -#include "frc/ErrorBase.h" - namespace frc { class Encoder; class Counter; @@ -16,12 +14,12 @@ class DutyCycle; class AnalogInput; class DMASample; -class DMA : public ErrorBase { +class DMA { friend class DMASample; public: DMA(); - ~DMA() override; + ~DMA(); DMA& operator=(DMA&& other) = default; DMA(DMA&& other) = default; diff --git a/wpilibc/src/main/native/include/frc/DigitalGlitchFilter.h b/wpilibc/src/main/native/include/frc/DigitalGlitchFilter.h index 3fce2d083f..e75ea3923b 100644 --- a/wpilibc/src/main/native/include/frc/DigitalGlitchFilter.h +++ b/wpilibc/src/main/native/include/frc/DigitalGlitchFilter.h @@ -11,7 +11,6 @@ #include #include "frc/DigitalSource.h" -#include "frc/ErrorBase.h" #include "frc/smartdashboard/Sendable.h" #include "frc/smartdashboard/SendableHelper.h" @@ -27,8 +26,7 @@ class Counter; * filter. The filter lets the user configure the time that an input must remain * high or low before it is classified as high or low. */ -class DigitalGlitchFilter : public ErrorBase, - public Sendable, +class DigitalGlitchFilter : public Sendable, public SendableHelper { public: DigitalGlitchFilter(); diff --git a/wpilibc/src/main/native/include/frc/DriverStation.h b/wpilibc/src/main/native/include/frc/DriverStation.h index ba521c7be0..91e36a6503 100644 --- a/wpilibc/src/main/native/include/frc/DriverStation.h +++ b/wpilibc/src/main/native/include/frc/DriverStation.h @@ -15,8 +15,6 @@ #include #include -#include "frc/ErrorBase.h" - namespace frc { class MatchDataSender; @@ -25,12 +23,12 @@ class MatchDataSender; * Provide access to the network communication data to / from the Driver * Station. */ -class DriverStation : public ErrorBase { +class DriverStation { public: enum Alliance { kRed, kBlue, kInvalid }; enum MatchType { kNone, kPractice, kQualification, kElimination }; - ~DriverStation() override; + ~DriverStation(); DriverStation(const DriverStation&) = delete; DriverStation& operator=(const DriverStation&) = delete; diff --git a/wpilibc/src/main/native/include/frc/DutyCycle.h b/wpilibc/src/main/native/include/frc/DutyCycle.h index 8b02e875bf..0cdf8c3019 100644 --- a/wpilibc/src/main/native/include/frc/DutyCycle.h +++ b/wpilibc/src/main/native/include/frc/DutyCycle.h @@ -8,7 +8,6 @@ #include -#include "frc/ErrorBase.h" #include "frc/smartdashboard/Sendable.h" #include "frc/smartdashboard/SendableHelper.h" @@ -29,9 +28,7 @@ class DMASample; * order to implement rollover checking. * */ -class DutyCycle : public ErrorBase, - public Sendable, - public SendableHelper { +class DutyCycle : public Sendable, public SendableHelper { friend class AnalogTrigger; friend class DMA; friend class DMASample; diff --git a/wpilibc/src/main/native/include/frc/DutyCycleEncoder.h b/wpilibc/src/main/native/include/frc/DutyCycleEncoder.h index 87789f2fc2..fc1d0108ea 100644 --- a/wpilibc/src/main/native/include/frc/DutyCycleEncoder.h +++ b/wpilibc/src/main/native/include/frc/DutyCycleEncoder.h @@ -12,7 +12,6 @@ #include "frc/AnalogTrigger.h" #include "frc/Counter.h" -#include "frc/ErrorBase.h" #include "frc/smartdashboard/Sendable.h" #include "frc/smartdashboard/SendableHelper.h" @@ -25,8 +24,7 @@ class DigitalSource; * PWM Output, the CTRE Mag Encoder, the Rev Hex Encoder, and the AM Mag * Encoder. */ -class DutyCycleEncoder : public ErrorBase, - public Sendable, +class DutyCycleEncoder : public Sendable, public SendableHelper { public: /** diff --git a/wpilibc/src/main/native/include/frc/Encoder.h b/wpilibc/src/main/native/include/frc/Encoder.h index db96187d6a..50f8cbf152 100644 --- a/wpilibc/src/main/native/include/frc/Encoder.h +++ b/wpilibc/src/main/native/include/frc/Encoder.h @@ -10,7 +10,6 @@ #include "frc/Counter.h" #include "frc/CounterBase.h" -#include "frc/ErrorBase.h" #include "frc/smartdashboard/Sendable.h" #include "frc/smartdashboard/SendableHelper.h" @@ -37,8 +36,7 @@ class DMASample; * All encoders will immediately start counting - Reset() them if you need them * to be zeroed before use. */ -class Encoder : public ErrorBase, - public CounterBase, +class Encoder : public CounterBase, public Sendable, public SendableHelper { friend class DMA; diff --git a/wpilibc/src/main/native/include/frc/Error.h b/wpilibc/src/main/native/include/frc/Error.h deleted file mode 100644 index 812d1491bb..0000000000 --- a/wpilibc/src/main/native/include/frc/Error.h +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright (c) FIRST and other WPILib contributors. -// Open Source Software; you can modify and/or share it under the terms of -// the WPILib BSD license file in the root directory of this project. - -#pragma once - -#include - -#include -#include - -#ifdef _WIN32 -#pragma push_macro("GetMessage") -#undef GetMessage -#endif - -namespace frc { - -class ErrorBase; - -/** - * Error object represents a library error. - */ -class Error { - public: - using Code = int; - - Error() = default; - Error(Code code, const wpi::Twine& contextMessage, wpi::StringRef filename, - wpi::StringRef function, int lineNumber, - const ErrorBase* originatingObject); - - bool operator<(const Error& rhs) const; - - Code GetCode() const; - std::string GetMessage() const; - std::string GetFilename() const; - std::string GetFunction() const; - int GetLineNumber() const; - const ErrorBase* GetOriginatingObject() const; - double GetTimestamp() const; - void Clear(); - void Set(Code code, const wpi::Twine& contextMessage, wpi::StringRef filename, - wpi::StringRef function, int lineNumber, - const ErrorBase* originatingObject); - - private: - void Report(); - - Code m_code = 0; - std::string m_message; - std::string m_filename; - std::string m_function; - int m_lineNumber = 0; - const ErrorBase* m_originatingObject = nullptr; - double m_timestamp = 0.0; -}; - -} // namespace frc - -#ifdef _WIN32 -#pragma pop_macro("GetMessage") -#endif diff --git a/wpilibc/src/main/native/include/frc/ErrorBase.h b/wpilibc/src/main/native/include/frc/ErrorBase.h deleted file mode 100644 index f9e8b00bf4..0000000000 --- a/wpilibc/src/main/native/include/frc/ErrorBase.h +++ /dev/null @@ -1,239 +0,0 @@ -// Copyright (c) FIRST and other WPILib contributors. -// Open Source Software; you can modify and/or share it under the terms of -// the WPILib BSD license file in the root directory of this project. - -#pragma once - -#include - -#include -#include - -#include "frc/Error.h" - -// Forward declared manually to avoid needing to pull in entire HAL header. -extern "C" const char* HAL_GetErrorMessage(int32_t code); - -#define wpi_setErrnoErrorWithContext(context) \ - this->SetErrnoError((context), __FILE__, __FUNCTION__, __LINE__) -#define wpi_setErrnoError() wpi_setErrnoErrorWithContext("") -#define wpi_setImaqErrorWithContext(code, context) \ - do { \ - if ((code) != 0) \ - this->SetImaqError((code), (context), __FILE__, __FUNCTION__, __LINE__); \ - } while (0) -#define wpi_setErrorWithContext(code, context) \ - do { \ - if ((code) != 0) \ - this->SetError((code), (context), __FILE__, __FUNCTION__, __LINE__); \ - } while (0) -#define wpi_setErrorWithContextRange(code, min, max, req, context) \ - do { \ - if ((code) != 0) \ - this->SetErrorRange((code), (min), (max), (req), (context), __FILE__, \ - __FUNCTION__, __LINE__); \ - } while (0) - -#define wpi_setHALError(code) \ - do { \ - if ((code) != 0) \ - this->SetError((code), HAL_GetErrorMessage(code), __FILE__, \ - __FUNCTION__, __LINE__); \ - } while (0) - -#define wpi_setHALErrorWithRange(code, min, max, req) \ - do { \ - if ((code) != 0) \ - this->SetErrorRange((code), (min), (max), (req), \ - HAL_GetErrorMessage(code), __FILE__, __FUNCTION__, \ - __LINE__); \ - } while (0) - -#define wpi_setError(code) wpi_setErrorWithContext(code, "") -#define wpi_setStaticErrorWithContext(object, code, context) \ - do { \ - if ((code) != 0) \ - object->SetError((code), (context), __FILE__, __FUNCTION__, __LINE__); \ - } while (0) -#define wpi_setStaticError(object, code) \ - wpi_setStaticErrorWithContext(object, code, "") - -#define wpi_setGlobalErrorWithContext(code, context) \ - do { \ - if ((code) != 0) \ - ::frc::ErrorBase::SetGlobalError((code), (context), __FILE__, \ - __FUNCTION__, __LINE__); \ - } while (0) - -#define wpi_setGlobalHALError(code) \ - do { \ - if ((code) != 0) \ - ::frc::ErrorBase::SetGlobalError((code), HAL_GetErrorMessage(code), \ - __FILE__, __FUNCTION__, __LINE__); \ - } while (0) - -#define wpi_setGlobalError(code) wpi_setGlobalErrorWithContext(code, "") -#define wpi_setWPIErrorWithContext(error, context) \ - this->SetWPIError(wpi_error_s_##error(), wpi_error_value_##error(), \ - (context), __FILE__, __FUNCTION__, __LINE__) -#define wpi_setWPIError(error) (wpi_setWPIErrorWithContext(error, "")) -#define wpi_setStaticWPIErrorWithContext(object, error, context) \ - object->SetWPIError(wpi_error_s_##error(), (context), __FILE__, \ - __FUNCTION__, __LINE__) -#define wpi_setStaticWPIError(object, error) \ - wpi_setStaticWPIErrorWithContext(object, error, "") -#define wpi_setGlobalWPIErrorWithContext(error, context) \ - ::frc::ErrorBase::SetGlobalWPIError(wpi_error_s_##error(), (context), \ - __FILE__, __FUNCTION__, __LINE__) -#define wpi_setGlobalWPIError(error) wpi_setGlobalWPIErrorWithContext(error, "") - -namespace frc { - -/** - * Base class for most objects. - * - * ErrorBase is the base class for most objects since it holds the generated - * error for that object. In addition, there is a single instance of a global - * error object. - */ -class ErrorBase { - // TODO: Consider initializing instance variables and cleanup in destructor - public: - ErrorBase(); - virtual ~ErrorBase() = default; - - ErrorBase(const ErrorBase&) = default; - ErrorBase& operator=(const ErrorBase&) = default; - ErrorBase(ErrorBase&&) = default; - ErrorBase& operator=(ErrorBase&&) = default; - - /** - * @brief Retrieve the current error. - * - * Get the current error information associated with this sensor. - */ - virtual Error& GetError(); - - /** - * @brief Retrieve the current error. - * - * Get the current error information associated with this sensor. - */ - virtual const Error& GetError() const; - - /** - * @brief Clear the current error information associated with this sensor. - */ - virtual void ClearError() const; - - /** - * @brief Set error information associated with a C library call that set an - * error to the "errno" global variable. - * - * @param contextMessage A custom message from the code that set the error. - * @param filename Filename of the error source - * @param function Function of the error source - * @param lineNumber Line number of the error source - */ - virtual void SetErrnoError(const wpi::Twine& contextMessage, - wpi::StringRef filename, wpi::StringRef function, - int lineNumber) const; - - /** - * @brief Set the current error information associated from the nivision Imaq - * API. - * - * @param success The return from the function - * @param contextMessage A custom message from the code that set the error. - * @param filename Filename of the error source - * @param function Function of the error source - * @param lineNumber Line number of the error source - */ - virtual void SetImaqError(int success, const wpi::Twine& contextMessage, - wpi::StringRef filename, wpi::StringRef function, - int lineNumber) const; - - /** - * @brief Set the current error information associated with this sensor. - * - * @param code The error code - * @param contextMessage A custom message from the code that set the error. - * @param filename Filename of the error source - * @param function Function of the error source - * @param lineNumber Line number of the error source - */ - virtual void SetError(Error::Code code, const wpi::Twine& contextMessage, - wpi::StringRef filename, wpi::StringRef function, - int lineNumber) const; - - /** - * @brief Set the current error information associated with this sensor. - * Range versions use for initialization code. - * - * @param code The error code - * @param minRange The minimum allowed allocation range - * @param maxRange The maximum allowed allocation range - * @param requestedValue The requested value to allocate - * @param contextMessage A custom message from the code that set the error. - * @param filename Filename of the error source - * @param function Function of the error source - * @param lineNumber Line number of the error source - */ - virtual void SetErrorRange(Error::Code code, int32_t minRange, - int32_t maxRange, int32_t requestedValue, - const wpi::Twine& contextMessage, - wpi::StringRef filename, wpi::StringRef function, - int lineNumber) const; - - /** - * @brief Set the current error information associated with this sensor. - * - * @param errorMessage The error message from WPIErrors.h - * @param contextMessage A custom message from the code that set the error. - * @param filename Filename of the error source - * @param function Function of the error source - * @param lineNumber Line number of the error source - */ - virtual void SetWPIError(const wpi::Twine& errorMessage, Error::Code code, - const wpi::Twine& contextMessage, - wpi::StringRef filename, wpi::StringRef function, - int lineNumber) const; - - virtual void CloneError(const ErrorBase& rhs) const; - - /** - * @brief Check if the current error code represents a fatal error. - * - * @return true if the current error is fatal. - */ - virtual bool StatusIsFatal() const; - - static void SetGlobalError(Error::Code code, const wpi::Twine& contextMessage, - wpi::StringRef filename, wpi::StringRef function, - int lineNumber); - - static void SetGlobalWPIError(const wpi::Twine& errorMessage, - const wpi::Twine& contextMessage, - wpi::StringRef filename, - wpi::StringRef function, int lineNumber); - - /** - * Retrieve the last global error. - */ - static Error GetGlobalError(); - - /** - * Retrieve all global errors. - */ - static std::vector GetGlobalErrors(); - - /** - * Clear global errors. - */ - void ClearGlobalErrors(); - - protected: - mutable Error m_error; -}; - -} // namespace frc diff --git a/wpilibc/src/main/native/include/frc/Errors.h b/wpilibc/src/main/native/include/frc/Errors.h new file mode 100644 index 0000000000..9b16072298 --- /dev/null +++ b/wpilibc/src/main/native/include/frc/Errors.h @@ -0,0 +1,139 @@ +// Copyright (c) FIRST and other WPILib contributors. +// Open Source Software; you can modify and/or share it under the terms of +// the WPILib BSD license file in the root directory of this project. + +#pragma once + +#include + +#include +#include +#include + +#include + +namespace frc { + +/** + * Runtime error exception. + */ +class RuntimeError : public std::runtime_error { + public: + RuntimeError(int32_t code, const wpi::Twine& message, const wpi::Twine& loc, + wpi::StringRef stack); + RuntimeError(int32_t code, const wpi::Twine& message, const char* fileName, + int lineNumber, const char* funcName, wpi::StringRef stack); + + int32_t code() const noexcept { return m_data->code; } + const char* loc() const noexcept { return m_data->loc.c_str(); } + const char* stack() const noexcept { return m_data->stack.c_str(); } + + /** + * Reports error to Driver Station (using HAL_SendError). + */ + void Report() const; + + private: + struct Data { + int32_t code; + std::string loc; + std::string stack; + }; + std::shared_ptr m_data; +}; + +/** + * Gets error message string for an error code. + */ +const char* GetErrorMessage(int32_t code); + +/** + * Reports an error to the driver station (using HAL_SendError). + * Generally the FRC_ReportError wrapper macro should be used instead. + * + * @param status error code + * @param message error message details + * @param fileName source file name + * @param lineNumber source line number + * @param funcName source function name + */ +void ReportError(int32_t status, const wpi::Twine& message, + const char* fileName, int lineNumber, const char* funcName); + +/** + * Makes a runtime error exception object. This object should be thrown + * by the caller. Generally the FRC_MakeError wrapper macro should be used + * instead. + * + * @param status error code + * @param message error message details + * @param fileName source file name + * @param lineNumber source line number + * @param funcName source function name + * @return runtime error object + */ +[[nodiscard]] RuntimeError MakeError(int32_t status, const wpi::Twine& message, + const char* fileName, int lineNumber, + const char* funcName); + +namespace err { +#define S(label, offset, message) inline constexpr int label = offset; +#include "frc/WPIErrors.mac" +#undef S +} // namespace err + +namespace warn { +#define S(label, offset, message) inline constexpr int label = offset; +#include "frc/WPIWarnings.mac" +#undef S +} // namespace warn +} // namespace frc + +/** + * Reports an error to the driver station (using HAL_SendError). + * + * @param status error code + * @param message error message details + */ +#define FRC_ReportError(status, message) \ + do { \ + if ((status) != 0) { \ + ::frc::ReportError(status, message, __FILE__, __LINE__, __FUNCTION__); \ + } \ + } while (0) + +/** + * Makes a runtime error exception object. This object should be thrown + * by the caller. + * + * @param status error code + * @param message error message details + * @return runtime error object + */ +#define FRC_MakeError(status, message) \ + ::frc::MakeError(status, message, __FILE__, __LINE__, __FUNCTION__) + +/** + * Checks a status code and depending on its value, either throws a + * RuntimeError exception, calls ReportError, or does nothing (if no error). + * + * @param status error code + * @param message error message details + */ +#define FRC_CheckErrorStatus(status, message) \ + do { \ + if ((status) < 0) { \ + throw FRC_MakeError(status, message); \ + } else if ((status) > 0) { \ + FRC_ReportError(status, message); \ + } \ + } while (0) + +#define FRC_AssertMessage(condition, message) \ + do { \ + if (!(condition)) { \ + throw FRC_MakeError(err::AssertionFailure, message); \ + } \ + } while (0) + +#define FRC_Assert(condition) FRC_AssertMessage(condition, #condition) diff --git a/wpilibc/src/main/native/include/frc/GenericHID.h b/wpilibc/src/main/native/include/frc/GenericHID.h index e441204034..880782b1d9 100644 --- a/wpilibc/src/main/native/include/frc/GenericHID.h +++ b/wpilibc/src/main/native/include/frc/GenericHID.h @@ -8,8 +8,6 @@ #include -#include "frc/ErrorBase.h" - namespace frc { class DriverStation; @@ -17,7 +15,7 @@ class DriverStation; /** * GenericHID Interface. */ -class GenericHID : public ErrorBase { +class GenericHID { public: enum RumbleType { kLeftRumble, kRightRumble }; @@ -44,7 +42,7 @@ class GenericHID : public ErrorBase { enum JoystickHand { kLeftHand = 0, kRightHand = 1 }; explicit GenericHID(int port); - ~GenericHID() override = default; + virtual ~GenericHID() = default; GenericHID(GenericHID&&) = default; GenericHID& operator=(GenericHID&&) = default; diff --git a/wpilibc/src/main/native/include/frc/I2C.h b/wpilibc/src/main/native/include/frc/I2C.h index f62aaf0731..5031673160 100644 --- a/wpilibc/src/main/native/include/frc/I2C.h +++ b/wpilibc/src/main/native/include/frc/I2C.h @@ -8,8 +8,6 @@ #include -#include "frc/ErrorBase.h" - namespace frc { /** @@ -18,7 +16,7 @@ namespace frc { * This class is intended to be used by sensor (and other I2C device) drivers. * It probably should not be used directly. */ -class I2C : public ErrorBase { +class I2C { public: enum Port { kOnboard = 0, kMXP }; @@ -30,7 +28,7 @@ class I2C : public ErrorBase { */ I2C(Port port, int deviceAddress); - ~I2C() override; + ~I2C(); I2C(I2C&&) = default; I2C& operator=(I2C&&) = default; diff --git a/wpilibc/src/main/native/include/frc/InterruptableSensorBase.h b/wpilibc/src/main/native/include/frc/InterruptableSensorBase.h index 6e361ab26d..cefc2d2d0c 100644 --- a/wpilibc/src/main/native/include/frc/InterruptableSensorBase.h +++ b/wpilibc/src/main/native/include/frc/InterruptableSensorBase.h @@ -10,11 +10,10 @@ #include #include "frc/AnalogTriggerType.h" -#include "frc/ErrorBase.h" namespace frc { -class InterruptableSensorBase : public ErrorBase { +class InterruptableSensorBase { public: enum WaitResult { kTimeout = 0x0, @@ -35,7 +34,7 @@ class InterruptableSensorBase : public ErrorBase { /** * Free the resources for an interrupt event. */ - ~InterruptableSensorBase() override; + virtual ~InterruptableSensorBase(); InterruptableSensorBase(InterruptableSensorBase&&) = default; InterruptableSensorBase& operator=(InterruptableSensorBase&&) = default; diff --git a/wpilibc/src/main/native/include/frc/MotorSafety.h b/wpilibc/src/main/native/include/frc/MotorSafety.h index b23a29c7ef..0f56b42f40 100644 --- a/wpilibc/src/main/native/include/frc/MotorSafety.h +++ b/wpilibc/src/main/native/include/frc/MotorSafety.h @@ -6,7 +6,6 @@ #include -#include "frc/ErrorBase.h" #include "frc/Timer.h" namespace wpi { @@ -21,10 +20,10 @@ namespace frc { * * The subclass should call Feed() whenever the motor value is updated. */ -class MotorSafety : public ErrorBase { +class MotorSafety { public: MotorSafety(); - ~MotorSafety() override; + virtual ~MotorSafety(); MotorSafety(MotorSafety&& rhs); MotorSafety& operator=(MotorSafety&& rhs); diff --git a/wpilibc/src/main/native/include/frc/Notifier.h b/wpilibc/src/main/native/include/frc/Notifier.h index df8c37ca53..fee783b937 100644 --- a/wpilibc/src/main/native/include/frc/Notifier.h +++ b/wpilibc/src/main/native/include/frc/Notifier.h @@ -18,11 +18,9 @@ #include #include -#include "frc/ErrorBase.h" - namespace frc { -class Notifier : public ErrorBase { +class Notifier { public: /** * Create a Notifier for timer event notification. @@ -63,7 +61,7 @@ class Notifier : public ErrorBase { /** * Free the resources for a timer event. */ - ~Notifier() override; + ~Notifier(); Notifier(Notifier&& rhs); Notifier& operator=(Notifier&& rhs); diff --git a/wpilibc/src/main/native/include/frc/PWM.h b/wpilibc/src/main/native/include/frc/PWM.h index 869b6453b9..65de64ba55 100644 --- a/wpilibc/src/main/native/include/frc/PWM.h +++ b/wpilibc/src/main/native/include/frc/PWM.h @@ -8,7 +8,6 @@ #include -#include "frc/ErrorBase.h" #include "frc/smartdashboard/Sendable.h" #include "frc/smartdashboard/SendableHelper.h" @@ -33,7 +32,7 @@ class SendableBuilder; * - 1 = minimum pulse width (currently 0.5ms) * - 0 = disabled (i.e. PWM output is held low) */ -class PWM : public ErrorBase, public Sendable, public SendableHelper { +class PWM : public Sendable, public SendableHelper { public: friend class AddressableLED; /** diff --git a/wpilibc/src/main/native/include/frc/PowerDistributionPanel.h b/wpilibc/src/main/native/include/frc/PowerDistributionPanel.h index d40b988989..ba285bc08f 100644 --- a/wpilibc/src/main/native/include/frc/PowerDistributionPanel.h +++ b/wpilibc/src/main/native/include/frc/PowerDistributionPanel.h @@ -6,7 +6,6 @@ #include -#include "frc/ErrorBase.h" #include "frc/smartdashboard/Sendable.h" #include "frc/smartdashboard/SendableHelper.h" @@ -18,8 +17,7 @@ class SendableBuilder; * Class for getting voltage, current, temperature, power and energy from the * CAN PDP. */ -class PowerDistributionPanel : public ErrorBase, - public Sendable, +class PowerDistributionPanel : public Sendable, public SendableHelper { public: PowerDistributionPanel(); diff --git a/wpilibc/src/main/native/include/frc/Preferences.h b/wpilibc/src/main/native/include/frc/Preferences.h index 0d192b7aba..7ecf25a008 100644 --- a/wpilibc/src/main/native/include/frc/Preferences.h +++ b/wpilibc/src/main/native/include/frc/Preferences.h @@ -12,8 +12,6 @@ #include -#include "frc/ErrorBase.h" - namespace frc { /** @@ -30,7 +28,7 @@ namespace frc { * This will also interact with {@link NetworkTable} by creating a table called * "Preferences" with all the key-value pairs. */ -class Preferences : public ErrorBase { +class Preferences { public: /** * Get the one and only {@link Preferences} object. @@ -226,7 +224,7 @@ class Preferences : public ErrorBase { protected: Preferences(); - ~Preferences() override; + ~Preferences(); Preferences(Preferences&&) = default; Preferences& operator=(Preferences&&) = default; diff --git a/wpilibc/src/main/native/include/frc/Relay.h b/wpilibc/src/main/native/include/frc/Relay.h index 14a59a8012..b623701649 100644 --- a/wpilibc/src/main/native/include/frc/Relay.h +++ b/wpilibc/src/main/native/include/frc/Relay.h @@ -9,7 +9,6 @@ #include #include -#include "frc/ErrorBase.h" #include "frc/MotorSafety.h" #include "frc/smartdashboard/Sendable.h" #include "frc/smartdashboard/SendableHelper.h" diff --git a/wpilibc/src/main/native/include/frc/Resource.h b/wpilibc/src/main/native/include/frc/Resource.h index 6880d65265..4109cc461c 100644 --- a/wpilibc/src/main/native/include/frc/Resource.h +++ b/wpilibc/src/main/native/include/frc/Resource.h @@ -12,8 +12,6 @@ #include -#include "frc/ErrorBase.h" - namespace frc { /** @@ -26,9 +24,9 @@ namespace frc { * resources; it just tracks which indices were marked in use by Allocate and * not yet freed by Free. */ -class Resource : public ErrorBase { +class Resource { public: - ~Resource() override = default; + virtual ~Resource() = default; /** * Factory method to create a Resource allocation-tracker *if* needed. diff --git a/wpilibc/src/main/native/include/frc/RobotBase.h b/wpilibc/src/main/native/include/frc/RobotBase.h index ffc59eec50..c477438639 100644 --- a/wpilibc/src/main/native/include/frc/RobotBase.h +++ b/wpilibc/src/main/native/include/frc/RobotBase.h @@ -14,6 +14,7 @@ #include #include "frc/Base.h" +#include "frc/Errors.h" namespace frc { @@ -25,12 +26,17 @@ namespace impl { template void RunRobot(wpi::mutex& m, Robot** robot) { - static Robot theRobot; - { - std::scoped_lock lock{m}; - *robot = &theRobot; + try { + static Robot theRobot; + { + std::scoped_lock lock{m}; + *robot = &theRobot; + } + theRobot.StartCompetition(); + } catch (const frc::RuntimeError& e) { + e.Report(); + throw; } - theRobot.StartCompetition(); } } // namespace impl diff --git a/wpilibc/src/main/native/include/frc/SPI.h b/wpilibc/src/main/native/include/frc/SPI.h index 59fe0db418..be9d6ef8f3 100644 --- a/wpilibc/src/main/native/include/frc/SPI.h +++ b/wpilibc/src/main/native/include/frc/SPI.h @@ -13,8 +13,6 @@ #include #include -#include "frc/ErrorBase.h" - namespace frc { class DigitalSource; @@ -26,7 +24,7 @@ class DigitalSource; * It probably should not be used directly. * */ -class SPI : public ErrorBase { +class SPI { public: enum Port { kOnboardCS0 = 0, kOnboardCS1, kOnboardCS2, kOnboardCS3, kMXP }; @@ -37,7 +35,7 @@ class SPI : public ErrorBase { */ explicit SPI(Port port); - ~SPI() override; + ~SPI(); SPI(SPI&&) = default; SPI& operator=(SPI&&) = default; diff --git a/wpilibc/src/main/native/include/frc/SerialPort.h b/wpilibc/src/main/native/include/frc/SerialPort.h index 9bbd9961ca..b11cb64dd7 100644 --- a/wpilibc/src/main/native/include/frc/SerialPort.h +++ b/wpilibc/src/main/native/include/frc/SerialPort.h @@ -11,8 +11,6 @@ #include #include -#include "frc/ErrorBase.h" - namespace frc { /** @@ -27,7 +25,7 @@ namespace frc { * and the NI-VISA Programmer's Reference Manual here: * http://www.ni.com/pdf/manuals/370132c.pdf */ -class SerialPort : public ErrorBase { +class SerialPort { public: enum Parity { kParity_None = 0, @@ -88,7 +86,7 @@ class SerialPort : public ErrorBase { int dataBits = 8, Parity parity = kParity_None, StopBits stopBits = kStopBits_One); - ~SerialPort() override; + ~SerialPort(); SerialPort(SerialPort&& rhs) = default; SerialPort& operator=(SerialPort&& rhs) = default; diff --git a/wpilibc/src/main/native/include/frc/SolenoidBase.h b/wpilibc/src/main/native/include/frc/SolenoidBase.h index c0acc4334f..82121fbe1b 100644 --- a/wpilibc/src/main/native/include/frc/SolenoidBase.h +++ b/wpilibc/src/main/native/include/frc/SolenoidBase.h @@ -4,16 +4,16 @@ #pragma once -#include "frc/ErrorBase.h" - namespace frc { /** * SolenoidBase class is the common base class for the Solenoid and * DoubleSolenoid classes. */ -class SolenoidBase : public ErrorBase { +class SolenoidBase { public: + virtual ~SolenoidBase() = default; + /** * Get the CAN ID of the module this solenoid is connected to. * diff --git a/wpilibc/src/main/native/include/frc/TimedRobot.h b/wpilibc/src/main/native/include/frc/TimedRobot.h index 817e6317b3..56221e765d 100644 --- a/wpilibc/src/main/native/include/frc/TimedRobot.h +++ b/wpilibc/src/main/native/include/frc/TimedRobot.h @@ -14,7 +14,6 @@ #include #include -#include "frc/ErrorBase.h" #include "frc/IterativeRobotBase.h" #include "frc2/Timer.h" @@ -29,7 +28,7 @@ namespace frc { * Periodic() functions from the base class are called on an interval by a * Notifier instance. */ -class TimedRobot : public IterativeRobotBase, public ErrorBase { +class TimedRobot : public IterativeRobotBase { public: static constexpr units::second_t kDefaultPeriod = 20_ms; diff --git a/wpilibc/src/main/native/include/frc/Ultrasonic.h b/wpilibc/src/main/native/include/frc/Ultrasonic.h index 78afc97be5..adb86b54bc 100644 --- a/wpilibc/src/main/native/include/frc/Ultrasonic.h +++ b/wpilibc/src/main/native/include/frc/Ultrasonic.h @@ -12,7 +12,6 @@ #include #include "frc/Counter.h" -#include "frc/ErrorBase.h" #include "frc/smartdashboard/Sendable.h" #include "frc/smartdashboard/SendableHelper.h" @@ -33,9 +32,7 @@ class DigitalOutput; * received. The time that the line is high determines the round trip distance * (time of flight). */ -class Ultrasonic : public ErrorBase, - public Sendable, - public SendableHelper { +class Ultrasonic : public Sendable, public SendableHelper { public: /** * Create an instance of the Ultrasonic Sensor. diff --git a/wpilibc/src/main/native/include/frc/WPIErrors.h b/wpilibc/src/main/native/include/frc/WPIErrors.mac similarity index 55% rename from wpilibc/src/main/native/include/frc/WPIErrors.h rename to wpilibc/src/main/native/include/frc/WPIErrors.mac index 3ff8dd23d4..f159ccd848 100644 --- a/wpilibc/src/main/native/include/frc/WPIErrors.h +++ b/wpilibc/src/main/native/include/frc/WPIErrors.mac @@ -2,18 +2,9 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -#pragma once - -#include - -#define S(label, offset, message) \ - constexpr inline const char* wpi_error_s_##label() { return message; } \ - constexpr inline int wpi_error_value_##label() { return offset; } - -// Fatal errors S(ModuleIndexOutOfRange, -1, "Allocating module that is out of range or not found") -S(ChannelIndexOutOfRange, -1, "Allocating channel that is out of range") +S(ChannelIndexOutOfRange, -45, "Allocating channel that is out of range") S(NotAllocated, -2, "Attempting to free unallocated resource") S(ResourceAlreadyAllocated, -3, "Attempted to reuse an allocated resource") S(NoAvailableResources, -4, "No available resources to allocate") @@ -26,13 +17,13 @@ S(IncompatibleMode, -9, "The object is in an incompatible mode") S(AnalogTriggerLimitOrderError, -10, "AnalogTrigger limits error. Lower limit > Upper Limit") S(AnalogTriggerPulseOutputError, -11, - "Attempted to read AnalogTrigger pulse output.") + "Attempted to read AnalogTrigger pulse output") S(TaskError, -12, "Task can't be started") -S(TaskIDError, -13, "Task error: Invalid ID.") -S(TaskDeletedError, -14, "Task error: Task already deleted.") -S(TaskOptionsError, -15, "Task error: Invalid options.") -S(TaskMemoryError, -16, "Task can't be started due to insufficient memory.") -S(TaskPriorityError, -17, "Task error: Invalid priority [1-255].") +S(TaskIDError, -13, "Task error: Invalid ID") +S(TaskDeletedError, -14, "Task error: Task already deleted") +S(TaskOptionsError, -15, "Task error: Invalid options") +S(TaskMemoryError, -16, "Task can't be started due to insufficient memory") +S(TaskPriorityError, -17, "Task error: Invalid priority [1-255]") S(DriveUninitialized, -18, "RobotDrive not initialized for the C interface") S(CompressorNonMatching, -19, "Compressor slot/channel doesn't match previous instance") @@ -41,19 +32,19 @@ S(CompressorUndefined, -21, "Using compressor functions without defining compressor") S(InconsistentArrayValueAdded, -22, "When packing data into an array to the dashboard, not all values added were " - "of the same type.") + "of the same type") S(MismatchedComplexTypeClose, -23, "When packing data to the dashboard, a Close for a complex type was called " - "without a matching Open.") + "without a matching Open") S(DashboardDataOverflow, -24, "When packing data to the dashboard, too much data was packed and the buffer " - "overflowed.") + "overflowed") S(DashboardDataCollision, -25, - "The same buffer was used for packing data and for printing.") -S(EnhancedIOMissing, -26, "IO is not attached or Enhanced IO is not enabled.") + "The same buffer was used for packing data and for printing") +S(EnhancedIOMissing, -26, "IO is not attached or Enhanced IO is not enabled") S(LineNotOutput, -27, - "Cannot SetDigitalOutput for a line not configured for output.") -S(ParameterOutOfRange, -28, "A parameter is out of range.") + "Cannot SetDigitalOutput for a line not configured for output") +S(ParameterOutOfRange, -28, "A parameter is out of range") S(SPIClockRateTooLow, -29, "SPI clock rate was below the minimum supported") S(JaguarVersionError, -30, "Jaguar firmware version error") S(JaguarMessageNotFound, -31, "Jaguar message not found") @@ -62,31 +53,9 @@ S(NetworkTablesBufferFull, -41, "Buffer full writing to NetworkTables socket") S(NetworkTablesWrongType, -42, "The wrong type was read from the NetworkTables entry") S(NetworkTablesCorrupt, -43, "NetworkTables data stream is corrupt") -S(SmartDashboardMissingKey, -43, "SmartDashboard data does not exist") +S(SmartDashboardMissingKey, -44, "SmartDashboard data does not exist") S(CommandIllegalUse, -50, "Illegal use of Command") S(UnsupportedInSimulation, -80, "Unsupported in simulation") S(CameraServerError, -90, "CameraServer error") S(InvalidParameter, -100, "Invalid parameter value") - -// Warnings -S(SampleRateTooHigh, 1, "Analog module sample rate is too high") -S(VoltageOutOfRange, 2, - "Voltage to convert to raw value is out of range [-10; 10]") -S(CompressorTaskError, 3, "Compressor task won't start") -S(LoopTimingError, 4, "Digital module loop timing is not the expected value") -S(NonBinaryDigitalValue, 5, "Digital output value is not 0 or 1") -S(IncorrectBatteryChannel, 6, - "Battery measurement channel is not correct value") -S(BadJoystickIndex, 7, "Joystick index is out of range, should be 0-3") -S(BadJoystickAxis, 8, "Joystick axis or POV is out of range") -S(InvalidMotorIndex, 9, "Motor index is out of range, should be 0-3") -S(DriverStationTaskError, 10, "Driver Station task won't start") -S(EnhancedIOPWMPeriodOutOfRange, 11, - "Driver Station Enhanced IO PWM Output period out of range.") -S(SPIWriteNoMOSI, 12, "Cannot write to SPI port with no MOSI output") -S(SPIReadNoMISO, 13, "Cannot read from SPI port with no MISO input") -S(SPIReadNoData, 14, "No data available to read from SPI") -S(IncompatibleState, 15, - "Incompatible State: The operation cannot be completed") - -#undef S +S(AssertionFailure, -110, "Assertion failed") diff --git a/wpilibc/src/main/native/include/frc/WPIWarnings.mac b/wpilibc/src/main/native/include/frc/WPIWarnings.mac new file mode 100644 index 0000000000..8e39ac1d6c --- /dev/null +++ b/wpilibc/src/main/native/include/frc/WPIWarnings.mac @@ -0,0 +1,23 @@ +// Copyright (c) FIRST and other WPILib contributors. +// Open Source Software; you can modify and/or share it under the terms of +// the WPILib BSD license file in the root directory of this project. + +S(SampleRateTooHigh, 1, "Analog module sample rate is too high") +S(VoltageOutOfRange, 2, + "Voltage to convert to raw value is out of range [-10; 10]") +S(CompressorTaskError, 3, "Compressor task won't start") +S(LoopTimingError, 4, "Digital module loop timing is not the expected value") +S(NonBinaryDigitalValue, 5, "Digital output value is not 0 or 1") +S(IncorrectBatteryChannel, 6, + "Battery measurement channel is not correct value") +S(BadJoystickIndex, 7, "Joystick index is out of range, should be 0-3") +S(BadJoystickAxis, 8, "Joystick axis or POV is out of range") +S(InvalidMotorIndex, 9, "Motor index is out of range, should be 0-3") +S(DriverStationTaskError, 10, "Driver Station task won't start") +S(EnhancedIOPWMPeriodOutOfRange, 11, + "Driver Station Enhanced IO PWM Output period out of range") +S(SPIWriteNoMOSI, 12, "Cannot write to SPI port with no MOSI output") +S(SPIReadNoMISO, 13, "Cannot read from SPI port with no MISO input") +S(SPIReadNoData, 14, "No data available to read from SPI") +S(IncompatibleState, 15, + "Incompatible State: The operation cannot be completed") diff --git a/wpilibc/src/main/native/include/frc/motorcontrol/NidecBrushless.h b/wpilibc/src/main/native/include/frc/motorcontrol/NidecBrushless.h index f92e0f3759..112cbd13b4 100644 --- a/wpilibc/src/main/native/include/frc/motorcontrol/NidecBrushless.h +++ b/wpilibc/src/main/native/include/frc/motorcontrol/NidecBrushless.h @@ -5,7 +5,6 @@ #pragma once #include "frc/DigitalOutput.h" -#include "frc/ErrorBase.h" #include "frc/MotorSafety.h" #include "frc/PWM.h" #include "frc/motorcontrol/MotorController.h" diff --git a/wpilibc/src/main/native/include/frc/romi/RomiGyro.h b/wpilibc/src/main/native/include/frc/romi/RomiGyro.h index eb33efc559..7523448a0f 100644 --- a/wpilibc/src/main/native/include/frc/romi/RomiGyro.h +++ b/wpilibc/src/main/native/include/frc/romi/RomiGyro.h @@ -6,7 +6,6 @@ #include -#include "frc/ErrorBase.h" #include "frc/interfaces/Gyro.h" #include "frc/smartdashboard/Sendable.h" #include "frc/smartdashboard/SendableHelper.h" @@ -19,10 +18,7 @@ namespace frc { * This class is for the Romi onboard gyro, and will only work in * simulation/Romi mode. Only one instance of a RomiGyro is supported. */ -class RomiGyro : public Gyro, - public ErrorBase, - public Sendable, - public SendableHelper { +class RomiGyro : public Gyro, public Sendable, public SendableHelper { public: RomiGyro(); diff --git a/wpilibc/src/main/native/include/frc/shuffleboard/ShuffleboardContainer.h b/wpilibc/src/main/native/include/frc/shuffleboard/ShuffleboardContainer.h index a25c81fe89..33b9fe713f 100644 --- a/wpilibc/src/main/native/include/frc/shuffleboard/ShuffleboardContainer.h +++ b/wpilibc/src/main/native/include/frc/shuffleboard/ShuffleboardContainer.h @@ -16,8 +16,6 @@ #include #include -#include "frc/ErrorBase.h" -#include "frc/WPIErrors.h" #include "frc/shuffleboard/BuiltInLayouts.h" #include "frc/shuffleboard/LayoutType.h" #include "frc/shuffleboard/ShuffleboardComponentBase.h" @@ -38,8 +36,7 @@ class SimpleWidget; /** * Common interface for objects that can contain shuffleboard components. */ -class ShuffleboardContainer : public virtual ShuffleboardValue, - public ErrorBase { +class ShuffleboardContainer : public virtual ShuffleboardValue { public: explicit ShuffleboardContainer(const wpi::Twine& title); diff --git a/wpilibc/src/main/native/include/frc/smartdashboard/SmartDashboard.h b/wpilibc/src/main/native/include/frc/smartdashboard/SmartDashboard.h index 1dd0a1efbf..6a6ee299cf 100644 --- a/wpilibc/src/main/native/include/frc/smartdashboard/SmartDashboard.h +++ b/wpilibc/src/main/native/include/frc/smartdashboard/SmartDashboard.h @@ -11,16 +11,13 @@ #include #include -#include "frc/ErrorBase.h" #include "frc/smartdashboard/ListenerExecutor.h" #include "frc/smartdashboard/Sendable.h" #include "frc/smartdashboard/SendableHelper.h" namespace frc { -class SmartDashboard : public ErrorBase, - public Sendable, - public SendableHelper { +class SmartDashboard : public Sendable, public SendableHelper { public: static void init(); diff --git a/wpilibcIntegrationTests/src/main/native/cpp/PowerDistributionPanelTest.cpp b/wpilibcIntegrationTests/src/main/native/cpp/PowerDistributionPanelTest.cpp index 06d12586c8..f213d56d9c 100644 --- a/wpilibcIntegrationTests/src/main/native/cpp/PowerDistributionPanelTest.cpp +++ b/wpilibcIntegrationTests/src/main/native/cpp/PowerDistributionPanelTest.cpp @@ -47,10 +47,8 @@ TEST_F(PowerDistributionPanelTest, CheckRepeatedCalls) { for (int i = 0; i < 50; i++) { for (int j = 0; j < numChannels; j++) { m_pdp->GetCurrent(j); - ASSERT_TRUE(m_pdp->GetError().GetCode() == 0); } m_pdp->GetVoltage(); - ASSERT_TRUE(m_pdp->GetError().GetCode() == 0); } std::this_thread::sleep_for(std::chrono::milliseconds(20)); } From ced654880ce3f7dac117f157f9085e8b95f2da46 Mon Sep 17 00:00:00 2001 From: Noam Zaks <63877260+noamzaks@users.noreply.github.com> Date: Mon, 26 Apr 2021 03:30:45 +0300 Subject: [PATCH 24/34] [glass, outlineviewer] Update Mac icons to macOS 11 style (#3313) --- glass/src/app/native/mac/glass.icns | Bin 80889 -> 302705 bytes outlineviewer/src/main/native/mac/ov.icns | Bin 92922 -> 320877 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/glass/src/app/native/mac/glass.icns b/glass/src/app/native/mac/glass.icns index 12047347610981732804cc1efc37a33c0b7de0db..74b6850595aa08afd4adf744e48d1517ba02a13f 100644 GIT binary patch literal 302705 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

    ;DIP!D@|-r1mnH6{lW`s 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

    P?D*ha-C(zSG&Eb0Q!&c&D3dG5F{ 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 ?Ga5Pwr`6Zr8qu_7lj#tct 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 G_5T84(Itui diff --git a/outlineviewer/src/main/native/mac/ov.icns b/outlineviewer/src/main/native/mac/ov.icns index b96e85928b5ea1a7440859b8899c42fc6a05a7b7..17f9770a0b2e27a7223f7297fcb7f2b9e8c2dabd 100644 GIT binary patch literal 320877 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

    J3f*6vR0*C2gD1&D!+dIr`3XEr}V9n4a(OO zF1gHvH~n8g$j({6H!cS?Zz%HbyffC=Fvqb@y>+}~zp14aBF>ABfyrM+ij@Aazdd1dzZrLgVAJ@)(!YKj$(Eu z!Fbz!5_k<^FTiswu#R5*+ZAH69O+IN6$%5El)mNmmpBKo_E#VCKZNtirRi71@EKSQ zjjRc@A-U#kBP=BBGN~9xMWUw#jV<_Z@*d6mA@S#SRm%>)d1t@(24ZJ~VN}77M)2zA zrdCww*wRNs=AollB$EeSVIqJAb8#LtS5O(c^3#3|gnOgm zXa0T_@_~TF8p*|b4@yZoQlLIzX}P~X)pCp01@ATbddI-)wp@4g`m=tw(u;GU!&sCg-lVmgXgWl^kcC4a!vyZf-*G%MAOdO;zP4c98$t@ zNj?6G3;mmCvaE_>shpd&ta}WwF{HIiu+4Ddez8dmT91Me&w}G#+ER@{Hc|vVGSS`g z{*L=}(f=Zt_=kfn*)&U()iM=VLN1^%_D9fIF=yuJtJrx@#q?9QkZBOAseWJ#1#3;^ zdChjuO`kkjuPF?t4%*w*2RKsY(_VcMggK(p(rbFZ={KSehI77EXjr_tGmF128HtH= zXky|Wo<#26E(n;tv)O*V_T!{hQW;e?B`$|Mvbs08wGzJ~I*{(=GHwY{eKLT2Tf`91 z9218oQJY=uS65aJG%9-@ns9cr6h5xZ!%|7xXhWq9>`kLr9BzSK4{#z#7#8H@M_t~` znmMDCGwp^FIQie_-d{V4b8(x=ll|^niABTH_5*G;thoX`sNCJG6V;!nrY>d$1cDYF;d<<2emC9@Q~-NL zah+(-zc)buVRC>3@Cp1gdD-|KB$q@4P}NuQ*atIvc|T8!U8Cu7Nguy=ULMYjRh8FZ zD-5NNWm3NxD}C8a;MF)=q71atf0ACn6FL<#r11EUHk&>687~uK9no}Ct^6_Jt0b_Y zKB`~)r};U7bm@7uht=!AIN?@J+)x7eZaJ=96Ti2%f&ufVIjnyVn*_GL#SXk z7Z)8Kb9yjJT#qSkxXWZ>9JUk}Ip&KR0oEMSEdB(z zb0xT15N~(-yrsKBz@?FOU>w9eV+$vbZ*2)^DU}vqBtK0(Etapr{pmZoec;jH$@pQ^ zHUB}Z&C)^Wh$(BU_i>?Pl{3!4b?3}nOtTG{lJ>Ake(ktPbQCdf{|69+IH26#_)1wG zIGlDIa@TD3CC;gk-`dgH@k=?PTPU$J&GxWTe5iG`%0f2)$VW4j!;_R0Eh4a~USQpC z=u-X8=J*+s0Lu+JpI`7imL-Z&IavrFODHYN;ycO~vd^#QNv@$cKeEnIT7BdQ+iub` zxlhLv!1qWUV>vHnq-gD)Tcn4Pn=F+%XY!Nv(l6z{Tn)dqUx^EtOWpPqkXb|0`$5;? zEB6_9>>AC=lEv~AlEN>~h_6*&b=~EaAn35v3URK)P3+FI3_Nsdccu4hL;7e|91w

    7V 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�+j9eiOcT5DKx#1)ds4`nvt^ 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
        F-lwA_*uclYoy1#v1?^&ZyzSK41 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 literal 92922 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 From 512738072765f281eaec1c37ccc107e458d0343f Mon Sep 17 00:00:00 2001 From: Thad House Date: Thu, 29 Apr 2021 09:56:54 -0700 Subject: [PATCH 25/34] [hal] Add HAL_GetLastError to enable better error messages from HAL calls (#3320) This uses thread local storage so a full error string can be provided, not just an error code. --- hal/src/main/native/athena/HAL.cpp | 2 + hal/src/main/native/athena/HALInternal.h | 4 +- hal/src/main/native/cpp/ErrorHandling.cpp | 41 +++++++++++++++++++ hal/src/main/native/cpp/jni/HALUtil.cpp | 12 ++++-- hal/src/main/native/include/hal/Errors.h | 4 ++ hal/src/main/native/include/hal/HALBase.h | 13 ++++++ hal/src/main/native/sim/HAL.cpp | 2 + wpilibc/src/main/native/cpp/Errors.cpp | 10 ++--- wpilibc/src/main/native/include/frc/Errors.h | 2 +- .../src/main/cpp/examples/HAL/c/Robot.c | 4 +- 10 files changed, 81 insertions(+), 13 deletions(-) create mode 100644 hal/src/main/native/cpp/ErrorHandling.cpp diff --git a/hal/src/main/native/athena/HAL.cpp b/hal/src/main/native/athena/HAL.cpp index fcee49897f..eefe34c9a2 100644 --- a/hal/src/main/native/athena/HAL.cpp +++ b/hal/src/main/native/athena/HAL.cpp @@ -233,6 +233,8 @@ const char* HAL_GetErrorMessage(int32_t code) { return HAL_CAN_BUFFER_OVERRUN_MESSAGE; case HAL_LED_CHANNEL_ERROR: return HAL_LED_CHANNEL_ERROR_MESSAGE; + case HAL_USE_LAST_ERROR: + return HAL_USE_LAST_ERROR_MESSAGE; default: return "Unknown error status"; } diff --git a/hal/src/main/native/athena/HALInternal.h b/hal/src/main/native/athena/HALInternal.h index b9f284ff6a..f104bfe7d5 100644 --- a/hal/src/main/native/athena/HALInternal.h +++ b/hal/src/main/native/athena/HALInternal.h @@ -6,7 +6,9 @@ #include +#include + namespace hal { void ReleaseFPGAInterrupt(int32_t interruptNumber); - +void SetLastError(int32_t status, const wpi::Twine& value); } // namespace hal diff --git a/hal/src/main/native/cpp/ErrorHandling.cpp b/hal/src/main/native/cpp/ErrorHandling.cpp new file mode 100644 index 0000000000..9f10853b52 --- /dev/null +++ b/hal/src/main/native/cpp/ErrorHandling.cpp @@ -0,0 +1,41 @@ +// Copyright (c) FIRST and other WPILib contributors. +// Open Source Software; you can modify and/or share it under the terms of +// the WPILib BSD license file in the root directory of this project. + +#include +#include + +#include "hal/Errors.h" +#include "hal/HALBase.h" + +namespace { +struct LastErrorStorage { + int32_t status; + wpi::SmallString<512> message; +}; +} // namespace + +static LastErrorStorage& GetThreadLastError() { + thread_local LastErrorStorage lastError; + return lastError; +} + +namespace hal { +void SetLastError(int32_t status, const wpi::Twine& value) { + LastErrorStorage& lastError = GetThreadLastError(); + lastError.message.clear(); + value.toVector(lastError.message); + lastError.status = status; +} +} // namespace hal + +extern "C" { +const char* HAL_GetLastError(int32_t* status) { + if (*status == HAL_USE_LAST_ERROR) { + LastErrorStorage& lastError = GetThreadLastError(); + *status = lastError.status; + return lastError.message.c_str(); + } + return HAL_GetErrorMessage(*status); +} +} // extern "C" diff --git a/hal/src/main/native/cpp/jni/HALUtil.cpp b/hal/src/main/native/cpp/jni/HALUtil.cpp index fc37e9dd56..cbeb9c97ee 100644 --- a/hal/src/main/native/cpp/jni/HALUtil.cpp +++ b/hal/src/main/native/cpp/jni/HALUtil.cpp @@ -85,7 +85,7 @@ void ThrowUncleanStatusException(JNIEnv* env, wpi::StringRef msg, void ThrowAllocationException(JNIEnv* env, int32_t minRange, int32_t maxRange, int32_t requestedValue, int32_t status) { - const char* message = HAL_GetErrorMessage(status); + const char* message = HAL_GetLastError(&status); wpi::SmallString<1024> buf; wpi::raw_svector_ostream oss(buf); oss << " Code: " << status << ". " << message @@ -96,7 +96,7 @@ void ThrowAllocationException(JNIEnv* env, int32_t minRange, int32_t maxRange, } void ThrowHalHandleException(JNIEnv* env, int32_t status) { - const char* message = HAL_GetErrorMessage(status); + const char* message = HAL_GetLastError(&status); wpi::SmallString<1024> buf; wpi::raw_svector_ostream oss(buf); oss << " Code: " << status << ". " << message; @@ -110,7 +110,7 @@ void ReportError(JNIEnv* env, int32_t status, bool doThrow) { if (status == HAL_HANDLE_ERROR) { ThrowHalHandleException(env, status); } - const char* message = HAL_GetErrorMessage(status); + const char* message = HAL_GetLastError(&status); if (doThrow && status < 0) { wpi::SmallString<1024> buf; wpi::raw_svector_ostream oss(buf); @@ -119,7 +119,11 @@ void ReportError(JNIEnv* env, int32_t status, bool doThrow) { } else { std::string func; auto stack = GetJavaStackTrace(env, &func, "edu.wpi.first"); - HAL_SendError(1, status, 0, message, func.c_str(), stack.c_str(), 1); + // Make a copy of message for safety, calling back into the HAL might + // invalidate the string. + wpi::SmallString<256> lastMessage{wpi::StringRef{message}}; + HAL_SendError(1, status, 0, lastMessage.c_str(), func.c_str(), + stack.c_str(), 1); } } diff --git a/hal/src/main/native/include/hal/Errors.h b/hal/src/main/native/include/hal/Errors.h index b2e4b081b0..1e87f54cb2 100644 --- a/hal/src/main/native/include/hal/Errors.h +++ b/hal/src/main/native/include/hal/Errors.h @@ -125,6 +125,10 @@ #define HAL_SIM_NOT_SUPPORTED -1155 #define HAL_SIM_NOT_SUPPORTED_MESSAGE "HAL: Method not supported in sim" +#define HAL_USE_LAST_ERROR -1156 +#define HAL_USE_LAST_ERROR_MESSAGE \ + "HAL: Use HAL_GetLastError(status) to get last error" + #define HAL_CAN_BUFFER_OVERRUN -35007 #define HAL_CAN_BUFFER_OVERRUN_MESSAGE \ "HAL: CAN Output Buffer Full. Ensure a device is attached" diff --git a/hal/src/main/native/include/hal/HALBase.h b/hal/src/main/native/include/hal/HALBase.h index dbbf15d803..ce2a420711 100644 --- a/hal/src/main/native/include/hal/HALBase.h +++ b/hal/src/main/native/include/hal/HALBase.h @@ -22,6 +22,19 @@ HAL_ENUM(HAL_RuntimeType) { HAL_Athena, HAL_Mock }; extern "C" { #endif +/** + * Gets the last error set on this thread, or the message for the status code. + * + * If passed HAL_USE_LAST_ERROR, the last error set on the thread will be + * returned. + * + * @param code the status code, set to the error status code if input is + * HAL_USE_LAST_ERROR + * @return the error message for the code. This does not need to be freed, + * but can be overwritten by another hal call on the same thread. + */ +const char* HAL_GetLastError(int32_t* status); + /** * Gets the error message for a specific status code. * diff --git a/hal/src/main/native/sim/HAL.cpp b/hal/src/main/native/sim/HAL.cpp index 97ad1b9bba..09409bc1fb 100644 --- a/hal/src/main/native/sim/HAL.cpp +++ b/hal/src/main/native/sim/HAL.cpp @@ -250,6 +250,8 @@ const char* HAL_GetErrorMessage(int32_t code) { return HAL_CAN_BUFFER_OVERRUN_MESSAGE; case HAL_LED_CHANNEL_ERROR: return HAL_LED_CHANNEL_ERROR_MESSAGE; + case HAL_USE_LAST_ERROR: + return HAL_USE_LAST_ERROR_MESSAGE; default: return "Unknown error status"; } diff --git a/wpilibc/src/main/native/cpp/Errors.cpp b/wpilibc/src/main/native/cpp/Errors.cpp index 56c55922a4..b432968845 100644 --- a/wpilibc/src/main/native/cpp/Errors.cpp +++ b/wpilibc/src/main/native/cpp/Errors.cpp @@ -36,10 +36,10 @@ void RuntimeError::Report() const { m_data->stack.c_str(), 1); } -const char* frc::GetErrorMessage(int32_t code) { +const char* frc::GetErrorMessage(int32_t* code) { using namespace err; using namespace warn; - switch (code) { + switch (*code) { #define S(label, offset, message) \ case label: \ return message; @@ -47,7 +47,7 @@ const char* frc::GetErrorMessage(int32_t code) { #include "frc/WPIWarnings.mac" #undef S default: - return HAL_GetErrorMessage(code); + return HAL_GetLastError(code); } } @@ -57,7 +57,7 @@ void frc::ReportError(int32_t status, const wpi::Twine& message, if (status == 0) { return; } - const char* statusMessage = GetErrorMessage(status); + const char* statusMessage = GetErrorMessage(&status); auto stack = wpi::GetStackTrace(2); wpi::SmallString<128> buf; HAL_SendError(status < 0, status, 0, @@ -70,7 +70,7 @@ void frc::ReportError(int32_t status, const wpi::Twine& message, RuntimeError frc::MakeError(int32_t status, const wpi::Twine& message, const char* fileName, int lineNumber, const char* funcName) { - const char* statusMessage = GetErrorMessage(status); + const char* statusMessage = GetErrorMessage(&status); auto stack = wpi::GetStackTrace(2); return RuntimeError{status, statusMessage + wpi::Twine{": "} + message, fileName, lineNumber, diff --git a/wpilibc/src/main/native/include/frc/Errors.h b/wpilibc/src/main/native/include/frc/Errors.h index 9b16072298..64b965f3c0 100644 --- a/wpilibc/src/main/native/include/frc/Errors.h +++ b/wpilibc/src/main/native/include/frc/Errors.h @@ -45,7 +45,7 @@ class RuntimeError : public std::runtime_error { /** * Gets error message string for an error code. */ -const char* GetErrorMessage(int32_t code); +const char* GetErrorMessage(int32_t* code); /** * Reports an error to the driver station (using HAL_SendError). diff --git a/wpilibcExamples/src/main/cpp/examples/HAL/c/Robot.c b/wpilibcExamples/src/main/cpp/examples/HAL/c/Robot.c index 29de08fb7b..b7c0826bad 100644 --- a/wpilibcExamples/src/main/cpp/examples/HAL/c/Robot.c +++ b/wpilibcExamples/src/main/cpp/examples/HAL/c/Robot.c @@ -65,7 +65,7 @@ int main(void) { HAL_DigitalHandle pwmPort = HAL_InitializePWMPort(HAL_GetPort(2), &status); if (status != 0) { - const char* message = HAL_GetErrorMessage(status); + const char* message = HAL_GetLastError(&status); printf("%s\n", message); return 1; } @@ -78,7 +78,7 @@ int main(void) { HAL_DigitalHandle dio = HAL_InitializeDIOPort(HAL_GetPort(2), 1, &status); if (status != 0) { - const char* message = HAL_GetErrorMessage(status); + const char* message = HAL_GetLastError(&status); printf("%s\n", message); status = 0; HAL_FreePWMPort(pwmPort, &status); From 6b50323b07999f23927b16f690781effc137d216 Mon Sep 17 00:00:00 2001 From: Thad House Date: Sat, 1 May 2021 07:07:37 -0700 Subject: [PATCH 26/34] [cscore] Use Lock2DSize if possible for Windows USB cameras (#3326) Can remove a memory copy in many cases. This also fixes a bug where any mjpeg cameras on windows wouldn't work if the fast path was taken. --- .../src/main/native/windows/UsbCameraImpl.cpp | 52 ++++++++++++------- 1 file changed, 33 insertions(+), 19 deletions(-) diff --git a/cscore/src/main/native/windows/UsbCameraImpl.cpp b/cscore/src/main/native/windows/UsbCameraImpl.cpp index 91944a34bd..10ce8d982a 100644 --- a/cscore/src/main/native/windows/UsbCameraImpl.cpp +++ b/cscore/src/main/native/windows/UsbCameraImpl.cpp @@ -293,32 +293,45 @@ void UsbCameraImpl::ProcessFrame(IMFSample* videoSample, return; } - bool lock2d = false; BYTE* ptr = NULL; LONG pitch = 0; - DWORD maxsize = 0, cursize = 0; + DWORD length = 0; - // "For 2-D buffers, the Lock2D method is more efficient than the Lock - // method" see IMFMediaBuffer::Lock method documentation: - // https://msdn.microsoft.com/en-us/library/windows/desktop/bb970366(v=vs.85).aspx - ComPtr buffer2d; - DWORD memLength2d = 0; - if (true) { - buffer2d = buf.As(); - if (buffer2d) { - buffer2d->GetContiguousLength(&memLength2d); - if (SUCCEEDED(buffer2d->Lock2D(&ptr, &pitch))) { - lock2d = true; + // First try to access using Lock2DSize, then try Lock2D, then fallback + // https://docs.microsoft.com/en-us/windows/win32/api/mfobjects/nf-mfobjects-imfmediabuffer-lock + + ComPtr buffer2d = buf.As(); + if (buffer2d) { + BYTE* scanline0 = nullptr; + HRESULT result; + ComPtr buffer2d2 = buf.As(); + if (buffer2d2) { + BYTE* datastart; + result = buffer2d2->Lock2DSize(MF2DBuffer_LockFlags_Read, &scanline0, + &pitch, &datastart, &length); + } else { + result = buffer2d->Lock2D(&scanline0, &pitch); + } + if (SUCCEEDED(result)) { + BOOL isContiguous; + if (pitch > 0 && SUCCEEDED(buffer2d->IsContiguousFormat(&isContiguous)) && + isContiguous && + (length || SUCCEEDED(buffer2d->GetContiguousLength(&length)))) { + // Use the buffer pointer. + ptr = scanline0; + } else { + // Release the buffer and fall back to Lock(). + buffer2d->Unlock2D(); } } } if (ptr == NULL) { - if (!SUCCEEDED(buf->Lock(&ptr, &maxsize, &cursize))) { + buffer2d = nullptr; + DWORD maxsize = 0; + if (!SUCCEEDED(buf->Lock(&ptr, &maxsize, &length))) { return; } } - if (!ptr) - return; cv::Mat tmpMat; std::unique_ptr dest; @@ -328,7 +341,7 @@ void UsbCameraImpl::ProcessFrame(IMFSample* videoSample, case cs::VideoMode::PixelFormat::kMJPEG: { // Special case PutFrame(VideoMode::kMJPEG, mode.width, mode.height, - wpi::StringRef(reinterpret_cast(ptr), cursize), + wpi::StringRef(reinterpret_cast(ptr), length), wpi::Now()); doFinalSet = false; break; @@ -360,10 +373,11 @@ void UsbCameraImpl::ProcessFrame(IMFSample* videoSample, PutFrame(std::move(dest), wpi::Now()); } - if (lock2d) + if (buffer2d) { buffer2d->Unlock2D(); - else + } else { buf->Unlock(); + } } LRESULT UsbCameraImpl::PumpMain(HWND hwnd, UINT uiMsg, WPARAM wParam, From c8ff626fe241ee26caa6857b494fd15495e96515 Mon Sep 17 00:00:00 2001 From: Noam Zaks Date: Sat, 1 May 2021 15:53:30 +0000 Subject: [PATCH 27/34] [wpimath] Move Java classes to edu.wpi.first.math (#3316) --- .../command/MecanumControllerCommand.java | 16 ++++----- .../wpilibj2/command/ProfiledPIDCommand.java | 2 +- .../command/ProfiledPIDSubsystem.java | 4 +-- .../wpilibj2/command/RamseteCommand.java | 14 ++++---- .../command/SwerveControllerCommand.java | 10 +++--- .../command/TrapezoidProfileCommand.java | 4 +-- .../command/TrapezoidProfileSubsystem.java | 2 +- .../command/MecanumControllerCommandTest.java | 18 +++++----- .../command/SwerveControllerCommandTest.java | 18 +++++----- .../java/edu/wpi/first/wpilibj/PIDBase.java | 1 + .../wpi/first/wpilibj/SlewRateLimiter.java | 4 +-- .../controller/HolonomicDriveController.java | 8 ++--- .../wpilibj/controller/PIDController.java | 2 +- .../controller/ProfiledPIDController.java | 4 +-- .../wpilibj/drive/DifferentialDrive.java | 2 +- .../first/wpilibj/drive/KilloughDrive.java | 2 +- .../wpi/first/wpilibj/drive/MecanumDrive.java | 2 +- .../wpi/first/wpilibj/interfaces/Gyro.java | 7 ++-- .../wpilibj/simulation/AnalogEncoderSim.java | 2 +- .../simulation/DifferentialDrivetrainSim.java | 32 ++++++++--------- .../first/wpilibj/simulation/ElevatorSim.java | 16 ++++----- .../first/wpilibj/simulation/FlywheelSim.java | 12 +++---- .../wpilibj/simulation/LinearSystemSim.java | 10 +++--- .../simulation/SingleJointedArmSim.java | 16 ++++----- .../first/wpilibj/smartdashboard/Field2d.java | 4 +-- .../wpilibj/smartdashboard/FieldObject2d.java | 8 ++--- .../edu/wpi/first/wpilibj/util/Color.java | 2 +- .../edu/wpi/first/wpilibj/util/Color8Bit.java | 2 +- .../{wpilibj => math}/util/ColorTest.java | 3 +- .../util/ErrorMessagesTest.java | 3 +- .../HolonomicDriveControllerTest.java | 18 +++++----- .../controller/ProfiledPIDControllerTest.java | 2 +- .../ProfiledPIDInputOutputTest.java | 2 +- .../simulation/AnalogEncoderSimTest.java | 2 +- .../DifferentialDrivetrainSimTest.java | 36 +++++++++---------- .../wpilibj/simulation/ElevatorSimTest.java | 8 ++--- .../simulation/SingleJointedArmSimTest.java | 6 ++-- .../ReplaceMeProfiledPIDCommand.java | 2 +- .../ReplaceMeProfiledPIDSubsystem.java | 2 +- .../ReplaceMeTrapezoidProfileCommand.java | 2 +- .../ReplaceMeTrapezoidProfileSubsystem.java | 2 +- .../armbot/subsystems/ArmSubsystem.java | 4 +-- .../subsystems/ArmSubsystem.java | 4 +-- .../wpilibj/examples/armsimulation/Robot.java | 6 ++-- .../differentialdrivebot/Drivetrain.java | 10 +++--- .../Drivetrain.java | 16 ++++----- .../drivedistanceoffboard/RobotContainer.java | 2 +- .../commands/DriveDistanceProfiled.java | 2 +- .../subsystems/DriveSubsystem.java | 4 +-- .../examples/elevatorprofiledpid/Robot.java | 2 +- .../examples/elevatorsimulation/Robot.java | 6 ++-- .../elevatortrapezoidprofile/Robot.java | 4 +-- .../subsystems/ShooterSubsystem.java | 2 +- .../commands/TurnToAngleProfiled.java | 2 +- .../examples/mecanumbot/Drivetrain.java | 12 +++---- .../mecanumcontrollercommand/Constants.java | 8 ++--- .../RobotContainer.java | 12 +++---- .../subsystems/DriveSubsystem.java | 8 ++--- .../mecanumdriveposeestimator/Drivetrain.java | 18 +++++----- .../ExampleGlobalMeasurementSensor.java | 10 +++--- .../examples/ramsetecommand/Constants.java | 2 +- .../ramsetecommand/RobotContainer.java | 18 +++++----- .../subsystems/DriveSubsystem.java | 6 ++-- .../ramsetecontroller/Drivetrain.java | 12 +++---- .../examples/ramsetecontroller/Robot.java | 16 ++++----- .../Drivetrain.java | 20 +++++------ .../Robot.java | 14 ++++---- .../wpilibj/examples/statespacearm/Robot.java | 24 ++++++------- .../Constants.java | 10 +++--- .../RobotContainer.java | 20 +++++------ .../subsystems/DriveSubsystem.java | 10 +++--- .../examples/statespaceelevator/Robot.java | 24 ++++++------- .../examples/statespaceflywheel/Robot.java | 20 +++++------ .../statespaceflywheelsysid/Robot.java | 18 +++++----- .../examples/swervebot/Drivetrain.java | 8 ++--- .../examples/swervebot/SwerveModule.java | 8 ++--- .../swervecontrollercommand/Constants.java | 6 ++-- .../RobotContainer.java | 12 +++---- .../subsystems/DriveSubsystem.java | 12 +++---- .../subsystems/SwerveModule.java | 6 ++-- .../swervesdriveposeestimator/Drivetrain.java | 14 ++++---- .../ExampleGlobalMeasurementSensor.java | 10 +++--- .../SwerveModule.java | 8 ++--- .../wpilibj/examples/ultrasonic/Robot.java | 2 +- .../wpilibj/examples/ultrasonicpid/Robot.java | 2 +- .../wpi/first/wpilibj/MotorEncoderTest.java | 3 +- wpimath/build.gradle | 8 ++--- wpimath/generate_numbers.py | 2 +- .../wpi/first/{wpiutil => }/math/DevMain.java | 2 +- wpimath/src/generate/GenericNumber.java.in | 6 ++-- wpimath/src/generate/Nat.java.in | 2 +- .../main/java/edu/wpi/first/math/Drake.java | 2 -- .../first/{wpiutil => }/math/MatBuilder.java | 2 +- .../first/{wpiutil => }/math/MathUtil.java | 2 +- .../wpi/first/{wpiutil => }/math/Matrix.java | 5 ++- .../first/{wpiutil => }/math/MatrixUtils.java | 4 +-- .../edu/wpi/first/{wpiutil => }/math/Num.java | 2 +- .../wpi/first/{wpiutil => }/math/Pair.java | 2 +- .../{wpiutil => }/math/SimpleMatrixUtils.java | 3 +- .../first/{wpiutil => }/math/VecBuilder.java | 22 ++++++------ .../wpi/first/{wpiutil => }/math/Vector.java | 4 +-- .../controller/ArmFeedforward.java | 2 +- ...ontrolAffinePlantInversionFeedforward.java | 14 ++++---- .../controller/ElevatorFeedforward.java | 2 +- .../LinearPlantInversionFeedforward.java | 12 +++---- .../controller/LinearQuadraticRegulator.java | 18 +++++----- .../math}/controller/RamseteController.java | 8 ++--- .../controller/SimpleMotorFeedforward.java | 2 +- .../estimator/AngleStatistics.java | 10 +++--- .../DifferentialDrivePoseEstimator.java | 32 ++++++++--------- .../estimator/ExtendedKalmanFilter.java | 18 +++++----- .../estimator/KalmanFilter.java | 16 ++++----- .../KalmanFilterLatencyCompensator.java | 10 +++--- .../estimator/KalmanTypeFilter.java | 8 ++--- .../estimator/MecanumDrivePoseEstimator.java | 28 +++++++-------- .../estimator/MerweScaledSigmaPoints.java | 10 +++--- .../estimator/SwerveDrivePoseEstimator.java | 28 +++++++-------- .../estimator/UnscentedKalmanFilter.java | 20 +++++------ .../filter}/LinearFilter.java | 2 +- .../filter}/MedianFilter.java | 2 +- .../{wpilibj => math}/geometry/Pose2d.java | 2 +- .../geometry/Rotation2d.java | 2 +- .../geometry/Transform2d.java | 2 +- .../geometry/Translation2d.java | 2 +- .../{wpilibj => math}/geometry/Twist2d.java | 2 +- .../kinematics/ChassisSpeeds.java | 4 +-- .../DifferentialDriveKinematics.java | 2 +- .../kinematics/DifferentialDriveOdometry.java | 8 ++--- .../DifferentialDriveWheelSpeeds.java | 2 +- .../kinematics/MecanumDriveKinematics.java | 4 +-- .../kinematics/MecanumDriveMotorVoltages.java | 2 +- .../kinematics/MecanumDriveOdometry.java | 8 ++--- .../kinematics/MecanumDriveWheelSpeeds.java | 2 +- .../kinematics/SwerveDriveKinematics.java | 6 ++-- .../kinematics/SwerveDriveOdometry.java | 8 ++--- .../kinematics/SwerveModuleState.java | 4 +-- .../math/Discretization.java | 10 +++--- .../math/StateSpaceUtil.java | 20 +++++------ .../spline/CubicHermiteSpline.java | 2 +- .../spline/PoseWithCurvature.java | 4 +-- .../spline/QuinticHermiteSpline.java | 2 +- .../{wpilibj => math}/spline/Spline.java | 6 ++-- .../spline/SplineHelper.java | 6 ++-- .../spline/SplineParameterizer.java | 2 +- .../system/LinearSystem.java | 10 +++--- .../system/LinearSystemLoop.java | 16 ++++----- .../system/NumericalIntegration.java | 16 ++++----- .../system/NumericalJacobian.java | 10 +++--- .../system/plant/DCMotor.java | 4 +-- .../system/plant/LinearSystemId.java | 18 +++++----- .../trajectory/Trajectory.java | 6 ++-- .../trajectory/TrajectoryConfig.java | 16 ++++----- .../trajectory/TrajectoryGenerator.java | 20 +++++------ .../trajectory/TrajectoryParameterizer.java | 6 ++-- .../trajectory/TrajectoryUtil.java | 6 ++-- .../trajectory/TrapezoidProfile.java | 2 +- .../CentripetalAccelerationConstraint.java | 4 +-- ...DifferentialDriveKinematicsConstraint.java | 8 ++--- .../DifferentialDriveVoltageConstraint.java | 10 +++--- .../EllipticalRegionConstraint.java | 8 ++--- .../constraint/MaxVelocityConstraint.java | 4 +-- .../MecanumDriveKinematicsConstraint.java | 8 ++--- .../RectangularRegionConstraint.java | 6 ++-- .../SwerveDriveKinematicsConstraint.java | 8 ++--- .../constraint/TrajectoryConstraint.java | 4 +-- .../first/{wpilibj => math}/util/Units.java | 2 +- .../{wpiutil => }/math/MathUtilTest.java | 2 +- .../first/{wpiutil => }/math/MatrixTest.java | 10 +++--- ...olAffinePlantInversionFeedforwardTest.java | 12 +++---- .../LinearPlantInversionFeedforwardTest.java | 12 +++---- .../LinearQuadraticRegulatorTest.java | 14 ++++---- .../controller/LinearSystemLoopTest.java | 24 ++++++------- .../controller/RamseteControllerTest.java | 14 ++++---- .../estimator/AngleStatisticsTest.java | 8 ++--- .../DifferentialDrivePoseEstimatorTest.java | 24 ++++++------- .../estimator/ExtendedKalmanFilterTest.java | 32 ++++++++--------- .../estimator/KalmanFilterTest.java | 30 ++++++++-------- .../MecanumDrivePoseEstimatorTest.java | 18 +++++----- .../estimator/MerweScaledSigmaPointsTest.java | 8 ++--- .../SwerveDrivePoseEstimatorTest.java | 18 +++++----- .../estimator/UnscentedKalmanFilterTest.java | 36 +++++++++---------- .../filter}/LinearFilterTest.java | 2 +- .../filter}/MedianFilterTest.java | 2 +- .../geometry/Pose2dTest.java | 2 +- .../geometry/Rotation2dTest.java | 2 +- .../geometry/Transform2dTest.java | 2 +- .../geometry/Translation2dTest.java | 2 +- .../geometry/Twist2dTest.java | 2 +- .../kinematics/ChassisSpeedsTest.java | 4 +-- .../DifferentialDriveKinematicsTest.java | 2 +- .../DifferentialDriveOdometryTest.java | 6 ++-- .../MecanumDriveKinematicsTest.java | 4 +-- .../kinematics/MecanumDriveOdometryTest.java | 8 ++--- .../kinematics/SwerveDriveKinematicsTest.java | 6 ++-- .../kinematics/SwerveDriveOdometryTest.java | 8 ++--- .../kinematics/SwerveModuleStateTest.java | 4 +-- .../math/StateSpaceUtilTest.java | 18 +++++----- .../spline/CubicHermiteSplineTest.java | 10 +++--- .../spline/QuinticHermiteSplineTest.java | 8 ++--- .../system/LinearSystemIDTest.java | 12 +++---- .../system/NumericalIntegrationTest.java | 10 +++--- ...CentripetalAccelerationConstraintTest.java | 6 ++-- ...erentialDriveKinematicsConstraintTest.java | 10 +++--- ...ifferentialDriveVoltageConstraintTest.java | 16 ++++----- .../EllipticalRegionConstraintTest.java | 14 ++++---- .../RectangularRegionConstraintTest.java | 14 ++++---- .../trajectory/TrajectoryConcatenateTest.java | 6 ++-- .../trajectory/TrajectoryGeneratorTest.java | 14 ++++---- .../trajectory/TrajectoryJsonTest.java | 6 ++-- .../trajectory/TrajectoryTransformTest.java | 10 +++--- .../trajectory/TrapezoidProfileTest.java | 2 +- .../{wpilibj => math}/util/UnitsTest.java | 2 +- 212 files changed, 918 insertions(+), 921 deletions(-) rename wpilibj/src/test/java/edu/wpi/first/{wpilibj => math}/util/ColorTest.java (95%) rename wpilibj/src/test/java/edu/wpi/first/{wpilibj => math}/util/ErrorMessagesTest.java (91%) rename wpimath/src/dev/java/edu/wpi/first/{wpiutil => }/math/DevMain.java (92%) rename wpimath/src/main/java/edu/wpi/first/{wpiutil => }/math/MatBuilder.java (97%) rename wpimath/src/main/java/edu/wpi/first/{wpiutil => }/math/MathUtil.java (98%) rename wpimath/src/main/java/edu/wpi/first/{wpiutil => }/math/Matrix.java (99%) rename wpimath/src/main/java/edu/wpi/first/{wpiutil => }/math/MatrixUtils.java (97%) rename wpimath/src/main/java/edu/wpi/first/{wpiutil => }/math/Num.java (91%) rename wpimath/src/main/java/edu/wpi/first/{wpiutil => }/math/Pair.java (94%) rename wpimath/src/main/java/edu/wpi/first/{wpiutil => }/math/SimpleMatrixUtils.java (99%) rename wpimath/src/main/java/edu/wpi/first/{wpiutil => }/math/VecBuilder.java (91%) rename wpimath/src/main/java/edu/wpi/first/{wpiutil => }/math/Vector.java (95%) rename wpimath/src/main/java/edu/wpi/first/{wpilibj => math}/controller/ArmFeedforward.java (99%) rename wpimath/src/main/java/edu/wpi/first/{wpilibj => math}/controller/ControlAffinePlantInversionFeedforward.java (94%) rename wpimath/src/main/java/edu/wpi/first/{wpilibj => math}/controller/ElevatorFeedforward.java (99%) rename wpimath/src/main/java/edu/wpi/first/{wpilibj => math}/controller/LinearPlantInversionFeedforward.java (94%) rename wpimath/src/main/java/edu/wpi/first/{wpilibj => math}/controller/LinearQuadraticRegulator.java (94%) rename {wpilibj/src/main/java/edu/wpi/first/wpilibj => wpimath/src/main/java/edu/wpi/first/math}/controller/RamseteController.java (96%) rename wpimath/src/main/java/edu/wpi/first/{wpilibj => math}/controller/SimpleMotorFeedforward.java (99%) rename wpimath/src/main/java/edu/wpi/first/{wpilibj => math}/estimator/AngleStatistics.java (94%) rename wpimath/src/main/java/edu/wpi/first/{wpilibj => math}/estimator/DifferentialDrivePoseEstimator.java (94%) rename wpimath/src/main/java/edu/wpi/first/{wpilibj => math}/estimator/ExtendedKalmanFilter.java (96%) rename wpimath/src/main/java/edu/wpi/first/{wpilibj => math}/estimator/KalmanFilter.java (94%) rename wpimath/src/main/java/edu/wpi/first/{wpilibj => math}/estimator/KalmanFilterLatencyCompensator.java (96%) rename wpimath/src/main/java/edu/wpi/first/{wpilibj => math}/estimator/KalmanTypeFilter.java (81%) rename wpimath/src/main/java/edu/wpi/first/{wpilibj => math}/estimator/MecanumDrivePoseEstimator.java (94%) rename wpimath/src/main/java/edu/wpi/first/{wpilibj => math}/estimator/MerweScaledSigmaPoints.java (96%) rename wpimath/src/main/java/edu/wpi/first/{wpilibj => math}/estimator/SwerveDrivePoseEstimator.java (94%) rename wpimath/src/main/java/edu/wpi/first/{wpilibj => math}/estimator/UnscentedKalmanFilter.java (97%) rename wpimath/src/main/java/edu/wpi/first/{wpilibj => math/filter}/LinearFilter.java (99%) rename wpimath/src/main/java/edu/wpi/first/{wpilibj => math/filter}/MedianFilter.java (98%) rename wpimath/src/main/java/edu/wpi/first/{wpilibj => math}/geometry/Pose2d.java (99%) rename wpimath/src/main/java/edu/wpi/first/{wpilibj => math}/geometry/Rotation2d.java (99%) rename wpimath/src/main/java/edu/wpi/first/{wpilibj => math}/geometry/Transform2d.java (98%) rename wpimath/src/main/java/edu/wpi/first/{wpilibj => math}/geometry/Translation2d.java (99%) rename wpimath/src/main/java/edu/wpi/first/{wpilibj => math}/geometry/Twist2d.java (97%) rename wpimath/src/main/java/edu/wpi/first/{wpilibj => math}/kinematics/ChassisSpeeds.java (97%) rename wpimath/src/main/java/edu/wpi/first/{wpilibj => math}/kinematics/DifferentialDriveKinematics.java (98%) rename wpimath/src/main/java/edu/wpi/first/{wpilibj => math}/kinematics/DifferentialDriveOdometry.java (95%) rename wpimath/src/main/java/edu/wpi/first/{wpilibj => math}/kinematics/DifferentialDriveWheelSpeeds.java (98%) rename wpimath/src/main/java/edu/wpi/first/{wpilibj => math}/kinematics/MecanumDriveKinematics.java (98%) rename wpimath/src/main/java/edu/wpi/first/{wpilibj => math}/kinematics/MecanumDriveMotorVoltages.java (97%) rename wpimath/src/main/java/edu/wpi/first/{wpilibj => math}/kinematics/MecanumDriveOdometry.java (96%) rename wpimath/src/main/java/edu/wpi/first/{wpilibj => math}/kinematics/MecanumDriveWheelSpeeds.java (98%) rename wpimath/src/main/java/edu/wpi/first/{wpilibj => math}/kinematics/SwerveDriveKinematics.java (98%) rename wpimath/src/main/java/edu/wpi/first/{wpilibj => math}/kinematics/SwerveDriveOdometry.java (96%) rename wpimath/src/main/java/edu/wpi/first/{wpilibj => math}/kinematics/SwerveModuleState.java (96%) rename wpimath/src/main/java/edu/wpi/first/{wpilibj => math}/math/Discretization.java (96%) rename wpimath/src/main/java/edu/wpi/first/{wpilibj => math}/math/StateSpaceUtil.java (93%) rename wpimath/src/main/java/edu/wpi/first/{wpilibj => math}/spline/CubicHermiteSpline.java (99%) rename wpimath/src/main/java/edu/wpi/first/{wpilibj => math}/spline/PoseWithCurvature.java (91%) rename wpimath/src/main/java/edu/wpi/first/{wpilibj => math}/spline/QuinticHermiteSpline.java (99%) rename wpimath/src/main/java/edu/wpi/first/{wpilibj => math}/spline/Spline.java (95%) rename wpimath/src/main/java/edu/wpi/first/{wpilibj => math}/spline/SplineHelper.java (98%) rename wpimath/src/main/java/edu/wpi/first/{wpilibj => math}/spline/SplineParameterizer.java (99%) rename wpimath/src/main/java/edu/wpi/first/{wpilibj => math}/system/LinearSystem.java (95%) rename wpimath/src/main/java/edu/wpi/first/{wpilibj => math}/system/LinearSystemLoop.java (96%) rename wpimath/src/main/java/edu/wpi/first/{wpilibj => math}/system/NumericalIntegration.java (97%) rename wpimath/src/main/java/edu/wpi/first/{wpilibj => math}/system/NumericalJacobian.java (95%) rename wpimath/src/main/java/edu/wpi/first/{wpilibj => math}/system/plant/DCMotor.java (98%) rename wpimath/src/main/java/edu/wpi/first/{wpilibj => math}/system/plant/LinearSystemId.java (95%) rename wpimath/src/main/java/edu/wpi/first/{wpilibj => math}/trajectory/Trajectory.java (99%) rename wpimath/src/main/java/edu/wpi/first/{wpilibj => math}/trajectory/TrajectoryConfig.java (90%) rename wpimath/src/main/java/edu/wpi/first/{wpilibj => math}/trajectory/TrajectoryGenerator.java (94%) rename wpimath/src/main/java/edu/wpi/first/{wpilibj => math}/trajectory/TrajectoryParameterizer.java (98%) rename wpimath/src/main/java/edu/wpi/first/{wpilibj => math}/trajectory/TrajectoryUtil.java (96%) rename wpimath/src/main/java/edu/wpi/first/{wpilibj => math}/trajectory/TrapezoidProfile.java (99%) rename wpimath/src/main/java/edu/wpi/first/{wpilibj => math}/trajectory/constraint/CentripetalAccelerationConstraint.java (96%) rename wpimath/src/main/java/edu/wpi/first/{wpilibj => math}/trajectory/constraint/DifferentialDriveKinematicsConstraint.java (92%) rename wpimath/src/main/java/edu/wpi/first/{wpilibj => math}/trajectory/constraint/DifferentialDriveVoltageConstraint.java (94%) rename wpimath/src/main/java/edu/wpi/first/{wpilibj => math}/trajectory/constraint/EllipticalRegionConstraint.java (93%) rename wpimath/src/main/java/edu/wpi/first/{wpilibj => math}/trajectory/constraint/MaxVelocityConstraint.java (92%) rename wpimath/src/main/java/edu/wpi/first/{wpilibj => math}/trajectory/constraint/MecanumDriveKinematicsConstraint.java (93%) rename wpimath/src/main/java/edu/wpi/first/{wpilibj => math}/trajectory/constraint/RectangularRegionConstraint.java (94%) rename wpimath/src/main/java/edu/wpi/first/{wpilibj => math}/trajectory/constraint/SwerveDriveKinematicsConstraint.java (93%) rename wpimath/src/main/java/edu/wpi/first/{wpilibj => math}/trajectory/constraint/TrajectoryConstraint.java (95%) rename wpimath/src/main/java/edu/wpi/first/{wpilibj => math}/util/Units.java (98%) rename wpimath/src/test/java/edu/wpi/first/{wpiutil => }/math/MathUtilTest.java (98%) rename wpimath/src/test/java/edu/wpi/first/{wpiutil => }/math/MatrixTest.java (94%) rename wpimath/src/test/java/edu/wpi/first/{wpilibj => math}/controller/ControlAffinePlantInversionFeedforwardTest.java (86%) rename wpimath/src/test/java/edu/wpi/first/{wpilibj => math}/controller/LinearPlantInversionFeedforwardTest.java (75%) rename wpimath/src/test/java/edu/wpi/first/{wpilibj => math}/controller/LinearQuadraticRegulatorTest.java (89%) rename wpimath/src/test/java/edu/wpi/first/{wpilibj => math}/controller/LinearSystemLoopTest.java (89%) rename {wpilibj/src/test/java/edu/wpi/first/wpilibj => wpimath/src/test/java/edu/wpi/first/math}/controller/RamseteControllerTest.java (86%) rename wpimath/src/test/java/edu/wpi/first/{wpilibj => math}/estimator/AngleStatisticsTest.java (89%) rename wpimath/src/test/java/edu/wpi/first/{wpilibj => math}/estimator/DifferentialDrivePoseEstimatorTest.java (85%) rename wpimath/src/test/java/edu/wpi/first/{wpilibj => math}/estimator/ExtendedKalmanFilterTest.java (91%) rename wpimath/src/test/java/edu/wpi/first/{wpilibj => math}/estimator/KalmanFilterTest.java (91%) rename wpimath/src/test/java/edu/wpi/first/{wpilibj => math}/estimator/MecanumDrivePoseEstimatorTest.java (88%) rename wpimath/src/test/java/edu/wpi/first/{wpilibj => math}/estimator/MerweScaledSigmaPointsTest.java (88%) rename wpimath/src/test/java/edu/wpi/first/{wpilibj => math}/estimator/SwerveDrivePoseEstimatorTest.java (88%) rename wpimath/src/test/java/edu/wpi/first/{wpilibj => math}/estimator/UnscentedKalmanFilterTest.java (94%) rename wpimath/src/test/java/edu/wpi/first/{wpilibj => math/filter}/LinearFilterTest.java (99%) rename wpimath/src/test/java/edu/wpi/first/{wpilibj => math/filter}/MedianFilterTest.java (97%) rename wpimath/src/test/java/edu/wpi/first/{wpilibj => math}/geometry/Pose2dTest.java (98%) rename wpimath/src/test/java/edu/wpi/first/{wpilibj => math}/geometry/Rotation2dTest.java (98%) rename wpimath/src/test/java/edu/wpi/first/{wpilibj => math}/geometry/Transform2dTest.java (96%) rename wpimath/src/test/java/edu/wpi/first/{wpilibj => math}/geometry/Translation2dTest.java (98%) rename wpimath/src/test/java/edu/wpi/first/{wpilibj => math}/geometry/Twist2dTest.java (98%) rename wpimath/src/test/java/edu/wpi/first/{wpilibj => math}/kinematics/ChassisSpeedsTest.java (90%) rename wpimath/src/test/java/edu/wpi/first/{wpilibj => math}/kinematics/DifferentialDriveKinematicsTest.java (98%) rename wpimath/src/test/java/edu/wpi/first/{wpilibj => math}/kinematics/DifferentialDriveOdometryTest.java (87%) rename wpimath/src/test/java/edu/wpi/first/{wpilibj => math}/kinematics/MecanumDriveKinematicsTest.java (98%) rename wpimath/src/test/java/edu/wpi/first/{wpilibj => math}/kinematics/MecanumDriveOdometryTest.java (94%) rename wpimath/src/test/java/edu/wpi/first/{wpilibj => math}/kinematics/SwerveDriveKinematicsTest.java (98%) rename wpimath/src/test/java/edu/wpi/first/{wpilibj => math}/kinematics/SwerveDriveOdometryTest.java (94%) rename wpimath/src/test/java/edu/wpi/first/{wpilibj => math}/kinematics/SwerveModuleStateTest.java (95%) rename wpimath/src/test/java/edu/wpi/first/{wpilibj => math}/math/StateSpaceUtilTest.java (94%) rename wpimath/src/test/java/edu/wpi/first/{wpilibj => math}/spline/CubicHermiteSplineTest.java (95%) rename wpimath/src/test/java/edu/wpi/first/{wpilibj => math}/spline/QuinticHermiteSplineTest.java (94%) rename wpimath/src/test/java/edu/wpi/first/{wpilibj => math}/system/LinearSystemIDTest.java (91%) rename wpimath/src/test/java/edu/wpi/first/{wpilibj => math}/system/NumericalIntegrationTest.java (87%) rename wpimath/src/test/java/edu/wpi/first/{wpilibj => math}/trajectory/CentripetalAccelerationConstraintTest.java (87%) rename wpimath/src/test/java/edu/wpi/first/{wpilibj => math}/trajectory/DifferentialDriveKinematicsConstraintTest.java (84%) rename wpimath/src/test/java/edu/wpi/first/{wpilibj => math}/trajectory/DifferentialDriveVoltageConstraintTest.java (88%) rename wpimath/src/test/java/edu/wpi/first/{wpilibj => math}/trajectory/EllipticalRegionConstraintTest.java (87%) rename wpimath/src/test/java/edu/wpi/first/{wpilibj => math}/trajectory/RectangularRegionConstraintTest.java (84%) rename wpimath/src/test/java/edu/wpi/first/{wpilibj => math}/trajectory/TrajectoryConcatenateTest.java (91%) rename wpimath/src/test/java/edu/wpi/first/{wpilibj => math}/trajectory/TrajectoryGeneratorTest.java (87%) rename wpimath/src/test/java/edu/wpi/first/{wpilibj => math}/trajectory/TrajectoryJsonTest.java (81%) rename wpimath/src/test/java/edu/wpi/first/{wpilibj => math}/trajectory/TrajectoryTransformTest.java (89%) rename wpimath/src/test/java/edu/wpi/first/{wpilibj => math}/trajectory/TrapezoidProfileTest.java (99%) rename wpimath/src/test/java/edu/wpi/first/{wpilibj => math}/util/UnitsTest.java (97%) diff --git a/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/MecanumControllerCommand.java b/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/MecanumControllerCommand.java index 9f7f399c68..fc1b5fd391 100644 --- a/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/MecanumControllerCommand.java +++ b/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/MecanumControllerCommand.java @@ -6,18 +6,18 @@ package edu.wpi.first.wpilibj2.command; import static edu.wpi.first.wpilibj.util.ErrorMessages.requireNonNullParam; +import edu.wpi.first.math.controller.SimpleMotorFeedforward; +import edu.wpi.first.math.geometry.Pose2d; +import edu.wpi.first.math.geometry.Rotation2d; +import edu.wpi.first.math.kinematics.ChassisSpeeds; +import edu.wpi.first.math.kinematics.MecanumDriveKinematics; +import edu.wpi.first.math.kinematics.MecanumDriveMotorVoltages; +import edu.wpi.first.math.kinematics.MecanumDriveWheelSpeeds; +import edu.wpi.first.math.trajectory.Trajectory; import edu.wpi.first.wpilibj.Timer; import edu.wpi.first.wpilibj.controller.HolonomicDriveController; import edu.wpi.first.wpilibj.controller.PIDController; import edu.wpi.first.wpilibj.controller.ProfiledPIDController; -import edu.wpi.first.wpilibj.controller.SimpleMotorFeedforward; -import edu.wpi.first.wpilibj.geometry.Pose2d; -import edu.wpi.first.wpilibj.geometry.Rotation2d; -import edu.wpi.first.wpilibj.kinematics.ChassisSpeeds; -import edu.wpi.first.wpilibj.kinematics.MecanumDriveKinematics; -import edu.wpi.first.wpilibj.kinematics.MecanumDriveMotorVoltages; -import edu.wpi.first.wpilibj.kinematics.MecanumDriveWheelSpeeds; -import edu.wpi.first.wpilibj.trajectory.Trajectory; import java.util.function.Consumer; import java.util.function.Supplier; diff --git a/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/ProfiledPIDCommand.java b/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/ProfiledPIDCommand.java index b3a747ce79..a3d874f721 100644 --- a/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/ProfiledPIDCommand.java +++ b/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/ProfiledPIDCommand.java @@ -4,7 +4,7 @@ package edu.wpi.first.wpilibj2.command; -import static edu.wpi.first.wpilibj.trajectory.TrapezoidProfile.State; +import static edu.wpi.first.math.trajectory.TrapezoidProfile.State; import static edu.wpi.first.wpilibj.util.ErrorMessages.requireNonNullParam; import edu.wpi.first.wpilibj.controller.ProfiledPIDController; diff --git a/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/ProfiledPIDSubsystem.java b/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/ProfiledPIDSubsystem.java index 6139961b64..f960f8b63d 100644 --- a/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/ProfiledPIDSubsystem.java +++ b/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/ProfiledPIDSubsystem.java @@ -4,11 +4,11 @@ package edu.wpi.first.wpilibj2.command; -import static edu.wpi.first.wpilibj.trajectory.TrapezoidProfile.State; +import static edu.wpi.first.math.trajectory.TrapezoidProfile.State; import static edu.wpi.first.wpilibj.util.ErrorMessages.requireNonNullParam; +import edu.wpi.first.math.trajectory.TrapezoidProfile; import edu.wpi.first.wpilibj.controller.ProfiledPIDController; -import edu.wpi.first.wpilibj.trajectory.TrapezoidProfile; /** * A subsystem that uses a {@link ProfiledPIDController} to control an output. The controller is run diff --git a/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/RamseteCommand.java b/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/RamseteCommand.java index 2bba7ea787..2ae3631bfc 100644 --- a/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/RamseteCommand.java +++ b/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/RamseteCommand.java @@ -6,15 +6,15 @@ package edu.wpi.first.wpilibj2.command; import static edu.wpi.first.wpilibj.util.ErrorMessages.requireNonNullParam; +import edu.wpi.first.math.controller.RamseteController; +import edu.wpi.first.math.controller.SimpleMotorFeedforward; +import edu.wpi.first.math.geometry.Pose2d; +import edu.wpi.first.math.kinematics.ChassisSpeeds; +import edu.wpi.first.math.kinematics.DifferentialDriveKinematics; +import edu.wpi.first.math.kinematics.DifferentialDriveWheelSpeeds; +import edu.wpi.first.math.trajectory.Trajectory; import edu.wpi.first.wpilibj.Timer; import edu.wpi.first.wpilibj.controller.PIDController; -import edu.wpi.first.wpilibj.controller.RamseteController; -import edu.wpi.first.wpilibj.controller.SimpleMotorFeedforward; -import edu.wpi.first.wpilibj.geometry.Pose2d; -import edu.wpi.first.wpilibj.kinematics.ChassisSpeeds; -import edu.wpi.first.wpilibj.kinematics.DifferentialDriveKinematics; -import edu.wpi.first.wpilibj.kinematics.DifferentialDriveWheelSpeeds; -import edu.wpi.first.wpilibj.trajectory.Trajectory; import java.util.function.BiConsumer; import java.util.function.Supplier; diff --git a/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/SwerveControllerCommand.java b/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/SwerveControllerCommand.java index 106d2d0097..52cc658925 100644 --- a/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/SwerveControllerCommand.java +++ b/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/SwerveControllerCommand.java @@ -6,15 +6,15 @@ package edu.wpi.first.wpilibj2.command; import static edu.wpi.first.wpilibj.util.ErrorMessages.requireNonNullParam; +import edu.wpi.first.math.geometry.Pose2d; +import edu.wpi.first.math.geometry.Rotation2d; +import edu.wpi.first.math.kinematics.SwerveDriveKinematics; +import edu.wpi.first.math.kinematics.SwerveModuleState; +import edu.wpi.first.math.trajectory.Trajectory; import edu.wpi.first.wpilibj.Timer; import edu.wpi.first.wpilibj.controller.HolonomicDriveController; import edu.wpi.first.wpilibj.controller.PIDController; import edu.wpi.first.wpilibj.controller.ProfiledPIDController; -import edu.wpi.first.wpilibj.geometry.Pose2d; -import edu.wpi.first.wpilibj.geometry.Rotation2d; -import edu.wpi.first.wpilibj.kinematics.SwerveDriveKinematics; -import edu.wpi.first.wpilibj.kinematics.SwerveModuleState; -import edu.wpi.first.wpilibj.trajectory.Trajectory; import java.util.function.Consumer; import java.util.function.Supplier; diff --git a/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/TrapezoidProfileCommand.java b/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/TrapezoidProfileCommand.java index 90b2011c5c..674328ea23 100644 --- a/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/TrapezoidProfileCommand.java +++ b/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/TrapezoidProfileCommand.java @@ -4,11 +4,11 @@ package edu.wpi.first.wpilibj2.command; -import static edu.wpi.first.wpilibj.trajectory.TrapezoidProfile.State; +import static edu.wpi.first.math.trajectory.TrapezoidProfile.State; import static edu.wpi.first.wpilibj.util.ErrorMessages.requireNonNullParam; +import edu.wpi.first.math.trajectory.TrapezoidProfile; import edu.wpi.first.wpilibj.Timer; -import edu.wpi.first.wpilibj.trajectory.TrapezoidProfile; import java.util.function.Consumer; /** diff --git a/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/TrapezoidProfileSubsystem.java b/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/TrapezoidProfileSubsystem.java index 330a633ac1..0a6c65877b 100644 --- a/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/TrapezoidProfileSubsystem.java +++ b/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/TrapezoidProfileSubsystem.java @@ -6,7 +6,7 @@ package edu.wpi.first.wpilibj2.command; import static edu.wpi.first.wpilibj.util.ErrorMessages.requireNonNullParam; -import edu.wpi.first.wpilibj.trajectory.TrapezoidProfile; +import edu.wpi.first.math.trajectory.TrapezoidProfile; /** * A subsystem that generates and runs trapezoidal motion profiles automatically. The user specifies diff --git a/wpilibNewCommands/src/test/java/edu/wpi/first/wpilibj2/command/MecanumControllerCommandTest.java b/wpilibNewCommands/src/test/java/edu/wpi/first/wpilibj2/command/MecanumControllerCommandTest.java index 8c5489d7ea..16cab4d12d 100644 --- a/wpilibNewCommands/src/test/java/edu/wpi/first/wpilibj2/command/MecanumControllerCommandTest.java +++ b/wpilibNewCommands/src/test/java/edu/wpi/first/wpilibj2/command/MecanumControllerCommandTest.java @@ -8,19 +8,19 @@ import static org.junit.jupiter.api.Assertions.assertAll; import static org.junit.jupiter.api.Assertions.assertEquals; import edu.wpi.first.hal.HAL; +import edu.wpi.first.math.geometry.Pose2d; +import edu.wpi.first.math.geometry.Rotation2d; +import edu.wpi.first.math.geometry.Translation2d; +import edu.wpi.first.math.kinematics.MecanumDriveKinematics; +import edu.wpi.first.math.kinematics.MecanumDriveOdometry; +import edu.wpi.first.math.kinematics.MecanumDriveWheelSpeeds; +import edu.wpi.first.math.trajectory.TrajectoryConfig; +import edu.wpi.first.math.trajectory.TrajectoryGenerator; +import edu.wpi.first.math.trajectory.TrapezoidProfile; import edu.wpi.first.wpilibj.Timer; import edu.wpi.first.wpilibj.controller.PIDController; import edu.wpi.first.wpilibj.controller.ProfiledPIDController; -import edu.wpi.first.wpilibj.geometry.Pose2d; -import edu.wpi.first.wpilibj.geometry.Rotation2d; -import edu.wpi.first.wpilibj.geometry.Translation2d; -import edu.wpi.first.wpilibj.kinematics.MecanumDriveKinematics; -import edu.wpi.first.wpilibj.kinematics.MecanumDriveOdometry; -import edu.wpi.first.wpilibj.kinematics.MecanumDriveWheelSpeeds; import edu.wpi.first.wpilibj.simulation.SimHooks; -import edu.wpi.first.wpilibj.trajectory.TrajectoryConfig; -import edu.wpi.first.wpilibj.trajectory.TrajectoryGenerator; -import edu.wpi.first.wpilibj.trajectory.TrapezoidProfile; import java.util.ArrayList; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; diff --git a/wpilibNewCommands/src/test/java/edu/wpi/first/wpilibj2/command/SwerveControllerCommandTest.java b/wpilibNewCommands/src/test/java/edu/wpi/first/wpilibj2/command/SwerveControllerCommandTest.java index 8610cde69b..3b48ff60b5 100644 --- a/wpilibNewCommands/src/test/java/edu/wpi/first/wpilibj2/command/SwerveControllerCommandTest.java +++ b/wpilibNewCommands/src/test/java/edu/wpi/first/wpilibj2/command/SwerveControllerCommandTest.java @@ -8,19 +8,19 @@ import static org.junit.jupiter.api.Assertions.assertAll; import static org.junit.jupiter.api.Assertions.assertEquals; import edu.wpi.first.hal.HAL; +import edu.wpi.first.math.geometry.Pose2d; +import edu.wpi.first.math.geometry.Rotation2d; +import edu.wpi.first.math.geometry.Translation2d; +import edu.wpi.first.math.kinematics.SwerveDriveKinematics; +import edu.wpi.first.math.kinematics.SwerveDriveOdometry; +import edu.wpi.first.math.kinematics.SwerveModuleState; +import edu.wpi.first.math.trajectory.TrajectoryConfig; +import edu.wpi.first.math.trajectory.TrajectoryGenerator; +import edu.wpi.first.math.trajectory.TrapezoidProfile; import edu.wpi.first.wpilibj.Timer; import edu.wpi.first.wpilibj.controller.PIDController; import edu.wpi.first.wpilibj.controller.ProfiledPIDController; -import edu.wpi.first.wpilibj.geometry.Pose2d; -import edu.wpi.first.wpilibj.geometry.Rotation2d; -import edu.wpi.first.wpilibj.geometry.Translation2d; -import edu.wpi.first.wpilibj.kinematics.SwerveDriveKinematics; -import edu.wpi.first.wpilibj.kinematics.SwerveDriveOdometry; -import edu.wpi.first.wpilibj.kinematics.SwerveModuleState; import edu.wpi.first.wpilibj.simulation.SimHooks; -import edu.wpi.first.wpilibj.trajectory.TrajectoryConfig; -import edu.wpi.first.wpilibj.trajectory.TrajectoryGenerator; -import edu.wpi.first.wpilibj.trajectory.TrapezoidProfile; import java.util.ArrayList; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; diff --git a/wpilibOldCommands/src/main/java/edu/wpi/first/wpilibj/PIDBase.java b/wpilibOldCommands/src/main/java/edu/wpi/first/wpilibj/PIDBase.java index 7116077bc5..cde77003bb 100644 --- a/wpilibOldCommands/src/main/java/edu/wpi/first/wpilibj/PIDBase.java +++ b/wpilibOldCommands/src/main/java/edu/wpi/first/wpilibj/PIDBase.java @@ -9,6 +9,7 @@ import static edu.wpi.first.wpilibj.util.ErrorMessages.requireNonNullParam; import edu.wpi.first.hal.FRCNetComm.tResourceType; import edu.wpi.first.hal.HAL; import edu.wpi.first.hal.util.BoundaryException; +import edu.wpi.first.math.filter.LinearFilter; import edu.wpi.first.wpilibj.smartdashboard.SendableBuilder; import edu.wpi.first.wpilibj.smartdashboard.SendableRegistry; import java.util.concurrent.locks.ReentrantLock; diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/SlewRateLimiter.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/SlewRateLimiter.java index 5569ba9ac0..849a869588 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/SlewRateLimiter.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/SlewRateLimiter.java @@ -4,13 +4,13 @@ package edu.wpi.first.wpilibj; -import edu.wpi.first.wpiutil.math.MathUtil; +import edu.wpi.first.math.MathUtil; /** * A class that limits the rate of change of an input value. Useful for implementing voltage, * setpoint, and/or output ramps. A slew-rate limit is most appropriate when the quantity being * controlled is a velocity or a voltage; when controlling a position, consider using a {@link - * edu.wpi.first.wpilibj.trajectory.TrapezoidProfile} instead. + * edu.wpi.first.math.trajectory.TrapezoidProfile} instead. */ public class SlewRateLimiter { private final double m_rateLimit; diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/controller/HolonomicDriveController.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/controller/HolonomicDriveController.java index f1d7f81da7..6cadf487b9 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/controller/HolonomicDriveController.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/controller/HolonomicDriveController.java @@ -4,10 +4,10 @@ package edu.wpi.first.wpilibj.controller; -import edu.wpi.first.wpilibj.geometry.Pose2d; -import edu.wpi.first.wpilibj.geometry.Rotation2d; -import edu.wpi.first.wpilibj.kinematics.ChassisSpeeds; -import edu.wpi.first.wpilibj.trajectory.Trajectory; +import edu.wpi.first.math.geometry.Pose2d; +import edu.wpi.first.math.geometry.Rotation2d; +import edu.wpi.first.math.kinematics.ChassisSpeeds; +import edu.wpi.first.math.trajectory.Trajectory; /** * This holonomic drive controller can be used to follow trajectories using a holonomic drive train diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/controller/PIDController.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/controller/PIDController.java index 08a0e28ec5..68e648da60 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/controller/PIDController.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/controller/PIDController.java @@ -6,10 +6,10 @@ package edu.wpi.first.wpilibj.controller; import edu.wpi.first.hal.FRCNetComm.tResourceType; import edu.wpi.first.hal.HAL; +import edu.wpi.first.math.MathUtil; import edu.wpi.first.wpilibj.Sendable; import edu.wpi.first.wpilibj.smartdashboard.SendableBuilder; import edu.wpi.first.wpilibj.smartdashboard.SendableRegistry; -import edu.wpi.first.wpiutil.math.MathUtil; /** Implements a PID control loop. */ @SuppressWarnings("PMD.TooManyFields") diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/controller/ProfiledPIDController.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/controller/ProfiledPIDController.java index 427291875b..514bd31dd3 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/controller/ProfiledPIDController.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/controller/ProfiledPIDController.java @@ -6,10 +6,10 @@ package edu.wpi.first.wpilibj.controller; import edu.wpi.first.hal.FRCNetComm.tResourceType; import edu.wpi.first.hal.HAL; +import edu.wpi.first.math.MathUtil; +import edu.wpi.first.math.trajectory.TrapezoidProfile; import edu.wpi.first.wpilibj.Sendable; import edu.wpi.first.wpilibj.smartdashboard.SendableBuilder; -import edu.wpi.first.wpilibj.trajectory.TrapezoidProfile; -import edu.wpi.first.wpiutil.math.MathUtil; /** * Implements a PID control loop whose setpoint is constrained by a trapezoid profile. Users should diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/drive/DifferentialDrive.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/drive/DifferentialDrive.java index 3231899673..90188ed91e 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/drive/DifferentialDrive.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/drive/DifferentialDrive.java @@ -7,11 +7,11 @@ package edu.wpi.first.wpilibj.drive; import edu.wpi.first.hal.FRCNetComm.tInstances; import edu.wpi.first.hal.FRCNetComm.tResourceType; import edu.wpi.first.hal.HAL; +import edu.wpi.first.math.MathUtil; import edu.wpi.first.wpilibj.Sendable; import edu.wpi.first.wpilibj.SpeedController; import edu.wpi.first.wpilibj.smartdashboard.SendableBuilder; import edu.wpi.first.wpilibj.smartdashboard.SendableRegistry; -import edu.wpi.first.wpiutil.math.MathUtil; import java.util.StringJoiner; /** diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/drive/KilloughDrive.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/drive/KilloughDrive.java index 6702fb84f6..b7dff3db4c 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/drive/KilloughDrive.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/drive/KilloughDrive.java @@ -7,11 +7,11 @@ package edu.wpi.first.wpilibj.drive; import edu.wpi.first.hal.FRCNetComm.tInstances; import edu.wpi.first.hal.FRCNetComm.tResourceType; import edu.wpi.first.hal.HAL; +import edu.wpi.first.math.MathUtil; import edu.wpi.first.wpilibj.Sendable; import edu.wpi.first.wpilibj.SpeedController; import edu.wpi.first.wpilibj.smartdashboard.SendableBuilder; import edu.wpi.first.wpilibj.smartdashboard.SendableRegistry; -import edu.wpi.first.wpiutil.math.MathUtil; import java.util.StringJoiner; /** diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/drive/MecanumDrive.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/drive/MecanumDrive.java index 7781b9409f..0fda4a0bfa 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/drive/MecanumDrive.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/drive/MecanumDrive.java @@ -7,11 +7,11 @@ package edu.wpi.first.wpilibj.drive; import edu.wpi.first.hal.FRCNetComm.tInstances; import edu.wpi.first.hal.FRCNetComm.tResourceType; import edu.wpi.first.hal.HAL; +import edu.wpi.first.math.MathUtil; import edu.wpi.first.wpilibj.Sendable; import edu.wpi.first.wpilibj.SpeedController; import edu.wpi.first.wpilibj.smartdashboard.SendableBuilder; import edu.wpi.first.wpilibj.smartdashboard.SendableRegistry; -import edu.wpi.first.wpiutil.math.MathUtil; import java.util.StringJoiner; /** diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/interfaces/Gyro.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/interfaces/Gyro.java index 8b9f9a7206..27377be03e 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/interfaces/Gyro.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/interfaces/Gyro.java @@ -4,7 +4,7 @@ package edu.wpi.first.wpilibj.interfaces; -import edu.wpi.first.wpilibj.geometry.Rotation2d; +import edu.wpi.first.math.geometry.Rotation2d; /** Interface for yaw rate gyros. */ public interface Gyro extends AutoCloseable { @@ -50,7 +50,7 @@ public interface Gyro extends AutoCloseable { double getRate(); /** - * Return the heading of the robot as a {@link edu.wpi.first.wpilibj.geometry.Rotation2d}. + * Return the heading of the robot as a {@link edu.wpi.first.math.geometry.Rotation2d}. * *

        The angle is continuous, that is it will continue from 360 to 361 degrees. This allows * algorithms that wouldn't want to see a discontinuity in the gyro output as it sweeps past from @@ -61,8 +61,7 @@ public interface Gyro extends AutoCloseable { * *

        This heading is based on integration of the returned rate from the gyro. * - * @return the current heading of the robot as a {@link - * edu.wpi.first.wpilibj.geometry.Rotation2d}. + * @return the current heading of the robot as a {@link edu.wpi.first.math.geometry.Rotation2d}. */ default Rotation2d getRotation2d() { return Rotation2d.fromDegrees(-getAngle()); diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/simulation/AnalogEncoderSim.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/simulation/AnalogEncoderSim.java index 676b13c49d..12c9b6d576 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/simulation/AnalogEncoderSim.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/simulation/AnalogEncoderSim.java @@ -5,8 +5,8 @@ package edu.wpi.first.wpilibj.simulation; import edu.wpi.first.hal.SimDouble; +import edu.wpi.first.math.geometry.Rotation2d; import edu.wpi.first.wpilibj.AnalogEncoder; -import edu.wpi.first.wpilibj.geometry.Rotation2d; /** Class to control a simulated analog encoder. */ public class AnalogEncoderSim { diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/simulation/DifferentialDrivetrainSim.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/simulation/DifferentialDrivetrainSim.java index fffd441d97..c265edd8c5 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/simulation/DifferentialDrivetrainSim.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/simulation/DifferentialDrivetrainSim.java @@ -4,21 +4,21 @@ package edu.wpi.first.wpilibj.simulation; +import edu.wpi.first.math.Matrix; +import edu.wpi.first.math.Nat; +import edu.wpi.first.math.VecBuilder; +import edu.wpi.first.math.geometry.Pose2d; +import edu.wpi.first.math.geometry.Rotation2d; +import edu.wpi.first.math.math.StateSpaceUtil; +import edu.wpi.first.math.numbers.N1; +import edu.wpi.first.math.numbers.N2; +import edu.wpi.first.math.numbers.N7; +import edu.wpi.first.math.system.LinearSystem; +import edu.wpi.first.math.system.NumericalIntegration; +import edu.wpi.first.math.system.plant.DCMotor; +import edu.wpi.first.math.system.plant.LinearSystemId; +import edu.wpi.first.math.util.Units; import edu.wpi.first.wpilibj.RobotController; -import edu.wpi.first.wpilibj.geometry.Pose2d; -import edu.wpi.first.wpilibj.geometry.Rotation2d; -import edu.wpi.first.wpilibj.math.StateSpaceUtil; -import edu.wpi.first.wpilibj.system.LinearSystem; -import edu.wpi.first.wpilibj.system.NumericalIntegration; -import edu.wpi.first.wpilibj.system.plant.DCMotor; -import edu.wpi.first.wpilibj.system.plant.LinearSystemId; -import edu.wpi.first.wpilibj.util.Units; -import edu.wpi.first.wpiutil.math.Matrix; -import edu.wpi.first.wpiutil.math.Nat; -import edu.wpi.first.wpiutil.math.VecBuilder; -import edu.wpi.first.wpiutil.math.numbers.N1; -import edu.wpi.first.wpiutil.math.numbers.N2; -import edu.wpi.first.wpiutil.math.numbers.N7; /** * This class simulates the state of the drivetrain. In simulationPeriodic, users should first set @@ -101,9 +101,9 @@ public class DifferentialDrivetrainSim { * * @param drivetrainPlant The {@link LinearSystem} representing the robot's drivetrain. This * system can be created with {@link - * edu.wpi.first.wpilibj.system.plant.LinearSystemId#createDrivetrainVelocitySystem(DCMotor, + * edu.wpi.first.math.system.plant.LinearSystemId#createDrivetrainVelocitySystem(DCMotor, * double, double, double, double, double)} or {@link - * edu.wpi.first.wpilibj.system.plant.LinearSystemId#identifyDrivetrainSystem(double, double, + * edu.wpi.first.math.system.plant.LinearSystemId#identifyDrivetrainSystem(double, double, * double, double)}. * @param driveMotor A {@link DCMotor} representing the drivetrain. * @param gearing The gearingRatio ratio of the robot, as output over input. This must be the same diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/simulation/ElevatorSim.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/simulation/ElevatorSim.java index cc271e21a5..753d61afb7 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/simulation/ElevatorSim.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/simulation/ElevatorSim.java @@ -4,14 +4,14 @@ package edu.wpi.first.wpilibj.simulation; -import edu.wpi.first.wpilibj.system.LinearSystem; -import edu.wpi.first.wpilibj.system.NumericalIntegration; -import edu.wpi.first.wpilibj.system.plant.DCMotor; -import edu.wpi.first.wpilibj.system.plant.LinearSystemId; -import edu.wpi.first.wpiutil.math.Matrix; -import edu.wpi.first.wpiutil.math.VecBuilder; -import edu.wpi.first.wpiutil.math.numbers.N1; -import edu.wpi.first.wpiutil.math.numbers.N2; +import edu.wpi.first.math.Matrix; +import edu.wpi.first.math.VecBuilder; +import edu.wpi.first.math.numbers.N1; +import edu.wpi.first.math.numbers.N2; +import edu.wpi.first.math.system.LinearSystem; +import edu.wpi.first.math.system.NumericalIntegration; +import edu.wpi.first.math.system.plant.DCMotor; +import edu.wpi.first.math.system.plant.LinearSystemId; /** Represents a simulated elevator mechanism. */ public class ElevatorSim extends LinearSystemSim { diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/simulation/FlywheelSim.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/simulation/FlywheelSim.java index 8dd7bc43dd..aaf13de0e4 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/simulation/FlywheelSim.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/simulation/FlywheelSim.java @@ -4,12 +4,12 @@ package edu.wpi.first.wpilibj.simulation; -import edu.wpi.first.wpilibj.system.LinearSystem; -import edu.wpi.first.wpilibj.system.plant.DCMotor; -import edu.wpi.first.wpilibj.system.plant.LinearSystemId; -import edu.wpi.first.wpilibj.util.Units; -import edu.wpi.first.wpiutil.math.Matrix; -import edu.wpi.first.wpiutil.math.numbers.N1; +import edu.wpi.first.math.Matrix; +import edu.wpi.first.math.numbers.N1; +import edu.wpi.first.math.system.LinearSystem; +import edu.wpi.first.math.system.plant.DCMotor; +import edu.wpi.first.math.system.plant.LinearSystemId; +import edu.wpi.first.math.util.Units; /** Represents a simulated flywheel mechanism. */ public class FlywheelSim extends LinearSystemSim { diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/simulation/LinearSystemSim.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/simulation/LinearSystemSim.java index aec9ce7273..155ec23ca3 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/simulation/LinearSystemSim.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/simulation/LinearSystemSim.java @@ -4,12 +4,12 @@ package edu.wpi.first.wpilibj.simulation; +import edu.wpi.first.math.Matrix; +import edu.wpi.first.math.Num; +import edu.wpi.first.math.math.StateSpaceUtil; +import edu.wpi.first.math.numbers.N1; +import edu.wpi.first.math.system.LinearSystem; import edu.wpi.first.wpilibj.RobotController; -import edu.wpi.first.wpilibj.math.StateSpaceUtil; -import edu.wpi.first.wpilibj.system.LinearSystem; -import edu.wpi.first.wpiutil.math.Matrix; -import edu.wpi.first.wpiutil.math.Num; -import edu.wpi.first.wpiutil.math.numbers.N1; import org.ejml.MatrixDimensionException; import org.ejml.simple.SimpleMatrix; diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/simulation/SingleJointedArmSim.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/simulation/SingleJointedArmSim.java index c0ecc9bede..3891090892 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/simulation/SingleJointedArmSim.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/simulation/SingleJointedArmSim.java @@ -4,14 +4,14 @@ package edu.wpi.first.wpilibj.simulation; -import edu.wpi.first.wpilibj.system.LinearSystem; -import edu.wpi.first.wpilibj.system.NumericalIntegration; -import edu.wpi.first.wpilibj.system.plant.DCMotor; -import edu.wpi.first.wpilibj.system.plant.LinearSystemId; -import edu.wpi.first.wpiutil.math.Matrix; -import edu.wpi.first.wpiutil.math.VecBuilder; -import edu.wpi.first.wpiutil.math.numbers.N1; -import edu.wpi.first.wpiutil.math.numbers.N2; +import edu.wpi.first.math.Matrix; +import edu.wpi.first.math.VecBuilder; +import edu.wpi.first.math.numbers.N1; +import edu.wpi.first.math.numbers.N2; +import edu.wpi.first.math.system.LinearSystem; +import edu.wpi.first.math.system.NumericalIntegration; +import edu.wpi.first.math.system.plant.DCMotor; +import edu.wpi.first.math.system.plant.LinearSystemId; /** Represents a simulated single jointed arm mechanism. */ public class SingleJointedArmSim extends LinearSystemSim { diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/smartdashboard/Field2d.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/smartdashboard/Field2d.java index 01181443f6..ad4ba32e97 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/smartdashboard/Field2d.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/smartdashboard/Field2d.java @@ -4,10 +4,10 @@ package edu.wpi.first.wpilibj.smartdashboard; +import edu.wpi.first.math.geometry.Pose2d; +import edu.wpi.first.math.geometry.Rotation2d; import edu.wpi.first.networktables.NetworkTable; import edu.wpi.first.wpilibj.Sendable; -import edu.wpi.first.wpilibj.geometry.Pose2d; -import edu.wpi.first.wpilibj.geometry.Rotation2d; import java.util.ArrayList; import java.util.List; diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/smartdashboard/FieldObject2d.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/smartdashboard/FieldObject2d.java index aee7e13dac..ae06494e3f 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/smartdashboard/FieldObject2d.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/smartdashboard/FieldObject2d.java @@ -4,11 +4,11 @@ package edu.wpi.first.wpilibj.smartdashboard; +import edu.wpi.first.math.geometry.Pose2d; +import edu.wpi.first.math.geometry.Rotation2d; +import edu.wpi.first.math.geometry.Translation2d; +import edu.wpi.first.math.trajectory.Trajectory; import edu.wpi.first.networktables.NetworkTableEntry; -import edu.wpi.first.wpilibj.geometry.Pose2d; -import edu.wpi.first.wpilibj.geometry.Rotation2d; -import edu.wpi.first.wpilibj.geometry.Translation2d; -import edu.wpi.first.wpilibj.trajectory.Trajectory; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.ArrayList; diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/util/Color.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/util/Color.java index eec2cdc774..ac031378da 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/util/Color.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/util/Color.java @@ -4,7 +4,7 @@ package edu.wpi.first.wpilibj.util; -import edu.wpi.first.wpiutil.math.MathUtil; +import edu.wpi.first.math.MathUtil; import java.util.Objects; /** diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/util/Color8Bit.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/util/Color8Bit.java index 4e47480b6d..cd7154ddde 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/util/Color8Bit.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/util/Color8Bit.java @@ -4,7 +4,7 @@ package edu.wpi.first.wpilibj.util; -import edu.wpi.first.wpiutil.math.MathUtil; +import edu.wpi.first.math.MathUtil; import java.util.Objects; /** Represents colors with 8 bits of precision. */ diff --git a/wpilibj/src/test/java/edu/wpi/first/wpilibj/util/ColorTest.java b/wpilibj/src/test/java/edu/wpi/first/math/util/ColorTest.java similarity index 95% rename from wpilibj/src/test/java/edu/wpi/first/wpilibj/util/ColorTest.java rename to wpilibj/src/test/java/edu/wpi/first/math/util/ColorTest.java index 64001d231b..668531427d 100644 --- a/wpilibj/src/test/java/edu/wpi/first/wpilibj/util/ColorTest.java +++ b/wpilibj/src/test/java/edu/wpi/first/math/util/ColorTest.java @@ -2,12 +2,13 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.first.wpilibj.util; +package edu.wpi.first.math.util; import static org.junit.jupiter.api.Assertions.assertAll; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.params.provider.Arguments.arguments; +import edu.wpi.first.wpilibj.util.Color; import java.util.stream.Stream; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; diff --git a/wpilibj/src/test/java/edu/wpi/first/wpilibj/util/ErrorMessagesTest.java b/wpilibj/src/test/java/edu/wpi/first/math/util/ErrorMessagesTest.java similarity index 91% rename from wpilibj/src/test/java/edu/wpi/first/wpilibj/util/ErrorMessagesTest.java rename to wpilibj/src/test/java/edu/wpi/first/math/util/ErrorMessagesTest.java index e68fe65eb6..5153b00f3e 100644 --- a/wpilibj/src/test/java/edu/wpi/first/wpilibj/util/ErrorMessagesTest.java +++ b/wpilibj/src/test/java/edu/wpi/first/math/util/ErrorMessagesTest.java @@ -2,13 +2,14 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.first.wpilibj.util; +package edu.wpi.first.math.util; import static edu.wpi.first.wpilibj.util.ErrorMessages.requireNonNullParam; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertThrows; import edu.wpi.first.wpilibj.UtilityClassTest; +import edu.wpi.first.wpilibj.util.ErrorMessages; import org.junit.jupiter.api.Test; class ErrorMessagesTest extends UtilityClassTest { diff --git a/wpilibj/src/test/java/edu/wpi/first/wpilibj/controller/HolonomicDriveControllerTest.java b/wpilibj/src/test/java/edu/wpi/first/wpilibj/controller/HolonomicDriveControllerTest.java index c7610b8329..12cf829f24 100644 --- a/wpilibj/src/test/java/edu/wpi/first/wpilibj/controller/HolonomicDriveControllerTest.java +++ b/wpilibj/src/test/java/edu/wpi/first/wpilibj/controller/HolonomicDriveControllerTest.java @@ -7,15 +7,15 @@ package edu.wpi.first.wpilibj.controller; import static org.junit.jupiter.api.Assertions.assertAll; import static org.junit.jupiter.api.Assertions.assertEquals; -import edu.wpi.first.wpilibj.geometry.Pose2d; -import edu.wpi.first.wpilibj.geometry.Rotation2d; -import edu.wpi.first.wpilibj.geometry.Twist2d; -import edu.wpi.first.wpilibj.kinematics.ChassisSpeeds; -import edu.wpi.first.wpilibj.trajectory.Trajectory; -import edu.wpi.first.wpilibj.trajectory.TrajectoryConfig; -import edu.wpi.first.wpilibj.trajectory.TrajectoryGenerator; -import edu.wpi.first.wpilibj.trajectory.TrapezoidProfile; -import edu.wpi.first.wpiutil.math.MathUtil; +import edu.wpi.first.math.MathUtil; +import edu.wpi.first.math.geometry.Pose2d; +import edu.wpi.first.math.geometry.Rotation2d; +import edu.wpi.first.math.geometry.Twist2d; +import edu.wpi.first.math.kinematics.ChassisSpeeds; +import edu.wpi.first.math.trajectory.Trajectory; +import edu.wpi.first.math.trajectory.TrajectoryConfig; +import edu.wpi.first.math.trajectory.TrajectoryGenerator; +import edu.wpi.first.math.trajectory.TrapezoidProfile; import java.util.ArrayList; import java.util.List; import org.junit.jupiter.api.Test; diff --git a/wpilibj/src/test/java/edu/wpi/first/wpilibj/controller/ProfiledPIDControllerTest.java b/wpilibj/src/test/java/edu/wpi/first/wpilibj/controller/ProfiledPIDControllerTest.java index c0f4fb04e8..c13e8b856e 100644 --- a/wpilibj/src/test/java/edu/wpi/first/wpilibj/controller/ProfiledPIDControllerTest.java +++ b/wpilibj/src/test/java/edu/wpi/first/wpilibj/controller/ProfiledPIDControllerTest.java @@ -6,7 +6,7 @@ package edu.wpi.first.wpilibj.controller; import static org.junit.jupiter.api.Assertions.assertEquals; -import edu.wpi.first.wpilibj.trajectory.TrapezoidProfile; +import edu.wpi.first.math.trajectory.TrapezoidProfile; import org.junit.jupiter.api.Test; class ProfiledPIDControllerTest { diff --git a/wpilibj/src/test/java/edu/wpi/first/wpilibj/controller/ProfiledPIDInputOutputTest.java b/wpilibj/src/test/java/edu/wpi/first/wpilibj/controller/ProfiledPIDInputOutputTest.java index 373bfc8a18..37265d494a 100644 --- a/wpilibj/src/test/java/edu/wpi/first/wpilibj/controller/ProfiledPIDInputOutputTest.java +++ b/wpilibj/src/test/java/edu/wpi/first/wpilibj/controller/ProfiledPIDInputOutputTest.java @@ -7,7 +7,7 @@ package edu.wpi.first.wpilibj.controller; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; -import edu.wpi.first.wpilibj.trajectory.TrapezoidProfile; +import edu.wpi.first.math.trajectory.TrapezoidProfile; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; diff --git a/wpilibj/src/test/java/edu/wpi/first/wpilibj/simulation/AnalogEncoderSimTest.java b/wpilibj/src/test/java/edu/wpi/first/wpilibj/simulation/AnalogEncoderSimTest.java index a5d78871c1..a3736e89af 100644 --- a/wpilibj/src/test/java/edu/wpi/first/wpilibj/simulation/AnalogEncoderSimTest.java +++ b/wpilibj/src/test/java/edu/wpi/first/wpilibj/simulation/AnalogEncoderSimTest.java @@ -6,9 +6,9 @@ package edu.wpi.first.wpilibj.simulation; import static org.junit.jupiter.api.Assertions.assertEquals; +import edu.wpi.first.math.geometry.Rotation2d; import edu.wpi.first.wpilibj.AnalogEncoder; import edu.wpi.first.wpilibj.AnalogInput; -import edu.wpi.first.wpilibj.geometry.Rotation2d; import org.junit.jupiter.api.Test; public class AnalogEncoderSimTest { diff --git a/wpilibj/src/test/java/edu/wpi/first/wpilibj/simulation/DifferentialDrivetrainSimTest.java b/wpilibj/src/test/java/edu/wpi/first/wpilibj/simulation/DifferentialDrivetrainSimTest.java index f9ecdfed69..246236ae0d 100644 --- a/wpilibj/src/test/java/edu/wpi/first/wpilibj/simulation/DifferentialDrivetrainSimTest.java +++ b/wpilibj/src/test/java/edu/wpi/first/wpilibj/simulation/DifferentialDrivetrainSimTest.java @@ -7,24 +7,24 @@ package edu.wpi.first.wpilibj.simulation; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; -import edu.wpi.first.wpilibj.controller.LinearPlantInversionFeedforward; -import edu.wpi.first.wpilibj.controller.RamseteController; -import edu.wpi.first.wpilibj.geometry.Pose2d; -import edu.wpi.first.wpilibj.geometry.Rotation2d; -import edu.wpi.first.wpilibj.kinematics.DifferentialDriveKinematics; -import edu.wpi.first.wpilibj.system.NumericalIntegration; -import edu.wpi.first.wpilibj.system.plant.DCMotor; -import edu.wpi.first.wpilibj.system.plant.LinearSystemId; -import edu.wpi.first.wpilibj.trajectory.TrajectoryConfig; -import edu.wpi.first.wpilibj.trajectory.TrajectoryGenerator; -import edu.wpi.first.wpilibj.trajectory.constraint.DifferentialDriveKinematicsConstraint; -import edu.wpi.first.wpilibj.util.Units; -import edu.wpi.first.wpiutil.math.Matrix; -import edu.wpi.first.wpiutil.math.Nat; -import edu.wpi.first.wpiutil.math.VecBuilder; -import edu.wpi.first.wpiutil.math.Vector; -import edu.wpi.first.wpiutil.math.numbers.N1; -import edu.wpi.first.wpiutil.math.numbers.N7; +import edu.wpi.first.math.Matrix; +import edu.wpi.first.math.Nat; +import edu.wpi.first.math.VecBuilder; +import edu.wpi.first.math.Vector; +import edu.wpi.first.math.controller.LinearPlantInversionFeedforward; +import edu.wpi.first.math.controller.RamseteController; +import edu.wpi.first.math.geometry.Pose2d; +import edu.wpi.first.math.geometry.Rotation2d; +import edu.wpi.first.math.kinematics.DifferentialDriveKinematics; +import edu.wpi.first.math.numbers.N1; +import edu.wpi.first.math.numbers.N7; +import edu.wpi.first.math.system.NumericalIntegration; +import edu.wpi.first.math.system.plant.DCMotor; +import edu.wpi.first.math.system.plant.LinearSystemId; +import edu.wpi.first.math.trajectory.TrajectoryConfig; +import edu.wpi.first.math.trajectory.TrajectoryGenerator; +import edu.wpi.first.math.trajectory.constraint.DifferentialDriveKinematicsConstraint; +import edu.wpi.first.math.util.Units; import java.util.List; import org.junit.jupiter.api.Test; diff --git a/wpilibj/src/test/java/edu/wpi/first/wpilibj/simulation/ElevatorSimTest.java b/wpilibj/src/test/java/edu/wpi/first/wpilibj/simulation/ElevatorSimTest.java index 13f57f71c2..b59b438960 100644 --- a/wpilibj/src/test/java/edu/wpi/first/wpilibj/simulation/ElevatorSimTest.java +++ b/wpilibj/src/test/java/edu/wpi/first/wpilibj/simulation/ElevatorSimTest.java @@ -7,14 +7,14 @@ package edu.wpi.first.wpilibj.simulation; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; +import edu.wpi.first.math.VecBuilder; +import edu.wpi.first.math.system.plant.DCMotor; +import edu.wpi.first.math.system.plant.LinearSystemId; +import edu.wpi.first.math.util.Units; import edu.wpi.first.wpilibj.Encoder; import edu.wpi.first.wpilibj.RobotController; import edu.wpi.first.wpilibj.controller.PIDController; import edu.wpi.first.wpilibj.motorcontrol.PWMVictorSPX; -import edu.wpi.first.wpilibj.system.plant.DCMotor; -import edu.wpi.first.wpilibj.system.plant.LinearSystemId; -import edu.wpi.first.wpilibj.util.Units; -import edu.wpi.first.wpiutil.math.VecBuilder; import org.junit.jupiter.api.Test; public class ElevatorSimTest { diff --git a/wpilibj/src/test/java/edu/wpi/first/wpilibj/simulation/SingleJointedArmSimTest.java b/wpilibj/src/test/java/edu/wpi/first/wpilibj/simulation/SingleJointedArmSimTest.java index a1449c2d2f..26aa6b9730 100644 --- a/wpilibj/src/test/java/edu/wpi/first/wpilibj/simulation/SingleJointedArmSimTest.java +++ b/wpilibj/src/test/java/edu/wpi/first/wpilibj/simulation/SingleJointedArmSimTest.java @@ -6,9 +6,9 @@ package edu.wpi.first.wpilibj.simulation; import static org.junit.jupiter.api.Assertions.assertEquals; -import edu.wpi.first.wpilibj.system.plant.DCMotor; -import edu.wpi.first.wpilibj.util.Units; -import edu.wpi.first.wpiutil.math.VecBuilder; +import edu.wpi.first.math.VecBuilder; +import edu.wpi.first.math.system.plant.DCMotor; +import edu.wpi.first.math.util.Units; import org.junit.jupiter.api.Test; public class SingleJointedArmSimTest { diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/commands/profiledpidcommand/ReplaceMeProfiledPIDCommand.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/commands/profiledpidcommand/ReplaceMeProfiledPIDCommand.java index 8e0ca10eee..b1d194d024 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/commands/profiledpidcommand/ReplaceMeProfiledPIDCommand.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/commands/profiledpidcommand/ReplaceMeProfiledPIDCommand.java @@ -4,8 +4,8 @@ package edu.wpi.first.wpilibj.commands.profiledpidcommand; +import edu.wpi.first.math.trajectory.TrapezoidProfile; import edu.wpi.first.wpilibj.controller.ProfiledPIDController; -import edu.wpi.first.wpilibj.trajectory.TrapezoidProfile; import edu.wpi.first.wpilibj2.command.ProfiledPIDCommand; // NOTE: Consider using this command inline, rather than writing a subclass. For more diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/commands/profiledpidsubsystem/ReplaceMeProfiledPIDSubsystem.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/commands/profiledpidsubsystem/ReplaceMeProfiledPIDSubsystem.java index c46afd964d..38240a9c52 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/commands/profiledpidsubsystem/ReplaceMeProfiledPIDSubsystem.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/commands/profiledpidsubsystem/ReplaceMeProfiledPIDSubsystem.java @@ -4,8 +4,8 @@ package edu.wpi.first.wpilibj.commands.profiledpidsubsystem; +import edu.wpi.first.math.trajectory.TrapezoidProfile; import edu.wpi.first.wpilibj.controller.ProfiledPIDController; -import edu.wpi.first.wpilibj.trajectory.TrapezoidProfile; import edu.wpi.first.wpilibj2.command.ProfiledPIDSubsystem; public class ReplaceMeProfiledPIDSubsystem extends ProfiledPIDSubsystem { diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/commands/trapezoidprofilecommand/ReplaceMeTrapezoidProfileCommand.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/commands/trapezoidprofilecommand/ReplaceMeTrapezoidProfileCommand.java index c31819b928..7a5571983b 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/commands/trapezoidprofilecommand/ReplaceMeTrapezoidProfileCommand.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/commands/trapezoidprofilecommand/ReplaceMeTrapezoidProfileCommand.java @@ -4,7 +4,7 @@ package edu.wpi.first.wpilibj.commands.trapezoidprofilecommand; -import edu.wpi.first.wpilibj.trajectory.TrapezoidProfile; +import edu.wpi.first.math.trajectory.TrapezoidProfile; import edu.wpi.first.wpilibj2.command.TrapezoidProfileCommand; // NOTE: Consider using this command inline, rather than writing a subclass. For more diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/commands/trapezoidprofilesubsystem/ReplaceMeTrapezoidProfileSubsystem.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/commands/trapezoidprofilesubsystem/ReplaceMeTrapezoidProfileSubsystem.java index c0660abf5b..e95f95792f 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/commands/trapezoidprofilesubsystem/ReplaceMeTrapezoidProfileSubsystem.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/commands/trapezoidprofilesubsystem/ReplaceMeTrapezoidProfileSubsystem.java @@ -4,7 +4,7 @@ package edu.wpi.first.wpilibj.commands.trapezoidprofilesubsystem; -import edu.wpi.first.wpilibj.trajectory.TrapezoidProfile; +import edu.wpi.first.math.trajectory.TrapezoidProfile; import edu.wpi.first.wpilibj2.command.TrapezoidProfileSubsystem; public class ReplaceMeTrapezoidProfileSubsystem extends TrapezoidProfileSubsystem { diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/armbot/subsystems/ArmSubsystem.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/armbot/subsystems/ArmSubsystem.java index 23b89a8ce4..12d56c2a79 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/armbot/subsystems/ArmSubsystem.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/armbot/subsystems/ArmSubsystem.java @@ -4,12 +4,12 @@ package edu.wpi.first.wpilibj.examples.armbot.subsystems; +import edu.wpi.first.math.controller.ArmFeedforward; +import edu.wpi.first.math.trajectory.TrapezoidProfile; import edu.wpi.first.wpilibj.Encoder; -import edu.wpi.first.wpilibj.controller.ArmFeedforward; import edu.wpi.first.wpilibj.controller.ProfiledPIDController; import edu.wpi.first.wpilibj.examples.armbot.Constants.ArmConstants; import edu.wpi.first.wpilibj.motorcontrol.PWMSparkMax; -import edu.wpi.first.wpilibj.trajectory.TrapezoidProfile; import edu.wpi.first.wpilibj2.command.ProfiledPIDSubsystem; /** A robot arm subsystem that moves with a motion profile. */ diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/armbotoffboard/subsystems/ArmSubsystem.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/armbotoffboard/subsystems/ArmSubsystem.java index acac10fabc..152700e367 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/armbotoffboard/subsystems/ArmSubsystem.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/armbotoffboard/subsystems/ArmSubsystem.java @@ -4,10 +4,10 @@ package edu.wpi.first.wpilibj.examples.armbotoffboard.subsystems; -import edu.wpi.first.wpilibj.controller.ArmFeedforward; +import edu.wpi.first.math.controller.ArmFeedforward; +import edu.wpi.first.math.trajectory.TrapezoidProfile; import edu.wpi.first.wpilibj.examples.armbotoffboard.Constants.ArmConstants; import edu.wpi.first.wpilibj.examples.armbotoffboard.ExampleSmartMotorController; -import edu.wpi.first.wpilibj.trajectory.TrapezoidProfile; import edu.wpi.first.wpilibj2.command.TrapezoidProfileSubsystem; /** A robot arm subsystem that moves with a motion profile. */ diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/armsimulation/Robot.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/armsimulation/Robot.java index 56ede78abc..ad376e7528 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/armsimulation/Robot.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/armsimulation/Robot.java @@ -4,6 +4,9 @@ package edu.wpi.first.wpilibj.examples.armsimulation; +import edu.wpi.first.math.VecBuilder; +import edu.wpi.first.math.system.plant.DCMotor; +import edu.wpi.first.math.util.Units; import edu.wpi.first.wpilibj.Encoder; import edu.wpi.first.wpilibj.Joystick; import edu.wpi.first.wpilibj.RobotController; @@ -14,9 +17,6 @@ import edu.wpi.first.wpilibj.simulation.BatterySim; import edu.wpi.first.wpilibj.simulation.EncoderSim; import edu.wpi.first.wpilibj.simulation.RoboRioSim; import edu.wpi.first.wpilibj.simulation.SingleJointedArmSim; -import edu.wpi.first.wpilibj.system.plant.DCMotor; -import edu.wpi.first.wpilibj.util.Units; -import edu.wpi.first.wpiutil.math.VecBuilder; /** This is a sample program to demonstrate the use of elevator simulation with existing code. */ public class Robot extends TimedRobot { diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/differentialdrivebot/Drivetrain.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/differentialdrivebot/Drivetrain.java index 7f7e76b949..ee5e804f70 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/differentialdrivebot/Drivetrain.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/differentialdrivebot/Drivetrain.java @@ -4,14 +4,14 @@ package edu.wpi.first.wpilibj.examples.differentialdrivebot; +import edu.wpi.first.math.controller.SimpleMotorFeedforward; +import edu.wpi.first.math.kinematics.ChassisSpeeds; +import edu.wpi.first.math.kinematics.DifferentialDriveKinematics; +import edu.wpi.first.math.kinematics.DifferentialDriveOdometry; +import edu.wpi.first.math.kinematics.DifferentialDriveWheelSpeeds; import edu.wpi.first.wpilibj.AnalogGyro; import edu.wpi.first.wpilibj.Encoder; import edu.wpi.first.wpilibj.controller.PIDController; -import edu.wpi.first.wpilibj.controller.SimpleMotorFeedforward; -import edu.wpi.first.wpilibj.kinematics.ChassisSpeeds; -import edu.wpi.first.wpilibj.kinematics.DifferentialDriveKinematics; -import edu.wpi.first.wpilibj.kinematics.DifferentialDriveOdometry; -import edu.wpi.first.wpilibj.kinematics.DifferentialDriveWheelSpeeds; import edu.wpi.first.wpilibj.motorcontrol.MotorController; import edu.wpi.first.wpilibj.motorcontrol.MotorControllerGroup; import edu.wpi.first.wpilibj.motorcontrol.PWMSparkMax; diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/differentialdriveposeestimator/Drivetrain.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/differentialdriveposeestimator/Drivetrain.java index 74daf11e80..4c6e48259e 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/differentialdriveposeestimator/Drivetrain.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/differentialdriveposeestimator/Drivetrain.java @@ -4,22 +4,22 @@ package edu.wpi.first.wpilibj.examples.differentialdriveposeestimator; +import edu.wpi.first.math.VecBuilder; +import edu.wpi.first.math.controller.SimpleMotorFeedforward; +import edu.wpi.first.math.estimator.DifferentialDrivePoseEstimator; +import edu.wpi.first.math.geometry.Pose2d; +import edu.wpi.first.math.kinematics.ChassisSpeeds; +import edu.wpi.first.math.kinematics.DifferentialDriveKinematics; +import edu.wpi.first.math.kinematics.DifferentialDriveWheelSpeeds; +import edu.wpi.first.math.util.Units; import edu.wpi.first.wpilibj.AnalogGyro; import edu.wpi.first.wpilibj.Encoder; import edu.wpi.first.wpilibj.Timer; import edu.wpi.first.wpilibj.controller.PIDController; -import edu.wpi.first.wpilibj.controller.SimpleMotorFeedforward; -import edu.wpi.first.wpilibj.estimator.DifferentialDrivePoseEstimator; import edu.wpi.first.wpilibj.examples.swervesdriveposeestimator.ExampleGlobalMeasurementSensor; -import edu.wpi.first.wpilibj.geometry.Pose2d; -import edu.wpi.first.wpilibj.kinematics.ChassisSpeeds; -import edu.wpi.first.wpilibj.kinematics.DifferentialDriveKinematics; -import edu.wpi.first.wpilibj.kinematics.DifferentialDriveWheelSpeeds; import edu.wpi.first.wpilibj.motorcontrol.MotorController; import edu.wpi.first.wpilibj.motorcontrol.MotorControllerGroup; import edu.wpi.first.wpilibj.motorcontrol.PWMSparkMax; -import edu.wpi.first.wpilibj.util.Units; -import edu.wpi.first.wpiutil.math.VecBuilder; /** Represents a differential drive style drivetrain. */ public class Drivetrain { diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/drivedistanceoffboard/RobotContainer.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/drivedistanceoffboard/RobotContainer.java index d70fdc16fd..c9705d6d18 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/drivedistanceoffboard/RobotContainer.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/drivedistanceoffboard/RobotContainer.java @@ -6,13 +6,13 @@ package edu.wpi.first.wpilibj.examples.drivedistanceoffboard; import static edu.wpi.first.wpilibj.XboxController.Button; +import edu.wpi.first.math.trajectory.TrapezoidProfile; import edu.wpi.first.wpilibj.GenericHID; import edu.wpi.first.wpilibj.XboxController; import edu.wpi.first.wpilibj.examples.drivedistanceoffboard.Constants.DriveConstants; import edu.wpi.first.wpilibj.examples.drivedistanceoffboard.Constants.OIConstants; import edu.wpi.first.wpilibj.examples.drivedistanceoffboard.commands.DriveDistanceProfiled; import edu.wpi.first.wpilibj.examples.drivedistanceoffboard.subsystems.DriveSubsystem; -import edu.wpi.first.wpilibj.trajectory.TrapezoidProfile; import edu.wpi.first.wpilibj2.command.Command; import edu.wpi.first.wpilibj2.command.InstantCommand; import edu.wpi.first.wpilibj2.command.RunCommand; diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/drivedistanceoffboard/commands/DriveDistanceProfiled.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/drivedistanceoffboard/commands/DriveDistanceProfiled.java index 30dc602605..5bfa10d177 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/drivedistanceoffboard/commands/DriveDistanceProfiled.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/drivedistanceoffboard/commands/DriveDistanceProfiled.java @@ -4,9 +4,9 @@ package edu.wpi.first.wpilibj.examples.drivedistanceoffboard.commands; +import edu.wpi.first.math.trajectory.TrapezoidProfile; import edu.wpi.first.wpilibj.examples.drivedistanceoffboard.Constants.DriveConstants; import edu.wpi.first.wpilibj.examples.drivedistanceoffboard.subsystems.DriveSubsystem; -import edu.wpi.first.wpilibj.trajectory.TrapezoidProfile; import edu.wpi.first.wpilibj2.command.TrapezoidProfileCommand; /** Drives a set distance using a motion profile. */ diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/drivedistanceoffboard/subsystems/DriveSubsystem.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/drivedistanceoffboard/subsystems/DriveSubsystem.java index 82373a834e..9e895acb40 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/drivedistanceoffboard/subsystems/DriveSubsystem.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/drivedistanceoffboard/subsystems/DriveSubsystem.java @@ -4,11 +4,11 @@ package edu.wpi.first.wpilibj.examples.drivedistanceoffboard.subsystems; -import edu.wpi.first.wpilibj.controller.SimpleMotorFeedforward; +import edu.wpi.first.math.controller.SimpleMotorFeedforward; +import edu.wpi.first.math.trajectory.TrapezoidProfile; import edu.wpi.first.wpilibj.drive.DifferentialDrive; import edu.wpi.first.wpilibj.examples.drivedistanceoffboard.Constants.DriveConstants; import edu.wpi.first.wpilibj.examples.drivedistanceoffboard.ExampleSmartMotorController; -import edu.wpi.first.wpilibj.trajectory.TrapezoidProfile; import edu.wpi.first.wpilibj2.command.SubsystemBase; public class DriveSubsystem extends SubsystemBase { diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/elevatorprofiledpid/Robot.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/elevatorprofiledpid/Robot.java index 13d59bad11..44771bc936 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/elevatorprofiledpid/Robot.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/elevatorprofiledpid/Robot.java @@ -4,13 +4,13 @@ package edu.wpi.first.wpilibj.examples.elevatorprofiledpid; +import edu.wpi.first.math.trajectory.TrapezoidProfile; import edu.wpi.first.wpilibj.Encoder; import edu.wpi.first.wpilibj.Joystick; import edu.wpi.first.wpilibj.TimedRobot; import edu.wpi.first.wpilibj.controller.ProfiledPIDController; import edu.wpi.first.wpilibj.motorcontrol.MotorController; import edu.wpi.first.wpilibj.motorcontrol.PWMSparkMax; -import edu.wpi.first.wpilibj.trajectory.TrapezoidProfile; public class Robot extends TimedRobot { private static double kDt = 0.02; diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/elevatorsimulation/Robot.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/elevatorsimulation/Robot.java index ccae2cb4d7..f8166c0720 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/elevatorsimulation/Robot.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/elevatorsimulation/Robot.java @@ -4,6 +4,9 @@ package edu.wpi.first.wpilibj.examples.elevatorsimulation; +import edu.wpi.first.math.VecBuilder; +import edu.wpi.first.math.system.plant.DCMotor; +import edu.wpi.first.math.util.Units; import edu.wpi.first.wpilibj.Encoder; import edu.wpi.first.wpilibj.Joystick; import edu.wpi.first.wpilibj.RobotController; @@ -14,9 +17,6 @@ import edu.wpi.first.wpilibj.simulation.BatterySim; import edu.wpi.first.wpilibj.simulation.ElevatorSim; import edu.wpi.first.wpilibj.simulation.EncoderSim; import edu.wpi.first.wpilibj.simulation.RoboRioSim; -import edu.wpi.first.wpilibj.system.plant.DCMotor; -import edu.wpi.first.wpilibj.util.Units; -import edu.wpi.first.wpiutil.math.VecBuilder; /** This is a sample program to demonstrate the use of elevator simulation with existing code. */ public class Robot extends TimedRobot { diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/elevatortrapezoidprofile/Robot.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/elevatortrapezoidprofile/Robot.java index 5c574d6211..d459eeb899 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/elevatortrapezoidprofile/Robot.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/elevatortrapezoidprofile/Robot.java @@ -4,10 +4,10 @@ package edu.wpi.first.wpilibj.examples.elevatortrapezoidprofile; +import edu.wpi.first.math.controller.SimpleMotorFeedforward; +import edu.wpi.first.math.trajectory.TrapezoidProfile; import edu.wpi.first.wpilibj.Joystick; import edu.wpi.first.wpilibj.TimedRobot; -import edu.wpi.first.wpilibj.controller.SimpleMotorFeedforward; -import edu.wpi.first.wpilibj.trajectory.TrapezoidProfile; public class Robot extends TimedRobot { private static double kDt = 0.02; diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/frisbeebot/subsystems/ShooterSubsystem.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/frisbeebot/subsystems/ShooterSubsystem.java index e6340af507..8f23abcccd 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/frisbeebot/subsystems/ShooterSubsystem.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/frisbeebot/subsystems/ShooterSubsystem.java @@ -4,9 +4,9 @@ package edu.wpi.first.wpilibj.examples.frisbeebot.subsystems; +import edu.wpi.first.math.controller.SimpleMotorFeedforward; import edu.wpi.first.wpilibj.Encoder; import edu.wpi.first.wpilibj.controller.PIDController; -import edu.wpi.first.wpilibj.controller.SimpleMotorFeedforward; import edu.wpi.first.wpilibj.examples.frisbeebot.Constants.ShooterConstants; import edu.wpi.first.wpilibj.motorcontrol.PWMSparkMax; import edu.wpi.first.wpilibj2.command.PIDSubsystem; diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/gyrodrivecommands/commands/TurnToAngleProfiled.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/gyrodrivecommands/commands/TurnToAngleProfiled.java index 14bcfed26a..671298c146 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/gyrodrivecommands/commands/TurnToAngleProfiled.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/gyrodrivecommands/commands/TurnToAngleProfiled.java @@ -4,10 +4,10 @@ package edu.wpi.first.wpilibj.examples.gyrodrivecommands.commands; +import edu.wpi.first.math.trajectory.TrapezoidProfile; import edu.wpi.first.wpilibj.controller.ProfiledPIDController; import edu.wpi.first.wpilibj.examples.gyrodrivecommands.Constants.DriveConstants; import edu.wpi.first.wpilibj.examples.gyrodrivecommands.subsystems.DriveSubsystem; -import edu.wpi.first.wpilibj.trajectory.TrapezoidProfile; import edu.wpi.first.wpilibj2.command.ProfiledPIDCommand; /** A command that will turn the robot to the specified angle using a motion profile. */ diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/mecanumbot/Drivetrain.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/mecanumbot/Drivetrain.java index c50a2b247e..acb4a8d6b1 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/mecanumbot/Drivetrain.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/mecanumbot/Drivetrain.java @@ -4,15 +4,15 @@ package edu.wpi.first.wpilibj.examples.mecanumbot; +import edu.wpi.first.math.controller.SimpleMotorFeedforward; +import edu.wpi.first.math.geometry.Translation2d; +import edu.wpi.first.math.kinematics.ChassisSpeeds; +import edu.wpi.first.math.kinematics.MecanumDriveKinematics; +import edu.wpi.first.math.kinematics.MecanumDriveOdometry; +import edu.wpi.first.math.kinematics.MecanumDriveWheelSpeeds; import edu.wpi.first.wpilibj.AnalogGyro; import edu.wpi.first.wpilibj.Encoder; import edu.wpi.first.wpilibj.controller.PIDController; -import edu.wpi.first.wpilibj.controller.SimpleMotorFeedforward; -import edu.wpi.first.wpilibj.geometry.Translation2d; -import edu.wpi.first.wpilibj.kinematics.ChassisSpeeds; -import edu.wpi.first.wpilibj.kinematics.MecanumDriveKinematics; -import edu.wpi.first.wpilibj.kinematics.MecanumDriveOdometry; -import edu.wpi.first.wpilibj.kinematics.MecanumDriveWheelSpeeds; import edu.wpi.first.wpilibj.motorcontrol.MotorController; import edu.wpi.first.wpilibj.motorcontrol.PWMSparkMax; diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/mecanumcontrollercommand/Constants.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/mecanumcontrollercommand/Constants.java index 5d3070fa45..895983d272 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/mecanumcontrollercommand/Constants.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/mecanumcontrollercommand/Constants.java @@ -4,10 +4,10 @@ package edu.wpi.first.wpilibj.examples.mecanumcontrollercommand; -import edu.wpi.first.wpilibj.controller.SimpleMotorFeedforward; -import edu.wpi.first.wpilibj.geometry.Translation2d; -import edu.wpi.first.wpilibj.kinematics.MecanumDriveKinematics; -import edu.wpi.first.wpilibj.trajectory.TrapezoidProfile; +import edu.wpi.first.math.controller.SimpleMotorFeedforward; +import edu.wpi.first.math.geometry.Translation2d; +import edu.wpi.first.math.kinematics.MecanumDriveKinematics; +import edu.wpi.first.math.trajectory.TrapezoidProfile; /** * The Constants class provides a convenient place for teams to hold robot-wide numerical or boolean diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/mecanumcontrollercommand/RobotContainer.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/mecanumcontrollercommand/RobotContainer.java index c209346a74..a2df728252 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/mecanumcontrollercommand/RobotContainer.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/mecanumcontrollercommand/RobotContainer.java @@ -4,6 +4,12 @@ package edu.wpi.first.wpilibj.examples.mecanumcontrollercommand; +import edu.wpi.first.math.geometry.Pose2d; +import edu.wpi.first.math.geometry.Rotation2d; +import edu.wpi.first.math.geometry.Translation2d; +import edu.wpi.first.math.trajectory.Trajectory; +import edu.wpi.first.math.trajectory.TrajectoryConfig; +import edu.wpi.first.math.trajectory.TrajectoryGenerator; import edu.wpi.first.wpilibj.GenericHID; import edu.wpi.first.wpilibj.XboxController; import edu.wpi.first.wpilibj.XboxController.Button; @@ -13,12 +19,6 @@ import edu.wpi.first.wpilibj.examples.mecanumcontrollercommand.Constants.AutoCon import edu.wpi.first.wpilibj.examples.mecanumcontrollercommand.Constants.DriveConstants; import edu.wpi.first.wpilibj.examples.mecanumcontrollercommand.Constants.OIConstants; import edu.wpi.first.wpilibj.examples.mecanumcontrollercommand.subsystems.DriveSubsystem; -import edu.wpi.first.wpilibj.geometry.Pose2d; -import edu.wpi.first.wpilibj.geometry.Rotation2d; -import edu.wpi.first.wpilibj.geometry.Translation2d; -import edu.wpi.first.wpilibj.trajectory.Trajectory; -import edu.wpi.first.wpilibj.trajectory.TrajectoryConfig; -import edu.wpi.first.wpilibj.trajectory.TrajectoryGenerator; import edu.wpi.first.wpilibj2.command.Command; import edu.wpi.first.wpilibj2.command.MecanumControllerCommand; import edu.wpi.first.wpilibj2.command.RunCommand; diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/mecanumcontrollercommand/subsystems/DriveSubsystem.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/mecanumcontrollercommand/subsystems/DriveSubsystem.java index bb4913476c..23da86770e 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/mecanumcontrollercommand/subsystems/DriveSubsystem.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/mecanumcontrollercommand/subsystems/DriveSubsystem.java @@ -4,15 +4,15 @@ package edu.wpi.first.wpilibj.examples.mecanumcontrollercommand.subsystems; +import edu.wpi.first.math.geometry.Pose2d; +import edu.wpi.first.math.kinematics.MecanumDriveMotorVoltages; +import edu.wpi.first.math.kinematics.MecanumDriveOdometry; +import edu.wpi.first.math.kinematics.MecanumDriveWheelSpeeds; import edu.wpi.first.wpilibj.ADXRS450_Gyro; import edu.wpi.first.wpilibj.Encoder; import edu.wpi.first.wpilibj.drive.MecanumDrive; import edu.wpi.first.wpilibj.examples.mecanumcontrollercommand.Constants.DriveConstants; -import edu.wpi.first.wpilibj.geometry.Pose2d; import edu.wpi.first.wpilibj.interfaces.Gyro; -import edu.wpi.first.wpilibj.kinematics.MecanumDriveMotorVoltages; -import edu.wpi.first.wpilibj.kinematics.MecanumDriveOdometry; -import edu.wpi.first.wpilibj.kinematics.MecanumDriveWheelSpeeds; import edu.wpi.first.wpilibj.motorcontrol.PWMSparkMax; import edu.wpi.first.wpilibj2.command.SubsystemBase; diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/mecanumdriveposeestimator/Drivetrain.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/mecanumdriveposeestimator/Drivetrain.java index 3fba18a6bc..547dc70dda 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/mecanumdriveposeestimator/Drivetrain.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/mecanumdriveposeestimator/Drivetrain.java @@ -4,22 +4,22 @@ package edu.wpi.first.wpilibj.examples.mecanumdriveposeestimator; +import edu.wpi.first.math.VecBuilder; +import edu.wpi.first.math.controller.SimpleMotorFeedforward; +import edu.wpi.first.math.estimator.MecanumDrivePoseEstimator; +import edu.wpi.first.math.geometry.Pose2d; +import edu.wpi.first.math.geometry.Translation2d; +import edu.wpi.first.math.kinematics.ChassisSpeeds; +import edu.wpi.first.math.kinematics.MecanumDriveKinematics; +import edu.wpi.first.math.kinematics.MecanumDriveWheelSpeeds; +import edu.wpi.first.math.util.Units; import edu.wpi.first.wpilibj.AnalogGyro; import edu.wpi.first.wpilibj.Encoder; import edu.wpi.first.wpilibj.Timer; import edu.wpi.first.wpilibj.controller.PIDController; -import edu.wpi.first.wpilibj.controller.SimpleMotorFeedforward; -import edu.wpi.first.wpilibj.estimator.MecanumDrivePoseEstimator; import edu.wpi.first.wpilibj.examples.swervesdriveposeestimator.ExampleGlobalMeasurementSensor; -import edu.wpi.first.wpilibj.geometry.Pose2d; -import edu.wpi.first.wpilibj.geometry.Translation2d; -import edu.wpi.first.wpilibj.kinematics.ChassisSpeeds; -import edu.wpi.first.wpilibj.kinematics.MecanumDriveKinematics; -import edu.wpi.first.wpilibj.kinematics.MecanumDriveWheelSpeeds; import edu.wpi.first.wpilibj.motorcontrol.MotorController; import edu.wpi.first.wpilibj.motorcontrol.PWMSparkMax; -import edu.wpi.first.wpilibj.util.Units; -import edu.wpi.first.wpiutil.math.VecBuilder; /** Represents a mecanum drive style drivetrain. */ @SuppressWarnings("PMD.TooManyFields") diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/mecanumdriveposeestimator/ExampleGlobalMeasurementSensor.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/mecanumdriveposeestimator/ExampleGlobalMeasurementSensor.java index c38cf1799d..4059768b70 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/mecanumdriveposeestimator/ExampleGlobalMeasurementSensor.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/mecanumdriveposeestimator/ExampleGlobalMeasurementSensor.java @@ -4,11 +4,11 @@ package edu.wpi.first.wpilibj.examples.mecanumdriveposeestimator; -import edu.wpi.first.wpilibj.geometry.Pose2d; -import edu.wpi.first.wpilibj.geometry.Rotation2d; -import edu.wpi.first.wpilibj.math.StateSpaceUtil; -import edu.wpi.first.wpilibj.util.Units; -import edu.wpi.first.wpiutil.math.VecBuilder; +import edu.wpi.first.math.VecBuilder; +import edu.wpi.first.math.geometry.Pose2d; +import edu.wpi.first.math.geometry.Rotation2d; +import edu.wpi.first.math.math.StateSpaceUtil; +import edu.wpi.first.math.util.Units; /** This dummy class represents a global measurement sensor, such as a computer vision solution. */ public final class ExampleGlobalMeasurementSensor { diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/ramsetecommand/Constants.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/ramsetecommand/Constants.java index e3f2a43fa9..8b73220b7b 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/ramsetecommand/Constants.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/ramsetecommand/Constants.java @@ -4,7 +4,7 @@ package edu.wpi.first.wpilibj.examples.ramsetecommand; -import edu.wpi.first.wpilibj.kinematics.DifferentialDriveKinematics; +import edu.wpi.first.math.kinematics.DifferentialDriveKinematics; /** * The Constants class provides a convenient place for teams to hold robot-wide numerical or boolean diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/ramsetecommand/RobotContainer.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/ramsetecommand/RobotContainer.java index 12d361e84a..a51672b26e 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/ramsetecommand/RobotContainer.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/ramsetecommand/RobotContainer.java @@ -6,22 +6,22 @@ package edu.wpi.first.wpilibj.examples.ramsetecommand; import static edu.wpi.first.wpilibj.XboxController.Button; +import edu.wpi.first.math.controller.RamseteController; +import edu.wpi.first.math.controller.SimpleMotorFeedforward; +import edu.wpi.first.math.geometry.Pose2d; +import edu.wpi.first.math.geometry.Rotation2d; +import edu.wpi.first.math.geometry.Translation2d; +import edu.wpi.first.math.trajectory.Trajectory; +import edu.wpi.first.math.trajectory.TrajectoryConfig; +import edu.wpi.first.math.trajectory.TrajectoryGenerator; +import edu.wpi.first.math.trajectory.constraint.DifferentialDriveVoltageConstraint; import edu.wpi.first.wpilibj.GenericHID; import edu.wpi.first.wpilibj.XboxController; import edu.wpi.first.wpilibj.controller.PIDController; -import edu.wpi.first.wpilibj.controller.RamseteController; -import edu.wpi.first.wpilibj.controller.SimpleMotorFeedforward; import edu.wpi.first.wpilibj.examples.ramsetecommand.Constants.AutoConstants; import edu.wpi.first.wpilibj.examples.ramsetecommand.Constants.DriveConstants; import edu.wpi.first.wpilibj.examples.ramsetecommand.Constants.OIConstants; import edu.wpi.first.wpilibj.examples.ramsetecommand.subsystems.DriveSubsystem; -import edu.wpi.first.wpilibj.geometry.Pose2d; -import edu.wpi.first.wpilibj.geometry.Rotation2d; -import edu.wpi.first.wpilibj.geometry.Translation2d; -import edu.wpi.first.wpilibj.trajectory.Trajectory; -import edu.wpi.first.wpilibj.trajectory.TrajectoryConfig; -import edu.wpi.first.wpilibj.trajectory.TrajectoryGenerator; -import edu.wpi.first.wpilibj.trajectory.constraint.DifferentialDriveVoltageConstraint; import edu.wpi.first.wpilibj2.command.Command; import edu.wpi.first.wpilibj2.command.RamseteCommand; import edu.wpi.first.wpilibj2.command.RunCommand; diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/ramsetecommand/subsystems/DriveSubsystem.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/ramsetecommand/subsystems/DriveSubsystem.java index 275b49f215..f64f3b7bc0 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/ramsetecommand/subsystems/DriveSubsystem.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/ramsetecommand/subsystems/DriveSubsystem.java @@ -4,14 +4,14 @@ package edu.wpi.first.wpilibj.examples.ramsetecommand.subsystems; +import edu.wpi.first.math.geometry.Pose2d; +import edu.wpi.first.math.kinematics.DifferentialDriveOdometry; +import edu.wpi.first.math.kinematics.DifferentialDriveWheelSpeeds; import edu.wpi.first.wpilibj.ADXRS450_Gyro; import edu.wpi.first.wpilibj.Encoder; import edu.wpi.first.wpilibj.drive.DifferentialDrive; import edu.wpi.first.wpilibj.examples.ramsetecommand.Constants.DriveConstants; -import edu.wpi.first.wpilibj.geometry.Pose2d; import edu.wpi.first.wpilibj.interfaces.Gyro; -import edu.wpi.first.wpilibj.kinematics.DifferentialDriveOdometry; -import edu.wpi.first.wpilibj.kinematics.DifferentialDriveWheelSpeeds; import edu.wpi.first.wpilibj.motorcontrol.MotorControllerGroup; import edu.wpi.first.wpilibj.motorcontrol.PWMSparkMax; import edu.wpi.first.wpilibj2.command.SubsystemBase; diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/ramsetecontroller/Drivetrain.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/ramsetecontroller/Drivetrain.java index d1ff5dd849..76cf574f6f 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/ramsetecontroller/Drivetrain.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/ramsetecontroller/Drivetrain.java @@ -4,15 +4,15 @@ package edu.wpi.first.wpilibj.examples.ramsetecontroller; +import edu.wpi.first.math.controller.SimpleMotorFeedforward; +import edu.wpi.first.math.geometry.Pose2d; +import edu.wpi.first.math.kinematics.ChassisSpeeds; +import edu.wpi.first.math.kinematics.DifferentialDriveKinematics; +import edu.wpi.first.math.kinematics.DifferentialDriveOdometry; +import edu.wpi.first.math.kinematics.DifferentialDriveWheelSpeeds; import edu.wpi.first.wpilibj.AnalogGyro; import edu.wpi.first.wpilibj.Encoder; import edu.wpi.first.wpilibj.controller.PIDController; -import edu.wpi.first.wpilibj.controller.SimpleMotorFeedforward; -import edu.wpi.first.wpilibj.geometry.Pose2d; -import edu.wpi.first.wpilibj.kinematics.ChassisSpeeds; -import edu.wpi.first.wpilibj.kinematics.DifferentialDriveKinematics; -import edu.wpi.first.wpilibj.kinematics.DifferentialDriveOdometry; -import edu.wpi.first.wpilibj.kinematics.DifferentialDriveWheelSpeeds; import edu.wpi.first.wpilibj.motorcontrol.MotorController; import edu.wpi.first.wpilibj.motorcontrol.MotorControllerGroup; import edu.wpi.first.wpilibj.motorcontrol.PWMSparkMax; diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/ramsetecontroller/Robot.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/ramsetecontroller/Robot.java index 0697473e26..14cb73ba1a 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/ramsetecontroller/Robot.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/ramsetecontroller/Robot.java @@ -4,19 +4,19 @@ package edu.wpi.first.wpilibj.examples.ramsetecontroller; +import edu.wpi.first.math.controller.RamseteController; +import edu.wpi.first.math.geometry.Pose2d; +import edu.wpi.first.math.geometry.Rotation2d; +import edu.wpi.first.math.geometry.Translation2d; +import edu.wpi.first.math.trajectory.Trajectory; +import edu.wpi.first.math.trajectory.TrajectoryConfig; +import edu.wpi.first.math.trajectory.TrajectoryGenerator; +import edu.wpi.first.math.util.Units; import edu.wpi.first.wpilibj.GenericHID; import edu.wpi.first.wpilibj.SlewRateLimiter; import edu.wpi.first.wpilibj.TimedRobot; import edu.wpi.first.wpilibj.Timer; import edu.wpi.first.wpilibj.XboxController; -import edu.wpi.first.wpilibj.controller.RamseteController; -import edu.wpi.first.wpilibj.geometry.Pose2d; -import edu.wpi.first.wpilibj.geometry.Rotation2d; -import edu.wpi.first.wpilibj.geometry.Translation2d; -import edu.wpi.first.wpilibj.trajectory.Trajectory; -import edu.wpi.first.wpilibj.trajectory.TrajectoryConfig; -import edu.wpi.first.wpilibj.trajectory.TrajectoryGenerator; -import edu.wpi.first.wpilibj.util.Units; import java.util.List; public class Robot extends TimedRobot { diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/simpledifferentialdrivesimulation/Drivetrain.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/simpledifferentialdrivesimulation/Drivetrain.java index 2cb16c13fa..221966ab59 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/simpledifferentialdrivesimulation/Drivetrain.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/simpledifferentialdrivesimulation/Drivetrain.java @@ -4,16 +4,20 @@ package edu.wpi.first.wpilibj.examples.simpledifferentialdrivesimulation; +import edu.wpi.first.math.controller.SimpleMotorFeedforward; +import edu.wpi.first.math.geometry.Pose2d; +import edu.wpi.first.math.kinematics.ChassisSpeeds; +import edu.wpi.first.math.kinematics.DifferentialDriveKinematics; +import edu.wpi.first.math.kinematics.DifferentialDriveOdometry; +import edu.wpi.first.math.kinematics.DifferentialDriveWheelSpeeds; +import edu.wpi.first.math.numbers.N2; +import edu.wpi.first.math.system.LinearSystem; +import edu.wpi.first.math.system.plant.DCMotor; +import edu.wpi.first.math.system.plant.LinearSystemId; import edu.wpi.first.wpilibj.AnalogGyro; import edu.wpi.first.wpilibj.Encoder; import edu.wpi.first.wpilibj.RobotController; import edu.wpi.first.wpilibj.controller.PIDController; -import edu.wpi.first.wpilibj.controller.SimpleMotorFeedforward; -import edu.wpi.first.wpilibj.geometry.Pose2d; -import edu.wpi.first.wpilibj.kinematics.ChassisSpeeds; -import edu.wpi.first.wpilibj.kinematics.DifferentialDriveKinematics; -import edu.wpi.first.wpilibj.kinematics.DifferentialDriveOdometry; -import edu.wpi.first.wpilibj.kinematics.DifferentialDriveWheelSpeeds; import edu.wpi.first.wpilibj.motorcontrol.MotorControllerGroup; import edu.wpi.first.wpilibj.motorcontrol.PWMSparkMax; import edu.wpi.first.wpilibj.simulation.AnalogGyroSim; @@ -21,10 +25,6 @@ import edu.wpi.first.wpilibj.simulation.DifferentialDrivetrainSim; import edu.wpi.first.wpilibj.simulation.EncoderSim; import edu.wpi.first.wpilibj.smartdashboard.Field2d; import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard; -import edu.wpi.first.wpilibj.system.LinearSystem; -import edu.wpi.first.wpilibj.system.plant.DCMotor; -import edu.wpi.first.wpilibj.system.plant.LinearSystemId; -import edu.wpi.first.wpiutil.math.numbers.N2; @SuppressWarnings("PMD.TooManyFields") public class Drivetrain { diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/simpledifferentialdrivesimulation/Robot.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/simpledifferentialdrivesimulation/Robot.java index 130721f6d8..f256cc8aff 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/simpledifferentialdrivesimulation/Robot.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/simpledifferentialdrivesimulation/Robot.java @@ -4,18 +4,18 @@ package edu.wpi.first.wpilibj.examples.simpledifferentialdrivesimulation; +import edu.wpi.first.math.controller.RamseteController; +import edu.wpi.first.math.geometry.Pose2d; +import edu.wpi.first.math.geometry.Rotation2d; +import edu.wpi.first.math.kinematics.ChassisSpeeds; +import edu.wpi.first.math.trajectory.Trajectory; +import edu.wpi.first.math.trajectory.TrajectoryConfig; +import edu.wpi.first.math.trajectory.TrajectoryGenerator; import edu.wpi.first.wpilibj.GenericHID; import edu.wpi.first.wpilibj.SlewRateLimiter; import edu.wpi.first.wpilibj.TimedRobot; import edu.wpi.first.wpilibj.Timer; import edu.wpi.first.wpilibj.XboxController; -import edu.wpi.first.wpilibj.controller.RamseteController; -import edu.wpi.first.wpilibj.geometry.Pose2d; -import edu.wpi.first.wpilibj.geometry.Rotation2d; -import edu.wpi.first.wpilibj.kinematics.ChassisSpeeds; -import edu.wpi.first.wpilibj.trajectory.Trajectory; -import edu.wpi.first.wpilibj.trajectory.TrajectoryConfig; -import edu.wpi.first.wpilibj.trajectory.TrajectoryGenerator; import java.util.List; public class Robot extends TimedRobot { diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/statespacearm/Robot.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/statespacearm/Robot.java index c7f8d24ece..9877906c05 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/statespacearm/Robot.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/statespacearm/Robot.java @@ -4,23 +4,23 @@ package edu.wpi.first.wpilibj.examples.statespacearm; +import edu.wpi.first.math.Nat; +import edu.wpi.first.math.VecBuilder; +import edu.wpi.first.math.controller.LinearQuadraticRegulator; +import edu.wpi.first.math.estimator.KalmanFilter; +import edu.wpi.first.math.numbers.N1; +import edu.wpi.first.math.numbers.N2; +import edu.wpi.first.math.system.LinearSystem; +import edu.wpi.first.math.system.LinearSystemLoop; +import edu.wpi.first.math.system.plant.DCMotor; +import edu.wpi.first.math.system.plant.LinearSystemId; +import edu.wpi.first.math.trajectory.TrapezoidProfile; +import edu.wpi.first.math.util.Units; import edu.wpi.first.wpilibj.Encoder; import edu.wpi.first.wpilibj.Joystick; import edu.wpi.first.wpilibj.TimedRobot; -import edu.wpi.first.wpilibj.controller.LinearQuadraticRegulator; -import edu.wpi.first.wpilibj.estimator.KalmanFilter; import edu.wpi.first.wpilibj.motorcontrol.MotorController; import edu.wpi.first.wpilibj.motorcontrol.PWMSparkMax; -import edu.wpi.first.wpilibj.system.LinearSystem; -import edu.wpi.first.wpilibj.system.LinearSystemLoop; -import edu.wpi.first.wpilibj.system.plant.DCMotor; -import edu.wpi.first.wpilibj.system.plant.LinearSystemId; -import edu.wpi.first.wpilibj.trajectory.TrapezoidProfile; -import edu.wpi.first.wpilibj.util.Units; -import edu.wpi.first.wpiutil.math.Nat; -import edu.wpi.first.wpiutil.math.VecBuilder; -import edu.wpi.first.wpiutil.math.numbers.N1; -import edu.wpi.first.wpiutil.math.numbers.N2; /** * This is a sample program to demonstrate how to use a state-space controller to control an arm. diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/statespacedifferentialdrivesimulation/Constants.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/statespacedifferentialdrivesimulation/Constants.java index 2c8036a122..2177bd7145 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/statespacedifferentialdrivesimulation/Constants.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/statespacedifferentialdrivesimulation/Constants.java @@ -4,11 +4,11 @@ package edu.wpi.first.wpilibj.examples.statespacedifferentialdrivesimulation; -import edu.wpi.first.wpilibj.kinematics.DifferentialDriveKinematics; -import edu.wpi.first.wpilibj.system.LinearSystem; -import edu.wpi.first.wpilibj.system.plant.DCMotor; -import edu.wpi.first.wpilibj.system.plant.LinearSystemId; -import edu.wpi.first.wpiutil.math.numbers.N2; +import edu.wpi.first.math.kinematics.DifferentialDriveKinematics; +import edu.wpi.first.math.numbers.N2; +import edu.wpi.first.math.system.LinearSystem; +import edu.wpi.first.math.system.plant.DCMotor; +import edu.wpi.first.math.system.plant.LinearSystemId; /** * The Constants class provides a convenient place for teams to hold robot-wide numerical or boolean diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/statespacedifferentialdrivesimulation/RobotContainer.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/statespacedifferentialdrivesimulation/RobotContainer.java index 4db41a60fc..b2999708c0 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/statespacedifferentialdrivesimulation/RobotContainer.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/statespacedifferentialdrivesimulation/RobotContainer.java @@ -4,22 +4,20 @@ package edu.wpi.first.wpilibj.examples.statespacedifferentialdrivesimulation; -import static edu.wpi.first.wpilibj.XboxController.Button; - +import edu.wpi.first.math.controller.RamseteController; +import edu.wpi.first.math.controller.SimpleMotorFeedforward; +import edu.wpi.first.math.geometry.Pose2d; +import edu.wpi.first.math.geometry.Rotation2d; +import edu.wpi.first.math.geometry.Translation2d; +import edu.wpi.first.math.trajectory.Trajectory; +import edu.wpi.first.math.trajectory.TrajectoryConfig; +import edu.wpi.first.math.trajectory.TrajectoryGenerator; +import edu.wpi.first.math.trajectory.constraint.DifferentialDriveVoltageConstraint; import edu.wpi.first.wpilibj.GenericHID; import edu.wpi.first.wpilibj.XboxController; import edu.wpi.first.wpilibj.XboxController.Button; import edu.wpi.first.wpilibj.controller.PIDController; -import edu.wpi.first.wpilibj.controller.RamseteController; -import edu.wpi.first.wpilibj.controller.SimpleMotorFeedforward; import edu.wpi.first.wpilibj.examples.statespacedifferentialdrivesimulation.subsystems.DriveSubsystem; -import edu.wpi.first.wpilibj.geometry.Pose2d; -import edu.wpi.first.wpilibj.geometry.Rotation2d; -import edu.wpi.first.wpilibj.geometry.Translation2d; -import edu.wpi.first.wpilibj.trajectory.Trajectory; -import edu.wpi.first.wpilibj.trajectory.TrajectoryConfig; -import edu.wpi.first.wpilibj.trajectory.TrajectoryGenerator; -import edu.wpi.first.wpilibj.trajectory.constraint.DifferentialDriveVoltageConstraint; import edu.wpi.first.wpilibj2.command.Command; import edu.wpi.first.wpilibj2.command.RamseteCommand; import edu.wpi.first.wpilibj2.command.RunCommand; diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/statespacedifferentialdrivesimulation/subsystems/DriveSubsystem.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/statespacedifferentialdrivesimulation/subsystems/DriveSubsystem.java index 32394ea1d3..fe2fd39c79 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/statespacedifferentialdrivesimulation/subsystems/DriveSubsystem.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/statespacedifferentialdrivesimulation/subsystems/DriveSubsystem.java @@ -4,16 +4,17 @@ package edu.wpi.first.wpilibj.examples.statespacedifferentialdrivesimulation.subsystems; +import edu.wpi.first.math.VecBuilder; +import edu.wpi.first.math.geometry.Pose2d; +import edu.wpi.first.math.geometry.Rotation2d; +import edu.wpi.first.math.kinematics.DifferentialDriveOdometry; +import edu.wpi.first.math.kinematics.DifferentialDriveWheelSpeeds; import edu.wpi.first.wpilibj.ADXRS450_Gyro; import edu.wpi.first.wpilibj.Encoder; import edu.wpi.first.wpilibj.RobotBase; import edu.wpi.first.wpilibj.RobotController; import edu.wpi.first.wpilibj.drive.DifferentialDrive; import edu.wpi.first.wpilibj.examples.statespacedifferentialdrivesimulation.Constants.DriveConstants; -import edu.wpi.first.wpilibj.geometry.Pose2d; -import edu.wpi.first.wpilibj.geometry.Rotation2d; -import edu.wpi.first.wpilibj.kinematics.DifferentialDriveOdometry; -import edu.wpi.first.wpilibj.kinematics.DifferentialDriveWheelSpeeds; import edu.wpi.first.wpilibj.motorcontrol.MotorControllerGroup; import edu.wpi.first.wpilibj.motorcontrol.PWMSparkMax; import edu.wpi.first.wpilibj.simulation.ADXRS450_GyroSim; @@ -22,7 +23,6 @@ import edu.wpi.first.wpilibj.simulation.EncoderSim; import edu.wpi.first.wpilibj.smartdashboard.Field2d; import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard; import edu.wpi.first.wpilibj2.command.SubsystemBase; -import edu.wpi.first.wpiutil.math.VecBuilder; public class DriveSubsystem extends SubsystemBase { // The motors on the left side of the drive. diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/statespaceelevator/Robot.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/statespaceelevator/Robot.java index 64c3bd1df7..f1aca19c81 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/statespaceelevator/Robot.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/statespaceelevator/Robot.java @@ -4,23 +4,23 @@ package edu.wpi.first.wpilibj.examples.statespaceelevator; +import edu.wpi.first.math.Nat; +import edu.wpi.first.math.VecBuilder; +import edu.wpi.first.math.controller.LinearQuadraticRegulator; +import edu.wpi.first.math.estimator.KalmanFilter; +import edu.wpi.first.math.numbers.N1; +import edu.wpi.first.math.numbers.N2; +import edu.wpi.first.math.system.LinearSystem; +import edu.wpi.first.math.system.LinearSystemLoop; +import edu.wpi.first.math.system.plant.DCMotor; +import edu.wpi.first.math.system.plant.LinearSystemId; +import edu.wpi.first.math.trajectory.TrapezoidProfile; +import edu.wpi.first.math.util.Units; import edu.wpi.first.wpilibj.Encoder; import edu.wpi.first.wpilibj.Joystick; import edu.wpi.first.wpilibj.TimedRobot; -import edu.wpi.first.wpilibj.controller.LinearQuadraticRegulator; -import edu.wpi.first.wpilibj.estimator.KalmanFilter; import edu.wpi.first.wpilibj.motorcontrol.MotorController; import edu.wpi.first.wpilibj.motorcontrol.PWMSparkMax; -import edu.wpi.first.wpilibj.system.LinearSystem; -import edu.wpi.first.wpilibj.system.LinearSystemLoop; -import edu.wpi.first.wpilibj.system.plant.DCMotor; -import edu.wpi.first.wpilibj.system.plant.LinearSystemId; -import edu.wpi.first.wpilibj.trajectory.TrapezoidProfile; -import edu.wpi.first.wpilibj.util.Units; -import edu.wpi.first.wpiutil.math.Nat; -import edu.wpi.first.wpiutil.math.VecBuilder; -import edu.wpi.first.wpiutil.math.numbers.N1; -import edu.wpi.first.wpiutil.math.numbers.N2; /** * This is a sample program to demonstrate how to use a state-space controller to control an diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/statespaceflywheel/Robot.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/statespaceflywheel/Robot.java index cb6d10a076..06e8d29bb6 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/statespaceflywheel/Robot.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/statespaceflywheel/Robot.java @@ -4,21 +4,21 @@ package edu.wpi.first.wpilibj.examples.statespaceflywheel; +import edu.wpi.first.math.Nat; +import edu.wpi.first.math.VecBuilder; +import edu.wpi.first.math.controller.LinearQuadraticRegulator; +import edu.wpi.first.math.estimator.KalmanFilter; +import edu.wpi.first.math.numbers.N1; +import edu.wpi.first.math.system.LinearSystem; +import edu.wpi.first.math.system.LinearSystemLoop; +import edu.wpi.first.math.system.plant.DCMotor; +import edu.wpi.first.math.system.plant.LinearSystemId; +import edu.wpi.first.math.util.Units; import edu.wpi.first.wpilibj.Encoder; import edu.wpi.first.wpilibj.Joystick; import edu.wpi.first.wpilibj.TimedRobot; -import edu.wpi.first.wpilibj.controller.LinearQuadraticRegulator; -import edu.wpi.first.wpilibj.estimator.KalmanFilter; import edu.wpi.first.wpilibj.motorcontrol.MotorController; import edu.wpi.first.wpilibj.motorcontrol.PWMSparkMax; -import edu.wpi.first.wpilibj.system.LinearSystem; -import edu.wpi.first.wpilibj.system.LinearSystemLoop; -import edu.wpi.first.wpilibj.system.plant.DCMotor; -import edu.wpi.first.wpilibj.system.plant.LinearSystemId; -import edu.wpi.first.wpilibj.util.Units; -import edu.wpi.first.wpiutil.math.Nat; -import edu.wpi.first.wpiutil.math.VecBuilder; -import edu.wpi.first.wpiutil.math.numbers.N1; /** * This is a sample program to demonstrate how to use a state-space controller to control a diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/statespaceflywheelsysid/Robot.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/statespaceflywheelsysid/Robot.java index 7bb4491f12..538f3f7484 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/statespaceflywheelsysid/Robot.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/statespaceflywheelsysid/Robot.java @@ -4,20 +4,20 @@ package edu.wpi.first.wpilibj.examples.statespaceflywheelsysid; +import edu.wpi.first.math.Nat; +import edu.wpi.first.math.VecBuilder; +import edu.wpi.first.math.controller.LinearQuadraticRegulator; +import edu.wpi.first.math.estimator.KalmanFilter; +import edu.wpi.first.math.numbers.N1; +import edu.wpi.first.math.system.LinearSystem; +import edu.wpi.first.math.system.LinearSystemLoop; +import edu.wpi.first.math.system.plant.LinearSystemId; +import edu.wpi.first.math.util.Units; import edu.wpi.first.wpilibj.Encoder; import edu.wpi.first.wpilibj.Joystick; import edu.wpi.first.wpilibj.TimedRobot; -import edu.wpi.first.wpilibj.controller.LinearQuadraticRegulator; -import edu.wpi.first.wpilibj.estimator.KalmanFilter; import edu.wpi.first.wpilibj.motorcontrol.MotorController; import edu.wpi.first.wpilibj.motorcontrol.PWMSparkMax; -import edu.wpi.first.wpilibj.system.LinearSystem; -import edu.wpi.first.wpilibj.system.LinearSystemLoop; -import edu.wpi.first.wpilibj.system.plant.LinearSystemId; -import edu.wpi.first.wpilibj.util.Units; -import edu.wpi.first.wpiutil.math.Nat; -import edu.wpi.first.wpiutil.math.VecBuilder; -import edu.wpi.first.wpiutil.math.numbers.N1; /** * This is a sample program to demonstrate how to use a state-space controller to control a diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/swervebot/Drivetrain.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/swervebot/Drivetrain.java index 10ab169224..acfff2112e 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/swervebot/Drivetrain.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/swervebot/Drivetrain.java @@ -4,11 +4,11 @@ package edu.wpi.first.wpilibj.examples.swervebot; +import edu.wpi.first.math.geometry.Translation2d; +import edu.wpi.first.math.kinematics.ChassisSpeeds; +import edu.wpi.first.math.kinematics.SwerveDriveKinematics; +import edu.wpi.first.math.kinematics.SwerveDriveOdometry; import edu.wpi.first.wpilibj.AnalogGyro; -import edu.wpi.first.wpilibj.geometry.Translation2d; -import edu.wpi.first.wpilibj.kinematics.ChassisSpeeds; -import edu.wpi.first.wpilibj.kinematics.SwerveDriveKinematics; -import edu.wpi.first.wpilibj.kinematics.SwerveDriveOdometry; /** Represents a swerve drive style drivetrain. */ public class Drivetrain { diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/swervebot/SwerveModule.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/swervebot/SwerveModule.java index cd5691ecf7..9fcf669d23 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/swervebot/SwerveModule.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/swervebot/SwerveModule.java @@ -4,15 +4,15 @@ package edu.wpi.first.wpilibj.examples.swervebot; +import edu.wpi.first.math.controller.SimpleMotorFeedforward; +import edu.wpi.first.math.geometry.Rotation2d; +import edu.wpi.first.math.kinematics.SwerveModuleState; +import edu.wpi.first.math.trajectory.TrapezoidProfile; import edu.wpi.first.wpilibj.Encoder; import edu.wpi.first.wpilibj.controller.PIDController; import edu.wpi.first.wpilibj.controller.ProfiledPIDController; -import edu.wpi.first.wpilibj.controller.SimpleMotorFeedforward; -import edu.wpi.first.wpilibj.geometry.Rotation2d; -import edu.wpi.first.wpilibj.kinematics.SwerveModuleState; import edu.wpi.first.wpilibj.motorcontrol.MotorController; import edu.wpi.first.wpilibj.motorcontrol.PWMSparkMax; -import edu.wpi.first.wpilibj.trajectory.TrapezoidProfile; public class SwerveModule { private static final double kWheelRadius = 0.0508; diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/swervecontrollercommand/Constants.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/swervecontrollercommand/Constants.java index 3352deed82..40a352237c 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/swervecontrollercommand/Constants.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/swervecontrollercommand/Constants.java @@ -4,9 +4,9 @@ package edu.wpi.first.wpilibj.examples.swervecontrollercommand; -import edu.wpi.first.wpilibj.geometry.Translation2d; -import edu.wpi.first.wpilibj.kinematics.SwerveDriveKinematics; -import edu.wpi.first.wpilibj.trajectory.TrapezoidProfile; +import edu.wpi.first.math.geometry.Translation2d; +import edu.wpi.first.math.kinematics.SwerveDriveKinematics; +import edu.wpi.first.math.trajectory.TrapezoidProfile; /** * The Constants class provides a convenient place for teams to hold robot-wide numerical or boolean diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/swervecontrollercommand/RobotContainer.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/swervecontrollercommand/RobotContainer.java index 20075a95d3..3a8f903dc0 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/swervecontrollercommand/RobotContainer.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/swervecontrollercommand/RobotContainer.java @@ -4,6 +4,12 @@ package edu.wpi.first.wpilibj.examples.swervecontrollercommand; +import edu.wpi.first.math.geometry.Pose2d; +import edu.wpi.first.math.geometry.Rotation2d; +import edu.wpi.first.math.geometry.Translation2d; +import edu.wpi.first.math.trajectory.Trajectory; +import edu.wpi.first.math.trajectory.TrajectoryConfig; +import edu.wpi.first.math.trajectory.TrajectoryGenerator; import edu.wpi.first.wpilibj.GenericHID; import edu.wpi.first.wpilibj.XboxController; import edu.wpi.first.wpilibj.controller.PIDController; @@ -12,12 +18,6 @@ import edu.wpi.first.wpilibj.examples.swervecontrollercommand.Constants.AutoCons import edu.wpi.first.wpilibj.examples.swervecontrollercommand.Constants.DriveConstants; import edu.wpi.first.wpilibj.examples.swervecontrollercommand.Constants.OIConstants; import edu.wpi.first.wpilibj.examples.swervecontrollercommand.subsystems.DriveSubsystem; -import edu.wpi.first.wpilibj.geometry.Pose2d; -import edu.wpi.first.wpilibj.geometry.Rotation2d; -import edu.wpi.first.wpilibj.geometry.Translation2d; -import edu.wpi.first.wpilibj.trajectory.Trajectory; -import edu.wpi.first.wpilibj.trajectory.TrajectoryConfig; -import edu.wpi.first.wpilibj.trajectory.TrajectoryGenerator; import edu.wpi.first.wpilibj2.command.Command; import edu.wpi.first.wpilibj2.command.RunCommand; import edu.wpi.first.wpilibj2.command.SwerveControllerCommand; diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/swervecontrollercommand/subsystems/DriveSubsystem.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/swervecontrollercommand/subsystems/DriveSubsystem.java index 11d7f2cb73..7a06bc4915 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/swervecontrollercommand/subsystems/DriveSubsystem.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/swervecontrollercommand/subsystems/DriveSubsystem.java @@ -4,15 +4,15 @@ package edu.wpi.first.wpilibj.examples.swervecontrollercommand.subsystems; +import edu.wpi.first.math.geometry.Pose2d; +import edu.wpi.first.math.geometry.Rotation2d; +import edu.wpi.first.math.kinematics.ChassisSpeeds; +import edu.wpi.first.math.kinematics.SwerveDriveKinematics; +import edu.wpi.first.math.kinematics.SwerveDriveOdometry; +import edu.wpi.first.math.kinematics.SwerveModuleState; import edu.wpi.first.wpilibj.ADXRS450_Gyro; import edu.wpi.first.wpilibj.examples.swervecontrollercommand.Constants.DriveConstants; -import edu.wpi.first.wpilibj.geometry.Pose2d; -import edu.wpi.first.wpilibj.geometry.Rotation2d; import edu.wpi.first.wpilibj.interfaces.Gyro; -import edu.wpi.first.wpilibj.kinematics.ChassisSpeeds; -import edu.wpi.first.wpilibj.kinematics.SwerveDriveKinematics; -import edu.wpi.first.wpilibj.kinematics.SwerveDriveOdometry; -import edu.wpi.first.wpilibj.kinematics.SwerveModuleState; import edu.wpi.first.wpilibj2.command.SubsystemBase; @SuppressWarnings("PMD.ExcessiveImports") diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/swervecontrollercommand/subsystems/SwerveModule.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/swervecontrollercommand/subsystems/SwerveModule.java index 12c6aad699..2bc737f42b 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/swervecontrollercommand/subsystems/SwerveModule.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/swervecontrollercommand/subsystems/SwerveModule.java @@ -4,14 +4,14 @@ package edu.wpi.first.wpilibj.examples.swervecontrollercommand.subsystems; +import edu.wpi.first.math.geometry.Rotation2d; +import edu.wpi.first.math.kinematics.SwerveModuleState; +import edu.wpi.first.math.trajectory.TrapezoidProfile; import edu.wpi.first.wpilibj.Encoder; import edu.wpi.first.wpilibj.controller.PIDController; import edu.wpi.first.wpilibj.controller.ProfiledPIDController; import edu.wpi.first.wpilibj.examples.swervecontrollercommand.Constants.ModuleConstants; -import edu.wpi.first.wpilibj.geometry.Rotation2d; -import edu.wpi.first.wpilibj.kinematics.SwerveModuleState; import edu.wpi.first.wpilibj.motorcontrol.Spark; -import edu.wpi.first.wpilibj.trajectory.TrapezoidProfile; public class SwerveModule { private final Spark m_driveMotor; diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/swervesdriveposeestimator/Drivetrain.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/swervesdriveposeestimator/Drivetrain.java index 3900225268..dd6843be8a 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/swervesdriveposeestimator/Drivetrain.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/swervesdriveposeestimator/Drivetrain.java @@ -4,15 +4,15 @@ package edu.wpi.first.wpilibj.examples.swervesdriveposeestimator; +import edu.wpi.first.math.VecBuilder; +import edu.wpi.first.math.estimator.SwerveDrivePoseEstimator; +import edu.wpi.first.math.geometry.Pose2d; +import edu.wpi.first.math.geometry.Translation2d; +import edu.wpi.first.math.kinematics.ChassisSpeeds; +import edu.wpi.first.math.kinematics.SwerveDriveKinematics; +import edu.wpi.first.math.util.Units; import edu.wpi.first.wpilibj.AnalogGyro; import edu.wpi.first.wpilibj.Timer; -import edu.wpi.first.wpilibj.estimator.SwerveDrivePoseEstimator; -import edu.wpi.first.wpilibj.geometry.Pose2d; -import edu.wpi.first.wpilibj.geometry.Translation2d; -import edu.wpi.first.wpilibj.kinematics.ChassisSpeeds; -import edu.wpi.first.wpilibj.kinematics.SwerveDriveKinematics; -import edu.wpi.first.wpilibj.util.Units; -import edu.wpi.first.wpiutil.math.VecBuilder; /** Represents a swerve drive style drivetrain. */ public class Drivetrain { diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/swervesdriveposeestimator/ExampleGlobalMeasurementSensor.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/swervesdriveposeestimator/ExampleGlobalMeasurementSensor.java index 232d6fd22f..3aeceb8564 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/swervesdriveposeestimator/ExampleGlobalMeasurementSensor.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/swervesdriveposeestimator/ExampleGlobalMeasurementSensor.java @@ -4,11 +4,11 @@ package edu.wpi.first.wpilibj.examples.swervesdriveposeestimator; -import edu.wpi.first.wpilibj.geometry.Pose2d; -import edu.wpi.first.wpilibj.geometry.Rotation2d; -import edu.wpi.first.wpilibj.math.StateSpaceUtil; -import edu.wpi.first.wpilibj.util.Units; -import edu.wpi.first.wpiutil.math.VecBuilder; +import edu.wpi.first.math.VecBuilder; +import edu.wpi.first.math.geometry.Pose2d; +import edu.wpi.first.math.geometry.Rotation2d; +import edu.wpi.first.math.math.StateSpaceUtil; +import edu.wpi.first.math.util.Units; /** This dummy class represents a global measurement sensor, such as a computer vision solution. */ public final class ExampleGlobalMeasurementSensor { diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/swervesdriveposeestimator/SwerveModule.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/swervesdriveposeestimator/SwerveModule.java index cf36020959..3110bc2040 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/swervesdriveposeestimator/SwerveModule.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/swervesdriveposeestimator/SwerveModule.java @@ -4,15 +4,15 @@ package edu.wpi.first.wpilibj.examples.swervesdriveposeestimator; +import edu.wpi.first.math.controller.SimpleMotorFeedforward; +import edu.wpi.first.math.geometry.Rotation2d; +import edu.wpi.first.math.kinematics.SwerveModuleState; +import edu.wpi.first.math.trajectory.TrapezoidProfile; import edu.wpi.first.wpilibj.Encoder; import edu.wpi.first.wpilibj.controller.PIDController; import edu.wpi.first.wpilibj.controller.ProfiledPIDController; -import edu.wpi.first.wpilibj.controller.SimpleMotorFeedforward; -import edu.wpi.first.wpilibj.geometry.Rotation2d; -import edu.wpi.first.wpilibj.kinematics.SwerveModuleState; import edu.wpi.first.wpilibj.motorcontrol.MotorController; import edu.wpi.first.wpilibj.motorcontrol.PWMSparkMax; -import edu.wpi.first.wpilibj.trajectory.TrapezoidProfile; public class SwerveModule { private static final double kWheelRadius = 0.0508; diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/ultrasonic/Robot.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/ultrasonic/Robot.java index 754d0eae2b..199d6f6a93 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/ultrasonic/Robot.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/ultrasonic/Robot.java @@ -4,8 +4,8 @@ package edu.wpi.first.wpilibj.examples.ultrasonic; +import edu.wpi.first.math.filter.MedianFilter; import edu.wpi.first.wpilibj.AnalogInput; -import edu.wpi.first.wpilibj.MedianFilter; import edu.wpi.first.wpilibj.TimedRobot; import edu.wpi.first.wpilibj.drive.DifferentialDrive; import edu.wpi.first.wpilibj.motorcontrol.PWMSparkMax; diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/ultrasonicpid/Robot.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/ultrasonicpid/Robot.java index 79726292da..61e2bf7818 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/ultrasonicpid/Robot.java +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/ultrasonicpid/Robot.java @@ -4,8 +4,8 @@ package edu.wpi.first.wpilibj.examples.ultrasonicpid; +import edu.wpi.first.math.filter.MedianFilter; import edu.wpi.first.wpilibj.AnalogInput; -import edu.wpi.first.wpilibj.MedianFilter; import edu.wpi.first.wpilibj.TimedRobot; import edu.wpi.first.wpilibj.controller.PIDController; import edu.wpi.first.wpilibj.drive.DifferentialDrive; diff --git a/wpilibjIntegrationTests/src/main/java/edu/wpi/first/wpilibj/MotorEncoderTest.java b/wpilibjIntegrationTests/src/main/java/edu/wpi/first/wpilibj/MotorEncoderTest.java index 87e594dfac..690ec42cb1 100644 --- a/wpilibjIntegrationTests/src/main/java/edu/wpi/first/wpilibj/MotorEncoderTest.java +++ b/wpilibjIntegrationTests/src/main/java/edu/wpi/first/wpilibj/MotorEncoderTest.java @@ -8,11 +8,12 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import edu.wpi.first.math.MathUtil; +import edu.wpi.first.math.filter.LinearFilter; import edu.wpi.first.wpilibj.controller.PIDController; import edu.wpi.first.wpilibj.fixtures.MotorEncoderFixture; import edu.wpi.first.wpilibj.test.AbstractComsSetup; import edu.wpi.first.wpilibj.test.TestBench; -import edu.wpi.first.wpiutil.math.MathUtil; import java.util.Arrays; import java.util.Collection; import java.util.logging.Logger; diff --git a/wpimath/build.gradle b/wpimath/build.gradle index 8464f87e08..2e002ca461 100644 --- a/wpimath/build.gradle +++ b/wpimath/build.gradle @@ -5,7 +5,7 @@ ext { groupId = 'edu.wpi.first.wpimath' nativeName = 'wpimath' - devMain = 'edu.wpi.first.wpiutil.math.DevMain' + devMain = 'edu.wpi.first.math.DevMain' } apply from: "${rootDir}/shared/jni/setupBuild.gradle" @@ -69,8 +69,8 @@ dependencies { def wpilibNumberFileInput = file("src/generate/GenericNumber.java.in") def natFileInput = file("src/generate/Nat.java.in") def natGetterInput = file("src/generate/NatGetter.java.in") -def wpilibNumberFileOutputDir = file("$buildDir/generated/java/edu/wpi/first/wpiutil/math/numbers") -def wpilibNatFileOutput = file("$buildDir/generated/java/edu/wpi/first/wpiutil/math/Nat.java") +def wpilibNumberFileOutputDir = file("$buildDir/generated/java/edu/wpi/first/math/numbers") +def wpilibNatFileOutput = file("$buildDir/generated/java/edu/wpi/first/math/Nat.java") def maxNum = 20 task generateNumbers() { @@ -111,7 +111,7 @@ task generateNat() { def importsString = ""; for(i in 0..maxNum) { - importsString += "import edu.wpi.first.wpiutil.math.numbers.N${i};\n" + importsString += "import edu.wpi.first.math.numbers.N${i};\n" template += natGetterInput.text.replace('${num}', i.toString()) + "\n" } template += "}\n" // Close the class body diff --git a/wpimath/generate_numbers.py b/wpimath/generate_numbers.py index 701f3e652b..de9ef447f1 100644 --- a/wpimath/generate_numbers.py +++ b/wpimath/generate_numbers.py @@ -38,7 +38,7 @@ def main(): importsString = "" for i in range(MAX_NUM + 1): - importsString += f"import edu.wpi.first.wpiutil.math.numbers.N{i};\n" + importsString += f"import edu.wpi.first.math.numbers.N{i};\n" template += getter.replace("${num}", str(i)) template += "}\n" diff --git a/wpimath/src/dev/java/edu/wpi/first/wpiutil/math/DevMain.java b/wpimath/src/dev/java/edu/wpi/first/math/DevMain.java similarity index 92% rename from wpimath/src/dev/java/edu/wpi/first/wpiutil/math/DevMain.java rename to wpimath/src/dev/java/edu/wpi/first/math/DevMain.java index b2a8254917..c7b779e007 100644 --- a/wpimath/src/dev/java/edu/wpi/first/wpiutil/math/DevMain.java +++ b/wpimath/src/dev/java/edu/wpi/first/math/DevMain.java @@ -2,7 +2,7 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.first.wpiutil.math; +package edu.wpi.first.math; public final class DevMain { /** Main entry point. */ diff --git a/wpimath/src/generate/GenericNumber.java.in b/wpimath/src/generate/GenericNumber.java.in index 5a36582e4d..3bdc1b6ba2 100644 --- a/wpimath/src/generate/GenericNumber.java.in +++ b/wpimath/src/generate/GenericNumber.java.in @@ -5,10 +5,10 @@ /* the project. */ /*----------------------------------------------------------------------------*/ -package edu.wpi.first.wpiutil.math.numbers; +package edu.wpi.first.math.numbers; -import edu.wpi.first.wpiutil.math.Nat; -import edu.wpi.first.wpiutil.math.Num; +import edu.wpi.first.math.Nat; +import edu.wpi.first.math.Num; /** * A class representing the number ${num}. diff --git a/wpimath/src/generate/Nat.java.in b/wpimath/src/generate/Nat.java.in index 666bd1c32a..2719df0c39 100644 --- a/wpimath/src/generate/Nat.java.in +++ b/wpimath/src/generate/Nat.java.in @@ -5,7 +5,7 @@ /* the project. */ /*----------------------------------------------------------------------------*/ -package edu.wpi.first.wpiutil.math; +package edu.wpi.first.math; //CHECKSTYLE.OFF: ImportOrder {{REPLACEWITHIMPORTS}} diff --git a/wpimath/src/main/java/edu/wpi/first/math/Drake.java b/wpimath/src/main/java/edu/wpi/first/math/Drake.java index 70c07391df..886f7da6f4 100644 --- a/wpimath/src/main/java/edu/wpi/first/math/Drake.java +++ b/wpimath/src/main/java/edu/wpi/first/math/Drake.java @@ -4,8 +4,6 @@ package edu.wpi.first.math; -import edu.wpi.first.wpiutil.math.Matrix; -import edu.wpi.first.wpiutil.math.Num; import org.ejml.simple.SimpleMatrix; public final class Drake { diff --git a/wpimath/src/main/java/edu/wpi/first/wpiutil/math/MatBuilder.java b/wpimath/src/main/java/edu/wpi/first/math/MatBuilder.java similarity index 97% rename from wpimath/src/main/java/edu/wpi/first/wpiutil/math/MatBuilder.java rename to wpimath/src/main/java/edu/wpi/first/math/MatBuilder.java index b75ef91ce6..e5b195224c 100644 --- a/wpimath/src/main/java/edu/wpi/first/wpiutil/math/MatBuilder.java +++ b/wpimath/src/main/java/edu/wpi/first/math/MatBuilder.java @@ -2,7 +2,7 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.first.wpiutil.math; +package edu.wpi.first.math; import java.util.Objects; import org.ejml.simple.SimpleMatrix; diff --git a/wpimath/src/main/java/edu/wpi/first/wpiutil/math/MathUtil.java b/wpimath/src/main/java/edu/wpi/first/math/MathUtil.java similarity index 98% rename from wpimath/src/main/java/edu/wpi/first/wpiutil/math/MathUtil.java rename to wpimath/src/main/java/edu/wpi/first/math/MathUtil.java index c57ca98422..c4a661a125 100644 --- a/wpimath/src/main/java/edu/wpi/first/wpiutil/math/MathUtil.java +++ b/wpimath/src/main/java/edu/wpi/first/math/MathUtil.java @@ -2,7 +2,7 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.first.wpiutil.math; +package edu.wpi.first.math; public final class MathUtil { private MathUtil() { diff --git a/wpimath/src/main/java/edu/wpi/first/wpiutil/math/Matrix.java b/wpimath/src/main/java/edu/wpi/first/math/Matrix.java similarity index 99% rename from wpimath/src/main/java/edu/wpi/first/wpiutil/math/Matrix.java rename to wpimath/src/main/java/edu/wpi/first/math/Matrix.java index a49273d3ff..be9d56d66c 100644 --- a/wpimath/src/main/java/edu/wpi/first/wpiutil/math/Matrix.java +++ b/wpimath/src/main/java/edu/wpi/first/math/Matrix.java @@ -2,10 +2,9 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.first.wpiutil.math; +package edu.wpi.first.math; -import edu.wpi.first.math.WPIMathJNI; -import edu.wpi.first.wpiutil.math.numbers.N1; +import edu.wpi.first.math.numbers.N1; import java.util.Objects; import org.ejml.MatrixDimensionException; import org.ejml.data.DMatrixRMaj; diff --git a/wpimath/src/main/java/edu/wpi/first/wpiutil/math/MatrixUtils.java b/wpimath/src/main/java/edu/wpi/first/math/MatrixUtils.java similarity index 97% rename from wpimath/src/main/java/edu/wpi/first/wpiutil/math/MatrixUtils.java rename to wpimath/src/main/java/edu/wpi/first/math/MatrixUtils.java index ac0e5fd670..7600e31573 100644 --- a/wpimath/src/main/java/edu/wpi/first/wpiutil/math/MatrixUtils.java +++ b/wpimath/src/main/java/edu/wpi/first/math/MatrixUtils.java @@ -2,9 +2,9 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.first.wpiutil.math; +package edu.wpi.first.math; -import edu.wpi.first.wpiutil.math.numbers.N1; +import edu.wpi.first.math.numbers.N1; import java.util.Objects; import org.ejml.simple.SimpleMatrix; diff --git a/wpimath/src/main/java/edu/wpi/first/wpiutil/math/Num.java b/wpimath/src/main/java/edu/wpi/first/math/Num.java similarity index 91% rename from wpimath/src/main/java/edu/wpi/first/wpiutil/math/Num.java rename to wpimath/src/main/java/edu/wpi/first/math/Num.java index 8201255e09..ef0fd2d815 100644 --- a/wpimath/src/main/java/edu/wpi/first/wpiutil/math/Num.java +++ b/wpimath/src/main/java/edu/wpi/first/math/Num.java @@ -2,7 +2,7 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.first.wpiutil.math; +package edu.wpi.first.math; /** A number expressed as a java class. */ public abstract class Num { diff --git a/wpimath/src/main/java/edu/wpi/first/wpiutil/math/Pair.java b/wpimath/src/main/java/edu/wpi/first/math/Pair.java similarity index 94% rename from wpimath/src/main/java/edu/wpi/first/wpiutil/math/Pair.java rename to wpimath/src/main/java/edu/wpi/first/math/Pair.java index 26229a047d..d1a68c7099 100644 --- a/wpimath/src/main/java/edu/wpi/first/wpiutil/math/Pair.java +++ b/wpimath/src/main/java/edu/wpi/first/math/Pair.java @@ -2,7 +2,7 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.first.wpiutil.math; +package edu.wpi.first.math; public class Pair { private final A m_first; diff --git a/wpimath/src/main/java/edu/wpi/first/wpiutil/math/SimpleMatrixUtils.java b/wpimath/src/main/java/edu/wpi/first/math/SimpleMatrixUtils.java similarity index 99% rename from wpimath/src/main/java/edu/wpi/first/wpiutil/math/SimpleMatrixUtils.java rename to wpimath/src/main/java/edu/wpi/first/math/SimpleMatrixUtils.java index eb042aaac2..c93ad320b9 100644 --- a/wpimath/src/main/java/edu/wpi/first/wpiutil/math/SimpleMatrixUtils.java +++ b/wpimath/src/main/java/edu/wpi/first/math/SimpleMatrixUtils.java @@ -2,9 +2,8 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.first.wpiutil.math; +package edu.wpi.first.math; -import edu.wpi.first.math.WPIMathJNI; import java.util.function.BiFunction; import org.ejml.data.DMatrixRMaj; import org.ejml.dense.row.NormOps_DDRM; diff --git a/wpimath/src/main/java/edu/wpi/first/wpiutil/math/VecBuilder.java b/wpimath/src/main/java/edu/wpi/first/math/VecBuilder.java similarity index 91% rename from wpimath/src/main/java/edu/wpi/first/wpiutil/math/VecBuilder.java rename to wpimath/src/main/java/edu/wpi/first/math/VecBuilder.java index 29e5a7a6a1..7bf10615ba 100644 --- a/wpimath/src/main/java/edu/wpi/first/wpiutil/math/VecBuilder.java +++ b/wpimath/src/main/java/edu/wpi/first/math/VecBuilder.java @@ -2,18 +2,18 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.first.wpiutil.math; +package edu.wpi.first.math; -import edu.wpi.first.wpiutil.math.numbers.N1; -import edu.wpi.first.wpiutil.math.numbers.N10; -import edu.wpi.first.wpiutil.math.numbers.N2; -import edu.wpi.first.wpiutil.math.numbers.N3; -import edu.wpi.first.wpiutil.math.numbers.N4; -import edu.wpi.first.wpiutil.math.numbers.N5; -import edu.wpi.first.wpiutil.math.numbers.N6; -import edu.wpi.first.wpiutil.math.numbers.N7; -import edu.wpi.first.wpiutil.math.numbers.N8; -import edu.wpi.first.wpiutil.math.numbers.N9; +import edu.wpi.first.math.numbers.N1; +import edu.wpi.first.math.numbers.N10; +import edu.wpi.first.math.numbers.N2; +import edu.wpi.first.math.numbers.N3; +import edu.wpi.first.math.numbers.N4; +import edu.wpi.first.math.numbers.N5; +import edu.wpi.first.math.numbers.N6; +import edu.wpi.first.math.numbers.N7; +import edu.wpi.first.math.numbers.N8; +import edu.wpi.first.math.numbers.N9; /** * A specialization of {@link MatBuilder} for constructing vectors (Nx1 matrices). diff --git a/wpimath/src/main/java/edu/wpi/first/wpiutil/math/Vector.java b/wpimath/src/main/java/edu/wpi/first/math/Vector.java similarity index 95% rename from wpimath/src/main/java/edu/wpi/first/wpiutil/math/Vector.java rename to wpimath/src/main/java/edu/wpi/first/math/Vector.java index 276ee34f6e..b2d989a039 100644 --- a/wpimath/src/main/java/edu/wpi/first/wpiutil/math/Vector.java +++ b/wpimath/src/main/java/edu/wpi/first/math/Vector.java @@ -2,9 +2,9 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.first.wpiutil.math; +package edu.wpi.first.math; -import edu.wpi.first.wpiutil.math.numbers.N1; +import edu.wpi.first.math.numbers.N1; import org.ejml.simple.SimpleMatrix; /** diff --git a/wpimath/src/main/java/edu/wpi/first/wpilibj/controller/ArmFeedforward.java b/wpimath/src/main/java/edu/wpi/first/math/controller/ArmFeedforward.java similarity index 99% rename from wpimath/src/main/java/edu/wpi/first/wpilibj/controller/ArmFeedforward.java rename to wpimath/src/main/java/edu/wpi/first/math/controller/ArmFeedforward.java index 967b1742f4..2991511237 100644 --- a/wpimath/src/main/java/edu/wpi/first/wpilibj/controller/ArmFeedforward.java +++ b/wpimath/src/main/java/edu/wpi/first/math/controller/ArmFeedforward.java @@ -2,7 +2,7 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.first.wpilibj.controller; +package edu.wpi.first.math.controller; /** * A helper class that computes feedforward outputs for a simple arm (modeled as a motor acting diff --git a/wpimath/src/main/java/edu/wpi/first/wpilibj/controller/ControlAffinePlantInversionFeedforward.java b/wpimath/src/main/java/edu/wpi/first/math/controller/ControlAffinePlantInversionFeedforward.java similarity index 94% rename from wpimath/src/main/java/edu/wpi/first/wpilibj/controller/ControlAffinePlantInversionFeedforward.java rename to wpimath/src/main/java/edu/wpi/first/math/controller/ControlAffinePlantInversionFeedforward.java index 9b3dc8c460..c01638ce10 100644 --- a/wpimath/src/main/java/edu/wpi/first/wpilibj/controller/ControlAffinePlantInversionFeedforward.java +++ b/wpimath/src/main/java/edu/wpi/first/math/controller/ControlAffinePlantInversionFeedforward.java @@ -2,13 +2,13 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.first.wpilibj.controller; +package edu.wpi.first.math.controller; -import edu.wpi.first.wpilibj.system.NumericalJacobian; -import edu.wpi.first.wpiutil.math.Matrix; -import edu.wpi.first.wpiutil.math.Nat; -import edu.wpi.first.wpiutil.math.Num; -import edu.wpi.first.wpiutil.math.numbers.N1; +import edu.wpi.first.math.Matrix; +import edu.wpi.first.math.Nat; +import edu.wpi.first.math.Num; +import edu.wpi.first.math.numbers.N1; +import edu.wpi.first.math.system.NumericalJacobian; import java.util.function.BiFunction; import java.util.function.Function; @@ -17,7 +17,7 @@ import java.util.function.Function; * *

        If given the vector valued function as f(x, u) where x is the state vector and u is the input * vector, the B matrix(continuous input matrix) is calculated through a {@link - * edu.wpi.first.wpilibj.system.NumericalJacobian}. In this case f has to be control-affine (of the + * edu.wpi.first.math.system.NumericalJacobian}. In this case f has to be control-affine (of the * form f(x) + Bu). * *

        The feedforward is calculated as u_ff = B+ (rDot - f(x)), where diff --git a/wpimath/src/main/java/edu/wpi/first/wpilibj/controller/ElevatorFeedforward.java b/wpimath/src/main/java/edu/wpi/first/math/controller/ElevatorFeedforward.java similarity index 99% rename from wpimath/src/main/java/edu/wpi/first/wpilibj/controller/ElevatorFeedforward.java rename to wpimath/src/main/java/edu/wpi/first/math/controller/ElevatorFeedforward.java index ebab615e6b..248015f134 100644 --- a/wpimath/src/main/java/edu/wpi/first/wpilibj/controller/ElevatorFeedforward.java +++ b/wpimath/src/main/java/edu/wpi/first/math/controller/ElevatorFeedforward.java @@ -2,7 +2,7 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.first.wpilibj.controller; +package edu.wpi.first.math.controller; /** * A helper class that computes feedforward outputs for a simple elevator (modeled as a motor acting diff --git a/wpimath/src/main/java/edu/wpi/first/wpilibj/controller/LinearPlantInversionFeedforward.java b/wpimath/src/main/java/edu/wpi/first/math/controller/LinearPlantInversionFeedforward.java similarity index 94% rename from wpimath/src/main/java/edu/wpi/first/wpilibj/controller/LinearPlantInversionFeedforward.java rename to wpimath/src/main/java/edu/wpi/first/math/controller/LinearPlantInversionFeedforward.java index 00d041f471..8610c58c64 100644 --- a/wpimath/src/main/java/edu/wpi/first/wpilibj/controller/LinearPlantInversionFeedforward.java +++ b/wpimath/src/main/java/edu/wpi/first/math/controller/LinearPlantInversionFeedforward.java @@ -2,13 +2,13 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.first.wpilibj.controller; +package edu.wpi.first.math.controller; -import edu.wpi.first.wpilibj.math.Discretization; -import edu.wpi.first.wpilibj.system.LinearSystem; -import edu.wpi.first.wpiutil.math.Matrix; -import edu.wpi.first.wpiutil.math.Num; -import edu.wpi.first.wpiutil.math.numbers.N1; +import edu.wpi.first.math.Matrix; +import edu.wpi.first.math.Num; +import edu.wpi.first.math.math.Discretization; +import edu.wpi.first.math.numbers.N1; +import edu.wpi.first.math.system.LinearSystem; import org.ejml.simple.SimpleMatrix; /** diff --git a/wpimath/src/main/java/edu/wpi/first/wpilibj/controller/LinearQuadraticRegulator.java b/wpimath/src/main/java/edu/wpi/first/math/controller/LinearQuadraticRegulator.java similarity index 94% rename from wpimath/src/main/java/edu/wpi/first/wpilibj/controller/LinearQuadraticRegulator.java rename to wpimath/src/main/java/edu/wpi/first/math/controller/LinearQuadraticRegulator.java index 0d6eca348f..5aa19e92ec 100644 --- a/wpimath/src/main/java/edu/wpi/first/wpilibj/controller/LinearQuadraticRegulator.java +++ b/wpimath/src/main/java/edu/wpi/first/math/controller/LinearQuadraticRegulator.java @@ -2,17 +2,17 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.first.wpilibj.controller; +package edu.wpi.first.math.controller; import edu.wpi.first.math.Drake; -import edu.wpi.first.wpilibj.math.Discretization; -import edu.wpi.first.wpilibj.math.StateSpaceUtil; -import edu.wpi.first.wpilibj.system.LinearSystem; -import edu.wpi.first.wpiutil.math.Matrix; -import edu.wpi.first.wpiutil.math.Nat; -import edu.wpi.first.wpiutil.math.Num; -import edu.wpi.first.wpiutil.math.Vector; -import edu.wpi.first.wpiutil.math.numbers.N1; +import edu.wpi.first.math.Matrix; +import edu.wpi.first.math.Nat; +import edu.wpi.first.math.Num; +import edu.wpi.first.math.Vector; +import edu.wpi.first.math.math.Discretization; +import edu.wpi.first.math.math.StateSpaceUtil; +import edu.wpi.first.math.numbers.N1; +import edu.wpi.first.math.system.LinearSystem; import org.ejml.simple.SimpleMatrix; /** diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/controller/RamseteController.java b/wpimath/src/main/java/edu/wpi/first/math/controller/RamseteController.java similarity index 96% rename from wpilibj/src/main/java/edu/wpi/first/wpilibj/controller/RamseteController.java rename to wpimath/src/main/java/edu/wpi/first/math/controller/RamseteController.java index 7f875874b3..086f8206a9 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/controller/RamseteController.java +++ b/wpimath/src/main/java/edu/wpi/first/math/controller/RamseteController.java @@ -2,11 +2,11 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.first.wpilibj.controller; +package edu.wpi.first.math.controller; -import edu.wpi.first.wpilibj.geometry.Pose2d; -import edu.wpi.first.wpilibj.kinematics.ChassisSpeeds; -import edu.wpi.first.wpilibj.trajectory.Trajectory; +import edu.wpi.first.math.geometry.Pose2d; +import edu.wpi.first.math.kinematics.ChassisSpeeds; +import edu.wpi.first.math.trajectory.Trajectory; /** * Ramsete is a nonlinear time-varying feedback controller for unicycle models that drives the model diff --git a/wpimath/src/main/java/edu/wpi/first/wpilibj/controller/SimpleMotorFeedforward.java b/wpimath/src/main/java/edu/wpi/first/math/controller/SimpleMotorFeedforward.java similarity index 99% rename from wpimath/src/main/java/edu/wpi/first/wpilibj/controller/SimpleMotorFeedforward.java rename to wpimath/src/main/java/edu/wpi/first/math/controller/SimpleMotorFeedforward.java index 5c28f68fad..85bdcf32a2 100644 --- a/wpimath/src/main/java/edu/wpi/first/wpilibj/controller/SimpleMotorFeedforward.java +++ b/wpimath/src/main/java/edu/wpi/first/math/controller/SimpleMotorFeedforward.java @@ -2,7 +2,7 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.first.wpilibj.controller; +package edu.wpi.first.math.controller; /** A helper class that computes feedforward outputs for a simple permanent-magnet DC motor. */ @SuppressWarnings("MemberName") diff --git a/wpimath/src/main/java/edu/wpi/first/wpilibj/estimator/AngleStatistics.java b/wpimath/src/main/java/edu/wpi/first/math/estimator/AngleStatistics.java similarity index 94% rename from wpimath/src/main/java/edu/wpi/first/wpilibj/estimator/AngleStatistics.java rename to wpimath/src/main/java/edu/wpi/first/math/estimator/AngleStatistics.java index c7077108bf..ce28dd48dc 100644 --- a/wpimath/src/main/java/edu/wpi/first/wpilibj/estimator/AngleStatistics.java +++ b/wpimath/src/main/java/edu/wpi/first/math/estimator/AngleStatistics.java @@ -2,12 +2,12 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.first.wpilibj.estimator; +package edu.wpi.first.math.estimator; -import edu.wpi.first.wpiutil.math.MathUtil; -import edu.wpi.first.wpiutil.math.Matrix; -import edu.wpi.first.wpiutil.math.Num; -import edu.wpi.first.wpiutil.math.numbers.N1; +import edu.wpi.first.math.MathUtil; +import edu.wpi.first.math.Matrix; +import edu.wpi.first.math.Num; +import edu.wpi.first.math.numbers.N1; import java.util.function.BiFunction; import org.ejml.simple.SimpleMatrix; diff --git a/wpimath/src/main/java/edu/wpi/first/wpilibj/estimator/DifferentialDrivePoseEstimator.java b/wpimath/src/main/java/edu/wpi/first/math/estimator/DifferentialDrivePoseEstimator.java similarity index 94% rename from wpimath/src/main/java/edu/wpi/first/wpilibj/estimator/DifferentialDrivePoseEstimator.java rename to wpimath/src/main/java/edu/wpi/first/math/estimator/DifferentialDrivePoseEstimator.java index 66873dec41..d78d254e12 100644 --- a/wpimath/src/main/java/edu/wpi/first/wpilibj/estimator/DifferentialDrivePoseEstimator.java +++ b/wpimath/src/main/java/edu/wpi/first/math/estimator/DifferentialDrivePoseEstimator.java @@ -2,29 +2,29 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.first.wpilibj.estimator; +package edu.wpi.first.math.estimator; -import edu.wpi.first.wpilibj.geometry.Pose2d; -import edu.wpi.first.wpilibj.geometry.Rotation2d; -import edu.wpi.first.wpilibj.kinematics.DifferentialDriveWheelSpeeds; -import edu.wpi.first.wpilibj.math.Discretization; -import edu.wpi.first.wpilibj.math.StateSpaceUtil; +import edu.wpi.first.math.MatBuilder; +import edu.wpi.first.math.Matrix; +import edu.wpi.first.math.Nat; +import edu.wpi.first.math.VecBuilder; +import edu.wpi.first.math.geometry.Pose2d; +import edu.wpi.first.math.geometry.Rotation2d; +import edu.wpi.first.math.kinematics.DifferentialDriveWheelSpeeds; +import edu.wpi.first.math.math.Discretization; +import edu.wpi.first.math.math.StateSpaceUtil; +import edu.wpi.first.math.numbers.N1; +import edu.wpi.first.math.numbers.N3; +import edu.wpi.first.math.numbers.N5; import edu.wpi.first.wpiutil.WPIUtilJNI; -import edu.wpi.first.wpiutil.math.MatBuilder; -import edu.wpi.first.wpiutil.math.Matrix; -import edu.wpi.first.wpiutil.math.Nat; -import edu.wpi.first.wpiutil.math.VecBuilder; -import edu.wpi.first.wpiutil.math.numbers.N1; -import edu.wpi.first.wpiutil.math.numbers.N3; -import edu.wpi.first.wpiutil.math.numbers.N5; import java.util.function.BiConsumer; /** - * This class wraps an {@link edu.wpi.first.wpilibj.estimator.UnscentedKalmanFilter Unscented Kalman + * This class wraps an {@link edu.wpi.first.math.estimator.UnscentedKalmanFilter Unscented Kalman * Filter} to fuse latency-compensated vision measurements with differential drive encoder * measurements. It will correct for noisy vision measurements and encoder drift. It is intended to - * be an easy drop-in for {@link edu.wpi.first.wpilibj.kinematics.DifferentialDriveOdometry}; in - * fact, if you never call {@link DifferentialDrivePoseEstimator#addVisionMeasurement} and only call + * be an easy drop-in for {@link edu.wpi.first.math.kinematics.DifferentialDriveOdometry}; in fact, + * if you never call {@link DifferentialDrivePoseEstimator#addVisionMeasurement} and only call * {@link DifferentialDrivePoseEstimator#update} then this will behave exactly the same as * DifferentialDriveOdometry. * diff --git a/wpimath/src/main/java/edu/wpi/first/wpilibj/estimator/ExtendedKalmanFilter.java b/wpimath/src/main/java/edu/wpi/first/math/estimator/ExtendedKalmanFilter.java similarity index 96% rename from wpimath/src/main/java/edu/wpi/first/wpilibj/estimator/ExtendedKalmanFilter.java rename to wpimath/src/main/java/edu/wpi/first/math/estimator/ExtendedKalmanFilter.java index 2f3343c982..325159ec20 100644 --- a/wpimath/src/main/java/edu/wpi/first/wpilibj/estimator/ExtendedKalmanFilter.java +++ b/wpimath/src/main/java/edu/wpi/first/math/estimator/ExtendedKalmanFilter.java @@ -2,17 +2,17 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.first.wpilibj.estimator; +package edu.wpi.first.math.estimator; import edu.wpi.first.math.Drake; -import edu.wpi.first.wpilibj.math.Discretization; -import edu.wpi.first.wpilibj.math.StateSpaceUtil; -import edu.wpi.first.wpilibj.system.NumericalIntegration; -import edu.wpi.first.wpilibj.system.NumericalJacobian; -import edu.wpi.first.wpiutil.math.Matrix; -import edu.wpi.first.wpiutil.math.Nat; -import edu.wpi.first.wpiutil.math.Num; -import edu.wpi.first.wpiutil.math.numbers.N1; +import edu.wpi.first.math.Matrix; +import edu.wpi.first.math.Nat; +import edu.wpi.first.math.Num; +import edu.wpi.first.math.math.Discretization; +import edu.wpi.first.math.math.StateSpaceUtil; +import edu.wpi.first.math.numbers.N1; +import edu.wpi.first.math.system.NumericalIntegration; +import edu.wpi.first.math.system.NumericalJacobian; import java.util.function.BiFunction; /** diff --git a/wpimath/src/main/java/edu/wpi/first/wpilibj/estimator/KalmanFilter.java b/wpimath/src/main/java/edu/wpi/first/math/estimator/KalmanFilter.java similarity index 94% rename from wpimath/src/main/java/edu/wpi/first/wpilibj/estimator/KalmanFilter.java rename to wpimath/src/main/java/edu/wpi/first/math/estimator/KalmanFilter.java index f98d40c47c..197bb9367e 100644 --- a/wpimath/src/main/java/edu/wpi/first/wpilibj/estimator/KalmanFilter.java +++ b/wpimath/src/main/java/edu/wpi/first/math/estimator/KalmanFilter.java @@ -2,17 +2,17 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.first.wpilibj.estimator; +package edu.wpi.first.math.estimator; import edu.wpi.first.math.Drake; import edu.wpi.first.math.MathSharedStore; -import edu.wpi.first.wpilibj.math.Discretization; -import edu.wpi.first.wpilibj.math.StateSpaceUtil; -import edu.wpi.first.wpilibj.system.LinearSystem; -import edu.wpi.first.wpiutil.math.Matrix; -import edu.wpi.first.wpiutil.math.Nat; -import edu.wpi.first.wpiutil.math.Num; -import edu.wpi.first.wpiutil.math.numbers.N1; +import edu.wpi.first.math.Matrix; +import edu.wpi.first.math.Nat; +import edu.wpi.first.math.Num; +import edu.wpi.first.math.math.Discretization; +import edu.wpi.first.math.math.StateSpaceUtil; +import edu.wpi.first.math.numbers.N1; +import edu.wpi.first.math.system.LinearSystem; /** * A Kalman filter combines predictions from a model and measurements to give an estimate of the diff --git a/wpimath/src/main/java/edu/wpi/first/wpilibj/estimator/KalmanFilterLatencyCompensator.java b/wpimath/src/main/java/edu/wpi/first/math/estimator/KalmanFilterLatencyCompensator.java similarity index 96% rename from wpimath/src/main/java/edu/wpi/first/wpilibj/estimator/KalmanFilterLatencyCompensator.java rename to wpimath/src/main/java/edu/wpi/first/math/estimator/KalmanFilterLatencyCompensator.java index 29685d33e6..52ebb5c063 100644 --- a/wpimath/src/main/java/edu/wpi/first/wpilibj/estimator/KalmanFilterLatencyCompensator.java +++ b/wpimath/src/main/java/edu/wpi/first/math/estimator/KalmanFilterLatencyCompensator.java @@ -2,12 +2,12 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.first.wpilibj.estimator; +package edu.wpi.first.math.estimator; -import edu.wpi.first.wpiutil.math.Matrix; -import edu.wpi.first.wpiutil.math.Nat; -import edu.wpi.first.wpiutil.math.Num; -import edu.wpi.first.wpiutil.math.numbers.N1; +import edu.wpi.first.math.Matrix; +import edu.wpi.first.math.Nat; +import edu.wpi.first.math.Num; +import edu.wpi.first.math.numbers.N1; import java.util.ArrayList; import java.util.List; import java.util.Map; diff --git a/wpimath/src/main/java/edu/wpi/first/wpilibj/estimator/KalmanTypeFilter.java b/wpimath/src/main/java/edu/wpi/first/math/estimator/KalmanTypeFilter.java similarity index 81% rename from wpimath/src/main/java/edu/wpi/first/wpilibj/estimator/KalmanTypeFilter.java rename to wpimath/src/main/java/edu/wpi/first/math/estimator/KalmanTypeFilter.java index 25de57a317..3fd3957d71 100644 --- a/wpimath/src/main/java/edu/wpi/first/wpilibj/estimator/KalmanTypeFilter.java +++ b/wpimath/src/main/java/edu/wpi/first/math/estimator/KalmanTypeFilter.java @@ -2,11 +2,11 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.first.wpilibj.estimator; +package edu.wpi.first.math.estimator; -import edu.wpi.first.wpiutil.math.Matrix; -import edu.wpi.first.wpiutil.math.Num; -import edu.wpi.first.wpiutil.math.numbers.N1; +import edu.wpi.first.math.Matrix; +import edu.wpi.first.math.Num; +import edu.wpi.first.math.numbers.N1; @SuppressWarnings({"ParameterName", "InterfaceTypeParameterName"}) interface KalmanTypeFilter { diff --git a/wpimath/src/main/java/edu/wpi/first/wpilibj/estimator/MecanumDrivePoseEstimator.java b/wpimath/src/main/java/edu/wpi/first/math/estimator/MecanumDrivePoseEstimator.java similarity index 94% rename from wpimath/src/main/java/edu/wpi/first/wpilibj/estimator/MecanumDrivePoseEstimator.java rename to wpimath/src/main/java/edu/wpi/first/math/estimator/MecanumDrivePoseEstimator.java index afca17dae9..79dcccefb0 100644 --- a/wpimath/src/main/java/edu/wpi/first/wpilibj/estimator/MecanumDrivePoseEstimator.java +++ b/wpimath/src/main/java/edu/wpi/first/math/estimator/MecanumDrivePoseEstimator.java @@ -2,28 +2,28 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.first.wpilibj.estimator; +package edu.wpi.first.math.estimator; -import edu.wpi.first.wpilibj.geometry.Pose2d; -import edu.wpi.first.wpilibj.geometry.Rotation2d; -import edu.wpi.first.wpilibj.geometry.Translation2d; -import edu.wpi.first.wpilibj.kinematics.MecanumDriveKinematics; -import edu.wpi.first.wpilibj.kinematics.MecanumDriveWheelSpeeds; -import edu.wpi.first.wpilibj.math.Discretization; -import edu.wpi.first.wpilibj.math.StateSpaceUtil; +import edu.wpi.first.math.Matrix; +import edu.wpi.first.math.Nat; +import edu.wpi.first.math.VecBuilder; +import edu.wpi.first.math.geometry.Pose2d; +import edu.wpi.first.math.geometry.Rotation2d; +import edu.wpi.first.math.geometry.Translation2d; +import edu.wpi.first.math.kinematics.MecanumDriveKinematics; +import edu.wpi.first.math.kinematics.MecanumDriveWheelSpeeds; +import edu.wpi.first.math.math.Discretization; +import edu.wpi.first.math.math.StateSpaceUtil; +import edu.wpi.first.math.numbers.N1; +import edu.wpi.first.math.numbers.N3; import edu.wpi.first.wpiutil.WPIUtilJNI; -import edu.wpi.first.wpiutil.math.Matrix; -import edu.wpi.first.wpiutil.math.Nat; -import edu.wpi.first.wpiutil.math.VecBuilder; -import edu.wpi.first.wpiutil.math.numbers.N1; -import edu.wpi.first.wpiutil.math.numbers.N3; import java.util.function.BiConsumer; /** * This class wraps an {@link UnscentedKalmanFilter Unscented Kalman Filter} to fuse * latency-compensated vision measurements with mecanum drive encoder velocity measurements. It will * correct for noisy measurements and encoder drift. It is intended to be an easy but more accurate - * drop-in for {@link edu.wpi.first.wpilibj.kinematics.MecanumDriveOdometry}. + * drop-in for {@link edu.wpi.first.math.kinematics.MecanumDriveOdometry}. * *

        {@link MecanumDrivePoseEstimator#update} should be called every robot loop. If your loops are * faster or slower than the default of 0.02s, then you should change the nominal delta time using diff --git a/wpimath/src/main/java/edu/wpi/first/wpilibj/estimator/MerweScaledSigmaPoints.java b/wpimath/src/main/java/edu/wpi/first/math/estimator/MerweScaledSigmaPoints.java similarity index 96% rename from wpimath/src/main/java/edu/wpi/first/wpilibj/estimator/MerweScaledSigmaPoints.java rename to wpimath/src/main/java/edu/wpi/first/math/estimator/MerweScaledSigmaPoints.java index 95fcb71681..daaadb457c 100644 --- a/wpimath/src/main/java/edu/wpi/first/wpilibj/estimator/MerweScaledSigmaPoints.java +++ b/wpimath/src/main/java/edu/wpi/first/math/estimator/MerweScaledSigmaPoints.java @@ -2,12 +2,12 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.first.wpilibj.estimator; +package edu.wpi.first.math.estimator; -import edu.wpi.first.wpiutil.math.Matrix; -import edu.wpi.first.wpiutil.math.Nat; -import edu.wpi.first.wpiutil.math.Num; -import edu.wpi.first.wpiutil.math.numbers.N1; +import edu.wpi.first.math.Matrix; +import edu.wpi.first.math.Nat; +import edu.wpi.first.math.Num; +import edu.wpi.first.math.numbers.N1; import org.ejml.simple.SimpleMatrix; /** diff --git a/wpimath/src/main/java/edu/wpi/first/wpilibj/estimator/SwerveDrivePoseEstimator.java b/wpimath/src/main/java/edu/wpi/first/math/estimator/SwerveDrivePoseEstimator.java similarity index 94% rename from wpimath/src/main/java/edu/wpi/first/wpilibj/estimator/SwerveDrivePoseEstimator.java rename to wpimath/src/main/java/edu/wpi/first/math/estimator/SwerveDrivePoseEstimator.java index 958e8357e7..40afdadb5d 100644 --- a/wpimath/src/main/java/edu/wpi/first/wpilibj/estimator/SwerveDrivePoseEstimator.java +++ b/wpimath/src/main/java/edu/wpi/first/math/estimator/SwerveDrivePoseEstimator.java @@ -2,28 +2,28 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.first.wpilibj.estimator; +package edu.wpi.first.math.estimator; -import edu.wpi.first.wpilibj.geometry.Pose2d; -import edu.wpi.first.wpilibj.geometry.Rotation2d; -import edu.wpi.first.wpilibj.geometry.Translation2d; -import edu.wpi.first.wpilibj.kinematics.SwerveDriveKinematics; -import edu.wpi.first.wpilibj.kinematics.SwerveModuleState; -import edu.wpi.first.wpilibj.math.Discretization; -import edu.wpi.first.wpilibj.math.StateSpaceUtil; +import edu.wpi.first.math.Matrix; +import edu.wpi.first.math.Nat; +import edu.wpi.first.math.VecBuilder; +import edu.wpi.first.math.geometry.Pose2d; +import edu.wpi.first.math.geometry.Rotation2d; +import edu.wpi.first.math.geometry.Translation2d; +import edu.wpi.first.math.kinematics.SwerveDriveKinematics; +import edu.wpi.first.math.kinematics.SwerveModuleState; +import edu.wpi.first.math.math.Discretization; +import edu.wpi.first.math.math.StateSpaceUtil; +import edu.wpi.first.math.numbers.N1; +import edu.wpi.first.math.numbers.N3; import edu.wpi.first.wpiutil.WPIUtilJNI; -import edu.wpi.first.wpiutil.math.Matrix; -import edu.wpi.first.wpiutil.math.Nat; -import edu.wpi.first.wpiutil.math.VecBuilder; -import edu.wpi.first.wpiutil.math.numbers.N1; -import edu.wpi.first.wpiutil.math.numbers.N3; import java.util.function.BiConsumer; /** * This class wraps an {@link UnscentedKalmanFilter Unscented Kalman Filter} to fuse * latency-compensated vision measurements with swerve drive encoder velocity measurements. It will * correct for noisy measurements and encoder drift. It is intended to be an easy but more accurate - * drop-in for {@link edu.wpi.first.wpilibj.kinematics.SwerveDriveOdometry}. + * drop-in for {@link edu.wpi.first.math.kinematics.SwerveDriveOdometry}. * *

        {@link SwerveDrivePoseEstimator#update} should be called every robot loop. If your loops are * faster or slower than the default of 0.02s, then you should change the nominal delta time using diff --git a/wpimath/src/main/java/edu/wpi/first/wpilibj/estimator/UnscentedKalmanFilter.java b/wpimath/src/main/java/edu/wpi/first/math/estimator/UnscentedKalmanFilter.java similarity index 97% rename from wpimath/src/main/java/edu/wpi/first/wpilibj/estimator/UnscentedKalmanFilter.java rename to wpimath/src/main/java/edu/wpi/first/math/estimator/UnscentedKalmanFilter.java index 146fbbe64e..c242e05ee6 100644 --- a/wpimath/src/main/java/edu/wpi/first/wpilibj/estimator/UnscentedKalmanFilter.java +++ b/wpimath/src/main/java/edu/wpi/first/math/estimator/UnscentedKalmanFilter.java @@ -2,17 +2,17 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.first.wpilibj.estimator; +package edu.wpi.first.math.estimator; -import edu.wpi.first.wpilibj.math.Discretization; -import edu.wpi.first.wpilibj.math.StateSpaceUtil; -import edu.wpi.first.wpilibj.system.NumericalIntegration; -import edu.wpi.first.wpilibj.system.NumericalJacobian; -import edu.wpi.first.wpiutil.math.Matrix; -import edu.wpi.first.wpiutil.math.Nat; -import edu.wpi.first.wpiutil.math.Num; -import edu.wpi.first.wpiutil.math.Pair; -import edu.wpi.first.wpiutil.math.numbers.N1; +import edu.wpi.first.math.Matrix; +import edu.wpi.first.math.Nat; +import edu.wpi.first.math.Num; +import edu.wpi.first.math.Pair; +import edu.wpi.first.math.math.Discretization; +import edu.wpi.first.math.math.StateSpaceUtil; +import edu.wpi.first.math.numbers.N1; +import edu.wpi.first.math.system.NumericalIntegration; +import edu.wpi.first.math.system.NumericalJacobian; import java.util.function.BiFunction; import org.ejml.simple.SimpleMatrix; diff --git a/wpimath/src/main/java/edu/wpi/first/wpilibj/LinearFilter.java b/wpimath/src/main/java/edu/wpi/first/math/filter/LinearFilter.java similarity index 99% rename from wpimath/src/main/java/edu/wpi/first/wpilibj/LinearFilter.java rename to wpimath/src/main/java/edu/wpi/first/math/filter/LinearFilter.java index 1f782d5281..ffb9178cc3 100644 --- a/wpimath/src/main/java/edu/wpi/first/wpilibj/LinearFilter.java +++ b/wpimath/src/main/java/edu/wpi/first/math/filter/LinearFilter.java @@ -2,7 +2,7 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.first.wpilibj; +package edu.wpi.first.math.filter; import edu.wpi.first.math.MathSharedStore; import edu.wpi.first.math.MathUsageId; diff --git a/wpimath/src/main/java/edu/wpi/first/wpilibj/MedianFilter.java b/wpimath/src/main/java/edu/wpi/first/math/filter/MedianFilter.java similarity index 98% rename from wpimath/src/main/java/edu/wpi/first/wpilibj/MedianFilter.java rename to wpimath/src/main/java/edu/wpi/first/math/filter/MedianFilter.java index 7c285834a9..b9c4198191 100644 --- a/wpimath/src/main/java/edu/wpi/first/wpilibj/MedianFilter.java +++ b/wpimath/src/main/java/edu/wpi/first/math/filter/MedianFilter.java @@ -2,7 +2,7 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.first.wpilibj; +package edu.wpi.first.math.filter; import edu.wpi.first.wpiutil.CircularBuffer; import java.util.ArrayList; diff --git a/wpimath/src/main/java/edu/wpi/first/wpilibj/geometry/Pose2d.java b/wpimath/src/main/java/edu/wpi/first/math/geometry/Pose2d.java similarity index 99% rename from wpimath/src/main/java/edu/wpi/first/wpilibj/geometry/Pose2d.java rename to wpimath/src/main/java/edu/wpi/first/math/geometry/Pose2d.java index 8f5687607e..6033b898f1 100644 --- a/wpimath/src/main/java/edu/wpi/first/wpilibj/geometry/Pose2d.java +++ b/wpimath/src/main/java/edu/wpi/first/math/geometry/Pose2d.java @@ -2,7 +2,7 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.first.wpilibj.geometry; +package edu.wpi.first.math.geometry; import com.fasterxml.jackson.annotation.JsonAutoDetect; import com.fasterxml.jackson.annotation.JsonCreator; diff --git a/wpimath/src/main/java/edu/wpi/first/wpilibj/geometry/Rotation2d.java b/wpimath/src/main/java/edu/wpi/first/math/geometry/Rotation2d.java similarity index 99% rename from wpimath/src/main/java/edu/wpi/first/wpilibj/geometry/Rotation2d.java rename to wpimath/src/main/java/edu/wpi/first/math/geometry/Rotation2d.java index c4c83a811c..74ef228919 100644 --- a/wpimath/src/main/java/edu/wpi/first/wpilibj/geometry/Rotation2d.java +++ b/wpimath/src/main/java/edu/wpi/first/math/geometry/Rotation2d.java @@ -2,7 +2,7 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.first.wpilibj.geometry; +package edu.wpi.first.math.geometry; import com.fasterxml.jackson.annotation.JsonAutoDetect; import com.fasterxml.jackson.annotation.JsonCreator; diff --git a/wpimath/src/main/java/edu/wpi/first/wpilibj/geometry/Transform2d.java b/wpimath/src/main/java/edu/wpi/first/math/geometry/Transform2d.java similarity index 98% rename from wpimath/src/main/java/edu/wpi/first/wpilibj/geometry/Transform2d.java rename to wpimath/src/main/java/edu/wpi/first/math/geometry/Transform2d.java index 19dd4d6056..2eb69dab60 100644 --- a/wpimath/src/main/java/edu/wpi/first/wpilibj/geometry/Transform2d.java +++ b/wpimath/src/main/java/edu/wpi/first/math/geometry/Transform2d.java @@ -2,7 +2,7 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.first.wpilibj.geometry; +package edu.wpi.first.math.geometry; import java.util.Objects; diff --git a/wpimath/src/main/java/edu/wpi/first/wpilibj/geometry/Translation2d.java b/wpimath/src/main/java/edu/wpi/first/math/geometry/Translation2d.java similarity index 99% rename from wpimath/src/main/java/edu/wpi/first/wpilibj/geometry/Translation2d.java rename to wpimath/src/main/java/edu/wpi/first/math/geometry/Translation2d.java index 23723a7216..251c078671 100644 --- a/wpimath/src/main/java/edu/wpi/first/wpilibj/geometry/Translation2d.java +++ b/wpimath/src/main/java/edu/wpi/first/math/geometry/Translation2d.java @@ -2,7 +2,7 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.first.wpilibj.geometry; +package edu.wpi.first.math.geometry; import com.fasterxml.jackson.annotation.JsonAutoDetect; import com.fasterxml.jackson.annotation.JsonCreator; diff --git a/wpimath/src/main/java/edu/wpi/first/wpilibj/geometry/Twist2d.java b/wpimath/src/main/java/edu/wpi/first/math/geometry/Twist2d.java similarity index 97% rename from wpimath/src/main/java/edu/wpi/first/wpilibj/geometry/Twist2d.java rename to wpimath/src/main/java/edu/wpi/first/math/geometry/Twist2d.java index 2d2a56ebf0..c73d23629a 100644 --- a/wpimath/src/main/java/edu/wpi/first/wpilibj/geometry/Twist2d.java +++ b/wpimath/src/main/java/edu/wpi/first/math/geometry/Twist2d.java @@ -2,7 +2,7 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.first.wpilibj.geometry; +package edu.wpi.first.math.geometry; import java.util.Objects; diff --git a/wpimath/src/main/java/edu/wpi/first/wpilibj/kinematics/ChassisSpeeds.java b/wpimath/src/main/java/edu/wpi/first/math/kinematics/ChassisSpeeds.java similarity index 97% rename from wpimath/src/main/java/edu/wpi/first/wpilibj/kinematics/ChassisSpeeds.java rename to wpimath/src/main/java/edu/wpi/first/math/kinematics/ChassisSpeeds.java index 5723ca3369..451c008006 100644 --- a/wpimath/src/main/java/edu/wpi/first/wpilibj/kinematics/ChassisSpeeds.java +++ b/wpimath/src/main/java/edu/wpi/first/math/kinematics/ChassisSpeeds.java @@ -2,9 +2,9 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.first.wpilibj.kinematics; +package edu.wpi.first.math.kinematics; -import edu.wpi.first.wpilibj.geometry.Rotation2d; +import edu.wpi.first.math.geometry.Rotation2d; /** * Represents the speed of a robot chassis. Although this struct contains similar members compared diff --git a/wpimath/src/main/java/edu/wpi/first/wpilibj/kinematics/DifferentialDriveKinematics.java b/wpimath/src/main/java/edu/wpi/first/math/kinematics/DifferentialDriveKinematics.java similarity index 98% rename from wpimath/src/main/java/edu/wpi/first/wpilibj/kinematics/DifferentialDriveKinematics.java rename to wpimath/src/main/java/edu/wpi/first/math/kinematics/DifferentialDriveKinematics.java index 29d6f14925..7984e396c9 100644 --- a/wpimath/src/main/java/edu/wpi/first/wpilibj/kinematics/DifferentialDriveKinematics.java +++ b/wpimath/src/main/java/edu/wpi/first/math/kinematics/DifferentialDriveKinematics.java @@ -2,7 +2,7 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.first.wpilibj.kinematics; +package edu.wpi.first.math.kinematics; import edu.wpi.first.math.MathSharedStore; import edu.wpi.first.math.MathUsageId; diff --git a/wpimath/src/main/java/edu/wpi/first/wpilibj/kinematics/DifferentialDriveOdometry.java b/wpimath/src/main/java/edu/wpi/first/math/kinematics/DifferentialDriveOdometry.java similarity index 95% rename from wpimath/src/main/java/edu/wpi/first/wpilibj/kinematics/DifferentialDriveOdometry.java rename to wpimath/src/main/java/edu/wpi/first/math/kinematics/DifferentialDriveOdometry.java index fe0af3c285..01395735a6 100644 --- a/wpimath/src/main/java/edu/wpi/first/wpilibj/kinematics/DifferentialDriveOdometry.java +++ b/wpimath/src/main/java/edu/wpi/first/math/kinematics/DifferentialDriveOdometry.java @@ -2,13 +2,13 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.first.wpilibj.kinematics; +package edu.wpi.first.math.kinematics; import edu.wpi.first.math.MathSharedStore; import edu.wpi.first.math.MathUsageId; -import edu.wpi.first.wpilibj.geometry.Pose2d; -import edu.wpi.first.wpilibj.geometry.Rotation2d; -import edu.wpi.first.wpilibj.geometry.Twist2d; +import edu.wpi.first.math.geometry.Pose2d; +import edu.wpi.first.math.geometry.Rotation2d; +import edu.wpi.first.math.geometry.Twist2d; /** * Class for differential drive odometry. Odometry allows you to track the robot's position on the diff --git a/wpimath/src/main/java/edu/wpi/first/wpilibj/kinematics/DifferentialDriveWheelSpeeds.java b/wpimath/src/main/java/edu/wpi/first/math/kinematics/DifferentialDriveWheelSpeeds.java similarity index 98% rename from wpimath/src/main/java/edu/wpi/first/wpilibj/kinematics/DifferentialDriveWheelSpeeds.java rename to wpimath/src/main/java/edu/wpi/first/math/kinematics/DifferentialDriveWheelSpeeds.java index 82ab4eb600..b84eeba3ad 100644 --- a/wpimath/src/main/java/edu/wpi/first/wpilibj/kinematics/DifferentialDriveWheelSpeeds.java +++ b/wpimath/src/main/java/edu/wpi/first/math/kinematics/DifferentialDriveWheelSpeeds.java @@ -2,7 +2,7 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.first.wpilibj.kinematics; +package edu.wpi.first.math.kinematics; /** Represents the wheel speeds for a differential drive drivetrain. */ @SuppressWarnings("MemberName") diff --git a/wpimath/src/main/java/edu/wpi/first/wpilibj/kinematics/MecanumDriveKinematics.java b/wpimath/src/main/java/edu/wpi/first/math/kinematics/MecanumDriveKinematics.java similarity index 98% rename from wpimath/src/main/java/edu/wpi/first/wpilibj/kinematics/MecanumDriveKinematics.java rename to wpimath/src/main/java/edu/wpi/first/math/kinematics/MecanumDriveKinematics.java index d8208a3a85..1ea1c2db06 100644 --- a/wpimath/src/main/java/edu/wpi/first/wpilibj/kinematics/MecanumDriveKinematics.java +++ b/wpimath/src/main/java/edu/wpi/first/math/kinematics/MecanumDriveKinematics.java @@ -2,11 +2,11 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.first.wpilibj.kinematics; +package edu.wpi.first.math.kinematics; import edu.wpi.first.math.MathSharedStore; import edu.wpi.first.math.MathUsageId; -import edu.wpi.first.wpilibj.geometry.Translation2d; +import edu.wpi.first.math.geometry.Translation2d; import org.ejml.simple.SimpleMatrix; /** diff --git a/wpimath/src/main/java/edu/wpi/first/wpilibj/kinematics/MecanumDriveMotorVoltages.java b/wpimath/src/main/java/edu/wpi/first/math/kinematics/MecanumDriveMotorVoltages.java similarity index 97% rename from wpimath/src/main/java/edu/wpi/first/wpilibj/kinematics/MecanumDriveMotorVoltages.java rename to wpimath/src/main/java/edu/wpi/first/math/kinematics/MecanumDriveMotorVoltages.java index 5696c96935..b504acc71d 100644 --- a/wpimath/src/main/java/edu/wpi/first/wpilibj/kinematics/MecanumDriveMotorVoltages.java +++ b/wpimath/src/main/java/edu/wpi/first/math/kinematics/MecanumDriveMotorVoltages.java @@ -2,7 +2,7 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.first.wpilibj.kinematics; +package edu.wpi.first.math.kinematics; /** Represents the motor voltages for a mecanum drive drivetrain. */ @SuppressWarnings("MemberName") diff --git a/wpimath/src/main/java/edu/wpi/first/wpilibj/kinematics/MecanumDriveOdometry.java b/wpimath/src/main/java/edu/wpi/first/math/kinematics/MecanumDriveOdometry.java similarity index 96% rename from wpimath/src/main/java/edu/wpi/first/wpilibj/kinematics/MecanumDriveOdometry.java rename to wpimath/src/main/java/edu/wpi/first/math/kinematics/MecanumDriveOdometry.java index b2dcc8649e..7bf9b5b7f0 100644 --- a/wpimath/src/main/java/edu/wpi/first/wpilibj/kinematics/MecanumDriveOdometry.java +++ b/wpimath/src/main/java/edu/wpi/first/math/kinematics/MecanumDriveOdometry.java @@ -2,13 +2,13 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.first.wpilibj.kinematics; +package edu.wpi.first.math.kinematics; import edu.wpi.first.math.MathSharedStore; import edu.wpi.first.math.MathUsageId; -import edu.wpi.first.wpilibj.geometry.Pose2d; -import edu.wpi.first.wpilibj.geometry.Rotation2d; -import edu.wpi.first.wpilibj.geometry.Twist2d; +import edu.wpi.first.math.geometry.Pose2d; +import edu.wpi.first.math.geometry.Rotation2d; +import edu.wpi.first.math.geometry.Twist2d; import edu.wpi.first.wpiutil.WPIUtilJNI; /** diff --git a/wpimath/src/main/java/edu/wpi/first/wpilibj/kinematics/MecanumDriveWheelSpeeds.java b/wpimath/src/main/java/edu/wpi/first/math/kinematics/MecanumDriveWheelSpeeds.java similarity index 98% rename from wpimath/src/main/java/edu/wpi/first/wpilibj/kinematics/MecanumDriveWheelSpeeds.java rename to wpimath/src/main/java/edu/wpi/first/math/kinematics/MecanumDriveWheelSpeeds.java index b9f753ea25..7a159fe661 100644 --- a/wpimath/src/main/java/edu/wpi/first/wpilibj/kinematics/MecanumDriveWheelSpeeds.java +++ b/wpimath/src/main/java/edu/wpi/first/math/kinematics/MecanumDriveWheelSpeeds.java @@ -2,7 +2,7 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.first.wpilibj.kinematics; +package edu.wpi.first.math.kinematics; import java.util.stream.DoubleStream; diff --git a/wpimath/src/main/java/edu/wpi/first/wpilibj/kinematics/SwerveDriveKinematics.java b/wpimath/src/main/java/edu/wpi/first/math/kinematics/SwerveDriveKinematics.java similarity index 98% rename from wpimath/src/main/java/edu/wpi/first/wpilibj/kinematics/SwerveDriveKinematics.java rename to wpimath/src/main/java/edu/wpi/first/math/kinematics/SwerveDriveKinematics.java index fdaf2ec782..5acb6eebbf 100644 --- a/wpimath/src/main/java/edu/wpi/first/wpilibj/kinematics/SwerveDriveKinematics.java +++ b/wpimath/src/main/java/edu/wpi/first/math/kinematics/SwerveDriveKinematics.java @@ -2,12 +2,12 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.first.wpilibj.kinematics; +package edu.wpi.first.math.kinematics; import edu.wpi.first.math.MathSharedStore; import edu.wpi.first.math.MathUsageId; -import edu.wpi.first.wpilibj.geometry.Rotation2d; -import edu.wpi.first.wpilibj.geometry.Translation2d; +import edu.wpi.first.math.geometry.Rotation2d; +import edu.wpi.first.math.geometry.Translation2d; import java.util.Arrays; import java.util.Collections; import org.ejml.simple.SimpleMatrix; diff --git a/wpimath/src/main/java/edu/wpi/first/wpilibj/kinematics/SwerveDriveOdometry.java b/wpimath/src/main/java/edu/wpi/first/math/kinematics/SwerveDriveOdometry.java similarity index 96% rename from wpimath/src/main/java/edu/wpi/first/wpilibj/kinematics/SwerveDriveOdometry.java rename to wpimath/src/main/java/edu/wpi/first/math/kinematics/SwerveDriveOdometry.java index 0dd4a0098f..df5c0e115a 100644 --- a/wpimath/src/main/java/edu/wpi/first/wpilibj/kinematics/SwerveDriveOdometry.java +++ b/wpimath/src/main/java/edu/wpi/first/math/kinematics/SwerveDriveOdometry.java @@ -2,13 +2,13 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.first.wpilibj.kinematics; +package edu.wpi.first.math.kinematics; import edu.wpi.first.math.MathSharedStore; import edu.wpi.first.math.MathUsageId; -import edu.wpi.first.wpilibj.geometry.Pose2d; -import edu.wpi.first.wpilibj.geometry.Rotation2d; -import edu.wpi.first.wpilibj.geometry.Twist2d; +import edu.wpi.first.math.geometry.Pose2d; +import edu.wpi.first.math.geometry.Rotation2d; +import edu.wpi.first.math.geometry.Twist2d; import edu.wpi.first.wpiutil.WPIUtilJNI; /** diff --git a/wpimath/src/main/java/edu/wpi/first/wpilibj/kinematics/SwerveModuleState.java b/wpimath/src/main/java/edu/wpi/first/math/kinematics/SwerveModuleState.java similarity index 96% rename from wpimath/src/main/java/edu/wpi/first/wpilibj/kinematics/SwerveModuleState.java rename to wpimath/src/main/java/edu/wpi/first/math/kinematics/SwerveModuleState.java index 2c800376d4..d005f18866 100644 --- a/wpimath/src/main/java/edu/wpi/first/wpilibj/kinematics/SwerveModuleState.java +++ b/wpimath/src/main/java/edu/wpi/first/math/kinematics/SwerveModuleState.java @@ -2,9 +2,9 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.first.wpilibj.kinematics; +package edu.wpi.first.math.kinematics; -import edu.wpi.first.wpilibj.geometry.Rotation2d; +import edu.wpi.first.math.geometry.Rotation2d; /** Represents the state of one swerve module. */ @SuppressWarnings("MemberName") diff --git a/wpimath/src/main/java/edu/wpi/first/wpilibj/math/Discretization.java b/wpimath/src/main/java/edu/wpi/first/math/math/Discretization.java similarity index 96% rename from wpimath/src/main/java/edu/wpi/first/wpilibj/math/Discretization.java rename to wpimath/src/main/java/edu/wpi/first/math/math/Discretization.java index b32548730b..25c85eb372 100644 --- a/wpimath/src/main/java/edu/wpi/first/wpilibj/math/Discretization.java +++ b/wpimath/src/main/java/edu/wpi/first/math/math/Discretization.java @@ -2,12 +2,12 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.first.wpilibj.math; +package edu.wpi.first.math.math; -import edu.wpi.first.wpiutil.math.Matrix; -import edu.wpi.first.wpiutil.math.Nat; -import edu.wpi.first.wpiutil.math.Num; -import edu.wpi.first.wpiutil.math.Pair; +import edu.wpi.first.math.Matrix; +import edu.wpi.first.math.Nat; +import edu.wpi.first.math.Num; +import edu.wpi.first.math.Pair; import org.ejml.simple.SimpleMatrix; @SuppressWarnings({"ParameterName", "MethodTypeParameterName"}) diff --git a/wpimath/src/main/java/edu/wpi/first/wpilibj/math/StateSpaceUtil.java b/wpimath/src/main/java/edu/wpi/first/math/math/StateSpaceUtil.java similarity index 93% rename from wpimath/src/main/java/edu/wpi/first/wpilibj/math/StateSpaceUtil.java rename to wpimath/src/main/java/edu/wpi/first/math/math/StateSpaceUtil.java index 827927a598..3739482d9a 100644 --- a/wpimath/src/main/java/edu/wpi/first/wpilibj/math/StateSpaceUtil.java +++ b/wpimath/src/main/java/edu/wpi/first/math/math/StateSpaceUtil.java @@ -2,18 +2,18 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.first.wpilibj.math; +package edu.wpi.first.math.math; +import edu.wpi.first.math.MathUtil; +import edu.wpi.first.math.Matrix; +import edu.wpi.first.math.Nat; +import edu.wpi.first.math.Num; +import edu.wpi.first.math.VecBuilder; import edu.wpi.first.math.WPIMathJNI; -import edu.wpi.first.wpilibj.geometry.Pose2d; -import edu.wpi.first.wpiutil.math.MathUtil; -import edu.wpi.first.wpiutil.math.Matrix; -import edu.wpi.first.wpiutil.math.Nat; -import edu.wpi.first.wpiutil.math.Num; -import edu.wpi.first.wpiutil.math.VecBuilder; -import edu.wpi.first.wpiutil.math.numbers.N1; -import edu.wpi.first.wpiutil.math.numbers.N3; -import edu.wpi.first.wpiutil.math.numbers.N4; +import edu.wpi.first.math.geometry.Pose2d; +import edu.wpi.first.math.numbers.N1; +import edu.wpi.first.math.numbers.N3; +import edu.wpi.first.math.numbers.N4; import java.util.Random; import org.ejml.simple.SimpleMatrix; diff --git a/wpimath/src/main/java/edu/wpi/first/wpilibj/spline/CubicHermiteSpline.java b/wpimath/src/main/java/edu/wpi/first/math/spline/CubicHermiteSpline.java similarity index 99% rename from wpimath/src/main/java/edu/wpi/first/wpilibj/spline/CubicHermiteSpline.java rename to wpimath/src/main/java/edu/wpi/first/math/spline/CubicHermiteSpline.java index 44b549723e..9bbeaf6032 100644 --- a/wpimath/src/main/java/edu/wpi/first/wpilibj/spline/CubicHermiteSpline.java +++ b/wpimath/src/main/java/edu/wpi/first/math/spline/CubicHermiteSpline.java @@ -2,7 +2,7 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.first.wpilibj.spline; +package edu.wpi.first.math.spline; import org.ejml.simple.SimpleMatrix; diff --git a/wpimath/src/main/java/edu/wpi/first/wpilibj/spline/PoseWithCurvature.java b/wpimath/src/main/java/edu/wpi/first/math/spline/PoseWithCurvature.java similarity index 91% rename from wpimath/src/main/java/edu/wpi/first/wpilibj/spline/PoseWithCurvature.java rename to wpimath/src/main/java/edu/wpi/first/math/spline/PoseWithCurvature.java index 1a1ce62be3..8bad7b159b 100644 --- a/wpimath/src/main/java/edu/wpi/first/wpilibj/spline/PoseWithCurvature.java +++ b/wpimath/src/main/java/edu/wpi/first/math/spline/PoseWithCurvature.java @@ -2,9 +2,9 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.first.wpilibj.spline; +package edu.wpi.first.math.spline; -import edu.wpi.first.wpilibj.geometry.Pose2d; +import edu.wpi.first.math.geometry.Pose2d; /** Represents a pair of a pose and a curvature. */ @SuppressWarnings("MemberName") diff --git a/wpimath/src/main/java/edu/wpi/first/wpilibj/spline/QuinticHermiteSpline.java b/wpimath/src/main/java/edu/wpi/first/math/spline/QuinticHermiteSpline.java similarity index 99% rename from wpimath/src/main/java/edu/wpi/first/wpilibj/spline/QuinticHermiteSpline.java rename to wpimath/src/main/java/edu/wpi/first/math/spline/QuinticHermiteSpline.java index d189a4fdf4..40170443f6 100644 --- a/wpimath/src/main/java/edu/wpi/first/wpilibj/spline/QuinticHermiteSpline.java +++ b/wpimath/src/main/java/edu/wpi/first/math/spline/QuinticHermiteSpline.java @@ -2,7 +2,7 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.first.wpilibj.spline; +package edu.wpi.first.math.spline; import org.ejml.simple.SimpleMatrix; diff --git a/wpimath/src/main/java/edu/wpi/first/wpilibj/spline/Spline.java b/wpimath/src/main/java/edu/wpi/first/math/spline/Spline.java similarity index 95% rename from wpimath/src/main/java/edu/wpi/first/wpilibj/spline/Spline.java rename to wpimath/src/main/java/edu/wpi/first/math/spline/Spline.java index 495d463781..5451eea46b 100644 --- a/wpimath/src/main/java/edu/wpi/first/wpilibj/spline/Spline.java +++ b/wpimath/src/main/java/edu/wpi/first/math/spline/Spline.java @@ -2,10 +2,10 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.first.wpilibj.spline; +package edu.wpi.first.math.spline; -import edu.wpi.first.wpilibj.geometry.Pose2d; -import edu.wpi.first.wpilibj.geometry.Rotation2d; +import edu.wpi.first.math.geometry.Pose2d; +import edu.wpi.first.math.geometry.Rotation2d; import java.util.Arrays; import org.ejml.simple.SimpleMatrix; diff --git a/wpimath/src/main/java/edu/wpi/first/wpilibj/spline/SplineHelper.java b/wpimath/src/main/java/edu/wpi/first/math/spline/SplineHelper.java similarity index 98% rename from wpimath/src/main/java/edu/wpi/first/wpilibj/spline/SplineHelper.java rename to wpimath/src/main/java/edu/wpi/first/math/spline/SplineHelper.java index af9ee84ee3..ed023cc181 100644 --- a/wpimath/src/main/java/edu/wpi/first/wpilibj/spline/SplineHelper.java +++ b/wpimath/src/main/java/edu/wpi/first/math/spline/SplineHelper.java @@ -2,10 +2,10 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.first.wpilibj.spline; +package edu.wpi.first.math.spline; -import edu.wpi.first.wpilibj.geometry.Pose2d; -import edu.wpi.first.wpilibj.geometry.Translation2d; +import edu.wpi.first.math.geometry.Pose2d; +import edu.wpi.first.math.geometry.Translation2d; import java.util.Arrays; import java.util.List; diff --git a/wpimath/src/main/java/edu/wpi/first/wpilibj/spline/SplineParameterizer.java b/wpimath/src/main/java/edu/wpi/first/math/spline/SplineParameterizer.java similarity index 99% rename from wpimath/src/main/java/edu/wpi/first/wpilibj/spline/SplineParameterizer.java rename to wpimath/src/main/java/edu/wpi/first/math/spline/SplineParameterizer.java index 23675bcb08..171f0c4486 100644 --- a/wpimath/src/main/java/edu/wpi/first/wpilibj/spline/SplineParameterizer.java +++ b/wpimath/src/main/java/edu/wpi/first/math/spline/SplineParameterizer.java @@ -26,7 +26,7 @@ * SOFTWARE. */ -package edu.wpi.first.wpilibj.spline; +package edu.wpi.first.math.spline; import java.util.ArrayDeque; import java.util.ArrayList; diff --git a/wpimath/src/main/java/edu/wpi/first/wpilibj/system/LinearSystem.java b/wpimath/src/main/java/edu/wpi/first/math/system/LinearSystem.java similarity index 95% rename from wpimath/src/main/java/edu/wpi/first/wpilibj/system/LinearSystem.java rename to wpimath/src/main/java/edu/wpi/first/math/system/LinearSystem.java index bd20199b85..395f2bb53a 100644 --- a/wpimath/src/main/java/edu/wpi/first/wpilibj/system/LinearSystem.java +++ b/wpimath/src/main/java/edu/wpi/first/math/system/LinearSystem.java @@ -2,12 +2,12 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.first.wpilibj.system; +package edu.wpi.first.math.system; -import edu.wpi.first.wpilibj.math.Discretization; -import edu.wpi.first.wpiutil.math.Matrix; -import edu.wpi.first.wpiutil.math.Num; -import edu.wpi.first.wpiutil.math.numbers.N1; +import edu.wpi.first.math.Matrix; +import edu.wpi.first.math.Num; +import edu.wpi.first.math.math.Discretization; +import edu.wpi.first.math.numbers.N1; @SuppressWarnings("ClassTypeParameterName") public class LinearSystem { diff --git a/wpimath/src/main/java/edu/wpi/first/wpilibj/system/LinearSystemLoop.java b/wpimath/src/main/java/edu/wpi/first/math/system/LinearSystemLoop.java similarity index 96% rename from wpimath/src/main/java/edu/wpi/first/wpilibj/system/LinearSystemLoop.java rename to wpimath/src/main/java/edu/wpi/first/math/system/LinearSystemLoop.java index a40a63f4e4..ea67468cbb 100644 --- a/wpimath/src/main/java/edu/wpi/first/wpilibj/system/LinearSystemLoop.java +++ b/wpimath/src/main/java/edu/wpi/first/math/system/LinearSystemLoop.java @@ -2,15 +2,15 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.first.wpilibj.system; +package edu.wpi.first.math.system; -import edu.wpi.first.wpilibj.controller.LinearPlantInversionFeedforward; -import edu.wpi.first.wpilibj.controller.LinearQuadraticRegulator; -import edu.wpi.first.wpilibj.estimator.KalmanFilter; -import edu.wpi.first.wpilibj.math.StateSpaceUtil; -import edu.wpi.first.wpiutil.math.Matrix; -import edu.wpi.first.wpiutil.math.Num; -import edu.wpi.first.wpiutil.math.numbers.N1; +import edu.wpi.first.math.Matrix; +import edu.wpi.first.math.Num; +import edu.wpi.first.math.controller.LinearPlantInversionFeedforward; +import edu.wpi.first.math.controller.LinearQuadraticRegulator; +import edu.wpi.first.math.estimator.KalmanFilter; +import edu.wpi.first.math.math.StateSpaceUtil; +import edu.wpi.first.math.numbers.N1; import java.util.function.Function; import org.ejml.MatrixDimensionException; import org.ejml.simple.SimpleMatrix; diff --git a/wpimath/src/main/java/edu/wpi/first/wpilibj/system/NumericalIntegration.java b/wpimath/src/main/java/edu/wpi/first/math/system/NumericalIntegration.java similarity index 97% rename from wpimath/src/main/java/edu/wpi/first/wpilibj/system/NumericalIntegration.java rename to wpimath/src/main/java/edu/wpi/first/math/system/NumericalIntegration.java index 5b52ee47fb..608c2f6e22 100644 --- a/wpimath/src/main/java/edu/wpi/first/wpilibj/system/NumericalIntegration.java +++ b/wpimath/src/main/java/edu/wpi/first/math/system/NumericalIntegration.java @@ -2,15 +2,15 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.first.wpilibj.system; +package edu.wpi.first.math.system; -import edu.wpi.first.wpiutil.math.Matrix; -import edu.wpi.first.wpiutil.math.Nat; -import edu.wpi.first.wpiutil.math.Num; -import edu.wpi.first.wpiutil.math.Pair; -import edu.wpi.first.wpiutil.math.numbers.N1; -import edu.wpi.first.wpiutil.math.numbers.N5; -import edu.wpi.first.wpiutil.math.numbers.N6; +import edu.wpi.first.math.Matrix; +import edu.wpi.first.math.Nat; +import edu.wpi.first.math.Num; +import edu.wpi.first.math.Pair; +import edu.wpi.first.math.numbers.N1; +import edu.wpi.first.math.numbers.N5; +import edu.wpi.first.math.numbers.N6; import java.util.function.BiFunction; import java.util.function.DoubleFunction; import java.util.function.Function; diff --git a/wpimath/src/main/java/edu/wpi/first/wpilibj/system/NumericalJacobian.java b/wpimath/src/main/java/edu/wpi/first/math/system/NumericalJacobian.java similarity index 95% rename from wpimath/src/main/java/edu/wpi/first/wpilibj/system/NumericalJacobian.java rename to wpimath/src/main/java/edu/wpi/first/math/system/NumericalJacobian.java index 97ef58ba09..6c2c896edb 100644 --- a/wpimath/src/main/java/edu/wpi/first/wpilibj/system/NumericalJacobian.java +++ b/wpimath/src/main/java/edu/wpi/first/math/system/NumericalJacobian.java @@ -2,12 +2,12 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.first.wpilibj.system; +package edu.wpi.first.math.system; -import edu.wpi.first.wpiutil.math.Matrix; -import edu.wpi.first.wpiutil.math.Nat; -import edu.wpi.first.wpiutil.math.Num; -import edu.wpi.first.wpiutil.math.numbers.N1; +import edu.wpi.first.math.Matrix; +import edu.wpi.first.math.Nat; +import edu.wpi.first.math.Num; +import edu.wpi.first.math.numbers.N1; import java.util.function.BiFunction; import java.util.function.Function; diff --git a/wpimath/src/main/java/edu/wpi/first/wpilibj/system/plant/DCMotor.java b/wpimath/src/main/java/edu/wpi/first/math/system/plant/DCMotor.java similarity index 98% rename from wpimath/src/main/java/edu/wpi/first/wpilibj/system/plant/DCMotor.java rename to wpimath/src/main/java/edu/wpi/first/math/system/plant/DCMotor.java index 49680b355a..7cd34ae87a 100644 --- a/wpimath/src/main/java/edu/wpi/first/wpilibj/system/plant/DCMotor.java +++ b/wpimath/src/main/java/edu/wpi/first/math/system/plant/DCMotor.java @@ -2,9 +2,9 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.first.wpilibj.system.plant; +package edu.wpi.first.math.system.plant; -import edu.wpi.first.wpilibj.util.Units; +import edu.wpi.first.math.util.Units; /** Holds the constants for a DC motor. */ public class DCMotor { diff --git a/wpimath/src/main/java/edu/wpi/first/wpilibj/system/plant/LinearSystemId.java b/wpimath/src/main/java/edu/wpi/first/math/system/plant/LinearSystemId.java similarity index 95% rename from wpimath/src/main/java/edu/wpi/first/wpilibj/system/plant/LinearSystemId.java rename to wpimath/src/main/java/edu/wpi/first/math/system/plant/LinearSystemId.java index 01a5bf4ec9..fc517f4928 100644 --- a/wpimath/src/main/java/edu/wpi/first/wpilibj/system/plant/LinearSystemId.java +++ b/wpimath/src/main/java/edu/wpi/first/math/system/plant/LinearSystemId.java @@ -2,14 +2,14 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.first.wpilibj.system.plant; +package edu.wpi.first.math.system.plant; -import edu.wpi.first.wpilibj.system.LinearSystem; -import edu.wpi.first.wpiutil.math.Matrix; -import edu.wpi.first.wpiutil.math.Nat; -import edu.wpi.first.wpiutil.math.VecBuilder; -import edu.wpi.first.wpiutil.math.numbers.N1; -import edu.wpi.first.wpiutil.math.numbers.N2; +import edu.wpi.first.math.Matrix; +import edu.wpi.first.math.Nat; +import edu.wpi.first.math.VecBuilder; +import edu.wpi.first.math.numbers.N1; +import edu.wpi.first.math.numbers.N2; +import edu.wpi.first.math.system.LinearSystem; public final class LinearSystemId { private LinearSystemId() { @@ -137,7 +137,7 @@ public final class LinearSystemId { * inputs are [voltage], and outputs are [velocity]. * *

        The distance unit you choose MUST be an SI unit (i.e. meters or radians). You can use the - * {@link edu.wpi.first.wpilibj.util.Units} class for converting between unit types. + * {@link edu.wpi.first.math.util.Units} class for converting between unit types. * * @param kV The velocity gain, in volts per (units per second) * @param kA The acceleration gain, in volts per (units per second squared) @@ -160,7 +160,7 @@ public final class LinearSystemId { * velocity]^T, inputs are [voltage], and outputs are [position]. * *

        The distance unit you choose MUST be an SI unit (i.e. meters or radians). You can use the - * {@link edu.wpi.first.wpilibj.util.Units} class for converting between unit types. + * {@link edu.wpi.first.math.util.Units} class for converting between unit types. * * @param kV The velocity gain, in volts per (units per second) * @param kA The acceleration gain, in volts per (units per second squared) diff --git a/wpimath/src/main/java/edu/wpi/first/wpilibj/trajectory/Trajectory.java b/wpimath/src/main/java/edu/wpi/first/math/trajectory/Trajectory.java similarity index 99% rename from wpimath/src/main/java/edu/wpi/first/wpilibj/trajectory/Trajectory.java rename to wpimath/src/main/java/edu/wpi/first/math/trajectory/Trajectory.java index 15b4119aa5..5c1f67663b 100644 --- a/wpimath/src/main/java/edu/wpi/first/wpilibj/trajectory/Trajectory.java +++ b/wpimath/src/main/java/edu/wpi/first/math/trajectory/Trajectory.java @@ -2,11 +2,11 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.first.wpilibj.trajectory; +package edu.wpi.first.math.trajectory; import com.fasterxml.jackson.annotation.JsonProperty; -import edu.wpi.first.wpilibj.geometry.Pose2d; -import edu.wpi.first.wpilibj.geometry.Transform2d; +import edu.wpi.first.math.geometry.Pose2d; +import edu.wpi.first.math.geometry.Transform2d; import java.util.ArrayList; import java.util.List; import java.util.Objects; diff --git a/wpimath/src/main/java/edu/wpi/first/wpilibj/trajectory/TrajectoryConfig.java b/wpimath/src/main/java/edu/wpi/first/math/trajectory/TrajectoryConfig.java similarity index 90% rename from wpimath/src/main/java/edu/wpi/first/wpilibj/trajectory/TrajectoryConfig.java rename to wpimath/src/main/java/edu/wpi/first/math/trajectory/TrajectoryConfig.java index 9cf359a2e9..fbf734fd31 100644 --- a/wpimath/src/main/java/edu/wpi/first/wpilibj/trajectory/TrajectoryConfig.java +++ b/wpimath/src/main/java/edu/wpi/first/math/trajectory/TrajectoryConfig.java @@ -2,15 +2,15 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.first.wpilibj.trajectory; +package edu.wpi.first.math.trajectory; -import edu.wpi.first.wpilibj.kinematics.DifferentialDriveKinematics; -import edu.wpi.first.wpilibj.kinematics.MecanumDriveKinematics; -import edu.wpi.first.wpilibj.kinematics.SwerveDriveKinematics; -import edu.wpi.first.wpilibj.trajectory.constraint.DifferentialDriveKinematicsConstraint; -import edu.wpi.first.wpilibj.trajectory.constraint.MecanumDriveKinematicsConstraint; -import edu.wpi.first.wpilibj.trajectory.constraint.SwerveDriveKinematicsConstraint; -import edu.wpi.first.wpilibj.trajectory.constraint.TrajectoryConstraint; +import edu.wpi.first.math.kinematics.DifferentialDriveKinematics; +import edu.wpi.first.math.kinematics.MecanumDriveKinematics; +import edu.wpi.first.math.kinematics.SwerveDriveKinematics; +import edu.wpi.first.math.trajectory.constraint.DifferentialDriveKinematicsConstraint; +import edu.wpi.first.math.trajectory.constraint.MecanumDriveKinematicsConstraint; +import edu.wpi.first.math.trajectory.constraint.SwerveDriveKinematicsConstraint; +import edu.wpi.first.math.trajectory.constraint.TrajectoryConstraint; import java.util.ArrayList; import java.util.List; diff --git a/wpimath/src/main/java/edu/wpi/first/wpilibj/trajectory/TrajectoryGenerator.java b/wpimath/src/main/java/edu/wpi/first/math/trajectory/TrajectoryGenerator.java similarity index 94% rename from wpimath/src/main/java/edu/wpi/first/wpilibj/trajectory/TrajectoryGenerator.java rename to wpimath/src/main/java/edu/wpi/first/math/trajectory/TrajectoryGenerator.java index b20b866676..e50c0020f6 100644 --- a/wpimath/src/main/java/edu/wpi/first/wpilibj/trajectory/TrajectoryGenerator.java +++ b/wpimath/src/main/java/edu/wpi/first/math/trajectory/TrajectoryGenerator.java @@ -2,18 +2,18 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.first.wpilibj.trajectory; +package edu.wpi.first.math.trajectory; import edu.wpi.first.math.MathSharedStore; -import edu.wpi.first.wpilibj.geometry.Pose2d; -import edu.wpi.first.wpilibj.geometry.Rotation2d; -import edu.wpi.first.wpilibj.geometry.Transform2d; -import edu.wpi.first.wpilibj.geometry.Translation2d; -import edu.wpi.first.wpilibj.spline.PoseWithCurvature; -import edu.wpi.first.wpilibj.spline.Spline; -import edu.wpi.first.wpilibj.spline.SplineHelper; -import edu.wpi.first.wpilibj.spline.SplineParameterizer; -import edu.wpi.first.wpilibj.spline.SplineParameterizer.MalformedSplineException; +import edu.wpi.first.math.geometry.Pose2d; +import edu.wpi.first.math.geometry.Rotation2d; +import edu.wpi.first.math.geometry.Transform2d; +import edu.wpi.first.math.geometry.Translation2d; +import edu.wpi.first.math.spline.PoseWithCurvature; +import edu.wpi.first.math.spline.Spline; +import edu.wpi.first.math.spline.SplineHelper; +import edu.wpi.first.math.spline.SplineParameterizer; +import edu.wpi.first.math.spline.SplineParameterizer.MalformedSplineException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; diff --git a/wpimath/src/main/java/edu/wpi/first/wpilibj/trajectory/TrajectoryParameterizer.java b/wpimath/src/main/java/edu/wpi/first/math/trajectory/TrajectoryParameterizer.java similarity index 98% rename from wpimath/src/main/java/edu/wpi/first/wpilibj/trajectory/TrajectoryParameterizer.java rename to wpimath/src/main/java/edu/wpi/first/math/trajectory/TrajectoryParameterizer.java index cfad464295..eb1a645ea4 100644 --- a/wpimath/src/main/java/edu/wpi/first/wpilibj/trajectory/TrajectoryParameterizer.java +++ b/wpimath/src/main/java/edu/wpi/first/math/trajectory/TrajectoryParameterizer.java @@ -26,10 +26,10 @@ * SOFTWARE. */ -package edu.wpi.first.wpilibj.trajectory; +package edu.wpi.first.math.trajectory; -import edu.wpi.first.wpilibj.spline.PoseWithCurvature; -import edu.wpi.first.wpilibj.trajectory.constraint.TrajectoryConstraint; +import edu.wpi.first.math.spline.PoseWithCurvature; +import edu.wpi.first.math.trajectory.constraint.TrajectoryConstraint; import java.util.ArrayList; import java.util.List; diff --git a/wpimath/src/main/java/edu/wpi/first/wpilibj/trajectory/TrajectoryUtil.java b/wpimath/src/main/java/edu/wpi/first/math/trajectory/TrajectoryUtil.java similarity index 96% rename from wpimath/src/main/java/edu/wpi/first/wpilibj/trajectory/TrajectoryUtil.java rename to wpimath/src/main/java/edu/wpi/first/math/trajectory/TrajectoryUtil.java index b9b14fd826..953245aecc 100644 --- a/wpimath/src/main/java/edu/wpi/first/wpilibj/trajectory/TrajectoryUtil.java +++ b/wpimath/src/main/java/edu/wpi/first/math/trajectory/TrajectoryUtil.java @@ -2,11 +2,11 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.first.wpilibj.trajectory; +package edu.wpi.first.math.trajectory; import edu.wpi.first.math.WPIMathJNI; -import edu.wpi.first.wpilibj.geometry.Pose2d; -import edu.wpi.first.wpilibj.geometry.Rotation2d; +import edu.wpi.first.math.geometry.Pose2d; +import edu.wpi.first.math.geometry.Rotation2d; import java.io.IOException; import java.nio.file.Path; import java.util.ArrayList; diff --git a/wpimath/src/main/java/edu/wpi/first/wpilibj/trajectory/TrapezoidProfile.java b/wpimath/src/main/java/edu/wpi/first/math/trajectory/TrapezoidProfile.java similarity index 99% rename from wpimath/src/main/java/edu/wpi/first/wpilibj/trajectory/TrapezoidProfile.java rename to wpimath/src/main/java/edu/wpi/first/math/trajectory/TrapezoidProfile.java index f9ddb8a765..e0e464a215 100644 --- a/wpimath/src/main/java/edu/wpi/first/wpilibj/trajectory/TrapezoidProfile.java +++ b/wpimath/src/main/java/edu/wpi/first/math/trajectory/TrapezoidProfile.java @@ -2,7 +2,7 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.first.wpilibj.trajectory; +package edu.wpi.first.math.trajectory; import edu.wpi.first.math.MathSharedStore; import edu.wpi.first.math.MathUsageId; diff --git a/wpimath/src/main/java/edu/wpi/first/wpilibj/trajectory/constraint/CentripetalAccelerationConstraint.java b/wpimath/src/main/java/edu/wpi/first/math/trajectory/constraint/CentripetalAccelerationConstraint.java similarity index 96% rename from wpimath/src/main/java/edu/wpi/first/wpilibj/trajectory/constraint/CentripetalAccelerationConstraint.java rename to wpimath/src/main/java/edu/wpi/first/math/trajectory/constraint/CentripetalAccelerationConstraint.java index 7b1ae798b3..13138e84ce 100644 --- a/wpimath/src/main/java/edu/wpi/first/wpilibj/trajectory/constraint/CentripetalAccelerationConstraint.java +++ b/wpimath/src/main/java/edu/wpi/first/math/trajectory/constraint/CentripetalAccelerationConstraint.java @@ -2,9 +2,9 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.first.wpilibj.trajectory.constraint; +package edu.wpi.first.math.trajectory.constraint; -import edu.wpi.first.wpilibj.geometry.Pose2d; +import edu.wpi.first.math.geometry.Pose2d; /** * A constraint on the maximum absolute centripetal acceleration allowed when traversing a diff --git a/wpimath/src/main/java/edu/wpi/first/wpilibj/trajectory/constraint/DifferentialDriveKinematicsConstraint.java b/wpimath/src/main/java/edu/wpi/first/math/trajectory/constraint/DifferentialDriveKinematicsConstraint.java similarity index 92% rename from wpimath/src/main/java/edu/wpi/first/wpilibj/trajectory/constraint/DifferentialDriveKinematicsConstraint.java rename to wpimath/src/main/java/edu/wpi/first/math/trajectory/constraint/DifferentialDriveKinematicsConstraint.java index 11c99ce07b..37d8d68bbe 100644 --- a/wpimath/src/main/java/edu/wpi/first/wpilibj/trajectory/constraint/DifferentialDriveKinematicsConstraint.java +++ b/wpimath/src/main/java/edu/wpi/first/math/trajectory/constraint/DifferentialDriveKinematicsConstraint.java @@ -2,11 +2,11 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.first.wpilibj.trajectory.constraint; +package edu.wpi.first.math.trajectory.constraint; -import edu.wpi.first.wpilibj.geometry.Pose2d; -import edu.wpi.first.wpilibj.kinematics.ChassisSpeeds; -import edu.wpi.first.wpilibj.kinematics.DifferentialDriveKinematics; +import edu.wpi.first.math.geometry.Pose2d; +import edu.wpi.first.math.kinematics.ChassisSpeeds; +import edu.wpi.first.math.kinematics.DifferentialDriveKinematics; /** * A class that enforces constraints on the differential drive kinematics. This can be used to diff --git a/wpimath/src/main/java/edu/wpi/first/wpilibj/trajectory/constraint/DifferentialDriveVoltageConstraint.java b/wpimath/src/main/java/edu/wpi/first/math/trajectory/constraint/DifferentialDriveVoltageConstraint.java similarity index 94% rename from wpimath/src/main/java/edu/wpi/first/wpilibj/trajectory/constraint/DifferentialDriveVoltageConstraint.java rename to wpimath/src/main/java/edu/wpi/first/math/trajectory/constraint/DifferentialDriveVoltageConstraint.java index afc7cd4e1f..0cfaf9631d 100644 --- a/wpimath/src/main/java/edu/wpi/first/wpilibj/trajectory/constraint/DifferentialDriveVoltageConstraint.java +++ b/wpimath/src/main/java/edu/wpi/first/math/trajectory/constraint/DifferentialDriveVoltageConstraint.java @@ -2,14 +2,14 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.first.wpilibj.trajectory.constraint; +package edu.wpi.first.math.trajectory.constraint; import static edu.wpi.first.wpiutil.ErrorMessages.requireNonNullParam; -import edu.wpi.first.wpilibj.controller.SimpleMotorFeedforward; -import edu.wpi.first.wpilibj.geometry.Pose2d; -import edu.wpi.first.wpilibj.kinematics.ChassisSpeeds; -import edu.wpi.first.wpilibj.kinematics.DifferentialDriveKinematics; +import edu.wpi.first.math.controller.SimpleMotorFeedforward; +import edu.wpi.first.math.geometry.Pose2d; +import edu.wpi.first.math.kinematics.ChassisSpeeds; +import edu.wpi.first.math.kinematics.DifferentialDriveKinematics; /** * A class that enforces constraints on differential drive voltage expenditure based on the motor diff --git a/wpimath/src/main/java/edu/wpi/first/wpilibj/trajectory/constraint/EllipticalRegionConstraint.java b/wpimath/src/main/java/edu/wpi/first/math/trajectory/constraint/EllipticalRegionConstraint.java similarity index 93% rename from wpimath/src/main/java/edu/wpi/first/wpilibj/trajectory/constraint/EllipticalRegionConstraint.java rename to wpimath/src/main/java/edu/wpi/first/math/trajectory/constraint/EllipticalRegionConstraint.java index 7ec8cee6ba..c3bd226c2f 100644 --- a/wpimath/src/main/java/edu/wpi/first/wpilibj/trajectory/constraint/EllipticalRegionConstraint.java +++ b/wpimath/src/main/java/edu/wpi/first/math/trajectory/constraint/EllipticalRegionConstraint.java @@ -2,11 +2,11 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.first.wpilibj.trajectory.constraint; +package edu.wpi.first.math.trajectory.constraint; -import edu.wpi.first.wpilibj.geometry.Pose2d; -import edu.wpi.first.wpilibj.geometry.Rotation2d; -import edu.wpi.first.wpilibj.geometry.Translation2d; +import edu.wpi.first.math.geometry.Pose2d; +import edu.wpi.first.math.geometry.Rotation2d; +import edu.wpi.first.math.geometry.Translation2d; /** Enforces a particular constraint only within an elliptical region. */ public class EllipticalRegionConstraint implements TrajectoryConstraint { diff --git a/wpimath/src/main/java/edu/wpi/first/wpilibj/trajectory/constraint/MaxVelocityConstraint.java b/wpimath/src/main/java/edu/wpi/first/math/trajectory/constraint/MaxVelocityConstraint.java similarity index 92% rename from wpimath/src/main/java/edu/wpi/first/wpilibj/trajectory/constraint/MaxVelocityConstraint.java rename to wpimath/src/main/java/edu/wpi/first/math/trajectory/constraint/MaxVelocityConstraint.java index c755aead34..d672295c6d 100644 --- a/wpimath/src/main/java/edu/wpi/first/wpilibj/trajectory/constraint/MaxVelocityConstraint.java +++ b/wpimath/src/main/java/edu/wpi/first/math/trajectory/constraint/MaxVelocityConstraint.java @@ -2,9 +2,9 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.first.wpilibj.trajectory.constraint; +package edu.wpi.first.math.trajectory.constraint; -import edu.wpi.first.wpilibj.geometry.Pose2d; +import edu.wpi.first.math.geometry.Pose2d; /** * Represents a constraint that enforces a max velocity. This can be composed with the {@link diff --git a/wpimath/src/main/java/edu/wpi/first/wpilibj/trajectory/constraint/MecanumDriveKinematicsConstraint.java b/wpimath/src/main/java/edu/wpi/first/math/trajectory/constraint/MecanumDriveKinematicsConstraint.java similarity index 93% rename from wpimath/src/main/java/edu/wpi/first/wpilibj/trajectory/constraint/MecanumDriveKinematicsConstraint.java rename to wpimath/src/main/java/edu/wpi/first/math/trajectory/constraint/MecanumDriveKinematicsConstraint.java index 80f5c732ee..9cb980a9aa 100644 --- a/wpimath/src/main/java/edu/wpi/first/wpilibj/trajectory/constraint/MecanumDriveKinematicsConstraint.java +++ b/wpimath/src/main/java/edu/wpi/first/math/trajectory/constraint/MecanumDriveKinematicsConstraint.java @@ -2,11 +2,11 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.first.wpilibj.trajectory.constraint; +package edu.wpi.first.math.trajectory.constraint; -import edu.wpi.first.wpilibj.geometry.Pose2d; -import edu.wpi.first.wpilibj.kinematics.ChassisSpeeds; -import edu.wpi.first.wpilibj.kinematics.MecanumDriveKinematics; +import edu.wpi.first.math.geometry.Pose2d; +import edu.wpi.first.math.kinematics.ChassisSpeeds; +import edu.wpi.first.math.kinematics.MecanumDriveKinematics; /** * A class that enforces constraints on the mecanum drive kinematics. This can be used to ensure diff --git a/wpimath/src/main/java/edu/wpi/first/wpilibj/trajectory/constraint/RectangularRegionConstraint.java b/wpimath/src/main/java/edu/wpi/first/math/trajectory/constraint/RectangularRegionConstraint.java similarity index 94% rename from wpimath/src/main/java/edu/wpi/first/wpilibj/trajectory/constraint/RectangularRegionConstraint.java rename to wpimath/src/main/java/edu/wpi/first/math/trajectory/constraint/RectangularRegionConstraint.java index 0827d991da..b29df5e9ae 100644 --- a/wpimath/src/main/java/edu/wpi/first/wpilibj/trajectory/constraint/RectangularRegionConstraint.java +++ b/wpimath/src/main/java/edu/wpi/first/math/trajectory/constraint/RectangularRegionConstraint.java @@ -2,10 +2,10 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.first.wpilibj.trajectory.constraint; +package edu.wpi.first.math.trajectory.constraint; -import edu.wpi.first.wpilibj.geometry.Pose2d; -import edu.wpi.first.wpilibj.geometry.Translation2d; +import edu.wpi.first.math.geometry.Pose2d; +import edu.wpi.first.math.geometry.Translation2d; /** Enforces a particular constraint only within a rectangular region. */ public class RectangularRegionConstraint implements TrajectoryConstraint { diff --git a/wpimath/src/main/java/edu/wpi/first/wpilibj/trajectory/constraint/SwerveDriveKinematicsConstraint.java b/wpimath/src/main/java/edu/wpi/first/math/trajectory/constraint/SwerveDriveKinematicsConstraint.java similarity index 93% rename from wpimath/src/main/java/edu/wpi/first/wpilibj/trajectory/constraint/SwerveDriveKinematicsConstraint.java rename to wpimath/src/main/java/edu/wpi/first/math/trajectory/constraint/SwerveDriveKinematicsConstraint.java index af61a44c7e..d87e3252cb 100644 --- a/wpimath/src/main/java/edu/wpi/first/wpilibj/trajectory/constraint/SwerveDriveKinematicsConstraint.java +++ b/wpimath/src/main/java/edu/wpi/first/math/trajectory/constraint/SwerveDriveKinematicsConstraint.java @@ -2,11 +2,11 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.first.wpilibj.trajectory.constraint; +package edu.wpi.first.math.trajectory.constraint; -import edu.wpi.first.wpilibj.geometry.Pose2d; -import edu.wpi.first.wpilibj.kinematics.ChassisSpeeds; -import edu.wpi.first.wpilibj.kinematics.SwerveDriveKinematics; +import edu.wpi.first.math.geometry.Pose2d; +import edu.wpi.first.math.kinematics.ChassisSpeeds; +import edu.wpi.first.math.kinematics.SwerveDriveKinematics; /** * A class that enforces constraints on the swerve drive kinematics. This can be used to ensure that diff --git a/wpimath/src/main/java/edu/wpi/first/wpilibj/trajectory/constraint/TrajectoryConstraint.java b/wpimath/src/main/java/edu/wpi/first/math/trajectory/constraint/TrajectoryConstraint.java similarity index 95% rename from wpimath/src/main/java/edu/wpi/first/wpilibj/trajectory/constraint/TrajectoryConstraint.java rename to wpimath/src/main/java/edu/wpi/first/math/trajectory/constraint/TrajectoryConstraint.java index 56275578d2..ed03a3185a 100644 --- a/wpimath/src/main/java/edu/wpi/first/wpilibj/trajectory/constraint/TrajectoryConstraint.java +++ b/wpimath/src/main/java/edu/wpi/first/math/trajectory/constraint/TrajectoryConstraint.java @@ -2,9 +2,9 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.first.wpilibj.trajectory.constraint; +package edu.wpi.first.math.trajectory.constraint; -import edu.wpi.first.wpilibj.geometry.Pose2d; +import edu.wpi.first.math.geometry.Pose2d; /** * An interface for defining user-defined velocity and acceleration constraints while generating diff --git a/wpimath/src/main/java/edu/wpi/first/wpilibj/util/Units.java b/wpimath/src/main/java/edu/wpi/first/math/util/Units.java similarity index 98% rename from wpimath/src/main/java/edu/wpi/first/wpilibj/util/Units.java rename to wpimath/src/main/java/edu/wpi/first/math/util/Units.java index 28774efa36..204a95a570 100644 --- a/wpimath/src/main/java/edu/wpi/first/wpilibj/util/Units.java +++ b/wpimath/src/main/java/edu/wpi/first/math/util/Units.java @@ -2,7 +2,7 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.first.wpilibj.util; +package edu.wpi.first.math.util; /** Utility class that converts between commonly used units in FRC. */ public final class Units { diff --git a/wpimath/src/test/java/edu/wpi/first/wpiutil/math/MathUtilTest.java b/wpimath/src/test/java/edu/wpi/first/math/MathUtilTest.java similarity index 98% rename from wpimath/src/test/java/edu/wpi/first/wpiutil/math/MathUtilTest.java rename to wpimath/src/test/java/edu/wpi/first/math/MathUtilTest.java index 6808b45fbb..af48bad955 100644 --- a/wpimath/src/test/java/edu/wpi/first/wpiutil/math/MathUtilTest.java +++ b/wpimath/src/test/java/edu/wpi/first/math/MathUtilTest.java @@ -2,7 +2,7 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.first.wpiutil.math; +package edu.wpi.first.math; import static org.junit.jupiter.api.Assertions.assertEquals; diff --git a/wpimath/src/test/java/edu/wpi/first/wpiutil/math/MatrixTest.java b/wpimath/src/test/java/edu/wpi/first/math/MatrixTest.java similarity index 94% rename from wpimath/src/test/java/edu/wpi/first/wpiutil/math/MatrixTest.java rename to wpimath/src/test/java/edu/wpi/first/math/MatrixTest.java index bfe382541d..45df8fe929 100644 --- a/wpimath/src/test/java/edu/wpi/first/wpiutil/math/MatrixTest.java +++ b/wpimath/src/test/java/edu/wpi/first/math/MatrixTest.java @@ -2,16 +2,16 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.first.wpiutil.math; +package edu.wpi.first.math; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; -import edu.wpi.first.wpiutil.math.numbers.N1; -import edu.wpi.first.wpiutil.math.numbers.N2; -import edu.wpi.first.wpiutil.math.numbers.N3; -import edu.wpi.first.wpiutil.math.numbers.N4; +import edu.wpi.first.math.numbers.N1; +import edu.wpi.first.math.numbers.N2; +import edu.wpi.first.math.numbers.N3; +import edu.wpi.first.math.numbers.N4; import org.ejml.data.SingularMatrixException; import org.junit.jupiter.api.Test; diff --git a/wpimath/src/test/java/edu/wpi/first/wpilibj/controller/ControlAffinePlantInversionFeedforwardTest.java b/wpimath/src/test/java/edu/wpi/first/math/controller/ControlAffinePlantInversionFeedforwardTest.java similarity index 86% rename from wpimath/src/test/java/edu/wpi/first/wpilibj/controller/ControlAffinePlantInversionFeedforwardTest.java rename to wpimath/src/test/java/edu/wpi/first/math/controller/ControlAffinePlantInversionFeedforwardTest.java index 7eeae12b45..350ef18c11 100644 --- a/wpimath/src/test/java/edu/wpi/first/wpilibj/controller/ControlAffinePlantInversionFeedforwardTest.java +++ b/wpimath/src/test/java/edu/wpi/first/math/controller/ControlAffinePlantInversionFeedforwardTest.java @@ -2,15 +2,15 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.first.wpilibj.controller; +package edu.wpi.first.math.controller; import static org.junit.jupiter.api.Assertions.assertEquals; -import edu.wpi.first.wpiutil.math.Matrix; -import edu.wpi.first.wpiutil.math.Nat; -import edu.wpi.first.wpiutil.math.VecBuilder; -import edu.wpi.first.wpiutil.math.numbers.N1; -import edu.wpi.first.wpiutil.math.numbers.N2; +import edu.wpi.first.math.Matrix; +import edu.wpi.first.math.Nat; +import edu.wpi.first.math.VecBuilder; +import edu.wpi.first.math.numbers.N1; +import edu.wpi.first.math.numbers.N2; import org.junit.jupiter.api.Test; class ControlAffinePlantInversionFeedforwardTest { diff --git a/wpimath/src/test/java/edu/wpi/first/wpilibj/controller/LinearPlantInversionFeedforwardTest.java b/wpimath/src/test/java/edu/wpi/first/math/controller/LinearPlantInversionFeedforwardTest.java similarity index 75% rename from wpimath/src/test/java/edu/wpi/first/wpilibj/controller/LinearPlantInversionFeedforwardTest.java rename to wpimath/src/test/java/edu/wpi/first/math/controller/LinearPlantInversionFeedforwardTest.java index 8695bbce70..98b0e6c661 100644 --- a/wpimath/src/test/java/edu/wpi/first/wpilibj/controller/LinearPlantInversionFeedforwardTest.java +++ b/wpimath/src/test/java/edu/wpi/first/math/controller/LinearPlantInversionFeedforwardTest.java @@ -2,15 +2,15 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.first.wpilibj.controller; +package edu.wpi.first.math.controller; import static org.junit.jupiter.api.Assertions.assertEquals; -import edu.wpi.first.wpiutil.math.Matrix; -import edu.wpi.first.wpiutil.math.Nat; -import edu.wpi.first.wpiutil.math.VecBuilder; -import edu.wpi.first.wpiutil.math.numbers.N1; -import edu.wpi.first.wpiutil.math.numbers.N2; +import edu.wpi.first.math.Matrix; +import edu.wpi.first.math.Nat; +import edu.wpi.first.math.VecBuilder; +import edu.wpi.first.math.numbers.N1; +import edu.wpi.first.math.numbers.N2; import org.junit.jupiter.api.Test; class LinearPlantInversionFeedforwardTest { diff --git a/wpimath/src/test/java/edu/wpi/first/wpilibj/controller/LinearQuadraticRegulatorTest.java b/wpimath/src/test/java/edu/wpi/first/math/controller/LinearQuadraticRegulatorTest.java similarity index 89% rename from wpimath/src/test/java/edu/wpi/first/wpilibj/controller/LinearQuadraticRegulatorTest.java rename to wpimath/src/test/java/edu/wpi/first/math/controller/LinearQuadraticRegulatorTest.java index 8c81fc717e..5658709382 100644 --- a/wpimath/src/test/java/edu/wpi/first/wpilibj/controller/LinearQuadraticRegulatorTest.java +++ b/wpimath/src/test/java/edu/wpi/first/math/controller/LinearQuadraticRegulatorTest.java @@ -2,16 +2,16 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.first.wpilibj.controller; +package edu.wpi.first.math.controller; import static org.junit.jupiter.api.Assertions.assertEquals; -import edu.wpi.first.wpilibj.system.LinearSystem; -import edu.wpi.first.wpilibj.system.plant.DCMotor; -import edu.wpi.first.wpilibj.system.plant.LinearSystemId; -import edu.wpi.first.wpiutil.math.VecBuilder; -import edu.wpi.first.wpiutil.math.numbers.N1; -import edu.wpi.first.wpiutil.math.numbers.N2; +import edu.wpi.first.math.VecBuilder; +import edu.wpi.first.math.numbers.N1; +import edu.wpi.first.math.numbers.N2; +import edu.wpi.first.math.system.LinearSystem; +import edu.wpi.first.math.system.plant.DCMotor; +import edu.wpi.first.math.system.plant.LinearSystemId; import org.junit.jupiter.api.Test; public class LinearQuadraticRegulatorTest { diff --git a/wpimath/src/test/java/edu/wpi/first/wpilibj/controller/LinearSystemLoopTest.java b/wpimath/src/test/java/edu/wpi/first/math/controller/LinearSystemLoopTest.java similarity index 89% rename from wpimath/src/test/java/edu/wpi/first/wpilibj/controller/LinearSystemLoopTest.java rename to wpimath/src/test/java/edu/wpi/first/math/controller/LinearSystemLoopTest.java index 8f9668f355..e0d8c14be6 100644 --- a/wpimath/src/test/java/edu/wpi/first/wpilibj/controller/LinearSystemLoopTest.java +++ b/wpimath/src/test/java/edu/wpi/first/math/controller/LinearSystemLoopTest.java @@ -2,22 +2,22 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.first.wpilibj.controller; +package edu.wpi.first.math.controller; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; -import edu.wpi.first.wpilibj.estimator.KalmanFilter; -import edu.wpi.first.wpilibj.system.LinearSystem; -import edu.wpi.first.wpilibj.system.LinearSystemLoop; -import edu.wpi.first.wpilibj.system.plant.DCMotor; -import edu.wpi.first.wpilibj.system.plant.LinearSystemId; -import edu.wpi.first.wpilibj.trajectory.TrapezoidProfile; -import edu.wpi.first.wpiutil.math.Matrix; -import edu.wpi.first.wpiutil.math.Nat; -import edu.wpi.first.wpiutil.math.VecBuilder; -import edu.wpi.first.wpiutil.math.numbers.N1; -import edu.wpi.first.wpiutil.math.numbers.N2; +import edu.wpi.first.math.Matrix; +import edu.wpi.first.math.Nat; +import edu.wpi.first.math.VecBuilder; +import edu.wpi.first.math.estimator.KalmanFilter; +import edu.wpi.first.math.numbers.N1; +import edu.wpi.first.math.numbers.N2; +import edu.wpi.first.math.system.LinearSystem; +import edu.wpi.first.math.system.LinearSystemLoop; +import edu.wpi.first.math.system.plant.DCMotor; +import edu.wpi.first.math.system.plant.LinearSystemId; +import edu.wpi.first.math.trajectory.TrapezoidProfile; import java.util.ArrayList; import java.util.List; import java.util.Random; diff --git a/wpilibj/src/test/java/edu/wpi/first/wpilibj/controller/RamseteControllerTest.java b/wpimath/src/test/java/edu/wpi/first/math/controller/RamseteControllerTest.java similarity index 86% rename from wpilibj/src/test/java/edu/wpi/first/wpilibj/controller/RamseteControllerTest.java rename to wpimath/src/test/java/edu/wpi/first/math/controller/RamseteControllerTest.java index 5800fdc0c1..58e7eb4c0b 100644 --- a/wpilibj/src/test/java/edu/wpi/first/wpilibj/controller/RamseteControllerTest.java +++ b/wpimath/src/test/java/edu/wpi/first/math/controller/RamseteControllerTest.java @@ -2,17 +2,17 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.first.wpilibj.controller; +package edu.wpi.first.math.controller; import static org.junit.jupiter.api.Assertions.assertAll; import static org.junit.jupiter.api.Assertions.assertEquals; -import edu.wpi.first.wpilibj.geometry.Pose2d; -import edu.wpi.first.wpilibj.geometry.Rotation2d; -import edu.wpi.first.wpilibj.geometry.Twist2d; -import edu.wpi.first.wpilibj.trajectory.TrajectoryConfig; -import edu.wpi.first.wpilibj.trajectory.TrajectoryGenerator; -import edu.wpi.first.wpiutil.math.MathUtil; +import edu.wpi.first.math.MathUtil; +import edu.wpi.first.math.geometry.Pose2d; +import edu.wpi.first.math.geometry.Rotation2d; +import edu.wpi.first.math.geometry.Twist2d; +import edu.wpi.first.math.trajectory.TrajectoryConfig; +import edu.wpi.first.math.trajectory.TrajectoryGenerator; import java.util.ArrayList; import org.junit.jupiter.api.Test; diff --git a/wpimath/src/test/java/edu/wpi/first/wpilibj/estimator/AngleStatisticsTest.java b/wpimath/src/test/java/edu/wpi/first/math/estimator/AngleStatisticsTest.java similarity index 89% rename from wpimath/src/test/java/edu/wpi/first/wpilibj/estimator/AngleStatisticsTest.java rename to wpimath/src/test/java/edu/wpi/first/math/estimator/AngleStatisticsTest.java index 0503ac865d..9fcf5e31c4 100644 --- a/wpimath/src/test/java/edu/wpi/first/wpilibj/estimator/AngleStatisticsTest.java +++ b/wpimath/src/test/java/edu/wpi/first/math/estimator/AngleStatisticsTest.java @@ -2,13 +2,13 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.first.wpilibj.estimator; +package edu.wpi.first.math.estimator; import static org.junit.jupiter.api.Assertions.assertTrue; -import edu.wpi.first.wpiutil.math.Matrix; -import edu.wpi.first.wpiutil.math.Nat; -import edu.wpi.first.wpiutil.math.VecBuilder; +import edu.wpi.first.math.Matrix; +import edu.wpi.first.math.Nat; +import edu.wpi.first.math.VecBuilder; import org.junit.jupiter.api.Test; public class AngleStatisticsTest { diff --git a/wpimath/src/test/java/edu/wpi/first/wpilibj/estimator/DifferentialDrivePoseEstimatorTest.java b/wpimath/src/test/java/edu/wpi/first/math/estimator/DifferentialDrivePoseEstimatorTest.java similarity index 85% rename from wpimath/src/test/java/edu/wpi/first/wpilibj/estimator/DifferentialDrivePoseEstimatorTest.java rename to wpimath/src/test/java/edu/wpi/first/math/estimator/DifferentialDrivePoseEstimatorTest.java index c800b65211..552ce788e4 100644 --- a/wpimath/src/test/java/edu/wpi/first/wpilibj/estimator/DifferentialDrivePoseEstimatorTest.java +++ b/wpimath/src/test/java/edu/wpi/first/math/estimator/DifferentialDrivePoseEstimatorTest.java @@ -2,21 +2,21 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.first.wpilibj.estimator; +package edu.wpi.first.math.estimator; import static org.junit.jupiter.api.Assertions.assertEquals; -import edu.wpi.first.wpilibj.geometry.Pose2d; -import edu.wpi.first.wpilibj.geometry.Rotation2d; -import edu.wpi.first.wpilibj.geometry.Translation2d; -import edu.wpi.first.wpilibj.kinematics.ChassisSpeeds; -import edu.wpi.first.wpilibj.kinematics.DifferentialDriveKinematics; -import edu.wpi.first.wpilibj.kinematics.DifferentialDriveWheelSpeeds; -import edu.wpi.first.wpilibj.trajectory.Trajectory; -import edu.wpi.first.wpilibj.trajectory.TrajectoryConfig; -import edu.wpi.first.wpilibj.trajectory.TrajectoryGenerator; -import edu.wpi.first.wpiutil.math.MatBuilder; -import edu.wpi.first.wpiutil.math.Nat; +import edu.wpi.first.math.MatBuilder; +import edu.wpi.first.math.Nat; +import edu.wpi.first.math.geometry.Pose2d; +import edu.wpi.first.math.geometry.Rotation2d; +import edu.wpi.first.math.geometry.Translation2d; +import edu.wpi.first.math.kinematics.ChassisSpeeds; +import edu.wpi.first.math.kinematics.DifferentialDriveKinematics; +import edu.wpi.first.math.kinematics.DifferentialDriveWheelSpeeds; +import edu.wpi.first.math.trajectory.Trajectory; +import edu.wpi.first.math.trajectory.TrajectoryConfig; +import edu.wpi.first.math.trajectory.TrajectoryGenerator; import java.util.List; import java.util.Random; import org.junit.jupiter.api.Test; diff --git a/wpimath/src/test/java/edu/wpi/first/wpilibj/estimator/ExtendedKalmanFilterTest.java b/wpimath/src/test/java/edu/wpi/first/math/estimator/ExtendedKalmanFilterTest.java similarity index 91% rename from wpimath/src/test/java/edu/wpi/first/wpilibj/estimator/ExtendedKalmanFilterTest.java rename to wpimath/src/test/java/edu/wpi/first/math/estimator/ExtendedKalmanFilterTest.java index 79810c8bff..f389c2fe5b 100644 --- a/wpimath/src/test/java/edu/wpi/first/wpilibj/estimator/ExtendedKalmanFilterTest.java +++ b/wpimath/src/test/java/edu/wpi/first/math/estimator/ExtendedKalmanFilterTest.java @@ -2,26 +2,26 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.first.wpilibj.estimator; +package edu.wpi.first.math.estimator; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertEquals; -import edu.wpi.first.wpilibj.geometry.Pose2d; -import edu.wpi.first.wpilibj.geometry.Rotation2d; -import edu.wpi.first.wpilibj.math.StateSpaceUtil; -import edu.wpi.first.wpilibj.system.NumericalIntegration; -import edu.wpi.first.wpilibj.system.NumericalJacobian; -import edu.wpi.first.wpilibj.system.plant.DCMotor; -import edu.wpi.first.wpilibj.trajectory.TrajectoryConfig; -import edu.wpi.first.wpilibj.trajectory.TrajectoryGenerator; -import edu.wpi.first.wpiutil.math.Matrix; -import edu.wpi.first.wpiutil.math.Nat; -import edu.wpi.first.wpiutil.math.VecBuilder; -import edu.wpi.first.wpiutil.math.numbers.N1; -import edu.wpi.first.wpiutil.math.numbers.N2; -import edu.wpi.first.wpiutil.math.numbers.N3; -import edu.wpi.first.wpiutil.math.numbers.N5; +import edu.wpi.first.math.Matrix; +import edu.wpi.first.math.Nat; +import edu.wpi.first.math.VecBuilder; +import edu.wpi.first.math.geometry.Pose2d; +import edu.wpi.first.math.geometry.Rotation2d; +import edu.wpi.first.math.math.StateSpaceUtil; +import edu.wpi.first.math.numbers.N1; +import edu.wpi.first.math.numbers.N2; +import edu.wpi.first.math.numbers.N3; +import edu.wpi.first.math.numbers.N5; +import edu.wpi.first.math.system.NumericalIntegration; +import edu.wpi.first.math.system.NumericalJacobian; +import edu.wpi.first.math.system.plant.DCMotor; +import edu.wpi.first.math.trajectory.TrajectoryConfig; +import edu.wpi.first.math.trajectory.TrajectoryGenerator; import java.util.ArrayList; import java.util.Arrays; import java.util.List; diff --git a/wpimath/src/test/java/edu/wpi/first/wpilibj/estimator/KalmanFilterTest.java b/wpimath/src/test/java/edu/wpi/first/math/estimator/KalmanFilterTest.java similarity index 91% rename from wpimath/src/test/java/edu/wpi/first/wpilibj/estimator/KalmanFilterTest.java rename to wpimath/src/test/java/edu/wpi/first/math/estimator/KalmanFilterTest.java index f497150455..7415e513bc 100644 --- a/wpimath/src/test/java/edu/wpi/first/wpilibj/estimator/KalmanFilterTest.java +++ b/wpimath/src/test/java/edu/wpi/first/math/estimator/KalmanFilterTest.java @@ -2,25 +2,25 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.first.wpilibj.estimator; +package edu.wpi.first.math.estimator; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertEquals; -import edu.wpi.first.wpilibj.geometry.Pose2d; -import edu.wpi.first.wpilibj.geometry.Rotation2d; -import edu.wpi.first.wpilibj.system.LinearSystem; -import edu.wpi.first.wpilibj.system.plant.DCMotor; -import edu.wpi.first.wpilibj.system.plant.LinearSystemId; -import edu.wpi.first.wpilibj.trajectory.TrajectoryConfig; -import edu.wpi.first.wpilibj.trajectory.TrajectoryGenerator; -import edu.wpi.first.wpiutil.math.Matrix; -import edu.wpi.first.wpiutil.math.Nat; -import edu.wpi.first.wpiutil.math.VecBuilder; -import edu.wpi.first.wpiutil.math.numbers.N1; -import edu.wpi.first.wpiutil.math.numbers.N2; -import edu.wpi.first.wpiutil.math.numbers.N3; -import edu.wpi.first.wpiutil.math.numbers.N6; +import edu.wpi.first.math.Matrix; +import edu.wpi.first.math.Nat; +import edu.wpi.first.math.VecBuilder; +import edu.wpi.first.math.geometry.Pose2d; +import edu.wpi.first.math.geometry.Rotation2d; +import edu.wpi.first.math.numbers.N1; +import edu.wpi.first.math.numbers.N2; +import edu.wpi.first.math.numbers.N3; +import edu.wpi.first.math.numbers.N6; +import edu.wpi.first.math.system.LinearSystem; +import edu.wpi.first.math.system.plant.DCMotor; +import edu.wpi.first.math.system.plant.LinearSystemId; +import edu.wpi.first.math.trajectory.TrajectoryConfig; +import edu.wpi.first.math.trajectory.TrajectoryGenerator; import java.util.ArrayList; import java.util.List; import java.util.Random; diff --git a/wpimath/src/test/java/edu/wpi/first/wpilibj/estimator/MecanumDrivePoseEstimatorTest.java b/wpimath/src/test/java/edu/wpi/first/math/estimator/MecanumDrivePoseEstimatorTest.java similarity index 88% rename from wpimath/src/test/java/edu/wpi/first/wpilibj/estimator/MecanumDrivePoseEstimatorTest.java rename to wpimath/src/test/java/edu/wpi/first/math/estimator/MecanumDrivePoseEstimatorTest.java index c59844dace..82845d7be0 100644 --- a/wpimath/src/test/java/edu/wpi/first/wpilibj/estimator/MecanumDrivePoseEstimatorTest.java +++ b/wpimath/src/test/java/edu/wpi/first/math/estimator/MecanumDrivePoseEstimatorTest.java @@ -2,18 +2,18 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.first.wpilibj.estimator; +package edu.wpi.first.math.estimator; import static org.junit.jupiter.api.Assertions.assertEquals; -import edu.wpi.first.wpilibj.geometry.Pose2d; -import edu.wpi.first.wpilibj.geometry.Rotation2d; -import edu.wpi.first.wpilibj.geometry.Translation2d; -import edu.wpi.first.wpilibj.kinematics.ChassisSpeeds; -import edu.wpi.first.wpilibj.kinematics.MecanumDriveKinematics; -import edu.wpi.first.wpilibj.trajectory.TrajectoryConfig; -import edu.wpi.first.wpilibj.trajectory.TrajectoryGenerator; -import edu.wpi.first.wpiutil.math.VecBuilder; +import edu.wpi.first.math.VecBuilder; +import edu.wpi.first.math.geometry.Pose2d; +import edu.wpi.first.math.geometry.Rotation2d; +import edu.wpi.first.math.geometry.Translation2d; +import edu.wpi.first.math.kinematics.ChassisSpeeds; +import edu.wpi.first.math.kinematics.MecanumDriveKinematics; +import edu.wpi.first.math.trajectory.TrajectoryConfig; +import edu.wpi.first.math.trajectory.TrajectoryGenerator; import java.util.List; import java.util.Random; import org.junit.jupiter.api.Test; diff --git a/wpimath/src/test/java/edu/wpi/first/wpilibj/estimator/MerweScaledSigmaPointsTest.java b/wpimath/src/test/java/edu/wpi/first/math/estimator/MerweScaledSigmaPointsTest.java similarity index 88% rename from wpimath/src/test/java/edu/wpi/first/wpilibj/estimator/MerweScaledSigmaPointsTest.java rename to wpimath/src/test/java/edu/wpi/first/math/estimator/MerweScaledSigmaPointsTest.java index 4462b7dc01..b6e32fc56b 100644 --- a/wpimath/src/test/java/edu/wpi/first/wpilibj/estimator/MerweScaledSigmaPointsTest.java +++ b/wpimath/src/test/java/edu/wpi/first/math/estimator/MerweScaledSigmaPointsTest.java @@ -2,13 +2,13 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.first.wpilibj.estimator; +package edu.wpi.first.math.estimator; import static org.junit.jupiter.api.Assertions.assertTrue; -import edu.wpi.first.wpiutil.math.Matrix; -import edu.wpi.first.wpiutil.math.Nat; -import edu.wpi.first.wpiutil.math.VecBuilder; +import edu.wpi.first.math.Matrix; +import edu.wpi.first.math.Nat; +import edu.wpi.first.math.VecBuilder; import org.junit.jupiter.api.Test; public class MerweScaledSigmaPointsTest { diff --git a/wpimath/src/test/java/edu/wpi/first/wpilibj/estimator/SwerveDrivePoseEstimatorTest.java b/wpimath/src/test/java/edu/wpi/first/math/estimator/SwerveDrivePoseEstimatorTest.java similarity index 88% rename from wpimath/src/test/java/edu/wpi/first/wpilibj/estimator/SwerveDrivePoseEstimatorTest.java rename to wpimath/src/test/java/edu/wpi/first/math/estimator/SwerveDrivePoseEstimatorTest.java index 8ed50c4e5c..52752bc849 100644 --- a/wpimath/src/test/java/edu/wpi/first/wpilibj/estimator/SwerveDrivePoseEstimatorTest.java +++ b/wpimath/src/test/java/edu/wpi/first/math/estimator/SwerveDrivePoseEstimatorTest.java @@ -2,18 +2,18 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.first.wpilibj.estimator; +package edu.wpi.first.math.estimator; import static org.junit.jupiter.api.Assertions.assertEquals; -import edu.wpi.first.wpilibj.geometry.Pose2d; -import edu.wpi.first.wpilibj.geometry.Rotation2d; -import edu.wpi.first.wpilibj.geometry.Translation2d; -import edu.wpi.first.wpilibj.kinematics.ChassisSpeeds; -import edu.wpi.first.wpilibj.kinematics.SwerveDriveKinematics; -import edu.wpi.first.wpilibj.trajectory.TrajectoryConfig; -import edu.wpi.first.wpilibj.trajectory.TrajectoryGenerator; -import edu.wpi.first.wpiutil.math.VecBuilder; +import edu.wpi.first.math.VecBuilder; +import edu.wpi.first.math.geometry.Pose2d; +import edu.wpi.first.math.geometry.Rotation2d; +import edu.wpi.first.math.geometry.Translation2d; +import edu.wpi.first.math.kinematics.ChassisSpeeds; +import edu.wpi.first.math.kinematics.SwerveDriveKinematics; +import edu.wpi.first.math.trajectory.TrajectoryConfig; +import edu.wpi.first.math.trajectory.TrajectoryGenerator; import java.util.List; import java.util.Random; import org.junit.jupiter.api.Test; diff --git a/wpimath/src/test/java/edu/wpi/first/wpilibj/estimator/UnscentedKalmanFilterTest.java b/wpimath/src/test/java/edu/wpi/first/math/estimator/UnscentedKalmanFilterTest.java similarity index 94% rename from wpimath/src/test/java/edu/wpi/first/wpilibj/estimator/UnscentedKalmanFilterTest.java rename to wpimath/src/test/java/edu/wpi/first/math/estimator/UnscentedKalmanFilterTest.java index 2a68283960..96b137aef6 100644 --- a/wpimath/src/test/java/edu/wpi/first/wpilibj/estimator/UnscentedKalmanFilterTest.java +++ b/wpimath/src/test/java/edu/wpi/first/math/estimator/UnscentedKalmanFilterTest.java @@ -2,29 +2,29 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.first.wpilibj.estimator; +package edu.wpi.first.math.estimator; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; -import edu.wpi.first.wpilibj.geometry.Pose2d; -import edu.wpi.first.wpilibj.geometry.Rotation2d; -import edu.wpi.first.wpilibj.math.Discretization; -import edu.wpi.first.wpilibj.math.StateSpaceUtil; -import edu.wpi.first.wpilibj.system.NumericalIntegration; -import edu.wpi.first.wpilibj.system.NumericalJacobian; -import edu.wpi.first.wpilibj.system.plant.DCMotor; -import edu.wpi.first.wpilibj.system.plant.LinearSystemId; -import edu.wpi.first.wpilibj.trajectory.TrajectoryConfig; -import edu.wpi.first.wpilibj.trajectory.TrajectoryGenerator; -import edu.wpi.first.wpiutil.math.Matrix; -import edu.wpi.first.wpiutil.math.Nat; -import edu.wpi.first.wpiutil.math.VecBuilder; -import edu.wpi.first.wpiutil.math.numbers.N1; -import edu.wpi.first.wpiutil.math.numbers.N2; -import edu.wpi.first.wpiutil.math.numbers.N4; -import edu.wpi.first.wpiutil.math.numbers.N6; +import edu.wpi.first.math.Matrix; +import edu.wpi.first.math.Nat; +import edu.wpi.first.math.VecBuilder; +import edu.wpi.first.math.geometry.Pose2d; +import edu.wpi.first.math.geometry.Rotation2d; +import edu.wpi.first.math.math.Discretization; +import edu.wpi.first.math.math.StateSpaceUtil; +import edu.wpi.first.math.numbers.N1; +import edu.wpi.first.math.numbers.N2; +import edu.wpi.first.math.numbers.N4; +import edu.wpi.first.math.numbers.N6; +import edu.wpi.first.math.system.NumericalIntegration; +import edu.wpi.first.math.system.NumericalJacobian; +import edu.wpi.first.math.system.plant.DCMotor; +import edu.wpi.first.math.system.plant.LinearSystemId; +import edu.wpi.first.math.trajectory.TrajectoryConfig; +import edu.wpi.first.math.trajectory.TrajectoryGenerator; import java.util.ArrayList; import java.util.Arrays; import java.util.List; diff --git a/wpimath/src/test/java/edu/wpi/first/wpilibj/LinearFilterTest.java b/wpimath/src/test/java/edu/wpi/first/math/filter/LinearFilterTest.java similarity index 99% rename from wpimath/src/test/java/edu/wpi/first/wpilibj/LinearFilterTest.java rename to wpimath/src/test/java/edu/wpi/first/math/filter/LinearFilterTest.java index 9405bf1ffc..41be6f62c4 100644 --- a/wpimath/src/test/java/edu/wpi/first/wpilibj/LinearFilterTest.java +++ b/wpimath/src/test/java/edu/wpi/first/math/filter/LinearFilterTest.java @@ -2,7 +2,7 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.first.wpilibj; +package edu.wpi.first.math.filter; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; diff --git a/wpimath/src/test/java/edu/wpi/first/wpilibj/MedianFilterTest.java b/wpimath/src/test/java/edu/wpi/first/math/filter/MedianFilterTest.java similarity index 97% rename from wpimath/src/test/java/edu/wpi/first/wpilibj/MedianFilterTest.java rename to wpimath/src/test/java/edu/wpi/first/math/filter/MedianFilterTest.java index ff2b77ef5b..06b3d019f0 100644 --- a/wpimath/src/test/java/edu/wpi/first/wpilibj/MedianFilterTest.java +++ b/wpimath/src/test/java/edu/wpi/first/math/filter/MedianFilterTest.java @@ -2,7 +2,7 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.first.wpilibj; +package edu.wpi.first.math.filter; import static org.junit.jupiter.api.Assertions.assertEquals; diff --git a/wpimath/src/test/java/edu/wpi/first/wpilibj/geometry/Pose2dTest.java b/wpimath/src/test/java/edu/wpi/first/math/geometry/Pose2dTest.java similarity index 98% rename from wpimath/src/test/java/edu/wpi/first/wpilibj/geometry/Pose2dTest.java rename to wpimath/src/test/java/edu/wpi/first/math/geometry/Pose2dTest.java index effdb2abb8..b6e66af400 100644 --- a/wpimath/src/test/java/edu/wpi/first/wpilibj/geometry/Pose2dTest.java +++ b/wpimath/src/test/java/edu/wpi/first/math/geometry/Pose2dTest.java @@ -2,7 +2,7 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.first.wpilibj.geometry; +package edu.wpi.first.math.geometry; import static org.junit.jupiter.api.Assertions.assertAll; import static org.junit.jupiter.api.Assertions.assertEquals; diff --git a/wpimath/src/test/java/edu/wpi/first/wpilibj/geometry/Rotation2dTest.java b/wpimath/src/test/java/edu/wpi/first/math/geometry/Rotation2dTest.java similarity index 98% rename from wpimath/src/test/java/edu/wpi/first/wpilibj/geometry/Rotation2dTest.java rename to wpimath/src/test/java/edu/wpi/first/math/geometry/Rotation2dTest.java index 468d2a2042..cb3f0f3dfd 100644 --- a/wpimath/src/test/java/edu/wpi/first/wpilibj/geometry/Rotation2dTest.java +++ b/wpimath/src/test/java/edu/wpi/first/math/geometry/Rotation2dTest.java @@ -2,7 +2,7 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.first.wpilibj.geometry; +package edu.wpi.first.math.geometry; import static org.junit.jupiter.api.Assertions.assertAll; import static org.junit.jupiter.api.Assertions.assertEquals; diff --git a/wpimath/src/test/java/edu/wpi/first/wpilibj/geometry/Transform2dTest.java b/wpimath/src/test/java/edu/wpi/first/math/geometry/Transform2dTest.java similarity index 96% rename from wpimath/src/test/java/edu/wpi/first/wpilibj/geometry/Transform2dTest.java rename to wpimath/src/test/java/edu/wpi/first/math/geometry/Transform2dTest.java index 40dd7a36a4..1a2f8e795d 100644 --- a/wpimath/src/test/java/edu/wpi/first/wpilibj/geometry/Transform2dTest.java +++ b/wpimath/src/test/java/edu/wpi/first/math/geometry/Transform2dTest.java @@ -2,7 +2,7 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.first.wpilibj.geometry; +package edu.wpi.first.math.geometry; import static org.junit.jupiter.api.Assertions.assertAll; import static org.junit.jupiter.api.Assertions.assertEquals; diff --git a/wpimath/src/test/java/edu/wpi/first/wpilibj/geometry/Translation2dTest.java b/wpimath/src/test/java/edu/wpi/first/math/geometry/Translation2dTest.java similarity index 98% rename from wpimath/src/test/java/edu/wpi/first/wpilibj/geometry/Translation2dTest.java rename to wpimath/src/test/java/edu/wpi/first/math/geometry/Translation2dTest.java index d2f58ea68c..2d8eeaabe7 100644 --- a/wpimath/src/test/java/edu/wpi/first/wpilibj/geometry/Translation2dTest.java +++ b/wpimath/src/test/java/edu/wpi/first/math/geometry/Translation2dTest.java @@ -2,7 +2,7 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.first.wpilibj.geometry; +package edu.wpi.first.math.geometry; import static org.junit.jupiter.api.Assertions.assertAll; import static org.junit.jupiter.api.Assertions.assertEquals; diff --git a/wpimath/src/test/java/edu/wpi/first/wpilibj/geometry/Twist2dTest.java b/wpimath/src/test/java/edu/wpi/first/math/geometry/Twist2dTest.java similarity index 98% rename from wpimath/src/test/java/edu/wpi/first/wpilibj/geometry/Twist2dTest.java rename to wpimath/src/test/java/edu/wpi/first/math/geometry/Twist2dTest.java index 45d8e2c420..c13bb09709 100644 --- a/wpimath/src/test/java/edu/wpi/first/wpilibj/geometry/Twist2dTest.java +++ b/wpimath/src/test/java/edu/wpi/first/math/geometry/Twist2dTest.java @@ -2,7 +2,7 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.first.wpilibj.geometry; +package edu.wpi.first.math.geometry; import static org.junit.jupiter.api.Assertions.assertAll; import static org.junit.jupiter.api.Assertions.assertEquals; diff --git a/wpimath/src/test/java/edu/wpi/first/wpilibj/kinematics/ChassisSpeedsTest.java b/wpimath/src/test/java/edu/wpi/first/math/kinematics/ChassisSpeedsTest.java similarity index 90% rename from wpimath/src/test/java/edu/wpi/first/wpilibj/kinematics/ChassisSpeedsTest.java rename to wpimath/src/test/java/edu/wpi/first/math/kinematics/ChassisSpeedsTest.java index 45311bf4c2..b9c3785caa 100644 --- a/wpimath/src/test/java/edu/wpi/first/wpilibj/kinematics/ChassisSpeedsTest.java +++ b/wpimath/src/test/java/edu/wpi/first/math/kinematics/ChassisSpeedsTest.java @@ -2,12 +2,12 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.first.wpilibj.kinematics; +package edu.wpi.first.math.kinematics; import static org.junit.jupiter.api.Assertions.assertAll; import static org.junit.jupiter.api.Assertions.assertEquals; -import edu.wpi.first.wpilibj.geometry.Rotation2d; +import edu.wpi.first.math.geometry.Rotation2d; import org.junit.jupiter.api.Test; class ChassisSpeedsTest { diff --git a/wpimath/src/test/java/edu/wpi/first/wpilibj/kinematics/DifferentialDriveKinematicsTest.java b/wpimath/src/test/java/edu/wpi/first/math/kinematics/DifferentialDriveKinematicsTest.java similarity index 98% rename from wpimath/src/test/java/edu/wpi/first/wpilibj/kinematics/DifferentialDriveKinematicsTest.java rename to wpimath/src/test/java/edu/wpi/first/math/kinematics/DifferentialDriveKinematicsTest.java index e2c5b1d2be..adee41fabc 100644 --- a/wpimath/src/test/java/edu/wpi/first/wpilibj/kinematics/DifferentialDriveKinematicsTest.java +++ b/wpimath/src/test/java/edu/wpi/first/math/kinematics/DifferentialDriveKinematicsTest.java @@ -2,7 +2,7 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.first.wpilibj.kinematics; +package edu.wpi.first.math.kinematics; import static org.junit.jupiter.api.Assertions.assertAll; import static org.junit.jupiter.api.Assertions.assertEquals; diff --git a/wpimath/src/test/java/edu/wpi/first/wpilibj/kinematics/DifferentialDriveOdometryTest.java b/wpimath/src/test/java/edu/wpi/first/math/kinematics/DifferentialDriveOdometryTest.java similarity index 87% rename from wpimath/src/test/java/edu/wpi/first/wpilibj/kinematics/DifferentialDriveOdometryTest.java rename to wpimath/src/test/java/edu/wpi/first/math/kinematics/DifferentialDriveOdometryTest.java index 136b45528a..f85e8fbf2b 100644 --- a/wpimath/src/test/java/edu/wpi/first/wpilibj/kinematics/DifferentialDriveOdometryTest.java +++ b/wpimath/src/test/java/edu/wpi/first/math/kinematics/DifferentialDriveOdometryTest.java @@ -2,13 +2,13 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.first.wpilibj.kinematics; +package edu.wpi.first.math.kinematics; import static org.junit.jupiter.api.Assertions.assertAll; import static org.junit.jupiter.api.Assertions.assertEquals; -import edu.wpi.first.wpilibj.geometry.Pose2d; -import edu.wpi.first.wpilibj.geometry.Rotation2d; +import edu.wpi.first.math.geometry.Pose2d; +import edu.wpi.first.math.geometry.Rotation2d; import org.junit.jupiter.api.Test; class DifferentialDriveOdometryTest { diff --git a/wpimath/src/test/java/edu/wpi/first/wpilibj/kinematics/MecanumDriveKinematicsTest.java b/wpimath/src/test/java/edu/wpi/first/math/kinematics/MecanumDriveKinematicsTest.java similarity index 98% rename from wpimath/src/test/java/edu/wpi/first/wpilibj/kinematics/MecanumDriveKinematicsTest.java rename to wpimath/src/test/java/edu/wpi/first/math/kinematics/MecanumDriveKinematicsTest.java index 0cf9aa1934..4b9f5a3465 100644 --- a/wpimath/src/test/java/edu/wpi/first/wpilibj/kinematics/MecanumDriveKinematicsTest.java +++ b/wpimath/src/test/java/edu/wpi/first/math/kinematics/MecanumDriveKinematicsTest.java @@ -2,12 +2,12 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.first.wpilibj.kinematics; +package edu.wpi.first.math.kinematics; import static org.junit.jupiter.api.Assertions.assertAll; import static org.junit.jupiter.api.Assertions.assertEquals; -import edu.wpi.first.wpilibj.geometry.Translation2d; +import edu.wpi.first.math.geometry.Translation2d; import org.junit.jupiter.api.Test; class MecanumDriveKinematicsTest { diff --git a/wpimath/src/test/java/edu/wpi/first/wpilibj/kinematics/MecanumDriveOdometryTest.java b/wpimath/src/test/java/edu/wpi/first/math/kinematics/MecanumDriveOdometryTest.java similarity index 94% rename from wpimath/src/test/java/edu/wpi/first/wpilibj/kinematics/MecanumDriveOdometryTest.java rename to wpimath/src/test/java/edu/wpi/first/math/kinematics/MecanumDriveOdometryTest.java index 5d26d0def7..2c6ed65e14 100644 --- a/wpimath/src/test/java/edu/wpi/first/wpilibj/kinematics/MecanumDriveOdometryTest.java +++ b/wpimath/src/test/java/edu/wpi/first/math/kinematics/MecanumDriveOdometryTest.java @@ -2,14 +2,14 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.first.wpilibj.kinematics; +package edu.wpi.first.math.kinematics; import static org.junit.jupiter.api.Assertions.assertAll; import static org.junit.jupiter.api.Assertions.assertEquals; -import edu.wpi.first.wpilibj.geometry.Pose2d; -import edu.wpi.first.wpilibj.geometry.Rotation2d; -import edu.wpi.first.wpilibj.geometry.Translation2d; +import edu.wpi.first.math.geometry.Pose2d; +import edu.wpi.first.math.geometry.Rotation2d; +import edu.wpi.first.math.geometry.Translation2d; import org.junit.jupiter.api.Test; class MecanumDriveOdometryTest { diff --git a/wpimath/src/test/java/edu/wpi/first/wpilibj/kinematics/SwerveDriveKinematicsTest.java b/wpimath/src/test/java/edu/wpi/first/math/kinematics/SwerveDriveKinematicsTest.java similarity index 98% rename from wpimath/src/test/java/edu/wpi/first/wpilibj/kinematics/SwerveDriveKinematicsTest.java rename to wpimath/src/test/java/edu/wpi/first/math/kinematics/SwerveDriveKinematicsTest.java index dc40fe3106..fb81b6da44 100644 --- a/wpimath/src/test/java/edu/wpi/first/wpilibj/kinematics/SwerveDriveKinematicsTest.java +++ b/wpimath/src/test/java/edu/wpi/first/math/kinematics/SwerveDriveKinematicsTest.java @@ -2,13 +2,13 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.first.wpilibj.kinematics; +package edu.wpi.first.math.kinematics; import static org.junit.jupiter.api.Assertions.assertAll; import static org.junit.jupiter.api.Assertions.assertEquals; -import edu.wpi.first.wpilibj.geometry.Rotation2d; -import edu.wpi.first.wpilibj.geometry.Translation2d; +import edu.wpi.first.math.geometry.Rotation2d; +import edu.wpi.first.math.geometry.Translation2d; import org.junit.jupiter.api.Test; class SwerveDriveKinematicsTest { diff --git a/wpimath/src/test/java/edu/wpi/first/wpilibj/kinematics/SwerveDriveOdometryTest.java b/wpimath/src/test/java/edu/wpi/first/math/kinematics/SwerveDriveOdometryTest.java similarity index 94% rename from wpimath/src/test/java/edu/wpi/first/wpilibj/kinematics/SwerveDriveOdometryTest.java rename to wpimath/src/test/java/edu/wpi/first/math/kinematics/SwerveDriveOdometryTest.java index 64e1aa587b..cb6dfdfdb0 100644 --- a/wpimath/src/test/java/edu/wpi/first/wpilibj/kinematics/SwerveDriveOdometryTest.java +++ b/wpimath/src/test/java/edu/wpi/first/math/kinematics/SwerveDriveOdometryTest.java @@ -2,14 +2,14 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.first.wpilibj.kinematics; +package edu.wpi.first.math.kinematics; import static org.junit.jupiter.api.Assertions.assertAll; import static org.junit.jupiter.api.Assertions.assertEquals; -import edu.wpi.first.wpilibj.geometry.Pose2d; -import edu.wpi.first.wpilibj.geometry.Rotation2d; -import edu.wpi.first.wpilibj.geometry.Translation2d; +import edu.wpi.first.math.geometry.Pose2d; +import edu.wpi.first.math.geometry.Rotation2d; +import edu.wpi.first.math.geometry.Translation2d; import org.junit.jupiter.api.Test; class SwerveDriveOdometryTest { diff --git a/wpimath/src/test/java/edu/wpi/first/wpilibj/kinematics/SwerveModuleStateTest.java b/wpimath/src/test/java/edu/wpi/first/math/kinematics/SwerveModuleStateTest.java similarity index 95% rename from wpimath/src/test/java/edu/wpi/first/wpilibj/kinematics/SwerveModuleStateTest.java rename to wpimath/src/test/java/edu/wpi/first/math/kinematics/SwerveModuleStateTest.java index 49ca2f642e..01815be00d 100644 --- a/wpimath/src/test/java/edu/wpi/first/wpilibj/kinematics/SwerveModuleStateTest.java +++ b/wpimath/src/test/java/edu/wpi/first/math/kinematics/SwerveModuleStateTest.java @@ -2,12 +2,12 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.first.wpilibj.kinematics; +package edu.wpi.first.math.kinematics; import static org.junit.jupiter.api.Assertions.assertAll; import static org.junit.jupiter.api.Assertions.assertEquals; -import edu.wpi.first.wpilibj.geometry.Rotation2d; +import edu.wpi.first.math.geometry.Rotation2d; import org.junit.jupiter.api.Test; class SwerveModuleStateTest { diff --git a/wpimath/src/test/java/edu/wpi/first/wpilibj/math/StateSpaceUtilTest.java b/wpimath/src/test/java/edu/wpi/first/math/math/StateSpaceUtilTest.java similarity index 94% rename from wpimath/src/test/java/edu/wpi/first/wpilibj/math/StateSpaceUtilTest.java rename to wpimath/src/test/java/edu/wpi/first/math/math/StateSpaceUtilTest.java index af810af69f..3c0dd01fb2 100644 --- a/wpimath/src/test/java/edu/wpi/first/wpilibj/math/StateSpaceUtilTest.java +++ b/wpimath/src/test/java/edu/wpi/first/math/math/StateSpaceUtilTest.java @@ -2,20 +2,20 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.first.wpilibj.math; +package edu.wpi.first.math.math; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; -import edu.wpi.first.wpilibj.geometry.Pose2d; -import edu.wpi.first.wpilibj.geometry.Rotation2d; -import edu.wpi.first.wpiutil.math.Matrix; -import edu.wpi.first.wpiutil.math.Nat; -import edu.wpi.first.wpiutil.math.SimpleMatrixUtils; -import edu.wpi.first.wpiutil.math.VecBuilder; -import edu.wpi.first.wpiutil.math.numbers.N1; -import edu.wpi.first.wpiutil.math.numbers.N2; +import edu.wpi.first.math.Matrix; +import edu.wpi.first.math.Nat; +import edu.wpi.first.math.SimpleMatrixUtils; +import edu.wpi.first.math.VecBuilder; +import edu.wpi.first.math.geometry.Pose2d; +import edu.wpi.first.math.geometry.Rotation2d; +import edu.wpi.first.math.numbers.N1; +import edu.wpi.first.math.numbers.N2; import java.util.ArrayList; import java.util.List; import org.ejml.dense.row.MatrixFeatures_DDRM; diff --git a/wpimath/src/test/java/edu/wpi/first/wpilibj/spline/CubicHermiteSplineTest.java b/wpimath/src/test/java/edu/wpi/first/math/spline/CubicHermiteSplineTest.java similarity index 95% rename from wpimath/src/test/java/edu/wpi/first/wpilibj/spline/CubicHermiteSplineTest.java rename to wpimath/src/test/java/edu/wpi/first/math/spline/CubicHermiteSplineTest.java index 8d4d101bf9..b92bb6a319 100644 --- a/wpimath/src/test/java/edu/wpi/first/wpilibj/spline/CubicHermiteSplineTest.java +++ b/wpimath/src/test/java/edu/wpi/first/math/spline/CubicHermiteSplineTest.java @@ -2,17 +2,17 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.first.wpilibj.spline; +package edu.wpi.first.math.spline; import static org.junit.jupiter.api.Assertions.assertAll; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; -import edu.wpi.first.wpilibj.geometry.Pose2d; -import edu.wpi.first.wpilibj.geometry.Rotation2d; -import edu.wpi.first.wpilibj.geometry.Translation2d; -import edu.wpi.first.wpilibj.spline.SplineParameterizer.MalformedSplineException; +import edu.wpi.first.math.geometry.Pose2d; +import edu.wpi.first.math.geometry.Rotation2d; +import edu.wpi.first.math.geometry.Translation2d; +import edu.wpi.first.math.spline.SplineParameterizer.MalformedSplineException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; diff --git a/wpimath/src/test/java/edu/wpi/first/wpilibj/spline/QuinticHermiteSplineTest.java b/wpimath/src/test/java/edu/wpi/first/math/spline/QuinticHermiteSplineTest.java similarity index 94% rename from wpimath/src/test/java/edu/wpi/first/wpilibj/spline/QuinticHermiteSplineTest.java rename to wpimath/src/test/java/edu/wpi/first/math/spline/QuinticHermiteSplineTest.java index f00ab9a1cf..d5fd071a23 100644 --- a/wpimath/src/test/java/edu/wpi/first/wpilibj/spline/QuinticHermiteSplineTest.java +++ b/wpimath/src/test/java/edu/wpi/first/math/spline/QuinticHermiteSplineTest.java @@ -2,16 +2,16 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.first.wpilibj.spline; +package edu.wpi.first.math.spline; import static org.junit.jupiter.api.Assertions.assertAll; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; -import edu.wpi.first.wpilibj.geometry.Pose2d; -import edu.wpi.first.wpilibj.geometry.Rotation2d; -import edu.wpi.first.wpilibj.spline.SplineParameterizer.MalformedSplineException; +import edu.wpi.first.math.geometry.Pose2d; +import edu.wpi.first.math.geometry.Rotation2d; +import edu.wpi.first.math.spline.SplineParameterizer.MalformedSplineException; import java.util.List; import org.junit.jupiter.api.Test; diff --git a/wpimath/src/test/java/edu/wpi/first/wpilibj/system/LinearSystemIDTest.java b/wpimath/src/test/java/edu/wpi/first/math/system/LinearSystemIDTest.java similarity index 91% rename from wpimath/src/test/java/edu/wpi/first/wpilibj/system/LinearSystemIDTest.java rename to wpimath/src/test/java/edu/wpi/first/math/system/LinearSystemIDTest.java index 8015a2f6a3..353d066b20 100644 --- a/wpimath/src/test/java/edu/wpi/first/wpilibj/system/LinearSystemIDTest.java +++ b/wpimath/src/test/java/edu/wpi/first/math/system/LinearSystemIDTest.java @@ -2,16 +2,16 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.first.wpilibj.system; +package edu.wpi.first.math.system; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; -import edu.wpi.first.wpilibj.system.plant.DCMotor; -import edu.wpi.first.wpilibj.system.plant.LinearSystemId; -import edu.wpi.first.wpiutil.math.Matrix; -import edu.wpi.first.wpiutil.math.Nat; -import edu.wpi.first.wpiutil.math.VecBuilder; +import edu.wpi.first.math.Matrix; +import edu.wpi.first.math.Nat; +import edu.wpi.first.math.VecBuilder; +import edu.wpi.first.math.system.plant.DCMotor; +import edu.wpi.first.math.system.plant.LinearSystemId; import org.junit.jupiter.api.Test; class LinearSystemIDTest { diff --git a/wpimath/src/test/java/edu/wpi/first/wpilibj/system/NumericalIntegrationTest.java b/wpimath/src/test/java/edu/wpi/first/math/system/NumericalIntegrationTest.java similarity index 87% rename from wpimath/src/test/java/edu/wpi/first/wpilibj/system/NumericalIntegrationTest.java rename to wpimath/src/test/java/edu/wpi/first/math/system/NumericalIntegrationTest.java index 1f67e26cab..3dc9b49619 100644 --- a/wpimath/src/test/java/edu/wpi/first/wpilibj/system/NumericalIntegrationTest.java +++ b/wpimath/src/test/java/edu/wpi/first/math/system/NumericalIntegrationTest.java @@ -2,14 +2,14 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.first.wpilibj.system; +package edu.wpi.first.math.system; import static org.junit.jupiter.api.Assertions.assertEquals; -import edu.wpi.first.wpiutil.math.Matrix; -import edu.wpi.first.wpiutil.math.Nat; -import edu.wpi.first.wpiutil.math.VecBuilder; -import edu.wpi.first.wpiutil.math.numbers.N1; +import edu.wpi.first.math.Matrix; +import edu.wpi.first.math.Nat; +import edu.wpi.first.math.VecBuilder; +import edu.wpi.first.math.numbers.N1; import org.junit.jupiter.api.Test; public class NumericalIntegrationTest { diff --git a/wpimath/src/test/java/edu/wpi/first/wpilibj/trajectory/CentripetalAccelerationConstraintTest.java b/wpimath/src/test/java/edu/wpi/first/math/trajectory/CentripetalAccelerationConstraintTest.java similarity index 87% rename from wpimath/src/test/java/edu/wpi/first/wpilibj/trajectory/CentripetalAccelerationConstraintTest.java rename to wpimath/src/test/java/edu/wpi/first/math/trajectory/CentripetalAccelerationConstraintTest.java index 3aab65c5f9..1805589c9e 100644 --- a/wpimath/src/test/java/edu/wpi/first/wpilibj/trajectory/CentripetalAccelerationConstraintTest.java +++ b/wpimath/src/test/java/edu/wpi/first/math/trajectory/CentripetalAccelerationConstraintTest.java @@ -2,12 +2,12 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.first.wpilibj.trajectory; +package edu.wpi.first.math.trajectory; import static org.junit.jupiter.api.Assertions.assertTrue; -import edu.wpi.first.wpilibj.trajectory.constraint.CentripetalAccelerationConstraint; -import edu.wpi.first.wpilibj.util.Units; +import edu.wpi.first.math.trajectory.constraint.CentripetalAccelerationConstraint; +import edu.wpi.first.math.util.Units; import java.util.Collections; import org.junit.jupiter.api.Test; diff --git a/wpimath/src/test/java/edu/wpi/first/wpilibj/trajectory/DifferentialDriveKinematicsConstraintTest.java b/wpimath/src/test/java/edu/wpi/first/math/trajectory/DifferentialDriveKinematicsConstraintTest.java similarity index 84% rename from wpimath/src/test/java/edu/wpi/first/wpilibj/trajectory/DifferentialDriveKinematicsConstraintTest.java rename to wpimath/src/test/java/edu/wpi/first/math/trajectory/DifferentialDriveKinematicsConstraintTest.java index b206d42ced..7614a83e12 100644 --- a/wpimath/src/test/java/edu/wpi/first/wpilibj/trajectory/DifferentialDriveKinematicsConstraintTest.java +++ b/wpimath/src/test/java/edu/wpi/first/math/trajectory/DifferentialDriveKinematicsConstraintTest.java @@ -2,15 +2,15 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.first.wpilibj.trajectory; +package edu.wpi.first.math.trajectory; import static org.junit.jupiter.api.Assertions.assertAll; import static org.junit.jupiter.api.Assertions.assertTrue; -import edu.wpi.first.wpilibj.kinematics.ChassisSpeeds; -import edu.wpi.first.wpilibj.kinematics.DifferentialDriveKinematics; -import edu.wpi.first.wpilibj.trajectory.constraint.DifferentialDriveKinematicsConstraint; -import edu.wpi.first.wpilibj.util.Units; +import edu.wpi.first.math.kinematics.ChassisSpeeds; +import edu.wpi.first.math.kinematics.DifferentialDriveKinematics; +import edu.wpi.first.math.trajectory.constraint.DifferentialDriveKinematicsConstraint; +import edu.wpi.first.math.util.Units; import java.util.Collections; import org.junit.jupiter.api.Test; diff --git a/wpimath/src/test/java/edu/wpi/first/wpilibj/trajectory/DifferentialDriveVoltageConstraintTest.java b/wpimath/src/test/java/edu/wpi/first/math/trajectory/DifferentialDriveVoltageConstraintTest.java similarity index 88% rename from wpimath/src/test/java/edu/wpi/first/wpilibj/trajectory/DifferentialDriveVoltageConstraintTest.java rename to wpimath/src/test/java/edu/wpi/first/math/trajectory/DifferentialDriveVoltageConstraintTest.java index 6ad7bad819..34e0a594f7 100644 --- a/wpimath/src/test/java/edu/wpi/first/wpilibj/trajectory/DifferentialDriveVoltageConstraintTest.java +++ b/wpimath/src/test/java/edu/wpi/first/math/trajectory/DifferentialDriveVoltageConstraintTest.java @@ -2,19 +2,19 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.first.wpilibj.trajectory; +package edu.wpi.first.math.trajectory; import static org.junit.jupiter.api.Assertions.assertAll; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertTrue; -import edu.wpi.first.wpilibj.controller.SimpleMotorFeedforward; -import edu.wpi.first.wpilibj.geometry.Pose2d; -import edu.wpi.first.wpilibj.geometry.Rotation2d; -import edu.wpi.first.wpilibj.geometry.Translation2d; -import edu.wpi.first.wpilibj.kinematics.ChassisSpeeds; -import edu.wpi.first.wpilibj.kinematics.DifferentialDriveKinematics; -import edu.wpi.first.wpilibj.trajectory.constraint.DifferentialDriveVoltageConstraint; +import edu.wpi.first.math.controller.SimpleMotorFeedforward; +import edu.wpi.first.math.geometry.Pose2d; +import edu.wpi.first.math.geometry.Rotation2d; +import edu.wpi.first.math.geometry.Translation2d; +import edu.wpi.first.math.kinematics.ChassisSpeeds; +import edu.wpi.first.math.kinematics.DifferentialDriveKinematics; +import edu.wpi.first.math.trajectory.constraint.DifferentialDriveVoltageConstraint; import java.util.ArrayList; import java.util.Collections; import org.junit.jupiter.api.Test; diff --git a/wpimath/src/test/java/edu/wpi/first/wpilibj/trajectory/EllipticalRegionConstraintTest.java b/wpimath/src/test/java/edu/wpi/first/math/trajectory/EllipticalRegionConstraintTest.java similarity index 87% rename from wpimath/src/test/java/edu/wpi/first/wpilibj/trajectory/EllipticalRegionConstraintTest.java rename to wpimath/src/test/java/edu/wpi/first/math/trajectory/EllipticalRegionConstraintTest.java index 948b270757..f9e3c1821f 100644 --- a/wpimath/src/test/java/edu/wpi/first/wpilibj/trajectory/EllipticalRegionConstraintTest.java +++ b/wpimath/src/test/java/edu/wpi/first/math/trajectory/EllipticalRegionConstraintTest.java @@ -2,17 +2,17 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.first.wpilibj.trajectory; +package edu.wpi.first.math.trajectory; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; -import edu.wpi.first.wpilibj.geometry.Pose2d; -import edu.wpi.first.wpilibj.geometry.Rotation2d; -import edu.wpi.first.wpilibj.geometry.Translation2d; -import edu.wpi.first.wpilibj.trajectory.constraint.EllipticalRegionConstraint; -import edu.wpi.first.wpilibj.trajectory.constraint.MaxVelocityConstraint; -import edu.wpi.first.wpilibj.util.Units; +import edu.wpi.first.math.geometry.Pose2d; +import edu.wpi.first.math.geometry.Rotation2d; +import edu.wpi.first.math.geometry.Translation2d; +import edu.wpi.first.math.trajectory.constraint.EllipticalRegionConstraint; +import edu.wpi.first.math.trajectory.constraint.MaxVelocityConstraint; +import edu.wpi.first.math.util.Units; import java.util.List; import org.junit.jupiter.api.Test; diff --git a/wpimath/src/test/java/edu/wpi/first/wpilibj/trajectory/RectangularRegionConstraintTest.java b/wpimath/src/test/java/edu/wpi/first/math/trajectory/RectangularRegionConstraintTest.java similarity index 84% rename from wpimath/src/test/java/edu/wpi/first/wpilibj/trajectory/RectangularRegionConstraintTest.java rename to wpimath/src/test/java/edu/wpi/first/math/trajectory/RectangularRegionConstraintTest.java index 7ad58c4e62..1ab826edea 100644 --- a/wpimath/src/test/java/edu/wpi/first/wpilibj/trajectory/RectangularRegionConstraintTest.java +++ b/wpimath/src/test/java/edu/wpi/first/math/trajectory/RectangularRegionConstraintTest.java @@ -2,17 +2,17 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.first.wpilibj.trajectory; +package edu.wpi.first.math.trajectory; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; -import edu.wpi.first.wpilibj.geometry.Pose2d; -import edu.wpi.first.wpilibj.geometry.Rotation2d; -import edu.wpi.first.wpilibj.geometry.Translation2d; -import edu.wpi.first.wpilibj.trajectory.constraint.MaxVelocityConstraint; -import edu.wpi.first.wpilibj.trajectory.constraint.RectangularRegionConstraint; -import edu.wpi.first.wpilibj.util.Units; +import edu.wpi.first.math.geometry.Pose2d; +import edu.wpi.first.math.geometry.Rotation2d; +import edu.wpi.first.math.geometry.Translation2d; +import edu.wpi.first.math.trajectory.constraint.MaxVelocityConstraint; +import edu.wpi.first.math.trajectory.constraint.RectangularRegionConstraint; +import edu.wpi.first.math.util.Units; import java.util.List; import org.junit.jupiter.api.Test; diff --git a/wpimath/src/test/java/edu/wpi/first/wpilibj/trajectory/TrajectoryConcatenateTest.java b/wpimath/src/test/java/edu/wpi/first/math/trajectory/TrajectoryConcatenateTest.java similarity index 91% rename from wpimath/src/test/java/edu/wpi/first/wpilibj/trajectory/TrajectoryConcatenateTest.java rename to wpimath/src/test/java/edu/wpi/first/math/trajectory/TrajectoryConcatenateTest.java index d8ff29f400..2e80d7b2ef 100644 --- a/wpimath/src/test/java/edu/wpi/first/wpilibj/trajectory/TrajectoryConcatenateTest.java +++ b/wpimath/src/test/java/edu/wpi/first/math/trajectory/TrajectoryConcatenateTest.java @@ -2,13 +2,13 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.first.wpilibj.trajectory; +package edu.wpi.first.math.trajectory; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; -import edu.wpi.first.wpilibj.geometry.Pose2d; -import edu.wpi.first.wpilibj.geometry.Rotation2d; +import edu.wpi.first.math.geometry.Pose2d; +import edu.wpi.first.math.geometry.Rotation2d; import java.util.List; import org.junit.jupiter.api.Test; diff --git a/wpimath/src/test/java/edu/wpi/first/wpilibj/trajectory/TrajectoryGeneratorTest.java b/wpimath/src/test/java/edu/wpi/first/math/trajectory/TrajectoryGeneratorTest.java similarity index 87% rename from wpimath/src/test/java/edu/wpi/first/wpilibj/trajectory/TrajectoryGeneratorTest.java rename to wpimath/src/test/java/edu/wpi/first/math/trajectory/TrajectoryGeneratorTest.java index fd580b3eb9..97c1858bd1 100644 --- a/wpimath/src/test/java/edu/wpi/first/wpilibj/trajectory/TrajectoryGeneratorTest.java +++ b/wpimath/src/test/java/edu/wpi/first/math/trajectory/TrajectoryGeneratorTest.java @@ -2,18 +2,18 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.first.wpilibj.trajectory; +package edu.wpi.first.math.trajectory; -import static edu.wpi.first.wpilibj.util.Units.feetToMeters; +import static edu.wpi.first.math.util.Units.feetToMeters; import static org.junit.jupiter.api.Assertions.assertAll; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; -import edu.wpi.first.wpilibj.geometry.Pose2d; -import edu.wpi.first.wpilibj.geometry.Rotation2d; -import edu.wpi.first.wpilibj.geometry.Transform2d; -import edu.wpi.first.wpilibj.geometry.Translation2d; -import edu.wpi.first.wpilibj.trajectory.constraint.TrajectoryConstraint; +import edu.wpi.first.math.geometry.Pose2d; +import edu.wpi.first.math.geometry.Rotation2d; +import edu.wpi.first.math.geometry.Transform2d; +import edu.wpi.first.math.geometry.Translation2d; +import edu.wpi.first.math.trajectory.constraint.TrajectoryConstraint; import java.util.ArrayList; import java.util.Arrays; import java.util.List; diff --git a/wpimath/src/test/java/edu/wpi/first/wpilibj/trajectory/TrajectoryJsonTest.java b/wpimath/src/test/java/edu/wpi/first/math/trajectory/TrajectoryJsonTest.java similarity index 81% rename from wpimath/src/test/java/edu/wpi/first/wpilibj/trajectory/TrajectoryJsonTest.java rename to wpimath/src/test/java/edu/wpi/first/math/trajectory/TrajectoryJsonTest.java index 9dfb0ca6a1..bb7260110d 100644 --- a/wpimath/src/test/java/edu/wpi/first/wpilibj/trajectory/TrajectoryJsonTest.java +++ b/wpimath/src/test/java/edu/wpi/first/math/trajectory/TrajectoryJsonTest.java @@ -2,13 +2,13 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.first.wpilibj.trajectory; +package edu.wpi.first.math.trajectory; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertEquals; -import edu.wpi.first.wpilibj.kinematics.DifferentialDriveKinematics; -import edu.wpi.first.wpilibj.trajectory.constraint.DifferentialDriveKinematicsConstraint; +import edu.wpi.first.math.kinematics.DifferentialDriveKinematics; +import edu.wpi.first.math.trajectory.constraint.DifferentialDriveKinematicsConstraint; import java.util.List; import org.junit.jupiter.api.Test; diff --git a/wpimath/src/test/java/edu/wpi/first/wpilibj/trajectory/TrajectoryTransformTest.java b/wpimath/src/test/java/edu/wpi/first/math/trajectory/TrajectoryTransformTest.java similarity index 89% rename from wpimath/src/test/java/edu/wpi/first/wpilibj/trajectory/TrajectoryTransformTest.java rename to wpimath/src/test/java/edu/wpi/first/math/trajectory/TrajectoryTransformTest.java index ad691d26fe..6268768d84 100644 --- a/wpimath/src/test/java/edu/wpi/first/wpilibj/trajectory/TrajectoryTransformTest.java +++ b/wpimath/src/test/java/edu/wpi/first/math/trajectory/TrajectoryTransformTest.java @@ -2,14 +2,14 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.first.wpilibj.trajectory; +package edu.wpi.first.math.trajectory; import static org.junit.jupiter.api.Assertions.assertEquals; -import edu.wpi.first.wpilibj.geometry.Pose2d; -import edu.wpi.first.wpilibj.geometry.Rotation2d; -import edu.wpi.first.wpilibj.geometry.Transform2d; -import edu.wpi.first.wpilibj.geometry.Translation2d; +import edu.wpi.first.math.geometry.Pose2d; +import edu.wpi.first.math.geometry.Rotation2d; +import edu.wpi.first.math.geometry.Transform2d; +import edu.wpi.first.math.geometry.Translation2d; import java.util.List; import org.junit.jupiter.api.Test; diff --git a/wpimath/src/test/java/edu/wpi/first/wpilibj/trajectory/TrapezoidProfileTest.java b/wpimath/src/test/java/edu/wpi/first/math/trajectory/TrapezoidProfileTest.java similarity index 99% rename from wpimath/src/test/java/edu/wpi/first/wpilibj/trajectory/TrapezoidProfileTest.java rename to wpimath/src/test/java/edu/wpi/first/math/trajectory/TrapezoidProfileTest.java index ca6f8b8fbf..de01bf791f 100644 --- a/wpimath/src/test/java/edu/wpi/first/wpilibj/trajectory/TrapezoidProfileTest.java +++ b/wpimath/src/test/java/edu/wpi/first/math/trajectory/TrapezoidProfileTest.java @@ -2,7 +2,7 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.first.wpilibj.trajectory; +package edu.wpi.first.math.trajectory; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotEquals; diff --git a/wpimath/src/test/java/edu/wpi/first/wpilibj/util/UnitsTest.java b/wpimath/src/test/java/edu/wpi/first/math/util/UnitsTest.java similarity index 97% rename from wpimath/src/test/java/edu/wpi/first/wpilibj/util/UnitsTest.java rename to wpimath/src/test/java/edu/wpi/first/math/util/UnitsTest.java index 788aa1015a..89484039a2 100644 --- a/wpimath/src/test/java/edu/wpi/first/wpilibj/util/UnitsTest.java +++ b/wpimath/src/test/java/edu/wpi/first/math/util/UnitsTest.java @@ -2,7 +2,7 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -package edu.wpi.first.wpilibj.util; +package edu.wpi.first.math.util; import static org.junit.jupiter.api.Assertions.assertEquals; From e338f9f1905affc96ce0b05b74b3b1da194dc94d Mon Sep 17 00:00:00 2001 From: Thad House Date: Sat, 1 May 2021 10:26:33 -0700 Subject: [PATCH 28/34] [build] Fix wpilibc runCpp task (#3327) --- wpilibc/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wpilibc/build.gradle b/wpilibc/build.gradle index f101852109..7af6314ada 100644 --- a/wpilibc/build.gradle +++ b/wpilibc/build.gradle @@ -243,7 +243,7 @@ model { def arch = it.targetPlatform.architecture.name if (arch == 'x86-64' || arch == 'x86') { dependsOn it.tasks.install - + commandLine it.tasks.install.runScriptFile.get().asFile.toString() found = true } } From 23d2326d1d4e2c3754b2611750198f3a3871ac70 Mon Sep 17 00:00:00 2001 From: Thad House Date: Sat, 1 May 2021 10:28:30 -0700 Subject: [PATCH 29/34] [hal] Report previous allocation location for indexed resource duplicates (#3322) --- hal/src/main/native/athena/AnalogGyro.cpp | 28 ++++--- hal/src/main/native/athena/AnalogInput.cpp | 29 ++++--- hal/src/main/native/athena/AnalogInternal.h | 2 + hal/src/main/native/athena/AnalogOutput.cpp | 35 ++++++--- hal/src/main/native/athena/DIO.cpp | 29 ++++--- hal/src/main/native/athena/DigitalInternal.h | 2 + hal/src/main/native/athena/HALInternal.h | 8 +- hal/src/main/native/athena/I2C.cpp | 4 +- hal/src/main/native/athena/PWM.cpp | 27 ++++--- hal/src/main/native/athena/Relay.cpp | 28 ++++--- hal/src/main/native/athena/SPI.cpp | 18 ++--- hal/src/main/native/athena/SerialPort.cpp | 11 +-- hal/src/main/native/athena/Solenoid.cpp | 10 +-- hal/src/main/native/cpp/ErrorHandling.cpp | 24 +++++- hal/src/main/native/cpp/jni/AnalogGyroJNI.cpp | 7 +- hal/src/main/native/cpp/jni/AnalogJNI.cpp | 17 +++-- hal/src/main/native/cpp/jni/DIOJNI.cpp | 10 ++- hal/src/main/native/cpp/jni/HALUtil.cpp | 23 +++--- hal/src/main/native/cpp/jni/PWMJNI.cpp | 8 +- hal/src/main/native/cpp/jni/RelayJNI.cpp | 8 +- hal/src/main/native/include/hal/AnalogGyro.h | 3 + hal/src/main/native/include/hal/AnalogInput.h | 6 +- .../main/native/include/hal/AnalogOutput.h | 6 +- hal/src/main/native/include/hal/DIO.h | 12 ++- hal/src/main/native/include/hal/Errors.h | 2 + hal/src/main/native/include/hal/PWM.h | 3 + hal/src/main/native/include/hal/Relay.h | 3 + .../hal/handles/DigitalHandleResource.h | 21 ++++-- .../hal/handles/IndexedHandleResource.h | 19 +++-- hal/src/main/native/sim/AnalogGyro.cpp | 31 +++++--- hal/src/main/native/sim/AnalogInput.cpp | 32 +++++--- hal/src/main/native/sim/AnalogInternal.h | 3 + hal/src/main/native/sim/AnalogOutput.cpp | 35 ++++++--- hal/src/main/native/sim/DIO.cpp | 32 ++++---- hal/src/main/native/sim/DigitalInternal.h | 3 + hal/src/main/native/sim/HALInternal.h | 19 +++++ hal/src/main/native/sim/PWM.cpp | 30 +++++--- hal/src/main/native/sim/Relay.cpp | 28 ++++--- hal/src/main/native/sim/Solenoid.cpp | 10 +-- .../native/cpp/mockdata/AnalogInDataTests.cpp | 14 ++-- .../cpp/mockdata/AnalogOutDataTests.cpp | 18 +++-- .../test/native/cpp/mockdata/DIODataTests.cpp | 14 ++-- .../test/native/cpp/mockdata/PWMDataTests.cpp | 14 ++-- .../native/cpp/mockdata/RelayDataTests.cpp | 14 ++-- .../src/main/native/cpp/AddressableLED.cpp | 5 +- wpilibc/src/main/native/cpp/AnalogGyro.cpp | 5 +- wpilibc/src/main/native/cpp/AnalogInput.cpp | 4 +- wpilibc/src/main/native/cpp/AnalogOutput.cpp | 4 +- wpilibc/src/main/native/cpp/DigitalInput.cpp | 6 +- wpilibc/src/main/native/cpp/DigitalOutput.cpp | 5 +- wpilibc/src/main/native/cpp/PWM.cpp | 5 +- wpilibc/src/main/native/cpp/Relay.cpp | 9 ++- .../src/main/cpp/examples/HAL/c/Robot.c | 6 +- .../src/main/native/include/wpi/jni_util.h | 75 +++++++++++++++++++ 54 files changed, 573 insertions(+), 251 deletions(-) create mode 100644 hal/src/main/native/sim/HALInternal.h diff --git a/hal/src/main/native/athena/AnalogGyro.cpp b/hal/src/main/native/athena/AnalogGyro.cpp index b8f4add6e2..83bc42f0be 100644 --- a/hal/src/main/native/athena/AnalogGyro.cpp +++ b/hal/src/main/native/athena/AnalogGyro.cpp @@ -4,12 +4,14 @@ #include "hal/AnalogGyro.h" +#include #include #include #include "AnalogInternal.h" #include "HALInitializer.h" +#include "HALInternal.h" #include "hal/AnalogAccumulator.h" #include "hal/AnalogInput.h" #include "hal/handles/IndexedHandleResource.h" @@ -21,6 +23,7 @@ struct AnalogGyro { double voltsPerDegreePerSecond; double offset; int32_t center; + std::string previousAllocation; }; } // namespace @@ -55,36 +58,43 @@ static void Wait(double seconds) { extern "C" { HAL_GyroHandle HAL_InitializeAnalogGyro(HAL_AnalogInputHandle analogHandle, + const char* allocationLocation, int32_t* status) { hal::init::CheckInit(); + // Handle will be type checked by HAL_IsAccumulatorChannel + int16_t channel = getHandleIndex(analogHandle); if (!HAL_IsAccumulatorChannel(analogHandle, status)) { if (*status == 0) { *status = HAL_INVALID_ACCUMULATOR_CHANNEL; + hal::SetLastErrorIndexOutOfRange(status, "Invalid Index for Analog Gyro", + 0, kNumAccumulators, channel); } return HAL_kInvalidHandle; } - // handle known to be correct, so no need to type check - int16_t channel = getHandleIndex(analogHandle); - - auto handle = analogGyroHandles->Allocate(channel, status); + HAL_GyroHandle handle; + auto gyro = analogGyroHandles->Allocate(channel, &handle, status); if (*status != 0) { + if (gyro) { + hal::SetLastErrorPreviouslyAllocated(status, "Analog Gyro", channel, + gyro->previousAllocation); + } else { + hal::SetLastErrorIndexOutOfRange(status, "Invalid Index for Analog Gyro", + 0, kNumAccumulators, channel); + } return HAL_kInvalidHandle; // failed to allocate. Pass error back. } // Initialize port structure - auto gyro = analogGyroHandles->Get(handle); - if (gyro == nullptr) { // would only error on thread issue - *status = HAL_HANDLE_ERROR; - return HAL_kInvalidHandle; - } gyro->handle = analogHandle; gyro->voltsPerDegreePerSecond = 0; gyro->offset = 0; gyro->center = 0; + gyro->previousAllocation = allocationLocation ? allocationLocation : ""; + return handle; } diff --git a/hal/src/main/native/athena/AnalogInput.cpp b/hal/src/main/native/athena/AnalogInput.cpp index 4fd77a654d..6b3add10e9 100644 --- a/hal/src/main/native/athena/AnalogInput.cpp +++ b/hal/src/main/native/athena/AnalogInput.cpp @@ -9,6 +9,7 @@ #include "AnalogInternal.h" #include "HALInitializer.h" +#include "HALInternal.h" #include "PortsInternal.h" #include "hal/AnalogAccumulator.h" #include "hal/handles/HandlesInternal.h" @@ -21,8 +22,9 @@ using namespace hal; extern "C" { -HAL_AnalogInputHandle HAL_InitializeAnalogInputPort(HAL_PortHandle portHandle, - int32_t* status) { +HAL_AnalogInputHandle HAL_InitializeAnalogInputPort( + HAL_PortHandle portHandle, const char* allocationLocation, + int32_t* status) { hal::init::CheckInit(); initializeAnalog(status); @@ -31,23 +33,28 @@ HAL_AnalogInputHandle HAL_InitializeAnalogInputPort(HAL_PortHandle portHandle, } int16_t channel = getPortHandleChannel(portHandle); - if (channel == InvalidHandleIndex) { - *status = PARAMETER_OUT_OF_RANGE; + if (channel == InvalidHandleIndex || channel >= kNumAnalogInputs) { + *status = RESOURCE_OUT_OF_RANGE; + hal::SetLastErrorIndexOutOfRange(status, "Invalid Index for Analog Input", + 0, kNumAnalogInputs, channel); return HAL_kInvalidHandle; } - HAL_AnalogInputHandle handle = analogInputHandles->Allocate(channel, status); + HAL_AnalogInputHandle handle; + auto analog_port = analogInputHandles->Allocate(channel, &handle, status); if (*status != 0) { + if (analog_port) { + hal::SetLastErrorPreviouslyAllocated(status, "Analog Input", channel, + analog_port->previousAllocation); + } else { + hal::SetLastErrorIndexOutOfRange(status, "Invalid Index for Analog Input", + 0, kNumAnalogInputs, channel); + } return HAL_kInvalidHandle; // failed to allocate. Pass error back. } // Initialize port structure - auto analog_port = analogInputHandles->Get(handle); - if (analog_port == nullptr) { // would only error on thread issue - *status = HAL_HANDLE_ERROR; - return HAL_kInvalidHandle; - } analog_port->channel = static_cast(channel); if (HAL_IsAccumulatorChannel(handle, status)) { analog_port->accumulator.reset(tAccumulator::create(channel, status)); @@ -59,6 +66,8 @@ HAL_AnalogInputHandle HAL_InitializeAnalogInputPort(HAL_PortHandle portHandle, analogInputSystem->writeScanList(channel, channel, status); HAL_SetAnalogAverageBits(handle, kDefaultAverageBits, status); HAL_SetAnalogOversampleBits(handle, kDefaultOversampleBits, status); + analog_port->previousAllocation = + allocationLocation ? allocationLocation : ""; return handle; } diff --git a/hal/src/main/native/athena/AnalogInternal.h b/hal/src/main/native/athena/AnalogInternal.h index d3403648f0..431c6242b7 100644 --- a/hal/src/main/native/athena/AnalogInternal.h +++ b/hal/src/main/native/athena/AnalogInternal.h @@ -7,6 +7,7 @@ #include #include +#include #include @@ -31,6 +32,7 @@ extern bool analogSampleRateSet; struct AnalogPort { uint8_t channel; std::unique_ptr accumulator; + std::string previousAllocation; }; extern IndexedHandleResource + #include "AnalogInternal.h" #include "HALInitializer.h" +#include "HALInternal.h" #include "PortsInternal.h" #include "hal/Errors.h" #include "hal/handles/HandlesInternal.h" @@ -17,6 +20,7 @@ namespace { struct AnalogOutput { uint8_t channel; + std::string previousAllocation; }; } // namespace @@ -36,8 +40,9 @@ void InitializeAnalogOutput() { extern "C" { -HAL_AnalogOutputHandle HAL_InitializeAnalogOutputPort(HAL_PortHandle portHandle, - int32_t* status) { +HAL_AnalogOutputHandle HAL_InitializeAnalogOutputPort( + HAL_PortHandle portHandle, const char* allocationLocation, + int32_t* status) { hal::init::CheckInit(); initializeAnalog(status); @@ -46,25 +51,31 @@ HAL_AnalogOutputHandle HAL_InitializeAnalogOutputPort(HAL_PortHandle portHandle, } int16_t channel = getPortHandleChannel(portHandle); - if (channel == InvalidHandleIndex) { - *status = PARAMETER_OUT_OF_RANGE; + if (channel == InvalidHandleIndex || channel >= kNumAnalogOutputs) { + *status = RESOURCE_OUT_OF_RANGE; + hal::SetLastErrorIndexOutOfRange(status, "Invalid Index for Analog Output", + 0, kNumAnalogOutputs, channel); return HAL_kInvalidHandle; } - HAL_AnalogOutputHandle handle = - analogOutputHandles->Allocate(channel, status); + HAL_AnalogOutputHandle handle; + auto port = analogOutputHandles->Allocate(channel, &handle, status); if (*status != 0) { + if (port) { + hal::SetLastErrorPreviouslyAllocated(status, "Analog Output", channel, + port->previousAllocation); + } else { + hal::SetLastErrorIndexOutOfRange(status, + "Invalid Index for Analog Output", 0, + kNumAnalogOutputs, channel); + } return HAL_kInvalidHandle; // failed to allocate. Pass error back. } - auto port = analogOutputHandles->Get(handle); - if (port == nullptr) { // would only error on thread issue - *status = HAL_HANDLE_ERROR; - return HAL_kInvalidHandle; - } - port->channel = static_cast(channel); + port->previousAllocation = allocationLocation ? allocationLocation : ""; + return handle; } diff --git a/hal/src/main/native/athena/DIO.cpp b/hal/src/main/native/athena/DIO.cpp index 38c90e455f..75909a0702 100644 --- a/hal/src/main/native/athena/DIO.cpp +++ b/hal/src/main/native/athena/DIO.cpp @@ -11,6 +11,7 @@ #include "DigitalInternal.h" #include "HALInitializer.h" +#include "HALInternal.h" #include "PortsInternal.h" #include "hal/cpp/fpga_clock.h" #include "hal/handles/HandlesInternal.h" @@ -38,7 +39,9 @@ void InitializeDIO() { extern "C" { HAL_DigitalHandle HAL_InitializeDIOPort(HAL_PortHandle portHandle, - HAL_Bool input, int32_t* status) { + HAL_Bool input, + const char* allocationLocation, + int32_t* status) { hal::init::CheckInit(); initializeDigital(status); @@ -48,23 +51,28 @@ HAL_DigitalHandle HAL_InitializeDIOPort(HAL_PortHandle portHandle, int16_t channel = getPortHandleChannel(portHandle); if (channel == InvalidHandleIndex || channel >= kNumDigitalChannels) { - *status = PARAMETER_OUT_OF_RANGE; + *status = RESOURCE_OUT_OF_RANGE; + hal::SetLastErrorIndexOutOfRange(status, "Invalid Index for DIO", 0, + kNumDigitalChannels, channel); return HAL_kInvalidHandle; } - auto handle = - digitalChannelHandles->Allocate(channel, HAL_HandleEnum::DIO, status); + HAL_DigitalHandle handle; + + auto port = digitalChannelHandles->Allocate(channel, HAL_HandleEnum::DIO, + &handle, status); if (*status != 0) { + if (port) { + hal::SetLastErrorPreviouslyAllocated(status, "PWM or DIO", channel, + port->previousAllocation); + } else { + hal::SetLastErrorIndexOutOfRange(status, "Invalid Index for DIO", 0, + kNumDigitalChannels, channel); + } return HAL_kInvalidHandle; // failed to allocate. Pass error back. } - auto port = digitalChannelHandles->Get(handle, HAL_HandleEnum::DIO); - if (port == nullptr) { // would only occur on thread issue. - *status = HAL_HANDLE_ERROR; - return HAL_kInvalidHandle; - } - port->channel = static_cast(channel); std::scoped_lock lock(digitalDIOMutex); @@ -114,6 +122,7 @@ HAL_DigitalHandle HAL_InitializeDIOPort(HAL_PortHandle portHandle, } digitalSystem->writeOutputEnable(outputEnable, status); + port->previousAllocation = allocationLocation ? allocationLocation : ""; return handle; } diff --git a/hal/src/main/native/athena/DigitalInternal.h b/hal/src/main/native/athena/DigitalInternal.h index 79656ad5e2..6b1e9097b8 100644 --- a/hal/src/main/native/athena/DigitalInternal.h +++ b/hal/src/main/native/athena/DigitalInternal.h @@ -7,6 +7,7 @@ #include #include +#include #include @@ -70,6 +71,7 @@ struct DigitalPort { int32_t centerPwm = 0; int32_t deadbandMinPwm = 0; int32_t minPwm = 0; + std::string previousAllocation; }; extern DigitalHandleResource= kNumPWMChannels) { - *status = PARAMETER_OUT_OF_RANGE; + *status = RESOURCE_OUT_OF_RANGE; + hal::SetLastErrorIndexOutOfRange(status, "Invalid Index for PWM", 0, + kNumPWMChannels, channel); return HAL_kInvalidHandle; } @@ -87,19 +91,22 @@ HAL_DigitalHandle HAL_InitializePWMPort(HAL_PortHandle portHandle, channel = remapMXPPWMChannel(channel) + 10; // remap MXP to proper channel } - auto handle = - digitalChannelHandles->Allocate(channel, HAL_HandleEnum::PWM, status); + HAL_DigitalHandle handle; + + auto port = digitalChannelHandles->Allocate(channel, HAL_HandleEnum::PWM, + &handle, status); if (*status != 0) { + if (port) { + hal::SetLastErrorPreviouslyAllocated(status, "PWM or DIO", channel, + port->previousAllocation); + } else { + hal::SetLastErrorIndexOutOfRange(status, "Invalid Index for PWM", 0, + kNumPWMChannels, channel); + } return HAL_kInvalidHandle; // failed to allocate. Pass error back. } - auto port = digitalChannelHandles->Get(handle, HAL_HandleEnum::PWM); - if (port == nullptr) { // would only occur on thread issue. - *status = HAL_HANDLE_ERROR; - return HAL_kInvalidHandle; - } - port->channel = origChannel; if (port->channel > tPWM::kNumHdrRegisters - 1) { @@ -113,6 +120,8 @@ HAL_DigitalHandle HAL_InitializePWMPort(HAL_PortHandle portHandle, // Defaults to allow an always valid config. HAL_SetPWMConfig(handle, 2.0, 1.501, 1.5, 1.499, 1.0, status); + port->previousAllocation = allocationLocation ? allocationLocation : ""; + return handle; } void HAL_FreePWMPort(HAL_DigitalHandle pwmPortHandle, int32_t* status) { diff --git a/hal/src/main/native/athena/Relay.cpp b/hal/src/main/native/athena/Relay.cpp index 368ce1b730..a01dd9d52b 100644 --- a/hal/src/main/native/athena/Relay.cpp +++ b/hal/src/main/native/athena/Relay.cpp @@ -4,8 +4,11 @@ #include "hal/Relay.h" +#include + #include "DigitalInternal.h" #include "HALInitializer.h" +#include "HALInternal.h" #include "PortsInternal.h" #include "hal/handles/IndexedHandleResource.h" @@ -16,6 +19,7 @@ namespace { struct Relay { uint8_t channel; bool fwd; + std::string previousAllocation; }; } // namespace @@ -38,6 +42,7 @@ void InitializeRelay() { extern "C" { HAL_RelayHandle HAL_InitializeRelayPort(HAL_PortHandle portHandle, HAL_Bool fwd, + const char* allocationLocation, int32_t* status) { hal::init::CheckInit(); initializeDigital(status); @@ -47,8 +52,10 @@ HAL_RelayHandle HAL_InitializeRelayPort(HAL_PortHandle portHandle, HAL_Bool fwd, } int16_t channel = getPortHandleChannel(portHandle); - if (channel == InvalidHandleIndex) { - *status = PARAMETER_OUT_OF_RANGE; + if (channel == InvalidHandleIndex || channel >= kNumRelayChannels) { + *status = RESOURCE_OUT_OF_RANGE; + hal::SetLastErrorIndexOutOfRange(status, "Invalid Index for Relay", 0, + kNumRelayChannels, channel); return HAL_kInvalidHandle; } @@ -56,18 +63,20 @@ HAL_RelayHandle HAL_InitializeRelayPort(HAL_PortHandle portHandle, HAL_Bool fwd, channel += kNumRelayHeaders; // add 4 to reverse channels } - auto handle = relayHandles->Allocate(channel, status); + HAL_RelayHandle handle; + auto port = relayHandles->Allocate(channel, &handle, status); if (*status != 0) { + if (port) { + hal::SetLastErrorPreviouslyAllocated(status, "Relay", channel, + port->previousAllocation); + } else { + hal::SetLastErrorIndexOutOfRange(status, "Invalid Index for Relay", 0, + kNumRelayChannels, channel); + } return HAL_kInvalidHandle; // failed to allocate. Pass error back. } - auto port = relayHandles->Get(handle); - if (port == nullptr) { // would only occur on thread issue. - *status = HAL_HANDLE_ERROR; - return HAL_kInvalidHandle; - } - if (!fwd) { // Subtract number of headers to put channel in range channel -= kNumRelayHeaders; @@ -78,6 +87,7 @@ HAL_RelayHandle HAL_InitializeRelayPort(HAL_PortHandle portHandle, HAL_Bool fwd, } port->channel = static_cast(channel); + port->previousAllocation = allocationLocation ? allocationLocation : ""; return handle; } diff --git a/hal/src/main/native/athena/SPI.cpp b/hal/src/main/native/athena/SPI.cpp index a237f668ea..43843ebfed 100644 --- a/hal/src/main/native/athena/SPI.cpp +++ b/hal/src/main/native/athena/SPI.cpp @@ -75,14 +75,14 @@ static void CommonSPIPortInit(int32_t* status) { } // MISO if ((digitalHandles[3] = HAL_InitializeDIOPort(createPortHandleForSPI(29), - false, status)) == + false, nullptr, status)) == HAL_kInvalidHandle) { std::printf("Failed to allocate DIO 29 (MISO)\n"); return; } // MOSI if ((digitalHandles[4] = HAL_InitializeDIOPort(createPortHandleForSPI(30), - false, status)) == + false, nullptr, status)) == HAL_kInvalidHandle) { std::printf("Failed to allocate DIO 30 (MOSI)\n"); HAL_FreeDIOPort(digitalHandles[3]); // free the first port allocated @@ -133,7 +133,7 @@ void HAL_InitializeSPI(HAL_SPIPort port, int32_t* status) { } // CS1, Allocate if ((digitalHandles[0] = HAL_InitializeDIOPort(createPortHandleForSPI(26), - false, status)) == + false, nullptr, status)) == HAL_kInvalidHandle) { std::printf("Failed to allocate DIO 26 (CS1)\n"); CommonSPIPortFree(); @@ -156,7 +156,7 @@ void HAL_InitializeSPI(HAL_SPIPort port, int32_t* status) { } // CS2, Allocate if ((digitalHandles[1] = HAL_InitializeDIOPort(createPortHandleForSPI(27), - false, status)) == + false, nullptr, status)) == HAL_kInvalidHandle) { std::printf("Failed to allocate DIO 27 (CS2)\n"); CommonSPIPortFree(); @@ -179,7 +179,7 @@ void HAL_InitializeSPI(HAL_SPIPort port, int32_t* status) { } // CS3, Allocate if ((digitalHandles[2] = HAL_InitializeDIOPort(createPortHandleForSPI(28), - false, status)) == + false, nullptr, status)) == HAL_kInvalidHandle) { std::printf("Failed to allocate DIO 28 (CS3)\n"); CommonSPIPortFree(); @@ -201,20 +201,20 @@ void HAL_InitializeSPI(HAL_SPIPort port, int32_t* status) { return; } if ((digitalHandles[5] = HAL_InitializeDIOPort(createPortHandleForSPI(14), - false, status)) == + false, nullptr, status)) == HAL_kInvalidHandle) { wpi::outs() << "Failed to allocate DIO 14\n"; return; } if ((digitalHandles[6] = HAL_InitializeDIOPort(createPortHandleForSPI(15), - false, status)) == + false, nullptr, status)) == HAL_kInvalidHandle) { wpi::outs() << "Failed to allocate DIO 15\n"; HAL_FreeDIOPort(digitalHandles[5]); // free the first port allocated return; } if ((digitalHandles[7] = HAL_InitializeDIOPort(createPortHandleForSPI(16), - false, status)) == + false, nullptr, status)) == HAL_kInvalidHandle) { wpi::outs() << "Failed to allocate DIO 16\n"; HAL_FreeDIOPort(digitalHandles[5]); // free the first port allocated @@ -222,7 +222,7 @@ void HAL_InitializeSPI(HAL_SPIPort port, int32_t* status) { return; } if ((digitalHandles[8] = HAL_InitializeDIOPort(createPortHandleForSPI(17), - false, status)) == + false, nullptr, status)) == HAL_kInvalidHandle) { wpi::outs() << "Failed to allocate DIO 17\n"; HAL_FreeDIOPort(digitalHandles[5]); // free the first port allocated diff --git a/hal/src/main/native/athena/SerialPort.cpp b/hal/src/main/native/athena/SerialPort.cpp index 27f17397ed..cf34d7b5c0 100644 --- a/hal/src/main/native/athena/SerialPort.cpp +++ b/hal/src/main/native/athena/SerialPort.cpp @@ -70,19 +70,14 @@ HAL_SerialPortHandle HAL_InitializeSerialPort(HAL_SerialPort port, HAL_SerialPortHandle HAL_InitializeSerialPortDirect(HAL_SerialPort port, const char* portName, int32_t* status) { - auto handle = serialPortHandles->Allocate(static_cast(port), status); + HAL_SerialPortHandle handle; + auto serialPort = + serialPortHandles->Allocate(static_cast(port), &handle, status); if (*status != 0) { return HAL_kInvalidHandle; } - auto serialPort = serialPortHandles->Get(handle); - - if (serialPort == nullptr) { - *status = HAL_HANDLE_ERROR; - return HAL_kInvalidHandle; - } - serialPort->portId = open(portName, O_RDWR | O_NOCTTY); if (serialPort->portId < 0) { *status = errno; diff --git a/hal/src/main/native/athena/Solenoid.cpp b/hal/src/main/native/athena/Solenoid.cpp index 669b87674f..b12d8bf1a7 100644 --- a/hal/src/main/native/athena/Solenoid.cpp +++ b/hal/src/main/native/athena/Solenoid.cpp @@ -60,16 +60,12 @@ HAL_SolenoidHandle HAL_InitializeSolenoidPort(HAL_PortHandle portHandle, return HAL_kInvalidHandle; } - auto handle = solenoidHandles->Allocate( - module * kNumSolenoidChannels + channel, status); + HAL_SolenoidHandle handle; + auto solenoidPort = solenoidHandles->Allocate( + module * kNumSolenoidChannels + channel, &handle, status); if (*status != 0) { return HAL_kInvalidHandle; } - auto solenoidPort = solenoidHandles->Get(handle); - if (solenoidPort == nullptr) { // would only occur on thread issues - *status = HAL_HANDLE_ERROR; - return HAL_kInvalidHandle; - } solenoidPort->module = static_cast(module); solenoidPort->channel = static_cast(channel); diff --git a/hal/src/main/native/cpp/ErrorHandling.cpp b/hal/src/main/native/cpp/ErrorHandling.cpp index 9f10853b52..fa54e4676e 100644 --- a/hal/src/main/native/cpp/ErrorHandling.cpp +++ b/hal/src/main/native/cpp/ErrorHandling.cpp @@ -21,11 +21,31 @@ static LastErrorStorage& GetThreadLastError() { } namespace hal { -void SetLastError(int32_t status, const wpi::Twine& value) { +void SetLastError(int32_t* status, const wpi::Twine& value) { LastErrorStorage& lastError = GetThreadLastError(); lastError.message.clear(); value.toVector(lastError.message); - lastError.status = status; + lastError.status = *status; + *status = HAL_USE_LAST_ERROR; +} + +void SetLastErrorIndexOutOfRange(int32_t* status, const wpi::Twine& message, + int32_t minimum, int32_t maximum, + int32_t requested) { + SetLastError(status, message + "\n Status: " + wpi::Twine(*status) + + "\n Minimum: " + wpi::Twine(minimum) + + " Maximum: " + wpi::Twine(maximum) + + " Reequested: " + wpi::Twine(requested)); +} + +void SetLastErrorPreviouslyAllocated(int32_t* status, const wpi::Twine& message, + int32_t channel, + const wpi::Twine& previousAllocation) { + hal::SetLastError( + status, + message + " " + wpi::Twine(channel) + + " previously allocated.\nLocation of the previous allocation:\n" + + previousAllocation + "\nLocation of the current allocation:"); } } // namespace hal diff --git a/hal/src/main/native/cpp/jni/AnalogGyroJNI.cpp b/hal/src/main/native/cpp/jni/AnalogGyroJNI.cpp index 8a6b131837..154301f8e7 100644 --- a/hal/src/main/native/cpp/jni/AnalogGyroJNI.cpp +++ b/hal/src/main/native/cpp/jni/AnalogGyroJNI.cpp @@ -6,6 +6,8 @@ #include +#include + #include "HALUtil.h" #include "edu_wpi_first_hal_AnalogGyroJNI.h" #include "hal/AnalogGyro.h" @@ -24,8 +26,9 @@ Java_edu_wpi_first_hal_AnalogGyroJNI_initializeAnalogGyro (JNIEnv* env, jclass, jint id) { int32_t status = 0; - HAL_GyroHandle handle = - HAL_InitializeAnalogGyro((HAL_AnalogInputHandle)id, &status); + auto stack = wpi::java::GetJavaStackTrace(env, "edu.wpi.first"); + HAL_GyroHandle handle = HAL_InitializeAnalogGyro((HAL_AnalogInputHandle)id, + stack.c_str(), &status); // Analog input does range checking, so we don't need to do so. CheckStatusForceThrow(env, status); return (jint)handle; diff --git a/hal/src/main/native/cpp/jni/AnalogJNI.cpp b/hal/src/main/native/cpp/jni/AnalogJNI.cpp index 04ec3a405e..5f0abce6be 100644 --- a/hal/src/main/native/cpp/jni/AnalogJNI.cpp +++ b/hal/src/main/native/cpp/jni/AnalogJNI.cpp @@ -6,6 +6,8 @@ #include +#include + #include "HALUtil.h" #include "edu_wpi_first_hal_AnalogJNI.h" #include "hal/AnalogAccumulator.h" @@ -29,9 +31,10 @@ Java_edu_wpi_first_hal_AnalogJNI_initializeAnalogInputPort (JNIEnv* env, jclass, jint id) { int32_t status = 0; - auto analog = HAL_InitializeAnalogInputPort((HAL_PortHandle)id, &status); - CheckStatusRange(env, status, 0, HAL_GetNumAnalogInputs(), - hal::getPortHandleChannel((HAL_PortHandle)id)); + auto stack = wpi::java::GetJavaStackTrace(env, "edu.wpi.first"); + auto analog = + HAL_InitializeAnalogInputPort((HAL_PortHandle)id, stack.c_str(), &status); + CheckStatusForceThrow(env, status); return (jint)analog; } @@ -57,10 +60,10 @@ Java_edu_wpi_first_hal_AnalogJNI_initializeAnalogOutputPort (JNIEnv* env, jclass, jint id) { int32_t status = 0; - HAL_AnalogOutputHandle analog = - HAL_InitializeAnalogOutputPort((HAL_PortHandle)id, &status); - CheckStatusRange(env, status, 0, HAL_GetNumAnalogOutputs(), - hal::getPortHandleChannel((HAL_PortHandle)id)); + auto stack = wpi::java::GetJavaStackTrace(env, "edu.wpi.first"); + HAL_AnalogOutputHandle analog = HAL_InitializeAnalogOutputPort( + (HAL_PortHandle)id, stack.c_str(), &status); + CheckStatusForceThrow(env, status); return (jlong)analog; } diff --git a/hal/src/main/native/cpp/jni/DIOJNI.cpp b/hal/src/main/native/cpp/jni/DIOJNI.cpp index 5dc59c8bda..236754169e 100644 --- a/hal/src/main/native/cpp/jni/DIOJNI.cpp +++ b/hal/src/main/native/cpp/jni/DIOJNI.cpp @@ -6,6 +6,8 @@ #include +#include + #include "HALUtil.h" #include "edu_wpi_first_hal_DIOJNI.h" #include "hal/DIO.h" @@ -27,10 +29,10 @@ Java_edu_wpi_first_hal_DIOJNI_initializeDIOPort (JNIEnv* env, jclass, jint id, jboolean input) { int32_t status = 0; - auto dio = HAL_InitializeDIOPort((HAL_PortHandle)id, - static_cast(input), &status); - CheckStatusRange(env, status, 0, HAL_GetNumDigitalChannels(), - hal::getPortHandleChannel((HAL_PortHandle)id)); + auto stack = wpi::java::GetJavaStackTrace(env, "edu.wpi.first"); + auto dio = HAL_InitializeDIOPort( + (HAL_PortHandle)id, static_cast(input), stack.c_str(), &status); + CheckStatusForceThrow(env, status); return (jint)dio; } diff --git a/hal/src/main/native/cpp/jni/HALUtil.cpp b/hal/src/main/native/cpp/jni/HALUtil.cpp index cbeb9c97ee..9894942f12 100644 --- a/hal/src/main/native/cpp/jni/HALUtil.cpp +++ b/hal/src/main/native/cpp/jni/HALUtil.cpp @@ -83,15 +83,13 @@ void ThrowUncleanStatusException(JNIEnv* env, wpi::StringRef msg, env->Throw(static_cast(exception)); } -void ThrowAllocationException(JNIEnv* env, int32_t minRange, int32_t maxRange, - int32_t requestedValue, int32_t status) { - const char* message = HAL_GetLastError(&status); +void ThrowAllocationException(JNIEnv* env, const char* lastError, + int32_t status) { wpi::SmallString<1024> buf; wpi::raw_svector_ostream oss(buf); - oss << " Code: " << status << ". " << message - << ", Minimum Value: " << minRange << ", Maximum Value: " << maxRange - << ", Requested Value: " << requestedValue; - env->ThrowNew(allocationExCls, buf.c_str()); + + oss << "Code: " << status << '\n' << lastError; + allocationExCls.Throw(env, buf.c_str()); } @@ -107,10 +105,11 @@ void ReportError(JNIEnv* env, int32_t status, bool doThrow) { if (status == 0) { return; } + const char* message = HAL_GetLastError(&status); if (status == HAL_HANDLE_ERROR) { ThrowHalHandleException(env, status); + return; } - const char* message = HAL_GetLastError(&status); if (doThrow && status < 0) { wpi::SmallString<1024> buf; wpi::raw_svector_ostream oss(buf); @@ -132,17 +131,19 @@ void ThrowError(JNIEnv* env, int32_t status, int32_t minRange, int32_t maxRange, if (status == 0) { return; } + const char* lastError = HAL_GetLastError(&status); if (status == NO_AVAILABLE_RESOURCES || status == RESOURCE_IS_ALLOCATED || status == RESOURCE_OUT_OF_RANGE) { - ThrowAllocationException(env, minRange, maxRange, requestedValue, status); + ThrowAllocationException(env, lastError, status); + return; } if (status == HAL_HANDLE_ERROR) { ThrowHalHandleException(env, status); + return; } - const char* message = HAL_GetErrorMessage(status); wpi::SmallString<1024> buf; wpi::raw_svector_ostream oss(buf); - oss << " Code: " << status << ". " << message; + oss << " Code: " << status << ". " << lastError; ThrowUncleanStatusException(env, buf.c_str(), status); } diff --git a/hal/src/main/native/cpp/jni/PWMJNI.cpp b/hal/src/main/native/cpp/jni/PWMJNI.cpp index 5099dba7d9..e83f11bcdf 100644 --- a/hal/src/main/native/cpp/jni/PWMJNI.cpp +++ b/hal/src/main/native/cpp/jni/PWMJNI.cpp @@ -6,6 +6,8 @@ #include +#include + #include "HALUtil.h" #include "edu_wpi_first_hal_PWMJNI.h" #include "hal/DIO.h" @@ -27,9 +29,9 @@ Java_edu_wpi_first_hal_PWMJNI_initializePWMPort (JNIEnv* env, jclass, jint id) { int32_t status = 0; - auto pwm = HAL_InitializePWMPort((HAL_PortHandle)id, &status); - CheckStatusRange(env, status, 0, HAL_GetNumPWMChannels(), - hal::getPortHandleChannel((HAL_PortHandle)id)); + auto stack = wpi::java::GetJavaStackTrace(env, "edu.wpi.first"); + auto pwm = HAL_InitializePWMPort((HAL_PortHandle)id, stack.c_str(), &status); + CheckStatusForceThrow(env, status); return (jint)pwm; } diff --git a/hal/src/main/native/cpp/jni/RelayJNI.cpp b/hal/src/main/native/cpp/jni/RelayJNI.cpp index 0bb7fa45e8..9e31a9da10 100644 --- a/hal/src/main/native/cpp/jni/RelayJNI.cpp +++ b/hal/src/main/native/cpp/jni/RelayJNI.cpp @@ -6,6 +6,8 @@ #include +#include + #include "HALUtil.h" #include "edu_wpi_first_hal_RelayJNI.h" #include "hal/Ports.h" @@ -26,10 +28,10 @@ Java_edu_wpi_first_hal_RelayJNI_initializeRelayPort (JNIEnv* env, jclass, jint id, jboolean fwd) { int32_t status = 0; + auto stack = wpi::java::GetJavaStackTrace(env, "edu.wpi.first"); HAL_RelayHandle handle = HAL_InitializeRelayPort( - (HAL_PortHandle)id, static_cast(fwd), &status); - CheckStatusRange(env, status, 0, HAL_GetNumRelayChannels(), - hal::getPortHandleChannel((HAL_PortHandle)id)); + (HAL_PortHandle)id, static_cast(fwd), stack.c_str(), &status); + CheckStatusForceThrow(env, status); return (jint)handle; } diff --git a/hal/src/main/native/include/hal/AnalogGyro.h b/hal/src/main/native/include/hal/AnalogGyro.h index 79e0f7d16e..16171b1384 100644 --- a/hal/src/main/native/include/hal/AnalogGyro.h +++ b/hal/src/main/native/include/hal/AnalogGyro.h @@ -22,9 +22,12 @@ extern "C" { * Initializes an analog gyro. * * @param handle handle to the analog port + * @param allocationLocation the location where the allocation is occuring + * (can be null) * @return the initialized gyro handle */ HAL_GyroHandle HAL_InitializeAnalogGyro(HAL_AnalogInputHandle handle, + const char* allocationLocation, int32_t* status); /** diff --git a/hal/src/main/native/include/hal/AnalogInput.h b/hal/src/main/native/include/hal/AnalogInput.h index bcfc633000..8275ea26d1 100644 --- a/hal/src/main/native/include/hal/AnalogInput.h +++ b/hal/src/main/native/include/hal/AnalogInput.h @@ -22,10 +22,12 @@ extern "C" { * Initializes the analog input port using the given port object. * * @param portHandle Handle to the port to initialize. + * @param allocationLocation the location where the allocation is occuring + * (can be null) * @return the created analog input handle */ -HAL_AnalogInputHandle HAL_InitializeAnalogInputPort(HAL_PortHandle portHandle, - int32_t* status); +HAL_AnalogInputHandle HAL_InitializeAnalogInputPort( + HAL_PortHandle portHandle, const char* allocationLocation, int32_t* status); /** * Frees an analog input port. diff --git a/hal/src/main/native/include/hal/AnalogOutput.h b/hal/src/main/native/include/hal/AnalogOutput.h index b104315b39..ef013af95e 100644 --- a/hal/src/main/native/include/hal/AnalogOutput.h +++ b/hal/src/main/native/include/hal/AnalogOutput.h @@ -22,10 +22,12 @@ extern "C" { * Initializes the analog output port using the given port object. * * @param handle handle to the port + * @param allocationLocation the location where the allocation is occuring + * (can be null) * @return the created analog output handle */ -HAL_AnalogOutputHandle HAL_InitializeAnalogOutputPort(HAL_PortHandle portHandle, - int32_t* status); +HAL_AnalogOutputHandle HAL_InitializeAnalogOutputPort( + HAL_PortHandle portHandle, const char* allocationLocation, int32_t* status); /** * Frees an analog output port. diff --git a/hal/src/main/native/include/hal/DIO.h b/hal/src/main/native/include/hal/DIO.h index cdfdd354bd..306321478e 100644 --- a/hal/src/main/native/include/hal/DIO.h +++ b/hal/src/main/native/include/hal/DIO.h @@ -21,12 +21,16 @@ extern "C" { /** * Creates a new instance of a digital port. * - * @param portHandle the port handle to create from - * @param input true for input, false for output - * @return the created digital handle + * @param portHandle the port handle to create from + * @param input true for input, false for output + * @param allocationLocation the location where the allocation is occuring + * (can be null) + * @return the created digital handle */ HAL_DigitalHandle HAL_InitializeDIOPort(HAL_PortHandle portHandle, - HAL_Bool input, int32_t* status); + HAL_Bool input, + const char* allocationLocation, + int32_t* status); /** * Checks if a DIO channel is valid. diff --git a/hal/src/main/native/include/hal/Errors.h b/hal/src/main/native/include/hal/Errors.h index 1e87f54cb2..6444ea6c9e 100644 --- a/hal/src/main/native/include/hal/Errors.h +++ b/hal/src/main/native/include/hal/Errors.h @@ -41,6 +41,8 @@ #define ERR_FRCSystem_NoDSConnection_MESSAGE \ "FRCSystem: No driver station connected" +#define HAL_SUCCESS 0 + #define SAMPLE_RATE_TOO_HIGH 1001 #define SAMPLE_RATE_TOO_HIGH_MESSAGE \ "HAL: Analog module sample rate is too high" diff --git a/hal/src/main/native/include/hal/PWM.h b/hal/src/main/native/include/hal/PWM.h index e0fa945c44..04bfc4f73e 100644 --- a/hal/src/main/native/include/hal/PWM.h +++ b/hal/src/main/native/include/hal/PWM.h @@ -22,9 +22,12 @@ extern "C" { * Initializes a PWM port. * * @param portHandle the port to initialize + * @param allocationLocation the location where the allocation is occuring + * (can be null) * @return the created pwm handle */ HAL_DigitalHandle HAL_InitializePWMPort(HAL_PortHandle portHandle, + const char* allocationLocation, int32_t* status); /** diff --git a/hal/src/main/native/include/hal/Relay.h b/hal/src/main/native/include/hal/Relay.h index 5fa714ac9f..d59da0a669 100644 --- a/hal/src/main/native/include/hal/Relay.h +++ b/hal/src/main/native/include/hal/Relay.h @@ -26,9 +26,12 @@ extern "C" { * * @param portHandle the port handle to initialize * @param fwd true for the forward port, false for the reverse port + * @param allocationLocation the location where the allocation is occuring + * (can be null) * @return the created relay handle */ HAL_RelayHandle HAL_InitializeRelayPort(HAL_PortHandle portHandle, HAL_Bool fwd, + const char* allocationLocation, int32_t* status); /** diff --git a/hal/src/main/native/include/hal/handles/DigitalHandleResource.h b/hal/src/main/native/include/hal/handles/DigitalHandleResource.h index 85a14a7d62..5154a43aad 100644 --- a/hal/src/main/native/include/hal/handles/DigitalHandleResource.h +++ b/hal/src/main/native/include/hal/handles/DigitalHandleResource.h @@ -22,7 +22,7 @@ namespace hal { * allows a limited number of handles that are allocated by index. * The enum value is separate, as 2 enum values are allowed per handle * Because they are allocated by index, each individual index holds its own - * mutex, which reduces contention heavily.] + * mutex, which reduces contention heavily. * * @tparam THandle The Handle Type (Must be typedefed from HAL_Handle) * @tparam TStruct The struct type held by this resource @@ -38,7 +38,8 @@ class DigitalHandleResource : public HandleBase { DigitalHandleResource(const DigitalHandleResource&) = delete; DigitalHandleResource& operator=(const DigitalHandleResource&) = delete; - THandle Allocate(int16_t index, HAL_HandleEnum enumValue, int32_t* status); + std::shared_ptr Allocate(int16_t index, HAL_HandleEnum enumValue, + THandle* handle, int32_t* status); int16_t GetIndex(THandle handle, HAL_HandleEnum enumValue) { return getHandleTypedIndex(handle, enumValue, m_version); } @@ -52,21 +53,27 @@ class DigitalHandleResource : public HandleBase { }; template -THandle DigitalHandleResource::Allocate( - int16_t index, HAL_HandleEnum enumValue, int32_t* status) { +std::shared_ptr +DigitalHandleResource::Allocate( + int16_t index, HAL_HandleEnum enumValue, THandle* handle, int32_t* status) { // don't acquire the lock if we can fail early. if (index < 0 || index >= size) { + *handle = HAL_kInvalidHandle; *status = RESOURCE_OUT_OF_RANGE; - return HAL_kInvalidHandle; + return nullptr; } std::scoped_lock lock(m_handleMutexes[index]); // check for allocation, otherwise allocate and return a valid handle if (m_structures[index] != nullptr) { + *handle = HAL_kInvalidHandle; *status = RESOURCE_IS_ALLOCATED; - return HAL_kInvalidHandle; + return m_structures[index]; } m_structures[index] = std::make_shared(); - return static_cast(hal::createHandle(index, enumValue, m_version)); + *handle = + static_cast(hal::createHandle(index, enumValue, m_version)); + *status = HAL_SUCCESS; + return m_structures[index]; } template diff --git a/hal/src/main/native/include/hal/handles/IndexedHandleResource.h b/hal/src/main/native/include/hal/handles/IndexedHandleResource.h index 2f158e1913..2e921eebcf 100644 --- a/hal/src/main/native/include/hal/handles/IndexedHandleResource.h +++ b/hal/src/main/native/include/hal/handles/IndexedHandleResource.h @@ -39,7 +39,8 @@ class IndexedHandleResource : public HandleBase { IndexedHandleResource(const IndexedHandleResource&) = delete; IndexedHandleResource& operator=(const IndexedHandleResource&) = delete; - THandle Allocate(int16_t index, int32_t* status); + std::shared_ptr Allocate(int16_t index, THandle* handle, + int32_t* status); int16_t GetIndex(THandle handle) { return getHandleTypedIndex(handle, enumValue, m_version); } @@ -54,21 +55,27 @@ class IndexedHandleResource : public HandleBase { template -THandle IndexedHandleResource::Allocate( - int16_t index, int32_t* status) { +std::shared_ptr +IndexedHandleResource::Allocate( + int16_t index, THandle* handle, int32_t* status) { // don't acquire the lock if we can fail early. if (index < 0 || index >= size) { *status = RESOURCE_OUT_OF_RANGE; - return HAL_kInvalidHandle; + *handle = HAL_kInvalidHandle; + return nullptr; } std::scoped_lock lock(m_handleMutexes[index]); // check for allocation, otherwise allocate and return a valid handle if (m_structures[index] != nullptr) { *status = RESOURCE_IS_ALLOCATED; - return HAL_kInvalidHandle; + *handle = HAL_kInvalidHandle; + return m_structures[index]; } m_structures[index] = std::make_shared(); - return static_cast(hal::createHandle(index, enumValue, m_version)); + *handle = + static_cast(hal::createHandle(index, enumValue, m_version)); + *status = HAL_SUCCESS; + return m_structures[index]; } template + #include "HALInitializer.h" +#include "HALInternal.h" #include "PortsInternal.h" #include "hal/AnalogAccumulator.h" #include "hal/Errors.h" @@ -15,6 +18,7 @@ namespace { struct AnalogGyro { HAL_AnalogInputHandle handle; uint8_t index; + std::string previousAllocation; }; } // namespace @@ -34,36 +38,41 @@ void InitializeAnalogGyro() { extern "C" { HAL_GyroHandle HAL_InitializeAnalogGyro(HAL_AnalogInputHandle analogHandle, + const char* allocationLocation, int32_t* status) { hal::init::CheckInit(); + // Handle will be type checked by HAL_IsAccumulatorChannel + int16_t channel = getHandleIndex(analogHandle); if (!HAL_IsAccumulatorChannel(analogHandle, status)) { if (*status == 0) { *status = HAL_INVALID_ACCUMULATOR_CHANNEL; + hal::SetLastErrorIndexOutOfRange(status, "Invalid Index for Analog Gyro", + 0, kNumAccumulators, channel); } return HAL_kInvalidHandle; } - // handle known to be correct, so no need to type check - int16_t channel = getHandleIndex(analogHandle); - - auto handle = analogGyroHandles->Allocate(channel, status); + HAL_GyroHandle handle; + auto gyro = analogGyroHandles->Allocate(channel, &handle, status); if (*status != 0) { + if (gyro) { + hal::SetLastErrorPreviouslyAllocated(status, "Analog Gyro", channel, + gyro->previousAllocation); + } else { + hal::SetLastErrorIndexOutOfRange(status, "Invalid Index for Analog Gyro", + 0, kNumAccumulators, channel); + } return HAL_kInvalidHandle; // failed to allocate. Pass error back. } - // Initialize port structure - auto gyro = analogGyroHandles->Get(handle); - if (gyro == nullptr) { // would only error on thread issue - *status = HAL_HANDLE_ERROR; - return HAL_kInvalidHandle; - } - gyro->handle = analogHandle; gyro->index = channel; SimAnalogGyroData[channel].initialized = true; + gyro->previousAllocation = allocationLocation ? allocationLocation : ""; + return handle; } diff --git a/hal/src/main/native/sim/AnalogInput.cpp b/hal/src/main/native/sim/AnalogInput.cpp index 3a9f572f05..4697dc2493 100644 --- a/hal/src/main/native/sim/AnalogInput.cpp +++ b/hal/src/main/native/sim/AnalogInput.cpp @@ -6,6 +6,7 @@ #include "AnalogInternal.h" #include "HALInitializer.h" +#include "HALInternal.h" #include "PortsInternal.h" #include "hal/AnalogAccumulator.h" #include "hal/handles/HandlesInternal.h" @@ -18,28 +19,32 @@ void InitializeAnalogInput() {} } // namespace hal::init extern "C" { -HAL_AnalogInputHandle HAL_InitializeAnalogInputPort(HAL_PortHandle portHandle, - int32_t* status) { +HAL_AnalogInputHandle HAL_InitializeAnalogInputPort( + HAL_PortHandle portHandle, const char* allocationLocation, + int32_t* status) { hal::init::CheckInit(); int16_t channel = getPortHandleChannel(portHandle); - if (channel == InvalidHandleIndex) { - *status = PARAMETER_OUT_OF_RANGE; + if (channel == InvalidHandleIndex || channel >= kNumAnalogInputs) { + *status = RESOURCE_OUT_OF_RANGE; + hal::SetLastErrorIndexOutOfRange(status, "Invalid Index for Analog Input", + 0, kNumAnalogInputs, channel); return HAL_kInvalidHandle; } - HAL_AnalogInputHandle handle = analogInputHandles->Allocate(channel, status); + HAL_AnalogInputHandle handle; + auto analog_port = analogInputHandles->Allocate(channel, &handle, status); if (*status != 0) { + if (analog_port) { + hal::SetLastErrorPreviouslyAllocated(status, "Analog Input", channel, + analog_port->previousAllocation); + } else { + hal::SetLastErrorIndexOutOfRange(status, "Invalid Index for Analog Input", + 0, kNumAnalogInputs, channel); + } return HAL_kInvalidHandle; // failed to allocate. Pass error back. } - // Initialize port structure - auto analog_port = analogInputHandles->Get(handle); - if (analog_port == nullptr) { // would only error on thread issue - *status = HAL_HANDLE_ERROR; - return HAL_kInvalidHandle; - } - analog_port->channel = static_cast(channel); if (HAL_IsAccumulatorChannel(handle, status)) { analog_port->isAccumulator = true; @@ -51,6 +56,9 @@ HAL_AnalogInputHandle HAL_InitializeAnalogInputPort(HAL_PortHandle portHandle, SimAnalogInData[channel].accumulatorInitialized = false; SimAnalogInData[channel].simDevice = 0; + analog_port->previousAllocation = + allocationLocation ? allocationLocation : ""; + return handle; } void HAL_FreeAnalogInputPort(HAL_AnalogInputHandle analogPortHandle) { diff --git a/hal/src/main/native/sim/AnalogInternal.h b/hal/src/main/native/sim/AnalogInternal.h index 835fddd8f1..89cb3593c3 100644 --- a/hal/src/main/native/sim/AnalogInternal.h +++ b/hal/src/main/native/sim/AnalogInternal.h @@ -6,6 +6,8 @@ #include +#include + #include "PortsInternal.h" #include "hal/handles/HandlesInternal.h" #include "hal/handles/IndexedHandleResource.h" @@ -20,6 +22,7 @@ static constexpr uint32_t kAccumulatorChannels[] = {0, 1}; struct AnalogPort { uint8_t channel; bool isAccumulator; + std::string previousAllocation; }; extern IndexedHandleResource + #include "HALInitializer.h" +#include "HALInternal.h" #include "PortsInternal.h" #include "hal/Errors.h" #include "hal/handles/HandlesInternal.h" @@ -16,6 +19,7 @@ using namespace hal; namespace { struct AnalogOutput { uint8_t channel; + std::string previousAllocation; }; } // namespace @@ -33,32 +37,39 @@ void InitializeAnalogOutput() { } // namespace hal::init extern "C" { -HAL_AnalogOutputHandle HAL_InitializeAnalogOutputPort(HAL_PortHandle portHandle, - int32_t* status) { +HAL_AnalogOutputHandle HAL_InitializeAnalogOutputPort( + HAL_PortHandle portHandle, const char* allocationLocation, + int32_t* status) { hal::init::CheckInit(); int16_t channel = getPortHandleChannel(portHandle); - if (channel == InvalidHandleIndex) { - *status = PARAMETER_OUT_OF_RANGE; + if (channel == InvalidHandleIndex || channel >= kNumAnalogOutputs) { + *status = RESOURCE_OUT_OF_RANGE; + hal::SetLastErrorIndexOutOfRange(status, "Invalid Index for Analog Output", + 0, kNumAnalogOutputs, channel); return HAL_kInvalidHandle; } - HAL_AnalogOutputHandle handle = - analogOutputHandles->Allocate(channel, status); + HAL_AnalogOutputHandle handle; + auto port = analogOutputHandles->Allocate(channel, &handle, status); if (*status != 0) { + if (port) { + hal::SetLastErrorPreviouslyAllocated(status, "Analog Output", channel, + port->previousAllocation); + } else { + hal::SetLastErrorIndexOutOfRange(status, + "Invalid Index for Analog Output", 0, + kNumAnalogOutputs, channel); + } return HAL_kInvalidHandle; // failed to allocate. Pass error back. } - auto port = analogOutputHandles->Get(handle); - if (port == nullptr) { // would only error on thread issue - *status = HAL_HANDLE_ERROR; - return HAL_kInvalidHandle; - } - port->channel = static_cast(channel); // Initialize sim analog input SimAnalogOutData[channel].initialized = true; + + port->previousAllocation = allocationLocation ? allocationLocation : ""; return handle; } diff --git a/hal/src/main/native/sim/DIO.cpp b/hal/src/main/native/sim/DIO.cpp index 4cb3d38810..0b85cd83dc 100644 --- a/hal/src/main/native/sim/DIO.cpp +++ b/hal/src/main/native/sim/DIO.cpp @@ -6,6 +6,7 @@ #include "DigitalInternal.h" #include "HALInitializer.h" +#include "HALInternal.h" #include "PortsInternal.h" #include "hal/handles/HandlesInternal.h" #include "hal/handles/LimitedHandleResource.h" @@ -31,36 +32,41 @@ void InitializeDIO() { extern "C" { HAL_DigitalHandle HAL_InitializeDIOPort(HAL_PortHandle portHandle, - HAL_Bool input, int32_t* status) { + HAL_Bool input, + const char* allocationLocation, + int32_t* status) { hal::init::CheckInit(); - if (*status != 0) { - return HAL_kInvalidHandle; - } int16_t channel = getPortHandleChannel(portHandle); if (channel == InvalidHandleIndex) { - *status = PARAMETER_OUT_OF_RANGE; + *status = RESOURCE_OUT_OF_RANGE; + hal::SetLastErrorIndexOutOfRange(status, "Invalid Index for DIO", 0, + kNumDigitalChannels, channel); return HAL_kInvalidHandle; } - auto handle = - digitalChannelHandles->Allocate(channel, HAL_HandleEnum::DIO, status); + HAL_DigitalHandle handle; + + auto port = digitalChannelHandles->Allocate(channel, HAL_HandleEnum::DIO, + &handle, status); if (*status != 0) { + if (port) { + hal::SetLastErrorPreviouslyAllocated(status, "PWM or DIO", channel, + port->previousAllocation); + } else { + hal::SetLastErrorIndexOutOfRange(status, "Invalid Index for DIO", 0, + kNumDigitalChannels, channel); + } return HAL_kInvalidHandle; // failed to allocate. Pass error back. } - auto port = digitalChannelHandles->Get(handle, HAL_HandleEnum::DIO); - if (port == nullptr) { // would only occur on thread issue. - *status = HAL_HANDLE_ERROR; - return HAL_kInvalidHandle; - } - port->channel = static_cast(channel); SimDIOData[channel].initialized = true; SimDIOData[channel].isInput = input; SimDIOData[channel].simDevice = 0; + port->previousAllocation = allocationLocation ? allocationLocation : ""; return handle; } diff --git a/hal/src/main/native/sim/DigitalInternal.h b/hal/src/main/native/sim/DigitalInternal.h index 080eb6fbe8..cd1ac5fcb5 100644 --- a/hal/src/main/native/sim/DigitalInternal.h +++ b/hal/src/main/native/sim/DigitalInternal.h @@ -6,6 +6,8 @@ #include +#include + #include "PortsInternal.h" #include "hal/AnalogTrigger.h" #include "hal/handles/DigitalHandleResource.h" @@ -56,6 +58,7 @@ struct DigitalPort { int32_t centerPwm = 0; int32_t deadbandMinPwm = 0; int32_t minPwm = 0; + std::string previousAllocation; }; extern DigitalHandleResource + +#include + +namespace hal { +void SetLastError(int32_t* status, const wpi::Twine& value); +void SetLastErrorIndexOutOfRange(int32_t* status, const wpi::Twine& message, + int32_t minimum, int32_t maximum, + int32_t channel); +void SetLastErrorPreviouslyAllocated(int32_t* status, const wpi::Twine& message, + int32_t channel, + const wpi::Twine& previousAllocation); +} // namespace hal diff --git a/hal/src/main/native/sim/PWM.cpp b/hal/src/main/native/sim/PWM.cpp index 06105e9981..698769fede 100644 --- a/hal/src/main/native/sim/PWM.cpp +++ b/hal/src/main/native/sim/PWM.cpp @@ -7,6 +7,7 @@ #include "ConstantsInternal.h" #include "DigitalInternal.h" #include "HALInitializer.h" +#include "HALInternal.h" #include "PortsInternal.h" #include "hal/handles/HandlesInternal.h" #include "mockdata/PWMDataInternal.h" @@ -20,15 +21,15 @@ void InitializePWM() {} extern "C" { HAL_DigitalHandle HAL_InitializePWMPort(HAL_PortHandle portHandle, + const char* allocationLocation, int32_t* status) { hal::init::CheckInit(); - if (*status != 0) { - return HAL_kInvalidHandle; - } int16_t channel = getPortHandleChannel(portHandle); if (channel == InvalidHandleIndex) { - *status = PARAMETER_OUT_OF_RANGE; + *status = RESOURCE_OUT_OF_RANGE; + hal::SetLastErrorIndexOutOfRange(status, "Invalid Index for PWM", 0, + kNumPWMChannels, channel); return HAL_kInvalidHandle; } @@ -40,19 +41,22 @@ HAL_DigitalHandle HAL_InitializePWMPort(HAL_PortHandle portHandle, channel = remapMXPPWMChannel(channel) + 10; // remap MXP to proper channel } - auto handle = - digitalChannelHandles->Allocate(channel, HAL_HandleEnum::PWM, status); + HAL_DigitalHandle handle; + + auto port = digitalChannelHandles->Allocate(channel, HAL_HandleEnum::PWM, + &handle, status); if (*status != 0) { + if (port) { + hal::SetLastErrorPreviouslyAllocated(status, "PWM or DIO", channel, + port->previousAllocation); + } else { + hal::SetLastErrorIndexOutOfRange(status, "Invalid Index for PWM", 0, + kNumPWMChannels, channel); + } return HAL_kInvalidHandle; // failed to allocate. Pass error back. } - auto port = digitalChannelHandles->Get(handle, HAL_HandleEnum::PWM); - if (port == nullptr) { // would only occur on thread issue. - *status = HAL_HANDLE_ERROR; - return HAL_kInvalidHandle; - } - port->channel = origChannel; SimPWMData[origChannel].initialized = true; @@ -60,6 +64,8 @@ HAL_DigitalHandle HAL_InitializePWMPort(HAL_PortHandle portHandle, // Defaults to allow an always valid config. HAL_SetPWMConfig(handle, 2.0, 1.501, 1.5, 1.499, 1.0, status); + port->previousAllocation = allocationLocation ? allocationLocation : ""; + return handle; } void HAL_FreePWMPort(HAL_DigitalHandle pwmPortHandle, int32_t* status) { diff --git a/hal/src/main/native/sim/Relay.cpp b/hal/src/main/native/sim/Relay.cpp index 7ba0438d50..4dfcdaf2a4 100644 --- a/hal/src/main/native/sim/Relay.cpp +++ b/hal/src/main/native/sim/Relay.cpp @@ -4,7 +4,10 @@ #include "hal/Relay.h" +#include + #include "HALInitializer.h" +#include "HALInternal.h" #include "PortsInternal.h" #include "hal/handles/IndexedHandleResource.h" #include "mockdata/RelayDataInternal.h" @@ -15,6 +18,7 @@ namespace { struct Relay { uint8_t channel; bool fwd; + std::string previousAllocation; }; } // namespace @@ -32,6 +36,7 @@ void InitializeRelay() { extern "C" { HAL_RelayHandle HAL_InitializeRelayPort(HAL_PortHandle portHandle, HAL_Bool fwd, + const char* allocationLocation, int32_t* status) { hal::init::CheckInit(); if (*status != 0) { @@ -39,8 +44,10 @@ HAL_RelayHandle HAL_InitializeRelayPort(HAL_PortHandle portHandle, HAL_Bool fwd, } int16_t channel = getPortHandleChannel(portHandle); - if (channel == InvalidHandleIndex) { - *status = PARAMETER_OUT_OF_RANGE; + if (channel == InvalidHandleIndex || channel >= kNumRelayChannels) { + *status = RESOURCE_OUT_OF_RANGE; + hal::SetLastErrorIndexOutOfRange(status, "Invalid Index for Relay", 0, + kNumRelayChannels, channel); return HAL_kInvalidHandle; } @@ -48,18 +55,20 @@ HAL_RelayHandle HAL_InitializeRelayPort(HAL_PortHandle portHandle, HAL_Bool fwd, channel += kNumRelayHeaders; // add 4 to reverse channels } - auto handle = relayHandles->Allocate(channel, status); + HAL_RelayHandle handle; + auto port = relayHandles->Allocate(channel, &handle, status); if (*status != 0) { + if (port) { + hal::SetLastErrorPreviouslyAllocated(status, "Relay", channel, + port->previousAllocation); + } else { + hal::SetLastErrorIndexOutOfRange(status, "Invalid Index for Relay", 0, + kNumRelayChannels, channel); + } return HAL_kInvalidHandle; // failed to allocate. Pass error back. } - auto port = relayHandles->Get(handle); - if (port == nullptr) { // would only occur on thread issue. - *status = HAL_HANDLE_ERROR; - return HAL_kInvalidHandle; - } - if (!fwd) { // Subtract number of headers to put channel in range channel -= kNumRelayHeaders; @@ -73,6 +82,7 @@ HAL_RelayHandle HAL_InitializeRelayPort(HAL_PortHandle portHandle, HAL_Bool fwd, } port->channel = static_cast(channel); + port->previousAllocation = allocationLocation ? allocationLocation : ""; return handle; } diff --git a/hal/src/main/native/sim/Solenoid.cpp b/hal/src/main/native/sim/Solenoid.cpp index 81be01a351..e59b393562 100644 --- a/hal/src/main/native/sim/Solenoid.cpp +++ b/hal/src/main/native/sim/Solenoid.cpp @@ -55,17 +55,13 @@ HAL_SolenoidHandle HAL_InitializeSolenoidPort(HAL_PortHandle portHandle, return HAL_kInvalidHandle; } - auto handle = solenoidHandles->Allocate( - module * kNumSolenoidChannels + channel, status); + HAL_SolenoidHandle handle; + auto solenoidPort = solenoidHandles->Allocate( + module * kNumSolenoidChannels + channel, &handle, status); if (handle == HAL_kInvalidHandle) { // out of resources *status = NO_AVAILABLE_RESOURCES; return HAL_kInvalidHandle; } - auto solenoidPort = solenoidHandles->Get(handle); - if (solenoidPort == nullptr) { // would only occur on thread issues - *status = HAL_HANDLE_ERROR; - return HAL_kInvalidHandle; - } solenoidPort->module = static_cast(module); solenoidPort->channel = static_cast(channel); diff --git a/hal/src/test/native/cpp/mockdata/AnalogInDataTests.cpp b/hal/src/test/native/cpp/mockdata/AnalogInDataTests.cpp index d9fbe01389..c657ebe36d 100644 --- a/hal/src/test/native/cpp/mockdata/AnalogInDataTests.cpp +++ b/hal/src/test/native/cpp/mockdata/AnalogInDataTests.cpp @@ -36,16 +36,18 @@ TEST(AnalogInSimTests, TestAnalogInInitialization) { status = 0; portHandle = 8000; gTestAnalogInCallbackName = "Unset"; - analogInHandle = HAL_InitializeAnalogInputPort(portHandle, &status); + analogInHandle = HAL_InitializeAnalogInputPort(portHandle, nullptr, &status); EXPECT_EQ(HAL_kInvalidHandle, analogInHandle); - EXPECT_EQ(PARAMETER_OUT_OF_RANGE, status); + EXPECT_EQ(HAL_USE_LAST_ERROR, status); + HAL_GetLastError(&status); + EXPECT_EQ(RESOURCE_OUT_OF_RANGE, status); EXPECT_STREQ("Unset", gTestAnalogInCallbackName.c_str()); // Successful setup status = 0; portHandle = HAL_GetPort(INDEX_TO_TEST); gTestAnalogInCallbackName = "Unset"; - analogInHandle = HAL_InitializeAnalogInputPort(portHandle, &status); + analogInHandle = HAL_InitializeAnalogInputPort(portHandle, nullptr, &status); EXPECT_TRUE(HAL_kInvalidHandle != analogInHandle); EXPECT_EQ(0, status); EXPECT_STREQ("Initialized", gTestAnalogInCallbackName.c_str()); @@ -54,8 +56,10 @@ TEST(AnalogInSimTests, TestAnalogInInitialization) { status = 0; portHandle = HAL_GetPort(INDEX_TO_TEST); gTestAnalogInCallbackName = "Unset"; - analogInHandle = HAL_InitializeAnalogInputPort(portHandle, &status); + analogInHandle = HAL_InitializeAnalogInputPort(portHandle, nullptr, &status); EXPECT_EQ(HAL_kInvalidHandle, analogInHandle); + EXPECT_EQ(HAL_USE_LAST_ERROR, status); + HAL_GetLastError(&status); EXPECT_EQ(RESOURCE_IS_ALLOCATED, status); EXPECT_STREQ("Unset", gTestAnalogInCallbackName.c_str()); @@ -69,7 +73,7 @@ TEST(AnalogInSimTests, TestAnalogInInitialization) { status = 0; portHandle = HAL_GetPort(INDEX_TO_TEST); gTestAnalogInCallbackName = "Unset"; - analogInHandle = HAL_InitializeAnalogInputPort(portHandle, &status); + analogInHandle = HAL_InitializeAnalogInputPort(portHandle, nullptr, &status); EXPECT_TRUE(HAL_kInvalidHandle != analogInHandle); EXPECT_EQ(0, status); EXPECT_STREQ("Initialized", gTestAnalogInCallbackName.c_str()); diff --git a/hal/src/test/native/cpp/mockdata/AnalogOutDataTests.cpp b/hal/src/test/native/cpp/mockdata/AnalogOutDataTests.cpp index 16e1d9d957..1ec14f4f19 100644 --- a/hal/src/test/native/cpp/mockdata/AnalogOutDataTests.cpp +++ b/hal/src/test/native/cpp/mockdata/AnalogOutDataTests.cpp @@ -36,16 +36,20 @@ TEST(AnalogOutSimTests, TestAnalogOutInitialization) { status = 0; portHandle = 8000; gTestAnalogOutCallbackName = "Unset"; - analogOutHandle = HAL_InitializeAnalogOutputPort(portHandle, &status); + analogOutHandle = + HAL_InitializeAnalogOutputPort(portHandle, nullptr, &status); EXPECT_EQ(HAL_kInvalidHandle, analogOutHandle); - EXPECT_EQ(PARAMETER_OUT_OF_RANGE, status); + EXPECT_EQ(HAL_USE_LAST_ERROR, status); + HAL_GetLastError(&status); + EXPECT_EQ(RESOURCE_OUT_OF_RANGE, status); EXPECT_STREQ("Unset", gTestAnalogOutCallbackName.c_str()); // Successful setup status = 0; portHandle = HAL_GetPort(INDEX_TO_TEST); gTestAnalogOutCallbackName = "Unset"; - analogOutHandle = HAL_InitializeAnalogOutputPort(portHandle, &status); + analogOutHandle = + HAL_InitializeAnalogOutputPort(portHandle, nullptr, &status); EXPECT_TRUE(HAL_kInvalidHandle != analogOutHandle); EXPECT_EQ(0, status); EXPECT_STREQ("Initialized", gTestAnalogOutCallbackName.c_str()); @@ -54,8 +58,11 @@ TEST(AnalogOutSimTests, TestAnalogOutInitialization) { status = 0; portHandle = HAL_GetPort(INDEX_TO_TEST); gTestAnalogOutCallbackName = "Unset"; - analogOutHandle = HAL_InitializeAnalogOutputPort(portHandle, &status); + analogOutHandle = + HAL_InitializeAnalogOutputPort(portHandle, nullptr, &status); EXPECT_EQ(HAL_kInvalidHandle, analogOutHandle); + EXPECT_EQ(HAL_USE_LAST_ERROR, status); + HAL_GetLastError(&status); EXPECT_EQ(RESOURCE_IS_ALLOCATED, status); EXPECT_STREQ("Unset", gTestAnalogOutCallbackName.c_str()); @@ -69,7 +76,8 @@ TEST(AnalogOutSimTests, TestAnalogOutInitialization) { status = 0; portHandle = HAL_GetPort(INDEX_TO_TEST); gTestAnalogOutCallbackName = "Unset"; - analogOutHandle = HAL_InitializeAnalogOutputPort(portHandle, &status); + analogOutHandle = + HAL_InitializeAnalogOutputPort(portHandle, nullptr, &status); EXPECT_TRUE(HAL_kInvalidHandle != analogOutHandle); EXPECT_EQ(0, status); EXPECT_STREQ("Initialized", gTestAnalogOutCallbackName.c_str()); diff --git a/hal/src/test/native/cpp/mockdata/DIODataTests.cpp b/hal/src/test/native/cpp/mockdata/DIODataTests.cpp index d584b45ec0..bc1345b091 100644 --- a/hal/src/test/native/cpp/mockdata/DIODataTests.cpp +++ b/hal/src/test/native/cpp/mockdata/DIODataTests.cpp @@ -36,16 +36,18 @@ TEST(DigitalIoSimTests, TestDigitalIoInitialization) { status = 0; portHandle = 8000; gTestDigitalIoCallbackName = "Unset"; - digitalIoHandle = HAL_InitializeDIOPort(portHandle, true, &status); + digitalIoHandle = HAL_InitializeDIOPort(portHandle, true, nullptr, &status); EXPECT_EQ(HAL_kInvalidHandle, digitalIoHandle); - EXPECT_EQ(PARAMETER_OUT_OF_RANGE, status); + EXPECT_EQ(HAL_USE_LAST_ERROR, status); + HAL_GetLastError(&status); + EXPECT_EQ(RESOURCE_OUT_OF_RANGE, status); EXPECT_STREQ("Unset", gTestDigitalIoCallbackName.c_str()); // Successful setup status = 0; portHandle = HAL_GetPort(INDEX_TO_TEST); gTestDigitalIoCallbackName = "Unset"; - digitalIoHandle = HAL_InitializeDIOPort(portHandle, true, &status); + digitalIoHandle = HAL_InitializeDIOPort(portHandle, true, nullptr, &status); EXPECT_TRUE(HAL_kInvalidHandle != digitalIoHandle); EXPECT_EQ(0, status); EXPECT_STREQ("Initialized", gTestDigitalIoCallbackName.c_str()); @@ -54,8 +56,10 @@ TEST(DigitalIoSimTests, TestDigitalIoInitialization) { status = 0; portHandle = HAL_GetPort(INDEX_TO_TEST); gTestDigitalIoCallbackName = "Unset"; - digitalIoHandle = HAL_InitializeDIOPort(portHandle, true, &status); + digitalIoHandle = HAL_InitializeDIOPort(portHandle, true, nullptr, &status); EXPECT_EQ(HAL_kInvalidHandle, digitalIoHandle); + EXPECT_EQ(HAL_USE_LAST_ERROR, status); + HAL_GetLastError(&status); EXPECT_EQ(RESOURCE_IS_ALLOCATED, status); EXPECT_STREQ("Unset", gTestDigitalIoCallbackName.c_str()); @@ -69,7 +73,7 @@ TEST(DigitalIoSimTests, TestDigitalIoInitialization) { status = 0; portHandle = HAL_GetPort(INDEX_TO_TEST); gTestDigitalIoCallbackName = "Unset"; - digitalIoHandle = HAL_InitializeDIOPort(portHandle, true, &status); + digitalIoHandle = HAL_InitializeDIOPort(portHandle, true, nullptr, &status); EXPECT_TRUE(HAL_kInvalidHandle != digitalIoHandle); EXPECT_EQ(0, status); EXPECT_STREQ("Initialized", gTestDigitalIoCallbackName.c_str()); diff --git a/hal/src/test/native/cpp/mockdata/PWMDataTests.cpp b/hal/src/test/native/cpp/mockdata/PWMDataTests.cpp index fb717971ac..c0bb7f0b31 100644 --- a/hal/src/test/native/cpp/mockdata/PWMDataTests.cpp +++ b/hal/src/test/native/cpp/mockdata/PWMDataTests.cpp @@ -35,16 +35,18 @@ TEST(PWMSimTests, TestPwmInitialization) { status = 0; portHandle = 8000; gTestPwmCallbackName = "Unset"; - pwmHandle = HAL_InitializePWMPort(portHandle, &status); + pwmHandle = HAL_InitializePWMPort(portHandle, nullptr, &status); EXPECT_EQ(HAL_kInvalidHandle, pwmHandle); - EXPECT_EQ(PARAMETER_OUT_OF_RANGE, status); + EXPECT_EQ(HAL_USE_LAST_ERROR, status); + HAL_GetLastError(&status); + EXPECT_EQ(RESOURCE_OUT_OF_RANGE, status); EXPECT_STREQ("Unset", gTestPwmCallbackName.c_str()); // Successful setup status = 0; portHandle = HAL_GetPort(INDEX_TO_TEST); gTestPwmCallbackName = "Unset"; - pwmHandle = HAL_InitializePWMPort(portHandle, &status); + pwmHandle = HAL_InitializePWMPort(portHandle, nullptr, &status); EXPECT_TRUE(HAL_kInvalidHandle != pwmHandle); EXPECT_EQ(0, status); EXPECT_STREQ("Initialized", gTestPwmCallbackName.c_str()); @@ -53,8 +55,10 @@ TEST(PWMSimTests, TestPwmInitialization) { status = 0; portHandle = HAL_GetPort(INDEX_TO_TEST); gTestPwmCallbackName = "Unset"; - pwmHandle = HAL_InitializePWMPort(portHandle, &status); + pwmHandle = HAL_InitializePWMPort(portHandle, nullptr, &status); EXPECT_EQ(HAL_kInvalidHandle, pwmHandle); + EXPECT_EQ(HAL_USE_LAST_ERROR, status); + HAL_GetLastError(&status); EXPECT_EQ(RESOURCE_IS_ALLOCATED, status); EXPECT_STREQ("Unset", gTestPwmCallbackName.c_str()); @@ -67,7 +71,7 @@ TEST(PWMSimTests, TestPwmInitialization) { status = 0; portHandle = HAL_GetPort(INDEX_TO_TEST); gTestPwmCallbackName = "Unset"; - pwmHandle = HAL_InitializePWMPort(portHandle, &status); + pwmHandle = HAL_InitializePWMPort(portHandle, nullptr, &status); EXPECT_TRUE(HAL_kInvalidHandle != pwmHandle); EXPECT_EQ(0, status); EXPECT_STREQ("Initialized", gTestPwmCallbackName.c_str()); diff --git a/hal/src/test/native/cpp/mockdata/RelayDataTests.cpp b/hal/src/test/native/cpp/mockdata/RelayDataTests.cpp index 6e5004667d..af85318006 100644 --- a/hal/src/test/native/cpp/mockdata/RelayDataTests.cpp +++ b/hal/src/test/native/cpp/mockdata/RelayDataTests.cpp @@ -35,16 +35,18 @@ TEST(RelaySimTests, TestRelayInitialization) { status = 0; portHandle = 8000; gTestRelayCallbackName = "Unset"; - pdpHandle = HAL_InitializeRelayPort(portHandle, true, &status); + pdpHandle = HAL_InitializeRelayPort(portHandle, true, nullptr, &status); EXPECT_EQ(HAL_kInvalidHandle, pdpHandle); - EXPECT_EQ(PARAMETER_OUT_OF_RANGE, status); + EXPECT_EQ(HAL_USE_LAST_ERROR, status); + HAL_GetLastError(&status); + EXPECT_EQ(RESOURCE_OUT_OF_RANGE, status); EXPECT_STREQ("Unset", gTestRelayCallbackName.c_str()); // Successful setup status = 0; portHandle = HAL_GetPort(INDEX_TO_TEST); gTestRelayCallbackName = "Unset"; - pdpHandle = HAL_InitializeRelayPort(portHandle, true, &status); + pdpHandle = HAL_InitializeRelayPort(portHandle, true, nullptr, &status); EXPECT_TRUE(HAL_kInvalidHandle != pdpHandle); EXPECT_EQ(0, status); EXPECT_STREQ("InitializedForward", gTestRelayCallbackName.c_str()); @@ -53,8 +55,10 @@ TEST(RelaySimTests, TestRelayInitialization) { status = 0; portHandle = HAL_GetPort(INDEX_TO_TEST); gTestRelayCallbackName = "Unset"; - pdpHandle = HAL_InitializeRelayPort(portHandle, true, &status); + pdpHandle = HAL_InitializeRelayPort(portHandle, true, nullptr, &status); EXPECT_EQ(HAL_kInvalidHandle, pdpHandle); + EXPECT_EQ(HAL_USE_LAST_ERROR, status); + HAL_GetLastError(&status); EXPECT_EQ(RESOURCE_IS_ALLOCATED, status); EXPECT_STREQ("Unset", gTestRelayCallbackName.c_str()); @@ -67,7 +71,7 @@ TEST(RelaySimTests, TestRelayInitialization) { status = 0; portHandle = HAL_GetPort(INDEX_TO_TEST); gTestRelayCallbackName = "Unset"; - pdpHandle = HAL_InitializeRelayPort(portHandle, true, &status); + pdpHandle = HAL_InitializeRelayPort(portHandle, true, nullptr, &status); EXPECT_TRUE(HAL_kInvalidHandle != pdpHandle); EXPECT_EQ(0, status); EXPECT_STREQ("InitializedForward", gTestRelayCallbackName.c_str()); diff --git a/wpilibc/src/main/native/cpp/AddressableLED.cpp b/wpilibc/src/main/native/cpp/AddressableLED.cpp index db297ffe46..6b8a751c19 100644 --- a/wpilibc/src/main/native/cpp/AddressableLED.cpp +++ b/wpilibc/src/main/native/cpp/AddressableLED.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include "frc/Errors.h" @@ -17,7 +18,9 @@ using namespace frc; AddressableLED::AddressableLED(int port) { int32_t status = 0; - m_pwmHandle = HAL_InitializePWMPort(HAL_GetPort(port), &status); + auto stack = wpi::GetStackTrace(1); + m_pwmHandle = + HAL_InitializePWMPort(HAL_GetPort(port), stack.c_str(), &status); FRC_CheckErrorStatus(status, "Port " + wpi::Twine{port}); if (m_pwmHandle == HAL_kInvalidHandle) { return; diff --git a/wpilibc/src/main/native/cpp/AnalogGyro.cpp b/wpilibc/src/main/native/cpp/AnalogGyro.cpp index 504c27384b..cdbf0e0254 100644 --- a/wpilibc/src/main/native/cpp/AnalogGyro.cpp +++ b/wpilibc/src/main/native/cpp/AnalogGyro.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include "frc/AnalogInput.h" #include "frc/Base.h" @@ -111,7 +112,9 @@ void AnalogGyro::Reset() { void AnalogGyro::InitGyro() { if (m_gyroHandle == HAL_kInvalidHandle) { int32_t status = 0; - m_gyroHandle = HAL_InitializeAnalogGyro(m_analog->m_port, &status); + std::string stackTrace = wpi::GetStackTrace(1); + m_gyroHandle = + HAL_InitializeAnalogGyro(m_analog->m_port, stackTrace.c_str(), &status); if (status == PARAMETER_OUT_OF_RANGE) { throw FRC_MakeError(err::ParameterOutOfRange, "channel must be accumulator channel"); diff --git a/wpilibc/src/main/native/cpp/AnalogInput.cpp b/wpilibc/src/main/native/cpp/AnalogInput.cpp index e9191d8b92..dee97343ab 100644 --- a/wpilibc/src/main/native/cpp/AnalogInput.cpp +++ b/wpilibc/src/main/native/cpp/AnalogInput.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include "frc/Errors.h" #include "frc/SensorUtil.h" @@ -28,7 +29,8 @@ AnalogInput::AnalogInput(int channel) { HAL_PortHandle port = HAL_GetPort(channel); int32_t status = 0; - m_port = HAL_InitializeAnalogInputPort(port, &status); + std::string stackTrace = wpi::GetStackTrace(1); + m_port = HAL_InitializeAnalogInputPort(port, stackTrace.c_str(), &status); FRC_CheckErrorStatus(status, "Analog Input " + wpi::Twine{channel}); HAL_Report(HALUsageReporting::kResourceType_AnalogChannel, channel + 1); diff --git a/wpilibc/src/main/native/cpp/AnalogOutput.cpp b/wpilibc/src/main/native/cpp/AnalogOutput.cpp index 1c86d17234..ce827f5a75 100644 --- a/wpilibc/src/main/native/cpp/AnalogOutput.cpp +++ b/wpilibc/src/main/native/cpp/AnalogOutput.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include "frc/Errors.h" #include "frc/SensorUtil.h" @@ -29,7 +30,8 @@ AnalogOutput::AnalogOutput(int channel) { HAL_PortHandle port = HAL_GetPort(m_channel); int32_t status = 0; - m_port = HAL_InitializeAnalogOutputPort(port, &status); + std::string stackTrace = wpi::GetStackTrace(1); + m_port = HAL_InitializeAnalogOutputPort(port, stackTrace.c_str(), &status); FRC_CheckErrorStatus(status, "analog output " + wpi::Twine(channel)); HAL_Report(HALUsageReporting::kResourceType_AnalogOutput, m_channel + 1); diff --git a/wpilibc/src/main/native/cpp/DigitalInput.cpp b/wpilibc/src/main/native/cpp/DigitalInput.cpp index 936067ad0b..b97e36cc46 100644 --- a/wpilibc/src/main/native/cpp/DigitalInput.cpp +++ b/wpilibc/src/main/native/cpp/DigitalInput.cpp @@ -4,12 +4,14 @@ #include "frc/DigitalInput.h" +#include #include #include #include #include #include +#include #include "frc/Errors.h" #include "frc/SensorUtil.h" @@ -26,7 +28,9 @@ DigitalInput::DigitalInput(int channel) { m_channel = channel; int32_t status = 0; - m_handle = HAL_InitializeDIOPort(HAL_GetPort(channel), true, &status); + std::string stackTrace = wpi::GetStackTrace(1); + m_handle = HAL_InitializeDIOPort(HAL_GetPort(channel), true, + stackTrace.c_str(), &status); FRC_CheckErrorStatus(status, "Digital Channel " + wpi::Twine{channel}); HAL_Report(HALUsageReporting::kResourceType_DigitalInput, channel + 1); diff --git a/wpilibc/src/main/native/cpp/DigitalOutput.cpp b/wpilibc/src/main/native/cpp/DigitalOutput.cpp index 41c5e4e1ec..351ac07ed5 100644 --- a/wpilibc/src/main/native/cpp/DigitalOutput.cpp +++ b/wpilibc/src/main/native/cpp/DigitalOutput.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include "frc/Errors.h" #include "frc/SensorUtil.h" @@ -27,7 +28,9 @@ DigitalOutput::DigitalOutput(int channel) { m_channel = channel; int32_t status = 0; - m_handle = HAL_InitializeDIOPort(HAL_GetPort(channel), false, &status); + std::string stackTrace = wpi::GetStackTrace(1); + m_handle = HAL_InitializeDIOPort(HAL_GetPort(channel), false, + stackTrace.c_str(), &status); FRC_CheckErrorStatus(status, "Digital Channel " + wpi::Twine{channel}); HAL_Report(HALUsageReporting::kResourceType_DigitalOutput, channel + 1); diff --git a/wpilibc/src/main/native/cpp/PWM.cpp b/wpilibc/src/main/native/cpp/PWM.cpp index d9c0a27454..b9aa1d8ffa 100644 --- a/wpilibc/src/main/native/cpp/PWM.cpp +++ b/wpilibc/src/main/native/cpp/PWM.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include "frc/Errors.h" #include "frc/SensorUtil.h" @@ -26,8 +27,10 @@ PWM::PWM(int channel, bool registerSendable) { return; } + auto stack = wpi::GetStackTrace(1); int32_t status = 0; - m_handle = HAL_InitializePWMPort(HAL_GetPort(channel), &status); + m_handle = + HAL_InitializePWMPort(HAL_GetPort(channel), stack.c_str(), &status); FRC_CheckErrorStatus(status, "PWM Channel " + wpi::Twine{channel}); m_channel = channel; diff --git a/wpilibc/src/main/native/cpp/Relay.cpp b/wpilibc/src/main/native/cpp/Relay.cpp index 66c63c3516..b5037b327d 100644 --- a/wpilibc/src/main/native/cpp/Relay.cpp +++ b/wpilibc/src/main/native/cpp/Relay.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include "frc/Errors.h" @@ -31,13 +32,17 @@ Relay::Relay(int channel, Relay::Direction direction) if (m_direction == kBothDirections || m_direction == kForwardOnly) { int32_t status = 0; - m_forwardHandle = HAL_InitializeRelayPort(portHandle, true, &status); + std::string stackTrace = wpi::GetStackTrace(1); + m_forwardHandle = + HAL_InitializeRelayPort(portHandle, true, stackTrace.c_str(), &status); FRC_CheckErrorStatus(status, "Relay Channel " + wpi::Twine{m_channel}); HAL_Report(HALUsageReporting::kResourceType_Relay, m_channel + 1); } if (m_direction == kBothDirections || m_direction == kReverseOnly) { int32_t status = 0; - m_reverseHandle = HAL_InitializeRelayPort(portHandle, false, &status); + std::string stackTrace = wpi::GetStackTrace(1); + m_reverseHandle = + HAL_InitializeRelayPort(portHandle, false, stackTrace.c_str(), &status); FRC_CheckErrorStatus(status, "Relay Channel " + wpi::Twine{m_channel}); HAL_Report(HALUsageReporting::kResourceType_Relay, m_channel + 128); } diff --git a/wpilibcExamples/src/main/cpp/examples/HAL/c/Robot.c b/wpilibcExamples/src/main/cpp/examples/HAL/c/Robot.c index b7c0826bad..0db402bf76 100644 --- a/wpilibcExamples/src/main/cpp/examples/HAL/c/Robot.c +++ b/wpilibcExamples/src/main/cpp/examples/HAL/c/Robot.c @@ -62,7 +62,8 @@ int main(void) { // Create a Motor Controller status = 0; - HAL_DigitalHandle pwmPort = HAL_InitializePWMPort(HAL_GetPort(2), &status); + HAL_DigitalHandle pwmPort = + HAL_InitializePWMPort(HAL_GetPort(2), NULL, &status); if (status != 0) { const char* message = HAL_GetLastError(&status); @@ -75,7 +76,8 @@ int main(void) { // Create an Input status = 0; - HAL_DigitalHandle dio = HAL_InitializeDIOPort(HAL_GetPort(2), 1, &status); + HAL_DigitalHandle dio = + HAL_InitializeDIOPort(HAL_GetPort(2), 1, NULL, &status); if (status != 0) { const char* message = HAL_GetLastError(&status); diff --git a/wpiutil/src/main/native/include/wpi/jni_util.h b/wpiutil/src/main/native/include/wpi/jni_util.h index df3075c390..11e8f542c2 100644 --- a/wpiutil/src/main/native/include/wpi/jni_util.h +++ b/wpiutil/src/main/native/include/wpi/jni_util.h @@ -593,6 +593,81 @@ class JSingletonCallbackManager : public JCallbackManager { } }; +inline std::string GetJavaStackTrace(JNIEnv* env, StringRef skipPrefix) { + // create a throwable + static JClass throwableCls(env, "java/lang/Throwable"); + if (!throwableCls) { + return ""; + } + static jmethodID constructorId = nullptr; + if (!constructorId) { + constructorId = env->GetMethodID(throwableCls, "", "()V"); + } + JLocal throwable(env, env->NewObject(throwableCls, constructorId)); + + // retrieve information from the exception. + // get method id + // getStackTrace returns an array of StackTraceElement + static jmethodID getStackTraceId = nullptr; + if (!getStackTraceId) { + getStackTraceId = env->GetMethodID(throwableCls, "getStackTrace", + "()[Ljava/lang/StackTraceElement;"); + } + + // call getStackTrace + JLocal stackTrace( + env, static_cast( + env->CallObjectMethod(throwable, getStackTraceId))); + + if (!stackTrace) { + return ""; + } + + // get length of the array + jsize stackTraceLength = env->GetArrayLength(stackTrace); + + // get toString methodId of StackTraceElement class + static JClass stackTraceElementCls(env, "java/lang/StackTraceElement"); + if (!stackTraceElementCls) { + return ""; + } + static jmethodID toStringId = nullptr; + if (!toStringId) { + toStringId = env->GetMethodID(stackTraceElementCls, "toString", + "()Ljava/lang/String;"); + } + + bool foundFirst = false; + std::string buf; + raw_string_ostream oss(buf); + for (jsize i = 0; i < stackTraceLength; i++) { + // add the result of toString method of each element in the result + JLocal curStackTraceElement( + env, env->GetObjectArrayElement(stackTrace, i)); + + // call to string on the object + JLocal stackElementString( + env, static_cast( + env->CallObjectMethod(curStackTraceElement, toStringId))); + + if (!stackElementString) { + return ""; + } + + // add a line to res + JStringRef elem(env, stackElementString); + if (!foundFirst) { + if (elem.str().startswith(skipPrefix)) { + continue; + } + foundFirst = true; + } + oss << "\tat " << elem << '\n'; + } + + return oss.str(); +} + inline std::string GetJavaStackTrace(JNIEnv* env, std::string* func, StringRef excludeFuncPrefix) { // create a throwable From 3c08461685f5b3d3fc9dbd9a9992987382253722 Mon Sep 17 00:00:00 2001 From: Thad House Date: Sat, 1 May 2021 13:22:08 -0700 Subject: [PATCH 30/34] [hal] Use last error reporting instead of PARAMETER_OUT_OF_RANGE (#3328) Makes the error messages much more specific to each error. --- hal/src/main/native/athena/AddressableLED.cpp | 11 +++++++-- hal/src/main/native/athena/AnalogTrigger.cpp | 6 +++++ hal/src/main/native/athena/Counter.cpp | 9 +++++++ hal/src/main/native/athena/DMA.cpp | 4 ++++ hal/src/main/native/athena/Encoder.cpp | 8 +++++++ hal/src/main/native/athena/FPGAEncoder.cpp | 6 +++++ hal/src/main/native/athena/PDP.cpp | 3 +++ hal/src/main/native/athena/SPI.cpp | 24 +++++++++++++++++++ hal/src/main/native/athena/SerialPort.cpp | 6 +++++ hal/src/main/native/cpp/jni/CounterJNI.cpp | 10 -------- hal/src/main/native/cpp/jni/EncoderJNI.cpp | 4 ---- hal/src/main/native/sim/AddressableLED.cpp | 10 +++++++- hal/src/main/native/sim/Encoder.cpp | 3 +++ hal/src/main/native/sim/PDP.cpp | 2 ++ wpilibc/src/main/native/cpp/AnalogGyro.cpp | 4 ---- 15 files changed, 89 insertions(+), 21 deletions(-) diff --git a/hal/src/main/native/athena/AddressableLED.cpp b/hal/src/main/native/athena/AddressableLED.cpp index 39db71c039..864c08ce07 100644 --- a/hal/src/main/native/athena/AddressableLED.cpp +++ b/hal/src/main/native/athena/AddressableLED.cpp @@ -11,6 +11,7 @@ #include "ConstantsInternal.h" #include "DigitalInternal.h" #include "HALInitializer.h" +#include "HALInternal.h" #include "PortsInternal.h" #include "hal/AddressableLEDTypes.h" #include "hal/ChipObject.h" @@ -142,8 +143,11 @@ void HAL_SetAddressableLEDLength(HAL_AddressableLEDHandle handle, return; } - if (length > HAL_kAddressableLEDMaxLength) { + if (length > HAL_kAddressableLEDMaxLength || length < 0) { *status = PARAMETER_OUT_OF_RANGE; + hal::SetLastError(status, "LED length must be less than or equal to " + + wpi::Twine(HAL_kAddressableLEDMaxLength) + + ". " + wpi::Twine(length) + " was requested"); return; } @@ -173,8 +177,11 @@ void HAL_WriteAddressableLEDData(HAL_AddressableLEDHandle handle, return; } - if (length > led->stringLength) { + if (length > led->stringLength || length < 0) { *status = PARAMETER_OUT_OF_RANGE; + hal::SetLastError(status, "Data length must be less than or equal to " + + wpi::Twine(led->stringLength) + ". " + + wpi::Twine(length) + " was requested"); return; } diff --git a/hal/src/main/native/athena/AnalogTrigger.cpp b/hal/src/main/native/athena/AnalogTrigger.cpp index 7d438572e8..d9e2b9251c 100644 --- a/hal/src/main/native/athena/AnalogTrigger.cpp +++ b/hal/src/main/native/athena/AnalogTrigger.cpp @@ -7,6 +7,7 @@ #include "AnalogInternal.h" #include "DutyCycleInternal.h" #include "HALInitializer.h" +#include "HALInternal.h" #include "PortsInternal.h" #include "hal/AnalogInput.h" #include "hal/DutyCycle.h" @@ -138,6 +139,11 @@ void HAL_SetAnalogTriggerLimitsDutyCycle( if (lower < 0.0 || upper > 1.0) { *status = PARAMETER_OUT_OF_RANGE; + auto lowerStr = std::to_string(lower); + auto upperStr = std::to_string(upper); + hal::SetLastError( + status, "Lower must be >= 0 and upper must be <=1. Requested lower " + + lowerStr + " Requested upper " + upperStr); return; } diff --git a/hal/src/main/native/athena/Counter.cpp b/hal/src/main/native/athena/Counter.cpp index 2605279ed3..d6a278ce74 100644 --- a/hal/src/main/native/athena/Counter.cpp +++ b/hal/src/main/native/athena/Counter.cpp @@ -7,6 +7,7 @@ #include "ConstantsInternal.h" #include "DigitalInternal.h" #include "HALInitializer.h" +#include "HALInternal.h" #include "PortsInternal.h" #include "hal/HAL.h" #include "hal/handles/LimitedHandleResource.h" @@ -146,6 +147,9 @@ void HAL_SetCounterDownSource(HAL_CounterHandle counterHandle, // TODO: wpi_setWPIErrorWithContext(ParameterOutOfRange, "Counter only // supports DownSource in TwoPulse and ExternalDirection modes."); *status = PARAMETER_OUT_OF_RANGE; + hal::SetLastError(status, + "Counter only supports DownSource in TwoPulse and " + "ExternalDirection mode."); return; } @@ -260,6 +264,11 @@ void HAL_SetCounterSamplesToAverage(HAL_CounterHandle counterHandle, } if (samplesToAverage < 1 || samplesToAverage > 127) { *status = PARAMETER_OUT_OF_RANGE; + hal::SetLastError( + status, + "Samples to average must be between 1 and 127 inclusive. Requested " + + wpi::Twine(samplesToAverage)); + return; } counter->counter->writeTimerConfig_AverageSize(samplesToAverage, status); } diff --git a/hal/src/main/native/athena/DMA.cpp b/hal/src/main/native/athena/DMA.cpp index 31ca1c27bc..40eaca6f4b 100644 --- a/hal/src/main/native/athena/DMA.cpp +++ b/hal/src/main/native/athena/DMA.cpp @@ -13,6 +13,7 @@ #include "AnalogInternal.h" #include "DigitalInternal.h" #include "EncoderInternal.h" +#include "HALInternal.h" #include "PortsInternal.h" #include "hal/AnalogAccumulator.h" #include "hal/AnalogGyro.h" @@ -528,6 +529,9 @@ void HAL_SetDMAExternalTrigger(HAL_DMAHandle handle, if (!success) { *status = PARAMETER_OUT_OF_RANGE; + hal::SetLastError(status, + "Digital Source unabled to be mapped properly. Likely " + "invalid handle passed."); return; } diff --git a/hal/src/main/native/athena/Encoder.cpp b/hal/src/main/native/athena/Encoder.cpp index e63eede3d1..10556844f6 100644 --- a/hal/src/main/native/athena/Encoder.cpp +++ b/hal/src/main/native/athena/Encoder.cpp @@ -7,6 +7,7 @@ #include "EncoderInternal.h" #include "FPGAEncoder.h" #include "HALInitializer.h" +#include "HALInternal.h" #include "PortsInternal.h" #include "hal/ChipObject.h" #include "hal/Counter.h" @@ -46,6 +47,9 @@ Encoder::Encoder(HAL_Handle digitalSourceHandleA, } default: *status = PARAMETER_OUT_OF_RANGE; + hal::SetLastError(status, "Encoding type " + + wpi::Twine(static_cast(encodingType)) + + " invalid."); return; } } @@ -181,6 +185,10 @@ void Encoder::SetReverseDirection(bool reverseDirection, int32_t* status) { void Encoder::SetSamplesToAverage(int32_t samplesToAverage, int32_t* status) { if (samplesToAverage < 1 || samplesToAverage > 127) { *status = PARAMETER_OUT_OF_RANGE; + hal::SetLastError( + status, + "Samples to average must be between 1 and 127 inclusive. Requested " + + wpi::Twine(samplesToAverage)); return; } if (m_counter) { diff --git a/hal/src/main/native/athena/FPGAEncoder.cpp b/hal/src/main/native/athena/FPGAEncoder.cpp index 8709139924..3bfe9d9a41 100644 --- a/hal/src/main/native/athena/FPGAEncoder.cpp +++ b/hal/src/main/native/athena/FPGAEncoder.cpp @@ -8,6 +8,7 @@ #include "DigitalInternal.h" #include "HALInitializer.h" +#include "HALInternal.h" #include "PortsInternal.h" #include "hal/handles/LimitedHandleResource.h" @@ -193,6 +194,11 @@ void HAL_SetFPGAEncoderSamplesToAverage(HAL_FPGAEncoderHandle fpgaEncoderHandle, } if (samplesToAverage < 1 || samplesToAverage > 127) { *status = PARAMETER_OUT_OF_RANGE; + hal::SetLastError( + status, + "Samples to average must be between 1 and 127 inclusive. Requested " + + wpi::Twine(samplesToAverage)); + return; } encoder->encoder->writeTimerConfig_AverageSize(samplesToAverage, status); } diff --git a/hal/src/main/native/athena/PDP.cpp b/hal/src/main/native/athena/PDP.cpp index 7db4dd2db5..0cdf30f831 100644 --- a/hal/src/main/native/athena/PDP.cpp +++ b/hal/src/main/native/athena/PDP.cpp @@ -7,6 +7,7 @@ #include #include "HALInitializer.h" +#include "HALInternal.h" #include "PortsInternal.h" #include "hal/CANAPI.h" #include "hal/Errors.h" @@ -117,6 +118,7 @@ HAL_PDPHandle HAL_InitializePDP(int32_t module, int32_t* status) { hal::init::CheckInit(); if (!HAL_CheckPDPModule(module)) { *status = PARAMETER_OUT_OF_RANGE; + hal::SetLastError(status, "Invalid pdp module " + wpi::Twine(module)); return HAL_kInvalidHandle; } @@ -192,6 +194,7 @@ double HAL_GetPDPChannelCurrent(HAL_PDPHandle handle, int32_t channel, int32_t* status) { if (!HAL_CheckPDPChannel(channel)) { *status = PARAMETER_OUT_OF_RANGE; + hal::SetLastError(status, "Invalid pdp channel " + wpi::Twine(channel)); return 0; } diff --git a/hal/src/main/native/athena/SPI.cpp b/hal/src/main/native/athena/SPI.cpp index 43843ebfed..090c53378e 100644 --- a/hal/src/main/native/athena/SPI.cpp +++ b/hal/src/main/native/athena/SPI.cpp @@ -18,6 +18,7 @@ #include "DigitalInternal.h" #include "HALInitializer.h" +#include "HALInternal.h" #include "hal/DIO.h" #include "hal/HAL.h" #include "hal/handles/HandlesInternal.h" @@ -103,6 +104,9 @@ void HAL_InitializeSPI(HAL_SPIPort port, int32_t* status) { hal::init::CheckInit(); if (port < 0 || port >= kSpiMaxHandles) { *status = PARAMETER_OUT_OF_RANGE; + hal::SetLastError(status, "Serial port must be between 0 and " + + wpi::Twine(kSpiMaxHandles) + ". Requested " + + wpi::Twine(static_cast(port))); return; } @@ -246,6 +250,8 @@ void HAL_InitializeSPI(HAL_SPIPort port, int32_t* status) { break; default: *status = PARAMETER_OUT_OF_RANGE; + hal::SetLastError( + status, "Invalid SPI port " + wpi::Twine(static_cast(port))); break; } } @@ -374,6 +380,9 @@ void HAL_SetSPIOpts(HAL_SPIPort port, HAL_Bool msbFirst, void HAL_SetSPIChipSelectActiveHigh(HAL_SPIPort port, int32_t* status) { if (port < 0 || port >= kSpiMaxHandles) { *status = PARAMETER_OUT_OF_RANGE; + hal::SetLastError(status, "Serial port must be between 0 and " + + wpi::Twine(kSpiMaxHandles) + ". Requested " + + wpi::Twine(static_cast(port))); return; } @@ -389,6 +398,9 @@ void HAL_SetSPIChipSelectActiveHigh(HAL_SPIPort port, int32_t* status) { void HAL_SetSPIChipSelectActiveLow(HAL_SPIPort port, int32_t* status) { if (port < 0 || port >= kSpiMaxHandles) { *status = PARAMETER_OUT_OF_RANGE; + hal::SetLastError(status, "Serial port must be between 0 and " + + wpi::Twine(kSpiMaxHandles) + ". Requested " + + wpi::Twine(static_cast(port))); return; } @@ -453,6 +465,9 @@ void HAL_SetSPIHandle(HAL_SPIPort port, int32_t handle) { void HAL_InitSPIAuto(HAL_SPIPort port, int32_t bufferSize, int32_t* status) { if (port < 0 || port >= kSpiMaxHandles) { *status = PARAMETER_OUT_OF_RANGE; + hal::SetLastError(status, "Serial port must be between 0 and " + + wpi::Twine(kSpiMaxHandles) + ". Requested " + + wpi::Twine(static_cast(port))); return; } @@ -484,6 +499,9 @@ void HAL_InitSPIAuto(HAL_SPIPort port, int32_t bufferSize, int32_t* status) { void HAL_FreeSPIAuto(HAL_SPIPort port, int32_t* status) { if (port < 0 || port >= kSpiMaxHandles) { *status = PARAMETER_OUT_OF_RANGE; + hal::SetLastError(status, "Serial port must be between 0 and " + + wpi::Twine(kSpiMaxHandles) + ". Requested " + + wpi::Twine(static_cast(port))); return; } @@ -586,11 +604,17 @@ void HAL_SetSPIAutoTransmitData(HAL_SPIPort port, const uint8_t* dataToSend, int32_t* status) { if (dataSize < 0 || dataSize > 32) { *status = PARAMETER_OUT_OF_RANGE; + hal::SetLastError( + status, "Data size must be between 0 and 32 inclusive. Requested " + + wpi::Twine(dataSize)); return; } if (zeroSize < 0 || zeroSize > 127) { *status = PARAMETER_OUT_OF_RANGE; + hal::SetLastError( + status, "Zero size must be between 0 and 127 inclusive. Requested " + + wpi::Twine(zeroSize)); return; } diff --git a/hal/src/main/native/athena/SerialPort.cpp b/hal/src/main/native/athena/SerialPort.cpp index cf34d7b5c0..dfacbecc7c 100644 --- a/hal/src/main/native/athena/SerialPort.cpp +++ b/hal/src/main/native/athena/SerialPort.cpp @@ -19,6 +19,7 @@ #include #include +#include "HALInternal.h" #include "hal/cpp/SerialHelper.h" #include "hal/handles/HandlesInternal.h" #include "hal/handles/IndexedHandleResource.h" @@ -183,6 +184,7 @@ void HAL_SetSerialBaudRate(HAL_SerialPortHandle handle, int32_t baud, BAUDCASE(4000000) default: *status = PARAMETER_OUT_OF_RANGE; + hal::SetLastError(status, "Invalid BaudRate: " + wpi::Twine(baud)); return; } int err = cfsetospeed(&port->tty, static_cast(port->baudRate)); @@ -225,6 +227,7 @@ void HAL_SetSerialDataBits(HAL_SerialPortHandle handle, int32_t bits, break; default: *status = PARAMETER_OUT_OF_RANGE; + hal::SetLastError(status, "Invalid data bits: " + wpi::Twine(bits)); return; } @@ -272,6 +275,7 @@ void HAL_SetSerialParity(HAL_SerialPortHandle handle, int32_t parity, break; default: *status = PARAMETER_OUT_OF_RANGE; + hal::SetLastError(status, "Invalid parity bits: " + wpi::Twine(parity)); return; } @@ -299,6 +303,7 @@ void HAL_SetSerialStopBits(HAL_SerialPortHandle handle, int32_t stopBits, break; default: *status = PARAMETER_OUT_OF_RANGE; + hal::SetLastError(status, "Invalid stop bits: " + wpi::Twine(stopBits)); return; } @@ -334,6 +339,7 @@ void HAL_SetSerialFlowControl(HAL_SerialPortHandle handle, int32_t flow, break; default: *status = PARAMETER_OUT_OF_RANGE; + hal::SetLastError(status, "Invalid fc bits: " + wpi::Twine(flow)); return; } diff --git a/hal/src/main/native/cpp/jni/CounterJNI.cpp b/hal/src/main/native/cpp/jni/CounterJNI.cpp index 07718a6cd5..0565d5b847 100644 --- a/hal/src/main/native/cpp/jni/CounterJNI.cpp +++ b/hal/src/main/native/cpp/jni/CounterJNI.cpp @@ -119,12 +119,6 @@ Java_edu_wpi_first_hal_CounterJNI_setCounterDownSource HAL_SetCounterDownSource((HAL_CounterHandle)id, (HAL_Handle)digitalSourceHandle, (HAL_AnalogTriggerType)analogTriggerType, &status); - if (status == PARAMETER_OUT_OF_RANGE) { - ThrowIllegalArgumentException(env, - "Counter only supports DownSource in " - "TwoPulse and ExternalDirection modes."); - return; - } CheckStatus(env, status); } @@ -240,10 +234,6 @@ Java_edu_wpi_first_hal_CounterJNI_setCounterSamplesToAverage { int32_t status = 0; HAL_SetCounterSamplesToAverage((HAL_CounterHandle)id, value, &status); - if (status == PARAMETER_OUT_OF_RANGE) { - ThrowBoundaryException(env, value, 1, 127); - return; - } CheckStatus(env, status); } diff --git a/hal/src/main/native/cpp/jni/EncoderJNI.cpp b/hal/src/main/native/cpp/jni/EncoderJNI.cpp index 80673ab49e..f93e064ac4 100644 --- a/hal/src/main/native/cpp/jni/EncoderJNI.cpp +++ b/hal/src/main/native/cpp/jni/EncoderJNI.cpp @@ -267,10 +267,6 @@ Java_edu_wpi_first_hal_EncoderJNI_setEncoderSamplesToAverage { int32_t status = 0; HAL_SetEncoderSamplesToAverage((HAL_EncoderHandle)id, value, &status); - if (status == PARAMETER_OUT_OF_RANGE) { - ThrowBoundaryException(env, value, 1, 127); - return; - } CheckStatus(env, status); } diff --git a/hal/src/main/native/sim/AddressableLED.cpp b/hal/src/main/native/sim/AddressableLED.cpp index b4a3ed80ae..2ddc38271a 100644 --- a/hal/src/main/native/sim/AddressableLED.cpp +++ b/hal/src/main/native/sim/AddressableLED.cpp @@ -6,6 +6,7 @@ #include "DigitalInternal.h" #include "HALInitializer.h" +#include "HALInternal.h" #include "PortsInternal.h" #include "hal/Errors.h" #include "hal/handles/HandlesInternal.h" @@ -110,8 +111,11 @@ void HAL_SetAddressableLEDLength(HAL_AddressableLEDHandle handle, *status = HAL_HANDLE_ERROR; return; } - if (length > HAL_kAddressableLEDMaxLength) { + if (length > HAL_kAddressableLEDMaxLength || length < 0) { *status = PARAMETER_OUT_OF_RANGE; + hal::SetLastError(status, "LED length must be less than or equal to " + + wpi::Twine(HAL_kAddressableLEDMaxLength) + + ". " + wpi::Twine(length) + " was requested"); return; } SimAddressableLEDData[led->index].length = length; @@ -127,6 +131,10 @@ void HAL_WriteAddressableLEDData(HAL_AddressableLEDHandle handle, } if (length > SimAddressableLEDData[led->index].length) { *status = PARAMETER_OUT_OF_RANGE; + hal::SetLastError(status, + "Data length must be less than or equal to " + + wpi::Twine(SimAddressableLEDData[led->index].length) + + ". " + wpi::Twine(length) + " was requested"); return; } SimAddressableLEDData[led->index].SetData(data, length); diff --git a/hal/src/main/native/sim/Encoder.cpp b/hal/src/main/native/sim/Encoder.cpp index 765288caf6..f34f651686 100644 --- a/hal/src/main/native/sim/Encoder.cpp +++ b/hal/src/main/native/sim/Encoder.cpp @@ -6,6 +6,7 @@ #include "CounterInternal.h" #include "HALInitializer.h" +#include "HALInternal.h" #include "PortsInternal.h" #include "hal/Errors.h" #include "hal/handles/HandlesInternal.h" @@ -246,6 +247,7 @@ void HAL_SetEncoderMinRate(HAL_EncoderHandle encoderHandle, double minRate, if (minRate == 0.0) { *status = PARAMETER_OUT_OF_RANGE; + hal::SetLastError(status, "minRate must not be 0"); return; } @@ -262,6 +264,7 @@ void HAL_SetEncoderDistancePerPulse(HAL_EncoderHandle encoderHandle, if (distancePerPulse == 0.0) { *status = PARAMETER_OUT_OF_RANGE; + hal::SetLastError(status, "distancePerPulse must not be 0"); return; } encoder->distancePerPulse = distancePerPulse; diff --git a/hal/src/main/native/sim/PDP.cpp b/hal/src/main/native/sim/PDP.cpp index 36fb50e45d..aa08ca0dfa 100644 --- a/hal/src/main/native/sim/PDP.cpp +++ b/hal/src/main/native/sim/PDP.cpp @@ -6,6 +6,7 @@ #include "CANAPIInternal.h" #include "HALInitializer.h" +#include "HALInternal.h" #include "PortsInternal.h" #include "hal/CANAPI.h" #include "hal/Errors.h" @@ -27,6 +28,7 @@ extern "C" { HAL_PDPHandle HAL_InitializePDP(int32_t module, int32_t* status) { if (!HAL_CheckPDPModule(module)) { *status = PARAMETER_OUT_OF_RANGE; + hal::SetLastError(status, "Invalid pdp module " + wpi::Twine(module)); return HAL_kInvalidHandle; } hal::init::CheckInit(); diff --git a/wpilibc/src/main/native/cpp/AnalogGyro.cpp b/wpilibc/src/main/native/cpp/AnalogGyro.cpp index cdbf0e0254..47a9429855 100644 --- a/wpilibc/src/main/native/cpp/AnalogGyro.cpp +++ b/wpilibc/src/main/native/cpp/AnalogGyro.cpp @@ -115,10 +115,6 @@ void AnalogGyro::InitGyro() { std::string stackTrace = wpi::GetStackTrace(1); m_gyroHandle = HAL_InitializeAnalogGyro(m_analog->m_port, stackTrace.c_str(), &status); - if (status == PARAMETER_OUT_OF_RANGE) { - throw FRC_MakeError(err::ParameterOutOfRange, - "channel must be accumulator channel"); - } FRC_CheckErrorStatus(status, "InitializeAnalogGyro"); } From f00dfed7ac642b8c2d07dfdd702a26b08b6d6487 Mon Sep 17 00:00:00 2001 From: Tyler Veness Date: Wed, 10 Apr 2019 20:21:11 -0700 Subject: [PATCH 31/34] [wpilib] Remove IterativeRobot base class TimedRobot supersedes it (see commit 81498e6 for reasoning). --- .../src/main/native/cpp/IterativeRobot.cpp | 46 ------------- .../main/native/include/frc/IterativeRobot.h | 50 --------------- .../edu/wpi/first/wpilibj/IterativeRobot.java | 64 ------------------- 3 files changed, 160 deletions(-) delete mode 100644 wpilibc/src/main/native/cpp/IterativeRobot.cpp delete mode 100644 wpilibc/src/main/native/include/frc/IterativeRobot.h delete mode 100644 wpilibj/src/main/java/edu/wpi/first/wpilibj/IterativeRobot.java diff --git a/wpilibc/src/main/native/cpp/IterativeRobot.cpp b/wpilibc/src/main/native/cpp/IterativeRobot.cpp deleted file mode 100644 index ffc4e8e175..0000000000 --- a/wpilibc/src/main/native/cpp/IterativeRobot.cpp +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright (c) FIRST and other WPILib contributors. -// Open Source Software; you can modify and/or share it under the terms of -// the WPILib BSD license file in the root directory of this project. - -#include "frc/IterativeRobot.h" - -#include -#include - -#include "frc/DriverStation.h" - -using namespace frc; - -static constexpr auto kPacketPeriod = 0.02_s; - -IterativeRobot::IterativeRobot() : IterativeRobotBase(kPacketPeriod) { - HAL_Report(HALUsageReporting::kResourceType_Framework, - HALUsageReporting::kFramework_Iterative); -} - -void IterativeRobot::StartCompetition() { - RobotInit(); - - if constexpr (IsSimulation()) { - SimulationInit(); - } - - // Tell the DS that the robot is ready to be enabled - HAL_ObserveUserProgramStarting(); - - // Loop forever, calling the appropriate mode-dependent function - while (true) { - // Wait for driver station data so the loop doesn't hog the CPU - DriverStation::GetInstance().WaitForData(); - if (m_exit) { - break; - } - - LoopFunc(); - } -} - -void IterativeRobot::EndCompetition() { - m_exit = true; - DriverStation::GetInstance().WakeupWaitForData(); -} diff --git a/wpilibc/src/main/native/include/frc/IterativeRobot.h b/wpilibc/src/main/native/include/frc/IterativeRobot.h deleted file mode 100644 index f6c1532917..0000000000 --- a/wpilibc/src/main/native/include/frc/IterativeRobot.h +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright (c) FIRST and other WPILib contributors. -// Open Source Software; you can modify and/or share it under the terms of -// the WPILib BSD license file in the root directory of this project. - -#pragma once - -#include - -#include "frc/IterativeRobotBase.h" - -namespace frc { - -/** - * IterativeRobot implements the IterativeRobotBase robot program framework. - * - * The IterativeRobot class is intended to be subclassed by a user creating a - * robot program. - * - * Periodic() functions from the base class are called each time a new packet is - * received from the driver station. - * - * @deprecated Use TimedRobot instead. It's a drop-in replacement that provides - * more regular execution periods. - */ -class IterativeRobot : public IterativeRobotBase { - public: - WPI_DEPRECATED( - "Use TimedRobot instead. It's a drop-in replacement that provides more " - "regular execution periods.") - IterativeRobot(); - ~IterativeRobot() override = default; - - /** - * Provide an alternate "main loop" via StartCompetition(). - * - * This specific StartCompetition() implements "main loop" behavior synced - * with the DS packets. - */ - void StartCompetition() override; - - /** - * Ends the main loop in StartCompetition(). - */ - void EndCompetition() override; - - private: - std::atomic m_exit{false}; -}; - -} // namespace frc diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/IterativeRobot.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/IterativeRobot.java deleted file mode 100644 index ba3cb2c40a..0000000000 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/IterativeRobot.java +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright (c) FIRST and other WPILib contributors. -// Open Source Software; you can modify and/or share it under the terms of -// the WPILib BSD license file in the root directory of this project. - -package edu.wpi.first.wpilibj; - -import edu.wpi.first.hal.FRCNetComm.tInstances; -import edu.wpi.first.hal.FRCNetComm.tResourceType; -import edu.wpi.first.hal.HAL; - -/** - * IterativeRobot implements the IterativeRobotBase robot program framework. - * - *

        The IterativeRobot class is intended to be subclassed by a user creating a robot program. - * - *

        periodic() functions from the base class are called each time a new packet is received from - * the driver station. - * - * @deprecated Use TimedRobot instead. It's a drop-in replacement that provides more regular - * execution periods. - */ -@Deprecated -public class IterativeRobot extends IterativeRobotBase { - private static final double kPacketPeriod = 0.02; - private volatile boolean m_exit; - - /** Create a new IterativeRobot. */ - public IterativeRobot() { - super(kPacketPeriod); - - HAL.report(tResourceType.kResourceType_Framework, tInstances.kFramework_Iterative); - } - - /** Provide an alternate "main loop" via startCompetition(). */ - @Override - public void startCompetition() { - robotInit(); - - if (isSimulation()) { - simulationInit(); - } - - // Tell the DS that the robot is ready to be enabled - HAL.observeUserProgramStarting(); - - // Loop forever, calling the appropriate mode-dependent function - while (!Thread.currentThread().isInterrupted()) { - // Wait for new data to arrive - m_ds.waitForData(); - if (m_exit) { - break; - } - - loopFunc(); - } - } - - /** Ends the main loop in startCompetition(). */ - @Override - public void endCompetition() { - m_exit = true; - m_ds.wakeupWaitForData(); - } -} From 497b712f67e202c9f8ce843ddc331746868cc013 Mon Sep 17 00:00:00 2001 From: Tyler Veness Date: Sat, 13 Apr 2019 14:26:42 -0700 Subject: [PATCH 32/34] [wpilib] Make IterativeRobotBase::m_period private with getter --- wpilibc/src/main/native/cpp/IterativeRobotBase.cpp | 4 ++++ wpilibc/src/main/native/cpp/TimedRobot.cpp | 4 ---- wpilibc/src/main/native/include/frc/IterativeRobotBase.h | 8 ++++++-- wpilibc/src/main/native/include/frc/TimedRobot.h | 5 ----- .../java/edu/wpi/first/wpilibj/IterativeRobotBase.java | 8 ++++++-- .../src/main/java/edu/wpi/first/wpilibj/TimedRobot.java | 5 ----- 6 files changed, 16 insertions(+), 18 deletions(-) diff --git a/wpilibc/src/main/native/cpp/IterativeRobotBase.cpp b/wpilibc/src/main/native/cpp/IterativeRobotBase.cpp index d96bef96f7..b20409b517 100644 --- a/wpilibc/src/main/native/cpp/IterativeRobotBase.cpp +++ b/wpilibc/src/main/native/cpp/IterativeRobotBase.cpp @@ -100,6 +100,10 @@ void IterativeRobotBase::SetNetworkTablesFlushEnabled(bool enabled) { m_ntFlushEnabled = enabled; } +units::second_t IterativeRobotBase::GetPeriod() const { + return m_period; +} + void IterativeRobotBase::LoopFunc() { m_watchdog.Reset(); diff --git a/wpilibc/src/main/native/cpp/TimedRobot.cpp b/wpilibc/src/main/native/cpp/TimedRobot.cpp index 0326859411..d4ddc6c656 100644 --- a/wpilibc/src/main/native/cpp/TimedRobot.cpp +++ b/wpilibc/src/main/native/cpp/TimedRobot.cpp @@ -69,10 +69,6 @@ void TimedRobot::EndCompetition() { HAL_StopNotifier(m_notifier, &status); } -units::second_t TimedRobot::GetPeriod() const { - return units::second_t(m_period); -} - TimedRobot::TimedRobot(double period) : TimedRobot(units::second_t(period)) {} TimedRobot::TimedRobot(units::second_t period) : IterativeRobotBase(period) { diff --git a/wpilibc/src/main/native/include/frc/IterativeRobotBase.h b/wpilibc/src/main/native/include/frc/IterativeRobotBase.h index 7d04868da7..beb7f4cbe1 100644 --- a/wpilibc/src/main/native/include/frc/IterativeRobotBase.h +++ b/wpilibc/src/main/native/include/frc/IterativeRobotBase.h @@ -160,6 +160,11 @@ class IterativeRobotBase : public RobotBase { */ void SetNetworkTablesFlushEnabled(bool enabled); + /** + * Gets time period between calls to Periodic() functions. + */ + units::second_t GetPeriod() const; + /** * Constructor for IterativeRobotBase. * @@ -186,12 +191,11 @@ class IterativeRobotBase : public RobotBase { void LoopFunc(); - units::second_t m_period; - private: enum class Mode { kNone, kDisabled, kAutonomous, kTeleop, kTest }; Mode m_lastMode = Mode::kNone; + units::second_t m_period; Watchdog m_watchdog; bool m_ntFlushEnabled = false; diff --git a/wpilibc/src/main/native/include/frc/TimedRobot.h b/wpilibc/src/main/native/include/frc/TimedRobot.h index 56221e765d..18038eaa79 100644 --- a/wpilibc/src/main/native/include/frc/TimedRobot.h +++ b/wpilibc/src/main/native/include/frc/TimedRobot.h @@ -42,11 +42,6 @@ class TimedRobot : public IterativeRobotBase { */ void EndCompetition() override; - /** - * Get the time period between calls to Periodic() functions. - */ - units::second_t GetPeriod() const; - /** * Constructor for TimedRobot. * diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/IterativeRobotBase.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/IterativeRobotBase.java index 1fc3d964a2..ca15194632 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/IterativeRobotBase.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/IterativeRobotBase.java @@ -32,8 +32,6 @@ import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard; * disabledPeriodic() - autonomousPeriodic() - teleopPeriodic() - testPeriodic() */ public abstract class IterativeRobotBase extends RobotBase { - protected double m_period; - private enum Mode { kNone, kDisabled, @@ -43,6 +41,7 @@ public abstract class IterativeRobotBase extends RobotBase { } private Mode m_lastMode = Mode.kNone; + private final double m_period; private final Watchdog m_watchdog; private boolean m_ntFlushEnabled; @@ -204,6 +203,11 @@ public abstract class IterativeRobotBase extends RobotBase { m_ntFlushEnabled = enabled; } + /** Gets time period between calls to Periodic() functions. */ + public double getPeriod() { + return m_period; + } + @SuppressWarnings("PMD.CyclomaticComplexity") protected void loopFunc() { m_watchdog.reset(); diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/TimedRobot.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/TimedRobot.java index 655758b3de..561fbdc040 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/TimedRobot.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/TimedRobot.java @@ -137,11 +137,6 @@ public class TimedRobot extends IterativeRobotBase { NotifierJNI.stopNotifier(m_notifier); } - /** Get time period between calls to Periodic() functions. */ - public double getPeriod() { - return m_period; - } - /** * Add a callback to run at a specific period. * From d6cfdd3bae529aa8def9cddcfaa6a387f461ae21 Mon Sep 17 00:00:00 2001 From: Noam Zaks Date: Thu, 6 May 2021 18:25:37 +0300 Subject: [PATCH 33/34] [wpilib] Preferences: Deprecate Put* in favor of Set* (#3337) This naming is more consistent with other APIs. Co-authored-by: Tyler Veness --- wpilibc/src/main/native/cpp/Preferences.cpp | 36 ++++++-- .../src/main/native/include/frc/Preferences.h | 68 +++++++++++++++ .../edu/wpi/first/wpilibj/Preferences.java | 86 +++++++++++++++++-- 3 files changed, 177 insertions(+), 13 deletions(-) diff --git a/wpilibc/src/main/native/cpp/Preferences.cpp b/wpilibc/src/main/native/cpp/Preferences.cpp index 8526c943b5..c80e966ca7 100644 --- a/wpilibc/src/main/native/cpp/Preferences.cpp +++ b/wpilibc/src/main/native/cpp/Preferences.cpp @@ -49,67 +49,91 @@ int64_t Preferences::GetLong(wpi::StringRef key, int64_t defaultValue) { return static_cast(m_table->GetNumber(key, defaultValue)); } -void Preferences::PutString(wpi::StringRef key, wpi::StringRef value) { +void Preferences::SetString(wpi::StringRef key, wpi::StringRef value) { auto entry = m_table->GetEntry(key); entry.SetString(value); entry.SetPersistent(); } +void Preferences::PutString(wpi::StringRef key, wpi::StringRef value) { + SetString(key, value); +} + void Preferences::InitString(wpi::StringRef key, wpi::StringRef value) { auto entry = m_table->GetEntry(key); entry.SetDefaultString(value); } -void Preferences::PutInt(wpi::StringRef key, int value) { +void Preferences::SetInt(wpi::StringRef key, int value) { auto entry = m_table->GetEntry(key); entry.SetDouble(value); entry.SetPersistent(); } +void Preferences::PutInt(wpi::StringRef key, int value) { + SetInt(key, value); +} + void Preferences::InitInt(wpi::StringRef key, int value) { auto entry = m_table->GetEntry(key); entry.SetDefaultDouble(value); } -void Preferences::PutDouble(wpi::StringRef key, double value) { +void Preferences::SetDouble(wpi::StringRef key, double value) { auto entry = m_table->GetEntry(key); entry.SetDouble(value); entry.SetPersistent(); } +void Preferences::PutDouble(wpi::StringRef key, double value) { + SetDouble(key, value); +} + void Preferences::InitDouble(wpi::StringRef key, double value) { auto entry = m_table->GetEntry(key); entry.SetDefaultDouble(value); } -void Preferences::PutFloat(wpi::StringRef key, float value) { +void Preferences::SetFloat(wpi::StringRef key, float value) { auto entry = m_table->GetEntry(key); entry.SetDouble(value); entry.SetPersistent(); } +void Preferences::PutFloat(wpi::StringRef key, float value) { + SetFloat(key, value); +} + void Preferences::InitFloat(wpi::StringRef key, float value) { auto entry = m_table->GetEntry(key); entry.SetDefaultDouble(value); } -void Preferences::PutBoolean(wpi::StringRef key, bool value) { +void Preferences::SetBoolean(wpi::StringRef key, bool value) { auto entry = m_table->GetEntry(key); entry.SetBoolean(value); entry.SetPersistent(); } +void Preferences::PutBoolean(wpi::StringRef key, bool value) { + SetBoolean(key, value); +} + void Preferences::InitBoolean(wpi::StringRef key, bool value) { auto entry = m_table->GetEntry(key); entry.SetDefaultBoolean(value); } -void Preferences::PutLong(wpi::StringRef key, int64_t value) { +void Preferences::SetLong(wpi::StringRef key, int64_t value) { auto entry = m_table->GetEntry(key); entry.SetDouble(value); entry.SetPersistent(); } +void Preferences::PutLong(wpi::StringRef key, int64_t value) { + SetLong(key, value); +} + void Preferences::InitLong(wpi::StringRef key, int64_t value) { auto entry = m_table->GetEntry(key); entry.SetDefaultDouble(value); diff --git a/wpilibc/src/main/native/include/frc/Preferences.h b/wpilibc/src/main/native/include/frc/Preferences.h index 7ecf25a008..c22c9af97e 100644 --- a/wpilibc/src/main/native/include/frc/Preferences.h +++ b/wpilibc/src/main/native/include/frc/Preferences.h @@ -11,6 +11,7 @@ #include #include +#include namespace frc { @@ -114,6 +115,18 @@ class Preferences { * @param key the key * @param value the value */ + void SetString(wpi::StringRef key, wpi::StringRef value); + + /** + * Puts the given string into the preferences table. + * + * The value may not have quotation marks, nor may the key have any whitespace + * nor an equals sign. + * + * @param key the key + * @param value the value + */ + WPI_DEPRECATED("Use SetString instead.") void PutString(wpi::StringRef key, wpi::StringRef value); /** @@ -130,6 +143,17 @@ class Preferences { * @param key the key * @param value the value */ + void SetInt(wpi::StringRef key, int value); + + /** + * Puts the given int into the preferences table. + * + * The key may not have any whitespace nor an equals sign. + * + * @param key the key + * @param value the value + */ + WPI_DEPRECATED("Use SetInt instead.") void PutInt(wpi::StringRef key, int value); /** @@ -146,6 +170,17 @@ class Preferences { * @param key the key * @param value the value */ + void SetDouble(wpi::StringRef key, double value); + + /** + * Puts the given double into the preferences table. + * + * The key may not have any whitespace nor an equals sign. + * + * @param key the key + * @param value the value + */ + WPI_DEPRECATED("Use SetDouble instead.") void PutDouble(wpi::StringRef key, double value); /** @@ -162,6 +197,17 @@ class Preferences { * @param key the key * @param value the value */ + void SetFloat(wpi::StringRef key, float value); + + /** + * Puts the given float into the preferences table. + * + * The key may not have any whitespace nor an equals sign. + * + * @param key the key + * @param value the value + */ + WPI_DEPRECATED("Use SetFloat instead.") void PutFloat(wpi::StringRef key, float value); /** @@ -178,6 +224,17 @@ class Preferences { * @param key the key * @param value the value */ + void SetBoolean(wpi::StringRef key, bool value); + + /** + * Puts the given boolean into the preferences table. + * + * The key may not have any whitespace nor an equals sign. + * + * @param key the key + * @param value the value + */ + WPI_DEPRECATED("Use SetBoolean instead.") void PutBoolean(wpi::StringRef key, bool value); /** @@ -194,6 +251,17 @@ class Preferences { * @param key the key * @param value the value */ + void SetLong(wpi::StringRef key, int64_t value); + + /** + * Puts the given long (int64_t) into the preferences table. + * + * The key may not have any whitespace nor an equals sign. + * + * @param key the key + * @param value the value + */ + WPI_DEPRECATED("Use SetLong instead.") void PutLong(wpi::StringRef key, int64_t value); /** diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/Preferences.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/Preferences.java index 018b0b24fd..43a8b02ac3 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/Preferences.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/Preferences.java @@ -75,14 +75,27 @@ public final class Preferences { * @param value the value * @throws NullPointerException if value is null */ - public void putString(String key, String value) { - requireNonNullParam(value, "value", "putString"); + public void setString(String key, String value) { + requireNonNullParam(value, "value", "setString"); NetworkTableEntry entry = m_table.getEntry(key); entry.setString(value); entry.setPersistent(); } + /** + * Puts the given string into the preferences table. + * + * @param key the key + * @param value the value + * @throws NullPointerException if value is null + * @deprecated Use {@link #setString(String, String)} + */ + @Deprecated + public void putString(String key, String value) { + setString(key, value); + } + /** * Puts the given string into the preferences table if it doesn't already exist. * @@ -100,12 +113,24 @@ public final class Preferences { * @param key the key * @param value the value */ - public void putInt(String key, int value) { + public void setInt(String key, int value) { NetworkTableEntry entry = m_table.getEntry(key); entry.setDouble(value); entry.setPersistent(); } + /** + * Puts the given int into the preferences table. + * + * @param key the key + * @param value the value + * @deprecated Use {@link #setInt(String, int)} + */ + @Deprecated + public void putInt(String key, int value) { + setInt(key, value); + } + /** * Puts the given int into the preferences table if it doesn't already exist. * @@ -123,12 +148,23 @@ public final class Preferences { * @param key the key * @param value the value */ - public void putDouble(String key, double value) { + public void setDouble(String key, double value) { NetworkTableEntry entry = m_table.getEntry(key); entry.setDouble(value); entry.setPersistent(); } + /** + * Puts the given double into the preferences table. + * + * @param key the key + * @param value the value + * @deprecated Use {@link #setDouble(String, double)} + */ + public void putDouble(String key, double value) { + setDouble(key, value); + } + /** * Puts the given double into the preferences table if it doesn't already exist. * @@ -146,12 +182,24 @@ public final class Preferences { * @param key the key * @param value the value */ - public void putFloat(String key, float value) { + public void setFloat(String key, float value) { NetworkTableEntry entry = m_table.getEntry(key); entry.setDouble(value); entry.setPersistent(); } + /** + * Puts the given float into the preferences table. + * + * @param key the key + * @param value the value + * @deprecated Use {@link #setFloat(String, float)} + */ + @Deprecated + public void putFloat(String key, float value) { + setFloat(key, value); + } + /** * Puts the given float into the preferences table if it doesn't already exist. * @@ -169,12 +217,24 @@ public final class Preferences { * @param key the key * @param value the value */ - public void putBoolean(String key, boolean value) { + public void setBoolean(String key, boolean value) { NetworkTableEntry entry = m_table.getEntry(key); entry.setBoolean(value); entry.setPersistent(); } + /** + * Puts the given boolean into the preferences table. + * + * @param key the key + * @param value the value + * @deprecated Use {@link #setBoolean(String, boolean)} + */ + @Deprecated + public void putBoolean(String key, boolean value) { + setBoolean(key, value); + } + /** * Puts the given boolean into the preferences table if it doesn't already exist. * @@ -192,12 +252,24 @@ public final class Preferences { * @param key the key * @param value the value */ - public void putLong(String key, long value) { + public void setLong(String key, long value) { NetworkTableEntry entry = m_table.getEntry(key); entry.setDouble(value); entry.setPersistent(); } + /** + * Puts the given long into the preferences table. + * + * @param key the key + * @param value the value + * @deprecated Use {@link #setLong(String, long)} + */ + @Deprecated + public void putLong(String key, long value) { + setLong(key, value); + } + /** * Puts the given long into the preferences table if it doesn't already exist. * From a3cd90dd71f0709ceeaa808545f7d45827af196c Mon Sep 17 00:00:00 2001 From: Tyler Veness Date: Sun, 9 May 2021 00:01:03 -0700 Subject: [PATCH 34/34] [wpimath] Fix classpath used by generate_numbers.py (#3339) --- wpimath/generate_numbers.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/wpimath/generate_numbers.py b/wpimath/generate_numbers.py index de9ef447f1..14c9cb7cd8 100644 --- a/wpimath/generate_numbers.py +++ b/wpimath/generate_numbers.py @@ -11,7 +11,7 @@ def main(): with open(f"{dirname}/src/generate/GenericNumber.java.in", "r") as templateFile: template = templateFile.read() - rootPath = f"{cmake_binary_dir}/generated/main/java/edu/wpi/first/wpiutil/math/numbers" + rootPath = f"{cmake_binary_dir}/generated/main/java/edu/wpi/first/math/numbers" if not os.path.exists(rootPath): os.makedirs(rootPath) @@ -30,7 +30,7 @@ def main(): with open(f"{dirname}/src/generate/Nat.java.in", "r") as templateFile: template = templateFile.read() - outputPath = f"{cmake_binary_dir}/generated/main/java/edu/wpi/first/wpiutil/math/Nat.java" + outputPath = f"{cmake_binary_dir}/generated/main/java/edu/wpi/first/math/Nat.java" with open(f"{dirname}/src/generate/NatGetter.java.in", "r") as getterFile: getter = getterFile.read()