/*****************************************************************************/
/* Copyright 1992 to 1996 MBARI                                              */
/*****************************************************************************/
/* Summary  : Lighting Dcon Interface Module for VxWorks                     */
/* Filename : lightingDcon.c                                                 */
/* Author   : Andrew Pearce                                                  */
/* Project  : Tiburon ROV                                                    */
/* Version  : Version 1.0                                                    */
/* Created  : 11/21/94                                                       */
/* Modified : 08/08/96                                                       */
/* Archived :                                                                */
/*****************************************************************************/
/* Modification History:                                                     */
/* $Header: lightingDcon.c,v 1.1 94/12/06 13:35:27 pean Exp $
 * $Log:        lightingDcon.c,v $
 * Revision 1.1  94/12/06  13:35:27  13:35:27  pean (Andrew Pearce)
 * Initial revision
 *
  */
/*****************************************************************************/

#include <vxWorks.h>                /* vxWorks system declarations           */
#include <semLib.h>                 /* vxWorks semaphore functions           */
#include <taskLib.h>                /* vxWorks task library functions        */
#include <systime.h>                /* vxWorks time and date functions       */
#include <strLib.h>                 /* vxWorks string function library       */
#include <msgQLib.h>                /* vxWorks Message Queue Library         */
#include <timers.h>                 /* vxWorks timer library                 */

#include <mbariTypes.h>             /* MBARI style guide type declarations   */
#include <mbariConst.h>             /* Miscellaneous constants               */

#include <rovPriority.h>            /* MBARI Rov Application Priorities      */
#include <usrTime.h>                /* MBARI time and date functions         */
#include <timerWrap.h>              /* MBARI wapper for timer functions      */

#include <datamgr.h>                /* Data Manager declarations             */
#include <dm_errno.h>               /* Data Manager error declarations       */

#include "powerAlloc.h"             /* Device Power Requirements             */
#include "powerDM.h"                /* Power System Data Manager Items       */
#include "powerTypes.h"             /* Power management Data Types           */
#include "console.h"                /* Topside Console Data Manager Items    */

#include "sio32.h"                  /* Sio23 hardware and driver information */
#include "sio32Server.h"            /* Sio32 server communication services   */
#include "sio32Client.h"            /* Sio32 client communication services   */
#include "lightingDcon.h"           /* Lighting Dcon Can Definitions         */
#include "filter.h"                 /* median filter data structers          */

#include "applic.h"                 /* Microcontroller applications          */
#include "microcmd.h"               /* Microcontroller commands              */
#include "microDm.h"                /* Micro Data Manager Items & Functions  */
#include "microTask.h"              /* Microcontroller Task IO definitions   */

#define Boolean MBool               /* Map microcontroller Boolean to MBool  */

#include "timer.h"                  /* IBC Micro Timer Definitions           */
#include "ibc_card.h"               /* IBC Card Types                        */
#include "ibc_cmd.h"                /* IBC Serial Commands                   */
#include "filter.h"                 /* IBC General IIR Filter Routines       */
#include "gf_board.h"               /* GF/5V Controller Board Definitions    */
#include "low_pwr.h"                /* Low Power Switch Board Definitions    */

#include "ibc.h"                    /* Function Prototypes for ibc.c         */
#include "switch.h"                 /* IBC Function Libarary Definitions     */
#include "ibcDm.h"                  /* IBC Data Manager Interface Functions  */
#include "hpsDm.h"                  /* IBC HPS Board Data Manager Functions  */
#include "gf_5vDm.h"                /* IBC Ground Fault Board DMgr Functions */
#include "gf_5v.h"                  /* IBC Ground Fault Board Definitions    */
#include "vicorDm.h"                /* IBC Dual Vicor Board Interface Funcs  */

#include "lighting.h"               /* Lighting Dcon Micro definitions       */

                                    /* Lighting Dcon Device Name             */
MLocal char* const lightDconName = "Lighting Dcon";
                                    /* Lighting Dcon DM Item Name Prefix     */
MLocal char* const lightDconDmPrefix = LIGHTING_IBC_DM_PREFIX;

#define GF5V_BOARD_ADDR             (Word) GROUND_FAULT_0_ADDR
#define LPS_BOARD_ADDR              (Word) LOW_PWR_SWITCH_48V_0_ADDR
#define VICOR_BOARD_ADDR            (Word) DUAL_VICOR_0_ADDR

#define ISOLATED_IO_BOARD_0_ADDR    (Word) ISO_IO_0_ADDR
#define ISOLATED_IO_BOARD_1_ADDR    (Word) ISO_IO_1_ADDR

#define HPS_BOARDS             3    /* Number of HPS Board in Lighting Dcon  */
#define STATUS_TASK  AUX_IN_TASK    /* Light status task index               */

MLocal Word hpsBoardAddr[HPS_BOARDS] =  /* HPS Board Assignments             */
{
    HIGH_PWR_SWITCH_0_ADDR,
    HIGH_PWR_SWITCH_1_ADDR,
    HIGH_PWR_SWITCH_2_ADDR
};

MLocal ibcCardConfig ibcCardSet =
{
    HPS_BOARDS + 5,                 /* Number of IBC Cards Expected          */
    {
         CPU196,
         GF5V_BOARD_ADDR,
         VICOR_BOARD_ADDR,
         HIGH_PWR_SWITCH_0_ADDR,
         HIGH_PWR_SWITCH_1_ADDR,
         HIGH_PWR_SWITCH_2_ADDR,
         ISOLATED_IO_BOARD_0_ADDR,
         ISOLATED_IO_BOARD_1_ADDR,
    }
};

                                    /* Isolated IO Board 0 Assignments       */
#define LIGHT_0_CHAN                     0 /* Light 0 On/Off channel         */
#define LIGHT_1_CHAN                     1 /* Light 1 On/Off channel         */
#define LIGHT_2_CHAN                     2 /* Light 2 On/Off channel         */
#define LIGHT_3_CHAN                     3 /* Light 3 On/Off channel         */
#define BALLAST_0_RETURN_CHAN            4 /* Ballast 0 power return channel */
#define BALLAST_1_RETURN_CHAN            5 /* Ballast 1 power return channel */
#define LIGHT_ISOLATE_0_CHAN             6 /* Isolation Switch Lights 0 & 1  */
#define LIGHT_ISOLATE_2_CHAN             7 /* Isolation Switch Lights 2 & 3  */

                                    /* Isolated IO Board 1 Assignments       */
#define LIGHT_4_CHAN                     0 /* Light 4 On/Off channel         */
#define LIGHT_5_CHAN                     1 /* Light 5 On/Off channel         */
#define BALLAST_2_RETURN_CHAN            4 /* Ballast 2 power return channel */
#define LIGHT_ISOLATE_4_CHAN             6 /* Isolation Switch Lights 4 & 5  */


#define LIGHTING_IBC_DM_PERIOD  100000 /* Lighting Dcon Data period 10 Hz    */
#define LIGHT_STATUS_DM_PERIOD  500000 /* Lighting Dcon Data period  2 Hz    */
#define HUMIDITY_DM_PERIOD     2000000 /* Humidity sensor period .5Hz        */

#define NUM_TASKS                 4 /* Number of co-operating tasks          */

typedef struct                      /* Lighting Dcon Data Mgr Item Handles   */
{
    DM_Item powerSwitch;            /* Data Concentrator Power Switch        */
    DM_Item canWaterAlarm;          /* Housing Water Alarm Detected          */
    DM_Item jboxWaterAlarm;         /* J Box Water Alarm Detected            */
    DM_Item waterAlarmThresh;       /* Water Alarm Threshold                 */

    ibcCpuDmItems  ibcDm;           /* IBC CPU Data Manager Items            */
    ibcGf5vDmItems gf5vDm;          /* GF/5V Board Data Manager Items        */
    ibcVicorDmItems vicorDm;        /* Dual Vicor Board Data Manager Items   */
    ibcHpsDmItems hpsDm[HPS_BOARDS];/* HPS Board Data Manager Items          */

    DM_Item ballastGfCheck[NUM_LIGHT_BALLASTS];
} lightDconDmItems;

typedef struct
{
    switchEntry lightSwitch;        /* Light Switch entry for ibc switches   */
    DM_Item     dimLevelDm;         /* Dim Level Data Mgr Item               */
    DM_Item     lightOnDm;          /* Confirms Lamp On/Off state            */
    DWord       switchChangedBit;   /* Light Switch changed bits             */
    DWord       dimLevelBit;        /* Dim Level Changed bit                 */

    timer_t     switchTimer;        /* Timer for light switch operations     */
    timer_t     currentTimer;       /* Timer for light current check         */
    MBool       switchPending;      /* Light switch operation pending        */
    MBool       checkLightCurrent;  /* Set if lamp current should be checked */
} lightSwitchDataStruct;

/*****************************************************************************/

Void lightingDconInTask(Reg lightDconDmItems *dmItems,
    microIOControl *microIOCtrl);

Void lightingDconOutTask(Reg lightDconDmItems* dmItems,
    microIOControl *microIOCtrl);

