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 org.wpilib.driverstation;
006
007import java.util.Optional;
008import org.wpilib.math.geometry.Rotation2d;
009import org.wpilib.system.Timer;
010
011/** A controller POV direction. */
012public enum POVDirection {
013  /** POV center. */
014  CENTER(0x00),
015  /** POV up. */
016  UP(0x01),
017  /** POV up right. */
018  UP_RIGHT(0x01 | 0x02),
019  /** POV right. */
020  RIGHT(0x02),
021  /** POV down right. */
022  DOWN_RIGHT(0x02 | 0x04),
023  /** POV down. */
024  DOWN(0x04),
025  /** POV down left. */
026  DOWN_LEFT(0x04 | 0x08),
027  /** POV left. */
028  LEFT(0x08),
029  /** POV up left. */
030  UP_LEFT(0x01 | 0x08);
031
032  private static final double INVALID_POV_VALUE_INTERVAL = 1.0;
033  private static double s_nextMessageTime;
034
035  /**
036   * Converts a byte value into a POVDirection enum value.
037   *
038   * @param value The byte value to convert.
039   * @return The corresponding POVDirection enum value.
040   * @throws IllegalArgumentException If value does not correspond to a POVDirection.
041   */
042  public static POVDirection of(byte value) {
043    for (var direction : values()) {
044      if (direction.value == value) {
045        return direction;
046      }
047    }
048    double currentTime = Timer.getTimestamp();
049    if (currentTime > s_nextMessageTime) {
050      DriverStationErrors.reportError("Invalid POV value " + value + "!", false);
051      s_nextMessageTime = currentTime + INVALID_POV_VALUE_INTERVAL;
052    }
053    return CENTER;
054  }
055
056  /** The corresponding HAL value. */
057  public final byte value;
058
059  POVDirection(int value) {
060    this.value = (byte) value;
061  }
062
063  /**
064   * Gets the angle of a POVDirection.
065   *
066   * @return The angle clockwise from straight up, or Optional.empty() if this POVDirection is
067   *     CENTER.
068   */
069  public Optional<Rotation2d> getAngle() {
070    return switch (this) {
071      case CENTER -> Optional.empty();
072      case UP -> Optional.of(Rotation2d.fromDegrees(0));
073      case UP_RIGHT -> Optional.of(Rotation2d.fromDegrees(45));
074      case RIGHT -> Optional.of(Rotation2d.fromDegrees(90));
075      case DOWN_RIGHT -> Optional.of(Rotation2d.fromDegrees(135));
076      case DOWN -> Optional.of(Rotation2d.fromDegrees(180));
077      case DOWN_LEFT -> Optional.of(Rotation2d.fromDegrees(225));
078      case LEFT -> Optional.of(Rotation2d.fromDegrees(270));
079      case UP_LEFT -> Optional.of(Rotation2d.fromDegrees(315));
080    };
081  }
082}