Vous êtes sur la page 1sur 70

Ejemplos con: Patrones de diseo por capas Manejo de puerto serial Control de puerto paralelo Comunicacin con PIC

s por RS232 Comunicacin con PLC s


Autor: Aaron Castro Bazua

Manual 3ra parte En esta parte del curso nos enfocamos al control de perifricos, la comunicacin con otros dispositivos y la organizacin de un proyecto por capas. Temario

Prctica: Chat por puerto serie entre 2 computadoras................................135 Prctica: Comunicacin con Microcontrolador PIC 16F887 va RS232 ......138 Programa para PIC: Comunicacin serie bidireccional con C#.NET...........139 Prctica: Control de puerto paralelo...........................................................144 Patrones de diseo por capas: Datos, Negocio y Presentacin..................149 Monitoreo de PLC's por medio de aplicaciones en C#.NET.........................166 Configurando un PLC KOYO DL06 de Automation Direct...........................168 Configurando un PLC Siemens S7-300 .....................................................174 Aplicacin Cliente para monitorear PLCs desde C#.NET...........................183 Prctica: Monitoreo y Control con Smartphone...........................................190

Prctica: Chat por puerto serie entre 2 computadoras


En este ejercicio vamos a crear un programa de Chat por RS232, para que funcione se ocupan dos computadoras con conector DB9 y el mismo programa, los cables se van a cruzar en los pines RX y TX. Tambin podemos probarlo cruzando los pines 2 y 3 de nuestro cable DB9 y observaremos como llega el mismo dato que enviamos. Es bsico contar con un puerto COM habilitado en el administrador de dispositivos de la PC, los convertidores USB-Serial son vlidos.

Conector DB9 PC1

Conector DB9 PC2

5 9 4 8 3 7 2 6 1

5 9 4 8 3 7 2 6 1

En nuestro caso utilizamos el COM3. Ahora creamos un nuevo proyecto de windows forms donde arrastramos los siguientes controles:

TextBox Name: txtConversacion ListBox Name: ListBoxChat

Formulario Name: frmChat

TextBox Name: txtMensaje Label Name: lblNick SerialPort Name: serialPortChat

Button Name: btnEnviar Enabled: False CheckBox Name: chkPuertoSerie TextBox Name: txtNick

135

El puerto serial lo configuramos con los siguientes datos: En nuestro cdigo declaramos un objeto string con mbito para toda la clase.
using using using using using using using using System; System.Collections.Generic; System.ComponentModel; System.Data; System.Drawing; System.Linq; System.Text; System.Windows.Forms;

