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 java.nio.ByteBuffer;
008import java.util.Arrays;
009
010/** Log raw byte array values. */
011public class RawLogEntry extends DataLogEntry {
012  /** The data type for raw values. */
013  public static final String kDataType = "raw";
014
015  /**
016   * Constructs a raw log entry.
017   *
018   * @param log datalog
019   * @param name name of the entry
020   * @param metadata metadata
021   * @param type Data type
022   * @param timestamp entry creation timestamp (0=now)
023   */
024  public RawLogEntry(DataLog log, String name, String metadata, String type, long timestamp) {
025    super(log, name, type, metadata, timestamp);
026  }
027
028  /**
029   * Constructs a raw log entry.
030   *
031   * @param log datalog
032   * @param name name of the entry
033   * @param metadata metadata
034   * @param type Data type
035   */
036  public RawLogEntry(DataLog log, String name, String metadata, String type) {
037    this(log, name, metadata, type, 0);
038  }
039
040  /**
041   * Constructs a raw log entry.
042   *
043   * @param log datalog
044   * @param name name of the entry
045   * @param metadata metadata
046   * @param timestamp entry creation timestamp (0=now)
047   */
048  public RawLogEntry(DataLog log, String name, String metadata, long timestamp) {
049    this(log, name, metadata, kDataType, timestamp);
050  }
051
052  /**
053   * Constructs a raw log entry.
054   *
055   * @param log datalog
056   * @param name name of the entry
057   * @param metadata metadata
058   */
059  public RawLogEntry(DataLog log, String name, String metadata) {
060    this(log, name, metadata, 0);
061  }
062
063  /**
064   * Constructs a raw log entry.
065   *
066   * @param log datalog
067   * @param name name of the entry
068   * @param timestamp entry creation timestamp (0=now)
069   */
070  public RawLogEntry(DataLog log, String name, long timestamp) {
071    this(log, name, "", timestamp);
072  }
073
074  /**
075   * Constructs a raw log entry.
076   *
077   * @param log datalog
078   * @param name name of the entry
079   */
080  public RawLogEntry(DataLog log, String name) {
081    this(log, name, 0);
082  }
083
084  /**
085   * Appends a record to the log.
086   *
087   * @param value Value to record; will send entire array contents
088   * @param timestamp Time stamp (0 to indicate now)
089   */
090  public void append(byte[] value, long timestamp) {
091    m_log.appendRaw(m_entry, value, timestamp);
092  }
093
094  /**
095   * Appends a record to the log.
096   *
097   * @param value Value to record; will send entire array contents
098   */
099  public void append(byte[] value) {
100    append(value, 0);
101  }
102
103  /**
104   * Appends a record to the log.
105   *
106   * @param value Data to record
107   * @param start Start position of data (in byte array)
108   * @param len Length of data (must be less than or equal to value.length - offset)
109   * @param timestamp Time stamp (0 to indicate now)
110   */
111  public void append(byte[] value, int start, int len, long timestamp) {
112    m_log.appendRaw(m_entry, value, start, len, timestamp);
113  }
114
115  /**
116   * Appends a record to the log.
117   *
118   * @param value Data to record
119   * @param start Start position of data (in byte array)
120   * @param len Length of data (must be less than or equal to value.length - offset)
121   */
122  public void append(byte[] value, int start, int len) {
123    append(value, start, len, 0);
124  }
125
126  /**
127   * Appends a record to the log.
128   *
129   * @param value Data to record; will send from value.position() to value.limit()
130   * @param timestamp Time stamp (0 to indicate now)
131   */
132  public void append(ByteBuffer value, long timestamp) {
133    m_log.appendRaw(m_entry, value, timestamp);
134  }
135
136  /**
137   * Appends a record to the log.
138   *
139   * @param value Data to record; will send from value.position() to value.limit()
140   */
141  public void append(ByteBuffer value) {
142    append(value, 0);
143  }
144
145  /**
146   * Appends a record to the log.
147   *
148   * @param value Data to record
149   * @param start Start position of data (in value buffer)
150   * @param len Length of data (must be less than or equal to value.length - offset)
151   * @param timestamp Time stamp (0 to indicate now)
152   */
153  public void append(ByteBuffer value, int start, int len, long timestamp) {
154    m_log.appendRaw(m_entry, value, start, len, timestamp);
155  }
156
157  /**
158   * Appends a record to the log.
159   *
160   * @param value Data to record
161   * @param start Start position of data (in value buffer)
162   * @param len Length of data (must be less than or equal to value.length - offset)
163   */
164  public void append(ByteBuffer value, int start, int len) {
165    append(value, start, len, 0);
166  }
167
168  /**
169   * Updates the last value and appends a record to the log if it has changed.
170   *
171   * <p>Note: the last value is local to this class instance; using update() with two instances
172   * pointing to the same underlying log entry name will likely result in unexpected results.
173   *
174   * @param value Value to record; will send entire array contents
175   * @param timestamp Time stamp (0 to indicate now)
176   */
177  public synchronized void update(byte[] value, long timestamp) {
178    if (!equalsLast(value, 0, value.length)) {
179      copyToLast(value, 0, value.length);
180      append(value, timestamp);
181    }
182  }
183
184  /**
185   * Updates the last value and appends a record to the log if it has changed.
186   *
187   * <p>Note: the last value is local to this class instance; using update() with two instances
188   * pointing to the same underlying log entry name will likely result in unexpected results.
189   *
190   * @param value Value to record; will send entire array contents
191   */
192  public void update(byte[] value) {
193    update(value, 0);
194  }
195
196  /**
197   * Updates the last value and appends a record to the log if it has changed.
198   *
199   * <p>Note: the last value is local to this class instance; using update() with two instances
200   * pointing to the same underlying log entry name will likely result in unexpected results.
201   *
202   * @param value Data to record
203   * @param start Start position of data (in byte array)
204   * @param len Length of data (must be less than or equal to value.length - offset)
205   * @param timestamp Time stamp (0 to indicate now)
206   */
207  public synchronized void update(byte[] value, int start, int len, long timestamp) {
208    if (!equalsLast(value, start, len)) {
209      copyToLast(value, start, len);
210      append(value, start, len, timestamp);
211    }
212  }
213
214  /**
215   * Updates the last value and appends a record to the log if it has changed.
216   *
217   * <p>Note: the last value is local to this class instance; using update() with two instances
218   * pointing to the same underlying log entry name will likely result in unexpected results.
219   *
220   * @param value Data to record
221   * @param start Start position of data (in byte array)
222   * @param len Length of data (must be less than or equal to value.length - offset)
223   */
224  public void update(byte[] value, int start, int len) {
225    update(value, start, len, 0);
226  }
227
228  /**
229   * Updates the last value and appends a record to the log if it has changed.
230   *
231   * <p>Note: the last value is local to this class instance; using update() with two instances
232   * pointing to the same underlying log entry name will likely result in unexpected results.
233   *
234   * @param value Data to record; will send from value.position() to value.limit()
235   * @param timestamp Time stamp (0 to indicate now)
236   */
237  public synchronized void update(ByteBuffer value, long timestamp) {
238    if (!equalsLast(value)) {
239      int start = value.position();
240      int len = value.limit() - start;
241      copyToLast(value, start, len);
242      append(value, start, len, timestamp);
243    }
244  }
245
246  /**
247   * Updates the last value and appends a record to the log if it has changed.
248   *
249   * <p>Note: the last value is local to this class instance; using update() with two instances
250   * pointing to the same underlying log entry name will likely result in unexpected results.
251   *
252   * @param value Data to record; will send from value.position() to value.limit()
253   */
254  public void update(ByteBuffer value) {
255    update(value, 0);
256  }
257
258  /**
259   * Updates the last value and appends a record to the log if it has changed.
260   *
261   * <p>Note: the last value is local to this class instance; using update() with two instances
262   * pointing to the same underlying log entry name will likely result in unexpected results.
263   *
264   * @param value Data to record
265   * @param start Start position of data (in value buffer)
266   * @param len Length of data (must be less than or equal to value.length - offset)
267   * @param timestamp Time stamp (0 to indicate now)
268   */
269  public synchronized void update(ByteBuffer value, int start, int len, long timestamp) {
270    if (!equalsLast(value, start, len)) {
271      copyToLast(value, start, len);
272      append(value, start, len, timestamp);
273    }
274  }
275
276  /**
277   * Updates the last value and appends a record to the log if it has changed.
278   *
279   * <p>Note: the last value is local to this class instance; using update() with two instances
280   * pointing to the same underlying log entry name will likely result in unexpected results.
281   *
282   * @param value Data to record
283   * @param start Start position of data (in value buffer)
284   * @param len Length of data (must be less than or equal to value.length - offset)
285   */
286  public void update(ByteBuffer value, int start, int len) {
287    update(value, start, len, 0);
288  }
289
290  /**
291   * Gets whether there is a last value.
292   *
293   * <p>Note: the last value is local to this class instance and updated only with update(), not
294   * append().
295   *
296   * @return True if last value exists, false otherwise.
297   */
298  public synchronized boolean hasLastValue() {
299    return m_lastValue != null;
300  }
301
302  /**
303   * Gets the last value.
304   *
305   * <p>Note: the last value is local to this class instance and updated only with update(), not
306   * append().
307   *
308   * @return Last value, or null if none.
309   */
310  @SuppressWarnings("PMD.ReturnEmptyCollectionRatherThanNull")
311  public synchronized byte[] getLastValue() {
312    if (m_lastValue == null) {
313      return null;
314    }
315    return Arrays.copyOf(m_lastValue.array(), m_lastValue.limit());
316  }
317
318  private boolean equalsLast(byte[] value, int start, int len) {
319    if (m_lastValue == null || m_lastValue.limit() != len) {
320      return false;
321    }
322    return Arrays.equals(m_lastValue.array(), 0, len, value, start, start + len);
323  }
324
325  @SuppressWarnings("PMD.SimplifyBooleanReturns")
326  private boolean equalsLast(ByteBuffer value) {
327    if (m_lastValue == null) {
328      return false;
329    }
330    return value.equals(m_lastValue);
331  }
332
333  private boolean equalsLast(ByteBuffer value, int start, int len) {
334    if (m_lastValue == null || m_lastValue.limit() != len) {
335      return false;
336    }
337    int origpos = value.position();
338    value.position(start);
339    int origlimit = value.limit();
340    value.limit(start + len);
341    boolean eq = value.equals(m_lastValue);
342    value.position(origpos);
343    value.limit(origlimit);
344    return eq;
345  }
346
347  private void copyToLast(byte[] value, int start, int len) {
348    if (m_lastValue == null || m_lastValue.limit() < len) {
349      m_lastValue = ByteBuffer.allocate(len);
350    }
351    System.arraycopy(value, start, m_lastValue.array(), 0, len);
352    m_lastValue.limit(len);
353  }
354
355  private void copyToLast(ByteBuffer value, int start, int len) {
356    if (m_lastValue == null || m_lastValue.limit() < len) {
357      m_lastValue = ByteBuffer.allocate(len);
358    }
359    int origpos = value.position();
360    value.position(start);
361    value.get(m_lastValue.array(), 0, len);
362    value.position(origpos);
363    m_lastValue.limit(len);
364  }
365
366  private ByteBuffer m_lastValue;
367}