McIDAS Programmer's Manual
Version 2003

[Search Manual] [Table of Contents] [ Go to Previous] [ Go to Next]


System utilities

The McIDAS-X library provides a group of functions for performing system-related tasks such as:

This section provides descriptions and examples of the functions that you can use in your applications to perform these tasks.

 

For additional information about the functions described in this section, see the online man pages provided with the McIDAS-X software.

String tables

A McIDAS-X string table is a collection of names and associated strings. String tables serve two purposes:

 

Users can run the commands TU, TL, TE, TD and REPEAT to manage string tables and simplify command entry. For information about these commands, see the McIDAS-X Learning Guide and the McIDAS User's Guide.

As a programmer, you will find string tables useful because they can serve as a scratch pad for applications to communicate with each other. One application writes information into a character string and saves it in the table where it is available to all subsequent applications, until either the string is changed or the string table is replaced with another.

Use the three functions below to read from or write to a string table.

Fortran function Description

lbget

gets a string from the current string table

lbput

writes a string to the current string table

lbputc

forms a character string from a sequence of character tokens and writes it to the string table

Although no separate operation exists to delete a string name from the string table, you can delete a string name using lbput with an argument string consisting of one or more blank characters.

Note that lbputc takes an input array of tokens and concatenates them. The resulting string is then associated with the string name only if it is less than 160 characters long. The lbput function will associate only a single string with the string name, subject to the same 160-character limit.

Limitations

You must observe the following limitations when developing applications that access string tables.

Cautions

String tables provide convenient, global storage for passing information in and out of applications without using the command line, text display or file I/O. Although the technique for implementing a string table is easy, using string tables for passing information between commands is not always desirable. If the applications using string tables do not provide a facility for reinitializing the strings used, unpredictable results may occur during subsequent use of the applications.

Storing or sharing information using the string table functions can make applications more convenient for users if used sparingly and documented clearly. It can just as easily lead to confusion, since a string name can't be protected for an application's exclusive use. Other applications or the user (via the string table editor TE) can modify or delete the string, causing the application relying on that string to behave unpredictably. To minimize the risk of name collisions in the string table, choose an unlikely name for the stored string.

Use the McIDAS-X disk file system if your application needs to save more than a few pieces of information that will be used by other applications at a later time. Use a text file, whenever practical, as it simplifies the editing and viewing of the file and makes debugging your application easier.

Examples

The code segments below demonstrate the McIDAS-X string table API. Sample user-level input, via the TE and ECHO commands, is also provided to show how string table entries can be written to and read from both the command line and within an application.

Reading a string from a string table

The lbget function extracts the contents of a string from within an application and places it in a character variable. If the string table value is longer than the character variable receiving it, the value stored in the character variable is truncated. For example, a user enters the following command from the McIDAS-X Text and Command Window.

TE HITTER "MICKEY MANTLE

The code segment below reads the contents of the string HITTER.

     character*12 string_name     ! name of McIDAS string to
                                  ! extract value from
     character*24 out_string      ! variable to store resulting 
                                  ! value in

     string_name = 'HITTER'
     ok = lbget (string_name, out_string)

     if (ok .lt. 0)then
        goto 999
     endif

c--- upon successful completion the contents of the variable 
c--- out_string will be 'MICKEY MANTLE'

Writing a string to a string table

The lbput function stores the contents of a character string in the current McIDAS-X string table associated with a specified string name, as shown in the code fragment below.

     character*12 string_name     ! name of McIDAS string to
                                  ! place new value in
     character*24 in_string       ! variable to store resulting 
                                  ! value in

     string_name = 'HITTER'
     in_string   = 'Willie Mays'
     ok = lbput (string_name, in_string)

     if (ok .lt. 0)then
        goto 999
     endif

c--- upon successful completion the contents of the McIDAS
c--- string HITTER will be 'Willie Mays'

If a user subsequently runs the following command from the McIDAS-X Text and Command Window:

ECHO "The Greatest Centerfielder of all time was #HITTER

The output displayed on the text screen will appear as shown below.

