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.hal;
006
007/**
008 * A wrapper around a simulator device handle.
009 *
010 * <p>Teams: if you are using this class, you are likely confusing it for {@link
011 * edu.wpi.first.wpilibj.simulation.SimDeviceSim}.
012 *
013 * <p>Vendors: This class should be used from inside the device class to define the
014 * properties/fields of the device. Use {@link #create} to get a SimDevice object, then use {@link
015 * #createDouble(String, Direction, double)} or similar to define the device's fields. See {@link
016 * edu.wpi.first.wpilibj.ADXRS450_Gyro} for an example implementation.
017 */
018public class SimDevice implements AutoCloseable {
019  /** Sim device direction. */
020  public enum Direction {
021    /** Input direction for simulation devices. */
022    kInput(SimDeviceJNI.kInput),
023    /** Output direction for simulation devices. */
024    kOutput(SimDeviceJNI.kOutput),
025    /** Bidirectional direction for simulation devices. */
026    kBidir(SimDeviceJNI.kBidir);
027
028    /** The native value of this Direction. */
029    public final int m_value;
030
031    Direction(int value) {
032      m_value = value;
033    }
034  }
035
036  /**
037   * Creates a simulated device.
038   *
039   * <p>The device name must be unique. Returns null if the device name already exists. If multiple
040   * instances of the same device are desired, recommend appending the instance/unique identifier in
041   * brackets to the base name, e.g. "device[1]".
042   *
043   * <p>Using a device name of the form "Type:Name" will create a WebSockets node with a type value
044   * of "Type" and a device value of "Name"
045   *
046   * <p>null is returned if not in simulation.
047   *
048   * @param name device name
049   * @return simulated device object
050   */
051  public static SimDevice create(String name) {
052    int handle = SimDeviceJNI.createSimDevice(name);
053    if (handle <= 0) {
054      return null;
055    }
056    return new SimDevice(handle);
057  }
058
059  /**
060   * Creates a simulated device.
061   *
062   * <p>The device name must be unique. Returns null if the device name already exists. This is a
063   * convenience method that appends index in brackets to the device name, e.g. passing index=1
064   * results in "device[1]" for the device name.
065   *
066   * <p>Using a device name of the form "Type:Name" will create a WebSockets node with a type value
067   * of "Type" and a device value of "Name"
068   *
069   * <p>null is returned if not in simulation.
070   *
071   * @param name device name
072   * @param index device index number to append to name
073   * @return simulated device object
074   */
075  public static SimDevice create(String name, int index) {
076    return create(name + "[" + index + "]");
077  }
078
079  /**
080   * Creates a simulated device.
081   *
082   * <p>The device name must be unique. Returns null if the device name already exists. This is a
083   * convenience method that appends index and channel in brackets to the device name, e.g. passing
084   * index=1 and channel=2 results in "device[1,2]" for the device name.
085   *
086   * <p>Using a device name of the form "Type:Name" will create a WebSockets node with a type value
087   * of "Type" and a device value of "Name"
088   *
089   * <p>null is returned if not in simulation.
090   *
091   * @param name device name
092   * @param index device index number to append to name
093   * @param channel device channel number to append to name
094   * @return simulated device object
095   */
096  public static SimDevice create(String name, int index, int channel) {
097    return create(name + "[" + index + "," + channel + "]");
098  }
099
100  /**
101   * Wraps a simulated device handle as returned by SimDeviceJNI.createSimDevice().
102   *
103   * @param handle simulated device handle
104   */
105  public SimDevice(int handle) {
106    m_handle = handle;
107  }
108
109  @Override
110  public void close() {
111    SimDeviceJNI.freeSimDevice(m_handle);
112  }
113
114  /**
115   * Get the internal device handle.
116   *
117   * @return internal handle
118   */
119  public int getNativeHandle() {
120    return m_handle;
121  }
122
123  /**
124   * Get the name of the simulated device.
125   *
126   * @return the name
127   */
128  public String getName() {
129    return SimDeviceJNI.getSimDeviceName(m_handle);
130  }
131
132  /**
133   * Creates a value on the simulated device.
134   *
135   * <p>Returns null if not in simulation.
136   *
137   * @param name value name
138   * @param direction input/output/bidir (from perspective of user code)
139   * @param initialValue initial value
140   * @return simulated value object
141   */
142  public SimValue createValue(String name, Direction direction, HALValue initialValue) {
143    int handle = SimDeviceJNI.createSimValue(m_handle, name, direction.m_value, initialValue);
144    if (handle <= 0) {
145      return null;
146    }
147    return new SimValue(handle);
148  }
149
150  /**
151   * Creates an int value on the simulated device.
152   *
153   * <p>Returns null if not in simulation.
154   *
155   * @param name value name
156   * @param direction input/output/bidir (from perspective of user code)
157   * @param initialValue initial value
158   * @return simulated double value object
159   */
160  public SimInt createInt(String name, Direction direction, int initialValue) {
161    int handle = SimDeviceJNI.createSimValueInt(m_handle, name, direction.m_value, initialValue);
162    if (handle <= 0) {
163      return null;
164    }
165    return new SimInt(handle);
166  }
167
168  /**
169   * Creates a long value on the simulated device.
170   *
171   * <p>Returns null if not in simulation.
172   *
173   * @param name value name
174   * @param direction input/output/bidir (from perspective of user code)
175   * @param initialValue initial value
176   * @return simulated double value object
177   */
178  public SimLong createLong(String name, Direction direction, long initialValue) {
179    int handle = SimDeviceJNI.createSimValueLong(m_handle, name, direction.m_value, initialValue);
180    if (handle <= 0) {
181      return null;
182    }
183    return new SimLong(handle);
184  }
185
186  /**
187   * Creates a double value on the simulated device.
188   *
189   * <p>Returns null if not in simulation.
190   *
191   * @param name value name
192   * @param direction input/output/bidir (from perspective of user code)
193   * @param initialValue initial value
194   * @return simulated double value object
195   */
196  public SimDouble createDouble(String name, Direction direction, double initialValue) {
197    int handle = SimDeviceJNI.createSimValueDouble(m_handle, name, direction.m_value, initialValue);
198    if (handle <= 0) {
199      return null;
200    }
201    return new SimDouble(handle);
202  }
203
204  /**
205   * Creates an enumerated value on the simulated device.
206   *
207   * <p>Enumerated values are always in the range 0 to numOptions-1.
208   *
209   * <p>Returns null if not in simulation.
210   *
211   * @param name value name
212   * @param direction input/output/bidir (from perspective of user code)
213   * @param options array of option descriptions
214   * @param initialValue initial value (selection)
215   * @return simulated enum value object
216   */
217  public SimEnum createEnum(String name, Direction direction, String[] options, int initialValue) {
218    int handle =
219        SimDeviceJNI.createSimValueEnum(m_handle, name, direction.m_value, options, initialValue);
220    if (handle <= 0) {
221      return null;
222    }
223    return new SimEnum(handle);
224  }
225
226  /**
227   * Creates an enumerated value on the simulated device with double values.
228   *
229   * <p>Enumerated values are always in the range 0 to numOptions-1.
230   *
231   * <p>Returns null if not in simulation.
232   *
233   * @param name value name
234   * @param direction input/output/bidir (from perspective of user code)
235   * @param options array of option descriptions
236   * @param optionValues array of option values (must be the same size as options)
237   * @param initialValue initial value (selection)
238   * @return simulated enum value object
239   */
240  public SimEnum createEnumDouble(
241      String name, Direction direction, String[] options, double[] optionValues, int initialValue) {
242    int handle =
243        SimDeviceJNI.createSimValueEnumDouble(
244            m_handle, name, direction.m_value, options, optionValues, initialValue);
245    if (handle <= 0) {
246      return null;
247    }
248    return new SimEnum(handle);
249  }
250
251  /**
252   * Creates a boolean value on the simulated device.
253   *
254   * <p>Returns null if not in simulation.
255   *
256   * @param name value name
257   * @param direction input/output/bidir (from perspective of user code)
258   * @param initialValue initial value
259   * @return simulated boolean value object
260   */
261  public SimBoolean createBoolean(String name, Direction direction, boolean initialValue) {
262    int handle =
263        SimDeviceJNI.createSimValueBoolean(m_handle, name, direction.m_value, initialValue);
264    if (handle <= 0) {
265      return null;
266    }
267    return new SimBoolean(handle);
268  }
269
270  private final int m_handle;
271}