TN309BATCH - Batch Hooks en .NET

TN309BATCH - Batch Hooks en .NET

INTRODUCCIÓN

Esta nota técnica explica cómo crear y registrar una DLL en .NET Framework para implementar hooks en AVEVA Batch Management. Se presenta un ejemplo que incluye la implementación de algunos hooks disponibles, permitiendo capturar distintos eventos del ciclo de vida de un batch. Esta integración puede utilizarse para trazabilidad, monitoreo, auditoría o como punto de conexión con sistemas externos.
Los hooks son puntos de entrada definidos por el sistema que se ejecutan en momentos concretos del ciclo de vida de un batch. A través de ellos, es posible ejecutar código propio cuando se inicializa un batch, antes o después de una operación o fase, al asignar equipos, y en muchos otros eventos. Para ello, el sistema permite registrar una clase COM que implemente la interfaz IBatchHook, la cual contiene todos los métodos disponibles.

Hooks disponibles

A continuación se listan los principales métodos definidos en la interfaz IBatchHook, cada uno de los cuales puede activarse o ignorarse según necesidad:

  • BatchInit: Se ejecuta al inicializar un nuevo batch.

  • BatchPrepare: Se llama antes de ejecutar un batch.

  • BatchComplete: Se ejecuta al finalizar un batch.

  • UnitProcedurePrepare / UnitProcedureComplete: Eventos relacionados con el inicio y finalización de un Unit Procedure.

  • PhasePrepare / PhaseComplete: Se ejecutan al inicio y tras la finalización de cada fase.

  • OperationPrepare / OperationCompleteSe ejecutan al inicio y tras la finalización de las operaciones de la receta.

  • EquipAllocChange: Detecta cambios en la asignación de equipos.

  • EvalUnitAllocation / EvalConnAllocation: Permiten intervenir en la lógica de selección de unidades o conexiones.

  • LogEquipStatus: Captura estados de los equipos definidos en el modelo.

  • RoutineStates: Método especial que permite declarar qué hooks están activos y si deben ejecutarse también al finalizar el entorno.

Mediante una implementación controlada de estos métodos, es posible extender las capacidades del sistema para registrar eventos, validar condiciones de ejecución, automatizar tareas externas o interactuar con otros sistemas industriales.

Info
Para información más detallada de cada hook consultar el manual "AVEVA™ Batch Management COM Technical Reference Guide" capítulo 7, página 330 (versión Mayo 2023).

REQUISITOS PREVIOS

Para implementar hooks en AVEVA Batch Management mediante una DLL en .NET, se requiere un entorno de desarrollo compatible con .NET Framework y soporte para el desarrollo de bibliotecas COM.

Software necesario

  1. Entorno de desarrollo con soporte para proyectos Class Library (.NET Framework).
  2. .NET Framework Developer Pack 4.7.2 o superior, incluyendo herramientas como regasm.exe para registro de ensamblados.
  3. Instalación de AVEVA Batch Management, con acceso a las librerías COM necesarias (BatchObjSrv y BatchVBServer).
  4. Acceso administrativo para registrar la DLL en el sistema.
Notes
Nota: Es necesario tener instalado AVEVA Batch Management en la misma máquina en la que se va a desarrollar la dll.

VISUAL STUDIO

Para implementar los hooks de AVEVA Batch Management se utiliza un proyecto de tipo Class Library (.NET Framework) desarrollado en Visual Studio. La configuración del entorno debe permitir la generación de una DLL compatible con COM y ejecutable en procesos de 32 bits.

Tipo de proyecto

El proyecto debe ser una biblioteca de clases (Class Library) basada en .NET Framework, no .NET Core ni .NET (5/6/7). El destino debe estar configurado como plataforma x86, ya que el entorno de ejecución de Batch Management es de 32 bits.

Referencias necesarias

Es necesario agregar referencias a las siguientes bibliotecas COM, que están disponibles en el sistema si AVEVA Batch Management está instalado:
  1. BatchObjSrv 1.0 Type Library
  2. BatchVBServer 1.0 Type Library
Estas librerías permiten acceder al modelo de objetos del entorno de ejecución batch e implementar la interfaz IBatchHook.

Firma del ensamblado

