001/*
002 * Copyright (C) Photon Vision.
003 *
004 * This program is free software: you can redistribute it and/or modify
005 * it under the terms of the GNU General Public License as published by
006 * the Free Software Foundation, either version 3 of the License, or
007 * (at your option) any later version.
008 *
009 * This program is distributed in the hope that it will be useful,
010 * but WITHOUT ANY WARRANTY; without even the implied warranty of
011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
012 * GNU General Public License for more details.
013 *
014 * You should have received a copy of the GNU General Public License
015 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
016 */
017
018package org.photonvision.common.networktables;
019
020import com.fasterxml.jackson.core.JsonProcessingException;
021import com.fasterxml.jackson.databind.ObjectMapper;
022import edu.wpi.first.networktables.RawPublisher;
023import java.util.HashSet;
024import java.util.Set;
025import org.photonvision.common.dataflow.structures.Packet;
026import org.photonvision.common.dataflow.structures.PacketSerde;
027
028@SuppressWarnings("doclint")
029public class PacketPublisher<T> implements AutoCloseable {
030    public final RawPublisher publisher;
031    private final PacketSerde<T> photonStruct;
032
033    public PacketPublisher(RawPublisher publisher, PacketSerde<T> photonStruct) {
034        this.publisher = publisher;
035        this.photonStruct = photonStruct;
036
037        var mapper = new ObjectMapper();
038        try {
039            this.publisher
040                    .getTopic()
041                    .setProperty("message_uuid", mapper.writeValueAsString(photonStruct.getInterfaceUUID()));
042        } catch (JsonProcessingException e) {
043            // TODO Auto-generated catch block
044            e.printStackTrace();
045            throw new RuntimeException(e);
046        }
047        addSchemaImpl(photonStruct, new HashSet<>());
048    }
049
050    public void set(T value, int byteSize) {
051        var packet = new Packet(byteSize);
052        photonStruct.pack(packet, value);
053        publisher.set(packet.getWrittenDataCopy());
054    }
055
056    public void set(T value) {
057        set(value, photonStruct.getMaxByteSize());
058    }
059
060    @Override
061    public void close() {
062        publisher.close();
063    }
064
065    /**
066     * Publish the schema for our type (and all nested types under it) to NT.
067     *
068     * <p>Copyright (c) FIRST and other WPILib contributors. Open Source Software; you can modify
069     * and/or share it under the terms of the WPILib BSD license file in the root directory of this
070     * project.
071     *
072     * @param struct The struct to publish
073     * @param seen The set of types we've already published
074     */
075    private void addSchemaImpl(PacketSerde<?> struct, Set<String> seen) {
076        var instance = this.publisher.getTopic().getInstance();
077
078        String typeString = struct.getTypeString();
079
080        if (instance.hasSchema(typeString)) {
081            return;
082        }
083
084        if (!seen.add(typeString)) {
085            throw new UnsupportedOperationException(typeString + ": circular reference with " + seen);
086        }
087
088        instance.addSchema(typeString, "photonstructschema", struct.getSchema());
089
090        for (var inner : struct.getNestedPhotonMessages()) {
091            addSchemaImpl(inner, seen);
092        }
093        for (var inner : struct.getNestedWpilibMessages()) {
094            instance.addSchema(inner);
095        }
096        seen.remove(typeString);
097    }
098}