Skip to content

How to write the API for a HOP service?

Konstantinos Panayiotou edited this page Jul 1, 2016 · 6 revisions

Currently, we support and maintain RAPP-API(s) for the following programming languages:

  • Python
  • JavaScript
  • C++

Under the rapp-api repository you can find more information regarding implemented and tested Rapp-Platform API calls.

As the integration tests are written in Python and uses the Python-Rapp-APi it is a good practice to start on the python-rapp-api.


Python RAPP-Platform-API Overview - Usage

API users are able to select from 2 API implementations:

  • High level API
  • Advanced API

The first one allow API users to easily call RAPP PLatform Services through simple function calls. The second one is for advanced usage, delivered for expert developers. This is an object-oriented implementation. As we will later describe, the advanced API usage allow creation of Cloud Messages. Both Platform requests and responses are described by static objects.

Note: The High Level API actually wraps the Advanced API.

Advanced API usage

RappPlatformService is the RAPP term for an established connection to the RAPP-Platform Services, over the www (World-Wide-Web). Each Platform Service has it's own unique Response and Request message.

The RappPlatformService class is used to establish connections to the RAPP-Platform Web-Services, while CloudMsg objects include:

  • Request object. RAPP-Platform Service specific Request message
  • Response object. RAPP-Platform Service specific Response message
from RappCloud import RappPlatformService

svcClient = RappPlatformService(persistent=True, timeout=30000)

By Default it connects to the localhost, assuming that the RAPP Platform has been setup on the local machine. The constructor of the RappPlatformService class allow to specify the RAPP Platform parameters to connect to.

from RappCloud import RappPlatformService

svcClient = RappPlatformService(address='RAPP_PLATFORM_IPv4_ADDRESS',
    port='RAPP_PLATFORM_PORT_NUMBER',
    protocol='http')

RappPlatformService object constructor allow to set:

  • persistent (Boolean): Force peristent connections. Defaults to True
  • timeout (Integer): Client timeout value. This is the timeout value waiting for a response from the RAPP Platform. Defaults to infinity
  • address (String): The RAPP Platform IPv4 address to connect to. Defaults to 'localhost'
  • port (String): The RAPP Platform listening port. Defaults to "9001"
  • protocol (String): The configured application protocol for the RAPP Platform. Valid values are "https" and "http". Defaults to "http"

The persistent and timeout properties of a RappPlatformService object are public members and can be set using the dot (.) notation:

svcClient = RappPlatformService()
svcClient.persistent = True
svcClient.timeout = 30000

CloudMsg objects are feed to the RappPlatformService object to specific RAPP-Platform Services. CloudMsg classes can be imported from the CloudMsgs submodule of the RappCloud module:

from RappCloud.CloudMsgs import FaceDetection

The above line of code is used as an example of importing the FaceDetection CloudMsg class.

A complete description on available CloudMsg classes as long as their Request and Response message classes is available here

CloudMsg objects hold a Request and a Response object:

from RappCloud.CloudMsgs import FaceDetection
faceDetectMsg = FaceDetection()

reqObj = faceDetectMsg.req
respObj = faceDetectMsg.resp

Request and Response objects of a CloudMsg can be serialized to a dictionary:

reqDict = faceDetectMsg.req.serialize()
print reqDict
  >> {fast: False, imageFilePath: ''}

respDict = faceDetectMsg.resp.serialize()
print respDict
  >> {faces: [], error: ''}

CloudMsg Request property values can be set through the req property of the CloudMsg object. or as keyword arguments to the constructor of a CloudMsg:

from RappCloud.CloudMsgs import FaceDetection

msg = FaceDetection(imageFilepath='/tmp/face-sample.png')
print msg.req.serialize()
  >> {fast: False, imageFilepath: '/tmp/face-sample.png'}

msg.req.fast = True
print msg.req.serialize()
  >> {fast: True, imageFilepath: '/tmp/face-sample.png'}

RappPlatfomrService objects have a .call() method for calling RAPP-Platform Services:

class RappPlatformService:
    ...

    def call(self, msg=None):
        ...
        return self.resp

    ...

The .call() method returns the Response object.

svcClient= RappPlatformService()
msg = FaceDetection()
msg.req.fast = True
msg.req.imageFilepath = '/tmp/face-sample.png'

response = svcClient.call(msg)
print response.faces
print response.error

CloudMsg objects are passed as argument to the .call() method of the RappPlatformService object:

svcClient= RappPlatformService()
msg = FaceDetection(imageFilepath='/tmp/face-sample.png')
response = svcClient.call(msg)

CloudMsg objects can also be passed to the constructor of the RappPlatformService class:

faceMsg = FaceDetection(imageFilepath='/tmp/face-sample.png')
svcClient= RappPlatformService(msg=faceMsg, timeout=15000)
response = svcClient.call()

