Asesoría Agenda una llamada de descubrimiento técnico hoy mismo »

· Eduardo Vieira · Industrial Programming  · 6 min de lectura

Clean Code para PLC: Patrones Prácticos IEC 61131-3

Guía práctica para software PLC legible: ciclo de scan, máquinas de estados, bloques, temporizadores, diagnóstico, pruebas y releases controlados.

Guía práctica para software PLC legible: ciclo de scan, máquinas de estados, bloques, temporizadores, diagnóstico, pruebas y releases controlados.

El código PLC limpio facilita inspeccionar, modificar y recuperar el control. Por sí solo no vuelve una aplicación conforme, certificada, segura ni portable. IEC 61131-3 especifica sintaxis y semántica; cada equipo debe definir arquitectura, revisiones, pruebas, despliegue y ciclo de vida de seguridad.

Esta guía usa ejemplos en Structured Text (ST), aunque las decisiones son aplicables a los lenguajes IEC 61131-3. La planificación de tareas, resolución de temporizadores, inicialización, retención y comportamiento del cambio online dependen del controlador, runtime, configuración y versión del fabricante. Verificalos en la documentación y sobre hardware representativo.

Empezá por la semántica del ciclo de scan

Una tarea PLC común lee entradas, ejecuta lógica y actualiza salidas repetidamente. El código observa una muestra durante una invocación, no cada transición física entre scans. Por eso el período, prioridad, jitter, actualización de E/S y tareas de comunicación deben formar parte del diseño.

Hacé determinista cada scan: derivá comandos de entradas y estado, invocá cada bloque con estado una vez y asigná salidas en todos los caminos relevantes. Si dos unidades escriben la misma salida, una ejecución posterior puede ocultar la decisión anterior.

Documentá supuestos como “invocado cada 20 ms” junto a la configuración, no como una constante mágica. Cuando el tiempo importe, medí el peor tiempo de ejecución y el jitter con carga realista; el código fuente no demuestra ninguno de los dos.

Expresá las secuencias como máquinas de estados

Usá una enumeración con nombres y CASE cuando un equipo tenga fases mutuamente excluyentes. Definí condiciones de entrada y salida, timeout, política de reset y un camino para estados inválidos. Asigná valores seguros a los comandos antes del CASE y habilitá solo lo necesario para el estado activo.

TYPE E_ConveyorState : (Idle, Starting, Running, Fault); END_TYPE

xMotorCommand := FALSE;

