docs: def call_remote_api(self, func_name, *args, **kwargs):
          """ Calls any remote API func in a thread_safe way.
  
          :param str func_name: name of the remote API func to call
          :param args: args to pass to the remote API call
          :param kwargs: args to pass to the remote API call
  
          .. note:: You can add an extra keyword to specify if you want to use the streaming or sending mode. The oneshot_wait mode is used by default (see `here <http://www.coppeliarobotics.com/helpFiles/en/remoteApiConstants.htm#operationModes>`_ for details about possible modes).
  
          .. warning:: You should not pass the clientId and the operationMode as arguments. They will be automatically added.
  
          As an example you can retrieve all joints name using the following call::
  
              vrep_io.remote_api_call('simxGetObjectGroupData',
                                      vrep_io.remote_api.sim_object_joint_type,
                                      0,
                                      streaming=True)
  
          """
          f = getattr(remote_api, func_name)
  
          mode = self._extract_mode(kwargs)
          kwargs['operationMode'] = vrep_mode[mode]
          # hard_retry = True
  
          if '_force' in kwargs:
              del kwargs['_force']
              _force = True
          else:
              _force = False
  
          for _ in range(VrepIO.MAX_ITER):
              with self._lock:
                  ret = f(self.client_id, *args, **kwargs)
  
              if _force:
                  return
  
              if mode == 'sending' or isinstance(ret, int):
                  err, res = ret, None
              else:
                  err, res = ret[0], ret[1:]
                  res = res[0] if len(res) == 1 else res
  
              err = [bool((err >> i) & 1) for i in range(len(vrep_error))]
  
              if remote_api.simx_return_novalue_flag not in err:
                  break
  
              time.sleep(VrepIO.TIMEOUT)
  
          # if any(err) and hard_retry:
          #     print "HARD RETRY"
          # self.stop_simulation() #nope
          #
          #     notconnected = True
          #     while notconnected:
          #         self.close()
          #         close_all_connections()
          #         time.sleep(0.5)
          #         try:
          #             self.open_io()
          #             notconnected = False
          #         except:
          #             print 'CONNECTION ERROR'
          #             pass
          #
          #     self.start_simulation()
          #
          #     with self._lock:
          #         ret = f(self.client_id, *args, **kwargs)
          #
          #         if mode == 'sending' or isinstance(ret, int):
          #             err, res = ret, None
          #         else:
          #             err, res = ret[0], ret[1:]
          #             res = res[0] if len(res) == 1 else res
          #
          #         err = [bool((err >> i) & 1) for i in range(len(vrep_error))]
          #
          #         return res
  
          if any(err):
              msg = ' '.join([vrep_error[2 ** i]
                              for i, e in enumerate(err) if e])
              raise VrepIOErrors(msg)
  
          return res