Multiple threaded scripts

Typically: "How do I... ", "How can I... " questions
Jonatas Teixeira
Posts: 17
Joined: 13 Oct 2023, 18:04

Multiple threaded scripts

Post by Jonatas Teixeira »

Hello,
I'm having a hard time simulating an assembling factory with 4 robots and a circular conveyor.
I have 15 threaded scripts running in the scene.
During the development and implementation of each work stations some of the scripts started to behave abnormally some of threaded scripts stopped working at all. I have already put a couple sim.switchThread() along the various scripts, but it still looks like my laptop is not getting to handle all the computation necessary to run the simulation properly.

Can you give me any advice on how to handle this situation? should I put more sim.switchThread() in the codes or something like that?

coppelia
Site Admin
Posts: 10504
Joined: 14 Dec 2012, 00:25

Re: Multiple threaded scripts

Post by coppelia »

Hello,

you can't add sim.step (or sim.switchThread, which is the same) here and there and expect things to magically work.

Can you describe in more details what you mean with
the scripts started to behave abnormally
?

A thread in CoppeliaSim will automatically yield after a specific number of ms. You can adjust this timing with sim.setAutoYieldDelay. If you do not want your script to automatically yield, then call sim.setStepping(true). In that case, the thread will block until sim.step is called (directly, or within a higher-level API function such as sim.moveToConfig, sim.moveToPose or sim.wait)

Also, are your threads running Lua or Python? Keep in mind that there are a few differences between Lua and Python in CoppeliaSim

Cheers

Jonatas Teixeira
Posts: 17
Joined: 13 Oct 2023, 18:04

Re: Multiple threaded scripts

Post by Jonatas Teixeira »

coppelia wrote: 27 Aug 2024, 05:57 you can't add sim.step (or sim.switchThread, which is the same) here and there and expect things to magically work.
Sure, that's why I'm here...
Also, are your threads running Lua or Python?
I'm using Lua.
Can you describe in more details what you mean with
Of course, I've been experiencing a lot of problems on the execution of the scripts but the very last one can be described as follows:

I have a robot which handles two pieces that ought to be assembled over a table. So the robot pick and place two different pieces over it. On the table I have a separated script which is enabled only if the robot is active, the objective of the table script is to assemble the pieces making the first to be the parent of the second. I enable the table script using a sim.setInt32Signal() in the robot script and a if loop in the table script. Once I had lesser scripts and elements it worked perfectly, but once I added the 4th work station it just doesn't work (I made some "print" to check the signal and everything is OK, but table script neglected it, and didn't execute at all.
A thread in CoppeliaSim will automatically yield after a specific number of ms
how long is it by default?
(directly, or within a higher-level API function such as sim.moveToConfig, sim.moveToPose or sim.wait)
I have about 20 calls of sim.moveToPose() function for each robot (4 robots in total), what is your advice regarding this issue?

I'm humbly asking for help I have tried many things like changing my code structure and number of conditional loops...

coppelia
Site Admin
Posts: 10504
Joined: 14 Dec 2012, 00:25

Re: Multiple threaded scripts

Post by coppelia »

The auto-yield delay is 2ms by default.
...but table script neglected it, and didn't execute at all.
So that script does not run as expected. Please show us the content of that script. Are there any error messages in the status bar?

If you have several scripts operating at the same time and relying on each other (or signals from each other) it can quickly happen that you land in a dead-lock situation. We do not know what code you are running in your scripts, and the cause of your problems could be multiple, including bugs in your code.

sim.moveToPose and similar are not functions that will waste time: they execute a small piece of code then automatically yield.

If you can post a minimalistic, self-contained version of your scene that illustrates your problem, that would make things somewhat easier..

Cheers

Jonatas Teixeira
Posts: 17
Joined: 13 Oct 2023, 18:04

Re: Multiple threaded scripts

Post by Jonatas Teixeira »

Please show us the content of that script

Code: Select all

