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.util.struct.Struct; 008import java.util.Arrays; 009import java.util.HashMap; 010import java.util.Map; 011import java.util.Objects; 012 013/** 014 * A backend implementation that only logs data when it changes. Useful for keeping bandwidth and 015 * file sizes down. However, because it still needs to check that data has changed, it cannot avoid 016 * expensive sensor reads. 017 */ 018public class LazyBackend implements EpilogueBackend { 019 private final EpilogueBackend m_backend; 020 021 // Keep a record of the most recent value written to each entry 022 // Note that this may duplicate a lot of data, and will box primitives. 023 private final Map<String, Object> m_previousValues = new HashMap<>(); 024 private final Map<String, NestedBackend> m_subLoggers = new HashMap<>(); 025 026 /** 027 * Creates a new lazy backend wrapper around another backend. 028 * 029 * @param backend the backend to delegate to 030 */ 031 public LazyBackend(EpilogueBackend backend) { 032 this.m_backend = backend; 033 } 034 035 @Override 036 public EpilogueBackend lazy() { 037 // Already lazy, don't need to wrap it again 038 return this; 039 } 040 041 @Override 042 public EpilogueBackend getNested(String path) { 043 return m_subLoggers.computeIfAbsent(path, k -> new NestedBackend(k, this)); 044 } 045 046 @Override 047 public void log(String identifier, int value) { 048 var previous = m_previousValues.get(identifier); 049 050 if (previous instanceof Integer oldValue && oldValue == value) { 051 // no change 052 return; 053 } 054 055 m_previousValues.put(identifier, value); 056 m_backend.log(identifier, value); 057 } 058 059 @Override 060 public void log(String identifier, long value) { 061 var previous = m_previousValues.get(identifier); 062 063 if (previous instanceof Long oldValue && oldValue == value) { 064 // no change 065 return; 066 } 067 068 m_previousValues.put(identifier, value); 069 m_backend.log(identifier, value); 070 } 071 072 @Override 073 public void log(String identifier, float value) { 074 var previous = m_previousValues.get(identifier); 075 076 if (previous instanceof Float oldValue && oldValue == value) { 077 // no change 078 return; 079 } 080 081 m_previousValues.put(identifier, value); 082 m_backend.log(identifier, value); 083 } 084 085 @Override 086 public void log(String identifier, double value) { 087 var previous = m_previousValues.get(identifier); 088 089 if (previous instanceof Double oldValue && oldValue == value) { 090 // no change 091 return; 092 } 093 094 m_previousValues.put(identifier, value); 095 m_backend.log(identifier, value); 096 } 097 098 @Override 099 public void log(String identifier, boolean value) { 100 var previous = m_previousValues.get(identifier); 101 102 if (previous instanceof Boolean oldValue && oldValue == value) { 103 // no change 104 return; 105 } 106 107 m_previousValues.put(identifier, value); 108 m_backend.log(identifier, value); 109 } 110 111 @Override 112 public void log(String identifier, byte[] value) { 113 var previous = m_previousValues.get(identifier); 114 115 if (previous instanceof byte[] oldValue && Arrays.equals(oldValue, value)) { 116 // no change 117 return; 118 } 119 120 m_previousValues.put(identifier, value); 121 m_backend.log(identifier, value); 122 } 123 124 @Override 125 public void log(String identifier, int[] value) { 126 var previous = m_previousValues.get(identifier); 127 128 if (previous instanceof int[] oldValue && Arrays.equals(oldValue, value)) { 129 // no change 130 return; 131 } 132 133 m_previousValues.put(identifier, value); 134 m_backend.log(identifier, value); 135 } 136 137 @Override 138 public void log(String identifier, long[] value) { 139 var previous = m_previousValues.get(identifier); 140 141 if (previous instanceof long[] oldValue && Arrays.equals(oldValue, value)) { 142 // no change 143 return; 144 } 145 146 m_previousValues.put(identifier, value); 147 m_backend.log(identifier, value); 148 } 149 150 @Override 151 public void log(String identifier, float[] value) { 152 var previous = m_previousValues.get(identifier); 153 154 if (previous instanceof float[] oldValue && Arrays.equals(oldValue, value)) { 155 // no change 156 return; 157 } 158 159 m_previousValues.put(identifier, value); 160 m_backend.log(identifier, value); 161 } 162 163 @Override 164 public void log(String identifier, double[] value) { 165 var previous = m_previousValues.get(identifier); 166 167 if (previous instanceof double[] oldValue && Arrays.equals(oldValue, value)) { 168 // no change 169 return; 170 } 171 172 m_previousValues.put(identifier, value); 173 m_backend.log(identifier, value); 174 } 175 176 @Override 177 public void log(String identifier, boolean[] value) { 178 var previous = m_previousValues.get(identifier); 179 180 if (previous instanceof boolean[] oldValue && Arrays.equals(oldValue, value)) { 181 // no change 182 return; 183 } 184 185 m_previousValues.put(identifier, value); 186 m_backend.log(identifier, value); 187 } 188 189 @Override 190 public void log(String identifier, String value) { 191 var previous = m_previousValues.get(identifier); 192 193 if (previous instanceof String oldValue && oldValue.equals(value)) { 194 // no change 195 return; 196 } 197 198 m_previousValues.put(identifier, value); 199 m_backend.log(identifier, value); 200 } 201 202 @Override 203 public void log(String identifier, String[] value) { 204 var previous = m_previousValues.get(identifier); 205 206 if (previous instanceof String[] oldValue && Arrays.equals(oldValue, value)) { 207 // no change 208 return; 209 } 210 211 m_previousValues.put(identifier, value); 212 m_backend.log(identifier, value); 213 } 214 215 @Override 216 public <S> void log(String identifier, S value, Struct<S> struct) { 217 var previous = m_previousValues.get(identifier); 218 219 if (Objects.equals(previous, value)) { 220 // no change 221 return; 222 } 223 224 m_previousValues.put(identifier, value); 225 m_backend.log(identifier, value, struct); 226 } 227 228 @Override 229 public <S> void log(String identifier, S[] value, Struct<S> struct) { 230 var previous = m_previousValues.get(identifier); 231 232 if (previous instanceof Object[] oldValue && Arrays.equals(oldValue, value)) { 233 // no change 234 return; 235 } 236 237 m_previousValues.put(identifier, value); 238 m_backend.log(identifier, value, struct); 239 } 240}