manual curso c net 3raparte

70
Autor: Aaron Castro Bazua Ejemplos con: Patrones de diseño por capas Manejo de puerto serial Control de puerto paralelo Comunicación con PIC s por RS232 Comunicación con PLC s Ejemplos con: Patrones de diseño por capas Manejo de puerto serial Control de puerto paralelo Comunicación con PIC s por RS232 Comunicación con PLC s

Upload: duarte-lopez-felipe

Post on 20-Jan-2016

168 views

Category:

Documents


39 download

TRANSCRIPT

Page 1: Manual Curso C NET 3raParte

Autor: Aaron Castro Bazua

Ejemplos con:

Patrones de diseño por capas

Manejo de puerto serial

Control de puerto paralelo

Comunicación con PIC s por RS232

Comunicación con PLC s

Ejemplos con:

Patrones de diseño por capas

Manejo de puerto serial

Control de puerto paralelo

Comunicación con PIC s por RS232

Comunicación con PLC s

Page 2: Manual Curso C NET 3raParte

Práctica: Chat por puerto serie entre 2 computadoras................................135

Práctica: Comunicación con Microcontrolador PIC 16F887 vía RS232 ......138

Programa para PIC: Comunicación serie bidireccional con C#.NET...........139

Práctica: Control de puerto paralelo...........................................................144

Patrones de diseño por capas: Datos, Negocio y Presentación..................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

Aplicación Cliente para monitorear PLC’s desde C#.NET...........................183

Práctica: Monitoreo y Control con Smartphone...........................................190

Manual 3ra parte

En esta parte del curso nos enfocamos al control de periféricos, la comunicación con otros dispositivos y

la organización de un proyecto por capas.

Temario

Page 3: Manual Curso C NET 3raParte

135

Práctica: 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.

También podemos probarlo cruzando los pines 2 y 3 de nuestro cable DB9 y observaremos como llega el mismo dato que enviamos.

Es básico contar con un puerto COM habilitado en el administrador de dispositivos de la PC, los convertidores USB-Serial son válidos.

5

4

3

2

1

5

4

3

2

1

9

8

7

6

9

8

7

6

ConectorDB9 PC1

ConectorDB9 PC2

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

ListBoxName: ListBoxChat

TextBoxName: txtMensajeLabelName: lblNick

SerialPortName: serialPortChat

CheckBoxName: chkPuertoSerie

TextBoxName: txtNick

ButtonName: btnEnviarEnabled: False

TextBoxName: txtConversacion

FormularioName: frmChat

Page 4: Manual Curso C NET 3raParte

136

El puerto serial lo configuramos con los siguientes datos:

En nuestro código declaramos un objeto string con ámbito para toda la clase.

usingusingusingusingusingusingusingusing

namespace

public partial class

stringpublic

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

