Starting Programs - program_startup

Cogent Tools Demo and Tutorials: Version 4.0
Prev | Chapter 9. Common Functions for Any Program (in `lib/common.g`) | Next

# 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");
                 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);
       }

Prev | Home | Next
Common Functions for Any Program (in `lib/common.g`) | Up | Starting qserve and nserve - `start_qnserves`

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