Skip to content

Commit

Permalink
Merge pull request #2 from iansuny/master
Browse files Browse the repository at this point in the history
web server version
  • Loading branch information
iansuny committed Nov 28, 2016
2 parents c960b92 + 58bf5ef commit 18379c7
Show file tree
Hide file tree
Showing 59 changed files with 9,452 additions and 220 deletions.
284 changes: 65 additions & 219 deletions sense/app.js
Original file line number Diff line number Diff line change
@@ -1,239 +1,85 @@
var agent = require('./agent'),
nats = require('nats').connect(),
fs = require('fs'),
path = require("path"),
respawn = require('respawn'),
bunyan = require("bunyan"),
log = bunyan.createLogger({name: 'Sense'}),
async = require('async'),
config = require('./config');
/* Module dependencies */

var modules = {};
var statusCheckHandle;
var web = require('./web');
var debug = require('debug')('humix-sense-web:server');
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 = './modules/core/';

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();
server.listen(port);
server.on('error', onError);
server.on('listening', onListening);

}).forEach(function (m) {
log.info("module : %s", m);
/* Normalize a port into a number, string, or false */

var p = respawn(['npm','start'],{cwd:m});
function normalizePort(val) {
var port = parseInt(val, 10);

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);
}
}
});
if (isNaN(port)) {
// named pipe
return val;
}

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

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();
return false;
}


function startStatusCheck() {

return setInterval(function () {

var moduleStatus = [];

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);
});
}


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);


/* 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':
console.error(bind + ' requires elevated privileges');
process.exit(1);
break;
case 'EADDRINUSE':
console.error(bind + ' is already in use');
process.exit(1);
break;
default:
throw error;
}
}

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');
}
});
/* Event listener for HTTP server "listening" event */

// 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);

}

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

agent.publish('humix-think', 'registerModule', requestModule);
console.log('##### 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,13 +12,19 @@
"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",
"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

0 comments on commit 18379c7

Please sign in to comment.