Note: Calling several different RAPP-Platform Services is done by passing the service specific Cloud Message objects to the .call() of the RappPlatformService object.

The following example creates a FaceDetection and a QrDetection CloudMsg to call both the Face-Detection and Qr-Detection RAPP-Platform Services.

from RappCloud import RappPlatformService
from RappCloud.CloudMsgs import (
    FaceDetection,
    QrDetection)

svcClient = RappPlatformService(timeout=1000)
faceMsg = FaceDetection(fast=True, imageFilepath='/tmp/face-sample.png')
qrMsg = QrDetection()
qrMsg.req.imageFilepath = '/tmp/qr-sample.png'

fdResp = svcClient.call(faceMsg)
print "Found %s Faces" %len(fdResp.faces)

qrResp = svcClient.call(qrMsg)
print "Found %s QRs: %s" %(len(qrResp.qr_centers), qrResp.qr_messages)

High Level API usage

Like previously mentioned, API users can also use the High Level implementation of the RAPP Platform API. Benefits from using this implementation is lack of knowledge of how Cloud Messages and RappPlatformService are used. Calls to the RAPP Platform are done through simple function calls, under the RappPlatformAPI module.

Below is an example of performing a query to the ontologyi, hosted on the RAPP Platform, using the High Level API implementation:

from RappCloud import RappPlatformAPI
ch = RappPlatformAPI()

response = ch.ontologySubclasses("Oven")

print response
>> {'results': [u'http://knowrob.org/kb/knowrob.owl#MicrowaveOven', u'http://knowrob.org/kb/knowrob.owl#RegularOven', u'http://knowrob.org/kb/knowrob.owl#ToasterOven'], 'error': u''}

The RappPlatformAPI usage and calls are fully documented here, also with examples of usage.


Example - Implementing the FaceDetection API call.

Lets say we want to implement the FaceDetection Cloud Message.

The face detection RAPP Platform Web Service has a Request and Response object

Web-Service Request

  • fast (Boolean): If true, detection will take less time but it will be less accurate
  • file (File): Image file.

Web-Service Response

  • faces (Array): An array of the detected faces coordinates (point2D), on the image frame.
  • error (String): Error message.

Start by creating the python source file for the FaceDeteciton Cloud Message implementation. Head to the RappCloud/CloudMsgs directory of the RappCloud module and create a file named FaceDetection.py

Cloud Messages classes inherit from the CloudMsg class and Request and Response classes inherit from CloudRequest and CloudResponse classes respectively. So first import those classes and write the structure of the FaceDetection Cloud Message:

from Cloud import (
    CloudMsg,
    CloudRequest,
    CloudResponse)

class FaceDetection(CloudMsg):

    class Request(CloudRequest):
        def __init__(self, **kwargs):
            pass


    class Response(CloudResponse):
        def __init__(self, **kwargs):
            pass


    def __init__(self, **kwargs):
        pass

Add the appropriate properties to the Request and Response classes. Property names can differ from the Web-Service request and response property names. Mapping from implemented property names to actual request payload will be studied later on:

class Request(CloudRequest):

    def __init__(self, **kwargs):
        ## File path to the image file
        self.imageFilepath = ''
        ## If true, detection will take less time but it will be less accurate
        self.fast = False
        ## Apply keyword arguments to the Request object.
        super(FaceDetection.Request, self).__init__(**kwargs)


class Response(CloudResponse):

    def __init__(self, **kwargs):
        ## Error message
        self.error = ''
        ## Detected faces. Array of face objects.
        self.faces = []
        ## Apply keyword arguments to the Request object.
        super(FaceDetection.Response, self).__init__(**kwargs)

Notice calling CloudResponse and CloudRequest construcors.

Remember that a Request class must implement two member methods, make_payload() and make_files. For the payload it's the fast property and imageFilepath is a file.

from RappCloud.Objects import (
    Payload,
    File)


class Request(CloudRequest):

    def __init__(self, **kwargs):
        ## File path to the image file
        self.imageFilepath = ''
        ## If true, detection will take less time but it will be less accurate
        self.fast = False
        ## Apply keyword arguments to the Request object.
        super(FaceDetection.Request, self).__init__(**kwargs)

    def make_payload(self):
        return Payload(fast=self.fast)

    def make_files(self):
        return [File(filepath=self.path, postfield="file")]

Next, you have to instantiate a Request and Response objects for the FaceDetection class to hold:

class FaceDetection(CloudMsg):
    ...

    def __init__(self, **kwargs):
        # Create and hold the Request object for this CloudMsg
        self.req = FaceDetection.Request()
        # Create and hold the Response object for this CloudMsg
        self.resp = FaceDetection.Response()

Each RAPP Platform Web Service has a unique service name resolving to a url name/path. The service name for the Face Detection RAPP Platform Web Service is:

