//---------------------------------------------------------------------------
/*
Remote Train Control Program for Windows

© Copyright 2015 by Mark DiVecchio

    This file is part of Remote Train Control.

    Remote Train Control is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    Remote Train Control is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with Remote Train Control.  If not, see <http://www.gnu.org/licenses/>.

*/
// Home Page : http://www.silogic.com/trains/RTC_Running.html
//---------------------------------------------------------------------------
#include <vcl.h>
#include <tchar.h>
#pragma hdrstop

#include "RTCMegaMaster.h"		// Dispatch List

#include "Engine_Commands.h"
#include "Train_Control.h"
#include "Op_Control.h"
#include "Volume_Control.h"
#include "Setup_Control.h"
#include "Debug_Controls.h"
#include "Message_Popup.h"
#include "Quick_Controls.h"
#include "Track_Signal_Thread.h"
#include "Track_Signal.h"
#include "Engine_Information_Thread.h"
#include "Engine_Info.h"
#include "Engine_Status_Thread.h"
#include "Engine_Status.h"
#include "Comm_Thread.h"
#include "DL_Thread.h"
#include "Clipbrd.hpp"

//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma link "_GClass"
#pragma link "AbCBitBt"
#pragma link "AbSwitch"
#pragma link "AbVSlide"
#pragma link "AbHSlide"
#pragma link "HideButton"
#pragma link "AbDial"
#pragma link "AbLED"
#pragma link "AbFlashT"
#pragma link "AbVBar"
#pragma link "ColorButton"
#pragma resource "*.dfm"

// NOTE: this is a public static variable, you can never use
// this when you create multiple instances of a Form
//TOp *Op;

//---------------------------------------------------------------------------
// Global vars for Op
TAbRockerSwitch * LINSwitch;	// pointer to Interior Light Switch for Simulated Rule 17
//---------------------------------------------------------------------------
extern TQuickCtrl * ptrQuick[QUICKPAGEMAX];			// Quick Control Pointers
extern TVolume * ptrVolume[ABSOLUTEMAX];	        // Volume Control Pointers - indexed by TIUNo-1 and actual engine number because 0 is a factory reset engine
extern TEngineInfo * ptrEngineInfo[ABSOLUTEMAX];	// Engine Information Pointers - indexed by actual engine number (0-99) because 0 is a factory reset engine
extern TEngineStatus * ptrEngineStatus[ABSOLUTEMAX];	// Engine Status Pointers - indexed by actual engine number (0-99) because 0 is a factory reset engine

extern TCommThread * ptrTIUCommThread;				// pointer to TIU communications thread
extern TDispatch_List_Thread * ptrDL_Thread;		// pointer to Display List Thread

extern bool SuperTIUMode, TIUSuperMode[TIUMAX];     // Flags to indicate if Super TIU Mode is active

//---------------------------------------------------------------------------
__fastcall TOp::TOp(TComponent* Owner)
    : TForm(Owner)
{
assert(false);	// never called since I used the overloaded constructor below
}
//---------------------------------------------------------------------------
__fastcall TOp::TOp(TComponent* Owner, int Remote, int Consist, int TIU, String ConName, char * ptrLUEngineList)
    : TForm(Owner)
{
// grab the Remote, Engine/LashUp, TIU numbers and Consist Name and LashUpEngineList
RemoteNo = Remote;
assert(RemoteNo >= 0 && RemoteNo <= REMOTEMAX);
ConsistNo =  Consist;
assert(ConsistNo >= (Setup->AllowZero->Checked? 0 : 1) && ConsistNo <= LASHUPMAX);	// 0 FACTORYRESET, 1-99, 100 "all engine operation, 101-120 LashUps
TIUNo = TIU;
assert(TIUNo >= 1 && TIUNo <= TIUMAX);
// Consist Name is either the engine name for engine operations or the lashup name for lashup operations
ConsistName = ConName;
// LashUp must have a LashUpEngineList
assert((ConsistNo >= LASHUPMIN && ptrLUEngineList != NULL) || (ConsistNo <= ALLENGINE && ptrLUEngineList == NULL));
LashUpEngineListOp = NULL;

LocalTransparencyOverride = false;
// Required for multiple monitors and save/restore position/size
Position=poDesigned;
DefaultMonitor=dmDesktop;
//
// read Preferences from registry if they exist
RegFileRootKey =  MainForm->RegFileRootKey;
TRegIniFile * const RegFile = new TRegIniFile(RegFileRootKey + "\\Operations");
assert(RegFile != NULL);
__try {
    // read window position
    Top = RegFile->ReadInteger("Op" + String(ConsistNo), "Top", Top);
    Left = RegFile->ReadInteger("Op" + String(ConsistNo), "Left", Left);
    // if using multiple monitors and one was removed, pull the window
    // back onto the remaining monitor
    int MC = Screen->MonitorCount;
    if (MC == 1) {
        if (Left < 0 ) Left = 0;
        int Wth = Screen->Monitors[0]->Width;
		if (Left > Wth) Left -= Wth;
        if (Top < 0 ) Top = 0;
        int Hth = Screen->Monitors[0]->Height;
        if (Top > Hth) Top -= Hth;
        }
	}
__finally {
	// Deallocate RegFile
	delete RegFile;
	}
//

if (ConsistNo <= ENGINEMAX) {	// Engines Factory Reset, 1-99
	// Normal Engine
    HeadEngineNo = ConsistNo;	// use engine number for Lead Engine Number
    // get the lead engine of a lashup or the engine name for single engine operations
    HeadEngineName = MainForm->EngineData->Cells[ENG_NAME][HeadEngineNo];
   	// no LashUpEngineList for regular engines
    String PSNote = " - ";
	if (MainForm->EngineData->Cells[ENG_PS][HeadEngineNo] == PS2) PSNote = " (PS2) ";
	if (MainForm->EngineData->Cells[ENG_PS][HeadEngineNo] == PS3) PSNote = " (PS3) ";
    Operation_Controls_Label->Caption = "Engine " + String(HeadEngineNo) + PSNote +
        ConsistName + (SuperTIUMode && TIUSuperMode[TIUNo-1] ? String(" SuperTIUMode") : " - TIU " + String(TIUNo));
    }
//
if (ConsistNo == ALLENGINE) {   // All Engine operation 100
    HeadEngineNo = 0;       // set lead engine number to zero
    HeadEngineName = "All Engine";
    // some invalid menu selections
    EngInfo->Visible = false;
    EngStat->Visible = false;
    TrkSignal->Visible = false;
    SetEnginePS2->Visible = false;
    SetEnginePS3->Visible = false;
    FeatureResetButton->Visible = false;
    FeatureResetMenu->Visible = false;
    Operation_Controls_Label->Caption = "All Engine Operation" +
		(SuperTIUMode && TIUSuperMode[TIUNo-1] ? String(" SuperTIUMode") : " - TIU " + String(TIUNo));
    }
    //
if (ConsistNo >= LASHUPMIN) {    // LashUps  101-120
    // parse the LashupEngineList and get the lead engine number
    LashUpEngineListOp = new char [LASHUP_ENGINE_LIST_LENGTH];
    assert(LashUpEngineListOp != NULL);
    assert(ptrLUEngineList[0] != '\0');
    strncpy(LashUpEngineListOp, ptrLUEngineList, LASHUP_ENGINE_LIST_LENGTH);	// LashUp information
    // MCD Consist number is 101-120
    Operation_Controls_Label->Caption = "LashUp " + String(ConsistNo) + " - " +
		ConsistName + (SuperTIUMode && TIUSuperMode[TIUNo-1] ? String(" SuperTIUMode") : " - TIU " + String(TIUNo));
    //
	// use DCS Engine Number of first engine in LashUp list
    HeadEngineNo = MainForm->GetLashUpEngine(LashUpEngineListOp, 0);  // get head engine
    //  ---------------------------------------------------------------------
    // get the lead engine of a lashup or the engine name for single engine operations
    HeadEngineName = MainForm->EngineData->Cells[ENG_NAME][HeadEngineNo];
    // change some menu items
    EngInfo->Caption = "Engine #" + String(HeadEngineNo) + " Information";
    EngStat->Caption = "Engine #" + String(HeadEngineNo) + " Status";
    TrkSignal->Caption = "Engine #" + String(HeadEngineNo) + " Track Signal (Ping)";
    // some invalid menu selections
    SetEnginePS2->Visible = false;
    SetEnginePS3->Visible = false;
    FeatureResetButton->Visible = false;
    FeatureResetMenu->Visible = false;
    }

this->Caption = "Controls for " + Operation_Controls_Label->Caption;


// pointers to Quick Control (if any)
ptrQuick_Dir = NULL;
ptrQuick_Bell = NULL;
ptrQuick_Smoke = NULL;
ptrQuick_Speed = NULL;
ptrQuick_EngNumber = NULL;
ptrQuick_RunState = NULL;	// pointer to an array indexed by Engine Number
ptrQuick_Sound = NULL;

// expanded to Whistle and Coupler Buttons
ptrQuick_Whistle = NULL;
ptrQuick_Horn = NULL;
ptrQuick_SFS = NULL;
ptrQuick_SRS = NULL;
ptrQuick_SXS = NULL;
ptrQuick_CPLRF = NULL;
ptrQuick_CPLRR = NULL;

// pointers to Volume Control Window when it is created (it's never there before the Operations Window).
ptrVol_EngineSoundOnSwitch = NULL;		// Sound Switch
ptrVol_MasterVolume = NULL;				// Master Volume

// pointers to Track Signal (pinging) thread
MyTrackSignal = NULL;

// set pointers when creating the Operations window if the Quick Control window
// is displayed and if our TIU and Engine Number are in there
for (int iPage = 1; iPage <= QUICKPAGEMAX ; iPage++) { // Quick Controls pages 1-5
    if (ptrQuick[iPage - 1]) {
        TQuickCtrl * pQuick = ptrQuick[iPage - 1];
        int iCnt = pQuick->TotalCtrlCount;              // number of controls on the page
        for (int index = 0 ; index < iCnt ; index++) {	
            if (pQuick->EngNumberContainer[index]->Text.ToInt() == ConsistNo) {
                ptrQuick_Dir = pQuick->DIRToggleContainer[index];
                ptrQuick_Bell = pQuick->BellSwitchContainer[index];
                ptrQuick_Smoke = pQuick->SmokeSwitchContainer[index];
                ptrQuick_Speed = pQuick->SpeedEditContainer[index];
                ptrQuick_EngNumber = pQuick->EngNumberContainer[index];
                ptrQuick_RunState = pQuick->RunningState;	// pointer to an array indexed by Engine Number
                ptrQuick_Sound = pQuick->VolumeSliderContainer[index];

                // expanded to Whistle and Coupler Buttons
                ptrQuick_Whistle = pQuick->WhistleButtonContainer[index];
                ptrQuick_Horn = pQuick->HornButtonContainer[index];
                ptrQuick_SFS = pQuick->SFSButtonContainer[index];
                ptrQuick_SRS = pQuick->SRSButtonContainer[index];
                ptrQuick_SXS = pQuick->SXSButtonContainer[index];
                ptrQuick_CPLRF = pQuick->CPLRFButtonContainer[index];
                ptrQuick_CPLRR = pQuick->CPLRRButtonContainer[index];
                break;                                                                   	
                }
            }
        }
    }

MotionSoundRequest->Checked = true;	// request sound on first train movement
LastSentSpeedValue = 0;             // Always start at zero MPH

// Experimental - use Drop Down Menu style in CB10.1
#if __BORLANDC__ >= 0x720
enum DECLSPEC_DENUM TButtonStyle : unsigned char { bsPushButton, bsCommandLink, bsSplitButton };
// bsSplitButton defined in Vcl.StdCtrls.hpp is really not defined
SoundControlsButton->Style = (TCustomButton::TButtonStyle)bsSplitButton;
SoundControlsButton->DropDownMenu = OpPopup;
#endif

// SavedMasterVolumeValue is set in Train Control.cpp as Sound Controls window is started

// show the Auto Bell/Horn/Whistle switches only if master enable is on in setup
AutoBellLED->Visible = Setup->AutoBell->Checked;
AutoWhistleLED->Visible = Setup->AutoWhistle->Checked;

//
// Set Maximum Speed
MaxSpeedSpin->MaxValue = Setup->MaxSpeed->Value;
MaxSpeedSpin->Value = Setup->MaxSpeed->Value;
SpeedSlider->SignalSettings->DigitalTo = MaxSpeedSpin->Value;
SpeedSlider->SignalSettings->ValueTo = MaxSpeedSpin->Value;
SpeedSlider->ScaleSettings->Steps = MaxSpeedSpin->Value / 10;
//
// bug in the slider causes it to start out at 10 Smph
// this sets the slider to zero without sending a "d0" or "s0" command
void __fastcall (__closure *SpeedSliderOnValueChange)(TObject *Sender);
SpeedSliderOnValueChange = SpeedSlider->OnValueChange;
SpeedSlider->OnValueChange = NULL;
SpeedSlider->Value = 0;
SpeedSlider->OnValueChange = SpeedSliderOnValueChange;
//
bLaboredSmoke = false; 	// Initially, labored smoke/rev exhaust state is off

if (Setup->DisableEmergencyStopButton->Checked) {
	ESTOP->Visible = false;
    ESTOP->Enabled = false;
    ESTOP->Hint = "Disabled in Setup";
    ESTOP->Cursor = crNo;
    }

if (DEBUGGING) {
	// Make these controls visible only in debug mode
	ShowEngineCommands1->Visible = true;		// Special Engine Command Testing Menu
    EC = NULL;
   	MotionSoundRequest->Visible = true;
    //QuillButton->Visible = true;
	DelayTest1->Visible = true;
    }
//
LastSetSpeed = 0;	// start up stopped
//
// Tags are set depending on which function is loaded for each switch
// See the #define SWITCH_* in the header file Op_Control.h
//
// If All Engine Operation, skip the rest of the setup
if (ConsistNo == ALLENGINE) {
	HeadEngineNo = 0;
	return;
    }
//
// Switch Tags - indicate programmable switch is not used
ab3ab2Switch->Tag = 0;	// Interior Programmable Switch
ab5ab4Switch->Tag = 0;	// Mars Programmable Switch
ab7ab6Switch->Tag = 0;  // Ditch Programmable Switch
//
// Errors in Engines
//
// There are errors in the responses to the Ixxx command from some engines.
// So far, it has been missing hotkey flags (unless they are there and I don't process them
// correctly) or extra hotkey flags.
//
// This should at some point be moved into an external file where it can be
// modified by users
//
LINSwitch = NULL;
bool DitchDisable = false;
if (ConsistName.TrimRight() == "P&LE GP38-2#2057" ||
    ConsistName.TrimRight() == "P&LE GP38-2#2060" ||
    ConsistName.TrimRight() == "P&LE GP38-2#2061") {	// compare
    DitchDisable = true;
	}
bool MarkerOverride = false, IntIntOverride = false;
if (ConsistName.TrimRight() == "P&LE GP7 #1501" ||
    ConsistName.TrimRight() == "P&LE GP7 #1500") {	// compare
	MarkerOverride = true;
    IntIntOverride = true;
    }
bool MarkerDisable = false;
if (ConsistName.TrimRight() == "NYC Berk #9401") {	// compare
	#ifdef NOTNEEDED_ISWAPPEDTHEPLUGSINSIDEOFTHEENGINE
    assert(falsex);
	// Swap the HDLT and Interior event handlers
    // this was deleted because I discovered a plugging error inside of the
    // engine where the HDLT and Interior plugs were interchanged. I moved
    // the plugs into the correct jacks.
    TNotifyEvent TempStatusChanged;
    TempStatusChanged = ab3ab2Switch->OnStatusChanged;
    ab3ab2Switch->OnStatusChanged = HDLTSwitch->OnStatusChanged;
    HDLTSwitch->OnStatusChanged = TempStatusChanged;
    #endif
    // Disable the Marker Switch as the Interior switch controls the markers
    MarkerDisable = true;
    }

// Setup last master volume setting
LastSentMasterVolumeValue = SavedMasterVolumeValue = MainForm->EngineData->Cells[VOL_MASTER][HeadEngineNo].ToIntDef(100);

if (Setup->Radio_COM_Mode && Setup->TXOnlyComm->Checked) {	//  In TX Only Mode, we can't get softkeys
	if (DEBUGGING) DB->Debug_Print("Hotkey : No Hotkeys set - TX Only Mode", clRed);
    } else {
    //
    // Activate Hot Keys per engine response
    //
    // NOTE: EngineNo in the 'I' command does not seem to matter
    // NOTE: The Engine Number as digits in the 'I' command do matter
    //
	byte SWbyteResp[CMDMAX];
    byte byteResp[CMDMAX];  // Array for decoded binary response
    int iTIUCount;			// integer for response count
    int respCode;			// integer for comm response code
	char Command[10];
	(void) snprintf(Command, sizeof Command, "I%d", HeadEngineNo + 1);	// Uses the DCS Engine Number in the "Ixx" command  (Operations Control Constructor)
	bool bretv = ptrTIUCommThread->Send_String_FIFO(HeadEngineNo, Command, TIUNo, RemoteNo, 173, I_WAIT, NO_RECORD, NULL, &respCode, &iTIUCount, SWbyteResp);
	if (!bretv) { // retry once
    	if (DEBUGGING) DB->Debug_Print("TOp() Retrying " + String(Command));
		bretv = ptrTIUCommThread->Send_String_FIFO(HeadEngineNo, Command, TIUNo, RemoteNo, 173, I_WAIT, NO_RECORD, NULL, &respCode, &iTIUCount, SWbyteResp);
		}
    // Get the info about what switches the engine supports.
	if (bretv && iTIUCount >= 173) {    // Radio is 173 bytes, WiFi is around 220 bytes depending on the # of characters in engine name
		if (Setup->Radio_COM_Mode->Checked) {     // in Radio mode, we get 173 bytes if an engine is found
            // Handle Radio COM
			// Got a good response
            // Response to Ixxx command:
            // 996AA5A65A5A555556A5555A55A566595999559A655556A656AA6566656A6565656566566566656A656A6669AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA555555555555555555555555555555555555555556556595AA6A55955555555555555555555556559A99696569A555555A5AA9A955555555555555555556 (0x00)
            //   9D  F1  0F  00  52  05  50  26  4C  45  20  53  57  31  35  30  30  23  31  35  35  36  FF  FF  FF  FF  FF  FF  FF  FF  FF  FF  FF  FF  FF  FF  FF  FF  FF  FF  FF  FF  FF  FF  FF  FF  FF  FF  FF  FF  FF  FF  FF  FF  00  00  00  00  00  00  00  00  00  00  02  60  BF  40  00  00  00  00  00  02  CE  38  78  00  0F  FC  00  00  00  00  01
            //                     ^^^^ is is 0x00/0x10 for Steam, 0x05/0x85 for Diesel, and 0x25 Gas/Electric
            // see spreadsheet named "Command ('Ixxx') Response.xls"  for decoding details
            // No format change necessary
            memcpy(byteResp, SWbyteResp, CMDMAX);
            // byteResp[] has the decoded response string
            }

		if (Setup->WTIU_WiFi_Mode->Checked) {    // in WTIU mode, we don't get an exact number of bytes because blanks at end of engine name are suppressed
			// The string starts with "I1"
            // Handle WTIU ASCII Response I10:05;P&LE SW1500#1556;FF,FF,FF,FF,FF,FF,FF,FF,FF,FF,FF,FF,FF,FF,FF,FF,FF,FF,FF,FF,FF,FF,FF,FF,FF,FF,FF,FF,FF,FF,FF,FF,00,00,00,00,00,00,00,00,00,00,02,60,BF,40,00,00,00,00,00,02,CE,38,78,00,0F,FC,00,00,00,00,00,63;01 okay
            // Got a good response
            //  Find last ';'
            // parse out the header info
            byte rawbyte[CMDMAX];
            int retv = sscanf(SWbyteResp, "I%*d:%*x;%*[^;];%[^;]", rawbyte);
            assert(retv == 1);
            // Convert each character hex value ("FF") to hex (0xff)and put into byteResp
            byteCharacterStringTobyteHexString(rawbyte, &byteResp[22], 64);
            // byteResp[] has the decoded response string
            }

        // Get the hotkey info
        // that info to enable or disable certain switches and set their names
        if (DEBUGGING) DB->Debug_Print("Hotkey Begin : ---------- (Op_Control.cpp) ----------");
        //-------------------------------------------------------
        //
        // For electric engines, need to add FBS (Boiler Start w1000) and FPR (Pressure Release W800)
        //
        // GG-1 PS2 3V manual says:
        // FBS - This plays the Boiler Start-up sound
        // FPR - This plays the Pressure Release sound. The Pressure Release sound is timed to run with the
        // smoke unit and will run at a random duration each time this softkey is pressed
        //
        // Possible:
        // n138, n8, n25 and n26 all seem to be the repeating Boiler Start-up sound.
        // n9 seems to be the Pressure Release sound but it is not repeating.
        // n139 and n24 seem to be the repeating Pressure Release sound.
        //
        //-------------------------------------------------------
        if (DEBUGGINGX(9)) DB->Debug_Print("Hotkey : Checking Pantograph");
        const int Byte10Value = byteResp[53 + 10];
        const int Pantograph = Byte10Value & 0x04;
        if (Pantograph) {						// Pantograph Menu
            PantographPanel->Visible = true;	// show the control
            PantographPanel->Enabled = true;	// enable the control
            if (DEBUGGING) DB->Debug_Print("Hotkey : Pantograph Menu");
            }

        if (DEBUGGINGX(9)) DB->Debug_Print("Hotkey : Checking Marker Lights on Mars");
        const int Byte14Value = byteResp[53 + 14];
        const int MarkerLightMars = 0x01 & Byte14Value;
        if (MarkerLightMars) {	// Marker Lights on Mars
            if (ab5ab4Switch->Enabled) if (DEBUGGING) DB->Debug_Print("Hotkey : Duplicate Mars Usage", clRed);
            ab5ab4Switch->Tag = SWITCH_MARKER;
            ab5ab4Switch->Font->Color = clBlack;
            ab5ab4Switch->LEDColorOff = clRed;
            ab5ab4Switch->LEDColorOn = clLime;
            ab5ab4Switch->Text = "Marker|Lights";
            ab5ab4Switch->Enabled = true;	// enable the control
            // load switch state from Engine List
            MainForm->SetSwitch(ab5ab4Switch, MainForm->EngineData->Cells[ENG_AUX2][HeadEngineNo].ToIntDef(0));
            ab5ab4Switch->Hint = "Turn the marker lights on or off";
            if (DEBUGGING) DB->Debug_Print("Hotkey : Marker Lights on Mars LMA");
            }

        if (DEBUGGINGX(9)) DB->Debug_Print("Hotkey : Checking AUX1 on Interior");
        const int Aux1Interior = 0x08 & Byte14Value;
        if (Aux1Interior) { // AUX Light 1 on Interior;
            if (ab3ab2Switch->Enabled) if (DEBUGGING) DB->Debug_Print("Hotkey : Duplicate Interior Usage", clRed);
            ab3ab2Switch->Tag = SWITCH_AUX1;
            ab3ab2Switch->Font->Color = clBlack;
            ab3ab2Switch->LEDColorOff = clRed;
            ab3ab2Switch->LEDColorOn = clLime;
            ab3ab2Switch->Text = "Aux|Light 1";
            // load switch state from Engine List
            // RAM 0x25 bit 1
            MainForm->SetSwitch(ab3ab2Switch, MainForm->EngineData->Cells[ENG_AUX1][HeadEngineNo].ToIntDef(0));
            ab3ab2Switch->Enabled = true;	// enable the control
            ab3ab2Switch->Hint = "Turn the auxiliary light 1 on or off";
            if (DEBUGGING) DB->Debug_Print("Hotkey : Auxiliary 1 on Interior");
            }

        if (DEBUGGINGX(9)) DB->Debug_Print("Hotkey : Checking AUX2 on Mars");
        const int Aux2Mars = 0x10 & Byte14Value;
        if (Aux2Mars) {	// AUX Light 2 on Mars
            if (ab5ab4Switch->Enabled) if (DEBUGGING) DB->Debug_Print("Hotkey : Duplicate Mars Usage", clRed);
            ab5ab4Switch->Tag = SWITCH_AUX2;
            ab5ab4Switch->Font->Color = clBlack;
            ab5ab4Switch->LEDColorOff = clRed;
            ab5ab4Switch->LEDColorOn = clLime;
            ab5ab4Switch->Text = "Aux|Light 2";
            // load switch state from Engine List
            // RAM 0x25 bit 2
            MainForm->SetSwitch(ab5ab4Switch, MainForm->EngineData->Cells[ENG_AUX2][HeadEngineNo].ToIntDef(0));
            ab5ab4Switch->Enabled = true;	// enable the control
            ab5ab4Switch->Hint = "Turn the auxiliary light 2 on or off";
            if (DEBUGGING) DB->Debug_Print("Hotkey : Auxiliary 2 on Mars");
            }

        if (DEBUGGINGX(9)) DB->Debug_Print("Hotkey : Checking AUX3 on Ditch");
        const int Aux3Ditch = 0x20 & Byte14Value;
        if (Aux3Ditch) {	// AUX Light 3 on Ditch
            if (ab7ab6Switch->Enabled) if (DEBUGGING) DB->Debug_Print("Hotkey : Duplicate Ditch Usage", clRed);
            ab7ab6Switch->Tag = SWITCH_AUX3;
            ab7ab6Switch->Font->Color = clBlack;
            ab7ab6Switch->LEDColorOff = clRed;
            ab7ab6Switch->LEDColorOn = clLime;
            ab7ab6Switch->Text = "Aux|Light 3";
            // load switch state from Engine List
            // RAM 0x25 bit 3
            MainForm->SetSwitch(ab7ab6Switch, MainForm->EngineData->Cells[ENG_AUX3][HeadEngineNo].ToIntDef(0));
            ab7ab6Switch->Enabled = true;	// enable the control
            ab7ab6Switch->Hint = "Turn the auxiliary light 3 on or off";
            if (DEBUGGING) DB->Debug_Print("Hotkey : Auxiliary 3 on Ditch");
            }

        if (DEBUGGINGX(9)) DB->Debug_Print("Hotkey : Checking FCC");
        const int ClickClack = 0x40 & Byte14Value;
        if (!ClickClack) {	// Click-Clack  FCC
            ClickClackSwitch->Font->Color = clGray;
            ClickClackSwitch->LEDColorOff = clInactiveBorder;
            ClickClackSwitch->LEDColorOn = clInactiveBorder;
            ClickClackSwitch->Checked = false;	// Always set off as default - value not saved
            ClickClackSwitch->Enabled = false;	// disable the control
            } else {
            if (DEBUGGING) DB->Debug_Print("Hotkey : Click Clack FCC");
            }

        if (DEBUGGINGX(9)) DB->Debug_Print("Hotkey : Checking Firebox Glow on Interior");
        const int Byte15Value = byteResp[53 + 15];
        const int FireboxInt = 0x01 & Byte15Value;
        if (FireboxInt) { 	// Firebox Glow on Interior
            if (ab3ab2Switch->Enabled) if (DEBUGGING) DB->Debug_Print("Hotkey : Duplicate Interior Usage", clRed);
            ab3ab2Switch->Tag = SWITCH_FIREBOX;
            ab3ab2Switch->Font->Color = clBlack;
            ab3ab2Switch->LEDColorOff = clRed;
            ab3ab2Switch->LEDColorOn = clLime;
            ab3ab2Switch->Text = "FireBox|Glow";
            ab3ab2Switch->Enabled = true;	// enable the control
            // load switch state from Engine List
            MainForm->SetSwitch(ab3ab2Switch, MainForm->EngineData->Cells[ENG_AUX1][HeadEngineNo].ToIntDef(0));
            ab3ab2Switch->Hint = "Turn the firebox glow on or off";
            if (DEBUGGING) DB->Debug_Print("Hotkey : Firebox Glow on Interior LFB");
            }

        if (DEBUGGINGX(9)) DB->Debug_Print("Hotkey : Checking Marker Lights on Ditch");
           const int MarkerLightDitch = 0x02 & Byte15Value;
        if (MarkerLightDitch) {		// Marker Lights on Ditch
            if (ab7ab6Switch->Enabled) if (DEBUGGING) DB->Debug_Print("Hotkey : Duplicate Ditch Usage", clRed);
            ab7ab6Switch->Tag = SWITCH_MARKER;
            ab7ab6Switch->Font->Color = clBlack;
            ab7ab6Switch->LEDColorOff = clRed;
            ab7ab6Switch->LEDColorOn = clLime;
            ab7ab6Switch->Text = "Marker|Lights";
            ab7ab6Switch->Enabled = true;	// enable the control
            // load switch state from Engine List
            MainForm->SetSwitch(ab7ab6Switch, MainForm->EngineData->Cells[ENG_AUX3][HeadEngineNo].ToIntDef(0));
            ab7ab6Switch->Hint = "Turn the marker lights on or off";
            if (DEBUGGING) DB->Debug_Print("Hotkey : Marker Lights on Ditch LMK");
            }


        if (DEBUGGINGX(9)) DB->Debug_Print("Hotkey : Checking Running Light on Ditch and Mars");
        const int RunningLightDitchMars = 0x04 & Byte15Value;
        if (RunningLightDitchMars) {	// Running Light on Ditch and Mars
            if (ab7ab6Switch->Enabled || ab5ab4Switch->Enabled) if (DEBUGGING) DB->Debug_Print("Hotkey : Duplicate Mars-Ditch Usage", clRed);
            ab7ab6Switch->Tag = SWITCH_RUNNING_DITCH_MARS;			// Indicate also send to Mars
            ab7ab6Switch->Font->Color = clBlack;
            ab7ab6Switch->LEDColorOff = clRed;
            ab7ab6Switch->LEDColorOn = clLime;
            ab7ab6Switch->Text = "Running|Lights";
            ab7ab6Switch->Enabled = true;	// enable the control
            // load switch state from Engine List
            MainForm->SetSwitch(ab7ab6Switch, MainForm->EngineData->Cells[ENG_AUX3][HeadEngineNo].ToIntDef(0));
            ab7ab6Switch->Hint = "Turn the running lights on or off";
            if (DEBUGGING) DB->Debug_Print("Hotkey : Running Lights on Ditch and Mars");
            }

        if (DEBUGGINGX(9)) DB->Debug_Print("Hotkey : Checking Interior Light on Mars");
        const int InteriorLightMars = 0x08 & Byte15Value;
        if (InteriorLightMars) {	// Interior Light on Mars
            if (ab5ab4Switch->Enabled) if (DEBUGGING) DB->Debug_Print("Hotkey : Duplicate Mars Usage", clRed);
            ab5ab4Switch->Tag = SWITCH_INTERIOR;
            ab5ab4Switch->Font->Color = clBlack;
            ab5ab4Switch->LEDColorOff = clRed;
            ab5ab4Switch->LEDColorOn = clLime;
            ab5ab4Switch->Text = "Int|Light";
            ab5ab4Switch->Enabled = true;	// enable the control
            LINSwitch = ab5ab4Switch;
            // load switch state from Engine List
            MainForm->SetSwitch(ab5ab4Switch, MainForm->EngineData->Cells[ENG_AUX2][HeadEngineNo].ToIntDef(0));
            ab5ab4Switch->Hint = "Turn the interior lights on or off";
            if (DEBUGGING) DB->Debug_Print("Hotkey : Interior Light on Mars LIN");
            }

        if (DEBUGGINGX(9)) DB->Debug_Print("Hotkey : Checking Interior Light on Ditch");
        const int InteriorLightDitch = 0x10 & Byte15Value;
        if (InteriorLightDitch) {	// Interior Light on Ditch
            if (ab7ab6Switch->Enabled) if (DEBUGGING) DB->Debug_Print("Hotkey : Duplicate Ditch Usage", clRed);
            ab7ab6Switch->Tag = SWITCH_INTERIOR;
            ab7ab6Switch->Font->Color = clBlack;
            ab7ab6Switch->LEDColorOff = clRed;
            ab7ab6Switch->LEDColorOn = clLime;
            ab7ab6Switch->Text = "Int|Light";
            ab7ab6Switch->Enabled = true;	// enable the control
            LINSwitch = ab7ab6Switch;
            // load switch state from Engine List
            MainForm->SetSwitch(ab7ab6Switch, MainForm->EngineData->Cells[ENG_AUX3][HeadEngineNo].ToIntDef(0));
            ab7ab6Switch->Hint = "Turn the interior lights on or off";
            if (DEBUGGING) DB->Debug_Print("Hotkey : Interior Light on Ditch LIN");
            }

        if (DEBUGGINGX(9)) DB->Debug_Print("Hotkey : Checking Number Board Lights on Ditch and Mars");
        const int NumberBoardLightDitchMars = 0x20 & Byte15Value;
        if (NumberBoardLightDitchMars) {	// Number Board Light on Ditch and Mars
            if (ab7ab6Switch->Enabled || ab5ab4Switch->Enabled) if (DEBUGGING) DB->Debug_Print("Hotkey : Duplicate Mars-Ditch Usage", clRed);
            ab7ab6Switch->Tag = SWITCH_NB_DITCH_MARS;				// Indicate also send to Mars
            ab7ab6Switch->Font->Color = clBlack;
            ab7ab6Switch->LEDColorOff = clRed;
            ab7ab6Switch->LEDColorOn = clLime;
            ab7ab6Switch->Text = "Number|Boards";
            ab7ab6Switch->Enabled = true;		// enable the control
            // load switch state from Engine List
            MainForm->SetSwitch(ab7ab6Switch, MainForm->EngineData->Cells[ENG_AUX2][HeadEngineNo].ToIntDef(0));
            ab7ab6Switch->Hint = "Turn the number boards on or off";
            if (DEBUGGING) DB->Debug_Print("Hotkey : Number Boards on Ditch and Mars LNB");
            }

        if (DEBUGGINGX(9)) DB->Debug_Print("Hotkey : Checking Firebox Glow on Mars");
        const int FireboxGlowMars = 0x40 & Byte15Value;
        if (FireboxGlowMars) {				// Firebox Glow Light on Mars
            if (ab5ab4Switch->Enabled) if (DEBUGGING) DB->Debug_Print("Hotkey : Duplicate Mars Usage", clRed);
            ab5ab4Switch->Tag = SWITCH_FIREBOX;
            ab5ab4Switch->Font->Color = clBlack;
            ab5ab4Switch->LEDColorOff = clRed;
            ab5ab4Switch->LEDColorOn = clLime;
            ab5ab4Switch->Text = "Firebox|Glow";
            ab5ab4Switch->Enabled = true;	// enable the control
            // load switch state from Engine List
            MainForm->SetSwitch(ab5ab4Switch, MainForm->EngineData->Cells[ENG_AUX2][HeadEngineNo].ToIntDef(0));
            ab5ab4Switch->Hint = "Turn the firebox glow on or off";
            if (DEBUGGING) DB->Debug_Print("Hotkey : Firebox Glow Light on Mars LFB");
            }

        if (DEBUGGINGX(9)) DB->Debug_Print("Hotkey : Checking Firebox Glow on Ditch");
        const int FireboxGlowDitch = 0x80 & Byte15Value;
        if (FireboxGlowDitch) {	// Firebox Glow Light on Ditch
            if (ab7ab6Switch->Enabled) if (DEBUGGING) DB->Debug_Print("Hotkey : Duplicate Ditch Usage", clRed);
            ab7ab6Switch->Tag = SWITCH_FIREBOX;
            ab7ab6Switch->Font->Color = clBlack;
            ab7ab6Switch->LEDColorOff = clRed;
            ab7ab6Switch->LEDColorOn = clLime;
            ab7ab6Switch->Text = "Firebox|Glow";
            ab7ab6Switch->Enabled = true;	// enable the control
            // load switch state from Engine List
            MainForm->SetSwitch(ab7ab6Switch, MainForm->EngineData->Cells[ENG_AUX3][HeadEngineNo].ToIntDef(0));
            ab7ab6Switch->Hint = "Turn the firebox glow on or off";
            if (DEBUGGING) DB->Debug_Print("Hotkey : Firebox Glow Light on Ditch LFB");
            }

        if (DEBUGGINGX(9)) DB->Debug_Print("Hotkey : Checking Beacon Light");
        const int Byte16Value = byteResp[53 + 16];
        const int Beacon = (0x01) & Byte16Value;
        if (Beacon) { 	// Beacon Light on its own dedicated switch - send "abC" and "abD"
            BeaconLightSwitch->Text = "Beacon|Lights";
            BeaconLightSwitch->Font->Color = clBlack;
            BeaconLightSwitch->LEDColorOff = clRed;
            BeaconLightSwitch->LEDColorOn = clLime;
            BeaconLightSwitch->Enabled = true;	// enable the control
            // load switch state from Engine List
            MainForm->SetSwitch(BeaconLightSwitch, MainForm->EngineData->Cells[ENG_BEACON][HeadEngineNo].ToIntDef(0));
            BeaconLightSwitch->Hint = "Turn the beacon light on or off";
            if (DEBUGGING) DB->Debug_Print("Hotkey : Beacon Light LBE");
            }

        // Remote Bug:This bit produces only  an "ab3" code from the remote. Never produces
        // an "ab2" code to turn off the lights. RTC corrects that and sends the "ab2" off code
        if (DEBUGGINGX(9)) DB->Debug_Print("Hotkey : Checking Marker Lights on Interior");
        const int MarkerLightInt = (0x08) & Byte16Value;
        if (MarkerLightInt) { 	// Marker Lights on Interior
            if (ab3ab2Switch->Enabled) if (DEBUGGING) DB->Debug_Print("Hotkey : Duplicate Interior Usage", clRed);
            ab3ab2Switch->Tag = SWITCH_MARKER;
            ab3ab2Switch->Text = "Marker|Lights";
            ab3ab2Switch->Font->Color = clBlack;
            ab3ab2Switch->LEDColorOff = clRed;
            ab3ab2Switch->LEDColorOn = clLime;
            ab3ab2Switch->Enabled = true;	// enable the control
            // load switch state from Engine List
            MainForm->SetSwitch(ab3ab2Switch, MainForm->EngineData->Cells[ENG_AUX1][HeadEngineNo].ToIntDef(0));
            ab3ab2Switch->Hint = "Turn the marker lights on or off";
            if (DEBUGGING) DB->Debug_Print("Hotkey : Marker Lights on Interior");
            }

        if (DEBUGGINGX(9)) DB->Debug_Print("Hotkey : Checking Marker Lights");
        const int MarkerLight = (0x04) & Byte16Value;
        if ((MarkerLight || MarkerOverride) && !MarkerDisable) { // Marker Light on its own dedicated switch - send "ab1C" and "ab1D"
            MarkerLightSwitch->Text = "Marker|Lights";
            MarkerLightSwitch->Font->Color = clBlack;
            MarkerLightSwitch->LEDColorOff = clRed;
            MarkerLightSwitch->LEDColorOn = clLime;
            MarkerLightSwitch->Enabled = true;	// enable the control
            // load switch state from Engine List
            // RAM 0x24 bit 7
            MainForm->SetSwitch(MarkerLightSwitch, MainForm->EngineData->Cells[ENG_MARKER][HeadEngineNo].ToIntDef(0));
            MarkerLightSwitch->Hint = "Turn the marker lights on or off";
            if (DEBUGGING) DB->Debug_Print("Hotkey : Marker Lights LMK");
            }

        if (DEBUGGINGX(9)) DB->Debug_Print("Hotkey : Checking Headlight on Interior");
        const int HeadlightInt = 0x10 & Byte16Value;
        if (HeadlightInt) { // HeadLight on Interior
            if (ab3ab2Switch->Enabled) if (DEBUGGING) DB->Debug_Print("Hotkey : Duplicate Interior Usage", clRed);
            ab3ab2Switch->Tag = SWITCH_HEADLIGHT;
            ab3ab2Switch->Font->Color = clBlack;
            ab3ab2Switch->LEDColorOff = clRed;
            ab3ab2Switch->LEDColorOn = clLime;
            ab3ab2Switch->Text = "Head|Light";
            ab3ab2Switch->Enabled = true;	// enable the control
            // load switch state from Engine List
            MainForm->SetSwitch(ab3ab2Switch, MainForm->EngineData->Cells[ENG_AUX1][HeadEngineNo].ToIntDef(9));
            ab3ab2Switch->Hint = "Turn the headlight on or off";
            if (DEBUGGING) DB->Debug_Print("Hotkey : Headlight on Interior");
            }

        if (DEBUGGINGX(9)) DB->Debug_Print("Hotkey : Checking Number Boards on Mars");
        const int NumberBoardMars = 0x20 & Byte16Value;
        if (NumberBoardMars) {	// Number Boards on Mars
            if (ab5ab4Switch->Enabled) if (DEBUGGING) DB->Debug_Print("Hotkey : Duplicate Mars Usage", clRed);
            ab5ab4Switch->Tag = SWITCH_NB;
            ab5ab4Switch->Font->Color = clBlack;
            ab5ab4Switch->LEDColorOff = clRed;
            ab5ab4Switch->LEDColorOn = clLime;
            ab5ab4Switch->Text = "Number|Boards";
            ab5ab4Switch->Enabled = true;		// enable the control
            // load switch state from Engine List
            MainForm->SetSwitch(ab5ab4Switch, MainForm->EngineData->Cells[ENG_AUX2][HeadEngineNo].ToIntDef(0));
            ab5ab4Switch->Hint = "Turn the number boards on or off";
            if (DEBUGGING) DB->Debug_Print("Hotkey : Number Boards on Mars LNB");
            }

        if (DEBUGGINGX(9)) DB->Debug_Print("Hotkey : Checking Inspection Lights on Ditch");
        const int TrackInspecitionLightDitch = 0x40 & Byte16Value;
        if (TrackInspecitionLightDitch) {	// Track Inspection Light on Ditch
            if (ab7ab6Switch->Enabled) if (DEBUGGING) DB->Debug_Print("Hotkey : Duplicate Ditch Usage", clRed);
            ab7ab6Switch->Tag = SWITCH_INSPECTION;
            ab7ab6Switch->Font->Color = clBlack;
            ab7ab6Switch->LEDColorOff = clRed;
            ab7ab6Switch->LEDColorOn = clLime;
            ab7ab6Switch->Text = "Track|Insp";
            ab7ab6Switch->Enabled = true;	// enable the control
            // load switch state from Engine List
            MainForm->SetSwitch(ab7ab6Switch, MainForm->EngineData->Cells[ENG_AUX3][HeadEngineNo].ToIntDef(0));
            ab7ab6Switch->Hint = "Turn the track inspection lights on or off";
            if (DEBUGGING) DB->Debug_Print("Hotkey : Track Inspection Light on Ditch");
            }

        if (DEBUGGINGX(9)) DB->Debug_Print("Hotkey : Checking Number Boards Lights on Ditch");
        const int NumberBoardsDitch = 0x80 & Byte16Value;
        if (NumberBoardsDitch) {	// Number Board Light on Ditch
            if (ab7ab6Switch->Enabled) if (DEBUGGING) DB->Debug_Print("Hotkey : Duplicate Ditch Usage", clRed);
            ab7ab6Switch->Tag = SWITCH_NB;
            ab7ab6Switch->Font->Color = clBlack;
            ab7ab6Switch->LEDColorOff = clRed;
            ab7ab6Switch->LEDColorOn = clLime;
            ab7ab6Switch->Text = "Number|Boards";
            ab7ab6Switch->Enabled = true;	// enable the control
            // load switch state from Engine List
            MainForm->SetSwitch(ab7ab6Switch, MainForm->EngineData->Cells[ENG_AUX3][HeadEngineNo].ToIntDef(0));
            ab7ab6Switch->Hint = "Turn the number boards on or off";
            if (DEBUGGING) DB->Debug_Print("Hotkey : Number Boards on Ditch");
            }

        if (DEBUGGINGX(9)) DB->Debug_Print("Hotkey : Checking Mars Light on Mars");
        const int Byte17Value = byteResp[53 + 17];
        const int MarsMars = 0x80 & Byte17Value;
        if (MarsMars) { // Mars Light on Mars
            if (ab5ab4Switch->Enabled) if (DEBUGGING) DB->Debug_Print("Hotkey : Duplicate Mars Usage", clRed);
            ab5ab4Switch->Tag = SWITCH_MARS;
            ab5ab4Switch->Font->Color = clBlack;
            ab5ab4Switch->LEDColorOff = clRed;
            ab5ab4Switch->LEDColorOn = clLime;
            ab5ab4Switch->Text = "Mars|Light";
            ab5ab4Switch->Enabled = true;	// enable the control
            // load switch state from Engine List
            MainForm->SetSwitch(ab5ab4Switch, MainForm->EngineData->Cells[ENG_AUX2][HeadEngineNo].ToIntDef(0));
            ab5ab4Switch->Hint = "Turn the mars light on or off";
            if (DEBUGGING) DB->Debug_Print("Hotkey : Mars Light on Mars");
            }

        if (DEBUGGINGX(9)) DB->Debug_Print("Hotkey : Checking Ditch Light Menu");
        const int Ditch = 0x40 & Byte17Value;
        if (Ditch && !DitchDisable) {	// Ditch Light Menu
            DitchLabel->Visible = true;
            DitchComboBox->Visible = true;	// show the control
            DitchComboBox->Enabled = true;	// enable the control
            // load combo box from Engine List
            // RAM ??
            MainForm->SetComboBox(DitchComboBox, MainForm->EngineData->Cells[ENG_DITCH][HeadEngineNo].ToIntDef(0));
            if (DEBUGGING) DB->Debug_Print("Hotkey : Ditch Lights Menu");
            }

        if (DEBUGGINGX(9)) DB->Debug_Print("Hotkey : Checking Interior Light on Interior");
        const int Interior_on_Interior = 0x20 & Byte17Value;
        if (Interior_on_Interior || IntIntOverride) { // Interior Light on Interior
            if (ab3ab2Switch->Enabled) if (DEBUGGING) DB->Debug_Print("Hotkey : Duplicate Interior Usage", clRed);
            ab3ab2Switch->Tag = SWITCH_INTERIOR;
            ab3ab2Switch->Font->Color = clBlack;
            ab3ab2Switch->LEDColorOff = clRed;
            ab3ab2Switch->LEDColorOn = clLime;
            ab3ab2Switch->Text = "Int|Light";
            ab3ab2Switch->Enabled = true;			// enable the control
            LINSwitch = ab3ab2Switch;
            // load switch state from Engine List
            MainForm->SetSwitch(ab3ab2Switch, MainForm->EngineData->Cells[ENG_AUX1][HeadEngineNo].ToIntDef(0));
            ab3ab2Switch->Hint = "Turn the interior lights on or off";
            if (DEBUGGING) DB->Debug_Print("Hotkey : Interior Lights on Interior LIN");
            }

        if (DEBUGGINGX(9)) DB->Debug_Print("Hotkey : Checking SXS");
        const int Byte20Value = byteResp[53 + 20];
        const int XingSignal = 0x02 & Byte20Value;		// SXS
        if (XingSignal) {				// Crossing Signal
            SXSButton->Visible = true;	// Show the control
            SXSButton->Enabled = true;	// Enable the control
            BellOnWithHornLED->Visible = true;	// Show the control (already enabled)
            if (DEBUGGING) DB->Debug_Print("Hotkey : Xing Signal SXS");
            }

        if (DEBUGGINGX(9)) DB->Debug_Print("Hotkey : Checking Alternate Horn SAH");
        const int AlternateHorn = 0x01 & Byte20Value;	// SAH
        if (AlternateHorn) {			// Alternate Horn
            SRSButton->Width = SRSButton->Width / 2;	// Show the control
            SFSButton->Left = SFSButton->Left + SRSButton->Width;
            SFSButton->Top = SRSButton->Top;
            SFSButton->Width = SRSButton->Width;
            if (DEBUGGING) DB->Debug_Print("Hotkey : Alternate Horn SAH");
            }

        // Initialize the SmokePanel
        ProtoWhistleSwitch->Font->Color = clGray;
        ProtoWhistleSwitch->LEDColorOff = clInactiveBorder;
        ProtoWhistleSwitch->LEDColorOn = clInactiveBorder;
        SmokeWhistleSwitch->Font->Color = clGray;
        SmokeWhistleSwitch->LEDColorOff = clInactiveBorder;
        SmokeWhistleSwitch->LEDColorOn = clInactiveBorder;
        SwingingBellSwitch->Font->Color = clGray;
        SwingingBellSwitch->LEDColorOff = clInactiveBorder;
        SwingingBellSwitch->LEDColorOn = clInactiveBorder;

        if (DEBUGGINGX(9)) DB->Debug_Print("Hotkey : Checking Quilling Proto Whistle");
        const int ProtoWhistle = 0x08 & Byte20Value;	// SPW	ab20/ab21
        if (ProtoWhistle) {			// Quilling Proto Whistle
            SmokePanel->Visible = true;
            ProtoWhistleSwitch->Enabled = true;
            ProtoWhistleSwitch->Font->Color = clBlack;
            ProtoWhistleSwitch->LEDColorOff = clRed;
            ProtoWhistleSwitch->LEDColorOn = clLime;
            // reset SPW switch state to send SPW off to engine (if it is on)
            if (ProtoWhistleSwitch->Checked) {
                MainForm->SetSwitch(ProtoWhistleSwitch, 0);
                }
            if (DEBUGGING) DB->Debug_Print("Hotkey : Quillable Proto Whistle SPW");
            WhistleSlider->Visible = false;
            }

        if (DEBUGGINGX(9)) DB->Debug_Print("Hotkey : Checking Smoke Whistle");
        const int SmokeWhistle = 0x10 & Byte20Value;	// FSW   ab22/ab23
        if (SmokeWhistle) {			//  Smoke Whistle
            SmokePanel->Visible = true;
            SmokeWhistleSwitch->Enabled = true;
            SmokeWhistleSwitch->Font->Color = clBlack;
            SmokeWhistleSwitch->LEDColorOff = clRed;
            SmokeWhistleSwitch->LEDColorOn = clLime;
            if (DEBUGGING) DB->Debug_Print("Hotkey : Smoke Whistle FSW");
            }

        if (DEBUGGINGX(9)) DB->Debug_Print("Hotkey : Checking Swinging Bell");
        const int SwingingBell = 0x20 & Byte20Value;	// FSB   ab24/ab25
        if (SwingingBell) {			//  Swinging Bell
            SmokePanel->Visible = true;
            SwingingBellSwitch->Enabled = true;
            SwingingBellSwitch->Font->Color = clBlack;
            SwingingBellSwitch->LEDColorOff = clRed;
            SwingingBellSwitch->LEDColorOn = clLime;
            if (DEBUGGING) DB->Debug_Print("Hotkey : Swinging Bell FSB");
            }

        if (DEBUGGINGX(9)) DB->Debug_Print("Hotkey : Checking Cab Chatter FCH");
        const int Byte22Value = byteResp[53 + 22];
        const int CabChat = 0x20 & Byte22Value;
        if (!CabChat) {	// Cab Chatter  FCH
            CabChatSwitch->Font->Color = clGray;
            CabChatSwitch->LEDColorOff = clInactiveBorder;
            CabChatSwitch->LEDColorOn = clInactiveBorder;
            // switch state already set by Train_Control.cpp when this Op window was created.
            CabChatSwitch->Enabled = false;	// disable the control
            } else {
            if (DEBUGGING) DB->Debug_Print("Hotkey : Cab Chatter FCH");
            }

        if (DEBUGGINGX(9)) DB->Debug_Print("Hotkey : Checking Coupler Slack SCS");
        const int CouplerSlack = 0x08 & Byte22Value;
        if (!CouplerSlack) {	// Coupler Slack  SCS
            CouplerSlackSwitch->Font->Color = clGray;
            CouplerSlackSwitch->LEDColorOff = clInactiveBorder;
            CouplerSlackSwitch->LEDColorOn = clInactiveBorder;
            CouplerSlackSwitch->Checked = false;
            CouplerSlackSwitch->Enabled = false;	// disable the control
            } else {
            if (DEBUGGING) DB->Debug_Print("Hotkey : Coupler Slack SCS");
            CouplerSlackSwitch->Checked = Setup->AutoCoupler->Checked;
            CouplerSlackSwitch->Enabled = Setup->AutoCoupler->Checked;	// enable or disable the control based on option setting in Setup
            if (!Setup->AutoCoupler->Checked) {
            	CouplerSlackSwitch->Font->Color = clGray;
            	CouplerSlackSwitch->LEDColorOff = clInactiveBorder;
            	CouplerSlackSwitch->LEDColorOn = clInactiveBorder;
            	}
            }
        if (DEBUGGING) DB->Debug_Print("Hotkey End: ---------- (Op_Control.cpp) ----------");
        } else {
        //  "I" command failed so don't try to decode hotkeys
        if (DEBUGGING) DB->Debug_Print("Hotkey : No Operations Hotkeys set - \"I\" Command Failed for Engine " + String(HeadEngineNo), clRed);
       	MainForm->DebugMessagePopup("Information", "Can't read up Engine " + String(HeadEngineNo) + " Hotkeys", "No Hotkeys Set", UIPOPUPTIMEOUT);
        return;
        }   // if (bretv && iTIUCount >= 173)
	}
if (Setup->AutoWhistle->Checked && AutoWhistleLED->Checked) {
    if (Setup->ExtendedSUSD->Checked) {
        // schedule double toot in 20 seconds for extended startup
        ptrDL_Thread->Send_String_DL_FIFO(20 * 1000, ConsistNo, DOUBLE_TOOT, TIUNo, RemoteNo, 11, SHORT_WAIT, RECORD_OK, LashUpEngineListOp);
        } else {
        // schedule double toot in 10 seconds for normal startup
        ptrDL_Thread->Send_String_DL_FIFO(10 * 1000, ConsistNo, DOUBLE_TOOT, TIUNo, RemoteNo, 11, SHORT_WAIT, RECORD_OK, LashUpEngineListOp);
        }
    }

if (DEBUGGINGX(9)) DB->Debug_Print("Hotkey : Checking Simulated Rule 17");
if (SimulatedRule17LED->Checked) {	// should we startup with headlight off and interior light on?
    HDLTSwitch->Checked = false;
    if (LINSwitch) LINSwitch->Checked = true;
	}
//
// reflect values onto any open Quick Controls window
if (ptrQuick_Smoke) {
    // The Smoke Switch on the Quick Controls window uses OnStatusChanged
    SmokeOnValueChanged = ptrQuick_Smoke->OnStatusChanged;
    ptrQuick_Smoke->OnStatusChanged = NULL;
    ptrQuick_Smoke->Checked = SmokeSwitch->Checked;
    ptrQuick_Smoke->OnStatusChanged = SmokeOnValueChanged;
    }
// reflect the change onto any open Quick Controls window
if (ptrQuick_Bell) {
    // The Whistle Button on the Quick Controls window uses OnStatusChanged
    BellOnValueChanged = ptrQuick_Bell->OnStatusChanged;
    ptrQuick_Bell->OnStatusChanged = NULL;
    ptrQuick_Bell->Checked = BellSwitch->Checked;
    ptrQuick_Bell->OnStatusChanged = BellOnValueChanged;
    }

// reflect the change onto any open Quick Controls window
if (ptrQuick_Dir) {
    // The Dir Switch on the Quick Controls window uses OnStatusChanged
    DirOnValueChanged = ptrQuick_Dir->OnStatusChanged;
    ptrQuick_Dir->OnStatusChanged = NULL;
    ptrQuick_Dir->Checked = DIRToggle->Checked;
    ptrQuick_Dir->OnStatusChanged = DirOnValueChanged;
    ptrQuick_EngNumber->Color = clLime;	// engine is started, set Engine Number field to green
    ptrQuick_RunState[ConsistNo] = clLime;
    }

// reflect the change onto any open Quick Controls window
if (ptrQuick_Speed) {
    // The Speed Edit on the Quick Controls window uses OnValueChanged
	SpeedOnValueChanged = ptrQuick_Speed->OnValueChanged;
    ptrQuick_Speed->OnValueChanged = NULL;
	ptrQuick_Speed->Value = 0;	// on startup, speed is always 0
    ptrQuick_Speed->OnValueChanged = SpeedOnValueChanged;
    ptrQuick_EngNumber->Color = clLime;	// engine is startup, set Engine Number field to green
    ptrQuick_RunState[ConsistNo] = clLime;
    }

// reflect the change on to any open Quick Controls window
if (ptrQuick_Sound) {
	// The VolumeSlider on the Quick Controls window uses OnValueChange......
	EventOnValueChange = ptrQuick_Sound->OnValueChange;
    ptrQuick_Sound->OnValueChange = NULL;
	ptrQuick_Sound->Value = LastSentMasterVolumeValue;
    ptrQuick_Sound->OnValueChange = EventOnValueChange;
    }

// reflect the change on to any open Quick Controls window
if (ptrQuick_SXS) {
        ptrQuick_SXS->Visible = SXSButton->Visible; // if the engine does not support n42
    	}

// expand to Whistle and Coupler Buttons
// for buttons, Checked property is always false but this code handle future thoughts
// reflect the change on to any open Quick Controls window
if (ptrQuick_Whistle) {
	ptrQuick_Whistle->Checked = WhistleButton->Checked;
    }

// reflect the change on to any open Quick Controls window
if (ptrQuick_Horn) {
	ptrQuick_Horn->Checked = HornButton->Checked;
    }

// reflect the change on to any open Quick Controls window
if (ptrQuick_SFS) {
	ptrQuick_SFS->Checked = SFSButton->Checked;
    }

// reflect the change on to any open Quick Controls window
if (ptrQuick_SRS) {
	ptrQuick_SRS->Checked = SRSButton->Checked;
    }

// reflect the change on to any open Quick Controls window
if (ptrQuick_SXS) {
	ptrQuick_SXS->Checked = SXSButton->Checked;
    }

// reflect the change on to any open Quick Controls window
if (ptrQuick_CPLRF) {
	ptrQuick_CPLRF->Checked = CPLRFButton->Checked;
    }

// reflect the change on to any open Quick Controls window
if (ptrQuick_CPLRR) {
	ptrQuick_CPLRR->Checked = CPLRRButton->Checked;
    }
}
//---------------------------------------------------------------------------

