/*
PROGRAM NAME	RxTx.dll

USED BY			BBC Academy Studio C installation

Version	Date		Programmer				Comments
======= ====		==========				========
1.0		25-06-2017	Andy Woodhouse			Initial Release

TARGET SCREEN SIZE	1024 x 768
PANEL SIZE			800 x 600	(Automatic panel)

Overview
========
RxTx is a BNCS version 4.5 automatic panel used to control the switching of the GPI relays used in studio 
rehearse/transmit state indication.

It monitors the control allocation of Studio A floor, Studio C Floor and Studio C Mini-News studio switching the light states
for Studio C as defined by the allocaions and reh/tx states. A similar automatic running in CTA controls the relays
that operate studio A floor indicators.

The galleries can indicate three possible operational states via slots in the "tally" infodriver:

Value	State		Properties
=====	=====		==========
0		Off			Comms System disabled, blue lights off, red lights off.
1		Rehearse	Comms System enabled,  blue lights on,  red lights off.
2		Transmit	Comms System enabled,  blue lights off, red lights on.

There are three further slots in the "Tally Infodriver" that are used to drive indicators on this panel. Each slot indicates
which gallery is the controlling agency for the associated area. The control values are:

Value	Meaning
=====	=======
0		Area control is unallocated
1		Area is controlled by Gallery A
2		Area is controlled by Gallery C
3		Area is controlled by Gallery B (a "just in case" setting)

The infodriver slots that use the above codes and the associated areas are:

Slot	Area
====	====
4		Studio A and B floor (also red and blue lights in studio galleries)
5		Studio C floor
6		Studio C News Studio

Setting the area allocations is done by another BNCS panel.

IMPORTANT NOTICE - GPI SWITCHING
================================
During the development of this and similar modules a problem was seen in the GPI interfacing.
Further investigation is needed into the engineering causes, but a work around has been
found and implemented on the house light GPI and the RxTx GPI switching.

The response to a change of studio state from rehearse to TX requires many relays to be switched.
Withing BNCS the gpiSwitch command supports a parameter for use by CSI. If the parameter is set
"false", the command is added to the queue of commands awaiting transmission. CSI will use a timed
cyclic process to send all commands in the buffer. If the parameter is set "true" the new command
and any in the queue are sent to the network.

The first version of this module used the "false" flag for most switches, with "true" used on the
final switch command to flush the transmit buffers. The switch command can be seen in captains log,
but the GPI switch crate only switches the first relay. Setting the "true" flag for all GPOI switch
transmission comkmands produces the design intent of all relays operating.

*/

#include <windows.h>
#include <stdio.h>
#include <bncs_string.h>
#include <bncs_config.h>
#include "RxTx.h"

#define MAIN_PNL	1
#define PANEL_NAME "RxTx.bncs_ui"

#define TIMER_SETUP	1

// ============= //
// Local defines //
// ============= //

// Define the infodriver slots
#define STA_STATE_SLOT	1		// Mode of Gallery A
#define STB_STATE_SLOT	2		// Mode of Gallery B (does not exist at time of writing.
#define STC_STATE_SLOT	3		// Mode of Gallery C
#define STA_FLOOR_MODE	4		// Control mode for Studio A+B floor
#define STC_FLOOR_MODE	5		// Control mode for Studio C floor
#define STC_NEWS_MODE	6		// Control mode for Studio C Mini News Studio

// Define the value indications in the area control slots
#define STATE_OFF 0
#define STATE_REH 1
#define STATE_TX 2

// Define the control indications
#define CNTL_NONE 0
#define CNTL_STA 1
#define CNTL_STC 2
#define CNTL_STB 3

// Define the destinations to monitor for output to line
#define VIDEOLINEOUT	"StC Mixer Out to CTA"		// Instance name for video line output
#define AUDIOLINEOUT 	"StC to Line"				// Audio destination for Studio Line Output

#define VIDEOTXSOURCE	"StC Mixer Main Out"
#define AUDIOTXSOURCE	"StC Audio Main Out"

// A series of defines for GPI output offsets from the first used contact
#define GPI_STC_REH_SSR 0
#define GPI_STC_TX_SSR 1
#define GPI_STC_REH_GAL 2
#define GPI_STC_TX_GAL 3
#define GPI_STC_REH_MINI 4
#define GPI_STC_TX_MINI 5
#define GPI_STC_REH_DRAKE 6
#define GPI_STC_TX_DRAKE 7
#define GPI_STC_REH_ROT 8
#define GPI_STC_TX_ROT 9
#define GPI_STC_REH_OA3000 10
#define GPI_STC_TX_OA3000 11

// Make our class visible to the outside world
EXPORT_BNCS_SCRIPT(RxTx)

///////////////////////////////////////////////
// Constructor - equivalent to ApplCore STARTUP
RxTx::RxTx(bncs_client_callback *parent, const char *path) : bncs_script_helper(parent, path)
{
	// Initialise local variables to ensure known states at startup
	currentState_A = -1;			// Holds the current Gallery A Off/Reh/TX status
	currentState_B = -1;
	currentState_C = -1;			// Holds the current Gallery C Off/Reh/TX status
	tallyInfodriver = 0;			// Holds the BNCS driver ID for the tally infodriver
	sdiRouterdriver = 0;			// Holds the BNCS driver ID for the sdi router
	audioRouterdriver = 0;			// Holds the BNCS driver ID for the analogue audio router
	GPIdriver = 0;					// Holds the driver ID of GPI driver.
	GPIcontact_1 = 0;				// Holds the offset to the first GPI output contact.
	StA_Floor = 0;					// Holds the current control mode for Studio A floor
	StC_Floor = 0;					// Holds the current control mode for Studio C floor
	StC_News = 0;					// Holds the current control mode for News Studio C
	StC_Video_Line = 0;				// Line output destination for SDI video to CTA
	StC_Video_Line_Src = 0;			// Holds the Video line source id used in Tx
	StC_Video_Current = 0;			// Holds the current video source id that is feeding the line output
	StC_Audio_Line = 0;				// Line output destination for analogue audio to CTA
	StC_Audio_Line_Src = 0;			// Holds the audio source id for feeding line during TX
	StC_Audio_Current = 0;			// Holds the current audio source index feed audio line output
	tallyrx = 0;					// No tally infodriver data yet received.

	tallyReg = false;				// Flags that show if the drivers have been registered for revertives
	SDIReg = false;
	audioReg = false;
	processActive = false;

	offmode = "WN_Studio_OFF";		// statesheets for mimic display control
	rehmode = "WN_Studio_REH";
	txmode = "WN_Studio_TX";

	// Show our status panel
	panelShow(MAIN_PNL, PANEL_NAME);

	// Collect information about the various drivers and slots
	getDev("CTA Tally Infodriver", &tallyInfodriver);					// Tally Infodriver holds many items
	infoRegister(tallyInfodriver, STA_STATE_SLOT, STC_NEWS_MODE);		// Register for range of slots
	tallyReg = true;													// Keep a note that the infodriver is registered

	getDev(VIDEOLINEOUT, &sdiRouterdriver, &StC_Video_Line);			// Get video to line matrix id and crosspoint
	routerRegister(sdiRouterdriver, StC_Video_Line, StC_Video_Line);	// Register for revertives
	SDIReg = true;														// Keep a note that the infodriver is registered

	getDev(AUDIOLINEOUT, &audioRouterdriver, &StC_Audio_Line);			// Get audio to line matrix id and crosspoint
	routerRegister(audioRouterdriver, StC_Audio_Line, StC_Audio_Line);	// Register for revertives
	SDIReg = true;														// Keep a note that the infodriver is registered

	getDev(VIDEOTXSOURCE, &sdiRouterdriver, &StC_Video_Line_Src);		// Get source to line index value 
	// getDev(AUDIOTXSOURCE, &audioRouterdriver, &StC_Audio_Line_Src);	// Future - once Studer is controlled by BNCS

	// Read the First GPI contact details.
	getDev("Studio C Floor Blue Lights", &GPIdriver, &GPIcontact_1);

	// Fire the startup timer with a 1 second timeout. If we do not get 6 infodriver revertives before the timeout,
	// a repeat poll is issued, and a debug message output.
	timerStart(TIMER_SETUP, 1000);

	// Poll the drivers to estabish initial conditions
	routerPoll(sdiRouterdriver, StC_Video_Line, StC_Video_Line);		// Check current line output settings for video
	routerPoll(audioRouterdriver, StC_Audio_Line, StC_Audio_Line);		// and audio
	infoPoll(tallyInfodriver, STA_STATE_SLOT, STC_NEWS_MODE);			// Fetch the Mode and controlling area slot data

}


////////////////////////////////////////////////
// Destructor - equivalent to ApplCore CLOSEDOWN
RxTx::~RxTx()
{
	if (tallyReg) infoUnregister(tallyInfodriver);
	if (SDIReg) routerUnregister(sdiRouterdriver);
	if (audioReg) routerUnregister(audioRouterdriver);
}


////////////////////////////////////////////////
// All button pushes and notifications come here
void RxTx::buttonCallback(buttonNotify *b)
{
	// No buttons active in this automatic
}


////////////////////////////////////////////////////////////////////////////////////////////////////
// All revertives come here.
//
// We expect revertives from the tally infodriver, the video router and the audio router.
int RxTx::revertiveCallback(revertiveNotify *r)
{
	// Check for revertives from the video router. Only interested in the line output destination.
	if ((r->device() == sdiRouterdriver) && (r->index() == StC_Video_Line))
	{
		textPut("text", r->info(), MAIN_PNL, "vls_id");
		textPut("text", r->sInfo(), MAIN_PNL, "vls_name");
		StC_Video_Current = r->info();						// Note for when enter TX
		return 0;
	}

	// Check for revertives from the audio router. Only interested in the line output destination.
	// In curent installation output is hard wired from sound desk to CTA. So this is mostly a 
	// placeholder until control of the AES and analogue routes in the sound desk are available.
	if ((r->device() == audioRouterdriver) && (r->index() == StC_Audio_Line))
	{
		textPut("text", r->info(), MAIN_PNL, "als_id");
		textPut("text", r->sInfo(), MAIN_PNL, "als_name");
		// StC_Audio_Current = r->info();					// Note current source for when we enter TX state
		return 0;
	}

	// Is this a revertive from our range of slots in the Infodriver?
	if ((r->device() == tallyInfodriver) && (r->index() >= STA_STATE_SLOT) && (r->index() <= STC_NEWS_MODE))
	{
		// Actions depend if we have had replies from all six slots of the infodriver. If not, we just
		// store the returned value and set the bit for that slot in "tallyrx". Once all 6 slots have returned
		// data the processing actions change.
		if (processActive == false)
		{
			int ix;

			// Still acquiring tally data, so update the local variables
			ix = r->sInfo().toInt();
			switch (r->index())
			{
				case STA_STATE_SLOT:
					if ((ix >= STATE_OFF) && (ix <= STATE_TX))
					{
						currentState_A = ix;	// Update stored value
					}
					else
						debug(bncs_string("StC RxTx: Bad value for StA State=%1\n").arg(ix));
					break;	// A can use C Floor and C News

				case STB_STATE_SLOT: 
					if ((ix >= STATE_OFF) && (ix <= STATE_TX))
					{
						currentState_B = ix;
					}
					else
						debug(bncs_string("StC RxTx: Bad value for StB State=%1\n").arg(ix));
					break;	// Not actively used as B has no gallery, but ....

				case STC_STATE_SLOT:
					if ((ix >= STATE_OFF) && (ix <= STATE_TX))
					{
						currentState_C = ix;
					}
					else
						debug(bncs_string("StC RxTx: Bad value for StC State=%1\n").arg(ix));
					break;	// Managed by this automatic

				case STA_FLOOR_MODE: 
					if ((ix >= CNTL_NONE) && (ix <= CNTL_STB))
					{
						StA_Floor = ix;
					}
					else
						debug(bncs_string("StC RxTx: Bad value for StA Floor control=%1\n").arg(ix));
					break;	// Managed by this automatic

				case STC_FLOOR_MODE:
					if ((ix >= CNTL_NONE) && (ix <= CNTL_STB))
					{
						StC_Floor = ix;
					}
					else
						debug(bncs_string("StC RxTx: Bad value for StC Floor control=%1\n").arg(ix));
					break;			// C Floor can be controlled by gallery A or gallery C

				case STC_NEWS_MODE:
					if ((ix >= CNTL_NONE) && (ix <= CNTL_STB))
					{
						StC_News = ix;
					}
					else
						debug(bncs_string("StC RxTx: Bad value for StC News control=%1\n").arg(ix));
					break;			// C News can be controlled by gallery A or gallery C

			}	// End of switch (r->index())

			tallyrx |= (1 << (r->index() - 1));		// Update flag bit 
			if (tallyrx == 0x3F)					// 0x3F is binary 00111111
			{
				// All 6 slots returned values. Switch process mode variable and force set current states to relay contacts.
				processActive = true;
				switch (currentState_C)
				{
					case STATE_OFF:	setStC_OFFgpi();	break;
					case STATE_REH:	setStC_REHgpi();	break;
					case STATE_TX:	setStC_TXgpi();		break;
				}

				// Check C News and C Floor control for non-studio C selections. Set state to match control data
				if (StC_Floor == CNTL_STC)
				{
					update_C_Floor_Tally(currentState_C);
				}

				if (StC_Floor == CNTL_NONE)
				{
					Update_C_Floor_Relays(STATE_OFF);
					update_C_Floor_Tally(STATE_OFF);
				}

				if (StC_Floor == CNTL_STA)
				{
					Update_C_Floor_Relays(currentState_A);
					update_C_Floor_Tally(currentState_A);
				}

				if (StC_News == CNTL_STC)
				{
					update_C_News_Tally(currentState_C);
				}

				if (StC_News == CNTL_NONE)
				{
					Update_C_News_Relays(STATE_OFF);
					update_C_News_Tally(STATE_OFF);
				}

				if (StC_News == CNTL_STA)
				{
					Update_C_News_Relays(currentState_A);
					update_C_News_Tally(currentState_A);
				}

				// Set Studio A floor mimic
				switch (StA_Floor)
				{
					case CNTL_NONE:	update_A_Floor_Tally(STATE_OFF); break;
					case CNTL_STA:	update_A_Floor_Tally(currentState_A); break;
					case CNTL_STB:	update_A_Floor_Tally(currentState_B); break;
					case CNTL_STC:	update_A_Floor_Tally(currentState_C); break;
				}
			}
			return 0;
		}	// End of if (processActive == false)

		// ========================================================================================= //
		// We arrive at this code because the initialisation of data from Tally Infodriver has been  //
		// completed, and we need to react to changes in control. Emphasis here is CHANGES.          //
		// ========================================================================================= //
		int new_idx;
		new_idx = r->sInfo().toInt();	// Convert string value to an integer.

		switch (r->index())
		{
			case STA_STATE_SLOT:
				// Gallery A has changed it's transmission state. If either of Studio C Floor or Studio C News
				// is controlled by Gallery A, then their controls and indicators must be updated.
				if ((new_idx >= STATE_OFF) && (new_idx <= STATE_TX))	// Check the range is valid
				{
					if (currentState_A != new_idx)
					{
						currentState_A = new_idx;					// Note the new state
						if (StC_Floor == CNTL_STA)
						{
							Update_C_Floor_Relays(currentState_A);	// Set Studio C control relays
							update_C_Floor_Tally(currentState_A);	// Set Studio C mimic state on our panel.
						}
						if (StC_News == CNTL_STA)
						{
							Update_C_News_Relays(currentState_A);
							update_C_News_Tally(currentState_A);
						}
						if (StA_Floor == CNTL_STA)
						{
							update_A_Floor_Tally(currentState_A);
						}
					}
				}
				break;	// case STA_STATE_SLOT

			case STB_STATE_SLOT:
				// Gallery B has changed it's transmission mode. At present, just note the value passed.
				if ((new_idx >= STATE_OFF) && (new_idx <= STATE_TX))	// Check the range is valid
				{
					currentState_B = new_idx;
				}
				break;

			case STC_STATE_SLOT:
				if ((new_idx >= STATE_OFF) && (new_idx <= STATE_TX))	// Check the range is valid
				{
					if (currentState_C != new_idx)
					{
						currentState_C = new_idx;							// Save updated value
						switch (new_idx)
						{
							case STATE_OFF:
								setStC_OFFgpi();	// Sets/clears relays for StC Floor and StC News if allocated to gallery C
								break;
							case STATE_REH:
								setStC_REHgpi();	// Sets/clears relays for StC Floor and StC News if allocated to gallery C
								break;
							case STATE_TX:
								setStC_TXgpi();		// Sets/clears relays for StC Floor and StC News if allocated to gallery C
								setOutputToLine();	// Makes sure correct sources are routed to line
								break;
						}
						if (StC_Floor == CNTL_STC)
						{
							update_C_Floor_Tally(new_idx);
						}
						if (StC_News == CNTL_STC)
						{
							update_C_News_Tally(new_idx);
						}
						if (StA_Floor == CNTL_STC)
						{
							update_A_Floor_Tally(currentState_C);
						}
					}
				}
				break;

			case STA_FLOOR_MODE:
				// Gallery controlling Studio A (and currently Studio B) floor has changed
				// Red/Blue light hardware is controlled by an automatic panel in CTA.
				if ((new_idx >= CNTL_NONE) && (new_idx <= CNTL_STB))
				{
					if (StA_Floor != new_idx)
					{
						StA_Floor = new_idx;
						switch (new_idx)
						{
							case CNTL_NONE:	update_A_Floor_Tally(STATE_OFF); break;
							case CNTL_STA:	update_A_Floor_Tally(currentState_A); break;
							case CNTL_STC:	update_A_Floor_Tally(currentState_C); break;
							case CNTL_STB:	update_A_Floor_Tally(currentState_B); break;
						}
					}
				}
				break;	// case STA_FLOOR_MODE:

			case STC_FLOOR_MODE:
				// Gallery controlling Studio C floor has changed
				if ((new_idx >= CNTL_NONE) && (new_idx <= CNTL_STB))
				{
					if (StC_Floor != new_idx)
					{
						StC_Floor = new_idx;
						switch (new_idx)
						{
							case CNTL_NONE:
								Update_C_Floor_Relays(CNTL_NONE);
								update_C_Floor_Tally(CNTL_NONE);
								break;
							case CNTL_STA:
								Update_C_Floor_Relays(currentState_A);
								update_C_Floor_Tally(currentState_A);
								break;
							case CNTL_STC:
								Update_C_Floor_Relays(currentState_C);
								update_C_Floor_Tally(currentState_C);
								break;
							case CNTL_STB:
								Update_C_Floor_Relays(currentState_B);
								update_C_Floor_Tally(currentState_B);
								break;
						}
					}
				}
				break;

			case STC_NEWS_MODE:
				// Gallery controlling Studio C News has changed
				if ((new_idx >= CNTL_NONE) && (new_idx <= CNTL_STB))
				{
					if (StC_News != new_idx)
					{
						StC_News = new_idx;
						switch (new_idx)
						{
							case CNTL_NONE:
								Update_C_News_Relays(CNTL_NONE);
								update_C_News_Tally(CNTL_NONE);
								break;
							case CNTL_STA:
								Update_C_News_Relays(currentState_A);
								update_C_News_Tally(currentState_A);
								break;
							case CNTL_STC:
								Update_C_News_Relays(currentState_C);
								update_C_News_Tally(currentState_C);
								break;
							case CNTL_STB:
								Update_C_News_Relays(currentState_B);
								update_C_News_Tally(currentState_B);
								break;
						}
					}
				}
				break;
		}

		return 0;
	}	// End of if (r->device() == ......

	// A catch all	
	return 0;
}


