Complete program - phsimple.g

Cogent Tools Demo and Tutorials: Version 4.0
Prev | Chapter 8. Photon Functions (in `phsimple.g`) | Next

# 8.7. Complete program - `phsimple.g`

    /*--------------------------------------------------------------------
     * File:        phsimple.g
     *
     * Description: Cogent Tools Demo Tutorial One for Photon.
     *
     * Functions:
     *              start_qnserves
     *              program_startup
     *              toggle_sym
     *              change_settings
     *              slider_callback
     *              num_callback
     *              accumulate_trends
     *              update_trends
     *              numeric_assign
     *              create_monitor
     *              main
     *------------------------------------------------------------------*/

    /********************************************************
     *                     STARTING UP                      *
     ********************************************************/

    require_lisp("PhotonWidgets.lsp");
    require_lisp("PhabTemplate.lsp");
    PtInit(nil);

    /*--------------------------------------------------------------------
     * Function:    start_qnserves
     * Returns:     t on success, or nil
     * Description: Used to start qserve or nserve, as specified.  Called by
     *              the program_startup() function.
     *------------------------------------------------------------------*/
    function start_qnserves(command, name)
    {
      for(i=0; i<=50; i++)
        {
          if ((_os_ == "Linux") || (_os_ == "QNX4"))
            if ((qnx_name_locate(0, name, 0) == nil))
              {
                system(command);
                usleep(10000);
              }
            else
              {
                i = 50;
                nil;
              }
          else
            if (access(string("/dev/", command), 0) == -1)
              {
                system(command);
                usleep(10000);
              }
            else
              {
                i = 50;
                nil;
              }
        }
    }

    /*--------------------------------------------------------------------
     * Function:    program_startup
     * Returns:     t or nil
     * Description: Starts any necessary Cogent software and the main window.
     *              Called by the main() function.
     *------------------------------------------------------------------*/
    function program_startup (proc_name, queue_name, win_fn)
    {
      local window, tsk;

      /* See if qserve and nserve are running. If not, start them. */
      start_qnserves("qserve", "qserve");
      start_qnserves("nserve", "sc/nserve");

      /* Start interprocess communication. */
      if (init_ipc(proc_name, queue_name, "toolsdemo") == nil)
        error("Could not initialize IPC.");

      /* See if a DataHub is running in the toolsdemo domain.
         If not, start it and a DataHub Viewer. */
      if ((tsk = locate_task ("/dh/toolsdemo", t)) == nil)
        system("datahub -d toolsdemo");
      else
        close_task (tsk);

      if ((pid_dhview = fork()) == 0)
        {
          /* This is the child process. */
          exec ("phdhview", "-d", "toolsdemo");
          /* In case the above exec commands fails, the following will be
           * called, and it's virtually guaranteed not to fail.  Thus in
           * any circumstance a child process gets created.*/
          exec("/bin/true");
        }

      /* Make sure the PID Emulator is not already running. */
      if ((tsk = locate_task ("emul", t)) != nil)
        {
          send(tsk, #exit_program(0));
          close_task (tsk);
        }

      /* Start the PID Emulator. */
      if ((pid_emul = fork()) == 0)
        {
          /* This is the child process. */
          exec ("gamma", "emul");
          exec("/bin/true");
        }

      /* Kill child processes when this program exits. */
      atexit(`kill(pid_dhview, SIGINT));
      atexit(`kill(pid_emul, SIGINT));

      /* Create and register some points with the datahub. */
      usleep(10000);
      SP_001 = register_point (#SP_001);
      PV_001 = register_point (#PV_001);
      MV_001 = register_point (#MV_001);
      AUTO_001 = register_point (#AUTO_001);
      FREQ_001 = register_point (#FREQ_001);
      PID1_Kp = register_point (#PID1_Kp);
      PID1_Ki = register_point (#PID1_Ki);
      PID1_Kd = register_point (#PID1_Kd);
      PROP_001 = register_point (#PROP_001);
      INT_001 = register_point (#INT_001);

      /* Create the main window, and put in a destructor function for GTK */
      window = eval(win_fn);
    }

    /********************************************************
     *                     THE MONITOR                      *
     ********************************************************/

    /*--------------------------------------------------------------------
     * Function:    toggle_sym
     * Returns:     t or nil
     * Description: Toggles a database point between 0 and 1 when a button
     *              is pressed.
     *------------------------------------------------------------------*/
    function toggle_sym(sym)
    {
      set (sym, eval(sym) == 0 ? 1 : 0);
      write_point (sym, eval(sym));
    }

    /*--------------------------------------------------------------------
     * Function:    change_settings
     * Returns:     t or nil
     * Description: Used by the Monitor to change PID settings, and optionally
     *              unclick the Auto button if it is on.  The numeric widgets
     *              won't execute callbacks after they have been changed
     *              programmatically, so we have to write the point using
     *              write_point().
     *------------------------------------------------------------------*/
    function change_settings(auto, n1, v1, n2, v2, n3, v3, n4, v4,
                             n5, v5, n6, v6, msgno, autobutton?)
    {
      if (auto == nil)
        {
          if (AUTO_001 != 0)
            {
              autobutton.onoff_state = 0;
              write_point(#AUTO_001, 0);
            }
        }
      else
        {
          autobutton.onoff_state = 1;
          write_point(#AUTO_001, 1);
          n1.numeric_value = v1;
          write_point(#FREQ_001, v1);
        }

      if (_os_ == "QNX4")
        {
          n2.numeric_value = v2;
          n3.numeric_value = v3;
          n4.numeric_value = v4;
          n5.numeric_value = v5;
          n6.numeric_value = v6;
        }
      else
        {
          n2.numeric_value = v2 * 100;
          n3.numeric_value = v3 * 100;
          n4.numeric_value = v4 * 100;
          n5.numeric_value = v5 * 100;
          n6.numeric_value = v6 * 100;
        }
      write_point(#PID1_Kp, v2);
      write_point(#PID1_Ki, v3);
      write_point(#PID1_Kd, v4);
      write_point(#PROP_001, v5);
      write_point(#INT_001, v6);
    }

    /*--------------------------------------------------------------------
     * Function:    slider_callback
     * Returns:     t or nil
     * Description: Attaches a callback and adds an exception function to
     *              a slider widget.  The callback writes a value to the
     *              datahub point specified, and the exception function
     *              assigns the value of the datahub point to the slider
     *              widget whenever the value of the point changes in the
     *              datahub.
     *------------------------------------------------------------------*/
    function slider_callback(sld, !pnt)
    {
      PtAttachCallback(sld, Pt_CB_SLIDER_MOVE,
                       `write_point(@pnt, ((@sld).gauge_value)));
      add_exception_function(eval(pnt),
                             `(@sld).gauge_value = (@eval(pnt)));
    }

    /*--------------------------------------------------------------------
     * Function:    num_callback
     * Returns:     t or nil
     * Description: Sets up a callback and an exception function for PtNumeric
     *              widgets to deal with sending changed values to and from
     *              the datahub.  Assigns initial values to the widgets.
     *------------------------------------------------------------------*/
    function num_callback(num, !pnt)
    {
      if(_os_ == "QNX4")
        {
          PtAttachCallback(num, Pt_CB_NUMERIC_CHANGED,
                           `write_point(@pnt, ((@eval(num)).numeric_value)));
          pnt = eval(pnt);
          num.numeric_value = eval(pnt);
          add_exception_function(pnt, `(@num).numeric_value = eval(@pnt));
        }
      else
        {

          /* Handle an annoying characteristic in Photon 2 (OS is QNX 6) whose
             PtNumericFloat widget does not work properly in Gamma. Thus we need
             to use a PtNumericInteger widget, and convert back and forth to write
             points and receive exceptions. The widget that corresponds to FREQ_001
             is a PtNumericInteger, which is already an integer.*/

          if (eval(pnt) != #FREQ_001)
            {
              PtAttachCallback(num, Pt_CB_NUMERIC_CHANGED,
                               `write_point(@pnt, ((@eval(num)).numeric_value) / 100.0));
              pnt = eval(pnt);
              num.numeric_value = round(eval(pnt) * 100);
              add_exception_function(pnt, `((@num).numeric_value = round(eval(@pnt) * 100)));
            }
          else
            {
              PtAttachCallback(num, Pt_CB_NUMERIC_CHANGED,
                               `write_point(@pnt, ((@eval(num)).numeric_value)));
              pnt = eval(pnt);
              num.numeric_value = eval(pnt);
              add_exception_function(pnt, `(@num).numeric_value = eval(@pnt));
            }
        }
    }

    /* Assign each DataHub point a property value, which in this case is an
     * array.  These arrays will be used to hold a short history of each point,
     * allowing them to be plotted by the trend widget.
     */
    setprop (#SP_001, #tdata, make_array (0));
    setprop (#MV_001, #tdata, make_array (0));
    setprop (#PV_001, #tdata, make_array (0));

    /*--------------------------------------------------------------------
     * Function:    accumulate_trends
     * Returns:     t or nil
     * Description: Adds new data to property value arrays.  These arrays
     *              are property values for the variables that correspond
     *              to the DataHub points, and are used to update the trend
     *              widget.
     *------------------------------------------------------------------*/
    function accumulate_trends (syms...)
    {
      local data;
      with sym in syms do
        {
          data = getprop (sym, #tdata);
          data[length(data)] = eval (sym);
        }
    }

    /*--------------------------------------------------------------------
     * Function:    update_trends
     * Returns:     t or nil
     * Description: Assigns the most recent arrays to the trend widget.
     *------------------------------------------------------------------*/
    function update_trends (widget, syms...)
    {
      local        tarray = make_array (length(syms)), i = 0, sym;

      with sym in syms do
        tarray[i++] = getprop (sym, #tdata);
      if(_os_ == "QNX4")
        widget.rttrend_data = tarray;
      else
        widget.trend_data = tarray;

      with sym in syms do
        shorten_array (getprop (sym, #tdata), 0);
    }

    /*--------------------------------------------------------------------
     * Function:    numeric_assign
     * Returns:     t or nil
     * Description: Converts DataHub entries to integer values for use in
     *              PtNumericInteger widgets.  This is necessary because the
     *              an irregularity in the Photon 2 PtNumericFloat widget
     *              prevents it from working properly in Gamma.
     *------------------------------------------------------------------*/
    function numeric_assign(num1, num2, num3, num4, num5)
    {
      if(_os_ == "QNX4")
        nil;
      else
        {
          num1.numeric_value = round(read_point(#PID1_Kp) * 100);
          num2.numeric_value = round(read_point(#PID1_Ki) * 100);
          num3.numeric_value = round(read_point(#PID1_Kd) * 100);
          num4.numeric_value = round(read_point(#PROP_001) * 100);
          num5.numeric_value = round(read_point(#INT_001) * 100);
        }
    }

    /*--------------------------------------------------------------------
     * 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");

      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);

      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);

      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);

      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));

      /* 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(20, 250);
      PtRealizeWidget(monitor_win);

      //  PtMainLoop();
      while(t) next_event();

    }

    /*--------------------------------------------------------------------
     * Function:    main
     * Returns:     doesn't return
     * Description: Calls the program_startup() function.
     *------------------------------------------------------------------*/
    function main()
    {
      program_startup("monitor", "monq", #create_monitor());
    }

Prev | Home | Next
QNX 6-specific Function - `numeric_assign` | Up | Tutorial Two

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