Skip to content

warkomzzz/groov_monitor

Repository files navigation

Manual — Groov EPIC → MongoDB Collector (Linux)

Resumen

Este manual explica cómo instalar desde cero un colector en Linux que:

  • Lee datos de Opto22 groov EPIC por API REST (analogInputs y digitalInputs).
  • Los guarda cada 2 segundos en MongoDB local.
  • Corre automáticamente al iniciar con systemd.
  • Incluye índices, TTL opcional, buenas prácticas y troubleshooting.

Probado el 22-08-2025 (America/Santiago). Asegúrate de usar JSON sin comentarios.


1) Requisitos del sistema

  • Ubuntu/Debian con acceso a red LAN donde están los groov EPIC.
  • Python 3 (con venv y pip) y privilegios para instalar mongodb-org y crear un servicio systemd.

2) Instalar MongoDB Community (local)

Usa el repositorio oficial para obtener una versión reciente.

# Clave GPG y repo
curl -fsSL https://pgp.mongodb.com/server-8.0.asc |   sudo gpg -o /usr/share/keyrings/mongodb-server-8.0.gpg --dearmor

echo "deb [ arch=amd64,arm64 signed-by=/usr/share/keyrings/mongodb-server-8.0.gpg ] https://repo.mongodb.org/apt/ubuntu $(. /etc/os-release && echo $VERSION_CODENAME)/mongodb-org/8.0 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-8.0.list

# Instalar y habilitar
sudo apt update
sudo apt install -y mongodb-org
sudo systemctl enable --now mongod

# Verificar
mongosh --eval 'db.runCommand({ ping: 1 })'

Notas

  • Por defecto MongoDB escucha en 127.0.0.1 (sólo local).
  • Warnings sobre XFS o autenticación son esperables; la sección Seguridad detalla cómo habilitar auth.

3) Preparar proyecto Python

mkdir -p ~/groov_monitor && cd ~/groov_monitor

# Entorno virtual (evita conflictos/PEP 668)
sudo apt install -y python3-venv python3-pip
python3 -m venv .venv
source .venv/bin/activate

3.1 Dependencias

Crea requirements.txt y luego instala:

cat > requirements.txt << 'EOF'
requests
pymongo
EOF

pip install -r requirements.txt

4) Configuración — config.json

Sin comentarios (JSON puro). Ajusta IPs y API keys.

{
  "devices": [
    {
      "ip": "192.168.100.11",
      "api_key": "bie44GXYj2imJKnyUSxrMjafUiPjQBFM",
      "endpoints": ["analogInputs", "digitalInputs"]
    },
    {
      "ip": "192.168.100.12",
      "api_key": "uWr2wd5hnL6JrHa9HHYwDCapQZvPsQKQ",
      "endpoints": ["analogInputs", "digitalInputs"]
    },
    {
      "ip": "192.168.100.14",
      "api_key": "svKofyhEXmfZE9ethLFr423g4fFz2KgL",
      "endpoints": ["analogInputs", "digitalInputs"]
    }
  ],
  "mongo": {
    "uri": "mongodb://127.0.0.1:27017",
    "db": "groov",
    "collection": "measurements",
    "ttl_days": 0
  },
  "sample_period_s": 2.0
}
chmod 600 config.json

5) Script — colectar_groov_mongo.py

Guarda lecturas cada ~2 s. Maneja NaN, booleanos, crea índices y soporta TTL si lo activas.

#!/usr/bin/env python3
import json, time, math, signal, sys
from datetime import datetime, timezone
from typing import Any, Dict, List
import requests
from pymongo import MongoClient, ASCENDING, DESCENDING
from pymongo.errors import PyMongoError
from urllib3.exceptions import InsecureRequestWarning
requests.packages.urllib3.disable_warnings(category=InsecureRequestWarning)

STOP = False
def handle_signal(signum, frame):
    global STOP
    STOP = True
for s in (signal.SIGINT, signal.SIGTERM):
    signal.signal(s, handle_signal)

def load_cfg(path="config.json"):
    with open(path, "r", encoding="utf-8") as fh:
        return json.load(fh)

def ensure_indexes(coll, ttl_days: int):
    coll.create_index(
        [("device_ip", ASCENDING), ("endpoint", ASCENDING), ("name", ASCENDING), ("ts", DESCENDING)],
        name="by_device_ep_name_ts"
    )
    if ttl_days and ttl_days > 0:
        seconds = int(ttl_days * 86400)
        coll.create_index("ts", name="ts_ttl", expireAfterSeconds=seconds)

def as_bool(v: Any):
    if isinstance(v, bool):
        return v, v
    if isinstance(v, str):
        low = v.strip().lower()
        if low in ("true", "1", "on", "si", "sí"):
            return True, v
        if low in ("false", "0", "off", "no"):
            return False, v
    return None, v

def as_float_or_nan(v: Any):
    if isinstance(v, (int, float)):
        if isinstance(v, float) and (math.isnan(v) or v != v):
            return None, v, True
        return float(v), v, False
    if isinstance(v, str):
        if v.strip().lower() == "nan":
            return None, v, True
        try:
            return float(v), v, False
        except Exception:
            return None, v, False
    return None, v, False

def fetch_one(ip: str, api_key: str, endpoint: str, timeout=6):
    url = f"https://{ip}/pac/device/strategy/ios/{endpoint}"
    r = requests.get(url, headers={"apiKey": api_key}, verify=False, timeout=timeout)
    r.raise_for_status()
    data = r.json()
    if not isinstance(data, list):
        raise ValueError(f"Respuesta inesperada {ip} {endpoint}")
    return data

def main():
    cfg = load_cfg()
    devices = cfg["devices"]
    mongo_uri = cfg["mongo"]["uri"]
    db_name = cfg["mongo"]["db"]
    coll_name = cfg["mongo"]["collection"]
    ttl_days = int(cfg["mongo"].get("ttl_days", 0))
    period = float(cfg.get("sample_period_s", 2.0))

    client = MongoClient(mongo_uri, appname="groov-collector")
    db = client[db_name]
    coll = db[coll_name]
    ensure_indexes(coll, ttl_days)

    print(f"[OK] Conectado a MongoDB en {mongo_uri}, db={db_name}, coll={coll_name}")
    while not STOP:
        t0 = time.monotonic()
        ts = datetime.now(timezone.utc)
        bulk_docs: List[Dict[str, Any]] = []
        for dev in devices:
            ip = dev["ip"]
            key = dev["api_key"]
            endpoints = dev.get("endpoints", ["analogInputs", "digitalInputs"])
            for ep in endpoints:
                try:
                    items = fetch_one(ip, key, ep)
                except Exception as e:
                    print(f"[WARN] Falló lectura {ip} {ep}: {e}")
                    continue
                for it in items:
                    name = it.get("name", "")
                    raw_value = it.get("value", None)
                    doc: Dict[str, Any] = {"ts": ts,"device_ip": ip,"endpoint": ep,"name": name}
                    if ep == "digitalInputs":
                        val_bool, raw = as_bool(raw_value)
                        doc.update({"value": val_bool,"raw_value": raw,"type": "digital"})
                    else:
                        val_float, raw, is_nan = as_float_or_nan(raw_value)
                        doc.update({"value": val_float,"raw_value": raw,"is_nan": is_nan,"type": "analog"})
                    bulk_docs.append(doc)
        if bulk_docs:
            try:
                coll.insert_many(bulk_docs, ordered=False)
                print(f"[OK] Insertados {len(bulk_docs)} docs @ {ts.isoformat()}")
            except PyMongoError as e:
                print(f"[ERROR] insert_many: {e}")
        elapsed = time.monotonic() - t0
        sleep_s = period - elapsed
        if sleep_s > 0:
            time.sleep(sleep_s)
    print("[INFO] Saliendo limpio.")

if __name__ == "__main__":
    main()
chmod +x colectar_groov_mongo.py

6) Prueba rápida

# Ejecutar en foreground
source .venv/bin/activate
python3 colectar_groov_mongo.py

En otra terminal:

mongosh
use groov
show collections
db.measurements.countDocuments()
db.measurements.find().sort({ ts: -1 }).limit(5).pretty()
db.measurements.find({ endpoint: "digitalInputs", value: true }).sort({ ts: -1 }).limit(10).pretty()

