pando-computing
Version:
Distribute processing of a stream of items to volunteers on the web.
230 lines (200 loc) • 6.4 kB
JavaScript
var package = require('../package.json')
var Socket = require('simple-websocket')
var log = require('debug')('pando-computing')
function display (info) {
if (typeof window === 'undefined') return
document.getElementById('throughput').value = info.throughput
document.getElementById('cpu-usage').value = info.cpuUsage
document.getElementById('data-transfer-load').value = info.dataTransferLoad
}
var id = Math.floor(Math.random() * Math.pow(2,32)).toString(16)
if (id.length % 2) { id = '0' + id }
var startTime = new Date()
var nbItems = 0
var units = 'items'
var throughputs = []
var cpuUsages = []
var dataTransferLoads = []
var cpuTime = 0
var dataTransferTime = 0
var interval = 3000
if (typeof window !== 'undefined' &&
typeof window.pando !== 'undefined' &&
typeof window.pando.config !== 'undefined') {
interval = window.pando.config.reportingInterval
}
function sum (a) {
var s = 0
for (var i = 0; i < a.length; ++i) {
s += a[i]
}
return s
}
function average (a) {
return sum(a)/a.length
}
function standardDeviation (a) {
var avg = average(a)
var deviations = 0
for (var i = 0; i < a.length; ++i) {
deviations += Math.abs(a[i] - avg)
}
return deviations / a.length
}
function maximum (a) {
var max = -Infinity
for (var i = 0; i < a.length; ++i) {
if (a[i] > max) {
max = a[i]
}
}
return max
}
function minimum (a) {
var min = Infinity
for (var i = 0; i < a.length; ++i) {
if (a[i] < min) {
min = a[i]
}
}
return min
}
setInterval(function () {
var status = {
id: id,
cpuTime: cpuTime,
dataTransferTime: dataTransferTime,
nbItems: nbItems,
units: units,
deviceName: deviceName,
throughput: 0,
throughputStats: { },
cpuUsage: 0,
cpuUsageStats: { },
dataTransferLoad: 0,
dataTransferStats: {}
}
var endTime = new Date()
var duration = endTime - startTime
startTime = endTime
var throughput = nbItems/(duration / 1000)
var avg = Number(average(throughputs)).toFixed(2)
var std = Number(standardDeviation(throughputs)).toFixed(2)
var max = Number(maximum(throughputs)).toFixed(2)
var min = Number(minimum(throughputs)).toFixed(2)
var throughputStats = '(avg: ' + avg + ', std: ' + std + ', max: ' + max + ', min: ' + min + ')'
status.throughput = throughput
status.throughputStats.average = avg
status.throughputStats['standard-deviation']= std
status.throughputStats.maximum = max
status.throughputStats.minimum = min
var cpuUsage = (cpuTime/duration) * 100
var avg = Number(average(cpuUsages)).toFixed(2)
var std = Number(standardDeviation(cpuUsages)).toFixed(2)
var max = Number(maximum(cpuUsages)).toFixed(2)
var min = Number(minimum(cpuUsages)).toFixed(2)
var cpuUsageStats = '(avg: ' + avg + ', std: ' + std + ', max: ' + max + ', min: ' + min + ')'
status.cpuUsage = cpuUsage
status.cpuUsageStats.average = avg
status.cpuUsageStats['standard-deviation']= std
status.cpuUsageStats.maximum = max
status.cpuUsageStats.minimum = min
var dataTransferLoad = (dataTransferTime/duration) * 100
var avg = Number(average(dataTransferLoads)).toFixed(2)
var std = Number(standardDeviation(dataTransferLoads)).toFixed(2)
var max = Number(maximum(dataTransferLoads)).toFixed(2)
var min = Number(minimum(dataTransferLoads)).toFixed(2)
var dataTransferStats = '(avg: ' + avg + ', std: ' + std + ', max: ' + max + ', min: ' + min + ')'
status.dataTransferLoad = dataTransferLoad
status.dataTransferStats.average = avg
status.dataTransferStats['standard-deviation']= std
status.dataTransferStats.maximum = max
status.dataTransferStats.minimum = min
display({
throughput: Number(throughput).toFixed(2) + ' ' + units + '/s ' + throughputStats,
cpuUsage: Number(cpuUsage).toFixed(2) + '% ' + cpuUsageStats,
dataTransferLoad: Number(dataTransferLoad).toFixed(2) + '% ' + dataTransferStats
})
// Update with device name
var deviceName = ''
if (typeof window !== 'undefined') {
deviceName = document.getElementById('device-name').value
status.deviceName = deviceName
}
submit(status)
nbItems = 0
cpuTime = 0
dataTransferTime = 0
throughputs.push(throughput)
cpuUsages.push(cpuUsage)
dataTransferLoads.push(dataTransferLoad)
}, interval)
function report (info) {
// Setup default values
var r = {
id: id,
cpuTime: info.cpuTime || 0,
dataTransferTime: info.dataTransferTime || 0,
nbItems: info.nbItems || 1,
units: info.units || 'items',
deviceName: ''
}
// Increase values used for display
nbItems += r.nbItems
units = r.units
cpuTime += r.cpuTime
dataTransferTime += r.dataTransferTime
}
var socketInitialized = false
var monitoringSocket = null
function connect() {
var protocol = window.pando.config.secure ? 'wss://' : 'ws://'
var host = window.pando.config.host
var url = protocol + host + '/volunteer-monitoring'
monitoringSocket = new Socket(url)
monitoringSocket
.on('connect', function () {
socketInitialized = true
console.log('Connected to report status at ' + url)
})
.on('close', function () {
socketInitialized = false
monitoringSocket.destroy()
monitoringSocket = null
console.log('Connection closed at ' + url)
})
.on('error', function () {
socketInitialized = false
monitoringSocket.destroy()
monitoringSocket = null
console.log('Connection closed at ' + url)
})
}
function submit (info) {
if (typeof window === 'undefined') return
if (!monitoringSocket && window.pando.config.protocol === 'websocket') {
console.log('connecting')
connect()
} else if (window.pando.config.protocol === 'webrtc') {
if (window.pando.processor) {
window.pando.processor.updatePerformance(info)
}
return
}
if (monitoringSocket && socketInitialized) {
monitoringSocket.send(JSON.stringify(info))
}
}
if (typeof window !== 'undefined') {
if (window.pando.config.version !== package.version) {
var msg = 'Incompatible Pando versions, you are running version ' + window.pando.config.version + ' while the application uses ' + package.version
console.log('ERROR: ' + msg)
throw new Error(msg)
} else {
log('Pando executable and application versions are compatible')
}
}
module.exports = {
version: package.version,
report: report
}