Estudio de aplicabilidad de los protocolos de última generación en

Anuncio
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
Descargar