UNPKG

pomeloes-robot

Version:

A fully modernized pomelo-robot, upgraded with ES6+ and latest dependencies. Now supports standalone mode.

166 lines (142 loc) 5.77 kB
const { Server: SocketIOServer } = require('socket.io'); const { NodeClient } = require('./nodeclient.js'); const { WebClient } = require('./webclient.js'); const logging = require('../common/logging').Logger; const stat = require('../monitor/stat'); const starter = require('./starter'); const STATUS_INTERVAL = 60 * 1000; // 60 seconds const HEARTBEAT_INTERVAL = 30 * 1000; // 30 seconds const STATUS_IDLE = 0; const STATUS_READY = 1; const STATUS_RUNNING = 2; class Server { constructor(conf) { this.log = logging; this.nodes = {}; this.web_clients = {}; this.conf = conf || {}; this.runconfig = null; this.status = STATUS_IDLE; setInterval(() => { this.log.info(`Nodes: ${Object.keys(this.nodes).length}, WebClients: ${Object.keys(this.web_clients).length}`); }, STATUS_INTERVAL); } listen(port) { this.io = new SocketIOServer(port, { allowEIO3: true, // Enable backward compatibility for Engine.IO v3 (socket.io v2 clients) cors: { origin: (origin, callback) => { // For a local development tool, reflecting the origin is a safe and flexible approach. callback(null, origin); }, methods: ["GET", "POST"], credentials: true } }); this.register(); } announce_node(socket, message) { const { nodeId } = message; if (this.nodes[nodeId]) { this.log.warn(`Warning: Node '${nodeId}' already exists, deleting old items`); socket.emit('node_already_exists'); delete this.nodes[nodeId]; } const node = new NodeClient(nodeId, socket, this); this.nodes[nodeId] = node; Object.values(this.web_clients).forEach(web_client => { web_client.add_node(node); }); socket.on('disconnect', () => { delete this.nodes[nodeId]; Object.values(this.web_clients).forEach(web_client => { web_client.remove_node(node); }); if (Object.keys(this.nodes).length <= 0) { this.status = STATUS_IDLE; } stat.clear(nodeId); }); socket.on('report', (message) => { stat.merge(nodeId, message); }); socket.on('error', (message) => { Object.values(this.web_clients).forEach(web_client => { web_client.error_node(node, message); }); }); socket.on('crash', (message) => { Object.values(this.web_clients).forEach(web_client => { web_client.error_node(node, message); }); this.status = STATUS_READY; }); } announce_web_client(socket) { const web_client = new WebClient(socket, this); this.web_clients[web_client.id] = web_client; Object.values(this.nodes).forEach(node => { web_client.add_node(node); }); setInterval(() => { this.io.to('web_clients').emit('statusreport', { status: this.status }); }, STATUS_INTERVAL / 10); socket.on('webreport', () => { if (this.status === STATUS_RUNNING) { socket.emit('webreport', this.runconfig.agent, this.runconfig.maxuser, stat.getTimeData(this), stat.getCountData()); } }); socket.on('detailreport', () => { if (this.status === STATUS_RUNNING) { socket.emit('detailreport', stat.getDetails()); } }); socket.on('disconnect', () => { delete this.web_clients[web_client.id]; }); } register() { this.io.on('connection', (socket) => { socket.on('announce_node', (message) => { this.log.info(`Registering new node ${JSON.stringify(message)}`); this.announce_node(socket, message); }); socket.on('announce_web_client', () => { this.log.info("Registering new web_client"); this.announce_web_client(socket); socket.on('run', (msg) => { stat.clear(); msg.agent = Object.keys(this.nodes).length; console.log('server begin notify client to run machine...'); this.runconfig = msg; Object.values(this.nodes).forEach((ele, i) => { msg.index = i; ele.socket.emit('run', msg); }); this.status = STATUS_RUNNING; }); socket.on('ready', (msg) => { console.log('server begin ready client ...'); // In socket.io v4, emitting 'disconnect' is forbidden. // The correct way to disconnect all clients in a room is to use disconnectSockets(). this.io.in('nodes').disconnectSockets(true); stat.clear(); this.status = STATUS_READY; this.runconfig = msg; // Pass the entire configuration object to the starter starter.run(this.conf.mainFile, msg, this.conf); }); socket.on('exit4reready', () => { Object.values(this.nodes).forEach(obj => { obj.socket.emit('exit4reready'); }); this.nodes = {}; }); }); }); setInterval(() => { this.io.emit('heartbeat'); }, HEARTBEAT_INTERVAL); } } exports.Server = Server;