设备仿真
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_INT
,HALValue.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);