/** * Reads the data from a given reader. * * @param aProject the project to read the settings to; * @param aReader the reader to read the data from, cannot be <code>null</code>. * @throws IOException in case of I/O problems. */ @SuppressWarnings("boxing") public static void read(final StubDataSet aDataSet, final Reader aReader) throws IOException { int size = -1; Integer rate = null, channels = null, enabledChannels = null; long triggerPos = -1L; long absLen = -1L; // assume 'new' file format is in use, don't support uncompressed ones... boolean compressed = true; final BufferedReader br = new BufferedReader(aReader); final List<String[]> dataValues = new ArrayList<String[]>(); String line; while ((line = br.readLine()) != null) { // Determine whether the line is an instruction, or data... final Matcher instructionMatcher = OLS_INSTRUCTION_PATTERN.matcher(line); final Matcher dataMatcher = OLS_DATA_PATTERN.matcher(line); if (dataMatcher.matches()) { final String[] dataPair = new String[] {dataMatcher.group(1), dataMatcher.group(2)}; dataValues.add(dataPair); } else if (instructionMatcher.matches()) { // Ok; found an instruction... final String instrKey = instructionMatcher.group(1); final String instrValue = instructionMatcher.group(2); if ("Size".equals(instrKey)) { size = safeParseInt(instrValue); } else if ("Rate".equals(instrKey)) { rate = safeParseInt(instrValue); } else if ("Channels".equals(instrKey)) { channels = safeParseInt(instrValue); } else if ("TriggerPosition".equals(instrKey)) { triggerPos = Long.parseLong(instrValue); } else if ("EnabledChannels".equals(instrKey)) { enabledChannels = safeParseInt(instrValue); } else if ("CursorEnabled".equals(instrKey)) { aDataSet.setCursorsEnabled(Boolean.parseBoolean(instrValue)); } else if ("Compressed".equals(instrKey)) { compressed = Boolean.parseBoolean(instrValue); } else if ("AbsoluteLength".equals(instrKey)) { absLen = Long.parseLong(instrValue); } else if ("CursorA".equals(instrKey)) { final long value = safeParseLong(instrValue); if (value > Long.MIN_VALUE) { aDataSet.getCursor(0).setTimestamp(value); } } else if ("CursorB".equals(instrKey)) { final long value = safeParseLong(instrValue); if (value > Long.MIN_VALUE) { aDataSet.getCursor(1).setTimestamp(value); } } else if (instrKey.startsWith("Cursor")) { final int idx = safeParseInt(instrKey.substring(6)); final long pos = Long.parseLong(instrValue); if (pos > Long.MIN_VALUE) { aDataSet.getCursor(idx).setTimestamp(pos); } } } } // Perform some sanity checks, make it not possible to import invalid // data... if (dataValues.isEmpty()) { throw new IOException("Data file does not contain any sample data!"); } if (!compressed) { throw new IOException( "Uncompressed data file found! Please send this file to the OLS developers!"); } // In case the size is not provided (as of 0.9.4 no longer mandatory), // take the length of the data values as size indicator... if (size < 0) { size = dataValues.size(); } if (size != dataValues.size()) { throw new IOException("Data file is corrupt?! Data size does not match sample count!"); } if (rate == null) { throw new IOException("Data file is corrupt?! Sample rate is not provided!"); } if ((channels == null) || (channels <= 0) || (channels > 32)) { throw new IOException("Data file is corrupt?! Channel count is not provided!"); } // Make sure the enabled channels are defined... if (enabledChannels == null) { enabledChannels = NumberUtils.getBitMask(channels); } int[] values = new int[size]; long[] timestamps = new long[size]; try { for (int i = 0; i < size; i++) { final String[] dataPair = dataValues.get(i); values[i] = (int) Long.parseLong(dataPair[0], 16); timestamps[i] = Long.parseLong(dataPair[1], 10) & Long.MAX_VALUE; } } catch (final NumberFormatException exception) { throw new IOException("Invalid data encountered.", exception); } // Allow the absolute length to be undefined, in which case the last // time stamp is used (+ some margin to be able to see the last // sample)... long absoluteLength = Math.max(absLen, timestamps[size - 1]); // Finally set the captured data, and notify all event listeners... aDataSet.setCapturedData( new CapturedData( values, timestamps, triggerPos, rate, channels, enabledChannels, absoluteLength)); }
/** Helper class that is capable of reading & writing OLS data files. */ public final class OlsDataHelper { // CONSTANTS /** The regular expression used to parse an (OLS-datafile) instruction. */ private static final Pattern OLS_INSTRUCTION_PATTERN = Pattern.compile("^;([^:]+):\\s+([^\r\n]+)$"); /** The regular expression used to parse an (OLS-datafile) data value. */ private static final Pattern OLS_DATA_PATTERN = Pattern.compile("^([0-9a-fA-F]+)@(\\d+)$"); // METHODS /** * Reads the data from a given reader. * * @param aProject the project to read the settings to; * @param aReader the reader to read the data from, cannot be <code>null</code>. * @throws IOException in case of I/O problems. */ @SuppressWarnings("boxing") public static void read(final StubDataSet aDataSet, final Reader aReader) throws IOException { int size = -1; Integer rate = null, channels = null, enabledChannels = null; long triggerPos = -1L; long absLen = -1L; // assume 'new' file format is in use, don't support uncompressed ones... boolean compressed = true; final BufferedReader br = new BufferedReader(aReader); final List<String[]> dataValues = new ArrayList<String[]>(); String line; while ((line = br.readLine()) != null) { // Determine whether the line is an instruction, or data... final Matcher instructionMatcher = OLS_INSTRUCTION_PATTERN.matcher(line); final Matcher dataMatcher = OLS_DATA_PATTERN.matcher(line); if (dataMatcher.matches()) { final String[] dataPair = new String[] {dataMatcher.group(1), dataMatcher.group(2)}; dataValues.add(dataPair); } else if (instructionMatcher.matches()) { // Ok; found an instruction... final String instrKey = instructionMatcher.group(1); final String instrValue = instructionMatcher.group(2); if ("Size".equals(instrKey)) { size = safeParseInt(instrValue); } else if ("Rate".equals(instrKey)) { rate = safeParseInt(instrValue); } else if ("Channels".equals(instrKey)) { channels = safeParseInt(instrValue); } else if ("TriggerPosition".equals(instrKey)) { triggerPos = Long.parseLong(instrValue); } else if ("EnabledChannels".equals(instrKey)) { enabledChannels = safeParseInt(instrValue); } else if ("CursorEnabled".equals(instrKey)) { aDataSet.setCursorsEnabled(Boolean.parseBoolean(instrValue)); } else if ("Compressed".equals(instrKey)) { compressed = Boolean.parseBoolean(instrValue); } else if ("AbsoluteLength".equals(instrKey)) { absLen = Long.parseLong(instrValue); } else if ("CursorA".equals(instrKey)) { final long value = safeParseLong(instrValue); if (value > Long.MIN_VALUE) { aDataSet.getCursor(0).setTimestamp(value); } } else if ("CursorB".equals(instrKey)) { final long value = safeParseLong(instrValue); if (value > Long.MIN_VALUE) { aDataSet.getCursor(1).setTimestamp(value); } } else if (instrKey.startsWith("Cursor")) { final int idx = safeParseInt(instrKey.substring(6)); final long pos = Long.parseLong(instrValue); if (pos > Long.MIN_VALUE) { aDataSet.getCursor(idx).setTimestamp(pos); } } } } // Perform some sanity checks, make it not possible to import invalid // data... if (dataValues.isEmpty()) { throw new IOException("Data file does not contain any sample data!"); } if (!compressed) { throw new IOException( "Uncompressed data file found! Please send this file to the OLS developers!"); } // In case the size is not provided (as of 0.9.4 no longer mandatory), // take the length of the data values as size indicator... if (size < 0) { size = dataValues.size(); } if (size != dataValues.size()) { throw new IOException("Data file is corrupt?! Data size does not match sample count!"); } if (rate == null) { throw new IOException("Data file is corrupt?! Sample rate is not provided!"); } if ((channels == null) || (channels <= 0) || (channels > 32)) { throw new IOException("Data file is corrupt?! Channel count is not provided!"); } // Make sure the enabled channels are defined... if (enabledChannels == null) { enabledChannels = NumberUtils.getBitMask(channels); } int[] values = new int[size]; long[] timestamps = new long[size]; try { for (int i = 0; i < size; i++) { final String[] dataPair = dataValues.get(i); values[i] = (int) Long.parseLong(dataPair[0], 16); timestamps[i] = Long.parseLong(dataPair[1], 10) & Long.MAX_VALUE; } } catch (final NumberFormatException exception) { throw new IOException("Invalid data encountered.", exception); } // Allow the absolute length to be undefined, in which case the last // time stamp is used (+ some margin to be able to see the last // sample)... long absoluteLength = Math.max(absLen, timestamps[size - 1]); // Finally set the captured data, and notify all event listeners... aDataSet.setCapturedData( new CapturedData( values, timestamps, triggerPos, rate, channels, enabledChannels, absoluteLength)); } /** * Writes the data to the given writer. * * @param aDataSet the project to write the settings for, cannot be <code>null</code> ; * @param aWriter the writer to write the data to, cannot be <code>null</code>. * @throws IOException in case of I/O problems. */ public static void write(final StubDataSet aDataSet, final Writer aWriter) throws IOException { final BufferedWriter bw = new BufferedWriter(aWriter); final AcquisitionResult capturedData = aDataSet.getCapturedData(); final Cursor[] cursors = aDataSet.getCursors(); final boolean cursorsEnabled = aDataSet.isCursorsEnabled(); try { final int[] values = capturedData.getValues(); final long[] timestamps = capturedData.getTimestamps(); bw.write(";Size: "); bw.write(Integer.toString(values.length)); bw.newLine(); bw.write(";Rate: "); bw.write(Integer.toString(capturedData.getSampleRate())); bw.newLine(); bw.write(";Channels: "); bw.write(Integer.toString(capturedData.getChannels())); bw.newLine(); bw.write(";EnabledChannels: "); bw.write(Integer.toString(capturedData.getEnabledChannels())); bw.newLine(); if (capturedData.hasTriggerData()) { bw.write(";TriggerPosition: "); bw.write(Long.toString(capturedData.getTriggerPosition())); bw.newLine(); } bw.write(";Compressed: "); bw.write(Boolean.toString(true)); bw.newLine(); bw.write(";AbsoluteLength: "); bw.write(Long.toString(capturedData.getAbsoluteLength())); bw.newLine(); bw.write(";CursorEnabled: "); bw.write(Boolean.toString(cursorsEnabled)); bw.newLine(); for (int i = 0; cursorsEnabled && (i < cursors.length); i++) { if (cursors[i].isDefined()) { bw.write(String.format(";Cursor%d: ", Integer.valueOf(i))); bw.write(Long.toString(cursors[i].getTimestamp())); bw.newLine(); } } for (int i = 0; i < values.length; i++) { bw.write(formatSample(values[i], timestamps[i])); bw.newLine(); } } finally { bw.flush(); } } /** * Formats the given value and timestamp into a single sample string. * * @param aValue the sample value to format; * @param aTimestamp the timestamp to format. * @return the sample string, in the form of * <value<sub>16</sub>>@<timestamp<sub>10</sub>>. */ @SuppressWarnings("boxing") static String formatSample(final int aValue, final long aTimestamp) { // values can become negative (full 32-bit is used!), while timestamps never // can be negative (it is a relative timestamp!)... return String.format("%08x@%d", aValue, (aTimestamp & Long.MAX_VALUE)); } }