The Greatest Centerfielder of all time was Willie Mays

Writing character tokens to a string table

The lbputc function allows a user to enter a group of character tokens into one string. The code fragment below demonstrates this function.

     parameter (MAXTOK = 3)
     character*12 string_name     ! name of McIDAS string to 
                                  ! place new value in
     character*12 tokens(MAXTOK)  ! string tokens to write into
                                  ! the McIDAS string 700CLUB

     string_name = '700CLUB'
     tokens(1)   = 'Hank Aaron'
     tokens(2)   = 'and'
     tokens(3)   = 'Babe Ruth'
     ok = lbputc (string_name, MAXTOK, tokens) 
     if (ok .lt. 0)then
        goto 999
     endif

c--- upon successful completion the contents of the McIDAS
c--- string 700CLUB will be 'Hank Aaron and Babe Ruth'

If a user subsequently runs the following command from the McIDAS-X Text and Command Window:

ECHO "#700CLUB are the two greatest sluggers of all time

The output displayed on the text screen will appear as shown below.

Hank Aaron and Babe Ruth are the two greatest sluggers of all time

User Common

McIDAS-X applications run in an environment consisting of both static and dynamic parts.

User Common contains the dynamic state of the session. All processes, including applications, can read and modify User Common. You will use it in applications to alter the display and make the applications interact with each other in predictable ways.

 

For information about the itrmch function, which provides details about the static part of the display, see the section in this chapter titled Display characteristics. For a detailed listing of the contents of User Common, see the file uc.doc in the McIDAS-X source directory.

Positive and negative User Common

User Common is divided into positive and negative regions. You must understand the differences between the two and use the appropriate region so that your applications will behave predictably. Note that each session running on a workstation has its own private copy of User Common.

Negative User Common is initialized when the process chain starts. All subsequent processes inherit negative User Common and any changes made to it by processes in the chain.

Batch files, McBASI scripts, and commands separated from each other by a semicolon on a single command line all initiate a chain. Because each process chain has its own copy, Negative User Common cannot be changed from outside the process chain and changes to it are not visible except to the owning process chain, including the display.

The following sequence of commands illustrates why both positive (instantaneous) and negative (process-dependent) User Common are necessary and how applications use them to achieve predictable interactive behavior. If the user enters the three commands below:

SF 2
IMGDISP EASTS/CONUS STA=MSN BAND=4
SF 4

The first command sets the current frame to 2; the second command requests that the latest GOES-EAST 4 km IR image, centered on Madison, Wisconsin, be displayed on the current frame. Now suppose the user wants to view a graphic already displayed on frame 4, and enters the third command before IMGDISP begins displaying the image. The display immediately switches to frame 4.

What happens to the IMGDISP command trying to display to the current frame? Is the current frame the frame the user intended (frame 2) or the frame presently displayed (frame 4)? The ambiguity is resolved with positive and negative User Common.

When SF 2 changes the appropriate word in User Common, in this case word 51, to 2, the display immediately reflects it. When IMGDISP starts, the display state, including the current frame number 2, is copied into negative User Common. IMGDISP can then examine the appropriate User Common word, in this case -1, to get the frame number that was current when it started. This value is always 2; whereas the true current frame (word 51) changes from 2 to 4 when SF 4 runs.

User Common API functions

The User Common functions are described in the table below.

C function Fortran function Description

Mcluc

luc

returns a value from User Common

Mcpuc

puc

changes a value in User Common

The most common error in using these functions is specifying the wrong User Common index value; the second most common error is transposing a new value with the User Common index value in puc and Mcpuc.

The m0glue.h include file has many of the User Common indexes enumerated using #define statements.

Several APIs are provided in addition to the basic Mcluc and Mcpuc functions. They are described in the table below.

C function Fortran function Description

McGetGraphicsFrameNumberI

mcgetgraphicsframenumberi

returns the current graphics frame number for an interactive application

McGetImageFrameNumberI

mcgetimageframenumberi

returns the current image frame number for an interactive application

McGetGraphicsFrameNumber

mcgetgraphicsframenumber

