--[[
---------------------------------------------------------------------------
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 values using RTCEngine and check their value
--
title = "Communcations Test for RTCEngine"
--
-- Use this script for testing with RTCEngine.
-- Use "Communications Test.lua" for testing with a real TIU.
-- This script reads up wrong values for 0x024 if "Engine Status" function is started
--
-- This test repeatedly reads several EEPROM 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";

local Check024 = 15;
local Check008 = 0;
local Check008i = 0;
local Check009 = 0x85;
local Check067 = 0;

local DebugThreshold = 3;

local PacketCount = 0;
local PreviousCount = 0;

local oldElapsedTime = 0;

local MyEngineNo, MyTIUNo;

--[[---------------------------------------------------------------------------------------]]
function setup(Engine)
	-- the setup() function is called once at the beginning of the run
	MyEngineNo = Engine;	-- Engine number selected on the RTC Main window
	MyTIUNo = GetTIUNo(MyEngineNo);	-- TIU associated with MyEngineNo	
	print("setup(): Engine Number " .. Engine .. " TIU Number " .. MyTIUNo .. " Debug " .. Debug());
	print("This script is only for RTCEngine. There is a version of the script for a real TIU.");
	-- setup check data
	Check024 = RAM(0x024, Engine, MyTIUNo);	-- q1F84	-- Status Byte 2
	print("RAM 0x024 = ", Check024);
	--
	-- Note 0x008 and 0x009 is the scale factor.
	-- In RTCEngine, it is hard coded as 0x5485 (big endian)
	-- RAM 0x008 = 84 (dec) 0x54 and RAM 0x009 = 133 (dec) 0x85
	--	
	Check008i = RAM2(0x008, Engine, MyTIUNo);	-- q3F84	-- Scaling Factor
	print("RAM4 0x008 = ", Check008i);
	if (Check008i ~= 0x5485) then
		return Stop("Did not read correct value from RAM 0x008");
		end;
	Check009 = RAM(0x009, Engine, MyTIUNo);	-- q3FB3	-- Scaling Factor only one byte
	print("RAM 0x009 = ", Check009);
	if (Check009 ~= 0x85) then
		return Stop("Did not read correct value from RAM 0x009");
		end;
	Check067 = RAM(0x067, Engine, MyTIUNo);	-- q3F84	-- Direction
	print("RAM 0x067 = ", Check067);
	print("Runs quietly unless Debug is at least " .. DebugThreshold);
	return true;		-- false=setup failed, true=setup succeeded
	end
	
--[[---------------------------------------------------------------------------------------]]
-- Send a RAM read command
-- 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("RAM(0x%X)", addr);
if (RAMValue1 == nil) then
	Beep();
	printc(clRed, StringVal, " Communications Error 1 - got nil");
	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, " 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, " 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, " 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 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 + 2;
local StringVal = string.format("RAM(0x%X)", addr);
if (RAMValue1 == nil) then
	Beep();
	printc(clRed, StringVal, " Communications Error 1 - got nil");
	BumpCounter(COUNTER02);	-- Comm Error
	-- read again
	local RAMValue2 = RAM2(addr, Engine, TIU);	-- q1F84	-- Status Byte 2
	PacketCount = PacketCount + 2;
	if (RAMValue2 == nil) then
		Beep();
		printc(clRed, StringVal, " 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, " 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, " 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 + 2;
local StringVal = string.format("RAM(0x%X)", addr);
if (RAMValue1 == nil) then
	Beep();
	printc(clRed, StringVal, " Communications Error 1 - got nil");
	BumpCounter(COUNTER02);	-- Comm Error
	-- read again
	local RAMValue2 = RAM4(addr, Engine, TIU);	-- q1F84	-- Status Byte 2
	PacketCount = PacketCount + 2;
	if (RAMValue2 == nil) then
		Beep();
		printc(clRed, StringVal, " 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, " 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, " 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() at least once in this function if it returns true

	BumpCounter(COUNTER03);
	--Sleep(0.1);	
	local myelapsedtime = GetCounter(ELAPSEDTIME);	-- elapsed time in seconds
	local modtime = myelapsedtime % 10;

	if ((Debug() >= DebugThreshold - 1) and (modtime == 0) and (oldElapsedTime ~= myelapsedtime)) then 
	--if ((Debug() >= DebugThreshold - 1) and ((modtime == 0) or (modtime == 1))) then 
		print(string.format("loop %d seconds elapsed time. WiFi timeouts = %d", myelapsedtime, GetDebugCounter(1)));
		oldElapsedTime = myelapsedtime;
		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, Check008i, Engine, MyTIUNo) == false) then	-- q3F84	-- Scaling Factor
		return Stop("Stopping on Error"); end;
	
	if (RAMFetch(0x024, Check024, Engine, MyTIUNo) == false) then	-- q3FB3	-- Scaling Factor
		return Stop("Stopping on Error"); end;

	Yield();
	---[[
	--print("Whistle/Horn Off");	
	if (Whistle(OFF, 0, Engine, MyTIUNo) == nil) then	-- bFFFD
		Beep();
		BumpCounter(COUNTER02);
		return Stop("Whistle Off command failed");
		end;
	PacketCount = PacketCount + 1;
--]]	
	if (RAMFetch(0x067, Check067, Engine, MyTIUNo) == false) then	-- q3F84	-- Direction
		return Stop("Stopping on Error"); end;

	Yield();
	if (RAMFetch(0x009, Check009, Engine, MyTIUNo) == false) then	-- q3F84	-- Scaling Factor
		return Stop("Stopping on Error"); 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
--[[---------------------------------------------------------------------------------------]]
