org.rdv.rbnb.RBNBExport.java Source code

Java tutorial

Introduction

Here is the source code for org.rdv.rbnb.RBNBExport.java

Source

/*
 * RDV
 * Real-time Data Viewer
 * http://rdv.googlecode.com/
 * 
 * Copyright (c) 2005-2007 University at Buffalo
 * Copyright (c) 2005-2007 NEES Cyberinfrastructure Center
 * Copyright (c) 2008 Palta Software
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 * 
 * $URL$
 * $Revision$
 * $Date$
 * $Author$
 */

package org.rdv.rbnb;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.SimpleTimeZone;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.rbnb.sapi.ChannelMap;
import com.rbnb.sapi.Sink;

/**
 * A class to export numeric or video data from DataTurbine to disk.
 * 
 * @author  Jason P. Hanley
 * @since   1.3
 */
public class RBNBExport {
    /**
     * The logger for this class.
     * 
     * @since  1.3
     */
    static Log log = LogFactory.getLog(RBNBExport.class.getName());

    /**
     * The RBNB host name to connect too.
     * 
     * @since  1.3
     */
    private String rbnbHostName;

    /**
     * The RBNB port number to connect too.
     * 
     * @since  1.3
     */
    private int rbnbPortNumber;

    /**
     * Tell the export thread to cancel
     * 
     * @since  1.3
     */
    private boolean cancelExport;

    /**
     * Initialize the class with the RBNB server to export data from.
     * 
     * @param rbnbHostName    the host name of the RBNB server
     * @param rbnbPortNumber  the port number of the RBNB server
     * @since                 1.3
     */
    public RBNBExport(String rbnbHostName, int rbnbPortNumber) {
        this.rbnbHostName = rbnbHostName;
        this.rbnbPortNumber = rbnbPortNumber;

        cancelExport = false;
    }

    /**
     * Start the export of the channels from the RBNB server to the specified
     * file. The data will be exported between the two time bounds provided.
     * <p>
     * This methods will spawn the export in another thread and return before it
     * is completed. Pass a listener for progress information.
     * 
     * @param channels   the list of channels to export
     * @param dataFile   the data file to write the data to
     * @param startTime  the start time of the data to export
     * @param endTime    the end time of the data to export
     * @param listener   a listener to post progress to
     * @since            1.3
     */
    public void startExport(final List<String> channels, final File dataFile, final List<String> multimediaChannels,
            final File dataDirectory, final double startTime, final double endTime,
            final ProgressListener listener) {
        new Thread("RBNBExport") {
            public void run() {
                exportData(channels, dataFile, multimediaChannels, dataDirectory, startTime, endTime, listener);
            }
        }.start();
    }

