//
//  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:   6046RemoteClient.h
//
//  Project:    6046
//
//  Author(s):  W. Arcus
//
//  Purpose:    Implements a remote client helper class for the 6046.
//
//  Notes:      1)  This class uses asynchronous comms. Recieved messages are passed
//                  back to the client via the StatusCallback
//
//              2)  In order for this class not to expose any private members of note, the connection
//                  object pertaining to each client is kept as a void *. It is cast internally when
//                  needed.
//

#include "StdAfx.h"
#include "Remote6046.h"
#include "6046RemoteClient.h"

#pragma warning( disable : 4018 )                                       // Microsoft's STL is dirty so disable appropriate warnings for now.
#include "..\..\Utils\NetUtils\7kSocketProtocol.h"
#pragma warning( default : 4018 )

#include "..\..\Utils\NetUtils\ThreadedSocket.h"
#include "..\..\Utils\NetUtils\7kCommand.h"
#include "..\..\Utils\NetUtils\7kStatus.h"
#include "..\..\Utils\NetUtils\7kAcknowlege.h"
#include "..\Components\EdgeTechFSDW\EdgeTechHeaders.h"

#include "..\..\Utils\NetUtils\BaseThread.h"

///////////////////////////////////////////////////////////////////////////////
// Macros, definitions and constants.

typedef CThreadedSocket<C7kSocketProtocol> CClientConnection;

namespace                                                               // Begin anonoymous namespace.
{

    class CConnection : protected CBaseThread
    {
    public:

        ///////////////
        // Services.

                                    CConnection         (   C6046RemoteClient * const   pRemoteClient         = NULL,
                                                            const unsigned long        &rulAliveMonitorPeriod = 10000UL );     // 30s alive monitoring period by default.

        virtual                    ~CConnection         (   void );

        bool                        Connect             (   PFN_REMOTECLIENT_CALLBACK    pfnStatusCallback,
                                                            void                        *vpStatusCallbackParam,
                                                            PFN_REMOTECLIENT_CALLBACK    pfnDataCallback,
                                                            void                        *pvDataCallbackParam,
                                                            const char                  *pszRemoteAddress,
                                                            const unsigned int           uiPort );

        bool                        Disconnect          (   void );
                                    
        bool                        IsAlive             (   void ) const;

        void                        AliveMonitoring     (   const bool                  &rbEnable );
                                    
        bool                        Send                (   const BYTE                  *pby7kRecord,
                                                            const unsigned long         &rulBytes );

    protected:

        ///////////////
        // Definitions.

        enum MESSAGES
        {
            messageIsAliveCheck = CBaseThread::threadMessageTerminate + 1
        };

        enum EMESSAGETYPE
        {
            messageTypeUnknown      = -1,
            messageTypeSubscribed,
            messageTypeStatus,
            messageTypeAlarm,
            messageTypeAckNak
        };

        ///////////////
        // Attributes.

        const unsigned long         m_ulMaxMessageLength;
        const int                   m_iMaxFailCount;

        bool                        m_bMonitorAliveStatus;
        bool                        m_bIsAlive;
        bool                        m_bIsAliveReplyPending;

        int                         m_iAliveFailCount;

        PFN_REMOTECLIENT_CALLBACK   m_pfnStatusCallback;
        PFN_REMOTECLIENT_CALLBACK   m_pfnDataCallback;

        void                       *m_pvParamStatusCallback;
        void                       *m_pvParamDataCallback;

        C6046RemoteClient          *m_pParent;

        CClientConnection          *m_pConnection;

        mutable CCritical           m_Critical;

        ///////////////
        // Services.

        void                        WatchCycle          (   void );
        BOOL                        ProcessMessage      (   MSG                         *psMsg );

        void                        IsPLCAlive          (   const bool                  &rbForceQuery   = false );

        EMESSAGETYPE                MessageType         (   const BYTE                  *pbyData,
                                                            const unsigned long         &rulBytes ) const;


        void                        HandleCallback      (   BYTE                        *pbyData,
                                                            unsigned long                ulNumBytes,
                                                            int                          iSocketIndex,
                                                            unsigned long                ulTimestamp );
        static
        void                        SocketCallback      (   BYTE                        *pbyData,                               // Data pointer where relevant - can be NULL.
                                                            PVOID                        lpvParam,                              // Optional parameter.
                                                            ULONG                        ulNumBytes,                            // Number of data bytes received - can be zero.
                                                            INT                          iSocketIndex,                          // Socket index (relevant to server end).
                                                            ULONG                        ulTimeStamp,                           // Time stamp.
                                                            INT                          iResonForCallback );                   // Reason for the callback.

        bool                        PreprocessMessage   (   const EMESSAGETYPE          &reMessageType,
                                                            const BYTE                  *pbyData,
                                                            const unsigned long         &rulBytes );

    };
};                                                                      // End anonymous namespace.

///////////////////////////////////////////////////////////////////////////////
// Macros.

#define Connection_m()              reinterpret_cast<CConnection *>( m_pvConnection )

#define IF_CONNECTED                CConnection *pConnection = Connection_m();                                           \
                                    if ( pConnection == NULL )                                                           \
                                    {                                                                                    \
                                        bSuccess = false;                                                                \
                                        m_eLastErrorCode = errorCodeNotConnected;                                        \
                                        TRACE( _T( "pConnection is NULL, File: " __FILE__ ", Line: %d.\n" ), __LINE__ ); \
                                        ASSERT( false );                                                                 \
                                    }                                                                                    \
                                    else

#define Send7kRecord_m( Record )    Send7kRecord( static_cast<BYTE *>( Record ), static_cast<unsigned long>( Record ) )

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif

///////////////////////////////////////////////////////////////////////////////
// C6046RemoteClient class implementation starting with public services.

C6046RemoteClient::C6046RemoteClient( void )
                  :m_ulRecordTypeAny( static_cast<unsigned long>( -1 ) ),
                   m_iSensorAny( -1 )
{
    ManageDllState_m();

    m_eLastErrorCode = errorCodeNone;
    m_pvConnection   = NULL;
}

C6046RemoteClient::~C6046RemoteClient( void )
{
    ManageDllState_m();

    __TRY
    {
        if ( m_pvConnection != NULL )
        {
            delete Connection_m();
            m_pvConnection = NULL;
        }
    }
    __FINALLY
    {
        m_eLastErrorCode = errorCodeNone;

        ASSERT( m_pvConnection == NULL );
        m_pvConnection = NULL;
    }
    __ENDFINALLY
}

///////////////////////////////////////////////////////////////////////////////
// Connection and subscription related services.

bool C6046RemoteClient::Connect(    PFN_REMOTECLIENT_CALLBACK   pfnStatusCallback,
                                    void                       *vpStatusCallbackParam,
                                    PFN_REMOTECLIENT_CALLBACK   pfnDataCallback,
                                    void                       *pvDataCallbackParam,
                                    const char                 *pszRemoteAddress,
                                    const unsigned int          uiPort )
{
    // Terminate and restablish any previous connection with the 6046 using the remote host info.

    ManageDllState_m();

    // Assume failure for now.

    bool bSuccess = false;

    // Kill off any pevious connections.

    Disconnect();

    // Connect to the server and then install the data and status callback handlers along with their optional parameters.

    m_pvConnection = new CConnection( this );

    if ( m_pvConnection != NULL )
    {
        CConnection *pConnection = Connection_m();
        ASSERT( pConnection != NULL );

        bSuccess = pConnection->Connect( pfnStatusCallback, vpStatusCallbackParam, pfnDataCallback, pvDataCallbackParam, pszRemoteAddress, uiPort );
    }

    if ( ! bSuccess )
    {
        TRACE( _T( "C6046RemoteClient::Connect(), Can't connect to client\n" ) );
        Disconnect();
    }

    return bSuccess;
}

bool C6046RemoteClient::Disconnect( void )
{
    ManageDllState_m();

    if ( m_pvConnection != NULL )
    {
        CConnection *pConnection = Connection_m();

        pConnection->Disconnect();

        delete pConnection;
        m_pvConnection = NULL;
    }

    return ( m_pvConnection == NULL );
}

bool C6046RemoteClient::Subscribe(  const int                   &riSensorIndex,
                                    const unsigned long         &rulRecordType )
{
    // Here we pass on the message/sensor subscription data to the 6046 for subsequent
    // message filtering. We therefore format the command and dispatch to the 6046

    ManageDllState_m();

    bool bSuccess = false;

    IF_CONNECTED
    {
        C7kCommand Command;

        if ( Command.Encode( C7kCommand::sensorIndexPLC,
                             C7kCommand::commandSubscribe,
                             C7kCommand::commandActionSet,
                             false,
                             "%d, %lu", riSensorIndex, rulRecordType ) )
        {
            bSuccess = Send7kRecord_m( Command );
        }
    }

    return bSuccess;
}

bool C6046RemoteClient::Unsubscribe(    const int           &riSensorIndex,
                                        const unsigned long &rulRecordType )
{
    ManageDllState_m();

    bool bSuccess = false;

    IF_CONNECTED
    {
        C7kCommand Command;

        if ( Command.Encode( C7kCommand::sensorIndexPLC,
                             C7kCommand::commandUnsubscribe,
                             C7kCommand::commandActionSet,
                             false,
                             "%d, %lu", riSensorIndex, rulRecordType ) )
        {
            bSuccess = Send7kRecord_m( Command );
        }
    }

    return bSuccess;
}

bool C6046RemoteClient::IsConnectionAlive( void )
{
    // If the connection is established, query the PLC to see if its responsive.

    ManageDllState_m();

    CConnection *pConnection = Connection_m();

    if ( pConnection != NULL )
    {
        return pConnection->IsAlive();
    }

    return false;
}

bool C6046RemoteClient::AliveMonitor( const bool &rbEnable )
{
    ManageDllState_m();

    CConnection *pConnection = Connection_m();

    if ( pConnection != NULL )
    {
        pConnection->AliveMonitoring( rbEnable );
        return true;
    }

    return false;
}

C6046RemoteClient::E6046ERRORCODE C6046RemoteClient::GetLastError( void ) const
{
    ManageDllState_m();
    return m_eLastErrorCode;
}

void C6046RemoteClient::SetError( const E6046ERRORCODE &reError )
{
    ManageDllState_m();
    m_eLastErrorCode = reError;
}

///////////////////////////////////////////////////////////////////////////////
// 6046 command and control.

bool C6046RemoteClient::Logging( const bool &rbEnable )
{
    ManageDllState_m();

    bool bSuccess = false;

    IF_CONNECTED
    {
        C7kCommand Command;

        if ( Command.Encode( C7kCommand::sensorIndexPLC,
                             C7kCommand::commandLogging,
                             C7kCommand::commandActionSet,
                             false,
                             "%d", (int) rbEnable ) )
        {
            bSuccess = Send7kRecord_m( Command );
        }
    }

    return bSuccess;
}

bool C6046RemoteClient::SwitchFileName( const char *pszFileName )
{
    ManageDllState_m();

    bool bSuccess = false;

    IF_CONNECTED
    {
        C7kCommand Command;

        CString FileName( ( pszFileName == NULL )? _T( "Auto" ) : pszFileName );

        if ( FileName.IsEmpty() )
        {
            FileName = _T( "Auto" );
        }

        if ( Command.Encode( C7kCommand::sensorIndexPLC,
                             C7kCommand::commandSwitch,
                             C7kCommand::commandActionSet,
                             false,
                             "%s", (LPCTSTR) FileName ) )
        {
            bSuccess = Send7kRecord_m( Command );
        }
    }

    return bSuccess;
}

bool C6046RemoteClient::SetPath( const char *pszPath )
{
    ManageDllState_m();

    bool bSuccess = false;

    if ( pszPath != NULL )
    {
        IF_CONNECTED
        {
            C7kCommand Command;

            if ( Command.Encode( C7kCommand::sensorIndexPLC,
                                 C7kCommand::commandPath,
                                 C7kCommand::commandActionSet,
                                 false,
                                 "%s", pszPath ) )
            {
                bSuccess = Send7kRecord_m( Command );
            }
        }
    }

    return bSuccess;
}

bool C6046RemoteClient::Version( void )
{
    ManageDllState_m();

    bool bSuccess = false;

    IF_CONNECTED
    {
        C7kCommand Command;

        if ( Command.Encode( C7kCommand::sensorIndexPLC,
                             C7kCommand::commandVersion,
                             C7kCommand::commandActionGet,
                             false,
                             NULL ) )
        {
            bSuccess = Send7kRecord_m( Command );
        }
    }

    return bSuccess;
}

bool C6046RemoteClient::Load( void )
{
    ManageDllState_m();

    bool bSuccess = false;

    IF_CONNECTED
    {
        C7kCommand Command;

        if ( Command.Encode( C7kCommand::sensorIndexPLC,
                             C7kCommand::commandLoad,
                             C7kCommand::commandActionGet,
                             false,
                             NULL ) )
        {
            bSuccess = Send7kRecord_m( Command );
        }
    }

    return bSuccess;
}

bool C6046RemoteClient::Save( void )
{
    ManageDllState_m();

    bool bSuccess = false;

    IF_CONNECTED
    {
        C7kCommand Command;

        if ( Command.Encode( C7kCommand::sensorIndexPLC,
                             C7kCommand::commandSave,
                             C7kCommand::commandActionGet,
                             false,
                             NULL ) )
        {
            bSuccess = Send7kRecord_m( Command );
        }
    }

    return bSuccess;
}

