//
//  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:   7kSubsystem.cpp
//
//  Project:    6046
//
//  Author(s):  W. Arcus
//
//  Purpose:    
//
//  Notes:      1)  The following notes are verbatim from Patrik Moberg regarding sonar messaging:
//
//                  a)  All #73xx, #74xx and #75xx are echoed out for D-UI and possible 6046 logging.
//                  b)  #7001 is never trasmited unless you request it. To request a 7001 set device id,
//                          subsystem id and enumerator to 0 in the record frame.
//                  c)  #7002, #7004, #7005 is only transmited if they change. You will need to request them for 
//                          each file you create.
//                  d)  #7000 is transmited once with each ping and sonar (one system can hold many sonars).
//
//              2)  The following changes in the 7k data center (as provided by Patrik) also apply:
//
//                  a)  The 7-P 7kCenter is now being addressed by device id 7000 (enum = 0) and not 0.
//                  b)  The 7kCenter will put an NF in front of each record written to OutBound MMF
//                  c)  Any writer will put an NF after the SOURCEINFO 'header' for any record
//                          written to InBound MMF, i.e., in between SOURCEINFO header and RDF.
//

#include "StdAfx.h"
#include "7kSubsystem.h"
#include "6046RemoteCommand.h"
#include "RESON7kSonar.h"
#include "7kSonarMessages.h"
#include "RegistryKeys.h"

#include "..\..\..\Utils\NetUtils\7kProtocol.h"
#include "..\..\..\Utils\NetUtils\7kStatus.h"
#include "..\..\..\Utils\NetUtils\7kAcknowlege.h"

#include <math.h>
#include <atlbase.h>
#include <algorithm>
#include <vector>

#pragma comment ( lib, "Rpcrt4.lib" )                                                               // Resolve linkage to UuidCreate().

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif

///////////////////////////////////////////////////////////////////////////////
// Internal definitions etc.

#undef  ENABLE_CONFIG
//#define ENABLE_CONFIG

namespace                                                                                           // Begin anonymous namespace,
{

    const unsigned long ulDefaultDataCollectionMode_c   =   C7kSubsystem::m_ulDataCollectionModeGenericSensors |
                                                                C7kSubsystem::m_ulDataCollectionModeBathymetricData |
                                                                    C7kSubsystem::m_ulDataCollectionModeBackscatteredImagery;

    const unsigned long ulConfigReplyTimeout_c          =   1000UL;                                 // Wait upto 1s for the sonar to send back its config settings.

    class CDeviceInfo
    {
    private:

        //////////////
        // Attributes.

        const LPCTSTR                       m_lpszRESON7kSonarInterfaceTypeRegistryKey;
        const LPCTSTR                       m_lpszRESON7kSonarAddressRegistryKey;
        const LPCTSTR                       m_lpszRESON7kSonarInPortRegistryKey;
        const LPCTSTR                       m_lpszRESON7kSonarOutPortRegistryKey;
        const LPCTSTR                       m_lpszRESON7kSonarCollectionModeRegistryKey;

    public:

        //////////////
        // Attributes.

        unsigned long                       m_ulCollectionMode;
        CDispatcher::EINTERFACETYPE         m_eInterfaceType;
        unsigned long                       m_ulPortOut;
        unsigned long                       m_ulPortIn;
        CString                             m_sAddress;


        //////////////
        // Services.

        CDeviceInfo( void )
            :m_lpszRESON7kSonarInterfaceTypeRegistryKey     ( "InterfaceType" ),
             m_lpszRESON7kSonarAddressRegistryKey           ( "Address" ),
             m_lpszRESON7kSonarInPortRegistryKey            ( "InPort" ),
             m_lpszRESON7kSonarOutPortRegistryKey           ( "OutPort" ),
             m_lpszRESON7kSonarCollectionModeRegistryKey    ( "CollectionMode" )
        {
            m_ulCollectionMode   = ulDefaultDataCollectionMode_c;

            m_eInterfaceType     = CDispatcher::interfaceTypeLocal;
            m_ulPortOut          = 0;
            m_ulPortIn           = 0;

            m_sAddress.Empty();
        }

        virtual ~CDeviceInfo( void )
        {
        }

        bool CDeviceInfo::RetrieveParameters(   const unsigned long     &rulDeviceId,
                                                const unsigned short    &runSystemEnumerator )
        {
            bool bRetrieved = false;

            try
            {
                CRegKey Key;

                Key.Attach( HKEY_USERS );

                CString DeviceKey;
        
                DeviceKey.Format( "%s\\Device_%lu_%d", lpszRESON7kSonarRegistryKey_c, rulDeviceId, runSystemEnumerator );

                if ( Key.Open( HKEY_USERS, DeviceKey ) == ERROR_SUCCESS )
                {
                    DWORD dwValue = 0;

                    if ( Key.QueryValue( dwValue, m_lpszRESON7kSonarCollectionModeRegistryKey ) == ERROR_SUCCESS )
                    {
                        m_ulCollectionMode = static_cast<unsigned long>( dwValue );
                    }

                    if ( Key.QueryValue( dwValue, m_lpszRESON7kSonarInterfaceTypeRegistryKey ) == ERROR_SUCCESS )
                    {
                        m_eInterfaceType = ( ( dwValue == 0UL ) ? CDispatcher::interfaceTypeLocal : CDispatcher::interfaceTypeRemote );
                    }

                    if ( m_eInterfaceType == CDispatcher::interfaceTypeRemote )
                    {
                        DWORD dwLength = _MAX_PATH;

                        CString Address;
                        dwValue = Key.QueryValue( Address.GetBuffer( _MAX_PATH ), m_lpszRESON7kSonarAddressRegistryKey, &dwLength );
                        Address.ReleaseBuffer();

                        if ( dwValue == ERROR_SUCCESS )
                        {
                            m_sAddress = Address;
                        }

                        if ( Key.QueryValue( dwValue, m_lpszRESON7kSonarInPortRegistryKey ) == ERROR_SUCCESS )
                        {
                            m_ulPortIn = static_cast<unsigned long>( dwValue );
                        }

                        if ( Key.QueryValue( dwValue, m_lpszRESON7kSonarOutPortRegistryKey ) == ERROR_SUCCESS )
                        {
                            m_ulPortOut = static_cast<unsigned long>( dwValue );
                        }
                    }

                    Key.Close();

                    bRetrieved = true;
                }
            }
            catch ( ... )
            {
                bRetrieved = false;
            }

            return bRetrieved;
        }

        void SetDefault( void )
        {
            m_ulCollectionMode   = ulDefaultDataCollectionMode_c;
            m_eInterfaceType     = CDispatcher::interfaceTypeLocal;
            m_ulPortOut          = 0;
            m_ulPortIn           = 0;
            m_sAddress.Empty();
        }
    };
}                                                                                                   // End anonymous namespace.

///////////////////////////////////////////////////////////////////////////////
// C7kSubsystem class implementation.

// Used namespaces and parts thereof plus internal static constants.

using namespace     N7kSonarMessages;
using namespace     NRESON7kSonarApp;
using               std::find;
using               std::sort;
using               std::vector;

const unsigned long C7kSubsystem::m_ulDataCollectionModeGenericSensors       = 0x00000001UL;        // Generic sensors       -- Record# 1000 to 1999.
const unsigned long C7kSubsystem::m_ulDataCollectionModeBeamData             = 0x00000002UL;        // Beam data             -- Record# 7008, 7000 to 7005.
const unsigned long C7kSubsystem::m_ulDataCollectionModeBathymetricData      = 0x00000004UL;        // Bathymetric data      -- Record# 7006, 7000 to 7005.
const unsigned long C7kSubsystem::m_ulDataCollectionModeBackscatteredImagery = 0x00000008UL;        // Backscattered imagery -- Record# 7007, 7000 to 7005.

///////////////////////////////////////////////////////////////////////////////
// Public services.

C7kSubsystem::C7kSubsystem( const int &riSensorIndex )
             :CSensor     ( riSensorIndex )

{
    m_bInitialized         = false;
    m_bAutoShutdown        = false;
    m_bParameterRouting    = true;
    m_bMessageRouting      = true;

    m_unSystemEnumerator   = 0;
    m_ulDeviceId           = 0UL;

    m_ulDataCollectionMode = ulDefaultDataCollectionMode_c;

    try
    {
        // Clear the various internal lists.

        m_ClientReplyList.clear();
        m_SonarReplyPendingList.clear();

        // Set the unique 128 bit client id to aid with ACK/NAK processing.

        GUID sNewId = { 0 };

        if ( ::UuidCreate( &sNewId ) == RPC_S_UUID_NO_ADDRESS )
        {
            ASSERT( false );
            memset( &sNewId, 0x00, sizeof ( GUID ) );
        }

        m_sClientId = sNewId;

        // Set the default configuration prior to overwriting from stored file.

        m_Config.SetDefault();

        // Map the PLC's shared sensor data pool for subsequent writing.

        if ( ! m_SensorDataPool.IsInitialized() )
        {
            ThrowMessage_m( "Failed to construct sensor data pool object" );
        }

        // Set our private command object's parent.

        m_SonarCommands.SetParent( this );

        // Assume all's well at this time.

        m_bInitialized = true;
    }
    catch ( LPCTSTR lpszMessage )
    {
        m_bInitialized = false;
        TRACE( _T( "C7kSubsystem::C7kSubsystem(), %s\n" ), lpszMessage );
    }
    catch ( ... )
    {
        m_bInitialized = false;
        TRACE( _T( "C7kSubsystem::C7kSubsystem(), Unspecified exception caught.\n" ) );
    }

    ASSERT( m_bInitialized );
}

C7kSubsystem::~C7kSubsystem( void )
{
    // Ensure orderly shutdown has occured before destroying our object.

    if ( IsStarted() )
    {
        if ( ! Shutdown() )
        {
            TRACE( _T( "C7kSubsystem::~C7kSubsystem(), Shutdown() failed.\n" ) );
        }
    }

    __TRY
    {
        m_SonarReplyListGuard.Enter();

        m_SonarReplyPendingList.clear();
        m_ClientReplyList.clear();
    }
    __FINALLY
    {
        m_SonarReplyListGuard.Leave();

        m_bMessageRouting      = false;
        m_bParameterRouting    = false;
        m_bAutoShutdown        = false;
        m_bInitialized         = false;

        m_ulDataCollectionMode = ulDefaultDataCollectionMode_c;
    }
    __ENDFINALLY

}

bool C7kSubsystem::IsInitialized( void ) const
{
    return m_bInitialized;
}

bool C7kSubsystem::SetDeviceIdInfo( SENSORINFO const * psSensorInfo )
{
    ASSERT( psSensorInfo != NULL );

    if ( psSensorInfo != NULL )
    {
        m_ulDeviceId         = psSensorInfo->m_ulDeviceId;
        m_unSystemEnumerator = psSensorInfo->m_unSystemEnumerator;
        return true;
    }

    return false;
}

bool C7kSubsystem::SetSystemTime(   const TIME7K        &rsTime7k,
                                    const unsigned long &rulTimeStamp )
{
    // Provide external access to allow the sonar's clock to be set... possibly required initially.

#pragma CompileMessage_m( "TESTNOTE: NEED TO TEST THIS METHOD AND ITS SUBORDINATES" )

    bool bSuccess = false;

    try
    {
        // Format the 7k time message.

        TIMEMESSAGERTH sRTH = { 0 };

        CGeneric7kRecordEncoder<TIMEMESSAGERTH> TimeRecord;

        // Encode the message.

        if ( TimeRecord.Encode( recordTypeTimeMessage, &sRTH ) )
        {
            // Apply the age correction directly to the embedded time structure...

            *(static_cast<TIME7K *>( TimeRecord )) = AgeTime( rsTime7k, rulTimeStamp );

            // then send the record to the sonar.

            __TRY
            {
                m_SonarCommandGuard.Enter();
                bSuccess = m_SonarCommands.SendRecord( TimeRecord );
            }
            __FINALLY
            {
                m_SonarCommandGuard.Leave();
            }
            __ENDFINALLY
        }
    }
    catch ( ... )
    {
        bSuccess = false;
        TRACE( _T( "C7kSubsystem::SetSystemTime(), Unspecified exception caught.\n" ) );
    }

    return bSuccess;
}

bool C7kSubsystem::Description( int &riSensorType, char *pszName )
{
    // Psuedo-synchronous call to acquire the sonar's descritpion. The invoking DLL entry function (Startup())
    // requires this info upon return. The configuration settings record (#7001) should have already been 
    // requested at startup thus we await its reciept and processing.
    
    // Assume failure for now; default is the generic 7100 MBES.

    bool bSuccess = false;

    riSensorType = sensorTypeMBES;

    try
    {
        if ( pszName == NULL )
        {
            bSuccess = true;
        }
        else if ( WaitForRecord( recordTypeConfigurationSettings, 2000 ) )
        {
            CDispatcher *pDispatcher = GetAndValidateDispatcher();
            ASSERT( pDispatcher != NULL );

            CSystemInfo::DeviceInfo_t DeviceTable = pDispatcher->DeviceInfo( SensorIndex() );

            for ( CSystemInfo::DeviceIdIterator_t pDevice = DeviceTable.begin(); pDevice != DeviceTable.end(); pDevice++ )
            {
                if ( ( pDevice->m_ulDeviceId         == m_ulDeviceId    )       &&
                     ( pDevice->m_unSystemEnumerator == m_unSystemEnumerator )   )
                {
                    strcpy( pszName, (pDevice->m_Name).c_str() );

                    TRACE( _T( "C7kSubsystem::Description(), 7k sonar device identified: \"%s\".\n" ), pszName );

                    bSuccess = true;
                    break;
                }
            }
        }
        else
        {
            TRACE( _T( "C7kSubsystem::Description(), Record #7001 reply not received within specified timeout period\n" ) );
            bSuccess = false;
        }
    }
    catch ( ... )
    {
        bSuccess = false;
        TRACE( _T( "C7kSubsystem::Description(), unspecified exception caught\n" ) );
    }

    if ( ! bSuccess && ( pszName != NULL ) )
    {
        ::strcpy( pszName, "SeaBat 7k sonar -- device type unknown" );
        TRACE( _T( "C7kSubsystem::Description(), SeaBat 7k, Unknown !!\n" ) );
        ASSERT( false );
    }

    return bSuccess;
}

// Various methods to set and retrieve the internal class attributes.

void C7kSubsystem::DataCollectionMode( const unsigned long &rulDataCollectionMode )
{
    m_ulDataCollectionMode = rulDataCollectionMode;
}

unsigned long C7kSubsystem::DataCollectionMode( void ) const
{
    return m_ulDataCollectionMode;
}

void C7kSubsystem::AutoShutdownMode( const bool &rbAutoShutdown )
{
    m_bAutoShutdown = rbAutoShutdown;
}

void C7kSubsystem::ParameterRouting( const bool &rbEnable )
{
    m_bParameterRouting = rbEnable;
}

bool C7kSubsystem::ParameterRouting( void ) const
{
    return m_bParameterRouting;
}

