This is a read-only snapshot of the ComputerCraft forums, taken in April 2020.
xBlizzDevious's profile picture

Monitor & control program with touch button overrides

Started by xBlizzDevious, 06 October 2014 - 01:35 PM
xBlizzDevious #1
Posted 06 October 2014 - 03:35 PM
Hello!

I'm writing a little program that I want to use to control some engines based on the level of the attached energy cell. I've got that code sorted just perfectly as I've done this many times before.

However, I'd like to have some buttons on my monitor that allow me to override the automatic function as and when needed.

I'm pretty sure I know all the code I need to do the buttons, however, I'm having some trouble figuring out how to get the os.pullEvent and yet have the program running in the background still.
- Will I have to use the parallel API or is there a simpler way for just having a couple of buttons controlling that application?


I basically want a toggle button in the bottom left of the monitor that will switch the engines on if they're off and off if they're on and then another button on the bottom right which will be a "reset to auto" button. The program runs just fine at the moment without the buttons, monitoring the Energy cell and applying a redstone signal once the cell reaches a certain threshold and then removing the redstone signal when reaching another threshold.

For reference: I'm using FTB monster V1.1.2 on MC 1.6.4.

Thanks for help in advance.
Edited on 06 October 2014 - 01:35 PM
Doyle3694 #2
Posted 06 October 2014 - 04:28 PM
While I am not experienced with parallel API and that might be the best thing for this, you could have an os.pullEvent() with a timer so that every 0.25 seconds it will check, then run your engine code, then check, the run your engine code and so on. Then again, you should be able to just use


-- Code

parallel.waitForAny(enginecode, buttoncode)

And then have it in the button code so that it returns if the button is pressed.
xBlizzDevious #3
Posted 06 October 2014 - 04:31 PM
While I am not experienced with parallel API and that might be the best thing for this, you could have an os.pullEvent() with a timer so that every 0.25 seconds it will check, then run your engine code, then check, the run your engine code and so on. Then again, you should be able to just use


-- Code

parallel.waitForAny(enginecode, buttoncode)

And then have it in the button code so that it returns if the button is pressed.

Hmm. That's what I thought. But checking for the os.pullEvent in my loop caused it to hang until I clicked. I need the rest of the program to constantly run.
Doyle3694 #4
Posted 06 October 2014 - 04:36 PM
Thats why you can use a timer with os.pullEvent: http://computercraft.info/wiki/Timer_%28event%29

But yes, the parallel API should be just fine for your needs
xBlizzDevious #5
Posted 06 October 2014 - 04:47 PM
Thats why you can use a timer with os.pullEvent: http://computercraft...mer_%28event%29

But yes, the parallel API should be just fine for your needs

Well I've never used the parallel API or used that sort of thing in any other language so it's a little daunting. I'll see if I can figure out how to do something with the timer thing. I didn't realise that I could just pull any event and then check what it was - in which case, I think I know what I need to do. Otherwise, I might have to look into the parallel API.

Thanks!
VSXGsonic #6
Posted 06 October 2014 - 09:46 PM
I used a os.pullevent for my multitasking

Check this out it might help you

http://www.computercraft.info/forums2/index.php?/topic/20584-how-do-i-run-rednetreceive-and-still-use-the-program/

Edit: mine is to work on a pocket pc to work stuff as I walk around but can easily put on a monitor. If you want I could convert mine to work how you want ?
Edited on 06 October 2014 - 07:49 PM
xBlizzDevious #7
Posted 06 October 2014 - 09:55 PM
I used a os.pullevent for my multitasking

Check this out it might help you

http://www.computerc...se-the-program/

Edit: mine is to work on a pocket pc to work stuff as I walk around but can easily put on a monitor. If you want I could convert mine to work how you want ?

Thanks. I've managed to get it working how I want now though - I think…

I'm sure there's a much more elegant way of writing that and there's probably a bug or three in there that I've not found yet, but from some quick testing, it works! And I think it's bad practice to run a function from one function that leads back to the original function. But as I said, it works!

Here's the code:

Spoiler

print("Running...")
hec = peripheral.wrap("back")
mon = peripheral.wrap("left")

rs.setOutput("front", false)
mon.setTextScale(1.5)

local curStore = 0
local maxStore = 0
local thirty = 0
local ninetyFive = 0
local engineStatus = "OFF"
local overrideText = "OFF"
local autoText = "OFF "

