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

© Copyright 2019 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
--[[---------------------------------------------------------------------------------------]]
-- This test measures the stopping distance of an engine.
-- Set the starting and ending speeds and the starting and ending deceleration rates.
-- The script will run all combinations and print the results.
--
-- Creates a table constructor for insertion into the
-- "LayoutX.lua" scripts for each layout
-- [11] = {[1] = 0.0, [2] = 0.5}
--
-- This seems to have trouble running in Debug mode. Maybe too much output
-- to keep up with the repeated 'q' commands
--[[---------------------------------------------------------------------------------------]]
title = "Distance Measurement Test"

require("defines");

--[[
When using this module, all variables (including functions!) must be declared through 
a regular assignment (even assigning nil will do) in a strict scope before being used 
anywhere or assigned to inside a nested scope.
https://github.com/lua-stdlib/strict
]]
local _ENV = require 'std.strict' (_G)	-- strict.lua will detect undeclared global variables
local _;

local TagCount = 0;
-- Counter Labels

Counter01Label = "tags()";
TAGCOUNT = COUNTER01;
Counter02Label = "Ping Count";
PINGCOUNT = COUNTER02;
Counter03Label = "Total";
PINGTOTAL = COUNTER03;
Counter04Label = "Lost";
PINGLOST = COUNTER04;

local Tags = {};		-- table to hold all of the tags detected in sequence
local Name;
local SoundOff = false;
local BeepOn = false;
local RadioPortConnected, TIUConnected, RFIDPortConnected;
local MyEngineNo, MyTIUNo;
local PacketLen;
local TagCount = 0;
local PS;
local EngineSoundSave, AccentSoundSave;
local DTOStart, DTOFinish;
local TableConstructor = {};
local OldSmiles = 0.0;

local PC = 0;
local PCloss = 0;
local P4avg = 0;
local P4ping = 0;
local P4localtotal = 0;

local EngSpeed = 20;
--[[---------------------------------------------------------------------------------------]]
function setup(Engine)
--
MyEngineNo = Engine;	-- Engine number selected on the RTC Main window
MyTIUNo = GetTIUNo(MyEngineNo);	-- TIU associated with MyEngineNo 
--
print(string.format("setup(): Debug level %d", Debug())); 
		
print("setup : TIU " .. MyTIUNo .. " Engine " .. MyEngineNo .. " Debug " .. Debug());
assert((MyTIUNo >= 1 and MyTIUNo <= 5),"TIU Number out of range");
-- make sure that RFID is connected
RadioPortConnected, TIUConnected, RFIDPortConnected = CommStatus();	
--print("RadioPortConnected : " .. tostring(RadioPortConnected) .. " - TIUConnected : " .. tostring(TIUConnected) .. " - RFIDPortConnected : " .. tostring(RFIDPortConnected));
if (not RFIDPortConnected) then
	print("RFID Port must be enabled for this script");
	return false;	-- false=setup failed
	end

print(string.format("Run Time = %.2f Seconds", RunTime()));

if (Smoke(OFF, 0, MyEngineNo, MyTIUNo) == nil) then
		return Stop("Smoke() failed");
		end;
Name, PS = GetName(MyEngineNo);
print("Engine#" .. MyEngineNo .. " " .. Name .. " PS" .. PS);
Title(title .. " - " .. GetName(MyEngineNo));

-- start selected engine
if (StartUp(0, MyEngineNo, MyTIUNo) == nil) then
		return Stop("StartUp() failed");
		end;
-- let engine startup
Sleep(7);
SoundOffButton(MyEngineNo, MyTIUNo);		-- run quietly
Sleep(2);
-- Stop the Garbage collector since this is a real-time program
collectgarbage("stop")
print("Setup : Garbage Collection stopped");
--

print(string.format("Run Time = %.2f Seconds", RunTime()));
DTOStart = DTO(MyEngineNo, MyTIUNo);
if (DTOStart  == nil) then
		return Stop("DTO() failed");
		end;

