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

Variables and coroutines

Started by Balthamel, 23 July 2015 - 05:00 AM
Balthamel #1
Posted 23 July 2015 - 07:00 AM
I have a coroutine that during the course of its proccess runs

actionTimerID = os.startTimer(0.35)

This timer fires, i catch it and i can see it detected in the main thread but in the main thread actionTImerID is nil
i local actionTimerID at the top of the main thread or in the header. Am i misunderstanding coroutines again?

http://pastebin.com/f7mUVPFJ

It does not have good readability.
Edited on 23 July 2015 - 07:31 AM
HPWebcamAble #2
Posted 23 July 2015 - 07:01 AM
Could you post the rest of the code involved?
Balthamel #3
Posted 23 July 2015 - 07:02 AM
It's 341 lines long.
Edited on 23 July 2015 - 07:31 AM
HPWebcamAble #4
Posted 23 July 2015 - 07:04 AM
You can put it on pastebin.com, and put a link to it here
Balthamel #5
Posted 23 July 2015 - 07:04 AM
I did. :)/>
On first run through coroutine on line 33 is called
switch at 98 to 100 is the only real part of the coroutine used
then it sits at line 301 until the timer fires but actionTimerID never makes it out of the loop
Edited on 23 July 2015 - 05:06 AM
HPWebcamAble #6
Posted 23 July 2015 - 07:21 AM
Not so sure this is doing what you think it is:

if turtle.dig() then --dig it
  digTimerID = os.startTimer(0.35) --start a timer until the dig action completes
  coroutine.yield("digging") --yield the coroutine until the dig timer expires
else
  --something is very wrong-----------------------------------------------------------------------------------------
end
When you call turtle.dig in the first line, it COMPLETES the dig, so it can return.
You then start a timer for .35 seconds, and yield.

Also, if it fails, its likely bedrock, or maybe some kind of block protection
(Though you might have known that and just not implemented it yet)


Then when you load the file:

local junkFile = io.open("junk","r") --open the file junkFile
repeat
  junkItems[junkFile.read("*line") or "endOfLine"] = true --save the line from file as the key set value true until all lines are done then set key endOfLine true
until junkItems["endOfLine"] --file is all imported
io.close(junkFile) --close the file

You can do that much neater like this:
(Assuming each line is a 'junkItem')

local junkItems = {}

local f = fs.open("junk","r")
for line in f.readLine do
  table.insert(junkItems,line)
end
f.close()



TLDR: (Which I wont blame you for)

You shouldn't need to manage it like this, with coroutine.

You do this to prevent losing modem/rednet messages while preforming turtle actions, correct?

Will that actually happen? I was under the impression it didn't, you may want to confirm that


After testing, I think you will need to use coroutines, but it can be much simpler if you just use the parallel API.
Edited on 23 July 2015 - 07:51 PM
Balthamel #7
Posted 23 July 2015 - 07:25 AM
It does, from experimentation, turtle dig yields, takes 1 tick to find out if it's a success and then takes about 6 ticks to perform the animation which would cause hanging issues if i tried to start another turtle[command] in those ticks so it starts a timer which is not a wait for completion but wait till it's safe to start another action.

Yep, that long line of — is code for me going you need to deal with this

Yep got told that about an hour ago. i'll tidy it up shortly.
Edited on 23 July 2015 - 05:26 AM
HPWebcamAble #8
Posted 23 July 2015 - 07:33 AM
It does, from experimentation, turtle dig yields, takes 1 tick to find out if it's a success and then takes about 6 ticks to perform the animation which would cause hanging issues if i tried to start another turtle[command] in those ticks so it starts a timer which is not a wait for completion but wait till it's safe to start another action.

Hm fair enough. I'll be doing some tests of my own when I have time, but for the now…

The only thing that I saw is minningStatus might be 0

Maybe you ought to throw some 'prints' in your code, so you can figure out what the problem is.
Balthamel #9
Posted 23 July 2015 - 07:38 AM
prints are flooding the live code, it definatly has miningStatus set to 1 it goes all the way through all the twists until it gets to line 98, it runs identify it yields the turtle returns the turtle_response event it returns to the coroutine and starts the timer, all of these things have print statements to back them up it then yields again the timer fires it matches the timer switch on line 307 buit then fails the if statement on 309 because actionTimerID is nil

I'm getting to that point where i'm questioning things I probably already answered, should there be a coroutine.yield() at line 136?
Edited on 23 July 2015 - 05:46 AM
Bomb Bloke #10
Posted 23 July 2015 - 09:05 AM
Frankly, I won't feel you've got a proper understanding of coroutines until I get you to admit that the parallel API is a better option than attempting to handle it yourself. But I've decided that's a pipe dream, so…

This chunk of code, as HPWebcamAble points out, is broken:

if turtle.dig() then --dig it
  digTimerID = os.startTimer(0.35) --start a timer until the dig action completes
  coroutine.yield("digging") --yield the coroutine until the dig timer expires
else
  --something is very wrong-----------------------------------------------------------------------------------------
end

turtle.dig() starts the action, yields, waits to be resumed with an event detailing the result, then finally returns that result. Until ALL of that happens, execution of this code segment won't make it past the first line. By the time you're starting your timer on the second line, turtle.dig() has finished, either successfully or otherwise, and so there's no point in timing it.

Your coroutine has a local "coroutineAction" variable, which will only be set the first time you resume it (because you never set it again anywhere else). It'll therefore entirely ignore the one you made local to the rest of the script.

I get the impression you're somehow thinking os.startTimer() yields? It doesn't; it simply starts a timer and returns the ID. After you set actionTimerID, actionCoroutine does not yield again at all (getting stuck looping from lines 34-137, doing nothing as coroutineAction has been set to false), and so your main loop never gets to check it. I'd assume the script either crashes or that the turtle completely shuts down, as ComputerCraft's yield protection system will step in shortly after that.

I know where you're going with all this, and, if implemented correctly, your ideas can indeed be made to work. But I still have to point out that you're doing things the long and hard way. I've finally given in and started writing a guide explaining coroutines (and how you're expected to use them within the ComputerCraft framework) - it may be worthwhile waiting for that. Quite frankly I'd like to re-write your whole script to make it operate the way you want it to, then re-write it again to get the same results in a more efficient manner. If only time were an unlimited resource…

But, I suppose the quick'n'dirty fix would be to change line 136 to "coroutineAction = os.pullEvent()". That should be sufficient to at least get things moving.
Balthamel #11
Posted 23 July 2015 - 09:23 AM
It Does get past it, check how turtle.dig() works in live it yields until it recieves the event which it gets (line 330) but that only takes 0.05s however it still takes time to move the axe so it can't do a subsequent turtle command until that action is finished. The event is fired and resolved in 0.05 seconds but the action itself takes longer, i'm not sure how I can explain this more clearly as I have definatly said it several times in this thread already.
It does finish, a print confirms this as i said the variable IS SET on line 102 a print(actionTimerID) on line 103 prints a number. the timer FIRES in the MAIN loop i can SEE it fire by adding print(eventQueue[1],eventQueue[2]) on line 305 but by that point actionTimerID is nil. Please read what I type.

the code hangs because and i cannot emphasise this enough
the timer event fires but in the switch the timer filter by ID is now nil and not the previous value (usually 24)

actionCoroutine is called with arguments in miningCoroutine for reasons

Yeah i was LOOKING at the infinite while loop but it doesnt crash and somehow it exits, if you look at my last post i'm considering a yield at line 138 but since it exits back to main i'm not sure if that might cause issues!

the coroutine is called all over in really odd ways, it's resumed everywhere and readability is definatly not good so please read what I have typed in this thread before saying that there are issues where there are none.

If you want to test this yourself in live I am launching it with
junk
minecraft:grass
minecraft:dirt
minecraft:stone

and
location
0
0
0
0
3001
0
1

Finally I cannot see how parallel would work
whule true do
paralleleaitforany(main,rednet)
end
would cause issues when it recieves a rednet message and then broke what was going on in main since I want rednet to immediately trigger actions
waitforall would just hang forever as main would have to be looped to do multiple things

TLDR
The issue you say is not the issue.
Edited on 23 July 2015 - 07:27 AM
Balthamel #12
Posted 23 July 2015 - 09:39 AM


timer event switch is
print("timer event switch") and exists on line 311
ignore the exits that is an exit() added on line 312 it says 317 because of all the other print statements in the code.

Timer set ID:35 is
Print("Timer set ID:"..actionTimerID)

http://pastebin.com/Lx81bCxT

Here's the live script exported from my turtle.~

Also I want to copy the actionCoroutine to the mining boss which will be psending and recieving rednet and plotting routes and moving all at the same time. I don't thing parallel would handle that properly since the moving and planning routes are heavily interrelated.




I'd assume the script either crashes or that the turtle completely shuts down, as ComputerCraft's yield protection system will step in shortly after that.
If you think I'm rude here this line is what has annoyed me. You have not read even the first post, it clearly explains that that is not the issue I am having.
Edited on 23 July 2015 - 07:47 AM
flaghacker #13
Posted 23 July 2015 - 10:44 AM
Because I had a bit of free time, and I didn't want to debug completely broken code, I decided to write up an example script of using the parallel api to really easily accomplish what you're trying to do.

A couple of things about the code:
It receives commands via rednet and executes them in a seperate coroutine. A command is a table with the key 'cmd' indicating what turtle function to execute (eg "up", "turnLeft", …), and a key 'count' to indicate how many times to execute that function (eg 1, 2, 14). The rednet message can be that table itself or a serialised version of it (for backwards compatibility).

The code is untested (I don't have access to a pc right now but I'll test it tonight). It can be found here:
http://pastebin.com/16xZRNLV

I hope this helps you understand how to go about this, feel free to ask questions about it or report errors to me.
Balthamel #14
Posted 23 July 2015 - 11:25 AM
Okay i fixed it, line 301

the resuming from file code was called during initialisation
then the variable was made global
then main started.
very simple nothing to do with using coroutines wrong at all.
Bomb Bloke #15
Posted 23 July 2015 - 01:40 PM
Please read what I type.

I did. It's not my fault if the code you provided did not contain the issue you were talking about.
Balthamel #16
Posted 23 July 2015 - 03:11 PM
Yeah, my bad there. Although, if the first question had just been answered as, yes variables should be fine moving from inside to outside a coroutine as long as they are declared in the header. You must be overwriting it somewhere. I would probably have found my mistake a lot sooner.
Edited on 23 July 2015 - 01:16 PM
KingofGamesYami #17
Posted 23 July 2015 - 03:44 PM
It Does get past it, check how turtle.dig() works in live it yields until it recieves the event which it gets (line 330) but that only takes 0.05s however it still takes time to move the axe so it can't do a subsequent turtle command until that action is finished.

…But the turtle_success or turtle_failure event is fired after the animation. Test using this code:

while true do
  turtle.dig()
  turtle.turnRight()
end

It should show clearly that the turtle waits until the animation has played before executing the next command.
Balthamel #18
Posted 23 July 2015 - 04:49 PM
It does when it runs entirely in its own function but if run inside a coroutine which yields out it doesn't! try


co=coroutine.create(function()
turtle.forward()
end)
print(os.clock())
coroutine.resume(co)
os.eventPull("turtle_response")
print(os.clock())

the difference between the two prints is 0.05, 1 tick, if you pass that back using coroutine.resume(co,unpack(eventpull)) then it will activate and continue in 1 tick then wait further along the line for the action to finish.
KingofGamesYami #19
Posted 23 July 2015 - 05:20 PM
So, like this?


co=coroutine.create(function()
turtle.forward()
end)
print(os.clock())
coroutine.resume(co)
e = {os.eventPull("turtle_response")}
print(os.clock())
coroutine.resume(co, unpack( e ))
print(os.clock())

…because for me, that prints


0.00
0.05
0.05

which tells me "The turtle_response event was fired 0.5 seconds after the turtle moved forward". It doesn't wait any further for any action to finish, I have no idea what you are talking about.
Balthamel #20
Posted 23 July 2015 - 05:22 PM
No look at it 0.05 that's 1/20th a second 1 tick not 0.5, 10 ticks
Balthamel #21
Posted 23 July 2015 - 05:36 PM
Sorry i totally messed that up.
Forget that one.
Consider this one

print(os.clock())
turtle.forward()
print(os.clock())
turtle.forward()
print(os.clock())
this results in times of
0.00
0.05
0.50
or thereabouts
the turtle movement continues the code after only 1 tick but it delays subsequent turtle.comands() by the time needed to actually finish the animation.
And i'm no confident that that delay is a yield delay, i suspect it's in the java method so it would hog the process in effect running sleep for the 0.4s or however long it lasts.
Correct me if i'm wrong.
Edited on 23 July 2015 - 03:37 PM
KingofGamesYami #22
Posted 23 July 2015 - 07:37 PM
When I run the code you posted, my exact results were:


66.55
66.6
67

which means the delay is there. But, I don't think you need to compensate for it - the results indicate "turtle_response" events will simply be fired later if needed.
Bomb Bloke #23
Posted 24 July 2015 - 02:59 AM
It does when it runs entirely in its own function but if run inside a coroutine which yields out it doesn't!

Your script is always running inside a coroutine - and these turtle functions always yield. Implementing a coroutine manager of your own inside that one doesn't change their behaviour.

But, I don't think you need to compensate for it - the results indicate "turtle_response" events will simply be fired later if needed.

I believe the goal is to minimise the time between the start and end of every movement, so that the file writes can be spammed with as little margin for error as possible. I mean, obviously the code doesn't do that now, making it look quite pointless, but I assume that's where Balthamel plans to take it.

Of course, simply defining wrapper functions for the regular turtle functions would be a much simpler method of handling that - replacing the entirety of "actionCoroutine", but using much of its code, say. Heck, just building a coroutine manager that doesn't try to handle every event type in "a special way" would cut down the code a fair bit.
Balthamel #24
Posted 24 July 2015 - 02:09 PM
Okay, after doing more tests it appears you are right, the turtle.command yeilds instead of sleeping (which is what I thought it did) when two turtle.commands act in sequence. I'll rewrite the structure of the action coroutine at some point.