////////////////////////////////////////////////////////////////////////////////
// MODULE: SonyVisca.cpp
//
// HEADER: SonyVisca.h
//
// This module has the code for interfacing with both the Visca Camera and
// the host infodriver.
//
// The module contains class definitions that:
// a)  Implement the callback functions for the external infodriver
// b)  Implement a FIFO queue for commands sent to the camera
// c)  Implement a FIFO queue for reply messages from the camera.
// 
// Most of the command messages implemented are common across a range of
// camera that support VISCA protocol. A small number of extra commands have 
// been implemented for a camera that has extra controls for colour balance
// and tally lamp control.
//
// This module contains a static array variable that holds the details of the
// commands implemented, and some that may asked for by users. The array holds
// a data structure that contains the enum command_id which matches the array
// index for that command, an enum value that defines the structure of the 
// response from the camera, some flags used during reply processing, and two
// 16-byte arrays that hold the message template to send to the camera, and a 
// message template for the reply. The arrays are based on a single camera in
// the control chain. Prior to transmission one or more bytes in the template
// may need to be set to the user selected paramter value (eg pan speed).
//
// This data centric mechanism means that it is simple to add new commands to
// this driver. There are three or 4 stages to implementing the new command:
// 1) Add a identifier to enum 'ViscaCommands' which is defined in the
//    SonyVisca.h header file. Identifier names start 'vc_'. The final entry
//    must be 'vc_ArraySize' which defines the size of the array of
//    structures.

// 2) Add the template data structure to variable mvc defined in this module.
//    Set any parameters to the most baic form, and choose desintaion camera 1.
//
// 3) Select the infodriver slot that will be used to request the command be 
//    sent to the camera. Add a case for that index to the function 
//    processSlotRequest(int device, int index, const string & slot) defined
//    in this module. In the case entry add code to modify the basic command to
//    pass the user provided data. Add the command to the camera transmission
//    queue (see an example case alreay defined) and use a SendMessage to
//    cause the message processor to check for messages awaiting transmission.
//    It queues only one message at a time, and waits (with timeout) for the 
//    reply.
//
// 4) If the command returns status data (ie an Inquiry message) add a case
//    statement to the processMsgReply() function defined in this module. As
//    a result of the processing new data values may need to be sent to the
//    infodriver for transmission to BNCS clients.
//
////////////////////////////////////////////////////////////////////////////////


#include "stdafx.h"
#include "SonyVisca.h"
#include "awExtInfo.h"
#include "include\bncsdef.h"
#include "include\bncsif32.h"
#include "include\COM32.H"
#include "include\cc.h"

/////////////////////////////////////
// Definitions used by this module //
/////////////////////////////////////
#define EOM 0xFF		// EOM marks End Of Message from Visca camera

// Forward reference templates only used by this compilation module.
void CommsRx(serialport *psp);
bool processMsgReply(const vCmdIf *msgblk, const RxMessage *rmsg);
void processSlotRequest(int device, int index, const string & slot);

//////////////////////////
// CLASS IMPLEMENTAIONS //
//////////////////////////

// =================================================================================================
class myDriver : public IccInfodriverCallback
{
public:
	myDriver(void) { m_info = new awExtInfo(this); };
	virtual ~myDriver() { delete m_info; };

	bool connect(int device, enum cc::ipcType ipc = cc::bbc, long ipcOptions = 0);	// Allow user to choose comms type
	bool disconnect(void) { return m_info->disconnect(); };
	bool IsInfoDriverConnected(void) { return m_info->isConnected(); };
	bool setInfoSlot(int index, const string & value, bool send);
	void setRedundancyMode(enum cc::redundancyMode mode);
	void setRedundancyState(enum cc::redundancyState state);

protected:
	// Re-implement those functions we need (declared in IccInfodriverCallback)
	void cciSlotRequest(int device, int index, const string & slot)
	{
		if (DEEP_DEBUG)
		{
			Debug("Slot request. Device %d, index %d contents %s", device, index, slot.c_str());
		}
		// Send the message to the content processor
		processSlotRequest(device, index, slot);
		updateInfoRxCount(1);
	};

	// Connected event 
	void cciConnected(void)
	{
		Debug("Infodriver reports connection event.");
	};
	
	// Disconnected event
	void cciDisconnected(void)
	{
		Debug("Infodriver reports disconnection. Driver Closing");
		PostQuitMessage(0);	// Call for driver close
	};

	// Reports the Tx/Rx mode of the external Infodriver
	void cciRedundancyState(enum cc::redundancyState state)
	{
		switch (state)
		{
			case cc::tx:
				driverMode = IFMODE_TXRX;	// driverModecontrols the colour of the dialog status field
				SetDlgItemText(hWndDlg, IDC_IDSTAT, "RxTx");
				break;

			case cc::rx:
				driverMode = IFMODE_RXONLY;
				SetDlgItemText(hWndDlg, IDC_IDSTAT, "Rx");
				// Try to force the mode back to RxTx
				setRedundancyState(cc::tx);
				break;

			case cc::forceTx:
			case cc::broken:
			case cc::unknown:
				driverMode = IFMODE_NONE;
				SetDlgItemText(hWndDlg, IDC_IDSTAT, "BAD");
				// Try to force the mode back to RxTx
				setRedundancyState(cc::tx);
				break;
		}

	}	// End of cciRedundancyState(enum cc::redundancyState state)

private:
	awExtInfo *m_info;
};

///////////////////////////////////////////////////////////////////////////////////////////////////

bool myDriver::connect(int device, enum cc::ipcType ipc, long ipcOptions)
{
	return m_info->connect(device, ipc, ipcOptions);
}

// Set a defined slot index to a value
bool myDriver::setInfoSlot(int index, const string & value, bool send)
{
	return m_info->setSlot(index, value, send);
}

void myDriver::setRedundancyMode(enum cc::redundancyMode mode)
{
	m_info->setRedundancyMode(mode);
}

void myDriver::setRedundancyState(enum cc::redundancyState state)
{
	m_info->setRedundancyState(state);
}


////////////////////////////////////////////////////////////////////////////////
// Declare a static instance of the driver type
myDriver myExt;

////////////////////////////////////////////////////////////////////////////////
// General interface functions to infodriver instance.
// Called to create an instance of myDriver
bool connectToExt(int driver_id)
{
	return myExt.connect(driver_id, cc::bbc);	// Connect to external infodriver using BBC DLL 32 bit comms
}

bool InfoDriverConnected(void)
{
	return myExt.IsInfoDriverConnected();
}

bool disconnectFromExt(void)
{
	return myExt.disconnect();
}

bool setSlotContent(int index, const char* value, bool sendRevertive)
{
	updateInfoTxCount(1);
	return myExt.setInfoSlot(index, value, sendRevertive);
}

void setSingleControllerMode(void)
{
	myExt.setRedundancyMode(cc::singleControllerCapable);
}

void setRedundancyState(enum cc::redundancyState state)
{
	myExt.setRedundancyState(state);
}


///////////////////////////////////////////////////////////////////
// Define the queue classes used internally within SonyVisca.cpp //
///////////////////////////////////////////////////////////////////
//
// Queue and dequeue class definitions to support reception and transmission of
// data to and from the Visca Camera. The vCmdQ class manages objects defined
// by the struct vCmdIf. This queue mechanism uses an array to hold the queued 
// objects.

class vCmdQ {
private:
	vCmdIf* firstSlot;	// Pointer to the first slot available in the heap
	int q_head;			// Index of first item in the queue
	int q_tail;			// Index of last item in the queue
	int numItems;		// Number of items currently in the queue
	int maxItems;		// Number of items the queue can hold
	int hwm;			// Maximum number of items stored in the queue.

public:
	vCmdQ(int numItemsInQueue);	// Creates a queue that holds number of items passed as parameter
	~vCmdQ(void);				// Free the memory borrowed from the heap
	vCmdIf front(void);			// Return command entry at the front of the queue
	void pop(void);				// Remove item from the front of the queue
	void push(vCmdIf nxtCmd);	// Add item to the queue
	BOOL empty(void);			// Return TRUE if there is nothing in the queue 
	int size(void);				// Return number of items currently in the queue
	vCmdIf back(void);			// Return value of last item in the queue
	int max_used(void);			// Return maximum depth of queue used.
};

vCmdQ::vCmdQ(int numItems)
{
	// Constructor - make an object with storage for the number of items passed in numItems
	if (numItems > 0)
	{
		this->firstSlot = NULL;			// Defensive programming - initialise to NULL
		try
		{
			this->firstSlot = new vCmdIf[numItems];	// Allocate some memory
		}
		catch (std::bad_alloc)
		{
			// Processing for a bad allocation
			MessageBox(hAppWnd, "Unable to allocate storage for Tx Command Queue", "Fatal Error", MB_ICONERROR);
			PostQuitMessage(0);
		}
		this->q_head = 0;
		this->q_tail = 0;
		this->numItems = 0;
		this->maxItems = numItems;
		this->hwm = 0;
	}
}

vCmdQ::~vCmdQ(void)
{
	// Destructor - release the memory we grabbed in the constructor.
	if (this->firstSlot)
	{
		delete[] this->firstSlot;	// Release the memory
		this->firstSlot = NULL;		// Set pointer to show no allocated memory
	}
}

vCmdIf vCmdQ::front(void)
{
	// Return the data in the front item.
	return this->firstSlot[q_head];
}

void vCmdQ::pop(void)
{
	// Remove the item at the head of the queue.
	if (this->numItems > 0)
	{
		this->q_head++;			// Move the head index to next item in the queue
		this->numItems--;
		if (this->q_head >= this->maxItems) q_head = 0;	// Manage circular buffer wrap around
	}
}

void vCmdQ::push(vCmdIf nxtCmd)
{
	// Add a new item to the end of the queue
	if (this->numItems < this->maxItems)
	{
		this->firstSlot[this->q_tail] = nxtCmd;	// Save the item at the end of the queue
		this->numItems++;						// Increment usage count
		this->q_tail++;							// Add one to the tail. This is index of next item to store
		if (this->q_tail >= this->maxItems) q_tail = 0;	// Do the wrap around needed by a circular buffer
		if (this->numItems > this->hwm) this->hwm = this->numItems;		// Update maximum used count as appropriate
	}
	else
		Debug("TxQ fill exceeded available space.");
}

BOOL vCmdQ::empty(void)
{
	// Returns TRUE if there are no objects in this queue
	return (this->numItems == 0) ? TRUE : FALSE;
}

int vCmdQ::size(void)
{
	// Returns the number of items in the queue.
	return this->numItems;
}

vCmdIf vCmdQ::back(void)
{
	// Returns the object at the end of the queue.
	if (this->numItems > 0)
		return this->firstSlot[this->q_tail - 1];
	else
		return this->firstSlot[this->q_head];
}

int vCmdQ::max_used(void)
{
	// Return value of high water mark - maximum depth of queue ever used.
	return this->hwm;
}


// + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 
// FIFO Message queue for responses received from camera.
class vRxMsg {
private:
	RxMessage* firstSlot;	// Pointer to the first slot available in the heap
	int q_head;				// Index of first item in the queue
	int q_tail;				// Index of last item in the queue
	int numItems;			// Number of items currently in the queue
	int maxItems;			// Number of items the queue can hold
	int hwm;				// Maximum number of items stored in the queue.

public:
	vRxMsg(int numItemsInQueue);// Creates a queue that holds number of items passed as parameter
	~vRxMsg(void);				// Free the memory borrowed from the heap
	RxMessage front(void);		// Return received message from the front of the queue
	void pop(void);				// Remove item from the front of the queue
	void push(RxMessage nxtMsg);	// Add item to the queue
	BOOL empty(void);			// Return TRUE if there is nothing in the queue 
	int size(void);				// Return number of items currently in the queue
	RxMessage back(void);		// Return value of last item in the queue
	int max_used(void);			// Return maximum depth of queue used.
};

vRxMsg::vRxMsg(int numItems)
{
	// Constructor - make an object with storage for the number of items passed in numItems
	if (numItems > 0)
	{
		this->firstSlot = NULL;			// Defensive programming - initialise to NULL
		try
		{
			this->firstSlot = new RxMessage[numItems];	// Allocate some memory
		}
		catch (std::bad_alloc)
		{
			// Processing for a bad allocation
			MessageBox(hAppWnd, "Unable to allocate storage for Receive Messages Queue", "Fatal Error", MB_ICONERROR);
			PostQuitMessage(0);
		}
		this->q_head = 0;
		this->q_tail = 0;
		this->numItems = 0;
		this->maxItems = numItems;
		this->hwm = 0;
	}
}

vRxMsg::~vRxMsg(void)
{
	// Destructor - release the memory we grabbed in the constructor.
	if (this->firstSlot)
	{
		delete[] this->firstSlot;	// Release the memory
		this->firstSlot = NULL;		// Set pointer to show no allocated memory
	}
}

RxMessage vRxMsg::front(void)
{
	// Return the data in the front item.
	return this->firstSlot[q_head];
}

void vRxMsg::pop(void)
{
	// Remove the item at the head of the queue.
	if (this->numItems > 0)
	{
		this->q_head++;			// Move the head index to next item in the queue
		this->numItems--;
		if (this->q_head >= this->maxItems) q_head = 0;	// Manage circular buffer wrap around
	}
}

void vRxMsg::push(RxMessage nxtMsg)
{
	// Add a new item to the end of the queue
	if (this->numItems < this->maxItems)
	{
		this->firstSlot[this->q_tail] = nxtMsg;	// Save the item at the end of the queue
		this->numItems++;						// Increment usage count
		this->q_tail++;							// Add one to the tail. This is index of next item to store
		if (this->q_tail >= this->maxItems) q_tail = 0;	// Do the wrap around needed by a circular buffer
		if (this->numItems > this->hwm) this->hwm = this->numItems;		// Update maximum used count as appropriate
	}
	else
		forceDebug("TxQ fill exceeded available space.");
}

BOOL vRxMsg::empty(void)
{
	// Returns TRUE if there are no objects in this queue
	return (this->numItems == 0) ? TRUE : FALSE;
}

int vRxMsg::size(void)
{
	// Returns the number of items in the queue.
	return this->numItems;
}

RxMessage vRxMsg::back(void)
{
	// Returns the object at the end of the queue.
	if (this->numItems > 0)
		return this->firstSlot[this->q_tail - 1];
	else
		return this->firstSlot[this->q_head];
}

int vRxMsg::max_used(void)
{
	// Return value of high water mark - maximum depth of queue ever used.
	return this->hwm;
}

// ************************************************************************************************

// ===================== //
// MODULE-WIDE VARIABLES //
// (Module Globals)      //
// ===================== //
vRxMsg* inputMsg = new vRxMsg(30);	// Message receive queue for messages from camera
vCmdQ *sendMsg = new vCmdQ(40);		// Command transmit queue for commands to camera
serialport *port = NULL;			// Comms library defines C++ serial comms object.
BYTE panSpeed = 12;					// Default camera pan speed.  Modified by data sent 
									// Allowed values can vary with camera model:
									// to slot 31. D30/31 1..24    BRC300 1..24
BYTE tiltSpeed = 12;				// Default camera tilt speed. Set by data sent to
									// slot 32. Allowed values can vary with camera model:
									// D30/31 1..20    BRC300 1..24


/* /////////////////////////////////////////////////////////////////////////////
Sony Visca Slot Usage
=====================
Note that many of the slots listed below are unused as the original Visca driver
was an add-on to a JVC-55 driver. This driver only implements commands for the
Visca camera series. Slots from 50 are extra facilities added by this driver
implementation, but not present in earlier BBC/Siemens/ATOS drivers.

Slot	R/W	Slot Name		Parameters
====	===	============	====================================================
   1	W	Reset Comms		1 = Reset Serial Comms to Camera
   2		---
   3	W	Iris & Gain		0=Auto		1=Manual
   4	W	White Balance	0=Auto		1=Indoor	2=Outdoor
   5	W	Auto White		1 = Set white balance and hold
   6	W	Gain			0 3 6 9 12 15 18 ONLY if IRIS is set to Manual
   7		Shutter			Could be invoked, but huge differences in code meaning between models.
   8	W	Zoom			0=Stop			
							1=Tele_slow		2=Wide_slow
							3=Tele_fast		4=Wide_fast
   9	W	Focus			0=Stop			1=Near_Slow		2=Far_slow	3=Near_fast
							4=Far_Fast						6=Auto		7=Manual
  10	W	Gamma
  11	W	Title
  12	W	Title Position
  13	W	Title Clear
  14	W	Title Edit
  15	W	Save
  16	W	SC Coarse
  17	W	Iris Detect		
  18		---
  19		---
  20		---
  21	W	Iris Level		0=Closed,	
							 1=F28     2=F22     3=F19     4=F16     5=F14
							 6=F11     7=F9.6    8=F8      9=F6.8   10=F5.6
							11=F4.8   12=F4     13=F3.4   14=F2.8   15=F2.4
							16=F2.0   17=F1.6
  22	W	Black Level
  23		---
  24		---
  25		---
  26		---
  27		---
  28		---
  29		---
  30		---
  31	W	Pan Speed		1=Slowest	24=Fastest
  32	W	Tilt Speed		1=Slowest	20=Fastest
  33	W	Pan Command		0=Stop
							1=Up		2=Down		3=Left		4=Right
							5=UpLeft	6=UpRight	7=DownLeft	8=DownRight
							9=Home
  34		---
  35		---
  36		---
  37		---
  38		---
  39		---
  40	W	Shot Box Set	1, 2, 3, 4, 5, 6.  D30/31 and BRC300 support 6 shot boxes
							                   Later cameras support up to 16.
  41	W	Shot Box Recall	1, 2, 3, 4, 5, 6
  42	W	Shot Box Reset	1, 2, 3, 4, 5, 6
  43		---
  44		---
  45		---
  46		---
  47		---
  48		---
  49		---
  50	W	Set Manual White Balance
  51	W	Reset Red Gain
  52	W	Set Red Gain  - values 0 to 255
  53	W	Reset Blue Gain
  54	W	Set Blue Gain - values 0 to 255
  55	W	Camera Tally light On/Off. 0=Off, 1=On
  ...
  4095	RW	Data written to this slot is output from slot 4096
  4096	RW	Data written to this slot is output from slot 4095

*/ /////////////////////////////////////////////////////////////////////////////



///////////////////////////////////////////
//                                       //
//   SERIAL COMMUNICATIONS WITH CAMERA   //
//   =================================   //
//                                       //
///////////////////////////////////////////
  
// Visca Message Structure
// =======================
// Messages and replies have the same basic structure - One byte header, 1 to 14 bytes message,
// 1 terminator byte. The header contains two fixed bits (MSB=1, Bit 3 = 0) and two 3-bit fields
// (Bits 6 to 4, bits 2 to 0). Bit field 1 (bits 6 to 4) is the address of the sender, bit field 2
// (bits 2 to 0) is the destination address. With a single drop link the destination address can
// be set at 1. The controller source address is 0, and the destination broadcast address is 0.
// Thus a broadcast message starts with 0x88. The terminating byte has all bits set to a '1', hence
// the terminator is 0xFF. This terminal value is defined as symbol EOM
//
// Whilst it is possible to overlap data to the camera and a response from the camera, the best
// mechanism is to send a command, and wait for a reply from the camera. Commands are processed
// one command per vertical interval. Once a command has been acknowledged, another command can
// be sent, but the user must then monitor which of two receive buffers is active.
//
// As the replies from the camera also end in EOM, the message receive processing can easily
// recognise the end of the return message. Responses to status enquiries are sent immediately,
// commands are acknowledged, then a command complete message is returned.
//
// ACK Message and Command Completion
// ==================================
// These messages are returned from the camera to the controller to acknowledge communications.
// Each message contains 3 (hexadecimal) bytes:
//
//       ACK                 z0 4y FF
//       Command completion  z0 5y FF
//
// The z nibble is the camera address plus 8. The y nibble is the command socket number (1 or 2). 
// For enquiries there is no ACK, and the comand completion message will have the socket ID = 0.
// Thus information returns from the camera take the format:  z0 50 .... FF
// 
// Camera power up causes a message to be sent to the controller z0 38 FF
//
// Error Messages
//    syntax error             z0 60 02 FF
//    command buffer full      z0 60 03 FF
//    command cancel           z0 60 04 FF
//    No sockets               z0 60 05 FF
//    Command not executable   z0 60 41 FF
//
// Communications initialise:
//    88 30 01 FF      (Address set broadcast)
//    88 01 00 01 FF   (IF Clear broadcast)
// 


///////////////////////////////////////////////////////////////////////////////
// FUNCTION: BOOL openSerialPort(void)
//
// PURPOSE:  Open the comms port specified in global variable comPort
//
// RETURNS:	 The value returned by the comms library.
//
// COMMENTS: 
// Sets the baud rate etc according to the values that were read from the
// DEV_XXX.INI file, and strored in global variables.
//
int openSerialPort(void)
{
	port = new serialport();				// Make an instance

	// Set the serial characteristics
	port->baudrate(comBaud);
	port->databits(comDataBit);
	port->stopbits(comStop);
	port->paritybits(comParity);
	return(port->open(comPort));			// Try to open the port, return the status value.
}


///////////////////////////////////////////////////////////////////////////////
// FUNCTION: void closeCommPort(void)
//
// PURPOSE: Close the serial comms port.
//
void closeCommPort(void)
{
	port->close();
	comPortOpen = FALSE;
}


///////////////////////////////////////////////////////////////////////////////
// FUNCTION: void sendSerial(msg, numchar)
//
// PURPOSE:  Send numChar bytes from the byte array msg
//
void sendSerial(BYTE *msg, long numChar)
{
	port->txdata(msg, numChar);
	updateSerialTxCount(1);			// Update the Serial TX message count
	SetTimer(hAppWnd, COMMS_TIMEOUT_ID, COMMS_TIMEOUT_INTERVAL, NULL);	// Initiate comms timeout timer
}


//==================================================================================================
// The messages returned by the Visca have variable length, but we know that each message ends with
// the byte value 0xFF (EOM). We also know that the minimum message length is 3 bytes, and the 
// maximum length is 16 bytes.
//
#define VISCA_MIN_MSG_LEN 1

////////////////////////////////////////////////////////////////////////////////////////////////////
// FUNCTION: setRxProcessor()
//
// PURPOSE:  Tells the comms library which function to call when data is received
BOOL setRxProcessor(void)
{
	return (port->notify(CommsRx, VISCA_MIN_MSG_LEN));
}

////////////////////////////////////////////////////////////////////////////////////////////////////
// FUNCTION: void CommsRx(serialport *psp)
//
// PURPOSE:  This function is the callback function accessed by the low level serial library when
//           data has arrived and needs processing.
//
// COMMENTS: With one exception, all of the comms with the camera use a command - response mode.
// A command is sent to the camera and it responds with the relevant Acknowledge, command complete
// or status values.

RxMessage iMsg;			// A permanent buffer in which we assemble an incomming message
						// whilst we wait for the 0xFF message terminator.
int outIdx = 0;			// Index counter into the input message character array.


void CommsRx(serialport *psp)
{
	BYTE bData[512];
	long iRead;
	COMSTAT cs;
	DWORD dwError;
	int iPort;			// Do we need to know this in our driver as we only use one serial port?
	UINT inIdx;
	BYTE ch;
	bool msgQueued;

	iPort = psp->port();							// Returns the open com port id.
	psp->state(&cs, &dwError);						// Get the Comm port status
	iRead = psp->rxdata(bData, cs.cbInQue);			// Copy the data to our buffer

	switch (dwError)
	{
		case CE_BREAK:			// Hardware detected a break condition.
		case CE_FRAME:			// Hardware detected a framing error.
		case CE_IOE:			// An IO error occured during comms with device.
		case CE_OVERRUN:		// A character buffer overrun has occured.
		case CE_RXOVER:			// An input buffer overflow has occured.
		case CE_RXPARITY:		// The hardware detected a parity error.
		case CE_TXFULL:			// Transmit buffer is full.
			forceDebug("SonyVisca: Serial Comms Error detected.");
			break;				// Do not parse any data

		default:
			// Need to build a complete message in our output buffer. Input message ends when the 
			// returned character is 0xFF. Copy the input by reading a byte into a temporary character.
			// The process loop stops/pauses when the 0xFF is found. Symbolic constant EOM is defined as 0xFF
			inIdx = 0;				// Reset input data index offset
			msgQueued = false;

			if (cs.cbInQue > 0)		// Be defensive - make sure that we have at least 1 character in the buffer
			{
				while (inIdx < cs.cbInQue)
				{
					// Transfer until part or whole of input buffer is moved to output buffer
					ch = bData[inIdx++];			// Read the byte and increment the index counter
					iMsg.response[outIdx++] = ch;	// Store the character or NULL in the output message buffer
					if (ch == EOM)					// Test for end of input message
					{
						inputMsg->push(iMsg);		// Send completed message to processing queue.
						outIdx = 0;					// And start to overwrite the assembly buffer from its beginning again.
						msgQueued = true;			// Set flag to send "process reply" message
						updateSerialRxCount(1);		// Update the serial Rx Count
					}
				}		// end of while(....
			}			// end of if (cs.cbInQue > 0)
			
			if (msgQueued)
			{
				// Wake up the message processor by sending a windows message to main window
				SendMessage(hAppWnd, RXMSG_PROCESS, mr_Reply, NULL);	// mr_Reply is reason message processor is invoked.
				msgQueued = false;										// Clear flag now it has been used
			}
			break;	// break for case default
	}

}	// End of CommsRx(serialport *psp)


////////////////////////////////////////////////////////////////////////////////
// FUNCTION: void zapBuffers(void)
//
// PURPOSE:  Empty the rx and Tx buffers used by serial comms.
void zapbuffers(void)
{
	port->flushbuf(PURGE_TXCLEAR);
	port->flushbuf(PURGE_RXCLEAR);
}


// ============================================================================================== //
// Visca commands. These are held in a large array created at compile time. Commands are copied   //
// to working buffer when a message is to be queued for transmission.                             //
// The array index used to fetch the command matches the entries in enum "ViscaCommands"          //
// ---------------------------------------------------------------------------------------------- //
vCmdIf mvc[vc_ArraySize] = {
		/* First entry is a dummy, "do nothing" command used by the message processor in idle */
		{
			vc_noCmd, vrmNone, 0, 0, 0, 0, 0,
			{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
			{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }
		},
		/* Start of Visca command definitions (actions) */
		{
			vc_AddressSet, vrmDataOnly, 0, 0, 0, 4, 4,
			{ 0x88, 0x30, 0x01, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
			{ 0x88, 0x30, 0x02, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }
		},
		{
			vc_IF_clear, vrmDataOnly, 0, 0, 0, 5, 5,
			{ 0x88, 0x01, 0x00, 0x01, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
			{ 0x88, 0x01, 0x00, 0x01, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }
		},
		{
			vc_ZoomStop, vrmAckComplete, 0, 0, 0, 6, 3,
			{ 0x81, 0x01, 0x04, 0x07, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
			{ 0x90, 0x50, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }
		},
		{
			vc_ZoomTeleStandard, vrmAckComplete, 0, 0, 0, 6, 3,
			{ 0x81, 0x01, 0x04, 0x07, 0x02, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
			{ 0x90, 0x50, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }
		},
		{
			vc_ZoomWideStandard, vrmAckComplete, 0, 0, 0, 6, 3,
			{ 0x81, 0x01, 0x04, 0x07, 0x03, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
			{ 0x90, 0x50, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }
		},
		{
			vc_ZoomTeleVar, vrmAckComplete, 0, 0, 0, 6, 3,
			{ 0x81, 0x01, 0x04, 0x07, 0x20, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
			{ 0x90, 0x50, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }
		},
		{
			vc_ZoomWideVar, vrmAckComplete, 0, 0, 0, 6, 3,
			{ 0x81, 0x01, 0x04, 0x07, 0x30, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
			{ 0x90, 0x50, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }
		},
		{
			vc_ZoomSetPosition, vrmAckComplete, 0, 0, 0, 9, 3,
			{ 0x81, 0x01, 0x04, 0x47, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
			{ 0x90, 0x50, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }
		},
		{
			vc_FocusStop, vrmAckComplete, 0, 0, 0, 6, 3,
			{ 0x81, 0x01, 0x04, 0x08, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
			{ 0x90, 0x50, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }
		},
		{
			vc_FocusFarStd, vrmAckComplete, 0, 0, 0, 6, 3,
			{ 0x81, 0x01, 0x04, 0x08, 0x02, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
			{ 0x90, 0x50, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }
		},
		{
			vc_FocusNearStd, vrmAckComplete, 0, 0, 0, 6, 3,
			{ 0x81, 0x01, 0x04, 0x08, 0x03, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
			{ 0x90, 0x50, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }
		},
		{
			// Not Available in all camera models
			vc_FocusFarVar, vrmAckComplete, 0, 0, 0, 6, 3,
			{ 0x81, 0x01, 0x04, 0x08, 0x20, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
			{ 0x90, 0x50, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }
		},
		{
			// Not Available in all camera models
			vc_FocusNearVar, vrmAckComplete, 0, 0, 0, 6, 3,
			{ 0x81, 0x01, 0x04, 0x08, 0x30, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
			{ 0x90, 0x50, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }
		},
		{
			vc_FocusDirect, vrmAckComplete, 0, 0, 0, 9, 3,
			{ 0x81, 0x01, 0x04, 0x48, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
			{ 0x90, 0x50, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }
		},
		{
			vc_FocusAuto, vrmAckComplete, 0, 0, 0, 6, 3,
			{ 0x81, 0x01, 0x04, 0x38, 0x02, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
			{ 0x90, 0x50, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }
		},
		{
			vc_FocusManual, vrmAckComplete, 0, 0, 0, 6, 3,
			{ 0x81, 0x01, 0x04, 0x38, 0x03, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
			{ 0x90, 0x50, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }
		},
		{
			vc_WBAuto, vrmAckComplete, 0, 0, 0, 6, 3,
			{ 0x81, 0x01, 0x04, 0x35, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
			{ 0x90, 0x50, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }
		},
		{
			vc_WBIndoor, vrmAckComplete, 0, 0, 0, 6, 3,
			{ 0x81, 0x01, 0x04, 0x35, 0x01, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
			{ 0x90, 0x50, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }
		},
		{
			vc_WBOutdoor, vrmAckComplete, 0, 0, 0, 6, 3,
			{ 0x81, 0x01, 0x04, 0x35, 0x02, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
			{ 0x90, 0x50, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }
		},
		{
			vc_WBOnePushMode, vrmAckComplete, 0, 0, 0, 6, 3,
			{ 0x81, 0x01, 0x04, 0x35, 0x03, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
			{ 0x90, 0x50, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }
		},
		{
			vc_WBOnePushTrigger, vrmAckComplete, 0, 0, 0, 6, 3,
			{ 0x81, 0x01, 0x04, 0x10, 0x05, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
			{ 0x90, 0x50, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }
		},
		{
			vc_AEAuto, vrmAckComplete, 0, 0, 0, 6, 3,
			{ 0x81, 0x01, 0x04, 0x39, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
			{ 0x90, 0x50, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }
		},
		{
			vc_AEManual, vrmAckComplete, 0, 0, 0, 6, 3,
			{ 0x81, 0x01, 0x04, 0x39, 0x03, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
			{ 0x90, 0x50, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }
		},
		{
			vc_AEShuttterPriority, vrmAckComplete, 0, 0, 0, 6, 3,
			{ 0x81, 0x01, 0x04, 0x39, 0x0A, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
			{ 0x90, 0x50, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }
		},
		{
			vc_AEIrisPriority, vrmAckComplete, 0, 0, 0, 6, 3,
			{ 0x81, 0x01, 0x04, 0x39, 0x0B, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
			{ 0x90, 0x50, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }
		},
		{
			vc_AEBrightMode, vrmAckComplete, 0, 0, 0, 6, 3,
			{ 0x81, 0x01, 0x04, 0x39, 0x0D, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
			{ 0x90, 0x50, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }
		},
		{
			vc_BrightReset, vrmAckComplete, 0, 0, 0, 6, 3,
			{ 0x81, 0x01, 0x04, 0x0D, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
			{ 0x90, 0x50, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }
		},
		{
			vc_BrightUp, vrmAckComplete, 0, 0, 0, 6, 3,
			{ 0x81, 0x01, 0x04, 0x0D, 0x02, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
			{ 0x90, 0x50, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }
		},
		{
			vc_BrightDown, vrmAckComplete, 0, 0, 0, 6, 3,
			{ 0x81, 0x01, 0x04, 0x0D, 0x03, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
			{ 0x90, 0x50, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }
		},
		{
			vc_ShutterReset, vrmAckComplete, 0, 0, 0, 6, 3,
			{ 0x81, 0x01, 0x04, 0x0A, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
			{ 0x90, 0x50, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }
		},
		{
			vc_ShutterUp, vrmAckComplete, 0, 0, 0, 6, 3,
			{ 0x81, 0x01, 0x04, 0x0A, 0x02, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
			{ 0x90, 0x50, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }
		},
		{
			vc_ShutterDown, vrmAckComplete, 0, 0, 0, 6, 3,
			{ 0x81, 0x01, 0x04, 0x0A, 0x03, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
			{ 0x90, 0x50, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }
		},
		{
			vc_ShutterDirect, vrmAckComplete, 0, 0, 0, 10, 3,
			{ 0x81, 0x01, 0x04, 0x4A, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
			{ 0x90, 0x50, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }
		},
		{
			vc_IrisReset, vrmAckComplete, 0, 0, 0, 6, 3,
			{ 0x81, 0x01, 0x04, 0x0B, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
			{ 0x90, 0x50, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }
		},
		{
			vc_IrisUp, vrmAckComplete, 0, 0, 0, 6, 3,
			{ 0x81, 0x01, 0x04, 0x0B, 0x02, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
			{ 0x90, 0x50, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }
		},
		{
			vc_IrisDown, vrmAckComplete, 0, 0, 0, 6, 3,
			{ 0x81, 0x01, 0x04, 0x0B, 0x03, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
			{ 0x90, 0x50, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }
		},
		{
			vc_IrisDirect, vrmAckComplete, 0, 0, 0, 9, 3,
			{ 0x81, 0x01, 0x04, 0x4B, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
			{ 0x90, 0x50, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }
		},
		{
			vc_GainReset, vrmAckComplete, 0, 0, 0, 6, 3,
			{ 0x81, 0x01, 0x04, 0x0C, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
			{ 0x90, 0x50, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }
		},
		{
			vc_GainUp, vrmAckComplete, 0, 0, 0, 6, 3,
			{ 0x81, 0x01, 0x04, 0x0C, 0x02, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
			{ 0x90, 0x50, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }
		},
		{
			vc_GainDown, vrmAckComplete, 0, 0, 0, 6, 3,
			{ 0x81, 0x01, 0x04, 0x0B, 0x03, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
			{ 0x90, 0x50, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }
		},
		{
			vc_GainDirect, vrmAckComplete, 0, 0, 0, 9, 3,
			{ 0x81, 0x01, 0x04, 0x4C, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
			{ 0x90, 0x50, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }
		},
		{
			vc_BacklightOn, vrmAckComplete, 0, 0, 0, 6, 3,
			{ 0x81, 0x01, 0x04, 0x33, 0x02, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
			{ 0x90, 0x50, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }
		},
		{
			vc_BacklightOff, vrmAckComplete, 0, 0, 0, 6, 3,
			{ 0x81, 0x01, 0x04, 0x33, 0x03, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
			{ 0x90, 0x50, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }
		},
		{
			vc_MemoryReset, vrmAckComplete, 0, 0, 0, 7, 3,
			{ 0x81, 0x01, 0x04, 0x3F, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
			{ 0x90, 0x50, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }
		},
		{
			vc_MemorySet, vrmAckComplete, 0, 0, 0, 7, 3,
			{ 0x81, 0x01, 0x04, 0x3F, 0x01, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
			{ 0x90, 0x50, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }
		},
		{
			vc_MemoryRecall, vrmAckComplete, 0, 0, 0, 7, 3,
			{ 0x81, 0x01, 0x04, 0x3F, 0x02, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
			{ 0x90, 0x50, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }
		},
		{
			vc_PanTiltUp, vrmAckComplete, 0, 0, 0, 9, 3,
			{ 0x81, 0x01, 0x06, 0x01, 0x00, 0x00, 0x03, 0x01, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
			{ 0x90, 0x50, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }
		},
		{
			vc_PanTiltDown, vrmAckComplete, 0, 0, 0, 9, 3,
			{ 0x81, 0x01, 0x06, 0x01, 0x00, 0x00, 0x03, 0x02, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
			{ 0x90, 0x50, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }
		},
		{
			vc_PanTiltLeft, vrmAckComplete, 0, 0, 0, 9, 3,
			{ 0x81, 0x01, 0x06, 0x01, 0x00, 0x00, 0x01, 0x03, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
			{ 0x90, 0x50, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }
		},
		{
			vc_PanTiltRight, vrmAckComplete, 0, 0, 0, 9, 3,
			{ 0x81, 0x01, 0x06, 0x01, 0x00, 0x00, 0x02, 0x03, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
			{ 0x90, 0x50, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }
		},
		{
			vc_PanTiltUpLeft, vrmAckComplete, 0, 0, 0, 9, 3,
			{ 0x81, 0x01, 0x06, 0x01, 0x00, 0x00, 0x01, 0x01, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
			{ 0x90, 0x50, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }
		},
		{
			vc_PanTiltUpRight, vrmAckComplete, 0, 0, 0, 9, 3,
			{ 0x81, 0x01, 0x06, 0x01, 0x00, 0x00, 0x02, 0x01, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
			{ 0x90, 0x50, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }
		},
		{
			vc_PanTiltDownLeft, vrmAckComplete, 0, 0, 0, 9, 3,
			{ 0x81, 0x01, 0x06, 0x01, 0x00, 0x00, 0x01, 0x02, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
			{ 0x90, 0x50, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }
		},
		{
			vc_PanTiltDownRight, vrmAckComplete, 0, 0, 0, 9, 3,
			{ 0x81, 0x01, 0x06, 0x01, 0x00, 0x00, 0x02, 0x02, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
			{ 0x90, 0x50, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }
		},
		{
			vc_PanTiltStop, vrmAckComplete, 0, 0, 0, 9, 3,
			{ 0x81, 0x01, 0x06, 0x01, 0x00, 0x00, 0x03, 0x03, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
			{ 0x90, 0x50, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }
		},
		{
			vc_PanTiltAbsolute, vrmAckComplete, 0, 0, 0, 15, 3,
			{ 0x81, 0x01, 0x06, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00 },
			{ 0x90, 0x50, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }
		},
		{
			vc_PanTiltHome, vrmAckComplete, 0, 0, 0, 5, 3,
			{ 0x81, 0x01, 0x06, 0x04, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
			{ 0x90, 0x50, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }
		},
		{
			vc_PanTiltReset, vrmAckComplete, 0, 0, 0, 5, 3,
			{ 0x81, 0x01, 0x06, 0x05, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
			{ 0x90, 0x50, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }
		},
		{
			vc_WBManual, vrmAckComplete, 0, 0, 0, 6, 3,
			{ 0x81, 0x01, 0x04, 0x35, 0x05, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
			{ 0x90, 0x50, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }
		},
		{
			vc_RedGainReset, vrmAckComplete, 0, 0, 0, 6, 3,
			{ 0x81, 0x01, 0x04, 0x03, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
			{ 0x90, 0x50, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }
		},
		{
			vc_RedGainValue, vrmAckComplete, 0, 0, 0, 9, 3,
			{ 0x81, 0x01, 0x04, 0x43, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
			{ 0x90, 0x50, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }
		},
		{
			vc_BlueGainReset, vrmAckComplete, 0, 0, 0, 6, 3,
			{ 0x81, 0x01, 0x04, 0x04, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
			{ 0x90, 0x50, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }
		},
		{
			vc_BlueGainValue, vrmAckComplete, 0, 0, 0, 9, 3,
			{ 0x81, 0x01, 0x04, 0x44, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
			{ 0x90, 0x50, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }
		},
		{
			vc_TallyLight, vrmAckComplete, 0, 0, 0, 8, 3,
			{ 0x81, 0x01, 0x7E, 0x01, 0x0A, 0x00, 0x03, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
			{ 0x90, 0x50, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }
		},
		/* Start of Visca Satus Inquiry messages */
		{
			vc_PowerInq, vrmDataOnly, 0, 0, 0, 5, 4,
			{ 0x81, 0x09, 0x04, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
			{ 0x90, 0x50, 0x02, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }
		}
};

// ============================================================================================== //

void ViscaMsg(int reason)
{
	// function ViscaMsg(reason) manages the message exchange with the Visca camera. The function 
	// may be invoked because:
	// a) a message has been added to the transmit queue (sendMsg),
	// b) a complete reply (message ending 0xFF) has been received by the serial inteface and
	//    added to the input queue (InputMsg),
	// c) a message reply timeout has occurred,
	// 
	// Input parameter reason informs the process which event triggered the call.
	//
	// Camera control messages are based on a command-response message exchange. There is only one
	// instance when the camera emits an unsolicited message called "Network Change". This message 
	// is generated in by:
	// a) Camera Power ON in a single link control chain
	// b) Power On or an extention camera added to the cable chain in a daisy-chained control chain.
	// This BNCS driver is designed for just one camera on the chain, so only camera power up is
	// significant.
	//
	// Data Processing Concept
	// =======================
	// The process function uses two local structures:
	//     command1 that holds a copy the newest message sent to the camera "sendMsg->front()",
	//     foque    that holds a copy of the most recently received message from the camera.
	//
	// When the function is invoked, command1 holds copy of most recent message sent to the camera.
	// This structure is initialised to dummy content, identical to command array index zero - mvc[0].
	static vCmdIf command1 = {
								vc_noCmd, vrmNone, 0, 0, 0, 0, 0,
								{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
								{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }
							 };
	RxMessage foque;		// Holds copy of message from camera.
	BYTE socket = 0;		// Which socket ion the camera was in use.

	// Use input parameter "reason" to decide on processing applied.
	switch (reason)
	{
		case mr_Command:
			// Actions depend on the state of the receive loop. If we are awaiting a reply from the
			// camera no action is required as a message is already added too the queue, but the camera 
			// is already busy. 
			//
			// If no reply is currently expected, initiate the transmission of next command packet
			// from the front of the queue. The reply expected status is maintained in the
			// command1.replymode field.
			if (command1.replymode == vrmNone)
			{
				if (!sendMsg->empty())	// Just being defensive!
				{
					command1 = sendMsg->front();						// Copy front message
					sendSerial(command1.cmdArray, command1.cmdLength);	// Send message to camera
					if (fDebug)
					{
						// Show message being sent to the camera
						char report[MAX_BUFFER_STRING];
						BYTE ch;
						int idx, j;
						j = sprintf_s(report, MAX_BUFFER_STRING, ">Camera:");	// Start to build the message
						idx = 0;
						do
						{
							ch = command1.cmdArray[idx++];
							j += sprintf_s(report + j, MAX_BUFFER_STRING - j, " %02X", ch);
						} while ((ch != EOM) && (idx < 17));
						Debug(report);
					}
				}
			}
			return;

			break;	// End of if (command1.replymode == vrmNone)

		case mr_Reply:
			// A reply packet has been received from the camera. This could be the power up message
			// or a response to a command packet sent to the camera.
			foque = inputMsg->front();		// Make a copy of the input message

			// Let Debug print this message when appropriate
			if (fDebug)
			{
				// Need to format a message to send to debug as we have variable number of bytes to convert.
				char report[MAX_BUFFER_STRING];
				BYTE ch;
				int idx, j;
				j = sprintf_s(report, MAX_BUFFER_STRING, "<Camera:");		// Start to build the message
				idx = 0;
				do
				{
					ch = foque.response[idx++];
					j += sprintf_s(report + j, MAX_BUFFER_STRING-j, " %02X", ch);
				} while ((ch != EOM) && (idx < 17));
				Debug(report);
			}

			// A network change or power up message is a three byte message:
			//   0xX0 0x38 0xFF       where X is the camera id+8.
			// It is only 3 byte message with 0x38 0xFF as bytes two and three.
			if ((foque.response[1] == 0x38) && (foque.response[2] == 0xFF))
			{
				// 99.99% certain that we have network change message (power up of a camera)
				// One final check to be certain - byte 1 lower nibble is zero, and upper nibble >= 9
				if (((foque.response[0] & 0x0F) == 0) && ((foque.response[0] & 0xF0) >= 0x90))
				{
					// Network has powered/reset. Remove all messages in all queues, re-initialise comms.
					forceDebug("Network change message - usually power up. Re-starting comms");
					zapbuffers();									// Flush any data in RX/TX Serial queues
					while (!(inputMsg->empty())) inputMsg->pop();	// Clear stack of received messages
					while (!(sendMsg->empty())) sendMsg->pop();		// Clear stack of transmitted messages
					command1 = mvc[0];								// Set "No comms outstanding"
					OpenCameraComms();								// Reset the comms
				}
			}

			// We have a message that should be expected. Process the message, but be aware that whilst
			// the expected reply is very probable, it could be one of the error responses:
			// a) Syntax error          z0 60 02 FF
			// b) Command buffer full   z0 60 03 FF
			// c) Command Cancelled     z0 6y 04 FF
			// d) No Socket				z0 6y 05 FF
			// e) Not Executable        z0 6y 41 FF
			//    where z = camera id+8, and y is the socket number
			//
			// So error messages can be found by first byte upper nibble > 8, lower nibble = 0,
			// second byte upper nibble = 6, and fourth byte = 0xFF
			
			// Look for Error message
			if (((foque.response[0] & 0xF0) > 0x80) && ((foque.response[0] & 0x0f) == 0) &&
				((foque.response[1] & 0xF0) == 0x60) && (foque.response[3] == 0xFF))
			{
				// We have detected an error packet. Sub-divide into specific error
				switch (foque.response[2])
				{
					case 0x02:
						// Syntax error
						if (DEEP_DEBUG) forceDebug("Message Syntax Error");
						break;

					case 0x03:
						// Command buffer full
						if (DEEP_DEBUG) forceDebug("Command buffer full");
						break;

					case 0x04:
						// Command Cancelled
						if (DEEP_DEBUG) forceDebug("Command Cancelled");
						break;

					case 0x05:
						// No socket
						if (DEEP_DEBUG) forceDebug("No socket available");
						break;

					case 0x41:
						// Command not executable
						Debug("Command not executable");
						break;

					default:
						// Should never be invoked unless get a corrupted character on serial link.
						forceDebug("Error message processor default - unknown problem");
						break;
				}	// End switch (foque.response[2])
				// Tidy up the queues...
				sendMsg->pop();
				inputMsg->pop();
				command1 = mvc[vc_noCmd];		// The "empty" command state.
				if (!sendMsg->empty())
				{
					SendMessage(hAppWnd, RXMSG_PROCESS, mr_Command, NULL);
				}
				return;
			}		// End if (((foque.response[0] & 0xF0) ...

			// Now process expected message returns
			// Consult the message structure for the last command sent. The structure has a field
			// that contains the type of reply we expect. Switch the processing as required.
			switch (command1.replymode)
			{
				case vrmAckOnly:
					// This is not currently a valid mode! Here just in case a future development
					// requires it. Two possible pattern (hex) 90 41 FF (ACK1) and 90 42 FF (ACK2)
					KillTimer(hAppWnd, COMMS_TIMEOUT_ID);
					if ((foque.response[0] == 0x90) && (foque.response[2] == 0xFF))
					{
						if ((foque.response[1] == 0x41) || (foque.response[1] == 0x42))
						{
							// ACK detected. Pop command and response off the queues
							if (!sendMsg->empty()) sendMsg->pop();
							if (!inputMsg->empty()) inputMsg->pop();
							command1 = mvc[vc_noCmd];
							// If there are commands in the queue, post message that will cause this
							// routine to run and try to send message.
							if (!sendMsg->empty())
							{
								SendMessage(hAppWnd, RXMSG_PROCESS, mr_Command, NULL);
							}
						}
					}
					break;	// End case vrmAckOnly

				case vrmDataOnly:
					// There are many possible data responses. So we need to check what is happening.
					// There is a template for the data response held in command1.
					// For the moment pop the message and reply off the queue
					KillTimer(hAppWnd, COMMS_TIMEOUT_ID);
					if (processMsgReply(&command1, &foque))
					{
						// All was well, pop command and reply off the stacks
						inputMsg->pop();
						sendMsg->pop();
						command1 = mvc[vc_noCmd];		// The "empty" command state.
						if (!sendMsg->empty())			// Any messages in the command queue?
						{
							SendMessage(hAppWnd, RXMSG_PROCESS, mr_Command, NULL);
						}
						return;
					}
					else
					{
						// processMsgReply() has detected an error. Tidy the receive
						// message stack and re-send the command.
						while (!inputMsg->empty()) inputMsg->pop();
						zapbuffers();										// Clear the serial buffers
						sendSerial(command1.cmdArray, command1.cmdLength);	// Resend the command
						return;
					}
					break;

				case vrmAckComplete:
					// Tend to get this for movement commands. ACK acknowledges validity 
					// of the command. Complete says that the command has been actioned.
					if (!command1.ackRx)
					{
						// We expect to receive a ACK - either (hex) 90 41 FF (ACK1) or
						// 90 42 FF (ACK2). We really expect ACK1 as this driver is not built
						// to use overlapped commands.
						if ((foque.response[0] == 0x90) && (foque.response[2] == 0xFF))
						{
							if ((foque.response[1] == 0x41) || (foque.response[1] == 0x42))
							{
								// Report to Debug is so needed
								if (DEEP_DEBUG)
								{
									Debug("ACK %d received from camera", foque.response[1] == 0x41 ? 1 : 2);
								}
								// ACK detected. Note the socket number
								command1.ackRx = true;
								command1.socket = foque.response[1] & 0x0F;
								// Merge socket id into expected response arry in command1
								command1.reply[1] |= (BYTE)command1.socket;
								inputMsg->pop();	// Remove ACK from input queue
								SetTimer(hAppWnd, COMMS_TIMEOUT_ID, COMMS_TIMEOUT_INTERVAL, NULL);	// Retrigger timeout timer
								return;				// Return and await completion message
							}
						}
					}
					else
					{
						// Expect to get the completion message. Compare received and expected messages
						// Need to compare the bytes as we have not defined == for byte arrays
						int i = 0;
						bool matched = true;
						do
						{
							matched &= (foque.response[i] == command1.reply[i]);
						} while (foque.response[i++] != EOM);

						if (matched)
						{
							if (DEEP_DEBUG)
							{
								Debug("Completion message %d received", (command1.reply[1] & 0x0F));
							}
							// We have received the completion message we needed.
							inputMsg->pop();		// Remove completion message from input queue
							sendMsg->pop();			// Remove command from command queue
							command1 = mvc[0];		// Put message processor into idle mode
							KillTimer(hAppWnd, COMMS_TIMEOUT_ID);
							if (!sendMsg->empty())	// Check for messages in the queue
							{
								SendMessage(hAppWnd, RXMSG_PROCESS, mr_Command, NULL);
							}
							return;
						}
					}

					break;	// End case vrmAckComplete:

				case vrmNone:
					// This should never occur, but is here just in case....
					// Remove command at front of queue, reset and prepare to re-invoke this function?
					return;
					break;
			}

			break;	// End of case mr_Reply:

		case mr_Timeout:
			// The camera failed to reply within a standard interal
			Debug("Comms Timeout");
			// Clear queues and wait for next background poll to check for comms status
			zapbuffers();									// Clear serial Tx/Rx queues
			while (!(inputMsg->empty())) inputMsg->pop();	// Clear stack of received messages
			while (!(sendMsg->empty())) sendMsg->pop();		// Clear stack of transmitted messages
			command1 = mvc[0];								// No comms outstanding
			// Reset comms status block in dialog.
			fCameraCommStat = FALSE;
			SetDlgItemText(hWndDlg, IDC_IDCOMSTAT, "Fail");
			break;	// End of case mr_Timeout:

		case mr_ClearAll:
			// A "Reset all internals" function. Empties all processing queues, clears message
			// waiting status.
			zapbuffers();									// Clear serial Tx/Rx queues
			while (!(inputMsg->empty())) inputMsg->pop();	// Clear stack of received messages
			while (!(sendMsg->empty())) sendMsg->pop();		// Clear stack of transmitted messages
			command1 = mvc[0];								// No comms outstanding
			break;	// End of case mr_ClearAll:
	}	// End of switch (reason)

}	// End of void ViscaMsg(void)


///////////////////////////////////////////////////////////////////////////////
// FUNCTION: void OpenCameraComms(void)
//
// PURPOSE:  Send comms initialise commands to the camera, also clearing any
//           existing command queue and serial comms.
//
void OpenCameraComms(void)
{
	vCmdIf	myMsg1, myMsg2;

	myMsg1 = mvc[vc_AddressSet];	// Get the AddressSet message
	myMsg2 = mvc[vc_IF_clear];		// Interface clear message

	// Ensure all comms is reset and message queues are empty
	while (!(sendMsg->empty())) sendMsg->pop();		// Clear Command Queue
	zapbuffers();	// Clear Serial RX and TX buffers

	// Add the messages to the transmit queue.
	sendMsg->push(myMsg1);
	sendMsg->push(myMsg2);
	SendMessage(hAppWnd, RXMSG_PROCESS, mr_Command, NULL);	// Alert message processor
}

// =================================================================================================

///////////////////////////////////////////////////////////////////////////////
// FUNCTION: void SendCommsTestMsg(void)
//
// PURPOSE:  Queues the message used to text link to camera for transmission.
//
void SendCommsTestMsg(void)
{
	sendMsg->push(mvc[vc_PowerInq]);						// Add power status check message to queue
	SendMessage(hAppWnd, RXMSG_PROCESS, mr_Command, NULL);	// Alert message processor
}

// =================================================================================================


///////////////////////////////////////////////////////////////////////////////
// FUNCTION: processMsgReply(unsigned int cmd_id, vCmdIf msgblk)
//
// PURPOSE:  Extracts data from the response message issued by camera reacting
//           to a command or status enquiry.
//
// COMMENTS: Uses the message id field in the current command to select the 
// required processing.
bool processMsgReply(const vCmdIf *msgblk, const RxMessage *rmsg)
{
	bool myFlag;
	bool reply = true;	// Assume there will be no problems.

	switch (msgblk->command)
	{
		case vc_AddressSet:
			// Check response packet when camera receives Address Set message.
			// Expect first 4 bytes to match, with possible exception of lower 
			// nibble of third byte. Not much that we can actually do.
			if (!((msgblk->reply[0] == rmsg->response[0])
				&& (msgblk->reply[1] == rmsg->response[1])
				&& (msgblk->reply[3] == rmsg->response[3])
				&& ((msgblk->reply[2] & 0xF0) == (rmsg->response[2] & 0xF0))))
			{
				Debug("Bad response received for Address Set command");
				reply = false;
			}
			break;

		case vc_IF_clear:
			// Check response packet when camera receives Interface Clear message.
			// Expect response to be exact copy of message.
			myFlag = true;		// Assume success
			for (int i = 0; i < msgblk->replyLength; i++)
			{
				myFlag &= (msgblk->reply[i] == rmsg->response[i]);
			}

			if (!myFlag)
			{
				Debug("Bad response received for Interface Clear command");
				reply = false;
			}
			break;

		case vc_PowerInq:
			// The Power Inquiry message is used by the comms checking system. It returns one of two
			// status answers - ON or Standby. These differ by the lsb of the third byte of the answer.
			// For this application, we only need to have the response arrive to prove the comms.
			if (!((msgblk->reply[0] == rmsg->response[0])
				&& (msgblk->reply[1] == rmsg->response[1])
				&& (msgblk->reply[3] == rmsg->response[3])
				&& ((msgblk->reply[2] & 0xFE) == (rmsg->response[2] & 0xFE))))
			{
				Debug("Bad response received for Power Enquiry");
				reply = false;		// MAY NEED TO ALWAYS BE TRUE WITH ERROR PROCESSING HERE. LOOK AT ACTIONS OF CALLING ROUTINE IN MESSAGE PROCESSOR
			}
			else
			{
				// Do the comms test status check report testing. Message arrived so comms ok.
				// Was the comms previously OK or were they broken? If there is a change in
				// status, the dialog box data must be updated, and must reset comms to camera.
				if (!fCameraCommStat)
				{
					fCameraCommStat = true;							// Now have good comms
					SetDlgItemText(hWndDlg, IDC_IDCOMSTAT, "OK");	// Set text to show now OK
					OpenCameraComms();								// Issue link reset command
				}

				// Set the timer to cause another comms poll. Wait for it to timeout and create a new TX for check message.
				reply = true;
			}
			break;

		default:
			// Comes here whe there is no response processor defined for the
			// response received. Push a message out through Debug.
			Debug("processMsgReply() missing processing for command id %d", msgblk->command);
			break;
	}

	return reply;
}


///////////////////////////////////////////////////////////////////////////////
// FUNCTION: processSlotRequest(int device, int index, const string & slot)
//
// PURPOSE:  Action commands received via the external infodriver.
//
// COMMENTS: The input parameter device is the BNCS driver ID that received the
// message. Parameter index is the Infodriver storage slot number. This value
// defines the base command message(s) that need to be sent to the camera. The
// final parameter is the character string that was stored in slot number 'index'
// and may require one or more bytes of the base command to be updated prior to
// message output to the camera.
void processSlotRequest(int device, int index, const string & slot)
{
	vCmdIf txMsg;
	int paramValue;
	int idx;

	// Validate the device (just in case).
	if (device != myDriverId) { return; }

	switch (index)
	{
		case 1:
			// Reset serial comms if slot value equals '1'
			if ((slot.length() == 1) && (slot.front() == '1'))
			{
				OpenCameraComms();
			}
			break;

			// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-

		case 3:
			// Set Iris mode. 0 = Auto, 1 = Manual
			// Check for single character and either '0' or '1'
			if ((slot.length() == 1) && ((slot.front() == '0') || (slot.front() == '1')))
			{
				if (slot.front() == '0')
				{
					txMsg = mvc[vc_AEAuto];
				}
				else
				{
					txMsg = mvc[vc_AEManual];
				}

				sendMsg->push(txMsg);
				SendMessage(hAppWnd, RXMSG_PROCESS, mr_Command, NULL);	// Invoke message processor
			}
			break;

			// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-

		case 4:
			// Set white balance mode. 0=Auto, 1=Indoor, 2=Outdoor
			paramValue = atoi(slot.c_str());
			if (paramValue < 0) paramValue = 0;
			if (paramValue > 2) paramValue = 2;
			switch (paramValue)						// Select message to use
			{
				case 0: idx = vc_WBAuto; break;
				case 1: idx = vc_WBIndoor; break;
				case 2: idx = vc_WBOutdoor; break;
			}
			sendMsg->push(mvc[idx]);
			SendMessage(hAppWnd, RXMSG_PROCESS, mr_Command, NULL);	// Invoke message processor
			break;

			// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-

		case 5:
			// Tell camera to make a one shot white balance if input value == 1
			paramValue = atoi(slot.c_str());
			if (paramValue == 1)
			{
				sendMsg->push(mvc[vc_WBOnePushMode]);
				sendMsg->push(mvc[vc_WBOnePushTrigger]);
				SendMessage(hAppWnd, RXMSG_PROCESS, mr_Command, NULL);	// Invoke message processor
			}
			break;

			// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-

		case 6:
			// Set the gain. Iris must be in manual mode for this to work. The range of values
			// and their meaning varies significantly with the camera model. This control segment
			// may therefore need more work to identify the mode required from a .ini parameter
			// The D30/31 camera supports 7 values (1..7) with gains of     0, +3, +6, +9, +12, +15, +18 dB. 
			// The BRC300 camera supports 8 values (0..7) with gains of -3, 0, +3, +6, +9, +12, +15, +18 dB. 
			// The Z700 camera supports 26 values (0x01 .. 0x19) plus 0x1A. For the 26 values the gain is
			// the value-1 dB. Parameter value 0x1A is called "Hyper"
			// At initial implementation, the supported range is 0 through 7, with inputs taken to
			// truncated values.
			paramValue = atoi(slot.c_str()) + 3;
			paramValue = (paramValue / 3);				// convert dB value into index value
			txMsg = mvc[vc_GainDirect];
			txMsg.cmdArray[7] = (BYTE)paramValue;
			sendMsg->push(txMsg);
			SendMessage(hAppWnd, RXMSG_PROCESS, mr_Command, NULL);	// Invoke message processor
			break;

			// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-

		case 8:
			// Zoom control: 0=Stop, 1=Tele_slow, 2=Wide_slow, 3=Tele_fast,	4=Wide_fast
			paramValue = atoi(slot.c_str());
			switch (paramValue)
			{
				case 0:		// Stop
					txMsg = mvc[vc_ZoomStop];
					break;

				case 1:		// Telephoto - Slow
					txMsg = mvc[vc_ZoomTeleVar];
					txMsg.cmdArray[4] = 0x20;			// Update speed control byte
					break;

				case 2:		// Wide Angle - Slow
					txMsg = mvc[vc_ZoomWideVar];
					txMsg.cmdArray[4] = 0x30;			// Update speed control byte
					break;

				case 3:		// Telephoto - Fast
					txMsg = mvc[vc_ZoomTeleVar];
					txMsg.cmdArray[4] = 0x27;			// Update speed control byte
					break;

				case 4:		// Wide angle - Fast
					txMsg = mvc[vc_ZoomWideVar];
					txMsg.cmdArray[4] = 0x37;			// Update speed control byte
					break;

				default:
					return;		// Ignore bad values
					break;
			}	// End switch (paramValue)

			sendMsg->push(txMsg);					// Queue the command
			SendMessage(hAppWnd, RXMSG_PROCESS, mr_Command, NULL);	// Alert message processor
			break;

			// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-

		case 9:
			// Focus Control: 0=Stop, 1=Near_Slow, 2= Far_Slow, 3=Near_Fast, 4=Far_Fast,
			//                6=Auto, 7=Manual
			paramValue = atoi(slot.c_str());		// Get the parameter as a number
			switch (paramValue)
			{
				case 0:		// Focus Stop
					txMsg = mvc[vc_FocusStop];
					break;

				case 1:		// Focus Near_Slow
					txMsg = mvc[vc_FocusNearVar];
					txMsg.cmdArray[4] = 0x20;		// Update the 
					break;

				case 2:		// Focus Far_Slow
					txMsg = mvc[vc_FocusFarVar];
					txMsg.cmdArray[4] = 0x30;
					break;

				case 3:		// Focus Near_Fast
					txMsg = mvc[vc_FocusNearVar];
					txMsg.cmdArray[4] = 0x27;
					break;

				case 4:		// Focus Far_Fast
					txMsg = mvc[vc_FocusFarVar];
					txMsg.cmdArray[4] = 0x37;
					break;

				case 6:		// Auto Focus
					txMsg = mvc[vc_FocusAuto];
					break;

				case 7:		// Manual Focus
					txMsg = mvc[vc_FocusManual];
					break;

				default:
					return;
					break;
			}

			sendMsg->push(txMsg);					// Queue the command
			SendMessage(hAppWnd, RXMSG_PROCESS, mr_Command, NULL);	// Alert message processor
			break;

			// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
		
		case 21:
			// Iris control when in manual iris
			// Input codes 0 to 17.
			paramValue = atoi(slot.c_str());
			if (paramValue < 0) paramValue = 0;
			if (paramValue > 17) paramValue = 17;
			txMsg = mvc[vc_IrisDirect];								// Get the raw command
			txMsg.cmdArray[6] = (BYTE)((paramValue & 0xF0) >> 4);	// Extract MS_nibble
			txMsg.cmdArray[7] = (BYTE)(paramValue & 0x0F);			// Extract LS nibble

			sendMsg->push(txMsg);									// Queue the command
			SendMessage(hAppWnd, RXMSG_PROCESS, mr_Command, NULL);	// Alert message processor
			break;

			// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-

		case 31:
			// Set PAN speed. The allowed pan speeds may vary with camera model. In this version of the 
			// driver, we allow the user to send any value from a minimum of 1 to a maximum of 255.
			// Most of the command sets seen so far have range 01 to 24 decimal
			paramValue = atoi(slot.c_str());
			if (paramValue < 1) { paramValue = 1; }			// Limit value to byte range
			if (paramValue > 255) { paramValue = 255; }
			panSpeed = (BYTE)paramValue;
			break;

			// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-

		case 32:
			// Set TILT speed. The allowed pan speeds may vary with camera model. In this version of the 
			// driver, we allow the user to send any value from a minimum of 1 to a maximum of 255.
			// D30/D31 camera supports 1..20, later models seem to have range 1..24
			paramValue = atoi(slot.c_str());
			if (paramValue < 1) { paramValue = 1; }			// Limit value to byte range
			if (paramValue > 255) { paramValue = 255; }
			panSpeed = (BYTE)paramValue;
			break;

			// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-

		case 33:
			// Pan and tilt commands. Integer values 0 through 9.
			// 0=STOP, 1=UP, 2=DOWN, 3=LEFT, 4=RIGHT, 5=UPLEFT, 6=UPRIGHT, 7=DOWNLEFT,  8=DOWNRIGHT, 9=HOME
			if ((slot.length() == 1) && (slot.front() >= '0') && (slot.front() <= '9'))
			{
				// Valid move command use value to make the action
				switch (slot.front())
				{
					case '0':
						// STOP
						txMsg = mvc[vc_PanTiltStop];	// Get the raw command from the command array
						txMsg.cmdArray[4] = panSpeed;	// Set pan speed
						txMsg.cmdArray[5] = tiltSpeed;	// Set tilt speed
						break;

					case '1':
						// UP
						txMsg = mvc[vc_PanTiltUp];
						txMsg.cmdArray[4] = panSpeed;	// Set pan speed
						txMsg.cmdArray[5] = tiltSpeed;	// Set tilt speed
						break;

					case '2':
						// DOWN
						txMsg = mvc[vc_PanTiltDown];
						txMsg.cmdArray[4] = panSpeed;	// Set pan speed
						txMsg.cmdArray[5] = tiltSpeed;	// Set tilt speed
						break;

					case '3':
						// LEFT
						txMsg = mvc[vc_PanTiltLeft];
						txMsg.cmdArray[4] = panSpeed;	// Set pan speed
						txMsg.cmdArray[5] = tiltSpeed;	// Set tilt speed
						break;

					case '4':
						// RIGHT
						txMsg = mvc[vc_PanTiltRight];
						txMsg.cmdArray[4] = panSpeed;	// Set pan speed
						txMsg.cmdArray[5] = tiltSpeed;	// Set tilt speed
						break;

					case '5':
						// UPLEFT
						txMsg = mvc[vc_PanTiltUpLeft];
						txMsg.cmdArray[4] = panSpeed;	// Set pan speed
						txMsg.cmdArray[5] = tiltSpeed;	// Set tilt speed
						break;

					case '6':
						// UPRIGHT
						txMsg = mvc[vc_PanTiltUpRight];
						txMsg.cmdArray[4] = panSpeed;	// Set pan speed
						txMsg.cmdArray[5] = tiltSpeed;	// Set tilt speed
						break;

					case '7':
						// DOWNLEFT
						txMsg = mvc[vc_PanTiltDownLeft];
						txMsg.cmdArray[4] = panSpeed;	// Set pan speed
						txMsg.cmdArray[5] = tiltSpeed;	// Set tilt speed
						break;

					case '8':
						// DOWNRIGHT
						txMsg = mvc[vc_PanTiltDownRight];
						txMsg.cmdArray[4] = panSpeed;	// Set pan speed
						txMsg.cmdArray[5] = tiltSpeed;	// Set tilt speed
						break;

					case '9':
						txMsg = mvc[vc_PanTiltHome];
						break;
				}	// End of switch (slot.front())
				sendMsg->push(txMsg);
				SendMessage(hAppWnd, RXMSG_PROCESS, mr_Command, NULL);
			}
			else
			{
				forceDebug("Invalid value for pan and tilt. Value provided was >>%s<<", slot.c_str());
				return;
			}

			break;	// Case 33

			// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-

		case 40:
			// Shot Box Set. Older cameras support 1 to 6, newest cameras have 16 shot boxes.
			// This software limits to 16, up to user not to send invalid values. Camera 
			// requires range starting at 0.
			paramValue = atoi(slot.c_str());				// Convert input string to an integer
			if (paramValue < 1) { paramValue = 1; }			// Limit value to valid range
			if (paramValue > 16) { paramValue = 16; }
			paramValue--;									// Convert BNCS value to Camera value
			txMsg = mvc[vc_MemorySet];						// Get the basic message
			txMsg.cmdArray[5] = (BYTE)paramValue;			// Update the memory number element in the message
			
			sendMsg->push(txMsg);
			SendMessage(hAppWnd, RXMSG_PROCESS, mr_Command, NULL);	
			break;	// Case 40

			// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-

		case 41:
			// Shot Box Recall. Older cameras support 1 to 6, newest cameras have 16 shot boxes.
			// This software limits to 16, up to user not to send invalid values. Camera 
			// requires range starting at 0.
			paramValue = atoi(slot.c_str());				// Convert input string to an integer
			if (paramValue < 1) { paramValue = 1; }			// Limit value to valid range
			if (paramValue > 16) { paramValue = 16; }
			paramValue--;									// Convert BNCS value to Camera value
			txMsg = mvc[vc_MemoryRecall];					// Get the basic message
			txMsg.cmdArray[5] = (BYTE)paramValue;			// Update the memory number element in the message

			sendMsg->push(txMsg);
			SendMessage(hAppWnd, RXMSG_PROCESS, mr_Command, NULL);
			break;	// Case 41

			// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-

		case 42:
			// Shot box reset. Older camera have 6 shot boxes, new cameras have more. This driver
			// limits the camera number to 16. BNCS uses 1 to 16, camera requires 0 to 15
			paramValue = atoi(slot.c_str());				// Convert input string to an integer
			if (paramValue < 1) { paramValue = 1; }			// Limit value to valid range
			if (paramValue > 16) { paramValue = 16; }
			paramValue--;									// Convert BNCS value to Camera value
			txMsg = mvc[vc_MemoryReset];					// Get the basic message
			txMsg.cmdArray[5] = (BYTE)paramValue;			// Update the memory number element in the message

			sendMsg->push(txMsg);
			SendMessage(hAppWnd, RXMSG_PROCESS, mr_Command, NULL);
			break;

		case 50:
			// Manual White Balance if input parameter = 1
			paramValue = atoi(slot.c_str());
			if (paramValue == 1)
			{
				sendMsg->push(mvc[vc_WBManual]);
				SendMessage(hAppWnd, RXMSG_PROCESS, mr_Command, NULL);
			}
			break;

			// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-

		case 51:
			// 1 = Red Gain Reset
			paramValue = atoi(slot.c_str());
			if (paramValue == 1)
			{
				sendMsg->push(mvc[vc_RedGainReset]);
				SendMessage(hAppWnd, RXMSG_PROCESS, mr_Command, NULL);
			}
			break;

			// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-

		case 52:
			// Red Gain, Values 0 to 255
			paramValue = atoi(slot.c_str());
			if (paramValue < 0) paramValue = 0;
			if (paramValue > 255) paramValue = 255;
			if ((paramValue >= 0) && (paramValue <= 255))
			{
				txMsg = mvc[vc_RedGainValue];
				txMsg.cmdArray[6] = (BYTE)((paramValue & 0xF0) >> 4);
				txMsg.cmdArray[7] = (BYTE)(paramValue & 0x0F);
				sendMsg->push(txMsg);
				SendMessage(hAppWnd, RXMSG_PROCESS, mr_Command, NULL);
			}
			break;

			// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-

		case 53:
			// Blue Gain Reset
			paramValue = atoi(slot.c_str());
			if (paramValue == 1)
			{
				sendMsg->push(mvc[vc_BlueGainReset]);
				SendMessage(hAppWnd, RXMSG_PROCESS, mr_Command, NULL);
			}
			break;

			// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-

		case 54:
			// Blue Gain Value
			paramValue = atoi(slot.c_str());
			if (paramValue < 0) paramValue = 0;
			if (paramValue > 255) paramValue = 255;
			if ((paramValue >= 0) && (paramValue <= 255))
			{
				txMsg = mvc[vc_BlueGainValue];
				txMsg.cmdArray[6] = (BYTE)((paramValue & 0xF0) >> 4);
				txMsg.cmdArray[7] = (BYTE)(paramValue & 0x0F);
				sendMsg->push(txMsg);
				SendMessage(hAppWnd, RXMSG_PROCESS, mr_Command, NULL);
			}
			break;

			// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-

		case 55:
			// Tally light on/off. 0=OFF, 1=ON
			paramValue = atoi(slot.c_str());
			if (paramValue < 0) paramValue = 1;		// Limit values
			if (paramValue > 1) paramValue = 1;
			txMsg = mvc[vc_TallyLight];				// Get basic message
			txMsg.cmdArray[6] = (paramValue == 0) ? 0x03 : 0x02;	// Update on/off control byte
			sendMsg->push(txMsg);									// Queue the message
			SendMessage(hAppWnd, RXMSG_PROCESS, mr_Command, NULL);
			break;

			// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-

		case 4095:
			// Part of a status testing loop. Data stored in slot 4095 is output in slot 4096
			setSlotContent(4096, slot.c_str(), true);
			break;

		case 4096:
			// Part of a status testing loop. Data stored in slot 4096 is output in slot 4095
			setSlotContent(4095, slot.c_str(), true);
			break;

		default:
			forceDebug("Unmanaged slot received data. Slot no %d, content >>%s<<", index, slot.c_str());
			return;
			break;
	}	// End of switch (index)
}



///////////////////////////////////////////////////////////////////////////////////////////////////
// FUNCTION: void updateSerialTxCount(UINT msgCount)
//
// PURPOSE:  Adds the value passed in msgCount to the total so far, and sends the total to the
//           dialog box.
//
// COMMENTS: Current count value is held in global memory.
//
void updateSerialTxCount(UINT msgCount)
{
	char cnt[20];		// Buffer for string version of count value.

	serialTxCount += msgCount;							// Add the count
	if (serialTxCount > 9999999999) serialTxCount = 0;	// Check for wrap around value
	sprintf_s(cnt, 20, "%010d", serialTxCount);			// Prepare the string version
	SetDlgItemText(hWndDlg, IDC_IDCAMERATX, cnt);		// Send to the dialog
}


///////////////////////////////////////////////////////////////////////////////////////////////////
// FUNCTION: void updateSerialRxCount(UINT msgCount)
//
// PURPOSE:  Adds the value passed in msgCount to the total so far, and sends the total to the
//           dialog box.
//
// COMMENTS: Current count value is held in global memory.
//
void updateSerialRxCount(UINT msgCount)
{
	char cnt[20];		// Buffer for string version of count value.

	serialRxCount += msgCount;							// Add the count
	if (serialTxCount > 9999999999) serialRxCount = 0;	// Check for wrap around value
	sprintf_s(cnt, 20, "%010d", serialRxCount);			// Prepare the string version
	SetDlgItemText(hWndDlg, IDC_IDCAMERARX, cnt);		// Send to the dialog
}

///////////////////////////////////////////////////////////////////////////////
// FUNCTION: updateInfoTxCount(UINT msgCount)
//
// PURPOSE:  Update the infodriver messages sent count in the dialog box.
//          
// RETURN:   Nothing
//
// COMMENTS:
// Current count value is in global variable 
//
void updateInfoTxCount(UINT msgCount)
{
	char cnt[20];			// Buffer for string version of count value.

	infoTxCount += msgCount;							// Add the count
	if (infoTxCount > 9999999999) infoTxCount = 0;	// Check for wrap around value
	sprintf_s(cnt, 20, "%010d", infoTxCount);		// Prepare the string version
	SetDlgItemText(hWndDlg, IDC_IDTX, cnt);			// Send to the dialog
}

///////////////////////////////////////////////////////////////////////////////
// FUNCTION: updateInfoRxCount(UINT msgCount)
//
// PURPOSE:  Update the messages received count in the dialog box.
//          
// RETURN:   Nothing
//
// COMMENTS:
// Current count value is in global variable csiRxCount 
//
void updateInfoRxCount(UINT msgCount)
{
	char cnt[20];			// Buffer for string version of count value.

	infoRxCount += msgCount;							// Add the count
	if (infoRxCount > 9999999999) infoRxCount = 0;	// Check for wrap around value
	sprintf_s(cnt, 20, "%010d", infoRxCount);		// Prepare the string version
	SetDlgItemText(hWndDlg, IDC_IDRX, cnt);			// Send to the dialog
}


///////////////////////////////////////////////////////////////////////////////
// FUNCTION: int inputHWM()
//
// PURPOSE:  Return the maximum number of messages that were stored in queue
//           awaiting processing
//          
int inputHWM(void)
{
	return inputMsg->max_used();
}


///////////////////////////////////////////////////////////////////////////////
// FUNCTION: int sendmsgHWM()
//
// PURPOSE:  Return the maximum number of messages that were stored in queue
//           awaiting processing
//          
int sendmsgHWM(void)
{
	return sendMsg->max_used();
}


///////////////////////////////////////////////////////////////////////////////
// FUNCTION: clearAllQueues(void)
//
// PURPOSE:  Delete any data in any message queues or serial buffers. Used by
//           Application exit code
//      
void clearAllQueues(void)
{
	zapbuffers();									// Flush any data in RX/TX Serial queues
	while (!(inputMsg->empty())) inputMsg->pop();	// Clear stack of received messages
	while (!(sendMsg->empty())) sendMsg->pop();		// Clear stack of transmitted messages
}