47public final class NetworkTableInstance
implements AutoCloseable {
49 public enum NetworkMode {
62 private final int value;
64 NetworkMode(
int value) {
86 private NetworkTableInstance(
int handle) {
93 public synchronized void close() {
94 if (m_owned && m_handle != 0) {
96 m_schemas.forEach((k, v) -> v.close());
108 return m_handle != 0;
119 public static synchronized NetworkTableInstance
getDefault() {
120 if (s_defaultInstance ==
null) {
121 s_defaultInstance =
new NetworkTableInstance(
NetworkTablesJNI.getDefaultInstance());
123 return s_defaultInstance;
132 public static NetworkTableInstance
create() {
133 NetworkTableInstance inst =
new NetworkTableInstance(
NetworkTablesJNI.createInstance());
145 return new NetworkTableInstance(handle);
164 Topic topic = m_topics.get(name);
167 topic =
new Topic(
this, handle);
168 Topic oldTopic = m_topics.putIfAbsent(name, topic);
169 if (oldTopic !=
null) {
173 m_topicsByHandle.putIfAbsent(handle, topic);
185 Topic topic = m_topics.get(name);
198 m_topics.put(name, wrapTopic);
201 m_topicsByHandle.put(handle, wrapTopic);
213 Topic topic = m_topics.get(name);
226 m_topics.put(name, wrapTopic);
229 m_topicsByHandle.put(handle, wrapTopic);
241 Topic topic = m_topics.get(name);
254 m_topics.put(name, wrapTopic);
257 m_topicsByHandle.put(handle, wrapTopic);
269 Topic topic = m_topics.get(name);
282 m_topics.put(name, wrapTopic);
285 m_topicsByHandle.put(handle, wrapTopic);
297 Topic topic = m_topics.get(name);
310 m_topics.put(name, wrapTopic);
313 m_topicsByHandle.put(handle, wrapTopic);
325 Topic topic = m_topics.get(name);
338 m_topics.put(name, wrapTopic);
341 m_topicsByHandle.put(handle, wrapTopic);
353 Topic topic = m_topics.get(name);
366 m_topics.put(name, wrapTopic);
369 m_topicsByHandle.put(handle, wrapTopic);
381 Topic topic = m_topics.get(name);
394 m_topics.put(name, wrapTopic);
397 m_topicsByHandle.put(handle, wrapTopic);
409 Topic topic = m_topics.get(name);
422 m_topics.put(name, wrapTopic);
425 m_topicsByHandle.put(handle, wrapTopic);
437 Topic topic = m_topics.get(name);
450 m_topics.put(name, wrapTopic);
453 m_topicsByHandle.put(handle, wrapTopic);
465 Topic topic = m_topics.get(name);
478 m_topics.put(name, wrapTopic);
481 m_topicsByHandle.put(handle, wrapTopic);
496 public <T, MessageType extends ProtoMessage<?>>
498 Topic topic = m_topics.get(name);
500 @SuppressWarnings(
"unchecked")
509 handle = topic.getHandle();
512 ProtobufTopic<T> wrapTopic = ProtobufTopic.wrap(
this, handle, proto);
513 m_topics.put(name, wrapTopic);
516 m_topicsByHandle.put(handle, wrapTopic);
530 StructTopic<T> getStructTopic(String name, Struct<T> struct) {
531 Topic topic = m_topics.get(name);
532 if (topic instanceof StructTopic<?> t && t.getStruct().equals(struct)) {
533 @SuppressWarnings(
"unchecked")
534 StructTopic<T> wrapTopic = (StructTopic<T>) topic;
540 handle = NetworkTablesJNI.getTopic(m_handle, name);
542 handle = topic.getHandle();
545 StructTopic<T> wrapTopic = StructTopic.wrap(
this, handle, struct);
546 m_topics.put(name, wrapTopic);
549 m_topicsByHandle.put(handle, wrapTopic);
563 StructArrayTopic<T> getStructArrayTopic(String name, Struct<T> struct) {
564 Topic topic = m_topics.get(name);
565 if (topic instanceof StructArrayTopic<?> t && t.getStruct().equals(struct)) {
566 @SuppressWarnings(
"unchecked")
567 StructArrayTopic<T> wrapTopic = (StructArrayTopic<T>) topic;
573 handle = NetworkTablesJNI.getTopic(m_handle, name);
575 handle = topic.getHandle();
578 StructArrayTopic<T> wrapTopic = StructArrayTopic.wrap(
this, handle, struct);
579 m_topics.put(name, wrapTopic);
582 m_topicsByHandle.put(handle, wrapTopic);
587 private Topic[] topicHandlesToTopics(
int[] handles) {
588 Topic[] topics =
new Topic[handles.length];
589 for (
int i = 0; i < handles.length; i++) {
590 topics[i] = getCachedTopic(handles[i]);
613 return topicHandlesToTopics(
NetworkTablesJNI.getTopics(m_handle, prefix, 0));
626 return topicHandlesToTopics(
NetworkTablesJNI.getTopics(m_handle, prefix, types));
639 return topicHandlesToTopics(
NetworkTablesJNI.getTopicsStr(m_handle, prefix, types));
690 private final ConcurrentMap<String, NetworkTableEntry> m_entries =
new ConcurrentHashMap<>();
703 if (oldEntry !=
null) {
711 private final ConcurrentMap<String, Topic> m_topics =
new ConcurrentHashMap<>();
712 private final ConcurrentMap<Integer, Topic> m_topicsByHandle =
new ConcurrentHashMap<>();
714 Topic getCachedTopic(String name) {
715 Topic topic = m_topics.get(name);
718 topic =
new Topic(
this, handle);
719 Topic oldTopic = m_topics.putIfAbsent(name, topic);
720 if (oldTopic !=
null) {
724 m_topicsByHandle.putIfAbsent(handle, topic);
729 Topic getCachedTopic(
int handle) {
730 Topic topic = m_topicsByHandle.get(handle);
732 topic =
new Topic(
this, handle);
733 Topic oldTopic = m_topicsByHandle.putIfAbsent(handle, topic);
734 if (oldTopic !=
null) {
742 private final ConcurrentMap<String, NetworkTable> m_tables =
new ConcurrentHashMap<>();
753 if (key.isEmpty() ||
"/".equals(key)) {
765 NetworkTable oldTable = m_tables.putIfAbsent(theKey, table);
766 if (oldTable !=
null) {
777 private static class ListenerStorage
implements AutoCloseable {
778 private final ReentrantLock m_lock =
new ReentrantLock();
779 private final Map<Integer, Consumer<NetworkTableEvent>> m_listeners =
new HashMap<>();
781 @SuppressWarnings(
"PMD.SingularField")
782 private Thread m_thread;
784 private
int m_poller;
785 private
boolean m_waitQueue;
786 private final
Event m_waitQueueEvent = new
Event();
787 private final Condition m_waitQueueCond = m_lock.newCondition();
796 EnumSet<NetworkTableEvent.Kind> eventKinds,
797 Consumer<NetworkTableEvent> listener) {
801 m_poller = NetworkTablesJNI.createListenerPoller(m_inst.
getHandle());
804 int h = NetworkTablesJNI.addListener(m_poller, prefixes, eventKinds);
805 m_listeners.put(h, listener);
814 EnumSet<NetworkTableEvent.Kind> eventKinds,
815 Consumer<NetworkTableEvent> listener) {
819 m_poller = NetworkTablesJNI.createListenerPoller(m_inst.getHandle());
822 int h = NetworkTablesJNI.addListener(m_poller, handle, eventKinds);
823 m_listeners.put(h, listener);
830 int addLogger(
int minLevel,
int maxLevel, Consumer<NetworkTableEvent> listener) {
834 m_poller = NetworkTablesJNI.createListenerPoller(m_inst.getHandle());
837 int h = NetworkTablesJNI.addLogger(m_poller, minLevel, maxLevel);
838 m_listeners.put(h, listener);
845 void remove(
int listener) {
848 m_listeners.remove(listener);
852 NetworkTablesJNI.removeListener(listener);
856 public void close() {
858 NetworkTablesJNI.destroyListenerPoller(m_poller);
863 @SuppressWarnings(
"PMD.AvoidCatchingGenericException")
864 private
void startThread() {
868 boolean wasInterrupted =
false;
869 int[] handles =
new int[] { m_poller, m_waitQueueEvent.getHandle() };
870 while (!Thread.interrupted()) {
872 WPIUtilJNI.waitForObjects(handles);
873 }
catch (InterruptedException ex) {
878 m_waitQueueCond.signalAll();
883 Thread.currentThread().interrupt();
885 wasInterrupted =
true;
888 for (NetworkTableEvent event :
889 NetworkTablesJNI.readListenerQueue(m_inst, m_poller)) {
890 Consumer<NetworkTableEvent> listener;
893 listener = m_listeners.get(event.listener);
897 if (listener !=
null) {
899 listener.accept(event);
900 }
catch (Throwable throwable) {
902 "Unhandled exception during listener callback: "
903 + throwable.toString());
904 throwable.printStackTrace();
912 m_waitQueueCond.signalAll();
920 if (!wasInterrupted) {
921 NetworkTablesJNI.destroyListenerPoller(m_poller);
929 m_thread.setDaemon(
true);
933 boolean waitForQueue(
double timeout) {
938 m_waitQueueEvent.set();
939 while (m_waitQueue) {
942 m_waitQueueCond.await();
944 return m_waitQueueCond.await((
long) (timeout * 1e9), TimeUnit.NANOSECONDS);
946 }
catch (InterruptedException ex) {
947 Thread.currentThread().interrupt();
959 private final ListenerStorage m_listeners =
new ListenerStorage(
this);
967 m_listeners.remove(listener);
980 return m_listeners.waitForQueue(timeout);
993 boolean immediateNotify, Consumer<NetworkTableEvent> listener) {
995 if (immediateNotify) {
998 return m_listeners.add(m_handle, eventKinds, listener);
1011 boolean immediateNotify, Consumer<NetworkTableEvent> listener) {
1013 if (immediateNotify) {
1016 return m_listeners.add(m_handle, eventKinds, listener);
1034 EnumSet<NetworkTableEvent.Kind> eventKinds,
1035 Consumer<NetworkTableEvent> listener) {
1037 throw new IllegalArgumentException(
"topic is not from this instance");
1039 return m_listeners.add(topic.
getHandle(), eventKinds, listener);
1055 EnumSet<NetworkTableEvent.Kind> eventKinds,
1056 Consumer<NetworkTableEvent> listener) {
1058 throw new IllegalArgumentException(
"subscriber is not from this instance");
1060 return m_listeners.add(subscriber.
getHandle(), eventKinds, listener);
1076 EnumSet<NetworkTableEvent.Kind> eventKinds,
1077 Consumer<NetworkTableEvent> listener) {
1079 throw new IllegalArgumentException(
"subscriber is not from this instance");
1081 return m_listeners.add(subscriber.
getHandle(), eventKinds, listener);
1096 EnumSet<NetworkTableEvent.Kind> eventKinds,
1097 Consumer<NetworkTableEvent> listener) {
1099 throw new IllegalArgumentException(
"entry is not from this instance");
1101 return m_listeners.add(entry.
getHandle(), eventKinds, listener);
1120 EnumSet<NetworkTableEvent.Kind> eventKinds,
1121 Consumer<NetworkTableEvent> listener) {
1122 return m_listeners.add(prefixes, eventKinds, listener);
1136 EnumSet<NetworkMode> rv = EnumSet.noneOf(
NetworkMode.class);
1138 if ((flags & mode.getValue()) != 0) {
1185 public void startServer(String persistFilename, String listenAddress) {
1197 public void startServer(String persistFilename, String listenAddress, String mdnsService) {
1209 public void startServer(String persistFilename, String listenAddress, String mdnsService,
1211 NetworkTablesJNI.startServer(m_handle, persistFilename, listenAddress, mdnsService, port);
1272 int[] ports =
new int[serverNames.length];
1273 Arrays.fill(ports, port);
1348 int[] ports =
new int[serverNames.length];
1349 Arrays.fill(ports, port);
1363 public void setServerMdns(String serviceName, String[] serverNames,
int[] ports) {
1378 String serviceName,
int mdnsPort, String[] serverNames,
int[] ports) {
1380 m_handle, serviceName, mdnsPort, serverNames, ports);
1564 return NetworkTablesJNI.startEntryDataLog(m_handle, log, prefix, logPrefix);
1608 public int addLogger(
int minLevel,
int maxLevel, Consumer<NetworkTableEvent> func) {
1609 return m_listeners.addLogger(minLevel, maxLevel, func);
1621 return m_schemas.containsKey(
"/.schema/" + name);
1635 public void addSchema(String name, String type,
byte[] schema) {
1636 m_schemas.computeIfAbsent(
"/.schema/" + name, k -> {
1654 public void addSchema(String name, String type, String schema) {
1655 m_schemas.computeIfAbsent(
"/.schema/" + name, k -> {
1657 pub.
setDefault(StandardCharsets.UTF_8.encode(schema));
1671 (typeString, schema) ->
addSchema(typeString,
"proto:FileDescriptorProto", schema));
1681 addSchemaImpl(struct,
new HashSet<>());
1685 public boolean equals(Object other) {
1686 return other ==
this || other instanceof
NetworkTableInstance inst && m_handle == inst.m_handle;
1690 public int hashCode() {
1694 private void addSchemaImpl(Struct<?> struct, Set<String> seen) {
1695 String typeString = struct.getTypeString();
1699 if (!seen.add(typeString)) {
1700 throw new UnsupportedOperationException(typeString +
": circular reference with " + seen);
1702 addSchema(typeString,
"structschema", struct.getSchema());
1703 for (Struct<?> inner : struct.getNested()) {
1704 addSchemaImpl(inner, seen);
1706 seen.remove(typeString);
1709 private boolean m_owned;
1710 private int m_handle;
1711 private final ConcurrentMap<String, RawPublisher> m_schemas =
new ConcurrentHashMap<>();