returns the current graphics frame number

McSetGraphicsFrameNumber

mcsetgraphicsframenumber

sets the current graphics frame number

McGetImageFrameNumber

mcgetimageframenumber

returns the current image frame

McSetImageFrameNumber

mcsetimageframenumber

sets the current image frame number

McGetMaxImageFrameNumber

mcgetmaximageframenumber

returns the maximum image frame number for the current session

McGetMaxGraphicsFrameNumber

mcgetmaxgraphicsframenumber

returns the maximum graphics frame number for the current session

McIsImageLooping

mcisimagelooping

returns current image looping state

McIsImageFrameOn

mcisimageframeon

returns whether or not the current image frame is visible

McSetImageFrameOn

mcsetimageframeon

sets current image frame to visible

McIsImageConnectedToLoop

mcisimageconnectedtoloop

indicates if the image frames are connected to the looping system

McIsGraphicsLooping

mcisgraphicslooping

returns current graphics looping state

McIsGraphicsFrameOn

mcisgraphicsframeon

returns whether the current graphics frame is visible

McSetGraphicsFrameOn

mcsetgraphicsframeon

sets current graphics frame to visible

McIsGraphicsConnectedToLoop

mcisgraphicsconnectedtoloop

indicates whether the graphics frames are connected to the looping system

McGetStdOutputDevice

mcgetstdoutputdevice

returns the destination device for standard output messages

McGetStdErrorDevice

mcgetstderrordevice

returns the destination device for standard error messages

McGetStdDebugDevice

mcgetstddebugdevice

returns the destination device for standard debug messages

McSetStdOutputDevice

mcsetstdoutputdevice

sets the destination device for standard output messages

McSetStdDebugDevice

mcsetstddebugdevice

sets the destination device for standard debug messages

McSetStdErrorDevice

mcsetstderrordevice

sets the destination device for standard error messages

McIsMcIDASRunning

mcismcidasrunning

indicates whether a McIDAS-X session is active

User Common functions are often used to verify that an item, such as a frame, etc., is within a valid range. The example below, from the MAP command, illustrates the use of one of these functions.

Determining if the current frame is within a valid range

The MAP command fragments below define what the highest numbered image frame is and compares that number to the desired frame to be used in the command.

c--- maximum image frame
     MAX_IMAGE = mcgetmaximageframenumber()
.
.
.
c--- get the current image frame
        def_image_frame = mcgetimageframenumber()

c--- get the user desired frame and check to see if it is
c--- out of range.  Note that the user may not have input
c--- a frame number, in which case the default frame is used.
         status = mccmdint('IMA.GE',1,'Image Frame',def_image_frame,
    &         1,MAX_IMAGE,nfri)
         if( status.lt.0 ) return

Note that mcgetimageframenumber, not mcgetimageframenumberi, determines the current frame. This ensures that the current frame is the one displayed at the time MAP is started, not the frame displayed at the time the above code fragment actually runs.

Restoring the display to its original state

The sample code below performs these three tasks:

Note the use of luc and puc since no other APIs are currently available.

c --- freeze the roam
      roam = luc( 178 )
      call puc( 1, 178 )

c --- connect cursor to mouse
      mouse = luc( 67 )
      call puc( 1, 67 )

<deleted code>

100   continue
      status = mcmoubtn(3, button(1), button(2), line, elem)
      altG = 0
      if(status.eq.2) altG=1
      altQ = 0
      if(status.eq.1) altQ=1

c --- check for program termination
      if( altQ.ne.0 ) then
     call beep(200,100)
           interact = -3
     return
      endif

<deleted code>

      goto 100

<deleted code>

c --- reset the mouse
      call puc( mouse, 67 )

c --- reset the roam
      call puc( roam, 178 )

Starting McIDAS-X commands from applications

When developing an applications program, check to see if a McIDAS-X command performs a needed task. If so, you should start that command from your application rather than duplicating the logic inside your program. If fixes are made to the command, your program will automatically contain the updated information. If keywords are removed from a command, it's your responsibility to make the necessary changes in your calling application.