__fastcall TOp::~TOp(void)
{
if (LashUpEngineListOp != NULL) {
	delete[] LashUpEngineListOp;
    LashUpEngineListOp = NULL;
    }
/* This caused deallocation errors when shutting down with the Quick Controls Window
// reflect shutdown status to any Quick Control windows
if (ptrQuick_Speed) {
    // The Speed Edit on the Quick Controls window uses OnValueChanged
	SpeedOnValueChanged = ptrQuick_Speed->OnValueChanged;
    ptrQuick_Speed->OnValueChanged = NULL;
	ptrQuick_Speed->Value = 0;	// on shutdown, speed is always 0
    ptrQuick_Speed->OnValueChanged = SpeedOnValueChanged;
    ptrQuick_EngNumber->Color = clRed;	// engine is shutdown, set Engine Number field to red
    ptrQuick_RunState[ConsistNo] = clRed;
    }
*/
if (MainForm->bSaveRegistryInfo) {
    TRegIniFile * const RegFile = new TRegIniFile(RegFileRootKey + "\\Operations");
    assert(RegFile != NULL);
    __try {	    // erase OP key
        RegFile->EraseSection("Op" + String(ConsistNo));
        // save window position
        RegFile->WriteInteger("Op" + String(ConsistNo), "Top", Top > 0 ? Top : 0);
        RegFile->WriteInteger("Op" + String(ConsistNo), "Left", Left);
        }
    __finally {
        // Deallocate RegFile
        delete RegFile;
        }
    }

if (EC != NULL) {
    delete EC;	// destroy special command window if created
    EC = NULL;
    }

// clear PFA
PFAButton->Tag = false;

// terminate any polling threads
if (MyTrackSignal != NULL) {
	delete MyTrackSignal;
    MyTrackSignal = NULL;
    }
if (ptrEngineInfo[HeadEngineNo] != NULL) {
	delete ptrEngineInfo[HeadEngineNo];
    ptrEngineInfo[HeadEngineNo] = NULL;
    }
if (ptrEngineStatus[HeadEngineNo] != NULL) {
	delete ptrEngineStatus[HeadEngineNo];
    ptrEngineStatus[HeadEngineNo] = NULL;
    }
}
//---------------------------------------------------------------------------
//void __fastcall (__closure *BellOnValueChanged)(TObject *Sender);
//---------------------------------------------------------------------------

void __fastcall TOp::BellSwitchStatusChanged(TObject *Sender)
{
// bell on or off
// https://stackoverflow.com/questions/31649315/c-builder-button-click-function-reentry
LOCKEM()

if (BellSwitch->Checked) {
	// start bell
	(void)ptrTIUCommThread->Send_String_FIFO(ConsistNo, BELL_ON, TIUNo, RemoteNo, 11, SHORT_WAIT, RECORD_OK, LashUpEngineListOp);
    // reflect the change on to any open Quick Controls window
	if (ptrQuick_Bell) {
    	// The Bell Switch on the Quick Controls window uses OnStatusChanged
		BellOnValueChanged = ptrQuick_Bell->OnStatusChanged;
	    ptrQuick_Bell->OnStatusChanged = NULL;
		ptrQuick_Bell->Checked = true;
	    ptrQuick_Bell->OnStatusChanged = BellOnValueChanged;
    	}
    if (Setup->AutoBellOff->Checked) {
        // turn off bell in specified number of seconds
        ptrDL_Thread->Send_Delayed_DL_FIFO(this, Handle, DC_BELLOFF, ConsistNo, TIUNo, RemoteNo, Setup->BellOffTime->Value * 1000, 0);
        }
    } else {
    // stop bell : -4  : FFFB one's complement
	(void)ptrTIUCommThread->Send_String_FIFO(ConsistNo, BELL_OFF, TIUNo, RemoteNo, 11, SHORT_WAIT, RECORD_OK, LashUpEngineListOp);
	// reflect the change on to any open Quick Controls window
	if (ptrQuick_Bell) {
    	// The Bell Switch on the Quick Controls window uses OnStatusChanged
		BellOnValueChanged = ptrQuick_Bell->OnStatusChanged;
	    ptrQuick_Bell->OnStatusChanged = NULL;
		ptrQuick_Bell->Checked = false;
	    ptrQuick_Bell->OnStatusChanged = BellOnValueChanged;
    	}
    }
}
//---------------------------------------------------------------------------

void __fastcall TOp::WhistleButton1MouseDown(TObject *Sender,
      TMouseButton Button, TShiftState Shift, int X, int Y)
{
if (Button == mbLeft) {
	// if (DEBUGGING) DB->Debug_Print("WhistleButtonMouseDown");
	// start whistle
    // reflect the change on to any open Quick Controls window
	if (ptrQuick_Whistle) {
    	// The Whistle Button on the Quick Controls window uses OnMouseDown but that won't trigger on changing the Checked property
		//ptrQuick_Whistle->Checked = true;		// this does not work to change the look of the button
        ptrQuick_Whistle->GradBtnFace->Visible = true;
        ptrQuick_Horn->GradBtnFace->Visible = true;
		}
	#ifdef BUTTONMETHOD
	// start whistle : 2
	if (BellOnWithHornLED->Checked) {
		ptrDL_Thread->Send_Delayed_DL_FIFO(this, Handle, DC_BELLON, ConsistNo, TIUNo, RemoteNo, 0, 0);
		}
	// start whistle
	(void)ptrTIUCommThread->Send_String_FIFO(ConsistNo, WHISTLE_ON, TIUNo, RemoteNo, 11, SHORT_WAIT, RECORD_OK, LashUpEngineListOp);
	#else
	// Send CM_WHISTLE message to ourselves which will perform this function
	(void) PostMessage(Handle, CM_WHISTLE, true, NULL);		// true = button down
	#endif
    }
}
//---------------------------------------------------------------------------

void __fastcall TOp::CMWhistle(TMessage &Message)
{
// if Proto Whistle is on, disable this action
if (ProtoWhistleSwitch->Checked) return;
//
//if (DEBUGGING) DB->Debug_Print("CMWhistle");
const bool Dn = Message.WParam;		// set button state from Word Parameter
if (Dn) {	// button down
	// start whistle : 2
	if (BellOnWithHornLED->Checked) {
		ptrDL_Thread->Send_Delayed_DL_FIFO(this, Handle, DC_BELLON, ConsistNo, TIUNo, RemoteNo, 0, 0);
		}
	// start whistle
	(void)ptrTIUCommThread->Send_String_FIFO(ConsistNo, WHISTLE_ON, TIUNo, RemoteNo, 11, SHORT_WAIT, RECORD_OK, LashUpEngineListOp);
	}
if (!Dn) {	// button up
	// stop whistle : -2  : FFFD one's complement
   (void)ptrTIUCommThread->Send_String_FIFO(ConsistNo, WHISTLE_OFF, TIUNo, RemoteNo, 11, SHORT_WAIT, RECORD_OK, LashUpEngineListOp);
   if (BellOnWithHornLED->Checked) {
		ptrDL_Thread->Send_Delayed_DL_FIFO(this, Handle, DC_BELLOFF, ConsistNo, TIUNo, RemoteNo, 0, 0);
		}
	}
}
//---------------------------------------------------------------------------

void __fastcall TOp::Whistle1ButtonMouseUp(TObject *Sender,
      TMouseButton Button, TShiftState Shift, int X, int Y)
{
if (Button == mbLeft) {
	// if (DEBUGGING) DB->Debug_Print("WhistleButtonMouseUp");
    // stop whistle
    // reflect the change on to any open Quick Controls window
	if (ptrQuick_Whistle) {
    	// The Whistle Button on the Quick Controls window uses OnMouseDown but that won't trigger on changing the Checked property
		//ptrQuick_Whistle->Checked = false;	// this does not work to change the look of the button
        ptrQuick_Whistle->GradBtnFace->Visible = false;
        ptrQuick_Horn->GradBtnFace->Visible = false;
		}
	#ifdef BUTTONMETHOD
	// stop whistle : -2  : FFFD one's complement
	(void)ptrTIUCommThread->Send_String_FIFO(ConsistNo, WHISTLE_OFF, TIUNo, RemoteNo, 11, SHORT_WAIT, RECORD_OK, LashUpEngineListOp);
	if (BellOnWithHornLED->Checked) {
		ptrDL_Thread->Send_Delayed_DL_FIFO(this, Handle, DC_BELLOFF, ConsistNo, TIUNo, RemoteNo, 0, 0);
		}
	#else
	// Send CM_WHISTLE message to ourselves which will perform this function
	(void) PostMessage(Handle, CM_WHISTLE, false, NULL);	// false = button up
	#endif
    }
}
//---------------------------------------------------------------------------

void __fastcall TOp::DIRToggleStatusChanged(TObject *Sender)
{
// https://stackoverflow.com/questions/31649315/c-builder-button-click-function-reentry
LOCKEM()
if (Setup->Radio_COM_Mode->Checked) {
    if (PFAButton->Tag == false) {	// if PFA is not running, set speed to zero
	    // set speed to zero
    	SetSpeedSliderValue(0.0);	// causes the 's0' command to be sent if the speed isn't already zero
	    //MotionSoundRequest->Checked = true; 	// request sound on next train movement
        } else {
        // PFA is running in Radio mode
        PFAButtonCount++;	// count clicks
	    }
    }
if (Setup->WTIU_WiFi_Mode->Checked) {
    // WTIU WiFi mode does not use the Dir toggle switch for PFA
    // set speed to zero
   	SetSpeedSliderValue(0.0);	// causes the 's0' command to be sent if the speed isn't already zero
    //MotionSoundRequest->Checked = true; 	// request sound on next train movement
    }
// Send Direction Forward or Direction Reverse
if (DIRToggle->Checked) {
    // Foward
	(void)ptrTIUCommThread->Send_String_FIFO(ConsistNo, DIRECTION_FORWARD, TIUNo, RemoteNo, 11, SHORT_WAIT, RECORD_OK, LashUpEngineListOp);
    // reflect the change on to any open Quick Controls window
	if (ptrQuick_Dir) {
       	// The Dir Switch on the Quick Controls window uses OnStatusChanged
		DirOnValueChanged = ptrQuick_Dir->OnStatusChanged;
	    ptrQuick_Dir->OnStatusChanged = NULL;
		ptrQuick_Dir->Checked = true;
	    ptrQuick_Dir->OnStatusChanged = DirOnValueChanged;
        ptrQuick_EngNumber->Color = clLime;	// engine is started, set Engine Number field to green
        ptrQuick_RunState[ConsistNo] = clLime;
    	}
	} else {
   	// Reverse
	(void)ptrTIUCommThread->Send_String_FIFO(ConsistNo, DIRECTION_REVERSE, TIUNo, RemoteNo, 11, SHORT_WAIT, RECORD_OK, LashUpEngineListOp);
    // reflect the change on to any open Quick Controls window
	if (ptrQuick_Dir) {
       	// The Dir Switch on the Quick Controls window uses OnStatusChanged
		DirOnValueChanged = ptrQuick_Dir->OnStatusChanged;
	    ptrQuick_Dir->OnStatusChanged = NULL;
		ptrQuick_Dir->Checked = false;
	    ptrQuick_Dir->OnStatusChanged = DirOnValueChanged;
    	ptrQuick_EngNumber->Color = clLime;	// engine is started, set Engine Number field to green
        ptrQuick_RunState[ConsistNo] = clLime;
       	}
    }
// if PFA is running in Radio mode, check for 4 clicks
// WTIU WiFi mode does not use the Dir toggle switch for PFA
if ((Setup->Radio_COM_Mode->Checked) && PFAButton->Tag == true) {
    if (PFAButtonCount == 4) {
		// End the PFA sequence
        // Remote does not send a "u0" Command
		// (void)ptrTIUCommThread->Send_String_FIFO(ConsistNo, "u0", TIUNo, RemoteNo, 11, SHORT_WAIT, RECORD_OK, LashUpEngineListOp);
		PFAButton->Tag = false;
        PFAButton->ColorOff = clBtnFace;
        PFAButton->TextOff = "     PFA";
        PFAButton->Hint = "Begin PFA (shift click for automatic sequencing)";
        }
	}
SpeedSlider->SetFocus();	// focus to speed slider
}
//---------------------------------------------------------------------------

