WriteCSV.g

WriteCSV.g — writes data to CSV files.

Code

[Note]

The code for this and other example scripts can be found in the DataHub distribution archive, typically at one of these locations:

    C:\Program Files\Cogent\OPC DataHub\scripts\

    C:\Program Files\Cogent\Cascade DataHub\scripts\

Please refer to Section 3.1, “How to Run a Script” for more information on using scripts.

/*
 * Write data to a number of different CSV files.  Each data set is written at
 * a different interval, either per second, per minute or per hour.  A new CSV
 * file is created every hour or every day.
 *
 * The points names in each data set are read from a file.  The file consists of each
 * point name, one per line.  The output file will contain a time stamp followed
 * by the value for each point in the order that the points are listed in the
 * point name file.
 *
 * To change the format of the file name, change the method "GenerateFileName".
 *
 * To change the extension of the file, change the member variable "filesuffix".
 *
 * To change the file names and timing, change the .NewDataSet calls in the
 * method WriteCSV.constructor.
 */

require ("Application");
require ("CSVSupport");

class WriteCSV Application
{
    datasets;
}

class MyCSVWriter CSVWriter
{
    pointfile;            // The name of a file containing the point list.
    sample_rate;          // one of #second, #minute, #hour or list(hour,minute,second)
    file_rotate_rate;     // one of #minute, #hour, #day or list(hour,minute,second)
    filesuffix = ".csv";  // normally .csv.  Override it to create .txt files.
}

/* This will produce a file name like:
 *     base_20090423_PM3.csv
 */
 
/*
method MyCSVWriter.GenerateFileName()
{
    local tm = localtime(nanoclock());
    format("%s_%d%02d%02d_%s%d%s", .filebase,
           tm.year+1900, tm.mon+1, tm.mday,
           tm.hour >= 12 ? "PM" : "AM",
           tm.hour > 12 ? tm.hour - 12 : (tm.hour == 0 ? 12 : tm.hour),
           .filesuffix);
}
*/

/* This will produce a file name like:
 *     base-20090423-1545.csv
 */
method MyCSVWriter.GenerateFileName()
{
    local tm = localtime(nanoclock());
    format("%s-%d%02d%02d-%02d%02d%s", .filebase,
           tm.year+1900, tm.mon+1, tm.mday,
           tm.hour, tm.min,
           .filesuffix);
}

/*
 * Create a new writer and start the timers to write new data and to
 * create a new log file.
 */
method WriteCSV.NewDataSet (output_file_base, pointfile, sample_rate, file_rotate_rate, separate_lines)
{
    local	writer = new MyCSVWriter();
    if (writer.ReadPointsFromCSV(pointfile))
    {
        writer.sample_rate = sample_rate;
        writer.file_rotate_rate = file_rotate_rate;
        writer.SetFileBase(output_file_base);
        writer.SetSeparateLines(separate_lines);
		
        switch(writer.sample_rate)
        {
            case (#second):
                .TimerAt(nil,nil,nil,nil,nil,nil,`(@self).WriteData(@writer));
            case (#minute):
                .TimerAt(nil,nil,nil,nil,nil,0,`(@self).WriteData(@writer));
            case (#hour):
                .TimerAt(nil,nil,nil,nil,0,0,`(@self).WriteData(@writer));
            case (#day):
                .TimerAt(nil,nil,nil,0,0,0,`(@self).WriteData(@writer));
            default:
                if (list_p(writer.sample_rate))
                {
                    // List of hour, minute, second specification
                    local  times = list_to_array(writer.sample_rate);
                    .TimerAt(nil,nil,nil,times[0],times[1],times[2],`(@self).WriteData(@writer));
                }
                else // Default is hourly
                {
                    .TimerAt(nil,nil,nil,nil,0,0,`(@self).WriteData(@writer));
                }
        }
        switch(writer.file_rotate_rate)
        {
            case (#minute):
                .TimerAt(nil,nil,nil,nil,nil,0,`(@self).RotateFile(@writer));
            case (#hour):
                .TimerAt(nil,nil,nil,nil,0,0,`(@self).RotateFile(@writer));
            case (#day):
                .TimerAt(nil,nil,nil,0,0,0,`(@self).RotateFile(@writer));
            default:
                if (list_p(writer.file_rotate_rate))
                {
                    // List of hour, minute, second specification
                    local  times = list_to_array(writer.file_rotate_rate);
                    .TimerAt(nil,nil,nil,times[0],times[1],times[2],`(@self).RotateFile(@writer));
                }
                else // Default is hourly
                {
                    .TimerAt(nil,nil,nil,nil,0,0,`(@self).RotateFile(@writer));
                }
        }
        .datasets = cons(writer, .datasets);
    }
}

method WriteCSV.WriteData(writer)
{
    writer.WriteLine();
}

method WriteCSV.RotateFile(writer)
{
    .TimerAfter(0.1, `(@writer).IncrementFileName());
}

/* Write the 'main line' of the program here. */
method WriteCSV.constructor ()
{
    /* Specify the CSV files to write.  Modify the .NewDataSet lines to specify how
     * to write each CSV file.
     * Arguments are:
     *    output_file_base:  The first part of the file, before the date string
     *    pointfile:  The name of a file containing the point names for this data set
     *    sample_rate:  One of #second, #minute, #hour or list(hour,minute,second)
     *					telling how frequently to write a line to the CSV file
     *    file_rotate_rate:  One of #minute, #hour, #day or list(hour,minute,second)
     *					telling how frequently to create a new CSV file.
     *    separate_lines:  If this is nil, write all point values on one line.  If this
     *                  is t, write each point value on a separate line.
     *
     * This function will read the pointfile as a CSV file and treat the first field in each
     * row as the name of a point to be recorded into the output file.
     *
     * When specifying timing, the input is the list of (hour,minute,second) as accepted
     * by the "at" function (see documentation).  A value of nil for hour, minute or second
     * stands for all values for that parameter.  A list of values for hour, minute or second
     * specifies an event only when the hour, minute or second matches one of the values in
     * the list.
     * Examples:
     *    list(nil,nil,nil)				- every second
     *    list(nil,list(0,15,30,45),0)	- every 15 minutes
     *	  list(nil,list(0,15,30,45),30) - every 15 minutes, 30 seconds past the minute
     *    list(list(0,8,16), 0, 0)      - at midnight, 8AM and 4PM exactly
     *    list(list(0,8,16), 5, 0)      - at 12:05AM, 8:05AM and 4:05PM 
     * 
     * The symbols #second, #minute, #hour, #day are conveniences for:
     *    second:   list(nil,nil,nil)  - any hour, any minute, every second
     *    minute:   list(nil,nil,0)    - any hour, every minute at 0 seconds
     *    hour:     list(nil,0,0)      - every hour, on the hour
     *    day:      list(0,0,0)        - at midnight (0 hour, 0 minute, 0 second)
     */
    .NewDataSet("c:/tmp/Group1", "c:/tmp/Group1_Points.txt", #second, #hour, nil);
    .NewDataSet("c:/tmp/Group2", "c:/tmp/Group2_Points.txt", #second, list(nil,list(0, 15,30,45),0), nil);
    .NewDataSet("c:/tmp/Group3", "c:/tmp/Group3_Points.txt", #second, list(nil,nil,0), nil);
}

/* Any code to be run when the program gets shut down. */
method WriteCSV.destructor ()
{
    with writer in .datasets do
    {
        destroy(writer);
    }
}

/* Start the program by instantiating the class.  If your
 * constructor code does not create a persistent reference to
 * the instance (self), then it will be destroyed by the
 * garbage collector soon after creation.  If you do not want
 * this to happen, assign the instance to a global variable, or
 * create a static data member in your class to which you assign
 * 'self' during the construction process.  ApplicationSingleton()
 * does this for you automatically. */
ApplicationSingleton (WriteCSV);