Perfecto. Buscaré documentación, ejemplos y recursos útiles sobre cómo interpretar el archivo .EXP exportado por TwinCAT 2.8, especialmente enfocados en los tokens de lenguaje ladder (LAD) como `_NETWORK`, `_LD_ASSIGN`, `_LD_AND`, `_LD_CONTACT`, etc. Priorizaré repositorios de GitHub y recursos prácticos que ayuden a traducir estos tokens a un pseudocódigo similar a Structured Text (ST), útil para documentación. Te aviso en cuanto tenga los resultados.


# Interpretación de archivos .EXP de TwinCAT 2.8 para lógica Ladder (LD)

## Formato de exportación ASCII en TwinCAT 2.8

TwinCAT 2 (basado en CoDeSys 2.x) permite exportar el código PLC a archivos de texto con extensión `.exp`. Dado que los lenguajes gráficos como Ladder Diagram (LD) o Function Block Diagram (FBD) no tienen una representación textual estándar en IEC 61131-3, TwinCAT utiliza un **formato ASCII propio** para exportar estos POUs. En un archivo `.EXP` exportado, cada red (rung) de un diagrama Ladder se describe mediante una secuencia de *tokens* o palabras clave especiales en texto plano. Estos tokens representan contactos, bobinas, operaciones lógicas y la estructura de las redes. Por ejemplo, el manual oficial indica que las POUs en LD/FBD se pueden guardar como ASCII porque “no existe un formato de texto para esto en IEC 61131-3”, por lo que TwinCAT escribe los objetos seleccionados a un archivo ASCII.

Al exportar en TwinCAT 2.8, puede elegirse exportar cada POU en un archivo separado `<nombre>.exp` (por ejemplo, `Program1.exp`) o combinar todo en un solo archivo. El contenido del `.exp` incluye secciones para variables, listas de símbolos y, lo más importante, el **código Ladder como secuencia de tokens** que representan la lógica. A continuación, detallamos los tokens Ladder más comunes y cómo mapearlos a pseudocódigo legible (similar a Structured Text).

## Tokens del lenguaje Ladder en archivos .EXP

A continuación se listan los principales tokens encontrados en un `.exp` exportado de TwinCAT 2.8 para Ladder, junto con su significado e interpretación:

* **`_NETWORK`** – Indica el inicio de una *red* Ladder (un rung). Cada red Ladder comienza con este token. Puede ir seguida de un identificador de red o comentario de rung. Por ejemplo, un `.exp` típico mostrará cada rung separado iniciando con `_NETWORK`. Si existen comentarios de red, suelen aparecer a continuación.
* **`_COMMENT` / `_END_COMMENT`** – Delimitan un bloque de comentario. TwinCAT 2 permitía agregar comentarios por red (rung comment) que en el archivo `.exp` aparecen entre `_COMMENT` y `_END_COMMENT`. Este bloque (si existe) contiene el texto del comentario del rung.
* **`_LD_CONTACT`** – Representa un **contacto** en Ladder. Va seguido de la referencia de la variable booleana asociada (p. ej. una entrada, bit interno, etc.). Indica un contacto normalmente abierto por defecto, a menos que se especifique lo contrario con un flag de inversión. Inmediatamente después del `_LD_CONTACT <Variable>` suele aparecer un token `_EXPRESSION` que define si el contacto está invertido o no.
* **`_EXPRESSION _POSITIV`** – Este par de tokens suele seguir a un contacto o a una asignación para indicar **polaridad positiva** (no invertido). En el caso de un contacto, `_POSITIV` significa que es un contacto normalmente abierto (pasa la energía cuando la variable es TRUE). Si el contacto fuese normalmente cerrado, aparecería un indicador distinto (por ejemplo, `_NEG` u otro flag en lugar de `_POSITIV` – en la documentación de terceros se describe este campo como *“si está negado”*, siendo `_POSITIV` el valor cuando **no** está negado). En resumen, `_EXPRESSION _POSITIV` después de `_LD_CONTACT Var` confirma que el contacto `Var` se evalúa directamente (TRUE cuando `Var`=TRUE). Si fuera un contacto negado, veríamos un flag indicando inversión (p.ej., `_EXPRESSION _NEG`), lo que implicaría que en pseudocódigo se interpreta como `NOT Var`.
* **`_LD_AND`** – Operador lógico **AND** en Ladder. Este token señala que se realiza una conjunción lógica de las condiciones previas en la red. Por ejemplo, si dos contactos en serie alimentan una bobina, en el `.exp` aparecerá un `_LD_AND` para combinar ambos contactos. Generalmente viene acompañado de `_LD_OPERATOR : N`, donde *N* es el número de operandos que está combinando. Un `_LD_AND` con `_LD_OPERATOR : 2` indica que dos condiciones previas se están combinando con AND (es decir, ambas deben ser TRUE). En pseudocódigo, esto equivale a la operación lógica `Cond1 AND Cond2`. De modo similar existe `_LD_OR` (no mostrado arriba pero presente en exportaciones con ramas paralelas) para la operación OR lógico entre ramas.
* **`_LD_OR`** – (Aunque no aparece en nuestros ejemplos concretos, es análogo a `_LD_AND`.) Representaría una operación OR entre condiciones/paralelos en la red Ladder. Por ejemplo, contactos en paralelo se exportarían con `_LD_OR` y un `_LD_OPERATOR : N` indicando cuántos caminos paralelos se están OR-combinando.
* **`_LD_ASSIGN`** – Marca el **fin de la evaluación lógica de la red** y el inicio de las asignaciones a salidas. Es decir, una vez que se han procesado todos los contactos y operaciones lógicas de la red, `_LD_ASSIGN` indica que ese resultado booleano (TRUE/FALSE) se va a asignar a una o varias bobinas de salida. En la exportación, después de `_LD_ASSIGN` típicamente vendrá otra línea `_EXPRESSION _POSITIV` (o `_NEG`) para indicar si el resultado de la red se utiliza tal cual o invertido para las salidas. Por lo general, será `_POSITIV` salvo que se invierta toda la lógica del rung (situación poco común).
* **`_OUTPUTS : N`** – Indica el **número de salidas (bobinas) en esta red**. Si un rung Ladder tiene varias bobinas en paralelo que dependen de la misma lógica de contactos (por ejemplo, bobinas paralelas), aquí se listarán cuántas son. En la mayoría de redes Ladder típicas N=1 (una bobina al final del rung). Un ejemplo del formato exportado: `_OUTPUTS : 1 --1 个输出` significa “1 salida”. Si hubiera, por ejemplo, dos bobinas en paralelo, veríamos `_OUTPUTS : 2`.
* **`_OUTPUT`** – Define una **bobina de salida** (coil) a activar. Tras `_OUTPUT` se indican flags que describen el tipo de bobina y su polaridad, seguidos del nombre de la variable de salida asociada. Por ejemplo: `_OUTPUT _POSITIV _NO_SET D0001`. Aquí `_POSITIV` indica que la bobina no está invertida (es una bobina “normalmente desactivada”, energizada directamente con el resultado TRUE de la lógica) y `_NO_SET` indica que es una **bobina regular** (no del tipo Set/Reset). Finalmente `D0001` sería el nombre o dirección de la variable de esa bobina. En un contexto real, en lugar de `D0001` aparecería el nombre de la salida (por ejemplo `MotorOn`) o la dirección (%QX etc., dependiendo de cómo se exporten las variables).

  * **Bobinas Set/Reset:** Si la bobina fuera del tipo *latch* (enganche) de Set/Reset, en lugar de `_NO_SET` aparecería otro token. Por ejemplo, es esperable `_SET` para una bobina de *Set* dominante y `_RESET` para una de *Reset*. En la documentación no oficial se observa que `_NO_SET` se usa para bobinas normales, por lo que presumiblemente existen `_SET`/`_NO_RESET` como flags alternativos. Asimismo, la polaridad `_POSITIV` podría cambiar a `_NEG` si se tratara de una bobina negada (una bobina especial energizada cuando la condición es FALSE). En general: `_OUTPUT _POSITIV _NO_SET Var` corresponde a `Var := Resultado_logico` cuando la lógica es TRUE (bobina estándar), mientras que una variante `_OUTPUT _POSITIV _SET Var` significaría que `Var` se *establece (latchea)* a TRUE con la condición, y `_OUTPUT _POSITIV _RESET Var` que `Var` se resetea con la condición.
* **`END_PROGRAM`** – Marca el fin del bloque de programa exportado. El archivo `.exp` típico comienza con la declaración del POU (p. ej. `PROGRAM NombreProg LD`) y finaliza con `END_PROGRAM` una vez listadas todas las redes Ladder y salidas. Todo lo descrito entre estos delimitadores corresponde al contenido del POU Ladder en formato textual.

**Ejemplo ilustrativo:** En un foro técnico se mostró un fragmento de `.exp` resultante de exportar Ladder, que ayuda a visualizar varios de estos tokens y su secuencia. Por ejemplo:

```plaintext
_LD_CONTACT  A0001  (... variable de entrada ...)
_LD_CONTACT  A0002  (... otra entrada ...)
_LD_AND 
_LD_OPERATOR : 2    ; AND de 2 operandos (A0001 y A0002)
_LD_ASSIGN 
_OUTPUTS : 1        ; Una salida en esta red
_OUTPUT  _POSITIV  _NO_SET  D0001
END_PROGRAM
```

En este caso hipotético, `A0001` y `A0002` podrían ser dos contactos en serie y `D0001` una bobina de salida. Los tokens indican: carga dos contactos (`_LD_CONTACT`), combínalos con un AND de 2 entradas (`_LD_AND` + `_LD_OPERATOR:2`), asigna el resultado (`_LD_ASSIGN`) a 1 salida (`_OUTPUTS:1`), que es una bobina normal no invertida (`_OUTPUT _POSITIV _NO_SET`) asignada a la variable D0001.

Del mismo modo, otro ejemplo simple tomado de la documentación no oficial muestra la estructura para una red con **un contacto y una bobina** únicamente: primero el contacto y su variable, luego la asignación y la bobina de salida. Allí se observa `_LD_CONTACT p1` seguido de `_EXPRESSION _POSITIV` (contacto normalmente abierto con variable **p1**), luego `_LD_ASSIGN` con `_EXPRESSION _POSITIV` y finalmente `_OUTPUTS:1` con `_OUTPUT _POSITIV _NO_SET p2` para energizar la variable **p2**. Esta red equivale a una lógica donde *p2 = p1*, es decir, la bobina p2 se activa cuando la entrada p1 está activa.

## Mapeo de la lógica Ladder a pseudocódigo (Structured Text)

El objetivo de interpretar estos tokens es poder traducir la lógica Ladder en texto entendible, similar a Structured Text (ST) o pseudocódigo, para facilitar la documentación. Básicamente, se trata de reconstruir las expresiones booleanas y asignaciones a partir de la secuencia de tokens:

* **Contactos:** Cada `_LD_CONTACT Var` se convierte en una condición booleana sobre `Var`. Si el token va seguido de `_POSITIV`, significa que la condición es simplemente `Var` (TRUE cuando la variable es TRUE). Si estuviera negado, la condición sería `NOT Var`. En pseudocódigo ST podemos representar un contacto normalmente abierto como `Var` y uno normalmente cerrado como `NOT Var`.
* **Operadores lógicos AND/OR:** Tokens como `_LD_AND` con `_LD_OPERATOR:n` indican combinaciones lógicas. Por ejemplo, si hay dos contactos seguidos de `_LD_AND`, en ST sería una conjunción: `Cond1 AND Cond2`. Si hubiera `_LD_OR`, sería una disyunción: `Cond1 OR Cond2`. Estos operadores reflejan ramas en serie (AND) o en paralelo (OR) en el esquema Ladder. Por ejemplo, `_LD_AND` con 2 operandos se traduce como `ExpresionResultado = (Expr1 AND Expr2)`.
* **Asignación a salidas:** El token `_LD_ASSIGN` señala que la expresión lógica formada por los contactos y operadores anteriores ya determina el resultado del rung. En Ladder, ese resultado se envía a una o varias bobinas. En pseudocódigo, esto corresponde a realizar asignaciones a las variables de salida. Si `_OUTPUTS : 1`, hay una sola salida y simplemente pondremos esa variable igual a la expresión booleana resultante. Si hay múltiples salidas (p. ej. dos bobinas en paralelo), cada una recibirá el mismo valor de la expresión lógica. Por ejemplo, si la lógica calculada es `Expr` y hay dos salidas `Out1` y `Out2`, en ST podríamos escribir: `Out1 := Expr;  Out2 := Expr;`.
* **Bobinas (coils):** Un `_OUTPUT _POSITIV _NO_SET Var` se interpreta como una asignación directa: `Var := ResultadoLogico`. Si la bobina estuviera invertida (`_NEG`), equivaldría a `Var := NOT(ResultadoLogico)`. Si es un coil de *Set*, en Ladder significa que cuando la condición es TRUE se *establece* la variable (la mantiene a 1 hasta otro evento), lo cual en pseudocódigo se modelaría con algo como `IF Resultado THEN Var := TRUE; END_IF` (y análogamente un coil de Reset con `IF Resultado THEN Var := FALSE; END_IF`). No obstante, Ladder maneja set/reset de forma interna, por lo que para documentación suele ser suficiente indicar “(Set)” o “(Reset)” junto a la asignación.
* **Rung completo:** En conjunto, cada `_NETWORK` puede traducirse a un bloque *IF/THEN* o a una expresión booleana asignada. Una forma de documentarlo estilo ST es escribir la ecuación booleana de la red. Por ejemplo, considerando el fragmento anterior con dos contactos en serie asignando una bobina `Motor1`, la pseudocódigo podría ser: `Motor1 := A0001 AND A0002;` (suponiendo `A0001` y `A0002` son variables booleanas). Si hubiera contactos en paralelo (OR), se agruparían con paréntesis adecuadamente. Alternativamente, se puede expresarlo como lógica condicional:

  ```st
  IF (A0001 AND A0002) THEN 
      Motor1 := TRUE; 
  ELSE 
      Motor1 := FALSE; 
  END_IF;
  ```

  Ambas formas representan la misma lógica de la red Ladder en un formato textual claro.

**Notas:** También existen tokens para construcciones especiales. Por ejemplo, `_JUMP <etiqueta>` puede aparecer en `.exp` para reflejar instrucciones de salto (gotos) dentro de Ladder *il* o saltos condicionales (similar a instrucciones en lenguaje IL) – aunque en Ladder puro estándar no son comunes, CoDeSys permitía elementos como `jump`. Otro posible token es `_EN`/`_ENO` para conexiones de habilitación a cajas de función (FB/funciones) insertadas en Ladder. Estos casos avanzados van más allá de simples contactos y bobinas, pero siguen una lógica similar: el `.exp` listará llamados a funciones o saltos con sus parámetros en texto. Si el objetivo es documentación, normalmente se enfoca en la lógica combinacional de contactos y bobinas, que es lo descrito arriba.

## Herramientas y recursos para la conversión

Encontrar documentación detallada de este formato no estándar puede ser difícil, pero existen **recursos oficiales y de la comunidad** que ayudan a interpretarlo. Beckhoff no publica abiertamente la gramática completa de `.exp`, pero la información fragmentada en manuales y foros nos da guía. Por ejemplo, un manual de HollySys (un PLC basado en CoDeSys) incluye una sección explicando cada token Ladder (\_LD\_CONTACT, \_LD\_AND, \_OUTPUT, etc.) y cómo corresponden a los elementos gráficos. Aunque esté en chino, confirma la semántica: por ejemplo, `_LD_CONTACT --触点标识... _EXPRESSION --是否置反标识 _POSITIV` significa que `_LD_CONTACT` identifica un contacto y `_POSITIV` indica que **no** está negado. Del mismo modo, muestra `_OUTPUT ... _POSITIV _NO_SET ...` para una bobina normal. Este tipo de documentación no oficial puede servir de referencia de mapeo.

En cuanto a herramientas automáticas para convertir `.exp` Ladder a código legible o ST, **no se conocen utilidades públicas específicas** enfocadas solo en TwinCAT 2 `.exp`. Sin embargo, hay enfoques posibles:

* **Uso de TwinCAT 3/PLCopen:** Beckhoff TwinCAT 3 ofrece un **convertidor de formatos TwinCAT 2** integrado. Es posible importar el proyecto o POU exportado de TwinCAT 2 (archivo `.exp` o `.tpy`) a TwinCAT 3 y luego exportarlo a XML PLCopen, que es un formato estándar. El XML PLCopen describirá la lógica Ladder de forma estructurada, más fácil de leer o de procesar con scripts (por ejemplo, extrayendo las ecuaciones lógicas). De hecho, un experto sugiere usar PLCopen XML como vía de intercambio entre plataformas. Esto requeriría tener TwinCAT 3 instalado para la conversión, pero puede ahorrar tiempo si se dispone de muchos rungs.
* **Scripting personalizado:** Dado que el `.exp` es texto ASCII, se puede escribir un script (en Python u otro lenguaje) para *parsear* los tokens y regenerar la lógica. La gramática es relativamente simple (como se detalló arriba). Por ejemplo, uno podría leer línea a línea, identificar bloques `_NETWORK ... _OUTPUTS` y construir la expresión booleana intermedia. No encontramos una librería Python ya hecha para esto, pero es viable implementarlo. Algunos usuarios en foros han discutido partes del formato precisamente con la idea de traducirlo; por ejemplo, en un foro chino se intentó “traducir un programa Ladder de Siemens (TIA) a Beckhoff” analizando un `.exp` de TwinCAT, lo que evidencia el interés en tales conversiones. Un proyecto open-source relacionado es *Blark* (un parser de código ST de TwinCAT), que aunque está orientado a Structured Text, demuestra que es posible crear gramáticas para lenguajes PLC en Python. Para Ladder, un desarrollador podría definir reglas similares: contactos -> operandos booleanos, `_LD_AND` -> operador AND, etc., o incluso usar expresiones regulares dado el formato estructurado lineal del `.exp`.
* **Recursos comunitarios:** Revisar comunidades de automatización y repositorios GitHub puede dar frutos. Si bien no hallamos una herramienta específica lista para usar, sitios como PLCtalk y Stack Overflow tienen hilos donde se menciona la exportación `.exp`. Por ejemplo, en Stack Overflow se preguntó sobre importar .exp entre distintos entornos CoDeSys, confirmando que `.exp` es un formato Codesys genérico utilizado por múltiples marcas. Esto significa que documentación de **CoDeSys 2.3** también aplica (muchos controladores usaban el mismo formato export). En suma, buscar por “CoDeSys export .exp Ladder” puede arrojar tips de usuarios que hayan hecho ingeniería inversa del formato.

**Conclusión:** Mediante la combinación de fuentes oficiales (manuales de TwinCAT/Codesys) y no oficiales (ejemplos en foros, manuales de terceros), es posible mapear los tokens `_NETWORK`, `_LD_CONTACT`, `_LD_AND`, `_LD_ASSIGN`, `_OUTPUT`, etc., a construcciones lógicas comprensibles. La clave es reconocer la secuencia: una red Ladder en `.exp` corresponde a una expresión booleana (derivada de contactos y operadores) asignada a una o varias salidas. Documentar esa lógica en pseudocódigo estilo ST – por ejemplo, escribiendo ecuaciones lógicas o condiciones IF/THEN – hará más legible el programa. Con los ejemplos y explicaciones recopilados aquí, se tiene un **guía de referencia** para emprender esa traducción manual o mediante script, facilitando la comprensión de programas Ladder exportados de TwinCAT 2.8.

**Referencias usadas:** Documentación de Beckhoff TwinCAT 2, fragmentos de manual Hollysys/AutoThink, y ejemplos de código `.exp` discutidos en foros, entre otros. Estas fuentes proporcionan casos prácticos y descripciones claras para respaldar la interpretación de cada token y su equivalente lógico. Por último, la utilización de herramientas modernas (TwinCAT 3, PLCopen) o scripts personalizados son caminos recomendados si se requiere automatizar la conversión de `.exp` Ladder a texto estructurado.