void C7kSubsystem::MessageRouting( const bool &rbEnable )
{
    m_bMessageRouting = rbEnable;
}

bool C7kSubsystem::MessageRouting( void ) const
{
    return m_bMessageRouting;
}

// Survey parameter wrappers.

bool C7kSubsystem::Roll( const float &rfRollInRadians )
{
    bool bSuccess = false;

    if ( ! m_bParameterRouting )
    {
        bSuccess = true;
    }
    else __TRY
    {
        m_SonarCommandGuard.Enter();
        bSuccess = m_SonarCommands.Roll( rfRollInRadians );
    }
    __FINALLY
    {
        m_SonarCommandGuard.Leave();
    }
    __ENDFINALLY

    return bSuccess;
}

bool C7kSubsystem::Pitch( const float &rfPitchInRadians )
{
    bool bSuccess = false;

    if ( ! m_bParameterRouting )
    {
        bSuccess = true;
    }
    else __TRY
    {
        m_SonarCommandGuard.Enter();
        bSuccess = m_SonarCommands.Pitch( rfPitchInRadians );
    }
    __FINALLY
    {
        m_SonarCommandGuard.Leave();
    }
    __ENDFINALLY

    return bSuccess;
}

bool C7kSubsystem::SoundSpeed( const float &rfSoundSpeedInMetersPerSecond )
{
    bool bSuccess = false;

    if ( ! m_bParameterRouting )
    {
        bSuccess = true;
    }
    else __TRY
    {
        m_SonarCommandGuard.Enter();
        bSuccess = m_SonarCommands.SoundSpeed( rfSoundSpeedInMetersPerSecond );
    }
    __FINALLY
    {
        m_SonarCommandGuard.Leave();
    }
    __ENDFINALLY

    return bSuccess;
}

bool C7kSubsystem::AbsorptionLoss( const float &rfAbsorptionLossIndBPerKilometer )
{
    bool bSuccess = false;

    if ( ! m_bParameterRouting )
    {
        bSuccess = true;
    }
    else __TRY
    {
        m_SonarCommandGuard.Enter();
        bSuccess = m_SonarCommands.AbsorptionLoss( rfAbsorptionLossIndBPerKilometer );
    }
    __FINALLY
    {
        m_SonarCommandGuard.Leave();
    }
    __ENDFINALLY

    return bSuccess;
}

bool C7kSubsystem::SpreadingLoss ( const float &rfSpreadingLossIndB )
{
    bool bSuccess = false;

    if ( ! m_bParameterRouting )
    {
        bSuccess = true;
    }
    else __TRY
    {
        m_SonarCommandGuard.Enter();
        bSuccess = m_SonarCommands.SpreadingLoss( rfSpreadingLossIndB );
    }
    __FINALLY
    {
        m_SonarCommandGuard.Leave();
    }
    __ENDFINALLY

    return bSuccess;
}

///////////////////////////////////////////////////////////////////////////////
// Base class overrides representing the abstracted sensor interface... the main
// entry points to the subsystem.

bool C7kSubsystem::Startup( const int           &riSensorIndex,
                            const DWORD         &rdwThreadId,
                            const unsigned int  &ruiDataReadyMessageId,
                            const unsigned int  &ruiReplyReadyMessageId,
                            const int           &riActivateOnStartup )
{
    bool bSuccess = false;              // Assume failure for now.

    try
    {
        // First, ensure this object was correctly constructed.

        if ( ! IsInitialized() )
        {
            ThrowMessage_m( "Sensor construction has previously failed" );
        }

        // Set the base-class attributes.

        if ( ! CSensor::Startup( riSensorIndex, rdwThreadId, ruiDataReadyMessageId, ruiReplyReadyMessageId, riActivateOnStartup ) )
        {
            ThrowMessage_m( "Startup has failed" );
        }

        // Get and validate the component data pointers.

        CRESON7kSonarApp *pApp = GetApp();

        if ( pApp == NULL )
        {
            ThrowMessage_m( "Can't acquire application object pApp" );
        }

        // Read the 7k device specific info from the registry. If we're unable to do this, set a default.

        CDeviceInfo Info;

        if ( ! Info.RetrieveParameters( m_ulDeviceId, m_unSystemEnumerator ) )
        {
            CString sMessage;

            sMessage.Format( _T( "Can't retrieve registry info for specified device (%lu, %u)... using default settings!!!\n" ),    m_ulDeviceId,
                                                                                                                                    m_unSystemEnumerator );

            LogEvent( eventLogDiagnostic, "%s", static_cast<LPCTSTR>( sMessage ) );

            Info.SetDefault();
        }

        // Create a subsystem representation and associate it with the appropriate sonar interface.

        CDispatcher *pDispatcher = GetAndValidateDispatcher();

        if ( ! pDispatcher->CreateSubsystem( DataReady,
                                                this,
                                                    riSensorIndex,
                                                        m_ulDeviceId,
                                                            m_unSystemEnumerator,
                                                                    Info.m_eInterfaceType,
                                                                        Info.m_ulPortOut,
                                                                            Info.m_ulPortIn,
                                                                                static_cast<LPCTSTR>( Info.m_sAddress ) ) )
        {
            ThrowMessage_m( "Can't acquire the dispatcher object" );
        }

        // Set the data collection mode for this subsystem.
        
        m_ulDataCollectionMode = Info.m_ulCollectionMode;

        // The first subsystem created gets to install the sensor non-specific data handler.

        if ( AddRef() == 1 )
        {
            pDispatcher->GenericSensorData( true, GenericSensorDataReady, this );
        }

        // Subscribe to messages as appropriate then query the general settings.

        if ( ! SubscribeToRecords( true ) )
        {
            TRACE( _T( "C7kSubsystem::Startup(), SubscribeToRecords() failed.\n" ) );
        }

        __TRY
        {
            m_SonarCommandGuard.Enter();

            if ( ! m_SonarCommands.QuerySonarCapabilities() )
            {
                TRACE( _T( "C7kSubsystem::Startup(), QuerySonarCapabilities() failed\n" ) );
            }
        }
        __FINALLY
        {
            m_SonarCommandGuard.Leave();
        }
        __ENDFINALLY


        // Wait for the configuration settings to allow the correct ranges to be known based on the sonar's capabilities.

        if ( ! WaitForRecord( recordTypeConfigurationSettings, 2000 ) )
        {
            TRACE( _T( "C7kSubsystem::Startup(), Configuration settings not recieved within timeout period.\n" ) );
        }

#ifdef ENABLE_CONFIG

        // Retrieve and enact the stored configuration, if possible.

        if ( ! RestoreConfiguration() )
        {
            TRACE( _T( "C7kSubsystem::Startup(), RestoreConfiguration() failed.\n" ) );
        }

#endif

        // Update the sonar state info and enable pinging as appropriate.

        __TRY
        {
            m_SonarCommandGuard.Enter();

            if ( ! m_SonarCommands.QuerySonarSettings() )
            {
                TRACE( _T( "C7kSubsystem::Startup(), QuerySonarSettings() failed\n" ) );
            }

#if 0
            // Set pinging state according to the startup mode.

            if ( ! m_SonarCommands.PingEnable( ActivateOnStartup() ) )
            {
                TRACE( _T( "C7kSubsystem::Startup(), PingEnable( %d ) failed\n" ), static_cast<int>( ActivateOnStartup() ) );
            }
#else

#pragma CompileMessage_m( "TODO: IMPLEMENT PROPOSED PINGENABLE/DISABLE STRATAGY AS FOLLOWS, UPON MINDY'S FEEDBACK" )
            // Have proposed the following:
            // 1) Reinstate coupling of the "ActivateOnStart" flag to effect pinging as per
            // EdgeTech ... 1 => logging and pinging on startup  0=> no logging of pining
            // on startup. Both cause logging and pinging to stop on shutdown....
            // consistent with designed AUV behavior and as previously.

            //2) IN ADDITION: Allow two optional registry flags at the per device level
            // that override this behavior thus:
            // StartPingingOnStartup     = 0 or 1    // override for pinging state on
            // sensor startup.
            // StopPingingOnShutdown  = 0 or 1    // override for pinging state on sensor
            // shutdown.
            //
            // If either one of these flags are not present then case 1) will apply.


            // Per request by Mindy for MBARI delivery, ActivateOnStart is to not affect pinging... pinging is to be enabled.

            if ( ! m_SonarCommands.PingEnable( true ) )
            {
                TRACE( _T( "C7kSubsystem::Startup(), PingEnable( %d ) failed\n" ), static_cast<int>( ActivateOnStartup() ) );
            }
#endif

        }
        __FINALLY
        {
            m_SonarCommandGuard.Leave();
        }
        __ENDFINALLY

        m_bStarted = true;
        bSuccess   = true;
    }
    catch ( LPCTSTR lpszMessage )
    {
        bSuccess = false;
        LogEvent( eventLogDiagnostic, "%s", lpszMessage );
        TRACE( _T( "Startup(), %s.\n" ), lpszMessage );
    }
    catch ( ... )
    {
        bSuccess = false;
        LogEvent( eventLogDiagnostic, "Startup, unspecified error" );
        TRACE( _T( "C7kSubsystem::Startup(), Unspecified exception caught\n" ) );
    }

    return bSuccess;
}
                                 
bool C7kSubsystem::Shutdown( void )
{
    // NB: Errors below should be flagged but NOT necessarily cause premature exit.

    bool bSuccess = true;

    try
    {

#ifdef ENABLE_CONFIG

        // Query the sonar for its latest volatile state and queue pending the saving of the config data; then, if received,
        // save the current volatile state subset (the one that gets restored on startup) else preserve the previous configuration.

        if ( ! WaitForConfigurationFromSonar( ulConfigReplyTimeout_c ) )
        {
            TRACE( _T( "C7kSubsystem::Shutdown(), WaitForConfigurationFromSonar() timed out... config will not be overwritten !\n" ) );
        }
        else if ( ! SaveConfiguration() )
        {
            bSuccess = false;
            TRACE( _T( "C7kSubsystem::Shutdown(), SaveConfiguration() failed\n" ) );
        }

#endif

        if ( ! SubscribeToRecords( false ) )
        {
            TRACE( _T( "C7kSubsystem::Shutdown(), SubscribeToRecords() failed.\n" ) );
        }

        // Give the base class a chance to shutdown first.

        if ( ! CSensor::Shutdown() )
        {
            TRACE( _T( "C7kSubsystem::Shutdown(), CSensor::Shutdown() failed.\n" ) );
        }

        // Get the dispatcher and validate it before use.

        CDispatcher *pDispatcher = GetAndValidateDispatcher();

        // Signal the subsystem is being released.

        const int iRefCount = ReleaseRef();

        // Revoke the generic device non-specific data handler.

        if ( iRefCount == 0 )
        {
            pDispatcher->GenericSensorData( false, NULL, NULL );
        }

        // Force mandatory state prior to shutdown (not saved in configuration).

        if ( ! ForceMandatoryState() )
        {
            bSuccess = false;
            TRACE( _T( "C7kSubsystem::Shutdown(), ForceMandatoryState() failed\n" ) );
        }

        // Use reference counting to decide when the last sonar subsystem is being shutdown in order to 
        // to handle clean up and sonar shutdown.

        if ( ( iRefCount == 0 ) && m_bAutoShutdown )
        {
            __TRY
            {
                m_SonarCommandGuard.Enter();

                if ( ! m_SonarCommands.ShutdownSonar() )
                {
                    bSuccess = false;
                    TRACE( _T("C7kSubsystem::Shutdown(), ShutdownSonar() failed\n") );
                }
            }
            __FINALLY
            {
                m_SonarCommandGuard.Leave();
            }
            __ENDFINALLY
        }

        // Finally, revoke the dispatcher/subsystem mapping and destroy the sonar interface as appropriate.

        if ( ! pDispatcher->DestroySubsystem( SensorIndex() ) )
        {
            bSuccess = false;
            TRACE( _T("C7kSubsystem::Shutdown(), pDispatcher->DestroySubsystem() failed\n") );
        }
    }
    catch ( LPCTSTR lpszMessage )
    {
        bSuccess = false;
        TRACE( _T( "C7kSubsystem::Shutdown(), %s\n" ), lpszMessage );
    }
    catch ( ... )
    {
        bSuccess = false;
        TRACE( _T( "C7kSubsystem::Shutdown(), unspecified exception caught\n" ) );
    }

    return bSuccess;
}
                                 
bool C7kSubsystem::Load(    BYTE            *pby7kRecord,
                            unsigned long   *pulSize,
                            unsigned long   *pulTimeStamp )
{
    // OBSOLETE; sensors now load data directly into the PLC's shared memory data pool using m_SensorDataPool.

    UNREFERENCED_PARAMETER( pby7kRecord );
    UNREFERENCED_PARAMETER( pulSize );
    UNREFERENCED_PARAMETER( pulTimeStamp );

    return false;
}
                                 
