Features are the main draw of Dairy, and they power a lot of the additional utilities provided by Core and other libraries.

Features add a lot of additional hooks to the OpMode lifecycle, and are capable of doing a lot of powerful work and tasks in the background of user code.

A hook is an update callback. Basically, when an event occurs in code, if that code is written to expose hooks, it will all all the hooks which are attached to that event.

Dairy adds hooks to the OpMode life cycle, which means that when your OpMode code runs (init, init_loop, start, loop, stop), Dairy makes it so that other Objects in code receive updates about these events occurring, without any added code for you.

If you are using OpMode, you don’t need to add any code, if you are using LinearOpMode, then additional lines of code to enable the hooks MUST be added.

The ‘pre’ hooks run in order of Features being activated, while ‘post’ hooks run in the reverse order. This ensures that less important Features are always enclosed by the code of the more important ones.

This is important to keep in mind when writing a Feature.

Features generally fall into two categories:

  1. Dynamic: Features that are instantiated during an OpMode, generally during init. These Features generally register themselves with the FeatureRegistrar when they are instantiated, or it must be done manually. Oftentimes they are meant to only exist for one OpMode, and so deregister themselves when it ends.
  2. Static: Features that are global singletons and are registered via Sinister, rather than by themselves. They exist all the time, and use the Dependency system, usually with annotations to attach to OpModes on demand.

Dairy offers utilities that come in both forms.

In your own code, you might use the static singleton approach for subsystems, if you’re doing them yourself, rather than with Mercurial, or a big global utility system.

In comparison, the dynamic approach is good for when the utility is needed on demand, and just needs to receive updates about the OpMode.

We’ll take a look at both the static and the dynamic approach.

Static:

  • Utility to automatically manage manual BulkReads.
  • Enabled for an OpMode by adding @BulkReads.Attach.
    This utility is available via the Curdled library.
    These classes are from the featuredev.[jdoc|kdoc] package of the Dairy examples.

Dynamic:

  • Utility to automatically update a PID in the background.
  • Enabled for an OpMode by instantiating it.
    Core has far more advanced support for PID controllers, go take a look.
    These classes are from the featuredev.[jdoc|kdoc] package of the Dairy examples.

I’m not going to actually write the proper code this time, as would add a decent number of lines of code to the examples, and it would just add noise to what this is being demonstrated. If you don’t like Dairy Controllers, then you could finish off this class and use it. This could easily be done with a pre-done PID class from another library, or you could fill in the blanks here by writing it yourself, it isn’t hard.

Now, lets look at how to use these in an OpMode:

These classes are from the featuredev.[jdoc|kdoc] package of the Dairy examples.