131 lines
No EOL
3.9 KiB
TypeScript
131 lines
No EOL
3.9 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' | 'mock';
|
|
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) {
|
|
// Generate mock data if no real data exists
|
|
return this.generateMockTimeSeriesData(timespan);
|
|
}
|
|
|
|
return sensorData.map(point => ({
|
|
time: point.time,
|
|
timestamp: point.timestamp,
|
|
value: point.value
|
|
}));
|
|
}
|
|
|
|
private generateMockTimeSeriesData(timespan: number): Array<{ time: string; timestamp: number; value: number }> {
|
|
const data = [];
|
|
const now = Date.now();
|
|
const interval = Math.max(1000, timespan / 288); // At least 1 second intervals, up to 288 points
|
|
|
|
for (let i = timespan; i >= 0; i -= interval) {
|
|
const timestamp = now - i;
|
|
const time = new Date(timestamp);
|
|
data.push({
|
|
time: time.toLocaleTimeString("en-US", { hour12: false }),
|
|
timestamp: timestamp,
|
|
value: Math.floor(Math.random() * 4096 + Math.sin(i / 60000) * 500 + 2000), // 0-4095 range
|
|
});
|
|
}
|
|
return data;
|
|
}
|
|
} |