mirror of
https://gitlab.com/wgp/dougal/software.git
synced 2025-12-06 10:57:07 +00:00
Make planned line names configurable.
Line names are made up based on: * Certain properties defined by the system * Values assigned to those properties either by the system or by the user (line number, sequence, attempt, etc.) * A line format specification configured by the user for each project (`online.line.lineNameBuilder.fields`) Closes #129.
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
module.exports = {
|
||||
project: require('./project'),
|
||||
line: require('./line'),
|
||||
linename: require('./linename'),
|
||||
sequence: require('./sequence'),
|
||||
event: require('./event'),
|
||||
plan: require('./plan'),
|
||||
|
||||
4
lib/www/server/lib/db/linename/index.js
Normal file
4
lib/www/server/lib/db/linename/index.js
Normal file
@@ -0,0 +1,4 @@
|
||||
module.exports = {
|
||||
properties: require('./properties'),
|
||||
post: require('./post'),
|
||||
};
|
||||
39
lib/www/server/lib/db/linename/post.js
Normal file
39
lib/www/server/lib/db/linename/post.js
Normal file
@@ -0,0 +1,39 @@
|
||||
const { setSurvey, transaction } = require('../connection');
|
||||
const lib = require('../plan/lib');
|
||||
|
||||
async function post (projectId, payload, opts = {}) {
|
||||
|
||||
const client = await setSurvey(projectId);
|
||||
try {
|
||||
|
||||
if (!payload.sequence) {
|
||||
payload.sequence = await lib.getSequence(client);
|
||||
}
|
||||
// if (!payload.ts0 || !payload.ts1) {
|
||||
// const ts = await lib.getTimestamps(client, projectId, payload);
|
||||
// if (!payload.ts0) {
|
||||
// payload.ts0 = ts.ts0;
|
||||
// }
|
||||
// if (!payload.ts1) {
|
||||
// payload.ts1 = ts.ts1;
|
||||
// }
|
||||
// }
|
||||
const name = await lib.getLineName(client, projectId, payload);
|
||||
|
||||
return name;
|
||||
} 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;
|
||||
15
lib/www/server/lib/db/linename/properties/get.js
Normal file
15
lib/www/server/lib/db/linename/properties/get.js
Normal file
@@ -0,0 +1,15 @@
|
||||
const lib = require('../../plan/lib');
|
||||
|
||||
async function get (projectId, payload, opts = {}) {
|
||||
|
||||
try {
|
||||
|
||||
return await lib.getLineNameProperties();
|
||||
|
||||
} catch (err) {
|
||||
throw err;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = get;
|
||||
3
lib/www/server/lib/db/linename/properties/index.js
Normal file
3
lib/www/server/lib/db/linename/properties/index.js
Normal file
@@ -0,0 +1,3 @@
|
||||
module.exports = {
|
||||
get: require('./get'),
|
||||
};
|
||||
@@ -1,6 +1,12 @@
|
||||
const YAML = require('yaml');
|
||||
const fs = require('fs').promises;
|
||||
const path = require('path');
|
||||
|
||||
const alert = require("../../../alerts");
|
||||
const configuration = require('../../configuration');
|
||||
|
||||
let lineNameProperties;
|
||||
|
||||
async function getDistance (client, payload) {
|
||||
const text = `
|
||||
SELECT ST_Distance(pp0.geometry, pp1.geometry) distance
|
||||
@@ -88,8 +94,6 @@ async function getPlanned (client) {
|
||||
|
||||
|
||||
async function getLineName (client, projectId, payload) {
|
||||
// FIXME TODO Get line name script from configuration
|
||||
// Ref.: https://gitlab.com/wgp/dougal/software/-/issues/129
|
||||
|
||||
// This is to monitor #165
|
||||
// https://gitlab.com/wgp/dougal/software/-/issues/incident/165
|
||||
@@ -97,6 +101,36 @@ async function getLineName (client, projectId, payload) {
|
||||
alert({function: "getLineName", client, projectId, payload});
|
||||
}
|
||||
|
||||
const lineNameBuilder = await configuration.get(projectId, "online/line/lineNameBuilder");
|
||||
const fields = lineNameBuilder?.fields;
|
||||
|
||||
if (fields) {
|
||||
const properties = await getLineNameProperties();
|
||||
const values = await getLineNameValues(client, projectId, payload, lineNameBuilder?.values);
|
||||
return buildLineName(properties, fields, values, payload?.name);
|
||||
} else {
|
||||
// TODO send a user notification via WS to let them know
|
||||
// they haven't configured the line name parameters
|
||||
}
|
||||
|
||||
// return undefined
|
||||
}
|
||||
|
||||
/** Get line properties that go into making a line name.
|
||||
*
|
||||
* The properties are defined in a separate YAML file for
|
||||
* convenience.
|
||||
*/
|
||||
async function getLineNameProperties () {
|
||||
if (!lineNameProperties) {
|
||||
const buffer = await fs.readFile(path.join(__dirname, 'linename-properties.yaml'));
|
||||
lineNameProperties = YAML.parse(buffer.toString());
|
||||
}
|
||||
|
||||
return lineNameProperties;
|
||||
}
|
||||
|
||||
async function getLineNameValues (client, projectId, payload, otherValues = {}) {
|
||||
const planned = await getPlanned(client);
|
||||
const previous = await getSequencesForLine(client, payload.line);
|
||||
const attempt = planned.filter(r => r.line == payload.line).concat(previous).length;
|
||||
@@ -104,9 +138,79 @@ async function getLineName (client, projectId, payload) {
|
||||
const incr = p.lsp > p.fsp;
|
||||
const sequence = p.sequence || 1;
|
||||
const line = p.line;
|
||||
return `${incr?"1":"2"}0${line}${attempt}${sequence.toString().padStart(3, "0")}S00000`;
|
||||
|
||||
return {
|
||||
...structuredClone(otherValues),
|
||||
line_number: payload.line,
|
||||
sequence_number: payload.sequence || 1,
|
||||
original_sequence: payload.meta?.original_sequence,
|
||||
pass_number: attempt,
|
||||
is_prime: attempt == 0,
|
||||
is_reshoot: payload.meta?.is_reshoot ?? (!payload.meta?.is_infill && attempt > 0),
|
||||
is_infill: payload.meta?.is_infill ?? false,
|
||||
direction: null, // TODO
|
||||
is_incrementing: incr
|
||||
};
|
||||
}
|
||||
|
||||
/** Compute the string representation of a line name field
|
||||
*/
|
||||
function fieldValue (properties, field, values) {
|
||||
let value;
|
||||
|
||||
if (field.item == "text") {
|
||||
value = field.value;
|
||||
} else if (properties[field.item]?.type == "boolean") {
|
||||
if (values[field.item] === field.when) {
|
||||
value = field.value;
|
||||
}
|
||||
} else {
|
||||
value = values[field.item];
|
||||
}
|
||||
|
||||
if (value != null) {
|
||||
|
||||
if (properties[field.item]?.type == "number") {
|
||||
if (field.scale_multiplier != null) {
|
||||
value *= field.scale_multiplier;
|
||||
}
|
||||
if (field.scale_offset != null) {
|
||||
value += field.scale_offset;
|
||||
}
|
||||
|
||||
if (field.format == "integer") {
|
||||
value = Math.round(value);
|
||||
}
|
||||
}
|
||||
|
||||
value = String(value);
|
||||
if (field.pad_side == "left") {
|
||||
value = value.padStart(field.length, field.pad_string ?? " ");
|
||||
} else if (field.pad_side == "right") {
|
||||
value = value.padEnd(field.length, field.pad_string ?? " ");
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** Build a line name out of its component properties, fields and values.
|
||||
*
|
||||
* NOTE: This is the same function as available client-side on
|
||||
* `fixed-string-encoder.vue`. Consider merging them.
|
||||
*/
|
||||
function buildLineName (properties, fields, values, str = "") {
|
||||
const length = fields.reduce( (acc, cur) => (cur.offset + cur.length) > acc ? (cur.offset + cur.length) : acc, str.length )
|
||||
str = str.padEnd(length);
|
||||
for (const field of fields) {
|
||||
const value = fieldValue(properties, field, values);
|
||||
if (value != null) {
|
||||
str = str.slice(0, field.offset) + value + str.slice(field.offset + field.length);
|
||||
}
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
getDistance,
|
||||
@@ -114,5 +218,8 @@ module.exports = {
|
||||
getTimestamps,
|
||||
getSequencesForLine,
|
||||
getPlanned,
|
||||
getLineName
|
||||
getLineNameProperties,
|
||||
getLineNameValues,
|
||||
getLineName,
|
||||
buildLineName
|
||||
};
|
||||
|
||||
51
lib/www/server/lib/db/plan/lib/linename-properties.yaml
Normal file
51
lib/www/server/lib/db/plan/lib/linename-properties.yaml
Normal file
@@ -0,0 +1,51 @@
|
||||
#
|
||||
# These are the properties that can be used to build
|
||||
# line names.
|
||||
#
|
||||
|
||||
line_number:
|
||||
summary: Line number
|
||||
description: The sailline number that is to be acquired
|
||||
type: number
|
||||
format: integer
|
||||
sequence_number:
|
||||
summary: Sequence
|
||||
description: The sequence number that will be assigned to this line
|
||||
type: number
|
||||
format: integer
|
||||
original_sequence:
|
||||
summary: Original sequence
|
||||
description: The original sequence number of the line that is being reshot
|
||||
type: number
|
||||
format: integer
|
||||
pass_number:
|
||||
summary: Pass number
|
||||
description: The number of times this line, or section of line, has been shot
|
||||
type: number
|
||||
format: integer
|
||||
is_prime:
|
||||
summary: Prime line
|
||||
description: Whether this is the first time this line is being acquired
|
||||
type: boolean
|
||||
is_reshoot:
|
||||
summary: Reshoot
|
||||
description: Whether this is a reshoot (mutually exclusive with `is_prime` and `is_infill`)
|
||||
type: boolean
|
||||
is_infill:
|
||||
summary: Infill line
|
||||
description: Whether this is an infill line (mutually exclusive with `is_prime` and `is_reshoot`)
|
||||
type: boolean
|
||||
direction:
|
||||
summary: Line azimuth
|
||||
direction: The line azimuth in the Incrementing shotpoints direction
|
||||
type: number
|
||||
format: float
|
||||
is_incrementing:
|
||||
summary: Incrementing
|
||||
description: Whether the line is being shot low to high point numbers or vice versa
|
||||
type: boolean
|
||||
text:
|
||||
summary: Fixed text
|
||||
description: Arbitrary user-entered text (line prefix, suffix, etc.)
|
||||
type: text
|
||||
|
||||
Reference in New Issue
Block a user