UNPKG

awv3

Version:
247 lines (211 loc) 8.46 kB
import Base from './base'; import { createContext, handleResult } from '../core/parser'; import Parser from '../core/parser'; export default class SignalR extends Base { constructor(options = {}) { super(options); this.ping = options.ping || 10000; this.onPause = undefined; this.onResume = undefined; this._connection; this._heartbeat; this._timeout; this._queueBlock; this._currentResolve; this._currentReject; this._queue = []; this._sequence = Promise.resolve(); this._handler = null; this._defaultContext = createContext(); this._defaultHandler = async data => { await handleResult(this._defaultContext, data); this._defaultContext.promises = []; this._defaultContext.results = []; }; this.connected = false; this.paused = false; this.transport = ''; this.serverState = undefined; // Initialize hub let hub = require('../communication/signalrhub').default; hub(this); this._proxy.clientHub.client.result = data => { // Parse data into JSON var obj = JSON.parse(data); if (!this.connected) { if (obj.command == 'PERMISSION') { this.connected = true; this.transport = obj.transport; this._currentResolve(this); } else if (obj.command == 'WAIT') { this.transport = obj.transport; } } else { if (obj.command === 'Service') { // Connection to a ClassCAD instance has been paused if (obj.event === 'Pause') { this.paused = true; this.serverState = obj.state; if (!!this.onPause) this.onPause(obj); // Connection to ClassCAD is reestablished } else if (obj.event === 'Resume') { this.paused = false; if (!!this.onResume) this.onResume(obj); } } else (this._handler || this._defaultHandler)(obj); } }; this._proxy.clientHub.client.disconnect = () => { if (this._heartbeat) clearInterval(this._heartbeat); this._hub.stop(); this.connected = false; }; this._proxy.clientHub.client.debug = message => { console.log(message); }; this._proxy.clientHub.client.queueNext = () => { var message = this._queue.shift(); if (message !== undefined) this._proxy.clientHub.server.send(message); else this._queueBlock = true; }; this._hub.disconnected(() => { if (this._heartbeat) clearInterval(this._heartbeat); if (this._timeout) clearTimeout(this._timeout); this.connected = false; this._currentReject(this._hub.url + ' not found'); }); } connect(url = this._hub.url) { console.log('connecting to ' + url); if (this.connected) return Promise.reject('Disconnect first!'); var first; /*if (!!this.options.loadBallanced) { first = this.canvas.parser.stream(url + '/ip').then(context => { if (context.results.length > 0) { var scaledUri = context.results[0].result.url + '/signalr'; return scaledUri; } }).catch(reason => Promise.reject("Supply URL!")); } else*/ first = Promise.resolve(url + '/signalr'); return first.then(url => { if (!url) return Promise.reject('Supply URL!'); if (this._timeout) clearTimeout(this._timeout); this._timeout = setTimeout( function() { // Slots occupied }, 4000 ); // Link URL this._hub.url = url; // Start hub this._hub.start(this.options).done(() => { this._proxy.clientHub.server.init( false, !!this.options.pause, !!this.options.timeOut ? this.options.timeOut : 0, !!this.options.rebuild ? this.options.rebuild : false ); if (this._heartbeat) clearInterval(this._heartbeat); this._heartbeat = setInterval( () => { this._proxy.clientHub.server.ping(); }, this.ping ); }); // Return promise return new Promise((resolve, reject) => { this._currentResolve = resolve; this._currentReject = reject; }); }); } disconnect() { if (!this.connected) return; if (this._proxy.clientHub !== undefined) { this._proxy.clientHub.client.disconnect(); this._currentResolve = null; this._currentReject = null; this.connected = false; if (this._timeout) clearTimeout(this._timeout); } } send(message) { if (this.transport === 'webSockets' || this._queueBlock) { this._queueBlock = false; this._proxy.clientHub.server.send(message); } else { this._queue.push(message); } } request(command, factory, timeout) { if (!this.connected) return Promise.reject('Not connected!'); // If we're pause, we need to load our previous state if (this.paused && !!this.serverState) { var state = this.serverState; this.serverState = undefined; this.setState(state, !!this.options.rebuild, !!this.options.rebuild); } command = Array.isArray(command) ? command : [command]; var action = () => { return new Promise((resolve, reject) => { var timeout = setTimeout(reject, timeout || 120000), context = createContext(factory, resolve, reject, command); context.options.callback({ type: Parser.Factory.Started, context }); // Override result callback for each transaction & handle all incoming packages this._handler = data => handleResult(context, data); this.send( JSON.stringify({ command: 'BeginFrame', transactionID: context.id }) ); for (let item of command) this.send(JSON.stringify(item)); this.send( JSON.stringify({ command: 'EndFrame', transactionID: context.id }) ); }) .then(results => results, failure => failure) .then(results => { results.options.callback({ type: Parser.Factory.Finished, context: results }); // Clean up and return context clearTimeout(this._timeout); this._handler = null; return results; }); }; // Fullfil last transaction, then queue next this._sequence = this._sequence.then(action, action); return this._sequence; } } SignalR.eliminate = (urls, options) => { if (urls.length === 1) return new SignalR(options).connect(urls.shift()); let first = urls.shift(); return urls.reduce( function(sequence, url) { return sequence.then(server => server, () => new SignalR(options).connect(url)); }, new SignalR(options).connect(first) ); }; SignalR.race = (urls, options) => { return new Promise(function(resolve, reject) { let count = 0, winner; let connections = urls.map(function(url) { return new SignalR(options) .connect(url) .then(item => !winner ? resolve(winner = item) : item.disconnect()) .catch(() => { if (++count === urls.length) reject(); }); }); }); };