7) Servicio systemd (inicio automático)

Crea /etc/systemd/system/groov_collector.service:

[Unit]
Description=Groov EPIC -> MongoDB Collector
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
User=marco
WorkingDirectory=/home/marco/groov_monitor
ExecStart=/home/marco/groov_monitor/.venv/bin/python /home/marco/groov_monitor/colectar_groov_mongo.py
Restart=always
RestartSec=5
Environment=PYTHONUNBUFFERED=1

[Install]
WantedBy=multi-user.target

Habilitar y verificar:

sudo systemctl daemon-reload
sudo systemctl enable --now groov_collector
systemctl status groov_collector --no-pager
journalctl -u groov_collector -f

Ejemplo de log correcto

[OK] Conectado a MongoDB en mongodb://127.0.0.1:27017, db=groov, coll=measurements
[OK] Período de muestreo: 2.0 s | TTL días: 0
[OK] Insertados 247 docs @ 2025-08-22T17:55:18.078511+00:00
...

8) Seguridad (opcional) — Habilitar autenticación

Si quieres usuario/clave para MongoDB local:

mongosh
use admin
db.createUser({ user: "admin", pwd: "UnaClaveFuerte", roles: [ { role: "root", db: "admin" } ] })

# Edita /etc/mongod.conf y agrega:
# security:
#   authorization: enabled
sudo systemctl restart mongod

# Crea usuario específico para DB groov
mongosh -u admin -p --authenticationDatabase admin
use groov
db.createUser({ user: "groovuser", pwd: "OtraClaveFuerte", roles: [ { role: "readWrite", db: "groov" } ] })

Actualiza config.json del colector:

"mongo": {
  "uri": "mongodb://groovuser:OtraClaveFuerte@127.0.0.1:27017/?authSource=groov",
  "db": "groov",
  "collection": "measurements",
  "ttl_days": 0
}

Reinicia el servicio:

sudo systemctl restart groov_collector

9) Retención de datos (TTL)

Para retener, por ejemplo, 30 días cambia en config.json:

"ttl_days": 30

El script creará un índice TTL sobre ts y MongoDB borrará automáticamente lo más antiguo.


10) Troubleshooting

  • JSONDecodeError al leer config → Verifica que config.json no tenga comentarios (// o /* ... */).
  • Timeout/SSL → El script usa verify=False por certificados propios; revisa conectividad y firewall.
  • countDocuments() == 0 → Verifica logs con journalctl -u groov_collector -f. Prueba curl -k a cada endpoint con su apiKey.
  • Servicio no arranca → Revisa User= y rutas en el service. Ejecuta manual: source .venv/bin/activate && python3 colectar_groov_mongo.py.
  • Latencia > 2 s → La pausa se ajusta dinámicamente. Si el ciclo supera 2 s, considera paralelizar lecturas o aumentar sample_period_s.

11) Comandos útiles

# Ver estado y logs
systemctl status groov_collector --no-pager
journalctl -u groov_collector -f

# Detener / Iniciar / Reiniciar
sudo systemctl stop groov_collector
sudo systemctl start groov_collector
sudo systemctl restart groov_collector

# Consultas en MongoDB
mongosh --eval 'db.getSiblingDB("groov").measurements.countDocuments()'
mongosh
use groov
db.measurements.find().sort({ ts:-1 }).limit(5).pretty()

12) Limpieza / Desinstalación (opcional)

# Detener servicio
sudo systemctl disable --now groov_collector
sudo rm -f /etc/systemd/system/groov_collector.service
sudo systemctl daemon-reload

# Borrar datos y proyecto (cuidado)
mongosh --eval 'db.getSiblingDB("groov").dropDatabase()'
rm -rf ~/groov_monitor

13) Estructura final esperada

~/groov_monitor/
├─ .venv/
├─ requirements.txt
├─ config.json
└─ colectar_groov_mongo.py
/etc/systemd/system/groov_collector.service

¡Listo! Con esto tendrás un colector robusto que arranca con el sistema y almacena datos del groov EPIC en MongoDB de forma continua.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages