1 | /* |
||
2 | * |
||
3 | * Description: |
||
4 | * This script is the Node.js server for OpenROV. It creates a server and instantiates an OpenROV |
||
5 | * and sets the interval to grab frames. The interval is set with the DELAY variable which is in |
||
6 | * milliseconds. |
||
7 | * |
||
8 | */ |
||
9 | // To eliminate hard coding paths for require, we are modifying the NODE_PATH to include our lib folder |
||
10 | var oldpath = ''; |
||
11 | if (process.env.NODE_PATH !== undefined) { |
||
12 | oldpath = process.env.NODE_PATH; |
||
13 | } |
||
14 | // Just in case already been set, leave it alone |
||
15 | process.env.NODE_PATH = __dirname + '/lib:' + oldpath; |
||
16 | require('module').Module._initPaths(); |
||
17 | |||
18 | // Add cockpit source dir to the environment variables |
||
19 | process.env[ "COCKPIT_PATH" ] = __dirname; |
||
0 ignored issues
–
show
|
|||
20 | |||
21 | // Set default logging options |
||
22 | // process.env.logger.debug( = "log*,error*," + process.env.logger.debug(; |
||
23 | |||
24 | |||
25 | var logger = require('AppFramework.js').logger; |
||
26 | //var trace = require('pino-trace')(logger); |
||
27 | |||
28 | |||
29 | logger.debug('Set NODE_PATH to: ' + process.env.NODE_PATH); |
||
30 | // Handle linux signals |
||
31 | if (process.platform === 'linux') { |
||
32 | process.on('SIGTERM', function () { |
||
33 | logger.error('got SIGTERM, shutting down...'); |
||
34 | process.exit(0); |
||
35 | }); |
||
36 | process.on('SIGINT', function () { |
||
37 | logger.error('got SIGINT, shutting down...'); |
||
38 | process.exit(0); |
||
39 | }); |
||
40 | } |
||
41 | |||
42 | var frameworkDeps = { |
||
43 | logging: logger |
||
44 | } |
||
0 ignored issues
–
show
There should be a semicolon.
Requirement of semicolons purely is a coding style issue since JavaScript has specific rules about semicolons which are followed by all browsers. Further Readings: ![]() |
|||
45 | |||
46 | require('systemd'); |
||
47 | var includesPollyfill=require("array-includes-pollyfill.js").enable(); |
||
48 | var CONFIG = require('./lib/config')(frameworkDeps); |
||
49 | var fs = require('fs'); |
||
50 | var express = require('express'); |
||
51 | var app = express(); |
||
52 | var server = require('http').createServer(app); |
||
53 | var io = require('socket.io').listen(server, { |
||
54 | log: false, |
||
55 | origins: '*:*', |
||
56 | path: '/cockpitsocket' |
||
57 | }); |
||
58 | var EventEmitter = require('events').EventEmitter; |
||
59 | var EventEmitter2 = require('eventemitter2').EventEmitter2; |
||
60 | var mkdirp = require('mkdirp'); |
||
61 | var path = require('path'); |
||
62 | var PluginLoader = require('./lib/PluginLoader'); |
||
63 | var CockpitMessaging = require('./lib/CockpitMessaging'); |
||
64 | var Q = require('q'); |
||
65 | var serveIndex = require('serve-index'); |
||
66 | var favicon = require('serve-favicon'); |
||
67 | var methodOverride = require('method-override'); |
||
68 | var session = require('express-session'); |
||
69 | var bodyParser = require('body-parser'); |
||
70 | var multer = require('multer'); |
||
71 | var errorHandler = require('errorhandler'); |
||
72 | var pluginFolder = CONFIG.preferences.get('pluginsDownloadDirectory'); |
||
73 | var cacheFolder = CONFIG.preferences.get('cacheDirectory'); |
||
74 | var Promise = require('bluebird'); |
||
75 | var rimrafAsync = Promise.promisify(require('rimraf')); |
||
76 | var mkdirpAsync = Promise.promisify(require('mkdirp')); |
||
77 | var COCKPIT_VERSION; |
||
78 | |||
79 | // Setup required directories |
||
80 | mkdirp(CONFIG.preferences.get('photoDirectory')); |
||
81 | mkdirp(cacheFolder); |
||
82 | if (!process.env.NODE_ENV){ |
||
83 | process.env.NODE_ENV = "production"; |
||
84 | } |
||
85 | |||
86 | |||
87 | // NOTE: If you don't increase the default max listeners, you can get a potential memory leak warning |
||
88 | var globalEventLoop = require('./static/js/eventEmitterStoreAndForward.js')(new EventEmitter2()); |
||
89 | globalEventLoop.setMaxListeners(20); |
||
90 | var DELAY = Math.round(1000 / CONFIG.video_frame_rate); |
||
91 | io = require('./static/js/socketIOStoreAndForward.js')(io); |
||
92 | var client = new CockpitMessaging(io); |
||
93 | client = require('./static/js/eventEmitterStoreAndForward.js')(client); |
||
94 | |||
95 | // --------------------------------------------------------------- |
||
96 | // Setup Express App |
||
97 | // --------------------------------------------------------------- |
||
98 | app.use(express.static(__dirname + '/static/')); |
||
99 | app.use(bodyParser.json()); |
||
100 | app.use(bodyParser.urlencoded({ extended: true })); |
||
101 | app.set('port', process.env.LISTEN_FDS > 0 ? 'systemd' : CONFIG.port); |
||
102 | app.set('views', '/'); |
||
103 | app.set('view engine', 'ejs', { pretty: true }); |
||
104 | app.use(favicon(__dirname + '/static/favicon.ico')); |
||
105 | |||
106 | // Temporary placeholder until new logging infrastructure implemented |
||
107 | if( process.env.ENABLE_EXPRESS_LOGS == 'true' ) |
||
108 | { |
||
109 | app.use(logger('dev')); |
||
110 | } |
||
111 | |||
112 | app.use('/components', express.static(path.join(__dirname, 'static/bower_components'))); |
||
113 | app.use('/components', express.static(pluginFolder)); |
||
114 | app.use('/components', express.static(path.join(__dirname, 'static/webcomponents'))); |
||
115 | app.use('/components', express.static(path.join(__dirname, 'plugins/telemetry/public/bower_components'))); |
||
116 | app.use('/components/telemetry', express.static(path.join(__dirname, 'plugins/telemetry/public/webcomponents'))); |
||
117 | app.use('/components/telemetry', serveIndex(path.join(__dirname, 'plugins/telemetry/public/webcomponents'))); |
||
118 | |||
119 | logger.debug( '!!!' + path.join(__dirname, 'src/static/bower_components') ); |
||
120 | |||
121 | app.get('/config.js', function (req, res) { |
||
122 | res.type('application/javascript'); |
||
123 | res.send('var CONFIG = ' + JSON.stringify(CONFIG)); |
||
124 | }); |
||
125 | //socket.io cross domain access |
||
126 | app.use(function (req, res, next) { |
||
127 | res.header('Access-Control-Allow-Origin', '*'); |
||
128 | res.header('Access-Control-Allow-Headers', 'X-Requested-With'); |
||
129 | res.header('Access-Control-Allow-Headers', 'Content-Type'); |
||
130 | res.header('Access-Control-Allow-Methods', 'PUT, GET, POST, DELETE, OPTIONS'); |
||
131 | next(); |
||
132 | }); |
||
133 | // --------------------------------------------------------------- |
||
134 | // Keep track of plugins js and css to load them in the view |
||
135 | var scripts = [], styles = [], applets = [], sysscripts = [], webcomponents = []; |
||
136 | var pathInfo = function () { |
||
137 | return { |
||
138 | scripts: scripts, |
||
139 | styles: styles, |
||
140 | sysscripts: sysscripts, |
||
141 | applets: applets, |
||
142 | webcomponents: webcomponents |
||
143 | }; |
||
144 | }; |
||
145 | // Prepare dependency map for plugins |
||
146 | var deps = { |
||
147 | server: server, |
||
148 | app: app, |
||
149 | cockpit: client, |
||
150 | config: CONFIG, |
||
151 | globalEventLoop: globalEventLoop, |
||
152 | loadedPlugins: [], |
||
153 | pathInfo: pathInfo, |
||
154 | logger: logger |
||
155 | }; |
||
156 | var numConnections = 0; |
||
157 | var socketConnectToken = null; |
||
158 | // Handle socket.io events |
||
159 | io.on('connection', function (client) { |
||
160 | if (socketConnectToken == null) { |
||
0 ignored issues
–
show
It is recommended to use
=== to compare with null .
Generally, it is recommended to use strict comparison whenever possible and not to rely on the weaker type-juggling comparison operator. ![]() |
|||
161 | socketConnectToken = client.id; |
||
162 | } |
||
163 | numConnections++; |
||
164 | |||
165 | logger.debug('HASTHEBALL:TRUE'); |
||
166 | |||
167 | client.hastheball = true; |
||
168 | client.emit('hastheball', socketConnectToken); |
||
169 | client.on('request-sessionToken', function (callback) { |
||
170 | callback(socketConnectToken); //TODO: On force, kill the other inbound connections |
||
171 | }); |
||
172 | |||
173 | logger.debug('Connection detected'); |
||
174 | logger.debug('Current connections: ' + numConnections); |
||
175 | |||
176 | client.on('disconnect', function () { |
||
177 | }); |
||
178 | }); |
||
179 | io.use(function (socket, next) { |
||
180 | logger.debug('Auth expecting %s. got %s', socketConnectToken == null ? '<NULL>' : socketConnectToken, socket.handshake.query.token == undefined ? '<UNDEFINED>' : socket.handshake.query.token); |
||
0 ignored issues
–
show
It is recommended to use
=== to compare with null .
Generally, it is recommended to use strict comparison whenever possible and not to rely on the weaker type-juggling comparison operator. ![]() It is recommended to use
=== to compare with undefined .
Generally, it is recommended to use strict comparison whenever possible and not to rely on the weaker type-juggling comparison operator. ![]() |
|||
181 | |||
182 | // return the result of next() to accept the connection. |
||
183 | if (socketConnectToken == null || socketConnectToken == socket.handshake.query.token || socket.handshake.query.token == 'reset') { |
||
0 ignored issues
–
show
It is recommended to use
=== to compare with null .
Generally, it is recommended to use strict comparison whenever possible and not to rely on the weaker type-juggling comparison operator. ![]() |
|||
184 | if (socket.handshake.query.token == 'reset') { |
||
185 | socketConnectToken = null; |
||
186 | //And kick anyone already connected |
||
187 | // if (typeof(io.sockets.server.eio.clients) == 'Array'){ |
||
188 | var socketCollection = io.sockets.connected; |
||
189 | Object.keys(socketCollection).forEach(function (key) { |
||
190 | var client = socketCollection[key]; |
||
191 | if (client.id !== socket.id) { |
||
192 | logger.debug('kicking out:', client.id); |
||
193 | setTimeout(function () { |
||
194 | client.emit('forced-disconnect'); |
||
195 | client.disconnect(); |
||
196 | }, 1000); |
||
197 | } |
||
198 | }); // } |
||
199 | } |
||
200 | return next(); |
||
201 | } |
||
202 | // call next() with an Error if you need to reject the connection. |
||
203 | next(new Error('Authentication error')); |
||
204 | }); |
||
205 | deps.cockpit.on('disconnect', function () { |
||
206 | numConnections--; |
||
207 | if (numConnections == 0) { |
||
0 ignored issues
–
show
It is recommended to use
=== to compare with 0 .
Generally, it is recommended to use strict comparison whenever possible and not to rely on the weaker type-juggling comparison operator. ![]() |
|||
208 | socketConnectToken = null; |
||
209 | } |
||
210 | logger.debug('Disconnect detected'); |
||
211 | logger.debug('Current connections: ' + numConnections); |
||
212 | }); |
||
213 | // Handle global events |
||
214 | deps.globalEventLoop.on('mcu.rovsys', function (data) { |
||
215 | deps.cockpit.emit('mcu.rovsys', data); |
||
216 | }); |
||
217 | // ----------------------------------------------------------------------- |
||
218 | // Load Plugins |
||
219 | // ----------------------------------------------------------------------- |
||
220 | mkdirp.sync(pluginFolder); |
||
221 | |||
222 | var options={ |
||
223 | cacheFile:path.join(cacheFolder,'cachedLoadPluginsResults_systemPlugins.json'), |
||
224 | required:true |
||
225 | } |
||
0 ignored issues
–
show
There should be a semicolon.
Requirement of semicolons purely is a coding style issue since JavaScript has specific rules about semicolons which are followed by all browsers. Further Readings: ![]() |
|||
226 | var loaderA = new PluginLoader(path.join(__dirname, 'system-plugins'), 'system-plugin', deps, options); |
||
227 | |||
228 | options.cacheFile=path.join(cacheFolder,'cachedLoadPluginsResults_plugins.json'); |
||
229 | var loaderB = new PluginLoader(path.join(__dirname, 'plugins'), 'plugin', deps,options); |
||
230 | |||
231 | options.required=false; |
||
232 | options.filter = function (file) { |
||
233 | return file.substring(0, 15) === 'openrov-plugin-'; |
||
234 | } |
||
0 ignored issues
–
show
There should be a semicolon.
Requirement of semicolons purely is a coding style issue since JavaScript has specific rules about semicolons which are followed by all browsers. Further Readings: ![]() |
|||
235 | options.cacheFile=path.join(cacheFolder,'cachedLoadPluginsResults_community.json'); |
||
236 | var loaderC = new PluginLoader(pluginFolder, 'community-plugin', deps,options ); |
||
237 | |||
238 | |||
239 | // Performance optimization, attempt to read from a cache of the plugins instead, fallback |
||
240 | // if not available. |
||
241 | |||
242 | var plugins = []; |
||
243 | |||
244 | loaderA.loadPluginsAsync(plugins) |
||
245 | .then( loaderB.loadPluginsAsync.bind( loaderB ) ) |
||
246 | .then( loaderC.loadPluginsAsync.bind( loaderC ) ) |
||
247 | .each(function(results) |
||
248 | { |
||
249 | addPluginAssets(results); |
||
250 | }) |
||
251 | .then(function () |
||
252 | { |
||
253 | //logger.debug('Starting following plugins:'); |
||
254 | //logger.debug(deps.loadedPlugins); |
||
255 | |||
256 | // Start each plugin |
||
257 | deps.loadedPlugins.forEach(function (plugin) |
||
258 | { |
||
259 | if (plugin.start !== undefined) |
||
260 | { |
||
261 | plugin.start(); |
||
262 | } |
||
263 | }); |
||
264 | }) |
||
265 | .then(function () |
||
266 | { |
||
267 | logger.debug('Plugin loading successful!'); |
||
268 | |||
269 | // Start the web server |
||
270 | server.listen(app.get('port'), function () |
||
271 | { |
||
272 | logger.info('Started listening on port: ' + app.get('port')); |
||
273 | }); |
||
274 | }) |
||
275 | .catch(function (err) |
||
276 | { |
||
277 | logger.error(err,'Error starting plugins: '); |
||
278 | process.abort(); |
||
279 | }); |
||
280 | // Helper function |
||
281 | function addPluginAssets(result) { |
||
282 | scripts = scripts.concat(result.scripts); |
||
283 | |||
284 | logger.debug('====== Scripts ======'); |
||
285 | logger.debug(result.scripts); |
||
286 | |||
287 | result.scripts.forEach(function (asset) |
||
288 | { |
||
289 | logger.debug('SCRIPT: ' + asset); |
||
290 | }); |
||
291 | |||
292 | styles = styles.concat(result.styles); |
||
293 | |||
294 | webcomponents = webcomponents.concat(result.webcomponents); |
||
295 | |||
296 | result.assets.forEach(function (asset) |
||
297 | { |
||
298 | logger.debug('TEST: ' + asset.path); |
||
299 | logger.debug( JSON.stringify( asset ) ); |
||
300 | app.use('/' + asset.path, express.static(asset.assets)); |
||
301 | }); |
||
302 | |||
303 | applets = applets.concat(result.applets); |
||
304 | |||
305 | if (result.plugins !== undefined) |
||
306 | { |
||
307 | deps.loadedPlugins = deps.loadedPlugins.concat(result.plugins); |
||
308 | } |
||
309 | } // ------------------------------------------------------------------------ |
||
310 |
You can rewrite this statement in dot notation: