You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

189 lines
6.3 KiB
JavaScript

// api/server.js
const express = require('express');
const cors = require('cors');
const app = express();
const PORT = 3000;
app.use(cors());
app.use(express.json());
// Función auxiliar para simular latencia
const delay = (ms) => new Promise(resolve => setTimeout(resolve, ms));
// Endpoint principal para obtener datos de los sensores (MOCK)
app.get('/api/sensors/:type', async (req, res) => {
const sensorType = req.params.type;
console.log(`[GET] Petición recibida para sensor: ${sensorType}`);
// Simulación de latencia I2C y procesamiento (400ms)
await delay(400);
const data = [];
const now = new Date();
// Generamos 60 registros simulados
for (let i = 60; i >= 0; i--) {
const timestamp = new Date(now.getTime() - i * 1000).toISOString();
// Simulación de valores con ruido térmico/químico
const mockValues = {
rtd: (25.0 + (Math.random() * 0.1 - 0.05)).toFixed(2),
ph: (7.2 + (Math.random() * 0.04 - 0.02)).toFixed(2),
do: (8.5 + (Math.random() * 0.1 - 0.05)).toFixed(2),
ec: (1050 + (Math.random() * 10 - 5)).toFixed(0)
};
if (sensorType === 'all') {
data.push({
Timestamp: timestamp,
Temperatura_C: mockValues.rtd,
pH: mockValues.ph,
DO_mgL: mockValues.do,
EC_uS: mockValues.ec
});
} else if (mockValues[sensorType]) {
data.push({
Timestamp: timestamp,
Sensor: sensorType.toUpperCase(),
Valor: mockValues[sensorType]
});
} else {
return res.status(400).json({ error: "Sensor no válido" });
}
}
res.json(data);
});
// Endpoint DEFINITIVO para comandos Atlas Scientific EZO
app.post('/api/sensors/:type/command', async (req, res) => {
const sensorType = req.params.type.toUpperCase();
const rawCommand = req.body.command ? req.body.command.toLowerCase().trim() : '';
console.log(`[COMANDO] RX: '${rawCommand}' para ${sensorType}`);
// Dividimos el comando por comas para analizar base y parámetros (ej. "cal,mid,7.00" -> ["cal", "mid", "7.00"])
const parts = rawCommand.split(',');
const baseCmd = parts[0];
let ezoResponse = "";
// 1. COMANDOS DE ESTADO Y CONFIGURACIÓN GLOBAL (Comunes a todos los EZO)
if (baseCmd === 'i') {
await delay(300);
ezoResponse = `?I,${sensorType},2.12`;
}
else if (baseCmd === 'status') {
await delay(300);
ezoResponse = `?STATUS,P,5.03`; // P = Power On, 5.03 = Voltaje
}
else if (baseCmd === 'sleep') {
ezoResponse = `[SLEEP MODE ACTIVADO]`;
}
else if (baseCmd === 'factory') {
await delay(800);
ezoResponse = `*OK`;
}
else if (baseCmd === 'find') {
await delay(300);
ezoResponse = `*OK`; // Hace parpadear el LED en blanco
}
else if (baseCmd === 'led') {
await delay(300);
if (parts[1] === '?') ezoResponse = `?LED,1`;
else ezoResponse = `*OK`;
}
else if (baseCmd === 'plock') {
// Protocol Lock (Evita cambios accidentales de I2C a UART)
await delay(300);
if (parts[1] === '?') ezoResponse = `?PLOCK,1`;
else ezoResponse = `*OK`;
}
else if (baseCmd === 'i2c') {
// Cambio de dirección I2C (ej. i2c,100)
await delay(300);
ezoResponse = `*OK`;
}
// 2. COMANDO DE LECTURA PRINCIPAL
else if (baseCmd === 'r') {
await delay(900); // Latencia real de procesamiento químico/eléctrico
if (sensorType === 'RTD') ezoResponse = (25.0 + Math.random() * 0.1).toFixed(3);
if (sensorType === 'PH') ezoResponse = (7.2 + Math.random() * 0.05).toFixed(2);
if (sensorType === 'DO') ezoResponse = (8.5 + Math.random() * 0.1).toFixed(2);
if (sensorType === 'EC') ezoResponse = (1050 + Math.random() * 5).toFixed(0);
}
// 3. SISTEMA DE CALIBRACIÓN UNIVERSAL
else if (baseCmd === 'cal') {
await delay(600);
if (parts[1] === 'clear') {
ezoResponse = `*OK`;
} else if (parts[1] === '?') {
ezoResponse = `?CAL,1`; // Devuelve puntos calibrados
} else {
// Acepta cal,mid,7.00 (pH) | cal,atm (DO) | cal,dry (EC) | cal,t (RTD)
ezoResponse = `*OK`;
}
}
// 4. COMPENSACIONES AMBIENTALES (Temperatura, Salinidad, Presión)
else if (baseCmd === 't' || baseCmd === 's' || baseCmd === 'p') {
await delay(300);
if (parts[1] === '?') {
const defaultVals = { 't': '25.0', 's': '0.00', 'p': '101.3' };
ezoResponse = `?${baseCmd.toUpperCase()},${defaultVals[baseCmd]}`;
} else {
ezoResponse = `*OK`;
}
}
// 5. COMANDOS ESPECÍFICOS POR SENSOR
else {
await delay(300);
// --- RTD (Temperatura) ---
if (sensorType === 'RTD' && baseCmd === 's') {
// Escala (c=Celsius, k=Kelvin, f=Fahrenheit)
ezoResponse = parts[1] === '?' ? `?S,C` : `*OK`;
}
// --- PH ---
else if (sensorType === 'PH' && baseCmd === 'slope') {
// Estado de salud del vidrio de la sonda
ezoResponse = parts[1] === '?' ? `?Slope,99.7,100.3,-0.89` : `*ER`;
}
// --- EC (Conductividad) ---
else if (sensorType === 'EC') {
if (baseCmd === 'k') {
// Constante de la sonda (0.1, 1.0, 10)
ezoResponse = parts[1] === '?' ? `?K,1.0` : `*OK`;
} else if (baseCmd === 'tc') {
// Coeficiente de temperatura
ezoResponse = parts[1] === '?' ? `?TC,1.90` : `*OK`;
} else if (baseCmd === 'o') {
// Habilitar/Deshabilitar parámetros de salida (TDS, Salinidad, Gravedad Específica)
ezoResponse = parts[1] === '?' ? `?O,EC,TDS,S,SG` : `*OK`;
} else {
ezoResponse = `*ER`;
}
}
// Si ninguna regla coincide, el comando no existe en el datasheet
else {
ezoResponse = `*ER`;
}
}
res.json({
success: true,
sensor: sensorType,
command: rawCommand,
response: ezoResponse
});
});
app.listen(PORT, () => {
console.log(`[MOCK SERVER] Backend Node.js corriendo en http://localhost:${PORT}`);
});