Proyecto base para la plataforma de monitoreo de baterias Horus.
- Comunicacion HTTP entre edge y backend.
- Almacenamiento: InfluxDB para telemetria y PostgreSQL para snapshots de estado y alarmas.
- Orquestacion: Docker Compose.
- Visualizacion: Grafana con dashboards preconfigurados.
sensors/: utilidades de sensores simulados.edge/: simulador externo que genera telemetria y status compatibles con el backend.backend/: app FastAPI que recibe payloads y los persiste en InfluxDB y PostgreSQL.frontend/grafana/: dashboards y provisioning para Grafana.docker-compose.yml: servicios para desarrollo local.
- Docker y Docker Compose
docker-compose up --buildEse comando levanta:
backendinfluxdbpostgresgrafana
El Edge Simulator ya no forma parte de docker-compose.yml. Si queres generar datos simulados, ejecutalo por fuera del stack apuntando al backend.
Accesos:
- Backend + Horus Control Center:
http://localhost:8000 - Grafana:
http://localhost:3000(admin/admin) - InfluxDB:
http://localhost:8086(admin/adminpassword) - PostgreSQL:
localhost:5432(horus_user/horus_pass)
El simulador se corre de forma externa al stack principal. Desde el repo, por ejemplo:
python -m edge.collectorSi el backend corre con docker-compose, usa localhost en las URLs del simulador para publicar contra el puerto expuesto 8000.
Variables de entorno relevantes:
BACKEND_BASE_URL=http://localhost:8000
BACKEND_TELEMETRY_URL=http://localhost:8000/ingest/telemetry
BACKEND_STATUS_URL=http://localhost:8000/ingest/status
INTERVAL=5
SENSOR_VERSION=sim-edge-2.1.0
SIMULATION_PROFILE=steady_day
SIMULATION_TARGETS=allPerfiles disponibles en el simulador:
steady_day: operacion estable con oscilaciones suaves.cloudy_swings: nubes intermitentes con caidas y recuperaciones.storm_front: tension baja y alarmas mas frecuentes.critical_load: corriente elevada y picos de potencia.night_watch: operacion nocturna con baja corriente.
Para lanzar un perfil puntual:
$env:SIMULATION_PROFILE="storm_front"; python -m edge.collectorPara limitar la simulacion a algunas localidades:
$env:SIMULATION_TARGETS="cordoba_capital,rio_cuarto"; python -m edge.collectorLocalidades simuladas actualmente:
cordoba_capitalvilla_carlos_pazrio_cuartovilla_mariasan_franciscoalta_graciajesus_mariabell_ville
Recibe telemetria instantanea del dispositivo y la persiste en InfluxDB.
Payload ejemplo:
{
"device_id": "cordoba_capital",
"timestamp": 1742893200,
"measurements": {
"ch1_current": 12.34,
"ch2_current": 10.91,
"ch3_voltage": 48.21,
"ch4_voltage": 47.88
}
}Consulta Flux de ejemplo para validar escrituras sin hardcodear una localidad:
from(bucket: "horus-bucket")
|> range(start: -1h)
|> filter(fn: (r) => r._measurement == "device_telemetry")
|> group(columns: ["device_id"])
|> last()
Response:
{
"status": "ok",
"device_id": "cordoba_capital",
"timestamp": 1742893200
}Reglas:
device_id: string obligatoriotimestamp: epoch UTC en segundos, obligatoriomeasurements: objeto obligatorio y no vacio- todos los valores de
measurementsdeben ser numericos
Recibe snapshots de estado del dispositivo y los persiste en PostgreSQL.
Payload ejemplo:
{
"device_id": "cordoba_capital",
"timestamp": 1742893200,
"fw_version": "sim-edge-2.1.0-steady_day",
"ip": "127.0.0.1",
"uptime_ms": 1234567,
"alarms": {
"ch1_max": false,
"ch1_min": false,
"ch2_max": false,
"ch2_min": false,
"ch3_max": false,
"ch3_min": false,
"ch4_max": false,
"ch4_min": false
}
}Response:
{
"status": "ok",
"device_id": "cordoba_capital",
"stored": true
}Reglas:
device_id: string obligatoriotimestamp: epoch UTC en segundos, obligatoriofw_version: string obligatorioip: IP valida obligatoriauptime_ms: integer obligatorioalarms: objeto obligatorio y no vacio- todos los valores de
alarmsdeben ser boolean
Verifica la salud de InfluxDB y PostgreSQL.
La portada del backend ya no muestra controles de simulacion. Solo embebe dos dashboards de Grafana:
- Dashboard operacional
- Dashboard de alarmas
Los 4 gauges principales muestran una metrica general por canal:
Corriente continua promedio: promedio del ultimo valor reportado por todas las localidades de Cordoba parach1_currentCorriente alterna promedio: promedio del ultimo valor reportado por todas las localidades de Cordoba parach2_currentVoltaje continuo promedio: promedio del ultimo valor reportado por todas las localidades de Cordoba parach3_voltageVoltaje alterno promedio: promedio del ultimo valor reportado por todas las localidades de Cordoba parach4_voltage
Semantica del payload:
ch1_current: corriente continuach2_current: corriente alternach3_voltage: voltaje continuoch4_voltage: voltaje alterno
Importante:
- no es un promedio temporal de los ultimos 15 minutos
- cada gauge calcula el promedio a partir del ultimo registro disponible de cada localidad incluida
- cada gauge principal tiene enlace al dashboard
Horus Detalle Por Localidad, donde se ve esa metrica abierta por localidad - el dashboard de detalle es una vista compacta 2x2 con las cuatro metricas abiertas por localidad
- el dashboard operacional separa los historicos en 4 paneles: corriente continua, corriente alterna, voltaje continuo y voltaje alterno
- la potencia tambien se separa en 2 paneles:
potencia_dc = ch1_current * ch3_voltageypotencia_ac = ch2_current * ch4_voltage - el panel de
Ultimos snapshots de estadofue removido del dashboard operacional para dejarlo enfocado en metricas energeticas
Rangos visuales configurados en las agujas:
- corriente continua: verde entre
1 Ay5 A - corriente alterna: verde entre
0.5 Ay4 A - voltaje continuo: verde entre
47 Vy50 V - voltaje alterno: verde entre
200 Vy230 V - cerca del limite: naranja
- fuera de rango: rojo
El dashboard Horus Alarmas consulta PostgreSQL y muestra:
- cantidad de localidades con alarmas activas en su ultimo estado
- cantidad de eventos de alarma en las ultimas 24 horas
- frecuencia por tipo de alarma
- ultimo estado activo por localidad
- historico persistido de eventos de alarma y descarga en CSV
- InfluxDB: telemetria (
device_telemetry) - PostgreSQL: snapshots de estado (
device_status)
Measurement: device_telemetry
- Tags:
device_id - Fields:
ch1_current,ch2_current,ch3_voltage,ch4_voltage - Timestamp: timestamp recibido en UTC con precision de segundos
CREATE TABLE device_status (
id BIGSERIAL PRIMARY KEY,
device_id TEXT NOT NULL,
ts TIMESTAMPTZ NOT NULL,
fw_version TEXT NOT NULL,
ip INET,
uptime_ms BIGINT NOT NULL,
alarms JSONB NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX idx_device_status_device_ts
ON device_status (device_id, ts DESC);Tabla persistida para historico descargable de alarmas activas.
Columnas principales:
device_idtsfw_versionipuptime_msalarm_keyalarm_valuecreated_at
- Las credenciales estan en
docker-compose.yml. Para produccion, reemplazarlas por secretos. - El backend ya no expone endpoints de simulacion ni genera escenarios por si mismo.
- El simulador
edge.collectorrespeta el payload actual de InfluxDB y PostgreSQL. - Los datos persisten mientras no se eliminen los volumenes (
docker compose down -vsi los borra).
Prerequisito:
pip install pillowEjecutar:
python scripts/generate_architecture.pyEsto genera docs/architecture.jpg.
El dashboard no muestra datos
- Verificar que el backend esta recibiendo telemetria/status:
docker logs horus-backend - Verificar que el proceso
python -m edge.collectoreste corriendo si queres datos simulados - Verificar conectividad de Grafana a InfluxDB y PostgreSQL
- Reiniciar Grafana:
docker-compose restart grafana
Quiero borrar todos los datos
- Ejecutar
docker compose down -v - Volver a levantar con
docker compose up --build
Error de conexión a PostgreSQL:
- Verificar que postgres está corriendo:
docker-compose ps - Verificar credenciales en
docker-compose.yml - Verificar que la tabla
eventsexiste:docker exec horus-postgres psql -U horus_user -d horus -c "\dt"
opcion1 (SNMP): Hay un reporsitorio maestro que posee el desarrollo de las cajas con los sensores. Estos endpoints tienen un cliente de SNMP el cual es consultado por un servidor SNMP remoto para obtener datos instantaneos. Se necesita un desarrollo que busca obtener datapoints similares a la simulación, pero que consulte mediante un servidor SNMP a los clientes SNMP que representan cada caja de sensores.
opcion2 (HTTP): Armar en un nuevo directorio codigo similar al simulador, que se usa acualmente en /edge, pero para deployar en placas esp32 y que le posteen las apis actuales del backend.