feat(platform): complete the Gelios external data loop
This commit is contained in:
@@ -0,0 +1,20 @@
|
||||
# Gelios provider package v3
|
||||
|
||||
Version 3 describes the production REST transport used by the L2 workflow:
|
||||
`GET https://api.geliospro.com/api/v1/units`. The provider credential keeps the
|
||||
Gelios access/refresh pair inside native NDC L2 Credentials; n8n refreshes the
|
||||
access token at request time and exposes neither secret to the graph. The
|
||||
normalized output is the immutable provider-neutral Data Product
|
||||
`fleet.positions.current.v2@2.0.0`.
|
||||
|
||||
The product adds four orthogonal state facets owned by L2 semantic mapping:
|
||||
|
||||
- `availability_state`: `online`, `offline`, `unknown`;
|
||||
- `motion_state`: `moving`, `stationary`, `unknown`;
|
||||
- `position_state`: `valid`, `low_quality`, `missing`;
|
||||
- `freshness_state`: `fresh`, `stale`.
|
||||
|
||||
`state_policy_version` identifies the mapping policy that produced the facets.
|
||||
`operational_status` remains in the product as a backwards-compatible coarse
|
||||
state; presentation classes, colours, pin proportions and label geometry remain
|
||||
Foundry-owned and are never part of this provider package.
|
||||
@@ -0,0 +1,9 @@
|
||||
export {
|
||||
GELIOS_POSITIONS_DATA_PRODUCT_ID,
|
||||
GELIOS_POSITIONS_DATA_PRODUCT_VERSION,
|
||||
GELIOS_POSITIONS_ONTOLOGY_REVISION,
|
||||
GELIOS_PROVIDER_PACKAGE_ID,
|
||||
GELIOS_PROVIDER_PACKAGE_VERSION,
|
||||
GELIOS_UNIT_SOURCE_ID_PREFIX,
|
||||
geliosProviderPackageV3,
|
||||
} from "./package.mjs";
|
||||
@@ -0,0 +1,185 @@
|
||||
import { geliosProviderPackageV1 } from "../v1/package.mjs";
|
||||
|
||||
export const GELIOS_PROVIDER_PACKAGE_ID = "gelios.provider.v3";
|
||||
export const GELIOS_PROVIDER_PACKAGE_VERSION = "3.0.0";
|
||||
export const GELIOS_POSITIONS_DATA_PRODUCT_ID = "fleet.positions.current.v2";
|
||||
export const GELIOS_POSITIONS_DATA_PRODUCT_VERSION = "2.0.0";
|
||||
export const GELIOS_POSITIONS_ONTOLOGY_REVISION = "ontology.map.moving_object.v2";
|
||||
export const GELIOS_UNIT_SOURCE_ID_PREFIX = "gelios-unit-";
|
||||
|
||||
const FIELD_POLICY_ID = "gelios.positions.current.fields.v2";
|
||||
const AUTH_MODE_ID = "gelios.rest-rotating-bearer.v3";
|
||||
const REALTIME_PROFILE_ID = "gelios.positions.current.realtime.v3";
|
||||
const MANUAL_PROFILE_ID = "gelios.positions.current.manual.v3";
|
||||
const MAPPING_ID = "gelios.units.to.fleet.positions.current.v3";
|
||||
const TEMPLATE_ID = "gelios.positions.current.l2.v3";
|
||||
const STATE_POLICY_VERSION = "map-moving-object-state/v1";
|
||||
|
||||
const targetFields = Object.freeze([
|
||||
"availability_state",
|
||||
"course_degrees",
|
||||
"display_name",
|
||||
"elevation_meters",
|
||||
"freshness_state",
|
||||
"geometry",
|
||||
"hdop",
|
||||
"horizontal_accuracy_meters",
|
||||
"motion_state",
|
||||
"object_kind",
|
||||
"operational_status",
|
||||
"position_source",
|
||||
"position_state",
|
||||
"position_valid",
|
||||
"quality_flags",
|
||||
"satellite_count",
|
||||
"speed_kph",
|
||||
"state_policy_version",
|
||||
]);
|
||||
|
||||
const value = structuredClone(geliosProviderPackageV1);
|
||||
value.id = GELIOS_PROVIDER_PACKAGE_ID;
|
||||
value.version = GELIOS_PROVIDER_PACKAGE_VERSION;
|
||||
value.manifest = {
|
||||
...value.manifest,
|
||||
id: "gelios.provider.manifest.v3",
|
||||
version: GELIOS_PROVIDER_PACKAGE_VERSION,
|
||||
authModeIds: [AUTH_MODE_ID],
|
||||
fieldPolicyIds: [FIELD_POLICY_ID],
|
||||
collectionProfileIds: [REALTIME_PROFILE_ID, MANUAL_PROFILE_ID],
|
||||
dataProductIds: [GELIOS_POSITIONS_DATA_PRODUCT_ID],
|
||||
mappingContractIds: [MAPPING_ID],
|
||||
l2TemplateIds: [TEMPLATE_ID],
|
||||
};
|
||||
value.authModes = [{
|
||||
...value.authModes[0],
|
||||
id: AUTH_MODE_ID,
|
||||
tokenLifecycle: {
|
||||
artifacts: ["access", "refresh"],
|
||||
requestArtifact: "access",
|
||||
refreshMode: "runtime_managed",
|
||||
},
|
||||
}];
|
||||
value.capabilities = [{
|
||||
...value.capabilities[0],
|
||||
authModeId: AUTH_MODE_ID,
|
||||
}];
|
||||
value.fieldPolicies = [{
|
||||
...value.fieldPolicies[0],
|
||||
id: FIELD_POLICY_ID,
|
||||
version: GELIOS_POSITIONS_DATA_PRODUCT_VERSION,
|
||||
dataProductId: GELIOS_POSITIONS_DATA_PRODUCT_ID,
|
||||
targetFields: [...targetFields],
|
||||
}];
|
||||
value.collectionProfiles = value.collectionProfiles.map((profile, index) => ({
|
||||
...profile,
|
||||
id: index === 0 ? 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 = [{
|
||||
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: [...targetFields],
|
||||
history: {
|
||||
mode: "sampled",
|
||||
intervalMs: 60000,
|
||||
strategy: "latest-per-entity-per-bucket",
|
||||
retentionDays: 90,
|
||||
},
|
||||
}];
|
||||
value.mappingContracts = [{
|
||||
...value.mappingContracts[0],
|
||||
id: MAPPING_ID,
|
||||
version: GELIOS_PROVIDER_PACKAGE_VERSION,
|
||||
fieldPolicyId: FIELD_POLICY_ID,
|
||||
target: {
|
||||
...value.mappingContracts[0].target,
|
||||
dataProductId: GELIOS_POSITIONS_DATA_PRODUCT_ID,
|
||||
version: GELIOS_POSITIONS_DATA_PRODUCT_VERSION,
|
||||
ontologyRevision: GELIOS_POSITIONS_ONTOLOGY_REVISION,
|
||||
},
|
||||
derivations: {
|
||||
...value.mappingContracts[0].derivations,
|
||||
availability_state: {
|
||||
kind: "ordered_rules",
|
||||
rules: [
|
||||
"observed_age_gt_limit.offline",
|
||||
"otherwise.online",
|
||||
],
|
||||
default: "unknown",
|
||||
parameters: { staleAfterMs: 60000 },
|
||||
},
|
||||
freshness_state: {
|
||||
kind: "ordered_rules",
|
||||
rules: [
|
||||
"observed_age_gt_limit.stale",
|
||||
"otherwise.fresh",
|
||||
],
|
||||
default: "stale",
|
||||
parameters: { staleAfterMs: 60000 },
|
||||
},
|
||||
motion_state: {
|
||||
kind: "ordered_rules",
|
||||
rules: [
|
||||
"speed_kph_gte_threshold.moving",
|
||||
"speed_kph_lt_threshold.stationary",
|
||||
"otherwise.unknown",
|
||||
],
|
||||
default: "unknown",
|
||||
parameters: { movingThresholdKph: 1 },
|
||||
},
|
||||
position_state: {
|
||||
kind: "ordered_rules",
|
||||
rules: [
|
||||
"position_invalid.missing",
|
||||
"position_quality_below_threshold.low_quality",
|
||||
"otherwise.valid",
|
||||
],
|
||||
default: "missing",
|
||||
parameters: {
|
||||
minSatelliteCount: 4,
|
||||
maxHdop: 5,
|
||||
maxHorizontalAccuracyMeters: 100,
|
||||
},
|
||||
},
|
||||
},
|
||||
fact: {
|
||||
...value.mappingContracts[0].fact,
|
||||
attributes: {
|
||||
...value.mappingContracts[0].fact.attributes,
|
||||
availability_state: { derive: "availability_state" },
|
||||
freshness_state: { derive: "freshness_state" },
|
||||
motion_state: { derive: "motion_state" },
|
||||
position_state: { derive: "position_state" },
|
||||
state_policy_version: { constant: STATE_POLICY_VERSION },
|
||||
},
|
||||
},
|
||||
}];
|
||||
value.l2Templates = [{
|
||||
...value.l2Templates[0],
|
||||
id: TEMPLATE_ID,
|
||||
version: GELIOS_PROVIDER_PACKAGE_VERSION,
|
||||
credentialBindings: value.l2Templates[0].credentialBindings.map((binding) => (
|
||||
binding.role === "provider" ? { ...binding, authModeId: AUTH_MODE_ID } : binding
|
||||
)),
|
||||
steps: value.l2Templates[0].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 geliosProviderPackageV3 = deepFreeze(value);
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
# Gelios provider package v4
|
||||
|
||||
Version 4 is the first provider package whose monitoring states are derived
|
||||
strictly from the Gelios Ontology `v1.1.0` value contracts.
|
||||
|
||||
The collection uses two official safe-read capabilities with the same native
|
||||
rotating Gelios credential:
|
||||
|
||||
- `GET /api/v1/users/me/monitoring-config` supplies the account/user
|
||||
`signalActiveDuration` and `signalSomewhatInactiveDuration` values used by
|
||||
the official monitoring client;
|
||||
- `GET /api/v1/units?incltrip=true` supplies the complete credential-visible
|
||||
unit set, last-message time, speed and position facts.
|
||||
|
||||
The immutable provider-neutral output is
|
||||
`fleet.positions.current.v3@3.0.0`. Its only monitoring-state fields are:
|
||||
|
||||
- `signal_state`: `active` or `inactive`;
|
||||
- `movement_state`: `moving` or `stopped`.
|
||||
|
||||
The realtime collection profile resolves the signal threshold in one explicit
|
||||
order: a positive `signalSomewhatInactiveDuration`, then a positive
|
||||
`signalActiveDuration`, then the Robot2B profile fallback of `120` seconds.
|
||||
The fallback is profile metadata with observable provenance; it is never a
|
||||
silent mapper default. If no live threshold and no positive profile fallback
|
||||
exist, publication fails closed instead of manufacturing `inactive` facts.
|
||||
|
||||
There is no unknown, freshness, GPS-quality, position-quality, parked,
|
||||
no-position or aggregate operational-status state. Missing geometry remains an
|
||||
absent geometry fact and never becomes a status. Labels, counters, colours and
|
||||
layout remain Foundry presentation metadata, but Foundry may only use the exact
|
||||
values declared by Ontology.
|
||||
@@ -0,0 +1,9 @@
|
||||
export {
|
||||
GELIOS_POSITIONS_DATA_PRODUCT_ID,
|
||||
GELIOS_POSITIONS_DATA_PRODUCT_VERSION,
|
||||
GELIOS_POSITIONS_ONTOLOGY_REVISION,
|
||||
GELIOS_PROVIDER_PACKAGE_ID,
|
||||
GELIOS_PROVIDER_PACKAGE_VERSION,
|
||||
GELIOS_UNIT_SOURCE_ID_PREFIX,
|
||||
geliosProviderPackageV4,
|
||||
} from "./package.mjs";
|
||||
@@ -0,0 +1,214 @@
|
||||
import { geliosProviderPackageV3 } from "../v3/package.mjs";
|
||||
|
||||
export const GELIOS_PROVIDER_PACKAGE_ID = "gelios.provider.v4";
|
||||
export const GELIOS_PROVIDER_PACKAGE_VERSION = "4.0.0";
|
||||
export const GELIOS_POSITIONS_DATA_PRODUCT_ID = "fleet.positions.current.v3";
|
||||
export const GELIOS_POSITIONS_DATA_PRODUCT_VERSION = "3.0.0";
|
||||
export const GELIOS_POSITIONS_ONTOLOGY_REVISION = "ontology.map.moving_object.v3";
|
||||
export const GELIOS_UNIT_SOURCE_ID_PREFIX = "gelios-unit-";
|
||||
|
||||
const UNIT_CAPABILITY_ID = "gelios.units.current.read";
|
||||
const MONITORING_CONFIG_CAPABILITY_ID = "gelios.monitoring_config.current.read";
|
||||
const FIELD_POLICY_ID = "gelios.positions.current.fields.v3";
|
||||
const REALTIME_PROFILE_ID = "gelios.positions.current.realtime.v4";
|
||||
const MANUAL_PROFILE_ID = "gelios.positions.current.manual.v4";
|
||||
const MAPPING_ID = "gelios.units.to.fleet.positions.current.v4";
|
||||
const TEMPLATE_ID = "gelios.positions.current.l2.v4";
|
||||
|
||||
const targetFields = Object.freeze([
|
||||
"course_degrees",
|
||||
"display_name",
|
||||
"elevation_meters",
|
||||
"geometry",
|
||||
"hdop",
|
||||
"horizontal_accuracy_meters",
|
||||
"movement_state",
|
||||
"object_kind",
|
||||
"position_source",
|
||||
"satellite_count",
|
||||
"signal_state",
|
||||
"speed_kph",
|
||||
]);
|
||||
|
||||
const value = structuredClone(geliosProviderPackageV3);
|
||||
const unitCapability = value.capabilities.find((capability) => capability.id === UNIT_CAPABILITY_ID);
|
||||
const baseMapping = value.mappingContracts[0];
|
||||
const baseAttributes = baseMapping.fact.attributes;
|
||||
const authModeId = value.authModes[0].id;
|
||||
|
||||
value.id = GELIOS_PROVIDER_PACKAGE_ID;
|
||||
value.version = GELIOS_PROVIDER_PACKAGE_VERSION;
|
||||
value.manifest = {
|
||||
...value.manifest,
|
||||
id: "gelios.provider.manifest.v4",
|
||||
version: GELIOS_PROVIDER_PACKAGE_VERSION,
|
||||
ontology: {
|
||||
packageId: "gelios",
|
||||
revision: "ontology.gelios.v1_1",
|
||||
},
|
||||
capabilityIds: [MONITORING_CONFIG_CAPABILITY_ID, UNIT_CAPABILITY_ID],
|
||||
fieldPolicyIds: [FIELD_POLICY_ID],
|
||||
collectionProfileIds: [REALTIME_PROFILE_ID, MANUAL_PROFILE_ID],
|
||||
dataProductIds: [GELIOS_POSITIONS_DATA_PRODUCT_ID],
|
||||
mappingContractIds: [MAPPING_ID],
|
||||
l2TemplateIds: [TEMPLATE_ID],
|
||||
};
|
||||
value.capabilities = [{
|
||||
id: MONITORING_CONFIG_CAPABILITY_ID,
|
||||
classification: "read",
|
||||
status: "implemented",
|
||||
authModeId,
|
||||
request: {
|
||||
method: "GET",
|
||||
baseUrl: "https://api.geliospro.com",
|
||||
path: "/api/v1/users/me/monitoring-config",
|
||||
query: {},
|
||||
response: {
|
||||
collectionPaths: ["$", "data"],
|
||||
pagination: "single_bounded_response",
|
||||
},
|
||||
},
|
||||
entityScope: {
|
||||
mode: "all_visible_to_credential",
|
||||
refresh: "each_collection_run",
|
||||
businessEntityFilter: "forbidden",
|
||||
},
|
||||
}, {
|
||||
...unitCapability,
|
||||
request: {
|
||||
...unitCapability.request,
|
||||
query: { incltrip: "true" },
|
||||
},
|
||||
}];
|
||||
value.fieldPolicies = [{
|
||||
...value.fieldPolicies[0],
|
||||
id: FIELD_POLICY_ID,
|
||||
version: GELIOS_POSITIONS_DATA_PRODUCT_VERSION,
|
||||
dataProductId: GELIOS_POSITIONS_DATA_PRODUCT_ID,
|
||||
targetFields: [...targetFields],
|
||||
}];
|
||||
value.collectionProfiles = value.collectionProfiles.map((profile, index) => ({
|
||||
...profile,
|
||||
id: index === 0 ? REALTIME_PROFILE_ID : MANUAL_PROFILE_ID,
|
||||
version: GELIOS_PROVIDER_PACKAGE_VERSION,
|
||||
capabilityIds: [MONITORING_CONFIG_CAPABILITY_ID, UNIT_CAPABILITY_ID],
|
||||
dataProductId: GELIOS_POSITIONS_DATA_PRODUCT_ID,
|
||||
mappingContractId: MAPPING_ID,
|
||||
fieldPolicyId: FIELD_POLICY_ID,
|
||||
l2TemplateId: TEMPLATE_ID,
|
||||
}));
|
||||
value.dataProducts = [{
|
||||
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: [...targetFields],
|
||||
history: {
|
||||
mode: "sampled",
|
||||
intervalMs: 60000,
|
||||
strategy: "latest-per-entity-per-bucket",
|
||||
retentionDays: 90,
|
||||
},
|
||||
}];
|
||||
value.mappingContracts = [{
|
||||
...baseMapping,
|
||||
id: MAPPING_ID,
|
||||
version: GELIOS_PROVIDER_PACKAGE_VERSION,
|
||||
sourceCapabilityId: UNIT_CAPABILITY_ID,
|
||||
fieldPolicyId: FIELD_POLICY_ID,
|
||||
target: {
|
||||
...baseMapping.target,
|
||||
dataProductId: GELIOS_POSITIONS_DATA_PRODUCT_ID,
|
||||
version: GELIOS_POSITIONS_DATA_PRODUCT_VERSION,
|
||||
ontologyRevision: GELIOS_POSITIONS_ONTOLOGY_REVISION,
|
||||
},
|
||||
derivations: {
|
||||
signal_state: {
|
||||
kind: "ordered_rules",
|
||||
rules: [
|
||||
"last_message_missing.inactive",
|
||||
"last_message_age_lt_resolved_monitoring_limit.active",
|
||||
"otherwise.inactive",
|
||||
],
|
||||
default: "inactive",
|
||||
parameters: {
|
||||
monitoringConfigCapability: MONITORING_CONFIG_CAPABILITY_ID,
|
||||
activeDurationPath: "signalActiveDuration",
|
||||
somewhatInactiveDurationPath: "signalSomewhatInactiveDuration",
|
||||
inactiveDurationPath: "signalSomewhatInactiveDuration",
|
||||
collectionProfileFallbackSeconds: 120,
|
||||
thresholdResolution: "somewhatInactive_then_active_then_profileFallback",
|
||||
lastMessageTimePath: "lastMsg.time",
|
||||
},
|
||||
},
|
||||
movement_state: {
|
||||
kind: "ordered_rules",
|
||||
rules: [
|
||||
"integer_speed_gt_threshold.moving",
|
||||
"otherwise.stopped",
|
||||
],
|
||||
default: "stopped",
|
||||
parameters: {
|
||||
speedPath: "lastMsg.speed",
|
||||
movingThresholdKph: 2,
|
||||
},
|
||||
},
|
||||
},
|
||||
fact: {
|
||||
...baseMapping.fact,
|
||||
attributes: {
|
||||
course_degrees: baseAttributes.course_degrees,
|
||||
display_name: baseAttributes.display_name,
|
||||
elevation_meters: baseAttributes.elevation_meters,
|
||||
hdop: baseAttributes.hdop,
|
||||
horizontal_accuracy_meters: baseAttributes.horizontal_accuracy_meters,
|
||||
movement_state: { derive: "movement_state" },
|
||||
object_kind: baseAttributes.object_kind,
|
||||
position_source: baseAttributes.position_source,
|
||||
satellite_count: baseAttributes.satellite_count,
|
||||
signal_state: { derive: "signal_state" },
|
||||
speed_kph: baseAttributes.speed_kph,
|
||||
},
|
||||
},
|
||||
}];
|
||||
value.l2Templates = [{
|
||||
...value.l2Templates[0],
|
||||
id: TEMPLATE_ID,
|
||||
version: GELIOS_PROVIDER_PACKAGE_VERSION,
|
||||
steps: [{
|
||||
id: "collection.trigger",
|
||||
kind: "collection_trigger",
|
||||
collectionProfileDriven: true,
|
||||
}, {
|
||||
id: "provider.fetch-monitoring-config",
|
||||
kind: "provider_request",
|
||||
capabilityId: MONITORING_CONFIG_CAPABILITY_ID,
|
||||
}, {
|
||||
id: "provider.fetch-units",
|
||||
kind: "provider_request",
|
||||
capabilityId: UNIT_CAPABILITY_ID,
|
||||
}, {
|
||||
id: "provider.extract-units",
|
||||
kind: "extract_items",
|
||||
capabilityId: UNIT_CAPABILITY_ID,
|
||||
}, {
|
||||
id: "ontology.map",
|
||||
kind: "semantic_mapping",
|
||||
mappingContractId: MAPPING_ID,
|
||||
}, {
|
||||
id: "data-product.publish",
|
||||
kind: "data_product_publish",
|
||||
dataProductId: GELIOS_POSITIONS_DATA_PRODUCT_ID,
|
||||
nodeType: "n8n-nodes-ndc.ndcDataProductPublish",
|
||||
}],
|
||||
}];
|
||||
|
||||
export const geliosProviderPackageV4 = deepFreeze(value);
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
# Gelios provider package v5
|
||||
|
||||
Version 5 preserves the Ontology-exact monitoring logic from provider package
|
||||
v4 and publishes it through the immutable `fleet.positions.current.v4@4.0.0`
|
||||
contract.
|
||||
|
||||
The Data Product now declares a machine-enforced contract for every published
|
||||
field. In particular:
|
||||
|
||||
- `signal_state` is required and accepts only `active` or `inactive`;
|
||||
- `movement_state` is required and accepts only `moving` or `stopped`;
|
||||
- identity/presentation attributes are typed and required where the mapping
|
||||
must always produce them;
|
||||
- optional telemetry is type-checked and bounded where the physical domain has
|
||||
a stable lower or upper limit;
|
||||
- geometry remains optional, but when present it must be a valid GeoJSON Point.
|
||||
|
||||
The provider mapping is checked against the same contract before it can be
|
||||
packaged, and External Data Plane checks every publish at runtime. Existing
|
||||
v1-v3 products remain immutable and backward compatible.
|
||||
@@ -0,0 +1,9 @@
|
||||
export {
|
||||
GELIOS_POSITIONS_DATA_PRODUCT_ID,
|
||||
GELIOS_POSITIONS_DATA_PRODUCT_VERSION,
|
||||
GELIOS_POSITIONS_ONTOLOGY_REVISION,
|
||||
GELIOS_PROVIDER_PACKAGE_ID,
|
||||
GELIOS_PROVIDER_PACKAGE_VERSION,
|
||||
GELIOS_UNIT_SOURCE_ID_PREFIX,
|
||||
geliosProviderPackageV5,
|
||||
} from "./package.mjs";
|
||||
@@ -0,0 +1,105 @@
|
||||
import { geliosProviderPackageV4 } from "../v4/package.mjs";
|
||||
|
||||
export const GELIOS_PROVIDER_PACKAGE_ID = "gelios.provider.v5";
|
||||
export const GELIOS_PROVIDER_PACKAGE_VERSION = "5.0.0";
|
||||
export const GELIOS_POSITIONS_DATA_PRODUCT_ID = "fleet.positions.current.v4";
|
||||
export const GELIOS_POSITIONS_DATA_PRODUCT_VERSION = "4.0.0";
|
||||
export const GELIOS_POSITIONS_ONTOLOGY_REVISION = "ontology.map.moving_object.v3";
|
||||
export const GELIOS_UNIT_SOURCE_ID_PREFIX = "gelios-unit-";
|
||||
|
||||
const FIELD_POLICY_ID = "gelios.positions.current.fields.v4";
|
||||
const REALTIME_PROFILE_ID = "gelios.positions.current.realtime.v5";
|
||||
const MANUAL_PROFILE_ID = "gelios.positions.current.manual.v5";
|
||||
const MAPPING_ID = "gelios.units.to.fleet.positions.current.v5";
|
||||
const TEMPLATE_ID = "gelios.positions.current.l2.v5";
|
||||
|
||||
const value = structuredClone(geliosProviderPackageV4);
|
||||
const dataProduct = {
|
||||
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: [...value.dataProducts[0].fields],
|
||||
fieldContracts: {
|
||||
course_degrees: { type: "number", required: false, minimum: 0, maximum: 360 },
|
||||
display_name: { type: "string", required: true },
|
||||
elevation_meters: { type: "number", required: false },
|
||||
geometry: { type: "point", required: false },
|
||||
hdop: { type: "number", required: false, minimum: 0 },
|
||||
horizontal_accuracy_meters: { 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 },
|
||||
signal_state: { type: "string", required: true, enum: ["active", "inactive"] },
|
||||
speed_kph: { type: "number", required: false, minimum: 0 },
|
||||
},
|
||||
history: { ...value.dataProducts[0].history },
|
||||
};
|
||||
|
||||
value.id = GELIOS_PROVIDER_PACKAGE_ID;
|
||||
value.version = GELIOS_PROVIDER_PACKAGE_VERSION;
|
||||
value.manifest = {
|
||||
...value.manifest,
|
||||
id: "gelios.provider.manifest.v5",
|
||||
version: GELIOS_PROVIDER_PACKAGE_VERSION,
|
||||
fieldPolicyIds: [FIELD_POLICY_ID],
|
||||
collectionProfileIds: [REALTIME_PROFILE_ID, MANUAL_PROFILE_ID],
|
||||
dataProductIds: [GELIOS_POSITIONS_DATA_PRODUCT_ID],
|
||||
mappingContractIds: [MAPPING_ID],
|
||||
l2TemplateIds: [TEMPLATE_ID],
|
||||
};
|
||||
value.fieldPolicies = value.fieldPolicies.map((policy) => ({
|
||||
...policy,
|
||||
id: FIELD_POLICY_ID,
|
||||
version: GELIOS_POSITIONS_DATA_PRODUCT_VERSION,
|
||||
dataProductId: GELIOS_POSITIONS_DATA_PRODUCT_ID,
|
||||
}));
|
||||
value.collectionProfiles = value.collectionProfiles.map((profile, index) => ({
|
||||
...profile,
|
||||
id: index === 0 ? 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 = [dataProduct];
|
||||
value.mappingContracts = value.mappingContracts.map((mapping) => ({
|
||||
...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,
|
||||
},
|
||||
}));
|
||||
const attributes = value.mappingContracts[0].fact.attributes;
|
||||
Object.assign(attributes.course_degrees, { minimum: 0, maximum: 360, omitIfInvalid: true });
|
||||
Object.assign(attributes.hdop, { minimum: 0, omitIfInvalid: true });
|
||||
Object.assign(attributes.horizontal_accuracy_meters, { minimum: 0, omitIfInvalid: true });
|
||||
Object.assign(attributes.satellite_count, { minimum: 0, omitIfInvalid: true });
|
||||
Object.assign(attributes.speed_kph, { minimum: 0, omitIfInvalid: true });
|
||||
value.l2Templates = value.l2Templates.map((template) => ({
|
||||
...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 geliosProviderPackageV5 = deepFreeze(value);
|
||||
|
||||
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;
|
||||
}
|
||||
Reference in New Issue
Block a user