示例#1
0
  // We received a ServiceInfo, and want to establish a connection
  void Session_Service::onServiceInfoResult(qi::Future<qi::ServiceInfo> result, long requestId, std::string protocol) {
    qiLogDebug() << "Got serviceinfo message";
    {
      boost::recursive_mutex::scoped_lock sl(_requestsMutex);
      ServiceRequest *sr = serviceRequest(requestId);
      if (!sr)
        return;
      if (result.hasError()) {
        sr->promise.setError(result.error());
        removeRequest(requestId);
        return;
      }
      const qi::ServiceInfo &si = result.value();
      sr->serviceId = si.serviceId();
      if (_sdClient->isLocal())
      { // Wait! If sd is local, we necessarily have an open socket
        // on which service was registered, whose lifetime is bound
        // to the service
        TransportSocketPtr s = _sdClient->_socketOfService(sr->serviceId);
        if (!s) // weird
          qiLogVerbose() << "_socketOfService returned 0";
        else
        {
          // check if the socket support that capability
          if (s->remoteCapability("ClientServerSocket", false))
          {
            qiLogVerbose() << "sd is local and service is capable, going through socketOfService";
            onTransportSocketResult(qi::Future<TransportSocketPtr>(s), requestId);
            return;
          }
        }
      }
      //empty serviceInfo
      if (!si.endpoints().size()) {
        std::stringstream ss;
        ss << "No endpoints returned for service:" << sr->name << " (id:" << sr->serviceId << ")";
        qiLogVerbose() << ss.str();
        sr->promise.setError(ss.str());
        removeRequest(requestId);
        return;
      }

      if (protocol != "")
      {
        std::vector<qi::Url>::const_iterator it = si.endpoints().begin();

        for (;
             it != si.endpoints().end() && it->protocol() != protocol;
             it++)
        {
          continue;
        }

        if (it == si.endpoints().end())
        {
          std::stringstream ss;
          ss << "No " << protocol << " endpoint available for service:" << sr->name << " (id:" << sr->serviceId << ")";
          qiLogVerbose() << ss.str();
          sr->promise.setError(ss.str());
        }
      }
    }
    qiLogDebug() << "Requesting socket from cache";
    qi::Future<qi::TransportSocketPtr> fut = _socketCache->socket(result.value(), protocol);
    fut.connect(&Session_Service::onTransportSocketResult, this, _1, requestId);
  }
示例#2
0
  qi::Future<AnyReference> RemoteObject::metaCall(AnyObject, unsigned int method, const qi::GenericFunctionParameters &in, MetaCallType callType, Signature returnSignature)
  {
    MetaMethod *mm = metaObject().method(method);
    if (!mm) {
      std::stringstream ss;
      ss << "Method " << method << " not found on service " << _service;
      return makeFutureError<AnyReference>(ss.str());
    }
    float canConvert = 1;
    if (returnSignature.isValid())
    {
      canConvert = mm->returnSignature().isConvertibleTo(returnSignature);
      qiLogDebug() << "return type conversion score: " << canConvert;
      if (canConvert == 0)
      {
        // last chance for dynamics and adventurous users
        canConvert = returnSignature.isConvertibleTo(mm->returnSignature());
        if (canConvert == 0)
          return makeFutureError<AnyReference>(
            "Call error: will not be able to convert return type from "
              + mm->returnSignature().toString()
              + " to " + returnSignature.toString());
        else
          qiLogVerbose() << "Return signature might be incorrect depending on the value, from "
            + mm->returnSignature().toString()
            + " to " + returnSignature.toString();
      }
    }
    /* The promise will be set:
     - From here in case of error
     - From a network callback, called asynchronously in thread pool
     So it is safe to use a sync promise.
     */
    qi::Promise<AnyReference> out(&PromiseNoop<AnyReference>, FutureCallbackType_Sync);
    qi::Message msg;
    TransportSocketPtr sock;
    // qiLogDebug() << this << " metacall " << msg.service() << " " << msg.function() <<" " << msg.id();
    {
      boost::mutex::scoped_lock lock(_socketMutex);
      boost::mutex::scoped_lock lock2(_promisesMutex);
      // Check socket while holding the lock to avoid a race with close()
      // where we would add a promise to the map after said map got cleared
      if (!_socket || !_socket->isConnected())
      {
        return makeFutureError<AnyReference>("Socket is not connected");
      }
      // The remote object can be concurrently closed / other operation that modifies _socket
      // (even set it to null). We store the current socket locally so that the behavior
      // of metacall stays consistent throughout the function's execution.
      sock = _socket;
      if (_promises.find(msg.id()) != _promises.end())
      {
        qiLogError() << "There is already a pending promise with id "
                                   << msg.id();
      }
      qiLogDebug() << "Adding promise id:" << msg.id();
      _promises[msg.id()] = out;
    }
    qi::Signature funcSig = mm->parametersSignature();
    try {
      msg.setValues(in, funcSig, this, sock.get());
    }
    catch(const std::exception& e)
    {
      qiLogVerbose() << "setValues exception: " << e.what();
      if (!sock->remoteCapability("MessageFlags", false))
        throw e;
      // Delegate conversion to the remote end.
      msg.addFlags(Message::TypeFlag_DynamicPayload);
      msg.setValues(in, "m", this, sock.get());
    }
    if (canConvert < 0.2)
    {
      msg.addFlags(Message::TypeFlag_ReturnType);
      msg.setValue(returnSignature.toString(), Signature("s"));
    }
    msg.setType(qi::Message::Type_Call);
    msg.setService(_service);
    msg.setObject(_object);
    msg.setFunction(method);

    //error will come back as a error message
    if (!sock->isConnected() || !sock->send(msg)) {
      qi::MetaMethod*   meth = metaObject().method(method);
      std::stringstream ss;
      if (meth) {
        ss << "Network error while sending data to method: '";
        ss << meth->toString();
        ss << "'.";
      } else {
        ss << "Network error while sending data an unknown method (id=" << method << ").";
      }
      if (!sock->isConnected()) {
        ss << " Socket is not connected.";
        qiLogVerbose() << ss.str();
      } else {
        qiLogError() << ss.str();
      }
      out.setError(ss.str());

      {
        boost::mutex::scoped_lock lock(_promisesMutex);
        qiLogDebug() << "Removing promise id:" << msg.id();
        _promises.erase(msg.id());
      }
    }
    else
      out.setOnCancel(qi::bind(&RemoteObject::onFutureCancelled, this, msg.id()));
    return out.future();
  }