ProyectoChatRS232{ : { datoSerial; frmChat() { InitializeComponent(); }

frmChat Form

Ahora en el evento DataReceived de nuestro puerto serie:

private void object

try

this new

catch

serialPortChat_DataReceived( sender, System.IO.Ports. e) { { serialPortChat.ReadTimeout = 2000; datoSerial = serialPortChat.ReadTo( ); .Invoke( (TextoRecibido)); } { .Show( ); serialPortChat.DiscardInBuffer(); } }}

SerialDataReceivedEventArgs

EventHandler

MessageBox

//intentamos leer el puerto

//espera 2 segundos por el dato//almacena toda la cadena de datos hasta que lee "-FIN-"

//ahora recorta los datos e invoca al manejador

//no llego la cadena completa, interceptamos el error

//borra los datos del buffer de entrada

"-FIN-"

"Error, no recibí el identificador final de la cadena -FIN-"

Creamos una función para manejar el evento TextoRecibido: (se debe teclear manualmente)

TextoRecibido( sender, e) { txtConversacion.Text = ; txtConversacion.AppendText(datoSerial); listBoxChat.Items.Add(datoSerial); }

private void object EventArgs

""

Page 5: Manual Curso C NET 3raParte

137

Finalmente en el CheckBox que habilita el puerto:

chkPuertoSerie_CheckedChanged( sender, e) { (chkPuertoSerie.Checked) { { (!serialPortChat.IsOpen) { serialPortChat.Open(); } .Enabled = ; chkPuertoSerie.Text = ; chkPuertoSerie.BackColor = .YellowGreen; } { .Show(

); } } { { (serialPortChat.IsOpen) { serialPortChat.Close(); chkPuertoSerie.BackColor = .Red; = ; } } { .Show( ); } } }

private void objectif

try

if

true

catch

else

try

if

false

catch

EventArgs

Color

MessageBox

Color

MessageBox

//si el puerto esta cerrado

//habilita el botón solo si esta abierto el puertobtnEnviar

btnEnviar.Enabled

"Puerto Abierto"

"Algo anda mal, no pude abrir el puerto COM"

"Algo anda mal no pude cerrar el COM"

Ahora podemos probar la aplicación:

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

En el evento Click del btnEnviar

private void object

string

btnEnviar_Click( sender, e) { mensaje = txtNick.Text + + txtMensaje.Text; serialPortChat.Write(mensaje + ); listBoxChat.Items.Add(mensaje.ToString()); }

EventArgs

" dice: ""-FIN-"

Page 6: Manual Curso C NET 3raParte

138

Práctica: Comunicación con Microcontrolador PIC 16f887 vía 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 análogo (0-5 volts) y 8 bits de salida TTL.

La manera de operar será mandando mensajes RS232 que soliciten información al PIC o le ordenen escribir datos, para este circuito ocupamos el siguiente material:

El diagrama es el siguiente:

Microswitch

16F887

VDDVDDVSSVSS

RA0/AN0/ULPWU/C12IN0-RA1/AN1/C12IN1-RA2/AN2/VREF-/CVREF/C2IN+RA3/AN3/VREF+/C1IN+RA4/T0CKI/C1OUTRA5/AN4/SS/C2OUTRA6/OSC2/CLKOUTRA7/OSC1/CLKIN

RE0/AN5/RDRE1/AN6/WRRE2/AN7/CSRE3/MCLR/Vpp/

RB0/AN12/INTRB1/AN10/C12IN3-

RB2/AN8RB3/AN9/PGM/C12IN2-

RB4/AN11RB5/AN13/T1GRB6/ICSPCLKRB7/ICSPDAT

RD0RD1RD2RD3RD4

RD5/P1BRD6/P1CRD7/P1D

RC0/T1OSO/T1CKIRC1/T1OSI/CCP2

RC2/P1A/CCP1RC3/SCK/SCLRC4/SDI/SDA

RC5/SDORC6/TX/CKRC7/RX/DT

+

+

5 Volts

Resistencias 330 ohm

Resistencias 1k ohm

Leds

3334353637383940

1920212227282930

1516171823242526

¡No ocupa oscilador!

323112

234567

1413

89

101

11

DISPLAY LCD

12345678

161514131211109

MA

X2

32

9

8

7

6

VccGndT1OUTR1INR1OUTT1INT2INR2OUT

C1+V+

C1-C2+C2-

V-T2OUT

R2IN

Capacitores 1 ufaradio

+

+

+

+

-

-

-

-

-

ConectorDB9

hembra

5 Volts

ENABLERSRW

D4D5D6D7

Entrada analógicacon potenciómetrode 10k ohm

5

4

3

2

1

Material:1 pic 16F887, 1 conector DB9 hembra,1 microswitch (8 polos), 1 potenciómetro 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, Multímetro.

Page 7: Manual Curso C NET 3raParte

Programa para PIC: Comunicación serie bidireccional con C#.NETCon este ejercicio el PIC envía el bit RE0/AN5 y un byte proveniente del microswitch a la aplicación de C#, también 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 aplicación en C#.NET que mande los datos debe agregar un Enter al final de la cadena.

139

Page 8: Manual Curso C NET 3raParte

140

Page 9: Manual Curso C NET 3raParte

Puede bajar los archivos fuente de este proyecto desde:

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:

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.

http://www.multitecnologia.com/ArchivosFuentePIC16f887CursoC.rar

Arrastramos los siguientes controles:

CheckBoxName: chkPuertoSerie

ListBoxName: listBoxDatosEntrada

NumericUpDownName: numStepByteMinimum: 0Maximum: 255

ButtonName: btnMandaByteEnabled: False

ButtonName: btnRecibeDatDigEnabled: False

ButtonName: btnRecibeAn0Enabled: False

LabelName: lblCanalAn0Text: RA0

FormularioName: frmTarjeta

SerialPortName: serialPort

TextBoxName: txtRecibeDatos

141

"escribe”

“Ra0"

"byteEnt"

: Cuando el PIC recibe esta cadena se queda esperando un número entre 0-255 con el que imprime los datos en el puerto B del PIC.

: Con este mensaje nos responde con un valor con la conversión a decimal del puerto analogico (0-5 volts),

: Esta cadena le dice al PIC que nos responda con los 8 bits de entrada.

la resolución es de 8 bits.

Page 10: Manual Curso C NET 3raParte

142

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

Este ejercicio es similar al Chat, también creamos un objeto string visible para toda la clase:

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

ProyectoPIC16f883{ : { datoSerial; frmTarjeta() { InitializeComponent(); }

usingusingusingusingusingusingusingusingnamespace

public partial class

stringpublic

frmTarjeta Form

En el CheckBox escribimos:

chkPuertoSerie_CheckedChanged( sender, e) { (chkPuertoSerie.Checked) { { (!serialPort.IsOpen) { serialPort.Open(); } btnMandaByte.Enabled = ; btnRecibeAn0.Enabled = ; btnRecibeDatDig.Enabled = ; chkPuertoSerie.Text = ; chkPuertoSerie.BackColor = .YellowGreen; } { .Show( ); } } { { (serialPort.IsOpen) { serialPort.Close(); chkPuertoSerie.BackColor = .Red; chkPuertoSerie.Text = ; btnMandaByte.Enabled = ; btnRecibeAn0.Enabled = ; btnRecibeDatDig.Enabled = ; } } { .Show( ); } }

private void object

iftry

if

truetrue

true

catch

elsetry

if

falsefalse

false

catch

EventArgs

Color

MessageBox

Color

MessageBox

//si el puerto esta cerrado

"Puerto Abierto"

"Algo anda mal con el puerto COM"

"Puerto Cerrado"

"Algo anda mal no pude cerrar el COM"

Page 11: Manual Curso C NET 3raParte

143

En el evento DataReceived del Puerto serie: serialPort_DataReceived( sender,

System.IO.Ports. e) {

}

De nuevo creamos una función que refleje los datos recibidos: TextoRecibido( sender, e) { txtRecibeDatos.Text = ; txtRecibeDatos.AppendText(datoSerial); analisisCadenas(datoSerial); }

Ahora otra nueva función para separar los datos: analisisCadenas( cadena) { cad;

(cadena.Contains( )) {

cad = cadena.Replace( , ); cad = cad.Replace( , ); lblCanalAn0.Text = + cad; } (cadena.Contains( )) { listBoxDatosEntrada.Items.Clear(); cad = cadena.Replace( , ); [] arreglocadena = cad.Split( ); ( st arreglocadena) { listBoxDatosEntrada.Items.Add(st.ToString()); } } }

private void object

private void object

private void string

string

if

if

stringforeach string in

SerialDataReceivedEventArgs

EventArgs

{ serialPort.ReadTimeout = 2000; datoSerial = serialPort.ReadTo( ); .Invoke( (TextoRecibido)); } { .Show( ); serialPort.DiscardInBuffer(); }

try

this new

catch

//intentamos leer el puerto

//espera 2 segundos por el dato//almacena toda la cadena de datos hasta que lee "Fin"

//ahora recorta los datos e invoca al manejador

//no llego la cadena completa, interceptamos el error

//borra los datos del buffer de entrada

"Fin"

"Error, no recibí el identificador final de la cadena: Fin"

EventHandler

MessageBox

// en esta función recibimos los datos para su análisis y reordenamiento.

//ahora llamamos a la función que extrae los datos// MessageBox.Show(datoSerial);

//cuando el PIC nos manda el dato análogo del Ra0 // le quitamos lo que no sirve

//El dato siempre llega de esta forma: “Ra0+ numero +”, // solo nos quedamos con: numero

//cuando nos llega la trama con los bits de entrada

//limpiamos todos los valores antes de actualizar

//con el Split cortamos cada parte de la cadena donde exista un '+'//con cada dato se forma un arreglo de cadenas

//ahora cada miembro del arreglo entra a los items del listbox

""

"Ra0+"

"Ra0+" """+" """Lectura RA0: "

"byteEnt"

"byteEnt" ""

'+'

Page 12: Manual Curso C NET 3raParte

144

Para cada uno de los botones en su evento Click escribimos: btnMandaByte_Click( sender, e)

{ serialPort.Write( + ( )13); serialPort.Write(numStepByte.Value.ToString() + ( )13); }

btnRecibeDatDig_Click( sender, e) { serialPort.Write( + ( )13); // }

btnRecibeAn0_Click( sender, e) { serialPort.Write( + ( )13);// }

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

private void object

char

char

private void object

char

private void object

char

EventArgs

EventArgs

EventArgs

// primero se manda la cadena "escribe" con el Enter para// avisar que viene un numero después, (char)13= Enter

// ahora envía el numero entre 0 y 255 del numStepByte

// 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

(char)13= Enter

//al mandar "Ra0" el PIC nos responde con el valor decimal de//la conversión del canal análogo a 8 bits

(char)13= Enter

"escribe"

"byteEnt"

"Ra0"

Práctica: Control de Puerto Paralelo

En este ejercicio vamos construir la interfaz electrónica 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 Protoboard1 Buffer 74LS5411 Microswitch4 resistencias de 1 kohm8 resistencias de 330 ohm8 leds comunes de 5mmCableado

Equipo necesario:1 fuente de voltaje de 5 volts DCPinzas, Multímetro.

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

Page 13: Manual Curso C NET 3raParte

145

IMPORTANTETrabajar con fuentes externas de voltaje y periféricos de la PC presenta un riesgo de quemar componentes de la tarjeta madre, se debe poner en común 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 provocaría un daño irreparable.

Como consideración inicial debemos respetar los colores en las líneas del protoboard:Azul = TierraRojo = Voltaje

Ahora implementamos el circuito:

Resistencias 330 ohm

Leds

Registro de Estatus

Registro de Control

Registro de Datos

S0 S1 S2 S3 S4 S5 S6 S7

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

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

D7 D6 D5 D4 D3 D2 D1 D0

C0 C1 C2 C3 C4 C5 C6 C7

Puerto Paralelo - Conector DB25

Diagrama del circuito

123456789

10

20191817161514131211

74

LS

54

1

Resistencias1 kohm

1

2

3

4

5

6

7

8

9

10

11

12

13

Microswitch

14

15

16

17

18

19

20

21

22

23

24

25

5 Volts

Fuente

- ++ -

OE

OE

Vcc

En nuestro conectortenemos el registro dedatos y estatus, falta el registro de control.

D0 D7Tierra

S3 S4 S5 S6 S7

EL S3 lo conectamosdirecto a tierra.Solo utilizamos un nible

15

El circuito lucirá de esta manera

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

{

Para los bits S0,S1,S2, no

tenemos acceso,dependiendo la

tarjeta madre estosvalores pueden ser:

000 ó 111

Page 14: Manual Curso C NET 3raParte

Antes de conectar el protoboard a la PC probamos los las entradas del buffer, observe que por defecto sus salidas están 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.También use su multímetro 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 rápidamente 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 dañado su tarjeta madre.

Cuando las tarjetas se dañan se debe a un choque de niveles de voltaje interno, en estos casos se queman integrados básicos 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 librería para funcionar: inpout32.dll

La descargamos desde:

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

www.multitecnologia.com/inpout32.rar

Con nuestra interfaz lista nos vamos a VisualStudio y creamos un nuevo proyecto de windows forms.

146

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 .

D7 D0Tierra

Buffer

Recuerde respetarlos colores del

protoboard:Línea azul: TierraLínea roja: Voltaje

Page 15: Manual Curso C NET 3raParte

147

Ahora arrastramos los siguientes controles:

Escribimos el código para los eventos de cada control mostrados: System; System.Collections.Generic; System.ComponentModel; System.Data; System.Drawing; System.Linq; System.Text; System.Windows.Forms; System.Runtime.InteropServices;

frmPuertoParalelo{ : { { [ ( , EntryPoint = )] Output( adress, value); [ ( , EntryPoint = )] Input( address); } frmPuertoParalelo() { InitializeComponent(); }

SBDato_Scroll( sender, e) {

.Output(888, SBDato.Value); lblDatos.Text = + SBDato.Value.ToString(); } btnLeerPuerto_Click( sender, e) { lblLecturaPuerto.Text = + .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.

usingusingusingusingusingusingusingusingusingnamespace

public partial class

public class

public static extern void int int

public static extern int int

public

private void object

private void object

//esta directiva es necesaria

//Creamos esta clase para importar la DLL y tener acceso a sus comandos

//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 dirección del registro de datos

//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 dirección del registro de Status

frmPuertoParalelo Form

PortAccess

DllImport

DllImport

ScrollEventArgs

PortAccess

EventArgs

PortAccess

"inpout32.dll" "Out32"

"inpout32.dll" "Inp32"

"Escritura: "

"Lectura: "

ButtonName: btnLeerPuerto

HSCrollBarName: SBDatoMinimum:0, Maximum: 255

LabelName: lblDatosText: Escritura:

LabelName: lblLecturaPuertoText: Lectura:

FormularioName: frmPuertoParalelo

Page 16: Manual Curso C NET 3raParte

148

ANEXOS

Código de colorespara resistencias

4N32

Page 17: Manual Curso C NET 3raParte

149

Práctica:Patrones de diseño por capas:

Datos, Negocio y Presentación.

¿Qué son? ¿Para que sirven?

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 diseño de software mediante las capas de Datos, Negocio y Presentación.

Estos patrones de diseño son metodologías de organización 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 metodología también 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 ingeniería de software.

La definición técnica de las capas es:

Capa de presentación: Es la que ve el usuario (también se la denomina "capa de usuario"), presenta el sistema al usuario, le comunica la información y captura la información del usuario en un mínimo de proceso (realiza un filtrado previo para comprobar que no hay errores de formato). También es conocida como interfaz gráfica y debe tener la característica de ser "amigable" (entendible y fácil 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 envían las respuestas tras el proceso. Se denomina capa de negocio (e incluso de lógica del negocio) porque es aquí donde se establecen todas las reglas que deben cumplirse. Esta capa se comunica con la capa de presentación, 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. También se consideran aquí los programas de aplicación.

Capa de datos: Es donde residen los datos y es la encargada de acceder a los mismos. Está formada por uno o más gestores de bases de datos que realizan todo el almacenamiento de datos, reciben solicitudes de almacenamiento o recuperación de información desde la capa de negocio.

CAPA DE DATOS

CAPA DE NEGOCIO

CAPA DE PRESENTACIÓN

Page 18: Manual Curso C NET 3raParte

150

Ahora en “lenguaje terrícola”…capa de presentación

capa de negocio t

capa de datos

La son los formularios, botones y controles con que interactúa el usuario, el desarrollador que diseña esta capa debe preocuparse por facilitar la navegación del usuario, hacer atractivos los gráficos, presentar la información de manera rápida y eficiente, etc.

Esta metodología permite que el equipo de trabajo avance en paralelo, es conveniente que una persona se encargue de interactuar con el cliente en cuestiones de diseño y la otra parte del equipo se enfoque a la parte lógica y de datos.

iende a confundir en cuanto a su definición, por lo regular asociamos este término con establecimientos comerciales, en este caso “negocio” se refiere a intercambio de información, también se le llama capa lógica 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 es la manera de separar los archivos físicos como bases de datos, documentos xml o txt que almacenen la información, para quien diseña 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 conexión al servidor.

En resumen, las capas nos facilitan el diseño de cualquier sistema y permiten reutilizar código 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, lógicamente la capa de presentación 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 presentación no tiene que ver con la capa datos, esto se refleja en las dependencias del proyecto.

La

Comenzamos abriendo un proyecto del tipo Solution en VisualStudio, elegimos solución en blanco.

En esta solución vamos a agrupar nuestras 3 capas.

Lo nombramos SolucionMuestrasSensor

En el explorador de soluciones nos aparece de esta manera:

Page 19: Manual Curso C NET 3raParte

151

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.

Page 20: Manual Curso C NET 3raParte

152

Nos queda:

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

Ahora tenemos la estructura básica de la solución:

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

Page 21: Manual Curso C NET 3raParte

Nos aparece esta ventana donde elegimos la capa de negocio en la pestaña de proyectos:

Ahora nuestra capa de presentación 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

Page 22: Manual Curso C NET 3raParte

154

Por lógica la capa de datos no depende de otras capas, es decir no tiene por que heredar de otras bibliotecas, esto crearía 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:

Damos click derecho agregar elemento existente.

www.multitecnologia.com/CapaNegocioSensores.rar

Seleccionamos las tres y nos queda de esta forma:

Ahora para la capa de datos descargamos este archivo:

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.

www.multitecnologia.com/CapaDatosSensores.rar

xx

Page 23: Manual Curso C NET 3raParte

Nuestra capa de datos nos queda de esta forma:

Aún 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 pestaña 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

Page 24: Manual Curso C NET 3raParte

156

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 también deben tener el mismo nombre de proyecto y namespace.

Volvemos a la capa de presentación y en nuestro formulario arrastramos los siguientes controles:

FormName: frmSensoresText. Generación de archivos para muestras

ButtonName: btnCrearMuestra,Text: Crear Muestra

ButtonName: btnGrabarTxt, Text: Grabar Muestras en Texto

ButtonName: btnGrabarXml, Text: Grabar Muestras en XML

ButtonName: btnGrabarDB, Text: Grabar Muestras en Base de Datos

saveFileDialogName: saveFileDialog

DataGridViewName: dGridViewMuestrasColumns: Colección Hora, Lectura

Page 25: Manual Curso C NET 3raParte

157

En nuestro código del formulario agregamos la directiva:

CapaNegocioSensores;

Aparte dos instancias de Sensor y Random:

nuestroSensor = (); lecturaSensor = ();

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

CapaPresentacionSensores{ : { nuestroSensor = (); lecturaSensor = (); frmSensores() { InitializeComponent(); }

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

frmSensores_Load( sender, e) { nuestroSensor.Nombre = ; nuestroSensor.Tag = ; }

Para el btnCrearMuestra:

btnCrearMuestra_Click( sender, e) { m = (); m.hora = .Now.ToString(); m.lectura = lecturaSensor.Next(255); nuestroSensor.agregarMuestra(m); dGridViewMuestras.Rows.Insert(0, m.hora, m.lectura); }

using

new

new

usingusingusingusingusingusingusingusingusingnamespace

public partial class

new

newpublic

private void object

private void object

new

Sensor Sensor

Random Random

frmSensores Form

Sensor Sensor

Random Random

EventArgs

EventArgs

Muestra MuestraDateTime

//con este objeto random obtenemos valores aleatorios en la lectura

//importamos la capa con las clases de negocio

//creamos nuestro objeto sensor con ámbito para toda la clase

//con este objeto random obtenemos valores aleatorios en la lectura

//para no estar reescribiendo el nombre y tag en el botón muestra//le damos sus propiedades al objeto sensor desde aqui

//creamos una instancia de muestra

//guardamos la hora//guardamos un entero aleatorio

//agrega una muestra a nuestro objeto sensor

//Imprime la muestra en el datagrid

"SensorTemperatura_5B""PLC5-DB1"

Page 26: Manual Curso C NET 3raParte

158

En los otros botones el código es el siguiente: btnGrabarTxt_Click( sender, e) { saveFileDialog.DefaultExt = ; saveFileDialog.Filter = ; saveFileDialog.ShowDialog(); ruta = saveFileDialog.FileName; { nuestroSensor.EscribirTxt(ruta); .Show( ); } ( ee) { .Show(ee.Message.ToString()); } } btnGrabarXml_Click( sender, e) { saveFileDialog.DefaultExt = ; saveFileDialog.Filter = ; saveFileDialog.ShowDialog(); ruta = saveFileDialog.FileName; { nuestroSensor.EscribirXml(ruta); .Show( ); } ( ee) { .Show(ee.Message.ToString()); } } btnGrabarDB_Click( sender, e) { saveFileDialog.DefaultExt = ; saveFileDialog.Filter = ; saveFileDialog.ShowDialog(); ruta = saveFileDialog.FileName; { nuestroSensor.EscribirBaseDatos(ruta); .Show( ); } ( ee) { .Show(ee.Message.ToString()); } }

private void object

stringtry

catch

private void object

stringtry

catch

private void object

stringtry

catch

EventArgs

MessageBox

ExceptionMessageBox

EventArgs

MessageBox

ExceptionMessageBox

EventArgs

MessageBox

Exception

MessageBox

"txt""txt files (*.txt)|*.txt"

"Confirmado, ya se guardo el archivo de texto"

"xml""xml files (*.xml)|*.xml"

"Confirmado, ya se guardo el archivo .XML"

"mdb""mdb files (*.mdb)|*.mdb"

"Confirmado, ya se guardo la base de datos Access"

//mandamos la ruta a la capa de negocio//operación de riesgo

//obtenemos cualquier mensaje de error

//mandamos la ruta a la capa de negocio//operación de riesgo

//obtenemos cualquier mensaje de error

//mandamos la ruta a la capa de negocio//operación de riesgo

//obtenemos cualquier mensaje de error

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

Page 27: Manual Curso C NET 3raParte

159

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

ANEXOSClases Capa Negocio

Clase Muestra.csusingusingusingusingnamespace

public class

stringpublic string

getreturn

setvalue

System; System.Collections.Generic; System.Linq; System.Text;

CapaNegocioSensores{ { _Hora = ; hora { { _Hora; } {_Hora = ; } }

Muestra

//Lo inicializamos con la cadena vacio para diferenciar cuando//el objeto aún no recibe su datoprivate "vacio"

Finalmente obtenemos tres archivos de respaldo en diferentes formatos.

Page 28: Manual Curso C NET 3raParte

//lo inicializamos con -1 para simbolizar que no ha sido llenado.

//_Lectura es entero por que las conversiones A-D siempre vienen en enteros

//la propiedad indice es privada,solo con el método asignaIndice se puede modificar

//no se ocupa un set por que no deseamos que se escriba este valor directamente

//este método permite recibir la propiedad .count del arraylist

//Nos permite usar un ArrayList

//con este método agregamos una instancia de muestra a nuestro ArrayList

//le mandamos el indice como propiedad a la muestra

//este método nos trae todo el arreglo completo de muestras

//este método recibe el índice del arreglo y regresa la muestra con ese indice

//busca en todas las muestras de esta colección

_Lectura = -1; lectura { { _Lectura; } {_Lectura = ; } } _Indice; indice { { _Indice; } } asignaIndice( indice) { _Indice = indice; } }}

System; System.Collections.Generic; System.Linq; System.Text; System.Collections;

CapaNegocioSensores{ : { agregarMuestra( m) { m.asignaIndice( .Count); .Add(m); } traerMuestras() { ; } traeMuestra( Indice) { ( m ) { (m.indice == Indice) { m; } } ; } }}

private int

public intget

return

setvalue

private intpublic int

getreturn

public void int

usingusingusingusingusingnamespace

public class

public void

thisthis

public

return this

public int

foreach in this

if

return

return null

Clase ArrayListMuestras.cs

ArrayListMuestras ArrayList

Muestra

ArrayListMuestras

Muestra

Muestra

160

Page 29: Manual Curso C NET 3raParte

Clase Sensor.csusingusingusingusingusingusingnamespace

public class

private stringpublic string

getreturn

setvalue

private stringpublic string

getreturn

setvalue

private

newnew

foreach in this

return

System; System.Collections.Generic; System.Linq; System.Text; System.Data; CapaDatosSensores;

CapaNegocioSensores{ : { _Nombre= ; Nombre { { _Nombre; } { _Nombre = ; } } _Tag = ; Tag { { _Tag; } { _Tag = ; } } ConversionDataset() { tablaMuestras = (); tabla = (); tablaMuestras.Tables.Add(tabla); tablaMuestras.Tables[0].TableName = ; tablaMuestras.Tables[0].Columns.Add( ); tablaMuestras.Tables[0].Columns.Add( ); tablaMuestras.Tables[0].Columns.Add( ); ( m ) { linea = tablaMuestras.Tables[0].NewRow(); linea[0] = m.indice.ToString(); linea[1] = m.hora; linea[2] = m.lectura.ToString(); tablaMuestras.Tables[0].Rows.Add(linea); } tablaMuestras; }

//nos permite instanciar DataSet

//recordemos que todo el modelado y la lógica 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

//valor inicial

//valor inicial

//esta función nos convierte nuestro arraylistMuestras (Indice, Hora, Lectura) en//un DataSet(tabla) que es fácil de mandar a otra capa con los datos ordenados

//agregamos las tres columnas de nuestra tabla: Indice, Hora, Lectura//nombre de la tabla

//columna 0 de la línea//columna 1 de la línea//columna 2 de la línea

Sensor ArrayListMuestras

DataSet

DataSet DataSetDataTable DataTable

Muestra

DataRow

"Sin Nombre"

"Sin Tag"

"Muestras""Indice""Hora""Lectura"

161

Page 30: Manual Curso C NET 3raParte

//estas funciónes son el intermediario entre la presentación y capa de datos//recibimos la ruta y debemos mandar la colección de muestras en un dataSet

//creamos nuestra instancia de la capa de datos y mandamos//la ruta al constructor como valor inicial

//si nos llega una ruta válida

//ahora convertimos nuestra tabla de muestras en un Dataset

// mandamos todo a la capa de datos para que se convierta en archivo txt

//si nos llega una ruta válida//nos convierte el arraylist en Dataset

// mandamos todo a la capa de datos para que se convierta en archivo XML

//si nos llega una ruta válida//nos convierte el arraylist en Dataset

// mandamos todo a la capa de datos para que se convierta en archivo MDB

EscribirTxt( ruta) { DatosTxt = (); (ruta != ) {

TMuestras = ConversionDataset(); DatosTxt.EscribeTxt(ruta, .Nombre, .Tag, TMuestras); } { ( ); } }

EscribirXml( ruta) { DatosXml = (); (ruta != ) {

TMuestras = ConversionDataset(); DatosXml.EscribeXml(ruta, .Nombre, .Tag, TMuestras); } {

( ); } } EscribirBaseDatos( ruta) { DatosBD = (); (ruta != ) {

TMuestras = ConversionDataset(); DatosBD.EscribeBD(ruta, .Nombre, .Tag, TMuestras); } { ( ); } } }}

public void string

newif null

this this

elsethrow new

public void string

newif null

this this

else

throw new

public void string

newif null

this this

elsethrow new

EscrituraMuestrasTxt EscrituraMuestrasTxt

DataSet

ArgumentException

EscrituraMuestrasXml EscrituraMuestrasXml

DataSet

ArgumentException

EscrituraMuestrasBaseDatos EscrituraMuestrasBaseDatos

DataSet

ArgumentException

"Existe un error en la colección o la ruta"

"Existe un error en la colección o la ruta"

"Existe un error en la colección o la ruta"

Siguiente...Clases Capa Datos

162

Page 31: Manual Curso C NET 3raParte

Clase EscrituraMuestrasBaseDatos.csusingusingusingusingusingusingusingusingnamespace

public class

public void string string string

new

nullstring

public void string string

string

string

newnew

System; System.Collections.Generic; System.ComponentModel; System.Data; System.Text; System.IO; ADOX; System.Data.OleDb;

CapaDatosSensores{ { EscribeBD( ruta, nombreSensor, tagSensor,

Muestras) { ADOX. cat = ADOX. (); cat.Create( + ruta + + ); cat = ; nombreTabla = nombreSensor; creaTabla(ruta,nombreTabla); insertaDatos(ruta,nombreTabla, Muestras); } creaTabla( ruta, nombreTabla) { conex = +

+ ruta + ; con; crear = + nombreTabla +

; con = (conex); cmd = (crear, con); con.Open(); cmd.ExecuteNonQuery(); con.Close(); }

}

//con esta clase convertimos nuestro Dataset en una Base de datos Access

//creamos las columnas de la tabla//insertamos los valores del Dataset

//ahora nos conectamos para insertar las columnas con sus tipos de datos

//se ejecuta la consulta //cerramos conexión

EscrituraMuestrasBaseDatos

DataSet

CatalogClass CatalogClass

OleDbConnection

OleDbConnectionOleDbCommand OleDbCommand

"Provider=Microsoft.Jet.OLEDB.4.0; Data Source=" ";""Jet OLEDB:Engine Type=5"

"Provider = Microsoft.Jet.OLEDB.4.0;" @"Data Source = " ";"

"CREATE TABLE " " (Indice INT, Hora VARCHAR(50), Lectura INT);"

public void string string

string

stringnew

for int

intstringint

new

insertaDatos( ruta, nombreTabla, Muestras) { conex = +

+ ruta + ; con; inserta; con = (conex); con.Open(); ( i = 0; i < Muestras.Tables[0].Rows.Count; i++) { ID = .ToInt32(Muestras.Tables[0].Rows[i][0]); Hora = Muestras.Tables[0].Rows[i][1].ToString(); Lectura = .ToInt32( Muestras.Tables[0].Rows[i][2]); inserta = + nombreTabla + + ID +

+ Hora + + Lectura + ; cmd = (inserta, con); cmd.ExecuteNonQuery(); } con.Close(); } }}

DataSet

OleDbConnection

OleDbConnection

Convert

Convert

OleDbCommand OleDbCommand

// nos conectamos para insertar las columnas con sus tipos de datos

//abrimos la conexión con la base de datos //con un ciclo vamos insertando cada línea de la tabla

//líneas

//se ejecuta la consulta

//cerramos conexión

"Provider = Microsoft.Jet.OLEDB.4.0;" @"Data Source = “ ";"

"INSERT INTO " " VALUES (" ",'” "'," ")"

163

Page 32: Manual Curso C NET 3raParte

Clase EscrituraMuestrasTxt.cs

System; System.Collections.Generic; System.Linq; System.Text; System.Collections; System.Data; System.IO;

CapaDatosSensores{ {

EscribeTxt( ruta, nombreSensor, tagSensor, Muestras)

{ [] texto; texto = [Muestras.Tables[0].Rows.Count + 1]; texto[0] = + nombreSensor + + tagSensor +

; ( i = 0; i < Muestras.Tables[0].Columns.Count; i++) { texto[0] += Muestras.Tables[0].Columns[i].ColumnName + ; } linea; ( i = 0; i < Muestras.Tables[0].Rows.Count; i++) { linea = .Empty; ( j = 0; j < Muestras.Tables[0].Columns.Count; j++) { linea += Muestras.Tables[0].Rows[i][j].ToString() + ; } texto[i + 1] = linea; } { .WriteAllLines(ruta, texto); } { (

); } }

}}

usingusingusingusingusingusingusingnamespace

public class

public void string string string

new

for int

for int

for int

try

catch

throw new

//Con esta Clase convertimos un DataSet de las Muestras en un archivo TXT

//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

//en este arreglo almacenamos toda la tabla

//Rellenamos la cabecera del archivo

//creamos las tres columnas con el encabezado: Indice, Hora, Lectura

//Rellenamos los valores de la tabla separando por líneas y comillas

//líneas

//limpiamos la línea para reutilizarla//Columnas

//se agrega al arreglo

//Creamos el archivo

EscrituraMuestrasTxt

DataSet

StringString

String

String

File

ArgumentException

"Sensor: " " Tag: " " Estructura: "

";"

";"

"No pude crear el archivo, esta vacío el DataSet"

164

Page 33: Manual Curso C NET 3raParte

Clase EscrituraMuestrasXml.cs

System; System.Collections.Generic; System.Linq; System.Text; System.Data; System.Xml;

CapaDatosSensores{ { EscribeXml( ruta, nombreSensor, tagSensor,

Muestras) { Muestras.EnforceConstraints = ; Muestras.DataSetName = ;

xmldoc = (Muestras); NodoRaiz = xmldoc.FirstChild; nuevoNodo = xmldoc.CreateNode( .Element, , ); AtributoNombre = xmldoc.CreateAttribute( ); AtributoNombre.Value = nombreSensor; AtributoTag = xmldoc.CreateAttribute( ); AtributoTag.Value = tagSensor; nuevoNodo.Attributes.Append(AtributoNombre); nuevoNodo.Attributes.Append(AtributoTag);

NodoRaiz.AppendChild(nuevoNodo); xmldoc.Save(ruta ); } }}

usingusingusingusingusingusingnamespace

public class

public void string string string

false

new

null

//En esta clase convertimos nuestro dataset en un archivo XML en forma de árbol

//eliminamos las reglas del Dataset para crear el xml

//cambiamos su nombre antes de mandarlo al XML

//grabamos la tabla simplemente asignando el dataset, // C# se encarga de acomodar los datos en forma de árbol XML

//localizamos la raíz del XML //creamos un nuevo nodo para los datos que faltan: Nombre, Tag

//creamos un par de atributos con el nombre y tag que nos manda la capa de negocio

//ahora agregamos esos atributos al nodo recién creado

//finalmente situamos ese nuevo nodo en la raíz

//guardamos nuestro documento

EscrituraMuestrasXml

DataSet

XmlDataDocument XmlDataDocumentXmlNode

XmlNode XmlNodeType

XmlAttribute

XmlAttribute

"MuestrasSensor"

"Sensor"

"Nombre"

"Tag"

En este ejercicio practicamos el concepto de patrones de diseño por capas y la generación

de archivos a partir de un objeto, es importante que aplique esta metodología para sacar real

provecho a C#.NET.

165

Page 34: Manual Curso C NET 3raParte

MONITOREO DE PLC'S POR MEDIO DE APLICACIONES EN C#.NETEn esta práctica 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 ControlEl estándar OPC permite la comunicación 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 estándar OPC es regulado por la fundación ( ), su objetivo es facilitar el uso de esta tecnología y proporciona código fuente para que diferentes fabricantes implementen sus servidores y clientes OPC.

http://www.opcfoundation.org/

En nuestro curso vamos trabajar con uno de los fabricantes más populares Kepware.

KepWare es una empresa especializada en conectividad para equipos industriales que maneja las marcas y modelos mas populares y los pone a disposición de cualquier aplicación cliente como LabView, Wonderware o en nuestro caso C#.NET.

Utilizaremos como intermediario una versión demo del servidor KepServer, solo vamos a configurar la conexión con los PLC y KepServer se encargará de mandar los datos a nuestra aplicación cliente en C#.NET

Empezamos entrando a la página de KepWare.

Para descargar la versión 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.

http://www.kepware.com/

166

En caso de ocupar más 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.

Page 35: Manual Curso C NET 3raParte

167

Una vez descargados comenzamos la instalación 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 están incluidas en la instalación.

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 simulación activada posiblemente vea más 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.

Page 36: Manual Curso C NET 3raParte

168

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 aplicación 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 comunicación 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.

Configurando un PLC KOYO DL06 de Automation Direct

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

Page 37: Manual Curso C NET 3raParte

169

En el dispositivo elegimos: Not Sure Protocolo: K Sequence

Ahora le damos un nombre a la conexión. Nos queda…

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

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

Cuando se crea con éxito la conexión, 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.

Vamos a insertar 3 contactos normalmente abiertos con nuestras entradas digitales y asignaremos la salida física 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

Page 38: Manual Curso C NET 3raParte

170

Ahora se muestran los elementos en la parte inferior.

Vamos a arrastrar 3 contactos normalmente abiertos: F2Nos aparece esta ventana:

Damos click en la lupa:

Por lo pronto solo Le asignamos su dirección (X0) y nickname (encendido)

Nos queda…

Repetimos esta operación para X1 y X2 con los nicknames: confirmación y accionamiento.

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

Con F4 nos aparece el catalogo…

Elegimos Standard Coil: OUT.

Page 39: Manual Curso C NET 3raParte

171

Nos aparece de esta manera pero falta la dirección física, 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 línea.

Con F5 asignamos un comando END

Finalmente todo el diagrama queda de esta forma:

Con el botón 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 activación 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 documentación de los elementos.

Page 40: Manual Curso C NET 3raParte

172

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 características anteriores.

Nos queda:

Ahora agregamos el dispositivo, damos un nombre relativo a la Marca.

Elegimos nuestro modelo:

Page 41: Manual Curso C NET 3raParte

173

Mantenemos el ID=1 Elegimos los parámetros propuestos:

Importamos nuestro archivo csv generado anteriormente con DirectSoft32.Continuamos hasta finalizar la conexión:

Al principio no aparece nada…

Damos click derecho en el dispositivo y en la ventana elegimos la pestaña “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.

Page 42: Manual Curso C NET 3raParte

174

Configurando un PLC Siemens S7-300En esta práctica vamos a configurar un PLC Siemens S7-300 CPU 315F -2PN/DP con dos módulos de entradas digitales y análogas: DI16/DO16xDC24V, A14/A02 x8BIT.

La característica 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 vacío.

Agregamos un nuevo objeto S7-300 Ahora damos Click en Hardware

Nos aparece otro programa de nombre HWConfig, aquí con f igu ramos todo e l hardware para que coincida con nuestro dispositivo real.

Page 43: Manual Curso C NET 3raParte

175

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

En caso de que nuestra versión 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 día todo el entorno de programación.

Con nuestro software actualizado podemos construir nuestro sistema.

En la localidad 1 agregamos una fuente de poder, primero seleccionamos el 1 y después la fuente.

Page 44: Manual Curso C NET 3raParte

Ahora seleccionamos el 2 y elegimos el CPU 315-2FJ14-0AB0, posiblemente aparezca la ventana de configuración IP que se muestra, si tenemos el PLC conectado directamente a la PC (sin router) podemos dejar la configuración mostrada con la IP del PLC 192.168.0.1, por lógica 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.

176

Continuando con nuestro bastidor nos saltamos hasta el slot 4 dejando el 3 vacío 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

Page 45: Manual Curso C NET 3raParte

177

Guardamos y compilamos nuestro sistema.

Para probar la sincronía con nuestro PLC nos vamos al menú y elegimos:

Elegimos la primera opción...

Ahora nos pide una confirmación de la IP del PLC que vamos a asignar.

Cerramos el HWConfig y regresamos al Simatic Manager.

En el nodo de programa elegimos Bloques:

Page 46: Manual Curso C NET 3raParte

178

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 recién configurado.

Seleccionando el primer segmento agregamos un contacto normalmente abierto:

En la dirección elegimos una entrada en el registro 1 bit 0

Page 47: Manual Curso C NET 3raParte

Asignamos la salid: Byte 0, bit 0

Guardamos el proyecto: Lo cargamos al PLC

179

Ahora elegimos una salida:

Si no existen problemas de comunicación 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 fácilmente.

Page 48: Manual Curso C NET 3raParte

180

En el adaptador de red podemos elegir “Default” y en la siguiente ventana aceptamos la opción predeterminada.

Finalizamos la conexión...

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

Damos Click en agregar dispositivos y asignamos un nombre relacionado con el área que se va a automatizar o la marca del equipo:

Ahora elegimos nuestro PLC S7-300

En esta parte es básico elegir la dirección IP del PLC que se le asigno desde Simatic al momento de configurar el CPU.

Page 49: Manual Curso C NET 3raParte

181

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 instalación 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.

Page 50: Manual Curso C NET 3raParte

Para asignar cada Tag no es necesario importar una biblioteca de símbolos, solo escribimos la dirección del PLC: I1.0

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 conexión, checar el cableado y que el PLC este operando.

Si desconocemos la localidad del Tag la podemos obtener de la tabla de símbolos del programa HWConfig (Simatic).

182

Page 51: Manual Curso C NET 3raParte

183

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

IMPORTANTE:Observe que no fue necesario programar el PLC para tener acceso a sus datos.

Para una entrada analógica solo escribimos la dirección del Tag (IW276) para tener acceso al dato tipo WORD.

Si no conocemos el tipo de dato elegimos Default.

Con KepServer manejando nuestros Tags y comunicándose con los dispositivos ya estamos listos

para programar el cliente en 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 algún Tag cambie su valor.

Aplicación Cliente para monitorear PLC’s desde C#.NET

Page 52: Manual Curso C NET 3raParte

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 derechosobre el cuadro deherramientas y elegirelementos.

Elegimos examinar y buscamos el archivo Kepware.ClientAce.DA_Junction.dll en

archivos de programa/Kepware Technologies

Al darle click y cerrar estasventanas nos debe apareceren el cuadro de herramientas

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

Se debe ver de esta forma, esta librería no es un componente del cuadro de herramientas.

184

Page 53: Manual Curso C NET 3raParte

En nuestro frmCliente arrastramos y configuramos los siguientes controles:

DataGridViewName:dGridTagsColumns: 1 columnacon Name: DirecciónTagHeaderText: Dirección Tag

ButtonName: btnRegistrarTag, Enabled: False, Backcolor: Red

TextBoxName: txtTag, Enabled: False

LabelName: lblTags, Text: Escriba la dirección del Tag

CheckBoxName: chkConectarse, Text: Conectarse a KepServer, Backcolor: Red

Formulario Name: frmCliente, Text: Cliente para KepServer

185

Agregamos el componente clientAceDA_Junction1 alformulario y conservamossu nombre.

Agregamos una columnaal dGridTags como se muestra.

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

Page 54: Manual Curso C NET 3raParte

186

El código:Agregamos estas directivas e instancias.

System; System.Collections.Generic; System.ComponentModel; System.Data; System.Drawing; System.Linq; System.Text; System.Windows.Forms; Kepware.ClientAce.OpcDaClient; Kepware.ClientAce.DA_Junction;

PruebasKepserver{ : { daServerMgt = (); [] itemIdent = [10]; activeServerSubscriptionHandle; numTags = 0;

frmCliente() { InitializeComponent(); daServerMgt.DataChanged += . (DataChanged);

}

En nuestro CheckBox “chkConectarse” chkConectarse_CheckedChanged( sender, e) { (chkConectarse.Checked) { url = ; clientHandle = 1; cInfo = (); cInfo.LocalId = ; cInfo.KeepAliveTime = 1000; cInfo.RetryAfterConnectionError = ; cInfo.RetryInitialConnection = ; connectFailed; { daServerMgt.Connect(url, clientHandle, cInfo, connectFailed); CreaInstancias();

chkConectarse.BackColor = .GreenYellow; btnRegistrarTag.BackColor = .GreenYellow; chkConectarse.Text = ; btnRegistrarTag.Enabled = ; txtTag.Enabled = ; dGridTags.Enabled = ; } ( ex) { connectFailed = ; .Show(ex.ToString()); } }

usingusingusingusingusingusingusingusingusingusingnamespace

public partial class

newnew

intbyte

public

new

private void object

if

stringint

new

truetrue

booltry

ref out

truetrue

true

catch

true

frmCliente Form

DaServerMgt DaServerMgtItemIdentifier ItemIdentifier

DaServerMgt DataChangedEventHandler

EventArgs

ConnectInfo ConnectInfo

ColorColor

Exception

MessageBox

//Instancia del Servidor//capacidad para 10 Tags

//nos sirve para suscribir Tags//contador para crear un ID con cada nuevo Tag

//registramos el evento Datachanged

//Si nuestro servidor es la versión 5 y esta operando usamos esta cadena

//Intentamos conectarnos a KepServer

//crea 10 instancias para tags con valores vacíos//como no salto la excepción cambiamos los gráficos a verde

//y habilitamos los controles

//se registro un error

"opcda:///Kepware.KEPServerEX.V5"

"MiConexion"

"Conectado"

Debe estar escrito exactamente igual, observe que son 3 diagonales ///

Page 55: Manual Curso C NET 3raParte

187

{ daServerMgt.Disconnect(); btnRegistrarTag.Enabled = ; txtTag.Enabled = ; dGridTags.Enabled = ; chkConectarse.Text = ; txtTag.Text = ; chkConectarse.BackColor = .Red; btnRegistrarTag.BackColor = .Red; } }

Creamos una función para llenar de manera temporal el arreglo para las 10 Tags y evitar errores posteriores.

CreaInstancias() { ( x = 0; x <= 9; x++) { itemIdent[x] = (); itemIdent[x].ItemName = ; itemIdent[x].ClientHandle = x; itemIdent[x].DataType = ; } }

Ahora otra función para suscribir los datos desde nuestro formulario.

SuscribirDatos( nombreTag) { (numTags < 10) { txtTag.Text = ; clientSubscriptionHandle = 1; active = ; updateRate = 1000; deadBand = 0.0f; itemIdent[numTags].ItemName = nombreTag; revisedUpdateRate; { daServerMgt.Subscribe(clientSubscriptionHandle, active, updateRate, revisedUpdateRate, deadBand, itemIdent, activeServerSubscriptionHandle); (itemIdent[numTags].ResultID.Succeeded == ) { .Show( + itemIdent[ ].ItemName.ToString()); } { dGridTags.Rows.Insert(0, itemIdent[numTags].ItemName.ToString()); numTags++; } }

else

falsefalse

false

private void

for int

new

null

private void string

if

intbool trueintfloat

inttry

out refout

if false

else

//nos desconectamos del servidor

//cambiamos los gráficos para simbolizar la desconexión

//esta función crea 10 instancias vacías 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

//no importa si se registra o no, el texto del Tag se borra//parámetros de la conexión

//actualizamos el nombre del Tag

//este método da de alta el grupo de Tags

//si llega a este punto el registro fue exitoso

//incrementamos el identificador del arreglo

"Conectarse a KepServer"""

""

""

"Falla: "

ColorColor

ItemIdentifier

MessageBox numTags

Page 56: Manual Curso C NET 3raParte

( ex) { .Show( + ex.Message.ToString()); } } { .Show( ); } }

Creamos la función DataChanged que actúa como interrupción cada vez que algun Tag registrado sufre algún cambio.

DataChanged( clientSubscription, allQualitiesGood, noErrors, [] itemValues) { { ( itemValue itemValues) { (itemValue.ResultID.Succeeded) { ventana = ();

ventana.parametrosTag( itemValue.ClientHandle.ToString(), itemValue.Value.ToString(), itemValue.Quality.Name.ToString(), itemValue.TimeStamp.ToString()); ventana.Show(); } } } ( ex) { .Show( , ex.ToString()); } }

Finalmente en el botón para registrar los Tags: btnRegistrarTag_Click( sender, e)

{ (txtTag.Text != ) { validaSuscribir = ; ( x = 0; x <= 9; x++) { (txtTag.Text == itemIdent[x].ItemName) { validaSuscribir = ; .Show( ); ; } } (validaSuscribir) {SuscribirDatos(txtTag.Text); } } { .Show( ); } }

catch

else

private void int bool bool

try

foreach in

if

new

catch

private void object

if

bool truefor int

if

false

break

if

else

Exception

MessageBox

MessageBox

ItemValueCallback

ItemValueCallback

frmVentanaDispositivo frmVentanaDispositivo

ExceptionMessageBox

EventArgs

MessageBox

MessageBox

"Fallo: "

"solo podemos registrar 10 Tags"

"Ocurrió un error, la razón es ... "

""

"este Tag ya esta dado de alta"

"Falta el nombre del Tag"

//si el numero de Tags no pasa de 10

//Este método se dispara ante cualquier cambio en los tags registrados

//busca en todos los tags registrados para encontrar cual tiene un cambio

//creamos una instancia del formulario para la ventana del Tag

//mandamos 4 parámetros al método publico en la clase frmVentanaDispositivo();//public void parametrosTag(string direccion,string valor,

//string calidad, string tiempo)

//Mandamos como parámetro el nombre del tag

//bandera para validar suscripción

//buscamos entre todos los Tags para ver si ya esta dado de alta

//no va a permitir que se suscriba

//si no encontro algún valor igual nos permite registrarlo

188

Page 57: Manual Curso C NET 3raParte

Ahora en el otro formulario arrastramos un control Label:

En su código agregamos un método público que nos permita mandarle datos desde frmCliente. System; System.Collections.Generic; System.ComponentModel; System.Data; System.Drawing; System.Linq; System.Text; System.Windows.Forms;

PruebasKepserver{ : { frmVentanaDispositivo() { InitializeComponent(); } parametrosTag( direccion, valor, calidad, tiempo) { lblMensaje.Text = + direccion +

+ valor; (valor == ) { .BackColor = .YellowGreen; } { .BackColor = .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.accionamientoChannel1.PLCAutomationDirect.X.confirmaciónChannel1.PLCAutomationDirect.X.encendidoEs necesario que el nombre sea exactamente el mismo, respetando acentos y mayúsculas.

usingusingusingusingusingusingusingusingnamespace

public partial class

public

public void string stringstring string

if

this

else

this

frmVentanaDispositivo Form

Color

Color

"La entrada: " " Tiene un valor: "

"True"

189

LabelName: lblMensaje

Page 58: Manual Curso C NET 3raParte

Registramos los Tags uno por uno.

Por ser una versión 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.

190

DISPOSITIVOS MÓVILES EN C#.NET

Práctica: Monitoreo y Control con SmartphoneEn este ejercicio vamos a crear un proyecto para recibir mensajes por un puerto serial bluetooth y mandar comandos de activación a otra aplicación.

La aplicación 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.

Page 59: Manual Curso C NET 3raParte

191

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

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

Renombramos Form1.cs con frmPrincipal.cs

Page 60: Manual Curso C NET 3raParte

192

Aparte agregamos otro formulario con las mismas características:

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 comunicación:

1.- Conectamos nuestro SmartPhone o PDA a la PC por su cable USB.

2.- Ejecutamos nuestro proyecto

.

A continuación salta una ventana donde aparecen las opciones de compilación:

Si nuestro Smartphone esta conectado elegimos la opción: Windows Mobile 6 Standard Device, en caso contrario elegimos el emulador mas parecido al dispositivo real.

Los emuladores permiten probar el código 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 código.

Page 61: Manual Curso C NET 3raParte

Una de las diferencias que tienen los proyectos para móviles es que la organización 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 código 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

usingusingusingusingnamespace

class

private stringpublic string

getreturn

setvalue

private stringpublic string

getreturn

setvalue

private stringpublic string

getreturn

set value

System; System.Collections.Generic; System.Linq; System.Text;

MonitoreoSmartPhone{ { _hora; Hora { { _hora; } { _hora = ; } } _concepto; Concepto { { _concepto; } { _concepto = ; } } _acción; Acción { { _acción; } { _acción = ; } } }}

Alarmas//en esta propiedad guardamos la hora de la Alarma

//Esta propiedad guarda el conceptode la Alarma//Ejemplo: "Sobrecalentamiento caldera 5A"

//En esta propiedad se guarda la Acción recomendable para arreglar la//falla para facilitar la operación y estandarizar el proceso// Ejemplo: "Ajustar el control PID"

193

Page 62: Manual Curso C NET 3raParte

En la clase AlarmasArrayList.cs guardamos la colección de objetos alarma y creamos dos métodos, uno para agregar cada mensaje y otro para crear una tabla de datos que posteriormente se incrustará en un Datagrid del dispositivo mobil.

System; System.Linq; System.Collections.Generic; System.ComponentModel; System.Data; System.Text; System.Collections;

MonitoreoSmartPhone{ : { agregaAlarma( alarma) { .Add(alarma); } TraeTabla() { tablaMuestras = (); tabla = (); tablaMuestras.Tables.Add(tabla);

tablaMuestras.Tables[0].TableName = ; tablaMuestras.Tables[0].Columns.Add( ); tablaMuestras.Tables[0].Columns.Add( ); tablaMuestras.Tables[0].Columns.Add( ); ( a ) { linea = tablaMuestras.Tables[0].NewRow(); linea[0] = a.Hora; linea[1] = a.Concepto; linea[2] = a.Acción; tablaMuestras.Tables[0].Rows.Add(linea); } tablaMuestras.Tables[0]; } }}

Finalmente creamos la clase Dispositivos.cs, en ella guardamos el estado actual de 4 motores aireadores que se activarán en la pantalla móvil.

System; System.Linq; System.Collections.Generic; System.Text;

MonitoreoSmartPhone{ { _activaAireador1= ; ActivaAireador1 { { _activaAireador1; } { _activaAireador1 = ; } }

usingusingusingusingusingusingusingnamespace

class

public voidthis

publicnew

new

foreach in this

return

usingusingusingusingnamespace

classprivate bool falsepublic bool

getreturn

setvalue

AlarmasArrayList ArrayList

Alarmas

DataTableDataSet DataSetDataTable DataTable

AlarmasDataRow

Dispositivos

//este método crea el arreglo de alarmas

//este método crea una tabla a partir del arraylist de alarmas

//nombre de la tabla

//agregamos las tres columnas de nuestra tabla:

//llenamos la tabla con cada objeto Alarma de nuestro Arraylist

//nos regresa una tabla ordenada con todas las alarmas para poder//incrustarla en el DataGrid

//en esta clase guardamos el estado actual de los dispositivos

"Alarmas"

"Hora""Alarma""Accion"

Recuerde que heredamos de Arraylist y debe estar incorporado System.Collections

194

Es importante que el namespace tenga el mismo nombre que el proyecto: MonitoreoSmartPhone

Page 63: Manual Curso C NET 3raParte

195

_activaAireador2 = ; ActivaAireador2 { { _activaAireador2; } { _activaAireador2 = ; } } _activaAireador3 = ; ActivaAireador3 { { _activaAireador3; } { _activaAireador3 = ; } } _activaAireador4 = ; ActivaAireador4 { { _activaAireador4; } { _activaAireador4 = ; } } }}

private bool falsepublic bool

getreturn

setvalue

private bool falsepublic bool

getreturn

setvalue

private bool falsepublic bool

getreturn

setvalue

Ahora en nuestro frmPrincipal modificamos la resolución 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 código también puede funcionar con PDA's con windows mobile de versiones anteriores. Para este modelo la resolución de pantalla es de 320x240 sin embargo al momento de cargar la aplicación 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.

DataGridName: dGridAlarmas

CheckBoxName: chkPuertoSerieText: Abrir Puerto Serie

LabelName: lblPuertoText: Msg

TextBoxName: txtDatoSerie

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

mainMenu1 viene por defecto

MenuItemsName: menuItemDispositivos, Text: Dispositivos Name: menuItemActualizaTabla, Text: Actualizar Tabla

Page 64: Manual Curso C NET 3raParte

Es importante conocer que este Datagrid para móviles es diferente al DatagridView de las aplicaciones de escritorio, en especial se encuentra muy limitado en sus métodos ya que no es posible insertar líneas 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, Acción.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 características:

Para el código de frmPrincipal creamos las siguientes instancias:

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

usingusingusingusingusingusingusingusingusing

namespace

public partial class

newnew

stringpublic

MonitoreoSmartPhone{ : { arrayAlarmas = (); aireadores = (); datoSerial; frmPrincipal() { InitializeComponent(); }

frmPrincipal Form

AlarmasArrayList AlarmasArrayListDispositivos Dispositivos

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

196

Page 65: Manual Curso C NET 3raParte

El código para abrir el puerto serie es similar a los ejemplos anteriores. chkPuertoSerie_CheckStateChanged( sender, e)

{ (chkPuertoSerie.Checked) { { (!serialPort.IsOpen) { serialPort.Open(); } chkPuertoSerie.Text = ; chkPuertoSerie.BackColor = .YellowGreen; lblPuerto.Text = + serialPort.PortName.ToString(); } { lblPuerto.Text= ; } } { { (serialPort.IsOpen) { serialPort.Close(); chkPuertoSerie.BackColor = .Red; } } { lblPuerto.Text= ; } } }

En el evento DataReceived del SerialPort: serialPort_DataReceived( sender,

System.IO.Ports. e) {

}

También creamos las funciones: analisisCadena( dato) {

[] arreglocadena = dato.Split( ); alarma = (); alarma.Hora= arreglocadena[0]; alarma.Concepto= arreglocadena[1]; alarma.Acción= arreglocadena[2]; arrayAlarmas.agregaAlarma(alarma); }

private void objectif

try

if

catch

elsetry

if

catch

private void object

private void string

string

new

EventArgs

Color

Color

SerialDataReceivedEventArgs

Alarmas Alarmas

//si el puerto esta cerrado lo abre

//habilita el botón solo si esta abierto el puerto

//recibimos una cadena con este formato// Hora + "+" + Alarma + "+" + Acción// "12:30 hrs + Sobrecalentamiento + Checar Bomba 1-5"

// Split crea un arreglo de los datos separados por un '+'

//guardamos los datos en nuestro objeto Alarma

//agregamos este mensaje a la colección de alarmas

"Puerto Abierto"

"COM: "

"No pude abrir el puerto COM"

"Algo anda mal no pude cerrar el COM"

'+'

{ serialPortChat.ReadTimeout = 2000; datoSerial = serialPortChat.ReadTo( ); .Invoke( (TextoRecibido)); } { .Show( ); serialPortChat.DiscardInBuffer(); }

try

this new

catch

//intentamos leer el puerto//espera 2 segundos por el dato

//almacena toda la cadena de datos hasta que lee "-FIN-"

//ahora recorta los datos e invoca al manejador

//no llego la cadena completa, interceptamos el error

//borra los datos del buffer de entrada

"-FIN-"

"Error, no recibí el identificador final de la cadena -FIN-"

EventHandler

MessageBox

197

Page 66: Manual Curso C NET 3raParte

198

PictureBoxEn la propiedad Image y SizeMode asignar a todos: aireador_OFF_small.jpgSizeMode: StretchImage

Name: picBox1Name: picBox2Name: picBox3Name: picBox4

CheckBoxName: chkAireador1 Text: Aireador 1Name: chkAireador2 Text: Aireador 2Name: chkAireador3 Text: Aireador 3Name: chkAireador4 Text: Aireador 4

mainMenu1 viene por defecto

Name: menuCerrar

private void object

public void

if

private void object

new

private void object

TextoRecibido( sender, e) { txtDatoSerie.Text = datoSerial; analisisCadena(datoSerial); }

comandoActivacion( chk) { (!serialPort.IsOpen) { serialPort.Open(); } serialPort.Write(chk.Name.ToString() + + chk.Checked.ToString() + ); }

Seleccionando cada botón de los menús en su evento click escribimos:

menuItemDispositivos_Click( sender, e) { planta = (); planta.Show(); }

menuItemActualizaTabla_Click( sender, e) { tabla = arrayAlarmas.TraeTabla(); dGridAlarmas.DataSource = tabla; }

EventArgs

CheckBox

EventArgs

frmPlanta frmPlanta

EventArgs

DataTable

//creamos este método publico para recibir valores //desde el otro formulario de dispositivos

//al cambiar de formualrio se puede cerrar el puerto, por eso se vuelve a abrir

//nos manda el valor del ChkAireador elegido

//Abrimos el otro formulario

//actualizamos el dataGrid con el método TraeTabla

//tabla: trae los valores organizados en 3 columnas: Hora, Alarma, Acción

" : " "Fin"

Page 67: Manual Curso C NET 3raParte

A nuestros 4 controles CheckBox les agregamos un método compartido en su evento Click, lo nombramos “AireadorElegido”.

Seccionando el botón del menú activamos su evento Click.

usingusingusingusingusingnamespace

public partial class

newpublic

private void object

string

if

switch

casenew

breakcase

newbreak

casenew

breakcase

newbreak

else

switch

System.ComponentModel; System.Data; System.Drawing; System.Text; System.Windows.Forms;

MonitoreoSmartPhone{ : { principal = (); frmPlanta() { InitializeComponent(); } AireadorElegido( sender, e) { ruta = System.IO. .GetDirectoryName( System.Reflection. .GetExecutingAssembly().GetName().CodeBase); ruta = ruta.Replace( , ); check = ( )sender; principal.comandoActivacion(check); (check.Checked) { (check.Text) { : picBox1.Image = System.Drawing. (ruta + ); ; : picBox2.Image = System.Drawing. (ruta + ); ; : picBox3.Image = System.Drawing. (ruta + ); ; : picBox4.Image = System.Drawing. (ruta + ); ; } } { (check.Text) {

frmPlanta Form

frmPrincipal frmPrincipal

EventArgs

PathAssembly

CheckBox CheckBox

Bitmap

Bitmap

Bitmap

Bitmap

//con este método cambiamos el gráfico del dispositivo //y mandamos el aviso al otro formulario

//se activa la casilla

// se desactiva la casilla

"file:\\" ""

"Aireador 1"@"\aireador_ON_small.jpg"

"Aireador 2"@"\aireador_ON_small.jpg"

"Aireador 3"@"\aireador_ON_small.jpg"

"Aireador 4"@"\aireador_ON_small.jpg"

199

Page 68: Manual Curso C NET 3raParte

: picBox1.Image = System.Drawing. (ruta + ); ;

: picBox2.Image = System.Drawing. (ruta + ); ; : picBox3.Image = System.Drawing. (ruta + ); ; : picBox4.Image = System.Drawing. (ruta + ); ; } } } menuCerrar_Click( sender, e) { principal.Dispose(); .Close(); } }}

Las imágenes que descargamos contienen el aireador en estado ON y OFF.

casenew

break case

newbreak

casenew

breakcase

newbreak

private void object

this

"Aireador 1"@"\aireador_OFF_small.jpg"

"Aireador 2"@"\aireador_OFF_small.jpg"

"Aireador 3"@"\aireador_OFF_small.jpg"

"Aireador 4"@"\aireador_OFF_small.jpg"

Bitmap

Bitmap

Bitmap

Bitmap

EventArgs

Para que el dispositivo pueda cargarlas en tiempo de ejecución debemos guardarla en los archivos de programa del SmartPhone o PDA, en este caso no tenemos la carpeta Bin/Debug.

IMPORTANTE: Las imágenes deben ser de un tamaño 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 aplicación en C# que mande los mensajes de alarma y reciba los comandos de activación 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

Hasta ahora ya tenemos el código para el Smartphone, pero nos falta sincronizarlo por medio de un puerto virtual Bluetooth y otra aplicación de escritorio que no mande y reciba nuestros comandos.

Page 69: Manual Curso C NET 3raParte

201

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 aplicación de escritorio de C#.

Para mandar los mensajes podemos usar los ejemplos anteriores de comunicación Rs232, solo agregamos un botón que mande un mensaje con la sintaxis que requiere nuestro SmartPhone para separar las alarmas.

Al abrir el puerto nuestro botón para mandar alarma debe mandar los mensajes separados por un '+' y con la palabra -FIN-

btnAlarma_Click( sender, e) { serialPort.Write( ); }

private void object EventArgs

"12:30 hrs + Sobrecalentamiento + Checar Bomba 1-5 -FIN-"

Finalmente tenemos un monitoreo y control inalámbrico en el rango de operación de nuestro Dongle Bluetooth.

Puede crear un proyecto nuevo de windows forms o modificar un ejemplo anterior.

Page 70: Manual Curso C NET 3raParte

Con este tema damos por concluido nuestro curso.

Gracias por su participación

Atte.Ing. Aaron Castro Bazua

Con este tema damos por concluido nuestro curso.

Gracias por su participación

Atte.Ing. Aaron Castro Bazua