///////////////////////////////////////////
// All database name changes come back here
void RxTx::databaseCallback(revertiveNotify *r)
{
}


//////////////////////////////////////////////////////////////////////////
// All parent notifications come here i.e. when this script is just one 
// component of another dialog then our host might want to tell us things.
bncs_string RxTx::parentCallback(parentNotify *p)
{
	if (p->command() == "return")
	{
		if (p->value() == "all")
		{	
			// Persisting values for bncs_vis_ed
			bncs_stringlist sl;
			
			sl << bncs_string("");
			
			return sl.toString( '\n' );
		}

	}


	// ***** CONNECTIONS EVENTS HELPER LIST *****
	else if (p->command() == "_events")
	{	
		// Helper-list of everything in this component generated by hostNotify's
		bncs_stringlist sl;

		sl << "notify=*";		
		
		return sl.toString( '\n' );
	}

	// ***** CONNECTIONS COMMANDS HELPER LIST *****
	else if (p->command() == "_commands")
	{	
		// Helper-list of any commands/parameters you might want to set at run-time
		bncs_stringlist sl;
		
		sl << "";
		
		return sl.toString( '\n' );
	}

	return "";
}


//////////////////////////
// Timer events come here.
void RxTx::timerCallback( int id )
{
	switch( id )
	{
	case TIMER_SETUP:
		// Fired at startup with 1 second delay. If we have not received all tally infodriver revertives
		// when timer fires, re-issue the poll for data. 
		if (processActive)
		{
			timerStop(id);	// We have seen revertives from all 6 slots that are of interest to us.
		}
		else
		{
			infoPoll(tallyInfodriver, STA_STATE_SLOT, STC_NEWS_MODE);
			debug("StC TxTx: Issued re-poll of slots 1 to 6 in Tally Infodriver. Failed to receive revertives within 1 second.\n");
		}
		break;

	default:	// Unhandled timer event
		timerStop(id);
		break;
	}
}


