Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Web Interface for Humix Sense #4

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added bin/gnatsd-darwin-old
Binary file not shown.
278 changes: 65 additions & 213 deletions sense/app.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/* Module dependencies */
var agent = require('./agent'),
nats = require('nats').connect(),
fs = require('fs'),
Expand All @@ -7,234 +8,85 @@ var agent = require('./agent'),
async = require('async'),
config = require('./config');

var modules = {};
var statusCheckHandle;
var web = require('./web');
var http = require('http');
var fs = require('fs');
var sense = require('./sense');

/* Get port from environment and store in Express. */

/* Constants */
var port = normalizePort(process.env.PORT || '3000');
web.set('port', port);

var STATUS_CHECK_INTERVAL = 3000;
var STATUS_CHECK_TIMEOUT = 5000;
/* Create HTTP server */

var server = http.createServer(web);

process.on('SIGTERM', function() {
if (agent.getState() === 'RUNNING') {
agent.stop();
}
});
/* Listen on provided port, on all network interfaces */

var senseId = process.argv[2] || undefined;
if (!senseId) {
senseId = config.senseId || 'humix';
}

humixSenseInit();

try {
agent.init(config.thinkURL, senseId, {autoreconnect: true, logger: log});
agent.start();

} catch (e) {
log.error('Error: '+e);
}

function humixSenseInit(){
if (config.log.file) {
var level = config.log.level || 'info';
log.addStream({path: config.log.file, level: level});
}

log.info("Init Humix Sense");

// starting core modules

var coreModulePath = path.join(__dirname,'modules/core/');

if(fs.existsSync(coreModulePath)){
fs.readdir(coreModulePath,function(err, coreModules){

if(err){

log.error('Failed to read core modules. Error:'+err);
return;
}

coreModules.map(function (m) {
return path.join(coreModulePath, m);

}).filter(function (m) {
return fs.statSync(m).isDirectory();

}).forEach(function (m) {
log.info("module : %s", m);

var p = respawn(['npm','start'],{cwd:m});

p.on('stdout', function(data) {
// sometimes sense module emit multiple JSON logs to this,
// so need split each JSON and output to file/console
data.toString().split('\n').forEach(function(e,i,a) {
try {
// quick quess if data is JSON object received from bunyan logging style
var m = JSON.parse(e);
log.info(m.msg);
} catch (err) {
if (e.trim().length > 0) {
log.info(e);
}
}
});

});

p.on('stderr', function(data) {
data.toString().split('\n').forEach(function(e,i,a) {
try {
var m = JSON.parse(e);
log.error(m.msg);
} catch (err) {
if (e.trim().length > 0) {
log.error(e);
}
}
});
});

p.on('spawn', function () {
log.info('process spawned')
});

p.on('exit', function (code, signal) {
log.error({msg: 'process exited, code: ' + code + ' signal: ' + signal});

});
p.start();
});

});
}
statusCheckHandle = startStatusCheck();
}


function startStatusCheck() {

return setInterval(function () {

var moduleStatus = [];
server.listen(port);
server.on('error', onError);
server.on('listening', onListening);

var myPromise = function (ms, module, callback) {
return new Promise(function(resolve, reject) {
callback(resolve, reject);
setTimeout(function() {
reject('Status Check promise timed out after ' + ms + ' ms');
}, ms);
});
}
/* Normalize a port into a number, string, or false */

function normalizePort(val) {
var port = parseInt(val, 10);

async.eachSeries(Object.keys(modules), function (module, cb) {

log.info('checking status of ' + module);

myPromise(STATUS_CHECK_TIMEOUT, module, function (resolve, reject) {
var t = 'humix.sense.mgmt.' + module + ".ping";
nats.request(t, null, { 'max': 1 }, function (res) {
resolve('success');

})

}).then(function (result) {
log.info('connection with module ' + module + " succeed");
moduleStatus.push({ moduleId: module, status: 'connected' });
cb(null);
}).catch(function () {
log.info('connection with module ' + module + " failed");
moduleStatus.push({ moduleId: module, status: 'disconnected' });
cb(null);
})

}, function (err) {
agent.publish('humix-think', 'module.status', moduleStatus);
});

},STATUS_CHECK_INTERVAL);
if (isNaN(port)) {
// named pipe
return val;
}

if (port >= 0) {
// port number
return port;
}
return false;

}

agent.events.on('module.command', function(data) {

log.info('Command: '+JSON.stringify(data));

var module = data.commandType;
var command = data.commandName;
var topic = 'humix.sense.'+module+'.command.'+command;

log.info('topic: '+topic + ', data: '+JSON.stringify(data.commandData));

if(modules.hasOwnProperty(module) && modules[module].commands.indexOf(command) != -1 ){

if (data.syncCmdId) {

// TODO: handle timeout here
data.commandData.syncCmdId = data.syncCmdId;
nats.request(topic, JSON.stringify(data.commandData), { 'max': 1 }, function (res) {
agent.publish_syncResult(data.syncCmdId, res);

})
} else {
nats.publish(topic, JSON.stringify(data.commandData));
}

log.debug('publish command');

}else{

log.info('skip command');
}
});

// handle module registration
nats.subscribe('humix.sense.mgmt.register', function(request, replyto){
log.info("Receive registration :"+ request);

var requestModule = JSON.parse(request);

if(modules.hasOwnProperty(requestModule.moduleName)){
log.info('Module [' + requestModule.moduleName + '] already register. Skip');
nats.publish(replyto,'module already registered');
return;
}

modules[requestModule.moduleName] = requestModule;


var eventPrefix = 'humix.sense.'+requestModule.moduleName+".event";

for ( var i in requestModule.events){

var event = requestModule.events[i];
var module = requestModule.moduleName;
var topic = eventPrefix + "." + event;

log.debug("subscribing topic:"+ topic);

(function(topic,module,event){

nats.subscribe(topic, function(data){
log.debug('about to publish topic:'+topic+", data:"+data);
agent.publish(module, event, data);
});
})(topic,module,event);
/* Event listener for HTTP server "error" event */

function onError(error) {
if (error.syscall !== 'listen') {
throw error;
}

var bind = typeof port === 'string'
? 'Pipe ' + port
: 'Port ' + port;

// handle specific listen errors with friendly messages
switch (error.code) {
case 'EACCES':
log.error(bind + ' requires elevated privileges. Abort');
process.exit(1);
break;
case 'EADDRINUSE':
log.error(bind + ' is already in use. Abort');
process.exit(1);
break;
default:
throw error;
}
}

}
/* Event listener for HTTP server "listening" event */

// register the module to humix-think
function onListening() {
var addr = server.address();
var bind = typeof addr === 'string'
? 'pipe ' + addr
: 'port ' + addr.port;
log.info('Listening on ' + bind);
}

agent.publish('humix-think', 'registerModule', requestModule);
log.info('##### Web Server Started #####');

log.debug('current modules:'+JSON.stringify(modules));
nats.publish(replyto,'module registration succeed');
// determine whether config is already set or not
var init = JSON.parse(fs.readFileSync('config.json', 'utf8')).init;
if (!init){
sense.humixSenseStart();
}

});
9 changes: 9 additions & 0 deletions sense/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"init": true,
"thinkURL": "https://todo-bot.mybluemix.net",
"senseId": "alpha",
"log": {
"file": "./sense.log",
"level": "debug"
}
}
6 changes: 6 additions & 0 deletions sense/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,20 @@
"dependencies": {
"assert": "latest",
"async": "latest",
"body-parser": "^1.15.2",
"bunyan": "latest",
"cookie-parser": "^1.4.3",
"ejs": "^2.5.2",
"express": "^4.14.0",
"humix-logger": "^1.0.4",
"mocha": "latest",
"morgan": "^1.7.0",
"nats": "^0.4.4",
"node-uuid": "latest",
"path": "^0.12.7",
"request": "^2.40.0",
"respawn": "^2.3.0",
"serve-favicon": "^2.3.2",
"should": "latest",
"ws": "0.7.2"
}
Expand Down
4 changes: 4 additions & 0 deletions sense/public/css/font-awesome.min.css

Large diffs are not rendered by default.

48 changes: 48 additions & 0 deletions sense/public/css/ie8.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/* Type */

body, input, select, textarea {
color: #ffffff;
}

/* Button */

input[type="submit"],
input[type="reset"],
input[type="button"],
button,
.button {
position: relative;
}

input[type="submit"]:after,
input[type="reset"]:after,
input[type="button"]:after,
button:after,
.button:after {
display: none;
}

/* Features */

.features {
border: solid 1px;
}

/* Form */

input[type="text"],
input[type="password"],
input[type="email"],
input[type="tel"],
select,
textarea {
background: transparent;
border: solid 1px;
}

/* Split */

.split.style1 > :first-child {
padding-right: 2em;
width: 70%;
}
Loading