McIDAS-X commands can run synchronously or asynchronously, with or without extended format.

You can start any McIDAS-X command from an application using the functions defined in the table below.

C function Fortran function Description

not available

keyin

starts a command asynchronously with extended format

not available

skeyin

starts a command synchronously with extended format

Mckeyin

mckeyin

starts a command asynchronously

Mcskeyin

mcskeyin

starts a command synchronously

Use the keyin, mckeyin and Mckeyin functions to start commands asynchronously. For example, the McIDAS-X time scheduler, sked, starts user commands asynchronously. If the scheduler started a long-running command synchronously, other scheduled commands couldn't start until that command finished.

Use the skeyin, mcskeyin and Mcskeyin functions to start commands synchronously. Using these functions, the application stops and will not continue until the specified command is done. For example, a user can run the ERASE command to erase a frame and then run the IMGDISP command to display an image on that frame. If these applications aren't run synchronously, some of the image load could occur before the ERASE command finishes, erasing part of the image.

Starting commands synchronously

The mckeyin and mcskeyin functions expect a single command in McIDAS-X format. The asynchronous version, mckeyin, returns the status of the command if one was started, or an error code if it wasn't started. The synchronous version, mcskeyin, returns the exit status of the started command. The status code returned from mcskeyin is set by calling the function Mccodeset, which is described in the next section titled Error handling.

Below is a code fragment from an application that uses Mcskeyin. The first call attempts to position the cursor at a certain latitude and longitude. If this call returns a successful status, a second call is made to report the position in all the relevant coordinate systems.

 /*
   * if there is exactly one match
   * try to put the cursor there
   */
  if(found==1)
  {
      char command[100];

      sprintf( command, "PC E %f %f DEV=NNN", slat, slon);

      /*
       * if the cursor positioning was successful
       * output the position
       */
      if(Mcskeyin( command )==0) (void)Mcskeyin("E");
  }

Missing Value codes

It is not uncommon to have data reports with missing values. The McIDAS-X system uses specific values that flag parameters as missing. Integer values for code written in C use the macro MCMISSING4 defined in mcidas.h. Integer values for code written in FORTRAN use the value HEX80 stored in hex80.inc.

The McIDAS-X library provides a group of functions for assigning and verifying floating-point missing value codes. The functions are described in the table below.

C function Fortran function Description

McGetMissingDbl

mcgetmissingdbl

returns the missing value code for a 64-bit floating point number

McGetMissingFlt

mcgetmissingreal

returns the missing value code for a 32-bit floating point number

McIsMissingDbl

mcismissingdbl

tests whether a 64-bit floating point number contains the missing value code

McIsMissingFlt

mcismissingreal

tests whether a 32-bit floating point number contains the missing value code

Error handling

Most functions use their return value to inform the calling program of their success or the nature of their failure. So it is with McIDAS-X commands, though in a more general way. When run from the command line, error messages generated by the edest or Mcprintf function are sufficient. These functions are described in the Text messages section earlier in this chapter.

McIDAS-X also allows scripts or McIDAS-X applications to run other applications, and depending upon the results, modify subsequent actions. For example, if you run a command to get data and then want to run a transformation on it, you can abandon the second command if the first one fails to acquire data.

This table describes the functions that an application should use to set an error code.

C function Fortran function Description

Mccodeset

mccodeset

sets the global status to return upon exiting

Mccodeget

mccodeget

returns the current value of the global status

not available

mcabort

sends an error message and exits

Mciniterr

not available

returns a string explaining why Mcinit failed

Some older McIDAS-X applications are not yet modified to use this status facility, so a zero, or successful status may sometimes be returned erroneously. Although Fortran programs that call mcabort return a status of 1, the process that started the command won't know if mcabort was called, or just return or exit. All new McIDAS-X applications are coded to call Mccodeset if they encounter a problem when running. Thus, when writing an application:

Do: Don't:

call edest, call mccodeset, and return a status in a function return code

call mcabort or exit since they are not very informative

Currently, SSEC uses the return codes below for McIDAS-X applications.

