--[=[
---------------------------------------------------------------------------
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 = "Sidings Test using Events"
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;
-- 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 = 10;
DONE = 11;
S0 = 0;
S1 = 1;
S2 = 2;
S3 = 3;
S4 = 4;
S5 = 5;
S6 = 6;
-- starting state
SM = IDLE;
StartRun = false;
BASESPEED = 20;
Direction = WB;	-- WB for all of these sidings
--[[---------------------------------------------------------------------------------------]]
ALIQUIPPA = 1;
COLLEGE = 2;
STRUTHERS = 3;
J_L = 4;
--[[---------------------------------------------------------------------------------------]]
-- Switches
local COLLEGE_WEST_AIU = 1;
local COLLEGE_WEST_CHAN = 1;
local COLLEGE_EAST_AIU = 1;
local COLLEGE_EAST_CHAN = 2;

local STRUTHERS_WEST_AIU = 2;
local STRUTHERS_WEST_CHAN = 5;
local STRUTHERS_EAST_AIU = 2;
local STRUTHERS_EAST_CHAN = 6;

local ALIQUIPPA_WEST_AIU = 1;
local ALIQUIPPA_WEST_CHAN = 9;
local ALIQUIPPA_EAST_AIU = 1;
local ALIQUIPPA_EAST_CHAN = 5;

--[[---------------------------------------------------------------------------------------]]
function setup(Engine, TIU)
print(os.date("setup() %d %b %Y   %X"));
-- Engine is not used in this function, it is set in the event() function
MyTIUNo = TIU;

SetLED(1, clRed, "LED1");
SetLED(2, clRed, "LED2");
SetLED(3, clRed, "LED3");
SetLED(4, clRed, "LED4");
--
print("setup() : TIU#" .. MyTIUNo .. " Debug " .. Debug());
print(string.format("Run Time = %.2f Seconds", RunTime()));

print("Please have dispatcher select the engine and a target station by sending an event..........");


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 hexidecimal
	--   Next 32 digits are the 16 bytes of Block 4 Information read from the Tag in hexidecimal
	--   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 
	Sleep(1);
	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 the Detector/Reader
	if (isEngine(EngineNo) and isDetector(Detector, Target_Detector) and isReader(Reader, Target_Reader)) then
		if (isTagFront(TagLocation)) then
			-- set stopping time delay to calculated value
			if (GetDirection(EngineNo, MyTIUNo) == FORWARD) then
				--print("S0 : Tag Detected in Forward");

				-- stop the engine
				-- time(seconds) = d(in) * 48 Smiles/mile * 3600 sec/hr * / (12 in/ft * 5280 ft/mile * X Smiles/hr)
				StopDelay =  (Target_Distance - SD.STOPDistance(EngineNo, EngSpeed)) * (48 / (12 * 5280 * EngSpeed)) * 3600;
				print(string.format("Target Distance = %d in - Stopping Distance = %.3f in - Speed %d Smph = Stop Delay %.3f seconds",
					Target_Distance, SD.STOPDistance(EngineNo, EngSpeed), EngSpeed, StopDelay));
				EngSpeed = 0;
				print("S0 : Set speed to " .. EngSpeed .. " after " .. StopDelay .. " seconds")
				Throttle(EngSpeed, StopDelay, EngineNo, MyTIUNo); -- set Speed to EngSpeed
				StopForwardScheduled = true;
				
				local StartDelay = StopDelay + STOPTIME;			
				-- set direction to reverse
				SetDirection(REVERSE, StartDelay, EngineNo, MyTIUNo);	
				-- start the engine moving in reverse
				EngSpeed = BASESPEED;
				StartDelay = StartDelay + 2;
				print("S0 : Set speed to " .. EngSpeed .. " after " .. StartDelay .. " seconds")
				Throttle(EngSpeed, StartDelay, EngineNo, MyTIUNo); -- set Speed to EngSpeed	
				BumpCounter(C1);	-- bump number of engine starts
				-- stay in S0
				return true;
				end;
			end;

		if (isTagRear(TagLocation)) then
			if (not StopForwardScheduled) then return Stop("S0 : Engine Front tag not detected"); end
			StopForwardScheduled = false;
			SetCounter(C2, S1);	-- go to S1			
			SM = S1			
			return true;
			end			
		end;
	-- Stay in S0
	return true;	-- false=stop calling loop(), true=continue to call loop()
	end;

	
if (SM == S1) then
	-- run reverse until the engine tag is detected over the Detector/Reader
	if (isEngine(EngineNo) and isDetector(Detector, Target_Detector) and isReader(Reader, Target_Reader)) then
		if (isTagRear(TagLocation)) then
			-- set stopping time delay to fixed value
			if (GetDirection(EngineNo, MyTIUNo) == REVERSE) then
				--print("S1 : Tag Detected in Reverse");

				-- stop the engine
				EngSpeed = 0;
				local StopDelay = 5;
				print("S1 : Set speed to " .. EngSpeed .. " after " .. StopDelay .. " seconds");
				Throttle(EngSpeed, StopDelay, EngineNo, MyTIUNo); -- set Speed to EngSpeed
				StopReverseScheduled = true;
				
				local StartDelay = 15;		
				-- set direction to forward
				SetDirection(FORWARD, StartDelay, EngineNo, MyTIUNo);	
				-- start the engine moving forward
				EngSpeed = BASESPEED;
				StartDelay = StartDelay + 2;
				print("S1 : Set speed to " .. EngSpeed .. " after " .. StartDelay .. " seconds");
				Throttle(EngSpeed, StartDelay, EngineNo, MyTIUNo); -- set Speed to EngSpeed
				BumpCounter(C1);	-- bump number of engine starts
				-- Stay in S1
				return true;				
				end;
			end;
			
		if (isTagFront(TagLocation)) then
			if (not StopReverseScheduled) then return Stop("S1 : Engine Rear tag not detected"); end
			StopReverseScheduled = false;
			SetCounter(C2, S0);	
			SM = S0;	
			return true;
			end;	
		end;
	-- Stay in S1
	return true;	-- false=stop calling loop(), true=continue to call loop()
	end;
	
if (SM == S2) then

	return true;	-- false=stop calling loop(), true=continue to call loop()
	end;
	
if (SM == S3) then

	return true;	-- false=stop calling loop(), true=continue to call loop()
	end;
	
if (SM == S4) then

	return true;	-- false=stop calling loop(), true=continue to call loop()
	end;
	
if (SM == S5) then
	Sleep(20);
	SM = DONE;		-- TBD End the script 
	return true;	-- false=stop calling loop(), true=continue to call loop()
	end;
	
if (SM == S6) then
	Sleep(20);
	SM = DONE;		-- TBD End the script 
	return true;	-- false=stop calling loop(), true=continue to call loop()
	end;
	
if (SM == S7) then
	Sleep(20);
	SM = DONE;		-- TBD End the script 
	return true;	-- false=stop calling loop(), true=continue to call loop()
	end;
	
if (SM == S8) then
	Sleep(20);
	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(Engine, TIU)
-- this function is called once when the user presses the [STOP] button
print(os.date("cleanup()  %d %b %Y   %X"));
print("cleanup() : TIU#" .. TIU .. " Engine#" .. Engine .. " Debug " .. Debug());
StartRun = false;
Throttle(0, 0, Engine, TIU);
DTOFinish = DTO(Engine, TIU);
-- 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));
Sleep(5);
ShutDown(0, Engine, TIU);
Sleep(5);
if (SoundOff) then
	SoundOnButton(Engine, TIU)
	end;