bool C6046RemoteClient::Alarm(  const int          &riAlarmId,
                                const EALARMACTION &reAlarmAction )
{
    ManageDllState_m();

    bool bSuccess = false;

    IF_CONNECTED
    {
        C7kCommand Command;

        if ( Command.Encode( C7kCommand::sensorIndexPLC,
                             C7kCommand::commandAlarm,
                             C7kCommand::commandActionSet,
                             false,
                             "%d, %d", riAlarmId, reAlarmAction ) )
        {
            bSuccess = Send7kRecord_m( Command );
        }
    }

    return bSuccess;
}

bool C6046RemoteClient::Status( const int &riSensorIndex )
{
    ManageDllState_m();

    bool bSuccess = false;

    IF_CONNECTED
    {
        C7kCommand Command;

        if ( Command.Encode( C7kCommand::sensorIndexPLC,
                             C7kCommand::commandStatus,
                             C7kCommand::commandActionGet,
                             false,
                             "%d", riSensorIndex ) )
        {
            bSuccess = Send7kRecord_m( Command );
        }
    }

    return bSuccess;
}

bool C6046RemoteClient::Verbose( const bool &rbEnable )
{
    ManageDllState_m();

    bool bSuccess = false;

    IF_CONNECTED
    {
        C7kCommand Command;

        if ( Command.Encode( C7kCommand::sensorIndexPLC,
                             C7kCommand::commandVerbose,
                             C7kCommand::commandActionSet,
                             false,
                             "%d", rbEnable ) )
        {
            bSuccess = Send7kRecord_m( Command );
        }
    }

    return bSuccess;
}

bool C6046RemoteClient::Reset( const int &riSensorIndex )
{
    ManageDllState_m();

    bool bSuccess = false;

    IF_CONNECTED
    {
        C7kCommand Command;

        if ( Command.Encode( riSensorIndex,
                             C7kCommand::commandReset,
                             C7kCommand::commandActionSet,
                             false,
                             NULL ) )
        {
            bSuccess = Send7kRecord_m( Command );
        }
    }

    return bSuccess;
}

bool C6046RemoteClient::Shutdown( const int &riMode )
{
    ManageDllState_m();

    bool bSuccess = false;

    IF_CONNECTED
    {
        C7kCommand Command;

        if ( Command.Encode( C7kCommand::sensorIndexPLC,
                             C7kCommand::commandShutdown,
                             C7kCommand::commandActionSet,
                             false,
                             "%d", riMode ) )
        {
            bSuccess = Send7kRecord_m( Command );
        }
    }

    return bSuccess;
}

bool C6046RemoteClient::SyncToTime( const SYSTEMTIME &rsSystemTime )
{
    ManageDllState_m();

    bool bSuccess = false;

    IF_CONNECTED
    {
        C7kCommand Command;

        CString FormattedTime;

        FormattedTime.Format( "%04u/%02u/%02u,%02u:%02u:%8.5f", rsSystemTime.wYear,
                                                                rsSystemTime.wMonth,
                                                                rsSystemTime.wDay,
                                                                rsSystemTime.wHour,
                                                                rsSystemTime.wMinute,
                                                                static_cast<float>( rsSystemTime.wSecond ) + 
                                                                    0.001f * static_cast<float>( rsSystemTime.wMilliseconds ) );

        if ( Command.Encode( C7kCommand::sensorIndexPLC,
                             C7kCommand::commandTimeSync,
                             C7kCommand::commandActionSet,
                             false,
                             FormattedTime ) )
        {
            bSuccess = Send7kRecord_m( Command );
        }
    }

    return bSuccess;
}

bool C6046RemoteClient::Modules( void )
{
    ManageDllState_m();

    bool bSuccess = false;

    IF_CONNECTED
    {
        C7kCommand Command;

        if ( Command.Encode( C7kCommand::sensorIndexPLC,
                             C7kCommand::commandModules,
                             C7kCommand::commandActionGet,
                             false,
                             NULL ) )
        {
            bSuccess = Send7kRecord_m( Command );
        }
    }

    return bSuccess;
}

bool C6046RemoteClient::RunList( void )
{
    ManageDllState_m();

    bool bSuccess = false;

    IF_CONNECTED
    {
        C7kCommand Command;

        if ( Command.Encode( C7kCommand::sensorIndexPLC,
                             C7kCommand::commandRunList,
                             C7kCommand::commandActionGet,
                             false,
                             NULL ) )
        {
            bSuccess = Send7kRecord_m( Command );
        }
    }

    return bSuccess;
}

bool C6046RemoteClient::Add(    const int  &riModuleNumber,
                                const int  &riSubsystemId,
                                const int  &riPortNumber,
                                LPCTSTR     pszName  )
{
    ManageDllState_m();

    bool bSuccess = false;

    IF_CONNECTED
    {
        C7kCommand Command;

        if ( Command.Encode( C7kCommand::sensorIndexPLC,
                             C7kCommand::commandAdd,
                             C7kCommand::commandActionSet,
                             false,
                             "%d, %d, %d, %s", riModuleNumber, riSubsystemId, riPortNumber, pszName ) )
        {
            bSuccess = Send7kRecord_m( Command );
        }
    }

    return bSuccess;
}

bool C6046RemoteClient::Remove( const int &riSensorIndex )
{
    ManageDllState_m();

    bool bSuccess = false;

    IF_CONNECTED
    {
        C7kCommand Command;

        if ( Command.Encode( C7kCommand::sensorIndexPLC,
                             C7kCommand::commandRemove,
                             C7kCommand::commandActionSet,
                             false,
                             "%d", riSensorIndex ) )
        {
            bSuccess = Send7kRecord_m( Command );
        }
    }

    return bSuccess;
}

bool C6046RemoteClient::Report( void )
{
    ManageDllState_m();

    bool bSuccess = false;

    IF_CONNECTED
    {
        C7kCommand Command;

        if ( Command.Encode( C7kCommand::sensorIndexPLC,
                             C7kCommand::commandReport,
                             C7kCommand::commandActionGet,
                             false,
                             NULL ) )
        {
            bSuccess = Send7kRecord_m( Command );
        }
    }

    return bSuccess;
}

bool C6046RemoteClient::Port(   const int &riSensorIndex,
                                const int &riPort )
{
    ManageDllState_m();

    bool bSuccess = false;

    IF_CONNECTED
    {
        C7kCommand Command;

        if ( Command.Encode( C7kCommand::sensorIndexPLC,
                             C7kCommand::commandPort,
                             C7kCommand::commandActionSet,
                             false,
                             "%d, %d", riSensorIndex, riPort ) )
        {
            bSuccess = Send7kRecord_m( Command );
        }
    }

    return bSuccess;
}

