diff --git a/hal/src/main/java/edu/wpi/first/hal/CounterJNI.java b/hal/src/main/java/edu/wpi/first/hal/CounterJNI.java index f164dbdf4b..8b95228b9b 100644 --- a/hal/src/main/java/edu/wpi/first/hal/CounterJNI.java +++ b/hal/src/main/java/edu/wpi/first/hal/CounterJNI.java @@ -7,6 +7,11 @@ package edu.wpi.first.hal; import java.nio.IntBuffer; public class CounterJNI extends JNIWrapper { + public static final int TWO_PULSE = 0; + public static final int SEMI_PERIOD = 1; + public static final int PULSE_LENGTH = 2; + public static final int EXTERNAL_DIRECTION = 3; + public static native int initializeCounter(int mode, IntBuffer index); public static native void freeCounter(int counterHandle); diff --git a/hal/src/main/java/edu/wpi/first/hal/JNIWrapper.java b/hal/src/main/java/edu/wpi/first/hal/JNIWrapper.java index b2b54da2df..6826cd8074 100644 --- a/hal/src/main/java/edu/wpi/first/hal/JNIWrapper.java +++ b/hal/src/main/java/edu/wpi/first/hal/JNIWrapper.java @@ -55,4 +55,6 @@ public class JNIWrapper { loader.loadLibrary(); libraryLoaded = true; } + + public static void suppressUnused(Object object) {} } diff --git a/hal/src/main/native/cpp/jni/CounterJNI.cpp b/hal/src/main/native/cpp/jni/CounterJNI.cpp index 0565d5b847..6fc12df803 100644 --- a/hal/src/main/native/cpp/jni/CounterJNI.cpp +++ b/hal/src/main/native/cpp/jni/CounterJNI.cpp @@ -11,6 +11,18 @@ #include "hal/Counter.h" #include "hal/Errors.h" +static_assert(HAL_Counter_Mode::HAL_Counter_kTwoPulse == + edu_wpi_first_hal_CounterJNI_TWO_PULSE); + +static_assert(HAL_Counter_Mode::HAL_Counter_kSemiperiod == + edu_wpi_first_hal_CounterJNI_SEMI_PERIOD); + +static_assert(HAL_Counter_Mode::HAL_Counter_kPulseLength == + edu_wpi_first_hal_CounterJNI_PULSE_LENGTH); + +static_assert(HAL_Counter_Mode::HAL_Counter_kExternalDirection == + edu_wpi_first_hal_CounterJNI_EXTERNAL_DIRECTION); + using namespace hal; extern "C" { diff --git a/wpilibc/src/main/native/cpp/counter/ExternalDirectionCounter.cpp b/wpilibc/src/main/native/cpp/counter/ExternalDirectionCounter.cpp new file mode 100644 index 0000000000..8c7bb5870d --- /dev/null +++ b/wpilibc/src/main/native/cpp/counter/ExternalDirectionCounter.cpp @@ -0,0 +1,103 @@ +// 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/counter/ExternalDirectionCounter.h" + +#include +#include +#include +#include + +#include "frc/DigitalSource.h" +#include "frc/Errors.h" + +using namespace frc; + +ExternalDirectionCounter::ExternalDirectionCounter( + DigitalSource& countSource, DigitalSource& directionSource) + : ExternalDirectionCounter( + {&countSource, wpi::NullDeleter()}, + {&directionSource, wpi::NullDeleter()}) {} + +ExternalDirectionCounter::ExternalDirectionCounter( + std::shared_ptr countSource, + std::shared_ptr directionSource) { + if (countSource == nullptr) { + throw FRC_MakeError(err::NullParameter, "{}", "countSource"); + } + if (directionSource == nullptr) { + throw FRC_MakeError(err::NullParameter, "{}", "directionSource"); + } + + m_countSource = countSource; + m_directionSource = directionSource; + + int32_t status = 0; + m_handle = HAL_InitializeCounter( + HAL_Counter_Mode::HAL_Counter_kExternalDirection, &m_index, &status); + FRC_CheckErrorStatus(status, "{}", m_index); + + HAL_SetCounterUpSource(m_handle, m_countSource->GetPortHandleForRouting(), + static_cast( + m_countSource->GetAnalogTriggerTypeForRouting()), + &status); + FRC_CheckErrorStatus(status, "{}", m_index); + HAL_SetCounterUpSourceEdge(m_handle, true, false, &status); + FRC_CheckErrorStatus(status, "{}", m_index); + + HAL_SetCounterDownSource( + m_handle, m_directionSource->GetPortHandleForRouting(), + static_cast( + m_directionSource->GetAnalogTriggerTypeForRouting()), + &status); + FRC_CheckErrorStatus(status, "{}", m_index); + HAL_SetCounterDownSourceEdge(m_handle, false, true, &status); + FRC_CheckErrorStatus(status, "{}", m_index); + + Reset(); + + HAL_Report(HALUsageReporting::kResourceType_Counter, m_index + 1); + wpi::SendableRegistry::AddLW(this, "External Direction Counter", m_index); +} + +ExternalDirectionCounter::~ExternalDirectionCounter() { + int32_t status = 0; + HAL_FreeCounter(m_handle, &status); +} + +int ExternalDirectionCounter::GetCount() const { + int32_t status = 0; + int val = HAL_GetCounter(m_handle, &status); + FRC_CheckErrorStatus(status, "{}", m_index); + return val; +} + +void ExternalDirectionCounter::SetReverseDirection(bool reverseDirection) { + int32_t status = 0; + HAL_SetCounterReverseDirection(m_handle, reverseDirection, &status); + FRC_CheckErrorStatus(status, "{}", m_index); +} + +void ExternalDirectionCounter::Reset() { + int32_t status = 0; + HAL_ResetCounter(m_handle, &status); + FRC_CheckErrorStatus(status, "{}", m_index); +} + +void ExternalDirectionCounter::SetEdgeConfiguration( + EdgeConfiguration configuration) { + int32_t status = 0; + bool rising = configuration == EdgeConfiguration::kRisingEdge || + configuration == EdgeConfiguration::kBoth; + bool falling = configuration == EdgeConfiguration::kFallingEdge || + configuration == EdgeConfiguration::kBoth; + HAL_SetCounterUpSourceEdge(m_handle, rising, falling, &status); + FRC_CheckErrorStatus(status, "{}", m_index); +} + +void ExternalDirectionCounter::InitSendable(wpi::SendableBuilder& builder) { + builder.SetSmartDashboardType("External Direction Counter"); + builder.AddDoubleProperty( + "Count", [&] { return GetCount(); }, nullptr); +} diff --git a/wpilibc/src/main/native/cpp/counter/Tachometer.cpp b/wpilibc/src/main/native/cpp/counter/Tachometer.cpp new file mode 100644 index 0000000000..4c181ff134 --- /dev/null +++ b/wpilibc/src/main/native/cpp/counter/Tachometer.cpp @@ -0,0 +1,116 @@ +// 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/counter/Tachometer.h" + +#include + +#include +#include +#include +#include + +#include "frc/Errors.h" + +using namespace frc; + +Tachometer::Tachometer(DigitalSource& source) + : Tachometer({&source, wpi::NullDeleter()}) {} +Tachometer::Tachometer(std::shared_ptr source) { + if (source == nullptr) { + throw FRC_MakeError(err::NullParameter, "{}", "source"); + } + + m_source = source; + + int32_t status = 0; + HAL_SetCounterUpSource(m_handle, m_source->GetPortHandleForRouting(), + static_cast( + m_source->GetAnalogTriggerTypeForRouting()), + &status); + FRC_CheckErrorStatus(status, "{}", m_index); + HAL_SetCounterUpSourceEdge(m_handle, true, false, &status); + FRC_CheckErrorStatus(status, "{}", m_index); + + HAL_Report(HALUsageReporting::kResourceType_Counter, m_index + 1); + wpi::SendableRegistry::AddLW(this, "Tachometer", m_index); +} + +Tachometer::~Tachometer() { + int32_t status = 0; + HAL_FreeCounter(m_handle, &status); +} + +units::hertz_t Tachometer::GetFrequency() const { + auto period = GetPeriod(); + if (period.to() == 0) { + return units::hertz_t{0.0}; + } + return 1 / period; +} + +units::second_t Tachometer::GetPeriod() const { + int32_t status = 0; + double period = HAL_GetCounterPeriod(m_handle, &status); + FRC_CheckErrorStatus(status, "Channel {}", m_source->GetChannel()); + return units::second_t{period}; +} + +int Tachometer::GetEdgesPerRevolution() const { + return m_edgesPerRevolution; +} +void Tachometer::SetEdgesPerRevolution(int edges) { + m_edgesPerRevolution = edges; +} + +units::revolutions_per_minute_t Tachometer::GetRevolutionsPerMinute() const { + auto period = GetPeriod(); + if (period.to() == 0) { + return units::revolutions_per_minute_t{0.0}; + } + int edgesPerRevolution = GetEdgesPerRevolution(); + if (edgesPerRevolution == 0) { + return units::revolutions_per_minute_t{0.0}; + } + auto rotationHz = ((1.0 / edgesPerRevolution) / period); + return units::revolutions_per_minute_t{rotationHz.to() * 60}; +} + +bool Tachometer::GetStopped() const { + int32_t status = 0; + bool stopped = HAL_GetCounterStopped(m_handle, &status); + FRC_CheckErrorStatus(status, "Channel {}", m_source->GetChannel()); + return stopped; +} + +int Tachometer::GetSamplesToAverage() const { + int32_t status = 0; + int32_t samplesToAverage = HAL_GetCounterSamplesToAverage(m_handle, &status); + FRC_CheckErrorStatus(status, "Channel {}", m_source->GetChannel()); + return samplesToAverage; +} + +void Tachometer::SetSamplesToAverage(int samples) { + int32_t status = 0; + HAL_SetCounterSamplesToAverage(m_handle, samples, &status); + FRC_CheckErrorStatus(status, "Channel {}", m_source->GetChannel()); +} + +void Tachometer::SetMaxPeriod(units::second_t maxPeriod) { + int32_t status = 0; + HAL_SetCounterMaxPeriod(m_handle, maxPeriod.to(), &status); + FRC_CheckErrorStatus(status, "Channel {}", m_source->GetChannel()); +} + +void Tachometer::SetUpdateWhenEmpty(bool updateWhenEmpty) { + int32_t status = 0; + HAL_SetCounterUpdateWhenEmpty(m_handle, updateWhenEmpty, &status); + FRC_CheckErrorStatus(status, "Channel {}", m_source->GetChannel()); +} + +void Tachometer::InitSendable(wpi::SendableBuilder& builder) { + builder.SetSmartDashboardType("Tachometer"); + builder.AddDoubleProperty( + "RPM", [&] { return GetRevolutionsPerMinute().to(); }, nullptr); +} diff --git a/wpilibc/src/main/native/cpp/counter/UpDownCounter.cpp b/wpilibc/src/main/native/cpp/counter/UpDownCounter.cpp new file mode 100644 index 0000000000..62537ee882 --- /dev/null +++ b/wpilibc/src/main/native/cpp/counter/UpDownCounter.cpp @@ -0,0 +1,106 @@ +// 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/counter/UpDownCounter.h" + +#include +#include +#include +#include + +#include "frc/DigitalSource.h" +#include "frc/Errors.h" + +using namespace frc; + +UpDownCounter::UpDownCounter(DigitalSource& upSource, DigitalSource& downSource) + : UpDownCounter({&upSource, wpi::NullDeleter()}, + {&downSource, wpi::NullDeleter()}) {} + +UpDownCounter::UpDownCounter(std::shared_ptr upSource, + std::shared_ptr downSource) { + m_upSource = upSource; + m_downSource = downSource; + + int32_t status = 0; + m_handle = HAL_InitializeCounter(HAL_Counter_Mode::HAL_Counter_kTwoPulse, + &m_index, &status); + FRC_CheckErrorStatus(status, "{}", m_index); + + if (m_upSource) { + HAL_SetCounterUpSource(m_handle, m_upSource->GetPortHandleForRouting(), + static_cast( + m_upSource->GetAnalogTriggerTypeForRouting()), + &status); + FRC_CheckErrorStatus(status, "{}", m_index); + HAL_SetCounterUpSourceEdge(m_handle, true, false, &status); + FRC_CheckErrorStatus(status, "{}", m_index); + } + + if (m_downSource) { + HAL_SetCounterDownSource( + m_handle, m_downSource->GetPortHandleForRouting(), + static_cast( + m_downSource->GetAnalogTriggerTypeForRouting()), + &status); + FRC_CheckErrorStatus(status, "{}", m_index); + HAL_SetCounterDownSourceEdge(m_handle, true, false, &status); + FRC_CheckErrorStatus(status, "{}", m_index); + } + + Reset(); + + HAL_Report(HALUsageReporting::kResourceType_Counter, m_index + 1); + wpi::SendableRegistry::AddLW(this, "UpDown Counter", m_index); +} + +UpDownCounter::~UpDownCounter() { + int32_t status = 0; + HAL_FreeCounter(m_handle, &status); +} + +int UpDownCounter::GetCount() const { + int32_t status = 0; + int val = HAL_GetCounter(m_handle, &status); + FRC_CheckErrorStatus(status, "{}", m_index); + return val; +} + +void UpDownCounter::SetReverseDirection(bool reverseDirection) { + int32_t status = 0; + HAL_SetCounterReverseDirection(m_handle, reverseDirection, &status); + FRC_CheckErrorStatus(status, "{}", m_index); +} + +void UpDownCounter::Reset() { + int32_t status = 0; + HAL_ResetCounter(m_handle, &status); + FRC_CheckErrorStatus(status, "{}", m_index); +} + +void UpDownCounter::SetUpEdgeConfiguration(EdgeConfiguration configuration) { + int32_t status = 0; + bool rising = configuration == EdgeConfiguration::kRisingEdge || + configuration == EdgeConfiguration::kBoth; + bool falling = configuration == EdgeConfiguration::kFallingEdge || + configuration == EdgeConfiguration::kBoth; + HAL_SetCounterUpSourceEdge(m_handle, rising, falling, &status); + FRC_CheckErrorStatus(status, "{}", m_index); +} + +void UpDownCounter::SetDownEdgeConfiguration(EdgeConfiguration configuration) { + int32_t status = 0; + bool rising = configuration == EdgeConfiguration::kRisingEdge || + configuration == EdgeConfiguration::kBoth; + bool falling = configuration == EdgeConfiguration::kFallingEdge || + configuration == EdgeConfiguration::kBoth; + HAL_SetCounterDownSourceEdge(m_handle, rising, falling, &status); + FRC_CheckErrorStatus(status, "{}", m_index); +} + +void UpDownCounter::InitSendable(wpi::SendableBuilder& builder) { + builder.SetSmartDashboardType("UpDown Counter"); + builder.AddDoubleProperty( + "Count", [&] { return GetCount(); }, nullptr); +} diff --git a/wpilibc/src/main/native/include/frc/counter/EdgeConfiguration.h b/wpilibc/src/main/native/include/frc/counter/EdgeConfiguration.h new file mode 100644 index 0000000000..508117d79f --- /dev/null +++ b/wpilibc/src/main/native/include/frc/counter/EdgeConfiguration.h @@ -0,0 +1,14 @@ +// 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 { +enum class EdgeConfiguration { + kNone = 0, + kRisingEdge = 0x1, + kFallingEdge = 0x2, + kBoth = 0x3 +}; +} // namespace frc diff --git a/wpilibc/src/main/native/include/frc/counter/ExternalDirectionCounter.h b/wpilibc/src/main/native/include/frc/counter/ExternalDirectionCounter.h new file mode 100644 index 0000000000..3591fa26b0 --- /dev/null +++ b/wpilibc/src/main/native/include/frc/counter/ExternalDirectionCounter.h @@ -0,0 +1,79 @@ +// 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 "EdgeConfiguration.h" + +namespace frc { +class DigitalSource; + +/** Counter using external direction. */ +class ExternalDirectionCounter + : public wpi::Sendable, + public wpi::SendableHelper { + public: + /** + * Constructs a new ExternalDirectionCounter. + * + * @param countSource The source for counting. + * @param directionSource The source for selecting count direction. + */ + ExternalDirectionCounter(DigitalSource& countSource, + DigitalSource& directionSource); + + /** + * Constructs a new ExternalDirectionCounter. + * + * @param countSource The source for counting. + * @param directionSource The source for selecting count direction. + */ + ExternalDirectionCounter(std::shared_ptr countSource, + std::shared_ptr directionSource); + + ~ExternalDirectionCounter() override; + + ExternalDirectionCounter(ExternalDirectionCounter&&) = default; + ExternalDirectionCounter& operator=(ExternalDirectionCounter&&) = default; + + /** + * Gets the current count. + * + * @return The current count. + */ + int GetCount() const; + + /** + * Sets to revert the counter direction. + * + * @param reverseDirection True to reverse counting direction. + */ + void SetReverseDirection(bool reverseDirection); + + /** Resets the current count. */ + void Reset(); + + /** + * Sets the edge configuration for counting. + * + * @param configuration The counting edge configuration. + */ + void SetEdgeConfiguration(EdgeConfiguration configuration); + + protected: + void InitSendable(wpi::SendableBuilder& builder) override; + + private: + std::shared_ptr m_countSource; + std::shared_ptr m_directionSource; + hal::Handle m_handle; + int32_t m_index = 0; +}; +} // namespace frc diff --git a/wpilibc/src/main/native/include/frc/counter/Tachometer.h b/wpilibc/src/main/native/include/frc/counter/Tachometer.h new file mode 100644 index 0000000000..6836a789c7 --- /dev/null +++ b/wpilibc/src/main/native/include/frc/counter/Tachometer.h @@ -0,0 +1,125 @@ +// 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 + +namespace frc { +class DigitalSource; + +/** + * Tachometer for getting rotational speed from a device. + */ +class Tachometer : public wpi::Sendable, + public wpi::SendableHelper { + public: + /** + * Constructs a new tachometer. + * + * @param source The source. + */ + explicit Tachometer(DigitalSource& source); + + /** + * Constructs a new tachometer. + * + * @param source The source. + */ + explicit Tachometer(std::shared_ptr source); + + ~Tachometer() override; + + Tachometer(Tachometer&&) = default; + Tachometer& operator=(Tachometer&&) = default; + + /** + * Gets the tachometer frequency. + * + * @return Current frequency. + */ + units::hertz_t GetFrequency() const; + + /** + * Gets the tachometer period. + * + * @return Current period. + */ + units::second_t GetPeriod() const; + + /** + * Gets the number of edges per revolution. + * + * @return Edges per revolution. + */ + int GetEdgesPerRevolution() const; + + /** + * Sets the number of edges per revolution. + * + * @param edges Edges per revolution. + */ + void SetEdgesPerRevolution(int edges); + + /** + * Gets the current tachometer revolutions per minute. + * + * SetEdgesPerRevolution must be set with a non 0 value for this to work. + * + * @return Current RPM. + */ + units::revolutions_per_minute_t GetRevolutionsPerMinute() const; + + /** + * Gets if the tachometer is stopped. + * + * @return True if the tachometer is stopped. + */ + bool GetStopped() const; + + /** + * Gets the number of sample to average. + * + * @return Samples to average. + */ + int GetSamplesToAverage() const; + + /** + * Sets the number of samples to average. + * + * @param samples Samples to average. + */ + void SetSamplesToAverage(int samples); + + /** + * Sets the maximum period before the tachometer is considered stopped. + * + * @param maxPeriod The max period. + */ + void SetMaxPeriod(units::second_t maxPeriod); + + /** + * Sets if to update when empty. + * + * @param updateWhenEmpty True to update when empty. + */ + void SetUpdateWhenEmpty(bool updateWhenEmpty); + + protected: + void InitSendable(wpi::SendableBuilder& builder) override; + + private: + std::shared_ptr m_source; + hal::Handle m_handle; + int m_edgesPerRevolution; + int32_t m_index; +}; +} // namespace frc diff --git a/wpilibc/src/main/native/include/frc/counter/UpDownCounter.h b/wpilibc/src/main/native/include/frc/counter/UpDownCounter.h new file mode 100644 index 0000000000..71d92ce4d2 --- /dev/null +++ b/wpilibc/src/main/native/include/frc/counter/UpDownCounter.h @@ -0,0 +1,85 @@ +// 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 "EdgeConfiguration.h" + +namespace frc { +class DigitalSource; + +/** Up Down Counter. */ +class UpDownCounter : public wpi::Sendable, + public wpi::SendableHelper { + public: + /** + * Constructs a new UpDown Counter. + * + * @param upSource The up count source (can be null). + * @param downSource The down count source (can be null). + */ + UpDownCounter(DigitalSource& upSource, DigitalSource& downSource); + + /** + * Constructs a new UpDown Counter. + * + * @param upSource The up count source (can be null). + * @param downSource The down count source (can be null). + */ + UpDownCounter(std::shared_ptr upSource, + std::shared_ptr downSource); + + ~UpDownCounter() override; + + UpDownCounter(UpDownCounter&&) = default; + UpDownCounter& operator=(UpDownCounter&&) = default; + + /** + * Gets the current count. + * + * @return The current count. + */ + int GetCount() const; + + /** + * Sets to revert the counter direction. + * + * @param reverseDirection True to reverse counting direction. + */ + void SetReverseDirection(bool reverseDirection); + + /** Resets the current count. */ + void Reset(); + + /** + * Sets the configuration for the up source. + * + * @param configuration The up source configuration. + */ + void SetUpEdgeConfiguration(EdgeConfiguration configuration); + + /** + * Sets the configuration for the down source. + * + * @param configuration The down source configuration. + */ + void SetDownEdgeConfiguration(EdgeConfiguration configuration); + + protected: + void InitSendable(wpi::SendableBuilder& builder) override; + + private: + void InitUpDownCounter(); + std::shared_ptr m_upSource; + std::shared_ptr m_downSource; + hal::Handle m_handle; + int32_t m_index = 0; +}; +} // namespace frc diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/DutyCycle.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/DutyCycle.java index 3332f49eaf..c700bca07e 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/DutyCycle.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/DutyCycle.java @@ -48,6 +48,7 @@ public class DutyCycle implements Sendable, AutoCloseable { /** Close the DutyCycle and free all resources. */ @Override public void close() { + SendableRegistry.remove(this); DutyCycleJNI.free(m_handle); } diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/counter/EdgeConfiguration.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/counter/EdgeConfiguration.java new file mode 100644 index 0000000000..9d58fbf1db --- /dev/null +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/counter/EdgeConfiguration.java @@ -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. + +package edu.wpi.first.wpilibj.counter; + +public enum EdgeConfiguration { + kNone(false, false), + kRisingEdge(true, false), + kFallingEdge(false, true), + kBoth(true, true); + + @SuppressWarnings("MemberName") + public final boolean rising; + + @SuppressWarnings("MemberName") + public final boolean falling; + + EdgeConfiguration(boolean rising, boolean falling) { + this.rising = rising; + this.falling = falling; + } +} diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/counter/ExternalDirectionCounter.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/counter/ExternalDirectionCounter.java new file mode 100644 index 0000000000..c09fd6f9c1 --- /dev/null +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/counter/ExternalDirectionCounter.java @@ -0,0 +1,105 @@ +// 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.counter; + +import static edu.wpi.first.wpilibj.util.ErrorMessages.requireNonNullParam; + +import edu.wpi.first.hal.CounterJNI; +import edu.wpi.first.hal.FRCNetComm.tResourceType; +import edu.wpi.first.hal.HAL; +import edu.wpi.first.util.sendable.Sendable; +import edu.wpi.first.util.sendable.SendableBuilder; +import edu.wpi.first.util.sendable.SendableRegistry; +import edu.wpi.first.wpilibj.DigitalSource; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; + +/** Counter using external direction. */ +public class ExternalDirectionCounter implements Sendable, AutoCloseable { + private final DigitalSource m_countSource; + private final DigitalSource m_directionSource; + + private final int m_handle; + + /** + * Constructs a new ExternalDirectionCounter. + * + * @param countSource The source for counting. + * @param directionSource The source for selecting count direction. + */ + public ExternalDirectionCounter(DigitalSource countSource, DigitalSource directionSource) { + m_countSource = requireNonNullParam(countSource, "countSource", "ExternalDirectionCounter"); + m_directionSource = + requireNonNullParam(directionSource, "directionSource", "ExternalDirectionCounter"); + + ByteBuffer index = ByteBuffer.allocateDirect(4); + // set the byte order + index.order(ByteOrder.LITTLE_ENDIAN); + m_handle = CounterJNI.initializeCounter(CounterJNI.EXTERNAL_DIRECTION, index.asIntBuffer()); + + CounterJNI.setCounterUpSource( + m_handle, + countSource.getPortHandleForRouting(), + countSource.getAnalogTriggerTypeForRouting()); + CounterJNI.setCounterUpSourceEdge(m_handle, true, false); + + CounterJNI.setCounterDownSource( + m_handle, + directionSource.getPortHandleForRouting(), + directionSource.getAnalogTriggerTypeForRouting()); + CounterJNI.setCounterDownSourceEdge(m_handle, false, true); + CounterJNI.resetCounter(m_handle); + + int intIndex = index.getInt(); + HAL.report(tResourceType.kResourceType_Counter, intIndex + 1); + SendableRegistry.addLW(this, "External Direction Counter", intIndex); + } + + /** + * Gets the current count. + * + * @return The current count. + */ + public int getCount() { + return CounterJNI.getCounter(m_handle); + } + + /** + * Sets to revert the counter direction. + * + * @param reverseDirection True to reverse counting direction. + */ + public void setReverseDirection(boolean reverseDirection) { + CounterJNI.setCounterReverseDirection(m_handle, reverseDirection); + } + + /** Resets the current count. */ + public void reset() { + CounterJNI.resetCounter(m_handle); + } + + /** + * Sets the edge configuration for counting. + * + * @param configuration The counting edge configuration. + */ + public void setEdgeConfiguration(EdgeConfiguration configuration) { + CounterJNI.setCounterUpSourceEdge(m_handle, configuration.rising, configuration.falling); + } + + @Override + public void close() throws Exception { + SendableRegistry.remove(this); + CounterJNI.freeCounter(m_handle); + CounterJNI.suppressUnused(m_countSource); + CounterJNI.suppressUnused(m_directionSource); + } + + @Override + public void initSendable(SendableBuilder builder) { + builder.setSmartDashboardType("External Direction Counter"); + builder.addDoubleProperty("Count", this::getCount, null); + } +} diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/counter/Tachometer.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/counter/Tachometer.java new file mode 100644 index 0000000000..037b28ecd0 --- /dev/null +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/counter/Tachometer.java @@ -0,0 +1,163 @@ +// 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.counter; + +import static edu.wpi.first.wpilibj.util.ErrorMessages.requireNonNullParam; + +import edu.wpi.first.hal.CounterJNI; +import edu.wpi.first.hal.FRCNetComm.tResourceType; +import edu.wpi.first.hal.HAL; +import edu.wpi.first.util.sendable.Sendable; +import edu.wpi.first.util.sendable.SendableBuilder; +import edu.wpi.first.util.sendable.SendableRegistry; +import edu.wpi.first.wpilibj.DigitalSource; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; + +/** Tachometer. */ +public class Tachometer implements Sendable, AutoCloseable { + private final DigitalSource m_source; + private final int m_handle; + private int m_edgesPerRevolution = 1; + + /** + * Constructs a new tachometer. + * + * @param source The source. + */ + public Tachometer(DigitalSource source) { + m_source = requireNonNullParam(source, "source", "Tachometer"); + + ByteBuffer index = ByteBuffer.allocateDirect(4); + // set the byte order + index.order(ByteOrder.LITTLE_ENDIAN); + m_handle = CounterJNI.initializeCounter(CounterJNI.TWO_PULSE, index.asIntBuffer()); + + CounterJNI.setCounterUpSource( + m_handle, source.getPortHandleForRouting(), source.getAnalogTriggerTypeForRouting()); + CounterJNI.setCounterUpSourceEdge(m_handle, true, false); + + int intIndex = index.getInt(); + HAL.report(tResourceType.kResourceType_Counter, intIndex + 1); + SendableRegistry.addLW(this, "Tachometer", intIndex); + } + + @Override + public void close() throws Exception { + SendableRegistry.remove(this); + CounterJNI.freeCounter(m_handle); + CounterJNI.suppressUnused(m_source); + } + + /** + * Gets the tachometer period. + * + * @return Current period (in seconds). + */ + public double getPeriod() { + return CounterJNI.getCounterPeriod(m_handle); + } + + /** + * Gets the tachometer frequency. + * + * @return Current frequency (in hertz). + */ + public double getFrequency() { + double period = getPeriod(); + if (period == 0) { + return 0; + } + return period; + } + + /** + * Gets the number of edges per revolution. + * + * @return Edges per revolution. + */ + public int getEdgesPerRevolution() { + return m_edgesPerRevolution; + } + + /** + * Sets the number of edges per revolution. + * + * @param edgesPerRevolution Edges per revolution. + */ + public void setEdgesPerRevolution(int edgesPerRevolution) { + m_edgesPerRevolution = edgesPerRevolution; + } + + /** + * Gets the current tachometer revolution per minute. + * + *

