001// Copyright (c) FIRST and other WPILib contributors.
002// Open Source Software; you can modify and/or share it under the terms of
003// the WPILib BSD license file in the root directory of this project.
004
005package edu.wpi.first.networktables;
006
007import java.util.EnumSet;
008import java.util.function.Consumer;
009
010/**
011 * Event listener. This calls back to a callback function when an event matching the specified mask
012 * occurs. The callback function is called asynchronously on a separate thread, so it's important to
013 * use synchronization or atomics when accessing any shared state from the callback function.
014 */
015public final class NetworkTableListener implements AutoCloseable {
016  /**
017   * Create a listener for changes to topics with names that start with any of the given prefixes.
018   * This creates a corresponding internal subscriber with the lifetime of the listener.
019   *
020   * @param inst Instance
021   * @param prefixes Topic name string prefixes
022   * @param eventKinds set of event kinds to listen to
023   * @param listener Listener function
024   * @return Listener
025   */
026  public static NetworkTableListener createListener(
027      NetworkTableInstance inst,
028      String[] prefixes,
029      EnumSet<NetworkTableEvent.Kind> eventKinds,
030      Consumer<NetworkTableEvent> listener) {
031    return new NetworkTableListener(inst, inst.addListener(prefixes, eventKinds, listener));
032  }
033
034  /**
035   * Create a listener for changes on a particular topic. This creates a corresponding internal
036   * subscriber with the lifetime of the listener.
037   *
038   * @param topic Topic
039   * @param eventKinds set of event kinds to listen to
040   * @param listener Listener function
041   * @return Listener
042   */
043  public static NetworkTableListener createListener(
044      Topic topic,
045      EnumSet<NetworkTableEvent.Kind> eventKinds,
046      Consumer<NetworkTableEvent> listener) {
047    NetworkTableInstance inst = topic.getInstance();
048    return new NetworkTableListener(inst, inst.addListener(topic, eventKinds, listener));
049  }
050
051  /**
052   * Create a listener for topic changes on a subscriber. This does NOT keep the subscriber active.
053   *
054   * @param subscriber Subscriber
055   * @param eventKinds set of event kinds to listen to
056   * @param listener Listener function
057   * @return Listener
058   */
059  public static NetworkTableListener createListener(
060      Subscriber subscriber,
061      EnumSet<NetworkTableEvent.Kind> eventKinds,
062      Consumer<NetworkTableEvent> listener) {
063    NetworkTableInstance inst = subscriber.getTopic().getInstance();
064    return new NetworkTableListener(inst, inst.addListener(subscriber, eventKinds, listener));
065  }
066
067  /**
068   * Create a listener for topic changes on a subscriber. This does NOT keep the subscriber active.
069   *
070   * @param subscriber Subscriber
071   * @param eventKinds set of event kinds to listen to
072   * @param listener Listener function
073   * @return Listener
074   */
075  public static NetworkTableListener createListener(
076      MultiSubscriber subscriber,
077      EnumSet<NetworkTableEvent.Kind> eventKinds,
078      Consumer<NetworkTableEvent> listener) {
079    NetworkTableInstance inst = subscriber.getInstance();
080    return new NetworkTableListener(inst, inst.addListener(subscriber, eventKinds, listener));
081  }
082
083  /**
084   * Create a listener for topic changes on an entry.
085   *
086   * @param entry Entry
087   * @param eventKinds set of event kinds to listen to
088   * @param listener Listener function
089   * @return Listener
090   */
091  public static NetworkTableListener createListener(
092      NetworkTableEntry entry,
093      EnumSet<NetworkTableEvent.Kind> eventKinds,
094      Consumer<NetworkTableEvent> listener) {
095    NetworkTableInstance inst = entry.getInstance();
096    return new NetworkTableListener(inst, inst.addListener(entry, eventKinds, listener));
097  }
098
099  /**
100   * Create a connection listener.
101   *
102   * @param inst instance
103   * @param immediateNotify notify listener of all existing connections
104   * @param listener listener function
105   * @return Listener
106   */
107  public static NetworkTableListener createConnectionListener(
108      NetworkTableInstance inst, boolean immediateNotify, Consumer<NetworkTableEvent> listener) {
109    return new NetworkTableListener(inst, inst.addConnectionListener(immediateNotify, listener));
110  }
111
112  /**
113   * Create a time synchronization listener.
114   *
115   * @param inst instance
116   * @param immediateNotify notify listener of current time synchronization value
117   * @param listener listener function
118   * @return Listener
119   */
120  public static NetworkTableListener createTimeSyncListener(
121      NetworkTableInstance inst, boolean immediateNotify, Consumer<NetworkTableEvent> listener) {
122    return new NetworkTableListener(inst, inst.addTimeSyncListener(immediateNotify, listener));
123  }
124
125  /**
126   * Create a listener for log messages. By default, log messages are sent to stderr; this function
127   * sends log messages with the specified levels to the provided callback function instead. The
128   * callback function will only be called for log messages with level greater than or equal to
129   * minLevel and less than or equal to maxLevel; messages outside this range will be silently
130   * ignored.
131   *
132   * @param inst instance
133   * @param minLevel minimum log level
134   * @param maxLevel maximum log level
135   * @param listener listener function
136   * @return Listener
137   */
138  public static NetworkTableListener createLogger(
139      NetworkTableInstance inst, int minLevel, int maxLevel, Consumer<NetworkTableEvent> listener) {
140    return new NetworkTableListener(inst, inst.addLogger(minLevel, maxLevel, listener));
141  }
142
143  @Override
144  public synchronized void close() {
145    if (m_handle != 0) {
146      m_inst.removeListener(m_handle);
147      m_handle = 0;
148    }
149  }
150
151  /**
152   * Determines if the native handle is valid.
153   *
154   * @return True if the native handle is valid, false otherwise.
155   */
156  public boolean isValid() {
157    return m_handle != 0;
158  }
159
160  /**
161   * Gets the native handle.
162   *
163   * @return Native handle
164   */
165  public int getHandle() {
166    return m_handle;
167  }
168
169  /**
170   * Wait for the topic listener queue to be empty. This is primarily useful for deterministic
171   * testing. This blocks until either the topic listener queue is empty (e.g. there are no more
172   * events that need to be passed along to callbacks or poll queues) or the timeout expires.
173   *
174   * @param timeout timeout, in seconds. Set to 0 for non-blocking behavior, or a negative value to
175   *     block indefinitely
176   * @return False if timed out, otherwise true.
177   */
178  public boolean waitForQueue(double timeout) {
179    return m_inst.waitForListenerQueue(timeout);
180  }
181
182  private NetworkTableListener(NetworkTableInstance inst, int handle) {
183    m_inst = inst;
184    m_handle = handle;
185  }
186
187  private final NetworkTableInstance m_inst;
188  private int m_handle;
189}