Messaging/interfaces/connectivity
There are several ways messages or data can be exchanged/transmitted/received in and around CoppeliaSim.
One can exchange data within CoppeliaSim, via:
signals
custom data blocks
calling plugin functions
calling script functions
broadcasting a message
Data can also be exchanged with an external application, other computer, machine, etc., via:
calling script functions
broadcasting a message
Remote API
ROS
ZeroMQ
WebSocket
serial port
sockets
other
Signals
Signals can be seen as global variables. They can be defined, redefined, read and cleared. For example:
-- script 1 writes the data to string signal mySignalName:
local myData={1,2,{"Hello","world",true,{value1=63,value2="aString"}}}
sim.setStringSignal("mySignalName",sim.packTable(myData))
-- script 2 reads the data from string signal mySignalName:
local myData=sim.getStringSignal("mySignalName")
if myData then
myData=sim.unpackTable(myData)
end
Custom data blocks
Custom data blocks is data that is stored inside of a scene object, or inside a scene. It can be used to store custom data to be saved together with a model or scene, but also as a means of communication. For example:
-- script 1 writes the data to the scene:
local myData={1,2,{"Hello","world",true,{value1=63,value2="aString"}}}
sim.writeCustomDataBlock(sim.handle_scene,"myTag",sim.packTable(myData))
-- script 2 reads the data from the scene:
local myData=sim.readCustomDataBlock(sim.handle_scene,"myTag")
if myData then
myData=sim.unpackTable(myData)
end
Calling plugin functions
Scripts can call specific plugin functions, so-called callback functions: in order to be able to do this, the plugin must first register its callback functions via simRegisterScriptFunction. This is a convenient mechanism to extend CoppeliaSim's functionality, but can also be used for complex data exchange between scripts and plugins. Following illustrates a very simple plugin function and its registration:
void myCallbackFunc(SScriptCallBack* p)
{
int stack=p->stackID;
CStackArray inArguments;
inArguments.buildFromStack(stack);
if ( (inArguments.getSize()>0)&&inArguments.isString(0) )
{
std::string tmp("we received a string: ");
tmp+=inArguments.getString(0);
simAddLog("ABC",sim_verbosity_msgs,tmp.c_str());
CStackArray outArguments;
outArguments.pushString("Hello to you too!");
outArguments.buildOntoStack(stack);
}
else
simSetLastError("simABC.func","Not enough arguments or wrong arguments.");
}
// Somewhere in the plugin's initialization code:
simRegisterScriptCallbackFunction("simABC.func@ABC","string reply=simABC.func(string inputStr)",myCallbackFunc);
Calling script functions
A script function can obviously be called from within the same script, but also:
across scripts (via sim.callScriptFunction)
from a plugin (via simCallScriptFunctionEx)
from a ROS client (via a callback mechanism)
or from a remote API client
The called script function can perform various tasks, then send back data to the caller. This is also a simple way to extend the functionality of an external application in a quick manner. It is however important that the called script doesn't perform lengthly tasks, otherwise everything will come to a halt (lengthly tasks should rather be triggered externally, and processed at an appropriate moment by the script itself when called from the regular system callbacks).
Broadcasting messages
A script or a remote API client can broadcast a message to all scripts at once, via the sim.broadcastMsg function. For instance, following will constantly broadcast a message to all scripts, including to itself:
function sysCall_init()
scriptHandle=sim.getScriptInt32Param(sim.handle_self,sim.scriptintparam_handle)
end
function sysCall_sensing()
local message={id='greetingMessage',data={msg='Hello!'}}
sim.broadcastMsg(message)
end
function sysCall_msg(msg,origin)
if origin~=scriptHandle and msg.id=='greetingMessage' then
print(string.format("Received following message from script %i:",origin))
print(msg.data.msg)
end
end
ZMQ
The ZeroMQ library, wrapped inside the ZMQ plugin, offers several API functions related to ZeroMQ messaging. Following illustrates a simple requester:
function sysCall_init()
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()
printf('Connecting to hello world server...')
context=simZMQ.ctx_new()
requester=simZMQ.socket(context,simZMQ.REQ)
simZMQ.connect(requester,'tcp://localhost:5555')
for request_nbr=0,10 do
print('-----------------------------------------')
local data='Hello'
printf('[requester] Sending "%s"...',data)
simZMQ.send(requester,data,0)
local rc,data=simZMQ.recv(requester,0)
printf('[requester] Received "%s"',data)
end
end
function sysCall_cleanup()
simZMQ.close(requester)
simZMQ.ctx_term(context)
end
And following would be the corresponding responder:
function sysCall_init()
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()
context=simZMQ.ctx_new()
responder=simZMQ.socket(context,simZMQ.REP)
local rc=simZMQ.bind(responder,'tcp://*:5555')
if rc~=0 then error('failed bind') end
while true do
local rc,data=simZMQ.recv(responder,0)
printf('[responder] Received "%s"',data)
data='World'
printf('[responder] Sending "%s"...',data)
simZMQ.send(responder,data,0)
end
end
function sysCall_cleanup()
simZMQ.close(responder)
simZMQ.ctx_term(context)
end
WebSocket
The WebSocket plugin, offers several API functions allowing to interact with a web browser. Following is a simple echo server:
function onMessage(server,connection,data)
simWS.send(server,connection,data)
end
function sysCall_init()
server=simWS.start(9000)
simWS.setMessageHandler(server,'onMessage')
end
And following is a simple broadcaster:
function onOpen(server,connection)
clients[server]=clients[server] or {}
clients[server][connection]=1
end
function onClose(server,connection)
clients[server][connection]=nil
end
function broadcast(server,data)
for connection,_ in pairs(clients[server] or {}) do
simWS.send(server,connection,data)
end
end
function sysCall_init()
clients={}
server=simWS.start(9000)
simWS.setOpenHandler(server,'onOpen')
simWS.setCloseHandler(server,'onClose')
end
Serial port
CoppeliaSim implements several serial port API functions for Lua. With Python, use the Python serial port extension.
Sockets
CoppeliaSim ships with the LuaSocket extension library for Lua. Following illustrates how to fetch a webpage:
http=require('socket.http')
page=http.request('http://www.google.com')
With Python, use the socket module:
import socket
Other
Many other means of communication can be directly supported from within a script, via a Lua extension library or via a Python extension. Indirectly, by passing via a plugin, there are even more possibilities, since a plugin can virtually link to any type of c/c++ communication library.
|