UNPKG

cct-lce

Version:
361 lines 15 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.CCT = void 0; const uuid_1 = require("uuid"); const node_fetch_1 = require("node-fetch"); const events_1 = require("events"); const socket_io_client_1 = require("socket.io-client"); const abort_controller_1 = require("abort-controller"); const Datacenter_1 = require("../@types/Datacenter"); const LCE_1 = require("./LCE"); const Util_1 = require("./Util"); const defaultSocketConfig = { reconnection: false, timeout: 5000, }; class CCT extends events_1.EventEmitter { constructor() { super(); this.allDatacenters = []; this.datacenters = []; this.runningLatency = false; this.runningBandwidth = false; this.idsToExclude = []; this.compatibleDCsWithSockets = []; this.lce = new LCE_1.LCE(); this.abortControllers = []; this.sockets = { latency: null, bandwidth: null, }; this.measurementConfigs = { latency: { type: 'latency', socketStartEvent: "latency:start", socketEndEvent: "latency:end", socketIterationEvent: "latency:iteration", socketTickEvent: "latency", iterationEvent: "latency:iteration", tickEvent: "latency", endEvent: "latency:end", getMeasurementResult: (dc) => this.lce.getLatencyFor(dc), }, bandwidth: { type: 'bandwidth', socketStartEvent: "bandwidth:start", socketEndEvent: "bandwidth:end", socketIterationEvent: "bandwidth:iteration", socketTickEvent: "bandwidth", iterationEvent: "bandwidth:iteration", tickEvent: "bandwidth", endEvent: "bandwidth:end", getMeasurementResult: (dc, params) => this.lce.getBandwidthFor(dc, params.bandwidthMode), }, }; } async fetchDatacenterInformation(dictionaryUrl = 'https://cct.demo-education.cloud.sap/datacenters?isActive=true') { try { const response = await (0, node_fetch_1.default)(dictionaryUrl); const datacenters = await response.json(); this.allDatacenters = datacenters; this.datacenters = datacenters; this.clean(); } catch (e) { this.allDatacenters = []; this.datacenters = []; } } async fetchCompatibleDCsWithSockets() { const checks = await Promise.all(this.datacenters.map(async (dc) => ({ dc, isCompatible: await this.lce.checkIfCompatibleWithSockets(dc.ip), }))); this.compatibleDCsWithSockets = checks.filter(({ isCompatible }) => isCompatible).map(({ dc }) => dc); return this.compatibleDCsWithSockets; } setFilters(filters) { this.datacenters = filters ? this.allDatacenters.filter((dc) => { if (this.idsToExclude.includes(dc.id)) { return false; } return Object.keys(filters).every((key) => { if (key === 'tags') { return filters[key].some((tag) => { return dc[key].toLowerCase().includes(tag.toLowerCase()); }); } return filters[key].map((filterVal) => filterVal.toLowerCase()).includes(dc[key].toLowerCase()); }); }) : this.allDatacenters.filter((dc) => !this.idsToExclude.includes(dc.id)); this.filters = filters; } stopMeasurements() { this.runningLatency = false; this.runningBandwidth = false; this.abortControllers.forEach((o) => { o.abort(); }); this.abortControllers = []; this.lce.terminate(); this.emit("latency:end"); this.emit("bandwidth:end"); } async startLatencyChecks(params = {}) { if (!this.compatibleDCsWithSockets.some((el) => el.id === params.from) && params.from !== undefined) { return; } if (params.from !== this.lastLatencyFrom) { this.cleanLatencyData(); this.lastLatencyFrom = params.from; } const { iterations = 16, save = true } = params; await this.startMeasurements('latency', { ...params, iterations, save }, new abort_controller_1.default()); } async startBandwidthChecks(params = {}) { if (!this.compatibleDCsWithSockets.some((el) => el.id === params.from) && params.from !== undefined) { return; } if (params.from !== this.lastBandwidthFrom) { this.cleanBandwidthData(); this.lastBandwidthFrom = params.from; } const { iterations = 4, save = true } = params; await this.startMeasurements('bandwidth', { ...params, iterations, save }, new abort_controller_1.default()); } async startMeasurements(type, params, abortController) { const config = this.measurementConfigs[type]; const stateProperty = type === 'latency' ? 'runningLatency' : 'runningBandwidth'; this[stateProperty] = true; this.abortControllers.push(abortController); const dc = params.from && this.compatibleDCsWithSockets.find((dc) => dc.id === params.from); if (dc) { await this.startCloudMeasurements(config, params, dc, abortController); } else if (this.datacenters.length !== 0) { await this.startLocalMeasurements(config, params, abortController); } if (!abortController.signal.aborted) { this[stateProperty] = false; this.emit(config.endEvent); } } setIdToExclude(ids) { this.idsToExclude = ids || []; this.setFilters(this.filters); } clearSocket(type) { const socket = this.sockets[type]; if (!socket) return; socket.emit("stop"); socket.removeAllListeners(); this.sockets[type] = null; } async startCloudMeasurements(config, params, dc, abortController) { return new Promise((resolve) => { const resolveAndClear = () => { this.clearSocket(config.type); resolve(); }; abortController.signal.addEventListener('abort', resolveAndClear); const socket = (0, socket_io_client_1.io)(`https://${dc.ip}`, { ...defaultSocketConfig, query: { id: dc.id } }); this.sockets[config.type] = socket; const events = [config.socketEndEvent, "disconnect", "connect_error"]; events.forEach((event) => socket.on(event, resolveAndClear)); socket.on("connect", () => { socket.emit(config.socketStartEvent, { ...params, id: dc.id, filters: this.filters, }); }); socket.on(config.socketIterationEvent, (data) => { this.emit(config.iterationEvent, data); }); socket.on(config.socketTickEvent, (data) => { this.handleEventData(data, params.save, config.type); this.emit(config.tickEvent, data); }); }); } async startLocalMeasurements(config, params, abortController) { let iterations = params.iterations; while (iterations-- > 0) { if (abortController.signal.aborted) return; const eventData = (await Promise.all(this.datacenters.map((dc) => this.startMeasurementFor(config, dc, params, abortController)))).filter((entry) => entry !== null); if (!abortController.signal.aborted) { this.emit(config.iterationEvent, eventData); } else { return; } if (params.interval) { await Util_1.Util.sleep(params.interval, abortController); } } } async startMeasurementFor(config, dc, params, abortController) { const result = await config.getMeasurementResult(dc, params); if (abortController.signal.aborted || result === null) return null; const data = { id: dc.id, data: result }; this.handleEventData(data, params.save, config.type); this.emit(config.tickEvent, data); return data; } handleEventData({ id, data }, save, dataType) { if (!save) return; const dcIndex = this.datacenters.findIndex((e) => e.id === id); if (dcIndex < 0) return; const dc = this.datacenters[dcIndex]; if (dataType === 'latency') { dc.latencies = dc.latencies || []; dc.latencies.push(data); const averageLatency = Util_1.Util.getAverageLatency(dc.latencies); dc.averageLatency = averageLatency; dc.latencyJudgement = Util_1.Util.judgeLatency(averageLatency); } else if (dataType === 'bandwidth') { dc.bandwidths = dc.bandwidths || []; dc.bandwidths.push(data); const averageBandwidth = Util_1.Util.getAverageBandwidth(dc.bandwidths); dc.averageBandwidth = averageBandwidth; dc.bandwidthJudgement = Util_1.Util.judgeBandwidth(averageBandwidth); } } getCurrentDatacentersSorted() { Util_1.Util.sortDatacenters(this.datacenters); return this.datacenters; } async getAddress() { const location = { address: '', latitude: 0, longitude: 0, }; return new Promise((resolve) => { if (navigator && (navigator === null || navigator === void 0 ? void 0 : navigator.geolocation)) { navigator.geolocation.getCurrentPosition(async (position) => { location.latitude = position.coords.latitude; location.longitude = position.coords.longitude; const geocoder = new google.maps.Geocoder(); await geocoder.geocode({ location: new google.maps.LatLng(location.latitude, location.longitude), }, (results, status) => { if (status === 'OK') { location.address = results[0].formatted_address; resolve(location); } else { location.address = ''; location.latitude = 0; location.longitude = 0; resolve(location); } }); }, () => { resolve(null); }); } else { resolve(null); } }); } async store(location, url = 'https://cct.demo-education.cloud.sap/measurement') { if (!location) return false; const minimumLatencyThresholdToSave = 16; const minimumBandwidthThresholdToCalculateAverage = 4; const data = []; this.datacenters.forEach((dc, index) => { const newLatencyMeasurementsCount = dc.latencies.length - (dc.storedLatencyCount || 0); if (newLatencyMeasurementsCount >= minimumLatencyThresholdToSave) { const newAverageLatency = Util_1.Util.getAverageLatency(dc.latencies, dc.storedLatencyCount); const isNewAverageBandwidthCalculable = this.lastBandwidthFrom === this.lastLatencyFrom && dc.bandwidths.length - (dc.storedBandwidthCount || 0) >= minimumBandwidthThresholdToCalculateAverage; const newAverageBandwidth = isNewAverageBandwidthCalculable ? Util_1.Util.getAverageBandwidth(dc.bandwidths, dc.storedBandwidthCount) : null; data.push({ id: dc.id, latency: newAverageLatency.toFixed(2), averageBandwidth: newAverageBandwidth && newAverageBandwidth.megaBitsPerSecond.toFixed(2), }); this.datacenters[index].storedLatencyCount = dc.latencies.length; this.datacenters[index].storedBandwidthCount = isNewAverageBandwidthCalculable ? dc.bandwidths.length : this.datacenters[index].storedBandwidthCount; } }); if (data.length === 0) { return false; } const payload = { uid: (0, uuid_1.v4)(), source_id: this.lastLatencyFrom, ...location, data, }; const body = JSON.stringify(payload, null, 4); try { const result = await (0, node_fetch_1.default)(url, { method: 'post', body: body, headers: { 'Content-Type': 'application/json' }, }); const json = await result.json(); return json.status === 'OK'; } catch (error) { return false; } } clean() { this.cleanBandwidthData(); this.cleanLatencyData(); } cleanLatencyData() { this.datacenters.forEach((dc) => { dc.position = 0; dc.averageLatency = 0; dc.latencies = []; dc.latencyJudgement = Datacenter_1.Speed.nothing; dc.storedLatencyCount = 0; }); } cleanBandwidthData() { this.datacenters.forEach((dc) => { dc.averageBandwidth = { bitsPerSecond: 0, kiloBitsPerSecond: 0, megaBitsPerSecond: 0, }; dc.bandwidths = []; dc.bandwidthJudgement = Datacenter_1.Speed.nothing; dc.storedBandwidthCount = 0; }); } async getClosestDatacenters({ latitude, longitude, top = 1, url = 'https://cct.demo-education.cloud.sap/datacenters?isActive=true', }) { if (!this.allDatacenters || !this.allDatacenters.length) { await this.fetchDatacenterInformation(url); } const datacentersWithDistances = this.allDatacenters.map((datacenter) => { const distance = Util_1.Util.calculateDistance(latitude, longitude, +datacenter.latitude, +datacenter.longitude); return { datacenter, distance }; }); datacentersWithDistances.sort((a, b) => a.distance - b.distance); const validTop = Math.min(top, this.allDatacenters.length); const topDatacenters = datacentersWithDistances.slice(0, validTop); return topDatacenters.map((entry) => entry.datacenter); } } exports.CCT = CCT; //# sourceMappingURL=CCT.js.map