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

© Copyright 2020 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
title = "Switch Timing Test"
--
-- this test runs a single engine over the layout
-- Each time it approaches a switch for a siding, the program
-- will attempt to throw that switch into the other position
-- before the engine gets there
--
-- The program works with the three sidings, College, Struthers and Aliquippa
require([[defines]]);
require([[functions]]);

local SD = require([[Distances]]);		-- Measured stopping and starting distances in inches

-- Counter Labels
Counter01Label = "Engine Starts";
C1 = COUNTER01;
Counter02Label = "State";
C2 = COUNTER02;
Counter03Label = "#Tags";
C3 = COUNTER03;
Counter04Label = "Target Distance";
C4 = COUNTER04;
Counter05Label = "C5";
C5 = COUNTER05;
Counter06Label = "C6";
C6 = COUNTER06;
-- These labels appear on each of the tabs for the Function Buttons
Tab01Label = "Destination";
Tab02Label = "Distance";
Tab03Label = "Control";
-- table to hold all of the tags detected in sequence
Tags = {};
TagCount = 0;
-- starting time
DTOStart = 0.0;
--
-- Siding entrance test
--
local STOPTIME = 15;
local STARTTIME = 10;
BeepOn = false;
SoundOff = false;
SPEEDINCREMENT = 10;

-- state machine states
IDLE = 99;
DONE = 100;
S0 = 0;
S1 = 1;
S2 = 2;
S3 = 3;
S4 = 4;
S5 = 5;
S6 = 6;
S7 = 7;
S8 = 8;
-- starting state
SM = IDLE;
StartRun = false;
BASESPEED = 40;
MyDirection = WB;
MyDest = 0;

Aliquippa_Siding = false;
College_Siding = false;
Struthers_Siding = false;

--[[---------------------------------------------------------------------------------------]]
function setup(Engine, TIU)
	print(os.date("setup() %d %b %Y   %X"));
	-- 
	MyTIUNo = TIU;
	MyEngineNo = Engine;
	--
	print("setup() : TIU#" .. MyTIUNo .. " Debug " .. Debug());
	print(string.format("Run Time = %.2f Seconds", RunTime()));

	Resp = InputQuery("Switch Timing Test", "Enter Engine Number", MyEngineNo);
	if (Resp == false or Resp == nil) then	-- user pressed [Cancel]
		return false;	-- false=setup failed, true=setup succeeded
		end;
	Enum = tonumber(Resp);
	if (Enum ~= nil) then	-- if the result is a number, then use it, otherwise, ignore it
		MyEngineNo = Enum;
		end

	SM = IDLE;
	SetCounter(C2, IDLE);	
	print("Starting Engine using Window #", PCNo());

	Name, PS = GetName(MyEngineNo);
	print("Engine#" .. MyEngineNo .. " " .. Name .. " PS" .. PS);
	Title(title .. " - " .. Name, MyEngineNo);
	-- TODO if engine is already running, then skip this startup code
	Smoke(OFF, 0, MyEngineNo, MyTIUNo);
	StartUp(0, MyEngineNo, MyTIUNo);	-- start selected engine
	-- let engine startup
	print("Aliquippa switches set to Reverse");
	Switch(REVERSE, ALIQUIPPA_EAST_CHAN, ALIQUIPPA_EAST_AIU, MyTIUNo, 0);  -- Aliquippa East
	Switch(REVERSE, ALIQUIPPA_WEST_CHAN, ALIQUIPPA_WEST_AIU, MyTIUNo, 3);  -- Aliquippa West
	Aliquippa_Siding = false;
	print("Struthers switches set to Reverse");
	Switch(REVERSE, STRUTHERS_EAST_CHAN, STRUTHERS_EAST_AIU, MyTIUNo, 4);  -- Struthers East
	Switch(REVERSE, STRUTHERS_WEST_CHAN, STRUTHERS_WEST_AIU, MyTIUNo, 6);  -- Struthers West
	Struthers_Siding = false;
	print("College switches set to Reverse");
	Switch(REVERSE, COLLEGE_EAST_CHAN, COLLEGE_EAST_AIU, MyTIUNo, 8);  -- College East
	Switch(REVERSE, COLLEGE_WEST_CHAN, COLLEGE_WEST_AIU, MyTIUNo, 10);  -- College West
	College_Siding = false;
	Sleep(15);
	-- set acceleration and deceleration values
	Rate(1, 1, 0, MyEngineNo, MyTIUNo);
	print("setup : Set acceleration/deceleration rate to 1");
	SwitchesToggled = false;
	print(string.format("Run Time = %.2f Seconds", RunTime()));
	StartTheEngine();

