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}