void __fastcall TOp::PFAButtonClick(TObject *Sender)
{
// This routine only used for Radio modes
//
const SHORT ShiftKeyState = GetKeyState(VK_SHIFT);
// key pressed =  0xFF8X
// key not pressed = 0x000X
// "X" can be 0 or 1 - indicated key was toggled
if (Setup->Radio_COM_Mode->Checked) {
    // PFA running in Radio mode on or off
    //
    if (PFAButton->Tag  && (ShiftKeyState < 0)) {	// PFA is running in manual mode so cancel it
        // Remote ends the PFA sequence with a "u0" command 
        (void)ptrTIUCommThread->Send_String_FIFO(ConsistNo, "u0", TIUNo, RemoteNo, 11, SHORT_WAIT, RECORD_OK, LashUpEngineListOp);
        PFAButton->Tag = false;		// indicate PFA is not running
        PFAButton->ColorOff = clBtnFace;
        PFAButtonCount = 0;
        SpeedSlider->SetFocus();	// focus to speed slider
        return;
        }
    PFAButton->Tag = true;	// indicate PFA is running
    PFAButton->ColorOff = clLime;
    // send the start PFA command
    (void)ptrTIUCommThread->Send_String_FIFO(ConsistNo, "u1", TIUNo, RemoteNo, 11, SHORT_WAIT, RECORD_OK, LashUpEngineListOp);
    // if the shift key is pressed, do the auto sequence
    // OR
    // if AutoPFA is set, do the auto sequence
    if ((ShiftKeyState < 0) || Setup->AutoPFA->Checked) {
        // if AutoPFA is set, do the automatic sequence of 4 DIR button pushes
        ptrDL_Thread->Send_Delayed_DL_FIFO(this, Handle, DC_PFA_DIR, ConsistNo, TIUNo, RemoteNo, (15) * 1000, 0);
        ptrDL_Thread->Send_Delayed_DL_FIFO(this, Handle, DC_PFA_DIR, ConsistNo, TIUNo, RemoteNo, (15+15) * 1000, 0);
        ptrDL_Thread->Send_Delayed_DL_FIFO(this, Handle, DC_PFA_DIR, ConsistNo, TIUNo, RemoteNo, (15+15+15) * 1000, 0);
        ptrDL_Thread->Send_Delayed_DL_FIFO(this, Handle, DC_PFA_DIR, ConsistNo, TIUNo, RemoteNo, (15+15+15+25) * 1000, 0);
        ptrDL_Thread->Send_Delayed_DL_FIFO(this, Handle, DC_PFA_DONE, ConsistNo, TIUNo, RemoteNo, (15+15+15+25+2) * 1000, 0);
        // DIRToggleStatusChanged will clear the PFA status and button color
        }
    // AutoPFA is not set so do the manual sequence
    PFAButtonCount = 0;	// used to count 4 presses of the Direction button
    // change button to Running
    PFAButton->TextOff = " Running";
    PFAButton->Hint = "Press the [Direction] button to proceed to next clip";
    }
return;
}

//---------------------------------------------------------------------------

void __fastcall TOp::PFAButtonStatusChanged(TObject *Sender)
{
// This routine only used for WTIU WiFi Mode
//
const SHORT ShiftKeyState = GetKeyState(VK_SHIFT);
// key pressed =  0xFF8X
// key not pressed = 0x000X
// "X" can be 0 or 1 - indicated key was toggled
if (Setup->WTIU_WiFi_Mode->Checked && PFAButton->Checked) {
    // WTIU WiFi mode does not use the Dir toggle switch for PFA
    // if PFA is running and click with shift key then cancel
    if (PFAButton->Tag && (ShiftKeyState < 0)) {	// PFA is running in manual mode so cancel it
        // End the PFA sequence
        (void)ptrTIUCommThread->Send_String_FIFO(ConsistNo, "u0", TIUNo, RemoteNo, 11, SHORT_WAIT, RECORD_OK, LashUpEngineListOp);
        PFAButton->Tag = false;		// indicate PFA is not running
        PFAButton->ColorOff = clBtnFace;
        PFAButtonCount = 0;
        PFAButton->TextOff = "     PFA";    // change button to PFA
        PFAButton->Hint = "Begin PFA (shift click for automatic sequencing)";
        SpeedSlider->SetFocus();	// focus to speed slider
        return;
        }
    switch (PFAButtonCount) {
        case 0:
            PFAButton->Tag = true;	    // indicate PFA is running
            PFAButton->ColorOff = clLime;
            // if the shift key is pressed, do the auto sequence
            // OR
            // if AutoPFA is set, do the auto sequence
            if ((ShiftKeyState < 0) || Setup->AutoPFA->Checked) {
                // if AutoPFA is set, do the automatic sequence of 5 DIR button pushes
                ptrDL_Thread->Send_Delayed_DL_FIFO(this, Handle, DC_PFA_PFA, ConsistNo, TIUNo, RemoteNo, (1) * 1000, 0);
                ptrDL_Thread->Send_Delayed_DL_FIFO(this, Handle, DC_PFA_PFA, ConsistNo, TIUNo, RemoteNo, (1+15) * 1000, 0);
                ptrDL_Thread->Send_Delayed_DL_FIFO(this, Handle, DC_PFA_PFA, ConsistNo, TIUNo, RemoteNo, (1+15+15) * 1000, 0);
                ptrDL_Thread->Send_Delayed_DL_FIFO(this, Handle, DC_PFA_PFA, ConsistNo, TIUNo, RemoteNo, (1+15+15+15) * 1000, 0);
                ptrDL_Thread->Send_Delayed_DL_FIFO(this, Handle, DC_PFA_PFA, ConsistNo, TIUNo, RemoteNo, (1+15+15+15+15) * 1000, 0);
                ptrDL_Thread->Send_Delayed_DL_FIFO(this, Handle, DC_PFA_PFA, ConsistNo, TIUNo, RemoteNo, (1+15+15+15+15+25) * 1000, 0);
                ptrDL_Thread->Send_Delayed_DL_FIFO(this, Handle, DC_PFA_DONE, ConsistNo, TIUNo, RemoteNo, (1+15+15+15+15+25+5) * 1000, 0);
                PFAButtonCount++;   // count clicks
                // change button to Activating
                PFAButton->TextOff = " Activating";
                PFAButton->Hint = "Activating PFA Sounds (shift click to cancel)";
                break;
                }

            PFAButtonCount++;	// count clicks
            // change button to Activate
            PFAButton->TextOff = "  Activate";
            PFAButton->Hint = "Click to Activate PFA Sounds (shift click to cancel)";
            break;
        case 1:     // Activate
            // Send u1 the start PFA command
            (void)ptrTIUCommThread->Send_String_FIFO(ConsistNo, "u1", TIUNo, RemoteNo, 11, SHORT_WAIT, RECORD_OK, LashUpEngineListOp);
            PFAButtonCount++;	// count clicks
            // change button to Arrival
            PFAButton->TextOff = "  Arrival";
            PFAButton->Hint = "Playing Arrival Sounds - Click to play Segment 1 Sounds (shift click to cancel)";
            break;
        case 2:     // Arrival
            // Send m24
            (void)ptrTIUCommThread->Send_String_FIFO(ConsistNo, "m24", TIUNo, RemoteNo, 11, SHORT_WAIT, RECORD_OK, LashUpEngineListOp);
            PFAButtonCount++;	// count clicks
            // change button to Segment 1
            PFAButton->TextOff = " Segment 1";
            PFAButton->Hint = "Playing Segment 1 Sounds - Click to play Segment 2 Sounds  (shift click to cancel)";
            break;
        case 3:     // Segment 1
            // Send m24
            (void)ptrTIUCommThread->Send_String_FIFO(ConsistNo, "m24", TIUNo, RemoteNo, 11, SHORT_WAIT, RECORD_OK, LashUpEngineListOp);
            PFAButtonCount++;	// count clicks
            // change button to Segment 2
            PFAButton->TextOff = " Segment 2";
            PFAButton->Hint = "Playing Segment 2 Sounds - Click to play Departure Sounds (shift click to cancel)";
            break;
        case 4:     // Segment 2
            // Send m24
            (void)ptrTIUCommThread->Send_String_FIFO(ConsistNo, "m24", TIUNo, RemoteNo, 11, SHORT_WAIT, RECORD_OK, LashUpEngineListOp);
            PFAButtonCount++;	// count clicks
            // change button to departure
            PFAButton->TextOff = "  Departure";
            PFAButton->Hint = "Playing Departure Sounds - Click to Finish PFA Sounds  (shift click to cancel)";
            break;
        case 5:     // Departure
            // Send m24
            (void)ptrTIUCommThread->Send_String_FIFO(ConsistNo, "m24", TIUNo, RemoteNo, 11, SHORT_WAIT, RECORD_OK, LashUpEngineListOp);
            PFAButtonCount++;	// count clicks
            // change button to Close
            PFAButton->TextOff = "    Close";
            PFAButton->Hint = "Finishing PFA Sounds (shift click to cancel)";
            break;
        case 6:     // Close
            // Send u0 End the PFA sequence
            (void)ptrTIUCommThread->Send_String_FIFO(ConsistNo, "u0", TIUNo, RemoteNo, 11, SHORT_WAIT, RECORD_OK, LashUpEngineListOp);
            PFAButton->Tag = false;
            PFAButton->ColorOff = clBtnFace;
            PFAButtonCount = 0;	// used to count presses of the PFA button
            // change button to PFA
            PFAButton->TextOff = "     PFA";
            PFAButton->Hint = "Begin PFA (shift click for automatic sequencing)";
            break;
        }
    }
}
//---------------------------------------------------------------------------

void __fastcall TOp::CMStopPFA(TMessage &Message)
{
// Send u0 End the PFA sequence
(void)ptrTIUCommThread->Send_String_FIFO(ConsistNo, "u0", TIUNo, RemoteNo, 11, SHORT_WAIT, RECORD_OK, LashUpEngineListOp);
PFAButton->Tag = false;
PFAButton->ColorOff = clBtnFace;
PFAButtonCount = 0;	// used to count presses of the PFA button
// change button to PFA
PFAButton->TextOff = "     PFA";
PFAButton->Hint = "Begin PFA (shift click for automatic sequencing)";}
//---------------------------------------------------------------------------

void __fastcall TOp::HDLTSwitchStatusChanged(TObject *Sender)
{
// Send HDLT on or HDLT off
if (HDLTSwitch->Checked) {
    // ON
	(void)ptrTIUCommThread->Send_String_FIFO(ConsistNo, "ab1", TIUNo, RemoteNo, 11, SHORT_WAIT, RECORD_OK, LashUpEngineListOp);
	} else {
   	// OFF
	(void)ptrTIUCommThread->Send_String_FIFO(ConsistNo, "ab0", TIUNo, RemoteNo, 11, SHORT_WAIT, RECORD_OK, LashUpEngineListOp);
    }
}
//---------------------------------------------------------------------------

void __fastcall TOp::HideButtonClick(TObject *Sender)
{
Hide();	      // Same as setting Visible to false
}
//---------------------------------------------------------------------------

void __fastcall TOp::SmokeSwitchStatusChanged(TObject *Sender)
{
// Send Smoke/Exhaust on or Smoke/Exhaust off
if (!SmokeSwitch->Enabled) return;
//
if (SmokeSwitch->Checked) {
    // ON
	(void)ptrTIUCommThread->Send_String_FIFO(ConsistNo, SMOKE_EXHAUST_ON, TIUNo, RemoteNo, 11, SHORT_WAIT, RECORD_OK, LashUpEngineListOp);
    // reflect the change on to any open Quick Controls window
	if (ptrQuick_Smoke) {
	    // The Smoke Switch on the Quick Controls window uses OnStatusChanged
		SmokeOnValueChanged = ptrQuick_Smoke->OnStatusChanged;
	    ptrQuick_Smoke->OnStatusChanged = NULL;
		ptrQuick_Smoke->Checked = true;
	    ptrQuick_Smoke->OnStatusChanged = SmokeOnValueChanged;
    	}
	} else {
   	// OFF
	(void)ptrTIUCommThread->Send_String_FIFO(ConsistNo, SMOKE_EXHAUST_OFF, TIUNo, RemoteNo, 11, SHORT_WAIT, RECORD_OK, LashUpEngineListOp);
	// reflect the change on to any open Quick Controls window
	if (ptrQuick_Smoke) {
	    // The Smoke Switch on the Quick Controls window uses OnStatusChanged
		SmokeOnValueChanged = ptrQuick_Smoke->OnStatusChanged;
	    ptrQuick_Smoke->OnStatusChanged = NULL;
		ptrQuick_Smoke->Checked = false;
	    ptrQuick_Smoke->OnStatusChanged = SmokeOnValueChanged;
    	}
    }
}
//---------------------------------------------------------------------------

void __fastcall TOp::ClickClackSwitchStatusChanged(TObject *Sender)
{
// Send FCC Clickety-Clack auto or Clickety-Clack off  FCC
if (!ClickClackSwitch->Enabled) return;
//
if (ClickClackSwitch->Checked) {
   	// Auto - normally "ab15"
	(void)ptrTIUCommThread->Send_String_FIFO(ConsistNo, "ab15", TIUNo, RemoteNo, 11, SHORT_WAIT, RECORD_OK, LashUpEngineListOp);
	} else {
   	// OFF - normally "ab16"
	(void)ptrTIUCommThread->Send_String_FIFO(ConsistNo, "ab16", TIUNo, RemoteNo, 11, SHORT_WAIT, RECORD_OK, LashUpEngineListOp);
    }
}
//---------------------------------------------------------------------------

void __fastcall TOp::SmokeGroupClick(TObject *Sender)
{
// Smoke Level
if (!SmokeGroup->Enabled) return;
// ab10	Maximum
// ab11	Medium
// ab12	Minimum
char str[15];
snprintf(str, sizeof str, "ab1%d", SmokeGroup->ItemIndex);
(void)ptrTIUCommThread->Send_String_FIFO(ConsistNo, str, TIUNo, RemoteNo, 11, SHORT_WAIT, RECORD_OK, LashUpEngineListOp);
}
//---------------------------------------------------------------------------

void __fastcall TOp::AccSpinChange(TObject *Sender)
{
// Send new Acc rate to Engine
int AccRate = AccSpin->Value;
// Convert value into chars
char str[15];
snprintf(str, sizeof str, ACC_RATE, AccRate);
(void)ptrTIUCommThread->Send_String_FIFO(ConsistNo, str, TIUNo, RemoteNo, 11, SHORT_WAIT, RECORD_OK, LashUpEngineListOp);
}
//---------------------------------------------------------------------------

void __fastcall TOp::DecSpinChange(TObject *Sender)
{
// Send new Dec rate to Engine
int DecRate = DecSpin->Value;
// Convert value into chars
char str[15];
snprintf(str, sizeof str, DEC_RATE, DecRate);
(void)ptrTIUCommThread->Send_String_FIFO(ConsistNo, str, TIUNo, RemoteNo, 11, SHORT_WAIT, RECORD_OK, LashUpEngineListOp);
}
//---------------------------------------------------------------------------

void __fastcall TOp::AccSpinValueChanged(TObject *Sender)
{
AccSpinChange(AccSpin);
}
//---------------------------------------------------------------------------

void __fastcall TOp::DecSpinValueChanged(TObject *Sender)
{
DecSpinChange(DecSpin);
}
//---------------------------------------------------------------------------

void __fastcall TOp::SetStandardAccDecValues(int EngineNo, int TIUNo)
{
// Set Acc and Dec values to 1 (the standard values)
assert(EngineNo <= ALLENGINE);
if (EngineNo == ALLENGINE) {    // all engine mode, force standard acc/dec
    // set spinners correctly and send Da command
    AccSpin->Value = 1;
    DecSpin->Value = 1;
    return;
    }
// read Acc/Dec rates from engine
int iAcc, iDec;	// routine returns default values
(void) MainForm->GetAccDecValues(EngineNo, TIUNo, &iAcc, &iDec);
if (iAcc == 1) {
    // Engine has standard value
    // Set spinner without sending Da command
    AccSpinOnValueChanged = AccSpinValueChanged;    // disable OnValueChanged event handler
    AccSpin->OnValueChanged = NULL;
    AccSpin->Value = 1;
    AccSpin->OnValueChanged = AccSpinOnValueChanged;    // enable OnValueChanged event handler
    } else {
    // set spinner correctly and send Da command
    AccSpin->Value = 1;
    }
if (iDec == 1) {
    // Engine has standard value
    // Set spinner without sending Dd command
    DecSpinOnValueChanged = DecSpinValueChanged;    // disable OnValueChanged event handler
    DecSpin->OnValueChanged = NULL;
    DecSpin->Value = 1;
    DecSpin->OnValueChanged = DecSpinOnValueChanged;    // enable OnValueChanged event handler
    } else {
    // set spinner correctly and send Dd command
    DecSpin->Value = 1;
    }
}
//---------------------------------------------------------------------------

void __fastcall TOp::CabChatSwitchStatusChanged(TObject *Sender)
{
if (!CabChatSwitch->Enabled) return;
// Send Cab Chatter Auto or off  FCH
//
if (CabChatSwitch->Checked) {
    // Auto - normally "ab13"
	(void)ptrTIUCommThread->Send_String_FIFO(ConsistNo, "ab13", TIUNo, RemoteNo, 11, SHORT_WAIT, RECORD_OK, LashUpEngineListOp);
	} else {
   	// OFF - normally "ab14"
	(void)ptrTIUCommThread->Send_String_FIFO(ConsistNo, "ab14", TIUNo, RemoteNo, 11, SHORT_WAIT, RECORD_OK, LashUpEngineListOp);
    }
}
//---------------------------------------------------------------------------

void __fastcall TOp::ProtoChuffSwitchStatusChanged(TObject *Sender)
{
if (!ProtoChuffSwitch->Enabled) return;
// Send ProtoChuff Auto or off
switch (EngineTypeComboBox->ItemIndex) {
	case STEAM_ENGINE:
		if (ProtoChuffSwitch->Checked) { 	// Steam Engine
	    	// Auto
			// Turn any Labor chuff off
	        RevLevelBar->Value = 1;
	   		(void)ptrTIUCommThread->Send_String_FIFO(ConsistNo, "r15", TIUNo, RemoteNo, 11, SHORT_WAIT, RECORD_OK, LashUpEngineListOp);
	        // set ProtoChuff to Auto
			(void)ptrTIUCommThread->Send_String_FIFO(ConsistNo, "ab19", TIUNo, RemoteNo, 11, SHORT_WAIT, RECORD_OK, LashUpEngineListOp);
			} else {
	   		// OFF
			(void)ptrTIUCommThread->Send_String_FIFO(ConsistNo, "ab1A", TIUNo, RemoteNo, 11, SHORT_WAIT, RECORD_OK, LashUpEngineListOp);
	    	}
	    break;
    case DIESEL_ENGINE:
    case ELECTRIC_ENGINE:
		if (ProtoChuffSwitch->Checked) { 	// Diesel & Electric Engines
   		 	// Auto
	        // Turn any Rev Level off
	        RevLevelBar->Value = 1;
	        (void)ptrTIUCommThread->Send_String_FIFO(ConsistNo, "g1", TIUNo, RemoteNo, 11, SHORT_WAIT, RECORD_OK, LashUpEngineListOp);
	        // set ProtoRev to Auto
			(void)ptrTIUCommThread->Send_String_FIFO(ConsistNo, "ab19", TIUNo, RemoteNo, 11, SHORT_WAIT, RECORD_OK, LashUpEngineListOp);
			} else {
	   		// OFF
			(void)ptrTIUCommThread->Send_String_FIFO(ConsistNo, "ab1A", TIUNo, RemoteNo, 11, SHORT_WAIT, RECORD_OK, LashUpEngineListOp);
	    	}
	    break;
    }
}
//---------------------------------------------------------------------------

void __fastcall TOp::ChuffRateSwitchStatusChanged(TObject *Sender)
{
// Send Chuff Rate 4 or 2  - NOTE: 'g' command is also used for Diesel Notch level engine sounds
if (!ChuffRateSwitch->Enabled) return;
// NOTE : Chuff rate can be anyvalue between 1 and 16 - RTC only uses 2 and 4 chuffs per revolution
switch (EngineTypeComboBox->ItemIndex) {
	case STEAM_ENGINE:
		if (ChuffRateSwitch->Checked) { 	// Steam Engine Only
	    	// 4 Chuffs per Revolution
			(void)ptrTIUCommThread->Send_String_FIFO(ConsistNo, "g4", TIUNo, RemoteNo, 11, SHORT_WAIT, RECORD_OK, LashUpEngineListOp);
			} else {
	   		// 2 Chuffs per Revolution
			(void)ptrTIUCommThread->Send_String_FIFO(ConsistNo, "g2", TIUNo, RemoteNo, 11, SHORT_WAIT, RECORD_OK, LashUpEngineListOp);
	    	}
	    break;
    case DIESEL_ENGINE:
    case ELECTRIC_ENGINE:
        // NIO
    	break;
    }
}
//---------------------------------------------------------------------------

void __fastcall TOp::BrakesSwitchStatusChanged(TObject *Sender)
{
// Send Brake Sound Auto or off
if (BrakesSwitch->Checked) {
    // Auto
	(void)ptrTIUCommThread->Send_String_FIFO(ConsistNo, "ab17", TIUNo, RemoteNo, 11, SHORT_WAIT, RECORD_OK, LashUpEngineListOp);
	} else {
   	// OFF
	(void)ptrTIUCommThread->Send_String_FIFO(ConsistNo, "ab18", TIUNo, RemoteNo, 11, SHORT_WAIT, RECORD_OK, LashUpEngineListOp);
    }
}

//---------------------------------------------------------------------------

void __fastcall TOp::SFSButtonMouseDown(TObject *Sender,
      TMouseButton Button, TShiftState Shift, int X, int Y)
{
if (Button == mbLeft) {
	// SFS on
	// Forward Sound
    // reflect the change on to any open Quick Controls window
	if (ptrQuick_SFS) {
    	// The SFS Button on the Quick Controls window uses OnMouseDown but that won't trigger on changing the Checked property
		//ptrQuick_SFS->Checked = true;		// this does not work to change the look of the button
        ptrQuick_SFS->GradBtnFace->Visible = true;
		}
	#ifdef BUTTONMETHOD
	if (ProtoWhistleSwitch->Checked) return;
	//
	(void)ptrTIUCommThread->Send_String_FIFO(ConsistNo, DOUBLE_TOOT, TIUNo, RemoteNo, 11, SHORT_WAIT, RECORD_OK, LashUpEngineListOp);
	#else
	// Send CM_SFS message to ourselves which will perform this function
	(void) PostMessage(Handle, CM_SFS, NULL, NULL);
	#endif
    }
}
//---------------------------------------------------------------------------

