/*******************************************************************************
* ALMA - Atacama Large Millimeter Array
* Copyright (c) ESO - European Southern Observatory, 2011
* (in the framework of the ALMA collaboration).
* All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*******************************************************************************/
package cl.utfsm.samplingSystemUI;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Date;
import java.util.StringTokenizer;
import alma.acs.util.IsoDateFormat;
import alma.acs.util.UTCUtility;
import cl.utfsm.samplingSystemUI.core.DataItem;
/**
* This class correlates various sampling sets through time, and outputs them in a nicely csv file.
*
* This class takes various Sampling Data file outputs, which are passed through the addSamplingSet method.
* Once finished adding Sampling Set, just call the dumpToFile method, and the data will be correlated.
* Please notice that the usage of memory is minimal and has to be kept whis way.
*
* @author Arturo Hoffstadt Urrutia <ahoffsta@inf.utfsm.cl>
*/
public class SamplingDataCorrelator {
private String filename="";
private String group="";
private long frequency;
private Date startTimestamp = null;
private FileWriter file;
private BufferedWriter writer;
private String header;
private ArrayList<ArrayList<DataItem>> data;
private ArrayList<BufferedReader> readers;
private ArrayList<String> headers;
private IsoDateFormat formater;
private int meanQty;
private ArrayList<ArrayList<Double>> meanData;
/**
* Default constructor, takes the parameters, stores them, and creates the filename.
* @param group The name of the Sampling Group to which the data belongs to.
* @param frequency The Frequency at which the data was sampled.
* @param startTimestamp At which time that sampling process started.
*/
public SamplingDataCorrelator( String group, long frequency, Date startTimestamp ){
data = new ArrayList<ArrayList<DataItem>>();
readers = new ArrayList<BufferedReader>();
headers = new ArrayList<String>();
this.group = group;
this.frequency = frequency;
this.startTimestamp = startTimestamp;
formater = new IsoDateFormat();
filename = "" + this.group.replace('/', '-') + "_" + this.frequency + "_" + formater.format( this.startTimestamp ) + ".csv";
header = "\"Timestamp in ISO Format\"";
meanQty = 4;
meanData = new ArrayList<ArrayList<Double>>();
}
/**
* Registers a Sampling Set, and read the first line, obtaining the component and the property which sampled.
* @param filaname Name of the file in which the data for this Sampling Set was dumped.
*/
public void addSamplingSet( String filename ){
BufferedReader br = openReadOnly(filename);
String line = null;
try {
if( ( line = br.readLine() ) != null ){
StringTokenizer st = new StringTokenizer( line , ";" );
while( st.hasMoreTokens() ){
st.nextToken();
headers.add(st.nextToken());
}
}
} catch (IOException e) {
e.printStackTrace();
}
readers.add(br);
data.add(new ArrayList<DataItem>());
header = header + ";" + headers.get(headers.size()-1);
meanData.add( new ArrayList<Double>() );
}
/**
* Start the Correlation and Dumping to File process.
*/
public void dumpToFile(){
dumpToFile( 0.5 );
for(BufferedReader br: readers){
try {
br.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
/**
* Starts the correlation process. This method should only be called if you know what you are doing.<br />
* Setting the prec lower or higher will get you leaks of data.
* @param prec How much of the Frequency will each entry take as a valid interval of time to look forward and backward for data.
*/
public void dumpToFile( double prec ){
boolean done = false;
int flag = 0;
frequency = 1000000L / frequency;
long w = (long) (frequency*prec);
// A brief buffer is created, according to how much data the mean needs.
consume();
for( int k = 0; k < meanQty; k++ ){
consume();
}
//The First timestamp (the earliest), is the one that is used to align the data.
long timestamp = data.get(0).get(0).getTime();
// The file is open, and the header is written
openReadWrite();
try {
writer.write(header+"\n");
} catch (IOException e1) {
e1.printStackTrace();
}
while( !done ){
consume();
String line = "" + formater.format(new Date(UTCUtility.utcOmgToJava(timestamp)));
boolean dataPresent = true;
for( ArrayList<DataItem> i: data ){
dataPresent = false;
if( i.isEmpty() ){
line += ";";
continue;
}
DataItem item = i.get(0);
if( (item.getTime() >= (timestamp-w) ) &&
(item.getTime() <= (timestamp+w) )){
line += ";" + item.getValue();
dataPresent = true;
addValueToMean( data.indexOf(i), item.getValue() );
i.remove(item);
}else{
if( ( item.getTime() >= ( timestamp + w )) &&
( item.getTime() <= ( timestamp + frequency - w ))){
line += ";" + mean(data.indexOf(i));
addValueToMean( data.indexOf(i), mean( data.indexOf(i)) );
i.remove(item);
}else{
line += ";" + mean(data.indexOf(i));
addValueToMean( data.indexOf(i), mean( data.indexOf(i)) );
}
}
if( i.isEmpty() )
flag++;
}
if( dataPresent ){
try {
writer.write( line + "\n" );
}catch(IOException e){
e.printStackTrace();
}
}
// Check if we passed over all dataItem recollected
if( flag == data.size() )
done = true;
timestamp += frequency;
//System.out.println("Array completed: " + flag + "\t Array Size: " + data.get(0).size());
}
try {
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* Method that consumes one line on every cvs file, storing them on memory.
* @return True in case that there remains lines to be consumed, and false in case that no lines remain in every file.
*/
public boolean consume(){
String line = null;
String timestamp = null;
String value = null;
int finished = 0;
int j = 0;
for( BufferedReader i : readers){
try {
if( ( line = i.readLine() ) != null ){
StringTokenizer st = new StringTokenizer( line , ";" );
while( st.hasMoreTokens() ){
timestamp = st.nextToken();
value = st.nextToken();
}
//System.out.println("Parsed: " + formater.format(new Date( formater.parse( timestamp ).getTime() )) + "; " + value );
data.get(j).add( new DataItem( UTCUtility.utcJavaToOmg((formater.parse(timestamp).getTime())), Double.parseDouble(value) ) );
}else{
finished++;
}
} catch (IOException e) {
e.printStackTrace();
} catch (ParseException e) {
e.printStackTrace();
}
j++;
}
if( finished == readers.size() )
return false;
else
return true;
}
/**
* Open the file with the filename specified in the object.
*/
private void openReadWrite(){
try {
file = new FileWriter(filename);
} catch (IOException e) {
e.printStackTrace();
}
writer = new BufferedWriter(file);
}
private BufferedReader openReadOnly(String filename){
try {
return new BufferedReader( new FileReader( filename ) );
} catch (FileNotFoundException e) {
e.printStackTrace();
return null;
}
}
private double mean( int index ){
if( meanData.get(index).isEmpty() )
return 0.0;
else{
double sum = 0;
for( Double i : meanData.get(index) ){
sum += i.doubleValue();
}
return sum/(double)(meanData.get(index).size());
}
}
private void addValueToMean( int index, double value ){
meanData.get( index ).add( new Double( value ) );
if( meanData.get( index ).size() > meanQty )
meanData.get( index ).remove(0);
}
public String getFilename() {
return filename;
}
}