Void lightStatusTask( microIOControl *microIOCtrl, switchEntry *ballast0Switch,
    switchEntry *ballast1Switch, switchEntry *ballast2Switch,
    lightSwitchDataStruct lightSwitch[NUM_LIGHTS]);

MLocal Word processLightingDconSRQ(lightDconDmItems *dmItems,
    microIOControl *microIOCtrl);

MLocal Void initLightingDconDmItems(lightDconDmItems *dmItems,
    sio32Chan* lightingDconDataChan);

MLocal Void initLightSwitchStruct( vehicleLights lightId,
    lightSwitchDataStruct *light, DM_Group lightGroup, SEM_ID dmUpdateSem );

MLocal STATUS switchBallastPower( switchEntry *ballastSwitch );
MLocal STATUS switchLightPower( lightSwitchDataStruct *light );

MLocal Void lightSwitchHandler(timer_t timer,  lightSwitchDataStruct *light);
MLocal Void lightCurrentHandler(timer_t timer, lightSwitchDataStruct *light);
MLocal STATUS setLightDimLevelDm( lightSwitchDataStruct *light );

MLocal Void checkBallastCurrent(switchEntry *ballast, DM_Item lampAlarmDm,
    Int16 *lastExpCurrent, lightSwitchDataStruct *light0,
    lightSwitchDataStruct *light1);

/*****************************************************************************/
/* Function    : lightingCanGetWaterAlarm                                    */
/* Purpose     : Read water alarm status from Lighting Dcon microcontroller  */
/* Inputs      : Sio32 socket and pointer to water alarm status              */
/* Outputs     : Returns OK or ERROR                                         */
/*****************************************************************************/
    LOCAL STATUS
lightingCanGetWaterAlarm( sio32Chan *IBC_SerialChan, Word *waterAlarmStatus )
{
                                    /* Send command to microcontroller       */
    return( microCommand(IBC_SerialChan, LIGHTING_IBC_APP | GET_WATER_ALARM,
                        waterAlarmStatus) );
} /* lightingCanGetWaterAlarm() */

/*****************************************************************************/
/* Function    : lightingGetWaterAlarmThresh                                 */
/* Purpose     : Reads water alarm threshold value from IBC CPU              */
/* Inputs      : Sio32 channel, pointer to water alarm threshold             */
/* Outputs     : Returns OK or ERROR                                         */
/*****************************************************************************/
    LOCAL STATUS
lightingGetWaterAlarmThresh( sio32Chan *IBC_SerialChan,
    Int16 *waterAlarmThresh )
{
                                    /* Send command to microcontroller       */
    return (microCommand( IBC_SerialChan,
          LIGHTING_IBC_APP | GET_WATER_ALARM_THRESH, waterAlarmThresh) );
} /* lightingGetWaterAlarmThresh() */

/*****************************************************************************/
/* Function    : initWaterAlarmThreshDm                                      */
/* Purpose     : Read Water Alarm Threshold and update data manager item     */
/* Inputs      : Sio32 channel, water alarm threshold dm item handle         */
/* Outputs     : Returns OK or ERROR                                         */
/*****************************************************************************/
    LOCAL STATUS
initWaterAlarmThreshDm( sio32Chan *IBC_SerialChan,
    DM_Item waterAlarmThreshDm )
{
    Int16   waterAlarmThresh;           /* Water Alarm Threshold from IBC    */

                                        /* Get Water Alarm Threshold         */
    if (lightingGetWaterAlarmThresh( IBC_SerialChan, &waterAlarmThresh )
        != OK) return (ERROR);
                                        /* We MUST use urgent open here as   */
                                        /* another task is normal provider   */
    return (urgentWriteDmItem(waterAlarmThreshDm, &waterAlarmThresh,
                      sizeof(waterAlarmThresh)) );
} /* initWaterAlarmThreshDm() */

/*****************************************************************************/
/* Function    : writeWaterAlarmThresh                                       */
/* Purpose     : Get water alarm threshold value from dm and send to IBC     */
/* Inputs      : Sio32 channel, water threshold dm item handle               */
/* Outputs     : Returns OK or ERROR                                         */
/*****************************************************************************/
    LOCAL STATUS
writeWaterAlarmThresh( sio32Chan *sio32DataChan, DM_Item waterAlarmThreshDm )
{
    Errno   status;                 /* Result of dm_read                     */
    Int16   waterAlarmThresh;       /* Water Alarm Threshold                 */

                                    /* Read Water Alarm Thresh from dmgr     */
    status = dm_read( waterAlarmThreshDm, (char *) &waterAlarmThresh,
         sizeof(waterAlarmThresh), (DM_Time *) NULL);
                                        /* Check for successful dm_read      */
    if ((status == SUCCESS) || (status == EDM_NOPROVIDER))

                                    /* Send command to microcontroller       */
        if ( sendMicroCommand( sio32DataChan, &status, sizeof(status), 2,
             LIGHTING_IBC_APP | SET_WATER_ALARM_THRESH, waterAlarmThresh )
             != ERROR )
                                    /* Check command result                  */
            return (wordFromBuf(&status, 0) == CMD_OK ? OK : ERROR);

    return(ERROR);
} /* writeWaterAlarmThresh() */

/*****************************************************************************/
/* Function    : updateWaterAlarmDm                                          */
/* Purpose     : Reads micro Water Alarm status & updates dmgr items         */
/* Inputs      : Sio32 chan & water alarm status data manager item handles   */
/* Outputs     : Returns OK or ERROR                                         */
/*****************************************************************************/
    LOCAL STATUS
updateWaterAlarmDm( sio32Chan *sio32DataChan,
    DM_Item canWaterAlarmDm, DM_Item jboxWaterAlarmDm )
{
    Word waterAlarmStatus;          /* Microcontroller Water Alarm Status    */
    MBool status;                   /* Boolean state of Water Alarm          */
                                    /* Read Water Alarm Status               */
    if (lightingCanGetWaterAlarm(sio32DataChan, &waterAlarmStatus) != OK)
        return(ERROR);

    status = ( (waterAlarmStatus & HOUSING_WATER_ALARM_BIT) ? TRUE : FALSE);
    writeDmItem(canWaterAlarmDm, &status, sizeof(status));

    status = ( (waterAlarmStatus & JBOX_WATER_ALARM_BIT) ? TRUE : FALSE);

                                    /* Write water alarm status to Dmgr      */
    return ( writeDmItem(jboxWaterAlarmDm, &status, sizeof(status)) );
} /* updateWaterAlarmDm() */

/*****************************************************************************/
/* Function    : createLightingDconAppDmItems                                */
/* Purpose     : Create Data Mgr Items specific to Lighting Dcon Application */
/* Inputs      : Pointer to structure of DM item handles, Dcon DM prefix,    */
/*               Lighting DM prefix, Lighting Data Concentrator Name         */
/* Outputs     : Returns OK or ERROR                                         */
/*****************************************************************************/
    LOCAL STATUS
createLightingDconAppDmItems( lightDconDmItems *lightingDmItems,
     const char* lightDconDmPrefix )
{
    dmItemList lightDconDmInit[] =

     {  { &lightingDmItems->canWaterAlarm,     "Housing Water Alarm",
                HOUSING_WATER_ALARM_DM,           DM_MBOOL, 1 },

        { &lightingDmItems->jboxWaterAlarm,    "J Box Water Alarm",
                JBOX_WATER_ALARM_DM,              DM_MBOOL, 1 },

        { &lightingDmItems->waterAlarmThresh,  "Water Alarm Threshold",
                WATER_ALARM_THRESH_DM,            DM_NAT16, 1 },

        { NO_ITEM, NULL, NULL, DM_ENDT, 0 } };

                                 /* Initialize Data Manager Items from table */
    if (initMicroDmItemList( lightDconDmInit, lightDconDmPrefix,
        "Lighting Dcon I/F" ) != OK)
        return(ERROR);

    if ( (lightingDmItems->powerSwitch = initMicroDmItem(
         "", LIGHTING_DCON_48V_SWITCH_STATUS_DM, DM_ENUM, 1)) == ERROR)
    {
       logMsg("Lighting Dcon: Error initializing Dcon Power Switch Item\n");
       return (ERROR);
    } /* if */

                                 /* Declare this task an item provider       */
    dm_start_provider(lightingDmItems->canWaterAlarm,  DM_STATIC);
    dm_start_provider(lightingDmItems->jboxWaterAlarm, DM_STATIC);

    dm_start_consumer(lightingDmItems->powerSwitch, DM_STATIC, SEM_NULL);

    return(OK);
} /* createLightingDconAppDmItems() */

/******************************************************************************/
/* Function    : lightingDconTask                                             */
/* Purpose     : Initializes communication with Lighting Data Concentrator.   */
/*               Provides interface between Lighting Data Concentrator and    */
/*               Data Manager items.                                          */
/* Inputs      : SIO32 serial channel number for Lighting Data Concentrator   */
/*               Physically maps the Data Concentrators to a serial channel   */
/* Outputs     : Normally runs forever, but returns ERROR on fatal error      */
/******************************************************************************/
    STATUS
