/****************************************************************************/
/* Copyright 1992 to 1995 MBARI                                             */
/****************************************************************************/
/* Summary  : telemetry board microcontroller interface task VxWorks        */
/* Filename : telemDiag.c                                                   */
/* Author   : Andrew Pearce                                                 */
/* Project  : Tiburon ROV Telemetry Board                                   */
/* Version  : 1.0                                                           */
/* Created  : 03/29/95                                                      */
/* Modified : 09/11/95                                                      */
/* Archived :                                                               */
/****************************************************************************/
/* Modification History:                                                    */
/* $Header$
 * $Log$
 */
/****************************************************************************/

#include <vxWorks.h>                /* vxWorks system declarations          */
#include <lstLib.h>                 /* vxWorks linked list library          */
#include <taskLib.h>                /* vxWorks task library functions       */
#include <wdLib.h>                  /* vxWorks watch dog timer functions    */
#include <semLib.h>                 /* vxWorks semaphore functions          */
#include <msgQLib.h>                /* vxWorks Message Queue Library        */
#include <timers.h>                 /* vxWorks Time/Timer functions         */

#include "mbariTypes.h"             /* mbari style guide type declarations  */
#include "mbariConst.h"             /* Miscellaneous constants              */
#include "ModNum.h"                 /* mbari module numbers for error code  */
#include "rovPriority.h"            /* mbari Rov Application Priorities     */
#include "usrTime.h"                /* mbari time and date functions        */
#include "datamgr.h"                /* Data Manager declarations            */
#include "telem_card.h"             /* Telemetry board definitions          */
#include "timerWrap.h"              /* POSIX timer helper functions         */

#include "../h/sio32Drv.h"          /* Sio32 communications definitions     */
#include "../h/sio32Server.h"       /* Sio32 server communication services  */
#include "../h/sio32Client.h"       /* Sio32 client communication services  */

#include "applic.h"                 /* Microcontroller applications         */
#include "microdef.h"               /* Microcontroller Definitions          */
#include "microcmd.h"               /* Microcontroller commands             */
#include "telem.h"                  /* Telem Microcontroller commands       */

#include "microCmd.h"               /* Microcontroller serial I/O functions */
#include "microDm.h"                /* Microcontroller Data Manager Items   */
#include "telemDiag.h"              /* Telemetry Board Data Manager items   */

MLocal const char *telemDmPrefix[] = /* Data Manager Item Name Prefix       */
{
    TOPSIDE_TELEM_DM_PREFIX,
    ROV_TELEM_DM_PREFIX
};

MLocal const char *telemBoardName[] =
{
    "Topside Telem Board",
    "ROV Telem Board"
};

#define TEMP_DM_PERIOD      2000000 /* Temperature sensor provider period   */
#define TELEM_DM_PERIOD      100000 /* PSU Status provider period           */

#define TELEM_IN_STACK         2500 /* Telemetry Micro In Task size & TCB   */
#define TELEM_OUT_STACK        2300 /* Telemetry Micro Out Task size & TCB  */

#define SIO32_PORTS SIO32_CHAN_PER_UART

#define TELEM_BIT_RATE  250000000.0 /* 250 M bits/second                    */
#define BIT_ERROR_RATE_WARN  1.0E-9 /* 10 to the minus 10                   */
#define BIT_ERROR_RATE_ALARM 1.0E-5 /* 10 to the minus 6                    */
#define TELEM_HIST_COUNT     10     /* Number of samples in telem history   */


#define BIT_ERROR_RESET_DELAY 5     /* 5 second delay after FDDI board is up*/

typedef struct                      /* Telemetry Data Manager Item Handles  */
{
    microDmItems microDm;           /* Microcontroller Data Manager Items   */

    DM_Item telemPsuStatus;         /* Telem Board Power Supply Status      */
    DM_Item telemRelayCtrl;         /* Telem Breakout Board Relay Control   */
    DM_Item serialStats[SIO32_PORTS];  /* Serial I/O statistics             */

    DM_Item microTemperature;       /* Temperature sensor value deg C       */
    DM_Item temperatureAlarm;       /* Temperature Alarm Detected TRUE/FALSE*/
    DM_Item tempWarnThresh;         /* Temperature Warning Threshold        */
    DM_Item tempAlarmThresh;        /* Temperature Alarm Threshold          */

    DM_Item localGfCurrent;         /* Local Ground Fault Current           */
    DM_Item localGndFaultAlarm;     /* Local Ground Fault Alarm             */
    DM_Item gndFaultTest;           /* Ground Fault Test Enable             */
    DM_Item gndFaultTestStatus;     /* Ground Fault Test Status             */

    DM_Item bitErrorRate;           /* Telemetry short term bit error rate  */
    DM_Item bitErrorRateLongTerm;   /* Telemetry long term bit error rate   */
    DM_Item bitErrorRateAlarm;      /* Telemetry bit error rate alarm       */

    DM_Item telemRecvPwr;           /* Receive Power flag                   */
    DM_Item telemFDDIStatus;        /* FDDI Status flag                     */
    DM_Item telemLaserStatus;       /* Laser Status flag                    */
    DM_Item telemSubMuxError;       /* Sub Mux error flag                   */
} telemDmItems;

MLocal struct
{
    Nat16 count;
    Nat16 avgCount;
    Nat32 totalCount;

    Flt32 rate;
    Flt32 longTermRate;

    Flt32 warnThresh;
    Flt32 alarmThresh;
    microAlarm alarm;

    time_t startTime;
    struct timespec updateTime;

    Int32 histPtr;
    Int16 countHistory[TELEM_HIST_COUNT];

    MBool   berHoldoff;
    timer_t berResetTimer;
} telemBitError;

MLocal Word telemStatusWord;

/****************************************************************************/

Void telemInTask(Reg telemDmItems* dmItems, sio32Chan* telemDataChan);

Void telemOutTask(Reg telemDmItems* dmItems, sio32Chan* telemDataChan,
    telemBoardId telemBoard);

MLocal Void telemDmItemInit( telemDmItems* dmItems,
    sio32Chan* telemDataChan);

MLocal STATUS createTelemBoardAppDmItems( telemDmItems *dmItems,
    const char* telemBoardDmPrefix, const char* telemBoardName );

MLocal Void telemWaitForSRQ( telemDmItems* dmItems, sio32Chan*
     telemDataChan, Int32 telemInputTask, Int32 telemOutputTask );

MLocal MBool processTelemSRQ(Reg telemDmItems* dmItems,
     sio32Chan *telemDataChan);

MLocal Void initTelemBitErrorRate( Void );
MLocal Void checkTelemBitErrorRate(Nat16 bitErrorCount,
    DM_Item bitErrorRate, DM_Item bitErrorLongTermRate, DM_Item bitErrorAlarm);

/****************************************************************************/
/* Function    : telemDiagTask                                              */
/* Purpose     : Initializes communication with Telem Mezzanine Board micro */
/*               Provides interface between Telem nostic Items and Data     */
/*               Manager items.                                             */
/* Inputs      : Telem serial channel number for Telem Microcontroller      */
/* Outputs     : Normally runs forever, but returns ERROR on fatal error    */
/****************************************************************************/
    STATUS
telemDiagTask( telemBoardId telemBoard, Nat16 telemSerialChan )
{
    sio32Chan telemDataChan;        /* Serial chan for communications       */
                                    /* to telemetry board microcontroller   */

    MLocal telemDmItems dmItems;    /* structure containing dm item handles */

    Int32   telemInputTask;         /* telem micro Input Task Ident         */
    Int32   telemOutputTask;        /* telem micro Output Task Ident        */

    Int32   chan;                   /* Channel for statistics item names    */
    char    statName[32];           /* Buffer for statistics item names     */
                                    /* Init telemetry micro serial channel  */
    if (sio32ChanInit(telemSerialChan, "/sio32/telem", &telemDataChan)
        == ERROR)
    {
        logMsg("Error initializing Telemetry Micro Serial Channel\n");
        return(ERROR);
    } /* if */

                                   /* Clear Data Manager Item handle list   */
    bzero(&dmItems, sizeof(dmItems));

    telemStatusWord = 0;           /* Clear telemetry status word           */

                                   /* Create Telemetry Board DM Items       */
    if (createTelemBoardAppDmItems(&dmItems, telemDmPrefix[telemBoard],
         telemBoardName[telemBoard]) == ERROR)
    {                              /* Error occurred so print message & exit*/
        sio32ChanDestroy( telemSerialChan, &telemDataChan );
        return(ERROR);
    } /* if */

    for (chan = 0; chan < SIO32_PORTS; chan++)
    {
        sprintf(statName, "%s%02d", TELEM_SERIAL_STATS_DM, chan);
        if ((dmItems.serialStats[chan] =
             initMicroDmItem(telemDmPrefix[telemBoard], statName, DM_NAT32, 4))
              == ERROR)
        {
            logMsg("telem: Error Initializing Statistics %d DM Item\n", chan);
            return(ERROR);
        } /* if */
    } /* for */
                                    /* Provider for Static Data & Alarms    */
    dm_start_provider(dmItems.microDm.idString,    DM_STATIC);
    dm_start_provider(dmItems.microDm.serialNo,    DM_STATIC);
    dm_start_provider(dmItems.microDm.softwareRev, DM_STATIC);
    dm_start_provider(dmItems.microDm.resetEvent,  DM_STATIC);
    dm_start_provider(dmItems.microDm.ramTest,     DM_STATIC);

    dm_start_provider(dmItems.temperatureAlarm,    DM_STATIC);
    dm_start_provider(dmItems.localGndFaultAlarm,  DM_STATIC);
    dm_start_provider(dmItems.gndFaultTestStatus,  DM_STATIC);

    dm_start_provider(dmItems.bitErrorRate,        DM_ASYNC);
    dm_start_provider(dmItems.bitErrorRateLongTerm,DM_ASYNC);
    dm_start_provider(dmItems.bitErrorRateAlarm,   DM_STATIC);

    dm_start_provider(dmItems.telemRecvPwr,        DM_STATIC);
    dm_start_provider(dmItems.telemFDDIStatus,     DM_STATIC);
    dm_start_provider(dmItems.telemLaserStatus,    DM_STATIC);
    dm_start_provider(dmItems.telemSubMuxError,    DM_STATIC);

                                    /* Initialize Static Data Manager Items */
    telemDmItemInit( &dmItems, &telemDataChan );

    initTelemBitErrorRate();        /* Initialize Bit Error Rate Measurement*/

    if (( telemInputTask =          /* Start telemetry micro Data Input Task*/
         taskSpawn("telemDiagIn", TELEM_DIAG_IN_PRIORITY, 0,
         TELEM_IN_STACK, (FUNCPTR) telemInTask, (int) &dmItems,
         (int) &telemDataChan, 0, 0, 0, 0, 0, 0, 0, 0 ) ) == ERROR)
    {
        sio32ChanDestroy( telemSerialChan, &telemDataChan );
        logMsg("telem: Error spawning data input task\n");
        return(ERROR);
    } /* if */

    if (( telemOutputTask =         /* Start telemetry micro Data Out Task  */
         taskSpawn("telemDiagOut", TELEM_DIAG_OUT_PRIORITY, 0,
         TELEM_OUT_STACK, (FUNCPTR) telemOutTask, (int) &dmItems,
         (int) &telemDataChan, telemBoard, 0, 0, 0, 0, 0, 0, 0)) == ERROR)
    {                               /* Delete telem Input Task              */
        taskDelete(telemInputTask);
                                    /* Close serial communication channel   */
        sio32ChanDestroy( telemSerialChan, &telemDataChan );
        logMsg("telem: Error spawning data output task\n");
        return(ERROR);
   } /* if */

    FOREVER
    {
        telemWaitForSRQ( &dmItems, &telemDataChan,
                 telemInputTask, telemOutputTask );

                                /* Reinitialize dm items when reset received*/
        telemDmItemInit( &dmItems, &telemDataChan );

    } /* FOREVER */

    taskDelete(telemInputTask); /* Delete telemnostic Input Task            */
    taskDelete(telemOutputTask);/* Delete telemnostic Output Task           */

                                    /* Close serial communication channel   */
    sio32ChanDestroy( telemSerialChan, &telemDataChan );

                        /* dm_stop_provider, dm_stop_consumer, etc are NOT  */
                        /* required when the task exits as the Data Manager */
                        /* does this automatically when the task is deleted */
                        /* via taskDeleteHookAdd() & dm_task_exit functions */

    return(OK);         /* That's all folks                                 */
} /* telemDiagTask() */

/****************************************************************************/
/* Function    : telemGetTemperature                                        */
/* Purpose     : Returns temperature sensor reading from telem micro        */
/* Inputs      : Sio32 channel, pointer to temperature value                */
/* Outputs     : Returns OK or ERROR                                        */
/****************************************************************************/
    LOCAL STATUS
telemGetTemperature( sio32Chan *telemDataChan, Word *temp )
{
    return (microCommand(telemDataChan, TELEM_APP | GET_TEMPERATURE, temp));
} /* telemGetTemperature() */

/****************************************************************************/
/* Function    : telemGetTempAlarmThresh                                    */
/* Purpose     : Returns temperature alarm threshold from microcontroller   */
/* Inputs      : Sio32 channel, pointer to temp alarm threshold value       */
/* Outputs     : Returns OK or ERROR                                        */
/****************************************************************************/
    STATUS
telemGetTempAlarmThresh( sio32Chan *telemDataChan,
        Int16 *tempWarn, Int16 *tempAlarm )
{
    Int16   cmdStatus;              /* Command Status                       */
    Word    reply[3];               /* Command Result from microcontroller  */

                                    /* Send command to microcontroller      */
    if ( sendMicroCommand( telemDataChan, &reply, sizeof(Word) * 3,
         1, TELEM_APP | GET_TEMP_ALARM_THRESH ) == ERROR )
         return(ERROR);

                                    /* Check if command result is CMD OK    */
    if (wordFromBuf(&reply, 0) != CMD_OK)
        return(ERROR);              /* Command failed so return ERROR       */
                                    /* Command OK so update thresholds      */
    wordsFromBuf(reply, 3, &cmdStatus, tempWarn,  tempAlarm );

    return (OK);                    /* Return OK                            */
} /* telemGetTempAlarmThresh() */

/****************************************************************************/
/* Function    : telemGetTempAlarm                                          */
/* Purpose     : Returns temperature alarm status from telem microcontroler */
/* Inputs      : Sio32 channel, pointer to temperature alarm status         */
/* Outputs     : Returns OK or ERROR                                        */
/****************************************************************************/
    STATUS
telemGetTempAlarm( sio32Chan *telemDataChan, microAlarm *tempAlarmStatus )
{
    Word alarmStatus;
                                /* Get Temperature Alarm Status from micro  */
    if (microCommand(telemDataChan, TELEM_APP | GET_TEMP_ALARM_STATUS,
         &alarmStatus) !=OK) return(ERROR);

    *tempAlarmStatus = (microAlarm) alarmStatus;
    return(OK);
} /* telemGetTempAlarm() */

/****************************************************************************/
/* Function    : updateMicroTempDm                                          */
/* Purpose     : Reads micro temperature sensor value & updates dmgr item   */
/* Inputs      : Sio32 chan & temperature sensor data manager item handle   */
/* Outputs     : Returns OK or ERROR                                        */
/****************************************************************************/
    LOCAL STATUS
updateMicroTempDm( sio32Chan *telemDataChan, DM_Item microTempDm )
{
    Int16   temperature;            /* Micro Temperature Sensor Value       */
                                    /* Read temperature sensor value        */
    if (telemGetTemperature(telemDataChan, (Word *) &temperature) != OK)
        return(ERROR);
                                    /* Write temperature to Data Manager    */
    return (writeDmItem(microTempDm, &temperature, sizeof(temperature)));
} /* updateMicroTempDm() */

/****************************************************************************/
/* Function    : updateTempAlarmDm                                          */
/* Purpose     : Reads micro temperature alarm status & updates dmgr item   */
/* Inputs      : Sio32 chan & temperature alarm data manager item handle    */
/* Outputs     : Returns OK or ERROR                                        */
/****************************************************************************/
    LOCAL STATUS
updateTempAlarmDm( sio32Chan *telemDataChan, DM_Item tempAlarmDm )
{
    microAlarm tempAlarmStatus;         /* Micro temperature alarm status   */
                                        /* Read temperature alarm status    */
    if (telemGetTempAlarm(telemDataChan, &tempAlarmStatus) != OK)
        return(ERROR);
                                        /* Write temperature alarm to Dmgr  */
    return(writeDmItem(tempAlarmDm, &tempAlarmStatus,
         sizeof(tempAlarmStatus)));
} /* updateTempAlarmDm() */

/****************************************************************************/
/* Function    : updateTempAlarm                                            */
/* Purpose     : Update temperature alarm DM item. Temperature updated also */
/*               so alarm status reflects state of temperature              */
/* Inputs      : Temp alarm status, serial chan, temp & temp alarm DM items */
/* Outputs     : None, but data manager items change                        */
/****************************************************************************/
     LOCAL Void
updateTempAlarm(sio32Chan *telemDataChan, microAlarm tempAlarm,
                 DM_Item tempAlarmDm, Reg DM_Item temperatureDm)
{
                                /* Use urgent calls as task is not provider */
    dm_urgent_open(temperatureDm, DM_STATIC);
                                /* Read temperature and update Dmgr item    */
    updateMicroTempDm(telemDataChan, temperatureDm);
    dm_urgent_close(temperatureDm);
                                /* Write temperature alarm to Dmgr          */
    writeDmItem(tempAlarmDm, &tempAlarm, sizeof(tempAlarm));
} /* updateTempAlarm() */

/****************************************************************************/
/* Function    : telemSetTempAlarmThresh                                    */
/* Purpose     : Sets telem Temperature Alarm Threshold                     */
/* Inputs      : Sio32 channel, temperature alarm threshold                 */
/* Outputs     : Return OK or ERROR                                         */
/****************************************************************************/
    LOCAL STATUS
telemSetTempAlarmThresh( Reg sio32Chan *telemDataChan, Int16 tempWarn,
                        Int16 tempAlarm )
{
    Word    status;                 /* Command Result from microcontroller  */

                                    /* Send command to microcontroller      */
    if ( sendMicroCommand( telemDataChan, &status, sizeof(status),
         3, TELEM_APP | SET_TEMP_ALARM_THRESH, tempWarn, tempAlarm )
         == ERROR ) return(ERROR);

                                    /* Check command result                 */
    return (wordFromBuf(&status, 0) == CMD_OK ? OK : ERROR);
} /* telemSetTempAlarmThresh() */

/****************************************************************************/
/* Function    : initTempAlarmThreshDm                                      */
/* Purpose     : Read Temp Alarm Threshold and update data manager item     */
/* Inputs      : Sio32 channel, temp alarm threshold dm item handle         */
/* Outputs     : Returns OK or ERROR                                        */
/****************************************************************************/
    LOCAL STATUS
initTempAlarmThreshDm ( sio32Chan *telemDataChan, DM_Item tempWarnDm,
    DM_Item tempAlarmDm )
{
    Int16  tempWarn, tempAlarm;         /* Temp Alarm Threshold from micro  */

                                        /* Get Temp Alarm Thresh from micro */
    if (telemGetTempAlarmThresh( telemDataChan, &tempWarn, &tempAlarm ) != OK)
        return (ERROR);
                                        /* We MUST use urgent open here as  */
                                        /* another task is normal provider  */
    urgentWriteDmItem(tempWarnDm,  &tempWarn,  sizeof(tempWarn));
    urgentWriteDmItem(tempAlarmDm, &tempAlarm, sizeof(tempAlarm));

    return(OK);
} /* initTempAlarmThreshDm() */

/****************************************************************************/
/* Function    : writeTempAlarmThresh                                       */
/* Purpose     : Get temperature alarm threshold and send to telem micro    */
/* Inputs      : Sio32 channel, temperature alarm threshold                 */
/* Outputs     : Returns OK or ERROR                                        */
/****************************************************************************/
    LOCAL STATUS
writeTempAlarmThresh( sio32Chan *telemDataChan, DM_Item tempWarnDm,
                DM_Item tempAlarmDm )
{
    Int16   tempWarn, tempAlarm;        /* Temp Alarm Threshold from Dmgr   */
                                        /* Read Temp Alarm Thresh from dmgr */
    if (dm_read( tempWarnDm, (char *) &tempWarn,
         sizeof(tempWarn), (DM_Time *)NULL) == SUCCESS )

        if (dm_read( tempAlarmDm, (char *) &tempAlarm,
             sizeof(tempAlarm), (DM_Time *)NULL) == SUCCESS )
                                        /* Send value to motor micro        */
            if (telemSetTempAlarmThresh( telemDataChan, tempWarn, tempAlarm)
                == OK) return (OK);

    return(ERROR);
} /* writeTempAlarmThresh() */

/****************************************************************************/
/* Function    : telemGetGndFltAlarm                                        */
/* Purpose     : Returns Groud Fault alarm status from telem microcontroler */
/* Inputs      : Sio32 channel, pointer to Ground Fault Alarm status        */
/* Outputs     : Returns OK or ERROR                                        */
/****************************************************************************/
    MLocal STATUS
telemGetGndFltAlarm( sio32Chan *telemDataChan, gfStatus *localGfAlarm )
{
    Word localGfStatus;
                                /* Get Ground Fault Alarm Status from micro*/
    if (microCommand(telemDataChan, MICRO_APP | GET_GNDFLT_STATUS,
         &localGfStatus) != OK) return(ERROR);

                                 /* Map to vxWorks ENUM definition         */
    *localGfAlarm = (gfStatus) localGfStatus;

    return(OK);
} /* telemGetGndFltAlarm() */

/****************************************************************************/
/* Function    : updateGndFltAlarmDm                                        */
/* Purpose     : Reads Ground Fault alarm status & updates dmgr item        */
/* Inputs      : Sio32 chan & Ground Fault alarm data manager item handle   */
/* Outputs     : Returns OK or ERROR                                        */
/****************************************************************************/
    LOCAL STATUS
updateGndFltAlarmDm( sio32Chan *telemDataChan, DM_Item localGfAlarmDm )
{
    gfStatus localGfAlarm;              /* Local Ground Fault alarm         */

    if (telemGetGndFltAlarm(telemDataChan, &localGfAlarm) != OK)
        return(ERROR);
                                        /* Write Ground Fault alarm to Dmgr */
    return(writeDmItem(localGfAlarmDm, &localGfAlarm, sizeof(localGfAlarm)));
} /* updateGndFltAlarmDm() */

/****************************************************************************/
/* Function    : updateGndFltTestDm                                         */
/* Purpose     : Reads Ground Fault Test status & updates dmgr item         */
/* Inputs      : Sio32 chan & Ground Fault test data manager item handle    */
/* Outputs     : Returns OK or ERROR                                        */
/****************************************************************************/
    LOCAL STATUS
updateGndFltTestDm( sio32Chan *telemDataChan, DM_Item gndFltTestDm )
{
    Word  gndFltTest;
    gfTestStatus gfTest;                /* Ground Fault alarm status        */

                                        /* Get GF Test Status from micro    */
    if (microCommand(telemDataChan, MICRO_APP | GET_GNDFLT_TEST,
         &gndFltTest) !=OK)
        return(ERROR);

    gfTest = (gfTestStatus) gndFltTest; /* Map to vxWorks definition        */

                                        /* Write Ground Fault alarm to Dmgr */
    return(writeDmItem(gndFltTestDm, &gfTest, sizeof(gfTest)));
} /* updateGndFltTestDm() */

/****************************************************************************/
/* Function    : telemGetGndFltCurrent                                      */
/* Purpose     : Returns Groud Fault current measurements from micro        */
/* Inputs      : Sio32 channel, pointer to local GF current                 */
/* Outputs     : Returns OK or ERROR                                        */
/****************************************************************************/
    MLocal STATUS
telemGetGndFltCurrent( sio32Chan *telemDataChan, Int16 *localGfCurrent)
{
    Int16 cmdStatus;                /* Command Status                       */
    Word  reply[2];                 /* Command Result from microcontroller  */
    Word  localGf;
                                    /* Get Ground Fault Currents from micro */
                                    /* Send command to microcontroller      */
    if ( sendMicroCommand( telemDataChan, &reply, sizeof(Word) * 2,
         1, MICRO_APP | GET_GNDFLT_VALUE ) == ERROR )
        return(ERROR);
                                    /* Check if command result is CMD OK    */
    if (wordFromBuf(&reply, 0) != CMD_OK)
        return(ERROR);              /* Command failed so return ERROR       */

                                    /* Command OK so update GF current      */
    wordsFromBuf(reply, 2, &cmdStatus, &localGf);
    *localGfCurrent = (Int16) localGf;

    return(OK);
} /* telemGetGndFltCurrent() */

/****************************************************************************/
/* Function    : updateGndFltCurrentDm                                      */
/* Purpose     : Reads Ground Fault current & updates dmgr items            */
/* Inputs      : Sio32 chan & Ground Fault current item handles             */
/* Outputs     : Returns OK or ERROR                                        */
/****************************************************************************/
    LOCAL STATUS
updateGndFltCurrentsDm( sio32Chan *sio32DataChan, DM_Item localGfCurrentDm)
{
    Int16 localGfCurrent;               /* Local Ground Fault Current       */

    if (telemGetGndFltCurrent(sio32DataChan, &localGfCurrent) != OK)
        return(ERROR);
                                        /* Write Ground Fault current to DM */
    return (writeDmItem(localGfCurrentDm, &localGfCurrent,
         sizeof(localGfCurrent)));
} /* updateGndFltCurrentDm() */

/****************************************************************************/
/* Function    : telemSetRelayState                                         */
/* Purpose     : Send command to set state of telemetry board relays        */
/* Inputs      : Telem Serial Channel, relay control word                   */
/* Outputs     : Returns OK or ERROR                                        */
/****************************************************************************/
    LOCAL STATUS
telemSetRelayState( sio32Chan *telemDataChan, DWord relayCtrl )
{
    Word    status;                 /* Command Result from microcontroller  */

                                    /* Send command to microcontroller      */
    if ( sendMicroCommand( telemDataChan, &status, sizeof(status),
        3, TELEM_APP | SET_RELAY_STATE,
        (relayCtrl >> 16) & 0xffff, relayCtrl & 0xffff) == ERROR )
             return(ERROR);

                                    /* Check command result                 */
    return (wordFromBuf(&status, 0) == CMD_OK ? OK : ERROR);
} /* telemSetRelayState() */

/****************************************************************************/
/* Function    : testTelemPowerSupply                                       */
/* Purpose     : Tests telem breakout board power supplies & updates dmgr   */
/* Inputs      : Telem Serial Channel                                       */
/* Outputs     : Returns Telemetry PSU Status Word                          */
/****************************************************************************/
    LOCAL Nat16
testTelemPowerSupply( sio32Chan *telemDataChan )
{
    Nat16  psuStatus;               /* telem PSU Status Word                */

                                    /* Read PSU Status Word                 */
    if ( (psuStatus = (Nat16) getSio32PSStatus(TELEM_BOARD))
        == (Nat16) ERROR) return(0);/* Assume all power supplies are bad    */


    if (psuStatus & (PS1_GOOD_BIT | PS2_GOOD_BIT) !=
        (PS1_GOOD_BIT | PS2_GOOD_BIT))
        return (psuStatus);         /* Power Supply 1 or 2 is bad           */

                                    /* Open all relays on breakout board    */
    if (telemSetRelayState(telemDataChan, 0xffffffff) != OK)
        return (psuStatus);         /* Relays failed to open                */

    taskDelay(1);                   /* Wait for relays to open              */

                                    /* Read PSU Status Word                 */
    if ( (psuStatus = (Nat16) getSio32PSStatus(TELEM_BOARD)) == (Nat16) ERROR)
        return(PS1_GOOD_BIT | PS2_GOOD_BIT);

                                    /* Close all relays on breakout board   */
    telemSetRelayState(telemDataChan, 0);

    if (~psuStatus & PS2_GOOD_BIT)  /* Relays opened so PS3 is good         */
        psuStatus |= PS3_GOOD_BIT;  /* Add PS2 & PS3 GOOD flag to bit mask  */

    psuStatus |= PS2_GOOD_BIT;      /* Add PS2 GOOD flag to bit mask        */

    return (psuStatus);
} /* testTelemPowerSupply() */

/****************************************************************************/
/* Function    : updateTelemPSUStatusDm                                     */
/* Purpose     : Reads telem PSU Status & updates dmgr item                 */
/* Inputs      : Sio32 PSU status data manager item                         */
/* Outputs     : Returns OK or ERROR                                        */
/****************************************************************************/
    LOCAL STATUS
updateTelemPSUStatusDm( DM_Item telemPsuStatusDm )
{
    Nat16  psuStatus;               /* telem PSU Status Word                */
    Nat16  oldStatus;               /* Previous PSU Status                  */
                                    /* Read PSU Status Word                 */
    if ( (psuStatus = (Nat16) getSio32PSStatus(TELEM_BOARD))
        == (Nat16) ERROR) return(ERROR);
                                    /* Read previous status vaule           */
    if (dm_read(telemPsuStatusDm, &oldStatus, sizeof(oldStatus),
                (DM_Time *)NULL) != SUCCESS )
        return (ERROR);
                                    /* Carry through PS3_GOOD_BIT           */
    psuStatus |= (oldStatus & PS3_GOOD_BIT);

    if (psuStatus == oldStatus)     /* Don't write Data Manager if no change*/
        return (OK);
                                    /* Write to Data Manager                */
    return (writeDmItem(telemPsuStatusDm, &psuStatus, sizeof(psuStatus)));
} /* updateTelemPSUStatusDm() */

/****************************************************************************/
/* Function    : updateTelemSerialStatsDm                                   */
/* Purpose     : Reads telem Serial I/O Statistics & updates dmgr items     */
/* Inputs      : Channel number and Data Manager item handle                */
/* Outputs     : Returns OK or ERROR                                        */
/****************************************************************************/
    LOCAL STATUS
updateTelemSerialStatsDm( Nat16 chan, DM_Item telemSerialStatsDm )
{
    pktStat  serialStats;

    if ((getSio32Statistics(TELEM_BOARD | chan, &serialStats)) == ERROR)
        return(ERROR);
                                    /* Write to Data Manager                */
    return (writeDmItem(telemSerialStatsDm, &serialStats,
         sizeof(serialStats)));
} /* updateTelemSerialStatsDm() */

/****************************************************************************/
/* Function    : initTelemRelayStateDm                                      */
/* Purpose     : Read telem Relay State and update data manager item        */
/* Inputs      : Sio32 channel, relay state dm item handle                  */
/* Outputs     : Returns OK or ERROR                                        */
/****************************************************************************/
    LOCAL STATUS
initTelemRelayStateDm ( sio32Chan *telemDataChan, DM_Item relayCtrlDm )
{
    DWord  relayCtrl;               /* Relay control word                   */
    Word   relayHigh, relayLow;     /* Relay Control Word High & Low        */
    Int16  cmdStatus;               /* Command Status                       */
    Word   reply[3];                /* Command Result from microcontroller  */

                                    /* Get Relay Ctrl Words from micro      */
    if ( sendMicroCommand( telemDataChan, &reply, sizeof(Word) * 3,
         1, TELEM_APP | GET_RELAY_STATE ) == ERROR )
         return(ERROR);

                                    /* Check if command result is CMD OK    */
    if (wordFromBuf(&reply, 0) != CMD_OK)
        return(ERROR);              /* Command failed so return ERROR       */

                                    /* Command OK so extract relay words    */
    wordsFromBuf(reply, 3, &cmdStatus, &relayHigh,  &relayLow );

                                    /* Convert to a 32 bit wide word        */
    relayCtrl = ((DWord) relayHigh << 16) | relayLow;

                                    /* We MUST use urgent open here as      */
                                    /* another task is normal provider      */
    return (urgentWriteDmItem(relayCtrlDm, &relayCtrl, sizeof(relayCtrl)));
} /* initTelemRelayStateDm() */

/****************************************************************************/
/* Function    : telemSetRelayControlCmd                                    */
/* Purpose     : Sets telem Relay Control bit vector                        */
/* Inputs      : Telem channel, relay control DM item handle                */
/* Outputs     : Return OK or ERROR                                         */
/****************************************************************************/
    LOCAL STATUS
telemSetRelayControlCmd( Reg sio32Chan *telemDataChan, DM_Item relayCtrlDm )
{
    Word    status;                 /* Command Result from microcontroller  */
    DWord   relayCtrl;              /* Relay control word                   */

    if (dm_read( relayCtrlDm, (char *) &relayCtrl,
             sizeof(relayCtrl), (DM_Time *) NULL) != SUCCESS )
        return (ERROR);

    relayCtrl = relayCtrl << 1;     /* Temporary fix until sio32 micro is   */
                                    /* updated. 1st Feb 1996 pean/audo      */

                                    /* Send command to microcontroller      */
    return (telemSetRelayState(telemDataChan, relayCtrl));
} /* telemSetRelayControlCmd() */

/****************************************************************************/
/* Function    : updateTelemetryStatusDm                                    */
/* Purpose     : Reads Telemetry Status Word & updates dmgr items           */
/* Inputs      : Sio32 chan & DM Item handle list                           */
/* Outputs     : Returns OK or ERROR                                        */
/****************************************************************************/
    LOCAL STATUS
updateTelemetryStatusDm( sio32Chan *telemDataChan, telemDmItems *dmItems)
{
    Word telemStatus;
                                /* Get telemetry status from micro          */
    if (microCommand(telemDataChan, TELEM_APP | GET_TELEM_ALARM_STATUS,
         &telemStatus) != OK) return(ERROR);

    writeBooleanDmItem(dmItems->telemRecvPwr,
       telemStatus & TELEM_RECV_PWR      ? TRUE : FALSE);

    writeBooleanDmItem(dmItems->telemFDDIStatus,
       telemStatus & TELEM_FDDI_STATUS   ? TRUE : FALSE);

    writeBooleanDmItem(dmItems->telemLaserStatus,
       telemStatus & TELEM_LASER_STATUS  ? TRUE : FALSE);

    writeBooleanDmItem(dmItems->telemSubMuxError,
       telemStatus & TELEM_SUB_MUX_ERROR ? TRUE : FALSE);

    telemStatusWord = telemStatus;

    return (OK);
} /* updateTelemetryStatusDm() */

/****************************************************************************/
/* Function    : createTelemBoardAppDmItems                                 */
/* Purpose     : Create Data Mgr Items specific to Telemetry Board          */
/* Inputs      : Pointer to structure of DM item handles, Telemetry Board   */
/*               DM prefix, Telemetry Board Microcontroller Name            */
/* Outputs     : Returns OK or ERROR                                        */
/****************************************************************************/
    LOCAL STATUS
createTelemBoardAppDmItems( telemDmItems *dmItems,
    const char* telemBoardDmPrefix, const char* telemBoardName )
{
    Int32   item;                   /* Counter for DM Item Initialization   */
    Errno   status;                 /* Result of dm_create function call    */

    struct                          /* Data Manager Item Table for table    */
    {                               /* driven item initialization           */
        DM_Item *dmItem;            /* pointer to actual dm item handle     */
        char    *errorMsg;          /* Message to display of dmCreate fails */
        char    *name;              /* Data Manager Item Name               */
        DM_Type type;               /* Data Manager Item Type               */
        DM_Num  len;                /* Data Manager Item Element Count      */
    } telemDmInit[] =

      { { &dmItems->microDm.resetEvent,      "Reset Event",
                MICRO_RESET_EVENT_DM,           DM_EMPTY, 1 },

        { &dmItems->microDm.resetCmd,        "Reset Command",
                MICRO_RESET_CMD_DM,             DM_EMPTY, 1 },

        { &dmItems->microDm.idString,        "Identification",
                MICRO_IDENT_DM,                 DM_CHAR, MICRO_ID_LEN  },

        { &dmItems->microDm.serialNo,        "Micro Serial Number",
                MICRO_SERIAL_NO_DM,             DM_CHAR, MICRO_SERNO_LEN },

        { &dmItems->microDm.softwareRev,     "Software Revision",
                SOFTWARE_REV_DM,                DM_CHAR, SOFTWARE_REV_LEN },

        { &dmItems->microDm.ramTest,         "Micro RAM Test",
                RAM_TEST_STATUS_DM,             DM_MBOOL, 1 },

        { &dmItems->telemPsuStatus,          "Telem PSU Status",
                TELEM_PSU_STATUS_DM,            DM_NAT16, 1 },

        { &dmItems->localGfCurrent,          "Local Ground Fault Current",
                LOCAL_GND_FLT_CURRENT_DM,       DM_INT16, 1 },

        { &dmItems->localGndFaultAlarm,      "Local Ground Fault Alarm",
                LOCAL_GND_FLT_ALARM_DM,         DM_ENUM, 1 },

        { &dmItems->gndFaultTest,            "Ground Fault Test Enable",
                GND_FLT_TEST_DM,                DM_MBOOL, 1 },

        { &dmItems->gndFaultTestStatus,      "Ground Fault Test Status",
                GND_FLT_TEST_STATUS_DM,         DM_ENUM,  1 },

        { &dmItems->microTemperature,        "Micro Temperature",
                TEMPERATURE_DM,                 DM_INT16, 1 },

        { &dmItems->temperatureAlarm,        "Temperature Alarm",
                TEMPERATURE_ALARM_DM,           DM_ENUM, 1 },

        { &dmItems->tempWarnThresh,          "Temperature Warning Threshold",
                TEMP_WARN_THRESH_DM,            DM_INT16, 1 },

        { &dmItems->tempAlarmThresh,         "Temperature Alarm Threshold",
                TEMP_ALARM_THRESH_DM,           DM_INT16, 1 },

        { &dmItems->telemRelayCtrl,          "Telem Relay Control",
                TELEM_RELAY_CONTROL_DM,         DM_NAT32, 1 },

        { &dmItems->bitErrorRate,            "Bit Error Rate",
                BIT_ERROR_RATE_SHORT_DM,        DM_FLT32, 1 },

        { &dmItems->bitErrorRateLongTerm,    "Long Term Bit Error Rate",
                BIT_ERROR_RATE_LONG_TERM_DM,    DM_FLT32, 1 },

        { &dmItems->bitErrorRateAlarm,       "Bit Error Rate Alarm",
                BIT_ERROR_RATE_ALARM_DM,        DM_ENUM, 1 },

        { &dmItems->telemRecvPwr,  "Receive Power Alarm",
                TELEM_RECV_PWR_ALARM_DM,        DM_MBOOL, 1 },

        { &dmItems->telemFDDIStatus,  "FDDI Status Alarm",
                TELEM_FDDI_STATUS_ALARM_DM,     DM_MBOOL, 1 },

        { &dmItems->telemLaserStatus,  "Laser Status Alarm",
                TELEM_LASER_STATUS_ALARM_DM,    DM_MBOOL, 1 },

        { &dmItems->telemSubMuxError,  "Sub Mux Board Error Alarm",
                TELEM_SUB_MUX_ALARM_DM,         DM_MBOOL, 1 },

        { NO_ITEM, NULL, NULL, DM_ENDT, 0 } };

                                  /* Initialize Telem Board DM Items       */
    for (item = 0; telemDmInit[item].dmItem != NO_ITEM; item++)
    {
        if ((*telemDmInit[item].dmItem =
             initMicroDmItem(telemBoardDmPrefix, telemDmInit[item].name,
             telemDmInit[item].type, telemDmInit[item].len)) == ERROR)
        {                         /* Error occurred so print message & exit*/
            logMsg("%s: Error Initializing %s DM Item\n",
                telemBoardName, telemDmInit[item].errorMsg);
            return(ERROR);
        } /* if */
    } /* for */

    return(OK);
} /* createTelemBoardAppDmItems() */

/****************************************************************************/
/* Function    : telemDmItemInit                                            */
/* Purpose     : Reads parameters from telem & update Data Manager items    */
/* Inputs      : Array of Data Manager Item handles & serial chan structure */
/* Outputs     : None, but Data Manager Items change                        */
/****************************************************************************/
    Void
telemDmItemInit( telemDmItems* dmItems, sio32Chan* telemDataChan )
{
    Int16 trys;
    Int16 gfStatus;
                                /* Send Command Enable Command to telem Micro*/
    for (trys = 0; ((microCmdEnable(telemDataChan) == ERROR) &&
         trys < MICRO_ENABLE_TRYS); trys++)
         taskDelay(sysClkRateGet() / 5);

    if (trys == MICRO_ENABLE_TRYS)
        logMsg("telem: Error Enabling telem Microcontroller\n");

                                /* Set Micro Identification initial value    */
    if (updateMicroIdentDm(telemDataChan, dmItems->microDm.idString) == ERROR)
        logMsg("telem: Error reading Identification String\n");

                                /* Set Micro Serial Number initial value     */
    if (updateSerialNoDm(telemDataChan, dmItems->microDm.serialNo) == ERROR)
        logMsg("telem: Error reading Micro Serial Number\n");

                                /* Set Software Revision initial value       */
    if (updateSoftwareRevDm(telemDataChan, dmItems->microDm.softwareRev)
        == ERROR) logMsg("telem: Error reading Software Revision\n");

                                /* Set Micro Ram Test Result value           */
    if (updateMicroRamTestDm(telemDataChan, dmItems->microDm.ramTest) == ERROR)
        logMsg("telem: Error checking micro RAM Test\n");

                                /* Set Micro Temperature Alarm initial value */
    if (updateTempAlarmDm(telemDataChan, dmItems->temperatureAlarm) == ERROR)
        logMsg("telem: Error checking temperature alarm status\n");

                                /* Set Micro Ground Fault Alarm initial value*/
    if (updateGndFltAlarmDm(telemDataChan, dmItems->localGndFaultAlarm)
        == ERROR)
        logMsg("telem: Error checking Ground Fault alarm status\n");

    if (updateGndFltTestDm(telemDataChan, dmItems->gndFaultTestStatus)
        == ERROR)
        logMsg("telem: Error checking Ground Fault Circuit Test status\n");

    if (updateTelemetryStatusDm(telemDataChan, dmItems) == ERROR)
        logMsg("telem: Error checking telemetry status\n");

} /* telemDmItemInit() */

/****************************************************************************/
/* Function    : telemwaitForSRQ                                            */
/* Purpose     : Waits for Service Rquest Packet from telem microcontroller */
/* Inputs      : Array of Data Manager Item handles & serial chan structure */
/*               task Id's for telem Input and Output Tasks                 */
/* Outputs     : Data Manager Items change & tasks states are modified      */
/****************************************************************************/
    MLocal Void
telemWaitForSRQ( telemDmItems* dmItems, sio32Chan* telemDataChan,
    Int32 telemInputTask, Int32 telemOutputTask )
{
    taskResume(telemInputTask);     /* Resume telem input & output tasks    */
    taskResume(telemOutputTask);

    while (!processTelemSRQ(dmItems, telemDataChan));
                                    /* Repeat until a reset event occurs    */
                                    /* Wait for Service Requests from micro */

                                    /* We recevied a reset SRQ from micro   */
                                    /* Wait for In & Out tasks to complete  */
                                    /* any outstanding communications by    */
                                    /* taking the mutual exclusion semaphore*/
    semTake(telemDataChan->mutexSem, WAIT_FOREVER);

    taskSuspend(telemInputTask);    /* It's now safe to suspend In/Out tasks*/
    taskSuspend(telemOutputTask);

                                    /* Post Reset Event Data Manager Item   */
    microResetEventDm(dmItems->microDm.resetEvent);

    taskDelay(sysClkRateGet() / 3); /* Wait 300 msec for traffic to stop    */
                                    /* Release serial chan mutual exclusion */
    semGive(telemDataChan->mutexSem);
} /* telemWaitForSRQ() */

/****************************************************************************/
/* Function    : telemOutTask                                               */
/* Purpose     : Thruster Output Task. Read DM items and write them to telem*/
/* Inputs      : Array of Data Manager Item handles & serial chan structure */
/* Outputs     : Normally runs forever, but exits if error occurs           */
/****************************************************************************/
    Void
telemOutTask(Reg telemDmItems *dmItems, Reg sio32Chan *telemDataChan,
    telemBoardId telemBoard)
{
    DM_Group dmGroup;               /* telem Data Manager Group             */
    SEM_ID  dmUpdateSem;            /* Data Manager Item Change wakeup sem  */
    SEM_ID  powerReqSem;            /* Power Request Update Semaphore       */

    DWord   telemGroupBits;         /* Holds dm_group_changes bit vector    */

    DWord   resetEventBit;          /* Microcontroller reset event bit      */
    DWord   resetCmdBit;            /* Microcontroller reset item change bit*/
    DWord   gndFltTestBit;          /* Ground Fault test item changed bit   */
    DWord   tempWarnBit;            /* Temp warning thresh item change bit  */
    DWord   tempAlarmBit;           /* Temp alarm threshold item changed bit*/
    DWord   relayControlBit;        /* Telem Relay Control item changed bit */

    Word    telemPsuStatus;         /* Telemetry breakout board PSU Status  */

                                    /* Create switch & power sync semaphores*/
    if ( ((dmUpdateSem = semBCreate(SEM_Q_FIFO, SEM_EMPTY)) == NULL) ||
         ((powerReqSem = semBCreate(SEM_Q_FIFO, SEM_EMPTY)) == NULL) )
    {
        logMsg("telem: Error initializing Input Task Resources\n");
        return;
    } /* if */
                                    /* Declare this task a consumer of items*/
                                    /* which are written to the telem micro */
    dm_start_consumer(dmItems->microDm.resetEvent, DM_STATIC, dmUpdateSem);
    dm_start_consumer(dmItems->microDm.resetCmd,   DM_STATIC, dmUpdateSem);
    dm_start_consumer(dmItems->gndFaultTest,       DM_STATIC, dmUpdateSem);
    dm_start_consumer(dmItems->tempWarnThresh,     DM_STATIC, dmUpdateSem);
    dm_start_consumer(dmItems->tempAlarmThresh,    DM_STATIC, dmUpdateSem);
    dm_start_consumer(dmItems->telemRelayCtrl,     DM_STATIC, dmUpdateSem);

    dmGroup = dm_create_group();    /* Create Data Manager Group for items  */

    dm_group_add_item(dmGroup, dmItems->microDm.resetEvent, &resetEventBit);
    dm_group_add_item(dmGroup, dmItems->microDm.resetCmd,   &resetCmdBit);

    dm_group_add_item(dmGroup, dmItems->gndFaultTest,       &gndFltTestBit);
    dm_group_add_item(dmGroup, dmItems->tempWarnThresh,     &tempWarnBit);
    dm_group_add_item(dmGroup, dmItems->tempAlarmThresh,    &tempAlarmBit);
    dm_group_add_item(dmGroup, dmItems->telemRelayCtrl,     &relayControlBit);

    FOREVER
    {                               /* Reset received so set Data Manager to*/
                                    /* reflect micro state for dm items     */
                                    /* Disable Ground Fault Testing, if on  */
        microWriteGndFaultTest(telemDataChan, dmItems->gndFaultTest);

        initTempAlarmThreshDm( telemDataChan, dmItems->tempWarnThresh,
            dmItems->tempAlarmThresh );

        if (telemBoard == ROV_TELEM_BOARD)
            telemSetRelayState(telemDataChan, TELEM_GF_ENABLE);

        initTelemRelayStateDm( telemDataChan, dmItems->telemRelayCtrl );

                                    /* Power up test of breakout board PSU's*/
        telemPsuStatus = testTelemPowerSupply(telemDataChan);
        urgentWriteDmItem(dmItems->telemPsuStatus, &telemPsuStatus,
                    sizeof(telemPsuStatus));

                                    /* Clear data manager item changed bits */
        telemGroupBits = dm_get_group_changes(dmGroup);

        FOREVER                     /* Repeat until micro controller reset  */
        {                           /* Wait for any data items to change    */
            semTake(dmUpdateSem, WAIT_FOREVER);

                                    /* Ask Data Manager which items changed */
            telemGroupBits = dm_get_group_changes(dmGroup);

                                    /* Check if micro Reset Event occurred  */
            if (telemGroupBits & resetEventBit)
                break;              /* Exit loop and re-initialize DM items */

            taskSafe();             /* Stop task from being deleted during  */
                                    /* serial IO and Data Manager updates   */

                                    /* Send Set Temp Warn/Alarm Threshold Cmd*/
            if ( (telemGroupBits & tempWarnBit) ||
                 (telemGroupBits & tempAlarmBit))
                writeTempAlarmThresh(telemDataChan, dmItems->tempWarnThresh,
                                     dmItems->tempAlarmThresh);

                                    /* Check if Relay Control item changed  */
            if (telemGroupBits & relayControlBit)
                                    /* Send Relay Control Command to Micro  */
                telemSetRelayControlCmd(telemDataChan,
                        dmItems->telemRelayCtrl);

            if (telemGroupBits & gndFltTestBit)
                microWriteGndFaultTest(telemDataChan, dmItems->gndFaultTest);

                                    /* Check if Reset Cmd request occurred  */
            if (telemGroupBits & resetCmdBit)
                                    /* Send Reset Command to Microcontroller*/
                microResetCmd(telemDataChan);

            taskUnsafe();           /* Task may now be safely deleted       */

        } /*  FOREVER */
    } /* FOREVER */

                      /* dm_stop_provider, dm_stop_consumer, dm_delete_group*/
                      /* etc are NOT required when the task exits as the    */
                      /* Data Manager does this automatically when the task */
                      /* is deleted via the taskDeleteHookAdd() and         */
                      /* dm_task_exit functions.                            */

                      /* That's all folks !                                 */

} /* telemOutTask() */

/****************************************************************************/
/* Function    : telemInTask                                                */
/* Purpose     : telem Diagnostic Input Task. Wakes up periodically, read   */
/*               data from the telem and write Data Manager Items           */
/* Inputs      : Array of Data Manager Item handles & serial chan structure */
/* Outputs     : Normally runs forever, but exits if error occurs           */
/****************************************************************************/
    Void
telemInTask(Reg telemDmItems* dmItems, sio32Chan* telemDataChan)
{
    SEM_ID  wakeupSem;              /* Used wake up this task periodically  */
    Int32   updates = 0;            /* Counts dm updates for rate calc      */
    Int32   chan;                   /* Sio32 channel counter                */

                                    /* Create wakeup semaphore              */
                                    /* timer for provider rate control      */
    if ( (wakeupSem = semBCreate(SEM_Q_FIFO, SEM_EMPTY)) == NULL)
    {
        logMsg("telem: Error initializing Input Task Resources\n");
        return;
    } /* if */
                                    /* Provider Task for dynamic data items */
    dm_start_provider(dmItems->microTemperature,  TEMP_DM_PERIOD);
    dm_start_provider(dmItems->localGfCurrent,    TELEM_DM_PERIOD);
    dm_start_provider(dmItems->telemPsuStatus,    TELEM_DM_PERIOD);
    dm_start_consumer(dmItems->telemPsuStatus,    TELEM_DM_PERIOD, SEM_NULL);

                                    /* Provider task for telem Statistics   */
    for (chan = 0; chan < SIO32_PORTS; chan++)
        dm_start_provider(dmItems->serialStats[chan],
            TELEM_DM_PERIOD * SIO32_PORTS);

                                    /* Update Rate is not guaranteed due to */
                                    /* the possibility of task preemption   */
    FOREVER
    {
        taskSafe();                 /* Stop task from being deleted during  */
                                    /* serial IO and Data Manager updates   */

                                    /* update data manager items we provide */
        if ((++updates % (TEMP_DM_PERIOD / TELEM_DM_PERIOD)) == 0)
        {
            updates = 0;
            updateMicroTempDm(telemDataChan, dmItems->microTemperature);
        } /* if */

                                    /* Check Power Supply Status            */
        updateTelemPSUStatusDm(dmItems->telemPsuStatus);

                                    /* Update ground fault current reading  */
        updateGndFltCurrentsDm(telemDataChan, dmItems->localGfCurrent);

                                    /* Update sio32 serial statistics       */
        for (chan = 0; chan < SIO32_PORTS; chan++)
            updateTelemSerialStatsDm(chan, dmItems->serialStats[chan]);

        taskUnsafe();               /* Task may now be deleted safely       */

                                    /* Pend until sem timeout wakes us up   */
        semTake(wakeupSem, sysClkRateGet() /
                (USECS_PER_SEC / TELEM_DM_PERIOD));
    } /* FOREVER */

    semDelete(wakeupSem);           /* semaphore resources                  */


                      /* dm_stop_provider, dm_stop_consumer, dm_delete_group*/
                      /* etc are NOT required when the task exits as the    */
                      /* Data Manager does this automatically when the task */
                      /* is deleted via the taskDeleteHookAdd() and         */
                      /* dm_task_exit functions.                            */

                      /* That's all folks !                                 */
} /* telemInTask() */

/****************************************************************************/
/* Function    : processTelemSRQ                                            */
/* Purpose     : Decode Service Request and take appropriate action         */
/* Inputs      : SRQ code, array of DM items, serial channel                */
/* Outputs     : Returns Reset Status & many DM items are changed           */
/****************************************************************************/
    LOCAL MBool
processTelemSRQ( Reg telemDmItems* dmItems, sio32Chan *telemDataChan)
{
    Word srqRequest;                /* Service Request Value                */
    Word tempAlarm;                 /* Telem Alarm Status Word              */
    Word alarmStatus;               /* Microcontroller Alarm Status Word    */
    Word telemStatus;
    gfStatus gfAlarm;               /* Ground Fault Alarm Status            */
    gfTestStatus gfTest;            /* Ground Fault Test Status             */

    Flt32 bitErrorCount, bitErrorRate;
    Byte buffer[SRQ_PACKET_LEN];    /* Buffer for micro communications data */

                                    /* Block on Reading Service Request     */
    if (readSRQPacket(telemDataChan, buffer, SRQ_PACKET_LEN,
            telemDataChan->timeout) != ERROR)
    {                               /* Service Request received so process  */
        srqRequest = wordFromBuf(buffer, 0);

        switch (srqRequest)             /* Decode Service Request from micro*/
        {
            case MICRO_RESET_SRQ:       /* Microcontroller Reset Occurred   */
                logMsg("telem Reset recvd\n");
                return(TRUE);           /* Return Reset Received Status     */
                break;

            case TEMP_ALARM_ON:         /* Temperature Alarm Status Change  */
                wordsFromBuf(buffer, 2, &srqRequest, &tempAlarm);

                                        /* Update Temperature & Alarm Status*/
                updateTempAlarm(telemDataChan, (microAlarm) tempAlarm,
                    dmItems->temperatureAlarm, dmItems->microTemperature);
                break;

            case GF_TEST_FAIL_ON_SRQ:
                gfTest = GF_TEST_FAIL_ON;
                writeDmItem(dmItems->gndFaultTestStatus, &gfTest,
                    sizeof(gfTest));
                break;

            case GF_TEST_FAIL_OFF_SRQ:
                gfTest = GF_TEST_FAIL_OFF;
                writeDmItem(dmItems->gndFaultTestStatus, &gfTest,
                    sizeof(gfTest));
                break;

            case LOCAL_GNDFLT_SRQ:
                wordsFromBuf(buffer, 2, &srqRequest, &alarmStatus);
                gfAlarm = (gfStatus) alarmStatus;
                writeDmItem(dmItems->localGndFaultAlarm, &gfAlarm,
                    sizeof(gfAlarm));
                break;

            case BIT_ERROR_COUNT_SRQ:
                checkTelemBitErrorRate(
                    wordFromBuf(buffer, sizeof(Word)), dmItems->bitErrorRate,
                    dmItems->bitErrorRateLongTerm, dmItems->bitErrorRateAlarm);
                break;

            case TELEM_STATUS_ALARM_SRQ:
                telemStatus = wordFromBuf(buffer, sizeof(Word));

                if ((telemStatus     & TELEM_RECV_PWR) !=
                    (telemStatusWord & TELEM_RECV_PWR))
                {
                    writeBooleanDmItem(dmItems->telemRecvPwr,
                         telemStatus & TELEM_RECV_PWR      ? TRUE : FALSE);

                    writeBooleanDmItem(dmItems->telemFDDIStatus,
                         telemStatus & TELEM_FDDI_STATUS   ? TRUE : FALSE);

                    writeBooleanDmItem(dmItems->telemLaserStatus,
                         telemStatus & TELEM_LASER_STATUS  ? TRUE : FALSE);

                    writeBooleanDmItem(dmItems->telemSubMuxError,
                         telemStatus & TELEM_SUB_MUX_ERROR ? TRUE : FALSE);

                    if (!(telemStatus & TELEM_RECV_PWR))
                    {
                        telemBitError.berHoldoff = TRUE;
                        startIntervalTimer(telemBitError.berResetTimer,
                           BIT_ERROR_RESET_DELAY, 0);
                    } /* if */
                }  /* if */

                telemStatusWord = telemStatus;
                break;

            default:
                printf("telem: Unrecognized Service Request 0x%x\n",
                    srqRequest);
        } /* switch */

    } /* if */

    return(FALSE);                      /* Return Reset Received Status     */
} /* processTelemSRQ() */

    MLocal Void
initTelemBitErrorRate( Void )
{
    Int32 ptr;
    MLocal timerCreated = FALSE;

    telemBitError.count        = 0;
    telemBitError.avgCount     = 0;
    telemBitError.totalCount   = 0;
    telemBitError.rate         = 0.0;
    telemBitError.longTermRate = 0.0;
    telemBitError.warnThresh   = BIT_ERROR_RATE_WARN;
    telemBitError.alarmThresh  = BIT_ERROR_RATE_ALARM;
    telemBitError.alarm        = MICRO_ALARM;
    telemBitError.berHoldoff   = FALSE;

    clock_gettime(CLOCK_REALTIME, &telemBitError.updateTime);
    telemBitError.startTime = telemBitError.updateTime.tv_sec;

    for ( ptr = 0; ptr < TELEM_HIST_COUNT; ptr++ )
        telemBitError.countHistory[ptr] = 0;

    telemBitError.histPtr = 0;

    if (!timerCreated)
    {
        timer_create(CLOCK_REALTIME, NULL, &telemBitError.berResetTimer);
        timer_connect(telemBitError.berResetTimer, initTelemBitErrorRate, 0);
        timerCreated = TRUE;
    } /* if */
} /* initTelemBitErrorRate() */


    MLocal Void
checkTelemBitErrorRate(Nat16 bitErrorCount, DM_Item bitErrorRateDm,
    DM_Item bitErrorRateLongTermDm, DM_Item bitErrorAlarmDm)
{
    struct timespec timeNow;
    microAlarm berAlarm;
    Flt32 deltaTime;
    Int32 ptr;

    if (telemBitError.berHoldoff)
        return;

    if (telemStatusWord & TELEM_RECV_PWR)
        initTelemBitErrorRate();

                                       /* Calculate time since last update  */
    clock_gettime(CLOCK_REALTIME, &timeNow);
    telemBitError.count = bitErrorCount;

    deltaTime =
        (Flt32)  (timeNow.tv_sec  - telemBitError.updateTime.tv_sec) +
        (Flt32) ((timeNow.tv_nsec - telemBitError.updateTime.tv_nsec)) /
               1000000000;
    if (deltaTime <= 0.0) deltaTime = 1.0;

                                        /* Save error count in history buffer*/
    telemBitError.countHistory[telemBitError.histPtr++] =
        telemBitError.count;

    if (telemBitError.histPtr >= TELEM_HIST_COUNT)
        telemBitError.histPtr = 0;      /* Fix up history buffer pointer    */

    telemBitError.avgCount = 0; /* Sum up last N samples            */
    for ( ptr = 0; ptr < TELEM_HIST_COUNT; ptr++ )
        telemBitError.avgCount += telemBitError.countHistory[ptr];
                                        /* Calculate mean error count       */
    telemBitError.avgCount /= TELEM_HIST_COUNT;
                                        /* Calc short term bit error rate   */
    telemBitError.rate = ((Flt32) telemBitError.avgCount) /
        (TELEM_BIT_RATE * deltaTime);

    deltaTime = (Flt32)             /* Calculate long term time interval*/
        (telemBitError.updateTime.tv_sec - telemBitError.startTime);
    if (deltaTime <= 0.0) deltaTime  = 1.0;

    telemBitError.updateTime.tv_sec  = timeNow.tv_sec;
    telemBitError.updateTime.tv_nsec = timeNow.tv_nsec;

                                        /* Acculmulate total error count    */
    telemBitError.totalCount   += telemBitError.count;
    telemBitError.longTermRate = ((Flt32) telemBitError.totalCount) /
            (TELEM_BIT_RATE * deltaTime);

    writeDmItem(bitErrorRateDm, &telemBitError.rate,
        sizeof(telemBitError.rate));

    writeDmItem(bitErrorRateLongTermDm, &telemBitError.longTermRate,
         sizeof(telemBitError.longTermRate));

    if (!(telemStatusWord & TELEM_RECV_PWR))
    {
        berAlarm = telemBitError.alarm;
        if (telemBitError.rate >= telemBitError.alarmThresh)
        {
            if (berAlarm != MICRO_ALARM)
                berAlarm = MICRO_ALARM;
        } /* if */

        else if (telemBitError.rate >= telemBitError.warnThresh)
        {
            if (berAlarm != MICRO_WARNING)
                berAlarm = MICRO_WARNING;
        } /* else */

        else if (berAlarm != MICRO_ALARM_OK)
            berAlarm = MICRO_ALARM_OK;

        if (berAlarm != telemBitError.alarm)
        {
            writeDmItem(bitErrorAlarmDm, &berAlarm, sizeof(berAlarm));
            telemBitError.alarm = berAlarm;
        } /* if */
    } /* if */
} /* checkTelemBitErrorRate()*/