void __fastcall TOp::SFSButtonMouseUp(TObject *Sender, TMouseButton Button,
      TShiftState Shift, int X, int Y)
{
if (Button == mbLeft) {
    // reflect the change on to any open Quick Controls window
	if (ptrQuick_SFS) {
    	// The SFS Button on the Quick Controls window uses OnMouseDown but that won't trigger on changing the Checked property
		//ptrQuick_SFS->Checked = false;	// this does not work to change the look of the button
        ptrQuick_SFS->GradBtnFace->Visible = false;
    	}
    }
}
//---------------------------------------------------------------------------

void __fastcall TOp::CMSFS(TMessage &Message)
{
// Handle Message : SFS ON
// Forward Sound
// if Proto Whistle is on, disable this action
if (ProtoWhistleSwitch->Checked) return;
//
(void)ptrTIUCommThread->Send_String_FIFO(ConsistNo, DOUBLE_TOOT, TIUNo, RemoteNo, 11, SHORT_WAIT, RECORD_OK, LashUpEngineListOp);
}
//---------------------------------------------------------------------------

void __fastcall TOp::AlternateHornButtonMouseDown(TObject *Sender,
      TMouseButton Button, TShiftState Shift, int X, int Y)
{
if (Button == mbLeft) {
	// SAH on
	// Alternate Horn Sound
	#ifdef BUTTONMETHOD
	if (ProtoWhistleSwitch->Checked) return;
	//
	(void)ptrTIUCommThread->Send_String_FIFO(ConsistNo, "n243", TIUNo, RemoteNo, 11, SHORT_WAIT, RECORD_OK, LashUpEngineListOp);
	#else
	// Send CM_SAH message to ourselves which will perform this function
	(void) PostMessage(Handle, CM_SAH, NULL, NULL);
	#endif
	}
}
//---------------------------------------------------------------------------

void __fastcall TOp::CMSAH(TMessage &Message)
{
// Handle Message : SAH ON
// Alternate Horn Sound
// if Proto Whistle is on, disable this action
if (ProtoWhistleSwitch->Checked) return;
//
(void)ptrTIUCommThread->Send_String_FIFO(ConsistNo, "n243", TIUNo, RemoteNo, 11, SHORT_WAIT, RECORD_OK, LashUpEngineListOp);
}
//---------------------------------------------------------------------------

void __fastcall TOp::SRSButtonMouseDown(TObject *Sender,
      TMouseButton Button, TShiftState Shift, int X, int Y)
{
if (Button == mbLeft) {
	// SRS on
	// Reverse Sound
    // reflect the change on to any open Quick Controls window
	if (ptrQuick_SRS) {
    	// The SRS Button on the Quick Controls window uses OnMouseDown but that won't trigger on changing the Checked property
		//ptrQuick_SRS->Checked = true;		// this does not work to change the look of the button
        ptrQuick_SRS->GradBtnFace->Visible = true;
		}
	#ifdef BUTTONMETHOD
	if (ProtoWhistleSwitch->Checked) return;
	//
	(void)ptrTIUCommThread->Send_String_FIFO(ConsistNo, TRIPLE_TOOT, TIUNo, RemoteNo, 11, SHORT_WAIT, RECORD_OK, LashUpEngineListOp);
	#else
	// Send CM_SRS message to ourselves which will perform this function
	(void) PostMessage(Handle, CM_SRS, NULL, NULL);
	#endif
    }
}
//---------------------------------------------------------------------------

void __fastcall TOp::CMSRS(TMessage &Message)
{
// Handle Message :  SRS ON
// Reverse Sound
// if Proto Whistle is on, disable this action
if (ProtoWhistleSwitch->Checked) return;
//
(void)ptrTIUCommThread->Send_String_FIFO(ConsistNo, TRIPLE_TOOT, TIUNo, RemoteNo, 11, SHORT_WAIT, RECORD_OK, LashUpEngineListOp);
}
//---------------------------------------------------------------------------

void __fastcall TOp::SXSButtonMouseDown(TObject *Sender,
      TMouseButton Button, TShiftState Shift, int X, int Y)
{
if (!SXSButton->Enabled) return;
//
if (Button == mbLeft) {
	// SXS on
	// Crossing Sound
    // reflect the change on to any open Quick Controls window
	if (ptrQuick_SXS) {
    	// The SXS Button on the Quick Controls window uses OnMouseDown but that won't trigger on changing the Checked property
		//ptrQuick_SXS->Checked = true;		// this does not work to change the look of the button
        ptrQuick_SXS->GradBtnFace->Visible = true;
		}
	#ifdef BUTTONMETHOD
	// if Proto Whistle is on, disable this action
	if (ProtoWhistleSwitch->Enabled && ProtoWhistleSwitch->Checked) return;
	//
	if (BellOnWithHornLED->Checked) {
		// if Bell should be sounded, start it ringing
		BellSwitch->Checked = true;
		}
	// Crossing Sound
	(void)ptrTIUCommThread->Send_String_FIFO(ConsistNo, "n42", TIUNo, RemoteNo, 11, SHORT_WAIT, RECORD_OK, LashUpEngineListOp);
	//
	if (BellOnWithHornLED->Checked) {
		ptrDL_Thread->Send_Delayed_DL_FIFO(this, Handle, DC_BELLOFF, ConsistNo, TIUNo, RemoteNo, 0, 0);
		}
	#else
	// Send CM_SXS message to ourselves which will perform this function
	(void) PostMessage(Handle, CM_SXS, NULL, NULL);
	#endif
	}
}
//---------------------------------------------------------------------------

void __fastcall TOp::CMSXS(TMessage &Message)
{
// Handle Message : SXS ON
//
if (!SXSButton->Enabled) return;
//
// if Proto Whistle is on, disable this action
if (ProtoWhistleSwitch->Enabled && ProtoWhistleSwitch->Checked) return;
//
if (BellOnWithHornLED->Checked) {
	// if Bell should be sounded, start it ringing
	BellSwitch->Checked = true;
	}
// Crossing Sound
(void)ptrTIUCommThread->Send_String_FIFO(ConsistNo, "n42", TIUNo, RemoteNo, 11, SHORT_WAIT, RECORD_OK, LashUpEngineListOp);
//
if (BellOnWithHornLED->Checked) {
	ptrDL_Thread->Send_Delayed_DL_FIFO(this, Handle, DC_BELLOFF, ConsistNo, TIUNo, RemoteNo, 0, 0);
	}
}
//---------------------------------------------------------------------------

void __fastcall TOp::CPLRFButtonMouseDown(TObject *Sender,
      TMouseButton Button, TShiftState Shift, int X, int Y)
{
if (Button == mbLeft) {
	//CPLR-F
	//if (DEBUGGING) DB->Debug_Print("CPLRFButtonMouseDown");
	// Uncouple Front
    // reflect the change on to any open Quick Controls window
	if (ptrQuick_CPLRF) {
    	// The CPLRF Button on the Quick Controls window uses OnMouseDown but that won't trigger on changing the Checked property
		//ptrQuick_CPLRF->Checked = true;		// this does not work to change the look of the button
        ptrQuick_CPLRF->GradBtnFace->Visible = true;
		}
	#ifdef BUTTONMETHOD
	(void)ptrTIUCommThread->Send_String_FIFO(ConsistNo, COUPLER_FRONT, TIUNo, RemoteNo, 11, VERY_LONG_WAIT, RECORD_OK, LashUpEngineListOp);
	#else
	// Send CM_CPRLF message to ourselves which will perform this function
	(void) PostMessage(Handle, CM_CPLRF, true, NULL);	// true = front coupler
	#endif
	}
}
//---------------------------------------------------------------------------

void __fastcall TOp::CMCPLRF(TMessage &Message)
{
// Handle Message : Uncouple Front
(void)ptrTIUCommThread->Send_String_FIFO(ConsistNo, COUPLER_FRONT, TIUNo, RemoteNo, 11, VERY_LONG_WAIT, RECORD_OK, LashUpEngineListOp);
}
//---------------------------------------------------------------------------

void __fastcall TOp::CPLRRButtonMouseDown(TObject *Sender,
      TMouseButton Button, TShiftState Shift, int X, int Y)
{
if (Button == mbLeft) {
	//CPLR-R
	//if (DEBUGGING) DB->Debug_Print("CPLRRButtonMouseDown");
	// Uncouple Rear
    // reflect the change on to any open Quick Controls window
	if (ptrQuick_CPLRR) {
    	// The CPLRR Button on the Quick Controls window uses OnMouseDown but that won't trigger on changing the Checked property
		//ptrQuick_CPLRR->Checked = true;		// this does not work to change the look of the button
        ptrQuick_CPLRR->GradBtnFace->Visible = true;
		}
	#ifdef BUTTONMETHOD
	(void)ptrTIUCommThread->Send_String_FIFO(ConsistNo, COUPLER_REAR, TIUNo, RemoteNo, 11, VERY_LONG_WAIT, RECORD_OK, LashUpEngineListOp);
	#else		
	// Send CM_CPRLR message to ourselves which will perform this function
	(void) PostMessage(Handle, CM_CPLRR, false, NULL);	// false = rear coupler
	#endif
    }
}
//---------------------------------------------------------------------------

void __fastcall TOp::CMCPLRR(TMessage &Message)
{
// Handle Message : Uncouple Rear
(void)ptrTIUCommThread->Send_String_FIFO(ConsistNo, COUPLER_REAR, TIUNo, RemoteNo, 11, VERY_LONG_WAIT, RECORD_OK, LashUpEngineListOp);
}
//---------------------------------------------------------------------------

void __fastcall TOp::OpShutdownClick(TObject *Sender)
{
if (!MainForm->IsTIUConnected()) {
	MainForm->DebugMessagePopup("Warning", MainForm->ConnectMsg, "");
	return;
    }

// disable the [Shutdown] button if this is ALLENGINE mode
MainForm->ShutdownButton->Enabled = (ConsistNo == ALLENGINE) ? false : true;
//
//MainForm->AltKeyState = OpShutdown->Focused() ? GetKeyState(VK_MENU) : 0;		// Not used yet
MainForm->ShiftKeyState = OpShutdown->Focused() ? GetKeyState(VK_SHIFT) : 0;	// Use Extended Shutdown
//MainForm->CtrlKeyState = OpShutdown->Focused() ? GetKeyState(VK_CONTROL) : 0;	// Not used yet
// key pressed =  0xFF8X
// key not pressed = 0x000X
// "X" can be 0 or 1 - indicated key was toggled

// if Quick Controls has this Consist displayed, change the color to indicate it is shutdown
if (ptrQuick_EngNumber) {
	ptrQuick_EngNumber->Color = clRed;	// engine is shutdown, set Engine Number field to red
    ptrQuick_RunState[ConsistNo] = clRed;
    }
// turn sound back on
SoundsSwitch->Checked = true;
// Send CM_ SHUTDOWN or CM_ LASHUPSHUTDOWN message to MainForm which will set speed to zero and issue shutdown command
// It will also close the Volume Control window if it is open.
//
if (ConsistNo < LASHUPMIN) {
    (void) PostMessage(MainForm->Handle, CM_SHUTDOWN, TIUNo, ConsistNo);
    } else {
    (void) PostMessage(MainForm->Handle, CM_LASHUPSHUTDOWN, (unsigned int) LashUpEngineListOp, (TIUNo << 8) | ConsistNo);
    }
// KeyState values are cleared in MainForm
}
//---------------------------------------------------------------------------

void __fastcall TOp::ESTOPClick(TObject *Sender)
{
// emergency stop
#ifdef BUTTONMETHOD
MainForm->EmergencyStopAllEngines();	// Stop Engines and take down windows
#else
// Send CM_EMERGENCYSTOP message to MainForm
(void) PostMessage(MainForm->Handle, CM_EMERGENCYSTOP, 0, 0);
#endif
}
//---------------------------------------------------------------------------

void __fastcall TOp::BoostButtonStatusChanged(TObject *Sender)
{
// Boost
// Increase speed by 1 Smph per timer interval until released
static float SavedSpeed;

if (BoostButton->Checked) {
	if (SpeedSlider->Value == SpeedSlider->SignalSettings->ValueTo) return;	// no effect when max
    SavedSpeed = SpeedSlider->Value;
	bBoosting = true;
    BoostBrakeTimerTimer(BoostBrakeTimer);	// Immediate Boost
    BoostBrakeTimer->Enabled = true;
    if (LastSentSpeedValue == 0) BoostBrakeTimer->Interval = 5000;	// five seconds if we start out at zero
    // every timer interval, boost the speed
	}

if (!BoostButton->Checked) {
	// button released - turn off the timer
    bBoosting = false;
    BoostBrakeTimer->Enabled = false;
    if (BRAKEBOOSTSTAY) {
    	// stay at new speed - so do nothing
        return;
        } else {
        // return to saved speed
        SetSpeedSliderValue(SavedSpeed);	// causes the 's' command to be sent
        return;
        }
	}
}
//---------------------------------------------------------------------------

void __fastcall TOp::BoostBrakeTimerTimer(TObject *Sender)
{
BoostBrakeTimer->Enabled = false;	// disable the timer while in this routine
if (bBoosting) {
	if (LastSentSpeedValue != SpeedSlider->SignalSettings->ValueTo) {	// Only when not at maximum
		float fMPH = SpeedSlider->Value + 1.0;	// increment by 1;
        SetSpeedSliderValue(fMPH);  // causes the 's' command to be sent preceeded by "h1" if needed
    	}
    }
if (bBraking) {
	if (LastSentSpeedValue != 0) {	// Only when not stopped
		float fMPH = SpeedSlider->Value - 1.0;	// decrement by 1
        SetSpeedSliderValue(fMPH);  // causes the 's' command to be sent
        }
	}
BoostBrakeTimer->Interval = 1000;	// one second
BoostBrakeTimer->Enabled = true;	// enable the timer on normal exit
}
//---------------------------------------------------------------------------

void __fastcall TOp::BrakeButtonStatusChanged(TObject *Sender)
{
// Brake
// Decrease speed by 1 Smph per timer interval until released
static float SavedSpeed;

if (BrakeButton->Checked) {
	if (LastSentSpeedValue == 0) return;	// no effect when stopped
    SavedSpeed = SpeedSlider->Value;
	bBraking = true;
    BoostBrakeTimerTimer(BoostBrakeTimer);	// Immediate Brake
    BoostBrakeTimer->Enabled = true;
    // every timer interval, brake the speed
	}

if (!BrakeButton->Checked) {
	// button released - turn off the timer
    bBraking = false;
    BoostBrakeTimer->Enabled = false;
    if (BRAKEBOOSTSTAY) {
    	// stay at new speed - so do nothing
        return;
        } else {
        // return to saved speed
        SetSpeedSliderValue(SavedSpeed); // causes the 's' command to be sent
        return;
        }
	}
}
//---------------------------------------------------------------------------

void __fastcall TOp::LaborRevUpButtonMouseDown(TObject *Sender,
      TMouseButton Button, TShiftState Shift, int X, int Y)
{
if (Button == mbLeft) {
	//if (DEBUGGING) DB->Debug_Print("LaborRevUpButtonMouseDown");
	// Send CM_LABORREVUP message to ourselves which will perform this function
	(void) PostMessage(Handle, CM_LABORREVUP, true, NULL);     // true = button down
    }
}
//---------------------------------------------------------------------------

void __fastcall TOp::CMLaborRevUp(TMessage &Message)
{
// Handle Message : Labor Rev Up
//
//if (DEBUGGING) DB->Debug_Print("CMLaborRevUp");

const bool Dn = Message.WParam;			// set button state from Word Parameter
if (Dn)	{	// button down
	// Labor / Rev Up On
    iRevLevel = RevLevelBar->Value;
	// Proto Chuff/Rev automatically turned off
	ProtoChuffSwitch->Checked = false;
	switch (EngineTypeComboBox->ItemIndex) {
		case STEAM_ENGINE:
			// for Steam Engines
	    	// start Labor
	    	(void)ptrTIUCommThread->Send_String_FIFO(ConsistNo, "r16", TIUNo, RemoteNo, 11, SHORT_WAIT, RECORD_OK, LashUpEngineListOp);
            // If smoke is on and Auto Labored Smoke is on, raise smoke level to maximum
			if (SmokeSwitch->Checked) {
            	if (AutoLaboredSmokeLED->Checked) {
                    // Smoke to max
                    bLaboredSmoke = true;
                    SavedSmokeLevel = SmokeGroup->ItemIndex;
                    SmokeGroup->ItemIndex = 0;	// Maximum Smoke
                    SmokeGroup->OnClick(SmokeGroup);
                	}
            	}
	    	break;
	    case DIESEL_ENGINE:
	    case ELECTRIC_ENGINE:
			// for Diesel & Electric Engines
		    if (iRevLevel != 8) {
		    	// Rev Up
		    	iRevLevel++;
	            RevLevelBar->Value = iRevLevel;
		        char str[15];
	 	       (void)snprintf(str, sizeof str, "g%d", iRevLevel);
				(void)ptrTIUCommThread->Send_String_FIFO(ConsistNo, str, TIUNo, RemoteNo, 11, SHORT_WAIT, RECORD_OK, LashUpEngineListOp);
		        }
            // if exhaust is on and Auto Labored Smoke (Exhaust) is on, change exhaust level based on rev number
			if (SmokeSwitch->Checked) {
            	if (AutoLaboredSmokeLED->Checked) {
                	if (iRevLevel <= 3) {
                    	SmokeGroup->ItemIndex = 2;	// minimum exhaust
	                    } else {
    	                if (iRevLevel > 3 && iRevLevel < 7) {
        	            	// exhaust to Medium
            	            SmokeGroup->ItemIndex = 1;	// medium exhaust
                            } else {
                            SmokeGroup->ItemIndex = 0;	// maximum exhaust
                            }
                        }
                    SmokeGroup->OnClick(SmokeGroup);
                	}
                }
	        break;
		}
	}
if (!Dn) {	// button up
	// Labor Chuff Off
	switch (EngineTypeComboBox->ItemIndex) {
		case STEAM_ENGINE:
			// for Steam Engines
   			// stop Labor
			(void)ptrTIUCommThread->Send_String_FIFO(ConsistNo, "r15", TIUNo, RemoteNo, 11, SHORT_WAIT, RECORD_OK, LashUpEngineListOp);
	        // If smoke is on and Auto Labored Smoke is on, return smoke level to saved value
			if (SmokeSwitch->Checked) {
            	if (AutoLaboredSmokeLED->Checked) {
                   	bLaboredSmoke = false;
                    // set smoke to saved value
                    SmokeGroup->ItemIndex = SavedSmokeLevel;
                    SmokeGroup->OnClick(SmokeGroup);
                 	}
                }
        	break;
	    case DIESEL_ENGINE:
	    case ELECTRIC_ENGINE:
   			// not needed for Diesel or Electric Engines
           	break;
    	}
	}
}
//---------------------------------------------------------------------------

void __fastcall TOp::LaborRevUpButtonMouseUp(TObject *Sender,
      TMouseButton Button, TShiftState Shift, int X, int Y)
{
if (Button == mbLeft) {
	//if (DEBUGGING) DB->Debug_Print("LaborRevUpButtonMouseUp");
	// Send CM_LABORREVUP message to ourselves which will perform this function
	(void) PostMessage(Handle, CM_LABORREVUP, false, NULL);     //false = button up
    }
}
//---------------------------------------------------------------------------

void __fastcall TOp::DriftRevDnButtonMouseDown(TObject *Sender,
      TMouseButton Button, TShiftState Shift, int X, int Y)
{
if (Button == mbLeft) {
	//if (DEBUGGING) DB->Debug_Print("DriftRevDnButtonMouseDown");
	// Send CM_DRIFTREVDN message to ourselves which will perform this function
	(void) PostMessage(Handle, CM_DRIFTREVDN, true, NULL);     // true = button down
    }
}
//---------------------------------------------------------------------------

void __fastcall TOp::CMDriftRevDn(TMessage &Message)
{
// Handle Message : Drift Rev Down
//
//if (DEBUGGING) DB->Debug_Print("CMDriftRevDn");

const bool Dn = Message.WParam;		// set button state from Word Parameter

if (Dn)	{	// button down
	// Drift / Rev Down On
    iRevLevel = RevLevelBar->Value;
	// Proto Chuff/Rev automatically turned off
	ProtoChuffSwitch->Checked = false;
	switch (EngineTypeComboBox->ItemIndex) {
		case STEAM_ENGINE:
	    	// for Steam Engines
			// start Drift
	    	(void)ptrTIUCommThread->Send_String_FIFO(ConsistNo, "r17", TIUNo, RemoteNo, 11, SHORT_WAIT, RECORD_OK, LashUpEngineListOp);
	        // If smoke is on and Auto Labored Smoke is on, lower smoke level to minumum
			if (SmokeSwitch->Checked) {
            	if (AutoLaboredSmokeLED->Checked) {
                    // Smoke to max
                    bLaboredSmoke = true;
                    SavedSmokeLevel = SmokeGroup->ItemIndex;
                    SmokeGroup->ItemIndex = 2;	// Minimum Smoke
                    SmokeGroup->OnClick(SmokeGroup);
                	}
            	}
	    	break;
	    case DIESEL_ENGINE:
	    case ELECTRIC_ENGINE:
			// for Diesel Engines
	    	if (iRevLevel != 1) {
				// Rev Down
			   	iRevLevel--;
                RevLevelBar->Value = iRevLevel;
			    char str[15];
			    (void)snprintf(str, sizeof str, "g%d", iRevLevel);
				(void)ptrTIUCommThread->Send_String_FIFO(ConsistNo, str, TIUNo, RemoteNo, 11, SHORT_WAIT, RECORD_OK, LashUpEngineListOp);
			    }
            // if exhaust is on and Auto Labored Smoke (Exhaust) is on, change exhaust level based on rev number
			if (SmokeSwitch->Checked) {
            	if (AutoLaboredSmokeLED->Checked) {
                	if (iRevLevel <= 3) { 	// 1-2-3
                    	SmokeGroup->ItemIndex = 2;	// minimum exhaust
	                    } else {
    	                if (iRevLevel > 3 && iRevLevel < 7) {	// 4-5-6
        	            	// exhaust to Medium
            	            SmokeGroup->ItemIndex = 1;	// medium exhaust
                            } else {	// 7-8-9
                            SmokeGroup->ItemIndex = 0;	// maximum exhaust
                            }
                        }
                    SmokeGroup->OnClick(SmokeGroup);
                	}
                }
	    	break;
		}
	}
if (!Dn) {	// button up
	// Drift Chuff Off
	switch (EngineTypeComboBox->ItemIndex) {
		case STEAM_ENGINE:
			// for Steam Engines
	    	// stop Drift
			(void)ptrTIUCommThread->Send_String_FIFO(ConsistNo, "r15", TIUNo, RemoteNo, 11, SHORT_WAIT, RECORD_OK, LashUpEngineListOp);
	        // If smoke is on and Auto Labored Smoke is on, return smoke level to saved value
			if (SmokeSwitch->Checked) {
	           	if (AutoLaboredSmokeLED->Checked) {
                   	bLaboredSmoke = false;
                    // set smoke to saved value
                    SmokeGroup->ItemIndex = SavedSmokeLevel;
                    SmokeGroup->OnClick(SmokeGroup);
                   	}
                }
			break;
	    case DIESEL_ENGINE:
	    case ELECTRIC_ENGINE:
    		// not needed for Diesel or Electric Engines
           	break;
	    }
	}
}
//---------------------------------------------------------------------------

void __fastcall TOp::DriftRevDnButtonMouseUp(TObject *Sender,
      TMouseButton Button, TShiftState Shift, int X, int Y)
{
if (Button == mbLeft) {
	//if (DEBUGGING) DB->Debug_Print("DriftRevDnButtonMouseUp");
	// Send CM_DRIFTREVUP message to ourselves which will perform this function
	(void) PostMessage(Handle, CM_DRIFTREVDN, false, NULL);     //false = button up
    }
}
//---------------------------------------------------------------------------

void __fastcall TOp::FeatureResetButtonClick(TObject *Sender)
{
// popup menu item is disabled for lashups and all engine operation
if (!MainForm->IsTIUConnected()) {
	MainForm->DebugMessagePopup("Warning", MainForm->ConnectMsg, "");
	return;
    }
String FeatureResetMsg;
if (ConsistNo >= ALLENGINE) return;	// can't reset All Engine or LashUps
//
if (ConsistNo <= ENGINEMAX) {	// DCS Engines 1-100
	// Feature Reset
	FeatureResetMsg = "Are you sure you want to Feature Reset engine " + String(ConsistNo) + " - " +
		ConsistName + "?";
    }
if (ConsistNo >= 101) {	// LashUp 101-120 (all DCS # 102)
    FeatureResetMsg = "Do you want to Feature Reset all of the engines in LashUp " + String(ConsistNo - 100) +  " - " +
	ConsistName + "?";
	}
int retv = Application->MessageBox(FeatureResetMsg.c_str(),	TEXT("Warning"), MB_YESNO | MB_ICONQUESTION);
if (retv != IDYES) {
    return;
    }
// Perform the Feature Reset
(void)ptrTIUCommThread->Send_String_FIFO(ConsistNo, "F0", TIUNo, RemoteNo, 11, VERY_LONG_WAIT, RECORD_OK, LashUpEngineListOp);
// Feature reset turns Smoke on so you must turn it off if you want it off.
}
//---------------------------------------------------------------------------

void __fastcall TOp::MaxSpeedSpinChange(TObject *Sender)
{
//     Now the boys all thought I'd lost my sense,
//     them telephone poles looked like a picket fence.
//     They said "slow down, I see spots,"
//     the lines on the road just looked like dots.
//     -- Hot Rod Lincoln - written by Charlie Ryan & W.S. Stevenson

 /*
SpeedSlider->SignalSettings->DigitalTo = MaxSpeedSpin->Value;
SpeedSlider->SignalSettings->ValueTo = MaxSpeedSpin->Value;
SpeedSlider->ScaleSettings->Steps = MaxSpeedSpin->Value / 10;
if (DEBUGGING) DB->Debug_Print("Engine Maximum Speed changed to " + String(MaxSpeedSpin->Value));
if (SpeedSlider->Value > SpeedSlider->SignalSettings->ValueTo) {
	SpeedSlider->Value = SpeedSlider->SignalSettings->ValueTo;	// sends a speed change command
    }
*/
}
//---------------------------------------------------------------------------

void __fastcall TOp::EngineTypeComboBoxClick(TObject *Sender)
{
// ItemIndex is -1 if nothing is selected
switch (EngineTypeComboBox->ItemIndex) {
  //------------------------------
  case STEAM_ENGINE:
    // Steam
    EngineTypeLED->LED->ColorOff = clAqua;
    EngineTypeLED->LED->ColorOn = clAqua;
    EngineTypeLED->Caption = "Steam";
    ChuffRateSwitch->Enabled = true;
    ChuffRateSwitch->LEDColorOff = clRed;
    ChuffRateSwitch->LEDColorOn = clLime;
    ChuffRateSwitch->Font->Color = clBlack;

    ProtoChuffSwitch->Enabled = true;
    ProtoChuffSwitch->LEDColorOff = clRed;
    ProtoChuffSwitch->LEDColorOn = clLime;
    ProtoChuffSwitch->Text = "Proto|Chuff";

    AutoBellLED->Caption = "Auto Bell";
    AutoWhistleLED->Caption = "Auto Whistle";

    SmokeGroup->Visible = true;
    SmokeGroup->Enabled = true;
    SmokeSwitch->Visible = true;
    SmokeSwitch->Enabled = true;
    AutoLaboredSmokeLED->Visible = true;
    SmokeGroup->Caption = "Smoke";
    SmokeSwitch->Text = "Smoke";
    AutoLaboredSmokeLED->Caption = "Auto Labored Smoke";

    LaborRevUpButton->TextOff = "LABOR|CHUFF";
    LaborRevUpButton->Hint = "Engage Labor Chuff Sound";
    LaborRevUpButton->Visible = true;
    LaborRevUpButton->Enabled = true;

    DriftRevDnButton->TextOff = "DRIFT|CHUFF";
    DriftRevDnButton->Hint = "Engage Drift Chuff Sound";
    DriftRevDnButton->Visible = true;
    DriftRevDnButton->Enabled = true;

    RevLevelBar->Visible = false;
    BellOnWithHornLED->Caption = "Ring Bell w/Whistle";
    break;
  //------------------------------
  case DIESEL_ENGINE:
	// Diesel
    EngineTypeLED->LED->ColorOff = clWhite;
    EngineTypeLED->LED->ColorOn = clWhite;
    EngineTypeLED->Caption = "Diesel";
    ChuffRateSwitch->Enabled = false;
    ChuffRateSwitch->LEDColorOff = clInactiveBorder;
    ChuffRateSwitch->LEDColorOn = clInactiveBorder;
	ChuffRateSwitch->Font->Color = clGray;

    ProtoChuffSwitch->Enabled = true;
    ProtoChuffSwitch->LEDColorOff = clRed;
    ProtoChuffSwitch->LEDColorOn = clLime;
    ProtoChuffSwitch->Text = "Proto|Rev";

	AutoBellLED->Caption = "Auto Bell";
    AutoWhistleLED->Caption = "Auto Horn";
    try {
	    WhistleButton->Glyph = HornButton->Glyph;
	    //WhistleButton->Repaint();
	    WhistleButton->Hint="Sounds the Horn when depressed";
        SFSButton->Glyph = Horn2Button->Glyph;
	    //SFSButton->Repaint();
        SRSButton->Glyph = Horn3Button->Glyph;
	    //SRSButton->Repaint();
        HornWhistlePanel->Repaint();
    	}
    catch(...) {
    	// ignore missing bit maps
    	}

    SmokeGroup->Visible = true;
    SmokeGroup->Enabled = true;
    SmokeSwitch->Visible = true;
    SmokeSwitch->Enabled = true;
    AutoLaboredSmokeLED->Visible = true;
    SmokeGroup->Caption = "Exhaust";
    SmokeSwitch->Text = "Exhaust";
    AutoLaboredSmokeLED->Caption = "Auto Rev Exhaust";

    LaborRevUpButton->TextOff = "REV UP";
    LaborRevUpButton->Hint = "Increase Revs one notch";
    LaborRevUpButton->Visible = true;
    LaborRevUpButton->Enabled = true;

    DriftRevDnButton->TextOff = "REV DN";
    DriftRevDnButton->Hint = "Decrease Revs one notch";
    DriftRevDnButton->Visible = true;
    DriftRevDnButton->Enabled = true;

    RevLevelBar->Visible = true;
    BellOnWithHornLED->Caption = "Ring Bell w/Horn";
    break;
  //------------------------------
  case ELECTRIC_ENGINE:
    EngineTypeLED->LED->ColorOff = clGreen;
    EngineTypeLED->LED->ColorOn = clGreen;
    EngineTypeLED->Caption = "Electric";
    ChuffRateSwitch->Enabled = false;
    ChuffRateSwitch->LEDColorOff = clInactiveBorder;
    ChuffRateSwitch->LEDColorOn = clInactiveBorder;
	ChuffRateSwitch->Font->Color = clGray;

    ProtoChuffSwitch->Enabled = false;

	AutoBellLED->Caption = "Auto Bell";
    AutoWhistleLED->Caption = "Auto Horn";
    try {
	    WhistleButton->Glyph = HornButton->Glyph;
	    //WhistleButton->Repaint();
	    WhistleButton->Hint="Sounds the Horn when depressed";
        SFSButton->Glyph = Horn2Button->Glyph;
	    //SFSButton->Repaint();
        SRSButton->Glyph = Horn3Button->Glyph;
	    //SRSButton->Repaint();
        HornWhistlePanel->Repaint();
        }
    catch(...) {
    	// ignore missing bit maps
	    }
    SmokeGroup->Visible = false;
    SmokeGroup->Enabled = false;
    SmokeSwitch->Visible = false;
    SmokeSwitch->Enabled = false;
    AutoLaboredSmokeLED->Visible = false;

    LaborRevUpButton->TextOff = "REV UP";
    LaborRevUpButton->Hint = "Rev Up";
    LaborRevUpButton->Visible = false;
    LaborRevUpButton->Enabled = false;

    DriftRevDnButton->TextOff = "REV DN";
    DriftRevDnButton->Hint = "Rev Down";
    DriftRevDnButton->Visible = false;
    DriftRevDnButton->Enabled = false;

	RevLevelBar->Visible = false;
    BellOnWithHornLED->Caption = "Ring Bell w/Horn";
    break;
	}
}
//---------------------------------------------------------------------------

void __fastcall TOp::EngineTypeLEDClick(TObject *Sender)
{
// toggle through engine types
EngineTypeComboBox->ItemIndex = (EngineTypeComboBox->ItemIndex + 1) % 3;
EngineTypeComboBoxClick(EngineTypeComboBox);	// process the change
}
//---------------------------------------------------------------------------

void __fastcall TOp::DitchComboBoxClick(TObject *Sender)
{
// Ditch Lights for Diesel Engines
// I don't understand what these three commands do and why three are needed
//
if (!DitchComboBox->Enabled) return;
DitchComboBox->SelStart = 0;	// remove highlight
// ItemIndex is -1 if nothing is selected
switch (EngineTypeComboBox->ItemIndex) {
    case DIESEL_ENGINE:
	switch (DitchComboBox->ItemIndex) {
		case 0:		// Off
			(void)ptrTIUCommThread->Send_String_FIFO(ConsistNo, "ab6", TIUNo, RemoteNo, 11, SHORT_WAIT, RECORD_OK, LashUpEngineListOp);
			(void)ptrTIUCommThread->Send_String_FIFO(ConsistNo, "ab8", TIUNo, RemoteNo, 11, SHORT_WAIT, RECORD_OK, LashUpEngineListOp);
			(void)ptrTIUCommThread->Send_String_FIFO(ConsistNo, "abA", TIUNo, RemoteNo, 11, SHORT_WAIT, RECORD_OK, LashUpEngineListOp);
	    	break;
	    case 1:		// Auto
			(void)ptrTIUCommThread->Send_String_FIFO(ConsistNo, "abB", TIUNo, RemoteNo, 11, SHORT_WAIT, RECORD_OK, LashUpEngineListOp);
			(void)ptrTIUCommThread->Send_String_FIFO(ConsistNo, "ab6", TIUNo, RemoteNo, 11, SHORT_WAIT, RECORD_OK, LashUpEngineListOp);
			(void)ptrTIUCommThread->Send_String_FIFO(ConsistNo, "ab8", TIUNo, RemoteNo, 11, SHORT_WAIT, RECORD_OK, LashUpEngineListOp);
	    	break;
	    case 2:		// On
			(void)ptrTIUCommThread->Send_String_FIFO(ConsistNo, "ab7", TIUNo, RemoteNo, 11, SHORT_WAIT, RECORD_OK, LashUpEngineListOp);
			(void)ptrTIUCommThread->Send_String_FIFO(ConsistNo, "ab8", TIUNo, RemoteNo, 11, SHORT_WAIT, RECORD_OK, LashUpEngineListOp);
			(void)ptrTIUCommThread->Send_String_FIFO(ConsistNo, "abA", TIUNo, RemoteNo, 11, SHORT_WAIT, RECORD_OK, LashUpEngineListOp);
	    	break;
	    case 3:		// Flashing
			(void)ptrTIUCommThread->Send_String_FIFO(ConsistNo, "ab9", TIUNo, RemoteNo, 11, SHORT_WAIT, RECORD_OK, LashUpEngineListOp);
			(void)ptrTIUCommThread->Send_String_FIFO(ConsistNo, "ab6", TIUNo, RemoteNo, 11, SHORT_WAIT, RECORD_OK, LashUpEngineListOp);
			(void)ptrTIUCommThread->Send_String_FIFO(ConsistNo, "abA", TIUNo, RemoteNo, 11, SHORT_WAIT, RECORD_OK, LashUpEngineListOp);
   		 	break;
	    }
	    break;
    case ELECTRIC_ENGINE:
    case STEAM_ENGINE:
    	// NOP
    	break;
	}	
}
//---------------------------------------------------------------------------

void __fastcall TOp::AutoPantSwitchStatusChanged(TObject *Sender)
{
// Send Pantograph Auto or off  - Codes from Tom N.
if (AutoPantSwitch->Checked) {
    // Auto
	(void)ptrTIUCommThread->Send_String_FIFO(ConsistNo, "ab6", TIUNo, RemoteNo, 11, SHORT_WAIT, RECORD_OK, LashUpEngineListOp);
	} else {
   	// Manual
	(void)ptrTIUCommThread->Send_String_FIFO(ConsistNo, "ab7", TIUNo, RemoteNo, 11, SHORT_WAIT, RECORD_OK, LashUpEngineListOp);
    }
// put down both pantographs
FrontPantSwitch->Checked = false;
RearPantSwitch->Checked = false;
}
//---------------------------------------------------------------------------

void __fastcall TOp::FrontPantSwitchStatusChanged(TObject *Sender)
{
// Send Front Pantograph up/down   - Codes from Tom N.
if (FrontPantSwitch->Checked) {
    // Up  FUP
	(void)ptrTIUCommThread->Send_String_FIFO(ConsistNo, "abB", TIUNo, RemoteNo, 11, SHORT_WAIT, RECORD_OK, LashUpEngineListOp);
	} else {
   	// Down  FDN
	(void)ptrTIUCommThread->Send_String_FIFO(ConsistNo, "abA", TIUNo, RemoteNo, 11, SHORT_WAIT, RECORD_OK, LashUpEngineListOp);
    }	
}
//---------------------------------------------------------------------------

void __fastcall TOp::RearPantSwitchStatusChanged(TObject *Sender)
{
// Send Rear Pantograph up/down   - Codes from Tom N.
if (RearPantSwitch->Checked) {
    // Up   RUP
	(void)ptrTIUCommThread->Send_String_FIFO(ConsistNo, "ab9", TIUNo, RemoteNo, 11, SHORT_WAIT, RECORD_OK, LashUpEngineListOp);
	} else {
   	// Down   RDN
	(void)ptrTIUCommThread->Send_String_FIFO(ConsistNo, "ab8", TIUNo, RemoteNo, 11, SHORT_WAIT, RECORD_OK, LashUpEngineListOp);
    }
}
//---------------------------------------------------------------------------

void __fastcall TOp::SetSpeedSliderValue(float fMPH)
{
SpeedSlider->Value = fMPH;	// change in ->Value will call SpeedSliderValueChange()
}
//---------------------------------------------------------------------------

void __fastcall TOp::SpeedSliderValueChange(TObject *Sender)
{
//
// https://stackoverflow.com/questions/31649315/c-builder-button-click-function-reentry
LOCKEM()
if (DEBUGGINGX(4)) DB->Debug_Print("SpeedSliderValueChange : Consist " + String(ConsistNo) + " - TIU " + String(TIUNo) + " : " + String(SpeedSlider->Value) + " Smph");
// Send speed command based on value of slider

int DelayValue; 	// time in milliseconds

//const TCursor OldCursor = Screen->Cursor;	// set Wait Cursor
//Screen->Cursor = crHourGlass;

#ifdef NOTNEEDED
float SpeedatEntry  = SpeedSlider->Value;	// save the current setting speed
#endif

const int iMPH = SpeedSlider->Value;

// reflect the change on to any open Quick Controls window
if (ptrQuick_Speed) {
    // The Speed Edit on the Quick Controls window uses OnValueChanged
	SpeedOnValueChanged = ptrQuick_Speed->OnValueChanged;
    ptrQuick_Speed->OnValueChanged = NULL;
	ptrQuick_Speed->Value = iMPH;
    ptrQuick_Speed->OnValueChanged = SpeedOnValueChanged;
    ptrQuick_EngNumber->Color = clLime;	// engine is started, set Engine Number field to green
    ptrQuick_RunState[ConsistNo] = clLime;
    }

__try {
	// Note: I cannot get both the bell and the coupler slack sounds to come out at the same time
	// If I turn off Auto Whistle, I get the coupler slack sound
    //
    DelayValue = 0;
    if (iMPH != 0) {
	    //
		// TODO : - set Smoke/Exhaust levels to data base values
    	//
    	if (MotionSoundRequest->Checked) {
			if (CouplerSlackSwitch->Checked) { 	// SCS Auto Coupler Slack
			    // send SCS before changing speed
				(void)ptrTIUCommThread->Send_String_FIFO(ConsistNo, "h1", TIUNo, RemoteNo, 11, SHORT_WAIT, RECORD_OK, LashUpEngineListOp);
		    	}
	    	// Headlight On and Interior Light off when starting to move if requested
		    if (SimulatedRule17LED->Checked) {
			    HDLTSwitch->Checked = true;					// this will send a headlight on command
        		if (LINSwitch) LINSwitch->Checked = false;	// this will send an interior light off command right away
		    	}
			if (Setup->AutoWhistle->Checked && AutoWhistleLED->Checked) {
				// send either forward (SFS) or reverse (SRS) horn/whistle
           	    if (!ProtoWhistleSwitch->Checked) {	// if Proto Whistle is on, disable this action
					if (DIRToggle->Checked) {
	                    // use either short or long Bell/Whistle Sequence
						if (Setup->ShortBellWhistleSequence->Checked) {
	    		    		// Send SFS (this is two shorts which is technicaly NOT "Release Brakes, Proceed")
							(void)ptrTIUCommThread->Send_String_FIFO(ConsistNo, "n43", TIUNo, RemoteNo, 11, SHORT_WAIT, RECORD_OK, LashUpEngineListOp);
                            } else {
                            // Send a real "Release Brakes, Proceed"
                            ptrDL_Thread->Send_Delayed_DL_FIFO(this, Handle, DC_HORN_WHISTLE_ON, ConsistNo, TIUNo, RemoteNo, DelayValue, 0);
                            DelayValue += 1000;	// horn/whistle on
                            ptrDL_Thread->Send_Delayed_DL_FIFO(this, Handle, DC_HORN_WHISTLE_OFF, ConsistNo, TIUNo, RemoteNo, DelayValue, 0);
                            DelayValue += 1500;	// horn/whistle off
                            ptrDL_Thread->Send_Delayed_DL_FIFO(this, Handle, DC_HORN_WHISTLE_ON, ConsistNo, TIUNo, RemoteNo, DelayValue, 0);
                            DelayValue += 1000;	// horn/whistle on
                            ptrDL_Thread->Send_Delayed_DL_FIFO(this, Handle, DC_HORN_WHISTLE_OFF, ConsistNo, TIUNo, RemoteNo, DelayValue, 0);
                            }
		   		    	} else {
	    	    	    // Send SRS
						(void)ptrTIUCommThread->Send_String_FIFO(ConsistNo, "n44", TIUNo, RemoteNo, 11, SHORT_WAIT, RECORD_OK, LashUpEngineListOp);
	        	    	}
                    }
	            }
            if (Setup->AutoBell->Checked && AutoBellLED->Checked) {
				// use either short or long Bell/Whistle Sequence
				if (Setup->ShortBellWhistleSequence->Checked) {
                	// short sequence - no bell
        	    	DelayValue += 1500;
	   	    	    } else {
                    // long sequence - bell if enabled
                    DelayValue += 1000;	// Schedule bell on for a few seconds later
                    ptrDL_Thread->Send_Delayed_DL_FIFO(this, Handle, DC_BELLON, ConsistNo, TIUNo, RemoteNo, DelayValue, 0);
                    DelayValue += 2000;
   	    		    }
                }
            }
        // TODO : Send speed delay with Auto Bell or Auto Whistle causes a problem from
        // speed zero. In order to get a resonable sequence, the speed command "s1" is delayed
        // until the bell/whistle sounds. These commands are put into the Dispatch List for
        // later transmission. But if the slider continues to be moved up, the "s2", "s3", etc
        // commands are transmitted before the "s1" command.
        if (DelayValue == 0) {
        	// if iMPH > 0 , this happens immediately
			Set_Speed(iMPH);
            } else {
            if (iMPH == 1 && LastSentSpeedValue == 0) {
                // set speed immediately
                Set_Speed(iMPH);
                } else {
                ptrDL_Thread->Send_Delayed_DL_FIFO(this, Handle, DC_MPH, ConsistNo, TIUNo, RemoteNo, DelayValue, iMPH);
                }
            }
	    //
       	if (MotionSoundRequest->Checked) {
	   		if (Setup->AutoBell->Checked && AutoBellLED->Checked) {
				// Auto Bell/Whistle
				if (Setup->ShortBellWhistleSequence->Checked) {		// use either short or long Bell/Whistle Sequence
                	// short sequence - no bell - Sequence ends with bell ringing and engine moving at speed
            		DelayValue += 200;
                    ptrDL_Thread->Send_Delayed_DL_FIFO(this, Handle, DC_BELLON, ConsistNo, TIUNo, RemoteNo, DelayValue, 0);
	            	} else {
                    // long sequence - bell if enabled - Sequence ends with bell silent and engine moving at speed
		            DelayValue += 9000;
                    ptrDL_Thread->Send_Delayed_DL_FIFO(this, Handle, DC_BELLOFF, ConsistNo, TIUNo, RemoteNo, DelayValue, 0);
        		    }
	    		}
			}
		MotionSoundRequest->Checked = false; 	// no sound on next train movement
    	}
	// if the speed is set to zero, request sounds on next move off zero, single toot and schedule turn off headlight in 6 seconds
	if (iMPH == 0) {
		if (Setup->AutoWhistle->Checked && AutoWhistleLED->Checked) {	// single toot - then stop
       	    if (!ProtoWhistleSwitch->Checked) {	// if Proto Whistle is on, disable this action
            	if (LastSentSpeedValue != 0) {
					(void)ptrTIUCommThread->Send_String_FIFO(ConsistNo, SINGLE_TOOT, TIUNo, RemoteNo, 11, SHORT_WAIT, RECORD_OK, LashUpEngineListOp);
                    }
    	        }
            }
   		Set_Speed(iMPH);	// if iMPH == 0, this happens immediately
		MotionSoundRequest->Checked = true;	// request sound on next train movement
        if (SimulatedRule17LED->Checked && (LastSentSpeedValue != 0)) {
        	if (LINSwitch) LINSwitch->Checked = true;	// interior light on right away
        	// schedule turn off headlight in 10 seconds (HDLTSwitch->Checked)
            ptrDL_Thread->Send_Delayed_DL_FIFO(this, Handle, DC_HDLT_OFF_IF_STOPPED, ConsistNo, TIUNo, RemoteNo, 6 * 1000L, 0);
        	}
		// TODO : - if it's a diesel engine, set iRevLevel to 0
        // TODO : - set Smoke/Exhaust level to Minimum  SMOKE_EXHAUST_MIN
	    }
    }
__finally {
	//Screen->Cursor = OldCursor;	// restore cursor
    }

LastSentSpeedValue = iMPH;	// save the last sent speed value
//
#ifdef NOTNEEDED
// Check for slider movement while setting speed
if (SpeedatEntry != SpeedSlider->Value) {
    if (DEBUGGING) DB->Debug_Print("SpeedSliderValueChange Speed Changed on Exit : " + String(SpeedSlider->Value) +
    	" from " + String(SpeedatEntry));
    SpeedSliderValueChange(SpeedSlider);	// call me again to process the changed speed value
	}
#endif
return;
}
//---------------------------------------------------------------------------
void __fastcall TOp::Set_Speed(int MPH)
{
// Set the speed immediately
// Scale the speed if requested in Setup
const int ScaledMPH = MPH / MainForm->GlobalMPHScaling;
// Convert value into chars
char str[10];
(void) snprintf(str, sizeof str, SPEED_SET, ScaledMPH);
(void)ptrTIUCommThread->Send_String_FIFO(ConsistNo, str, TIUNo, RemoteNo, 11, SHORT_WAIT, RECORD_OK, LashUpEngineListOp);
LastSetSpeed = MPH;
}
//---------------------------------------------------------------------------