bool C7kSubsystem::SendCommand( int     iClientIndex,
                                char   *pszCommand )
{
    // Inbound generic command from the PLC received.

    bool bSuccess = false;

    DBG_UNREFERENCED_PARAMETER( iClientIndex );

    C6046RemoteCommand m_Command;

    if ( ! m_Command.Decode( pszCommand ) )
    {
        bSuccess = false;
        TRACE( _T( "C7kSubsystem::SendCommand(), m_Command.Decode() failed.\n" ) );
    }
    else __TRY
    {
        m_SonarCommandGuard.Enter();

        CSonarCommands::PARAMETERRANGES sRanges = m_SonarCommands.ParameterRanges();

        switch ( m_Command.Command() )
        {
            case C6046RemoteCommand::commandRange:

                bSuccess = m_SonarCommands.Range( static_cast<float>( m_Command ) );
                break;

            case C6046RemoteCommand::commandPing:

                bSuccess = m_SonarCommands.PingEnable( static_cast<bool>( m_Command ) );
                break;

            case C6046RemoteCommand::commandTxPower:

                {
                    // Set the power selection as f32 in dB re µPa. Here we convert from % to dB. The percentage is assumed to be in same units as
                    // the sonar capability. For 7k systems, this is a percentage of its dB range.

                    const float fPercentage = static_cast<float>( m_Command );

                    if ( ( fPercentage >= 0.0f ) && ( fPercentage <= 100.0f ) )
                    {
                        const float fPowerIndB = 0.01f * fPercentage * ( sRanges.m_fMaxPower - sRanges.m_fMinPower ) + sRanges.m_fMinPower;
                        bSuccess = m_SonarCommands.TransmitPower( fPowerIndB );
                    }
                    else
                    {
                        TRACE( _T( "C7kSubsystem::SendCommand(), Power setting out of range (%.2f).\n" ), fPercentage );
                    }
                }

                break;

            case C6046RemoteCommand::commandRxGain:

                // Receiver gain... values are dimensionless; for the 7k sonar, should be in dB directly.

                bSuccess = m_SonarCommands.ReceiverGain( static_cast<float>( m_Command ) );
                break;

            case C6046RemoteCommand::commandDuration:

                // Not supported for the 7k sonar; should use the range setting.

                bSuccess = true;
                break;

            case C6046RemoteCommand::commandPulse:

                //{
                //    CClientReply Reply( iClientIndex, CClientReply::replyIDNone );
                //    m_ClientReplyList.push_back( Reply );
                //}

                bSuccess = true; //SetPulseFile( static_cast<const char *>( Parameter ) );
                break;

            case C6046RemoteCommand::commandPulseFiles:

                bSuccess = true; //RetrievePulseFileListQuery( iClientIndex );
                break;

            case C6046RemoteCommand::commandDefaultState:

                SetDefaultState();
                bSuccess = true;
                break;

            case C6046RemoteCommand::commandQueryParameters:

                bSuccess = m_SonarCommands.QuerySonarStaticSettings();
                break;

            case C6046RemoteCommand::commandQueryCapabilities:

                bSuccess = m_SonarCommands.QuerySonarCapabilities();
                break;

            case C6046RemoteCommand::commandQuerySonarRemoteSettings:

                bSuccess = m_SonarCommands.QuerySonarRemoteSettings();
                break;

            case C6046RemoteCommand::commandPulseWidth:

                bSuccess = m_SonarCommands.PulseWidth( static_cast<float>( m_Command ) );
                break;

            case C6046RemoteCommand::commandRangeDepthFilters:

                {
                    bool      bFound              = true;
                    const int iNumberOfParameters = m_Command.NumberOfParameters();

                    ASSERT( iNumberOfParameters == 4 );

                    if ( iNumberOfParameters == 4 ) 
                    {
                        float fParam[ 4 ] = { 0.0f };

                        m_Command.ResetParamRetrievalPoint();

                        for ( int iParam = 0; iParam < iNumberOfParameters; iParam++ )
                        {
                            if ( ! m_Command.GetNextParameter( &fParam[ iParam ], C6046RemoteCommand::paramTypeFloat ) )
                            {
                                bFound = false;
                                break;
                            }
                        }

                        if ( bFound )
                        {
                            bSuccess = m_SonarCommands.BottomDetectionFilterInfo( fParam[ 0 ], fParam[ 1 ], fParam[ 2 ], fParam[ 3 ] );
                        }
                    }
                }

                break;

            case C6046RemoteCommand::commandBottomDetectFlags:

                bSuccess = m_SonarCommands.BottomDetectMethod( static_cast<unsigned long>( m_Command ) );
                break;

            case C6046RemoteCommand::commandMaxPingRate:

                bSuccess = m_SonarCommands.MaxPingRate( static_cast<float>( m_Command ) );
                break;

            case C6046RemoteCommand::commandAbsorptionCoef:

                bSuccess = m_SonarCommands.AbsorptionLoss( static_cast<float>( m_Command ) );
                break;

            case C6046RemoteCommand::commandSoundSpeed:

                bSuccess = m_SonarCommands.SoundSpeed( static_cast<float>( m_Command ) );
                break;

            case C6046RemoteCommand::commandSpreadingLoss:

                bSuccess = m_SonarCommands.SpreadingLoss( static_cast<float>( m_Command ) );
                break;

            default:

                bSuccess = true;
                TRACE( _T( "C7kSubsystem::SendCommand(), Unknown command recieved.\n" ) );
                break;
        }
    }
    __FINALLY
    {
        m_SonarCommandGuard.Leave();
    }
    __ENDFINALLY

    return bSuccess;
}

bool C7kSubsystem::RetrieveStatus(  BYTE           *pby7kRecord,
                                    unsigned long  *pulSize,
                                    unsigned long  *pulTimeStamp,
                                    int            *piClientIndex )

{
    // Returns false if no more queued status reply messages are available for retrieval otherwise
    // returns true -- wherein pby7kRecord points to a valid 7k record. NB: this method is invoked
    // sucessively by the PLC until all messages are retrieved in response to a reply ready message being send to
    // the PLC's main thread from within.

    bool bMessageRetrieved = false;

    if ( pby7kRecord == NULL )
    {
        TRACE( _T( "C7kSubsystem::RetrieveStatus(), Target buffer is NULL.\n" ) );
    }
    else if ( m_Status.IsQueued() )
    {
        // Construct a message object to be retrieved.

        CSensorStatus::CMessageDescriptor Message;

        if ( m_Status.RetrieveNextMessage( Message ) )
        {
            *piClientIndex = Message.m_iClientIndex;

            if ( Message.m_bAckNak )
            {
                C7kAcknowlege           AckNakMessage;
                C7kAcknowlege::ESTATUS  eStatus = C7kAcknowlege::statusUnknown;

                if ( Message.m_eAckNakType == CSensorStatus::CMessageDescriptor::ackNakTypeNotAcknowledge )
                {
                    eStatus = C7kAcknowlege::statusRejected;
                }
                else if ( Message.m_eAckNakType == CSensorStatus::CMessageDescriptor::ackNakTypeAcknowledge )
                {
                    eStatus = C7kAcknowlege::statusAccepted;
                }

                if ( AckNakMessage.Encode( eStatus, Message.m_ulRemoteControlId, SensorIndex(), false ) )
                {
                    const unsigned long ulSize = static_cast<unsigned long>( AckNakMessage );

                    if ( ulSize > 0UL )
                    {
                        *pulSize      = ulSize;
                        *pulTimeStamp = Message.m_ulTimeStamp;

                        memcpy( pby7kRecord, static_cast<BYTE *>( AckNakMessage ), ulSize );

                        bMessageRetrieved = true;
                    }
                }
            }
            else
            {
                C7kStatus StatusMessage;

                C7kStatus::EMESSAGETYPE eType       = static_cast<C7kStatus::EMESSAGETYPE>( Message.m_iType );
                C7kStatus::EMODE        eMode       = static_cast<C7kStatus::EMODE>( Message.m_iMode );
                C7kStatus::EMESSAGEID   eId         = static_cast<C7kStatus::EMESSAGEID>( Message.m_iId );
                LPCTSTR                 lpszMessage = ( ( Message.m_sMessage.empty() ) ? NULL : Message.m_sMessage.c_str() );

                if ( StatusMessage.Encode( eType, eMode, eId, lpszMessage, false ) )
                {
                    const unsigned long ulSize = static_cast<unsigned long>( StatusMessage );

                    if ( ulSize > 0UL )
                    {
                        *pulSize      = ulSize;
                        *pulTimeStamp = Message.m_ulTimeStamp;

                        memcpy( pby7kRecord, static_cast<BYTE *>( StatusMessage ), ulSize );

                        bMessageRetrieved = true;
                    }
                }
            }
        }
    }

    return bMessageRetrieved;
}
                                 
bool C7kSubsystem::IsSensorHealthy( void )
{
    return ( m_bInitialized && m_HealthStatus.IsResponsive() );
}

bool C7kSubsystem::Reset( void )
{
    bool bSuccess = true;

    try
    {
        if ( ! CSensor::Reset() )
        {
            ASSERT( false );
        }

        // Get and validate the component data pointers.

        CRESON7kSonarApp *pApp = GetApp();

        if ( pApp == NULL )
        {
            ThrowMessage_m( "Can't acquire application object pApp" );
        }

        const int iSensorIndex = SensorIndex();

        CDispatcher *pDispatcher = GetAndValidateDispatcher();

        if ( ! pDispatcher->DestroySubsystem( iSensorIndex ) )
        {
            TRACE( _T( "C7kSubsystem::Reset(), pDispatcher->DestroySubsystem() failure.\n" ) );
        }

        // Read the 7k device specific info from the registry. If we're unable to do this, set a default.

        CDeviceInfo Info;

        if ( ! Info.RetrieveParameters( m_ulDeviceId, m_unSystemEnumerator ) )
        {
            CString sMessage;
            sMessage.Format( _T( "Can't retrieve registry info for specified device (%lu, %u)... using default settings!!!\n" ),    m_ulDeviceId,
                                                                                                                                    m_unSystemEnumerator );

            LogEvent( eventLogDiagnostic, "%s", static_cast<LPCTSTR>( sMessage ) );
            Info.SetDefault();
        }

        if ( ! pDispatcher->CreateSubsystem( DataReady,
                                                this,
                                                    iSensorIndex,
                                                        m_ulDeviceId,
                                                            m_unSystemEnumerator,
                                                                    Info.m_eInterfaceType,
                                                                        Info.m_ulPortOut,
                                                                            Info.m_ulPortIn,
                                                                                static_cast<LPCTSTR>( Info.m_sAddress ) ) )
        {
            ThrowMessage_m( "Can't acquire the dispatcher object" );
        }

        bSuccess = true;
    }
    catch( ... )
    {
        bSuccess = false;
    }

    return bSuccess;
}

bool C7kSubsystem::RouteMessage(    const int           &riClientIndex,
                                    const BYTE          *pbyMessage,
                                    const unsigned long &rulSize,
                                    const unsigned long &rulTimeStamp )
{
    UNREFERENCED_PARAMETER( rulTimeStamp );

    ASSERT( pbyMessage != NULL );
    ASSERT( rulSize    >  0UL );

    bool bSuccess = false;

    try
    {
        if ( ! m_bMessageRouting )
        {
            bSuccess = true;
        }
        else if ( IsStarted() && ( SensorIndex() != m_iInvalidSensorIndex ) )
        {
            __TRY
            {
                m_SonarCommandGuard.Enter();
                bSuccess = m_SonarCommands.SendRecord( pbyMessage, rulSize, riClientIndex );
            }
            __FINALLY
            {
                m_SonarCommandGuard.Leave();
            }
            __ENDFINALLY
        }
        else
        {
            bSuccess = false;
        }
    }
    catch ( ... )
    {
        bSuccess = false;
    }

    return bSuccess;
}

bool C7kSubsystem::Identify( SENSORINFO *psSensorInfo )
{
    return CSensor::Identify( psSensorInfo );
}

bool C7kSubsystem::QueryHealth( void )
{
    // This method is called by the watchdog thread (i.e, from within the CRESON7kSonarApp::m_pSentinel object) or otherwise to
    // periodically query the subsystem's health and to subsequently drive the m_HealthStatus helper object.

    __TRY
    {
        m_SonarCommandGuard.Enter();

        if ( ! m_SonarCommands.QueryIsAlive() )
        {
            TRACE( _T( "C7kSubsystem::QueryHealth(), m_SonarCommands.QueryIsAlive() failed.\n" ) );
        }
    }
    __FINALLY
    {
        m_SonarCommandGuard.Leave();
    }
    __ENDFINALLY

    MaintainPendingReplyList();

    return true;
}

///////////////////////////////////////////////////////////////////////////////
// Private services.

inline
bool C7kSubsystem::SetControlRanges( void )
{
    bool bSuccess = false;

    CDispatcher *pDispatcher = GetAndValidateDispatcher();
    ASSERT( pDispatcher != NULL );

    CSystemInfo::DeviceInfo_t DeviceTable = pDispatcher->DeviceInfo( SensorIndex() );

    for ( CSystemInfo::DeviceIdIterator_t pDevice = DeviceTable.begin(); pDevice != DeviceTable.end(); pDevice++ )
    {
        if ( ( pDevice->m_ulDeviceId         == m_ulDeviceId    )       &&
             ( pDevice->m_unSystemEnumerator == m_unSystemEnumerator )   )
        {
            __TRY
            {
                m_SonarCommandGuard.Enter();
                bSuccess = m_SonarCommands.ParameterRanges( *pDevice );
            }
            __FINALLY
            {
                m_SonarCommandGuard.Leave();
            }
            __ENDFINALLY

            break;
        }
    }

    return bSuccess;
}

inline
CDispatcher * C7kSubsystem::GetAndValidateDispatcher( void ) const
{
    CDispatcher *pDispatcher = static_cast<CDispatcher *>( m_Dispatcher );
    ASSERT ( pDispatcher != NULL );

    if ( pDispatcher == NULL )
    {
        ThrowMessage_m( "Dispatcher object is NULL" );
    }

    if ( ! pDispatcher->IsInitialized() )
    {
        ThrowMessage_m( "The dispatcher object has not been correctly initialized" );
    }

    return pDispatcher;
}

inline
bool C7kSubsystem::NotifyHostOfReply( const int &riClientIndex )
{
    bool        bSuccess   = false;
    const DWORD dwThreadId = NotifyThreadId();

    ASSERT( dwThreadId != m_dwInvalidThreadId );

    if ( dwThreadId != m_dwInvalidThreadId )
    {
        if ( PostThreadMessage( dwThreadId, ReplyReadyMessage(), static_cast<WPARAM>( SensorIndex() ), static_cast<LPARAM>( riClientIndex ) ) )
        {
            bSuccess = true;
        }
        else
        {
            TRACE( _T( "C7kSubsystem::NotifyHostOfReply(), PostThreadMessage() failed with code: %lu\n" ), ::GetLastError() );
        }
    }

    ASSERT( bSuccess );

    return bSuccess;
}

inline
bool C7kSubsystem::SaveConfiguration( void )
{
    // Writes a subset of the volatile state to disk. Assumes that the m_Config state structure
    // is maintained elsewhere.

    bool bSuccess = false;                  // Assume failure for now.

    if ( m_ConfigReadback.SaveOnExit() )
    {
        __TRY
        {
            if ( ! m_Config.Open( SensorIndex(), Config_t::accessWrite ) )
            {
                TRACE( _T( "C7kSubsystem::SaveConfiguration(), Can't open config file.\n" ) );
            }
            else if ( ! m_Config.Write() )
            {
                TRACE( _T( "C7kSubsystem::SaveConfiguration(), Can't write to config file.\n" ) );
            }
            else
            {
                bSuccess = true;
            }
        }
        __FINALLY
        {
            m_Config.Close();
        }
        __ENDFINALLY
    }
    else
    {
        bSuccess = true;
        TRACE( _T( "C7kSubsystem::SaveConfiguration(), Save on exit is disabled. Last settings will be preserved.\n" ) );
    }

    return bSuccess;
}

inline
bool C7kSubsystem::RestoreConfiguration( void )
{
    bool bSuccess = false;      // Assume failure for now.

    __TRY
    {
        // Open the config file and read the various parameters, if possible. If we can't, we'll keep the 
        // sonar's own last settings.

        m_Config.SetDefault();

        if ( m_Config.Open( SensorIndex(), Config_t::accessRead ) && m_Config.Read() )
        {
            // Enact the relevant volatile state.

            bSuccess = true;

            __TRY
            {
                m_SonarCommandGuard.Enter();

                bSuccess = bSuccess && m_SonarCommands.Range                    ( m_Config.m_fRange );
                bSuccess = bSuccess && m_SonarCommands.TransmitPower            ( m_Config.m_fPower );
                bSuccess = bSuccess && m_SonarCommands.ReceiverGain             ( m_Config.m_fGain );
                bSuccess = bSuccess && m_SonarCommands.MaxPingRate              ( m_Config.m_fMaxPingRate );
                bSuccess = bSuccess && m_SonarCommands.BottomDetectionFilterInfo( m_Config.m_fBottomDetectionMinRange,
                                                                                  m_Config.m_fBottomDetectionMaxRange,
                                                                                  m_Config.m_fBottomDetectionMinDepth,
                                                                                  m_Config.m_fBottomDetectionMaxDepth );
                bSuccess = bSuccess && m_SonarCommands.SoundSpeed               ( m_Config.m_fSoundSpeed );
                bSuccess = bSuccess && m_SonarCommands.AbsorptionLoss           ( m_Config.m_fAbsorption );
                bSuccess = bSuccess && m_SonarCommands.SpreadingLoss            ( m_Config.m_fSpreadingLoss );
            }
            __FINALLY
            {
                m_SonarCommandGuard.Leave();
            }
            __ENDFINALLY
        }
        else
        {
            m_Config.SetDefault();
        }
    }
    __FINALLY
    {
        m_Config.Close();
    }
    __ENDFINALLY

    return bSuccess;
}

