8.2. The Monitor Window - create_monitor

   /*--------------------------------------------------------------------
    * Function:    create_monitor
    * Returns:     doesn't return
    * Description: Creates the Monitor window.
    *------------------------------------------------------------------*/
   function create_monitor ()
   {
     local wfile, window, monitor_win, slidesp, progmv, progpv;
     local slideauto, slidedp, numkp, numki, numkd, numprop, numint;
     local butam, butgood, butpoor, butosc, butout, butx;
     
     if (_os_ == "QNX4")
       wfile = PhabReadWidgetFile("phmonitorwin/wgt/Ptmonitor.wgtw");
     else
       wfile = PhabReadWidgetFile("ph2monitorwin/wgt/Ptmonitor.wgtw");
   

All of the windows in this demo and its tutorials were created in the Photon Application Builder ("PhAB" in QNX 4, the "Builder" in QNX 6). These windows and all their child widgets get imported into Gamma using either PhabReadWidgetFile or PhabAttachWidgets. The Monitor window discussed here gets imported using the PhabReadWidgetFile function. Since there are two widget files (one in Photon 1.14 for QNX 4, the other in Photon 2 for QNX 6), we have to choose the appropriate one using an if statement.

Now that we have the widget file, we can create all the window's child widgets in Gamma/Photon using the PhabCreateWidgets function. Then we can look up and name any individual widget, using Gamma/Photon's PhabLookupWidget function.

[Note]

The hash character (#) is a quote operator which protects a symbol from evaluation. We need to use this quote operator because the function takes the symbol as its argument, not the value of the symbol.

     window = PhabCreateWidgets(wfile, nil, nil);
     monitor_win = PhabLookupWidget(window, #Ptmonitor, nil);
     slidesp = PhabLookupWidget(window, #PtSliderSP, nil);
     slideclr = PhabLookupWidget(window, #RtProgressColor, nil);
     numfreq = PhabLookupWidget(window, #PtNum1, nil);
     numkp = PhabLookupWidget(window, #PtNum2, nil);
     numki = PhabLookupWidget(window, #PtNum3, nil);
     numkd = PhabLookupWidget(window, #PtNum4, nil);
     numprop = PhabLookupWidget(window, #PtNum5, nil);
     numint = PhabLookupWidget(window, #PtNum6, nil);
     
     butgood = PhabLookupWidget(window, #PtToggleGood, nil);
     butpoor = PhabLookupWidget(window, #PtTogglePoor, nil);
     butosc = PhabLookupWidget(window, #PtToggleOscillating, nil);
     butout = PhabLookupWidget(window, #PtToggleOOC, nil);
     butam = PhabLookupWidget(window, #PtOnOffButtonAutomode, nil);
     labprop = PhabLookupWidget(window, #PtLabelPropVal, nil);
     progmv = PhabLookupWidget(window, #ProgressMV, nil);
     progpv = PhabLookupWidget(window, #ProgressPV, nil);
     trend1 = PhabLookupWidget(window, #Trend1, nil);
     
     slidesp.gauge_value = read_point(#SP_001);
     slideclr.gauge_value = read_point(#SP_001);
     progmv.gauge_value = read_point(#MV_001);
     progpv.gauge_value = read_point(#PV_001);
     butam.onoff_state = read_point(#AUTO_001);
     numfreq.numeric_value = read_point(#FREQ_001);
   
     numeric_assign(numkp, numki, numkd, numprop, numint);
     

Once the widgets are named, they can be manipulated. Here we assign values to the slider and progress bars. (The slider was created using two widgets: a transparent slider, and a progress bar. Each of these widgets needs to be assigned a value.) The values being assigned are the values of corresponding points in the Cascade DataHub, collected by using the Gamma read_point function. The numeric_assign function was created to convert values in QNX 6 from hundredths to integers.

These assignments give the widgets an initial value, but the widgets also need to be updated every time the corresponding point in the Cascade DataHub changes its value. For this we use the Gamma functions add_exception_function and add_echo_function. These function lets you specify another function that will be called whenever the Cascade DataHub point changes value. In this case, our function is simply an assignment that sets the value of a widget's .onoff_state or .gauge_value.

     add_exception_function(#AUTO_001, `(@butam).onoff_state = AUTO_001);
     add_exception_function(#SP_001, `(@slideclr).gauge_value = SP_001);
     add_echo_function(#SP_001, `(@slideclr).gauge_value = SP_001);
     add_exception_function(#MV_001, `(@progmv).gauge_value = MV_001);
     add_exception_function(#PV_001, `(@progpv).gauge_value = PV_001);
[Note]

Notice the use of the quote operators ` and @ in the above function calls. The ` operator prevents the assignment from taking place when the code is being read, but the @ operator allows the name of the widget to be evaluated, so that its instance variable can be changed. The # operator is used in the first argument of the function to protect the Cascade DataHub point name from evaluation, but is not needed when making the assignment because there we want the value of the point.

     
     slider_callback (slidesp, #SP_001);
     
     num_callback (numfreq, #FREQ_001);
     num_callback (numkp, #PID1_Kp);
     num_callback (numki, #PID1_Ki);
     num_callback (numkd, #PID1_Kd);
     num_callback (numprop, #PROP_001);
     num_callback (numint, #INT_001);
     

In addition to getting data from the Cascade DataHub, we also need to write data back to it. To get values from the widgets into the Cascade DataHub, we use callbacks. The slider_callback and num_callback functions (above) assign callbacks and add exception functions for the slider and all the spin buttons. The radio buttons get their callbacks attached individually (below). Their callbacks activate the change_settings function, which changes the settings of the spin buttons, as well as writing new values to the Cascade DataHub.

     PtAttachCallback(butgood, Pt_CB_ACTIVATE,
                      `change_settings(t, @numfreq, 4, @numkp, .05,
                                       @numki, .45, @numkd, .05,
                                       @numprop, .7, @numint, 1.0,
                                       3.8, @butam));
     
     PtAttachCallback(butpoor, Pt_CB_ACTIVATE,
                      `change_settings(t, @numfreq, 10, @numkp, .05,
                                       @numki, .70, @numkd, .05,
                                       @numprop, .7, @numint, 5.0,
                                       3.8, @butam));
     
     PtAttachCallback(butosc, Pt_CB_ACTIVATE,
                      `change_settings(nil, @numfreq, 10, @numkp, .05,
                                       @numki, 1.37, @numkd, .05,
                                       @numprop, .7, @numint, .54,
                                       3.8, @butam));
     
     PtAttachCallback(butout, Pt_CB_ACTIVATE,
                      `change_settings(nil, @numfreq, 10, @numkp, .05,
                                       @numki, .45, @numkd, 1.00,
                                       @numprop, .7, @numint, .54,
                                       3.8, @butam));
     
     PtAttachCallback(butam, Pt_CB_ONOFF_NEW_VALUE, `toggle_sym(#AUTO_001));
     
     butx = PhabLookupWidget(window, #Ptxbut, nil);
     PtAttachCallback(butx, Pt_CB_ACTIVATE, #exit_program(-1));
     

The last noteworthy task is to set up the trend widget, using the Gamma array function, and then animate it using the accumulate_trends and update_trends functions.

     /* Set up the trend widget.  We must set the trend resources in the
      * order below to ensure that the trend widget knows what to do with
      * the line colors.  We want 3 traces, in the same colors that the
      * designer set when creating the slider and progress bars in the
      * Photon App Builder.
      */
     trend1.trend_count = 3;
     trend1.trend_color_list = array (slideclr.progress_bar_color,
   				   progmv.progress_bar_color,
   				   progpv.progress_bar_color);
     trend1.trend_attributes = array (1, 2, 3);
     
     /* Animate trends every tenth of a second.  The trend data arrays are
      * stored in the property lists of the associated database variables.
      * We can accumulate trend data faster than we display it by changing
      * the rates of accumulation and update.  It does not make sense to display
      * trend data faster than about 27Hz, because the eye cannot distinguish
      * anything beyond that from smooth motion.  In reality, 10Hz is plenty.
      */
     every (0.1, #accumulate_trends (#SP_001, #MV_001, #PV_001));
     every (0.1, #update_trends (trend1, #SP_001, #MV_001, #PV_001));
     
     monitor_win.SetPos(360, 10);
       PtRealizeWidget(monitor_win);
     send_message("nsnames");
     
   //  PtMainLoop();
     while(t) next_event();
     
   }
   
   /*--------------------------------------------------------------------
    * Function:    main
    * Returns:     
    * Description: 
    *------------------------------------------------------------------*/
   function main()
   {
     /* Get access to the library of common functions. */
     require("lib/common.g");
   
     program_startup("monitor", "monq", #create_monitor(), "3");
   }