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
028public class PacketPublisher<T> implements AutoCloseable {
029    public final RawPublisher publisher;
030    private final PacketSerde<T> photonStruct;
031
032    public PacketPublisher(RawPublisher publisher, PacketSerde<T> photonStruct) {
033        this.publisher = publisher;
034        this.photonStruct = photonStruct;
035
036        var mapper = new ObjectMapper();
037        try {
038            this.publisher
039                    .getTopic()
040                    .setProperty("message_uuid", mapper.writeValueAsString(photonStruct.getInterfaceUUID()));
041        } catch (JsonProcessingException e) {
042            // TODO Auto-generated catch block
043            e.printStackTrace();
044            throw new RuntimeException(e);
045        }
046        addSchemaImpl(photonStruct, new HashSet<>());
047    }
048
049    public void set(T value, int byteSize) {
050        var packet = new Packet(byteSize);
051        photonStruct.pack(packet, value);
052        publisher.set(packet.getWrittenDataCopy());
053    }
054
055    public void set(T value) {
056        set(value, photonStruct.getMaxByteSize());
057    }
058
059    @Override
060    public void close() {
061        publisher.close();
062    }
063
064    /**
065     * Publish the schema for our type (and all nested types under it) to NT.
066     *
067     * <p>Copyright (c) FIRST and other WPILib contributors. Open Source Software; you can modify
068     * and/or share it under the terms of the WPILib BSD license file in the root directory of this
069     * project.
070     *
071     * @param struct The struct to publish
072     * @param seen The set of types we've already published
073     */
074    private void addSchemaImpl(PacketSerde<?> struct, Set<String> seen) {
075        var instance = this.publisher.getTopic().getInstance();
076
077        String typeString = struct.getTypeString();
078
079        if (instance.hasSchema(typeString)) {
080            return;
081        }
082
083        if (!seen.add(typeString)) {
084            throw new UnsupportedOperationException(typeString + ": circular reference with " + seen);
085        }
086
087        instance.addSchema(typeString, "photonstructschema", struct.getSchema());
088
089        for (var inner : struct.getNestedPhotonMessages()) {
090            addSchemaImpl(inner, seen);
091        }
092        for (var inner : struct.getNestedWpilibMessages()) {
093            instance.addSchema(inner);
094        }
095        seen.remove(typeString);
096    }
097}