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 server version #2

Merged
merged 1 commit into from
Nov 28, 2016
Merged
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
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