///////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////// Callbacks above - Methods below ///////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////


////////////////////////////////////////////////////////////////////////////////////////////////////
// function void RxTx::setStC_TXgpi(void) is called to set the GPI relays to the correct states to
// indicate Studio C Gallery and other areas in studio C that may be under Gallery C control are
// set to indicate transmission state is active.
void RxTx::setStC_TXgpi(void)
{
	// Sets the GPI relay states needed for Studio C Gallery and registered areas Transmission mode
	if (StC_Floor == CNTL_STC)		// Check Studio C floor control allocation
	{
		gpiSwitch(GPIdriver, false, GPIcontact_1 + GPI_STC_REH_SSR, true);	// Solid state relay, blue lights
		gpiSwitch(GPIdriver, true, GPIcontact_1 + GPI_STC_TX_SSR, true);	// Solid state relay, red lights
	}

	if (StC_News == CNTL_STC)		// Check the mini-studio control allocation
	{
		gpiSwitch(GPIdriver, false, GPIcontact_1 + GPI_STC_REH_MINI, true);// LED Sign, blue lights
		gpiSwitch(GPIdriver, true, GPIcontact_1 + GPI_STC_TX_MINI, true);	// LED Sign, red lights
	}

	// These contacts are always switched
	gpiSwitch(GPIdriver, false, GPIcontact_1 + GPI_STC_REH_GAL, true);		// Gallery LED Sign, blue lights
	gpiSwitch(GPIdriver, true, GPIcontact_1 + GPI_STC_TX_GAL, true);		// Gallery LED Sign, red lights
	gpiSwitch(GPIdriver, false, GPIcontact_1 + GPI_STC_REH_DRAKE, true);	// Drake comms rehearse enable contact
	gpiSwitch(GPIdriver, true, GPIcontact_1 + GPI_STC_TX_DRAKE, true);		// Drake comms transmit enable contact
	gpiSwitch(GPIdriver, false, GPIcontact_1 + GPI_STC_REH_ROT, true);		// Digital Rapids ROT recorder stop control
	gpiSwitch(GPIdriver, true, GPIcontact_1 + GPI_STC_TX_ROT, true);		// Digital Rapids ROT recorder record enble
	gpiSwitch(GPIdriver, false, GPIcontact_1 + GPI_STC_REH_OA3000, true);	// Studio C OnAir 3000 sound desk
	gpiSwitch(GPIdriver, true, GPIcontact_1 + GPI_STC_TX_OA3000, true);		// Studio C OnAir 3000 sound desk

}	// End of function void RxTx::setStC_TXgpi(void)
	

////////////////////////////////////////////////////////////////////////////////////////////////////
// function void RxTx::setStC_REHgpi(void) is called to set the GPI relays to the correct states to
// indicate Studio C Gallery and other areas in studio C that may be under Gallery C control are
// set to indicate rehearsal state is active.
void RxTx::setStC_REHgpi(void)
{
	// Sets the GPI relay states needed for Rehearse mode active
	if (StC_Floor == CNTL_STC)		// Check Studio C floor control allocation
	{
		gpiSwitch(GPIdriver, true, GPIcontact_1 + GPI_STC_REH_SSR, true);	// Solid state relay, blue lights
		gpiSwitch(GPIdriver, false, GPIcontact_1 + GPI_STC_TX_SSR, true);	// Solid state relay, red lights
	}

	if (StC_News == CNTL_STC)		// Check the mini-studio control allocation
	{
		gpiSwitch(GPIdriver, true, GPIcontact_1 + GPI_STC_REH_MINI, true);// LED Sign, blue lights
		gpiSwitch(GPIdriver, false, GPIcontact_1 + GPI_STC_TX_MINI, true);	// LED Sign, red lights
	}

	// These contacts are always switched
	gpiSwitch(GPIdriver, true, GPIcontact_1 + GPI_STC_REH_GAL, true);		// Gallery LED Sign, blue lights
	gpiSwitch(GPIdriver, false, GPIcontact_1 + GPI_STC_TX_GAL, true);		// Gallery LED Sign, red lights
	gpiSwitch(GPIdriver, true, GPIcontact_1 + GPI_STC_REH_DRAKE, true);	// Drake comms rehearse enable contact
	gpiSwitch(GPIdriver, false, GPIcontact_1 + GPI_STC_TX_DRAKE, true);	// Drake comms transmit enable contact
	gpiSwitch(GPIdriver, true, GPIcontact_1 + GPI_STC_REH_ROT, true);		// Digital Rapids ROT recorder stop control
	gpiSwitch(GPIdriver, false, GPIcontact_1 + GPI_STC_TX_ROT, true);		// Digital Rapids ROT recorder record enble
	gpiSwitch(GPIdriver, true, GPIcontact_1 + GPI_STC_REH_OA3000, true);	// Studio C OnAir 3000 sound desk
	gpiSwitch(GPIdriver, false, GPIcontact_1 + GPI_STC_TX_OA3000, true);	// Studio C OnAir 3000 sound desk
}	// End of function void RxTx::setStC_REHgpi(void)