namespace ProyectoChatRS232 { public partial class frmChat : Form { string datoSerial; public frmChat() { InitializeComponent(); }

Ahora en el evento DataReceived de nuestro puerto serie:


private void serialPortChat_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e) { try //intentamos leer el puerto { serialPortChat.ReadTimeout = 2000;//espera 2 segundos por el dato //almacena toda la cadena de datos hasta que lee "-FIN-" datoSerial = serialPortChat.ReadTo("-FIN-"); //ahora recorta los datos e invoca al manejador this.Invoke(new EventHandler(TextoRecibido)); } catch //no llego la cadena completa, interceptamos el error { MessageBox.Show("Error, no recib el identificador final de la cadena -FIN-"); serialPortChat.DiscardInBuffer(); //borra los datos del buffer de entrada } } }

Creamos una funcin para manejar el evento TextoRecibido: (se debe teclear manualmente)
private void TextoRecibido(object sender, EventArgs e) { txtConversacion.Text = ""; txtConversacion.AppendText(datoSerial); listBoxChat.Items.Add(datoSerial); }

136

En el evento Click del btnEnviar


private void btnEnviar_Click(object sender, EventArgs e) { string mensaje = txtNick.Text + " dice: " + txtMensaje.Text; serialPortChat.Write(mensaje + "-FIN-"); listBoxChat.Items.Add(mensaje.ToString()); }

Finalmente en el CheckBox que habilita el puerto:


private void chkPuertoSerie_CheckedChanged(object sender, EventArgs e) {if (chkPuertoSerie.Checked) { try { if (!serialPortChat.IsOpen) //si el puerto esta cerrado { serialPortChat.Open(); } //habilita el botn solo si esta abierto el puerto btnEnviar.Enabled = true; chkPuertoSerie.Text = "Puerto Abierto"; chkPuertoSerie.BackColor = Color.YellowGreen; } catch { MessageBox.Show("Algo anda mal, no pude abrir el puerto COM"); } } else { try { if (serialPortChat.IsOpen) { serialPortChat.Close(); chkPuertoSerie.BackColor = Color.Red; btnEnviar.Enabled = false; } } catch {MessageBox.Show("Algo anda mal no pude cerrar el COM"); } } }

Ahora podemos probar la aplicacin: Si no contamos con otra PC podemos unir los pines 2 y 3 del DB9 y ver como nos llega el dato que enviamos.

137

Prctica: Comunicacin con Microcontrolador PIC 16f887 va RS232


En este ejercicio vamos a combinar hardware externo con C#, con la ayuda del PIC vamos a disponer de 8 bits digitales de entrada, 1 canal anlogo (0-5 volts) y 8 bits de salida TTL. La manera de operar ser mandando mensajes RS232 que soliciten informacin al PIC o le ordenen escribir datos, para este circuito ocupamos el siguiente material: El diagrama es el siguiente:
5 Volts

11 32 31 12

VDD VDD VSS VSS

16F887
No ocupa oscilador!
Leds
Resistencias 330 ohm

RB0/AN12/INT RB1/AN10/C12IN3RB2/AN8 RB3/AN9/PGM/C12IN2RB4/AN11 RB5/AN13/T1G RB6/ICSPCLK RB7/ICSPDAT

33 34 35 36 37 38 39 40

DISPLAY LCD

5 Volts

8 9 10 1

15 16 17 18 23 24 25 26

+ -

Resistencias 1k ohm

Entrada analgica con potencimetro de 10k ohm

5 9 4 8 3 7 2 6 1

Microswitch

Conector DB9 hembra

Material: 1 pic 16F887, 1 conector DB9 hembra,1 microswitch (8 polos), 1 potencimetro de 10 k ohm, 8 resistencias 1 kOhm, 8 resistencias 330 Ohm, 8 leds, 2 protoboard, 1 max232, 4 capacitores 1uf 63volts, 1 metro de cable de red, 1 display lcd 16x2. Equipo necesario: 1 Programador de Pics (40 pines), 1 Fuente de voltaje (5 volts), Pinzas, Multmetro.

MAX232

2 3 4 5 6 7 14 13

RA0/AN0/ULPWU/C12IN0RA1/AN1/C12IN1RA2/AN2/VREF-/CVREF/C2IN+ RA3/AN3/VREF+/C1IN+ RA4/T0CKI/C1OUT RA5/AN4/SS/C2OUT RA6/OSC2/CLKOUT RA7/OSC1/CLKIN RC0/T1OSO/T1CKI RC1/T1OSI/CCP2 RE0/AN5/RD RC2/P1A/CCP1 RE1/AN6/WR RC3/SCK/SCL RE2/AN7/CS RC4/SDI/SDA RE3/MCLR/Vpp/ RC5/SDO RC6/TX/CK RC7/RX/DT

RD0 RD1 RD2 RD3 RD4 RD5/P1B RD6/P1C RD7/P1D

19 20 21 22 27 28 29 30

ENABLE RS RW D4 D5 D6 D7

Capacitores 1 ufaradio

+ 1 2 3 4 5 6 7 8 16 15 14 13 12 11 10 9 Vcc Gnd T1OUT R1IN R1OUT T1IN T2IN R2OUT

+ + -

C1+ V+ C1C2+ C2VT2OUT R2IN

138

Programa para PIC: Comunicacin serie bidireccional con C#.NET


Con este ejercicio el PIC enva el bit RE0/AN5 y un byte proveniente del microswitch a la aplicacin de C#, tambin recibe un byte para escribirlo en el puerto A, se utiliza el LCD para mostrar mensajes sobre lo que esta haciendo el PIC. Requerimientos: El lcd debe estar conectado para que funcione La aplicacin en C#.NET que mande los datos debe agregar un Enter al final de la cadena.

139

140

Puede bajar los archivos fuente de este proyecto desde: http://www.multitecnologia.com/ArchivosFuentePIC16f887CursoC.rar Si no cuenta con CCS para compilar el programa puede grabar el hexadecimal directamente con MPLAB. Como puede observar el PIC esta siempre esperando tres mensajes y responde a esos comandos: "escribe : Cuando el PIC recibe esta cadena se queda esperando un nmero entre 0255 con el que imprime los datos en el puerto B del PIC. Ra0" : Con este mensaje nos responde con un valor con la conversin a decimal del puerto analogico (0-5 volts), la resolucin es de 8 bits. "byteEnt" : Esta cadena le dice al PIC que nos responda con los 8 bits de entrada. Observe que todos los mensajes PIC PC tienen un final con la cadena: +Fin, esto permite hacer mas eficiente la lectura en C# y conocer el final de la trama de datos: printf("+%u+Fin", valorAnalogo); printf("Se escribio en el puerto B +Fin"); Ahora con su circuito armado y el PIC programado abrimos un nuevo proyecto de windows forms. Arrastramos los siguientes controles:

TextBox Name: txtRecibeDatos Button Name: btnRecibeAn0 Enabled: False

Formulario Name: frmTarjeta

Label Name: lblCanalAn0 Text: RA0

Button Name: btnRecibeDatDig Enabled: False

ListBox Name: listBoxDatosEntrada NumericUpDown Name: numStepByte Minimum: 0 Maximum: 255 CheckBox Name: chkPuertoSerie

Button Name: btnMandaByte Enabled: False

SerialPort Name: serialPort

141

El puerto serial debe estar con la misma configuracin del PIC: #use rs232(baud=9600,parity=N,xmit=PIN_C6,rcv=PIN_C7,bits=8)

Este ejercicio es similar al Chat, tambin creamos un objeto string visible para toda la clase:
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; namespace ProyectoPIC16f883 { public partial class frmTarjeta : Form { string datoSerial; public frmTarjeta() { InitializeComponent(); }

En el CheckBox escribimos:
private void chkPuertoSerie_CheckedChanged(object sender, EventArgs e) { if (chkPuertoSerie.Checked) {try {if (!serialPort.IsOpen) //si el puerto esta cerrado { serialPort.Open(); } btnMandaByte.Enabled = true; btnRecibeAn0.Enabled = true; btnRecibeDatDig.Enabled = true; chkPuertoSerie.Text = "Puerto Abierto"; chkPuertoSerie.BackColor = Color.YellowGreen; } catch {MessageBox.Show("Algo anda mal con el puerto COM"); } } else {try {if (serialPort.IsOpen) { serialPort.Close(); chkPuertoSerie.BackColor = Color.Red; chkPuertoSerie.Text = "Puerto Cerrado"; btnMandaByte.Enabled = false; btnRecibeAn0.Enabled = false; btnRecibeDatDig.Enabled = false; } } catch { MessageBox.Show("Algo anda mal no pude cerrar el COM"); } }

142

En el evento DataReceived del Puerto serie:


private void serialPort_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e) { try //intentamos leer el puerto { serialPort.ReadTimeout = 2000;//espera 2 segundos por el dato //almacena toda la cadena de datos hasta que lee "Fin" datoSerial = serialPort.ReadTo("Fin"); //ahora recorta los datos e invoca al manejador this.Invoke(new EventHandler(TextoRecibido)); } catch //no llego la cadena completa, interceptamos el error { MessageBox.Show("Error, no recib el identificador final de la cadena: Fin"); serialPort.DiscardInBuffer(); //borra los datos del buffer de entrada } }

De nuevo creamos una funcin que refleje los datos recibidos:


private void TextoRecibido(object sender, EventArgs e) { // en esta funcin recibimos los datos para su anlisis y reordenamiento. txtRecibeDatos.Text = ""; txtRecibeDatos.AppendText(datoSerial); //ahora llamamos a la funcin que extrae los datos // MessageBox.Show(datoSerial); analisisCadenas(datoSerial); }

Ahora otra nueva funcin para separar los datos:


private void analisisCadenas(string cadena) { string cad; //cuando el PIC nos manda el dato anlogo del Ra0 // le quitamos lo que no sirve if (cadena.Contains("Ra0+")) {//El dato siempre llega de esta forma: Ra0+ numero +, // solo nos quedamos con: numero cad = cadena.Replace("Ra0+", ""); cad = cad.Replace("+", ""); lblCanalAn0.Text = "Lectura RA0: " + cad; } //cuando nos llega la trama con los bits de entrada if (cadena.Contains("byteEnt")) {//limpiamos todos los valores antes de actualizar listBoxDatosEntrada.Items.Clear(); cad = cadena.Replace("byteEnt", ""); //con el Split cortamos cada parte de la cadena donde exista un '+' //con cada dato se forma un arreglo de cadenas string[] arreglocadena = cad.Split('+'); foreach (string st in arreglocadena) { //ahora cada miembro del arreglo entra a los items del listbox listBoxDatosEntrada.Items.Add(st.ToString()); } } }

143

Para cada uno de los botones en su evento Click escribimos:


private void btnMandaByte_Click(object sender, EventArgs e) { // primero se manda la cadena "escribe" con el Enter para // avisar que viene un numero despus, (char)13= Enter serialPort.Write("escribe" + (char)13); // ahora enva el numero entre 0 y 255 del numStepByte serialPort.Write(numStepByte.Value.ToString() + (char)13); } private void btnRecibeDatDig_Click(object sender, EventArgs e) { // Al mandar "byteEnt" al PIC nos regresa los 8 bits de entrada // el orden viene de esta manera: // b0=RA7, b1=RA6, b2=RC0,b3=RC1,b4=RC2,b5=RC3,b6=RC4,b7=RC5 serialPort.Write("byteEnt" + (char)13); //(char)13= Enter } private void btnRecibeAn0_Click(object sender, EventArgs e) { //al mandar "Ra0" el PIC nos responde con el valor decimal de //la conversin del canal anlogo a 8 bits serialPort.Write("Ra0" + (char)13);// (char)13= Enter }

Ahora podemos probar la comunicacin con el PIC, cuide no conectar la tierra del DB9 a un voltaje externo.

Al ejecutar puede comprobar como el Pic digitaliza el dato anlogo y los 8 bits de entrada al pulsar los botones en C#, tambin escribe en el puerto B del pic el valor del Numeric Up Down.

Prctica: Control de Puerto Paralelo


En este ejercicio vamos construir la interfaz electrnica para controlar 8 bits de salida y leer 4 entrada por medio del puerto DB25. Vamos a implementar el siguiente circuito con el Buffer 74L541 para alimentar los leds y reducir la carga de corriente en el puerto, el material necesario es: 1 Protoboard 1 Buffer 74LS541 1 Microswitch 4 resistencias de 1 kohm 8 resistencias de 330 ohm 8 leds comunes de 5mm Cableado Equipo necesario: 1 fuente de voltaje de 5 volts DC Pinzas, Multmetro.

144

IMPORTANTE Trabajar con fuentes externas de voltaje y perifricos de la PC presenta un riesgo de quemar componentes de la tarjeta madre, se debe poner en comn la tierra de la PC con la fuente externa y tener mucho cuidado en que no entre voltaje por ese punto a la computadora, esto provocara un dao irreparable. Como consideracin inicial debemos respetar los colores en las lneas del protoboard: Azul = Tierra Rojo = Voltaje Ahora implementamos el circuito:

Puerto Paralelo - Conector DB25


Registro de Estatus Registro de Datos
S0 S1 S2 S3 S4 S5 S6 S7 D7 D6 D5 D4 D3 D2 D1 D0

{
7

13 12

11 10

25 24 23 22 21 20 19 18 17 16 15 14

Para los bits S0,S1,S2, no tenemos acceso, dependiendo la tarjeta madre estos valores pueden ser: 000 111

C0 C1 C2 C3 C4 C5 C6 C7

Registro de Control
El circuito lucir de esta manera

Diagrama del circuito


Vcc
1

OE

14 15 16 17 18 19 20 21 22 23 24 25
Resistencias 1 kohm

2 3 4 5 6 7 8 9 10 11 12 13

1 2 3 4 5 6 7 8 9 10

20 19 18 17 16 15 14 13 12 11

OE

Leds

74LS541

En nuestro conector tenemos el registro de datos y estatus, falta el registro de control.

S3

S4 S5 S6 S7

D0
Resistencias 330 ohm

D7

Tierra

Importante El Bit S7 esta negado internamente, el dato que ingresemos ser invertido al entrar al registro.

+
5 Volts

Fuente

Microswitch

- +

EL S3 lo conectamos 15 directo a tierra. Solo utilizamos un nible

145

El cable DB25 entra al circuito de esta manera: El canal del protoboard separa cada conector de cable plano, por un lado tenemos el bus de datos con la tierra y por el otro el registro de estatus .

Recuerde respetar los colores del protoboard: Lnea azul: Tierra Lnea roja: Voltaje

Buffer
Tierra D7
D0

Antes de conectar el protoboard a la PC probamos los las entradas del buffer, observe que por defecto sus salidas estn activadas, por lo tanto esta esperando una tierra en su entrada para apagar el led, tomamos cualquier cable largo y lo conectamos a tierra, el otro extremo lo posicionamos en cada entrada del buffer para comprobar como se apaga cada led. Una vez probado el protoboard verifique que no existan cables sueltos que puedan provocar cortos. Tambin use su multmetro para probar continuidad entre el cable DB25 y las entradas del buffer. Antes de prender la fuente que alimenta el protoboard observe el monitor de la PC, si nota un parpadeo al encenderla apague rpidamente la fuente o desconecte el cable, esto puede salvar su tarjeta madre, si no hay cambio todo esta bien conectado, en caso de apagarse el monitor y la PC lamento decirle que ha daado su tarjeta madre. Cuando las tarjetas se daan se debe a un choque de niveles de voltaje interno, en estos casos se queman integrados bsicos que pueden ser mas caros de reparar que comprar una tarjeta nueva. Considerando que su hardware externo esta listo vamos con la interfaz de la PC. El puerto paralelo requiere una librera para funcionar: inpout32.dll La descargamos desde: www.multitecnologia.com/inpout32.rar Ahora colocamos este archivo en la carpeta C:\Windows\System32\inpout32.dll Posiblemente se ocupe registrar en la ventana de ejecutar: Escribir: REGSVR32 c:\windows\system32\inpout32.dll Con nuestra interfaz lista nos vamos a VisualStudio y creamos un nuevo proyecto de windows forms.

146

Ahora arrastramos los siguientes controles:


Formulario Name: frmPuertoParalelo Label Name: lblLecturaPuerto Text: Lectura: Button Name: btnLeerPuerto HSCrollBar Name: SBDato Minimum:0, Maximum: 255 Label Name: lblDatos Text: Escritura:

Escribimos el cdigo para los eventos de cada control mostrados:


using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; using System.Runtime.InteropServices; //esta directiva es necesaria namespace frmPuertoParalelo { public partial class frmPuertoParalelo : Form { //Creamos esta clase para importar la DLL y tener acceso a sus comandos public class PortAccess { [DllImport("inpout32.dll", EntryPoint = "Out32")] public static extern void Output(int adress, int value); [DllImport("inpout32.dll", EntryPoint = "Inp32")] public static extern int Input(int address); } public frmPuertoParalelo() { InitializeComponent(); } private void SBDato_Scroll(object sender, ScrollEventArgs e) { //escribimos un dato de 8 bits (0-255) en el registro de datos //observe que el valor se manda en decimal (int) //el valor 888 es la direccin del registro de datos PortAccess.Output(888, SBDato.Value); lblDatos.Text = "Escritura: " + SBDato.Value.ToString(); } private void btnLeerPuerto_Click(object sender, EventArgs e) { //leemos el registro de status con los bits S4, S5, S6, S7 del diagrama, //el bit S3 se encuentra forzado a tierra sin el microswitch. //los bits S0,S1,S2 no estan disponibles y por lo regular su valor es 0 //el valor 889 es la direccin del registro de Status lblLecturaPuerto.Text = "Lectura: " + PortAccess.Input(889).ToString(); } } }

Finalmente ejecutamos y al mover el scrollbar los leds deben cambiar su estado, para leer diferentes datos movemos los interruptores del microswitch y damos click en el btnLeerPuerto para actualizar. 147

ANEXOS
Cdigo de colores para resistencias

4N32

148

Prctica: Patrones de diseo por capas:

Datos, Negocio y Presentacin.


En este ejercicio vamos a retomar los objetos para toma de muestras para respaldarlos en tres tipos de archivos: texto, xml y base de datos. La parte importante de este proyecto es comprender el diseo de software mediante las capas de Datos, Negocio y Presentacin. Qu son? Para que sirven? Estos patrones de diseo son metodologas de organizacin que nos permiten sacar mejor provecho a los objetos, segmentar las tareas e implementar de manera eficiente. La idea comenz desde los 60's, uno de los pioneros en el concepto fue Edsger Dijkstra, sin embargo tomo auge hasta los 90's con los primeros lenguajes populares orientados a objetos como Java y Delphi. Esta metodologa tambin trata de solucionar La crisis del software que se hizo presento en una conferencia de la OTAN en 1968, posteriormente comenzaron las doctrinas para la ingeniera de software. La definicin tcnica de las capas es: Capa de presentacin: Es la que ve el usuario (tambin se la denomina "capa de usuario"), presenta el sistema al usuario, le comunica la informacin y captura la informacin del usuario en un mnimo de proceso (realiza un filtrado previo para comprobar que no hay errores de formato). Tambin es conocida como interfaz grfica y debe tener la caracterstica de ser "amigable" (entendible y fcil de usar) para el usuario. Esta capa se comunica nicamente con la capa de negocio. Capa de negocio: Es donde residen los programas que se ejecutan, se reciben las peticiones del usuario y se envan las respuestas tras el proceso. Se denomina capa de negocio (e incluso de lgica del negocio) porque es aqu donde se establecen todas las reglas que deben cumplirse. Esta capa se comunica con la capa de presentacin, para recibir las solicitudes y presentar los resultados, y con la capa de datos, para solicitar al gestor de base de datos almacenar o recuperar datos de l. Tambin se consideran aqu los programas de aplicacin. Capa de datos: Es donde residen los datos y es la encargada de acceder a los mismos. Est formada por uno o ms gestores de bases de datos que realizan todo el almacenamiento de datos, reciben solicitudes de almacenamiento o recuperacin de informacin desde la capa de negocio.

CAPA DE DATOS

CAPA DE NEGOCIO

CAPA DE PRESENTACIN

149

Ahora en lenguaje terrcola La capa de presentacin son los formularios, botones y controles con que interacta el usuario, el desarrollador que disea esta capa debe preocuparse por facilitar la navegacin del usuario, hacer atractivos los grficos, presentar la informacin de manera rpida y eficiente, etc. Esta metodologa permite que el equipo de trabajo avance en paralelo, es conveniente que una persona se encargue de interactuar con el cliente en cuestiones de diseo y la otra parte del equipo se enfoque a la parte lgica y de datos. La capa de negocio tiende a confundir en cuanto a su definicin, por lo regular asociamos este trmino con establecimientos comerciales, en este caso negocio se refiere a intercambio de informacin, tambin se le llama capa lgica y es donde reside realmente todo el modelado del sistema, es importante mencionar que aqu no existen formularios ni controles, todo son clases con diferentes tareas. Finalmente la capa de datos es la manera de separar los archivos fsicos como bases de datos, documentos xml o txt que almacenen la informacin, para quien disea la capa de negocio resulta completamente irrelevante cual es el fabricante de la base de datos ya que solo tiene que mandar sus objetos y esperar recibirlos sin preocuparse por las consultas o la conexin al servidor. En resumen, las capas nos facilitan el diseo de cualquier sistema y permiten reutilizar cdigo por bloques sacando mejor provecho a los objetos. Es importante que en nuestras soluciones de VisualStudio se definan las capas por medio de 3 proyectos, lgicamente la capa de presentacin es la nica que maneja formularios, mientras que la de negocio y datos son bibliotecas de clases. Por lo que hemos visto la capa de presentacin no tiene que ver con la capa datos, esto se refleja en las dependencias del proyecto. Comenzamos abriendo un proyecto del tipo Solution en VisualStudio, elegimos solucin en blanco. En esta solucin vamos a agrupar nuestras 3 capas. Lo nombramos SolucionMuestrasSensor En el explorador de soluciones nos aparece de esta manera:

150

Agregamos una carpeta dando click en el cono, la nombramos Proyectos Estando en la carpeta y dando click derecho elegimos agregar proyecto:

Elegimos un proyecto de windows forms con el nombre: CapaPresentacionSensores:

Nos queda de esta forma:

De nuevo damos click en la carpeta y agregamos otro proyecto del tipo Biblioteca de Clases, lo nombramos CapaNegocioSensores.

151

Nos queda:

Finalmente agregamos otro proyecto de tipo Biblioteca de Clases con el nombre: CapaDatosSensores.

Ahora tenemos la estructura bsica de la solucin:

Ahora vamos a crear la dependencia entre las capas: Damos click derecho en la carpeta References de nuestra capa de presentacin, elegimos agregar referencia.

152

Nos aparece esta ventana donde elegimos la capa de negocio en la pestaa de proyectos:

Ahora nuestra capa de presentacin puede acceder a todas las clases de la capa de negocio:

Repetimos el proceso con la capa de negocio y agregamos como referencia la capa de datos.

Nos queda:

153

Por lgica la capa de datos no depende de otras capas, es decir no tiene por que heredar de otras bibliotecas, esto creara una referencia circular que rompe con el concepto que buscamos sobre separar las tareas. En las capas de Negocio y Datos borramos las clases por defecto Class1.cs Empezamos con la capa de negocio, agregamos las siguientes clases: Muestra, ArrayListMuestras, Sensor. Para ahorrar tiempo las descargamos desde: www.multitecnologia.com/CapaNegocioSensores.rar Damos click derecho agregar elemento existente.

x x

Seleccionamos las tres y nos queda de esta forma:

Ahora para la capa de datos descargamos este archivo: www.multitecnologia.com/CapaDatosSensores.rar Siguiendo el mismo proceso agregamos estos 3 elementos existentes:

Elegimos las 3:

Cada una de estas clases se encarga de convertir en un archivo nuestro objeto sensor.

154

Nuestra capa de datos nos queda de esta forma:

An nos falta algo En la capa de datos vamos a agregar dos referencias necesarias para crear la base de datos y poder conectarnos a ella:

Ambas se encuentran en la pestaa de los componentes COM. Las referencias son: Microsoft ActiveX Data Objects 2.8 Library Microsoft ADO Ext. 2.8 for DDL and Security

Otro detalle importante es que los tres proyectos deben ejecutarse para plataforma x86, para seleccionarlo nos vamos a: Proyecto/Propiedades/Generar.

Recuerde seleccionar cada proyecto en particular y abrir esta ventana para seleccionar X86, hacemos esto por que los componentes COM que acabamos de insertar son para esta plataforma. 155

Para que se compile el namespace en cada clase debe tener el mismo nombre que el proyecto, en el caso de la capa de datos las tres clases:

En la capa de negocio las clases tambin deben tener el mismo nombre de proyecto y namespace.

Volvemos a la capa de presentacin y en nuestro formulario arrastramos los siguientes controles: Form Name: frmSensores Text. Generacin de archivos para muestras Button Name: btnCrearMuestra,Text: Crear Muestra DataGridView Name: dGridViewMuestras Columns: Coleccin Hora, Lectura

saveFileDialog Name: saveFileDialog

Button Name: btnGrabarTxt, Text: Grabar Muestras en Texto Button Name: btnGrabarXml, Text: Grabar Muestras en XML Button Name: btnGrabarDB, Text: Grabar Muestras en Base de Datos 156

En nuestro cdigo del formulario agregamos la directiva:


using CapaNegocioSensores;

Aparte dos instancias de Sensor y Random:


Sensor nuestroSensor = new Sensor(); //con este objeto random obtenemos valores aleatorios en la lectura Random lecturaSensor = new Random();

using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; using CapaNegocioSensores; //importamos la capa con las clases de negocio namespace CapaPresentacionSensores { public partial class frmSensores : Form {//creamos nuestro objeto sensor con mbito para toda la clase Sensor nuestroSensor = new Sensor(); //con este objeto random obtenemos valores aleatorios en la lectura Random lecturaSensor = new Random(); public frmSensores() { InitializeComponent(); }

En el evento Load del formulario le damos valores a Sensor:


private void frmSensores_Load(object sender, EventArgs e) {//para no estar reescribiendo el nombre y tag en el botn muestra //le damos sus propiedades al objeto sensor desde aqui nuestroSensor.Nombre = "SensorTemperatura_5B"; nuestroSensor.Tag = "PLC5-DB1"; }

Para el btnCrearMuestra:
private void btnCrearMuestra_Click(object sender, EventArgs e) {//creamos una instancia de muestra Muestra m = new Muestra(); m.hora = DateTime.Now.ToString();//guardamos la hora m.lectura = lecturaSensor.Next(255); //guardamos un entero aleatorio //agrega una muestra a nuestro objeto sensor nuestroSensor.agregarMuestra(m); //Imprime la muestra en el datagrid dGridViewMuestras.Rows.Insert(0, m.hora, m.lectura); }

157

En los otros botones el cdigo es el siguiente:


private void btnGrabarTxt_Click(object sender, EventArgs e) { saveFileDialog.DefaultExt = "txt"; saveFileDialog.Filter = "txt files (*.txt)|*.txt"; saveFileDialog.ShowDialog(); string ruta = saveFileDialog.FileName; //mandamos la ruta a la capa de negocio try //operacin de riesgo { nuestroSensor.EscribirTxt(ruta); MessageBox.Show("Confirmado, ya se guardo el archivo de texto"); } catch (Exception ee) //obtenemos cualquier mensaje de error {MessageBox.Show(ee.Message.ToString()); } } private void btnGrabarXml_Click(object sender, EventArgs e) { saveFileDialog.DefaultExt = "xml"; saveFileDialog.Filter = "xml files (*.xml)|*.xml"; saveFileDialog.ShowDialog(); string ruta = saveFileDialog.FileName; //mandamos la ruta a la capa de negocio try //operacin de riesgo { nuestroSensor.EscribirXml(ruta); MessageBox.Show("Confirmado, ya se guardo el archivo .XML"); } catch (Exception ee) //obtenemos cualquier mensaje de error {MessageBox.Show(ee.Message.ToString()); } } private void btnGrabarDB_Click(object sender, EventArgs e) { saveFileDialog.DefaultExt = "mdb"; saveFileDialog.Filter = "mdb files (*.mdb)|*.mdb"; saveFileDialog.ShowDialog(); string ruta = saveFileDialog.FileName; //mandamos la ruta a la capa de negocio try //operacin de riesgo { nuestroSensor.EscribirBaseDatos(ruta); MessageBox.Show("Confirmado, ya se guardo la base de datos Access"); } catch (Exception ee) //obtenemos cualquier mensaje de error { MessageBox.Show(ee.Message.ToString()); } }

Respetando todos estos detalles no debe existir problema para ejecutar, solo creamos algunas muestras con el botn crear muestra y convertimos a diferentes formatos de archivo nuestro objeto Sensor.

158

Para cada caso nos pide el lugar donde vamos a guardar el archivo y nos confirma cuando se gener correctamente.

Finalmente obtenemos tres archivos de respaldo en diferentes formatos.

ANEXOS Clases Capa Negocio


Clase Muestra.cs using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace CapaNegocioSensores { public class Muestra { //Lo inicializamos con la cadena vacio para diferenciar cuando //el objeto an no recibe su dato private string _Hora = "vacio"; public string hora {get {return _Hora; } set {_Hora = value; } }

159

//lo inicializamos con -1 para simbolizar que no ha sido llenado. private int _Lectura = -1; //_Lectura es entero por que las conversiones A-D siempre vienen en enteros public int lectura { get { return _Lectura; } set {_Lectura = value; } } //la propiedad indice es privada,solo con el mtodo asignaIndice se puede modificar private int _Indice; public int indice { get {return _Indice; } //no se ocupa un set por que no deseamos que se escriba este valor directamente } //este mtodo permite recibir la propiedad .count del arraylist public void asignaIndice(int indice) { _Indice = indice; } } }

Clase ArrayListMuestras.cs
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Collections; //Nos permite usar un ArrayList namespace CapaNegocioSensores { public class ArrayListMuestras:ArrayList { //con este mtodo agregamos una instancia de muestra a nuestro ArrayList public void agregarMuestra(Muestra m) { //le mandamos el indice como propiedad a la muestra m.asignaIndice(this.Count); this.Add(m); } //este mtodo nos trae todo el arreglo completo de muestras public ArrayListMuestras traerMuestras() { return this; } //este mtodo recibe el ndice del arreglo y regresa la muestra con ese indice public Muestra traeMuestra(int Indice) { foreach (Muestra m in this) //busca en todas las muestras de esta coleccin { if (m.indice == Indice) { return m; } } return null; } } }

160

Clase Sensor.cs
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Data; //nos permite instanciar DataSet using CapaDatosSensores; namespace CapaNegocioSensores { public class Sensor: ArrayListMuestras { //recordemos que todo el modelado y la lgica de la capa de negocio es //independiente de los datos, nuestras Clases: Sensor, Muestra, ArrayListMuestra //no son instanciables en esta capa, la capa de Negocio depende de la capa de Datos //No viceversa private string _Nombre="Sin Nombre"; //valor inicial public string Nombre { get { return _Nombre; } set { _Nombre = value; } } private string _Tag = "Sin Tag"; //valor inicial public string Tag { get { return _Tag; } set { _Tag = value; } } //esta funcin nos convierte nuestro arraylistMuestras (Indice, Hora, Lectura) en //un DataSet(tabla) que es fcil de mandar a otra capa con los datos ordenados private DataSet ConversionDataset() { DataSet tablaMuestras = new DataSet(); DataTable tabla = new DataTable(); tablaMuestras.Tables.Add(tabla); //agregamos las tres columnas de nuestra tabla: Indice, Hora, Lectura tablaMuestras.Tables[0].TableName = "Muestras"; //nombre de la tabla tablaMuestras.Tables[0].Columns.Add("Indice"); tablaMuestras.Tables[0].Columns.Add("Hora"); tablaMuestras.Tables[0].Columns.Add("Lectura"); foreach (Muestra m in this) { DataRow linea = tablaMuestras.Tables[0].NewRow(); linea[0] = m.indice.ToString(); //columna 0 de la lnea //columna 1 de la lnea linea[1] = m.hora; linea[2] = m.lectura.ToString();//columna 2 de la lnea tablaMuestras.Tables[0].Rows.Add(linea); } return tablaMuestras; }

161

//estas funcines son el intermediario entre la presentacin y capa de datos //recibimos la ruta y debemos mandar la coleccin de muestras en un dataSet public void EscribirTxt( string ruta) { //creamos nuestra instancia de la capa de datos y mandamos //la ruta al constructor como valor inicial EscrituraMuestrasTxt DatosTxt = new EscrituraMuestrasTxt(); if (ruta != null) //si nos llega una ruta vlida { //ahora convertimos nuestra tabla de muestras en un Dataset DataSet TMuestras = ConversionDataset(); // mandamos todo a la capa de datos para que se convierta en archivo txt DatosTxt.EscribeTxt(ruta, this.Nombre, this.Tag, TMuestras); } else { throw new ArgumentException("Existe un error en la coleccin o la ruta"); } } public void EscribirXml(string ruta) { EscrituraMuestrasXml DatosXml = new EscrituraMuestrasXml(); if (ruta != null) //si nos llega una ruta vlida { //nos convierte el arraylist en Dataset DataSet TMuestras = ConversionDataset(); // mandamos todo a la capa de datos para que se convierta en archivo XML DatosXml.EscribeXml(ruta, this.Nombre, this.Tag, TMuestras); } else { throw new ArgumentException("Existe un error en la coleccin o la ruta"); } } public void EscribirBaseDatos(string ruta) { EscrituraMuestrasBaseDatos DatosBD = new EscrituraMuestrasBaseDatos(); if (ruta != null) //si nos llega una ruta vlida { //nos convierte el arraylist en Dataset DataSet TMuestras = ConversionDataset(); // mandamos todo a la capa de datos para que se convierta en archivo MDB DatosBD.EscribeBD(ruta, this.Nombre, this.Tag, TMuestras); } else { throw new ArgumentException("Existe un error en la coleccin o la ruta"); } } } }

Siguiente... Clases Capa Datos

162

Clase EscrituraMuestrasBaseDatos.cs
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Text; using System.IO; using ADOX; using System.Data.OleDb; namespace CapaDatosSensores { //con esta clase convertimos nuestro Dataset en una Base de datos Access public class EscrituraMuestrasBaseDatos { public void EscribeBD(string ruta, string nombreSensor, string tagSensor, DataSet Muestras) { ADOX.CatalogClass cat = new ADOX.CatalogClass(); cat.Create("Provider=Microsoft.Jet.OLEDB.4.0; Data Source=" + ruta + ";" + "Jet OLEDB:Engine Type=5"); cat = null; string nombreTabla = nombreSensor; creaTabla(ruta,nombreTabla); //creamos las columnas de la tabla insertaDatos(ruta,nombreTabla, Muestras);//insertamos los valores del Dataset } public void creaTabla(string ruta, string nombreTabla) {//ahora nos conectamos para insertar las columnas con sus tipos de datos string conex = "Provider = Microsoft.Jet.OLEDB.4.0;" + @"Data Source = " + ruta + ";"; OleDbConnection con; string crear = "CREATE TABLE " + nombreTabla + " (Indice INT, Hora VARCHAR(50), Lectura INT);"; con = new OleDbConnection(conex); OleDbCommand cmd = new OleDbCommand(crear, con); con.Open(); cmd.ExecuteNonQuery();//se ejecuta la consulta //cerramos conexin con.Close(); } } public void insertaDatos(string ruta,string nombreTabla, DataSet Muestras) {// nos conectamos para insertar las columnas con sus tipos de datos string conex = "Provider = Microsoft.Jet.OLEDB.4.0;" + @"Data Source = + ruta + ";"; OleDbConnection con; string inserta; con = new OleDbConnection(conex); con.Open(); //abrimos la conexin con la base de datos //con un ciclo vamos insertando cada lnea de la tabla for (int i = 0; i < Muestras.Tables[0].Rows.Count; i++)//lneas { int ID = Convert.ToInt32(Muestras.Tables[0].Rows[i][0]); string Hora = Muestras.Tables[0].Rows[i][1].ToString(); int Lectura = Convert.ToInt32( Muestras.Tables[0].Rows[i][2]); inserta = "INSERT INTO " + nombreTabla + " VALUES (" + ID + ",' + Hora + "'," + Lectura + ")"; OleDbCommand cmd = new OleDbCommand(inserta, con); cmd.ExecuteNonQuery();//se ejecuta la consulta } //cerramos conexin con.Close(); } } }

163

Clase EscrituraMuestrasTxt.cs
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Collections; using System.Data; using System.IO; namespace CapaDatosSensores { //Con esta Clase convertimos un DataSet de las Muestras en un archivo TXT public class EscrituraMuestrasTxt { //La capa de datos solo necesita el nombre, tag y tabla de muestras (Dataset) //para crear un archivo, no le interesa como fueron modelados //los objetos en la capa de negocio public void EscribeTxt(string ruta, string nombreSensor, string tagSensor, DataSet Muestras) { String[] texto; //en este arreglo almacenamos toda la tabla texto = new String[Muestras.Tables[0].Rows.Count + 1]; //Rellenamos la cabecera del archivo texto[0] = "Sensor: " + nombreSensor + " Tag: " + tagSensor + " Estructura: "; //creamos las tres columnas con el encabezado: Indice, Hora, Lectura for (int i = 0; i < Muestras.Tables[0].Columns.Count; i++) { texto[0] += Muestras.Tables[0].Columns[i].ColumnName + ";"; } //Rellenamos los valores de la tabla separando por lneas y comillas String linea; for (int i = 0; i < Muestras.Tables[0].Rows.Count; i++)//lneas { linea = String.Empty; //limpiamos la lnea para reutilizarla for (int j = 0; j < Muestras.Tables[0].Columns.Count; j++)//Columnas { linea += Muestras.Tables[0].Rows[i][j].ToString() + ";"; } texto[i + 1] = linea; //se agrega al arreglo } //Creamos el archivo try { File.WriteAllLines(ruta, texto); } catch { throw new ArgumentException("No pude crear el archivo, esta vaco el DataSet"); } } } }

164

Clase EscrituraMuestrasXml.cs using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Data; using System.Xml; namespace CapaDatosSensores { //En esta clase convertimos nuestro dataset en un archivo XML en forma de rbol public class EscrituraMuestrasXml { public void EscribeXml(string ruta, string nombreSensor, string tagSensor, DataSet Muestras) { //eliminamos las reglas del Dataset para crear el xml Muestras.EnforceConstraints = false; //cambiamos su nombre antes de mandarlo al XML Muestras.DataSetName = "MuestrasSensor"; //grabamos la tabla simplemente asignando el dataset, // C# se encarga de acomodar los datos en forma de rbol XML XmlDataDocument xmldoc = new XmlDataDocument(Muestras); XmlNode NodoRaiz = xmldoc.FirstChild; //localizamos la raz del XML //creamos un nuevo nodo para los datos que faltan: Nombre, Tag XmlNode nuevoNodo = xmldoc.CreateNode(XmlNodeType.Element, "Sensor", null); //creamos un par de atributos con el nombre y tag que nos manda la capa de negocio XmlAttribute AtributoNombre = xmldoc.CreateAttribute("Nombre"); AtributoNombre.Value = nombreSensor; XmlAttribute AtributoTag = xmldoc.CreateAttribute("Tag"); AtributoTag.Value = tagSensor; //ahora agregamos esos atributos al nodo recin creado nuevoNodo.Attributes.Append(AtributoNombre); nuevoNodo.Attributes.Append(AtributoTag); //finalmente situamos ese nuevo nodo en la raz NodoRaiz.AppendChild(nuevoNodo); //guardamos nuestro documento xmldoc.Save(ruta ); } } }

En este ejercicio practicamos el concepto de patrones de diseo por capas y la generacin de archivos a partir de un objeto, es importante que aplique esta metodologa para sacar real provecho a C#.NET.

165

MONITOREO DE PLC'S POR MEDIO DE APLICACIONES EN C#.NET


En esta prctica nos vamos a conectar a un equipo Siemens S7-300 y Koyo DL06 por medio de un servidor OPC. Qu es OPC? OLE for Process Control El estndar OPC permite la comunicacin entre dispositivos industriales para aplicaciones de monitoreo y control, cada marca que soporta este protocolo crea su propio driver para integrarse a la red y compartir sus recursos. El estndar OPC es regulado por la fundacin (http://www.opcfoundation.org/), su objetivo es facilitar el uso de esta tecnologa y proporciona cdigo fuente para que diferentes fabricantes implementen sus servidores y clientes OPC. En nuestro curso vamos trabajar con uno de los fabricantes ms populares Kepware. KepWare es una empresa especializada en conectividad para equipos industriales que maneja las marcas y modelos mas populares y los pone a disposicin de cualquier aplicacin cliente como LabView, Wonderware o en nuestro caso C#.NET. Utilizaremos como intermediario una versin demo del servidor KepServer, solo vamos a configurar la conexin con los PLC y KepServer se encargar de mandar los datos a nuestra aplicacin cliente en C#.NET Empezamos entrando a la pgina de KepWare. http://www.kepware.com/ Para descargar la versin demo debemos registrarnos y activar una cuenta. Al entrar como usuario podemos descargar KepServer y operarlo durante dos horas continuas antes que nos pida un reset.

En caso de ocupar ms de dos horas para las pruebas se reinicia el servidor o la PC. En nuestro panel de usuario descargamos KepServer y ClientAce. ClientAce nos permite conectarnos desde C#.NET a nuestro servidor.

166

Una vez descargados comenzamos la instalacin de KepServer cuidando los siguientes detalles:

Debemos seleccionar todos los modelos de PLC a los que deseamos conectarnos, las casillas que no sean activadas no estn incluidas en la instalacin. En nuestro caso debemos activar todos los drivers para Siemens, Allen-Bradley y AutomationDirect. Continuamos los pasos hasta finalizar.

Al instalar nuestro servidor y activarlo obtenemos un panel de monitoreo como el mostrado, si usted tiene alguna simulacin activada posiblemente vea ms datos en la tabla.

En Kepserver los datos de acceso se organizan por medio de canales, cada canal tiene diferentes dispositivos que agrupan nuestros datos. Ahora con nuestro servidor instalado vamos a preparar los PLC's para mandar sus datos.

167

Configurando un PLC KOYO DL06 de Automation Direct


En este ejemplo nos vamos a conectar a un PLC KOYO DL06 con 20 entradas y 16 salidas digitales, por medio de un cable RS232- RJ45 y el protocolo K Sequence vamos a dar de alta los datos en KepServer, una vez disponibles en el servidor podemos consultar las Tags con nuestra aplicacin cliente en C#.NET Al abrir DirectSoft32 nos pregunta por el nombre del proyecto el modelo del PLC, el nombre del proyecto no puede ser largo.

De forma inmediata nos aparece el entorno para programar en Ladder.

Para establecer comunicacin con el PLC nos vamos al men principal: Connect

Elegimos COM1, si estamos usando un convertidor USB-RS232 y se registro un COM mayor a 2 tenemos que irnos a las propiedades de ese puerto en Windows y cambiarle el ID a 1 o 2.

Antes debemos tener nuestro PLC encendido con el cable conectado al puerto COM de la PC. Al aparecer esta ventana damos click en aadir.

168

En el dispositivo elegimos: Not Sure

Protocolo: K Sequence

Ahora le damos un nombre a la conexin.

Nos queda

Al seleccionarlo es importante tener el PLC en estado de terminal o nos aparecer el siguiente mensaje.

Cuando se crea con xito la conexin, DirectSoft compara las versiones del programa entre PLC y pantalla, posteriormente salta una ventana donde nos pregunta si deseamos cargar a pantalla el programa grabado en el equipo o viceversa.

Elegimos usar Use Disk para comenzar con nuestro proyecto en blanco. Para empezar a crear el diagrama DirectSoft debe estar en modo Program.

Vamos a insertar 3 contactos normalmente abiertos con nuestras entradas digitales y asignaremos la salida fsica Y0. En el PLC DL06 las entradas se encuentran en los registros X y las salidas en los registros Y. Para ver nuestra barra de dispositivo elegimos Edit Mode: ON

169

Ahora se muestran los elementos en la parte inferior. Vamos a arrastrar 3 contactos normalmente abiertos: F2 Nos aparece esta ventana:

Damos click en la lupa: Por lo pronto solo Le asignamos su direccin (X0) y nickname (encendido) Nos queda

Repetimos esta operacin para X1 y X2 con los nicknames: confirmacin y accionamiento.

Para la salida nos vamos al botn de bobinas manteniendo seleccionado el final del diagrama (NOP).

Con F4 nos aparece el catalogo

Elegimos Standard Coil: OUT.

170

Nos aparece de esta manera pero falta la direccin fsica, de nuevo click en la lupa. Elegimos Y0 con el nickname salida.

Para terminar debemos indicar que nuestro diagrama ha terminado, nos vamos al final de la siguiente lnea. Con F5 asignamos un comando END

Finalmente todo el diagrama queda de esta forma:

Con el botn de Status accionado podemos cerrar y ver se activan los contactos en color azul.

Ahora guardamos el diagrama en el PLC.

Al guardarlo y poner el PLC en RUN comprobamos en pantalla la activacin de la salida.

Muy bien, ahora nos falta dar de alta estas direcciones Tags en el servidor IPC (KepServer). Para esto nos vamos men y en archivo elegimos exportar la documentacin de los elementos.

171

Elegimos una ruta para nuestro archivo csv y listo.

Ahora nos vamos a KepServer y agregamos un canal:

Elegimos el mismo protocolo que se uso para programar el PLC: K Sequence No olvidemos habilitar el diagnostico.

Los datos del COM se mantienen con las mismas caractersticas anteriores.

Nos queda:

Ahora agregamos el dispositivo, damos un nombre relativo a la Marca. Elegimos nuestro modelo:

172

Mantenemos el ID=1

Elegimos los parmetros propuestos:

Importamos nuestro archivo csv generado anteriormente con DirectSoft32. Continuamos hasta finalizar la conexin: Al principio no aparece nada

Damos click derecho en el dispositivo y en la ventana elegimos la pestaa Database Creation Autocreate.

Ahora podemos ver nuestros Tags separados por entradas X y salidas Y.

Para conocer su valor damos click en el Quik Client:

Ahora al mover nuestros interruptores X0, X1 y X2 del PLC podemos ver como cambian su valor. Con este ejercicio comprobamos que es posible acceder a los datos del PLC DL06 por medio de KepServer. 173

Configurando un PLC Siemens S7-300


En esta prctica vamos a configurar un PLC Siemens S7-300 CPU 315F -2PN/DP con dos mdulos de entradas digitales y anlogas: DI16/DO16xDC24V, A14/A02 x8BIT. La caracterstica principal de este CPU es contar con una interfaz ethernet para comunicarse directamente con PC's o por medio de una red TCP/IP. Vamos configurar el hardware del S7-300 con el Simatic Manager Creamos un nuevo proyecto: Asignamos un nombre:

Nos aparece nuestro proyecto vaco.

Agregamos un nuevo objeto S7-300

Ahora damos Click en Hardware

Nos aparece otro programa de nombre HWConfig, aqu configuramos todo el hardware para que coincida con nuestro dispositivo real.

174

Como primer paso necesitamos un bastidor, lo arrastramos desde el men derecho.

En caso de que nuestra versin de Simatic no se encuentre actualizada debemos conectarnos a Internet y descargar todos los modelos actuales:

Cuando se ocupan actualizaciones se crea una lista para descargar y Simatic se encarga de poner al da todo el entorno de programacin.

Con nuestro software actualizado podemos construir nuestro sistema.

En la localidad 1 agregamos una fuente de poder, primero seleccionamos el 1 y despus la fuente.

175

Ahora seleccionamos el 2 y elegimos el CPU 315-2FJ14-0AB0, posiblemente aparezca la ventana de configuracin IP que se muestra, si tenemos el PLC conectado directamente a la PC (sin router) podemos dejar la configuracin mostrada con la IP del PLC 192.168.0.1, por lgica nuestra PC debe tener cualquier otra IP que este en el mismo segmento y que no choque con la del PLC ejemplo: 192.168.0.254.

Continuando con nuestro bastidor nos saltamos hasta el slot 4 dejando el 3 vaco ya que solo se usa en aplicaciones complejas. Arrastramos la tarjeta: DI16/DO16xDC24V al slot 4

Finalmente en el slot 5 arrastramos la tarjeta A14/A02x8BIT

176

Guardamos y compilamos nuestro sistema.

Para probar la sincrona con nuestro PLC nos vamos al men y elegimos:

Elegimos la primera opcin...

Ahora nos pide una confirmacin de la IP del PLC que vamos a asignar.

Cerramos el HWConfig y regresamos al Simatic Manager.

En el nodo de programa elegimos Bloques:

177

Por defecto nos aparece el bloque OB1 donde vamos a crear nuestro programa en lenguaje de escalera (KOP).

Al elegir KOP y Aceptar nos aparece Step7 para programar nuestro PLC recin configurado.

Seleccionando el primer segmento agregamos un contacto normalmente abierto:

En la direccin elegimos una entrada en el registro 1 bit 0

178

Ahora elegimos una salida:

Asignamos la salid: Byte 0, bit 0 Guardamos el proyecto: Lo cargamos al PLC

Si no existen problemas de comunicacin podemos probar con los interruptores del PLC. Ahora nos vamos a KepServer para dar de alta nuestro S7-300 El procedimiento para agregar un Tag proveniente del PLC es el siguiente: Dando click derecho elegimos agregar canal: Como nos vamos a conectar por Ethernet a nuestro S7-300 elegimos: Siemens TCPIP/Ethernet y activamos la casilla de diagnostico para localizar errores fcilmente.

179

En el adaptador de red podemos elegir Default y en la siguiente ventana aceptamos la opcin predeterminada.

Finalizamos la conexin...

Al terminar nos queda un canal de comunicacin listo para dar de alta los PLC's.

Ahora elegimos nuestro PLC S7-300 Damos Click en agregar dispositivos y asignamos un nombre relacionado con el rea que se va a automatizar o la marca del equipo:

En esta parte es bsico elegir la direccin IP del PLC que se le asigno desde Simatic al momento de configurar el CPU.

180

Si deseamos comprobar que nuestro PLC ya tiene una IP podemos hacer un PING desde la ventana de comandos de Windows: teclamos cmd

Y ahora escribimos la IP de este ejemplo: ping 192.168.0.1 El PLC debe responder como cualquier dispositivo que maneje TCP/IP.

Continuando con la instalacin elegimos los valores de timing por default.

Ahora nos queda nuestro canal apuntando a un dispositivo en particular, solo nos resta asignarle los tags que vamos a monitorear.

181

Para asignar cada Tag no es necesario importar una Si desconocemos la localidad del Tag la biblioteca de smbolos, solo escribimos la direccin podemos obtener de la tabla de smbolos del PLC: I1.0 del programa HWConfig (Simatic).

Ahora nos aparece el Tag en el panel del servidor.

Para probar que realmente tenemos acceso a nuestro Tag abrimos el Quick Client

La ventana nos muestra otros accesos a memoria del PLC y propiedades del canal. Nos vamos a Channel1.EquipoSiemens y nuestro Tag debe aparecer con calidad Good en caso contrario debemos reconfigurar la conexin, checar el cableado y que el PLC este operando.

182

Para una entrada analgica solo escribimos la direccin del Tag (IW276) para tener acceso al dato tipo WORD. Si no conocemos el tipo de dato elegimos Default.

Ahora al activar al QuickClient y variar los potencimetros del PLC podemos ver los cambios en la variable.

IMPORTANTE: Observe que no fue necesario programar el PLC para tener acceso a sus datos. Con KepServer manejando nuestros Tags y comunicndose con los dispositivos ya estamos listos para programar el cliente en C#.NET.

Aplicacin Cliente para monitorear PLCs desde C#.NET


En este ejercicio vamos a crear un cliente en C# que se conecte con KepServer y pueda monitorear 10 Tags digitales. Vamos a crear un proyecto sencillo con dos formularios como se muestra: El frmCliente.cs es donde vamos a registrar las Tags. El frmVentanaDispositivo.cs es una ventana emergente que se va a disparar cuando algn Tag cambie su valor. 183

Para trabajar en este ejemplo debemos tener a la mano los controles del ClientAce en la barra e herramientas.

Importante: Si a usted no le aparece ClienAceDa_Junction en el cuadro de herramientas lo debe agregar manualmente siguiendo estos pasos...(siguiente hoja).

Dar click derecho sobre el cuadro de herramientas y elegir elementos.

Elegimos examinar y buscamos el archivo Kepware.ClientAce.DA_Junction.dll en archivos de programa/Kepware Technologies

Al darle click y cerrar estas ventanas nos debe aparecer en el cuadro de herramientas

Ahora ocupamos agregar otra dll al proyecto, nos vamos a la carpeta de referencias y agregamos el archivo Kepware.ClientACE.OpcClient.dll

Se debe ver de esta forma, esta librera no es un componente del cuadro de herramientas. 184

Para evitar problemas a la hora de compilar en algunas mquinas con Windows 7 vamos a desactivar el LoaderLock siguiendo estos pasos: Depurar-> Excepciones->Managed Degubbed assistants-->Loaderlock (desactivarlo).

En nuestro frmCliente arrastramos y configuramos los siguientes controles: Formulario Name: frmCliente, Text: Cliente para KepServer CheckBox Name: chkConectarse, Text: Conectarse a KepServer, Backcolor: Red Label Name: lblTags, Text: Escriba la direccin del Tag TextBox Name: txtTag, Enabled: False Button Name: btnRegistrarTag, Enabled: False, Backcolor: Red

DataGridView Name:dGridTags Columns: 1 columna con Name: DireccinTag HeaderText: Direccin Tag

Agregamos el componente clientAceDA_Junction1 al formulario y conservamos su nombre. Agregamos una columna al dGridTags como se muestra.

185

El cdigo: Agregamos estas directivas e instancias.


using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; using Kepware.ClientAce.OpcDaClient; using Kepware.ClientAce.DA_Junction; namespace PruebasKepserver { public partial class frmCliente : Form { DaServerMgt daServerMgt = new DaServerMgt(); //Instancia del Servidor ItemIdentifier[] itemIdent = new ItemIdentifier[10]; //capacidad para 10 Tags int activeServerSubscriptionHandle; //nos sirve para suscribir Tags byte numTags = 0; //contador para crear un ID con cada nuevo Tag public frmCliente() { InitializeComponent(); //registramos el evento Datachanged daServerMgt.DataChanged += new DaServerMgt.DataChangedEventHandler(DataChanged); }

En nuestro CheckBox chkConectarse


private void chkConectarse_CheckedChanged(object sender, EventArgs e) { if (chkConectarse.Checked) { //Si nuestro servidor es la versin 5 y esta operando usamos esta cadena string url = "opcda:///Kepware.KEPServerEX.V5"; int clientHandle = 1; ConnectInfo cInfo = new ConnectInfo(); cInfo.LocalId = "MiConexion"; cInfo.KeepAliveTime = 1000; Debe estar escrito exactamente igual, cInfo.RetryAfterConnectionError = true; observe que son 3 diagonales /// cInfo.RetryInitialConnection = true; bool connectFailed; try { //Intentamos conectarnos a KepServer daServerMgt.Connect(url, clientHandle, ref cInfo, out connectFailed); CreaInstancias();//crea 10 instancias para tags con valores vacos //como no salto la excepcin cambiamos los grficos a verde //y habilitamos los controles chkConectarse.BackColor = Color.GreenYellow; btnRegistrarTag.BackColor = Color.GreenYellow; chkConectarse.Text = "Conectado"; btnRegistrarTag.Enabled = true; txtTag.Enabled = true; dGridTags.Enabled = true; } catch (Exception ex) { //se registro un error connectFailed = true; MessageBox.Show(ex.ToString()); } }

186

else { //nos desconectamos del servidor daServerMgt.Disconnect(); //cambiamos los grficos para simbolizar la desconexin btnRegistrarTag.Enabled = false; txtTag.Enabled = false; dGridTags.Enabled = false; chkConectarse.Text = "Conectarse a KepServer"; txtTag.Text = ""; chkConectarse.BackColor = Color.Red; btnRegistrarTag.BackColor = Color.Red;

} }

Creamos una funcin para llenar de manera temporal el arreglo para las 10 Tags y evitar errores posteriores.
//esta funcin crea 10 instancias vacas para nuestro arreglo //ItemIdentifier[] itemIdent = new ItemIdentifier[10]; //al momento de registrar cada una se le cambia el nombre, la idea es llenar //el arreglo de manera temporal para no provocar error con valores nulos private void CreaInstancias() { for (int x = 0; x <= 9; x++) { itemIdent[x] = new ItemIdentifier(); itemIdent[x].ItemName = ""; itemIdent[x].ClientHandle = x; itemIdent[x].DataType = null; } }

Ahora otra funcin para suscribir los datos desde nuestro formulario.
private void SuscribirDatos( string nombreTag) { if (numTags < 10) { txtTag.Text = ""; //no importa si se registra o no, el texto del Tag se borra //parmetros de la conexin int clientSubscriptionHandle = 1; bool active = true; int updateRate = 1000; float deadBand = 0.0f; itemIdent[numTags].ItemName = nombreTag; //actualizamos el nombre del Tag int revisedUpdateRate; try { //este mtodo da de alta el grupo de Tags daServerMgt.Subscribe(clientSubscriptionHandle, active, updateRate, out revisedUpdateRate, deadBand, ref itemIdent, out activeServerSubscriptionHandle); if (itemIdent[numTags].ResultID.Succeeded == false) { MessageBox.Show("Falla: " + itemIdent[numTags].ItemName.ToString()); } else {//si llega a este punto el registro fue exitoso dGridTags.Rows.Insert(0, itemIdent[numTags].ItemName.ToString()); numTags++; //incrementamos el identificador del arreglo } }

187

catch (Exception ex) { MessageBox.Show("Fallo: " + ex.Message.ToString()); } }//si el numero de Tags no pasa de 10 else {MessageBox.Show("solo podemos registrar 10 Tags"); } }

Creamos la funcin DataChanged que acta como interrupcin cada vez que algun Tag registrado sufre algn cambio.
//Este mtodo se dispara ante cualquier cambio en los tags registrados private void DataChanged(int clientSubscription, bool allQualitiesGood, bool noErrors, ItemValueCallback[] itemValues) { try //busca en todos los tags registrados para encontrar cual tiene un cambio { foreach (ItemValueCallback itemValue in itemValues) { if (itemValue.ResultID.Succeeded) { //creamos una instancia del formulario para la ventana del Tag frmVentanaDispositivo ventana = new frmVentanaDispositivo(); //mandamos 4 parmetros al mtodo publico en la clase frmVentanaDispositivo(); //public void parametrosTag(string direccion,string valor, //string calidad, string tiempo) ventana.parametrosTag( itemValue.ClientHandle.ToString(), itemValue.Value.ToString(), itemValue.Quality.Name.ToString(), itemValue.TimeStamp.ToString()); ventana.Show(); } } } catch (Exception ex) { MessageBox.Show("Ocurri un error, la razn es ... ", ex.ToString()); } }

Finalmente en el botn para registrar los Tags:


private void btnRegistrarTag_Click(object sender, EventArgs e) { //Mandamos como parmetro el nombre del tag if (txtTag.Text != "") { bool validaSuscribir = true;//bandera para validar suscripcin for (int x = 0; x <= 9; x++) { //buscamos entre todos los Tags para ver si ya esta dado de alta if (txtTag.Text == itemIdent[x].ItemName) { validaSuscribir = false; //no va a permitir que se suscriba MessageBox.Show("este Tag ya esta dado de alta"); break; } } //si no encontro algn valor igual nos permite registrarlo if (validaSuscribir) {SuscribirDatos(txtTag.Text); } } else { MessageBox.Show("Falta el nombre del Tag"); } 188 }

Ahora en el otro formulario arrastramos un control Label:

Label Name: lblMensaje

En su cdigo agregamos un mtodo pblico que nos permita mandarle datos desde frmCliente.
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; namespace PruebasKepserver { public partial class frmVentanaDispositivo : Form { public frmVentanaDispositivo() { InitializeComponent(); } public void parametrosTag(string direccion,string valor, string calidad, string tiempo) { lblMensaje.Text = "La entrada: " + direccion + " Tiene un valor: " + valor; if (valor == "True") { this.BackColor = Color.YellowGreen; } else { this.BackColor = Color.Red; } } } }

Al ejecutar nos conectamos al servidor y registramos los Tags con el mismo nombre que tienen en la pantalla de KepServer.

Para el ejemplo de este curso los Tags grabados en el PLC Koyo DL06 y KepServer son: Channel1.PLCAutomationDirect.X.accionamiento Channel1.PLCAutomationDirect.X.confirmacin Channel1.PLCAutomationDirect.X.encendido Es necesario que el nombre sea exactamente el mismo, respetando acentos y maysculas.

189

Registramos los Tags uno por uno.

Por ser una versin Demo de ClientAce nos salta esta ventana:

Ahora cuando se va registrando cada nuevo Tag salta la ventana con los valores nuevos.

Si usted mueve el interruptor del Tag registrado debe observar como se crea otra ventana con los datos.

Con este ejercicio comprobamos que es posible conectarnos a un PLC desde C#.NET aplicando el software de Kepware.

DISPOSITIVOS MVILES EN C#.NET

Prctica: Monitoreo y Control con Smartphone


En este ejercicio vamos a crear un proyecto para recibir mensajes por un puerto serial bluetooth y mandar comandos de activacin a otra aplicacin. La aplicacin consiste en monitorear una tabla de alarmas y activar 4 motores aireadores desde el Smartphone.

Posiblemente ocupe actualizar VisualStudio con los SDK, descargue los marcados dependiendo su sistema operativo e idioma.

190

Creamos un nuevo proyecto tipo Smart Device y seleccionamos Windows Mobile 6 Standard SDK.

Por defecto nos aparece un SmartPhone rectangular, para cambiarlo usamos la propiedad FormFactor: Windows Mobile 6 Standard Landscape QVGA

Renombramos Form1.cs con frmPrincipal.cs

191

Aparte agregamos otro formulario con las mismas caractersticas: Le asignamos el nombre: frmPlanta.cs

Nos queda: Nuestro proyecto solamente manejar dos formularios, en el primero vamos a monitorear una tabla de alarmas y en el segundo activaremos 4 motores aireadores. Vamos a realizar una prueba de comunicacin: 1.- Conectamos nuestro SmartPhone . o PDA a la PC por su cable USB. 2.- Ejecutamos nuestro proyecto A continuacin salta una ventana donde aparecen las opciones de compilacin:

Si nuestro Smartphone esta conectado elegimos la opcin: Windows Mobile 6 Standard Device, en caso contrario elegimos el emulador mas parecido al dispositivo real. Los emuladores permiten probar el cdigo sin ocupar un dispositivo real, sin embargo no cubren todas las funciones, en este caso de los puertos Bluetooth que aplicaremos en este curso. El ejercicio que vamos a codificar necesita un dispositivo real con puerto Bluetooth y al menos un puerto COM virtual. Si fue posible cargar los formularios a nuestro dispositivo real estamos listos para agregar el cdigo. 192

Una de las diferencias que tienen los proyectos para mviles es que la organizacin de las dependencias es diferente, si ocupamos implementar capas de negocio y datos las vamos a incorporar como carpetas en el mismo proyecto. Dando click derecho sobre el proyecto agregamos una carpeta como se muestra, la nombramos Capa Negocio. Ahora dando click derecho sobre la carpeta CapaNegocio agregamos 3 nuevas clases con los nombres: El cdigo para la clase Alarmas.cs nos define 3 propiedades de tipo string donde guardaremos mensajes de alarmas. Es importante que el namespace tenga el mismo nombre que el proyecto: MonitoreoSmartPhone
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace MonitoreoSmartPhone { class Alarmas {//en esta propiedad guardamos la hora de la Alarma private string _hora; public string Hora { get { return _hora; } set { _hora = value; } } //Esta propiedad guarda el conceptode la Alarma //Ejemplo: "Sobrecalentamiento caldera 5A" private string _concepto; public string Concepto { get { return _concepto; } set { _concepto = value; } } //En esta propiedad se guarda la Accin recomendable para arreglar la //falla para facilitar la operacin y estandarizar el proceso // Ejemplo: "Ajustar el control PID" private string _accin; public string Accin { get { return _accin; } set { _accin = value; } } } }

193

En la clase AlarmasArrayList.cs guardamos la coleccin de objetos alarma y creamos dos mtodos, uno para agregar cada mensaje y otro para crear una tabla de datos que posteriormente se incrustar en un Datagrid del dispositivo mobil.
using System; using System.Linq; using System.Collections.Generic; Es importante que el namespace tenga el using System.ComponentModel; mismo nombre que el proyecto: using System.Data; MonitoreoSmartPhone using System.Text; using System.Collections; namespace MonitoreoSmartPhone Recuerde que heredamos de Arraylist y debe class AlarmasArrayList:ArrayList { estar incorporado System.Collections { //este mtodo crea el arreglo de alarmas public void agregaAlarma(Alarmas alarma) this.Add(alarma); { } //este mtodo crea una tabla a partir del arraylist de alarmas public DataTable TraeTabla() DataSet tablaMuestras = new DataSet(); { DataTable tabla = new DataTable(); tablaMuestras.Tables.Add(tabla); //nombre de la tabla tablaMuestras.Tables[0].TableName = "Alarmas"; //agregamos las tres columnas de nuestra tabla: tablaMuestras.Tables[0].Columns.Add("Hora"); tablaMuestras.Tables[0].Columns.Add("Alarma"); tablaMuestras.Tables[0].Columns.Add("Accion"); //llenamos la tabla con cada objeto Alarma de nuestro Arraylist foreach (Alarmas a in this) DataRow linea = tablaMuestras.Tables[0].NewRow(); { linea[0] = a.Hora; linea[1] = a.Concepto; linea[2] = a.Accin; tablaMuestras.Tables[0].Rows.Add(linea); } //nos regresa una tabla ordenada con todas las alarmas para poder //incrustarla en el DataGrid return tablaMuestras.Tables[0]; } } }

Finalmente creamos la clase Dispositivos.cs, en ella guardamos el estado actual de 4 motores aireadores que se activarn en la pantalla mvil.
using System; using System.Linq; using System.Collections.Generic; using System.Text; namespace MonitoreoSmartPhone { //en esta clase guardamos el estado actual de los dispositivos class Dispositivos private bool _activaAireador1=false; { public bool ActivaAireador1 { get { return _activaAireador1; } set { _activaAireador1 = value; } }

194

private bool _activaAireador2 = false; public bool ActivaAireador2 { get { return _activaAireador2; } set { _activaAireador2 = value; } } private bool _activaAireador3 = false; public bool ActivaAireador3 { get { return _activaAireador3; } set { _activaAireador3 = value; } } private bool _activaAireador4 = false; public bool ActivaAireador4 { get { return _activaAireador4; } set { _activaAireador4 = value; } } } }

Ahora en nuestro frmPrincipal modificamos la resolucin y deshabilitamos la propiedad skin donde aparece el fondo de los botones. El SmartPhone aplicado en este ejercicio es el Sony Erickson modelo Aspen, sin embargo el cdigo tambin puede funcionar con PDA's con windows mobile de versiones anteriores. Para este modelo la resolucin de pantalla es de 320x240 sin embargo al momento de cargar la aplicacin los controles se ven mas chicos, para arreglarlo cambiamos el pixelaje de nuestros formularios a 430x 240. Agregamos los siguientes controles y modificamos las propiedades. DataGrid Name: dGridAlarmas CheckBox Name: chkPuertoSerie Text: Abrir Puerto Serie Label Name: lblPuerto Text: Msg TextBox Name: txtDatoSerie
mainMenu1 viene por defecto

SerialPort Name: serialPort 9,600 baudios, databits 8, paridad None, para este Smartphone seleccionamos el COM0 por Blueetooth.

MenuItems Name: menuItemDispositivos, Text: Dispositivos

Name: menuItemActualizaTabla, Text: Actualizar Tabla

195

Es importante conocer que este Datagrid para mviles es diferente al DatagridView de las aplicaciones de escritorio, en especial se encuentra muy limitado en sus mtodos ya que no es posible insertar lneas paso por paso, debemos hacerlo por medio de una tabla. En estos pasos vamos a configurarlo para Mapear una tabla con tres columnas: Hora, Alarma, Accin. Configuramos el Datagrid de la siguiente forma: En el dataGridTableStyle escribimos Alarmas en la propiedad MappingName. A la propiedad GridColumnStyles agregamos 3 columnas con las siguientes caractersticas:

Para el cdigo de frmPrincipal creamos las siguientes instancias:


using using using using using using using using using System; System.Linq; System.Collections.Generic; System.ComponentModel; System.Data; System.Drawing; System.Text; System.Windows.Forms; System.Collections;

namespace MonitoreoSmartPhone { public partial class frmPrincipal : Form { AlarmasArrayList arrayAlarmas = new AlarmasArrayList(); Dispositivos aireadores = new Dispositivos(); string datoSerial; public frmPrincipal() { InitializeComponent(); }

Para instertar bien los datos es necesario que los nombres del mapeo coincidan con la tabla que se insertar a futuro.

196

El cdigo para abrir el puerto serie es similar a los ejemplos anteriores.


private void chkPuertoSerie_CheckStateChanged(object sender, EventArgs e) { if (chkPuertoSerie.Checked) {try { if (!serialPort.IsOpen) //si el puerto esta cerrado lo abre { serialPort.Open(); } //habilita el botn solo si esta abierto el puerto chkPuertoSerie.Text = "Puerto Abierto"; chkPuertoSerie.BackColor = Color.YellowGreen; lblPuerto.Text = "COM: " + serialPort.PortName.ToString(); } catch { lblPuerto.Text="No pude abrir el puerto COM"; } } else {try { if (serialPort.IsOpen) { serialPort.Close(); chkPuertoSerie.BackColor = Color.Red; } } catch { lblPuerto.Text="Algo anda mal no pude cerrar el COM"; } } }

En el evento DataReceived del SerialPort:


private void serialPort_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e) { try //intentamos leer el puerto { serialPortChat.ReadTimeout = 2000;//espera 2 segundos por el dato //almacena toda la cadena de datos hasta que lee "-FIN-" datoSerial = serialPortChat.ReadTo("-FIN-"); //ahora recorta los datos e invoca al manejador this.Invoke(new EventHandler(TextoRecibido)); } catch //no llego la cadena completa, interceptamos el error { MessageBox.Show("Error, no recib el identificador final de la cadena -FIN-"); serialPortChat.DiscardInBuffer(); //borra los datos del buffer de entrada } }

Tambin creamos las funciones:


private void analisisCadena(string dato) { //recibimos una cadena con este formato // Hora + "+" + Alarma + "+" + Accin // "12:30 hrs + Sobrecalentamiento + Checar Bomba 1-5" // Split crea un arreglo de los datos separados por un '+' string[] arreglocadena = dato.Split('+'); //guardamos los datos en nuestro objeto Alarma Alarmas alarma = new Alarmas(); alarma.Hora= arreglocadena[0]; alarma.Concepto= arreglocadena[1]; alarma.Accin= arreglocadena[2]; //agregamos este mensaje a la coleccin de alarmas arrayAlarmas.agregaAlarma(alarma); }

197

private void TextoRecibido(object sender, EventArgs e) { txtDatoSerie.Text = datoSerial; analisisCadena(datoSerial); } //creamos este mtodo publico para recibir valores //desde el otro formulario de dispositivos public void comandoActivacion(CheckBox chk) { //al cambiar de formualrio se puede cerrar el puerto, por eso se vuelve a abrir if (!serialPort.IsOpen) { serialPort.Open(); } //nos manda el valor del ChkAireador elegido serialPort.Write(chk.Name.ToString() + " : " + chk.Checked.ToString() + "Fin"); }

Seleccionando cada botn de los mens en su evento click escribimos:

private void menuItemDispositivos_Click(object sender, EventArgs e) { //Abrimos el otro formulario frmPlanta planta = new frmPlanta(); planta.Show(); } private void menuItemActualizaTabla_Click(object sender, EventArgs e) { //actualizamos el dataGrid con el mtodo TraeTabla DataTable tabla = arrayAlarmas.TraeTabla(); //tabla: trae los valores organizados en 3 columnas: Hora, Alarma, Accin dGridAlarmas.DataSource = tabla; } PictureBox En la propiedad Image y SizeMode asignar a todos: aireador_OFF_small.jpg SizeMode: StretchImage Name: picBox1 Name: picBox2 Name: picBox3 Name: picBox4 CheckBox Name: chkAireador1 Name: chkAireador2 Name: chkAireador3 Name: chkAireador4 Text: Aireador 1 Text: Aireador 2 Text: Aireador 3 Text: Aireador 4

mainMenu1 viene por defecto

Name: menuCerrar 198

A nuestros 4 controles CheckBox les agregamos un mtodo compartido en su evento Click, lo nombramos AireadorElegido.

Seccionando el botn del men activamos su evento Click.

using System.ComponentModel; using System.Data; using System.Drawing; using System.Text; using System.Windows.Forms; namespace MonitoreoSmartPhone { public partial class frmPlanta : Form { frmPrincipal principal = new frmPrincipal(); public frmPlanta() { InitializeComponent(); } //con este mtodo cambiamos el grfico del dispositivo //y mandamos el aviso al otro formulario private void AireadorElegido(object sender, EventArgs e) { string ruta = System.IO.Path.GetDirectoryName( System.Reflection.Assembly.GetExecutingAssembly().GetName().CodeBase); ruta = ruta.Replace("file:\\", ""); CheckBox check = ( CheckBox)sender; principal.comandoActivacion(check); if (check.Checked) //se activa la casilla { switch (check.Text) { case "Aireador 1": picBox1.Image = new System.Drawing.Bitmap(ruta + @"\aireador_ON_small.jpg"); break; case "Aireador 2": picBox2.Image = new System.Drawing.Bitmap(ruta + @"\aireador_ON_small.jpg"); break; case "Aireador 3": picBox3.Image = new System.Drawing.Bitmap(ruta + @"\aireador_ON_small.jpg"); break; case "Aireador 4": picBox4.Image = new System.Drawing.Bitmap(ruta + @"\aireador_ON_small.jpg"); break; } } else // se desactiva la casilla { switch (check.Text) {

199

case "Aireador 1": picBox1.Image = new System.Drawing.Bitmap(ruta break; case "Aireador 2": picBox2.Image = new System.Drawing.Bitmap(ruta + break; case "Aireador 3": picBox3.Image = new System.Drawing.Bitmap(ruta + break; case "Aireador 4": picBox4.Image = new System.Drawing.Bitmap(ruta + break; } } }

+ @"\aireador_OFF_small.jpg");

@"\aireador_OFF_small.jpg");

@"\aireador_OFF_small.jpg");

@"\aireador_OFF_small.jpg");

private void menuCerrar_Click(object sender, EventArgs e) { principal.Dispose(); this.Close(); } } }

Las imgenes que descargamos contienen el aireador en estado ON y OFF. Para que el dispositivo pueda cargarlas en tiempo de ejecucin debemos guardarla en los archivos de programa del SmartPhone o PDA, en este caso no tenemos la carpeta Bin/Debug.

Hasta ahora ya tenemos el cdigo para el Smartphone, pero nos falta sincronizarlo por medio de un puerto virtual Bluetooth y otra aplicacin de escritorio que no mande y reciba nuestros comandos. IMPORTANTE: Las imgenes deben ser de un tamao chico (menor de 8kbytes), de lo contrario se provocar un error por falta de memoria. Para probar este proyecto es necesario conectarse a una PC con Bluetooth y otra aplicacin en C# que mande los mensajes de alarma y reciba los comandos de activacin de los aireadores. Si su PC no tiene Bluetooth puede utilizar un Dongle como BlueSoleil para crear estos puertos COM virtuales. El sofware de Bluesoleil permite conectarse a cualquier dispositivo Bluetooth por medio de este panel:

200

En el administrador de dispositivos de la PC puede ver el ID de los puertos COM virtuales:

Cuando se asocia al SmartPhone debe aparecer el puerto COM que puede utilizar en la aplicacin de escritorio de C#.

Para mandar los mensajes podemos usar los ejemplos anteriores de comunicacin Rs232, solo agregamos un botn que mande un mensaje con la sintaxis que requiere nuestro SmartPhone para separar las alarmas. Puede crear un proyecto nuevo de windows forms o modificar un ejemplo anterior.

Al abrir el puerto nuestro botn para mandar alarma debe mandar los mensajes separados por un '+' y con la palabra -FINprivate void btnAlarma_Click(object sender, EventArgs e) { serialPort.Write("12:30 hrs + Sobrecalentamiento + Checar Bomba 1-5 }

-FIN-");

Finalmente tenemos un monitoreo y control inalmbrico en el rango de operacin de nuestro Dongle Bluetooth. 201

Con este tema damos por concluido nuestro curso. Gracias por su participacin Atte. Ing. Aaron Castro Bazua

Vous aimerez peut-être aussi