inline
bool C7kSubsystem::WaitForConfigurationFromSonar( const unsigned long &rulTimeout )
{
    bool bSuccess = false;      // Assume failure for now.

    try
    {
        ASSERT( rulTimeout != INFINITE );

        m_ConfigReadback.SaveOnExit( false );

        if ( m_ConfigReadback.Configure() )
        {
            bool bQueried = false;

            __TRY
            {
                m_SonarCommandGuard.Enter();
                bQueried = m_SonarCommands.QueryConfigSettings();
            }
            __FINALLY
            {
                m_SonarCommandGuard.Leave();
            }
            __ENDFINALLY

            if ( bQueried && m_ConfigReadback.WaitForConfigReceived( rulTimeout ) )
            {
                m_ConfigReadback.SaveOnExit( true );
            }
        }

        m_ConfigReadback.Release();

        bSuccess = true;
    }
    catch ( ... )
    {
        m_ConfigReadback.SaveOnExit( false );
        TRACE( _T( "C7kSubsystem::WaitForConfigurationFromSonar(), Unspecified exception caught.\n" ) );
        bSuccess = false;
    }

    return bSuccess;
}

inline
bool C7kSubsystem::IsDataCollectionModeGenericSensors( void ) const
{
    return ( ( m_ulDataCollectionMode & m_ulDataCollectionModeGenericSensors ) != 0 );
}

inline
bool C7kSubsystem::IsDataCollectionModeBeamData( void ) const
{
    return ( ( m_ulDataCollectionMode & m_ulDataCollectionModeBeamData ) != 0 );
}

inline
bool C7kSubsystem::IsDataCollectionModeBathymetricData( void ) const
{
    return ( ( m_ulDataCollectionMode & m_ulDataCollectionModeBathymetricData ) != 0 );
}

inline
bool C7kSubsystem::IsDataCollectionModeBackscatteredImagery( void ) const
{
    return ( ( m_ulDataCollectionMode & m_ulDataCollectionModeBackscatteredImagery ) != 0 );
}

void C7kSubsystem::GenericSensorDataReady(  const void                 *pvParam,
                                            const BYTE                 *pby7kRecord,
                                            const unsigned long        &rulBytes,
                                            const unsigned long        &rulTimeStamp )
{
    // Static callback handler for sensor non-specific (generic sensors etc) 7k data records.

    // This callback is invoked by the sonar interface object in the context of its internal read
    // thread. Here, pvParam is our previously installed this pointer and pby7kRecord and rulBytes
    // pertain to a 7k data record itself.

    try
    {
        C7kSubsystem *pthis = static_cast<C7kSubsystem *>( const_cast<void *>( pvParam ) );
        ASSERT( pthis != NULL );

        if ( pthis == NULL )
        {
            ThrowMessage_m( "pthis pointer is NULL" );
        }
        else if ( ! pthis->HandleSonarData( pby7kRecord, rulBytes, rulTimeStamp, false ) )
        {
            ThrowMessage_m( "pthis->HandleSonarData() failed" );
        }
    }
    catch ( LPCTSTR lpszMessage )
    {
        TRACE( _T( "C7kSubsystem::GenericSensorDataReady(), %s.\n" ), lpszMessage );
    }
    catch ( ... )
    {
        TRACE( _T( "C7kSubsystem::GenericSensorDataReady(), Unspecified exception caught!\n" ) );
    }
}

void C7kSubsystem::DataReady(  const void                 *pvParam,
                               const BYTE                 *pby7kRecord,
                               const unsigned long        &rulBytes,
                               const unsigned long        &rulTimeStamp )
{
    // Static callback handler for sensor specific 7k record processing.

    // This callback is invoked by the sonar interface object in the context of its internal read
    // thread. Here, pvParam is our previously installed this pointer and pby7kRecord and rulBytes
    // pertain to a 7k data record itself.

    try
    {
        C7kSubsystem *pthis = static_cast<C7kSubsystem *>( const_cast<void *>( pvParam ) );
        ASSERT( pthis != NULL );

        if ( pthis == NULL )
        {
            ThrowMessage_m( "pthis pointer is NULL" );
        }
        else if ( ! pthis->HandleSonarData( pby7kRecord, rulBytes, rulTimeStamp, true ) )
        {
            ThrowMessage_m( "pthis->HandleSonarData() failed" );
        }
    }
    catch ( LPCTSTR lpszMessage )
    {
        TRACE( _T( "C7kSubsystem::SubsystemDataReady(), %s.\n" ), lpszMessage );
    }
    catch ( ... )
    {
        TRACE( _T( "C7kSubsystem::SubsystemDataReady(), Unspecified exception caught!\n" ) );
    }
}

inline
bool C7kSubsystem::HandleSonarData( const BYTE          *pby7kRecord,
                                    const unsigned long &rulBytes,
                                    const unsigned long &rulTimeStamp,
                                    const bool          &rbSubsystemSpecificData )
{
    // Called to handle records coming from the sonar. Records may be subsystem specific or non-specific.
    // In the latter case, generic sensor data is handled by the first subsystem instantiated acting as a proxy.
    // Note: this method is invoked by callback from a Dispatcher.

    UNREFERENCED_PARAMETER( rbSubsystemSpecificData );                      // Obsolete. (?)

    bool bSuccess = true;                                                   // Assume success for now.

    try
    {
        if ( ! C7kProtocol::IsValid7kRecord( pby7kRecord, rulBytes ) )
        {
            ThrowMessage_m( "Invalid 7k record detected." );
        }

        bool                bQueue       = false;
        const unsigned long ulRecordType = C7kProtocol::RecordType( pby7kRecord, rulBytes );

#pragma CompileMessage_m( "TODO: ONCE REPLY MESSAGES ARE KNOW, QUEUE TO m_Status AND SET eReplyId" )
        CClientReply::EREPLYID eReplyId = CClientReply::replyIDNone;    // Message doesn't constitude a reply at this time.

        //TRACE( _T( "C7kSubsystem::HandleSonarData(), Record %lu detected\n" ), ulRecordType );

#ifdef ENABLE_CONFIG

        if ( m_ConfigReadback.IsAccumulating() )
        {
            m_ConfigReadback.DecodeRecord( ulRecordType, pby7kRecord, rulBytes );
        }

#endif

        switch ( ulRecordType )
        {
            case 0UL:

                TRACE( _T( "C7kSubsystem::HandleSonarData(), anomalous record number detected!\n" ) );
                break;

            case recordTypeConfigurationSettings:                                                               // Set the sonar caps plus ensure we pass on for logging and subscribing clients.

                if ( ! SetControlRanges() )
                {
                    TRACE( _T( "C7kSubsystem::HandleSonarData(), SetControRanges() failed.\n" ) );
                }

                bQueue = true;
                break;

            case recordTypeRemoteControlAck:
            case recordTypeRemoteControlNak:

                if ( ! HandleReceivedAckNak( ulRecordType, pby7kRecord ) )
                {
                    TRACE( _T( "C7kSubsystem::HandleSonarData(), Invalid 7k record detected.\n" ) );
                }

                break;

            case recordTypeRemoteControl:                                                                       // Remote control commands appear in the outbound MMF so ignore these for now.
                break;

            case recordTypeRemoteControlSonarSettings:

                bQueue = true;
                break;

            case recordTypeVolatileSettings:
            case recordTypeMatchFilter:
            case recordTypeBeamGeometry:
            case recordTypeCalibrationData:

                bQueue = IsDataCollectionModeBathymetricData() ||
                            IsDataCollectionModeBackscatteredImagery() ||
                                IsDataCollectionModeBeamData();

                break;

            case recordTypeSystemEvents:
            case recordTypeSystemEventMessage:

#pragma CompileMessage_m( "TODO: !!!! DEBUGGING NEEDED !!!!!!" )
                //if ( ! HandleSystemEvent( ulRecordType, pby7kRecord, rulBytes, rulTimeStamp ) )
                //{
                //    TRACE( _T( "C7kSubsystem::HandleSonarData(), HandleSystemEvent() failed.\n" ) );
                //}

                break;

            case recordTypeDataStorageStatusInfo:
            case recordTypeFileHeader:

                break;

            case recordTypeTimeMessage:

                if ( ! HandleTimeMessage( ulRecordType, pby7kRecord, rulBytes, rulTimeStamp ) )
                {
                    TRACE( _T( "C7kSubsystem::HandleSonarData(), HandleTimeMessage() failed.\n" ) );
                }

                break;

            case recordTypeTrigger:
            case recordTypeTriggerSequenceSetup:
            case recordTypeTriggerSequenceDone:

                if ( ! HandleTriggerMessages( ulRecordType, pby7kRecord, rulBytes, rulTimeStamp ) )
                {
                    TRACE( _T( "C7kSubsystem::HandleSonarData(), HandleTriggerMessages() failed.\n" ) );
                }

                break;

            case recordTypeBathymetricData:

                bQueue = IsDataCollectionModeBathymetricData();
                break;

            case recordTypeBackscatterImageData:

                bQueue = IsDataCollectionModeBackscatteredImagery();
                break;

            case recordTypeBeamData:
            case recordTypeImageData:

                bQueue = IsDataCollectionModeBeamData();
                break;

            default:

                if ( ( ulRecordType >= recordTypeGenericSensorBegin ) &&
                     ( ulRecordType <= recordTypeGenericSensorEnd   )  )
                {
                    bQueue = IsDataCollectionModeGenericSensors();
                }
                else if ( ( ulRecordType >= recordTypeSonarCorrectionDataBegin ) &&
                          ( ulRecordType <= recordTypeSonarCorrectionDataEnd   )  )
                {
                    bQueue = true;
                }
                else
                {
                    TRACE( _T( "C7kSubsystem::HandleSonarData(), R%lu ignored.\n" ), ulRecordType );
                }

                break;
        }

        if ( bQueue )
        {
            if ( ! WriteToPLCSharedMemory( pby7kRecord, rulBytes, rulTimeStamp ) )
            {
                ThrowMessage_m( "WriteToPLCSharedMemory() failed" );
            }
        }

        if ( ( eReplyId != CClientReply::replyIDNone ) && ( ! m_ClientReplyList.empty() ) )
        {
            ClientReplyListIterator_t pReply = find( m_ClientReplyList.begin(), m_ClientReplyList.end(), eReplyId );

            if ( pReply != m_ClientReplyList.end() )
            {
                NotifyHostOfReply( pReply->m_iClientIndex );
                m_ClientReplyList.erase( pReply );
            }
        }
    }
    catch ( LPCTSTR lpszMessage )
    {
        bSuccess = false;
        TRACE( _T( "C7kSubsystem::HandleSonarData(), %s.\n" ), lpszMessage );
    }
    catch ( ... )
    {
        bSuccess = false;
        TRACE( _T( "C7kSubsystem::HandleSonarData(), unspecified exception caught.\n" ) );
    }

    return bSuccess;
}

inline
bool C7kSubsystem::WriteToPLCSharedMemory(  const BYTE          *pby7kRecord,
                                            const unsigned long &rulBytes,
                                            const unsigned long &rulTimeStamp )
{
    RECORDHEADER sRecordHeader             = { 0 };

    sRecordHeader.m_ulRecordCounter        = 0UL;
    sRecordHeader.m_ulMillisecondTimestamp = rulTimeStamp;
    sRecordHeader.m_iSensorIndex           = SensorIndex();
    sRecordHeader.m_ulRecordSizeInBytes    = rulBytes;

    return m_SensorDataPool.Write( &sRecordHeader, pby7kRecord, rulBytes );
}

inline
bool C7kSubsystem::SubscribeToRecords( const bool &rbEnable )
{
    bool bSuccess = true;

    __TRY
    {
        m_SonarCommandGuard.Enter();

        if ( rbEnable )
        {
            if ( ! m_SonarCommands.VolatileDataFeedSubscribe() )
            {
                bSuccess = false;
                TRACE( _T( "C7kSubsystem::Startup(), m_SonarCommands.VolatileDataFeedSubscribe() failed.\n" ) );
            }
        }
        else if ( ! m_SonarCommands.DisableVolatileDataFeed() )
        {
            bSuccess = false;
            TRACE( _T( "C7kSubsystem::Startup(), m_SonarCommands.DisableVolatileDataFeed() failed.\n" ) );
        }
    }
    __FINALLY
    {
        m_SonarCommandGuard.Leave();
    }
    __ENDFINALLY

    return bSuccess;
}

inline
void C7kSubsystem::SetDefaultState( void )
{
    if ( ! SubscribeToRecords( false ) )
    {
        TRACE( _T( "C7kSubsystem::SetDefaultState(), SubscribeToRecords() failed.\n" ) );
    }

    __TRY
    {
        m_SonarCommandGuard.Enter();

        if ( ! m_SonarCommands.LoadDeviceDefaults() )
        {
            TRACE( _T( "C7kSubsystem::SetDefaultState(), m_SonarCommands.LoadDeviceDefaults() failed.\n" ) );
        }

        if ( ! m_SonarCommands.QuerySonarSettings() )
        {
            TRACE( _T( "C7kSubsystem::SetDefaultState(), m_SonarCommands.QuerySonarSettings() failed.\n" ) );
        }
    }
    __FINALLY
    {
        m_SonarCommandGuard.Leave();
    }
    __ENDFINALLY

    if ( ! SubscribeToRecords( true ) )
    {
        TRACE( _T( "C7kSubsystem::SetDefaultState(), SubscribeToRecords() failed.\n" ) );
    }
}

inline
TIME7K C7kSubsystem::AgeTime(   const TIME7K        &rsTime7k,
                                const unsigned long &rulTimeStamp )
{
    bool   bSuccess      = false;                      // Assume failure for now.
    TIME7K sAdjustedTime = { 0 };

    try
    {
        // Reformat the 7KTIME struct to SYSTEMTIME format.

        SYSTEMTIME sTimeInSystemTimeFormat = { 0 };

        sTimeInSystemTimeFormat.wYear   = rsTime7k.m_unYear;
        sTimeInSystemTimeFormat.wHour   = rsTime7k.m_ucHours;
        sTimeInSystemTimeFormat.wMinute = rsTime7k.m_ucMinutes;

        int iDay   = static_cast<int>( sTimeInSystemTimeFormat.wDay   );
        int iMonth = static_cast<int>( sTimeInSystemTimeFormat.wMonth );
        int iYear  = static_cast<int>( sTimeInSystemTimeFormat.wYear  );

        CSystemTime::FromJulianDay( iDay, iMonth, iYear, rsTime7k.m_unDay );

        sTimeInSystemTimeFormat.wDay   = static_cast<WORD>( iDay );
        sTimeInSystemTimeFormat.wMonth = static_cast<WORD>( iMonth );
        sTimeInSystemTimeFormat.wYear  = static_cast<WORD>( iYear );

        double dWholeSeconds       = 0.0;
        double dFractionsOfSeconds = ::modf( static_cast<double>( rsTime7k.m_fSeconds ), &dWholeSeconds );

        sTimeInSystemTimeFormat.wSecond       = static_cast<WORD>( dWholeSeconds );
        sTimeInSystemTimeFormat.wMilliseconds = static_cast<WORD>( dFractionsOfSeconds * 1000.0 );

        // Convert the SYSTEMTIME struct to FILETIME format and compute the age based on the systems's current timestamp
        // and the timestamp when the message was received.

        const unsigned __int64 ui64MilliSecTo100NanoSec_c = ( unsigned __int64 ) ( 10000UL   );
        const unsigned __int64 ui64AgeIn100NanoSecIncr    = ui64MilliSecTo100NanoSec_c * (unsigned __int64) (CSystemTime::GetTickCount() - rulTimeStamp );

        FILETIME sTimeInFileTimeFormat = { 0 };
        
        if ( ::SystemTimeToFileTime( &sTimeInSystemTimeFormat, &sTimeInFileTimeFormat ) )
        {
            // Apply the age correction...

            unsigned __int64 ui64Corrected = (*((unsigned __int64 *) &sTimeInFileTimeFormat )) + ui64AgeIn100NanoSecIncr;

            // and finally, convert back to SYSTEMTIME format.

            SYSTEMTIME sCorrectedTimeInSystemTimeFormat = { 0 };

            if ( ::FileTimeToSystemTime( (FILETIME *)( &ui64Corrected ), &sCorrectedTimeInSystemTimeFormat ) )
            {
                // Convert the corrected time back to TIME7K format.

                const int iJulianDay = CSystemTime::ToJulianDay( sCorrectedTimeInSystemTimeFormat.wDay, sCorrectedTimeInSystemTimeFormat.wMonth, sCorrectedTimeInSystemTimeFormat.wYear );

                sAdjustedTime.m_unYear    = static_cast<unsigned short>( sCorrectedTimeInSystemTimeFormat.wYear );              // Year                 u16 0 - 65535
                sAdjustedTime.m_unDay     = static_cast<unsigned short>( iJulianDay );                                          // Day                  u16 1 - 366
                sAdjustedTime.m_ucHours   = static_cast<unsigned char> ( sCorrectedTimeInSystemTimeFormat.wHour );              // Hours                u8  0 - 23
                sAdjustedTime.m_ucMinutes = static_cast<unsigned char> ( sCorrectedTimeInSystemTimeFormat.wMinute );            // Minutes              u8  0 - 59
                sAdjustedTime.m_fSeconds  = static_cast<float>( sCorrectedTimeInSystemTimeFormat.wSecond ) +                    // Seconds              f32 0.000000 - 59.000000
                                                0.001f * static_cast<float>( sCorrectedTimeInSystemTimeFormat.wMilliseconds );

                bSuccess = true;
            }
        }
    }
    catch ( ... )
    {
        bSuccess = false;
    }

    // If successfull, return a copy of the corrected else uncorrected time.

    return ( ( bSuccess ) ? sAdjustedTime : rsTime7k );
}

inline
bool C7kSubsystem::HandleSystemEvent(   const unsigned long  &rulRecordType,
                                        const BYTE           *pby7kRecord,
                                        const unsigned long  &rulBytes,
                                        const unsigned long  &rulTimeStamp )
{
    bool bSuccess = false;

    UNREFERENCED_PARAMETER( rulTimeStamp );

    if ( rulBytes == 0UL )
    {
        bSuccess = true;                    // Nothing to do.
    }
    else if ( pby7kRecord == NULL )
    {
        bSuccess = false;
        TRACE( _T( "C7kSubsystem::HandleSystemEvent(), pby7kRecord is NULL.\n" ) );
    }
    else if ( rulRecordType == recordTypeSystemEventMessage )
    {
        BYTE           *pbyDynamic     = NULL;
        RECORDHEADER7K *psRecordHeader = NULL;
        unsigned long   ulDynamicBytes = 0UL;
        C7kProtocol     Record;

        if ( ! Record.DecodeRecord( pby7kRecord, rulBytes, false, psRecordHeader, pbyDynamic, ulDynamicBytes ) )
        {
            TRACE( _T( "C7kSubsystem::HandleSystemEvent(), Record.DecodeRecord() failed.\n" ) );
        }
        else if ( ( ulDynamicBytes >= sizeof( SYSTEMEVENTMESSAGERTH ) ) && ( pbyDynamic != NULL ) )
        {
            try
            {
                enum EEVENTID
                {
                    eventIdSuccess      = 0,
                    eventIdInformation,
                    eventIdWarning,
                    eventIdError
                };

                PSYSTEMEVENTMESSAGERTH psEventRTH = reinterpret_cast<PSYSTEMEVENTMESSAGERTH>( pbyDynamic );
                ASSERT( psEventRTH != NULL );

                if ( ( psEventRTH->m_unEventId == eventIdWarning ) ||
                     ( psEventRTH->m_unEventId == eventIdError   )  )
                {
                    std::string sMessage;

                    if ( psEventRTH->m_unMessageLength > 0 )
                    {
                        sMessage = reinterpret_cast<char *>( pbyDynamic + sizeof( SYSTEMEVENTMESSAGERTH ) );
                    }
                    else if ( psEventRTH->m_unEventId == eventIdError )
                    {
                        sMessage = "Internal error.";
                    }
                    else
                    {
                        sMessage = "Internal warning.";
                    }

                    if ( psEventRTH->m_unEventId == eventIdError )
                    {
                        if ( ! ReportAlarm( true,
                                                alarmIdSensorReportedFailure,
                                                    "%d, %d, %d, %s",
                                                        SensorIndex(),
                                                            1,
                                                                static_cast<int>( psEventRTH->m_unEventId ),
                                                                    sMessage.c_str() ) )
                        {
                            TRACE( _T( "C7kSubsystem::HandleSystemEvent(), ReportAlarm() failed.\n" ) );
                        }
                    }
                    else
                    {
                        if ( ! LogEvent( eventLogAdvisory,
                                            "%d, %d, %d, %s",
                                                SensorIndex(),
                                                    1,
                                                    static_cast<int>( psEventRTH->m_unEventId ),
                                                        sMessage.c_str() ) )

                        {
                            TRACE( _T( "C7kSubsystem::HandleSystemEvent(), LogEvent() failed.\n" ) );
                        }
                    }
                }

                bSuccess = true;
            }
            catch ( ... )
            {
                bSuccess = false;
                TRACE( _T( "C7kSubsystem::HandleSystemEvent(), Unspecified exception caught.\n" ) );
            }
        }
    }
    else
    {
        TRACE( _T( "C7kSubsystem::HandleSystemEvent(), unhandled record type: %lu.\n" ), rulRecordType );
    }

    return bSuccess;
}

inline
bool C7kSubsystem::HandleTriggerMessages(   const unsigned long  &rulRecordType,
                                            const BYTE           *pby7kRecord,
                                            const unsigned long  &rulBytes,
                                            const unsigned long  &rulTimeStamp )
{
#pragma CompileMessage_m( "TODO: COMPLETION PENDING 7K IMPLEMENTATION ETC." )

    DBG_UNREFERENCED_PARAMETER( rulRecordType );
    DBG_UNREFERENCED_PARAMETER( pby7kRecord );
    DBG_UNREFERENCED_PARAMETER( rulBytes );
    DBG_UNREFERENCED_PARAMETER( rulTimeStamp );

    return true;
}

inline
bool C7kSubsystem::HandleTimeMessage(   const unsigned long  &rulRecordType,
                                        const BYTE           *pby7kRecord,
                                        const unsigned long  &rulBytes,
                                        const unsigned long  &rulTimeStamp )
{
    UNREFERENCED_PARAMETER( pby7kRecord );
    UNREFERENCED_PARAMETER( rulBytes );
    UNREFERENCED_PARAMETER( rulTimeStamp );

    if ( rulRecordType != recordTypeTimeMessage )
    {
        return false;
    }

    // Nothing to do at this time.

    return true;
}

inline
bool C7kSubsystem::ForceMandatoryState( void )
{
    bool bSuccess = true;

    __TRY
    {
        m_SonarCommandGuard.Enter();

#if 0   // Disabled for initial MBARI delivery per request by Mindy.

        // Turn pinging off.

        bSuccess = m_SonarCommands.PingEnable( false );

#endif

        // Add any mandatory sonar state setting here prior to shutdown as necessary.


    }
    __FINALLY
    {
        m_SonarCommandGuard.Leave();
    }
    __ENDFINALLY

    return bSuccess;
}

inline
bool C7kSubsystem::WaitForRecord(   const unsigned long &rulRecordType,
                                    const unsigned long &rulTimeout ) const
{
    // Psuedo-synchronous call to wait for the relevant record reply.

    bool bSuccess = false;

    try
    {
        const DWORD dwInitialTime = CSystemTime::GetTickCount();

        while ( ( CSystemTime::GetTickCount() - dwInitialTime ) < rulTimeout )
        {
            if ( IsRecordReceived( rulRecordType ) )
            {
                bSuccess = true;
                break;
            }
            else
            {
                MSG sMsg;

                if ( ::PeekMessage( &sMsg, NULL, 0, 0, PM_NOREMOVE ) )
                { 
                    ::TranslateMessage( &sMsg );
                    ::DispatchMessage(  &sMsg );
                }
            }

            Sleep( 0 );
        }

        //TRACE( _T( "C7kSubsystem::WaitForRecord(), Waited %lums, Reply: %d\n" ), CSystemTime::GetTickCount() - dwInitialTime, bSuccess );
    }
    catch ( ... )
    {
        bSuccess = false;
        TRACE( _T( "C7kSubsystem::WaitForRecord(), Unspecified exception caught\n" ) );
    }

    return bSuccess;
}

inline
bool C7kSubsystem::IsRecordReceived( const unsigned long &rulRecordType ) const
{
    // Scan the sonar system's last received message queue (maintianed by the dispatcher) to see if
    // it has processed the specified record.

    CDispatcher *pDispatcher = GetAndValidateDispatcher();
    ASSERT( pDispatcher != NULL );

    if ( pDispatcher != NULL )
    {
        return pDispatcher->HasRecordBeenReceived( rulRecordType, SensorIndex() );
    }

    return false;
}

/////////////////////////////////////////////////////////////////////////////
// ACK/NAK handlers.

inline
bool C7kSubsystem::QueueAckNakPending(  const BYTE          *pby7kRecord,
                                        const unsigned long &rulBytes,
                                        const int           &riClientIndex )
{
    // This member should be called whenever a outbound command is to be sent to the sonar. Here we check to see
    // if the record will result in an ACK/NAK or some other response and queue a pending reply accordingly.

    bool bSuccess = false;

    const unsigned long ulRecordType = C7kProtocol::RecordType( pby7kRecord, rulBytes );

    if ( ulRecordType == static_cast<unsigned long>( recordTypeRemoteControl ) )
    {
        const REMOTECONTROLRTH * const psRTH = reinterpret_cast<const REMOTECONTROLRTH *>( pby7kRecord + DATARECORDFRAME::Size() );

        //TRACE( _T( "Queuing remote reply... Command Id: %lu, Ticket number: %lu\n" ),   psRTH->m_ulCommandId,
        //                                                                                psRTH->m_ulTicket );

        CAckNakReply Ticket( psRTH->m_ulCommandId, psRTH->m_ulTicket, *reinterpret_cast<GUID *>( const_cast<BYTE *>( &(psRTH->m_ucTrackingNumber[ 0 ]) ) ), riClientIndex );

        __TRY
        {
            m_SonarReplyListGuard.Enter();

            if (  m_SonarReplyPendingList.empty() || ( find( m_SonarReplyPendingList.begin(), m_SonarReplyPendingList.end(), Ticket.m_ulTicketNumber ) == m_SonarReplyPendingList.end() ) )
            {
                m_SonarReplyPendingList.push_back( Ticket );

                ASSERT( m_SonarReplyPendingList.back().m_ulTicketNumber == Ticket.m_ulTicketNumber );

                bSuccess = true;
            }
        }
        __FINALLY
        {
            m_SonarReplyListGuard.Leave();
        }
        __ENDFINALLY
    }
    else
    {
        bSuccess = true;        // Nothing to do.
    }

    return bSuccess;
}

inline
bool C7kSubsystem::HandleReceivedAckNak(    const unsigned long &rulRecordType,
                                            const BYTE          *pby7kRecord   )
{
    bool bSuccess = false;

    try
    {
        unsigned long *pulTicket    = NULL;
        GUID          *psClientId   = NULL;
        unsigned long *pulErrorCode = NULL;

        switch ( rulRecordType )
        {
            case recordTypeRemoteControlAck:

                {
                    REMOTECONTROLACKRTH *psAck = reinterpret_cast<REMOTECONTROLACKRTH *>( const_cast<BYTE *>( pby7kRecord ) + tagRECORDHEADER7K::Size() );
                    pulTicket  = &(psAck->m_ulTicket);
                    psClientId = reinterpret_cast<GUID *>( &(psAck->m_ucTrackingNumber[ 0 ]) );
                    //TRACE( _T( "ACK: %lu\n" ), *pulTicket );
                    //TRACE( _T( "ACK: %lu, %I64u\n" ), *pulTicket, *psClientId );
                }

                break;

            case recordTypeRemoteControlNak:

                {
                    REMOTECONTROLNAKRTH *psNak = reinterpret_cast<REMOTECONTROLNAKRTH *>( const_cast<BYTE *>( pby7kRecord ) + tagRECORDHEADER7K::Size() );

                    pulTicket    = &(psNak->m_ulTicket);
                    psClientId   = reinterpret_cast<GUID *>( &(psNak->m_ucTrackingNumber[ 0 ]) );
                    pulErrorCode = &(psNak->m_ulErrorCode);
                    TRACE( _T( "C7kSubsystem::HandleReceivedAckNak(), NAK, Ticket: %lu, Error code : %lu, Sensor index: %d.\n" ), *pulTicket, *pulErrorCode, SensorIndex() );
                    //TRACE( _T( "NAK: %lu, %I64u, %lu\n" ), *pulTicket, *psClientId, *pulErrorCode );
                }

                break;
        }

        if ( ( psClientId != NULL ) && ( pulTicket != NULL ) && ( *psClientId == m_sClientId ) )
        {
            __TRY
            {
                m_SonarReplyListGuard.Enter();

                SonarReplyListIterator_t pReply = find( m_SonarReplyPendingList.begin(), m_SonarReplyPendingList.end(), *pulTicket );

                if ( pReply != m_SonarReplyPendingList.end() )
                {
                    bool bAck = ( pulErrorCode == NULL );

                    if ( bAck )
                    {
                        HandleAckReply( pReply );
                    }
                    else
                    {
                        HandleNakReply( *pulErrorCode, pReply );
                    }

                    AckNakClient( *pReply, bAck );

                    m_SonarReplyPendingList.erase( pReply );
                }

                if (  ! m_SonarReplyPendingList.empty() )
                {
                    m_SonarReplyPendingList.sort();
                }
            }
            __FINALLY
            {
                m_SonarReplyListGuard.Leave();
            }
            __ENDFINALLY
        }

        MaintainPendingReplyList();

        bSuccess = true;
    }
    catch ( ... )
    {
        bSuccess = false;
    }

    return bSuccess;
}

inline
void C7kSubsystem::MaintainPendingReplyList( void )
{
    __TRY
    {
        m_SonarReplyListGuard.Enter();

        SonarReplyListIterator_t pReply = m_SonarReplyPendingList.begin();

        while ( pReply != m_SonarReplyPendingList.end() )
        {
            if ( pReply->IsStale() )
            {
                CommandFailed( *pReply );
                pReply = m_SonarReplyPendingList.erase( pReply );
            }
            else
            {
                pReply++;
            }
        }

        if (  ! m_SonarReplyPendingList.empty() )
        {
            m_SonarReplyPendingList.sort();
        }
    }
    __FINALLY
    {
        m_SonarReplyListGuard.Leave();
    }
    __ENDFINALLY
}

inline
void C7kSubsystem::CommandFailed( const CAckNakReply &rReply )
{
    // No corresponding ACK or NAK reply was recieved by the queued reply object... it's about to
    // be erased so here we report it as an alarm. At this time we need to propagate a NAK back to
    // any client (e.g. routed command).

    __TRY
    {
        if ( rReply.m_ulRemoteControlId == C7kRemoteControl::commandIdVerifySystemResponse )
        {
            SensorResponsive( false );
        }
        else
        {
            CString      sDescription;

            sDescription.Format( "%d, %d, Command id %lu failed -- no reply.", SensorIndex(), -1, rReply.m_ulRemoteControlId );

            const char * pszMessage = static_cast<const char *>(static_cast<LPCTSTR>( sDescription ));

            if ( ! LogEvent( eventLogAdvisory, "%s", pszMessage ) )
            {
                TRACE( _T( "C7kSubsystem::CommandFailed(), LogEvent() failed.\n" ) );
            }
        }
    }
    __FINALLY
    {
        // Place any clean up here is necessary.
    }
    __ENDFINALLY
}

inline
void C7kSubsystem::HandleAckReply( const SonarReplyListIterator_t &rpReply )
{
    switch ( rpReply->m_ulRemoteControlId )
    {
        case C7kRemoteControl::commandIdVerifySystemResponse:

            SensorResponsive( true );
            break;

        default:
            break;
    }
}

inline
void C7kSubsystem::HandleNakReply(  const unsigned long            &rulErrorCode,
                                    const SonarReplyListIterator_t &rpReply )
{
    enum ENAKERRORCODE
    {
        nakErrorCodeReserved            = 0,
        nakErrorCodeRejectedCommand,
        nakErrorCodeUnknownCommand
    };

    __TRY
    {
        CString sDescription;
        sDescription.Format( "%d, %d, Command id %lu failed", SensorIndex(), -1, rpReply->m_ulRemoteControlId );

        // Handle specific error codes per DFD.

        switch ( rulErrorCode )
        {
            case nakErrorCodeReserved:

                sDescription += " -- reserved.";
                break;

            case nakErrorCodeRejectedCommand:

                sDescription += " -- rejected command.";
                break;

            case nakErrorCodeUnknownCommand:

                sDescription += " -- unknown command.";
                break;

            default:

                sDescription += ".";
                break;
        }

        if ( rpReply->m_ulRemoteControlId == C7kRemoteControl::commandIdVerifySystemResponse )
        {
            SensorResponsive( false );
        }
        else
        {
            const char * pszMessage = static_cast<const char *>(static_cast<LPCTSTR>( sDescription ) );

            //if ( ! ReportAlarm( true, alarmIdSensorCommandFailed, const_cast<char *>( pszMessage ) ) )
            //{
            //    TRACE( _T( "C7kSubsystem::HandleNakReply(), %s.\n" ), static_cast<LPCTSTR>( sDescription ) );
            //}

            if ( ! LogEvent( eventLogAdvisory, "%s", pszMessage ) )
            {
                TRACE( _T( "C7kSubsystem::HandleNakReply(), LogEvent() failed.\n" ) );
            }
        }
    }
    __FINALLY
    {
        // Place any clean up here as necessary.
    }
    __ENDFINALLY
}

inline
void C7kSubsystem::AckNakClient( const CAckNakReply &rReply, const bool &rbAck )
{
    if ( rReply.m_iClientIndex != -1 )
    {
        CSensorStatus::CMessageDescriptor Message;

        Message.SetAckNak(  ( ( rbAck ) ? CSensorStatus::CMessageDescriptor::ackNakTypeAcknowledge : CSensorStatus::CMessageDescriptor::ackNakTypeNotAcknowledge ),
                                rReply.m_iClientIndex,
                                    rReply.m_ulRemoteControlId );

        if ( ! m_Status.QueueMessage( Message ) )
        {
            TRACE( _T( "C7kSubsystem::AckNakClient(), m_Status.QueueMessage() failed.\n" ) );
        }
        else if ( ! NotifyHostOfReply( rReply.m_iClientIndex ) )
        {
            TRACE( _T( "C7kSubsystem::AckNakClient(), NotifyHostOfReply() failed.\n" ) );
        }
    }
}

inline
void C7kSubsystem::SensorResponsive( const bool &rbResponsive )
{
    if ( m_HealthStatus.UpdateStatus( rbResponsive ) )
    {
        CString     sDescription;
        const int   iSensorIndex = SensorIndex();
        bool        bResponding  = m_HealthStatus.IsResponsive();

        // If we're responsive then retract the alarm otherwise raise it.

        if ( bResponding )
        {
            __TRY
            {
                m_SonarCommandGuard.Enter();

                if ( ! m_SonarCommands.VolatileDataFeedSubscribe() )
                {
                    TRACE( _T( "C7kSubsystem::SensorResponsive(), m_SonarCommands.VolatileDataFeedSubscribe() failed.\n" ) );
                }

                if ( ! m_SonarCommands.QuerySonarCapabilities() )
                {
                    TRACE( _T( "C7kSubsystem::SensorResponsive(), QuerySonarCapabilities() failed\n" ) );
                }
            }
            __FINALLY
            {
                m_SonarCommandGuard.Leave();
            }
            __ENDFINALLY

            sDescription.Format( "%d, Sensor responding.", iSensorIndex );
        }
        else
        {
            sDescription.Format( "%d, Sensor not responding.", iSensorIndex );
        }

        const char *pszMessage = static_cast<const char *>( static_cast<LPCTSTR>( sDescription ) );

        if ( ! ReportAlarm( ( ! bResponding ) , alarmIdSensorNotResponding, const_cast<char *>( pszMessage ) ) )
        {
            TRACE( _T( "C7kSubsystem::SensorResponsive(), ReportAlarm() failed, %s.\n" ), pszMessage );
        }
    }
}

///////////////////////////////////////////////////////////////////////////////
// C7kSubsystem::CClientReply helper class implementation.

inline
C7kSubsystem::CClientReply::CClientReply(   const int       &riClientIndex,
                                            const EREPLYID  &reReplyId )
{
    m_iClientIndex = riClientIndex;
    m_eReplyId     = reReplyId;
}

inline
C7kSubsystem::CClientReply::CClientReply( const CClientReply &rRhs )
{
    m_iClientIndex = rRhs.m_iClientIndex;
    m_eReplyId     = rRhs.m_eReplyId;
}

inline
C7kSubsystem::CClientReply & C7kSubsystem::CClientReply::operator = ( const CClientReply &rRhs )
{
    if ( this != &rRhs )
    {
        m_iClientIndex = rRhs.m_iClientIndex;
        m_eReplyId     = rRhs.m_eReplyId;
    }

    return *this;
}

inline
C7kSubsystem::CClientReply::~CClientReply( void )
{
    m_iClientIndex = -1;
    m_eReplyId     = replyIDNone;
}

inline
bool C7kSubsystem::CClientReply::operator == ( const EREPLYID &reReplyId ) const
{
    return ( m_eReplyId == reReplyId );
}

inline
bool C7kSubsystem::CClientReply::IsValid( void ) const
{
    return ( m_iClientIndex != -1 );
}

///////////////////////////////////////////////////////////////////////////////
// C7kSubsystem::tagCONFIGURATIONDATA implementation.

inline
bool C7kSubsystem::tagCONFIGURATIONDATA::SetDefault( void )
{
    m_fPower                   = 3.0f;                                          // dB re: 1uPa.
    m_fRange                   = 20.0f;                                         // m.
    m_fGain                    = 1.0f;                                          // dB.
    m_fMaxPingRate             = 10.0f;                                         // Pings/s.

    m_fBottomDetectionMinRange = 1.0f;                                          // Arbitrary for now.
    m_fBottomDetectionMaxRange = 100.0f;                                        //  ditto.
    m_fBottomDetectionMinDepth = 1.0f;                                          //  ditto.
    m_fBottomDetectionMaxDepth = 100.0f;                                        //  ditto.

    m_fSoundSpeed              = 1490.0f;                                       // Fresh water 1490.0 m/s, 1425.0 m/s otherwise.
    m_fAbsorption              = 60.0f;                                         // 60 dB/km.
    m_fSpreadingLoss           = 30.0f;                                         // 30 dB.

    return true;
}

///////////////////////////////////////////////////////////////////////////////
// C7kSubsystem::CAckNakReply helper class implementation.

inline
C7kSubsystem::CAckNakReply::CAckNakReply    (   void )
                           :m_ulStalePeriod (   10000UL )                   // 10s (in ms). Time for which a pending ACK/NAK request is considered to be stale thus deleted.
{
    m_ulTicketNumber    = 0UL;
    m_ulRemoteControlId = 0UL;
    m_ulTimeStamp       = CSystemTime::GetTickCount();
    m_iClientIndex      = -1;

    memset( &m_sUniqueId, 0x00, sizeof( GUID ) );
}

inline
C7kSubsystem::CAckNakReply::CAckNakReply    (   const unsigned long &rulRemoteControlId,
                                                const unsigned long &rulTicketNumber,
                                                const GUID          &rsUniqueId,
                                                const int           &riClientIndex )

                           :m_ulStalePeriod (   10000UL )                    // 10s; time for which a pending ACK/NAK request is considered to be stale thus deleted.

{
    m_ulTicketNumber    = rulTicketNumber;
    m_ulRemoteControlId = rulRemoteControlId;
    m_sUniqueId         = rsUniqueId;
    m_ulTimeStamp       = CSystemTime::GetTickCount();
    m_iClientIndex      = riClientIndex;
}

inline
C7kSubsystem::CAckNakReply::~CAckNakReply( void )
{
    __TRY
    {
        memset( &m_sUniqueId, 0x00, sizeof( GUID ) );
    }
    __FINALLY
    {
        m_ulTicketNumber    = 0UL;
        m_ulRemoteControlId = 0UL;
        m_ulTimeStamp       = 0UL;
        m_iClientIndex      = -1;
    }
    __ENDFINALLY
}

inline
bool C7kSubsystem::CAckNakReply::operator == ( const unsigned long &rulTicketNumber ) const
{
    return ( m_ulTicketNumber == rulTicketNumber );
}

inline
bool C7kSubsystem::CAckNakReply::operator < ( const CAckNakReply &rRhs ) const
{
    return ( m_ulTicketNumber < rRhs.m_ulTicketNumber );
}

inline
bool C7kSubsystem::CAckNakReply::IsStale( void ) const
{
    return ( ( m_ulTimeStamp > 0UL ) ? ( ( CSystemTime::GetTickCount() - m_ulTimeStamp ) >= m_ulStalePeriod ) : false );
}

///////////////////////////////////////////////////////////////////////////////
// C7kSubsystem::CSonarCommands helper class implementation.

inline
C7kSubsystem::CSonarCommands::CSonarCommands        ( void )
                             :m_ulSystemDeviceId    ( 7000UL ),
                              m_unSystemEnumerator  ( 0 )
{
    m_sParamRanges.m_fMinRange        = 1.0;
    m_sParamRanges.m_fMaxRange        = 1.0;
    m_sParamRanges.m_fMinGain         = 0.0;
    m_sParamRanges.m_fMaxGain         = 100.0;
    m_sParamRanges.m_fMinPower        = 0.0;
    m_sParamRanges.m_fMaxPower        = 200.0;
    m_sParamRanges.m_fMinPulseLength  = 10.0;
    m_sParamRanges.m_fMaxPulseLength  = 100000.0;
    m_sParamRanges.m_fMinPingRate     = 0.5;
    m_sParamRanges.m_fMaxPingRate     = 50.0;
}

inline
C7kSubsystem::CSonarCommands::~CSonarCommands( void )
{
    m_sParamRanges.m_fMinRange        = 1.0;
    m_sParamRanges.m_fMaxRange        = 1.0;
    m_sParamRanges.m_fMinGain         = 0.0;
    m_sParamRanges.m_fMaxGain         = 100.0;
    m_sParamRanges.m_fMinPower        = 0.0;
    m_sParamRanges.m_fMaxPower        = 200.0;
    m_sParamRanges.m_fMinPulseLength  = 10.0;
    m_sParamRanges.m_fMaxPulseLength  = 100000.0;
    m_sParamRanges.m_fMinPingRate     = 0.5;
    m_sParamRanges.m_fMaxPingRate     = 50.0;
}

inline
bool C7kSubsystem::CSonarCommands::SetParent( C7kSubsystem * const pSubsystem )
{
    ASSERT( pSubsystem != NULL );

    m_pParent = pSubsystem;

    if ( m_pParent != NULL )
    {
        m_Command.SetClientId( pSubsystem->m_sClientId );

        return true;
    }

    return false;
}

inline
bool C7kSubsystem::CSonarCommands::ParameterRanges( const CSystemInfo::DEVICEINFO &rsDeviceInfo )
{
    try
    {
        m_sParamRanges.m_fMinRange       = rsDeviceInfo.m_fMinRange;
        m_sParamRanges.m_fMaxRange       = rsDeviceInfo.m_fMaxRange;
        m_sParamRanges.m_fMinGain        = rsDeviceInfo.m_fMinGain;
        m_sParamRanges.m_fMaxGain        = rsDeviceInfo.m_fMaxGain;
        m_sParamRanges.m_fMinPower       = rsDeviceInfo.m_fMinPower;
        m_sParamRanges.m_fMaxPower       = rsDeviceInfo.m_fMaxPower;
        m_sParamRanges.m_fMinPulseLength = rsDeviceInfo.m_fMinPulseLength;
        m_sParamRanges.m_fMaxPulseLength = rsDeviceInfo.m_fMaxPulseLength;
        m_sParamRanges.m_fMinPingRate    = rsDeviceInfo.m_fMinPingRate;
        m_sParamRanges.m_fMaxPingRate    = rsDeviceInfo.m_fMaxPingRate;
    }
    catch ( ... )
    {
        return false;
    }

    return true;
}

