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.util.datalog; 006 007import edu.wpi.first.util.struct.Struct; 008import edu.wpi.first.util.struct.StructBuffer; 009import java.nio.ByteBuffer; 010 011/** 012 * Log struct-encoded values. 013 * 014 * @param <T> value class 015 */ 016public final class StructLogEntry<T> extends DataLogEntry { 017 private StructLogEntry( 018 DataLog log, String name, Struct<T> struct, String metadata, long timestamp) { 019 super(log, name, struct.getTypeString(), metadata, timestamp); 020 m_buf = StructBuffer.create(struct); 021 m_immutable = struct.isImmutable(); 022 m_cloneable = struct.isCloneable(); 023 log.addSchema(struct, timestamp); 024 } 025 026 /** 027 * Creates a struct-encoded log entry. 028 * 029 * @param <T> value class (inferred from struct) 030 * @param log datalog 031 * @param name name of the entry 032 * @param struct struct serialization implementation 033 * @param metadata metadata 034 * @param timestamp entry creation timestamp (0=now) 035 * @return StructLogEntry 036 */ 037 public static <T> StructLogEntry<T> create( 038 DataLog log, String name, Struct<T> struct, String metadata, long timestamp) { 039 return new StructLogEntry<>(log, name, struct, metadata, timestamp); 040 } 041 042 /** 043 * Creates a struct-encoded log entry. 044 * 045 * @param <T> value class (inferred from struct) 046 * @param log datalog 047 * @param name name of the entry 048 * @param struct struct serialization implementation 049 * @param metadata metadata 050 * @return StructLogEntry 051 */ 052 public static <T> StructLogEntry<T> create( 053 DataLog log, String name, Struct<T> struct, String metadata) { 054 return create(log, name, struct, metadata, 0); 055 } 056 057 /** 058 * Creates a struct-encoded log entry. 059 * 060 * @param <T> value class (inferred from struct) 061 * @param log datalog 062 * @param name name of the entry 063 * @param struct struct serialization implementation 064 * @param timestamp entry creation timestamp (0=now) 065 * @return StructLogEntry 066 */ 067 public static <T> StructLogEntry<T> create( 068 DataLog log, String name, Struct<T> struct, long timestamp) { 069 return create(log, name, struct, "", timestamp); 070 } 071 072 /** 073 * Creates a struct-encoded log entry. 074 * 075 * @param <T> value class (inferred from struct) 076 * @param log datalog 077 * @param name name of the entry 078 * @param struct struct serialization implementation 079 * @return StructLogEntry 080 */ 081 public static <T> StructLogEntry<T> create(DataLog log, String name, Struct<T> struct) { 082 return create(log, name, struct, 0); 083 } 084 085 /** 086 * Appends a record to the log. 087 * 088 * @param value Value to record 089 * @param timestamp Time stamp (0 to indicate now) 090 */ 091 public void append(T value, long timestamp) { 092 synchronized (m_buf) { 093 ByteBuffer bb = m_buf.write(value); 094 m_log.appendRaw(m_entry, bb, 0, bb.position(), timestamp); 095 } 096 } 097 098 /** 099 * Appends a record to the log. 100 * 101 * @param value Value to record 102 */ 103 public void append(T value) { 104 append(value, 0); 105 } 106 107 /** 108 * Updates the last value and appends a record to the log if it has changed. 109 * 110 * <p>Note: the last value is local to this class instance; using update() with two instances 111 * pointing to the same underlying log entry name will likely result in unexpected results. 112 * 113 * @param value Value to record 114 * @param timestamp Time stamp (0 to indicate now) 115 */ 116 public void update(T value, long timestamp) { 117 synchronized (m_buf) { 118 if (m_immutable || m_cloneable) { 119 if (value.equals(m_lastValue)) { 120 return; 121 } 122 try { 123 if (m_immutable) { 124 m_lastValue = value; 125 } else { 126 m_lastValue = m_buf.getStruct().clone(value); 127 } 128 ByteBuffer bb = m_buf.write(value); 129 m_log.appendRaw(m_entry, bb, 0, bb.position(), timestamp); 130 return; 131 } catch (CloneNotSupportedException e) { 132 // fall through 133 } 134 } 135 doUpdate(m_buf.write(value), timestamp); 136 } 137 } 138 139 /** 140 * Updates the last value and appends a record to the log if it has changed. 141 * 142 * <p>Note: the last value is local to this class instance; using update() with two instances 143 * pointing to the same underlying log entry name will likely result in unexpected results. 144 * 145 * @param value Value to record 146 */ 147 public void update(T value) { 148 update(value, 0); 149 } 150 151 /** 152 * Gets whether there is a last value. 153 * 154 * <p>Note: the last value is local to this class instance and updated only with update(), not 155 * append(). 156 * 157 * @return True if last value exists, false otherwise. 158 */ 159 public boolean hasLastValue() { 160 synchronized (m_buf) { 161 if (m_immutable) { 162 return m_lastValue != null; 163 } else if (m_cloneable && m_lastValue != null) { 164 return true; 165 } 166 return m_lastValueBuf != null; 167 } 168 } 169 170 /** 171 * Gets the last value. 172 * 173 * <p>Note: the last value is local to this class instance and updated only with update(), not 174 * append(). 175 * 176 * @return Last value, or null if none. 177 */ 178 public T getLastValue() { 179 synchronized (m_buf) { 180 if (m_immutable) { 181 return m_lastValue; 182 } else if (m_cloneable && m_lastValue != null) { 183 try { 184 return m_buf.getStruct().clone(m_lastValue); 185 } catch (CloneNotSupportedException e) { 186 // fall through 187 } 188 } 189 if (m_lastValueBuf == null) { 190 return null; 191 } 192 T val = m_buf.read(m_lastValueBuf); 193 m_lastValueBuf.position(0); 194 return val; 195 } 196 } 197 198 private void doUpdate(ByteBuffer bb, long timestamp) { 199 int len = bb.position(); 200 bb.limit(len); 201 bb.position(0); 202 if (m_lastValueBuf == null || !bb.equals(m_lastValueBuf)) { 203 // update last value 204 if (m_lastValueBuf == null || m_lastValueBuf.limit() < len) { 205 m_lastValueBuf = ByteBuffer.allocate(len); 206 } 207 bb.get(m_lastValueBuf.array(), 0, len); 208 bb.position(0); 209 m_lastValueBuf.limit(len); 210 211 // append to log 212 m_log.appendRaw(m_entry, bb, 0, len, timestamp); 213 } 214 } 215 216 private final StructBuffer<T> m_buf; 217 private ByteBuffer m_lastValueBuf; 218 private final boolean m_immutable; 219 private final boolean m_cloneable; 220 private T m_lastValue; 221}