diff --git a/index.js b/index.js
index f198704..4388d6e 100644
--- a/index.js
+++ b/index.js
@@ -39,7 +39,7 @@ let upload = multer({
})
});
-app.post('/newTask', addRequestId, upload.array('images'), (req, res) => {
+app.post('/task/new', addRequestId, upload.array('images'), (req, res) => {
if (req.files.length === 0) res.json({error: "Need at least 1 file."});
else{
// Move to data
@@ -81,11 +81,11 @@ let getTaskFromUuid = (req, res, next) => {
}else res.json({error: `${req.params.uuid} not found`});
}
-app.get('/taskInfo/:uuid', getTaskFromUuid, (req, res) => {
+app.get('/task/:uuid/info', getTaskFromUuid, (req, res) => {
res.json(req.task.getInfo());
});
-app.get('/taskOutput/:uuid', getTaskFromUuid, (req, res) => {
- res.json(req.task.getOutput());
+app.get('/task/:uuid/output', getTaskFromUuid, (req, res) => {
+ res.json(req.task.getOutput(req.query.line));
});
let uuidCheck = (req, res, next) => {
@@ -100,15 +100,15 @@ let successHandler = res => {
};
};
-app.post('/cancelTask', uuidCheck, (req, res) => {
+app.post('/task/cancel', uuidCheck, (req, res) => {
taskManager.cancel(req.body.uuid, successHandler(res));
});
-app.post('/removeTask', uuidCheck, (req, res) => {
+app.post('/task/remove', uuidCheck, (req, res) => {
taskManager.remove(req.body.uuid, successHandler(res));
});
-app.post('/restartTask', uuidCheck, (req, res) => {
+app.post('/task/restart', uuidCheck, (req, res) => {
taskManager.restart(req.body.uuid, successHandler(res));
});
diff --git a/libs/Task.js b/libs/Task.js
index 54fc76a..94fac91 100644
--- a/libs/Task.js
+++ b/libs/Task.js
@@ -33,7 +33,13 @@ module.exports = class Task{
// Get path where images are stored for this task
// (relative to nodejs process CWD)
getImagesFolderPath(){
- return `data/${this.uuid}/images`;
+ return `${this.getProjectFolderPath()}/images`;
+ }
+
+ // Get path of project (where all images and assets folder are contained)
+ // (relative to nodejs process CWD)
+ getProjectFolderPath(){
+ return `data/${this.uuid}`;
}
setStatus(code, extra){
@@ -68,7 +74,7 @@ module.exports = class Task{
if (this.status.code === statusCodes.QUEUED){
this.setStatus(statusCodes.RUNNING);
this.runnerProcess = odmRunner.run({
- projectPath: `${__dirname}/../${this.getImagesFolderPath()}`
+ projectPath: `${__dirname}/../${this.getProjectFolderPath()}`
}, (err, code, signal) => {
if (err){
this.setStatus(statusCodes.FAILED, {errorMessage: `Could not start process (${err.message})`});
@@ -81,6 +87,8 @@ module.exports = class Task{
}
done();
}, output => {
+ // Replace console colors
+ output = output.replace(/\x1b\[[0-9;]*m/g, "");
this.output.push(output);
});
@@ -120,7 +128,6 @@ module.exports = class Task{
// Returns the output of the OpenDroneMap process
// Optionally starting from a certain line number
getOutput(startFromLine = 0){
- let lineNum = Math.min(this.output.length, startFromLine);
- return this.output.slice(lineNum, this.output.length);
+ return this.output.slice(startFromLine, this.output.length);
}
};
\ No newline at end of file
diff --git a/libs/TaskManager.js b/libs/TaskManager.js
index 442c031..28e771f 100644
--- a/libs/TaskManager.js
+++ b/libs/TaskManager.js
@@ -3,7 +3,7 @@ let assert = require('assert');
let Task = require('./Task');
let statusCodes = require('./statusCodes');
-let PARALLEL_QUEUE_PROCESS_LIMIT = 1;
+let PARALLEL_QUEUE_PROCESS_LIMIT = 2;
module.exports = class TaskManager{
constructor(){
@@ -50,8 +50,6 @@ module.exports = class TaskManager{
this.runningQueue = this.runningQueue.filter(t => {
return t !== task;
});
-
- console.log("New queue length: " + this.runningQueue.length);
}
addNew(task){
@@ -90,7 +88,10 @@ module.exports = class TaskManager{
restart(uuid, cb){
let task;
if (task = this.find(uuid, cb)){
- task.restart(cb);
+ task.restart(err => {
+ this.processNextTask();
+ cb(err);
+ });
}
}
diff --git a/public/css/main.css b/public/css/main.css
index 26d651e..72dcc94 100644
--- a/public/css/main.css
+++ b/public/css/main.css
@@ -48,4 +48,11 @@
.task .actionButtons{
text-align: right;
+}
+
+.task .consoleOutput{
+ width: 100%;
+ height: 200px;
+ font-family: monospace;
+ font-size: 90%;
}
\ No newline at end of file
diff --git a/public/index.html b/public/index.html
index 3b83d46..42d10a7 100644
--- a/public/index.html
+++ b/public/index.html
@@ -64,6 +64,8 @@
Name:
Images:
Status:
+
+
diff --git a/public/js/main.js b/public/js/main.js
index 373d721..f7e8596 100644
--- a/public/js/main.js
+++ b/public/js/main.js
@@ -7,7 +7,7 @@ $(function(){
return new Task(uuid);
}));
}
- TaskList.prototype.addNew = function(task) {
+ TaskList.prototype.add = function(task) {
this.tasks.push(task);
this.saveTaskListToLocalStorage();
};
@@ -30,6 +30,8 @@ $(function(){
this.uuid = uuid;
this.loading = ko.observable(true);
this.info = ko.observable({});
+ this.viewingOutput = ko.observable(false);
+ this.resetOutput();
var statusCodes = {
10: {
@@ -64,7 +66,6 @@ $(function(){
this.icon = ko.pureComputed(function(){
if (this.info().status && this.info().status.code){
if(statusCodes[this.info().status.code]){
- console.log(statusCodes[this.info().status.code].icon);
return statusCodes[this.info().status.code].icon;
}else return "glyphicon-question-sign";
}else return "";
@@ -73,14 +74,11 @@ $(function(){
return this.info().status && this.info().status.code === 50;
}, this);
- this.refreshInfo();
- this.refreshInterval = setInterval(function(){
- self.refreshInfo();
- }, 2000);
+ this.startRefreshingInfo();
}
Task.prototype.refreshInfo = function(){
var self = this;
- var url = "/taskInfo/" + this.uuid;
+ var url = "/task/" + this.uuid + "/info";
$.get(url)
.done(self.info)
.fail(function(){
@@ -88,9 +86,61 @@ $(function(){
})
.always(function(){ self.loading(false); });
};
+ Task.prototype.consoleMouseOver = function(){ this.autoScrollOutput = false; }
+ Task.prototype.consoleMouseOut = function(){ this.autoScrollOutput = true; }
+ Task.prototype.resetOutput = function(){
+ this.viewOutputLine = 0;
+ this.autoScrollOutput = true;
+ this.output = ko.observableArray();
+ };
+ Task.prototype.viewOutput = function(){
+ var self = this;
+
+ function fetchOutput(){
+ var url = "/task/" + self.uuid + "/output";
+ $.get(url, {line: self.viewOutputLine})
+ .done(function(output){
+ for (var i in output){
+ self.output.push(output[i]);
+ }
+ if (output.length){
+ self.viewOutputLine += output.length;
+ if (self.autoScrollOutput){
+ var $console = $("#console_" + self.uuid);
+ $console.scrollTop($console[0].scrollHeight - $console.height())
+ }
+ }
+ })
+ .fail(function(){
+ self.info({error: url + " is unreachable."});
+ });
+ }
+ this.fetchOutputInterval = setInterval(fetchOutput, 2000);
+ fetchOutput();
+
+ this.viewingOutput(true);
+ };
+ Task.prototype.hideOutput = function(){
+ if (this.fetchOutputInterval) clearInterval(this.fetchOutputInterval);
+ this.viewingOutput(false);
+ };
+ Task.prototype.startRefreshingInfo = function() {
+ var self = this;
+ this.stopRefreshingInfo();
+ this.refreshInfo();
+ this.refreshInterval = setInterval(function(){
+ self.refreshInfo();
+ }, 2000);
+ };
+ Task.prototype.stopRefreshingInfo = function() {
+ if (this.refreshInterval){
+ clearInterval(this.refreshInterval);
+ this.refreshInterval = null;
+ }
+ };
Task.prototype.remove = function() {
var self = this;
- var url = "/removeTask";
+ var url = "/task/remove";
$.post(url, {
uuid: this.uuid
@@ -102,13 +152,11 @@ $(function(){
self.info({error: json.error});
}
- if (self.refreshInterval){
- clearInterval(self.refreshInterval);
- self.refreshInterval = null;
- }
+ self.stopRefreshingInfo();
})
.fail(function(){
self.info({error: url + " is unreachable."});
+ self.stopRefreshingInfo();
});
};
@@ -116,34 +164,32 @@ $(function(){
return function(){
var self = this;
-
- // TODO: maybe there's a better way
- // to handle refreshInfo here...
-
$.post(url, {
uuid: this.uuid
})
.done(function(json){
if (json.success){
- self.refreshInfo();
+ self.startRefreshingInfo();
}else{
+ self.stopRefreshingInfo();
self.info({error: json.error});
}
})
.fail(function(){
self.info({error: url + " is unreachable."});
+ self.stopRefreshingInfo();
});
}
};
- Task.prototype.cancel = genApiCall("/cancelTask");
- Task.prototype.restart = genApiCall("/restartTask");
+ Task.prototype.cancel = genApiCall("/task/cancel");
+ Task.prototype.restart = genApiCall("/task/restart");
var taskList = new TaskList();
ko.applyBindings(taskList);
// Handle uploads
$("#images").fileinput({
- uploadUrl: '/newTask',
+ uploadUrl: '/task/new',
showPreview: false,
allowedFileExtensions: ['jpg', 'jpeg'],
elErrorContainer: '#errorBlock',
@@ -170,7 +216,7 @@ $(function(){
$("#images").fileinput('reset');
if (params.response.success && params.response.uuid){
- taskList.addNew(new Task(params.response.uuid));
+ taskList.add(new Task(params.response.uuid));
}
})
.on('filebatchuploadcomplete', function(){