14.7. GTK: Displaying Query Data - show_data, write_minmax, file_add

The show_data function reads a hsoutput*.dat file, which contains interpolated query output from the Cascade Historian, retrieved by the InterpolatorSettings.get_data method. This function modifies the data slightly as necessary, puts it into the text widget, and sets up the histoutputpl.dat data file for gnuplot.

   /*--------------------------------------------------------------------
    * Function:    show_data
    * Returns:     t or nil
    * Description: Shows interpolated query results from the Cascade Historian
    *              in the text widget.  Sets up a single file (hsoutputpl.dat)
    *              that contains the content of all existing hsoutput files.
    *              This is necessary because gnuplot must read from a single file.
    *              Uses file_add() to do the actual reading and writing of the files.
    *------------------------------------------------------------------*/
   function show_data(txt_wgt, int_set, button, datalist, minmaxlist)
   {
     local start_time, tsk, infile, line, string_list, line_ret;
     local title_string, x_list, y_list;
     local f1, f2, f3, f4;
   
     x_list = car(datalist);
     y_list = cadr(datalist);
     midnite = find_midnite();
     
     if (int_set.fn == "NoInterpolator"  || int_set.fn == "Periodic")
       {
         x_is_time = t;
         title_string = "Query results: \n     Time             Y History\n";
       }
     else
       {
         x_is_time = nil;
         title_string = "Query results: \nX-hist / Y-hist\n";
       }
   
   //  midnite = find_midnite();
     txt_wgt.freeze();
     txt_wgt.backward_delete(txt_wgt.get_length());
     txt_wgt.insert(FONT, nil, nil, title_string, -1);
     
     if(int_set.dbflag == "NONE")
       infile = open("/tmp/cogentdemo/hsoutput.dat", "r");
     if(int_set.dbflag == "DB")
       infile = open("/tmp/cogentdemo/hsoutput2.dat", "r");
     if(int_set.dbflag == "DBP")
       infile = open("/tmp/cogentdemo/hsoutput3.dat", "r");
     if (infile)
       {
         line = nil;
         while(line != _eof_)
           {
             line = read_line(infile);
             if (line != "Unexpected end of file")
               {
                 if ((strstr(line,"#") != -1))
                   {
                     txt_wgt.insert(FONT, nil, nil, line, -1);
                     read_line(infile);
                     read_line(infile);
                   }

If the first character in the first line of output is "#", this means there was no data available. If this is the case, the .get_data method will have printed two dummy data lines into the hsoutputpl.dat file. These two lines must be read now, so that they will not get inserted into the text widget by the rest of this function. Hence we put in two extra Gamma read_line calls.

                 else
                   {
                     string_list = string_split(line, " ", 2);
                     if (int_set.fn == "NoInterpolator"
                         || int_set.fn == "Periodic")
                       line_ret = string(number(car(string_list)) - midnite,
                                         " ", cadr(string_list),"\n");

Valid data in the hsoutputpl.dat file comes in two space-separated columns of numbers. If the numbers in the first column are time values (created using NoInterpolator or Periodic for the interpolator), this function recalculates each value to the number of seconds after midnight. This makes the data easier to read, and makes a better display in the plot.

                     else
                       line_ret = string(number(car(string_list)),
                                         " ", cadr(string_list),"\n");
                     txt_wgt.insert(FONT, nil, nil, line_ret, -1);
                   }
               }
           }
         close(infile);
         
         if(x_list && y_list && minmaxlist != nil)
           {
             if (x_is_time)
               {
                 x_min = caar(minmaxlist);
                 x_max = cadar(minmaxlist);
               }        
             else
               {
                 x_min = floor(caar(minmaxlist));
                 x_max = ceil(cadar(minmaxlist));
               }          
             y_min = floor(caadr(minmaxlist));
             y_max = ceil(car(cdadr(minmaxlist))) + 1;
             int_set.misc_info = string("x_min = ", x_min, "\n",
                                        "x_max = ", x_max, "\n",
                                        "y_min = ", y_min, "\n",
                                        "y_max = ", y_max, "\n");
             write_minmax(int_set.misc_info);
           }
         else
           {
             txt_wgt.backward_delete(txt_wgt.get_point());
             txt_wgt.insert(FONT, nil, nil,
                            string("Sorry, there was an error retrieving\n",
                                   "data from the Cascade Historian."), -1);
           }
       }
     txt_wgt.thaw();

Having calculated the minimums and maximums, we put the information into one long string, for gnuplot to read, and write it to the plhistsetup.dat setup file, using the write_minmax function.

Next we need to create the output file for gnuplot. This code concatenates all the existing hsoutput*.dat files in order. These files were each created by the show_data function itself, and now it needs to compile the data from all existing files to include data for deadband queries.

   
     if(is_file("/tmp/cogentdemo/hsoutput.dat"))
       f1 = open("/tmp/cogentdemo/hsoutput.dat", "r");
     if(is_file("/tmp/cogentdemo/hsoutput2.dat"))
       f2 = open("/tmp/cogentdemo/hsoutput2.dat", "r");
     if(is_file("/tmp/cogentdemo/hsoutput3.dat"))
       f3 = open("/tmp/cogentdemo/hsoutput3.dat", "r");
     f4 = open("/tmp/cogentdemo/hsoutputpl.dat", "w");
   
     if(f1)
       {
         file_add(f1, f4);
         if(f2)
           file_add(f2, f4);
         else
           /* Put in a spacer line so gnuplot starts a new plot. */
           writec(f4, "999 999\n\n\n");
         if(f3)
           file_add(f3, f4);
       }
     
     close(f4);
   }

The gnuplot program can show multiple plots, if properly configured, as long as the data for each plot is separated by two newlines. However we might want to show one, two, or three plots, depending on the chosen Deadband options. To keep the code as simple as possible, and still cover all 7 possible ways that the plots could be displayed, we simply add the data for each plot to one final output file (hsoutput.dat), using the file_add function. The only drawback is that gnuplot can't handle extra empty spaces in its data file. There must be two and only two newlines separating each data set. So, to show plots one and three without plot two, we created a filler plot made of a bogus line of data ("999 999") followed by the right number of spaces. The bogus data is way outside any possible plot range, and thus the plot never appears.

   /*--------------------------------------------------------------------
    * Function:    write_minmax
    * Returns:     t or nil
    * Description: Writes a string to the plhistsetup.dat file containing
    *              four lines, each line assigning a value for an X or Y axis
    *              minimum or maximum variable, which are used by gnuplot.
    *------------------------------------------------------------------*/
   function write_minmax(minmax_string)
   {
     local fp = open("/tmp/cogentdemo/plhistsetup.dat", "a", nil);
     writec(fp, minmax_string);
     close(fp);
   }
   
   /*--------------------------------------------------------------------
    * Function:    file_add
    * Returns:     t or nil
    * Description: Adds content from a file previously opened for reading
    *              (inptr) to a file previously opened for writing (outptr).
    *              Adds extra newlines, which gnuplot uses to mark the beginning
    *              of a new index file.  Called only by show_data().
    *------------------------------------------------------------------*/
   function file_add(inptr, outptr)
   {
     local line;
     while(line != _eof_)
       {
         line = read_line(inptr);
         if (line != "Unexpected end of file")
           writec(outptr, string(line, "\n"));
       }
     writec(outptr, "\n\n");
     close(inptr);
   }