EVOLV CoreSync node — FROST/SensorThings handoff path. First version forwards FROST-ready HTTP request messages on the dbase output; a downstream Node-RED http-request node performs the actual POST and feeds responses back on msg.topic = "frost.response". Lazy stream resolver, latest-wins queue for unresolved/FROST-down streams (keep first + latest, drop middle), knot-emit on slope change, provenance preserved in Observation parameters. Interview state + open Q20 (slope angle vs. relative delta) recorded in superproject CORESYNC_FROST_INTERVIEW_HANDOFF.md. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
77 lines
2.1 KiB
JavaScript
77 lines
2.1 KiB
JavaScript
'use strict';
|
|
|
|
const DEFAULT_UNITS = {
|
|
pressure: 'Pa',
|
|
flow: 'm3/s',
|
|
power: 'W',
|
|
temperature: 'K',
|
|
density: 'kg/m3',
|
|
level: 'm',
|
|
volume: 'm3',
|
|
control: '1',
|
|
percentage: '1',
|
|
efficiency: '1',
|
|
};
|
|
|
|
function firstPresent(...values) {
|
|
for (const value of values) {
|
|
if (value !== undefined && value !== null && value !== '') return value;
|
|
}
|
|
return undefined;
|
|
}
|
|
|
|
function sanitizeToken(value, fallback) {
|
|
const raw = firstPresent(value, fallback);
|
|
return String(raw || '').trim();
|
|
}
|
|
|
|
function parseFieldKey(fieldKey) {
|
|
const parts = String(fieldKey || '').split('.').filter(Boolean);
|
|
return {
|
|
type: parts[0] || 'value',
|
|
variant: parts[1] || 'measured',
|
|
position: parts[2] || 'atEquipment',
|
|
sensorTag: parts[3] || undefined,
|
|
};
|
|
}
|
|
|
|
function resolveIdentity(input, options = {}) {
|
|
const tags = input.tags || {};
|
|
const parsed = parseFieldKey(input.fieldKey);
|
|
const thingTag = sanitizeToken(
|
|
options.assetTagOverride,
|
|
tags.tagcode || tags.tagCode || tags.asset_tagCode || tags.asset_tagcode || input.measurement,
|
|
);
|
|
const type = sanitizeToken(input.type, parsed.type).toLowerCase();
|
|
const variant = sanitizeToken(input.variant, parsed.variant).toLowerCase();
|
|
const position = sanitizeToken(input.position, parsed.position);
|
|
const sensorTag = sanitizeToken(
|
|
options.sensorTagOverride,
|
|
parsed.sensorTag || tags.sensorTag || tags.sensor_tagCode || `${variant.toUpperCase()}-${thingTag}`,
|
|
);
|
|
const unit = sanitizeToken(input.unit, tags.unit || input.source?.unit || DEFAULT_UNITS[type] || '1');
|
|
const streamKey = [thingTag, type, variant, position, sensorTag].join(':');
|
|
|
|
return {
|
|
thingTag,
|
|
type,
|
|
variant,
|
|
position,
|
|
sensorTag,
|
|
unit,
|
|
fieldKey: input.fieldKey,
|
|
streamKey,
|
|
thingExternalKey: `thing:${thingTag}`,
|
|
observedPropertyExternalKey: `observedProperty:${type}`,
|
|
sensorExternalKey: `sensor:${sensorTag}`,
|
|
featureOfInterestExternalKey: `foi:${thingTag}:${position}`,
|
|
datastreamExternalKey: `datastream:${streamKey}`,
|
|
};
|
|
}
|
|
|
|
module.exports = {
|
|
DEFAULT_UNITS,
|
|
parseFieldKey,
|
|
resolveIdentity,
|
|
};
|