setEdgesPerRevolution must be set with a non 0 value for this to return valid values. + * + * @return Current RPM. + */ + public double getRevolutionsPerMinute() { + double period = getPeriod(); + if (period == 0) { + return 0; + } + int edgesPerRevolution = getEdgesPerRevolution(); + if (edgesPerRevolution == 0) { + return 0; + } + return ((1.0 / edgesPerRevolution) / period) * 60; + } + + /** + * Gets if the tachometer is stopped. + * + * @return True if the tachometer is stopped. + */ + public boolean getStopped() { + return CounterJNI.getCounterStopped(m_handle); + } + + /** + * Gets the number of samples to average. + * + * @return Samples to average. + */ + public int getSamplesToAverage() { + return CounterJNI.getCounterSamplesToAverage(m_handle); + } + + /** + * Sets the number of samples to average. + * + * @param samplesToAverage Samples to average. + */ + public void setSamplesToAverage(int samplesToAverage) { + CounterJNI.setCounterSamplesToAverage(m_handle, samplesToAverage); + } + + /** + * Sets the maximum period before the tachometer is considered stopped. + * + * @param maxPeriod The max period (in seconds). + */ + public void setMaxPeriod(double maxPeriod) { + CounterJNI.setCounterMaxPeriod(m_handle, maxPeriod); + } + + /** + * Sets if to update when empty. + * + * @param updateWhenEmpty Update when empty if true. + */ + public void setUpdateWhenEmpty(boolean updateWhenEmpty) { + CounterJNI.setCounterUpdateWhenEmpty(m_handle, updateWhenEmpty); + } + + @Override + public void initSendable(SendableBuilder builder) { + builder.setSmartDashboardType("Tachometer"); + builder.addDoubleProperty("RPM", this::getRevolutionsPerMinute, null); + } +} diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/counter/UpDownCounter.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/counter/UpDownCounter.java new file mode 100644 index 0000000000..0d3e341136 --- /dev/null +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/counter/UpDownCounter.java @@ -0,0 +1,113 @@ +// 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.counter; + +import edu.wpi.first.hal.CounterJNI; +import edu.wpi.first.hal.FRCNetComm.tResourceType; +import edu.wpi.first.hal.HAL; +import edu.wpi.first.util.sendable.Sendable; +import edu.wpi.first.util.sendable.SendableBuilder; +import edu.wpi.first.util.sendable.SendableRegistry; +import edu.wpi.first.wpilibj.DigitalSource; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; + +/** Up Down Counter. */ +public class UpDownCounter implements Sendable, AutoCloseable { + private DigitalSource m_upSource; + private DigitalSource m_downSource; + + private final int m_handle; + + /** + * Constructs a new UpDown Counter. + * + * @param upSource The up count source (can be null). + * @param downSource The down count source (can be null). + */ + public UpDownCounter(DigitalSource upSource, DigitalSource downSource) { + ByteBuffer index = ByteBuffer.allocateDirect(4); + // set the byte order + index.order(ByteOrder.LITTLE_ENDIAN); + m_handle = CounterJNI.initializeCounter(CounterJNI.TWO_PULSE, index.asIntBuffer()); + + if (upSource != null) { + m_upSource = upSource; + CounterJNI.setCounterUpSource( + m_handle, upSource.getPortHandleForRouting(), upSource.getAnalogTriggerTypeForRouting()); + CounterJNI.setCounterUpSourceEdge(m_handle, true, false); + } + + if (downSource != null) { + m_downSource = downSource; + CounterJNI.setCounterDownSource( + m_handle, + downSource.getPortHandleForRouting(), + downSource.getAnalogTriggerTypeForRouting()); + CounterJNI.setCounterDownSourceEdge(m_handle, true, false); + } + + reset(); + + int intIndex = index.getInt(); + HAL.report(tResourceType.kResourceType_Counter, intIndex + 1); + SendableRegistry.addLW(this, "UpDown Counter", intIndex); + } + + @Override + public void close() throws Exception { + SendableRegistry.remove(this); + CounterJNI.freeCounter(m_handle); + CounterJNI.suppressUnused(m_upSource); + CounterJNI.suppressUnused(m_downSource); + } + + /** + * Sets the configuration for the up source. + * + * @param configuration The up source configuration. + */ + public void setUpEdgeConfiguration(EdgeConfiguration configuration) { + CounterJNI.setCounterUpSourceEdge(m_handle, configuration.rising, configuration.falling); + } + + /** + * Sets the configuration for the down source. + * + * @param configuration The down source configuration. + */ + public void setDownEdgeConfiguration(EdgeConfiguration configuration) { + CounterJNI.setCounterDownSourceEdge(m_handle, configuration.rising, configuration.falling); + } + + /** Resets the current count. */ + public void reset() { + CounterJNI.resetCounter(m_handle); + } + + /** + * Sets to revert the counter direction. + * + * @param reverseDirection True to reverse counting direction. + */ + public void setReverseDirection(boolean reverseDirection) { + CounterJNI.setCounterReverseDirection(m_handle, reverseDirection); + } + + /** + * Gets the current count. + * + * @return The current count. + */ + public int getCount() { + return CounterJNI.getCounter(m_handle); + } + + @Override + public void initSendable(SendableBuilder builder) { + builder.setSmartDashboardType("UpDown Counter"); + builder.addDoubleProperty("Count", this::getCount, null); + } +}