Value Definition

0

command ran successfully

1

command failed with an unrecoverable error

2 to 99

command failed with a potentially recoverable error

100 to 126

reserved for locally developed applications

127

error of unknown origin

For example, use a return code of 1 to tell users they entered a command with a syntax error. Use a return code of 2 to indicate the user's request is valid, but the data requested is not available yet.

The code fragment below is from a command that uses the mccodeset function.

    integer ret_val
    character*12 option

c---    get command option

    ok = mccmdstr(' ', 1, ' ', option)
    if (option .ne. 'LIST' .and. option .ne. 'COPY')then
      ret_val = 1
      goto 999
    endif

    :
    :

c---    get data

    ok =  m0ptget (dataset, nsort, sort, nparms, parms, units, 
     &               form, scales, maxbyte, 1)
    if (ok .lt. 0)then
      ret_val = 2
      goto 999
    endif

c---    process data

    :
    :

    ret_val = 0
999 continue
    call mccodeset (ret_val)
    call edest('done',0)
    return
    end

Suspending applications

The mcsleep function suspends an action or application for a specified number of milliseconds. It is most often used for polling, or repetitively checking to see if something has changed.

The term sleep means that an application tells the operating system that it doesn't want to be considered ready to be dispatched for a period of time. The system does not guarantee that the application will be dispatched again exactly when its sleep interval is over because that depends on system load. However, it does guarantee that the application will not wake up again until at least that period of time expires.

For example, the sked application checks the scheduler file every thirty seconds to see if any of its entries should be run. The Mcmoubtn function sleeps for 10 milliseconds if it is trying to detect a change in the cursor position. Even though these intervals are very different, their purpose is the same: to allow other processing to continue without slowing down the system by checking something more often than necessary.

The choice of a time interval is a matter of experimentation since you can't know how fast or busy the system will be. Don't test an application only on very fast machines if it will also be run on slow ones. Factors, such as whether file systems or servers are local, can also affect how long a unit of work takes, which in turn affects your choice of a time interval.

Below is a code fragment showing how long-running processes use the mcsleep function to perform polling. Notice that they always check the system shutdown User Common word (word 194) using the McIsMcIDASRunning function after waking up so they won't continue running when they're no longer needed.

c-- go to sleep for 10 seconds

      call mcsleep(10000)

c-- verify that mcidas is still running

      if mcismcidasrunning ( ).eq.0) then

         return

      endif
      .
      .
      .

File locks

An application sometimes needs exclusive use of a resource, such as one or more words in shared memory or one or more bytes in a file. The lock and unlock functions, shown in the table below, control the exclusive use of a resource, guaranteeing that once the program starts to run, nothing will interfere until it's finished.

C function Fortran function Description

M0lock

lock

blocks another application from using a resource

M0unlock

unlock

releases a resource so another application can use it

For example, assume two programs want to update a record in a file of 100-byte records. The first program wants to change the third byte of the first record; the second program wants to change the fourth byte of that record.

If both programs read the record, change their respective byte, and write the record back out, one of the changes will be lost because both programs read the unmodified record before making their changes.

Using the lock function will prevent this from happening. If each program locks before it reads the record and unlocks after it writes the record, one process will wait for the other to complete before it begins.

Consider the following recommendations when locking and unlocking a resource.

The sample code below is from the file DDESERVF. The lock function is called by the function that updates the file; the unlock function is called after the file is closed so another process can access the lock.

c--- Open the resolver file, and read contents
   istat=volnam('RESOLV.SRV', pathname)
   call lock('RESOLV.SRV')

   open(21, file=pathname, status='old', err=100)
 1  read(21, 2, err=99, end=99) work2
 2  format(A)
c
   ... (real processing goes on here)
99  close(21)
c
c--- set return code based on whether or not the
c--- name was resolved
c
   if (valid.eq.0) then
      m0sxresolv=-1
   else
      m0scresolv=0
   endif

   call unlock('RESOLV.SRV')
100 return
   end

[Search Manual] [Table of Contents] [ Go to Previous] [ Go to Next]