Child scripts
|
| Non-threaded child scripts |
Non-threaded child scripts should contain a collection of system callback functions. Those should not be blocking. This means that every time they are called, they should perform some task and then return control. If control is not returned, then the whole simulation halts. Non-threaded child script functions are called by the main script at least twice per simulation step from the main script's actuation and sensing functions. The system will also call other system callback functions where appropriate (e.g. during child script initialization, clean-up, etc). Non-threaded child scripts should always be chosen over threaded child scripts whenever possible.
Non-threaded child scripts follow a precise calling or execution order: by default, child scripts are called starting with leaf objects (or childless objects), and ending with root objects (or parentless objects). The command sim.handleChildScripts, called from the default main script, handles the most important system callback functions: imagine an example of a simulation model representing an automatic door: a proximity sensor in the front and the back allows detecting an approaching person. When the person is close enough, the door opens automatically. Following code shows a typical non-threaded child script illustrating above example:
function sysCall_init()
sensorHandleFront=sim.getObjectHandle("DoorSensorFront")
sensorHandleBack=sim.getObjectHandle("DoorSensorBack")
motorHandle=sim.getObjectHandle("DoorMotor")
end
function sysCall_actuation()
resF=sim.readProximitySensor(sensorHandleFront)
resB=sim.readProximitySensor(sensorHandleBack)
if ((resF>0)or(resB>0)) then
sim.setJointTargetVelocity(motorHandle,-0.2)
else
sim.setJointTargetVelocity(motorHandle,0.2)
end
end
A non-threaded child script tyically is segmented into a collection of system callback functions, the most important ones are:
There are however many more system callback functions a non-threaded child script can use to react to various events:
function sysCall_init() -- not optional!!
-- Put some initialization code here
end
function sysCall_actuation()
-- Put some actuation code here
end
function sysCall_sensing()
-- Put some sensing code here
end
function sysCall_cleanup()
-- Put some restoration code here
end
function sysCall_dynCallback(inData)
-- See the dynamics callback function section in the user manual for details about the input argument
end
function sysCall_jointCallback(inData)
-- See the joint callback function section in the user manual for details about input/output arguments
return outData
end
function sysCall_contactCallback(inData)
-- See the contact callback function section in the user manual for details about input/output arguments
return outData
end
function sysCall_vision(inData)
-- See the vision callback function section in the user manual for details about input/output arguments
return outData
end
function sysCall_trigger(inData)
-- See the trigger callback function section in the user manual for details about input/output arguments
return outData
end
function sysCall_beforeCopy(inData)
for key,value in pairs(inData.objectHandles) do
print("Object with handle "..key.." will be copied")
end
end
function sysCall_afterCopy(inData)
for key,value in pairs(inData.objectHandles) do
print("Object with handle "..key.." was copied")
end
end
function sysCall_afterCreate(inData)
for key,value in pairs(inData.objectHandles) do
print("Object with handle "..value.." was created")
end
end
function sysCall_beforeDelete(inData)
for key,value in pairs(inData.objectHandles) do
print("Object with handle "..key.." will be deleted")
end
-- inData.allObjects indicates if all objects in the scene will be deleted
end
function sysCall_afterDelete(inData)
for key,value in pairs(inData.objectHandles) do
print("Object with handle "..key.." was deleted")
end
-- inData.allObjects indicates if all objects in the scene were deleted
end
function sysCall_suspend()
-- Simulation is about to be suspended
end
function sysCall_suspended()
-- Simulation is suspended
end
function sysCall_resume()
-- Simulation is about to be resumed
end
| Threaded child scripts |
Threaded child scripts are scripts that will launch in a thread. The launch (and resume) of threaded child scripts is handled by the default main script code, via the sim.launchThreadedChildScripts and sim.resumeThreads functions. Launch/resume of threaded child scripts is executed in a precise order. When a threaded child script's execution is still underway, it will not be launched a second time. When a threaded child script ended, it can be relaunched only if the execute once item in the script properties is unchecked. A threaded child script icon in the scene hierarchy is displayed in light blue instead of white to indicate that it will be launched in a thread.
Threaded child scripts have several weaknesses compared to non-threaded child scripts if not programmed appropriately: they are more resource-intensive, they can waste some processing time, and they can be a little bit less responsive to a simulation stop command.
Following shows a typical threaded child script code, which is however not perfect since it is wasting precious computation time in the loop (the code handles the automatic sliding doors from above's example):
function sysCall_threadmain()
-- Put some initialization code here:
sensorHandleFront=sim.getObjectHandle("DoorSensorFront")
sensorHandleBack=sim.getObjectHandle("DoorSensorBack")
motorHandle=sim.getObjectHandle("DoorMotor")
-- Here we execute the regular thread code:
while sim.getSimulationState()~=sim.simulation_advancing_abouttostop do
resF=sim.readProximitySensor(sensorHandleFront)
resB=sim.readProximitySensor(sensorHandleBack)
if ((resF>0)or(resB>0)) then
sim.setJointTargetVelocity(motorHandle,-0.2)
else
sim.setJointTargetVelocity(motorHandle,0.2)
end
-- this loop wastes precious computation time since we should only read new
-- values when the simulation time has changed (i.e. in next simulation step).
end
end
function sysCall_cleanup()
-- Put some clean-up code here:
end
A threaded child script typically is segmented into a collection of system callback functions, the main ones being:
CoppeliaSim uses threads to mimic the behavior of coroutines, instead of using them traditionally, which allows for a great deal of flexibility and control: by default a threaded child script will execute for about 1-2 milliseconds before automatically switching to another thread. This default behavior can be changed with the sim.setThreadSwitchTiming or sim.setThreadAutomaticSwitch. Once the current thread was switched, it will resume execution in next simulation pass (i.e. at a time currentTime+simulationTimeStep). The thread switching is automatic (occurs after the specified time), but the sim.switchThread command allows to shorten that time when needed. Using above three commands, a perfect synchronization with the main simulation loop can be achieved. Following code (handling the automatic sliding doors from above's example) shows child script synchronization with the main simulation loop:
function sysCall_threadmain()
-- Put some initialization code here:
sim.setThreadAutomaticSwitch(false) -- disable automatic thread switches
sensorHandleFront=sim.getObjectHandle("DoorSensorFront")
sensorHandleBack=sim.getObjectHandle("DoorSensorBack")
motorHandle=sim.getObjectHandle("DoorMotor")
-- Here we execute the regular thread code:
while sim.getSimulationState()~=sim.simulation_advancing_abouttostop do
resF=sim.readProximitySensor(sensorHandleFront)
resB=sim.readProximitySensor(sensorHandleBack)
if ((resF>0)or(resB>0)) then
sim.setJointTargetVelocity(motorHandle,-0.2)
else
sim.setJointTargetVelocity(motorHandle,0.2)
end
sim.switchThread() -- Explicitely switch to another thread now!
-- from now on, above loop is executed once in each simulation step.
-- this way you do not waste precious computation time and run synchronously.
end
end
function sysCall_cleanup()
-- Put some clean-up code here
end
Above while loop will now execute exactly once for each main simulation loop and not waste time reading sensors states again and again for same simulation times. By default, threads always resume when the main script calls sim.resumeThreads(sim.scriptthreadresume_default). If you need to make sure your thread runs only while the main script is in the sensing phase, you can resquedule the thread's resume location with the API function sim.setThreadResumeLocation.
The coroutine-like behavior of CoppeliaSim's threads cannot be distinguished from regular threads, with the exception that if an external command (for example socket communication commands provided by Lua libraries) is blocking, then CoppeliaSim will also appear as blocking. In that case, a non-blocking section can be defined as in following example:
sim.setThreadIsFree(true) -- Start of the non-blocking section
http = require("socket.http")
print(http.request("http://www.google.com")) -- this command may take several seconds to execute
sim.setThreadIsFree(false) -- End of the non-blocking section
While in a non-blocking section, avoid at all cost calling sim-functions. Never forget to close the blocking section otherwise CoppeliaSim might hang or run much slowlier. Also refer to the thread-related functions and to the blocking functions for more details on thread operation in CoppeliaSim.
Some operation should not be interrupted in order to execute correctly (imagine moving several objects in a loop). In that case, you can temporarily forbid thread switches with the sim.setThreadAutomaticSwitch function.
There are a few other system callback functions a threaded child script can use to react to various events:
function sysCall_threadmain() -- not optional!!
-- Initialization and main loop
end
function sysCall_cleanup()
-- Put some restoration code here
end
function sysCall_vision(inData)
-- See the vision callback function section in the user manual for details about input/output arguments
return outData
end
function sysCall_trigger(inData)
-- See the trigger callback function section in the user manual for details about input/output arguments
return outData
end