El ensamblado debe estar firmado con un nombre fuerte (strong name) para poder ser registrado como servidor COM con la opción /codebase. Esto se configura desde las propiedades del proyecto, generando una clave (.snk) o utilizando una existente.

Info
Para asignar la clave .snk:
  1. Click derecho en el nombre del proyecto > Propiedades > Firma
  2. Habilitar Firmar el ensamblado
  3. Abrir el desplegable y seleccionar Nuevo
  4. Elegir un nombre para la clave .snk

Configuración COM

La clase que implementa los hooks debe estar marcada como visible para COM y tener un identificador único:

  1. [ComVisible(true)]
  2. [Guid("XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX")]
  3. [ClassInterface(ClassInterfaceType.None)]
  4. public class Hooks : IBatchHook
Info
Para obtener un guid nuevo desde Visual Studio;
  1. Herramientas
  2. Crear GUID
  3. Seleccionar Formato de Registro 
  4. Copiar
 También es necesario que el archivo AssemblyInforcs incluya lo siguiente:
  1. [assembly: ComVisible(true)]

Compilación

El proyecto debe compilarse en configuración Release (o Debug durante desarrollo) y con plataforma de destino x86. Esto garantiza la compatibilidad con el entorno Batch, que no admite DLLs de 64 bits.

Desarrollo de la funcionalidad extendida por hook

La clase principal del proyecto debe implementar la interfaz IBatchHook, que define todos los métodos que pueden ser interceptados durante el ciclo de vida de un batch en AVEVA Batch Management. Cada uno de estos métodos representa un punto de extensión donde es posible insertar lógica personalizada.

Estructura de la clase

La clase debe estar marcada con los atributos que permiten su registro como objeto COM:

  1. [ComVisible(true)]
  2. [Guid("XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX")]
  3. [ClassInterface(ClassInterfaceType.None)]
  4. public class Hooks : IBatchHook
  5. {
  6.     // Implementación de los métodos
  7. }

Métodos obligatorios

Aunque no se utilicen todos, la interfaz IBatchHook exige que se implementen todos sus métodos. En los casos donde no se requiera lógica específica, los métodos pueden estar vacíos o retornar un valor por defecto según corresponda.

  1. public int BatchInit(object pIBatch) => 1;

  2. public void BatchComplete(object pIBatch)
  3. {
  4.     // Ejemplo: registrar evento en log
  5.     var batch = (BOBatch)pIBatch;
  6.     System.IO.File.AppendAllText(@"C:\Logs\batchlog.txt",
  7.         $"Batch completado: {batch.Name} - {DateTime.Now}\n");
  8. }

Activación selectiva de hooks

El método RoutineStates permite declarar qué eventos se desean interceptar. Esto evita llamadas innecesarias al resto de los métodos.

  1. public void RoutineStates(object pIRoutines)
  2. {
  3.     BORoutines r = (BORoutines)pIRoutines;
  4.     r.RoutineState =
  5.         BatchRtnConstants.wwBatchInit |
  6.         BatchRtnConstants.wwBatchComplete |
  7.         BatchRtnConstants.wwRoutineStatesOnTerm;
  8. }
Los valores de RoutineState se pueden combinar para activar únicamente los hooks necesarios, o utilizar wwAllCOMInterfaces para activarlos todos.

Registro de la DLL

Una vez compilado el proyecto, el ensamblado debe registrarse en el sistema operativo para que AVEVA Batch Management pueda instanciar la clase Hooks como servidor COM. Este proceso se realiza mediante la herramienta regasm.exe incluida en el .NET Framework.

Ubicación de regasm.exe

Debe utilizarse la versión de 32 bits de regasm, ubicada en:

  1. C:\Windows\Microsoft.NET\Framework\v4.0.30319\regasm.exe

Notes
No debe utilizarse la versión de 64 bits (Framework64) ni la incluida en .NET Core.

Comando de registro

El comando a ejecutar, con privilegios de administrador, es:

  1. regasm "RutaCompleta\NombreDelProyecto.dll" /codebase

Por ejemplo:

  1. regasm "C:\Batch\Hooks\bin\x86\Release\BatchHooks.dll" /codebase