inline
C7kSubsystem::CSonarCommands::PARAMETERRANGES C7kSubsystem::CSonarCommands::ParameterRanges( void ) const
{
    // Return a copy of the parameter ranges structure for external clients to pre-scale the data
    // items, if necessary. E.g., power is a percentage, and needs to converted to dB re: 1uPa, externally.

    return m_sParamRanges;
}

inline
bool C7kSubsystem::CSonarCommands::QuerySonarSettings( void )
{
    // Query the following 7k record types using the remote control, single record request command
    // (record 7500, id 1050):
    //
    // 7002 - 7k Matched filter settings.
    // 7004 - 7k Beam geometry.
    // 7005 - 7k Calibration data.
    //
    // Note:    a) once the solicited reponses are received, they will be passed along for logging in the normal manner.
    //          b) Patrik notes: record #7001 is never trasmited unless you request it.
    //              To request a 7001 set device id, subsystem id and enumerator to 0 in the record frame. 

    static COMMAND sCommands[] =    {
                                        { recordTypeRemoteControlSonarSettings, false },    // R7503
                                        { recordTypeVolatileSettings,           false },    // R7000
                                        { recordTypeMatchFilter,                false },    // R7002
                                        { recordTypeBeamGeometry,               false },    // R7004
                                        { recordTypeCalibrationData,            false }     // R7005
                                    };

    return QueryMultipleSettings( &sCommands[ 0 ], DimOf_m( sCommands ) );
}

inline
bool C7kSubsystem::CSonarCommands::QuerySonarStaticSettings( void )
{
    // Query records 7002 (matched filter), 7004 (beam geometry) and 7005 (calibration data).
    // Typically performed every time the logging file is to be changed to ensure sufficient
    // info about the data is included within the resultant log file.

    static COMMAND sCommands[] =    {
                                        { recordTypeMatchFilter,                false },    // R7002
                                        { recordTypeBeamGeometry,               false },    // R7004
                                        { recordTypeCalibrationData,            false }     // R7005
                                    };

    return QueryMultipleSettings( &sCommands[ 0 ], DimOf_m( sCommands ) );
}

inline
bool C7kSubsystem::CSonarCommands::QueryConfigSettings( void )
{
    static COMMAND sCommands[] =    {
                                        { recordTypeVolatileSettings,           false }     // R7000
                                    };

    return QueryMultipleSettings( &sCommands[ 0 ], DimOf_m( sCommands ) );
}

inline
bool C7kSubsystem::CSonarCommands::QuerySonarCapabilities( void )
{
    static COMMAND sCommands[] =    {
                                        { recordTypeConfigurationSettings,      true  }     // R7001
                                    };

    return QueryMultipleSettings( &sCommands[ 0 ], DimOf_m( sCommands ) );
}

inline
bool C7kSubsystem::CSonarCommands::QuerySonarRemoteSettings( void )
{
    static COMMAND sCommands[] =    {
                                        { recordTypeRemoteControlSonarSettings, false },    // R7503
                                    };

    return QueryMultipleSettings( &sCommands[ 0 ], DimOf_m( sCommands ) );
}

inline
bool C7kSubsystem::CSonarCommands::QueryMultipleSettings(   const COMMAND * const   pasCommands,
                                                            const unsigned int      uiNumberOfCommands )
{
    // Use the remote control single record request (record 7500, id 1050) to request the specified 
    // records in the array pointed to by pasCommands.

    bool bSuccess = true;                   // Assume success until we know otherwise.

    for ( unsigned int uiCommand = 0U; uiCommand < uiNumberOfCommands; uiCommand++ )
    {
        ASSERT( m_pParent != NULL );

        unsigned long  ulDeviceId   = m_pParent->m_ulDeviceId;
        unsigned short unEnumerator = m_pParent->m_unSystemEnumerator;

        if ( pasCommands[ uiCommand ].m_bSystemCommand )
        {
            ulDeviceId   = m_ulSystemDeviceId;
            unEnumerator = m_unSystemEnumerator;
        }

        if ( ! RequestSingleRecord( pasCommands[ uiCommand ].m_ulCommand, ulDeviceId, unEnumerator ) )
        {
            bSuccess = false;
            TRACE( _T( "C7kSubsystem::CSonarCommands::QueryMultipleSettings(), RequestSingleRecord() failed for record %lu\n" ), pasCommands[ uiCommand ].m_ulCommand );
        }
    }

    ASSERT( bSuccess );

    return bSuccess;
}

inline
bool C7kSubsystem::CSonarCommands::VolatileDataFeedSubscribe( void )
{
    // Here we subscribe to all necessary record types.

    bool bSuccess = true;               // Assume success for now.

    try
    {
        vector<unsigned long> MessageIds;
        MessageIds.reserve( 50 );

        // General messages.

        MessageIds.push_back( recordTypeRemoteControlAck );
        MessageIds.push_back( recordTypeRemoteControlNak );

        MessageIds.push_back( recordTypeSystemEvents );
        MessageIds.push_back( recordTypeSystemEventMessage );

        MessageIds.push_back( recordTypeTimeMessage );

        MessageIds.push_back( recordTypeTrigger );
        MessageIds.push_back( recordTypeTriggerSequenceSetup );
        MessageIds.push_back( recordTypeTriggerSequenceDone );

        // Configuration state info.

        MessageIds.push_back( recordTypeRemoteControlSonarSettings );
        MessageIds.push_back( recordTypeVolatileSettings );

        // Mode specific records.

        if ( m_pParent->IsDataCollectionModeBathymetricData() ||
             m_pParent->IsDataCollectionModeBackscatteredImagery() ||
             m_pParent->IsDataCollectionModeBeamData() )
        {
            MessageIds.push_back( recordTypeMatchFilter );
            MessageIds.push_back( recordTypeBeamGeometry );
            MessageIds.push_back( recordTypeCalibrationData );
        }

        if ( m_pParent->IsDataCollectionModeBeamData() )
        {
            MessageIds.push_back( recordTypeBeamData );
            MessageIds.push_back( recordTypeImageData );
        }

        if ( m_pParent->IsDataCollectionModeBathymetricData() )
        {
            MessageIds.push_back( recordTypeBathymetricData );
        }

        if ( m_pParent->IsDataCollectionModeBackscatteredImagery() )
        {
            MessageIds.push_back( recordTypeBackscatterImageData );
        }

        ASSERT( m_pParent != NULL );

        if ( ! VolatileDataFeed( MessageIds.size(), &MessageIds[ 0 ] ) )
        {
            TRACE( _T( "C7kSubsystem::CSonarCommands::VolatileDataFeedSubscribe(), VolatileDataFeed() failed.\n" ) );
            bSuccess = false;
        }

        // Finally use the volatile data feed command w/ range of records to subscribe to generic sensor data as necessary.

        if ( m_pParent->IsDataCollectionModeGenericSensors() )
        {
            if ( ! VolatileDataFeed( recordTypeGenericSensorBegin, recordTypeGenericSensorEnd ) )
            {
                TRACE( _T( "C7kSubsystem::CSonarCommands::VolatileDataFeedSubscribe(), Can't subscribe to generic sensor data.\n" ) );
                bSuccess = false;
            }
        }
    }
    catch ( ... )
    {
        bSuccess = false;
        TRACE( _T( "C7kSubsystem::CSonarCommands::VolatileDataFeedSubscribe(), Unspecified exception caught.\n" ) );
    }

    return bSuccess;
}

inline
bool C7kSubsystem::CSonarCommands::QueryIsAlive( void )
{
    if ( m_Command.VerifySystemResponse() )
    {
        return SendRecord( m_Command );
    }

    return false;
}

inline
bool C7kSubsystem::CSonarCommands::PingEnable(  const bool &rbEnable )
{
    ASSERT( m_pParent != NULL );

    if ( m_Command.PingEnable( rbEnable, m_pParent->m_ulDeviceId, m_pParent->m_unSystemEnumerator ) )
    {
        return SendRecord( m_Command );
    }

    return false;
}

inline
bool C7kSubsystem::CSonarCommands::ShutdownSonar( void )
{
    if ( m_Command.Shutdown() )
    {
        return SendRecord( m_Command );
    }

    return false;
}

inline
bool C7kSubsystem::CSonarCommands::Reboot( void )
{
    if ( m_Command.Reboot() )
    {
        return SendRecord( m_Command );
    }

    return false;
}

inline
bool C7kSubsystem::CSonarCommands::Calibrate( void )
{
    if ( m_Command.Calibrate() )
    {
        return SendRecord( m_Command );
    }

    return false;
}

inline
bool C7kSubsystem::CSonarCommands::Range( const float &rfRangeInMeters )
{
    ASSERT( m_pParent != NULL );

    if ( m_Command.Range(   max( min( rfRangeInMeters, m_sParamRanges.m_fMaxRange ), m_sParamRanges.m_fMinRange ),
                                m_pParent->m_ulDeviceId,
                                    m_pParent->m_unSystemEnumerator ) )
    {
        return SendRecord( m_Command );
    }

    return false;
}

inline
bool C7kSubsystem::CSonarCommands::MaxPingRate( const float &rfMaxPingsPerSecond )
{
    ASSERT( m_pParent != NULL );

    if ( m_Command.MaxPingRate( max( min( rfMaxPingsPerSecond, m_sParamRanges.m_fMaxPingRate ), m_sParamRanges.m_fMinPingRate ),
                                    m_pParent->m_ulDeviceId,
                                        m_pParent->m_unSystemEnumerator ) )
    {
        return SendRecord( m_Command );
    }

    return false;
}

inline
bool C7kSubsystem::CSonarCommands::TransmitPower( const float &rfdBReOneMicroPascal )
{
    ASSERT( m_pParent != NULL );

    if ( m_Command.TransmitPower(   max( min( rfdBReOneMicroPascal, m_sParamRanges.m_fMaxPower ), m_sParamRanges.m_fMinPower ),
                                        m_pParent->m_ulDeviceId,
                                            m_pParent->m_unSystemEnumerator ) )
    {
        return SendRecord( m_Command );
    }

    return false;
}

inline
bool C7kSubsystem::CSonarCommands::PulseWidth(  const float &rfTxPulseWidthInSeconds )
{
    ASSERT( m_pParent != NULL );

    if ( m_Command.PulseWidth(  max( min( rfTxPulseWidthInSeconds, m_sParamRanges.m_fMaxPulseLength ), m_sParamRanges.m_fMinPulseLength ),
                                    m_pParent->m_ulDeviceId,
                                        m_pParent->m_unSystemEnumerator ) )
    {
        return SendRecord( m_Command );
    }

    return false;
}

inline
bool C7kSubsystem::CSonarCommands::PulseType( const unsigned long &rulPulseType )
{
    ASSERT( m_pParent != NULL );

    if ( m_Command.PulseType( rulPulseType, m_pParent->m_ulDeviceId, m_pParent->m_unSystemEnumerator ) )
    {
        return SendRecord( m_Command );
    }

    return false;
}

inline
bool C7kSubsystem::CSonarCommands::ReceiverGain( const float &rfGainIndB )
{
    ASSERT( m_pParent != NULL );

    if ( m_Command.ReceiverGain( max( min( rfGainIndB, m_sParamRanges.m_fMaxGain ), m_sParamRanges.m_fMinGain ),
                                    m_pParent->m_ulDeviceId,
                                        m_pParent->m_unSystemEnumerator ) )
    {
        return SendRecord( m_Command );
    }

    return false;
}

inline
bool C7kSubsystem::CSonarCommands::BottomDetectMethod( const unsigned long &rulBottomDetectMask )
{
    ASSERT( m_pParent != NULL );

    if ( m_Command.BottomDetectMethod( rulBottomDetectMask, m_pParent->m_ulDeviceId, m_pParent->m_unSystemEnumerator ) )
    {
        return SendRecord( m_Command );
    }

    return false;
}

inline
bool C7kSubsystem::CSonarCommands::BottomDetectionFilterInfo(   const float &rfMinRange,
                                                                const float &rfMaxRange,
                                                                const float &rfMinDepth,
                                                                const float &rfMaxDepth )
{
    float fMinRange = max( min( rfMinRange, m_sParamRanges.m_fMaxRange ), m_sParamRanges.m_fMinRange );
    float fMaxRange = max( min( rfMaxRange, m_sParamRanges.m_fMaxRange ), m_sParamRanges.m_fMinRange );
    float fMinDepth = rfMinDepth;
    float fMaxDepth = rfMaxDepth;

    if ( fMinRange > fMaxRange )
    {
        float fTemp = fMaxRange;
        fMaxRange = fMinRange;
        fMinRange = fTemp;
    }

    if ( fMinDepth > fMaxDepth )
    {
        float fTemp = fMaxDepth;
        fMaxDepth   = fMinDepth;
        fMinDepth   = fTemp;
    }

    ASSERT( m_pParent != NULL );

    if ( m_Command.BottomDetectionFilterInfo(   fMinRange,
                                                    fMaxRange,
                                                        fMinDepth,
                                                            fMaxDepth,
                                                                m_pParent->m_ulDeviceId,
                                                                    m_pParent->m_unSystemEnumerator ) )
    {
        return SendRecord( m_Command );
    }

    return false;
}

inline
bool C7kSubsystem::CSonarCommands::ProjectorSelection( const unsigned long &rulProjectorMagicNumber )
{
    ASSERT( m_pParent != NULL );

    if ( m_Command.ProjectorSelection( rulProjectorMagicNumber, m_pParent->m_ulDeviceId, m_pParent->m_unSystemEnumerator ) )
    {
        return SendRecord( m_Command );
    }

    return false;
}

inline
bool C7kSubsystem::CSonarCommands::ProjectorStabilization(  const unsigned long &rulProjectorMask )
{
    ASSERT( m_pParent != NULL );

    if ( m_Command.ProjectorStabilization( rulProjectorMask, m_pParent->m_ulDeviceId, m_pParent->m_unSystemEnumerator ) )
    {
        return SendRecord( m_Command );
    }

    return false;
}

inline
bool C7kSubsystem::CSonarCommands::TransmitterStabilization( const unsigned long &rulStabalizationMask )
{
    ASSERT( m_pParent != NULL );

    if ( m_Command.TransmitterStabilization( rulStabalizationMask, m_pParent->m_ulDeviceId, m_pParent->m_unSystemEnumerator ) )
    {
        return SendRecord( m_Command );
    }

    return false;
}

inline
bool C7kSubsystem::CSonarCommands::AutoRange( const unsigned long &rulAutoRangeMask )
{
    if ( m_Command.AutoRange( rulAutoRangeMask ) )
    {
        return SendRecord( m_Command );
    }

    return false;
}

inline
bool C7kSubsystem::CSonarCommands::SelectHydroPhone( const unsigned long &rulHydroPhoneMagicNumber )
{
    if ( m_Command.SelectHydroPhone( rulHydroPhoneMagicNumber ) )
    {
        return SendRecord( m_Command );
    }

    return false;
}

