mirror of
https://gitlab.com/wgp/dougal/software.git
synced 2025-12-06 08:47:07 +00:00
Add planner endpoints
This commit is contained in:
@@ -103,6 +103,17 @@ app.map({
|
||||
// get: [ mw.sequence.get ],
|
||||
patch: [ mw.sequence.patch ],
|
||||
},
|
||||
|
||||
'/project/:project/plan/': {
|
||||
get: [ mw.plan.list ],
|
||||
put: [ mw.plan.put ],
|
||||
post: [ mw.plan.post ]
|
||||
},
|
||||
'/project/:project/plan/:sequence': {
|
||||
// get: [ mw.plan.get ],
|
||||
patch: [ mw.plan.patch ],
|
||||
delete: [ mw.plan.delete ]
|
||||
},
|
||||
//
|
||||
'/project/:project/event/': {
|
||||
get: [ mw.event.cache.get, mw.event.list, mw.event.cache.save ],
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
module.exports = {
|
||||
event: require('./event'),
|
||||
plan: require('./plan'),
|
||||
line: require('./line'),
|
||||
project: require('./project'),
|
||||
sequence: require('./sequence'),
|
||||
|
||||
17
lib/www/server/api/middleware/plan/delete.js
Normal file
17
lib/www/server/api/middleware/plan/delete.js
Normal file
@@ -0,0 +1,17 @@
|
||||
|
||||
const { plan } = require('../../../lib/db');
|
||||
|
||||
module.exports = async function (req, res, next) {
|
||||
|
||||
try {
|
||||
const payload = req.body;
|
||||
|
||||
await plan.delete(req.params.project, req.params.sequence);
|
||||
res.status(201).send();
|
||||
next();
|
||||
} catch (err) {
|
||||
next(err);
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
0
lib/www/server/api/middleware/plan/get.js
Normal file
0
lib/www/server/api/middleware/plan/get.js
Normal file
8
lib/www/server/api/middleware/plan/index.js
Normal file
8
lib/www/server/api/middleware/plan/index.js
Normal file
@@ -0,0 +1,8 @@
|
||||
module.exports = {
|
||||
list: require('./list'),
|
||||
get: require('./get'),
|
||||
post: require('./post'),
|
||||
put: require('./put'),
|
||||
patch: require('./patch'),
|
||||
delete: require('./delete')
|
||||
};
|
||||
14
lib/www/server/api/middleware/plan/list.js
Normal file
14
lib/www/server/api/middleware/plan/list.js
Normal file
@@ -0,0 +1,14 @@
|
||||
|
||||
const { plan } = require('../../../lib/db');
|
||||
|
||||
module.exports = async function (req, res, next) {
|
||||
|
||||
try {
|
||||
res.status(200).send(await plan.list(req.params.project, req.query));
|
||||
next();
|
||||
} catch (err) {
|
||||
next(err);
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
17
lib/www/server/api/middleware/plan/patch.js
Normal file
17
lib/www/server/api/middleware/plan/patch.js
Normal file
@@ -0,0 +1,17 @@
|
||||
|
||||
const { plan } = require('../../../lib/db');
|
||||
|
||||
module.exports = async function (req, res, next) {
|
||||
|
||||
try {
|
||||
const payload = req.body;
|
||||
|
||||
await plan.patch(req.params.project, req.params.sequence, payload);
|
||||
res.status(201).send();
|
||||
next();
|
||||
} catch (err) {
|
||||
next(err);
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
17
lib/www/server/api/middleware/plan/post.js
Normal file
17
lib/www/server/api/middleware/plan/post.js
Normal file
@@ -0,0 +1,17 @@
|
||||
|
||||
const { plan } = require('../../../lib/db');
|
||||
|
||||
module.exports = async function (req, res, next) {
|
||||
|
||||
try {
|
||||
const payload = req.body;
|
||||
|
||||
await plan.post(req.params.project, payload);
|
||||
res.status(201).send();
|
||||
next();
|
||||
} catch (err) {
|
||||
next(err);
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
17
lib/www/server/api/middleware/plan/put.js
Normal file
17
lib/www/server/api/middleware/plan/put.js
Normal file
@@ -0,0 +1,17 @@
|
||||
|
||||
const { plan } = require('../../../lib/db');
|
||||
|
||||
module.exports = async function (req, res, next) {
|
||||
|
||||
try {
|
||||
const payload = req.body;
|
||||
|
||||
await plan.put(req.params.project, payload);
|
||||
res.status(201).send();
|
||||
next();
|
||||
} catch (err) {
|
||||
next(err);
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
@@ -4,6 +4,7 @@ module.exports = {
|
||||
line: require('./line'),
|
||||
sequence: require('./sequence'),
|
||||
event: require('./event'),
|
||||
plan: require('./plan'),
|
||||
gis: require('./gis'),
|
||||
label: require('./label'),
|
||||
configuration: require('./configuration'),
|
||||
|
||||
24
lib/www/server/lib/db/plan/delete.js
Normal file
24
lib/www/server/lib/db/plan/delete.js
Normal file
@@ -0,0 +1,24 @@
|
||||
const { setSurvey, transaction } = require('../connection');
|
||||
|
||||
async function del (projectId, sequence, opts = {}) {
|
||||
|
||||
const client = await setSurvey(projectId);
|
||||
try {
|
||||
|
||||
const text = `
|
||||
DELETE
|
||||
FROM planned_lines
|
||||
WHERE sequence = $1;
|
||||
`;
|
||||
|
||||
await client.query(text, [sequence]);
|
||||
} catch (err) {
|
||||
throw err;
|
||||
} finally {
|
||||
client.release();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
module.exports = del;
|
||||
0
lib/www/server/lib/db/plan/get.js
Normal file
0
lib/www/server/lib/db/plan/get.js
Normal file
9
lib/www/server/lib/db/plan/index.js
Normal file
9
lib/www/server/lib/db/plan/index.js
Normal file
@@ -0,0 +1,9 @@
|
||||
|
||||
module.exports = {
|
||||
list: require('./list'),
|
||||
get: require('./get'),
|
||||
post: require('./post'),
|
||||
put: require('./put'),
|
||||
patch: require('./patch'),
|
||||
delete: require('./delete')
|
||||
}
|
||||
41
lib/www/server/lib/db/plan/list.js
Normal file
41
lib/www/server/lib/db/plan/list.js
Normal file
@@ -0,0 +1,41 @@
|
||||
const { setSurvey } = require('../connection');
|
||||
|
||||
async function list (projectId, opts = {}) {
|
||||
const client = await setSurvey(projectId);
|
||||
|
||||
// const sortFields = [ "line", "length", "azimuth", "fsp", "lsp", "num_points", "incr", "remarks" ];
|
||||
// const sortKey = opts.sortBy && sortFields.includes(opts.sortBy) && opts.sortBy || "line";
|
||||
// const sortDir = opts.sortDesc == "true" ? "DESC" : "ASC";
|
||||
// const offset = Math.abs((opts.page-1)*opts.itemsPerPage) || 0;
|
||||
// const limit = Math.abs(Number(opts.itemsPerPage)) || null;
|
||||
|
||||
const text = `
|
||||
SELECT
|
||||
pl.*,
|
||||
(SELECT count(*)
|
||||
FROM preplot_points pp
|
||||
WHERE pp.line = pl.line
|
||||
AND pp.class = pl.class
|
||||
AND pp.point BETWEEN SYMMETRIC pl.fsp AND pl.lsp
|
||||
) num_points,
|
||||
(ts1-ts0) duration,
|
||||
ST_Distance(pp0.geometry, pp1.geometry) length,
|
||||
ST_Azimuth(pp0.geometry, pp1.geometry)*180.0/pi() azimuth,
|
||||
-- speed? Nah
|
||||
ST_Transform(ST_MakeLine(pp0.geometry, pp1.geometry), 4326)::jsonb geometry
|
||||
FROM planned_lines pl
|
||||
INNER JOIN preplot_points pp0
|
||||
ON pl.line = pp0.line AND pl.fsp = pp0.point AND pl.class = pp0.class
|
||||
INNER JOIN preplot_points pp1
|
||||
ON pl.line = pp1.line AND pl.lsp = pp1.point AND pl.class = pp1.class
|
||||
INNER JOIN preplot_points pp
|
||||
ON pl.line = pp.line AND pp.point BETWEEN SYMMETRIC pl.fsp AND pl.fsp AND pl.class = pp.class
|
||||
ORDER BY sequence ASC;
|
||||
`;
|
||||
|
||||
const res = await client.query(text);
|
||||
client.release();
|
||||
return res.rows;
|
||||
}
|
||||
|
||||
module.exports = list;
|
||||
33
lib/www/server/lib/db/plan/patch.js
Normal file
33
lib/www/server/lib/db/plan/patch.js
Normal file
@@ -0,0 +1,33 @@
|
||||
const { setSurvey, transaction } = require('../connection');
|
||||
|
||||
async function patch (projectId, sequence, payload, opts = {}) {
|
||||
const client = await setSurvey(projectId);
|
||||
|
||||
try {
|
||||
|
||||
const text = `
|
||||
UPDATE planned_lines
|
||||
SET
|
||||
sequence = COALESCE($2, sequence),
|
||||
fsp = COALESCE($3, fsp),
|
||||
lsp = COALESCE($4, lsp),
|
||||
ts0 = COALESCE($5, ts0),
|
||||
ts1 = COALESCE($6, ts1),
|
||||
remarks = COALESCE($7, remarks),
|
||||
meta = COALESCE($8, meta)
|
||||
WHERE sequence = $1;
|
||||
`
|
||||
|
||||
const p = payload; // For short
|
||||
const values = [ sequence, p.sequence, p.fsp, p.lsp, p.lsp, p.ts0, p.ts1, p.remarks, p.meta ];
|
||||
|
||||
await client.query(text, values);
|
||||
} catch (err) {
|
||||
transaction.rollback(err);
|
||||
throw err;
|
||||
} finally {
|
||||
client.release();
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = patch;
|
||||
147
lib/www/server/lib/db/plan/post.js
Normal file
147
lib/www/server/lib/db/plan/post.js
Normal file
@@ -0,0 +1,147 @@
|
||||
const { setSurvey, transaction } = require('../connection');
|
||||
const configuration = require('../configuration');
|
||||
|
||||
async function getDistance (client, payload) {
|
||||
const text = `
|
||||
SELECT ST_Distance(pp0.geometry, pp1.geometry) distance
|
||||
FROM preplot_points pp0,
|
||||
preplot_points pp1
|
||||
WHERE
|
||||
pp0.line = $1 AND pp1.line = $1
|
||||
AND pp0.class = $4 AND pp1.class = $4
|
||||
AND pp0.point = $2 AND pp1.point = $3;
|
||||
`;
|
||||
|
||||
const p = payload;
|
||||
const res = await client.query(text, [p.line, p.fsp, p.lsp, p.class || "V"]);
|
||||
if (res.rows.length) {
|
||||
return res.rows[0].distance;
|
||||
} // else undefined
|
||||
}
|
||||
|
||||
async function getSequence (client) {
|
||||
const text = `
|
||||
SELECT max(sequence)+1 AS sequence
|
||||
FROM (
|
||||
SELECT sequence
|
||||
FROM raw_lines
|
||||
UNION SELECT sequence
|
||||
FROM planned_lines
|
||||
) t;
|
||||
`;
|
||||
|
||||
const res = await client.query(text);
|
||||
return res.rows[0] && res.rows[0].sequence;
|
||||
}
|
||||
|
||||
async function getTimestamps (client, projectId, payload) {
|
||||
|
||||
const defaultLineChangeDuration = (await configuration.get(projectId, "planner/defaultLineChangeDuration") || 30) * 60*1000; // minutes to milliseconds
|
||||
|
||||
const defaultAcquisitionSpeed = (await configuration.get(projectId, "planner/defaultAcquisitionSpeed") || 4.8) * 1.852 / 3.6; // Knots to m/s
|
||||
|
||||
const distance = await getDistance(client, payload);
|
||||
|
||||
const text = `
|
||||
SELECT * FROM planned_lines;
|
||||
`;
|
||||
|
||||
const res = await client.query(text);
|
||||
|
||||
const ts0 = new Date(
|
||||
(res.rows.length
|
||||
? res.rows.map(r => r.ts1).reduce( (a, b) => Math.max(a, b) )
|
||||
: Date.now()
|
||||
) + defaultLineChangeDuration
|
||||
);
|
||||
|
||||
const ts1 = new Date(ts0.valueOf() + (distance / defaultAcquisitionSpeed)*1000);
|
||||
|
||||
return {ts0, ts1};
|
||||
}
|
||||
|
||||
async function getSequencesForLine (client, line) {
|
||||
const text = `
|
||||
SELECT * from sequences_summary WHERE line = $1 ORDER BY sequence;
|
||||
`;
|
||||
|
||||
const res = await client.query(text, [line]);
|
||||
return res.rows;
|
||||
}
|
||||
|
||||
async function getPlanned (client) {
|
||||
const text = `
|
||||
SELECT * FROM planned_lines ORDER BY sequence;
|
||||
`;
|
||||
|
||||
const res = await client.query(text);
|
||||
return res.rows;
|
||||
}
|
||||
|
||||
async function getLineName (client, projectId, payload) {
|
||||
// FIXME TODO Get line name script from configuration
|
||||
|
||||
const planned = await getPlanned(client);
|
||||
const previous = await getSequencesForLine(client, payload.line);
|
||||
console.log("Previous", previous);
|
||||
const attempt = planned.filter(r => r.line == payload.line).concat(previous).length;
|
||||
const p = payload;
|
||||
const incr = p.lsp > p.fsp;
|
||||
const sequence = p.sequence;
|
||||
const line = p.line;
|
||||
return `${incr?"1":"2"}0${line}${attempt}${sequence.toString().padStart(3, "0")}S00000`;
|
||||
}
|
||||
|
||||
async function post (projectId, payload, opts = {}) {
|
||||
|
||||
const client = await setSurvey(projectId);
|
||||
try {
|
||||
|
||||
if (!payload.sequence) {
|
||||
payload.sequence = await getSequence(client);
|
||||
console.log("Sequence", payload.sequence);
|
||||
}
|
||||
if (!payload.ts0 || !payload.ts1) {
|
||||
const ts = await getTimestamps(client, projectId, payload);
|
||||
console.log("ts", ts);
|
||||
if (!payload.ts0) {
|
||||
payload.ts0 = ts.ts0;
|
||||
}
|
||||
if (!payload.ts1) {
|
||||
payload.ts1 = ts.ts1;
|
||||
}
|
||||
}
|
||||
if (!payload.name) {
|
||||
payload.name = await getLineName(client, projectId, payload);
|
||||
}
|
||||
|
||||
const p = Object.assign({
|
||||
remarks: "",
|
||||
meta: {}
|
||||
}, payload);
|
||||
|
||||
const text = `
|
||||
INSERT
|
||||
INTO planned_lines (sequence, line, fsp, lsp, ts0, ts1, name, remarks, meta)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9);
|
||||
`;
|
||||
const values = [ p.sequence, p.line, p.fsp, p.lsp, p.ts0, p.ts1, p.name, p.remarks, p.meta ];
|
||||
|
||||
console.log(text, values);
|
||||
await client.query(text, values);
|
||||
} catch (err) {
|
||||
if (err.code && Math.trunc(err.code/1000) == 23) {
|
||||
// Class 23 — Integrity Constraint Violation
|
||||
console.error(err);
|
||||
throw { status: 400, message: "Malformed request" };
|
||||
} else {
|
||||
throw err;
|
||||
}
|
||||
} finally {
|
||||
client.release();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
module.exports = post;
|
||||
36
lib/www/server/lib/db/plan/put.js
Normal file
36
lib/www/server/lib/db/plan/put.js
Normal file
@@ -0,0 +1,36 @@
|
||||
const { setSurvey, transaction } = require('../connection');
|
||||
|
||||
async function put (projectId, payload, opts = {}) {
|
||||
|
||||
const client = await setSurvey(projectId);
|
||||
try {
|
||||
|
||||
const p = Object.assign({
|
||||
remarks: "",
|
||||
meta: {}
|
||||
}, payload);
|
||||
|
||||
const text = `
|
||||
INSERT
|
||||
INTO planned_lines (sequence, line, fsp, lsp, ts0, ts1, name, remarks, meta)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9);
|
||||
`;
|
||||
const values = [ p.sequence, p.line, p.fsp, p.lsp, p.ts0, p.ts1, p.name, p.remarks, p.meta ];
|
||||
|
||||
await client.query(text, values);
|
||||
} catch (err) {
|
||||
if (err.code && Math.trunc(err.code/1000) == 23) {
|
||||
// Class 23 — Integrity Constraint Violation
|
||||
console.error(err);
|
||||
throw { status: 400, message: "Malformed request" };
|
||||
} else {
|
||||
throw err;
|
||||
}
|
||||
} finally {
|
||||
client.release();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
module.exports = put;
|
||||
Reference in New Issue
Block a user