@smarterservices/smartersockets

1.5.2 • Public • Published

SmarterSockets

SmarterSockets is a websocket implemention backed by AWS IoT & DynamoDB that provides the following functionallity:

  • Provides an interface for request => response logic
  • Allows for messages to be persisted if client does not disconnect gracefully. Upon reconnect the messages will be sent down to the client.

##Getting Started

Installation

npm install @smarterservices/smartersockets --save

Configuration Options

  • device:
    • host: The full host for the IoT endpoint. e.g. randomidhere.iot.us-east-1.amazonaws.com required
    • client_id: A unique client id that will be used to connect to the socket. Value much be unique required
    • access_key_id: The AWS temp access key for the connection. required
    • secret_access_key : The AWS temp secret key for the connection. required
    • session_token: The AWS security token that is to be used for the temp credentials above. Only passed if your using temp credentials
  • state_management : An object containing the preferences for the state management. Only required if you plan to have state managed
    • ping_interval : The number of seconds the heartbeat message should be sent from the client. Default: 2 seconds.
    • sync_interval: The interval (in seconds) that should be used to check the status of all other clients.
    • disconnect_threshold : The number of seconds that should be used as the threshold for when a client is assumed to be disconnected and can the client.disconnect event can be fired.
    • removal_threshold : The number of seconds that should be used as the threshold for when a client should actually be removed from the internal data storage and the client.removed event emitted.

Example

var config = {
				"device": {
					"host":"randomidhere.iot.us-east-1.amazonaws.com"
					"client_id":"some-client-id"
					"access_key_id": "mykey",
					"secret_access_id": "mysecret",
					"session_token": "a023unadnasona..."
				},
				"state_management" : {
					"ping_interval" : 2,
					"sync_interval" : 5,
					"disconnect_threshold" : 10,
					"removal_threshold" : 60
				}
			};

state_management only required if using state manage from below

Sample Usage(no state management)

var smartersocket = require('@smarterservices/smartersockets')
var socket = new smartersocket(config) //check below for required configs
	
socket.register(DATA_OBJ).res(CALLBACK_FUNCTION)

##Methods provided

register()

Will publish to session_id+'_input session id must be inside data_object

description: Will both publish to your topic the data provided and register that publish with and id and callback for when the response is recieved.

Required params:

data_object: the object sent to iot. Look below
res: the callback function tagged for when a response is received

Uses promise style for callback registration so the proper format for params is:

socket.register(data_object).res(callback_function)

publish()

description: Same as socket.register but you do not register a callback and should not expect a res from what your publishing.

Required params:

data_object: the object sent to iot. Look below(Publish payload)


socket.publish(data_object)

addListener()

description: Assignes a new listener to a new topic and if using state manager will start sending heartbeats and the inital knock.

Required params:

topic_id: the topic you will start listening on

socket.addListener('topicname');

You will now start recieveing emits to any message sent to topicname

setInterval()

description: This function is used to assign a function that will be set to resolve in an interval and publish to the topic with a callback.

Required params:

resolve_function: Function that needs to resolve and return a data_object in the standard format(Publish payload below).
timer: time in milliseconds to reresolve and publish.
callback_function: function that will be registered and called when the published message gets a response.
EX:

socket.setInterval(function () {
    return {
        type: 'TYPE_HERE',
        topic: 'TOPIC',
        data: {
            test_data: 1
        }
    }
}, 5000, function (res) {
    console.log(res);
})	

close()

Call close to pulish a message to everyone else listening letting them know they can remove you from state.

Required params:

topic_id: 'topic to remove yourself from'

socket.close('TOPIC')

registerTypeCallback()

Call used to register a callback function based on type if one is not registered via message_id. In globa name space so avoid the following names as first params

addListener
close
registerTypeCallback
listen
register
publish
setInterval
attachManager
fire
knock
heartbeat
client_removal

Required params:

type: 'they type your registering a callback for'
callback: 'function that is ran when message with that type is recieved

socket.registerTypeCallback('type_name',CALLBACK FUNCT)

fallback function arguments are (topic_id,obj)    	

##Publish payload Any message sent via .register() should follow this standard for payload.

If you pass client_id, timestamp or message_id in it will overwrite what would normally be handled in the module

payload: {
	type: The type of message. This is used to start a lambda based on it. IE 'speed_check',
	topic_id:Required as its used as the topic to send to.
	data: {OBJECT containing any of the data needed for the worker}
}		
socket.register(data_object).res(callback)

##Returned payload Any message sent via .register() that is published back from a lambda

worker will follow this standard:

	payload: {
		type: 'reply', //will always be reply
		topic_id:The topic sent to,
		client_id: id of the client the payload is comming from
		data: {OBJECT containing any of the data needed for the worker},
		message_id: 'Id that was assigned to message on send',
		timestamp: 'Timestamp of return'
	}

Events Emitted

socket.on('message',function(topic,res){})

description: generic listener for anything not expecting a res from .register method.

Example:

socket.on('message',function(topic,res) {
	console.log(topic,res	
}

socket.on('connect',function(){})

description: called when connect. Should always nest all other listeners inside this.

Example:

socket.on('connect',function(){
	console.log('now connected');
};

**socket.on('reconnect',function(){})**

description:If reconnect happens.

Example:

socket.on('reconnect',function(){
	console.log('now reconnected');
};

**socket.on('error',function(error){})**

description: happens if error happens during connect or publish.

Example:

socket.on('error',function(error){
	console.log(error);
};

**socket.on('offline',function(){})**

description: happens if state is switched to offline.

Example:

socket.on('offline',function(){
	console.log('now offline');
};

**socket.on('close',function(){})**

description: happens if socket connection is closed.

Example:

socket.on('close',function(){
	console.log('now closed socket');
};

###All events after this only avaliable with state management class below:

socket.on('client.connected',function(obj){})

description: Happens if a new client connects to your topic

return: OBJECT {topic:topic,client:client_id}

Example:

socket.on('client.connected',function(res){
	console.log(res.topic,res.client);
};

**socket.on('client.disconnected',function(obj){})**

description: Happens if an already connected client disconnects

return: OBJECT {topic:topic,client:client_id}

Example:

socket.on('client.disconnected',function(res){
	console.log(res.topic,res.client);
};

**socket.on('client.reconnected',function(obj){})**

description: Happens if a disconnected client reconnects

return: OBJECT {topic:topic,client:client_id}

Example:

socket.on('client.reconnected',function(res){
	console.log(res.topic,res.client);
};

**socket.on('client.removed',function(obj){})**

description: Happens when a client ends their session and will not be reconnecting

return: OBJECT {topic:topic,client:client_id}

Example:

socket.on('client.removed',function(res){
	console.log(res.topic,res.client);
};

Nest all listeners inside of socket.on(connect)

Must be called after connect has been emitted

Client State & Restoring Data

The system allows for persisting data if/when a client does not reconnect gracefully. This is enabled largely by the ability of AWS IoT to stream data directly into a DynamoDB table. For our purposes, all messages that are sent for topics will be inserted into a DynamoDB table via explicit rules that are setup in the AWS IoT configuration.

Getting started:

Socket can work alone without stateManager but in order to track state you need to pass socket into state manager and then register the manager with socket like follows:

var socket = new socket(configs)
var manager = new stateManager(socket)
socket.attachManager(manager)

Note this needs to happen BEFORE you call socket.addListener()

=======

Handling Client State

Client state is handled by the internal library in a simple json object. When a client initially listens to a topic the sync function will also be put into a repeat loop behind the scenes. This function will be called repeatedly based on the interval provided when the socket was opened via the configuration variable state_management.sync_interval.

State management class

State management is all handled in the stateManagement.js class, a high level overview of the functions the class provides is below.

constructor(state_management_config, socket)

Creates an instance of the state management class. When this method is called the following should happen:

  • Call the sendHeartbeat function. To let everyone know that they are now online.
  • Call the knock function to get everyone that is listening.
sendHeartbeat(topic_id, client_id, data)

This function is called with the a message with message_type heartbeat comes in. This will make sure the internal data storage is updated with the most current information for the client that emitted the message.

clientUpdate(topic_id, client_id, data)

This function is called with the a message with message_type heartbeat comes in. This will make sure the internal data storage is updated with the most current information for the client that emitted the message.

If the client is newly added to the data store then the client.connected event should be emitted.

clientDisconnected(topic_id, client_id)

Called when a client is to be marked as disconnected. This function should remove the client and emit the client.disconnected event with the proper data.

Removed(topic_id, client_id)

Called when a client is to be removed. This function should remove the client and emit the client.removed event with the proper data.

sync()

Reviews all clients and makes some decisions based on the thresholds provided if clients are still connected, disconnected, or need to be removed.

This function should be called repeatedly based on the value in state_management.sync_interval.

knock(topic_id)

Sends a message out on the topic with the message_type = knock. This will trigger all listening clients to reply with an immediate heartbeat message. When each message comes back in this will naturally call the clientUpdate() function to update their state/data within the local datastore.

Internal state management data model

Client state is mananged in memory in a simple object in the form:

  • topic_id : The topic_if for which the state is being managed for.
    • client_id: The id of the client.
      • state: The current state of the client. Valid options are connected or disconnected.
      • first_seen: The unix timestamp the client was first seen as added to the topic.
      • last_seen: The unix timestamp the client was last seen on the topic - IE the last time a heartbeat message was received from the client.
      • label: The label for the client.
      • meta: An object of key/value paris containing any other meta data for the client.
{ 
	"topic1" :  {
		"client1" : {
			"first_seen" : 1459436043,
			"last_seen" : 1459436043,
			"label" : "Sample User",
			"meta" : {
				"role" : "Proctor",
				"email" : "sample@proctor.com"
			}
		},
		"client2" : {... },
		"client3" : {... }
	},
	"topic2" : {...},
	"topic3" : {...}
}

Events Emitted

client.connected

Emitted when a client has connected

client.disconnected

Emitted when a client has disconnected

client.reconnected

Emitted when a client has reconnected

client.removed

Emitted when a client has been removed.

Reserved Message Types

heartbeat

  • Message indicates the client is still on the topic and contains details about the client.

knock

  • Triggers all clients to immedtately send their heartbeat message.

client_disconnect

  • Lets all clients know they have disconnected.

client_removal

  • Lets all clients know they should be removed from the topic.

Readme

Keywords

none

Package Sidebar

Install

npm i @smarterservices/smartersockets

Weekly Downloads

6

Version

1.5.2

License

ISC

Last publish

Collaborators

  • xiupan
  • cameron_wise
  • matthew.underhill
  • smarterservicesdev
  • jasonfill