6.2. Starting Programs - program_startup

This function is called by the main function of both gtksimple.g and phsimple.g. It handles the necessary start-up tasks for those programs. For arguments, it takes the program's process name and queue name as strings (proc_name and queue_name), and a function that starts the program's main window (win_fn).

[Note]

This function is an adaptation of the common function program_startup explained in Tutorial Two.

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

The first task is to call the start_qnserves function to start the Cogent tools that support interprocess communication: qserve and nserve.

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

Once qserve and nserve are running, the we can initiate interprocess communication (IPC) using the Gamma init_ipc function:

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

For interprocess communication, each program needs a name to be used by nserve (proc_name); and a queue name to be used by qserve (queue_name), which are passed in as function arguments. The program also is assigned a domain (toolsdemo), to keep the demo program and its datahub points separate from other Cogent processes that may be running on the system.

Now that IPC is running, we can start the Cascade DataHub.

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

The Gamma locate_task function queries nserve to see if a datahub is running in the toolsdemo domain. If there is a datahub in that domain, the program uses it and closes the task with the Gamma close_task function. If no datahub is running, the program starts one using the Gamma system function.

We next use the Gamma functions fork to spawn child processes, and exec to start the DataHub Viewer and PID Emulator. The Gamma princ function is used to print messages to the console.

     if ((pid_dhview = fork()) == 0)        
       {
         /* The child process is the DataHub Viewer. */
         exec ("dhview", "-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");
       }
     princ("The process ID of the DataHub Viewer is: ", pid_dhview, "\n");
   
     /* 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)        
       {
         /* The child process is the PID Emulator. */
         exec ("gamma", "emul");      
         exec("/bin/true");
       }
     princ("The process ID of the PID Emulator is: ", pid_emul, "\n");
     

To terminate the PID Emulator and DataHub Viewer when the program exits, we use the Gamma functions atexit and kill.

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

The Monitor needs to register some point names with the Cascade DataHub on startup, using the Gamma register_point function.

     /* Create and register some points with the datahub. */
     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);
     

Finally, we create the main window for the program. The GTK version of this program needs a destroy signal, but Photon does not, so the final line in this function only appears in gtksimple.g.

     /* Create the main window, and put in a destructor function for GTK */
     window = eval(win_fn);
     window.signal ("destroy", #exit_program(0));
   }