Commit b7ec8291 authored by Björn Butzin's avatar Björn Butzin
Browse files

Updated and simplified Hands On Code + Added Documentation

parent 6cb04e16
# Tutorial for jCoAP Hands-on
In this tutorial we will explain how to develop a simple message exchange between devices using jCoAP.
Furthermore, we will introduce the observe mechanism.
The following points will be covered by this tutorial:
1. Installation of Copper plugin for Mozilla Firefox
2. Introduction of jCoAP
3. Import of prepared project into Eclipse
4. Task 1: Implementation of client/server and enable simple message exchange
5. Task 2: Implementation of an air conditioner control by using the CoAP-observe mechanism
## 1. Requirements for this tutorial
* Java SE JDK 1.6+
* Eclipse IDE for Java development
* Prepared [Java project files for Hands-on](https://gitlab.amd.e-technik.uni-rostock.de/ws4d/jcoap/tree/master/ws4d-jcoap-handsOn)
## 2. Installation of the Copper Plugin for Mozilla Firefox
- [https://addons.mozilla.org/de/firefox/addon/copper-270430/](https://addons.mozilla.org/de/firefox/addon/copper-270430/)
- Click on `add to Firefox` and Confirm Installation
- Restart Firefox
After the installation you can enter anything like `coap://host:port/resourcePath/?query=filter` in the address bar.
Copper will allow you to make any CoAP interaction.
![Copper Screenshot](https://gitlab.amd.e-technik.uni-rostock.de/ws4d/jcoap/raw/master/ws4d-jcoap-handsOn/img/CopperScreenshot.jpg)
## 3. Introduction of jCoAP
- WS4D-jCoAP: Java implementation of CoAP
- [http://ws4d.org/](http://ws4d.org/)
- [https://gitlab.amd.e-technik.uni-rostock.de/ws4d/jcoap](https://gitlab.amd.e-technik.uni-rostock.de/ws4d/jcoap)
#### Client Side
![Client Side UML Diagram](https://gitlab.amd.e-technik.uni-rostock.de/ws4d/jcoap/raw/master/ws4d-jcoap-handsOn/img/Client%20UML.jpg)
**CoapClient:** Interface that must be implemented by a client
**Client:** Customized implementation of a client application, implements `CoapClient`
**Channel:** A channel represents a connection between a Client and a Server
**ChannelManager:** Manages `Channels` (Timeouts, Matching Requests and Responses)
#### Server Side
![Server Side UML Diagram](https://gitlab.amd.e-technik.uni-rostock.de/ws4d/jcoap/raw/master/ws4d-jcoap-handsOn/img/ServerUML.jpg)
**CoapResource:** Interface that must be supported by each resource.
**BasicCoapResource:** Already implemented resource with basic functionality, implements `CoapResource`
**TemperatureResource:** Example of a customized resource, inherits from `BasicCoapResource`
**CoapServer:** Interface that must be supported by a resource server
**CoapResourceServer:** Manages a list of resources, enables access of these resources from outside, implements `CoapServer`
**Server:** Example of a customized implementation of a server application, creates `CoapResourceServer` and `CoapResources`
## 4. Import of prepared project into Eclipse
You can find the required files in our repository at [https://gitlab.amd.e-technik.uni-rostock.de/ws4d/jcoap/tree/master/ws4d-jcoap-handsOn](https://gitlab.amd.e-technik.uni-rostock.de/ws4d/jcoap/tree/master/ws4d-jcoap-handsOn)
1. *[File » Import]*
2. *[General » Existing Projects into Workspace]*
3. Browse <Project Folder>
4. Finish
* We have prepared some FIXME and TODO annotations:
* Just open the „Task“ view
* FIXMEs are for the first task (message exchange)
* TODOs are for the second task (observe and AC control)
* If you do not have a „Task“ view:
* *[Window &raquo; Show View &raquo; Other]*
* Type `task`
* Select *[General &raquo; Task]* view
## 5. Task 1: Implementation of client/server and enable simple message exchange
### Sequence Diagram
![Task 1 – Sequence Diagram](https://gitlab.amd.e-technik.uni-rostock.de/ws4d/jcoap/raw/master/ws4d-jcoap-handsOn/img/Task1Sequence.png)
### Server
1. Create a new resource class TemperatureResource (already done in our example Server)
2. Instantiate a new ResourceServer
3. Instantiate a new TemperatureResource
4. Add the TemperatureResource to the ResourceServer
5. Start the ResourceServer
6. Test
#### 1. Create a new resource class TemperatureResource (TemperatureResource.java):
* We could have used the predefined `BasicCoapResource`
* `BasicCoapResource` is a resource that just keeps a static `byte[]` which is:
* returned on GET requests
* replaced by the payload on PUT requests
* appended with the payload on POST requests
* deleted on DELETE requests
* We do not want a static `byte[]`
* Instead we want a random number to be returned on a GET
* PUT, POST and DELETE are not used
* ->So we implemented `TemperatureResource` which extends `BasicCoapResource` with:
* A constructor to initialize the resource and disallow POST, PUT and DELETE requests and
* Two get() methods:
```java
CoapData get(List<CoapMediaType> mediaTypesAccepted);
CoapData get(List<String> query, List<CoapMediaType> mediaTypesAccepted);
```
#### 2. Instantiate a new ResourceServer (Server.java, FIXME 0):
* Need a CoapResourceServer to maintain resources
```java
CoapResourceServer resourceServer = new CoapResourceServer();
```
#### 3. Instantiate a new TemperatureResource (Server.java, FIXME 1):
* Resources are created like normal objects
```java
CoapResource resource = new CoapResource();
```
#### 4. Add the TemperatureResource to the ResourceServer (Server.java, FIXME 2):
```java
resourceServer.createResource(resource);
```
#### 5. Start the ResourceServer (Server.java FIXME 3):
```java
resourceServer.start(port);
resourceServer.start(); // equals port = CoapConstants.COAP_DEFAULT_PORT
```
#### 6. Test:
* Run Server: Click on *[Run &raquo; Run]* in the Menu bar
* To stop the server, press the red terminate button in the console/task area
* Test it with Copper: `coap://127.0.0.1`
* Stretch goal:
* Create another resource type e.g.: humidity or current time, use real sensor values if possible
* Tip: make a copy of `TemperatureResource.java`
### Client
1. Establish a connection to the Server using the ChannelManager
2. Create a CoapRequest & add some Options
3. Send the CoapRequest
4. Wait for CoapResponse & Print the CoapResponse on the console (already done in our example)
5. Test
#### 1. Establish a connection to the Server using the ChannelManager (Client.java, FIXME 4-5):
* A client must implement CoapClient interface
```java
public class Client implements CoapClient {...}
```
* A CoapChannelManager is used to manage different connections and to establish a connection to a server
```java
channelManager = BasicCoapChannelManager.getInstance();
clientChannel = channelManager.connect(CoapClient client,InetAddress serverIP, int serverPort);
```
#### 2. Create a CoapRequest & add some Options (Client.java, FIXME 6-8):
* A channel represents a single connection and is used to create and send requests
```java
Boolean reliable = false;
CoapRequestCode reqCode = CoapRequestCode.GET;
CoapRequest request = clientChannel.createRequest(reliable,reqCode);
request.setUriPath("/temperature");
```
#### 3. Send the CoapRequest (Client.java, FIXME 9):
```java
clientChannel.sendMessage(request);
```
#### 4. Wait for CoapResponse & Print the CoapResponse on the console:
* A client has some callbacks that are invoked, when the corresponding event occurs
```java
public void onConnectionFailed(...);
public void onResponse(...); // = Unicast
public void onMCResponse(...); // MC = Multicast
```
#### 5. Test:
* Run Server: select Server.java and click on *[Run &raquo; Run]* in the Menu bar
* Run Client: select Client.java and click on *[Run &raquo; Run]* in the Menu bar
* Stretch goal:
* If you have written your own resources before: GET them
* GET the `/.well-known/core` resource (it is generated automatically by the server)
* GET the `/.well-known/core` resource using multicast (aka. Multicast Discovery)
```java
// Multicast addresses
// CoapConstants.COAP_ALL_NODES_IPV4_MC_ADDR
// CoapConstants.COAP_ALL_NODES_IPV6_LL_MC_ADDR
// CoapConstants.COAP_ALL_NODES_IPV6_SL_MC_ADDR
// On Multicast: add token to match multicast request and unicast responses
request.setToken("MCToken".getBytes());
```
## 6. Task 2: Implementation of a AC control by using the CoAP-observe mechanism
1. Use the eventing mechanism CoAP-Observe
2. Let the server notify clients every 5 seconds about a changed TemperatureResource
3. Implement an Air Conditioner Resource with the path `/ACControl`, that can be set to `high`, `medium`, `low` or `off`
![Task 2 - Sequence diagram](https://gitlab.amd.e-technik.uni-rostock.de/ws4d/jcoap/raw/master/ws4d-jcoap-handsOn/img/Task2Sequence.png)
### 1. Use the eventing mechanism CoAP-Observe (Server.java, TODO 10):
* Mark the TemperatureResource as observable
```java
resource.setObservable(true);
```
### 2. Let server notify clients every 5 s about changed TemperatureResource (Server.java, TODO 11):
* indicate a change for resource every 5 seconds
```java
while (true) {
try {Thread.sleep(5000);}
catch (InterruptedException e) {/*do nothing*/}
resource.changed(); // Notify
}
```
### 3. Implement an Air Conditioner Resource with the path `/ACControl`, that can be set to `high`, `medium`, `low` or `off` (Client.java & Server.java, TODO 12-15):
* Change exitAfterResponse to false (Client.java, TODO 12)
* Add the observe-option to your CoAP-GET request (Client.java, TODO 13)
```java
request.setObserveOption(0);
```
* add a BasicCoapResource to the ResourceServer (Server.java, TODO 14)
```java
resourceServer.createResource(newBasicCoapResource("/ACControl","off",CoapMediaType.text_plain));
```
* send PUT request (Client.java, TODO 15)
```java
CoapRequest request = clientChannel.createRequest(true, CoapRequestCode.PUT);
request.setUriPath("/ACControl");
request.setContentType(CoapMediaType.text_plain);
```
Depending on the received temperature, set the payload to high, medium, low or off
```
high 28 <= Temperature
medium 25 <= Temperature < 28
low 21 <= Temperature < 25
off Temperature < 21
```
```java
request.setPayload("medium".getBytes());
clientChannel.sendMessage(request);
```
ws4d-jcoap-handsOn/img/Client UML.jpg

42.3 KB | W: | H:

ws4d-jcoap-handsOn/img/Client UML.jpg

26.2 KB | W: | H:

ws4d-jcoap-handsOn/img/Client UML.jpg
ws4d-jcoap-handsOn/img/Client UML.jpg
ws4d-jcoap-handsOn/img/Client UML.jpg
ws4d-jcoap-handsOn/img/Client UML.jpg
  • 2-up
  • Swipe
  • Onion skin
......@@ -15,10 +15,7 @@ import org.ws4d.coap.core.messages.api.CoapResponse;
public class Client implements CoapClient {
// TODO 12: Skip for now! In the 2nd step we change this to false.
/**
* if you want to send multiple requests you should set this to false
* otherwise the program will terminate after the first response arrives
*/
/** should program terminate after the first response? */
private boolean exitAfterResponse = true;
/** A manager to keep track of our connections */
......@@ -30,18 +27,18 @@ public class Client implements CoapClient {
/** The method containing the creation of our requests */
public void start(String serverAddress, int serverPort) {
/* FIXME 4: Initialize the ChannelManager and get a client channel to the server */
/* FIXME 4: Get the ChannelManager instance */
// this.channelManager = BasicCoapChannelManager.getInstance();
this.clientChannel = null;
try {
/* FIXME 5: Connect to the server */
/* FIXME 5: Create a channel to the server */
// this.clientChannel = this.channelManager.connect(this, InetAddress.getByName(serverAddress), serverPort);
/* Make sure that the channel is not null */
if (this.clientChannel == null) {
System.err.println("Connect failed: clientChannel in null!");
System.err.println("Connect failed: clientChannel is null!");
System.exit(-1);
}
} catch (Exception e) {
......@@ -49,22 +46,24 @@ public class Client implements CoapClient {
System.exit(-1);
}
/* Create an empty request and prepare parameters */
// do we expect an acknowledgement
boolean reliable = true;
/* Create an request and prepare parameters */
/* FIXME 6: What kind of request do we want to send? */
// CoapRequestCode requestCode = CoapRequestCode.GET;
// CoapRequestCode requestCode = CoapRequestCode.<CTRL><SPACE>;
// do we want an acknowledgement?
boolean reliable = true;
/* FIXME 7: Create the request */
// CoapRequest request = this.clientChannel.createRequest(reliable, requestCode);
/* FIXME 8: At least we have to set the path of the requested resource */
// request.setUriPath("/temperature");
/* FIXME 8: Set the resource path */
// request.setUriPath("/???");
/* TODO 13: Skip for now! In the 2nd step we add the observe option here */
// request.setObserveOption(0);
/* TODO 13: Skip for now! In the 2nd step we add the observe option here
* Tip: the Parameter sequenceNumber is recommended to be 0
* */
// request.set<CTRL><SPACE>;
/* For further exploration: other interesting options */
// request.setPayload(payload); // for PUT and POST operations you need to provide some payload
......@@ -73,7 +72,7 @@ public class Client implements CoapClient {
// request.addAccept(CoapMediaType.text_plain); // only accept certain media types as response; you can add multiple
// request.setProxyUri(proxyUri); // send this request through a proxy
/* FIXME 9: Send your Message here! */
/* FIXME 9: Send your message */
// this.clientChannel.sendMessage(request);
}
......@@ -92,7 +91,7 @@ public class Client implements CoapClient {
// We just print out the response
if (response.getPayload() != null) {
/* TODO 15: Skip for now! In the 3rd step we replace this behavior.*/
System.out.println("Response: " + response.toString() + " (" + new String(response.getPayload()) + ")");
System.out.println("Response: " + response.toString() + " Payload: " + new String(response.getPayload()));
} else {
System.out.println("Response: " + response.toString());
}
......
package org.ws4d.coap.handsOn;
import org.ws4d.coap.core.CoapConstants;
import org.ws4d.coap.core.enumerations.CoapMediaType;
import org.ws4d.coap.core.rest.BasicCoapResource;
import org.ws4d.coap.core.rest.CoapResourceServer;
......@@ -14,16 +15,16 @@ public class Server {
if (this.resourceServer != null){
this.resourceServer.stop();
}
/* FIXME 1: Instantiate a CoapResourceServer */
/* FIXME 0: Instantiate a new CoapResourceServer */
// this.resourceServer = new CoapResourceServer();
/* FIXME 2: Instantiate a TemperatureResource */
/* FIXME 1: Instantiate a TemperatureResource */
// TemperatureResource resource = new TemperatureResource();
/* TODO 10: Skip for now! In the 2nd step we will make this resource observable right here */
// resource.setObservable(true);
// resource.set<CTRL><SPACE>;
/* FIXME 3: Add our resource to the server */
/* FIXME 2: Add our resource to the server */
// this.resourceServer.createResource(resource);
/* TODO 14: Skip for now! In the 3rd step we will add another resource here*/
......@@ -31,7 +32,8 @@ public class Server {
/* this starts the server */
try {
this.resourceServer.start();
/* FIXME 3: determine port to run */
// this.resourceServer.start(CoapConstants.COAP_DEFAULT_PORT);
} catch (Exception e) {
System.err.println(e.getLocalizedMessage());
}
......@@ -40,7 +42,7 @@ public class Server {
// while(true){
// try {Thread.sleep(5000);}
// catch (InterruptedException e) {/*do nothing*/}
// resource.changed();
// resource.changed(); // Notify about changed resource
// }
}
......
......@@ -20,6 +20,10 @@ public class TemperatureResource extends BasicCoapResource {
this.setDeletable(false);
this.setPostable(false);
this.setPutable(false);
// add some meta Information (optional)
this.setResourceType("Temperature");
this.setInterfaceDescription("GET only");
}
public TemperatureResource(){
......@@ -39,9 +43,4 @@ public class TemperatureResource extends BasicCoapResource {
/* we just ignore query parameters*/
return get(mediaTypesAccepted);
}
@Override
public synchronized String getResourceType() {
return "Temperature";
}
}
......@@ -47,7 +47,7 @@ public class MulticastDiscoveryClient implements CoapClient {
/* Create request to /.well-known/core resource */
/* *************************************************************************************************** */
// set reliablity and request method
// set reliability and request method
CoapRequest request = this.clientChannel.createRequest(false, CoapRequestCode.GET);
// set resource path
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment