blob: af596ad379e491f3cd725894a77dc2484f9919d1 [file] [log] [blame]
// Copyright 2016 The Vanadium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
'use strict';
var _ = require('underscore');
var stfService = require('../services/stfService');
var turkHitService = require('../services/turkHitService');
var config = require('../config');
const POLL_INTERVAL = config.pollIntervalMillis || 15000;
const TASK_MINS = config.polledTaskMinutes || 5.0;
/**
* PollingService polls devices and issues tokens based on availability.
* @constructor
*/
function PollingService() {
this.activePolling = false;
}
/**
* Starts the polling interval.
*/
PollingService.prototype.start = function() {
if (pollingService.activePolling) {
return;
}
pollingService.activePolling = true;
pollingService.poll();
};
/**
* Stops the polling interval.
*/
PollingService.prototype.stop = function() {
pollingService.activePolling = false;
};
/**
* Reports the polling interval status.
* @return {Boolean} activePolling - the polling status.
*/
PollingService.prototype.isActive = function() {
return pollingService.activePolling;
};
/**
* Polls for available devices who don't have tokens assigned to them.
*/
PollingService.prototype.poll = function() {
if (!pollingService.isActive()) {
return;
}
stfService.getTokens(function(err, tokens) {
if (err) {
console.error('Error getting tokens', err);
return pollingService.stop();
}
var unusedTokens = _(tokens).filter(function(token) {
return token.status === 'unused' || token.status === 'active';
});
stfService.getDevices(function(err, devices) {
if (err) {
console.error('Error getting devices', err);
return pollingService.stop();
}
var presentDevices = _(devices).filter(function(device) {
return device.present;
});
var unusedDevices = _(presentDevices).filter(function(presentDevice) {
return !presentDevice.token;
});
if (unusedTokens.length >= presentDevices.length) {
return setTimeout(pollingService.poll, POLL_INTERVAL);
}
if (unusedDevices.length > 0) {
pollingService.allocateTokens(unusedDevices);
} else {
return setTimeout(pollingService.poll, POLL_INTERVAL);
}
});
});
};
/**
* Assigns tokens to devices and issues HITs for the tokens.
* @param {Array<Object>} unusedDevices - list of free device objects
*/
PollingService.prototype.allocateTokens = function(unusedDevices) {
var tokens = [];
var getToken = function(device) {
console.info('Generating token for device:', device.serial);
stfService.generateToken(device.serial, TASK_MINS, function(err, tokenObj) {
if (err) {
console.error('Error generating token', err);
// Continue with the rest despite the error.
if (unusedDevices.length) {
return getToken(unusedDevices.pop());
}
// If we didn't issue tokens, return to polling.
if (!tokens.length) {
return setTimeout(pollingService.poll, POLL_INTERVAL);
}
} else {
tokens.push(tokenObj);
}
if (unusedDevices.length) {
return getToken(unusedDevices.pop());
} else {
var createHit = function(tk) {
turkHitService.createHit(tk.token, TASK_MINS, tk.appId,
function done(err) {
if (err) {
console.error('Error creating hit', err);
// Fallback and undo token allocation.
stfService.deleteToken(tk.token, function(err) {
if (err) {
console.error('Error deleting token for bad hit', err);
} else {
console.info('Deleted token for bad HIT');
}
});
// Continue creating HITs.
if (tokens.length > 0) {
return createHit(tokens.pop());
}
return setTimeout(pollingService.poll, POLL_INTERVAL);
}
if (tokens.length > 0) {
createHit(tokens.pop());
} else {
return setTimeout(pollingService.poll, POLL_INTERVAL);
}
});
};
createHit(tokens.pop());
}
});
};
getToken(unusedDevices.pop());
};
var pollingService = new PollingService();
module.exports = pollingService;