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

© Copyright 2022 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
-- Tag Detection Test
title = "Tag Detection Test"
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 _;


local TagCount = 0;
-- Counter Labels
Counter01Label = "tags()";
Counter02Label = "nametags()";
Counter03Label = "uidtags()";
Counter04Label = "response()";

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

local SoundOn = true;
local RadioPortConnected, TIUConnected, RFIDPortConnected;
local MyEngineNo, MyTIUNo;
local PacketLen;
local TagCount = 0;

-- Configuration Variables
local AutoBellWhistle = true;
	
--[[---------------------------------------------------------------------------------------]]
function setup(Engine, TIU)
	-- this function is called once when the [Run Script] button is pushed
	-- Engine : Engine number selected on the RTC Main window
	-- TIU : TIU number selected on the RTC Main window
	print("setup()");

	-- 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
	-- Requires at least 8 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 power on layout and try again.")
		end;
	--	
	MyEngineNo = Engine;	-- Engine number selected on the RTC Main window
	MyTIUNo = TIU;		-- TIU number selected on the RTC Main window
	-- debug level as selected on the Debug window, values 0 (off) to 9 (maximum)
	if (Debug() > 0) then 
		print(string.format("setup(): Debug level %d", Debug())); 
		print("setup(): Engine Number ", MyEngineNo ," TIU Number ", TIU);
		end
	Title(title .. " - " .. GetName(Engine));
	print("TIU : " .. TIU);
	assert((TIU >= 1 and TIU <= 5),"TIU Number out of range");
	--
	print(string.format("RunTime = %.2f seconds", RunTime()));
	return true;		-- false=setup failed, true=setup succeeded
	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() >= 3) then print("tag() : UID + Data Length = ", PacketLen); end;
	if (Debug() >= 4) 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 (SoundOn) 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)));
		-- follow on to process the detected engine
		else	-- it's a car
		if (SoundOn) 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));

		if (EngineNo == 0 and (CarModel == CABOOSE or CarModel == OBSERVATION)) then
			if ((GetCounter(COUNTER01)) % 5 ~= 0) then
				return Stop("Tag counter not multiple of 5");
				end;				
			end			
			
		-- ignore other 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() >= 2) 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 nametag(Detector, Reader, UID, CarName)
	-- This function is called each time a tag programmed with 32 bytes of car name is detected (TAGDATABLOCK5)
	-- UID contains 8 characters (4 hex digits) of UID and CarName contains 32 characters of car name
	-- of block 5 data from the programmed tag. 
	Beep(QUESTION);	-- sound a tone
	BumpCounter(COUNTER02);	
	local UIDLen = #UID;
	local CarNameLen = #CarName
	if (Debug() >= 3) then print("nametag() : UID Length = ", UIDLen); end;
	if (Debug() >= 3) then print("nametag() : CarName Length = ", CarNameLen); end;
	print(string.format([[nametag(%.2f) : Detector %d Reader %d]], RunTime(), Detector, Reader));
	if (Debug() >= 2) then print(string.format("nametag() : UID  %s", string.sub(UID, 1, 8))); end	
	if (Debug() >= 2) then print(string.format("nametag() : Car Name  %s", string.sub(CarName, 1, 16))); end	
	return true;	-- true=continue to process tags, false=stop processing tags
	end
--[[---------------------------------------------------------------------------------------]]
function uidtag(Detector, Reader, UID)
	-- This function is called each time an unprogrammed tag (TAGDETECTED) is detected
	-- Only the tag UID is returned
	-- UID contains 8 characters (4 hex digits) of UID	.
	-- When used as an error catch all, it could call EmergencyStop().	
	Beep(CRITICAL_STOP);	-- sound a tone
	BumpCounter(COUNTER03);	
	local UIDLen = #UID;
	if (Debug() >= 3) then print("uidtag() : UID Length = ", UIDLen); end;
	printc(clRed, string.format([[uidtag(%.2f) : Detector %d Reader %d]], RunTime(), Detector, Reader));
	--   8 digits are the 4 byte Tag UID in hexidecimal
	if (Debug() >= 2) then print(string.format("uidtag() : UID  %s", string.sub(UID, 1, 8))); end		
	TagCount = TagCount + 1;	-- count the number of tags	
	return true;	-- true=continue to process tags, false=stop processing tags
	end
