--[[
---------------------------------------------------------------------------
Remote Train Control Program for Windows

© Copyright 2025 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
---------------------------------------------------------------------------]]
-- Semicolons are not required in Lua code
local title = "RFID Sensor Test";
require([[defines]]);
require([[functions]]);
--
--[[
When using this module, all variables (including functions!) must be declared through 
a regular assignment (even assigning nil will do) in a strict scope before being used 
anywhere or assigned to inside a nested scope.
https://github.com/lua-stdlib/strict
]]
local _ENV = require 'std.strict' (_G)	-- strict.lua will detect undeclared global variables
local _;
--
local MyDirection, OppDirection;
local TagCount = 0;
local EngineFrontTagCount = 0;
-- Counter Labels
Counter01Label = "Tags Count";
C1 = COUNTER01;
Counter03Label = "Block Speed Limit";
local C3 = COUNTER03;
Counter04Label = "Engine Count";
local C4 = COUNTER04;
Counter05Label = "Car Count";
local C5 = COUNTER05;
Counter06Label = "Cab/Obs Count";
local C6 = COUNTER06;

local Tags = {};		-- table to hold all of the tags detected in sequence
local SoundOn = true;
local CurrentBlockNo, LastBlockNo, NextBlockNo;
local StartBlockNo;
local ExpectedReader = 0;	-- check that we get the next expected tag reader
local StartedFlag = false;
local RFIDFlag = false;
local Headway = false;
--
-- Instrumented Layout 4 loaded by "require"
local Layout4 = nil;
local MyTIUNo, Enum = 0, 0;
local MyEngineNo = 0;
local SoundOff = false;
local EngineSoundSave;
local BeepOn = false;
local RunningSpeed = 25;
local WaitForCaboose = false;
local SetupComplete = false;	-- if true, permits settings to be saved.

-- These labels appear on each of the tabs for the Function Buttons
Tab01Label = "Control";
Tab02Label = "Support";
Tab03Label = "Not Used";
--
--[[---------------------------------------------------------------------------------------]]
-- settings are saved in this file
local settingsfile = "RFIDSensor-PC" .. tostring(PCNo()) .. ".ini";

local function readSettings()
	local f = io.open(settingsfile, "r");
	if f then
		print("Loading settings from " .. settingsfile);
		-- Engine Number
		MyEngineNo   = tonumber(string.match(f:read("*l"), "%d+")) or 1;
		print("Engine = " .. MyEngineNo .. " " .. GetName(MyEngineNo));
		-- Running Speed
		RunningSpeed = tonumber(string.match(f:read("*l"), "%d+")) or 20;
		print("Running Speed = " .. RunningSpeed .. " Smph");
		-- Direction
		MyDirection  = tonumber(string.match(f:read("*l"), "%d+")) or WB;
		print("Direction = " .. MyDirection .. " " .. DirectionNames[MyDirection]);
		-- Starting Block
		StartBlockNo = tonumber(string.match(f:read("*l"), "%d+")) or YOUNGSTOWN_BLOCK;
		CurrentBlockNo = StartBlockNo;
		Message(BlockName[CurrentBlockNo]);	-- display the saved current block
		print("Start Block = " .. StartBlockNo .. " " .. BlockName[StartBlockNo]);		
		f:close()
		return true;
	end;
	return false;
end

--[[---------------------------------------------------------------------------------------]]

function writeSettings(pflag)
	-- setup must be complete before settings can be saved
	-- pflag == false for less printed output
	if (not SetupComplete) then
		return false;
		end;
	local f = io.open(settingsfile, "w");
	if f then
		print("Saving settings in " .. settingsfile);
		-- Engine Number
		if (pflag) then print("Engine = " .. MyEngineNo .. " " .. GetName(MyEngineNo)); end;
        f:write(tostring(MyEngineNo).. " " .. GetName(MyEngineNo) .. "\n");		
		-- Running Speed
		if (pflag) then print("Running Speed = " .. RunningSpeed .. " Smph"); end;
		f:write(tostring(RunningSpeed).. " Smph\n");	
		-- Direction
		if (pflag) then print("Direction = " .. MyDirection .. " " .. DirectionNames[MyDirection]); end;		
		f:write(tostring(MyDirection).. " " .. DirectionNames[MyDirection] .. "\n");
		-- Starting Block
		if (pflag) then print("Current Block = " .. CurrentBlockNo .. " " .. BlockName[CurrentBlockNo]); end;
		f:write(tostring(CurrentBlockNo).. " " .. BlockName[CurrentBlockNo] .. "\n");	-- save current block for restart
		f:close();
		return true;
	end;	
	return false;
end;

--[[---------------------------------------------------------------------------------------]]

function setup(Engine, TIU)
print("setup() Running");

-- Instrumented Layout 4
Layout4 = require([[Layout4]]);		-- All of the details about the Layout
MyEngineNo = Engine;	-- this engine number may be changed by the query below
MyDirection = WB;		-- this direction may be changed by the query below
if (Layout4.LLLoop == false) then
	return Stop("Requires a Looping Layout Configuration");
	end;
--
MyTIUNo = Layout4.MASTER_TIU;
--
readSettings();		-- read the .ini file and load MyEngineNo, MyDirection and StartBlockNo
--
print("Runs eastbound trains on the passing/station tracks and westbound on the thru tracks");
--
Title(title, MyEngineNo);

-- Requires at least 8 tag detectors and 13 tag readers
local NumDetectors, NumReaders;
NumDetectors, NumReaders = GetTagReaders();
print("Number of Tag Detectors = " .. NumDetectors .. " Number of Tag Readers =  " .. NumReaders);
if ((Debug() == 0) and (NumDetectors < 8 or (NumReaders < 13 and NumReaders ~= 0))) then
	return Stop("Requires at least 8 Tag Detectors and 13 Tag Readers. Cycle layout power and try again.");
	end;	
--

if (LayoutShowing()) then	-- only if the layout is showing can we process any signals or flags
	ClearAllOccupancy();	-- set all global flags to UNOCCUPIED
	end;
	
math.randomseed(os.time());	-- random seed

--[[--------------------------------------------]]
local EngineNameList = {};	-- table to hold all of the Engine numbers and Names
local EngineNameIndex = 1;
local EngineNameCount = 1;

	for iEng = 1, 99 do	-- check all possible engines on one TIU
		if (Exists(iEng)) then
			local Name, PS;
			Name, PS = GetName(iEng);
		--print(iEng .. " " .. Name .. " PS" .. PS);
			EngineNameList[iEng] = iEng .. " " .. Name .. " PS" .. PS;
			if (MyEngineNo == iEng) then
				EngineNameIndex = EngineNameCount;
				--print("EngineNameIndex = " .. EngineNameIndex);
				end;
			EngineNameCount = EngineNameCount + 1;		
			end
		end
--
print(EngineNameCount - 1 .. " Engines Available");
--
-- Ask the dispatcher for the engine number
--
print("Select Engine");
local ResponseLine = InputDropDown(title, "Select Engine", EngineNameIndex, EngineNameCount - 1, EngineNameList);
--print("ResponseLine ", ResponseLine);	-- should print the engine selected
local typ = type(ResponseLine);
--print("Type ", typ);

