Messaging/interfaces/connectivity

There are several ways messages or data can be exchanged/transmitted/received in and around CoppeliaSim, but also between CoppeliaSim and an external application, other computer, machine, etc.:

One can exchange data via:

  • signals
  • custom data blocks
  • calling plugin functions
  • 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:

    #python # script 1 writes the data to string signal mySignalName: myData = [1, 2, ["Hello", "world", True, {"value1": 63, "value2": "aString"}]] sim.setStringSignal("mySignalName", sim.packTable(myData)) --lua -- 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))
    #python # script 2 reads the data from string signal mySignalName: myData = sim.getStringSignal("mySignalName") if myData != None: myData = sim.unpackTable(myData) --lua -- 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:

    #python # script 1 writes the data to the scene: myData = [1, 2, ["Hello", "world", True, {"value1": 63, "value2": "aString"}]] sim.writeCustomDataBlock(sim.handle_scene, "myTag", sim.packTable(myData)) --lua -- 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))
    #python # script 2 reads the data from the scene: myData = sim.readCustomDataBlock(sim.handle_scene, "myTag") if myData != None: myData = sim.unpackTable(myData) --lua -- 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(nullptr, "Not enough arguments or wrong arguments."); } // Somewhere in the plugin's initialization code: simRegisterScriptCallbackFunction("func", nullptr, myCallbackFunc);

    Calling script functions

    A script function can obviously be called from within the same script, but also:

  • across scripts (via sim.callScriptFunction or sim.getScriptFunctions)
  • 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:

    #python def sysCall_init(): sim = require('sim') self.scriptHandle = sim.getScriptInt32Param(sim.handle_self, sim.scriptintparam_handle) def sysCall_sensing(): message = {'id': 'greetingMessage', 'data': {'msg': 'Hello!'}} sim.broadcastMsg(message) def sysCall_msg(msg, origin): if origin != self.scriptHandle and msg.id == 'greetingMessage': print("Received following message from script {}:".format(origin)) print(msg['data']['msg']) --lua function sysCall_init() sim = require('sim') 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. When using Python, one is of course also free to using the pyzmq package for the ZeroMQ functionality. Following illustrates a simple requester:

    #python def sysCall_thread(): sim = require('sim') simZMQ = require('simZMQ') # suppose we do not use the pyzmq package print('Connecting to hello world server...') self.context = simZMQ.ctx_new() self.requester = simZMQ.socket(self.context, simZMQ.REQ) simZMQ.connect(self.requester, 'tcp://localhost:5555') for request_nbr in range(11): print('-----------------------------------------') data = 'Hello' print(f'[requester] Sending "{data}"...') simZMQ.send(self.requester, data, 0) rc, data = simZMQ.recv(self.requester, 0) print(f'[requester] Received "{data}"') def sysCall_cleanup(): simZMQ.close(self.requester) simZMQ.ctx_term(self.context) --lua function sysCall_thread() sim = require('sim') simZMQ = require('simZMQ') print('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:

    #python def sysCall_thread(): sim = require('sim') simZMQ = require('simZMQ') # suppose we do not use the pyzmq package self.context = simZMQ.ctx_new() self.responder = simZMQ.socket(self.context, simZMQ.REP) rc = simZMQ.bind(self.responder, 'tcp://*:5555') if rc != 0: raise Exception('failed bind') while True: rc, data = simZMQ.recv(self.responder, 0) print(f'[responder] Received "{data}"') data = 'World' print(f'[responder] Sending "{data}"...') simZMQ.send(self.responder, data, 0) def sysCall_cleanup(): simZMQ.close(self.responder) simZMQ.ctx_term(self.context) --lua function sysCall_thread() sim = require('sim') simZMQ = require('simZMQ') 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. When using Python, one is of course also free to using a Python specific WS package. Following is a simple echo server:

    #python def onMessage(server, connection, data): simWS.send(server, connection, data) def sysCall_init(): simWS = require('simWS') # suppose one is not using any specific Python package related to WS server = simWS.start(9000) simWS.setMessageHandler(server, 'onMessage') --lua function onMessage(server, connection, data) simWS.send(server, connection, data) end function sysCall_init() simWS = require('simWS') server = simWS.start(9000) simWS.setMessageHandler(server, 'onMessage') end

    And following is a simple broadcaster:

    #python def onOpen(server, connection): if server not in clients: clients[server] = {} clients[server][connection] = 1 def onClose(server, connection): del clients[server][connection] def broadcast(server, data): for connection in clients.get(server, {}): simWS.send(server, connection, data) def sysCall_init(): simWS = require('simWS') # suppose one is not using any specific Python package related to WS clients = {} server = simWS.start(9000) simWS.setOpenHandler(server, onOpen) simWS.setCloseHandler(server, onClose) --lua 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() simWS = require('simWS') 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 package.


    Sockets

    CoppeliaSim ships with the LuaSocket extension library for Lua, while several packages are available for Python. Following illustrates how to fetch a webpage:

    #python import requests response = requests.get('http://www.google.com') page = response.text --lua http = require('socket.http') page = http.request('http://www.google.com')

    Other

    Many other means of communication can be directly supported from within a script, via a Lua extension library or via a Python package. 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.