maxStore = hec.getMaxEnergyStored("")
thirty = maxStore*0.3
ninetyFive = maxStore*0.95

function createButtons()
	mon.setTextColor(colors.white)
	if overrideText == "ON " then
		mon.setBackgroundColor(colors.green)
	else
		mon.setBackgroundColor(colors.red)
	end
	mon.setCursorPos(1,5)
	mon.write("	 ")
	mon.setCursorPos(1,6)
	mon.write(" "..overrideText.." ")
	mon.setCursorPos(1,7)
	mon.write("	 ")
	
	mon.setBackgroundColor(colors.black)

	if autoText == "AUTO" then
		mon.setBackgroundColor(colors.green)
	else
		mon.setBackgroundColor(colors.red)
	end
	mon.setCursorPos(8,5)
	mon.write("	  ")
	mon.setCursorPos(8,6)
	mon.write(" "..autoText.." ")
	mon.setCursorPos(8,7)
	mon.write("	  ")
	
	mon.setBackgroundColor(colors.black)
end

function auto()
	overrideText = "OFF"
	autoText = "AUTO"
	
	curStore = hec.getEnergyStored("")
	
	if curStore <= thirty then
		rs.setOutput("front",true)
		engineStatus = "ON"
	elseif curStore > ninetyFive then
		rs.setOutput("front",false)
		engineStatus = "OFF"
	end
	
	mainLoop()
end

function override()
	overrideText = "ON "
	autoText = "OFF "
	
	rs.setOutput("front", true)
	
	mainLoop()
end

function mainLoop()
	while true do
		os.startTimer(1)
		mon.setTextColor(colors.white)
		
		
		
		mon.setCursorPos(1,1)
		mon.clearLine()
		mon.setTextColor(colors.white)
		mon.write("Current Energy = ")
		mon.setTextColor(colors.red)
		mon.write(tostring(curStore))
		
		mon.setCursorPos(1,2)
		mon.setTextColor(colors.white)
		mon.write("Maximum Energy = ")
		mon.setTextColor(colors.blue)
		mon.write(tostring(maxStore))
		
		mon.setCursorPos(1,3)
		mon.clearLine()
		mon.setTextColor(colors.white)
		mon.write("Engines are: ")
		if rs.getOutput("front") == true then
			mon.setTextColor(colors.green)
			engineStatus = "ON"
		else
			mon.setTextColor(colors.red)
			engineStatus = "OFF"
		end
		mon.write(engineStatus)
		
		createButtons()

		local event = os.pullEvent()
		if event == "monitor_touch" then
			local event, side, X, Y = os.pullEvent("monitor_touch")
			if X >= 1 and X <= 6 and Y >= 5 and Y <= 8 then
				override()
			elseif X >= 8 and X <= 14 and Y >= 5 and Y <= 8 then
				auto()
			end
		end
		
		sleep(1)
		
		if autoText == "AUTO" then
			auto()
		end
	end
end

auto()



EDIT: found a bug…
I'm getting:


startup:21: vm error:
java.lang.ArrayIndexOutOfBoundsException

Line 21 is:

function createButtons()
	mon.setTextColor(colors.white) --THIS ONE
	if overrideText == "ON " then

Any ideas as to why that's causing what I assume is an external Java error rather than a Lua error?
Edited on 06 October 2014 - 08:07 PM
VSXGsonic #8
Posted 06 October 2014 - 10:07 PM
Well aslong as your happy with it and it works . Mine is going to run everything in my place from one screen so the funception was the easyer way around ^-^.
xBlizzDevious #9
Posted 06 October 2014 - 10:07 PM
Well aslong as your happy with it and it works . Mine is going to run everything in my place from one screen so the funception was the easyer way around ^-^.

Please see edits above.
VSXGsonic #10
Posted 07 October 2014 - 02:07 AM
I used a os.pullevent for my multitasking

Check this out it might help you

http://www.computerc...se-the-program/

Edit: mine is to work on a pocket pc to work stuff as I walk around but can easily put on a monitor. If you want I could convert mine to work how you want ?

Thanks. I've managed to get it working how I want now though - I think…

I'm sure there's a much more elegant way of writing that and there's probably a bug or three in there that I've not found yet, but from some quick testing, it works! And I think it's bad practice to run a function from one function that leads back to the original function. But as I said, it works!

Here's the code:

Spoiler

print("Running...")
hec = peripheral.wrap("back")
mon = peripheral.wrap("left")

rs.setOutput("front", false)
mon.setTextScale(1.5)

