@sailboat-computer/data-storage
Version:
Shared data storage library for sailboat computer v3
226 lines • 8.66 kB
JavaScript
;
/**
* Maintenance-based downsampling strategy implementation
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.createMaintenanceBasedDownsampler = exports.MaintenanceBasedDownsampler = void 0;
/**
* Maintenance-based downsampler implementation
*/
class MaintenanceBasedDownsampler {
constructor() {
this.type = 'maintenance-based';
}
/**
* Downsample data using maintenance-based strategy
*
* @param data - Data to downsample
* @param strategy - Maintenance-based strategy
* @returns Downsampled data
*/
async downsample(data, strategy) {
if (data.length === 0) {
return [];
}
try {
// Sort data by timestamp
const sortedData = [...data].sort((a, b) => {
const aTime = new Date(a.metadata.timestamp).getTime();
const bTime = new Date(b.metadata.timestamp).getTime();
return aTime - bTime;
});
// Find maintenance events
const maintenanceEvents = this.findMaintenanceEvents(sortedData, strategy.maintenanceEventType);
if (maintenanceEvents.length === 0) {
// No maintenance events found, apply normal downsampling
return this.applyNormalDownsampling(sortedData, strategy.normalSamplingInterval);
}
// Process data around maintenance events
const result = [];
let lastProcessedIndex = -1;
for (const eventIndex of maintenanceEvents) {
// Process data before the event (if not already processed)
if (eventIndex > lastProcessedIndex + 1) {
const beforeData = sortedData.slice(lastProcessedIndex + 1, eventIndex - strategy.samplesBeforeEvent);
const downsampledBeforeData = this.applyNormalDownsampling(beforeData, strategy.normalSamplingInterval);
result.push(...downsampledBeforeData);
}
// Add detailed data around the event
const startIndex = Math.max(0, eventIndex - strategy.samplesBeforeEvent);
const endIndex = Math.min(sortedData.length - 1, eventIndex + strategy.samplesAfterEvent);
const detailedData = sortedData.slice(startIndex, endIndex + 1);
// Apply detailed downsampling if there are too many points
if (detailedData.length > strategy.samplesBeforeEvent + strategy.samplesAfterEvent + 1) {
const downsampledDetailedData = this.applyDetailedDownsampling(detailedData, strategy.detailedSamplingInterval);
result.push(...downsampledDetailedData);
}
else {
result.push(...detailedData);
}
lastProcessedIndex = endIndex;
}
// Process remaining data after the last event
if (lastProcessedIndex < sortedData.length - 1) {
const afterData = sortedData.slice(lastProcessedIndex + 1);
const downsampledAfterData = this.applyNormalDownsampling(afterData, strategy.normalSamplingInterval);
result.push(...downsampledAfterData);
}
// Add downsampling metadata
for (const item of result) {
item.metadata.tags = {
...item.metadata.tags,
downsampled: 'true',
downsampledFrom: data.length.toString(),
downsamplingStrategy: 'maintenance-based'
};
}
return result;
}
catch (error) {
console.error('Failed to apply maintenance-based downsampling:', error);
// Return original data on error
return data;
}
}
/**
* Find maintenance events in data
*
* @param data - Data to find maintenance events in
* @param eventType - Maintenance event type
* @returns Indices of maintenance events
*/
findMaintenanceEvents(data, eventType) {
const events = [];
for (let i = 0; i < data.length; i++) {
const item = data[i];
// Check if item is a maintenance event
if (this.isMaintenanceEvent(item, eventType)) {
events.push(i);
}
}
return events;
}
/**
* Check if data is a maintenance event
*
* @param data - Data to check
* @param eventType - Maintenance event type
* @returns Whether data is a maintenance event
*/
isMaintenanceEvent(data, eventType) {
// Check if data has a maintenance event tag
if (data.metadata.tags && data.metadata.tags.eventType === eventType) {
return true;
}
// Check if data has a maintenance event type
if (data.data.eventType === eventType) {
return true;
}
// Check if data has a maintenance event flag
if (data.data.maintenanceEvent === true && data.data.type === eventType) {
return true;
}
// Check if data has a maintenance event in a nested object
if (data.data.event && typeof data.data.event === 'object') {
const event = data.data.event;
if (event.type === eventType || event.eventType === eventType) {
return true;
}
}
return false;
}
/**
* Apply normal downsampling to data
*
* @param data - Data to downsample
* @param interval - Sampling interval
* @returns Downsampled data
*/
applyNormalDownsampling(data, interval) {
if (data.length === 0) {
return [];
}
// Parse interval
const intervalMs = this.parseInterval(interval);
if (intervalMs <= 0) {
return data;
}
// Always include first and last points
const result = [];
if (data.length > 0) {
result.push(data[0]);
}
// Apply interval-based sampling
if (data.length > 2) {
const firstTime = new Date(data[0].metadata.timestamp).getTime();
const lastTime = new Date(data[data.length - 1].metadata.timestamp).getTime();
// Calculate number of intervals
const duration = lastTime - firstTime;
const intervals = Math.floor(duration / intervalMs);
if (intervals > 0) {
// Calculate step size
const step = data.length / (intervals + 1);
// Add samples at interval boundaries
for (let i = 1; i <= intervals; i++) {
const index = Math.round(i * step);
if (index > 0 && index < data.length - 1) {
result.push(data[index]);
}
}
}
}
if (data.length > 1) {
result.push(data[data.length - 1]);
}
return result;
}
/**
* Apply detailed downsampling to data
*
* @param data - Data to downsample
* @param interval - Sampling interval
* @returns Downsampled data
*/
applyDetailedDownsampling(data, interval) {
// Use a smaller interval for detailed downsampling
return this.applyNormalDownsampling(data, interval);
}
/**
* Parse interval string to milliseconds
*
* @param interval - Interval string (e.g., '1m', '5m', '1h')
* @returns Interval in milliseconds
*/
parseInterval(interval) {
const match = interval.match(/^(\d+)([smhd])$/);
if (!match) {
console.warn(`Invalid interval format: ${interval}`);
return 0;
}
const value = parseInt(match[1]);
const unit = match[2];
switch (unit) {
case 's':
return value * 1000;
case 'm':
return value * 60 * 1000;
case 'h':
return value * 60 * 60 * 1000;
case 'd':
return value * 24 * 60 * 60 * 1000;
default:
return 0;
}
}
}
exports.MaintenanceBasedDownsampler = MaintenanceBasedDownsampler;
/**
* Create a new maintenance-based downsampler
*
* @returns Maintenance-based downsampler
*/
function createMaintenanceBasedDownsampler() {
return new MaintenanceBasedDownsampler();
}
exports.createMaintenanceBasedDownsampler = createMaintenanceBasedDownsampler;
//# sourceMappingURL=maintenance-based.js.map