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

[Question] Inputs not being received intermittently in complex programs.

Started by TheGeek, 20 November 2012 - 10:59 AM
TheGeek #1
Posted 20 November 2012 - 11:59 AM
I am currently working on a Command & Control computer program that facilitates the setting of states of items around our base area, such as piston drawbridges and wireless melee turtles. This computer has two buttons and a monitor, and displays the states of various items.

So far I have able to overcome various bugs and such, but this one is really proving to be troublesome.

I appear to be having issues with my program not sensing inputs; IE i press the button and the program does nothing, 1 out of 4 times. The best solution would be to set an event to halt the program until that button is pressed, but its not practical as this program also is listening for RedNet messages, and responding with the proper state.

I am having a similar issue with RedNet computers not receiving messages, as I set a short timeout period for rednet.receive() so the program can deal with other inputs, such as a tripwire triggering a melee turtle to attack. The problem is solved on the piston drawbridge control computer, as it only receives during its main loop, so an infinite timeout period is practical.

Here is my code: (please be forgiving, I'm a LUA noob :(/>/>)
Command & Control Computer:
Spoiler

--Base Command and Control Script v0.5
--************************************************************************************
--Variables for setting
--************************************************************************************
local modemside = "back" --side with the modem on it
local baseID = "b01" --Base ID
local defaultstate = "auto" --default turtledrone status (passive/aggressive/auto)
local defaultdrawbridge = "down" --default drawbridge status (up/down)
--************************************************************************************
--Initialize Variables
local turtdronestate = defaultstate
local drawbridgestate = defaultdrawbridge
local error = "none"
--Get Monitor Perhperal
mon = peripheral.wrap("top") --aquires monitor object
mX, mY = mon.getSize() --gets size of monitor
--************************************************************************************
--Define functions
--************************************************************************************
--split RedNet messages into base, header, and content
function RedNetSplit(message)
if message==nil then  --string.sub doesn't like nil; cant conicate nil
  local base = "000"
  local header = "herpderp"
  local content = "herpderp"
  return base,header,content
else
  print(id.." "..message)
  local base = string.sub(message, 1, 3)
  local header = string.sub(message, 4, 11)
  local content = string.sub(message, 12)
  return base,header,content
end
end
--update monitor with information
function UpdateMonitor(turtledrone, drawbridge)
  mon.clear()
  mon.setCursorPos(1,1)
  mon.write("TurtleDrone Status: "..turtledrone)
  mon.setCursorPos(1,2)
  mon.write("Drawbridge Status: "..drawbridge)
--Labels for buttons
mon.setCursorPos(1, mY - 1) --set cursor to one line above bottom
mon.write("TurtleDrones")
mon.setCursorPos(mX - 11, mY - 1) --set cursor to one line above bottom and move it over the number of characters
mon.write("Drawbridges")
--update left button with next state
  mon.setCursorPos(1,mY)
  if turtledrone == "auto" then
   mon.write("Left:Set Passive")
  elseif turtledrone == "passive" then
   mon.write("Left:Set Aggressive")  
  elseif turtledrone == "aggressive" then
   mon.write("Left:Set Auto")
  end
  --update right button with next state
  mon.setCursorPos(mX - 8, mY) --put the cursor in the bottom right, and move it over the number of characters in the string
  if drawbridge == "up" then
   mon.write("DN :Right")
  elseif drawbridge == "down" then
   mon.write("UP :Right")  
  end
  print("Monitor Updated") --debug
end
--************************************************************************************
--end functions
--************************************************************************************
--************************************************************************************
--boot sequence
--************************************************************************************
term.clear() --clear terminal
term.setCursorPos(1,1)
mon.clear()  --clear monitor
mon.setCursorPos(1,1)
print("Monitor x:"..mX.." Monitor y: "..mY)
mon.write("Base Control Computer v0.5")
mon.setCursorPos(1,2)
mon.write("Booting... ")
sleep(0.5)
mon.write("Please Wait...")
sleep(2)
mon.setCursorPos(1,3)
mon.write("Enabling RedNet...")
if peripheral.getType(modemside)=="modem" then --if a modem is on the side indicated
rednet.open(modemside) --then turn it on!
sleep(0.5)
mon.write("OK")
else --uh oh, no modem
sleep(0.5)
mon.write("ERROR!")
mon.setCursorPos(1,4)
mon.write("ERROR: Modem not Found on side "..modemside)
quit() --this doesn't work as its supposed to, but it does error out the console and stops the program so 'meh'
end
sleep(2)
mon.setCursorPos(1,4)
mon.write("Finding Repeaters...")
--Ping script goes here
mon.write("Disabled")
sleep(2)
--Set Default State for TurtleDrones
mon.setCursorPos(1,5)
mon.write("Turtle default set to "..defaultstate)
sleep(0.5)
mon.setCursorPos(1,6)
mon.write("Setting TurtleDrones to "..defaultstate.."...")
turtdronestate = defaultstate
rednet.broadcast(baseID.."turtdron"..turtdronestate)
print("sent "..baseID.."turtdron"..turtdronestate)
sleep(0.5)
mon.setCursorPos(1,7)
mon.write("Done")
sleep(2)
--Set default state for drawbridges
mon.setCursorPos(1,8)
mon.write("Drawbridges set to default "..defaultdrawbridge)
sleep(0.5)
mon.setCursorPos(1,9)
mon.write("Setting Drawbridges to "..defaultdrawbridge.."...")
drawbridgestate = defaultdrawbridge
rednet.broadcast(baseID.."drawbrid"..drawbridgestate)
print("sent "..baseID.."drawbrid"..drawbridgestate)
sleep(0.5)
mon.setCursorPos(1,10)
mon.write("Done")
sleep(2)
--************************************************************************************
--end boot sequence
--************************************************************************************
mon.clear()  --clear monitor
mon.setCursorPos(1,1)
UpdateMonitor(turtdronestate, drawbridgestate)  --update the monitor before looping
--************************************************************************************
--Main Loop
--************************************************************************************
while true do
sleep(0)
--Listen for messages
id,message = rednet.receive(1)
base,header,content = RedNetSplit(message)

if base==baseID and header=="commctrl" then
--Respond to Pings
  if content=="PING" then
   rednet.send(id,baseID..header.."PONG")
   print("sent PONG")
--Respond to TurtleDrones requesting state
  elseif content=="requeststatus" then
   rednet.broadcast(baseID.."turtdron"..turtdronestate)
   print("TurtleDrone requested status")
  --respond to drawbridge control requesting state
  elseif content=="drawbridgestatus" then
   rednet.broadcast(baseID.."drawbrid"..drawbridgestate)
   print("Drawbridge Control Requested status")
  end
end
--Left button toggles TurtleDrone state
if redstone.getInput("left") and not redstone.getInput("right") then
  if turtdronestate == "auto" then
   turtdronestate = "passive"
  elseif turtdronestate == "passive" then
   turtdronestate = "aggressive"
  elseif turtdronestate == "aggressive" then
   turtdronestate = "auto"
  else
   print("invalid state reached! Defaulting to auto!")
   turtdronestate = "auto"
  end
  rednet.broadcast(baseID.."turtdron"..turtdronestate)
  print("sent "..baseID.."turtdron"..turtdronestate)
  UpdateMonitor(turtdronestate, drawbridgestate)
  sleep(2)
end
--Right button toggles Drawbridge state
if redstone.getInput("right") and not redstone.getInput("left") then
  if drawbridgestate=="up" then
   drawbridgestate = "down"
  elseif drawbridgestate=="down" then
   drawbridgestate = "up"
  end
  rednet.broadcast(baseID.."drawbrid"..drawbridgestate)
  print("sent "..baseID.."drawbrid"..drawbridgestate)
  UpdateMonitor(turtdronestate, drawbridgestate)
  sleep(2)
end
end
--************************************************************************************
--end main loop
--************************************************************************************
--print error that caused loop to end
mon.clear()  --clear monitor
mon.setCursorPos(1,1)
mon.write(error)
--turn off modem before you go!
rednet.close(modemside)

Drawbridge Control Computer:
Spoiler

--Drawbridge control comp v0.3
--Configureable variables
local baseID = "b01"
local defaultmode = "up" --default state up/down
local modemside = "right" --side modem is on
--Init variables
local modeset = "false"
--define functions
function RedNetSplit(message)
if message==nil then  --string.sub doesn't like nil; cant conecate nil
  local base = "000"
  local header = "herpderp"
  local content = "herpderp"
  return base,header,content
else
  print(id.." "..message)
  local base = string.sub(message, 1, 3)
  local header = string.sub(message, 4, 11)
  local content = string.sub(message, 12)
  return base,header,content
end
end
--Clear Terminal
term.clear()
term.setCursorPos(1,1)
--Bootup
print("Drawbridge Computer v0.3 Starting Up")
write("Activating RedNet modem...")
if peripheral.getType(modemside)=="modem" then --if a modem is on the side indicated
rednet.open(modemside) --then turn it on!
sleep(0.5)
write("OK")
else --uh oh, no modem
sleep(0.5)
print("ERROR: Modem not found on side "..modemside)
quit() --this doesn't work as its supposed to, but it does error out the console and stops the program so 'meh'
end
print("Contacting C&C Computer...")
rednet.broadcast(baseID.."commctrlPING")
id,message,distance = rednet.receive(5)
if message == baseID.."commctrlPONG" then
print("Server reached!")
print(id.." "..message.." dist:"..distance)
serveractive = true
elseif message == nil then
print("Can't reach server! Defaulting to "..defaultmode)
mode = defaultmode
modeset = true
else
print("lines are busy! Defaulting to "..defaultmode)
print(id.." "..message.." dist:"..distance)
mode = defaultmode
modeset = true
end
--if server was reached then get mode
if serveractive == true then
sleep(2)
rednet.broadcast(baseID.."commctrldrawbridgestatus") --get drawbridge status
id,message = rednet.receive(5)
base,header,content = RedNetSplit(message)
if base==baseID and header=="drawbrid" and content == "up" or content == "down" then
  print("Recieved mode "..content)
  mode = content
  modeset = true
else
  print("Server sent invalid mode! Defaulting to "..defaultmode)
  mode = defaultmode
  modeset = true
end
end
--main loop
while true do
sleep(0)
--get any incoming messages
id,message = rednet.receive() --drawbridges really don't need to broadcast anything or do anything else, so just have it wait until a message is recieved (debug not getting RedNet messages)
base,header,content = RedNetSplit(message)
--if addressed to drawbridges set content to state
if base==baseID and header=="drawbrid" and content == "up" or content == "down" then
  print("Server sent "..content)
  mode = content
  modeset = true
end
--set up or down if modeset = true
if mode == "up" and modeset == true then
  redstone.setOutput("top", true)--activate piston above
  redstone.setOutput("left", true)--activate repeaters around
  redstone.setOutput("back", true)--activate repeaters
  redstone.setOutput("right", true)--ditto
  modeset = false --only set up or down once
elseif mode == "down" and modeset == true then
  redstone.setOutput("top", false)--activate piston above
  redstone.setOutput("left", false)--activate repeaters around
  redstone.setOutput("back", false)--activate repeaters
  redstone.setOutput("right", false)--ditto
  modeset = false
end
end

And for kicks, the Wireless Melee Turtle (or as I have nicknamed them TurtleDrones) Control Program:
Spoiler

--TurtleDrone control Program V0.5
--Initialize Variables
local serveractive = false
local nTime = os.time()
--Configureable variables
local baseID = "b01"
local defaultmode = "auto" --auto/passive/aggressive
--define functions
function RedNetSplit(message) --returns three strings: base, header, and content only if message was not nil
if message==nil then  --string.sub doesn't like nil; cant conecate nil
  local base = "000"
  local header = "herpderp"
  local content = "herpderp"
  return base,header,content
else
  print(id.." "..message)
  local base = string.sub(message, 1, 3)
  local header = string.sub(message, 4, 11)
  local content = string.sub(message, 12)
  return base,header,content
end
end
--Clear Terminal
term.clear()
term.setCursorPos(1,1)
--Bootup
print("TurtleDrone 0.5 Starting Up")
print("Activating RedNet modem...")
if peripheral.getType("right")=="modem" then --if a modem is on the right (wireless turtles are always on the right)
rednet.open("right") --then turn it on!
sleep(0.5)
print("OK")
else --uh oh, this is not a wireless turtle!
sleep(0.5)
print("ERROR! This is not a wireless Turtle!")
quit() --this doesn't work as its supposed to, but it does error out the console and stops the program so 'meh'
end
print("Contacting C&C Computer...")
rednet.broadcast(baseID.."commctrlPING")
id,message,distance = rednet.receive(2)
if message == baseID.."commctrlPONG" then
print("Server reached!")
print(id.." "..message.." dist:"..distance)
serveractive = true
elseif message == nil then
print("Can't reach server! Defaulting to "..defaultmode)
mode = defaultmode
else
print("lines are busy! Defaulting to "..defaultmode)
mode = defaultmode
end
--if server was reached then get mode
if serveractive == true then
sleep(2)
rednet.broadcast(baseID.."commctrlrequeststatus")
id,message = rednet.receive(5)
base,header,content = RedNetSplit(message)
if base==baseID and header=="turtdrone" and content=="aggressive"or content=="passive" or content=="auto" then
  print("Recieved mode "..content)
  mode = content
else
  print("Server sent invalid mode! Defaulting to "..defaultmode)
  mode = defaultmode
end
end
--main loop
while true do
sleep(0)
--check what time it is
nTime = os.time()
--get any incoming messages
id,message = rednet.receive(1)
base,header,content = RedNetSplit(message)
--if addressed to turtledrones set content to state
if base==baseID and header=="turtdron" then
  if content=="aggressive" or content=="passive" or content=="auto" then
   print("Server sent "..content)
   mode = content
  else
   print("ERROR! Invalid State Recieved! Defaulting to "..defaultmode)
   mode = defaultmode
  end
end

--Set top redstone lamp to true if aggressive or auto and nighttime
if mode == "aggressive" then
  redstone.setOutput("top", true)
elseif mode == "auto" then
  if nTime < 5 or nTime > 20 then
   redstone.setOutput("top", true)
  elseif nTime > 5 and nTime < 20 then
   redstone.setOutput("top", false)
  end
elseif mode == "passive" then
  redstone.setOutput("top", false)  
else
  redstone.setOutput("top", false)
end

--Attack if tripwire true and mode aggressive
if mode=="aggressive" and redstone.getInput("bottom") then
  turtle.attack()
  print("Die foul Being!")
  sleep(0.5)
end
--Open door if tripwire true and mode passive
if mode=="passive" and redstone.getInput("bottom") then
  turtle.turnLeft()
  redstone.setOutput("front", true)
  redstone.setOutput("back", true)
  print("Access Granted!")
  sleep(5)
  redstone.setOutput("front", false)
  redstone.setOutput("back", false)
  turtle.turnRight()
end
--if auto mode, attack if night, open door if day
if mode=="auto" and redstone.getInput("bottom") then
  if nTime > 5 and nTime < 20 then
   turtle.turnLeft()
   redstone.setOutput("front", true)
   redstone.setOutput("back", true)
   print("It is Day! Access Granted!")
   sleep(5)
   redstone.setOutput("front", false)
   redstone.setOutput("back", false)
   turtle.turnRight()
  elseif nTime < 5 or nTime > 20 then
   turtle.attack()
   print("It is Night! Die foul Being!")
   sleep(0.5)
  end
end
end

In my efforts to come up with a solution, I theorized that if I split off the button inputs into another computer that could have a dedicated button input, sending rednet signals to the C&amp;C comp when buttons were pressed, enabling the C&amp;C comp to work with infinite timeouts. This really wouldn't work with the Turtle's though…

Thanks in advance for the help!

EDIT: Its not really a LUA problem, its more of a question. Maybe now I might get some help.
Lyqyd #2
Posted 20 November 2012 - 02:10 PM
You need to be handling incoming events if you want to listen for more that one thing. Something like this:


while true do
    event, p1, p2, p3 = os.pullEvent()
    if event == "redstone" then
        --check your redstone inputs to see what changed and respond accordingly
    elseif event == "rednet_message" then
        --handle the rednet message
    end
end
TheGeek #3
Posted 20 November 2012 - 03:42 PM
You need to be handling incoming events if you want to listen for more that one thing. Something like this:


while true do
	event, p1, p2, p3 = os.pullEvent()
	if event == "redstone" then
		--check your redstone inputs to see what changed and respond accordingly
	elseif event == "rednet_message" then
		--handle the rednet message
	end
end

Hmmm. Looking at the info for os.pullEvent(), it says it will stop the program from running until an event occurs, but since it can be any event and not just RedNet OR Redstone, I think I can make this work. I guess having the time displayed will be a function of alarm or somesuch, but adding a time display to the monitor is low on the priority list.

Just to make sure I understand how to use this correctly the code would be:

while true do
	event, p1, p2, p3 = os.pullEvent()
	if event == "redstone" then
        buttonpressed = redstone.getInput(side)
       if buttonpressed == true then
		--check your redstone inputs to see what changed and respond accordingly
       end
	elseif event == "rednet_message" then
       id,message = rednet.receive()
		--handle the rednet message
	end
end

Do i have that right?
Lyqyd #4
Posted 20 November 2012 - 04:50 PM
Not quite. When handling a rednet_message event, the parameters that rednet.receive() would have returned are used as the event parameters. That is to say, in the above example, p1 and p2 are the ID of the sender of the rednet message and the message itself, when the event is a rednet_message event.
TheGeek #5
Posted 20 November 2012 - 05:47 PM
Not quite. When handling a rednet_message event, the parameters that rednet.receive() would have returned are used as the event parameters. That is to say, in the above example, p1 and p2 are the ID of the sender of the rednet message and the message itself, when the event is a rednet_message event.

Ah i figured that out well after I posted that.

Thanks for the help!