--[[
---------------------------------------------------------------------------
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 = "Engineer On Board - Cutoff";
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 _;
--
--[[---------------------------------------------------------------------------------------]]

-- in settings.ini file
local MyEngineNo = 16;
local StartBlockNo = 1;
local MyDirection = WB;
local RunningSpeed = 25;
local EndBlockNo;
--
Title(title);
local OppDirection = EB;
local StopYoungstown = false; -- set to true to have Eastbound engine stop at the Youngstown station
local StopNewCastle = false; -- set to true to have Westbound engine stop at the New Castle station
--[[
Headway decision making allows the engine to move into the next block
if that block is unoccupied and the signal for that block shows a clear aspect.

This is different than the non-headway condition. Then the engine can only move
into the next block if that block and all blocks into the next siding
show a clear aspect.

In both Headway and normal operating, blocks must still be reserved up to the
next siding.

--]]
local Headway;	-- Set to true to enable Headway decision making
local Signals = false;	-- Set to true to incorporate absolute signals and stop/proceed signals into the decision making
--[[---------------------------------------------------------------------------------------]]
local TagCount = 0;
-- Counter Labels - all globals
Counter01Label = "tags()";
C1 = COUNTER01;
Counter03Label = "Block Speed Limit";
C3 = COUNTER03;
Counter04Label = "Target Distance";
C4 = COUNTER04;
Counter05Label = "Stopping Distance";
C5 = COUNTER05;
Counter06Label = "Stop Delay";
C6 = COUNTER06;

local Tags = {};		-- table to hold all of the tags detected in sequence
local LastSentSpeed = 0;

local CurrentBlockNo, LastBlockNo, NextBlockNo;
local StartedFlag = false;
local RFIDFlag = false;
local ResetinProgress = false;
--
-- Instrumented Layout 4 loaded by "require"
local Layout = nil;
local MyTIUNo;

local SoundOff = false;
local EngineSoundSave = nil;
local BeepOn = false;
local RunStarted = false;	-- flag to prevent double click on "Start Run" button

local BlockSpeedLimit = 0;	-- Smph
local LastBlockSpeedLimit = 0;	-- Smph
--local UnderSpeedPermitted = 10;	-- maximum speed deficit permitted over a tag
local ExpectedReader = 0;	-- check that we get the next expected tag reader
-- clear all tag handling functions
local Func30 = nil;
local Func51 = nil;
local Func10 = nil;
local Func41 = nil;
local Func21 = nil;
local Func20 = nil;
local Func90 = nil;
local Func40 = nil;
local Func50 = nil;
local Func60 = nil;
local Func61 = nil;
local Func100 = nil;
local Func101 = nil;
local Func110 = nil;
local Func150 = nil;
--
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 = "Debug";

--
--[[---------------------------------------------------------------------------------------]]
function createMainLine2Passing(flag)
	local WaitforCaboose = flag;
	local EngineStopped = false;
	local StopDelay = 0;
	local GotBlock = false;
	local StopTime = 0;
	local Target_Distance = 0;
	local MyCurrentBlockNo, MyLastBlockNo, MyNextBlockNo;
	local HomeSignal, DistantSignal;	
	local EngineDetectedTime = 0;
	local function MainLine2Passing(DetectorID, EngineNo, TagLocation, CarModel, Normal_Track, Reverse_Track, ThisSwitch)
		-- uses globals: MyEngineNo, LastBlockNo, CurrentBlockNo, NextBlockNo
		-- Apparently, ThisSwitch is no longer used anywhere
		if (EngineNo == MyEngineNo and ((TagLocation == TAG_ON_FRONT_TRUCK) or
			(not WaitforCaboose and (TagLocation == TAG_ON_REAR_TRUCK)))) then		-- if MyEngineNo in tag
			
--[[--- Engine Detected -----]]
			if (WaitforCaboose) then
				return EmergencyStop("ML2P : expected a caboose but got an engine");
				end;
--[[--- 1 Initialization -----]]			
			EngineDetectedTime = RunTime();	-- Engine detected time	
			print("ML2P : " .. DetectorID .. "Engine " .. MyEngineNo .. " detected");
			local Spd = GetSpeed(MyEngineNo, MyTIUNo);	-- get speed as engine covers tag
			--if (Spd ~= nil and (math.abs(Spd - BlockSpeedLimit) > UnderSpeedPermitted)) then
			--	print("ML2P : Speed over tag = " .. Spd .. " Smph - expected = " .. BlockSpeedLimit .. " Smph");
			--	end;			
			LastBlockNo = CurrentBlockNo;
			CurrentBlockNo = NextBlockNo;
			-- initialize block local variables
			EngineStopped = false;
			GotBlock = false;
--[[--- 2 Register the new block's home and distant signals -----]]	
			local Aspect, DistantAspect;			
			local NumLightsHome, NumLightsDistant;
			HomeSignal = NextSignal[MyDirection][CurrentBlockNo][HOMESIGNAL];	-- next signal number (0 if none)
			Aspect, NumLightsHome = GetAspect(HomeSignal);	-- 1, 2 or 3 lights on the next signal head (nil if none)
			DistantSignal = NextSignal[MyDirection][CurrentBlockNo][DISTANTSIGNAL];	-- distant signal number (0 if none)
			DistantAspect, NumLightsDistant = GetAspect(DistantSignal);	-- 1, 2 or 3 lights on the next signal head (nil if none)
			CreateCab(NumLightsHome, NumLightsDistant);	-- create light signals - (home, distant)			
			RegisterCabSignals(HomeSignal, DistantSignal);
--[[--- 3 Adjust speed for the current block -----]]			 
			LastBlockSpeedLimit = BlockSpeedLimit;
			BlockSpeedLimit = Layout.AdjustSpeedEx(Signals, MyDirection, CurrentBlockNo, Aspect);	-- adjust speed for some randomness in high speed blocks
			SetCounter(C3, BlockSpeedLimit);
			LastSentSpeed = SetSpeed(BlockSpeedLimit, 0, MyEngineNo, MyTIUNo);
			print(string.format("ML2P : %s : Speed %d Smph", BlockName[CurrentBlockNo], BlockSpeedLimit));			
			print ("ML2P : Home Signal in ", BlockName[CurrentBlockNo], " : " .. AspectNames[Aspect]);

--[[--- 4 check if home signal is showing permissive stop -----]]
			-- If it is, stop in this block. Set EngineStopped = true.
			-- NOTE: Mainline2Passing home signal can only be a permissive signal			
			if (Signals and (Aspect == AsStopandProceed)) then
				print("ML2P : Home Signal aspect is Stop and Proceed. Stop the engine");
				-- stop in CurrentBlockNo
				StopDelay, Target_Distance = StopBlockAfterDelay(Layout, true, CurrentBlockNo, MyEngineNo, MyDirection, LastBlockSpeedLimit, BlockSpeedLimit)
				EngineStopped = true;
				GotBlock = false;
				StopTime = RunTime();				
				end;
			if (Aspect == AsStopSignal) then
				return EmergencyStop("ML2P : Home Signal aspect is an unexpected absolute signal.");
				end;

--[[--- 5 ASAP, set the switch at entrance to passing/station track -----]]
			-- N/A
--[[--- 6 set next block number -----]]
			if (MyDirection == SuperiorDirection) then
				NextBlockNo = Normal_Track;		-- Westbound trains are superior, take the through track
				else
				NextBlockNo = Reverse_Track;	-- Eastbound trains are inferior, take the passing/station track
				end;
--[[--- 7 If we didn't stop because of a signal aspect, then attempt to obtain occupancy lock -----]]				
			if (EngineStopped == false) then
-- TBD
--[[--- 8 stop the engine if this is the end block -----]]
				if (CurrentBlockNo == EndBlockNo) then
					-- stop in CurrentBlockNo/EndBlockNo
					StopDelay, Target_Distance = StopBlockAfterDelay(Layout, true, CurrentBlockNo, MyEngineNo, MyDirection, LastBlockSpeedLimit, BlockSpeedLimit)
					EngineStopped = true;	-- engine was stopped
					GotBlock = false;
					StopTime = RunTime();				
-- TBD
--[[--- 9 check for occupancy -----]]			
					-- check for occupancy of thru track or passing/station track
					-- choose based on superiority.
					-- If we can't get a lock, schedule a stop once the entire train is in the block.
					elseif (MyDirection == SuperiorDirection) then
						if (TrySetOccupancy(Normal_Track, MyEngineNo | (MyDirection << 8))) then	-- Thru track
							-- Got a lock on the next block, see if a stop is requested
							if (StopYoungstown and (CurrentBlockNo == YOUNGSTOWN_BLOCK)) then 
								-- if requested to stop, then do so
								StopDelay, Target_Distance = StopBlockAfterDelay(Layout, true, CurrentBlockNo, MyEngineNo, MyDirection, LastBlockSpeedLimit,BlockSpeedLimit)
								EngineStopped = true;
								GotBlock = false;
								StopTime = RunTime();
								print("ML2P : Stopped at " .. BlockName[CurrentblockNo] .. " for 3 minutes");
								Sleep(3);	-- 3 minutes (180 seconds)
								-- after waiting, continue on
								StopYoungstown = false;
								StopDelay = 0;
								EngineStopped = false;
								GotBlock = true;
								StopTime = 0;
								else
								-- Got a lock on the next block, continue on
								StopDelay = 0;
								EngineStopped = false;
								GotBlock = true;
								StopTime = 0;
								end 
							else
--[[--- 10 if we can't get a lock, stop in CurrentBlockNo -----]]							
							StopDelay, Target_Distance = StopBlockAfterDelay(Layout, true, CurrentBlockNo, MyEngineNo, MyDirection, LastBlockSpeedLimit,BlockSpeedLimit)
							EngineStopped = true;
							GotBlock = false;
							StopTime = RunTime();
							end
						else	-- MyDirection ~= SuperiorDirection
						if (TrySetOccupancy(Reverse_Track, MyEngineNo | (MyDirection << 8))) then	-- passing/station track
							-- got a lock on the next block, see if a stop is requested
							if (StopNewCastle and (CurrentBlockNo == NEW_CASTLE_JUNCTION_BLOCK)) then 
								-- if requested to stop, then do so
								StopDelay, Target_Distance = StopBlockAfterDelay(Layout, true, CurrentBlockNo, MyEngineNo, MyDirection, LastBlockSpeedLimit,BlockSpeedLimit)
								EngineStopped = true;
								GotBlock = false;
								StopTime = RunTime();
								print("ML2P : Stopped at " .. BlockName[CurrentblockNo] .. " for 3 minutes");
								Sleep(3);	-- 3 minutes (180 seconds)
								-- after waiting, continue on
								StopYoungstown = false;
								StopDelay = 0;
								EngineStopped = false;
								GotBlock = true;
								StopTime = 0;
								else
								-- Got a lock on the next block, continue on
								StopDelay = 0;
								EngineStopped = false;
								GotBlock = true;
								StopTime = 0;
								end 
							else
--[[--- 10a if we can't get a lock, stop in CurrentBlockNo -----]]
							StopDelay, Target_Distance = StopBlockAfterDelay(Layout, true, CurrentBlockNo, MyEngineNo, MyDirection, LastBlockSpeedLimit,BlockSpeedLimit)
							EngineStopped = true;
							GotBlock = false;
							StopTime = RunTime();
							end;
						end;
					end;
--[[--- 11 set NextSwitch switch -----]]
			-- N/A
--[[--- 12 Cleanup -----]]				
			WaitforCaboose = true;
			-- save current state for the Caboose detection call of this function
			DisplayBlockNames("ML2P : ", LastBlockNo, CurrentBlockNo, NextBlockNo);
			MyCurrentBlockNo = CurrentBlockNo;
			MyLastBlockNo = LastBlockNo;
			MyNextBlockNo = NextBlockNo;			
			return true;
			
--[[--- Caboose Detected -----]]

		elseif (EngineNo == 0 and (CarModel == CABOOSE or CarModel == OBSERVATION) and WaitforCaboose) then
			-- in the Caboose block, use the local copy of CurrentBlockNo and LastBlockNo
			print("ML2P : " .. DetectorID .. "End of Train after " .. RunTime() - EngineDetectedTime .. " seconds (Stop Delay = " .. StopDelay .. ")");

			--
			-- ASAP Once the caboose is detected (without any clearing delay) return switch at entrance of passing/station track back
			-- to NORMAL.
--[[--- 1 set ThisSwitch switch but this is apparently not used anywhere anymore -----]]
			if (ThisSwitch ~= nil) then
				Switch(NORMAL, Switches[ThisSwitch][CHAN_NUMBER], Switches[ThisSwitch][AIU_NUMBER], Switches[ThisSwitch][TIU_NUMBER], 0);
				print("ML2P : " .. SwitchName[ThisSwitch] .. " to NORMAL");
				end;
			--print("MyCurrentBlockNo " .. MyCurrentBlockNo .. " MyDirection " .. MyDirection);
			--print("Opposite Direction " .. OppDirection);
			--print("Detector = " .. DETECTOR .. " Reader = " .. READER);		
			--print(" Detector = " .. Layout.STOPBlock(MyCurrentBlockNo, OppDirection, DETECTOR));
			--print(" Reader = " ..   Layout.STOPBlock(MyCurrentBlockNo, OppDirection, READER));
			ExpectedReader = Layout.STOPBlock(MyCurrentBlockNo, OppDirection, DETECTOR) * 10 + Layout.STOPBlock(MyCurrentBlockNo, OppDirection, READER);
			print("ML2P : Expected Reader Changed to " .. ExpectedReader);
--[[--- 1a clear occupancy -----]]
			if (MyLastBlockNo ~= 0) then
				ClearOccupancy(MyLastBlockNo, MyEngineNo | (MyDirection << 8));	-- clear LastBlockNo occupancy
				end;
			writeSettings(false);	-- save state without comment
-- TBD
			if (MyCurrentBlockNo ~= EndBlockNo) then	-- EndBlockNo is always zero for loop configuration
--[[--- 2 If we are not in EndBlockNo, then wait for block(s) then restart engine if it was stopped -----]]
-- TBD				
				if (not GotBlock) then
--[[--- 3 wait for MyNextBlockNo to be locked (either Normal_Track or Reverse_Track) (default wait 30 minutes) -----]]				
					local result;
					-- set occupancy flag in MyNextBlockNo
					if (MyNextBlockNo == Normal_Track) then
						result, MyNextBlockNo = SingleSetOccupancy({Normal_Track}, MyEngineNo | (MyDirection << 8));
						if (not result) then
							return EmergencyStop("ML2P : Timed out waiting for block: " .. BlockName[Normal_Track]);
							end;
						end;
					if (MyNextBlockNo == Reverse_Track) then
						result, MyNextBlockNo = SingleSetOccupancy({Reverse_Track}, MyEngineNo | (MyDirection << 8));
						if (not result) then
							return EmergencyStop("ML2P : Timed out waiting for block: " .. BlockName[Reverse_Track]);
							end;
						end;
					GotBlock = true;	-- indicate needed Block is locked
					end;
					
--[[--- 4 start moving if stopped -----]]
				if (EngineStopped) then
					-- Check if Stop and Proceed aspect is showing
					local Aspect = GetCabAspect(HOMESIGNAL);
					print ("ML2P : Home Signal in ", BlockName[MyCurrentBlockNo], " : " .. AspectNames[Aspect]);				

--[[--- 5 if we are stopped in this block due to signal aspect, wait 10 seconds before restarting -----]]
					-- NOTE: Mainline2Passing can only be permissive signal
					if (Signals and (Aspect == AsStopandProceed)) then
						Sleep(10);
						end;

					-- calcuate Time delay for start
					local UsedUpTime = RunTime() - StopTime;	-- never negative
					local StartDelay;
					if (UsedUpTime > StopDelay) then	-- if StopDelay == 0 then always true
						StartDelay = 5;
						else
						StartDelay = StopDelay - UsedUpTime + 5;	-- UsedUpTime always <= StopDelay
						end;			
					--print("ML2P : Used Up Time ", UsedUpTime, " Stop Delay ", StopDelay, " Start Delay ", StartDelay);
					-- start the engine
					LastBlockSpeedLimit = BlockSpeedLimit;
					LastSentSpeed = SetSpeed(BlockSpeedLimit, StartDelay, MyEngineNo, MyTIUNo);
					print(string.format("ML2P : %s : Speed %d Smph = Start Delay %.3f seconds",
						BlockName[MyCurrentBlockNo], BlockSpeedLimit, StartDelay));		
					EngineStopped = false;	-- indicate that we handled the stop here
					end;
-- TBD
				else
				print("ML2P : Engine Stopped in ".. BlockName[MyCurrentBlockNo]);
				RFIDFlag = false;
				end;
-- TBD				
			WaitforCaboose = false;
			end;
	return true;
	end;

return MainLine2Passing;
end;
--[[---------------------------------------------------------------------------------------]]

function createMainLine2MainLine(flag, SwDistance)
	local WaitforCaboose = flag;
	local SwitchClearingDistance = SwDistance or 0;
	local EngineStopped = false;
	local StopDelay = 0
	local GotBlock = false;
	local StopTime = 0;
	local Target_Distance = 0;
	local MyCurrentBlockNo, MyLastBlockNo, MyNextBlockNo;
	local HomeSignal, DistantSignal;	
	local EngineDetectedTime = 0;
	local function MainLine2MainLine(DetectorID, EngineNo, TagLocation, CarModel, EngineNormal, EngineReverse, CabooseNormal, CabooseReverse, Block)
		-- uses globals: MyEngineNo, LastBlockNo, CurrentBlockNo, NextBlockNo
		-- ThisSwitch may be nil if no switch is present
		if (type(Block) ~= "table") then
			return EmergencyStop("ML2ML Block not a table");
			end
		if (type(EngineNormal) ~= "table") then
			return EmergencyStop("ML2ML EngineNormal not a table");
			end
		if (type(EngineReverse) ~= "table") then
			return EmergencyStop("ML2ML EngineReverse not a table");
			end
		if (type(CabooseNormal) ~= "table") then
			return EmergencyStop("ML2ML CabooseNormal not a table");
			end
		if (type(CabooseReverse) ~= "table") then
			return EmergencyStop("ML2ML CabooseReverse not a table");
			end			
		if (EngineNo == MyEngineNo and ((TagLocation == TAG_ON_FRONT_TRUCK) or
			(not WaitforCaboose and (TagLocation == TAG_ON_REAR_TRUCK)))) then		-- if MyEngineNo in tag
			
--[[--- Engine Detected -----]]
			if (WaitforCaboose) then
				return EmergencyStop("ML2ML : expected a caboose but got an engine");
				end;	
--[[--- 1 Initialization -----]]				
			EngineDetectedTime = RunTime();	-- Engine detected time	
			print("ML2ML " .. DetectorID .. "Engine " .. MyEngineNo .. " detected");
			local Spd = GetSpeed(MyEngineNo, MyTIUNo);	-- get speed as engine covers tag
			--if (Spd ~= nil and (math.abs(Spd - BlockSpeedLimit) > UnderSpeedPermitted)) then
			--	print("ML2ML : Speed over tag = " .. Spd .. " Smph - expected = " .. BlockSpeedLimit .. " Smph");
			--	end;
			LastBlockNo = CurrentBlockNo;
			CurrentBlockNo = NextBlockNo;
			-- initialize block local variables
			EngineStopped = false;
			GotBlock = false;
--[[--- 2 Register the new block's home and distant signals -----]]	
			local Aspect, DistantAspect;			
			local NumLightsHome, NumLightsDistant;
			HomeSignal = NextSignal[MyDirection][CurrentBlockNo][HOMESIGNAL];	-- next signal number (0 if none)
			Aspect, NumLightsHome = GetAspect(HomeSignal);	-- 1, 2 or 3 lights on the next signal head (nil if none)
			DistantSignal = NextSignal[MyDirection][CurrentBlockNo][DISTANTSIGNAL];	-- distant signal number (0 if none)
			DistantAspect, NumLightsDistant = GetAspect(DistantSignal);	-- 1, 2 or 3 lights on the next signal head (nil if none)
			CreateCab(NumLightsHome, NumLightsDistant);	-- create light signals - (home, distant)			
			RegisterCabSignals(HomeSignal, DistantSignal);
--[[--- 3 Adjust speed for the current block -----]]
			LastBlockSpeedLimit = BlockSpeedLimit;
			BlockSpeedLimit = Layout.AdjustSpeedEx(Signals, MyDirection, CurrentBlockNo, Aspect);	-- adjust speed for some randomness in high speed blocks
			SetCounter(C3, BlockSpeedLimit);
			LastSentSpeed = SetSpeed(BlockSpeedLimit, 0, MyEngineNo, MyTIUNo);
			print(string.format("ML2ML : %s : Speed %d Smph", BlockName[CurrentBlockNo], BlockSpeedLimit));
			print ("ML2ML : Home Signal in ", BlockName[CurrentBlockNo], " : " .. AspectNames[Aspect]);

--[[--- 4 check if home signal is showing permissive stop -----]]
			-- If it is, stop in this block. Set EngineStopped = true.
			-- NOTE: MainLine2MainLine can only be permissive signal
			if (Signals and (Aspect == AsStopandProceed)) then
				print("ML2ML : Home Signal aspect is Stop and Proceed. Stop the engine");
				-- stop in CurrentBlockNo
				StopDelay, Target_Distance = StopBlockAfterDelay(Layout, true, CurrentBlockNo, MyEngineNo, MyDirection, LastBlockSpeedLimit, BlockSpeedLimit)
				EngineStopped = true;
				GotBlock = false;
				StopTime = RunTime();				
				end;
			--[[ can't test this since this absolute signal is shared between two routes
			if (Aspect == AsStopSignal) then
				return EmergencyStop("ML2ML : Home Signal is an unexpected absolute signal.");
				end;
			--]]
--[[--- 5 ASAP, set the switch(es) at engine detection -----]]
			for k, ThisSwitch in ipairs(EngineNormal) do
				Switch(NORMAL, Switches[ThisSwitch][CHAN_NUMBER], Switches[ThisSwitch][AIU_NUMBER], Switches[ThisSwitch][TIU_NUMBER], 0);
				print("ML2ML : " .. SwitchName[ThisSwitch] .. " to NORMAL on Engine detection");
				end;
				
			for k, ThisSwitch in ipairs(EngineReverse) do
				Switch(REVERSE, Switches[ThisSwitch][CHAN_NUMBER], Switches[ThisSwitch][AIU_NUMBER], Switches[ThisSwitch][TIU_NUMBER], 0);
				print("ML2ML : " .. SwitchName[ThisSwitch] .. " to REVERSE on Engine detection");
				end;

--[[--- 6 set next block number -----]]
			NextBlockNo = Block[1];	-- set NextBlockNo to Block[1]
--[[--- 7 If we didn't stop because of a signal aspect, then attempt to obtain occupancy lock on one/all blocks depending on Headway -----]]	
			if (EngineStopped == false) then	
-- TBD
--[[--- 8 stop the engine if this is the end block -----]]
				if (CurrentBlockNo == EndBlockNo) then
					-- stop in CurrentBlockNo/EndBlockNo
					StopDelay, Target_Distance = StopBlockAfterDelay(Layout, true, CurrentBlockNo, MyEngineNo, MyDirection, LastBlockSpeedLimit, BlockSpeedLimit)
					EngineStopped = true;	-- engine was stopped
					GotBlock = false;
					StopTime = RunTime();				
-- TBD
--[[--- 9 check for occupancy -----]]	
					-- no engine entrance switch settings
					-- moving into a block requires the block to be empty (== 0) or
					-- be already reserved for this engine (== EnginNo)
					elseif (not TryMultiSetOccupancy(Headway and {Block[1]} or Block, MyEngineNo | (MyDirection << 8))) then	-- set occupancy flag in one/all blocks	
--[[--- 10 if we can't get a lock, stop in CurrentBlockNo -----]]
						StopDelay, Target_Distance = StopBlockAfterDelay(Layout, true, CurrentBlockNo, MyEngineNo, MyDirection, LastBlockSpeedLimit, BlockSpeedLimit)
						EngineStopped = true;	-- engine was stopped
						GotBlock = false;
						StopTime = RunTime();
						else
						-- got a lock on the needed block(s), continue on
						StopDelay = 0;
						EngineStopped = false;	-- engine was not stopped
						GotBlock = true;
						StopTime = 0;
						end;
					end;
--[[--- 11 set NextSwitch switch -----]]
			if (GotBlock) then
			
				end;
--[[--- 12 Cleanup -----]]		
			WaitforCaboose = true;
			-- save current state for the Caboose detection call of this function
			DisplayBlockNames("ML2ML : ", LastBlockNo, CurrentBlockNo, NextBlockNo);
			MyCurrentBlockNo = CurrentBlockNo;
			MyLastBlockNo = LastBlockNo;
			MyNextBlockNo = NextBlockNo;
			return true;
			
--[[--- Caboose Detected -----]]

		elseif (EngineNo == 0 and (CarModel == CABOOSE or CarModel == OBSERVATION) and WaitforCaboose) then
			-- in the Caboose block, use the local copy of CurrentBlockNo and LastBlockNo
			print("ML2ML : " .. DetectorID .. "End of Train after " .. RunTime() - EngineDetectedTime .. " seconds (Stop Delay = " .. StopDelay .. ")");
			if (SwitchClearingDistance > 0) then
				-- equation for Delay calculation
				-- time(seconds) = d(in) * 48 Smiles/mile * 3600 sec/hr * / (12 in/ft * 5280 ft/mile * X Smiles/hr)
				local SleepDelay =  (SwitchClearingDistance) * (48 / (12 * 5280 * BlockSpeedLimit)) * 3600;
				print(string.format("P2ML : %s : Switch Clearing Distance = %.3f in - Speed %d Smph = Sleep Delay %.3f seconds",
					BlockName[MyCurrentBlockNo], SwitchClearingDistance, BlockSpeedLimit, SleepDelay));
				Sleep(SleepDelay);	-- give the caboose time to clear the switch
				print("ML2ML : " .. "Switch cleared after " .. SleepDelay .. " seconds")
				end;
			--
			-- Once the caboose is detected (with any clearing delay) return switch at entrance of current block back
			-- to NORMAL - this switch was set as result of the engine leaving the last block
--[[--- 1 set the switch(es) at caboose detection -----]]
			for k, LastSwitch in ipairs(CabooseNormal) do
				Switch(NORMAL, Switches[LastSwitch][CHAN_NUMBER], Switches[LastSwitch][AIU_NUMBER], Switches[LastSwitch][TIU_NUMBER], 0);
				print("ML2ML : " .. SwitchName[LastSwitch] .. " to NORMAL on Caboose detection");
				end;

			-- debug
			--print("MyCurrentBlockNo " .. MyCurrentBlockNo .. " MyDirection " .. MyDirection);
			--print("Opposite Direction " .. OppDirection);
			--print("Detector = " .. DETECTOR .. " Reader = " .. READER);		
			--print(" Detector = " .. Layout.STOPBlock(MyCurrentBlockNo, OppDirection, DETECTOR));
			--print(" Reader = " ..   Layout.STOPBlock(MyCurrentBlockNo, OppDirection, READER));
			ExpectedReader = Layout.STOPBlock(MyCurrentBlockNo, OppDirection, DETECTOR) * 10 + Layout.STOPBlock(MyCurrentBlockNo, OppDirection, READER);
			print("ML2ML : Expected Reader Changed to " .. ExpectedReader);			
			--				
			if (MyLastBlockNo ~= 0) then
				ClearOccupancy(MyLastBlockNo, MyEngineNo | (MyDirection << 8));	-- clear LastBlockNo occupancy				
				end;
			writeSettings(false);	-- save state without comment
				
-- TBD
			if (MyCurrentBlockNo ~= EndBlockNo) then	-- EndBlockNo is always zero for loop configuration
--[[--- 2 If we are not in EndBlockNo, then wait for block(s) then restart engine if it was stopped -----]]
 
-- TBD
				if (not GotBlock) then
--[[--- 3 wait for one/all blocks to be locked depending on Headway (default wait 30 minutes) -----]]				
					if (not MultiSetOccupancy(Headway and {Block[1]} or Block, MyEngineNo | (MyDirection << 8))) then	-- set occupancy flag in one/all blocks			
						return EmergencyStop("ML2ML : Timed out waiting for " .. BlockName[MyNextBlockNo]);
						end;
					GotBlock = true;
					end;
--[[--- 3.5 set next switch after occupancy lock -----]]	
				if (GotBlock) then			
					for k, NextSwitch in ipairs(CabooseReverse) do
						Switch(REVERSE, Switches[NextSwitch][CHAN_NUMBER], Switches[NextSwitch][AIU_NUMBER], Switches[NextSwitch][TIU_NUMBER], 0);
						print("ML2ML : " .. SwitchName[NextSwitch] .. " to REVERSE on Caboose detection");
						end;					
					end;

--[[--- 4 start moving if stopped -----]]					
				if (EngineStopped) then
					-- Check if Stop and Proceed aspect is showing
					local Aspect = GetCabAspect(HOMESIGNAL);
					print ("ML2ML : Home Signal in ", BlockName[MyCurrentBlockNo], " : " .. AspectNames[Aspect]);

--[[--- 5 if we are stopped in this block due to signal aspect, wait 10 seconds before restarting -----]]
					-- NOTE: MainLine2MainLine can only be permissive signal
					if (Signals and (Aspect == AsStopandProceed)) then
						Sleep(10);
						end;

					-- calcuate Time delay for start
					local UsedUpTime = RunTime() - StopTime;	-- never negative
					local StartDelay;
					if (UsedUpTime > StopDelay) then	-- if StopDelay == 0 then always true
						StartDelay = 5;
						else
						StartDelay = StopDelay - UsedUpTime + 5;	-- UsedUpTime always <= StopDelay
						end;				
					--print("ML2ML : Used Up Time ", UsedUpTime, " Stop Delay ", StopDelay, " Start Delay ", StartDelay);
					-- start the engine
					LastBlockSpeedLimit = BlockSpeedLimit;
					LastSentSpeed = SetSpeed(BlockSpeedLimit, StartDelay, MyEngineNo, MyTIUNo);
					print(string.format("ML2ML : %s : Speed %d Smph = Start Delay %.3f seconds",
						BlockName[MyCurrentBlockNo], BlockSpeedLimit, StartDelay));
					EngineStopped = false;	-- indicate that we handled the stop here
					end;
-- TBD
				else
				print("ML2ML : Engine Stopped in ".. BlockName[MyCurrentBlockNo]);
				RFIDFlag = false;
				end;
-- TBD				
			WaitforCaboose = false;
			end;
	return true;
	end;

return MainLine2MainLine;
end;
--[[---------------------------------------------------------------------------------------]]

function createPassing2MainLine(flag, SwDistance)
	local WaitforCaboose = flag;
	local SwitchClearingDistance = SwDistance or 0;
	local EngineStopped = false;
	local StopDelay = 0;
	local GotBlock = false;
	local StopTime = 0;
	local Target_Distance = 0;
	local MyCurrentBlockNo, MyLastBlockNo, MyNextBlockNo;	
	local HomeSignal, DistantSignal;		
	local EngineDetectedTime = 0;	
	local function Passing2MainLine(DetectorID, EngineNo, TagLocation, CarModel, Normal_Track, Reverse_Track, ThisSwitch, NextSwitch, Block)
		-- uses globals: MyEngineNo, LastBlockNo, CurrentBlockNo, NextBlockNo
		if (type(Block) ~= "table") then
			return EmergencyStop("P2ML : Block not a table");
			end
		if (EngineNo == MyEngineNo and ((TagLocation == TAG_ON_FRONT_TRUCK) or
			(not WaitforCaboose and (TagLocation == TAG_ON_REAR_TRUCK)))) then		-- if MyEngineNo in tag
			
--[[--- Engine Detected -----]]
			if (WaitforCaboose) then
				return EmergencyStop("P2ML : expected a caboose but got an engine");
				end;
--[[--- 1 Initialization -----]]				
			EngineDetectedTime = RunTime();	-- Engine detected time	
			print("P2ML : " .. DetectorID .. "Engine " .. MyEngineNo .. " detected");
			local Spd = GetSpeed(MyEngineNo, MyTIUNo);	-- get speed as engine covers tag
			--if (Spd ~= nil and (math.abs(Spd - BlockSpeedLimit) > UnderSpeedPermitted)) then
			--	print("P2ML : Speed over tag = " .. Spd .. " Smph - expected = " .. BlockSpeedLimit .. " Smph");
			--	end;
			LastBlockNo = CurrentBlockNo;
			CurrentBlockNo = NextBlockNo;
			-- initialize block local variables
			EngineStopped = false;
			GotBlock = false;
--[[--- 2 Register the new block's home and distant signals -----]]
			local Aspect, DistantAspect;			
			local NumLightsHome, NumLightsDistant;
			HomeSignal = NextSignal[MyDirection][CurrentBlockNo][HOMESIGNAL];	-- next signal number (0 if none)
			Aspect, NumLightsHome = GetAspect(HomeSignal);	-- 1, 2 or 3 lights on the next signal head (nil if none)
			DistantSignal = NextSignal[MyDirection][CurrentBlockNo][DISTANTSIGNAL];	-- distant signal number (0 if none)
			DistantAspect, NumLightsDistant = GetAspect(DistantSignal);	-- 1, 2 or 3 lights on the next signal head (nil if none)
			CreateCab(NumLightsHome, NumLightsDistant);	-- create light signals - (home, distant)			
			RegisterCabSignals(HomeSignal, DistantSignal);
--[[--- 3 Adjust speed for the current block -----]]
			LastBlockSpeedLimit = BlockSpeedLimit;
			BlockSpeedLimit = Layout.AdjustSpeedEx(Signals, MyDirection, CurrentBlockNo, Aspect);	-- adjust speed for some randomness in high speed blocks
			SetCounter(C3, BlockSpeedLimit);
			LastSentSpeed = SetSpeed(BlockSpeedLimit, 0, MyEngineNo, MyTIUNo);
			print(string.format("P2ML : %s : Speed %d Smph", BlockName[CurrentBlockNo], BlockSpeedLimit));
			print ("P2ML : Home Signal in ", BlockName[CurrentBlockNo], " : " .. AspectNames[Aspect]);

--[[--- 4 check if home signal is showing absolute stop -----]]
			-- If it is, stop in this block. Set EngineStopped = true.
			-- NOTE: Passing2MainLine is the only case where an absolute signal can and must appear
			if (Signals and (Aspect == AsStopSignal)) then
				print("P2ML : Home Signal aspect is Stop Signal. Stop the engine");
				-- stop in CurrentBlockNo
				StopDelay, Target_Distance = StopBlockAfterDelay(Layout, true, CurrentBlockNo, MyEngineNo, MyDirection, LastBlockSpeedLimit, BlockSpeedLimit)
				EngineStopped = true;
				GotBlock = false;
				StopTime = RunTime();				
				end;
			if (Aspect == AsStopandProceed) then
				return EmergencyStop("P2ML : Home Signal aspect is an unexpected permissive signal.");
				end;				

--[[--- 5 ASAP, set the switch at entrance to passing/station track -----]]
			if (ThisSwitch ~= nil) then
				if (CurrentBlockNo == Normal_Track) then
					Switch(NORMAL, Switches[ThisSwitch][CHAN_NUMBER], Switches[ThisSwitch][AIU_NUMBER], Switches[ThisSwitch][TIU_NUMBER], 0);
					print("P2ML : " .. SwitchName[ThisSwitch] .. " to NORMAL");
					else
					Switch(REVERSE, Switches[ThisSwitch][CHAN_NUMBER], Switches[ThisSwitch][AIU_NUMBER], Switches[ThisSwitch][TIU_NUMBER], 0);
					print("P2ML : " .. SwitchName[ThisSwitch] .. " to REVERSE");
					end;
				end;
--[[--- 6 set next block number -----]]				
			NextBlockNo = Block[1];	-- set NextBlockNo to Block[1]
--[[--- 7 If we didn't stop because of a signal aspect, then attempt to obtain occupancy lock in one/all blocks depending on Headway -----]]			
			if (EngineStopped == false) then
-- TBD
--[[--- 8 stop the engine if this is the end block -----]]
				if (CurrentBlockNo == EndBlockNo) then
					-- stop in CurrentBlockNo/EndBlockNo
					StopDelay, Target_Distance = StopBlockAfterDelay(Layout, true, CurrentBlockNo, MyEngineNo, MyDirection, LastBlockSpeedLimit, BlockSpeedLimit)
					EngineStopped = true;	-- engine was stopped
					GotBlock = false;
					StopTime = RunTime();				
-- TBD
--[[--- 9 check for occupancy -----]]
					-- moving into a block requires the block to be empty (== 0) or
					-- be already reserved for this engine (== MyEngineNo)
					elseif (not TryMultiSetOccupancy(Headway and {Block[1]} or Block, MyEngineNo | (MyDirection << 8))) then 	-- set occupancy flag in one/all blocks	
--[[--- 10 if we can't get a lock, stop in CurrentBlockNo -----]]							
						StopDelay, Target_Distance = StopBlockAfterDelay(Layout, true, CurrentBlockNo, MyEngineNo, MyDirection, LastBlockSpeedLimit, BlockSpeedLimit)
						EngineStopped = true;	-- engine was stopped
						GotBlock = false;
						StopTime = RunTime();
						else
						-- got a lock on the needed block(s), continue on
						StopDelay = 0;
						EngineStopped = false;	-- engine was not stopped
						GotBlock = true;
						StopTime = 0;
						end;
					end;
					
			if ((NextSwitch ~= nil) and GotBlock) then
--[[--- 11 set NextSwitch switch -----]]
				if (CurrentBlockNo == Normal_Track) then
					Switch(NORMAL, Switches[NextSwitch][CHAN_NUMBER], Switches[NextSwitch][AIU_NUMBER], Switches[NextSwitch][TIU_NUMBER], 0);
					print("P2ML : Next Switch " .. SwitchName[NextSwitch] .. " to NORMAL");
				elseif (CurrentBlockNo == Reverse_Track) then
					Switch(REVERSE, Switches[NextSwitch][CHAN_NUMBER], Switches[NextSwitch][AIU_NUMBER], Switches[NextSwitch][TIU_NUMBER], 0);
					print("P2ML : NextSwitch " .. SwitchName[NextSwitch] .. " to REVERSE");
					end;
				end;
--[[--- 12 Cleanup -----]]				
			WaitforCaboose = true;
			-- save current state for the Caboose detection call of this function
			DisplayBlockNames("P2ML : ", LastBlockNo, CurrentBlockNo, NextBlockNo);
			MyCurrentBlockNo = CurrentBlockNo;
			MyLastBlockNo = LastBlockNo;
			MyNextBlockNo = NextBlockNo;			
			return true;
			
--[[--- Caboose Detected -----]]

		elseif (EngineNo == 0 and (CarModel == CABOOSE or CarModel == OBSERVATION) and WaitforCaboose) then
			-- in the elseif block, use the local copy of CurrentBlockNo and LastBlockNo
			print("P2ML : " .. DetectorID .. "End of Train after " .. RunTime() - EngineDetectedTime .. " seconds (Stop Delay = " .. StopDelay .. ")");
			if (SwitchClearingDistance > 0) then
				-- equation for Delay calculation
				-- time(seconds) = d(in) * 48 Smiles/mile * 3600 sec/hr * / (12 in/ft * 5280 ft/mile * X Smiles/hr)
				local SleepDelay =  (SwitchClearingDistance) * (48 / (12 * 5280 * BlockSpeedLimit)) * 3600;
				print(string.format("P2ML : %s : Switch Clearing Distance = %.3f in - Speed %d Smph = Sleep Delay %.3f seconds",
					BlockName[MyCurrentBlockNo], SwitchClearingDistance, BlockSpeedLimit, SleepDelay));
				Sleep(SleepDelay);	-- give the caboose time to clear the switch
				print("P2ML : " .. "Switch cleared after " .. SleepDelay .. " seconds")
				end;
			--
			-- Once we reach the switch clearing distance, return switch at entrance to passing/station track back
			-- to NORMAL - this switch was set as result of the engine entering the block
--[[--- 1 set ThisSwitch switch -----]]
			if (ThisSwitch ~= nil) then
				Switch(NORMAL, Switches[ThisSwitch][CHAN_NUMBER], Switches[ThisSwitch][AIU_NUMBER], Switches[ThisSwitch][TIU_NUMBER], 0);
				print("P2ML : " .. SwitchName[ThisSwitch] .. " to NORMAL");
				end;
			--print("MyCurrentBlockNo " .. MyCurrentBlockNo .. " MyDirection " .. MyDirection);
			--print("Opposite Direction " .. OppDirection);
			--print("Detector = " .. DETECTOR .. " Reader = " .. READER);		
			--print(" Detector = " .. Layout.STOPBlock(MyCurrentBlockNo, OppDirection, DETECTOR));
			--print(" Reader = " ..   Layout.STOPBlock(MyCurrentBlockNo, OppDirection, READER));
			ExpectedReader = Layout.STOPBlock(MyCurrentBlockNo, OppDirection, DETECTOR) * 10 + Layout.STOPBlock(MyCurrentBlockNo, OppDirection, READER);
			print("P2ML : Expected Reader Changed to " .. ExpectedReader);
			--				
			if (MyLastBlockNo ~= 0) then			
				ClearOccupancy(MyLastBlockNo, MyEngineNo | (MyDirection << 8));	-- clear LastBlockNo occupancy	
				end;
			writeSettings(false);	-- save state without comment  
-- TBD
			if (MyCurrentBlockNo ~= EndBlockNo) then	-- EndBlockNo is always zero for loop configuration
--[[--- 2 If we are not in EndBlockNo, then wait for block(s) then restart engine if it was stopped -----]]
-- TBD
				if (not GotBlock) then
--[[--- 3 wait for one/all blocks to be locked depending on Headway (default wait 30 minutes) -----]]				
					if (not MultiSetOccupancy(Headway and {Block[1]} or Block, MyEngineNo | (MyDirection << 8))) then	-- set occupancy flag in one/all blocks			
						return EmergencyStop("P2ML : Timed out waiting for " .. BlockName[MyNextBlockNo]);
						end;
					GotBlock = true;	-- indicate one/all Blocks are locked
					-- set next switch (passing/station track exit) based on current block
					if (NextSwitch ~= nil and GotBlock) then
						-- set NextSwitch switch
						if (MyCurrentBlockNo == Normal_Track) then
							Switch(NORMAL, Switches[NextSwitch][CHAN_NUMBER], Switches[NextSwitch][AIU_NUMBER], Switches[NextSwitch][TIU_NUMBER], 0);
							print("P2ML : Next Switch " .. SwitchName[NextSwitch] .. " to NORMAL");
							end;
						if (MyCurrentBlockNo == Reverse_Track) then
							Switch(REVERSE, Switches[NextSwitch][CHAN_NUMBER], Switches[NextSwitch][AIU_NUMBER], Switches[NextSwitch][TIU_NUMBER], 0);
							print("P2ML : Next Switch " .. SwitchName[NextSwitch] .. " to REVERSE");
							end;			
						end;
					end;
--[[---4 start moving if stopped -----]]
				if (EngineStopped) then
					-- start moving if stopped 
					local Aspect = GetCabAspect(HOMESIGNAL);
					print ("P2ML : Home Signal in ", BlockName[MyCurrentBlockNo], " : " .. AspectNames[Aspect]);

--[[--- 5 if we are stopped in this block due to signal aspect, wait for it to clear -----]]
					-- NOTE: Passing2Mainline is the only case where an absolute signal can and must appear
					if (Signals and Aspect == AsStopSignal) then
						print("P2ML : Stop Signal");
						WaitforStopSignaltoClear();		
						end;

					-- calcuate Time delay for start
					local UsedUpTime = RunTime() - StopTime;	-- never negative
					local StartDelay;
					if (UsedUpTime > StopDelay) then	-- if StopDelay == 0 then always true
						StartDelay = 5;
						else
						StartDelay = StopDelay - UsedUpTime + 5;	-- UsedUpTime always <= StopDelay
						end;			
					--print("P2ML : Used Up Time ", UsedUpTime, " Stop Delay ", StopDelay, " Start Delay ", StartDelay);
					-- start the engine
					LastBlockSpeedLimit = BlockSpeedLimit;
					LastSentSpeed = SetSpeed(BlockSpeedLimit, StartDelay, MyEngineNo, MyTIUNo);
					print(string.format("P2ML : %s : Speed %d Smph = Start Delay %.3f seconds",
						BlockName[MyCurrentBlockNo], BlockSpeedLimit, StartDelay));
					EngineStopped = false;	-- indicate that we handled the stop here
					end;
-- TBD
				else
				print("P2ML : Engine Stopped in ".. BlockName[MyCurrentBlockNo]);
				RFIDFlag = false;
				end;
-- TBD
			WaitforCaboose = false;				
			end;
	return true;
	end;

return Passing2MainLine;
end;
--[[---------------------------------------------------------------------------------------]]

function createMainLine2Yard(flag, SwDistance)
	local WaitforCaboose = flag;
	local SwitchClearingDistance = SwDistance or 24;	
	local EngineStopped = false;
	local StopDelay = 0;
	local GotBlock = false;
	local StopTime = 0;
	local Target_Distance = 0;
	local MyCurrentBlockNo, MyLastBlockNo, MyNextBlockNo;
	local HomeSignal, DistantSignal;	
	local EngineDetectedTime = 0;
	local function MainLine2Yard(DetectorID, EngineNo, TagLocation, CarModel, ThisSwitch, Block)
		-- uses globals: MyEngineNo, LastBlockNo, CurrentBlockNo, NextBlockNo
		-- ThisSwitch may be nil if no switch is present

		if (type(Block) ~= "table") then
			return EmergencyStop("ML2Y : Block not a table");
			end
		if (EngineNo == MyEngineNo and ((TagLocation == TAG_ON_FRONT_TRUCK) or
			(not WaitforCaboose and (TagLocation == TAG_ON_REAR_TRUCK)))) then		-- if MyEngineNo in tag
			
--[[--- Engine Detected -----]]
			if (WaitforCaboose) then
				return EmergencyStop("ML2Y : expected a caboose but got an engine");
				end;
--[[--- 1 Initialization -----]]					
			EngineDetectedTime = RunTime();	-- Engine detected time	
			print("ML2Y : " .. DetectorID .. "Engine " .. MyEngineNo .. " detected");
			local Spd = GetSpeed(MyEngineNo, MyTIUNo);	-- get speed as engine covers tag
			--if (Spd ~= nil and (math.abs(Spd - BlockSpeedLimit) > UnderSpeedPermitted)) then
			--	print("ML2Y : Speed over tag = " .. Spd .. " Smph - expected = " .. BlockSpeedLimit .. " Smph");
			--	end;			
			LastBlockNo = CurrentBlockNo;
			CurrentBlockNo = NextBlockNo;
			-- initialize block local variables
			EngineStopped = false;
			GotBlock = false;
--[[--- 2 Register the new block's home and distant signals -----]]
			local Aspect, DistantAspect;			
			local NumLightsHome, NumLightsDistant;
			HomeSignal = NextSignal[MyDirection][CurrentBlockNo][HOMESIGNAL];	-- next signal number (0 if none)
			Aspect, NumLightsHome = GetAspect(HomeSignal);	-- 1, 2 or 3 lights on the next signal head (nil if none)
			DistantSignal = NextSignal[MyDirection][CurrentBlockNo][DISTANTSIGNAL];	-- distant signal number (0 if none)
			DistantAspect, NumLightsDistant = GetAspect(DistantSignal);	-- 1, 2 or 3 lights on the next signal head (nil if none)
			CreateCab(NumLightsHome, NumLightsDistant);	-- create light signals - (home, distant)			
			RegisterCabSignals(HomeSignal, DistantSignal);
--[[--- 3 Adjust speed for the current block -----]]
			LastBlockSpeedLimit = BlockSpeedLimit;
			BlockSpeedLimit = Layout.AdjustSpeedEx(Signals, MyDirection, CurrentBlockNo, Aspect);	-- adjust speed for some randomness in high speed blocks
			SetCounter(C3, BlockSpeedLimit);
			LastSentSpeed = SetSpeed(BlockSpeedLimit, 0, MyEngineNo, MyTIUNo);
			print(string.format("ML2Y : %s : Speed %d Smph", BlockName[CurrentBlockNo], BlockSpeedLimit));	
			print ("ML2Y : Home Signal in ", BlockName[CurrentBlockNo], " : " .. AspectNames[Aspect]);

--[[--- 4 check if home signal is showing permissive stop -----]]
			-- If it is, stop in this block. Set EngineStopped = true.
			-- NOTE: MainLine2Yard home signal can only be a permissive signal
			if (Signals and (Aspect == AsStopandProceed)) then
				-- stop in this block
				print("ML2Y : Home Signal aspect is Stop and Proceed. Stop the engine");
				-- stop in CurrentBlockNo
				StopDelay, Target_Distance = StopBlockAfterDelay(Layout, true, CurrentBlockNo, MyEngineNo, MyDirection, LastBlockSpeedLimit, BlockSpeedLimit)
				EngineStopped = true;
				GotBlock = false;
				StopTime = RunTime();				
				end;
			if (Aspect == AsStopSignal) then
				return EmergencyStop("ML2Y : Home Signal aspect is an unexpected absolute signal.");
				end;
--[[--- 5 ASAP, set the switch at entrance to passing/station track -----]]
			-- N/A			

--[[--- 6 set next block number -----]]
			NextBlockNo = Block[1];		-- set NextBlockNo to Block[1]				
--[[--- 7 If we didn't stop because of a signal aspect, then attempt to obtain occupancy lock -----]]	
			if (EngineStopped == false) then
-- TBD
--[[--- 8 stop the engine if this is the end block -----]]
				if (CurrentBlockNo == EndBlockNo) then
					-- stop in CurrentBlockNo/EndBlockNo
					StopDelay, Target_Distance = StopBlockAfterDelay(Layout, true, CurrentBlockNo, MyEngineNo, MyDirection, LastBlockSpeedLimit, BlockSpeedLimit)
					EngineStopped = true;	-- engine was stopped
					GotBlock = false;
					StopTime = RunTime();				
-- TBD
--[[--- 9 check for occupancy -----]]				
					-- check for Yard occupancy - Ending block must be unoccupied otherwise stop engine
					-- If we can't get a lock, schedule a stop once the entire train is in the block.					
					elseif (TrySetOccupancy(NextBlockNo, MyEngineNo | (MyDirection << 8))) then
						-- Got a lock on the next block, continue on
						StopDelay = 0;
						EngineStopped = false;
						GotBlock = true;
						StopTime = 0;
						else
--[[--- 10 if we can't get a lock, stop in CurrentBlockNo -----]]
						StopDelay, Target_Distance = StopBlockAfterDelay(Layout, true, CurrentBlockNo, MyEngineNo, MyDirection, LastBlockSpeedLimit, BlockSpeedLimit)
						EngineStopped = true;
						GotBlock = false;
						StopTime = RunTime();
						end;
					end;
				
--[[--- 11 set NextSwitch -----]]
			-- N/A
--[[--- 12 Cleanup -----]]					
			WaitforCaboose = true;
			-- save current state for the Caboose detection call of this function
			DisplayBlockNames("ML2Y : ", LastBlockNo, CurrentBlockNo, NextBlockNo);
			MyCurrentBlockNo = CurrentBlockNo;
			MyLastBlockNo = LastBlockNo;
			MyNextBlockNo = NextBlockNo;			
			return true;
			
--[[--- Caboose Detected -----]]

		elseif (EngineNo == 0 and (CarModel == CABOOSE or CarModel == OBSERVATION) and WaitforCaboose) then
			print("ML2Y : " .. DetectorID .. "End of Train after " .. RunTime() - EngineDetectedTime .. " seconds (Stop Delay = " .. StopDelay .. ")");
			if (SwitchClearingDistance > 0) then
				-- equation for Delay calculation
				-- time(seconds) = d(in) * 48 Smiles/mile * 3600 sec/hr * / (12 in/ft * 5280 ft/mile * X Smiles/hr)
				local SleepDelay =  (SwitchClearingDistance) * (48 / (12 * 5280 * BlockSpeedLimit)) * 3600;
				print(string.format("ML2Y : %s : Switch Clearing Distance = %.3f in - Speed %d Smph = Sleep Delay %.3f seconds",
					BlockName[MyCurrentBlockNo], SwitchClearingDistance, BlockSpeedLimit, SleepDelay));
				Sleep(SleepDelay);	-- give the caboose time to clear the switch
				print("ML2Y : " .. "Switch cleared after " .. SleepDelay .. " seconds")
				end;
			--
			-- ASAP Once we reach the switch clearing distance, return switch at exit of passing/station track back
			-- to NORMAL - this switch was set as result of the engine entering the block
--[[--- 1 set ThisSwitch switch -----]]			
			if (ThisSwitch ~= nil) then
				Switch(NORMAL, Switches[ThisSwitch][CHAN_NUMBER], Switches[ThisSwitch][AIU_NUMBER], Switches[ThisSwitch][TIU_NUMBER], 0);
				print("P2ML : " .. SwitchName[ThisSwitch] .. " to NORMAL");
				end;
			-- debug
			--print("MyCurrentBlockNo " .. MyCurrentBlockNo .. " MyDirection " .. MyDirection);
			--print("Opposite Direction " .. OppDirection);
			--print("Detector = " .. DETECTOR .. " Reader = " .. READER);		
			--print(" Detector = " .. Layout.STOPBlock(MyCurrentBlockNo, OppDirection, DETECTOR));
			--print(" Reader = " ..   Layout.STOPBlock(MyCurrentBlockNo, OppDirection, READER));
			ExpectedReader = Layout.STOPBlock(MyCurrentBlockNo, OppDirection, DETECTOR) * 10 + Layout.STOPBlock(MyCurrentBlockNo, OppDirection, READER);
			print("ML2Y : Expected Reader Changed to " .. ExpectedReader);
	--[[--- 1a clear occupancy -----]]
			if (MyLastBlockNo ~= 0) then
				ClearOccupancy(MyLastBlockNo, MyEngineNo | (MyDirection << 8));	-- clear LastBlockNo occupancy
				end;
			writeSettings(false);	-- save state without comment				
-- TBD
			if (MyCurrentBlockNo ~= EndBlockNo) then	-- EndBlockNo is always zero for loop configuration
--[[--- 2 If we are not in EndBlockNo, then wait for block(s) then restart engine if it was stopped -----]]
-- TBD							
				if (not GotBlock) then
--[[--- 3 wait for MyNextBlockNo to be locked (either Yard track) (default wait 30 minutes)-----]]				
					local result;
					-- set occupancy flag in MyNextBlockNo
					SetOccupancy(MyNextBlockNo, MyEngineNo | (MyDirection << 8));	-- set occupancy flag in MyNextBlockNo
					GotBlock = true;	-- indicate needed Block is locked
					end;
					
--[[--- 4 start moving if stopped -----]]
				if (EngineStopped) then
					-- Check if Stop and Proceed aspect is showing
					local Aspect = GetCabAspect(HOMESIGNAL);
					print ("ML2Y : Home Signal in ", BlockName[MyCurrentBlockNo], " : " .. AspectNames[Aspect]);

--[[--- 5 if we are stopped in this block due to signal aspect, wait 10 seconds before restarting -----]]
					-- NOTE: MainLine2Yard can only be permissive signal
					if (Signals and (Aspect == AsStopandProceed)) then
						Sleep(10);
						end;

					-- calcuate Time delay for start
					local UsedUpTime = RunTime() - StopTime;	-- never negative
					local StartDelay;
					if (UsedUpTime > StopDelay) then	-- if StopDelay == 0 then always true
						StartDelay = 5;
						else
						StartDelay = StopDelay - UsedUpTime + 5;	-- UsedUpTime always <= StopDelay
						end;			
					--print("ML2Y : Used Up Time ", UsedUpTime, " Stop Delay ", StopDelay, " Start Delay ", StartDelay);
					-- start the engine
					LastBlockSpeedLimit = BlockSpeedLimit;
					LastSentSpeed = SetSpeed(BlockSpeedLimit, StartDelay, MyEngineNo, MyTIUNo);
					print(string.format("ML2Y : %s : Speed %d Smph = Start Delay %.3f seconds",
						BlockName[MyCurrentBlockNo], BlockSpeedLimit, StartDelay));		
					EngineStopped = false;	-- indicate that we handled the stop here
					end;
-- TBD
				else
				print("ML2Y : Engine Stopped in ".. BlockName[MyCurrentBlockNo]);
				RFIDFlag = false;
				end;
-- TBD				
			WaitforCaboose = false;
			end;
	return true;
	end;

return MainLine2Yard;
end;
--[[---------------------------------------------------------------------------------------]]

function createStopInYard(flag, SwDistance)
	local WaitforCaboose = flag;
	local SwitchClearingDistance = SwDistance or 24;		
	local EngineStopped = false;
	local StopDelay = 0;
	local StopTime = 0;	
	local Target_Distance;	
	local MyCurrentBlockNo, MyLastBlockNo, MyNextBlockNo;	
	local HomeSignal, DistantSignal;		
	local EngineDetectedTime = 0;	
	local function StopInYard(DetectorID, EngineNo, TagLocation, CarModel, Yard1, Yard2, YardSwitch, OffLayoutBlock)
		-- uses globals: MyEngineNo, LastBlockNo, CurrentBlockNo, NextBlockNo
		if (type(OffLayoutBlock) ~= "table") then
			return EmergencyStop("SIY : OffLayoutBlock not a table");
			end
		if (EngineNo == MyEngineNo and ((TagLocation == TAG_ON_FRONT_TRUCK) or
			(not WaitforCaboose and (TagLocation == TAG_ON_REAR_TRUCK)))) then		-- if MyEngineNo in tag
			
--[[--- Engine Detected -----]]
			if (WaitforCaboose) then
				return EmergencyStop("SIY : expected a caboose but got an engine");
				end;		
--[[--- 1 Initialization -----]]					
			EngineDetectedTime = RunTime();	-- Engine detected time	
			print("SIY : " .. DetectorID .. "Engine " .. MyEngineNo .. " detected");
			local Spd = GetSpeed(MyEngineNo, MyTIUNo);	-- get speed as engine covers tag
			--if (Spd ~= nil and (math.abs(Spd - BlockSpeedLimit) > UnderSpeedPermitted)) then
			--	print("SIY : Speed over tag = " .. Spd .. " Smph - expected = " .. BlockSpeedLimit .. " Smph");
			--	end;
			LastBlockNo = CurrentBlockNo;
			CurrentBlockNo = NextBlockNo;
			-- initialize block local variables
			EngineStopped = false;
--[[--- 2 Register the new block's home and distant signals -----]]	
			-- no signals once in Yard Block
			RegisterCabSignals(0, 0); -- no Cab signals
--[[--- 3 Adjust speed for the current block -----]]
			LastBlockSpeedLimit = BlockSpeedLimit;
			BlockSpeedLimit = Layout.AdjustSpeedEx(Signals, MyDirection, CurrentBlockNo, AsNone);	-- adjust speed for some randomness in high speed blocks
			SetCounter(C3, BlockSpeedLimit);
			LastSentSpeed = SetSpeed(BlockSpeedLimit, 0, MyEngineNo, MyTIUNo);
			print(string.format("SIY : %s : Speed %d Smph", BlockName[CurrentBlockNo], BlockSpeedLimit));	
			--print ("SIY : Home Signal in ", BlockName[CurrentBlockNo], " : " .. AspectNames[Aspect]);
--[[--- 4 check if home signal is showing absolute or permissive stop -----]]
			-- N/A

--[[--- 5 ASAP, set the switch for the correct yard track  -----]]
			if (CurrentBlockNo == Yard1) then
				Switch(NORMAL, Switches[YardSwitch][CHAN_NUMBER], Switches[YardSwitch][AIU_NUMBER], Switches[YardSwitch][TIU_NUMBER], 0);
				print("SIY : Switch " .. SwitchName[YardSwitch] .. " to NORMAL");
				end;
			if (CurrentBlockNo == Yard2) then
				Switch(REVERSE, Switches[YardSwitch][CHAN_NUMBER], Switches[YardSwitch][AIU_NUMBER], Switches[YardSwitch][TIU_NUMBER], 0);
				print("SIY : Switch " .. SwitchName[YardSwitch] .. " to REVERSE");
				end;

--[[--- 6 set next block number -----]]
			NextBlockNo = OffLayoutBlock[1];
--[[--- 7 If we didn't stop because of a signal aspect -----]]	
			-- N/A	
--[[--- 8 stop the engine if this is the end block -----]]
			-- N/A	
--[[--- 9 check for occupancy -----]]
			-- N/A	
--[[--- 10 schedule stop in CurrentBlockNo -----]]			
			StopDelay, Target_Distance = StopBlockAfterDelay(Layout, true, CurrentBlockNo, MyEngineNo, MyDirection, LastBlockSpeedLimit, BlockSpeedLimit)
			EngineStopped = true;
			StopTime = RunTime();
--[[--- 11 set NextSwitch switch -----]]
			-- N/A
		
--[[--- 12 Cleanup -----]]				
			WaitforCaboose = true;
			-- save current state for the Caboose detection call of this function
			DisplayBlockNames("SIY : ", LastBlockNo, CurrentBlockNo, NextBlockNo);
			MyCurrentBlockNo = CurrentBlockNo;
			MyLastBlockNo = LastBlockNo;
			MyNextBlockNo = NextBlockNo;			
			return true;
			
--[[--- Caboose Detected -----]]

		elseif (EngineNo == 0 and (CarModel == CABOOSE or CarModel == OBSERVATION) and WaitforCaboose) then
			print("SIY : " .. DetectorID .. "End of Train after " .. RunTime() - EngineDetectedTime .. " seconds (Stop Delay = " .. StopDelay .. ")");
			if (SwitchClearingDistance > 0) then
				-- This is the time from when the caboose passes over the detector and when the caboose
				-- clears the last switch (may be more than one) after the detector.
				-- This calculation assumes that the train has slowed down to the new block speed before
				-- caboose passes over the detector.
				-- equation for Delay calculation
				-- time(seconds) = d(in) * 48 Smiles/mile * 3600 sec/hr * / (12 in/ft * 5280 ft/mile * X Smiles/hr)
				local SleepDelay =  (SwitchClearingDistance) * (48 / (12 * 5280 * BlockSpeedLimit)) * 3600;
				print(string.format("SIY : %s : Switch Clearing Distance = %.3f in - Speed %d Smph = Sleep Delay %.3f seconds",
					BlockName[MyCurrentBlockNo], SwitchClearingDistance, BlockSpeedLimit, SleepDelay));
				Sleep(SleepDelay);	-- give the caboose time to clear the switch
				print("SIY : " .. "Switch cleared after " .. SleepDelay .. " seconds")
				end;
			--
			--print("MyCurrentBlockNo " .. MyCurrentBlockNo .. " MyDirection " .. MyDirection);
			--print("Opposite Direction " .. OppDirection);
			--print("Detector = " .. DETECTOR .. " Reader = " .. READER);		
			--print(" Detector = " .. Layout.STOPBlock(MyCurrentBlockNo, OppDirection, DETECTOR));
			--print(" Reader = " ..   Layout.STOPBlock(MyCurrentBlockNo, OppDirection, READER));			
			if (MyLastBlockNo ~= 0) then
				ClearOccupancy(MyLastBlockNo, MyEngineNo | (MyDirection << 8));	-- clear LastBlockNo occupancy
				end;
			writeSettings(false);	-- save state without comment
				
			if (EngineStopped) then
				print("SIY : Engine Stopped in ".. BlockName[MyCurrentBlockNo]);
				RFIDFlag = false;
				end
			WaitforCaboose = false;
			end;	
	return true;
	end;

return StopInYard;
end;

--[[---------------------------------------------------------------------------------------]]
-- settings are saved in this file
local settingsfile = "Engineer_On_Board_Cutoff-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 NO_BLOCK;
		CurrentBlockNo = StartBlockNo;
		print("Start Block = " .. StartBlockNo .. " " .. BlockName[StartBlockNo]);		
		-- Ending Block
		local LastLine = f:read("*l");
		-- debug
		--print ("LastLine = " .. LastLine);
		if (LastLine ~= nil) then
			EndBlockNo = tonumber(string.match(LastLine, "%d+")) or NO_BLOCK;
			-- debug
			--print ("EndBlockNo = " .. EndBlockNo);
			else
			EndBlockNo = NO_BLOCK;
			end;
		if (EndBlockNo ~= nil) then
			print("End Block = " .. EndBlockNo .. " " .. BlockName[EndBlockNo]);
			end;
		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
		-- Ending Block
		if (EndBlockNo ~= nil) then
			if (pflag) then print("Ending Block = " .. EndBlockNo .. " " .. BlockName[EndBlockNo]); end;
			f:write(tostring(EndBlockNo).. " " .. BlockName[EndBlockNo] .. "\n");	-- save end block for restart
			end;
		f:close();
		return true;
	end;	
	return false;
end;

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

function setup(Engine, TIU)
print("setup() Running");
MyEngineNo = Engine;	-- this engine number may be changed by the query below
-- The TIU which controls layout 4 is defined in the Layout4.lua file	
-- Instrumented Layout 4
LoadButton();		-- All of the details about the Layout
--
-- EOB Cutoff will work with either the Looping or the Point-to-Point Layout Configuration
-- The TIU which controls layout 4 is defined in the Layout4.lua file
MyTIUNo = Layout.MASTER_TIU;
if (Layout.LLLoop == true) then
	print("Balloon Loop");
	else
	return Stop("Point-to-Point layout set - Balloon Loop Required");
	end;
	
-- read the .ini file and load MyEngineNo, Running Speed, MyDirection and StartBlockNo
readSettings();
--
Title(title, MyEngineNo);

-- Requires 10 tag detectors and 15 tag readers
local NumDetectors, NumReaders;
NumDetectors, NumReaders = GetTagReaders();
print("Number of Tag Detectors = " .. NumDetectors .. " Number of Tag Readers =  " .. NumReaders);
if ((Debug() == 0) and (NumDetectors < 10 or (NumReaders < 15 and NumReaders ~= 0))) then
	return Stop("Requires 10 Tag Detectors and 15 Tag Readers. Cycle layout power and try again.");
	end;	
--
	
if ((Debug() == 0 ) and (not LayoutShowing())) then
	return Stop([[Run "Layout APB Master.lua" to handle the signals]]);
	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 Enum = InputDropDown(title, "Select Engine", EngineNameIndex, EngineNameList);
--print("Enum ", Enum);
local typ = type(Enum);
--print("Type ", typ);

if (typ == "boolean" and Enum == false) then
	return Stop("Canceled");
	end;
		
if (Enum ~= nil and Enum ~= "") 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(Enum, "%d+");
	if (EngNo ~= nil) then
		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	
--[[--------------------------------------------]]	
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, {"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 Superior"; -- Westbound
	OppDirection = EB;
	Headway = false;	-- Cutoff cannot run in Headway
	end;
if (MyDirection == EB) then
	title = "EB"; -- Eastbound
	OppDirection = WB;
	Headway = false;
	end;
--[[--------------------------------------------]]	
Title(title .. " Cutoff - 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 EOB - Cutoff
			if ((iBlock == BYPASS) or (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) or
				(iBlock == BALLOON_BLOCK) or (iBlock == J_L_PASSING_TRACK) or (iBlock == J_L_THRU_TRACK) or 
				(iBlock == STRUTHERS_THRU_TRACK) or (iBlock == YOUNGSTOWN_BLOCK) or (iBlock == MCKEES_ROCKS_BLOCK)) then
				goto nextBlock;
				end;
			-- in Point-to-Point Configuration, dont allow selection of Balloon Track
			if ((Layout.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 ((Layout.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 McKees Rocks 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 Gateway Yard
				if ((iBlock ~= J_L_PASSING_TRACK) and (iBlock ~= COLLEGE_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
--print("Initial Index Value = " .. BlockNameIndex); 
--for iBlock = 1, BLOCK_COUNT do
--	print(BlockName[iBlock] .. "(" .. iBlock .. ")");
--	end
-- Debug
local StartingBlock = InputDropDown(title, "Select Starting Block", BlockNameIndex, BlockNameList);
--print("StartingBlock ", StartingBlock);
typ = type(StartingBlock);
--print("Type ", typ);

if (typ == "boolean" and StartingBlock == false) then
	return Stop("Canceled");
	end;
		
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	
--[[--------------------------------------------]]
print("Setup : Starting Block = " .. BlockName[StartBlockNo]);	
CurrentBlockNo = StartBlockNo;
--[[--------------------------------------------]]

	-- EndBlockNo is used only for the Point-to-Point Layout
	--
	-- Ask the dispatcher for Ending block number
	--
	-- For superior westbound trains, ending block can be any Thru block or any mainline block
	-- For inferior eastbound trains, ending block can be any Station/Passing block or any mainline block
	--
	BlockNameList = {};	-- table to hold all of the block numbers and names
	BlockNameIndex = 1;
	BlockNameCount = 1;
	
if (Layout.LLLoop == false) then
	for iBlock = 1, BLOCK_COUNT do
		local Name = BlockName[iBlock];
		if (Name ~= nil and iBlock <= LAST_BLOCK) then
			if ((iBlock == BYPASS) or (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) or
				(iBlock == BALLOON_BLOCK) or (iBlock == J_L_PASSING_TRACK) or (iBlock == J_L_THRU_TRACK) or 
				(iBlock == STRUTHERS_THRU_TRACK) or (iBlock == YOUNGSTOWN_BLOCK) or (iBlock == MCKEES_ROCKS_BLOCK)) then
				goto nextBlock;
				end;		
			-- in Point-to-Point Configuration, dont allow selection of Balloon Track
			if ((Layout.LLLoop == false) and (iBlock == BALLOON_BLOCK)) then
				goto nextBlock
				end;
			--print(Name .. "(" .. iBlock .. ")");
			if (MyDirection == EB) then
				-- don't allow selection of thru blocks or ending in Gateway Yard
				if ((iBlock ~= J_L_THRU_TRACK) and (iBlock ~= COLLEGE_THRU_TRACK) and (iBlock ~= STRUTHERS_THRU_TRACK) and
					(iBlock ~= GATEWAY_YARD1_TRACK) and (iBlock ~= GATEWAY_YARD2_TRACK)) then
					BlockNameList[BlockNameCount] = iBlock .. " " .. Name; 
					--print("BlockNameList[" .. BlockNameCount .. "] = " .. BlockNameList[BlockNameCount]);
					--------------------------------------------
					if (EndBlockNo == 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 ending in McKees Rocks Repair Shops Yard
				if ((iBlock ~= J_L_PASSING_TRACK) and (iBlock ~= COLLEGE_STATION_TRACK) and (iBlock ~= STRUTHERS_STATION_TRACK) and
					(iBlock ~= MCKEES_ROCKS_REPAIR_SHOPS_YARD1_TRACK) and (iBlock ~= MCKEES_ROCKS_REPAIR_SHOPS_YARD2_TRACK)) then 
					BlockNameList[BlockNameCount] = iBlock .. " " .. Name; 
					-- Debug
					--print("BlockNameList[" .. BlockNameCount .. "] = " .. BlockNameList[BlockNameCount]);
					--------------------------------------------
					if (EndBlockNo == iBlock) then
						BlockNameIndex = BlockNameCount;
						print("BlockNameIndex = " .. BlockNameIndex);
						end;
					BlockNameCount = BlockNameCount + 1;		
					--------------------------------------------
					end;
				end;
			end;
		::nextBlock::
		end;
	--
	-- read Ending Block Number
	print("Select Ending Block");
	--print("Initial Index Value = " .. BlockNameIndex); 
	--for iBlock = 1, BLOCK_COUNT do
	--	print(BlockName[iBlock] .. "(" .. iBlock .. ")");
	--	end
	local EndingBlock = InputDropDown(title, "Select Ending Block", BlockNameIndex, BlockNameList);
	--print("EndingBlock ", EndingBlock);
	typ = type(EndingBlock);
	--print("Type ", typ);

	if (typ == "boolean" and EndingBlock == false) then
		return Stop("Canceled");
		end;
			
	if (EndingBlock ~= nil and EndingBlock ~= "") 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(EndingBlock, "%d+");
		if (BlockNo ~= nil) then
			EndBlockNo = 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
			EndBlockNo = 1;
			end
		end	
	--[[--------------------------------------------]]
	print("Setup : Ending Block = " .. BlockName[EndBlockNo]);	
	--[[--------------------------------------------]]
	end
--[[--------------------------------------------]]
--
-- set the next expected detector and reader
--[[
--print("CurrentBlockNo " .. CurrentBlockNo .. " MyDirection " .. MyDirection);
--print("Opposite Direction " .. OppDirection);
----print("Detector = " .. DETECTOR .. " Reader = " .. READER);	
--print(" Detector = " .. Layout.STOPBlock(CurrentBlockNo, OppDirection, DETECTOR));
--print(" Reader = " .. Layout.STOPBlock(CurrentBlockNo, OppDirection, READER));
--]]
ExpectedReader = Layout.STOPBlock(CurrentBlockNo, OppDirection, DETECTOR) * 10 + Layout.STOPBlock(CurrentBlockNo, OppDirection, READER)
print("Setup : Expected Reader = " .. ExpectedReader);
-----------------------------------------------------------------------------------------------------
-- create and register the cab signals (Layout must be showing for this script to function correctly)
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))]);
--
-----------------------------------------------------------------------------------------------------	
-- 
BlockSpeedLimit = BlockSpeed[MyDirection][CurrentBlockNo];
LastBlockSpeedLimit = BlockSpeedLimit;
SetCounter(C3, BlockSpeedLimit);
--
-- Balloon Switch is set in Layout4.lua
--
--[[-------------------------]]
if (MyDirection == EB) then
	print("Setup : Eastbound");
	else
	print("Setup : Westbound");
	end
--[[-------------------------]]
-- 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 this is a passing/station track,clear the adjacent thru track of any soft occupation performed by Layout4APBMaster
if (CurrentBlockNo == COLLEGE_STATION_TRACK) then
	ClearOccupancy(COLLEGE_THRU_TRACK, SOFTOCCUPY);	
		elseif (CurrentBlockNo == STRUTHERS_STATION_TRACK) then
			ClearOccupancy(STRUTHERS_THRU_TRACK, SOFTOCCUPY);	
			elseif (CurrentBlockNo == J_L_PASSING_TRACK) then
				ClearOccupancy(J_L_THRU_TRACK, SOFTOCCUPY);	
					end;

OverrideOccupancy(CurrentBlockNo, MyEngineNo | (MyDirection << 8));	-- force set occupancy flag on the starting block
	
-- 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(C1);	
local PacketLen = #TagPacket;

if (Debug() >= 8) then print("Packet Length = ", PacketLen); end;
if (Debug() >= 7) then print(TagPacket); 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 (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)));
		-- In this script, only check the ExpectedReader on MyEngineNo. Since many trains can be running
		-- at the same time, tags can be coming in from all over at any time. 
		if (MyReader ~= ExpectedReader) then
			--return EmergencyStop(RunTime() .. " tag() : Got Reader = " .. MyReader .. " Expected Reader = " .. ExpectedReader);
			return Stop(RunTime() .. " tag() : Got Reader = " .. MyReader .. " Expected Reader = " .. ExpectedReader);
			end
		else
		-- ignore engines that are not me
		return true;	-- true=continue to process tags, false=stop processing tags
		end;
	-- Continue on to process the detected engine
	else	-- it's a car
	if (CarModel == CABOOSE or CarModel == OBSERVATION) then
		if (BeepOn) then Beep(EXCLAMATION); end;
		-- Every caboose comes through here even on other trains
		--printc(clBlue, string.format([[tag(%.2f) : Detector %d Reader %d Car %s %s #%s]],
		--	RunTime(), Detector, Reader, GetRailroadName(Railroad), GetCarName(CarModel), CarNumber));
		else
		-- ignore all non-end of train cars
		return true;	-- true=continue to process tags, false=stop processing tags
		end;		
	-- Continue on to process end of train tags
	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("tag : Packet  %s  %s", string.sub(TagPacket, 1, 8),
	string.sub(TagPacket,9,40) )); end

--[[--------------------------------- Westbound --------------------------------------------]]
--[[--------------------------]]
--[[ Westbound trains are superior --]]
--[[--------------------------]]
--
-- ResetinProgress probably does not work anymore
--
if (MyDirection == WB) then
	if (ResetinProgress) then
		-- run back to MCKEES_ROCKS_REPAIR_SHOPS_YARD_TAG
		if (Detector == MCKEES_ROCKS_REPAIR_SHOPS_YARD_DETECTOR and Reader == MCKEES_ROCKS_REPAIR_SHOPS_YARD_READER) then
			local DetectorID = "@ MCKEES_ROCKS_REPAIR_SHOPS_YARD_TAG ";
			local StopDelay = 0;
			if (EngineNo == 0 and (CarModel == CABOOSE or CarModel == OBSERVATION)) then		-- going in reverse, execute on caboose detection
				print(DetectorID .. "End of Train detected");
				-- schedule stop in StartBlockNo
				StopDelay = StopBlockAfterDelay(Layout, true, StartBlockNo, EngineNo, MyDirection, LastBlockSpeedLimit, BlockSpeedLimit)
				SetDirection(FORWARD, StopDelay + 5, EngineNo, MyTIUNo);
				ResetinProgress = false;
				end;
			end;
		return true;

	elseif (not RFIDFlag) then	-- ignore tags unless engine is moving
		return true;
	-- WEST_ALIQUIPPA_TAG Detected Westbound
	elseif (Detector == WEST_ALIQUIPPA_DETECTOR and Reader == WEST_ALIQUIPPA_READER) then
		if (Func100 == nil) then
			Func100 = createMainLine2MainLine(false);
			end;
		-- status: just entered NextBlockNo, occupancy already set
		-- No switch to set on Engine or Caboose detection.
		Func100("@ WEST_ALIQUIPPA_TAG ", EngineNo, TagLocation, CarModel, {}, {}, {}, {},
			{MONACA_BLOCK, FALLSTON_BLOCK, BEAVER_FALLS_BLOCK, COLLEGE_THRU_TRACK}); 

	-- MONACA_EAST_TAG Detected Westbound
	elseif (Detector == MONACA_EAST_DETECTOR and Reader == MONACA_EAST_READER) then
		-- entering tunnel, reduce volume
		if (EngineNo == MyEngineNo and TagLocation == TAG_ON_FRONT_TRUCK and SoundOff == false) then
			SetVolume(MASTER_VOLUME, math.floor(EngineSoundSave * 5/6), 0, MyEngineNo, MyTIUNo);
			SetVolume(MASTER_VOLUME, math.floor(EngineSoundSave * 4/6), 1, MyEngineNo, MyTIUNo);
			SetVolume(MASTER_VOLUME, math.floor(EngineSoundSave * 1/2), 2, MyEngineNo, MyTIUNo);
			end;
		if (Func60 == nil) then
			Func60 = createMainLine2MainLine(false);
			end;
		-- No switch to set on Caboose detection.
		Func60("@ MONACA_EAST_TAG ", EngineNo, TagLocation, CarModel, {}, {}, {}, {},
			{FALLSTON_BLOCK, BEAVER_FALLS_BLOCK, COLLEGE_THRU_TRACK});
		
	-- MONACA_WEST_TAG Detected Westbound
	elseif (Detector == MONACA_WEST_DETECTOR and Reader == MONACA_WEST_READER) then
		-- leaving tunnel, increase volume
		if (EngineNo == MyEngineNo and TagLocation == TAG_ON_FRONT_TRUCK and SoundOff == false) then
			SetVolume(MASTER_VOLUME, math.floor(EngineSoundSave * 4/6), 0, MyEngineNo, MyTIUNo);
			SetVolume(MASTER_VOLUME, math.floor(EngineSoundSave * 5/6), 1, MyEngineNo, MyTIUNo);
			SetVolume(MASTER_VOLUME, EngineSoundSave, 2, MyEngineNo, MyTIUNo);
			end;
		if (Func61 == nil) then
			Func61 = createMainLine2MainLine(false);
			end;
		-- No switch to set on Engine or Caboose detection.
		Func61("@ MONACA_EAST_TAG ", EngineNo, TagLocation, CarModel, {}, {}, {}, {},
			{BEAVER_FALLS_BLOCK, COLLEGE_THRU_TRACK});

	-- FALLSTON_TAG Detected Westbound
	elseif (Detector == FALLSTON_DETECTOR and Reader == FALLSTON_READER) then
		if (Func101 == nil) then
			Func101 = createMainLine2Passing(false);
			end;
		Func101("@ FALLSTON_TAG ", EngineNo, TagLocation, CarModel, COLLEGE_THRU_TRACK, COLLEGE_STATION_TRACK, nil);

	-- COLLEGE_EAST_TAG Detected Westbound
	elseif (Detector == COLLEGE_EAST_DETECTOR and Reader == COLLEGE_EAST_READER) then
		if (Func41 == nil) then
			Func41 = createPassing2MainLine(false, SwitchClearingInches[COLLEGE_EAST_TAG][MyDirection]);
			end;
		-- status: in block NextBlockNo (COLLEGE_THRU_TRACK), occupancy already set
		Func41("@ COLLEGE_EAST_TAG ", EngineNo, TagLocation, CarModel, COLLEGE_THRU_TRACK, COLLEGE_STATION_TRACK, COLLEGE_EAST, COLLEGE_WEST, 
			{NEW_CASTLE_JUNCTION_BLOCK, LOWELLVILLE_BLOCK, STRUTHERS_STATION_TRACK});

	-- COLLEGE_WEST_TAG Detected Westbound
	elseif (Detector == COLLEGE_WEST_DETECTOR and Reader == COLLEGE_WEST_READER) then
		if (Func21 == nil) then
			Func21 = createMainLine2MainLine(false);
			end;
		-- status: just entered NextBlockNo (NEW_CASTLE_JUNCTION_BLOCK), occupancy already set
		-- Set COLLEGE_WEST to NORMAL on Caboose detection.
		Func21("@ COLLEGE_WEST_TAG ", EngineNo, TagLocation, CarModel, {}, {}, {COLLEGE_WEST}, {},	-- error was ALIQUIPPA_WEST switch
			{LOWELLVILLE_BLOCK, STRUTHERS_STATION_TRACK, ALIQUIPPA_CUTOFF}); 		
			
	-- LOWELLVILLE_TAG Detected Westbound
	elseif (Detector == LOWELLVILLE_DETECTOR and Reader == LOWELLVILLE_READER) then
		if (Func90 == nil) then
			Func90 = createMainLine2MainLine(false);
			end;
		-- No switch to set on Engine or Caboose detection.
		Func90("@ LOWELLVILLE_TAG ", EngineNo, TagLocation, CarModel, {}, {}, {}, {},
			{STRUTHERS_STATION_TRACK, ALIQUIPPA_CUTOFF});

	-- Next three tags are changes to implement the Cutoff route

	-- STRUTHERS_EAST_TAG Detected Westbound
	elseif (Detector == STRUTHERS_EAST_DETECTOR and Reader == STRUTHERS_EAST_READER) then
		if (Func20 == nil) then
			Func20 = createMainLine2MainLine(false, SwitchClearingInches[STRUTHERS_EAST_TAG][MyDirection]);
			end;
		-- status: just entered NextBlockNo (STRUTHERS_STATION_BLOCK), occupancy already set
		-- on engine detection, set STRUTHERS_EAST to REVERSE which is the only way it can go here
		-- on engine detection, set CAMPBELL switch to REVERSE which is the only way it can go here
		-- on caboose detection + switch clearing distance, set STRUTHERS_EAST to NORMAL.
		Func20("@ STRUTHERS_EAST_TAG ", EngineNo, TagLocation, CarModel, {}, {STRUTHERS_EAST, CAMPBELL}, {STRUTHERS_EAST}, {},
			{ALIQUIPPA_CUTOFF});		
		
	-- CAMPBELL_TAG Detected Westbound 15-0
	elseif (Detector == CAMPBELL_DETECTOR and Reader == CAMPBELL_READER) then
		if (Func150 == nil) then
			Func150 = createMainLine2MainLine(false);
			end;
		-- status: in block NextBlockNo (ALIQUIPPA_CUTOFF), occupancy already set
		-- on Caboose detection, set CAMPBELL switch to NORMAL
		Func150("@ CAMPBELL_TAG ", EngineNo, TagLocation, CarModel, {}, {}, {CAMPBELL}, {},
			{ALIQUIPPA_BLOCK, WEST_ALIQUIPPA_BLOCK, MONACA_BLOCK, FALLSTON_BLOCK, BEAVER_FALLS_BLOCK, COLLEGE_THRU_TRACK});

	-- WOODLAWN_TAG Detected Westbound 11-0
	elseif (Detector == WOODLAWN_DETECTOR and Reader == WOODLAWN_READER) then
		if (Func110 == nil) then
			Func110 =createMainLine2MainLine(false, SwitchClearingInches[WOODLAWN_TAG][MyDirection]);
			end;
		-- status: just entered NextBlockNo (ALIQUIPPA_BLOCK), occupancy already set
		-- on engine detection, set WOODLAWN switch to REVERSE
		-- on caboose detection + switch clearing distance, set WOODLAWN to NORMAL
		Func110("@ WOODLAWN_TAG ", EngineNo, TagLocation, CarModel, {}, {WOODLAWN}, {WOODLAWN}, {},
			{WEST_ALIQUIPPA_BLOCK, MONACA_BLOCK, FALLSTON_BLOCK, BEAVER_FALLS_BLOCK, COLLEGE_THRU_TRACK});						
		
		end;
	end;
--[[--------------------------------- Eastbound --------------------------------------------]]

if (MyDirection == EB) then
	if (ResetinProgress) then
		-- run back to GATEWAY_YARD_TAG
		if (Detector == GATEWAY_YARD_DETECTOR and Reader == GATEWAY_YARD_READER) then
			local DetectorID = "@ GATEWAY_YARD_TAG ";
			local StopDelay = 0;
			if (EngineNo == 0 and (CarModel == CABOOSE or CarModel == OBSERVATION)) then		-- going in reverse, execute on caboose detection
				print(DetectorID .. "End of Train detected");
				-- schedule stop in StartBlockNo
				StopDelay = StopBlockAfterDelay(Layout, true, StartBlockNo, EngineNo, MyDirection, LastBlockSpeedLimit, BlockSpeedLimit)
				SetDirection(FORWARD, StopDelay + 5, EngineNo, MyTIUNo);
				ResetinProgress = false;
				end;
			end;
		return true;

	elseif (not RFIDFlag) then	-- ignore tags unless engine is moving
		return true;

	-- LOWELLVILLE_TAG Detected Eastbound
	elseif (Detector == LOWELLVILLE_DETECTOR and Reader == LOWELLVILLE_READER) then
		if (Func90 == nil) then
			Func90 = createMainLine2Passing(false);
			end;
		-- status: just entered NextBlockNo, occupancy already set
		Func90("@ LOWELLVILLE_TAG ", EngineNo, TagLocation, CarModel, COLLEGE_THRU_TRACK, COLLEGE_STATION_TRACK, nil);

	-- COLLEGE_WEST_TAG Detected Eastbound
	elseif (Detector == COLLEGE_WEST_DETECTOR and Reader == COLLEGE_WEST_READER) then
		if (Func21 == nil) then
			Func21 = createPassing2MainLine(false, SwitchClearingInches[COLLEGE_WEST_TAG][MyDirection]);
			end;
		-- status: in block NextBlockNo, occupancy already set.
		-- Set COLLEGE_WEST to NORMAL on Caboose detection.
		Func21("@ COLLEGE_WEST_TAG ", EngineNo, TagLocation, CarModel, COLLEGE_THRU_TRACK, COLLEGE_STATION_TRACK, COLLEGE_WEST, COLLEGE_EAST, 
			{BEAVER_FALLS_BLOCK, FALLSTON_BLOCK, MONACA_BLOCK, WEST_ALIQUIPPA_BLOCK, ALIQUIPPA_BLOCK, ALIQUIPPA_CUTOFF});

	-- COLLEGE_EAST_TAG Detected Eastbound
	elseif (Detector == COLLEGE_EAST_DETECTOR and Reader == COLLEGE_EAST_READER) then
		if (Func41 == nil) then
			Func41 = createMainLine2MainLine(false); 
			end;
		-- status: just entered NextBlockNo, occupancy already set
		-- Set COLLEGE_EAST to NORMAL on Caboose detection.
		Func41("@ COLLEGE_EAST_TAG ", EngineNo, TagLocation, CarModel, {}, {}, {COLLEGE_EAST}, {},
			{FALLSTON_BLOCK, MONACA_BLOCK, WEST_ALIQUIPPA_BLOCK, ALIQUIPPA_BLOCK, ALIQUIPPA_CUTOFF});

	-- FALLSTON_TAG Detected Eastbound
	elseif (Detector == FALLSTON_DETECTOR and Reader == FALLSTON_READER) then
		if (Func101 == nil) then
			Func101 = createMainLine2MainLine(false);
			end;
		-- No switch to set on Engine or Caboose detection.
		Func101("@ FALLSTON_TAG ", EngineNo, TagLocation, CarModel, {}, {}, {}, {},
			{MONACA_BLOCK, WEST_ALIQUIPPA_BLOCK, ALIQUIPPA_BLOCK, ALIQUIPPA_CUTOFF});

	-- MONACA_WEST_TAG Detected Eastbound
	elseif (Detector == MONACA_WEST_DETECTOR and Reader == MONACA_WEST_READER) then
		-- entering tunnel, reduce volume
		if (EngineNo == MyEngineNo and TagLocation == TAG_ON_FRONT_TRUCK and SoundOff == false) then
			SetVolume(MASTER_VOLUME, math.floor(EngineSoundSave * 5/6), 0, MyEngineNo, MyTIUNo);
			SetVolume(MASTER_VOLUME, math.floor(EngineSoundSave * 4/6), 1, MyEngineNo, MyTIUNo);
			SetVolume(MASTER_VOLUME, math.floor(EngineSoundSave * 1/2), 2, MyEngineNo, MyTIUNo);
			end;
		if (Func61 == nil) then
			Func61 = createMainLine2MainLine(false);
			end;
		-- No switch to set on Engine or Caboose detection.
		Func61("@ MONACA_WEST_TAG ", EngineNo, TagLocation, CarModel, {}, {}, {}, {},
			{WEST_ALIQUIPPA_BLOCK, ALIQUIPPA_BLOCK, ALIQUIPPA_CUTOFF});

	-- MONACA_EAST_TAG Detected Eastbound
	elseif (Detector == MONACA_EAST_DETECTOR and Reader == MONACA_EAST_READER) then
		-- leaving tunnel, increase volume
		if (EngineNo == MyEngineNo and TagLocation == TAG_ON_FRONT_TRUCK and SoundOff == false) then
			SetVolume(MASTER_VOLUME, math.floor(EngineSoundSave * 4/6), 0, MyEngineNo, MyTIUNo);
			SetVolume(MASTER_VOLUME, math.floor(EngineSoundSave * 5/6), 1, MyEngineNo, MyTIUNo);
			SetVolume(MASTER_VOLUME, EngineSoundSave, 2, MyEngineNo, MyTIUNo);
			end;
		if (Func60 == nil) then
			Func60 = createMainLine2MainLine(false);
			end;
		-- No switch to set on Engine or Caboose detection.
		Func60("@ MONACA_EAST_TAG ", EngineNo, TagLocation, CarModel, {}, {}, {}, {},
			{ALIQUIPPA_BLOCK,  ALIQUIPPA_CUTOFF});

	-- Next four tags are changes to implement the Cutoff route

	-- WEST_ALIQUIPPA_TAG Detected Eastbound
	elseif (Detector == WEST_ALIQUIPPA_DETECTOR and Reader == WEST_ALIQUIPPA_READER) then
		if (Func100 == nil) then
			Func100 = createMainLine2MainLine(false);
			end;
		-- status: in block NextBlockNo, occupancy already set (ALIQUIPPA_BLOCK).
		-- Throw WOODLAWN switch to REVERSE if Caboose tag ----------------------------------------------------------------------
		Func100("@ WEST_ALIQUIPPA_TAG ", EngineNo, TagLocation, CarModel, {}, {}, {}, {WOODLAWN},
			{ALIQUIPPA_CUTOFF});
			
	-- WOODLAWN_TAG Detected Eastbound 11-0
	elseif (Detector == WOODLAWN_DETECTOR and Reader == WOODLAWN_READER) then
		if (Func110 == nil) then
			Func110 =createMainLine2MainLine(false);
			end;
		-- status: just entered NextBlockNo, occupancy already set (ALIQUIPPA_CUTOFF).
		-- Throw WOODLAWN switch to NORMAL on Caboose detection.
		Func110("@ WOODLAWN_TAG ", EngineNo, TagLocation, CarModel,  {}, {}, {WOODLAWN}, {},
			{STRUTHERS_STATION_TRACK});

	-- CAMPBELL_TAG Detected Eastbound 15-0
	elseif (Detector == CAMPBELL_DETECTOR and Reader == CAMPBELL_READER) then
		if (Func150 == nil) then
			Func150 = createMainLine2MainLine(false);
			end;
		-- status: in block NextBlockNo, occupancy already set (STRUTHERS_STATION_TRACK).
		-- Throw CAMPBELL switch to REVERSE on Engine detection
		-- Throw STRUTHERS_EAST switch to REVERSE on Caboose detection
		Func150("@ CAMPBELL_TAG ", EngineNo, TagLocation, CarModel, {}, {CAMPBELL}, {}, {STRUTHERS_EAST}, 
			{LOWELLVILLE_BLOCK, NEW_CASTLE_JUNCTION_BLOCK, COLLEGE_STATION_TRACK});	
			
	-- STRUTHERS_EAST_TAG Detected Eastbound
	elseif (Detector == STRUTHERS_EAST_DETECTOR and Reader == STRUTHERS_EAST_READER) then
		if (Func20 == nil) then
			Func20 = createMainLine2MainLine(false);
			end;
		-- status: just entered NextBlockNo, occupancy already set (LOWELLVILLE_BLOCK).
		-- Throw STRUTHERS_EAST to NORMAL on Caboose detection.	
		-- Throw CAMPBELL switch to NORMAL on Caboose detection
		Func20("@ STRUTHERS_EAST_TAG ", EngineNo, TagLocation, CarModel, {}, {}, {STRUTHERS_EAST, CAMPBELL}, {},
			{NEW_CASTLE_JUNCTION_BLOCK, COLLEGE_STATION_TRACK});
		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
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
LastSentSpeed = SetSpeed(0, 0, MyEngineNo, MyTIUNo);
Rate(Layout.AccDecRate, Layout.AccDecRate, 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;
ResetinProgress = false;
print(string.format("cleanup() Complete at %.2f", RunTime()));
return true;		-- false=cleanup failed, true=cleanup succeeded
end
--[[---------------------------------------------------------------------------------------]]

function Plus5Button()
BlockSpeedLimit = BlockSpeedLimit + 5;
LastSentSpeed = SetSpeed(BlockSpeedLimit, 0, MyEngineNo, MyTIUNo);
SetCounter(C3, BlockSpeedLimit)
return true;
end
Function01Name, Function01Label = Plus5Button, "Speed +5";
--[[---------------------------------------------------------------------------------------]]

function Minus5Button()
BlockSpeedLimit = BlockSpeedLimit - 5;
LastSentSpeed = SetSpeed(BlockSpeedLimit, 0, MyEngineNo, MyTIUNo);
SetCounter(C3, BlockSpeedLimit)
return true;
end
Function02Name, Function02Label = Minus5Button, "Speed -5";
--[[---------------------------------------------------------------------------------------]]

function DTOButton()
print(string.format("Engine %d Trip Odometer %.3f Smiles", MyEngineNo, DTO(MyEngineNo, MyTIUNo)));
return true;
end
Function21Name, Function21Label = DTOButton, "Show DTO";
--[[---------------------------------------------------------------------------------------]]

function DODButton()
print(string.format("Engine %d Odometer %.3f Smiles", MyEngineNo, DOD(MyEngineNo, MyTIUNo)));
return true;
end
Function22Name, Function22Label = DODButton, "Show DOD";
--[[---------------------------------------------------------------------------------------]]

function StopYoungstownButton()
if (MyDirection ~= EB) then return false; end;
-- toggle StopYoungstown flag
StopYoungstown =  not StopYoungstown;
print("East bound trains to stop @ Youngstown set to " .. tostring(StopYoungstown));
return true;
end
Function23Name, Function23Label = StopYoungstownButton, "Stop EB@Youngstown";
--[[---------------------------------------------------------------------------------------]]

function StopNewCastleButton()
if (MyDirection ~= WB) then return false; end;
-- toggle StopNewCastle flag
StopNewCastle =  not StopNewCastle;
print("West bound trains to stop @ New Castle set to " .. tostring(StopNewCastle));
return true;
end
Function24Name, Function24Label = StopNewCastleButton, "Stop WB@New Castle";
--[[---------------------------------------------------------------------------------------]]

function StopTimeButton()
-- Stop for a random time
SetSpeed(BlockSpeedLimit, 0, MyEngineNo, MyTIUNo);
local WaitTime = math.random(60, 90);
print("Waiting for restart in ", WaitTime, " seconds");
while (true) do
	-- wait for WaitTime
	if (((WaitTime >> 1) % 10) == 0) then
		print("Waiting for restart in ", WaitTime, " seconds");
		end;
	Sleep(5);
	WaitTime = WaitTime - 5;
	if (WaitTime <= 0) then
		SetSpeed(LastSentSpeed, 0, MyEngineNo, MyTIUNo);
		return true
		end	
	end;
return true;
end
Function25Name, Function25Label = StopTimeButton, "Stop Random";
--[[---------------------------------------------------------------------------------------]]

function ClearButton()
print("Clear Occupancy Flags");
-- clear occupancy flags on request but not at startup (cleared in "Layout4 APB Master")
local x;
for x = FIRST_BLOCK, LAST_BLOCK do
	OverrideOccupancy(x, UNOCCUPIED);
	end;
print("Occupancy Flags cleared");
return true;
end
Function11Name, Function11Label = ClearButton, "Clear Flags";
--[[---------------------------------------------------------------------------------------]]

function HeadwayButton()
print("Toggle Headway");
-- toggle Headway flag
Headway =  not Headway;
print("Headway set to " .. tostring(Headway));
return true;
end
Function04Name, Function04Label = HeadwayButton, "Headway";
--[[---------------------------------------------------------------------------------------]]

function SignalsButton()
print("Toggle Signals");
-- toggle Signals flag
Signals =  not Signals;
print("Signals set to " .. tostring(Signals));
return true;
end
Function05Name, Function05Label = SignalsButton, "Signals";
--[[---------------------------------------------------------------------------------------]]

function BalloonButton()
print("Balloon Switch Tracks")
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);
Switch(NORMAL, Switches[BALLOON][CHAN_NUMBER], Switches[BALLOON][AIU_NUMBER], Switches[BALLOON][TIU_NUMBER], 0);	
print("Balloon and McKees Rocks Yard switches set to NORMAL");
return true;
end
Function18Name, Function18Label = BalloonButton, "Balloon";		
--[[---------------------------------------------------------------------------------------]]

function SoundOffButton()
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");
--[[-----------------------------]]
-- clear all tag handling functions
Func10 = nil;
Func20 = nil;
Func21 = nil;
Func30 = nil;
Func40 = nil;
Func41 = nil;
Func50 = nil;
Func51 = nil;
Func60 = nil;
Func61 = nil;
Func90 = nil;
Func100 = nil;
Func101 = nil;
Func110 = nil;
Func150 = nil;

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

function StartButton()
if (RunStarted) then	-- prevent double click on StartButton
	return true;
	end;
RunStarted = true;
print("Start the Run");

InitButton(MyEngineNo, MyTIUNo);

--[[------------------------------------------]]
--print("StationName = " .. Pretty(StationName));
--print("StationName = " .. Pretty(BlockName));
--print("Switches = " ..  Pretty(Switches));
--print("NextBlock = " ..  Pretty(NextBlock));
print("Headway set to " .. tostring(Headway));
print("Signals set to " .. tostring(Signals));
-- start the engine
Rate(Layout.AccDecRate, Layout.AccDecRate, 0, MyEngineNo, MyTIUNo);
print("setup : Set accel/decel rates to " .. Layout.AccDecRate);
if (StartedFlag == false) then
	EngineStartUp(MyEngineNo, MyTIUNo);
	print("Starting up the Engine");
	StartedFlag = true;
	Sleep(20);
	end
SetDirection(FORWARD, 0, MyEngineNo, MyTIUNo);
-- note: next call may not be needed as it is done earlier (in setup())
OverrideOccupancy(CurrentBlockNo, MyEngineNo | (MyDirection << 8));	-- force set occupancy flag for starting block
--[[--------------------------------- Westbound --------------------------------------------]]
if (MyDirection == WB) then
	local LocalHeadway = Headway;
	--
	-- Engine can start in any cutoff route block, set exit switches from meet points as necessary
	-- Westbound engines normally run in Headway but you can manually clear the Headway flag
	--
	if (CurrentBlockNo == ALIQUIPPA_CUTOFF) then -- "Aliquippa Cutoff" -- Westbound
		LastBlockNo = STRUTHERS_STATION_TRACK;
		NextBlockNo = ALIQUIPPA_BLOCK;
		--
		-- Additional test for Westbound trains in Headway mode starting from ALIQUIPPA_CUTOFF:
		-- if in Headway, check all blocks (except last one) for presence of a eastbound train.
		-- if there is an eastbound train in any of those blocks, continue to wait.		
		if (LocalHeadway) then
			print("LocalHeadway Check from " .. BlockName[CurrentBlockNo]);
			flag = math.abs(GetFlag(ALIQUIPPA_BLOCK));
			if (flag ~= UNOCCUPIED) then
					print(BlockName[ALIQUIPPA_BLOCK] .. "(#" .. ALIQUIPPA_BLOCK .. ") Occupied : Engine #" .. (flag & 0x00FF) .. " " .. DirectionNames[(flag & 0xFF00) >> 8]);
					if ((flag & 0xFF00) >> 8 == EB) then
						LocalHeadway = false;
						end;
					end;
			flag = math.abs(GetFlag(WEST_ALIQUIPPA_BLOCK));
			if (flag ~= UNOCCUPIED) then
					print(BlockName[WEST_ALIQUIPPA_BLOCK] .. "(#" .. WEST_ALIQUIPPA_BLOCK .. ") Occupied : Engine #" .. (flag & 0x00FF) .. " " .. DirectionNames[(flag & 0xFF00) >> 8]);
					if ((flag & 0xFF00) >> 8 == EB) then
						LocalHeadway = false;
						end;
					end;
			flag = math.abs(GetFlag(MONACA_BLOCK));
			if (flag ~= UNOCCUPIED) then
					print(BlockName[MONACA_BLOCK] .. "(#" .. MONACA_BLOCK .. ") Occupied : Engine #" .. (flag & 0x00FF) .. " " .. DirectionNames[(flag & 0xFF00) >> 8]);
					if ((flag & 0xFF00) >> 8 == EB) then
						LocalHeadway = false;
						end;
					end;
			flag = math.abs(GetFlag(FALLSTON_BLOCK));
			if (flag ~= UNOCCUPIED) then
					print(BlockName[FALLSTON_BLOCK] .. "(#" .. FALLSTON_BLOCK .. ") Occupied : Engine #" .. (flag & 0x00FF) .. " " .. DirectionNames[(flag & 0xFF00) >> 8]);
					if ((flag & 0xFF00) >> 8 == EB) then
						LocalHeadway = false;
						end;
					end;
			flag = math.abs(GetFlag(BEAVER_FALLS_BLOCK));
			if (flag ~= UNOCCUPIED) then
					print(BlockName[BEAVER_FALLS_BLOCK] .. "(#" .. BEAVER_FALLS_BLOCK .. ") Occupied : Engine #" .. (flag & 0x00FF) .. " " .. DirectionNames[(flag & 0xFF00) >> 8]);
					if ((flag & 0xFF00) >> 8 == EB) then
						LocalHeadway = false;
						end;
					end;
			flag = math.abs(GetFlag(COLLEGE_THRU_TRACK));
			if (flag ~= UNOCCUPIED) then
					print(BlockName[COLLEGE_THRU_TRACK] .. "(#" .. COLLEGE_THRU_TRACK .. ") Occupied : Engine #" .. (flag & 0x00FF) .. " " .. DirectionNames[(flag & 0xFF00) >> 8]);
					if ((flag & 0xFF00) >> 8 == EB) then
						LocalHeadway = false;
						end;				
					end;					
			print("Startup Headway set to " .. tostring(LocalHeadway));
		end;					
		MultiSetOccupancy(LocalHeadway and {NextBlockNo} or {NextBlockNo, WEST_ALIQUIPPA_BLOCK, MONACA_BLOCK, FALLSTON_BLOCK, BEAVER_FALLS_BLOCK, COLLEGE_THRU_TRACK}, MyEngineNo | (MyDirection << 8));				
		-- Position WOODLAWN after locking next block
		Switch(NORMAL, Switches[WOODLAWN][CHAN_NUMBER], Switches[WOODLAWN][AIU_NUMBER], Switches[WOODLAWN][TIU_NUMBER], 0);
		
	elseif (CurrentBlockNo == ALIQUIPPA_BLOCK) then -- "Aliquippa Block" -- Westbound
		LastBlockNo = J_L_THRU_TRACK;
		NextBlockNo = WEST_ALIQUIPPA_BLOCK;
		MultiSetOccupancy(LocalHeadway and {NextBlockNo} or {NextBlockNo, MONACA_BLOCK, FALLSTON_BLOCK, BEAVER_FALLS_BLOCK, COLLEGE_THRU_TRACK}, MyEngineNo | (MyDirection << 8));	
		----MultiSetOccupancy({NextBlockNo, MONACA_BLOCK, FALLSTON_BLOCK, BEAVER_FALLS_BLOCK, COLLEGE_THRU_TRACK}, MyEngineNo | (MyDirection << 8));	
			
	elseif (CurrentBlockNo == WEST_ALIQUIPPA_BLOCK) then -- "West Aliquippa Block" -- Westbound
		LastBlockNo = J_L_THRU_TRACK;
		NextBlockNo = MONACA_BLOCK;
		MultiSetOccupancy(LocalHeadway and {NextBlockNo} or {NextBlockNo, FALLSTON_BLOCK, BEAVER_FALLS_BLOCK, COLLEGE_THRU_TRACK}, MyEngineNo | (MyDirection << 8));	
		----MultiSetOccupancy({NextBlockNo, FALLSTON_BLOCK, BEAVER_FALLS_BLOCK, COLLEGE_THRU_TRACK}, MyEngineNo | (MyDirection << 8));	

	elseif (CurrentBlockNo == MONACA_BLOCK) then -- "Monaca Block" -- Westbound
		-- if starting in Monaca Block, the sound volume will have been turned down so we need a default restore value;
		EngineSoundSave = 90;
		LastBlockNo = WEST_ALIQUIPPA_BLOCK;
		NextBlockNo = FALLSTON_BLOCK;
		MultiSetOccupancy(LocalHeadway and {NextBlockNo} or {NextBlockNo, BEAVER_FALLS_BLOCK, COLLEGE_THRU_TRACK}, MyEngineNo | (MyDirection << 8));	
		----MultiSetOccupancy({NextBlockNo, BEAVER_FALLS_BLOCK, COLLEGE_THRU_TRACK}, MyEngineNo | (MyDirection << 8));	

	elseif (CurrentBlockNo == FALLSTON_BLOCK) then -- "Fallston Block" -- Westbound
		LastBlockNo = MONACA_BLOCK;
		NextBlockNo = BEAVER_FALLS_BLOCK;
		MultiSetOccupancy(LocalHeadway and {NextBlockNo} or {NextBlockNo, COLLEGE_THRU_TRACK}, MyEngineNo | (MyDirection << 8));	
		----MultiSetOccupancy({NextBlockNo, COLLEGE_THRU_TRACK}, MyEngineNo | (MyDirection << 8));	
		
	elseif (CurrentBlockNo == BEAVER_FALLS_BLOCK) then -- "Beaver Falls Block" -- Westbound
		LastBlockNo = FALLSTON_BLOCK;
		NextBlockNo = COLLEGE_THRU_TRACK;
		-- check that the Next Block is not occupied
		SingleSetOccupancy({NextBlockNo}, MyEngineNo | (MyDirection << 8));
		
	elseif (CurrentBlockNo == COLLEGE_THRU_TRACK) then -- "College Thru Track" -- Westbound
		local flag;
		LastBlockNo = BEAVER_FALLS_BLOCK;
		NextBlockNo = NEW_CASTLE_JUNCTION_BLOCK;
		-- Additional test for Westbound trains in Headway mode starting from COLLEGE_THRU_TRACK:
		-- if in Headway, check all blocks (except last one) for presence of a eastbound train.
		-- if there is an eastbound train in any of those blocks, continue to wait.		
		if (LocalHeadway) then
			print("LocalHeadway Check from " .. BlockName[CurrentBlockNo]);
			flag = math.abs(GetFlag(NEW_CASTLE_JUNCTION_BLOCK));
			if (flag ~= UNOCCUPIED) then
					print(BlockName[NEW_CASTLE_JUNCTION_BLOCK] .. "(#" .. NEW_CASTLE_JUNCTION_BLOCK .. ") Occupied : Engine #" .. (flag & 0x00FF) .. " " .. DirectionNames[(flag & 0xFF00) >> 8]);
					if ((flag & 0xFF00) >> 8 == EB) then
						LocalHeadway = false;
						end;				
					end;
			flag = math.abs(GetFlag(LOWELLVILLE_BLOCK));
			if (flag ~= UNOCCUPIED) then		
					print(BlockName[LOWELLVILLE_BLOCK] .. "(#" .. LOWELLVILLE_BLOCK .. ") Occupied : Engine #" .. (flag & 0x00FF) .. " " .. DirectionNames[(flag & 0xFF00) >> 8]);
					if ((flag & 0xFF00) >> 8 == EB) then
						LocalHeadway = false;
						end;				
					end;

			flag = math.abs(GetFlag(STRUTHERS_STATION_TRACK));
			if (flag ~= UNOCCUPIED) then
					print(BlockName[STRUTHERS_STATION_TRACK] .. "(#" .. STRUTHERS_STATION_TRACK .. ") Occupied : Engine #" .. (flag & 0x00FF) .. " " .. DirectionNames[(flag & 0xFF00) >> 8]);
					if ((flag & 0xFF00) >> 8 == EB) then
						LocalHeadway = false;
						end;				
					end;

			flag = math.abs(GetFlag(ALIQUIPPA_CUTOFF));
			if (flag ~= UNOCCUPIED) then
					print(BlockName[ALIQUIPPA_CUTOFF] .. "(#" .. ALIQUIPPA_CUTOFF .. ") Occupied : Engine #" .. (flag & 0x00FF) .. " " .. DirectionNames[(flag & 0xFF00) >> 8]);
					if ((flag & 0xFF00) >> 8 == EB) then
						LocalHeadway = false;
						end;				
					end;			
			print("Startup Headway set to " .. tostring(LocalHeadway));
		end;
		--
		MultiSetOccupancy(LocalHeadway and {NextBlockNo} or {NextBlockNo, LOWELLVILLE_BLOCK, STRUTHERS_STATION_TRACK, ALIQUIPPA_CUTOFF}, MyEngineNo | (MyDirection << 8));				
		----MultiSetOccupancy({NextBlockNo, LOWELLVILLE_BLOCK, STRUTHERS_THRU_TRACK, ALIQUIPPA_CUTOFF}, MyEngineNo | (MyDirection << 8));				
		-- Position COLLEGE_WEST after locking next block
		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 normally use the station track
		LastBlockNo = BEAVER_FALLS_BLOCK;
		NextBlockNo = NEW_CASTLE_JUNCTION_BLOCK;
		MultiSetOccupancy(LocalHeadway and {NextBlockNo} or {NextBlockNo, LOWELLVILLE_BLOCK, STRUTHERS_STATION_TRACK, ALIQUIPPA_CUTOFF}, MyEngineNo | (MyDirection << 8));
		----MultiSetOccupancy({NextBlockNo, LOWELLVILLE_BLOCK, STRUTHERS_THRU_TRACK}, MyEngineNo | (MyDirection << 8));
		-- Position COLLEGE_WEST after locking next block
		Switch(REVERSE, Switches[COLLEGE_WEST][CHAN_NUMBER], Switches[COLLEGE_WEST][AIU_NUMBER], Switches[COLLEGE_WEST][TIU_NUMBER], 0);
		
	elseif (CurrentBlockNo == NEW_CASTLE_JUNCTION_BLOCK) then -- "New Castle Junction Block" -- Westbound
		LastBlockNo = COLLEGE_THRU_TRACK;
		NextBlockNo = LOWELLVILLE_BLOCK;
		MultiSetOccupancy(LocalHeadway and {NextBlockNo} or {NextBlockNo, STRUTHERS_STATION_TRACK, ALIQUIPPA_CUTOFF}, MyEngineNo | (MyDirection << 8));	
		----MultiSetOccupancy({NextBlockNo, STRUTHERS_THRU_TRACK}, MyEngineNo | (MyDirection << 8));	
		
	elseif (CurrentBlockNo == LOWELLVILLE_BLOCK) then -- "Lowellville Block" -- Westbound
		LastBlockNo = NEW_CASTLE_JUNCTION_BLOCK;
		NextBlockNo = STRUTHERS_STATION_TRACK;
		MultiSetOccupancy(LocalHeadway and {NextBlockNo} or {NextBlockNo, ALIQUIPPA_CUTOFF}, MyEngineNo | (MyDirection << 8));	
		
	elseif (CurrentBlockNo == STRUTHERS_STATION_TRACK) then -- "Struthers Station Track" -- Westbound
		LastBlockNo = LOWELLVILLE_BLOCK;
		NextBlockNo = ALIQUIPPA_CUTOFF;
		-- CAMPBELL switch must be in REVERSE to not foul the points
		Switch(REVERSE, Switches[CAMPBELL][CHAN_NUMBER], Switches[CAMPBELL][AIU_NUMBER], Switches[CAMPBELL][TIU_NUMBER], 0)
		--print("Campbell Yard Switch to REVERSE"); Sleep(2);
		-- check that the Next Block is not occupied
		SingleSetOccupancy({NextBlockNo}, MyEngineNo | (MyDirection << 8));
		end;

	end;

--[[--------------------------------- Eastbound --------------------------------------------]]
if (MyDirection == EB) then
	local LocalHeadway = Headway;
	--
	-- Engine can start in any cutoff route block, set exit switches from meet points as necessary
	-- Eastbound engines NEVER normally run in Headway but you man manually set the Headway flag
	--	
	if (CurrentBlockNo == ALIQUIPPA_CUTOFF) then -- "Aliquippa Cutoff"	Eastbound
		LastBlockNo = ALIQUIPPA_BLOCK;
		NextBlockNo = STRUTHERS_STATION_TRACK;
		--MultiSetOccupancy(LocalHeadway and {NextBlockNo} or {NextBlockNo, LOWELLVILLE_BLOCK, NEW_CASTLE_JUNCTION_BLOCK, COLLEGE_STATION_TRACK}, MyEngineNo | (MyDirection << 8));				
		SingleSetOccupancy({NextBlockNo}, MyEngineNo | (MyDirection << 8));
		
	elseif (CurrentBlockNo == STRUTHERS_STATION_TRACK) then -- "Struthers Station Track"	Eastbound
		LastBlockNo = ALIQUIPPA_CUTOFF;
		NextBlockNo = LOWELLVILLE_BLOCK;
		
		-- Additional test for eastbound trains in Headway mode starting from STRUTHERS_STATION_TRACK:
		-- if in Headway, check all blocks (except last one) for presence of a westbound train.
		-- if there is an westbound train in any of those blocks, continue to wait.
		if (LocalHeadway) then
			print("LocalHeadway Check from " .. BlockName[CurrentBlockNo]);
			flag = math.abs(GetFlag(LOWELLVILLE_BLOCK));
			if (flag ~= UNOCCUPIED) then		
					print(BlockName[LOWELLVILLE_BLOCK] .. "(#" .. LOWELLVILLE_BLOCK .. ") Occupied : Engine #" .. (flag & 0x00FF) .. " " .. DirectionNames[(flag & 0xFF00) >> 8]);
					if ((flag & 0xFF00) >> 8 == WB) then
						LocalHeadway = false;
						end;				
					end;

			flag = math.abs(GetFlag(NEW_CASTLE_JUNCTION_BLOCK));				
			if (flag ~= UNOCCUPIED) then
					print(BlockName[NEW_CASTLE_JUNCTION_BLOCK] .. "(#" .. NEW_CASTLE_JUNCTION_BLOCK .. ") Occupied : Engine #" .. (flag & 0x00FF) .. " " .. DirectionNames[(flag & 0xFF00) >> 8]);
					if ((flag & 0xFF00) >> 8 == WB) then
						LocalHeadway = false;
						end;				
					end;
			-- don't need to check next passing track (COLLEGE_STATION_TRACK)
			print("Startup Headway set to " .. tostring(LocalHeadway));
		end;
		
		-- CAMPBELL switch must be in REVERSE to not foul the points
		Switch(REVERSE, Switches[CAMPBELL][CHAN_NUMBER], Switches[CAMPBELL][AIU_NUMBER], Switches[CAMPBELL][TIU_NUMBER], 0)
		--print("Campbell Yard Switch to REVERSE"); Sleep(2);
		MultiSetOccupancy(LocalHeadway and {NextBlockNo} or {NextBlockNo, NEW_CASTLE_JUNCTION_BLOCK, COLLEGE_STATION_TRACK}, MyEngineNo | (MyDirection << 8));				
		
	elseif (CurrentBlockNo == LOWELLVILLE_BLOCK) then -- "Lowellville Block"	Eastbound
		LastBlockNo = STRUTHERS_STATION_TRACK;
		NextBlockNo = NEW_CASTLE_JUNCTION_BLOCK;
		MultiSetOccupancy(LocalHeadway and {NextBlockNo} or {NextBlockNo, COLLEGE_STATION_TRACK}, MyEngineNo | (MyDirection << 8));
		--MultiSetOccupancy({NextBlockNo, COLLEGE_STATION_TRACK}, MyEngineNo | (MyDirection << 8));
		
	elseif (CurrentBlockNo == NEW_CASTLE_JUNCTION_BLOCK) then -- "New Castle Junction Block"
		LastBlockNo = LOWELLVILLE_BLOCK;
		NextBlockNo = COLLEGE_STATION_TRACK;
		-- check that the Next Block is not occupied
		SingleSetOccupancy({NextBlockNo}, MyEngineNo | (MyDirection << 8));				
			
	elseif (CurrentBlockNo == COLLEGE_STATION_TRACK) then -- "College Station Track"	Eastbound
		LastBlockNo = NEW_CASTLE_JUNCTION_BLOCK;
		NextBlockNo = BEAVER_FALLS_BLOCK;
		
		-- Additional test for eastbound trains in Headway mode starting from COLLEGE_STATION_TRACK:
		-- if in Headway, check all blocks (except last one) for presence of a westbound train.
		-- if there is an westbound train in any of those blocks, continue to wait.
		if (LocalHeadway) then
			print("LocalHeadway Check from " .. BlockName[CurrentBlockNo]);		
			flag = math.abs(GetFlag(BEAVER_FALLS_BLOCK));
			if (flag ~= UNOCCUPIED) then
					print(BlockName[BEAVER_FALLS_BLOCK] .. "(#" .. BEAVER_FALLS_BLOCK .. ") Occupied : Engine #" .. (flag & 0x00FF) .. " " .. DirectionNames[(flag & 0xFF00) >> 8]);
					if ((flag & 0xFF00) >> 8 == WB) then
						LocalHeadway = false;
						end;
					end;
			flag = math.abs(GetFlag(FALLSTON_BLOCK));
			if (flag ~= UNOCCUPIED) then
					print(BlockName[FALLSTON_BLOCK] .. "(#" .. FALLSTON_BLOCK .. ") Occupied : Engine #" .. (flag & 0x00FF) .. " " .. DirectionNames[(flag & 0xFF00) >> 8]);
					if ((flag & 0xFF00) >> 8 == WB) then
						LocalHeadway = false;
						end;
					end;
			flag = math.abs(GetFlag(MONACA_BLOCK));
			if (flag ~= UNOCCUPIED) then
					print(BlockName[MONACA_BLOCK] .. "(#" .. MONACA_BLOCK .. ") Occupied : Engine #" .. (flag & 0x00FF) .. " " .. DirectionNames[(flag & 0xFF00) >> 8]);
					if ((flag & 0xFF00) >> 8 == WB) then
						LocalHeadway = false;
						end;
					end;
			flag = math.abs(GetFlag(WEST_ALIQUIPPA_BLOCK)); 
			if (flag ~= UNOCCUPIED) then 
					print(BlockName[WEST_ALIQUIPPA_BLOCK] .. "(#" .. WEST_ALIQUIPPA_BLOCK .. ") Occupied : Engine #" .. (flag & 0x00FF) .. " " .. DirectionNames[(flag & 0xFF00) >> 8]);
					if ((flag & 0xFF00) >> 8 == WB) then
						LocalHeadway = false;
						end;
					end;
			flag = math.abs(GetFlag(ALIQUIPPA_BLOCK));				
			if (flag ~= UNOCCUPIED) then	
					print(BlockName[ALIQUIPPA_BLOCK] .. "(#" .. ALIQUIPPA_BLOCK .. ") Occupied : Engine #" .. (flag & 0x00FF) .. " " .. DirectionNames[(flag & 0xFF00) >> 8]);
					if ((flag & 0xFF00) >> 8 == WB) then
						LocalHeadway = false;
						end;
					end;

			flag = math.abs(GetFlag(ALIQUIPPA_CUTOFF));				
			if (flag ~= UNOCCUPIED) then	
					print(BlockName[ALIQUIPPA_CUTOFF] .. "(#" .. ALIQUIPPA_CUTOFF .. ") Occupied : Engine #" .. (flag & 0x00FF) .. " " .. DirectionNames[(flag & 0xFF00) >> 8]);
					if ((flag & 0xFF00) >> 8 == WB) then
						LocalHeadway = false;
						end;
					end;			
			print("Startup Headway set to " .. tostring(LocalHeadway));
		end;
				
		MultiSetOccupancy(LocalHeadway and {NextBlockNo} or {NextBlockNo, FALLSTON_BLOCK, MONACA_BLOCK, WEST_ALIQUIPPA_BLOCK, ALIQUIPPA_BLOCK, ALIQUIPPA_CUTOFF}, MyEngineNo | (MyDirection << 8));				
		--MultiSetOccupancy({NextBlockNo, FALLSTON_BLOCK, MONACA_BLOCK, WEST_ALIQUIPPA_BLOCK, ALIQUIPPA_BLOCK, ALIQUIPPA_CUTOFF}, MyEngineNo | (MyDirection << 8));				
		-- Position COLLEGE_EAST after locking next block
		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 normally use the thru track
		LastBlockNo = NEW_CASTLE_JUNCTION_BLOCK;
		NextBlockNo = BEAVER_FALLS_BLOCK;
		MultiSetOccupancy(LocalHeadway and {NextBlockNo} or {NextBlockNo, FALLSTON_BLOCK, MONACA_BLOCK, WEST_ALIQUIPPA_BLOCK, ALIQUIPPA_BLOCK, ALIQUIPPA_CUTOFF}, MyEngineNo | (MyDirection << 8));				
		--MultiSetOccupancy({NextBlockNo, FALLSTON_BLOCK, MONACA_BLOCK, WEST_ALIQUIPPA_BLOCK, ALIQUIPPA_BLOCK, ALIQUIPPA_CUTOFF}, MyEngineNo | (MyDirection << 8));				
		-- Position COLLEGE_EAST after locking next block
		Switch(NORMAL, Switches[COLLEGE_EAST][CHAN_NUMBER], Switches[COLLEGE_EAST][AIU_NUMBER], Switches[COLLEGE_EAST][TIU_NUMBER], 0);
		
	elseif (CurrentBlockNo == BEAVER_FALLS_BLOCK) then -- "Beaver Falls Block"	Eastbound
		LastBlockNo = COLLEGE_STATION_TRACK;
		NextBlockNo = FALLSTON_BLOCK;
		MultiSetOccupancy(LocalHeadway and {NextBlockNo} or {NextBlockNo, MONACA_BLOCK, WEST_ALIQUIPPA_BLOCK, ALIQUIPPA_BLOCK, ALIQUIPPA_CUTOFF}, MyEngineNo | (MyDirection << 8));	
		--MultiSetOccupancy({NextBlockNo, MONACA_BLOCK, WEST_ALIQUIPPA_BLOCK, ALIQUIPPA_BLOCK, ALIQUIPPA_CUTOFF}, MyEngineNo | (MyDirection << 8));	

	elseif (CurrentBlockNo == FALLSTON_BLOCK) then -- "Fallston Block"	Eastbound
		LastBlockNo = BEAVER_FALLS_BLOCK;
		NextBlockNo = MONACA_BLOCK;
		MultiSetOccupancy(LocalHeadway and {NextBlockNo} or {NextBlockNo, WEST_ALIQUIPPA_BLOCK, ALIQUIPPA_BLOCK, ALIQUIPPA_CUTOFF}, MyEngineNo | (MyDirection << 8));
		--MultiSetOccupancy({NextBlockNo, WEST_ALIQUIPPA_BLOCK, ALIQUIPPA_BLOCK, ALIQUIPPA_CUTOFF}, MyEngineNo | (MyDirection << 8));
		
	elseif (CurrentBlockNo == MONACA_BLOCK) then -- "Monaca Block"	Eastbound
		-- if starting in Monaca Block, the sound volume will have been turned down so we need a default restore value;
		EngineSoundSave = 90;	
		LastBlockNo = FALLSTON_BLOCK;
		NextBlockNo = WEST_ALIQUIPPA_BLOCK;
		MultiSetOccupancy(LocalHeadway and {NextBlockNo} or {NextBlockNo, ALIQUIPPA_BLOCK,  ALIQUIPPA_CUTOFF}, MyEngineNo | (MyDirection << 8));
		--MultiSetOccupancy({NextBlockNo, ALIQUIPPA_BLOCK,  ALIQUIPPA_CUTOFF}, MyEngineNo | (MyDirection << 8));

	elseif (CurrentBlockNo == WEST_ALIQUIPPA_BLOCK) then -- "West Aliquippa Block"	Eastbound
		LastBlockNo = MONACA_BLOCK;
		NextBlockNo = ALIQUIPPA_BLOCK;
		MultiSetOccupancy(LocalHeadway and {NextBlockNo} or {NextBlockNo, ALIQUIPPA_CUTOFF}, MyEngineNo | (MyDirection << 8));
		--MultiSetOccupancy({NextBlockNo, ALIQUIPPA_CUTOFF}, MyEngineNo | (MyDirection << 8));

	elseif (CurrentBlockNo == ALIQUIPPA_BLOCK) then -- "Aliquippa Block"	Eastbound
		LastBlockNo = WEST_ALIQUIPPA_BLOCK;
		NextBlockNo = ALIQUIPPA_CUTOFF;
		-- check that the Next Block is not occupied
		SingleSetOccupancy({NextBlockNo}, MyEngineNo | (MyDirection << 8));
		end;

	end;
--
DisplayBlockNames("Start Run : ", LastBlockNo, CurrentBlockNo, NextBlockNo);
--
RFIDFlag = true;
Whistle(ON, 20, MyEngineNo, MyTIUNo);
Whistle(OFF, 22, MyEngineNo, MyTIUNo);
local Odo = DTO(MyEngineNo, MyTIUNo);
if (Odo ~= nil) then
	print("Trip Odometer " .. Odo);
	end;
Sleep(20);

--
BlockSpeedLimit = BlockSpeed[MyDirection][StartBlockNo];
LastSentSpeed = SetSpeed(BlockSpeedLimit, 5, MyEngineNo, MyTIUNo);
SetCounter(C3, BlockSpeedLimit);
LastBlockSpeedLimit = BlockSpeedLimit;
--	
--[[----------------------------------------------------]]
print(string.format("RunTime = %.2f seconds", RunTime()));
--
return true;
end
Function07Name, Function07Label = StartButton, "Start Run";
--[[---------------------------------------------------------------------------------------]]
--[[ Not Used
function event(FromPC, Type, P1, P2, P3)
print("event() Running - From PC# ", FromPC, " EventType = ", Type, " Parameters = ", P1, " ", P2, " ", P3);
return true;		-- false=event failed, true=event succeeded	
end;
--]]
--[[---------------------------------------------------------------------------------------]]

function OccButton(Eng, TIU)
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";
--[[---------------------------------------------------------------------------------------]]

function ResetButton()
-- Run the engine back to the start (in reverse)
if (ResetinProgress) then
	return;	-- already being reset
	end;
print("Return to Initial Position");
-- stop the engine
LastSentSpeed = SetSpeed(0, 0, MyEngineNo, MyTIUNo);
RFIDFlag = false;
Sleep(5);
--[[-----------------------------]]
InitButton(MyEngineNo, MyTIUNo);

-- position yard switch as needed
-- depending on value of StartBlockNo
if (StartBlockNo == MCKEES_ROCKS_REPAIR_SHOPS_YARD1_TRACK) then
	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);

elseif (StartBlockNo == MCKEES_ROCKS_REPAIR_SHOPS_YARD2_TRACK) then
	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);

elseif (StartBlockNo == GATEWAY_YARD1_TRACK) then
	Switch(NORMAL, Switches[GATEWAY_YARD][CHAN_NUMBER], Switches[GATEWAY_YARD][AIU_NUMBER], Switches[GATEWAY_YARD][TIU_NUMBER], 0);

elseif (StartBlockNo == GATEWAY_YARD2_TRACK) then
	Switch(REVERSE, Switches[GATEWAY_YARD][CHAN_NUMBER], Switches[GATEWAY_YARD][AIU_NUMBER], Switches[GATEWAY_YARD][TIU_NUMBER], 0);
	end;
--
-- start the engine
if (not StartedFlag) then
	print("Starting up the Engine");
	EngineStartUp(MyEngineNo, MyTIUNo);
	StartedFlag = true;
	Sleep(20);
	end
-- direction to reverse
SetDirection(REVERSE, 0, MyEngineNo, MyTIUNo);
-- 
BlockSpeedLimit = BlockSpeed[MyDirection][CurrentBlockNo];
LastSentSpeed = SetSpeed(BlockSpeedLimit, 5, MyEngineNo, MyTIUNo);
SetCounter(C3, BlockSpeedLimit);
LastBlockSpeedLimit = BlockSpeedLimit;
--
ResetinProgress = true;
return true
end;
Function12Name, Function12Label = ResetButton, "Initial Position";
--[[---------------------------------------------------------------------------------------]]

function ZeroSpeedButton()
	print("Manually set Speed to 0 Smph, was " .. LastSentSpeed .. " Smph");
	SetSpeed(0, 0, MyEngineNo, MyTIUNo);	
	return true;
end
Function09Name, Function09Label = ZeroSpeedButton, "Set Speed = 0";
--[[---------------------------------------------------------------------------------------]]

function ResumeSpeedButton()
	-- resume speed at LastSentSpeed
	print("Manually Resumed Speed " .. LastSentSpeed .. " Smph");
	LastSentSpeed = SetSpeed(LastSentSpeed, 0, MyEngineNo, MyTIUNo);	
	return true;
end
Function10Name, Function10Label = ResumeSpeedButton, "Resume Speed";
--[[---------------------------------------------------------------------------------------]]

function LoadButton()
	print("Loading layout4.lua");
	package.loaded["layout4"] = nil;	-- unload
	Layout = require([[layout4]]);	-- All of the details about the Layout
	Layout.SetRoute(100);	-- indicate Aliquippa Cutoff route
	return true;
end
Function03Name, Function03Label = LoadButton, "(Re)Load";
--[[---------------------------------------------------------------------------------------]]

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()
	if (Layout.LLLoop == true) then
		-- Loop Configuration
		print("Loop Configuration");
		Switch(REVERSE, Switches[BALLOON][CHAN_NUMBER], Switches[BALLOON][AIU_NUMBER], Switches[BALLOON][TIU_NUMBER], 0);	
		else
		-- Point-to-Point Configuration
		print("Point-to-Point Configuration");
		Switch(NORMAL, Switches[BALLOON][CHAN_NUMBER], Switches[BALLOON][AIU_NUMBER], Switches[BALLOON][TIU_NUMBER], 0);	
		end;
	return true;
end
Function16Name, Function16Label = LLLButton, "Balloon/Pt-to-Pt";
--[[---------------------------------------------------------------------------------------]]
