// RxDlg.cpp : implementation file
//
#include "stdafx.h"
#include "arcview.h"
#include "RxDlg.h"
//
#include "Globals.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CRxDlg dialog


CRxDlg::CRxDlg(CWnd* pParent /*=NULL*/)
	: CDialog(CRxDlg::IDD, pParent)
{
	//{{AFX_DATA_INIT(CRxDlg)
	//}}AFX_DATA_INIT
}


void CRxDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
	//{{AFX_DATA_MAP(CRxDlg)
	DDX_Control(pDX, IDC_SEQERRORS, m_SeqErrors);
	DDX_Control(pDX, IDC_VERIFY_CHKSUM, m_VerifyChksum);
	DDX_Control(pDX, IDC_SEQCHECK, m_SequenceCheck);
	DDX_Control(pDX, IDC_RXALL, m_RxAll);
	DDX_Control(pDX, IDC_ECHO, m_Echo);
	DDX_Control(pDX, IDC_DISKLOG, m_DiskLog);
	DDX_Control(pDX, IDC_RESET, m_Reset);
	DDX_Control(pDX, IDC_STOP, m_Stop);
	DDX_Control(pDX, IDC_START, m_Start);
	DDX_Control(pDX, IDC_ERRCOUNT, m_ErrCount);
	DDX_Control(pDX, IDC_RXCOUNT, m_RxCount);
	//}}AFX_DATA_MAP
}


BEGIN_MESSAGE_MAP(CRxDlg, CDialog)
	//{{AFX_MSG_MAP(CRxDlg)
	ON_BN_CLICKED(IDC_START, OnStart)
	ON_BN_CLICKED(IDC_STOP, OnStop)
	ON_BN_CLICKED(IDOK, OnExit)
	ON_BN_CLICKED(IDC_RESET, OnReset)
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// Local Variables.
//
static bool		RxRunning = FALSE;
static bool		RxExit = FALSE;
//
static int		cbEchoPackets;
static int		cbSequenceCheck;
static int		cbVerifyChecksum;
static int		cbReceiveAll;
static int		cbSaveToDisk;
//
static long		rx_PacketCount;
static long		rx_Errors;
static long		seq_Errors;