print(string.format("Trip Odometer Start = %.2f Smiles", DTOStart));
print(string.format("Run Time = %.2f Seconds", RunTime()));
print(string.format("Smiles  Delta  Detector Reader             Odometer    Seconds"));
TableConstructor = "[" .. Engine .. "] = {";

if(Debug() > 4) then print("Set speed to " .. EngSpeed .. " Smph"); end
if (Throttle(EngSpeed, 0, MyEngineNo, MyTIUNo) == nil) then
	return Stop("Throttle() failed");
	end;
return true;
end
--[[---------------------------------------------------------------------------------------]]
function loop()
local P4 = Ping(MyEngineNo, MyTIUNo);
if (P4 ~= nil) then
	print(string.format("Ping success = %d", P4));
	BumpCounter(PINGCOUNT);
	PC = PC + P4;
	PCloss = PCloss + (100 - P4);
	SetCounter(PINGTOTAL, PC);
	SetCounter(PINGLOST, PCloss);
	-- TODO keep average p4
	P4ping = P4ping + 1;
	P4localtotal = P4localtotal + P4;
	P4avg = P4localtotal / P4ping;
	else
	print(string.format("Ping failed"));
	end;
Sleep(1);	-- needed so as to not hit the engine too hard with the p4 ping
return true;	-- false=stop calling loop(), true=continue to call loop()
end;
--[[---------------------------------------------------------------------------------------]]
function tag(Detector, Reader, EngineNo, TagLocation, CarModel, Railroad, CarNumber, TagPacket)
	-- This function is called each time a tag programmed with 32 bytes of data is detected (TAGDATABLOCK4)
	-- TagPacket contains 8 characters (4 hex digits) of UID and 32 characters
	-- of block 4 data from the programmed tag.

	BumpCounter(COUNTER01);	
	PacketLen = #TagPacket;

	--if (Debug() >= 2) then print("tag() : UID + Data Length = ", PacketLen); end;
	if (Debug() >= 5) 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
	
	if (EngineNo > 0) then	-- its an engine
		if (BeepOn) then Beep(ASTERISK); end;
		
		if (TagLocation == TAG_ON_FRONT_TRUCK) then 
			local DTOatTag = DTO(EngineNo, MyTIUNo);
			if (DTOatTag == nil) then
				return Stop("DTO() failed");
				end;	
			--if(Debug() > 1) then print(string.format("S4 : Trip Odometer at " .. Spd .. " Smph is %.5f Smiles", DTOatTag)); end		
			local Smiles = DTOatTag - DTOStart;
		
			print(string.format("%7.5f  %7.5f  %2d   %2d                  %6.3f      %6.1f", 
				Smiles, Smiles - OldSmiles, Detector, Reader, DTOatTag, RunTime()));
			OldSmiles = Smiles;
			--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)));
			-- TODO Print average p4 back to last tag detection
			print(string.format("Ping average (since previous tag) : %5.1f ", P4avg));
			P4localtotal = 0;
			P4ping = 0;
			P4avg = 0;
			end;
		-- follow on to process the detected engine
		else	-- it's a car
		if (BeepOn) then Beep(EXCLAMATION); end;
		--printc(clBlue, string.format([[tag(%.2f) : Detector %d Reader %d Car %s %s #%s]],
		--	RunTime(), Detector, Reader, GetRailroadName(Railroad), GetCarName(CarModel), CarNumber));
		-- ignore non engine tags
		return true;	-- true=continue to process tags, false=stop processing 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() >= 4) then print(string.format("tag() : Packet  %s  %s", string.sub(TagPacket, 1, 8),
		string.sub(TagPacket,9,40) )); end

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

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

function cleanup()
--
print("cleanup : TIU " .. MyTIUNo .. " Engine " .. MyEngineNo .. " Debug " .. Debug());

if (SetSpeed(0, 0, MyEngineNo, MyTIUNo) == nil) then
	print("SetSpeed() failed in cleanup()");
	end;
DTOFinish = DTO(MyEngineNo, MyTIUNo);
if (DTOFinish == nil) then
	print("DTO() failed in cleanup()");
	end;
-- wait for dispatch list to empty
while(CountDispatchList() > 0) do Sleep(1); end
print(string.format("Run Time = %.2f Seconds", RunTime()));
print(string.format("Trip Odometer Finish = %.2f Smiles", DTOFinish));

--
if (SoundOff) then
	SoundOnButton(MyEngineNo, MyTIUNo)
	end;
--
if (ShutDown(0, MyEngineNo, MyTIUNo) == nil) then
	print("ShutDown() failed in cleanup()");
	end;
	
print("All Newly Detected Tags:");
for k,v in  pairs(Tags) do		-- dump out the Tags table
	--                           . . . . . . . . . . . . . . . .
	-- print format:  6619044A  88080101030108011B02202031353536
	print(k .. "  " .. v);
	end
-- restart the garbage collector
collectgarbage("restart") ;
return true;
end
--[[---------------------------------------------------------------------------------------]]
function ShowDTO()
	print(string.format("Run Time = %.2f Seconds", RunTime()));
	print(string.format("Trip Odometer = %.2f Smiles", DTO(MyEngineNo, MyTIUNo)));
	return true;
end
Function10Name, Function10Label = ShowDTO, "Trip Odometer";
--[[---------------------------------------------------------------------------------------]]
function SoundOffButton()
	print("Sound Off Button()");
	local SD = GetVolume(ENGINE_VOLUME, MyEngineNo, MyTIUNo);	-- get Engine Volume
	if (SD == nil) then
		return Stop("GetVolume(ENGINE_VOLUME) failed in SoundOffButton()");
		end; 
	if (SD == 0) then SD = 30; end;
	print(" Sound Off - Engine " .. MyEngineNo .. " Engine Level was " .. SD);
	EngineSoundSave = SD;
	
	SD = GetVolume(ACCENT_VOLUME, MyEngineNo, MyTIUNo);	-- get Accent Volume
	if (SD == nil) then
		return Stop("GetVolume(ACCENT_VOLUME) failed in SoundOffButton()");
		end; 
	if (SD == 0) then SD = 30; end;
	print(" Sound Off - Engine " .. MyEngineNo .. " Accent Level was " .. SD);
	AccentSoundSave = SD;
	
	SoundOff = true;
	SetVolume(ENGINE_VOLUME, 0, 0, MyEngineNo, MyTIUNo);
	SetVolume(ACCENT_VOLUME, 0, 0, MyEngineNo, MyTIUNo);
	return true;
end
Function04Name, Function04Label = SoundOffButton, "Sound Off";
--[[---------------------------------------------------------------------------------------]]
function SoundOnButton()
	print("Sound On Button()");
	if (SoundOff) then	
		local SD = EngineSoundSave;	
		print(" Sound On - Engine " .. MyEngineNo .. " Engine Level restored to " .. SD);
		SoundOff = false;;
		SD = SetVolume(ENGINE_VOLUME, SD, 0, MyEngineNo, MyTIUNo);
		if (SD == nil) then
			return Stop("SetVolume(ENGINE_VOLUME) failed in SoundOnButton()");
			end	

		SD = AccentSoundSave;	
		print(" Sound On - Engine " .. MyEngineNo .. " Accent Level restored to " .. SD);
		SoundOff = false;;
		SD = SetVolume(ACCENT_VOLUME, SD, 0, MyEngineNo, MyTIUNo);
		if (SD == nil) then
			return Stop("SetVolume(ACCENT_VOLUME) failed in SoundOnButton()");
			end				
		end;
	return true;
end
Function05Name, Function05Label = SoundOnButton, "Sound On";
--[[---------------------------------------------------------------------------------------]]
function BeepOffButton()
	print("Beep Off");
	BeepOn = false;
	return true;
end
Function06Name, Function06Label = BeepOffButton, "Beep Off";
--[[---------------------------------------------------------------------------------------]]
function BeepOnButton()
	print("Beep On");
	BeepOn = true;
	return true;
end
Function07Name, Function07Label = BeepOnButton, "Beep On";
--[[---------------------------------------------------------------------------------------]]

