设备仿真

WPILib提供了一种以SimDevice API形式管理仿真设备数据的方法。

模拟核心WPILib设备类

核心WPILib设备类(即``Encoder’’,``Ultrasonic’’等)具有名为``EncoderSim’’,``UltrasonicSim’’等的仿真类。这些类允许与设备数据进行交互,而这些交互在模拟之外是不可能的或无效的。在模拟之外构造它们可能不会干扰您的代码,但是调用它们的函数等是未定义的行为-最好的情况下,它们什么都不做,更糟的情况可能会使您的代码崩溃!将功能仿真代码放在仅仿真功能中(例如simulationPeriodic())或用RobotBase.isReal()/ RobotBase :: IsReal()检查将它们包装起来(即constexpr``在C ++中)。

备注

本示例将使用EncoderSim类作为示例。其他模拟类的使用几乎是相同的。

创建模拟设备对象

可以通过两种方式构造仿真设备对象:

  • 接受常规硬件对象的构造函数。

  • 接受设备连接到的端口/索引/通道号的构造函数或工厂方法。这些数字将与用于构造常规硬件对象的数字相同。这对于单元测试<unit-testing>尤其有用。

// create a real encoder object on DIO 2,3
Encoder encoder = new Encoder(2, 3);
// create a sim controller for the encoder
EncoderSim simEncoder = new EncoderSim(encoder);
// create a real encoder object on DIO 2,3
frc::Encoder encoder{2, 3};
// create a sim controller for the encoder
frc::sim::EncoderSim simEncoder{encoder};

读写设备数据

每个模拟类对于每个字段``Xxx’’都有getter(getXxx()/''GetXxx())和setter(setXxx(value)/SetXxx(value))函数``。 getter函数将返回与常规设备类的getter相同的函数。

simEncoder.setCount(100);
encoder.getCount(); // 100
simEncoder.getCount(); // 100
simEncoder.SetCount(100);
encoder.GetCount(); // 100
simEncoder.GetCount(); // 100

注册回调

除了getter和setter之外,每个字段还具有一个registerXxxCallback()函数,该函数注册一个回调函数,只要该字段值发生更改并返回CallbackStore对象,该回调函数便会运行。回调接受字段名称的字符串参数和包含新值的“ HALValue”对象。从``HALValue’’中获取值之前,请检查包含的值的类型。可能的类型是``HALValue.kBoolean’’/HAL_BOOL'',``HALValue.kDouble''/``HAL_DOUBLE'',``HALValue.kEnum''/``HAL_ENUM'',``HALValue.kInt ``/``HAL_INTHALValue.kLong /HAL_LONG

在Java中,对CallbackStore对象调用close()取消回调。保留对对象的引用,以免垃圾被回收-否则回调将被GC取消。要向回调提供任意数据,请在lambda中捕获它或使用方法引用。

在C ++中,将“ CallbackStore”对象保存在正确的范围内-当对象超出范围并被销毁时,回调将被取消。可以通过param参数将任意数据传递给回调。

警告

尝试从包含其他类型的HALValue中检索类型的值是未定义的行为。

NotifyCallback callback = (String name, HALValue value) -> {
  if (value.getType() == HALValue.kInt) {
    System.out.println("Value of " + name + " is " + value.getInt());
  }
}
CallbackStore store = simEncoder.registerCountCallback(callback);

store.close(); // cancel the callback
HAL_NotifyCallback callback = [](const char* name, void* param, const HALValue* value) {
  if (value->type == HAL_INT) {
    wpi::outs() << "Value of " << name << " is " << value->data.v_int << '\n';
  }
};
frc::sim::CallbackStore store = simEncoder.RegisterCountCallback(callback);
// the callback will be canceled when ``store`` goes out of scope

模拟其他设备-SimDeviceSim类

备注

供应商可能会实现与SimDevice API的连接,与此处所述稍有不同。他们可能还会提供特定于其设备类的仿真类。有关他们支持什么以及如何获得更多信息,请参阅供应商的文档。

The SimDeviceSim (not SimDevice!) class is a general device simulation object for devices that aren’t core WPILib devices and therefore don’t have specific simulation classes - such as vendor devices. These devices will show up in the Other Devices tab of the SimGUI.

使用与厂商用来在其设备类中构造基础``SimDevice’’的键相同的字符串键创建``SimDeviceSim’’对象。该密钥是设备在``其他设备’’选项卡中显示的密钥,通常为``前缀:设备名称[索引]’’的形式。如果键包含端口/索引/通道号,则可以将它们作为单独的参数传递给“ SimDeviceSim”构造函数。该密钥包含一个默认情况下在SimGUI中隐藏的前缀,可以通过选择:guilabel:`Show prefix`选项来显示。在传递给``SimDeviceSim``的密钥中不包含此前缀将与设备不匹配!

SimDeviceSim device = new SimDeviceSim(deviceKey, index);
frc::sim::SimDeviceSim device{deviceKey, index};

一旦有了“ SimDeviceSim”,就可以获取代表设备字段的“ SimValue”对象。特定类型的``SimDouble’’,``SimInt’’,``SimLong’’,``SimBoolean’’和``SimEnum``子类也存在,并且应该使用它们来代替类型不安全的``SimValue ``课。这些是使用与供应商用来定义该字段的字符串相同的字符串键从“ SimDeviceSim”构造的。该键是该字段在SimGUI中显示的键。尝试在模拟之外或设备或字段键不匹配时检索“ SimValue”对象将返回“ null”-这可能会导致Java中出现“ NullPointerException”或C ++中出现未定义的行为。

SimDouble field = device.getDouble(fieldKey);
field.get();
field.set(value);
hal::SimDouble field = device.GetDouble(fieldKey);
field.Get();
field.Set(value);