//
//  Copyright © 2002, RESON Inc. All Rights Reserved.
//
//  No part of this file may be reproduced or transmitted in any form or by
//  any means, electronic or mechanical, including photocopy, recording, or
//  information storage or retrieval system, without permission in writing
//  from RESON Inc.
//
//  Filename:   
//
//  Project:    6046
//
//  Author(s):  W. Arcus
//
//  Purpose:    Implements the main module interface for the BlueFinVC DLL and its
//              exported C interface functions.
//
//  Notes:      1)  If this DLL is dynamically linked against the MFC
//                  DLLs, any functions exported from this DLL which
//                  call into MFC must have the AFX_MANAGE_STATE macro
//                  added at the very beginning of the function.
//                  The macro ManageDllState_m() is defined for this purpose (see below).
//
//                  See MFC Technical Notes 33 and 58 for additional info.
//

#include "StdAfx.h"
#include "BlueFinVC.h"

#include "..\..\Include\Exports.h"
#include "..\..\Include\SurveyEventTypes.h"

#include <atlbase.h>

#ifdef _DEBUG
#pragma comment( lib, "..\\..\\..\\Utils\\NetUtils\\Debug\\NetUtils.lib" )
#else
#pragma comment( lib, "..\\..\\..\\Utils\\NetUtils\\Release\\NetUtils.lib" )
#endif

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// Internal module definitions and constants etc.

#define GetAppOrAbort_m( text )     CBlueFinVCApp * pApp = GetApp_m();                                          \
                                                                                                                \
                                    if ( pApp == NULL )                                                         \
                                    {                                                                           \
                                        TRACE( _T( "File: " __FILE__ ", Line: %d: " text "\n" ), __LINE__ );    \
                                        ASSERT( false );                                                        \
                                        return ERROR_CODE_FAILURE;                                              \
                                    }

namespace                   // Unnamed namespace to guarantee internal linkage.
{
    const int               iMaxAddressSize_c               = 16;
    const double            dModuleVersion_c                = 3.0000;

    const unsigned long     ulDefaultPort_c                 = 3100UL;
    LPCTSTR                 lpszDefaultAddress_c            = _T( "127.0.0.1" );

    LPCTSTR                 lpsz6046BlueFinVCRegistryKey_c  = _T( ".DEFAULT\\SOFTWARE\\RESON\\6046\\BlueFin" );
    LPCTSTR                 lpsz6046BlueFinVCAddressKey_c   = _T( "Address" );
    LPCTSTR                 lpsz6046BlueFinVCPortKey_c      = _T( "Port" );

}                           // End unnamed namespace.

/////////////////////////////////////////////////////////////////////////////
// CBlueFinVCApp implementation -- starting with static member initialization.

BEGIN_MESSAGE_MAP(CBlueFinVCApp, CWinApp)
    //{{AFX_MSG_MAP(CBlueFinVCApp)
        // NOTE - the ClassWizard will add and remove mapping macros here.
        //    DO NOT EDIT what you see in these blocks of generated code!
    //}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CBlueFinVCApp construction.

CBlueFinVCApp::CBlueFinVCApp( void )
              :CSensorInterface()
{
    m_ulPort            = ulDefaultPort_c;
    m_Address           = lpszDefaultAddress_c;
    m_pWatchSentinel    = NULL;
}

CBlueFinVCApp::~CBlueFinVCApp( void )
{
    __TRY
    {
        if ( m_pWatchSentinel != NULL )
        {
            delete m_pWatchSentinel;
            m_pWatchSentinel = NULL;
        }
    }
    __FINALLY
    {
        ASSERT( m_pWatchSentinel == NULL );

        m_pWatchSentinel = NULL;
        m_ulPort         = ulDefaultPort_c;
        m_Address        = lpszDefaultAddress_c;
    }
    __ENDFINALLY
}

CBlueFinVCApp theApp;                           // The one and only CBlueFinVCApp object.

CBlueFinVCApp * CBlueFinVCApp::This( void )
{
    return ( static_cast<CBlueFinVCApp *>( CSensorInterface::This() ) );
}

BOOL CBlueFinVCApp::InitInstance( void )
{
    // Main initialization method.

    if ( ! AfxSocketInit() )
    {
        TRACE( _T( "CBlueFinVCApp::InitInstance(), Windows sockets initialization failure\n" ) );
        ASSERT( false );
        return FALSE;
    }

    // Now set the modules version and restore any one-time parameters from the registry.

    Version( dModuleVersion_c );

    SettingsFromRegistry();

    return TRUE;
}

int CBlueFinVCApp::ExitInstance( void )
{
    // Significant clean-up may go in here.

    return CWinApp::ExitInstance();
}

void CBlueFinVCApp::SettingsFromRegistry( void )
{
    __TRY
    {
        m_Critical.Enter();

        if ( ! m_bRetrievedSonarSettings )
        {
            CRegKey Key;
            DWORD   dwValue = 0UL;

            Key.Attach( HKEY_USERS );

            if ( Key.Open( HKEY_USERS, lpsz6046BlueFinVCRegistryKey_c ) == ERROR_SUCCESS )
            {
                DWORD dwAddressLength = static_cast<DWORD>( iMaxAddressSize_c );

                CString Address;
                LONG lSuccessCode = Key.QueryValue( Address.GetBuffer( iMaxAddressSize_c ), lpsz6046BlueFinVCAddressKey_c, &dwAddressLength );
                Address.ReleaseBuffer();

                if ( ( lSuccessCode == ERROR_SUCCESS ) && ( dwAddressLength > 0UL ) )
                {
                    m_Address = Address;
                    ASSERT( ! m_Address.IsEmpty() );
                }

                if ( Key.QueryValue( dwValue, lpsz6046BlueFinVCPortKey_c ) == ERROR_SUCCESS )
                {
                    m_ulPort = static_cast<unsigned long>( dwValue );
                }

                Key.Close();
            }

            m_bRetrievedSonarSettings = true;
        }
    }
    __FINALLY
    {
        m_Critical.Leave();
    }
    __ENDFINALLY
}

CString CBlueFinVCApp::Address( void ) const
{
    CString Address;

    __TRY
    {
        m_Critical.Enter();
        Address = m_Address;
    }
    __FINALLY
    {
        m_Critical.Leave();
    }
    __ENDFINALLY

    return Address;
}

unsigned long CBlueFinVCApp::Port( void ) const
{
    unsigned long ulPort = 0UL;

    __TRY
    {
        m_Critical.Enter();
        ulPort = m_ulPort;
    }
    __FINALLY
    {
        m_Critical.Leave();
    }
    __ENDFINALLY

    return ulPort;
}

CSensorList<CVehicleData> & CBlueFinVCApp::SensorList( void ) const
{
    return m_SensorList;
}

bool CBlueFinVCApp::WatchSentinel(  const bool          &rbEnable,
                                    const unsigned long &rulCheckPeriod )
{
    bool bSuccess = false;

    if ( rbEnable && ( m_pWatchSentinel == NULL ) )
    {
        __TRY
        {
            m_pWatchSentinel = new CWatchSentinel( rulCheckPeriod );
        }
        __FINALLY
        {
            ASSERT( m_pWatchSentinel != NULL );

            if ( m_pWatchSentinel != NULL )
            {
                bSuccess = m_pWatchSentinel->SetHealthCheckCallback( StatusCheckCallback, this );
            }
        }
        __ENDFINALLY
    }
    else if ( ( ! rbEnable ) && ( m_pWatchSentinel != NULL ) )
    {
        __TRY
        {
            delete m_pWatchSentinel;
            m_pWatchSentinel = NULL;
        }
        __FINALLY
        {
            bSuccess = ( m_pWatchSentinel == NULL );
            m_pWatchSentinel = NULL;
        }
        __ENDFINALLY
    }
    else
    {
        bSuccess = true;            // Nothing to do.
    }

    return bSuccess;
}

void CBlueFinVCApp::StatusCheckCallback(    void       *pvParam,
                                            const int  &riSensorIndex )
{
    // Static callback for status and health checking.

    try
    {
        if ( pvParam != NULL )
        {
            static_cast<CBlueFinVCApp *>( pvParam )->CheckAndReportHealth( riSensorIndex );
        }
    }
    catch ( ... )
    {
        TRACE( _T( "CBlueFinVCApp::StatusCheckCallback(), Unspecified exception.\n" ) );
    }
}

void CBlueFinVCApp::CheckAndReportHealth( const int &riSensorIndex )
{
    // This is a callback executed from a thread within the m_pSentinel object.

    __TRY
    {
        m_SensorListGuard.Enter();

        std::vector<int>           IndexList;
        CSensorList<CVehicleData> &rSensorList = SensorList();

        if ( riSensorIndex == -1 )
        {
            IndexList = rSensorList.GetSensorIndicies();
        }
        else if ( riSensorIndex >= 0 )
        {
            IndexList.push_back( riSensorIndex );
        }

        for ( std::vector<int>::iterator pIndex = IndexList.begin(); pIndex != IndexList.end(); pIndex++ )
        {
            CVehicleData *pSensor = rSensorList.GetSensor( *pIndex );
            ASSERT( pSensor != NULL );

            if ( pSensor != NULL )
            {
                pSensor->UpdateSensorHealth();
            }
        }
    }
    __FINALLY
    {
        m_SensorListGuard.Leave();
    }
    __ENDFINALLY
}

///////////////////////////////////////////////////////////////////////////////
// Exported 6046 interface functions.

extern "C" __declspec( dllexport )
int WINAPI Startup( SENSORINFO     *psSensorInfo,
                    DWORD           dwThreadId,
                    UINT            uiDataReadyMessageId,
                    UINT            uiReplyReadyMessageId,
                    void           *vpfnLog,
                    void           *vpCriticalParameters )
{
    ManageDllState_m();

    GetAppOrAbort_m( "[BlueFinVC] Startup(), pApp is NULL" );

    int iSuccess = ERROR_CODE_FAILURE;                              // Assume failure for now.

    try
    {
        // The sensor index is assigned by the 6046 and is unique to each sensor in the suite.

        ASSERT( ( psSensorInfo != NULL ) && ( psSensorInfo->m_iSensorIndex >= 0 ) );

        const int iSensorIndex = psSensorInfo->m_iSensorIndex;
        int       iRefCount    = pApp->AddSensorRef();

        if ( iRefCount == 1 )
        {
            pApp->EventLog( vpfnLog );
#if 1
#pragma CompileMessage_m( "TODO: RE-ENABLE CRITICAL PARAMETER HANDLING ONCE MECHAMISM IS COMPLETED" )
            DBG_UNREFERENCED_PARAMETER( vpCriticalParameters );
#else
            pApp->CriticalParameterTable( vpCriticalParameters );
#endif
        }

        __TRY
        {
            pApp->m_SensorListGuard.Enter();

            CSensorList<CVehicleData>  &rSensorList = pApp->SensorList();
            CVehicleData *pSensor     = rSensorList.AddSensor( iSensorIndex );
            ASSERT( pSensor != NULL );

            // Sensor id is a 6046 specific code for each sensor (see ESENSORID of processinf.h) whereas
            // the device ID is the 7k device ID per the 7k ICD. They shouldn't be confused.

            psSensorInfo->m_iPriority            = 1;
            psSensorInfo->m_iFilter              = 0;
            psSensorInfo->m_iSensorId            = sensorIdBlueFinVC;           // 6046 specific index for this device type.
            psSensorInfo->m_iMediaType           = mediaTypeInternal;
            psSensorInfo->m_iAccepts7kRecords    = 0;                           // Sensor doesn't accept routed (inbound) 7k messages.

            // Note: iSubsytemId defines the subsytem type. After the sensor is started, we retrieve the 
            // type and populate the remaining fields of the psSensorInfo struct.

            if ( ! pSensor->Configure( pApp->Address(), static_cast<unsigned short>( pApp->Port() ) ) )
            {
                pApp->SensorList().RemoveSensor( iSensorIndex );
                TRACE( _T( "Sensor configuration setting failure.\n" ) );
            }
            else
            {
                if ( ! pSensor->Description( psSensorInfo->m_ulDeviceId, psSensorInfo->m_iSensorType, &(psSensorInfo->m_szName[ 0 ]) ) )
                {
                    TRACE( _T( "BlueFinVC DLL, Startup(), Unable to retrieve sensor description (device id, sensor type or name).\n" ) );
                    pApp->LogEvent( eventLogDiagnostic, iUnspecified_c, iUnspecified_c, "[BlueFinVC] unable to retrieve sensor description" );
                }

                if ( ! pSensor->Startup( iSensorIndex, dwThreadId, uiDataReadyMessageId, uiReplyReadyMessageId, psSensorInfo->m_iActivateOnStart ) )
                {
                    pApp->SensorList().RemoveSensor( iSensorIndex );
                    pApp->LogEvent( eventLogDiagnostic, iUnspecified_c, iUnspecified_c, "[BlueFinVC] Startup failure" );
                }
                else
                {
                    pApp->LogEvent( eventLogAdvisory, iUnspecified_c, iUnspecified_c, "[BlueFinVC] Startup (Index: %d, AutoStart: %d)", iSensorIndex, psSensorInfo->m_iActivateOnStart );
                    iSuccess = ERROR_CODE_SUCCESS;

                    if ( iRefCount == 1 )
                    {
                        pApp->WatchSentinel( true );
                    }
                }
            }
        }
        __FINALLY
        {
            pApp->m_SensorListGuard.Leave();
        }
        __ENDFINALLY
    }
    catch ( LPCTSTR lpszMessage )
    {
        iSuccess = ERROR_CODE_FAILURE;
        TRACE( _T( "BlueFinVC DLL, Startup() failed: %s\n" ), lpszMessage );
    }
    catch ( ... )
    {
        iSuccess = ERROR_CODE_FAILURE;
        TRACE( _T( "BlueFinVC DLL, Startup(), unspecified exception\n" ) );
    }

    return iSuccess;
}

extern "C" __declspec( dllexport )
int WINAPI Shutdown( int iSensorIndex )
{
    ManageDllState_m();

    GetAppOrAbort_m( "[BlueFinVC] Shutdown(), pApp is NULL" );

    int iSuccess = ERROR_CODE_FAILURE;                  // Assume failure for now.

    try
    {
        pApp->LogEvent( eventLogAdvisory,
                            iUnspecified_c,
                                iUnspecified_c,
                                    "[BlueFinVC] Shutdown (Index: %d).", iSensorIndex );

        const int iSensorRef = pApp->ReleaseSensorRef();

        if ( iSensorRef == 0 )
        {
            pApp->WatchSentinel( false );
        }

        __TRY
        {
            pApp->m_SensorListGuard.Enter();

            CSensorList<CVehicleData>  &rSensorList = pApp->SensorList();
            CVehicleData *pSensor = rSensorList.GetSensor( iSensorIndex );

            if ( pSensor != NULL )
            {
                iSuccess = ERROR_CODE_SUCCESS;

                if ( ! pSensor->Shutdown() )
                {
                    iSuccess = ERROR_CODE_FAILURE;
                }

                if ( ! rSensorList.RemoveSensor( iSensorIndex ) )
                {
                    iSuccess = ERROR_CODE_FAILURE;
                }
            }
            else
            {
                iSuccess = ERROR_CODE_FAILURE;
            }
        }
        __FINALLY
        {
            pApp->m_SensorListGuard.Leave();
        }
        __ENDFINALLY

        if ( iSensorRef == 0 )
        {
            pApp->CriticalParameterTable( NULL );
            pApp->EventLog( NULL );
        }
    }
    catch ( LPCTSTR lpszMessage )
    {
        iSuccess = ERROR_CODE_FAILURE;
        TRACE( _T( "BlueFinVC DLL, Startup() failed: %s\n" ), lpszMessage );
    }
    catch ( ... )
    {
        iSuccess = ERROR_CODE_FAILURE;
        TRACE( _T( "Shutdown(), BlueFinVC DLL - unspecified exception\n" ) );
    }

    return iSuccess;
}

extern "C" __declspec( dllexport )
int WINAPI Identify( SENSORINFO *psSensorInfo, int iSensorIndex )
{
    ManageDllState_m();

    GetAppOrAbort_m( "[BlueFinVC] Identify(), pApp is NULL" );

    int iSuccess = ERROR_CODE_FAILURE;      // Assume failure for now.

    try
    {
        pApp->LogEvent( eventLogDiagnostic, iUnspecified_c, iUnspecified_c, "[BlueFinVC] Identify() request." );

        __TRY
        {
            pApp->m_SensorListGuard.Enter();

            CSensorList<CVehicleData> &rSensorList = pApp->SensorList();
            CVehicleData *pSensor = rSensorList.GetSensor( iSensorIndex );

            if ( ( pSensor != NULL ) && ( pSensor->Identify( psSensorInfo ) ) )
            {
                iSuccess = ERROR_CODE_SUCCESS;
            }
        }
        __FINALLY
        {
            pApp->m_SensorListGuard.Leave();
        }
        __ENDFINALLY
    }
    catch ( LPCTSTR lpszMessage )
    {
        iSuccess = ERROR_CODE_FAILURE;
        TRACE( _T( "BlueFinVC DLL, Identify() failed: %s\n" ), lpszMessage );
    }
    catch ( ... )
    {
        iSuccess = ERROR_CODE_FAILURE;
        TRACE( _T( "BlueFinVC DLL, Identify() - unspecified exception\n" ) );
    }

    return iSuccess;
}

extern "C" __declspec( dllexport )
int WINAPI Load(    BYTE           *pbyData,
                    unsigned long  *pulSize,
                    unsigned long  *pulTimestamp,
                    int             iSensorIndex )
{
    ManageDllState_m();

    // NB: Load() is obsolete. Sensors now load their data directly into the PLC's sensor data pool (shared memory).

    UNREFERENCED_PARAMETER( pbyData );
    UNREFERENCED_PARAMETER( pulSize );
    UNREFERENCED_PARAMETER( pulTimestamp );
    UNREFERENCED_PARAMETER( iSensorIndex );

    return ERROR_CODE_FAILURE;
}

extern "C" __declspec( dllexport )
double WINAPI GetModuleVersion( void )
{
    ManageDllState_m();

    double dVersion = 0.00;

    CBlueFinVCApp *pApp = GetApp_m();

    ASSERT( pApp != NULL );

    if ( pApp != NULL )
    {
        dVersion = pApp->Version();
    }

    pApp->LogEvent( eventLogDiagnostic, iUnspecified_c, iUnspecified_c, "[BlueFinVC] GetModuleVersion(), Version is %.2lf", dVersion );

    return dVersion;
}

extern "C" __declspec( dllexport )
int WINAPI SendCommand( int     iSensorIndex,
                        int     iClientIndex,
                        char   *pszCommand )
{
    ManageDllState_m();

    GetAppOrAbort_m( "[BlueFinVC] SendCommand(), pApp is NULL" );

    int iSuccess = ERROR_CODE_FAILURE;

    try
    {
        pApp->LogEvent( eventLogAdvisory, iUnspecified_c, iUnspecified_c, "[BlueFinVC] Sensor: %d, Command: %s", iSensorIndex, pszCommand );

        __TRY
        {
            pApp->m_SensorListGuard.Enter();

            CSensorList<CVehicleData>  &rSensorList = pApp->SensorList();
            CVehicleData *pSensor     = rSensorList.GetSensor( iSensorIndex );

            if ( ( pSensor != NULL ) && ( ! pSensor->SendCommand( iClientIndex, pszCommand ) ) )
            {
                iSuccess = ERROR_CODE_SUCCESS;
            }
        }
        __FINALLY
        {
            pApp->m_SensorListGuard.Leave();
        }
        __ENDFINALLY
    }
    catch ( LPCTSTR lpszMessage )
    {
        iSuccess = ERROR_CODE_FAILURE;
        TRACE( _T( "BlueFinVC DLL, SendCommand() failed: %s\n" ), lpszMessage );
    }
    catch ( ... )
    {
        iSuccess = ERROR_CODE_FAILURE;
        TRACE( _T( "BlueFinVC DLL, SendCommand() - unspecified exception\n" ) );
    }

    return iSuccess;
}

