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 edu.wpi.first.util.protobuf.Protobuf;
008import edu.wpi.first.util.protobuf.ProtobufBuffer;
009
010/**
011 * NetworkTables protobuf-encoded value topic.
012 *
013 * @param <T> value class
014 */
015public final class ProtobufTopic<T> extends Topic {
016  private ProtobufTopic(Topic topic, Protobuf<T, ?> proto) {
017    super(topic.m_inst, topic.m_handle);
018    m_proto = proto;
019  }
020
021  private ProtobufTopic(NetworkTableInstance inst, int handle, Protobuf<T, ?> proto) {
022    super(inst, handle);
023    m_proto = proto;
024  }
025
026  /**
027   * Create a ProtobufTopic from a generic topic.
028   *
029   * @param <T> value class (inferred from proto)
030   * @param topic generic topic
031   * @param proto protobuf serialization implementation
032   * @return ProtobufTopic for value class
033   */
034  public static <T> ProtobufTopic<T> wrap(Topic topic, Protobuf<T, ?> proto) {
035    return new ProtobufTopic<>(topic, proto);
036  }
037
038  /**
039   * Create a ProtobufTopic from a native handle; generally NetworkTableInstance.getProtobufTopic()
040   * should be used instead.
041   *
042   * @param <T> value class (inferred from proto)
043   * @param inst Instance
044   * @param handle Native handle
045   * @param proto protobuf serialization implementation
046   * @return ProtobufTopic for value class
047   */
048  public static <T> ProtobufTopic<T> wrap(
049      NetworkTableInstance inst, int handle, Protobuf<T, ?> proto) {
050    return new ProtobufTopic<>(inst, handle, proto);
051  }
052
053  /**
054   * Create a new subscriber to the topic.
055   *
056   * <p>The subscriber is only active as long as the returned object is not closed.
057   *
058   * <p>Subscribers that do not match the published data type do not return any values. To determine
059   * if the data type matches, use the appropriate Topic functions.
060   *
061   * @param defaultValue default value used when a default is not provided to a getter function
062   * @param options subscribe options
063   * @return subscriber
064   */
065  public ProtobufSubscriber<T> subscribe(T defaultValue, PubSubOption... options) {
066    return new ProtobufEntryImpl<>(
067        this,
068        ProtobufBuffer.create(m_proto),
069        NetworkTablesJNI.subscribe(
070            m_handle, NetworkTableType.kRaw.getValue(), m_proto.getTypeString(), options),
071        defaultValue,
072        false);
073  }
074
075  /**
076   * Create a new publisher to the topic.
077   *
078   * <p>The publisher is only active as long as the returned object is not closed.
079   *
080   * <p>It is not possible to publish two different data types to the same topic. Conflicts between
081   * publishers are typically resolved by the server on a first-come, first-served basis. Any
082   * published values that do not match the topic's data type are dropped (ignored). To determine if
083   * the data type matches, use the appropriate Topic functions.
084   *
085   * @param options publish options
086   * @return publisher
087   */
088  public ProtobufPublisher<T> publish(PubSubOption... options) {
089    m_inst.addSchema(m_proto);
090    return new ProtobufEntryImpl<>(
091        this,
092        ProtobufBuffer.create(m_proto),
093        NetworkTablesJNI.publish(
094            m_handle, NetworkTableType.kRaw.getValue(), m_proto.getTypeString(), options),
095        null,
096        true);
097  }
098
099  /**
100   * Create a new publisher to the topic, with type string and initial properties.
101   *
102   * <p>The publisher is only active as long as the returned object is not closed.
103   *
104   * <p>It is not possible to publish two different data types to the same topic. Conflicts between
105   * publishers are typically resolved by the server on a first-come, first-served basis. Any
106   * published values that do not match the topic's data type are dropped (ignored). To determine if
107   * the data type matches, use the appropriate Topic functions.
108   *
109   * @param properties JSON properties
110   * @param options publish options
111   * @return publisher
112   * @throws IllegalArgumentException if properties is not a JSON object
113   */
114  public ProtobufPublisher<T> publishEx(String properties, PubSubOption... options) {
115    m_inst.addSchema(m_proto);
116    return new ProtobufEntryImpl<>(
117        this,
118        ProtobufBuffer.create(m_proto),
119        NetworkTablesJNI.publishEx(
120            m_handle,
121            NetworkTableType.kRaw.getValue(),
122            m_proto.getTypeString(),
123            properties,
124            options),
125        null,
126        true);
127  }
128
129  /**
130   * Create a new entry for the topic.
131   *
132   * <p>Entries act as a combination of a subscriber and a weak publisher. The subscriber is active
133   * as long as the entry is not closed. The publisher is created when the entry is first written
134   * to, and remains active until either unpublish() is called or the entry is closed.
135   *
136   * <p>It is not possible to use two different data types with the same topic. Conflicts between
137   * publishers are typically resolved by the server on a first-come, first-served basis. Any
138   * published values that do not match the topic's data type are dropped (ignored), and the entry
139   * will show no new values if the data type does not match. To determine if the data type matches,
140   * use the appropriate Topic functions.
141   *
142   * @param defaultValue default value used when a default is not provided to a getter function
143   * @param options publish and/or subscribe options
144   * @return entry
145   */
146  public ProtobufEntry<T> getEntry(T defaultValue, PubSubOption... options) {
147    return new ProtobufEntryImpl<>(
148        this,
149        ProtobufBuffer.create(m_proto),
150        NetworkTablesJNI.getEntry(
151            m_handle, NetworkTableType.kRaw.getValue(), m_proto.getTypeString(), options),
152        defaultValue,
153        false);
154  }
155
156  /**
157   * Returns the protobuf.
158   *
159   * @return The protobuf.
160   */
161  public Protobuf<T, ?> getProto() {
162    return m_proto;
163  }
164
165  @Override
166  public boolean equals(Object other) {
167    if (other == this) {
168      return true;
169    }
170    if (!(other instanceof ProtobufTopic)) {
171      return false;
172    }
173
174    return super.equals(other) && m_proto == ((ProtobufTopic<?>) other).m_proto;
175  }
176
177  @Override
178  public int hashCode() {
179    return super.hashCode() ^ m_proto.hashCode();
180  }
181
182  private final Protobuf<T, ?> m_proto;
183}