bool C6046RemoteClient::Health( void )
{
    ManageDllState_m();

    bool bSuccess = false;

    IF_CONNECTED
    {
        C7kCommand Command;

        if ( Command.Encode( C7kCommand::sensorIndexPLC,
                             C7kCommand::commandHealth,
                             C7kCommand::commandActionGet,
                             false,
                             NULL ) )
        {
            bSuccess = Send7kRecord_m( Command );
        }
    }

    return bSuccess;
}

bool C6046RemoteClient::AutoStart( const DWORD &rdwMode )
{
    ManageDllState_m();

    bool bSuccess = false;

    IF_CONNECTED
    {
        C7kCommand Command;

        if ( Command.Encode( C7kCommand::sensorIndexPLC,
                             C7kCommand::commandAutoStart,
                             C7kCommand::commandActionSet,
                             false,
                             "%lu", rdwMode ) )
        {
            bSuccess = Send7kRecord_m( Command );
        }
    }

    return bSuccess;

}

bool C6046RemoteClient::AutoHealthCheck(    const bool          &rbEnable,
                                            const unsigned long &rulPeriodMilliSeconds )
{
    ManageDllState_m();

    bool bSuccess = false;

    IF_CONNECTED
    {
        C7kCommand Command;

        if ( Command.Encode( C7kCommand::sensorIndexPLC,
                             C7kCommand::commandAutoHealthCheck,
                             C7kCommand::commandActionSet,
                             false,
                             "%d, %lu", static_cast<int>( rbEnable ? 1 : 0 ), rulPeriodMilliSeconds ) )
        {
            bSuccess = Send7kRecord_m( Command );
        }
    }

    return bSuccess;
}

bool C6046RemoteClient::RouteMessages( const int &riRoutingSensorIndex )
{
    ManageDllState_m();

    bool bSuccess = false;

    IF_CONNECTED
    {
        C7kCommand Command;
        
        if ( Command.Encode( C7kCommand::sensorIndexPLC,
                             C7kCommand::commandRouteMessages,
                             C7kCommand::commandActionSet,
                             false,
                             "%d", riRoutingSensorIndex ) )
        {
            bSuccess = Send7kRecord_m( Command );
        }
    }

    return bSuccess;
}

bool C6046RemoteClient::ReportAlarms( void )
{
    ManageDllState_m();

    bool bSuccess = false;

    IF_CONNECTED
    {
        C7kCommand  Command;
        
        if ( Command.Encode( C7kCommand::sensorIndexPLC,
                             C7kCommand::commandReportAlarms,
                             C7kCommand::commandActionSet,
                             false,
                             NULL ) )
        {
            bSuccess = Send7kRecord_m( Command );
        }
    }

    return bSuccess;
}

bool C6046RemoteClient::Send7kMessage(  const BYTE           *pby7kRecord,
                                        const unsigned long  &rulBytes )
{
    ManageDllState_m();

    bool bSuccess = false;

    IF_CONNECTED
    {
        bSuccess = Send7kRecord( pby7kRecord, rulBytes );
    }

    return bSuccess;
}

bool C6046RemoteClient::LoggingSetup( void )
{
    ManageDllState_m();

    bool bSuccess = false;

    IF_CONNECTED
    {
        C7kCommand Command;
        
        if ( Command.Encode( C7kCommand::sensorIndexPLC,
                             C7kCommand::commandLoggingSetup,
                             C7kCommand::commandActionGet,
                             false,
                             NULL ) )
        {
            bSuccess = Send7kRecord_m( Command );
        }
    }

    return bSuccess;
}

bool C6046RemoteClient::LoggingSetup(   const unsigned long &rulMaxFileSize,
                                        const unsigned long &rulRecordOverlap )
{
    ManageDllState_m();

    bool bSuccess = false;

    IF_CONNECTED
    {
        C7kCommand Command;
        
        if ( Command.Encode( C7kCommand::sensorIndexPLC,
                             C7kCommand::commandLoggingSetup,
                             C7kCommand::commandActionSet,
                             false,
                             "%lu, %lu",
                             rulMaxFileSize,
                             rulRecordOverlap ) )
        {
            bSuccess = Send7kRecord_m( Command );
        }
    }

    return bSuccess;
}

///////////////////////////////////////////////////////////////////////////////
// Sensor commands.

bool C6046RemoteClient::SetRange( const int   &riSensorIndex,
                                  const float &rfRange )
{
    ManageDllState_m();

    bool bSuccess = false;

    IF_CONNECTED
    {
        C7kCommand Command;

        if ( Command.Encode( riSensorIndex,
                             C7kCommand::commandGeneric,
                             C7kCommand::commandActionSet,
                             false,
                             "Range = %.2f", rfRange ) )
        {
            bSuccess = Send7kRecord_m( Command );
        }
    }

    return bSuccess;
}

bool C6046RemoteClient::GenericCommand( const int &riSensorIndex,
                                        char      *pszString )
{
    ManageDllState_m();

    bool bSuccess = false;

    if ( ( pszString != NULL ) && ( ::strlen( pszString ) > 0 ) )
    {
        IF_CONNECTED
        {
            C7kCommand Command;

            if ( Command.Encode( riSensorIndex,
                                 C7kCommand::commandGeneric,
                                 C7kCommand::commandActionUnspecified,
                                 false,
                                 "%s", pszString ) )
            {
                bSuccess = Send7kRecord_m( Command );
            }
        }
    }

    return bSuccess;
}

bool C6046RemoteClient::SetPulse(   const int   &riSensorIndex,
                                    const char  *pszPulseFileName )
{
    ManageDllState_m();

    bool bSuccess = false;

    if ( ( pszPulseFileName != NULL ) && ( strlen( pszPulseFileName ) > 0 ) )
    {
        IF_CONNECTED
        {
            C7kCommand Command;

            if ( Command.Encode( riSensorIndex,
                                 C7kCommand::commandGeneric,
                                 C7kCommand::commandActionSet,
                                 false,
                                 "Pulse = %s", pszPulseFileName ) )
            {
                bSuccess = Send7kRecord_m( Command );
            }
        }
    }

    return bSuccess;
}

bool C6046RemoteClient::PingEnable( const int   &riSensorIndex,
                                    const bool  &rbEnable )
{
    ManageDllState_m();

    bool bSuccess = false;

    IF_CONNECTED
    {
        C7kCommand Command;

        if ( Command.Encode( riSensorIndex,
                             C7kCommand::commandGeneric,
                             C7kCommand::commandActionSet,
                             false,
                             "Ping = %d", rbEnable ) )
        {
            bSuccess = Send7kRecord_m( Command );
        }
    }

    return bSuccess;
}

