m2-inno-bedpressure/services/SensorDataStorage.ts

116 lines
No EOL
3.4 KiB
TypeScript

import fs from 'fs/promises';
import path from 'path';
const DATA_DIR = path.join(process.cwd(), 'data');
const SENSOR_DATA_FILE = path.join(DATA_DIR, 'sensor-data.json');
export interface SensorDataPoint {
sensorId: string;
value: number; // Changed from pressure to value (0-4095)
timestamp: number;
time: string;
source: 'hardware';
pin?: number;
digitalState?: number;
}
export class SensorDataStorage {
private static instance: SensorDataStorage;
private dataCache: SensorDataPoint[] = [];
private constructor() {
this.initializeStorage();
}
static getInstance(): SensorDataStorage {
if (!SensorDataStorage.instance) {
SensorDataStorage.instance = new SensorDataStorage();
}
return SensorDataStorage.instance;
}
private async initializeStorage() {
try {
// Ensure data directory exists
await fs.mkdir(DATA_DIR, { recursive: true });
// Load existing data
await this.loadData();
} catch (error) {
console.warn('Failed to initialize sensor data storage:', error);
}
}
private async loadData() {
try {
const data = await fs.readFile(SENSOR_DATA_FILE, 'utf8');
this.dataCache = JSON.parse(data);
console.log(`Loaded ${this.dataCache.length} sensor data points from storage`);
} catch {
// File doesn't exist or is corrupted, start with empty cache
this.dataCache = [];
console.log('Starting with empty sensor data cache');
}
}
private async saveData() {
try {
await fs.writeFile(SENSOR_DATA_FILE, JSON.stringify(this.dataCache, null, 2));
} catch (error) {
console.error('Failed to save sensor data:', error);
}
}
async addDataPoint(dataPoint: SensorDataPoint) {
this.dataCache.push(dataPoint);
// Keep only last 7 days of data to prevent unlimited storage growth
const sevenDaysAgo = Date.now() - (7 * 24 * 60 * 60 * 1000);
this.dataCache = this.dataCache.filter(point => point.timestamp > sevenDaysAgo);
// Save to disk every 10 data points to reduce I/O
if (this.dataCache.length % 10 === 0) {
await this.saveData();
}
}
async getDataForSensor(
sensorId: string,
timespan: number = 24 * 60 * 60 * 1000 // Default 24 hours in milliseconds
): Promise<SensorDataPoint[]> {
const cutoffTime = Date.now() - timespan;
return this.dataCache
.filter(point => point.sensorId === sensorId && point.timestamp > cutoffTime)
.sort((a, b) => a.timestamp - b.timestamp);
}
async getAllRecentData(timespan: number = 24 * 60 * 60 * 1000): Promise<SensorDataPoint[]> {
const cutoffTime = Date.now() - timespan;
return this.dataCache
.filter(point => point.timestamp > cutoffTime)
.sort((a, b) => a.timestamp - b.timestamp);
}
async forceSave() {
await this.saveData();
} // Generate time series data for a specific timespan
generateTimeSeriesData(
sensorData: SensorDataPoint[],
timespan: number = 24 * 60 * 60 * 1000
): Array<{ time: string; timestamp: number; value: number }> {
if (sensorData.length === 0) {
// Return empty array if no real data exists
return [];
}
// Filter data by timespan
const cutoffTime = Date.now() - timespan;
const filteredData = sensorData.filter(point => point.timestamp > cutoffTime);
return filteredData.map(point => ({
time: point.time,
timestamp: point.timestamp,
value: point.value
}));
}
}