ShowTags(Engine, TIU);		-- dump out the Tags table	
return true;		-- false=cleanup failed, true=cleanup succeeded
end;
--[[---------------------------------------------------------------------------------------]]
function Aliquippa()
-- aim for the this reader
Target_Station = StationName[ALIQUIPPA];
Direction = WB;
Target_Detector = SD.STOP(ALIQUIPPA, Direction, DETECTOR);
Target_Reader = SD.STOP(ALIQUIPPA, Direction, READER);
if (MyEngineNo == 7 or MyEngineNo == 13) then
	Target_Distance = SD.STOP(ALIQUIPPA, Direction, PASSENGER);	--Passenger StoppingDistance;
	else
	Target_Distance = SD.STOP(ALIQUIPPA, Direction, FREIGHT);	--Freight StoppingDistance;
	end

SetCounter(C4, Target_Distance);
print("Aliquippa Target Distance = " .. Target_Distance);
StartTheEngine();
return true;
end;
Function01Name, Function01Label = Aliquippa, "Aliquippa";
--[[---------------------------------------------------------------------------------------]]
function College()
-- aim for the this reader
Target_Station = StationName[COLLEGE];
Direction = WB;
Target_Detector =  SD.STOP(COLLEGE, Direction, DETECTOR);
Target_Reader = SD.STOP(COLLEGE, Direction, READER);
if (MyEngineNo == 7 or MyEngineNo == 13) then
	Target_Distance = SD.STOP(COLLEGE, Direction, PASSENGER);	--Passenger StoppingDistance;
	else
	Target_Distance = SD.STOP(COLLEGE, Direction, FREIGHT);	--Freight StoppingDistance;
	end

SetCounter(C4, Target_Distance);
print("College Target Distance = " .. Target_Distance);
StartTheEngine();
return true;
end;
Function02Name, Function02Label = College, "College";
--[[---------------------------------------------------------------------------------------]]
function Struthers()
-- aim for the this reader
Target_Station = StationName[STRUTHERS];
Direction = WB;
Target_Detector = SD.STOP(STRUTHERS, Direction, DETECTOR);
Target_Reader = SD.STOP(STRUTHERS, Direction, READER);
if (MyEngineNo == 7 or MyEngineNo == 13) then
	Target_Distance = SD.STOP(STRUTHERS, Direction, PASSENGER);	--Passenger StoppingDistance;
	else
	Target_Distance = SD.STOP(STRUTHERS, Direction, FREIGHT);	--Freight StoppingDistance;
	end