extern "C" __declspec( dllexport )
int WINAPI RetrieveStatus(  BYTE           *pbyData,
                            unsigned long  *pulSize,
                            unsigned long  *pulTimestamp,
                            int            *piClientIndex,
                            int             iSensorIndex )
{
    ManageDllState_m();

    GetAppOrAbort_m( "[BlueFinVC] RetrieveStatus(), pApp is NULL" );

    int iSuccess = ERROR_CODE_FAILURE;

    try
    {
        __TRY
        {
            pApp->m_SensorListGuard.Enter();

            CSensorList<CVehicleData>  &rSensorList = pApp->SensorList();
            CVehicleData *pSensor     = rSensorList.GetSensor( iSensorIndex );

            ASSERT( pSensor != NULL );

            if ( pSensor != NULL )
            {
                // Here, the fail code simply means there is no more queued data available.

                iSuccess = ( pSensor->RetrieveStatus( pbyData, pulSize, pulTimestamp, piClientIndex ) ) ? ERROR_CODE_SUCCESS : ERROR_CODE_FAILURE;
            }
        }
        __FINALLY
        {
            pApp->m_SensorListGuard.Leave();
        }
        __ENDFINALLY
    }
    catch ( LPCTSTR lpszMessage )
    {
        iSuccess = ERROR_CODE_FAILURE;
        TRACE( _T( "BlueFinVC DLL, RetrieveStatus() failed: %s\n" ), lpszMessage );
    }
    catch ( ... )
    {
        iSuccess = ERROR_CODE_FAILURE;
        TRACE( _T( "BlueFinVC DLL, RetrieveStatus() - unspecified exception\n" ) );
    }

    return iSuccess;
}

extern "C" __declspec( dllexport )
int WINAPI RouteMessage(    BYTE           *pbyMessage,
                            unsigned long   ulSize,
                            unsigned long   ulTimeStamp,
                            int             iSocketIndex,
                            int             iSensorIndex )
{
    ManageDllState_m();

    GetAppOrAbort_m( "[BlueFinVC] RouteMessage(), pApp is NULL" );

    UNREFERENCED_PARAMETER( iSocketIndex );

    int iSuccess = ERROR_CODE_FAILURE;

    try
    {
        pApp->LogEvent( eventLogAdvisory, iUnspecified_c, iUnspecified_c, "[BlueFinVC] Routed Message, Sensor: %d, Size: %lu", iSensorIndex, ulSize );

        __TRY
        {
            pApp->m_SensorListGuard.Enter();

            CSensorList<CVehicleData>  &rSensorList = pApp->SensorList();
            CVehicleData *pSensor     = rSensorList.GetSensor( iSensorIndex );

            if ( ( pSensor != NULL ) && ( pSensor->RouteMessage( pbyMessage, ulSize, ulTimeStamp ) ) )
            {
                iSuccess = ERROR_CODE_SUCCESS;
            }
            else
            {
                TRACE( _T( "pSensor is NULL or pSensor->RouteMessage() failed\n" ) );
            }
        }
        __FINALLY
        {
            pApp->m_SensorListGuard.Leave();
        }
        __ENDFINALLY
    }
    catch ( LPCTSTR lpszMessage )
    {
        iSuccess = ERROR_CODE_FAILURE;
        TRACE( _T( "BlueFinVC DLL RouteMessage(), %s.\n" ), lpszMessage );
    }
    catch ( ... )
    {
        iSuccess = ERROR_CODE_FAILURE;
        TRACE( _T( "BlueFinVC DLL, RouteMessage() - unspecified exception\n" ) );
    }

    return iSuccess;
}

