Skip to content
NukeMinecart edited this page Dec 14, 2025 · 1 revision

BadgerLog supports two types of events. Normal events are ones that can only happen on BadgerLog entries, but can have any value. Raw events are directly from NetworkTables; there is no value conversion.

Contents

  • Normal Events
    • Watchers
    • Watched
  • Raw Events

Normal Events

Normal events are ones that can only listen to BadgerLog entries. Even though this is limiting the possiblities, it is required because normal events can have any type associated with them.

Types with normal events are the types on the robot code side, not on NetworkTables. For example, a struct type may have multiple NetworkTables entries, but only one BadgerLog entry. Normal events use the BadgerLog entry type.

Declaring an Event

To declare an event, a method must be annotated with @Watcher and an @Entry annotated field must be annotated with @Watched. When this is the case, any change to any part of the entry's value will result in a call to the @Watcher method.

Example @Watcher declaration

@Watcher(type = Pose2d.class, name = "Pose Event")
private void pose2dWatcher(EventData<Pose2d> data) {
    // event logic here
}

Notice 3 important features of this event method and annotation.

  1. The @Watcher specifies the same class type as in the type of the EventData. This type is enforced at compile time.
  2. There is a name of the event in the @Event. This is required to identify the event
  3. The method returns void. No other type may be returned.

Annotating a method with @Watcher creates an event, but no entry is registered with it.

Watching an Entry

Normal events require that both an event is defined and an already created entry is annotated with @Watched

The @Watched annotation takes a list of event names to notify when the value changes. Each name must match the name defined by @Watcher exactly.

Example Usage

@Entry(EntryType.SUBSCRIBER)
@Watched("Integer Event")
private final int integer = 1;

@Watcher(type = int.class, name = "Integer Event")
private void integerWatcher(EventData<Integer> data) {
    // event code here
}

The most important part that must match between the @Watched and the @Watched is the type. The type must match that of the entry, unless the value defined in the event is void.class, which it then matches any type.

The event will now trigger when any changes to the integer entry occur.

Raw Events

Raw events differ from normal events in that they listen for changes on keys specified in NetworkTables and are invoked when the value changes. They only can listen to types already present on NetworkTables. (primitives, arrays of them, and strings)

They only require a method to register them. They cannot have a field explicitly declare it as a listener.

Example @Raw Watcher Declaration

@RawWatcher(type = int.class, keys = "/BadgerLog", eventType = EventType.ALL)
private void integerRawWatcher(EventData<Integer> data){
    // event code here
}

It is similar to the normal @Watcher events because the type must match the type of EventData exactly.

It differs from normal events in 2 ways.

  1. It requires an array of keys to listen to
  2. It needs an EventType

The array of keys may be either partial keys or full keys. Partial keys only contain some table names. Full keys match exactly one entry.

Example: if there is the entry upper/lower/entry then /upper, /upper/lower and /upper/lower/entry all match it.

IMPORTANT: Each key MUST begin with the table separator (/), or the event will never fire.

The event type present in the @RawWatcher specifies when the event should be fired.

In the example above, the int type is used. Any valid NetworkTableType may be used instead.

Cautions

Events should not modify the state of the entry.

This causes a recursive loop in which it can never escape. Use with validation is appropriate as long as there is acceptable bounds. It should almost never directly set the field which is being watched.

The void type of event may be too broad

Consider avoiding using the scope of void as it leads to unpredicable situations with awkward type casting. Prefer to split into multiple, more specific events instead.