GPC files support, cleanups, options support, testing

pull/1/head
Piero Toffanin 2016-07-30 13:30:56 -05:00
rodzic f22e572a91
commit 61269d37c1
9 zmienionych plików z 110 dodań i 45 usunięć

Wyświetl plik

@ -21,6 +21,7 @@ let config = require('./config.js');
let logger = require('./libs/logger');
let fs = require('fs');
let path = require('path');
let async = require('async');
let express = require('express');
@ -68,6 +69,11 @@ let upload = multer({
app.post('/task/new', addRequestId, upload.array('images'), (req, res) => {
if (req.files.length === 0) res.json({error: "Need at least 1 file."});
else{
let srcPath = `tmp/${req.id}`;
let destPath = `data/${req.id}`;
let destImagesPath = `${destPath}/images`;
let destGpcPath = `${destPath}/gpc`;
async.series([
cb => {
odmOptions.filterOptions(req.body.options, (err, options) => {
@ -79,18 +85,27 @@ app.post('/task/new', addRequestId, upload.array('images'), (req, res) => {
});
},
// Move uploads to data dir
// Move all uploads to data/<uuid>/images dir
cb => {
fs.stat(`data/${req.id}`, (err, stat) => {
fs.stat(destPath, (err, stat) => {
if (err && err.code === 'ENOENT') cb();
else cb(new Error(`Directory exists (should not have happened: ${err.code})`));
});
},
cb => { fs.mkdir(`data/${req.id}`, undefined, cb); },
cb => fs.mkdir(destPath, undefined, cb),
cb => fs.mkdir(destGpcPath, undefined, cb),
cb => fs.rename(srcPath, destImagesPath, cb),
cb => {
fs.rename(`tmp/${req.id}`, `data/${req.id}/images`, err => {
if (!err) cb();
else cb(new Error("Could not move images folder."))
// Find any *.txt (GPC) file and move it to the data/<uuid>/gpc directory
fs.readdir(destImagesPath, (err, entries) => {
if (err) cb(err);
else{
async.eachSeries(entries, (entry, cb) => {
if (/\.txt$/gi.test(entry)){
fs.rename(path.join(destImagesPath, entry), path.join(destGpcPath, entry), cb);
}else cb();
}, cb);
}
});
},

Wyświetl plik

@ -16,8 +16,12 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
"use strict";
let async = require('async');
let assert = require('assert');
let logger = require('./logger');
let fs = require('fs');
let path = require('path');
let rmdir = require('rimraf');
let odmRunner = require('./odmRunner');
let archiver = require('archiver');
@ -36,16 +40,40 @@ module.exports = class Task{
this.processingTime = -1;
this.setStatus(statusCodes.QUEUED);
this.options = options;
this.gpcFiles = [];
this.output = [];
this.runnerProcess = null;
// Read images info
fs.readdir(this.getImagesFolderPath(), (err, files) => {
if (err) done(err);
else{
this.images = files;
done(null, this);
async.series([
// Read images info
cb => {
fs.readdir(this.getImagesFolderPath(), (err, files) => {
if (err) cb(err);
else{
this.images = files;
logger.debug(`Found ${this.images.length} images for ${this.uuid}`)
cb(null);
}
});
},
// Find GCP (if any)
cb => {
fs.readdir(this.getGpcFolderPath(), (err, files) => {
if (err) cb(err);
else{
files.forEach(file => {
if (/\.txt$/gi.test(file)){
this.gpcFiles.push(file);
}
});
logger.debug(`Found ${this.gpcFiles.length} GPC files (${this.gpcFiles.join(" ")}) for ${this.uuid}`);
cb(null);
}
});
}
], err => {
done(err, this);
});
}
@ -74,6 +102,12 @@ module.exports = class Task{
return `${this.getProjectFolderPath()}/images`;
}
// Get path where GPC file(s) are stored
// (relative to nodejs process CWD)
getGpcFolderPath(){
return `${this.getProjectFolderPath()}/gpc`;
}
// Get path of project (where all images and assets folder are contained)
// (relative to nodejs process CWD)
getProjectFolderPath(){
@ -194,9 +228,14 @@ module.exports = class Task{
return result;
}, {});
runnerOptions["project-path"] = `${__dirname}/../${this.getProjectFolderPath()}`;
runnerOptions["project-path"] = fs.realpathSync(this.getProjectFolderPath());
runnerOptions["pmvs-num-cores"] = os.cpus().length;
if (this.gpcFiles.length > 0){
runnerOptions["odm_georeferencing-useGcp"] = true;
runnerOptions["odm_georeferencing-gcpFile"] = fs.realpathSync(path.join(this.getGpcFolderPath(), this.gpcFiles[0]));
}
this.runnerProcess = odmRunner.run(runnerOptions, (err, code, signal) => {
if (err){
this.setStatus(statusCodes.FAILED, {errorMessage: `Could not start process (${err.message})`});

Wyświetl plik

@ -91,7 +91,7 @@ module.exports = class TaskManager{
async.eachSeries(entries, (entry, cb) => {
let dirPath = path.join(DATA_DIR, entry);
if (fs.statSync(dirPath).isDirectory() &&
entry.match(/[\w\d]+\-[\w\d]+\-[\w\d]+\-[\w\d]+\-[\w\d]+/) &&
entry.match(/^[\w\d]+\-[\w\d]+\-[\w\d]+\-[\w\d]+\-[\w\d]+$/) &&
!this.tasks[entry]){
logger.info(`Found orphaned directory: ${entry}, removing...`);
rmdir(dirPath, cb);
@ -101,7 +101,6 @@ module.exports = class TaskManager{
});
}
// Load tasks that already exists (if any)
restoreTaskListFromDump(done){
fs.readFile(TASKS_DUMP_FILE, (err, data) => {
@ -238,7 +237,7 @@ module.exports = class TaskManager{
fs.writeFile(TASKS_DUMP_FILE, JSON.stringify(output), err => {
if (err) logger.error(`Could not dump tasks: ${err.message}`);
else logger.debug("Dumped tasks list.");
else logger.info("Dumped tasks list.");
if (done !== undefined) done();
})
}

Wyświetl plik

@ -18,6 +18,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
"use strict";
let spawn = require('child_process').spawn;
let config = require('../config.js');
let logger = require('./logger');
module.exports = {
run: function(options = {
@ -38,7 +39,9 @@ module.exports = {
command.push(value);
}
}
logger.info(`About to run: python ${command.join(" ")}`);
// Launch
let childProcess = spawn("python", command, {cwd: config.odm_path});

Wyświetl plik

@ -43,7 +43,7 @@
<label for="taskName">Project Name:</lable> <input type="text" class="form-control" value="" id="taskName" />
</div>
<div class="form-group">
<label for="images">Aerial Imageries:</label> <input id="images" name="images" multiple type="file">
<label for="images">Aerial Imageries and GCP List (optional):</label> <input id="images" name="images" multiple type="file">
<div id="errorBlock" class="help-block"></div>
</div>
<div class="text-right"><input type="submit" class="btn btn-success" value="Start Task" id="btnUpload" /></div>

Wyświetl plik

@ -3205,8 +3205,8 @@
$.fn.fileinputLocales.en = {
fileSingle: 'file',
filePlural: 'files',
browseLabel: 'Browse &hellip;',
removeLabel: 'Remove',
browseLabel: 'Add &hellip;',
removeLabel: 'Clear All',
removeTitle: 'Clear selected files',
cancelLabel: 'Cancel',
cancelTitle: 'Abort ongoing upload',

Wyświetl plik

@ -14,8 +14,8 @@
$.fn.fileinputLocales['_LANG_'] = {
fileSingle: 'file',
filePlural: 'files',
browseLabel: 'Browse &hellip;',
removeLabel: 'Remove',
browseLabel: 'Add &hellip;',
removeLabel: 'Clear All',
removeTitle: 'Clear selected files',
cancelLabel: 'Cancel',
cancelTitle: 'Abort ongoing upload',

Wyświetl plik

@ -59,6 +59,14 @@ $(function(){
this.saveTaskListToLocalStorage();
};
var codes = {
QUEUED: 10,
RUNNING: 20,
FAILED: 30,
COMPLETED: 40,
CANCELED: 50
};
function Task(uuid){
var self = this;
@ -70,13 +78,6 @@ $(function(){
this.resetOutput();
this.timeElapsed = ko.observable("00:00:00");
var codes = {
QUEUED: 10,
RUNNING: 20,
FAILED: 30,
COMPLETED: 40,
CANCELED: 50
};
var statusCodes = {
10: {
descr: "Queued",
@ -205,22 +206,30 @@ $(function(){
var self = this;
var url = "/task/remove";
$.post(url, {
uuid: this.uuid
})
.done(function(json){
if (json.success || self.info().error){
taskList.remove(self);
}else{
self.info({error: json.error});
}
function doRemove(){
$.post(url, {
uuid: self.uuid
})
.done(function(json){
if (json.success || self.info().error){
taskList.remove(self);
}else{
self.info({error: json.error});
}
self.stopRefreshingInfo();
})
.fail(function(){
self.info({error: url + " is unreachable."});
self.stopRefreshingInfo();
});
self.stopRefreshingInfo();
})
.fail(function(){
self.info({error: url + " is unreachable."});
self.stopRefreshingInfo();
});
}
if (this.info().status && this.info().status.code === codes.COMPLETED){
if (confirm("Are you sure?")) doRemove();
}else{
doRemove();
}
};
function genApiCall(url, onSuccess){
@ -260,7 +269,7 @@ $(function(){
$("#images").fileinput({
uploadUrl: '/task/new',
showPreview: false,
allowedFileExtensions: ['jpg', 'jpeg'],
allowedFileExtensions: ['jpg', 'jpeg', 'txt'],
elErrorContainer: '#errorBlock',
showUpload: false,
uploadAsync: false,

Plik binarny nie jest wyświetlany.

Przed

Szerokość:  |  Wysokość:  |  Rozmiar: 72 KiB

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 100 KiB