local curStore = 0
local maxStore = 0
local thirty = 0
local ninetyFive = 0
local engineStatus = "OFF"
local overrideText = "OFF"
local autoText = "OFF "

maxStore = hec.getMaxEnergyStored("")
thirty = maxStore*0.3
ninetyFive = maxStore*0.95

function createButtons()
	mon.setTextColor(colors.white)
	if overrideText == "ON " then
		mon.setBackgroundColor(colors.green)
	else
		mon.setBackgroundColor(colors.red)
	end
	mon.setCursorPos(1,5)
	mon.write("	 ")
	mon.setCursorPos(1,6)
	mon.write(" "..overrideText.." ")
	mon.setCursorPos(1,7)
	mon.write("	 ")
	
	mon.setBackgroundColor(colors.black)

	if autoText == "AUTO" then
		mon.setBackgroundColor(colors.green)
	else
		mon.setBackgroundColor(colors.red)
	end
	mon.setCursorPos(8,5)
	mon.write("	  ")
	mon.setCursorPos(8,6)
	mon.write(" "..autoText.." ")
	mon.setCursorPos(8,7)
	mon.write("	  ")
	
	mon.setBackgroundColor(colors.black)
end

function auto()
	overrideText = "OFF"
	autoText = "AUTO"
	
	curStore = hec.getEnergyStored("")
	
	if curStore <= thirty then
		rs.setOutput("front",true)
		engineStatus = "ON"
	elseif curStore > ninetyFive then
		rs.setOutput("front",false)
		engineStatus = "OFF"
	end
	
	mainLoop()
end

function override()
	overrideText = "ON "
	autoText = "OFF "
	
	rs.setOutput("front", true)
	
	mainLoop()
end

function mainLoop()
	while true do
		os.startTimer(1)
		mon.setTextColor(colors.white)
		
		
		
		mon.setCursorPos(1,1)
		mon.clearLine()
		mon.setTextColor(colors.white)
		mon.write("Current Energy = ")
		mon.setTextColor(colors.red)
		mon.write(tostring(curStore))
		
		mon.setCursorPos(1,2)
		mon.setTextColor(colors.white)
		mon.write("Maximum Energy = ")
		mon.setTextColor(colors.blue)
		mon.write(tostring(maxStore))
		
		mon.setCursorPos(1,3)
		mon.clearLine()
		mon.setTextColor(colors.white)
		mon.write("Engines are: ")
		if rs.getOutput("front") == true then
			mon.setTextColor(colors.green)
			engineStatus = "ON"
		else
			mon.setTextColor(colors.red)
			engineStatus = "OFF"
		end
		mon.write(engineStatus)
		
		createButtons()

		local event = os.pullEvent()
		if event == "monitor_touch" then
			local event, side, X, Y = os.pullEvent("monitor_touch")
			if X >= 1 and X <= 6 and Y >= 5 and Y <= 8 then
				override()
			elseif X >= 8 and X <= 14 and Y >= 5 and Y <= 8 then
				auto()
			end
		end
		
		sleep(1)
		
		if autoText == "AUTO" then
			auto()
		end
	end
end

auto()



EDIT: found a bug…
I'm getting:


startup:21: vm error:
java.lang.ArrayIndexOutOfBoundsException

Line 21 is:

function createButtons()
	mon.setTextColor(colors.white) --THIS ONE
	if overrideText == "ON " then

Any ideas as to why that's causing what I assume is an external Java error rather than a Lua error?

ArrayIndexOutOfBoundsException indicates that an array has been accessed with an illegal index. The index is either negative or greater than or equal to the size of the array. but i have tested it and i have not been given this error. when did it occur?
EDIT: nevermind got the error i will play with it and your energy reading stops updating and that give error
Edited on 07 October 2014 - 12:34 AM
Bomb Bloke #11
Posted 07 October 2014 - 02:39 AM
It's happening because of the recursive function calls.

Let's say you've got a script that looks like this:

function moo()
  print("Moo")
  sleep(0)
  moo2()
end

function moo2()
  print("Moo2")
  sleep(0)
  moo()
end

moo()

When the moo() function calls moo2(), it doesn't end. It stays loaded in memory, waiting for the moo2() function to end so that it can carry on from where it left off.

But moo2() doesn't end. Instead, it calls moo() again, and therefore also stays loaded in memory, waiting for that fresh new instance of moo() to complete so it can carry on.

