kopia lustrzana https://github.com/OpenDroneMap/NodeODM
Task start working, console output
rodzic
7a4bee53b9
commit
48e292a6cf
14
index.js
14
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."});
|
if (req.files.length === 0) res.json({error: "Need at least 1 file."});
|
||||||
else{
|
else{
|
||||||
// Move to data
|
// Move to data
|
||||||
|
@ -81,11 +81,11 @@ let getTaskFromUuid = (req, res, next) => {
|
||||||
}else res.json({error: `${req.params.uuid} not found`});
|
}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());
|
res.json(req.task.getInfo());
|
||||||
});
|
});
|
||||||
app.get('/taskOutput/:uuid', getTaskFromUuid, (req, res) => {
|
app.get('/task/:uuid/output', getTaskFromUuid, (req, res) => {
|
||||||
res.json(req.task.getOutput());
|
res.json(req.task.getOutput(req.query.line));
|
||||||
});
|
});
|
||||||
|
|
||||||
let uuidCheck = (req, res, next) => {
|
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));
|
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));
|
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));
|
taskManager.restart(req.body.uuid, successHandler(res));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
15
libs/Task.js
15
libs/Task.js
|
@ -33,7 +33,13 @@ module.exports = class Task{
|
||||||
// Get path where images are stored for this task
|
// Get path where images are stored for this task
|
||||||
// (relative to nodejs process CWD)
|
// (relative to nodejs process CWD)
|
||||||
getImagesFolderPath(){
|
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){
|
setStatus(code, extra){
|
||||||
|
@ -68,7 +74,7 @@ module.exports = class Task{
|
||||||
if (this.status.code === statusCodes.QUEUED){
|
if (this.status.code === statusCodes.QUEUED){
|
||||||
this.setStatus(statusCodes.RUNNING);
|
this.setStatus(statusCodes.RUNNING);
|
||||||
this.runnerProcess = odmRunner.run({
|
this.runnerProcess = odmRunner.run({
|
||||||
projectPath: `${__dirname}/../${this.getImagesFolderPath()}`
|
projectPath: `${__dirname}/../${this.getProjectFolderPath()}`
|
||||||
}, (err, code, signal) => {
|
}, (err, code, signal) => {
|
||||||
if (err){
|
if (err){
|
||||||
this.setStatus(statusCodes.FAILED, {errorMessage: `Could not start process (${err.message})`});
|
this.setStatus(statusCodes.FAILED, {errorMessage: `Could not start process (${err.message})`});
|
||||||
|
@ -81,6 +87,8 @@ module.exports = class Task{
|
||||||
}
|
}
|
||||||
done();
|
done();
|
||||||
}, output => {
|
}, output => {
|
||||||
|
// Replace console colors
|
||||||
|
output = output.replace(/\x1b\[[0-9;]*m/g, "");
|
||||||
this.output.push(output);
|
this.output.push(output);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -120,7 +128,6 @@ module.exports = class Task{
|
||||||
// Returns the output of the OpenDroneMap process
|
// Returns the output of the OpenDroneMap process
|
||||||
// Optionally starting from a certain line number
|
// Optionally starting from a certain line number
|
||||||
getOutput(startFromLine = 0){
|
getOutput(startFromLine = 0){
|
||||||
let lineNum = Math.min(this.output.length, startFromLine);
|
return this.output.slice(startFromLine, this.output.length);
|
||||||
return this.output.slice(lineNum, this.output.length);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
|
@ -3,7 +3,7 @@ let assert = require('assert');
|
||||||
let Task = require('./Task');
|
let Task = require('./Task');
|
||||||
let statusCodes = require('./statusCodes');
|
let statusCodes = require('./statusCodes');
|
||||||
|
|
||||||
let PARALLEL_QUEUE_PROCESS_LIMIT = 1;
|
let PARALLEL_QUEUE_PROCESS_LIMIT = 2;
|
||||||
|
|
||||||
module.exports = class TaskManager{
|
module.exports = class TaskManager{
|
||||||
constructor(){
|
constructor(){
|
||||||
|
@ -50,8 +50,6 @@ module.exports = class TaskManager{
|
||||||
this.runningQueue = this.runningQueue.filter(t => {
|
this.runningQueue = this.runningQueue.filter(t => {
|
||||||
return t !== task;
|
return t !== task;
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log("New queue length: " + this.runningQueue.length);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
addNew(task){
|
addNew(task){
|
||||||
|
@ -90,7 +88,10 @@ module.exports = class TaskManager{
|
||||||
restart(uuid, cb){
|
restart(uuid, cb){
|
||||||
let task;
|
let task;
|
||||||
if (task = this.find(uuid, cb)){
|
if (task = this.find(uuid, cb)){
|
||||||
task.restart(cb);
|
task.restart(err => {
|
||||||
|
this.processNextTask();
|
||||||
|
cb(err);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -48,4 +48,11 @@
|
||||||
|
|
||||||
.task .actionButtons{
|
.task .actionButtons{
|
||||||
text-align: right;
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.task .consoleOutput{
|
||||||
|
width: 100%;
|
||||||
|
height: 200px;
|
||||||
|
font-family: monospace;
|
||||||
|
font-size: 90%;
|
||||||
}
|
}
|
|
@ -64,6 +64,8 @@
|
||||||
<div class="taskItem"><strong>Name:</strong> <span data-bind="text: info().name"></span></div>
|
<div class="taskItem"><strong>Name:</strong> <span data-bind="text: info().name"></span></div>
|
||||||
<div class="taskItem"><strong>Images:</strong> <span data-bind="text: info().imagesCount"></span></div>
|
<div class="taskItem"><strong>Images:</strong> <span data-bind="text: info().imagesCount"></span></div>
|
||||||
<div class="taskItem"><strong>Status:</strong> <span data-bind="text: statusDescr()"></span></div>
|
<div class="taskItem"><strong>Status:</strong> <span data-bind="text: statusDescr()"></span></div>
|
||||||
|
<div class="taskItem"><strong>Output:</strong> <a href="javascript:void(0);" data-bind="click: viewOutput, visible: !viewingOutput()">View</a><a href="javascript:void(0);" data-bind="click: hideOutput, visible: viewingOutput()">Hide</a></div>
|
||||||
|
<textarea class="consoleOutput" data-bind="value: output().join(''), visible: viewingOutput(), event: {mouseover: consoleMouseOver, mouseout: consoleMouseOut}, attr: {id: 'console_' + uuid}"></textarea>
|
||||||
|
|
||||||
<span data-bind="css: 'statusIcon glyphicon ' + icon()"></span>
|
<span data-bind="css: 'statusIcon glyphicon ' + icon()"></span>
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@ $(function(){
|
||||||
return new Task(uuid);
|
return new Task(uuid);
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
TaskList.prototype.addNew = function(task) {
|
TaskList.prototype.add = function(task) {
|
||||||
this.tasks.push(task);
|
this.tasks.push(task);
|
||||||
this.saveTaskListToLocalStorage();
|
this.saveTaskListToLocalStorage();
|
||||||
};
|
};
|
||||||
|
@ -30,6 +30,8 @@ $(function(){
|
||||||
this.uuid = uuid;
|
this.uuid = uuid;
|
||||||
this.loading = ko.observable(true);
|
this.loading = ko.observable(true);
|
||||||
this.info = ko.observable({});
|
this.info = ko.observable({});
|
||||||
|
this.viewingOutput = ko.observable(false);
|
||||||
|
this.resetOutput();
|
||||||
|
|
||||||
var statusCodes = {
|
var statusCodes = {
|
||||||
10: {
|
10: {
|
||||||
|
@ -64,7 +66,6 @@ $(function(){
|
||||||
this.icon = ko.pureComputed(function(){
|
this.icon = ko.pureComputed(function(){
|
||||||
if (this.info().status && this.info().status.code){
|
if (this.info().status && this.info().status.code){
|
||||||
if(statusCodes[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;
|
return statusCodes[this.info().status.code].icon;
|
||||||
}else return "glyphicon-question-sign";
|
}else return "glyphicon-question-sign";
|
||||||
}else return "";
|
}else return "";
|
||||||
|
@ -73,14 +74,11 @@ $(function(){
|
||||||
return this.info().status && this.info().status.code === 50;
|
return this.info().status && this.info().status.code === 50;
|
||||||
}, this);
|
}, this);
|
||||||
|
|
||||||
this.refreshInfo();
|
this.startRefreshingInfo();
|
||||||
this.refreshInterval = setInterval(function(){
|
|
||||||
self.refreshInfo();
|
|
||||||
}, 2000);
|
|
||||||
}
|
}
|
||||||
Task.prototype.refreshInfo = function(){
|
Task.prototype.refreshInfo = function(){
|
||||||
var self = this;
|
var self = this;
|
||||||
var url = "/taskInfo/" + this.uuid;
|
var url = "/task/" + this.uuid + "/info";
|
||||||
$.get(url)
|
$.get(url)
|
||||||
.done(self.info)
|
.done(self.info)
|
||||||
.fail(function(){
|
.fail(function(){
|
||||||
|
@ -88,9 +86,61 @@ $(function(){
|
||||||
})
|
})
|
||||||
.always(function(){ self.loading(false); });
|
.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() {
|
Task.prototype.remove = function() {
|
||||||
var self = this;
|
var self = this;
|
||||||
var url = "/removeTask";
|
var url = "/task/remove";
|
||||||
|
|
||||||
$.post(url, {
|
$.post(url, {
|
||||||
uuid: this.uuid
|
uuid: this.uuid
|
||||||
|
@ -102,13 +152,11 @@ $(function(){
|
||||||
self.info({error: json.error});
|
self.info({error: json.error});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (self.refreshInterval){
|
self.stopRefreshingInfo();
|
||||||
clearInterval(self.refreshInterval);
|
|
||||||
self.refreshInterval = null;
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
.fail(function(){
|
.fail(function(){
|
||||||
self.info({error: url + " is unreachable."});
|
self.info({error: url + " is unreachable."});
|
||||||
|
self.stopRefreshingInfo();
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -116,34 +164,32 @@ $(function(){
|
||||||
return function(){
|
return function(){
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
|
|
||||||
// TODO: maybe there's a better way
|
|
||||||
// to handle refreshInfo here...
|
|
||||||
|
|
||||||
$.post(url, {
|
$.post(url, {
|
||||||
uuid: this.uuid
|
uuid: this.uuid
|
||||||
})
|
})
|
||||||
.done(function(json){
|
.done(function(json){
|
||||||
if (json.success){
|
if (json.success){
|
||||||
self.refreshInfo();
|
self.startRefreshingInfo();
|
||||||
}else{
|
}else{
|
||||||
|
self.stopRefreshingInfo();
|
||||||
self.info({error: json.error});
|
self.info({error: json.error});
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.fail(function(){
|
.fail(function(){
|
||||||
self.info({error: url + " is unreachable."});
|
self.info({error: url + " is unreachable."});
|
||||||
|
self.stopRefreshingInfo();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
Task.prototype.cancel = genApiCall("/cancelTask");
|
Task.prototype.cancel = genApiCall("/task/cancel");
|
||||||
Task.prototype.restart = genApiCall("/restartTask");
|
Task.prototype.restart = genApiCall("/task/restart");
|
||||||
|
|
||||||
var taskList = new TaskList();
|
var taskList = new TaskList();
|
||||||
ko.applyBindings(taskList);
|
ko.applyBindings(taskList);
|
||||||
|
|
||||||
// Handle uploads
|
// Handle uploads
|
||||||
$("#images").fileinput({
|
$("#images").fileinput({
|
||||||
uploadUrl: '/newTask',
|
uploadUrl: '/task/new',
|
||||||
showPreview: false,
|
showPreview: false,
|
||||||
allowedFileExtensions: ['jpg', 'jpeg'],
|
allowedFileExtensions: ['jpg', 'jpeg'],
|
||||||
elErrorContainer: '#errorBlock',
|
elErrorContainer: '#errorBlock',
|
||||||
|
@ -170,7 +216,7 @@ $(function(){
|
||||||
$("#images").fileinput('reset');
|
$("#images").fileinput('reset');
|
||||||
|
|
||||||
if (params.response.success && params.response.uuid){
|
if (params.response.success && params.response.uuid){
|
||||||
taskList.addNew(new Task(params.response.uuid));
|
taskList.add(new Task(params.response.uuid));
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.on('filebatchuploadcomplete', function(){
|
.on('filebatchuploadcomplete', function(){
|
||||||
|
|
Ładowanie…
Reference in New Issue