bool C6046RemoteClient::TxPower(    const int   &riSensorIndex,
                                    const float &rfPercentage )
{
    ManageDllState_m();

    bool bSuccess = false;

    IF_CONNECTED
    {
        C7kCommand Command;

        if ( Command.Encode( riSensorIndex,
                             C7kCommand::commandGeneric,
                             C7kCommand::commandActionSet,
                             false,
                             "TxPower = %.2f", rfPercentage ) )
        {
            bSuccess = Send7kRecord_m( Command );
        }
    }

    return bSuccess;
}

bool C6046RemoteClient::RxGain(     const int   &riSensorIndex,
                                    const int   &riRxGain )
{
    ManageDllState_m();

    bool bSuccess = false;

    IF_CONNECTED
    {
        C7kCommand Command;

        if ( Command.Encode( riSensorIndex,
                             C7kCommand::commandGeneric,
                             C7kCommand::commandActionSet,
                             false,
                             "RxGain = %d", riRxGain ) )
        {
            bSuccess = Send7kRecord_m( Command );
        }
    }

    return bSuccess;
}

bool C6046RemoteClient::SetDuration(    const int   &riSensorIndex,
                                        const float &rfDurationInMilliseconds )
{
    ManageDllState_m();

    bool bSuccess = false;

    IF_CONNECTED
    {
        C7kCommand Command;

        if ( Command.Encode( riSensorIndex,
                             C7kCommand::commandGeneric,
                             C7kCommand::commandActionSet,
                             false,
                             "Duration = %.2f", rfDurationInMilliseconds ) )
        {
            bSuccess = Send7kRecord_m( Command );
        }
    }

    return bSuccess;
}

bool C6046RemoteClient::GetPulseFiles(  const int &riSensorIndex )
{
    ManageDllState_m();

    bool bSuccess = false;

    IF_CONNECTED
    {
        C7kCommand Command;

        if ( Command.Encode( riSensorIndex,
                             C7kCommand::commandGeneric,
                             C7kCommand::commandActionSet,
                             false,
                             "PulseFiles" ) )
        {
            bSuccess = Send7kRecord_m( Command );
        }
    }

    return bSuccess;
}

bool C6046RemoteClient::SetDefaultState( const int &riSensorIndex )
{
    ManageDllState_m();

    bool bSuccess = false;

    IF_CONNECTED
    {
        C7kCommand Command;

        if ( Command.Encode( riSensorIndex,
                             C7kCommand::commandGeneric,
                             C7kCommand::commandActionSet,
                             false,
                             "DefaultState" ) )
        {
            bSuccess = Send7kRecord_m( Command );
        }
    }

    return bSuccess;
}

bool C6046RemoteClient::QueryParameters( const int  &riSensorIndex )
{
    ManageDllState_m();

    bool bSuccess = false;

    IF_CONNECTED
    {
        C7kCommand Command;

        if ( Command.Encode( riSensorIndex,
                             C7kCommand::commandGeneric,
                             C7kCommand::commandActionGet,
                             false,
                             "QueryParameters" ) )
        {
            bSuccess = Send7kRecord_m( Command );
        }
    }

    return bSuccess;
}

bool C6046RemoteClient::QueryCapabilities( const int &riSensorIndex )
{
    ManageDllState_m();

    bool bSuccess = false;

    IF_CONNECTED
    {
        C7kCommand Command;

        if ( Command.Encode( riSensorIndex,
                             C7kCommand::commandGeneric,
                             C7kCommand::commandActionGet,
                             false,
                             "QueryCapabilities" ) )
        {
            bSuccess = Send7kRecord_m( Command );
        }
    }

    return bSuccess;
}

bool C6046RemoteClient::QueryRemoteSettings( const int &riSensorIndex )
{
    ManageDllState_m();

    bool bSuccess = false;

    IF_CONNECTED
    {
        C7kCommand Command;

        if ( Command.Encode( riSensorIndex,
                             C7kCommand::commandGeneric,
                             C7kCommand::commandActionGet,
                             false,
                             "QueryRemoteSettings" ) )
        {
            bSuccess = Send7kRecord_m( Command );
        }
    }

    return bSuccess;
}

bool C6046RemoteClient::PulseWidth( const int   &riSensorIndex,
                                    const float &rfPulseWidth )
{
    ManageDllState_m();

    bool bSuccess = false;

    IF_CONNECTED
    {
        C7kCommand Command;

        if ( Command.Encode( riSensorIndex,
                             C7kCommand::commandGeneric,
                             C7kCommand::commandActionGet,
                             false,
                             "PulseWidth = %.6f", rfPulseWidth ) )
        {
            bSuccess = Send7kRecord_m( Command );
        }
    }

    return bSuccess;
}

bool C6046RemoteClient::RangeDepthFilters(  const int   &riSensorIndex,
                                            const float &rfMinRange,
                                            const float &rfMaxRange,
                                            const float &rfMinDepth,
                                            const float &rfMaxDepth )
{
    ManageDllState_m();

    bool bSuccess = false;

    IF_CONNECTED
    {
        C7kCommand Command;

        if ( Command.Encode( riSensorIndex,
                             C7kCommand::commandGeneric,
                             C7kCommand::commandActionGet,
                             false,
                             "RangeDepthFilters = %.2f, %.2f, %.2f, %.2f", rfMinRange, rfMaxRange, rfMinDepth, rfMaxDepth ) )
        {
            bSuccess = Send7kRecord_m( Command );
        }
    }

    return bSuccess;
}

bool C6046RemoteClient::BottomDetectFlags(  const int           &riSensorIndex,
                                            const unsigned long &rulBottomDetectFlags )
{
    ManageDllState_m();

    bool bSuccess = false;

    IF_CONNECTED
    {
        C7kCommand Command;

        if ( Command.Encode( riSensorIndex,
                             C7kCommand::commandGeneric,
                             C7kCommand::commandActionGet,
                             false,
                             "BottomDetectFlags = %lu", rulBottomDetectFlags ) )
        {
            bSuccess = Send7kRecord_m( Command );
        }
    }

    return bSuccess;
}

bool C6046RemoteClient::MaxPingRate(    const int   &riSensorIndex,
                                        const float &rfMaxPingRate )
{
    ManageDllState_m();

    bool bSuccess = false;

    IF_CONNECTED
    {
        C7kCommand Command;

        if ( Command.Encode( riSensorIndex,
                             C7kCommand::commandGeneric,
                             C7kCommand::commandActionGet,
                             false,
                             "MaxPingRate = %.2f", rfMaxPingRate ) )
        {
            bSuccess = Send7kRecord_m( Command );
        }
    }

    return bSuccess;
}

bool C6046RemoteClient::AbsorptionCoefficient(  const int   &riSensorIndex,
                                                const float &rfAbsorptionCoefficient )
{
    ManageDllState_m();

    bool bSuccess = false;

    IF_CONNECTED
    {
        C7kCommand Command;

        if ( Command.Encode( riSensorIndex,
                             C7kCommand::commandGeneric,
                             C7kCommand::commandActionGet,
                             false,
                             "AbsorptionCoef = %.2f", rfAbsorptionCoefficient ) )
        {
            bSuccess = Send7kRecord_m( Command );
        }
    }

    return bSuccess;
}

bool C6046RemoteClient::SoundSpeed( const int   &riSensorIndex,
                                    const float &rfSoundSpeed )
{
    ManageDllState_m();

    bool bSuccess = false;

    IF_CONNECTED
    {
        C7kCommand Command;

        if ( Command.Encode( riSensorIndex,
                             C7kCommand::commandGeneric,
                             C7kCommand::commandActionGet,
                             false,
                             "SoundSpeed = %.2f", rfSoundSpeed ) )
        {
            bSuccess = Send7kRecord_m( Command );
        }
    }

    return bSuccess;
}

bool C6046RemoteClient::SpreadingLoss(  const int   &riSensorIndex,
                                        const float &rfSpreadingLoss )
{
    ManageDllState_m();

    bool bSuccess = false;

    IF_CONNECTED
    {
        C7kCommand Command;

        if ( Command.Encode( riSensorIndex,
                             C7kCommand::commandGeneric,
                             C7kCommand::commandActionGet,
                             false,
                             "SpreadingLoss = %.2f", rfSpreadingLoss ) )
        {
            bSuccess = Send7kRecord_m( Command );
        }
    }

    return bSuccess;
}

bool C6046RemoteClient::SimulateCommand(    const unsigned long    &rulCommandId,
                                            const char             *pszOptions, 
                                                                    ... )
{
    ManageDllState_m();

    bool bSuccess = false;

    IF_CONNECTED
    {
        CString Options;
        CString FullCommand;
        va_list pArg;

        va_start( pArg, pszOptions );
        Options.FormatV( pszOptions, pArg );
        va_end( pArg );

        FullCommand.Format( "%lu", rulCommandId );

        if ( ! Options.IsEmpty() )
        {
            FullCommand += ( CString( _T( ", " ) ) + Options );
        }

        C7kCommand Command;

        if ( Command.Encode( C7kCommand::sensorIndexPLC,
                                C7kCommand::commandSimulate,
                                    C7kCommand::commandActionSet,
                                        false,
                                            FullCommand ) )
        {
            bSuccess = Send7kRecord_m( Command );
        }
    }

    return bSuccess;
}

bool C6046RemoteClient::EmitTime( const bool &rbEmitTime )
{
    ManageDllState_m();

    bool bSuccess = false;

    IF_CONNECTED
    {
        C7kCommand  Command;

        if ( Command.Encode( C7kCommand::sensorIndexPLC,
                                C7kCommand::commandTimeMessage,
                                    C7kCommand::commandActionSet,
                                        false,
                                            "%lu",
                                                rbEmitTime ? 1UL : 0UL ) )
        {
            bSuccess = Send7kRecord_m( Command );
        }
    }

    return bSuccess;
}

///////////////////////////////////////////////////////////////////////////////
// Internal helper functions (private services).

inline
bool C6046RemoteClient::Send7kRecord( const BYTE *pby7kRecord, const unsigned long &rulBytes )
{
    bool bSuccess = false;      // Assume failure for now.

    IF_CONNECTED
    {
        if ( rulBytes == 0UL )
        {
            bSuccess = true;
        }
        else if ( C7kProtocol::IsValid7kRecord( pby7kRecord, rulBytes ) )
        {
            bSuccess = pConnection->Send( const_cast<BYTE *>( pby7kRecord ), rulBytes );

            if ( ! bSuccess )
            {
                TRACE( _T( "C6046RemoteClient::Send7kRecord_m(), pConnection->Send() failed\n" ) );
            }
        }
        else
        {
            bSuccess = false;
            m_eLastErrorCode = errorInvalid7kRecord;
            TRACE( _T( "C6046RemoteClient::Send7kRecord_m(), pby7kRecord is invalid.\n" ) );
        }
    }

    return bSuccess;
}

////////////////////////////////////////////////////////////////////////
// CConnection internal helper.

inline
CConnection::CConnection          ( C6046RemoteClient *const     pRemoteClient,
                                    const unsigned long         &rulAliveMonitorPeriod )

            :CBaseThread          ( rulAliveMonitorPeriod ),

             m_ulMaxMessageLength ( ulMaxEdgeTechDynamicDataSize_c ),
             m_iMaxFailCount      ( 3 )
{
    m_bIsAlive              = false;
    m_bMonitorAliveStatus   = false;
    m_bIsAliveReplyPending  = false;

    m_iAliveFailCount       = 0;

    m_pfnStatusCallback     = NULL;
    m_pfnDataCallback       = NULL;

    m_pvParamStatusCallback = NULL;
    m_pvParamDataCallback   = NULL;

    m_pParent               = pRemoteClient;

    m_pConnection           = NULL;

    if ( CBaseThread::ResumeThread() == CBaseThread::m_dwResumeFailCode )
    {
        TRACE( _T( "CConnection::CConnection(), CBaseThread::ResumeThread() failed.\n" ) );
    }
}

inline
CConnection::~CConnection( void )
{
    __TRY
    {
        if ( m_pConnection != NULL )
        {
            delete m_pConnection;
            m_pConnection = NULL;
        }
    }
    __FINALLY
    {
        ASSERT( m_pConnection == NULL );
        m_pConnection           = NULL;

        m_pParent               = NULL;

        m_bIsAlive              = FALSE;
        m_bMonitorAliveStatus   = false;
        m_bIsAliveReplyPending  = false;

        m_iAliveFailCount       = 0;

        m_pfnStatusCallback     = NULL;
        m_pvParamStatusCallback = NULL;
        m_pfnDataCallback       = NULL;
        m_pvParamDataCallback   = NULL;
    }
    __ENDFINALLY
}

inline
bool CConnection::Connect(  PFN_REMOTECLIENT_CALLBACK   pfnStatusCallback,
                            void                       *vpStatusCallbackParam,
                            PFN_REMOTECLIENT_CALLBACK   pfnDataCallback,
                            void                       *pvDataCallbackParam,
                            const char                 *pszRemoteAddress,
                            const unsigned int          uiPort )
{
    bool bSuccess = false;

    try
    {
        if ( ( m_pConnection = new CClientConnection(   SocketCallback,
                                                            this,
                                                                pszRemoteAddress,
                                                                    uiPort,
                                                                        m_ulMaxMessageLength,
                                                                            SOCK_STREAM ) ) != NULL )
        {
            if ( m_pConnection->IsInitialized() && m_pConnection->Connect( true ) )
            {
                m_pfnStatusCallback     = pfnStatusCallback;
                m_pvParamStatusCallback = vpStatusCallbackParam;

                m_pfnDataCallback       = pfnDataCallback;
                m_pvParamDataCallback   = pvDataCallbackParam;

                ASSERT( m_pParent != NULL );
                m_pParent->SetError( C6046RemoteClient::errorCodeNone );

                __TRY
                {
                    m_Critical.Enter();
                    m_iAliveFailCount = 0;
                }
                __FINALLY
                {
                    m_Critical.Leave();
                }
                __ENDFINALLY

                bSuccess = true;
            }
        }
    }
    catch ( ... )
    {
        bSuccess = false;
        TRACE( _T( "CConnection::Connect(), Unspecified exception caught.\n" ) );
    }

    return bSuccess;
}

inline
bool CConnection::Disconnect( void )
{
    bool bSuccess = false;

    __TRY
    {
        if ( m_pConnection != NULL )
        {
            delete m_pConnection;
            m_pConnection = NULL;
        }
    }
    __FINALLY
    {
        bSuccess = ( m_pConnection == NULL );
    }
    __ENDFINALLY

    return bSuccess;
}
     
inline
bool CConnection::Send( const BYTE          *pby7kRecord,
                        const unsigned long &rulBytes )
{
    bool bSuccess = false;          // Assume failure for now.

    if ( rulBytes == 0UL )
    {
        bSuccess = true;            // Nothing to do.
    }
    else if ( ( pby7kRecord != NULL ) && ( m_pConnection != NULL ) )
    {
        bSuccess = m_pConnection->Send( const_cast<BYTE *>( pby7kRecord ), rulBytes );
    }
    else
    {
        bSuccess = false;
        ASSERT( pby7kRecord != NULL );
        ASSERT( m_pConnection != NULL );
    }

    return bSuccess;
}

inline
void CConnection::AliveMonitoring( const bool &rbEnable )
{
    __TRY
    {
        m_Critical.Enter();
        m_bMonitorAliveStatus = rbEnable;
    }
    __FINALLY
    {
        m_Critical.Leave();
    }
    __ENDFINALLY
}

inline
bool CConnection::IsAlive( void ) const
{
    bool bIsAlive = true;

    __TRY
    {
        m_Critical.Enter();
        bIsAlive = m_bIsAlive;
    }
    __FINALLY
    {
        m_Critical.Leave();
    }
    __ENDFINALLY

    return bIsAlive;
}

void CConnection::WatchCycle( void )
{
    // Base class override for main thread processing.

    __TRY
    {
        const DWORD dwEventCount = 0UL;
        LPHANDLE    lpaHandles   = NULL;

        while ( IsActive() )
        {
            // Block this thread and wait for any significant event or message to occur.

            DWORD dwSignalState = ::MsgWaitForMultipleObjects(  dwEventCount,                   // Number of waitable kernel objects (events, timers etc) to wait for.
                                                                    lpaHandles,                 // Handle array.
                                                                        FALSE,                  // Wait for any one of the specified event types.
                                                                            m_dwDwellPeriod,    // Timeout processing period in ms; see constructor initializer list for CBaseThread().
                                                                                QS_ALLINPUT );  // Unblock on any message in the thread's queue.

            // The following priority scheme applies:
            //
            //  1) Termination request.
            //  2) Queued thread messages.
            //  3) Win32 kernel object events.
            //  4) Timeout processing (not relevant here).

            if ( IsInactive() )
            {
                break;
            }
            else if ( dwSignalState == ( WAIT_OBJECT_0 + dwEventCount ) )
            {
                PumpMessages();
            }
            else if ( ( dwEventCount  >  0 )             &&
                      ( dwSignalState >= WAIT_OBJECT_0 ) &&
                      ( dwSignalState <= ( WAIT_OBJECT_0 + dwEventCount - 1 ) ) )
            {
                // ProcessEvents( &ahEventList[ 0 ], dwSignalState - WAIT_OBJECT_0 );
            }
            else if ( dwSignalState == WAIT_TIMEOUT )
            {
                IsPLCAlive();
            }
            else
            {
                // Must be an abandoned mutex.  We shouldn't get here unless things are hideously pair shaped.

                TRACE( _T( "CConnection::WatchCycle(), Abandoned mutex !!!\n" ) );
                ASSERT( false );
            }
        }
    }
    __FINALLY
    {
        // Ensure we always attempt to clean up.
    }
    __ENDFINALLY
}

BOOL CConnection::ProcessMessage( MSG *psMsg )
{
    // Override for CBaseThread::ProcessMessage()

    BOOL bMessageProcessed = FALSE;

    switch ( psMsg->message )
    {
        case messageIsAliveCheck:                           // Specific "is alive" detection request received.

            IsPLCAlive( true );
            bMessageProcessed = TRUE;
            break;

        default:

            // Give the base class the chance to handle the message if it's not specifically handled above.

            bMessageProcessed = CBaseThread::ProcessMessage( psMsg );
            break;
    }

    return bMessageProcessed;
}

inline
void CConnection::IsPLCAlive( const bool &rbForceQuery )
{
    try
    {
        bool bNextQuery = false;
        bool bLinkDown  = false;

        __TRY
        {
            m_Critical.Enter();

            if ( rbForceQuery )
            {
                bNextQuery = true;
            }
            else if ( m_bMonitorAliveStatus )
            {
                if ( m_bIsAliveReplyPending )
                {
                    m_bIsAlive = false;

                    if ( m_iAliveFailCount++ >= m_iMaxFailCount )
                    {
                        bLinkDown = true;
                    }
                }
                else
                {
                    m_bIsAlive = true;
                    m_iAliveFailCount = 0;
                }

                bNextQuery = true;
            }
            else
            {
                bNextQuery = false;
            }
        }
        __FINALLY
        {
            m_Critical.Leave();
        }
        __ENDFINALLY

        if ( bLinkDown )
        {
            // Maximum of no replys exceeded therefore notify client that the link is down or the PLC is not
            // responding.

            ASSERT( m_pParent != NULL );

            if ( m_pParent != NULL )
            {
                m_pParent->SetError( C6046RemoteClient::errorConnectionFailed );
            }

            if ( m_pfnStatusCallback != NULL )
            {
                C7kAcknowlege IsAliveMsg;

                if ( IsAliveMsg.Encode( C7kAcknowlege::statusUnknown, C7kCommand::commandCommandIsAlive, -1, false ) )
                {
                    ( m_pfnStatusCallback )( m_pvParamStatusCallback, static_cast<BYTE *>( IsAliveMsg ), static_cast<unsigned long>( IsAliveMsg ) );
                }
            }
        }
        else if ( bNextQuery )
        {
            // Format the query and send to the PLC.

            C7kCommand Command;

            if ( ! Command.Encode(  C7kCommand::sensorIndexPLC,
                                        C7kCommand::commandCommandIsAlive,
                                            C7kCommand::commandActionGet,
                                                false,
                                                    NULL ) )
            {
                TRACE( _T( "CConnection::IsPLCAlive(), Command.Encode() failed.\n" ) );
            }
            else if ( ! Send( static_cast<BYTE *>( Command ), static_cast<unsigned long>( Command ) ) )
            {
                TRACE( _T( "CConnection::IsPLCAlive(), Send() failed.\n" ) );
            }

            __TRY
            {
                m_Critical.Enter();
                m_bIsAliveReplyPending = true;
            }
            __FINALLY
            {
                m_Critical.Leave();
            }
            __ENDFINALLY
        }
    }
    catch ( ... )
    {
        TRACE( _T( "CConnection::IsPLCAlive(), Unspecified exception caught.\n" ) );
    }
}