As the script continues, you get more and more copies of these functions pouring into memory, until eventually the space reserved for them - which we'll call the "function stack" - runs out, and the whole thing crashes (a "stack overflow").

Calling any function adds to the stack, and that includes your monitor's "setTextColor" function. Usually this doesn't matter, because when those functions complete they're removed from the stack (which can hold something like 256 functions at a time). But your "auto()" and "override()" functions never finish - they just keep on starting up fresh instances of mainLoop(), which in turn starts up fresh new instances of auto()/override().

You could get around this with a process called "tail calling", but personally I think you'd be better off re-organising the script from this sort of structure:

function auto()
  -- do stuff
  mainLoop()
end

function override()
  -- do stuff
  mainLoop()
end

function mainLoop()
  while true do
    if stuff then
      auto()
    else
      override()
    end
  end
end

To this sort of structure:

function auto()
  -- do stuff
end

function override()
  -- do stuff
end

function doMenu()
  if stuff then
    return "auto"
  else
    return "override"
  end
end

while true do
  local menuResult = doMenu()
  if menuResult == "auto" then auto() elseif menuResult == "override" then override() end
end
VSXGsonic #12
Posted 07 October 2014 - 04:56 AM
its like 5 am here and i made this for you to try it will update and i haven't changed how it looks much .(i dont think ) :wacko:/> and it shouldn't give you the error

what you think ?

Spoilerterm.clear()
term.setCursorPos(1,1)
print("Running…")
mon.setTextScale(1.5)
hec = peripheral.wrap("back")
mon = peripheral.wrap("left")
mon.setTextScale(0.5)
mon.setTextColor(colors.white)
local curStore = 0
local maxStore = 0
local thirty = 0
local ninetyFive = 0
local auto = true
mon.setTextScale(1.5)
maxStore = hec.getMaxEnergyStored("")
thirty = maxStore*0.3
ninetyFive = maxStore*0.95
function loadScreen() – prints standard info on screen
mon.setCursorPos(1,1)
mon.setTextColor(colors.white)
mon.write("Current Energy = ")
mon.setCursorPos(1,2)
mon.write("Maximum Energy = ")
mon.setTextColor(colors.blue)
mon.write(tostring(maxStore))
mon.setTextColor(colors.white)
mon.setCursorPos(1,3)
mon.write("Engines are: ")
mon.setCursorPos(1,5)
mon.write("Override Auto Run")
end
function check()
curStore = hec.getEnergyStored("")
mon.setCursorPos(18,1)
mon.setTextColor(colors.red)
mon.write(tostring(curStore))
mon.setTextColor(colors.white)
end
function autoOn()
mon.setTextColor(colors.white)
mon.setBackgroundColor(colors.red)
mon.setCursorPos(2,6)
mon.write(" ")
mon.setCursorPos(2,7)
mon.write(" OFF ")
mon.setCursorPos(2,8)
mon.write(" ")
mon.setBackgroundColor(colors.green)
mon.setCursorPos(14,6)
mon.write(" ")
mon.setCursorPos(14,7)
mon.write(" ON ")
mon.setCursorPos(14,8)
mon.write(" ")
mon.setBackgroundColor(colors.black)
end
function autoOff()
mon.setTextColor(colors.white)
mon.setBackgroundColor(colors.green)
mon.setCursorPos(2,6)
mon.write(" ")
mon.setCursorPos(2,7)
mon.write(" ON ")
mon.setCursorPos(2,8)
mon.write(" ")
mon.setBackgroundColor(colors.red)
mon.setCursorPos(14,6)
mon.write(" ")
mon.setCursorPos(14,7)
mon.write(" OFF ")
mon.setCursorPos(14,8)
mon.write(" ")
mon.setBackgroundColor(colors.black)
end
function override()
rs.setOutput("front", true)
end
function auto_run()
if curStore <= thirty then
rs.setOutput("front",true)
mon.setTextColor(colors.green)
mon.setCursorPos(14,3)
mon.write("ON ")
mon.setTextColor(colors.white)
elseif curStore > ninetyFive then
rs.setOutput("front",false)
mon.setTextColor(colors.red)
mon.setCursorPos(14,3)
mon.write("OFF")
mon.setTextColor(colors.white)
end
end
–==loads this before anything else
loadScreen()
—===========
while true do
check()
—== checks which its doing
if auto == true then
autoOn()
auto_run()
elseif auto == false then
autoOff()
override()
end
–===========
os.startTimer(0.6)
local event, side, X, Y = os.pullEvent()
if event == "monitor_touch" then
if X >= 1 and X <= 6 and Y >= 5 and Y <= 8 then
auto = false
elseif X >= 13 and X <= 20 and Y >= 5 and Y <= 9 then
auto = true
end
end
end

