/************************************************************************/
/* Copyright 1992 MBARI							*/
/************************************************************************/
/* $Header: /usr/tiburon/unix/gui/proto/RCS/alarm.c,v 1.1 1998/01/28 16:17:12 oreilly Exp $		*/
/* Summary  : Routines to Manage Alarms for ROV User Interface		*/
/* Filename : alarm.c							*/
/* Author   : Bob Herlien (rah)						*/
/* Project  : New ROV							*/
/* $Revision: 1.1 $							*/
/* Created  : 11/07/92							*/
/************************************************************************/
/* Modification History:						*/
/* $Log: alarm.c,v $
 * Revision 1.1  1998/01/28 16:17:12  oreilly
 * Initial revision
 *
 * Revision 2.2  96/05/15  11:15:39  11:15:39  bobh (Bob Herlien)
 * Still supporting proto during final vehicle build
 * 
 * Revision 2.0  95/09/20  14:23:07  14:23:07  hebo (Bob Herlien)
 * Support for final vehicle integration.  AC, PDU micros.
 * 
 * Revision 1.8  94/08/19  17:20:28  17:20:28  hebo (Bob Herlien)
 * Interface to Unix version of Data Manager
 * 
 * Revision 1.7  94/03/21  14:15:22  14:15:22  hebo (Bob Herlien)
 * Misc bug fixes and enhancements
 * 
 * Revision 1.6  93/12/17  15:05:06  15:05:06  hebo (Bob Herlien)
 * December Wet Test
 * 
 * Revision 1.5  93/07/08  18:11:01  18:11:01  hebo (Bob Herlien)
 * Prior to Prototype Phase I testing
 * 
 * Revision 1.4  93/05/27  15:48:07  15:48:07  hebo (Bob Herlien)
 * Data Manager Interface
 * 
 * Revision 1.2  93/01/21  10:34:22  10:34:22  hebo (Bob Herlien)
 * Changes after Level 0
 * 
 * Revision 1.0  92/11/20  12:03:30  12:03:30  hebo (Bob Herlien)
 * Initial revision
 * 
*/
/************************************************************************/

#include <mbari/types.h>
#include <mbari/const.h>
#include <Xm/Xm.h>
#include <X11/Intrinsic.h>
#include <X11/Xlib.h>
#include <Xm/Label.h>
#include <Xm/MessageB.h>
#include <Xm/SelectioB.h>
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include "rov.h"
#include "tiburon.h"
#include "alarm.h"
#include "rdmif.h"
#include <datamgr.h>
#include <microdef.h>
#include <ibc_card.h>
#include <time.h>
#include <microDm.h>
#include <gf5vDef.h>
#include <moogdef.h>

#define PRINT_ALARMS		FALSE

#define ALARM_LIST_STRLEN	8192
#define MAX_ALARM_DIALOG_LIST	1024
#define PEER_STRING_SIZE	1024
#define PEER_NAMESIZE		32


/********************************/
/*	External Functions	*/
/********************************/

Extern Widget	rovError( Widget parent, char *msg );


/********************************/
/*	External Data		*/
/********************************/

Extern XtAppContext	appcontext;	/* The application context	*/
Extern AppData		appdata;	/* Application-wide resources	*/
Extern Widget		toplevel;	/* The top-level shell		*/
Extern Widget		topFormW;	/* Top-level form		*/
Extern Widget		alarmWindow;	/* Scrolled window for alarm list*/
Extern Widget		alarmList;	/* Alarm List Label Widget	*/
Extern char		buff[];		/* Misc buffer used application-wide*/
Extern alarmDesc	*alarms[];	/* Alarm descriptor ptr		*/
Extern Nat32		nalarmDesc;	/* Number of descs in alarms[]	*/


/********************************/
/*	Global Data		*/
/********************************/

Global char		*ibcAlarmStrings[] = { "OK", "Warning", "Alarm" };
Global char		*gfAlarmStrings[] =  { "OK", "Advisory", "Warning", 
						"Critical" };
Global char		*gfTestStrings[] =  { "OK", "Failed On", "Failed Off" };

Global char		*gf5vTestStrings[] = 
{ "Selftest not run", "A to D Failed", "GF Sense Test Failed",
  "Selftest Passed" };

Global char		*motorStatus[] = 
{ "Unknown", "OK", "Control Voltage Alarm", "Temperature Error", "Stalled",
  "Resolver Failure", "OverCurrent", "Misc Fault" };

#if PRINT_ALARMS	 
Void	printAlarmStrings( AlarmFuncPtr func );
#endif


/********************************/
/*	Module Local Data	*/
/********************************/

MLocal XtIntervalId	timeoutId;
MLocal alarmDialogStruct alarmDialogs[MAX_ALARMS];
MLocal char		alarmListString[ALARM_LIST_STRLEN];
MLocal Widget		alarmClipWindow, alarmEnableDialog;
MLocal Pixel		alarmoff_fg, alarmoff_bg;
MLocal alarmCallback	*cbList = NULLACB;
MLocal XmString		alarmDialogStrings[MAX_ALARM_DIALOG_LIST];
MLocal FILE		*alarm_fp = (FILE *)NULL;
MLocal char		*nullstr = "";


/************************************************************************/
/* Function    : dialogDismissCallback					*/
/* Purpose     : Callback when user dismisses Alarm Dialog		*/
/* Inputs      : Widget ID, alarmStruct ptr, CallbackStruct pointer	*/
/* Outputs     : None							*/
/************************************************************************/
    Void
dialogDismissCallback( Widget w, alarmDialogStruct *adp, 
		      XmAnyCallbackStruct *cp )
{
    if ( adp->alarm != NULLA )
	adp->alarm->dialog = NULLAD;
    adp->alarm = NULLA;

} /* dialogDismissCallback() */


/************************************************************************/
/* Function    : dialogSilenceCallback					*/
/* Purpose     : Callback when user presses "Silence" button on Alarm Dialog*/
/* Inputs      : Widget ID, alarmStruct ptr, CallbackStruct pointer	*/
/* Outputs     : None							*/
/************************************************************************/
    Void
dialogSilenceCallback( Widget w, alarmDialogStruct *adp, 
		      XmAnyCallbackStruct *cp )
{
    if ( adp->alarm != NULLA )
    {
	adp->alarm->dialog = NULLAD;
	adp->alarm->silence = TRUE;
    }
    adp->alarm = NULLA;

} /* dialogSilenceCallback() */


/************************************************************************/
/* Function    : getDialog						*/
/* Purpose     : Get an unused Alarm Dialog				*/
/* Inputs      : None							*/
/* Outputs     : Ptr to alarmDialogStruct that was used			*/
/************************************************************************/
     alarmDialogStruct *
getDialog( Void )
{
    Reg int			i;
    Reg Widget			w;
    Reg alarmDialogStruct	*adp;
    XmString			str, str1, str2;

    for ( i = 0; i < MAX_ALARMS; i++ )
    {
	adp = &alarmDialogs[i];
	if ( adp->alarm == NULLA )
	{
	    if ( adp->widget == NULLW )
	    {
		w = XmCreateWarningDialog(topFormW, "alarmDialog", 
					  (ArgList)NULL, 0);
		XtUnmanageChild(XmMessageBoxGetChild(w, XmDIALOG_HELP_BUTTON));
		str = XmStringCreateSimple( "ALARM" );
		str1 = XmStringCreateSimple( "Dismiss" );
		str2 = XmStringCreateSimple( "Silence" );
		XtVaSetValues( w, XmNdialogTitle, str,
			      XmNokLabelString, str1,
			      XmNcancelLabelString, str2,
			      XmNdefaultPosition, False, 
			      XmNx, appdata.alarm_xpos + i * appdata.alarm_xinc,
			      XmNy, appdata.alarm_ypos + i * appdata.alarm_yinc,
			      NULL );
		XmStringFree( str );
		XmStringFree( str1 );
		XmStringFree( str2 );
		XtAddCallback( w, XmNokCallback,
			      (XtCallbackProc)dialogDismissCallback, adp );
		XtAddCallback( w, XmNcancelCallback,
			      (XtCallbackProc)dialogSilenceCallback, adp );
		adp->widget = w;
	    }

	    XtVaSetValues( adp->widget, 
			   XmNdefaultButtonType, XmDIALOG_OK_BUTTON, NULL );
	    return( adp );
	}
    }

    return( NULLAD );

} /* getDialog() */


/************************************************************************/
/* Function    : assertDialog						*/
/* Purpose     : Put up an Alarm warning dialog				*/
/* Inputs      : Alarm Struct ptr, Message String			*/
/* Outputs     : Ptr to alarmDialogStruct that was used			*/
/************************************************************************/
     Void
assertDialog( alarmStruct *asp, char *msg )
{
    XmString	str;
    Widget	w;

    if ( asp->dialog == NULLAD )
	if ( (asp->dialog = getDialog()) == NULLAD )
	    return;
    asp->dialog->alarm = asp;
    w = asp->dialog->widget;
    str = XmStringCreateSimple( msg );
    XtVaSetValues( w, XmNmessageString, str, NULL );
    XmStringFree( str );
    XtManageChild( w );

    if ( appdata.ring_bell )
	XBell( XtDisplay(w), 100 );
    
} /* assertDialog() */


/************************************************************************/
/* Function    : displayAlarmList					*/
/* Purpose     : Display alarmListString in the alarmList widget	*/
/* Inputs      : None							*/
/* Outputs     : None							*/
/************************************************************************/
     Void
displayAlarmList( Void )
{
    XmString	str;
    Pixel	fg, bg;
    Int32	i;

    fg = appdata.alarmon_fg;
    bg = appdata.alarmon_bg;
    
    if ( (i = strlen(alarmListString)) == 0 )
    {
	strcpy( alarmListString, "None" );
	fg = alarmoff_fg;
	bg = alarmoff_bg;
    }
    else if ( alarmListString[i-1] == '\n' )
	alarmListString[i-1] = '\0';		/* Remove trailing '\n'	*/

    str = XmStringCreateLtoR(alarmListString, XmSTRING_DEFAULT_CHARSET);

    XtVaSetValues( alarmList, XmNlabelString, str,
		   XmNforeground, fg, XmNbackground, bg, NULL );

    XtVaSetValues( alarmClipWindow, XmNbackground, bg, NULL );

    XmStringFree(str);

} /* displayAlarmList() */


/************************************************************************/
/* Function    : logAlarm						*/
/* Purpose     : Log Alarm to Log file					*/
/* Inputs      : alarmStruct, alarmCan, alarmItem			*/
/* Outputs     : None							*/
/************************************************************************/
     Void
logAlarm( alarmStruct *asp, alarmDesc *adp, alarmCan *acp, alarmItem *aip )
{
    struct timeval	logtime;
    struct tm		*log_tm;

    if ( alarm_fp == (FILE *)NULL )
	return;

    gettimeofday( &logtime, (struct timezone *)NULL );
    log_tm = localtime( (time_t *)&logtime.tv_sec );

    fprintf( alarm_fp, "%02d/%02d/%02d %02d:%02d:%02d.%01d ",
	     log_tm->tm_year, log_tm->tm_mon + 1, log_tm->tm_mday,
	     log_tm->tm_hour, log_tm->tm_min, log_tm->tm_sec,
	     logtime.tv_usec/100000 );

    if ( adp->usePwrName )
	fprintf( alarm_fp, TIBURON_DM_PREFIX "POWER.%s.%s",
             acp->canDmName, aip->dmName );
    else
	fprintf( alarm_fp, "%s%s", acp->canDmName, aip->dmName );

    fprintf( alarm_fp, " = %d  ", asp->dmValue );

    fprintf( alarm_fp, "%s %s %s\n", acp->canAlarmName, aip->alarmString,
	     asp->status);

    fflush( alarm_fp );

} /* logAlarm() */


/************************************************************************/
/* Function    : getAlarms						*/
/* Purpose     : Get Alarm Status for one Alarm Group			*/
/* Inputs      : alarmDesc pointer, alarms to check			*/
/* Outputs     : None							*/
/************************************************************************/
     Void
getAlarms( Reg alarmDesc *adp, DWord changes )
{
    Reg Nat32		i, j;
    Reg alarmStruct	*asp;
    Reg alarmItem	*aip;
    MBool		assert;

    asp = adp->alarms;

    for ( i = 0; i < adp->nCanNames; i++ )
	for ( j = 0; j < adp->nAlarmItems; j++, asp = asp->next )
	    if ( (changes & asp->dmGroupBit) && (asp->dmitem != NO_ITEM) )
	    {
		aip = &adp->alarmItems[j];
		if ( (*aip->alarmFunc)(asp, &assert, aip->alarmParm) )
		{
		    if ( aip->dmType != DM_EMPTY )
			adp->alarmsOn |= asp->dmGroupBit;

		    if ( assert && !(asp->silence) )
		    {
			sprintf( buff, "%s %s %s", 
				 adp->alarmCans[i].canAlarmName,
				 aip->alarmString, asp->status);
			assertDialog( asp, buff );
		    }
		    if ( assert )
			logAlarm( asp, adp, &adp->alarmCans[i], aip );
		}
		else
		    adp->alarmsOn &= ~asp->dmGroupBit;
	    }

} /* getAlarms() */


/************************************************************************/
/* Function    : WriteAlarmList						*/
/* Purpose     : Write Alarms to the alarmListString			*/
/* Inputs      : alarmDesc pointer					*/
/* Outputs     : None							*/
/************************************************************************/
     Void
writeAlarmList( Reg alarmDesc *adp )
{
    Reg Nat32		i, j, n;
    Reg alarmStruct	*asp;

    asp = adp->alarms;
    n = strlen( alarmListString );

    for ( i = 0; i < adp->nCanNames; i++ )
	for ( j = 0; j < adp->nAlarmItems; j++, asp = asp->next )
	    if ( adp->alarmsOn & asp->dmGroupBit )
		n += sprintf( alarmListString + n, "%s %s %s\n", 
			      adp->alarmCans[i].canAlarmName, 
			      adp->alarmItems[j].alarmString, asp->status);

} /* writeAlarmList() */


/************************************************************************/
/* Function    : alarmTimeoutProc					*/
/* Purpose     : Routine called to periodically check alarms		*/
/* Inputs      : None							*/
/* Outputs     : None							*/
/* Comment     : Typically called at 2 Hz.  Be quick!			*/
/************************************************************************/
	Void
alarmTimeoutProc( XtPointer client_data, XtIntervalId *id )
{
    Reg DWord		changes;
    Reg alarmCallback	*acp;
    Reg Nat32		i;
    Reg MBool		gotchange;

    gotchange = FALSE;
    for ( i = 0; i < nalarmDesc; i++ )
	if ( (changes = rdm_get_group_changes(alarms[i]->groupId)) != 0 )
	{
	    getAlarms( alarms[i], changes );
	    gotchange = TRUE;
	}

    if ( gotchange )
    {
	alarmListString[0] = '\0';
	for ( i = 0; i < nalarmDesc; i++ )
	    writeAlarmList( alarms[i] );
	displayAlarmList();

	for ( acp = cbList; acp != NULLACB; acp = acp->cb_next )
	    (*acp->cb_func)(acp->cb_data);
    }

    timeoutId = XtAppAddTimeOut(appcontext, appdata.alarm_update,
				alarmTimeoutProc, NULL);

} /* alarmTimeoutProc() */


/************************************************************************/
/* Function    : createAlarms						*/
/* Purpose     : Create an alarm group, allocate alarmStruct's		*/
/* Inputs      : alarmDesc pointer					*/
/* Outputs     : Bit vector of all non-zero length DM items		*/
/************************************************************************/
    DWord
createAlarms( alarmDesc *adp )
{
    Reg Nat32		i, j;
    Reg alarmStruct	*asp, **asp_link;
    Reg alarmItem	*aip;
    Reg DWord		rtn;
    Nat16		val;

    if ( (adp->groupId = rdm_create_group(adp->alarmCans[0].canAlarmName))
			== NO_GROUP )
	return( 0 );

    asp_link = &adp->alarms;
    rtn = (DWord)0;

    for ( i = 0; i < adp->nCanNames; i++ )
	for ( j = 0; j < adp->nAlarmItems; j++ )
	{
	    asp = (alarmStruct *)XtCalloc( 1, sizeof(alarmStruct) );
	    *asp_link = asp;
	    asp_link = &asp->next;
	    asp->next = NULLA;
	    aip = &adp->alarmItems[j];
	    if ( adp->usePwrName )
		sprintf( buff, TIBURON_DM_PREFIX "POWER.%s.%s",
                     adp->alarmCans[i].canDmName, aip->dmName );
	    else
		sprintf( buff, "%s%s", adp->alarmCans[i].canDmName, 
			 aip->dmName );

	    rdm_create( buff, 1, &asp->dmitem, aip->dmType, 1 );

#if PRINT_ALARMS	 
	    printf( "%s  \"%s %s", buff, adp->alarmCans[i].canAlarmName,
				 aip->alarmString);
	    printAlarmStrings( aip->alarmFunc );
	    printf( "\"\n" );
#endif

	    if ( asp->dmitem != NO_ITEM )
	    {
		rdm_start_consumer(asp->dmitem, 
				   1000 * appdata.alarm_update, SEM_NULL);
		rdm_group_add_item(adp->groupId, asp->dmitem, &asp->dmGroupBit);
		if ( aip->dmType != DM_EMPTY )
		    rtn |= asp->dmGroupBit;

/* For debugging only */
		if ((appdata.debug & PWR_DEBUG) && (aip->dmType == DM_NAT16))
		{	
		    val = aip->alarmParm;
		    rdm_write_once( asp->dmitem, (char *)&val, sizeof(Nat16) );
		}
	    }
	    asp->dialog = (alarmDialogStruct *)NULL;
	    asp->silence = (appdata.debug & SILENCE_ALARMS) ? TRUE : FALSE;
	    asp->dmValue = aip->alarmParm;
	}

    return( rtn );

} /* createAlarms() */


/************************************************************************/
/* Function    : alarmAddCallback					*/
/* Purpose     : Add a Callback when alarms change			*/
/* Inputs      : Function to call, data to call with			*/
/* Outputs     : alarmCallback ID					*/
/************************************************************************/
     alarmCallback *
alarmAddCallback( FuncPtr func, XtPointer client_data )
{
    Reg alarmCallback	*acp, **link;

    if ((acp = (alarmCallback *)XtCalloc(1, sizeof(alarmCallback))) != NULLACB)
    {
	acp->cb_next = NULLACB;
	acp->cb_func = func;
	acp->cb_data = client_data;

	for ( link = &cbList; *link != NULLACB; link = &(*link)->cb_next )
	    ;

	*link = acp;
    }

    return( acp );

} /* alarmAddCallback() */


/************************************************************************/
/* Function    : alarmRemoveCallback					*/
/* Purpose     : Remove an Alarm Callback				*/
/* Inputs      : Alarm Callback ID					*/
/* Outputs     : None							*/
/************************************************************************/
     Void
alarmRemoveCallback( alarmCallback *acp )
{
    Reg alarmStruct	*asp;
    Reg alarmCallback	**link;

    for ( link = &cbList; *link != NULLACB; link = &(*link)->cb_next )
	if ( *link == acp )
	{
	    *link = acp->cb_next;
	    XtFree( (char *)acp );
	    return;
	}

} /* alarmRemoveCallback() */


/************************************************************************/
/* Function    : getAlarmStruct						*/
/* Purpose     : Find the Alarm Struct for a given DM item		*/
/* Inputs      : Data Manager item					*/
/* Outputs     : alarmStruct ptr					*/
/************************************************************************/
     alarmStruct *
getAlarmStruct( DM_Item item )
{
    Reg alarmStruct	*asp;
    Reg alarmDesc	*adp;
    Reg Nat32		i, j, k, n;

    for ( i = 0; i < nalarmDesc; i++)
    {
	adp = alarms[i];
	asp = adp->alarms;

	for ( j = 0; j < adp->nCanNames; j++ )
	    for ( k = 0; k < adp->nAlarmItems; k++, asp = asp->next )
		if ( asp->dmitem == item )
		    return( asp );
    }

    return( NULLA );

} /* getAlarmStruct() */


/************************************************************************/
/* Function    : enableAlarmCallback					*/
/* Purpose     : Dialog Callback to enable or silence one alarm		*/
/* Inputs      : Widget, on/off Boolean, Callback struct		*/
/* Outputs     : None							*/
/************************************************************************/
    Void
enableAlarmCallback(Widget w, Nat32 on, XmSelectionBoxCallbackStruct *cbs )
{
    Reg alarmDesc	*adp;
    Reg alarmStruct	*asp;
    Reg Nat32		i, j, k;
    char		*s;

    XmStringGetLtoR( cbs->value, XmSTRING_DEFAULT_CHARSET, &s );

    for ( k = 0; k < nalarmDesc; k++ )
    {
	adp = alarms[k];
	asp = adp->alarms;

	for ( i = 0; i < adp->nCanNames; i++ )
	    for ( j = 0; j < adp->nAlarmItems; j++, asp = asp->next )
	    {
		sprintf( buff, "%s %s", adp->alarmCans[i].canAlarmName,
			    	adp->alarmItems[j].alarmString );
		if ( strcmp(s, buff) == 0 )
		    asp->silence = (on ? FALSE : TRUE );
	    }
    }

} /* enableAlarmCallback() */


/************************************************************************/
/* Function    : enableAllAlarms					*/
/* Purpose     : Callback to enable or silence all alarms		*/
/* Inputs      : Widget, on/off Boolean, Callback struct		*/
/* Outputs     : None							*/
/************************************************************************/
    Void
enableAllAlarms(Widget w, Nat32 on, XtPointer call)
{
    Reg alarmStruct	*asp;
    Reg Nat32		i;

    for ( i = 0; i < nalarmDesc; i++ )
	for ( asp = alarms[i]->alarms; asp != NULLA; asp = asp->next )
	    asp->silence = (on ? FALSE : TRUE );

    XtUnmanageChild( alarmEnableDialog );

} /* enableAllAlarms() */


/************************************************************************/
/* Function    : enableAlarms						*/
/* Purpose     : Callback to enable or silence alarms			*/
/* Inputs      : Widget, on/off Boolean, Callback struct		*/
/* Outputs     : None							*/
/************************************************************************/
    Void
enableAlarms(Widget w, Nat32 on, XtPointer call)
{
    Reg XmString	str, str1;
    Reg alarmDesc	*adp;
    Reg alarmStruct	*asp;
    Reg Int32		i, j, k, n;
    char		*typestr;

    typestr = on ? "Enable" : "Silence";

    sprintf( buff, "Choose Alarm to %s", typestr );
    str = XmStringCreateSimple( buff );
    sprintf( buff, "%s\nAll", typestr );
    str1 = XmStringCreateLtoR( buff, XmSTRING_DEFAULT_CHARSET );
    XtVaSetValues( alarmEnableDialog, XmNlistLabelString, str, 
		   XmNapplyLabelString, str1, NULL );
    XmStringFree( str );
    XmStringFree( str1 );

    XtRemoveAllCallbacks( alarmEnableDialog, XmNokCallback );
    XtAddCallback( alarmEnableDialog, XmNokCallback, 
		   (XtCallbackProc)enableAlarmCallback, (XtPointer)on );

    XtRemoveAllCallbacks( alarmEnableDialog, XmNapplyCallback );
    XtAddCallback( alarmEnableDialog, XmNapplyCallback, 
		   (XtCallbackProc)enableAllAlarms, (XtPointer)on );

    for ( k = n = 0; k < nalarmDesc; k++ )
    {
	adp = alarms[k];
	asp = adp->alarms;

	for ( i = 0; i < adp->nCanNames; i++ )
	    for ( j = 0; j < adp->nAlarmItems; j++, asp = asp->next )
		if ( (asp->silence && on) || (!asp->silence && !on) )
		{
		    sprintf( buff, "%s %s", adp->alarmCans[i].canAlarmName,
			    	adp->alarmItems[j].alarmString );
		    alarmDialogStrings[n++] = XmStringCreateSimple( buff );
		}
    }

    XtVaSetValues( alarmEnableDialog, XmNlistItems, alarmDialogStrings,
		   XmNlistItemCount, n, NULL );

    for ( i = 0; i < n; i++ )
	XmStringFree( alarmDialogStrings[i] );

    if ( n > 0 )
	XtManageChild( alarmEnableDialog );
    else
    {
	sprintf( buff, "All Alarms %sd", typestr );
	rovError( topFormW, buff );
    }

} /* enableAlarms() */


/************************************************************************/
/* Function    : initAlarm						*/
/* Purpose     : Initialize function for alarms				*/
/* Inputs      : None							*/
/* Outputs     : None							*/
/************************************************************************/
    Void
initAlarm( Void )
{
    Reg Nat32		i;
    Reg DWord		changes;
    XmString		str;


    alarmListString[0] = '\0';

    for ( i = 0; i < MAX_ALARMS; i++ )
    {
	alarmDialogs[i].widget = NULLW;
	alarmDialogs[i].alarm = NULLA;
    }

    if ( appdata.log_alarms )
    {
	i = sprintf( buff, "%s.", appdata.alarm_file );
	gethostname( buff + i, 1024 );
	alarm_fp = fopen( buff, "a" );
    }

    for ( i = 0; i < nalarmDesc; i++ )
    {
	changes = createAlarms(alarms[i]);
	getAlarms( alarms[i], changes );
	writeAlarmList( alarms[i] );
    }

    XtVaGetValues( alarmList, XmNforeground, &alarmoff_fg,
			      XmNbackground, &alarmoff_bg, NULL );

    XtVaGetValues( alarmWindow, XmNclipWindow, &alarmClipWindow, NULL );

    displayAlarmList();

    alarmEnableDialog = XmCreateSelectionDialog(topFormW, "enableAlarm",
						(ArgList)NULL, 0);

    XtUnmanageChild(XmSelectionBoxGetChild(alarmEnableDialog, 
					   XmDIALOG_HELP_BUTTON));

    str = XmStringCreateSimple( "Dismiss" );
    XtVaSetValues( alarmEnableDialog, XmNcancelLabelString, str, NULL );
    XmStringFree( str );

    timeoutId = XtAppAddTimeOut(appcontext, appdata.alarm_update,
				alarmTimeoutProc, NULL);

} /* initAlarm() */


/************************************************************************/
/* Function    : termAlarm						*/
/* Purpose     : Terminate function for alarms				*/
/* Inputs      : None							*/
/* Outputs     : None							*/
/************************************************************************/
    Void
termAlarm( Void )
{
    Reg alarmStruct	*asp;
    Reg int		i;

    if ( alarm_fp != (FILE *)NULL )
	fclose( alarm_fp );

    for ( i = 0; i < nalarmDesc; i++ )
    {
	rdm_delete_group( alarms[i]->groupId );

	for ( asp = alarms[i]->alarms; asp != NULLA; asp = asp->next )
	    rdm_stop_consumer( asp->dmitem );
    }

} /* termAlarm() */


/************************************************************************/
/* Functions to Return whether an alarm is on 				*/
/************************************************************************/
/************************************************************************/
/* Function    : getMotorStatus						*/
/* Purpose     : Get Motor Alarm Status, return corresponding string	*/
/* Inputs      : AlarmStruct, Boolean to assert dialog			*/
/* Outputs     : TRUE if Motor has alarm condition (not OK), else FALSE	*/
/************************************************************************/
	MBool
getMotorStatus( alarmStruct *asp, MBool *assert )
{
    motorFaultStatus	val;

    rdm_read( asp->dmitem, (char *)&val, sizeof(val) );

    if ( val > MOTOR_FAULT )
	val = MOTOR_FAULT;

    *assert = ( val != asp->dmValue );
    asp->status = motorStatus[val];
    asp->dmValue = (Int32)val;

    return( val > MOTOR_NO_FAULT );

} /* getMotorStatus() */


/************************************************************************/
/* Function    : getEventAlarm						*/
/* Purpose     : Return Alarm status for event				*/
/* Inputs      : AlarmStruct						*/
/* Outputs     : Always TRUE						*/
/* Comment     : If this func called, event has happened, so return TRUE*/
/************************************************************************/
	MBool
getEventAlarm( alarmStruct *asp, MBool *assert )
{
    asp->dmValue = TRUE;
    asp->status = nullstr;
    *assert = TRUE;
    return( TRUE );

} /* getEventAlarm() */


/************************************************************************/
/* Function    : getMBoolAlarm						*/
/* Purpose     : Get Alarm Status for MBool				*/
/* Inputs      : AlarmStruct						*/
/* Outputs     : TRUE if alarm, else FALSE				*/
/************************************************************************/
	MBool
getMBoolAlarm( alarmStruct *asp, MBool *assert )
{
    MBool	val;

    rdm_read( asp->dmitem, (char *)&val, sizeof(MBool) );
    *assert = !asp->dmValue;
    asp->dmValue = (Int32)val;
    asp->status = nullstr;
    return( val );

} /* getMBoolAlarm() */


/************************************************************************/
/* Function    : getNat16Alarm						*/
/* Purpose     : Get Alarm Status for Nat16 item			*/
/* Inputs      : AlarmStruct						*/
/* Outputs     : TRUE if alarm, else FALSE				*/
/************************************************************************/
	MBool
getNat16Alarm( alarmStruct *asp, MBool *assert, Nat32 okvalue )
{
    Nat16	val;

    rdm_read( asp->dmitem, (char *)&val, sizeof(Nat16) );

    *assert = (asp->dmValue == okvalue);
    asp->dmValue = (Nat32)val;

    if ( asp->status == NULL )
	asp->status = XtMalloc( 32 );

    sprintf( asp->status, "%#x", val );
    return( val != okvalue );

} /* getNat16Alarm() */


/************************************************************************/
/* Function    : getMicroAlarm						*/
/* Purpose     : Get Alarm Status for microAlarm type			*/
/* Inputs      : AlarmStruct						*/
/* Outputs     : TRUE if alarm, else FALSE				*/
/************************************************************************/
	MBool
getMicroAlarm( alarmStruct *asp, MBool *assert )
{
    microAlarm val;

    rdm_read( asp->dmitem, (char *)&val, sizeof(microAlarm) );

    if ( (Nat32)val > MICRO_ALARM )
	val = MICRO_ALARM;

    *assert = (val > asp->dmValue);
    asp->dmValue = val;
    asp->status = ibcAlarmStrings[val];
    return( val != MICRO_ALARM_OK );

} /* getMicroAlarm() */


/************************************************************************/
/* Function    : getGfAlarm						*/
/* Purpose     : Get Gnd Fault Alarm Status				*/
/* Inputs      : AlarmStruct						*/
/* Outputs     : TRUE if alarm, else FALSE				*/
/************************************************************************/
	MBool
getGfAlarm( alarmStruct *asp, MBool *assert )
{
    gfStatus	val;

    rdm_read( asp->dmitem, (char *)&val, sizeof(gfStatus) );

    if ( (Nat32)val > GF_CRITICAL )
	val = GF_CRITICAL;

    *assert = (val > asp->dmValue);
    asp->dmValue = val;
    asp->status = gfAlarmStrings[val];
    return( val != GF_NORMAL );

} /* getGfAlarm() */


/************************************************************************/
/* Function    : getGfTestAlarm						*/
/* Purpose     : Get Gnd Fault Test Alarm Status			*/
/* Inputs      : AlarmStruct						*/
/* Outputs     : TRUE if alarm, else FALSE				*/
/************************************************************************/
	MBool
getGfTestAlarm( alarmStruct *asp, MBool *assert )
{
    gfTestStatus	val;

    rdm_read( asp->dmitem, (char *)&val, sizeof(gfTestStatus) );

    if ( (Nat32)val > GF_TEST_FAIL_OFF )
	val = GF_TEST_FAIL_OFF;

    *assert = (asp->dmValue == GF_TEST_OK);
    asp->dmValue = val;
    asp->status = gfTestStrings[val];
    return( val != GF_TEST_OK );

} /* getGfTestAlarm() */


/************************************************************************/
/* Function    : getGf5vTestAlarm					*/
/* Purpose     : Get Gnd Fault Test Alarm Status			*/
/* Inputs      : AlarmStruct						*/
/* Outputs     : TRUE if alarm, else FALSE				*/
/************************************************************************/
	MBool
getGf5vTestAlarm( alarmStruct *asp, MBool *assert )
{
    gf5v_gfTestStatus	val;

    rdm_read( asp->dmitem, (char *)&val, sizeof(val) );

    if ( (Nat32)val > GF_SELFTEST_PASSED )
	val = GF_SELFTEST_FAILED;

    *assert = (asp->dmValue == GF_SELFTEST_PASSED);
    asp->dmValue = val;
    asp->status = gf5vTestStrings[val];
    return( (val != GF_SELFTEST_PASSED) && (val != GF_TEST_NOT_RUN) );

} /* getGf5vTestAlarm() */


/****************************************************/
/* Following crap is just to print out alarm strings*/
/****************************************************/
#if PRINT_ALARMS

typedef struct				/********************************/
{					/* Struct for alarm strings	*/
    AlarmFuncPtr	alarmFunc;	/* Function to return alarm status*/
    char		**alarmStrs;	/* Alarm string array		*/
    Nat32		nstrs;		/* Number of strings in alarmStrs*/
} alarmStsStrings;			/********************************/

MLocal char	*nat16String[] = { "<hex number>" };

MLocal alarmStsStrings alarmStrings[] = 
{ { getMotorStatus, motorStatus, XtNumber(motorStatus) },
  { getNat16Alarm, nat16String,  XtNumber(nat16String) },
  { getIbcAlarm, ibcAlarmStrings, XtNumber(ibcAlarmStrings) },
  { getGfAlarm,  gfAlarmStrings, XtNumber(gfAlarmStrings) },
  { getGfTestAlarm, gfTestStrings, XtNumber(gfTestStrings) }
};

/************************************************************************/
/* Function    : printAlarmStrings					*/
/* Purpose     : Print possible alarmString values			*/
/* Inputs      : alarmFunc ptr						*/
/* Outputs     : None							*/
/************************************************************************/
	Void
printAlarmStrings( AlarmFuncPtr func )
{
    Reg Nat32		i, j;

    for ( i = 0; i < XtNumber(alarmStrings); i++ )
	if ( alarmStrings[i].alarmFunc == func )
	{
	    printf( " {" );
	    for ( j = 0; j < alarmStrings[i].nstrs; j++ )
	    {
		if ( j > 0 )
		    printf( " " );
		printf( "%s", alarmStrings[i].alarmStrs[j] );
	    }
	    printf( "}" );
	}

} /* printAlarmStrings() */

#endif /* PRINT_ALARMS */
