001/*
002 * Copyright (c) 2018-2021 REV Robotics
003 *
004 * Redistribution and use in source and binary forms, with or without
005 * modification, are permitted provided that the following conditions are met:
006 *
007 * 1. Redistributions of source code must retain the above copyright notice,
008 *    this list of conditions and the following disclaimer.
009 * 2. Redistributions in binary form must reproduce the above copyright
010 *    notice, this list of conditions and the following disclaimer in the
011 *    documentation and/or other materials provided with the distribution.
012 * 3. Neither the name of REV Robotics nor the names of its
013 *    contributors may be used to endorse or promote products derived from
014 *    this software without specific prior written permission.
015 *
016 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
017 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
018 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
019 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
020 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
021 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
022 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
023 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
024 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
025 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
026 * POSSIBILITY OF SUCH DAMAGE.
027 */
028
029package com.revrobotics;
030
031import com.revrobotics.jni.CANSparkMaxJNI;
032import java.util.Locale;
033
034/**
035 * Get an instance of this class by using {@link CANSparkMax#getForwardLimitSwitch(Type)} or {@link
036 * CANSparkMax#getReverseLimitSwitch(Type)}.
037 */
038public class SparkMaxLimitSwitch implements CANDigitalInput {
039  // package-private (can only be used by other classes in this package)
040  enum Direction {
041    kForward(0),
042    kReverse(1);
043
044    @SuppressWarnings("MemberName")
045    public final int value;
046
047    Direction(int value) {
048      this.value = value;
049    }
050  }
051
052  public enum Type {
053    kNormallyOpen(0),
054    kNormallyClosed(1);
055
056    @SuppressWarnings("MemberName")
057    public final int value;
058
059    Type(int value) {
060      this.value = value;
061    }
062  }
063
064  private final CANSparkMax m_device;
065  private final Direction m_limitSwitch;
066  final Type m_switchType;
067
068  // package-private (can only be used by other classes in this package)
069  SparkMaxLimitSwitch(CANSparkMax device, Direction direction, Type switchType) {
070    m_device = device;
071    m_limitSwitch = direction;
072    m_switchType = switchType;
073
074    if (direction == null) {
075      throw new IllegalArgumentException("limitSwitch must not be null");
076    }
077
078    if (switchType == null) {
079      throw new IllegalArgumentException("polarity must not be null");
080    }
081
082    REVLibError error =
083        REVLibError.fromInt(
084            CANSparkMaxJNI.c_SparkMax_AttemptToSetDataPortConfig(
085                device.sparkMaxHandle, CANSparkMax.DataPortConfig.kLimitSwitches.m_value));
086    if (error == REVLibError.kSparkMaxDataPortAlreadyConfiguredDifferently) {
087      CANSparkMax.DataPortConfig currentConfig =
088          CANSparkMax.DataPortConfig.fromInt(
089              CANSparkMaxJNI.c_SparkMax_GetDataPortConfig(device.sparkMaxHandle));
090      throw new IllegalStateException(
091          String.format(
092              Locale.ENGLISH,
093              "Limit switches cannot be used while %s is active",
094              currentConfig.m_name));
095    }
096
097    CANSparkMaxJNI.c_SparkMax_SetLimitPolarity(
098        m_device.sparkMaxHandle, direction.value, switchType.value);
099  }
100
101  /**
102   * Returns {@code true} if the limit switch is pressed, based on the selected polarity.
103   *
104   * <p>This method works even if the limit switch is not enabled for controller shutdown.
105   *
106   * @return {@code true} if the limit switch is pressed
107   */
108  public boolean isPressed() {
109    m_device.throwIfClosed();
110    if (m_limitSwitch == Direction.kForward) {
111      return m_device.getFault(CANSparkMax.FaultID.kHardLimitFwd);
112    } else {
113      return m_device.getFault(CANSparkMax.FaultID.kHardLimitRev);
114    }
115  }
116
117  /**
118   * Returns {@code true} if the limit switch is pressed, based on the selected polarity.
119   *
120   * <p>This method works even if the limit switch is not enabled.
121   *
122   * @return {@code true} if the limit switch is pressed
123   * @deprecated Use {@link #isPressed()} instead
124   */
125  @Override
126  @Deprecated(forRemoval = true)
127  public boolean get() {
128    return isPressed();
129  }
130
131  /**
132   * Enables or disables controller shutdown based on the limit switch.
133   *
134   * @param enable Enable/disable motor shutdown based on the limit switch state. This does not
135   *     affect the result of the get() command.
136   * @return {@link REVLibError#kOk} if successful
137   */
138  @Override
139  public REVLibError enableLimitSwitch(boolean enable) {
140    m_device.throwIfClosed();
141    return REVLibError.fromInt(
142        CANSparkMaxJNI.c_SparkMax_EnableLimitSwitch(
143            m_device.sparkMaxHandle, m_limitSwitch.value, enable));
144  }
145
146  /** @return {@code true} if the limit switch is enabled */
147  @Override
148  public boolean isLimitSwitchEnabled() {
149    m_device.throwIfClosed();
150    return CANSparkMaxJNI.c_SparkMax_IsLimitEnabled(m_device.sparkMaxHandle, m_limitSwitch.value);
151  }
152}