Photon: Sending Queries - send_query, .assign_values, .get_data

Cogent Tools Demo and Tutorials: Version 4.0
Prev | Chapter 14. History Functions | Next

# 14.14. Photon: Sending Queries - `send_query`, `.assign_values`, `.get_data`

The `send_query` function calls the `.assign_values` method to get the currently-entered values from the various entry widgets, and then sends two queries. The first query is a set-up query, which is used to find the maximum and minimum `X` and `Y` values for the interpolated data. Those values are then stored in a list.

       /*--------------------------------------------------------------------
        * Function:    send_query
        * Returns:     t or nil
        * Description: Calls the necessary functions to send a query to the
        *              Historian, and handles the data returned.  It is activated
        *              by the "Record" button popping out, or any radio button.
        *------------------------------------------------------------------*/
       function send_query(iset, text, parent, ent1, ent2,
                           ent3, ent4, ent5, ent6, button)
       {
         local fnholder, datalist, minmaxlist;

         if ((button == hw.NoIntBut || button == hw.PeriodBut ||
              button == hw.RelBut || button  == hw.FixRelBut ||
              button == hw.DisplayBut || button  == hw.RecBut))
           iset.dbflag = "NONE";

         iset.assign_values(ent1, ent2, ent3, ent4, ent5, ent6);

         datalist = list();
         fnholder = iset.fn;

         /* Assign a name for the setup query. */
         if ((iset.fn == "NoInterpolator") || (iset.fn == "Periodic"))
           iset.fn = "TimeSetup";
         else
           iset.fn = "RelSetup";

There are two possible setups: time and relative, depending on what the X-axis represents. The NoInterpolator and Periodic options plot a single history against time, and thus give time-based output. The Relative and FixedRelative options plot one history against another, and thus require the X-axis to have values ranging from 0 to 200.

To create the largest plot possible in the given space, we need to set the range of our axes to the minimums and maximums of the data set. This information is obtained using the `min_max` function.

         /* Make the setup query and assign minimum and maximum values. */
         datalist = iset.get_data();
         if (car(datalist) && cadr(datalist))
           minmaxlist = list(min_max(car(datalist)), min_max(cadr(datalist)));
         else
           minmaxlist = nil;

         /* Make the actual query. */
         iset.fn = fnholder;
         datalist = iset.get_data();
         show_data(text, iset, parent, button, datalist, minmaxlist);
       }

Both the first and second query bring in their data using the `.get_data` method (below). The second query then shows the actual requested query data using the show_data function.

This method is called by `send_query` to get the current values from the `InterpolatorSettings` class before actually sending a query. It uses the common function `assign_history` to assign the `y_history` and `x_history`.

       /*--------------------------------------------------------------------
        * Method:      InterpolatorSettings.assign_values
        * Returns:     t or nil
        * Description: Assigns the values from the interpolator entry widgets
        *              to the appropriate instance variables of the class.
        *------------------------------------------------------------------*/
       method InterpolatorSettings.assign_values(e1, e2, e3, e4, e5, e6)
       {
         .y_history = assign_history(.dbflag, .y_history, e1.text_string);
         .start = number(e2.text_string);
         .duration = number(e3.text_string);
         .x_history = assign_history(.dbflag, .x_history, e4.text_string);
         .interval = number(e5.text_string);
         .max_gap = number(e6.text_string);
       }

The `.get_data` method of the `InterpolatorSettings` class sends a query to the Cascade Historian, using an interpolate command. The interpolator used is determined by calling the `.set_interpolator` method. The Cascade Historian returns that interpolated query as a list of lists, with each sub-list containing a pair of numbers. The `.get_data` method extracts those numbers and writes them in two columns in the output file. This method then creates and returns a list of two sub-lists, each of which contains all the values for one column. Why bother writing the data to a file when we have it in memory as a list? Because for plotting we need access to the query results of all three deadband options at any given time.

       /*--------------------------------------------------------------------
        * Method:      InterpolatorSettings.get_data
        * Returns:     a list of x-data and y-data, as strings
        * Description: Sends a query to the Historian and receives the data.
        *              Activated by the send_query function.
        *------------------------------------------------------------------*/
       method InterpolatorSettings.get_data()
       {
         local tsk, intpl, buf_length, alldata, count, dur, data, outfile;
         local xdata = list();
         local ydata = list();
         local midnite = find_midnite();

         /* Make sure there are valid values in the entry widgets.*/
         if (undefined_p(.start) || (!number_p(.start)))
           {
             .start = 0;
             hw.StartEnt.text_string = "0";
           }
         if (undefined_p(.duration) || (!number_p(.duration))
             || (.duration <= 0))
           {
             .duration = 10;
             hw.DurEnt.text_string = "10";
           }
         if (undefined_p(.interval) || (!number_p(.interval))
             || (.interval < .01))
           {
             .interval = .01;
             hw.IntEnt.text_string = "0.01";
           }

We test the values entered in three of the entry widgets, using Gamma's `undefined_p` and `number_p` predicate functions.

Next we we write the data to the output file.

         if ((tsk = locate_task("demohistdb", nil)) != nil)
           {
             intpl = self.set_interpolator(tsk);

             buf_length = caddr(send(tsk, `bufferIdLength(intpl)));
             if(!((.fn == "TimeSetup") || (.fn == "RelSetup")))
               hw.NumPts.text_string = string(buf_length);

             alldata = list();
             count = 0;

             while (count < buf_length)
               {
                 if((buf_length - count) > 100)
                   dur = 100;
                 else
                   dur = buf_length - count;
                 data = send(tsk, `bufferIdDataAscii(intpl, @count, @dur));
                 data = cddr(cdr(data));
                 alldata = append(alldata, data);
                 count = count + 100;
               }

Since the interpolated data set can be very large, potentially requiring long processing times, we used the Cascade Historian's bufferIdLength command to calculate the length of the buffer, which then allows us to process it in chunks. The chunks can be any size; we chose a size of 100 values. For each chunk we send a bufferIdDataAscii command to get the values in ASCII format.

             if (outfile = open("/tmp/cogentdemo/hsoutput.dat", "w"))
               if (buf_length == 0)
                 {
                   writec(outfile, "# Sorry, no data matched your query.");
                   writec(outfile, string("\n", midnite, " 0",
                                          "\n", (midnite + 1), " 0"));
                   close(outfile);
                 }
               else
                 {
                   with d in alldata do
                     writec(outfile, string(car(d), " ", cadr(d), "\n"));
                   close(outfile);
                 }

             while (alldata != nil)
               {
                 if(.fn == "TimeSetup" ||
                    .fn == "NoInterpolator" || .fn == "Periodic")
                   xdata = cons(caar(alldata) - midnite, xdata);
                 else
                   xdata = cons(caar(alldata), xdata);
                 ydata = cons(cadar(alldata), ydata);
                 alldata = cdr(alldata);
               }
             xdata = reverse(xdata);
             ydata = reverse(ydata);
             send(tsk, #bufferIdDestroy(intpl));
           }
         list(xdata, ydata);
       }

In addition to the written output file, we also want to have the data available as a list, to be returned to the `send_query` function (above) for further use. After gathering the data for the list, we have no more use for the data buffer, and so destroy it to free up memory, using the Cascade Historian's bufferIdDestroy command. Then we create the data list using the Gamma `list` function, and it becomes the return value of the function.

Prev | Home | Next
Photon: Editing Queries - `.change_int`, `.allow_entry_values` | Up | Photon: Displaying Query Data - `show_data`

Copyright 1995-2002 by Cogent Real-Time Systems, Inc.