lightingDconTask( Nat16 serialChannel )
{
    lightDconDmItems dmItems;       /* Lighting Application DM item handles   */
    microIOControl microIOCtrl;     /* Microcontroller IO Task control        */
    Int16   board;                  /* High Power Switch Board Index          */

                                    /* Initialize serial channel and task   */
                                    /* synchronization resources            */
    if (initMicroTaskIO( &microIOCtrl, NUM_TASKS, serialChannel,
       lightDconDmPrefix, "/sio32/lightingDcon",
       LIGHTING_DCON_48V_SWITCH_STATUS_DM ) == ERROR)
    {
        logMsg("Error initializing IO for ERS DCon");
        return(ERROR);
    } /* if */
                                   /* Clear Lighting DCon DM handles          */
    bzero((char *) &dmItems, sizeof(dmItems));

                                   /* Create IBC CPU Data Manager Items       */
    if (createIbcCpuDmItems(&dmItems.ibcDm, lightDconDmPrefix,
        lightDconName) == ERROR)
    {                              /* Error occurred so print message & exit  */
        sio32ChanDestroy( serialChannel, &microIOCtrl.sio32DataChan );
        return(ERROR);
    } /* if */

                                   /* Create Lighting Dcon Application Items  */
    if (createLightingDconAppDmItems(&dmItems, lightDconDmPrefix)
       == ERROR)
    {                              /* Error occurred so print message & exit  */
        sio32ChanDestroy( serialChannel, &microIOCtrl.sio32DataChan );
        return(ERROR);
    } /* if */

                                   /* Create GF/5V Board Data Manager Items   */
    createIbcGf5vDmItems( &dmItems.gf5vDm, lightDconDmPrefix, lightDconName );
    initIbcGf5vAlarmProvider( &dmItems.gf5vDm );

                                   /* Create IBC Dual Vicor DMgr Items        */
    createIbcVicorDmItems( dmItems.vicorDm, lightDconDmPrefix, lightDconName );
    initIbcVicorAlarmProvider( dmItems.vicorDm );

    for (board = 0; board < HPS_BOARDS; board++)
    {                              /* Create IBC HPS Data Manager Items       */
        createIbcHpsDmItems( &dmItems.hpsDm[board], lightDconDmPrefix,
                lightDconName, board );
        initIbcHpsAlarmProvider( &dmItems.hpsDm[board] );
    } /* for */

                                    /* Initialize Lighting Dcon Data Out Task */
    if ((microIOCtrl.childTasks[OUT_TASK].taskId =
         taskSpawn("lightDconOut", LIGHTING_DCON_OUT_PRIORITY, 0,
         LIGHTING_DCON_OUT_STACKSIZE, (FUNCPTR) lightingDconOutTask,
         (int) &dmItems, (int) &microIOCtrl, 0, 0, 0, 0, 0, 0, 0, 0)) == ERROR)
    {
                                    /* Close serial communication channel     */
        sio32ChanDestroy( serialChannel, &microIOCtrl.sio32DataChan );
        logMsg("Lighting Dcon: Error spawning data output task\n");
        return(ERROR);
    } /* if */

                                    /* Initialize Lighting Dcon Data In Task  */
    if (( microIOCtrl.childTasks[IN_TASK].taskId =
         taskSpawn( "lightDconIn", LIGHTING_DCON_IN_PRIORITY, 0,
         LIGHTING_DCON_IN_STACKSIZE, (FUNCPTR) lightingDconInTask,
         (int) &dmItems, (int) &microIOCtrl, 0, 0, 0, 0, 0, 0, 0, 0) ) == ERROR)
    {                               /* Delete Lighting Dcon In Task           */
        taskDelete(microIOCtrl.childTasks[OUT_TASK].taskId);
        sio32ChanDestroy( serialChannel, &microIOCtrl.sio32DataChan );
        logMsg("Lighting Dcon: Error spawning data input task\n");
        return(ERROR);
    } /* if */

    FOREVER
    {                               /* Check if Dcon is switched on           */
        if ((microDevPowerSwitchState(&microIOCtrl) != SWITCH_ON) ||
           ((microDevPowerSwitchState(&microIOCtrl) == SWITCH_ON) &&
            (microIOCtrl.resetEventCount != 0)))

                                    /* Process SRQ's from micro               */
           while(processLightingDconSRQ(&dmItems, &microIOCtrl)
               != MICRO_RESET_SRQ);

        microIOCtrl.resetEventCount++;   /* Count reset events ffrom micro    */

                                    /* Reset event received from Micro so     */
                                    /* Mark serial coms link as up            */
        setSerialLinkState(&microIOCtrl, LINK_UP);

                                    /* Initialize Static IBC DM Items         */
        if (initIbcCpuDmItems( &dmItems.ibcDm, &microIOCtrl.sio32DataChan,
            lightDconName, ibcCardSet) == OK)

        {                           /* Initialize Lighting Dcon Applic Items  */
            initLightingDconDmItems( &dmItems, &microIOCtrl.sio32DataChan );

            updateIbcGf5vAlarmsDm( &microIOCtrl.sio32DataChan,
               GF5V_BOARD_ADDR, &dmItems.gf5vDm );

            updateIbcVicorAlarmsDm(&microIOCtrl.sio32DataChan,
               VICOR_BOARD_ADDR, dmItems.vicorDm );

            for (board = 0; board < HPS_BOARDS; board++)
                updateIbcHpsAlarmsDm( &microIOCtrl.sio32DataChan,
                   hpsBoardAddr[board], &dmItems.hpsDm[board] );

                                     /* Wake up output task to configure micro*/
            semGive(microIOCtrl.childTasks[OUT_TASK].taskSyncSem);
        } /* if */
        else
            setSerialLinkState(&microIOCtrl, LINK_DOWN);

    } /* FOREVER  */

                                    /* Delete Child Tasks                     */
    taskDelete(microIOCtrl.childTasks[IN_TASK].taskId);
    taskDelete(microIOCtrl.childTasks[OUT_TASK].taskId);

                                        /* Close serial communication chan   */
    sio32ChanDestroy( serialChannel, &microIOCtrl.sio32DataChan );

                        /* 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() and dm_task_exit function */

    return(OK);         /* That's all folks                                  */
} /* lightingDconTask() */

/*****************************************************************************/
/* Function    : initLightingDconDmItems                                     */
/* Purpose     : Reads parameters from Lighting Dcon & update Data Mgr items */
/* Inputs      : Array of Data Manager Item handles & serial chan structure  */
/* Outputs     : None, but Data Manager Items change                         */
/*****************************************************************************/
    LOCAL Void
initLightingDconDmItems( lightDconDmItems *dmItems,
    sio32Chan *lightingDconDataChan )
{
                                /* Set Micro Water Alarms initial value      */
    if (updateWaterAlarmDm(lightingDconDataChan,
            dmItems->canWaterAlarm, dmItems->jboxWaterAlarm) == ERROR)
        logMsg("Lighting Dcon: Error checking water alarm status\n");
} /* initLightingDconDmItems() */

/*****************************************************************************/
/* Function    : lightingDconOutTask                                         */
/* Purpose     : Lighting Dcon Out. Read DM items & write to Lighting Dcon   */
/* Inputs      : Array of Data Manager Item handles & serial chan structure  */
/* Outputs     : Normally runs forever, but exits if error occurs            */
/*****************************************************************************/
    Void
