//
//  Copyright © 2004, 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:   RESON7kSonar.cpp
//
//  Project:    6046
//
//  Author(s):  W. Arcus
//
//  Purpose:    Implementation of the main DLL app class CRESON7kSonarApp and C API entry points for
//              the 6046 interface.
//
//  Notes:      1)  This DLL is dynamically linked against the MFC DLLs. Therefore any functions exported from 
//                  this DLL which calls into MFC must have the AFX_MANAGE_STATE macro added at the very beginning
//                  of each function. The macro ManageDllState_m() is defined for this purpose. See ..\..\Include\Utils.h
//                  and MFC technical notes 33 and 58.
//

#include "StdAfx.h"
#include "RESON7kSonar.h"
#include "RegistryKeys.h"

#include "..\..\Include\Exports.h"                              // 6046 interface entry point definitions.
#include "7kOutBoundMemory.h"

#include <atlbase.h>

// Link against the NetUtils DLL for various support utilities.

#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 helpers using an unnamed namespace to ensure internal linkage.

namespace                                                           // Begin unnamed namespace.
{

    const double    dModuleVersion_c                                = 3.0000;

}                                                                   // End unnamed namespace.

#pragma CompileMessage_m( "TESTING NOTE: CHECK THAT ADDITION OF CRITICAL SECTION m_SensorListGuard ADDED IN THIS MODULE HAS NO ADVERSE EFFECT" )

#define BEGINSENSORANDAPP( Text, Index )        ManageDllState_m();                                                                 \
                                                int iSuccess = ERROR_CODE_FAILURE;                                                  \
                                                CRESON7kSonarApp * pApp = GetApp();                                                 \
                                                if ( pApp == NULL )                                                                 \
                                                {                                                                                   \
                                                    TRACE( _T( "File: " __FILE__ ", Line: %d: " #Text "\n" ), __LINE__ );           \
                                                    ThrowMessage_m( "pApp is NULL" );                                               \
                                                }                                                                                   \
                                                pApp->m_SensorListGuard.Enter();                                                    \
                                                try                                                                                 \
                                                {                                                                                   \
                                                    CSensorList<C7kSubsystem> &rSensorList = pApp->SensorList();                    \
                                                    C7kSubsystem              *pSensor     = rSensorList.GetSensor( Index );        \
                                                    if ( pSensor == NULL )                                                          \
                                                    {                                                                               \
                                                        ThrowMessage_m( "pSensor is NULL" );                                        \
                                                    }


#define ENDSENSORANDAPP( Text )                 }                                                                                   \
                                                catch ( LPCTSTR lpszMessage )                                                       \
                                                {                                                                                   \
                                                    iSuccess = ERROR_CODE_FAILURE;                                                  \
                                                    TRACE( _T( "[RESON7kSonar DLL] %s failed: %s\n" ), Text, lpszMessage );         \
                                                }                                                                                   \
                                                catch ( ... )                                                                       \
                                                {                                                                                   \
                                                    iSuccess = ERROR_CODE_FAILURE;                                                  \
                                                    TRACE( _T( "[RESON7kSonar DLL] %s unspecified exception.\n" ), Text );          \
                                                }                                                                                   \
                                                pApp->m_SensorListGuard.Leave();


/////////////////////////////////////////////////////////////////////////////
// CRESON7kSonarApp class implementation.

using namespace NRESON7kSonarApp;

CRESON7kSonarApp theApp;                         // Our one and only CRESON7kSonarApp object.

BEGIN_MESSAGE_MAP(CRESON7kSonarApp, CWinApp)
    //{{AFX_MSG_MAP(CRESON7kSonarApp)
        // 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()

/////////////////////////////////////////////////////////////////////////////
// CRESON7kSonarApp class implementation.

CRESON7kSonarApp::CRESON7kSonarApp( void )
                 :CSensorInterface()
{
    try
    {
        m_bRemote           = false;

        m_ulAutoShutdown    = 0UL;
        m_bMessageRouting   = 1UL;
        m_bParameterRouting = 1UL;

        m_ulPort            = 7000UL;
        m_szAddress         = _T( "127.0.0.1" );

        m_pSentinel         = NULL;
    }
    catch ( CMemoryException *pMemException )
    {
        TRACE( _T( "CRESON7kSonarApp::CRESON7kSonarApp(), CMemoryException caught!\n" ) );
        pMemException->Delete();
    }
    catch ( ... )
    {
        TRACE( _T( "CRESON7kSonarApp::CRESON7kSonarApp(), Unspecified exception caught!\n" ) );
    }
}

CRESON7kSonarApp::~CRESON7kSonarApp( void )
{
    __TRY
    {
        if ( m_pSentinel != NULL )
        {
            delete m_pSentinel;
            m_pSentinel = NULL;
        }
    }
    __FINALLY
    {
        ASSERT( m_pSentinel == NULL );

        m_pSentinel      = NULL;
        m_szAddress      = _T( "127.0.0.1" );
        m_ulPort         = 7000UL;
        m_ulAutoShutdown = 0UL;
        m_bRemote        = false;
    }
    __ENDFINALLY
}

BOOL CRESON7kSonarApp::InitInstance( void )
{
    // Main initialization service here.

    BOOL bSuccess = FALSE;                      // Assume failure for now.

    try
    {
        // Initialize WinSock first...

        if ( ! AfxSocketInit() )
        {
            ThrowMessage_m( "Windows sockets initialization failure" );
        }

        // then set the module version number and get main settings from the registry.

        Version( dModuleVersion_c );

        SettingsFromRegistry();

        bSuccess = TRUE;
    }
    catch ( LPCTSTR lpszMessage )
    {
        bSuccess = FALSE;
        TRACE( _T( "CRESON7kSonarApp::InitInstance(), %s.\n" ), lpszMessage );
    }
    catch ( ... )
    {
        bSuccess = FALSE;
        TRACE( _T( "CRESON7kSonarApp::InitInstance(), Unspecified exception caught.\n" ) );
    }

    ASSERT( bSuccess );

    return bSuccess;
}

int CRESON7kSonarApp::ExitInstance( void )
{
    // Place significant per instance clean up here.

    return CWinApp::ExitInstance();
}

CRESON7kSonarApp * CRESON7kSonarApp::This( void )
{
    // Retrieve our psuedo this pointer -- a helper for static members.

    return ( static_cast<CRESON7kSonarApp *>( CSensorInterface::This() ) );
}

CSensorList<C7kSubsystem> & CRESON7kSonarApp::SensorList( void ) const
{
    return m_SensorList;
}

bool CRESON7kSonarApp::AutoShutdown( void ) const
{
    return static_cast<bool>( m_ulAutoShutdown != 0UL );
}

void CRESON7kSonarApp::SettingsFromRegistry( void )
{
    try
    {
        if ( ! m_bRetrievedSonarSettings )
        {
            CRegKey Key;
            DWORD   dwValue = 0UL;

            Key.Attach( HKEY_USERS );

            if ( Key.Open( HKEY_USERS, lpszRESON7kSonarRegistryKey_c ) == ERROR_SUCCESS )
            {
                if ( Key.QueryValue( dwValue, lpsz6046RESON7kSonarAutoShutdownRegistryKey_c ) == ERROR_SUCCESS )
                {
                    m_ulAutoShutdown = static_cast<unsigned long>( dwValue );
                }

                if ( Key.QueryValue( dwValue, lpsz6046RESON7kSonarParameterRoutingRegistryKey_c ) == ERROR_SUCCESS )
                {
                    m_bParameterRouting = static_cast<unsigned long>( dwValue );
                }

                if ( Key.QueryValue( dwValue, lpsz6046RESON7kSonarMessageRoutingRegistryKey_c ) == ERROR_SUCCESS )
                {
                    m_bMessageRouting = static_cast<unsigned long>( dwValue );
                }

                Key.Close();
            }

            m_bRetrievedSonarSettings = true;
        }
    }
    catch ( LPCTSTR lpszMessage )
    {
        TRACE( _T( "CRESON7kSonarApp::SettingsFromRegistry(), %s\n" ), lpszMessage );
    }
    catch ( ... )
    {
        TRACE( _T( "CRESON7kSonarApp::SettingsFromRegistry(), unspecified exception caught\n" ) );
    }
}

void CRESON7kSonarApp::HandleSubscribedCriticalParameterUpdate( const int                          &riSensorIndex,
                                                                const unsigned long                &rulTableSize,
                                                                const CRITICALPARAMENTRY * const    psTable )
{
    // Virtual override from base class CSensorInterface.

    // This member represents the callback invoked from a thread running within the Payload Controller. Accordingly,
    // it needs to be appropriately synchronize access to shared resources.

#pragma CompileMessage_m( "TODO: CHECK THAT CRITICAL PARAMETER TABLE IS BEING GUARDED WRT THREADS INTERNALLY" )
#pragma CompileMessage_m( "TODO: ONLY UPATE SENSOR EITHER A) FIRST TIME OR B) WHEN PARAMETER CHANGES SIGNIFICANTLY" )

    if ( ( rulTableSize > 0UL ) && ( riSensorIndex >= 0 ) && ( psTable != NULL ) )
    {
        __TRY
        {
            m_SensorListGuard.Enter();

            CRESON7kSonarApp * pApp = GetApp();
            ASSERT( pApp != NULL );

            if ( pApp != NULL )
            {
                CSensorList<C7kSubsystem> &rSensorList = pApp->SensorList();
                C7kSubsystem              *pSensor     = rSensorList.GetSensor( riSensorIndex );
                ASSERT( pSensor != NULL );

                if ( pSensor != NULL )
                {
                    for ( unsigned long ulEntry = 0UL; ulEntry < rulTableSize; ulEntry++ )
                    {
                        switch ( psTable[ ulEntry ].m_ulParameterId )
                        {
                            case criticalParameterSoundSpeed:

                                if ( ! pSensor->SoundSpeed( static_cast<float>( psTable[ ulEntry ].m_dValue ) ) )
                                {
                                    ThrowMessage_m( "pSensor->SoundSpeed() failed" );
                                }

                                //TRACE( _T( "[CRESON7kSonar DLL], SoundSpeed: %.2lf\n" ), psTable[ ulEntry ].m_dValue );

                                break;

                            case criticalParameterVesselSpeed:
                                break;

                            case criticalParameterAltitude:
                                break;

                            case criticalParameterYaw:
                                break;

                            case criticalParameterPitch:

                                if ( ! pSensor->Pitch( static_cast<float>( psTable[ ulEntry ].m_dValue ) ) )
                                {
                                    ThrowMessage_m( "pSensor->Pitch() failed" );
                                }

                                //TRACE( _T( "[CRESON7kSonar DLL], Pitch: %.2lf\n" ), psTable[ ulEntry ].m_dValue );

                                break;

                            case criticalParameterRoll:

                                if ( ! pSensor->Roll( static_cast<float>( psTable[ ulEntry ].m_dValue ) ) )
                                {
                                    ThrowMessage_m( "pSensor->Roll() failed" );
                                }

                                //TRACE( _T( "[CRESON7kSonar DLL], Roll: %.2lf\n" ), psTable[ ulEntry ].m_dValue );

                                break;

                            default:

                                if ( ( psTable[ ulEntry ].m_ulParameterId >= criticalParameterPulseRepPeriod0  ) &&
                                     ( psTable[ ulEntry ].m_ulParameterId <= criticalParameterPulseRepPeriod15 )  )
                                {
                                    // TBD.
                                }
                                else
                                {
                                    TRACE( _T( "CRESON7kSonarApp::HandleSubscribedCriticalParameterUpdate(), Unknown parameter detected.\n" ) );
                                }

                                break;
                        }
                    }
                }
            }
        }
        __FINALLY
        {
            m_SensorListGuard.Leave();
        }
        __ENDFINALLY
    }
}

bool CRESON7kSonarApp::ConfigureCriticalParameters( const int &riSensorIndex )
{
    bool bSuccess = false;

    try
    {
        CCriticalParameterTable::ParameterList_t CriticalParameters;

#pragma CompileMessage_m( "TODO: EXTEND CRTICAL PARAMETER SUBSCRIPTION TO ATTITUDE DATA ETC ... TBD" )

        CriticalParameters.push_back( criticalParameterSoundSpeed );

        bSuccess = SubscribeToCriticalParameters( riSensorIndex, CriticalParameters );
    }
    catch ( ... )
    {
        bSuccess = false;
        TRACE( _T( "CRESON7kSonarApp::ConfigureCriticalParameters(), Unspecified exception caught!" ) );
    }

    return bSuccess;
}

bool CRESON7kSonarApp::IsMessageRoutingEnabled( void ) const
{
    return ( m_bMessageRouting != 0UL );
}

bool CRESON7kSonarApp::IsParameterRoutingEnabled( void ) const
{
    return ( m_bParameterRouting != 0UL );
}

void CRESON7kSonarApp::Sentinel( const bool &rbStart )
{
    if ( rbStart )
    {
        ASSERT( m_pSentinel == NULL );

        if ( m_pSentinel == NULL )
        {
            __TRY
            {
                m_pSentinel = new CWatchSentinel;
            }
            __FINALLY
            {
                ASSERT( m_pSentinel != NULL );
            }
            __ENDFINALLY

            if ( ( m_pSentinel == NULL ) || ( ! m_pSentinel->SetHealthCheckCallback( CheckSensorStatus, this ) ) )
            {
                LogEvent( eventLogDiagnostic,
                            iUnspecified_c,
                                iUnspecified_c,
                                    "[RESON7kSonar] Can not start the sonar watchdog sentinel." );
            }
        }
    }
    else if ( m_pSentinel != NULL )
    {
        delete m_pSentinel;
        m_pSentinel = NULL;
    }
}

void CRESON7kSonarApp::CheckSensorStatus( void *pvParam, const int &riSensorIndex )
{
    CRESON7kSonarApp *pthis = static_cast<CRESON7kSonarApp *>( pvParam );
    ASSERT( pthis != NULL );

    if ( pthis != NULL )
    {
        pthis->SensorCheck( riSensorIndex );
    }
}

void CRESON7kSonarApp::SensorCheck( const int &riSensorIndex )
{
    // This is a callback executed from a thread within the m_pSentinel object.

    //TRACE( _T( "CRESON7kSonarApp::SensorCheck(), About to check sensor health. Index:%d.\n" ), riSensorIndex );

    __TRY
    {
        m_SensorListGuard.Enter();

        CSensorList<C7kSubsystem> &rSensorList = SensorList();
        C7kSubsystem              *pSensor     = NULL;

        if ( riSensorIndex == -1 )
        {
            // No specific sensor specified so query all.

            std::vector<int> IndexList = rSensorList.GetSensorIndicies();

            for ( std::vector<int>::iterator pIndex = IndexList.begin(); pIndex != IndexList.end(); pIndex++ )
            {
                if ( ( pSensor = rSensorList.GetSensor( *pIndex ) ) != NULL )
                {
                    if ( ! pSensor->QueryHealth() )
                    {
                        TRACE( _T( "CRESON7kSonarApp::SensorCheck(), pSensor->QueryHealth() failed, sensor index: %d.\n" ), *pIndex );
                    }
                }
            }
        }
        else if ( riSensorIndex >= 0 )
        {
            if ( ( pSensor = rSensorList.GetSensor( riSensorIndex ) ) != NULL )
            {
                if ( ! pSensor->QueryHealth() )
                {
                    TRACE( _T( "CRESON7kSonarApp::SensorCheck(), pSensor->QueryHealth() failed, sensor index: %d.\n" ), riSensorIndex );
                }
            }
        }
    }
    __FINALLY
    {
        m_SensorListGuard.Leave();
    }
    __ENDFINALLY
}

bool CRESON7kSonarApp::Check7kCenterIsRunning( void )
{
    return C7kOutBoundMemory::DoesExist();
}

///////////////////////////////////////////////////////////////////////////////
// 6046 exported interface functions.

extern "C" __declspec( dllexport )
int WINAPI Startup( SENSORINFO     *psSensorInfo,
                    DWORD           dwThreadId,
                    UINT            uiDataReadyMessageId,
                    UINT            uiReplyReadyMessageId,
                    void           *vpfnLog,
                    void           *vpCriticalParameters )
{
    ManageDllState_m();

    int iSuccess = ERROR_CODE_FAILURE;

    try
    {
        C7kSubsystem     *pSensor      = NULL;
        CRESON7kSonarApp *pApp         = GetApp();
        int               iRefCount    = 0;

        const int         iSensorIndex = psSensorInfo->m_iSensorIndex;      // Note: the sensor index is unique to each sensor and assigned by the PLC.

        if ( pApp == NULL )
        {
            ThrowMessage_m( "Can't acquire DLL APP object" );
        }
        else if ( pApp->SensorRefCount() == 0 )
        {
            // Install the event log to report problems etc.

            pApp->EventLog( vpfnLog );

            // Check that the 7k center is running... for now its an error but we may want to consider trying to launch it before proceeding.

            if ( ! CRESON7kSonarApp::Check7kCenterIsRunning() )
            {
                pApp->LogEvent( eventLogAdvisory,
                                    iUnspecified_c,
                                        iUnspecified_c,
                                            "[RESON7kSonar] Startup, 7kCenter is not running. Reported by sensor: %d",
                                                iSensorIndex );

                ThrowMessage_m( "7kCenter is not running" );
            }

            // Only now is it appropriate for the reference count to be incremented so that when the PLC invokes the shutdown, 
            // relevant cleanup will occur.

            iRefCount = pApp->AddSensorRef();

#if 1
#pragma CompileMessage_m( "TODO: RE-ENABLE CRITICAL PARAMETER HANDLING ONCE MECHAMISM IS COMPLETED" )
            DBG_UNREFERENCED_PARAMETER( vpCriticalParameters );
#else
            pApp->CriticalParameterTable( vpCriticalParameters );
#endif
        }
        else
        {
            iRefCount = pApp->AddSensorRef();
        }

        // Ok, we're go for lauch... so increment the reference count and proceed as normal.

        CSensorList<C7kSubsystem> &rSensorList = pApp->SensorList();

        pSensor = rSensorList.AddSensor( iSensorIndex );

        // The sensor id is a PLC specific code for each sensor (see ESENSORID of processinf.h) whereas
        // device Id is the 7k device id per the 7k data format ICD -- they shouldn't be confused. The latter
        // is used to form the 7k file header -- record #7200 -- during logging.

        psSensorInfo->m_iPriority            = 1;                           // Not used at this time.
        psSensorInfo->m_iFilter              = 0;                           // Not used at this time.
        psSensorInfo->m_iSensorId            = sensorIdRESON7kSonar;        // PLC specific index for this device type & generic.
        psSensorInfo->m_iMediaType           = mediaTypeInternal;           // Not used at this time.
        psSensorInfo->m_unSystemEnumerator   = 0;                           // Set by the PLC to discriminate amongst multiple sensors w/ the same device id.
        psSensorInfo->m_iAccepts7kRecords    = 1;                           // Device can accept incoming (routed) 7k data records.

        if ( ! pSensor->SetDeviceIdInfo( psSensorInfo ) )
        {
            ThrowMessage_m( "Can't set device id parameters" );
        }

        // Now set the key sensor parameters and descriptors then start it.

        if ( ! pSensor->Startup( iSensorIndex, dwThreadId, uiDataReadyMessageId, uiReplyReadyMessageId, psSensorInfo->m_iActivateOnStart ) )
        {
            // The sensor has failed to start so remove it from the linked-list of sensors and decrement the reference count.

            pApp->ReleaseSensorRef();
            pApp->SensorList().RemoveSensor( iSensorIndex );

            ThrowMessage_m( "Sensor startup failure" );
        }

        // Set miscellaneous parameters.

        pSensor->AutoShutdownMode( pApp->AutoShutdown() );

        // The specific sensor type is unknown until the sonar has started. We therefore retrieve the specific sonar type
        // and populate the remaining fields. REMEMBER: psSensorInfo->m_iDeviceId corresponds to the 7k device ID 
        // (per the device Id table of the 7k data format ICD).

        pSensor->Description( psSensorInfo->m_iSensorType, &(psSensorInfo->m_szName[ 0 ]) );

        // Complete any one time post-startup initialization here -- e.g., setting the sensor's time etc as appropriate.

        if ( iRefCount == 1 )
        {
            // Kick off the watchdog.

            pApp->Sentinel( true );

#pragma CompileMessage_m( "TODO: WE MAY NEED TO SET THE 7K TIME INITIALLY, EVEN IF IN SLAVE MODE (TBD)" )
            // Set the sensor's time initially, perhaps... TBD.
            //if ( ! SetSystemTime( sTime7k, ulTimeStamp ) )
            //{
            //}
        }

        // Enable parameter and message routing.

        pSensor->ParameterRouting( pApp->IsParameterRoutingEnabled() );
        pSensor->MessageRouting( pApp->IsMessageRoutingEnabled() );

        // Subscribe to the relevant system critical parameter updates.

#if 1
#pragma CompileMessage_m( "TODO: RE-ENABLE CRITICAL PARAMETER HANDLING ONCE MECHAMISM IS COMPLETED" )
#else
        if ( ! pApp->ConfigureCriticalParameters( iSensorIndex ) )
        {
            ThrowMessage_m( "Can't subscribe for critical parameter update" );
        }
#endif

        // Annotate the survey event log that we're away.

        pApp->LogEvent( eventLogAdvisory,
                            iUnspecified_c,
                                iUnspecified_c,
                                    "[RESON7kSonar] Startup (Index: %d, AutoStart: %d)",
                                        iSensorIndex,
                                            psSensorInfo->m_iActivateOnStart );

        iSuccess = ERROR_CODE_SUCCESS;
    }
    catch ( LPCTSTR lpszMessage )
    {
        iSuccess = ERROR_CODE_FAILURE;
        TRACE( _T( "[RESON7kSonar DLL] Startup() failed: %s\n" ), lpszMessage );
    }
    catch ( ... )
    {
        iSuccess = ERROR_CODE_FAILURE;
        TRACE( _T( "[RESON7kSonar DLL] Startup() unspecified exception\n" ) );
    }

    return iSuccess;
}

extern "C" __declspec( dllexport )
int WINAPI Shutdown( int iSensorIndex )
{
    BEGINSENSORANDAPP( "[RESON7kSonar DLL] Shutdown(), pApp is NULL", iSensorIndex )
    {
        iSuccess = ERROR_CODE_SUCCESS;

        pApp->LogEvent( eventLogAdvisory,
                            iUnspecified_c,
                                iUnspecified_c,
                                    "[RESON7kSonar] Shutdown (Index: %d).", iSensorIndex );


#if 1
#pragma CompileMessage_m( "TODO: RE-ENABLE CRITICAL PARAMETER HANDLING ONCE MECHAMISM IS COMPLETED" )
#else
        // Unsubscribe from critical parameter updates hence terminating callbacks.

        if ( ! pApp->UnsubscribeFromCriticalParameters( iSensorIndex ) )
        {
            iSuccess = ERROR_CODE_FAILURE;
        }
#endif

        // Shutdown the sensor.

        if ( ! pSensor->Shutdown() )
        {
            iSuccess = ERROR_CODE_FAILURE;
        }

        // Remove the sensor from the linked-list of sensors.

        if ( ! rSensorList.RemoveSensor( iSensorIndex ) )
        {
            iSuccess = ERROR_CODE_FAILURE;
        }

        // Decrement the reference count and do any last-minute clean up when the last sensor
        // of this type is shutdown.

        if ( pApp->ReleaseSensorRef() == 0 )
        {
            pApp->CriticalParameterTable( NULL );
            pApp->Sentinel( false );
#pragma CompileMessage_m( "TODO: CHECK TO SEE WHETHER WE NEED TO REMOVE/CLEANUP ETC AFTER CRITICAL PARAMETER OBJECT ETC" )
            pApp->EventLog( NULL );
        }

        // Verify none of the intermediate shutdown procedures have failed.

        ASSERT( iSuccess == ERROR_CODE_SUCCESS );
    }
    ENDSENSORANDAPP( "Shutdown()" );

    return iSuccess;
}

extern "C" __declspec( dllexport )
int WINAPI Identify( SENSORINFO *psSensorInfo, int iSensorIndex )
{
    BEGINSENSORANDAPP( "[RESON7kSonar DLL] Identify(), pApp is NULL", iSensorIndex )
    {
        if ( ! pSensor->Identify( psSensorInfo ) )
        {
            ThrowMessage_m( "pSensor->Identify() failed." );
        }

        iSuccess = ERROR_CODE_SUCCESS;
    }
    ENDSENSORANDAPP( "Identify()" );

    return iSuccess;
}

extern "C" __declspec( dllexport )
int WINAPI Load(    BYTE           *pbyData,
                    unsigned long  *pulSize,
                    unsigned long  *pulTimestamp,
                    int             iSensorIndex )
{
    // THIS ENTRY POINT IS OBSOLETE: Sensors now load their 7k-formatted record data directly
    // into the PLC's shared memory data pool.

    ManageDllState_m();

    UNREFERENCED_PARAMETER( pbyData );
    UNREFERENCED_PARAMETER( pulTimestamp );
    UNREFERENCED_PARAMETER( iSensorIndex );

    *pulSize = 0UL;

    ASSERT( false );

    return ERROR_CODE_FAILURE;
}

extern "C" __declspec( dllexport )
double WINAPI GetModuleVersion( void )
{
    ManageDllState_m();

    CRESON7kSonarApp *pApp = GetApp();
    ASSERT( pApp != NULL );

    return ( ( pApp == NULL ) ? 0.0 : pApp->Version() );
}

extern "C" __declspec( dllexport )
int WINAPI SendCommand( int     iSensorIndex,
                        int     iClientIndex,
                        char   *pszCommand )
{
    BEGINSENSORANDAPP( "[RESON7kSonar DLL] SendCommand(), pApp is NULL", iSensorIndex )
    {
        if ( ! pSensor->SendCommand( iClientIndex, pszCommand ) )
        {
            ThrowMessage_m( "pSensor->SendCommand() failed" );
        }

        iSuccess = ERROR_CODE_SUCCESS;
    }
    ENDSENSORANDAPP( "SendCommand()" );

    return iSuccess;
}

extern "C" __declspec( dllexport )
int WINAPI RetrieveStatus(  BYTE           *pbyData,
                            unsigned long  *pulSize,
                            unsigned long  *pulTimestamp,
                            int            *piClientIndex,
                            int             iSensorIndex )
{
    BEGINSENSORANDAPP( "[RESON7kSonar DLL] RetrieveStatus(), pApp is NULL", iSensorIndex )
    {
        // Here, the fail code simply means there is no more queued data available.

        iSuccess = ERROR_CODE_SUCCESS;

        if ( ! pSensor->RetrieveStatus( pbyData, pulSize, pulTimestamp, piClientIndex ) )
        {
            iSuccess = ERROR_CODE_FAILURE;
        }
    }
    ENDSENSORANDAPP( "RetrieveStatus()" );

    return iSuccess;
}

extern "C" __declspec( dllexport )
int WINAPI RouteMessage(    BYTE           *pbyMessage,
                            unsigned long   ulSize,
                            unsigned long   ulTimeStamp,
                            int             iClientIndex,
                            int             iSensorIndex )
{
    BEGINSENSORANDAPP( "[RESON7kSonar DLL] RouteMessage(), pApp is NULL", iSensorIndex )
    {
        if ( ! pSensor->RouteMessage( iClientIndex, pbyMessage, ulSize, ulTimeStamp ) )
        {
            ThrowMessage_m( "pSensor->RouteMessage() failed" );
        }

        iSuccess = ERROR_CODE_SUCCESS;
    }
    ENDSENSORANDAPP( "RouteMessage()" );

    return iSuccess;
}

extern "C" __declspec( dllexport )
int WINAPI ResetSensor( int iSensorIndex )
{
    BEGINSENSORANDAPP( "[RESON7kSonar DLL] ResetSensor(), pApp is NULL", iSensorIndex )
    {
        if ( ! pSensor->Reset() )
        {
            ThrowMessage_m( "pSensor->Reset() failed." );
        }

        iSuccess = ERROR_CODE_SUCCESS;
    }
    ENDSENSORANDAPP( "ResetSensor()" );

    return iSuccess;
}

extern "C" __declspec( dllexport )
int WINAPI IsSensorHealthy( int iSensorIndex )
{
    BEGINSENSORANDAPP( "[RESON7kSonar DLL] IsSensorHealthy(), pApp is NULL", iSensorIndex )
    {
        // 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;
    }
    ENDSENSORANDAPP( "IsSensorHealthy()" );

    return iSuccess;
}

///////////////////////////////////////////////////////////////////////////////
// File scope helpers.

CRESON7kSonarApp * NRESON7kSonarApp::GetApp( void )
{
    return CRESON7kSonarApp::This();
}

bool NRESON7kSonarApp::LogEvent(    const int   &riEventType,
                                    char        *pszFormat, ... )
{
    bool bSuccess = false;

    try
    {
        CRESON7kSonarApp *pApp = CRESON7kSonarApp::This();

        if ( pApp == NULL )
        {
            ThrowMessage_m( "pApp is NULL" );
        }

        CString sMessage;
        va_list pArgumentList;

        va_start( pArgumentList, pszFormat );
        sMessage.FormatV( pszFormat,  pArgumentList );
        va_end( pArgumentList );

        sMessage = _T( "[RESON7kSonar] " ) + sMessage;

        bSuccess = pApp->LogEvent(  riEventType,
                                        iUnspecified_c,
                                            iUnspecified_c,
                                                const_cast<char *>( static_cast<LPCTSTR>( sMessage ) ) );
    }
    catch ( ... )
    {
        bSuccess = false;
    }

    return bSuccess;
}

bool NRESON7kSonarApp::ReportAlarm(  const bool  &rbSet,
                                     const int   &riAlarmId,
                                     char        *pszFormat, ... )
{
    bool bSuccess = false;

    try
    {
        CRESON7kSonarApp *pApp = CRESON7kSonarApp::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;
}



