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

© Copyright 2019 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
//---------------------------------------------------------------------------
/*
Supports Engines and Lashups
There are two waitable timers MyTimer (100 milliseconds, auto reset) and MySleepTimer (manual reset)

If this thread crashes, we usually then cannot [Disconnect] from the main window.
Something does not get cleared on the crash. Most likely ptrPC_Thread != NULL
*/
//---------------------------------------------------------------------------
#include <vcl.h>
#include <tchar.h>
#pragma hdrstop

#ifndef BAD_CAST
#include <typeinfo.h>
//using namespace std;
namespace std {
	int stdname = 9;
    }
#endif
// RTC Near Field Communications
#include "RTCMegaMaster.h"		// Dispatch List
#include "RTC_NFC.h"
#include "Engine_Commands.h"
#include "Train_Control.h"
#include "Timer_Thread.h"
#include "PC_Thread.h"
#include "Prog_Control.h"
#include "Setup_Control.h"
#include "Debug_Controls.h"
#include "LashUp_Selection.h"
#include "Comm_Thread.h"
#include "AIU_Control.h"
#include "Op_Control.h"
#include "Volume_Control.h"
#include "Conv_Mode.h"
#include "DL_Thread.h"
#include "Message_Thread.h"
#include "Delay_Win_Msg.h"
#include "DetectedTags.h"
#include "Layout.h"
#include "DDQuery.h"
#include "Route_Scene_Thread.h"
#include "WatchdogSignal.h"
#include "Watchdog_Thread.h"
#include <mmsystem.h>

#pragma package(smart_init)

#define ENGNUMIDX 0
#define FLAGIDX 1
#define DETECTED true
#define NOTDETECTED false
//
// define DEBUGTIMER to print out timer information as this thread runs
//#define DEBUGTIMER
//
//
extern float ScalingFactor[ENGINEMAX];	    // cached DTO and DOD Scaling Factors read up from engines 0-99
extern byte PSType[ENGINEMAX];		        // cached engine type PS2 or PS3 from engines 0-99
//
extern bool TIUSelection[TIUMAX];                       // Flags to indicate that a TIU is present
extern bool SuperTIUMode, TIUSuperMode[TIUMAX];         // Flags to indicate if Super TIU Mode is active
//
extern volatile bool bRadioCOMConnected;	// Radio COM Port Connection Complete Flag
extern volatile bool bRFIDCOMConnected;		// RFID COM Port Connection Complete Flag
extern volatile bool bTIUConnected;			// TIU acknowledged a Command
extern bool bEmergencyStop;		// emergency stop in effect

extern char * Amp(const char *a);
extern volatile bool bRFIDUDPWiFiConnected;	// RFID WiFi UDP Port Connection Flag
extern volatile bool bRFIDTCPWiFiConnected;	// RFID WiFi TCP Port Connection Flag
//From Help: The keyword __thread is used in multithread programs to preserve a unique
//copy of global and static class variables. Each program thread maintains a private
//copy of a __thread variable for each thread.
__declspec(thread) TProgram_Control * ptrPC_local;
//__declspec(thread) int i_local = 0xDEAD;
//---------------------------------------------------------------------------
extern TVolume * ptrVolume[ABSOLUTEMAX];	// Volume Control Pointers - indexed by actual engine number because 0 is a factory reset engine
extern TOp * ptrOp[ABSOLUTEMAX];   			// Operation Control Pointers - indexed by actual engine number because 0 is a factory reset engine
extern TAIUControl * ptrAIUControl[TIUMAX][AIUMAX];	// AIU Control Pointers for 25 AIU - indexed by TIUNo-1 and AIUNo-1
extern char * ptrLUNameList[ABSOLUTEMAX];	// LashUp Engine Name Pointers - indexed by actual engine number because 0 is a factory reset engine
extern char * ptrLUEngineList[ABSOLUTEMAX];	// LashUp Engine List Pointers - indexed by actual engine number because 0 is a factory reset engine
extern HANDLE hRFIDReqEvent[PCMAX];					// pointer(s) to RFID Events created by PC threads
extern TCommThread * ptrTIUCommThread;				// pointer to TIU communications thread
extern TDispatch_List_Thread * ptrDL_Thread;		// pointer to Dispatch List Thread
extern TAbNumEdit *ptrPC_ETime[PCMAX];				// pointers to ETime TAbNumEdit in each Program Control window
extern TMyTimerThread * ptrTimer_Thread;			// pointer to single Timer Thread
extern int PC_Tenths;								// RunTimeinTenths counter (system wide)
extern int ptrPC_Tenths[PCMAX];						// RunTimeinTenths counter in each Program Control window

extern TProgram_Control *ptrProgram_Control[PCMAX];	// pointer(s) to Program Control Windows
extern TThreadList * Event_FIFO[PCMAX];	    		//  FIFO Event List for Program Control Threads - Thread Safe
extern bool Request_Events[PCMAX];					// Block Occupancy Event Support from Layout.cpp
extern int TIUVersion[TIUMAX];                      // TIU version number as an int * 100 (eg v1.06 will have a value of 106)
extern String TIUVersionString[TIUMAX];             // TIU version number as a string
extern struct SIGNAL Signals[SIGNALMAX];		   	// Flags to indicate a Signal was found
extern struct DETECTOR Detector[RFIDMAX];           // Flags to indicate an RFID detector device with up to 2 readers was found

//---------------------------------------------------------------------------
// Cab Signal Numbers and names
extern int iCabSignal[PCMAX][2];					// Signal number of each of two cab signals in PC Control window
													// [HOMESIGNAL] is Top Signal, [DISTANTSIGNAL] is Bottom Signal
extern String AspectNames[];						// Aspect Names (defined in layout.cpp)
//---------------------------------------------------------------------------
void ShowinBuffer(TRichEdit * Buf, String PrintLine, TColor PrintColor = clBlack);
//---------------------------------------------------------------------------
// Calling lua from C
// http://www.troubleshooters.com/codecorn/lua/lua_c_calls_lua.htm
//---------------------------------------------------------------------------
/**
 * charToHex
 *
 * 'ch' Character to be converted to hexadecimal
 *
 * Returns:
 *  Hex value, 0x00 on bad hex character
 */
//---------------------------------------------------------------------------
inline byte charToHex(byte ch)
{
byte val;
if (ch >= 'A' && ch <= 'F')
    val = ch - 55;
    else if (ch >= 'a' && ch <= 'f')
      val = ch - 87;
      else if (ch >= '0' && ch <= '9')
         val = ch - 48;
         else
         val = 0x00;
return val;
}
//---------------------------------------------------------------------------
/* Hex2Digit_to_Decimal
 *
 * convert a two digit hex character string into an 8 bit unsigned decimal number
 */
//--------------------------------------------------------------------------
unsigned char Hex2Digit_to_Decimal(unsigned char * str) {
// convert a 2 digit char string to decimal
unsigned char val = (charToHex(*str) << 4) & 0xF0;
val |= charToHex(*(str + 1)) & 0x0F;
return val;
}
//---------------------------------------------------------------------------
// external function to trim leading and trailing spaces from a char string
void trim_str(char *);
//---------------------------------------------------------------------------
//------------------------ Lua Functions ------------------------------------
//---------------------------------------------------------------------------
// http://systemmanager.ru/svcsunix.en/extfile/portapps/stdargvsvarargs.htm
//
// This is here only for an example of varargs. It is not used
//
/*
pr_str(char *first, ...)
{
	String PLine = "";
	char * current;
	va_list argp;
	va_start(argp,first);
	current = first;
	while (current != NULL){
    	PLine += current;
	   	current = va_arg(argp, char *);
   		}
	va_end(argp);
	ShowinBuffer(ptrPC_local->PCMessages, PLine, (TColor) clNavy);		// Display in Message List
    return 1;
}
*/
//---------------------------------------------------------------------------
static void luaH_tableDump(lua_State *L, int i);
//---------------------------------------------------------------------------
static void luaH_decodeItem(lua_State *L, int i) {
int t = lua_type(L, i);
switch (t) {
	case LUA_TSTRING:	// lua string
		ShowinBuffer(ptrPC_local->PCMessages, Format((String)"%2d : string %s", ARRAYOFCONST((i, lua_tostring(L, i)))), clBlue);
        break;
    case LUA_TBOOLEAN:	// lua boolean
		ShowinBuffer(ptrPC_local->PCMessages, Format((String)"%2d : boolean %s", ARRAYOFCONST((i, lua_toboolean(L, i) ? "true" : "false"))), clBlue);
        break;
    case LUA_TNUMBER:	// lua number
		#if __BORLANDC__ >= 0x570			// BCB4.0 does not support __int64 in ARRAYOFCONST so this can only be used in CB10.1
        // http://www.codenewsfast.com/cnf/article/0/waArticleBookmark.7264296
        // quote: "Type-cast the SecondsLeft value to an "int" when calling ARRAYOFCONST. TVarRec apparently does not support __int64 in CB2006"
        if (lua_isinteger(L, i)) {	// lua integer?
			ShowinBuffer(ptrPC_local->PCMessages, Format((String)"%2d : integer %d", ARRAYOFCONST((i, lua_tointeger(L, i)))), clBlue);
            } else {				// lua float
			ShowinBuffer(ptrPC_local->PCMessages, Format((String)"%2d : number %g", ARRAYOFCONST((i, lua_tonumber(L, i)))), clBlue);
            }
        #else
		ShowinBuffer(ptrPC_local->PCMessages, Format((String)"%2d : number %g", ARRAYOFCONST((i, lua_tonumber(L, i)))), clBlue);
        #endif
        break;
    case LUA_TTABLE:	// lua table
		ShowinBuffer(ptrPC_local->PCMessages, Format((String)"%2d : Type : %s", ARRAYOFCONST((i, lua_typename(L, t)))), clBlue);
        luaH_tableDump(L, i);
        break;
    case LUA_TNIL:		// lua nil
		ShowinBuffer(ptrPC_local->PCMessages, Format((String)"%2d : nil", ARRAYOFCONST((i))), clBlue);
        break;
    default:			// other values
        ShowinBuffer(ptrPC_local->PCMessages, Format((String)"%2d : Type : %s", ARRAYOFCONST((i, lua_typename(L, t)))), clBlue);
        break;
    }
}
//---------------------------------------------------------------------------
//
// Dump a table
//
static void luaH_tableDump(lua_State *L, int i) {
int TOS = lua_gettop(L);
/* table is in the stack at index 't' */
lua_pushnil(L);  /* first key */
while (lua_next(L, i) != 0) {
	/* uses 'key' (at index -2) and 'value' (at index -1) */
	//ShowinBuffer(ptrPC_local->PCMessages, Format((String)"%s - %s",
	//	ARRAYOFCONST(( lua_typename(L, lua_type(L, -2)), lua_typename(L, lua_type(L, -1)))) ),
	//	(TColor) clNavy);
    luaH_decodeItem(L, -2);
    luaH_decodeItem(L, -1);
	/* removes 'value'; keeps 'key' for next iteration */
	lua_pop(L, 1);
	}
assert(lua_gettop(L) == TOS);
}
//---------------------------------------------------------------------------
//
// Dump the Stack
//
static int luaH_StackDump(lua_State *L) {
int i;
int top = lua_gettop(L);	// depth of the stack
ShowinBuffer(ptrPC_local->PCMessages, "StackDump(L)", clBlue); // end the listing
for (i = 1; i <= top ; i++) {	// repeat for each level
	luaH_decodeItem(L, i);
    }
ShowinBuffer(ptrPC_local->PCMessages, "-------", clBlue); // end the listing
return 0;	// nothing returned
}
//---------------------------------------------------------------------------
// Displays a string on the Program Control Message List
//
// return true on success
//
// https://stackoverflow.com/questions/3412434/how-to-use-the-operator-in-lua-with-c-code 	// How to use the ... operator in lua with C code
static int luaH_PrintString(lua_State *L) {
bool bretv = false;
String PLine = "";
int n = lua_gettop(L);	//n is the number of arguments, use if needed
if (n == 0) {
	lua_pushboolean(L, bretv);	// no argument, just return
	return 1; //we are returning one value
	}
if(lua_checkstack(L, n + 1)) {
	int i;
	for(i = 1 ; i <= n ; i++) {
    	int t = lua_type(L, i);
        switch(t) {
            case LUA_TSTRING:
            case LUA_TNUMBER:
			    // lua_tolstring will convert numbers and integers to strings
				PLine += String(lua_tolstring(L, i, NULL));
				break;
            case LUA_TBOOLEAN:
            	{	// print boolean value
				bool bval = lua_toboolean(L, i);
				PLine += (bval) ? "true" : "false";
                }
                break;
			case LUA_TNIL:
            	// print nil value
				PLine += "nil";
				break;
			default:
				// print type name for all other types
				PLine += lua_typename(L, t);
				break;
         	}
		}
    bretv = true;
	} else {
	throw Format((String)"Cannot grow stack by %d in call to PrintString(...)/print(...)", ARRAYOFCONST((n)));
	}
ShowinBuffer(ptrPC_local->PCMessages, PLine, clBlack);		// Display in Message List
lua_pushboolean(L, bretv);
return 1; //we are returning one value
}
//---------------------------------------------------------------------------
// Displays a string on the Program Control Message List in color
// First argument is a color as defined in "defines.lua"
//
// return true on success, false on error
//
// https://stackoverflow.com/questions/3412434/how-to-use-the-operator-in-lua-with-c-code 	// How to use the ... operator in lua with C code
static int luaH_PrintStringColor(lua_State *L) {
bool bretv = false;
String PLine = "";
int n = lua_gettop(L);	//n is the number of arguments, use if needed
if (n < 2) {
	HARDERROR(ptrPC_local->PCMessages, Format((String)"Wrong # of arguments in call to printc() - got %d need at least 2", ARRAYOFCONST((n))), (TColor) clRed);		// Display in Message List
    lua_pushnil(L);
	return 1; //we are returning one value
	}

// first argument is print color
TColor CColor;
int ColorValue = luaL_checknumber(L,1);
switch (ColorValue) {
    case luaGreen:
		CColor = clGreen;
    	break;
    case luaYellow:
		CColor = clYellow;
    	break;
    case luaRed:
		CColor = clRed;
    	break;
    case luaBlack:
    default:
		CColor = clBlack;
    	break;
    case luaBlue:
		CColor = clBlue;
    	break;
    case luaFuchsia:
		CColor = clFuchsia;
    	break;
	}
if(lua_checkstack(L, n + 1)) {
	int i;
	for(i = 2 ; i <= n ; i++) {
    	int t = lua_type(L, i);
        switch(t) {
            case LUA_TSTRING:
            case LUA_TNUMBER:
			    // lua_tolstring will convert numbers and integers to strings
				PLine += String(lua_tolstring(L, i, NULL));
				break;
            case LUA_TBOOLEAN:
            	{	// print boolean value
				bool bval = lua_toboolean(L, i);
				PLine += (bval) ? "true" : "false";
                }
                break;
			case LUA_TNIL:
            	// print nil value
				PLine += "nil";
				break;
			default:
				// print type name for all other types
				PLine += lua_typename(L, t);
				break;
         	}
		}
    bretv = true;
	} else {
    throw Format((String)"Cannot grow stack by %d in call to PrintStringColor(...)/printc(...)", ARRAYOFCONST((n)));
	}
ShowinBuffer(ptrPC_local->PCMessages, PLine, CColor);		// Display in Message List
lua_pushboolean(L, bretv);
return 1; //we are returning one value
}
//---------------------------------------------------------------------------
// get a global integer (Lua book pg 290)
//
// returns the value as an integer
// returns -1 on error
//
int getglobint(lua_State *L, const char *var) {
int isnum, iresult;
if (lua_getglobal(L, var) != LUA_TNUMBER) {
	// not a global integer so return -1
    iresult = -1;
    } else {
    // convert the value into iresult
	iresult = lua_tointegerx(L, -1, &isnum);
	if (!isnum) {
    	// can't convert to an integer so return -1
        iresult = -1;
    	}
    }
lua_pop(L, 1);	// remove result from stack
return iresult;
}
//---------------------------------------------------------------------------
// get a global string (Lua book pg 290)
//
// returns true on success, false on failure ('dst' is not changed)
// string of maximum size 'length' returned in 'dst'
//
boolean getglobstring(lua_State *L, const char *var, char * dst, size_t length) {
if (lua_getglobal(L, var) != LUA_TSTRING) {
    // var string not defined, don't change dst string
    lua_pop(L,1);	// remove result from stack
    return false;
	} else {
	const char * result = lua_tostring(L, -1);
    strncpy(dst, result, length);
    }
lua_pop(L, 1);	// remove result from stack
return true;
}
//---------------------------------------------------------------------------
// get a global boolean (Lua book pg 290)
//
// returns the value of the boolean
// returns false on failure
//
boolean getglobbool(lua_State *L, const char *var) {
boolean bresult;
if (lua_getglobal(L, var) != LUA_TBOOLEAN) {
	// not a global boolean so return false
    bresult = false;
    } else {
    // convert the value into bresult
	bresult = lua_toboolean(L, -1);
    }
lua_pop(L, 1);	// remove result from stack
return bresult;
}
//---------------------------------------------------------------------------
// Displays a string on the Program Control Message List
// return true on success
// THIS IS OLD VERSION OF lua_PrintString that takes 1 argument
// NOT USED ANY MORE
/*
static int luaH_varprint (lua_State *L) {
	bool bretv = true;
	int n = lua_gettop(L);	//n is the number of arguments, use if needed
    if (n > 1) {
   		throw Format((String)"Too many arguments in call to PrintString(...)/print(...) : %d", ARRAYOFCONST((n)));
    	}
	String Str = String(lua_tolstring(L,-1, NULL));
    ShowinBuffer(ptrPC_local->PCMessages, Str);		// Display in Message List
	lua_pushboolean(L, bretv);
	return 1; //we are returning one value
}
*/
//---------------------------------------------------------------------------
static int luaH_ConvertEngineList(lua_State *L, int iLashUpNo)
{
// Convert a Lua table of engines into an ASCII (Remote or WTIU WiFi) engine list.
// Negative engine number means reverse running and causes the H.O. bit to be turned on
// and is represented in the final list with the abs() value or'ed with 0x80
// [0] = 0x2C ','
// [1] = lead engine number
// [2..n] = middle engine numbers (if any)
// [n+1] = trailing engine number
// [n+2] = 0xFF
// [n+3] = 0x00
//
// The table on the top of the stack
//
// These three functions have similar code and should be consolidated into one function:
//
// void __fastcall TQuickCtrl::ConvertCSVString(int LashUpNo, int ControlNo)
// static int luaH_ConvertEngineList(lua_State *L, int iTIUNo, int iLashUpNo)
// void __fastcall TLashUp::ParseLashUpEngine(String LashUpEncoded, TSyncListBox * EnginesinLashUpLB, TCheckListBox * RevCheckLB)
//
char LashUpCommand[LASHUP_ENGINE_LIST_LENGTH];
int EngIndex = 0;   // position in output engine list
LashUpCommand[EngIndex++] = 0x2C;	// Comma
// extract Engine Numbers from Table
int count = lua_rawlen(L, -1);

if(!lua_istable(L,-1)) {
    HARDERROR(ptrPC_local->PCMessages, "luaH_ConvertEngineList : LashUp List is not a Table", (TColor) clRed);		// Display in Message List
	lua_pushnil(L);
   	return 1; //we are returning one value
    }

 for (int i = 1; i <= count; i++) {
    int isnum;
    lua_rawgeti(L, -1, i);
    int val = (char) (lua_tonumberx(L, -1, &isnum));
    lua_pop(L, 1);

    if (isnum == NULL) {
        LashUpCommand[EngIndex] = 0x00;
        HARDERROR(ptrPC_local->PCMessages, Format((String)"Invalid Engine number in Lashup Engine List position %d", ARRAYOFCONST((i))), (TColor) clRed);		// Display in Message List
	    lua_pushnil(L);
   		return 1; //we are returning one value
        }
    try {
        // DCS V6 and later and WTIU
        int iEng = abs(val);
       	// Engine 1-99, DCS 2-100 (so we add 1 here because the LashUp string is in DCS Engine #)
        iEng++; // convert to DCS Engine Number
        if (val < 0) {
            iEng |= 0x80;	// H.O bit indicates reverse running
            }
        // DCS V6 and later and WTIU
        // DCS V5 and earlier is no longer supported
        sprintf(&LashUpCommand[EngIndex], "%02X", iEng);    // convert one byte to 2 ASCII characters
        EngIndex += 2;
        // Radio Mode (ASCII)
        //5555669965AA656969596966656665656666AAAAAA9995696A95A95A4D
        //  00  66  75  34  2C  39  31  30  33  FF  EE  94  6A  AD
        //     'f' 'u' '4' ',' '9' '1' '0' '3' 'ÿ'
        // or
        // u4,9103ÿ
        // WiFi Mode (ASCII)
        // u4,9103ÿ
        LashUpCommand[EngIndex] = '\0';
        assert(EngIndex < (LASHUP_ENGINE_LIST_LENGTH - 2));		// Check for string limit

        }
    catch(...) {
        // ignore errors but don't crash
        LashUpCommand[0] = 0x00;	// Null terminator
        ptrLUEngineList[iLashUpNo] = NULL;
        return 0;
        }
    }
LashUpCommand[EngIndex++] = 0xFF;
LashUpCommand[EngIndex] = 0x00;	// Null terminator
//
// create a permanent spot and move the Lashup Engine List into it
if (ptrLUEngineList[iLashUpNo] == NULL) {
    ptrLUEngineList[iLashUpNo] = new char [LASHUP_ENGINE_LIST_LENGTH];	// deleted when main thread exits
    assert(ptrLUEngineList[iLashUpNo] != NULL);		// make sure that memory allocation worked
    }
strncpy(ptrLUEngineList[iLashUpNo], LashUpCommand, LASHUP_ENGINE_LIST_LENGTH);
return 1;
}
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
static int luaH_CSVIntegersToLuaTable(lua_State *L, const char *CSV)
{
// Request to ChatGPT:
// Convert a CSV engine list into a lua table
// Convert each CSV field to a Lua integer
//
// The table on the top of the stack on exit
//
/*
luaH_CSVNumbersToLuaTable(L, "12,-45,7,-123,99");

leaves this table on the top of the Lua stack:

+------------------+
| Lua table        |
+------------------+
| [1] = 12         |
| [2] = -45        |
| [3] = 7          |
| [4] = -123       |
| [5] = 99         |
+------------------+

in lua:

print(t[1])    -- 12
print(t[2])    -- -45
print(t[4])    -- -123


If we use the CSVIntegersToLuaTable() function from above, the Lua-callable
wrapper should retrieve the CSV argument from the Lua stack, call the C++
function, and return 1 because the table is already on the Lua stack.

//---------------------------------------------------------------------------
static int Lua_CSVToTable(lua_State *L)
{
    const char *CSV;

    // Check that argument #1 is a string.
    CSV = luaL_checkstring(L, 1);

    // This creates the Lua table and leaves it
    // on top of the Lua stack.
    CSVIntegersToLuaTable(L, CSV);

    // Return one value to Lua -- the table.
    return 1;
}
//---------------------------------------------------------------------------
*/
    int index = 1;
    const char *p;
    const char *start;

    lua_newtable(L);

    if (CSV == NULL || *CSV == '\0') return 0;

    p = CSV;
    start = CSV;

    while (1) {
        if (*p == ',' || *p == '\0') {
            char Number[32];
            int len, value;

            len = (int)(p - start);

            if (len > sizeof(Number) - 1)
                len = sizeof(Number) - 1;

            memcpy(Number, start, len); // Copy the number into a temporary buffer.
            Number[len] = '\0';

            value = strtol(Number, NULL, 10);   // Convert CSV field to a number.

            // Push key.
            lua_pushinteger(L, index);

            // Push value.
            lua_pushinteger(L, value);

            // table[index] = value
            lua_rawset(L, -3);

            index++;

            if (*p == '\0')
                break;

            p++;    // Skip the comma.
            start = p;
        }
        else
        {
            p++;
        }
    }

    return index - 1;
}
//---------------------------------------------------------------------------
// Title(text, EngineNo)
//
// Set the title and optionally the Engine Number on the Program Control window
// returns true if at least one valid argument is given, returns false on error
//
static int luaH_Title(lua_State *L) {
	int n = lua_gettop(L);	//n is the number of arguments
    bool bretv = true;
    if (n < 1) {
		HARDERROR(ptrPC_local->PCMessages, Format((String)"Wrong # of arguments in call to Title() - got %d need at least 1", ARRAYOFCONST((n))), (TColor) clRed);		// Display in Message List
	    lua_pushnil(L);
   		return 1; //we are returning one value
        } else {
        if (n >= 1) {
        	// Set Title String
		    String TextValue = luaL_checkstring(L, 1);
		    if (TextValue.Length() > 0) ptrTIUCommThread->SetLabelText(ptrPC_local->Title, TextValue);
            if (n == 2) {
            	int val = luaL_checknumber(L, 2);
            	// Set Engine number spinner
                ptrTIUCommThread->SetSpinnerValue(ptrPC_local->EngineSpin, val);
            	}
            }
        }
	lua_pushboolean(L, bretv);
	return 1; //we are returning one value
}
//---------------------------------------------------------------------------
//Startup(float when, byte iEngineNo, byte TIUNo);// time delay (when) in seconds
//Startup(float when, byte iLashUpNo, table LashUpEngineList, byte TIUNo);// time delay (when) in seconds
//
// Startup an Engine/Lashup without opening an Operations Windows
// return true on success, nil on error
//
static int luaH_StartUp (lua_State *L) {
	int n = lua_gettop(L);	//n is the number of arguments
    bool bretv = false;
    byte iConsistNo, iTIUNo;
    float when;
    if (n != 3 && n != 4) {
		HARDERROR(ptrPC_local->PCMessages, Format((String)"Wrong # of arguments in call to StartUp() - got %d need 3 or 4", ARRAYOFCONST((n))), (TColor) clRed);		// Display in Message List
    	lua_pushnil(L);
	   	return 1; //we are returning one value
        }
    if (n == 3) {
		when = luaL_checknumber(L,1);
		iConsistNo = luaL_checkinteger(L,2);
		iTIUNo = luaL_checkinteger(L,3);
        luaL_argcheck(L, iConsistNo >= ENGINEMIN && iConsistNo <= ENGINEMAX, 2, "Engine Number out of range");	// Engine 1-99
        luaL_argcheck(L, iTIUNo >= 1 && iTIUNo <= TIUMAX, 3, "TIU Number out of range");
		bretv = ptrPC_local->ptrPC_Thread->StartUp(when, iConsistNo, iTIUNo);	// TIUNo is 1  to 5, iEngine is 1 to 99
        }
    if (n == 4) {
        when = luaL_checknumber(L,1);
        iConsistNo = luaL_checkinteger(L,2);
        luaL_checktype(L, 3, LUA_TTABLE);	        // 3th argument must be a table
        iTIUNo = luaL_checkinteger(L,4);
        luaL_argcheck(L, iConsistNo >= LASHUPMIN && iConsistNo <= LASHUPMAX, 2, "Lashup Number out of range");	// Lashup 101-120
        luaL_argcheck(L, iTIUNo >= 1 && iTIUNo <= TIUMAX, 4, "TIU Number out of range");
        if (iConsistNo < LASHUPMIN || iConsistNo > LASHUPMAX) {
			HARDERROR(ptrPC_local->PCMessages, Format((String)"Invalid Lashup number %d in call to StartUp()", ARRAYOFCONST((iConsistNo))), (TColor) clRed);		// Display in Message List
		    lua_pushnil(L);
   			return 1; //we are returning one value
            } else {
			// extract Engine Numbers from Table passed as fourth argument and put on top of stack
	        lua_pushvalue (L, 3);			// push table
    	    luaH_ConvertEngineList(L, iConsistNo);
        	lua_pop(L, 1);					// remove table
	        // Startup LashUp
			bretv = ptrPC_local->ptrPC_Thread->StartUp(when, iConsistNo, iTIUNo);	// TIUNo is 1  to 5, LashUpNo is 101 to 120
        	}
        }
	lua_pushboolean(L, bretv);
	return 1; //we are returning one value
}
//---------------------------------------------------------------------------
//ShutDown(float when, byte iEngineNo, byte TIUNo);// time delay (when) in seconds
//ShutDown(float when, byte iLashUpNo, byte TIUNo);// time delay (when) in seconds
//
// Shutdown an Engine/Lashup *without* an Operations Window
// Return true on success, nil on error
//
static int luaH_ShutDown (lua_State *L) {
	bool bretv = false;
	int n = lua_gettop(L);	//n is the number of arguments, use if needed
    if (n != 3) {
    	HARDERROR(ptrPC_local->PCMessages, Format((String)"Wrong # of arguments in call to ShutDown() - got %d need 3", ARRAYOFCONST((n))), (TColor) clRed);		// Display in Message List
    	lua_pushnil(L);
	   	return 1; //we are returning one value
       	} else {
	    float when = luaL_checknumber(L,1);
		byte iConsistNo = luaL_checkinteger(L,2);
		byte iTIUNo = luaL_checkinteger(L,3);
        luaL_argcheck(L, iConsistNo >= ENGINEMIN && iConsistNo <= LASHUPMAX, 2, "Engine/LashUp Number out of range"); 	// Consists 1-120
        luaL_argcheck(L, iTIUNo >= 1 && iTIUNo <= TIUMAX, 3, "TIU Number out of range");
		bretv = ptrPC_local->ptrPC_Thread->ShutDown(when, iConsistNo, iTIUNo);
        if (!bretv) {
            lua_pushnil(L);
            return 1;
        	}
        }
	lua_pushboolean(L, bretv);
	return 1; //we are returning one value
}
//---------------------------------------------------------------------------
//EngineStartUp(int Engine, int TIU, int iDelayTimeSeconds = 0);
//
// Startup an Engine, *opening* an Operations Window
// return true on success
//
static int luaH_EngineStartUp (lua_State *L) {
	bool bretv = false;
	int n = lua_gettop(L);	//n is the number of arguments
    byte iEngineNo;
    byte iTIUNo;
    int iDelayTimeSeconds = 0;
    if (n != 2 && n != 3) {
    	HARDERROR(ptrPC_local->PCMessages, Format((String)"Wrong # of arguments in call to EngineStartUp() - got %d need 2 or 3", ARRAYOFCONST((n))), (TColor) clRed);		// Display in Message List
    	lua_pushnil(L);
	   	return 1; //we are returning one value
    	}
    if (n == 2) {
		iEngineNo = luaL_checkinteger(L,1);
		iTIUNo = luaL_checkinteger(L,2);
        luaL_argcheck(L, iEngineNo >= ENGINEMIN && iEngineNo <= ENGINEMAX, 1, "Engine Number out of range");		// Engine 1-99
        luaL_argcheck(L, iTIUNo >= 1 && iTIUNo <= TIUMAX, 2, "TIU Number out of range");
		ptrPC_local->ptrPC_Thread->MyEngineStartUp(iEngineNo, iTIUNo, 0);	// Startup Engine and open Operations window
        bretv = true;
        } else
        if (n == 3) {
			iEngineNo = luaL_checkinteger(L,1);
			iTIUNo = luaL_checkinteger(L,2);
        	iDelayTimeSeconds = luaL_checkinteger(L,3);
            luaL_argcheck(L, iEngineNo >= ENGINEMIN && iEngineNo <= ENGINEMAX, 1, "Engine Number out of range");	// Engine 1-99
	        luaL_argcheck(L, iTIUNo >= 1 && iTIUNo <= TIUMAX, 2, "TIU Number out of range");
			ptrPC_local->ptrPC_Thread->MyEngineStartUp(iEngineNo, iTIUNo, iDelayTimeSeconds);	// Startup Engine and open Operations window
            bretv = true;
	        }
lua_pushboolean(L, bretv);
return 1; //we are returning one value
}
//---------------------------------------------------------------------------
//LashUpStartUp(LashUpName, iLashUpNo, LashUpEngineList, iTIUNo);
//
// Startup a LashUp *opening* an Operations Window
// return true on success, nil on error
//
static int luaH_LashUpStartUp (lua_State *L) {
int n = lua_gettop(L);	//n is the number of arguments
bool bretv = false;
byte iLashUpNo;
byte iTIUNo;
if (n != 4) {
    HARDERROR(ptrPC_local->PCMessages, Format((String)"Wrong # of arguments in call to LashUpStartUp() - got %d need 4", ARRAYOFCONST((n))), (TColor) clRed);		// Display in Message List
    lua_pushnil(L);
   	return 1; //we are returning one value
	} else {
    // extract arguments
    iLashUpNo = luaL_checkinteger(L,2);
    luaL_checktype(L, 3, LUA_TTABLE);		    		// 3rd argument must be a table
    iTIUNo = luaL_checkinteger(L,4);
	luaL_argcheck(L, iLashUpNo >= LASHUPMIN && iLashUpNo <= LASHUPMAX, 2, "Lashup Number out of range");	// Lashup 101-120
	luaL_argcheck(L, iTIUNo >= 1 && iTIUNo <= TIUMAX, 4, "TIU Number out of range");
	ptrLUNameList[iLashUpNo] = new char [17];	// deleted when main thread exits if not sooner
    assert(ptrLUNameList[iLashUpNo]);			// make sure the construction worked
    snprintf(ptrLUNameList[iLashUpNo], 17, "%.16s",luaL_checkstring(L,1));	// maximum of 16 characters
    if (iLashUpNo < LASHUPMIN || iLashUpNo > LASHUPMAX) {
	    HARDERROR(ptrPC_local->PCMessages, Format((String)"Invalid Lashup number %d in call to LashUpStartUp()", ARRAYOFCONST((iLashUpNo))), (TColor) clRed);		// Display in Message List
    	lua_pushnil(L);
	   	return 1; //we are returning one value
        } else {
	    // extract Engine Numbers from Table passed as third argument and put on top of stack
    	lua_pushvalue (L, 3);			// push table
	    luaH_ConvertEngineList(L, iLashUpNo);
    	lua_pop(L, 1);					// remove table
	    // Startup LashUp and open Operations window
    	// Send CM_LASHUPSTARTUP message to MainForm which will issue the startup command
	    // Pointer to cLashUpEngineList may go out of scope if PostMessage is used
    	(void) SendMessage(MainForm->Handle, CM_LASHUPSTARTUP, (unsigned int) ptrLUEngineList[iLashUpNo], (iTIUNo << 8) | iLashUpNo);
                                        // iTIUNo is 1  to 5, LashUpNo is 101 to 120
	    bretv = true;
        }
    }
lua_pushboolean(L, bretv);
return 1; //we are returning one value
}
//---------------------------------------------------------------------------
//EngineShutDown(int iEngine, int TIU, int iDelayTimeSeconds = 0);
//
// Shutdown an Engine *closing any open* Operations Windows
// return true on success, nil on error
//
static int luaH_EngineShutDown (lua_State *L) {
bool bretv = true;
int n = lua_gettop(L);	//n is the number of arguments
byte iEngineNo;
byte iTIUNo;
int iDelayTimeSeconds = 0;
if (n != 2 && n != 3) {
	HARDERROR(ptrPC_local->PCMessages, Format((String)"Wrong # of arguments in call to EngineShutDown() - got %d need 2 or 3", ARRAYOFCONST((n))), (TColor) clRed);		// Display in Message List
    lua_pushnil(L);
   	return 1; //we are returning one value
    }
if (n == 2) {
    iDelayTimeSeconds = 0;
    } else
    if (n == 3) {
        iDelayTimeSeconds = luaL_checkinteger(L,3);
        }
iEngineNo = luaL_checkinteger(L,1);
iTIUNo = luaL_checkinteger(L,2);
luaL_argcheck(L, iEngineNo >= ENGINEMIN && iEngineNo <= ENGINEMAX, 1, "Engine Number out of range");	// Engine 1-99
luaL_argcheck(L, iTIUNo >= 1 && iTIUNo <= TIUMAX, 2, "TIU Number out of range");
ptrPC_local->ptrPC_Thread->MyEngineShutDown(iEngineNo, iTIUNo, iDelayTimeSeconds);	// Shutdown Engine and close Operations window
lua_pushboolean(L, bretv);
return 1; //we are returning one value
}
//---------------------------------------------------------------------------
//LashUpShutDown(int iLashUpNo, int iTIUNo);
//
// Shutdown a Lashup *closing any open* Operations Window
// return true on success, nil on error
//
static int luaH_LashUpShutDown (lua_State *L) {
    bool bretv = false;
    byte iLashUpNo;
    byte iTIUNo;
	int n = lua_gettop(L);	//n is the number of arguments, use if needed
	if (n != 2) {
    	HARDERROR(ptrPC_local->PCMessages, Format((String)"Wrong # of arguments in call to LashUpShutDown() - got %d need 2", ARRAYOFCONST((n))), (TColor) clRed);		// Display in Message List
	    lua_pushnil(L);
   		return 1; //we are returning one value
		} else {
        iLashUpNo = luaL_checkinteger(L,1);
        iTIUNo = luaL_checkinteger(L,2);
        luaL_argcheck(L, iLashUpNo >= LASHUPMIN && iLashUpNo <= LASHUPMAX, 1, "Lashup Number out of range");	// Lashup 101-120
        luaL_argcheck(L, iTIUNo >= 1 && iTIUNo <= TIUMAX, 2, "TIU Number out of range");
        if (ptrLUEngineList[iLashUpNo] == NULL) {   // does Engine List exist? (created by LashUpStartUp()
            lua_pushnil(L);
            return 1; //we are returning one value
            }
        // Send CM_ LASHUPSHUTDOWN message to MainForm which will set speed to zero and issue shutdown command
        // Use SendMessage (instead of PostMessage) to wait for the shutdown commands to be sent
	    // Pointer to cLashUpEngineList may go out of scope if PostMessage is used
    	(void) SendMessage(MainForm->Handle, CM_LASHUPSHUTDOWN, (unsigned int) ptrLUEngineList[iLashUpNo], (iTIUNo << 8) | iLashUpNo);
                                        // iTIUNo is 1  to 5, LashUpNo is 101 to 120
        bretv = true;
        // Note: CM_ LASHUPSHUTDOWN deletes the LUEngineList and LUName if they exist
        }
    lua_pushboolean(L, bretv);
    return 1; //we are returning one value
}
//---------------------------------------------------------------------------
//StartUpActiveEngines(int iDelayTimeSeconds = 0);
//
// Startup all active engines opening an Operations window for each one
// return true on success, nil on error
//
// Older version of this function was:
// StartUpActiveEngines(int TIUNo, int iDelayTimeSeconds = 0);
//
static int luaH_StartUpActiveEngines (lua_State *L) {
	int n = lua_gettop(L);	//n is the number of arguments
    bool bretv = true;
	if (n != 0 && n != 1) {
    	HARDERROR(ptrPC_local->PCMessages, Format((String)"Wrong # of arguments in call to StartUpActiveEngines() - got %d need 0 or 1", ARRAYOFCONST((n))), (TColor) clRed);		// Display in Message List
	    lua_pushnil(L);
   		return 1; //we are returning one value
    	} else {
        int LocalDelayTimeSeconds = 0;
		if (n == 1) {
            LocalDelayTimeSeconds = luaL_checkinteger(L,1);
            } else {
            // n == 0
            LocalDelayTimeSeconds = 0;
            }
        if (DEBUGGINGX(9)) ShowinBuffer(ptrPC_local->PCMessages, "StartUpActiveEngines", (TColor) clNavy);		// Display in Message List
        ptrPC_local->ptrPC_Thread->MyStartUpActiveEngines(LocalDelayTimeSeconds);	// Startup All Active Engines and open Operations windows
        }
    lua_pushboolean(L, bretv);
    return 1; //we are returning one value
}
//---------------------------------------------------------------------------
//ShutDownAllEngines(int iDelayTimeSeconds = 0);
//
// Shutdown all engines closing any open Operations Windows
// return true on success, nil on error
//
// Older version of this function was:
//ShutDownAllEngines(int TIUNo, int iDelayTimeSeconds = 0);
//
static int luaH_ShutDownAllEngines (lua_State *L) {
	int n = lua_gettop(L);	//n is the number of arguments
    bool bretv = true;
	if (n != 0 && n != 1) {
	    HARDERROR(ptrPC_local->PCMessages, Format((String)"Wrong # of arguments in call to ShutDownAllEngines() - got %d need 0 or 1", ARRAYOFCONST((n))), (TColor) clRed);		// Display in Message List
	    lua_pushnil(L);
   		return 1; //we are returning one value
    	} else {
        int LocalDelayTimeSeconds = 0;
        if (n == 1) {
            LocalDelayTimeSeconds = luaL_checkinteger(L,1);
            } else {
            // n == 0
            LocalDelayTimeSeconds = 0;
            }
		if (DEBUGGINGX(9)) ShowinBuffer(ptrPC_local->PCMessages, "ShutDownAllEngines", (TColor) clNavy);		// Display in Message List
		ptrPC_local->ptrPC_Thread->MyShutDownAllEngines(LocalDelayTimeSeconds);	// Shutdown All Engines and close Operations windows
		}
    lua_pushboolean(L, bretv);
    return 1; //we are returning one value
}
//---------------------------------------------------------------------------
//ShutDownActiveEngines(int iDelayTimeSeconds = 0);
//
// Shutdown all active engines closing any open Operations Window
// return true on success, nil on error
//
// Older version of this function was:
//ShutDownActiveEngines(int TIUNo, int iDelayTimeSeconds = 0);
//
static int luaH_ShutDownActiveEngines (lua_State *L) {
	int n = lua_gettop(L);	//n is the number of arguments
    bool bretv = true;
	if (n != 0 && n != 1) {
	    HARDERROR(ptrPC_local->PCMessages, Format((String)"Wrong # of arguments in call to ShutDownActiveEngines() - got %d need 0 or 1", ARRAYOFCONST((n))), (TColor) clRed);		// Display in Message List
	    lua_pushnil(L);
   		return 1; //we are returning one value
    	} else {
        int LocalDelayTimeSeconds = 0;
        if (n == 1) {;
            LocalDelayTimeSeconds = luaL_checkinteger(L,1);
            } else {
            // n == 0
            LocalDelayTimeSeconds = 0;
            }
        if (DEBUGGINGX(9)) ShowinBuffer(ptrPC_local->PCMessages, "ShutDownActiveEngines", (TColor) clNavy);		// Display in Message List
        ptrPC_local->ptrPC_Thread->MyShutDownActiveEngines(LocalDelayTimeSeconds);	// Shutdown Active Engines and close Operations windows
		}
    lua_pushboolean(L, bretv);
    return 1; //we are returning one value
}
//---------------------------------------------------------------------------
//CabChat(bool bState, float when, byte ConsistNo, byte TIUNo);// time delay (when) in seconds
//
// return true on success, nil on error
//
static int luaH_CabChat (lua_State *L) {
    bool bretv = false;
    int n = lua_gettop(L);	//n is the number of arguments, use if needed
	if (n != 4) {
		HARDERROR(ptrPC_local->PCMessages, Format((String)"Wrong # of arguments in call to CabChat() - got %d need 4", ARRAYOFCONST((n))), (TColor) clRed);		// Display in Message List
	    lua_pushnil(L);
   		return 1; //we are returning one value
		} else {
        bool bState = lua_toboolean(L,1);
        float when = luaL_checknumber(L,2);
        byte iConsistNo = luaL_checkinteger(L,3);
        byte iTIUNo = luaL_checkinteger(L,4);
        luaL_argcheck(L, iConsistNo >= ENGINEMIN && iConsistNo <= LASHUPMAX, 3, "Engine/Lashup Number out of range");
        luaL_argcheck(L, iTIUNo >= 1 && iTIUNo <= TIUMAX, 4, "TIU Number out of range");
        bretv = ptrPC_local->ptrPC_Thread->CabChat(bState, when, iConsistNo, iTIUNo);
        if (!bretv) {
        	lua_pushnil(L);
            return 1;
            }
        }
    lua_pushboolean(L, bretv);
    return 1; //we are returning one value
}
//---------------------------------------------------------------------------
//Schedule(char * sCommand, float when, byte iConsistNo, byte iTIUNo) {
//
// Schedule a command to run, time delay (when) in seconds
// return true on success, nil on error
//
static int luaH_Schedule (lua_State *L) {
    bool bretv = false;
    int n = lua_gettop(L);	//n is the number of arguments, use if needed
    if (n != 4) {
    	HARDERROR(ptrPC_local->PCMessages, Format((String)"Wrong # of arguments in call to Schedule() - got %d need 4", ARRAYOFCONST((n))), (TColor) clRed);		// Display in Message List
	    lua_pushnil(L);
   		return 1; //we are returning one value
		} else {
        AnsiString Command = luaL_checkstring(L,1);// AnsiString needed because .c_str() is taken and char string required
        float when = luaL_checknumber(L,2);
        byte iConsistNo = luaL_checkinteger(L,3);
        byte iTIUNo = luaL_checkinteger(L,4);
        luaL_argcheck(L, iConsistNo >= ENGINEMIN && iConsistNo <= LASHUPMAX, 3, "Engine/Lashup Number out of range");
        luaL_argcheck(L, iTIUNo >= 1 && iTIUNo <= TIUMAX, 4, "TIU Number out of range");
        if (DEBUGGINGX(9)) ShowinBuffer(ptrPC_local->PCMessages, "Schedule", (TColor) clNavy);		// Display in Message List
        if (DEBUGGINGX(8)) {
            ShowinBuffer(ptrPC_local->PCMessages, "Schedule Command : " + Command, (TColor) clNavy);		// Display in Message List
            ShowinBuffer(ptrPC_local->PCMessages, "Schedule When : " + String(when), (TColor) clNavy);		// Display in Message List
            ShowinBuffer(ptrPC_local->PCMessages, "Schedule ConsistNo : " + String(iConsistNo), (TColor) clNavy);		// Display in Message List
            ShowinBuffer(ptrPC_local->PCMessages, "Schedule TIUNo : " + String(iTIUNo), (TColor) clNavy);		// Display in Message List
            }
        bretv = ptrPC_local->ptrPC_Thread->Schedule(Command.c_str(), when, iConsistNo, iTIUNo, NULL, 0);
        if (!bretv) {
        	lua_pushnil(L);
            return 1;
            }
        }
	lua_pushboolean(L, bretv);
	return 1; //we are returning one value
}
//---------------------------------------------------------------------------
//Setting(int iSet, float when, byte iConsistNo, byte TIUNo);// time delay (when) in seconds
//
// sends the "ab" command with value iSet
// return true on success, nil on error
//
static int luaH_Setting (lua_State *L) {
	bool bretv = false;
	int n = lua_gettop(L);	//n is the number of arguments, use if needed
    if (n != 4) {
    	HARDERROR(ptrPC_local->PCMessages, Format((String)"Wrong # of arguments in call to Setting() - got %d need 4", ARRAYOFCONST((n))), (TColor) clRed);		// Display in Message List
	    lua_pushnil(L);
   		return 1; //we are returning one value
    	} else {
        int iSet = luaL_checkinteger(L,1);
        float when = luaL_checknumber(L,2);
        byte iConsistNo = luaL_checkinteger(L,3);
        byte iTIUNo = luaL_checkinteger(L,4);
        luaL_argcheck(L, iConsistNo >= ENGINEMIN && iConsistNo <= LASHUPMAX, 3, "Engine/Lashup Number out of range");
        luaL_argcheck(L, iTIUNo >= 1 && iTIUNo <= TIUMAX, 4, "TIU Number out of range");
        bretv = ptrPC_local->ptrPC_Thread->Setting(iSet, when, iConsistNo, iTIUNo);
        if (!bretv) {
        	lua_pushnil(L);
            return 1;
            }
        }
	lua_pushboolean(L, bretv);
	return 1; //we are returning one value
}
//---------------------------------------------------------------------------
//SetVolume(byte Chan, byte Level, float when, byte iConsistNo, byte TIUNo);// time delay (when) in seconds
//
// return true on success, nil on error
//
static int luaH_SetVolume (lua_State *L) {
	bool bretv = false;
	int n = lua_gettop(L);	//n is the number of arguments, use if needed
    if (n != 5) {
    	HARDERROR(ptrPC_local->PCMessages, Format((String)"Wrong # of arguments in call to SetVolme() - got %d need 5", ARRAYOFCONST((n))), (TColor) clRed);		// Display in Message List
	    lua_pushnil(L);
   		return 1; //we are returning one value
    	} else {
        byte iChan = luaL_checkinteger(L,1);
        byte iLevel = luaL_checkinteger(L,2);
        float when = luaL_checknumber(L,3);
        byte iConsistNo = luaL_checkinteger(L,4);
        byte iTIUNo = luaL_checkinteger(L,5);
        luaL_argcheck(L, iChan <= 4, 1, "Channel Number out of range");
        luaL_argcheck(L, iLevel <= 100, 2, "Sound Level out of range");
        luaL_argcheck(L, iConsistNo >= ENGINEMIN && iConsistNo <= LASHUPMAX, 4, "Engine/Lashup Number out of range");
		luaL_argcheck(L, iTIUNo >= 1 && iTIUNo <= TIUMAX, 5, "TIU Number out of range");
		bretv = ptrPC_local->ptrPC_Thread->SetVolume(iChan, iLevel, when, iConsistNo, iTIUNo);
        if (!bretv) {
        	lua_pushnil(L);
            return 1;
            }
        }
	lua_pushboolean(L, bretv);
	return 1; //we are returning one value
}
//---------------------------------------------------------------------------
//Accessory(bool bState, int iChan, int iAIUNo, byte TIUNo, float when);		// time delay (when) in seconds
//
// changes the state of the selected Accessory. EngineNo not used but must be specified.
// return true on success, nil on error
//
static int luaH_Accessory (lua_State *L) {
	bool bretv = false;
	int n = lua_gettop(L);	//n is the number of arguments
    if (n != 5) {
	    HARDERROR(ptrPC_local->PCMessages, Format((String)"Wrong # of arguments in call to Accessory() - got %d need at least 5", ARRAYOFCONST((n))), (TColor) clRed);		// Display in Message List
	    lua_pushnil(L);
   		return 1; //we are returning one value
        } else {
	   	bool bState = lua_toboolean(L,1);
		int iChan = luaL_checkinteger(L,2);
		int iAIUNo = luaL_checkinteger(L,3);
		byte iTIUNo = luaL_checkinteger(L,4);
		float when = luaL_checknumber(L,5);
        luaL_argcheck(L, iChan >= 1 && iChan <= CHANMAX, 2, "Channel Number out of range");
        luaL_argcheck(L, iAIUNo >= 1 && iAIUNo <= AIUMAX, 3, "AIU Number out of range");
        luaL_argcheck(L, iTIUNo >= 1 && iTIUNo <= TIUMAX, 4, "TIU Number out of range");
	    bretv = ptrPC_local->ptrPC_Thread->Accessory(bState, iAIUNo, iChan, when, iTIUNo);
        if (!bretv) {
        	lua_pushnil(L);
            return 1;
            }
        }
	lua_pushboolean(L, bretv);
	return 1; //we are returning one value
}
//---------------------------------------------------------------------------
// Set AIU Channel for Accessory - ON/OFF
//
// Params:
// bState - ON or OFF
// iAIUNo - AIU number 1 to AIUMAX
// iChan - AIU channel number 1 to 10
// when - time in seconds from now to execute the command
//
//---------------------------------------------------------------------------
bool __fastcall TProg_Control_Thread::Accessory(bool bState, int iAIUNo, int iChan, float when, byte iTIUNo) {
// Send Accessory command
bool bretv = true;
// read up the current accessory status in order to load the accessory data from the .ini file
if (!ptrAIUControl[iTIUNo - 1][iAIUNo - 1]) {  // if we don't have an AIU Control window, get it created
	bool bState;
	ptrTIUCommThread->GetAIUAccState(iChan, iAIUNo, iTIUNo, bState, ptrPC_local->ptrPC_Thread->MyPCNo);
	}
const int iNum = (iChan - 1) + ((iAIUNo - 1) * 10);
if (ptrAIUControl[iTIUNo - 1][iAIUNo - 1]) {  // if we have an AIU Control window, use it
    if (bState == ON) {
        // first two parameters for Send_Delayed_DL_FIFO are only for Operations Window commands
		ptrDL_Thread->Send_Delayed_DL_FIFO(NULL, NULL, DC_ACCESSORY_ON, FACTORYRESET, iTIUNo, MyRemoteNo, when * 1000, iNum);
        } else {
        // first two parameters for Send_Delayed_DL_FIFO are only for Operations Window commands
		ptrDL_Thread->Send_Delayed_DL_FIFO(NULL, NULL, DC_ACCESSORY_OFF, FACTORYRESET, iTIUNo, MyRemoteNo, when * 1000, iNum);
        }

	} else {
	char command[CMDMAX];
	snprintf(command, sizeof command, bState ? ACC_ON : ACC_OFF, iNum);
	// schedule an accessory command
	bretv = ptrDL_Thread->Send_String_DL_FIFO(when * 1000, FACTORYRESET, command, iTIUNo, MyRemoteNo, 11, SHORT_WAIT, RECORD_OK);	// Shortened Form
	if (bState == OFF) {	// send Accessory Off two times
		bretv = ptrDL_Thread->Send_String_DL_FIFO(when * 1000, FACTORYRESET, command, iTIUNo, MyRemoteNo, 11, SHORT_WAIT, RECORD_OK);	// Shortened Form
		}
    }
return bretv;
}
//---------------------------------------------------------------------------
//Siding(bool bState, int iChan, int iAIUNo, byte TIUNo);
//
// Set siding power by activating an AIU Accessory channel and then send a Watchdog Signal
// Return true on success, nil on error
//
static int luaH_Siding (lua_State *L) {
	bool bretv = false;
	int n = lua_gettop(L);	//n is the number of arguments
    if (n != 4) {
	    HARDERROR(ptrPC_local->PCMessages, Format((String)"Wrong # of arguments in call to Siding() - got %d need 4", ARRAYOFCONST((n))), (TColor) clRed);		// Display in Message List
	    lua_pushnil(L);
   		return 1; //we are returning one value
        } else {
	   	bool bState = lua_toboolean(L,1);
		int iChan = luaL_checkinteger(L,2);
		int iAIUNo = luaL_checkinteger(L,3);
		byte iTIUNo = luaL_checkinteger(L,4);
        luaL_argcheck(L, iChan >= 1 && iChan <= CHANMAX, 2, "Channel Number out of range");
        luaL_argcheck(L, iAIUNo >= 1 && iAIUNo <= AIUMAX, 3, "AIU Number out of range");
        luaL_argcheck(L, iTIUNo >= 1 && iTIUNo <= TIUMAX, 4, "TIU Number out of range");
	    bretv = ptrPC_local->ptrPC_Thread->Siding(bState, iAIUNo, iChan, iTIUNo);
        if (!bretv) {
        	lua_pushnil(L);
            return 1;
            }
        }
	lua_pushboolean(L, bretv);
	return 1; //we are returning one value
}
//---------------------------------------------------------------------------
// Set Siding Power (AIU Channel) and send Watchdog Signal- ON/OFF
//
// Params:
// bState - ON or OFF, if ON then send Watchdog Signal
// iAIUNo - AIU number 1 to AIUMAX
// iChan - AIU channel number 1 to 10
//
// Command cannot be delayed, executes immediately
//
//---------------------------------------------------------------------------
bool __fastcall TProg_Control_Thread::Siding(bool bState, int iAIUNo, int iChan, byte iTIUNo) {
// Send Accessory command
bool bretv = true;
// read up the current accessory status in order to load the accessory data from the .ini file
if (!ptrAIUControl[iTIUNo - 1][iAIUNo - 1]) {  // if we don't have an AIU Control window, get it created
	bool bState;
	ptrTIUCommThread->GetAIUAccState(iChan, iAIUNo, iTIUNo, bState, ptrPC_local->ptrPC_Thread->MyPCNo);
	}
const int iNum = (iChan - 1) + ((iAIUNo - 1) * 10);
// It is OK to record this command as the response is not used
if (bretv && ptrAIUControl[iTIUNo - 1][iAIUNo - 1]) {  // if we have an AIU Control window, use it to toggle the state
    // first two parameters for Send_Delayed_DL_FIFO are only for Operations Window commands
    ptrDL_Thread->Send_Delayed_DL_FIFO(NULL, NULL, bState == ON ? DC_ACCESSORY_ON : DC_ACCESSORY_OFF, FACTORYRESET, iTIUNo, MyRemoteNo, 0, iNum);
    } else {
    char command[CMDMAX];
    snprintf(command, sizeof command, bState ? ACC_ON : ACC_OFF, iNum); // eg  "Ae09" or "Af09"
    int iSidingCount;
    byte SidingbyteResp[CMDMAX]; 	// Array for decoded binary response
    bretv = ptrTIUCommThread->Send_String_FIFO(FACTORYRESET, command, iTIUNo, MyRemoteNo, 11, LONG_WAIT, RECORD_OK, NULL, NULL, &iSidingCount, SidingbyteResp);
    }

// Send an "I0" command here - used to keep newly powered on engines in Command Mode
if (bretv && bState == ON) {   // Watchdog only if siding is being turned on
    //ptrPC_local->ptrPC_Thread->
    PCSleepinSeconds(L, WATCHDOGWAIT/1000.0);	// by experimentation, I found that a short delay after the accessory command works best
    // this sends an "I0" and updates the engine list and active engine counts
    //SyncTIUNo = iTIUNo;
    Synchronize(FindEngines);
    }

return bretv;
}
//--------------------------------------------------------------------------
// Watchdog(int TIUNo)
//
// Send a Watchdog Signal to the tracks powered by the given TIUNo
// Return true on success, nil on error
//
static int luaH_Watchdog (lua_State *L) {
	bool bretv = true;
	int n = lua_gettop(L);	//n is the number of arguments
    if (n != 1) {
	    HARDERROR(ptrPC_local->PCMessages, Format((String)"Wrong # of arguments in call to Watchdog() - got %d need 1", ARRAYOFCONST((n))), (TColor) clRed);		// Display in Message List
	    lua_pushnil(L);
   		return 1; //we are returning one value
        } else {
	   	int iTIUNo = luaL_checkinteger(L,1);
        luaL_argcheck(L, iTIUNo >= 1 && iTIUNo <= TIUMAX, 1, "TIU Number out of range");
        // Send "I0" command to act as a Watchdog
        int iTIUCount;
        byte REbyteResp[CMDMAX]; 	// Array for decoded binary response
        bretv = ptrTIUCommThread->Send_String_FIFO(FACTORYRESET, "I0", iTIUNo, ptrPC_local->ptrPC_Thread->MyRemoteNo, 39, I_WAIT, NO_RECORD, NULL, NULL, &iTIUCount, REbyteResp);
        if (bretv && (iTIUCount == 39 || iTIUCount == 50)) {    // TIU responds with 39 bytes, WTIU responds with 50 bytes
            // success
            bretv = true;
            } else {
            // failure
    	    lua_pushnil(L);
   	    	return 1; //we are returning one value
            }
        }
	lua_pushboolean(L, bretv);
	return 1; //we are returning one value
}
//---------------------------------------------------------------------------
// Count Active Engines - counts the number of active and inactive engines
//
// Called using Synchronize()
//
// Params:
// none
//
//---------------------------------------------------------------------------
void __fastcall TProg_Control_Thread::CountActiveEngines(void) {
// Update Active/Inactive Engine Counts
MainForm->CountActiveEngines();
}
//---------------------------------------------------------------------------
// FindEngines() - Send watchdog signal then update the engine list and active engine counts.
//
// Called using Synchronize()
//
// Params:  none
//
//
//---------------------------------------------------------------------------
void __fastcall TProg_Control_Thread::FindEngines(void) {
// for each active TIU, this sends an "I0" and updates the engine list and active engine counts
MainForm->FindEngines();
}
//--------------------------------------------------------------------------
// TIUActive(int TIUNo)
//
// Returns true if the given TIU is active (ie, selected on the "Select Active TIU" window, otherwise returns false
// Returns nil on error
//
static int luaH_TIUActive (lua_State *L) {
	bool bretv = true;
	int n = lua_gettop(L);	//n is the number of arguments
    if (n != 1) {
	    HARDERROR(ptrPC_local->PCMessages, Format((String)"Wrong # of arguments in call to TIUActive() - got %d need 1", ARRAYOFCONST((n))), (TColor) clRed);		// Display in Message List
	    lua_pushnil(L);
   		return 1; //we are returning one value
        } else {
	   	int iTIUNo = luaL_checkinteger(L,1);
        luaL_argcheck(L, iTIUNo >= 1 && iTIUNo <= TIUMAX, 1, "TIU Number out of range");
        bretv = TIUSelection[iTIUNo - 1];
        }
	lua_pushboolean(L, bretv);
	return 1; //we are returning one value
}
//---------------------------------------------------------------------------
//VirtualAccessory(bool bState, int iChan, int iAIUNo, byte TIUNo);
//
// changes the state of the selected VirtualAccessory. Does not send any commands to the AIU
// return true on success, nil on error
//
static int luaH_VirtualAccessory (lua_State *L) {
	bool bretv = false;
	int n = lua_gettop(L);	//n is the number of arguments
    if (n != 4) {
	    HARDERROR(ptrPC_local->PCMessages, Format((String)"Wrong # of arguments in call to VirtualAccessory() - got %d need 4", ARRAYOFCONST((n))), (TColor) clRed);		// Display in Message List
	    lua_pushnil(L);
   		return 1; //we are returning one value
        } else {
	   	bool bState = lua_toboolean(L,1);
		int iChan = luaL_checkinteger(L,2);
		int iAIUNo = luaL_checkinteger(L,3);
		byte iTIUNo = luaL_checkinteger(L,4);
        luaL_argcheck(L, iAIUNo >= 1 && iAIUNo <= AIUMAX, 2, "AIU Number out of range");
        luaL_argcheck(L, iChan >= 1 && iChan <= CHANMAX, 3, "Channel Number out of range");
        luaL_argcheck(L, iTIUNo >= 1 && iTIUNo <= TIUMAX, 4, "TIU Number out of range");
        // if the AIU window is not open, then open it then set the requested value as a boolean value
		(void) ptrTIUCommThread->SetVirtualAIUAccState(bState, iChan, iAIUNo, iTIUNo, ptrPC_local->ptrPC_Thread->MyPCNo);
		//
        }
	lua_pushboolean(L, bretv);
	return 1; //we are returning one value
}
//---------------------------------------------------------------------------
//Switch(bool bState, int iChan, int iAIUNo, int TIUNo, float when);// time delay (when) in seconds
//
// changes the state of the selected Switch. EngineNo not used but must be specified.
// return true on success, nil on error
//
static int luaH_Switch (lua_State *L) {
	bool bretv = false;
	int n = lua_gettop(L);	//n is the number of arguments
    if (n != 5) {
	    HARDERROR(ptrPC_local->PCMessages, Format((String)"Wrong # of arguments in call to Switch() - got %d need 5", ARRAYOFCONST((n))), (TColor) clRed);		// Display in Message List
	    lua_pushnil(L);
   		return 1; //we are returning one value
        } else {
	   	bool bState = lua_toboolean(L,1);
		int iChan = luaL_checkinteger(L,2);
		int iAIUNo = luaL_checkinteger(L,3);
		byte iTIUNo = luaL_checkinteger(L,4);
		float when = luaL_checknumber(L,5);
        luaL_argcheck(L, bState == NORMAL || bState == REVERSE, 1, "Switch State not valid");
        luaL_argcheck(L, iChan >= 1 && iChan <= CHANMAX, 2, "Channel Number out of range");
        luaL_argcheck(L, iAIUNo >= 1 && iAIUNo <= AIUMAX, 3, "AIU Number out of range");
        luaL_argcheck(L, iTIUNo >= 1 && iTIUNo <= TIUMAX, 4, "TIU Number out of range");
        // if Safety Mode is on, the Switch() function will toggle the switch twice
	    bretv = ptrPC_local->ptrPC_Thread->Switch(bState, iAIUNo, iChan, when, iTIUNo);
        if (!bretv) {
        	lua_pushnil(L);
            return 1;
            }
        }
	lua_pushboolean(L, bretv);
	return 1; //we are returning one value
}
//---------------------------------------------------------------------------
// Set AIU Channel for a Switch - NORMAL/REVERSE
//
// Params:
// bState - NORMAL or REVERSE
// iAIUNo - AIU number 1 to AIUMAX
// iChan - AIU channel number 1 to 10
// when - time in seconds from now to execute the command
//
//---------------------------------------------------------------------------
bool __fastcall TProg_Control_Thread::Switch(bool bState, int iAIUNo, int iChan, float when, byte iTIUNo) {
// Send Switch command
bool bretv = true;
// read up the current switch status in order to load the switch data from the .ini file
if (!ptrAIUControl[iTIUNo - 1][iAIUNo - 1]) {  // if we don't have an AIU Control window, get it created
	bool bState;
	ptrTIUCommThread->GetAIUSwitchState(iChan, iAIUNo, iTIUNo, bState, ptrPC_local->ptrPC_Thread->MyPCNo);
	}
const int iNum = (iChan - 1) + ((iAIUNo - 1) * CHANMAX);
if (ptrAIUControl[iTIUNo - 1][iAIUNo - 1]) {  // if we have an AIU Control window, use it
    if (bState == STRAIGHT) {
		ptrDL_Thread->Send_Delayed_DL_FIFO(NULL, NULL, DC_SWITCH_NORMAL, FACTORYRESET, iTIUNo, MyRemoteNo, when * 1000, iNum);
        if (Setup->Safety_Mode->Checked) {	// in Safety Mode, toggle the switch twice
			ptrDL_Thread->Send_Delayed_DL_FIFO(NULL, NULL, DC_SWITCH_REVERSE, FACTORYRESET, iTIUNo, MyRemoteNo, (when+1) * 1000, iNum);
			ptrDL_Thread->Send_Delayed_DL_FIFO(NULL, NULL, DC_SWITCH_NORMAL, FACTORYRESET, iTIUNo, MyRemoteNo, (when+2) * 1000, iNum);
           	}
        } else {
		ptrDL_Thread->Send_Delayed_DL_FIFO(NULL, NULL, DC_SWITCH_REVERSE, FACTORYRESET, iTIUNo, MyRemoteNo, when * 1000, iNum);
        if (Setup->Safety_Mode->Checked) {	// in Safety Mode, toggle the switch twice
			ptrDL_Thread->Send_Delayed_DL_FIFO(NULL, NULL, DC_SWITCH_NORMAL, FACTORYRESET, iTIUNo, MyRemoteNo, (when+1) * 1000, iNum);
			ptrDL_Thread->Send_Delayed_DL_FIFO(NULL, NULL, DC_SWITCH_REVERSE, FACTORYRESET, iTIUNo, MyRemoteNo, (when+2) * 1000, iNum);
           	}
        }
	} else {
	char command[CMDMAX];
	snprintf(command, sizeof command, bState ? SWITCH_NORMAL : SWITCH_REVERSE, iNum);
	// schedule a switch command
	bretv = ptrDL_Thread->Send_String_DL_FIFO(when * 1000, FACTORYRESET, command, iTIUNo, MyRemoteNo, 11, AIU_WAIT, RECORD_OK);	// Shortened Form
    if (bretv && Setup->Safety_Mode->Checked) {	// in Safety Mode, toggle the switch twice
		char command2[CMDMAX];
		snprintf(command2, sizeof command2, (!bState) ? SWITCH_NORMAL : SWITCH_REVERSE, iNum);
		// schedule a switch command
		bretv |= ptrDL_Thread->Send_String_DL_FIFO((when+1) * 1000, FACTORYRESET, command2, iTIUNo, MyRemoteNo, 11, AIU_WAIT, RECORD_OK);	// Shortened Form
		bretv |= ptrDL_Thread->Send_String_DL_FIFO((when+2) * 1000, FACTORYRESET, command, iTIUNo, MyRemoteNo, 11, AIU_WAIT, RECORD_OK);	// Shortened Form
    	}
    }
return bretv;
}
//---------------------------------------------------------------------------
//Whistle(bool bState, float when, byte iConsistNo, byte TIUNo); // time delay (when) in seconds
//
// return true on success, nil on eeror
//
static int luaH_Whistle (lua_State *L) {
	bool bretv = false;
	int n = lua_gettop(L);	//n is the number of arguments, use if needed
    if (n != 4) {
    	HARDERROR(ptrPC_local->PCMessages, Format((String)"Wrong # of arguments in call to Whistle() - got %d need 4", ARRAYOFCONST((n))), (TColor) clRed);		// Display in Message List
	    lua_pushnil(L);
   		return 1; //we are returning one value
		} else {
        bool bState = lua_toboolean(L,1);
        float when = luaL_checknumber(L,2);
        byte iConsistNo = luaL_checkinteger(L,3);
        byte iTIUNo = luaL_checkinteger(L,4);
        luaL_argcheck(L, iConsistNo >= ENGINEMIN && iConsistNo <= LASHUPMAX, 3, "Engine/Lashup Number out of range");
        luaL_argcheck(L, iTIUNo >= 1 && iTIUNo <= TIUMAX, 4, "TIU Number out of range");
        bretv = ptrPC_local->ptrPC_Thread->Whistle(bState, when, iConsistNo, iTIUNo);
        if (!bretv) {
        	lua_pushnil(L);
            return 1;
            }
        }
	lua_pushboolean(L, bretv);
	return 1; //we are returning one value
}
//---------------------------------------------------------------------------
//Bell(bool bState, float when, byte iConsistNo, byte TIUNo);// time delay (when) in seconds
//
// return true on success, nil on error
//
static int luaH_Bell (lua_State *L) {
	bool bretv = false;
	int n = lua_gettop(L);	//n is the number of arguments, use if needed
    if (n != 4) {
    	HARDERROR(ptrPC_local->PCMessages, Format((String)"Wrong # of arguments in call to Bell() - got %d need 4", ARRAYOFCONST((n))), (TColor) clRed);		// Display in Message List
	    lua_pushnil(L);
   		return 1; //we are returning one value
		} else {
        bool bState = lua_toboolean(L,1);
        float when = luaL_checknumber(L,2);
        byte iConsistNo = luaL_checkinteger(L,3);
        byte iTIUNo = luaL_checkinteger(L,4);
        luaL_argcheck(L, iConsistNo >= ENGINEMIN && iConsistNo <= LASHUPMAX, 3, "Engine/Lashup Number out of range");
        luaL_argcheck(L, iTIUNo >= 1 && iTIUNo <= TIUMAX, 4, "TIU Number out of range");
        bretv = ptrPC_local->ptrPC_Thread->Bell(bState, when, iConsistNo, iTIUNo);
        if (!bretv) {
        	lua_pushnil(L);
            return 1;
            }
        }
	lua_pushboolean(L, bretv);
	return 1; //we are returning one value
}
//---------------------------------------------------------------------------
//Markers(bool bState, float when, byte iConsistNo, byte TIUNo);// time delay (when) in seconds
//
// return true on success, nil on error
//
static int luaH_Markers (lua_State *L) {
	bool bretv = false;
	int n = lua_gettop(L);	//n is the number of arguments, use if needed
    if (n != 4) {
    	HARDERROR(ptrPC_local->PCMessages, Format((String)"Wrong # of arguments in call to Markers() - got %d need 4", ARRAYOFCONST((n))), (TColor) clRed);		// Display in Message List
	    lua_pushnil(L);
   		return 1; //we are returning one value
		} else {
        bool bState = lua_toboolean(L,1);
        float when = luaL_checknumber(L,2);
        byte iConsistNo = luaL_checkinteger(L,3);
        byte iTIUNo = luaL_checkinteger(L,4);
        luaL_argcheck(L, iConsistNo >= ENGINEMIN && iConsistNo <= LASHUPMAX, 3, "Engine/Lashup Number out of range");
        luaL_argcheck(L, iTIUNo >= 1 && iTIUNo <= TIUMAX, 4, "TIU Number out of range");
        bretv = ptrPC_local->ptrPC_Thread->Markers(bState, when, iConsistNo, iTIUNo);
        if (!bretv) {
        	lua_pushnil(L);
            return 1;
            }
        }
	lua_pushboolean(L, bretv);
	return 1; //we are returning one value
}
//---------------------------------------------------------------------------
//Beacon(bool bState, float when, byte iConsistNo, byte TIUNo);// time delay (when) in seconds
//
// return true on success, nil on error
//
static int luaH_Beacon (lua_State *L) {
	bool bretv = false;
	int n = lua_gettop(L);	//n is the number of arguments, use if needed
    if (n != 4) {
    	HARDERROR(ptrPC_local->PCMessages, Format((String)"Wrong # of arguments in call to Beacon() - got %d need 4", ARRAYOFCONST((n))), (TColor) clRed);		// Display in Message List
	    lua_pushnil(L);
   		return 1; //we are returning one value
		} else {
        bool bState = lua_toboolean(L,1);
        float when = luaL_checknumber(L,2);
        byte iConsistNo = luaL_checkinteger(L,3);
        byte iTIUNo = luaL_checkinteger(L,4);
        luaL_argcheck(L, iConsistNo >= ENGINEMIN && iConsistNo <= LASHUPMAX, 3, "Engine/Lashup Number out of range");
        luaL_argcheck(L, iTIUNo >= 1 && iTIUNo <= TIUMAX, 4, "TIU Number out of range");
        bretv = ptrPC_local->ptrPC_Thread->Beacon(bState, when, iConsistNo, iTIUNo);
        if (!bretv) {
        	lua_pushnil(L);
            return 1;
            }
        }
	lua_pushboolean(L, bretv);
	return 1; //we are returning one value
}
//---------------------------------------------------------------------------
//Smoke(bool bState, float when, byte iConsistNo, byte TIUNo);// time delay (when) in seconds
//
// return true on success, nil on error
//
static int luaH_Smoke (lua_State *L) {
	bool bretv = false;
	int n = lua_gettop(L);	//n is the number of arguments, use if needed
    if (n != 4) {
    	HARDERROR(ptrPC_local->PCMessages, Format((String)"Wrong # of arguments in call to Smoke() - got %d need 4", ARRAYOFCONST((n))), (TColor) clRed);		// Display in Message List
	    lua_pushnil(L);
   		return 1; //we are returning one value
		} else {
        bool bState = lua_toboolean(L,1);
        float when = luaL_checknumber(L,2);
        byte iConsistNo = luaL_checkinteger(L,3);
        byte iTIUNo = luaL_checkinteger(L,4);
        luaL_argcheck(L, iConsistNo >= ENGINEMIN && iConsistNo <= LASHUPMAX, 3, "Engine/Lashup Number out of range");
        luaL_argcheck(L, iTIUNo >= 1 && iTIUNo <= TIUMAX, 4, "TIU Number out of range");
        bretv = ptrPC_local->ptrPC_Thread->Smoke(bState,  when, iConsistNo, iTIUNo);
        if (!bretv) {
        	lua_pushnil(L);
            return 1;
            }		}
	lua_pushboolean(L, bretv);
	return 1; //we are returning one value
}
//---------------------------------------------------------------------------
//ChuffRate(int Rate, float when, byte iConsistNo, byte TIUNo);// time delay (when) in seconds
//
// return true on success, nil on error
//
static int luaH_ChuffRate (lua_State *L) {
	bool bretv = false;
	int n = lua_gettop(L);	//n is the number of arguments, use if needed
    if (n != 4) {
    	HARDERROR(ptrPC_local->PCMessages, Format((String)"Wrong # of arguments in call to ChuffRate() - got %d need 4", ARRAYOFCONST((n))), (TColor) clRed);		// Display in Message List
	    lua_pushnil(L);
   		return 1; //we are returning one value
		} else {
        int Rate = luaL_checkinteger(L,1);
        if (Rate == 2 || Rate == 4) {	// only valid values are 2 and 4
            float when = luaL_checknumber(L,2);
            byte iConsistNo = luaL_checkinteger(L,3);
            byte iTIUNo = luaL_checkinteger(L,4);
	        luaL_argcheck(L, iConsistNo >= ENGINEMIN && iConsistNo <= LASHUPMAX, 3, "Engine/Lashup Number out of range");
    	    luaL_argcheck(L, iTIUNo >= 1 && iTIUNo <= TIUMAX, 4, "TIU Number out of range");
            bretv = ptrPC_local->ptrPC_Thread->ChuffRate(Rate, when, iConsistNo, iTIUNo);
	        if (!bretv) {
    	    	lua_pushnil(L);
        	    return 1;
            	}
            } else {
            HARDERROR(ptrPC_local->PCMessages, "Argument to ChuffRate() can only be 2 or 4", (TColor) clRed);		// Display in Message List
		    lua_pushnil(L);
   			return 1; //we are returning one value
            }
        }
	lua_pushboolean(L, bretv);
	return 1; //we are returning one value
}
//---------------------------------------------------------------------------
//Rate(byte iAcc, byte iDec, float when, byte iConsistNo, byte TIUNo);// time delay (when) in seconds
//
// return true on success, nil on error
//
static int luaH_Rate (lua_State *L) {
	bool bretv = false;
	int n = lua_gettop(L);	//n is the number of arguments, use if needed
    if (n != 5) {
    	HARDERROR(ptrPC_local->PCMessages, Format((String)"Wrong # of arguments in call to Rate() - got %d need 5", ARRAYOFCONST((n))), (TColor) clRed);		// Display in Message List
	    lua_pushnil(L);
   		return 1; //we are returning one value
		} else {
		byte iAcc = luaL_checkinteger(L,1);	// always positive
		byte iDec = luaL_checkinteger(L,2); // always positive
		float when = luaL_checknumber(L,3);
		byte iConsistNo = luaL_checkinteger(L,4);
        byte iTIUNo = luaL_checkinteger(L,5);
        // zero rate for either value prevents extra commands sent to engine
        // zero rate means no change from current value
		luaL_argcheck(L, iAcc <= 25, 1, "Acceleration out of range");
        luaL_argcheck(L, iDec <= 25, 2, "Decceleration out of range");
        luaL_argcheck(L, iConsistNo >= ENGINEMIN && iConsistNo <= LASHUPMAX, 4, "Consist Number out of range");
        luaL_argcheck(L, iTIUNo >= 1 && iTIUNo <= TIUMAX, 5, "TIU Number out of range");
        bretv = ptrPC_local->ptrPC_Thread->Rate(iAcc, iDec, when, iConsistNo, iTIUNo);
        if (!bretv) {
        	lua_pushnil(L);
            return 1;
            }
        }
	lua_pushboolean(L, bretv);
	return 1; //we are returning one value
}
//---------------------------------------------------------------------------
//GetRate(byte iConsistNo, byte TIUNo);
//
// return two values, AccRate and DecRate on success
// returns nil,nil on failure
//
static int luaH_GetRate (lua_State *L) {
	bool bretv = false;
	byte iAcc, iDec;
	int n = lua_gettop(L);	//n is the number of arguments, use if needed
	if (n != 2) {
		HARDERROR(ptrPC_local->PCMessages, Format((String)"Wrong # of arguments in call to GetRate() - got %d need 2", ARRAYOFCONST((n))), (TColor) clRed);		// Display in Message List
	    lua_pushnil(L);
   		return 1; //we are returning one value
		} else {
		byte iEngineNo = luaL_checkinteger(L,1);
        byte iTIUNo = luaL_checkinteger(L,2);
        luaL_argcheck(L, iEngineNo >= ENGINEMIN && iEngineNo <= ENGINEMAX,1, "Engine Number out of range");
        luaL_argcheck(L, iTIUNo >= 1 && iTIUNo <= TIUMAX, 2, "TIU Number out of range");
        bretv = ptrPC_local->ptrPC_Thread->GetRate(&iAcc, &iDec, iEngineNo, iTIUNo);
        if (bretv) {
			lua_pushinteger(L, iAcc);
			lua_pushinteger(L, iDec);
			return 2; //we are returning two values
        	}
        }
	lua_pushnil(L);
	lua_pushnil(L);
	return 2; //we are returning two values
}
//---------------------------------------------------------------------------
//OpenCoupler(bool bCoupler, float when, byte iConsistNo, byte TIUNo);// time delay (when) in seconds
//
// return true on success, nil on error
//
static int luaH_OpenCoupler (lua_State *L) {
	bool bretv = false;
	int n = lua_gettop(L);	//n is the number of arguments, use if needed
    if (n != 4) {
    	HARDERROR(ptrPC_local->PCMessages, Format((String)"Wrong # of arguments in call to OpenCoupler() - got %d need 4", ARRAYOFCONST((n))), (TColor) clRed);		// Display in Message List
	    lua_pushnil(L);
   		return 1; //we are returning one value
    	} else {
        bool bCoupler = lua_toboolean(L,1);
        float when = luaL_checknumber(L,2);
        byte iConsistNo = luaL_checkinteger(L,3);
        byte iTIUNo = luaL_checkinteger(L,4);
        luaL_argcheck(L, iConsistNo >= ENGINEMIN && iConsistNo <= LASHUPMAX, 3, "Engine/Lashup Number out of range");
        luaL_argcheck(L, iTIUNo >= 1 && iTIUNo <= TIUMAX, 4, "TIU Number out of range");
        bretv = ptrPC_local->ptrPC_Thread->OpenCoupler(bCoupler, when, iConsistNo, iTIUNo);
        if (!bretv) {
        	lua_pushnil(L);
            return 1;
            }
        }
	lua_pushboolean(L, bretv);
	return 1; //we are returning one value
}
//---------------------------------------------------------------------------
//SetDirection(bool bDIR, float when, byte iConsistNo, byte TIUNo); // time delay (when) in seconds
//
// return true on success, nil on error
//
static int luaH_SetDirection (lua_State *L) {
	bool bretv = false;
	int n = lua_gettop(L);	//n is the number of arguments, use if needed
    if (n != 4) {
		HARDERROR(ptrPC_local->PCMessages, Format((String)"Wrong # of arguments in call to SetDirection() - got %d need 4", ARRAYOFCONST((n))), (TColor) clRed);		// Display in Message List
	    lua_pushnil(L);
   		return 1; //we are returning one value
		} else {
        bool bDIR = lua_toboolean(L,1);
        float when = luaL_checknumber(L,2);
        byte iConsistNo = luaL_checkinteger(L,3);
        byte iTIUNo = luaL_checkinteger(L,4);
        luaL_argcheck(L, iConsistNo >= ENGINEMIN && iConsistNo <= LASHUPMAX, 3, "Engine/Lashup Number out of range");
        luaL_argcheck(L, iTIUNo >= 1 && iTIUNo <= TIUMAX, 4, "TIU Number out of range");
        bretv = ptrPC_local->ptrPC_Thread->SetDirection(bDIR, when, iConsistNo, iTIUNo);
        if (!bretv) {
        	lua_pushnil(L);
            return 1;
            }
        }
	lua_pushboolean(L, bretv);
	return 1; //we are returning one value
}
//---------------------------------------------------------------------------
//Throttle(int iMPH, float when, byte iConsistNo, byte TIUNo);// time delay (when) in seconds
//
// return iMPH as an integer on success, nil on error
//
static int luaH_Throttle (lua_State *L) {
	bool bretv = false;
    int iMPH = 0;
	int n = lua_gettop(L);	//n is the number of arguments, use if needed
    if (n != 4) {
    	HARDERROR(ptrPC_local->PCMessages, Format((String)"Wrong # of arguments in call to Throttle() - got %d need 4", ARRAYOFCONST((n))), (TColor) clRed);		// Display in Message List
	    lua_pushnil(L);
   		return 1; //we are returning one value
		} else {
        iMPH = luaL_checkinteger(L,1);
        float when = luaL_checknumber(L,2);
        byte iConsistNo = luaL_checkinteger(L,3);
        byte iTIUNo = luaL_checkinteger(L,4);
        luaL_argcheck(L, iConsistNo >= ENGINEMIN && iConsistNo <= LASHUPMAX, 3, "Engine/Lashup Number out of range");
        luaL_argcheck(L, iTIUNo >= 1 && iTIUNo <= TIUMAX, 4, "TIU Number out of range");
        bretv = ptrPC_local->ptrPC_Thread->Throttle(iMPH, when, iConsistNo, iTIUNo);
        if (!bretv) {
        	lua_pushnil(L);
            return 1;
            }
        }
	lua_pushinteger(L, iMPH);
	return 1; //we are returning one value
}
//---------------------------------------------------------------------------
//Noise(int NoiseNo)
//
// Play the sound in the Remote_Control.rc file.
// NoiseNo is given in defines.lua and Train_Control.h
//
// Any playing sounds are stopped to play this sound.
//
// If PlaySound() cannot find the specified sound, Noise uses the default system event
// sound entry instead. If the function can find neither the system default
// entry nor the default sound, it makes no sound and returns nil.
//
// return true on success, nil on error
//
static int luaH_Noise (lua_State *L) {
	bool bretv = false;
	int n = lua_gettop(L);	//n is the number of arguments, use if needed
	if (n != 1) {
		HARDERROR(ptrPC_local->PCMessages, Format((String)"Wrong # of arguments in call to Noise() - got %d need 1", ARRAYOFCONST((n))), (TColor) clRed);		// Display in Message List
	    lua_pushnil(L);
   		return 1; //we are returning one value
		} else {
        int NoiseNo = luaL_checkinteger(L,1);
        bretv = PlaySound(MAKEINTRESOURCE(NoiseNo), HInstance, SND_RESOURCE | SND_ASYNC | SND_NODEFAULT /* | SND_NOSTOP */);
        }
	lua_pushboolean(L, bretv);
	return 1; //we are returning one value
}
//---------------------------------------------------------------------------
//PlaySound(int iSoundNo, float when, byte iConsistNo, byte TIUNo);// time delay (when) in seconds
//
// return true on success, nil on error
//
static int luaH_PlaySound (lua_State *L) {
	bool bretv = false;
	int n = lua_gettop(L);	//n is the number of arguments, use if needed
	if (n != 4) {
		HARDERROR(ptrPC_local->PCMessages, Format((String)"Wrong # of arguments in call to PlaySound() - got %d need 4", ARRAYOFCONST((n))), (TColor) clRed);		// Display in Message List
	    lua_pushnil(L);
   		return 1; //we are returning one value
		} else {
        int iSoundNo = luaL_checkinteger(L,1);
        float when = luaL_checknumber(L,2);
        byte iConsistNo = luaL_checkinteger(L,3);
        byte iTIUNo = luaL_checkinteger(L,4);
        luaL_argcheck(L, iConsistNo >= ENGINEMIN && iConsistNo <= LASHUPMAX, 3, "Engine/Lashup Number out of range");
        luaL_argcheck(L, iTIUNo >= 1 && iTIUNo <= TIUMAX, 4, "TIU Number out of range");
        bretv = ptrPC_local->ptrPC_Thread->MyPlaySound(iSoundNo, when, iConsistNo, iTIUNo);
        if (!bretv) {
        	lua_pushnil(L);
            return 1;
            }
        }
	lua_pushboolean(L, bretv);
	return 1; //we are returning one value
}
//---------------------------------------------------------------------------
//SetSpeed(int iMPH, float when, byte iConsistNo, byte TIUNo);// time delay (when) in seconds
//
// This is an older version of SetSpeed() that requires a TIUNo. Newer versions
// of RTC associate engines with a TIU number when RTC starts up. Use the newer SetSpeedEx() function for this.
// But there are still many other commands that require TIUNo. Use GetTIUNo(EngNo) to get the
// correct TIU number. Example:
//        MyEngineNo = Engine;
//        MyTIUNo = GetTIUNo(MyEngineNo);
//        LastSentSpeed = SetSpeed(BlockSpeedLimit, 0, MyEngineNo, MyTIUNo);
// return iMPH as an integer on success, nil on error
//
static int luaH_SetSpeed (lua_State *L) {
	bool bretv = false;
    int iMPH = 0;
	int n = lua_gettop(L);	//n is the number of arguments, use if needed
    if (n != 4) {
    	HARDERROR(ptrPC_local->PCMessages, Format((String)"Wrong # of arguments in call to SetSpeed() - got %d need 4", ARRAYOFCONST((n))), (TColor) clRed);		// Display in Message List
	    lua_pushnil(L);
   		return 1; //we are returning one value
		} else {
        iMPH = luaL_checkinteger(L,1);
        float when = luaL_checknumber(L,2);
        byte iConsistNo = luaL_checkinteger(L,3);
        byte iTIUNo = luaL_checkinteger(L,4);
        luaL_argcheck(L, iConsistNo >= ENGINEMIN && iConsistNo <= LASHUPMAX, 3, "Engine/Lashup Number out of range");
        luaL_argcheck(L, iTIUNo >= 1 && iTIUNo <= TIUMAX, 4, "TIU Number out of range");
        bretv = ptrPC_local->ptrPC_Thread->SetSpeed(iMPH, when, iConsistNo, iTIUNo);
        if (!bretv) {
        	lua_pushnil(L);
            return 1;
            }
        }
	lua_pushinteger(L, iMPH);
	return 1; //we are returning one value
}
//---------------------------------------------------------------------------
//SetSpeedEx(int iMPH, float when, byte iConsistNo, byte TIUNo);// time delay (when) in seconds
//
// TIUNo is not needed here and should not be given - if not given the TIU_NUMBER value from EngineData is used .
// TIUNo is maintained for backward compatibility.
//
// In addition, there are many, many other commands that, right now, require the MyTIUNo parameter.
// If we change SetSpeed() to SetSpeedEx(), we really need to change all of the other commands
// make the TIU number parameter optional
//
// return iMPH as an integer on success, nil on error
//
static int luaH_SetSpeedEx (lua_State *L) {
	bool bretv = false;
    int iMPH = 0;
	int n = lua_gettop(L);	//n is the number of arguments, use if needed
    if (n < 3) {
    	HARDERROR(ptrPC_local->PCMessages, Format((String)"Wrong # of arguments in call to SetSpeedEx() - got %d need 3 or 4", ARRAYOFCONST((n))), (TColor) clRed);		// Display in Message List
	    lua_pushnil(L);
   		return 1; //we are returning one value
		} else {
        iMPH = luaL_checkinteger(L,1);
        luaL_argcheck(L, iMPH >= 0 && iMPH <= 120, 1, "Speed out of range");
        float when = luaL_checknumber(L,2);
        byte iConsistNo = luaL_checkinteger(L,3);
        luaL_argcheck(L, iConsistNo >= ENGINEMIN && iConsistNo <= LASHUPMAX, 3, "Engine/Lashup Number out of range");
        int iTIUNo;
        if (n == 3) {
            // no fourth parameter so get the TIU Number in another way
            if (iConsistNo >= LASHUPMIN) {
                // TIUNo of head engine of lashup
                int iHeadEngine;
                int retv = sscanf(&ptrLUEngineList[iConsistNo][1], "%2X", &iHeadEngine);   // V5 and higher - Remote and WTIU engine list is in ASCII
                if (retv != 1) {
                    lua_pushnil(L);	// extraction failed
               		return 1; //we are returning one value
                    }
                iHeadEngine = (iHeadEngine & 0x7F) - 1;	    // convert from DCS Engine Number to Engine Number
                ptrTIUCommThread->GetTIU(iHeadEngine, iTIUNo);
                } else {
                // TIUNo of engine
                ptrTIUCommThread->GetTIU(iConsistNo, iTIUNo);
                }
            } else {
            // Fourth parameter given so use it
            iTIUNo = luaL_checkinteger(L,4);
            luaL_argcheck(L, iTIUNo >= 1 && iTIUNo <= TIUMAX, 4, "TIU Number out of range");
            }
        bretv = ptrPC_local->ptrPC_Thread->SetSpeed(iMPH, when, iConsistNo, iTIUNo);
        if (!bretv) {
        	lua_pushnil(L);
            return 1;
            }
        }
	lua_pushinteger(L, iMPH);
	return 1; //we are returning one value
}
//---------------------------------------------------------------------------
//SetQuill(int iTone, float when, byte iConsistNo, byte TIUNo);// time delay (when) in seconds
//
// return true on success, nil on error
//
static int luaH_SetQuill (lua_State *L) {
	bool bretv = false;
	int n = lua_gettop(L);	//n is the number of arguments, use if needed
	if (n != 4) {
    	HARDERROR(ptrPC_local->PCMessages, Format((String)"Wrong # of arguments in call to SetQuill() - got %d need 4", ARRAYOFCONST((n))), (TColor) clRed);		// Display in Message List
	    lua_pushnil(L);
   		return 1; //we are returning one value
		} else {
        int iTone = luaL_checkinteger(L,1);
        float when = luaL_checknumber(L,2);
        byte iConsistNo = luaL_checkinteger(L,3);
        byte iTIUNo = luaL_checkinteger(L,4);
        luaL_argcheck(L, iConsistNo >= ENGINEMIN && iConsistNo <= LASHUPMAX, 3, "Engine/Lashup Number out of range");
        luaL_argcheck(L, iTIUNo >= 1 && iTIUNo <= TIUMAX, 4, "TIU Number out of range");
        bretv = ptrPC_local->ptrPC_Thread->SetQuill(iTone, when, iConsistNo, iTIUNo);
        if (!bretv) {
        	lua_pushnil(L);
            return 1;
            }
    	}
	lua_pushboolean(L, bretv);
	return 1; //we are returning one value
}
//---------------------------------------------------------------------------
//ProtoWhistle(bool bState, float when, byte iConsistNo, byte TIUNo);// time delay (when) in seconds
//
// return true on success, nil on error
//
static int luaH_ProtoWhistle(lua_State *L) {
	bool bretv = false;
	int n = lua_gettop(L);	//n is the number of arguments, use if needed
    if (n != 4) {
    	HARDERROR(ptrPC_local->PCMessages, Format((String)"Wrong # of arguments in call to ProtoWhistle() - got %d need 4", ARRAYOFCONST((n))), (TColor) clRed);		// Display in Message List
	    lua_pushnil(L);
   		return 1; //we are returning one value
		} else {
        bool bState = lua_toboolean(L,1);
        float when = luaL_checknumber(L,2);
        byte iConsistNo = luaL_checkinteger(L,3);
        byte iTIUNo = luaL_checkinteger(L,4);
        luaL_argcheck(L, iConsistNo >= ENGINEMIN && iConsistNo <= LASHUPMAX, 3, "Engine/Lashup Number out of range");
        luaL_argcheck(L, iTIUNo >= 1 && iTIUNo <= TIUMAX, 4, "TIU Number out of range");
        bretv = ptrPC_local->ptrPC_Thread->ProtoWhistle(bState, when, iConsistNo, iTIUNo);
        if (!bretv) {
        	lua_pushnil(L);
            return 1;
            }
        }
	lua_pushboolean(L, bretv);
	return 1; //we are returning one value
}
//---------------------------------------------------------------------------
//SmokeWhistle(bool bState, float when, byte iConsistNo, byte TIUNo);// time delay (when) in seconds
//
// return true on success, nil on error
//
static int luaH_SmokeWhistle (lua_State *L) {
	bool bretv = false;
	int n = lua_gettop(L);	//n is the number of arguments, use if needed
    if (n != 4) {
    	HARDERROR(ptrPC_local->PCMessages, Format((String)"Wrong # of arguments in call to SmokeWhistle() - got %d need 4", ARRAYOFCONST((n))), (TColor) clRed);		// Display in Message List
	    lua_pushnil(L);
   		return 1; //we are returning one value
    	} else {
        bool bState = lua_toboolean(L,1);
        float when = luaL_checknumber(L,2);
        byte iConsistNo = luaL_checkinteger(L,3);
        byte iTIUNo = luaL_checkinteger(L,4);
        luaL_argcheck(L, iConsistNo >= ENGINEMIN && iConsistNo <= LASHUPMAX, 3, "Engine/Lashup Number out of range");
        luaL_argcheck(L, iTIUNo >= 1 && iTIUNo <= TIUMAX, 4, "TIU Number out of range");
		bretv = ptrPC_local->ptrPC_Thread->SmokeWhistle(bState, when, iConsistNo, iTIUNo);
        if (!bretv) {
        	lua_pushnil(L);
            return 1;
            }
        }
	lua_pushboolean(L, bretv);
	return 1; //we are returning one value
}
//---------------------------------------------------------------------------
//SwingingBell(bool bState, float when, byte iConsistNo, byte TIUNo);// time delay (when) in seconds
//
// Turn the swinging bell on and off
// return true on success, nil on error
//
static int luaH_SwingingBell (lua_State *L) {
	bool bretv = false;
	int n = lua_gettop(L);	//n is the number of arguments, use if needed
    if (n != 4) {
    	HARDERROR(ptrPC_local->PCMessages, Format((String)"Wrong # of arguments in call to SwingingBell() - got %d need 4", ARRAYOFCONST((n))), (TColor) clRed);		// Display in Message List
	    lua_pushnil(L);
   		return 1; //we are returning one value
		} else {
        bool bState = lua_toboolean(L,1);
        float when = luaL_checknumber(L,2);
        byte iConsistNo = luaL_checkinteger(L,3);
        byte iTIUNo = luaL_checkinteger(L,4);
        luaL_argcheck(L, iConsistNo >= ENGINEMIN && iConsistNo <= LASHUPMAX, 3, "Engine/Lashup Number out of range");
        luaL_argcheck(L, iTIUNo >= 1 && iTIUNo <= TIUMAX, 4, "TIU Number out of range");
        bretv = ptrPC_local->ptrPC_Thread->SwingingBell(bState, when, iConsistNo, iTIUNo);
        if (!bretv) {
        	lua_pushnil(L);
            return 1;
            }
        }
	lua_pushboolean(L, bretv);
	return 1; //we are returning one value
}
//---------------------------------------------------------------------------
//IsEngineRunning(int Engine);
//
// return true if the engine has been started otherwise returns false, returns nil on error
//
static int luaH_IsEngineRunning (lua_State *L) {
	int n = lua_gettop(L);	//n is the number of arguments, use if needed
    if (n != 1) {
    	HARDERROR(ptrPC_local->PCMessages, Format((String)"Wrong # of arguments in call to IsEngineRunning() - got %d need 1", ARRAYOFCONST((n))), (TColor) clRed);		// Display in Message List
	    lua_pushnil(L);
   		return 1; //we are returning one value
		} else {
		byte iEngineNo = luaL_checkinteger(L,1);
        luaL_argcheck(L, iEngineNo >= ENGINEMIN && iEngineNo <= ENGINEMAX, 1, "Engine Number out of range");
	    lua_pushboolean(L, ptrOp[iEngineNo] != NULL);   // indexed by actual engine number
        return 1;  //we are returning one value
        }
}
//---------------------------------------------------------------------------
//IsLashUpRunning(int LashUp);
//
// return true if the lashup has been started otherwise returns false, return nil on error
//
static int luaH_IsLashUpRunning (lua_State *L) {
	int n = lua_gettop(L);	//n is the number of arguments, use if needed
    if (n != 1) {
    	HARDERROR(ptrPC_local->PCMessages, Format((String)"Wrong # of arguments in call to IsLashUpRunning() - got %d need 1", ARRAYOFCONST((n))), (TColor) clRed);		// Display in Message List
	    lua_pushnil(L);
   		return 1; //we are returning one value
		} else {
        byte iLashUpNo = luaL_checkinteger(L,1);
        luaL_argcheck(L, iLashUpNo >= LASHUPMIN && iLashUpNo <= LASHUPMAX, 1, "Lashup Number out of range");
        lua_pushboolean(L, ptrLUEngineList[iLashUpNo] != NULL);  // indexed by actual lashup number
        return 1;  //we are returning one value
        }
}
//---------------------------------------------------------------------------
//
// Bump one of the counters on the Program Control Window
//
// return true on success, nil on error
//
static int luaH_BumpCounter (lua_State *L) {
bool bretv = false;
int n = lua_gettop(L);	//n is the number of arguments, use if needed
if (n != 1) {
	HARDERROR(ptrPC_local->PCMessages, Format((String)"Wrong # of arguments in call to BumpCounter() - got %d need 1", ARRAYOFCONST((n))), (TColor) clRed);		// Display in Message List
    lua_pushnil(L);
	return 1; //we are returning one value
	} else {
    byte Cntr = luaL_checkinteger(L,1);
    switch(Cntr) {
        case COUNTER01:
            ptrTIUCommThread->Bump(ptrPC_local->Counter01);
            bretv = true;
            break;
        case COUNTER02:
            ptrTIUCommThread->Bump(ptrPC_local->Counter02);
            bretv = true;
            break;
        case COUNTER03:
            ptrTIUCommThread->Bump(ptrPC_local->Counter03);
            bretv = true;
            break;
        case COUNTER04:
            ptrTIUCommThread->Bump(ptrPC_local->Counter04);
            bretv = true;
            break;
        case COUNTER05:
            ptrTIUCommThread->Bump(ptrPC_local->Counter05);
            bretv = true;
            break;
        case COUNTER06:
            ptrTIUCommThread->Bump(ptrPC_local->Counter06);
            bretv = true;
            break;
        case TAGSUB:
            ptrTIUCommThread->Bump(DetectedRFIDTags->TagSubCount);
            bretv = true;
            break;
        case UNIQTAG:
            ptrTIUCommThread->Bump(DetectedRFIDTags->UniqueTagCount);
            bretv = true;
            break;
        case TAGCNT:
            ptrTIUCommThread->Bump(DetectedRFIDTags->TagCount);
            bretv = true;
            break;
        case ELAPSEDTIME:
            ptrTIUCommThread->Bump(ptrPC_local->ETime);
            bretv = true;
            break;
        case ENGINENO:
            ptrTIUCommThread->Bump(MainForm->EngineSpin);
            bretv = true;
            break;
        case NUMENG:
            ptrTIUCommThread->Bump(MainForm->StartedConsists);
            bretv = true;
            break;
		default:
            HARDERROR(ptrPC_local->PCMessages, Format((String)"Invalid argument (%d) in call to BumpCounter()", ARRAYOFCONST((Cntr))), (TColor) clRed);		// Display in Message List
		    lua_pushnil(L);
	   		return 1; //we are returning one value
        }
    }
lua_pushboolean(L, bretv);
return 1; //we are returning one value
}
//---------------------------------------------------------------------------
//
// Decrement one of the counters on the Program Control Window
//
// return true on success, nil on error
//
static int luaH_DecCounter (lua_State *L) {
bool bretv = false;
int n = lua_gettop(L);	//n is the number of arguments, use if needed
if (n != 1) {
	HARDERROR(ptrPC_local->PCMessages, Format((String)"Wrong # of arguments in call to DecCounter() - got %d need 1", ARRAYOFCONST((n))), (TColor) clRed);		// Display in Message List
    lua_pushnil(L);
	return 1; //we are returning one value
	} else {
    byte Cntr = luaL_checkinteger(L,1);
    switch(Cntr) {
        case COUNTER01:
            ptrTIUCommThread->Dec(ptrPC_local->Counter01);
            bretv = true;
            break;
        case COUNTER02:
            ptrTIUCommThread->Dec(ptrPC_local->Counter02);
            bretv = true;
            break;
        case COUNTER03:
            ptrTIUCommThread->Dec(ptrPC_local->Counter03);
            bretv = true;
            break;
        case COUNTER04:
            ptrTIUCommThread->Dec(ptrPC_local->Counter04);
            bretv = true;
            break;
        case COUNTER05:
            ptrTIUCommThread->Dec(ptrPC_local->Counter05);
            bretv = true;
            break;
        case COUNTER06:
            ptrTIUCommThread->Dec(ptrPC_local->Counter06);
			bretv = true;
            break;
        case TAGSUB:
            ptrTIUCommThread->Dec(DetectedRFIDTags->TagSubCount);
            bretv = true;
            break;
        case UNIQTAG:
            ptrTIUCommThread->Dec(DetectedRFIDTags->UniqueTagCount);
            bretv = true;
            break;
        case TAGCNT:
            ptrTIUCommThread->Dec(DetectedRFIDTags->TagCount);
			bretv = true;
            break;
        case ELAPSEDTIME:
            ptrTIUCommThread->Dec(ptrPC_local->ETime);
            bretv = true;
            break;
        case ENGINENO:
            ptrTIUCommThread->Dec(MainForm->EngineSpin);
            bretv = true;
            break;
        case NUMENG:
            ptrTIUCommThread->Dec(MainForm->StartedConsists);
            bretv = true;
            break;
        default:
            HARDERROR(ptrPC_local->PCMessages, Format((String)"Invalid argument (%d) in call to DecCounter()", ARRAYOFCONST((Cntr))), (TColor) clRed);		// Display in Message List
		    lua_pushnil(L);
   			return 1; //we are returning one value
        }
    }
lua_pushboolean(L, bretv);
return 1; //we are returning one value
}
//---------------------------------------------------------------------------
//
// Get one of the counters on the Program Control Window
//
// return value as a integer, nil on error
//
static int luaH_GetCounter (lua_State *L) {
int CntrVal;
int n = lua_gettop(L);	//n is the number of arguments, use if needed
if (n != 1) {
	HARDERROR(ptrPC_local->PCMessages, Format((String)"Wrong # of arguments in call to GetCounter() - got %d need 1", ARRAYOFCONST((n))), (TColor) clRed);		// Display in Message List
    lua_pushnil(L);
	return 1; //we are returning one value
	} else {
    byte Cntr = luaL_checkinteger(L,1);
    switch(Cntr) {
        case COUNTER01:
            ptrTIUCommThread->GetCounterValue(ptrPC_local->Counter01, &CntrVal);
            break;
        case COUNTER02:
            ptrTIUCommThread->GetCounterValue(ptrPC_local->Counter02, &CntrVal);
            break;
        case COUNTER03:
            ptrTIUCommThread->GetCounterValue(ptrPC_local->Counter03, &CntrVal);
            break;
        case COUNTER04:
            ptrTIUCommThread->GetCounterValue(ptrPC_local->Counter04, &CntrVal);
            break;
        case COUNTER05:
            ptrTIUCommThread->GetCounterValue(ptrPC_local->Counter05, &CntrVal);
            break;
		case COUNTER06:
            ptrTIUCommThread->GetCounterValue(ptrPC_local->Counter06, &CntrVal);
            break;
        case TAGSUB:
            ptrTIUCommThread->GetCounterValue(DetectedRFIDTags->TagSubCount, &CntrVal);
            break;
        case UNIQTAG:
            ptrTIUCommThread->GetCounterValue(DetectedRFIDTags->UniqueTagCount, &CntrVal);
            break;
		case TAGCNT:
            ptrTIUCommThread->GetCounterValue(DetectedRFIDTags->TagCount, &CntrVal);
            break;
		case ELAPSEDTIME:
            ptrTIUCommThread->GetCounterValue(ptrPC_local->ETime, &CntrVal);
            break;
        case ENGINENO:
            ptrTIUCommThread->GetCounterValue(ptrPC_local->EngineSpin, &CntrVal);
        	break;
        case NUMENG:
            ptrTIUCommThread->GetCounterValue(MainForm->StartedConsists, &CntrVal);
            break;
        default:
            HARDERROR(ptrPC_local->PCMessages, Format((String)"Invalid argument (%d) in call to GetCounter()", ARRAYOFCONST((Cntr))), (TColor) clRed);		// Display in Message List
		    lua_pushnil(L);
   			return 1; //we are returning one value
        }
    }
lua_pushinteger(L, CntrVal);
return 1; //we are returning one value
}
//---------------------------------------------------------------------------
//
// Set one of the counters on the Program Control Window to zero
//
// return true on success, nil on error
//
static int luaH_ClearCounter (lua_State *L) {
bool bretv = false;
int n = lua_gettop(L);	//n is the number of arguments, use if needed
if (n != 1) {
	HARDERROR(ptrPC_local->PCMessages, Format((String)"Wrong # of arguments in call to ClearCounter() - got %d need 1", ARRAYOFCONST((n))), (TColor) clRed);		// Display in Message List
	lua_pushnil(L);
	return 1; //we are returning one value
	} else {
    byte Cntr = luaL_checkinteger(L,1);
    switch(Cntr) {
        case COUNTER01:
            ptrTIUCommThread->ClearCounter(ptrPC_local->Counter01);
            bretv = true;
            break;
        case COUNTER02:
            ptrTIUCommThread->ClearCounter(ptrPC_local->Counter02);
            bretv = true;
            break;
        case COUNTER03:
            ptrTIUCommThread->ClearCounter(ptrPC_local->Counter03);
            bretv = true;
            break;
        case COUNTER04:
            ptrTIUCommThread->ClearCounter(ptrPC_local->Counter04);
            bretv = true;
            break;
        case COUNTER05:
            ptrTIUCommThread->ClearCounter(ptrPC_local->Counter05);
            bretv = true;
            break;
        case COUNTER06:
            ptrTIUCommThread->ClearCounter(ptrPC_local->Counter06);
			bretv = true;
            break;
        case TAGSUB:
            ptrTIUCommThread->ClearCounter(DetectedRFIDTags->TagSubCount);
            bretv = true;
            break;
        case UNIQTAG:
            ptrTIUCommThread->ClearCounter(DetectedRFIDTags->UniqueTagCount);
            bretv = true;
            break;
        case TAGCNT:
            ptrTIUCommThread->ClearCounter(DetectedRFIDTags->TagCount);
			bretv = true;
            break;
        case ELAPSEDTIME:
            ptrTIUCommThread->ClearCounter(ptrPC_local->ETime);
            bretv = true;
            break;
        default:
            HARDERROR(ptrPC_local->PCMessages, Format((String)"Invalid argument (%d) in call to ClearCounter()", ARRAYOFCONST((Cntr))), (TColor) clRed);		// Display in Message List
			lua_pushnil(L);
			return 1; //we are returning one value
        }
    }
lua_pushboolean(L, bretv);
return 1; //we are returning one value
}
//---------------------------------------------------------------------------
//Sound(bool bState, float when, byte iConsistNo, byte TIUNo);// time delay (when) in seconds
//
// Turn the sound off and on, nil on error
//
static int luaH_Sound (lua_State *L) {
	bool bretv = false;
	int n = lua_gettop(L);	//n is the number of arguments, use if needed
    if (n != 4) {
    	HARDERROR(ptrPC_local->PCMessages, Format((String)"Wrong # of arguments in call to Sound() - got %d need 4", ARRAYOFCONST((n))), (TColor) clRed);		// Display in Message List
		lua_pushnil(L);
		return 1; //we are returning one value
		} else {
        bool bState = lua_toboolean(L,1);
        float when = luaL_checknumber(L,2);
        byte iConsistNo = luaL_checkinteger(L,3);
        byte iTIUNo = luaL_checkinteger(L,4);
        luaL_argcheck(L, iConsistNo >= ENGINEMIN && iConsistNo <= LASHUPMAX, 3, "Engine/Lashup Number out of range");
        luaL_argcheck(L, iTIUNo >= 1 && iTIUNo <= TIUMAX, 4, "TIU Number out of range");
        bretv = ptrPC_local->ptrPC_Thread->Sound(bState,  when, iConsistNo, iTIUNo);
        if (!bretv) {
        	lua_pushnil(L);
            return 1;
            }
        }
	lua_pushboolean(L, bretv);
	return 1; //we are returning one value
}
//---------------------------------------------------------------------------
//
// Set one of the counters on the Program Control Window to a given value
//
// return true on success, nil on error
//
static int luaH_SetCounter (lua_State *L) {
bool bretv = false;
int n = lua_gettop(L);	//n is the number of arguments, use if needed
if (n != 2) {
	HARDERROR(ptrPC_local->PCMessages, Format((String)"Wrong # of arguments in call to SetCounter() - got %d need 2", ARRAYOFCONST((n))), (TColor) clRed);		// Display in Message List
	lua_pushnil(L);
	return 1; //we are returning one value
	} else {
    byte Cntr = luaL_checkinteger(L,1);
    float value = luaL_checknumber(L,2);
    switch(Cntr) {
        case COUNTER01:
            ptrTIUCommThread->SetCounter(ptrPC_local->Counter01, value);
            bretv = true;
            break;
        case COUNTER02:
            ptrTIUCommThread->SetCounter(ptrPC_local->Counter02, value);
            bretv = true;
            break;
        case COUNTER03:
            ptrTIUCommThread->SetCounter(ptrPC_local->Counter03, value);
            bretv = true;
            break;
        case COUNTER04:
            ptrTIUCommThread->SetCounter(ptrPC_local->Counter04, value);
            bretv = true;
            break;
        case COUNTER05:
            ptrTIUCommThread->SetCounter(ptrPC_local->Counter05, value);
            bretv = true;
            break;
        case COUNTER06:
            ptrTIUCommThread->SetCounter(ptrPC_local->Counter06, value);
			bretv = true;
            break;
        case TAGSUB:
            ptrTIUCommThread->SetCounter(DetectedRFIDTags->TagSubCount, value);
            bretv = true;
            break;
        case UNIQTAG:
            ptrTIUCommThread->SetCounter(DetectedRFIDTags->UniqueTagCount, value);
            bretv = true;
            break;
        case TAGCNT:
            ptrTIUCommThread->SetCounter(DetectedRFIDTags->TagCount, value);
			bretv = true;
            break;
        case ELAPSEDTIME:
            ptrTIUCommThread->SetCounter(ptrPC_local->ETime, value);
            bretv = true;
            break;
        default:
            HARDERROR(ptrPC_local->PCMessages, Format((String)"Invalid argument (%d) in call to SetCounter()", ARRAYOFCONST((Cntr))), (TColor) clRed);		// Display in Message List
			lua_pushnil(L);
			return 1; //we are returning one value
        }
   }
lua_pushboolean(L, bretv);
return 1; //we are returning one value
}
//---------------------------------------------------------------------------
//
// Add value to one of the counters on the Program Control Window
//
// return true on success, nil on error
//
static int luaH_AddToCounter (lua_State *L) {
bool bretv = false;
int n = lua_gettop(L);	//n is the number of arguments, use if needed
if (n != 2) {
	HARDERROR(ptrPC_local->PCMessages, Format((String)"Wrong # of arguments in call to AddToCounter() - got %d need 2", ARRAYOFCONST((n))), (TColor) clRed);		// Display in Message List
	lua_pushnil(L);
	return 1; //we are returning one value
	} else {
    byte Cntr = luaL_checkinteger(L,1);
    float value = luaL_checknumber(L,2);
    switch(Cntr) {
        case COUNTER01:
            ptrTIUCommThread->AddCntr(ptrPC_local->Counter01, value);
            bretv = true;
            break;
        case COUNTER02:
            ptrTIUCommThread->AddCntr(ptrPC_local->Counter02, value);
            bretv = true;
            break;
        case COUNTER03:
            ptrTIUCommThread->AddCntr(ptrPC_local->Counter03, value);
            bretv = true;
            break;
        case COUNTER04:
            ptrTIUCommThread->AddCntr(ptrPC_local->Counter04, value);
            bretv = true;
            break;
        case COUNTER05:
            ptrTIUCommThread->AddCntr(ptrPC_local->Counter05, value);
            bretv = true;
            break;
        case COUNTER06:
            ptrTIUCommThread->AddCntr(ptrPC_local->Counter06, value);
			bretv = true;
            break;
        case TAGSUB:
            ptrTIUCommThread->AddCntr(DetectedRFIDTags->TagSubCount, value);
            bretv = true;
            break;
        case UNIQTAG:
            ptrTIUCommThread->AddCntr(DetectedRFIDTags->UniqueTagCount, value);
            bretv = true;
            break;
        case TAGCNT:
            ptrTIUCommThread->AddCntr(DetectedRFIDTags->TagCount, value);
			bretv = true;
            break;
        case ELAPSEDTIME:
            ptrTIUCommThread->AddCntr(ptrPC_local->ETime, value);
            bretv = true;
            break;
        default:
            HARDERROR(ptrPC_local->PCMessages, Format((String)"Invalid argument (%d) in call to AddToCounter()", ARRAYOFCONST((Cntr))), (TColor) clRed);		// Display in Message List
			lua_pushnil(L);
			return 1; //we are returning one value
        }
   }
lua_pushboolean(L, bretv);
return 1; //we are returning one value
}
//---------------------------------------------------------------------------
//Sleep(DelayTimeSeconds);	// time delay in seconds
//
// Sleep for the number of seconds
// DelayTimeSeconds is a float, eg  1, 1.1, 1.001, 1.0001, 0.01, etc
// resolution is 100 nanoseconds or 0.0000001
// return true on sleeping success, false if the sleep was interrupted, nil on error
//
static int luaH_Sleep (lua_State *L) {
	bool bretv = false;
	int n = lua_gettop(L);	//n is the number of arguments
    if (n != 1) {
        HARDERROR(ptrPC_local->PCMessages, "Missing argument in call to Sleep()", (TColor) clRed);		// Display in Message List
		lua_pushnil(L);
		return 1; //we are returning one value
        } else {
		float DelayTime = luaL_checknumber(L,1);
    	if (DEBUGGINGX(8)) ShowinBuffer(ptrPC_local->PCMessages, "Sleep Start : " + String(DelayTime), (TColor) clNavy);		// Display in Message List
		if (DelayTime != 0) {
            /*
            PCSleepinSeconds returns true on success and false on failure
            (Unknown error and Program Termination
            */
        	bretv = ptrPC_local->ptrPC_Thread->PCSleepinSeconds(L, DelayTime);	// DelayTime in seconds
            }
        }
    lua_pushboolean(L, bretv);
    return 1; //we are returning one value
}
//---------------------------------------------------------------------------
// Ping(Engine, TIU)
//
// Sends 100 packets to an engine and returns the number of successful packets
//
static int luaH_Ping (lua_State *L) {
	bool bretv = false;
    int p4loss = 0;
	int n = lua_gettop(L);	//n is the number of arguments
    if (n != 2) {
        HARDERROR(ptrPC_local->PCMessages, Format((String)"Wrong # of arguments in call to GetSetSpeed() - got %d need 2", ARRAYOFCONST((n))), (TColor) clRed);		// Display in Message List
	    lua_pushnil(L);
    	return 1; //we are returning one value
        } else {
    	byte iEngineNo = luaL_checkinteger(L,1);
		byte iTIUNo = luaL_checkinteger(L,2);
        luaL_argcheck(L, iEngineNo >= ENGINEMIN && iEngineNo <= ENGINEMAX, 1, "Engine Number out of range");
        luaL_argcheck(L, iTIUNo >= 1 && iTIUNo <= TIUMAX, 2, "TIU Number out of range");
        bretv = ptrPC_local->ptrPC_Thread->Ping(&p4loss, iEngineNo, iTIUNo);
        if (bretv) {
			lua_pushinteger(L, 100 - p4loss);
			return 1; //we are returning one value
        	}
        }
    lua_pushnil(L);
    return 1; //we are returning one value
}
//---------------------------------------------------------------------------
//MicroSeconds();	// return the value of the Performance Counter in microseconds
//
// Returns the Performance Counter in microseconds as an integer
// https://docs.microsoft.com/en-us/windows/desktop/SysInfo/acquiring-high-resolution-time-stamps
//
static int luaH_MicroSeconds (lua_State *L) {
	//int n = lua_gettop(L);	//n is the number of arguments, use if needed
    //
    LARGE_INTEGER CurrentTime, Frequency;
    //
    QueryPerformanceFrequency(&Frequency);
	QueryPerformanceCounter(&CurrentTime);
    //
    // We now have the elapsed number of ticks, along with the
    // number of ticks-per-second. We use these values
    // to convert to the number of elapsed microseconds.
    // To guard against loss-of-precision, we convert
    // to microseconds *before* dividing by ticks-per-second.
    //
    CurrentTime.QuadPart *= 1000000;
	CurrentTime.QuadPart /= Frequency.QuadPart;
    //
	lua_pushinteger(L, CurrentTime.QuadPart);
    return 1; //we are returning one value
}
//---------------------------------------------------------------------------
// RunTime()
//
// Return the current run time in seconds + tenths of seconds as a number
//
static int luaH_GetRunTime (lua_State *L) {
    //int n = lua_gettop(L);	//n is the number of arguments, use if needed
    // no argument needed - ignore any that are passed
    lua_pushnumber(L, PC_Tenths/10.0);	// global run time from single Timer Thread
	// Old way
    //lua_pushnumber(L, ptrPC_Tenths[ptrPC_local->ptrPC_Thread->MyPCNo - 1]/10.0);	// run time from single Timer Thread
    return 1; //we are returning one value
}
//---------------------------------------------------------------------------
// PCNo()
//
// Return the Program_Control window number of this PC Thread (1-10) as an integer
//
static int luaH_GetPCNo (lua_State *L) {
    //int n = lua_gettop(L);	//n is the number of arguments, use if needed
    // no argument needed - ignore any that are passed
    lua_pushinteger(L, ptrPC_local->ptrPC_Thread->MyPCNo);	// PCNo
    return 1; //we are returning one value
}
//---------------------------------------------------------------------------
// GetTIUVersion(TIUNo)
//
// Return the TIU Version as an integer and as a string
// String might be 106 or 130 / "v1.06" or "v1.3.0" - WTIU
// Integer might be 610 / "v6.10" - TIU
// Return of 0 / "Connect" indicates a bad response from the TIU/WTIU
//
static int luaH_GetTIUVersion (lua_State *L) {
	int n = lua_gettop(L);	//n is the number of arguments
    if (n != 1) {
        HARDERROR(ptrPC_local->PCMessages, "Missing TIU number argument in call to GetTIUVersion()", (TColor) clRed);		// Display in Message List
		lua_pushnil(L);
		return 1; //we are returning one value
        } else {
   		byte iTIUNo = luaL_checkinteger(L,1);
        luaL_argcheck(L, iTIUNo >= 1 && iTIUNo <= TIUMAX, 1, "TIU Number out of range");
        lua_pushinteger(L, TIUVersion[iTIUNo - 1]); // returns an integer
        AnsiString ResponseString = TIUVersionString[iTIUNo - 1];  // AnsiString needed because .c_str() is taken and char string required
        lua_pushstring(L, ResponseString.c_str()); // returns a string
        return 2; //we are returning two values
        }
}
//---------------------------------------------------------------------------
// ActiveCnt, InactiveCnt = Counts();
//
// Return the number of Active and Inactive Engines as integers
// returns nil, nil on error
//
static int luaH_GetCounts (lua_State *L) {
	int n = lua_gettop(L);	//n is the number of arguments
    if (n != 0) {
        HARDERROR(ptrPC_local->PCMessages, "Unneeded arguments in call to Counts()", (TColor) clRed);		// Display in Message List
		ptrPC_local->ptrPC_Thread->MyActiveCount = 0;
        ptrPC_local->ptrPC_Thread->MyInactiveCount = 0;
		// returns nil, nil on error
	   	lua_pushnil(L);
	   	lua_pushnil(L);
	    return 2; //we are returning two values
    	}
	ptrPC_local->ptrPC_Thread->MyGetCounts();
   	lua_pushinteger(L, ptrPC_local->ptrPC_Thread->MyActiveCount);
   	lua_pushinteger(L, ptrPC_local->ptrPC_Thread->MyInactiveCount);
	return 2; //we are returning two values
}
//---------------------------------------------------------------------------
// RadioPortConnected, TIUConnected, RFIDPortConnected = CommStatus();
//
// Return status of connected serial and WiFi ports
//
static int luaH_GetCommStatus (lua_State *L) {
	//int n = lua_gettop(L);	//n is the number of arguments, use if needed
    // no argument needed - ignore any that are passed
   	lua_pushboolean(L, bRadioCOMConnected);
   	lua_pushboolean(L, bTIUConnected);
	lua_pushboolean(L, bRFIDCOMConnected || bRFIDUDPWiFiConnected || bRFIDTCPWiFiConnected);
    return 3; //we are returning three values
}
//---------------------------------------------------------------------------
// Stop(message, beep)
// Stops the script after running cleanup()
// Displays optional message prepended with "Stop() : "
// Optionally beeps on second parameter set to true
// Always returns false
//
static int luaH_Stop(lua_State *L) {
//
	boolean PlayBeep = false;
    int n = lua_gettop(L);	//n is the number of arguments, use if needed
    if (n > 0) {	// Display message
        String PLine = luaL_checkstring(L, 1);
        ShowinBuffer(ptrPC_local->PCMessages, String("Stop() : ") + PLine, (TColor) clRed);
        }
    if (n > 1) {	// Beep Sound
    	PlayBeep = lua_toboolean(L, 2);
        }
    // Request stop by sending a message to Progam_Control.cpp
    // first, clear pending events
    ResetEvent(ptrPC_local->ptrPC_Thread->RFIDReqEvent);
    // prevent 7 of the 8 functions from running
    ptrPC_local->ptrPC_Thread->CallTagFunction = false;
    ptrPC_local->ptrPC_Thread->CallUIDTagFunction = false;
    ptrPC_local->ptrPC_Thread->CallNameTagFunction = false;
    ptrPC_local->ptrPC_Thread->CallResponseFunction = false;
    ptrPC_local->ptrPC_Thread->CallLoopFunction = false;
    ptrPC_local->ptrPC_Thread->CallTickFunction = false;
    ptrPC_local->ptrPC_Thread->CallEventFunction = false;
    // Allow cleanup() to still run
    ResetEvent(ptrPC_local->TerminationPCEvent);
    ResetEvent(ptrPC_local->FunctionPCEvent);
    ResetEvent(ptrPC_local->LoopPCEvent);
    ResetEvent(ptrPC_local->SetupPCEvent);
    ResetEvent(ptrPC_local->CleanupPCEvent);
    ResetEvent(ptrPC_local->EventPCEvent);
    ResetEvent(ptrPC_local->InterpreterPCEvent);
    ResetEvent(ptrPC_local->InputPCEvent);

    // Send CM_ PROGCTRLSTOP message to Program Control which will perform this function
    (void) PostMessage(ptrPC_local->Handle, CM_PROGCTRLSTOP, NULL, NULL);
    if (PlayBeep) {
    	MessageBeep(MB_ICONEXCLAMATION);	// stop sound
        }
    lua_pushboolean(L, false);
    return 1; //we are returning one value
}
//---------------------------------------------------------------------------
// EmergencyStop(message, beep)
//
// send an emergency stop command to all TIU
// Displays optional message prepended with "EmergencyStop() : "
// Optionally beeps on second parameter set to true
// always returns nil
//
static int luaH_Emergency(lua_State *L) {
	boolean PlayBeep = false;
    int n = lua_gettop(L);	//n is the number of arguments, use if needed
    if (n > 0) {	// Display message
        String PLine = luaL_checkstring(L, 1);
        ShowinBuffer(ptrPC_local->PCMessages, String("EmergencyStop() : ") + PLine, (TColor) clRed);
        }
    if (n > 1) {	// Beep Sound
    	PlayBeep = lua_toboolean(L, 2);
        }
	// Request emergency stop by sending CM_ EMERGENCYSTOP message to MainForm
    // first, clear pending events
    ResetEvent(ptrPC_local->ptrPC_Thread->RFIDReqEvent);
    // prevent 7 of the 8 functions from running
    ptrPC_local->ptrPC_Thread->CallTagFunction = false;
    ptrPC_local->ptrPC_Thread->CallUIDTagFunction = false;
    ptrPC_local->ptrPC_Thread->CallNameTagFunction = false;
    ptrPC_local->ptrPC_Thread->CallResponseFunction = false;
    ptrPC_local->ptrPC_Thread->CallLoopFunction = false;
    ptrPC_local->ptrPC_Thread->CallTickFunction = false;
    ptrPC_local->ptrPC_Thread->CallEventFunction = false;
    // Allow cleanup() to still run
    ResetEvent(ptrPC_local->TerminationPCEvent);
    ResetEvent(ptrPC_local->FunctionPCEvent);
    ResetEvent(ptrPC_local->LoopPCEvent);
    ResetEvent(ptrPC_local->SetupPCEvent);
    ResetEvent(ptrPC_local->CleanupPCEvent);
    ResetEvent(ptrPC_local->EventPCEvent);
    ResetEvent(ptrPC_local->InterpreterPCEvent);
    ResetEvent(ptrPC_local->InputPCEvent);

    // Send CM_ EMERGENCYSTOP message to MainForm which will perform this function
	(void) PostMessage(MainForm->Handle, CM_EMERGENCYSTOP, 0, 0);
    if (PlayBeep) {
    	MessageBeep(MB_ICONSTOP);	// stop sound
        }
	lua_pushnil(L);
    return 1; //we are returning one value
}
//---------------------------------------------------------------------------
// Exists(int EngineNo)
//
// Return true if the engine (1-99) or Lashup (101-120) exists
// returns nil on error
//
static int luaH_Exists (lua_State *L) {
	int n = lua_gettop(L);	//n is the number of arguments, use if needed
	bool bretv = false;
    if (n != 1) {
		HARDERROR(ptrPC_local->PCMessages, Format((String)"Wrong # of arguments in call to Exists() - got %d need 1", ARRAYOFCONST((n))), (TColor) clRed);		// Display in Message List
        lua_pushnil(L);
        return 1; //we are returning one value
   	    } else {
	    int iConsistNo = luaL_checkinteger(L,1);
        if (iConsistNo < ENGINEMIN || iConsistNo > LASHUPMAX) {
            HARDERROR(ptrPC_local->PCMessages, "Consist Number out of range in call to Exists()", (TColor) clRed);		// Display in Message List
        	lua_pushnil(L);
            return 1; //we are returning one value
            } else {
	        ptrPC_local->ptrPC_Thread->MyDoesConsistExist(iConsistNo);
			if (ptrPC_local->ptrPC_Thread->MybResponse) {
    			bretv = true;
	        	}
            }
        }
    lua_pushboolean(L, bretv);
    return 1; //we are returning one value
}
//---------------------------------------------------------------------------
// IsActive(int EngineNo)
//
// Return true if the engine is active
// returns nil on error
//
static int luaH_IsActive (lua_State *L) {
	int n = lua_gettop(L);	//n is the number of arguments, use if needed
	bool bretv = false;
	if (n != 1) {
		HARDERROR(ptrPC_local->PCMessages, Format((String)"Wrong # of arguments in call to IsActive() - got %d need 1", ARRAYOFCONST((n))), (TColor) clRed);		// Display in Message List
       	lua_pushnil(L);
        return 1; //we are returning one value
     	} else {
	    int iEngineNo = luaL_checkinteger(L,1);
        luaL_argcheck(L, iEngineNo >= ENGINEMIN && iEngineNo <= ENGINEMAX, 1, "Engine Number out of range");
        ptrPC_local->ptrPC_Thread->MyIsEngineActive(iEngineNo);
		if (ptrPC_local->ptrPC_Thread->MybResponse) {
    		bretv = true;
	        }
		}
    lua_pushboolean(L, bretv);
    return 1; //we are returning one value
}
//---------------------------------------------------------------------------
// RAM(int addr, int Engine, int TIU)
//
// read and return one byte of Engine RAM
// returns nil on error
//
static int luaH_RAM(lua_State *L) {
	byte ival;
	int n = lua_gettop(L);	//n is the number of arguments, use if needed
    if (n != 3) {
        HARDERROR(ptrPC_local->PCMessages, Format((String)"Wrong # of arguments in call to RAM() - got %d need 3", ARRAYOFCONST((n))), (TColor) clRed);		// Display in Message List
	    lua_pushnil(L);
    	return 1; //we are returning one value
        } else {
	    unsigned int addr = luaL_checkinteger(L,1);
    	byte iEngineNo = luaL_checkinteger(L,2);
		byte iTIUNo = luaL_checkinteger(L,3);
        luaL_argcheck(L, addr <= 0x3FF, 1, "RAM address out of range");
        luaL_argcheck(L, iEngineNo >= ENGINEMIN && iEngineNo <= ENGINEMAX, 2, "Engine Number out of range");
        luaL_argcheck(L, iTIUNo >= 1 && iTIUNo <= TIUMAX, 3, "TIU Number out of range");
        // Due to the way the DL_FIFO and the FIFO are handled, it is possible that
        // this RAM fetch can execute before previous commands.
        (void) ptrPC_local->ptrPC_Thread->PCSleepinSeconds(L, 0.1);	// delay for a tenth of a second
    	if (!ptrPC_local->ptrPC_Thread->FetchByte(addr, &ival, iEngineNo, iTIUNo)) {
        	lua_pushnil(L);
            return 1;
// 	       	SOFTERROR(ptrPC_local->PCMessages, Format((String)"RAM() - read of address 0x%3.3X failed", ARRAYOFCONST(((int)addr))), (TColor) clRed);
            }
        }
    lua_pushinteger(L, ival);
    return 1; //we are returning one value
}
//---------------------------------------------------------------------------
// RAM2(int addr, int Engine, int TIU)
//
// read and return 2 bytes (word) of Engine RAM
// returns nil on error
//
static int luaH_RAM2(lua_State *L) {
	unsigned short ival;
	int n = lua_gettop(L);	//n is the number of arguments, use if needed
    if (n != 3) {
        HARDERROR(ptrPC_local->PCMessages, Format((String)"Wrong # of arguments in call to RAM2() - got %d need 3", ARRAYOFCONST((n))), (TColor) clRed);		// Display in Message List
	    lua_pushnil(L);
    	return 1; //we are returning one value
        } else {
	    unsigned int addr = luaL_checkinteger(L,1);
    	byte iEngineNo = luaL_checkinteger(L,2);
		byte iTIUNo = luaL_checkinteger(L,3);
        luaL_argcheck(L, addr <= 0x3FF, 1, "RAM address out of range");
        luaL_argcheck(L, iEngineNo >= ENGINEMIN && iEngineNo <= ENGINEMAX, 2, "Engine Number out of range");
        luaL_argcheck(L, iTIUNo >= 1 && iTIUNo <= TIUMAX, 3, "TIU Number out of range");
        // Due to the way the DL_FIFO and the FIFO are handled, it is possible that
        // this RAM fetch can execute before previous commands.
        (void) ptrPC_local->ptrPC_Thread->PCSleepinSeconds(L, 0.1);	// delay for a tenth of a second
    	if (!ptrPC_local->ptrPC_Thread->FetchWord(addr, &ival, iEngineNo, iTIUNo)) {
        	lua_pushnil(L);
            return 1;
// 	       	SOFTERROR(ptrPC_local->PCMessages, Format((String)"RAM2() - read of address 0x%3.3X failed", ARRAYOFCONST(((int)addr))), (TColor) clRed);
            }
        }
    lua_pushinteger(L, ival);
    return 1; //we are returning one value
}
//---------------------------------------------------------------------------
// RAM4(int addr, int Engine, int TIU)
//
// read and return 4 bytes (int) of Engine RAM
// returns nil on error
//
static int luaH_RAM4(lua_State *L) {
	unsigned int ival;
	int n = lua_gettop(L);	//n is the number of arguments, use if needed
    if (n != 3) {
        HARDERROR(ptrPC_local->PCMessages, Format((String)"Wrong # of arguments in call to RAM4() - got %d need 3", ARRAYOFCONST((n))), (TColor) clRed);		// Display in Message List
	    lua_pushnil(L);
    	return 1; //we are returning one value
        } else {
	    unsigned int addr = luaL_checkinteger(L,1);
    	byte iEngineNo = luaL_checkinteger(L,2);
		byte iTIUNo = luaL_checkinteger(L,3);
        luaL_argcheck(L, addr <= 0x3FF, 1, "RAM address out of range");
        luaL_argcheck(L, iEngineNo >= ENGINEMIN && iEngineNo <= ENGINEMAX, 2, "Engine Number out of range");
        luaL_argcheck(L, iTIUNo >= 1 && iTIUNo <= TIUMAX, 3, "TIU Number out of range");
        // Due to the way the DL_FIFO and the FIFO are handled, it is possible that
        // this RAM fetch can execute before previous commands.
        (void) ptrPC_local->ptrPC_Thread->PCSleepinSeconds(L, 0.1);	// delay for a tenth of a second
    	if (!ptrPC_local->ptrPC_Thread->FetchInt(addr, &ival, iEngineNo, iTIUNo)) {
        	lua_pushnil(L);
            return 1;
// 	       	SOFTERROR(ptrPC_local->PCMessages, Format((String)"RAM4() - read of address 0x%3.3X failed", ARRAYOFCONST(((int)addr))), (TColor) clRed);
            }
        }
    lua_pushinteger(L, ival);
    return 1; //we are returning one value
}
//---------------------------------------------------------------------------
// DTO(int Engine, int TIU)
// read and return the trip odometer as a number
// returns nil on error
//
static int luaH_DTO(lua_State *L) {
	double dval;
	int n = lua_gettop(L);	//n is the number of arguments, use if needed
    if (n != 2) {
        HARDERROR(ptrPC_local->PCMessages, Format((String)"Wrong # of arguments in call to DTO() - got %d need 2", ARRAYOFCONST((n))), (TColor) clRed);		// Display in Message List
	    lua_pushnil(L);
    	return 1; //we are returning one value
        } else {
    	byte iEngineNo = luaL_checkinteger(L,1);
		byte iTIUNo = luaL_checkinteger(L,2);
        luaL_argcheck(L, iEngineNo >= ENGINEMIN && iEngineNo <= ENGINEMAX, 1, "Engine Number out of range");
        luaL_argcheck(L, iTIUNo >= 1 && iTIUNo <= TIUMAX, 2, "TIU Number out of range");
    	if (!ptrPC_local->ptrPC_Thread->FetchDTO(&dval, iEngineNo, iTIUNo)) {
        	lua_pushnil(L);
            return 1;
// 		   	SOFTERROR(ptrPC_local->PCMessages, Format((String)"DTO() - read of Engine %d failed", ARRAYOFCONST((iEngineNo))), (TColor) clRed);
            }
        }
    lua_pushnumber(L, dval);
    return 1; //we are returning one value
}
//---------------------------------------------------------------------------
// DCH(int Engine, int TIU)
// read and return the chronometer as a int
// returns nil on error
//
static int luaH_DCH(lua_State *L) {
	double dval;
	int n = lua_gettop(L);	//n is the number of arguments, use if needed
	if (n != 2) {
        HARDERROR(ptrPC_local->PCMessages, Format((String)"Wrong # of arguments in call to DCH() - got %d need 2", ARRAYOFCONST((n))), (TColor) clRed);		// Display in Message List
	    lua_pushnil(L);
    	return 1; //we are returning one value
        } else {
    	byte iEngineNo = luaL_checkinteger(L,1);
		byte iTIUNo = luaL_checkinteger(L,2);
        luaL_argcheck(L, iEngineNo >= ENGINEMIN && iEngineNo <= ENGINEMAX, 1, "Engine Number out of range");
        luaL_argcheck(L, iTIUNo >= 1 && iTIUNo <= TIUMAX, 2, "TIU Number out of range)");
    	if (!ptrPC_local->ptrPC_Thread->FetchDCH(&dval, iEngineNo, iTIUNo)) {
        	lua_pushnil(L);
            return 1;
//         	SOFTERROR(ptrPC_local->PCMessages, Format((String)"DCH() - read of Engine %d failed", ARRAYOFCONST((iEngineNo))), (TColor) clRed);
            }
        }
    lua_pushnumber(L, dval);
    return 1; //we are returning one value
}
//---------------------------------------------------------------------------
// DOD(int Engine, int TIU)
// read and return the odometer as a number
// returns nil on error
//
static int luaH_DOD(lua_State *L) {
	double dval;
	int n = lua_gettop(L);	//n is the number of arguments, use if needed
    if (n != 2) {
        HARDERROR(ptrPC_local->PCMessages, Format((String)"Wrong # of arguments in call to DOD() - got %d need 2", ARRAYOFCONST((n))), (TColor) clRed);		// Display in Message List
	    lua_pushnil(L);
    	return 1; //we are returning one value
        } else {
    	byte iEngineNo = luaL_checkinteger(L,1);
		byte iTIUNo = luaL_checkinteger(L,2);
        luaL_argcheck(L, iEngineNo >= ENGINEMIN && iEngineNo <= ENGINEMAX, 1, "Engine Number out of range");
        luaL_argcheck(L, iTIUNo >= 1 && iTIUNo <= TIUMAX, 2, "TIU Number out of range");
    	if (!ptrPC_local->ptrPC_Thread->FetchDOD(&dval, iEngineNo, iTIUNo)) {
        	lua_pushnil(L);
            return 1;
// 	       	SOFTERROR(ptrPC_local->PCMessages, Format((String)"DOD() - read of Engine %d failed", ARRAYOFCONST((iEngineNo))), (TColor) clRed);
            }
        }
    lua_pushnumber(L, dval);
    return 1; //we are returning one value
}
//---------------------------------------------------------------------------
// GetSetSpeed(int Engine, int TIU)
// read and return the set engine speed as an integer
// Engine may not be running at this speed but may be accellerating or decellerating to this speed
// returns nil on error
//
static int luaH_GetSetSpeed(lua_State *L) {
	unsigned int ival;
	int n = lua_gettop(L);	//n is the number of arguments, use if needed
    if (n != 2) {
        HARDERROR(ptrPC_local->PCMessages, Format((String)"Wrong # of arguments in call to GetSetSpeed() - got %d need 2", ARRAYOFCONST((n))), (TColor) clRed);		// Display in Message List
	    lua_pushnil(L);
    	return 1; //we are returning one value
        } else {
    	byte iEngineNo = luaL_checkinteger(L,1);
		byte iTIUNo = luaL_checkinteger(L,2);
        luaL_argcheck(L, iEngineNo >= ENGINEMIN && iEngineNo <= ENGINEMAX, 1, "Engine Number out of range");
        luaL_argcheck(L, iTIUNo >= 1 && iTIUNo <= TIUMAX, 2, "TIU Number out of range");
    	if (!ptrPC_local->ptrPC_Thread->FetchSpeed(&ival, iEngineNo, iTIUNo)) {
        	lua_pushnil(L);
            return 1;
//         	SOFTERROR(ptrPC_local->PCMessages, Format((String)"GetSetSpeed() - read of Engine %d failed", ARRAYOFCONST((iEngineNo))), (TColor) clRed);
            }
        }
    lua_pushinteger(L, ival);
    return 1; //we are returning one value
}
//---------------------------------------------------------------------------
// GetSpeed(int Engine, int TIU)
// read and return the instantaneous engine speed as an integer
// returns nil on error or if speed is greater than the MaxSpeed spinner value
//
static int luaH_GetSpeed(lua_State *L) {
	unsigned int ival;
	int n = lua_gettop(L);	//n is the number of arguments, use if needed
    if (n != 2) {
		HARDERROR(ptrPC_local->PCMessages, Format((String)"Wrong # of arguments in call to GetSpeed() - got %d need 2", ARRAYOFCONST((n))), (TColor) clRed);		// Display in Message List
	    lua_pushnil(L);
    	return 1; //we are returning one value
        } else {
    	byte iEngineNo = luaL_checkinteger(L,1);
		byte iTIUNo = luaL_checkinteger(L,2);
        luaL_argcheck(L, iEngineNo >= ENGINEMIN && iEngineNo <= ENGINEMAX, 1, "Engine Number out of range");
        luaL_argcheck(L, iTIUNo >= 1 && iTIUNo <= TIUMAX, 2, "TIU Number out of range");
    	if (!ptrPC_local->ptrPC_Thread->FetchInstSpeed(&ival, iEngineNo, iTIUNo)) {
        	lua_pushnil(L);
            return 1;
//			SOFTERROR(ptrPC_local->PCMessages, Format((String)"GetSpeed() - read of Engine %d failed", ARRAYOFCONST((iEngineNo))), (TColor) clRed);
            }
        }

    // if speed is greater than max speed, emergency stop
    int MaxSpeed;
    ptrTIUCommThread->GetSpinnerValue(Setup->MaxSpeed, &MaxSpeed);
    if (ival > (unsigned int) MaxSpeed) {
        ShowinBuffer(ptrPC_local->PCMessages, String("GetSpeed() : Max Speed Spinner Exceeded : " + String(ival) + " Smph"), (TColor) clRed);
#if false
// The above test does not work reliably so don't emergency stop
        // Send CM_ EMERGENCYSTOP message to MainForm which will perform this function
        (void) PostMessage(MainForm->Handle, CM_EMERGENCYSTOP, 0, 0);
       	lua_pushnil(L);
        return 1;
#endif
        }

    lua_pushinteger(L, ival);
    return 1; //we are returning one value
}
//---------------------------------------------------------------------------
// GetVolume(int Chan, int Engine, int TIU)
//
// read and return the channel's volume as an integer
// returns nil on error
//
static int luaH_GetVolume(lua_State *L) {
	int ival;
	int n = lua_gettop(L);	//n is the number of arguments, use if needed
    if (n != 3) {
        HARDERROR(ptrPC_local->PCMessages, Format((String)"Wrong # of arguments in call to GetVolume() - got %d need 3", ARRAYOFCONST((n))), (TColor) clRed);		// Display in Message List
	    lua_pushnil(L);
    	return 1; //we are returning one value
        } else {
    	byte iChan = luaL_checkinteger(L,1);
		byte iEngineNo = luaL_checkinteger(L,2);
		byte iTIUNo = luaL_checkinteger(L,3);
        luaL_argcheck(L, iChan <= BELL_VOLUME, 1, "Sound Volume Channel # out of range");
        luaL_argcheck(L, iEngineNo >= ENGINEMIN && iEngineNo <= ENGINEMAX, 2, "Engine Number out of range");
        luaL_argcheck(L, iTIUNo >= 1 && iTIUNo <= TIUMAX, 3, "TIU Number out of range");
    	if (!ptrPC_local->ptrPC_Thread->FetchVolume(iChan, &ival, iEngineNo, iTIUNo)) {
        	lua_pushnil(L);
            return 1;
//         	SOFTERROR(ptrPC_local->PCMessages, Format((String)"GetVolume() - read of Engine %d failed", ARRAYOFCONST((iEngineNo))), (TColor) clRed);
            }
        }
    lua_pushinteger(L, ival);
    return 1; //we are returning one value
}
//---------------------------------------------------------------------------
// GetDebug()
//
// returns the current value of the global debug setting
//
static int luaH_GetDebug(lua_State *L) {
	//int n = lua_gettop(L);	//n is the number of arguments, use if needed
    lua_pushinteger(L, MainForm->GlobalDBLevel);
    return 1; //we are returning one value
}
//---------------------------------------------------------------------------
// GetDirection(int Engine, int TIU)
// read and return the engine direction as an boolean. true = forward, false = reverse
// returns nil on error
//
static int luaH_GetDirection(lua_State *L) {
	bool bval = false;
	int n = lua_gettop(L);	//n is the number of arguments, use if needed
    if (n != 2) {
    	HARDERROR(ptrPC_local->PCMessages, Format((String)"Wrong # of arguments in call to GetDirection() - got %d need 2", ARRAYOFCONST((n))), (TColor) clRed);		// Display in Message List
	    lua_pushnil(L);
    	return 1; //we are returning one value
		} else {
        byte iEngineNo = luaL_checkinteger(L,1);
        byte iTIUNo = luaL_checkinteger(L,2);
        luaL_argcheck(L, iEngineNo >= ENGINEMIN && iEngineNo <= ENGINEMAX, 1, "Engine Number out of range");
        luaL_argcheck(L, iTIUNo >= 1 && iTIUNo <= TIUMAX, 2, "TIU Number out of range");
        if (!ptrPC_local->ptrPC_Thread->FetchDirection(&bval, iEngineNo, iTIUNo)) {
        	lua_pushnil(L);
            return 1;
//         	SOFTERROR(ptrPC_local->PCMessages, Format((String)"GetDirection() - read of Engine %d failed", ARRAYOFCONST((iEngineNo))), (TColor) clRed);
            }
        }
    lua_pushboolean(L, bval);
    return 1; //we are returning one value
}
//---------------------------------------------------------------------------
// Beep(Sound)
// play the standard system beep if Sound is not given
// otherwise plays one of the Waveform Sounds
// 0 = Default Beep Sound MB_OK
// 1 = Critical Stop Sound MB_ICONSTOP
// 2 = Question Sound MB_ICONQUESTION
// 3 = Exclamation Sound MB_ICONEXCLAMATION
// 4 = Asterisk Sound MB_ICONASTERISK
// returns true on success, false on failure to beep
//
static int luaH_Beep(lua_State *L) {
// no argument needed - defaults to system beep
bool bval = false;
int n = lua_gettop(L);	//n is the number of arguments
if (n == 0) {
    Beep();
    bval = true;
	}
if (n == 1) {
	byte iSound = luaL_checkinteger(L,1);
    bval = MessageBeep(iSound * 0x10) != 0;	// returns nonzero if successful
	}
lua_pushboolean(L, bval);
return 1; //we are returning one value
}
//---------------------------------------------------------------------------
// ClearDispatchList()
// clears the dispatch list and returns true
// returns false as a boolean if the dispatch list thread is not running
//
static int luaH_ClearDispatchList(lua_State *L) {
//
if (ptrDL_Thread) {					// pointer to Dispatch List Thread
	ptrDL_Thread->ClearDispatchList();
   	lua_pushboolean(L, true);
	return 1; //we are returning one value
    }
lua_pushboolean(L, false);
return 1; //we are returning one value
}
//---------------------------------------------------------------------------
// CountDispatchList()
// returns the number of entries in the dispatch list as an integer
// returns false if the dispatch list thread is not running
//
static int luaH_CountDispatchList(lua_State *L) {
//
int DL_Count;
if (ptrDL_Thread) {					// pointer to Dispatch List Thread
	DL_Count = ptrDL_Thread->Count_Dispatch_List();
   	lua_pushinteger(L, DL_Count);
	return 1; //we are returning one value
    }
lua_pushboolean(L, false);
return 1; //we are returning one value
}
//---------------------------------------------------------------------------
// InputBox("Caption", "Prompt", "Default string")
// returns the default string or the modified default string
// returns nil if the parameters are not correct
// returns the default string if [Cancel] is pressed
//
static int luaH_InputBox(lua_State *L) {
//
int n = lua_gettop(L);	//n is the number of arguments, use if needed
//if (MainForm->GlobalDBLevel > 0) luaH_StackDump(L);	// dump stack if debug
if (n == 3) {
    ptrPC_local->ptrPC_Thread->ACaption = luaL_checkstring(L,1);
    ptrPC_local->ptrPC_Thread->APrompt = luaL_checkstring(L,2);
    ptrPC_local->ptrPC_Thread->ADefault = luaL_checkstring(L,3);
    ptrPC_local->ptrPC_Thread->MyCallInputBox();
	// return the result string - after having to convert to AnsiString then to char
	AnsiString ResponseString = ptrPC_local->ptrPC_Thread->InputString;// AnsiString needed because .c_str() is taken and char string required
	lua_pushstring(L, ResponseString.c_str());
	return 1;	// we are returning one value
    } else {
    HARDERROR(ptrPC_local->PCMessages, Format((String)"Wrong # of arguments in call to InputBox() - got %d need 3", ARRAYOFCONST((n))), (TColor) clRed);		// Display in Message List
    lua_pushnil(L);
    return 1; //we are returning one value
    }
}
//---------------------------------------------------------------------------
// InputLine(WaitTime)
// Opens the interactive Lua input box and after the user presses <Enter>,
// returns the value typed to the script
// WaitTime is the number of seconds to wait for input
// returns nil on timeout or error
//
static int luaH_InputLine(lua_State *L) {
int n = lua_gettop(L);	//n is the number of arguments, use if needed
float WaitTime;
if (n == 1) {
    WaitTime = luaL_checknumber(L,1);
    } else {
    WaitTime = 30.0;   // default wait is 30 seconds
    }
ptrPC_local->ptrPC_Thread->InputString = "";
ptrPC_local->ptrPC_Thread->MyGetInputLine();
//
// wait for input event from PC_Control
// process other events as needed
//
bool bretv = ptrPC_local->ptrPC_Thread->MyWaitForInputLine(L, WaitTime);
ptrPC_local->ptrPC_Thread->MyGotInputLine();   // take down the input box
if (!bretv) {
    // timed out
    lua_pushnil(L);
    return 1;
    }
//
AnsiString ResponseString = ptrPC_local->ptrPC_Thread->InputString;// AnsiString needed because .c_str() is taken and char string required
lua_pushstring(L, ResponseString.c_str());
return 1; //we are returning one value
}
//---------------------------------------------------------------------------
// InputQuery("Caption", "Prompt", "Default string")
// returns the default string or the modified default string
// returns nil if the parameters are not correct
// returns false if [Cancel] is pressed
//
static int luaH_InputQuery(lua_State *L) {
//
int n = lua_gettop(L);	//n is the number of arguments, use if needed
//if (MainForm->GlobalDBLevel > 0) luaH_StackDump(L);	// dump stack if debug
if (n == 3) {
    ptrPC_local->ptrPC_Thread->ACaption = luaL_checkstring(L,1);
    ptrPC_local->ptrPC_Thread->APrompt = luaL_checkstring(L,2);
	ptrPC_local->ptrPC_Thread->ADefault = luaL_checkstring(L,3);
    ptrPC_local->ptrPC_Thread->MyCallInputQuery();
    if (ptrPC_local->ptrPC_Thread->InputResponse == false) {
    	lua_pushboolean(L, false);
        return 1;
        }
	// return the result string - after having to convert to AnsiString then to char
	AnsiString ResponseString = ptrPC_local->ptrPC_Thread->InputString;// AnsiString needed because .c_str() is taken and char string required
	lua_pushstring(L, ResponseString.c_str());
    return 1;	// we are returning one value
    } else {
    HARDERROR(ptrPC_local->PCMessages, Format((String)"Wrong # of arguments in call to InputQuery() - got %d need 3", ARRAYOFCONST((n))), (TColor) clRed);		// Display in Message List
    lua_pushnil(L);
    return 1; //we are returning one value
    }
}
//---------------------------------------------------------------------------
// InputDropDown("Caption", "Prompt", index, numDropDownList, DropDownList)
// or
// InputDropDown("Caption", "Prompt", index, DropDownList)
//
// returns the default string or the modified default string
// returns nil if the parameters are not correct
// returns false if [Cancel] is pressed
//
static int luaH_InputDropDown(lua_State *L) {
//
int n = lua_gettop(L);	//n is the number of arguments, use if needed
//if (MainForm->GlobalDBLevel > 0) luaH_StackDump(L);	// dump stack if debug
if (n == 4 || n == 5) {
    ptrPC_local->ptrPC_Thread->ACaption = luaL_checkstring(L,1);
    ptrPC_local->ptrPC_Thread->APrompt = luaL_checkstring(L,2);
    ptrPC_local->ptrPC_Thread->AIndex = luaL_checkinteger(L,3) - 1; // convert to Lua index
    int iNum, iSlot;
    if (n == 4) {
        iNum = ENGINEMAX;
    luaL_checktype(L,4,LUA_TTABLE);
        iSlot = 4;
        } else {
        iNum = luaL_checknumber(L,4);
        luaL_checktype(L,5,LUA_TTABLE);
        iSlot = 5;
        }

    ptrPC_local->ptrPC_Thread->DropDownList = new TStringList();
	ptrPC_local->ptrPC_Thread->DropDownList->Clear();
	unsigned int len;

	// this requires that the table be an ordinary array
    // size of table is in iNum
    // table is in the stack at index 4 or 5
	// get each table element (that is non-nil) put it into DropDownList
    for (int i  = 1 ; i <= iNum ; i++) {
        lua_geti(L, iSlot, i);      // push table[i]
        if ((lua_type(L, iSlot + 1) != LUA_TNIL) && (luaL_len(L, iSlot + 1) > 0)) {
            ptrPC_local->ptrPC_Thread->DropDownList->Add(lua_tolstring(L, iSlot + 1, &len));    // get the list element
			if (DEBUGGINGX(3)) {
                ShowinBuffer(ptrPC_local->PCMessages, lua_tolstring(L, iSlot + 1, &len), clGreen);		// Display in Message List
                }
			}
        lua_pop(L, 1);          // pop the list element
        }
	//
	ptrPC_local->ptrPC_Thread->MyCallInputDropDown();
    if (ptrPC_local->ptrPC_Thread->InputResponse == false) {
    	lua_pushboolean(L, false);
        delete ptrPC_local->ptrPC_Thread->DropDownList;
        return 1;
        }
	// return the result string - after having to convert to AnsiString then to char
	AnsiString ResponseString = ptrPC_local->ptrPC_Thread->InputString;// AnsiString needed because .c_str() is taken and char string required
	lua_pushstring(L, ResponseString.c_str());
	//
	delete ptrPC_local->ptrPC_Thread->DropDownList;
    return 1;	// we are returning one value
    } else {
        HARDERROR(ptrPC_local->PCMessages, Format((String)"Wrong # of arguments in call to InputDropDown() - got %d need 4 or 5", ARRAYOFCONST((n))), (TColor) clRed);		// Display in Message List
    lua_pushnil(L);
    return 1; //we are returning one value
    }
}
//---------------------------------------------------------------------------//---------------------------------------------------------------------------
// SetLED(LED#, State, Caption);
//
// Set the Status LED on the Program Control window to clGreen, clYellow or clRed
// Setting to clClear will make the LED disappear
// clXXXX colors are defined in "defines.lua"
//
// return true on success
// returns nil on error
//
static int luaH_SetLED (lua_State *L) {
bool bretv = false;
int n = lua_gettop(L);	//n is the number of arguments, use if needed
if (n != 2 && n != 3) {
	HARDERROR(ptrPC_local->PCMessages, Format((String)"Wrong # of arguments in call to SetLED() - got %d need 2 or 3", ARRAYOFCONST((n))), (TColor) clRed);		// Display in Message List
    lua_pushnil(L);
    return 1; //we are returning one value
	} else {
    int iLED = luaL_checkinteger(L, 1);
    int iState = luaL_checkinteger(L, 2);
    String Caption;
	if (n == 3) {
		Caption = luaL_checkstring(L, 3);
        } else {
        Caption = "";
        }
    switch(iLED) {
    	case 1:
		    ptrTIUCommThread->SetPCLED(ptrPC_local->UserLED1, iState, Caption);
		    bretv = true;
            break;
    	case 2:
		    ptrTIUCommThread->SetPCLED(ptrPC_local->UserLED2, iState, Caption);
		    bretv = true;
            break;
    	case 3:
		    ptrTIUCommThread->SetPCLED(ptrPC_local->UserLED3, iState, Caption);
		    bretv = true;
            break;
    	case 4:
		    ptrTIUCommThread->SetPCLED(ptrPC_local->UserLED4, iState, Caption);
		    bretv = true;
            break;
        default:
        	bretv = false;
            break;
        }
    }
lua_pushboolean(L, bretv);
return 1; //we are returning one value
}
//---------------------------------------------------------------------------
// Info(EngineNo)
// Returns information about the engine, returns 6 values
// 1. Engine Type as an integer
// 2. Scaling Factor as a Number
// 3. Last Sent Speed Value
// 4. Motion Sound Request
// 5. Auto Bell
// 6. Auto Whistle
//
// Returns nil on error
static int luaH_Info(lua_State *L) {
//
int n = lua_gettop(L);	//n is the number of arguments, use if needed
if (n != 1) {
    HARDERROR(ptrPC_local->PCMessages, Format((String)"Unneeded arguments in call to Info() - got %d need 1", ARRAYOFCONST((n))), (TColor) clRed);		// Display in Message List
    lua_pushnil(L);
   	return 1; //we are returning one value
    } else {
   	byte iEngineNo = luaL_checkinteger(L,1);
    luaL_argcheck(L, iEngineNo >= ENGINEMIN && iEngineNo <= ENGINEMAX, 1, "Engine Number out of range");
	lua_pushinteger(L, PSType[iEngineNo]);
	lua_pushnumber(L, ScalingFactor[iEngineNo]);
    lua_pushinteger(L, ptrPC_local->ptrPC_Thread->LastSentSpeedValue[iEngineNo]);
    lua_pushboolean(L, ptrPC_local->ptrPC_Thread->MotionSoundRequest[iEngineNo]);
    lua_pushboolean(L, ptrPC_local->ptrPC_Thread->AutoBell[iEngineNo]);
    lua_pushboolean(L, ptrPC_local->ptrPC_Thread->AutoWhistle[iEngineNo]);
    }
return 6; //we are returning six values
}
//---------------------------------------------------------------------------
// GetName(Index)
// if Index is less than ENGINEMAX
// Returns the engine name from the main window Engine List as a string and the PS level as an integer
// otherwise
//  Returns the lashup name from the Lashup Window Lashup List as a string and the Engine List as a string
//
// Returns nil on error
static int luaH_GetName(lua_State *L) {
int n = lua_gettop(L);	//n is the number of arguments, use if needed
if (n != 1)  {
    HARDERROR(ptrPC_local->PCMessages, Format((String)"Wrong # of arguments in call to GetName() - got %d need 1", ARRAYOFCONST((n))), (TColor) clRed);		// Display in Message List
    lua_pushnil(L);
   	return 1; //we are returning one value
    } else {
    byte Index = luaL_checkinteger(L,1);
    if (Index <= ENGINEMAX){
    	AnsiString EM = "";// AnsiString needed because .c_str() is taken and char string required
    int PS;
    ptrTIUCommThread->GetStringGridText(MainForm->EngineData, EM, PS, Index);
	// returns the Engine Model or an empty "" string if Index is out of range
	lua_pushstring(L, EM.c_str());
    lua_pushinteger(L, PS);
        } else {
    	AnsiString LashupName = "";// AnsiString needed because .c_str() is taken and char string required
    	AnsiString EngineList = "";// AnsiString needed because .c_str() is taken and char string required
        ptrTIUCommThread->GetStringGridLashup(LashUp->LashUpData, LashupName, EngineList, Index);
    	// returns the Lashup Name, a "*" if no lashup exists at that index or an empty "" string if Index is out of range
    	lua_pushstring(L, LashupName.c_str());
        luaH_CSVIntegersToLuaTable(L, EngineList.c_str());   // returns the table on the top of the stack
        }
    }
        return 2;	// return two values
}
//---------------------------------------------------------------------------
// GetEngineName(Index)
// Returns a string describing the Engine Model or an empty string "" if the Index is out of range
// Returns nil on error
static int luaH_GetEngineName(lua_State *L) {
int n = lua_gettop(L);	//n is the number of arguments, use if needed
if (n != 1)  {
    HARDERROR(ptrPC_local->PCMessages, Format((String)"Wrong # of arguments in call to GetEngineName() - got %d need 1", ARRAYOFCONST((n))), (TColor) clRed);		// Display in Message List
    lua_pushnil(L);
   	return 1; //we are returning one value
    } else {
    byte Index = luaL_checkinteger(L,1);
	AnsiString EM;  // AnsiString needed because .c_str() is taken and char string required
    ptrTIUCommThread->GetComboBoxText(MainForm->EngineModelComboBox, EM, Index);
	// returns the Engine Model or an empty "" string if Index is out of range
	lua_pushstring(L, EM.c_str());
    }
return 1;	// return one string
}
//---------------------------------------------------------------------------
// GetCarName(Index)
// Returns a string describing the Car Model or an empty string "" if the Index is out of range
// Returns nil on error
static int luaH_GetCarName(lua_State *L) {
int n = lua_gettop(L);	//n is the number of arguments, use if needed
if (n != 1)  {
    HARDERROR(ptrPC_local->PCMessages, Format((String)"Wrong # of arguments in call to GetCarName() - got %d need 1", ARRAYOFCONST((n))), (TColor) clRed);		// Display in Message List
    lua_pushnil(L);
   	return 1; //we are returning one value
    } else {
    byte Index = luaL_checkinteger(L,1);
	AnsiString CM;// AnsiString needed because .c_str() is taken and char string required
    ptrTIUCommThread->GetComboBoxText(MainForm->CarModelComboBox, CM, Index);
	// returns the Car Model or an empty "" string if Index is out of range
	lua_pushstring(L, CM.c_str());
    }
return 1;	// return one string
}
//---------------------------------------------------------------------------
// GetRailroadName(Index)
// Returns a string describing the Railroad Name or an empty string "" if the Index is out of range
// Returns nil on error
static int luaH_GetRailroadName(lua_State *L) {
int n = lua_gettop(L);	//n is the number of arguments, use if needed
if (n != 1)  {
    HARDERROR(ptrPC_local->PCMessages, Format((String)"Wrong # of arguments in call to GetRailroadName() - got %d need 1", ARRAYOFCONST((n))), (TColor) clRed);		// Display in Message List
    lua_pushnil(L);
   	return 1; //we are returning one value
    } else {
    byte Index = luaL_checkinteger(L,1);
	AnsiString RRN;// AnsiString needed because .c_str() is taken and char string required
    ptrTIUCommThread->GetComboBoxText(MainForm->RailroadNameComboBox, RRN, Index);
	// returns the Railroad Name or an empty "" string if Index is out of range
	lua_pushstring(L, RRN.c_str());
    }
return 1;	// return one string
}
//---------------------------------------------------------------------------
// GetEngineType(Index)
// Returns a string describing the Engine Type or an empty string "" if the Index is out of range
// Returns nil on error
static int luaH_GetEngineType(lua_State *L) {
int n = lua_gettop(L);	//n is the number of arguments, use if needed
if (n != 1)  {
    HARDERROR(ptrPC_local->PCMessages, Format((String)"Wrong # of arguments in call to GetEngineType() - got %d need 1", ARRAYOFCONST((n))), (TColor) clRed);		// Display in Message List
    lua_pushnil(L);
   	return 1; //we are returning one value
    } else {
    byte Index = luaL_checkinteger(L,1);
	AnsiString S;// AnsiString needed because .c_str() is taken and char string required
    ptrTIUCommThread->GetComboBoxText(MainForm->EngineTypeComboBox, S, Index);
	// returns the Engine Type or an empty "" string if Index is out of range
	lua_pushstring(L, S.c_str());
    }
return 1;	// return one string
}
//---------------------------------------------------------------------------
// GetTagLocation(Index)
// Returns a string describing the Tag Location or an empty string "" if the Index is out of range
// Returns nil on error
static int luaH_GetTagLocation(lua_State *L) {
int n = lua_gettop(L);	//n is the number of arguments, use if needed
if (n != 1)  {
    HARDERROR(ptrPC_local->PCMessages, Format((String)"Wrong # of arguments in call to GetTagLocation() - got %d need 1", ARRAYOFCONST((n))), (TColor) clRed);		// Display in Message List
    lua_pushnil(L);
   	return 1; //we are returning one value
    } else {
    byte Index = luaL_checkinteger(L,1);
	AnsiString S;// AnsiString needed because .c_str() is taken and char string required
    ptrTIUCommThread->GetComboBoxText(MainForm->TagLocationComboBox, S, Index);
	// returns the Tag Location or an empty "" string if Index is out of range
	lua_pushstring(L, S.c_str());
    }
return 1;	// return one string
}
//---------------------------------------------------------------------------
// GetCouplerLocation(Index)
// Returns a string describing the Coupler Locations or an empty string "" if the Index is out of range
// Returns nil on error
static int luaH_GetCouplerLocation(lua_State *L) {
int n = lua_gettop(L);	//n is the number of arguments, use if needed
if (n != 1)  {
    HARDERROR(ptrPC_local->PCMessages, Format((String)"Wrong # of arguments in call to GetCouplerLocation() - got %d need 1", ARRAYOFCONST((n))), (TColor) clRed);		// Display in Message List
    lua_pushnil(L);
   	return 1; //we are returning one value
    } else {
    byte Index = luaL_checkinteger(L,1);
	AnsiString S;// AnsiString needed because .c_str() is taken and char string required
    ptrTIUCommThread->GetComboBoxText(MainForm->CouplerComboBox, S, Index);
	// returns the Coupler Location or an empty "" string if Index is out of range
	lua_pushstring(L, S.c_str());
    }
return 1;	// return one string
}
//---------------------------------------------------------------------------
// GetSwitchPositionNames()
// Returns two strings containing the words used to describe switch positions : "Normal" - "Reverse"
// Returns nil on error
static int luaH_GetSwitchPositionNames(lua_State *L) {
//int n = lua_gettop(L);	//n is the number of arguments, use if needed
	// returns the words
	AnsiString SS, SD;	// AnsiString needed because .c_str() is taken and char string required
	SS = Setup->SwitchStraight;
	SD = Setup->SwitchDiverge;
	lua_pushstring(L, SS.c_str());
	lua_pushstring(L, SD.c_str());
return 2;	// return two strings
}
//---------------------------------------------------------------------------
// GetAccStatus(Channel, AIU, TIU)
// return the accessory status
// true = on
// false = off
static int luaH_GetAccStatus(lua_State *L) {
int n = lua_gettop(L);	//n is the number of arguments
if (n != 3) {
    HARDERROR(ptrPC_local->PCMessages, Format((String)"Wrong # of arguments in call to GetAccStatus() - got %d need 3", ARRAYOFCONST((n))), (TColor) clRed);		// Display in Message List
    lua_pushnil(L);
   	return 1; //we are returning one value
	}
int iChannel = luaL_checkinteger(L, 1);
if (iChannel < 1 || iChannel > CHANMAX) {
    HARDERROR(ptrPC_local->PCMessages, Format((String)"Invalid Channel number in call to GetAccStatus() - got %d", ARRAYOFCONST((iChannel))), (TColor) clRed);		// Display in Message List
    lua_pushnil(L);
   	return 1; //we are returning one value
	}

int iAIU = luaL_checkinteger(L, 2);
int iTIU = luaL_checkinteger(L, 3);
if (iAIU > AIUMAX || iAIU < 1) {
    HARDERROR(ptrPC_local->PCMessages, Format((String)"Invalid AIU number in call to GetAccStatus() - got %d", ARRAYOFCONST((iAIU))), (TColor) clRed);		// Display in Message List
    lua_pushnil(L);
   	return 1; //we are returning one value
	}
// if the AIU window is not open, then open it then fetch the requested value as a boolean value
bool bState;
ptrTIUCommThread->GetAIUAccState(iChannel, iAIU, iTIU, bState, ptrPC_local->ptrPC_Thread->MyPCNo);
//
lua_pushboolean(L, bState);
return 1;	// return one boolean
}
//---------------------------------------------------------------------------
// GetSwitchStatus(Channel, AIU, TIU)
// return the switch status
// true = normal
// false = reverse
static int luaH_GetSwitchStatus(lua_State *L) {
int n = lua_gettop(L);	//n is the number of arguments
if (n != 3) {
    HARDERROR(ptrPC_local->PCMessages, Format((String)"Wrong # of arguments in call to GetSwitchStatus() - got %d need 3", ARRAYOFCONST((n))), (TColor) clRed);		// Display in Message List
    lua_pushnil(L);
   	return 1; //we are returning one value
	}
int iChannel = luaL_checkinteger(L, 1);
if (iChannel < 1 || iChannel > CHANMAX) {
    HARDERROR(ptrPC_local->PCMessages, Format((String)"Invalid Channel number in call to GetSwitchStatus() - got %d", ARRAYOFCONST((iChannel))), (TColor) clRed);		// Display in Message List
    lua_pushnil(L);
   	return 1; //we are returning one value
	}
int iAIU = luaL_checkinteger(L, 2);
int iTIU = luaL_checkinteger(L, 3);
if (iAIU > AIUMAX || iAIU < 1) {
    HARDERROR(ptrPC_local->PCMessages, Format((String)"Invalid AIU number in call to GetSwitchStatus() - got %d", ARRAYOFCONST((iAIU))), (TColor) clRed);		// Display in Message List
    lua_pushnil(L);
   	return 1; //we are returning one value
	}
// if the AIU window is not open, then open it then fetch the requested value as a boolean value
bool bState;
ptrTIUCommThread->GetAIUSwitchState(iChannel, iAIU, iTIU, bState, ptrPC_local->ptrPC_Thread->MyPCNo);
//
lua_pushboolean(L, bState);
return 1;	// return one boolean
}
//---------------------------------------------------------------------------
// GetSwitchName(Channel, AIU, TIU)
// return the switch name from the AIU window as a string

static int luaH_GetSwitchName(lua_State *L) {
int n = lua_gettop(L);	//n is the number of arguments
if (n != 3) {
    HARDERROR(ptrPC_local->PCMessages, Format((String)"Wrong # of arguments in call to GetSwitchName() - got %d need 3", ARRAYOFCONST((n))), (TColor) clRed);		// Display in Message List
    lua_pushnil(L);
   	return 1; //we are returning one value
	}
int iChannel = luaL_checkinteger(L, 1);
if (iChannel < 1 || iChannel > CHANMAX) {
    HARDERROR(ptrPC_local->PCMessages, Format((String)"Invalid Channel number in call to GetSwitchName() - got %d", ARRAYOFCONST((iChannel))), (TColor) clRed);		// Display in Message List
    lua_pushnil(L);
   	return 1; //we are returning one value
	}
int iAIU = luaL_checkinteger(L, 2);
int iTIU = luaL_checkinteger(L, 3);
if (iAIU > AIUMAX || iAIU < 1) {
    HARDERROR(ptrPC_local->PCMessages, Format((String)"Invalid AIU number in call to GetSwitchName() - got %d", ARRAYOFCONST((iAIU))), (TColor) clRed);		// Display in Message List
    lua_pushnil(L);
   	return 1; //we are returning one value
	}
// if the AIU window is not open, then open it then fetch the requested value as a string
AnsiString sName; // AnsiString needed because .c_str() is taken and char string required
ptrTIUCommThread->GetAIUSwitchName(iChannel, iAIU, iTIU, sName, ptrPC_local->ptrPC_Thread->MyPCNo);
//
lua_pushstring(L, sName.c_str());
return 1;	// return one boolean
}
//---------------------------------------------------------------------------
// GetAccName(Channel, AIU, TIU)
// return the accessory name from the AIU window as a string

static int luaH_GetAccName(lua_State *L) {
int n = lua_gettop(L);	//n is the number of arguments
if (n != 3) {
    HARDERROR(ptrPC_local->PCMessages, Format((String)"Wrong # of arguments in call to GetAccName() - got %d need 3", ARRAYOFCONST((n))), (TColor) clRed);		// Display in Message List
    lua_pushnil(L);
   	return 1; //we are returning one value
	}
int iChannel = luaL_checkinteger(L, 1);
if (iChannel < 1 || iChannel > CHANMAX) {
    HARDERROR(ptrPC_local->PCMessages, Format((String)"Invalid Channel number in call to GetAccName() - got %d", ARRAYOFCONST((iChannel))), (TColor) clRed);		// Display in Message List
    lua_pushnil(L);
   	return 1; //we are returning one value
	}
int iAIU = luaL_checkinteger(L, 2);
int iTIU = luaL_checkinteger(L, 3);
if (iAIU > AIUMAX || iAIU < 1) {
    HARDERROR(ptrPC_local->PCMessages, Format((String)"Invalid AIU number in call to GetAccName() - got %d", ARRAYOFCONST((iAIU))), (TColor) clRed);		// Display in Message List
    lua_pushnil(L);
   	return 1; //we are returning one value
	}
// if the AIU window is not open, then open it then fetch the requested value as a string
AnsiString sName; // AnsiString needed because .c_str() is taken and char string required
ptrTIUCommThread->GetAIUAccName(iChannel, iAIU, iTIU, sName, ptrPC_local->ptrPC_Thread->MyPCNo);
//
lua_pushstring(L, sName.c_str());
return 1;	// return one string
}
//---------------------------------------------------------------------------
// MakeItSo(AIU, TIU)
// set all switch and accessory positions on the given AIU
// returns true on success, nil on parameter error
static int luaH_MakeItSo(lua_State *L) {
bool bretv = false;
int n = lua_gettop(L);	//n is the number of arguments
if (n != 2) {
    HARDERROR(ptrPC_local->PCMessages, Format((String)"Wrong # of arguments in call to MakeItSo() - got %d need 2", ARRAYOFCONST((n))), (TColor) clRed);		// Display in Message List
    lua_pushnil(L);
   	return 1; //we are returning one value
	}
int iAIU = luaL_checkinteger(L, 1);
int iTIU = luaL_checkinteger(L, 2);
if (iAIU > AIUMAX || iAIU < 1) {
    HARDERROR(ptrPC_local->PCMessages, Format((String)"Invalid AIU number in call to MakeItSo() - got %d", ARRAYOFCONST((iAIU))), (TColor) clRed);		// Display in Message List
    lua_pushnil(L);
   	return 1; //we are returning one value
	}
ptrTIUCommThread->MakeItSo(iAIU, iTIU, ptrPC_local->ptrPC_Thread->MyPCNo);
bretv = true;
lua_pushboolean(L, bretv);
return 1;	// return one string
}
//---------------------------------------------------------------------------
// ClearGlobals();
// the global integers are set to zero and the global strings are set to ""
static int luaH_ClearGlobals(lua_State *L) {

EnterCriticalSection(&MainForm->Global_Flag_CS);
// Set the GlobalInt[] and GlobalString[] arrays

for (int i = 0 ; i < GLOBALINTLIMIT ; i++) {
	MainForm->GlobalInt[i] = 0;       // zero
    }
for (int i = 0 ; i < GLOBALSTRINGLIMIT ; i++) {
    MainForm->GlobalString[i] = "";   // empty string
    }
LeaveCriticalSection(&MainForm->Global_Flag_CS);

lua_pushboolean(L, true);
return 1;	// return one string
}
//---------------------------------------------------------------------------
// SetGlobalInt(index, Value)
// Set a value into one of the GLOBALINTLIMIT entry GlobalInt[] array
// returns nil on error
static int luaH_SetGlobalInt(lua_State *L) {
bool bretv = false;
int n = lua_gettop(L);	//n is the number of arguments
if (n != 2) {
    HARDERROR(ptrPC_local->PCMessages, Format((String)"Wrong # of arguments in call to SetGlobalInt() - got %d need 2", ARRAYOFCONST((n))), (TColor) clRed);		// Display in Message List
    lua_pushnil(L);
   	return 1; //we are returning one value
	}
const int index = luaL_checkinteger(L, 1);
const int iValue = luaL_checkinteger(L, 2);
luaL_argcheck(L, index >= 1 && index <= GLOBALINTLIMIT, 1, "SetGlobalInt() index out of range)");

EnterCriticalSection(&MainForm->Global_Flag_CS);
// Set the GlobalInt[] array
MainForm->GlobalInt[index - 1] = iValue;	// adjust for C index starting at 0
LeaveCriticalSection(&MainForm->Global_Flag_CS);

bretv = true;
lua_pushboolean(L, bretv);
return 1;
}
//---------------------------------------------------------------------------
// TrySetGlobalInt(index, Value)
// Set a non-zero value into one of the GLOBALINTLIMIT entry GlobalInt[] array
// This function will only succeed if the global at [index-1] is zero or equal to value
// returns false if we can't get the lock on the global at [index-1] (DOES NOT WAIT for the lock),
// returns true if we successfully got the lock on the global at [index-1]
// returns nil on error
static int luaH_TrySetGlobalInt(lua_State *L) {
bool bretv = false;
int n = lua_gettop(L);	//n is the number of arguments
if (n != 2) {
    HARDERROR(ptrPC_local->PCMessages, Format((String)"Wrong # of arguments in call to TrySetGlobalInt() - got %d need 2", ARRAYOFCONST((n))), (TColor) clRed);		// Display in Message List
    lua_pushnil(L);
   	return 1; //we are returning one value
	}
const int index = luaL_checkinteger(L, 1);
const int iValue = luaL_checkinteger(L, 2);
luaL_argcheck(L, index >= 1 && index <= GLOBALINTLIMIT, 1, "TrySetGlobalInt() index out of range)");

EnterCriticalSection(&MainForm->Global_Flag_CS);
// Check if we can gain a lock on the GlobalInt[] array
const int iFlag = MainForm->GlobalInt[index - 1];             // adjust for C index starting at 0
if ((iFlag == 0) || (iFlag == iValue)) { // success if GlobalInt[] equals 0 or if it equals iValue
    // Set the GlobalInt[] array
	MainForm->GlobalInt[index - 1] = iValue;		// adjust for C index starting at 0
    bretv = true;
	} else {
    bretv = false;
    }
LeaveCriticalSection(&MainForm->Global_Flag_CS);

lua_pushboolean(L, bretv);
return 1;
}
//---------------------------------------------------------------------------
// TryMultiSetGlobalInt(table, Value)
// Set a non-zero value into all of the GlobalInt[] array locations listed in the table.
// This function will only succeed if all of the globals are zero or equal to value
// returns two values
// 1: returns false if we can't get the lock on all of the globals (DOES NOT WAIT for the lock),
//     returns true if we successfully got the lock on all of the globals
//     returns nil on parameter error
// 2: if we can't get a lock on a block, return the block number of block, otherwise return 0.
//
static int luaH_TryMultiSetGlobalInt(lua_State *L) {
int n = lua_gettop(L);	//n is the number of arguments
if (n != 2) {
    HARDERROR(ptrPC_local->PCMessages, Format((String)"Wrong # of arguments in call to TryMultiSetGlobalInt() - got %d need 2", ARRAYOFCONST((n))), (TColor) clRed);		// Display in Message List
    lua_pushnil(L);
    lua_pushnil(L);
   	return 2; //we are returning two values
	}
// first parameter must be a table
if (lua_istable(L, 1) != 1) {
    HARDERROR(ptrPC_local->PCMessages, Format((String)"First argument in call to TryMultiSetGlobalInt() must be a table", ARRAYOFCONST((n))), (TColor) clRed);		// Display in Message List
    lua_pushnil(L);
    lua_pushnil(L);
   	return 2; //we are returning two values
    }

int tablen = lua_rawlen(L, 1);
int iValue = luaL_checkinteger(L, 2);   // second parameter must be an integer

// check that all indecies are in range
for (int tableindex = 1 ; tableindex <= tablen; tableindex++) {
	lua_Integer n = tableindex;
    lua_rawgeti(L, 1, n);	// result on stack
    const int block = luaL_checkinteger(L, -1);
    lua_pop(L, 1);
	luaL_argcheck(L, block >= 1 && block <= GLOBALINTLIMIT, 1, "luaH_TryMultiSetGlobalInt() index out of range");
	}
lua_pop(L, 1);
// indecies are good, do the check

EnterCriticalSection(&MainForm->Global_Flag_CS);
// first, check that all globals integers referenced in the table are equal to 0 or equal to iValue.
for (int tableindex = 1 ; tableindex <= tablen; tableindex++) {
	lua_Integer n = tableindex;
    lua_rawgeti(L, 1, n);	// result on stack
    const int block = luaL_checkinteger(L, -1);
    lua_pop(L, 1);
    const int iFlag = MainForm->GlobalInt[block - 1];			// adjust for C index starting at 0
    // Check if we can gain a lock on the GlobalInt[] array
	if ((iFlag != 0) && (iFlag != iValue)) {	// fail if global int not equals 0 and if it not equal to iValue
		LeaveCriticalSection(&MainForm->Global_Flag_CS);
		lua_pushboolean(L, false);	// first return value
        lua_pushinteger(L, block);	// second return value
        // can't get a lock on all of the indecies
		return 2; // we are returning two values
		}
    }
// Got a lock, set all GlobalInt[] to iValue (this chunk of code can't fail)
for (int tableindex = 1 ; tableindex <= tablen; tableindex++) {
	lua_Integer n = tableindex;
    lua_rawgeti(L, 1, n);	// result on stack
    const int block = luaL_checkinteger(L, -1);
    lua_pop(L, 1);
    // Set the GlobalInt[] array
    MainForm->GlobalInt[block - 1] = iValue;		// adjust for C index starting at 0
	}
LeaveCriticalSection(&MainForm->Global_Flag_CS);

lua_pushboolean(L, true);	// first return value
lua_pushinteger(L, 0);		// second return value
return 2; // we are returning two values
}
//---------------------------------------------------------------------------
// GetGlobalInt(index)
// Get a value from one of the GLOBALINTLIMIT entry global integer array
// returns nil on error
static int luaH_GetGlobalInt(lua_State *L) {
int n = lua_gettop(L);	//n is the number of arguments
if (n != 1) {
    HARDERROR(ptrPC_local->PCMessages, Format((String)"Wrong # of arguments in call to GetGlobalInt() - got %d need 1", ARRAYOFCONST((n))), (TColor) clRed);		// Display in Message List
    lua_pushnil(L);
   	return 1; //we are returning one value
	}
const int index = luaL_checkinteger(L, 1);
luaL_argcheck(L, index >= 1 && index <= GLOBALINTLIMIT, 1, "GetGlobalInt() index out of range");

EnterCriticalSection(&MainForm->Global_Flag_CS);
const int iValue = MainForm->GlobalInt[index - 1];			// adjust for C index starting at 0
LeaveCriticalSection(&MainForm->Global_Flag_CS);

lua_pushinteger(L, iValue);
return 1;
}
//---------------------------------------------------------------------------
// SetGlobalString(index, sText)
// Set a string into one of the GLOBALSTRINGLIMIT entry global String array
// returns nil on error
static int luaH_SetGlobalString(lua_State *L) {
AnsiString sText = "";
bool bretv = false;
int n = lua_gettop(L);	//n is the number of arguments
if (n != 2) {
    HARDERROR(ptrPC_local->PCMessages, Format((String)"Wrong # of arguments in call to SetGlobalString() - got %d need 2", ARRAYOFCONST((n))), (TColor) clRed);		// Display in Message List
    lua_pushnil(L);
   	return 1; //we are returning one value
	}
const int index = luaL_checkinteger(L, 1);
sText = luaL_checkstring(L, 2);
luaL_argcheck(L, index >= 1 && index <= GLOBALSTRINGLIMIT, 1, "SetGlobalString() index out of range");

EnterCriticalSection(&MainForm->Global_Flag_CS);
MainForm->GlobalString[index - 1] = sText;              // adjust for C index starting at 0
LeaveCriticalSection(&MainForm->Global_Flag_CS);

bretv = true;
lua_pushboolean(L, bretv);
return 1;
}
//---------------------------------------------------------------------------
// GetGlobalString(index)
// Get a string from one of the GLOBALSTRINGLIMIT entry global String array
// returns nil on error
static int luaH_GetGlobalString(lua_State *L) {
AnsiString sText = "";// AnsiString needed because .c_str() is taken and char string required
int n = lua_gettop(L);	//n is the number of arguments
if (n != 1) {
    HARDERROR(ptrPC_local->PCMessages, Format((String)"Wrong # of arguments in call to GetGlobalString() - got %d need 1", ARRAYOFCONST((n))), (TColor) clRed);		// Display in Message List
    lua_pushnil(L);
   	return 1; //we are returning one value
	}
int index = luaL_checkinteger(L, 1);
luaL_argcheck(L, index >= 1 && index <= GLOBALSTRINGLIMIT, 1, "GetGlobalString() index out of range");

EnterCriticalSection(&MainForm->Global_Flag_CS);
sText = MainForm->GlobalString[index - 1];              // adjust for C index starting at 0
LeaveCriticalSection(&MainForm->Global_Flag_CS);

lua_pushstring(L, sText.c_str());
return 1;
}
//---------------------------------------------------------------------------
// SetEvent(DestPCNo, EventType, P1, P2, P3)
// Send a message to a Program Control Window in the form of an event
// Passing the data P1/P2/P3 using a thread safe FIFO
// Only DestPCNo is required, if EventType/P1/P2/P3 are not given, zero is passed
// Returns nil on error, true on success
//
static int luaH_SetEvent(lua_State *L) {
int n = lua_gettop(L);	//n is the number of arguments
if (n == 0) {
    HARDERROR(ptrPC_local->PCMessages, Format((String)"Not enough arguments in call to SetEvent() - got %d need at least 1", ARRAYOFCONST((n))), (TColor) clRed);		// Display in Message List
    lua_pushnil(L);
   	return 1; //we are returning one value
	}
int iDestPCNo = luaL_checkinteger(L, 1);
EventItem * EvItem = new EventItem;	// deleted here on failure or upon success, in ProcessEventEvent()
EvItem->iFromPCNo = ptrPC_local->ptrPC_Thread->MyPCNo;
EvItem->iDestPCNo = iDestPCNo;
EvItem->EventType = (n > 1 ) ? luaL_checkinteger(L, 2) : 0;
EvItem->iP1 = (n > 2 ) ? luaL_checkinteger(L, 3) : 0;
EvItem->iP2 = (n > 3 ) ? luaL_checkinteger(L, 4) : 0;
EvItem->iP3 = (n > 4 ) ? luaL_checkinteger(L, 5) : 0;
//
if (ptrProgram_Control[iDestPCNo - 1] != NULL) {
	if (ptrProgram_Control[iDestPCNo - 1]->ptrPC_Thread != NULL) {
        // add to the end of the Event list
        if (Event_FIFO[iDestPCNo - 1] != NULL) {
            TList *myList = Event_FIFO[iDestPCNo - 1]->LockList();
            assert(myList);
            Event_FIFO[iDestPCNo - 1]->Add((void *)(EvItem));   // Thread Safe
            // Signal PC_Thread that an event is ready (may be this or another PC_Thread)
            SetEvent(ptrProgram_Control[iDestPCNo - 1]->EventPCEvent);
            Event_FIFO[iDestPCNo - 1]->UnlockList();
            }
    	} else {
        delete EvItem;
        lua_pushnil(L);	// No PC Thread
        return 1;
        }
    } else {
    delete EvItem;
    lua_pushnil(L);		// No PC window
    return 1;
    }
lua_pushboolean(L, true);	// success
return 1;
}
//---------------------------------------------------------------------------
// Popup(Message, Caption, Status, Delay)
// Shows a popup Message with Caption and 2nd line Status for Delay seconds
// Caption defaults to "Information"
// 2nd line Status default to NULL
// Delay defaults to 2 seconds
// Returns nil on error, true on success
//
static int luaH_Popup(lua_State *L) {
AnsiString Message, Caption = "Information", Status = "";
int Delay;
int n = lua_gettop(L);	//n is the number of arguments
if (n < 1) {
    HARDERROR(ptrPC_local->PCMessages, Format((String)"Not enough arguments in call to Popup() - got %d need at least 1", ARRAYOFCONST((n))), (TColor) clRed);		// Display in Message List
    lua_pushnil(L);
   	return 1; //we are returning one value
	}
Message = luaL_checkstring(L, 1);	// this parameter may be an empty string
if (n > 1) {
	Caption = luaL_checkstring(L, 2);
    if (Caption.IsEmpty()) Caption = "Information";	// this parameter cannot be an empty string
    }
if (n > 2) Status = luaL_checkstring(L, 3);	// this parameter may be an empty string
if (n > 3) {
	Delay = 1000.0 * luaL_checknumber(L, 4);
    if (Delay == 0) Delay = UIPOPUPTIMEOUT;	// this parameter may be zero
    }
	else Delay = UIPOPUPTIMEOUT;

// Call VCL based routine to display the Message in a Popup window
ptrTIUCommThread->PopupMessage(Message, Caption, Status, Delay);
lua_pushboolean(L, true);	// success
return 1;
}

//---------------------------------------------------------------------------
// SetFlag(FlagNo, iState)
// Set a value (iState) into one of the Flags' "Tag" & "Checked" properties
// Does not check the contents of the Flag beforehand, just overwrites it
// returns -1 on error (invalid flag number) otherwise returns the value written into the flag
static int luaH_SetFlag(lua_State *L) {
int n = lua_gettop(L);	//n is the number of arguments
if (n != 2) {
    HARDERROR(ptrPC_local->PCMessages, Format((String)"Wrong # of arguments in call to SetFlag() - got %d need 2", ARRAYOFCONST((n))), (TColor) clRed);		// Display in Message List
    lua_pushnil(L);
   	return 1; //we are returning one value
	}
int index = luaL_checkinteger(L, 1);
int iState = luaL_checkinteger(L, 2);
luaL_argcheck(L, index >= 1 && index <= FLAGLIMIT, 1, "SetFlag() flag number out of range");

// Requires a Layout window be shown, call ShowLayout() if needed
EnterCriticalSection(&MainForm->Global_Flag_CS);
// set flag on Layout Window
ptrTIUCommThread->SetFlag(index, iState);   // protected with Global_Flag_CS critical section
LeaveCriticalSection(&MainForm->Global_Flag_CS);

lua_pushinteger(L, iState);
return 1;
}
//---------------------------------------------------------------------------
// GetFlag(FlagNo)
// Get a value from one of the FLAGLIMIT Flags' "Tag" property, returns it as int
// returns -1 on error (invalid flag number)
static int luaH_GetFlag(lua_State *L) {
int iState;
int n = lua_gettop(L);	//n is the number of arguments
if (n != 1) {
    HARDERROR(ptrPC_local->PCMessages, Format((String)"Wrong # of arguments in call to GetFlag() - got %d need 1", ARRAYOFCONST((n))), (TColor) clRed);		// Display in Message List
    lua_pushnil(L);
   	return 1; //we are returning one value
	}
int index = luaL_checkinteger(L, 1);
luaL_argcheck(L, index >= 1 && index <= FLAGLIMIT, 1, "GetFlag() flag number out of range");

// Requires a Layout window be shown, call ShowLayout() if needed
EnterCriticalSection(&MainForm->Global_Flag_CS);
// get flag from Layout Window
ptrTIUCommThread->GetFlag(index, iState);   // protected with Global_Flag_CS critical section
LeaveCriticalSection(&MainForm->Global_Flag_CS);

lua_pushinteger(L, iState);
return 1;
}
//---------------------------------------------------------------------------
// SetFlagName(FlagNo, FlagName)
// Set a string into one of the FLAGLIMIT Flag Names
// Overwrites any previous value
// returns nil on error otherwise returns -1
static int luaH_SetFlagName(lua_State *L) {
String sText;
const int n = lua_gettop(L);	//n is the number of arguments
if (n != 2) {
    HARDERROR(ptrPC_local->PCMessages, Format((String)"Wrong # of arguments in call to SetFlagName() - got %d need 2", ARRAYOFCONST((n))), (TColor) clRed);		// Display in Message List
    lua_pushnil(L);
   	return 1; //we are returning one value
	}
const int index = luaL_checkinteger(L, 1);
sText = luaL_checkstring(L, 2);
luaL_argcheck(L, index >= 1 && index <= FLAGLIMIT, 1, "SetFlagName() flag number out of range");

int iState;
// Requires a Layout window be shown, call ShowLayout() if needed
EnterCriticalSection(&MainForm->Global_Flag_CS);
ptrTIUCommThread->SetFlagName(index, sText, iState);   // protected with Global_Flag_CS critical section
LeaveCriticalSection(&MainForm->Global_Flag_CS);

lua_pushinteger(L, iState);
return 1;
}
//---------------------------------------------------------------------------
// GetFlagName(FlagNo)
// Get a string from one of the FLAGLIMIT Flag Names
// returns nil on error otherwise returns the Flag's name
static int luaH_GetFlagName(lua_State *L) {
String sText = "";
const int n = lua_gettop(L);	//n is the number of arguments
if (n != 1) {
    HARDERROR(ptrPC_local->PCMessages, Format((String)"Wrong # of arguments in call to GetFlagName() - got %d need 1", ARRAYOFCONST((n))), (TColor) clRed);		// Display in Message List
    lua_pushnil(L);
   	return 1; //we are returning one value
	}
const int index = luaL_checkinteger(L, 1);
luaL_argcheck(L, index >= 1 && index <= FLAGLIMIT, 1, "GetFlagName() flag number out of range");

// Requires a Layout window be shown, call ShowLayout() if needed
EnterCriticalSection(&MainForm->Global_Flag_CS);
ptrTIUCommThread->GetFlagName(index, sText);   // protected with Global_Flag_CS critical section
LeaveCriticalSection(&MainForm->Global_Flag_CS);

AnsiString asText = sText; 		// need AnsiString to get char string with c_str()
lua_pushstring(L, asText.c_str());
return 1;
}

//---------------------------------------------------------------------------
// TrySetFlag(index, Value)
// Set a value into one of the Flags' "Tag" and "Checked" values
// This function will only succeed if the "Tag" value for the flag at index is zero or equal to value or equal to SOFTOCCUPY
// returns false if we can't get the lock on the flag (DOES NOT WAIT for the lock),
// returns true if we successfully got the lock on the flag
// returns nil on error
static int luaH_TrySetFlag(lua_State *L) {
bool bretv = false;
const int n = lua_gettop(L);	//n is the number of arguments
if (n != 2) {
    HARDERROR(ptrPC_local->PCMessages, Format((String)"Wrong # of arguments in call to TrySetFlag() - got %d need 2", ARRAYOFCONST((n))), (TColor) clRed);		// Display in Message List
    lua_pushnil(L);
   	return 1; //we are returning one value
	}
const int index = luaL_checkinteger(L, 1);
int iValue = luaL_checkinteger(L, 2);
luaL_argcheck(L, index >= 1 && index <= FLAGLIMIT, 1, "luaH_TrySetFlag() index out of range)");
int iFlag;

EnterCriticalSection(&MainForm->Global_Flag_CS);
// Check if we can gain a lock on the flag
ptrTIUCommThread->GetFlag(index, iFlag);   // protected with Global_Flag_CS critical section
if (iFlag == 0 || iFlag == iValue || iFlag == SOFTOCCUPY) {	// success if Flag's "Tag" value equals 0 or if it equals iValue or SOFTOCCUPY
	// set flag's "Checked" property on Layout Window
    ptrTIUCommThread->SetFlag(index, iValue);// protected with Global_Flag_CS critical section
    bretv = true;
	} else {
    bretv = false;
    }
LeaveCriticalSection(&MainForm->Global_Flag_CS);

lua_pushboolean(L, bretv);
return 1;
}
//---------------------------------------------------------------------------
// TryMultiSetFlag(table, Value)
// Set a non-zero value into all of the Flags listed in the table.
// (into all of the VCL flag's boolean "Checked" properties and "Tag" properties)
// This function will only succeed if all of the flags' "Tag" values are zero or equal to value or equal to SOFTOCCUPY
// returns two values
// 1: returns false if we can't get the lock on all of the flags (DOES NOT WAIT for the lock),
//     returns true if we successfully got the lock on all of the flags
//     returns nil on parameter error
// 2: if we can't get a lock on a block, return the block number of block, otherwise return 0.
//
static int luaH_TryMultiSetFlag(lua_State *L) {
const int n = lua_gettop(L);	//n is the number of arguments
if (n != 2) {
    HARDERROR(ptrPC_local->PCMessages, Format((String)"Wrong # of arguments in call to TryMultiSetFlag() - got %d need 2", ARRAYOFCONST((n))), (TColor) clRed);		// Display in Message List
    lua_pushnil(L);
    lua_pushnil(L);
   	return 2; //we are returning two values
	}
// first parameter must be a table
if (lua_istable(L, 1) != 1) {
    HARDERROR(ptrPC_local->PCMessages, Format((String)"First argument in call to TryMultiSetFlag() must be a table", ARRAYOFCONST((n))), (TColor) clRed);		// Display in Message List
    lua_pushnil(L);
    lua_pushnil(L);
   	return 2; //we are returning two values
    }

int tablen = lua_rawlen(L, 1);
int iValue = luaL_checkinteger(L, 2);   // second parameter must be an integer

// check that all indecies are in range
for (int tableindex = 1 ; tableindex <= tablen; tableindex++) {
	lua_Integer n = tableindex;
    lua_rawgeti(L, 1, n);	// result on stack
    const int block = luaL_checkinteger(L, -1);
    lua_pop(L, 1);
	luaL_argcheck(L, block >= 1 && block <= FLAGLIMIT, 1, "luaH_TryMultiSetFlag() index out of range");
	}
lua_pop(L, 1);
// indecies are good, do the check

EnterCriticalSection(&MainForm->Global_Flag_CS);
// first, check that all flags referenced in the table are equal to 0 or equal to iValue.
for (int tableindex = 1 ; tableindex <= tablen; tableindex++) {
	lua_Integer n = tableindex;
    lua_rawgeti(L, 1, n);	// result on stack
    const int block = luaL_checkinteger(L, -1);
    lua_pop(L, 1);
    int iFlag;
	ptrTIUCommThread->GetFlag(block, iFlag);   // protected with Global_Flag_CS critical section
    // Check if we can gain a lock on the Flag
	if ((iFlag != 0) && (iFlag != iValue) && (iFlag != SOFTOCCUPY)) {	// fail if flag not equal 0 and if it not equal to iValue
		LeaveCriticalSection(&MainForm->Global_Flag_CS);
		lua_pushboolean(L, false);	// first return value
        lua_pushinteger(L, block);	// second return value
        // can't get a lock on all of the indecies
		return 2; // we are returning two values
		}
    }

// Got a lock, set all Flags to iValue (this chunk of code can't fail)
for (int tableindex = 1 ; tableindex <= tablen; tableindex++) {
	lua_Integer n = tableindex;
    lua_rawgeti(L, 1, n);	// result on stack
    const int block = luaL_checkinteger(L, -1);
    lua_pop(L, 1);
    // Set the LED flag's "Tag" and "Checked" properties
    ptrTIUCommThread->SetFlag(block, iValue);   // protected with Global_Flag_CS critical section
	}

LeaveCriticalSection(&MainForm->Global_Flag_CS);

lua_pushboolean(L, true);	// first return value
lua_pushinteger(L, 0);		// second return value
return 2; // we are returning two values
}
//---------------------------------------------------------------------------
//
// Signals displayed on the Layout Window
//
//---------------------------------------------------------------------------
// SetAspect(iSignalNo, iAspect)
// Set the aspect (iAspect) of the given signal (iSignalNo)
// If iAspect is negative set approach lighting but the actual aspect is saved in the signal
// Does not check the aspect of the Signal beforehand, just overwrites it
// returns nil on error otherwise returns the aspect written into the signal including approach lighting status
static int luaH_SetAspect(lua_State *L) {
int n = lua_gettop(L);	//n is the number of arguments
if (n != 2) {
    HARDERROR(ptrPC_local->PCMessages, Format((String)"Wrong # of arguments in call to SetAspect() - got %d need 2", ARRAYOFCONST((n))), (TColor) clRed);		// Display in Message List
    lua_pushnil(L);
   	return 1; //we are returning one value
	}
int iSignalNo = luaL_checkinteger(L, 1);
int iAspect = luaL_checkinteger(L, 2);
luaL_argcheck(L, iSignalNo >= 1 && iSignalNo <= SIGNALLIMIT, 1, "SetAspect() signal number out of range");
luaL_argcheck(L, iAspect >= -(AsLast - 1) && iAspect <= (AsLast - 1), 2, "SetAspect() aspect out of range");
//
if (DEBUGGINGX(8)) ptrTIUCommThread->PrintItem("SetAspect on signal #" + String(iSignalNo) + " to aspect " + iAspect);
//
// Requires a Layout window be shown, call ShowLayout() if needed
EnterCriticalSection(&MainForm->Signals_CS);
ptrTIUCommThread->SetAspect(iSignalNo, iAspect);		// set signal on layout window including approach lighting
// check the Program Control window's Cab Display to see if there is a signal we should set
String sName;
int iNumberPlate;	// for Number Plate for signal on Layout window
int iType;          // for Signal Type for signal on Layout window
ptrTIUCommThread->GetSignalName(iSignalNo, sName, iNumberPlate, iType);
for (int iPCNo = 1 ; iPCNo <= PCMAX ; iPCNo++){
	if (iCabSignal[iPCNo - 1][HOMESIGNAL] == iSignalNo) {
        // set PC Window Home Signal
        ptrTIUCommThread->SetCabAspect(HOMESIGNAL, abs(iAspect), iPCNo);    //  cab display always lit
        ptrTIUCommThread->SetCabLabels(HOMESIGNAL, sName, iNumberPlate, iType, iPCNo);
        // continue the for loop to look for other uses of this signal number
    	}
	if (iCabSignal[iPCNo - 1][DISTANTSIGNAL] == iSignalNo) {
		// set PC Window Distant Signal
        ptrTIUCommThread->SetCabAspect(DISTANTSIGNAL, abs(iAspect), iPCNo); // cab display always lit
        ptrTIUCommThread->SetCabLabels(DISTANTSIGNAL, sName, iNumberPlate, iType, iPCNo);
        // continue the for loop to look for other uses of this signal number
    	}
    }
LeaveCriticalSection(&MainForm->Signals_CS);
//
lua_pushinteger(L, iAspect);    // return value includes approach status
return 1;
}
//---------------------------------------------------------------------------
// GetAspect(SignalNo)
// Returns 3 values, the signal's Aspect as an integer, the number of lights as an integer and the signal type
// always >= 0 as approach lighting status (< 0) is not returned
// returns 2 values, nil, 0 on no signal
// returns 1 value, nil on on error
static int luaH_GetAspect(lua_State *L) {
int iAspect, iMode, iType;
int n = lua_gettop(L);	//n is the number of arguments
if (n != 1) {
    HARDERROR(ptrPC_local->PCMessages, Format((String)"Wrong # of arguments in call to GetAspect() - got %d need 1", ARRAYOFCONST((n))), (TColor) clRed);		// Display in Message List
    lua_pushnil(L);
   	return 1; //we are returning one value
	}
int iSignalNo = luaL_checkinteger(L, 1);
if (iSignalNo == 0) {   // zero means there is no signal
    lua_pushnil(L);
    lua_pushinteger(L, 0);
    return 2;
    }
luaL_argcheck(L, iSignalNo >= 1 && iSignalNo <= SIGNALLIMIT, 1, "GetAspect() signal number out of range");

// Requires a Layout window be shown, call ShowLayout() if needed
EnterCriticalSection(&MainForm->Signals_CS);
ptrTIUCommThread->GetAspect(iSignalNo, iAspect, iMode, iType);		// get Aspect, number of lights and signal type from Layout Window
LeaveCriticalSection(&MainForm->Signals_CS);
//
lua_pushinteger(L, abs(iAspect));   // always >= 0 as approach lighting status (< 0) is not returned
lua_pushinteger(L, iMode);
lua_pushinteger(L, iType);
return 3;
}
//---------------------------------------------------------------------------
// CreateSignal(iSignalNo, iDir, iType, Row, Col sSignalName, iAspect, iMode, NumberPlate)
// Create a signal head on the layout window
// If the iSignalNo already exists, it is deleted first
// returns nil on error otherwise returns iAspect without approach lighting status
// Use DeleteSignal(iSignalNo) to delete a signal head
// Returns negative dark aspect
//
static int luaH_CreateSignal(lua_State *L) {
String sSignalName;
int n = lua_gettop(L);	//n is the number of arguments
if (n != 9) {
    HARDERROR(ptrPC_local->PCMessages, Format((String)"Wrong # of arguments in call to CreateSignal() - got %d need 9", ARRAYOFCONST((n))), (TColor) clRed);		// Display in Message List
    lua_pushnil(L);
   	return 1; //we are returning one value
	}
int iSignalNo = luaL_checkinteger(L, 1);
luaL_argcheck(L, iSignalNo >= 1 && iSignalNo <= SIGNALLIMIT, 1, "CreateSignal() signal number out of range");
int iDir = luaL_checkinteger(L, 2);			//  WB = 1, EB = 2, NB = 3, SB = 4
luaL_argcheck(L, iDir == WB || iDir == EB || iDir == NB || iDir == SB, 2, "CreateSignal() direction not WB/EB/NB/SB");
int iType = luaL_checkinteger(L, 3);		// Signal type
luaL_argcheck(L, iType >= 0 && iType <= LAST_SIGNAL_TYPE, 3, "CreateSignal() invalid signal type");
int Col = luaL_checkinteger(L, 4);// no limit to row or column position
int Row = luaL_checkinteger(L, 5);
sSignalName = luaL_checkstring(L, 6);
int iAspect = luaL_checkinteger(L, 7);		// must be -(AsLast-1) through (AsLast-1)
luaL_argcheck(L, iAspect >= -(AsLast - 1) && iAspect <= (AsLast - 1), 7, "CreateSignal() aspect out of range");
/*
    -- Mode is the operational type of signal:
    --     Hd1Light : one head with one tri-color light
    --     Hd2Light : one head with two tri-color lights
    --     Hd2Light : one head with three tri-color lights
    --     HdMLight : one head with three LEDs (Green - Yellow - Red)
*/
int iMode = luaL_checkinteger(L, 8);		// must be 0, 1, 2, 3, or 4. iMode == 0 is used to delete the signal.
luaL_argcheck(L, iMode >= 0 && iMode <= 4, 8, "CreateSignal() Mode is out of range (0-4)");
int iNumberPlate = luaL_checkinteger(L, 9);	// 0 means no Number Plate, any integer value is acceptable but might not display in full
if (iType == ABSOLUTE_HEAD_BLOCK) {
	luaL_argcheck(L, iNumberPlate == 0, 9, "CreateSignal() Absolute signal cannot have a Number Plate");
	} else {
	luaL_argcheck(L, iNumberPlate != 0, 9, "CreateSignal() Permissive signal must have a Number Plate");
	}

if (DEBUGGINGX(8)) ptrTIUCommThread->PrintItem("CreateSignal #" + String(iSignalNo) + " with aspect " + iAspect);

// Requires a Layout window be shown, call ShowLayout() if needed
EnterCriticalSection(&MainForm->Signals_CS);
ptrTIUCommThread->CreateSignal(iSignalNo, iDir, iType, Col, Row, sSignalName, iAspect, iMode, iNumberPlate);
// check the Program Control window's Cab Display to see if there is a signal we should set
for (int iPCNo = 1 ; iPCNo <= PCMAX ; iPCNo++){
	if (iCabSignal[iPCNo - 1][HOMESIGNAL] == iSignalNo) {
        // set PC Window Home Signal
        ptrTIUCommThread->SetCabAspect(HOMESIGNAL, abs(iAspect), iPCNo);
        ptrTIUCommThread->SetCabLabels(HOMESIGNAL, sSignalName, iNumberPlate, iType, iPCNo);
        // continue the for loop to look for other uses of this signal number
    	}
	if (iCabSignal[iPCNo - 1][DISTANTSIGNAL] == iSignalNo) {
		// set PC Window Distant Signal
        ptrTIUCommThread->SetCabAspect(DISTANTSIGNAL, abs(iAspect), iPCNo);
        ptrTIUCommThread->SetCabLabels(DISTANTSIGNAL, sSignalName, iNumberPlate, iType, iPCNo);
        // continue the for loop to look for other uses of this signal number
    	}
    }
LeaveCriticalSection(&MainForm->Signals_CS);

lua_pushinteger(L, abs(iAspect));    // does not include approach lighting status 
return 1;
}
//---------------------------------------------------------------------------
// DeleteSignal(iSignalNo)
// Delete a signal head on the layout window
// returns nil on error otherwise returns iSignalNo
//
static int luaH_DeleteSignal(lua_State *L) {
String sSignalName;
int n = lua_gettop(L);	//n is the number of arguments
if (n != 1) {
    HARDERROR(ptrPC_local->PCMessages, Format((String)"Wrong # of arguments in call to DeleteSignal() - got %d need 1", ARRAYOFCONST((n))), (TColor) clRed);		// Display in Message List
    lua_pushnil(L);
   	return 1; //we are returning one value
	}
int iSignalNo = luaL_checkinteger(L, 1);
luaL_argcheck(L, iSignalNo >= 1 && iSignalNo <= SIGNALLIMIT, 1, "DeleteSignal() signal number out of range");
if (iSignalNo > 0) {	// delete signal
	if (DEBUGGINGX(8)) ptrTIUCommThread->PrintItem("DeleteSignal() deleting signal #" + String(iSignalNo));
    // Requires a Layout window be shown, call ShowLayout() if needed
	EnterCriticalSection(&MainForm->Signals_CS);
	ptrTIUCommThread->DeleteSignal(iSignalNo);
	LeaveCriticalSection(&MainForm->Signals_CS);
    lua_pushinteger(L, iSignalNo);	// return the negative signal number
	return 1;
	}
lua_pushnil(L);
return 1;
}
//---------------------------------------------------------------------------
// GetSignalName(SignalNo)
// Returns THREE values
// Get a string from one of the SIGNALLIMIT Signal Names, an Integer from the Number Plate and the Signal Type
// returns nil on error otherwise returns the Signal's name
static int luaH_GetSignalName(lua_State *L) {
String sName = "";
int n = lua_gettop(L);	//n is the number of arguments
if (n != 1) {
    HARDERROR(ptrPC_local->PCMessages, Format((String)"Wrong # of arguments in call to GetSignalName() - got %d need 1", ARRAYOFCONST((n))), (TColor) clRed);		// Display in Message List
    lua_pushnil(L);
    lua_pushnil(L);
   	return 2; //we are returning two values
	}
int iSignalNo = luaL_checkinteger(L, 1);
luaL_argcheck(L, iSignalNo >= 1 && iSignalNo <= SIGNALLIMIT, 1, "GetSignalName() signal number out of range");
int iNumberPlate = 0;
int iType = 0;
// Requires a Layout window be shown, call ShowLayout() if needed
EnterCriticalSection(&MainForm->Signals_CS);
ptrTIUCommThread->GetSignalName(iSignalNo, sName, iNumberPlate, iType);
LeaveCriticalSection(&MainForm->Signals_CS);

AnsiString aName = sName; 		// need AnsiString to get char string with c_str()
lua_pushstring(L, aName.c_str());
lua_pushinteger(L, iNumberPlate);
lua_pushinteger(L, iType);
return 3;
}
//---------------------------------------------------------------------------
// DeleteAllSignals()
// Delete all signals in the Layout Window
static int luaH_DeleteAllSignals(lua_State *L) {
ptrTIUCommThread->DeleteAllSignals();
lua_pushboolean(L, true);
return 1;	// always return true
}
//---------------------------------------------------------------------------
// ShowLayout()
// Create and/or open the Layout Window
static int luaH_ShowLayout(lua_State *L) {
ptrTIUCommThread->ShowLayout();
lua_pushboolean(L, true);
return 1;	// always return true
}
//---------------------------------------------------------------------------
// LayoutShowing()
// Check if the layout window is being displayed
static int luaH_LayoutShowing(lua_State *L) {
if (LayoutForm) {
    lua_pushboolean(L, true);
    } else {
    lua_pushboolean(L, false);
    }
return 1;	// always return true
}
//---------------------------------------------------------------------------
//
// Cab Signals displayed on the PC window
//
//---------------------------------------------------------------------------
// SetCabSignal(Signal, Aspect, Name, NumberPlate, Type);
//
// Sets the Heads Up Display signal head (Home or Distant) to given aspect, SignalName and Number Plate.
// Name, NumberPlate and Type are optional and if not present, are not set
//
// Aspect is positive and does not include approach light status
//
// return true on success
// returns nil on error
//
static int luaH_SetCabSignal (lua_State *L) {
bool bretv = false;
int n = lua_gettop(L);	//n is the number of arguments, use if needed
if (n != 2 && n != 3 && n != 4 && n != 5) {
	HARDERROR(ptrPC_local->PCMessages, Format((String)"Wrong # of arguments in call to SetCabSignal() - got %d need 2, 3, 4, or 5", ARRAYOFCONST((n))), (TColor) clRed);		// Display in Message List
    lua_pushnil(L);
    return 1; //we are returning one value
	} else {
    int iSignal = luaL_checkinteger(L, 1);
	luaL_argcheck(L, iSignal >= HOMESIGNAL && iSignal <= DISTANTSIGNAL, 1, "SetCabSignal() cab signal ID invalid");
	int iAspect = luaL_checkinteger(L, 2);
	luaL_argcheck(L, iAspect >= 0 && iAspect <= (AsLast - 1), 2, "SetCabSignal() aspect out of range");

	ptrTIUCommThread->SetCabAspect(iSignal, iAspect, ptrPC_local->ptrPC_Thread->MyPCNo);
	AnsiString sName;
	if (n == 3) {	// Add Signal Name to Cab Signal, don't change the Number Plate
		sName = luaL_checkstring(L, 3);
		ptrTIUCommThread->SetCabLabels(iSignal, sName, -1, -1, ptrPC_local->ptrPC_Thread->MyPCNo);
		}
	if (n == 4) {	// Add Signal Name and Number Plate to Cab Signal
	    sName = luaL_checkstring(L, 3);
		int NumberPlate = luaL_checkinteger(L, 4);
		ptrTIUCommThread->SetCabLabels(iSignal, sName, NumberPlate, -1, ptrPC_local->ptrPC_Thread->MyPCNo);
		}
    if (n == 5) {	// Add Signal Name, Number Plate and Type to Cab Signal
	    sName = luaL_checkstring(L, 3);
		int NumberPlate = luaL_checkinteger(L, 4);
        int iType = luaL_checkinteger(L, 5);
		ptrTIUCommThread->SetCabLabels(iSignal, sName, NumberPlate, iType, ptrPC_local->ptrPC_Thread->MyPCNo);
    	}
    bretv = true;
    }
lua_pushboolean(L, bretv);
return 1; //we are returning one value
}
//---------------------------------------------------------------------------
// GetCabAspect(Signal);
//
// Returns 3 values, the Aspect for the given cab signal, the number of lights on the signal head and the signal type
// Does not include approach lighting status
// returns 1 value, AsNone on error or if the Cab Signal does not exist
//
static int luaH_GetCabAspect (lua_State *L) {
int n = lua_gettop(L);	//n is the number of arguments, use if needed
if (n != 1) {
	HARDERROR(ptrPC_local->PCMessages, Format((String)"Wrong # of arguments in call to GetCabAspect() - got %d need 1", ARRAYOFCONST((n))), (TColor) clRed);		// Display in Message List
    lua_pushnil(L);
    return 1; //we are returning one value
	} else {
    int iSignal = luaL_checkinteger(L, 1);
    luaL_argcheck(L, iSignal >= HOMESIGNAL && iSignal <= DISTANTSIGNAL, 1, "GetCabAspect() cab signal ID invalid");
    int iAspect;
    int iMode;
    int iType;
	ptrTIUCommThread->GetCabAspect(iSignal, iAspect, iMode, iType, ptrPC_local->ptrPC_Thread->MyPCNo);
    if (iAspect == AsNone) {
    	lua_pushinteger(L, iAspect);		// return 1 value, AsNone if the cab signal is not created
        return 1;
        }
	lua_pushinteger(L, abs(iAspect));   // does not include approach lighting status
    lua_pushinteger(L, iMode);
    lua_pushinteger(L, iType);
    return 3; //we are returning three values
    }
}
//---------------------------------------------------------------------------
// CreateCab(iHome, iDistant)
// creates the Cab Signals in the Program Control window with the mode given
// iDistant can be zero in which case, no distant signal is created in the Cab
// return true on success
// returns nil on error
//
static int luaH_CreateCab(lua_State *L) {
bool bretv = false;
int n = lua_gettop(L);	//n is the number of arguments, use if needed
if (n != 1 && n != 2) {
	HARDERROR(ptrPC_local->PCMessages, Format((String)"Wrong # of arguments in call to CreateCab() - got %d need 1 or 2", ARRAYOFCONST((n))), (TColor) clRed);		// Display in Message List
    lua_pushnil(L);
    return 1; //we are returning one value
	} else {
    // home signal is required  but can be zero
    int iHome = luaL_checkinteger(L, 1);
    luaL_argcheck(L, iHome >= HdNONE && iHome <= HdMAX, 1, "CreateCab() cab home signal ID invalid");
    // distant signal is optional and can be zero
	int iDistant = 0;
    if (n == 2) {
    	iDistant = luaL_checkinteger(L, 2);
        luaL_argcheck(L, iDistant >= HdNONE && iDistant <= HdMAX, 2, "CreateCab() cab distant signal ID invalid");
        }
	ptrTIUCommThread->CreateCabSignals(iHome, iDistant, ptrPC_local->ptrPC_Thread->MyPCNo);
    ptrPC_local->ptrPC_Thread->CabCreated = true;
    bretv = true;
    }
lua_pushboolean(L, bretv);
return 1; //we are returning one value
}
//---------------------------------------------------------------------------
// RegisterCabSignals(iHome, iDistant)
// Registers the home and distant signals for updates by Layout.cpp
//
// return true on success
// returns nil on error
//
static int luaH_RegisterCabSignals(lua_State *L) {
bool bretv = false;
int n = lua_gettop(L);	//n is the number of arguments, use if needed
if (LayoutForm == NULL) {
	HARDERROR(ptrPC_local->PCMessages, Format((String)"in RegisterCabSignals() - Layout Window must be open to register Cab signals", ARRAYOFCONST((n))), (TColor) clRed);		// Display in Message List
    lua_pushnil(L);
    return 1; //we are returning one value
	}
if (!ptrPC_local->ptrPC_Thread->CabCreated) {
	HARDERROR(ptrPC_local->PCMessages, Format((String)"in RegisterCabSignals() - Cab must be created in order to register Cab signals", ARRAYOFCONST((n))), (TColor) clRed);		// Display in Message List
    lua_pushnil(L);
    return 1; //we are returning one value
	}
if (n != 1 && n != 2) {
	HARDERROR(ptrPC_local->PCMessages, Format((String)"Wrong # of arguments in call to RegisterCabSignals() - got %d need 1 or 2", ARRAYOFCONST((n))), (TColor) clRed);		// Display in Message List
    lua_pushnil(L);
    return 1; //we are returning one value
	} else {
    // home signal is required but can be zero
    int iHome = luaL_checkinteger(L, 1);
    luaL_argcheck(L, iHome >= 0 && iHome <= SIGNALLIMIT, 1, "RegisterCabSignals() cab home signal ID invalid");
    // distant signal is optional and can be zero
	int iDistant = 0;
    if (n == 2) {
    	iDistant = luaL_checkinteger(L, 2);
        luaL_argcheck(L, iDistant >= 0 && iDistant <= SIGNALLIMIT, 2, "RegisterCabSignals() cab distant signal ID invalid");
        }
	EnterCriticalSection(&MainForm->Signals_CS);
    // register the signals - home and distant can both be zero
	iCabSignal[ptrPC_local->ptrPC_Thread->MyPCNo - 1][HOMESIGNAL] = iHome;
	iCabSignal[ptrPC_local->ptrPC_Thread->MyPCNo - 1][DISTANTSIGNAL] = iDistant;
    // check to see if the signal exists and if so, get the name and number plate into the Cab signal
    if (LayoutForm) {    // set the name, number plate and aspect into the cab display
        int iAspect, iMode, iType = 0;
        String sName;
        int iNumberPlate = 0;
        // Home signal is required but can be zero
        if (iHome != 0) {
	        ptrTIUCommThread->GetAspect(iHome, iAspect, iMode, iType);
    	    ptrTIUCommThread->SetCabAspect(HOMESIGNAL, abs(iAspect), ptrPC_local->ptrPC_Thread->MyPCNo);
        	ptrTIUCommThread->GetSignalName(iHome, sName, iNumberPlate, iType);
	        ptrTIUCommThread->SetCabLabels(HOMESIGNAL, sName, iNumberPlate, iType, ptrPC_local->ptrPC_Thread->MyPCNo);
            } else {	// no local signal so blank out the cab display
    	    ptrTIUCommThread->SetCabAspect(HOMESIGNAL, AsDark, ptrPC_local->ptrPC_Thread->MyPCNo);
	        ptrTIUCommThread->SetCabLabels(HOMESIGNAL, "**", 0, 0, ptrPC_local->ptrPC_Thread->MyPCNo);
            }
        // Distant signal is optional
        if (iDistant != 0) {
            ptrTIUCommThread->GetAspect(iDistant, iAspect, iMode, iType);
            ptrTIUCommThread->SetCabAspect(DISTANTSIGNAL, abs(iAspect), ptrPC_local->ptrPC_Thread->MyPCNo);
            ptrTIUCommThread->GetSignalName(iDistant, sName, iNumberPlate, iType);
            ptrTIUCommThread->SetCabLabels(DISTANTSIGNAL, sName, iNumberPlate, iType, ptrPC_local->ptrPC_Thread->MyPCNo);
            } else { 	// no distant signal so blank out the cab display
            ptrTIUCommThread->SetCabAspect(DISTANTSIGNAL, AsDark, ptrPC_local->ptrPC_Thread->MyPCNo);
            ptrTIUCommThread->SetCabLabels(DISTANTSIGNAL, "**", 0, 0, ptrPC_local->ptrPC_Thread->MyPCNo);
            }
        }
   	LeaveCriticalSection(&MainForm->Signals_CS);
    bretv = true;
    }
lua_pushboolean(L, bretv);
return 1; //we are returning one value
}
//---------------------------------------------------------------------------
// Yield()
// Releases the thread to check if other events have come up (expecially a termination event)
//
// return true on success
// returns false on termination event or error
//
static int luaH_Yield(lua_State *L) {
bool bretv = true;
bretv = ptrPC_local->ptrPC_Thread->MyYield(L);
lua_pushboolean(L, bretv);
return 1;
}
//---------------------------------------------------------------------------
// ReqBlockEvents(bState)
// Request the Layout Window to send BLOCK_OCCUPANCY events whenever
// a user manually changes the status of a flag to the value given "true" or "false"
// returns true on success
// return nil on failure which most likely means the Layout Window is not created
//
static int luaH_ReqBlockEvents(lua_State *L) {
bool bretv = true;
int n = lua_gettop(L);	//n is the number of arguments, use if needed
if (n != 1) {
	HARDERROR(ptrPC_local->PCMessages, Format((String)"Wrong # of arguments in call to ReqBlockEvents() - got %d need 1", ARRAYOFCONST((n))), (TColor) clRed);		// Display in Message List
    lua_pushnil(L);
    return 1; //we are returning one value
	} else {
    bool bState = lua_toboolean(L,1);
    Request_Events[ptrPC_local->ptrPC_Thread->MyPCNo - 1] = bState;
    }
lua_pushboolean(L, bretv);
return 1;
}
//---------------------------------------------------------------------------
// GetTagReaders()
// Returns 2 values, the number of Tag Detectors Connected via WiFi and
// the number of Tag Readers connected to those Tag Detectors.
// returns nil on error
static int luaH_GetTagReaders(lua_State *L) {
// Any other parameters are ignored
int Detectors, NumReaders;
ptrTIUCommThread->GetCounterValue(MainForm->NumDetectors, &Detectors);
ptrTIUCommThread->GetCounterValue(MainForm->NumReaders, &NumReaders);
lua_pushinteger(L, Detectors);      // Number of Detectors
lua_pushinteger(L, NumReaders);     // Number of Tag Readers
return 2;
}
//---------------------------------------------------------------------------
// CheckReader(int DetectorNum, int ReaderNum);
// checks that RFID detector and reader exist, if so, returns true,
// otherwise returns false. If ReadersPresent is 1, there is exactly
// 1 reader and it is number 0. If ReadersPresent is 2, there are exactly
// 2 readers and they are number 0 and number 1.
// returns nil on error
static int luaH_CheckReader(lua_State *L) {
	bool bval = false;
	int n = lua_gettop(L);	//n is the number of arguments, use if needed
    if (n != 2) {
    	HARDERROR(ptrPC_local->PCMessages, Format((String)"Wrong # of arguments in call to CheckReader() - got %d need 2", ARRAYOFCONST((n))), (TColor) clRed);		// Display in Message List
	    lua_pushnil(L);
    	return 1; //we are returning one value
		} else {
        int iDetNo = luaL_checkinteger(L,1);
        int iReadNo = luaL_checkinteger(L,2);
        luaL_argcheck(L, iDetNo >= 1 && iDetNo <= RFIDMAX, 1, "RFID Detector Number out of range");
        luaL_argcheck(L, iReadNo >= 0 && iReadNo <= 5, 2, "RFID Reader Number out of range (0-5)");
        //
        bool DetPresent = Detector[iDetNo].Found;
        int ReadersPresent = Detector[iDetNo].NumReaders;
        if (DetPresent && iReadNo < ReadersPresent) {
            bval = true;
            }
        }
    lua_pushboolean(L, bval);
    return 1; //we are returning one value
}
//---------------------------------------------------------------------------
// GetSignals()
// Returns 1 value, the number of Signals Connected via WiFi
// returns nil on error
static int luaH_GetSignals(lua_State *L) {
// Any other parameters are ignored
int NumSignals;
ptrTIUCommThread->GetCounterValue(MainForm->NumSignals, &NumSignals);
lua_pushinteger(L, NumSignals);     // Number of Signals
return 1;
}
//---------------------------------------------------------------------------
// CheckSignal(int SignalNum);
// checks that signal numbers exists, if so, returns three values - true, the IP and the Port,
// otherwise returns 1 value - false.
// returns 1 value - nil on error
static int luaH_CheckSignal(lua_State *L) {

	int n = lua_gettop(L);	//n is the number of arguments, use if needed
    if (n != 1) {
    	HARDERROR(ptrPC_local->PCMessages, Format((String)"Wrong # of arguments in call to CheckSignal() - got %d need 1", ARRAYOFCONST((n))), (TColor) clRed);		// Display in Message List
	    lua_pushnil(L);
    	return 1; //we are returning one value
		} else {
        byte iSigNo = luaL_checkinteger(L,1);
        //
        bool SignalPresent = Signals[iSigNo].Found;
        if (SignalPresent == true) {
            lua_pushboolean(L, true);
            // TAddr must be AnsiString because the c_str() taken needs to be a char type
            lua_pushstring(L, Signals[iSigNo].Addr.c_str());
            lua_pushinteger(L, Signals[iSigNo].Port);
            return 3; //we are returning three values
            } else {
            lua_pushboolean(L, false);
    		return 1; //we are returning one value
			}
    	}
}
//---------------------------------------------------------------------------
// FeatureReset(int Engine, int TIU)
// sends a Feature Reset command to the engine on the TIU
// returns true on success and nil on error
//
static int luaH_FeatureReset(lua_State *L) {
	bool bval = true;
	int n = lua_gettop(L);	//n is the number of arguments, use if needed
    if (n != 2) {
    	HARDERROR(ptrPC_local->PCMessages, Format((String)"Wrong # of arguments in call to FeatureReset() - got %d need 2", ARRAYOFCONST((n))), (TColor) clRed);		// Display in Message List
	    lua_pushnil(L);
    	return 1; //we are returning one value
		} else {
        byte iEngineNo = luaL_checkinteger(L,1);
        byte iTIUNo = luaL_checkinteger(L,2);
        luaL_argcheck(L, iEngineNo >= ENGINEMIN && iEngineNo <= ENGINEMAX, 1, "Engine Number out of range");
        luaL_argcheck(L, iTIUNo >= 1 && iTIUNo <= TIUMAX, 2, "TIU Number out of range");
        //
        bval = ptrPC_local->ptrPC_Thread->FeatureReset(iEngineNo, iTIUNo);
        if (!bval) {
        	lua_pushnil(L);
            return 1;
            }
        }
    lua_pushboolean(L, bval);
    return 1; //we are returning one value
}
//---------------------------------------------------------------------------
// FactoryReset(int Engine, int TIU)
// sends a Factory Reset command to the engine on the TIU
// returns true on success and nil on error
//
static int luaH_FactoryReset(lua_State *L) {
	bool bval = true;
	int n = lua_gettop(L);	//n is the number of arguments, use if needed
    if (n != 2) {
    	HARDERROR(ptrPC_local->PCMessages, Format((String)"Wrong # of arguments in call to FactoryReset() - got %d need 2", ARRAYOFCONST((n))), (TColor) clRed);		// Display in Message List
	    lua_pushnil(L);
    	return 1; //we are returning one value
		} else {
        byte iEngineNo = luaL_checkinteger(L,1);
        byte iTIUNo = luaL_checkinteger(L,2);
        luaL_argcheck(L, iEngineNo >= ENGINEMIN && iEngineNo <= ENGINEMAX, 1, "Engine Number out of range");
        luaL_argcheck(L, iTIUNo >= 1 && iTIUNo <= TIUMAX, 2, "TIU Number out of range");
        //
        bval = ptrPC_local->ptrPC_Thread->FactoryReset(iEngineNo, iTIUNo);
        if (!bval) {
        	lua_pushnil(L);
            return 1;
            }        }
    lua_pushboolean(L, bval);
    return 1; //we are returning one value
}
//---------------------------------------------------------------------------
//Command(sCommandLine, iConsistNo, TIUNo);
//
// Send the given command to the engine on the TIU
// Command is sent exactly as given.
// Does not work with lashups
//
// returns 1 value on failure:
//  1. nil on failure
//
// returns 3 values on success:
//  1. true
//  2. the number of characters in the response to the command
//  3. the string response to the command
//
static int luaH_Command (lua_State *L) {
	bool bretv = false;
    int iCmdCount;
    byte CmdbyteResp[CMDMAX];
	int n = lua_gettop(L);	//n is the number of arguments, use if needed
	if (n != 3) {
		HARDERROR(ptrPC_local->PCMessages, Format((String)"Wrong # of arguments in call to Command() - got %d need 3", ARRAYOFCONST((n))), (TColor) clRed);		// Display in Message List
	    lua_pushnil(L);
   		return 1; //we are returning one value
		} else {
        AnsiString sCommand = luaL_checkstring(L, 1);// AnsiString needed because .c_str() is taken and char string required
        byte iEngineNo = luaL_checkinteger(L,2);
        byte iTIUNo = luaL_checkinteger(L,3);
        luaL_argcheck(L, sCommand.Length() >= 1, 1, "Command String must be at least one character long");
        luaL_argcheck(L, iEngineNo >= ENGINEMIN && iEngineNo <= ENGINEMAX, 2, "Engine Number out of range");
        luaL_argcheck(L, iTIUNo >= 1 && iTIUNo <= TIUMAX, 3, "TIU Number out of range");
        int Expected = 11;  // most commands return an ACK packet of 11 characters
        if (sCommand == "I0")
            Expected = 39;
        bretv = ptrPC_local->ptrPC_Thread->Command(sCommand.c_str(), iEngineNo, iTIUNo, Expected, &iCmdCount, CmdbyteResp);
        if (bretv) {
			lua_pushboolean(L, true);
            lua_pushinteger(L, iCmdCount);
        	lua_pushstring(L, CmdbyteResp);
			return 3; //we are returning three values
        	}
        }
	lua_pushnil(L);
	return 1; //we are returning one value
}
//---------------------------------------------------------------------------
//GetTIUNo(EngineNo);
//
// returns the TIU Number associated with the given Engine Number
//
// returns TIU Number on success, nil on failure
//
static int luaH_GetTIUNo (lua_State *L) {
	int n = lua_gettop(L);	//n is the number of arguments, use if needed
	if (n != 1) {
		HARDERROR(ptrPC_local->PCMessages, Format((String)"Wrong # of arguments in call to GetTIUNo() - got %d need 1", ARRAYOFCONST((n))), (TColor) clRed);		// Display in Message List
	    lua_pushnil(L);
   		return 1; //we are returning one value
		} else {
        int TIUNo;
        int iEngineNo = luaL_checkinteger(L,1);
        luaL_argcheck(L, iEngineNo >= ENGINEMIN && iEngineNo <= ENGINEMAX, 1, "Engine Number out of range");
        ptrTIUCommThread->GetTIU(iEngineNo, TIUNo);
        lua_pushinteger(L, TIUNo);
        return 1;    //we are returning one value
        }
}
//---------------------------------------------------------------------------
//
// Get one of the debug counters on the Main Window
//
// return value as a integer, nil on error
//
static int luaH_GetDebugCounter (lua_State *L) {
int CntrVal;
int n = lua_gettop(L);	//n is the number of arguments, use if needed
if (n != 1) {
	HARDERROR(ptrPC_local->PCMessages, Format((String)"Wrong # of arguments in call to GetFrnugCounter() - got %d need 1", ARRAYOFCONST((n))), (TColor) clRed);		// Display in Message List
    lua_pushnil(L);
	return 1; //we are returning one value
	} else {
    byte Cntr = luaL_checkinteger(L,1);
    switch(Cntr) {
        case 1:
            ptrTIUCommThread->GetCounterValue(MainForm->WiFiTO, &CntrVal);
            break;
        case 2:
            ptrTIUCommThread->GetCounterValue(MainForm->CKSum, &CntrVal);
            break;
        case 3:
            ptrTIUCommThread->GetCounterValue(MainForm->PacketsSent, &CntrVal);
            break;
        case 4:
            ptrTIUCommThread->GetCounterValue(MainForm->sendDataBusy, &CntrVal);
            break;
        case 5:
            ptrTIUCommThread->GetCounterValue(MainForm->Retries, &CntrVal);
            break;
        case 6:
            ptrTIUCommThread->GetCounterValue(MainForm->NAK0T, &CntrVal);
            break;
		case 7:
            ptrTIUCommThread->GetCounterValue(MainForm->NAKCRC, &CntrVal);
            break;
        case 8:
            ptrTIUCommThread->GetCounterValue(MainForm->Tags, &CntrVal);
            break;
        case 9:
            ptrTIUCommThread->GetCounterValue(MainForm->DL_FIFO_Count, &CntrVal);
            break;
		case 10:
            ptrTIUCommThread->GetCounterValue(MainForm->ReentrantErrors, &CntrVal);
            break;
		case 11:
            ptrTIUCommThread->GetCounterValue(MainForm->CaughtErrors, &CntrVal);
            break;
        case 12:
            ptrTIUCommThread->GetCounterValue(MainForm->TIU_Total_Byte_Count, &CntrVal);
        	break;
        case 13:
            ptrTIUCommThread->GetCounterValue(MainForm->NAKShort, &CntrVal);
        	break;
        case 14:
            ptrTIUCommThread->GetCounterValue(MainForm->NAKLen, &CntrVal);
        	break;
        case 15:
            ptrTIUCommThread->GetCounterValue(MainForm->Dispatched_Cmd_Count, &CntrVal);
        	break;
        case 16:
            ptrTIUCommThread->GetCounterValue(MainForm->DL_FIFO_Total, &CntrVal);
        	break;
        case 17:
            ptrTIUCommThread->GetCounterValue(MainForm->StartedConsists, &CntrVal);
        	break;
        case 18:
            ptrTIUCommThread->GetCounterValue(MainForm->EngNotFound, &CntrVal);
        	break;
        // Setup Spinners
        case 19:
            ptrTIUCommThread->GetSpinnerValue(Setup->MaxSpeed, &CntrVal);
        	break;
        case 20:
            ptrTIUCommThread->GetSpinnerValue(Setup->RadioCOMPortNum, &CntrVal);
        	break;
        case 21:
            ptrTIUCommThread->GetSpinnerValue(Setup->RFIDCOMPortNum, &CntrVal);
        	break;
        //
        default:
            HARDERROR(ptrPC_local->PCMessages, Format((String)"Invalid argument (%d) in call to GetDebugCounter()", ARRAYOFCONST((Cntr))), (TColor) clRed);		// Display in Message List
		    lua_pushnil(L);
   			return 1; //we are returning one value
        }
    }
lua_pushinteger(L, CntrVal);
return 1; //we are returning one value
}
//---------------------------------------------------------------------------
//RSActivate(int iRSNo);
//
// changes the state of the selected Accessory. EngineNo not used but must be specified.
// return true on success, nil on error
//
static int luaH_RSActivate (lua_State *L) {
	bool bretv = false;
	int n = lua_gettop(L);	//n is the number of arguments
    if (n != 1) {
	    HARDERROR(ptrPC_local->PCMessages, Format((String)"Wrong # of arguments in call to RSActivate() - got %d need 1", ARRAYOFCONST((n))), (TColor) clRed);		// Display in Message List
	    lua_pushnil(L);
   		return 1; //we are returning one value
        } else {
		int iRSNo = luaL_checkinteger(L,1);
        luaL_argcheck(L, iRSNo >= 1 && iRSNo <= RSMAX, 1, "Route/Scene Number out of range");
	    ptrTIUCommThread->ProcessRS(iRSNo, RSACTIVATE);
        }
	lua_pushboolean(L, bretv);
	return 1; //we are returning one value
}
//---------------------------------------------------------------------------
//RSReset(int iRSNo);
//
// changes the state of the selected Accessory. EngineNo not used but must be specified.
// return true on success, nil on error
//
static int luaH_RSReset (lua_State *L) {
	bool bretv = false;
	int n = lua_gettop(L);	//n is the number of arguments
    if (n != 1) {
	    HARDERROR(ptrPC_local->PCMessages, Format((String)"Wrong # of arguments in call to RSReset() - got %d need 1", ARRAYOFCONST((n))), (TColor) clRed);		// Display in Message List
	    lua_pushnil(L);
   		return 1; //we are returning one value
        } else {
		int iRSNo = luaL_checkinteger(L,1);
        luaL_argcheck(L, iRSNo >= 1 && iRSNo <= RSMAX, 1, "Route/Scene Number out of range");
	    ptrTIUCommThread->ProcessRS(iRSNo, RSRESET);
        }
	lua_pushboolean(L, bretv);
	return 1; //we are returning one value
}
//---------------------------------------------------------------------------
//RSAllSwitchesNormal(int iRSNo);
//
// changes the state of the selected Accessory. EngineNo not used but must be specified.
// return true on success, nil on error
//
static int luaH_RSAllSwitchesNormal (lua_State *L) {
	bool bretv = false;
	int n = lua_gettop(L);	//n is the number of arguments
    if (n != 1) {
	    HARDERROR(ptrPC_local->PCMessages, Format((String)"Wrong # of arguments in call to RSAllSwitchesNormal() - got %d need 1", ARRAYOFCONST((n))), (TColor) clRed);		// Display in Message List
	    lua_pushnil(L);
   		return 1; //we are returning one value
        } else {
		int iRSNo = luaL_checkinteger(L,1);
        luaL_argcheck(L, iRSNo >= 1 && iRSNo <= RSMAX, 1, "Route/Scene Number out of range");
	    ptrTIUCommThread->ProcessRS(iRSNo, SETALLSWNORMAL);
        }
	lua_pushboolean(L, bretv);
	return 1; //we are returning one value
}
//---------------------------------------------------------------------------
//RSAllAccyOn(int iRSNo);
//
// changes the state of the selected Accessory. EngineNo not used but must be specified.
// return true on success, nil on error
//
static int luaH_RSAllAccyOn (lua_State *L) {
	bool bretv = false;
	int n = lua_gettop(L);	//n is the number of arguments
    if (n != 1) {
	    HARDERROR(ptrPC_local->PCMessages, Format((String)"Wrong # of arguments in call to RSAllAccyOn() - got %d need 1", ARRAYOFCONST((n))), (TColor) clRed);		// Display in Message List
	    lua_pushnil(L);
   		return 1; //we are returning one value
        } else {
		int iRSNo = luaL_checkinteger(L,1);
        luaL_argcheck(L, iRSNo >= 1 && iRSNo <= RSMAX, 1, "Route/Scene Number out of range");
	    ptrTIUCommThread->ProcessRS(iRSNo, SETALLACCYON);
        }
	lua_pushboolean(L, bretv);
	return 1; //we are returning one value
}
//---------------------------------------------------------------------------
//RSAllAccyOff(int iRSNo);
//
// changes the state of the selected Accessory. EngineNo not used but must be specified.
// return true on success, nil on error
//
static int luaH_RSAllAccyOff(lua_State *L) {
	bool bretv = false;
	int n = lua_gettop(L);	//n is the number of arguments
    if (n != 1) {
	    HARDERROR(ptrPC_local->PCMessages, Format((String)"Wrong # of arguments in call to RSAllAccyOff() - got %d need 1", ARRAYOFCONST((n))), (TColor) clRed);		// Display in Message List
	    lua_pushnil(L);
   		return 1; //we are returning one value
        } else {
		int iRSNo = luaL_checkinteger(L,1);
        luaL_argcheck(L, iRSNo >= 1 && iRSNo <= RSMAX, 1, "Route/Scene Number out of range");
	    ptrTIUCommThread->ProcessRS(iRSNo, SETALLACCYOFF);
        }
	lua_pushboolean(L, bretv);
	return 1; //we are returning one value
}
//---------------------------------------------------------------------------
// SetUpLabels()
// Message(MessageText)
//
// When called as SetUpLabels():
// After user changes the values of "FunctionXXLabel", "FunctionXXName", or "CounterXXLabel" call this
// routine to update the labels/function of buttons. For this operation, do not pass any parameters.
//
// When called as Message(MessageText):
// If a text string is passed, set the text string into the "Messages" caption field on the PC window.
// The message appears to the left of the [Layout] button.
// Note: ShowAccelChar is set to false so '&' just displays as '&'
//
// always return true
//
static int luaH_SetUpLabels(lua_State *L) {
	int n = lua_gettop(L);	//n is the number of arguments
    if (n == 1) {
       	// Set Message String
	    String TextValue = luaL_checkstring(L, 1);
        ptrTIUCommThread->SetLabelText(ptrPC_local->MessageNote, TextValue);
        } else {
        // setup labels
    ptrPC_local->ptrPC_Thread->SetUpLabelsSync();	// configuration variables "FunctionXXName", "FunctionXXLabel", "CounterXXLabel", title
        }
	lua_pushboolean(L, true);
	return 1; //we are returning one value
}
//---------------------------------------------------------------------------
// LuaVersion()
//
// display the Lua Version in the messages window
// returns 6 values
//
//
static int luaH_LuaVersion(lua_State *L) {
  String PLine;
  ShowinBuffer(ptrPC_local->PCMessages, "LUA_VERSION_NUM = " + String(LUA_VERSION_NUM), clBlack);		// Display in Message List
  lua_pushinteger(L, LUA_VERSION_NUM);
  ShowinBuffer(ptrPC_local->PCMessages, "LUA_VERSION_MAJOR = " + String(LUA_VERSION_MAJOR), clBlack);		// Display in Message List
  lua_pushstring(L, LUA_VERSION_MAJOR);
  ShowinBuffer(ptrPC_local->PCMessages, "LUA_VERSION_MINOR = " + String(LUA_VERSION_MINOR), clBlack);		// Display in Message List
  lua_pushstring(L, LUA_VERSION_MINOR);
  ShowinBuffer(ptrPC_local->PCMessages, "LUA_VERSION_RELEASE = " + String(LUA_VERSION_RELEASE), clBlack);		// Display in Message List
  lua_pushstring(L, LUA_VERSION_RELEASE);
  ShowinBuffer(ptrPC_local->PCMessages, "LUA_VERSION = " + String(LUA_VERSION), clBlack);		// Display in Message List
  lua_pushstring(L, LUA_VERSION);
  ShowinBuffer(ptrPC_local->PCMessages, "LUA_RELEASE = " + String(LUA_RELEASE), clBlack);		// Display in Message List
  lua_pushstring(L, LUA_RELEASE);
  return 6; //we are returning 6 values
}
//---------------------------------------------------------------------------
// Num = ReadEngines()
//
// Perform a READ function and return the number of engines found
//
static int luaH_ReadEngines(lua_State *L) {
int Count;
ptrTIUCommThread->ReadEngines(&Count);
lua_pushinteger(L, Count);
return 1;   //we are returning 1 value
}
//---------------------------------------------------------------------------
// Num = FindEngines()
//
// Perform a FIND function and return the number of engines found
//
static int luaH_FindEngines(lua_State *L) {
int Count;
ptrTIUCommThread->FindEngines(&Count);
lua_pushinteger(L, Count);
return 1;   //we are returning 1 value
}
//---------------------------------------------------------------------------
// End of Lua Interface Routines
//---------------------------------------------------------------------------
//
void __fastcall TProg_Control_Thread::Lua_Register() {
// Register all of the C++ functions available to the Lua Script
lua_register(L,"PrintString", luaH_PrintString);
lua_register(L,"print", luaH_PrintString);	// replace print() with PrintString()
lua_register(L,"printc", luaH_PrintStringColor);
lua_register(L,"LuaVersion", luaH_LuaVersion);
lua_register(L,"CabChat", luaH_CabChat);
lua_register(L,"Schedule", luaH_Schedule);
lua_register(L,"EmergencyStop", luaH_Emergency);
lua_register(L,"CommStatus", luaH_GetCommStatus);
lua_register(L,"RAM", luaH_RAM);
lua_register(L,"RAM2", luaH_RAM2);
lua_register(L,"RAM4", luaH_RAM4);
lua_register(L,"SetLED", luaH_SetLED);
//
lua_register(L,"StartUpActiveEngines", luaH_StartUpActiveEngines);
lua_register(L,"StartupActiveEngines", luaH_StartUpActiveEngines);
lua_register(L,"ShutDownAllEngines", luaH_ShutDownAllEngines);
lua_register(L,"ShutdownAllEngines", luaH_ShutDownAllEngines);
lua_register(L,"ShutDownActiveEngines", luaH_ShutDownActiveEngines);
lua_register(L,"ShutdownActiveEngines", luaH_ShutDownActiveEngines);
//
lua_register(L,"StartUp", luaH_StartUp);
lua_register(L,"Startup", luaH_StartUp);    // lowercase alias
lua_register(L,"ShutDown", luaH_ShutDown);
lua_register(L,"Shutdown", luaH_ShutDown);    // lowercase alias
//
lua_register(L,"EngineStartUp", luaH_EngineStartUp);
lua_register(L,"EngineStartup", luaH_EngineStartUp);    // lowercase alias
lua_register(L,"LashUpStartUp", luaH_LashUpStartUp);
lua_register(L,"LashupStartup", luaH_LashUpStartUp);    // lowercase alias
//
lua_register(L,"EngineShutDown", luaH_EngineShutDown);
lua_register(L,"EngineShutdown", luaH_EngineShutDown);    // lowercase alias
lua_register(L,"LashUpShutDown", luaH_LashUpShutDown);
lua_register(L,"LashupShutdown", luaH_LashUpShutDown);    // lowercase alias
//
lua_register(L,"Exists", luaH_Exists);
lua_register(L,"IsActive", luaH_IsActive);
//
lua_register(L,"Accessory", luaH_Accessory);
lua_register(L,"Siding", luaH_Siding);
lua_register(L,"Watchdog", luaH_Watchdog);
lua_register(L,"TIUActive", luaH_TIUActive);
lua_register(L,"VirtualAccessory", luaH_VirtualAccessory);
lua_register(L,"Switch", luaH_Switch);
lua_register(L,"Whistle", luaH_Whistle);
lua_register(L,"Bell", luaH_Bell);
lua_register(L,"Markers", luaH_Markers);
lua_register(L,"Beacon", luaH_Beacon);
lua_register(L,"Smoke", luaH_Smoke);
lua_register(L,"ChuffRate", luaH_ChuffRate);
lua_register(L,"Rate", luaH_Rate);
lua_register(L,"GetRate", luaH_GetRate);
lua_register(L,"OpenCoupler", luaH_OpenCoupler);
lua_register(L,"SetDirection", luaH_SetDirection);
lua_register(L,"GetDirection", luaH_GetDirection);
lua_register(L,"Throttle", luaH_Throttle);
lua_register(L,"PlaySound", luaH_PlaySound);
lua_register(L,"SetSpeed", luaH_SetSpeed);
lua_register(L,"SetSpeedEx", luaH_SetSpeedEx);
lua_register(L,"GetSetSpeed", luaH_GetSetSpeed);
lua_register(L,"GetSpeed", luaH_GetSpeed);
lua_register(L,"Setting", luaH_Setting);
lua_register(L,"SetVolume", luaH_SetVolume);
lua_register(L,"GetVolume", luaH_GetVolume);
lua_register(L,"IsEngineRunning", luaH_IsEngineRunning);
lua_register(L,"IsLashUpRunning", luaH_IsLashUpRunning);
lua_register(L,"BumpCounter", luaH_BumpCounter);
lua_register(L,"DecCounter", luaH_DecCounter);
lua_register(L,"GetCounter", luaH_GetCounter);
lua_register(L,"ClearCounter", luaH_ClearCounter);
lua_register(L,"SetCounter", luaH_SetCounter);
lua_register(L,"AddToCounter", luaH_AddToCounter);
lua_register(L,"Sound", luaH_Sound);
lua_register(L,"Sleep", luaH_Sleep);
lua_register(L,"sleep", luaH_Sleep);
lua_register(L,"Ping", luaH_Ping);

lua_register(L,"SetQuill", luaH_SetQuill);
lua_register(L,"ProtoWhistle", luaH_ProtoWhistle);
lua_register(L,"SmokeWhistle", luaH_SmokeWhistle);
lua_register(L,"SwingingBell", luaH_SwingingBell);
lua_register(L,"DTO", luaH_DTO);
lua_register(L,"DOD", luaH_DOD);
lua_register(L,"DCH", luaH_DCH);
//
lua_register(L,"RunTime", luaH_GetRunTime);
lua_register(L,"PCNo", luaH_GetPCNo);
lua_register(L,"TIUVersion", luaH_GetTIUVersion);
lua_register(L,"Counts", luaH_GetCounts);
lua_register(L,"MicroSeconds", luaH_MicroSeconds);
//
lua_register(L,"Beep", luaH_Beep);
lua_register(L,"ClearDispatchList", luaH_ClearDispatchList);
lua_register(L,"CountDispatchList", luaH_CountDispatchList);
lua_register(L,"Stop", luaH_Stop);
lua_register(L,"Debug", luaH_GetDebug);
//
lua_register(L,"InputBox", luaH_InputBox);
lua_register(L,"InputQuery", luaH_InputQuery);
lua_register(L,"InputDropDown", luaH_InputDropDown);
lua_register(L,"StackDump", luaH_StackDump);
lua_register(L,"Info", luaH_Info);

lua_register(L,"GetName", luaH_GetName);
lua_register(L,"GetEngineName", luaH_GetEngineName);
lua_register(L,"GetCarName", luaH_GetCarName);
lua_register(L,"GetRailroadName", luaH_GetRailroadName);
lua_register(L,"GetEngineType", luaH_GetEngineType);
lua_register(L,"GetTagLocation", luaH_GetTagLocation);
lua_register(L,"GetCouplerLocation", luaH_GetCouplerLocation);

lua_register(L,"GetSwitchPositionNames", luaH_GetSwitchPositionNames);
lua_register(L,"GetSwitchStatus", luaH_GetSwitchStatus);
lua_register(L,"GetAccStatus", luaH_GetAccStatus);

lua_register(L,"GetSwitchName", luaH_GetSwitchName);
lua_register(L,"GetAccName", luaH_GetAccName);
lua_register(L,"MakeItSo", luaH_MakeItSo);

lua_register(L,"ClearGlobals", luaH_ClearGlobals);
lua_register(L,"SetGlobalInt", luaH_SetGlobalInt);
lua_register(L,"TrySetGlobalInt", luaH_TrySetGlobalInt);
lua_register(L,"TryMultiSetGlobalInt", luaH_TryMultiSetGlobalInt);
lua_register(L,"GetGlobalInt", luaH_GetGlobalInt);

lua_register(L,"TrySetFlag", luaH_TrySetFlag);
lua_register(L,"TryMultiSetFlag", luaH_TryMultiSetFlag);

lua_register(L,"SetGlobalString", luaH_SetGlobalString);
lua_register(L,"GetGlobalString", luaH_GetGlobalString);
lua_register(L,"Title", luaH_Title);
lua_register(L,"SetEvent", luaH_SetEvent);
// Layout
lua_register(L,"ShowLayout", luaH_ShowLayout);
lua_register(L,"LayoutShowing", luaH_LayoutShowing);
// Flags
lua_register(L,"SetFlag", luaH_SetFlag);
lua_register(L,"GetFlag", luaH_GetFlag);
lua_register(L,"SetFlagName", luaH_SetFlagName);
lua_register(L,"GetFlagName", luaH_GetFlagName);
// Fixed Block Signals on Layout Window
lua_register(L,"SetAspect", luaH_SetAspect);
lua_register(L,"GetAspect", luaH_GetAspect);
lua_register(L,"CreateSignal", luaH_CreateSignal);
lua_register(L,"DeleteSignal", luaH_DeleteSignal);
lua_register(L,"GetSignalName", luaH_GetSignalName);
lua_register(L,"DeleteAllSignals", luaH_DeleteAllSignals);
// Cab Signals - Home and Distant on Program Control Window
lua_register(L,"CreateCab", luaH_CreateCab);
lua_register(L,"SetCabSignal", luaH_SetCabSignal);
lua_register(L,"GetCabAspect", luaH_GetCabAspect);
lua_register(L,"RegisterCabSignals", luaH_RegisterCabSignals);
//
lua_register(L,"Yield", luaH_Yield);
//
lua_register(L,"Popup", luaH_Popup);
//
lua_register(L,"ReqBlockEvents", luaH_ReqBlockEvents);
//
lua_register(L,"GetTagReaders", luaH_GetTagReaders);
lua_register(L,"CheckReader", luaH_CheckReader);
lua_register(L,"GetSignals", luaH_GetSignals);
lua_register(L,"CheckSignal", luaH_CheckSignal);
//
lua_register(L,"FactoryReset", luaH_FactoryReset);
lua_register(L,"FeatureReset", luaH_FeatureReset);
lua_register(L,"Command", luaH_Command);
lua_register(L,"GetTIUNo", luaH_GetTIUNo);
lua_register(L,"GetDebugCounter", luaH_GetDebugCounter);
//
lua_register(L,"InputLine", luaH_InputLine);
//
lua_register(L,"RSActivate", luaH_RSActivate);
lua_register(L,"RSReset", luaH_RSReset);
lua_register(L,"RSAllSwitchesNormal", luaH_RSAllSwitchesNormal);
lua_register(L,"RSAllAccyOn", luaH_RSAllAccyOn);
lua_register(L,"RSAllAccyOff", luaH_RSAllAccyOff);
//
lua_register(L,"SetUpLabels", luaH_SetUpLabels);
lua_register(L,"Message", luaH_SetUpLabels);
lua_register(L,"Noise", luaH_Noise);
//
lua_register(L,"ReadEngines", luaH_ReadEngines);
lua_register(L,"FindEngines", luaH_FindEngines);
}
//--------------------------------------------------------------------------
//---------------------------------------------------------------------------
//   Important: Methods and properties of objects in VCL can only be
//   used in a method called using Synchronize, for example:
//
//      Synchronize(UpdateCaption);
//
//   where UpdateCaption could look like:
//
//      void __fastcall Prog_Control_Thread::UpdateCaption()
//      {
//        Form1->Caption = "Updated in a thread";
//      }
//---------------------------------------------------------------------------
__fastcall TProg_Control_Thread::TProg_Control_Thread(bool CreateSuspended)
	: TThread(CreateSuspended)
{
assert(false);	// never called since I used the overloaded constructor below
}
//---------------------------------------------------------------------------
__fastcall TProg_Control_Thread::TProg_Control_Thread(bool CreateSuspended, TForm * Callingthis,
		 HWND Hndl, int PCNo, int Engine, int Remote, int TIU, enum PROGRAMNO Prog, AnsiString FN)
	: TThread(CreateSuspended)
{
// passed TIU Number is not used
MyRemoteNo = Remote;
MyEngineNo = Engine;
assert(MyRemoteNo >= 0 && MyRemoteNo <= REMOTEMAX);
ptrPC = reinterpret_cast<TProgram_Control*>(Callingthis);
assert(ptrPC);	// must not be NULL
MyPCNo = PCNo;
// This thread can only be called by Program_Control but can be called multiple times
MyHndl = Hndl;		// Hndl is the Windows Handle of the MainForm - not used
FileName = FN;
CommandSuffix = NULL;
Finished = false;
// termination
OnTerminate = &RunOnTerminate;
FreeOnTerminate = true;			// thread will free itself on exit
Priority = tpHighest;
// ReturnValue is accessable using WaitFor()
ReturnValue = -1;
ProgramRunning = Prog;						// Compile/Run/Interactive
// clear the RFID tag FIFO pointer
RFIDFIFO = NULL;
//
TenthSecondCounter = 0;
TagCount_Thread = 0;
// Lua State Pointer
L = NULL;
StopFlag = false;
//
// prevent calling the 8 operational functions
CallTickFunction = CallLoopFunction = CallTagFunction = CallCleanupFunction = false;
CallUIDTagFunction = CallResponseFunction = CallNameTagFunction = false;
CallEventFunction = false;
Title[0] = '\0';
// Timer thread started when Program Control window started by Train_Control.cpp
// Initialize status
//
for (int iDex = FACTORYRESET ; iDex < ABSOLUTEMAX ; iDex++) {
    LastSentSpeedValue[iDex] = 0;
    MotionSoundRequest[iDex] = true;
    AutoBell[iDex] = false;
    AutoWhistle[iDex] = false;
    direction[iDex] = ENG_FORWARD;
    predictedDirection[iDex] = ENG_FORWARD;
    bell[iDex] = OFF;
    whistle[iDex] = OFF;
    state[iDex] = SHUTDOWN;
    marker[iDex] = OFF;
    cabchat[iDex] = OFF;
    smoke[iDex] = OFF;
    sound[iDex] = OFF;
    chuffrate[iDex] = 2;
    beacon[iDex] = OFF;
    }

AutoBellGlobal = false;
AutoWhistleGlobal = false;
CompileSuccess = false;
CabCreated = false;

// set ETime counter to be updated by the Timer Thread
ptrPC_ETime[MyPCNo - 1] = ptrPC->ETime;
}
//---------------------------------------------------------------------------
__fastcall TProg_Control_Thread::~TProg_Control_Thread(void)
{
//
// Destructor
//
if (DEBUGGINGX(1)) ptrTIUCommThread->PrintItem("TProg_Control_Thread(" + String(MyPCNo) + "):~ThreadID " + IntToHex((int)ThreadID, 8), (TColor) clGreen);
// Lua Cleanup
if (L != NULL) {
	lua_close(L);
    L = NULL;
    }
//
if (Event_FIFO[MyPCNo - 1]) {
	delete Event_FIFO[MyPCNo - 1];
    Event_FIFO[MyPCNo - 1] = NULL;
    }

// clear pointer to ETime counter to be updated by the Timer Thread
if (ptrPC_ETime[MyPCNo - 1]) {
	ptrTIUCommThread->SetNumEditColor(ptrPC_ETime[MyPCNo - 1], clRed);	// set Run Time counter to red
	ptrPC_ETime[MyPCNo - 1] = NULL;
	}
// delete allocated data areas
if (MyTimer) {
	ResetEvent(MyTimer);
	CancelWaitableTimer(MyTimer);
	CloseHandle(MyTimer);
    MyTimer = NULL;
    }
if (MySleepTimer) {
	ResetEvent(MySleepTimer);
	CancelWaitableTimer(MySleepTimer);
	CloseHandle(MySleepTimer);
    MySleepTimer = NULL;
	}

hRFIDReqEvent[MyPCNo - 1] = NULL;
if (RFIDReqEvent != INVALID_HANDLE_VALUE) CloseHandle(RFIDReqEvent);			// not really needed - the system closes the handle automatically when the process terminates
RFIDReqEvent = INVALID_HANDLE_VALUE;
// delete the RFID tag FIFO and clear the pointer
if (RFIDFIFO) {
	delete RFIDFIFO;
    RFIDFIFO = NULL;
    }
// clear pointer since the thread is being freed
ptrPC_local->ptrPC_Thread = NULL;
// Send CM_ PCTHREADTERM message to Program Control which will do additional cleanups needed
(void) PostMessage(ptrPC_local->Handle, CM_PCTHREADTERM, NULL, NULL);
//
Finished = true;
}
//---------------------------------------------------------------------------
void __fastcall TProg_Control_Thread::Execute()
{
// Calls to Sleep() are ok in this thread
// NO VCL Calls in this thread
ptrPC_local = ptrPC;	// this must be done here, not in constructor
						// as constructor runs in the context of the main thread
if (DEBUGGINGX(8)) {
	ptrTIUCommThread->PrintItem("TProg_Control_Thread(" + String(MyPCNo) + "): ptrPC 0x" + IntToHex((int)ptrPC,8), (TColor) clGreen);
	ptrTIUCommThread->PrintItem("TProg_Control_Thread(" + String(MyPCNo) + "): &ptrPC 0x" + IntToHex((int)&ptrPC,8), (TColor) clGreen);
	ptrTIUCommThread->PrintItem("TProg_Control_Thread(" + String(MyPCNo) + "): ptrPC_local 0x" + IntToHex((int)ptrPC_local,8), (TColor) clGreen);
	ptrTIUCommThread->PrintItem("TProg_Control_Thread(" + String(MyPCNo) + "): &ptrPC_local 0x" + IntToHex((int)&ptrPC_local,8), (TColor) clGreen);
//	ptrTIUCommThread->PrintItem("TProg_Control_Thread(" + String(MyPCNo) + "): i_local 0x" + IntToHex((int)i_local,8), (TColor) clGreen);
//	ptrTIUCommThread->PrintItem("TProg_Control_Thread(" + String(MyPCNo) + "): &i_local 0x" + IntToHex((int)&i_local,8), (TColor) clGreen);
//	i_local = 0xBEEF;
    }
// debug results showing different addresses for thread local variable for each thread
// i_local was used for testing only
/*
TMainForm:MainThreadID 00000CA8
TMyTimerThread:ThreadID 00002284
TProg_Control_Thread(1): ptrPC 0x0220CD4C
TProg_Control_Thread(1): &ptrPC 0x022242EC
TProg_Control_Thread(1): ptrPC_local 0x0220CD4C
TProg_Control_Thread(1): &ptrPC_local 0x00343548
TProg_Control_Thread(1): i_local 0x0000DEAD
TProg_Control_Thread(1): &i_local 0x0034354C
TProg_Control_Thread(1):ThreadID 00002030
TProg_Control_Thread(2): ptrPC 0x02232478
TProg_Control_Thread(2): &ptrPC 0x022431E8
TProg_Control_Thread(2): ptrPC_local 0x02232478
TProg_Control_Thread(2): &ptrPC_local 0x0036A118
TProg_Control_Thread(2): i_local 0x0000DEAD
TProg_Control_Thread(2): &i_local 0x0036A11C
TProg_Control_Thread(2):ThreadID 00001CA8
*/
ThreadID = GetCurrentThreadId();
if (DEBUGGINGX(1)) {
	// print the ThreadId
    ptrTIUCommThread->PrintItem("TProg_Control_Thread(" + String(MyPCNo) + "):ThreadID " + IntToHex((int)ThreadID, 8), (TColor) clGreen);
	}

// this does not seem to work in BCB4.0 and Thread view not available in CB10.1
// it does work on BCB6.0
// http://docwiki.embarcadero.com/RADStudio/Rio/en/Converting_an_Unnamed_Thread_to_a_Named_Thread
THREADNAME_INFO info;
info.dwType = 0x1000;
char sName[25];
snprintf(sName, sizeof sName, "PC%1d_Thread", MyPCNo);
info.szName = sName;
info.dwThreadID = -1;
info.dwFlags = 0;
__try {		// Convert unnamed thread to named thread
    RaiseException(0x406D1388, 0, sizeof(info) / sizeof(DWORD),
        (DWORD*)&info);
    }
__except (EXCEPTION_CONTINUE_EXECUTION) {
    }
//
// create Event FIFO - thread safe
//
Event_FIFO[MyPCNo - 1] = new TThreadList();	// - Thread Safe
assert(Event_FIFO[MyPCNo - 1]);				// check for successful creation
TList *myList = Event_FIFO[MyPCNo - 1]->LockList();
try {
	myList->Capacity = 100;
    }
__finally {
	Event_FIFO[MyPCNo - 1]->UnlockList();
    }
// reset MessageNote text incase it was changed by the previous script    
ptrTIUCommThread->SetLabelText(ptrPC_local->MessageNote, "Messages");
//
// -- create RFID request event
RFIDReqEvent = CreateEvent(NULL, false, false, NULL);	// Auto Reset - nonsignaled initial state
assert(RFIDReqEvent != NULL);
hRFIDReqEvent[MyPCNo - 1] = RFIDReqEvent;

// Create two unnamed waitable timers.
MyTimer = CreateWaitableTimer(NULL, false, NULL);		// Autoreset --> the timer is a synchronization timer
if (MyTimer == NULL) {
	return;	// CreateWaitableTimer failed
    }
MySleepTimer = CreateWaitableTimer(NULL, true, NULL);	// Manual Reset
if (MySleepTimer == NULL) {
	return;	// CreateWaitableTimer failed
    }
//
if (Terminated) {
	return;						// in case we are terminated before we even start
    }
//
// The following try/catch block is used to error exit when the lua interface
// detects a parameter error. The lua interface functions use "throw" to
// get out when a bad parameter is detected
// The Lua language supports a similar mechanism using luaL_error() but that
// routine terminates the entire program (RTC).
// This code catches the error, displays a message in the message list on the
// Program Control window and then terminates only this thread
//
try {		// Main Execute() function
	int load_stat, retv;
    switch(ProgramRunning) {
        // ------------------------------------------------------------------------
        case RUN_LUASCRIPT:	// Compile the script, execute then script and then wait for Event to run setup(), loop() and cleanup()
        	for (int iTIU = 0 ; iTIU < TIUMAX ; iTIU++) {
	            for (int iDex = FACTORYRESET ; iDex < ABSOLUTEMAX ; iDex++) { 	// clear the arrays
    	            MotionSoundRequest[iDex] = true;		// Use MotionSoundRequest to use horn and bell when moving from 0 Smph
					AutoBell[iDex] = false;				// No Automatic Bell/Whistle/Horn on engine/lashup start up unless set below as configuration variable
					AutoWhistle[iDex] = false;
					}
                }
			AutoBellGlobal = false;
			AutoWhistleGlobal = false;
            Request_Events[MyPCNo - 1] = false;
            if (L != NULL) {
                lua_close(L); 	// close any open state
                L = NULL;
                }
            // initialization
            L = luaL_newstate();
            if (L == NULL) {
				ShowinBuffer(ptrPC->PCMessages, "Cannot create state: not enough memory", clRed);
				// thread will delete itself
                return;
                }
            //
            // Note: not all libraries are needed for script processing (eg. coroutine, io, os, utf8 and debug).
            // It might be possible to use the method described in Chapter 33, pg 358 of the lua
            // book to only load a the basic libraries. This would put the processing of
            // the script in a sandbox and make implementation safer.
            //
            luaL_openlibs(L);
            //
            Lua_Register();	//Register the functions to be called from LUA script to execute commands
            // Rgister must run before the print() function is redefined to output to the PCMessages window
            // Add the WorkingDir at the end of the Lua package path
                do {
                String WorkingDir = ExtractFilePath(FileName);
                // AnsiString needed because .c_str() is taken and char string required
                AnsiString PathString = "package.path = package.path .. [[;" + WorkingDir + "?.out]] .. [[;" + WorkingDir + "?.lua]]";
                AnsiString PathcString = "package.cpath = package.cpath .. [[;" + WorkingDir + "?.dll]]";
                boolean bretv;
                bretv = luaL_dostring(L, PathString.c_str());
                assert(!bretv);	// fail on error
                bretv = luaL_dostring(L, PathcString.c_str());
                assert(!bretv);	// fail on error
                } while (false);
            // compile and execute file
            CompileSuccess = false;
            CabCreated = false;
                do {
                load_stat = luaL_loadfile(L, FileName.c_str());	// load the chunk
                if (load_stat) {
                	//LUA_YIELD		1	//LUA_ERRRUN	2
                    //LUA_ERRSYNTAX	3   //LUA_ERRMEM	4
                    //LUA_ERRGCMM	5   //LUA_ERRERR	6
                    throw "Error " + String(load_stat) + " : " + String(lua_tostring(L, -1));
                    }
                retv = lua_pcall(L, 0, 0, 0);	// Run the chunk, no parameters, no results
                if (retv != LUA_OK) {
                	//LUA_YIELD		1	//LUA_ERRRUN	2
                    //LUA_ERRSYNTAX	3   //LUA_ERRMEM	4
                    //LUA_ERRGCMM	5   //LUA_ERRERR	6
                    throw "Error " + String(retv) + " : " + String(lua_tostring(L, -1));
                    }
                } while (false);
            ShowinBuffer(ptrPC->PCMessages, "Success compiling " + FileName);
            CompileSuccess = true;
			// load configuration variables
            getglobstring(L, "title", Title, sizeof Title);
			Synchronize(SetUpLabels);	// configuration variables "FunctionXXName", "FunctionXXLabel", "CounterXXLabel", title
            //
            // set ETime counter to be updated by the Timer Thread
			ptrPC_ETime[MyPCNo - 1] = ptrPC->ETime;
            //
            ptrTIUCommThread->SetNumEditColor(ptrPC_ETime[MyPCNo - 1], clLime);	// set Run Time counter to green
			//
            ptrPC_Tenths[MyPCNo - 1] = 0;	// initialize the timer to zero
            break;	// run until terminated by the user
        // ------------------------------------------------------------------------
        case COMPILE_LUASCRIPT: // Compile the script, execute the script and then terminate the thread
            if (L != NULL) {
                lua_close(L); 	// close any open state
                L = NULL;
                }
            // initialization
            L = luaL_newstate();
            if (L == NULL) {
                ShowinBuffer(ptrPC->PCMessages, "Cannot create state: not enough memory", clRed);
            	// thread will delete itself
                return;
                }
            //
            // Note: not all libraries are needed for script compilation (eg. coroutine, io, os, utf8 and debug).
            // It might be possible to use the method described in Chapter 33, pg 358 of the lua
            // book to only load a the basic libraries. This would put the compilation of
            // the script in a sandbox and make implementation safer.
            //                
            luaL_openlibs(L);
            Lua_Register();	//Register the functions to be called from LUA script to execute commands
            // Rgister must run before the print() function is redefined to output to the PCMessages window            
            // Add the WorkingDir at the end of the Lua package path
                do {
                String WorkingDir = ExtractFilePath(FileName);
                // AnsiString needed because .c_str() is taken and char string required
                AnsiString PathString = "package.path = package.path .. [[;" + WorkingDir + "?.out]] .. [[;" + WorkingDir + "?.lua]]";
                AnsiString PathcString = "package.cpath = package.cpath .. [[;" + WorkingDir + "?.dll]]";
                load_stat = luaL_dostring(L, PathString.c_str());
	            if (load_stat) {
    	            throw "Error " + String(load_stat) + " : " + String(lua_tostring(L, -1));
				    }
                load_stat = luaL_dostring(L, PathcString.c_str());
	            if (load_stat) {
    	            throw "Error " + String(load_stat) + " : " + String(lua_tostring(L, -1));
				    }
                } while (false);
            // compile and execute file
            CompileSuccess = false;
            CabCreated = false;
                do {
                load_stat = luaL_loadfile(L, FileName.c_str());	// load the chunk
                if (load_stat) {
                	//LUA_YIELD		1	//LUA_ERRRUN	2
                    //LUA_ERRSYNTAX	3   //LUA_ERRMEM	4
                    //LUA_ERRGCMM	5   //LUA_ERRERR	6
	                throw "Error " + String(load_stat) + " : " + String(lua_tostring(L, -1));
                    }
                retv = lua_pcall(L, 0, 0, 0);	// run the chunk - no parameters, no results
                if (retv != LUA_OK) {
                	//LUA_YIELD		1	//LUA_ERRRUN	2
                    //LUA_ERRSYNTAX	3   //LUA_ERRMEM	4
                    //LUA_ERRGCMM	5   //LUA_ERRERR	6
                    throw "Error " + String(retv) + " : " + String(lua_tostring(L, -1));
                    }
                } while (false);
            ShowinBuffer(ptrPC->PCMessages, "Success compiling " + FileName);
            // After compiling, exit
            CompileSuccess = true;
            // load configuration variables
            getglobstring(L, "title", Title, sizeof Title);
			Synchronize(SetUpLabels);	// configuration variables "FunctionXXName", "FunctionXXLabel", "CounterXXLabel", title
			if (L != NULL) {
				lua_close(L);
                L = NULL;
                }
            return;
        // ------------------------------------------------------------------------
        case RUN_LUAINTERACTIVE: // run the Lua interpreter until the user presses [ESC] or [CTRL-Z] and then terminate the thread
        	//
            // you can type statements or expressions:
            // Statement:
            // for m,n in pairs(_G) do print(m, "  " , n) end
            // aa = 4 print(aa," ",2,3,4,5)
            //
            // Expression:
            // = 5 + 345.43
            // =math.sqrt(math.pi * math.pi)
            //
        	for (int iTIU = 0 ; iTIU < TIUMAX ; iTIU++) {
	            for (int iDex = FACTORYRESET ; iDex < ABSOLUTEMAX ; iDex++) { 	// clear the arrays
    	            MotionSoundRequest[iDex] = true;		// Use MotionSoundRequest to use horn and bell when moving from 0 Smph
					AutoBell[iDex] = false;				// No Automatic Bell/Whistle/Horn on engine/lashup start up unless set below as configuration variable
					AutoWhistle[iDex] = false;
					}
                }
   			AutoBellGlobal = false;
   			AutoWhistleGlobal = false;
            Request_Events[MyPCNo - 1] = false;
            if (L != NULL) {
                lua_close(L); 	// close any open state
                L = NULL;
                }
            // initialization
			L = luaL_newstate();
            if (L == NULL) {
				ShowinBuffer(ptrPC->PCMessages, "Cannot create state: not enough memory", clRed);
                // thread will delete itself
                return;
                }
            //
            // Note: not all libraries are needed for interactive processing (eg. coroutine, io, os, utf8 and debug).
            // It might be possible to use the method described in Chapter 33, pg 358 of the lua
            // book to only load a the basic libraries. This would put the interactive session
            // in a sandbox and make implementation safer.
            //                
            luaL_openlibs(L);
            //
            Lua_Register();	//Register the functions to be called from LUA script to execute commands
            // Rgister must run before the print() function is redefined to output to the PCMessages window
            load_stat = luaL_dostring(L, "print(\"Interactive Mode \", _VERSION)");	// display the lua version number
            if (load_stat) {
                throw "Error " + String(load_stat) + " : " + String(lua_tostring(L, -1));
			    }
            // Add the WorkingDir at the end of the Lua package path
            do {	// do block to avoid E2126 Case bypasses intialization of a local variable
                String WorkingDir = ExtractFilePath(FileName);
                // AnsiString needed because .c_str() is taken and char string required
                AnsiString PathString = "package.path = package.path .. [[;" + WorkingDir + "?.out]] .. [[;" + WorkingDir + "?.lua]]";
                AnsiString PathcString = "package.cpath = package.cpath .. [[;" + WorkingDir + "?.dll]]";
                boolean bretv;
                bretv = luaL_dostring(L, PathString.c_str());
                assert(!bretv);	// fail on error
                bretv = luaL_dostring(L, PathcString.c_str());
                assert(!bretv);	// fail on error
                //
                // if defines.lua or functions.lua exist, then run them
                //
                load_stat = luaL_dostring(L, "require([[defines]])");
                if (load_stat) {
	                // ignore error if file is not found
                    if (DEBUGGING) ptrTIUCommThread->PrintItem("defines.lua not found", (TColor) clBlue);
                    }
                load_stat = luaL_dostring(L, "require([[functions]])");
                if (load_stat) {
	                // ignore error if file is not found
                    if (DEBUGGING) ptrTIUCommThread->PrintItem("functions.lua not found", (TColor) clBlue);
                    }
                } while (false);
            // at this point, all setup requirements have succeeded and we can hook the timer
            //
			// set RTime counter to be updated by the Timer Thread
			ptrPC_ETime[MyPCNo - 1] = ptrPC->ETime;
            //
            ptrTIUCommThread->SetNumEditColor(ptrPC_ETime[MyPCNo - 1], clLime);	// set Run Time counter to green
			//
            ptrPC_Tenths[MyPCNo - 1] = 0;	// initialize the elapsed time timer to zero
            // compile and run each line typed when user presses [Enter], exit on user pressing [Esc] or [Ctrl][Z]
            break;	// run until terminated by the user
        // ------------------------------------------------------------------------
        default:
            // undefined
            return;
        }
    //-----------------------------------------------------------
    #ifdef DEBUGTIMER
    //
    QueryPerformanceFrequency(&StartFrequency);
    QueryPerformanceCounter(&StartTime);
    //
    // We now have the elapsed number of ticks, along with the
    // number of ticks-per-second. We use these values
    // to convert to the number of elapsed microseconds.
    // To guard against loss-of-precision, we convert
    // to microseconds *before* dividing by ticks-per-second.
    //
    StartTime.QuadPart *= 1000000;
    StartTime.QuadPart /= StartFrequency.QuadPart;
    #endif
    //-----------------------------------------------------------
    // https://docs.microsoft.com/en-us/windows/desktop/sync/using-waitable-timer-objects
    LARGE_INTEGER liDueTime;
    liDueTime.QuadPart = 0L;
    HANDLE handles[] = { MyTimer, RFIDReqEvent, ptrPC->TerminationPCEvent, ptrPC->FunctionPCEvent,
        ptrPC->LoopPCEvent, ptrPC->SetupPCEvent, ptrPC->CleanupPCEvent, ptrPC->InterpreterPCEvent,
        ptrPC->EventPCEvent};
        // REMEMBER TO SET CORRECT COUNT in call to WaitForMultipleObjects()
    DWORD dwretv;
    // start the waitable Timer for 100 milliseconds and continue on if it starts up OK.
    bool bTimerretv = SetWaitableTimer(MyTimer, &liDueTime, 100, NULL, NULL, false);
    if (bTimerretv) {
        // Timer is running OK
        while(!Terminated) {
            //
            dwretv = WaitForMultipleObjects(9, handles, FALSE, INFINITE);
            // Terminate on each TerminationPCEvent or error
            if (dwretv == WAIT_FAILED) {	// unknown failure?
				// prevent calling the 8 operational functions
                CallTickFunction = CallLoopFunction = CallTagFunction = CallCleanupFunction = false;
                CallUIDTagFunction = CallResponseFunction = CallNameTagFunction = false;
                CallEventFunction = false;
                Terminate();
                break;	// time to terminate
                }
			//--Execute----------------------  Cleanup PC Event -----------------------------------
            if (dwretv == WAIT_OBJECT_0 + 6) {	// CleanupPCEvent Event Signaled?
                if (DEBUGGINGX(8)) ptrTIUCommThread->PrintItem("Prog_Control_Thread(" + String(MyPCNo) + "): CleanupPCEvent");
                // Lua Cleanup
                if (L != NULL && CompileSuccess) {
    	        	if (CallCleanupFunction) {
                        luaH_CallFunction("cleanup", EventData.EngineNo, EventData.TIUNo);	// run cleanup()
                        if (DEBUGGINGX(8)) ShowinBuffer(ptrPC->PCMessages, "cleanup() complete", clNavy);
        	            }
                    // prevent calling the 8 operational functions
                    CallCleanupFunction = false;
                    CallTagFunction = false;
					CallUIDTagFunction = false;
					CallNameTagFunction = false;
					CallResponseFunction = false;
					CallLoopFunction = false;
					CallTickFunction = false;
                    CallEventFunction = false;
                    }
                Terminate();
                break;	// time to terminate
                }
			//--Execute----------------------  Event PC Event -----------------------------------
            if (dwretv == WAIT_OBJECT_0 + 8) {	// EventPCEvent Event Signaled?
                if (DEBUGGINGX(8)) ptrTIUCommThread->PrintItem("Prog_Control_Thread(" + String(MyPCNo) + "): EventPCEvent");
				ProcessEventEvent();
                continue;
                }
			//--Execute----------------------  Timer PC Event -----------------------------------
            if (dwretv == WAIT_OBJECT_0 + 0) {	// MyTimer Event Signaled ?
                //-----------------------------------------------------------
                #ifdef DEBUGTIMER
                LARGE_INTEGER CurrentTime, Frequency;
                //
                QueryPerformanceFrequency(&Frequency);
                QueryPerformanceCounter(&CurrentTime);
                //
                // We now have the elapsed number of ticks, along with the
                // number of ticks-per-second. We use these values
                // to convert to the number of elapsed microseconds.
                // To guard against loss-of-precision, we convert
                // to microseconds *before* dividing by ticks-per-second.
                //
                CurrentTime.QuadPart *= 1000000;
                CurrentTime.QuadPart /= Frequency.QuadPart;
                #endif
                //-----------------------------------------------------------
                if (CallTickFunction) {
                    TenthSecondCounter++;
                    if (TenthSecondCounter == 10) {
                        TenthSecondCounter = 0;
                        //-----------------------------------------------------------
                        #ifdef DEBUGTIMER
                        if (DEBUGGING) {
                         	// print timer information in milliseconds
                            ptrTIUCommThread->PrintItem("TProg_Control_Thread(" + String(MyPCNo) + "): Perf Counter (ms) " + String((CurrentTime.QuadPart - StartTime.QuadPart)/1000) +
                                " " + String(ptrPC->ptrTimer_Thread->RunTimeinTenths * 100.0), (TColor) clGreen);
                            }
                        #endif
                        //-----------------------------------------------------------
                        if (DEBUGGINGX(9)) {
							#if 1
                            PrintLine = Format((String)"PC%d Program Control Timer - Run Time %.2f seconds", ARRAYOFCONST((MyPCNo, PC_Tenths/10.0)));
                            #else
                            PrintLine = Format((String)"PC%d Program Control Timer - Run Time %.2f seconds", ARRAYOFCONST((MyPCNo, ptrPC_Tenths[ptrPC->ptrPC_Thread->MyPCNo - 1]/10.0)));
                            #endif
                            ptrTIUCommThread->PrintItem(PrintLine, clBlack);
                            }
                        if (ProgramRunning == RUN_LUASCRIPT && CallTickFunction) {
                            lua_getglobal(L,"tick");		// get the function and push it onto the stack
                            if(lua_isfunction(L, -1)) {		// is the tick() function present in the script?
                                // push three arguments onto the stack
                                lua_pushinteger(L, EventData.EngineNo);
                                lua_pushinteger(L, EventData.TIUNo);
                                retv = lua_pcall(L, 2, 1, 0);	// Call the function - 2 integer parameters and 1 boolean result which is not used
                                if (retv  != LUA_OK) {	// check the return value
                                    //LUA_YIELD		1	//LUA_ERRRUN	2
                                    //LUA_ERRSYNTAX	3   //LUA_ERRMEM	4
                                    //LUA_ERRGCMM	5   //LUA_ERRERR	6
                                    ShowinBuffer(ptrPC->PCMessages, "Error " + String(retv) + " running tick() : " + String(lua_tostring(L, -1)), (TColor) clRed);		// Display in Message List
                                    }
                                // return value not used, lua_pcall() clears function and 2 parameters from stack
                                lua_pop(L, 1);	// pop returned value from tick()
                                } else {
                                // not a function, don't try to call it
                                CallTickFunction = false;
                                lua_pop(L, 1);	// pop result of getglobal
                                }
                            }
                        }
                    }
            	continue;
                }
			//--Execute----------------------  Loop PC Event -----------------------------------
            if ((dwretv == WAIT_OBJECT_0 + 4) && CompileSuccess) {	// LoopPCEvent Event Signaled ?
                // call loop() until loop() returns false or StopFlag is set
                // while this is running Function button are not responded to
                if (CallLoopFunction) {
                	bool bretv = luaH_CallFunction("loop", EventData.EngineNo, EventData.TIUNo);
                    if (DEBUGGINGX(8)) ShowinBuffer(ptrPC->PCMessages, "loop() complete", clNavy);
                    // if stop not requested  and loop() returns true, continue to loop() by sending another event.
                    if (!StopFlag && bretv) {
                        SetEvent(ptrPC->LoopPCEvent);	// 2nd and on runs of loop()
                        } else {
                        CallLoopFunction = false;	// loop() returned false so quit calling it
                        }
                    }
                continue;
                }
			//--Execute----------------------  RFID Request PC Event -----------------------------------
            if ((dwretv == WAIT_OBJECT_0 + 1) && CompileSuccess) {	// RFIDReqEvent Event Signaled ?
                // every PC thread receives every tag
                // The PC thread should ignore any tag that it does not care about.
                // call RFIDTagHandler() on each RFIDReqEvent
                if (RFIDFIFO) RFIDTagHandler();	// tag received by the PC Thread, so handle it (if a tag() function was defined)
                continue;
                }
            if (dwretv == WAIT_OBJECT_0 + 1) {	//else ignore it if we didn't have a successful compile of a script
            	continue;
                }
			//--Execute----------------------  Termination PC Event -----------------------------------
            if (dwretv == WAIT_OBJECT_0 + 2) {	// TerminationPCEvent Event Signaled?
	            if (DEBUGGINGX(8)) ptrTIUCommThread->PrintItem("Prog_Control_Thread(" + String(MyPCNo) + "): TerminationPCEvent");
                Terminate();
                break;	// time to terminate
                }
			//--Execute----------------------  Setup PC Event -----------------------------------
            if ((dwretv == WAIT_OBJECT_0 + 5) && CompileSuccess) {	// SetupPCEvent Event Signaled ?
                if (DEBUGGINGX(8)) ptrTIUCommThread->PrintItem("Prog_Control_Thread(" + String(MyPCNo) + "): SetupPCEvent");
            	//
                // enable the Script function calls
                //
                // if loop() function is present, enable it
                // load the function from global
                lua_getglobal(L,"loop");		// get the function and push it onto the stack
                if(lua_isfunction(L, -1)) {		// is the loop() function present in the script?
                    CallLoopFunction = true;	// enable calling of the loop() function
                    }
                lua_pop(L,1);	// remove function
                //
                // if cleanup() function is present, enable it
                // load the function from global
                lua_getglobal(L,"cleanup");		// get the function and push it onto the stack
                if(lua_isfunction(L, -1)) {		// is the cleanup() function present in the script?
                    CallCleanupFunction = true;	// enable calling of the cleanup() function
                    }
                lua_pop(L,1);	// remove function
                //
                // if event() function is present, enable it
                // load the function from global
                lua_getglobal(L,"event");		// get the function and push it onto the stack
                if(lua_isfunction(L, -1)) {		// is the event() function present in the script?
                    CallEventFunction = true;	// enable calling of the event() function
                    }
                lua_pop(L,1);	// remove function
                //
                // if tick() function is present, enable it
                // load the function from global
                lua_getglobal(L,"tick");		// get the function and push it onto the stack
                if(lua_isfunction(L, -1)) {		// is the tick() function present in the script?
                    CallTickFunction = true;	// enable calling of the tick() function
                    }
                lua_pop(L,1);	// remove function
                //
                // if uidtag() function is present, enable it
                // load the function from global
                lua_getglobal(L,"uidtag");		// get the function and push it onto the stack
                if(lua_isfunction(L, -1)) {		// is the uidtag() function present in the script?
                    CallUIDTagFunction = true;	// enable calling of the uidtag() function
                    }
                lua_pop(L,1);	// remove function
                //
                // if response() function is present, enable it
                // load the function from global
                lua_getglobal(L,"response");		// get the function and push it onto the stack
                if(lua_isfunction(L, -1)) {		// is the response() function present in the script?
                    CallResponseFunction = true;	// enable calling of the response() function
                    }
                lua_pop(L,1);	// remove function
                //
                // if nametag() function is present, enable it
                // load the function from global
                lua_getglobal(L,"nametag");		// get the function and push it onto the stack
                if(lua_isfunction(L, -1)) {		// is the nametag() function present in the script?
                    CallNameTagFunction = true;	// enable calling of the nametag() function
                    }
                lua_pop(L,1);	// remove function
                //
				// if tag() function is present, enable it
                // load the function from global
                lua_getglobal(L,"tag");			// get the function and push it onto the stack
                if(lua_isfunction(L, -1)) {		// is the tag() function present in the script?
                    CallTagFunction = true;		// enable calling of the tag() function
                    }
                lua_pop(L,1);	// remove function
                //
                if  (CallTagFunction || CallResponseFunction || CallNameTagFunction || CallUIDTagFunction) {
					// Every PC thread has its own RFIDFIFO if it has a tag(), response(), nametag() or uidtag function
					// FIFO - Thread safe
					RFIDFIFO = new TThreadList();	//  - Thread Safe - deleted when this thread exits
					assert(RFIDFIFO != NULL);	// check for successful creation
					TList *myList = RFIDFIFO->LockList();
					try {	// Initialize myList to 100 entries
					    myList->Capacity = 100;
					    }
					__finally {
					    RFIDFIFO->UnlockList();
					    }
                	}
                // if setup() function is missing, throw an error
                // load the function from global
                lua_getglobal(L,"setup");			// get the function and push it onto the stack
                if(!lua_isfunction(L, -1)) {		// is the setup() function present in the script?
					throw TEXT("Error : setup() function not found");		// no
                    }
                lua_pop(L,1);	// remove function
                //
				if (!DEBUGGING && !bRFIDCOMConnected && !bRFIDUDPWiFiConnected && !bRFIDTCPWiFiConnected && (CallTagFunction || CallResponseFunction || CallNameTagFunction || CallUIDTagFunction)) {
					throw TEXT("Error : tag(), nametag(), response() and uidtag() functions requires that the RFID system be enabled and connected");
                    }
                // run setup() and if it returns true then run loop() if it is defined
                bool bretv = luaH_CallFunction("setup", EventData.EngineNo, EventData.TIUNo);
                if (DEBUGGINGX(8)) ShowinBuffer(ptrPC->PCMessages, "setup() complete", clNavy);
                if (bretv) {
					// setup() returned true
					if ((bRFIDCOMConnected || bRFIDUDPWiFiConnected || bRFIDTCPWiFiConnected) && (CallTagFunction || CallResponseFunction || CallNameTagFunction || CallUIDTagFunction)) {
                        if (DEBUGGINGX(8)) ShowinBuffer(ptrPC->PCMessages, "Waiting for RFID Tag Detection", clNavy);
                        }
					// load the loop() function from global only if setup() worked
                    if (CallLoopFunction) SetEvent(ptrPC->LoopPCEvent);	// first run of loop()
					}
                continue;
                }
			//--Execute----------------------  Function PC Event -----------------------------------
            if ((dwretv == WAIT_OBJECT_0 + 3) && CompileSuccess) {	// FunctionPCEvent Event Signaled ?
                char func[20];
                (void) snprintf(func, sizeof func - 1, "Function%02dName", EventData.EventNo);
                if (DEBUGGINGX(8)) ptrTIUCommThread->PrintItem("Prog_Control_Thread(" + String(MyPCNo) + "): FunctionPCEvent: " + String(func));
                luaH_CallFunction(func, EventData.EngineNo, EventData.TIUNo); 	// run Functionxx()
                continue;
                }
			//--Execute--------------------  Interpreter PC Event ----------------------------------
            if (dwretv == WAIT_OBJECT_0 + 7) {	// InterpreterPCEvent Event Signaled ?
                if (DEBUGGINGX(8)) ptrTIUCommThread->PrintItem("Prog_Control_Thread(" + String(MyPCNo) + "): InterpreterPCEvent");
            	doInterpreter();
                continue;
                }
            //--Execute-----------------------------------------------------------------------------
            // ignore other events and continue waiting
            }
		}
    }
catch(int i) {
	// integer handler
	ShowinBuffer(ptrPC->PCMessages, "Thrown exception " + String(i), (TColor) clRed);		// Display in Message List
	// Request ptrPC to stop all threads
	(void) PostMessage(ptrPC->Handle, CM_PROGCTRLSTOP, 0, 0);
	// Send CM_ EMERGENCYSTOP message to MainForm  (action can be disabled in Setup)
	if (MainForm->IsRadioConnected()) (void) PostMessage(MainForm->Handle, CM_EMERGENCYSTOP, 0, 0);
	}
catch(String s) {
	// String handler (AnsiString in BCB4.0, String in CB10.1)
	ShowinBuffer(ptrPC->PCMessages, s, (TColor) clRed);		// Display in Message List
	// Request ptrPC to stop all threads
	(void) PostMessage(ptrPC->Handle, CM_PROGCTRLSTOP, 0, 0);
	}
#if __BORLANDC__ >= 0x720
catch(AnsiString s) {
	// AnsiString handler needed in CB10.1 only
	ShowinBuffer(ptrPC->PCMessages, s, (TColor) clRed);		// Display in Message List
	// Request ptrPC to stop all threads
	(void) PostMessage(ptrPC->Handle, CM_PROGCTRLSTOP, 0, 0);
	}
    catch(const std::runtime_error& re) {
	// specific handling for runtime_error - supported in CB10.1 only
	ShowinBuffer(ptrPC->PCMessages, "Runtime error: " + String(re.what()), (TColor) clRed);		// Display in Message List
	// Request ptrPC to stop all threads
	(void) PostMessage(ptrPC->Handle, CM_PROGCTRLSTOP, 0, 0);
	}
catch(const std::exception& ex) {
	// specific handling for all exceptions extending std::exception - supported in CB10.1 only
    // except std::runtime_error which is handled explicitly
	ShowinBuffer(ptrPC->PCMessages, "Error occurred: " + String(ex.what()), (TColor) clRed);		// Display in Message List
	// Request ptrPC to stop all threads
	(void) PostMessage(ptrPC->Handle, CM_PROGCTRLSTOP, 0, 0);
	// Send CM_ EMERGENCYSTOP message to MainForm
	if (bRadioCOMConnected) (void) PostMessage(MainForm->Handle, CM_EMERGENCYSTOP, 0, 0);
	}
#endif
catch(const TCHAR * s) {
	// char string handler
	ShowinBuffer(ptrPC->PCMessages, String(s), (TColor) clRed);		// Display in Message List
	// Request ptrPC to stop all threads
	(void) PostMessage(ptrPC->Handle, CM_PROGCTRLSTOP, 0, 0);
	}
#ifndef BAD_CAST
catch(std::bad_cast &) {
	std::stdname = 9;	// Bad Cast handler
	ShowinBuffer(ptrPC->PCMessages, "Caught a bad dynamic_cast", (TColor) clRed);		// Display in Message List
	// Request ptrPC to stop all threads
	(void) PostMessage(ptrPC->Handle, CM_PROGCTRLSTOP, 0, 0);
	// Send CM_ EMERGENCYSTOP message to MainForm
	if (MainForm->IsRadioConnected()) (void) PostMessage(MainForm->Handle, CM_EMERGENCYSTOP, 0, 0);
    }
#endif
catch(...) {
	// catch everything else
	ShowinBuffer(ptrPC->PCMessages, "Your script did something that I didn't understand. Sorry, I can't help you figure it out.", (TColor) clRed);		// Display in Message List
	// Request ptrPC to stop all threads
	(void) PostMessage(ptrPC->Handle, CM_PROGCTRLSTOP, 0, 0);
	// Send CM_ EMERGENCYSTOP message to MainForm
	if (MainForm->IsRadioConnected()) (void) PostMessage(MainForm->Handle, CM_EMERGENCYSTOP, 0, 0);
	}
//
// Terminated in Execute()
//
// all clean up code moved to destructor
//
}
//--------------------------------------------------------------------------
/* int handleTag()
 *
 * Process the string received from the RFID Tag reader
 *
 * return the packet type on successful command and not an error message
 *
 * sTagPacket is a pointer to char array, null terminated.
 * ilen is the length of the array, not counting the null
 *
 */
//-------------------------------------------------------------------------
int __fastcall TProg_Control_Thread::handleTag(char * sTagPacket, int ilen) {
//
// sTagPacket contains the entire packet as received from RFCNFC
//
// Note that each packet has a checksum and an end of packet character (0xF0) appended
// sTagPacket is null terminated
//
// the string pointed to by sTagPacket is delete[]d in RFIDTagHandler()
//
// On entry, the Checksum has already been checked and may have been zeroed out
//
UP Packet;
assert(sTagPacket != NULL);
assert(ilen > 10 && ilen <= (28 * 2));
String PrintLine;
char msgBuf[128];
int iretv = 0;

if (DEBUGGINGX(8)) {
	#if 1
	PrintLine = Format((String)"PC%d handleTag() - Run Time %.2f seconds", ARRAYOFCONST((MyPCNo, PC_Tenths/10.0)));
    #else
	PrintLine = Format((String)"PC%d handleTag() - Run Time %.2f seconds", ARRAYOFCONST((MyPCNo, ptrPC_Tenths[ptrPC->ptrPC_Thread->MyPCNo - 1]/10.0)));
    #endif
    ptrTIUCommThread->PrintItem(PrintLine, clBlack);
    }
if (Hex2Digit_to_Decimal(&sTagPacket[SOPIDX]) != START_OF_PACKET){
	// if SOP is missing we have to assume that the packet is junk and should be ignored
    if (DEBUGGINGX(8)) {
		PrintLine = "PC" + String(MyPCNo) + " handleTag() : SOP Missing";
	    ptrTIUCommThread->PrintItem(PrintLine, clRed);
        }
    iretv = SOPMISSING;
    return iretv;
    } else {
	// convert the packet to binary
	for (int iDex = 0 ; iDex < (ilen/2); iDex++) {
		Packet.Byte_Array[iDex] = Hex2Digit_to_Decimal(&sTagPacket[iDex * 2]);
    	}
	if (DEBUGGINGX(8)) {
		#if 1
		snprintf(msgBuf, sizeof msgBuf, "PC%d Detector %d : (%.1fs) Reader %d TagCount %d Type/Cmd 0x%02X ByteCount %d",
			MyPCNo, Packet.Data_Packet.Header.Detector, PC_Tenths/10.0, Packet.Data_Packet.Header.Reader, Packet.Data_Packet.Header.TagCount, Packet.Data_Packet.Header.PacketType, Packet.Data_Packet.Header.ByteCount);
        #else
		snprintf(msgBuf, sizeof msgBuf, "PC%d Detector %d : (%.1fs) Reader %d TagCount %d Type/Cmd 0x%02X ByteCount %d",
			MyPCNo, Packet.Data_Packet.Header.Detector, ptrPC_Tenths[ptrPC->ptrPC_Thread->MyPCNo - 1]/10.0, Packet.Data_Packet.Header.Reader, Packet.Data_Packet.Header.TagCount, Packet.Data_Packet.Header.PacketType, Packet.Data_Packet.Header.ByteCount);
        #endif
        PrintLine = "Tag Reader Message " + String(msgBuf);
	    ptrTIUCommThread->PrintItem(PrintLine, clBlue);
	    }
   	char cmsgBuf[17];
	switch (Packet.Data_Packet.Header.PacketType) {
    	case NOP:
			if (DEBUGGINGX(8)) {
        	    PrintLine = "PC" + String(MyPCNo) + " Detector " + String(Packet.Data_Packet.Header.Detector) + " : NOP";
			    ptrTIUCommThread->PrintItem(PrintLine, clBlack);
                }
			iretv = NOP;  // do nothing
            break;
	    case ACK:
			if (DEBUGGINGX(8)) {
				PrintLine = "PC" + String(MyPCNo) + " Detector " + String(Packet.Data_Packet.Header.Detector) + " : ACK";
			    ptrTIUCommThread->PrintItem(PrintLine, clBlack);
                }
			iretv =  ACK;  // do nothing for now
    	    break;
	    case WACK:
			if (DEBUGGINGX(8)) {
				PrintLine = "PC" + String(MyPCNo) + " Detector " + String(Packet.Data_Packet.Header.Detector) + " : Write ACK";
			    ptrTIUCommThread->PrintItem(PrintLine, clBlack);
                }
			iretv =  WACK;  // do nothing for now
    	    break;
	    case NAK:
			if (DEBUGGINGX(8)) {
				PrintLine = "PC" + String(MyPCNo) + " Detector " + String(Packet.Data_Packet.Header.Detector) + " : NAK";
			    ptrTIUCommThread->PrintItem(PrintLine);
                }
			iretv =  NAK;  // do nothing for now
            break;
		case TAGDATABLOCK5:	// 0x22
    		if (DEBUGGINGX(8)) {
                char CarName[18];
                memcpy(CarName, &Packet.Data_Packet.EngineData, 16);
                CarName[17] = '\0';	// NULL terminate
                trim_str(CarName);	// trim leading and trailing spaces
				PrintLine = "PC" + String(MyPCNo) + " Detector " + String(Packet.Data_Packet.Header.Detector) + " : Engine Name " + String(CarName);
				ptrTIUCommThread->PrintItem(PrintLine, clBlue);
	            }

            //---------------------------------------------------------------------------
            if (ProgramRunning == RUN_LUASCRIPT && CallNameTagFunction) {
                luaL_checkstack(L, 5, NULL);	// check for enough stack space
                lua_getglobal(L,"nametag");			// get the function and push it onto the stack
                if(lua_isfunction(L, -1) ) {		// is the nametag() function present in the script?
                    // arguments
                    lua_pushinteger(L, Packet.Data_Packet.Header.Detector);
                    lua_pushinteger(L, Packet.Data_Packet.Header.Reader);
					lua_pushlstring(L, &sTagPacket[UIDIDX], DATAIDX - UIDIDX);	// 8 characters
                    lua_pushlstring(L, Packet.Name_Packet.CarName, 16);	// 16 characters
                    // run the function inside a try{} block
                    bool bVal = false;
                    try{	// in handleTag(), run the nametag() function
                        int retv = lua_pcall(L, 4, 1, 0);	// Call the function - 4 arguments and 1 boolean result which might be nil
                        // lua_pcall() clears function and arguments from stack
                        if (retv  != LUA_OK) {	// check the return value
                            //LUA_YIELD		1	//LUA_ERRRUN	2
                            //LUA_ERRSYNTAX	3   //LUA_ERRMEM	4
							//LUA_ERRGCMM	5   //LUA_ERRERR	6
							ShowinBuffer(ptrPC->PCMessages, "Error " + String(retv) + " in tag(): " + String(lua_tostring(L, -1)), clRed);
							CallNameTagFunction = false;
                            break;
                            }
                        if (!lua_isnil(L, -1)) {	// is the result nil?
                            bVal = lua_toboolean(L, -1);	// if not then eval the boolean result
                            lua_pop(L,1);	// remove the boolean result from the stack
                            // success, continue
                            } else {
                            // chunk failed
							CallNameTagFunction = false;
                            ShowinBuffer(ptrPC->PCMessages, "Failed - nametag()", clRed);
                            break;
                            }
						}
                    catch(EMySTOP &E) {
						if (DEBUGGING) ShowinBuffer(ptrPC->PCMessages, E.Message + " call terminated by [STOP] in tag()", (TColor) clRed);		// Display in Message List
						bVal = false;
						}
                    catch(...) {
                        // rethrow everything else
                        throw;
                        }

                    if (DEBUGGINGX(9)) ptrTIUCommThread->PrintItem("nametag() Result = " + String(bVal ? "true" : "false"), clBlue);
                    if (!bVal) {
                        CallNameTagFunction = false;	// stop calling nametag() functions if the chunk returned false
                        if (DEBUGGING) ShowinBuffer(ptrPC->PCMessages, "nametag() processing terminated", clRed);
                        break;
                        }
                    } else {	// not a function, [put up a short message]
                    // not a function, don't try to call it
                    CallNameTagFunction = false;
                    lua_pop(L, 1);	// pop result of getglobal
                    break;
                    }
                // continue
                }
            //---------------------------------------------------------------------------
			iretv =  TAGDATABLOCK5;
            break;
		case TAGDETECTED: 	// 0x03 but could not read up the 16 byte block 4 data but returned the tag UID only
        	// Note: tag substitution is perfomed in Train_Control.cpp. Once that substitution is done, TAGDATABLOCK4
            // will be used, not this TAGDETECTED code. If the substitution cannot be done because the tag is completely
            // unknown, then this TAGDETECTED code is run. If that happens, the function uidtag() is called.
			if (DEBUGGINGX(8)) {
				String smsgBuf = "PC" + String(MyPCNo) + " Detector " + String(Packet.Data_Packet.Header.Detector) + " : Tag UID Only Detected - reader " +
					String(Packet.Data_Packet.Header.Reader) + " - Tag UID " +
					IntToHex(Packet.UID_Only_Packet.UID[0], 2) + IntToHex(Packet.UID_Only_Packet.UID[1], 2) +
					IntToHex(Packet.UID_Only_Packet.UID[2], 2) + IntToHex(Packet.UID_Only_Packet.UID[3], 2);
				ptrTIUCommThread->PrintItem(smsgBuf, clBlue);
				}
            //ptrTIUCommThread->Bump(DetectedRFIDTags->UIDTagCount);	// UID only tag now bumped in Train_Control.cpp : CMProcessRFIDData()
            //---------------------------------------------------------------------------
            if (ProgramRunning == RUN_LUASCRIPT && CallUIDTagFunction) {
                luaL_checkstack(L, 4, NULL);	// check for enough stack space
                lua_getglobal(L,"uidtag");			// get the function and push it onto the stack
                if(lua_isfunction(L, -1) ) {		// is the uidtag() function present in the script?
                    // five arguments
                    lua_pushinteger(L, Packet.Data_Packet.Header.Detector);
                    lua_pushinteger(L, Packet.Data_Packet.Header.Reader);
                    lua_pushlstring(L, &sTagPacket[UIDIDX], DATAIDX - UIDIDX);	// 8 characters
                    // run the function inside a try{} block
                    bool bVal = false;
                    try{	// in handleTag(), run the uidtag() function
                        int retv = lua_pcall(L, 3, 1, 0);	// Call the function - 3 arguments and 1 boolean result which might be nil
                        // lua_pcall() clears function and arguments from stack
                        if (retv  != LUA_OK) {	// check the return value
                            //LUA_YIELD		1	//LUA_ERRRUN	2
                            //LUA_ERRSYNTAX	3   //LUA_ERRMEM	4
                            //LUA_ERRGCMM	5   //LUA_ERRERR	6
							ShowinBuffer(ptrPC->PCMessages, "Error " + String(retv) + " in tag(): " + String(lua_tostring(L, -1)), clRed);
							CallUIDTagFunction = false;
                            break;
                            }
                        if (!lua_isnil(L, -1)) {	// is the result nil?
                            bVal = lua_toboolean(L, -1);	// if not then eval the boolean result
                            lua_pop(L,1);	// remove the boolean result from the stack
                            // success, continue
                            } else {
                            // chunk failed
							CallUIDTagFunction = false;
                            ShowinBuffer(ptrPC->PCMessages, "Failed - uidtag()", clRed);
                            break;
                            }
						}
					catch(EMySTOP &E) {
						if (DEBUGGING) ShowinBuffer(ptrPC->PCMessages, E.Message + " call terminated by [STOP] in tag()", (TColor) clRed);		// Display in Message List
						bVal = false;
						}
                    catch(...) {
                        // rethrow everything else
						throw;
                        }

                    if (DEBUGGINGX(9)) ptrTIUCommThread->PrintItem("uidtag() Result = " + String(bVal ? "true" : "false"), clBlue);
                    if (!bVal) {
                        CallUIDTagFunction = false;	// stop calling uidtag() functions if the chunk returned false
                        if (DEBUGGING) ShowinBuffer(ptrPC->PCMessages, "uidtag() processing terminated", clRed);
                        break;
                        }
                    } else {	// not a function, [put up a short message]
                    // not a function, don't try to call it
                    CallUIDTagFunction = false;
                    lua_pop(L, 1);	// pop result of getglobal
                    break;
                    }
                // continue
                }
			//---------------------------------------------------------------------------
            TagCount_Thread++;
            if (DEBUGGINGX(8)) {;
            	PrintLine = "PC" + String(MyPCNo) + " handleTag() : Tag Count = " + String(TagCount_Thread);
			    ptrTIUCommThread->PrintItem(PrintLine, clBlack);
                }
			iretv = TAGDETECTED;
			break;
		case TAGDATABLOCK4:		// 0x21
            // look at the Engine Data from the tag
            if (Packet.Data_Packet.EngineData.TagID != TAG_PROGRAMMED) {
                if (DEBUGGINGX(8)) {
                    PrintLine = "PC" + String(MyPCNo) + " Detector " + String(Packet.Data_Packet.Header.Detector) + " : Tag not Programmed";
                    ptrTIUCommThread->PrintItem(PrintLine, clRed);
                    }
                iretv =  TAGNOTPROG;
                break;
                }
            // display the tag information
            if (DEBUGGINGX(8)) {
                char CarNumber[7];
                memcpy(CarNumber, &Packet.Data_Packet.EngineData.n1, 6);
                CarNumber[6] = '\0';	// NULL terminate
                trim_str(CarNumber);
                if (Packet.Data_Packet.EngineData.EngineNo == NOT_AN_ENGINE) {
                    // Not an Engine (a regular car)
                    AnsiString CarModel, RailroadName;
                    ptrTIUCommThread->GetComboBoxText(MainForm->CarModelComboBox, CarModel, Packet.Data_Packet.EngineData.CarModel);
                    ptrTIUCommThread->GetComboBoxText(MainForm->RailroadNameComboBox, RailroadName, Packet.Data_Packet.EngineData.Railroad);
                    snprintf(msgBuf, sizeof msgBuf, "PC%d Detector %d Reader %d (%.1fs) %s %s #%s",
                        MyPCNo, Packet.Data_Packet.Header.Detector, Packet.Data_Packet.Header.Reader,
						#if 1
                        PC_Tenths/10.0,
                        #else
                        ptrPC_Tenths[ptrPC->ptrPC_Thread->MyPCNo - 1]/10.0,
                        #endif
						RailroadName.c_str(), CarModel.c_str(), CarNumber);
                    } else {
                    //It is an engine
                    AnsiString EngineModel, RailroadName, TagLocation;
                    ptrTIUCommThread->GetComboBoxText(MainForm->EngineModelComboBox, EngineModel, Packet.Data_Packet.EngineData.CarModel);
                    ptrTIUCommThread->GetComboBoxText(MainForm->RailroadNameComboBox, RailroadName, Packet.Data_Packet.EngineData.Railroad);
                    ptrTIUCommThread->GetComboBoxText(MainForm->TagLocationComboBox, TagLocation, Packet.Data_Packet.EngineData.TagLocation);

                    snprintf(msgBuf, sizeof msgBuf, "PC%d Detector %d Reader %d (%.1fs) Engine #%d %s %s #%s %s",
                        MyPCNo, Packet.Data_Packet.Header.Detector, Packet.Data_Packet.Header.Reader,
						#if 1
                        PC_Tenths/10.0,
                        #else
                        ptrPC_Tenths[ptrPC->ptrPC_Thread->MyPCNo - 1]/10.0,
                        #endif
                        Packet.Data_Packet.EngineData.EngineNo,
                        RailroadName.c_str(), EngineModel.c_str(),
                        CarNumber,
                        TagLocation.c_str()
                        );
                    }
                ptrTIUCommThread->PrintItem(msgBuf, clBlue);
                }
            ptrTIUCommThread->Bump(DetectedRFIDTags->TagCount);	// tag block 4 data
            //---------------------------------------------------------------------------
            if (ProgramRunning == RUN_LUASCRIPT && CallTagFunction) {
                luaL_checkstack(L, 9, NULL);	// check for enough stack space
                lua_getglobal(L,"tag");			// get the function and push it onto the stack
                if(lua_isfunction(L, -1)) {		// is the tag() function present in the script?
                    // arguments
                    lua_pushinteger(L, Packet.Data_Packet.Header.Detector);
                    lua_pushinteger(L, Packet.Data_Packet.Header.Reader);
                    lua_pushinteger(L, Packet.Data_Packet.EngineData.EngineNo);
                    lua_pushinteger(L, Packet.Data_Packet.EngineData.TagLocation);
                    lua_pushinteger(L, Packet.Data_Packet.EngineData.CarModel);
                    lua_pushinteger(L, Packet.Data_Packet.EngineData.Railroad);
					char CarNumber[7];
                    memcpy(CarNumber, &Packet.Data_Packet.EngineData.n1, 6);
                    CarNumber[6] = '\0';	// NULL terminate
                    trim_str(CarNumber);	// trim leading and trailing spaces
                    lua_pushstring(L, CarNumber);	// up to 6 character string w/trailing NULL containing the car/engine number
                                                    // leading and trailing blanks removed
                    lua_pushlstring(L, &sTagPacket[UIDIDX], CRCIDX - UIDIDX);	// 40 characters
                    // run the function inside a try{} block
                    bool bVal = false;
                    try{	// in handleTag(), run the tag() function
                        int retv = lua_pcall(L, 8, 1, 0);	// Call the function - 8 arguments and 1 boolean result which might be nil
                        // lua_pcall() clears function and arguments from stack
                        if (retv  != LUA_OK) {	// check the return value
                            //LUA_YIELD		1	//LUA_ERRRUN	2
                            //LUA_ERRSYNTAX	3   //LUA_ERRMEM	4
							//LUA_ERRGCMM	5   //LUA_ERRERR	6
							ShowinBuffer(ptrPC->PCMessages, "Error " + String(retv) + " in tag(): " + String(lua_tostring(L, -1)), clRed);
							CallTagFunction = false;
                            CallUIDTagFunction = false;
							CallNameTagFunction = false;
							CallResponseFunction = false;
                            break;
                            }
                        if (!lua_isnil(L, -1)) {	// is the result nil?
                            bVal = lua_toboolean(L, -1);	// if not then eval the boolean result
                            lua_pop(L,1);	// remove the boolean result from the stack
                            // success, continue
                            } else {
                            // chunk failed
							CallTagFunction = false;
                            CallUIDTagFunction = false;
							CallNameTagFunction = false;
							CallResponseFunction = false;
                            ShowinBuffer(ptrPC->PCMessages, "Failed - tag()", clRed);
                            break;
                            }
						}
					catch(EMySTOP &E) {
						if (DEBUGGING) ShowinBuffer(ptrPC->PCMessages, E.Message + " call terminated by [STOP] in tag()", (TColor) clRed);		// Display in Message List
						bVal = false;
						}
					catch(...) {
                        // rethrow everything else
						throw;
                        }

                    if (DEBUGGINGX(9)) ptrTIUCommThread->PrintItem("tag() Result = " + String(bVal ? "true" : "false"), clBlue);
                    if (!bVal) {
                        CallTagFunction = false;	// stop calling tag() if the chunk returned false
                        CallUIDTagFunction = false;
						CallNameTagFunction = false;
						CallResponseFunction = false;
                        if (DEBUGGING) ShowinBuffer(ptrPC->PCMessages, "tag() processing terminated", clRed);
                        break;
                        }
                    } else {
                    // not a function, don't try to call it
                    lua_pop(L, 1);	// pop result of getglobal
                    //if (DEBUGGING) ShowinBuffer(ptrPC->PCMessages, "'tag()' function not found", clRed);
                    CallTagFunction = false;	// stop calling tag()
                    CallUIDTagFunction = false;
					CallNameTagFunction = false;
					CallResponseFunction = false;
                    break;
                    }
                // continue
                }
            //---------------------------------------------------------------------------
            TagCount_Thread++;
            if (DEBUGGINGX(8)) {;
            	PrintLine = "PC" + String(MyPCNo) + " handleTag() : Tag Count = " + String(TagCount_Thread);
			    ptrTIUCommThread->PrintItem(PrintLine, clBlack);
				}
            iretv = TAGDATABLOCK4;	// Success
            break;
        case TAGNOTPROG:
            if (DEBUGGINGX(8)) {
				PrintLine = "PC" + String(MyPCNo) + " Detector " + String(Packet.Data_Packet.Header.Detector) + " : Tag Not Programmed - reader " + String(Packet.Data_Packet.Header.Reader);
			    ptrTIUCommThread->PrintItem(PrintLine, clBlack);
                }
            iretv =  TAGNOTPROG;
            break;
        case AUTHERROR:
            if (DEBUGGINGX(8)) {
				PrintLine = "PC" + String(MyPCNo) + " Detector " + String(Packet.Data_Packet.Header.Detector) + " : Tag Authentication Failed - reader " + String(Packet.Data_Packet.Header.Reader);
			    ptrTIUCommThread->PrintItem(PrintLine, clBlack);
                }
            iretv =  AUTHERROR;
            break;
        case PN53xNOTFOUND:
            if (DEBUGGINGX(8)) {
				PrintLine = "PC" + String(MyPCNo) + " Detector " + String(Packet.Data_Packet.Header.Detector) + " : Didn't find PN53x - reader " + String(Packet.Data_Packet.Header.Reader);
			    ptrTIUCommThread->PrintItem(PrintLine, clBlack);
                }
            iretv =  PN53xNOTFOUND;
            break;
        case CANTREADBLOCK:
            if (DEBUGGINGX(8)) {;
				PrintLine = "PC" + String(MyPCNo) + " Detector " + String(Packet.Data_Packet.Header.Detector) + " : Unable to read Tag - reader " + String(Packet.Data_Packet.Header.Reader) + " - block " + String(Packet.Byte_Array[UIDIDX/2]);
			    ptrTIUCommThread->PrintItem(PrintLine, clBlack);
                }
            iretv =  CANTREADBLOCK;
            break;
        case HELLOPACKET:
            if (DEBUGGINGX(8)) {
				PrintLine = "PC" + String(MyPCNo) + " Detector " + String(Packet.Data_Packet.Header.Detector) + " : Hello v" + String(Packet.Data_Packet.Header.Reader - 0x30) + "." +
							String(Packet.Data_Packet.Header.TagCount - 0x30) + " - Number of RFID Readers Attached " + String(Packet.Byte_Array[UIDIDX/2]);
			    ptrTIUCommThread->PrintItem(PrintLine, clBlack);
				}
            iretv =  HELLOPACKET;   // Success
            break;
        case PN53xFOUND:
            if (DEBUGGINGX(8)) {
				PrintLine = "PC" + String(MyPCNo) + " Detector " + String(Packet.Data_Packet.Header.Detector) + " : Found chip PN5xx - RFID Reader " + String(Packet.Data_Packet.Header.Reader);
			    ptrTIUCommThread->PrintItem(PrintLine, clBlack);
                }
            iretv =  PN53xFOUND;   // success
            break;
        case WAITING:
            if (DEBUGGINGX(8)) {
				PrintLine = "PC" + String(MyPCNo) + " Detector " + String(Packet.Data_Packet.Header.Detector) + " : Waiting for an ISO14443A tag detection";
			    ptrTIUCommThread->PrintItem(PrintLine, clBlack);
                }
            iretv =  WAITING;   // success
            break;
        case DETECTORNAME:
            if (DEBUGGINGX(8)) {
				memcpy(cmsgBuf, &Packet.Detector_Name_Packet.Detector_Name, 16);
				cmsgBuf[16] = '\0';	// Null Terminate
				{	// remove trailing spaces
					int idx = 15;
					while(1) {
						if (idx == -1) break;	// done
						if (cmsgBuf[idx] == ' ') {	// is the character a space?
							cmsgBuf[idx] = '\0';	// yes
							idx--;
							continue;
							}
						break;	// first non-space and we are done
						}
				}
				snprintf(msgBuf, sizeof msgBuf, " Detector %d : Name:%s:",
				    Packet.Detector_Name_Packet.Header.Detector, cmsgBuf);
				PrintLine = "PC" + String(MyPCNo) + String(msgBuf);
                ptrTIUCommThread->PrintItem(PrintLine, clBlack);
                }
            iretv =  DETECTORNAME;   // success
            break;
        case DETECTORIP:
            if (DEBUGGINGX(8)) {
				memcpy(cmsgBuf, &Packet.Detector_Name_Packet.Detector_Name, 16);
				cmsgBuf[16] = '\0';	// Null Terminate
				{	// remove trailing spaces
					int idx = 15;
					while(1) {
						if (idx == -1) break;	// done
						if (cmsgBuf[idx] == ' ') {	// is the character a space?
							cmsgBuf[idx] = '\0';	// yes
							idx--;
							continue;
							}
						break;	// first non-space and we are done
						}
				}
				snprintf(msgBuf, sizeof msgBuf, " Detector %d : IP \"%s\" using %s",
					Packet.Detector_Name_Packet.Header.Detector, cmsgBuf,
					Packet.Data_Packet.Header.Reader == 0 ? "UDP" : "TCP");
					
                PrintLine = "PC" + String(MyPCNo) + String(msgBuf);
                ptrTIUCommThread->PrintItem(PrintLine, clBlack);
                }
            iretv =  DETECTORIP;   // success
            break;
        default:
            if (DEBUGGINGX(8)) {
				PrintLine = "PC" + String(MyPCNo) + " Unknown Packet - Ignored";
			    ptrTIUCommThread->PrintItem(PrintLine, clBlack);	
                }
            iretv =  -1;   // Failure
            break;
        }	// switch (Packet.Data_Packet.Header.PacketType)
	}	// if !SOP
//
//---------------------------------------------------------------------------
// All of the responses from the Detectors fall through to here
// We call the catch all routine "response(Detector, Reader, Type, Packet) in our lua script
//
// PacketType can be found in RTC_NFC.h in the section titled "Message packet format"
//---------------------------------------------------------------------------
if (ProgramRunning == RUN_LUASCRIPT && CallResponseFunction) {
    luaL_checkstack(L, 5, NULL);	// check for enough stack space
    lua_getglobal(L,"response");		// get the function and push it onto the stack
	if(lua_isfunction(L, -1) ) {		// is the response() function present in the script?
        // arguments
        lua_pushinteger(L, Packet.Data_Packet.Header.Detector);
        lua_pushinteger(L, Packet.Data_Packet.Header.Reader);
        lua_pushinteger(L, Packet.Data_Packet.Header.PacketType);
        lua_pushlstring(L, sTagPacket, ilen);	// variable number of characters
        // run the function inside a try{} block
        bool bVal = false;
        try{	// in handleTag(), run the response() function
            int retv = lua_pcall(L, 4, 1, 0);	// Call the function - 4 arguments and 1 boolean result which might be nil
            // lua_pcall() clears function and arguments from stack
            if (retv  != LUA_OK) {	// check the return value
                //LUA_YIELD		1	//LUA_ERRRUN	2
                //LUA_ERRSYNTAX	3   //LUA_ERRMEM	4
                //LUA_ERRGCMM	5   //LUA_ERRERR	6
				ShowinBuffer(ptrPC->PCMessages, "Error " + String(retv) + " running function 'response()': " + String(lua_tostring(L, -1)), clRed);
				CallResponseFunction = false;
                }
            if (!lua_isnil(L, -1)) {	// is the result nil?
                bVal = lua_toboolean(L, -1);	// if not then eval the boolean result
                lua_pop(L,1);	// remove the boolean result from the stack
                // success, continue running
                } else {
                // chunk failed
				CallResponseFunction = false;
                ShowinBuffer(ptrPC->PCMessages, "Failed - response()", clRed);
                }
            }
		catch(EMySTOP &E) {
			if (DEBUGGING) ShowinBuffer(ptrPC->PCMessages, E.Message + " call terminated by [STOP] in response()", (TColor) clRed);		// Display in Message List
			bVal = false;
			}
        catch(...) {
            // rethrow everything else
			throw;
            }

        if (DEBUGGINGX(9)) ptrTIUCommThread->PrintItem("response() Result = " + String(bVal ? "true" : "false"), clBlue);
        if (!bVal) {
            CallResponseFunction = false;	// stop calling response() function if the chunk returned false
            if (DEBUGGING) ShowinBuffer(ptrPC->PCMessages, "tag processing terminated", clRed);
            }
        } else {		// not a function, don't try to call it
        CallResponseFunction = false;
        lua_pop(L, 1);	// pop result of getglobal
        }
    }

if (DEBUGGINGX(8)) {
	#if 1
	PrintLine = "PC" + String(MyPCNo) + " handleTag() - Run Time " + String(PC_Tenths/10.0) + "s - End";
    #else
	PrintLine = "PC" + String(MyPCNo) + " handleTag() - Run Time " + String(ptrPC_Tenths[ptrPC->ptrPC_Thread->MyPCNo - 1]/10.0) + "s - End";
    #endif
    ptrTIUCommThread->PrintItem(PrintLine);
    }
return iretv;		// return when tag is processed
}
//---------------------------------------------------------------------------

void __fastcall TProg_Control_Thread::MyStartUpActiveEngines(int iDelayTimeSeconds)
{
SyncDelayTimeSeconds = iDelayTimeSeconds;
Synchronize(StartUpActiveEngines);
}
//---------------------------------------------------------------------------

void __fastcall TProg_Control_Thread::MyShutDownAllEngines(int iDelayTimeSeconds)
{
SyncDelayTimeSeconds = iDelayTimeSeconds;
Synchronize(ShutDownAllEngines);
}
//---------------------------------------------------------------------------

void __fastcall TProg_Control_Thread::MyShutDownActiveEngines(int iDelayTimeSeconds)
{
SyncDelayTimeSeconds = iDelayTimeSeconds;
Synchronize(ShutDownActiveEngines);
}
//---------------------------------------------------------------------------
void __fastcall TProg_Control_Thread::StartUpActiveEngines(void)     // called using Synchronize()
{
MainForm->StartupAllActiveEngines(SyncDelayTimeSeconds);
}
//---------------------------------------------------------------------------

void __fastcall TProg_Control_Thread::ShutDownAllEngines(void)     // called using Synchronize()
{
MainForm->ShutdownAllEngines(SyncDelayTimeSeconds);
}
//---------------------------------------------------------------------------

void __fastcall TProg_Control_Thread::ShutDownActiveEngines(void)     // called using Synchronize()
{
MainForm->ShutDownActiveEngines(SyncDelayTimeSeconds);
}
//---------------------------------------------------------------------------

void __fastcall TProg_Control_Thread::MyEngineStartUp(byte iEngineNo, int iTIUNo, int iDelayTimeSeconds)
{
// Delay time in seconds
SyncEngineNo = iEngineNo;
SyncDelayTimeSeconds = iDelayTimeSeconds;
Synchronize(EngineStartUp);
}
//---------------------------------------------------------------------------

void __fastcall TProg_Control_Thread::MyEngineShutDown(byte iEngineNo, int iTIUNo, int iDelayTimeSeconds)
{
SyncEngineNo = iEngineNo;
SyncDelayTimeSeconds = iDelayTimeSeconds;
Synchronize(EngineShutDown);
}
//---------------------------------------------------------------------------

void __fastcall TProg_Control_Thread::EngineStartUp(void)     // called using Synchronize()
{                                                       // DelayTime in Seconds
int iTIUNo = MainForm->EngineData->Cells [TIU_NUMBER] [SyncEngineNo].ToIntDef(0);
MainForm->EngineStartUp(SyncEngineNo, iTIUNo, false, SyncDelayTimeSeconds);	// Startup Engine and open Operations window
}
//---------------------------------------------------------------------------

void __fastcall TProg_Control_Thread::EngineShutDown(void)     // called using Synchronize()
{                                                        // DelayTime in Seconds
int iTIUNo = MainForm->EngineData->Cells [TIU_NUMBER] [SyncEngineNo].ToIntDef(0);
MainForm->ConsistShutDown(SyncEngineNo, iTIUNo, NULL, SyncDelayTimeSeconds);	// Shutdown Engine and close Operations window
}
//---------------------------------------------------------------------------

void __fastcall TProg_Control_Thread::MyGetCounts(void)
{
Synchronize(GetCounts);
}
//---------------------------------------------------------------------------

void __fastcall TProg_Control_Thread::GetCounts(void)     // called using Synchronize()
{
MyActiveCount = MainForm->ActiveEngineCount->ValueAsInt;
MyInactiveCount = MainForm->InactiveEngineCount->ValueAsInt;
}
//---------------------------------------------------------------------------

void __fastcall TProg_Control_Thread::MyIsEngineActive(int iEngineNo)
{
SyncEngineNo = iEngineNo;
Synchronize(GetIsEngineActive);
}
//---------------------------------------------------------------------------

void __fastcall TProg_Control_Thread::GetIsEngineActive(void)     // called using Synchronize()
{
MybResponse = MainForm->IsEngineActive(SyncEngineNo);
}
//---------------------------------------------------------------------------

void __fastcall TProg_Control_Thread::MyDoesConsistExist(int iConsistNo)
{
SyncConsistNo = iConsistNo;
Synchronize(GetDoesConsistExist);
}
//---------------------------------------------------------------------------

void __fastcall TProg_Control_Thread::GetDoesConsistExist(void)     // called using Synchronize()
{
MybResponse = MainForm->DoesConsistExist(SyncConsistNo);
}
//---------------------------------------------------------------------------

void __fastcall TProg_Control_Thread::MyCallInputBox(void)
{
Synchronize(CallInputBox);
}
//---------------------------------------------------------------------------

void __fastcall TProg_Control_Thread::MyCallInputQuery(void)
{
Synchronize(CallInputQuery);
}
//---------------------------------------------------------------------------

void __fastcall TProg_Control_Thread::MyCallInputDropDown(void)
{
Synchronize(CallInputDropDown);
}
//---------------------------------------------------------------------------

void __fastcall TProg_Control_Thread::CallInputBox(void)     // called using Synchronize()
{
/*
Call InputBox to bring up an input dialog box ready for the user to enter a string
in its edit box. The ACaption parameter is the caption of the dialog box, the
APrompt parameter is the text that prompts the user to enter input in the edit
box, and the ADefault parameter is the string that appears in the edit box when
the dialog box first appears.

If the user chooses the Cancel button, InputBox returns the default string. If
the user chooses the OK button, InputBox returns the string in the edit box.

Use the InputBox function when there is a default value that should be used
when the user chooses the Cancel button (or presses Esc) to exit the dialog. If
the application needs to know whether the user chooses OK or Cancel, use the
InputQuery function instead
*/
InputString = InputBox(ACaption, APrompt, ADefault);
}
//---------------------------------------------------------------------------

void __fastcall TProg_Control_Thread::CallInputQuery(void)     // called using Synchronize()
{
/*
Call InputQuery to bring up an input dialog box ready for the user to enter a
string in its edit box. The ACaption parameter is the caption of the dialog box,
the APrompt parameter is the text that prompts the user to enter input in the
edit box, and the Value parameter is the string that appears in the edit box
when the dialog box first appears.

If the user enters a string in the edit box and chooses OK, the Value parameter
changes to the new value.

InputQuery returns true if the user chooses OK, and false if the user chooses
Cancel or presses the Esc key.

If the default value should be used when the user cancels out of the dialog, use
InputBox.
*/
InputResponse = InputQuery(ACaption, APrompt, ADefault);
InputString = ADefault;
}
//---------------------------------------------------------------------------

void __fastcall TProg_Control_Thread::CallInputDropDown(void)     // called using Synchronize()
{
int retv;

//TDropDownQuery * ptrDDQ = new TDropDownQuery(ptrPC);
TDropDownQuery * ptrDDQ = DropDownQuery;
assert(ptrDDQ);

ptrDDQ->Caption = ACaption;
ptrDDQ->Question->Caption = APrompt;
ptrDDQ->InputComboBox->Visible = true;
ptrDDQ->NumberSpin->Visible = false;
ptrDDQ->InputComboBox->Items = DropDownList;
    /*
    Read ItemIndex to determine which item in the list is the value of the combo
    box. This value appears in the edit box portion of the combo box. The first
    item in the list has index 0, the second item has index 1, and so on. If no
    item is selected, the value of ItemIndex is -1.

    Set ItemIndex to programmatically select an item from the list.
    */
ptrDDQ->InputComboBox->ItemIndex = AIndex;
#if __BORLANDC__ >= 0x720
// Defined for CB10.1 C++ programs that use the VCL.
ptrDDQ->Position    = poDesigned;
ptrDDQ->PopupMode = pmExplicit;
ptrDDQ->PopupParent = ptrPC;
#endif
/*
// debug
    ShowMessage("Owner Left/Top: " + IntToStr(ptrPC->Left) + "," + IntToStr(ptrPC->Top) +
            "\nPopup Left/Top: " + IntToStr(DropDownQuery->Left) + "," + IntToStr(DropDownQuery->Top) +
            "\nMonitor: " + IntToStr(DropDownQuery->Monitor->MonitorNum));
    */
retv = ptrDDQ->ShowModal();

if (retv == mrCancel) {
    InputString = "";
    InputResponse = false;
    //delete ptrDDQ;
    return;
    }
InputString = ptrDDQ->InputComboBox->Text;
InputResponse = true;
//delete ptrDDQ;
}

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

void __fastcall TProg_Control_Thread::MyGetCommandLine(void)
{
Synchronize(GetCommandLine);
}
//---------------------------------------------------------------------------

void __fastcall TProg_Control_Thread::GetCommandLine(void)     // called using Synchronize()
{
InputString = ptrPC->InteractiveCommandComboBox->Text;
ptrPC->InteractiveCommandComboBox->Text = "";
}
//---------------------------------------------------------------------------

void __fastcall TProg_Control_Thread::MyGetInputLine(void)
{
Synchronize(GetInputLine);
}
//---------------------------------------------------------------------------

void __fastcall TProg_Control_Thread::GetInputLine(void)     // called using Synchronize()
{

ptrPC->InteractiveCommandComboBox->Tag = 1; // indicate to InteractiveCommandComboBoxKeyPress
                                            // that InputPCEvent should be sent on <Enter>
ptrPC->InputLabel->Caption = "input>";
ptrPC->InterpreterInputLinePanel->Visible = true;
// set focus to input line
ptrPC->InteractiveCommandComboBox->SetFocus();
//
// Wait for user to type input.
// GotInputLine() via MyGotInputLine() is called when <Enter> is pressed followed
// by the generation of an InputPCEvent.
}
//---------------------------------------------------------------------------

void __fastcall TProg_Control_Thread::MyGotInputLine(void)
{
Synchronize(GotInputLine);
}
//---------------------------------------------------------------------------
void __fastcall TProg_Control_Thread::GotInputLine(void)     // called using Synchronize()
{
//
InputString = ptrPC->InteractiveCommandComboBox->Text;
ptrPC->InteractiveCommandComboBox->Text = "";
//
ptrPC->InteractiveCommandComboBox->Tag = 0; // indicate to InteractiveCommandComboBoxKeyPress
                                            // that InteractivePCEvent should be sent on <Enter>
ptrPC->InterpreterInputLinePanel->Visible = false;
ptrPC->InputLabel->Caption = "lua>";
}
//---------------------------------------------------------------------------//---------------------------------------------------------------------------

void SetFunctionLabel(lua_State * L, TProgram_Control * MyptrPC, TButton * But, char * Lab, char * Func, int HighLight)
{
// called by a routine which is called using Synchronize so VCL reference is OK.
// Note: button labels use '&' as Keyboard Shortcut. Need to convert '&' to "&&" to display an ampersand
//
// Bold Highlight is used for an indication that the button was pushed (done in Prog_Control.cpp)
// Values:  0   no change
//          -1  clear highligthing
//          1   bold
//          2   underline
// NOTE NOTE: never completed the highlighting code (Could not figure out a good way to do it)
//
if (lua_getglobal(L, Lab) == LUA_TSTRING) {
	char * F = (char *)lua_tostring(L, -1);
    char * G = Amp(F);	// convert & to &&
	But->Caption = String(G);
    But->Hint = "Run " + String(F);
    But->Enabled = true;
    if (HighLight != 0) {
        switch (HighLight) {
            case 0:
                break;
            case -1:
                But->Font->Style = But->Font->Style >> fsBold >> fsUnderline;
                break;
            case 1:
                But->Font->Style = But->Font->Style << fsBold;
                break;
            case 2:
                But->Font->Style = But->Font->Style << fsUnderline;
                break;
            }
        }
	if (lua_getglobal(L, Func) == LUA_TFUNCTION) {
   		if (DEBUGGINGX(8)) ptrTIUCommThread->ShowinBuffer(MyptrPC->PCMessages, "Function \"" + String(Func) + "\" OK", (TColor) clNavy, MyptrPC->AutoScroll->Checked);		// Display in Message List
	    } else {
        if (DEBUGGINGX(8)) ptrTIUCommThread->ShowinBuffer(MyptrPC->PCMessages, "Function \"" + String(Func) + "\" Invalid", (TColor) clRed, MyptrPC->AutoScroll->Checked);	// Display in Message List
    	}
    free(G);
	lua_pop(L,1);	// remove function from stack
	} else {
    But->Caption = "";
    But->Hint = "";
    But->Enabled = false;
    But->Font->Style = But->Font->Style >> fsBold >> fsUnderline;
    }
lua_pop(L,1);	// remove global from stack
}
//---------------------------------------------------------------------------

void SetCounterLabel(lua_State * L,  TProgram_Control * MyptrPC, TLabel * Lbl, TAbNumEdit * Num,  char * Lab)
{
// called by a routine which is called using Synchronize so VCL reference is OK.
// Note: ShowAccelChar is set to false so & just display as &
//
if (lua_getglobal(L, Lab) == LUA_TSTRING) {
	const char * F = lua_tostring(L, -1);
	Lbl->Caption = F;
    Num->Hint = "Count of number of " + String(F);
    Lbl->Visible = true;
    Num->Visible = true;
    Num->Value = 0;
    Num->Tag = true;		// User defined
    if (DEBUGGINGX(8)) ptrTIUCommThread->ShowinBuffer(MyptrPC->PCMessages, "Counter Label \"" + String(F) + "\" OK", (TColor) clNavy, MyptrPC->AutoScroll->Checked);		// Display in Message List
	} else {
	Num->Hint = "User Defined";
	Lbl->Visible = false;
    Num->Tag = false;		// predefined
    }
lua_pop(L,1);	// remove global from stack
}
//---------------------------------------------------------------------------

void SetTabLabel(lua_State * L,  TProgram_Control * MyptrPC, TTabSheet * Lbl,  char * Lab)
{
// called by a routine which is called using Synchronize so VCL reference is OK.
// Note: ShowAccelChar is set to false so '&' just displays as '&'
//
if (lua_getglobal(L, Lab) == LUA_TSTRING) {
	const char * F = lua_tostring(L, -1);
	Lbl->Caption = F;
    if (DEBUGGINGX(8)) ptrTIUCommThread->ShowinBuffer(MyptrPC->PCMessages, "Tab Label \"" + String(F) + "\" OK", (TColor) clNavy, MyptrPC->AutoScroll->Checked);		// Display in Message List
	}
lua_pop(L,1);	// remove global from stack
}
//---------------------------------------------------------------------------

void __fastcall TProg_Control_Thread::SetUpLabelsSync(void)
{
// recreates the button and Counter labels after they are changed
//
Synchronize(SetUpLabels);	// configuration variables "FunctionXXName", "FunctionXXLabel", "CounterXXLabel", title
}
//---------------------------------------------------------------------------

void __fastcall TProg_Control_Thread::SetUpLabels(void)     // called using Synchronize()
{
// VCL reference is OK.
// Function Button Labels
SetFunctionLabel(L, ptrPC, ptrPC->Function01Button, "Function01Label", "Function01Name");
SetFunctionLabel(L, ptrPC, ptrPC->Function02Button, "Function02Label", "Function02Name");
SetFunctionLabel(L, ptrPC, ptrPC->Function03Button, "Function03Label", "Function03Name");
SetFunctionLabel(L, ptrPC, ptrPC->Function04Button, "Function04Label", "Function04Name");
SetFunctionLabel(L, ptrPC, ptrPC->Function05Button, "Function05Label", "Function05Name");
SetFunctionLabel(L, ptrPC, ptrPC->Function06Button, "Function06Label", "Function06Name");
SetFunctionLabel(L, ptrPC, ptrPC->Function07Button, "Function07Label", "Function07Name");
SetFunctionLabel(L, ptrPC, ptrPC->Function08Button, "Function08Label", "Function08Name");
SetFunctionLabel(L, ptrPC, ptrPC->Function09Button, "Function09Label", "Function09Name");
SetFunctionLabel(L, ptrPC, ptrPC->Function10Button, "Function10Label", "Function10Name");
//
SetFunctionLabel(L, ptrPC, ptrPC->Function11Button, "Function11Label", "Function11Name");
SetFunctionLabel(L, ptrPC, ptrPC->Function12Button, "Function12Label", "Function12Name");
SetFunctionLabel(L, ptrPC, ptrPC->Function13Button, "Function13Label", "Function13Name");
SetFunctionLabel(L, ptrPC, ptrPC->Function14Button, "Function14Label", "Function14Name");
SetFunctionLabel(L, ptrPC, ptrPC->Function15Button, "Function15Label", "Function15Name");
SetFunctionLabel(L, ptrPC, ptrPC->Function16Button, "Function16Label", "Function16Name");
SetFunctionLabel(L, ptrPC, ptrPC->Function17Button, "Function17Label", "Function17Name");
SetFunctionLabel(L, ptrPC, ptrPC->Function18Button, "Function18Label", "Function18Name");
SetFunctionLabel(L, ptrPC, ptrPC->Function19Button, "Function19Label", "Function19Name");
SetFunctionLabel(L, ptrPC, ptrPC->Function20Button, "Function20Label", "Function20Name");
//
SetFunctionLabel(L, ptrPC, ptrPC->Function21Button, "Function21Label", "Function21Name");
SetFunctionLabel(L, ptrPC, ptrPC->Function22Button, "Function22Label", "Function22Name");
SetFunctionLabel(L, ptrPC, ptrPC->Function23Button, "Function23Label", "Function23Name");
SetFunctionLabel(L, ptrPC, ptrPC->Function24Button, "Function24Label", "Function24Name");
SetFunctionLabel(L, ptrPC, ptrPC->Function25Button, "Function25Label", "Function25Name");
SetFunctionLabel(L, ptrPC, ptrPC->Function26Button, "Function26Label", "Function26Name");
SetFunctionLabel(L, ptrPC, ptrPC->Function27Button, "Function27Label", "Function27Name");
SetFunctionLabel(L, ptrPC, ptrPC->Function28Button, "Function28Label", "Function28Name");
SetFunctionLabel(L, ptrPC, ptrPC->Function29Button, "Function29Label", "Function29Name");
SetFunctionLabel(L, ptrPC, ptrPC->Function30Button, "Function30Label", "Function30Name");
// Tab Labels
SetTabLabel(L, ptrPC, ptrPC->ButtonSheet1, "Tab01Label");
SetTabLabel(L, ptrPC, ptrPC->ButtonSheet2, "Tab02Label");
SetTabLabel(L, ptrPC, ptrPC->ButtonSheet3, "Tab03Label");
// Counter Labels
// all six counters can become user defined
SetCounterLabel(L, ptrPC, ptrPC->Counter01Label, ptrPC->Counter01, "Counter01Label");
SetCounterLabel(L, ptrPC, ptrPC->Counter02Label, ptrPC->Counter02, "Counter02Label");
SetCounterLabel(L, ptrPC, ptrPC->Counter03Label, ptrPC->Counter03, "Counter03Label");
SetCounterLabel(L, ptrPC, ptrPC->Counter04Label, ptrPC->Counter04, "Counter04Label");
SetCounterLabel(L, ptrPC, ptrPC->Counter05Label, ptrPC->Counter05, "Counter05Label");
SetCounterLabel(L, ptrPC, ptrPC->Counter06Label, ptrPC->Counter06, "Counter06Label");
// setup title on Program Control window if requested
if (strlen(Title) > 0) ptrPC->Title->Caption = Title;
ptrPC->Update();	// repaint
}
//---------------------------------------------------------------------------

void __fastcall TProg_Control_Thread::RFIDTagHandler()
{
// process the tag
// pick the RFID packet out of the RFIDFIFO
//
// This function is only called if a tag() function is defined
// and the RFIDFIFO was created
//
char * sTagBuf;
while(!Terminated) {
    TList *myList = RFIDFIFO->LockList();
    //
    if (myList->Count > 0) {
        // RFIDFIFO has a tag ready to handle
       	// sTagBuf created by new, must be delete[]d
        sTagBuf = reinterpret_cast<char *>(myList->Items[0]);  // Thread Safe
        // remove(sTagBuf) the Items[0] pointer from the FIFO (note: actual buffer memory is still allocated)
        // from Help: Alternative: Delete by Index:
        // While Remove is generally preferred when you have a pointer to the item, TList also has a Delete(Index: Integer) method if you need to remove an item by its position. This also needs to be performed within a locked section.
		myList->Delete(0);
        RFIDFIFO->UnlockList();
        // process the tag
        int iBufLen = strlen(sTagBuf);
        assert(iBufLen > 10 && iBufLen <= 28 * 2);  // This fails sometimes on the layout when a reader appears
                                                    // to be confused.
        if (CallTagFunction || CallUIDTagFunction || CallResponseFunction || CallNameTagFunction) {
        	(void) handleTag(sTagBuf, iBufLen);		// handleTag() will ignore tags it does not care about
	   	    // increment global tag count done in train_control.cpp before RFIDReqEvent is send
            }
        // free the tag's memory
        delete[] sTagBuf;							// At this point, we have processed the tag so we can delete[] it
        // go and try to process another Ttag
        } else {
        // RFIDFIFO is empty
        RFIDFIFO->UnlockList();
        break; 	// nothing left to do right now
        }
	}
return;
}
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
// Throttle
//
// Params:
// iMPH - speed in Scale miles per hour
// when - time in seconds from now to execute the command
//
// Does not sound the bell and whistle unless the Operations window is up and AutoBellWhistle is set to true
//
//---------------------------------------------------------------------------

bool __fastcall TProg_Control_Thread::Throttle(int iMPH, float when, byte iEngineNo, byte iTIUNo) { // Operand Speed # 0-120 in decimal
// set speed
if ((iMPH < 0) || (iMPH > SPEEDMAX)) iMPH = 0; // invalid - stop the engine
// Scale the speed if requested in Setup
const int ScaledMPH = iMPH / MainForm->GlobalMPHScaling;
bool bretv = true;

// schedule a speed command
if (ptrOp[iEngineNo]) {	// if we have an Operations window, use it
    ptrDL_Thread->Send_Delayed_DL_FIFO(ptrOp[iEngineNo], ptrOp[iEngineNo]->Handle, DC_SETSPEED, iEngineNo, iTIUNo, MyRemoteNo, when * 1000, iMPH);
    if (Setup->Safety_Mode->Checked) ptrDL_Thread->Send_Delayed_DL_FIFO(ptrOp[iEngineNo], ptrOp[iEngineNo]->Handle, DC_SETSPEED, iEngineNo, iTIUNo, MyRemoteNo, when * 1000, iMPH);
	} else {
	char command[CMDMAX];
	snprintf(command, sizeof command, SPEED_SET, ScaledMPH);
	bretv = ptrDL_Thread->Send_String_DL_FIFO(when * 1000, iEngineNo, command,
		iTIUNo, MyRemoteNo, 11, SHORT_WAIT, RECORD_OK, ptrLUEngineList[iEngineNo], NULL, iMPH);	// Use RTC's slected remote
	// in Safety_Mode send the speed command twice to be sure it takes - suggested by Mike Hewett
	if (bretv && Setup->Safety_Mode->Checked) bretv |= ptrDL_Thread->Send_String_DL_FIFO(when * 1000, iEngineNo, command,
		iTIUNo, MyRemoteNo, 11, SHORT_WAIT, RECORD_OK, ptrLUEngineList[iEngineNo], NULL, iMPH);	// Use RTC's slected remote
    }
if (bretv) LastSentSpeedValue[iEngineNo] = iMPH;
return bretv;
}
//---------------------------------------------------------------------------
// Set Engine Direction
//
// Params:
// bDir - direction, FORWARD or REVERSE
// when - time in seconds from now to execute the command
//
//---------------------------------------------------------------------------

bool __fastcall TProg_Control_Thread::SetDirection(bool bDir, float when, byte iEngineNo, byte iTIUNo) { // FORWARD = forward,  REVERSE = reverse
// set speed
bool bretv = true;
predictedDirection[iEngineNo] = bDir; // future predicted direction
if (ptrOp[iEngineNo]) {	// if we have an Operations window, use it
   	if (bDir == ENG_FORWARD) {
	    // in Safety_Mode always send the direction command twice to be sure it takes - suggested by Mike Hewett
	    ptrDL_Thread->Send_Delayed_DL_FIFO(ptrOp[iEngineNo], ptrOp[iEngineNo]->Handle, DC_DIR_FORWARD, iEngineNo, iTIUNo, MyRemoteNo, when * 1000, 0);
	    if (Setup->Safety_Mode->Checked) ptrDL_Thread->Send_Delayed_DL_FIFO(ptrOp[iEngineNo], ptrOp[iEngineNo]->Handle, DC_DIR_FORWARD, iEngineNo, iTIUNo, MyRemoteNo, when * 1000, 0);
    	} else {
	    // in Safety_Mode send the direction command twice to be sure it takes - suggested by Mike Hewett
	    ptrDL_Thread->Send_Delayed_DL_FIFO(ptrOp[iEngineNo], ptrOp[iEngineNo]->Handle, DC_DIR_REVERSE, iEngineNo, iTIUNo, MyRemoteNo, when * 1000, 0);
	    if (Setup->Safety_Mode->Checked) ptrDL_Thread->Send_Delayed_DL_FIFO(ptrOp[iEngineNo], ptrOp[iEngineNo]->Handle, DC_DIR_REVERSE, iEngineNo, iTIUNo, MyRemoteNo, when * 1000, 0);
        }
	} else {
	char command[CMDMAX];
	snprintf(command, sizeof command, DIRECTION_CHANGE, bDir == (ENG_FORWARD ? 0 : 1));
	// schedule a direction command
	bretv = ptrDL_Thread->Send_String_DL_FIFO(when * 1000, iEngineNo, command,
		iTIUNo, MyRemoteNo, 11, SHORT_WAIT, RECORD_OK, ptrLUEngineList[iEngineNo], NULL, bDir);	// Use RTC's slected remote
	
	// in Safety_Mode send the direction command twice to be sure it takes - suggested by Mike Hewett
	if (bretv && Setup->Safety_Mode->Checked) bretv |= ptrDL_Thread->Send_String_DL_FIFO(when * 1000, iEngineNo, command,
		iTIUNo, MyRemoteNo, 11, SHORT_WAIT, RECORD_OK, ptrLUEngineList[iEngineNo], NULL, bDir);	// Use RTC's slected remote
    }
if (bretv) direction[iEngineNo] = bDir;
return bretv;
}
//---------------------------------------------------------------------------
// Open Front or Rear Coupler
//
// Params:
// bCoupler - coupler, FRONT or REAR
// when - time in seconds from now to execute the command
//
//---------------------------------------------------------------------------

bool __fastcall TProg_Control_Thread::OpenCoupler(bool bCoupler, float when, byte iConsistNo, byte iTIUNo) { // FRONT = front coupler,  REAR = rear coupler
// open coupler
char command[CMDMAX];
snprintf(command, sizeof command, COUPLER_CMD, bCoupler);
// schedule a coupler open command
bool bretv = ptrDL_Thread->Send_String_DL_FIFO(when * 1000, iConsistNo, command, iTIUNo, MyRemoteNo, 11, SHORT_WAIT, RECORD_OK, ptrLUEngineList[iConsistNo]);	// Shortened Form
return bretv;
}
//---------------------------------------------------------------------------
// Set Acc/Dec Rate
//
// Params:
// iAcc - Acceleration rate 1 to 25 (in Smph/sec), 0 means no change
// iDec - Deceleration rate 1 to 25 (in Smph/sec), 0 means no change
// when - time in seconds from now to execute the command
//
//---------------------------------------------------------------------------

bool __fastcall TProg_Control_Thread::Rate(byte iAcc, byte iDec, float when, byte iConsistNo, byte iTIUNo) {
bool bretv = true;
if (iAcc > 0) {	// no command sent to engine if rate is 0 or negative
  if (iAcc > 25) iAcc = 25; // range check
  char command1[CMDMAX];
  snprintf(command1, sizeof command1, ACC_RATE, iAcc);
  // schedule Acceleration command
  bretv = ptrDL_Thread->Send_String_DL_FIFO(when * 1000, iConsistNo, command1, iTIUNo, MyRemoteNo, 11, SHORT_WAIT, RECORD_OK, ptrLUEngineList[iConsistNo]);	// Shortened Form
  }
//
if (iDec > 0) {	// no command sent to engine if rate is 0 or negative
  if (iDec > 25) iDec = 25; // range check
  char command2[CMDMAX];
  snprintf(command2, sizeof command2, DEC_RATE, iDec);
  // schedule Deceleration command
  bretv = ptrDL_Thread->Send_String_DL_FIFO(when * 1000, iConsistNo, command2, iTIUNo, MyRemoteNo, 11, SHORT_WAIT, RECORD_OK, ptrLUEngineList[iConsistNo]);	// Shortened Form
  }
return bretv;
}
//---------------------------------------------------------------------------
// GetRate
//
// Params:
// iAcc - pointer to Acceleration rate 1 to 25 (in Smph/sec)
// iDec - pointer to Deceleration rate 1 to 25 (in Smph/sec)
//
// returns true on success, false on failure
//---------------------------------------------------------------------------

bool __fastcall TProg_Control_Thread::GetRate(byte * iAcc, byte * iDec, int EngineNo, int TIUNo)
{
//
*iAcc = 0; *iDec = 0;
int iTIUCount;
byte ADbyteResp[CMDMAX];
bool bretv = ptrTIUCommThread->Send_String_FIFO(EngineNo, "Dq", TIUNo, MyRemoteNo, 17, MED_WAIT, NO_RECORD, NULL, NULL, &iTIUCount, ADbyteResp);

if (bretv && (iTIUCount == 17 || iTIUCount == 12)) {	// this command seems to return a response code of 0x00 so that is not an error indication
	// pull out the digits from the Response
    if (iTIUCount == 17 ) {
    	// A969 5A6A 6656 5555 5655 5559 5655
	    //                          Acc  Dec
        *iAcc = ADbyteResp[5];
        *iDec = ADbyteResp[6];
        } else {
        // Handle WTIU ASCII Response
        //Dq:04,02
        //   ^^      <---- Acc  -- returned by the TIU
        //      ^^   <---- Dec
        int a1, d1;
		int retv = sscanf(ADbyteResp, "Dq:%d,%d", &a1, &d1);
        if (retv == 2) {
            *iAcc = a1;
            *iDec = d1;
            } else {
            *iAcc = 4;
            *iDec = 2;
            }
        }
    return true;
	}
// on error, don't change iACC or iDec
return false;
}
//---------------------------------------------------------------------------
// Bell
//
// Params:
// bState - ON or OFF
// when - time in seconds from now to execute the command
//
//---------------------------------------------------------------------------
bool __fastcall TProg_Control_Thread::Bell(bool bState, float when, byte iEngineNo, byte iTIUNo) {   // bState : ON = on, OFF = off, time delay in seconds
bool bretv = true;
// schedule a bell command
if (ptrOp[iEngineNo]) {	// if we have an Operations window, use it
	if (bState == ON) {
        ptrDL_Thread->Send_Delayed_DL_FIFO(ptrOp[iEngineNo], ptrOp[iEngineNo]->Handle, DC_BELLON, iEngineNo, iTIUNo, MyRemoteNo, when * 1000, 0);
		} else {
        ptrDL_Thread->Send_Delayed_DL_FIFO(ptrOp[iEngineNo], ptrOp[iEngineNo]->Handle, DC_BELLOFF, iEngineNo, iTIUNo, MyRemoteNo, when * 1000, 0);
        // in Safety_Mode this sends bell off twice to be sure it takes - suggested by Mike Hewett
        if (Setup->Safety_Mode->Checked) ptrDL_Thread->Send_Delayed_DL_FIFO(ptrOp[iEngineNo], ptrOp[iEngineNo]->Handle, DC_BELLOFF, iEngineNo, iTIUNo, MyRemoteNo, when * 1000, 0);
	    }
    } else {
    bretv = ptrDL_Thread->Send_String_DL_FIFO(when * 1000, iEngineNo, bState == ON ? BELL_ON : BELL_OFF,
		iTIUNo, MyRemoteNo, 11, SHORT_WAIT, RECORD_OK, ptrLUEngineList[iEngineNo], NULL, bState);	// Use RTC's slected remote
	if (bretv && bState == OFF && Setup->Safety_Mode->Checked) {	// if bell off, send twice in Safety_Mode to be sure it takes - suggested by Mike Hewett
	    bretv |= ptrDL_Thread->Send_String_DL_FIFO(when * 1000, iEngineNo, BELL_OFF,
			iTIUNo, MyRemoteNo, 11, SHORT_WAIT, RECORD_OK, ptrLUEngineList[iEngineNo], NULL, bState);	// Use RTC's slected remote
		}
	}
if (bretv) bell[iEngineNo] = bState;
return bretv;
}
//---------------------------------------------------------------------------
// Markers
//
// Params:
// bState - ON or OFF
// when - time in seconds from now to execute the command
//
//---------------------------------------------------------------------------
bool __fastcall TProg_Control_Thread::Markers(bool bState, float when, byte iEngineNo, byte iTIUNo) {   // bState : ON = on, OFF = off, time delay in seconds
bool bretv = true;
// schedule a markers command
if (ptrOp[iEngineNo]) {	// if we have an Operations window, use it
	if (bState == ON) {
		ptrDL_Thread->Send_Delayed_DL_FIFO(ptrOp[iEngineNo], ptrOp[iEngineNo]->Handle, DC_MARKERS_ON, iEngineNo, iTIUNo, MyRemoteNo, when * 1000, 0);
		} else {
		ptrDL_Thread->Send_Delayed_DL_FIFO(ptrOp[iEngineNo], ptrOp[iEngineNo]->Handle, DC_MARKERS_OFF, iEngineNo, iTIUNo, MyRemoteNo, when * 1000, 0);
	    }
    } else {
	bretv = ptrDL_Thread->Send_String_DL_FIFO(when * 1000, iEngineNo, bState == ON ? MARKERS_ON : MARKERS_OFF,
		iTIUNo, MyRemoteNo, 11, SHORT_WAIT, RECORD_OK, ptrLUEngineList[iEngineNo], NULL, bState);	// Use RTC's slected remote
	}
if (bretv) marker[iEngineNo] = bState;
return bretv;
}
//---------------------------------------------------------------------------
// Beacon
//
// Params:
// bState - ON or OFF
// when - time in seconds from now to execute the command
//
//---------------------------------------------------------------------------
bool __fastcall TProg_Control_Thread::Beacon(bool bState, float when, byte iEngineNo, byte iTIUNo) {   // bState : ON = on, OFF = off, time delay in seconds
bool bretv = true;
// schedule a beacon command
if (ptrOp[iEngineNo]) {	// if we have an Operations window, use it
	if (bState == ON) {
		ptrDL_Thread->Send_Delayed_DL_FIFO(ptrOp[iEngineNo], ptrOp[iEngineNo]->Handle, DC_BEACON_ON, iEngineNo, iTIUNo, MyRemoteNo, when * 1000, 0);
		} else {
		ptrDL_Thread->Send_Delayed_DL_FIFO(ptrOp[iEngineNo], ptrOp[iEngineNo]->Handle, DC_BEACON_OFF, iEngineNo, iTIUNo, MyRemoteNo, when * 1000, 0);
	    }
    } else {
	bretv = ptrDL_Thread->Send_String_DL_FIFO(when * 1000, iEngineNo, bState == ON ? BEACON_ON : BEACON_OFF,
		iTIUNo, MyRemoteNo, 11, SHORT_WAIT, RECORD_OK, ptrLUEngineList[iEngineNo], NULL, bState);	// Use RTC's slected remote
	}
if (bretv) beacon[iEngineNo] = bState;
return bretv;
}
//---------------------------------------------------------------------------
// Whistle or Horn
//
// Params:
// bState - ON or OFF
// when - time in seconds from now to execute the command
//
//---------------------------------------------------------------------------
bool __fastcall TProg_Control_Thread::Whistle(bool wState, float when, byte iEngineNo, byte iTIUNo) {   // wState : ON = on, OFF = off
bool bretv = true;
// schedule a whistle or horn command
if (ptrOp[iEngineNo]) {	// if we have an Operations window, use it
    if (wState == ON) {
		ptrDL_Thread->Send_Delayed_DL_FIFO(ptrOp[iEngineNo], ptrOp[iEngineNo]->Handle, DC_HORN_WHISTLE_ON, iEngineNo, iTIUNo, MyRemoteNo, when * 1000, 0);
    	} else {
        ptrDL_Thread->Send_Delayed_DL_FIFO(ptrOp[iEngineNo], ptrOp[iEngineNo]->Handle, DC_HORN_WHISTLE_OFF, iEngineNo, iTIUNo, MyRemoteNo, when * 1000, 0);
        // in Safety_Mode this sends horn/whistle off twice to be sure it takes - suggested by Mike Hewett
		if (Setup->Safety_Mode->Checked) ptrDL_Thread->Send_Delayed_DL_FIFO(ptrOp[iEngineNo], ptrOp[iEngineNo]->Handle, DC_HORN_WHISTLE_OFF, iEngineNo, iTIUNo, MyRemoteNo, when * 1000, 0);
        }
	} else {
    bretv = ptrDL_Thread->Send_String_DL_FIFO(when * 1000, iEngineNo, wState == ON ? WHISTLE_ON : WHISTLE_OFF,
		iTIUNo, MyRemoteNo, 11, SHORT_WAIT, RECORD_OK, ptrLUEngineList[iEngineNo], NULL, wState);	// Use RTC's slected remote
	if (bretv && wState == OFF && Setup->Safety_Mode->Checked) {	// if whistle off, send twice in Safety_Mode to be sure it takes - suggested by Mike Hewett
    	bretv |= ptrDL_Thread->Send_String_DL_FIFO(when * 1000, iEngineNo, WHISTLE_OFF,
			iTIUNo, MyRemoteNo, 11, SHORT_WAIT, RECORD_OK, ptrLUEngineList[iEngineNo], NULL, wState);	// Use RTC's slected remote
		}
    }
if (bretv) whistle[iEngineNo] = wState;
return bretv;
}
//---------------------------------------------------------------------------
// Cab Chat
//
// Params:
// bState - ON or OFF
// when - time in seconds from now to execute the command
//
//---------------------------------------------------------------------------
bool __fastcall TProg_Control_Thread::CabChat(bool bState, float when, byte iEngineNo, byte iTIUNo) {   // bState : ON = on, OFF = off, time delay in seconds
bool bretv = true;
// schedule a Cab Chat command
if (ptrOp[iEngineNo]) {	// if we have an Operations window, use it
	if (bState == ON) {
		ptrDL_Thread->Send_Delayed_DL_FIFO(ptrOp[iEngineNo], ptrOp[iEngineNo]->Handle, DC_CAB_CHAT_ON, iEngineNo, iTIUNo, MyRemoteNo, when * 1000, 0);
		} else {
		ptrDL_Thread->Send_Delayed_DL_FIFO(ptrOp[iEngineNo], ptrOp[iEngineNo]->Handle, DC_CAB_CHAT_OFF, iEngineNo, iTIUNo, MyRemoteNo, when * 1000, 0);
	    }
    } else {
    // TODO : Patched Cab Chat for version 1.06 of the WTIU software
	//bretv = ptrDL_Thread->Send_String_DL_FIFO(when * 1000, iEngineNo, bState == ON ? CAB_CHAT_ON : CAB_CHAT_OFF,
	bretv = ptrDL_Thread->Send_String_DL_FIFO(when * 1000, iEngineNo, bState == ON ? CAB_CHAT_OFF : CAB_CHAT_ON,
		iTIUNo, MyRemoteNo, 11, SHORT_WAIT, RECORD_OK, ptrLUEngineList[iEngineNo], NULL, bState);	// Use RTC's slected remote
	}
if (bretv) cabchat[iEngineNo] = bState;
return bretv;
}
//---------------------------------------------------------------------------
// Smoke
//
// Params:
// bState - ON or OFF
// when - time in seconds from now to execute the command
//
//---------------------------------------------------------------------------
bool __fastcall TProg_Control_Thread::Smoke(bool bState, float when, byte iEngineNo, byte iTIUNo) {   // bState : ON = on, OFF = off, time delay in seconds
bool bretv = true;
// schedule a Smoke command
if (ptrOp[iEngineNo]) {	// if we have an Operations window, use it
	if (bState == ON) {
		ptrDL_Thread->Send_Delayed_DL_FIFO(ptrOp[iEngineNo], ptrOp[iEngineNo]->Handle, DC_SMOKE_ON, iEngineNo, iTIUNo, MyRemoteNo, when * 1000, 0);
		} else {
		ptrDL_Thread->Send_Delayed_DL_FIFO(ptrOp[iEngineNo], ptrOp[iEngineNo]->Handle, DC_SMOKE_OFF, iEngineNo, iTIUNo, MyRemoteNo, when * 1000, 0);
	    }
    } else {
	bretv = ptrDL_Thread->Send_String_DL_FIFO(when * 1000, iEngineNo, bState == ON ? SMOKE_EXHAUST_ON : SMOKE_EXHAUST_OFF,
		iTIUNo, MyRemoteNo, 11, SHORT_WAIT, RECORD_OK, ptrLUEngineList[iEngineNo], NULL, bState);	// Use RTC's slected remote
	}
if (bretv) smoke[iEngineNo] = bState;
return bretv;
}
//---------------------------------------------------------------------------
// Sound - Set Master Volume to either 100% (on) or 0% (off)
//
// Params:
// bState - ON or OFF
// when - time in seconds from now to execute the command
//
//---------------------------------------------------------------------------
bool __fastcall TProg_Control_Thread::Sound(bool bState, float when, byte iEngineNo, byte iTIUNo) {   // bState : ON = on, OFF = off, time delay in seconds
bool bretv = true;
// schedule a Sound command
if (ptrOp[iEngineNo]) {	// if we have an Operations window, use it
	if (bState == ON) {
		ptrDL_Thread->Send_Delayed_DL_FIFO(ptrOp[iEngineNo], ptrOp[iEngineNo]->Handle, DC_SOUNDS_ON, iEngineNo, iTIUNo, MyRemoteNo, when * 1000, 0);
		} else {
		ptrDL_Thread->Send_Delayed_DL_FIFO(ptrOp[iEngineNo], ptrOp[iEngineNo]->Handle, DC_SOUNDS_OFF, iEngineNo, iTIUNo, MyRemoteNo, when * 1000, 0);
	    }
    } else {
	bretv = ptrDL_Thread->Send_String_DL_FIFO(when * 1000, iEngineNo, bState == ON ? "v0100" : "v0000",
		iTIUNo, MyRemoteNo, 11, SHORT_WAIT, RECORD_OK, ptrLUEngineList[iEngineNo], NULL, bState);	// Use RTC's slected remote
	}
if (bretv) sound[iEngineNo] = bState;
return bretv;
}
//---------------------------------------------------------------------------
// Chuff Rate
//
// Params:
// bState - 2 or 4
// when - time in seconds from now to execute the command
//
//---------------------------------------------------------------------------
bool __fastcall TProg_Control_Thread::ChuffRate(int Rate, float when, byte iEngineNo, byte iTIUNo) {   // bState : ON = on, OFF = off, time delay in seconds
bool bretv = true;
assert(Rate == 2 || Rate == 4);
// schedule a Chuff Rate Command command
if (ptrOp[iEngineNo]) {	// if we have an Operations window, use it
	if (Rate == 4) {
		ptrDL_Thread->Send_Delayed_DL_FIFO(ptrOp[iEngineNo], ptrOp[iEngineNo]->Handle, DC_CHUFF_RATE_4, iEngineNo, iTIUNo, MyRemoteNo, when * 1000, 0);
		} else {
		ptrDL_Thread->Send_Delayed_DL_FIFO(ptrOp[iEngineNo], ptrOp[iEngineNo]->Handle, DC_CHUFF_RATE_2, iEngineNo, iTIUNo, MyRemoteNo, when * 1000, 0);
	    }
    } else {
	bretv = ptrDL_Thread->Send_String_DL_FIFO(when * 1000, iEngineNo, Rate == 2 ? CHUFF_RATE_2 : CHUFF_RATE_4,
		iTIUNo, MyRemoteNo, 11, SHORT_WAIT, RECORD_OK, ptrLUEngineList[iEngineNo], NULL, Rate);	// Use RTC's slected remote
	}
if (bretv) chuffrate[iEngineNo] = Rate;
return bretv;
}
//---------------------------------------------------------------------------
// StartUp - run a complete engine/lashup startup sequence including double toot (if AutoBellWhistle was set to true in the script)
//
// Params:
// when - time in seconds from now to execute the command
//
//---------------------------------------------------------------------------
bool __fastcall TProg_Control_Thread::StartUp(float when, byte iConsistNo, byte iTIUNo) {   //time delay in seconds
// load configuration variables
do { 	//  global AutoBellWhistle in the script overrides program settings
    if (lua_getglobal(L, "AutoBell") == LUA_TBOOLEAN) {	// true or false
        bool ABW = lua_toboolean(L, -1);
        AutoBellGlobal = ABW;
        } else {
        // if an operations window is open, use its setting
        if (ptrOp[iConsistNo]) {
            AutoBellGlobal = Setup->AutoBell->Checked &&  ptrOp[iConsistNo]->AutoBellLED->Checked;
            } else {
            // lastly use the program setting from Setup
            AutoBellGlobal = Setup->AutoBell->Checked;
            }
        }
    lua_pop(L,1);	// remove boolean from stack
    if (lua_getglobal(L, "AutoWhistle") == LUA_TBOOLEAN) {	// true or false
        bool ABW = lua_toboolean(L, -1);
        AutoWhistleGlobal = ABW;
        } else {
        // if an operations window is open, use its setting
        if (ptrOp[iConsistNo]) {
            AutoWhistleGlobal = Setup->AutoWhistle->Checked &&  ptrOp[iConsistNo]->AutoWhistleLED->Checked;
            } else {
            // lastly use the program setting from Setup
            AutoWhistleGlobal = Setup->AutoWhistle->Checked;
            }
        }
    lua_pop(L,1);	// remove boolean from stack
    } while (false);
AutoBell[iConsistNo] = AutoBellGlobal;
AutoWhistle[iConsistNo] = AutoWhistleGlobal;

bool bretv;
if (when == 0.0) when++; // always wait a second before starting to make it slightly in the future
// schedule set of startup commands
if (iConsistNo <= ALLENGINE) {
	bretv = ptrDL_Thread->Send_String_DL_FIFO(when * 1000, iConsistNo, COMMAND_MODE, iTIUNo, MyRemoteNo, 11, SHORT_WAIT, RECORD_OK, ptrLUEngineList[iConsistNo]);	// Shortened Form
	bretv |= ptrDL_Thread->Send_String_DL_FIFO(when * 1000, iConsistNo, COMMAND_STARTUP, iTIUNo, MyRemoteNo,    // in PC StartUp()
    	11, SHORT_WAIT, RECORD_OK, ptrLUEngineList[iConsistNo], NULL, STARTED);	// Use RTC's slected remote
	bretv |= SetDirection(ENG_FORWARD, when + 5, iConsistNo, iTIUNo);		// direction to forward
    if (AutoWhistle[iConsistNo]) bretv |= MyPlaySound(43, when + 7, iConsistNo, iTIUNo);				// double toot
    } else {
	// Send "U" command
    bretv = ptrDL_Thread->Send_String_DL_FIFO(when * 1000, iConsistNo, COMMAND_CREATE_LASHUP, iTIUNo, MyRemoteNo,
        11, VERY_LONG_WAIT, RECORD_OK, ptrLUEngineList[iConsistNo] + 1);	// Shortened Form, skip the ','
    // Send Startup Command
    bretv |= ptrDL_Thread->Send_String_DL_FIFO(when * 1000, iConsistNo, COMMAND_STARTUP, iTIUNo, MyRemoteNo,        // in PC StartUp()
    	11, SHORT_WAIT, RECORD_OK, ptrLUEngineList[iConsistNo], NULL, STARTED);	// Use RTC's slected remote
    bretv |= SetDirection(ENG_FORWARD, when + 5, iConsistNo, iTIUNo);		// direction to forward
    if (AutoWhistle[iConsistNo]) bretv |= MyPlaySound(43, when + 7, iConsistNo, iTIUNo);				// double toot
    }
if (bretv) {
    state[iConsistNo] = STARTED;
    ptrTIUCommThread->Bump(MainForm->StartedConsists);  // in PC StartUp()
    }
return bretv;
}
//---------------------------------------------------------------------------
// StartUp - run a complete LashUp startup sequence including double toot (if AutoBellWhistle was set to true in the script)
//
// Params:
// when - time in seconds from now to execute the command
//
//---------------------------------------------------------------------------
bool __fastcall TProg_Control_Thread::StartUpNOTUSED(float when, byte iLashUpNo, byte iTIUNo, char * ptrLashUpEngineList) {   //time delay in seconds
assert(false);		// not used
bool bretv = false;
/*
// load configuration variables
do { 	//  global AutoBellWhistle in the script overrides program settings
    if (lua_getglobal(L, "AutoBell") == LUA_TBOOLEAN) {	// true or false
        bool ABW = lua_toboolean(L, -1);
        AutoBellGlobal = ABW;
        } else {
        // if an operations window is open, use its setting
        if (ptrOp[iLashUpNo]) {
            AutoBellGlobal = Setup->AutoBell->Checked &&  ptrOp[iLashUpNo]->AutoBellLED->Checked;
            } else {
            // lastly use the program setting from Setup
            AutoBellGlobal = Setup->AutoBell->Checked;
            }
        }
    lua_pop(L,1);	// remove boolean
    } while (false);
do { 	//  global AutoBellWhistle in the script overrides program settings
    if (lua_getglobal(L, "AutoWhistle") == LUA_TBOOLEAN) {	// true or false
        bool ABW = lua_toboolean(L, -1);
        AutoWhistleGlobal = ABW;
        } else {
        // if an operations window is open, use its setting
        if (ptrOp[iLashUpNo]) {
            AutoWhistleGlobal = Setup->AutoWhistle->Checked &&  ptrOp[iLashUpNo]->AutoWhistleLED->Checked;
            } else {
            // lastly use the program setting from Setup
            AutoWhistleGlobal = Setup->AutoWhistle->Checked;
            }
        }
    lua_pop(L,1);	// remove boolean
    } while (false);

AutoBell[iLashUpNo] = AutoBellGlobal;
AutoWhistle[iLashUpNo] = AutoWhistleGlobal;
if (when == 0.0) when++; // always wait a second before starting to make it slightly in the future
// schedule set of startup commands
// Send "U" command
bretv = ptrDL_Thread->Send_String_DL_FIFO(when * 1000, iLashUpNo, COMMAND_CREATE_LASHUP, iTIUNo, MyRemoteNo,
	11, SHORT_WAIT, RECORD_OK, ptrLashUpEngineList + 1);	// Shortened Form, skip the ','
// Send Startup Command
bretv |= ptrDL_Thread->Send_String_DL_FIFO(when * 1000, iLashUpNo, COMMAND_STARTUP, // in StartUpNOTUSED()
	iTIUNo, MyRemoteNo, 11, SHORT_WAIT, RECORD_OK, ptrLashUpEngineList, &state[iLashUpNo], STARTED);	// Use RTC's slected remote
state[iLashUpNo] = STARTED;
bretv |= SetDirection(FORWARD, when + 15, iLashUpNo, iTIUNo);		// direction to forward
if (AutoWhistle[iLashUpNo]) bretv |= MyPlaySound(43, when + 17, iLashUpNo, iTIUNo);				// double toot
*/
return bretv;
}
//---------------------------------------------------------------------------
// ShutDown - run a complete shutdown sequence for an Engine or a LashUp
//
// Params:
// when - time in seconds from now to execute the command
//
//---------------------------------------------------------------------------
bool __fastcall TProg_Control_Thread::ShutDown(float when, byte iEngineNo, byte iTIUNo) {   //time delay in seconds
// schedule set of shutdown commands
bool bretv = ptrDL_Thread->Send_String_DL_FIFO(when * 1000, iEngineNo, COMMAND_SHUTDOWN,    // in PC Thread ShutDown
	iTIUNo, MyRemoteNo, 11, SHORT_WAIT, RECORD_OK, ptrLUEngineList[iEngineNo], NULL, SHUTDOWN);	// Use RTC's slected remote
if (bretv) {
    state[iEngineNo] = SHUTDOWN;
    ptrTIUCommThread->Dec(MainForm->StartedConsists);  // in PC ShutDown()
    }
return bretv;
}
//---------------------------------------------------------------------------
// Schedule any command string
//
// Params:
// Command - Fully formed ASCII character string
// when - time in seconds from now to execute the command
// iEngineNo - engine number
// iTIUNo - TIU number
// *Flag - pointer to int for FlagValue
// FlagValue - value to store into *(int*)Flag when command is complete
//
// Always waits for LONG_WAIT time for response
//
//---------------------------------------------------------------------------
bool __fastcall TProg_Control_Thread::Schedule(char * sCommand, float when, byte iEngineNo, byte iTIUNo, int * ipFlag, int FlagValue) {   // command string, time delay in seconds
assert(sCommand != NULL);
assert(strlen(sCommand) <= (CMDMAX - 1)); // invalid - can't process
// schedule a command
bool bretv = ptrDL_Thread->Send_String_DL_FIFO(when * 1000, iEngineNo, sCommand,
	iTIUNo, MyRemoteNo, 11, SHORT_WAIT, RECORD_OK, ptrLUEngineList[iEngineNo], ipFlag, FlagValue);	// Use RTC's slected remote
return bretv;
}
//---------------------------------------------------------------------------
// Set volume for one channel
//
// Chan  - 0-4 (see .h file)
// Level - Volume # 0-100 in decimal
// when - time in seconds from now to execute the command
//
// returns true on success
//
//---------------------------------------------------------------------------
bool __fastcall TProg_Control_Thread::SetVolume(byte Chan, byte Level, float when, byte iConsistNo, byte iTIUNo) {
// Send Volume command
bool bretv = true;
if (Level > 100) Level = 50; // invalid - set to mid range
if (ptrOp[iConsistNo] && ptrVolume[iConsistNo]) {	// if we have an Operations window and Volume Window use it
    switch(Chan) {
        case 0:
            ptrDL_Thread->Send_Delayed_DL_FIFO(ptrOp[iConsistNo], ptrOp[iConsistNo]->Handle, DC_MASTER_VOLUME, iConsistNo, iTIUNo, MyRemoteNo, when * 1000, Level);
        	break;
        case 1:
            ptrDL_Thread->Send_Delayed_DL_FIFO(ptrOp[iConsistNo], ptrOp[iConsistNo]->Handle, DC_ENGINE_VOLUME, iConsistNo, iTIUNo, MyRemoteNo, when * 1000, Level);
        	break;
        case 2:
            ptrDL_Thread->Send_Delayed_DL_FIFO(ptrOp[iConsistNo], ptrOp[iConsistNo]->Handle, DC_ACCENT_VOLUME, iConsistNo, iTIUNo, MyRemoteNo, when * 1000, Level);
        	break;
        case 3:
            ptrDL_Thread->Send_Delayed_DL_FIFO(ptrOp[iConsistNo], ptrOp[iConsistNo]->Handle, DC_HORN_WHISTLE_VOLUME, iConsistNo, iTIUNo, MyRemoteNo, when * 1000, Level);
        	break;
        case 4:
            ptrDL_Thread->Send_Delayed_DL_FIFO(ptrOp[iConsistNo], ptrOp[iConsistNo]->Handle, DC_BELL_VOLUME, iConsistNo, iTIUNo, MyRemoteNo, when * 1000, Level);
        	break;
    	}
	} else {
    // otherwise, just create and send the command
	char command[CMDMAX];
	snprintf(command, sizeof command, VOLUME_CHANGE, Chan, Level);
	// schedule a volume command
	bretv = ptrDL_Thread->Send_String_DL_FIFO(when * 1000, iConsistNo, command, iTIUNo, MyRemoteNo, 11, SHORT_WAIT, RECORD_OK, ptrLUEngineList[iConsistNo]);	// Shortened Form
    }
return bretv;
}
//---------------------------------------------------------------------------
// Setting
//
// Params:
// iSet - Setting to change
// when - time in seconds from now to execute the command
//
// returns true on success
//
//---------------------------------------------------------------------------
bool __fastcall TProg_Control_Thread::Setting(int iSet, float when, byte iConsistNo, byte iTIUNo) { // Operand a number for "ab" command
// set setting "abXX" command
if ((iSet < 0x00) || (iSet > 0xFF)) return false; // invalid - ignore it
char command[CMDMAX];
snprintf(command, sizeof command, SETCMD, iSet);
// schedule a set switch command
bool bretv = ptrDL_Thread->Send_String_DL_FIFO(when * 1000, iConsistNo, command, iTIUNo, MyRemoteNo, 11, SHORT_WAIT, RECORD_OK, ptrLUEngineList[iConsistNo]);	// Shortened Form
return bretv;
}
//---------------------------------------------------------------------------
// Play a Sound
//
// Params:
// iSoundNo - the sound number
// when - time in seconds from now to execute the command
//
// returns true on success
//
//---------------------------------------------------------------------------
bool __fastcall TProg_Control_Thread::MyPlaySound	(int iSoundNo, float when, byte iConsistNo, byte iTIUNo) {  // Operand Sound # 1-255 in decimal
if ((iSoundNo < 1) || (iSoundNo > 255)) iSoundNo = 41; // invalid - toot
// play sound
char command[CMDMAX];
snprintf(command, sizeof command, PLAY_SOUND, iSoundNo);
// schedule a sound command
bool bretv = ptrDL_Thread->Send_String_DL_FIFO(when * 1000, iConsistNo, command, iTIUNo, MyRemoteNo, 11, SHORT_WAIT, RECORD_OK, ptrLUEngineList[iConsistNo]);	// Shortened Form
return bretv;
}
//---------------------------------------------------------------------------
// Set Engine Speed to a value, playing all sounds as necessary (depending on setting of AutoBell and AutoWhistle)
//
// Params:
// iMPH - speed in scale miles per hour
// when - time in seconds from now to execute the command
//
// returns true on success
//
//---------------------------------------------------------------------------
bool __fastcall TProg_Control_Thread::SetSpeed(int iMPH, float when, byte iConsistNo, byte iTIUNo) {  // Operand speed # 0-120 in decimal
bool bretv = true;
if ((iMPH < 0) || (iMPH > SPEEDMAX)) iMPH = 0; // invalid - stop the engine
//
if (ptrOp[iConsistNo]) { 	// if we have an Operations window, use it
    ptrDL_Thread->Send_Delayed_DL_FIFO(ptrOp[iConsistNo], ptrOp[iConsistNo]->Handle, DC_SETSPEED, iConsistNo, iTIUNo, MyRemoteNo, when * 1000, iMPH);
	LastSentSpeedValue[iConsistNo] = iMPH;
    } else {
	if (iMPH != 0) {
	  if (MotionSoundRequest[iConsistNo]) {
    	if (AutoWhistle[iConsistNo]) {
	      // send either forward (SFS) or reverse (SRS) horn/whistle -
    	  // use predicted future direction
	      if (predictedDirection[iConsistNo] == ENG_FORWARD) {
    	    // Send SFS
        	MyPlaySound(43, when, iConsistNo, iTIUNo);
	        } else {
    	    // Send SRS
        	MyPlaySound(44, when, iConsistNo, iTIUNo);
	        }
	      }
        if (AutoBell[iConsistNo]) {
    	  Bell(ON, when + 2, iConsistNo, iTIUNo); // send bell on in 2 seconds
          }
	    }
      // set speed
      Throttle(iMPH, when + 4, iConsistNo, iTIUNo);
      //
      if (MotionSoundRequest[iConsistNo]) {
        if (AutoBell[iConsistNo]) {
          Bell(OFF, when + 9, iConsistNo, iTIUNo); // send bell off in 9 seconds
          }
        }
      MotionSoundRequest[iConsistNo] = false; // no sound on next train movement
      }
    //
    // if the speed is set to zero, request sounds on next move off zero
    if (iMPH == 0) {
	  if (AutoWhistle[iConsistNo]) { // single toot - stop
        MyPlaySound(41, when, iConsistNo, iTIUNo);
        }
      // if iMPH == 0, send speed zero command
      Throttle(0, when, iConsistNo, iTIUNo);
      MotionSoundRequest[iConsistNo] = true;  // request sound on next train movement
      }
	}
return bretv;
}
//---------------------------------------------------------------------------
// Proto Whistle
//
// Params:
// bState - ON or OFF
// when - time in seconds from now to execute the command
//
//---------------------------------------------------------------------------
bool __fastcall TProg_Control_Thread::ProtoWhistle(bool bState, float when, byte iEngineNo, byte iTIUNo) {   // bState : ON = on, OFF = off, time delay in seconds
bool bretv = true;
// schedule a beacon command
if (ptrOp[iEngineNo]) {	// if we have an Operations window, use it
	if (bState == ON) {
        ptrDL_Thread->Send_Delayed_DL_FIFO(ptrOp[iEngineNo], ptrOp[iEngineNo]->Handle, DC_PROTO_WHISTLE_ON, iEngineNo, iTIUNo, MyRemoteNo, when * 1000, 0);
		} else {
        ptrDL_Thread->Send_Delayed_DL_FIFO(ptrOp[iEngineNo], ptrOp[iEngineNo]->Handle, DC_PROTO_WHISTLE_OFF, iEngineNo, iTIUNo, MyRemoteNo, when * 1000, 0);
	    }
    } else {
	bretv = ptrDL_Thread->Send_String_DL_FIFO(when * 1000, iEngineNo, bState == ON ? CMD_PROTOWHISTLE_ON : CMD_PROTOWHISTLE_OFF,
		iTIUNo, MyRemoteNo, 11, SHORT_WAIT, RECORD_OK, ptrLUEngineList[iEngineNo]);	// Use RTC's slected remote
	}
return bretv;
}
//---------------------------------------------------------------------------
// Set Quilling Whistle to a value
//
// Params:
// iTone - tone 0 to 3
// when - time in seconds from now to execute the command
//
// returns true on success
//
//---------------------------------------------------------------------------
bool __fastcall TProg_Control_Thread::SetQuill(int iTone, float when, byte iConsistNo, byte iTIUNo) {  // Operand tone # 0-3 in decimal
bool bretv = true;
if ((iTone < 0) || (iTone > 3)) iTone = 0; // invalid argument
//
if (ptrOp[iConsistNo]) { 	// if we have an Operations window, use it
    ptrDL_Thread->Send_Delayed_DL_FIFO(ptrOp[iConsistNo], ptrOp[iConsistNo]->Handle, DC_PROTO_WHISTLE, iConsistNo, iTIUNo, MyRemoteNo, when * 1000, iTone);
    } else {
	// build a command and send it
	char command[CMDMAX];
	snprintf(command, sizeof command, SETCMD, iTone + 0x26);
	bretv = ptrDL_Thread->Send_String_DL_FIFO(when * 1000, iConsistNo, command,
		iTIUNo, MyRemoteNo, 11, SHORT_WAIT, RECORD_OK, ptrLUEngineList[iConsistNo], NULL, NULL);	// Use RTC's slected remote
    }
return bretv;
}
//---------------------------------------------------------------------------
// Smoke Whistle
//
// Params:
// bState - ON or OFF
// when - time in seconds from now to execute the command
//
//---------------------------------------------------------------------------
bool __fastcall TProg_Control_Thread::SmokeWhistle(bool bState, float when, byte iEngineNo, byte iTIUNo) {   // bState : ON = on, OFF = off, time delay in seconds
bool bretv = true;
// schedule a beacon command
if (ptrOp[iEngineNo]) {	// if we have an Operations window, use it
	if (bState == ON) {
    	ptrDL_Thread->Send_Delayed_DL_FIFO(ptrOp[iEngineNo], ptrOp[iEngineNo]->Handle, DC_SMOKE_WHISTLE_ON, iEngineNo, iTIUNo, MyRemoteNo, when * 1000, 0);
		} else {
    	ptrDL_Thread->Send_Delayed_DL_FIFO(ptrOp[iEngineNo], ptrOp[iEngineNo]->Handle, DC_SMOKE_WHISTLE_OFF, iEngineNo, iTIUNo, MyRemoteNo, when * 1000, 0);
	    }
    } else {
	bretv = ptrDL_Thread->Send_String_DL_FIFO(when * 1000, iEngineNo, bState == ON ? CMD_SMOKEWHISTLE_ON : CMD_SMOKEWHISTLE_OFF,
		iTIUNo, MyRemoteNo, 11, SHORT_WAIT, RECORD_OK, ptrLUEngineList[iEngineNo]);	// Use RTC's slected remote
	}
return bretv;
}
//---------------------------------------------------------------------------
// Swinging bell
//
// Params:
// bState - ON or OFF
// when - time in seconds from now to execute the command
//
//---------------------------------------------------------------------------
bool __fastcall TProg_Control_Thread::SwingingBell(bool bState, float when, byte iEngineNo, byte iTIUNo) {   // bState : ON = on, OFF = off, time delay in seconds
bool bretv = true;
// schedule a beacon command
if (ptrOp[iEngineNo]) {	// if we have an Operations window, use it
	if (bState == ON) {
    	ptrDL_Thread->Send_Delayed_DL_FIFO(ptrOp[iEngineNo], ptrOp[iEngineNo]->Handle, DC_SWINGING_BELL_ON, iEngineNo, iTIUNo, MyRemoteNo, when * 1000, 0);
		} else {
    	ptrDL_Thread->Send_Delayed_DL_FIFO(ptrOp[iEngineNo], ptrOp[iEngineNo]->Handle, DC_SWINGING_BELL_OFF, iEngineNo, iTIUNo, MyRemoteNo, when * 1000, 0);
	    }
    } else {
	bretv = ptrDL_Thread->Send_String_DL_FIFO(when * 1000, iEngineNo, bState == ON ? CMD_SWINGINGBELL_ON : CMD_SWINGINGBELL_OFF,
		iTIUNo, MyRemoteNo, 11, SHORT_WAIT, RECORD_OK, ptrLUEngineList[iEngineNo], NULL, bState);	// Use RTC's slected remote
	}
if (bretv) beacon[iEngineNo] = bState;
return bretv;
}
//---------------------------------------------------------------------------

bool __fastcall TProg_Control_Thread::DisplayMessage(String Line, TColor Color) {
//
// Display a message in Debug Messages and in the Message List
//
// returns true on success
//
if (DEBUGGING) {
	ptrTIUCommThread->PrintItem(Line, Color);		            		// Message to Debug Messages
    }
//
ShowinBuffer(ptrPC->PCMessages, Line, Color);		// Display in Message List
return true;
}
//---------------------------------------------------------------------------
int __fastcall TProg_Control_Thread::StopRequest()
{
//
// Runs in the context of the caller (Prog_Control.cpp)
//
StopFlag = true;
SetEvent(ptrPC->TerminationPCEvent);
return true;
}
//---------------------------------------------------------------------------
int __fastcall TProg_Control_Thread::CleanupRequest()
{
//
// Runs in the context of the caller (Prog_Control.cpp)
//
StopFlag = true;
if (!bEmergencyStop) SetEvent(ptrPC->CleanupPCEvent);
return true;
}
//---------------------------------------------------------------------------
//
// Run a Lua Function
//
bool __fastcall TProg_Control_Thread::luaH_CallFunction(char * FuncName, __int64 i0, __int64 i1, __int64 i2, __int64 i3, __int64 i4)
{
//
// load the function from global
lua_getglobal(L,FuncName);		// get the function and push it onto the stack
if(lua_isfunction(L, -1)) {		// is the function present in the script?
    // push four arguments onto the stack
    lua_pushinteger(L, i0);
    lua_pushinteger(L, i1);
    lua_pushinteger(L, i2);
    lua_pushinteger(L, i3);
    lua_pushinteger(L, i4);
    // run the function inside of a try{} block
	bool bVal;
    try {	// in luaH_CallFunction, run the given FuncName() function
		int retv = lua_pcall(L, 5, 1, 0);	// Call the function - 5 integer parameters, 1 boolean result
        // lua_pcall() clears function and parameters from stack
		if (retv != LUA_OK) {	// check the return value
            //LUA_YIELD		1	//LUA_ERRRUN	2
            //LUA_ERRSYNTAX	3   //LUA_ERRMEM	4
            //LUA_ERRGCMM	5   //LUA_ERRERR	6
            throw "Error " + String(retv) + " running function " + String(FuncName) + "() : " + String(lua_tostring(L, -1));
            }
        if (!lua_isnil(L, -1)) {	// is the result nil?
	        bVal = lua_toboolean(L, -1);	// if not then eval the boolean result
		    lua_pop(L,1);	// remove boolean from stack
            // success, continue
            } else {
            // chunk failed and returned nil
            ShowinBuffer(ptrPC->PCMessages, "Failed - " + String(FuncName) + "()", clRed);
            bVal = false;
            }
		}
	catch(EMySTOP &E) {
		if (DEBUGGING) ShowinBuffer(ptrPC->PCMessages, E.Message + " call terminated by [STOP] in CallFunction(" + String(FuncName) + ")", (TColor) clRed);		// Display in Message List
		bVal = false;
		}
    catch(...) {
        // rethrow everything else
		throw;
		}

    if (DEBUGGINGX(9)) ptrTIUCommThread->PrintItem(String(FuncName) + " Result = " + String(bVal? "true" : "false"), clBlue);
    if (!bVal) {
        // function terminated if the chunk returned false
        if (DEBUGGING) ShowinBuffer(ptrPC->PCMessages, String(FuncName) + "() returned false", clRed);
        return false;
        }
    } else { 	// not a function, don't try to call it
    lua_pop(L, 1);	// pop result of getglobal
    return false;
    }
return true;	// success
}
//---------------------------------------------------------------------------

bool __fastcall TProg_Control_Thread::MyWaitForInputLine(lua_State *L, float WaitTime)
{
//
// Wait number of seconds given by WaitTime. WaitTime can be eg: 1, 1.0, 0.00001, etc
// Process events : MyTimer(0), MySleepTimer(1), input(2), termination(3), Cleanup(4)
//
bool bretv = false;	// true = input ready , false = MySleepTimer timed out, termination request or error
//-----------------------------------------------------------
// https://docs.microsoft.com/en-us/windows/desktop/sync/using-waitable-timer-objects
LARGE_INTEGER liDueTimeforSleep;
// WaitTime seconds (in 100 nanosecond intervals) for MySleepTimer
liDueTimeforSleep.QuadPart = -10000000L * WaitTime;   // Negative values indicate relative time.

HANDLE handles[] = {MyTimer, MySleepTimer,  ptrPC->InputPCEvent, ptrPC->TerminationPCEvent, ptrPC->CleanupPCEvent};
// REMEMBER TO SET CORRECT COUNT in call to WaitForMultipleObjects()
// MyTimer continues to run set to 100 milliseconds
DWORD dwretv;
// start the waitable MySleepTimer for WaitTime 100 nanoseconds and continue on if it starts up OK.
bool bMySleepTimer = SetWaitableTimer(MySleepTimer, &liDueTimeforSleep, 0, NULL, NULL, false);
// MyTimer (1/10 second Run Time Timer) is already running
if (bMySleepTimer) {
    // Timer is running OK
    while(!Terminated) {
        //
        dwretv = WaitForMultipleObjects(5, handles, FALSE, INFINITE);
        if (dwretv == WAIT_FAILED) {	// unknown failure?
            // Retrieve the system error message for the last-error code
            LPVOID lpMsgBuf;
            DWORD dw = GetLastError();
            FormatMessage(
                FORMAT_MESSAGE_ALLOCATE_BUFFER |
                FORMAT_MESSAGE_FROM_SYSTEM |
                FORMAT_MESSAGE_IGNORE_INSERTS,
                NULL,
                dw,
                MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
                (LPTSTR) &lpMsgBuf,
                0, NULL );
            // Display the error message
            if (DEBUGGINGX(3)) ptrTIUCommThread->PrintItem("MyWaitForInputLine(" + String(MyPCNo) + "): WaitForMultipleObjects() Failed - " + (LPCTSTR)lpMsgBuf);
            LocalFree(lpMsgBuf);
            Terminate();
            bretv = false;
            break;	// time to terminate
            }
        //--MyWaitForInputLine----------------------  Input PC Event (2)-----------------------------------
        // User has typed input
        if (dwretv == WAIT_OBJECT_0 + 2) {						// InputPCEvent Event Signaled?
            if (DEBUGGINGX(3)) ptrTIUCommThread->PrintItem("MyWaitForInputLine(" + String(MyPCNo) + "): InputPCEvent");
            // Input is ready, signal success
            bretv = true;
            break;
            }
        //--MyWaitForInputLine----------------------  Termination PC Event (3)-----------------------------------
        // Terminate on TerminationPCEvent
        if (dwretv == WAIT_OBJECT_0 + 3) {						// TerminationPCEvent Event Signaled?
            if (DEBUGGINGX(3)) ptrTIUCommThread->PrintItem("MyWaitForInputLine(" + String(MyPCNo) + "): TerminationPCEvent");
            SetEvent(ptrPC->TerminationPCEvent);	// bump termination event to main loop
            bretv = false;
            break;	// time to terminate
            }
        if (Terminated) {
            bretv = false;
            break;
            }
        //--MyWaitForInputLine----------------------  MyTimer Event (0)-----------------------------------
        if (dwretv == WAIT_OBJECT_0 + 0) {						// MyTimer Event Signaled ?

            //-----------------------------------------------------------
            TenthSecondCounter++;
	        if (TenthSecondCounter == 10) {
                TenthSecondCounter = 0;
                //-----------------------------------------------------------

                //-----------------------------------------------------------
                if (CallTickFunction) {
                    if (DEBUGGINGX(3)) {
                        PrintLine = Format((String)"PC%d MyWaitForInputLine - Run Time %.2f seconds", ARRAYOFCONST((MyPCNo, PC_Tenths/10.0)));
						ptrTIUCommThread->PrintItem(PrintLine, clBlack);
                        }
                    if (L != NULL) {
                        luaL_checkstack(L, 4, NULL);	// check for enough stack space
                        lua_getglobal(L,"tick");		// get the function and push it onto the stack
                        if(lua_isfunction(L, -1)) {		// is the tick() function present in the script?
                            // push three arguments onto the stack
                            lua_pushinteger(L, EventData.EngineNo);
                            lua_pushinteger(L, EventData.TIUNo);
                            // tick() can not call Sleep() so it will never throw an error
                            int retv = lua_pcall(L, 2, 1, 0);	// Call the function - 2 integer parameters and 1 boolean/nil result
                            if (retv  != LUA_OK) {	// check the return value
                                //LUA_YIELD		1	// LUA_ERRRUN	2
								//LUA_ERRSYNTAX	3	// LUA_ERRMEM	4
								//LUA_ERRGCMM	5	// LUA_ERRERR	6
                                ShowinBuffer(ptrPC->PCMessages, "Error " + String(retv) + " running function tick() : " + String(lua_tostring(L, -1)), (TColor) clRed);		// Display in Message List
								CallTickFunction = false;
                                }
                            bool bVal;
                            if (!lua_isnil(L, -1)) {	// turned a boolean
                                bVal = lua_toboolean(L, -1);
                                lua_pop(L,1);	// pop returned value from tick()
                                if (bVal == false) CallTickFunction = false;	// stop calling tick()
                                // success, tick()
                                } else {
                                // returned nil indicating some kind of failure
                                CallTickFunction = false;
                                ShowinBuffer(ptrPC->PCMessages, "Failed - tick()", clRed);
                                break;
                                }
                            } else {
                            // not a function, don't attempt to call it
							CallTickFunction = false;
                            lua_pop(L, 1);	// pop result of getglobal
                            }
                        }
                    }
                }
            continue;
            }
        //--MyWaitForInputLine----------------------  MySleepTimer Event (1)-----------------------------------
        if (dwretv == WAIT_OBJECT_0 + 1) {						// MySleepTimer Event Signaled ?
        	// cancel the MySleepTimer
            ResetEvent(MySleepTimer);
            CancelWaitableTimer(MySleepTimer);
            //-----------------------------------------------------------
            if (DEBUGGINGX(3)) {
                ptrTIUCommThread->PrintItem("MyWaitForInputLine(" + String(MyPCNo) + "): Input Request Timed Out");
                ShowinBuffer(ptrPC->PCMessages, "Input Request Timed Out", (TColor) clNavy);		// Display in Message List
                }
            bretv = false; 		// My Sleep Timer timed out, return failure
            break;
            }
        //--MyWaitForInputLine----------------------  Cleanup PC Event (4)-----------------------------------
        // Cleanup and Exit
        if (dwretv == WAIT_OBJECT_0 + 4) {						// CleanupPCEvent Event Signaled?
            if (DEBUGGINGX(3)) ptrTIUCommThread->PrintItem("MyWaitForInputLine(" + String(MyPCNo) + "): CleanupPCEvent");
            // https://docs.microsoft.com/en-us/windows/win32/sync/event-objects
            /*
			 * Auto-reset event
             *
			 * An event object whose state remains signaled until a single waiting thread is released,
			 * at which time the system automatically sets the state to nonsignaled. If no threads are
			 * waiting, the event object's state remains signaled. If more than one thread is waiting,
			 * a waiting thread is selected. Do not assume a first-in, first-out (FIFO) order. External
			 * events such as kernel-mode APCs can change the wait order
             */
            SetEvent(ptrPC->CleanupPCEvent);	// bump cleanup event to main loop
			// throw an exception as the [STOP] button was pressed during MyWaitForInputLine() call
			throw EMySTOP("MyWaitForInputLine()");
            }
        }
    }
return bretv;
}
//---------------------------------------------------------------------------
bool __fastcall TProg_Control_Thread::PCSleepinSeconds(lua_State *L, float DelayTime)
{
//
// Delay number of seconds given by DelayTime. DelayTime can be eg: 1, 1.0, 0.00001, etc
// Process events : MyTimer(0), MySleepTimer(1), termination(2), cleanup(3), Interpreter(4), Function Button(5) and Event(6)
//
bool bretv = true;	// true = MySleepTimer timed out, false = termination request or error
//-----------------------------------------------------------
#ifdef DEBUGTIMER
//
QueryPerformanceFrequency(&StartFrequency);
QueryPerformanceCounter(&StartTime);
//
// We now have the elapsed number of ticks, along with the
// number of ticks-per-second. We use these values
// to convert to the number of elapsed microseconds.
// To guard against loss-of-precision, we convert
// to microseconds *before* dividing by ticks-per-second.
//
StartTime.QuadPart *= 1000000;
StartTime.QuadPart /= StartFrequency.QuadPart;
#endif
//-----------------------------------------------------------
// https://docs.microsoft.com/en-us/windows/desktop/sync/using-waitable-timer-objects
LARGE_INTEGER liDueTimeforSleep;
// WaitTime seconds (in 100 nanosecond intervals) for MySleepTimer
liDueTimeforSleep.QuadPart = -10000000L * DelayTime;   // Negative values indicate relative time.

HANDLE handles[] = { MyTimer, MySleepTimer, ptrPC->TerminationPCEvent, ptrPC->CleanupPCEvent, ptrPC->InterpreterPCEvent,
	ptrPC->FunctionPCEvent, ptrPC->EventPCEvent};
// REMEMBER TO SET CORRECT COUNT in call to WaitForMultipleObjects()
// MyTimer continues to run set to 100 milliseconds
DWORD dwretv;
// start the waitable MySleepTimer for DelayTime 100 nanoseconds and continue on if it starts up OK.
bool bMySleepTimer = SetWaitableTimer(MySleepTimer, &liDueTimeforSleep, 0, NULL, NULL, false);
// MyTimer (1/10 second Run Time Timer) is already running
if (bMySleepTimer) {
    // Timer is running OK
    while(!Terminated) {
        //
        dwretv = WaitForMultipleObjects(7, handles, FALSE, INFINITE);
        if (dwretv == WAIT_FAILED) {	// unknown failure?
            if (DEBUGGINGX(3)) ptrTIUCommThread->PrintItem("MySleepTimer(" + String(MyPCNo) + "): Unknown Failure");
            Terminate();
            bretv = false;
            break;		// return false - time to terminate
            }
        //--MySleep----------------------  Termination PC Event (2)-----------------------------------
        // Terminate on TerminationPCEvent
        if (dwretv == WAIT_OBJECT_0 + 2) {						// TerminationPCEvent Event Signaled?
            if (DEBUGGINGX(3)) ptrTIUCommThread->PrintItem("MySleepTimer(" + String(MyPCNo) + "): TerminationPCEvent");
            SetEvent(ptrPC->TerminationPCEvent);	// bump termination event to main loop
            bretv = false;
            break;	// return false - time to terminate
            }
        if (Terminated) {
            if (DEBUGGINGX(3)) ptrTIUCommThread->PrintItem("MySleepTimer(" + String(MyPCNo) + "):Terminated");
            bretv = false;
            break;	// return false - time to terminate
            }
        //--MySleep----------------------  Cleanup PC Event (3)-----------------------------------
        // Cleanup and Exit
        if (dwretv == WAIT_OBJECT_0 + 3) {						// CleanupPCEvent Event Signaled?
            if (DEBUGGINGX(3)) ptrTIUCommThread->PrintItem("MySleepTimer(" + String(MyPCNo) + "): CleanupPCEvent");
            // https://docs.microsoft.com/en-us/windows/win32/sync/event-objects
            /*
			 * Auto-reset event
             *
			 * An event object whose state remains signaled until a single waiting thread is released,
			 * at which time the system automatically sets the state to nonsignaled. If no threads are
			 * waiting, the event object's state remains signaled. If more than one thread is waiting,
			 * a waiting thread is selected. Do not assume a first-in, first-out (FIFO) order. External
			 * events such as kernel-mode APCs can change the wait order
             */
            SetEvent(ptrPC->CleanupPCEvent);	// bump cleanup event to main loop
			// throw an exception as the [STOP] button was pressed during Sleep() call
			throw EMySTOP("Sleep()");
            }
        //--MySleep----------------------  MyTimer Event (0)-----------------------------------
        if (dwretv == WAIT_OBJECT_0 + 0) {						// MyTimer Event Signaled ?
            //-----------------------------------------------------------
            #ifdef DEBUGTIMER
                LARGE_INTEGER CurrentTime, Frequency;
                //
                QueryPerformanceFrequency(&Frequency);
                QueryPerformanceCounter(&CurrentTime);
                //
                // We now have the elapsed number of ticks, along with the
                // number of ticks-per-second. We use these values
                // to convert to the number of elapsed microseconds.
                // To guard against loss-of-precision, we convert
                // to microseconds *before* dividing by ticks-per-second.
                //
                CurrentTime.QuadPart *= 1000000;
                CurrentTime.QuadPart /= Frequency.QuadPart;
            #endif
            //-----------------------------------------------------------
            TenthSecondCounter++;
	        if (TenthSecondCounter == 10) {
                TenthSecondCounter = 0;
                //-----------------------------------------------------------
                #ifdef DEBUGTIMER
                if (DEBUGGING) {
                 	// print timer information in milliseconds
                    ptrTIUCommThread->PrintItem("MySleepTimer(" + String(MyPCNo) + "): Perf Counter (ms) " + String((CurrentTime.QuadPart - StartTime.QuadPart)/1000) +
                        " " + String(ptrPC->ptrTimer_Thread->RunTimeinTenths * 100.0), (TColor) clGreen);
                    }
                #endif
                //-----------------------------------------------------------
                if (CallTickFunction) {
                    if (DEBUGGINGX(8)) {
						#if 1
                        PrintLine = Format((String)"PC%d MySleepTimer - Run Time %.2f seconds", ARRAYOFCONST((MyPCNo, PC_Tenths/10.0)));
                        #else
                        PrintLine = Format((String)"PC%d MySleepTimer - Run Time %.2f seconds", ARRAYOFCONST((MyPCNo, ptrPC_Tenths[ptrPC->ptrPC_Thread->MyPCNo - 1]/10.0)));
                        #endif
						ptrTIUCommThread->PrintItem(PrintLine, clBlack);
                        }
                    if (L != NULL) {
                        luaL_checkstack(L, 4, NULL);	// check for enough stack space
                        lua_getglobal(L,"tick");		// get the function and push it onto the stack
                        if(lua_isfunction(L, -1)) {		// is the tick() function present in the script?
                            // push three arguments onto the stack
                            lua_pushinteger(L, EventData.EngineNo);
                            lua_pushinteger(L, EventData.TIUNo);
                            // tick() can not call Sleep() so it will never throw an error
                            int retv = lua_pcall(L, 2, 1, 0);	// Call the function - 2 integer parameters and 1 boolean/nil result
                            if (retv  != LUA_OK) {	// check the return value
                                //LUA_YIELD		1	// LUA_ERRRUN	2
								//LUA_ERRSYNTAX	3	// LUA_ERRMEM	4
								//LUA_ERRGCMM	5	// LUA_ERRERR	6
                                ShowinBuffer(ptrPC->PCMessages, "Error " + String(retv) + " running function tick() : " + String(lua_tostring(L, -1)), (TColor) clRed);		// Display in Message List
								CallTickFunction = false;
                                }
                            bool bVal;
                            if (!lua_isnil(L, -1)) {	// turned a boolean
                                bVal = lua_toboolean(L, -1);
                                lua_pop(L,1);	// pop returned value from tick()
                                if (bVal == false) CallTickFunction = false;	// stop calling tick()
                                // success, tick()
                                } else {
                                // returned nil indicating some kind of failure
                                CallTickFunction = false;
                                ShowinBuffer(ptrPC->PCMessages, "Failed - tick()", clRed);
                                break;
                                }
                            } else {
                            // not a function, don't attempt to call it
							CallTickFunction = false;
                            lua_pop(L, 1);	// pop result of getglobal
                            }
                        }
                    }
                }
            continue;
            }
        //--MySleep----------------------  MySleepTimer Event (1)-----------------------------------
        if (dwretv == WAIT_OBJECT_0 + 1) {						// MySleepTimer Event Signaled ?
        	// cancel the MySleepTimer
            ResetEvent(MySleepTimer);
            CancelWaitableTimer(MySleepTimer);
            //-----------------------------------------------------------
            #ifdef DEBUGTIMER
                LARGE_INTEGER CurrentTime, Frequency;
                //
                QueryPerformanceFrequency(&Frequency);
                QueryPerformanceCounter(&CurrentTime);
                //
                // We now have the elapsed number of ticks, along with the
                // number of ticks-per-second. We use these values
                // to convert to the number of elapsed microseconds.
                // To guard against loss-of-precision, we convert
                // to microseconds *before* dividing by ticks-per-second.
                //
                CurrentTime.QuadPart *= 1000000;
                CurrentTime.QuadPart /= Frequency.QuadPart;
            #endif
            //-----------------------------------------------------------
            //-----------------------------------------------------------
            #ifdef DEBUGTIMER
            if (DEBUGGING) {
            	// print timer information in milliseconds
            	ptrTIUCommThread->PrintItem("MySleepTimer(" + String(MyPCNo) + "): Perf Counter (ms) " + String((CurrentTime.QuadPart - StartTime.QuadPart)/1000) +
                	" " + String(ptrPC->ptrTimer_Thread->RunTimeinTenths * 100.0), (TColor) clGreen);
                }
            #endif
            //-----------------------------------------------------------
            if (DEBUGGINGX(8)) ShowinBuffer(ptrPC->PCMessages, "Sleep End", (TColor) clNavy);		// Display in Message List
            bretv = true; 		// My Sleep Timer timed out, return success
            break;
            }
        //--MySleep--------------------  Interpreter PC Event (4)----------------------------------
        if (dwretv == WAIT_OBJECT_0 + 4) {						// InterpreterPCEvent Event Signaled ?
            if (DEBUGGINGX(6)) ptrTIUCommThread->PrintItem("MySleepTimer(" + String(MyPCNo) + "): InterpreterPCEvent");
        	doInterpreter();
            continue;
            }
		//--MySleep----------------------  Function PC Event (5)-----------------------------------
        if (dwretv == WAIT_OBJECT_0 + 5 && CompileSuccess) {	// FunctionPCEvent Event Signaled ?
            char func[20];
            (void) snprintf(func, sizeof func - 1, "Function%02dName", EventData.EventNo);
            if (DEBUGGINGX(6)) ptrTIUCommThread->PrintItem("MySleepTimer(" + String(MyPCNo) + "): FunctionPCEvent: " + String(func));
            luaH_CallFunction(func, EventData.EngineNo, EventData.TIUNo); 	// run Functionxx()
            continue;
            }
		//--Execute----------------------  Event PC Event (6)-----------------------------------
		if (dwretv == WAIT_OBJECT_0 + 6) {						// EventPCEvent Event Signaled?
            if (DEBUGGINGX(6)) ptrTIUCommThread->PrintItem("MySleepTimer(" + String(MyPCNo) + "): EventPCEvent");
            ProcessEventEvent();
            continue;
            }            
		//--MySleep------------------------------------------------------------------------------
        }
    }
return bretv;
}
//---------------------------------------------------------------------------
bool __fastcall TProg_Control_Thread::MyYield(lua_State *L)
{
//
// Used by Lua scripts which suspend using other means than a call to Sleep(). For example,
// udp.receive() which blocks with its own timeout.
#ifdef OLDWAY
not used
Application->ProcessMessages();// can't call this function in a thread
return true;
#else
//
// Calling Yield() in such a blocking loop lets the Program Control window process events
// such as pressing the [STOP] button or a function button (see list below).
//
// Yield() never blocks but uses WaitForMultipleObjects() to check if any of the processed
// events is waiting. If not, it immediately returns.
//
// A call to Yield() executes very quickly if no events are pending. In the loop() function,
// a call to Sleep() is still required to handle the UI.
//
// Process termination(0), cleanup(1), Interpreter(2), Function Button(3) and Event(4), events.
//
bool bretv = true;	// true = Event, Interpreter or Function Button, false = termination request or error
DWORD dwretv;
HANDLE handles[] = { ptrPC->TerminationPCEvent, ptrPC->CleanupPCEvent, ptrPC->InterpreterPCEvent,
	ptrPC->FunctionPCEvent, ptrPC->EventPCEvent};
// REMEMBER TO SET CORRECT COUNT in call to WaitForMultipleObjects()
//
dwretv = WaitForMultipleObjects(5, handles, FALSE, 0);	// test the state of the objects and return immediately
if (dwretv == WAIT_FAILED) {	// unknown failure?
    if (DEBUGGINGX(3)) ptrTIUCommThread->PrintItem("Yield(" + String(MyPCNo) + "): Unkonwn Failure");
    Terminate();
    return false;
    }
//--Yield----------------------  Termination PC Event (0)-----------------------------------
// Terminate on TerminationPCEvent
if (dwretv == WAIT_OBJECT_0 + 0) {						// TerminationPCEvent Event Signaled?
    if (DEBUGGINGX(3)) ptrTIUCommThread->PrintItem("Yield(" + String(MyPCNo) + "): TerminationPCEvent");
    SetEvent(ptrPC->TerminationPCEvent);	// bump termination event to main loop
    return false;
    }
if (Terminated) {
    if (DEBUGGINGX(3)) ptrTIUCommThread->PrintItem("Yield(" + String(MyPCNo) + "): Terminated");
    return false;
    }
//--Yield----------------------  Cleanup PC Event (1)-----------------------------------
// Cleanup and Exit
if (dwretv == WAIT_OBJECT_0 + 1) {						// CleanupPCEvent Event Signaled?
    if (DEBUGGINGX(8)) ptrTIUCommThread->PrintItem("Yield(" + String(MyPCNo) + "): CleanupPCEvent");
    // https://docs.microsoft.com/en-us/windows/win32/sync/event-objects
    /*
    * Auto-reset event
    *
    * An event object whose state remains signaled until a single waiting thread is released,
    * at which time the system automatically sets the state to nonsignaled. If no threads are
    * waiting, the event object's state remains signaled. If more than one thread is waiting,
    * a waiting thread is selected. Do not assume a first-in, first-out (FIFO) order. External
    * events such as kernel-mode APCs can change the wait order
    */
    SetEvent(ptrPC->CleanupPCEvent);	// bump cleanup event to main loop
    // throw an exception as the [STOP] button was pressed
    throw EMySTOP("Yield()");
    }
//--Yield--------------------  Interpreter PC Event (2)----------------------------------
if (dwretv == WAIT_OBJECT_0 + 2) {						// InterpreterPCEvent Event Signaled ?
    if (DEBUGGINGX(6)) ptrTIUCommThread->PrintItem("Yield(" + String(MyPCNo) + "): InterpreterPCEvent");
    doInterpreter();
    return bretv;
    }
//--Yield----------------------  Function PC Event (3)-----------------------------------
if (dwretv == WAIT_OBJECT_0 + 3 && CompileSuccess) {	// FunctionPCEvent Event Signaled ?
    char func[20];
    (void) snprintf(func, sizeof func - 1, "Function%02dName", EventData.EventNo);
    if (DEBUGGINGX(6)) ptrTIUCommThread->PrintItem("Yield(" + String(MyPCNo) + "): FunctionPCEvent: " + String(func));
    luaH_CallFunction(func, EventData.EngineNo, EventData.TIUNo); 	// run Functionxx()
    return bretv;
    }
//--Yield----------------------  Event PC Event (4)-----------------------------------
if (dwretv == WAIT_OBJECT_0 + 4) {						// EventPCEvent Event Signaled?
	if (DEBUGGINGX(6)) ptrTIUCommThread->PrintItem("Yield(" + String(MyPCNo) + "): EventPCEvent");
    ProcessEventEvent();
    return bretv;
    }
//--Yield------------------------------------------------------------------------------
return bretv;
#endif
}
//---------------------------------------------------------------------------
bool __fastcall TProg_Control_Thread::FetchByte(unsigned int addr, byte * value, int EngineNo, int TIUNo)
{
//
// Fetch one byte of RAM memory
// addr points to the byte address
// byte result in 1 byte
// Never addressed to a Lashup
// returns false on error
//
assert(addr <= 0x03FF);
assert(EngineNo <= ENGINEMAX);
*value = 0;
int iTIUCount;
byte FBResp[CMDMAX];
char CommandString[15];
snprintf(CommandString, sizeof CommandString, "V1%03X", addr);
bool bretv = ptrTIUCommThread->Send_String_FIFO(EngineNo, CommandString, TIUNo, MyRemoteNo, 21, QUERY_WAIT, NO_RECORD, NULL, NULL, &iTIUCount, FBResp);
if (!bretv) {	// retry once
	if (DEBUGGING) ptrTIUCommThread->PrintItem("FetchByte() Retrying " + String(CommandString));
	bretv = ptrTIUCommThread->Send_String_FIFO(EngineNo, CommandString, TIUNo, MyRemoteNo, 21, QUERY_WAIT, NO_RECORD, NULL, NULL, &iTIUCount, FBResp);
	if (!bretv) return false;	// return false with value == 0 if we can't read RAM
    }

if (bretv && (iTIUCount == 21 || iTIUCount == 22)) {
    if (iTIUCount == 21) {
        // Handle Radio Response
        // pull out the value         0    1    2    3
        // 965A 99A6 5555 5555 5559 5559 A596 6566 555A
        //   87   D9   00   00   04   04   E1   31   05
        //                            ^^    RAM -- returned by the TIU
        *value =  FBResp[5];
        }
    if (iTIUCount == 22) {
        // Handle WTIU ASCII Response  "qE8 00 36 0A  okay"
        //                               ^^    RAM -- returned by the TIU
        int h0;
		int retv = sscanf(&FBResp[1], "%x", &h0);   // %x requires &int
        if (retv == 1) {
            *value = h0;
            } else {
            return false;
            }
        }
    }
if (DEBUGGING) ptrTIUCommThread->PrintItem("FetchByte - address : 0x" + IntToHex((int)addr, 3) + " value : 0x" + IntToHex((int)*value, 2));
return true;
}
//---------------------------------------------------------------------------
bool __fastcall TProg_Control_Thread::FetchWord(unsigned int addr, unsigned short * value, int EngineNo, int TIUNo)
{
//
// Fetch two bytes of RAM memory
// addr points to the lowest byte address
// unsigned short result in 2 bytes
// Never addressed to a Lashup
// Returns false on error
//
assert(addr <= 0x03FF);
assert(EngineNo <= ENGINEMAX);
*value = 0;
int iTIUCount;
byte FBResp[CMDMAX];
char CommandString[15];
snprintf(CommandString, sizeof CommandString, "V2%03X", addr);
bool bretv = ptrTIUCommThread->Send_String_FIFO(EngineNo, CommandString, TIUNo, MyRemoteNo, 21, QUERY_WAIT, NO_RECORD, NULL, NULL, &iTIUCount, FBResp);
if (!bretv) {	// retry once
	if (DEBUGGING) ptrTIUCommThread->PrintItem("FetchWord() Retrying " + String(CommandString));
	bretv = ptrTIUCommThread->Send_String_FIFO(EngineNo, CommandString, TIUNo, MyRemoteNo, 21, QUERY_WAIT, NO_RECORD, NULL, NULL, &iTIUCount, FBResp);
	if (!bretv) return false;	// return false with value == 0 if we can't read RAM
    }

if (bretv && (iTIUCount == 21 || iTIUCount == 22)) {
    if (iTIUCount == 21) {
        // Handle Radio Response
        // pull out the value         0    1    2    3
        // 965A 99A6 5555 5555 5559 5559 A596 6566 555A
        //   87   D9   00   00   04   04   E1   31   05
        //                            ^^   ^^  RAM -- returned by the TIU
        *value =  FBResp[5] << 8 | FBResp[6];
        }
    if (iTIUCount == 22) {
        // Handle WTIU ASCII Response  "qE8 00 36 0A  okay"
        //                               ^^ ^^  RAM -- returned by the TIU
        int h0, h1;
		int retv = sscanf(&FBResp[1], "%x %x", &h0, &h1);   // %x requires &int
        if (retv == 2) {
            *value = h0 << 8 | h1;
            } else {
            return false;
            }
        }
    }
if (DEBUGGING) ptrTIUCommThread->PrintItem("FetchWord - address : 0x" + IntToHex((int)addr, 3) + " value : 0x" + IntToHex((int)*value, 4));
return true;
}
//---------------------------------------------------------------------------
bool __fastcall TProg_Control_Thread::FetchInt(unsigned int addr, unsigned int * value, int EngineNo, int TIUNo)
{
//
// Fetch four bytes of RAM memory
// addr points to the lowest byte address
// unsigned int result in 4 bytes
// Never addressed to a Lashup
// Returns false on error
//
assert(addr <= 0x03FF);
assert(EngineNo <= ENGINEMAX);
*value = 0;
int iTIUCount;
byte FBResp[CMDMAX];
char CommandString[15];
snprintf(CommandString, sizeof CommandString, "V4%03X", addr);
bool bretv = ptrTIUCommThread->Send_String_FIFO(EngineNo, CommandString, TIUNo, MyRemoteNo, 21, QUERY_WAIT, NO_RECORD, NULL, NULL, &iTIUCount, FBResp);
if (!bretv) {	// retry once
	if (DEBUGGING) ptrTIUCommThread->PrintItem("FetchInt() Retrying " + String(CommandString));
	bretv = ptrTIUCommThread->Send_String_FIFO(EngineNo, CommandString, TIUNo, MyRemoteNo, 21, QUERY_WAIT, NO_RECORD, NULL, NULL, &iTIUCount, FBResp);
	if (!bretv) return false;	// return false with value == 0 if we can't read RAM
    }

if (bretv && (iTIUCount == 21 || iTIUCount == 22)) {
    if (iTIUCount == 21) {
        // Handle Radio Response
        // pull out the value         0    1    2    3
        // 965A 99A6 5555 5555 5559 5559 A596 6566 555A
        //   87   D9   00   00   04   04   E1   31   05
        //                            ^^   ^^   ^^   ^^    RAM -- returned by the TIU
        *value =  FBResp[5] << 24 | FBResp[6] << 16 | FBResp[7] << 8 | FBResp[8];
        }
    if (iTIUCount == 22) {
        // Handle WTIU ASCII Response  "qE8 00 36 0A  okay"
        //                               ^^ ^^ ^^ ^^    RAM -- returned by the TIU
        int h0, h1, h2, h3;
        int retv = sscanf(&FBResp[1], "%x %x %x %x", &h0, &h1, &h2, &h3);   // %x requires &int
        if (retv == 4) {
            *value = h0 << 24 | h1 << 16 | h2 << 8 | h3;
            } else {
            return false;
            }
        }
    }
if (DEBUGGING) ptrTIUCommThread->PrintItem("FetchInt  - address : 0x" + IntToHex((int)addr, 3) + " value : 0x" + IntToHex((int)*value, 8));
return true;
}
//---------------------------------------------------------------------------
bool __fastcall TProg_Control_Thread::FetchVolume(int Chan, int *value, int EngineNo, int TIUNo)
{
//
// Fetch the Engine Sound Volume Levels
// Never addressed to a Lashup
// Returns false on error
//
assert(EngineNo <= ENGINEMAX);
assert (Chan >= MASTER_VOLUME && Chan <= BELL_VOLUME);
*value = 100;
unsigned int addr = 0;
switch(Chan) {
    case MASTER_VOLUME:
		addr = 0x0E;    // "V200E"
    	break;
    case ENGINE_VOLUME:
		addr = 0x10;    // "V2010"
    	break;
    case ACCENT_VOLUME:
		addr = 0x12;    // "V2012"
    	break;
    case HORN_VOLUME:
		addr = 0x14;    // "V2014"
    	break;
    case BELL_VOLUME:
		addr = 0x16;    // "V2016"
    	break;
	}

unsigned short iVal;
bool bretv = FetchWord(addr, &iVal, EngineNo, TIUNo);
if (!bretv) {
    *value = 0;
    return false;
    }
iVal = iVal / 327;	// divide by 327

*value = iVal;
return true;
}
//---------------------------------------------------------------------------
bool __fastcall TProg_Control_Thread::FetchSpeed(unsigned int *value, int EngineNo, int TIUNo)
{
//
// Fetch the Engine Set Speed
// Never addressed to a Lashup
// Returns false on error
//
assert(EngineNo <= ENGINEMAX);

*value = 0;
// fetch RAM 0x62 and 0x63  "V2062"

unsigned short iVal;
bool bretv = FetchWord(0x62, &iVal, EngineNo, TIUNo);    // Engine Speed * 8 (goal not instantaneous)-- returned by the TIU
if (!bretv) {
    *value = 0;
    return false;
    }

iVal = iVal >> 3;	// divide by 8

*value = iVal;
return true;
}
//---------------------------------------------------------------------------
bool __fastcall TProg_Control_Thread::FetchInstSpeed(unsigned int * value, int EngineNo, int TIUNo)
{
//
// Fetch the Instantaneous Engine Speed
//
// Different between PS2 and PS3 Engines
// Never addressed to a Lashup
// Returns false on error
//
assert(EngineNo <= ENGINEMAX);
*value = 0;
bool bretv;

if (PSType[EngineNo] == UDEF0) {
	byte iLevel;
    bool bretv = FetchByte(0x50, &iLevel, EngineNo, TIUNo);
    if (!bretv) {
        *value = 0;
        return false;
        }
	if (iLevel == 0x00)
		PSType[EngineNo] = PS3;
    	else
		PSType[EngineNo] = PS2;
 	}
switch (PSType[EngineNo]) {
    default:
	case UDEF0:
        return false;	// return false with value == 0 if we can't read RAM to determine the PS2 or PS3 level

    case PS2:
        // fetch RAM 0x60 and 0x61 using FetchWord()  "V2060"

        unsigned short iVal;
        bretv = FetchWord(0x60, &iVal, EngineNo, TIUNo);    // Instantaneous Engine Speed * 8 (instantaneous, not goal)-- returned by the TIU
        if (!bretv) {
            *value = 0;
            return false;
            }
        iVal = iVal >> 3;   // divide by 8
        *value = iVal;

        if (*value <= 120) {
			if (DEBUGGINGX(8)) {
				ShowinBuffer(ptrPC->PCMessages, Format((String)"FetchInstSpeed=%d PS2", ARRAYOFCONST(((int)*value))), clBlue);
			    }
        	return true;

            } else {
            // otherwise its a PS3 engine and RTC is confused or there was a transmission error
            // retry one more time
            bretv = FetchWord(0x60, &iVal, EngineNo, TIUNo);    // Instantaneous Engine Speed * 8 (instantaneous, not goal)-- returned by the TIU
            if (!bretv) {
                *value = 0;
                return false;
                }
            iVal = iVal >> 3;   // divide by 8
            *value = iVal;
            if (*value <= 120) {
                if (DEBUGGINGX(7)) {
                    ShowinBuffer(ptrPC->PCMessages, Format((String)"FetchInstSpeed=%d PS2", ARRAYOFCONST(((int)*value))), clBlue);
                    }
                return true;
                } // otherwise, this routine will return false with invalid data
            }
        break;

    case PS3:
    	// fetch RAM 0x60 using FetchByte  "V1060"

        byte cVal;
        bretv = FetchByte(0x60, &cVal, EngineNo, TIUNo);    // Instantaneous Engine Speed (instantaneous, not goal)-- returned by the TIU
        if (!bretv) {
            *value = 0;
            return false;
            }
        *value = cVal;

		if (DEBUGGINGX(8)) {
			ShowinBuffer(ptrPC->PCMessages, Format((String)"FetchInstSpeed=%d PS3", ARRAYOFCONST(((int)*value))), clBlue);
		    }
        return true;
	}
return false;
}
//---------------------------------------------------------------------------
bool __fastcall TProg_Control_Thread::FetchDirection(bool * value, int EngineNo, int TIUNo)
{
//
// Fetch the Engine Direction
// returns in value : true on forward, false on reverse
// Never addressed to a Lashup
// Returns false on failure
//
assert(EngineNo <= ENGINEMAX);
*value = ENG_FORWARD;

// fetch RAM 0x67  "V1067"

byte bval;
bool bretv = FetchByte(0x67, &bval, EngineNo, TIUNo);
if (!bretv) {
    *value = 0;
    return false;
    }
bval = bval  & 0x01;	// Low Order Bit

*value = (bval == 0x00);
return true;
}
//---------------------------------------------------------------------------
bool __fastcall TProg_Control_Thread::FetchDTO(double * value, int EngineNo, int TIUNo)
{
//
// Fetch the Trip Odometer as a float
// Never addressed to a Lashup
//
// Returns -1.0 in "value" on failure
// Returns false on failure
//
assert(EngineNo <= ENGINEMAX);
*value = 0.0;

double fSF = ScalingFactor[EngineNo];
if (fSF == 0.0) {
	// memory locations 0x08-0x09 "V2008"
    unsigned short ival;
    bool bretv = FetchWord(0x08, &ival, EngineNo, TIUNo);
    if (!bretv) {
        *value = 0;
        return false;
        }
    fSF = ival;
    ScalingFactor[EngineNo] = fSF;		// Save the Scaling Factor
    }
// memory locations 0x3C-0x3F  "V403C"

unsigned int lVal;
bool bretv = FetchInt(0x3C, &lVal, EngineNo, TIUNo);
if (!bretv) {
    *value = 0;
    return false;
    }
if (fSF == 0.0) {		// prevent divide by zero
	*value = -1.0;
	return false;       // return false with value == -1.0 if we would divide by zero
    }
const double fDTO = DTOEQUATION(lVal, fSF);
if (DEBUGGINGX(8)) {
	const double fDTOx = lVal / (fSF * DTOFACTOR);
    if (fDTO != fDTOx) {
        ShowinBuffer(ptrPC->PCMessages, Format((String)"FetchDTO=%.6f FetchDTOx=%.6f", ARRAYOFCONST((fDTO, fDTOx))), clRed);
        }
	ShowinBuffer(ptrPC->PCMessages, Format((String)"FetchDTO=%.6f raw=%d DTOFACTOR=%.6f ScaleFactor=%f ", ARRAYOFCONST((fDTO, (int)lVal, DTOFACTOR, fSF))), clBlue);
    }
*value = fDTO;	// return long word from RAM scaled by SF and converted to a float
return true;
}
//---------------------------------------------------------------------------
bool __fastcall TProg_Control_Thread::FetchDOD(double * value, int EngineNo, int TIUNo)
{
//
// Fetch the Odometer as a float
// Never addressed to a Lashup
// Returns false on failure
//
assert(EngineNo <= ENGINEMAX);
#ifdef OLDWAY
not used
byte FactorResp[CMDMAX]; 	// Array for decoded binary response
byte DODbyteResp[CMDMAX]; 	// Array for decoded binary response
int iTIUCount;
bool bretv;
#endif
*value = 0.0;

double fSF = ScalingFactor[EngineNo];
if (fSF == 0.0) {
    // memory locations 0x08-0x09  "V2008"
    unsigned short ival;
    bool bretv = FetchWord(0x08, &ival, EngineNo, TIUNo);
    if (!bretv) {
        *value = 0;
        return false;
        }
    fSF = ival;
    ScalingFactor[EngineNo] = fSF;		// Save the Scaling Factor
    }
// memory locations 0x34-0x37  "V4034"

unsigned int lVal;
bool bretv = FetchInt(0x34, &lVal, EngineNo, TIUNo);
if (!bretv) {
    *value = 0;
    return false;
    }
if (fSF == 0.0) {		// prevent divide by zero
	*value = -1.0;
    return false;       // return false with value == -1.0 if we would divide by zero
    }
const double fDOD = DODEQUATION (lVal, fSF);	// multiplier found by experimentation
if (DEBUGGINGX(8)) {
	ShowinBuffer(ptrPC->PCMessages, Format((String)"FetchDOD=%.6f raw=%d DODFACTOR=%.6f ScaleFactor=%f ", ARRAYOFCONST((fDOD, (int)lVal, /*DODFACTOR*/ 512.0/DTOFACTOR, fSF))), clBlue);
    }
*value = fDOD;	// return long word from RAM scaled by SF and converted to a float
return true;
}
//---------------------------------------------------------------------------
bool __fastcall TProg_Control_Thread::FetchDCH(double * value, int EngineNo, int TIUNo)
{
//
// Fetch the chronometer as a integer
// Never addressed to a Lashup
// Returns -1 on failure
//
assert(EngineNo <= ENGINEMAX);
*value = 0.0;

// memory locations 0x0x38-0x8B  "V4038"

unsigned int lVal;
bool bretv = FetchInt(0x38, &lVal, EngineNo, TIUNo);
if (!bretv) {
    *value = 0;
    return false;
    }
const double fDCH = DCHEQUATION(lVal);	// DCHFACTOR is estimated ticks per second
if (DEBUGGINGX(8)) {
	const double fDCHx = lVal / DCHFACTOR;	// DCHFACTOR is estimated ticks per second
    if (fDCH != fDCHx) {
        ShowinBuffer(ptrPC->PCMessages, Format((String)"FetchDCH=%.6f FetchDCHx=%.6f", ARRAYOFCONST((fDCH, fDCHx))), clRed);
        }
    ShowinBuffer(ptrPC->PCMessages, Format((String)"FetchDCH=%.6f raw=%d DCH FACTOR=%.6f", ARRAYOFCONST((fDCH, (int)lVal, DCHFACTOR))), clBlue);
    }
*value = fDCH;	// return long word from RAM converted to a integer
return true;
}
//---------------------------------------------------------------------------
void __fastcall TProg_Control_Thread::RunOnTerminate(TObject * Sender)     // called Automatically using Synchronize()
{
// not used
}
//---------------------------------------------------------------------------
void __fastcall TProg_Control_Thread::doInterpreter(void)
{
// interpret one Lua expression (preceeded by the equal size) or statement
//
int TOS = lua_gettop(L);
MyGetCommandLine();	// fetch and clear the command line
if (DEBUGGING) ShowinBuffer(ptrPC->PCMessages, InputString, clGreen);	// echo
// AnsiString needed because .c_str() is taken and char string required
AnsiString aInputString = InputString;	// to convert to a C string
//
int load_stat, retv;
if (aInputString.c_str()[0] == '=') {
	// user typed an expression (starting with equal sign) for compatibility with 5.2, ...
	aInputString = aInputString.Delete(1,1);	// remove '=' sign
    const char * CmdStr = lua_pushfstring(L, "print (%s)", aInputString.c_str());  // surround input line with 'print()'
    load_stat = luaL_loadstring(L, CmdStr);	// print() makes it a statement - compile it
    lua_remove(L, -2);  // remove modified line (added 'print()')
    // possible compile error message OR compiled function on TOS, just below, pass the compiled function to lua_pcall()
    } else {
    // we don't know if the user typed a statement or an expression
	if ((load_stat = luaL_loadstring(L, aInputString.c_str())) != LUA_OK) {	// compile it - will be OK if it was a statement
		// Compile error message on TOS but we don't care about the message, just the fact that the line is not a statement
		lua_pop(L, 1);	// pop compile error message from the stack
		// compile errors so put a print() call around it to make it a statement and try again
        const char * CmdStr = lua_pushfstring(L, "print (%s)", aInputString.c_str());  // surround input line with 'print()'
		load_stat = luaL_loadstring(L, CmdStr);	// print() makes it a statement - compile it
	    lua_remove(L, -2);  // remove modified line (added 'print()')
		// possible compile error message OR compiled function on TOS, just below, pass the compiled function to lua_pcall()
		}
	}
//
if (load_stat) {
	ShowinBuffer(ptrPC->PCMessages, String(lua_tostring(L, -1)), clRed);
	lua_pop(L, 1);	// pop compile error message from the TOS
    assert(lua_gettop(L) == TOS);
    return; 		// to process another expression or statement
    }

// Top of Stack has the compiled function, execute it with no arguments and no results
retv = lua_pcall(L, 0, 0, 0);	// call the function, no parameters, no results

if (retv) {	// check for an error
	//LUA_YIELD		1	//LUA_ERRRUN	2
    //LUA_ERRSYNTAX	3   //LUA_ERRMEM	4
    //LUA_ERRGCMM	5   //LUA_ERRERR	6
    ShowinBuffer(ptrPC->PCMessages, String(lua_tostring(L, -1)), clRed);
    lua_pop(L, 1);	// pop run time error message from the stack
    assert(lua_gettop(L) == TOS);
    return;			// to process another expression or statement
    }
assert(lua_gettop(L) == TOS);
return;				// to process another expression or statement
}
//---------------------------------------------------------------------------

void __fastcall TProg_Control_Thread::ProcessEventEvent(void)
{
// Process an EventEvent
struct EventItem * EvItem = NULL;
while(!Terminated) {
	TList *myList = Event_FIFO[MyPCNo - 1]->LockList();
	if (myList->Count > 0) {
		EvItem = reinterpret_cast<struct EventItem *>(myList->Items[0]); // Thread Safe
		// Remove(EvItem) item pointer from Event_FIFO
        // from Help: Alternative: Delete by Index:
        // While Remove is generally preferred when you have a pointer to the item,
        // TList also has a Delete(Index: Integer) method if you need to remove an
        // item by its position. This also needs to be performed within a locked section.
        myList->Delete(0);
		Event_FIFO[MyPCNo - 1]->UnlockList();
	    // Lua Handle Windows Message
       	if (CallEventFunction) {
           	// the values of iFromPCNo, EventType and P1/P2/P3 for events from other PC Windows
            int iFromPCNo = EvItem->iFromPCNo;
            __int64 EventType = EvItem->EventType;
            __int64 iP1 = EvItem->iP1;
            __int64 iP2 = EvItem->iP2;
            __int64 iP3 = EvItem->iP3;
	        luaH_CallFunction("event", iFromPCNo, EventType, iP1, iP2, iP3);	// run event()
            if (DEBUGGINGX(8)) ShowinBuffer(ptrPC->PCMessages, "event() complete", clNavy);
            }
        // delete the item
  	    delete EvItem;	// created in luaH_SetEvent()
	    EvItem = NULL;
        // go and try to process another Event FIFO request
		} else {
		Event_FIFO[MyPCNo - 1]->UnlockList();
        break;	// All events from the Event_FIFO have been sent to the lua script
        }
	}
}
//---------------------------------------------------------------------------

void ShowinBuffer(TRichEdit * Buf, String PrintLine, TColor PrintColor)
{
// Insert Autoscroll checkbox setting and call actual routine in Comm_Thread.cpp
if (ptrPC_local == NULL) {
	ptrTIUCommThread->ShowinBuffer(Buf, PrintLine, PrintColor, true);
	} else {
	ptrTIUCommThread->ShowinBuffer(Buf, PrintLine, PrintColor, ptrPC_local->AutoScroll->Checked);
	}
}
//---------------------------------------------------------------------------

bool __fastcall TProg_Control_Thread::FactoryReset(int EngineNo, int TIUNo)
{
//
// Send a Factory Reset
// returns true on success, false on failure
// Never addressed to a Lashup
//
assert(EngineNo <= ENGINEMAX);
bool bretv = ptrDL_Thread->Send_String_DL_FIFO(0, EngineNo, "F1", TIUNo, MyRemoteNo, 11, SHORT_WAIT, RECORD_OK, NULL);
return bretv;
}
//---------------------------------------------------------------------------

bool __fastcall TProg_Control_Thread::FeatureReset(int EngineNo, int TIUNo)
{
//
// Send a Feature Reset
// returns true on success, false on failure
// Never addressed to a Lashup
//
assert(EngineNo <= ENGINEMAX);
bool bretv = ptrDL_Thread->Send_String_DL_FIFO(0, EngineNo, "F0", TIUNo, MyRemoteNo, 11, SHORT_WAIT, RECORD_OK, NULL);
return bretv;
}
//---------------------------------------------------------------------------

bool __fastcall TProg_Control_Thread::Command(char sCommand[], int EngineNo, int TIUNo, int Expected, int * iCmdCount, byte * CmdbyteResp)
{
//
// Send a command to the given EngineNo and TIUNo.
// Does not work with lashups
//
// returns true on success, false on failure
// returns the number of characters in the response in iCmdCount
// returns the response as a character string in CmdbyteResp
// Waits for I_WAIT milliseconds for response
//
bool bretv = ptrTIUCommThread->Send_String_FIFO(EngineNo, sCommand, TIUNo, MyRemoteNo, Expected, I_WAIT, RECORD_OK, NULL, NULL, iCmdCount, CmdbyteResp);
return bretv;
}
//---------------------------------------------------------------------------
bool __fastcall TProg_Control_Thread::Ping(int * p4loss, int EngineNo, int TIUNo)
{
//
*p4loss = TSMAX;  // start assuming that all packets were lost
int iPingCount;
byte PingbyteResp[CMDMAX]; 	// Array for decoded binary response

if (Setup->WTIU_WiFi_Mode->Checked) {
    // the WTIU requires a "yX" command before the "p4" command
    AnsiString aCommand = "y" + String(EngineNo + 1);
	(void)ptrTIUCommThread->Send_String_FIFO(2, aCommand.c_str(), TIUNo, MyRemoteNo, 11, LONG_WAIT, NO_RECORD);
    }

bool bretv = ptrTIUCommThread->Send_String_FIFO(EngineNo, "p4", TIUNo, MyRemoteNo, 15, LONG_WAIT, NO_RECORD, NULL, NULL, &iPingCount, PingbyteResp);

if (bretv && (iPingCount == 15 || iPingCount <= 13)) {
	// pull out the digits from the Response
    if (iPingCount == 15 ) {
        // Handle Radio Binary Response
        // pull out the value
        //A59959AA5559555555565955
        //  E4  5D  04  00  01  08
        //                ^^^^ ------ Indicates there is 1 data byte in the response
        //                    ^^^^ -- this is the response - the number of packets to the engine out of TSMAX packets sent that were lost.
        //
        *p4loss = PingbyteResp[5];
        } else {
        // Handle WTIU ASCII Response (maximum 13 characters)
        // pull out the value
		// p4xxx okay \r\n->
        //   ^^^ -- this is the response - the number of packets to the engine out of TSMAX packets sent that were lost.
    	int h0;
    	int retv = sscanf(PingbyteResp, "p4%d", &h0);
    	if (retv == 1) {
        	*p4loss = h0;
        	} else {
        	*p4loss = TSMAX;
        	}
        }
    return true;
	}
// on error, don't change p4loss
return false;
}
//---------------------------------------------------------------------------