if (typ ~= "string" and typ ~= "boolean" and typ ~= "nil") then
	return Stop("InputDropDown did not return a string, boolean or nil");
	end;
if (typ == "boolean" and ResponseLine == false) then
	return Stop("InputDropDown Canceled");
	else		
	if (ResponseLine ~= nil and ResponseLine ~= "") then	-- if the result is a string, then use first number in the string, otherwise, ignore it
		-- pull first integer found in the string
		local EngNo = string.match(ResponseLine, "%d+");
		if (EngNo ~= nil) then
			if (EngNo == 0) then
				return Stop("InputDropDown Canceled");
				end;
			MyEngineNo = tonumber(EngNo);			
			else
			return Stop("Invalid Engine Number");
			end;
		else
		if (typ == "nil") then
			-- nil returned only if parameters are invalid
			return Stop("Invalid Parameters to InputDropDown");
			else
			MyEngineNo = 1;
			end;
		end;	
	end;
--[[--------------------------------------------]]
print("Setup : Engine Number = " .. MyEngineNo);
--[[--------------------------------------------]]
-- Show new title with Engine # and Name and put Engine # into Engine Number Spinner
local Name = GetName(MyEngineNo);
Title(title .. " : Engine #" .. MyEngineNo .. " " .. Name, MyEngineNo);	
--[[--------------------------------------------]]
--
-- Ask the dispatcher for direction
--	
print("Select Direction");
local DirValue = InputDropDown(title, "Select Direction", MyDirection, 2, {"1 Westbound", "2 Eastbound"});
--print("DirValue ", DirValue);
typ = type(DirValue);
--print("Type ", typ);

if (typ == "boolean" and DirValue == false) then
	return Stop("Canceled");
	end;
		
if (DirValue ~= nil and DirValue ~= "") then	-- if the result is a string, then use first number in the string, otherwise, ignore it
	-- pull first integer found in the string
	local DirNo = string.match(DirValue, "%d+");
	if (DirNo ~= nil) then
		MyDirection = tonumber(DirNo);
		else
		return Stop("Invalid Direction");
		end;
	else
	if (typ == "nil") then
		-- nil returned only if parameters are invalid
		return Stop("Invalid Parameters to InputDropDown");
		else
		--MyDirection is not changed
		end
	end	
if (MyDirection == WB) then
	title = "WB RFID Sensor"; -- Westbound
	OppDirection = EB;
	else
	title = "EB RFID Sensor"; -- Eastbound
	OppDirection = WB;
	end;
--[[--------------------------------------------]]	
Title(title .. " Engine #" .. MyEngineNo .. " " .. Name, MyEngineNo);
print("Setup : Direction = " .. DirectionNames[MyDirection]);
--[[--------------------------------------------]]
	--
	-- Ask the dispatcher for starting block number
	--
	-- For superior westbound trains, starting block can be any Thru block or any mainline block
	-- For inferior eastbound trains, starting block can be any Station/Passing block or any mainline block
	--
	local BlockNameList = {};	-- table to hold all of the block numbers and names
	local BlockNameIndex = 1;
	local BlockNameCount = 1;
	
	for iBlock = 1, BLOCK_COUNT do
		local Name = BlockName[iBlock];
		if (Name ~= nil and iBlock <= LAST_BLOCK) then		
			-- blocks not valid for RFID Sensor Test
			if ((iBlock == BYPASS) or (iBlock == ALIQUIPPA_CUTOFF)) then
				goto nextBlock;
				end;
			-- in Point-to-Point Configuration, dont allow selection of Balloon Track
			if ((Layout4.LLLoop == false) and (iBlock == BALLOON_BLOCK)) then
				goto nextBlock
				end;
			-- in Loop Configuration, don't allow selection of any Youngstown or McKees Rocks yard track
			if ((Layout4.LLLoop == true) and ((iBlock == MCKEES_ROCKS_REPAIR_SHOPS_YARD1_TRACK) or (iBlock == MCKEES_ROCKS_REPAIR_SHOPS_YARD2_TRACK) or 
				(iBlock == GATEWAY_YARD1_TRACK) or (iBlock == GATEWAY_YARD2_TRACK))) then
				goto nextBlock
				end
			--print(Name .. "(" .. iBlock .. ")");
			if (MyDirection == EB) then
				-- don't allow selection of thru blocks or starting in McKeesRocks Yard
				if ((iBlock ~= J_L_THRU_TRACK) and (iBlock ~= COLLEGE_THRU_TRACK) and (iBlock ~= STRUTHERS_THRU_TRACK) and
					(iBlock ~= MCKEES_ROCKS_REPAIR_SHOPS_YARD1_TRACK) and (iBlock ~= MCKEES_ROCKS_REPAIR_SHOPS_YARD2_TRACK)) then 
					BlockNameList[BlockNameCount] = iBlock .. " " .. Name; 
					--print("BlockNameList[" .. BlockNameCount .. "] = " .. BlockNameList[BlockNameCount]);
					--------------------------------------------
					if (StartBlockNo == iBlock) then
						BlockNameIndex = BlockNameCount;
						--print("BlockNameIndex = " .. BlockNameIndex);
						end;
					BlockNameCount = BlockNameCount + 1;		
					--------------------------------------------					
					end;
				end;
			if (MyDirection == WB) then
				-- don't allow selection of station/passing blocks or starting in Youngstown Yard
				if ((iBlock ~= J_L_PASSING_TRACK) and (iBlock ~= COLLEGE_STATION_TRACK) and (iBlock ~= STRUTHERS_STATION_TRACK) and
					(iBlock ~= GATEWAY_YARD1_TRACK) and (iBlock ~= GATEWAY_YARD2_TRACK)) then
					BlockNameList[BlockNameCount] = iBlock .. " " .. Name; 
					--print("BlockNameList[" .. BlockNameCount .. "] = " .. BlockNameList[BlockNameCount]);
					--------------------------------------------
					if (StartBlockNo == iBlock) then
						BlockNameIndex = BlockNameCount;
						print("BlockNameIndex = " .. BlockNameIndex);
						end;
					BlockNameCount = BlockNameCount + 1;		
					--------------------------------------------					
					end;
				end;
			end;
		::nextBlock::
		end;
--
-- read Starting Block Number
print("Select Starting Block");
CurrentBlockNo = StartBlockNo;
-- Debug
--
local StartingBlock = InputDropDown(title, "Select Starting Block", BlockNameIndex, BlockNameCount - 1, BlockNameList);
--print("StartingBlock ", StartingBlock);
typ = type(StartingBlock);
--print("Type ", typ);

if (typ == "boolean" and StartingBlock == false) then
	return Stop("Canceled");
	else
		
	if (StartingBlock ~= nil and StartingBlock ~= "") then	-- if the result is a string, then use first number in the string, otherwise, ignore it
		-- pull first integer found in the string
		local BlockNo = string.match(StartingBlock, "%d+");
		if (BlockNo ~= nil) then
			StartBlockNo = tonumber(BlockNo);
			else
			return Stop("Invalid Block Number");
			end;
		else
		if (typ == "nil") then
			-- nil returned only if parameters are invalid
			return Stop("Invalid Parameters to InputDropDown");
			else
			StartBlockNo = 1;
			end;
		end;	
	end;
--[[--------------------------------------------]]
print("Setup : Starting Block = " .. BlockName[StartBlockNo]);
CurrentBlockNo = StartBlockNo;
--[[--------------------------------------------]]
-- set the next expected detector and reader
--[[
print("CurrentBlockNo " .. CurrentBlockNo .. " MyDirection " .. MyDirection);
print("Opposite Direction " .. OppDirection);
print("Detector = " .. DETECTOR .. " Reader = " .. READER);	
print(" Detector = " .. Layout4.STOPBlock(CurrentBlockNo, OppDirection, DETECTOR));
print(" Reader = " .. Layout4.STOPBlock(CurrentBlockNo, OppDirection, READER));
--]]
ExpectedReader = Layout4.STOPBlock(CurrentBlockNo, OppDirection, DETECTOR) * 10 + Layout4.STOPBlock(CurrentBlockNo, OppDirection, READER)
print("Expected Reader = " .. ExpectedReader);
--[[---------------------------------------------------------------------------------------]]
--
if (MyDirection == WB) then
	print("Setup : Westbound");
	--
	-- Engine can start in any block, set exit switches from meet points as necessary
	--
	if (CurrentBlockNo == MCKEES_ROCKS_REPAIR_SHOPS_YARD1_TRACK) then
		-- position MCKEES_ROCKS_REPAIR_SHOPS_YARD switch as needed depending on value of CurrentBlockNo
		Switch(NORMAL, Switches[MCKEES_ROCKS_REPAIR_SHOPS_YARD][CHAN_NUMBER], Switches[MCKEES_ROCKS_REPAIR_SHOPS_YARD][AIU_NUMBER], Switches[MCKEES_ROCKS_REPAIR_SHOPS_YARD][TIU_NUMBER], 0);
		-- position BALLOON switch as needed
		Switch(NORMAL, Switches[BALLOON][CHAN_NUMBER], Switches[BALLOON][AIU_NUMBER], Switches[BALLOON][TIU_NUMBER], 0);	
		
	elseif (CurrentBlockNo == MCKEES_ROCKS_REPAIR_SHOPS_YARD2_TRACK) then		
		-- position MCKEES_ROCKS_REPAIR_SHOPS_YARD switch as needed depending on value of CurrentBlockNo
		Switch(REVERSE, Switches[MCKEES_ROCKS_REPAIR_SHOPS_YARD][CHAN_NUMBER], Switches[MCKEES_ROCKS_REPAIR_SHOPS_YARD][AIU_NUMBER], Switches[MCKEES_ROCKS_REPAIR_SHOPS_YARD][TIU_NUMBER], 0);
		-- position BALLOON switch as needed
		Switch(NORMAL, Switches[BALLOON][CHAN_NUMBER], Switches[BALLOON][AIU_NUMBER], Switches[BALLOON][TIU_NUMBER], 0);	
		
	elseif (CurrentBlockNo == J_L_THRU_TRACK) then -- "J&L Thru Track"
		-- Position ALIQUIPPA_WEST
		Switch(NORMAL, Switches[ALIQUIPPA_WEST][CHAN_NUMBER], Switches[ALIQUIPPA_WEST][AIU_NUMBER], Switches[ALIQUIPPA_WEST][TIU_NUMBER], 0);	
		
	elseif (CurrentBlockNo == J_L_PASSING_TRACK) then -- "J&L Passing Track" -- Westbound trains don't use the passing track
		-- Position ALIQUIPPA_WEST
		Switch(REVERSE, Switches[ALIQUIPPA_WEST][CHAN_NUMBER], Switches[ALIQUIPPA_WEST][AIU_NUMBER], Switches[ALIQUIPPA_WEST][TIU_NUMBER], 0);

	elseif (CurrentBlockNo == COLLEGE_THRU_TRACK) then -- "College Thru Track"
		-- Position COLLEGE_WEST
		Switch(NORMAL, Switches[COLLEGE_WEST][CHAN_NUMBER], Switches[COLLEGE_WEST][AIU_NUMBER], Switches[COLLEGE_WEST][TIU_NUMBER], 0);
		
	elseif (CurrentBlockNo == COLLEGE_STATION_TRACK) then -- "College Station Track" -- Westbound trains don't use the station track
		-- Position COLLEGE_WEST
		Switch(REVERSE, Switches[COLLEGE_WEST][CHAN_NUMBER], Switches[COLLEGE_WEST][AIU_NUMBER], Switches[COLLEGE_WEST][TIU_NUMBER], 0);
		
	elseif (CurrentBlockNo == STRUTHERS_THRU_TRACK) then -- "Struthers Thru Track"
		-- Position STRUTHERS_WEST
		Switch(NORMAL, Switches[STRUTHERS_WEST][CHAN_NUMBER], Switches[STRUTHERS_WEST][AIU_NUMBER], Switches[STRUTHERS_WEST][TIU_NUMBER], 0);
		
	elseif (CurrentBlockNo == STRUTHERS_STATION_TRACK) then -- "Struthers Station Track" -- Westbound trains don't use the station track
		-- Position STRUTHERS_WEST
		Switch(REVERSE, Switches[STRUTHERS_WEST][CHAN_NUMBER], Switches[STRUTHERS_WEST][AIU_NUMBER], Switches[STRUTHERS_WEST][TIU_NUMBER], 0);
		end;
	end;

--[[--------------------------------- Eastbound --------------------------------------------]]
if (MyDirection == EB) then
	print("Setup : Eastbound");
	--
	-- Engine can start in any block, set exit switches from meet points as necessary
	--
	if (CurrentBlockNo == GATEWAY_YARD1_TRACK) then
		-- position GATEWAY_YARD switch as needed depending on value of CurrentBlockNo
		Switch(NORMAL, Switches[GATEWAY_YARD][CHAN_NUMBER], Switches[GATEWAY_YARD][AIU_NUMBER], Switches[GATEWAY_YARD][TIU_NUMBER], 0);
		-- position BALLOON switch as needed
		Switch(NORMAL, Switches[BALLOON][CHAN_NUMBER], Switches[BALLOON][AIU_NUMBER], Switches[BALLOON][TIU_NUMBER], 0);	
		
	elseif (CurrentBlockNo == GATEWAY_YARD2_TRACK) then
		-- position GATEWAY_YARD switch as needed depending on value of CurrentBlockNo
		Switch(REVERSE, Switches[GATEWAY_YARD][CHAN_NUMBER], Switches[GATEWAY_YARD][AIU_NUMBER], Switches[GATEWAY_YARD][TIU_NUMBER], 0);
		-- position BALLOON switch as needed
		Switch(NORMAL, Switches[BALLOON][CHAN_NUMBER], Switches[BALLOON][AIU_NUMBER], Switches[BALLOON][TIU_NUMBER], 0);	
		
	elseif (CurrentBlockNo == STRUTHERS_STATION_TRACK) then -- "Struthers Station Track"
		-- Position STRUTHERS_EAST
		Switch(REVERSE, Switches[STRUTHERS_EAST][CHAN_NUMBER], Switches[STRUTHERS_EAST][AIU_NUMBER], Switches[STRUTHERS_EAST][TIU_NUMBER], 0);	

	elseif (CurrentBlockNo == STRUTHERS_THRU_TRACK) then -- "Struthers Thru Track" -- Eastbound trains don't use the thru track
		-- Position STRUTHERS_EAST
		Switch(NORMAL, Switches[STRUTHERS_EAST][CHAN_NUMBER], Switches[STRUTHERS_EAST][AIU_NUMBER], Switches[STRUTHERS_EAST][TIU_NUMBER], 0);	

	elseif (CurrentBlockNo == COLLEGE_STATION_TRACK) then -- "College Station Track"
		-- Position COLLEGE_EAST
		Switch(REVERSE, Switches[COLLEGE_EAST][CHAN_NUMBER], Switches[COLLEGE_EAST][AIU_NUMBER], Switches[COLLEGE_EAST][TIU_NUMBER], 0);
		
	elseif (CurrentBlockNo == COLLEGE_THRU_TRACK) then -- "College Thru Track" -- Eastbound trains don't use the thru track
		-- Position COLLEGE_EAST
		Switch(NORMAL, Switches[COLLEGE_EAST][CHAN_NUMBER], Switches[COLLEGE_EAST][AIU_NUMBER], Switches[COLLEGE_EAST][TIU_NUMBER], 0);
		
	elseif (CurrentBlockNo == J_L_PASSING_TRACK) then -- "J&L Passing Track"
		-- Position ALIQUIPPA_EAST
		Switch(REVERSE, Switches[ALIQUIPPA_EAST][CHAN_NUMBER], Switches[ALIQUIPPA_EAST][AIU_NUMBER], Switches[ALIQUIPPA_EAST][TIU_NUMBER], 0);

	elseif (CurrentBlockNo == J_L_THRU_TRACK) then -- "J&L Thru Track" -- Eastbound trains don't use the thru track
		-- Position ALIQUIPPA_EAST
		Switch(NORMAL, Switches[ALIQUIPPA_EAST][CHAN_NUMBER], Switches[ALIQUIPPA_EAST][AIU_NUMBER], Switches[ALIQUIPPA_EAST][TIU_NUMBER], 0);
		end;
	end;	
--[[---------------------------------------------------------------------------------------]]

if (LayoutShowing()) then
	-- create and register the cab signals
	print("Setup : Creating Cab Signals");
	-- Register the new block's home and distant signals
	local NumLightsHome, NumLightsDistant;
	local HomeSignal = NextSignal[MyDirection][CurrentBlockNo][HOMESIGNAL];	-- next signal number
_, NumLightsHome = GetAspect(HomeSignal);	-- 1, 2 or 3 lights or 3 LEDs on the signal head
	local DistantSignal = NextSignal[MyDirection][CurrentBlockNo][DISTANTSIGNAL]
	_, NumLightsDistant = GetAspect(DistantSignal);
	CreateCab(NumLightsHome, NumLightsDistant);	-- create light signals - (home, distant)			
	print("Setup : Registering the Cab Signals #", HomeSignal, " and #", DistantSignal);
	RegisterCabSignals(HomeSignal, DistantSignal);
							-- associate the Home cab signal with next signal and
							-- the Distant cab signal with second signal.
							-- Signals must be created by the "Layout4 Signal Master.lua"
							-- script which should be run in a different PC window
	print("Setup : Reflecting signals from Layout window into the Cab Display");
	print ("Setup : Home Signal in ", BlockName[CurrentBlockNo], " : " .. AspectNames[(GetCabAspect(HOMESIGNAL))]);
	print ("Setup : Distant Signal in ", BlockName[CurrentBlockNo], " : " .. AspectNames[(GetCabAspect(DISTANTSIGNAL))]);
end;
-----------------------------------------------------------------------------------------------------
SetCounter(C3, RunningSpeed);
--[[-------------------------]]
-- Set Loop Switches to looping
Switch(REVERSE, Switches[BALLOON][CHAN_NUMBER], Switches[BALLOON][AIU_NUMBER], Switches[BALLOON][TIU_NUMBER], 0)	
--[[-------------------------]]
-- read up current sound volume
local SoundSave = GetVolume(MASTER_VOLUME, MyEngineNo, MyTIUNo);	-- get Volume
if (SoundSave ~= nil) then
	EngineSoundSave = SoundSave;
	else
	EngineSoundSave = 90;
	end;

if (LayoutShowing()) then	
	OverrideOccupancy(CurrentBlockNo, MyEngineNo | (MyDirection << 8));	-- force set occupancy flag on the starting block
	-- notify the APB Master script of intitial block occpancy conditions.
	-- This is the only place where we send these events to the APB Master
	SetEvent(1, BLOCK_OCCUPANCY_EVENT, CurrentBlockNo, OCCUPIED, MyDirection);
	end;
--
-- indicate that setup is complete and the settings can be saved
SetupComplete = true;
-- Stop the Garbage collector since this is a real-time program
collectgarbage("stop")
print("Setup : Garbage Collection stopped");
--collectgarbage("collect")
--collectgarbage("restart")
--
print("Setup : press [Start Run] to begin");
return true;		-- false=setup failed, true=setup succeeded
end
--[[---------------------------------------------------------------------------------------]]

function DisplayBlockNames(Title, LastBlockNo, CurrentBlockNo, NextBlockNo)
print(Title .. BlockName[LastBlockNo] .. " > " .. BlockName[CurrentBlockNo] .. " > " .. BlockName[NextBlockNo]);
end;
--[[---------------------------------------------------------------------------------------]]

function tag(Detector, Reader, EngineNo, TagLocation, CarModel, Railroad, CarNumber, TagPacket)
-- This function is called each time a tag programmed with 32 bytes of data is detected (TAGDATABLOCK4)
-- TagPacket contains 8 characters (4 hex digits) of UID and 32 characters
-- of block 4 data from the programmed tag.

BumpCounter(COUNTER01);	
local PacketLen = #TagPacket;

if (Debug() >= 8) then print("Packet Length = ", PacketLen); end;
if (Debug() >= 7) then print(TagPacket); end;

-- The complete packet received is in TagPacket 
-- (all of the fields have already been extracted into the first parameters to tag()
--   First 8 digits are the 4 byte Tag UID in hexidecimal
--   Next 32 digits are the 16 bytes of Block 4 Information read from the Tag in hexidecimal
if (Debug() >= 6) then 
	print(string.format("Packet  %s  %s", string.sub(TagPacket, 1, 8),
	string.sub(TagPacket,9,40) ));
	end

if (not RFIDFlag) then	-- ignore tags unless engine is moving
	return true;
	end;
	
-- index the table Tags[] by tag ID, value is the tag packet itself
Tags[string.sub(TagPacket, 1, 8)] = string.sub(TagPacket, 9, 40);	-- remove checksum and EOP
TagCount = TagCount + 1;	-- count the number of tags

local MyReader = (Detector * 10) + Reader;
if (MyReader ~= ExpectedReader) then
	return Stop("Got Reader = " .. MyReader .. " Expected Reader = " .. ExpectedReader);
	end

if (EngineNo > 0) then	-- it's an engine
	if (EngineNo == MyEngineNo) then
		if (BeepOn) then Beep(ASTERISK); end;
		-- next line shows a different way to quote a string using brackets
		printc(clBlue, string.format([[tag(%.2f) : Detector %d Reader %d Eng#%d %s %s #%s %s]],
			RunTime(), Detector, Reader, EngineNo, GetRailroadName(Railroad), GetEngineName(CarModel), CarNumber, GetTagLocation(TagLocation)));
		if (TagLocation == TAG_ON_FRONT_TRUCK) then
			EngineFrontTagCount = EngineFrontTagCount + 1;
			print("Engine Count = " .. EngineFrontTagCount);
			BumpCounter(C4);	-- Engine Count - Front Truck only
			WaitForCaboose = true;
			-- determine the new block
			LastBlockNo = CurrentBlockNo;

			if (MyDirection == WB) then
				-- Tag 3-0
				if (MyReader == MCKEES_ROCKS_REPAIR_SHOPS_YARD_TAG) then
					CurrentBlockNo = MCKEES_ROCKS_BLOCK;
				-- Tag 5-0
				elseif (MyReader == ALIQUIPPA_EAST_TAG) then
					-- set Aliquippa East switch to NORMAL
					-- set Aliquippa West switch to NORMAL
					Switch(NORMAL, Switches[ALIQUIPPA_EAST][CHAN_NUMBER], Switches[ALIQUIPPA_EAST][AIU_NUMBER], Switches[ALIQUIPPA_EAST][TIU_NUMBER], 0)
					Switch(NORMAL, Switches[ALIQUIPPA_WEST][CHAN_NUMBER], Switches[ALIQUIPPA_WEST][AIU_NUMBER], Switches[ALIQUIPPA_WEST][TIU_NUMBER], 1)	
					CurrentBlockNo = J_L_THRU_TRACK;
				-- Tag 1-0
				elseif (MyReader == ALIQUIPPA_WEST_TAG) then
					CurrentBlockNo = ALIQUIPPA_BLOCK;
				-- Tag 6-0
				elseif (MyReader == MONACA_EAST_TAG) then
					CurrentBlockNo = MONACA_BLOCK;
					-- entering tunnel, reduce volume
					if (EngineNo == MyEngineNo and TagLocation == TAG_ON_FRONT_TRUCK and SoundOff == false) then
						SetVolume(MASTER_VOLUME, math.floor(EngineSoundSave * 2/3), 0, MyEngineNo, MyTIUNo);
						SetVolume(MASTER_VOLUME, math.floor(EngineSoundSave * 1/3), 1, MyEngineNo, MyTIUNo);
						SetVolume(MASTER_VOLUME, 0,                                 2, MyEngineNo, MyTIUNo);
						end;
		
				-- Tag 6-1
				elseif (MyReader == MONACA_WEST_TAG) then
					CurrentBlockNo = FALLSTON_BLOCK;	
					-- leaving tunnel, increase volume
					if (EngineNo == MyEngineNo and TagLocation == TAG_ON_FRONT_TRUCK and SoundOff == false) then
						SetVolume(MASTER_VOLUME, math.floor(EngineSoundSave * 1/3), 0, MyEngineNo, MyTIUNo);
						SetVolume(MASTER_VOLUME, math.floor(EngineSoundSave * 2/3), 1, MyEngineNo, MyTIUNo);
						SetVolume(MASTER_VOLUME,            EngineSoundSave,        2, MyEngineNo, MyTIUNo);
						end;
		
				-- Tag 4-1
				elseif (MyReader == COLLEGE_EAST_TAG) then
					-- set College East switch to NORMAL
					-- set College West switch to NORMAL
					Switch(NORMAL, Switches[COLLEGE_EAST][CHAN_NUMBER], Switches[COLLEGE_EAST][AIU_NUMBER], Switches[COLLEGE_EAST][TIU_NUMBER], 0)
					Switch(NORMAL, Switches[COLLEGE_WEST][CHAN_NUMBER], Switches[COLLEGE_WEST][AIU_NUMBER], Switches[COLLEGE_WEST][TIU_NUMBER], 1)
					CurrentBlockNo = COLLEGE_THRU_TRACK;
				-- Tag 2-1
				elseif (MyReader == COLLEGE_WEST_TAG) then
					CurrentBlockNo = NEW_CASTLE_JUNCTION_BLOCK;
				-- Tag 9-0
				elseif (MyReader == LOWELLVILLE_TAG) then
					CurrentBlockNo = LOWELLVILLE_BLOCK;					
				-- Tag 2-0
				elseif (MyReader == STRUTHERS_EAST_TAG) then
					-- set Struthers East switch to NORMAL
					-- set Struthers West switch to NORMAL
					Switch(NORMAL, Switches[STRUTHERS_EAST][CHAN_NUMBER], Switches[STRUTHERS_EAST][AIU_NUMBER], Switches[STRUTHERS_EAST][TIU_NUMBER], 0)					
					Switch(NORMAL, Switches[STRUTHERS_WEST][CHAN_NUMBER], Switches[STRUTHERS_WEST][AIU_NUMBER], Switches[STRUTHERS_WEST][TIU_NUMBER], 1)
					CurrentBlockNo = STRUTHERS_THRU_TRACK;
				-- Tag 4-0
				elseif (MyReader == STRUTHERS_WEST_TAG) then
					CurrentBlockNo = YOUNGSTOWN_BLOCK;
				-- Tag 5-1
				elseif (MyReader == GATEWAY_YARD_TAG) then
					CurrentBlockNo = LIONEL_IVES_BLOCK;
				elseif (MyReader == FALLSTON_TAG) then
					CurrentBlockNo = BEAVER_FALLS_BLOCK;
				elseif (MyReader == WEST_ALIQUIPPA_TAG) then
					CurrentBlockNo = WEST_ALIQUIPPA_BLOCK;
				else
					return Stop("Unrecognized Reader = " .. MyReader);
					end;
				end;

			if (MyDirection == EB) then
				-- Tag 3-0
				if (MyReader == MCKEES_ROCKS_REPAIR_SHOPS_YARD_TAG) then
					CurrentBlockNo = LIONEL_IVES_BLOCK;
				-- Tag 5-0
				elseif (MyReader == ALIQUIPPA_EAST_TAG) then
					CurrentBlockNo = MCKEES_ROCKS_BLOCK;
				-- Tag 1-0
				elseif (MyReader == ALIQUIPPA_WEST_TAG) then
					-- set Aliquippa West switch to REVERSE
					-- set Aliquippa East switch to REVERSE
					Switch(REVERSE, Switches[ALIQUIPPA_WEST][CHAN_NUMBER], Switches[ALIQUIPPA_WEST][AIU_NUMBER], Switches[ALIQUIPPA_WEST][TIU_NUMBER], 0)	
					Switch(REVERSE, Switches[ALIQUIPPA_EAST][CHAN_NUMBER], Switches[ALIQUIPPA_EAST][AIU_NUMBER], Switches[ALIQUIPPA_EAST][TIU_NUMBER], 1)
					CurrentBlockNo = J_L_PASSING_TRACK;
				-- Tag 6-0
				elseif (MyReader == MONACA_EAST_TAG) then
					CurrentBlockNo = WEST_ALIQUIPPA_BLOCK;
					-- leaving tunnel, increase volume
					if (EngineNo == MyEngineNo and TagLocation == TAG_ON_FRONT_TRUCK and SoundOff == false) then
						SetVolume(MASTER_VOLUME, math.floor(EngineSoundSave * 1/3), 0, MyEngineNo, MyTIUNo);
						SetVolume(MASTER_VOLUME, math.floor(EngineSoundSave * 2/3), 1, MyEngineNo, MyTIUNo);
						SetVolume(MASTER_VOLUME,            EngineSoundSave,        2, MyEngineNo, MyTIUNo);
						end;
		
				-- Tag 6-1
				elseif (MyReader == MONACA_WEST_TAG) then
					CurrentBlockNo = MONACA_BLOCK;				
					-- entering tunnel, reduce volume
					if (EngineNo == MyEngineNo and TagLocation == TAG_ON_FRONT_TRUCK and SoundOff == false) then
						SetVolume(MASTER_VOLUME, math.floor(EngineSoundSave * 2/3), 0, MyEngineNo, MyTIUNo);
						SetVolume(MASTER_VOLUME, math.floor(EngineSoundSave * 1/3), 1, MyEngineNo, MyTIUNo);
						SetVolume(MASTER_VOLUME,            0,                      2, MyEngineNo, MyTIUNo);
						end;
		
				-- Tag 4-1
				elseif (MyReader == COLLEGE_EAST_TAG) then
					CurrentBlockNo = BEAVER_FALLS_BLOCK;
				-- Tag 2-1
				elseif (MyReader == COLLEGE_WEST_TAG) then
					-- set College West switch to REVERSE
					-- set College East switch to REVERSE
					Switch(REVERSE, Switches[COLLEGE_WEST][CHAN_NUMBER], Switches[COLLEGE_WEST][AIU_NUMBER], Switches[COLLEGE_WEST][TIU_NUMBER], 0)
					Switch(REVERSE, Switches[COLLEGE_EAST][CHAN_NUMBER], Switches[COLLEGE_EAST][AIU_NUMBER], Switches[COLLEGE_EAST][TIU_NUMBER], 1)
					CurrentBlockNo = COLLEGE_STATION_TRACK;
				-- Tag 2-0
				elseif (MyReader == STRUTHERS_EAST_TAG) then
					CurrentBlockNo = LOWELLVILLE_BLOCK;
				-- Tag 9-0
				elseif (MyReader == LOWELLVILLE_TAG) then
					CurrentBlockNo = NEW_CASTLE_JUNCTION_BLOCK;					
				-- Tag 4-0
				elseif (MyReader == STRUTHERS_WEST_TAG) then
					-- set Struthers West switch to REVERSE
					-- set Struthers East switch to REVERSE
					Switch(REVERSE, Switches[STRUTHERS_WEST][CHAN_NUMBER], Switches[STRUTHERS_WEST][AIU_NUMBER], Switches[STRUTHERS_WEST][TIU_NUMBER], 0)
					Switch(REVERSE, Switches[STRUTHERS_EAST][CHAN_NUMBER], Switches[STRUTHERS_EAST][AIU_NUMBER], Switches[STRUTHERS_EAST][TIU_NUMBER], 1)					
					CurrentBlockNo = STRUTHERS_STATION_TRACK;
				-- Tag 5-1
				elseif (MyReader == GATEWAY_YARD_TAG) then
					CurrentBlockNo = YOUNGSTOWN_BLOCK;
				elseif (MyReader == FALLSTON_TAG) then
					CurrentBlockNo = FALLSTON_BLOCK;
				elseif (MyReader == WEST_ALIQUIPPA_TAG) then
					CurrentBlockNo = ALIQUIPPA_BLOCK;					
				else
					return Stop("Unrecognized Reader = " .. MyReader);
					end;
				end;
				
			if (LayoutShowing()) then
				SetOccupancy(CurrentBlockNo, MyEngineNo | (MyDirection << 8));
				end;
			if (LayoutShowing()) then
				-- Register the new block's home and distant signals
				local Aspect;			
				local NumLightsHome, NumLightsDistant;
				local HomeSignal = NextSignal[MyDirection][CurrentBlockNo][HOMESIGNAL];	-- next signal number
				Aspect, NumLightsHome = GetAspect(HomeSignal);	-- 1, 2 or 3 lights on the signal head
				local DistantSignal = NextSignal[MyDirection][CurrentBlockNo][DISTANTSIGNAL]
				_, NumLightsDistant = GetAspect(DistantSignal);
				CreateCab(NumLightsHome, NumLightsDistant);	-- create light signals - (home, distant)			
				RegisterCabSignals(HomeSignal, DistantSignal);					
				end;
			end;
		-- done processing the detected engine front tag
		return true;	-- true=continue to process tags, false=stop processing tags
		else
		-- ignore engines that are not me
		return true;	-- true=continue to process tags, false=stop processing tags
		end;
	
	else
	if (WaitForCaboose == true and (CarModel == CABOOSE or CarModel == OBSERVATION)) then	-- it's a Caboose or Observation car
		if (BeepOn) then Beep(EXCLAMATION); end;
		printc(clBlue, string.format([[tag(%.2f) : Detector %d Reader %d Car %s %s #%s]],
			RunTime(), Detector, Reader, GetRailroadName(Railroad), GetCarName(CarModel), CarNumber));
		BumpCounter(C6);	-- Caboose/Observation car Count
		WaitForCaboose = false;
		if (((TagCount) % 5) ~= 0) then
			return Stop("Complete set of 5 tags not detected by a tag reader");
			else
			print("Tag Count OK");
			end;
		--print("CurrentBlockNo " .. CurrentBlockNo .. " MyDirection " .. MyDirection);
		--print("Opposite Direction " .. OppDirection);
		--print("Detector = " .. DETECTOR .. " Reader = " .. READER);		
		--print(" Detector = " .. Layout4.STOPBlock(CurrentBlockNo, OppDirection, DETECTOR));
		--print(" Reader = " ..   Layout4.STOPBlock(CurrentBlockNo, OppDirection, READER));
		ExpectedReader = Layout4.STOPBlock(CurrentBlockNo, OppDirection, DETECTOR) * 10 + Layout4.STOPBlock(CurrentBlockNo, OppDirection, READER);
		print("Expected Reader Changed to " .. ExpectedReader);
		--[[---------------------------------------------------------------------------------------]]			
		if (LayoutShowing()) then
			ClearOccupancy(LastBlockNo, MyEngineNo);
			end;
		if (MyDirection == WB) then
			if (MyReader == ALIQUIPPA_WEST_TAG) then
				-- set Aliquippa East switch to NORMAL
				-- set Aliquippa West switch to NORMAL
				Switch(NORMAL, Switches[ALIQUIPPA_EAST][CHAN_NUMBER], Switches[ALIQUIPPA_EAST][AIU_NUMBER], Switches[ALIQUIPPA_EAST][TIU_NUMBER], 0)	
				Switch(NORMAL, Switches[ALIQUIPPA_WEST][CHAN_NUMBER], Switches[ALIQUIPPA_WEST][AIU_NUMBER], Switches[ALIQUIPPA_WEST][TIU_NUMBER], 1)	
			elseif (MyReader == COLLEGE_WEST_TAG) then
				-- set College East switch to NORMAL
				-- set College West switch to NORMAL
				Switch(NORMAL, Switches[COLLEGE_EAST][CHAN_NUMBER], Switches[COLLEGE_EAST][AIU_NUMBER], Switches[COLLEGE_EAST][TIU_NUMBER], 0)
				Switch(NORMAL, Switches[COLLEGE_WEST][CHAN_NUMBER], Switches[COLLEGE_WEST][AIU_NUMBER], Switches[COLLEGE_WEST][TIU_NUMBER], 1)
				-- Tag 2-0
			elseif (MyReader == STRUTHERS_WEST_TAG) then
				-- set Struthers East switch to NORMAL
				-- set Struthers West switch to NORMAL
				Switch(NORMAL, Switches[STRUTHERS_EAST][CHAN_NUMBER], Switches[STRUTHERS_EAST][AIU_NUMBER], Switches[STRUTHERS_EAST][TIU_NUMBER], 0)				
				Switch(NORMAL, Switches[STRUTHERS_WEST][CHAN_NUMBER], Switches[STRUTHERS_WEST][AIU_NUMBER], Switches[STRUTHERS_WEST][TIU_NUMBER], 1)
				end;
			end;
		if (MyDirection == EB) then
			if (MyReader == ALIQUIPPA_EAST_TAG) then
				-- set Aliquippa West switch to NORMAL
				-- set Aliquippa East switch to NORMAL
				Switch(NORMAL, Switches[ALIQUIPPA_WEST][CHAN_NUMBER], Switches[ALIQUIPPA_WEST][AIU_NUMBER], Switches[ALIQUIPPA_WEST][TIU_NUMBER], 0)	
				Switch(NORMAL, Switches[ALIQUIPPA_EAST][CHAN_NUMBER], Switches[ALIQUIPPA_EAST][AIU_NUMBER], Switches[ALIQUIPPA_EAST][TIU_NUMBER], 1)		
			elseif (MyReader == COLLEGE_EAST_TAG) then
				-- set College West switch to NORMAL
				-- set College East switch to NORMAL
				Switch(NORMAL, Switches[COLLEGE_WEST][CHAN_NUMBER], Switches[COLLEGE_WEST][AIU_NUMBER], Switches[COLLEGE_WEST][TIU_NUMBER], 0)
				Switch(NORMAL, Switches[COLLEGE_EAST][CHAN_NUMBER], Switches[COLLEGE_EAST][AIU_NUMBER], Switches[COLLEGE_EAST][TIU_NUMBER], 1)	
				-- Tag 4-0
			elseif (MyReader == STRUTHERS_EAST_TAG) then
				-- set Struthers West switch to NORMAL
				-- set Struthers East switch to NORMAL
				Switch(NORMAL, Switches[STRUTHERS_WEST][CHAN_NUMBER], Switches[STRUTHERS_WEST][AIU_NUMBER], Switches[STRUTHERS_WEST][TIU_NUMBER], 0)
				Switch(NORMAL, Switches[STRUTHERS_EAST][CHAN_NUMBER], Switches[STRUTHERS_EAST][AIU_NUMBER], Switches[STRUTHERS_EAST][TIU_NUMBER], 1)								
				end;
			end;
		if (SoundOff == false) then	-- toot only if sound is on
			PlaySound(43, 0, MyEngineNo, MyTIUNo); -- SFS
			end;
		-- at this point, C4 == C6 and C5 = C4 * 2
		if ((GetCounter(C4) ~= GetCounter(C6)) or (GetCounter(C5) ~= GetCounter(C4) * 2)) then
			return Stop("Car Counts Wrong");
			end;
		-- done processing the detected caboose
		return true;	-- true=continue to process tags, false=stop processing tags
		else	-- it's a car (something other than an engine or a caboose)
		if (BeepOn) then Beep(DEFAULT_BEEP); end;
		printc(clFuchsia, string.format([[tag(%.2f) : Detector %d Reader %d Car %s %s #%s]],
			RunTime(), Detector, Reader, GetRailroadName(Railroad), GetCarName(CarModel), CarNumber));
		BumpCounter(C5);	-- Count of non-Caboose and non-Observation cars
		-- done processing the detected car
		return true;	-- true=continue to process tags, false=stop processing tags
		end;

	end;

return true;	-- true=continue to process tags, false=stop processing tags
end	
--[[---------------------------------------------------------------------------------------]]

function cleanup()
-- this function is called once when the user presses the [STOP] button or the Stop() function is called
SoundOnButton(MyEngineNo, MyTIUNo)
print("All Newly Detected Tags:");
local k, v;
for k,v in  pairs(Tags) do		-- dump out the Tags table
	--                           . . . . . . . . . . . . . . . .
	-- print format:  6619044A  88080101030108011B02202031353536
	print(k .. "  " .. v);
	end
SetSpeed(0, 0, MyEngineNo, MyTIUNo);
Rate(1, 1, 2, MyEngineNo, MyTIUNo);
--
writeSettings(true);		-- write the .ini file and save MyEngineNo, MyDirection and StartBlockNo
--
print("Shutdown in Progress.....");
--
if (StartedFlag) then
	print("Shutting down the Engine");
	SoundOnButton()
	Sleep(10);
	EngineShutdown(MyEngineNo, MyTIUNo);
	StartedFlag = false;
	end
RFIDFlag = false;
print(string.format("cleanup() Complete at %.2f", RunTime()));
return true;		-- false=cleanup failed, true=cleanup succeeded
end
--[[---------------------------------------------------------------------------------------]]
function DTOButton()
print(string.format("Engine %d Trip Odometer %.3f Smiles", MyEngineNo, DTO(MyEngineNo, MyTIUNo)));
return true;
end
Function01Name, Function01Label = DTOButton, "Show DTO";
--[[---------------------------------------------------------------------------------------]]
function DODButton()
print(string.format("Engine %d Odometer %.3f Smiles", MyEngineNo, DOD(MyEngineNo, MyTIUNo)));
return true;
end
Function02Name, Function02Label = DODButton, "Show DOD";
--[[---------------------------------------------------------------------------------------]]
function Plus5Button()
RunningSpeed = RunningSpeed + 5;
SetSpeed(RunningSpeed, 0, MyEngineNo, MyTIUNo);
SetCounter(C3, RunningSpeed)
return true;
end
Function03Name, Function03Label = Plus5Button, "Speed +5";
--[[---------------------------------------------------------------------------------------]]
function Minus5Button()
RunningSpeed = RunningSpeed - 5;
SetSpeed(RunningSpeed, 0, MyEngineNo, MyTIUNo);
SetCounter(C3, RunningSpeed)
return true;
end
Function04Name, Function04Label = Minus5Button, "Speed -5";
--[[---------------------------------------------------------------------------------------]]
function SoundOffButton()
print("Sound Off");
if (SoundOff == false) then
	-- read up current sound volume
	local SoundSave = GetVolume(MASTER_VOLUME, MyEngineNo, MyTIUNo);	-- get Volume
	if (SoundSave ~= nil) then
		EngineSoundSave = SoundSave;
		else
		EngineSoundSave = 90;
		end;
	print(" Sound Off - Engine " .. MyEngineNo .. " Level was " .. EngineSoundSave);
	SoundOff = true;
	SetVolume(MASTER_VOLUME, 0, 0, MyEngineNo, MyTIUNo);
	end;
return true;
end
Function19Name, Function19Label = SoundOffButton, "Sound Off";
--[[---------------------------------------------------------------------------------------]]

function SoundOnButton()
if (SoundOff == true) then
	if (EngineSoundSave ~= nil) then
		EngineSoundSave = 90;		-- default value
		end;
	print(" Sound On - Engine " .. MyEngineNo .. " Level restored to " .. EngineSoundSave);
	SoundOff = false;
	SetVolume(MASTER_VOLUME, EngineSoundSave, 0, MyEngineNo, MyTIUNo);
	end;
return true;
end
Function20Name, Function20Label = SoundOnButton, "Sound On";
--[[---------------------------------------------------------------------------------------]]
function InitButton()
print("Initialize");
WaitForCaboose = false;
print("Set all Switches to Normal postion");
local k, v;
-- set all switches to NORMAL position
for k,v in pairs(Switches) do
	print("k = " .. k .. " TIU = " .. v[TIU_NUMBER] .. " AIU = " .. v[AIU_NUMBER] .. " Channel = " .. v[CHAN_NUMBER]);
	Switch(NORMAL, v[CHAN_NUMBER], v[AIU_NUMBER], v[TIU_NUMBER], 0);
	Sleep(0.5);
	end
if (Debug() > 3) then
	-- set all switches to REVERSE
	for k,v in pairs(Switches) do
		print("k = " .. k .. " TIU = " .. v[TIU_NUMBER] .. " AIU = " .. v[AIU_NUMBER] .. " Channel = " .. v[CHAN_NUMBER]);
		Switch(REVERSE, v[CHAN_NUMBER], v[AIU_NUMBER], v[TIU_NUMBER], 0);
		Sleep(0.5);
		end;
	-- set all switches to NORMAL
	for k,v in pairs(Switches) do
		print("k = " .. k .. " TIU = " .. v[TIU_NUMBER] .. " AIU = " .. v[AIU_NUMBER] .. " Channel = " .. v[CHAN_NUMBER]);
		Switch(NORMAL, v[CHAN_NUMBER], v[AIU_NUMBER], v[TIU_NUMBER], 0);
		Sleep(0.5);
		end;
	end;

print("Initialization is Complete");
return true;
end
Function06Name, Function06Label = InitButton, "Initialize Layout";
--[[---------------------------------------------------------------------------------------]]

function StartButton()
print("Start the Run");
-- start the engine
Rate(1, 1, 0, MyEngineNo, MyTIUNo);
print("setup : Set accel/decel rates to 1");
if (StartedFlag == false) then
	EngineStartUp(MyEngineNo, MyTIUNo);
	print("Starting up the Engine");
	StartedFlag = true;
	Sleep(20);
	end
SetDirection(FORWARD, 0, MyEngineNo, MyTIUNo);
WaitForCaboose = false;
--
RFIDFlag = true;
Whistle(ON, 20, MyEngineNo, MyTIUNo);
Whistle(OFF, 22, MyEngineNo, MyTIUNo);
local MyDTO = DTO(MyEngineNo, MyTIUNo);
if (MyDTO == nil) then
	return Stop("Can't Communicate with TIU");
	end;
print("Trip Odometer " .. MyDTO); 	
Sleep(10);

SetSpeed(RunningSpeed, 5, MyEngineNo, MyTIUNo);
SetCounter(C3, RunningSpeed)
	
--[[----------------------------------------------------]]
print(string.format("RunTime = %.2f seconds", RunTime()));
--
return true;
end
Function07Name, Function07Label = StartButton, "Start Run";
--[[---------------------------------------------------------------------------------------]]

function OccButton()
printc(clGreen, "Occupancy Flags and Direction ---------");
local x, FColor;
for x = FIRST_BLOCK, LAST_BLOCK do
	local flag = GetFlag(x);	-- Engine number (YY) and Direction (XX) : 0xXXYY
	if (flag == UNOCCUPIED) then
		FColor = clBlack;
		else
		FColor = clRed;
		end;
	printc(FColor, BlockName[x] .. "(" .. x .. ") = Engine #", (flag & 0x00FF), " Direction ", DirectionNames[(flag & 0xFF00) >> 8])
	end;	
return true;
end
Function08Name, Function08Label = OccButton, "Occupancy Flags";
--[[---------------------------------------------------------------------------------------]]
-- Reset
function ResetButton()

print("Reset");
-- stop the engine
SetSpeed(0, 0, MyEngineNo, MyTIUNo);
RFIDFlag = false;
Sleep(5);
--[[-----------------------------]]

--
-- start the engine
if (not StartedFlag) then
	print("Starting up the Engine");
	EngineStartUp(MyEngineNo, MyTIUNo);
	StartedFlag = true;
	Sleep(20);
	end

SetSpeed(RunningSpeed, 5, MyEngineNo, MyTIUNo);
SetCounter(C3, RunningSpeed);

return true
end;
Function12Name, Function12Label = ResetButton, "Reset";
--[[---------------------------------------------------------------------------------------]]

function StopButton()
	print("Stop");
	SetSpeed(0, 0, MyEngineNo, MyTIUNo);	
	return true;
end
Function09Name, Function09Label = StopButton, "Stop";
--[[---------------------------------------------------------------------------------------]]

function ResumeSpeedButton()
	print("Resume Speed " .. RunningSpeed .. " Smph");
	SetSpeed(RunningSpeed, 0, MyEngineNo, MyTIUNo);	
	return true;
end
Function10Name, Function10Label = ResumeSpeedButton, "Resume Speed";
--[[---------------------------------------------------------------------------------------]]

function ReLoadButton()
	print("Loading layout4.lua");
	package.loaded["layout4"] = nil;	-- unload
	Layout4 = require([[layout4]]);	-- All of the details about the Layout
	Layout4.SetRoute(0);	-- indicate default full route
	return true;
end
Function11Name, Function11Label = ReLoadButton, "Reload";
--[[---------------------------------------------------------------------------------------]]

function BeepOffButton()
	print("Beep Off");
	BeepOn = false;
	return true;
end
Function14Name, Function14Label = BeepOffButton, "Beep Off";
--[[---------------------------------------------------------------------------------------]]

function BeepOnButton()
	print("Beep On");
	BeepOn = true;
	return true;
end
Function15Name, Function15Label = BeepOnButton, "Beep On";
--[[---------------------------------------------------------------------------------------]]
--[[
function LLLButton()

	return true;
end
Function05Name, Function05Label = LLLButton, "5";
--]]
--[[---------------------------------------------------------------------------------------]]