void CConnection::SocketCallback(   BYTE    *pbyData,                // Data pointer where relevant - can be NULL.
                                    PVOID    lpvParam,               // Optional parameter.
                                    ULONG    ulNumBytes,             // Number of data bytes received - can be zero.
                                    INT      iSocketIndex,           // Socket index (relevant to server end).
                                    ULONG    ulTimeStamp,            // Time stamp.
                                    INT      iResonForCallback )     // Reason for the callback.
{
    CConnection *pthis = reinterpret_cast<CConnection *>( lpvParam );

    if ( pthis == NULL )
    {
        TRACE( _T( "CConnection::SocketCallback(), pthis is NULL\n" ) );
        ASSERT( false );
    }
    else
    {
        switch ( iResonForCallback )
        {
            case callbackConnect:
                break;

            case callbackDisconnect:
                break;

            case callbackData:

                pthis->HandleCallback( pbyData, ulNumBytes, iSocketIndex, ulTimeStamp );
                break;

            default:

                TRACE( _T( "CConnection::SocketCallback(), Unrecognized reason for callback\n" ) );
                break;
        }
    }
}

void CConnection::HandleCallback(   BYTE           *pbyData,
                                    unsigned long   ulNumBytes,
                                    int             iSocketIndex,
                                    unsigned long   ulTimeStamp )
{
    // Decode the message and determine whether its a status, alarm or data message and
    // then invoke the approriate callback to the client.

    UNREFERENCED_PARAMETER( ulTimeStamp );
    UNREFERENCED_PARAMETER( iSocketIndex );

    try
    {
        // Decode inbound message to determine its type.

        EMESSAGETYPE eMessageType = MessageType( pbyData, ulNumBytes );

        // Preprocess the message and ignore thereafter if necessary.

        if ( PreprocessMessage( eMessageType, pbyData, ulNumBytes ) )
        {
            return;
        }

        switch ( eMessageType )
        {
            case messageTypeSubscribed:

                if ( m_pfnDataCallback != NULL )
                {
                    ( m_pfnDataCallback )( m_pvParamDataCallback, pbyData, ulNumBytes );
                }

                break;

            case messageTypeStatus:
            case messageTypeAlarm:
            case messageTypeAckNak:

                if ( m_pfnStatusCallback != NULL )
                {
                    ( m_pfnStatusCallback )( m_pvParamStatusCallback, pbyData, ulNumBytes );
                }

                break;

            default:

                TRACE( _T( "CConnection::HandleCallback(), unhandled message type: %d\n" ), eMessageType );
                break;
        }
    }
    catch ( ... )
    {
        TRACE( _T( "CConnection::HandleCallback(), exception caught !!!\n" ) );
    }
}

CConnection::EMESSAGETYPE CConnection::MessageType( const BYTE          *pbyData,
                                                    const unsigned long &rulBytes ) const
{
    EMESSAGETYPE eMessageType = messageTypeUnknown;

    if ( ! C7kProtocol::IsValid7kRecord( pbyData, rulBytes ) )
    {
        TRACE( _T( "CConnection::MessageType(), \n" ) );
    }
    else
    {
        const RECORDHEADER7K * const psHeader = reinterpret_cast<const RECORDHEADER7K *>( pbyData );

        switch ( psHeader->m_ulRecordType )
        {
            case 11000UL:                               // Command

                // Suppress command messages back to the host.

                eMessageType = messageTypeUnknown;
                break;

            case 11001UL:                               // Ack/Nak

                eMessageType = messageTypeAckNak;
                break;

            case 11002UL:                               // Alarm or status.

                {
                    C7kStatus::EMESSAGETYPE e7kStatusType;
                    C7kStatus::EMODE        e7kStatusMode;
                    int                     iMessageId;

                    C7kStatus               StatusOrAlarm;

                    StatusOrAlarm.Decode( const_cast<BYTE *>( pbyData ), rulBytes, false, e7kStatusType, e7kStatusMode, iMessageId );

                    switch ( e7kStatusType )
                    {
                        case C7kStatus::messageTypeStatus:

                            eMessageType = messageTypeStatus;
                            break;

                        case C7kStatus::messageTypeAlarm:
                        
                            eMessageType = messageTypeAlarm;
                            break;

                        default:

                            eMessageType = messageTypeUnknown;
                            break;
                    }
                }

                break;

            default:

                // All other messages should be subscriptions.

                eMessageType = messageTypeSubscribed;
                break;
        }
    }

    return eMessageType;
}

bool CConnection::PreprocessMessage(    const EMESSAGETYPE  &reMessageType,
                                        const BYTE          *pbyData,
                                        const unsigned long &rulBytes )
{
    bool bIgnoreMessage = false;

    switch ( reMessageType )
    {
        case messageTypeAckNak:

            {
                C7kAcknowlege          Reply;
                C7kAcknowlege::ESTATUS eStatus     = C7kAcknowlege::statusUnknown;
                unsigned long          ulCommandId = 0UL;
                int                    iSensorId   = 0;

                if ( Reply.Decode( const_cast<BYTE *>( pbyData ), rulBytes, false, eStatus, ulCommandId, iSensorId ) )
                {
                    if ( ulCommandId == static_cast<unsigned long>( C7kCommand::commandCommandIsAlive ) )
                    {
                        bIgnoreMessage         = true;

                        __TRY
                        {
                            m_Critical.Enter();

                            m_bIsAliveReplyPending = false;

                            if ( m_bMonitorAliveStatus )
                            {
                                m_bIsAlive = ( eStatus == C7kAcknowlege::statusAccepted );
                            }
                        }
                        __FINALLY
                        {
                            m_Critical.Leave();
                        }
                        __ENDFINALLY
                    }
                }
            }

            break;

        default:
            break;
    }

    return bIgnoreMessage;
}