    /**
     * Export the channels from the RBNB server to the specified file. The data
     * will be exported between the two time bounds provided.
     * 
     * @param channels   the list of channels to export
     * @param dataFile   the data file to write the data to
     * @param startTime  the start time of the data to export
     * @param endTime    the end time of the data to export
     * @param listener   a listener to post progress to
     * @since            1.3
     */
    private synchronized void exportData(List<String> numericChannels, File dataFile,
            List<String> multimediaChannels, File dataDirectory, double startTime, double endTime,
            ProgressListener listener) {

        if (listener == null) {
            // create dummy listener if needed
            listener = new ProgressListener() {
                public void postProgress(double progress) {
                }

                public void postCompletion() {
                }

                public void postError(String errorMessage) {
                }
            };
        }

        if (numericChannels == null) {
            numericChannels = new ArrayList<String>();
        }

        if (multimediaChannels == null) {
            multimediaChannels = new ArrayList<String>();
        }

        if (numericChannels.size() == 0 && multimediaChannels.size() == 0) {
            listener.postError("No channels were specified.");
            return;
        }

        if (numericChannels.size() > 0) {
            if (dataFile == null) {
                listener.postError("The data file is not specified.");
                return;
            } else if (dataFile.isDirectory()) {
                listener.postError("The data file is a directory.");
                return;
            }
        }

        if (multimediaChannels.size() > 0) {
            if (dataDirectory == null) {
                listener.postError("Data directory is invalid.");
                return;
            } else if (!dataDirectory.isDirectory()) {
                listener.postError("the data directory isn't a directory.");
                return;
            }
        }

        if (startTime > endTime) {
            listener.postError("The start time must be greater than the end time");
            return;
        }

        double time = startTime;

        try {
            Sink sink = new Sink();
            sink.OpenRBNBConnection(rbnbHostName + ":" + rbnbPortNumber, "RDVExport");
            ChannelMap cmap = new ChannelMap();
            for (int i = 0; i < numericChannels.size(); i++) {
                cmap.Add(numericChannels.get(i));
            }
            for (int i = 0; i < multimediaChannels.size(); i++) {
                cmap.Add(multimediaChannels.get(i));
            }

            BufferedWriter fileWriter = null;
            if (numericChannels.size() > 0) {
                fileWriter = new BufferedWriter(new FileWriter(dataFile));
            }

            if (fileWriter != null) {
                fileWriter.write("Start time: " + RBNBUtilities.secondsToISO8601(startTime) + "\r\n");
                fileWriter.write("End time: " + RBNBUtilities.secondsToISO8601(endTime) + "\r\n");
                fileWriter.write(
                        "Export time: " + RBNBUtilities.millisecondsToISO8601(System.currentTimeMillis()) + "\r\n");
                fileWriter.write("\r\n");

                // write channel names
                fileWriter.write("Time\t");
                for (int i = 0; i < numericChannels.size(); i++) {
                    String channel = numericChannels.get(i);
                    String[] channelParts = channel.split("/");
                    fileWriter.write(channelParts[channelParts.length - 1]);
                    if (i != numericChannels.size() - 1) {
                        fileWriter.write('\t');
                    }
                }
                fileWriter.write("\r\n");

                // fetch channel metadata and write channel units (if available)
                sink.RequestRegistration(cmap);
                ChannelMap rmap = sink.Fetch(-1);

                fileWriter.write("Seconds\t");
                for (int i = 0; i < numericChannels.size(); i++) {
                    String channel = numericChannels.get(i);
                    String unit = null;
                    int index = rmap.GetIndex(channel);
                    String[] metadata = rmap.GetUserInfo(index).split(",");
                    for (int j = 0; j < metadata.length; j++) {
                        String[] elements = metadata[j].split("=");
                        if (elements.length == 2 && elements[0].equals("units")) {
                            unit = elements[1];
                            break;
                        }
                    }
                    if (unit != null) {
                        fileWriter.write(unit);
                    }
                    fileWriter.write('\t');
                }
                fileWriter.write("\r\n");
            }

            listener.postProgress(0);

            double dataStartTime = -1;

            while (time < endTime && !cancelExport) {
                double duration = 2;
                if (time + duration > endTime) {
                    duration = endTime - time;
                }

                sink.Request(cmap, time, duration, "absolute");
                ChannelMap dmap = sink.Fetch(-1);

                ArrayList<Sample> samples = new ArrayList<Sample>();
                for (int i = 0; i < numericChannels.size(); i++) {
                    String channel = numericChannels.get(i);
                    int index = dmap.GetIndex(channel);
                    if (index != -1) {
                        int type = dmap.GetType(index);
                        double[] times = dmap.GetTimes(index);
                        for (int j = 0; j < times.length; j++) {
                            Sample sample;

                            /* Skip data that isn't in the requested time bounds. This is due
                             * to overlap between requests for data. 
                             */
                            if (times[j] > startTime && times[j] <= time) {
                                continue;
                            }

                            switch (type) {
                            case ChannelMap.TYPE_INT32:
                                sample = new Sample(channel, dmap.GetDataAsInt32(index)[j], times[j]);
                                break;
                            case ChannelMap.TYPE_INT64:
                                sample = new Sample(channel, dmap.GetDataAsInt64(index)[j], times[j]);
                                break;
                            case ChannelMap.TYPE_FLOAT32:
                                sample = new Sample(channel, dmap.GetDataAsFloat32(index)[j], times[j]);
                                break;
                            case ChannelMap.TYPE_FLOAT64:
                                sample = new Sample(channel, dmap.GetDataAsFloat64(index)[j], times[j]);
                                break;
                            default:
                                sample = new Sample(channel, "", times[j]);
                            }
                            samples.add(sample);
                        }
                    }
                }

                Collections.sort(samples, new SampleTimeComparator());

                Iterator<Sample> it = samples.iterator();
                boolean end = false;

                Sample s = null;
                if (it.hasNext()) {
                    s = it.next();
                } else {
                    end = true;
                }

                while (!end) {
                    double t = s.getTime();

                    if (dataStartTime == -1) {
                        dataStartTime = t;
                    }

                    fileWriter.write(Double.toString(t - dataStartTime) + "\t");
                    for (int i = 0; i < numericChannels.size(); i++) {
                        String c = numericChannels.get(i);
                        if (c.equals(s.getChannel()) && t == s.getTime()) {
                            fileWriter.write(s.getData());
                            if (it.hasNext()) {
                                s = it.next();
                            } else {
                                fileWriter.write("\r\n");
                                end = true;
                                break;
                            }
                        }
                        if (i == numericChannels.size() - 1) {
                            fileWriter.write("\r\n");
                        } else {
                            fileWriter.write('\t');
                        }
                    }
                }

                String videoChannel, fileName;
                SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH.mm.ss.SSS'Z'");
                dateFormat.setTimeZone(new SimpleTimeZone(0, "UTC"));

                for (int i = 0; i < multimediaChannels.size(); i++) {
                    videoChannel = multimediaChannels.get(i);
                    int index = dmap.GetIndex(videoChannel);
                    if (index != -1) {
                        int type = dmap.GetType(index);
                        if (type == ChannelMap.TYPE_BYTEARRAY) {
                            double[] times = dmap.GetTimes(index);
                            byte[][] datas = dmap.GetDataAsByteArray(index);
                            for (int j = 0; j < times.length; j++) {
                                byte[] data = datas[j];
                                // write image file
                                try {
                                    fileName = videoChannel;
                                    if (fileName.endsWith(".jpg")) {
                                        fileName = fileName.substring(0, fileName.length() - 4);
                                    }
                                    if (fileName.endsWith("/video")) {
                                        fileName = fileName.substring(0, fileName.length() - 6);
                                    }
                                    fileName = fileName.replace("/", "-");

                                    String timeStamp = dateFormat.format(new Date((long) (times[j] * 1000)));

                                    fileName += "_" + timeStamp + ".jpg";
                                    File outputFile = new File(dataDirectory, fileName);
                                    FileOutputStream out = new FileOutputStream(outputFile);
                                    out.write(data);
                                    out.close();
                                } catch (Exception e) {
                                    e.printStackTrace();
                                }
                            }
                        }
                    }
                }

                time += duration;

                listener.postProgress((time - startTime) / (endTime - startTime));
            }

            if (fileWriter != null) {
                fileWriter.close();
            }

            sink.CloseRBNBConnection();

            if (cancelExport) {
                dataFile.delete();
                listener.postError("Export canceled.");
            } else {
                listener.postCompletion();
            }
        } catch (Exception e) {
            e.printStackTrace();
            listener.postError("Error exporting data: " + e.getMessage());
            return;
        }

        cancelExport = false;

        return;
    }

    public void cancelExport() {
        cancelExport = true;
    }

    class Sample {
        String channel;
        String data;
        double time;

        public Sample(String channel, int data, double time) {
            this(channel, Integer.toString(data), time);
        }

        public Sample(String channel, long data, double time) {
            this(channel, Long.toString(data), time);
        }

        public Sample(String channel, float data, double time) {
            this(channel, Float.toString(data), time);
        }

        public Sample(String channel, double data, double time) {
            this(channel, Double.toString(data), time);
        }

        public Sample(String channel, String data, double time) {
            this.channel = channel;
            this.data = data;
            this.time = time;
        }

        public String getChannel() {
            return channel;
        }

        public String getData() {
            return data;
        }

        public double getTime() {
            return time;
        }
    }

    class SampleTimeComparator implements Comparator<Sample> {
        public int compare(Sample arg0, Sample arg1) {
            double t1 = arg0.getTime();
            double t2 = arg1.getTime();
            if (t1 == t2) {
                return 0;
            } else if (t1 < t2) {
                return -1;
            } else {
                return 1;
            }
        }
    }
}