////////////////////////////////////////////////////////////////////////////////////////////////////
// function void RxTx::setStC_OFFgpi(void) is called to set the GPI relays to the correct 
// states to indicate Studio C Gallery and other areas in studio C that may be under Gallery C
// control are set to indicate neither rehearse nor transmission are active.
void RxTx::setStC_OFFgpi(void)
{
	// There are 12 GPI output contacts, 6 associated with REH, 6 with TX
	// The Studio C floor aqnd Studio C Mini contacts should only be cleared if these areas are under the control of gallery C
	if (StC_Floor == CNTL_STC)		// Check Studio C floor control allocation
	{
		gpiSwitch(GPIdriver, false, GPIcontact_1 + GPI_STC_REH_SSR, true);	// Solid state relay, blue lights
		gpiSwitch(GPIdriver, false, GPIcontact_1 + GPI_STC_TX_SSR, true);	// Solid state relay, red lights
	}

	if (StC_News == CNTL_STC)		// Check the mini-studio control allocation
	{
		gpiSwitch(GPIdriver, false, GPIcontact_1 + GPI_STC_REH_MINI, true);// LED Sign, blue lights
		gpiSwitch(GPIdriver, false, GPIcontact_1 + GPI_STC_TX_MINI, true);	// LED Sign, red lights
	}

	// These contacts are always switched
	gpiSwitch(GPIdriver, false, GPIcontact_1 + GPI_STC_REH_GAL, true);		// Gallery LED Sign, blue lights
	gpiSwitch(GPIdriver, false, GPIcontact_1 + GPI_STC_TX_GAL, true);		// Gallery LED Sign, red lights
	gpiSwitch(GPIdriver, false, GPIcontact_1 + GPI_STC_REH_DRAKE, true);	// Drake comms rehearse enable contact
	gpiSwitch(GPIdriver, false, GPIcontact_1 + GPI_STC_TX_DRAKE, true);	// Drake comms transmit enable contact
	gpiSwitch(GPIdriver, false, GPIcontact_1 + GPI_STC_REH_ROT, true);		// Digital Rapids ROT recorder stop control
	gpiSwitch(GPIdriver, false, GPIcontact_1 + GPI_STC_TX_ROT, true);		// Digital Rapids ROT recorder record enble
	gpiSwitch(GPIdriver, false, GPIcontact_1 + GPI_STC_REH_OA3000, true);	// Studio C OnAir 3000 sound desk
	gpiSwitch(GPIdriver, false, GPIcontact_1 + GPI_STC_TX_OA3000, true);	// Studio C OnAir 3000 sound desk.
}	// End of function void RxTx::clearStC_TX_REHgpi(void)


////////////////////////////////////////////////////////////////////////////////////////////////////
// function void RxTx::setOutputToLine(void) is called to set the video and audio line outputs
// to standard values when studio enters Tx mode. The crosspoint IDs are read from the instances 
// file at startup.
void RxTx::setOutputToLine(void)
{
	if (StC_Video_Current != StC_Video_Line_Src)
	{
		routerCrosspoint(sdiRouterdriver, StC_Video_Line_Src, StC_Video_Line, "", true);
	}

	/*
	if (StC_Audio_Current != StC_Audio_Line_Src)
	{
		routerCrosspoint(audioRouterdriver, StC_Audio_Line_Src, StC_Audio_Line, "", true);
	}
	*/
}

