Périphérique de simulation

WPILib offre un moyen de gérer les données du périphérique de simulation sous la forme de l’API SimDevice

Simulation des classes fondamentales de l’infrastructure WPILib

Core WPILib device classes (i.e Encoder, Joystick, etc.) have simulation classes named EncoderSim, JoystickSim, and so on. These classes allow interactions with the device data that wouldn’t be possible or valid outside of simulation. Constructing them outside of simulation likely won’t interfere with your code, but calling their functions and the like is undefined behavior - in the best case they will do nothing, worse cases might crash your code! Place functional simulation code in simulation-only functions (such as simulationPeriodic()) or wrap them with RobotBase.isReal()/ RobotBase::IsReal() checks (which are constexpr in C++).

Note

Cet exemple utilisera la classe EncoderSim à titre d’exemple. L’utilisation d’autres classes de simulation se fera quasiment de la même manière.

Création d’objets pour le dispositif de simulation

L’objet du dispositif de simulation peut être construit de deux façons :

  • Un constructeur qui accepte l’objet matériel ordinaire.

  • Un constructeur ou une méthode par défaut qui accepte le numéro de port/index/canal à qui l’appareil est connecté. Ce serait le même numéro qui a été utilisé pour construire l’objet matériel régulier. Ceci est particulièrement utile pour 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
wpi::Encoder encoder{2, 3};
// create a sim controller for the encoder
wpi::sim::EncoderSim simEncoder{encoder};

Lecture et écriture des données de simulation

Chaque classe de simulation a des fonctions getter (getXxx()/GetXxx()) et setter (setXxx(value)/SetXxx(value)) pour chaque champ Xxx. Les fonctions getter retourneront la même chose que le getter de la classe régulière pour le robot réel.

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

Enregistrement des Callbacks

En plus des getters et des setters, chaque champ dispose également d’une fonction registerXxxCallback() qui enregistre un callback ou rappel à exécuter à chaque fois que la valeur du champ change et renvoie un objet CallbackStore. Les callbacks acceptent un paramètre de type chaîne de caractère correspondant au nom du champ et un objet HALValue contenant la nouvelle valeur. Avant de récupérer les valeurs d’une HALValue, vérifiez le type de valeur contenue. Les types possibles sont HALValue.kBoolean/HAL_BOOL, HALValue.kDouble/HAL_DOUBLE, HALValue.kEnum/HAL_ENUM, HALValue.kInt/HAL_INT, HALValue.kLong/HAL_LONG.

En Java, appelez close() sur l’objet CallbackStore pour annuler le callback. Gardez une référence à l’objet afin qu’il ne soit pas récupéré par le garbage collector - sinon le callback sera annulé par GC. Pour fournir des données arbitraires au callback, capturez-les dans le lambda ou utilisez une référence de méthode.

En C++, enregistrez l’objet CallbackStore dans la bonne portée - le callback sera annulé lorsque l’objet sort de sa portée et est détruit. Les données arbitraires peuvent être transmises aux callbacks via le paramètre param.

Avertissement

Tenter de récupérer une valeur d’un type à partir d’une HALValue contenant un type différent est un comportement non défini.

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';
  }
};
wpi::sim::CallbackStore store = simEncoder.RegisterCountCallback(callback);
// the callback will be canceled when ``store`` goes out of scope

Simulation d’autres dispositifs - La classe SimDevicesim

Note

Les fournisseurs peuvent implémenter leur connexion à l’API SimDevice d’une manière légèrement différente de celle décrite ici. Ils peuvent également fournir une classe de simulation spécifique pour leurs classes de composants. Consultez la documentation de votre fournisseur pour plus d’informations sur ce qu’il prend en charge et la manière de le faire.

La classe SimDeviceSim (pas SimDevice!) est un objet de simulation de périphérique général pour les périphériques qui ne sont pas des périphériques WPILib de base et qui n’ont donc pas de classes de simulation spécifiques, telles que les périphériques des fournisseurs. Ces composants apparaîtront dans l’onglet Other Devices du document SimGUI.

L’objet SimDeviceSim est créé à l’aide d’une clé de chaîne identique à la clé utilisée par le fournisseur pour construire le SimDevice sous-jacent dans sa classe du composant. Cette clé est celle avec qui le composant apparaît dans l’onglet Other Devices, et est généralement de la forme Prefix:Device Name[index]. Si la clé contient des ports/index/numéros de canal, ils peuvent être transmis sous forme d’arguments distincts au constructeur SimDeviceSim. La clé contient un préfixe qui est caché par défaut dans le SimGUI, il peut être affiché en sélectionnant l’option Show prefix. Ne pas inclure ce préfixe dans la clé transmise à SimDeviceSim ne se reférera pas au bon composant!

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

Une fois que nous avons le SimDeviceSim, nous pouvons obtenir des objets SimValue représentant les champs du composant. Il existe également des sous-classes de type spécifique SimDouble, SimInt, SimLong, SimBoolean, et SimEnum qui devraient être utilisées au lieu de la classe non fiable SimValue. Celles-ci sont construites à partir de SimDeviceSim à l’aide d’une clé identique à celle utilisée par le fournisseur pour définir le champ. Cette clé est celle inscrite dans le champ apparaissant telle que dans le SimGUI. Tenter de récupérer un objet SimValue en dehors du simulateur ou lorsque le composant ou les clés de champ n’ont pas d’objet retournera null - cela peut provoquer NullPointerException en Java ou un comportement non défini dans C++.

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