/** * 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.integration; import java.text.ParseException; import java.time.Instant; import java.time.LocalDateTime; import java.time.ZoneId; import java.time.ZoneOffset; import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatterBuilder; import java.time.format.DateTimeParseException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.NavigableMap; import java.util.Map.Entry; import java.util.concurrent.ConcurrentSkipListMap; import org.diirt.datasource.formula.StatefulFormulaFunction; import org.diirt.datasource.timecache.Cache; import org.diirt.datasource.timecache.CacheConfig; import org.diirt.datasource.timecache.CacheFactory; import org.diirt.datasource.timecache.impl.SimpleFileDataSource; import org.diirt.datasource.timecache.impl.SimpleMemoryStorage; import org.diirt.datasource.timecache.query.Query; import org.diirt.datasource.timecache.query.QueryData; import org.diirt.datasource.timecache.query.QueryParameters; import org.diirt.datasource.timecache.query.QueryResult; import org.diirt.datasource.timecache.util.CacheHelper; import org.diirt.util.array.ArrayDouble; import org.diirt.util.time.TimeRelativeInterval; import org.diirt.vtype.VDouble; import org.diirt.vtype.VString; import org.diirt.vtype.VTable; import org.diirt.vtype.VType; import org.diirt.vtype.ValueFactory; /** * @author Fred Arnaud (Sopra Group) - ITER */ public class TCQueryFunction extends StatefulFormulaFunction { final private static DateTimeFormatter tsFormat = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"); final private static String S1 = "src/test/resources/archive-ramps-1H.csv"; // from '2014-12-04 00:00:01' to '2014-12-04 01:00:01' final private static String S2 = "src/test/resources/archive-ramps-1D.csv"; // from '2014-12-03 00:00:00' to '2014-12-04 00:00:00' final private static String S3 = "src/test/resources/archive-ramps-1W.csv"; // from '2014-11-25 23:59:59' to '2014-12-02 23:59:59' final private static String S4 = "src/test/resources/archive-ramps-2DUS.csv"; // from '2014-12-02 00:00:00' to '2014-12-04 00:00:00' but only multiple of 5 private final Cache cache; private Query currentQuery; private String currentPV; private Instant currentBegin; private Instant currentEnd; private NavigableMap<Instant, Double> valueMap; private VTable previousValue; public TCQueryFunction() { valueMap = new ConcurrentSkipListMap<Instant, Double>(); CacheConfig config = new CacheConfig(); config.addSource(new SimpleFileDataSource(S4)); config.addSource(new SimpleFileDataSource(S3)); config.addSource(new SimpleFileDataSource(S2)); config.addSource(new SimpleFileDataSource(S1)); config.setStorage(new SimpleMemoryStorage()); cache = CacheFactory.getCache(config); cache.setStatisticsEnabled(true); } @Override public boolean isVarArgs() { return false; } @Override public String getName() { return "tcQuery"; } @Override public String getDescription() { return "Query for data threw a TimeCache"; } @Override public List<Class<?>> getArgumentTypes() { return Arrays.<Class<?>> asList(VString.class, VString.class, VString.class); } @Override public List<String> getArgumentNames() { return Arrays.asList("pvName", "begin", "end"); } @Override public Class<?> getReturnType() { return VTable.class; } @Override public Object calculate(List<Object> args) { if (args == null || args.size() < 3) { return null; } if (previousValue == null) { previousValue = ValueFactory.newVTable( Arrays.<Class<?>> asList(String.class, double.class), Arrays.asList("Timestamp", "Value"), Arrays.asList(new ArrayList<String>(), new ArrayDouble())); } String newPV = ((VString) args.get(0)).getValue(); Instant newBegin = null, newEnd = null; try { newBegin = LocalDateTime.parse(((VString) args.get(1)).getValue(), tsFormat).toInstant(ZoneOffset.UTC); newEnd = LocalDateTime.parse(((VString) args.get(2)).getValue(), tsFormat).toInstant(ZoneOffset.UTC); } catch (DateTimeParseException e) { return previousValue; } if (currentPV == null || !currentPV.equals(newPV)) { currentPV = newPV; currentBegin = newBegin; currentEnd = newEnd; QueryParameters parameters = new QueryParameters() .timeInterval(TimeRelativeInterval.of(newBegin, newEnd)); if (currentQuery != null) { currentQuery.close(); } currentQuery = cache.createQuery(currentPV, VType.class, parameters); } else if (!currentBegin.equals(newBegin) || !currentEnd.equals(newEnd)) { QueryParameters parameters = new QueryParameters() .timeInterval(TimeRelativeInterval.of(newBegin, newEnd)); currentQuery.update(parameters); } // Create table by merging chunks as they come QueryResult result = currentQuery.getUpdate(); // If not new data is available, return the previous table if (result.getData().isEmpty()) { return previousValue; } int index = 0; for (QueryData data : result.getData()) { index = 0; for (VType dataToDisplay : data.getData()) { if (dataToDisplay instanceof VDouble) { valueMap.put(data.getTimestamps().get(index), ((VDouble) dataToDisplay).getValue()); } index++; } } index = 0; double[] array = new double[valueMap.size()]; List<String> times = new ArrayList<String>(); for (Entry<Instant, Double> entry : valueMap.entrySet()) { times.add(CacheHelper.format(entry.getKey())); array[index] = entry.getValue(); index++; } previousValue = ValueFactory.newVTable( Arrays.<Class<?>> asList(String.class, double.class), Arrays.asList("Timestamp", "Value"), Arrays.asList(times, new ArrayDouble(array, true))); return previousValue; } @Override public void dispose() { if (currentQuery != null) { currentQuery.close(); } } }