lightingDconOutTask(Reg lightDconDmItems *dmItems, microIOControl *microIOCtrl)
{
    DM_Group cpuGroup;              /* CPU & Misc Item Data Manager Group    */
    DM_Group gf5vGroup;             /* GF/5V Board Data Manager Group        */
    DM_Group vicorGroup;            /* Vicor Board Data Manager Group        */

    DM_Group hpsGroup[HPS_BOARDS];  /* HPS Board Data Manager Group          */

    SEM_ID  dmUpdateSem;            /* Data Manager Item Change wakeup sem   */
    SEM_ID  powerReqSem;            /* Power Request Update Semaphore        */

    Int16 board;                    /* HPS Board Index                       */
    vehicleLights light;            /* Light switch index                    */
    Int32 lightStatusTaskId;        /* Light status check task Id            */
    Int32 ballast;

    cpuUpdateBits   ibcCpuBits;     /* CPU Board Parameter Update Bits       */
    gf5vUpdateBits  gf5vBits;       /* GF/5V Board Paramater Update Bits     */
    vicorUpdateBits vicorBits;      /* Vicor Board Paramater Update Bits     */
    hpsUpdateBits   hpsBits[HPS_BOARDS];  /* HPS Board Parameter Update Bits */

    DWord   lightDconGroupBits;     /* Holds dm_group_changes bit vector     */
    DWord   waterAlarmThreshBit;    /* Water alarm threshold item change bit */

    DWord   lightDcon48VSwitchBit;  /* 48V Dcon Power Switch changed bit     */
    DWord   lightDcon240VSwitchBit; /* 240V Dcon Power Switch changed bit    */

    DWord   ballastSwitch0Bit;      /* Light Ballast 0 Switch changed bit    */
    DWord   ballastSwitch1Bit;      /* Light Ballast 1 Switch changed bit    */
    DWord   ballastSwitch2Bit;      /* Light Ballast 2 Switch changed bit    */

    DWord   ballastReturn0Bit;      /* Ballast 0 Return Switch changed bit   */
    DWord   ballastReturn1Bit;      /* Ballast 1 Return Switch changed bit   */
    DWord   ballastReturn2Bit;      /* Ballast 2 Return Switch changed bit   */

    DWord   portSizeLaserSwitchBit; /* Port Sizing Laser Switch changed bit  */
    DWord   stbdSizeLaserSwitchBit; /* Stbd Sizing Laser Switch changed bit  */

    DWord   ballastGfCheckBit[NUM_LIGHT_BALLASTS];

    DWord   lightingDconSwitchBit;  /* Lighting Dcon Switch Req changed bit  */

    switchEntry lightDcon48VSwitch; /* Lighting DCon 48V Switch              */
    switchEntry lightDcon240VSwitch;/* Lighting DCon 240V Switch             */

    switchEntry ballastSwitch0;     /* Light Ballast 0 Switch                */
    switchEntry ballastSwitch1;     /* Light Ballast 1 Switch                */
    switchEntry ballastSwitch2;     /* Light Ballast 2 Switch                */

    switchEntry portSizeLaserSwitch;  /* Port Sizing Laser Switch          */
    switchEntry stbdSizeLaserSwitch;  /* Stbd Sizing Laser Switch          */

                                    /* Light switch data structurei. Switch  */
                                    /* entries for lights are in this struct */
    lightSwitchDataStruct lightSwitch[NUM_LIGHTS];

/******************************************************************************/
/* The code is this task implements the following interconnected switches for
   the power can, lighting data concentrator, ballasts & lights.

              240V Dcon     HPS 0                    ISO IO 0/0
  240V Bus _____ \____________ \_____ ballast0 __ \_____ \______ light 0 _____
                         |               |          |                        |
                         |               |          |___ \______ light 1 ____|
                         |               |           ISO IO 0/1              |
                         |               |________________________ \_________|
                         |                                      Return
                         |
                         |  HPS 1                    ISO IO 0/2
                         |____ \_____ ballast1 __ \_____ \______ light 2 _____
                         |               |          |                        |
                         |               |          |___ \______ light 3 ____|
                         |               |           ISO IO 0/3              |
                         |               |________________________ \_________|
                         |                                      Return
                         |
                         |  HPS 2                    ISO IO 1/0
                         |____ \_____ ballast2 __ \_____ \______ light 4 ____
                                         |           |                       |
                                         |           |__ \______ light 5 ____|
                                         |           ISO IO 1/1              |
                                         |_________________________ \________|
                                                                Return

   */
/******************************************************************************/

                                    /* Create switch & power sync semaphores */
    if ( ((dmUpdateSem = semBCreate(SEM_Q_FIFO, SEM_EMPTY)) == NULL) ||
         ((powerReqSem = semBCreate(SEM_Q_FIFO, SEM_EMPTY)) == NULL) )
    {
        logMsg("Lighting Dcon: Error initializing Input Task Resources\n");
        return;
    } /* if */

    duplicateIbcSwitchEntry( &lightDcon48VSwitch,
        LIGHTING_DCON_48V_SWITCH_REQ_DM,  LIGHTING_DCON_48V_SWITCH_STATUS_DM,
        dmUpdateSem, lightDconName, "Lighting DCon 48V Switch");

    duplicateIbcSwitchEntry( &lightDcon240VSwitch,
        LIGHTING_DCON_240V_SWITCH_REQ_DM, LIGHTING_DCON_240V_SWITCH_STATUS_DM,
        dmUpdateSem, lightDconName, "Lighting DCon 240V Switch");

    initIbcSwitchEntry( &ballastSwitch0, SERIAL_CHAN,
        LIGHT_BALLAST_POWER,               B240,                 LIGHTS,
        HIGH_POWER_SWITCH,                 hpsBoardAddr[0],      0,
        LIGHT_BALLAST_0_SWITCH_REQ_DM,     LIGHT_BALLAST_0_SWITCH_STATUS_DM,
        dmUpdateSem,
        LIGHT_BALLAST_0_PWR_REQ_DM,        LIGHT_BALLAST_0_PWR_OK_DM,
        powerReqSem,                       lightDconName,
        "Lighting Ballast 0 Power Switch");

    initIbcSwitchEntry( &ballastSwitch1, SERIAL_CHAN,
        DSPL_LIGHT_BALLAST_POWER,          B240,                 LIGHTS,
        HIGH_POWER_SWITCH,                 hpsBoardAddr[1],      0,
        LIGHT_BALLAST_1_SWITCH_REQ_DM,     LIGHT_BALLAST_1_SWITCH_STATUS_DM,
        dmUpdateSem,
        LIGHT_BALLAST_1_PWR_REQ_DM,        LIGHT_BALLAST_1_PWR_OK_DM,
        powerReqSem,                       lightDconName,
        "Lighting Ballast 1 Power Switch");

    initIbcSwitchEntry( &ballastSwitch2, SERIAL_CHAN,
        DSPL_LIGHT_BALLAST_POWER,          B240,                 LIGHTS,
        HIGH_POWER_SWITCH,                 hpsBoardAddr[2],      0,
        LIGHT_BALLAST_2_SWITCH_REQ_DM,     LIGHT_BALLAST_2_SWITCH_STATUS_DM,
        dmUpdateSem,
        LIGHT_BALLAST_2_PWR_REQ_DM,        LIGHT_BALLAST_2_PWR_OK_DM,
        powerReqSem,                       lightDconName,
        "Lighting Ballast 2 Power Switch");

    initIbcSwitchEntry( &lightSwitch[0].lightSwitch, SERIAL_CHAN,
        HMI_LIGHT_POWER,                   B240,                 LIGHTS,
        ISOLATED_IO_POS_TRUE,       ISOLATED_IO_BOARD_0_ADDR,    LIGHT_0_CHAN,
        LIGHT_0_SWITCH_REQ_DM,             LIGHT_0_SWITCH_STATUS_DM,
        dmUpdateSem,
        LIGHT_0_PWR_REQ_DM,                LIGHT_0_PWR_OK_DM,
        powerReqSem,                       lightDconName,
        "Light 0 Power Switch");

    initIbcSwitchEntry( &lightSwitch[1].lightSwitch, SERIAL_CHAN,
        HMI_LIGHT_POWER,                   B240,                 LIGHTS,
        ISOLATED_IO_POS_TRUE,       ISOLATED_IO_BOARD_0_ADDR,    LIGHT_1_CHAN,
        LIGHT_1_SWITCH_REQ_DM,             LIGHT_1_SWITCH_STATUS_DM,
        dmUpdateSem,
        LIGHT_1_PWR_REQ_DM,                LIGHT_1_PWR_OK_DM,
        powerReqSem,                       lightDconName,
        "Light 1 Power Switch");

    initIbcSwitchEntry( &lightSwitch[2].lightSwitch, SERIAL_CHAN,
        0,                   B240,                 LIGHTS,
        ISOLATED_IO_POS_TRUE,      ISOLATED_IO_BOARD_0_ADDR,     LIGHT_2_CHAN,
        LIGHT_2_SWITCH_REQ_DM,             LIGHT_2_SWITCH_STATUS_DM,
        dmUpdateSem,
        LIGHT_2_PWR_REQ_DM,                LIGHT_2_PWR_OK_DM,
        powerReqSem,                       lightDconName,
        "Light 2 Power Switch");

    initIbcSwitchEntry( &lightSwitch[3].lightSwitch, SERIAL_CHAN,
        0,                   B240,                 LIGHTS,
        ISOLATED_IO_POS_TRUE,      ISOLATED_IO_BOARD_0_ADDR,     LIGHT_3_CHAN,
        LIGHT_3_SWITCH_REQ_DM,             LIGHT_3_SWITCH_STATUS_DM,
        dmUpdateSem,
        LIGHT_3_PWR_REQ_DM,                LIGHT_3_PWR_OK_DM,
        powerReqSem,                       lightDconName,
        "Light 3 Power Switch");

    initIbcSwitchEntry( &lightSwitch[4].lightSwitch, SERIAL_CHAN,
        0,                   B240,                 LIGHTS,
        ISOLATED_IO_POS_TRUE,      ISOLATED_IO_BOARD_1_ADDR,     LIGHT_4_CHAN,
        LIGHT_4_SWITCH_REQ_DM,             LIGHT_4_SWITCH_STATUS_DM,
        dmUpdateSem,
        LIGHT_4_PWR_REQ_DM,                LIGHT_4_PWR_OK_DM,
        powerReqSem,                       lightDconName,
        "Light 4 Power Switch");

    initIbcSwitchEntry( &lightSwitch[5].lightSwitch, SERIAL_CHAN,
        0,                   B240,                 LIGHTS,
        ISOLATED_IO_POS_TRUE,      ISOLATED_IO_BOARD_1_ADDR,     LIGHT_5_CHAN,
        LIGHT_5_SWITCH_REQ_DM,             LIGHT_5_SWITCH_STATUS_DM,
        dmUpdateSem,
        LIGHT_5_PWR_REQ_DM,                LIGHT_5_PWR_OK_DM,
        powerReqSem,                       lightDconName,
        "Light 5 Power Switch");

    initIbcSwitchEntry( &portSizeLaserSwitch, SERIAL_CHAN,
        SIZING_LASER_POWER,                B240,                 HOTEL,
        DUAL_VICOR,                        VICOR_BOARD_ADDR,     0,
        PORT_SIZING_LASER_SWITCH_REQ_DM,   PORT_SIZING_LASER_SWITCH_STATUS_DM,
        dmUpdateSem,
        PORT_SIZING_LASER_PWR_REQ_DM,      PORT_SIZING_LASER_PWR_OK_DM,
        powerReqSem,                       lightDconName,
        "Port Sizing Laser Power Switch");


    initIbcSwitchEntry( &stbdSizeLaserSwitch, SERIAL_CHAN,
        SIZING_LASER_POWER,                B240,                 HOTEL,
        DUAL_VICOR,                        VICOR_BOARD_ADDR,     1,
        STBD_SIZING_LASER_SWITCH_REQ_DM,   STBD_SIZING_LASER_SWITCH_STATUS_DM,
        dmUpdateSem,
        STBD_SIZING_LASER_PWR_REQ_DM,      STBD_SIZING_LASER_PWR_OK_DM,
        powerReqSem,                       lightDconName,
        "Stbd Sizing Laser Power Switch");


                                   /* Devices on Lighting Can 48V Bus        */
    ibcAddSwitchInSeries( &lightDcon48VSwitch,  &portSizeLaserSwitch );
    ibcAddSwitchInSeries( &lightDcon48VSwitch,  &stbdSizeLaserSwitch );

    ibcAddSwitchInSeries( &lightDcon240VSwitch, &ballastSwitch0 );
    ibcAddSwitchInSeries( &lightDcon240VSwitch, &ballastSwitch1 );
    ibcAddSwitchInSeries( &lightDcon240VSwitch, &ballastSwitch2 );

                                   /* Add light switches to ballast switches */
    ibcAddSwitchInSeries( &ballastSwitch0, &lightSwitch[0].lightSwitch );
    ibcAddSwitchInSeries( &ballastSwitch0, &lightSwitch[1].lightSwitch );
    ibcAddSwitchInSeries( &ballastSwitch1, &lightSwitch[2].lightSwitch );
    ibcAddSwitchInSeries( &ballastSwitch1, &lightSwitch[3].lightSwitch );
    ibcAddSwitchInSeries( &ballastSwitch2, &lightSwitch[4].lightSwitch );
    ibcAddSwitchInSeries( &ballastSwitch2, &lightSwitch[5].lightSwitch );


    ibcConnectVicorDmItems( dmItems->vicorDm, &portSizeLaserSwitch,
         lightDconName, "Port Sizing Laser",  0,
         PORT_SIZING_LASER_TEMP_DM,
         PORT_SIZING_LASER_TEMP_ALARM_DM,
         PORT_SIZING_LASER_TEMP_WARN_THRESH_DM,
         PORT_SIZING_LASER_TEMP_ALARM_THRESH_DM);

    ibcConnectVicorDmItems( dmItems->vicorDm, &stbdSizeLaserSwitch,
         lightDconName, "Stbd Sizing Laser",  1,
         STBD_SIZING_LASER_TEMP_DM,
         STBD_SIZING_LASER_TEMP_ALARM_DM,
         STBD_SIZING_LASER_TEMP_WARN_THRESH_DM,
         STBD_SIZING_LASER_TEMP_ALARM_THRESH_DM);

    ibcConnectHpsDmItems( &dmItems->hpsDm[BALLAST_0],
         &ballastSwitch0, lightDconName, "Ballast 0",
         BALLAST0_CURRENT_DM,
         BALLAST0_CURRENT_ALARM_DM,
         BALLAST0_CURRENT_WARN_THRESH_DM,
         BALLAST0_CURRENT_ALARM_THRESH_DM,
         BALLAST0_SWITCH_TRIP_DM );

    ibcConnectHpsDmItems( &dmItems->hpsDm[BALLAST_1],
         &ballastSwitch1, lightDconName, "Ballast 1",
         BALLAST1_CURRENT_DM,
         BALLAST1_CURRENT_ALARM_DM,
         BALLAST1_CURRENT_WARN_THRESH_DM,
         BALLAST1_CURRENT_ALARM_THRESH_DM,
         BALLAST1_SWITCH_TRIP_DM );

    ibcConnectHpsDmItems( &dmItems->hpsDm[BALLAST_2],
         &ballastSwitch2, lightDconName, "Ballast 2",
         BALLAST2_CURRENT_DM,
         BALLAST2_CURRENT_ALARM_DM,
         BALLAST2_CURRENT_WARN_THRESH_DM,
         BALLAST2_CURRENT_ALARM_THRESH_DM,
         BALLAST2_SWITCH_TRIP_DM );


                                    /* Create Data Manager Group for items   */
    cpuGroup = initIbcCpuConsumer(&dmItems->ibcDm, &ibcCpuBits, dmUpdateSem);

                                    /* Use CPU Group for misc parameters     */
    dm_group_add_item(cpuGroup, lightDcon48VSwitch.switchStatusDm,
        &lightingDconSwitchBit);

    dm_start_consumer(dmItems->waterAlarmThresh, DM_STATIC, dmUpdateSem);

    dm_group_add_item(cpuGroup, dmItems->waterAlarmThresh,
        &waterAlarmThreshBit);

    for (ballast = 0; ballast < NUM_LIGHT_BALLASTS; ballast++)
    {
        dm_start_consumer(dmItems->ballastGfCheck[ballast], DM_STATIC,
            dmUpdateSem);

        dm_group_add_item(cpuGroup, dmItems->ballastGfCheck[ballast],
            &ballastGfCheckBit[ballast]);
    } /* for */

                                      /* Create GF/5V Data Manager Group     */
    gf5vGroup = initIbcGf5vConsumer( &dmItems->gf5vDm, &gf5vBits, dmUpdateSem );

    vicorGroup =                      /* Create Dual Vicor DM Group          */
        initIbcVicorConsumer( dmItems->vicorDm, &vicorBits, dmUpdateSem );

                                      /* Create HPS Data Manager Group       */
    for (board = 0; board < HPS_BOARDS; board++)
        hpsGroup[board] = initIbcHpsConsumer(&dmItems->hpsDm[board],
            &hpsBits[board], dmUpdateSem);

                                      /* Add Switch Requests to DM Group     */
    dm_group_add_item(cpuGroup, lightDcon48VSwitch.switchRequestDm,
                      &lightDcon48VSwitchBit);

    dm_group_add_item(cpuGroup, lightDcon240VSwitch.switchRequestDm,
                      &lightDcon240VSwitchBit);

    dm_group_add_item(cpuGroup, ballastSwitch0.switchRequestDm,
                      &ballastSwitch0Bit);

    dm_group_add_item(cpuGroup, ballastSwitch1.switchRequestDm,
                      &ballastSwitch1Bit);

    dm_group_add_item(cpuGroup, ballastSwitch2.switchRequestDm,
                      &ballastSwitch2Bit);

    dm_group_add_item(cpuGroup, portSizeLaserSwitch.switchRequestDm,
                      &portSizeLaserSwitchBit);

    dm_group_add_item(cpuGroup, stbdSizeLaserSwitch.switchRequestDm,
                      &stbdSizeLaserSwitchBit);

    for (light = LIGHT_1; light <= LIGHT_6; light++)
       initLightSwitchStruct(light, &lightSwitch[light], cpuGroup, dmUpdateSem);

                                     /* Initialize Light Status Task         */
    if ((microIOCtrl->childTasks[STATUS_TASK].taskId =
         taskSpawn( "lightStatus", LIGHT_STATUS_PRIORITY, 0,
         LIGHT_STATUS_STACKSIZE, (FUNCPTR) lightStatusTask,
         (int) microIOCtrl,     (int) &ballastSwitch0, (int) &ballastSwitch1,
         (int) &ballastSwitch2, (int) &lightSwitch, 0, 0, 0, 0, 0) ) == ERROR)
              logMsg("Lighting Dcon: Error spawning light status check task\n");

    FOREVER
    {                               /* Mark micro configuration as invalid   */
        microIOCtrl->microConfigured = FALSE;

                                    /* Wait for micro reset via SRQ Task     */
        semTake(microIOCtrl->childTasks[OUT_TASK].taskSyncSem, WAIT_FOREVER);

        /* We need to initialize default state of Data manager Items here    */

        ibcSwitchControl( SERIAL_CHAN, ISOLATED_IO,
            ISOLATED_IO_BOARD_0_ADDR, BALLAST_0_RETURN_CHAN, OFF);

        ibcSwitchControl( SERIAL_CHAN, ISOLATED_IO,
            ISOLATED_IO_BOARD_0_ADDR, BALLAST_1_RETURN_CHAN, OFF);

        ibcSwitchControl( SERIAL_CHAN, ISOLATED_IO,
            ISOLATED_IO_BOARD_1_ADDR, BALLAST_2_RETURN_CHAN, OFF);

        ibcSetSwitchState (&ballastSwitch0,    SWITCH_OFF);
        ibcSetSwitchState (&ballastSwitch1,    SWITCH_OFF);
        ibcSetSwitchState (&ballastSwitch2,    SWITCH_OFF);

        ibcSwitchControl( SERIAL_CHAN, ISOLATED_IO,
            ISOLATED_IO_BOARD_0_ADDR, LIGHT_ISOLATE_0_CHAN, ON);

        ibcSwitchControl( SERIAL_CHAN, ISOLATED_IO,
            ISOLATED_IO_BOARD_0_ADDR, LIGHT_ISOLATE_2_CHAN, ON);

        ibcSwitchControl( SERIAL_CHAN, ISOLATED_IO,
            ISOLATED_IO_BOARD_1_ADDR, LIGHT_ISOLATE_4_CHAN, ON);

        for (light = LIGHT_1; light <= LIGHT_6; light++)
            ibcSetSwitchState (&lightSwitch[light].lightSwitch, SWITCH_OFF);

        ibcSetSwitchState(&portSizeLaserSwitch,  SWITCH_OFF);
        ibcSetSwitchState(&stbdSizeLaserSwitch,  SWITCH_OFF);

        initIbcHumidityThreshDm(SERIAL_CHAN,
            dmItems->ibcDm.humidityWarnThresh,
            dmItems->ibcDm.humidityAlarmThresh);

        initWaterAlarmThreshDm(SERIAL_CHAN, dmItems->waterAlarmThresh);

        updateIbcGf5vDmItems( SERIAL_CHAN, GF5V_BOARD_ADDR,
                             &dmItems->gf5vDm, lightDconName );

        setGf5vChargeTimes( &dmItems->gf5vDm,
                LOW_VOLTAGE_GF_CHARGE_TIME, LOW_VOLTAGE_GF_CHARGE_TIME,
                LOW_VOLTAGE_GF_CHARGE_TIME, LOW_VOLTAGE_GF_CHARGE_TIME );

        enableGf5vMonitoring( SERIAL_CHAN, GF5V_BOARD_ADDR,
           &dmItems->gf5vDm, TRUE );

        runGf5vBoardSelftest( SERIAL_CHAN, GF5V_BOARD_ADDR, &dmItems->gf5vDm );

        for (board = 0; board < HPS_BOARDS; board++)
            updateIbcHpsDmItems( SERIAL_CHAN, hpsBoardAddr[board],
                &dmItems->hpsDm[board], lightDconName );

                                    /* Ignore any Data Manager Item changes  */
        lightDconGroupBits = dm_get_group_changes(cpuGroup);

                                    /* Flag that micro has been configured   */
        microIOCtrl->microConfigured = TRUE;

                                    /* Wake up input task to read from micro */
        semGive(microIOCtrl->childTasks[IN_TASK].taskSyncSem);

        while (microIOCtrl->serialLinkState == LINK_UP)
        {                           /* Repeat until micro controller reset   */
                                    /* Wait for any data items to change     */
            semTake(dmUpdateSem, WAIT_FOREVER);

                                   /* Ask Data Manager which items changed   */
            lightDconGroupBits = dm_get_group_changes(cpuGroup);

                                    /* Check if micro Reset Event occurred   */
            if (lightDconGroupBits & ibcCpuBits.resetEventBit)
                break;              /* Exit loop and re-initialize DM items  */

            taskSafe();             /* Prevent task from being deleted during*/
                                    /* serial IO and Data Manager updates    */

                                    /*  Lighting Dcon Power Switch Request   */
            if (lightDconGroupBits & lightingDconSwitchBit)
                ibcProcessDupSwitchRequest( &lightDcon48VSwitch );

                                    /* Lighting Can 240V Power Switch Request*/
            if (lightDconGroupBits & lightDcon240VSwitchBit)
            {
                if (ibcSwitchRequestDmState(&lightDcon240VSwitch) == SWITCH_OFF)
                {
                    for (light = LIGHT_1; light <= LIGHT_6; light++)
                        ibcSetSwitchState (&lightSwitch[light].lightSwitch,
                            SWITCH_OFF);

                    ibcSetSwitchState (&ballastSwitch0, SWITCH_OFF);
                    ibcSetSwitchState (&ballastSwitch1, SWITCH_OFF);
                    ibcSetSwitchState (&ballastSwitch2, SWITCH_OFF);
                } /* if */
            } /* if */

                                   /* Port Sizing Laser Power Switch Request */
            if (lightDconGroupBits & portSizeLaserSwitchBit)
                ibcProcessSwitchRequest( &portSizeLaserSwitch );

                                   /* Stbd Sizing Laser Power Switch Request */
            if (lightDconGroupBits & stbdSizeLaserSwitchBit)
                ibcProcessSwitchRequest( &stbdSizeLaserSwitch );


                                   /* Light Ballast 0 Power Switch Request   */
            if (lightDconGroupBits & ballastSwitch0Bit)
                switchBallastPower( &ballastSwitch0 );

                                   /* Light Ballast 1 Power Switch Request   */
            if (lightDconGroupBits & ballastSwitch1Bit)
                switchBallastPower( &ballastSwitch1 );

                                   /* Light Ballast 2 Power Switch Request   */
            if (lightDconGroupBits & ballastSwitch2Bit)
                switchBallastPower( &ballastSwitch2 );

            for (light = LIGHT_1; light <= LIGHT_6; light++)
            {
                if (lightDconGroupBits & lightSwitch[light].switchChangedBit)
                    switchLightPower( &lightSwitch[light] );

                if (lightDconGroupBits & lightSwitch[light].dimLevelBit)
                    setLightDimLevelDm( &lightSwitch[light] );
            } /* for */

                                    /* Check for GF/5V Board Parameter Update*/
            ibcGf5vUpdateCheck( SERIAL_CHAN, GF5V_BOARD_ADDR,
                              &dmItems->gf5vDm, &gf5vBits, gf5vGroup );

                                    /* Check for Vicor Board Parameter Update*/
            ibcVicorUpdateCheck( SERIAL_CHAN, VICOR_BOARD_ADDR,
                              dmItems->vicorDm, &vicorBits, vicorGroup );

            for (board = 0; board < HPS_BOARDS; board++)
                ibcHpsUpdateCheck( SERIAL_CHAN, hpsBoardAddr[board],
                      &dmItems->hpsDm[board], &hpsBits[board],
                      hpsGroup[board] );

                                    /* Send Set Humidity Alarm Threshold cmd */
            if ((lightDconGroupBits & ibcCpuBits.humidityWarnThreshBit) ||
                (lightDconGroupBits & ibcCpuBits.humidityAlarmThreshBit))
                writeIbcHumidityThresh(SERIAL_CHAN,
                    dmItems->ibcDm.humidityWarnThresh,
                    dmItems->ibcDm.humidityAlarmThresh);

                                    /* Send Set Water Alarm Threshold cmd    */
            if (lightDconGroupBits & waterAlarmThreshBit)
                writeWaterAlarmThresh(SERIAL_CHAN,
                                      dmItems->waterAlarmThresh);

                                    /* Check if Reset Command requested      */
            if (lightDconGroupBits & ibcCpuBits.resetCmdBit)
            {                       /* Mark serial link as down              */
                setSerialLinkState(microIOCtrl, LINK_DOWN);
                                    /* Manual reset so ammounce it           */
                microIOCtrl->announceResets = TRUE;
                                    /* Send Reset Command to Microcontroller */
                microResetCmd(SERIAL_CHAN);
                break;              /* Exit loop and wait for RESET          */
            } /* if */

            taskUnsafe();           /* Task may now be safely deleted        */

        } /* while (LINK_UP) */
    } /* 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 !                                */
} /* lightingDconOutTask() */


