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  public enum Direction {
020    kInput(SimDeviceJNI.kInput),
021    kOutput(SimDeviceJNI.kOutput),
022    kBidir(SimDeviceJNI.kBidir);
023
024    public final int m_value;
025
026    Direction(int value) {
027      m_value = value;
028    }
029  }
030
031  /**
032   * Creates a simulated device.
033   *
034   * <p>The device name must be unique. Returns null if the device name already exists. If multiple
035   * instances of the same device are desired, recommend appending the instance/unique identifer in
036   * brackets to the base name, e.g. "device[1]".
037   *
038   * <p>null is returned if not in simulation.
039   *
040   * @param name device name
041   * @return simulated device object
042   */
043  public static SimDevice create(String name) {
044    int handle = SimDeviceJNI.createSimDevice(name);
045    if (handle <= 0) {
046      return null;
047    }
048    return new SimDevice(handle);
049  }
050
051  /**
052   * Creates a simulated device.
053   *
054   * <p>The device name must be unique. Returns null if the device name already exists. This is a
055   * convenience method that appends index in brackets to the device name, e.g. passing index=1
056   * results in "device[1]" for the device name.
057   *
058   * <p>null is returned if not in simulation.
059   *
060   * @param name device name
061   * @param index device index number to append to name
062   * @return simulated device object
063   */
064  public static SimDevice create(String name, int index) {
065    return create(name + "[" + index + "]");
066  }
067
068  /**
069   * Creates a simulated device.
070   *
071   * <p>The device name must be unique. Returns null if the device name already exists. This is a
072   * convenience method that appends index and channel in brackets to the device name, e.g. passing
073   * index=1 and channel=2 results in "device[1,2]" for the device name.
074   *
075   * <p>null is returned if not in simulation.
076   *
077   * @param name device name
078   * @param index device index number to append to name
079   * @param channel device channel number to append to name
080   * @return simulated device object
081   */
082  public static SimDevice create(String name, int index, int channel) {
083    return create(name + "[" + index + "," + channel + "]");
084  }
085
086  /**
087   * Wraps a simulated device handle as returned by SimDeviceJNI.createSimDevice().
088   *
089   * @param handle simulated device handle
090   */
091  public SimDevice(int handle) {
092    m_handle = handle;
093  }
094
095  @Override
096  public void close() {
097    SimDeviceJNI.freeSimDevice(m_handle);
098  }
099
100  /**
101   * Get the internal device handle.
102   *
103   * @return internal handle
104   */
105  public int getNativeHandle() {
106    return m_handle;
107  }
108
109  /**
110   * Creates a value on the simulated device.
111   *
112   * <p>Returns null if not in simulation.
113   *
114   * @param name value name
115   * @param readonly if the value should not be written from simulation side
116   * @param initialValue initial value
117   * @return simulated value object
118   * @deprecated Use direction function instead
119   */
120  @Deprecated
121  public SimValue createValue(String name, boolean readonly, HALValue initialValue) {
122    return createValue(name, readonly ? Direction.kOutput : Direction.kInput, initialValue);
123  }
124
125  /**
126   * Creates a value on the simulated device.
127   *
128   * <p>Returns null if not in simulation.
129   *
130   * @param name value name
131   * @param direction input/output/bidir (from perspective of user code)
132   * @param initialValue initial value
133   * @return simulated value object
134   */
135  public SimValue createValue(String name, Direction direction, HALValue initialValue) {
136    int handle = SimDeviceJNI.createSimValue(m_handle, name, direction.m_value, initialValue);
137    if (handle <= 0) {
138      return null;
139    }
140    return new SimValue(handle);
141  }
142
143  /**
144   * Creates an int value on the simulated device.
145   *
146   * <p>Returns null if not in simulation.
147   *
148   * @param name value name
149   * @param direction input/output/bidir (from perspective of user code)
150   * @param initialValue initial value
151   * @return simulated double value object
152   */
153  public SimInt createInt(String name, Direction direction, int initialValue) {
154    int handle = SimDeviceJNI.createSimValueInt(m_handle, name, direction.m_value, initialValue);
155    if (handle <= 0) {
156      return null;
157    }
158    return new SimInt(handle);
159  }
160
161  /**
162   * Creates a long value on the simulated device.
163   *
164   * <p>Returns null if not in simulation.
165   *
166   * @param name value name
167   * @param direction input/output/bidir (from perspective of user code)
168   * @param initialValue initial value
169   * @return simulated double value object
170   */
171  public SimLong createLong(String name, Direction direction, long initialValue) {
172    int handle = SimDeviceJNI.createSimValueLong(m_handle, name, direction.m_value, initialValue);
173    if (handle <= 0) {
174      return null;
175    }
176    return new SimLong(handle);
177  }
178
179  /**
180   * Creates a double value on the simulated device.
181   *
182   * <p>Returns null if not in simulation.
183   *
184   * @param name value name
185   * @param readonly if the value should not be written from simulation side
186   * @param initialValue initial value
187   * @return simulated double value object
188   * @deprecated Use direction function instead
189   */
190  @Deprecated
191  public SimDouble createDouble(String name, boolean readonly, double initialValue) {
192    return createDouble(name, readonly ? Direction.kOutput : Direction.kInput, initialValue);
193  }
194
195  /**
196   * Creates a double value on the simulated device.
197   *
198   * <p>Returns null if not in simulation.
199   *
200   * @param name value name
201   * @param direction input/output/bidir (from perspective of user code)
202   * @param initialValue initial value
203   * @return simulated double value object
204   */
205  public SimDouble createDouble(String name, Direction direction, double initialValue) {
206    int handle = SimDeviceJNI.createSimValueDouble(m_handle, name, direction.m_value, initialValue);
207    if (handle <= 0) {
208      return null;
209    }
210    return new SimDouble(handle);
211  }
212
213  /**
214   * Creates an enumerated value on the simulated device.
215   *
216   * <p>Enumerated values are always in the range 0 to numOptions-1.
217   *
218   * <p>Returns null if not in simulation.
219   *
220   * @param name value name
221   * @param readonly if the value should not be written from simulation side
222   * @param options array of option descriptions
223   * @param initialValue initial value (selection)
224   * @return simulated enum value object
225   * @deprecated Use direction function instead
226   */
227  @Deprecated
228  public SimEnum createEnum(String name, boolean readonly, String[] options, int initialValue) {
229    return createEnum(name, readonly ? Direction.kOutput : Direction.kInput, options, initialValue);
230  }
231
232  /**
233   * Creates an enumerated value on the simulated device.
234   *
235   * <p>Enumerated values are always in the range 0 to numOptions-1.
236   *
237   * <p>Returns null if not in simulation.
238   *
239   * @param name value name
240   * @param direction input/output/bidir (from perspective of user code)
241   * @param options array of option descriptions
242   * @param initialValue initial value (selection)
243   * @return simulated enum value object
244   */
245  public SimEnum createEnum(String name, Direction direction, String[] options, int initialValue) {
246    int handle =
247        SimDeviceJNI.createSimValueEnum(m_handle, name, direction.m_value, options, initialValue);
248    if (handle <= 0) {
249      return null;
250    }
251    return new SimEnum(handle);
252  }
253
254  /**
255   * Creates an enumerated value on the simulated device with double values.
256   *
257   * <p>Enumerated values are always in the range 0 to numOptions-1.
258   *
259   * <p>Returns null if not in simulation.
260   *
261   * @param name value name
262   * @param direction input/output/bidir (from perspective of user code)
263   * @param options array of option descriptions
264   * @param optionValues array of option values (must be the same size as options)
265   * @param initialValue initial value (selection)
266   * @return simulated enum value object
267   */
268  public SimEnum createEnumDouble(
269      String name, Direction direction, String[] options, double[] optionValues, int initialValue) {
270    int handle =
271        SimDeviceJNI.createSimValueEnumDouble(
272            m_handle, name, direction.m_value, options, optionValues, initialValue);
273    if (handle <= 0) {
274      return null;
275    }
276    return new SimEnum(handle);
277  }
278
279  /**
280   * Creates a boolean value on the simulated device.
281   *
282   * <p>Returns null if not in simulation.
283   *
284   * @param name value name
285   * @param readonly if the value should not be written from simulation side
286   * @param initialValue initial value
287   * @return simulated boolean value object
288   * @deprecated Use direction function instead
289   */
290  @Deprecated
291  public SimBoolean createBoolean(String name, boolean readonly, boolean initialValue) {
292    return createBoolean(name, readonly ? Direction.kOutput : Direction.kInput, initialValue);
293  }
294
295  /**
296   * Creates a boolean value on the simulated device.
297   *
298   * <p>Returns null if not in simulation.
299   *
300   * @param name value name
301   * @param direction input/output/bidir (from perspective of user code)
302   * @param initialValue initial value
303   * @return simulated boolean value object
304   */
305  public SimBoolean createBoolean(String name, Direction direction, boolean initialValue) {
306    int handle =
307        SimDeviceJNI.createSimValueBoolean(m_handle, name, direction.m_value, initialValue);
308    if (handle <= 0) {
309      return null;
310    }
311    return new SimBoolean(handle);
312  }
313
314  private final int m_handle;
315}