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

While True Do Loop Issue

Started by Sorcelator, 24 September 2013 - 11:25 AM
Sorcelator #1
Posted 24 September 2013 - 01:25 PM
I've been trying to build a program that will automate some aspects of my crafting setup. I'm using Open.Peripheral to detect items in a chest and move them to my Macerator and then my Electric Furnace. However, I'm trying to use While True Do to loop these commands so I don't have to run it each time.

here is my code so far:

chest.condense() --Organize the Chest
local slotnum = chest.getSizeInventory() --Use the Max Invantory Size to calculate the Slot number and Loop amount
local macedone = mace.getStackInSlot(1)
local efurndone = efurn.getStackInSlot(1)
while true do
os.startTimer(5)
event = os.pullEvent()
if event == "timer" then
  --push completed items out of the mace and efurn--
  mace.push("north", 1, 64)
  efurn.push("up", 1, 64)
  --start looking for ore--
  for i=1,slotnum do -- LOOP!
  local tableInfo = chest.getStackInSlot(i)
  if tableInfo ~= nil then
   for key, value in pairs(tableInfo) do
    if string.find(value, "Ore") then -- Find Ore
	 print("Ore Found! Moving to Macerator!")
	 chest.push("north", i, 64)
    else
    end
   end
  else
  end
end
else
end
end

There must be something braking in the code that detects Ore that causes the loop to end. I'm just not sure what it is.
TheOddByte #2
Posted 24 September 2013 - 02:36 PM
Please post your full error.. Does it give you an error code?
Sorcelator #3
Posted 24 September 2013 - 02:39 PM
it gives no error code. It just stops.
BigTwisty #4
Posted 24 September 2013 - 02:40 PM
Several issues:

1. I don't see where the tables named "chest", "mace" or "efurn" are created, so either you aren't showing all your code or these are missing.
2. If you don't tell us what error you are getting, we can't tell you where it is coming from.
3. You have a bunch of useless "else" statements cluttering up your code. Remove them.
4. Proper indentation helps us read it. See corrected indentation here:

chest.condense() --Organize the Chest
local slotnum = chest.getSizeInventory() --Use the Max Invantory Size to calculate the Slot number and Loop amount
local macedone = mace.getStackInSlot(1)
local efurndone = efurn.getStackInSlot(1)
while true do
  os.startTimer(5)
  event = os.pullEvent()
  if event == "timer" then
	--push completed items out of the mace and efurn--
	mace.push("north", 1, 64)
	efurn.push("up", 1, 64)
	--start looking for ore--
	for i=1,slotnum do -- LOOP!
	  local tableInfo = chest.getStackInSlot(i)
	  if tableInfo ~= nil then
		for key, value in pairs(tableInfo) do
		  if string.find(value, "Ore") then -- Find Ore
			print("Ore Found! Moving to Macerator!")
			chest.push("north", i, 64)
		  else
		  end
		end
	  else
	  end
	end
  else
  end
end

Edit: Ninja'd on the error
Sorcelator #5
Posted 24 September 2013 - 06:39 PM
1. http://pastebin.com/mpy2HvTE <– This is the start up scrip that wraps the peripherals. I'm doubtful you'll find anything odd here its pretty normal stuff.
2. I would love to give you an error, but it doesn't actually print one. It runs ONCE and then stops. Almost as if the "while true do" didn't even exist in my code. Everything in the code works, right up until it is supposed to loop.

I even created a simple loop that does this:

while true do
     os.startTimer(5)
     event = os.pullEvent()
     if event == "timer" then
          mace.push("north", 1, 64)
          efurn.push("up", 1, 64)
     end
end
and it works just fine.
amtra5 #6
Posted 24 September 2013 - 08:26 PM
Have you tried inserting the peripheral wrap at the start of your program, not in startup?
BigTwisty #7
Posted 24 September 2013 - 11:24 PM
One issue with using that particular timer code is that os.pullEvent will pull the first available event, whether it is a timer or not. The way you have it coded, every time a non-timer event occurs, the program will loop back and create a new timer event. The system will then respond to both the new timer and the old one. You could end up with thousands of active timers, looping on each one.

If you want to keep it simple, remove the timer stuff from the top and add sleep(5) to the bottom of the loop. Otherwise you'll need to use the value returned by os.startTimer to ensure only the latest timer is being responded to.
Sorcelator #8
Posted 25 September 2013 - 08:06 AM
That actually makes some sense. I'll give that a shot!

EDIT: unfortunately, that doesn't work either.


chest.condense() --Organize the Chest
local slotnum = chest.getSizeInventory() --Use the Max Invantory Size to calculate the Slot number and Loop amount
while true do
	sleep(5)
	--push completed items out of the mace and efurn--
	mace.push("north", 1, 64)
	efurn.push("up", 1, 64)
	--start looking for ore--
	for i=1,slotnum do -- LOOP!
		local tableInfo = chest.getStackInSlot(i)
		if tableInfo ~= nil then
			for key, value in pairs(tableInfo) do
				if string.find(value, "Ore") then -- Find Ore
					print("Ore Found! Moving to Macerator!")
					chest.push("north", i, 64)
					
				end
			end
			
		end
	end

end


This also ends right after it finds Ore (or doesn't find)
Its clearly something to do with the code that comes after the –start looking for ore– comment. I'm not sure why exactly it doesn't like it. My only guess is that it has something to do with the For loop that checks each slot, pulls its info and checks its values.
Sorcelator #9
Posted 25 September 2013 - 11:49 AM
Ok so I figured out what the issue was and here is how I fixed it.

chest.condense() --Organize the Chest
local slotnum = chest.getSizeInventory() --Use the Max Invantory Size to calculate the Slot number and Loop amount
local i = 0
while true do
	sleep(.5)
	i = i+1
	--push completed items out of the mace and efurn--
	mace.push("north", 1, 64)
	efurn.push("up", 1, 64)
	--start looking for ore--
	local tableInfo = chest.getStackInSlot(i)
	if tableInfo ~= nil then
		if string.find(tableInfo.name, "Ore") then -- Find Ore
			print("Ore Found in slot "..i..". Moving to Macerator!")
			chest.push("north", i, 64)
		else
			shell.run("clear")
			print("Chest contains "..tostring(slotnum).." slots")
			print("Slot "..i.." contains no Ore")				
		end
	else
		i = 0
	end		
	if i == 27 then
		i = 1
	end
end

When the code that finds the ore finds nothing, it starts spits out a nill. My code said "if tableinfo ~=nil then do stuff" so it would work for a while and then die out. I figured out that it was failing right after it hit slots with NO items in it. This would cause a Nil and the code would do the else command. If the code spit out nil to many times the while true do loop would kill itself which is likely a failsafe in Computercraft. The reason I never got an error is that when the code did present me a nil I told it to do something. So it did what I asked and never showed me the nil error.

To fix this i changed what happened in the Else portion of the "if tableinfo ~= nil then". Instead of posting a message, I told it to reset the i variable to 0. This stops it from getting to many nil errors and starts from the beginning again. As long as there is one item in the chest that can't be moved then it will loop forever.

I've cleaned this up a lot, made it only look for the key value of Name in the tableinfo table. This allowed me to get rid of the "for i=1,slotnum do" and the for key, value in pairs(tableInfo) do portion of the code because it was redundant. Now the code works exactly like I need it to. It finds anything that has Ore in its name and moves it to the Macerator and gets the smelting process going. The only downside i've found so far is that it finds items like Cinnabar Ore, which can't be used in a macerator. I'm sure I can figure out a way to add items to a filter or something later.
electrodude512 #10
Posted 25 September 2013 - 04:57 PM
Ok so I figured out what the issue was and here is how I fixed it.
-snip-

About Cinnabar Ore, replace

if string.find(tableInfo.name, "Ore") then -- Find Ore
with

if string.find(tableInfo.name, "Ore") and not string.find(tableInfo.name, "Cinnabar") then -- Find Ore

Just some notes on things you can improve (good practice, not actual problems):

if i == 27 then
	i = 1
end
should be

if i >= slotnum then
	i = 1
end
The >= is in case i somehow gets a higher value - this shouldn't happen but it's still good practice. Use slotnum instead of 27 in case you decide to move to a bigger chest, so you don't have to fix your code.
"shell.run("clear")" can be replaced with "term.setCursorPos(1,1) term.clear()". You can put these two commands in a function if you want. This is more efficient than running a program just to clear the screen.
"if tableInfo ~= nil then … end" can be "if tableInfo then … end" as long as tableInfo isn't false (which it won't be unless you screw up OpenP)

I've been trying to figure out for a while how to organize my machines best to facilitate complete automation in as small a space as possible. You just gave me an great idea - cram all of them next to each other in some way that makes sense, connect them all to peripheral cables, and machine.push() and .pull() items around through chests! Much less confusing than a sensor turtle that has to man all of the machines! I never would have thought of that, as simple as it sounds. Thanks!

stacked layers of:
+AM
MBsM
+CM
where ABC are various machines, s is a chest (s for storage), M's are wired modems connected to the network, and +'s are power cables. There's no machine D so you can stick modems (probably with peripheral proxies) on the chests. Multiple layers would be stacked, allowing the chests to exchange things vertically. This could be optimized by vertically stacking machines commonly used together, like macerators and furnaces, directly above each other, so you don't have to move items through the chest to smelt your macerated metal dusts.