mirror of
https://github.com/OpenMTC/OpenMTC.git
synced 2024-12-22 14:22:30 +00:00
686 lines
23 KiB
Markdown
686 lines
23 KiB
Markdown
|
# SDK - The low-level CSE Client
|
||
|
|
||
|
|
||
|
## Introduction
|
||
|
|
||
|
The OpenMTC SDK offers a client module for low-level access to the
|
||
|
oneM2M resource tree exposed by a CSE's reference point. Currently,
|
||
|
only the http and https protocols are supported.
|
||
|
|
||
|
Basically, there are different types of Common Service Entities (CSE):
|
||
|
|
||
|
* MN-CSE: Middle Node CSE (OpenMTC Gateway)
|
||
|
* IN-CSE: Infrastructure Node CSE (OpenMTC Backend)
|
||
|
|
||
|
The client module comprises classes for representing requests,
|
||
|
responses as well as classes that provide an abstraction for a
|
||
|
connection to a CSE's reference point (the actual client itself).
|
||
|
|
||
|
|
||
|
## Requests
|
||
|
|
||
|
Requests to a CSE are called a OneM2MRequest. The OpenMTC SDK provides
|
||
|
this class for representing the different types of requests that can
|
||
|
be issued towards a CSE. This class resides under the
|
||
|
`openmtc_onem2m.transport` package. The following requests
|
||
|
(OneM2MOperation) are available:
|
||
|
|
||
|
* `retrieve`
|
||
|
* `delete`
|
||
|
* `create`
|
||
|
* `notify`
|
||
|
* `update`
|
||
|
|
||
|
|
||
|
### OneM2MRequest - Retrieve
|
||
|
|
||
|
The most trivial case of a `OneM2MRequest` is the `retrieve`. It takes
|
||
|
the path of the resource to be retrieved as parameter upon
|
||
|
construction.
|
||
|
|
||
|
This file can be found [here](./training/onem2m-examples/onem2m-example-3.py).
|
||
|
``` py
|
||
|
# Example 3: Retrieve OneM2MRequest
|
||
|
|
||
|
from openmtc_onem2m.transport import OneM2MRequest
|
||
|
|
||
|
request = OneM2MRequest("retrieve", to="onem2m")
|
||
|
|
||
|
print request.to
|
||
|
#>>> onem2m
|
||
|
```
|
||
|
|
||
|
|
||
|
### OneM2MRequest - Delete
|
||
|
|
||
|
Like the `retrieve` `OneM2MRequest`, a `delete` `OneM2MRequest` merely
|
||
|
takes the path of the resource to be deleted as parameter upon
|
||
|
construction.
|
||
|
|
||
|
This file can be found [here](./training/onem2m-examples/onem2m-example-4.py).
|
||
|
``` py
|
||
|
# Example 4: Delete OneM2MRequest
|
||
|
|
||
|
from openmtc_onem2m.transport import OneM2MRequest
|
||
|
|
||
|
request = OneM2MRequest("delete", to="onem2m")
|
||
|
|
||
|
print request.to
|
||
|
#>>> onem2m
|
||
|
```
|
||
|
|
||
|
|
||
|
### OneM2MRequest - Create
|
||
|
|
||
|
When creating a `create` `OneM2MRequest` object we need to specify the
|
||
|
object to be created together with the path where it is to be
|
||
|
created. In most cases this is done by creating an appropriate
|
||
|
resource object and passing it.
|
||
|
|
||
|
This file can be found [here](./training/onem2m-examples/onem2m-example-5a.py).
|
||
|
``` py
|
||
|
# Example 5a: Create OneM2MRequest
|
||
|
|
||
|
from openmtc_onem2m.transport import OneM2MRequest
|
||
|
from openmtc_onem2m.model import AE
|
||
|
|
||
|
my_app = AE(App_ID="myApp")
|
||
|
|
||
|
request = OneM2MRequest("create", to="onem2m", pc="my_app")
|
||
|
|
||
|
print request.to
|
||
|
#>>> onem2m
|
||
|
print request.pc
|
||
|
#>>> myApp
|
||
|
```
|
||
|
|
||
|
When creating contentInstances, we can also pass in a string of raw
|
||
|
data. In this case, we also need to specify the mime-type of the data
|
||
|
via the `resource_type` parameter (ty).
|
||
|
|
||
|
This file can be found [here](./training/onem2m-examples/onem2m-example-5b.py).
|
||
|
``` py
|
||
|
# Example 5b: Create OneM2MRequest with data
|
||
|
|
||
|
from openmtc_onem2m.transport import OneM2MRequest
|
||
|
import json
|
||
|
|
||
|
sensor_data = {"type": "temperature",
|
||
|
"value": 15 }
|
||
|
|
||
|
data_string = json.dumps(sensor_data)
|
||
|
|
||
|
request = OneM2MRequest("create",
|
||
|
to="onem2m",
|
||
|
pc=data_string,
|
||
|
ty="application/json")
|
||
|
|
||
|
print request.to
|
||
|
#>>> onem2m
|
||
|
print request.pc
|
||
|
#>>> {"type": "temperature", "value": 15}
|
||
|
```
|
||
|
|
||
|
|
||
|
### OneM2MRequest - Notify
|
||
|
|
||
|
For `notify` `OneM2MRequest` objects the same semantics as for
|
||
|
`create` `OneM2MRequest` apply.
|
||
|
|
||
|
This file can be found [here](./training/onem2m-examples/onem2m-example-6a.py).
|
||
|
``` py
|
||
|
# Example 6a: Notify OneM2MRequest
|
||
|
|
||
|
from openmtc_onem2m.transport import OneM2MRequest
|
||
|
from openmtc_onem2m.model import AE
|
||
|
|
||
|
my_app = AE(App_ID="myApp")
|
||
|
|
||
|
request = OneM2MRequest("notify", to="onem2m", pc=my_app)
|
||
|
|
||
|
print request.to
|
||
|
#>>> onem2m
|
||
|
print request.pc.App_ID
|
||
|
#>>> myApp
|
||
|
```
|
||
|
|
||
|
This file can be found [here](./training/onem2m-examples/onem2m-example-6b.py).
|
||
|
``` py
|
||
|
# Example 6b: Notify OneM2MRequest with data
|
||
|
|
||
|
from openmtc_onem2m.transport import OneM2MRequest
|
||
|
import json
|
||
|
|
||
|
sensor_data = {"type": "temperature",
|
||
|
"value": 15 }
|
||
|
|
||
|
data_string = json.dumps(sensor_data)
|
||
|
|
||
|
request = OneM2MRequest("create",
|
||
|
to="onem2m",
|
||
|
pc=data_string,
|
||
|
ty="application/json")
|
||
|
|
||
|
print request.to
|
||
|
#>>> onem2m
|
||
|
print request.pc
|
||
|
#>>> {"type": "temperature", "value": 15}
|
||
|
```
|
||
|
|
||
|
|
||
|
### OneM2MRequest - Update
|
||
|
|
||
|
The `update` `OneM2MRequest` can be used to update specific attributes
|
||
|
of an object (AE). If the request is legal, four different cases are
|
||
|
distinguished:
|
||
|
|
||
|
* If an attribute value **is provided** in the `OneM2MRequest` that
|
||
|
**exists** in the target resource, the CSE will simply update that
|
||
|
attribute in the resource representation.
|
||
|
* If an attribute **is not provided** in the `OneM2MRequest`, but the
|
||
|
attribute **exists** in the target resource, the hosting CSE will
|
||
|
simply leave the value of that attribute unchanged.
|
||
|
* If an attribute **is provided** in the `OneM2MRequest` and does
|
||
|
**not exist** in the target resource, the hosting CSE will create
|
||
|
such attribute with the provided value.
|
||
|
* If an attribute is **set to NULL** in the `OneM2MRequest` and
|
||
|
**exists** in the target resource, the hosting CSE will delete such
|
||
|
attribute if the deletion of the attribute is allowed by the local
|
||
|
policy.
|
||
|
|
||
|
The following example shows the creation of a `update`
|
||
|
`OneM2MRequest`. The CSE would either update the attribute (labels) in
|
||
|
the resource representation if it exists already exists there, or
|
||
|
create the attribute labels with the provided value if it does not
|
||
|
exist in the CSE resource representation yet.
|
||
|
|
||
|
This file can be found [here](./training/onem2m-examples/onem2m-example-7.py).
|
||
|
``` py
|
||
|
# Example 7: Update OneM2MRequest
|
||
|
|
||
|
from openmtc_onem2m.transport import OneM2MRequest
|
||
|
from openmtc_onem2m.model import AE
|
||
|
|
||
|
my_app = AE(App_ID="myApp", labels=["keyword1", "keyword2"])
|
||
|
|
||
|
request = OneM2MRequest("update", to="onem2m", pc=my_app.labels)
|
||
|
|
||
|
print request.to
|
||
|
#>>> onem2m
|
||
|
print request.pc
|
||
|
#>>> [u'keyword1', u'keyword2']
|
||
|
```
|
||
|
|
||
|
|
||
|
## Responses
|
||
|
|
||
|
Upon servicing a request, a CSE will return a `OneM2MResponse`, which
|
||
|
is a class of the client module. This class is defined in the
|
||
|
`openmtc_onem2m.transport` module and derives from the `object` base
|
||
|
class. The following response types are possible:
|
||
|
|
||
|
* `Create`
|
||
|
* `Retrieve`
|
||
|
* `Update`
|
||
|
* `Delete`
|
||
|
* `Notify`
|
||
|
* `Execute`
|
||
|
* `Observe`
|
||
|
|
||
|
An `OneM2MResponse` has the following properties:
|
||
|
|
||
|
* `status_code` - Denotes the result status of the operation (see
|
||
|
below).
|
||
|
* `request` - The type of the operation. One of (`create`, `retrieve`,
|
||
|
`update`, `delete`, `notify`, `execute`, `observe`).
|
||
|
* `rqi` - Denotes the request identifier (`requestIdentifier`).
|
||
|
* `pc` - Denotes the resource content (`primitiveContent`).
|
||
|
* `to` - Denotes to destination of the response.
|
||
|
|
||
|
|
||
|
### Error Responses
|
||
|
|
||
|
If an error occurs on the CSE servicing the request, the CSE will
|
||
|
return a `OneM2MErrorResponse`. Note that the `OneM2MErrorResponse`
|
||
|
class is an `Exception`. In case that any error is reported by the CSE
|
||
|
during processing a request, the client will *raise* an instance of
|
||
|
`OneM2MErrorResponse`. The `OneM2MErrorResponse` heritates from the
|
||
|
classes `OneM2MResponse` and `OneM2MError` (`OpenMTCError`) and is not
|
||
|
yet implemented (pass).
|
||
|
|
||
|
|
||
|
### Status Codes
|
||
|
|
||
|
The `status_code` of `OneM2MResponse` objects are defined as constants
|
||
|
in the `openmtc_onem2m.exc` module. The following constants are
|
||
|
defined:
|
||
|
|
||
|
| ``STATUS`` | numeric_code | http_status_code |
|
||
|
|:-----------|:------------:|:----------------:|
|
||
|
| ``STATUS_ACCEPTED`` | 1000 | 202 |
|
||
|
| ``STATUS_OK`` | 2000 | 200 |
|
||
|
| ``STATUS_CREATED`` | 2001 | 201 |
|
||
|
| ``STATUS_BAD_REQUEST`` | 4000 | 400 |
|
||
|
| ``STATUS_NOT_FOUND`` | 4004 | 404 |
|
||
|
| ``STATUS_OPERATION_NOT_ALLOWED`` | 4005 | 405 |
|
||
|
| ``STATUS_REQUEST_TIMEOUT`` | 4008 | 408 |
|
||
|
| ``STATUS_SUBSCRIPTION_CREATOR_HAS_NO_PRIVILEGE`` | 4101 | 403 |
|
||
|
| ``STATUS_CONTENTS_UNACCEPTABLE`` | 4102 | 400 |
|
||
|
| ``STATUS_ACCESS_DENIED`` | 4103 | 403 |
|
||
|
| ``STATUS_GROUP_REQUEST_IDENTIFIER_EXISTS`` | 4104 | 409 |
|
||
|
| ``STATUS_CONFLICT`` | 4015 | 409 |
|
||
|
| ``STATUS_INTERNAL_SERVER_ERROR`` | 5000 | 500 |
|
||
|
| ``STATUS_NOT_IMPLEMENTED`` | 5001 | 501 |
|
||
|
| ``STATUS_TARGET_NOT_REACHABLE`` | 5103 | 404 |
|
||
|
| ``STATUS_NO_PRIVILEGE`` | 5105 | 403 |
|
||
|
| ``STATUS_ALREADY_EXISTS`` | 5106 | 403 |
|
||
|
| ``STATUS_TARGET_NOT_SUBSCRIBABLE`` | 5203 | 403 |
|
||
|
| ``STATUS_SUBSCRIPTION_VERIFICATION_INITIATION_FAILED`` | 5204 | 500 |
|
||
|
| ``STATUS_SUBSCRIPTION_HOST_HAS_NO_PRIVILEGE`` | 5205 | 403 |
|
||
|
| ``STATUS_NON_BLOCKING_REQUEST_NOT_SUPPORTED`` | 5206 | 501 |
|
||
|
| ``STATUS_EXTERNAL_OBJECT_NOT_REACHABLE`` | 6003 | 404 |
|
||
|
| ``STATUS_EXTERNAL_OBJECT_NOT_FOUND`` | 6005 | 404 |
|
||
|
| ``STATUS_MAX_NUMBER_OF_MEMBER_EXCEEDED`` | 6010 | 400 |
|
||
|
| ``STATUS_MEMBER_TYPE_INCONSISTENT`` | 6011 | 400 |
|
||
|
| ``STATUS_MANAGEMENT_SESSION_CANNOT_BE_ESTABLISHED`` | 6020 | 500 |
|
||
|
| ``STATUS_MANAGEMENT_SESSION_ESTABLISHMENT_TIMEOUT`` | 6021 | 500 |
|
||
|
| ``STATUS_INVALID_CMDTYPE`` | 6022 | 400 |
|
||
|
| ``STATUS_INVALID_ARGUMENTS`` | 6023 | 400 |
|
||
|
| ``STATUS_INSUFFICIENT_ARGUMENT`` | 6024 | 400 |
|
||
|
| ``STATUS_MGMT_CONVERSION_ERROR`` | 6025 | 500 |
|
||
|
| ``STATUS_CANCELLATION_FAILED`` | 6026 | 500 |
|
||
|
| ``STATUS_ALREADY_COMPLETE`` | 6028 | 400 |
|
||
|
| ``STATUS_COMMAND_NOT_CANCELLABLE`` | 6029 | 400 |
|
||
|
|
||
|
|
||
|
## Exceptions
|
||
|
|
||
|
In addition to raising an instance of `OneM2MErrorResponse`, the CSE
|
||
|
client might also inidcate error conditions that do not occur while
|
||
|
the CSE was processing the request. This will mainly happen when the
|
||
|
client was unable to contact the CSE for whatever reason.
|
||
|
|
||
|
Exeptions that are raised will be subclasses of the `OpenMTCError`
|
||
|
class defined in the `openmtc.exc` module.
|
||
|
|
||
|
|
||
|
## Using the Client
|
||
|
|
||
|
The client implementation for interfacing with the HTTP interface of
|
||
|
an CSE resides in the `openmtc_onem2m.client.http` module. The
|
||
|
implementing class is called `OneM2MHTTPClient`. In the current
|
||
|
version of the SDK, we simply import the class directly. This is
|
||
|
planned to be replaced with a more sophisticated factory pattern that
|
||
|
creates appropriate clients based on the transport scheme (e.g. `http`
|
||
|
or `mqtt`) that is used.
|
||
|
|
||
|
Client objects expose a method called `send_onem2m_request` for
|
||
|
sending `OneM2MRequest` objects to a CSE.
|
||
|
|
||
|
|
||
|
### Creating a Client
|
||
|
|
||
|
To create a client object, we simply import the `OneM2MHTTPClient`
|
||
|
class from the `openmtc_onem2m.client.http` module and create an
|
||
|
instance of it with the URI of a reference point of an oneM2M CSE.
|
||
|
|
||
|
This file can be found [here](./training/onem2m-examples/onem2m-example-8a.py).
|
||
|
``` py
|
||
|
# Example 8a: Creating a Client
|
||
|
|
||
|
from openmtc_onem2m.client.http import OneM2MHTTPClient
|
||
|
|
||
|
# create a OneM2MHTTPClient object
|
||
|
client = OneM2MHTTPClient("http://localhost:8000", False)
|
||
|
```
|
||
|
|
||
|
|
||
|
### Making Requests
|
||
|
|
||
|
To retrieve a resource from the CSE's resource tree, we can use the
|
||
|
`send_onem2m_request` method and pass an appropriate `OneM2MRequest`
|
||
|
object. In this case we retrieve the `CSEBase` resource of the CSE's
|
||
|
resource tree. If successful, the operation returns a promise, which
|
||
|
contains an `OneM2MResponse` object. The `OneM2MResponse` can be
|
||
|
obtained from the promise by using `.get()`. The `content` property of
|
||
|
the `OneM2MResponse` holds the appropriate `CSEBase` object.
|
||
|
|
||
|
This file can be found [here](./training/onem2m-examples/onem2m-example-8b.py).
|
||
|
``` py
|
||
|
# Example 8b: Making Requests
|
||
|
|
||
|
from openmtc_onem2m.client.http import OneM2MHTTPClient
|
||
|
from openmtc_onem2m.transport import OneM2MRequest
|
||
|
|
||
|
# create a OneM2MHTTPClient object
|
||
|
client = OneM2MHTTPClient("http://localhost:8000", False)
|
||
|
|
||
|
# create a OneM2MRequest object
|
||
|
onem2m_request = OneM2MRequest("retrieve", to="onem2m")
|
||
|
# send the OneM2MRequest to the CSE
|
||
|
promise = client.send_onem2m_request(onem2m_request)
|
||
|
# reteive the OneM2MResponse from the returned promise
|
||
|
onem2m_response = promise.get()
|
||
|
|
||
|
print onem2m_response.to
|
||
|
#>>> onem2m
|
||
|
print onem2m_response.response_status_code
|
||
|
#>>> STATUS(numeric_code=2000, description='OK', http_status_code=200)
|
||
|
print onem2m_response.content
|
||
|
#>>> CSEBase(path='None', id='cb0')
|
||
|
```
|
||
|
|
||
|
**Note:** This example (and most of the following ones) will only work
|
||
|
as shown, if a `gateway` instance is running in the background of the
|
||
|
localhost. This can be launched by running the
|
||
|
`openmtc-open-source/openmtc-gevent/run_gateway` script.
|
||
|
|
||
|
To create a resource on the CSE, we first create the desired resource
|
||
|
object and then send a create `OneM2MRequest`.
|
||
|
|
||
|
In the following example, we will add the optional pararmeter
|
||
|
`resourceName="MYAPP"` to the creation of the AE in order to
|
||
|
facilitate the retrieval of this AE in the browser. After execution of
|
||
|
the example (and the condition to have running CSE on the localhost)
|
||
|
the created AE on the CSE should be retrievable at URL
|
||
|
`http://localhost:8000/onem2m/MYAPP` in a browser on the
|
||
|
localhost. Further, we add the mandatory parameter
|
||
|
`requestReachability=False` which states, that the created AE should
|
||
|
have no server capability and therefore no reachability for other
|
||
|
instances.
|
||
|
|
||
|
For a `create` `OneM2MRequest`, there are two additional parameters:
|
||
|
`ty=AE` indicates that the resource that should be created on the CSE
|
||
|
is of type AE (ApplicationEntity). The statement `pc=my_app` specifies
|
||
|
what resource should be created on the CSE. In this case, it is the AE
|
||
|
created previously.
|
||
|
|
||
|
This file can be found [here](./training/onem2m-examples/onem2m-example-10.py).
|
||
|
``` py
|
||
|
# Example 10: Create a resource
|
||
|
|
||
|
from openmtc_onem2m.model import AE
|
||
|
from openmtc_onem2m.client.http import OneM2MHTTPClient
|
||
|
from openmtc_onem2m.transport import OneM2MRequest
|
||
|
|
||
|
# create a OneM2MHTTPClient object
|
||
|
client = OneM2MHTTPClient("http://localhost:8000", False)
|
||
|
|
||
|
# create a resource to be created on the CSE
|
||
|
# resourceName: (optional) for easy check in browser
|
||
|
# requestReachability: (mandatory) for servercapability of the AE
|
||
|
my_app = AE(App_ID="myApp",
|
||
|
labels=["keyword1", "keyword2"],
|
||
|
resourceName="MYAPP",
|
||
|
requestReachability=False)
|
||
|
|
||
|
# create a OneM2MRequest object of type 'create'
|
||
|
# ty: resource_type of the created resource
|
||
|
# pc: Resource content to be transferred
|
||
|
onem2m_request = OneM2MRequest("create", to="onem2m", ty=AE, pc=my_app)
|
||
|
|
||
|
# send the 'create' OneM2MRequest to the CSE
|
||
|
promise = client.send_onem2m_request(onem2m_request)
|
||
|
|
||
|
# reteive the OneM2MResponse from the returned promise
|
||
|
onem2m_response = promise.get()
|
||
|
|
||
|
print onem2m_response.to
|
||
|
#>>> onem2m
|
||
|
print onem2m_response.response_status_code
|
||
|
#>>> STATUS(numeric_code=2001, description='CREATED', http_status_code=201)
|
||
|
print onem2m_response.content
|
||
|
#>>> AE(path='None', id='ae0')
|
||
|
print onem2m_response.content.App_ID
|
||
|
#>>> myApp
|
||
|
print onem2m_response.content.labels
|
||
|
#>>> [u'keyword1', u'keyword2']
|
||
|
```
|
||
|
|
||
|
**Note:** If this example throws a `OneM2MErrorResponse` with
|
||
|
`response_status_code: STATUS(numeric_code=4015,
|
||
|
description='CONFLICT', http_status_code=409)`, then the
|
||
|
`resourceName` might already be registered at the CSE. Try to alter
|
||
|
the `resourceName`. ResourceNames need to be unique on the
|
||
|
CSE. Alternatively, the running CSE process can be terminated and
|
||
|
restarted. This avoids the need to change the `resourceName`.
|
||
|
|
||
|
**Note:** At this point the application object has been created in the
|
||
|
CSE's resource tree. However, the original object we created in our
|
||
|
program (`my_application`) has not been altered in any
|
||
|
way. Specifically, it does not contain any attributes that may have
|
||
|
been set or altered by the CSE, nor has its `path` property been set.
|
||
|
|
||
|
If we want to continue working with the application object it is good
|
||
|
practice to retrieve the object again through the resourceName.
|
||
|
|
||
|
This file can be found [here](./training/onem2m-examples/onem2m-example-11a.py).
|
||
|
``` py
|
||
|
# Example 11a: Create a resource (continued)
|
||
|
|
||
|
from openmtc_onem2m.model import AE
|
||
|
from openmtc_onem2m.client.http import OneM2MHTTPClient
|
||
|
from openmtc_onem2m.transport import OneM2MRequest
|
||
|
|
||
|
client = OneM2MHTTPClient("http://localhost:8000", False)
|
||
|
|
||
|
my_app = AE(App_ID="myApp",
|
||
|
labels=["keyword1", "keyword2"],
|
||
|
resourceName="MYAPP1",
|
||
|
requestReachability=False)
|
||
|
|
||
|
onem2m_request = OneM2MRequest("create", to="onem2m", ty=AE, pc=my_app)
|
||
|
|
||
|
promise = client.send_onem2m_request(onem2m_request)
|
||
|
|
||
|
onem2m_response = promise.get()
|
||
|
|
||
|
print onem2m_response.response_status_code
|
||
|
#>>> STATUS(numeric_code=2001, description='CREATED', http_status_code=201)
|
||
|
|
||
|
# Build path to retieve from
|
||
|
path = "onem2m/" + onem2m_response.content.resourceName
|
||
|
print path
|
||
|
#>>> onem2m/MYAPP
|
||
|
|
||
|
# Retrieve the AE from the CSE
|
||
|
onem2m_request = OneM2MRequest("retrieve", to=path)
|
||
|
promise = client.send_onem2m_request(onem2m_request)
|
||
|
onem2m_response = promise.get()
|
||
|
|
||
|
print onem2m_response.response_status_code
|
||
|
#>>> STATUS(numeric_code=2000, description='OK', http_status_code=200)
|
||
|
print onem2m_response.content
|
||
|
#>>> AE(path='None', id='ae0')
|
||
|
|
||
|
# Set the local AE to the retrieved content
|
||
|
my_app = None
|
||
|
my_app = onem2m_response.content
|
||
|
|
||
|
print my_app.App_ID
|
||
|
#>>> myApp
|
||
|
print my_app.resourceName
|
||
|
#>>> MYAPP
|
||
|
print my_app.labels
|
||
|
#>>> [u'keyword1', u'keyword2']
|
||
|
```
|
||
|
|
||
|
**Note:** Again, if this example throws a `OneM2MErrorResponse` with
|
||
|
`response_status_code: STATUS(numeric_code=4015,
|
||
|
description='CONFLICT', http_status_code=409)`, then the
|
||
|
`resourceName` might already be registered at the CSE. Try to alter
|
||
|
the `resourceName`. Alternatively, the running CSE process can be
|
||
|
terminated and restarted. This avoids the need to change the
|
||
|
`resourceName`.
|
||
|
|
||
|
The following example showcases how to update some fields using
|
||
|
`OneM2MRequest` `update`.
|
||
|
|
||
|
This file can be found [here](./training/onem2m-examples/onem2m-example-11b.py).
|
||
|
``` py
|
||
|
# Example 11b: Updating a resource using OneM2MRequest Update
|
||
|
|
||
|
from openmtc_onem2m.model import AE
|
||
|
from openmtc_onem2m.client.http import OneM2MHTTPClient
|
||
|
from openmtc_onem2m.transport import OneM2MRequest
|
||
|
|
||
|
client = OneM2MHTTPClient("http://localhost:8000", False)
|
||
|
|
||
|
my_app = AE(App_ID="myApp",
|
||
|
labels=["keyword1", "keyword2"],
|
||
|
resourceName="MYAPP2",
|
||
|
requestReachability=False)
|
||
|
|
||
|
# Create the AE 'my_app' at the CSE
|
||
|
onem2m_request = OneM2MRequest("create", to="onem2m", ty=AE, pc=my_app)
|
||
|
promise = client.send_onem2m_request(onem2m_request)
|
||
|
onem2m_response = promise.get()
|
||
|
print onem2m_response.content.labels
|
||
|
#>>> [u'keyword1', u'keyword2']
|
||
|
|
||
|
# Retrieve the AE from the CSE and check the labels
|
||
|
path = "onem2m/" + onem2m_response.content.resourceName
|
||
|
onem2m_request = OneM2MRequest("retrieve", to=path)
|
||
|
promise = client.send_onem2m_request(onem2m_request)
|
||
|
onem2m_response = promise.get()
|
||
|
print onem2m_response.content.labels
|
||
|
#>>> [u'keyword1', u'keyword2']
|
||
|
|
||
|
# Update the changes labels in the remote resource
|
||
|
# Therefore a temporay AE object is needed
|
||
|
# This temporary AE object should ONLY contian the fields that need to be updated
|
||
|
tmp_app = AE(labels=["foo", "bar", "coffee"])
|
||
|
onem2m_request = OneM2MRequest("update", to=path, pc=tmp_app)
|
||
|
promise = client.send_onem2m_request(onem2m_request)
|
||
|
onem2m_response = promise.get()
|
||
|
print onem2m_response.content.labels
|
||
|
#>>> [u'foo', u'bar', u'coffee']
|
||
|
|
||
|
# Set the local AE to the retrieved content
|
||
|
my_app = None
|
||
|
my_app = onem2m_response.content
|
||
|
print my_app.labels
|
||
|
#>>> [u'foo', u'bar', u'coffee']
|
||
|
```
|
||
|
|
||
|
|
||
|
### Error Handling
|
||
|
|
||
|
The examples above have so far omitted error handling for the sake of
|
||
|
clarity and brevity. Obviously however, many things can go wrong at
|
||
|
various stages of processing and these cases need to be dealt with.
|
||
|
|
||
|
Any errors that are returned from the CSE will be represented in the
|
||
|
form of an `OneM2MErrorResponse` instance. As stated before, the
|
||
|
`OneM2MErrorResponse` class derives from `Exception`. Consequently,
|
||
|
`OneM2MErrorResponse` objects are not returned from the method,
|
||
|
instead they are raised as exceptions.
|
||
|
|
||
|
In addition, it is possible that the CSE could not be contacted at all
|
||
|
in the first place. In this case, an instance of
|
||
|
`openmtc.exc.ConnectionFailed` will be raised, which also derives from
|
||
|
`Exception`.
|
||
|
|
||
|
**Note:** This implies that whenever one of the client methods returns
|
||
|
normally, we can be sure that the operation has succeeded and continue
|
||
|
working with the result as planned without further inspecting the
|
||
|
result's status. This allows a very convenient and pythonic separation
|
||
|
of error and result handling.
|
||
|
|
||
|
With this in mind we can extend *Example 8b* by simply enclosing the
|
||
|
invocation of the client method in a `try`/`except`/`else` block.
|
||
|
|
||
|
This file can be found [here](./training/onem2m-examples/onem2m-example-12a.py).
|
||
|
``` py
|
||
|
# Example 12a: Making Requests with error handling
|
||
|
|
||
|
from openmtc_onem2m.client.http import OneM2MHTTPClient
|
||
|
from openmtc_onem2m.transport import OneM2MRequest, OneM2MErrorResponse
|
||
|
from openmtc.exc import OpenMTCError
|
||
|
|
||
|
client = OneM2MHTTPClient("http://localhost:8000", False)
|
||
|
|
||
|
try:
|
||
|
onem2m_request = OneM2MRequest("retrieve", to="onem2m")
|
||
|
promise = client.send_onem2m_request(onem2m_request)
|
||
|
onem2m_response = promise.get()
|
||
|
except OneM2MErrorResponse as e:
|
||
|
print "CSE reported an error:", e
|
||
|
raise
|
||
|
except OpenMTCError as e:
|
||
|
print "Failed to reach the CSE:", e
|
||
|
raise
|
||
|
else:
|
||
|
pass
|
||
|
|
||
|
# no exception was raised, the method returned normally.
|
||
|
print onem2m_response.to
|
||
|
#>>> onem2m
|
||
|
print onem2m_response.response_status_code
|
||
|
#>>> STATUS(numeric_code=2000, description='OK', http_status_code=200)
|
||
|
print onem2m_response.content
|
||
|
#>>> CSEBase(path='None', id='cb0')
|
||
|
```
|
||
|
|
||
|
|
||
|
### Forwarding
|
||
|
|
||
|
OpenMTC will automatically handle forwarding of a OneM2MRequest if it
|
||
|
is referring to a different CSE than the one the client is connected
|
||
|
to. Forwarding in OneM2M is based on CSE-IDs whereas the ETSI M2M
|
||
|
equivalent Retargeting is based on IPs.
|
||
|
|
||
|
Lets suppose that a gateway is availabe at `localhost:8000` and has
|
||
|
the CSE-ID `mn-cse-1`. Then, its backend is available at
|
||
|
`localhost:18000` and has the CSE-ID `in-cse-1`.
|
||
|
|
||
|
Due to forwarding, the following requests will have the same results:
|
||
|
|
||
|
* `localhost:8000/onem2m` and `localhost:18000/~/mn-cse-1/onem2m`
|
||
|
* `localhost:8000/onem2m` and `localhost:8000/~/mn-cse-1/onem2m"`
|
||
|
* `localhost:8000/~/in-cse-1/onem2m` and `localhost:18000/onem2m`
|
||
|
|
||
|
The following exaple illustrates this:
|
||
|
|
||
|
This file can be found [here](./training/onem2m-examples/onem2m-example-12b.py).
|
||
|
``` py
|
||
|
# Example 12b: Forwarding
|
||
|
|
||
|
from openmtc_onem2m.client.http import OneM2MHTTPClient
|
||
|
from openmtc_onem2m.transport import OneM2MRequest
|
||
|
|
||
|
client = OneM2MHTTPClient("http://localhost:8000", False)
|
||
|
|
||
|
onem2m_request = OneM2MRequest("retrieve", to="onem2m")
|
||
|
onem2m_response = client.send_onem2m_request(onem2m_request).get()
|
||
|
print "---> Request to: http://localhost:8000" + "/" + onem2m_request.to
|
||
|
print onem2m_response.to
|
||
|
#>>> onem2m
|
||
|
print onem2m_response.response_status_code
|
||
|
#>>> STATUS(numeric_code=2000, description='OK', http_status_code=200)
|
||
|
print onem2m_response.content
|
||
|
#>>> CSEBase(path='None', id='cb0')
|
||
|
|
||
|
onem2m_request = OneM2MRequest("retrieve", to="~/mn-cse-1/onem2m")
|
||
|
onem2m_response = client.send_onem2m_request(onem2m_request).get()
|
||
|
print "---> Request to: http://localhost:8000" + "/" + onem2m_request.to
|
||
|
print onem2m_response.to
|
||
|
#>>> ~/mn-cse-1/onem2m
|
||
|
print onem2m_response.response_status_code
|
||
|
#>>> STATUS(numeric_code=2000, description='OK', http_status_code=200)
|
||
|
print onem2m_response.content
|
||
|
#>>> CSEBase(path='None', id='cb0')
|
||
|
|
||
|
client.port = 18000
|
||
|
onem2m_request = OneM2MRequest("retrieve", to="~/mn-cse-1/onem2m")
|
||
|
onem2m_response = client.send_onem2m_request(onem2m_request).get()
|
||
|
print "---> Request to: http://localhost:18000" + "/" + onem2m_request.to
|
||
|
print onem2m_response.to
|
||
|
#>>> ~/mn-cse-1/onem2m
|
||
|
print onem2m_response.response_status_code
|
||
|
#>>> STATUS(numeric_code=2000, description='OK', http_status_code=200)
|
||
|
print onem2m_response.content
|
||
|
#>>> CSEBase(path='None', id='cb0')
|
||
|
```
|
||
|
|