UNIVERSIDAD SIMÓN BOLÍVAR Decanato de Estudios Profesionales Coordinación de Ingeniería Electrónica Estudio de aplicabilidad de los protocolos de última generación en Automatización Eléctrica con el sistema EMS Sinaut Spectrum Por Carlos Enrique Varela Belloso Álvaro Alonso Fernández González Sartenejas, Marzo de 2005 UNIVERSIDAD SIMÓN BOLÍVAR Decanato de Estudios Profesionales Coordinación de Ingeniería Electrónica Estudio de aplicabilidad de los protocolos de última generación en Automatización Eléctrica con el sistema EMS Sinaut Spectrum. Por Carlos Enrique Varela Belloso Álvaro Alonso Fernández Gonzáles Realizado con la Asesoría de Tutor Académico: Prof. José Manuel Andrade Da S., M. Sc. Tutor Industrial: Ing. Hugo Méndez INFORME FINAL DE CURSOS DE COOPERACION Presentado ante la Ilustre Universidad Simón Bolívar como requisito parcial para optar al título de Ingeniero Electrónico Sartenejas, Marzo de 2005 UNIVERSIDAD SIMÓN BOLÍVAR Decanato de Estudios Profesionales Coordinación de Ingeniería Electrónica Estudio de aplicabilidad de los protocolos de última generación en Automatización Eléctrica con el sistema EMS Sinaut Spectrum. PROYECTO DE GRADO presentado por: Carlos Enrique Varela Belloso Álvaro Alonso Fernández González REALIZADO CON LA ASESORIA DE: Prof. José Manuel Andrade Da S., M. Sc. Ing. Hugo Méndez. RESUMEN En la Corporación Enelven se plantea la utilización de la norma IEC-61850 para la automatización de nuevas subestaciones y para la expansión y/o renovación de las ya existentes. El sistema SCADA actual sólo es compatible con los protocolos de comunicación IEC-61870-101 e ICCP. Para alargar la vida útil del sistema SCADA actual se realiza un estudio para verificar la viabilidad del desarrollo de interfaces que permitan la comunicación de dicho sistema a través de otros protocolos. Se estudian diferentes protocolos de automatización recientes para elaborar una propuesta sobre cómo debe ser la aplicación de los mismos en una subestación típica de Enelven y cómo sería su integración al sistema EMS Sinaut Spectrum. Se elaboró dicha propuesta basada en la norma IEC-61850, como filosofía de diseño y organización de los datos de una subestación eléctrica, y el protocolo OLE for Process Control (OPC), como medio de comunicación entre la subestación y el centro de control. Se desarrolló un prototipo de interfaz entre el protocolo OPC y el sistema SCADA. PALABRAS CLAVES: SCADA, Protocolo OPC, Norma IEC-61850, Subestaciones Eléctricas, Sinaut Spectrum. Aprobado con mención: Postulado para el premio: Sartenejas, Marzo 2005. DEDICATORIA AGRADECIMIENTOS i ÍNDICE GENERAL ÍNDICE DE FIGURAS ...................................................................................... iii INDICE DE TABLAS ........................................................................................ iv ÍNDICE GENERAL.............................................................................................i LISTA DE ABREVIATURAS ...............................................................................v CAPÍTULO 1. INTRODUCCIÓN. ........................................................................1 CAPÍTULO 2. FUNDAMENTOS TEÓRICOS ..........................................................3 2.1 Introducción ........................................................................................3 2.2 Definiciones Básicas .............................................................................3 2.3 Sinaut Spectrum ................................................................................ 12 2.4 Proceso de Recolección de datos en una Subestación Eléctrica .................. 19 2.5 Evolución de las comunicaciones en el sector eléctrico: Norma IEC-61850. . 24 2.6 OLE for Process Control (OPC).............................................................. 29 CAPÍTULO 3. PLANTEAMIENTO DEL PROBLEMA................................................ 38 3.1 Introducción:..................................................................................... 38 3.2 Formulación del Problema.................................................................... 38 CAPÍTULO 4. DESARROLLO DEL PROYECTO .................................................... 41 4.1 Introducción ...................................................................................... 41 4.2 Metodología....................................................................................... 41 4.3 Familiarización con el Ambiente de Trabajo. ........................................... 43 4.4 Estudio del Sistema EMS Sinaut Spectrum ............................................. 44 4.5 Estudio del flujo de datos entre las S/E y el centro de control ................... 45 4.6 Interfaz Propuesta.............................................................................. 49 4.6.1 Solaris – Interfaz OPC SCADA (SIOS). ............................................... 52 4.6.2 Protocolo IOS. ................................................................................ 59 4.6.3 Windows – Interfaz OPC SCADA (WIOS)............................................. 64 4.6.4 Incorporación de las subestaciones al proyecto IOS ............................. 71 CAPÍTULO 5. PRUEBAS Y RESULTADOS .......................................................... 79 5.1 Introducción:..................................................................................... 79 5.2 Pruebas Realizadas: ........................................................................... 79 CAPÍTULO 6. CONCLUSIONES Y RECOMENDACIONES....................................... 83 REFERENCIAS BIBLIOGRÁFICAS ........................................................................ 77 ii ÍNDICE DE FIGURAS Figura # 2-1 Topologías de Red en Anillo ..............................................................5 Figura # 2-2 Topología en Estrella .......................................................................5 Figura # 2-3 Topología de Reb estilo Bus ..............................................................6 Figura # 2-4 Topologías de Red...........................................................................6 Figura # 2-5 Dirección tecnológica ..................................................................... 16 Figura # 2-6 Diagrama de Distribución de los Datos. ............................................ 18 Figura # 2-7 Diagrama de una RTU SICAM SAS. .................................................. 20 Figura # 2-8 RTU SICAM SAS........................................................................... 20 Figura # 2-9 Diagrama de conexiones de una S/E ............................................... 21 Figura # 2-10 Diagrama de la distribución de los datos. ........................................ 22 Figura # 2-11 Diagrama Unifilar Subestación Don Bosco ....................................... 23 Figura # 2-12 Disminución de Drivers específicos................................................. 30 Figura # 2-13 Arquitectura OPC......................................................................... 33 Figura # 2-14 Jerarquía del objeto de automatización OPC .................................... 34 Figura # 4-1 Estructura general del IOS ............................................................. 50 Figura # 4-2 Sistema de Adquisición de Datos del Sinaut Spectrum. ....................... 51 Figura # 4-3 Inserción del IOS al Sistema SCADA. ............................................... 51 Figura # 4-4 Comportamiento de la cola de mensajes........................................... 53 Figura # 4-5 Funcionamiento general del SIOS. ................................................... 54 Figura # 4-6 Diagrama de funcionamiento del núcleo. .......................................... 55 Figura # 4-7 Diagrama de Flujo del Despachador ................................................. 56 Figura # 4-8 Diagrama de Flujo del Receptor PIOS............................................... 58 Figura # 4-9 Diagrama de flujo del receptor softbus. ........................................... 59 Figura # 4-10 Diagrama de Flujo del proceso de conexión PIOS. ............................ 60 Figura # 4-11 Estructura de las tramas pios. ....................................................... 60 Figura # 4-12 Ejemplos de Encabezados PIOS. .................................................... 61 Figura # 4-13 Ejemplo de Bloque de Datos. ....................................................... 63 Figura # 4-14 Diagrama de Bloques del WIOS .................................................... 64 Figura # 4-15 Tablas de la Base de Datos utilizada en el prototipo WIOS................. 66 Figura # 4-16: Diagrama de Bloques de la Utilidad de Configuración....................... 70 Figura # 4-17: Diagrama Top-Down de la UC IOS ................................................ 71 Figura # 4-18 Funcionamiento del Núcleo de WIOS. Diagrama básico ..................... 72 Figura # 4-19 Arquitectura del Sistema usando un Servidor OPC Matrikon ............... 73 Figura # 4-20 Comunicación SICAM – OPC – WIOS ............................................. 74 iii Figura # 4-21 Incorporación de equipos IEC-61850 en una subestación ya establecida ............................................................................................................... 75 Figura # 4-22 Subestación diseñada utilizando la norma IEC61850......................... 76 Figura # 4-23: Propuesta de ABB para una subestacion compatible con la norma IEC61850 ...................................................................................................... 77 Figura # 5-1 Exploración del Servidor OPC utilizando la UC IOS ............................. 79 Figura # 5-2 Archivo de Registro SIOS ............................................................... 82 INDICE DE TABLAS Tabla # 2-1 Capas del Modelo OSI .......................................................................7 Tabla # 2-2 Evolución de las comunicaciones en el sector eléctrico ........................ 24 Tabla # 2-3 Estructura de la Norma IEC-61850 ................................................... 29 Tabla # 2-4 Descripción de los objetos OPC ........................................................ 35 Tabla # 4-1 Resultados del estudio comparativo: IEC-101 e IEC-104 ...................... 48 Tabla # 4-2: Resultados del estudio comparativo: OPC ......................................... 48 Tabla # 4-3 Valores Posibles en Encabezado PIOS................................................ 62 Tabla # 4-4 Valores posibles en bytes de control y tipo de datos. ........................... 63 Tabla # 4-5 Descripción de las tablas que conforman la Base de Datos ................... 67 Tabla # 5-1 Valores generados por el servidor OPC genérico ................................. 82 iv LISTA DE ABREVIATURAS ACSI: ADM: Abstract Communication Service Interface (Interfaz abstracta para servicios de comunicación). Administration Server (Servidor Administrador). CNM: Computer Network Management (Manejo de redes de computadoras). COM: Component Object Model (Modelo de objeto Componente). DAS: Data Acquisition Subsystem (Subsistema de Adquisición de Datos). DBA: Data Base Administrador (Administrador de Base de Datos). DCOM: Distributed COM (COM Distribuido). DLL: Dynamic Link Library (Librería de Enlace Dinámico). DNP: Distributed Network Protocol (Protocolo de red Distribuida). EMS: Energy Management System (Sistema Manejo de Energía). HMI: Human-Machine Interface (Interfaz Hombre-Máquina HTTP: Hypertext Transfer Protocolo (Protocolo de Transferencia de Hipertexto). IEC: International Electric Committee (Comité Eléctrico Internacional) IEC-101: IEC 60870-T-5-101. IED: Intelligent Electronic Device (Dispositivo Electrónico Inteligente). IOS: Interfaz OPC SOFTBUS. LAN: Local Area Network (Red de Área Local). MMS: Manufacturing Message Specification (Especificación de mensajes de manufactura) ODB: Operacional Data Base (Base de datos operacional). OLE: Object Link Embedding (Enlace de Objetos Embebidos). OPC: OLE for Process Control (OLE para control de procesos). OPCDA: OPC Data Access (OPC para Acceso a Datos). PC: Process Computer (Computador operativo en la red SCADA). PIOS: Protocolo IOS. PLC: Programmable Logic Controller (Controlador Lógico Programable) RTC: Real Time Communicator (Comunicador en Tiempo Real). RTU: Remote Terminal Unit (Unidad Terminal Remota). S/E: Subestación Eléctrica. SAS: Sistema de Automatización de Subestaciones. SB: Servidor en modo Stand-By SCADA: Supervisory Control and Data Acquisition (Control Supervisorio y Adquisición de Datos). v SCSM: Specific Communication Service Mapping (Servicio de Mapeo para Comunicaciones Específicas). SDM: Source Data Manager (Manejador de Fuente de Datos). SIOS: Solaris IOS. TCP/IP: Transmission Control Protocol / Internet Protocol (Protocolo de Control de Transmisión, Protocolo de Internet). TIF: Telecontrol Interface (Interfaz de Telecontrol). UI: User Interface (Interfaz de Usuario). WAN: Wide Area Network (Red de Área extendida). WIOS: Windows IOS. XML: Extensible Markup Language (lenguaje de marcación extensible). 1 CAPITULO 1. INTRODUCCIÓN CAPÍTULO 1. INTRODUCCIÓN. Procesamiento Electrónico de Datos (PROCEDATOS) es una empresa que se consolida el 01 de Abril de 1969, con la misión de proveer servicios de Informática y Telecomunicaciones, que satisfagan las necesidades de los clientes, implantando soluciones innovadoras, confiables y oportunas, que generen rentabilidad y satisfacción a los empleados y accionistas. En términos generales, ofrece soluciones tecnológicas a la medida de las necesidades de sus clientes entre los cuales se encuentran la Energía Eléctrica de Venezuela (ENELVEN), Energía Eléctrica de la Costa Oriental (ENELCO), Aseo Urbano, Alcaldías, entre otros. Uno de los principales retos que debe afrontar toda empresa de este sector es el cambio constante en las tecnologías y la innovación de productos en el mercado, es por ello que se ven en la necesidad de impulsar estudios e investigaciones con el objetivo de mantenerse actualizados y aprovechar las ventajas que estas nuevas tecnologías puedan brindar a sus clientes. Desde la década de los ochenta, Procedatos provee servicios de automatización industrial, sistemas de control supervisorio y adquisición de datos (SCADA) y de entonación de procesos en la industria. El principal cliente de Procedatos en esta área es ENELVEN, el cual utiliza un sistema SCADA propietario de SIEMENS llamado SINAUT SPECTRUM®. Éste permite controlar todas las subestaciones eléctricas del estado Zulia desde un centro de control ubicado en Caujarito. A este sistema es transmitida la información proveniente de las subestaciones, permitiendo a los operadores supervisar y controlar el proceso de generación, transmisión y distribución de energía eléctrica. El medio de transmisión de los datos es el protocolo IEC-60870-5-101, el cual está orientado a medios seriales. Recientemente se han desarrollado nuevos protocolos en el área de Automatización de la Industria Eléctrica que ofrecen una funcionalidad mucho mayor que el IEC-101; estos protocolos están orientados a medios de comunicación más versátiles como TCP/IP sobre Ethernet, lo cual ofrece una gran cantidad de ventajas a nivel de costos, simplicidad en la implementación del sistema, entrenamiento del personal de mantenimiento e instalación, entre otras. Actualmente en ENELVEN se están realizando esfuerzos para equipar subestaciones eléctricas con soporte para IEC-61850; sin embargo, el sistema SINAUT Spectrum actualmente no soporta los protocolos especificados en esta norma; en vista de la gran inversión que representa este sistema para ENELVEN surge la necesidad de expandir su funcionalidad para que soporte nuevos protocolos. En respuesta a esta necesidad fue propuesto el “Estudio de aplicabilidad de los protocolos de última CAPITULO 1. INTRODUCCIÓN 2 generación en Automatización Eléctrica con el sistema EMS SINAUT Spectrum” que busca el desarrollo de una interfaz entre el sistema SCADA y subestaciones eléctricas que utilicen protocolos diferentes al IEC-101. Con este estudio se pretenden analizar las modificaciones que se deberán hacer, tanto al sistema SCADA como a los procesos de recolección de datos en las subestaciones eléctricas, para expandir las posibilidades de conectividad y eliminar las actuales limitaciones de comunicación del sistema. El alcance de este proyecto consiste en un diagnóstico de los sistemas de comunicación entre las S/E y el centro de control presentes en la corporación, un estudio de nuevos protocolos de automatización eléctrica, la elaboración de una propuesta de cómo incorporar estos protocolos al sistema actual de la empresa y el desarrollo de un prototipo de interfaz al sistema SCADA que demuestre la viabilidad y aplicabilidad de dicha propuesta. El presente informe está dividido en seis capítulos, siendo esta introducción el primero de ellos. El segundo capítulo trata los conceptos teóricos empleados en el desarrollo del proyecto, se presenta un breve estudio de los protocolos IEC-61850 y OPC, una descripción del sistema SINAUT Spectrum y del proceso actual de recolección de datos en las S/E. En el tercer capítulo, se plantea el problema, así como los objetivos que persigue el proyecto y su justificación. En el cuarto capítulo se resuelve el problema plantead, detallando cada una de las etapas del desarrollo. Las pruebas y resultados se encuentran sintetizados en el capítulo seis. El séptimo capítulo expone las conclusiones y recomendaciones finales del proyecto. CAPÍTULO 2. 2.1 FUNDAMENTOS TEÓRICOS Introducción En este capítulo se agrupan conceptos y definiciones que se emplearán a lo largo de este informe. Se incluyen tanto conceptos generales como definiciones técnicas específicas y descripciones de procesos relevantes para el desarrollo del proyecto. Se comienza con definiciones básicas relacionadas con el proceso de transmisión de energía eléctrica, de los equipos que se utilizan en el mismo, así como de protocolos y lenguajes asociados al proceso de comunicación. Se continúa con una descripción detallada del sistema de manejo de energía de ENELVEN y el flujo de los datos dentro del mismo. Finalizando con una descripción de los protocolos que se estudian. 2.2 Definiciones Básicas a) Subestaciones Eléctricas (S/E). Una subestación eléctrica consta de un conjunto de equipos con la funcionalidad necesaria para dirigir el flujo de energía eléctrica desde las fuentes hasta las cargas en un sistema de potencia, de forma tal que garantice la seguridad, disponibilidad y confiabilidad del servicio, permitiendo la redistribución del flujo de energía a través de rutas alternas durante contingencias. Para lograr esto se requieren múltiples dispositivos electrónicos inteligentes (IED) encargados de realizar las mediciones, ejecutar los comandos de control, proteger los equipos de potencia y mantener la comunicación con los concentradores de información locales y remotos como las unidades terminales remotas o las interfaces hombre-máquina [2]. b) Dispositivo Electrónico Inteligente (IED). El término IED se refiere a todos aquellos dispositivos de campo que incorporen uno o más procesadores con la capacidad de recibir o enviar datos desde y hasta una fuente externa; debe ser capaz también de controlarse externamente y de ejercer control sobre un proceso externo. ej. Protecciones, Relés, etc. [2]. c) Unidad Terminal Remota (RTU). En sistemas SCADA, una RTU es un dispositivo en una ubicación remota que recolecta los datos de campo y los codifica de acuerdo al protocolo de transmisión utilizado para enviarlos al centro de control. Una RTU también recibe información CAPITULO 2. FUNDAMENTOS TEÓRICOS 4 proveniente del centro de control para ejecutar procesos dirigidos por el personal ubicado en el mismo. Están equipadas con canales de entrada para sensar y medir señales; canales de salida para control de procesos, indicaciones o alarmas; y con un puerto de comunicaciones para la transmisión de datos [2]. d) Sistema de Control Supervisorio y Adquisición de Datos (SCADA). Se trata de una aplicación software especialmente diseñada para funcionar sobre ordenadores en el control de producción, proporcionando comunicación con los dispositivos de campo (controladores autónomos, autómatas programables, etc.) y controlando el proceso de forma automática desde la pantalla del ordenador. Además, provee de toda la información que se genera en el proceso productivo a diversos usuarios, tanto del mismo nivel como de otros supervisores dentro de la empresa: control de calidad, supervisión, mantenimiento, etc. [1]. Entre las funciones principales de un sistema SCADA se pueden resaltar [2]: 1) Recabar, almacenar y mostrar información en forma continua y confiable correspondiente a la señalización de campo. 2) Ejecutar acciones de control de forma automática o manual iniciadas por el operador. 3) Generar datos históricos de la señal de planta, que puedan ser aprovechados por otras aplicaciones. 4) Permitir desarrollar aplicaciones basadas en PC, con captura de datos, análisis de señales, presentaciones en pantallas, envió de resultados a discos e impresoras, etc. e) Topologías de Red La topología o forma lógica de una red se define como la forma de tender el cable a estaciones de trabajo individuales; por muros, suelos y techos del edificio. Existen tres topologías comunes [5]: • Anillo: Las estaciones están unidas unas con otras formando un círculo por medio de un cable común Figura # 2-1. El último nodo de la cadena se conecta al primero cerrando el anillo. Las señales circulan en un solo sentido alrededor del círculo, regenerándose en cada nodo. Con esta metodología, cada nodo examina la información que es enviada a través del anillo. Si la información no está dirigida al nodo que la examina, la pasa al siguiente. La desventaja del anillo es que si se rompe una conexión, se cae la red 5 CAPITULO 2. FUNDAMENTOS TEÓRICOS completa. Existe una variación de esta topología anillo, utilizada principalmente en redes de fibra óptica, conocida como el doble anillo, este permite mejorar la confiabilidad del sistema al incluir redundancia en el medio de transmisión. Figura # 2-1 Topologías de Red en Anillo [20] • Estrella: La red se une en un único punto, normalmente con un panel de control centralizado, como un concentrador de cableado Figura # 2-2. Los bloques de información son dirigidos a través del panel de control central hacia sus destinos. Este esquema tiene la ventaja de poder monitorear el tráfico y evitar las colisiones. Figura # 2-2 Topología en Estrella [20] • Bus: Las estaciones están conectadas por un único segmento de cable Figura # 2-3. A diferencia del anillo, el bus es pasivo, no se produce regeneración de las señales en cada nodo. Los nodos en una red de bus transmiten la información y esperan que ésta no vaya a chocar con otra información transmitida por otro de los nodos. Si esto ocurre, cada nodo espera una pequeña cantidad retransmitir la información. de tiempo al azar, después intenta 6 CAPITULO 2. FUNDAMENTOS TEÓRICOS Figura # 2-3 Topología de Reb estilo Bus [20] • Híbridas: El bus lineal, la estrella y el anillo se combinan algunas veces para formar combinaciones de redes híbridas Figura # 2-4. o Anillo en estrella: Esta topología se utiliza con el fin de facilitar la administración de la red. Físicamente, la red es una estrella centralizada en un concentrador, mientras que a nivel lógico, la red es un anillo. o Bus en estrella: El fin es igual a la topología anterior. En este caso la red es un "bus" que se cablea físicamente como una estrella por medio de concentradores. o Estrella jerárquica: Esta estructura de cableado se utiliza en la mayor parte de las redes locales actuales, por medio de concentradores dispuestos en cascada par formar una red jerárquica. Anillo en Estrella Bus en Estrella Estrella Jerárquica Figura # 2-4 Topologías de Red [20] f) Modelo OSI (Open System interconnection) El Modelo OSI es un lineamiento funcional para tareas de comunicaciones producto de un esfuerzo auspiciado por organismos internacionales incluso externos al 7 CAPITULO 2. FUNDAMENTOS TEÓRICOS sector de las comunicaciones. El modelo no especifica un estándar de comunicación; sin embargo, muchos estándares y protocolos cumplen con los lineamientos del Modelo bien sea total o parcialmente. El modelo ha divido el proceso de comunicaciones en siete (7) capas o niveles. En la Tabla # 2-1 se muestran estas capas junto con una breve descripción. Capa Descripción Este (7) Aplicación nivel proporciona comunicación entre dos procesos de aplicación, tales como: programas de aplicación, aplicaciones de red, etc. Proporciona aspectos de comunicaciones para aplicaciones especificas entre usuarios de redes: manejo de la red, protocolos de transferencias de archivos (ftp), etc. Este nivel traduce el formato y asignan una sintaxis a los datos para (6) Presentación su transmisión en la red. Determina la forma de presentación de los datos sin preocuparse de su significado o semántica. Establece independencia a los procesos de aplicación considerando las diferencias en la representación de datos. (5) Sesión Este nivel provee los servicios utilizados para la organización y sincronización del diálogo entre usuarios y el manejo e intercambio de datos. Establece el inicio y término de la sesión. Este nivel actúa como un puente entre los tres niveles inferiores (4) Transporte totalmente orientados a las comunicaciones y los tres niveles superiores totalmente orientados al procesamiento. Además, garantiza una entrega confiable de la información. Este nivel define como direccionar la localidad física de los dispositivos de la red. (3) Red (2) Enlace (1) Física Este nivel define el enrutamiento y el envío de paquetes entre redes. Es responsabilidad de este nivel establecer, mantener y terminar las conexiones. Este nivel proporciona facilidades para la transmisión de bloques de datos entre dos estaciones de red. Esto es, organiza los 1's y los 0's del Nivel Físico en formatos o grupos lógicos de información Define el medio de comunicación utilizado para la transferencia de información, dispone del control de este medio y especifica bits de control. Tabla # 2-1 Capas del Modelo OSI [12] CAPITULO 2. FUNDAMENTOS TEÓRICOS 8 Estas capas se han convertido en la referencia para todo documento actual sobre transmisión; sin embargo aunque está plenamente vigente y su uso es prácticamente universal, es un modelo todavía inacabado. Su complejidad creciente es la razón principal detrás de un desarrollo tan tortuoso. Faltan por completar los niveles superiores, los cuales suponen la aportación más innovadora del modelo: que las comunicaciones deben quedar cubiertas por el nivel de las aplicaciones siendo elementos transparentes, no visibles, dentro de los programas que las personas normalmente usan [16]. g) Arquitectura cliente-servidor: El modelo cliente-servidor o servidor-cliente es una forma de dividir y especializar programas y equipos de cómputo a fin de que la tarea que cada uno de ellos realiza se efectúe con la mayor eficiencia, y permita simplificar las actualizaciones y mantenimiento del sistema. En esta arquitectura la capacidad de proceso está repartida entre el servidor y los clientes. En una arquitectura monolítica no hay distribución. En un comienzo, los Mainframes, grandes computadores centrales, concentraban la funcionalidad de almacenamiento y lógica. A ellos se conectaban terminales con muy poca o nula capacidad de procesamiento, diseñadas para ser simples interfaces con el usuario. En el modelo cliente-servidor, en cambio, el trabajo se reparte entre dos computadores. Así el servidor o host no necesita tanta potencia de procesamiento, pues parte del proceso se reparte con los clientes. Otro beneficio es la reducción del tráfico de red, pues el cliente se conecta al servidor solo cuando requiere datos del mismo, al obtenerlos cierra la conexión dejando la red libre, mientras procesa los datos recién obtenidos o lleva a cabo otras tareas locales [4]. h) TCP/IP TCP/IP es un conjunto de protocolos diseñado con una arquitectura en capas. Las capas permiten a los diseñadores del protocolo dividir en módulos las tareas y servicios que realizará el mismo. El diseño también especifica la manera en que un módulo interactúa con otros. La arquitectura en capas de los protocolos está diseñada como una pila en la que los protocolos de más alto nivel interactúan con protocolos de niveles más bajos. El modelo del TCP/IP, a diferencia del Modelo OSI consta de 4 capas [1]: 1) Físico y Red: Corresponde a la interfaz de la red real dado que TCP/IP no especifica ningún protocolo concreto para este fin. CAPITULO 2. FUNDAMENTOS TEÓRICOS 9 2) Internet: Es equivalente a lo definido para la capa de red del modelo OSI. Nos encontramos en esta capa al protocolo IP, el cual se encarga de enviar los paquetes de información a sus destinos correspondientes. Es empleado a estos fines por los protocolos de la capa de transporte. 3) Transporte: Es equivalente a lo definido para la capa de transporte del modelo OSI. Protocolos de esta capa, tales como TCP y UDP, tienen como función manejar los datos y proporcionar la fiabilidad necesaria en el transporte de los mismos. 4) Aplicación: Corresponde a las capas OSI de aplicación, presentación y sesión. Es la la encargada de enlazarse con la capa de transporte. El modelo TCP/IP fue creado en la época de los setenta, siendo parte de la investigación del DARPA sobre la conectividad de diferentes tipos de computadoras y redes. Debido a que se emplearon fondos públicos para desarrollar TCP/IP, los estándares son no propietarios; esto es, que nadie tiene derechos exclusivos de uso.[1] i) Protocolo TCP: El protocolo de transmisión de datos (TCP) es usado como un protocolo altamente confiable entre computadores dentro de una red de comunicación paquetizada, así como en los sistemas interconectados de dichas redes. Para permitir que muchos procesos dentro de un mismo host o servidor usen las facilidades de comunicación TCP de forma simultanea, el protocolo provee un juego de direcciones o puertos dentro de cada host. Un socket es la unión de una dirección de red, un número de puerto y un protocolo de Red. Cuando dos procesos desean comunicarse, deben primero inicializar una conexión TCP, al hacerlo en cada host se crea un socket TCP. Un par de socket identifica cada conexión una forma única permitiendo una comunicación confiable entre ambos equipos [21]. j) Objetos Los lenguajes de programación orientados a objetos (C++, Java, etc.) suponen una nueva forma de entender la programación. La orientación a objetos implica realizar un esfuerzo por abstraer cuáles son los elementos esenciales del sistema y los aspectos fundamentales de los mismos. Las partes esenciales se pueden traducir como los objetos y los aspectos que los describen son las propiedades y los métodos. 10 CAPITULO 2. FUNDAMENTOS TEÓRICOS Un objeto tiene tres partes claramente diferenciadas [16]: k) a) Nombre como identificador del objeto. b) Atributos o propiedades, que contienen la información del objeto. c) Métodos que definen las operaciones que el objeto puede realizar. Lenguaje de Consulta Estructurado (SQL) SQL es un método basado en un potente lenguaje, para organizar, administrar y consultar datos almacenados en una computadora. SQL o "Structured Query Language".por sus siglas en inglés, está definido en torno al modelo de bases de datos relacionales, basado en el álgebra relacional, esto le da a SQL ventajas que lo imponen como el sistema de mayor aceptación. Algunas de las ventajas son [7]: 1) Marco teórico sólido, fundamentado en el álgebra relacional 2) Simplicidad de conceptos (tablas, líneas, columnas, etc.) 3) Definición de vínculos en la consulta 4) Fácil y rápido aprendizaje 5) Arquitectura cliente-servidor 6) Integración con cualquier lenguaje de programación 7) Estandarización Sin embargo no todos los manejadores de bases de datos que indican que usan SQL como lenguaje nativo, son 100% compatibles con las especificaciones de SQL, pudiendo presentarse ciertos problemas de compatibilidad al momento migrar bases de datos de un sistema a otro. l) COM (Component Object Model) Cuando Microsoft estaba desarrollando sus primeros sistemas operativos Windows se encontró con un problema, necesitaban poder insertar gráficos de una de sus aplicaciones (Microsoft Graph) en otra de ellas (Microsoft PowerPoint). En 1991 diseñaron un protocolo mediante el cual en un documento podrían insertarse objetos mantenidos por programas distintos en los que se estaba editando. El protocolo se llamaba OLE 1.0 y se basaba en el paso de mensajes y el uso de memoria global compartida. El resultado fue realmente malo, no sólo por la fragilidad del sistema sino también por la complejidad de la realización de componentes. La siguiente versión de OLE fue rescrita desde cero y dio origen al COM. Las nuevas versiones que realizó Microsoft ampliaron el modelo para poder usar los componentes dentro de una red de computadores (DCOM o Distributed COM) [8]. COM viene incluido como parte de Windows, más aún, muchas partes de Windows son objetos COM. Si el programador 11 CAPITULO 2. FUNDAMENTOS TEÓRICOS quiere realizar una extensión del sistema operativo, tendrá que realizar un objeto COM. El desarrollo de un Sistema Operativo lo realizan multitud de personas, encontrar un modelo que se adapte al desarrollo global del sistema es difícil. La programación orientada a objetos es una alternativa. COM realiza esta programación a nivel de programas binarios, en contra de lo que suele ser normal. Es decir, no hace falta tener los códigos fuentes del objeto para poder usarlo. Sin embargo, la principal desventaja del COM es su complejidad. A menudo los objetos COM se hallan encapsulado dentro de DLLs (Librerías de Enlace Dinámico). El uso de DLL implica ciertas ventajas entre las cuales están [12]: 1) Minimizar el uso de memoria de programas al existir sólo una copia de las funciones en memoria 2) Las actualizaciones de las librerías realizan mejoras sobre todos los programas que las usan; aunque esto puede tornarse en un inconveniente, pues todas las nuevas versiones deben de ser 100% compatibles con las anteriores 3) Se pueden usar y escribir funciones desde distintos lenguajes de programación. DCOM (Distributed COM) m) El Modelo de Objeto Componente Distribuido es un juego de conceptos e interfaces de programa de Microsoft en el cual un programa cliente pueden solicitar servicios de objetos de programa servidores en otros computadores dentro de una red. El DCOM también puede funcionar dentro de una empresa o en redes distintas de la Internet pública. Usa protocolos TCP/IP y HTTP, este último es un protocolo basado en petición y respuesta en un esquema cliente servidor, es el método principal para obtener información de la web El DCOM viene como parte de NT 4.0 y es una actualización gratuita para Windows 95. El DCOM reemplaza la Automatización Remota del OLE (OLE Remote Automation).[12] El DCOM es en lo general equivalente a una Arquitectura de Intermediación de Solicitud de Objetos Comunes (Common Object Request Broker Architecture, CORBA) por cuanto proporciona un juego de servicios distribuidos. El DCOM es la aproximación de Microsoft a un ambiente de programas y objetos de datos para toda una red. La CORBA es patrocinada por el resto de la industria de tecnología de la información bajo los auspicios Administración de Objetos (Object Management Group OMG). del Grupo de 12 CAPITULO 2. FUNDAMENTOS TEÓRICOS n) XML: XML es una forma restringida de SGML (lenguaje de marcas estándar generalizado, Standard Generalized Markup Language, ISO8879) desarrollada por un grupo de trabajo bajo los auspicios del Consorcio World Wide Web (W3C) en 1996. XML es un metalenguaje que permite diseñar un lenguaje propio basado en etiquetas para múltiples clases de documentos. Los documentos XML se componen de unidades de almacenamiento llamadas entidades (entities), que contienen datos analizados (parsed) o sin analizar (unparsed). Los datos analizados se componen de caracteres, algunos de los cuales forman los datos del documento y el resto forman las etiquetas. Las etiquetas codifican la descripción de la estructura lógica y de almacenamiento del documento. Cada aplicación de computadora usa sus propios tipos de datos y los almacena en un formato interno propio. La comunicación entre aplicaciones es una operación compleja que requiere llegar a un acuerdo acerca del formato de la comunicación. Si las aplicaciones se ejecutan en plataformas de hardware y de sistema operativo diferentes, la situación se hace aún más compleja. El lenguaje de marcación extensible (XML) alivia esta situación al proveer un formato autodescriptivo para expresar datos semiestructurados en formato de texto puro, compatible con la mayoría de los sistemas informáticos [24]. 2.3 Sinaut Spectrum El sistema SCADA de Enelven es el Sinaut Spectrum® de Siemens y tiene como función la supervisión y control de la red eléctrica del Estado Zulia, a través de la medición remota de las variables siguientes: voltaje, corriente, estado de interruptores, estado de seccionadores, posición de los TAP Changer de los transformadores, etc. Es capaz de mantener una base de datos con valores de importancia para la industria eléctrica como lo son: potencia activa, potencia reactiva, kilovatios/hora (suministro/consumo), armónicos, distorsión total armónica (THD) y factor de potencia. Existen 54 subestaciones eléctricas (S/E) interconectadas con el SCADA, en cada una de ellas funciona una RTU que actúa como servidor, es decir que provee la información de los procesos que allí se llevan a cabo. El sistema SCADA hace peticiones de monitoreo y control a todas y cada una de las subestaciones. El medio de comunicación entre el Centro de Control y las subestaciones es heterogéneo y está ligado a la topología de la red de comunicaciones de Procedatos. CAPITULO 2. FUNDAMENTOS TEÓRICOS 13 El sistema Sinaut Spectrum es más que un sistema SCADA, es también un Sistema de Manejo de Energía (EMS) cuya arquitectura de hardware utiliza Solaris como sistema operativo, principalmente debido a la orientación multiusuario y multitareas del mismo. Un computador o equipo que contenga programas del sistema Sinaut Spectrum es conocido como servidor. Estos servidores se intercomunican entre sí, vía TCP/IP a través de una LAN/WAN Ethernet. Las aplicaciones del sistema están distribuidas entre varios servidores los cuales se dividen las diferentes capacidades, las funciones que demandan tiempos de procesamiento prolongados están físicamente separadas de aquellas que requieren comportamiento en tiempo real, garantizando el óptimo desempeño y evitando interferencia entre ambas categorías. Todos los servidores utilizan ciertas funciones básicas comunes y además de estas se reparten tareas específicas que los diferencian entre sí. Esta distribución organiza las funcionalidades principales del sistema en servidores separados, mientras mantiene la funcionalidad básica necesaria en cada uno de los servidores, las funcionalidades distribuidas comprenden [3]: a) Servidor maestro de fuentes del sistema, SDM b) Administrador de bases de Datos Estática, ADM c) Administrador de bases de Datos dinámica, SCADA, RTC (Real Time Control) d) Interfaz de Usuario, UI e) Adquisición de Datos, DS f) Manejo de Data Histórica y Futura, RDB g) Aplicaciones de temporizaron de Tareas y Aplicaciones de Potencia APP2 h) Aplicaciones de Red, APP1 i) Simulador de Entrenamiento de Despachadores, DTS j) Sistema Básico, TODOS LOS SERVIDORES El sistema básico comprende: a) Sistema Operativo b) Manejo de Red de Computadores TCP/IP - CNM. c) Base de Datos Operacional. d) Comunicación entre procesos. SOFTBUS. e) Utilidades y herramientas para monitoreo. 14 CAPITULO 2. FUNDAMENTOS TEÓRICOS f) Manejo de Errores. El sistema Sinaut Spectrum ofrece dos métodos de redundancia. El primero consiste en el método HOT / STAND-BY, en el cual cada servidor tiene su par con la misma funcionalidad, y ambos actualizan de una forma simultánea y constante su base de datos de procesos; en caso de una falla del servidor principal (PC) su par de respaldo (SB) toma inmediatamente el control de la funcionalidad en cuestión, a este proceso se le denomina “changeover”. El otro método de redundancia consiste en tener uno o varios servidores sin una funcionalidad definida denominados SPARE, en caso de una falla en un servidor, dicho SPARE se reinicia con la configuración del que falló, este método no ofrece la misma disponibilidad que el HOT / STAND-BY, ya que el tiempo de recuperación es mucho mayor. Es posible utilizar una combinación de ambas configuraciones dependiendo de las necesidades de la empresa. Los servidores que comprenden el sistema Sinaut Spectrum son [6]: a) Interfaz de Usuario (UI): Se utiliza para la visualización y el control de la red eléctrica; es capaz de desplegar la información de la red y de aceptar entradas del usuario como comandos o modificaciones a la red. b) Manejador de Datos (SDM): Es el maestro de la Base de Datos, su labor principal es actualizar los otros servidores en el momento de su inicio y cuando se producen cambios en la base de datos estática. c) Comunicador en Tiempo Real (RTC): Realiza el procesamiento y contiene la base de datos operacional (ODB) del Sinaut Spectrum. Las siguientes funciones las provee el RTC: Procesamiento de Datos: Procesamiento de información recibida a través del DAS, monitoreo de cambios, distribución de datos a otros subsistemas, escritura en la base de datos operacional. Ofrece también procesamiento de mensajes, de valores analógicos, de contadores y de combinaciones de datos. Control Supervisorio: Maneja peticiones de los operadores para controlar dispositivos de potencia, o para cambiar las condiciones de operación de los mismos dispositivos. Soporta órdenes de secuencias de operaciones, ordenes simples de control, establecimiento de valores de consigna. Manejo de procedimientos de Operaciones: Consiste en un sistema para generar, guardar, modificar y ejecutar trabajos de control con ordenes supervisorias. 15 CAPITULO 2. FUNDAMENTOS TEÓRICOS Administración de Energía: Provee la capacidad de recolectar y guardar datos sobre cargas, generación e intercambio de Energía en el sistema periódicamente. Distribución de Carga: Maneja estrategias de Emergencia, prioridad de la carga, procesamiento de casos de operación. d) Análisis de la Red (APP1): Incluye todo el software para el análisis de la red en tiempo real. Tiene funciones como procesamiento y estimación de estados, Bus scheduler, Reducción de la Red, cálculo del factor de potencia, cálculo de corto circuito, evaluación de contingencia. e) Aplicaciones de Potencia (APP2): Provee funcionalidad de aplicaciones de potencia como monitor de reserva, despacho económico, cálculo de pérdidas de Wheeling, costos de producción, predicción de carga a corto plazo. f) Subsistema de Adquisición de Datos (DAS): Es la interfaz de proceso en tiempo real con las Unidades Terminales Remotas (RTU), tiene una base de datos de proceso actualizada, consiste de un servidor DS y hasta cuatro ( 4 ) interfaces de telecontrol (TIF) que se comunican utilizando el protocolo serial IEC-101. El DS se encarga del procesamiento de los datos entrantes, comparación con datos anteriores, pre-procesamiento de valores analógicos, procesamiento de datos salientes, comandos directos, procesamiento del tiempo y de errores, supervisión de los canales de comunicación. La base de datos del Sinaut Spectrum es una base de datos distribuida, cada uno de los servidores pertenecientes al sistema tiene su propia copia de la base de datos para reducir los tiempos de acceso [10]. Hay dos tipos principales de datos: estáticos y dinámicos, los estáticos son todos aquellos que tienen que ver con la descripción del sistema y de la red eléctrica; mientras que los dinámicos son aquellos que vienen del proceso en si, es decir, los estados actuales de los interruptores, valores de corriente, potencias, tensiones, etc. a) Base de datos estática: Esta base de datos es manejada por el servidor ADM y contiene la información sobre la topología de la red, los elementos que la componen y cómo están 16 CAPITULO 2. FUNDAMENTOS TEÓRICOS interconectados entre sí, es decir, toda la descripción de la red eléctrica está almacenada en esta base de datos [10]. Se consideran como elementos de la red eléctrica los dispositivos de potencia existentes en una S/E. Todo elemento en la red eléctrica de Enelven está identificado unívocamente en el sistema por una dirección tecnológica de 4 campos, un 5to campo identifica las diferentes informaciones asociadas a ese elemento como pueden ser: voltajes, corrientes, potencias, etc. Por lo tanto, cualquier dato de proceso en el sistema está identificado por una dirección tecnológica de cinco (5) campos como se presenta en la Figura # 2-5. Los campos que describen una dirección tecnológica son: 1) B1: Corresponde a la Subestación o ubicación geográfica. 2) B2: Corresponde al nivel de tensión. 3) B3: Corresponde a la topología eléctrica. 4) Elem: Corresponde al elemento de la topología al que se esta refiriendo. 5) Info: Corresponde a la característica del elemento que se esta estudiando. B1 B2 B3 Elem Info D.BOS Subestación Don Bosco 24ST 24 KVolt. ZAPAR Línea que alimenta Zapara CB1 Interruptor 1 STATUS Abierto / Cerrado Figura # 2-5 Dirección tecnológica También existe una base de datos de despliegues, en la cual se mantiene la información sobre los planos de la red eléctrica de Enelven en varios niveles. El más alto de estos niveles es el plano de sistema, el cual comprende una vista de la interconexión entre las subestaciones, un nivel por debajo se encuentra el plano de 17 CAPITULO 2. FUNDAMENTOS TEÓRICOS red, en el cual se pueden observar diferentes partes de la subestación y algunos elementos básicos, luego está el plano de subestación en el cual se observan los elementos eléctricos que la comprenden. Está también el nivel de detalle en el cual se pueden observar detalles de los elementos básicos como lo son los IED [10], sin embargo este tipo de plano no se utiliza en ENELVEN. b) Base de Datos Operacional (ODB) o de Proceso Esta base de datos es manejada por el servidor RTC, quien se encarga de actualizar la ODB en los otros servidores, por ejemplo en las Interfaces de Usuario (UI). En la ODB se almacenan los datos relacionados con el proceso como voltajes, corrientes, estado de interruptores, potencia, flicker, etc [10]. Todo elemento tiene una dirección física real en la red eléctrica, a esta dirección física, se le asigna una dirección tecnológica con la que posteriormente será tratado el elemento en el sistema. Esta dirección física viene formada por: 1) Link Address: Dirección de Canal Lógico, especifica un grupo de RTU. 2) Common Address: Especifica una determinada RTU dentro del grupo. 3) Tag Address: Dirección de Punto, especifica el elemento y la información que se transmitirá. El conjunto de estas tres direcciones identifican un elemento en la red; sin embargo, también es necesario identificar por cual canal del TIF está llegando. Esta dirección viene dada por: 1) Tarjeta: Especifica en que tarjeta del TIF está llegando la información. 2) Canal de Tarjeta: Especifica el canal de la tarjeta por el cual llega la información. c) SOFTBUS: Para garantizar la correcta y eficiente comunicación, el Sinaut Spectrum utiliza un sistema llamado SOFTBUS diseñado por Siemens específicamente para el Sinaut Spectrum. Toda la comunicación entre los servidores del Sinaut Spectrum se hace a través de este sistema. CAPITULO 2. FUNDAMENTOS TEÓRICOS 18 El Softbus es un sistema de comunicación entre procesos, donde algunos ofrecen servicios y otros los utilizan, la petición y entrega de servicios en el Sinaut Spectrum se realiza exclusivamente a través de él. El Softbus está presente en cada uno de los servidores del sistema, es el que hace posible la manera distribuida de funcionar del Sinaut Spectrum ya que permite la comunicación entre procesos independientemente de la localización física dentro del sistema haciendo uso de la red LAN. El softbus está implementado como un protocolo de transporte ubicado sobre la capa 4 del modelo OSI, embebido dentro del TCP/IP. Cada servidor conectado al sistema SCADA debe estar ejecutando en todo momento el soporte del SOFTBUS. Dicho demonio de comunicación se conoce con el nombre de BULS y está activo en todo momento en cada uno de los servidores. La comunicación entre servidores debe pasar primero por este demonio quien se encarga de direccionar al servidor destinatario. En la Figura # 2-6 se muestra el flujo de datos a través del Softbus, los números en la figura indican la secuencia del flujo. Figura # 2-6 Diagrama de Distribución de los Datos [10]. El programa A envía una comunicación, esta es interceptada por BULS, el cual se encarga de localizar el servidor destinatario del mensaje y enviarlo. En el destino el BULS recibe el mensaje y lo entrega al destinatario (Programa B y C en este caso). El remitente y el destinatario pueden en la práctica ser el mismo servidor. SOFTBUS tiene una serie de reglas y características que hacen posible y eficiente su funcionamiento [6]: a) Todos los programas se comunican a través del softbus, el sistema tiene un poderoso mecanismo de manejo de buffer para las colas de mensajes. CAPITULO 2. FUNDAMENTOS TEÓRICOS 19 b) No sólo programas sino librerías pueden enlazarse independientemente de su ubicación, esto hace que el código sea completamente portable dentro del sistema. c) Todo programa al iniciar debe subscribirse al softbus para poder utilizarlo y debe tener un número llamado ObjectID, que lo identifica unívocamente dentro del sistema; sin embargo, un mismo objeto puede subscribirse a varias direcciones de softbus y recibir a través de todas ellas. Igualmente pueden estar subscritos varios programas a una misma dirección y recibir todos a través de la misma. d) Softbus no manipula ni interpreta la información, éste solamente se encarga del envío de los mismos. e) El SOFTBUS, al igual que el UNIX, maneja una serie de señales; las cuales son mensajes que no van a la cola, sino que son leídos inmediatamente por el destinatario, es decir el proceso es parecido a una interrupción por software. f) Todo programa debe escuchar por lo menos las señales de initialization o inicialización de programas o recursos, lifecheck o verificación el estado de ejecución de un programa y la señal Kill o terminar ejecución del programa. 2.4 Proceso de Recolección de datos en una Subestación Eléctrica La mayoría de la subestaciones de ENELVEN están concebidas para trabajar sin requerir personal para su supervisión local. El control y la supervisión se hacen de forma remota desde el centro de control Caujarito. Para lograr esto los dispositivos electrónicos inteligentes (IED) se conectan a las unidades terminales remotas (RTU). Estas RTU están programadas para responder a las peticiones de interrogación recibidas desde el Centro de Control. ENELVEN posee 54 subestaciones distribuidas en toda la geografía del estado Zulia. La tecnología presente en la mayoría de ellas es la SICAM SAS de Siemens, donde la RTU utilizada es un controlador lógico programable (PLC) modelo S7/400 de Siemens específicamente diseñado para la automatización de S/E. Si bien existen algunas S/E donde se utilizan RTU Siemens – Landis, Siemens – Empros, GE – Harris y Sistemas de control local (HMI, ABB y Wonderware) estas no serán consideradas en esta descripción ya que representan menos de un 5% del total existente en la corporación. 20 CAPITULO 2. FUNDAMENTOS TEÓRICOS Profibus SICAM DNP 3.0 SAS IEC-103 Procesador COM CO IEC-101 AI 32 DI 16 32 COM: Módulo de Comunicaciones DI: Entradas Digitales CO: Salida de Comandos AI: Entradas Analógicas. IEC-101 Centro de Control Figura # 2-7 Diagrama de una RTU SICAM SAS. La SICAM SAS posee una arquitectura modular, donde podemos encontrar la fuente de alimentación, procesador central, procesador de comunicaciones, tarjetas de recolección de datos y de ejecución de comandos. En la Figura # 2-7 se muestra la estructura de una RTU SICAM SAS y en la Figura # 2-8 se observa una foto real del dispositivo señalando los diferentes módulos. En este caso el módulo de entradas analógicas no está presente. Figura # 2-8 RTU SICAM SAS Los IED se conectan al módulo central, y al de comunicaciones a través de protocolos específicos como los que se enumeran a continuación: a) IEC 61870-5 -101 b) DNP3.0 21 CAPITULO 2. FUNDAMENTOS TEÓRICOS c) IEC 61870-5-103 d) Profibus e) Modbus f) LON g) SPA h) ADLP 80 En la Figura # 2-9 se puede apreciar un esquema de conexión típico de una S/E. En ésta los sensores toman la medición y adecuan la señal para que sea procesada por el IED correspondiente. La RTU interroga periódicamente a los IED con la finalidad de obtener los datos que luego retransmitirá al centro de control. La comunicación entre las subestaciones y el Centro de Control se realiza utilizando el protocolo serial IEC-101. Entre los medios físicos utilizados encontramos: a) Enlaces de Microondas Punto a Punto b) Enlaces de Microondas Punto Multipunto c) Fibra Óptica d) Enlaces Ópticos Inalámbricos Al Centro de Control IED IED R.T.U. SICAM SAS IED IED IED Adecuador de Señal Adecuador de Señal Adecuador de Señal Dispositivo Dispositivo Dispositivo Potencia de de Potencia Figura # 2-9 Diagrama de conexiones de una S/E Potencia de 22 CAPITULO 2. FUNDAMENTOS TEÓRICOS El medio físico utilizado es transparente para el SCADA, la división de telecomunicaciones de la corporación se encarga de entregar un canal por cada RTU. El esquema de comunicación entre las RTU y el centro de control es del tipo MaestroEsclavo donde la interfaz de telecontrol (TIF) interroga a cada una de las RTU para que transmitan los datos relevantes que se hayan recolectado. El TIF funciona como puerta de enlace entre las S/E y el centro de control. Se comunica con el Data Server (DS) del Sinaut Spectrum a través del protocolo serial X25, este servidor, una vez que recibe los datos los convierte a unidades de ingeniería, actualiza su base de datos operacional (ODB) y los envía al RTC utilizando mecanismos proporcionados por el Softbus. El RTC se encarga de la distribución de los datos dentro del sistema, actualizando la base de datos de proceso de cada uno de los servidores en el orden señalado por los números en la Figura # 2-10. 4 R.T.C. U.I. 3 Red LAN 2 5 D.A.S. S.D.M. Power Applications 1 R.T.U. Figura # 2-10 Diagrama de la distribución de los datos. En el RTC se ejecutan ciertas funciones de control y las acciones necesarias se toman enviando comandos a los dispositivos de campo, estos comandos se envían igualmente a través del DS y el TIF. 23 CAPITULO 2. FUNDAMENTOS TEÓRICOS Utilizando la base de datos estática y la de despliegues se hace una representación unifilar de toda la subestación como la mostrada en la Figura # 2-11, a su vez se utiliza la base de datos de proceso para observar las características de los elementos como: voltajes, potencias, corrientes, etc. Si un operador o despachador desea enviar un comando lo hace a través de este despliegue, para él es transparente el proceso interno que esto requiera. TRINI T MIRAN 32.6 MV 11.7 MVAr 34.6 MVA T K-203 1.3 MV 4.5 MVAr 4.7 MVA K-103 K-205 K-105 RR RR K-208 K-104 K-138 99.1 % 136.8 KV BARRA 2 K-134 99.1 % 136.8 KV BARRA 1 K-130 K-114 K-218 T-2 RT 18.5 4.5 19.8 -1 2 MV MVAr MVA LTC Tap Fijo T-1 RT 18.5 4.5 19.8 -1 2 MV MVAr MVA LTC Tap Fijo C-180 C-280 auto auto 24.0 C-288 24.0 101.0 % 24.1 KV C-138 TXA BARRA 2 C-134 C-184 100.6 % 24.0 KV TXA BARRA 1 C-130 C-1008 C-808 C-604 C-504 C-404 C-1005 RR RT C-805 RR RT C-605 RR RT C-505 RR RT C-405 RR RT C-1003 C-803 T 8.8 MVA T 6.7 MVA ZAPARA COTORRERA C-603 T 7.3 MVA HOSPITAL COROMOTO C-503 T 6.7 MVA VIRGINIA C-403 T 4.3 MVA COSTA VERDE Figura # 2-11 Diagrama Unifilar Subestación Don Bosco Si se modifica la red eléctrica, el mecanismo que se debe seguir para incluir dicha modificación en el sistema es completamente manual. Se debe incluir el cambio en los parámetros de la RTU de la subestación, de ser un elemento nuevo se debe asignar una dirección física, que corresponda luego a una dirección tecnológica como se mostró en el ejemplo de la Figura # 2-5. Después se debe hacer en el sistema la inclusión de los datos correspondientes como conexiones y ubicación. Por último, se realizan las modificaciones al diagrama unifilar de la subestación. Luego de realizar estas modificaciones es necesario activar el cambio en el sistema para colocarlo en funcionamiento. 24 CAPITULO 2. FUNDAMENTOS TEÓRICOS 2.5 Evolución de las comunicaciones en el sector eléctrico: Norma IEC-61850. Al igual que en otros sectores de la industria, en los sistemas eléctricos las comunicaciones de datos han evolucionado para aprovechar las ventajas que las nuevas tecnologías ofrecen. Se puede dividir esta evolución en cuatro etapas. Esta división se muestra en la Tabla # 2-2 junto con una breve descripción general de las mismas [16]. Etapa Descripción Sistemas individualidades de mecanismos comunicaciones de interconexión patentados. de El sistemas desarrollo entre de distintos fabricantes era lento, necesitaba personal muy especializado y requería equipos específicos para cada desarrollo. Desarrollo de las primeras normas producto de las especificaciones internas de las empresas dominantes del sector: normalizaciones a nivel físico IBM y ATT. Nace y se masifica el RS-232. Se hace posible trasmitir cadenas de bits sin errores entre computadores. Sin embargo, si bien en las aplicaciones reales los bits eran trasmitidos perfectamente, los mensajes enviados por un computador no necesariamente eran entendidos por otro. Es posible que equipos de distintos fabricantes se entiendan. En el sector eléctrico se desarrolla el protocolo IEC-60870 con un gran impacto benefactor. A pesar de estos resultados, frecuentemente se requería el uso de programas de conversión de protocolos para protocolos interconectar equipos de distintos fabricantes. Las comunicaciones siguen siendo la causa más común de fallas al cambiar un equipo por uno diseñado por otro fabricante. El final de la etapa de los protocolos viene marcado por el desarrollo del modelo OSI y el protocolo TCP/IP. Se desarrolla los conceptos de Objeto, métodos y propiedades. La orientación a objetos implica realizar un esfuerzo por abstraer objetos cuáles son los elementos esenciales del sistema (objetos) y los aspectos fundamentales de los mismos (métodos y propiedades). Lo principal ya no es el diseño interno de los equipos o aplicaciones, sino cómo interactúan entre sí. Tabla # 2-2 Evolución de las comunicaciones en el sector eléctrico CAPITULO 2. FUNDAMENTOS TEÓRICOS 25 A la par que los sistemas de comunicaciones se desarrollaban, la programación fue también evolucionando para poder realizar programas cada vez más complejos. Con el nacimiento de la programación estructurada se sentaron las bases de una programación más metódica. Mediante el uso de módulos, funciones y tipos; estos lenguajes permitían estructurar mejor el código y los datos. Pero en esta misma generación surgieron nuevos paradigmas, uno de ellos es la programación orientada a objetos. Actualmente tenemos algunos lenguajes tan conocidos como C++ o Java. La novedad fundamental que va a traer la IEC-61850 es la descripción de los niveles superiores de abstracción de un sistema de comunicaciones para las subestaciones eléctricas. La norma habla de transformadores, seccionadores, sistemas de protecciones, etc.; en definitiva, se refiere a los objetos que se encuentran en una subestación y que necesitarán comunicarse entre sí. La norma no se limita a nombrarlos, al hablar de los transformadores por ejemplo, dice entre otras cosas, que un transformador de medida de corriente, tendrá la información de su modo de operación, de la corriente que está midiendo, de la calidad con que la está midiendo, del momento preciso de esa última medida, etc. La norma se esmera en indicar cuáles son las propiedades de ese objeto, sus características, así como también describe los métodos asociados, fijando o leyendo valores, devolviendo datos, etc. [24]. En la norma IEC-61850 confluyen el nivel superior de los sistemas de comunicaciones y el nuevo paradigma de la programación orientada a objetos. Desde la perspectiva de un sistema de comunicaciones se ve cómo la norma, apoyándose en el modelo OSI, proporciona la descripción detallada de lo que es el nivel más abstracto: la capa de aplicación. Esta descripción se da de forma sistemática, sin lugar a ambigüedades, mediante el empleo de una interpretación libre de las técnicas de programación orientadas a objetos. Las líneas eléctricas sirven para transportar la energía desde las centrales generadoras hasta los consumidores finales. Hay múltiples factores que aconsejan realizar uniones redundantes e interconectar las distintas redes, en unos casos por cuestiones técnicas como la confiabilidad y la estabilidad del sistema, en otros por cuestiones económicas. Sea por unas razones o por otras, el caso es que la situación actual es la de redes de distribución bastante tupidas en cada país, y con interconexiones débiles entre los distintos países [16]. En el caso del estado Zulia es posible observar esta situación, en donde las redes de transmisión y distribución si 26 CAPITULO 2. FUNDAMENTOS TEÓRICOS bien son todas manejadas por la corporación, poseen múltiples puntos de interconexión con otras empresas eléctricas a nivel nacional (EDELCA, CADAFE) y Organismos de Supervisión (OPSIS). En este escenario, son frecuentes los casos en los que distintos sistemas deben comunicarse para funcionar conjuntamente. Dentro de cada subestación hay elementos muy diferentes que también necesitan comunicarse entre sí. Como es usual que se empleen protocolos distintos, una gran parte de los costes de las compañías eléctricas deben ir destinados a integrar los distintos equipos y sistemas. Además, numerosos países han elaborado leyes que favorecen la entrada de múltiples competidores en las diversas áreas de proceso de distribución de energía eléctrica. Las compañías eléctricas ahora sumergidas en un entorno competitivo, tienen que mejorar forzosamente la eficiencia y reducir sus costos. En el caso particular venezolano es necesario mencionar también los efectos que tendrá la inminente entrada en vigencia de la Ley Orgánica del Servicio Eléctrico (LOSE), en cuanto a las exigencias de calidad de servicio y transparencia de la gestión entre otras. Todos estos factores obligan a conseguir una más fácil integración de los equipos de distintos fabricantes. Uno de los objetivos que pretende alcanzar la norma es la interoperabilidad. Se pretende por tanto que cuando equipos de distintos fabricantes se conecten entre sí, puedan comunicarse sin necesidad de realizar un trabajo adicional. Éste es un objetivo ambicioso, pero que ya se ha logrado alcanzar en otros ámbitos, es lo que en los computadores personales suele conocerse como “Plug & Play”. Por esta razón, en la norma, se identifican y describen las funciones típicas de los dispositivos, pero en ningún momento se pretende limitar las funciones que puede realizar un equipo. Es necesario enfatizar que el objetivo de la norma no es la intercambiabilidad sino la interoperabilidad entre los equipos, entendiendo a la primera como la posibilidad de intercambiar equipos de distintos fabricantes sin ningún tipo de reconfiguración. La intercambiabilidad tendería a limitar las funcionalidades que se pueden agregar a los IED en aras de garantizar la compatibilidad 100%, desestimulando la generación de nuevas tecnologías por parte de los fabricantes. En la IEC-61850 se define de forma precisa tanto el sistema de comunicaciones, como los elementos sobre los que se intercambia información (los motores, generadores, transformadores, líneas eléctricas, etc.) y sus propiedades. Esto, unido a CAPITULO 2. FUNDAMENTOS TEÓRICOS 27 un mecanismo flexible que permite que un objeto pueda dar una auto-descripción de sí mismo, permitirá por fin la interconexión automática entre equipos de distintos fabricantes. La norma logra así complementar el acuerdo sobre la sintaxis provisto por los protocolos, con un acuerdo sobre la semántica de los datos intercambiados. La norma incorpora numerosos elementos útiles para un sistema de comunicaciones en el ámbito de las subestaciones eléctricas [16]: a) Modelo para el establecimiento de conexiones y mecanismos de control de acceso. b) Intercambios de mensajes orientados a eventos. c) Capacidad de sustitución manual de los valores. d) Escalado de las medidas analógicas. e) Límites de banda muerta con los que disminuir las cantidades de datos a transmitir f) Marcas de tiempo en los datos transmitidos. g) Especificación de la calidad de los valores. h) Etiquetas. i) Establecimiento de las condiciones para la generación de informes automáticos. j) Secuencias de control, con sus números de secuencia correspondientes. k) Sincronización de tiempo. l) Generación de informes y registros. m) Transferencia de archivos. Importantes fabricantes, convencidos de la necesidad de la norma y de los beneficios que traerá, han decidido apoyarla. Se lanzaron varios proyectos, entre ellos el proyecto OCIS, un proyecto alemán desarrollado por los fabricantes ABB, ALSTOM y Siemens, la empresa de servicio alemana VEW y el instituto de investigación FGH. Este proyecto verificó que la norma servía para modelar satisfactoriamente los elementos de una subestación, y que Ethernet se puede emplear en la práctica, como bus de una subestación. ABB y Siemens llevaron a cabo una primera prueba [16], se comprobó el correcto funcionamiento mezclando equipos de los dos fabricantes y de este modo se verificó la interoperabilidad entre los mismos. La norma, con el título general de “Redes de Comunicaciones y Sistemas en las Subestaciones”, estará formada por un conjunto de documentos, divididos en diez partes. (Ver Tabla # 2-3). Las bases del sistema de comunicaciones se establecen en 28 CAPITULO 2. FUNDAMENTOS TEÓRICOS las partes 5 y 7-1. En estos documentos se da una descripción funcional mediante la presentación de los elementos fundamentales del sistema de comunicaciones. Estructura de la Norma IEC 61850 Parte (1) Introducción (2) Glosario (3) Descripción Introducción y resumen de las siguientes partes de la norma Glosario de términos específicos y definiciones propios de los Sistemas de Automatización de Subestaciones Requisitos generales de las redes de comunicación, se enfoca en Requisitos los generales servicios auxiliares (4) Gestión de sistemas y proyectos (5) Requisitos de las comunicaciones (6) Lenguaje de configuración (7) Estructuras de Comunicación en las subestaciones (8) SCSM – Mapeo a MMS requerimientos de calidad, condiciones ambientales y Se refiere a la gestión de: los procesos de ingeniería y las herramientas que los soportan, el ciclo de vida del sistema completo y sus IED, y el control de la calidad del SAS y sus IED Se identifican todas las funciones desempeñadas dentro del SAS, los modelos de dispositivo y sus requerimientos de comunicación Se describe el lenguaje de configuración relacionado con la configuración y parámetros de los IED, así como la descripción de los sistemas de comunicación, equipos de potencia y las relaciones entre los mismos Estructura básica del sistema de comunicaciones. Se subdivide en cuatro secciones que explican los principios básicos, la interfaz ACSI, las clases de datos y los nodos lógicos compatibles Especifica un método para intercambiar información a través de una LAN al mapear la ACSI a MMS. Se aplica al Bus de Estación (9) Esta sección se subdivide en dos secciones, las cuales se enfocan SCSM – Valores en métodos para trasmitir los valores muestreados utilizando Muestreados (10) Pruebas de ISO/IEC 8802-3 y enlaces seriales punto-multipunto. No ha sido publicada aun. Incluirá las pruebas requeridas para asegurar la compatibilidad de los equipos y aplicaciones. CAPITULO 2. FUNDAMENTOS TEÓRICOS 29 Conformidad Tabla # 2-3 Estructura de la Norma IEC-61850 [25] En la parte 7-2 se proporciona una definición más detallada del sistema de comunicaciones con la denominada ACSI. Esta descripción es a un nivel abstracto, mediante la definición exhaustiva de los objetos que componen el sistema de comunicaciones. Posteriormente, en las partes 8 y 9 se explica cómo aplicar estos conceptos abstractos para cada protocolo concreto mediante el uso del SCSM (Specific Communication Service Mapping). Las partes 7-3 y 7-4 continúan con la definición de objetos. En estos dos documentos se ha hecho un esfuerzo muy importante modelando los elementos de uso común en las subestaciones eléctricas. En concreto en la parte 74 se han desarrollado unos cien modelos, mediante el empleo de más de dos mil atributos. La parte 7-3 define los atributos más comunes que aparecen en multitud de objetos. La parte 6 cumple también una labor complementaria muy importante mediante la definición de un lenguaje de configuración. Este nuevo lenguaje, basado en XML, permite extender las definiciones de objetos que proporciona la norma, evitando así los inconvenientes de un modelo rígido [16]. El principal objetivo de este lenguaje es permitir el intercambio las descripciones de las capacidades de los IED y de SAS entre las herramientas de ingeniería de los IED y aquellas herramientas del Sistema diseñadas por diferentes fabricantes [25]. La correspondencia entre la ACSI y los protocolos concretos de comunicaciones se establece en las partes 8 y 9. En concreto en la parte 8 se dan los detalles para el bus de la subestación. Las partes 9-1 y 9-2 proporcionan una nueva correspondencia, esta vez para el bus de proceso. La captura de medidas en tiempo real, que hasta ahora venía haciéndose de forma analógica, se propone pasar a realizarla de forma digital, empleando como tecnología base Ethernet, y fundamentalmente con fibra óptica. En concreto la parte 9-1 propone organizar la comunicación mediante enlaces unidireccionales, mientras que en la parte 9-2 se plantea la clásica arquitectura en bus [16]. 2.6 OLE for Process Control (OPC) OPC define un estándar de intercambio de información y las reglas de negociación entre dispositivos de diferentes tipos. Así cualquier dispositivo que posea un software de control de tipo OPC podrá conectarse con cualquier software cliente OPC, consiguiendo de esta manera una gran flexibilidad, conectividad, y la capacidad 30 CAPITULO 2. FUNDAMENTOS TEÓRICOS de añadir diferentes dispositivos a un software de control y adquisición de datos sin tener que modificar el mismo. Tradicionalmente, cada diseñador de software necesitaba construir su propia interfaz o driver para poder intercambiar información entre sus equipos. Esto implica: a) Multiplicación de esfuerzos: los programas necesitan un driver específico para trabajar con un determinado equipo. b) Falta de consistencia entre drivers: hay características del hardware, que no todos los fabricantes de drivers deciden soportar. c) Cambios en el hardware: hacen que los drivers queden obsoletos. d) Conflictos de acceso: generalmente, dos programas no pueden acceder simultáneamente al mismo dispositivo puesto que poseen drivers independientes. En la Figura # 2-12 se observa como OPC permite lograr una disminución de las interfaces necesarias para la comunicación entre equipos y aplicaciones. Sin OPC se requiere desarrollar un driver distinto para cada aplicación. Los fabricantes de hardware no pueden desarrollar un driver eficiente utilizable por todos los clientes debido a las diferencias de protocolos entre los mismos [18]. OPC elimina esta restricción definiendo una interfaz común y de alto rendimiento que permite que el trabajo sea hecho una sola vez y reutilizado en cualquier aplicación de control y monitoreo [19]. Figura # 2-12 Disminución de Drivers específicos CAPITULO 2. FUNDAMENTOS TEÓRICOS 31 OPC se basa en la tecnología OLE/COM (Object Linking and Embedding / Component Object Model) de Microsoft. Esta es la tecnología que permite que componentes de software (escritos en C y C++ por expertos en un sector) sean utilizados por una aplicación (escrita en Delphi o Visual Basic para otro sector). De esta forma se desarrollan componentes en C y C++ que encapsulan los detalles de como acceder a los datos de un dispositivo, de manera que quienes desarrollen aplicaciones empresariales puedan escribir código en Visual Basic que recoja y utilice datos de planta. El diseño de las interfaces OPC soporta arquitecturas distribuidas en red. El acceso a servidores OPC remotos se hace empleando la tecnología DCOM (Distributed COM) de Microsoft [18]. El trabajo del estándar OPC actualmente está avalado por más de las 200 compañías y asociaciones importantes del sector como por ejemplo Microsoft, CERN, Compaq o National Instruments, lo que garantiza un soporte constante y continuas revisiones del estándar. Al desarrollar un sistema basado en este protocolo estándar se logra la interoperabilidad de los sistemas físicos y lógicos de monitoreo. Esto permite a su vez una gran independencia y generalidad, ya que cualquier dispositivo que utilice este protocolo podrá intercambiar información con este sistema. Esto libera a las empresas del compromiso de tener que utilizar el software propio del diseñador de los elementos físicos, lo que supondrá un abaratamiento sustancial de los costes y una disminución de la dependencia de los fabricantes. Dentro de la tecnología OPC existen varias subtecnologías dependiendo del tipo y la forma de intercambiar información con los servidores OPC [19]. a) OPC AL (OPC Alarms and Events): Suscripción a alarmas y eventos de un servidor OPC. b) OPC DA (OPC Data Access): Intercambio de datos entre servidor/cliente OPC. c) OPC HDA (OPC Historical Data Access): Acceso histórico a datos OPC. d) OPC XMLDA (OPC XML Data Access): Acceso en formato XML y utilizando SOAP y servicios Web a servidores OPC. Esta es la última apuesta por esta tecnología por parte de la fundación OPC y parece que será la predominante en poco tiempo. 32 CAPITULO 2. FUNDAMENTOS TEÓRICOS OPC y sobre todo la parte de Acceso de Datos de OPC, ha sido diseñado para proveer a los usuarios de un medio de integrar el hardware y el software. OPC DA es un mecanismo de comunicación del tipo cliente/servidor. Un servidor de Acceso de Datos típicamente tendrá acceso a dispositivos usando los drivers apropiados y/o protocolos (variables según el fabricante de cada dispositivo). Utilizando la interfaz OPC el servidor hace posible que varias aplicaciones clientes accedan a la información contenida en los dispositivos de una manera uniforme. Un servidor OPC se compone de tres objetos que se ajustan a la norma COM [19]: 1) El objeto servidor: contiene información sobre la configuración del servidor OPC y sirve de contenedor para los objetos de tipo grupo. 2) El objeto grupo: sirve para organizar los items que lee y escribe un cliente determinado. Un cliente puede crear varios grupos en un mismo servidor. Un grupo puede ser público y ser compartido por varios clientes OPC. 3) El objeto ítem: representa conexiones a fuentes de datos en el servidor, no son las fuentes de datos en sí. Tiene asociados los atributos Valor (Value), Calidad (Quality) y Estampa de Tiempo (Timestamp). Los accesos a los items OPC se hacen únicamente a través de los grupos OPC. Estos objetos son representaciones lógicas que no tienen por qué coincidir con la implementación que se haga del servidor OPC. El acceso a los mismos se hace a través de dos posibles interfaces: la interfaz custom y la interfaz de automatización, que son lo único que ven los clientes OPC. La interfaz custom permite acceder directamente al servidor. Esta requiere que el cliente haya sido desarrollado en C o C++ utilizando las llamadas y procedimientos propios del modelo de objeto componente local (COM) o distribuido (DCOM). La interfaz de automatización se vale de una librería externa (Wrapper) para conectarse al servidor y permitir que un cliente desarrollado en leguajes que soporten interfaces de automatización (Visual Basic, Delphi, entre otros) pueda acceder a los datos presentes en el servidor. En la Figura # 2-13 se muestran las interfaces custom y de automatización, aquí las aplicaciones son clientes y hacen sus peticiones directamente o utilizando el wrapper de automatización. El servidor se encarga de solicitar los datos a los CAPITULO 2. FUNDAMENTOS TEÓRICOS 33 dispositivos físicos, crear un cache con los últimos valores recolectados y asociar estos valores a items previamente configurados. El cliente puede solicitar que los datos provengan de la fuente de su preferencia, sea ésta el cache o directamente de una nueva solicitud al dispositivo. Figura # 2-13 Arquitectura OPC El cliente OPC se encarga de ejecutar operaciones tales como: revisar servidores disponibles en un nodo, conectarse a un servidor, listar los items disponibles, validar los items, entre otras funciones descritas en las especificaciones de 34 CAPITULO 2. FUNDAMENTOS TEÓRICOS la interfaz OPC seleccionada. Debido a la organización de los datos inherente a los Figura # 2-14 Jerarquía del objeto de automatización OPC servidores OPC, al momento de ejecutar estas operaciones, el cliente debe crear objetos OPC que luego asociará a los objetos presentes en el servidor OPC. En la Figura # 2-14 se puede apreciar la jerarquía de los objetos OPC y en la Tabla # 2-4 se cita una breve descripción de cada uno de los objetos. Cada objeto posee una serie de métodos y propiedades, todos ellos están ampliamente explicados en el Data Access Automation Interface Standard [6]. OBJETOS OPC NOMBRE OPCserver OPCgroups OPCgroup OPCitems DESCRIPCION Instancia de un servidor OPC. Se debe crear un objeto OPCserver antes de poder obtener referencias a otros objetos. Contiene la colección OPCGroups y crea el objeto OPCBrowser. Colección contentiva de todos los objetos OPCGroup que un cliente haya creado dentro del ámbito del OPCServer al que la aplicación se haya conectado utilizando el método OPCServer.Connect() Instancia de un objeto OPCGroup. El propósito de este objeto es proporcionar el mecanismo para proveer servicios de adquisición de datos al objeto colección OPCitems al que hace referencia el objeto OPCgroup. Además mantiene la información sobre el estatus de dicha colección. Colección contentiva de todos los objetos OPCItem que el cliente haya creado dentro del ámbito del OPCServer y el objeto OPCGroup correspondiente. CAPITULO 2. FUNDAMENTOS TEÓRICOS OPCitem OPCBrowser 35 Objeto que mantiene la definición de la definición de un ítem, su valor actual, información de estatus, estampa de tiempo de la última actualización. Objeto que permite explorar los nombres de los items disponibles en la configuración del servidor. Solo puede existir una instancia de un objeto OPCBrowser por cada instancia de un objeto OPCServer. Tabla # 2-4 Descripción de los objetos OPC Cuatro mecanismos de comunicación han sido definidos para OPC DA: a) Llamadas síncronas (Synchronous calls) b) Llamadas asíncronas (Asynchronous calls) c) Refrescamiento (Refresh) d) Suscripción (Subscription) Cuando un cliente emite una petición de lectura síncrona, el servidor no libera el control al hilo que genera la petición hasta que los valores solicitados sean enviados. También están permitidas las peticiones de escritura síncronas. Si en cambio se emite una petición de lectura asíncrona, el servidor libera inmediatamente al hilo, siendo luego más tarde cuando envía, usando un camino de comunicación predefinido, los valores solicitados. También están permitidas las peticiones de escritura asíncronas. Las llamadas síncronas y asíncronas requieren que los clientes especifiquen la lista de los valores que tienen que ser leídos o escritos. Estos mecanismos funcionan por polling. Son usados cuando un cliente requiere de acceso a un dispositivo de forma periódica. El Refrescamiento y la Suscripción son mecanismos de callback. Son usados para tener acceso a sets predefinidos de datos en el servidor. El Refrescamiento es un mecanismo del tipo “pull” (halar), la Suscripción es tipo “push” (empujar). Cuando un cliente solicita un Refrescamiento, el Servidor OPC devuelve asincrónicamente los valores actuales del set de datos, usando un camino de comunicación predefinido. La suscripción es un mecanismo basado en eventos. Los servidores notifican a los clientes cuando los cambios significativos ocurren dentro de un set de datos a los que previamente se hallan suscrito. Es así como en un refrescamiento el cliente solicita (pulls) la data mientras que en la suscripción es el servidor quien envía (pushes) la data hacia los clientes. 36 CAPITULO 2. FUNDAMENTOS TEÓRICOS Las comunicaciones entre los componentes OPC DA y el comportamiento de un Servidor OPC pueden ser controladas por los clientes. A continuación se dará una descripción muy breve de los parámetros: a) Active (Activo): Tanto los grupos como los items tienen un atributo denominado "Active". Los clientes pueden habilitar (set) o dehabilitar (reset) este atributo. Al dehabilitarlo, el cliente declara que ya no esta interesado en un ítem OPC especifico o en cualquiera perteneciente al grupo OPC correspondiente. Así el Servidor OPC no devolverá más información sobre estos items cuando sea solicitada una lectura ni cuando se presenten cambios en ellos. El Servidor OPC igualmente puede dejar de leer los items de datos inactivos correspondientes en el dispositivo. b) Cache / Dispositivo: Se espera que un Servidor OPC tenga una copia local - un cache - de sus items de datos de proceso. Al momento de emitir una llamada de lectura o de refrescamiento, los clientes pueden especificar si los datos serán devueltos del cache o directamente de los dispositivos. Los siguientes tres atributos sólo afectan los mecanismos de suscripción. c) Enable (Permitir): Un cliente puede temporalmente, suspender el envío de callbacks de items suscritos relacionadas con un grupo. Esto puede ser controlado al habilitar o deshabilitar el atributo Enable de un grupo. d) Deadband (Banda muerta): Si un cliente no requiere recibir callbacks causadas por pequeñas variaciones de items con datos analógicos, puede especificar la variación mínima requerida para disparar los mensajes de callback OnDataChange. Este atributo es un porcentaje que se aplica a los items analógicos del grupo afectado. Los items OPC a su vez deberán tener definidos sus valores mínimos y máximos significativos. Al ajustar este atributo a 0, el cliente será notificado de cualquier cambio en la próxima actualización (de estar seteado el atributo "enabled"). CAPITULO 2. FUNDAMENTOS TEÓRICOS 37 e) Tasa de Actualización (UpdateRate): El atributo "UpdateRate" es especificado por el cliente. Esto determina la tasa en la cual los límites de banda muerta son comprobados dentro del servidor. Por lo tanto, los callback OnDataChange no se generaran más rápido que el valor fijado para este parámetro. Aunque la tasa de actualización es una petición de un cliente, los servidores pueden establecer valores máximos o mínimos, o incluso seleccionar un valor diferente, esto queda a criterio del fabricante. Al especificar una tasa de actualización de 0, los clientes pueden solicitar ser notificados tan pronto como una nueva información se hace disponible en sus items OPC seleccionados. Además, la tasa de actualización puede ser usada por el servidor para determinar cuan a menudo deben leerse los dispositivos. CAPÍTULO 3. PLANTEAMIENTO DEL PROBLEMA 3.1 Introducción: Debido a los esfuerzos que se están realizando en la corporación Enelven por implementar nuevos protocolos de automatización en las subestaciones eléctricas, Procedatos como brazo tecnológico de la corporación, siempre debe estar en la búsqueda de nuevas tendencias en el área de Automatización Eléctrica; con la intención de estudiar la factibilidad y ventajas de su implementación en la corporación. Ante la necesidad de la Corporación Enelven, cliente principal de la empresa, Procedatos inicia la búsqueda de formas de incorporar nuevas tecnologías al sistema SCADA existente. Antes de entrar en detalle sobre el desarrollo del proyecto es necesario comprender el problema existente en la empresa y la necesidad de resolverlo. En este capítulo se plantea dicho problema y se expone a su vez la justificación de desarrollar este proyecto. Luego se exponen los objetivos, tanto el general como los específicos. 3.2 Formulación del Problema. La división de planificación de la corporación recientemente ha iniciado estudios sobre la necesidad de llevar a cabo la migración de los esquemas de protección, monitoreo y supervisión en las subestaciones [2], con la finalidad de aprovechar los beneficios brindados por la norma IEC-61850 para el diseño de nuevas subestaciones, proporcionando compatibilidad con las ya existentes y facilitando la progresiva actualización de las mismas. La incorporación de esta norma hará posible un ahorro en gastos operativos por concepto de: ajuste, configuración y mantenimiento en un 60%, traslado a campo en un 20%. También se reducirán las dimensiones del cuarto de control en un 40%, y se verá una reducción en el impacto causado al ambiente [2]. Actualmente las subestaciones de Enelven utilizan el protocolo serial IEC60870-5-101 para comunicar estas con el sistema SCADA que posee la corporación en el centro de control. La interfaz de telecontrol está limitada a la utilización de este protocolo para la mencionada comunicación, es necesario actualizar el sistema SCADA para lograr la incorporación de nuevos protocolos, siendo de particular interés aquellos compatibles con la norma IEC-61850. 39 CAPITULO 3: PLANTEAMIENTO DEL PROBLEMA Es necesario realizar un estudio sobre las modificaciones que se deberán realizar tanto en el sistema SCADA como en los procesos de recolección de datos en las S/E en respuesta al proceso de migración propuesto por la división de planificación. El sistema SCADA actual representa una cuantiosa inversión para la corporación, y siendo relativamente nuevo se hace imprescindible prolongar al máximo su tiempo de vida útil en aras de proteger esta inversión. Por esta razón se hace necesario buscar la manera de expandirlo incorporando interfaces a nuevos protocolos de automatización utilizando vías de acceso al SCADA diferentes al TIF, para lograr esto es necesario estudiar el sistema de comunicación interno del Sinaut Spectrum llamado Softbus y así poder construir una interfaz capaz de lograr los objetivos de comunicación deseados. A continuación se presenta el objetivo general y los objetivos específicos del proyecto: Objetivo General Hacer un Análisis Técnico cualitativo de los distintos protocolos de última generación utilizados en la Automatización de Subestaciones Eléctricas. Estudiar la factibilidad de Integrarlos con el de Sistema de Manejo de Energía Sinaut Spectrum. Objetivos específicos 1) Hacer una documentación sobre los protocolos para automatización de Subestaciones eléctricas. Como IEC-61850, OPC, IEC 104, etc. 2) Realizar un estudio del protocolo de comunicación entre computadoras “SOFTBUS” (Utilizado por Sinaut Spectrum). 3) Realizar un planteamiento general de cómo aplicar la filosofía en una subestación eléctrica típica de ENELVEN. Obteniendo el mayor número de ventajas de los mismos. 4) Realizar un software basado en un PC, que permita la comunicación a través de uno de los protocolos de Subestaciones. (IEC-61850, OPC, IEC 104, etc.). 5) Realizar un software basado en Unix, que permita la comunicación con el protocolo Softbus. 40 CAPITULO 3: PLANTEAMIENTO DEL PROBLEMA 6) Realizar la integración comunicación fluida. entre ambos programas permitiendo una CAPÍTULO 4. DESARROLLO DEL PROYECTO 4.1 Introducción Este capítulo pretende dar una solución al problema planteado en el capítulo anterior. Luego de describir la metodología utilizada para la realización de cada una de las fases del proyecto, se presentan las ventajas y desventajas de los protocolos estudiados, se muestra cual fue el protocolo de automatización seleccionado para el desarrollo del proyecto, además de las razones por las cuales fue seleccionado. Se realiza un planteamiento sobre la implantación de la solución propuesta en una subestación eléctrica típica de ENELVEN. Luego de plantear la solución general se describe la realización y el funcionamiento de cada una de las interfaces necesarias para la implantación de dicha solución, se mostrará con diagramas de flujo el comportamiento de la interfaz al sistema SCADA Sinaut Spectrum a través del Softbus y el comportamiento de la interfaz ambientada en Windows con el protocolo mencionado, junto con la utilidad de configuración de dicha interfaz. 4.2 Metodología Para lograr desarrollar el proyecto exitosamente, es conveniente dividirlo en distintas fases con objetivos y lineamientos bien definidos para hacer luego posible su integración en un proyecto que satisfaga las expectativas y necesidades de la empresa. Para la realización de este proyecto es necesario diferenciar dos campos de acción; por una parte el campo de las subestaciones eléctricas y por otra el centro de control y el sistema SCADA; la comunicación entre el centro de control y las subestaciones representaría la integración de ambos. Fase 1: Familiarización con el Sistema SCADA y Subestaciones Eléctricas: a) Revisión Bibliográfica: Antes de poder decidir cual sería la mejor solución en cuanto a la implementación de nuevos protocolos en el sistema de automatización eléctrica de Enelven, es necesario comprender a fondo cuales son los sistemas y equipos involucrados en dicha implantación. Dicha comprensión se logra utilizando manuales técnicos, de usuario, de operación y de funcionamiento de dichos sistemas [6]. También se utilizan previos estudios de implantación de protocolos en subestaciones CAPITULO 4: DESARROLLO DEL PROYECTO 42 eléctricas, además de toda la información disponible en la Internet sobre sistemas SCADA y subestaciones eléctricas. b) Observación Directa y familiarización con los equipos: Es importante que la comprensión de los sistemas mencionados (Subestaciones Eléctricas y Sistema EMS Sinaut Spectrum) no se quede en una revisión teórica, es necesario el contacto directo con los mismos, y observar la actual configuración de los equipos de campo hecha para los propósitos específicos de la empresa. Todo esto con la finalidad de conocer el sistema en estudio lo mejor posible. Fase 2: Estudio de recientes protocolos de automatización eléctrica: Luego de familiarizarse con los equipos y sistemas a utilizar, se realizará un estudio comparativo de recientes protocolos de automatización, analizando las ventajas y desventajas que estos presentan al momento de comunicar información entre las subestaciones y el centro de control, así como las posibles modificaciones que el uso de estos nuevos protocolos impliquen realizar en el sistema SCADA. Fase 3: Elaboración de la propuesta: Después de haber analizado con detalle las ventajas y desventajas que traería a la empresa la implantación de los protocolos a estudiar, se debe elaborar un planteamiento general, proponiendo una solución a la problemática expuesta, identificando los pasos a seguir para lograr su implantación. Fase 4: Elaboración de un prototipo de interfaz: Esta fase comprende tres partes diferenciables: a) La elaboración de la interfaz al sistema SCADA EMS Sinaut Spectrum mediante la utilización del SOFTBUS, la cual debe ser desarrollada en un ambiente UNIX, ya que, cualquier software que desee utilizar los recursos del sistema SCADA debe seguir una serie de directivas para lograrlo, estas directivas y mecanismos (principalmente los brindados por el SOFTBUS) están implementados para UNIX. b) La elaboración de la interfaz entre la subestación y el centro de control. Esta será desarrollada en un ambiente Windows y tendrá una utilidad de configuración amigable al usuario que permita ajustar parámetros de conexión y funcionamiento. CAPITULO 4: DESARROLLO DEL PROYECTO 43 c) La integración de ambas interfaces; para esto se debe desarrollar un protocolo interno con el objetivo de lograr la comunicación entre ambas partes de la interfaz. Fase 5: Pruebas: En esta fase del proyecto se realizan pruebas de desempeño del prototipo de interfaz creado con la finalidad de verificar su funcionamiento. Se verificará la adquisición de datos de un servidor OPC genérico, la transmisión de estos datos, por medio del protocolo diseñado, a la interfaz en el sistema SCADA y se verificará la comunicación de esta interfaz con el sistema. Fase 6: Documentación: Esta fase comprende la elaboración de un informe técnico que recopile el trabajo desarrollado. 4.3 Familiarización con el Ambiente de Trabajo. Para el desarrollo de este proyecto fue necesario familiarizarse tanto con el sistema Sinaut Spectrum como con los procesos y dispositivos de campo en las S/E; es tal la complejidad de cada una de estas áreas que el departamento de automatización de Procedatos, ubicado en el centro de control Caujarito, se encuentra dividido principalmente en dos equipos de trabajo: uno ubicado en el taller de automatización y otro en la sala de SCADA; el primero se encarga de realizar pruebas y reparaciones a los dispositivos de campo, atender cualquier falla que ocurra en las subestaciones y realizar el mantenimiento rutinario de los equipos de automatización ubicados en las mismas. En la sala de SCADA se encuentran los servidores del sistema Sinaut Spectrum; la principal función del personal que allí se encuentra es velar por el óptimo funcionamiento del sistema corrigiendo cualquier falla que se presente y haciendo las actualizaciones necesarias al sistema conforme va cambiando la red eléctrica. Ambos equipos de trabajo se coordinan, en caso de una falla, para reducir al mínimo posible el tiempo de la indisponibilidad causada por dicha falla. Desde un principio se decidió enfrentar el problema desde dos perspectivas; una se enfocó en estudiar el sistema SCADA actual, particularmente la forma en la que éste maneja la entrada y salida de datos de proceso. La otra comprendió el estudio del flujo de datos entre las S/E y el centro de control. Se trabajó de forma paralela en ambos frentes con el objetivo de lograr incorporar nuevos protocolos al SCADA permitiéndole a éste ajustarse a los cambios que la corporación prevé implementar. CAPITULO 4: DESARROLLO DEL PROYECTO 4.4 44 Estudio del Sistema EMS Sinaut Spectrum El estudio del Sinaut Spectrum comprendió la lectura de los manuales de usuario, administrador y programador del sistema provistos por Siemens a ENELVEN como parte del SCADA. Este estudio se enfocó principalmente en el subsistema de adquisición de datos (DAS), en el sistema Softbus y en la estructura de la base de datos por ser éstas las partes más directamente relacionadas con el flujo de datos de proceso. Al inicio del proyecto, el personal SCADA contaba con información muy básica sobre el sistema SOFTBUS así como del funcionamiento interno del DAS. Se pensaba que el SOFTBUS era un sistema libre y abierto, siendo en realidad un sistema propietario diseñado por Siemens específicamente para el Sinaut Spectrum [10]. Esto limitó considerablemente las fuentes de información que se esperaba disponer para el desarrollo de la aplicación. Si bien se dispuso de la totalidad del código fuente del sistema, la gran extensión del mismo y el hecho de estar comentado en alemán retrasó esta etapa de la investigación. Cabe acotar que toda la información, contenida tanto en los manuales como en el código, es de carácter confidencial por ser propiedad de Siemens. Si bien Sinaut Spectrum está diseñado para ejecutarse de forma distribuida en una red de computadores, existe la posibilidad de configurar un servidor con las principales funcionalidades del sistema (RTC, ADM, UI, DS) para ejecutarse sin necesidad de estar conectado a la red, esta configuración se denomina “All-in-One”. En los manuales se pudo encontrar los archivos para esta configuración [10]. Estos archivos sirven para cambiar la configuración de un servidor en el que ya se encuentre funcionando el sistema Sinaut Spectrum. Se deben cambiar una serie de parámetros y reiniciar con la nueva configuración. Con la finalidad de disponer de un servidor de pruebas en el cual desarrollar y probar la interfaz propuesta, a partir de piezas de repuesto de otros servidores se ensambló un computador con las siguientes características: a) Dos discos duros de 8GBytes en blanco. b) Tarjeta de Video. c) Tarjeta de Red. d) 768MBytes de memoria RAM. e) 2 procesadores Sun Ultra Sparc de 200MHz cada uno. CAPITULO 4: DESARROLLO DEL PROYECTO 45 La adecuación de este servidor requirió instalar el sistema operativo Solaris 8 de SUN Microsystems. Esto se hizo siguiendo los pasos indicados en los archivos de ayuda del Spectrum, el cual exige configuraciones específicas en cuanto a las particiones de los discos. Una vez instalado el sistema operativo se requería descargar el código fuente del Spectrum desde el servidor SDM operativo en la red SCADA de ENELVEN. Este procedimiento es potencialmente riesgoso ya que requiere la utilización de recursos del sistema en operación y puede indisponer al servidor si no es apropiadamente ejecutado. Por tal motivo debía ser realizado con previa planificación y autorización del personal de ENELVEN. Con la ayuda del departamento de sistemas de la empresa se logró instalar el sistema Sinaut Spectrum en el servidor de pruebas; sin embargo, la funcionalidad alcanzada no fue completa, ya que al intentar levantar el sistema se generaban errores en la estructura de la base de datos interna. Luego de varios intentos se recurrió a un mecanismo de depuración incluido en el Spectrum para levantar el sistema parcialmente; de esta forma se consiguió tener un servidor con las funcionalidades básicas del Sinaut Spectrum exceptuando la base de datos operacional. La falta de dicha funcionalidad en este servidor no permite la realización de pruebas sobre la inclusión de datos recibidos al sistema SCADA; sin embargo no impide las pruebas de comunicaciones con el SOFTBUS ni la utilización de las herramientas que éste proporciona para la inclusión de datos al sistema. 4.5 Estudio del flujo de datos entre las S/E y el centro de control El medio físico utilizado para las comunicaciones entre el centro de control y las S/E consta de enlaces de fibra óptica, enlaces de microondas punto-punto y puntomultipunto y el protocolo de comunicación utilizado es el IEC-101. Este protocolo pertenece a una familia de protocolos incluidos en la norma IEC 60870, junto con él están el IEC-103 y el IEC-104. Este último es una versión TCP/IP del IEC-101 Al comienzo del proyecto, se presumía que IEC-61850 estaba orientado a comunicaciones TCP /IP; sin embargo al no haber sido estudiada previamente, la información que se manejaba dentro de la empresa era poca, existía cierta confusión. Incluso se pensaba que IEC-61850 era un protocolo de comunicación que pretendía remplazar al IEC-101. Por esto, el comienzo del estudio radicó en hacer una búsqueda de información concerniente a todo lo relacionado con IEC-61850, enfocándose 46 CAPITULO 4: DESARROLLO DEL PROYECTO principalmente en los mecanismos de comunicación, con la finalidad de lograr desarrollar una solución capaz de integrar este nuevo protocolo al sistema SCADA. Dado lo novedoso de la norma IEC-61850, el principal mecanismo de búsqueda de información fue Internet. El primer sitio encontrado fue el sitio Web del Comité Eléctrico Internacional, en donde se encontró que el protocolo estaba dividido en diez secciones, de las cuales habían sido publicadas nueve. La descarga de dichas secciones no es gratuita y representaba una inversión fuera del alcance de este proyecto exploratorio. Sin embargo, fue posible descargar los resúmenes de cada una de las secciones publicadas. De la lectura de los resúmenes se pudo apreciar que IEC-61850 no es un protocolo de comunicación sino una norma que tiene entre sus objetivos estandarizar tanto las comunicaciones dentro de las subestaciones eléctricas, como los elementos sobre los que se intercambia información. Sin embargo, no quedo claro que protocolo de comunicación especificaba la norma a la hora de transmitir datos entre las subestaciones y el centro de control. Ahondando la búsqueda de información, se encontró un servidor de demostración con datos ajustados a la norma [13]. Instalando el servidor en una máquina y el cliente en otra se procedió a analizar el tráfico de red a través del una versión de demostración del software analizador de protocolos MMS-Ethereal, se utilizó este software por ser posible su descarga gratuita de la red, además de encontrarse sugerido en el sitio web http://nettedautomation.schwarz-interactive.de/, sitio dedicado a promover y discutir la norma IEC-61850. Los resultados comprobaron que el protocolo de comunicación utilizado por el demo era parte de la Especificación de Mensajes de Manufactura (MMS), tal y como lo dice la norma en su sección número ocho (8). En esta etapa de la investigación se procedió a indagar sobre los detalles de la especificación MMS. Los resultados fueron que el MMS es un estándar internacional (ISO 9506), diseñado para permitir la comunicación entre dispositivos ubicados en fabricas (robots, PLC, etc.). En este punto el proceso de elaborar una interfaz utilizando MMS se vislumbró como una alternativa de comunicación; sin embargo, debido a la falta de herramientas de desarrollo, y la poca información disponible, se decidió seguir buscando otras alternativas para lograr la comunicación con el centro de control. En una presentación hecha por un representante de ABB Suiza, invitado por el departamento de protecciones para hacer una demostración de las ventajas de la 47 CAPITULO 4: DESARROLLO DEL PROYECTO aplicación de la norma IEC-61850 en las S/E, se observo la inclusión de una puerta de enlace para las comunicaciones hacia el centro de control. La razón expuesta para justificar dicha inclusión es que la norma aun no ha definido protocolos para llevar a cabo esta comunicación, por lo cual ABB sugiere seguir usando los protocolos actuales, en especial el protocolo IEC-101, para trasmitir datos hacia el centro de control [12]. A partir de este momento se abandonó la búsqueda de mecanismos para interconectar directamente el bus de estación de las S/E con el Centro de Control. Se procedió a iniciar un estudio sobre los gateways alternativos que se pudiesen incorporar en las subestaciones a fin de interconectarlas con el sistema SCADA. Se evaluó el uso de los protocolos IEC-101, IEC 104 y OPC. El primero por ser el actualmente usado para la comunicación, el segundo por ser la evolución del IEC-101 a las comunicaciones basadas en TCP/IP, OPC fue incluido por sugerencia del personal de automatización, en vista del hecho de que ya existían equipos en la corporación que utilizaban OPC como protocolo de comunicación, específicamente el micro SCADA ubicado en la planta generadora TERMOZULIA. A continuación se presentan los resultados de este estudio donde se resaltan las ventajas e inconvenientes de los tres protocolos evaluados. En la Tabla # 4-1 se resumen las ventajas e inconvenientes de los protocolos IEC-101 e IEC-104, se hace lo mismo con OPC en la Tabla # 4-2 Gateway IEC-101 Ventajas 1) Es el protocolo actualmente utilizado para la comunicación con el Centro de Control. 2) El Personal está familiarizado con el uso del mismo. Además, La corporación ya posee herramientas de diagnóstico específicas para el IEC -101 3) Es un estándar internacional. 4) Sólo deben incorporarse gateways en las subestaciones nuevas. Inconvenientes 1) Diseñado para trabajar sobre canales de baja velocidad. 2) Utiliza índices para identificar los datos, perdiéndose la autodescripción presente en los objetos incorporados por la norma. 3) Requiere mantener dos tipos de comunicación con cada S/E; una serial y otra TCP/IP si se desea tener acceso remoto a los IED Gateway IEC-104 Ventajas 1) Es una actualización del IEC-101, encapsula las tramas del IEC-101 dentro de paquetes TCP/IP permitiendo el enrutamiento de los datos utilizando software y hardware CAPITULO 4: DESARROLLO DEL PROYECTO 48 ampliamente soportados y probados en aplicaciones masivas (Internet) Inconvenientes 1) Utiliza índices para identificar los datos. 2) Requiere incorporar gateways en todas las S/E 3) Requiere el desarrollo de una interfaz IEC-104 en el SCADA Tabla # 4-1 Resultados del estudio comparativo: IEC-101 e IEC-104 Gateway OPC Ventajas 1) Actualmente OPC es utilizado dentro de la corporación para aplicaciones de control (TERMOZULIA). 2) El gateway OPC utiliza DCOM sobre TCP/IP para comunicarse con el Centro de Control. 3) Dada la amplia disponibilidad de drivers disponibles en el mercado, se facilita la integración con los IED presentes en la S/E. 4) OPC utiliza etiquetas para identificar los diferentes parámetros de supervisión y control, lo cual permite mantener la semántica. 5) Permite que la información de supervisión y control presente en las S/E este disponible para ser utilizado por otras aplicaciones dentro de la corporación (Estadísticos, financieras, seguridad etc.). 6) Actualmente el desarrollo de OPC se encuentra en auge, no sólo por el impulso de los fabricantes de equipos sino por terceros 7) Rápida Transmisión de los datos, 5000 valores por segundo aprox. (ver [16]). Ya se encuentran en el mercado gateways OPC - IEC 61850. Inconvenientes 1) El uso de la tecnología DCOM resulta en dificultades a la hora de configurar los equipos por primera vez. 2) OPC es un estándar, pero COM es propietario de Microsoft Corp. 3) Requiere incorporar gateways en todas las S/E. Requiere el desarrollo de una interfaz OPC en el SCADA. Tabla # 4-2: Resultados del estudio comparativo: OPC El uso de un gateway IEC-101, permite incorporar las nuevas subestaciones compatibles con la norma al sistema SCADA actual, sin llevar a cabo ninguna modificación en este último. Sin embargo, el uso de dos canales para comunicarse con la S/E, uno TCP/IP para acceder a los IEDs y otro serial para supervisar y controlar la S/E podría acarrear inconvenientes. El IEC-104 solucionaría el problema de la utilización de dos canales de comunicación, pero requería la implantación de una CAPITULO 4: DESARROLLO DEL PROYECTO 49 interfaz en el SCADA que no aportaría mayor valor adicional que convertir el IEC-104 a IEC-101. El uso de gateways OPC permite la unificación de los canales de comunicación y aunque se requiere el desarrollo de una interfaz, OPC es un protocolo que garantiza el alto grado de compatibilidad con los equipos actuales y futuros, el resguardo de la inversión al ser un estándar en auge. Esto a su vez reduce los costos de integración, al contar con una amplia base de fabricantes desarrollado drivers OPC para interconectar tanto sus equipos como los de terceros. La utilización de etiquetas para identificar los diferentes parámetros de supervisión y control facilita la configuración de los datos requeridos por el SCADA de una forma más sencilla. Actualmente se requiere conocer índices numéricos (ver la descripción de la Base de Datos Operacional en el capítulo 2), OPC permite remplazar estos índices por etiquetas que muestren relación con el dato asociado. Así el estado de un interruptor de 24kV ubicado en la subestación Don Bosco asociado a la línea Zapara puede ser representado como D.BOS.24ST.ZAPAR.CB1.Status, en caso de desear hacer coincidir la etiqueta con el formato de dirección tecnológica del Sinaut Spectrum. La norma IEC-61850 promueve el desarrollo de mecanismos de auto descripción de los datos provistos por los IED ubicados en las S/E; ésto unido al sistema de etiquetas flexibles puede ser usado para explorar datos específicos de una subestación y asociarlos a un elemento en el SCADA, sin requerir el traslado de personal a la S/E. 4.6 Interfaz Propuesta. Una vez seleccionado el gateway fue necesario desarrollar una interfaz que permitiese la comunicación entre éstas y el Sistema SCADA. La implementación de dicha interfaz debió ser dividida en dos partes desarrolladas en sistemas operativos diferentes. Esta división fue necesaria ya que en la versión de Solaris instalada en los servidores SCADA no está implementado el modelo COM, lo cual es necesario para el desarrollo de un cliente OPC. Las partes en las que fue dividida la interfaz se llaman SIOS y WIOS (Solaris IOS y Windows IOS respectivamente). Para la comunicación interna entre estas interfaces se desarrolló un protocolo basado en TCP/IP. CAPITULO 4: DESARROLLO DEL PROYECTO 50 La estructura general de esta propuesta se muestra en la Figura # 4-1. En ella se pueden observar los elementos clave de la interfaz, el actual sistema SCADA, una subestación eléctrica (S/E) y su gateway correspondiente. Como se mencionó anteriormente, la adquisición de datos en el sistema SCADA se realiza a través del sistema de adquisición de datos DAS, el cual se ve representado en la Figura # 4-2. El protocolo seleccionado para la comunicación con el centro de control es el OLE for Process Control (OPC); se propone como interfaz a desarrollar un sistema de adquisición de datos (DAS) alternativo, el cual no utilice como puerta de enlace el TIF, sino que aproveche la red LAN ya existente en el sistema SCADA. Este DAS alternativo, pretende servir de apoyo al DAS existente, aliviando su carga y funcionando además como puerta de enlace a los datos a través de otros protocolos; en este caso OPC. Figura # 4-1 Estructura general del IOS CAPITULO 4: DESARROLLO DEL PROYECTO 51 Figura # 4-2 Sistema de Adquisición de Datos del Sinaut Spectrum. Debe existir un punto de inserción dentro del sistema para la interfaz a desarrollar. Este punto de inserción debe permitir a la interfaz el acceso a programas y rutinas ejecutándose continuamente dentro del SCADA, así como también permitir el acceso a la base de datos no sólo para lectura sino también para escritura. Los mecanismos para lograr esto los proporciona el sistema interno del SCADA para comunicación entre procesos llamado Softbus, lo cual hace ideal la inserción de la interfaz en este punto como se muestra en la Figura # 4-3. Figura # 4-3 Inserción del IOS al Sistema SCADA. CAPITULO 4: DESARROLLO DEL PROYECTO 52 Para que esta interfaz sea funcional, es necesario que cumpla con todas las funciones con las que cumpliría el DAS original, por lo tanto, debe ser capaz de recibir datos del proceso de campo y entregarlos al sistema SCADA en el formato pertinente, debe también ser capaz de enviar comandos a los elementos de campo para que estos tomen acciones. Para simplificar el contenido de la propuesta se ha subdividido en tres secciones 1) Solaris IOS 2) Protocolo IOS 3) Windows IOS 4.6.1 Solaris – Interfaz OPC SCADA (SIOS). El S.I.O.S. es la parte de la interfaz IOS que está ejecutándose en ambiente UNIX en modo demonio, ésta está desarrollada completamente en lenguaje C y consta principalmente de cuatro (4) partes o hilos, estos son: a) Núcleo Principal. b) Despachador. c) Lazo de Recepción PIOS. d) Lazo de Recepción Softbus. Además de estos hilos principales existe un mecanismo de comunicación entre ellos: La Cola de Mensajes. Esta es un mecanismo cuyo funcionamiento permite agregar mensajes a un buffer común a todos los hilos; para luego ser retirados o leídos por cualquiera de los hilos en el mismo orden en el que fueron insertados, es decir, tiene un comportamiento FIFO (First In First Out) aunque con ciertas excepciones. Es posible asignar a un mensaje un número correspondiente a su prioridad, mientras mayor sea este número más prioridad tiene el mensaje; esto significa que será extraído de la cola antes que todos aquellos mensajes cuya prioridad sea menor, independientemente del orden de llegada. En la Figura # 4-4 se muestra como se van ordenando los mensajes en la cola de acuerdo al orden de llegada pero respetando la prioridad. CAPITULO 4: DESARROLLO DEL PROYECTO 53 Figura # 4-4 Comportamiento de la cola de mensajes En el caso del SIOS, todos los mensajes correspondientes a procesamiento de datos o intercambio de información tienen la misma prioridad (cinco (5)), mientras que un comando tiene una prioridad mayor (diez (10)). Estos son los únicos valores de prioridad utilizados por el SIOS. En la Figura # 4-5 se observa el diagrama de funcionamiento general del SIOS, señalando la relación entre los diferentes hilos que serán explicados más adelante. El lazo de Recepción PIOS obtiene cualquier información proveniente del WIOS y la agrega a la cola de mensajes. El lazo de recepción Softbus obtiene información proveniente del SCADA, si está relacionada con el proceso de campo es colocada en la cola. El despachador toma la información de la cola de mensajes y dependiendo de su contenido la transmite procesada ya sea al SCADA mediante el Softbus o al WIOS mediante el PIOS. En caso de que la información sea un comando, el despachador da una orden al núcleo del programa para que éste se encargue de ejecutar el comando. Posteriormente se describe detalladamente cada una de las partes que conforman el SIOS. CAPITULO 4: DESARROLLO DEL PROYECTO 54 Figura # 4-5 Funcionamiento general del SIOS. a) Núcleo: El núcleo es la parte del programa que controla todas las demás, es el encargado de crear los demás hilos y de cerrarlos cuando sea necesario. Al inicializar el programa SIOS, el núcleo se debe registrar al sistema Sinaut Spectrum para poder utilizar sus recursos, luego debe notificar al Softbus para inicializar una dirección, inicializar los descriptores de la base de datos del Spectrum para poder acceder a datos contenidos en la misma y debe inicializar el mecanismo de cola de mensajes. En la Figura # 4-6 se muestra el diagrama de funcionamiento del núcleo del SIOS. 55 CAPITULO 4: DESARROLLO DEL PROYECTO Inicialización de Punteros a Archivos para Registro Inicialización de Socket y espera por conexión PIOS Notificación del programa al Sinaut Spectrum Subscripción al Softbus Inicialización de Base de Datos Creación de Hilo Receptor Softbus Conexión PIOS Valida Inicialización de Cola de Mensajes Creación de Hilo Receptor PIOS y de Hilo Despachador Espera por Comandos Llega un Comando Ejecuta Comando Figura # 4-6 Diagrama de funcionamiento del núcleo. Luego de esta serie de inicializaciones se crea el hilo de recepción Softbus para atender mensajes de sistema que puedan llegar. Seguidamente se espera por una conexión PIOS válida para crear los hilos restantes que serían el despachador y el lazo de recepción PIOS. b) Despachador: El despachador es un lazo que constantemente extrae mensajes de la cola para su procesamiento como se muestra en la Figura # 4-7. Si la cola se encuentra vacía el hilo se suspende (sin utilizar recursos del sistema) hasta que un nuevo mensaje llegue a la cola. Al extraer un mensaje cuyo destinatario es OPC se envía a través de la conexión PIOS. Si el destinatario es SOFTBUS, dependiendo del valor del byte de control, se hace la solicitud de un valor al SCADA o la distribución del valor recibido. En caso de que llegue un comando este será ejecutado inmediatamente por el núcleo del programa. Si el destinatario del mensaje es desconocido se registra el error y se vuelve a suspender el hilo esperando por nuevos mensajes. 56 CAPITULO 4: DESARROLLO DEL PROYECTO Hilo suspendido hasta que llegue nuevo mensaje a la cola Llegó Mensaje a la cola Mensaje es Comando? SI Núcleo Ejecuta Comando SI Envía Mensaje a través de PIOS. NO Mensaje Para WIOS? NO Es para Softbus? Distribución SI Byte de Control? Petición Envía Datos de Proceso a Softbus Envía Petición a Softbus NO Error Figura # 4-7 Diagrama de Flujo del Despachador La solicitud del valor al sistema SCADA o la distribución de un valor al mismo se hace utilizando mecanismos provistos por el sistema Softbus. Para poder utilizar estos mecanismos es necesario proporcionar ciertos parámetros sobre los datos en cuestión, estos parámetros son: Tipo de Dato, Dirección Tecnológica, número de datos que se requieren o envían y tipo de petición (envío o solicitud). Estos mecanismos se comunican con un programa suministrador de valores del Sinaut Spectrum. Es este programa el encargado de hacer la búsqueda o inserción en la base de datos. c) Lazo de Recepción PIOS: Este receptor es un hilo que escucha a través de una conexión PIOS, al recibir un mensaje revisa la integridad y coherencia de los datos recibidos como se observa en la Figura # 4-8. En caso de algún error este es registrado y los datos desechados, los errores pueden ser por timeout, por CAPITULO 4: DESARROLLO DEL PROYECTO 57 incoherencia de los datos recibidos, o por alguna falla en la conexión; si no se encuentra error se envía el mensaje a la cola con la prioridad correspondiente para luego ser procesado por el despachador. d) Lazo de Recepción de Softbus: Este receptor constantemente escucha el buffer de recepción de Softbus, este hilo no sólo procesa mensajes relacionados con la interfaz sino señales y mensajes del sistema Spectrum, en la Figura # 4-9 se observa un diagrama de flujo que muestra el funcionamiento de este hilo. Se omite lo referente a mensajes y señales del sistema debido a la confidencialidad requerida por Siemens con respecto a la documentación del sistema Sinaut Spectrum®. El hilo se mantiene suspendido sin utilizar recursos mientras escucha, una vez que llega una orden de Softbus el hilo determina si es un mensaje de sistema o datos de campo, en el primer caso se envía el mensaje al núcleo y este lo procesará según lo requiera el Spectrum. Si son datos de campo, se agregan a la cola de mensajes con la prioridad correspondiente para luego ser procesados por el despachador. Luego el receptor suspende nuevamente hasta la llegada de una nueva orden. 58 CAPITULO 4: DESARROLLO DEL PROYECTO Hilo suspendido esperando por datos en conexión PIOS Se reciben Datos Lee buffer de recepción Secuencia de Inicio? Activa Timer de 10 Seg. NO SI Lee buffer de recepción Pasan 10 seg antes de desactivar. Interrumpe Recepción, desecha datos y registrra error Desecha Caracter Secuencia de Fin? Deshabilita Timer Almacena Datos en Buffer NO SI Desactiva Timer Chequeo de Longitud ok? NO Error SI Es un comando? NO SI Envía paquete completo a Cola de Mensajes con prioridad 10 Envía paquete completo a Cola de Mensajes con prioridad 5 Figura # 4-8 Diagrama de Flujo del Receptor PIOS CAPITULO 4: DESARROLLO DEL PROYECTO 59 Figura # 4-9 Diagrama de flujo del receptor softbus. 4.6.2 Protocolo IOS. El PIOS es un protocolo de comunicación diseñado específicamente para el intercambio de información entre el WIOS y el SIOS. Está diseñado para funcionar sobre TCP/IP, su estructura está basada en la transmisión de paquetes. El PIOS es orientado a conexión, sobre una conexión TCP/IP se establece una conexión PIOS la cual debe ser confirmada. El mecanismo de confirmación requiere que ambos objetos conectados envíen tramas con una secuencia específica de confirmación, si después de establecer la conexión no se recibe inmediatamente dicha trama la conexión se declara como inválida y se descarta como se ve en la Figura # 4-10. Dentro de cada paquete PIOS es posible enviar varios bloques de información concerniente al proceso, para informar al receptor cuantos bloques de información vienen en un determinado paquete se utiliza un byte dentro del encabezado del mismo. Se estableció un máximo de bloques por paquete de 10. Las tramas o paquetes PIOS, encapsuladas bajo TCP poseen la estructura mostrada en la Figura # 4-11. 60 CAPITULO 4: DESARROLLO DEL PROYECTO Figura # 4-10 Diagrama de Flujo del proceso de conexión PIOS. Paquete TCP ENCABEZADO DATOS FOOTER Paquete PIOS N Bloques de Datos ENCABEZADO Encabezado PIOS Sec. Final Datos PIOS Inicio Length Emisor Dest. #Bloques N Bloques de Datos Final 2 2 1 1 1 57xN 2 N veces: Control Tipo de Dato B1 B2 B3 Elem Info Sec. mSec Calidad Valor # de Bytes: 1 1 8 8 8 8 8 4 2 1 8 # de Bytes: Dirección Tecnológica Time Stamp Figura # 4-11 Estructura de las tramas pios. CAPITULO 4: DESARROLLO DEL PROYECTO 61 Cada paquete PIOS posee un encabezado con siete (7) bytes de longitud; en estos bytes se incluye información sobre el emisor, el destinatario, secuencia de inicio, número de bloques de datos en el paquete y longitud total del paquete. Esta longitud incluye encabezado y fin de paquete. Si la información a enviar es un comando IOS, el valor del byte de número de bloques de información es cero (0) como se refleja en el primer ejemplo de la Figura # 4-12; el comando respectivo se envía en el byte de destinatario y en el byte de emisor se envía el valor correspondiente a comando IOS, de manera que el receptor interprete la información como comando. Figura # 4-12 Ejemplos de Encabezados PIOS. Los valores posibles para los diferentes bytes del encabezado se muestra en la Tabla # 4-3. 62 CAPITULO 4: DESARROLLO DEL PROYECTO Valores Posibles en Encabezado PIOS Byte Inicio Longitud Emisor Destino # de Bloques Final Valores Posibles 0xF0AE 0x00 -> 0x0412 0x01 0x02 0x03 0x01 0x02 0x50 0x51 0x52 0x53 0x00 -> 0x0A 0xEBC4 Significado Secuencia de Inicio de Paquete IOS Longitud del paquete IOS, máximo 1042 bytes Emisor es OPC Emisor es SCADA Byte de Destino es Comando IOS Destino es OPC Destino es SCADA Comando IOS = Cerrar Despacho Comando IOS = Cerrar Receptor PIOS Comando IOS = Cerrar Receptor Softbus Comando IOS = Cerrar SIOS # de Bloques de Información de Proceso Secuencia de Final de Paquete IOS Tabla # 4-3 Valores Posibles en Encabezado PIOS Luego del encabezado siguen los bloques de información del proceso, habrá tantos bloques como lo indique el byte de número de bloques. Cada uno de ellos contiene información sobre el tipo de dato, la solicitud a realizar, ya sea distribuir el valor o solicitar un valor, la dirección tecnológica, la estampa de tiempo, la calidad de la medición y el valor en sí. En la Figura # 4-13 se muestra un ejemplo de bloque de información detallando el significado de cada uno de los datos contenidos. 63 CAPITULO 4: DESARROLLO DEL PROYECTO Estampa de Tiempo Ctrl Tipo 0x03 0x06 Sec Dirección Tecnológica 0x7B 0x1C 0x02 mSec 0x01 0x02 0x01 Calidad 0x7B 0x01 Valor transmitido = ON Calidad del Valor transmitido Fracción de Seg. en mseg. # de Segundos desde 1-01-1970 Tipo de Dato = Switch / Binario Control = Distribuir Valor Figura # 4-13 Ejemplo de Bloque de Datos. La estampa de tiempo viene dada como el número de segundos transcurridos desde el primero de enero de 1970 a las 12:00:00 a.m. U.T.C. más el número de milisegundos transcurridos desde el último segundo. Esto se conoce también como EPOCH. Los valores de calidad son como se especifica en la referencia de OPC. Los valores posibles para los bytes de control y tipo de datos se muestran en la Tabla # 4-4. Byte Control Tipo de Dato Valores Posibles de Control y Tipo de Dato. Valores Posibles Significado 0x01 Petición de OPC a SCADA 0x02 Datos de proceso de OPC a SCADA 0x03 Datos de proceso de SCADA a OPC 0x02 Double Float, entero contador. 0x03 Float, valor medido, punto flotante 0x06 Switch, valores posibles 0 o 1 Tabla # 4-4 Valores posibles en bytes de control y tipo de datos. La asignación de los valores en las tablas 4-3 y 4-4 se hizo de tal manera que fuese compatible con los valores manejados por el sistema SCADA. Con esto se logra un menor procesamiento por cada paquete en la interfaz SIOS. La estructura de los paquetes es también similar a la requerida por el Softbus para la transmisión de órdenes; de igual forma la decisión de manejar las estampas de tiempo utilizando el método EPOCH es debido a que es la que maneja el sistema SCADA. Al finalizar los 64 CAPITULO 4: DESARROLLO DEL PROYECTO bloques de información de proceso se agregan al paquete dos (2) bytes que corresponden a la secuencia de finalización del paquete. 4.6.3 Windows – Interfaz OPC SCADA (WIOS) Para llevar a cabo la comunicación entre las subestaciones y el centro de control, se desarrolló un esquema cliente – servidor, donde un cliente OPC es capaz de acceder a los datos de múltiples servidores OPC (gateways) instalados en las S/E. La base de este esquema se concentra en lo que se ha denominado WIOS, el cual es un software basado en Windows desarrollado en Visual Basic 6, encargado de concentrar todos los datos provenientes de las S/E y enrutarlos a través del SIOS hacia el SCADA. El WIOS posee cuatro elementos funcionales básicos: una base de datos, un cliente OPC, una interfaz TCP/IP con el SIOS, y un Núcleo que se encarga de coordinar el funcionamiento de los elementos anteriores con la finalidad de que el WIOS realice su labor. Además, se desarrolló una herramienta externa para la configuración del WIOS. En la Figura # 4-14 se observa el diagrama de bloques del WIOS. Router Cliente OPC Base de Datos Núcleo Red de Datos de la Corporación Interfaz PIOS PIOS SIOS Figura # 4-14 Diagrama de Bloques del WIOS A continuación se explica cada uno de los elementos que conforma el WIOS: CAPITULO 4: DESARROLLO DEL PROYECTO 65 a) Base de Datos: Al configurar los servidores OPC se asocia una etiqueta o Tag a cada variable que el servidor obtiene de los dispositivos de campo. Es así como una entrada digital en un PLC se puede conocer como DI 4.15.123 y en el servidor OPC aparecer como “barra1.salida.p.reactiva”. El uso de etiquetas permite mantener la semántica de los datos, y, al compararlo con el uso de índices numéricos, facilita la configuración de aplicaciones remotas. En el sistema SCADA actual, las mediciones están identificadas con índices (link address, common address, etc.) y luego llevados por el DAS al Sinaut Spectrum con su dirección tecnológica correspondiente (B1,B2,B3, Elem, Info). Ante esto se plantearon dos posibles soluciones. Una es estandarizar los tags de acuerdo a los requerimientos del SCADA haciendo coincidir las etiquetas con la dirección tecnológica, y la otra es crear una tabla de conversión entre los tags OPC y los identificadores de Spectrum. Es evidente que el uso de una tabla de conversión involucra un procesamiento adicional; sin embargo, la flexibilidad que proporciona lo justifica. La tendencia en los protocolos actuales es de contextualizar el dato y brindar mecanismos de auto descripción, por lo tanto no conviene estandarizar los datos aguas abajo (en las S/E), pues se perderían así las ventajas de la autodescripción incluida en los nuevos equipos compatibles con la norma IEC-61850. El utilizar una tabla de conversión aguas arriba, permite al programador seleccionar los items que requiera del campo e insertarlos en la forma que crea conveniente en el SCADA. Una ventaja adicional es que los datos podrían ponerse a la disposición de otras herramientas de software (estadísticos, finanzas, etc.) dentro de la red de la corporación, herramientas para las cuales la nomenclatura del SCADA no resultaría necesariamente útil. La base de datos (BD) seleccionada para ser utilizada en el prototipo fue un archivo de Microsoft Access, y el motor de Base de Datos utilizado por la aplicación fue Microsoft Jet 4. Ambos resultan muy prácticos para un rápido desarrollo del prototipo. Sin embargo, en la aplicación final se debe contar con un Motor más potente, confiable y apropiado a aplicaciones exigentes. Existen múltiples en el mercado, por lo que se sugiere realizar un estudio posterior para seleccionar el más adecuado para una implementación operativa de la interfaz propuesta. La estructura de la base de datos utilizada consta de cuatro tablas: Items, Servidores, Tipodesenal y WIOS. Como se puede observar en la Figura # 4-15 las primeras tres tablas están relacionadas entre si, la ultima guarda los parámetros de CAPITULO 4: DESARROLLO DEL PROYECTO 66 configuración del demonio WIOS. A continuación se presenta una descripción de cada una de las tablas, luego en la Tabla # 4-5 se describe cada uno de los campos que las conforman. 1) La tabla items: posee nueve campos, es aquí donde se concentran los datos requeridos para hacer la conversión. Dispone de una clave única para cada ítem llamada id, así como dos claves asociadas con las tablas servidores y Tiposdesenal. Figura # 4-15 Tablas de la Base de Datos utilizada en el prototipo WIOS 2) La tabla servidores: almacena el listado de todos los servidores activos. Posee tres (3) campos: un identificador único para poder asociarlo a la tabla items y dos parámetros que permiten la conexión con el servidor: su nombre, referido este en la especificación OPC como PROGID, y el nodo donde reside. 3) La tabla Tiposdesenal: proporciona un complemento informativo sobre los tipos de señales de campo, facilitando así la contextualización del dato. Por ser de carácter informativo la información contenida en esta tabla no afecta el proceso de conversión de las etiquetas OPC-SCADA. 4) La tabla Wios: guarda los valores de configuración del demonio. Actualmente consta de dos campos básicos, IP y RefreshRate. Sin embargo, podrían añadirse más, a futuro, para manejar otros parámetros de configuración. b) Cliente OPC: El cliente OPC se encarga de llevar a cabo la comunicación entre el WIOS y las subestaciones. Proporcionando la dirección IP del servidor OPC (gateway) ubicado en la S/E y las etiquetas asociadas a los items solicitados el cliente recupera la información que el núcleo le solicite. Las especificaciones del protocolo OPC 67 CAPITULO 4: DESARROLLO DEL PROYECTO establecen dos tipos de interfaces para conectarse a los servidores OPC: Una interfaz custom y una interfaz de automatización. La primera esta diseñada para ser accedida utilizando programación en leguajes tales como C o C++, entre otros, mientras que la segunda permite el acceso a los datos utilizando lenguajes de rápido desarrollo como Visual Basic, Delphi, entre otros. TABLAS DE LA BASE DE DATOS NOMBRE: CAMPO Id Serverid Items DESCRIPCION Identificador único del ítem Identificador del servidor OPC al que pertenece este ítem. Sirve como clave de búsqueda en la tabla servidores access Tipo de acceso según el servidor OPC opckey Etiqueta OPC scadakey Etiqueta SCADA scadaRW Tipo de acceso requerido por el SCADA modificado DataType TipodeSenal Fecha de la última modificación hecha a los campos del reglón correspondiente Entero que indica el tipo canónico de datos que corresponde al ítem Identificador que indica que tipo de señal representa este ítem. Sirve como clave de búsqueda en la tabla Tiposdesenal. Es de carácter informativo y su inclusión es opcional. NOMBRE: Servidores CAMPO DESCRIPCION serverid Identificador único del servidor Servername nodename ProgID del Servidor. Nombre del Nodo donde se encuentra el servidor OPC. Puede ser un nombre de red o una dirección IP NOMBRE: CAMPO id descripción Tipodesenal DESCRIPCION Identificador único del tipo de señal Texto que indica el tipo de señal NOMBRE: CAMPO WIOS DESCRIPCION IP Dirección IP del SIOS RefreshRate Rata de refrescamiento Tabla # 4-5 Descripción de las tablas que conforman la Base de Datos CAPITULO 4: DESARROLLO DEL PROYECTO 68 Dado que los servidores OPC deben ser escritos en C o C++, la interfaz custom permite un mejor desempeño y flexibilidad a la hora de crear un cliente pues permiten interactuar directamente con el Servidor OPC. Sin embargo, para lograr un correcto diseño de un cliente OPC que utilice la interfaz custom, es necesario utilizar el modelo COM de Microsoft. La interfaz de automatización, en cambio brinda un rápido acceso a los datos, pues incorpora un modulo intermedio que se encarga de todo el proceso de comunicación COM, proporcionando funciones prácticas que agilizan en gran medida el desarrollo de un cliente OPC. Para la realización del prototipo se decidió utilizar la interfaz de automatización, por la facilidad de uso, la rapidez con la que pueden elaborarse prototipos de prueba. Para los fines demostrativos de este proyecto la interfaz de automatización es apropiada y permite mostrar los beneficios de utilizar el protocolo OPC. Además permite justificar un desarrollo más elaborado utilizando la interfaz custom al momento de llegar a implementar la solución propuesta en los procesos operativos de la corporación Enelven. c) Interfaz PIOS: Es el mecanismo utilizado para comunicarse con el SIOS. En principio se intentó utilizar Visual Basic para crear los paquetes tal como se muestran en la sección 4.6.2, pero se presentaron problemas por la poca flexibilidad que brinda el leguaje al momento de manejar cadenas de bits. Luego de varios intentos se decidió desarrollar una librería (DLL) escrita en C++ con la finalidad de crear y trasmitir los paquetes. En ella se incluyeron funciones que permitían crear los paquetes PIOS, enviarlos al SIOS, recibirlos de éste, interpretarlos y enviarlos al núcleo. Para ello se requiere que desde la aplicación se le pasen los datos que conforman el paquete, así como el identificador de un Socket previamente creado. Este Socket se crea utilizando el Winsock de Visual Basic, al momento de establecer una conexión TCP/IP orientada a conexión con el SIOS. Durante las pruebas hechas a la librería se encontraron problemas al momento de trasmitir los paquetes, estos llegaban con la estructura correcta, pero los valores no coincidían. Se identificó en ese momento que el orden de los bits difiere entre las plataformas Sun y Windows. Esto obligó al desarrollo de una capa de presentación encargada del proceso de reordenamiento de los paquetes PIOS. Todo este proceso se realiza dentro de la librería y es transparente para el núcleo. 69 CAPITULO 4: DESARROLLO DEL PROYECTO d) Núcleo: Coordina las funcionalidades de los elementos anteriores permitiendo que el WIOS cumpla su función. En la Figura # 4-18 se puede apreciar un diagrama que muestra el funcionamiento básico del núcleo y sus interacciones con los elementos anteriormente mencionados. El hilo principal (indicaciones) funciona síncronamente, interrogando los gateways OPC y trasmitiendo los datos al SIOS. Una vez interrogados todos los servidores, el ciclo se repite infinitamente, hasta que la aplicación sea terminada. En este ciclo se interrogan todos los servidores activos que se encuentren en la tabla servidores de la Base de Datos. El evento comandos se dispara al recibir del SIOS una instrucción identificada como comando hacia las S/E, al igual que las indicaciones los comandos poseen etiquetas asociadas que deben ser traducidas y luego enviadas al servidor OPC correspondiente. El evento finalizar simboliza el hecho de que la aplicación puede ser detenida en cualquier momento mediante la intervención del usuario. Cabe resaltar que el Núcleo no modifica nunca la BD, sólo se realizan consultas, el elemento encargado de modificar la BD es la utilidad de configuración. Para ahondar en la implementación del núcleo véase el código fuente en el ANEXO E. e) Utilidad de configuración (UC IOS): Mediante esta utilidad es posible configurar la base de datos y los parámetros de funcionamiento del WIOS. Permite ubicar los servidores OPC disponibles en un nodo de la red corporativa, así como los items disponibles en cada servidor. Usando esta información es posible construir la tabla de conversión, asignando las etiquetas del SCADA a los items OPC seleccionado. En la Figura # 4-16 se puede ver como la utilidad de configuración tiene integrado su propio cliente OPC con el cual accede a la red corporativa. Además posee un acceso bidireccional a la base de datos, lo que hace que la UC sea el único componente de la solución WIOS que puede escribir en ella. Esto disminuye las posibilidades de fallas asociadas a corrupción de los datos una vez que el sistema se encuentra activo. 70 CAPITULO 4: DESARROLLO DEL PROYECTO UC IOS Cliente OPC Base de Datos Red de Datos de la Corporación Figura # 4-16: Diagrama de Bloques de la Utilidad de Configuración La UC IOS cuenta con una interfaz gráfica que permite que el usuario lleve a cabo las tareas de: 1. Buscar servidores OPC en la red, pueden ser los gateways u otros servidores OPC existentes en la corporación (ej.: TERMOZULIA). 2. Probar la conexión a dichos servidores. 3. Agregar/eliminar un servidor a la tabla de servidores registrados que el WIOS debe interrogar. 4. Hacer una exploración de los items presentes en un determinado servidor registrado, seleccionando aquellos cuyos valores se deseen transmitir al SCADA. 5. Crear una tabla de conversión donde el usuario puede asignar una dirección tecnológica compatible con el SCADA a cada ítem OPC. 6. Validar que los items seleccionados no presenten errores de sintaxis, o problemas de conexión. En el diagrama Top-Down mostrado en la Figura # 4-17 se incluyen las pantallas que el usuario se encontrará al momento de interactuar con la aplicación. Para mayor información puede ver el manual de usuario de la utilidad en el Anexo A. Si se desea profundizar en la implementacion de la UC, todo el código fuente se incluye en el Anexo E. CAPITULO 4: DESARROLLO DEL PROYECTO 71 Figura # 4-17: Diagrama Top-Down de la UC IOS 4.6.4 Incorporación de las subestaciones al proyecto IOS El WIOS debe conectarse a un servidor OPC presente en cada S/E; sin embargo, la forma en que este servidor obtiene los datos de los elementos que conforman la subestación varía dependiendo de la naturaleza de los mismos. Se debe considerar por separado la recopilación de datos de las subestaciones actuales y la forma en que se hará en las futuras S/E compatibles con la norma IEC-61850. a) S/E actuales: Las RTU SICAM SAS utilizadas actualmente en la mayoría de las subestaciones de la corporación son PLC Siemens modelo SIMATIC S7-400. Hoy en día no poseen conexión Ethernet. Sin embargo, dada la facilidad de expansión inherente a los PLC se encontró que existen módulos que permiten la comunicación de estos PLC vía Ethernet. Dichos módulos no son sólo provistos por Siemens sino por un buen grupo de terceros. 72 CAPITULO 4: DESARROLLO DEL PROYECTO INDICACIONES COMANDOS INICIO EVENTO INICIO Verificar la Existencia de Servidores Interfaz PIOS BD Verificar la Existencia de Items asociados Recepción de Comandos BD Conectarse a Servidores OPC Cliente OPC Envió comandos a los servidores OPC BD Conectarse a SIOS BD Interfaz PIOS BD Conversión de la etiqueta SCADA a su ítem OPC correspondiente Cliente OPC Polling de los ítems en un servidor OPC activo listado en la tabla servidores Cliente OPC BD FIN EVENTO Transmitir Datos al SIOS Interfaz PIOS FINALIZACION INICIO EVENTO Siguiente Servidor NO ¿Es el último servidor de la tabla? ¿Salir? NO SI Reiniciar Polling, comenzando con el primer serviidor de la tabla servidores SI FIN APLICACION Figura # 4-18 Funcionamiento del Núcleo de WIOS. Diagrama básico FIN EVENTO CAPITULO 4: DESARROLLO DEL PROYECTO 73 La conexión a Ethernet de la RTU permitiría, de acuerdo a los fabricantes, la programación remota de las mismas utilizando software provisto por ellos para este fin. También es posible realizar la conexión utilizando RS-232, ejemplo de esto podemos ver en la Figura # 4-19 donde se observa el esquema de conexión brindado por la empresa Matrikon. Se puede apreciar que los servidores OPC requieren una PC adicional a la RTU, esto es debido a que se suele utilizar el HMI presente en las S/E como plataforma para correr el Servidor OPC. En el caso de que una subestación cuente con una HMI no representaría costo adicional. En aquellas que aun no la posean se requerirá su implantación. Su bajo costo y las herramientas de diagnostico que pueden instalarse en el HMI, bien hacen valer la inversión. Además, ya se han hecho estudios donde se sugiere la inclusión de un HMI en cada una de las S/E [2]. Figura # 4-19 Arquitectura del Sistema usando un Servidor OPC Matrikon [20] Una vez instalado el Servidor OPC en el HMI, este debe ser configurado con las puntas o tags de todas las indicaciones, alarmas y comandos programados en la RTU o al menos los mínimos requeridos por el Centro de Control, así como las políticas de seguridad DCOM. Una vez hecho todo correctamente, la información está disponible en la red operativa de las subestaciones. La comunicación con el WIOS se muestra en la Figura # 4-20 CAPITULO 4: DESARROLLO DEL PROYECTO 74 Figura # 4-20 Comunicación SICAM – OPC – WIOS Si se desea incorporar IEDs compatibles con la IEC-61850 dentro de una subestación manejada por una SICAM, el esquema sugerido se muestra en la Figura # 4-21. Aquí la SICAM es considerada como un IED que, mediante el uso de un gateway ubicado en el HMI, es capaz de intercambiar información con los otros IED conectados al bus de estación. 75 CAPITULO 4: DESARROLLO DEL PROYECTO RS-485 IED compatibl e con SICAM RS-232 Adaptador MPI SICAM IED compatibl e con SICAM HMI Gateway IEC 61850 OPC RS-232 BUS S/ E IED compatible con 61850 ... IED compatible con 61850 WIOS Router Red Operativa Subestaciones Eléctricas Figura # 4-21 Incorporación de equipos IEC-61850 en una subestación ya establecida Los datos tanto de la SICAM como los IED compatibles con la norma son procesados por el servidor OPC ubicado en el HMI, para posteriormente ser trasmitidos al WIOS. Sin embargo, al estar los IEDs conectados al bus de la S/E, el servidor OPC no interfiere con la posibilidad de programar los IED remotamente. b) S/E compatibles con IEC 61850: Si se parte desde cero en el diseño de una subestación utilizando la norma IEC-61850, no tiene sentido seguir utilizando una SICAM para enviar los datos al Centro de Control, en su lugar se debe utilizar una puerta de enlace o gateway que permita la comunicación con el Centro de Control. Actualmente existe una incipiente cantidad de productos en mercado que brinda conectividad OPC – IEC 61850, algunos están diseñados para correr dentro de un computador, ej. AX-MMS de SISCO, o como equipos separados: SICAM-PAS de Siemens, el communication center de ABB, entre otros. En la Figura # 4-22 se puede observar como sería el esquema de conexión típico propuesto de una subestación basada en la norma IEC61850. En esta figura se distinguen dos buses característicos de la norma: el bus de estación y el bus de proceso. 76 CAPITULO 4: DESARROLLO DEL PROYECTO Centro de Control Gateway OPC HMI Router Bus de Estación IEC-61850 Control Protección Protección y Control Bus de Proceso IEC - 61850 Control Protección Conexionado Tradicional Interfaz de Proceso Interfaz de Proceso Interfaz de Proceso Figura # 4-22 Subestación diseñada utilizando la norma IEC61850 En la Figura # 4-23 se muestra una representación física del ejemplo anterior. Puede observar como el bus de estación se implementa como una red tipo anillo, se sugiere utilizar fibra óptica por su inmunidad ante perturbaciones electromagnéticas y el asilamiento eléctrico que proporciona a los equipos. A esta red se incorporan switches que permiten la conexión de los IEDs de control y protección, el HMI y el gateway OPC, además permite la comunicación entre los mismos IED. Mediante un enrutador se enlaza la subestación a la red operativa de las S/E, logrando así la conexión con el Centro de Control y la Coordinación Técnica (COTE), el cual es el ente de la corporación encargado de llevar a cabo la coordinación y programación de las protecciones. CAPITULO 4: DESARROLLO DEL PROYECTO 77 Figura # 4-23: Propuesta de ABB para una subestacion compatible con la norma IEC-61850 [12] El bus de Proceso conecta los IEDs de Control y Protección con los dispositivos de medición y actuadores de los procesos de campo (CT, VT, interruptores, reclosers, etc.), si bien la norma sugiere que este bus sea Ethernet, puede coexistir con el cableado tradicional. Aunque el dispositivo de campo esté conectado físicamente a un solo IED, su información puede ser accedida por los demás utilizando el mecanismo de Eventos Genéricos de Subestación (GSE por sus siglas en inglés, anteriormente conocido como GOOSE). Es así como la disponibilidad de los datos se incrementa de una manera significativa sin aumentar significativamente los costos de conexión. Esto permite que esquemas de control anteriormente muy complejos o costosos de implementar, puedan ser incorporados dentro de una subestación compatible con la norma IEC-61850. Debido a la naturaleza auto-descriptiva del Lenguaje de Configuración de Subestación (SCL), basado en XML e incorporado por la norma IEC-61850, la configuración del gateway se simplifica. Ya no se requiere traducir puntos de conexión a etiquetas OPC, la norma refuerza el uso de las etiquetas, por lo que el gateway puede hacer una exploración local de todos los items disponibles dentro de los equipos presentes en la S/E que soporten la autodescripción y ponerlos a disposición del Centro de Control. Esto reduce la necesidad de enviar personal a la S/E a modificar tablas CAPITULO 4: DESARROLLO DEL PROYECTO 78 cada vez que el Despacho requiera un nuevo parámetro. A través de la utilidad de configuración WIOS puede explorarse los datos de la S/E y redefinir la etiqueta SCADA para que se refleje el nuevo valor. CAPÍTULO 5. 5.1 PRUEBAS Y RESULTADOS Introducción: En este capítulo se explican las pruebas que verifican el funcionamiento del prototipo desarrollado. Luego de comprobar la comunicación de la interfaz WIOS con diferentes servidores OPC genéricos se comprueba la comunicación de la interfaz SIOS con el sistema Softbus. Luego se verifica la funcionalidad tanto de la utilidad de configuración del WIOS como del protocolo de comunicación PIOS y la integración de ambas interfaces desarrolladas para completar la comunicación OPC <-> Sinaut Spectrum. 5.2 Pruebas Realizadas: Las pruebas llevadas a cabo y los resultados obtenidos se describen de inmediato: a) Pruebas configuración de WIOS con OPC: El sistema WIOS se probó utilizando un servidor OPC genérico de demostración (Demo), debido a que la empresa no dispone aun de servidores de prueba certificados. El demo utilizado fue el OPCLabs.Kitserver.2 de la compañía OPCLabs. Este demo, aún cuando contaba con limitaciones tale como: límite de tiempo número máximo de items, número de conexiones simultáneas, etc.; sirvió para los fines demostrativos del proyecto. Figura # 5-1 Exploración del Servidor OPC utilizando la UC IOS Mediante la utilidad de configuración (UC) se comprobó que es posible hacer una exploración (“browsing”) de los items disponibles en los servidores OPC. La Figura CAPITULO 5: PRUEBAS Y RESULTADOS 80 # 5-1 muestra la ventana de la UC que permite hacer esta exploración y posteriormente agregar los items a la Base de Datos, para más información sobre el funcionamiento de la UC véase su manual de usuario en el Anexo A. La inclusión en la base de datos de los items funcionó sin problemas. Sin embargo, por una limitación de software sólo pueden visualizarse alrededor de 150 items debido a que el control Vertical Scroll Bar de Visual Basic 6 solo permite desplazarse un numero de 215 píxeles, dado que la altura de cada fila es de aproximadamente 215px se tiene que el espacio de pantalla sóo puede albergar aproximadamente 151 líneas. La solución a este problema sería mostrar los datos de forma parcial o remplazar el control Vertical Scroll Bar, sin embargo para los efectos del prototipo 150 items es una cantidad suficiente. Cabe acotar que en la base de datos no existió nunca esta limitación. La UC permite también realizar una validación de los items OPC con la finalidad detectar errores en la conexión o en la sintaxis de las etiquetas asociadas, alertando al usuario y permitiéndole realizar los cambios que resulten convenientes; además como valor agregado, al momento de hacer esta validación la UC obtiene los permisos de lectura/escritura de cada ítem directamente desde su servidor OPC asociado b) Prueba de comunicación del SIOS con Softbus: El Sinaut Spectrum posee una utilidad de monitoreo del sistema Softbus llamada BUM (Bus Manager), en esta utilidad se observa el tráfico en las direcciones Softbus que estén activas en el sistema en ese momento. Por razones de confidencialidad no es posible detallar aquí los procedimientos a seguir para la configuración de la utilidad y las pantallas de información que genera; sin embargo, se puede decir que utilizando esta utilidad se comprobó que la información enviada por el SIOS a través del Softbus llegaba a su destino. Es importante destacar que el servidor del que disponíamos no se encontraba en completo funcionamiento y lográndose activar el SCADA en dicho servidor con funcionalidad limitada. Se logró que uno de los subsistemas funcionales fuese el Softbus, sin embargo la inserción de datos por parte del Softbus al sistema no se pudo comprobar debido a que la base de datos del servidor de pruebas no se encontraba en funcionamiento. La implementación de la parte de la interfaz correspondiente al envío de comandos desde el SCADA a los sistemas de campo no fue posible debido a que no se pudo conocer el mecanismo interno propietario de Siemens a través del cual son 81 CAPITULO 5: PRUEBAS Y RESULTADOS enviados estos comandos (Dirección de Softbus, Tipo de Orden, petición, etc.) y por lo tanto no se encontró manera de interceptar estas llamadas que hace el sistema cuando envía un comando a campo; sin embargo, con un sistema de pruebas completo, se podría analizar el proceso mediante distintas pruebas, la cuales no se podrían realizar en el sistema SCADA operativo por riesgo a indisponerlo temporal o indefinidamente, para conocer las variables que hacen falta para lograr la implementación de esta parte de la interfaz. Otra solución más sencilla sería llegar a un acuerdo con Siemens mediante el cual se pueda conocer de estos mecanismos internos y así poder hacer el desarrollo de la interfaz. c) Prueba del sistema IOS: Luego de haber realizado pruebas por separado a cada una de las partes que comprenden el sistema se realiza una prueba al sistema completo para verificar la correcta integración y el desempeño del mismo. La prueba consiste en dejar por una hora el sistema en funcionamiento, es decir, el demonio SIOS ejecutándose en el servidor UNIX con el sistema SCADA funcionando con ciertas limitaciones como se mencionó anteriormente, el demonio WIOS ejecutándose en el computador PC. El WIOS obtiene datos de un servidor OPC genérico y los transmite a través de una conexión PIOS al SIOS el cual los transmite al Softbus y los registra en logs archivados en el servidor UNIX. En la Figura # 5-2 se muestra un extracto de un archivo de registro de la aplicación SIOS. Esta prueba se realizó con el WIOS configurado para interrogar y transmitir 30 (treinta) items del servidor OPC genérico. Si bien el WIOS no presenta una interfaz de visualización, el servidor OPCLabs.Kitserver.2 genera datos siguiendo ciertos patrones tales como incrementos de uno a uno en ciertas variables, y funciones matemáticas en otras. Durante una (1) hora los programas se ejecutaron sin problemas. Al Comparar la información mostrada en el registro de la Figura # 5-2 con los datos generados por el servidor OPC OPCLab.KitServer.2 mostrados en la Tabla # 5-1 se puede comprobar que no existen discrepancias entre los mismos, efectivamente la transmisión e interpretación de los datos se logró sin errores. Tuesday, January 11 @ 09:34:03:23 => Connected to host 128.7.0.124 on port 5500 Tuesday, January 11 @ 09:34:05:40 => Conection Confirmed 82 CAPITULO 5: PRUEBAS Y RESULTADOS Tuesday, January 11 @ 09:36:02:12 => 0 @ D_BOS.24ST.ZAPAR.CB1.STATUS Tuesday, January 11 @ 09:36:03:09 => 28.84944 @ D_BOS.24ST.ZAPAR.CB1.TEMP Tuesday, January 11 @ 09:36:03:45 => 0.1953125 @ D_BOS.24ST.ZAPAR.CB1.P Tuesday, January 11 @ 09:36:04:32 => -0.290290128774727 @ D_BOS.24ST.ZAPAR.CB1.V Tuesday, January 11 @ 09:36:05:01 => -0.290290128774727 @ D_BOS.24ST.ZAPAR.CB1.I Tuesday, January 11 @ 09:36:05:54 => 0 @ D_BOS.24ST.ZAPAR.CB1.STATUS Tuesday, January 11 @ 09:36:06:29 => 28.91542 @ D_BOS.24ST.ZAPAR.CB1.TEMP Tuesday, January 11 @ 09:36:06:50 => 0.295312404632568 @ D_BOS.24ST.ZAPAR.CB1.P Tuesday, January 11 @ 09:36:07:17 => -0.290287679937688 @ D_BOS.24ST.ZAPAR.CB1.V Tuesday, January 11 @ 09:36:07:49 => -0.290287679937688 @ D_BOS.24ST.ZAPAR.CB1.I Tuesday, January 11 @ 09:38:57:42 => 1 @ D_BOS.24ST.ZAPAR.CB1.STATUS Tuesday, January 11 @ 09:38:58:25 => 28.97968 @ D_BOS.24ST.ZAPAR.CB1.TEMP Tuesday, January 11 @ 09:38:59:07 => 0.395312547683716 @ D_BOS.24ST.ZAPAR.CB1.P Tuesday, January 11 @ 09:38:59:51 => -0.290285231098748 @ D_BOS.24ST.ZAPAR.CB1.V Tuesday, January 11 @ 09:39:00:34 => -0.290285231098748 @ D_BOS.24ST.ZAPAR.CB1.I Figura # 5-2 Archivo de Registro SIOS Luego se realizó la misma prueba incrementando la cantidad de items a interrogar por el WIOS. Se configuró en WIOS para un cantidad de items de cuarenta (40), sesenta (60), ochenta (80) y cien (100). En este punto el WIOS comenzaba a presentar problemas de memoria y al cabo de un tiempo corto la aplicación se interrumpía. Entre las causas posibles para este comportamiento encontramos que la PC en la que se ejecutaba el programa disponía de 64MB de RAM, disponiendo de más memoria RAM posiblemente se podría configurar el sistema para interrogar una cantidad mayor de items. ITEM Valor 1 Valor 2 Valor 3 0 0 1 28.84944 28.91542 28.97968 0.1953125 0.2953124046325 0.3953125476837 Trends.Sine (1 s) -0.290290128774 -0.290287679937 -0.290287679937 Trends.Ramp (1 min) -0.290290128774 -0.290287679937 -0.290285231098 Greenhouse.Sprinklers Greenhouse.Temperature Trends.Ramp (10 s) Tabla # 5-1 Valores generados por el servidor OPC genérico CAPÍTULO 6. CONCLUSIONES Y RECOMENDACIONES La conclusión fundamental de este proyecto es que la aplicación de protocolos de última generación a la comunicación entre subestaciones eléctricas y el centro de control no se limita al desarrollo de convertidores de protocolo, sino que involucra tanto cambios al sistema SCADA actual como la incorporación de equipos a las S/E. La utilización de OPC representa una solución viable y factible para la comunicación entre subestaciones y el centro de control pues permite la interconexión de sistemas heterogéneos. OPC abre las puertas del mundo TCP/IP, incorporando las ventajas de una tecnología económica, ampliamente probada y estandarizada, en constante desarrollo y no propietaria. Para lograr el desarrollo de una interfaz que permitiese la comunicación con el sistema SCADA fue necesaria la utilización de los mecanismos provistos por el Softbus; sin embargo, el manejo de esta herramienta no es suficiente para lograr la construcción de una interfaz completamente funcional, se requiere hacer ligeros cambios en el Spectrum que permitan la correcta incorporación de la interfaz IOS como un módulo funcional del sistema. Las restricciones de propiedad intelectual requieren que la realización de estas modificaciones sea hecha en colaboración con el personal de Siemens. El desarrollo de una interfaz que permita la incorporación del protocolo OPC al sistema SCADA Sinaut Spectrum implica un beneficio directo para la corporación en cuanto a alargar el tiempo de vida útil del sistema, pues OPC permite incorporar interfaces a diferentes protocolos, tanto los previstos en la norma IEC-61850, como los ya establecidos IEC-101, DNP3, IEC-104, eliminando las limitaciones de comunicación presentes en el sistema actual. Las funcionalidades que adicionalmente provee IEC-61850, tales como Oscilografía y Programación Remota de las Protecciones, ameritarán conexiones de un ancho de banda mayor que las conexiones seriales actualmente utilizadas. El uso del OPC permitirá aprovechar estas nuevas conexiones, unificando los mecanismos de comunicación utilizados en la corporación. Esto conlleva a su vez una reducción de los gastos de soporte, mantenimiento y ampliaciones de la red. CAPITULO 6: CONCLUSIONES Y RECOMENDACIONES 84 Se logró cumplir con los objetivos planteados al inicio del trabajo, al sugerir esquemas para el desarrollo de una interfaz funcional y operativa. Sin embargo, este desarrollo requerirá de la colaboración de Siemens, una empresa externa a la corporación, un estudio profundo de los mecanismos de comunicación OPC (DCOM, Interfaz Custom) y de un banco de pruebas apropiado. A continuación se presentan unas recomendaciones para cualquier desarrollo futuro que se piense hacer con el sistema Sinaut Spectrum. La habilitación de un sistema de pruebas para el Spectrum es sumamente necesaria para cualquier desarrollo que se pretenda hacer con el sistema. Este permitirá llevar a cabo pruebas que no son posibles de realizar en la red operativa dado el riesgo de indisponibilidad que esto representa en un sistema que controla un servicio de vital importancia para la comunidad, como lo es la energía eléctrica. Además, permitiría a la empresa una disminución en el costo de soporte técnico prestado por Siemens y la posibilidad de entrenar personal local para el mantenimiento de las aplicaciones del sistema. Aprovechar el desarrollo de la tecnología OPC basada en XML. Al ser XML un lenguaje abierto permitirá omitir la complejidad y los problemas de configuración asociados a la tecnología DCOM. Ademas como XML es un lenguaje multiplataforma por naturaleza, podrían fusionarse las interfaces SIOS Y WIOS. Esto redundaría en un aumento en el desempeño y disminuiría la complejidad del proyecto. Sin embargo, esto ameritaría un estudio previo del OPC XMLDA, el cual está actualmente en una etapa inicial de su desarrollo. Dada la importancia estratégica inherente a la industria eléctrica, se sugiere luego de un desarrollo más profundo, implementar la propuesta planteada en la creación de un centro de control alternativo ubicado fuera de las instalaciones de Centro de Control Caujarito, único con que cuenta actualmente la corporación. Una indisponibilidad del Centro de Control causada por alguna fuerza mayor, catástrofe natural, sabotaje, etc. causaría una incapacidad de controlar el proceso, traduciéndose en pérdidas para la empresa, y una suspensión del servicio eléctrico a la comunidad. En estos casos, un centro de control alternativo puede tomar el control del proceso mientras se restituye el funcionamiento del centro de control principal. REFERENCIAS BIBLIOGRÁFICAS [1] Acosta, N., TCP-IP Obtenido el 11 de Marzo de 2005 en http://www.itlp.edu.mx/publica/revistas/revistali/anteriores/marzo99/nora.html [2] Batista, M. A., & Chávez, J. D. 2004. Impacto de la estandarización de sistemas automatizados en subestaciones eléctricas de la Corporación ENELVEN. Plan Rector”. Trabajo Especial de Grado. Universidad Rafael Urdaneta, Facultad de Ingeniería, Escuela de Eléctrica, Maracaibo. [3] CERN. A brief introduction to OPC Data Access. http://itcofe.web.cern.ch/itcofe/Services/OPC/GeneralInformation/Specifications/ RelatedDocuments/DASummary/DataAccessOvw.html [4] Cliente-servidor. Obtenido el 6 de abril de 2005, de http://es.wikipedia.org/wiki/Cliente-servidor [5] Conceptos Básicos de Redes. Obtenido el 6 de Abril de 2005 de http://coqui.metro.inter.edu/cedu6320/mlozada/redtopol.htm [6] Data Access Automation Interface Standard. OPC Foundation, 2005 [7] Database management moves into the Grid http://expresscomputeronline.com/20040329/dms01.shtml [8] DCOM, Terra. http://www2.terra.com/informatica/que-es/dcom.cfm [9] Documentación de SIEMENS (2000). SCADA and Energy Management System Sinaut Spectrum: Spectrum Base. Versión 4.1. SIEMENS, División de Sistemas de Potencia y Control. [10] Documentación de SIEMENS (2000). SCADA and Energy Management System Sinaut Spectrum: Spectrum Administrator. Versión 4.1. SIEMENS, División de Sistemas de Potencia y Control. [11] Documentación de SIEMENS (2000). SCADA and Energy Management System Sinaut Spectrum: Utility Guide. Versión 4.1. SIEMENS, Dvisión de Sistemas de Potencia y Control, 2000. [12] Florez, M. (2004). Communication Trends in Substation Automation IEC 61850, ABB Switzerland Ltd, IV Jornada de Actualización Tecnológica. PROCEDATOS [13] Free Demo Server Software. The Net is the Automation. http://nettedautomation.schwarz-interactive.de [14] Gerónimo, A. “Modelo OSI” http://www.monografias.com/trabajos13/modosi/modosi.shtml [15] Gimeno, D., & Ángel, F. COM/DCOM en C++ http://usuarios.lycos.es/andromeda_studios/paginas/tutoriales/articulo02.htm 86 REFERENCIAS BIBLIOGRÁFICAS [16] Lange, J. OPC — A standard for the practice. Softing GmbH. [17] Mateo, C., & Talavera, J. (2004) Los sistemas eléctricos ante un nuevo cambio. El impacto de la norma CEI 61850. Anales de Mecánica y Electricidad. Revista de la asociación de Ingenieros del I.C.A.I. Fascículo Número II (Marzo-Abril 2004). http://www.icai.es/publicaciones/index.php?fascic=II&anyo=2004 [18] OPC. Obtenido el 10 de Octubre de 2004 de: http://www.automatas.org/redes/opc.htm. Última modificación: 30 de marzo de 2003. [19] ¿Qué es OPC?, METALOGIC http://www.visualopcbuilder.com/tecnologia/tutorial1.htm [20] Redes, Clasificación por Topología. Obtenido el 6 de Abril de 2005 de http://www.tecnotopia.com.mx/redes/redtiptopologia.htm [21] RFC: 793 Transmission Control Protocol DARPA Internet Program Protocol Specification. (Septiembre de 1981) Information Sciences Institute University of Southern California. [22] SISTEMAS SCADA, www.automatas.org/redes/scada.htm. Última modificación: 30 de marzo de 2003 [23] Sitio Web Matrikon: http://www.matrikonopc.com/products/opcdrivers/details.asp?driver=1438 [24] Udren, E., Kunsman, communication S., & standardization Dolezilek, D. (2000). developments. Significant Western Power substation Delivery Automation Conference. http://www.selinc.com/techpprs/6105.pdf [25] Versiones preliminares de muestra de la norma IEC 61850. Sitio Web IEC. www.iec.ch [26] XML Site, “Introducción al XML”. http://www.xml.com.ve/que_es_xml.htm ANEXOS Anexo A MANUAL DE USUARIO UTILIDAD DE CONFIGURACION IOS Como complemento a modulo WIOS propuesto en el Proyecto IOS se ha desarrollado una utilidad de configuración externa. Esta utilidad es la encargada de configurar las listas de servidores OPC (ubicados en las Subestaciones) a los cuales el WIOS debe solicitar datos, cuales de todos los ítems presentes en cada servidor son relevantes para el SCADA y con que etiquetas deben ser introducidos al sistema SCADA. Una vez iniciada la aplicación se hará visible en pantalla la ventana mostrada en la figura 1. Esta es la ventana de presentación de la aplicación en ella puede observarse dos elementos en la barra de Menú: Servidores y Configuración. El primero esta relacionado con la búsqueda de servidores dentro de la red, mientras el segundo con la configuración de los ítems que deben ser enviados al Figura 1: Inicio de Aplicación SCADA. MANEJO DE SERVIDORES Bajo el menú servidores aparece el submenú Manejo de Servidores. Pulsando este submenú se muestra un formulario (ver Figura 2) que permite buscar servidores OPC en una red, así como también visualizar y modificar la lista de servidores almacenada en la Base de Datos (BD). A continuación se describirán las operaciones típicas que pueden llevarse a cabo dentro de este formulario Figura 2: Manejo de Servidores Buscar servidores OPC en la red Paso 1: Si desea buscar servidores OPC en la red solo debe ingresar la dirección del nodo1 que desee explorar. Podrá ver como el botón que se encuentra inmediatamente debajo va cambiando para indicar que la búsqueda se realizará en el nodo recién escrito. Si desea explorar la maquina local deje la casilla nodo en blanco. Paso 2: Una vez completada2 la exploración aparecerá un mensaje en pantalla indicándole el número de servidores encontrados. Para visualizarlos desplácese por la lista servidores ubicada en la esquina superior izquierda del formulario. Si la exploración no es exitosa se mostrara un mensaje describiendo el error. Paso 3: Ya completada la exploración, es posible probar la conexión con un servidor. Para ello seleccione un servidor de la lista servidores y luego pulse sobre el botón Prueba de Conexión. De ser exitosa la conexión podrá observar, a la derecha del botón, el nombre del servidor (PROGID), el nombre del vendedor o fabricante, la fecha en que fue iniciado, y cuanto tiempo lleva corriendo desde entonces. Todo esta información esta relacionada sobre el servidor OPC en prueba. Paso 4: Una vez probada la conexión puede detenerla volviendo a hacer clic en el botón ahora llamado Finalizar Prueba. Manejar la lista de servidores registrados En la esquina inferior izquierda se encuentra la lista de servidores registrados, es decir aquellos que ya han sido introducidos a la base de datos del WIOS. Agregar un servidor: Paso 1: Asegúrese de haber completado la sección anterior y de haber encontrado al menos un servidor OPC en el nodo seleccionado. De no hacerlo el botón Agregar Servidor se mostrara deshabilitado. Paso 2: Seleccione un servidor de la lista servidores. Paso 3: Presione el botón Agregar Servidor. 1 En la terminología OPC nodo se refiere al equipo en la red que este corriendo un servidor OPC, por lo tanto la dirección del nodo es la dirección de un equipo en la red. Comúnmente se utilizará la dirección IP del equipo, sin embargo OPC también puede trabajar con los nombres de las maquinas en caso de haber un servidor de dominio. En caso de duda consulte su departamento de informática. 2 Al explorar un nodo remoto el proceso puede demorarse dependiendo de la velocidad y congestionamiento de la red Luego de este último paso se agregara automáticamente el servidor seleccionado a la lista de servidores registrados. Eliminar un servidor: Paso 1: Seleccione un servidor de la lista de Servidores Registrados. Paso 2: Presione el botón Eliminar Servidor Paso 3: Aparecerá un mensaje de advertencia solicitando su confirmación para eliminar el servidor Advertencia: Al borrar un servidor, automáticamente se borraran todos aquellos ítems asociados a dicho servidor. Esta acción es irreversible y en caso de volver a agregar el servidor deberá agregar de nuevo todos los ítems. MANEJO DE SERVIDORES LOS ITEMS ASOCIADOS A LOS Bajo el menú configuración aparece el submenú Tabla de Conversión (ver Figura 3). Al pulsar este submenú se muestra un formulario que permite manejar lo relacionado a los ítems OPC disponibles dentro de los Servidores OPC. Aquí es posible agregar nuevos ítems, remover los ya existentes, validarlos y actualizar la base de datos. Este formulario incorpora una barra de menú diferente a la ventana de inicio. Esta posee tres menús: ActualiFigura 3: Tabla de Conversión zar BD, Validar Ítems y Modificar Actualizar la Base de Datos A continuación se presentaran formas de trabajar con la lista de ítems que el sistema WIOS debe manejar, luego de realizar cualquier modificación esta solo será efectiva cuando se pulse en el menú Actualizar BD. De no hacerlo los cambios se perderán al cerrar el formulario. Agregar un Ítem Existen dos formas de agregar ítems a la tabla de conversión, manual o asistidamente explicaremos a continuación cada una. Agregar un ítem de forma automática Paso 1: Bajo el menú Modificar encontrara la opción de Agregar Asistidamente, al hacer clic en esta opción se nos muestra un nuevo formulario (ver Figura 4). Paso 2: Ya en el formulario Agregar Ítems debe seleccionar un servidor de la lista ubicada en la esquina superior derecha. Esta lista es una copia de la lista de servidores registrados. Una vez seleccionado un servidor, debe hacer clic en el botón Buscar Ítems ubicada inmediatamente a la derecha de la lista. Paso 3: Si la exploración se completa exitosamente notara que en el recuadro inferior izquierdo se muestran el nombre del servidor OPC seleccionado3. Aquí es posible desplegar las ramas presentes en el servidor OPC Al hacer doble clic sobre el nombre podrá explorar todas las ramas existentes en el servidor. Figura 4: Formulario Agregar Ítems Paso 4: Al seleccionar una rama que posea ítems disponibles, estos se mostrarán en lista ubicada a la derecha del árbol de ramas. Aquí podrán ser seleccionados al hacer doble clic o pulsar el botón “+” (previa selección resaltamiento del ítem). Nótese que la ubicación o “Path” del ítem se muestra a la derecha de la etiqueta Ubicación. Paso 5: Los ítems seleccionados se muestran en la lista ubicada a en la esquina inferior derecha. Si desea remover alguno solo debe hacer doble clic sobre el elemento a remover o seleccionándolo y luego pulsándole botón ”-” Paso 6: Una vez terminada la selección de los ítems pulse Aceptar para que estos sean incorporados a la tabla de conversión. Paso 7: De vuelta en la tabla de conversión debe completarse el la dirección tecnológica (B1, B2, B3, Elem, Info), el tipo de acceso que debe presentársele al SCADA, y el tipo de señal que representa el ítem OPC, todo esto se hace de forma manual. Paso 8: En este punto es posible cambiar manualmente, si el usuario lo desea, el servidor asociado al ítem, así como también el nombre del ítem 3 Una vez completada la exploración, la lista de servidores se deshabilita, si desea Agregar Ítems de otro servidor, finalice con el servidor actual, cierre el formulario agregar ítems y vuelva al paso 1 Paso 9: Para que los cambios se vean reflejados en la BD, debe pulsarse sobre el menú Actualizar BD. De no hacerlo se perderán los cambios al cerrar el formulario. Agregar un ítem de forma manual Paso 1: Bajo el menú Modificar encontrara la opción de agregar Manualmente, al hacer clic en esta opción se insertara una nueva línea en la tabla de conversión Paso 2: Debe seleccionarse el servidor OPC, el OPCid (etiqueta OPC), la dirección tecnológica, el tipo de acceso que debe presentársele al SCADA, y el tipo de señal que representa el ítem OPC. El tipo de Acceso propio del ítem OPC se obtiene al validar el Ítem y no puede incluirse manualmente. Paso 3: Para que los cambios se vean reflejados en la BD, debe pulsarse sobre el menú Actualizar BD. De no hacerlo se perderán los cambios al cerrar el formulario. Remover ítems Remover un ítem Paso 1: Seleccione un ítem haciendo marcando la casilla de verificación ubicada a la derecha de la línea. Puede seleccionar más de uno si lo desea. Paso 2: Pulse en el menú Modificar, y luego en Remover. El ítem es removido automáticamente de la BD, no es necesario actualizarla. Remover todos los ítems Si desea remover todos los ítems pulse en el menú Modificar, y luego en Remover Todos. Se presenta un mensaje solicitando la confirmación de la acción. Los ítems son removidos automáticamente de la BD, no es necesario actualizarla. Esta acción es irreversible Validar los Ítems Una vez modificada la tabla el usuario puede proceder a validar sus ítems. Al hacerlo la aplicación verificará la posibilidad de obtener información de los Servidores OPC. Al hacer clic el menú validar ítems un nuevo formulario se abrirá para mostrar el pro- greso y los resultados de la validación (ver Figura 5 ). Una vez culminada se podrá observar en la tabla de conversión que los ítems han sido resaltados con tres posibles colores. En la Tabla 1 se muestran el significado de este código de color. Luego de observar los resultados el usuario puede editar la tabla de conversión y volver a correr la rutina de validación tantas veces como sea requerido. En el formulario de resultados se encuentran tres botones que permiten copiar al portapapeles el reporte generado, borrar dicho reporte o cerrar el formulario. Una vez cerrado el formulario de resultados, la tabla de conversión es Figura 5: Resultados de la Validación restaurada a su forma original. Recuerde actualizar la BD una vez concluido el proceso de validación. SIGNIFICADO COLOR SERVIDOR ITEMS Verde Servidor encontrado y conectado Ítem validado exitode forma exitosa samente. Amarillo No pudo encontrarse al servidor en No se tiene informala red. ción sobre los ítems Rojo Error de conexión con el servidor. No se encuentra un Los ítems asociados a este servidor ítem con el OPCid serán resaltados en amarillo especificado. Tabla 1: Código de Colores utilizado en la validación de los Ítems CONFIGURACIÓN DE LOS FUNCIONAMIENTO DEL WIOS PARÁMETROS DE Bajo el menú configuración aparece el submenú WIOS. Al pulsar este submenú se muestra un formulario (ver Figura 6) que permite modificar los parámetros de funcionamiento del WIOS: la dirección IP del SIOS, y la tasa de refrescamiento requerida deseada. Bajo la sección de Parámetros Actuales se muestra los últimos valores ingresados (si no se ha ingresado valores antes estos campos aparecerán vacíos) Luego esta la sección de Cambiar Parámetros, aquí puede modificar los valores de los cuadros de texto, con los valores requeridos. Una vez modificados, puede hacer clic en aplicar o aceptar para proceder a guardar los cambios hechos. En caso de no desear realizar los cambios pulse el botón Cancelar 4 Figura 6: Configuración de WIOS 4 Si ya ha pulsado aplicar los cambios no pueden ser revertidos pulsando cancelar, debe modificar los campos con los valores anteriores y volver a pulsar aplicar o aceptar Instrucciones de uso de la interfaz SIOS. Para correr el demonio ejecutar: ios Para detener la ejecución del demonio: ios exit Para abrir una consola de pruebas: ios test [debug level 0 -> 3] El nivel de debug indica que tanto feedback habrá por parte del programa, un nivel de debug más bajo implica que solo se registraran las acciones y errores más críticos, mientras un nivel de debug alto implica que se registra cualquier acción que tome el programa y cualquier error ocurrido, incluso los datos transmitidos se registran. Desde esta consola de pruebas se pueden ejecutar los diferentes hilos y secuencias del programa manualmente, a continuación una lista de comandos ejecutables desde la consola de pruebas: Srv [puerto > 1500]: Coloca al programa en modo espera por conexión PIOS en el puerto indicado por “puerto”. Ejemplo: “srv 5500” Coloca al programa a escuchar en el puerto 5500 por una conexión PIOS. quit: Cierra todos los hilos y termina el programa. cls: Limpia la pantalla. Con [puerto] [ip]: Intenta una conexión PIOS a través del puerto puerto a la dirección ip ip. Ejemplo: “con 5500 127.0.0.1” intenta conectarse a través del puerto 5500 a la dirección 127.0.0.1 Desp: Comienza el hilo Despachador. Recv: Comienza el hilo Receptor de PIOS. Send: Envía un paquete de prueba a la conexión PIOS ya establecida. Xdesp: Cierra el hilo despachador. Xrecv: Cierra el hilo Receptor. Disc: Desconecta la conexión PIOS activa. Xsrv: Sale del modo espera por conexión. Stat: Indica el status de la consola de pruebas, que hilos están activos, cual es la conexión válida. Omq: Activa el mecanismo de cola de mensajes. ANEXO C. Código Fuente S.I.O.S. Letras en formato itálico son comentarios, las líneas que comiencen con dos asteriscos (***) son descripción de código omitido por razones de confidencialidad. C.1 .- ios.h (Encabezado de toda la aplicación). ***INCLUSIÓN DE ENCABEZADOS DEL SPECTRUM. //inclusion de encabezados utilizados: #include <signal.h> #include <mqueue.h> #include <stdio.h> #include <stdlib.h> #include <errno.h> #include <thread.h> #include <semaphore.h> #include <time.h> #include <sys/types.h> #include <unistd.h> #include <sys/stat.h> #include <poll.h> //definiciones de constantes: #define LOGFILE "../logs/IOSlog" //archivo de registro de mensajes. #define ERRFILE "../logs/IOSError" //archivo de registro de errores. #define DEBFILE "../logs/IOSDebug" //archivo de registro de mensajes de depuración. //Opciones de configuración SIOS. #define CHECKSOCK 1 #define CONFIRM 1 #define SBUS_ON 1 #define NIM_ON 0 #define PRINTOUT 1 #define PRINTFILES 1 //activar o desactivar revisión de la conexión antes de envío. //activar o desactivar confirmación de conexión PIOS. //Activar o desactivar funciones de Softbus. //Activar o desactivar funciones de Base de Datos Spectrum. //Activar o desactivar salida de mensajes en la consola. //Activar o desactivar salida de mensajes a los archivos de registro. ***DEFINICIONES RELACIONADAS CON EL SPECTRUM #define GetValue #define PutValue #define PrintMsg #define EndRecv 1 2 3 -1 int debug = 0; //Nivel de depuración. typedef struct{ tNimVar NimVar; tNamVar NamVar; tTaName TaName; tTa Ta; }OPCRequest; //Estructura OPC Request para manejo de peticiones #define MAXREQUESTS 20 //maximo numero de peticiones simultaneas /*Variables Globales del Spectrum*/ int RequestUse[MAXREQUESTS]; OPCRequest request[MAXREQUESTS]; //variable auxiliary para manejo interno. //variable auxiliary para manejo interno tNamVar GNamVar; tNimVar GNimVar; SbAddressS SbAddressS ObjectIdS /*Para la transformacion de Nombre a Numero de la base de datos*/ /*Variable para acceso a las relaciones*/ SbAddTra = SBATRA; SbAddRec = SBAREC; MyObj = OBID; /*Direccion de Softbus principal del Programa*/ /*Object ID de el programa principal*/ ***VARIABLES NECESARIAS PARA USO DE LAS INTERFACES SOFTBUS. long float double SwVal; MVal; CVal; /*Valor del estado de un switch*/ /*Valor medido, real*/ /*Valor de un contador*/ long float double SwValdis; MValdis; CValdis; /*Valor del estado de un switch para distribucion*/ /*Valor medido, real para distribucion*/ /*Valor de un contador para distribucion*/ /*------------------------------------------------------------------------------------------------------*/ #define PORT 5500 #define MAXCONNECTIONS 5 //Puerto para la conexión PIOS //Maximo de conecciones simultaneas. #define TIMEOUT_CONFIRM 3 #define TIMEOUT_RECV 10 //timeout para realizar confirmación. //tiempo maximo de espera durante recepción //Definiciones del Protocolo IOS. #define IOS_FRAME_START 0xF0AE #define IOS_FRAME_END 0xEBC4 #define IOSMaxBlocks 10 //Seq. De inicio //Seq de finalización. //Maximo de bloques en un frame. #define H_LENGTH 18 #define MAX_LENGTH 1024 + H_LENGTH //largo de todo lo que no es data. incluyendo caracter de fin. //largo máximo del frame. //valores para el campo sender y receiver del frame PIOS. #define SBUS 1 #define OPC 2 #define COMMAND 3 #define IOSEXIT 55 //Errores #define ERROR_CONEXION -3 #define ERROR_CONFIRMACION -2 #define ERROR -1 #define OK -4 #define ERROR_TIMEOUT -5 #define MQ_IOS "/mqios" //nombre de la cola de mensajes, requerida por UNIX //------COMMANDoS----------// #define QUITDISPATCH 50 #define QUITCMD 51 #define QUITIOS 52 //------CONSOLE COMMANDS-------// #define InitServer 100 #define Connect 101 #define Quit 102 #define Clear 99 #define Despacho 103 #define Recibir 104 #define Send 105 #define XDespacho 106 #define XReceptor 107 #define Desconectar 108 #define XServer 109 #define Status 110 #define OpenQueue 111 //Definición del Bloque de datos PIOS typedef struct IOSDataBlock{ char control; //Tipo de accion requerida. char dtype; //tipo de datos en el bloque: neMeasVal, neCountVal, neSwitch. tTaName tadd; //direccion tecnologica en nombres, b1, b2, b3, elem, info. tTechData tdata; //Valor actual del dato, calidad, timestamp. }IOSDataBlock; #define IOSDBSize sizeof(IOSDataBlock) //macro para calcular tamaño del bloque de datos PIOS //Definición del frame PIOS typedef struct IOSframe{ short start; short len; char sender; char receiver; byte BlockCount; IOSDataBlock data[IOSMaxBlocks]; }IOSFrame; //Caracteres de Inicio de la trama. //2 bytes para el tamano de la trama incluyendo header. //Identificacion del Emisario. //Identificacion del Destinatario. //Enviar o requerir valor o comando. //bloques de datos a enviar; //macro para calcular tamaño del frame PIOS #define IOSFrameSize(N) sizeof(IOSFrame)-IOSDBSize*(IOSMaxBlocks-(N))+2 //+2 por el caracter final. //Control Options #define REQUEST 1 #define VALFROMOPC 2 #define VALFROMSCADA 3 C.2 .- error.c (Impresión y Registro de Errores de Softbus y Base de Datos). /*--------------------------------------------------------------------------------------------------------------FUNCTION: void SbError(int flag, char * func) DESCRIPTION: This fuction logs the softbus error description of the error represented by "flag". PARAMETERS: in: flag: Error number. func: Fuction that generated the error. RETURN: VARIABLES: ------------------------------------------------------------------------------------------------------------*/ void SbError(int flag, char * func) { sprintf(pb, "Error en: %s\n", func); print(err, 0, pb); switch (flag) { case: ***CODIGO DE ERROR DE SOFTBUS print(err, 0, "***DESCRIPCION DEL ERROR"); break; default: sprintf(pb, "Error no reconocido, Flag = %d", flag); print(err, 0, pb); break; } return; } /*--------------------------------------------------------------------------------------------------------------FUNCTION: void DbError(int flag, char * func) DESCRIPTION: This fuction logs the data base error description of the error represented by "flag". PARAMETERS: in: flag: Error number. func: Fuction that generated the error. RETURN: VARIABLES: ------------------------------------------------------------------------------------------------------------*/ void DbError(int flag, char * func) { sprintf(pb, "Error en: %s\n", func); print(err, 0, pb); switch (flag) { case: ***CODIGO DE ERROR DE BASE DE DATOS print(err, 0, "***DESCRIPCION DEL ERROR"); break; default: sprintf(pb, "Error no reconocido, Flag = %d", flag); print(err, 0, pb); break; } return; } C.3 .- funciones.c (Funciones Generales). /*-------------------------------------------------------------------------------FUNCTION: catch_sig. DESCRIPTION: This is the signal handler for various signals, UNIX calls this function when a singal sig_num arrives if this function has been registered as its signal hendler, see signal.h for more. PARAMETERS: int sig_num: Incomming signal to be handled, signal numbers specified in signal.h (3head) VARIABLES: none. --------------------------------------------------------------------------------*/ void catch_sig(int sig_num) { sprintf(pb, "Caught a signal %d", sig_num); print(deb, 2, pb); if (sig_num == SIGALRM); if (sig_num == SIG_MQ); if (sig_num == SIG_PAUSE) pause(); if (sig_num == IOSEND) exit(0); } /*-------------------------------------------------------------------------------- FUNCTION: print - Output manager for IOS. DESCRIPTION: Directs Output to correspondigns file and adds time stamp to each entry. If the debug level is not high enough for the message then it is not displayed or written to the file. PARAMETERS: FILE * out: File to which the output will go. int level: Debug level the message needs to be displayed. char * string: Message to be written to file. VARIABLES: time_t hora: Time Stamp for entrries. chat t: buffer for storing formated time and date. --------------------------------------------------------------------------------*/ void print(FILE * out, int level, char * string) { time_t hora; char t[50]; if (level > debug) return; time(&hora); sprintf(t, ctime(&hora)); t[strlen(t)-1] = 0; if (PRINTOUT) fprintf(stdout, "\n%s %d-> %s", t, pid_ios, string); if (PRINTFILES) fprintf(out, "\n%s %d-> %s", t, pid_ios, string); fflush(log); return; } /*-------------------------------------------------------------------------------FUNCTION: Pos. DESCRIPTION: Finds the position of any two bytes given in "X" in the string buf. P-IOS calls it to deecode the incomming frames, finding Start and End sequences within the frame. PARAMETERS: (i)char * buf: pointer to the string in wich to look for X. (i)short X: bytes to find within the string. (IOS_FRAME_START or IOS_FRAME_END). (i)int n: string length. RETURN: The funtion returns the position of the first byte of X in the string if X was found or -1 if it wasn't. VARIABLES: int i: counter. --------------------------------------------------------------------------------*/ int Pos(char * buf, short X, int n) { int i; for(i = 0; i<=n; i++) if (*(buf+i) == (char)hibyte(X)) if (*(buf+i+1) == (char)lobyte(X)) return(i); return(-1); } /*----------------------------------------------------------------------------------------------------------------FUNCTION: int daemon() DESCRIPTION: This fuction sets the program as a daemon process. PARAMETERS: RETURN: returns 1; VARIABLES: -----------------------------------------------------------------------------------------------------------------*/ int daemon() { print(stdout, 0, "Openning IOS Daemon\n"); if (fork()) return(0); setsid(); if (fork()) return(0); return(1); } /*----------------------------------------------------------------------------------------------------------------FUNCTION: void ios_exit() DESCRIPTION: This fuction shuts down S-IOS. PARAMETERS: RETURN: returns 1; VARIABLES: -----------------------------------------------------------------------------------------------------------------*/ void ios_exit() { kill(pid_receptor, SIGKILL); kill(getppid(), SIGKILL); sleep(1); exit(0); } /*----------------------------------------------------------------------------------------------------------------FUNCTION: void daemon_kill() DESCRIPTION: This fuction closes all processes related to the IOS daemon. PARAMETERS: RETURN: VARIABLES: -----------------------------------------------------------------------------------------------------------------*/ void daemon_kill() { mqd_t mq; iosOpenMQ(&mq); iosAddCmdMQ(&mq, QUITIOS); iosCloseMQ(&mq, false); return; } /*-------------------------------------------------------------------------------FUNCTION: CheckSocket. DESCRIPTION: Checks if a socket holds a valid connection. PARAMETERS: (i)int socket: Socket to check. RETURN: Returns -1 if invalid socket or not connected, returns 0 if socket is connected. VARIABLES: char name, ip: holder for remote ip and hostname. int port: holder for remote port. --------------------------------------------------------------------------------*/ int CheckSocket(int * socket) { char name[64], ip[16]; int port; if (!CHECKSOCK) return(0); if (getPeerInfo(*socket, name, ip, (short *)&port) == 0) { if (debug > 1) { print(deb, 1 ,"================================"); printf(pb,"Client hostname %s", name); print(deb, 1 , pb); printf(pb,"Client IP: %s", ip); print(deb, 1 , pb); printf(pb,"Client port: %d", port); print(deb, 1 , pb); print(deb, 1 ,"================================"); } } else { print(err, 0, "Error...Socket no valido o sin conexion"); return(-1); } return(0); } C.4 .- frames.c (Manejo de frames PIOS). /*-------------------------------------------------------------------------------FUNCTION: iosBuildFrame DESCRIPTION: It fills a frame structure with the required fields for sending it, such as Sender, Receiver, Start, End, length. The actual data is added by iosAddBlock and iosAddValue functions which should be called prior to this one. PARAMETERS: (i)char Sender: Sender of the message. (i)char Receiver: Receiver of the message. (i/o)IOSFrame frame: frame structure to fill. RETURN: VARIABLES: --------------------------------------------------------------------------------*/ void iosBuildFrame(char sender, char receiver, IOSFrame * frame) { frame->start = IOS_FRAME_START; frame->sender = sender; frame->receiver = receiver; frame->len = IOSFrameSize(frame->BlockCount); return; } /*-------------------------------------------------------------------------------FUNCTION: iosCharToFrame DESCRIPTION: Copies the data stream containing the receeived data to an IOSFrame structure. PARAMETERS: (i)char *data: Received Data. (o)IOSFrame frame: frame structure to fill. RETURN: VARIABLES: --------------------------------------------------------------------------------*/ void iosCharToFrame(char * data, IOSFrame * frame) { int i = 0, n=0; char * vlong = malloc(sizeof(long)); short * vshort = malloc(sizeof(short)) + sizeof(short); char * ptr = data; vlong = vlong + sizeof(long)-1; frame->start = *(short *)ptr; ptr = ptr + sizeof(short); frame->len = *(short *)ptr; ptr = ptr + sizeof(short); frame->sender = *ptr; ptr++; frame->receiver = *ptr; ptr++; frame->BlockCount = *ptr; ptr++; for (i = 0; i < frame->BlockCount; i++) { frame->data[i].control = *ptr; ptr++; frame->data[i].dtype = *ptr; ptr++; strncpy((char *)&(frame->data[i].tadd.B1), ptr, 8); ptr = ptr + 8; strncpy((char *)&(frame->data[i].tadd.B2), ptr, 8); ptr = ptr + 8; strncpy((char *)&(frame->data[i].tadd.B3), ptr, 8); ptr = ptr + 8; strncpy((char *)&(frame->data[i].tadd.Elem), ptr, 8); ptr = ptr + 8; strncpy((char *)&(frame->data[i].tadd.Info), ptr, 8); ptr = ptr + 8; *vlong = *ptr; vlong--; ptr++; *vlong = *ptr; vlong--; ptr++; *vlong = *ptr; vlong--; ptr++; *vlong = *ptr; ptr++; frame->data[i].tdata.Sec = *(long *)vlong; *vshort = *ptr; vshort--; ptr++; *vshort = *ptr; ptr++; frame->data[i].tdata.mSec = *vshort; frame->data[i].tdata.Qb0 = *ptr; ptr++; switch (frame->data[i].dtype) { case neSwitch: frame->data[i].tdata.Val.Vlong = *(long *)ptr; ptr = ptr + sizeof(long); break; case neMeasVal: frame->data[i].tdata.Val.Vfloat = *(float *)ptr; ptr = ptr + sizeof(float); break; case neCountVal: frame->data[i].tdata.Val.Vdouble = *(double *)ptr; ptr = ptr + sizeof(double); break; default: break; } } frame->len = IOSFrameSize(frame->BlockCount); return; } /*-------------------------------------------------------------------------------FUNCTION: FrameToChar DESCRIPTION: Copies the frame into a data buffer as a string of characters PARAMETERS: (i)char *data: Data Stream. (o)IOSFrame frame: frame structure to fill in to data stream. RETURN: VARIABLES: --------------------------------------------------------------------------------*/ void iosFrameToChar(char * data, IOSFrame frame) { char * ptr = data +2; int i = 0; short len; short end = IOS_FRAME_END; memcpy(ptr, &frame.start, sizeof(frame.start)); ptr = ptr + sizeof(frame.start); memcpy(ptr, &frame.len, sizeof(frame.len)); ptr = ptr + sizeof(frame.len); memcpy(ptr, &frame.sender, sizeof(frame.sender)); ptr = ptr + sizeof(frame.sender); memcpy(ptr, &frame.receiver, sizeof(frame.receiver)); ptr = ptr + sizeof(frame.receiver); memcpy(ptr, &frame.BlockCount, sizeof(frame.BlockCount)); ptr = ptr + sizeof(frame.BlockCount); for (i = 0; i < frame.BlockCount; i++) { memcpy(ptr, &frame.data[i].control, sizeof(frame.data[i].control)); ptr = ptr + sizeof(frame.data[i].control); memcpy(ptr, &frame.data[i].dtype, sizeof(frame.data[i].dtype)); ptr = ptr + sizeof(frame.data[i].dtype); memcpy(ptr, &frame.data[i].tadd.B1, 8); ptr = ptr + sizeof(frame.data[i].tadd.B1); memcpy(ptr, &frame.data[i].tadd.B2, 8); ptr = ptr + sizeof(frame.data[i].tadd.B2); memcpy(ptr, &frame.data[i].tadd.B3, 8); ptr = ptr + sizeof(frame.data[i].tadd.B3); memcpy(ptr, &frame.data[i].tadd.Elem, 8); ptr = ptr + sizeof(frame.data[i].tadd.Elem); memcpy(ptr, &frame.data[i].tadd.Info, 8); ptr = ptr + sizeof(frame.data[i].tadd.Info); memcpy(ptr, &frame.data[i].tdata.Sec, sizeof(frame.data[i].tdata.Sec)); ptr = ptr + sizeof(frame.data[i].tdata.Sec); memcpy(ptr, &frame.data[i].tdata.mSec, sizeof(frame.data[i].tdata.mSec)); ptr = ptr + sizeof(frame.data[i].tdata.mSec); memcpy(ptr, &frame.data[i].tdata.Qb0, sizeof(frame.data[i].tdata.Qb0)); ptr = ptr + sizeof(frame.data[i].tdata.Qb0); switch (frame.data[i].dtype) { case neSwitch: memcpy(ptr, &frame.data[i].tdata.Val.Vlong, sizeof(frame.data[i].tdata.Val.Vlong)); ptr = ptr + sizeof(frame.data[i].tdata.Val.Vlong); break; case neMeasVal: memcpy(ptr, &frame.data[i].tdata.Val.Vfloat, sizeof(frame.data[i].tdata.Val.Vfloat)); ptr = ptr + sizeof(frame.data[i].tdata.Val.Vfloat); break; case neCountVal: memcpy(ptr, &frame.data[i].tdata.Val.Vdouble, sizeof(frame.data[i].tdata.Val.Vdouble)); ptr = ptr + sizeof(frame.data[i].tdata.Val.Vdouble); break; default: break; } } memcpy(ptr, &end, sizeof(short)); len = ptr - data; memcpy(data, &len, sizeof(short)); return; } /*-------------------------------------------------------------------------------FUNCTION: iosAddValue DESCRIPTION: Adds a value specified in val to a data block. It specifies quality and data type also PARAMETERS: (i/o)IOSDataBlock block: DataBlock to add value to. (i)byte dtype: Data type of value. (i)void * val: Pointer to the variable containing the actual value to send.(variable type must match dtype to guarantee valid data (i)byte Quality: Quality of the value to send. RETURN: VARIABLES: --------------------------------------------------------------------------------*/ void iosAddValue(IOSDataBlock * block, byte etype, void * val, byte quality) { switch (etype) { case neSwitch: block->tdata.Val.Vlong = *(long *)val; break; case neMeasVal: block->tdata.Val.Vfloat = *(float *)val; break; case neCountVal: block->tdata.Val.Vdouble = *(double *)val; break; default: break; } GetTim(&(block->tdata.Sec), &(block->tdata.mSec)); //Coloca en Sec y mSec el time Stamp correspondiente. block->tdata.Qb0 = quality; block->dtype = etype; return; } /*-------------------------------------------------------------------------------FUNCTION: iosAddBlock DESCRIPTION: Adds a new block to "frame", it increments the BlockCount of the frame as well. PARAMETERS: (o)IOSFrame frame: frame structure to add block to. (i)IOSDataBlock: Block to add to frrame, containning 5 step TA plus value, quality and control byte. RETURN: VARIABLES: --------------------------------------------------------------------------------*/ void iosAddBlock(IOSFrame * frame, IOSDataBlock * block) { memcpy((char *)&(frame->data[frame->BlockCount]), (char *)block, IOSDBSize); frame->BlockCount++; return; } /*-------------------------------------------------------------------------------FUNCTION: iosPrintFrame DESCRIPTION: Prints a frame to the dedbug file specified in ios.h PARAMETERS: (i)IOSFrame frame: frame to print (i)int level: debug level needed for printing. RETURN: VARIABLES: --------------------------------------------------------------------------------*/ void iosPrintFrame(IOSFrame buf2, int level) { int i; char t[100]; /*sprintf(pb, " Start = %d", buf2.start); print(deb, level, pb); sprintf(pb, " Length = %d", buf2.len); print(deb, level, pb); sprintf(pb, " Sender = %d", buf2.sender); print(deb, level, pb); sprintf(pb, " Destin.= %d", buf2.receiver); print(deb, level, pb); sprintf(pb, " Count = %d", buf2.BlockCount); print(deb, level, pb); */ for(i = 0; i<buf2.BlockCount; i++) { sprintf(pb, " data[%d].control = %d", i, buf2.data[i].control); /*print(deb, level, pb); sprintf(pb, " data[%d].dtype = %d", i, buf2.data[i].dtype); print(deb, level, pb); sprintf(pb, " data[%d].tadd.B1 = %s", i, buf2.data[i].tadd.B1); print(deb, level, pb); sprintf(pb, " data[%d].tadd.B2 = %s", i, buf2.data[i].tadd.B2); print(deb, level, pb); sprintf(pb, " data[%d].tadd.B3 = %s", i, buf2.data[i].tadd.B3); print(deb, level, pb); sprintf(pb, " data[%d].tadd.Elem = %s", i, buf2.data[i].tadd.Elem); print(deb, level, pb); sprintf(pb, " data[%d].tadd.Info = %s", i, buf2.data[i].tadd.Info); print(deb, level, pb); */sprintf(t, ctime((time_t *)&buf2.data[i].tdata.Sec)); t[strlen(t)-1] = 0; sprintf(pb, "Time Stamp: %s", t); print(deb, level, pb); /*sprintf(pb, " data[%d].tdata.mSec = %d", i, buf2.data[i].tdata.mSec); print(deb, level, pb); sprintf(pb, " data[%d].tdata.Qb0 = %d", i, buf2.data[i].tdata.Qb0); print(deb, level, pb); */ switch (buf2.data[i].dtype) { case neSwitch: //sprintf(pb, " data[%d].tdata.Val.Vlong = %d", i, buf2.data[i].tdata.Val.Vlong); sprintf(pb, "%d @ %s.%s.%s.%s.%s", buf2.data[i].tdata.Val.Vlong, buf2.data[i].tadd.B1, buf2.data[i].tadd.B2, buf2.data[i].tadd.B3, buf2.data[i].tadd.Elem, buf2.data[i].tadd.Info); print(deb, level, pb); break; case neMeasVal: //sprintf(pb, " data[%d].tdata.Val.Vfloat = %f", i, buf2.data[i].tdata.Val.Vfloat); sprintf(pb, "%f @ %s.%s.%s.%s.%s", buf2.data[i].tdata.Val.Vfloat, buf2.data[i].tadd.B1, buf2.data[i].tadd.B2, buf2.data[i].tadd.B3, buf2.data[i].tadd.Elem, buf2.data[i].tadd.Info); print(deb, level, pb); break; case neCountVal: //sprintf(pb, " data[%d].tdata.Val.Vdouble = %f", i, buf2.data[i].tdata.Val.Vdouble); sprintf(pb, "%f @ %s.%s.%s.%s.%s", buf2.data[i].tdata.Val.Vdouble, buf2.data[i].tadd.B1, buf2.data[i].tadd.B2, buf2.data[i].tadd.B3, buf2.data[i].tadd.Elem, buf2.data[i].tadd.Info); print(deb, level, pb); break; default: break; } } //sprintf(pb, " End = %d\n", *(short *)&(buf2.data[i])); //print(deb, level, pb); return; } C.5 .- libproto.c (Manejo de protocolo PIOS). /*-------------------------------------------------------------------------------FUNCTION: iosStartServer. DESCRIPTION: Opens a socket, binds it to a port and blocks until a connection is available, then it confirms that it is in fact a P-IOS connection and then returns the connected socket. This function uses the ServerSocket and GetConnection functions from the msock library slightly modified to restrict creation of new threads for each connection. PARAMETERS: (i)int port: Local port in which to listen for connections. RETURN: The funtion returns the connected socket after confirming a valid P-IOS connection, it returns ERROR_CONEXION if the connection could'nt be succesfully established or ERROR_CONFIRMACION if the connection was established but not a valid P-IOS connection. VARIABLES: int socket: Socket for the connection. short rport: Remote port connected to the server. char rip, host: IP and hostname of the connected client. --------------------------------------------------------------------------------*/ int iosStartServer(int port, int max) { int socket; short rport; char rip[15], host[64]; print(err, 0, "IOS Waiting for connection..."); socket = ServerSocketIOS((u_short)port, max); if (socket==-1) return(ERROR_CONEXION); getPeerInfo(socket, host, rip, &rport); sprintf(pb, "\nConnecting to %s (%s) on port %d.....", host, rip, rport); print(err, 0, pb); if (CONFIRM) { if (IOSconfirm(socket)) return(socket); return(ERROR_CONFIRMACION); } return(socket); } int ServerSocketIOS(u_short port,int max_server) { int dummy=(-1); int sock_fd; u_short nport; nport=htons(port); sock_fd=getConnectionIOS(SOCK_STREAM,nport,&dummy,max_server); return (sock_fd); } int getConnectionIOS(int socket_type,u_short port,int *listener,int max_serv) { struct sockaddr_in address; int listeningSocket; int connectedSocket = -1; int newProcess; int one = 1; memset((char *) &address,0,sizeof(address)); address.sin_family=AF_INET; address.sin_port=port; address.sin_addr.s_addr=htonl(INADDR_ANY); listeningSocket=socket(AF_INET,socket_type,0); if (listeningSocket < 0) { (void) fprintf (stderr,"Unable to open socket!"); perror("socket"); exit (1); } if (listener != (int *) NULL) *listener=listeningSocket; setsockopt(listeningSocket,SOL_SOCKET,SO_REUSEADDR,(char *) &one, sizeof(int)); if (bind(listeningSocket,(struct sockaddr *) &address, sizeof(address)) < 0) { (void) fprintf (stderr,"Unable to bind to socket"); (void) fprintf (stderr,"Probably the port is already in use!"); (void) fprintf (stderr,"Or you do not have permission to bind!"); close(listeningSocket); exit (1); } if (socket_type == SOCK_STREAM) { listen(listeningSocket,max_serv); while (connectedSocket < 0) { connectedSocket=accept(listeningSocket,NULL,NULL); if (connectedSocket < 0) { if (errno != EINTR) { (void) fprintf (stderr,"unable to accept!"); perror("accept"); close (listeningSocket); exit (1); } else continue; /* don't fork, do the accept again*/ } close(listeningSocket); return (connectedSocket); } } } /*-------------------------------------------------------------------------------FUNCTION: iosRecvFrame. DESCRIPTION: This function reads the reception buffer and extracts a frame, anything in the buffer before the first valid frame will be discarded. If "timeout" seconds elapse and no data is received then the fuctions exits with ERROR_TIMEOUT. PARAMETERS: (i)int socket: connected socket through which P-IOS will receive. (o)IOSFrame * data: pointer to Frame Structure which will be filled with the received data. (i)int timeout: time to wait for a frame to arrive. RETURN: The function returns ERROR_TIMEOUT if the receeiver times out, if it fails to find a valid frame it returns false, otherwise returns true. VARIABLES: int rc: Number of bytes received. int i: counter. int ix: to store the position of start and end of frame char buf: buffer for received data. char desecho: buffer for recieving non-valid data. bool inicio: internally used for searching start or end. --------------------------------------------------------------------------------*/ int iosRecvFrame(int socket, IOSFrame * data, int timeout) { int rc,i, ix; //numero de bytes recibidos. int src = sizeof(rc); //necesario para la funcion getsockopt. char buf[1024]; char desecho[1024]; bool inicio = false; //para distinguir entre buscar inicio y buscar final. rc = 0; signal(SIGALRM, catch_sig); print(log, 1, "Recibiendo paquete"); alarm(timeout); while (rc == 0) { rc = recv(socket, buf, 1024, MSG_PEEK); //Leo el buffer de entrada SIN VACIARLO, los datos quedan. if (rc < 0) { getsockopt(socket, SOL_SOCKET, SO_ERROR, (void *)&rc, &src); if (rc == 0) print(err, 1, "Timeout Recibiendo"); else sprintf(pb, "IOS Receive Error = %d", rc); print(err, 0, pb); alarm(0); signal(SIGALRM, SIG_DFL); return(false); //false = hubo error } if (rc!=0) //si se recibio algo se busca caracter de inicio o de final segun corresponda(variable inicio) { sprintf(pb, "Recibidos %d bytes", rc); print(log, 1, pb); //busco caracter de inicio, si no es el primero desecha lo que no sirve que quede en el buffer. if (!inicio) { print(deb, 1, "Buscando caracter de inicio....."); ix = Pos(buf, IOS_FRAME_START, rc); //devuelve posicion del caracter de inicio en el buffer if (ix != -1) { print(deb, 1, "OK"); if (ix) //elimino todo lo que este antes de ese caracter. { sprintf(pb, "Limpiando %d bytes", (ix)); print(log, 1, pb); sockRead(socket, desecho, ix); } inicio = true; } else { rc = 0; print(deb, 1, "No se encontro/nDesechando..."); } } if (rc != 0) //si rc esta en 0 es porque tenia que buscar inicio y no se encontró. { print(deb, 1, "Buscando caracter final....."); ix = Pos(buf, IOS_FRAME_END, rc); //busco posicion de caracter final. ix++; if (ix != -1) { print(deb, 1, "OK"); sockRead(socket, buf, ix+1); //leo el resto del mensaje y lo saco del buffer inicio = false; print(log, 1, "Frame recibido exitosamente"); rc = 1; } else { rc = 0; sleep(1); print(deb, 1, "No se encontro/nDesechando..."); } } } }//receive. alarm(0); iosCharToFrame(buf, data); signal(SIGALRM, SIG_DFL); return(true); } /*-------------------------------------------------------------------------------FUNCTION: iosSendFrame DESCRIPTION: Sends a frame to the connected IOS-client. PARAMETERS: (i)int socket: connected socket through which P-IOS will send. (i)IOSFrame * frame: pointer to frame strtucture to send. RETURN: The funtion returns true if the frame was succesfully sent or false if there was an error. --------------------------------------------------------------------------------*/ int iosSendFrame(int socket, IOSFrame * frame) { return(!sockWrite(socket, (char *)(frame), frame->len)); } /*-------------------------------------------------------------------------------FUNCTION: IOSConnect. DESCRIPTION: Attempts an P-IOS connection to "ip" on port "port". Once connected it confirms if it is a valid P-IOS Connection. PARAMETERS: (i)int port: remote port to connect to. (i)char * ip: Pointer to ip string. RETURN: The funtion returns the connected socket after confirming a valid P-IOS connection, it returns ERROR_CONEXION if the connection could'nt be succesfully established or ERROR_CONFIRMACION if the connection was established but not a valid P-IOS connection. VARIABLES: int socket: Socket for the connection. short rport: Remote port connected to the server. char rip, host: IP and hostname of the connected client. --------------------------------------------------------------------------------*/ int IOSConnect(char * ip, int port) { int socket; short rport; char rip[15], host[64]; print(log, 1, "Conectando....."); socket = ClientSocket(ip, port); if (socket < 0) { print(log, 1, "retry in 10 sec."); //sleep(10); socket = ClientSocket(ip, port); if (socket < 0) return(ERROR_CONEXION); } getPeerInfo(socket, host, rip, &rport); sprintf(pb, "\nConnecting to %s (%s) on port %d.....", host, rip, rport); print(err, 0, pb); if (CONFIRM) { if (IOSconfirm(socket)) return(socket); close(socket); return(ERROR_CONFIRMACION); } return(socket); } /*-------------------------------------------------------------------------------FUNCTION: IOSconfirm. DESCRIPTION: It checks an established connection to verify if it is a valid P-IOS Connection. PARAMETERS: (i)int socket: Connected socket to confirm. RETURN: True if the connection in socket is valid and false if it is not or if there was an error. VARIABLES: IOSFrame frame1: Frame to send to the other side of connection. for confirmation of P-IOS protocol. IOSFrame frame2: buffer for receiving confirmation. --------------------------------------------------------------------------------*/ bool IOSconfirm(int socket) { int i; IOSFrame frame1, frame2; frame1.BlockCount = 0; iosBuildFrame(OBID, OBID, &frame1);//Build frame with 0 datablocks. iosPrintFrame(frame1, 1); print(log, 0, "Confirmando conexion...."); if(iosSendFrame(socket, &frame1)) return(false); if(!iosRecvFrame(socket, &frame2, TIMEOUT_CONFIRM)) return(false); print(log, 0, "Conexion confirmada"); return(true); } /*-------------------------------------------------------------------------------FUNCTION: iosOpenMQ - Open Message Queue DESCRIPTION: Opens the message queue "MQ_IOS" (name defined in ios.h) to handle internal S-IOS messages. Openning the M.Q. gives the calling thread access to the message queue in question. For more information read mq_open man page. PARAMETERS: (o)mqd_t * mq: pointer to Message Queue deescriptor for later use by other functions. RETURN: Returns true if able to open message queue and returns file desciptor for accessing this MQ in mq. False if not able to open MQ. --------------------------------------------------------------------------------*/ bool iosOpenMQ(mqd_t * mq) //436 { print(log, 1, "Abriendo MsgQueue"); *mq = mq_open(MQ_IOS, O_RDWR | O_NONBLOCK, FMODE, NULL); sprintf(pb, "mq = %d", mq); print(deb, 0, pb); if (*mq == (mqd_t)-1) if (errno == ENOENT) { print(deb, 0, "No existe, creandola..."); *mq = mq_open(MQ_IOS, O_RDWR | O_CREAT | O_EXCL | O_NONBLOCK, FMODE,NULL); print(deb, 0, "MsgQueue Creada"); return(false); } else { print(err, 0, "IOS Error creating MsgQueue"); return(true); } print(deb, 0, "MsgQueue Abierta"); return(false); } /*-------------------------------------------------------------------------------FUNCTION: iosCloseMQ - Close mesage queue. DESCRIPTION: Closes the message queue "MQ_IOS" (name defined in ios.h), if "dedstroy" is True the the MQ is also destroyed. (No other thread must be reading frrom the MQ or the function blocks). For more information read mq_close man page. PARAMETERS: (i)mqd_t * mq: Pointer to Message Queue descriptor. (i)bool destroy: If TRUE deestroy MQ, if FALSE just close. RETURN: Returns true if able to close message false if not. --------------------------------------------------------------------------------*/ bool iosCloseMQ(mqd_t * mq, bool destroy) { if (destroy) { sprintf(pb, "Destruyendo MsgQueue %s", MQ_IOS); print(deb, 0, pb); if ( -1 != mq_unlink(MQ_IOS)) { print(deb, 0, "MsgQueue destruida"); return(false); } else { sprintf(pb, "Error %d al destruir MsgQueue", errno); print(err, 0, pb); return(true); } } print(deb, 0, "Cerrando MsgQueue"); if (-1 != mq_close(*mq)) { print(deb, 0, "MsgQueue cerrada"); return(false); } print(deb, 0, "No se pudo cerrar el MsgQueue"); return(true); } /*-------------------------------------------------------------------------------FUNCTION: iosFrameQueue. DESCRIPTION: Adds frame to message queue for processing by the dispatcher. PARAMETERS: (i)int priority: Priority of the message. (i)mqd_t * mq: Pointer to MQ in which to add the frame. (i)IOSFrame * frame: Frame to add to MQ. RETURN: True if the frame is added succesfully, false if it is not. --------------------------------------------------------------------------------*/ bool iosFrameQueue(mqd_t * mq, IOSFrame * frame, int priority) { print(deb, 1, "Agregando Frame a la cola"); if (-1 == mq_send(*mq, (char *)frame, frame->len, priority)) { print(deb, 1, "No se pudo agregar el mensaje"); return (true); } print(deb, 1, "Frame Agregado Exitosamente"); return(false); } /*-------------------------------------------------------------------------------FUNCTION: iosGetFrameMQ - Read Frame from message queue. DESCRIPTION: Retrieves a frame from the message queue for processing. PARAMETERS: (i)mqd_t * mq: Pointer to MQ from which to get the frame. (o)IOSFrame * frame: buffer for retrieved frame. RETURN: Returns EMPTY is MQ had no messages, (-1) if there was an error. Returns number of bytes retrieved from Queue if the message was read properly. VARIABLES: int bytes: Number of bytes read from MQ. --------------------------------------------------------------------------------*/ int iosGetFrameMQ(mqd_t * mq, IOSFrame * frame) { int bytes = mq_receive(*mq, (char *)frame, MAX_LENGTH, NULL); if ((bytes == -1) && (errno == EAGAIN)) { print(log, 1, "La cola esta vacia"); return(EMPTY); } if (bytes < 0) return(ERROR); if (bytes == frame->len) return(bytes); return(-1); } /*-------------------------------------------------------------------------------FUNCTION: iosAddCmdMQ - Add command to MQ. DESCRIPTION: Adds a command to the MQ to be received and executed by the dispatcher. This is done by adding a frame with no blocks and sender = COMMAND and receiver = actual command. Posibble commands can be seen in ios.h PARAMETERS: (i)mqd_t * mq: Pointer to MQ in which to add command. (i)short cmd: Command to be added. RETURN: Returns true. VARIABLES: IOSFrame frame: frame to send in which the command is enclosed. --------------------------------------------------------------------------------*/ int iosAddCmdMQ(mqd_t * mq, short cmd) { IOSFrame frame; frame.BlockCount = 0; iosBuildFrame(COMMAND, cmd, &frame); iosFrameQueue(mq, &frame, 10); return(0); } /*-------------------------------------------------------------------------------THREAD: Dispatch - Mesage dispatcher to softbus or WIOS DESCRIPTION: Separate thread that loops retrieving messages from the message queue and delivering them to the appropiate receiver (SBUS, OPC, Internal functions), it also supports user commands. PARAMETERS: (i)int socket: Socket to send messages to WIOS. RETURN: Return is unimportant. VARIABLES: IOSFrame fbuf: frame buffer to retrieve the messages. int i: Counter. mqd_t mqd: Message queue from which to retrieve messages. sigevent notify: Notification Structure to register the program for notification when messages arrive to MQ. ---------------------------------------------------------------------------------------------*/ bool iosDispatch(int * socket) { mqd_t mqd; int i; char msg[MAX_LENGTH]; IOSFrame fbuf; struct sigevent notify; bool LOOP = true; iosOpenLogs(); notify.sigev_notify = SIGEV_SIGNAL; //Structure needed for signal requesting on message arrival to MQ notify.sigev_signo = SIG_MQ; notify.sigev_value.sival_int = 0; notify.sigev_value.sival_ptr = NULL; if (!iosOpenMQ(&mqd)) print(log, 0, "Dispatch: Dispatch Started"); else { print(err, 0, "Dispatch: Uanble to Start"); return(true); } while(LOOP) { if (iosGetFrameMQ(&mqd, &fbuf) == EMPTY) { mq_notify(mqd, &notify); signal(SIG_MQ, catch_sig); sigpause(SIG_MQ); } if (fbuf.sender==COMMAND) //If command received: { print(log, 0, "Dispatch: Received command"); switch(fbuf.receiver) { case QUITDISPATCH: print(log, 0, "Dispatch: Closing Dispatcher"); LOOP = false; break; case QUITIOS: print(log, 0, "Dispatch: Closing IOS"); iosCloseMQ(&mqd, false); close(*socket); ios_exit(); return(0); }//switch } else if(fbuf.receiver == OPC) //If message for OPC received: { print(log, 1, "Dispatch: Received frame for OPC"); //iosPrintFrame(fbuf, 1); if (CheckSocket(socket)==-1) { print(err, 0, "Dispatch: No active conexion"); //return(true); } else iosSendFrame(*socket, &fbuf); } else if(fbuf.receiver == SBUS) //if message for SBUS received. { print(log, 1, "Dispatch: Received frame for SBUS"); for(i=0; i<fbuf.BlockCount; i++) iosRequestFromOPC(fbuf.data[i]); } } print(log, 0, "Dispatch: Closing Dispatch..."); iosCloseMQ(&mqd, false); print(log, 0, "Dispatch Closed"); return(true); } /*-------------------------------------------------------------------------------THREAD: iosRecieveLoop - Reciever program. DESCRIPTION: Loop for recieving messages from W-IOS. Retrieves messages from incomming buffer and takes appropiate action depending on the message. PARAMETERS: (i)int socket: Socket for recieving. RETURN: Returns -1 if invalid socket or not connected, returns 0 if socket is connected. VARIABLES: IOSFrame frame: frame buffer to retrieve the messages. mqd_t mq: Message queue to send messages to dispatcher. int error: Auxiliary variable to store errors from recieve. pollfd fds: poll descriptor to register events to be sent as they happen to file descriptor registered. --------------------------------------------------------------------------------*/ bool iosReceiveLoop(int * socket) { mqd_t mqd; struct pollfd fds; int error; IOSFrame frame; iosOpenLogs(); fds.fd = *socket; fds.events = POLLIN | POLLERR; if (CheckSocket(socket)==-1) return(false); if (!iosOpenMQ(&mqd)) print(log, 0, "Receiver: Comenzado"); else { print(err, 0, "Receiver: No se pudo levantar el receptor"); return(false); } while(1) { if (-1==poll(&fds, 1, -1)) { sprintf(pb, "Receiver: Error en funcion poll...%d", errno); print(err, 1, pb); } else { if (fds.revents&POLLIN) { //printf("poll: %d, %d, %d, %d", fds.revents, POLLIN, POLLERR, fds.revents&POLLIN); if (!iosRecvFrame(*socket, &frame, TIMEOUT_RECV)) { print(err, 0, "Receiver: Hubo error recibiendo"); return(true); } print(log, 1, "Receiver: Received frame from OPC"); iosPrintFrame(frame, 0); if (frame.receiver == SBUS) iosFrameQueue(&mqd, &frame, 5); else if (frame.sender == OPC) { frame.receiver = SBUS; iosFrameQueue(&mqd, &frame, 5); } } if (fds.revents&POLLERR) { print(log, 0, "Receiver: Conexion terminada por el cliente"); break; } } } print(log, 0, "Receiver: Cerrando iosRecieveLoop"); return(true); } //Consola de Pruebas int iosTestConsole() { int i = 0; int client; char command[64]; bool error; char data[MAX_LENGTH]; mqd_t mq; bool CONSOLE = true; int cmd, port, client_port, s_sock, c_sock, socket; char * temp; char ip[64]; IOSFrame buf, buf2; IOSDataBlock block; float valor = 3.14; double CValor = 69; long SValor = 1; //---------PIDs----------// int desp_pid = 0; int recv_pid = 0; int main_pid = getpid(); block.control = GetValue; sprintf(block.tadd.B1, "AMPAR"); sprintf(block.tadd.B2, "138ST"); sprintf(block.tadd.B3, "GVERDE"); sprintf(block.tadd.Elem, "P"); sprintf(block.tadd.Info, "MvMoment"); buf.BlockCount = 0; //iosAddValue(&block, neSwitch, &SValor, 1); //iosAddValue(&block, neMeasVal, &valor, 1); iosAddValue(&block, neCountVal, &CValor, 1); iosAddBlock(&buf, &block); iosBuildFrame(OPC, OPC, &buf); signal(SIG_PAUSE, catch_sig); printf("\ndouble = %f", CValor); while(CONSOLE) { printf("\nIOS-TestConsole# %d #", main_pid); gets(command); cmd = GetCommand(command); switch (cmd) { case InitServer: { strtok(command, " "); temp = strtok(NULL, " "); if (temp==NULL) { port = DFL_PORT; printf("\nTomando puerto por defecto %d", port); } else port = strtol(temp, NULL, 10); if ((port < 1500)||(!port)) { printf("\nUso: srv [port(1500 - 8000)]"); break; } printf("\nComenzando Servidor"); s_sock=iosStartServer(port, 5); if (s_sock < 0) { if (s_sock == ERROR_CONFIRMACION) printf("Error de confirmacion"); if (s_sock == ERROR_CONEXION) printf("Error de conexion"); } //kill(main_pid, SIG_PAUSE); //main_pid = getpid(); socket = s_sock; break; }//init server case Connect: { strtok(command, " "); temp = strtok(NULL, " "); if (temp==NULL) { port = DFL_PORT; printf("\nTomando puerto por defecto %d", port); } else port = strtol(temp, NULL, 10); if ((port < 1500)||(!port)) { printf("\nUso: con [port(1500 - 8000)] [ip]"); break; } temp = strtok(NULL, " "); if (temp == NULL) { sprintf(ip, "%s", DFL_IP); printf("\nTomando ip por defecto %s", ip); } else strcpy(ip, temp); c_sock = IOSConnect(ip, port); if (c_sock < 0) { printf("Error en la conexion"); break; } socket = c_sock; break; } case Despacho: { if (desp_pid != 0) { printf("\nEl dispatch ya esta activo"); break; } iosOpenMQ(&mq); desp_pid = fork(); if(desp_pid != 0) break; setpgid(0, GrpPid); iosDispatch(&socket); desp_pid = 0; thr_exit((void *)NULL); break; } case Recibir: { if (recv_pid != 0) { printf("\nEl receptor ya esta activo"); break; } recv_pid = fork(); if(recv_pid != 0) break; setpgid(0, GrpPid); iosReceiveLoop(&socket); recv_pid = 0; thr_exit((void *)NULL); break; } case Send: { iosBuildFrame(OPC, OPC, &buf); iosFrameQueue(&mq, &buf, 10); sleep(1); break; } case XDespacho: { if(desp_pid == 0) { printf("\nEl proceso no estaba activo"); break; } iosAddCmdMQ(&mq, QUITDISPATCH); desp_pid = 0; break; } case XReceptor: { printf("\nCerrando iosRecieveLoop..."); kil(&recv_pid); break; } case Desconectar: { if(desp_pid == 0) { printf("\nEl proceso no estaba activo"); break; } else { iosAddCmdMQ(&mq, QUITDISPATCH); desp_pid = 0; } printf("\nCerrando iosRecieveLoop..."); kil(&recv_pid); printf("\nCerrando Conexion..."); close(socket); break; } case Quit: { CONSOLE=false; printf("\nCerrando Consola de Pruebas..."); iosCloseMQ(&mq, false); kil(&recv_pid); if(desp_pid == 0) { printf("\nEl proceso no estaba activo"); break; } else { iosAddCmdMQ(&mq, QUITDISPATCH); desp_pid = 0; } kil(&main_pid); thr_exit((void *)NULL); break; } case Status: { printf("\nChecking system status:"); printf("\nConnected?....."); if (CheckSocket(&socket)==0) printf("yes"); else printf("no"); printf("\nDispatch active?......."); if (desp_pid) printf("yes"); else printf("no"); printf("\nReceiver active?......."); if (recv_pid) printf("yes"); else printf("no"); break; } case OpenQueue: { iosOpenMQ(&mq); break; } case Clear: { printf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"); break; }//case clear default: { if (!strcmp(command, "")) break; printf("\nComando no reconocido"); break; }//case default }//switch command }//while console printf(""); }//main. C.6 .- spec.c (interfaces del spectrum). ***iosNotify: This function registers the program to the softbus. ***iosNimInit: This function opens the main relations needed for spectrum usage. Initializaes the database within the program. This function establishes a new request record in which to store relevant request information such as DB index and Standard Element Type. It converts the Tech Address from names to numbers and stores in the request structure all necesary values for the making of the request. Fills Namvar and Nimvar within request structure. This function sends a value request to the SBUS. This function searches an inclomming value from the softbus against the request array to see which does it belong to, and then sends it back to OPC. As many times as values come in the SB message. This function sends a value to the SBUS, to change it in the DB. ***iosNewRequest: ***iosRequestInfo: ***iosRecv_Value: ***iosDist_Value: ***iosSbRecvLoop: This is the Receive Loop for SoftBus, it awaits any softbus message and takes appropiate action depending on the mesage. /*---------------------------------------------------------------------------------------------------------------FUNCTION: iosRequestFromOPC DESCRIPTION: This function is called when a message from OPC arrives, it fills a new request if the message is not repeated and it distributes the value or sends the request depending on the incomming message. PARAMETERS: in: IOSDataBlock data: Incomming data from the OPC message. RETURN: VARIABLES: int i: Counter int found: auxiliary variable determining if the OPC message is repeated. ----------------------------------------------------------------------------------------------------------------*/ void iosRequestFromOPC(IOSDataBlock data) { int i=0; int found=false; if (!SBUS_ON) { print(log, 1, "iosRequestFromOPC"); return; } for(i; i<MAXREQUESTS; i++) { if (!strncmp((char *)&(data.tadd), (char *)&(request[i].Ta), sizeof(tTa))) { found = true; break; } } if (!found) { for(i=0;i<MAXREQUESTS;i++) { if (!RequestUse[i]) { iosNewRequest(i); break; } } } switch(data.control) { case VALFROMOPC: iosDist_Value(i, data.tdata); RequestUse[i] = 0; break; case REQUEST: iosRequestInfo(request[i].NimVar); break; } return; } /*---------------------------------------------------------------------------------------------------------------FUNCTION: iosSendBacktoOPC DESCRIPTION: This function sends a value to OPC, builds the frame and queues it. PARAMETERS: in: int j: Request Index within array in: tTechData tdata Technological Data, value, time stamp, etc. in: NormElem NoEl, Element type. RETURN: VARIABLES: IOSFrame frame: frame to send. IOSDataBlock block to add to frame. ----------------------------------------------------------------------------------------------------------------*/ void iosSendBacktoOPC(int j, tTechData tdata, NormElem NoEl) { IOSFrame frame; IOSDataBlock block; block.control = VALFROMSCADA; block.dtype = NoEl; block.tdata = tdata; block.tadd = request[j].TaName; iosAddBlock(&frame, &block); iosBuildFrame(SBUS, OPC, &frame); iosFrameQueue(&mq, &frame, 5); return; } C.7 .- spec.c Archivo Principal. //inclusion de demás archivos SIOS. #include "ios.h" #include "error.c" #include "spec.c" #include "frames.c" #include "libproto.c" #include "funciones.c" /*----------------------------------------------------------------------------------------------------------------FUNCTION: iosInitSystem DESCRIPTION: This function initializes necessary parts of the program such as data base, softbus, sinaut notification, message queue and request array. PARAMETERS: (i) argc, char * argv: parameters needed for sinaut registration. (i/o) tNimVar * NimVar, tNamVar * NamVar: parameters needed for DB initialization.. RETURN: VARIABLES: --------------------------------------------------------------------------------------------------------------------*/ void iosInitSystem(int argc, char *argv[], tNimVar * NimVar, tNamVar * NamVar) { iosOpenLogs(); print(log, 0, "--------------------------------------------------------------"); print(deb, 0, "-------------------------------------------------------------"); ***REGISTRA AL PROGRAMA DENTRO DEL SPECTRUM. if (iosNotify(OBID)) print(log, 0, "SBus Notificado"); ***BLOQUEA RECEPCIÓN DE SOFTBUS HASTA QUE ESTE INICIALIZADO. iosNimInit(NimVar, NamVar); iosInitRequests(); iosOpenMQ(&mq); return; } /*----------------------------------------------------------------------------------------------------------------FUNCTION: iosOpenLogs DESCRIPTION: This fuction open the files necessary for IOS logging. PARAMETERS: RETURN: VARIABLES: log: log file file descriptor. err: error log file descriptor. deb: debug log file descriptor. -----------------------------------------------------------------------------------------------------------------*/ void iosOpenLogs() { log = fopen(LOGFILE, "a"); err = fopen(ERRFILE, "a"); deb = fopen(DEBFILE, "a"); } /*----------------------------------------------------------------------------FUNCTION: main. DESCRIPTION: This fuction starts the IOS system. to start test console type: ios test, to kill daemon type ios exit. PARAMETERS: int argc, argv: RETURN: unimportant VARIABLES: -----------------------------------------------------------------------------*/ int main(int argc, char *argv[]) { pid_t pid; pid_ios = getpid(); signal(IOSEND, catch_sig); //registers signal handler for signal IOSEND if(argc > 2) debug = atol(argv[2]); printf("\nDebug level = %d",debug); if (argc > 1) { if (!strcmp(argv[1], "exit")) //Start in shutdown mode, to close ios Daemon. { print(stdout, 0, "Closing IOS Daemon\n"); iosOpenLogs(); daemon_kill(); return(0); } if (!strcmp(argv[1], "test")) //start test console. { print(stdout, 0, "Starting test Console"); iosOpenLogs(); iosTests(); kill(0, SIGKILL); return(0); } sprintf(pb, "%s no es reconocido", argv[1]); print(stdout, 0, pb); return(0); } if (!daemon()) return(0); iosInitSystem(argc, argv, &GNimVar, &GNamVar); printf("\antes de arrancar server"); IOSsocket = iosStartServer(PORT, MAXCONNECTIONS); if (IOSsocket == ERROR_CONFIRMACION) iosStartServer(PORT, MAXCONNECTIONS); pid = fork(); //Start receiver thread. if (pid == 0) { iosReceiveLoop(&IOSsocket); return(0); } pid_receptor = pid; pid = fork(); //Start Dispatcher thread. if (pid == 0) { iosDispatch(&IOSsocket); return(0); } pid_despacho = pid; ***DESBLOQUEO LA RECEPCION DEL SOFTBUS iosSbRecvLoop(); kill(0, SIGKILL); return(0); } ANEXO D WINDOS - IOS CODIGO FUENTE WIOS Visual Basic Declaraciones.bas Option Explicit Option Base 1 Public Type T_ServerData ServerName As String serverID As Integer nodename As String rsItems As Recordset OpcServers As OPCServer OpcGroups As OpcGroups OpcGroup As OpcGroup OpcItems As OpcItems OpcItem As OpcItem ServidorActivo As Boolean End Type Public Const DEBUGGING As Integer = 1 Public Const MAXITEMS As Integer = 1000 Public Const FECHA1970 As Date = "1/1/70 0:0:0" Public ServerData() As T_ServerData Public Declare Function enviadata Lib "bin\vbcomm.dll" _ (ByVal a As String, ByRef Valor As Variant, _ ByVal calidad As Long, ByVal timestamp As Long, _ ByVal Socket As Long) As Integer MainForm.frm Option Explicit Option Base 1 Private Sub CargarDesdeBD(ByRef ServerInfo() As T_ServerData) 'DESCRIPCION: _ Esta subrutina carga la base de datos en registros locales que _ permitan su rapido acceso 'PARAMETROS: _ ServerInfo(): _ Arreglo de tipos T_ServerData donde se agrupa la informacion _ necesaria por cada servidor. _ 'RETORNO: _ 'VARIABLES: _ conex: _ Conexion a la Base de Datos _ rsServers: _ RecordSet donde se almacena la informacion sobre _ los servidores disponibles en la BD _ i: _ Contador generico Dim Dim Dim Dim Dim conex As Connection rsServers As Recordset i As Integer dbsource As String dbString As String dbsource = App.Path & "\scadaopc.mdb" dbString = "Provider=Microsoft.Jet.OLEDB.4.0;" & _ "Data Source=" & dbsource & ";" ConexBd.Conex1.ConnectionString = dbString Set conex = ConexBd.Conex1 conex.Open Dim rsConfig As Recordset Set rsConfig = New Recordset 'Obtengo los parametros de configuracion del WIOS de la BD rsConfig.Open "SELECT * FROM wios", conex txtIP = rsConfig!ip tmrTimer.Interval = rsConfig!refreshrate Set rsServers = New Recordset rsServers.Open "SELECT * FROM servidores", conex 'Redimensiono el arreglo de estructuras ServerInfo para _ ajustarse al numero de servidores encontrados en la BD ReDim ServerInfo(rsServers.RecordCount) As T_ServerData i=1 If rsServers.RecordCount > 0 Then rsServers.MoveFirst Do While (rsServers.EOF = False) 'Comienzo el rellenadio de las estructuras serverinfo con _ la data proveniente de la BD With ServerInfo(i) .nodename = rsServers!nodename .serverID = rsServers!serverID .ServerName = rsServers!ServerName Set .rsItems = New Recordset .rsItems.Open "SELECT * FROM scadaopc WHERE serverid = " & .serverID, conex If DEBUGGING > 0 Then Debug.Print .ServerName & " " & .nodename If .rsItems.RecordCount > 0 Then .rsItems.MoveFirst Do While .rsItems.EOF = False Debug.Print .rsItems!opckey .rsItems.MoveNext Loop End If Debug.Print End If End With rsServers.MoveNext i=i+1 Loop End If End Sub Private Sub cmdCOnectarTcp_Click() InicializarSocket With wskScada If .State = sckClosed Then .Connect End If End With End Sub Private Sub cmdTimer_Click() With cmdTimer If .Caption = "Detener" Then .Caption = "Reanudar" tmrTimer.Enabled = False Else .Caption = "Detener" tmrTimer.Enabled = True End If End With End Sub Private Sub Form_Load() If DEBUGGING < 2 Then CargarDesdeBD ServerData ConectarOPC ServerData PrepararItemsOPC ServerData tmrTimer.Enabled = True Else InicializarSocket End If End Sub Private Sub PrepararItemsOPC(ByRef ServerInfo() As T_ServerData) 'DESCRIPCION: _ Esta subrutina se encarga de conectar la aplicacion e con los _ servidores OPC registrados en la BD _ 'PARAMETROS: _ ServerInfo(): _ Arreglo de estructuras T_serverdata, de donde se extraera _ la información para conectrarse a los servidores OPC 'RETORNO: _ 'VARIABLES: _ i,j: _ Contadores genericos _ ClientHandles(): _ Arreglo en Longs, donde se almacenan temporalemte los handles que _ como clientes queremos darle a nuestros items OPC en los Servidores _ Es un parametro de entrada del metodo additems de los opcgroups _ ItemIds(): _ Arreglo de Strings, donde se almacenan los identificadores _ OPC de cada item Es un parametro de entradasalida del metodo _ additems de los opcgroups _ ServerHandles(): _ Arreglo en Longs, donde se almacenan temporalemte los handles que _ el servidor ha asgnado a los items OPC seleccionados. Es un _ parametro de salida del metodo additems de los opcgroups _ ItemsErrors(): _ Arreglo en Longs, donde se almacenan temporalemte los codigos _ de error que el servidor devuelve en caso de presentarse alguno _ al momento de introducir los items. Es un parametro de salida _ del metodo additems de los opcgroups _ Dim Dim Dim Dim Dim i, j As Integer ClientHandles() As Long ItemIds() As String ServerHandles() As Long ItemsErrors() As Long On Error GoTo errores j=0 For i = LBound(ServerData) To UBound(ServerData) With ServerInfo(i) If .ServidorActivo = True Then Set .OpcGroups = .OpcServers.OpcGroups Set .OpcGroup = .OpcGroups.Add("IOS") Set .OpcItems = .OpcGroup.OpcItems If .rsItems.RecordCount > 0 Then 'Redimensiono los arreglos segun el numero de items a _ ingresar por servidor OPC ReDim ClientHandles(.rsItems.RecordCount) As Long ReDim ItemIds(.rsItems.RecordCount) As String ReDim ServerHandles(.rsItems.RecordCount) As Long ReDim ItemsErrors(.rsItems.RecordCount) As Long .rsItems.MoveFirst j=1 Do While (.rsItems.EOF = False) ClientHandles(j) = (MAXITEMS * i) + j Debug.Print ClientHandles(j) ItemIds(j) = .rsItems!opckey Debug.Print ItemIds(j) .rsItems.MoveNext j=j+1 Loop 'Agrego los items opc a el grupo "IOS" .OpcItems.AddItems .rsItems.RecordCount, ItemIds, ClientHandles, ServerHandles, ItemsErrors End If End If End With Next Exit Sub errores: 'En caso de error en algun servidor lo desactivo ServerInfo(i).ServidorActivo = False Resume Next End Sub Private Sub Form_Unload(Cancel As Integer) 'DESCRIPCION: _ Subrutina que se encarga de hacer finalizar la aplicacion _ 'PARAMETROS: _ 'RETORNO: _ 'VARIABLES: _ i: _ Contador generico Dim i As Integer On Error Resume Next If DEBUGGING < 2 Then For i = LBound(ServerData) To UBound(ServerData) With ServerData(i) If .ServidorActivo = True Then ServerData(i).OpcServers.Disconnect Set ServerData(i).OpcServers = Nothing End If End With Next End If End Sub Private Sub tmrTimer_Timer() '***************************************************** 'DESCRIPCION: _ Subrutina que se encarga de hacer el pooling de _ los Items OPC cada trmtTimer.interval mS 'PARAMETROS: _ 'RETORNO: _ 'VARIABLES: _ i,j: _ Contadores genericos _ scadakey : _ String utilizado para almacenar el nombre que _ debe ser enviado al SCADA, luego de ser _ recuperado del recordset temporal _ valor: _ Variant donde se almacena el valor retornado _ por el Servidor OPC _ calidad: _ Long donde se almacena la variable quality _ retornada por el Servidor OPC _ TimeStamp: _ ? donde se almacena la variable timestamp _ retornada por el Servidor OPC _ '***************************************************** Dim i, j As Integer Dim ScadaName As String Dim Valor As Variant Dim calidad As Long Dim timestamp As Long 'On Error GoTo errores Me.Caption = "Datos " & Now logger.Text = logger.Text & vbNewLine & "********************** COMIENZO CICLO *********************" For i = LBound(ServerData) To UBound(ServerData) With ServerData(i) If .ServidorActivo = True Then If .OpcItems.Count = 0 Then logger.Text = logger.Text & vbNewLine & "No Hay ningun item disponible en la base de datos relacionados" logger.Text = logger.Text & vbNewLine & "con el servidor " & .ServerName & " o de haberlos estos no fueron" logger.Text = logger.Text & vbNewLine & "reconocidos por el servidor, posiblemente por oun error es su" logger.Text = logger.Text & vbNewLine & "identificador o por haber sido eliminados del servidor" 'logger.Text = logger.Text & vbNewLine & "********************** FIN CICLO *********************" Else For j = 1 To .OpcItems.Count 'Lectura del item opc .OpcItems.Item(j).Read OPCDevice 'Aqui recupero ScadaName .rsItems.AbsolutePosition = (.OpcItems.Item(j).ClientHandle - (MAXITEMS * i)) ScadaName = .rsItems!scadakey Valor = .OpcItems.Item(j).Value calidad = .OpcItems.Item(j).Quality timestamp = DateDiff("s", FECHA1970, .OpcItems.Item(j).timestamp) If wskScada.State = sckConnected Then enviadata ScadaName, Valor, calidad, timestamp, wskScada.SocketHandle 'enviadata "a", .OpcItems.Item(j).ItemID, wskScada.SocketHandle End If If DEBUGGING > 0 Then logger.Text = logger.Text & vbNewLine _ &""&j_ & " " & ScadaName _ & " " & .OpcItems.Item(j).ClientHandle _ & " " & "*/Tstamp: " & Hex(timestamp) _ & " " & .OpcItems.Item(j).ItemID _ & " " & .OpcItems.Item(j).Value & " (" & TypeName(.OpcItems.Item(j).Value) & ")" _ & " " & calidad _ & " " & timestamp End If Next logger.Text = logger.Text & vbNewLine & "----------------------------" End If End If End With Next logger.Text = logger.Text & vbNewLine & "********************** FIN CICLO *********************" Exit Sub errores: MsgBox "Error al intentar leer los datos", vbCritical, "Error" tmrTimer.Enabled = False Exit Sub End Sub Private Sub ConectarOPC(ByRef ServerInfo() As T_ServerData) 'DESCRIPCION: _ Esta subrutina se encarga de conectar la aplicacion e con los _ servidores OPC registrados en la BD _ 'PARAMETROS: _ ServerInfo(): _ Arreglo de estructuras T_serverdata, de donde se extraera _ la información para conectrarse a los servidores OPC 'RETORNO: _ 'VARIABLES: _ i: _ Contador generico Dim i As Integer On Error GoTo errores: For i = LBound(ServerInfo) To UBound(ServerInfo) With ServerInfo(i) Set .OpcServers = New OPCServer .ServidorActivo = True 'Se inicializa como true _ si llegase a presentar un error que imposibilite _ la conexion se establecera como false 'Aqui se lleva a cabo la Conexion al servidor .OpcServers.Connect .ServerName, .nodename End With Next Exit Sub errores: With ServerInfo(i) .ServidorActivo = False logger.Text = logger.Text & vbNewLine & "Fallo de conexion con el servidor " _ & .ServerName & " @ " & .nodename End With Resume Next End Sub Private Sub InicializarSocket() 'DESCRIPCION: _ Subrutina que se encarga de inicializar el control _ wskScada 'PARAMETROS: _ 'RETORNO: _ 'VARIABLES: _ With wskScada MsgBox .State If .State <> sckError Then .RemoteHost = txtIP.Text .RemotePort = txtPuerto.Text End If End With End Sub Private Sub cmdClearLog_Click() logger.Text = vbNullString End Sub Private Sub wskScada_Close() With wskScada MsgBox "Se ha desconectado el equipo en " & .RemoteHostIP .Close End With lblConexStatus.BackColor = vbRed lblConexStatus.Caption = "Desconectado" End Sub Private Sub wskScada_Connect() With wskScada lblConexStatus.BackColor = vbGreen lblConexStatus.Caption = "Conectado" End With End Sub Libreria vbcomm.dll C++Builder ios.h //--------------------------------------------------------------------------#ifndef iosH #define iosH #include #include #include #include <stdio.h> <mem.h> <string.h> <time.h> //#pragma hdrstop #define MAXCONNECTIONS 5 //#define IOS_FRAME_START 0xF0AE //#define IOS_FRAME_END 0xEBC4 #define IOS_FRAME_START 0xAEF0 #define IOS_FRAME_END 0xC4EB #define IOSMaxBlocks 10 #define SEP_CHAR '_' #define H_LENGTH 10 #define MAX_LENGTH 1024 + H_LENGTH //Sender & Receiver Options. #define SBUS 1 #define OPC 2 #define COMMAND 3 #define #define #define #define bool int true 1 false 0 byte char //Valores posibles para dtype (tipo de dato) #define neSwitch 6 //revisar en el scada despues. #define neMeasVal 2 #define neCountVal 3 //------Defaults------------// #define DFL_PORT 5500 #define DFL_IP "127.0.0.1" //Control Options #define REQUEST 1 #define VALFROMOPC 2 #define VALFROMSCADA 3 typedef struct { long Sec; short mSec; char Qb0; char Qb1; union { bool Vboolean; long Vlong; float Vfloat; double Vdouble; /* 00:04 time stamp [seconds] /* 04:02 time stamp [milliseconds] /* 06:01 quality ->tKenn.Kb0 (=tallK) /* 07:01 quality ->tKenn.Kb1 (=Limit) /* 08:08 value /* 00:01 1 byte boolean */ /* 00:04 4 byte integer /* 00:04 4 byte real /* 00:08 8 byte real */ */ */ */ */ */ */ */ char long float bool } Val; } tTechData; Vstring8[8]; VlongA[2]; VfloatA[2]; VbooleanA[8]; typedef struct { char B1[8]; char B2[8]; char B3[8]; char Elem[8]; char Info[8]; } tTaName; /* /* /* /* /* typedef struct IOSDataBlock{ char control; char dtype; tTaName tadd; tTechData tdata; }IOSDataBlock; 00 08 16 24 32 /* /* 00:08 8 byte string /* 00:08 2 * 4 byte integer /* 00:08 2 * 4 byte real 00:08 8 byte boolean /* 16 sizeof(tTechData) block name 1 block name 2 block name 3 element name info name */ /* 40 sizeof(tTaName) */ */ */ */ */ */ */ */ */ */ //Tipo de accion requerida. //tipo de datos en el bloque: neMeasVal, neCountVal, neSwitch. //direccion tecnologica en nombres, b1, b2, b3, elem, info. //Valor actual del dato, calidad, timestamp. #define IOSDBSize sizeof(IOSDataBlock) typedef struct IOSframe{ short start; //Caracteres de Inicio de la trama. short len; //2 bytes para el tamano de la trama incluyendo header. Char sender; //Identificacion del Emisario. char receiver;//Identificacion del Destinatario. char BlockCount; //Enviar o requerir valor o comando. IOSDataBlock data[IOSMaxBlocks]; //bloques de datos a enviar; }IOSFrame; #define IOSFrameSize(N) sizeof(IOSFrame)-IOSDBSize*(IOSMaxBlocks-(N))+2 final. //+2 por el caracter //FUNCIONES bool BuildFrame(char sender, char receiver, IOSFrame * frame); bool CharToFrame(char * data, IOSFrame * frame); void AddValue(IOSDataBlock * block, void * val, byte quality); void AddBlock(IOSFrame * frame, IOSDataBlock * block); void PrintFrame(IOSFrame buf2); char * split(char * ptr, char sep); void FillTaName(IOSDataBlock * block, char * name); IOSFrame NewFrame(char * name, void * val, char quality, long timestamp); bool FrameToChar(char * data, IOSFrame frame); char * funcion(int num, char *names, void * val, char * quality, long * timestamp); //Funciones que intercambian el orden de los bytes //-----------------------------------------------double dton(double val); float fton(float val); //-----------------------------------------------//--------------------------------------------------------------------------#endif ios.cpp //--------------------------------------------------------------------------#include <winsock.h> #include "ios.h" //Funciones para construir los frames!!!. bool BuildFrame(char sender, char receiver, IOSFrame * frame) { frame->start = IOS_FRAME_START; frame->sender = sender; frame->receiver = receiver; frame->len = htons(IOSFrameSize(frame->BlockCount)); return(true); } bool CharToFrame(char * data, IOSFrame * frame) { int i = 0, n=0; char * ptr = data; frame->start = *(short *)ptr; ptr = ptr + sizeof(short); frame->len = *(short *)ptr; ptr = ptr + sizeof(short); frame->sender = *ptr; ptr++; frame->receiver = *ptr; ptr++; frame->BlockCount = *ptr; ptr++; for (i = 0; i < frame->BlockCount; i++) { frame->data[i].control = *ptr; ptr++; frame->data[i].dtype = *ptr; ptr++; strncpy((char *)&(frame->data[i].tadd.B1), ptr, 8); ptr = ptr + 8; strncpy((char *)&(frame->data[i].tadd.B2), ptr, 8); ptr = ptr + 8; strncpy((char *)&(frame->data[i].tadd.B3), ptr, 8); ptr = ptr + 8; strncpy((char *)&(frame->data[i].tadd.Elem), ptr, 8); ptr = ptr + 8; strncpy((char *)&(frame->data[i].tadd.Info), ptr, 8); ptr = ptr + 8; //printf("\ndebug step %d", n++); frame->data[i].tdata.Sec = *(long *)ptr; ptr = ptr + sizeof(long); //printf("\ndebug step %d", n++); frame->data[i].tdata.mSec = *(short *)ptr; ptr = ptr + sizeof(short); //printf("\ndebug step %d", n++); frame->data[i].tdata.Qb0 = *ptr; ptr++; //printf("\ndebug step %d", n++); switch (frame->data[i].dtype) { case neSwitch: frame->data[i].tdata.Val.Vlong = *(long *)ptr; ptr = ptr + sizeof(long); break; case neMeasVal: frame->data[i].tdata.Val.Vfloat = *(float *)ptr; ptr = ptr + sizeof(float); break; case neCountVal: frame->data[i].tdata.Val.Vdouble = *(double *)ptr; ptr = ptr + sizeof(double); break; default: break; } } // printf("\ndebug step %d", n++); frame->len = IOSFrameSize(frame->BlockCount); return(true); } void AddValue(IOSDataBlock * block, void * val, byte quality) { switch (*(char *)val) { case 3: case 2: //Switch en OPC neSwitch = 6 en el SCADA. block->tdata.Val.Vlong = *(long *)((int)val+8); block->dtype = neSwitch; break; case 4: //Float en OPC, neMeasVal = 2 en SCADA block->tdata.Val.Vfloat = *(float *)((int)val+8); block->dtype = neMeasVal; break; case 5: //double en OPC, neCountVal = 3 en SCADA block->tdata.Val.Vdouble = *(double *)((int)val+8); block->dtype = neCountVal; break; default: block->dtype = 99; //para señalizar tipo de datos no reconocido por SCADA break; } //Esta funcion rellena Sec y mSec con los valores actuales. //time(&(block->tdata.Sec), &(block->tdata.mSec)); block->tdata.Qb0 = quality; return; } void AddBlock(IOSFrame * frame, IOSDataBlock * block) { memcpy((char *)&(frame->data[frame->BlockCount]), (char *)block, IOSDBSize); frame->BlockCount++; return; } void PrintFrame(IOSFrame buf2) { int i; printf("\nStart = %d", buf2.start); printf("\nLength = %d", buf2.len); printf("\nSender = %d", buf2.sender); printf("\nDestin.= %d", buf2.receiver); printf("\nCount = %d", buf2.BlockCount); for(i = 0; i<buf2.BlockCount; i++) { printf("\ndata[%d].control = %d", i, buf2.data[i].control); printf("\ndata[%d].dtype = %d", i, buf2.data[i].dtype); printf("\ndata[%d].tadd.B1 = %s", i, buf2.data[i].tadd.B1); printf("\ndata[%d].tadd.B2 = %s", i, buf2.data[i].tadd.B2); printf("\ndata[%d].tadd.B3 = %s", i, buf2.data[i].tadd.B3); printf("\ndata[%d].tadd.Elem = %s", i, buf2.data[i].tadd.Elem); printf("\ndata[%d].tadd.Info = %s", i, buf2.data[i].tadd.Info); printf("\ndata[%d].tdata.Sec = %d", i, buf2.data[i].tdata.Sec); printf("\ndata[%d].tdata.mSec = %d", i, buf2.data[i].tdata.mSec); printf("\ndata[%d].tdata.Qb0 = %d", i, buf2.data[i].tdata.Qb0); switch (buf2.data[i].dtype) { case neSwitch: printf("\ndata[%d].tdata.Val.Vlong = %d", i, buf2.data[i].tdata.Val.Vlong); break; case neMeasVal: printf("\ndata[%d].tdata.Val.Vfloat = %f", i, buf2.data[i].tdata.Val.Vfloat); break; case neCountVal: printf("\ndata[%d].tdata.Val.Vdouble = %d", i, buf2.data[i].tdata.Val.Vdouble); break; default: break; } } printf("\nEnd = %d\n", *(short *)&(buf2.data[i])); return; } /*Funcion para separar un string usando un separador, la funcion devuelve un apuntador a string terminado en 0 que contiene la informacion del string original hasta antes del caracter "sep". ptr apunta al string que se quiere separar por "sep". si ptr = NULL no se tomara la primera palabra sino la siguiente a la llamada anterior a split */ char * split(char * ptr, char sep) { static char word[9]; static char * ant; int i = 0; if (ptr == NULL) ptr = ant; for (i = 0; i<9; i++) { if (*ptr != sep) word[i] = *ptr; else { ant = ptr + 1; word[i] = 0; break; } ptr++; } return(word); } void FillTaName(IOSDataBlock * block, char * name) { strcpy(block->tadd.B1, split(name, SEP_CHAR)); strcpy(block->tadd.B2, split(NULL, SEP_CHAR)); strcpy(block->tadd.B3, split(NULL, SEP_CHAR)); strcpy(block->tadd.Elem, split(NULL, SEP_CHAR)); strcpy(block->tadd.Info, split(NULL, SEP_CHAR)); return; } bool FrameToChar(char * data, IOSFrame frame) { char * ptr = data+2; short len; long lvalor; float fvalor; double dvalor; int i = 0; short end = IOS_FRAME_END; memcpy(ptr, &frame.start, sizeof(frame.start)); ptr = ptr + sizeof(frame.start); memcpy(ptr, &frame.len, sizeof(frame.len)); ptr = ptr + sizeof(frame.len); memcpy(ptr, &frame.sender, sizeof(frame.sender)); ptr = ptr + sizeof(frame.sender); memcpy(ptr, &frame.receiver, sizeof(frame.receiver)); ptr = ptr + sizeof(frame.receiver); memcpy(ptr, &frame.BlockCount, sizeof(frame.BlockCount)); ptr = ptr + sizeof(frame.BlockCount); for (i = 0; i < frame.BlockCount; i++) { memcpy(ptr, &frame.data[i].control, sizeof(frame.data[i].control)); ptr = ptr + sizeof(frame.data[i].control); memcpy(ptr, &frame.data[i].dtype, sizeof(frame.data[i].dtype)); ptr = ptr + sizeof(frame.data[i].dtype); memcpy(ptr, &frame.data[i].tadd.B1, 8); ptr = ptr + sizeof(frame.data[i].tadd.B1); memcpy(ptr, &frame.data[i].tadd.B2, 8); ptr = ptr + sizeof(frame.data[i].tadd.B2); memcpy(ptr, &frame.data[i].tadd.B3, 8); ptr = ptr + sizeof(frame.data[i].tadd.B3); memcpy(ptr, &frame.data[i].tadd.Elem, 8); ptr = ptr + sizeof(frame.data[i].tadd.Elem); memcpy(ptr, &frame.data[i].tadd.Info, 8); ptr = ptr + sizeof(frame.data[i].tadd.Info); lvalor = htonl(frame.data[i].tdata.Sec); memcpy(ptr, &lvalor, sizeof(lvalor)); ptr = ptr + sizeof(frame.data[i].tdata.Sec); memcpy(ptr, &frame.data[i].tdata.mSec, sizeof(frame.data[i].tdata.mSec)); ptr = ptr + sizeof(frame.data[i].tdata.mSec); memcpy(ptr, &frame.data[i].tdata.Qb0, sizeof(frame.data[i].tdata.Qb0)); ptr = ptr + sizeof(frame.data[i].tdata.Qb0); switch (frame.data[i].dtype) { case neSwitch: lvalor = htonl(frame.data[i].tdata.Val.Vlong); memcpy(ptr, &lvalor, sizeof(frame.data[i].tdata.Val.Vlong)); ptr = ptr + sizeof(frame.data[i].tdata.Val.Vlong); break; case neMeasVal: fvalor = fton(frame.data[i].tdata.Val.Vfloat); memcpy(ptr, &fvalor, sizeof(frame.data[i].tdata.Val.Vfloat)); ptr = ptr + sizeof(frame.data[i].tdata.Val.Vfloat); break; case neCountVal: dvalor = dton(frame.data[i].tdata.Val.Vdouble); memcpy(ptr, &dvalor, sizeof(frame.data[i].tdata.Val.Vdouble)); ptr = ptr + sizeof(frame.data[i].tdata.Val.Vdouble); break; default: break; } } memcpy(ptr, &end, sizeof(short)); len = ptr - data; memcpy(data, &len, sizeof(short)); return(true); } IOSFrame NewFrame(char * name, void * val, char quality, long timestamp) { //Descripcion: Esta funcion crea el frame // //Variables: //name: String con el nombre // //dyte: tipo de dato //val: valor de la medicion //qualyty: calidad de la medicion //timestamp: timestamp de la medicion // IOSFrame frame; IOSDataBlock block; frame.BlockCount = 0; block.tdata.Sec = htonl(timestamp); block.tdata.mSec = (short)0; block.control = 2; FillTaName(&block, name); AddValue(&block, val, quality); AddBlock(&frame, &block); BuildFrame(OPC, SBUS, &frame); return(frame); } float fton(float val) { float result; char * ptr = (char *)&result; char * end = (char *)&val + sizeof(float) -1; int i; for (i=0; i<sizeof(float); i++) { *ptr = *end; ptr++; end --; } return(result); } double dton(double val) { double result; char * ptr = (char *)&result; char * end = (char *)&val + sizeof(double) - 1; int i; for (i=0; i<sizeof(double); i++) { *ptr = *end; ptr++; end --; } return(result); } char * funcion(int num, char *names, void * val, char * quality, long * timestamp) { int i = 0; char buf[MAX_LENGTH]; IOSFrame frame; IOSDataBlock block; frame.BlockCount = 0; for (i = 0; i<num; i++) { block.tdata.Sec = htonl(timestamp[i]); block.tdata.mSec = (short)0; block.control = 2; FillTaName(&block, &(names[i])); AddValue(&block, val, quality[i]); AddBlock(&frame, &block); } BuildFrame(OPC, SBUS, &frame); FrameToChar(buf, frame); return(&buf[0]); } vbcomm.h #ifndef VBCOM_H #define VBCOM_H //extern "C" __declspec(dllexport) short WINAPI enviadata (BSTR scadaname, SOCKET s); extern "C" __declspec(dllexport) short WINAPI enviadata (BSTR scadaname,void * val, long quality, long timestamp, SOCKET s); //concatenast bool enviar( SOCKET Ssend, char FAR * buffer,int n); bool recibir( SOCKET Srecv, char FAR * buffer,int n ); #endif vbcomm.cpp //--------------------------------------------------------------------------#include <vcl.h> #include <windows.h> #include <winsock.h> #include "ios.h" #pragma hdrstop #include "vbcomm.h" // extern "C" __declspec(dllexport)void #pragma argsused int WINAPI DllEntryPoint(HINSTANCE hinst, unsigned long reason, void* lpReserved) { return 1; } //--------------------------------------------------------------------------short WINAPI enviadata (BSTR scadaname,void * val, long quality, long timestamp, SOCKET s) { LPSTR bufferB, bufferA ; bufferA = (LPSTR)scadaname; bufferB = funcion(1, bufferA, val, (char *)&quality, &timestamp); enviar(s,bufferB+sizeof(short),*(short *)bufferB); return 1; } //FUNCIONES MEJORADAS PARA SEND & RECV //---------------------------------------------------------------//DESCRIPCION: Estas 2 funciones se encargan de enviar y recibir data // a traves de un socket previamente abierto, verifican // que la data se trasmita completamente //VARIABLES : // SOCKET Ssend: es el socket previamente creado // char FAR * buffer : es el apuntador a la data a trasmitir // int n : es el numero de caracteres (BYTES) a trasmitir bool enviar( SOCKET Ssend, char FAR * buffer,int n) { int i; do { i=send(Ssend,buffer,n,0); if(i==SOCKET_ERROR) return false; n-=i; buffer+=i; } while(n>0); return true; } bool recibir( SOCKET Srecv, char FAR * buffer,int n ) { int i; do { i=recv(Srecv,buffer,n,0); if(i==SOCKET_ERROR) return false; n-=i; buffer+=i; } while(n>0); return true; } //---------------------------------------------------------------- ANEXO E UTILIDAD DE CONFIGURACION CODIGO FUENTE 'LISTA DE CONVENCIONES PARA LOS CONTROLES: ' Todos los controles presentes en los formularios seguirán esta convención ' prefijo + Nombre, donde el nombre comenzara con Mayúscula ' Ejemplo: txtNodo ' 'Control Prefijo Ejemplo 'Check box chk chkSoloEscritura 'Combo box cbo cboLenguaje 'Command button cmd cmdCancelar 'Directory list box dir dirDestino 'Drive list box drv drvFuente 'File list box fil filSeleccionado 'Form frm frmPrincipal 'Frame fra frmImpresoras 'Grid grd grdCantidades 'Horizontal Scroll Bar hsb hsbColor 'Image img imgBitMap 'Label lbl lblAyuda 'Line lin linSeleccionado 'List Box lst lstCodigoColor 'Menu mnu mnuAbrirArchivo 'Option Button opt optNegritaBold 'ProgressBar pb pbProgreso 'Picture Box pic shpCuadrado 'Text Box txt txtEntrada 'Timer tmr tmrInciaAlarma 'TreeView trv trvArbol 'Vertical Scroll Bar vsb vsbRango ' 'NOMBRE DEL FORMULARIO: frmInicio 'DESCRIPCION: ' En este formulario se abre al inicial la aplicación, _ sirve de marco a todos los de mas formularios _ inicializa la conexión a la BD. _ Private Sub MDIForm_Load() 'DESCRIPCION: _ Inicio de la Aplicación, aquí se inicializa la conexión a _ la base de datos 'PARAMETROS: _ Entrada: _ Salida: _ 'RETORNO: _ 'VARIABLES: _ dbsource, dbstring: _ String utilizadas para inicializar la conexión a la _ Base de Datos Dim dbsource As String Dim dbString As String dbsource = App.Path & "\scadaopc.mdb" dbString = "Provider=Microsoft.Jet.OLEDB.4.0;" & _ "Data Source=" & dbsource & ";" dtable.dbconex.ConnectionString = dbString End Sub 'Manejo de los Menus Private Sub mnuVerBD_Click() frmVerBD.Show End Sub Private Sub mnuWioscfg_Click() frmWiosCfg.Show End Sub Private Sub mnurecoverBD_Click() recuperardatos.Show End Sub Private Sub mnuserversearch_Click() frmServidores.SetFocus frmServidores.Show End Sub Private Sub mnuDatos_Click() frmConectarServidor.Show End Sub Option Explicit Option Base 1 'NOMBRE: frmServidores 'ARCHIVO: servidores.frm 'DESCRIPCION DEL FORMULARIO: ' En este objeto se hace un browsing de todos los servidores OPC, buscando la como clave básica _ la Direccion IP del se servidor 'OBJETOS INCLUIDOS 'ComboBox: ' cboServidores: _ Donde se seleccionan los servidores OPC asociados a un nodo determinado 'ListBox: ' lstServidores_BD: _ Aqui se listan los servidores actualmente registrados en la BD. 'TextBox: ' txtNodename: _ Aquí se puede introducir la información sobre el nodo al que se desea conectar _ puede ser una dirección IP, un nombre de red. Si se deja en blanco, la aplicación _ tomara como valor por defecto "127.0.0.1" _ 'Timer: ' tmrTrtimer: _ Se utiliza para actualizar continuamente el tiempo que lleva corriendo el servidor al _ cual se ha conectado mediante la opcion de realizar una prueba de conexión _ 'CommandButton: ' cmdListservs: _ Se utiliza para iniciar la búsqueda de servidores en el nodo selecionado, y llenar la lista cboServidores _ cmdConectar: _ Realiza la Prueba de conexion, luego permite _ desconectarlo al hacer click nuevamente _ cmdAgregar: _ Agrega un servidor a la BD _ cmdEliminar: _ Elimina el servidor seleccionado de la BD _ cmdCancelar: _ Cierra el Formulario 'Label: ' lblServerinfo(4): _ 4 labels que muestran información sobre el servidor al que se hace una prueba de conexión. Esta _ información incluye el nombre del servidor, el nombre de su fabricante la fecha en que fue arrancado _ por ultima vez y el tiempo que lleva corriendo actualmente. ' lbl1(6): _ 6 labels genéricos, muestran información visual sobre otros objetos 'VARIABLES GLOBALES ' F_Myserver: _ OPCserver, el servidor opc al que se esta conectado Public WithEvents F_Myserver As OPCServer 'OPC Server Object Private Sub cmdCancelar_Click() 'DESCRIPCION: _ Cierra la ventana sin realizar ninguna acción, además libera la variable F_Myserver 'PARAMETROS: _ Entrada: _ Salida: _ 'RETORNO: _ 'VARIABLES: Me.Hide Unload Me Set F_Myserver = Nothing End Sub Private Sub cmdAgregar_Click() 'DESCRIPCION: _ Esta subrutina agrega un servidor selecionado en lstservidores a la BD. Se busca Comparar _ con los Servidores existentes, para no incluir repetidos 'PARAMETROS: _ Entrada: _ Salida: _ 'RETORNO: _ 'VARIABLES: _ nodo: _ string donde se almacena el nodo asociado al servidor a agregar _ rsTmp: _ Recorset temporal utilizado para verificar la BD _ query: _ string SQL _ conex: _ Conexion a la base datos Dim Dim Dim Dim nodo As String rsTmp As Recordset query As String conex As Connection Screen.MousePointer = vbHourglass 'Utilizo una conexion previamente creada en el asistente dtable Set conex = dtable.dbconex Set rsTmp = New Recordset If cboServidores.Text = vbNullString Or cboServidores.Text = "Servidores" Then MsgBox "No ha selecionado un Servidor", vbExclamation, "Error" Screen.MousePointer = vbDefault Exit Sub End If nodo = txtNodename.Text If nodo = "" Then nodo = "127.0.0.1" End If 'El siguiente Query cheque la existencia previa del servidor que se desea agregar rsTmp.Open "SELECT * FROM servidores WHERE ( servername = '" & cboServidores.Text & "' AND " & " nodename ='" & nodo & "') ", conex, , adLockBatchOptimistic If rsTmp.RecordCount > 0 Then 'La busqueda ha encontrado un servidor en la BD _ coincide con que se quiere introducir MsgBox "El servidor seleccionado ya se se encuentra registrado" rsTmp.Close Screen.MousePointer = vbDefault Exit Sub End If 'Si no existe ningu registro coincidente en la BD, creo uno nuevo query = "INSERT INTO servidores (servername,nodename) VALUES ('" _ & cboServidores.Text & "','" & nodo & "')" 'Ejecuto el query conex.Execute query 'Agrego a la lista de servidores lstServidores_BD.AddItem cboServidores.Text & " @ " & nodo 'Activo el botn para eliminar cmdEliminar.Enabled = True Screen.MousePointer = vbDefault End Sub Private Sub cmdConectar_Click() 'DESCRIPCION: Conecta al servidor opc seleccionado de la lista lstservidores activa el timer de actualización _ del tiempo de corrida del servidor rellena los campos de información del servidor desactiva el botón y _ activa el de desconectar. Inicializa la variable myserver. Si ya se encontraba conectado al hacer click _ en el botón se procede a desconectar el servidor 'PARAMETROS: _ Entrada: _ Salida: _ 'RETORNO: _ 'VARIABLES: _ On Error GoTo errortrap Screen.MousePointer = vbHourglass '-------------------------------------------------------------'CONEXION ' If cmdConectar.Caption = "&Prueba de Conexión" Then Set F_Myserver = New OPCServer Dim dummie As Variant If txtNodename.Text <> "" Then dummie = MsgBox("Esta operación puede tardar unos minutos dependiendo de su conexión", vbOKCancel, "Conexion a servidor Remoto") If dummie = vbCancel Then Screen.MousePointer = vbDefault Exit Sub End If End If If cboServidores <> "" And cboServidores <> "Servidores" Then 'Aqui se lleva a cabo la conexion al servidor OPC F_Myserver.Connect cboServidores, CVar(txtNodename.Text) 'Mostrar los detalles del servidor al que recien se ha conectado With F_Myserver lblServerinfo(0) = .ServerName lblServerinfo(1) = .VendorInfo lblServerinfo(2) = CStr(.StartTime) lblServerinfo(3) = CDate(.CurrentTime - .StartTime) End With trmTrtimer.Enabled = True cmdConectar.Caption = "Finalizar &Prueba" Else MsgBox "No ha selecionado un Servidor", vbExclamation, "Error" Screen.MousePointer = vbDefault Exit Sub End If MsgBox "Prueba de conexion satisfactoria", vbOKOnly + vbInformation, "Prueba de Conexión" '-------------------------------------------------------------'DESCONEXION ' Else 'Ya se encontraba conectado y se desea desconectar 'Desconecto al servidor F_Myserver.Disconnect trmTrtimer.Enabled = False 'Libero la memoria Set F_Myserver = Nothing cmdConectar.Caption = "&Prueba de Conexión" Screen.MousePointer = vbDefault End If Screen.MousePointer = vbDefault '--------------------------------------------------------------Exit Sub errortrap: Select Case err.Number Case &H80070002 Beep MsgBox "No es posible comunicarse con el servidor, puede estar caido o no existir" & vbNewLine _ & "Por favor verifique la configuracion del servidor OPC", vbCritical vbOKOnly _ , "Prueba de Conexion Fallida" Screen.MousePointer = vbDefault err.Clear Exit Sub Case &H80004005 Beep MsgBox "No es posible comunicarse con el servidor, puede estar caido o no existir" & vbNewLine _ & "Por favor verifique la configuracion del servidor OPC", vbCritical vbOKOnly _ , "Prueba de Conexion Fallida" Screen.MousePointer = vbDefault err.Clear Exit Sub Case &H1AD 'Error de conexion, permiso denegado para crear el objeto activex Beep MsgBox "No es posible comunicarse con el servidor, puede estar caido o no existir" & vbNewLine _ & "Por favor verifique la configuracion del servidor OPC", vbCritical vbOKOnly _ , "Prueba de Conexion Fallida" Screen.MousePointer = vbDefault err.Clear Exit Sub Case 430 Beep MsgBox "No es posible comunicarse con el servidor, puede estar caido o no existir" & vbNewLine _ & "Por favor verifique la configuracion del servidor OPC" & vbNewLine err.Description, vbCritical + vbOKOnly _ , "Prueba de Conexion Fallida" Screen.MousePointer = vbDefault err.Clear Exit Sub Case Else merrores err Resume Next End Select End Sub + + + & Private Sub cmdEliminar_Click() 'DESCRIPCION: _ Elimina un servidor de la lista, con la finalidad _ de evitar errores en la bd, los servidores no se Eliminan _ realmente, sino solo se deshabilitan 'PARAMETROS: _ Entrada: _ Salida: _ 'RETORNO: _ 'VARIABLES : _ query: _ string SQL _ On Error GoTo errores: Dim query As String Screen.MousePointer = vbHourglass If lstServidores_BD.ListIndex = -1 Then 'No hay ningun servidor seleccionado MsgBox "No ha seleccionado un servidor de la lista", vbExclamation, "Error" Screen.MousePointer = vbDefault Exit Sub End If If MsgBox("Esta Seguro?, se borraran tambien todos los items asociados " & vbNewLine & _ " a este servidor OPC", vbYesNoCancel + vbExclamation, "Confirmación de Borrado") = vbYes Then 'Las siguientes querys a la BD borran tanto el servidor como los items query = "DELETE FROM servidores WHERE serverid = " & lstServidores_BD.ItemData(lstServidores_BD.ListIndex) dtable.dbconex.Execute query query = "DELETE FROM scadaopc WHERE serverid = " & lstServidores_BD.ItemData(lstServidores_BD.ListIndex) dtable.dbconex.Execute query 'Elimino al sevidor de la lista With lstServidores_BD .RemoveItem .ListIndex End With 'Desactivo el botn elimiar si no hay mas servidores en la BD If lstServidores_BD.ListCount <= 0 Then cmdEliminar.Enabled = False End If End If Screen.MousePointer = vbDefault Exit Sub errores: MsgBox err.Description & " " & err.Number End End Sub Private Sub Form_Load() 'DESCRIPCION: _ Esta subrutina corre al momento de cargar el formulario. Se encarga de ajustar todo lo relacionado con la posición _ visual de los controles. Además busca los servidores registrados en la BD y los muestra en la lista lstServidores_BD 'PARAMETROS: _ Entrada: _ Salida: _ 'RETORNO: _ 'VARIABLES : _ i: _ Contador genérico _ rsServers: _ Recordset temporal utilizado para recuperar los servidores _ de la BD 'Solamente ajustando la estetica un poco Dim i As Integer Dim rsServers As Recordset For i = lblServerinfo.LBound To lblServerinfo.UBound lbl1(i).Top = 900 + (240 * i) lblServerinfo(i).Top = 900 + (240 * i) lbl1(i).Left = 10 lblServerinfo(i).Left = 960 Next With dtable If .rsservidores.State = adStateOpen Then .rsservidores.Close End If 'Leo La BD mediante el comando dtable.Servidores .Servidores Set rsServers = .rsservidores End With 'Lllenar la lista de servidores registrados en la BD If rsServers.RecordCount > 0 Then 'Hay al menos un servidor With rsServers .MoveFirst Do While .EOF = False lstServidores_BD.AddItem .Fields("Servername").Value & " @ " & .Fields("Nodename").Value lstServidores_BD.ItemData(lstServidores_BD.NewIndex) = .Fields("serverid").Value .MoveNext Loop End With Else cmdEliminar.Enabled = False End If End Sub Private Sub cmdlistservs_Click() 'DESCRIPCION: ' Esta funcion crea una lista de los servidores disponibles _ el nodo "TxtTNodename". La lista es mostrada en el control _ lstServidores 'PARAMETROS: _ Entrada: _ Salida: _ 'RETORNO: _ 'VARIABLES: 'On Error GoTo errortrap cboServidores.Enabled = False If buscarOPC(txtNodename.Text) Then cboServidores.Enabled = True MsgBox "Se encontraron " & cboServidores.ListCount _ & " Servidores" & vbNewLine _ & "Puede revisarlo haciendo clic en la lista de servidores" _ , vbInformation + vbOKOnly, "Búsqueda Exitosa" cmdAgregar.Enabled = True cmdConectar.Enabled = True Else MsgBox "No fue posible listar los Servidores", vbOKOnly + vbCritical, "Error" End If Exit Sub errortrap: merrores err End Sub Private Sub F_MyServer_ServerShutDown(ByVal Reason As String) 'DESCRIPCION: _ Evento que se dispara cuando el servidor es cerrado inesperadamente 'PARAMETROS: _ Entrada: _ Salida: _ 'RETORNO: _ 'VARIABLES: On Error GoTo errores trmTrtimer.Enabled = False MsgBox "El servidor OPC " & F_Myserver.ServerName & " ha finalizado inesperadamente" & vbNewLine _ & "Razon: " & Reason, vbCritical + vbOKOnly, "Desconexion del Servidor OPC" Set F_Myserver = Nothing Exit Sub errores: merrores err End Sub Private Sub trmTrtimer_Timer() 'DESCRIPCION: _ ' Este evento se dispara luego de TRtimer.interval segundos _ Su funcion es actualziar periodicamente el tiempo que lleva _ vorriendo el servidor opc 'PARAMETROS: _ Entrada: _ Salida: _ 'RETORNO: _ 'VARIABLES: On Error Resume Next If F_Myserver.ServerState = OPCRunning Then lblServerinfo(3) = CDate(F_Myserver.CurrentTime - F_Myserver.StartTime) Else If F_Myserver.ServerState = OPCFailed Then trmTrtimer.Enabled = False MsgBox "Ha ocurrido un error inesperado en el servidor OPC" End If End If End Sub Private Function buscarOPC(nodo As String) As Boolean 'DESCRIPCION: 'Esta funcion crea una lista de los servidores disponible _ el nodo. La lista es mostrada en el control lstServidores 'PARAMETROS: 'Entrada: _ nodo: _ direccion donde se buscaran los servidores 'Salida: _ 'RETORNO _ buscarOPC: booleano que indica si fue posible o no listar los _ servidores OPC del nodo especificado 'VARIABLES: ' Getserver: _ Una variable del OPCserver para poder utilizar el _ metodo GetOPCServers _ servers: _ Un array donde se almacenan los servidores opc encontrados _ i: _ contador generico Dim Getserver As OPCServer Dim servers As Variant Dim i As Integer On Error GoTo errortrap buscarOPC = False Screen.MousePointer = vbHourglass cboServidores.Clear Set Getserver = New OPCServer 'Buscar lista de servidores en el nodo servers = Getserver.GetOPCServers(nodo) buscarOPC = True 'Rellenar el combobox lstServidores con la lista de servidores OPC For i = LBound(servers) To UBound(servers) cboServidores.AddItem servers(i) Next i 'Liberar los objetos creados Set Getserver = Nothing Set servers = Nothing Screen.MousePointer = vbDefault Exit Function errortrap: 'RUTINA DE MANEJO DE ERRORES O EXCEPCIONES Select Case err.Number Case &H80070002 ' Falla de Conexion 'conex_status = False MsgBox "No fue posible realizar la conexion", _ vbCritical, "Error" Screen.MousePointer = vbDefault err.Clear buscarOPC = False Exit Function Case &H80004005 'conex_status = False MsgBox "No fue posible realizar la conexion al equipo remoto", _ vbCritical, "Error" err.Clear Screen.MousePointer = vbDefault buscarOPC = False Exit Function Case &H1AD 'Error de conexion, permiso denegado para crear el objeto activex 'conex_status = False MsgBox "No es posible realizar la verificacion, " & vbNewLine & _ " revise la configuracion de DCOM " & vbNewLine & "Error #" & err.Number & _ " " & err.Description, vbCritical, "Error" err.Clear Screen.MousePointer = vbDefault buscarOPC = False Exit Function Case Else merrores err Resume Next End Select End Function Private Sub txtNodename_Change() 'DESCRIPCION: _ Evento que se dispara al cambiar el texto escrito en _ txtNodename. Modifica el caption de boton cmdListservs _ para incluir el nombre o la direcicon del nodo. 'PARAMETROS: _ Entrada: _ Salida: _ 'RETORNO: _ 'VARIABLES: _ If txtNodename.Text = vbNullString Then cmdListservs.Caption = "Buscar &Servidores en: Máquina Local" Else cmdListservs.Caption = "Buscar &Servidores en: " & txtNodename.Text End If End Sub 'NOMBRE DEL FORMULARIO: frmVerBD 'DESCRIPCION: ' En este objeto se hace un browsing de todos los servidores OPC, buscando la como clave basica _ la Dirección IP del se servidor 'OBJETOS INCLUIDOS 'uc (UserControl): ' usrCampos: _ Un control definido por el usuario creado para manejar la data de la BD _ 'Menu: 'mnuAgregar: _ Agregar un item Manualmente _ mnuAgregarAuto: _ Agregar un item Asistidamente _ mnuModificar: _ Titulo de menu, donde se agrupan los submenus de agregar y eliminar campos _ mnuRemove_all: _ Borrar todos los items _ mnuRemove_item: _ Borra los items selecionados _ mnuUpdateBD: _ Actuliza la Base de Datos _ mnuValidarBD: _ Valida la base de datos _ 'Label: ' lblTop(): _ Labels genericos, muestran informacion visual sobre otros objetos Option Base 1 Option Explicit 'Variables de VerBD _ oldPos: La utiliza la funcion que maneja el scroll vertical Vbscroll1 _ itemsopc: Variable para almacenar los items opc _ datosBD: recordset donde se almacena los items presentes en la BD _ ServerBD: recordset donde se almacena los servidores presentes en la BD _ 'Constantes de diseño '####################################### Const anchoVsb = 360 Const tope1 = 315 '####################################### Dim Dim Dim Dim oldPos As Integer itemsOPC As OPCItems datosBD As Recordset serverBD As Recordset '*********************************************************** 'CARGA Y DESCARGA DEL FORMULARIO '*********************************************************** Private Sub Form_Load() 'DESCRIPCION: _ Subrutina que se ejecuta al moemento en que se carga el formulario 'PARAMETROS: _ Entrada: _ Salida: _ 'RETORNO: _ 'VARIABLES: _ i: contador generico _ totalfilas: la cantidad de registros de items presentes en la BD _ Scroller: _ VScroller1: Scroller vertical _ iFullFormHeigth: _ iDisplayHeight: _ Campos: Todos arreglos de tamaño totalfilas _ usrCampos(): _ Campo donde se muetra al usuario la informacion de los _ elementos opc almacenads en la base de datos On Error GoTo errores Dim conex As Connection Dim i As Integer Dim totalfilas As Integer Dim rsNumRecords As Recordset Me.Width = 3800 Me.Height = 4000 Set conex = dtable.dbconex If conex.State = adStateClosed Then conex.Open End If 'Variables para el scroller Dim iFullFormHeigth As Integer Dim iDisplayHeight As Integer 'Fin variables scroller Set rsNumRecords = conex.Execute("SELECT id FROM scadaopc") totalfilas = rsNumRecords.RecordCount Set rsNumRecords = Nothing 'Altura del form iDisplayHeight = 3820 Me.Height = iDisplayHeight Me.Top = 0 Me.Left = 0 Me.Width = 0 If totalfilas < 9 Then iFullFormHeigth = iDisplayHeight ' 360 * 12 vsbScroll.Visible = False Else iFullFormHeigth = usrCampos(1).Height * (totalfilas + 3) End If With vsbScroll .Height = Me.ScaleHeight .Min = 0 .Max = iFullFormHeigth - iDisplayHeight .SmallChange = Screen.TwipsPerPixelY * 100 .LargeChange = .SmallChange End With 'Inicializacion de los campos (Ubicacion en el form) '--------------------------------------------------With usrCampos(1) .Top = tope1 .Left = 0 Me.Width = Me.Width + .Width End With With vsbScroll .Top = 0 .Width = anchoVsb Me.Width = Me.Width + .Width .Left = usrCampos(1).Left + usrCampos(1).Width End With For i = lblTop.LBound To lblTop.UBound lblTop(i).Height = usrCampos(1).Height lblTop(i).Top = 0 Next With usrCampos(1) lblTop(2).Left = .Server_Left lblTop(8).Left = .B1_Left lblTop(9).Left = .B2_Left lblTop(10).Left = .B3_Left lblTop(11).Left = .Elem_Left lblTop(12).Left = .Info_Left lblTop(1).Left = .Id_Left lblTop(5).Left = .opckey_Left lblTop(6).Left = .datatype_Left lblTop(13).Left = .TipoSenal_Left End With Me.Width = usrCampos(1).Width + vsbScroll.Width Debug.Print usrCampos(1).Width & "FORMA" '--------------------------------------------------'Proceso de Cargado (LOAD) de las líneas necesarias For i = 2 To totalfilas cargalinea (i) Next 'Rellenado de dichas líneas con la data proveniente de la BD rellenalinea 'Esconder primera línea si no hay registros en la bd If totalfilas = 0 Then LineaVisible False End If Exit Sub errores: Select Case err.Number Case (-2147467259) MsgBox "Faltan archivo indispensables para la ejecución del programa" & vbNewLine _ & err.Description, vbCritical + vbOKOnly, "Error" Case Else merrores err End Select End Sub Private Sub Form_Unload(Cancel As Integer) 'DESCRIPCION: _ Esta subrutina se efectua al momento de cerrar el form _ y su finalidad es liberar los accesos a la BD 'PARAMETROS: _ Entrada: _ Salida: _ 'RETORNO: _ 'VARIABLES: _ 'Cierro los recordset asociados a la BD With dtable If .rsleer.State = adStateOpen Then .rsleer.Close End If If .rsservidores.State = adStateOpen Then .rsservidores.Close End If End With 'Set datosBD = Nothing Set serverBD = Nothing Set itemsOPC = Nothing Unload frmrptvalid oldPos = 0 End Sub '*********************************************************** 'ACTUALIZACION DE LA BASE DE DATOS '*********************************************************** Private Sub mnuUpdateBD_Click() 'DESCRIPCION: _ Este evento se dispara al hacer click en en el menu actualizar BD, aquí se actualizan los campos modificados así como se incorporan los campos nuevos 'PARAMETROS: _ Entrada: _ Salida: _ 'RETORNO: _ 'VARIABLES: 'i: _ Contador genérico Dim i As Integer '***************************************** ' Inicicialización de Recordsets With dtable If .rsleer.State = adStateOpen Then .rsleer.Close End If If .rsservidores.State = adStateOpen Then .rsservidores.Close End If End With dtable.leer Set datosBD = dtable.rsleer dtable.Servidores '******************************************* 'Si los Campos no estan aptos solicito la intervencion del usuario If ValidarCampos() = False Then Screen.MousePointer = vbDefault Exit Sub End If 'Comienzo recorrido por los elementos If datosBD.RecordCount > 0 Then 'Si hay campos en la BD datosBD.MoveFirst For i = usrCampos.LBound To usrCampos.UBound 'voy a recorrerlos todos If i <= datosBD.RecordCount Then 'Si i es menor o igual que el numero inicial, _ Actualizo los campos datosBD!id = i With usrCampos(i) datosBD!serverID = .serverID datosBD!access = .opcrw datosBD!opckey = .nombre datosBD!datatype = .DataTypeInt datosBD!scadakey = .scadaname datosBD!scadarw = .rw datosBD!tipodesenal = .senalID End With datosBD!modificado = Now() datosBD.MoveNext ElseIf usrCampos(i).server >= 0 Then 'Si i es mayor que el #inicial creo _ nuevos campos en la bd. Revalido que se haya seleccionado un servidor datosBD.AddNew With usrCampos(i) datosBD!id = i datosBD!serverID = .serverID datosBD!access = .opcrw datosBD!opckey = .nombre datosBD!datatype = .DataTypeInt datosBD!scadakey = .scadaname datosBD!scadarw = .rw datosBD!tipodesenal = .senalID End With datosBD!modificado = Now() End If Next Else 'No habia ningun dato en la bd For i = usrCampos.LBound To usrCampos.UBound 'voy a recorrerlos todos datosBD.AddNew datosBD!id = i With usrCampos(i) datosBD!serverID = .serverID datosBD!access = .opcrw datosBD!opckey = .nombre datosBD!datatype = .DataTypeInt datosBD!scadakey = .scadaname datosBD!scadarw = .rw datosBD!tipodesenal = .senalID End With datosBD!modificado = Now() Next End If datosBD.UpdateBatch datosBD.Close End Sub '************************************************************ 'REMOCION DE ITEMS '************************************************************ Private Sub mnuremove_item_Click() 'DESCRIPCION: _ Se encarga de Remover los items previamente selecionados _ 'PARAMETROS: _ Entrada: _ Salida: _ 'RETORNO: _ 'VARIABLES: _ i: _ contador generico _ ultimo_lista: _ entero que almacena el indice del ultimo campo _ QuedaUna: _ Booleano que indica si borrando las lineas selccionadas _ quedan o no lineas adicionales_ On Error GoTo errores: Dim i As Integer Dim ultimo_lista As Integer Dim QuedaUna As Boolean 'Reviso todas las checks a ver si todas estan marcadas QuedaUna = False For i = usrCampos.LBound To usrCampos.UBound If usrCampos(i).Checks = vbUnchecked Then QuedaUna = True Exit For End If Next If QuedaUna = False Then 'Procedo a borrar todosl los campos Call mnuRemove_all_Click Exit Sub End If i = usrCampos.LBound ultimo_lista = usrCampos.UBound 'Recorro todos los registros - el ultimo por ser un caso especial Do While i < ultimo_lista If usrCampos(i).Checks = vbChecked Then 'Query a la BD dtable.dbconex.Execute "DELETE * FROM scadaopc WHERE id=" & usrCampos(i).ids DescargaLinea (i) vsbScroll.Max = vsbScroll.Max - usrCampos(1).Height i = i - 1 ultimo_lista = usrCampos.UBound End If i = i + 1 Loop 'Trato el ultimo de la lista If usrCampos.LBound = usrCampos.UBound Then 'Solo queda un item If usrCampos(usrCampos.LBound).Checks = vbChecked Then Call mnuRemove_all_Click Exit Sub 'dtable.dbconex.Execute "DELETE * FROM scadaopc WHERE id=" & usrCampos(i).ids 'LineaVisible (False) Else Exit Sub ' Solo quesda una linea pero no esta marcada para borrar End If Else If usrCampos(usrCampos.UBound).Checks = vbChecked Then 'Query a la BD dtable.dbconex.Execute "DELETE * FROM scadaopc WHERE id=" & usrCampos(usrCampos.UBound).ids DescargaLinea (usrCampos.UBound) vsbScroll.Max = vsbScroll.Max - usrCampos(1).Height i = i - 1 ultimo_lista = usrCampos.UBound End If End If ultimo_lista = usrCampos.UBound Exit Sub '-------------------------------------------------------'MANEJO DE ERRORES errores: Select Case err.Number Case 340 ' Error al eliminar err.Clear MsgBox "Error inesperado al tratar de eliminar el item" Resume Next Case Else merrores err err.Clear End Select Resume Next End Sub Private Sub mnuRemove_all_Click() 'DESCRIPCION: _ 'Esta subrutina elimina todos los items OPC que se _ encuentren en la BD. Se hace requiere la confirmacion _ del usuario _ 'PARAMETROS: _ 'Entrada: _ 'Salida: _ 'RETORNO: _ 'VARIABLES: _ ultimo_lista: _ Entero utilizado para almacenar el indice de l ultimo _ item en la lista. _ i: _ contador generico Dim ultimo_lista As Integer Dim i As Integer If MsgBox("¿Esta seguro que desea borrar todos los registros?", vbYesNoCancel + vbExclamation, "Borrar todos los Registros") = vbYes Then 'Ejecuto el query encargado de borrar toda la tabla dtable.dbconex.Execute "DELETE FROM scadaopc" 'Ajusto el scrollbar vsbScroll.Max = vsbScroll.Max - (usrCampos(1).Height * usrCampos.UBound) 'Elimino todos los campos ultimo_lista = usrCampos.UBound i = usrCampos.LBound Do While i < ultimo_lista DescargaLinea (i) i = i - 1 ultimo_lista = usrCampos.UBound i = i + 1 Loop 'Escondo la primera linea LineaVisible (False) End If End Sub '************************************************************ 'AGREGADO DE ITEMS '************************************************************ Private Sub mnuAgregar_Click() 'DESCRIPCION: _ Este menu se encarga de ingresar un nuevo campo _ OPC de forma manual 'PARAMETROS: _ 'Entrada: _ 'Salida: _ 'RETORNO: _ 'VARIABLES: _ On Error GoTo errortrap: Dim Index As Integer Index = usrCampos.UBound + 1 'Limitacion por el scroller If Index > 110 Then MsgBox "Actualmente limitado a 110 campos" Exit Sub End If If Index > 10 Then vsbScroll.Visible = True End If If Index = 2 And usrCampos(1).Visible = False Then 'No habia ningun campo en la lista usrCampos(1).ids = 1 LineaVisible True Index = 1 Exit Sub Else cargalinea (Index) usrCampos(Index).ids = usrCampos(Index - 1).ids + 1 End If 'Rellenar la lista de servidores serverBD.MoveFirst Do While serverBD.EOF = False usrCampos(Index).RellenarLstServers serverBD.Fields!ServerName, serverBD.Fields!Nodename, serverBD!serverID serverBD.MoveNext Loop usrCampos(Index).nombre = vbNullString usrCampos(Index).scadaname = vbNullString usrCampos(Index).nodo = vbNullString RellenarTipoSeñal Index 'Desplazar la vista hacia el nuevo item If Index > 10 Then vsbScroll.Max = vsbScroll.Max + usrCampos(Index).Height vsbScroll.Value = vsbScroll.Max End If Exit Sub 'MANEJO DE ERRORES '--------------------------------------------------errortrap: merrores err End Sub Private Sub mnuAgregarAuto_Click() 'DESCRIPCION: _ Abre el formulario para la busqueda de Items de forma asisitida 'VARIABLES: _ frmAgregarItem.Show vbModal End Sub Public Sub AgregadoAuto(serverID As Integer, elementID As String) 'DESCRIPCION: _ Esta funcion se encarga de ingresar los datos seleccionado en formulario frmAgregarItem 'PARAMETROS: _ 'Entrada: _ serverID: _ Entero que contiene la identificacion de servidor OPC _ elementID: _ String que contiene el identificador del item opc 'Salida: _ 'RETORNO: _ 'VARIABLES: _ Index : _ Entero que indica el indice en el qu se agregara el item _ (ultima posicion de la lista) Dim Index As Integer ‘On Error GoTo errortrap: Index = usrCampos.UBound + 1 'Limitacion de scrollbar, trabajndo en solucionarlo If Index > 110 Then MsgBox "Actualmente limitado a 110 campos" Exit Sub End If 'Si hay mas de 10 campos en pantalla, muestro el scrollbar If Index > 10 Then vsbScroll.Visible = True End If 'Si no habian ningun campo en el formulario, visibilizo _ el primero que estaba escondido If Index = 2 And usrCampos(1).Visible = False Then LineaVisible True Index = 1 Else 'Sino cargo una nueva linea cargalinea (Index) usrCampos(Index).ids = usrCampos(Index - 1).ids + 1 End If dtable.Servidores Set serverBD = dtable.rsservidores serverBD.MoveFirst Dim Pos_Defecto As Integer Do While serverBD.EOF = False usrCampos(Index).RellenarLstServers serverBD!ServerName, serverBD!Nodename, serverBD.Fields("serverid") If serverID = serverBD.Fields("serverid") Then Pos_Defecto = usrCampos(Index).ServerNewIndex usrCampos(Index).nodo = serverBD!Nodename End If serverBD.MoveNext Loop With usrCampos(Index) .nombre = elementID .scadaname = vbNullString .server = Pos_Defecto RellenarTipoSeñal Index End With vsbScroll.Max = vsbScroll.Max + usrCampos(1).Height Exit Sub errortrap: merrores err End Sub '*************************************************** 'SECCION VALIDACION OPC '*************************************************** Private Sub mnuvalidarBD_Click() 'DESCRIPCION: _ Validar cada linea, confirmando que realmente _ exista un dato en el opc server, ademas extrae _ informacion sobre las propiedades de lectura _ escritura del item durectamente del servidor OPC 'PARAMETROS: _ Entrada: _ Salida: _ 'RETORNO: _ 'VARIABLES: 'rsNumsrv: _ Recordset donde se almacena los servidores _ que tiene campos en la bd 'OPCsrvONE: _ Objeto OPCserver utilizado para validacion 'OPCsrvGroups: _ Objeto OPCGroups utilizado para validacion 'OPCsrvGroup: _ Objeto OPCGroup utilizado para validacion 'OPCsrvItems: _ Objeto OPCItems utilizado para validacion 'OPCsrvitem: _ Objeto OPCItem utilizado para validacion 'NumSrvtoVal: _ Entero que indica el # de servidores a validar _ NumItemstoVal: _ Entero que indica el # de items a validar por servidor _ i: _ Entero contador usado para llevar registro del _ numero de items validados por servidor _ j: _ Entero contador _ serverid: _ Entero que guarda el identificador del servidor _ servername: _ String donde se almacena temporalmente el nombre del servidor opc _ nodename: _ String donde se almacena temporalmente el nombre del servidor opc _ srvalido: _ booleano que indica si el ProgID de servidor fue devuelto por el método GetOPCservers 'conex_status: _ booleano que indica si hay o no conexión con el servidor opc 'conex_descrip: _ string utilizado en las rutinas de error para describir los errores en la conexión 'servers: _ Array donde se almacena la lista de servidores devuelta por método getOPCservers _ vname: _ Variant generico que se usa para trabajar con los lazos for each 'itemcount: _ Entero que indica el número de ítems por servidor 'errores(): _ Arreglo donde se almacena los errores devueltos al validar un ítem (en caso de no existir se llena de ceros" 'opcname (1): _ Arreglo de un solo valor donde se almacena el opc ID del item antes de ingresarlo a la funcion_ On Error GoTo errores: 'BD Dim conexBD As Connection Dim rsNumsrvBD As Recordset 'OPC Dim OPCsrvONE As OPCServer Dim OPCsrvGroups As OPCGroups Dim OPCsrvGroup As OPCGroup Dim OPCsrvItems As OPCItems Dim OPCsrvitem As OPCItem Dim Dim Dim Dim Dim numItemErrados As Integer NumSrvtoVal As Integer i As Integer j As Integer serverID As Integer Dim ServerName As String Dim Nodename As String Dim srvalido As Boolean Dim conex_status As Boolean Dim conex_descrip As String 'Validacion Dim servers As Variant Dim vname As Variant Dim itemcount As Long Dim errores() As Long Dim opcname(1) As String Screen.MousePointer = vbHourglass 'Si los Campos no estan aptos solicito la intervencion del usuario 'No es la validacion en servidores OPC If ValidarCampos() = False Then Screen.MousePointer = vbDefault Exit Sub Else 'Actualizar la BDn para incorporar los ultimos cambios efectuados Call mnuUpdateBD_Click End If 'Verifico el estado de los recorsets a utilizar Set conexBD = dtable.dbconex Set rsNumsrvBD = conexBD.Execute("SELECT DISTINCT(serverid) FROM scadaopc") If datosBD.State = adStateClosed Then dtable.leer Set datosBD = dtable.rsleer Else dtable.rsleer.Requery End If If serverBD.State = adStateClosed Then dtable.Servidores Set serverBD = dtable.rsservidores Else dtable.rsservidores.Requery End If 'Mostrar el formulario anexo para mostrar el reporte de progreso de la validación With frmrptvalid .Top = Me.Height .Width = Me.Width .Left = Me.Left .Visible = True .Show End With 'Inicializacion de las barras de progreso en la forma frmrtpvalid frmrptvalid.PBvalidacion_srv = 0 frmrptvalid.PBvalidacion_items = 0 NumSrvtoVal = rsNumsrvBD.RecordCount srvalido = False 'Comienza lazo de validacion rsNumsrvBD.MoveFirst Do 'Obtengo el server ID del recorset rsNumsrBD serverID = rsNumsrvBD!serverID 'En el siguiente lazo DO WHILE Obtengo el nombre del servidor _ y su nodo serverBD.MoveFirst Do While serverBD.EOF = False If serverBD!serverID = serverID Then ServerName = serverBD!ServerName Nodename = serverBD!Nodename Exit Do End If serverBD.MoveNext Loop 'Se inicaliza el objeto OPCsrvONE con la finalidad de utilizar _ sus metodos de validacion Set OPCsrvONE = New OPCServer 'A continuacion llevo a cabo una verificacion en el listado de _ los servidores disponibles en el nodo asociado reportar "Verificando existencia de " & ServerName & " en " & Nodename & "... ", False 'Si se esta buscando un servidor en localhsot se hace el _ siguiente ajuste If Nodename = "127.0.0.1" Then Nodename = vbNullString End If servers = OPCsrvONE.GetOPCServers(Nodename) For Each vname In servers If vname = ServerName Then srvalido = True Exit For End If Next vname If srvalido Then 'Servidor encontrado en el nodo asociado reportar " OK", True reportar "Validando la posibilidad de conexion con " & ServerName & "...", True 'Una vez encontrado, procedemos a intentar conectarnos conex_status = True 'En caso de error de conexion una rutina de error _ hara conex_status = false 'CONEXION AL SERVIDOR OPC OPCsrvONE.Connect ServerName, Nodename If conex_status And OPCsrvONE.ServerState = OPCRunning Then 'La conexion se ha realizado exitosamente reportar " OK", True numItemErrados = 0 i = 0 frmrptvalid.PBvalidacion_items.Value = 0 'i = 0 For j = usrCampos.LBound To usrCampos.UBound 'Aqui incremento la barra de progreso asociada a los items With frmrptvalid.PBvalidacion_items If .Value + (.Max / usrCampos.UBound) <= 100 Then .Value = .Value + (.Max / usrCampos.UBound) Else .Value = 100 End If End With If usrCampos(j).serverID = serverID Then i = Set Set Set i + 1 OPCsrvGroups = OPCsrvONE.OPCGroups OPCsrvGroup = OPCsrvGroups.Add("validacion") OPCsrvItems = OPCsrvGroup.OPCItems 'Dado que la fucnion validate solo acepta arrays 'convierto el nombre opc a un array llamado opcname opcname(1) = usrCampos(j).nombre '############################################## 'Aqui se lleva a cabo la validacion OPC OPCsrvItems.Validate 1, opcname, errores '############################################## If errores(1) Then numItemErrados = numItemErrados + 1 reportar opcname(1) & " Error #" & errores(1) & "", True With usrCampos(j) .VldCode IOS_OPC_FALLO_ITEM End With Else Set OPCsrvitem = OPCsrvItems.AddItem(opcname(1), 1) 'Aqui debajo obtengo los permisos de acceso de los items With usrCampos(j) .VldCode IOS_OPC_OK End With With usrCampos(j) .opcrw = OPCsrvitem.AccessRights .DataTypeInt = OPCsrvitem.CanonicalDataType End With End If 'Reseteo la variable de grupos opc, con la finalidad de 'reutilizarla OPCsrvGroups.RemoveAll End If Next reportar "Finalizada la Validacion para el servidor " & ServerName & " (" & Nodename & ") " & vbNewLine & _ "Se verificaron " & i & " items, encontrandose " & numItemErrados & " con erorres", True 'Desconecto al servidor OPC OPCsrvONE.Disconnect Else If conex_status = False Then 'El servidor esta visible pero no es posible conectarse a el reportar "FALLO: " & conex_descrip, True err.Clear For j = usrCampos.LBound To usrCampos.UBound If usrCampos(j).serverID = serverID Then With usrCampos(j) .VldCode IOS_OPC_FALLO_CONEXION End With End If Next Else 'Es posible conectarse al servidor, pero este no esta corriendo adecuadamente If debugging > 0 Then MsgBox OPCsrvONE.ServerState End If End If End If Else 'No se logró encontrar al servidor OPC en el nodo asociado reportar " FALLO", True 'Se indican los items cuyo servidor no pudo ser localizado marcándolos en rojo For j = usrCampos.LBound To usrCampos.UBound If usrCampos(j).serverID = serverID Then usrCampos(j).VldCode IOS_OPC_SERVIDOR_NO_ENCONTADO End If Next End If 'Actualizo la barra de progreso de servidores en frmrptvalid With frmrptvalid.PBvalidacion_srv If .Value + (.Max / usrCampos.UBound) <= 100 Then .Value = .Value + (.Max / NumSrvtoVal) Else .Value = 100 End If End With srvalido = False rsNumsrvBD.MoveNext Loop While rsNumsrvBD.EOF = False 'FINAL CICLO PRINCIPAL 'Cierro los Recorset rsNumsrvBD.Close Screen.MousePointer = vbDefault Exit Sub errores: 'Aqui se manejan los errores y la excepciones que genera el codigo Select Case err.Number Case &H80070002 ' Falla de Conexion conex_status = False conex_descrip = "No fue posible realizar la conexion" err.Clear Resume Next Case &H80004005 conex_status = False conex_descrip = "No fue posible realizar la conexion" err.Clear Resume Next Case &H1AD 'Error de conexion, permiso denegado para crear el objeto activex conex_status = False conex_descrip = "No es posible realizar la verificacion, " & vbNewLine & _ " revise la configuracion de DCOM " & vbNewLine & "Error #" & err.Number & _ " " & err.Description err.Clear Resume Next Case Else 'merrores err Resume Next End Select End Sub '*************************************************** 'SECCION VALIDACION CAMPOS '*************************************************** Private Function ValidarCampos() As Boolean 'DESCRIPCION: _ Esta funcion validara que los campos esten aptos para ser _ ingresados en la BD o validados en los servidores OPC 'PARAMETROS: _ 'Entrada: _ 'Salida: _ 'RETORNO: _ ValidarCampos: _ Booleando que indica si la validacion se completo con exito o no 'VARIABLES: _ i: _ Contador generico Dim i As Integer For i = usrCampos.LBound To usrCampos.UBound 'voy a recorrerlos todos los campos If usrCampos(i).server < 0 Then vsbScroll.Value = Round(((i / usrCampos.UBound) * vsbScroll.Max), 0) MsgBox "Al item #" & i & " no se le ha asignado servidor", vbExclamation, "Error" ValidarCampos = False Exit Function End If If usrCampos(i).nombre = vbNullString Then vsbScroll.Value = Round(((i / usrCampos.UBound) * vsbScroll.Max), 0) MsgBox "Al item #" & i & " no posee identificador OPC", vbExclamation, "Error" ValidarCampos = False Exit Function End If If debugging = 0 Then If usrCampos(i).scadaname = vbNullString Then MsgBox "Al item #" & i & " no posee identificador SCADA", vbExclamation, "Error" ValidarCampos = False Exit Function End If End If Next '-------------------------------------------------------ValidarCampos = True End Function Private Sub reportar(reporte As String, nuevalinea As Boolean) 'DESCRIPCION: _ Esta funcion se encarga escribir los reportes en el formulario _ frmRptValid 'PARAMETROS: _ 'Entrada: _ reporte: _ Cadena de caracter a mostrar en la forma de reportes _ nuevalinea: _ Booleano que indica se desea que luego del reporte se incerte _ una nueva linea 'Salida: _ 'RETORNO _ 'VARIABLES: _ If nuevalinea Then frmrptvalid.txtrptvalid.Text = frmrptvalid.txtrptvalid.Text & reporte & vbNewLine Else frmrptvalid.txtrptvalid.Text = frmrptvalid.txtrptvalid.Text & reporte End If End Sub '*********************************************************** 'SCROLLER '*********************************************************** Private Sub vsbScroll_Change() 'DESCRIPCION: _ Evento del ScrollBar, se lama a la funcion pScrollForm 'PARAMETROS: _ 'Entrada: _ 'Salida: _ 'RETORNO _ 'VARIABLES: _ Call pScrollForm End Sub Private Sub vsbScroll_Scroll() 'DESCRIPCION: _ Evento del ScrollBar, se lama a la funcion pScrollForm 'PARAMETROS: _ 'Entrada: _ 'Salida: _ 'RETORNO _ 'VARIABLES: _ Call pScrollForm End Sub Private Sub pScrollForm() 'DESCRIPCION: _ Subrutina que controla el desplazamiento vertical de _ los controles al accionar sobre el scroll bar 'PARAMETROS: _ 'Entrada: _ 'Salida: _ 'RETORNO _ 'VARIABLES: _ ctr: Control Generico Dim ctl As Control For Each ctl In Me.Controls If (TypeOf ctl Is uc) Then ' Solo se mueven los usrcontrols ctl.Top = ctl.Top + oldPos - vsbScroll.Value End If Next oldPos = vsbScroll.Value End Sub '*********************************************************** 'MANEJO DE CAMPOS EN EL FORMULARIO '*********************************************************** Private Sub rellenalinea() 'DESCRIPCION: _ Esta funcion rellena la lista con lo valores _ previamente almacendos En la BD, tambien crea un _ grupo opc, con lo que posibilita que los valores _ sean recuperados del OPCserver 'PARAMETROS: _ 'Entrada: _ 'Salida: _ 'RETORNO: _ 'VARIABLES: _ OPCxxx: _ Variables OPC necesarias _ i: _ Contador genérico _ conexBD: _ conexión a la base de datos _ datosBD: _ Recordset donde se almacena la información de los ítems encontrados _ serverBD: _ Recordset donde se almacena la información de los servidores encontrados _ rsSenalBD: _ Recordset donde se almacena la información sobre la identificación de las señales _ numlista: _ Se usa para almacenar la posición en el combobox que indica el servidor asociado a un ítem opc, así pueden _ rellenarse todo el combo con los nombres de los servidores y dejar seleccionado el servidor actualmente asignado _ en la bd '***************************************** Dim i As Integer Dim numlista As Integer Dim conexBD As Connection Dim rsSenalBD As Recordset '**************************************** Set conexBD = dtable.dbconex Set rsSenalBD = conexBD.Execute("SELECT * from tiposdesenal") Set datosBD = conexBD.Execute("SELECT * FROM scadaopc ORDER BY id ASC ") Set serverBD = conexBD.Execute("SELECT * from servidores") 'End With If datosBD.EOF = False Then datosBD.MoveFirst For i = usrCampos.LBound To usrCampos.UBound With usrCampos(i) 'Rellenar los ids .ids = datosBD.Fields("id").Value 'Rellenar los OPCid .nombre = datosBD.Fields!opckey 'Rellenar el ScadaID .scadaname = (datosBD.Fields("scadakey").Value) 'Rellenar el datatype .DataTypeInt = datosBD!datatype 'Inicializar los checkmarks .Checks = vbUnchecked 'Set rw y opcrw .rw = datosBD.Fields("scadarw").Value .opcrw = datosBD.Fields("access").Value End With 'Rellenar los combobox de servidores serverBD.MoveFirst Do While serverBD.EOF = False usrCampos(i).RellenarLstServers serverBD!ServerName, serverBD!Nodename, serverBD.Fields("serverid") If serverBD!serverID = datosBD.Fields("serverid").Value Then numlista = usrCampos(i).ServerNewIndex usrCampos(i).nodo = serverBD!Nodename End If serverBD.MoveNext Loop usrCampos(i).server = numlista 'Rellenar los combobox de tipos de señales rsSenalBD.MoveFirst Do While rsSenalBD.EOF = False usrCampos(i).RellenarLstSenales rsSenalBD!descripcion, rsSenalBD!id rsSenalBD.MoveNext Loop usrCampos(i).senalID = datosBD!tipodesenal datosBD.MoveNext Next Else 'Si no hay datos relleno la primera linea solamente serverBD.MoveFirst Do While serverBD.EOF = False usrCampos(1).RellenarLstServers serverBD!ServerName, serverBD!Nodename, serverBD.Fields("serverid") usrCampos(1).nodo = serverBD!Nodename serverBD.MoveNext Loop 'Rellenar los combobox de tipos de señales rsSenalBD.MoveFirst Do While rsSenalBD.EOF = False usrCampos(1).RellenarLstSenales rsSenalBD!descripcion, rsSenalBD!id rsSenalBD.MoveNext Loop LineaVisible True End If 'Limpieza de Memoria Set conexBD = Nothing Set rsSenalBD = Nothing End Sub Private Sub cargalinea(Index As Integer) 'DESCRIPCION: _ Función que se encarga de crear los campos que sean necesarios para mostrar los datos en pantalla 'PARAMETROS: _ Entrada: _ Index: _ Indica el orden de la linea que se va a cargar _ Salida: _ 'RETORNO: _ 'VARIABLES: _ Load usrCampos(Index) With usrCampos(Index) .Top = usrCampos(Index - 1).Top + 315 .Visible = True End With End Sub Private Sub DescargaLinea(Index As Integer) 'DESCRIPCION: _ Funcion que se encarga de eliminar los campos que sean requeridos mediante los checkmarks a la izquierda_ Aunque aparenta remover la línea, en verdad la única línea que se elimina es la última, previa traslación de todas_ las líneas a una superior (a partir de la que se desea eliminar 'PARAMETROS: _ Entrada: _ Index: _ Indica la línea en la que hay un checkmark activado _ Salida: _ 'RETORNO: _ 'VARIABLES: 'Ultimo: Indica el límite superior de las líneas 'i: Contador generico Dim ultimo As Integer Dim i As Integer ultimo = usrCampos.UBound 'Caso particular If Index = 1 And ultimo = 1 Then ' Estamos el caso de que solo quede una linea usrCampos(1).Visible = False vsbScroll.Visible = False Exit Sub End If 'Caso Normal If Index <> usrCampos.UBound Then 'Si fuese el ultimo solo se elimina sin afectar el resto de las lineas For i = Index To usrCampos.UBound - 1 With usrCampos(i) .Checks = usrCampos(i + 1).Checks .nombre = usrCampos(i + 1).nombre .ids = usrCampos(i + 1).ids .server = usrCampos(i + 1).server .nodo = usrCampos(i + 1).nodo .scadaname = usrCampos(i + 1).scadaname .rw = usrCampos(i + 1).rw .opcrw = usrCampos(i + 1).opcrw .Visible = True End With Next End If 'Aqui se remueve la ultima linea Unload usrCampos(ultimo) 'Si quedan pocos campos, oculto el scroll bar If usrCampos.UBound <= 10 Then vsbScroll.Visible = False End If End Sub Private Sub LineaVisible(Accion As Boolean, Optional Index As Integer = 1) 'DESCRIPCION: 'Esconde de la vista del usuario la linea # "Index" 'PARAMETROS: _ Entrada: _ Index: _ Entero que indica el numero de la linea procesar 'Salida: _ 'RETORNO: _ 'VARIABLES: _ With usrCampos(Index) .Visible = Accion End With End Sub Private Sub RellenarTipoSeñal(Index As Integer, Optional Value As Integer = 0) 'DESCRIPCION: _ Esta subrutina se encarga de llenar los combobox del usrcampos donde se muestra el tipo de señal asignado _ al elemento 'PARAMETROS: _ Entrada: _ Salida: _ 'RETORNO: _ 'VARIABLES: _ conex: _ conexión a la base de datos _ rsSenalBD: _ Recordset donde se almacena la información sobre la identificación de las señales _ Dim conex As Connection Dim rsSenalBD As Recordset Set conex = dtable.dbconex Set rsSenalBD = conex.Execute("SELECT * from tiposdesenal") rsSenalBD.MoveFirst Do While rsSenalBD.EOF = False usrCampos(Index).RellenarLstSenales rsSenalBD!descripcion, rsSenalBD!id rsSenalBD.MoveNext Loop usrCampos(Index).senalID = Value End Sub 'NOMBRE DEL FORMULARIO: frmRptValid 'DESCRIPCION: ' En este formulario se muestran los reportes del proceso de validación OPC que se lleva a cabo en el formulario _ frmVerBD _ 'OBJETOS INCLUIDOS 'TextBox: 'txtrptvalid: _ Es aqui donde se escriben los reportes 'ProgressBar: 'PBvalidacion_srv _ Barra que muestra el avance en cuanto a los servidores validados _ 'PBvalidacion_items _ Barra que muestra el avance en cuanto a los ítems validados por cada servidor 'CommandButtom: 'cmdclose: _ Cierra el formulario 'cmdClear: _ Limpia el campo de texto txtrptvalid 'cmdPortapeles: _ Copia el contenido de txtrptvalid al portapapeles de Windows 'VARIABLES GLOBALES _ Option Explicit Private Sub Form_Resize() 'DESCRIPCION: _ Se encarga de acomodar los elementos del formulario al _ Momento en que es cargado 'PARAMETROS: _ Entrada: _ Salida: _ 'RETORNO: _ 'VARIABLES: _ SEPARACION: _ Constante usada para marcar la separación entre los componentes _ ancho: _ Entero que almacena el ancho de los botones del formulario Const SEPARACION As Integer = 75 Dim ancho As Integer ancho = cmdclose.Width txtrptvalid.Width = Abs(Me.Width - ancho - (SEPARACION * 3)) PBvalidacion_srv.Width = Abs(Me.Width - ancho - SEPARACION) PBvalidacion_items.Width = Abs(Me.Width - ancho - SEPARACION) cmdClear.Left = txtrptvalid.Width + SEPARACION cmdclose.Left = txtrptvalid.Width + SEPARACION CmdPortaPapeles.Left = txtrptvalid.Width + SEPARACION End Sub Private Sub Form_Unload(Cancel As Integer) 'DESCRIPCION: _ Evento que se ejecuta al cerrar el formulario aqui se restablece la presentacion original de los usrcampos del _ formulario frmVerBD 'PARAMETROS: _ Entrada: _ Salida: _ Cancel: _ Parametro predeterminado del evento Unload si es cero, el formulario se descarga, sino _ el formulario sigue abierto luego de ejecutar la rutina 'RETORNO: _ 'VARIABLES: _ Dim i As Integer For i = frmVerBD.usrCampos.LBound To frmVerBD.usrCampos.UBound With frmVerBD.usrCampos(i) .PintarPorDefecto End With Next End Sub Private Sub cmdClear_Click() 'DESCRIPCION: _ Esta subrutina limpia el cuadro de texto txtrptvalid 'PARAMETROS: _ Entrada: _ Salida: _ 'RETORNO: _ 'VARIABLES: _ txtrptvalid.Text = vbNullString End Sub Private Sub cmdclose_Click() 'DESCRIPCION: _ Cierra el formulario 'PARAMETROS: _ Entrada: _ Salida: _ 'RETORNO: _ 'VARIABLES: _ Unload Me End Sub Private Sub CmdPortaPapeles_Click() 'DESCRIPCION: _ Copia el contenido de txtrptvalid al portapapeles 'PARAMETROS: _ Entrada: _ Salida: _ 'RETORNO: _ 'VARIABLES: _ Clipboard.SetText txtrptvalid.Text End Sub 'NOMBRE DEL FORMULARIO: frmAgregarItem 'DESCRIPCION: ' En este objeto se hace un browsing de todos los ítems opc disponibles por cada servidor. Brinda _ la posibilidad de asistir al usuario al momento de agregar nuevos ítems a la base de datos 'OBJETOS INCLUIDOS: 'TreeView: _ trvOPCbrowser: _ Vista de arbol donde se muestra las ramas o directorios devueltos al hacer browsing al servidor opc 'ListBox: _ lstElementos: _ Lista donde se agrupan los elementos previo sean ingresados a la lista en frmVerBD _ lsthojas : _ Lista donde se agrupan las hojas (o items) opc de las ramas (o directorios) seleccionado en trvOPCbrowser 'ComboBox: _ cboServidores_BD: _ Combo donde se selecciona el servidor al cual se le quiere hacer una exploracion (browsing) de _ los items OPC que contenga. 'CommandButtom: _ cmdListaItems: _ Muestra las ramas del servidor seleccionado en la vista de arbol _ cmdRefrescar: _ Refresca la informacion de la vista de arbol _ cmdRefrescar : _ Agrega un elemento seleccionado de lsthojas a _ lstElementos _ cmdremover_elemento: _ Elimina un elemento seleccionado de lstElementos _ cmdAceptar: _ Agrega los items a la lista de campos en el formulario frmVerBD, luego cierra el formulario _ cmdCancelar: _ Cierra el formulario sin realizar ninguna accion 'Label: _ lblopc_addr: _ Etiqueta que muestra la ruta o path del item opc seleccionado. _ lblLabel : _ Etiqueta generica que muestra informacion visual al usuario _ 'ProgressBar: _ PBprogreso: _ Barra que muestra visualmente el avance del proceso de exploracion de los items disponibles en el _ servidor OPC 'VARIABLES GLOBALES: 'MYServer: _ objeto OPCsever 'OPCb: _ Browser OPC, objeto creado con el metodo _ CreateBrowser del OPCServer Option Explicit Option Base 1 Dim WithEvents myserver As OPCServer Dim OPCb As OPCBrowser Private Sub cmdagregar_elemento_Click() 'DESCRIPCION: _ Agrega un elemento de la lista lsthojas a la lista lstElementos (siendo estas las que se pueden agregar al _ servidor 'PARAMETROS: _ Entrada: _ Salida: _ 'RETORNO: _ 'VARIABLES: _ If lsthojas.Text <> vbNullString Then lstElementos.AddItem OPCb.GetItemID(lsthojas.Text) cmdremover_elemento.Enabled = True cmdAceptar.Enabled = True End If End Sub Private Sub cmdAceptar_Click() 'DESCRIPCION: _ Agrega los lstElementos que se encuentran en la lista _ "lstElementos" a los de la forma verBD 'PARAMETROS: _ Entrada: _ Salida: _ 'RETORNO: _ 'VARIABLES: _ Dim i As Integer For i = 0 To lstElementos.ListCount - 1 lstElementos.ListIndex = i frmVerBD.AgregadoAuto cboServidores_BD.ItemData(cboServidores_BD.ListIndex), lstElementos.List(i) Next Unload Me frmVerBD.Show End Sub Private Sub cmdCancelar_Click() 'DESCRIPCION: _ Salgo dea formulario sin efectuar ningun cambio 'PARAMETROS: _ Entrada: _ Salida: _ 'RETORNO: _ 'VARIABLES: _ Me.Hide frmVerBD.Show Unload Me End Sub Private Sub lstElementos_DblClick() 'DESCRIPCION: _ Elimina una entrada en la lista lstElementos 'PARAMETROS: _ Entrada: _ Salida: _ 'RETORNO: _ 'VARIABLES: _ Call cmdremover_elemento_Click End Sub Private Sub cmdListaItems_Click() 'DESCRIPCION: _ Conecta al servidor opc seleccionado de la lista _ lstServidores. Rellena el arbol con las ramas opc del _ servidor selecionado 'PARAMETROS: _ Entrada: _ Salida: _ 'RETORNO: _ 'VARIABLES: _ myServer: _ Servidor OPC _ choice: _ Entero generico para guardar la respuesta de un _ msgBox _ serverinfo: _ Arreglo donde se almacenan tanto el nombre del _ servidor como el nodo donde esta ubicado Set myserver = New OPCServer Dim choice As Integer Dim serverinfo As Variant On Error GoTo errortrap If cboServidores_BD.Text = "" Then MsgBox "Seleccione un servidor de la lista" Exit Sub End If PBprogreso.Value = 0 serverinfo = Split(cboServidores_BD.Text, "@") serverinfo(0) = Trim(serverinfo(0)) serverinfo(1) = Trim(serverinfo(1)) 'Si el servidor no es local se advierte que puede haber _ retardo If serverinfo(1) <> "127.0.0.1" Then choice = MsgBox("Esta operación puede tardar unos minutos dependiendo de su conexión", vbOKCancel, "Conexion a servidor Remoto") If choice = vbCancel Then Exit Sub End If End If Screen.MousePointer = vbHourglass 'Aqui se lleva a cabo la conexcion con el servidor OPC myserver.Connect serverinfo(0), serverinfo(1) PBprogreso.Value = 50 'Cargar el Arbol Call cmdRefrescar_Click Screen.MousePointer = vbDefault trvOPCbrowser.Enabled = True PBprogreso.Value = 100 cmdRefrescar.Enabled = True cboServidores_BD.Enabled = False Exit Sub '************************************************* errortrap: Debug.Print err.Number & " "; Hex(err.Number) Select Case err.Number Case &H80004005 MsgBox "Error de Conexion", vbCritical, "Error" Screen.MousePointer = vbDefault Exit Sub Case Else MsgBox "Error de Conexion", vbCritical, "Error" Screen.MousePointer = vbDefault Exit Sub Resume Next End Select '************************************************* End Sub Private Sub lsthojas_DblClick() 'DESCRIPCION: _ Al hacer doble clik sobre un elemento de la lista "hojas" se agrega directamte a la lista lstElementos _ llamando a subrutina del evento cmdagregar_elemento_Click 'PARAMETROS: _ Entrada: _ Salida: _ 'RETORNO: _ 'VARIABLES: _ Call cmdagregar_elemento_Click End Sub Private Sub MyServer_ServerShutDown(ByVal Reason As String) 'DESCRIPCION: _ Evento que dispara el OPC server al momento en que se desconecta 'PARAMETROS: _ Entrada: _ Salida: _ 'RETORNO: _ 'VARIABLES: _ MsgBox "El servidor se ha desconectado inesperadamente" Unload Me End Sub Private Sub cmdRefrescar_Click() 'DESCRIPCION: _ Esta subrutina es la encargada de crear y recrear el arbol 'PARAMETROS: _ Entrada: _ Salida: _ 'RETORNO: _ 'VARIABLES: _ unbrowser: _ Browser OPC creado a partir del servidor seleccionado en "servidores" _ nodeitem: _ Nodo generico del arbol, puede ser rama u hoja _ Dim unbrowser As OPCBrowser Dim nodeitem As Node On Error GoTo errores 'Creacion del Browser Set unbrowser = myserver.CreateBrowser 'Limpiar arbol (inicializacion) trvOPCbrowser.Nodes.Clear lsthojas.Clear unbrowser.MoveToRoot 'Nodo inicial del servidor Set nodeitem = trvOPCbrowser.Nodes.Add(, tvwFirst, myserver.VendorInfo, myserver.VendorInfo) 'Llamada a la funcion recorre recorre unbrowser, myserver.VendorInfo, "" 'Limpieza de memoria Set unbrowser = Nothing Exit Sub errores: merrores err Resume Next End Sub Private Sub Form_Load() Dim rsServers As Recordset 'DESCRIPCION: _ 'PARAMETROS: _ Entrada: _ Salida: _ 'RETORNO: _ 'VARIABLES: _ 'Estetica Me.Top = 0 Me.Left = 0 'Leo La BD Set rsServers = dtable.dbconex.Execute(SERVERQUERY) 'Lllenar la lista de servidores registrados en la BD If rsServers.RecordCount > 0 Then With rsServers .MoveFirst Do While .EOF = False cboServidores_BD.AddItem .Fields("Servername").Value & " @ " & .Fields("Nodename").Value cboServidores_BD.ItemData(cboServidores_BD.NewIndex) = .Fields("serverid").Value .MoveNext Loop End With Else MsgBox "No hay servidores registrados en la BD" & vbNewLine _ & "Por favor ingrese alguno y reintente luego", vbExclamation + vbOKOnly, _ "No se encontraron servidores" Unload Me Exit Sub End If End Sub Private Sub cmdremover_elemento_Click() 'DESCRIPCION: _ Elimina un elemento de la lista lstElementos 'PARAMETROS: _ Entrada: _ Salida: _ 'RETORNO: _ 'VARIABLES: _ indicelista: _ Entero que guarda la posicion del elemento a eliminar, para luego volver a colocar el cursor en dicha posicion Dim indicelista As Integer indicelista = lstElementos.ListIndex With lstElementos If lstElementos.ListIndex <> -1 Then lstElementos.RemoveItem indicelista If indicelista > 0 Then .ListIndex = indicelista - 1 ElseIf indicelista = 0 Then .ListIndex = -1 End If If lstElementos.ListCount <= 0 Then cmdremover_elemento.Enabled = False cmdAceptar.Enabled = False End If End If End With End Sub Private Sub trvOPCbrowser_Collapse(ByVal Node As MSComctlLib.Node) 'DESCRIPCION: _ Evento de la vista de arbol, Aqui se llama al la subrutina del _ evento trvOPCbrowser_NodeClick(Node) 'PARAMETROS: _ Entrada: _ Node: _ Nodo que disparo el evento _ Salida: _ 'RETORNO: _ 'VARIABLES: _ Call trvOPCbrowser_NodeClick(Node) End Sub Private Sub trvOPCbrowser_Expand(ByVal Node As MSComctlLib.Node) 'DESCRIPCION: _ Evento de la vista de arbol, Aqui se llama al la subrutina del _ evento trvOPCbrowser_NodeClick(Node) 'PARAMETROS: _ Entrada: _ Node: _ Nodo que disparo el evento _ Salida: _ 'RETORNO: _ 'VARIABLES: _ Call trvOPCbrowser_NodeClick(Node) End Sub Private Sub trvOPCbrowser_NodeClick(ByVal Node As MSComctlLib.Node) 'DESCRIPCION: _ Esta sub maneja el momento en que se hace click en algún nodo del arbol, sea servidor o rama. Llenando la lista _ "lsthojas" con todas las hojas que se encuentras en la rama 'PARAMETROS: _ Entrada: _ Entrada: _ Node: _ Nodo que disparo el evento _ Salida: _ 'RETORNO: _ 'VARIABLES: _ num: _ contador generico _ ruta(): _ array donde se almacena _ tmpstring: _ Array donde se almacena el resultado de subdividir la ruta del nodo "node.key" _ => node.key="rama1.rama2.hoja => tmpstring = (rama1, rama2, hoja) _ vname: _ Variant generico para usar los for each Dim Dim Dim Dim num As Integer ruta() As String tmpstring As Variant vname As Variant On Error Resume Next 'Inicialización del OPCbrowser Set OPCb = myserver.CreateBrowser OPCb.MoveToRoot lblopc_addr.Caption = Node.Key 'Actualizo la barra de direcciones lsthojas.Clear tmpstring = Split(Node.Key, ".") ReDim ruta(UBound(tmpstring) + 1) As String For num = LBound(ruta) To UBound(ruta) ruta(num) = tmpstring(num - 1) Next If ruta(UBound(tmpstring) + 1) = "" Then OPCb.MoveToRoot Else OPCb.MoveTo ruta End If OPCb.ShowBranches If OPCb.Count > 0 Then OPCb.ShowLeafs For Each vname In OPCb lsthojas.AddItem vname Next vname Else OPCb.ShowLeafs For Each vname In OPCb lsthojas.AddItem vname Next vname End If 'Determinar si se debe activar o no el botón para _ 'agregar items If lsthojas.ListCount > 0 Then cmdagregar_elemento.Enabled = True Else cmdagregar_elemento.Enabled = False End If End Sub Private Sub recorre(ByVal Mybrowser As OPCBrowser, ByVal Ramal As Variant, ByVal Posicion As Variant) 'DESCRIPCION: _ Esta función se encarga de llenar la vista arbol, es una _ funcion que lleva a cabo su labor de forma recursiva 'PARAMETROS: _ Entrada: _ OPCbrowser: _ Browser OPC de donde se extraeran los _ identificadores de los Items _ Ramal: _ Rama del arbol donde se agregaran los items _ Salida: _ Posicion: _ Guarda la posicion actual dentro del arbol _ 'RETORNO: _ 'VARIABLES: _ postmp: _ Variant donde temporalmente se guarda la posicion actual _ nodeitem: _ Nodo generico para trabajar _ vname: _ Variant generico para los for each Dim vname As Variant Dim postmp As Variant Dim nodeitem As Node On Error GoTo errores Mybrowser.ShowBranches If Mybrowser.Count = 0 Then Mybrowser.MoveUp Else For Each vname In Mybrowser If Posicion = "" Then Posicion = vname postmp = "" Else postmp = Posicion Posicion = Posicion & "." & vname End If Set nodeitem = trvOPCbrowser.Nodes.Add(Ramal, tvwChild, Posicion, vname) Mybrowser.MoveDown (vname) recorre Mybrowser, Posicion, Posicion Posicion = postmp Next vname If (Posicion <> "") Then Mybrowser.MoveUp End If End If Exit Sub errores: merrores err Resume Next End Sub 'NOMBRE DEL FORMULARIO: frmWiosCfg 'DESCRIPCION: ' Este formulario ajusta los parametros de conexion del wios 'OBJETOS INCLUIDOS: _ 'label: _ lblparams(0) y lblparams(1): _ Muestran la informacion que esta almacendada en la BD 'TextBox: _ txtParamsIP()_ _ 4 textboxes donde se solicitan los 4 bytes de la direccion IP _ txtParamsRata: _ Aqui se ingresa la rata de refrescamiento _ 'CommandButtom: _ cmdAplicar: _ Aplica los cambios a la BD _ cmdAceptar: _ Aplica los cambios y cierra el formulario _ cmdCancelar: _ Cierra el formulario_ 'VARIABLES GLOBALES: _ Option Base 1 Option Explicit Private Sub Form_Load() 'DESCRIPCION: _ Evento que se dispara al cargar el formulario, ajusta _ los valores de ancho y alto. Recupera la info de la BD _ para mostrarsela al usuario 'PARAMETROS: _ Entrada: _ Salida: _ 'RETORNO: _ 'VARIABLES: _ conex: _ Conexion a la base de datos _ rsWIOScfg: _ Recordset utilizado para crear el query a la bd _ i: _ Contador Generico _ getips(): _ Arreglo de string utilizado para separar el ip _ proveniente de la BD en los 4 campos txtparamsIP On Error GoTo errores Dim conex As Connection Dim rsWIOScfg As Recordset Dim getips() As String Dim i As Integer Me.Width = 3800 Me.Height = 4000 Set conex = dtable.dbconex If conex.State = adStateClosed Then conex.Open End If Set rsWIOScfg = conex.Execute(CFGQUERY) If rsWIOScfg.RecordCount > 0 Then lblparams(0).Caption = rsWIOScfg!ip getips = Split(rsWIOScfg!ip, ".") For i = txtParamsIP.LBound To txtParamsIP.UBound txtParamsIP(i).Text = getips(i) Next lblparams(1).Caption = rsWIOScfg!refreshrate txtParamsRata.Text = rsWIOScfg!refreshrate Else lblparams(0).Caption = "No hay ninguno establecido" lblparams(1).Caption = "No hay ninguno establecido" End If Exit Sub errores: Select Case err.Number Case (-2147467259) MsgBox "Faltan archivo indispensables para la ejecucion del programa" & vbNewLine _ & err.Description, vbCritical + vbOKOnly, "Error" Case Else MsgBox err.Description, vbCritical + vbOKOnly, "Error" End Select End Sub Private Sub txtParamsIP_Change(Index As Integer) 'DESCRIPCION: _ Llama a la rutina de validacion de txtParamsIP 'PARAMETROS: _ Entrada: _ Salida: _ 'RETORNO: _ 'VARIABLES: _ If Not txtParamsIP(Index).Text = vbNullString Then Call txtParamsIP_Validate(Index, False) End If End Sub Private Sub txtParamsIP_Validate(Index As Integer, KeepFocus As Boolean) 'DESCRIPCION: _ Funcion que verifica que el campo txtParamsIP(INDEX) sea rellenado _ con valores adecuados 'PARAMETROS: _ Entrada: _ Salida: _ KeepFocus: _ Booleano que indica si se debe mantener el foco _ (en caso de error) o no 'RETORNO: _ 'VARIABLES: _ i: _ Contador Generico Dim i As Integer If IsNumeric(txtParamsIP(Index).Text) = False Then MsgBox "Este campo requiere un numero entero", vbCritical + vbOKOnly, "Error" KeepFocus = True ElseIf (txtParamsIP(Index).Text < 0 Or txtParamsIP(Index).Text > 255) Then MsgBox "El rango es entre 0 y 255", vbCritical + vbOKOnly, "Error: Fuera de Rango" KeepFocus = True End If 'Compruebo todos los campos For i = txtParamsIP.LBound To txtParamsIP.UBound If txtParamsIP(i).Text = vbNullString Then cmdAplicar.Enabled = False cmdAceptar.Enabled = False Exit Sub End If Next If Not txtParamsRata.Text = vbNullString Then cmdAplicar.Enabled = True cmdAceptar.Enabled = True Else cmdAplicar.Enabled = False cmdAceptar.Enabled = False End If End Sub Private Sub txtParamsRata_Change() 'DESCRIPCION: _ Llama a la rutina de validacion de txtParamsRata 'PARAMETROS: _ Entrada: _ Salida: _ 'RETORNO: _ 'VARIABLES: _ If Not txtParamsRata.Text = vbNullString Then Call txtParamsRata_Validate(False) End If End Sub Private Sub txtParamsRata_Validate(KeepFocus As Boolean) 'DESCRIPCION: _ Funcion que verifica que el campo txtParamsRata sea rellenado _ con valores adecuados 'PARAMETROS: _ Entrada: _ Salida: _ KeepFocus: _ Booleano que indica si se debe mantener el foco _ (en caso de error) o no 'RETORNO: _ 'VARIABLES: _ i: _ Contador Generico Dim i As Integer If IsNumeric(CVar(txtParamsRata.Text)) = False Then MsgBox "La rata debe se un numero entero de milisegundos", vbCritical + vbOKOnly, "Error" KeepFocus = True End If 'Compruebo todos los campos For i = txtParamsIP.LBound To txtParamsIP.UBound If txtParamsIP(i).Text = vbNullString Then cmdAplicar.Enabled = False cmdAceptar.Enabled = False Exit Sub End If Next If Not txtParamsRata.Text = vbNullString Then cmdAplicar.Enabled = True cmdAceptar.Enabled = True Else cmdAplicar.Enabled = False cmdAceptar.Enabled = False End If End Sub Private Sub cmdAplicar_Click() 'DESCRIPCION: _ Aplica los cambios que se desea realizar en la bd sin cerrar el formulario 'PARAMETROS: _ Entrada: _ Salida: _ 'RETORNO: _ 'VARIABLES: _ conex: _ Conexion a la base de datos _ query: _ String utilizado para crear el query a la bd _ i: _ Contador Generico Dim conex As Connection Dim query As String Dim i As Integer Set conex = dtable.dbconex If conex.State = adStateClosed Then conex.Open End If query = vbNullString For i = txtParamsIP.LBound To txtParamsIP.UBound query = query & "." & txtParamsIP(i).Text Next query = Right(query, Len(query) - 1) query = "UPDATE wios SET ip = '" & query _ & "', refreshrate = '" & txtParamsRata.Text & "'" conex.Execute (query) cmdAplicar.Enabled = False cmdCancelar.Caption = "Cerrar" End Sub Private Sub cmdAceptar_Click() 'DESCRIPCION: _ Aplica los cambios que se desea realizar en la bd y cierra el formulario 'PARAMETROS: _ Entrada: _ Salida: _ 'RETORNO: _ 'VARIABLES: _ Call cmdAplicar_Click Unload Me End Sub Private Sub cmdCancelar_Click() 'DESCRIPCION: _ Cierra el formulario 'PARAMETROS: _ Entrada: _ Salida: _ 'RETORNO: _ 'VARIABLES: _ Unload Me End Sub MODULOS 'Nombre del Modulo: funciones 'Archivo: funciones.bas 'Manejo de Errores Public Sub merrores(err As ErrObject) 'Dim error As Long Dim getopc As OPCServer Set getopc = New OPCServer Dim dumm As Variant Select Case err.Number Case OPCBadRights, OPCBadType, OPCClamp, OPCDuplicateName, OPCInuse, _ OPCInvalidConfig, OPCInvalidFilter, OPCInvalidHandle, _ OPCInvalidItemID, OPCInvalidPID, OPCNotFound, OPCPublic, _ OPCRange, OPCUnknownItemID, OPCUnknownPath, OPCUnsupportedRate MsgBox "Es un error OPC" MsgBox "Se ha producido un error OPC", vbCritical, "IOS ERROR" MsgBox "Error #0x" & Hex(err.Number) & ", Error #Dx" & err.Number & vbNewLine & err.Description ' Case OPCUnsupportedRate Case -2147024894 MsgBox "Error de Conexion", vbCritical, "Error" 'merrores = errConex Exit Sub Case &H80040200 'No se ha configurado la Base de Datos 'dtable.dbconex.Open MsgBox "Error en la BD", vbCritical, "Error" End 'Resume Next Case Else MsgBox "Se ha producido un Error Desconocido", vbCritical, "IOS ERROR" MsgBox "Error #0x" & Hex(err.Number) & ", Error #Dx" & err.Number & vbNewLine & err.Description End Select err.Clear End Sub 'Nombre del Modulo: OpcM 'Nombre archivo: OpcM.bas 'Descripcion: Aqui se incluyen ciertas definiciones OPC Public Public Public Public gruposopc As OPCGroups grupoopc As OPCGroup itemsOPC As OPCItems itemopc As OPCItem Public Enum miserores errConex = 2 End Enum Public Enum Codigos_Validacion IOS_OPC_OK = 1 IOS_OPC_FALLO_CONEXION IOS_OPC_SERVIDOR_NO_ENCONTADO IOS_OPC_FALLO_ITEM End Enum Public Const debugging As Integer = 1 'Public Const LIMITE = 10 Public Const SERVERQUERY As String = "SELECT * FROM servidores ORDER BY serverid" Public Const CFGQUERY As String = "SELECT * FROM wios" Public Enum tipos_OPC VT_EMPTY = 0 VT_NULL VT_I2 VT_I4 VT_R4 VT_R8 VT_CY VT_DATE VT_BSTR VT_DISPATCH VT_ERROR VT_BOOL VT_VARIANT VT_UNKNOWN VT_DECIMAL End Enum Controles Diseñados Nombre del Control: UC Nombre del Archivo: usrCampos.ctl Option Base 1 Option Explicit 'Constantes de diseño '####################################### Const ancho = 1100 Const anchokey = (2 * ancho) - 180 Const anchoventana = 7 * ancho Const anchorw = 300 Const alto = 315 Const tope1 = 315 Const anchoDT = 1150 Public Width Private uc_DataTypeInt As Integer Private uc_Nodename As String '####################################### Event ServerChange() Private Sub uc_server_Change() RaiseEvent ServerChange End Sub Private Sub UserControl_Resize() Dim i As Integer Height = alto uc_Checks.Move 1, 1, anchorw * 0.75, alto uc_ids.Move uc_Checks.Left + uc_Checks.Width, 0, anchorw, alto uc_server.Move uc_ids.Left + uc_ids.Width, 0, anchokey + ancho 'uc_nodo.Move uc_server.Left + uc_server.Width, 0, ancho, alto 'uc_opcrw.Move uc_nodo.Left + uc_nodo.Width, 0, anchorw * 2 uc_opcrw.Move uc_server.Left + uc_server.Width, 0, anchorw * 2 uc_nombre.Move uc_opcrw.Left + uc_opcrw.Width, 0, anchokey, alto uc_DataType.Move uc_nombre.Left + uc_nombre.Width, 0, ancho * (2 / 3), alto uc_scada(uc_scada.LBound).Move uc_DataType.Left + uc_DataType.Width, 0, anchoDT, alto For i = uc_scada.LBound + 1 To uc_scada.UBound With uc_scada(i) .Move uc_scada(i - 1).Left + uc_scada(i - 1).Width, 0, anchoDT, alto End With Next uc_rw.Move uc_scada(uc_scada.UBound).Left + uc_scada(uc_scada.UBound).Width, 0, anchorw * 2 uc_TipoSenal.Move uc_rw.Left + uc_rw.Width, 0, ancho 'Width = 1 Width = uc_Checks.Width + uc_ids.Width + uc_server.Width _ + uc_opcrw.Width + uc_nombre.Width _ + uc_rw.Width + ((uc_scada.Count) * uc_scada(uc_scada.LBound).Width) _ + uc_TipoSenal.Width + uc_DataType.Width Debug.Print Width & "control" End Sub Private Sub UserControl_Initialize() 'Rellenar los rw acceso SCADA With uc_rw .Clear .AddItem "?", 0 .AddItem "r", 1 .AddItem "w", 2 .AddItem "r/w", 3 End With With uc_opcrw .Clear .AddItem "?", 0 .AddItem "r", 1 .AddItem "w", 2 .AddItem "r/w", 3 End With uc_Checks.Value = vbUnchecked Width = 0 End Sub Public Property Let nombre(ByVal opckey As String) uc_nombre.Text = opckey End Property Public Property Get nombre() As String nombre = uc_nombre.Text End Property Public Property Let scadaname(ByVal scadaname As String) Dim names() As String Dim i As Integer names = Split(scadaname, "_") For i = uc_scada.LBound To uc_scada.UBound If i <= UBound(names) + 1 Then uc_scada(i).Text = names(i - 1) Else uc_scada(i).Text = vbNullString End If Next End Property Public Property Get scadaname() As String Dim names(5) As String Dim i As Integer For i = LBound(names) To UBound(names) If i <= 5 Then names(i) = uc_scada(i).Text End If Next scadaname = Join(names, "_") End Property Public Property Let DataTypeInt(ByVal datatype As Integer) 'Public Enum tipos_OPC 'VT_EMPTY = 0, ' VT_NULL = 1, ' VT_I2 = 2, ' VT_I4 = 3, ' VT_R4 = 4, ' VT_R8 = 5, ' VT_CY = 6, ' VT_DATE = 7, ' VT_BSTR = 8, ' VT_DISPATCH = 9, ' VT_ERROR = 10, 'End Enum uc_DataTypeInt = datatype Select Case datatype Case VT_EMPTY uc_DataType.Text = "Nothing" Case VT_I2 uc_DataType.Text = "Integer" Case VT_I4 uc_DataType.Text Case VT_R4 uc_DataType.Text Case VT_R8 uc_DataType.Text Case VT_BOOL uc_DataType.Text Case VT_BSTR uc_DataType.Text Case VT_DATE uc_DataType.Text Case VT_CY uc_DataType.Text Case Else uc_DataType.Text End Select End Property = "Long" = "Single" = "Double" = "Boolean" = "String" = "Date" = "Currency" = "No implementado" Public Property Get datatype() As String datatype = uc_DataType.Text End Property Public Property Get DataTypeInt() As Integer DataTypeInt = uc_DataTypeInt End Property Public Property Let nodo(ByVal nodo As String) uc_Nodename = nodo End Property Public Property Get nodo() As String nodo = uc_Nodename End Property Public Property Let ids(ByVal ids As String) uc_ids.Text = ids End Property Public Property Get ids() As String ids = uc_ids.Text End Property Public Property Let Checks(ByVal Checks As String) uc_Checks.Value = Checks End Property Public Property Get Checks() As String Checks = uc_Checks.Value End Property Public Property Let rw(ByVal rw As String) Select Case rw Case OPCReadable, OPCWritable, OPCReadable + OPCWritable uc_rw.ListIndex = rw Case Else uc_rw.ListIndex = 0 End Select End Property Public Property Get rw() As String rw = uc_rw.ListIndex End Property Public Property Let opcrw(ByVal opcrw As String) Select Case opcrw Case OPCReadable, OPCWritable, OPCReadable + OPCWritable uc_opcrw.ListIndex = opcrw Case Else uc_opcrw.ListIndex = 0 End Select End Property Public Property Get opcrw() As String opcrw = uc_opcrw.ListIndex End Property Public Property Let server(ByVal server As Integer) uc_server.ListIndex = server End Property Public Property Get server() As Integer server = uc_server.ListIndex End Property Public Property Get serverID() As Integer serverID = uc_server.ItemData(uc_server.ListIndex) End Property Public Property Get ServerNewIndex() As String ServerNewIndex = uc_server.NewIndex End Property Public Property Get senalID() As Integer If uc_TipoSenal.ListIndex <> -1 Then senalID = uc_TipoSenal.ItemData(uc_TipoSenal.ListIndex) Else senalID = -1 End If End Property Public Property Let senalID(ByVal senalID As Integer) Dim i As Integer With uc_TipoSenal For i = 0 To .ListCount - 1 If .ItemData(i) = senalID Then .ListIndex = i Exit Property End If Next End With End Property Public Property Get Server_Left() As Integer Server_Left = uc_server.Left End Property Public Property Get B1_Left() As Integer B1_Left = uc_scada(1).Left End Property Public Property Get B2_Left() As Integer B2_Left = uc_scada(2).Left End Property Public Property Get B3_Left() As Integer B3_Left = uc_scada(3).Left End Property Public Property Get Elem_Left() As Integer Elem_Left = uc_scada(4).Left End Property Public Property Get Info_Left() As Integer Info_Left = uc_scada(5).Left End Property Public Property Get Id_Left() As Integer Id_Left = uc_ids.Left End Property Public Property Get opckey_Left() As Integer opckey_Left = uc_nombre.Left End Property Public Property Get datatype_Left() As Integer datatype_Left = uc_DataType.Left End Property Public Property Get TipoSenal_Left() As Integer TipoSenal_Left = uc_TipoSenal.Left End Property '-----------------------------------------------'SUBS Public Sub RellenarLstServers(ByVal ServerName As String, ByVal Nodename As String, ByVal serverID As Integer) With uc_server .AddItem ServerName & " @ " & Nodename .ItemData(.NewIndex) = serverID End With End Sub Public Sub VldCode(ByVal Codigo_Validacion) Dim ctl As Control 'IOS_OPC_OK = 1 ' IOS_OPC_FALLO_CONEXION ' IOS_OPC_SERVIDOR_NO_ENCONTADO ' IOS_OPC_FALLO_ITEM Select Case Codigo_Validacion Case IOS_OPC_OK For Each ctl In Controls ctl.BackColor = vbGreen Next Case IOS_OPC_FALLO_ITEM For Each ctl In Controls ctl.BackColor = vbRed Next uc_server.BackColor = vbGreen 'uc_nodo.BackColor = vbGreen uc_ids.BackColor = vbGreen uc_Checks.BackColor = vbGreen Case IOS_OPC_FALLO_CONEXION For Each ctl In Controls ctl.BackColor = vbYellow Next uc_server.BackColor = vbRed 'uc_nodo.BackColor = vbRed uc_ids.BackColor = vbRed uc_Checks.BackColor = vbRed Case IOS_OPC_SERVIDOR_NO_ENCONTADO For Each ctl In Controls ctl.BackColor = vbYellow Next Case Else For Each ctl In Controls ctl.BackColor = vbYellow Next End Select End Sub Public Sub RellenarLstSenales(ByVal SenalName As String, ByVal senalID As Integer) With uc_TipoSenal .AddItem SenalName .ItemData(.NewIndex) = senalID End With End Sub Public Sub PintarPorDefecto() Dim i As Integer uc_Checks.BackColor = &H8000000F 'azul uc_DataType.BackColor = &HFFFFC0 uc_ids.BackColor = &HFFFFC0 uc_opcrw.BackColor = &HFFFFC0 uc_nombre.BackColor = &HFFFFC0 uc_server.BackColor = &HFFFFC0 'verde uc_rw.BackColor = &HC0FFC0 uc_TipoSenal.BackColor = &HC0FFC0 For i = uc_scada.LBound To uc_scada.UBound uc_scada(i).BackColor = &HC0FFC0 Next End Sub