--[[
---------------------------------------------------------------------------
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
-- Communications Test - read up RAM values from an Engine and check their values
--
title = "Communcations Test"
--
-- Use this script for testing with a real TIU.
-- Use "Communications Test for RTCEngine.lua" for testing with RTCEngine.
--
-- This test repeatedly reads several RAM values in the Engine and compares
-- the values returned with initialially read values.
--
-- We should be able to do this forever without any detected errors.
--
-- When you press STOP to end the test, the script prints out a "Loops per second"
-- value which can be used to determine the relative speed of the communications hardware
-- when run with different configurations (ie: WiFi vs RS232, panStamp vs Seeeduino, etc).
--
-- Typical results:
-- USB with Seeeduino : 0.74 loops/second
-- WiFi with ESP8266  : 1.03 loops/second
--
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 _;

-- Counter Labels
Counter01Label = "Data Errors";
Counter02Label = "Comm Errors";
Counter03Label = "Loops";
Counter04Label = "Packets";

local Check010;
local Check008;
local Check012;
local Check004;

local DebugThreshold = 3;

local PacketCount = 0;
local PreviousCount = 0;

local ReturnValue = true;	-- if false, test terminates on first error, o/w it continues to loop

local MyEngineNo, MyTIUNo;
--[[---------------------------------------------------------------------------------------]]
function setup(Engine)
	-- the setup() function is called once at the beginning of the run
	MyEngineNo = Engine;
	MyTIUNo = GetTIUNo(MyEngineNo);	-- TIU associated with MyEngineNo	
	print("setup(): Engine Number " .. Engine .. " TIU Number " .. MyTIUNo .. " Debug " .. Debug());
	print("This script will not work with RTCEngine. There is a version of the script for that emulator.");
	-- setup check data
	Check010 = RAM(0x010, Engine, MyTIUNo);	-- Engine Volume
	print("RAM 0x010 (Engine Volume) = ", Check010);
	Check012 = RAM(0x012, Engine, MyTIUNo);	-- Accent Volume
	print("RAM 0x012 (Accent Volume) = ", Check012);	
	Check008 = RAM2(0x008, Engine, MyTIUNo);	-- q1F84 	-- Scaling Factor	typical value 0x54AB  == 21675
	print("RAM2 0x008 (Scaling Factor) = ", Check008);
	Check004 = RAM4(0x004, Engine, MyTIUNo);	-- Unknown
	print("RAM4 0x004 (Address 0x4) = ", Check004);	
	print("Runs quietly (with status msg about every 60 seconds) unless Debug is at least " .. DebugThreshold);
	return true;		-- false=setup failed, true=setup succeeded
	end
--[[---------------------------------------------------------------------------------------]]
-- Send a RAM read command look for the result
-- Check the result value
-- Communications failure will trigger a retry
--	
function RAMFetch(addr, CheckVal, Engine, TIU)
--
-- readup a value from RAM and check it with correct value
-- Returns true on success, false on either Communications or Data error
--
local RAMValue1 = RAM(addr, Engine, TIU);	-- q1F84	-- Status Byte 2
PacketCount = PacketCount + 2;
local StringVal = string.format("RAMFetch(0x%X)", addr);
if (RAMValue1 == nil) then
	Beep();
	printc(clRed, StringVal, "RAMFetch Communications Error 1");
	BumpCounter(COUNTER02);	-- Comm Error
	-- read again
	local RAMValue2 = RAM(addr, Engine, TIU);	-- q1F84	-- Status Byte 2
	PacketCount = PacketCount + 2;
	if (RAMValue2 == nil) then
		Beep();
		printc(clRed, StringVal, "RAMFetch Communications Error 2");
		BumpCounter(COUNTER02);	-- Comm Error
		return ReturnValue; -- Comm Error after two tries
		else
		if (Debug() >= DebugThreshold) then print(StringVal, " (", CheckVal, ") = ", RAMValue2); end;
		if (RAMValue2 ~= CheckVal) then
			printc(clRed, StringVal, "RAMFetch Data Error 2");
			BumpCounter(COUNTER01);
			return ReturnValue; -- Data Error on 2nd try
			end;
		return true;	-- good data on 2nd read
		end;
	else
	if (Debug() >= DebugThreshold) then print(StringVal, " (", CheckVal, ") = ", RAMValue1); end;
	if (RAMValue1 ~= CheckVal) then
		printc(clRed, StringVal, "RAMFetch Data Error 1");
		BumpCounter(COUNTER01);
		return ReturnValue;	-- Data Error on 1st try
		end;
	return true;	-- good data on 1st read
	end;
return true;	-- should never reach here
end;
--[[---------------------------------------------------------------------------------------]]
function RAMFetch2(addr, CheckVal, Engine, TIU)
--
-- readup a value from RAM and check it with correct value
-- Returns true on success, false on either Communications or Data error
--
local RAMValue1 = RAM2(addr, Engine, TIU);	-- q1F84	-- Status Byte 2
PacketCount = PacketCount + 3;
local StringVal = string.format("RAMFetch2(0x%X)", addr);
if (RAMValue1 == nil) then
	Beep();
	printc(clRed, StringVal, "RAMFetch2 Communications Error 1 - got nil");
	BumpCounter(COUNTER02);	-- Comm Error
	-- read again
	local RAMValue2 = RAM2(addr, Engine, TIU);	-- q1F84	-- Status Byte 2
	PacketCount = PacketCount + 3;
	if (RAMValue2 == nil) then
		Beep();
		printc(clRed, StringVal, "RAMFetch2 Communications Error 2 - got nil");
		BumpCounter(COUNTER02);	-- Comm Error
		return false; -- Comm Error after two tries
		else
		if (Debug() >= DebugThreshold) then print(StringVal, " (", CheckVal, ") = ", RAMValue2); end;
		if (RAMValue2 ~= CheckVal) then
			printc(clRed, StringVal, "RAMFetch2 Data Error 2 - got " + RAMValue2);
			BumpCounter(COUNTER01);
			return false; -- Data Error on 2nd try
			end;
		return true;	-- good data on 2nd read
		end;
	else
	if (Debug() >= DebugThreshold) then print(StringVal, " (", CheckVal, ") = ", RAMValue1); end;
	if (RAMValue1 ~= CheckVal) then
		printc(clRed, StringVal, "RAMFetch2 Data Error 1 - got " + RAMValue1);
		BumpCounter(COUNTER01);
		return false;	-- Data Error on 1st try
		end;
	return true;	-- good data on 1st read
	end;
return true;	-- should never reach here
end;
--[[---------------------------------------------------------------------------------------]]
function RAMFetch4(addr, CheckVal, Engine, TIU)
--
-- readup a value from RAM and check it with correct value
-- Returns true on success, false on either Communications or Data error
--
local RAMValue1 = RAM4(addr, Engine, TIU);	-- q1F84	-- Status Byte 2
PacketCount = PacketCount + 5;
local StringVal = string.format("RAMFetch4(0x%X)", addr);
if (RAMValue1 == nil) then
	Beep();
	printc(clRed, StringVal, "RAMFetch4 Communications Error 1 - got nil");
	BumpCounter(COUNTER02);	-- Comm Error
	-- read again
	local RAMValue2 = RAM4(addr, Engine, TIU);	-- q1F84	-- Status Byte 2
	PacketCount = PacketCount + 5;
	if (RAMValue2 == nil) then
		Beep();
		printc(clRed, StringVal, "RAMFetch4 Communications Error 2 - got nil");
		BumpCounter(COUNTER02);	-- Comm Error
		return false; -- Comm Error after two tries
		else
		if (Debug() >= DebugThreshold) then print(StringVal, " (", CheckVal, ") = ", RAMValue2); end;
		if (RAMValue2 ~= CheckVal) then
			printc(clRed, StringVal, "RAMFetch4 Data Error 2 - got " + RAMValue2);
			BumpCounter(COUNTER01);
			return false; -- Data Error on 2nd try
			end;
		return true;	-- good data on 2nd read
		end;
	else
	if (Debug() >= DebugThreshold) then print(StringVal, " (", CheckVal, ") = ", RAMValue1); end;
	if (RAMValue1 ~= CheckVal) then
		printc(clRed, StringVal, "RAMFetch4 Data Error 1 - got " .. RAMValue1);
		BumpCounter(COUNTER01);
		return false;	-- Data Error on 1st try
		end;
	return true;	-- good data on 1st read
	end;
return true;	-- should never reach here
end;	
--[[---------------------------------------------------------------------------------------]]
function loop(Engine)
	-- The loop() function is repeatedly called
	-- Always call Sleep() or Yield() at least once in this function if it returns true
	--
	SetCounter(COUNTER04, PacketCount);
	BumpCounter(COUNTER03);
	local myelapsedtime = GetCounter(ELAPSEDTIME);	-- elapsed time in seconds
	if ((myelapsedtime > 0) and ((myelapsedtime % 60) == 0)) then 
		print(string.format("loop %d seconds elapsed time. %f loops/minute, %f packets/second", myelapsedtime, GetCounter(COUNTER03)/(myelapsedtime/60), PacketCount/myelapsedtime));
		end;
	
	-- test the result of fetching values from the Engine
	if (RAMFetch(0x010, Check010, Engine, MyTIUNo) == false) then 	-- Engine Volume
		return ReturnValue; end;

	Yield();
	--print("Bell Off");
	if (Bell(OFF, 0, Engine, MyTIUNo) == nil) then	-- bFFFB
		Beep();
		BumpCounter(COUNTER02);
		return Stop("Bell Off command failed");
		end;
	PacketCount = PacketCount + 1;
		
	if (RAMFetch2(0x008, Check008, Engine, MyTIUNo) == false) then	-- Scaling Factor
		return ReturnValue; end;

	if (RAMFetch(0x012, Check012, Engine, MyTIUNo) == false) then	-- Accent Volume
		return ReturnValue; end;

	if (RAMFetch4(0x004, Check004, Engine, MyTIUNo) == false) then	-- Address 4
		return ReturnValue; end;
		
	Yield();
	--print("Whistle Off");
	if (Whistle(OFF, 0, Engine, MyTIUNo) == nil) then	-- bFFFD
		Beep();
		BumpCounter(COUNTER02);
		return Stop("Whistle Off command failed");
		end;
	PacketCount = PacketCount + 1;
	
	Yield();

	if (RAMFetch2(0x008, Check008, Engine, MyTIUNo) == false) then	-- Scaling Factor
		return ReturnValue; end;

	if (RAMFetch(0x012, Check012, Engine, MyTIUNo) == false) then	-- Accent Volume
		return ReturnValue; end;

	Yield();
	return true;	-- false=do not loop, true=continue to loop
	end
--[[---------------------------------------------------------------------------------------]]
function cleanup(Engine)
	-- the cleanup() function is called once when the [STOP] button is pressed
	print(string.format("cleanup %.2f", RunTime()));
	-- print loops per second
	print("Loops per second = ", GetCounter(COUNTER03)/GetCounter(ELAPSEDTIME));
	return true;		-- false=cleanup failed, true=cleanup succeeded
	end
--[[---------------------------------------------------------------------------------------]]