/////////////////////////////////////////////////////////////////////////////
// Local Functions.
//
// Receive thread.
//
UINT RxThread( LPVOID pParam )
{
    CRxDlg                      *hDlg = (CRxDlg *)pParam;
    char						buffer[128];
	unsigned short				checksum;
    int							i;
	DWORD						wait_ret;
    short                       setup1;
	unsigned char				currentNode;
	unsigned char				nodeMap[256];
    unsigned char				seqNumber[256] , seqNumChk;
	long						seqNumErr[256];
	long						packetCnt[256];
	COM20020_RECEIVE_BUFFER		rxb;
	COM20020_TRANSMIT_BUFFER	txb;
	FILE						*rx_file;
	HANDLE						hRxEvent;

    // Assign known value.
    setup1 = -1;

    // If we should log to disk.
	if ( cbSaveToDisk )
    {
        // Open a file.
        rx_file = fopen( "arcnet.tmp" , "w" );
        // If unable to open the file.
        if ( rx_file == NULL )
        {
            // Bring up error message.
			hDlg->MessageBox( "Unable to open log file" , "File Open Error" , MB_OK | MB_ICONEXCLAMATION);
            // Set flag.
            RxExit = TRUE;
            // And return.
            return -1;
        }
    }

	// If we should perform a sequence number check.
	if ( cbSequenceCheck )
	{
		// Zero the node information.
		for ( i = 0 ; i < 256 ; ++i )
		{
			nodeMap[ i ] = 0;
			seqNumber[ i ] = 0;
			seqNumErr[ i ] = 0;
			packetCnt[ i ] = 0;
		}
	}

    // Create event.
    hRxEvent   	= CreateEvent( NULL , FALSE , FALSE , NULL );

	// Make sure it's reset.
    ResetEvent( hRxEvent );

    // Enable event driven operation.
    Com20020WakeOnReceive( hRxEvent );

    // If we should receive all packets.
    if ( cbReceiveAll )
    {
        // Get SETUP1 register.
        setup1 = regSubRead( REG_SETUP1 );
        // And output with receive all checked.
        regSubWrite( REG_SETUP1 , ( setup1 | 0x10 ) );
    }

	// Initialize local variables.
    rx_PacketCount = 0;
	rx_Errors = 0;
	seq_Errors = 0;

	// Flush the receive buffer.
	Com20020FlushRX();

    // Let our parent know we're running.
    RxRunning = TRUE;

    // Main loop.
    while ( RxRunning )
    {
		// Wait a short time for a received packet.
		wait_ret = WaitForSingleObject( hRxEvent , 50 );
		// Continue if not signaled.
		if ( wait_ret != WAIT_OBJECT_0 )
			continue;
		// Loop to pick off any available data.
		do
		{
			// If there is a packet waiting.
			if ( Com20020Receive(&rxb) == 0 )
			{
				// Packet received, show received packet
				rx_PacketCount++;
                // Update display.
                sprintf( buffer , "%ld" , rx_PacketCount );
				hDlg->m_RxCount.SetWindowText( buffer );

				// If we're supposed to echo the packet.
				if ( cbEchoPackets )
				{
					// Setup TX buffer.
					txb.byDestinationNodeID = rxb.bySourceNodeID;
					txb.uiNumberOfBytes = rxb.uiNumberOfBytes;
					// Copy data.
					memcpy( txb.byDataBuffer , rxb.byDataBuffer , rxb.uiNumberOfBytes );
					// Send out the packet.
					Com20020Transmit( &txb );
				}

				// If we should perform a sequence number check.
				if ( cbSequenceCheck )
				{
					// Get node number.
					currentNode = rxb.bySourceNodeID;
					// If this is the first time we've seen this node.
					if ( nodeMap[ currentNode ] == 0 )
					{
						// Mark it as known.
						nodeMap[ currentNode ] = 1;
						// Assign expected next sequence number for node.
						seqNumber[ currentNode ] = rxb.byDataBuffer[0] + 1;
						// Increment the node packet count.
						++packetCnt[ currentNode ];
					}
					//
					// Else we've seen this node before.
					//
					else
					{
						// Increment the node packet count.
						++packetCnt[ currentNode ];
						// Get sequence number from packet.
						seqNumChk = rxb.byDataBuffer[0];
						// If it doesn't match expected value.
						if ( seqNumChk != seqNumber[ currentNode ] )
						{
							// Increment error count.
							++seqNumErr[ currentNode ];
							// Reset expected next number so we can keep going.
							seqNumber[ currentNode ] = seqNumChk;
							// Update the error display.
							sprintf( buffer , "%ld" , ++seq_Errors );
							hDlg->m_SeqErrors.SetWindowText( buffer );
						}
						// Increment next expected sequence number.
						++seqNumber[ currentNode ];
					}
				}

				// If we should check for errors.
				if ( cbVerifyChecksum )
				{
					//	Structure of data packets:
					//
					//	data[0]		Sequence marker byte; starts at '0' and increments (rolling over).
					//	data[1]		IP marker byte; last byte of our IP address.
					//	data[2]		Low  byte of checksum; data if checksum not used.
					//	data[3]		High byte of checksum; data if checksum not used.
					//	data...		Data bytes.
					//
					// Initialize checksum.
					checksum = rxb.byDataBuffer[0];
					checksum += rxb.byDataBuffer[1];
					// Add each data byte to checksum.
					for ( i = 4 ; i < rxb.uiNumberOfBytes ; ++i )
					{
						checksum += rxb.byDataBuffer[ i ];
					}
					// If checksums don't match.
					if ( *((unsigned short *)&rxb.byDataBuffer[2]) != checksum )
                    {
						// Increment error count.
						++rx_Errors;
                        // Update display.
                        sprintf( buffer , "%ld" , rx_Errors );
						hDlg->m_ErrCount.SetWindowText( buffer );
                    }
				}

                // If we're supposed to store the data to file.
				if ( cbSaveToDisk )
				{
					// Do it.
					writePacket( rx_file , &rxb );
				}
			}
		// Continue if there is another waiting.
		} while ( rxb.dwNumberOfFilledBuffers >= 1 );
    }

    // If we were logging to disk.
	if ( cbSaveToDisk )
    {
    	// Close the file.
        fclose( rx_file );
    }

    // If we were receiving all packets.
	if ( cbReceiveAll )
    {
       	// Restore original setup1 register.
        regSubWrite( REG_SETUP1 , (char)setup1 );
    }

	// End event notification.
	Com20020ResetWakeOnReceive();

	// Get rid of event handle.
	CloseHandle( hRxEvent );

    // Set flag.
    RxExit = TRUE;

	// And return.
	return 0;
}

/////////////////////////////////////////////////////////////////////////////
// CRxDlg message handlers

void CRxDlg::OnStart() 
{
	CWinThread		*RxThreadPtr;

	// Set the checkbox flags, so the thread doesn't use the dialog
	// box handle. That way, we can check for entry and exit.
	cbEchoPackets = m_Echo.GetCheck();
	cbSequenceCheck = m_SequenceCheck.GetCheck();
	cbVerifyChecksum = m_VerifyChksum.GetCheck();
	cbReceiveAll = m_RxAll.GetCheck();
	cbSaveToDisk = m_DiskLog.GetCheck();
	// Clear the flags.
	RxRunning = RxExit = FALSE;
	// Start the receive thread.
	RxThreadPtr = AfxBeginThread( RxThread , this , 0 , 0 , 0 , 0 );
	// Wait for it to start; be prepared for an early exit.
	while ( ( RxRunning == FALSE ) && ( RxExit == FALSE ) )
	{
		// Sleep for a short time.
		Sleep( 50 );
	}
	// Disable start button.
	m_Start.EnableWindow( FALSE );
	// Enable stop button.
	m_Stop.EnableWindow( TRUE );
	// Disable reset button.
	m_Reset.EnableWindow( FALSE );
}

void CRxDlg::OnStop() 
{
	// If RX thread is running.
	if ( RxRunning == TRUE )
	{
		// Clear the thread running flag.
		RxRunning = FALSE;
		// _DBUG: Race condition.
		/*
		// Wait for it.
		while ( RxExit == FALSE )
		{
			Sleep( 500 );
		}
		*/
	}
	// Enable start button.
	m_Start.EnableWindow( TRUE );
	// Disable stop button.
	m_Stop.EnableWindow( FALSE );
	// Enable reset button.
	m_Reset.EnableWindow( TRUE );
}

void CRxDlg::OnExit() 
{
	// If RX thread is running.
	if ( RxRunning == TRUE )
	{
		// Clear the thread running flag.
		RxRunning = FALSE;
		// _DBUG: Race condition.
		/*
		// Wait for it.
		while ( RxExit == FALSE )
		{
			Sleep( 500 );
		}
		*/
	}
	// And exit normally.
	CDialog::OnOK();
}

BOOL CRxDlg::OnInitDialog() 
{
	CDialog::OnInitDialog();
	
	// Set initial values for counts.
	rx_PacketCount = 0;
	rx_Errors = 0;
	seq_Errors = 0;

	// Put '0' in display windows.
	m_RxCount.SetWindowText( "0" );
	m_ErrCount.SetWindowText( "0" );
	m_SeqErrors.SetWindowText( "0" );

	// Assume we want a loop test.
	// _DBUG: Let's not assume.
	//m_Echo.SetCheck( TRUE );

	
	return TRUE;  // return TRUE unless you set the focus to a control
	              // EXCEPTION: OCX Property Pages should return FALSE
}

void CRxDlg::OnReset() 
{
	// Set initial values for counts.
	rx_PacketCount = 0;
	rx_Errors = 0;
	seq_Errors = 0;

	// Put '0' in display windows.
	m_RxCount.SetWindowText( "0" );
	m_ErrCount.SetWindowText( "0" );
	m_SeqErrors.SetWindowText( "0" );
}