function sysCall_init()
    sim = require('sim')
    Table=sim.getObject('.')
    count=0
    part1=0
    Lpd=sim.getInt32Signal('ALPd')
    corout=coroutine.create(coroutineMain)
end

function sysCall_actuation()
    if coroutine.status(corout)~='dead' then
        local ok,errorMsg=coroutine.resume(corout)
        if errorMsg then
            error(debug.traceback(corout,errorMsg),2)
        end
    end
end

function coroutineMain()
    while true do
        if sim.getInt32Signal("NAwork")==1 then
             if count==0 then
                local res,collPair1=sim.checkCollision(Table,sim.handle_all)
                if res==1 and collPair1[2]~=sim.getObjectParent(Table) then
                    local p = sim.getObjectSpecialProperty(Table)
                    sim.setObjectSpecialProperty(Table, p ~ sim.objectspecialproperty_collidable)
                    sim.setObjectParent(collPair1[2],Table,true)
                    count=count+1
                    part1=collPair1[2]
                    print("Table + square")
                end
            end
            sim.step()
            if (count>0) then 
                if(sim.getInt32Signal("NAmount")<3) then
                    local res2,collPair2=sim.checkCollision(part1,sim.handle_all)
                    if res2==1 then
                        if collPair2[1]~=LPd and collPair2[2]~=LPd and collPair2[2]~=sim.getObjectParent(part1) then
                            sim.setObjectParent(collPair2[2],part1,true)
                            local p = sim.getObjectSpecialProperty(part1)
                            sim.setObjectSpecialProperty(part1, p ~ sim.objectspecialproperty_collidable)
                            count=count+1
                            part2=collPair2[2]
                            print("part + part")
                            sim.setInt32Signal("NAmount",3)
                            print("NAmount= "..sim.getInt32Signal("NAmount"))
                            print("Count= "..count)
                            if count==2 then
                                p = sim.getObjectSpecialProperty(part1)
                                sim.setObjectSpecialProperty(part1, p ~ sim.objectspecialproperty_collidable)
                                p = sim.getObjectSpecialProperty(part2)
                                sim.setObjectSpecialProperty(part2, p ~ sim.objectspecialproperty_collidable)
                                sim.setObjectSpecialProperty(part2, p ~ sim.objectspecialproperty_detectable)
                                p = sim.getObjectSpecialProperty(Table)
                                sim.setObjectSpecialProperty(Table, p ~ sim.objectspecialproperty_collidable)
                            end
                            count=0
                            part1=0
                        end
                    end
                end
            end
            sim.step()
        end
    end
end
function sysCall_cleanup()
   
end
Are there any error messages in the status bar?
Nope, just some scripts not running, yesterday I had a meeting with my tutors and we decided to change the code structure once more to manually control every step by turning using sim.setStepping(true), I will try this solution and see what happens.
If you can post a minimalistic, self-contained version of your scene that illustrates your problem, that would make things somewhat easier..
Unfortunately I can't post the scene since it is part of my doctoral research.

PS: One of my tutors also suggested to try to use the sim.systemSemaphore function, but I don't really know how I would use it here, I couldn't find any example of use.

coppelia
Site Admin
Posts: 10504
Joined: 14 Dec 2012, 00:25

Re: Multiple threaded scripts

Post by coppelia »

Please add a lot of prints to the script you shared, in order to understand where and when it stops responding. e.g.:

Code: Select all

function sysCall_init()
    print("sysCall_init, A")
    sim = require('sim')
    Table=sim.getObject('.')
    count=0
    part1=0
    Lpd=sim.getInt32Signal('ALPd')
    corout=coroutine.create(coroutineMain)
    print("sysCall_init, B")
end

function sysCall_actuation()
    print("sysCall_actuation, A")
    if coroutine.status(corout)~='dead' then
        print("sysCall_actuation, B")
        local ok,errorMsg=coroutine.resume(corout)
        if errorMsg then
            error(debug.traceback(corout,errorMsg),2)
        end
    end
    print("sysCall_actuation, C")
end

function coroutineMain()
    while true do
        print("coroutineMain, A")
        if sim.getInt32Signal("NAwork")==1 then
            print("coroutineMain, B")
             if count==0 then
                local res,collPair1=sim.checkCollision(Table,sim.handle_all)
                if res==1 and collPair1[2]~=sim.getObjectParent(Table) then
                    local p = sim.getObjectSpecialProperty(Table)
                    sim.setObjectSpecialProperty(Table, p ~ sim.objectspecialproperty_collidable)
                    sim.setObjectParent(collPair1[2],Table,true)
                    count=count+1
                    part1=collPair1[2]
                    print("Table + square")
                end
            end
            print("coroutineMain, C")
            sim.step()
            print("coroutineMain, D")
            if (count>0) then 
                print("coroutineMain, E")
                if(sim.getInt32Signal("NAmount")<3) then
                    print("coroutineMain, F")
                    local res2,collPair2=sim.checkCollision(part1,sim.handle_all)
                    if res2==1 then
                        print("coroutineMain, G")
                        if collPair2[1]~=LPd and collPair2[2]~=LPd and collPair2[2]~=sim.getObjectParent(part1) then
                            print("coroutineMain, H")
                            sim.setObjectParent(collPair2[2],part1,true)
                            local p = sim.getObjectSpecialProperty(part1)
                            sim.setObjectSpecialProperty(part1, p ~ sim.objectspecialproperty_collidable)
                            count=count+1
                            part2=collPair2[2]
                            print("part + part")
                            sim.setInt32Signal("NAmount",3)
                            print("NAmount= "..sim.getInt32Signal("NAmount"))
                            print("Count= "..count)
                            if count==2 then
                                print("coroutineMain, I")
                                p = sim.getObjectSpecialProperty(part1)
                                sim.setObjectSpecialProperty(part1, p ~ sim.objectspecialproperty_collidable)
                                p = sim.getObjectSpecialProperty(part2)
                                sim.setObjectSpecialProperty(part2, p ~ sim.objectspecialproperty_collidable)
                                sim.setObjectSpecialProperty(part2, p ~ sim.objectspecialproperty_detectable)
                                p = sim.getObjectSpecialProperty(Table)
                                sim.setObjectSpecialProperty(Table, p ~ sim.objectspecialproperty_collidable)
                            end
                            count=0
                            part1=0
                        end
                    end
                end
            end
            print("coroutineMain, J")
            sim.step()
            print("coroutineMain, K")
        end
        print("coroutineMain, L")
    end
end
function sysCall_cleanup()
    print("sysCall_cleanup, A")
end
There is nothing in particular that blocks your script from running. Of course it could behave differently than expected, if it doesn't receive (and clears) the various signals. You can easily get into a dead-lock when 2 or more scripts are waiting on each other via signals that are not properly set/reset.

sim.systemSemaphore won't help in your case (that function is to be able to synchronize 2 or more CoppeliaSim instances running in paralel)

Cheers

Jonatas Teixeira
Posts: 17
Joined: 13 Oct 2023, 18:04

Re: Multiple threaded scripts

Post by Jonatas Teixeira »

First minutes of simulation gave a loop of:

Code: Select all

coroutineMain, A
sysCall_actuation, C
sysCall_actuation, A
sysCall_actuation, B
coroutineMain, L
The first time the scritpt starts running I get:

Code: Select all

sysCall_actuation, C
sysCall_actuation, A
sysCall_actuation, B
coroutineMain, A
sysCall_actuation, C
sysCall_actuation, A
sysCall_actuation, B
coroutineMain, B
sysCall_actuation, C
sysCall_actuation, A
sysCall_actuation, B
coroutineMain, C
sysCall_actuation, C
sysCall_actuation, A
sysCall_actuation, B
sysCall_actuation, C
sysCall_actuation, A
sysCall_actuation, B
coroutineMain, D
sysCall_actuation, C
sysCall_actuation, A
sysCall_actuation, B
sysCall_actuation, C
sysCall_actuation, A
sysCall_actuation, B
coroutineMain, J
sysCall_actuation, C
sysCall_actuation, A
sysCall_actuation, B
sysCall_actuation, C
sysCall_actuation, A
sysCall_actuation, B
coroutineMain, K
sysCall_actuation, C
sysCall_actuation, A
sysCall_actuation, B
coroutineMain, L
Once the Table finished the process of the first piece it backs to the first loop I posted...

the second time it run to process the second piece it gave me this:

Code: Select all

coroutineMain, A
sysCall_actuation, C
sysCall_actuation, A
sysCall_actuation, B
coroutineMain, B
sysCall_actuation, C
sysCall_actuation, A
sysCall_actuation, B
coroutineMain, C
sysCall_actuation, C
sysCall_actuation, A
sysCall_actuation, B
sysCall_actuation, C
sysCall_actuation, A
sysCall_actuation, B
coroutineMain, D
sysCall_actuation, C
sysCall_actuation, A
sysCall_actuation, B
sysCall_actuation, C
sysCall_actuation, A
sysCall_actuation, B
coroutineMain, J
sysCall_actuation, C
sysCall_actuation, A
sysCall_actuation, B
sysCall_actuation, C
sysCall_actuation, A
sysCall_actuation, B
coroutineMain, K
sysCall_actuation, C
sysCall_actuation, A
sysCall_actuation, B
sysCall_actuation, C
sysCall_actuation, A
sysCall_actuation, B
coroutineMain, L

coppelia
Site Admin
Posts: 10504
Joined: 14 Dec 2012, 00:25

Re: Multiple threaded scripts

Post by coppelia »

Please add sim.setStepping(true) in the init section and try again. What is the output?
Having a simple, self-sufficient scene would make things much simpler..

Cheers

Jonatas Teixeira
Posts: 17
Joined: 13 Oct 2023, 18:04

Re: Multiple threaded scripts

Post by Jonatas Teixeira »

Got into a infinite loop of:

Code: Select all

coroutineMain, A
coroutineMain, L
coroutineMain, A
coroutineMain, L
coroutineMain, A
coroutineMain, L
coroutineMain, A
Needed to abort the script, I'll probably need to add more steps.
Having a simple, self-sufficient scene would make things much simpler..
I'll restructure my codes to use the sim.setStepping(true) on each script I wrote.

Tyvm for all the help. I'll let you know if I succeeded or not.

Jonatas Teixeira
Posts: 17
Joined: 13 Oct 2023, 18:04

Re: Multiple threaded scripts

Post by Jonatas Teixeira »

Hi there,

I've solved the last problem with the code execution that we discussed last week using the sim.setStepping(true) as you suggested, but I'm having troubles with the execution of another script. I then decided to put a print in inside the while loop of each script to see if the execution order was somewhat interfering in the overall behavior and got a loop like this:

Code: Select all

coroutineMain, NA/Mesa
coroutineMain, NQ/Check
coroutineMain, NM/Drill
coroutineMain, NA
coroutineMain, NA
coroutineMain, NA
coroutineMain, NA
coroutineMain, NW
coroutineMain, NW
coroutineMain, NW
coroutineMain, B2
coroutineMain, F2
coroutineMain, F1
coroutineMain, NQ
coroutineMain, NQ
coroutineMain, NQ
coroutineMain, F3
coroutineMain, B3
coroutineMain, F4
coroutineMain, B4
coroutineMain, NM
coroutineMain, NM
coroutineMain, NM
coroutineMain, T1
coroutineMain, T0


As you can see, it is repeating some of the scripts before going for the next ones, those I didn't use the sim.setStepping(true) so they are switching automatically in 2ms.

The question I have is: Is there a way to control the script execution flow? choosing which script will be executed first, the second, and so on?
Last edited by Jonatas Teixeira on 05 Sep 2024, 19:36, edited 1 time in total.

Post Reply