001/*----------------------------------------------------------------------------*/
002/* Copyright (c) FIRST 2008-2017. All Rights Reserved.                        */
003/* Open Source Software - may be modified and shared by FRC teams. The code   */
004/* must be accompanied by the FIRST BSD license file in the root directory of */
005/* the project.                                                               */
006/*----------------------------------------------------------------------------*/
007
008package edu.wpi.first.wpilibj;
009
010import edu.wpi.first.wpilibj.hal.FRCNetComm.tResourceType;
011import edu.wpi.first.wpilibj.hal.HAL;
012import edu.wpi.first.wpilibj.livewindow.LiveWindow;
013import edu.wpi.first.wpilibj.tables.ITable;
014import edu.wpi.first.wpilibj.tables.ITableListener;
015
016/**
017 * Standard hobby style servo.
018 *
019 * <p>The range parameters default to the appropriate values for the Hitec HS-322HD servo provided
020 * in the FIRST Kit of Parts in 2008.
021 */
022public class Servo extends PWM {
023
024  private static final double kMaxServoAngle = 180.0;
025  private static final double kMinServoAngle = 0.0;
026
027  protected static final double kDefaultMaxServoPWM = 2.4;
028  protected static final double kDefaultMinServoPWM = .6;
029
030  /**
031   * Constructor.<br>
032   *
033   * <p>By default {@value #kDefaultMaxServoPWM} ms is used as the maxPWM value<br> By default
034   * {@value #kDefaultMinServoPWM} ms is used as the minPWM value<br>
035   *
036   * @param channel The PWM channel to which the servo is attached. 0-9 are on-board, 10-19 are on
037   *                the MXP port
038   */
039  public Servo(final int channel) {
040    super(channel);
041    setBounds(kDefaultMaxServoPWM, 0, 0, 0, kDefaultMinServoPWM);
042    setPeriodMultiplier(PeriodMultiplier.k4X);
043
044    LiveWindow.addActuator("Servo", getChannel(), this);
045    HAL.report(tResourceType.kResourceType_Servo, getChannel());
046  }
047
048
049  /**
050   * Set the servo position.
051   *
052   * <p>Servo values range from 0.0 to 1.0 corresponding to the range of full left to full right.
053   *
054   * @param value Position from 0.0 to 1.0.
055   */
056  public void set(double value) {
057    setPosition(value);
058  }
059
060  /**
061   * Get the servo position.
062   *
063   * <p>Servo values range from 0.0 to 1.0 corresponding to the range of full left to full right.
064   *
065   * @return Position from 0.0 to 1.0.
066   */
067  public double get() {
068    return getPosition();
069  }
070
071  /**
072   * Set the servo angle.
073   *
074   * <p>Assume that the servo angle is linear with respect to the PWM value (big assumption, need to
075   * test).
076   *
077   * <p>Servo angles that are out of the supported range of the servo simply "saturate" in that
078   * direction In other words, if the servo has a range of (X degrees to Y degrees) than angles of
079   * less than X result in an angle of X being set and angles of more than Y degrees result in an
080   * angle of Y being set.
081   *
082   * @param degrees The angle in degrees to set the servo.
083   */
084  public void setAngle(double degrees) {
085    if (degrees < kMinServoAngle) {
086      degrees = kMinServoAngle;
087    } else if (degrees > kMaxServoAngle) {
088      degrees = kMaxServoAngle;
089    }
090
091    setPosition(((degrees - kMinServoAngle)) / getServoAngleRange());
092  }
093
094  /**
095   * Get the servo angle.
096   *
097   * <p>Assume that the servo angle is linear with respect to the PWM value (big assumption, need to
098   * test).
099   *
100   * @return The angle in degrees to which the servo is set.
101   */
102  public double getAngle() {
103    return getPosition() * getServoAngleRange() + kMinServoAngle;
104  }
105
106  private double getServoAngleRange() {
107    return kMaxServoAngle - kMinServoAngle;
108  }
109
110  /*
111   * Live Window code, only does anything if live window is activated.
112   */
113  public String getSmartDashboardType() {
114    return "Servo";
115  }
116
117  private ITable m_table;
118  private ITableListener m_tableListener;
119
120  @Override
121  public void initTable(ITable subtable) {
122    m_table = subtable;
123    updateTable();
124  }
125
126  @Override
127  public void updateTable() {
128    if (m_table != null) {
129      m_table.putNumber("Value", get());
130    }
131  }
132
133  @Override
134  public void startLiveWindowMode() {
135    m_tableListener = new ITableListener() {
136      public void valueChanged(ITable itable, String key, Object value, boolean bln) {
137        set(((Double) value).doubleValue());
138      }
139    };
140    m_table.addTableListener("Value", m_tableListener, true);
141  }
142
143  @Override
144  public void stopLiveWindowMode() {
145    // TODO: Broken, should only remove the listener from "Value" only.
146    m_table.removeTableListener(m_tableListener);
147  }
148}