9.2. Starting Programs - program_startup

This function is called by the main function of all the Gamma demo programs. It handles the start-up tasks common to all programs, and a few special tasks as necessary. For arguments, it takes the program's process name and queue name as strings (proc_name and queue_name), a function that starts the program's main window (win_fn), and an information message about the program (msg), passed as a string.

   /*--------------------------------------------------------------------
    * Function:    program_startup
    * Returns:     t or nil
    * Description: Starts any necessary Cogent software and the main window
    *              of the Controller, Monitor, Log, and History programs,
    *              as specified by the proc_name and queue_name arguments.
    *------------------------------------------------------------------*/
   function program_startup (proc_name, queue_name, win_fn, msg)
   {
     local window, tsk;
   

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

     /* 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 and do a few tasks specific to the Controller.

     /* See if the Cascade DataHub is installed and running in the toolsdemo domain.
        If not installed, send a message; if not running, start it up. */
     if (proc_name == "control")
       {
         if (find_on_path("datahub"))
           {
             if ((tsk = locate_task ("/dh/toolsdemo", t)) == nil)
               system("datahub -d toolsdemo -p 4600");
             else
               close_task (tsk);
           }
         else
           {
             after (1, `anygui_makemsg(string("This demo requires the Cascade DataHub,\n",
                                              "which apparently is not installed on your",
                                              " system.\n\nYou can download the Cascade",
                                              " DataHub from the Cogent Web Site\n",
                                              "at http://www.cogent.ca/Software/DataHub",
                                              "_Software.html")));
           }

The find_on_path common function checks to see if the datahub command is available on the system. If not, the abstracted anygui_makemsg function puts up a message window for the user after one second (using the Gamma after timer function). If the datahub command is available, the Gamma locate_task function queries nserve to see if an instance of the datahub is already 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.

Another Controller-specific job is to check for tasks starting or dying. When a task starts or dies, we need to send the information to the Process Status text widget. The Gamma add_hook function lets us add our started_died_hook function for that purpose.

         /* Add hooks for when tasks die, for the Process Status display. */
         add_hook (#taskstarted_hook, #started_died_hook);
         add_hook (#taskdied_hook, #started_died_hook);
         
         /* Prepare a new data files directory, and remove it when the Controller exits.*/
         system("rm -fr /tmp/cogentdemo/");
         make_datadir();     
         atexit(#system("rm -fr /tmp/cogentdemo/"));
       }
         

Finally, we need a temporary directory (/tmp/cogentdemo/) to store data from the Log and History programs. Here we first remove any previously-existing data that might be left over from a crash, and call the make_datadir function to create a new one. We use Gamma's atexit function to remove it when the Controller exits.

The next three sections of this function are specific to the Monitor, History, and Log programs. When one of these programs calls this function, the program is identified by checking the proc_name parameter with an if statement and runs the specific code. In the first section, the Monitor program needs to register some point names with the Cascade DataHub on startup.

     /* Create and register with the datahub some of the points for the Monitor. */
     if (proc_name == "monitor")
       {
         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);
       }
   

Next, the History program automatically starts a copy of the Cascade Historian named demohistdb. To avoid any possible conflicts, we check the process name to make sure that there is no Cascade Historian currently running with that name, then we start it up, using the start_process function.

     /* For use by the History program only.  Check to see if the Cascade Historian
        is installed, kill any running instances of it that are named "demohistdb",
        and then start it up.  Also create a histories/ directory to hold the data,
        if one doesn't already exist.*/
     if (proc_name == "history")
       {
         if (find_on_path("histdb"))
           {
             if ((tsk = locate_task("demohistdb", nil)) != nil)
               {
                 send_string(tsk, "(exit)");
                 close_task (tsk);
               }
             start_process(nil, "histdb", "demohistdb",        
                           list("-n", "demohistdb", "-d", "toolsdemo",
                                "-f", "hist.cfg", "-D", "-q", "demohistq"));
           }
         else
           {
             after (1, `anygui_makemsg(string
                                       ("To work correctly, this part of the demo requires",
                                        " the Cascade Historian,\nwhich apparently is not",
                                        " installed on your system.\n\nYou can download",
                                        " the Cascade Historian from the Cogent Web Site\n",
                                        "at http://www.cogent.ca/Software/Historian",
                                        "_Software.html")));
           }
         if(!is_file("/tmp/cogentdemo/histories/"))
           mkdir("/tmp/cogentdemo/histories", 0o777);
       }
     
     

Finally, for the Log program, we need to kill any running copies of the Cascade TextLogger. But we don't actually start the Cascade TextLogger; the Log program does that at the user's request to start logging.

     /* If necessary, kill any active TextLoggers named "tlog". */
     if ((proc_name == "log") && ((tsk = locate_task("tlog", nil)) != nil))
       {
         send_string(tsk, "(exit)");
         close_task (tsk);
       }
   
   

Then there are a few things that need to be set up to handle child processes. First, we use the Gamma atexit function to call the stop_processes function, which lets us stop all child processes belonging to the current program before it exits.

     /* Make sure all processes stop when this program exits. */
     atexit(`stop_processes());
     

We also need to use the Gamma signal function to call the anygui_sigchild function whenever a child process of the current program terminates. This lets us set up a single mechanism for handling the termination of processes, whether they terminate from within the program, or independently.

     /* Set up handling for death of sub-processes. */
     signal(SIGCHLD, `anygui_sigchild());
   

Before starting up, the program sends a message to the Controller's text widget to give some general information to the user. This is done with the send_message function.

     /* Display program info in the Controller text widget. */
     send_message(msg);
   

Finally, we create the main window for the program. Programs in GTK need a destructor function, which we wrap in the abstracted function anygui_destroyer to give a parallel (dummy) function in QNX.

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