/*****************************************************************************/
/* Function    : lightingDconInTask                                          */
/* Purpose     : Lighting Dcon Input Task. Wakes up periodically, read data  */
/*               from the Lighting Dcon 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
lightingDconInTask(Reg lightDconDmItems* dmItems, microIOControl *microIOCtrl)
{
    SEM_ID  wakeupSem;              /* Used by watch dog to wake up this task*/
    Int32   updates = 0;            /* Counts dm updates for rate calculation*/
    Int16   board;                  /* HPS Board Index                       */

                                    /* Create wakeup semaphore and watch dog */
                                    /* timer for provider rate control       */
    if ((wakeupSem = semBCreate(SEM_Q_FIFO, SEM_EMPTY)) == NULL )
    {
        logMsg("Lighting Dcon: Error initializing Input Task Resources\n");
        return;
    } /* if */
                                    /* Provider Task for non-static data items*/
    initIbcCpuProvider   (&dmItems->ibcDm,    HUMIDITY_DM_PERIOD);
    initIbcGf5vProvider  (&dmItems->gf5vDm,   LIGHTING_IBC_DM_PERIOD);
    initIbcVicorProvider ( dmItems->vicorDm,  LIGHTING_IBC_DM_PERIOD);

    for (board = 0; board < HPS_BOARDS; board++)
        initIbcHpsProvider(&dmItems->hpsDm[board], LIGHTING_IBC_DM_PERIOD);

    FOREVER
    {                               /* Wait for IBC Coms to be initialized  */
        semTake(microIOCtrl->childTasks[IN_TASK].taskSyncSem, WAIT_FOREVER);

        while (microIOCtrl->serialLinkState == LINK_UP)
        {
            taskSafe();             /* Prevent task from being deleted during*/
                                    /* serial IO and Data Manager updates    */

            if ((updates % (HUMIDITY_DM_PERIOD / LIGHTING_IBC_DM_PERIOD)) == 0)
            {
                updateIbcHumidityDm( SERIAL_CHAN,
                    dmItems->ibcDm.microHumidity );
            } /* if */

            updateNewIbcGf5vDm(SERIAL_CHAN, GF5V_BOARD_ADDR, &dmItems->gf5vDm);

            updateIbcVicorDm(SERIAL_CHAN,  VICOR_BOARD_ADDR, dmItems->vicorDm);

            for (board = 0; board < HPS_BOARDS; board++)
                updateIbcHpsDm(SERIAL_CHAN, hpsBoardAddr[board],
                    &dmItems->hpsDm[board]);

            taskUnsafe();           /* task may now be deleted safely         */

                                    /* increment & range check update counter */
            if (++updates == 10000) updates  = 0;

                                    /* pend until sem take times out          */
            semTake(wakeupSem, sysClkRateGet() /
                (USECS_PER_SEC / LIGHTING_IBC_DM_PERIOD));
        } /* while */
    } /* FOREVER */

    semDelete(wakeupSem);           /* Release 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 !                                 */
} /* lightingDConInTask() */

/******************************************************************************/
/* Function    : lightStatusTask                                              */
/* Purpose     : Light Status Task. Periodically check current for            */
/*               lights and ballasts and updates alarm data manager item      */
/* Inputs      : Switch entries for ballast & lights                          */
/* Outputs     : Normally runs forever, but exits if error occurs             */
/******************************************************************************/
    Void
lightStatusTask(microIOControl *microIOCtrl, switchEntry *ballast0Switch,
    switchEntry *ballast1Switch, switchEntry *ballast2Switch,
    lightSwitchDataStruct lightSwitch[NUM_LIGHTS])
{
    SEM_ID  wakeupSem;              /* Used to wake up this task              */
    Int16   ballast, light;         /* counter for ballast and light items   */
    const   char   itemPrefix[32];  /* Data Manager Item Prefix Name         */

    DM_Item ballastAlarmDm[NUM_LIGHT_BALLASTS];
    Int16   lastExpectedCurrent[NUM_LIGHT_BALLASTS] = { 0 };

                                    /* Create wakeup semaphore for timer      */
    if ((wakeupSem = semBCreate(SEM_Q_FIFO, SEM_EMPTY)) == NULL )
    {
        logMsg("Lighting Dcon: Error initializing Status Task Resources\n");
        return;
    } /* if */

    dm_start_consumer(ballast0Switch->switchStatusDm, DM_STATIC, SEM_NULL);
    dm_start_consumer(ballast1Switch->switchStatusDm, DM_STATIC, SEM_NULL);
    dm_start_consumer(ballast2Switch->switchStatusDm, DM_STATIC, SEM_NULL);

    for (ballast = BALLAST_0; ballast <= BALLAST_2; ballast++)
    {
        sprintf(itemPrefix, "%sBALLAST%d.", lightDconDmPrefix, ballast);

                                    /* Create Ballast Alarm DM Item           */
        ballastAlarmDm[ballast] = initMicroDmItem( itemPrefix,
            LIGHT_BALLAST_ALARM_DM, DM_MBOOL, 1);

        dm_start_provider(ballastAlarmDm[ballast], DM_STATIC);
    } /* for */

    for (light = LIGHT_1; light <= LIGHT_6; light++)
        dm_start_consumer(lightSwitch[light].lightSwitch.switchStatusDm,
            DM_STATIC, SEM_NULL);

    FOREVER
    {                               /* Wait for IBC Coms to be initialized   */
        semTake(microIOCtrl->childTasks[STATUS_TASK].taskSyncSem, WAIT_FOREVER);

        while (microIOCtrl->serialLinkState == LINK_UP)
        {
            checkBallastCurrent(ballast0Switch, ballastAlarmDm[BALLAST_0],
               &lastExpectedCurrent[BALLAST_0], &lightSwitch[LIGHT_1],
               &lightSwitch[LIGHT_2]);

            checkBallastCurrent(ballast1Switch, ballastAlarmDm[BALLAST_1],
               &lastExpectedCurrent[BALLAST_1], &lightSwitch[LIGHT_3],
               &lightSwitch[LIGHT_4]);

            checkBallastCurrent(ballast0Switch, ballastAlarmDm[BALLAST_2],
               &lastExpectedCurrent[BALLAST_2], &lightSwitch[LIGHT_5],
               &lightSwitch[LIGHT_6]);

                                   /* pend until sem take times out          */
            semTake(wakeupSem, sysClkRateGet() /
                (USECS_PER_SEC / LIGHTING_IBC_DM_PERIOD));
        } /* while */
    } /* FOREVER */

    semDelete(wakeupSem);           /* Release resources                      */
} /* lightingDConStatusTask() */