El modificador /codebase permite que el sistema localice la DLL sin necesidad de instalarla en el GAC. Para su uso, el ensamblado debe estar firmado con un nombre fuerte (strong name).

Confirmación

Si el registro es exitoso, se mostrará el mensaje:

  1. Types registered successfully
Notes

En caso de que no se registren tipos, verificar:

  • Que la clase tenga los atributos [ComVisible(true)], [Guid(...)], y ClassInterfaceType.None.

  • Que el ensamblado esté correctamente firmado.

  • Que se haya compilado como x86.


Configuración del entorno Batch Management

Para utilizar esta DLL con los Hooks no es necesario tener instalado en el mismo servidor de Batch el visual studio, una vez desarrollada la DLL se puede llevar y registrar en cualquier servidor con Batch.

Una vez registrada la DLL en el sistema, es necesario indicar al entorno de AVEVA Batch Management qué clase COM debe utilizar para ejecutar los hooks implementados. 

Parámetro COM Interface

La referencia al servidor COM se configura desde el Environment Editor de Batch Management, dentro del entorno en uso.

  1. Abrir el entorno en el Environment Editor.

  2. Buscar el parámetro COM Interface dentro del objeto de entorno.

  3. Establecer como valor el nombre completo de la clase implementada, en el siguiente formato

  1. <Namespace>.<NombreClase>

Por ejemplo:


Notes
Este nombre debe coincidir exactamente con el namespace y el nombre de la clase pública definida en la DLL. El sistema es sensible a mayúsculas y minúsculas.
Además, el número de caracteres total no debe superar 20 ya que es el límite que tiene el parámetro COM Interface para configurar en el Environment Editor.

Verificación

Para verificar que el entorno está reconociendo la clase COM correctamente, se recomienda:

  • Ejecutar un batch de prueba que dispare alguno de los eventos implementados.

  • Confirmar que la lógica definida en los métodos de hook se ejecuta correctamente (por ejemplo, escritura en log o generación de mensaje temporal).

Notes

En caso de error o de que no se registre actividad, revisar:

  • Que el nombre en el parámetro COM Interface sea exacto.

  • Que la DLL esté registrada correctamente y disponible en la ruta indicada.

  • Que el servicio de Batch tenga permisos para acceder al archivo y ejecutar la clase.


Warning
Si el parámetro COM Interface se configura erróneamente no será posible iniciar el servicio de BatchMngr obteniendo un mensaje en el SMC: 
  1. VB COM Server Class not found in registry [Exec.c               630 (error:     2)]

EJEMPLO

