001// Copyright (c) FIRST and other WPILib contributors.
002// Open Source Software; you can modify and/or share it under the terms of
003// the WPILib BSD license file in the root directory of this project.
004
005package edu.wpi.first.wpilibj.counter;
006
007import static edu.wpi.first.util.ErrorMessages.requireNonNullParam;
008
009import edu.wpi.first.hal.CounterJNI;
010import edu.wpi.first.hal.FRCNetComm.tResourceType;
011import edu.wpi.first.hal.HAL;
012import edu.wpi.first.util.sendable.Sendable;
013import edu.wpi.first.util.sendable.SendableBuilder;
014import edu.wpi.first.util.sendable.SendableRegistry;
015import edu.wpi.first.wpilibj.DigitalSource;
016import java.nio.ByteBuffer;
017import java.nio.ByteOrder;
018
019/**
020 * Counter using external direction.
021 *
022 * <p>This counts on an edge from one digital input and the whether it counts up or down based on
023 * the state of a second digital input.
024 */
025public class ExternalDirectionCounter implements Sendable, AutoCloseable {
026  private final DigitalSource m_countSource;
027  private final DigitalSource m_directionSource;
028
029  private final int m_handle;
030
031  /**
032   * Constructs a new ExternalDirectionCounter.
033   *
034   * @param countSource The source for counting.
035   * @param directionSource The source for selecting count direction.
036   */
037  public ExternalDirectionCounter(DigitalSource countSource, DigitalSource directionSource) {
038    m_countSource = requireNonNullParam(countSource, "countSource", "ExternalDirectionCounter");
039    m_directionSource =
040        requireNonNullParam(directionSource, "directionSource", "ExternalDirectionCounter");
041
042    ByteBuffer index = ByteBuffer.allocateDirect(4);
043    // set the byte order
044    index.order(ByteOrder.LITTLE_ENDIAN);
045    m_handle = CounterJNI.initializeCounter(CounterJNI.EXTERNAL_DIRECTION, index.asIntBuffer());
046
047    CounterJNI.setCounterUpSource(
048        m_handle,
049        countSource.getPortHandleForRouting(),
050        countSource.getAnalogTriggerTypeForRouting());
051    CounterJNI.setCounterUpSourceEdge(m_handle, true, false);
052
053    CounterJNI.setCounterDownSource(
054        m_handle,
055        directionSource.getPortHandleForRouting(),
056        directionSource.getAnalogTriggerTypeForRouting());
057    CounterJNI.setCounterDownSourceEdge(m_handle, false, true);
058    CounterJNI.resetCounter(m_handle);
059
060    int intIndex = index.getInt();
061    HAL.report(tResourceType.kResourceType_Counter, intIndex + 1);
062    SendableRegistry.addLW(this, "External Direction Counter", intIndex);
063  }
064
065  /**
066   * Gets the current count.
067   *
068   * @return The current count.
069   */
070  public int getCount() {
071    return CounterJNI.getCounter(m_handle);
072  }
073
074  /**
075   * Sets to reverse the counter direction.
076   *
077   * @param reverseDirection True to reverse counting direction.
078   */
079  public void setReverseDirection(boolean reverseDirection) {
080    CounterJNI.setCounterReverseDirection(m_handle, reverseDirection);
081  }
082
083  /** Resets the current count. */
084  public void reset() {
085    CounterJNI.resetCounter(m_handle);
086  }
087
088  /**
089   * Sets the edge configuration for counting.
090   *
091   * @param configuration The counting edge configuration.
092   */
093  public void setEdgeConfiguration(EdgeConfiguration configuration) {
094    CounterJNI.setCounterUpSourceEdge(m_handle, configuration.rising, configuration.falling);
095  }
096
097  @Override
098  public void close() {
099    SendableRegistry.remove(this);
100    CounterJNI.freeCounter(m_handle);
101    CounterJNI.suppressUnused(m_countSource);
102    CounterJNI.suppressUnused(m_directionSource);
103  }
104
105  @Override
106  public void initSendable(SendableBuilder builder) {
107    builder.setSmartDashboardType("External Direction Counter");
108    builder.addDoubleProperty("Count", this::getCount, null);
109  }
110}