Skip to content

QBSCarlosClemente/opcua-nodejs-industrial

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 
 
 

Repository files navigation

🏭 OPC UA Industrial Communication with Node.js

Ejemplo práctico de comunicación OPC UA con Node.js para integración IT/OT. Lectura de PLCs, suscripción a eventos en tiempo real, y dashboard web con WebSockets para monitoreo de piso de producción.

Node.js TypeScript OPC UA


¿Qué es OPC UA?

OPC UA (Unified Architecture) es el estándar de comunicación industrial para conectar sistemas IT (ERP, MES, dashboards) con sistemas OT (PLCs, SCADA, sensores).

graph LR
    subgraph "🖥️ MUNDO IT"
        DASH["Dashboard<br/>React / Web"]
        MES["MES / ERP"]
    end

    subgraph "🔧 BRIDGE"
        NODE["Node.js<br/>Bridge"]
    end

    subgraph "🏭 MUNDO OT"
        OPC["OPC UA Server<br/>(Kepware)"]
        PLC1["PLC<br/>Allen-Bradley"]
        PLC2["PLC<br/>Siemens"]
        SENSORS["Sensores<br/>HMI"]
        SCADA["SCADA"]
    end

    DASH <-->|WebSocket| NODE
    MES <-->|REST API| NODE
    NODE <-->|OPC UA| OPC
    OPC --- PLC1
    OPC --- PLC2
    OPC --- SENSORS
    OPC --- SCADA

    style DASH fill:#1e293b,stroke:#38bdf8,color:#f1f5f9
    style MES fill:#1e293b,stroke:#38bdf8,color:#f1f5f9
    style NODE fill:#334155,stroke:#22c55e,color:#f1f5f9
    style OPC fill:#44403c,stroke:#f97316,color:#f1f5f9
    style PLC1 fill:#44403c,stroke:#f97316,color:#f1f5f9
    style PLC2 fill:#44403c,stroke:#f97316,color:#f1f5f9
    style SENSORS fill:#44403c,stroke:#f97316,color:#f1f5f9
    style SCADA fill:#44403c,stroke:#f97316,color:#f1f5f9
Loading

Flujo de Datos en Producción

graph LR
    PLC["PLC<br/>Allen-Bradley /<br/>Siemens"] -->|"EtherNet/IP<br/>Profinet"| KEP["Kepware<br/>OPC Server"]
    KEP -->|"OPC UA<br/>tcp/49320"| BRIDGE["Node.js<br/>Bridge"]
    BRIDGE -->|"WebSocket<br/>REST API"| FRONT["Dashboard<br/>MES / ERP"]

    style PLC fill:#44403c,stroke:#f97316,color:#f1f5f9
    style KEP fill:#334155,stroke:#eab308,color:#f1f5f9
    style BRIDGE fill:#1e3a5f,stroke:#22c55e,color:#f1f5f9
    style FRONT fill:#1e293b,stroke:#38bdf8,color:#f1f5f9
Loading

Estructura del Proyecto

📦 opcua-nodejs-industrial
├── 📂 src/
│   ├── 📂 opcua/
│   │   ├── client.ts           # Conexión OPC UA
│   │   ├── browser.ts          # Explorar nodos del servidor
│   │   ├── reader.ts           # Lectura de variables
│   │   ├── subscriber.ts       # Suscripción a cambios en tiempo real
│   │   └── writer.ts           # Escritura a PLCs (con confirmación)
│   │
│   ├── 📂 bridge/
│   │   ├── opcua-to-ws.ts      # Puente OPC UA → WebSocket
│   │   ├── data-logger.ts      # Registro en PostgreSQL
│   │   └── alarm-handler.ts    # Manejo de alarmas industriales
│   │
│   ├── 📂 api/
│   │   ├── routes.ts           # REST API para datos históricos
│   │   └── health.ts           # Health check de conexión OPC UA
│   │
│   ├── 📂 dashboard/
│   │   └── index.html          # Dashboard de monitoreo (ejemplo)
│   │
│   ├── config.ts               # Configuración de nodos y conexiones
│   └── server.ts               # Entry point
│
├── 📂 simulation/
│   └── opcua-server.ts         # Servidor OPC UA simulado (para desarrollo)
│
├── 📂 docs/
│   ├── kepware-setup.md        # Guía de configuración Kepware
│   ├── plc-addressing.md       # Tabla de direcciones PLC comunes
│   └── architecture.md
│
├── docker-compose.yml
├── .env.example
└── README.md

Quick Start

# Levantar simulador + PostgreSQL + dashboard
docker compose up -d

# El simulador OPC UA estará en: opc.tcp://localhost:4840
# El dashboard estará en: http://localhost:3000

Flujo de Suscripción en Tiempo Real