/******************************************************************************/
/* Function    : processLightingDconSRQ                                       */
/* Purpose     : Decode Service Request and take appropriate action           */
/* Inputs      : Array of DM items, serial channel                            */
/* Outputs     : Returns SRQ Received & many DM items are changed             */
/******************************************************************************/
    LOCAL Word
processLightingDconSRQ( Reg lightDconDmItems* dmItems,
    microIOControl *microIOCtrl)
{
    Word srqRequest;                /* Service Request Value                  */
    Word status;                    /* IBC Alarm Status Word                  */
    Byte buffer[SRQ_PACKET_LEN];    /* Buffer for micro communications data   */
    ibcAlarm alarmStatus;           /* IBC Alarm Status                       */

                                    /* Wait for Service Requests              */
    if (readSRQPacket(SERIAL_CHAN, buffer, SRQ_PACKET_LEN, WAIT_FOREVER)
        == ERROR) return (ERROR);
                                    /* Service Request received so process it */
    srqRequest = wordFromBuf(buffer, 0);

    switch (srqRequest)             /* Decode Service Request from micro      */
    {
      case MICRO_RESET_SRQ:         /* Microcontroller Reset Occurred         */
                                    /* Post Reset Event DM Item               */
        if (microIOCtrl->announceResets)
            microResetEventDm(dmItems->ibcDm.microDm.resetEvent);
        break;

      case CAN_WATER_ALARM_ON_SRQ:         /* Housing Water Alarm Active     */
        writeBooleanDmItem(dmItems->canWaterAlarm, TRUE);
        break;

      case CAN_WATER_ALARM_OFF_SRQ:        /* Housing Water Alarm Inactive   */
        writeBooleanDmItem(dmItems->canWaterAlarm, FALSE);
        break;

      case JBOX_WATER_ALARM_ON_SRQ:        /* J Box Water Alarm Active       */
        writeBooleanDmItem(dmItems->jboxWaterAlarm, TRUE);
        break;

      case JBOX_WATER_ALARM_OFF_SRQ:       /* J Box Water Alarm Inactive     */
        writeBooleanDmItem(dmItems->jboxWaterAlarm, FALSE);
        break;

      default:
        if (processIbcCpuSRQ(srqRequest, buffer, &dmItems->ibcDm) != TRUE)

        if (processIbcGf5vSRQ(srqRequest,  buffer, GF5V_BOARD_ADDR,
            &dmItems->gf5vDm) != TRUE)

        if (processIbcVicorSRQ(srqRequest, buffer, VICOR_BOARD_ADDR,
            dmItems->vicorDm ) != TRUE)

        if (processIbcHpsSRQ(srqRequest, buffer, hpsBoardAddr[0],
            &dmItems->hpsDm[0]) != TRUE)

        if (processIbcHpsSRQ(srqRequest, buffer, hpsBoardAddr[1],
            &dmItems->hpsDm[1]) != TRUE)

        if (processIbcHpsSRQ(srqRequest, buffer, hpsBoardAddr[2],
            &dmItems->hpsDm[2]) != TRUE)

            printf("Lighting Dcon: Unrecognized SRQ 0x%x\n", srqRequest);
    } /* switch */

    return(srqRequest);                 /* Return SRQ received to caller      */
} /* processLightingDconSRQ() */

/******************************************************************************/
/* Function    : setLightDimLevel                                             */
/* Purpose     : Sets light dimming level to value specified                  */
/* Inputs      : Light power switch entry, dim level                          */
/* Outputs     : Returns OK or ERROR                                          */
/* NOTE        : This functions assumes that the On/Off control channels and  */
/*               the DAC channels are mapped one-to-one.                      */
/******************************************************************************/
    MLocal STATUS
setLightDimLevel( switchEntry *sw, Nat16 level )
{
    return (IBC_ISO_IO_SetDacLevel(sw->ibcSerialChan,
        sw->boardAddr, sw->channel, level));
} /* setLightDimLevel() */

/******************************************************************************/
/* Function    : initLightSwitchStruct                                        */
/* Purpose     : Initializes light switch data structure                      */
/* Inputs      : light ID, light data structure, Data Mgr group & wakeup sem  */
/* Outputs     : NONE                                                         */
/******************************************************************************/
    MLocal Void
