/** * Copyright (C) 2010-14 diirt developers. See COPYRIGHT.TXT * All rights reserved. Use is subject to license terms. See LICENSE.TXT */ package org.diirt.datasource.timecache.impl; import java.io.BufferedReader; import java.io.FileReader; import java.time.Instant; import java.util.TreeMap; import java.util.logging.Level; import java.util.logging.Logger; import org.diirt.datasource.timecache.DataChunk; import org.diirt.datasource.timecache.source.DataSource; import org.diirt.datasource.timecache.source.SourceData; import org.diirt.util.array.ArrayDouble; import org.diirt.util.time.TimeInterval; import org.diirt.vtype.Alarm; import org.diirt.vtype.AlarmSeverity; import org.diirt.vtype.Display; import org.diirt.vtype.Time; import org.diirt.vtype.VType; import org.diirt.vtype.ValueFactory; /** * {@link DataSource} implementation which read samples from a file dump of * 'sample_view' from Archive RDB. * @author Fred Arnaud (Sopra Group) - ITER */ public class SimpleFileDataSource implements DataSource { private static final Logger log = Logger.getLogger(SimpleFileDataSource.class.getName()); private final String csvFile; private final String csvSplitBy = ";"; private static final Integer channel_name = 0; private static final Integer smpl_time = 1; private static final Integer nanosecs = 2; private static final Integer severity = 3; // private static final Integer status = 4; private static final Integer num_val = 5; private static final Integer float_val = 6; private static final Integer str_val = 7; private static final Integer array_nval = 8; private static final Integer array_val = 9; private TreeMap<Instant, Integer> indexes; private int chunkSize = 1000; public SimpleFileDataSource(String csvFilePath) { this.csvFile = csvFilePath; indexes = new TreeMap<Instant, Integer>(); } public SimpleFileDataSource(String csvFilePath, int chunkSize) { this(csvFilePath); this.chunkSize = chunkSize; } /** {@inheritDoc} */ @Override public DataChunk getData(String channelName, Instant from) { if (channelName == null || channelName.isEmpty() || from == null) return new DataChunk(); try { return readSamples(channelName.trim(), from); } catch (Exception e) { log.log(Level.SEVERE, e.getMessage()); } return new DataChunk(); } private DataChunk readSamples(String channelName, Instant from) throws Exception { DataChunk chunk = new DataChunk(chunkSize); BufferedReader br = null; String currentLine = ""; Instant lastIndex = indexes.floorKey(from); Integer lineToStart = lastIndex == null ? 1 : indexes.get(lastIndex); Integer lineNumber = -1; try { br = new BufferedReader(new FileReader(csvFile)); while ((currentLine = br.readLine()) != null) { lineNumber++; if (lineNumber < lineToStart) continue; String[] columns = getColumns(currentLine); if (columns[channel_name] != null && columns[channel_name].equals(channelName)) { // Get time stamp final java.sql.Timestamp stamp = java.sql.Timestamp .valueOf(columns[smpl_time]); stamp.setNanos(Integer.valueOf(columns[nanosecs])); final Instant time = fromSQLTimestamp(stamp); if (time.compareTo(from) >= 0) { final VType value = decodeValue(columns, time); SourceData data = new SourceData(time, value); if (!chunk.add(data)) { TimeInterval i = chunk.getInterval(); if (i != null) indexes.put(i.getEnd(), lineNumber); break; } } } } } finally { if (br != null) br.close(); } return chunk; } private VType decodeValue(final String[] columns, final Instant ts) throws Exception { Time time = ValueFactory.newTime(ts); Alarm alarm = ValueFactory.alarmNone(); String alarmStr = columns[severity]; if (alarmStr != null) { for (AlarmSeverity s : AlarmSeverity.values()) { if (alarmStr.startsWith(s.name())) { alarm = ValueFactory.newAlarm(s, alarmStr); break; } } } Display display = ValueFactory.displayNone(); // Determine the value type // Try double if (columns[float_val] != null && !columns[float_val].isEmpty()) { Double dbl0 = Double.valueOf(columns[float_val]); // Get array elements - if any. final double data[] = readBlobArrayElements(dbl0, columns); if (data.length == 1) { return ValueFactory.newVDouble(data[0], alarm, time, display); } else { return ValueFactory.newVDoubleArray(new ArrayDouble(data), alarm, time, display); } } // Try integer if (columns[num_val] != null && !columns[num_val].isEmpty()) { final int num = Integer.valueOf(columns[num_val]); return ValueFactory.newVInt(num, alarm, time, display); } // Default to string final String txt = columns[str_val]; return ValueFactory.newVString(txt, alarm, time); } private double[] readBlobArrayElements(final double dbl0, final String[] columns) throws Exception { final String datatype = columns[array_nval]; // ' ' or NULL indicate: Scalar, not an array if (datatype == null || datatype.isEmpty()) return new double[] { dbl0 }; // Decode BLOB final String[] values = columns[array_val].split(" "); final double[] array = new double[values.length]; for (int i = 0; i < values.length; i++) array[i] = Double.valueOf(values[i]); return array; } private Instant fromSQLTimestamp(final java.sql.Timestamp sql_time) { final long millisecs = sql_time.getTime(); final long seconds = millisecs / 1000; final int nanoseconds = sql_time.getNanos(); return Instant.ofEpochSecond(seconds, nanoseconds); } private String[] getColumns(final String line) { return line.split(csvSplitBy, -1); } public String getCsvFile() { return csvFile; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((csvFile == null) ? 0 : csvFile.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; SimpleFileDataSource other = (SimpleFileDataSource) obj; if (csvFile == null) { if (other.csvFile != null) return false; } else if (!csvFile.equals(other.csvFile)) return false; return true; } }