A continuación se presenta un ejemplo funcional que implementa únicamente los hooks BatchInit, BatchPrepare y BatchComplete, dejando el resto deshabilitado desde RoutineStates. Como demostración, se registra información básica en un archivo de log cuando se activan los eventos relevantes.

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Threading.Tasks;
  6. using BATCHOBJSRVLib;
  7. using BATCHVBSERVERLib;
  8. using System.Runtime.InteropServices;

  9. namespace BatchFunction
  10. {
  11.     [ComVisible(true)]
  12.     [Guid("1422205F-E57B-4254-B344-F621D6AB1365")]  // Cambia esto por un nuevo Guid si quieres
  13.     [ClassInterface(ClassInterfaceType.None)]
  14.     public class Hooks :IBatchHook
  15.     {
  16.         public int BatchInit(object pIBatch)
  17.         {
  18.             try
  19.             {
  20.                 BOBatch batch = (BOBatch)pIBatch;
  21.                 string campaign = batch.Campaign;
  22.                 string lot = batch.Lot;
  23.                 string sbatch = batch.Batch;
  24.                 string size = batch.Size.ToString();
  25.                 string path = @"C:\Logs\batchlog.txt";

  26.                 System.IO.File.AppendAllText(path, $"[Batch Inicializado]: {campaign}, {lot}, {sbatch}, {size} - {DateTime.Now}\n");
  27.             }
  28.             catch (Exception ex)
  29.             {
  30.                 System.IO.File.AppendAllText(@"C:\Logs\batchlog.txt", $"Error: {ex.Message}\n");
  31.             }
  32.             return 1;
  33.         }
  34.         public void BatchComplete(object pIBatch)
  35.         {
  36.             try
  37.             {
  38.                 BOBatch batch = (BOBatch)pIBatch;
  39.                 string campaign = batch.Campaign;
  40.                 string lot = batch.Lot;
  41.                 string sbatch = batch.Batch;
  42.                 string size = batch.Size.ToString();
  43.                 string path = @"C:\Logs\batchlog.txt";

  44.                 System.IO.File.AppendAllText(path, $"[Batch completado]: {campaign}, {lot}, {sbatch}, {size} - {DateTime.Now}\n");
  45.             }
  46.             catch (Exception ex)
  47.             {
  48.                 System.IO.File.AppendAllText(@"C:\Logs\batchlog.txt", $"Error: {ex.Message}\n");
  49.             }
  50.         }
  51.         public int BatchPrepare(object pIBatch)
  52.         {
  53.             try
  54.             {
  55.                 BOBatch batch = (BOBatch)pIBatch;
  56.                 string campaign = batch.Campaign;
  57.                 string lot = batch.Lot;
  58.                 string sbatch = batch.Batch;
  59.                 string size = batch.Size.ToString();
  60.                 string path = @"C:\Logs\batchlog.txt";

  61.                 System.IO.File.AppendAllText(path, $"[Batch iniciado]: {campaign}, {lot}, {sbatch}, {size} - {DateTime.Now}\n");
  62.             }
  63.             catch (Exception ex)
  64.             {
  65.                 System.IO.File.AppendAllText(@"C:\Logs\batchlog.txt", $"Error: {ex.Message}\n");
  66.             }
  67.             return 1;
  68.         }
  69.         public void EquipAllocChange(object pIBatch, object pIEquipment)
  70.         {
  71.         }
  72.         public int EvalConnAllocation(object pIBatch, object pIEquipment)
  73.         {
  74.             return 1;
  75.         }
  76.         public int EvalUnitAllocation(object pIBatch, object pIEquipment)
  77.         {
  78.             return 1;
  79.         }
  80.         public void LogEquipStatus(object pIEquipStatus)
  81.         {
  82.         }
  83.         public void OperationComplete(object pIBatch, string opname, string
  84.         processinstance)
  85.         {
  86.         }
  87.         public void OperationPrepare(object pIBatch, string opname, string
  88.         processinstance)
  89.         {
  90.         }
  91.         public void PhaseComplete(object pIBatch, object pIPhase, object
  92.         pIEquipment)
  93.         {
  94.         }
  95.         public void PhasePrepare(object pIBatch, object pIPhase, object
  96.         pIEquipment)
  97.         {
  98.         }
  99.         
  100.         public void RoutineStates(object pIRoutines)
  101.         {
  102.             // Check to see if you are terminating
  103.             if (pIRoutines == null)
  104.             {
  105.                 // You are terminating- Do cleanup and exit
  106.                 return;
  107.             }
  108.             // Define required local variable
  109.             BORoutines hook_routines = null;
  110.             // Enable all of the required routines
  111.             hook_routines = (BORoutines)pIRoutines;
  112.             //*******Habilitar todos los Hooks
  113.             //hook_routines.RoutineState = BatchRtnConstants.wwAllCOMInterfaces 
  114.             //|
  115.             // BatchRtnConstants.wwRoutineStatesOnTerm;
  116.             //*******Habilitar los Hooks -> BatchInit, BatchComplete y BatchPrepare
  117.             hook_routines.RoutineState =
  118.                 BatchRtnConstants.wwBatchInit |
  119.                 BatchRtnConstants.wwBatchComplete |
  120.                 BatchRtnConstants.wwBatchPrepare |
  121.                 BatchRtnConstants.wwRoutineStatesOnTerm;
  122.         }
  123.         public void UnitProcedureComplete(object pIBatch, string
  124.         unitprocedurename, string processinstance)
  125.         {
  126.         }
  127.         public void UnitProcedurePrepare(object pIBatch, string unitprocedurename,
  128.         string processinstance)
  129.         {
  130.         }
  131.     }
  132. }

Este ejemplo asegura una implementación válida y funcional del hook, respetando los requisitos del entorno COM, y registrando solo los eventos relevantes. El resto de métodos se mantiene implementado pero sin contenido, ya que la interfaz IBatchHook lo exige.