initLightSwitchStruct( vehicleLights lightId, lightSwitchDataStruct *light,
    DM_Group lightGroup, SEM_ID dmUpdateSem )
{
    const   char   itemPrefix[32];  /* Data Manager Item Prefix Name         */

    sprintf(itemPrefix, "%sLIGHT%d.", lightDconDmPrefix, lightId);

                                    /* Create Light On/Off Status DM Item    */
    light->lightOnDm = initMicroDmItem( itemPrefix,
        LIGHT_ON_STATUS_DM, DM_MBOOL, 1);

    dm_start_provider(light->lightOnDm, DM_STATIC);

                                    /* Create Light Dim Level DM Item        */
    light->dimLevelDm = initMicroDmItem( itemPrefix,
        LIGHT_DIM_LEVEL_DM, DM_NAT16, 1);

    dm_start_consumer(light->dimLevelDm, DM_STATIC, dmUpdateSem);
    dm_group_add_item(lightGroup, light->dimLevelDm, &light->dimLevelBit);

    dm_group_add_item(lightGroup, light->lightSwitch.switchRequestDm,
                      &light->switchChangedBit);

    timer_create(CLOCK_REALTIME, NULL, &light->switchTimer);
    timer_connect(light->switchTimer, (VOIDFUNCPTR) lightSwitchHandler,
       (int) &light->lightSwitch);

    light->switchPending = FALSE;

    timer_create(CLOCK_REALTIME, NULL, &light->currentTimer);
    timer_connect(light->currentTimer, (VOIDFUNCPTR) lightCurrentHandler,
       (int) &light->lightSwitch);

    light->checkLightCurrent = FALSE;
} /* initLightSwitchStruct() */

/******************************************************************************/
/* Function    : lightCurrentHandler                                          */
/* Purpose     : Handler for light current timer. Sets check current flag     */
/* Inputs      : timer data structure and light switch data structure         */
/* Outputs     : None                                                         */
/******************************************************************************/
    MLocal Void
lightCurrentHandler(timer_t timer, lightSwitchDataStruct *light)
{
    light->checkLightCurrent = TRUE;     /* Check current for this light      */
} /* lightCurrentHandler() */

/******************************************************************************/
/* Function    : lightSwitchHandler                                           */
/* Purpose     : Handler for light switch timer. Cancel switch pending flag   */
/* Inputs      : timer data structure and light switch data structure         */
/* Outputs     : None                                                         */
/******************************************************************************/
    MLocal Void
lightSwitchHandler(timer_t timer, lightSwitchDataStruct *light)
{
    light->switchPending = FALSE;   /* Allow user to cycle power             */
} /* lightSwitchHandler() */

/******************************************************************************/
/* Function    : switchBallastPower                                           */
/* Purpose     : Switch a ballast power On or Off                             */
/* Inputs      : Ballast power switch entry                                   */
/* Outputs     : Returns OK or ERROR                                          */
/******************************************************************************/
    MLocal STATUS
switchBallastPower( switchEntry *ballastSwitch )
{
    switchEntry *lightSwitch;

    for ( lightSwitch = ballastSwitch->busHead;
         lightSwitch != NO_SWITCH;
         lightSwitch = lightSwitch->busNext )
    {                           /* First switch off all lights on the ballast*/
        if (ibcSwitchDmState(lightSwitch ) != SWITCH_OFF)
            ibcSetSwitchState(lightSwitch, SWITCH_OFF);
                                /* Set dim level to full on                  */
        setLightDimLevel(lightSwitch, HMI_LIGHT_FULL_ON);
    } /* for */
                                /* Then switch power to ballast              */
    return (ibcProcessSwitchRequest( ballastSwitch ));
} /* switchBallastPower() */

/******************************************************************************/
/* Function    : switchLightPower                                             */
/* Purpose     : Switch light power On or Off                                 */
/* Inputs      : Light power switch entry                                     */
/* Outputs     : Returns OK or ERROR                                          */
/******************************************************************************/
    MLocal STATUS
switchLightPower( lightSwitchDataStruct *light )
{
                                    /* Read desired light switch status       */
    if (ibcSwitchRequestDmState (&light->lightSwitch) == SWITCH_ON)
    {
        if (light->switchPending)   /* Last switch operation in process       */
        {
            logMsg("Attempt to switch on light too soon\n");
            return (ERROR);         /* so stop user cycling power             */
        } /* if */
                                    /* Set dim level to full on               */
        setLightDimLevel(&light->lightSwitch, HMI_LIGHT_FULL_ON);

                                    /* Then switch on power to light          */
        if (ibcProcessSwitchRequest( &light->lightSwitch ) == SWITCH_ON)
        {
            light->switchPending = TRUE;
            startIntervalTimer(light->switchTimer, HMI_LIGHT_STRIKE_TIME, 0 );

            light->checkLightCurrent = FALSE;
            startIntervalTimer(light->currentTimer, HMI_LIGHT_SWITCH_ON_TIME,0);

        } /* if */
    } /* if */

    else                        /* request is to switch light OFF             */
    {                           /* Stop timer if its running                  */
        timer_cancel(light->switchTimer);
        timer_cancel(light->currentTimer);

        if (ibcProcessSwitchRequest( &light->lightSwitch ) == SWITCH_OFF)
        {
          light->switchPending = TRUE;   /* Stop user from cycling power      */
          startIntervalTimer(light->switchTimer, HMI_LIGHT_SWITCH_ON_TIME, 0);

          light->checkLightCurrent = FALSE;
          startIntervalTimer(light->currentTimer, HMI_LIGHT_SWITCH_OFF_TIME, 0);
        } /* if */
    } /* else */
    return (OK);
} /* switchLightPower() */

/******************************************************************************/
/* Function    : setLightDimLevelDm                                           */
/* Purpose     : Read light's dim level DM item value set the light dim level */
/* Inputs      : Light data structure                                         */
/* Outputs     : OK or ERROR                                                  */
/******************************************************************************/
    MLocal STATUS
setLightDimLevelDm( lightSwitchDataStruct *light )
{
    Nat16 dimLevel;

    if (light->switchPending)
    {
        logMsg("Attempt to dim light while striking\n");
        return (ERROR);
    } /* if */
                                /* Read light dim level from Data Manager     */
    if (dm_read(light->dimLevelDm, &dimLevel, sizeof(dimLevel),
       (DM_Time *) NULL) == SUCCESS)
                               /* If MAX_LEVEL or light is switched on        */
        if ( (dimLevel == HMI_LIGHT_FULL_ON) ||
            (ibcSwitchDmState(&light->lightSwitch) == SWITCH_ON) )
                               /* Dim the light                               */
            return (setLightDimLevel( &light->lightSwitch, dimLevel ));

    return (ERROR);
} /* setLightDimLevelDm() */


    MLocal Void
checkBallastCurrent(switchEntry *ballast, DM_Item lampAlarmDm,
    Int16 *lastExpCurrent, lightSwitchDataStruct *light0,
    lightSwitchDataStruct *light1)
{
    Nat16 current;
    Nat16 expectedCurrent;
    switchStatus switchState = SWITCH_OFF;
    DM_Time switchOnTime;               /* Ballast Switch On Time           */
    DM_Time timeNow;                    /* Time of day now                  */

                                        /* Read present switch status       */
    dm_read( ballast->switchStatusDm, (char *) &switchState,
        sizeof(switchState), &switchOnTime);

    if (switchState != SWITCH_ON)
        return;

    gettimeofday(&timeNow, (struct timezone *)NULL);

    switchOnTime.tv_sec  = (((timeNow.tv_sec  - switchOnTime.tv_sec) * 10) +
      ((timeNow.tv_usec - switchOnTime.tv_usec) / (USECS_PER_SEC / 10))) / 10;

    if (switchOnTime.tv_sec < 10)
        return;

    expectedCurrent = BALLAST_ON_CURRENT;

    if ( (light0->checkLightCurrent) &&
         (ibcSwitchDmState(&light0->lightSwitch) == SWITCH_ON) )
        expectedCurrent += HMI_LIGHT_ON_CURRENT;

    if ( (light1->checkLightCurrent) &&
         (ibcSwitchDmState(&light1->lightSwitch) == SWITCH_ON) )
        expectedCurrent += HMI_LIGHT_ON_CURRENT;

    if (expectedCurrent == BALLAST_ON_CURRENT)
        return;

    if (dm_read(ballast->currentDm, &current, sizeof(current),
       (DM_Time *) NULL) == SUCCESS)
    {
        if ((current < expectedCurrent) &&
            (*lastExpCurrent != expectedCurrent))
        {
            writeBooleanDmItem(lampAlarmDm, TRUE);
            logMsg("Lamp didn't strike  %5d %5d\n", current, expectedCurrent);
            *lastExpCurrent = expectedCurrent;
        } /* if */

        else if ((current >= expectedCurrent) &&
             (*lastExpCurrent > expectedCurrent))
        {
            writeBooleanDmItem(lampAlarmDm, FALSE);
            logMsg("Lamp didn't go off %5d %5d\n",current, expectedCurrent );
            *lastExpCurrent = expectedCurrent;
        } /* if */

    } /* if */
} /* checkBallastCurrent() */