--[[---------------------------------------------------------------------------------------]]
function response(Detector, Reader, Type, RespPacket)
	-- This function is called for all response from the Detectors
	-- This means that for regular block 4 tags (TAGDATABLOCK4) and for UID only tags (TAGDETECTED), this
	-- function is called right after tag() or uidtag() is called.
	-- It is up the user to skip those responses if he does want to do additional processing.
	-- RespPacket may contain a variable number of characters - inlcudes all header fields, data fields and trailer fields 
	-- THIS IS ONLY USEFUL WHEN DEBUGGING.
	-- Using this function requires an understanding of the RAW Tag Packet format.
	-- When used as an error catch all, it could call EmergencyStop() when an error packet is received.
	--Beep();	-- sound a tone
	BumpCounter(COUNTER04);	
	if (Debug() < 1) then return true; end;
	PacketLen = #RespPacket;
	-- Packet Types are defined in defines.lua
	if (Debug() >= 3) then print(string.format("response() : Header + UID + Data Length = %d Packet Type = 0x%02X", PacketLen, Type)); end;
	printc(clBlue, string.format([[response(%.2f) : Detector %d Reader %d Packet Type = 0x%02X]], RunTime(), Detector, Reader, Type));
	-- The complete packet received is in "RespPacket" - inlcudes all header fields, data fields and trailer fields 
	if (Debug() >= 2) then print(string.format("response() : Packet  %s", RespPacket)); 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("All Newly Detected Tags:");
	for k,v in  pairs(Tags) do		-- dump out the Tags table
		--                           . . . . . . . . . . . . . . . .
		-- print format:  6619044A  88080101030108011B02202031353536
		print(k .. "  " .. v);
		end
	print(string.format("cleanup() Complete at %.2f", RunTime()));
	--
	return true;		-- false=cleanup failed, true=cleanup succeeded
	end
--[[---------------------------------------------------------------------------------------]]
function tick(Engine, TIU)
	-- this is called once per second while waiting for RFID tags to arrive
	-- Do not call Sleep() from this function
	if (Debug() > 8) then print(string.format("tick() : RunTime = %.2f seconds", RunTime())); end;
	return true;
	end
--[[---------------------------------------------------------------------------------------]]
function DTOButton(Eng, TIU)
	print(string.format("Engine %d Trip Odometer %.3f Smiles", Eng, DTO(Eng, TIU)));
	return true;
end
Function01Name, Function01Label = DTOButton, "Show DTO";
--[[---------------------------------------------------------------------------------------]]
function DODButton(Eng, TIU)
	print(string.format("Engine %d Odometer %.3f Smiles", Eng, DOD(Eng, TIU)));
	return true;
end
Function02Name, Function02Label = DODButton, "Show DOD";
--[[---------------------------------------------------------------------------------------]]
function SoundOffButton(Eng, TIU)
	print("Sound Off");
	SoundOn = false;
	return true;
end
Function04Name, Function04Label = SoundOffButton, "Sound Off";
--[[---------------------------------------------------------------------------------------]]
function SoundOnButton(Eng, TIU)
	print("Sound On");
	SoundOn = true;
	return true;
end
Function05Name, Function05Label = SoundOnButton, "Sound On";
--[[---------------------------------------------------------------------------------------]]
function event(FromPC, Type, P1, P2, P3)
print("event() : Running - From PC# ", FromPC, " EventType = ", Type, " Parameters = ", P1, " ", P2, " ", P3);
	return true;		-- false=event failed, true=event succeeded	
	end
--[[---------------------------------------------------------------------------------------]]