////////////////////////////////////////////////////////////////////////////////////////////////////
// function void RxTx::Update_C_Floor_Relays(int TX_State) is called to set the rehearse and 
// transmit relays for Studio C floor according to the parameter TX_State. These are controlled
// through the solid state relays in the studio C dimmer room.
void RxTx::Update_C_Floor_Relays(int TX_State)
{
	switch (TX_State)
	{
		case STATE_OFF:
			gpiSwitch(GPIdriver, false, GPIcontact_1 + GPI_STC_REH_SSR, true);
			gpiSwitch(GPIdriver, false, GPIcontact_1 + GPI_STC_TX_SSR, true);
			break;

		case STATE_REH: 
			gpiSwitch(GPIdriver, true, GPIcontact_1 + GPI_STC_REH_SSR, true);
			gpiSwitch(GPIdriver, false, GPIcontact_1 + GPI_STC_TX_SSR, true);
			break;

		case STATE_TX:
			gpiSwitch(GPIdriver, false, GPIcontact_1 + GPI_STC_REH_SSR, true);
			gpiSwitch(GPIdriver, true, GPIcontact_1 + GPI_STC_TX_SSR, true);
			break;
	}
}


////////////////////////////////////////////////////////////////////////////////////////////////////
// function void RxTx::Update_C_Floor_Relays(int TX_State) is called to set the rehearse and 
// transmit relays for Studio C News floor according to the parameter TX_State
void RxTx::Update_C_News_Relays(int TX_State)
{
	switch (TX_State)
	{
		case STATE_OFF: 
			gpiSwitch(GPIdriver, false, GPIcontact_1 + GPI_STC_REH_MINI, true);// LED Sign, blue lights
			gpiSwitch(GPIdriver, false, GPIcontact_1 + GPI_STC_TX_MINI, true);	// LED Sign, red lights
			break;

		case STATE_REH:
			gpiSwitch(GPIdriver, true, GPIcontact_1 + GPI_STC_REH_MINI, true);	// LED Sign, blue lights
			gpiSwitch(GPIdriver, false, GPIcontact_1 + GPI_STC_TX_MINI, true);	// LED Sign, red lights
			break;

		case STATE_TX:
			gpiSwitch(GPIdriver, false, GPIcontact_1 + GPI_STC_REH_MINI, true);// LED Sign, blue lights
			gpiSwitch(GPIdriver, true, GPIcontact_1 + GPI_STC_TX_MINI, true);	// LED Sign, red lights
			break;
	}
}	// End of void RxTx::Update_C_News_Relays(int TX_State)


////////////////////////////////////////////////////////////////////////////////////////////////////
// function void RxTx::update_C_Floor_Tally(int newstate) is called to update the visual mimic 
// panel display of the status of Studio C floor.  
void RxTx::update_C_Floor_Tally(int newstate)
{
	bncs_string *ns;

	switch (newstate)
	{
		case STATE_OFF:
			ns = &offmode;
			break;
	
		case STATE_REH:
			ns = &rehmode;
			break;
		
		case STATE_TX:
			ns = &txmode;
			break;
	}

	textPut("statesheet", *ns, MAIN_PNL, "StC_Floor");
}


////////////////////////////////////////////////////////////////////////////////////////////////////
// 
void RxTx::update_C_News_Tally(int newstate)
{
	bncs_string *ns;

	switch (newstate)
	{
		case STATE_OFF:
			ns = &offmode;
			break;

		case STATE_REH:
			ns = &rehmode;
			break;

		case STATE_TX:
			ns = &txmode;
			break;
	}

	textPut("statesheet", *ns, MAIN_PNL, "StC_News");
}


////////////////////////////////////////////////////////////////////////////////////////////////////
// function void RxTx::update_A_Floor_Tally(int newstate) sets local panel mimic to show Studio A
// Floor controlled state
void RxTx::update_A_Floor_Tally(int newstate)
{
	bncs_string *ns;

	switch (newstate)
	{
		case STATE_OFF:
			ns = &offmode;
			break;

		case STATE_REH:
			ns = &rehmode;
			break;

		case STATE_TX:
			ns = &txmode;
			break;
	}

	textPut("statesheet", *ns, MAIN_PNL, "StA_Floor");
}