CASE eState OF
    E_ConveyorState.Idle:
        IF rtStart.Q THEN eState := E_ConveyorState.Starting; END_IF
    E_ConveyorState.Starting:
        tonStart(IN := TRUE, PT := T#3s);
        IF xAtSpeed THEN
            eState := E_ConveyorState.Running;
        ELSIF tonStart.Q THEN
            eState := E_ConveyorState.Fault;
        END_IF
    E_ConveyorState.Running:
        xMotorCommand := TRUE;
        IF NOT xPermit THEN eState := E_ConveyorState.Fault; END_IF
    E_ConveyorState.Fault:
        IF xReset AND xPermit THEN eState := E_ConveyorState.Idle; END_IF
ELSE
    eState := E_ConveyorState.Fault;
END_CASE

Es un ejemplo de arquitectura, no lógica de seguridad lista para producción. Una máquina real requiere parada definida, realimentación de actuadores, enclavamientos, fallos retenidos y análisis de peligros.

Elegí conscientemente los límites de funciones y bloques

Usá una función para un cálculo cuyo resultado dependa solo de sus argumentos y no necesite estado entre ejecuciones. Usá un bloque de función cuando el comportamiento posea estado entre scans, como filtrado, secuencias o estado de equipo. Mantené el mapeo de hardware y la orquestación fuera de los bloques reutilizables para probar la lógica con valores ordinarios.

Una instancia de FB contiene estado. No compartas un temporizador, trigger o equipo entre dispositivos independientes. Diseñá interfaces direccionales: comandos y observaciones entran; estado, diagnóstico y salidas solicitadas salen. Evitá globales cuando una interfaz explícita comunique propiedad.

Hacé visibles los flancos y el tiempo

Un pulsador sostenido no es un comando de un solo ciclo. Usá un detector como R_TRIG cuando una acción deba ocurrir una vez en una transición de falso a verdadero, e invocá esa instancia en cada scan previsto. La biblioteca Standard de CODESYS documenta R_TRIG como un FB con estado cuya salida Q informa el flanco ascendente.

Los temporizadores también retienen estado. El TON documentado por CODESYS comienza con el flanco ascendente de IN, se reinicia cuando IN baja y activa Q después de PT mientras IN siga verdadero. No supongas igual resolución, overflow, persistencia o planificación en todos los runtimes. Usá nombres con propósito, como tonStartTimeout, y unidades explícitas.

Dejá que tipos y nombres expresen intención

Preferí enumeraciones para modos, estructuras para valores relacionados y unidades en nombres cuando el sistema de tipos no pueda expresarlas: rPressure_bar, udiPulseCount, tStartTimeout. Diferenciá E/S cruda, valores de ingeniería, comandos, realimentación y estado. Una convención del equipo importa más que prefijos decorativos; aplicá un vocabulario documentado de forma consistente.

Convertí en límites claros y comprobá rango antes de reducir tipos. Definí inicio y retención después de una descarga, pérdida de energía o cambio de estructura. Un dato retenido obsoleto nunca debe convertirse silenciosamente en un comando.

Diseñá el diagnóstico como parte del comportamiento

Un bit xFault casi nunca alcanza. Exponé estado, fallo activo, contexto inicial, tiempo relevante, diferencia entre comando y realimentación, y elegibilidad de reset. La marca temporal puede pertenecer a supervisión, pero la identidad del evento debe originarse cerca de la decisión.

Separá condición presente, historial y reconocimiento. El diagnóstico debe explicar una transición fallida sin exigir un trace online.

Creá puntos de prueba alrededor del scan

Separá el mapeo de E/S de las decisiones, inyectá observaciones y tiempo transcurrido cuando la plataforma lo permita, y capturá salidas solicitadas en vez de escribir directamente al hardware. Probá scan por scan: arranque normal, arranque sostenido, límite del timeout, pérdida de permiso, reset denegado, estado inválido, reinicio y migración de datos retenidos.

El verificador del repositorio contiene un fixture determinista con la biblioteca estándar de Node.js que refleja el scan, flanco ascendente, retardo a la conexión y transiciones del ejemplo. Es solo una simulación: comprueba la lógica del artículo y su capacidad de prueba, no timing PLC, equivalencia entre fabricantes, hardware, conformidad IEC, certificación ni validación de seguridad.

Tratá el cambio online como un release controlado

Antes de desplegar, archivá fuentes, bibliotecas, compilador, runtime, configuración, checksums y un paquete de restauración probado. Revisá layout, inicialización, retención, instancias, E/S y si se requiere descarga completa o reinicio.

“Cambio online” no significa cero downtime ni cero riesgo. El fabricante y el tipo de cambio determinan qué puede transferirse y qué efectos habrá. Definí aprobación, ventana productiva, backup, pruebas de aceptación, disparador de rollback, responsable y procedimiento de reversión comprobado. Probá el camino exacto en equipo representativo antes de producción.

Separá control estándar y aseguramiento de seguridad

El código legible ayuda a revisar, pero no reemplaza evaluación de riesgos, requisitos, controlador apropiado, funciones validadas, pruebas ni control de cambios. No uses un enclavamiento estándar como única reducción de riesgo para movimiento peligroso. La seguridad corresponde a personal competente siguiendo normas, manuales y el ciclo de vida del sitio.

Fuentes primarias

Consultadas el 2026-07-24:

Volver al blog

Related Posts

View All Posts »