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.epilogue.logging; 006 007import edu.wpi.first.networktables.BooleanArrayPublisher; 008import edu.wpi.first.networktables.BooleanPublisher; 009import edu.wpi.first.networktables.DoubleArrayPublisher; 010import edu.wpi.first.networktables.DoublePublisher; 011import edu.wpi.first.networktables.FloatArrayPublisher; 012import edu.wpi.first.networktables.FloatPublisher; 013import edu.wpi.first.networktables.IntegerArrayPublisher; 014import edu.wpi.first.networktables.IntegerPublisher; 015import edu.wpi.first.networktables.NetworkTableInstance; 016import edu.wpi.first.networktables.Publisher; 017import edu.wpi.first.networktables.RawPublisher; 018import edu.wpi.first.networktables.StringArrayPublisher; 019import edu.wpi.first.networktables.StringPublisher; 020import edu.wpi.first.networktables.StructArrayPublisher; 021import edu.wpi.first.networktables.StructPublisher; 022import edu.wpi.first.util.struct.Struct; 023import java.util.HashMap; 024import java.util.Map; 025 026/** 027 * A backend implementation that sends data over network tables. Be careful when using this, since 028 * sending too much data may cause bandwidth or CPU starvation. 029 */ 030public class NTEpilogueBackend implements EpilogueBackend { 031 private final NetworkTableInstance m_nt; 032 033 private final Map<String, Publisher> m_publishers = new HashMap<>(); 034 private final Map<String, NestedBackend> m_nestedBackends = new HashMap<>(); 035 036 /** 037 * Creates a logging backend that sends information to NetworkTables. 038 * 039 * @param nt the NetworkTable instance to use to send data to 040 */ 041 public NTEpilogueBackend(NetworkTableInstance nt) { 042 this.m_nt = nt; 043 } 044 045 @Override 046 public EpilogueBackend getNested(String path) { 047 return m_nestedBackends.computeIfAbsent(path, k -> new NestedBackend(k, this)); 048 } 049 050 @Override 051 public void log(String identifier, int value) { 052 ((IntegerPublisher) 053 m_publishers.computeIfAbsent(identifier, k -> m_nt.getIntegerTopic(k).publish())) 054 .set(value); 055 } 056 057 @Override 058 public void log(String identifier, long value) { 059 ((IntegerPublisher) 060 m_publishers.computeIfAbsent(identifier, k -> m_nt.getIntegerTopic(k).publish())) 061 .set(value); 062 } 063 064 @Override 065 public void log(String identifier, float value) { 066 ((FloatPublisher) 067 m_publishers.computeIfAbsent(identifier, k -> m_nt.getFloatTopic(k).publish())) 068 .set(value); 069 } 070 071 @Override 072 public void log(String identifier, double value) { 073 ((DoublePublisher) 074 m_publishers.computeIfAbsent(identifier, k -> m_nt.getDoubleTopic(k).publish())) 075 .set(value); 076 } 077 078 @Override 079 public void log(String identifier, boolean value) { 080 ((BooleanPublisher) 081 m_publishers.computeIfAbsent(identifier, k -> m_nt.getBooleanTopic(k).publish())) 082 .set(value); 083 } 084 085 @Override 086 public void log(String identifier, byte[] value) { 087 ((RawPublisher) 088 m_publishers.computeIfAbsent(identifier, k -> m_nt.getRawTopic(k).publish("raw"))) 089 .set(value); 090 } 091 092 @Override 093 @SuppressWarnings("PMD.UnnecessaryCastRule") 094 public void log(String identifier, int[] value) { 095 // NT backend only supports int64[], so we have to manually widen to 64 bits before sending 096 long[] widened = new long[value.length]; 097 098 for (int i = 0; i < value.length; i++) { 099 widened[i] = (long) value[i]; 100 } 101 102 ((IntegerArrayPublisher) 103 m_publishers.computeIfAbsent(identifier, k -> m_nt.getIntegerArrayTopic(k).publish())) 104 .set(widened); 105 } 106 107 @Override 108 public void log(String identifier, long[] value) { 109 ((IntegerArrayPublisher) 110 m_publishers.computeIfAbsent(identifier, k -> m_nt.getIntegerArrayTopic(k).publish())) 111 .set(value); 112 } 113 114 @Override 115 public void log(String identifier, float[] value) { 116 ((FloatArrayPublisher) 117 m_publishers.computeIfAbsent(identifier, k -> m_nt.getFloatArrayTopic(k).publish())) 118 .set(value); 119 } 120 121 @Override 122 public void log(String identifier, double[] value) { 123 ((DoubleArrayPublisher) 124 m_publishers.computeIfAbsent(identifier, k -> m_nt.getDoubleArrayTopic(k).publish())) 125 .set(value); 126 } 127 128 @Override 129 public void log(String identifier, boolean[] value) { 130 ((BooleanArrayPublisher) 131 m_publishers.computeIfAbsent(identifier, k -> m_nt.getBooleanArrayTopic(k).publish())) 132 .set(value); 133 } 134 135 @Override 136 public void log(String identifier, String value) { 137 ((StringPublisher) 138 m_publishers.computeIfAbsent(identifier, k -> m_nt.getStringTopic(k).publish())) 139 .set(value); 140 } 141 142 @Override 143 public void log(String identifier, String[] value) { 144 ((StringArrayPublisher) 145 m_publishers.computeIfAbsent(identifier, k -> m_nt.getStringArrayTopic(k).publish())) 146 .set(value); 147 } 148 149 @Override 150 @SuppressWarnings("unchecked") 151 public <S> void log(String identifier, S value, Struct<S> struct) { 152 m_nt.addSchema(struct); 153 ((StructPublisher<S>) 154 m_publishers.computeIfAbsent(identifier, k -> m_nt.getStructTopic(k, struct).publish())) 155 .set(value); 156 } 157 158 @Override 159 @SuppressWarnings("unchecked") 160 public <S> void log(String identifier, S[] value, Struct<S> struct) { 161 m_nt.addSchema(struct); 162 ((StructArrayPublisher<S>) 163 m_publishers.computeIfAbsent( 164 identifier, k -> m_nt.getStructArrayTopic(k, struct).publish())) 165 .set(value); 166 } 167}