signalk-parquet
Version:
SignalK plugin and webapp that archives SK data to Parquet files with a regimen control system, advanced querying, Claude integrated AI analysis, spatial capabilities, and REST API.
205 lines • 6.99 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.detectPathType = detectPathType;
exports.detectPathTypeFromSample = detectPathTypeFromSample;
/**
* Numeric units that indicate a numeric data type
*/
const NUMERIC_UNITS = [
'm', // meters
'm/s', // meters per second
'knots', // nautical miles per hour
'V', // volts
'A', // amperes
'Hz', // hertz
'K', // kelvin
'Pa', // pascals
'kg', // kilograms
'J', // joules
'ratio', // dimensionless ratio
'deg', // degrees (non-angular context like temperature)
];
/**
* Angular units that require degree/radian conversion
*/
const ANGULAR_UNITS = [
'rad', // radians
];
/**
* Position-specific paths
*/
const POSITION_PATHS = [
'navigation.position',
'navigation.gnss.antennaPosition',
];
/**
* Detect the data type of a SignalK path based on metadata
* Based on schema-service.ts metadata lookup pattern
*/
async function detectPathType(path, app) {
try {
// Special case: all paths ending with "position" are position types
if (path.endsWith('position') || POSITION_PATHS.includes(path)) {
return {
dataType: 'position',
description: 'Geographic position (latitude/longitude)',
};
}
// Try to get metadata from SignalK
let metadata;
if (app) {
// Use app's getMetadata method if available
metadata = app.getMetadata(path);
}
if (!metadata) {
app?.debug(`Type detection: metadata not found for ${path}`);
return { dataType: 'unknown' };
}
// Type guard helpers
const getStringProperty = (obj, key) => {
const value = obj[key];
return typeof value === 'string' ? value : undefined;
};
const getArrayProperty = (obj, key) => {
const value = obj[key];
return Array.isArray(value) ? value : undefined;
};
// Check for units to determine numeric vs angular
const unit = getStringProperty(metadata, 'units');
if (unit) {
// Angular data (radians)
if (ANGULAR_UNITS.includes(unit)) {
return {
dataType: 'angular',
unit,
description: getStringProperty(metadata, 'description'),
rawMetadata: metadata,
};
}
// Numeric data
if (NUMERIC_UNITS.includes(unit)) {
return {
dataType: 'numeric',
unit,
description: getStringProperty(metadata, 'description'),
rawMetadata: metadata,
};
}
app?.debug(`Type detection: unknown unit "${unit}" for ${path}, defaulting to numeric`);
return {
dataType: 'numeric',
unit,
description: getStringProperty(metadata, 'description'),
rawMetadata: metadata,
};
}
// Check for enum values in metadata
const enumValues = getArrayProperty(metadata, 'enum') ||
getArrayProperty(metadata, 'values');
if (enumValues && enumValues.length > 0) {
return {
dataType: 'enum',
enumValues,
description: getStringProperty(metadata, 'description'),
rawMetadata: metadata,
};
}
// Try to infer from description or type field
const type = getStringProperty(metadata, 'type');
const description = getStringProperty(metadata, 'description');
if (type === 'boolean' || description?.toLowerCase().includes('boolean')) {
return {
dataType: 'boolean',
description,
rawMetadata: metadata,
};
}
// Try to sample the actual value to detect boolean
if (app) {
try {
const sampleValue = app.getSelfPath(path);
// Handle both raw values and SignalK value objects
const actualValue = sampleValue &&
typeof sampleValue === 'object' &&
'value' in sampleValue
? sampleValue.value
: sampleValue;
if (typeof actualValue === 'boolean') {
return {
dataType: 'boolean',
description: description || 'Boolean value (detected from sample)',
rawMetadata: metadata,
};
}
}
catch (sampleError) {
// Ignore sampling errors, continue with default detection
}
}
// Default to string if no clear type detected
app?.debug(`Type detection: no clear type for ${path}, defaulting to string`);
return {
dataType: 'string',
description,
rawMetadata: metadata,
};
}
catch (error) {
app?.error(`Type detection error for ${path}: ${error.message}`);
return { dataType: 'unknown' };
}
}
/**
* Sample the actual data to help determine type when metadata is unavailable
* This is a fallback mechanism
*/
async function detectPathTypeFromSample(path, sampleValue, app) {
// Position object detection
if (typeof sampleValue === 'object' &&
sampleValue !== null &&
'latitude' in sampleValue &&
'longitude' in sampleValue) {
return {
dataType: 'position',
description: 'Geographic position (detected from sample)',
};
}
// Boolean detection
if (typeof sampleValue === 'boolean') {
return {
dataType: 'boolean',
description: 'Boolean value (detected from sample)',
};
}
// Numeric detection
if (typeof sampleValue === 'number') {
// Check if path suggests angular data
const angularPatterns = [
/heading/i,
/course/i,
/bearing/i,
/angle/i,
/direction/i,
];
if (angularPatterns.some(pattern => pattern.test(path))) {
return {
dataType: 'angular',
description: 'Angular value (detected from path pattern)',
};
}
return {
dataType: 'numeric',
description: 'Numeric value (detected from sample)',
};
}
// String detection
if (typeof sampleValue === 'string') {
return {
dataType: 'string',
description: 'String value (detected from sample)',
};
}
app?.debug(`Type detection: unable to detect type from sample for ${path}`);
return { dataType: 'unknown' };
}
//# sourceMappingURL=type-detector.js.map