hope it works for you :unsure:/>
Edited on 07 October 2014 - 02:59 AM
xBlizzDevious #13
Posted 07 October 2014 - 08:20 AM
It's happening because of the recursive function calls.

Let's say you've got a script that looks like this:

function moo()
  print("Moo")
  sleep(0)
  moo2()
end

function moo2()
  print("Moo2")
  sleep(0)
  moo()
end

moo()

When the moo() function calls moo2(), it doesn't end. It stays loaded in memory, waiting for the moo2() function to end so that it can carry on from where it left off.

But moo2() doesn't end. Instead, it calls moo() again, and therefore also stays loaded in memory, waiting for that fresh new instance of moo() to complete so it can carry on.

As the script continues, you get more and more copies of these functions pouring into memory, until eventually the space reserved for them - which we'll call the "function stack" - runs out, and the whole thing crashes (a "stack overflow").

Calling any function adds to the stack, and that includes your monitor's "setTextColor" function. Usually this doesn't matter, because when those functions complete they're removed from the stack (which can hold something like 256 functions at a time). But your "auto()" and "override()" functions never finish - they just keep on starting up fresh instances of mainLoop(), which in turn starts up fresh new instances of auto()/override().

You could get around this with a process called "tail calling", but personally I think you'd be better off re-organising the script from this sort of structure:

function auto()
  -- do stuff
  mainLoop()
end

function override()
  -- do stuff
  mainLoop()
end

function mainLoop()
  while true do
	if stuff then
	  auto()
	else
	  override()
	end
  end
end

To this sort of structure:

function auto()
  -- do stuff
end

function override()
  -- do stuff
end

function doMenu()
  if stuff then
	return "auto"
  else
	return "override"
  end
end

while true do
  local menuResult = doMenu()
  if menuResult == "auto" then auto() elseif menuResult == "override" then override() end
end

I'll have a look at this later on, just off to work shortly. Thanks for the info, and that also explains why it's bad to call a function that leads back to the original one - I've witnessed the problems now! Haha

its like 5 am here and i made this for you to try it will update and i haven't changed how it looks much .(i dont think ) :wacko:/> and it shouldn't give you the error

what you think ?

-snip-
hope it works for you :unsure:/>

Haha, thanks a lot! I'll give it a try and see if it works. (I've got to neaten up that code first, though).
VSXGsonic #14
Posted 07 October 2014 - 08:44 AM
All I changed was one part of the screen it now shows
Override auto run
ON OFF

It was neat ish before I pasted but it did its own thing haha. But u can just save it in game as test and try it I tryed to make it bug and lag free as possible well as much as lots of early coffee would allow I used notepad to write it didnt check but should work aslong as u havent moved the objects placement ^-^
xBlizzDevious #15
Posted 07 October 2014 - 04:21 PM
All I changed was one part of the screen it now shows
Override auto run
ON OFF

It was neat ish before I pasted but it did its own thing haha. But u can just save it in game as test and try it I tryed to make it bug and lag free as possible well as much as lots of early coffee would allow I used notepad to write it didnt check but should work aslong as u havent moved the objects placement ^-^

Thanks for the code, but two bugs I have found:

One: "Engines are: " on/off doesn't show. I've checked the code through and I see where it happens, but it doesn't seem to work - not sure why.

Two: Every time you click, something happens with the OS timer and it runs faster. So when you click enough times, eventually, it bugs out. I kept having this same issue too, but I don't know how to fix it - or what's causing it - though I guess it's to do with the timer duplicating or restarting or something.
VSXGsonic #16
Posted 07 October 2014 - 08:00 PM

One: "Engines are: " on/off doesn't show. I've checked the code through and I see where it happens, but it doesn't seem to work - not sure why.

Two: Every time you click, something happens with the OS timer and it runs faster. So when you click enough times, eventually, it bugs out. I kept having this same issue too, but I don't know how to fix it - or what's causing it - though I guess it's to do with the timer duplicating or restarting or something.


engines i see what you mean with the engine oops . i carnt make it do it i have been sat here for 5 mins clicking buttons and it still works. how did you make it bug ? silly question but is all up to date? i just updated to all new open peripherals and it works or it just doesn't wanna do it for me