From e464f5f887dffe53477a5696293dde8e6af0268e Mon Sep 17 00:00:00 2001 From: "D. Berge" Date: Fri, 1 Aug 2025 16:47:50 +0200 Subject: [PATCH] Refactor code handling binary sequence requests. Instead of the user giving the recipe for the payload, it now only handles predefined payload configurations. Those are denoted by the `type` query parameter. The only valid value as of this commit is `type=2`. Look at lib/binary/bundle.js for the definition of a type 2 bundle. --- .../api/middleware/sequence/get/binary.js | 48 +---- lib/www/server/lib/binary/bundle.js | 166 ++++++++++++++++++ lib/www/server/lib/binary/index.js | 1 + lib/www/server/package.json | 2 + 4 files changed, 171 insertions(+), 46 deletions(-) create mode 100644 lib/www/server/lib/binary/bundle.js diff --git a/lib/www/server/api/middleware/sequence/get/binary.js b/lib/www/server/api/middleware/sequence/get/binary.js index 56205f6..1b934d0 100644 --- a/lib/www/server/api/middleware/sequence/get/binary.js +++ b/lib/www/server/api/middleware/sequence/get/binary.js @@ -1,55 +1,11 @@ - +const { bundle } = require('../../../../lib/binary'); const { sequence } = require('../../../../lib/db'); -const { MSGTYPE, encode } = require('../../../../lib/binary'); module.exports = async function (req, res, next) { try { const json = await sequence.get(req.params.project, req.params.sequence, req.query); - - let msgType, geometry, error; - switch (req.query.geometry) { - case "F": - case "FINAL": - msgType = MSGTYPE.FINAL_OPT; - geometry = "geometryfinal"; - error = "errorfinal"; - break; - case "f": - case "final": - msgType = MSGTYPE.FINAL; - geometry = "geometryfinal"; - error = "errorfinal"; - break; - case "R": - case "RAW": - msgType = MSGTYPE.RAW_OPT; - geometry = "geometryraw"; - error = "errorraw"; - break; - case "r": - case "raw": - msgType = MSGTYPE.RAW; - geometry = "geometryraw"; - error = "errorraw"; - break; - case "P": - case "PREPLOT": - msgType = MSGTYPE.PREPLOT_RAWERROR_OPT; - geometry = "geometrypreplot"; - error = ["errorfinal", "errorraw"]; - break; - case "p": - case "preplot": - default: - msgType = MSGTYPE.PREPLOT; - geometry = "geometrypreplot"; - break; - } - - const endianness = req.query.endianness != "big"; - - const data = encode(json, geometry, error, msgType, endianness); + const data = bundle(json, {type: req.query.type}); console.log("bundle", data); res.status(200).send(Buffer.from(data)); diff --git a/lib/www/server/lib/binary/bundle.js b/lib/www/server/lib/binary/bundle.js new file mode 100644 index 0000000..bfec08c --- /dev/null +++ b/lib/www/server/lib/binary/bundle.js @@ -0,0 +1,166 @@ +const d3a = require('d3'); +const { encode } = require('@dougal/binary'); + +function bundle (json, opts = {}) { + + const { type, geometries, payload } = opts; + + const deltas = []; + const values = []; + + // console.log("JSON LENGTH", json.length); + // console.log("OPTS", geometries, payload); + + /* Gun information: + * + * - Δelem 0: BigUint64Array + Int16Array – timestamps + * - elem 0‒1: Float32Array, Float32Array – Raw positions (x, y) + * - elem 2‒3: Int16Array, Int16Array – (×10) Raw position errors (i, j) + * - elem 4‒6: Int8Array, Int8Array, Uint8Array – Gun deltas (μ, σ, R) + * - elem 7‒9: Uint16Array, Uint8Array, Uint16Array – Gun pressures (μ, σ, R) + * - elem 10‒12: Gun depths (μ, σ, R) + * - elem 13‒15: Gun fill times (μ, σ, R) + * - elem 16‒18: Gun delay (μ, σ, R) + * - elem 19: No fire / autofire (in a single byte) + */ + if (type == 2) { + + // Add timestamps + deltas.push({ + // Timestamp + key: el => el.tstamp.valueOf(), + baseType: BigUint64Array, + incrType: Int16Array + }); + + // Add raw positions + values.push({ + // longitude + key: el => el.geometryraw?.coordinates?.[0] ?? el.geometry.preplot?.coordinates?.[0], + type: Float32Array + }); + values.push({ + // latitude + key: el => el.geometryraw?.coordinates?.[1] ?? el.geometry.preplot?.coordinates?.[1], + type: Float32Array + }); + + // Add raw inline, crossline errors + values.push({ + // Crossline + key: el => el.errorraw?.coordinates?.[0] * 10, + type: Float32Array + }); + values.push({ + // Inline + key: el => el.errorraw?.coordinates?.[1] * 10, + type: Float32Array + }); + + // Include delta errors info + values.push({ + // Average + key: el => (el.meta?.raw?.smsrc?.avg_delta ?? 0) * 10, + type: Int8Array + }); + values.push({ + // Standard deviation + key: el => (el.meta?.raw?.smsrc?.std_delta ?? 0) * 10, + type: Int8Array + }); + values.push({ + // Spread + key: el => (el.meta?.raw?.smsrc?.spread ?? 0) * 10, + type: Uint8Array + }); + + // Include pressure info + const press = el => el.meta.raw?.smsrc?.guns?.map( i => i[11] ?? 0) ?? []; + + values.push({ + // Average + key: el => d3a.mean(press(el)) ?? 0, + type: Uint16Array + }); + values.push({ + // Standard deviation + key: el => d3a.deviation(press(el)) ?? 0, + type: Uint8Array + }); + values.push({ + // Spread + key: el => (d3a.max(press(el)) ?? 0) - (d3a.min(press(el)) ?? 0), + type: Uint16Array + }); + + // Include depth info (× 10) + const depths = el => el.meta.raw?.smsrc?.guns?.map( i => i[10] ?? 0).filter(i => i) ?? []; + + values.push({ + // Average + key: el => Math.min(d3a.mean(depths(el)) ?? 0, 25.5) * 10, + type: Uint8Array + }); + values.push({ + // Standard deviation + key: el => (d3a.deviation(depths(el)) ?? 0) * 10, + type: Uint8Array + }); + values.push({ + // Spread + key: el => ((d3a.max(depths(el)) ?? 0) - (d3a.min(depths(el)) ?? 0)) * 10, + type: Uint8Array + }); + + // Include fill times + const filltime = el => el.meta.raw?.smsrc?.guns?.map( i => i[13] ?? 0).filter(i => i) ?? []; + + values.push({ + // Average + key: el => d3a.mean(filltime(el)) ?? 0, + type: Uint16Array + }); + values.push({ + // Standard deviation + key: el => d3a.deviation(filltime(el)) ?? 0, + type: Uint16Array + }); + values.push({ + // Spread + key: el => ((d3a.max(filltime(el)) ?? 0) - (d3a.min(filltime(el)) ?? 0)), + type: Uint16Array + }); + + // Include fire delay + const delay = el => el.meta.raw?.smsrc?.guns?.map( i => i[9] ?? 0).filter(i => i) ?? []; + + values.push({ + // Average + key: el => (d3a.mean(delay(el)) ?? 0) * 10, + type: Uint16Array + }); + values.push({ + // Standard deviation + key: el => (d3a.deviation(delay(el)) ?? 0) * 10, + type: Uint16Array + }); + values.push({ + // Spread + key: el => ((d3a.max(delay(el)) ?? 0) - (d3a.min(delay(el)) ?? 0)) * 10, + type: Uint16Array + }); + + // Include no fire and autofire (same byte) + values.push({ + key: el => (el.meta.raw?.smsrc?.no_fire ?? 0) << 4 | (el.meta.raw?.smsrc?.num_auto ?? 0), + type: Uint8Array + }); + } + + console.log("DELTAS", deltas); + console.log("VALUES", values); + + return encode.sequential(json, el => el.sequence, el => el.point, deltas, values, type) +} + +module.exports = { bundle }; diff --git a/lib/www/server/lib/binary/index.js b/lib/www/server/lib/binary/index.js index 66ccf94..b8d13f2 100644 --- a/lib/www/server/lib/binary/index.js +++ b/lib/www/server/lib/binary/index.js @@ -1,3 +1,4 @@ module.exports = { + ...require('./bundle'), }; diff --git a/lib/www/server/package.json b/lib/www/server/package.json index 4fcbfe1..ff7a1f9 100644 --- a/lib/www/server/package.json +++ b/lib/www/server/package.json @@ -26,11 +26,13 @@ "linux" ], "dependencies": { + "@dougal/binary": "file:../../modules/@dougal/binary", "@dougal/organisations": "file:../../modules/@dougal/organisations", "@dougal/user": "file:../../modules/@dougal/user", "body-parser": "gitlab:aaltronav/contrib/expressjs/body-parser", "cookie-parser": "^1.4.5", "csv": "^6.3.3", + "d3": "^6.7.0", "debug": "^4.3.4", "express": "^4.17.1", "express-jwt": "^8.4.1",