face_detection

CloudMsg constructor takes as input the service name:

class FaceDetection(CloudMsg):
    ...

    def __init__(self, **kwargs):
        # Create and hold the Request object for this CloudMsg
        self.req = FaceDetection.Request()
        # Create and hold the Response object for this CloudMsg
        self.resp = FaceDetection.Response()
        super(FaceDetection, self).__init__(svcname='face_detection', **kwargs)

Note: Dont forget to document the code using doxygen

Below is the complete FaceDetection.py file

from RappCloud.Objects import (
    File,
    Payload)

from Cloud import (
    CloudMsg,
    CloudRequest,
    CloudResponse)


  class FaceDetection(CloudMsg):
      """ Face Detection CloudMsg object"""

      class Request(CloudRequest):
          """ Face Detection Cloud Request object. FaceDetection.Request """
          def __init__(self, **kwargs):
              """!
              Constructor
              @param **kwargs - Keyword arguments. Apply values to the request attributes.
              - @ref imageFilepath
              - @ref fast
              """

              ## File path to the image to load. This is the image to perform
              # face-detection on.
              self.imageFilepath = ''
              ## If true, detection will take less time but it will be less
              # accurate
              self.fast = False
              # Apply passed keyword arguments to the Request object.
              super(FaceDetection.Request, self).__init__(**kwargs)


        def make_payload(self):
            """ Create and return the Payload of the Request. """
            return Payload(fast=self.fast)

        def make_files(self):
            """ Create and return Array of File objects of the Request. """
            return [File(self.imageFilepath, postfield='file')]

        class Response(CloudResponse):
            """ Face Detection Cloud Response object. FaceDetection.Response """
            def __init__(self, **kwargs):
                """!
                Constructor

                @param **kwargs - Keyword arguments. Apply values to the request attributes.
                - @ref error
                - @ref faces
                """
                ## Error message
                self.error = ''
                ## Detected faces. Array of face objects. TODO create face object.
                self.faces = []
                ## Apply passed keyword arguments to the Request object.
                super(FaceDetection.Response, self).__init__(**kwargs)


        def __init__(self, **kwargs):
            """!
            Constructor

            @param **kwargs - Keyword arguments. Apply values to the request attributes.
                - @ref Request.fast
                - @ref Request.imageFilepath
            """

            # Create and hold the Request object for this CloudMsg
            self.req = FaceDetection.Request()
            # Create and hold the Response object for this CloudMsg
            self.resp = FaceDetection.Response()
            super(FaceDetection, self).__init__(svcname='face_detection', **kwargs)

Finally append the following line of code in the RappCloud/CloudMsgs/__init__.py file:

from FaceDetection import FaceDetection

Now everything is in place to call the newly created Face detection RAPP Platform Service, using the python implementation of the rapp-platform-api. An example is presented below:

from RappCloud.CloudMsgs import FaceDetection
from RappCloud import RappPlatformService

svcClient = RappPlatformService(persistent=True, timeout=30000)
msg = FaceDetection(imageFilepath="PATH", fast=True)

response svcClient.call(msg)

if response.error:
    print "An error has occured: %s" %response.error
else:
    print response.faces

If you want to also include it in the High Level API implementation, you will have to modify the RappPlatformApi.py file.

First import the FaceDetection Cloud Message, that was previously implemented:

...

from CloudMsgs import FaceDetection

Next, we must implemend the faceDetection method for the RappPlatformAPI class. Input arguments to the method are:

  • imageFilepath: Path to the image file to perform face detection on.
  • fast: Force fast detection. If true, detection takes less time but it will be less accurate

The output is a python dict of the response fields:

  • faces: Detected faces
  • error: Error message, if one occures.

The faceDetection method implementation must be as presented below

...

class RappPlatformAPI():
    """ RAPP Platform simple API implementation """
    def __init__(self):
        self.svc_caller = RappPlatformService()

    ...

    def faceDetection(self, imageFilepath, fast = False):
        """! Face detection API service call.
        @type imageFilepath: string
        @param imageFilepath: Path to the image file.
        @type fast: bool
        @param fast: Perform fast detection. If true, detection will take
        less time but it will be less accurate.
        @rtype: dict
        @return: Returns a dictionary of the service call response.
        {'faces': [], 'error': ''}
        """
        msg = FaceDetection()
        try:
            msg.req.imageFilepath = imageFilepath
            response = self.svc_caller.call(msg)
        except Exception as e:
            response = FaceDetection.Response(error=str(e))
        return {
            'faces': response.faces,
            'error': response.error
        }

Note: Make sure to launch both the back-end and the, listening for requests, HOP Web Server, of the RAPP Platform, before executing the above example. Here you can find instructions on how to launch the RAPP Platform.

Clone this wiki locally