Skip to content

Commit

Permalink
Merge pull request #26 from telefonicaid/develop
Browse files Browse the repository at this point in the history
Release 0.2.0
  • Loading branch information
Fermín Galán Márquez committed Feb 12, 2015
2 parents 9e1f124 + 85bd93d commit fc537ee
Show file tree
Hide file tree
Showing 16 changed files with 246 additions and 66 deletions.
2 changes: 2 additions & 0 deletions .npmignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
.idea
ghpages
6 changes: 6 additions & 0 deletions CHANGES_NEXT_RELEASE
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
- Documentation improvements
- Add 'disconnect' and 'quit' commands for the command line client
- Add support for LWM2M Client scritp in the command line client
- Add error handlers for COAP errors
- Add Cancel Observation features for the LWM2M Client
- Add extended debug traces
3 changes: 2 additions & 1 deletion Gruntfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,8 @@ module.exports = function(grunt) {
unit: {
options: {
ui: 'bdd',
reporter: 'spec'
reporter: 'spec',
timeout: 2000
},
src: [
'tools/mocha-globals.js',
Expand Down
103 changes: 85 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,41 @@

[![Dependency Status](https://david-dm.org/telefonicaid/iotagent-lwm2m-lib.png)](https://david-dm.org/telefonicaid/iotagent-lwm2m-lib)

## Overview
## Index

* [Overview](#overview)
* [Command line applications](#commandline)
* [Usage](#libraryusage)
* [Configuration](#configuration)
* [Development Documentation](#development)

## <a name="overview"/> Overview
The [Open Mobile Alliance Lightweight M2M protocol](http://openmobilealliance.org/about-oma/work-program/m2m-enablers/) is a machine to machine communication protocol built over [COAP](https://tools.ietf.org/html/draft-ietf-core-coap), and meant to
communicate resource constrained devices. The protocol defines two roles for the devices: a Lightweight M2M Client (the constrained device) and a Lightweight M2M Server (meant to consume the device data and control its execution).

This library aims to provide a simple way to build Lightweight M2M Servers and Clients with Node.js, giving an abstraction over the COAP Protocol based on function calls and handlers. The provided features are:
* Creation of a server listening to Client calls for the LWTM2M Interfaces, linked to handlers defined by the user.
* Registry of devices connected to the server (in-memory registry for the first version).
* Server calls to the registered devices in the registry (for Device Management Interface mostly) to retrieve information.
This library aims to provide a simple way to build Lightweight M2M Servers and Clients with Node.js, giving an abstraction over the COAP Protocol based on function calls and handlers.

Features provided by the server library:
* Creation of a COAP server listening for Client calls for the LWTM2M Interfaces, linked to handlers defined by the user.
* Registry of devices connected to the server (transient in-memory registry and persistent MongoDB based one).
* Server calls to the registered devices in the registry (Device Management Interface) to retrieve and write resource information and entity attributes.
* Subscriptions to changes in resource values (Information Management Interface), on change or timed (and subscription management).

Features provided by the client library:
* Functions for connecting and disconnecting from remote servers.
* Creation of a COAP server listening for commands issued from the LWM2M Server side linked to handlers defined by the user.
* Transient in-memory object registry, to store the current objects and instances along with their resource values and attributes.
* Support for subscriptions from the server (using COAP Observe) both timed and on-change (both of them based in the values of the resources currently available in the registry).

The library also provides command line clients to test both its client and server capabilities.

## Command line applications
## <a name="commandline"/> Command line applications
The library provides two command line applications in order to help developing both Lightweight M2M clients and/or servers. This applications can be used to simulate the behavior of one of the peers of the communication. Both of them use the iotagent-lwm2m-lib library to serve all the LWTM2M requests. The following sections explain the basic features of each one.

To use any of the applications you need to clone the project in your computer, and execute `npm install` from the root of the project, in order to download the dependencies. You can start both applications from the same folder using different console windows.
There are multiple ways of using the applications:

* You may clone the project in your computer, and execute `npm install` from the root of the project, in order to download the dependencies. You can start both applications from the same folder using different console windows.
* Another option is to install directly with `npm install -g iotagent-lwm2m-lib`. This will install the library in the global `node_modules`.

Take into account that all the information loaded or registered by any of the applications is transient, so it will be lost once the processes have been stopped.

Expand Down Expand Up @@ -57,13 +77,30 @@ read <deviceId> <resourceId>
Reads the value of the resource indicated by the URI (in LWTM2M format) in the given device.
discover <deviceId> <resourceId>
discover <deviceId> <objTypeId> <objInstanceId> <resourceId>
Sends a discover order for the given resource to the given device.
discoverObj <deviceId> <objTypeId> <objInstanceId>
Sends a discover order for the given instance to the given device.
discoverType <deviceId> <objTypeId>
Sends a discover order for the given resource to the given device.
observe <deviceId> <objTypeId> <objInstanceId> <resourceId>
Stablish an observation over the selected resource.
Sends a discover order for the given resource (defined with a LWTM2M URI) to the given device.
writeAttr <deviceId> <objTypeId> <objInstanceId> <resourceId> <attributes>
cancel <deviceId> <resourceId>
Write a new set of observation attributes to the selected resource. The attributes should be
in the following format: name=value(,name=value)*. E.g.: pmin=1,pmax=2.
Cancel the discover order for the given resource (defined with a LWTM2M URI) to the given device.
cancel <deviceId> <objTypeId> <objInstanceId> <resourceId>
Cancel the observation order for the given resource (defined with a LWTM2M URI) to the given device.
config
Expand All @@ -84,7 +121,21 @@ You can type `help` in the command line at any moment to get a full list of the

All the client configuration is read from the `config.js` file in the root of the project. You can print the configuration that is actually being used using the `config` command.

To exit the command line client, use `CTRL-C`.
To exit the command line client, use `CTRL-C` or the `quit` command.

The command line client can also be used to execute scripts. Each line of the script is interpreted as a line in the command line. You have to take two things into account:
* The script is executed as an input to the client, line by line, and it will not end unless a `quit` command is explicitly issued. In that case, the client will end up in a prepaired state, with the prompt available to receive further commands.
* Currently, all the commands are executed asynchronously, so actions like connection and discconnection may rise errors if executed in the same script (as the disconnection could be exectued before the connection is completed).

The following is an example of script:
````
create /75001/2
create /75002/2
set /75001/2 0 440.81
set /75002/2 1 Connected
connect localhost 60001 PruebasDuplex /
quit
```
#### Command reference
```
Expand Down Expand Up @@ -112,7 +163,7 @@ list
List all the available objects along with its resource names and values.
connect <host> <port> <endpointName>
connect <host> <port> <endpointName> <url>
Connect to the server in the selected host and port, using the selected endpointName.
Expand All @@ -127,13 +178,17 @@ disconnect
config
Print the current config.
quit
Exit the client.
```
## Usage
Note: as it is not yet published in npm repositories, this module has to be currently used as a github dependency in the package.json. To do so, add the following dependency to your package.json file, indicating the commit you want to use:
## <a name="libraryusage"/> Usage
In order to use the library, add the following dependency to your package.json file:
```
"iotagent-lwm2m-lib": "https://github.com/dmoranj/iotagent-lwm2m-lib/tarball/43664dd4b011673dd56d52b00d825cc3cf2ef679"
"iotagent-lwm2m-lib": "*"
```
In order to use this library, first you must require it:
Expand Down Expand Up @@ -230,15 +285,27 @@ The repository offers the standard CRUD operations over objects, and methods to
All the objects are identified by a URI that is composed of an Object ID and an Object Instance sepparated by slashes, as specified by the Lightweight M2M specification (e.g.: /1/3).
### Configuration
## <a name="configuration"/> Configuration
The configuration object should contain the following fields:
* `server.port`: port where the server's COAP server will start listening.
* `server.defaultType`: type that will be assigned to a device registering in the server if there is no other.
* `server.logLevel`: log level for the internal logger (could have any of the following values: DEBUG, INFO, ERROR, FATAL).
* `server.types`: in the case of multiple URLs, mapping between URL and type (see bellow [Configuring multiple southbound interfaces](#multipleinterfaces).
* `client.port`: port where the client's COAP server will start listening.
* `client.lifetime`: lifetime in miliseconds of the client registration. After that lifetime, the registration will be dismissed.
* `client.version`: version of the Lightweight M2M protocol. Currently `1.0` is the only valid option.
* `client.observe`: default parameters for resource observation (they can be overwritten from the server).
## Development documentation
### <a name="multipleinterfaces"/> Configuring multiple southbound interfaces
The Lightweight M2M Server library can be configured to accept registrations in multiple southbound paths (all of them sharing IP and Port). In this case, each device will be assigned a different root base for its requests (that will be returned in the `Location-path` option), and will be assigned a device type, that can be used to group devices.
The southbound interfaces can be configured in the `server.types` configuration parameter. This parameter is a list of objects composed of two attributes:
* `name`: name of the type that will be assigned to the device.
* `url`: url prefix used to identify the devices (take into account that the registration URL will be the concatenation of this value with the `/rd` standard registration path).
Devices that arrive to the global `/rd` registration path will be assigned the default type instead (configured in `server.defaultType` configuration attribute).
## <a name="development"/> Development documentation
### Project build
The project is managed using Grunt Task Runner.
Expand Down
29 changes: 27 additions & 2 deletions bin/iotagent-lwm2m-client.js
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,26 @@ function connect(command) {
});
}

function disconnect(command) {
if (globalDeviceInfo) {
lwm2mClient.unregister(globalDeviceInfo, function(error) {
if (error) {
clUtils.handleError(error);
} else {
console.log('\nDisconnected:\n--------------------------------\n');
clUtils.prompt();
}
});
} else {
console.error('\nCouldn\'t find device information (the connection may have not been completed).');
}
}

function quit(command) {
console.log('\nExiting client\n--------------------------------\n');
process.exit();
}

var commands = {
'create': {
parameters: ['objectUri'],
Expand Down Expand Up @@ -179,13 +199,18 @@ var commands = {
'disconnect': {
parameters: [],
description: '\tDisconnect from the current server.',
handler: clUtils.notImplemented
handler: disconnect
},
'config': {
parameters: [],
description: '\tPrint the current config.',
handler: clUtils.showConfig(config, 'client')
},
'quit': {
parameters: [],
description: '\tExit the client.',
handler: quit
}
};

clUtils.initialize(commands, 'LWM2M-Client>');
clUtils.initialize(commands, 'LWM2M-Client> ');
2 changes: 1 addition & 1 deletion config.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,4 @@ config.client = {
}
}

module.exports = config;
module.exports = config;
16 changes: 13 additions & 3 deletions lib/commandLineUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@
* please contact with::[contacto@tid.es]
*/

var readline = require('readline');

var rl = readline.createInterface({
var readline = require('readline'),
fs = require('fs'),
rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
Expand Down Expand Up @@ -117,6 +117,16 @@ function initialize(commands, prompt) {
rl.on('line', function (cmd) {
executeCommander(cmd.split(' '), commands);
});

if (process.argv.length > 2) {
var lines = fs.readFileSync(process.argv[2], 'utf8').split('\n');

for (var i in lines) {
if (lines[i]) {
rl.write(lines[i] + '\n');
}
}
}
}

function printName(name) {
Expand Down
14 changes: 13 additions & 1 deletion lib/errors.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,12 +69,24 @@ module.exports = {
TypeNotFound: function(url) {
this.name = 'TYPE_NOT_FOUND';
this.message = 'No type matching found for URL ' + url;
this.code = '4.00';
this.code = '4.04';
},
IllegalTypeUrl: function(url) {
this.name = 'ILLEGAL_TYPE_URL';
this.message = 'Illegal URL for type: ' + url + '. Types begining with "/rd" are not allowed';
},
RegistrationError: function(msg) {
this.name = 'REGISTRATION_ERROR';
this.message = 'There was an error connecting to the LWM2M Server for registration: ' + msg;
},
UpdateRegistrationError: function(msg) {
this.name = 'UPDATE_REGISTRATION_ERROR';
this.message = 'There was an error connecting to the LWM2M Server for update registration: ' + msg;
},
UnregistrationError: function(msg) {
this.name = 'UNREGISTRATION_ERROR';
this.message = 'There was an error connecting to the LWM2M Server for unregistration: ' + msg;
},
RegistrationFailed: function(code) {
this.name = 'REGISTRATION_FAILED';
this.message = 'Registration to the Lightweight M2M server failed with code: ' + code;
Expand Down
31 changes: 30 additions & 1 deletion lib/iotagent-lwm2m-client.js → lib/lwm2m-client.js
Original file line number Diff line number Diff line change
Expand Up @@ -169,12 +169,24 @@ function register(host, port, url, endpointName, callback) {
rs.push(null);
rs.pipe(req);

innerCallback(null);
rs.on('error', function(error) {
logger.error(context, 'Error found trying to send registration request: %s', payload);
innerCallback(new errors.RegistrationError(error));
});

rs.on('end', function() {
innerCallback();
});
}

function registerResponseListener(deviceInformation, innerCallback) {
req.on('response', createRegisterResponseHandler(deviceInformation, callback));

req.on('error', function(error) {
logger.error(context, 'An error was found while trying to register a response listener');
innerCallback(new errors.RegistrationError(error));
});

innerCallback();
}

Expand Down Expand Up @@ -224,6 +236,11 @@ function unregister(deviceInformation, callback) {
coapRouter.stop(deviceInformation.serverInfo, callback);
});

req.on('error', function(error) {
logger.error(context, 'There was an error during unregistration: %s', error);
callback(new errors.UnregistrationError(error));
});

req.end();
}

Expand All @@ -244,6 +261,13 @@ function updateRegistration(deviceInformation, callback) {
rs.push(payload);
rs.push(null);
rs.pipe(req);

rs.on('end', callback);

rs.on('error', function(error) {
logger.error(context, 'There was a connection error during update registration process: %s', error);
callback(new errors.UpdateRegistrationError(error));
});
}

req.on('response', function(res) {
Expand All @@ -252,6 +276,11 @@ function updateRegistration(deviceInformation, callback) {
callback(null, deviceInformation);
});

req.on('error', function(error) {
logger.error(context, 'Request error during update registration process: %s', error);
callback(new errors.UpdateRegistrationError(error));
});

async.waterfall([
objRegistry.list,
generatePayload,
Expand Down
4 changes: 2 additions & 2 deletions lib/iotagent-lwm2m-lib.js → lib/lwm2m-node-lib.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,5 @@

'use strict';

exports.server = require('./iotagent-lwm2m-server');
exports.client = require('./iotagent-lwm2m-client');
exports.server = require('./lwm2m-server');
exports.client = require('./lwm2m-client');
File renamed without changes.
Loading

0 comments on commit fc537ee

Please sign in to comment.