static bool SpaceKey = false;

void __fastcall TOp::FormKeyDown(TObject *Sender, WORD &Key,
      TShiftState Shift)
{
// Make sure KeyPreview is true for this form
assert(KeyPreview);
// Pass function keys to the MainForm, handle rest of the keys here.
switch (Key) {
	case VK_SPACE:
	    WhistleButton->Checked = true;		// This does not start the horn, need CM_WHISTLE message
		// Send CM_WHISTLE message to ourselves which will perform this function
		if (SpaceKey == false) (void) PostMessage(Handle, CM_WHISTLE, true, NULL);		// true = button down
        Key = 0;	// handled
        SpaceKey = true;
		break;
    case VK_F1:
    case VK_F2:
    case VK_F3:
    case VK_F4:
    case VK_F5:
    case VK_F6:
    case VK_F7:
    case VK_F8:
    case VK_F9:
    case VK_F10:
    case VK_F11:
    case VK_F12:
		MainForm->FormKeyDown(Sender, Key, Shift);
        Key = 0;	// handled
		break;
	}
}
//---------------------------------------------------------------------------

void __fastcall TOp::FormKeyUp(TObject *Sender, WORD &Key,
      TShiftState Shift)
{
// Make sure KeyPreview is true for this form
assert(KeyPreview);
// Pass function keys to the MainForm, handle rest of the keys here.
switch (Key) {
	case VK_SPACE:
   	    WhistleButton->Checked = false;		// This does not end the horn, need CM_WHISTLE message
		// Send CM_WHISTLE message to ourselves which will perform this function
		(void) PostMessage(Handle, CM_WHISTLE, false, NULL);		// false = button up
        Key = 0;	// handled
        SpaceKey = false;
		break;
    case VK_F1:
    case VK_F2:
    case VK_F3:
    case VK_F4:
    case VK_F5:
    case VK_F6:
    case VK_F7:
    case VK_F8:
    case VK_F9:
    case VK_F10:
    case VK_F11:
    case VK_F12:
		MainForm->FormKeyUp(Sender, Key, Shift);
        Key = 0;	// handled
		break;
	}
}
//---------------------------------------------------------------------------

void __fastcall TOp::BellSecondsClick(TObject *Sender)
{
// Ring the bell for 10, 20 or 30 seconds (default 10 seconds)
const TMenuItem * MenuUsed = (TMenuItem *)Sender;
int dw = 10;
// setup delay time in seconds
if (MenuUsed->Name.Pos("10")) dw = 10;
if (MenuUsed->Name.Pos("20")) dw = 20;
if (MenuUsed->Name.Pos("30")) dw = 30;
BellSwitch->Checked = true;	// bell on
ptrDL_Thread->Send_Delayed_DL_FIFO(this, Handle, DC_BELLOFF, ConsistNo, TIUNo, RemoteNo, dw * 1000L, 0);
}
//---------------------------------------------------------------------------

void __fastcall TOp::StopButtonOpMouseDown(TObject *Sender,
      TMouseButton Button, TShiftState Shift, int X, int Y)
{
// speed 0
SetSpeedSliderValue(0.0);	// causes the 's0' command to be sent if the speed was not zero
// Even if the speed was zero, send the stop command again in case the engine is still moving
Set_Speed(0);
//
SpeedSlider->SetFocus();	// focus to speed slider
//MotionSoundRequest->Checked = true;	// request sound on next train movement
}
//---------------------------------------------------------------------------

void __fastcall TOp::StopButtonOpClick(TObject *Sender)
{
// speed 0
SetSpeedSliderValue(0.0);	// causes the 's0' command to be sent if the speed was not zero
// Even if the speed was zero, send the stop command again in case the engine is still moving
Set_Speed(0);
//
}
//---------------------------------------------------------------------------

void __fastcall TOp::EightShortClick(TObject *Sender)
{
for (int i = 0 ; i < 8 ; i++) {
    ptrDL_Thread->Send_Delayed_DL_FIFO(this, Handle, DC_PLAY_SOUND, ConsistNo, TIUNo, RemoteNo, i * MFACTOR * (LONGTOOT), 41);
	}
}
//---------------------------------------------------------------------------

void __fastcall TOp::TwoLongClick(TObject *Sender)
{
ptrDL_Thread->Send_Delayed_DL_FIFO(this, Handle, DC_HORN_WHISTLE_ON, ConsistNo, TIUNo, RemoteNo, 0, 0);
ptrDL_Thread->Send_Delayed_DL_FIFO(this, Handle, DC_HORN_WHISTLE_OFF, ConsistNo, TIUNo, RemoteNo, MFACTOR * (LONGTOOT), 0);
ptrDL_Thread->Send_Delayed_DL_FIFO(this, Handle, DC_HORN_WHISTLE_ON, ConsistNo, TIUNo, RemoteNo,  MFACTOR * (LONGTOOT + SHORTPAUSE), 0);
ptrDL_Thread->Send_Delayed_DL_FIFO(this, Handle, DC_HORN_WHISTLE_OFF, ConsistNo, TIUNo, RemoteNo, MFACTOR * (LONGTOOT + SHORTPAUSE + LONGTOOT), 0);
}
//---------------------------------------------------------------------------

void __fastcall TOp::GradeXingClick(TObject *Sender)
{
if (SXSButton->Enabled) {
	// if SXS command is available then,
	// Send CM_SXS message to ourselves which will perform this function
	(void) PostMessage(Handle, CM_SXS, NULL, NULL);
	} else {
    // otherwise manually create a sound optionally with Bell
    ptrDL_Thread->Send_Delayed_DL_FIFO(this, Handle, DC_HORN_WHISTLE_ON, ConsistNo, TIUNo, RemoteNo, 0, 0);
    ptrDL_Thread->Send_Delayed_DL_FIFO(this, Handle, DC_HORN_WHISTLE_OFF, ConsistNo, TIUNo, RemoteNo, MFACTOR * (LONGTOOT), 0);
    ptrDL_Thread->Send_Delayed_DL_FIFO(this, Handle, DC_HORN_WHISTLE_ON, ConsistNo, TIUNo, RemoteNo,  MFACTOR * (LONGTOOT + SHORTPAUSE), 0);
    ptrDL_Thread->Send_Delayed_DL_FIFO(this, Handle, DC_HORN_WHISTLE_OFF, ConsistNo, TIUNo, RemoteNo, MFACTOR * (LONGTOOT + SHORTPAUSE + LONGTOOT), 0);
    ptrDL_Thread->Send_Delayed_DL_FIFO(this, Handle, DC_HORN_WHISTLE_ON, ConsistNo, TIUNo, RemoteNo,  MFACTOR * (LONGTOOT + SHORTPAUSE + LONGTOOT + SHORTPAUSE), 0);
    ptrDL_Thread->Send_Delayed_DL_FIFO(this, Handle, DC_HORN_WHISTLE_OFF, ConsistNo, TIUNo, RemoteNo, MFACTOR * (LONGTOOT + SHORTPAUSE + LONGTOOT + SHORTPAUSE + SHORTTOOT), 0);
    ptrDL_Thread->Send_Delayed_DL_FIFO(this, Handle, DC_HORN_WHISTLE_ON, ConsistNo, TIUNo, RemoteNo,  MFACTOR * (LONGTOOT + SHORTPAUSE + LONGTOOT + SHORTPAUSE + SHORTTOOT + SHORTPAUSE), 0);
    ptrDL_Thread->Send_Delayed_DL_FIFO(this, Handle, DC_HORN_WHISTLE_OFF, ConsistNo, TIUNo, RemoteNo, MFACTOR * (LONGTOOT + SHORTPAUSE + LONGTOOT + SHORTPAUSE + SHORTTOOT + SHORTPAUSE + LONGTOOT), 0);
    }
}
//---------------------------------------------------------------------------

void __fastcall TOp::OneLongClick(TObject *Sender)
{
ptrDL_Thread->Send_Delayed_DL_FIFO(this, Handle, DC_HORN_WHISTLE_ON, ConsistNo, TIUNo, RemoteNo, 0, 0);
ptrDL_Thread->Send_Delayed_DL_FIFO(this, Handle, DC_HORN_WHISTLE_OFF, ConsistNo, TIUNo, RemoteNo, MFACTOR * (LONGTOOT), 0);
}
//---------------------------------------------------------------------------

void __fastcall TOp::TwoShortClick(TObject *Sender)
{
ptrDL_Thread->Send_Delayed_DL_FIFO(this, Handle, DC_HORN_WHISTLE_ON, ConsistNo, TIUNo, RemoteNo, 0, 0);
ptrDL_Thread->Send_Delayed_DL_FIFO(this, Handle, DC_HORN_WHISTLE_OFF, ConsistNo, TIUNo, RemoteNo, MFACTOR * (SHORTTOOT), 0);
ptrDL_Thread->Send_Delayed_DL_FIFO(this, Handle, DC_HORN_WHISTLE_ON, ConsistNo, TIUNo, RemoteNo,  MFACTOR * (SHORTTOOT + SHORTPAUSE), 0);
ptrDL_Thread->Send_Delayed_DL_FIFO(this, Handle, DC_HORN_WHISTLE_OFF, ConsistNo, TIUNo, RemoteNo, MFACTOR * (SHORTTOOT + SHORTPAUSE + SHORTTOOT), 0);
}
//---------------------------------------------------------------------------

void __fastcall TOp::ThreeShortClick(TObject *Sender)
{
ptrDL_Thread->Send_Delayed_DL_FIFO(this, Handle, DC_HORN_WHISTLE_ON, ConsistNo, TIUNo, RemoteNo, 0, 0);
ptrDL_Thread->Send_Delayed_DL_FIFO(this, Handle, DC_HORN_WHISTLE_OFF, ConsistNo, TIUNo, RemoteNo, MFACTOR * (SHORTTOOT), 0);
ptrDL_Thread->Send_Delayed_DL_FIFO(this, Handle, DC_HORN_WHISTLE_ON, ConsistNo, TIUNo, RemoteNo,  MFACTOR * (SHORTTOOT + SHORTPAUSE), 0);
ptrDL_Thread->Send_Delayed_DL_FIFO(this, Handle, DC_HORN_WHISTLE_OFF, ConsistNo, TIUNo, RemoteNo, MFACTOR * (SHORTTOOT + SHORTPAUSE + SHORTTOOT), 0);
ptrDL_Thread->Send_Delayed_DL_FIFO(this, Handle, DC_HORN_WHISTLE_ON, ConsistNo, TIUNo, RemoteNo,  MFACTOR * (SHORTTOOT + SHORTPAUSE + SHORTTOOT + SHORTPAUSE), 0);
ptrDL_Thread->Send_Delayed_DL_FIFO(this, Handle, DC_HORN_WHISTLE_OFF, ConsistNo, TIUNo, RemoteNo, MFACTOR * (SHORTTOOT + SHORTPAUSE + SHORTTOOT + SHORTPAUSE + SHORTTOOT), 0);
}
//---------------------------------------------------------------------------

void __fastcall TOp::OneLongTwoShortClick(TObject *Sender)
{
ptrDL_Thread->Send_Delayed_DL_FIFO(this, Handle, DC_HORN_WHISTLE_ON, ConsistNo, TIUNo, RemoteNo, 0, 0);
ptrDL_Thread->Send_Delayed_DL_FIFO(this, Handle, DC_HORN_WHISTLE_OFF, ConsistNo, TIUNo, RemoteNo, MFACTOR * (LONGTOOT), 0);
ptrDL_Thread->Send_Delayed_DL_FIFO(this, Handle, DC_HORN_WHISTLE_ON, ConsistNo, TIUNo, RemoteNo,  MFACTOR * (LONGTOOT + SHORTPAUSE), 0);
ptrDL_Thread->Send_Delayed_DL_FIFO(this, Handle, DC_HORN_WHISTLE_OFF, ConsistNo, TIUNo, RemoteNo, MFACTOR * (LONGTOOT + SHORTPAUSE + SHORTTOOT), 0);
ptrDL_Thread->Send_Delayed_DL_FIFO(this, Handle, DC_HORN_WHISTLE_ON, ConsistNo, TIUNo, RemoteNo,  MFACTOR * (LONGTOOT + SHORTPAUSE + SHORTTOOT + SHORTPAUSE), 0);
ptrDL_Thread->Send_Delayed_DL_FIFO(this, Handle, DC_HORN_WHISTLE_OFF, ConsistNo, TIUNo, RemoteNo, MFACTOR * (LONGTOOT + SHORTPAUSE + SHORTTOOT + SHORTPAUSE + SHORTTOOT), 0);
}
//---------------------------------------------------------------------------

void __fastcall TOp::Speed05Click(TObject *Sender)
{
SpeedSlider->Value = 5;
}
//---------------------------------------------------------------------------

void __fastcall TOp::Speed10Click(TObject *Sender)
{
SpeedSlider->Value = 10;
}
//---------------------------------------------------------------------------

void __fastcall TOp::Speed20Click(TObject *Sender)
{
SpeedSlider->Value = 20;
}
//---------------------------------------------------------------------------

void __fastcall TOp::Speed25Click(TObject *Sender)
{
SpeedSlider->Value = 25;
}
//---------------------------------------------------------------------------

void __fastcall TOp::Speed30Click(TObject *Sender)
{
SpeedSlider->Value = 30;
}
//---------------------------------------------------------------------------

void __fastcall TOp::Stopin10SecondsClick(TObject *Sender)
{
// Stop in 10 seconds
if (SpeedSlider->Value == 0 ) return;	// already stopped
ptrDL_Thread->Send_Delayed_DL_FIFO(this, Handle, DC_SETSPEED, ConsistNo, TIUNo, RemoteNo, 10 * 1000L, 0);
}
//---------------------------------------------------------------------------

void __fastcall TOp::Stopin20SecondsClick(TObject *Sender)
{
// Stop in 20 seconds
if (SpeedSlider->Value == 0 ) return;	// already stopped
ptrDL_Thread->Send_Delayed_DL_FIFO(this, Handle, DC_SETSPEED, ConsistNo, TIUNo, RemoteNo, 20 * 1000L, 0);
}
//---------------------------------------------------------------------------

void __fastcall TOp::Stopin30SecondsClick(TObject *Sender)
{
// Stop in 30 seconds
if (SpeedSlider->Value == 0 ) return;	// already stopped
ptrDL_Thread->Send_Delayed_DL_FIFO(this, Handle, DC_SETSPEED, ConsistNo, TIUNo, RemoteNo, 30 * 1000L, 0);
}
//---------------------------------------------------------------------------

void __fastcall TOp::Decelerateto0SmphClick(TObject *Sender)
{
int iMPH = SpeedSlider->Value;
if (iMPH == 0) return;	// Speed is already zero
int endingspeed = 0;
int speedchange = -DecSpin->Value;
int secondsfromnow = 1;
for (int spd = iMPH; spd >= endingspeed ; spd += speedchange) {
    if (spd < endingspeed) spd = endingspeed;	// can't go below ending speed
    if (DEBUGGING) DB->Debug_Print("Decelerating to " + String(spd) + " Smph");
    ptrDL_Thread->Send_Delayed_DL_FIFO(this, Handle, DC_SETSPEED, ConsistNo, TIUNo, RemoteNo, secondsfromnow * 1000, spd);
	secondsfromnow++;
    continue;
	}
if (DEBUGGING) DB->Debug_Print("Decelerating to " + String(endingspeed) + " Smph");
ptrDL_Thread->Send_Delayed_DL_FIFO(this, Handle, DC_SETSPEED, ConsistNo, TIUNo, RemoteNo, secondsfromnow * 1000, endingspeed);
}
//---------------------------------------------------------------------------

void __fastcall TOp::ShowEngineCommands1Click(TObject *Sender)
{
assert(Setup != NULL);		// setup must be created
// EngineCommands Form should be manually created as it is rarely needed
// if the form is not created, then create it, otherwise show the previously created form
//
const TCursor OldCursor = Screen->Cursor; 	// save cursor
Screen->Cursor = crHourGlass;
//
if (EC == NULL) {	// Must be deleted in the destructor
	EC = new TEngineCommands(this, RemoteNo, ConsistNo, TIUNo, ConsistName, LashUpEngineListOp);     // Pass "this" as the owner
	assert(EC != NULL);			// make sure that construction worked
    EC->ParentHandle = Handle;	// for return messages
	} else {   					// it's already created
	EC->Show();
	}
Screen->Cursor = OldCursor;
}
//---------------------------------------------------------------------------

void __fastcall TOp::TrainWreckClick(TObject *Sender)
{
// Send Train Wreck
(void)ptrTIUCommThread->Send_String_FIFO(ConsistNo, "u3", TIUNo, RemoteNo, 11, SHORT_WAIT, RECORD_OK, LashUpEngineListOp);
}
//---------------------------------------------------------------------------

void __fastcall TOp::SoundsSwitchStatusChanged(TObject *Sender)
{
LOCKEM();
//static saved_volume = 0;
//SoundsSwitch->Enabled = false;
//
// Note: the sound on/off button on the App, turns off the sound by
// setting V1 and v2 to zero. In RTC, I set V0 to zero.
//
// Turn Sounds on or off
if (SoundsSwitch->Checked) {	// Sounds Switch
    // Sounds ON
    // set master volume to saved value
	// v0 = Master Volume
    char str[15];
    snprintf(str, sizeof str, "v0%03d", SavedMasterVolumeValue);
    (void)ptrTIUCommThread->Send_String_FIFO(ConsistNo, str, TIUNo, RemoteNo, 11, SHORT_WAIT, RECORD_OK, LashUpEngineListOp);
	LastSentMasterVolumeValue = SavedMasterVolumeValue;
    } else {
    // Sounds OFF - mute
    // read up current volume from the head engine
    unsigned short ival;
    bool bretv = MainForm->FetchWord(0x0E, &ival, HeadEngineNo, TIUNo);
    if (!bretv)
        SavedMasterVolumeValue = 100;           // default value
        else
        SavedMasterVolumeValue = ival / 327;	// divide by 327
    // set volume to Zero
   	// Sounds OFF - mute
	(void)ptrTIUCommThread->Send_String_FIFO(ConsistNo, "v0000", TIUNo, RemoteNo, 11, SHORT_WAIT, RECORD_OK, LashUpEngineListOp);
    LastSentMasterVolumeValue = 0;
    }
// reflect the change on to any open Volume Controls window
// EngineSoundOnSwitch : TAbRockerSwitch
if (ptrVol_EngineSoundOnSwitch) {
	// The EngineSoundOnSwitch on the Sound Controls window uses OnStatusChanged......
    EventOnStatusChanged = ptrVol_EngineSoundOnSwitch->OnStatusChanged;
    ptrVol_EngineSoundOnSwitch->OnStatusChanged = NULL;
    ptrVol_EngineSoundOnSwitch->Checked = SoundsSwitch->Checked;
    ptrVol_EngineSoundOnSwitch->OnStatusChanged = EventOnStatusChanged;
	}
    
// reflect the change on to any open Volume Controls window
// MasterVolume : TAbVSlider
if (ptrVol_MasterVolume) {
	// The MasterVolume slider on the Sound Controls window uses OnValueChange......
    EventOnValueChange = ptrVol_MasterVolume->OnValueChange;
    ptrVol_MasterVolume->OnValueChange = NULL;
    ptrVol_MasterVolume->Value = LastSentMasterVolumeValue;
    ptrVol_MasterVolume->OnValueChange = EventOnValueChange;
	}

// reflect the change on to any open Quick Controls window
// ptrSound : TAbHSlider
if (ptrQuick_Sound) {
	// The VolumeSlider on the Quick Controls window uses OnValueChange......
	EventOnValueChange = ptrQuick_Sound->OnValueChange;
    ptrQuick_Sound->OnValueChange = NULL;
	ptrQuick_Sound->Value = LastSentMasterVolumeValue;
    ptrQuick_Sound->OnValueChange = EventOnValueChange;
    }

//SoundsSwitch->Enabled = true;
}
//---------------------------------------------------------------------------

void __fastcall TOp::BeaconLightSwitchStatusChanged(TObject *Sender)
{
// Turn Beacon Light on or off
if (!BeaconLightSwitch->Enabled) return;
// Send LBE on or off
if (BeaconLightSwitch->Checked) {
	// ON
	(void)ptrTIUCommThread->Send_String_FIFO(ConsistNo, "abD", TIUNo, RemoteNo, 11, SHORT_WAIT, RECORD_OK, LashUpEngineListOp);
	} else {
	// OFF
	(void)ptrTIUCommThread->Send_String_FIFO(ConsistNo, "abC", TIUNo, RemoteNo, 11, SHORT_WAIT, RECORD_OK, LashUpEngineListOp);
	}
}
//---------------------------------------------------------------------------

void __fastcall TOp::MarkerLightSwitchStatusChanged(TObject *Sender)
{
// Turn Marker Lights on or off
if (!MarkerLightSwitch->Enabled) return;
// Send LMK on or off
if (MarkerLightSwitch->Checked) {
	// On
	(void)ptrTIUCommThread->Send_String_FIFO(ConsistNo, "ab1C", TIUNo, RemoteNo, 11, SHORT_WAIT, RECORD_OK, LashUpEngineListOp);
	} else {
	// Off
	(void)ptrTIUCommThread->Send_String_FIFO(ConsistNo, "ab1D", TIUNo, RemoteNo, 11, SHORT_WAIT, RECORD_OK, LashUpEngineListOp);
    }
}
//---------------------------------------------------------------------------

void __fastcall TOp::ab3ab2SwitchStatusChanged(TObject *Sender)
{
// "Interior" switch
if (!ab3ab2Switch->Enabled) return;
// ab3-ab2 Command
if (ab3ab2Switch->Checked) {
	// On
	(void)ptrTIUCommThread->Send_String_FIFO(ConsistNo, "ab3", TIUNo, RemoteNo, 11, SHORT_WAIT, RECORD_OK, LashUpEngineListOp);
	} else {
	// Off
	(void)ptrTIUCommThread->Send_String_FIFO(ConsistNo, "ab2", TIUNo, RemoteNo, 11, SHORT_WAIT, RECORD_OK, LashUpEngineListOp);
    }
}
//---------------------------------------------------------------------------

void __fastcall TOp::ab5ab4SwitchStatusChanged(TObject *Sender)
{
// "Mars" Switch
if (!ab5ab4Switch->Enabled) return;
// ab5-ab4 Command
if (ab5ab4Switch->Checked) {
	// On
	(void)ptrTIUCommThread->Send_String_FIFO(ConsistNo, "ab5", TIUNo, RemoteNo, 11, SHORT_WAIT, RECORD_OK, LashUpEngineListOp);
	} else {
	// Off
	(void)ptrTIUCommThread->Send_String_FIFO(ConsistNo, "ab4", TIUNo, RemoteNo, 11, SHORT_WAIT, RECORD_OK, LashUpEngineListOp);
    }
}
//---------------------------------------------------------------------------

void __fastcall TOp::ab7ab6SwitchStatusChanged(TObject *Sender)
{
// "Ditch" Switch
if (!ab7ab6Switch->Enabled) return;
// ab7-ab6 Command
if (ab7ab6Switch->Checked) {
	// On (Under certain settings, both Mars and Ditch need to be sent)
    if (ab7ab6Switch->Tag == SWITCH_RUNNING_DITCH_MARS || ab7ab6Switch->Tag == SWITCH_NB_DITCH_MARS)
    	(void)ptrTIUCommThread->Send_String_FIFO(ConsistNo, "ab5", TIUNo, RemoteNo, 11, SHORT_WAIT, RECORD_OK, LashUpEngineListOp);
	(void)ptrTIUCommThread->Send_String_FIFO(ConsistNo, "ab7", TIUNo, RemoteNo, 11, SHORT_WAIT, RECORD_OK, LashUpEngineListOp);
	} else {
	// Off
	if (ab7ab6Switch->Tag == SWITCH_RUNNING_DITCH_MARS || ab7ab6Switch->Tag == SWITCH_NB_DITCH_MARS)
    	(void)ptrTIUCommThread->Send_String_FIFO(ConsistNo, "ab4", TIUNo, RemoteNo, 11, SHORT_WAIT, RECORD_OK, LashUpEngineListOp);
	(void)ptrTIUCommThread->Send_String_FIFO(ConsistNo, "ab6", TIUNo, RemoteNo, 11, SHORT_WAIT, RECORD_OK, LashUpEngineListOp);
    }
}
//---------------------------------------------------------------------------

void __fastcall TOp::SoundControlsButtonClick(TObject *Sender)
{
if (ConsistNo <= ALLENGINE) { // Engine
	// Send Open Sound Control window to the MainForm which will open the Volume Control Form
	(void) MainForm->Perform(CM_SOUNDCONTROL, TIUNo, ConsistNo);
    	// TIUNo is 1  to 5, ConsistNo is 0 to 100
    }
if (ConsistNo >= 101) { // LashUp
	// Send CM_LASHUPSOUNDCTRLS message to MainForm which will open the Volume Control Form
	// Pointer to LashUpEngineListOp may go out of scope if PostMessage is used
	(void) MainForm->Perform(CM_LASHUPSOUNDCTRLS, (unsigned int) LashUpEngineListOp, (TIUNo << 8) | ConsistNo);
		// TIUNo is 1  to 5, ConsistNo is 101 to 120
    }
// pointers to Volume Control Window that was just openeded
if (ptrVolume[ConsistNo]) {
    ptrVol_EngineSoundOnSwitch = ptrVolume[ConsistNo]->EngineSoundOnSwitch;
    ptrVol_MasterVolume = ptrVolume[ConsistNo]->MasterVolume;
	} else {
    ptrVol_EngineSoundOnSwitch = NULL;
    ptrVol_MasterVolume = NULL;
	}
}
//---------------------------------------------------------------------------

void __fastcall TOp::AutoLaboredSmokeLEDStatusChanged(TObject *Sender)
{
// this StatusChanged event handler needed for assert() call in
// SetSwitch() called in MainForm::ConsistStartUp
//
bLaboredSmoke = false;	// turn off if it's on - but I don't think this can ever happen
if (AutoLaboredSmokeLED->Checked) {
	// do nothing
    } else {
    // set smoke/exhaust initial setting depending on value of iRevLevel
	switch (EngineTypeComboBox->ItemIndex) {
	    case DIESEL_ENGINE:
	        // if exhaust is on, change exhaust level based on rev number
			if (SmokeSwitch->Checked) {
	           	if (AutoLaboredSmokeLED->Checked) {
	               	if (iRevLevel <= 3) {
	                   	SmokeGroup->ItemIndex = 2;	// minimum exhaust
		                } else {
	                    if (iRevLevel > 3 && iRevLevel < 7) {
	       	            	// exhaust to Medium
	           	            SmokeGroup->ItemIndex = 1;	// medium exhaust
	                        } else {
	                        SmokeGroup->ItemIndex = 0;	// maximum exhaust
	                        }
	                    }
	                SmokeGroup->OnClick(SmokeGroup);
	               	}
	            }
	    	break;
	    case STEAM_ENGINE:
        case ELECTRIC_ENGINE:
	    	// For Steam engines, we don't have an initial setting
		    break;
    	}
    }
}
//---------------------------------------------------------------------------

void __fastcall TOp::CopyrightClick(TObject *Sender)
{
if (DEBUGGING) {
	// Open Special Commands Window
	ShowEngineCommands1Click(ShowEngineCommands1);
	ShowEngineCommands1->Checked = true;
    } else {
    // Copy the Form to the clipboard.
    // In BCB4.0, there is no SetSize method associated with TBitmap
    // so we have to use Width and Height separately.
    //
    // see:
    // https://stackoverflow.com/questions/3190777/how-do-i-copy-a-form-as-an-image-to-the-clipboard
    //
    Graphics::TBitmap * bm;
    bm = new Graphics::TBitmap;
    try
        {
        #if __BORLANDC__ >= 0x720
        // Defined for CB10.1 C++ programs that use the VCL.
        bm->SetSize(ClientWidth, ClientHeight);
        #else
        bm->Width = ClientWidth;
        bm->Height = ClientHeight;
        #endif
        BitBlt(bm->Canvas->Handle, 0, 0, ClientWidth, ClientHeight, Canvas->Handle, 0, 0, SRCCOPY);
        Clipboard()->Assign(bm);
        }
    __finally
        {
        delete bm;
        }
	}
}
//---------------------------------------------------------------------------

void __fastcall TOp::HDLTSwitchDblClick(TObject *Sender)
{
ptrDL_Thread->Send_Delayed_DL_FIFO(this, Handle, DC_MARS, ConsistNo, TIUNo, RemoteNo, 1 * 1000, 0);
}
//---------------------------------------------------------------------------

void __fastcall TOp::SendOneCommand(unsigned char * Cmd)
{
// Called from Synchronize() in threads created by calls to Delayed_Command.cpp
// Always called by :  CallingOpScreen->SendOneCommand("Xnn");
//
// If this is a LashUp, then LashUpEngineListOp should be set correctly and this should work
(void)ptrTIUCommThread->Send_String_FIFO(ConsistNo, Cmd, TIUNo, RemoteNo, 11, SHORT_WAIT, RECORD_OK, LashUpEngineListOp);
}
//---------------------------------------------------------------------------

void __fastcall TOp::BeaconLightSwitchDblClick(TObject *Sender)
{
ptrDL_Thread->Send_Delayed_DL_FIFO(this, Handle, DC_BEACON, ConsistNo, TIUNo, RemoteNo, 1 * 1000, 0);
}
//---------------------------------------------------------------------------

void __fastcall TOp::AllLightsOff1Click(TObject *Sender)
{
//
HDLTSwitch->Checked = false;
if (MarkerLightSwitch->Enabled) MarkerLightSwitch->Checked = false;
if (BeaconLightSwitch->Enabled) BeaconLightSwitch->Checked = false;
if (ab3ab2Switch->Enabled) ab3ab2Switch->Checked = false;
if (ab5ab4Switch->Enabled) ab5ab4Switch->Checked = false;
if (ab7ab6Switch->Enabled) ab7ab6Switch->Checked = false;
}
//---------------------------------------------------------------------------

void __fastcall TOp::AllLightsOn1Click(TObject *Sender)
{
//
HDLTSwitch->Checked = true;
if (MarkerLightSwitch->Enabled) MarkerLightSwitch->Checked = true;
if (BeaconLightSwitch->Enabled) BeaconLightSwitch->Checked = true;
if (ab3ab2Switch->Enabled) ab3ab2Switch->Checked = true;
if (ab5ab4Switch->Enabled) ab5ab4Switch->Checked = true;
if (ab7ab6Switch->Enabled) ab7ab6Switch->Checked = true;
}
//---------------------------------------------------------------------------

void __fastcall TOp::WhistleSliderValueChange(TObject *Sender)
{
// ab26 - ab27 - ab28 - ab29 Commands
LOCKEM();
assert(WhistleSlider->Value < 4);	// 0-3 valid
const int iW = WhistleSlider->Value + 0x26;
// Convert value into chars
char str[10];
(void) snprintf(str, sizeof str, "ab%X", iW);
(void)ptrTIUCommThread->Send_String_FIFO(ConsistNo, str, TIUNo, RemoteNo, 11, SHORT_WAIT, RECORD_OK, LashUpEngineListOp);
}
//---------------------------------------------------------------------------

void __fastcall TOp::ProtoWhistleSwitchStatusChanged(TObject *Sender)
{
if (!ProtoWhistleSwitch->Enabled) return;
LOCKEM();
// Proto Whistle  SPW
if (ProtoWhistleSwitch->Checked) {	// turn SPW on
    (void)ptrTIUCommThread->Send_String_FIFO(ConsistNo, "ab20", TIUNo, RemoteNo, 11, SHORT_WAIT, RECORD_OK, LashUpEngineListOp);
	// There is apparently a need to delay after sending this command - I see this delay when I press the SPW button on the Remote
	// If I don't do this, I get Engine Not on Track errors
	MainForm->Delay(2000);
   	WhistleSlider->Visible = true;
    WhistleSlider->Value = 0;
    HornWhistlePanel->Visible = false;
    } else {						// turn SPW off
    (void)ptrTIUCommThread->Send_String_FIFO(ConsistNo, "ab21", TIUNo, RemoteNo, 11, SHORT_WAIT, RECORD_OK, LashUpEngineListOp);
	// There is apparently a need to delay after sending this command - I see this delay when I press the SPW button on the Remote
	// If I don't do this, I get Engine Not on Track errors
	MainForm->Delay(2000);
   	WhistleSlider->Visible = false;
    HornWhistlePanel->Visible = true;
    }
}
//---------------------------------------------------------------------------

void __fastcall TOp::SmokeWhistleSwitchStatusChanged(TObject *Sender)
{
if (!SmokeWhistleSwitch->Enabled) return;
// Smoke Whistle  FSW
if (SmokeWhistleSwitch->Checked) {
    (void)ptrTIUCommThread->Send_String_FIFO(ConsistNo, "ab22", TIUNo, RemoteNo, 11, SHORT_WAIT, RECORD_OK, LashUpEngineListOp);
    } else {
    (void)ptrTIUCommThread->Send_String_FIFO(ConsistNo, "ab23", TIUNo, RemoteNo, 11, SHORT_WAIT, RECORD_OK, LashUpEngineListOp);
    }
}
//---------------------------------------------------------------------------

void __fastcall TOp::SwingingBellSwitchStatusChanged(TObject *Sender)
{
if (!SwingingBellSwitch->Enabled) return;
// Swinging Bell  FSB
if (SwingingBellSwitch->Checked) {
    (void)ptrTIUCommThread->Send_String_FIFO(ConsistNo, "ab24", TIUNo, RemoteNo, 11, SHORT_WAIT, RECORD_OK, LashUpEngineListOp);
    } else {
    (void)ptrTIUCommThread->Send_String_FIFO(ConsistNo, "ab25", TIUNo, RemoteNo, 11, SHORT_WAIT, RECORD_OK, LashUpEngineListOp);
    }
}
//---------------------------------------------------------------------------
// Workaround for SetLayeredWindowAttributes() to make transparent windows
extern lpfnSetLayeredWindowAttributes m_pSetLayeredWindowAttributes;
//---------------------------------------------------------------------------

void __fastcall TOp::FormActivate(TObject *Sender)
{
#if __BORLANDC__ >= 0x720      // CB10.1 only
AlphaBlend = false; // Remove Transparency
#else
// Remove WS_EX_LAYERED from this window styles
SetWindowLong(Handle, GWL_EXSTYLE, GetWindowLong(Handle, GWL_EXSTYLE) & ~WS_EX_LAYERED);
// Ask the window and its children to repaint
//Repaint();
#endif
}
//---------------------------------------------------------------------------

void __fastcall TOp::FormDeactivate(TObject *Sender)
{
if (Setup->TransparentSpin->Value == Setup->TransparentSpin->MaxValue) return;
if (LocalTransparencyOverride) return;
#if __BORLANDC__ >= 0x720      // CB10.1 only
AlphaBlendValue = (255 * Setup->TransparentSpin->Value) / 100;
AlphaBlend = true;  // Add Transparency
#else
// Set WS_EX_LAYERED on this window
SetWindowLong(Handle, GWL_EXSTYLE, GetWindowLong(Handle, GWL_EXSTYLE) | WS_EX_LAYERED);
//SetLayeredWindowAttributes
m_pSetLayeredWindowAttributes(Handle, NULL, (255 * Setup->TransparentSpin->Value) / 100, LWA_ALPHA);
#endif
}
//---------------------------------------------------------------------------

void __fastcall TOp::Operation_Controls_LabelClick(TObject *Sender)
{
LocalTransparencyOverride = !LocalTransparencyOverride;	// Toggle transparency locally
}
//---------------------------------------------------------------------------

void __fastcall TOp::MaxSpeedSpinValueChanged(TObject *Sender)
{
SpeedSlider->SignalSettings->DigitalTo = MaxSpeedSpin->Value;
SpeedSlider->SignalSettings->ValueTo = MaxSpeedSpin->Value;
SpeedSlider->ScaleSettings->Steps = MaxSpeedSpin->Value / 10;
if (DEBUGGING) DB->Debug_Print("Engine Maximum Speed changed to " + String(MaxSpeedSpin->Value));
if (SpeedSlider->Value > SpeedSlider->SignalSettings->ValueTo) {
	SpeedSlider->Value = SpeedSlider->SignalSettings->ValueTo;	// sends a speed change command
    }
}
//---------------------------------------------------------------------------

void __fastcall TOp::DelayTest1Click(TObject *Sender)
{
//
// Debug use Only - Deleted
//

}
//---------------------------------------------------------------------------

void __fastcall TOp::EngStatClick(TObject *Sender)
{
// display the Engine Status window for the engine given by HeadEngineNo
// (menu item is disabled for All Engine Operation)
// Manually create form and show it
// if the form is not already created, then create it
// ------------ With programmed create using Global Pointer so it must be deleted on exit
if (ptrEngineStatus[HeadEngineNo] == NULL) {	// Engine 0-99 (0 is factory reset, 1-99)
	ptrEngineStatus[HeadEngineNo] = new TEngineStatus(this, Handle, RemoteNo, HeadEngineNo, TIUNo, HeadEngineName);   // create the Form and show it
								// Pass "this" as the owner
    							// 1-99 Engine Number
	assert(ptrEngineStatus[HeadEngineNo] != NULL);	// make sure that construction worked
	} else {    // it's already created
	ptrEngineStatus[HeadEngineNo]->Show();
	}
}
//---------------------------------------------------------------------------

void __fastcall TOp::EngInfoClick(TObject *Sender)
{
// display the Engine Information window for the engine given by HeadEngineNo
// (menu item is disabled for All Engine Operation)
// Manually create form and show it
// if the form is not already created, then create it
// ------------ With programmed create using Global Pointer so it must be deleted on exit
if (ptrEngineInfo[HeadEngineNo] == NULL) {	// Engine 0-99 (0 is factory reset, 1-99)
	ptrEngineInfo[HeadEngineNo] = new TEngineInfo(this, Handle, RemoteNo, HeadEngineNo, TIUNo, HeadEngineName);   // create the Form and show it
								// Pass "this" as the owner
    							// 1-99 Engine Number
	assert(ptrEngineInfo[HeadEngineNo] != NULL);	// make sure that construction worked
	} else {    // it's already created
	ptrEngineInfo[HeadEngineNo]->Show();
	}
}
//---------------------------------------------------------------------------

void __fastcall TOp::TrkSignalClick(TObject *Sender)
{
// display the Track Signal window for the engine given by HeadEngineNo
// (menu item is disabled for All Engine Operation)
// Manually create form and show it
// if the form is not already created, then create it
// ------------ With programmed create using Local Pointer so it must be deleted on exit
if (MyTrackSignal == NULL) {	// Engine 0-99 (0 is factory reset, 1-99)
	MyTrackSignal = new TTrackSignal(this, Handle, RemoteNo, HeadEngineNo, TIUNo, HeadEngineName);   // create the Form and show it
								// Pass "this" as the owner
    							// 1-99 Engine Number
	assert(MyTrackSignal != NULL);	// make sure that construction worked
	} else {    // it's already created
	MyTrackSignal->Show();
	}
}
//---------------------------------------------------------------------------

void __fastcall TOp::SetEnginePS2Click(TObject *Sender)
{
// (menu item is disabled for All Engine Operation)
SetEnginePS2->Checked = true;
SetEnginePS3->Checked = false;
MainForm->EngineData->Cells[ENG_PS][HeadEngineNo] = PS2;
}
//---------------------------------------------------------------------------

void __fastcall TOp::SetEnginePS3Click(TObject *Sender)
{
// (menu item is disabled for All Engine Operation)
SetEnginePS3->Checked = true;
SetEnginePS2->Checked = false;
MainForm->EngineData->Cells[ENG_PS][HeadEngineNo] = PS3;
}
//---------------------------------------------------------------------------

void __fastcall TOp::OpPopupPopup(TObject *Sender)
{
// Some menu items disabled for All Engine Operation
// In Lashup mode, some menu items operate on the the lead engine
if (SetEnginePS2->Enabled && SetEnginePS3->Enabled) {
	SetEnginePS2->Checked = (MainForm->EngineData->Cells[ENG_PS][HeadEngineNo] == PS2);
	SetEnginePS3->Checked = (MainForm->EngineData->Cells[ENG_PS][HeadEngineNo] == PS3);
    }
}
//---------------------------------------------------------------------------

void __fastcall TOp::SimulatedRule17LEDStatusChanged(TObject *Sender)
{
// Dummy needed for SetLED() to work
}
//---------------------------------------------------------------------------

void __fastcall TOp::BellOnWithHornLEDStatusChanged(TObject *Sender)
{
// Dummy needed for SetLED() to work
}
//---------------------------------------------------------------------------

void __fastcall TOp::AutoBellLEDStatusChanged(TObject *Sender)
{
// Dummy needed for SetLED() to work
}
//---------------------------------------------------------------------------

void __fastcall TOp::AutoWhistleLEDStatusChanged(TObject *Sender)
{
// Dummy needed for SetLED() to work
}
//---------------------------------------------------------------------------

void __fastcall TOp::CPLRFButtonMouseUp(TObject *Sender,
      TMouseButton Button, TShiftState Shift, int X, int Y)
{
if (Button == mbLeft) {
    // reflect the change on to any open Quick Controls window
	if (ptrQuick_CPLRF) {
    	// The CPLRF Button on the Quick Controls window uses OnMouseDown but that won't trigger on changing the Checked property
		//ptrQuick_CPLRF->Checked = false;	// this does not work to change the look of the button
        ptrQuick_CPLRF->GradBtnFace->Visible = false;
    	}
    }
}
//---------------------------------------------------------------------------

void __fastcall TOp::CPLRRButtonMouseUp(TObject *Sender,
      TMouseButton Button, TShiftState Shift, int X, int Y)
{
if (Button == mbLeft) {
    // reflect the change on to any open Quick Controls window
	if (ptrQuick_CPLRR) {
    	// The CPLRR Button on the Quick Controls window uses OnMouseDown but that won't trigger on changing the Checked property
		//ptrQuick_CPLRR->Checked = false;	// this does not work to change the look of the button
        ptrQuick_CPLRR->GradBtnFace->Visible = false;
    	}
    }
}
//---------------------------------------------------------------------------

void __fastcall TOp::SXSButtonMouseUp(TObject *Sender, TMouseButton Button,
      TShiftState Shift, int X, int Y)
{
if (Button == mbLeft) {
    // reflect the change on to any open Quick Controls window
	if (ptrQuick_SXS) {
    	// The SXS Button on the Quick Controls window uses OnMouseDown but that won't trigger on changing the Checked property
		//ptrQuick_SXS->Checked = false;	// this does not work to change the look of the button
        ptrQuick_SXS->GradBtnFace->Visible = false;
    	}
    }
}
//---------------------------------------------------------------------------

void __fastcall TOp::SRSButtonMouseUp(TObject *Sender, TMouseButton Button,
      TShiftState Shift, int X, int Y)
{
if (Button == mbLeft) {
    // reflect the change on to any open Quick Controls window
	if (ptrQuick_SRS) {
    	// The SRS Button on the Quick Controls window uses OnMouseDown but that won't trigger on changing the Checked property
		//ptrQuick_SRS->Checked = false;	// this does not work to change the look of the button
        ptrQuick_SRS->GradBtnFace->Visible = false;
    	}
    }
}
//---------------------------------------------------------------------------
// if user sets "Auto Bell", then "Ring Bell w/Horn" must be set false
void __fastcall TOp::AutoBellLEDClick(TObject *Sender)
{
if (AutoBellLED->Checked) {
     BellOnWithHornLED->Checked = false;
     }
}
//---------------------------------------------------------------------------
// if user sets "Auto Whistle", then "Ring Bell w/Horn" must be set false
void __fastcall TOp::AutoWhistleLEDClick(TObject *Sender)
{
if (AutoWhistleLED->Checked) {
     BellOnWithHornLED->Checked = false;
     }
}
//---------------------------------------------------------------------------
// if user sets "Ring Bell w/Horn", then "Auto Bell" and "Auto Whistle" must be set false
void __fastcall TOp::BellOnWithHornLEDClick(TObject *Sender)
{
if (BellOnWithHornLED->Checked) {
    AutoBellLED->Checked = false;
    AutoWhistleLED->Checked = false;
    }
}
//---------------------------------------------------------------------------