sequenceDiagram
    participant PLC as 🏭 PLC
    participant OPC as 📡 OPC UA Server
    participant NODE as 🔧 Node.js Bridge
    participant WS as 🌐 WebSocket
    participant DASH as 🖥️ Dashboard

    NODE->>OPC: Suscribirse a Temperature, Pressure
    
    loop Cada cambio de valor
        PLC->>OPC: Temperatura = 85°C
        OPC->>NODE: DataChange notification
        NODE->>NODE: ¿Alarma? (> 95°C = crítico)
        NODE->>WS: Broadcast a clientes
        WS->>DASH: Actualizar gauge
    end

    Note over PLC,DASH: Latencia total: < 500ms
Loading

Ejemplos de Código

Conectar al Servidor OPC UA

import { OpcuaClient } from './opcua/client'

const client = new OpcuaClient({
  endpoint: 'opc.tcp://localhost:4840',
  // Para Kepware en producción:
  // endpoint: 'opc.tcp://kepware-server:49320',
  security: {
    mode: 'SignAndEncrypt',
    policy: 'Basic256Sha256',
  },
  reconnect: {
    enabled: true,
    interval: 5000,
    maxRetries: Infinity,  // Producción 24/7: nunca dejar de intentar
  }
})

await client.connect()

Suscribirse a Cambios

import { OpcuaSubscriber } from './opcua/subscriber'

const subscriber = new OpcuaSubscriber(client, {
  publishingInterval: 500,  // Updates cada 500ms
})

subscriber.on('ns=2;s=Channel1.Device1.Temperature', (data) => {
  console.log(`Temperatura: ${data.value}°C`)
  
  // Enviar a dashboard via WebSocket
  wss.broadcast({ type: 'temperature', value: data.value })
  
  // Guardar en histórico
  dataLogger.log('temperature', data.value, data.timestamp)
})

await subscriber.start()

Leer Variables (Batch)

const values = await reader.readMany([
  'ns=2;s=Channel1.Device1.Temperature',
  'ns=2;s=Channel1.Device1.Pressure',
  'ns=2;s=Channel1.Device1.MotorSpeed',
  'ns=2;s=Channel1.Device1.ConveyorStatus',
])

Escribir a PLC

const result = await writer.write(
  'ns=2;s=Channel1.Device1.MotorSpeed',
  750  // RPM
)
// result.status === 'Good' → Escritura exitosa

Sistema de Alarmas

graph TD
    VALUE["Valor<br/>recibido"] --> CHECK{"Evaluar<br/>umbrales"}
    CHECK -->|"< warnLow o > warnHigh"| WARNING["🟡 Warning"]
    CHECK -->|"< alarmLow o > alarmHigh"| CRITICAL["🔴 Critical"]
    CHECK -->|"En rango normal"| NORMAL["🟢 Normal"]
    
    WARNING --> LOG["📝 Log"]
    CRITICAL --> LOG
    CRITICAL --> EMAIL["📧 Email<br/>a supervisor"]
    CRITICAL --> SOUND["🔊 Alarma<br/>sonora"]
    NORMAL --> DASH["Dashboard"]

    style NORMAL fill:#22c55e,stroke:#16a34a,color:#fff
    style WARNING fill:#eab308,stroke:#ca8a04,color:#000
    style CRITICAL fill:#ef4444,stroke:#dc2626,color:#fff
Loading

Configuración de Nodos

export const nodes: NodeConfig[] = [
  {
    nodeId: 'ns=2;s=Channel1.Device1.Temperature',
    name: 'Temperatura Horno',
    unit: '°C',
    warnLow: 60, warnHigh: 85,
    alarmLow: 50, alarmHigh: 95,
    log: true,
    interval: 1000,
  },
  {
    nodeId: 'ns=2;s=Channel1.Device1.Pressure',
    name: 'Presión Línea',
    unit: 'PSI',
    warnHigh: 120,
    alarmHigh: 150,
    log: true,
    interval: 2000,
  },
]

Manejo de Reconexión 24/7

En manufactura, el sistema debe correr 24/7:

  • Detección de desconexión en < 3 segundos
  • Reintento cada 5 segundos indefinidamente
  • Re-establecimiento automático de todas las suscripciones al reconectar
  • Datos perdidos durante desconexión marcados como quality: 'Uncertain'

Basado en experiencia real

Este ejemplo refleja la arquitectura que uso en proyectos de manufactura como Pulso (MES para semiremolques), GymSys (integración biométrica), y mis años como MES/IT-OT Integration Engineer en Capgemini/Essity y LTI, donde conecté PLCs Allen-Bradley y Siemens con sistemas MES y SCADA vía OPC UA y Kepware.

Autor

Carlos Clemente Olivares Senior Software Engineer | IT/OT Integration Specialist · 25+ años

LinkedIn

About

OPC UA industrial communication with Node.js — PLC data reading, real-time subscriptions, WebSocket dashboard for shop floor

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors