--[[
---------------------------------------------------------------------------
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";
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 _;
--
--[[---------------------------------------------------------------------------------------]]
--[[
On setup(), asks for starting block, sets flags, then sends Block Occupancy
event to APB Master for initial conditions. APB Master uses the information
to keep its own Layout4BlockOcc[] up to date.
Gets tags using tags()
Sets and clears flags based on tags received.
Does not request Events from Flags in layout window (does not call ReqBlockEvents(true))

]]
-- 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
--[[
There is a tag detection case that is not handled. If the engine passes a detector
and the next block is AsClear, and then before the caboose passses the detector, the next
block goes to AsStop or AsStopandProceed, it is too late to respond correctly
to the new aspect.
--]]
--[[
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.

Headway, often called a Following Movement, authority must be from a Timetable
or received via a Train Order.
--]]
local Headway = false;	-- 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 RestrictedSpeedFlag = false;	-- set to true to force restricted speed in the next block after a Stop and Proceed indication
--[[---------------------------------------------------------------------------------------]]
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 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;
			Message(BlockName[CurrentBlockNo]);	-- display the current block
			-- 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, RestrictedSpeedFlag);	-- adjust speed for some randomness in high speed blocks
			RestrictedSpeedFlag = false;
			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[math.abs(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) then
				if (Aspect == AsStopandProceed) then
					-- stop in this block
					printc(clGreen, "ML2P : Stop and Proceed at Restricted Speed");
					-- stop in CurrentBlockNo
					StopDelay, Target_Distance = StopBlockAfterDelay(Layout, true, CurrentBlockNo, MyEngineNo, MyDirection, LastBlockSpeedLimit, BlockSpeedLimit)
					EngineStopped = true;
					BlockSpeedLimit = RESTRICTED_SPEED;	-- proceed at Restricted Speed
					RestrictedSpeedFlag = true;
					GotBlock = false;
					StopTime = RunTime();				
					else
					printc(clGreen, "ML2P : Home Signal aspect is Clear or Approach");
					end;
				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
					RestrictedSpeedFlag = false;
					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;
								RestrictedSpeedFlag = false;
								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;
							RestrictedSpeedFlag = false;
							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;
								RestrictedSpeedFlag = false;
								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;
							RestrictedSpeedFlag = false;
							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[math.abs(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) then
						if (Aspect == AsStopandProceed) then
							printc(clGreen, "ML2P : Stop and Proceed at Restricted Speed");
							Sleep(10);
							end;
						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)
	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 MainLine2MainLine(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("ML2ML : 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("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;
			Message(BlockName[CurrentBlockNo]);	-- display the current block
			-- 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, RestrictedSpeedFlag);	-- adjust speed for some randomness in high speed blocks
			RestrictedSpeedFlag = false;
			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[math.abs(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) then
				if (Aspect == AsStopandProceed) then
					-- stop in this block
					printc(clGreen, "ML2ML : Stop and Proceed at Restricted Speed");
					-- stop in CurrentBlockNo
					StopDelay, Target_Distance = StopBlockAfterDelay(Layout, true, CurrentBlockNo, MyEngineNo, MyDirection, LastBlockSpeedLimit, BlockSpeedLimit)
					EngineStopped = true;
					BlockSpeedLimit = RESTRICTED_SPEED;	-- proceed at Restricted Speed
					RestrictedSpeedFlag = true;
					GotBlock = false;
					StopTime = RunTime();				
					else
					printc(clGreen, "ML2ML : Home Signal aspect is Clear or Approach");
					end;
				end;
			if (Aspect == AsStopSignal) then
				return EmergencyStop("ML2ML : Home Signal 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 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
					RestrictedSpeedFlag = false;
					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
						RestrictedSpeedFlag = false;
						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 -----]]
			-- N/A	
--[[--- 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 .. ")");
			-- on any MainLine exit readers, the switch is before the tag reader so 
			-- the SwitchClearingDistance is zero
			--
			-- Once the caboose is detected (with any clearing delay) return switch at entrance of current back
			-- to NORMAL - this switch was set as result of the engine leaving the last 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("ML2ML : " .. 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("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;
--[[--- 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[math.abs(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) then
						if (Aspect == AsStopandProceed) then
							printc(clGreen, "ML2ML : Stop and Proceed at Restricted Speed");
							Sleep(10);
							end;
						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;
			Message(BlockName[CurrentBlockNo]);	-- display the current block
			-- 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, RestrictedSpeedFlag);	-- adjust speed for some randomness in high speed blocks
			RestrictedSpeedFlag = false;
			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[math.abs(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) then
				if (Aspect == AsStopSignal) then
					-- stop in this block
					printc(clGreen, "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();
					else
					printc(clGreen, "P2ML : Home Signal aspect is Clear or Approach");
					end;
				end;
			if (Aspect == AsStopandProceed) then
				return EmergencyStop("P2ML : Home Signal aspect is an unexpected permissive signal.");
				end;				
--[[--- 5 ASAP, set the switch(es) at engine detection 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
					RestrictedSpeedFlag = false;
					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
						RestrictedSpeedFlag = false;
						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
--[[--- 3.5 set next switch after occupancy lock 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[math.abs(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) then
						if (Aspect == AsStopSignal) then
							printc(clGreen, "P2ML : Stop Signal - Wait for Stop Signal to Clear");
							WaitforStopSignaltoClear();		
							else
							printc(clGreen, "P2ML : Home Signal aspect is Clear or Approach");
							end;
						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;
			Message(BlockName[CurrentBlockNo]);	-- display the current block
			-- 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, RestrictedSpeedFlag);	-- adjust speed for some randomness in high speed blocks
			RestrictedSpeedFlag = false;
			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[math.abs(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) then
				if (Aspect == AsStopandProceed) then
					-- stop in this block
					printc(clGreen, "ML2Y : Stop and Proceed at Restricted Speed");
					-- stop in CurrentBlockNo
					StopDelay, Target_Distance = StopBlockAfterDelay(Layout, true, CurrentBlockNo, MyEngineNo, MyDirection, LastBlockSpeedLimit, BlockSpeedLimit)
					EngineStopped = true;
					BlockSpeedLimit = RESTRICTED_SPEED;	-- proceed at Restricted Speed
					RestrictedSpeedFlag = true;
					GotBlock = false;
					StopTime = RunTime();
					else
					printc(clGreen, "ML2Y : Home Signal aspect is Clear or Approach");
					end;
				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
					RestrictedSpeedFlag = false;
					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[math.abs(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) then
						if (Aspect == AsStopandProceed) then
							printc(clGreen, "ML2Y : Stop and Proceed at Restricted Speed");
							Sleep(10);
							end;
						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;
			Message(BlockName[CurrentBlockNo]);	-- display the current block
			-- 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, RestrictedSpeedFlag);	-- adjust speed for some randomness in high speed blocks
			RestrictedSpeedFlag = false;
			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[math.abs(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;
			RestrictedSpeedFlag = false;
			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-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;
		Message(BlockName[CurrentBlockNo]);	-- display the saved current block
		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)
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 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 at least 8 tag detectors and 13 tag readers
local NumDetectors, NumReaders;
NumDetectors, NumReaders = GetTagReaders();
print("Number of Tag Detectors = " .. NumDetectors .. " Number of Tag Readers =  " .. NumReaders);
if ((Debug() == 0) and (NumDetectors < 8 or (NumReaders < 13 and NumReaders ~= 0))) then
	return Stop("Requires at least 8 Tag Detectors and 13 Tag Readers. Cycle layout power and try again.");
	end;	
--
	
if ((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, EngineNameCount - 1, 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, 2, {"1 Westbound", "2 Eastbound"});
--print("DirValue ", DirValue);
typ = type(DirValue);
--print("Type ", typ);

if (typ == "boolean" and DirValue == false) then
	return Stop("Canceled");
	end;
		
if (DirValue ~= nil and DirValue ~= "") then	-- if the result is a string, then use first number in the string, otherwise, ignore it
	-- pull first integer found in the string
	local DirNo = string.match(DirValue, "%d+");
	if (DirNo ~= nil) then
		MyDirection = tonumber(DirNo);
		else
		return Stop("Invalid Direction");
		end;
	else
	if (typ == "nil") then
		-- nil returned only if parameters are invalid
		return Stop("Invalid Parameters to InputDropDown");
		else
		--MyDirection is not changed
		end
	end	
if (MyDirection == WB) then
	title = "WB Superior"; -- Westbound
	OppDirection = EB;
	Headway = true;	-- Allow westbound trains to make headway when possible
	Signals = true;	-- Set to true to incorporate absolute signals and stop/proceed signals into the decision making
	end;
if (MyDirection == EB) then
	title = "EB"; -- Eastbound
	OppDirection = WB;
	Headway = false;
	Signals = true;	-- Set to true to incorporate absolute signals and stop/proceed signals into the decision making
	end;
--[[--------------------------------------------]]	
Title(title .. " Engine #" .. MyEngineNo .. " " .. Name, MyEngineNo);
print("Setup : Direction = " .. DirectionNames[MyDirection]);
--[[--------------------------------------------]]
	--
	-- Ask the dispatcher for starting block number
	--
	-- For superior westbound trains, starting block can be any Thru block or any mainline block
	-- For inferior eastbound trains, starting block can be any Station/Passing block or any mainline block
	--
	local BlockNameList = {};	-- table to hold all of the block numbers and names
	local BlockNameIndex = 1;
	local BlockNameCount = 1;

	for iBlock = 1, BLOCK_COUNT do
		local Name = BlockName[iBlock];
		if (Name ~= nil and iBlock <= LAST_BLOCK) then
			-- blocks not valid for EOB
			if ((iBlock == BYPASS) or (iBlock == ALIQUIPPA_CUTOFF)) 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 Youngstown Yard
				if ((iBlock ~= J_L_PASSING_TRACK) and (iBlock ~= COLLEGE_STATION_TRACK) and (iBlock ~= STRUTHERS_STATION_TRACK) and
					(iBlock ~= GATEWAY_YARD1_TRACK) and (iBlock ~= GATEWAY_YARD2_TRACK)) then
					BlockNameList[BlockNameCount] = iBlock .. " " .. Name; 
					--print("BlockNameList[" .. BlockNameCount .. "] = " .. BlockNameList[BlockNameCount]);
					--------------------------------------------
					if (StartBlockNo == iBlock) then
						BlockNameIndex = BlockNameCount;
						print("BlockNameIndex = " .. BlockNameIndex);
						end;
					BlockNameCount = BlockNameCount + 1;		
					--------------------------------------------
					end;
				end;
			end;
		::nextBlock::
		end;
--
-- read Starting Block Number
print("Select Starting Block");
CurrentBlockNo = StartBlockNo;
Message(BlockName[CurrentBlockNo]);	-- display the saved start block
-- Debug
--
local StartingBlock = InputDropDown(title, "Select Starting Block", BlockNameIndex, BlockNameCount + 1, BlockNameList);
--print("StartingBlock ", StartingBlock);
typ = type(StartingBlock);
--print("Type ", typ);

if (typ == "boolean" and StartingBlock == false) then
	return Stop("Canceled");
	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;
Message(BlockName[CurrentBlockNo]);	-- display the newly selected start block
--[[--------------------------------------------]]

	-- 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
			-- blocks not valid for EOB
			if ((iBlock == BYPASS) or (iBlock == ALIQUIPPA_CUTOFF)) 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 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, BlockNameCount + 1; 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
-- notify the APB Master script of intitial block occpancy conditions.
-- This is the only place where we send this event to the APB Master
print("SetEvent to APB Master - Block: " .. BlockName[CurrentBlockNo] .. " - Direction: " .. MyDirection);
SetEvent(1, BLOCK_OCCUPANCY_EVENT, CurrentBlockNo, OCCUPIED, MyDirection);
	
-- 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
--[[--------------------------]]
if (MyDirection == WB) then
	--
	-- ResetinProgress probably does not work anymore
	--
	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;
		
	-- MCKEES_ROCKS_REPAIR_SHOPS_YARD_TAG Detected westbound
	elseif (Detector == MCKEES_ROCKS_REPAIR_SHOPS_YARD_DETECTOR and Reader == MCKEES_ROCKS_REPAIR_SHOPS_YARD_READER) then
		if (Func30 == nil) then
			Func30 = createMainLine2Passing(false);
			end;
		-- status: just entered NextBlockNo, occupancy already set
		Func30("@ MCKEES_ROCKS_REPAIR_SHOPS_YARD_TAG ", EngineNo, TagLocation, CarModel, J_L_THRU_TRACK, J_L_PASSING_TRACK, nil);

	-- ALIQUIPPA_EAST_TAG Detected westbound
	elseif (Detector == ALIQUIPPA_EAST_DETECTOR and Reader == ALIQUIPPA_EAST_READER) then
		if (Func51 == nil) then
			Func51 = createPassing2MainLine(false, SwitchClearingInches[ALIQUIPPA_EAST_TAG][MyDirection]);
			end;
		-- status: in block NextBlockNo, occupancy already set
		Func51("@ ALIQUIPPA_EAST_TAG ", EngineNo, TagLocation, CarModel, J_L_THRU_TRACK, J_L_PASSING_TRACK, ALIQUIPPA_EAST, ALIQUIPPA_WEST, 
			{ALIQUIPPA_BLOCK, WEST_ALIQUIPPA_BLOCK, MONACA_BLOCK, FALLSTON_BLOCK, BEAVER_FALLS_BLOCK, COLLEGE_THRU_TRACK});

	-- ALIQUIPPA_WEST_TAG Detected westbound
	elseif (Detector == ALIQUIPPA_WEST_DETECTOR and Reader == ALIQUIPPA_WEST_READER) then
		if (Func10 == nil) then
			Func10 = createMainLine2MainLine(false);
			end;
		-- status: just entered NextBlockNo, occupancy already set
		-- Set ALIQUIPPA_WEST to NORMAL on Caboose detection.
		Func10("@ ALIQUIPPA_WEST_TAG ", EngineNo, TagLocation, CarModel, ALIQUIPPA_WEST,
			{WEST_ALIQUIPPA_BLOCK, MONACA_BLOCK, FALLSTON_BLOCK, BEAVER_FALLS_BLOCK, COLLEGE_THRU_TRACK}); 

	-- 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 Caboose detection.
		Func100("@ WEST_ALIQUIPPA_TAG ", EngineNo, TagLocation, CarModel, nil,
			{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 * 2/3), 0, MyEngineNo, MyTIUNo);
			SetVolume(MASTER_VOLUME, math.floor(EngineSoundSave * 1/3), 1, MyEngineNo, MyTIUNo);
			SetVolume(MASTER_VOLUME,            0,                      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, nil,
			{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 * 1/3), 0, MyEngineNo, MyTIUNo);
			SetVolume(MASTER_VOLUME, math.floor(EngineSoundSave * 2/3), 1, MyEngineNo, MyTIUNo);
			SetVolume(MASTER_VOLUME,            EngineSoundSave,        2, MyEngineNo, MyTIUNo);
			end;
		if (Func61 == nil) then
			Func61 = createMainLine2MainLine(false);
			end;
		-- No switch to set on Caboose detection.
		Func61("@ MONACA_EAST_TAG ", EngineNo, TagLocation, CarModel, nil,
			{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, 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_THRU_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, 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_THRU_TRACK}); 		
			
	-- LOWELLVILLE_TAG Detected westbound
	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, STRUTHERS_THRU_TRACK, STRUTHERS_STATION_TRACK, nil);

	-- STRUTHERS_EAST_TAG Detected westbound
	elseif (Detector == STRUTHERS_EAST_DETECTOR and Reader == STRUTHERS_EAST_READER) then
		if (Layout.LLLoop == false) then
			-- Point-to-Point Configuration
			if (Func20 == nil) then
				Func20 = createPassing2MainLine(false, SwitchClearingInches[STRUTHERS_EAST_TAG][MyDirection]);
				end;
			Func20("@ STRUTHERS_EAST_TAG ", EngineNo, TagLocation, CarModel, STRUTHERS_THRU_TRACK, STRUTHERS_STATION_TRACK, STRUTHERS_EAST, STRUTHERS_WEST,
				{YOUNGSTOWN_BLOCK, EndBlockNo});
			else
			-- Loop Configuration
			if (Func20 == nil) then
				Func20 = createPassing2MainLine(false, SwitchClearingInches[STRUTHERS_EAST_TAG][MyDirection]);
				end;
			-- status: in block NextBlockNo, occupancy already set
			Func20("@ STRUTHERS_EAST_TAG ", EngineNo, TagLocation, CarModel, STRUTHERS_THRU_TRACK, STRUTHERS_STATION_TRACK, STRUTHERS_EAST, STRUTHERS_WEST,
				{YOUNGSTOWN_BLOCK, BALLOON_BLOCK, MCKEES_ROCKS_BLOCK, J_L_THRU_TRACK});
			end;

	-- STRUTHERS_WEST_TAG Detected westbound
	elseif (Detector == STRUTHERS_WEST_DETECTOR and Reader == STRUTHERS_WEST_READER) then
		-- status: just entered NextBlockNo, occupancy already set
		if (Layout.LLLoop == false) then
			-- Point-to-Point Configuration
			if (Func40 == nil) then
				Func40 = createMainLine2Yard(false, SwitchClearingInches[STRUTHERS_WEST_TAG][MyDirection]);
				end;
			if (EngineNo == MyEngineNo and TagLocation == TAG_ON_FRONT_TRUCK) then
				Switch(NORMAL, Switches[BALLOON][CHAN_NUMBER], Switches[BALLOON][AIU_NUMBER], Switches[BALLOON][TIU_NUMBER], 0);
				end;
			Func40("@ STRUTHERS_WEST_TAG ", EngineNo, TagLocation, CarModel, STRUTHERS_WEST, {EndBlockNo});
			else
			-- Loop Configuration
			if (Func40 == nil) then
				Func40 = createMainLine2MainLine(false);	-- no switches if staying on Mainline
				end;
			if (EngineNo == MyEngineNo and TagLocation == TAG_ON_FRONT_TRUCK) then
				Switch(REVERSE, Switches[BALLOON][CHAN_NUMBER], Switches[BALLOON][AIU_NUMBER], Switches[BALLOON][TIU_NUMBER], 0);
				end;
			-- Set STRUTHERS_WEST to REVERSE on Caboose detection.
			Func40("@ STRUTHERS_WEST_TAG ", EngineNo, TagLocation, CarModel, STRUTHERS_WEST,
				{BALLOON_BLOCK, MCKEES_ROCKS_BLOCK,	J_L_THRU_TRACK});
			end;

	-- GATEWAY_YARD_TAG Detected westbound
	elseif (Detector == GATEWAY_YARD_DETECTOR and Reader == GATEWAY_YARD_READER) then
		-- status: in block NextBlockNo, occupancy already set
		if (Layout.LLLoop == false) then
			-- Point-to-Point Configuration
			if (Func50 == nil) then
				Func50 = createStopInYard(false, SwitchClearingInches[GATEWAY_YARD_TAG][MyDirection]);	-- clear yard switch
				end;
			Func50("@ GATEWAY_YARD_TAG ", EngineNo, TagLocation, CarModel, GATEWAY_YARD1_TRACK, GATEWAY_YARD2_TRACK, GATEWAY_YARD, {CLEVELAND_EL});
			else	
			-- Loop Configuration
			if (Func50 == nil) then
				Func50 = createMainLine2MainLine(false);	-- no switches if staying on Mainline
				end;
			-- No switch to set on Caboose detection.
			Func50("@ GATEWAY_YARD_TAG ", EngineNo, TagLocation, CarModel, nil,
				{MCKEES_ROCKS_BLOCK, J_L_THRU_TRACK});
			end;
		end;
	end;
--[[--------------------------------- Eastbound --------------------------------------------]]

if (MyDirection == EB) then
	--
	-- ResetinProgress probably does not work anymore
	--
	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;
		
	-- GATEWAY_YARD_TAG Detected eastbound
	elseif (Detector == GATEWAY_YARD_DETECTOR and Reader == GATEWAY_YARD_READER) then
		if (Func50 == nil) then
			Func50 = createMainLine2Passing(false);
			end;
		-- status: just entered NextBlockNo, occupancy already set (YOUGHSTOWN_BLOCK).
		Func50("@ GATEWAY_YARD_TAG ", EngineNo, TagLocation, CarModel, STRUTHERS_THRU_TRACK, STRUTHERS_STATION_TRACK, nil);

	-- STRUTHERS_WEST_TAG Detected eastbound
	elseif (Detector == STRUTHERS_WEST_DETECTOR and Reader == STRUTHERS_WEST_READER) then
		if (Func40 == nil) then
			Func40 = createPassing2MainLine(false, SwitchClearingInches[STRUTHERS_WEST_TAG][MyDirection]);
			end;
		-- status: just entered NextBlockNo, occupancy already set (STRUTHERS_THRU_TRACK OR STRUTHERS_STATION_TRACK).
		-- Set STRUTHERS_WEST to NORMAL on Caboose detection.
		Func40("@ STRUTHERS_WEST_TAG ", EngineNo, TagLocation, CarModel, STRUTHERS_THRU_TRACK, STRUTHERS_STATION_TRACK, STRUTHERS_WEST, 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).
		-- Set STRUTHERS_EAST to NORMAL on Caboose detection.	
		Func20("@ STRUTHERS_EAST_TAG ", EngineNo, TagLocation, CarModel, STRUTHERS_EAST,
			{NEW_CASTLE_JUNCTION_BLOCK, COLLEGE_STATION_TRACK});

	-- 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, J_L_PASSING_TRACK});

	-- 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, J_L_PASSING_TRACK});

	-- 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 Caboose detection.
		Func101("@ FALLSTON_TAG ", EngineNo, TagLocation, CarModel, nil,
			{MONACA_BLOCK, WEST_ALIQUIPPA_BLOCK, ALIQUIPPA_BLOCK, J_L_PASSING_TRACK});

	-- 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 * 2/3), 0, MyEngineNo, MyTIUNo);
			SetVolume(MASTER_VOLUME, math.floor(EngineSoundSave * 1/3), 1, MyEngineNo, MyTIUNo);
			SetVolume(MASTER_VOLUME,            0,                      2, MyEngineNo, MyTIUNo);
			end;
		if (Func61 == nil) then
			Func61 = createMainLine2MainLine(false);
			end;
		-- No switch to set on Caboose detection.
		Func61("@ MONACA_WEST_TAG ", EngineNo, TagLocation, CarModel, nil,
			{WEST_ALIQUIPPA_BLOCK, ALIQUIPPA_BLOCK, J_L_PASSING_TRACK});

	-- 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 * 1/3), 0, MyEngineNo, MyTIUNo);
			SetVolume(MASTER_VOLUME, math.floor(EngineSoundSave * 2/3), 1, MyEngineNo, MyTIUNo);
			SetVolume(MASTER_VOLUME,            EngineSoundSave,        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, nil,
			{ALIQUIPPA_BLOCK,  J_L_PASSING_TRACK});

	-- WEST_ALIQUIPPA_TAG Detected eastbound
	elseif (Detector == WEST_ALIQUIPPA_DETECTOR and Reader == WEST_ALIQUIPPA_READER) then
		if (Func100 == nil) then
			Func100 = createMainLine2Passing(false);
			end;
		Func100("@ WEST_ALIQUIPPA_TAG ", EngineNo, TagLocation, CarModel, J_L_THRU_TRACK, J_L_PASSING_TRACK, nil);

	-- ALIQUIPPA_WEST_TAG Detected eastbound
	elseif (Detector == ALIQUIPPA_WEST_DETECTOR and Reader == ALIQUIPPA_WEST_READER) then
		if (Layout.LLLoop == false) then
			-- Point-to-Point Configuration
			if (Func10 == nil) then
				Func10 = createPassing2MainLine(false, SwitchClearingInches[ALIQUIPPA_WEST_TAG][MyDirection]);
				end;
			Func10("@ 1-0 ", EngineNo, TagLocation, CarModel, J_L_THRU_TRACK, J_L_PASSING_TRACK, ALIQUIPPA_WEST, ALIQUIPPA_EAST,
				{MCKEES_ROCKS_BLOCK, EndBlockNo});
			else
			-- Loop Configuration
			if (Func10 == nil) then
				Func10 = createPassing2MainLine(false, SwitchClearingInches[ALIQUIPPA_WEST_TAG][MyDirection]);
				end;
			-- Set ALIQUIPPA_WEST to NORMAL on Caboose detection.
			Func10("@ ALIQUIPPA_WEST_TAG ", EngineNo, TagLocation, CarModel, J_L_THRU_TRACK, J_L_PASSING_TRACK, ALIQUIPPA_WEST, ALIQUIPPA_EAST,
				{MCKEES_ROCKS_BLOCK, BALLOON_BLOCK, YOUNGSTOWN_BLOCK, STRUTHERS_STATION_TRACK});
			end;

	-- ALIQUIPPA_EAST_TAG Detected eastbound
	elseif (Detector == ALIQUIPPA_EAST_DETECTOR and Reader == ALIQUIPPA_EAST_READER) then
		-- status: just entered NextBlockNo, occupancy already set
		if (Layout.LLLoop == false) then
			-- Point-to-Point Configuration
			if (Func51 == nil) then
				Func51 = createMainLine2Yard(false, SwitchClearingInches[ALIQUIPPA_EAST_TAG][MyDirection]);
				end;
			if (EngineNo == MyEngineNo and TagLocation == TAG_ON_FRONT_TRUCK) then
				Switch(NORMAL, Switches[BALLOON][CHAN_NUMBER], Switches[BALLOON][AIU_NUMBER], Switches[BALLOON][TIU_NUMBER], 0);
				end;	
			Func51("@ ALIQUIPPA_EAST_TAG ", EngineNo, TagLocation, CarModel, ALIQUIPPA_EAST, {EndBlockNo});
			else
			-- Loop Configuration
			if (Func51 == nil) then
				Func51 = createMainLine2MainLine(false);	-- no switches if staying on Mainline
				end;
			if (EngineNo == MyEngineNo and TagLocation == TAG_ON_FRONT_TRUCK) then
				Switch(REVERSE, Switches[BALLOON][CHAN_NUMBER], Switches[BALLOON][AIU_NUMBER], Switches[BALLOON][TIU_NUMBER], 0);
				end;
			-- Set ALIQUIPPA_EAST to NORMAL on Caboose detection.
			Func51("@ ALIQUIPPA_EAST_TAG ", EngineNo, TagLocation, CarModel, ALIQUIPPA_EAST,
				{BALLOON_BLOCK, YOUNGSTOWN_BLOCK, STRUTHERS_STATION_TRACK});
			end;

	-- MCKEES_ROCKS_REPAIR_SHOPS_YARD_TAG Detected eastbound
	elseif (Detector == MCKEES_ROCKS_REPAIR_SHOPS_YARD_DETECTOR and Reader == MCKEES_ROCKS_REPAIR_SHOPS_YARD_READER) then
		-- status: in block NextBlockNo, occupancy already set
		if (Layout.LLLoop == false) then
			-- Point-to-Point Configuration
			if (Func30 == nil) then
				Func30 = createStopInYard(false, SwitchClearingInches[MCKEES_ROCKS_REPAIR_SHOPS_YARD_TAG][MyDirection]);	-- clear yard switch
				end;
			Func30("@ MCKEES_ROCKS_REPAIR_SHOPS_YARD_TAG ", EngineNo, TagLocation, CarModel, MCKEES_ROCKS_REPAIR_SHOPS_YARD1_TRACK, MCKEES_ROCKS_REPAIR_SHOPS_YARD2_TRACK, MCKEES_ROCKS_REPAIR_SHOPS_YARD, {POINTS_SOUTH_N_W});
			else
			-- Loop Configuration			
			if (Func30 == nil) then
				Func30 = createMainLine2MainLine(false);	-- no switches if staying on Mainline
				end;
			-- No switch to set on Caboose detection.
			Func30("@ MCKEES_ROCKS_REPAIR_SHOPS_YARD_TAG ", EngineNo, TagLocation, CarModel, nil,
				{YOUNGSTOWN_BLOCK, STRUTHERS_STATION_TRACK});
			end;
		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;

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 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 == MCKEES_ROCKS_REPAIR_SHOPS_YARD1_TRACK) then	-- Point-to-Point Configuration Only
		LastBlockNo = POINTS_SOUTH_N_W;
		NextBlockNo = MCKEES_ROCKS_BLOCK;
		MultiSetOccupancy(LocalHeadway and {NextBlockNo} or {NextBlockNo, J_L_THRU_TRACK}, MyEngineNo | (MyDirection << 8));
		----MultiSetOccupancy({NextBlockNo, J_L_THRU_TRACK}, MyEngineNo | (MyDirection << 8));
		-- position MCKEES_ROCKS_REPAIR_SHOPS_YARD switch to NORMAL
		Switch(NORMAL, Switches[MCKEES_ROCKS_REPAIR_SHOPS_YARD][CHAN_NUMBER], Switches[MCKEES_ROCKS_REPAIR_SHOPS_YARD][AIU_NUMBER], Switches[MCKEES_ROCKS_REPAIR_SHOPS_YARD][TIU_NUMBER], 0);
		-- position BALLOON switch as needed after locking next block
		Switch(NORMAL, Switches[BALLOON][CHAN_NUMBER], Switches[BALLOON][AIU_NUMBER], Switches[BALLOON][TIU_NUMBER], 0);	
		
	elseif (CurrentBlockNo == MCKEES_ROCKS_REPAIR_SHOPS_YARD2_TRACK) then	-- Point-to-Point Configuration Only
		LastBlockNo = CUMBERLAND_B_O;
		NextBlockNo = MCKEES_ROCKS_BLOCK;
		MultiSetOccupancy(LocalHeadway and {NextBlockNo} or {NextBlockNo, J_L_THRU_TRACK}, MyEngineNo | (MyDirection << 8));		
		----MultiSetOccupancy({NextBlockNo, J_L_THRU_TRACK}, MyEngineNo | (MyDirection << 8));		
		-- position MCKEES_ROCKS_REPAIR_SHOPS_YARD switch to REVERSE
		Switch(REVERSE, Switches[MCKEES_ROCKS_REPAIR_SHOPS_YARD][CHAN_NUMBER], Switches[MCKEES_ROCKS_REPAIR_SHOPS_YARD][AIU_NUMBER], Switches[MCKEES_ROCKS_REPAIR_SHOPS_YARD][TIU_NUMBER], 0);
		-- position BALLOON switch as needed after locking next block
		Switch(NORMAL, Switches[BALLOON][CHAN_NUMBER], Switches[BALLOON][AIU_NUMBER], Switches[BALLOON][TIU_NUMBER], 0);	
		
	elseif (CurrentBlockNo == MCKEES_ROCKS_BLOCK) then -- "McKees Rocks Block"
		if (Layout.LLLoop == true) then
			-- Loop Configuration
			LastBlockNo = BALLOON_BLOCK;
			NextBlockNo = J_L_THRU_TRACK;
			else
			-- Point-to-Point Configuration
			LastBlockNo = StartBlockNo;
			NextBlockNo = J_L_THRU_TRACK;
			end;
		-- check that the Next Block is not occupied
		SingleSetOccupancy({NextBlockNo}, MyEngineNo | (MyDirection << 8));

	elseif (CurrentBlockNo == J_L_THRU_TRACK) then -- "J&L Thru Track"
		local flag;
		LastBlockNo = MCKEES_ROCKS_BLOCK;
		NextBlockNo = ALIQUIPPA_BLOCK;
		-- Additional test for Westbound trains in Headway mode starting from J_L_THRU_TRACK / COLLEGE_THRU_TRACK / STRUTHERS_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.
		--
		-- Note that this special case occurs only at startup. In the example I saw, there were trains
		-- in the J&L_THRU_TRACK (westbound) and WEST_ALIQUIPPA_BLOCK (eastbound). The westbound train in the J&L_THRU_TRACK was released
		-- and it was going to run in to the front of the eastbound train in the WEST_ALIQUIPPA_BLOCK.
		-- That was because Headway was true so the train in the J&L_THRU_TRACK only checked the
		-- next block , ALIQUIPPA_BLOCK, for occupancy. That was not enough.
		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;
			-- Don't need to check next siding

			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));
		----MultiSetOccupancy({NextBlockNo, WEST_ALIQUIPPA_BLOCK, MONACA_BLOCK, FALLSTON_BLOCK, BEAVER_FALLS_BLOCK, COLLEGE_THRU_TRACK}, MyEngineNo | (MyDirection << 8));
		-- Position ALIQUIPPA_WEST after locking next block
		Switch(NORMAL, Switches[ALIQUIPPA_WEST][CHAN_NUMBER], Switches[ALIQUIPPA_WEST][AIU_NUMBER], Switches[ALIQUIPPA_WEST][TIU_NUMBER], 0);	
		
	elseif (CurrentBlockNo == J_L_PASSING_TRACK) then -- "J&L Passing Track" -- Westbound trains don't use the passing track
		LastBlockNo = MCKEES_ROCKS_BLOCK;
		NextBlockNo = ALIQUIPPA_BLOCK;
		MultiSetOccupancy(LocalHeadway and {NextBlockNo} or {NextBlockNo, WEST_ALIQUIPPA_BLOCK, MONACA_BLOCK, FALLSTON_BLOCK, BEAVER_FALLS_BLOCK, COLLEGE_THRU_TRACK}, MyEngineNo | (MyDirection << 8));				
		----MultiSetOccupancy({NextBlockNo, WEST_ALIQUIPPA_BLOCK, MONACA_BLOCK, FALLSTON_BLOCK, BEAVER_FALLS_BLOCK, COLLEGE_THRU_TRACK}, MyEngineNo | (MyDirection << 8));				
		-- Position ALIQUIPPA_WEST after locking next block
		Switch(REVERSE, Switches[ALIQUIPPA_WEST][CHAN_NUMBER], Switches[ALIQUIPPA_WEST][AIU_NUMBER], Switches[ALIQUIPPA_WEST][TIU_NUMBER], 0);

	elseif (CurrentBlockNo == ALIQUIPPA_BLOCK) then -- "Aliquippa Block"
		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;
		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;
			-- Don't need to check next siding

			print("Startup Headway set to " .. tostring(LocalHeadway))	
		end;
		--
		MultiSetOccupancy(LocalHeadway and {NextBlockNo} or {NextBlockNo, LOWELLVILLE_BLOCK, STRUTHERS_THRU_TRACK}, MyEngineNo | (MyDirection << 8));				
		----MultiSetOccupancy({NextBlockNo, LOWELLVILLE_BLOCK, STRUTHERS_THRU_TRACK}, 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_THRU_TRACK}, 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_THRU_TRACK}, 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_THRU_TRACK;
		-- check that the Next Block is not occupied
		SingleSetOccupancy({NextBlockNo}, MyEngineNo | (MyDirection << 8));
		
	elseif (CurrentBlockNo == STRUTHERS_THRU_TRACK) then -- "Struthers Thru Track"
		local flag;
		LastBlockNo = LOWELLVILLE_BLOCK;
		NextBlockNo = YOUNGSTOWN_BLOCK;
		if (LocalHeadway) then		
			print("LocalHeadway Check from " .. BlockName[CurrentBlockNo]);
		
			flag = math.abs(GetFlag(YOUNGSTOWN_BLOCK));
			if (flag ~= UNOCCUPIED) then
					print(BlockName[YOUNGSTOWN_BLOCK] .. "(# " .. YOUNGSTOWN_BLOCK .. ") Occupied : Engine #" .. (flag & 0x00FF) .. " " .. DirectionNames[(flag & 0xFF00) >> 8]);
					if ((flag & 0xFF00) >> 8 == EB) then
						LocalHeadway = false;
						end;				
					end;
			flag = math.abs(GetFlag(BALLOON_BLOCK));
			if (flag ~= UNOCCUPIED) then
					print(BlockName[BALLOON_BLOCK] .. "(#" .. BALLOON_BLOCK .. ") Occupied : Engine #" .. (flag & 0x00FF) .. " " .. DirectionNames[(flag & 0xFF00) >> 8]);
					if ((flag & 0xFF00) >> 8 == EB) then
						LocalHeadway = false;
						end;				
					end;
			flag = math.abs(GetFlag(MCKEES_ROCKS_BLOCK));
			if (flag ~= UNOCCUPIED) then
					print(BlockName[MCKEES_ROCKS_BLOCK] .. "(#" .. MCKEES_ROCKS_BLOCK .. ") Occupied : Engine #" .. (flag & 0x00FF) .. " " .. DirectionNames[(flag & 0xFF00) >> 8]);
					if ((flag & 0xFF00) >> 8 == EB) then
						LocalHeadway = false;
						end;				
					end;
			-- Don't need to check next siding
			print("Startup Headway set to " .. tostring(LocalHeadway))
		end;			
		--
		MultiSetOccupancy(LocalHeadway and {NextBlockNo} or {NextBlockNo, BALLOON_BLOCK, MCKEES_ROCKS_BLOCK, J_L_THRU_TRACK}, MyEngineNo | (MyDirection << 8));
		----MultiSetOccupancy({NextBlockNo, BALLOON_BLOCK, MCKEES_ROCKS_BLOCK, J_L_THRU_TRACK}, MyEngineNo | (MyDirection << 8));
		-- Position STRUTHERS_WEST after locking next block
		Switch(NORMAL, Switches[STRUTHERS_WEST][CHAN_NUMBER], Switches[STRUTHERS_WEST][AIU_NUMBER], Switches[STRUTHERS_WEST][TIU_NUMBER], 0);
		
	elseif (CurrentBlockNo == STRUTHERS_STATION_TRACK) then -- "Struthers Station Track" -- Westbound trains don't use the station track
		LastBlockNo = LOWELLVILLE_BLOCK;
		NextBlockNo = YOUNGSTOWN_BLOCK;
		MultiSetOccupancy(LocalHeadway and {NextBlockNo} or {NextBlockNo, BALLOON_BLOCK, MCKEES_ROCKS_BLOCK, J_L_THRU_TRACK}, MyEngineNo | (MyDirection << 8));				
		----MultiSetOccupancy({NextBlockNo, BALLOON_BLOCK, MCKEES_ROCKS_BLOCK, J_L_THRU_TRACK}, MyEngineNo | (MyDirection << 8));				
		-- Position STRUTHERS_WEST after locking next block
		Switch(REVERSE, Switches[STRUTHERS_WEST][CHAN_NUMBER], Switches[STRUTHERS_WEST][AIU_NUMBER], Switches[STRUTHERS_WEST][TIU_NUMBER], 0);

	elseif (CurrentBlockNo == YOUNGSTOWN_BLOCK) then -- "Youngstown Block"
		if (Layout.LLLoop == true) then
			-- Loop Configuration
			LastBlockNo = STRUTHERS_THRU_TRACK;
			NextBlockNo = BALLOON_BLOCK;
			MultiSetOccupancy(LocalHeadway and {NextBlockNo} or {NextBlockNo, MCKEES_ROCKS_BLOCK, J_L_THRU_TRACK}, MyEngineNo | (MyDirection << 8));	
			----MultiSetOccupancy({NextBlockNo, MCKEES_ROCKS_BLOCK, J_L_THRU_TRACK}, MyEngineNo | (MyDirection << 8));	
			else
			-- Point-to-Point Configurationn
			LastBlockNo = STRUTHERS_THRU_TRACK;
			NextBlockNo = EndBlockNo;
			SingleSetOccupancy({NextBlockNo}, MyEngineNo | (MyDirection << 8));	
		end;

	-- WB cannot start in GATEWAY_YARD_TRACK

	elseif (CurrentBlockNo == BALLOON_BLOCK) then -- "Balloon Block" -- AKA Lionel-Ives Block	-- Loop Configuration Only
		LastBlockNo = YOUNGSTOWN_BLOCK;
		NextBlockNo = MCKEES_ROCKS_BLOCK;
		MultiSetOccupancy(LocalHeadway and {NextBlockNo} or {NextBlockNo, J_L_THRU_TRACK}, MyEngineNo | (MyDirection << 8));
		----MultiSetOccupancy({NextBlockNo, J_L_THRU_TRACK}, MyEngineNo | (MyDirection << 8));
		end;

	end;

--[[--------------------------------- Eastbound --------------------------------------------]]
if (MyDirection == EB) then
	local LocalHeadway = Headway;
	--
	-- Engine can start in any 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 == GATEWAY_YARD1_TRACK) then	-- Point-to-Point Configuration Only
		LastBlockNo = ASHTABULA_NYC;
		NextBlockNo = YOUNGSTOWN_BLOCK;
		MultiSetOccupancy(LocalHeadway and {NextBlockNo} or {NextBlockNo, STRUTHERS_STATION_TRACK}, MyEngineNo | (MyDirection << 8));
		--MultiSetOccupancy({NextBlockNo, STRUTHERS_STATION_TRACK}, MyEngineNo | (MyDirection << 8));
		-- position GATEWAY_YARD switch as needed depending on value of CurrentBlockNo
		Switch(NORMAL, Switches[GATEWAY_YARD][CHAN_NUMBER], Switches[GATEWAY_YARD][AIU_NUMBER], Switches[GATEWAY_YARD][TIU_NUMBER], 0);
		-- position BALLOON switch as needed after locking next block
		Switch(NORMAL, Switches[BALLOON][CHAN_NUMBER], Switches[BALLOON][AIU_NUMBER], Switches[BALLOON][TIU_NUMBER], 0);	
		
	elseif (CurrentBlockNo == GATEWAY_YARD2_TRACK) then	-- Point-to-Point Configuration Only
		LastBlockNo = CLEVELAND_EL;
		NextBlockNo = YOUNGSTOWN_BLOCK;
		MultiSetOccupancy(LocalHeadway and {NextBlockNo} or {NextBlockNo, STRUTHERS_STATION_TRACK}, MyEngineNo | (MyDirection << 8));		
		--MultiSetOccupancy({NextBlockNo, STRUTHERS_STATION_TRACK}, MyEngineNo | (MyDirection << 8));		
		-- position GATEWAY_YARD switch as needed depending on value of CurrentBlockNo
		Switch(REVERSE, Switches[GATEWAY_YARD][CHAN_NUMBER], Switches[GATEWAY_YARD][AIU_NUMBER], Switches[GATEWAY_YARD][TIU_NUMBER], 0);
		-- position BALLOON switch as needed after locking next block
		Switch(NORMAL, Switches[BALLOON][CHAN_NUMBER], Switches[BALLOON][AIU_NUMBER], Switches[BALLOON][TIU_NUMBER], 0);	
		
	elseif (CurrentBlockNo == YOUNGSTOWN_BLOCK) then -- "Youngstown Block"
		if (Layout.LLLoop == true) then
			-- Loop Configuration
			LastBlockNo = BALLOON_BLOCK;
			NextBlockNo = STRUTHERS_STATION_TRACK;
			else
			-- Point-to-Point Configuration
			LastBlockNo = StartBlockNo;
			NextBlockNo = STRUTHERS_STATION_TRACK;
			end;
		-- check that the Next Block is not occupied
		SingleSetOccupancy({NextBlockNo}, MyEngineNo | (MyDirection << 8));

	elseif (CurrentBlockNo == STRUTHERS_STATION_TRACK) then -- "Struthers Station Track"
		LastBlockNo = YOUNGSTOWN_BLOCK;
		NextBlockNo = LOWELLVILLE_BLOCK;
		
		-- Additional test for eastbound trains in Headway mode starting from J_L_PASSING_TRACK / COLLEGE_STATION_TRACK / 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 siding
			print("Startup Headway set to " .. tostring(LocalHeadway))	
		end;

		MultiSetOccupancy(LocalHeadway and {NextBlockNo} or {NextBlockNo, NEW_CASTLE_JUNCTION_BLOCK, COLLEGE_STATION_TRACK}, MyEngineNo | (MyDirection << 8));
		--MultiSetOccupancy({NextBlockNo, NEW_CASTLE_JUNCTION_BLOCK, COLLEGE_STATION_TRACK}, MyEngineNo | (MyDirection << 8));
		-- Position STRUTHERS_EAST after locking next block
		Switch(REVERSE, Switches[STRUTHERS_EAST][CHAN_NUMBER], Switches[STRUTHERS_EAST][AIU_NUMBER], Switches[STRUTHERS_EAST][TIU_NUMBER], 0);	

	elseif (CurrentBlockNo == STRUTHERS_THRU_TRACK) then -- "Struthers Thru Track" -- Eastbound trains don't use the thru track
		LastBlockNo = YOUNGSTOWN_BLOCK;
		NextBlockNo = LOWELLVILLE_BLOCK;
		MultiSetOccupancy(LocalHeadway and {NextBlockNo} or {NextBlockNo, NEW_CASTLE_JUNCTION_BLOCK, COLLEGE_STATION_TRACK}, MyEngineNo | (MyDirection << 8));			
		--MultiSetOccupancy({NextBlockNo, NEW_CASTLE_JUNCTION_BLOCK, COLLEGE_STATION_TRACK}, MyEngineNo | (MyDirection << 8));			
		-- Position STRUTHERS_EAST after locking next block
		Switch(NORMAL, Switches[STRUTHERS_EAST][CHAN_NUMBER], Switches[STRUTHERS_EAST][AIU_NUMBER], Switches[STRUTHERS_EAST][TIU_NUMBER], 0);	

	elseif (CurrentBlockNo == LOWELLVILLE_BLOCK) then -- "Lowellville Block"
		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 J_L_PASSING_TRACK / COLLEGE_STATION_TRACK / 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(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;
			-- Don't need to check next siding (J_L_PASSING_TRACK)
			print("Startup Headway set to " .. tostring(LocalHeadway));
		end;
		
		MultiSetOccupancy(LocalHeadway and {NextBlockNo} or {NextBlockNo, FALLSTON_BLOCK, MONACA_BLOCK, WEST_ALIQUIPPA_BLOCK, ALIQUIPPA_BLOCK, J_L_PASSING_TRACK}, MyEngineNo | (MyDirection << 8));				
		--MultiSetOccupancy({NextBlockNo, FALLSTON_BLOCK, MONACA_BLOCK, WEST_ALIQUIPPA_BLOCK, ALIQUIPPA_BLOCK, J_L_PASSING_TRACK}, 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 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, J_L_PASSING_TRACK}, MyEngineNo | (MyDirection << 8));				
		--MultiSetOccupancy({NextBlockNo, FALLSTON_BLOCK, MONACA_BLOCK, WEST_ALIQUIPPA_BLOCK, ALIQUIPPA_BLOCK, J_L_PASSING_TRACK}, 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"
		LastBlockNo = COLLEGE_STATION_TRACK;
		NextBlockNo = FALLSTON_BLOCK;
		MultiSetOccupancy(LocalHeadway and {NextBlockNo} or {NextBlockNo, MONACA_BLOCK, WEST_ALIQUIPPA_BLOCK, ALIQUIPPA_BLOCK, J_L_PASSING_TRACK}, MyEngineNo | (MyDirection << 8));	
		--MultiSetOccupancy({NextBlockNo, MONACA_BLOCK, WEST_ALIQUIPPA_BLOCK, ALIQUIPPA_BLOCK, J_L_PASSING_TRACK}, MyEngineNo | (MyDirection << 8));	

	elseif (CurrentBlockNo == FALLSTON_BLOCK) then -- "Fallston Block"
		LastBlockNo = BEAVER_FALLS_BLOCK;
		NextBlockNo = MONACA_BLOCK;
		MultiSetOccupancy(LocalHeadway and {NextBlockNo} or {NextBlockNo, WEST_ALIQUIPPA_BLOCK, ALIQUIPPA_BLOCK, J_L_PASSING_TRACK}, MyEngineNo | (MyDirection << 8));
		--MultiSetOccupancy({NextBlockNo, WEST_ALIQUIPPA_BLOCK, ALIQUIPPA_BLOCK, J_L_PASSING_TRACK}, MyEngineNo | (MyDirection << 8));
		
	elseif (CurrentBlockNo == MONACA_BLOCK) then -- "Monaca Block"
		-- 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,  J_L_PASSING_TRACK}, MyEngineNo | (MyDirection << 8));
		--MultiSetOccupancy({NextBlockNo, ALIQUIPPA_BLOCK,  J_L_PASSING_TRACK}, MyEngineNo | (MyDirection << 8));

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

	elseif (CurrentBlockNo == ALIQUIPPA_BLOCK) then -- "Aliquippa Block"
		LastBlockNo = WEST_ALIQUIPPA_BLOCK;
		NextBlockNo = J_L_PASSING_TRACK;
		-- check that the Next Block is not occupied
		SingleSetOccupancy({NextBlockNo}, MyEngineNo | (MyDirection << 8));
		
	elseif (CurrentBlockNo == J_L_PASSING_TRACK) then -- "J&L Passing Track"
		LastBlockNo = ALIQUIPPA_BLOCK;
		NextBlockNo = MCKEES_ROCKS_BLOCK;
		
		-- Additional test for eastbound trains in Headway mode starting from J_L_PASSING_TRACK / COLLEGE_STATION_TRACK / 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(MCKEES_ROCKS_BLOCK));
			if (flag ~= UNOCCUPIED) then
					print(BlockName[MCKEES_ROCKS_BLOCK] .. "(#" .. MCKEES_ROCKS_BLOCK .. ") Occupied : Engine #" .. (flag & 0x00FF) .. " " .. DirectionNames[(flag & 0xFF00) >> 8]);
					if ((flag & 0xFF00) >> 8 == WB) then
						LocalHeadway = false;
						end;				
					end;	
			flag = math.abs(GetFlag(BALLOON_BLOCK));
			if (flag ~= UNOCCUPIED) then
					print(BlockName[BALLOON_BLOCK] .. "(#" .. BALLOON_BLOCK .. ") Occupied : Engine #" .. (flag & 0x00FF) .. " " .. DirectionNames[(flag & 0xFF00) >> 8]);
					if ((flag & 0xFF00) >> 8 == WB) then
						LocalHeadway = false;
						end;				
					end;				
			flag = math.abs(GetFlag(YOUNGSTOWN_BLOCK));
			if (flag ~= UNOCCUPIED) then
					print(BlockName[YOUNGSTOWN_BLOCK] .. "(# " .. YOUNGSTOWN_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 siding (STRUTHERS_STATION_TRACK)
			print("Startup Headway set to " .. tostring(LocalHeadway));
		end;
		
		MultiSetOccupancy(LocalHeadway and {NextBlockNo} or {NextBlockNo, BALLOON_BLOCK, YOUNGSTOWN_BLOCK, STRUTHERS_STATION_TRACK}, MyEngineNo | (MyDirection << 8));
		--MultiSetOccupancy({NextBlockNo, BALLOON_BLOCK, YOUNGSTOWN_BLOCK, STRUTHERS_STATION_TRACK}, MyEngineNo | (MyDirection << 8));
		-- Position ALIQUIPPA_EAST after locking next block
		Switch(REVERSE, Switches[ALIQUIPPA_EAST][CHAN_NUMBER], Switches[ALIQUIPPA_EAST][AIU_NUMBER], Switches[ALIQUIPPA_EAST][TIU_NUMBER], 0);

	elseif (CurrentBlockNo == J_L_THRU_TRACK) then -- "J&L Thru Track" -- Eastbound trains don't use the thru track
		LastBlockNo = ALIQUIPPA_BLOCK;
		NextBlockNo = MCKEES_ROCKS_BLOCK;
		MultiSetOccupancy(LocalHeadway and {NextBlockNo} or {NextBlockNo, BALLOON_BLOCK, YOUNGSTOWN_BLOCK, STRUTHERS_STATION_TRACK}, MyEngineNo | (MyDirection << 8));				
		--MultiSetOccupancy({NextBlockNo, BALLOON_BLOCK, YOUNGSTOWN_BLOCK, STRUTHERS_STATION_TRACK}, MyEngineNo | (MyDirection << 8));				
		-- Position ALIQUIPPA_EAST after locking next block
		Switch(NORMAL, Switches[ALIQUIPPA_EAST][CHAN_NUMBER], Switches[ALIQUIPPA_EAST][AIU_NUMBER], Switches[ALIQUIPPA_EAST][TIU_NUMBER], 0);
		
	elseif (CurrentBlockNo == MCKEES_ROCKS_BLOCK) then -- "McKees Rocks Block"
		if (Layout.LLLoop == true) then
			-- Loop Configuration
			LastBlockNo = J_L_PASSING_TRACK;
			NextBlockNo = BALLOON_BLOCK;
			MultiSetOccupancy(LocalHeadway and {NextBlockNo} or {NextBlockNo, YOUNGSTOWN_BLOCK, STRUTHERS_STATION_TRACK}, MyEngineNo | (MyDirection << 8));				
			--MultiSetOccupancy({NextBlockNo, YOUNGSTOWN_BLOCK, STRUTHERS_STATION_TRACK}, MyEngineNo | (MyDirection << 8));				
			else
			-- Point-to-Point Configuration
			LastBlockNo = J_L_PASSING_TRACK;
			NextBlockNo = EndBlockNo;
			SingleSetOccupancy({NextBlockNo}, MyEngineNo | (MyDirection << 8));				
			end;

	-- EB Cannot start in MCKEES_ROCKS_REPAIR_SHOPS_YARD
				
	elseif (CurrentBlockNo == BALLOON_BLOCK) then -- "Balloon Block" -- AKA Lionel-Ives Block	-- Loop Configuration Only
		LastBlockNo = MCKEES_ROCKS_BLOCK;
		NextBlockNo = YOUNGSTOWN_BLOCK;	-- check that the Next Block is not occupied
		MultiSetOccupancy(LocalHeadway and {NextBlockNo} or {NextBlockNo, STRUTHERS_STATION_TRACK}, MyEngineNo | (MyDirection << 8));
		--MultiSetOccupancy({NextBlockNo, STRUTHERS_STATION_TRACK}, 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";
--[[---------------------------------------------------------------------------------------]]

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

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(0);	-- indicate default full 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";
--[[---------------------------------------------------------------------------------------]]