extern "C" __declspec( dllexport )
int WINAPI ResetSensor( int iSensorIndex )
{
    ManageDllState_m();

    GetAppOrAbort_m( "[BlueFinVC] ResetSensor(), pApp is NULL" );

    int iSuccess = ERROR_CODE_FAILURE;

    try
    {
        pApp->LogEvent( eventLogDiagnostic, iUnspecified_c, iUnspecified_c, "[BlueFinVC] ResetSensor(), Sensor: %d", iSensorIndex );

        __TRY
        {
            pApp->m_SensorListGuard.Enter();

            CSensorList<CVehicleData>  &rSensorList = pApp->SensorList();
            CVehicleData *pSensor     = rSensorList.GetSensor( iSensorIndex );

            if ( ( pSensor != NULL ) && ( pSensor->Reset() ) )
            {
                iSuccess = ERROR_CODE_SUCCESS;
            }
            else
            {
                TRACE( _T( "pSensor is NULL or pSensor->Reset() failed.\n" ) );
            }
        }
        __FINALLY
        {
            pApp->m_SensorListGuard.Leave();
        }
        __ENDFINALLY
    }
    catch ( LPCTSTR lpszMessage )
    {
        iSuccess = ERROR_CODE_FAILURE;
        TRACE( _T( "BlueFinVC DLL, ResetSensor() failed: %s\n" ), lpszMessage );
    }
    catch ( ... )
    {
        iSuccess = ERROR_CODE_FAILURE;
        TRACE( _T( "BlueFinVC DLL, ResetSensor() - unspecified exception\n" ) );
    }

    return iSuccess;
}

extern "C" __declspec( dllexport )
int WINAPI IsSensorHealthy( int iSensorIndex )
{
    ManageDllState_m();

    GetAppOrAbort_m( "[BlueFinVC] IsSensorHealthy(), pApp is NULL" );

    int iSuccess = ERROR_CODE_FAILURE;

    try
    {
        pApp->LogEvent( eventLogDiagnostic, iUnspecified_c, iUnspecified_c, "[BlueFinVC] IsSensorHealthy(), Sensor: %d", iSensorIndex );

        iSuccess = ERROR_CODE_SUCCESS;

        DBG_UNREFERENCED_PARAMETER( iSensorIndex );

        __TRY
        {
            pApp->m_SensorListGuard.Enter();

            CSensorList<CVehicleData>  &rSensorList = pApp->SensorList();
            CVehicleData *pSensor     = rSensorList.GetSensor( iSensorIndex );

            if ( pSensor != NULL )
            {
                // Here the fail code means that the sensor has failed its own gross binary health check.

                iSuccess = ( pSensor->IsSensorHealthy() ) ? ERROR_CODE_SUCCESS : ERROR_CODE_FAILURE;
            }
            else
            {
                TRACE( _T( "pSensor is NULL\n" ) );
            }
        }
        __FINALLY
        {
            pApp->m_SensorListGuard.Leave();
        }
        __ENDFINALLY

        if ( iSuccess != ERROR_CODE_SUCCESS )
        {
            pApp->LogEvent( eventLogDiagnostic, iUnspecified_c, iUnspecified_c, "[BlueFinVC] IsSensorHealthy(), Sensor is unhealthy" );
        }
    }
    catch ( LPCTSTR lpszMessage )
    {
        iSuccess = ERROR_CODE_FAILURE;
        TRACE( _T( "BlueFinVC DLL, IsSensorHealthy() failed: %s\n" ), lpszMessage );
    }
    catch ( ... )
    {
        iSuccess = ERROR_CODE_FAILURE;
        TRACE( _T( "BlueFinVC DLL, IsSensorHealthy() - unspecified exception\n" ) );
    }

    return iSuccess;
}

///////////////////////////////////////////////////////////////////////////////
// External helpers.

bool ReportAlarm( const bool &rbSet, const int &riAlarmId, char *pszFormat, ... )
{
    bool bSuccess = false;

    try
    {
        CBlueFinVCApp *pApp = CBlueFinVCApp::This();

        if ( pApp != NULL )
        {
            CString sMessage;

            va_list pArgumentList;
            va_start( pArgumentList, pszFormat );
            sMessage.FormatV( pszFormat,  pArgumentList );
            va_end( pArgumentList );

            bSuccess = pApp->LogEvent(  eventLogAlarm,
                                            ( ( rbSet ) ? eventActionAlarmSet : eventActionCleared ),
                                                riAlarmId,
                                                    const_cast<char *>( static_cast<LPCTSTR>( sMessage ) ) );
        }
    }
    catch ( ... )
    {
        bSuccess = false;
    }

    return bSuccess;
}