SetCounter(C4, Target_Distance);
print("Struthers Target Distance = " .. Target_Distance);
StartTheEngine();
return true;
end;
Function03Name, Function03Label = Struthers, "Struthers";
--[[---------------------------------------------------------------------------------------]]
function FormAJL()
-- aim for the this reader
Target_Station = StationName[J_L];
Direction = WB;
Target_Detector = SD.STOP(J_L, Direction, DETECTOR);
Target_Reader = SD.STOP(J_L, Direction, READER);
if (MyEngineNo == 7 or MyEngineNo == 13) then
	Target_Distance = SD.STOP(J_L, Direction, PASSENGER);	--Passenger StoppingDistance;
	else
	Target_Distance = SD.STOP(J_L, Direction, FREIGHT);	--Freight StoppingDistance;
	end

SetCounter(C4, Target_Distance);
print("J&L Target Distance = " .. Target_Distance);
StartTheEngine();
return true;
end;
Function04Name, Function04Label = J_L, "J&L";
--[[---------------------------------------------------------------------------------------]]
function StartTheEngine()
	if (StartRun) then return true; end;	-- engine already running
	StartRun = true;
	-- start engine
	BumpCounter(C1);
	EngSpeed = BASESPEED;
	print("StartTheEngine : Set speed to " .. EngSpeed);
	Throttle(EngSpeed, 0, MyEngineNo, MyTIUNo); -- set Speed to EngSpeed
	BumpCounter(C1);
	-- check that the engine has reached speed
	local StartTime = STARTTIME * 2;
	while (GetSpeed(MyEngineNo, MyTIUNo) ~= BASESPEED) do 
		if (StartTime == 0) then
			return EmergencyStop("StartTheEngine : Engine#" .. MyEngineNo .. " Did not Start");		-- failure
			end
		Sleep(2); 
		StartTime = StartTime - 2;
	end;

	SetCounter(C2, S0);	
	SM = S0;		-- go wait for first tag
end;
--[[---------------------------------------------------------------------------------------]]
function IncTarget(Eng, TIU)
	Target_Distance = Target_Distance + 1;
	print("Target Distance = " .. Target_Distance);
	SetCounter(C4, Target_Distance);
	return true;
end;
Function06Name, Function06Label = IncTarget, "Target +1";
--[[---------------------------------------------------------------------------------------]]
function DecTarget(Eng, TIU)
	Target_Distance = Target_Distance -1;
	print("Target Distance = " .. Target_Distance);
	SetCounter(C4, Target_Distance);
	return true;
end;
Function07Name, Function07Label = DecTarget, "Target -1";
--[[---------------------------------------------------------------------------------------]]
--[[
function B8(Eng, TIU)
Direction = EB;
return true;
end;
Function08Name, Function08Label = B8, "East Bound";
--]]
--[[---------------------------------------------------------------------------------------]]
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;
Function09Name, Function09Label = 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;
Function05Name, Function05Label = 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;
Function10Name, Function10Label = SoundOnButton, "Sound On";
--[[---------------------------------------------------------------------------------------]]
function event(FromPC, P1, P2, P3)
	-- receive an event from the dispatcher and setup the engine in use
	-- and the destination
	MyDest = P1;
	MyEngineNo = P2;
	Name, PS = GetName(MyEngineNo);
	print("Engine#" .. MyEngineNo .. " " .. Name .. " PS" .. PS);
	Title(title .. " - " .. GetName(MyEngineNo));
		
	Smoke(OFF, 0, MyEngineNo, MyTIUNo);
	StartUp(0, MyEngineNo, MyTIUNo);	-- start selected engine
	-- let engine startup
	Sleep(15);
	SM = IDLE;
	SetCounter(C2, IDLE);
	print(string.format("Run Time = %.2f Seconds", RunTime()));
	DTOStart = DTO(MyEngineNo, MyTIUNo);
	print(string.format("Trip Odometer Start %.2f Smiles", DTOStart));
	print(string.format("Run Time = %.2f Seconds", RunTime()));
	-- set acceleration and decelleration values
	Decell = 1;
	Rate(1, Decell, 0, MyEngineNo, MyTIUNo);
	print("setup : Set decelleration rate to " .. Decell);
	StopForwardScheduled = false;
	StopReverseScheduled = false;

	if (MyDest == ALIQUIPPA) then
		Aliquippa();
		end;
	if (MyDest == COLLEGE) then
		College();
		end;
	if (MyDest == STRUTHERS) then
		Struthers();
		end;
	if (MyDest == J_L) then
		FormAJL();
		end;
	return true;
end;
--[[---------------------------------------------------------------------------------------]]