inline
bool C7kSubsystem::CSonarCommands::ProjectorSteering(   const float &rfSteeringAngleX,
                                                        const float &rfSteeringAngleY )
{
    if ( m_Command.ProjectorSteering( rfSteeringAngleX, rfSteeringAngleY ) )
    {
        return SendRecord( m_Command );
    }

    return false;
}

inline
bool C7kSubsystem::CSonarCommands::ReceiverGainType( const unsigned long &rulReceiverGainType )
{
    if ( m_Command.ReceiverGainType( rulReceiverGainType ) )
    {
        return SendRecord( m_Command );
    }

    return false;
}

inline
bool C7kSubsystem::CSonarCommands::TVGCoefficients( void )
{
    if ( m_Command.TVGCoefficients() )
    {
        return SendRecord( m_Command );
    }

    return false;
}

inline
bool C7kSubsystem::CSonarCommands::AutoReceiverGain( const unsigned long &rulGainFlag )
{
    ASSERT( m_pParent != NULL );

    if ( m_Command.AutoReceiverGain( rulGainFlag, m_pParent->m_ulDeviceId, m_pParent->m_unSystemEnumerator ) )
    {
        return SendRecord( m_Command );
    }

    return false;
}

inline
bool C7kSubsystem::CSonarCommands::RequestSingleRecord( const unsigned long     &rulRecordType,
                                                        const unsigned long     &rulDeviceId,
                                                        const unsigned short    &runEnumerator )
{
    ASSERT( m_pParent != NULL );

    if ( m_Command.RequestSingleRecord( rulRecordType, rulDeviceId, runEnumerator ) )
    {
        return SendRecord( m_Command );
    }

    return false;
}

inline
bool C7kSubsystem::CSonarCommands::VolatileDataFeed(    const unsigned long     &rulNumberOfRecords,
                                                        const unsigned long     *paulRecordNumbers )
{
    ASSERT( m_pParent != NULL );

    if ( m_Command.VolatileDataFeed( rulNumberOfRecords, paulRecordNumbers, m_pParent->m_ulDeviceId, m_pParent->m_unSystemEnumerator ) )
    {
        return SendRecord( m_Command );
    }

    return false;
}

inline
bool C7kSubsystem::CSonarCommands::VolatileDataFeed(    const unsigned long     &rulBeginRecordNumber,
                                                        const unsigned long     &rulEndRecordNumber )
{
    ASSERT( m_pParent != NULL );

    if ( m_Command.VolatileDataFeed( rulBeginRecordNumber, rulEndRecordNumber, m_pParent->m_ulDeviceId, m_pParent->m_unSystemEnumerator ) )
    {
        return SendRecord( m_Command );
    }

    return false;
}

inline
bool C7kSubsystem::CSonarCommands::DisableVolatileDataFeed( void )
{
    ASSERT( m_pParent != NULL );

    if ( m_Command.DisableVolatileDataFeed( m_pParent->m_ulDeviceId, m_pParent->m_unSystemEnumerator ) )
    {
        return SendRecord( m_Command );
    }

    return false;
}

inline
bool C7kSubsystem::CSonarCommands::PersistentDataFeed(  const bool           &rbEnable,
                                                        const unsigned long  &rulIPAddress,
                                                        const unsigned short &runPort,
                                                        const unsigned short &runProtocolFlag,
                                                        const unsigned long  &rulNumberOfRecords,
                                                        const unsigned long  *paulRecordNumbers )
{
    ASSERT( m_pParent != NULL );

    if ( m_Command.PersistentDataFeed( rbEnable, rulIPAddress, runPort, runProtocolFlag, rulNumberOfRecords, paulRecordNumbers, m_pParent->m_ulDeviceId, m_pParent->m_unSystemEnumerator ) )
    {
        return SendRecord( m_Command );
    }

    return false;
}

inline
bool C7kSubsystem::CSonarCommands::LoadSystemDefaults( void )
{
    if ( m_Command.LoadSystemDefaults() )
    {
        return SendRecord( m_Command );
    }

    return false;
}

inline
bool C7kSubsystem::CSonarCommands::LoadDeviceDefaults(  void )
{
    ASSERT( m_pParent != NULL );

    if ( m_Command.LoadDeviceDefaults( m_pParent->m_ulDeviceId, m_pParent->m_unSystemEnumerator ) )
    {
        return SendRecord( m_Command );
    }

    return false;
}

inline
bool C7kSubsystem::CSonarCommands::Roll( const float &rfRollInRadians )
{
    if ( m_DataRecord.Encode( recordTypeRoll, &rfRollInRadians ) )
    {
        return SendRecord( m_DataRecord );
    }

    return false;
}

inline
bool C7kSubsystem::CSonarCommands::Pitch( const float &rfPitchInRadians )
{
    if ( m_DataRecord.Encode( recordTypePitch, &rfPitchInRadians ) )
    {
        return SendRecord( m_DataRecord );
    }

    return false;
}

inline
bool C7kSubsystem::CSonarCommands::SoundSpeed( const float &rfSoundSpeedMetersInPerSecond )
{
    if ( m_DataRecord.Encode( recordTypeSoundSpeed, &rfSoundSpeedMetersInPerSecond ) )
    {
        return SendRecord( m_DataRecord );
    }

    return false;
}

inline
bool C7kSubsystem::CSonarCommands::AbsorptionLoss( const float &rfAbsorptionLossIndBPerKilometer )
{
    if ( m_DataRecord.Encode( recordTypeAbsorptionLoss, &rfAbsorptionLossIndBPerKilometer ) )
    {
        return SendRecord( m_DataRecord );
    }

    return false;
}

inline
bool C7kSubsystem::CSonarCommands::SpreadingLoss( const float &rfSpreadingLossIndB )
{
    if ( m_DataRecord.Encode( recordTypeSpreadingLoss, &rfSpreadingLossIndB ) )
    {
        return SendRecord( m_DataRecord );
    }

    return false;
}

///////////////////////////////////////////////////////////////////////////////
// Overloads for C7kSubsystem::CSonarCommands::SendRecord().

inline
bool C7kSubsystem::CSonarCommands::SendRecord(  const BYTE          *pby7kRecord,
                                                const unsigned long &rulBytes,
                                                const int           &riClientIndex )
{
    bool bSuccess = false;              // Assume failure for now.

    try
    {
        if ( rulBytes >= 0UL )
        {
            if ( pby7kRecord == NULL )
            {
                ThrowMessage_m( "pbyRecord is NULL" );
            }

            if ( ! C7kProtocol::IsValid7kRecord( pby7kRecord, rulBytes ) )
            {
                ThrowMessage_m( "7k record is invalid" );
            }

            ASSERT ( m_pParent != NULL );

            CDispatcher * pDispatcher = static_cast<CDispatcher *>( m_pParent->m_Dispatcher );

            if ( ( pDispatcher == NULL ) || ( ! pDispatcher->IsInitialized() ) )
            {
                ThrowMessage_m( "Can't acquire the dispatcher object or the object is not initialized" );
            }

            if ( ! m_pParent->QueueAckNakPending( pby7kRecord, rulBytes, riClientIndex ) )
            {
                ThrowMessage_m( "Can't queue pending ACK/NAK" );
            }

            bSuccess = pDispatcher->Write7kRecord( m_pParent->SensorIndex(), pby7kRecord, rulBytes );
        }
        else
        {
            bSuccess = true;
        }
    }
    catch ( LPCTSTR lpszMessage )
    {
        bSuccess = false;
        TRACE ( _T( "C7kSubsystem::CSonarCommands::SendRecord(), %s.\n" ), lpszMessage );
    }
    catch ( ... )
    {
        bSuccess = false;
        TRACE ( _T( "C7kSubsystem::CSonarCommands::SendRecord(), unspecified exception caught.\n" ) );
    }

    return bSuccess;
}

inline
bool C7kSubsystem::CSonarCommands::SendRecord( const C7kRemoteControl &rRecord )
{
    return SendRecord( static_cast<BYTE *>( rRecord ), static_cast<unsigned long>( rRecord ) );
}

template <typename tRTH>
inline bool C7kSubsystem::CSonarCommands::SendRecord( const CGeneric7kRecordEncoder<tRTH> &rRecord )

{
    return SendRecord( static_cast<BYTE *>( rRecord ), static_cast<unsigned long>( rRecord ) );
}

///////////////////////////////////////////////////////////////////////////////
// C7kSubsystem::CConfigQuery helper class implementation.

inline
C7kSubsystem::CConfigQuery::CConfigQuery( void )
{
    m_bSaveOnExit   = false;
    m_bAccumulating = false;
    m_hDoneEvent    = NULL;
}

inline
C7kSubsystem::CConfigQuery::~CConfigQuery( void )
{
    __TRY
    {
        if ( m_hDoneEvent != NULL )
        {
            CloseHandle( m_hDoneEvent );
        }
    }
    __FINALLY
    {
        m_hDoneEvent    = NULL;
        m_bAccumulating = false;
        m_bSaveOnExit   = false;
    }
    __ENDFINALLY
}

inline
bool C7kSubsystem::CConfigQuery::Configure( void )
{
    m_bSaveOnExit   = true;
    m_bAccumulating = true;

    if ( m_hDoneEvent == NULL )
    {
        m_hDoneEvent = CreateEvent( NULL, TRUE, FALSE, NULL );
    }

    if ( m_hDoneEvent != NULL )
    {
        ResetEvent( m_hDoneEvent );
    }

    return ( m_hDoneEvent != NULL );
}

inline
void C7kSubsystem::CConfigQuery::Release( void )
{
    if ( m_hDoneEvent != NULL )
    {
        // Are we possibly still waiting? If so, set the event to allow possible waiting threads to be
        // released then relinquish this thread's time slice.

        __TRY
        {
            if ( m_bAccumulating )
            {
                SetEvent( m_hDoneEvent );
                Sleep( 0 );
            }

            CloseHandle( m_hDoneEvent );
        }
        __FINALLY
        {
            m_hDoneEvent = NULL;
        }
        __ENDFINALLY
    }
}

inline
void C7kSubsystem::CConfigQuery::SaveOnExit( const bool &rbEnable )
{
    m_bSaveOnExit = rbEnable;
}

inline
bool C7kSubsystem::CConfigQuery::SaveOnExit( void ) const
{
    return m_bSaveOnExit;
}

inline
bool C7kSubsystem::CConfigQuery::WaitForConfigReceived( const unsigned long &rulTimeout )
{
    return ( ( m_hDoneEvent != NULL ) && ( WaitForSingleObject( m_hDoneEvent, rulTimeout ) != WAIT_TIMEOUT ) );
}

inline
void C7kSubsystem::CConfigQuery::DoneAccumulating( void )
{
    m_bAccumulating = false;

    if ( m_hDoneEvent != NULL )
    {
        SetEvent( m_hDoneEvent );
    }
}

inline
bool C7kSubsystem::CConfigQuery::IsAccumulating( void ) const
{
    return m_bAccumulating;
}

inline
C7kSubsystem::CConfigQuery::operator C7kSubsystem::CONFIGURATIONDATA * ( void )
{
    return ( static_cast<CONFIGURATIONDATA *>( this ) );
}

inline
void C7kSubsystem::CConfigQuery::DecodeRecord(  const unsigned long    &rulRecordNumber,
                                                const BYTE             *pby7kRecord,
                                                const unsigned long    &rulBytes )
{
    if ( m_bAccumulating && ( rulBytes > 0UL ) && ( pby7kRecord != NULL ) )
    {
        switch ( rulRecordNumber )
        {
            case recordTypeVolatileSettings:

                // Here decode and extract the relevant volatile settings.

                __TRY
                {
                    C7kProtocol Record;

                    PRECORDHEADER7K psDRF            = NULL;
                    PBYTE           pbyDynamicStream = NULL;
                    unsigned long   ulDynamicBytes   = 0UL;

                    if ( Record.DecodeRecord( pby7kRecord, rulBytes, false, psDRF, pbyDynamicStream, ulDynamicBytes ) )
                    {
                        ASSERT( psDRF->IsValid() );
                        ASSERT( psDRF->m_ulRecordType == recordTypeVolatileSettings );

                        PR7000 psRTH = reinterpret_cast<PR7000>( pbyDynamicStream );

                        m_fPower                    = psRTH->power;
                        m_fRange                    = psRTH->range;
                        m_fGain                     = psRTH->gain;
                        m_fMaxPingRate              = psRTH->max_ping_rate;

                        m_fBottomDetectionMinRange  = psRTH->bottom_detection_info1;
                        m_fBottomDetectionMaxRange  = psRTH->bottom_detection_info2;
                        m_fBottomDetectionMinDepth  = psRTH->bottom_detection_info3;
                        m_fBottomDetectionMaxDepth  = psRTH->bottom_detection_info4;

                        m_fSoundSpeed               = psRTH->sound_velocity;
                        m_fAbsorption               = psRTH->absorption;
                        m_fSpreadingLoss            = psRTH->spreading_loss;
                    }
                }
                __FINALLY
                {
                    DoneAccumulating();
                }
                __ENDFINALLY

                break;

            default:
                break;
        }
    }
}


C7kSubsystem::CHealthStatus::CHealthStatus( void )

                            :m_iMinFailCount( 0 ),
                             m_iMaxFailCount( 5 )
{
    __TRY
    {
        m_StateGuard.Enter();

        m_bFailed    = false;
        m_iFailCount = 0;
    }
    __FINALLY
    {
        m_StateGuard.Leave();
    }
    __ENDFINALLY
}

C7kSubsystem::CHealthStatus::~CHealthStatus( void )
{
    __TRY
    {
        m_StateGuard.Enter();

        m_bFailed    = false;
        m_iFailCount = 0;
    }
    __FINALLY
    {
        m_StateGuard.Leave();
    }
    __ENDFINALLY
}

bool C7kSubsystem::CHealthStatus::IsResponsive( void ) const
{
    bool bResponsive = true;

    __TRY
    {
        m_StateGuard.Enter();
        bResponsive = ! m_bFailed;
    }
    __FINALLY
    {
        m_StateGuard.Leave();
    }
    __ENDFINALLY

    return bResponsive;
}

bool C7kSubsystem::CHealthStatus::UpdateStatus( const bool &rbResponding )
{
    bool bStatusChanged = false;

    __TRY
    {
        m_StateGuard.Enter();

        // Increment or decrement the fail counter as appropriate then ceil the result on the range [m_iMinFailCount, m_iMaxFailCount]. This
        // give a hysteresis thus smoothing the transition between modes.

        m_iFailCount = max( min( ( ( rbResponding ) ? --m_iFailCount : ++m_iFailCount ), m_iMaxFailCount ), m_iMinFailCount );

        if ( m_iFailCount == m_iMaxFailCount )
        {
            bStatusChanged = ( m_bFailed == false );
            m_bFailed = true;
        }
        else if ( m_iFailCount == m_iMinFailCount )
        {
            bStatusChanged = ( m_bFailed == true );
            m_bFailed = false;
        }
    }
    __FINALLY
    {
        m_StateGuard.Leave();
    }
    __ENDFINALLY

    return bStatusChanged;
}

