NODEDC_PLATFORM/packages/external-provider-contract/providers/gelios/v7/package.mjs

215 lines
7.8 KiB
JavaScript

import { geliosProviderPackageV6 } from "../v6/package.mjs";
export const GELIOS_PROVIDER_PACKAGE_ID = "gelios.provider.v7";
export const GELIOS_PROVIDER_PACKAGE_VERSION = "7.0.0";
export const GELIOS_POSITIONS_DATA_PRODUCT_ID = "fleet.positions.current.v5";
export const GELIOS_POSITIONS_DATA_PRODUCT_VERSION = "5.0.0";
export const GELIOS_POSITIONS_ONTOLOGY_REVISION = "ontology.map.moving_object.v3";
export const GELIOS_UNIT_SOURCE_ID_PREFIX = "gelios-unit-";
const PREVIOUS_PRODUCT_ID = "fleet.positions.current.v4";
const PREVIOUS_FIELD_POLICY_ID = "gelios.positions.current.fields.v4";
const PREVIOUS_REALTIME_PROFILE_ID = "gelios.positions.current.realtime.v5";
const PREVIOUS_MANUAL_PROFILE_ID = "gelios.positions.current.manual.v5";
const PREVIOUS_MAPPING_ID = "gelios.units.to.fleet.positions.current.v5";
const PREVIOUS_TEMPLATE_ID = "gelios.positions.current.l2.v5";
const FIELD_POLICY_ID = "gelios.positions.current.fields.v5";
const REALTIME_PROFILE_ID = "gelios.positions.current.realtime.v7";
const MANUAL_PROFILE_ID = "gelios.positions.current.manual.v7";
const MAPPING_ID = "gelios.units.to.fleet.positions.current.v7";
const TEMPLATE_ID = "gelios.positions.current.l2.v7";
const positionFields = Object.freeze([
"course_degrees",
"display_name",
"elevation_meters",
"engine_hours",
"geometry",
"hdop",
"horizontal_accuracy_meters",
"mileage_km",
"movement_state",
"object_kind",
"position_source",
"satellite_count",
"sensor_readings",
"signal_state",
"speed_kph",
]);
const value = structuredClone(geliosProviderPackageV6);
const previousMapping = value.mappingContracts.find((mapping) => mapping.id === PREVIOUS_MAPPING_ID);
if (!previousMapping) throw new Error("gelios_v7_previous_mapping_missing");
value.id = GELIOS_PROVIDER_PACKAGE_ID;
value.version = GELIOS_PROVIDER_PACKAGE_VERSION;
value.manifest = {
...value.manifest,
id: "gelios.provider.manifest.v7",
version: GELIOS_PROVIDER_PACKAGE_VERSION,
fieldPolicyIds: replaceOne(value.manifest.fieldPolicyIds, PREVIOUS_FIELD_POLICY_ID, FIELD_POLICY_ID),
collectionProfileIds: replaceOne(
replaceOne(value.manifest.collectionProfileIds, PREVIOUS_REALTIME_PROFILE_ID, REALTIME_PROFILE_ID),
PREVIOUS_MANUAL_PROFILE_ID,
MANUAL_PROFILE_ID,
),
dataProductIds: replaceOne(value.manifest.dataProductIds, PREVIOUS_PRODUCT_ID, GELIOS_POSITIONS_DATA_PRODUCT_ID),
mappingContractIds: replaceOne(value.manifest.mappingContractIds, PREVIOUS_MAPPING_ID, MAPPING_ID),
l2TemplateIds: replaceOne(value.manifest.l2TemplateIds, PREVIOUS_TEMPLATE_ID, TEMPLATE_ID),
};
value.capabilities = value.capabilities.map((capability) => (
capability.id === "gelios.units.current.read"
? {
...capability,
request: {
...capability.request,
query: {
...capability.request.query,
incltrip: "true",
inclcntrs: "true",
inclsnsrs: "true",
incllsv: "true",
},
},
}
: capability
));
value.fieldPolicies = value.fieldPolicies.map((policy) => (
policy.id === PREVIOUS_FIELD_POLICY_ID
? {
...policy,
id: FIELD_POLICY_ID,
version: GELIOS_POSITIONS_DATA_PRODUCT_VERSION,
dataProductId: GELIOS_POSITIONS_DATA_PRODUCT_ID,
targetFields: [...positionFields],
restrictedSourcePaths: policy.restrictedSourcePaths
.filter((path) => path !== "sensors")
.concat(["sensors.conversionTable"]),
}
: policy
));
value.collectionProfiles = value.collectionProfiles.map((profile) => {
if (profile.dataProductId !== PREVIOUS_PRODUCT_ID) return profile;
return {
...profile,
id: profile.mode === "realtime" ? REALTIME_PROFILE_ID : MANUAL_PROFILE_ID,
version: GELIOS_PROVIDER_PACKAGE_VERSION,
dataProductId: GELIOS_POSITIONS_DATA_PRODUCT_ID,
mappingContractId: MAPPING_ID,
fieldPolicyId: FIELD_POLICY_ID,
l2TemplateId: TEMPLATE_ID,
};
});
value.dataProducts = value.dataProducts.map((product) => (
product.id === PREVIOUS_PRODUCT_ID
? {
id: GELIOS_POSITIONS_DATA_PRODUCT_ID,
version: GELIOS_POSITIONS_DATA_PRODUCT_VERSION,
ontologyRevision: GELIOS_POSITIONS_ONTOLOGY_REVISION,
deliveryMode: "snapshot+patch",
semanticTypes: ["map.moving_object"],
fields: [...positionFields],
fieldContracts: {
course_degrees: { type: "number", required: false, minimum: 0, maximum: 360 },
display_name: { type: "string", required: true },
elevation_meters: { type: "number", required: false },
engine_hours: { type: "number", required: false, minimum: 0 },
geometry: { type: "point", required: false },
hdop: { type: "number", required: false, minimum: 0 },
horizontal_accuracy_meters: { type: "number", required: false, minimum: 0 },
mileage_km: { type: "number", required: false, minimum: 0 },
movement_state: { type: "string", required: true, enum: ["moving", "stopped"] },
object_kind: { type: "string", required: true },
position_source: { type: "string", required: true },
satellite_count: { type: "number", required: false, minimum: 0 },
sensor_readings: { type: "telemetry_readings", required: false },
signal_state: { type: "string", required: true, enum: ["active", "inactive"] },
speed_kph: { type: "number", required: false, minimum: 0 },
},
history: { mode: "sampled", intervalMs: 60_000, strategy: "latest-per-entity-per-bucket", retentionDays: 90 },
}
: product
));
value.mappingContracts = value.mappingContracts.map((mapping) => {
if (mapping.id !== PREVIOUS_MAPPING_ID) return mapping;
return {
...mapping,
id: MAPPING_ID,
version: GELIOS_PROVIDER_PACKAGE_VERSION,
fieldPolicyId: FIELD_POLICY_ID,
target: {
...mapping.target,
dataProductId: GELIOS_POSITIONS_DATA_PRODUCT_ID,
version: GELIOS_POSITIONS_DATA_PRODUCT_VERSION,
ontologyRevision: GELIOS_POSITIONS_ONTOLOGY_REVISION,
},
derivations: {
...mapping.derivations,
sensor_readings: {
kind: "bounded_readings",
rules: ["last_sensor_values.normalized"],
default: [],
parameters: { maxReadings: 128 },
},
},
fact: {
...mapping.fact,
attributes: {
...mapping.fact.attributes,
engine_hours: {
strategy: "first_non_empty",
paths: ["counters.engineHours.value"],
coerce: "number",
minimum: 0,
omitIfMissing: true,
omitIfInvalid: true,
},
mileage_km: {
strategy: "first_non_empty",
paths: ["counters.mileage.value"],
coerce: "number",
minimum: 0,
omitIfMissing: true,
omitIfInvalid: true,
},
sensor_readings: { derive: "sensor_readings" },
},
},
};
});
value.l2Templates = value.l2Templates.map((template) => {
if (template.id !== PREVIOUS_TEMPLATE_ID) return template;
return {
...template,
id: TEMPLATE_ID,
version: GELIOS_PROVIDER_PACKAGE_VERSION,
steps: template.steps.map((step) => {
if (step.kind === "semantic_mapping") return { ...step, mappingContractId: MAPPING_ID };
if (step.kind === "data_product_publish") return { ...step, dataProductId: GELIOS_POSITIONS_DATA_PRODUCT_ID };
return step;
}),
};
});
export const geliosProviderPackageV7 = deepFreeze(value);
function replaceOne(items, previous, next) {
if (!Array.isArray(items) || items.filter((item) => item === previous).length !== 1) {
throw new Error(`gelios_v7_manifest_predecessor_invalid:${previous}`);
}
return items.map((item) => item === previous ? next : item);
}
function deepFreeze(input) {
if (!input || typeof input !== "object" || Object.isFrozen(input)) return input;
Object.freeze(input);
for (const child of Object.values(input)) deepFreeze(child);
return input;
}