return true;
end
--[[---------------------------------------------------------------------------------------]]

function tag(Detector, Reader, EngineNo, TagLocation, CarModel, Railroad, CarNumber, TagPacket)
	-- this function is called each time a tag is detected
	if (EngineNo ~= MyEngineNo) then return true; end;
	if (Debug() and BeepOn) then Beep(); end;	-- sound a tone
	-- Line below shows a different way to quote a string using brackets
	if (EngineNo > 0) then	-- it's an engine
		print(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)));
		else	-- it's a car
		print(string.format([[tag(%.2f) : Detector %d Reader %d Car %s %s #%s]],
			RunTime(), Detector, Reader, GetRailroadName(Railroad), GetCarName(CarModel), CarNumber));			
		end

	-- The complete packet received is in TagPacket 
	-- (some of the most used fields have already been extracted into the first parameters to tag()
	--   First 8 digits are the 4 byte Tag UID in hexadecimal
	--   Next 32 digits are the 16 bytes of Block 4 Information read from the Tag in hexadecimal
	--   Final 4 digits are the checksum and End of Packet character
	if (Debug() > 3) then print(string.format("Packet  %s", string.sub(TagPacket, 1, 40))); 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
	BumpCounter(C3);		-- display the number of tags
	
--[[---------------------------------------------------------------------------------------]]
-- STATE MACHINE
--[[---------------------------------------------------------------------------------------]]	
if (SM == IDLE) then	-- do nothing 
	return true;	-- false=stop calling loop(), true=continue to call loop()
	end;
	
if (SM == DONE) then	-- finish up
	SetCounter(C2, IDLE);
	SM = IDLE;	-- go idle
	print("Almost Done...");
	return Stop("DONE");
	end;
	
if (SM == S0) then
	-- run forward until the engine tag is detected over a reader
	if (EngineNo == MyEngineNo and isEngine(EngineNo)) then
		if (isTagFront(TagLocation)) then
			-- toggle the switch position depending on which reader we passed over
			--print("toggle");
			if (MyDirection == WB) then
				if (isDetector(Detector, ALIQUIPPA_EAST_DETECTOR) and isReader(Reader, ALIQUIPPA_EAST_READER)) then
					if (Aliquippa_Siding) then
						print("Aliquippa switches set to Reverse");
						Switch(REVERSE, ALIQUIPPA_EAST_CHAN, ALIQUIPPA_EAST_AIU, MyTIUNo, 0);  -- Aliquippa East
						Switch(REVERSE, ALIQUIPPA_WEST_CHAN, ALIQUIPPA_WEST_AIU, MyTIUNo, 4);  -- Aliquippa West
						Aliquippa_Siding = false;
						else
						print("Aliquippa switches set to Normal");
						Switch(NORMAL, ALIQUIPPA_EAST_CHAN, ALIQUIPPA_EAST_AIU, MyTIUNo, 0);  -- Aliquippa East
						Switch(NORMAL, ALIQUIPPA_WEST_CHAN, ALIQUIPPA_WEST_AIU, MyTIUNo, 4);  -- Aliquippa West
						Aliquippa_Siding = true;
						end
					end;
					
				if (isDetector(Detector, STRUTHERS_EAST_DETECTOR) and isReader(Reader, STRUTHERS_EAST_READER)) then
					if (Struthers_Siding) then
						print("Struthers switches set to Reverse");
						Switch(REVERSE, STRUTHERS_EAST_CHAN, STRUTHERS_EAST_AIU, MyTIUNo, 0);  -- Struthers East
						Switch(REVERSE, STRUTHERS_WEST_CHAN, STRUTHERS_WEST_AIU, MyTIUNo, 4);  -- Struthers West
						Struthers_Siding = false;
						else
						print("Struthers switches set to Normal");
						Switch(NORMAL, STRUTHERS_EAST_CHAN, STRUTHERS_EAST_AIU, MyTIUNo, 0);  -- Struthers East
						Switch(NORMAL, STRUTHERS_WEST_CHAN, STRUTHERS_WEST_AIU, MyTIUNo, 4);  -- Struthers West
						Struthers_Siding = true;
						end				
					end;
					
				if (isDetector(Detector, COLLEGE_EAST_DETECTOR) and isReader(Reader, COLLEGE_EAST_READER)) then
					if (College_Siding) then
						print("College switches set to Reverse");
						Switch(REVERSE, COLLEGE_EAST_CHAN, COLLEGE_EAST_AIU, MyTIUNo, 0);  -- College East
						Switch(REVERSE, COLLEGE_WEST_CHAN, COLLEGE_WEST_AIU, MyTIUNo, 4);  -- College West
						College_Siding = false;
						else
						print("College switches set to Normal");
						Switch(NORMAL, COLLEGE_EAST_CHAN, COLLEGE_EAST_AIU, MyTIUNo, 0);  -- College East
						Switch(NORMAL, COLLEGE_WEST_CHAN, COLLEGE_WEST_AIU, MyTIUNo, 4);  -- College West
						College_Siding = true;
						end				
				
					end;
			
				end;
			if (MyDirection == EB) then
			
			
				end;
			SwitchesToggled = true;
			-- stay in S0 to check for rear tag detection
			return true;
			end;

		if (isTagRear(TagLocation)) then
			if (not SwitchesToggled) then 
				Throttle(0, 0, EngineNo, MyTIUNo);
				return Stop("S0 : Engine Front tag not detected");
				end;
			--print("toggle cleared");
			SwitchesToggled = false;
			return true;
			end			
		end;
	-- Stay in S0
	return true;	-- false=stop calling loop(), true=continue to call loop()
	end;

	
if (SM == S1) then
	SM = DONE;		-- TBD End the script 
	return true;	-- false=stop calling loop(), true=continue to call loop()
	end;
	
if (SM == S2) then
	SM = DONE;		-- TBD End the script 
	return true;	-- false=stop calling loop(), true=continue to call loop()
	end;
	
if (SM == S3) then
	SM = DONE;		-- TBD End the script 
	return true;	-- false=stop calling loop(), true=continue to call loop()
	end;
	
if (SM == S4) then
	SM = DONE;		-- TBD End the script 
	return true;	-- false=stop calling loop(), true=continue to call loop()
	end;
	
if (SM == S5) then
	SM = DONE;		-- TBD End the script 
	return true;	-- false=stop calling loop(), true=continue to call loop()
	end;
	
if (SM == S6) then
	SM = DONE;		-- TBD End the script 
	return true;	-- false=stop calling loop(), true=continue to call loop()
	end;
	
if (SM == S7) then
	SM = DONE;		-- TBD End the script 
	return true;	-- false=stop calling loop(), true=continue to call loop()
	end;
	
if (SM == S8) then
	SM = DONE;		-- TBD End the script 
	return true;	-- false=stop calling loop(), true=continue to call loop()
	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
print(os.date("cleanup()  %d %b %Y   %X"));
print("cleanup() : TIU#" .. MyTIUNo .. " Engine#" .. MyEngineNo .. " Debug " .. Debug());
StartRun = false;
Throttle(0, 0, MyEngineNo, MyTIUNo);Sleep(10);
DTOFinish = DTO(MyEngineNo, MyTIUNo);
ShutDown(0, MyEngineNo, MyTIUNo);
Sleep(5);
if (SoundOff) then
	SoundOnButton(MyEngineNo, MyTIUNo)
	end;
-- wait for dispatch list to empty
if (CountDispatchList() > 0) then
	ClearDispatchList();
	end;
print(string.format("Run Time = %.2f seconds", RunTime()));
print(string.format("Trip Odometer Finish %.2f Smiles", DTOFinish));
ShowTags(MyEngineNo, MyTIUNo);		-- dump out the Tags table	
return true;		-- false=cleanup failed, true=cleanup succeeded
end;
--[[---------------------------------------------------------------------------------------]]
function TAliquippa()
RunToStation(ALIQUIPPA, MyDirection);
return true;
end;
Function01Name, Function01Label = TAliquippa, StationName[ALIQUIPPA];
--[[---------------------------------------------------------------------------------------]]
function TCollege()
RunToStation(COLLEGE, MyDirection);
return true;
end;
Function02Name, Function02Label = TCollege, StationName[COLLEGE];
--[[---------------------------------------------------------------------------------------]]
function TStruthers()
RunToStation(STRUTHERS, MyDirection);
return true;
end;
Function03Name, Function03Label = TStruthers, StationName[STRUTHERS];
--[[---------------------------------------------------------------------------------------]]
function TJL()
RunToStation(J_L, MyDirection);
return true;
end;
Function04Name, Function04Label = TJL, StationName[J_L];
--[[---------------------------------------------------------------------------------------]]
function TMCKEES_ROCKS()
RunToStation(MCKEES_ROCKS, MyDirection);
return true;
end;
Function06Name, Function06Label = TMCKEES_ROCKS, StationName[MCKEES_ROCKS];
--[[---------------------------------------------------------------------------------------]]
function TNEWCASTLE()
RunToStation(NEWCASTLE, MyDirection);
return true;
end;
--[[---------------------------------------------------------------------------------------]]
function RunToStation(Station, Dir)
-- aim for the this Station
Target_Station = StationName[Station];
Target_Detector = SD.STOP(Station, Dir, DETECTOR);
Target_Reader = SD.STOP(Station, Dir, READER);
if (MyEngineNo == 7 or MyEngineNo == 13) then
	Target_Distance = SD.STOP(Station, Dir, PASSENGER);	--Passenger StoppingDistance;
	else
	Target_Distance = SD.STOP(Station, Dir, FREIGHT);	--Freight StoppingDistance;
	end
SetCounter(C4, Target_Distance);
print(Target_Station .. " Target Distance = " .. Target_Distance);
StartTheEngine();
return true;
end;
--[[---------------------------------------------------------------------------------------]]
function StartTheEngine()
	if (StartRun) then 	
		print(string.format("Run Time = %.2f Seconds", RunTime()));
		SetCounter(C2, S0);	
		SM = S0;		-- go wait for tag	
		return true; 
		end;	-- engine already running
		
	StartRun = true;
	-- start engine
	BumpCounter(C1);
	SetDirection(FORWARD, 0, MyEngineNo, MyTIUNo);
	EngSpeed = BASESPEED;
	print("StartTheEngine : Set speed to " .. EngSpeed);
	Throttle(EngSpeed, 0, MyEngineNo, MyTIUNo); -- set Speed to EngSpeed
	BumpCounter(C1);	-- Bump number of engine starts
	-- check that the engine has reached speed
	local StartTime = STARTTIME * 2;
	while (GetSpeed(MyEngineNo, MyTIUNo) ~= EngSpeed) do 
		if (StartTime == 0) then
			return EmergencyStop("StartTheEngine : Engine#" .. MyEngineNo .. " Did not Start");		-- failure
			end
		Sleep(2); 
		StartTime = StartTime - 2;
	end;
	print(string.format("Run Time = %.2f Seconds", RunTime()));
	SetCounter(C2, S0);	
	SM = S0;		-- go wait for tag
end;
--[[---------------------------------------------------------------------------------------]]
function IncTarget(Eng, TIU)
	Target_Distance = Target_Distance + 1;
	print("Target Distance = " .. Target_Distance);
	SetCounter(C4, Target_Distance);
	return true;
end;
Function16Name, Function16Label = IncTarget, "Target +1";
--[[---------------------------------------------------------------------------------------]]
function DecTarget(Eng, TIU)
	Target_Distance = Target_Distance -1;
	print("Target Distance = " .. Target_Distance);
	SetCounter(C4, Target_Distance);
	return true;
end;
Function17Name, Function17Label = DecTarget, "Target -1";
--[[---------------------------------------------------------------------------------------]]
function WBTarget(Eng, TIU)
	MyDirection = WB;
	print("Direction = Westbound");
	return true;
end;
Function28Name, Function28Label = WBTarget, "Westbound";
--[[---------------------------------------------------------------------------------------]]
function EBTarget(Eng, TIU)
	MyDirection = EB;
	print("Direction = Eastbound");
	return true;
end;
Function29Name, Function29Label = EBTarget, "Eastbound";
--[[---------------------------------------------------------------------------------------]]
function ShowTags(Eng, TIU)
	print("Tags detected:");
	--if (#Tags == 0) then
	--	print ("none");
	--	end
	for k,v in  pairs(Tags) do		-- dump out the Tags table
		print(k .. "  " .. v);
		end
	return true;
end;
Function23Name, Function23Label = ShowTags, "Show Tags";
--[[---------------------------------------------------------------------------------------]]
function SoundOffButton(Eng, TIU)
	print("Sound Off Button()");
	S1 = GetVolume(MASTER_VOLUME, Eng, TIU);	-- get Volume
	print(" Sound Off - Engine " .. Eng .. " Level was " .. S1);
	EngineSoundSave = S1;
	SoundOff = true;
	SetVolume(MASTER_VOLUME, 0, 0, Eng, TIU);
	return true;
end;
Function21Name, Function21Label = SoundOffButton, "Sound Off";
--[[---------------------------------------------------------------------------------------]]
function SoundOnButton(Eng, TIU)
	print("Sound On Button()");
	if (SoundOff) then
		S1 = EngineSoundSave;		
		print(" Sound On - Engine " .. Eng .. " Level restored to " .. S1);
		SoundOff = false;
		SetVolume(MASTER_VOLUME, S1, 0, Eng, TIU);
		end;
	return true;
end;
Function22Name, Function22Label = SoundOnButton, "Sound On";
--[[---------------------------------------------------------------------------------------]]





