Pep Lluis Baño

Anuncio
Robot dispensador
para MSDN Vídeo
Pep Lluis Baño
1
nº
ADVERTENCIA LEGAL
Todos los derechos de esta obra están reservados a Pep Lluis
Baño y a Netalia, S.L.
El editor prohibe cualquier tipo de fijación, reproducción, transformación o distribución de esta obra, ya sea mediante venta,
alquiler o cualquier otra forma de cesión de uso o comunicación
pública de la misma, total o parcialmente, por cualquier sistema
o en cualquier soporte, ya sea por fotocopia, medio mecánico o
electrónico, incluido el tratamiento informático de la misma, en
cualquier lugar del mundo.
La vulneración de cualesquiera de estos derechos podrá ser considerada como una actividad penal tipificada en los artículos 270
y siguientes del Código Penal.
La protección de esta obra se extiende al mundo entero, de
acuerdo con las leyes y convenios internacionales.
© Pep Lluis Baño, 2005
© Netalia, S.L., 2005
Robot dispensador para MSDN Vídeo
Cuaderno Técnico de dotNetManía nº1
Autor: Pep Lluis Baño
Responsable editorial: Paco Marín
Diseño de cubierta: Silvia Gil (Éride, S.L.)
Editado por Netalia S.L.
c/ Robledal, 135
28529 - Rivas Vaciamadrid (Madrid -España)
Tel. (34) 91 666 74 77 - Fax (34) 91 499 13 64 - http://www.dotnetmania.com
EJEMPLAR GRATUITO
Este ejemplar es gratuito gracias a la cesión de los derechos de autor de Pep Lluis Baño, a Microsoft
que ha sufragado los costes de impresión y a Netalia que ha costeado el diseño, maquetación, revisión
y distribución a los lectores de dotNetManía.
Déposito Legal: M-47915-2005
Impreso en Madrid (España) en noviembre de 2005
índice
Robot dispensador para
MSDN Vídeo
1
nº
Introducción . . . . . . . . . . . . . . . . . . . . . . . . . . . .
9
1. Entregon+ . . . . . . . . . . . . . . . . . . . . . . . . . . . .
13
1.1. Descripción . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
13
1.2. Ensamblado . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
14
1.3. Puesta en servicio . . . . . . . . . . . . . . . . . . . . . . . . . .
15
2. El puerto serie . . . . . . . . . . . . . . . . . . . . . . . .
17
2.1. El puerto serie . . . . . . . . . . . . . . . . . . . . . . . . . . . .
17
2.2. Protocolo de comunicaciones DispeDataLink . . .
19
2.3. OSI Layer 1. La capa física . . . . . . . . . . . . . . . . . . . .
19
2.4. OSI Layer 2. Enlace a datos . . . . . . . . . . . . . . . . . . .
21
2.5. OSI Layer 2. Definir características del enlace
de datos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
22
3. La aplicación . . . . . . . . . . . . . . . . . . . . . . . . . .
25
3.1. OSI Layer 7. La aplicación . . . . . . . . . . . . . . . . . . .
25
3.2.Tratamiento de eventos, recepción y errores . . .
26
3.3. System.IO.Ports ¡El Espacio! . . . . . . . . . . . . . . . . .
27
4. Programando . . . . . . . . . . . . . . . . . . . . . . . . .
29
4.1. Nuestra primera aplicación . . . . . . . . . . . . . . . . . .
29
5.Programando con técnica . . . . . . . . . . . . . . .
37
5.1. Programando con técnica . . . . . . . . . . . . . . . . . . .
37
6. ServidorComm. La clase . . . . . . . . . . . . . . . .
47
6.1. ServidorComm. Introducción a la clase . . . . . . . .
47
7. ServidorCom.dll. Nuestro laboratorio . . . .
53
7.1. ServidorCom.dll . . . . . . . . . . . . . . . . . . . . . . . . . . . .
53
7.2. Formato tramas envío/recepción . . . . . . . . . . . . .
54
7.3. Petición/respuesta entrega de cápsula - 01 . . . . . .
55
7.4. Petición/respuesta introducción de cápsula - 02 .
56
7.5. Petición/respuesta estatus del sistema - 03 . . . . .
57
7.6. Códigos retorno y operación . . . . . . . . . . . . . . . .
58
7.7. Códigos Indicadores de Incidencias . . . . . . . . . . . .
59
7.8. Cálculo del CRC (pasos previos) . . . . . . . . . . . . . .
59
7.9. El simulador . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
74
7.10. Mensajes en el EventLog . . . . . . . . . . . . . . . . . . .
82
7.11.Avisos de incidencias automatizados . . . . . . . . . .
83
7.12. Los archivos de configuración . . . . . . . . . . . . . . .
86
7.13. La gestión del puerto serie . . . . . . . . . . . . . . . . .
89
7.14.Acceso a una base de datos Access . . . . . . . . . .
97
8. El ClienteEPlus. La solución . . . . . . . . . . . . . 103
8.1. ClienteEPlus . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
103
8.2. [F5] . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
122
9. De beta 1 a beta 2 . . . . . . . . . . . . . . . . . . . . . 123
10. Conclusiones . . . . . . . . . . . . . . . . . . . . . . . . . 127
10.1. Conclusiones . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
127
10.2. La reflexión (pero en serio) . . . . . . . . . . . . . . . . .
127
10.2. Dedicatorias . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
128
10.2.Agradecimientos . . . . . . . . . . . . . . . . . . . . . . . . . . .
128
Introducción
La idea de este “mini-libro”, nace a tren de dos iniciativas. La primera de
ellas era escribir un artículo que hablara del puerto serie. Pocas veces desarrollamos temas relacionados con él, entre otras cosas porque son muy pocos, los programadores que se ven involucrados en diseños que trabajen las comunicaciones
serie entre equipos y dispositivos. O sea que somos una minoría. Desconozco el
interés que pueda representar para la audiencia en general, sin embargo técnicamente me resulta insuficiente escribir un pequeño artículo de cinco o seis páginas,
repasando y dando ejemplos genéricos e impersonales hablando de las excelencias
de la nueva versión del entorno.
De ahí y pensando en colaborar con “Desarrolla con MSDN” y su aplicación de
“Vídeo”, pude reconciliar las dos necesidades: desarrollar un ejemplo práctico de aplicación que interactúe con un dispositivo electro-mecánico ficticio unido al dispensador de vídeo, usando una comunicación serie con la electrónica que lo controla.
Por cierto, os animo a descargaros y aprender de la aplicación MSDN Vídeo en
www.desarrollaconmsdn.com, uno de los mejores recursos para aprender a desenvolverse dentro de la plataforma .NET, entorno a una impecable aplicación práctica y en
castellano.
página
Soy consciente que realizar esta puesta en escena, no va a tener interés práctico
para el gran grueso de desarrolladores, sin embargo quiero llamar su atención a un
mundo desconocido para la mayoría de ellos. Los acostumbrados a las aplicaciones
típicas de gestión con bases de datos, conoceréis en los siguientes capítulos aspectos
curiosos y desconocidos de estas nuestras “atípicas” aplicaciones de comunicaciones;
será divertido hablar de tramas y sus formatos, hablar de envíos, recepciones o crear
un simulador del mecanismo. Puede ser una oportunidad para intuir el lado más
industrial de un sector tan amplio como el nuestro. Para los que ya estáis en el pastel,
quiero animaros a encontrar entre líneas la suficiente información para introduciros
en el uso Visual Studio 2005, sin duda y hasta la fecha, el mejor de los entornos de
desarrollo rápido.
9
<<
Robot dispensador para MSDN Vídeo
En nuestro oficio y englobando a la totalidad de desarrolladores, existen muchísimos niveles en la asimilación a los avances que estamos expuestos. Mientras os estoy
hablando del nuevo Framework 2.0 y Visual Studio 2005, muchos de nosotros aun
estamos trabajando con la antigua versión 6 del entorno.
Sólo nosotros, los programadores, sabemos lo que significa un cambio en el versionado del lenguaje, no es de extrañar que cada vez que afrontamos una migración,
padezcamos de “migraña”. En una mano tenemos el convencimiento de las grandes
ventajas en utilizar el nuevo entorno, en la otra mano tenemos nuestra aplicación en
la versión anterior, el corazón nos dice “¡salta!” y el cerebro nos aconseja “¡Espérate!”.
Los que hemos pasado varias veces por esto, sabemos que determinadas versiones de producto marcan un camino que cuaja durante años, no por nada hemos hablado de la versión 6 de Visual Studio. A pesar de que no todos estarán de acuerdo, faltaría más, mi opinión es que con Visual Studio 2005 afrontamos y estamos frente a
uno de los entorno de desarrollo de esta plataforma, que volverá a marcar un punto
de referencia dentro de la comunidad de desarrolladores.
Hablando de la asimilación de esos avances, me lleva a pensar en Visual Studio 2005,
como una herramienta avanzada, robusta y madura, que cubre en su alcance una extensa
diversidad de niveles, complejidades y ámbitos, ayudándonos a encontrar el camino mas
fácil para llevar a cabo cualquier reto que se nos plantee en nuestro trabajo diario.
Después de la argumentación anterior, estoy en disposición de explicaros, que
todo el material que encontrareis a continuación, está escrito simulando o pensando
en un manual de uso, como el que te entregan al comprar la máquina (Entregon+), no
tardareis mucho en saber que se trata de nuestro robot dispensador de MSDN Vídeo.
He puesto un gran esfuerzo en escribir los ejemplos y el código de una manera llana
y sencilla, o sea, que no os preocupéis, pues somos muchos los que pertenecemos al grupo
que necesita lanzar la ayuda cada vez que debe usar alguna de esas instrucciones complicadas y que sin complejos tira de beta de los 101 ejemplos cuando necesita entender como
funciona alguna de las nuevas prestaciones que aporta la plataforma.
En este punto me excuso, pues como digo, no he cuidado las expresiones o formas siguiendo las conocidas recomendaciones y buenas practicas de los manuales del
“Buen Programador” o la programación eXtrema (¡qué miedo!). En todo momento
he dado preferencia a un estilo de programación para los más sencillos, donde el objetivo primordial es conseguir que los algoritmos funcionen a buen ritmo, dejando de
lado algunos purismos o conocimientos profundos.
página
10
Robot dispensador para MSDN Vídeo
>>
A pesar de que nos cueste reconocerlo, hablando entre nosotros en confianza, a
muchos de los que aún no hemos saltado a .NET, nos incomoda hablar de tipos, delegados, sobrecargas , polimorfismos, hilos de ejecución, invocaciones, herencias, abstracciones e incluso de clases. Es evidente que no estamos acostumbrados a ellas, la
intención de estos laboratorios no es otra que ayudar a PERDER EL MIEDO, para
que en un futuro muy corto, podamos saltar sobre la gran tabla .NET, sin complejos
ni vergüenzas!
Finalmente os pido licencia y benevolencia, por presentaros un trabajo escrito a
horas intempestivas entre sábados, domingos y jueves hasta la una de la madrugada
mientras intentaba evitar seguir esos concursos para cantantes que tan de moda están
en televisión.
Aclarar que nadie ha revisado concienzudamente ni el texto ni el código, está
hecho con cariño pero sin encargo, lo que hay, es lo que veis. Que nadie espere ver
un libro en la forma habitual, no pretende serlo, simplemente es un “desparrame”
de borradores. Os pido disculpas, si a pesar de todo no me salvo de la quema y ¡termino siendo culpable!.
página
Pep,
11
capítulo
1
Entregon+
Descripción
Entregon+ pertenece a la familia de los prestigiosos dispensadores del grupo
Dispensotron, potentes robots dispensadores de encapsulados o carcasas que incorporan un
mecanismo de lectores grabadores con etiquetas electrónicas que permiten construir un sistema flexible y versátil, controlando tanto el contenido como el destinatario y recepción del
mismo. Elimina la necesidad de las aburridas secretarias encargadas del tedioso control
manual de entradas/salidas de material, siendo un almacén inteligente, sin llegar a serlo.
Fabricado en base a una potente aleación de aluminio/titanio, para paliar la acidez palmar de los usuarios de este tipo de contenedores, así como la incorporación de
su bandeja de entrega antivandálica, hacen que Entregon+ sea uno de los dispensadores de películas de video o CD más potentes del mercado.
Su sencillez es alarde de robustez, un simple sistema de coordenadas de eje horizontal con la movilidad del cabezal en su eje vertical, permiten una sorprendente simplicidad de estructura mecánica, tolerante a fallos y libre de mantenimiento.
Su concepción permite dispensar encapsulados, tanto en cajeros automatizados
como en mostrador, siendo de uso compartido y eliminando la inoperativa concepción
de dispensadores exclusivos (cajero/dispensador). Además de optimizar el rendimiento, organización y coste por cápsula dispensada.
Sus principales características son:
Carcasa de aleación exclusiva Entregon+.
Mecanizado de barras cabezal, niqueladas con auto engrase.
Lector/grabador de etiquetas electrónicas incorporados en el cabezal.
Juego de 1.000 (EE) preparadas para más de 1 millón de grabaciones.
Localizador/dispensador de carcasas de alta velocidad, 3ª generación.
Matriz de carcasas, con un máximo de 4m de largo por 2m de alto.
Sistema de doble procesador (RISC), tolerante a fallo.
Comunicaciones RS232/485.
página
•
•
•
•
•
•
•
•
13
<<
Robot dispensador para MSDN Vídeo
Antes de dar alimentación al robot debe haber completado todos los pasos y
recomendaciones de instalación.
Preparación: Desembale y desempaquete todos los elementos que componen el
dispensador, según le indican las instrucciones detalladas en el lateral de las cajas. Una
vez localizadas y ordenadas por orden ascendente siga a su ensamblaje siguiendo los
pasos detallados en el primer apartado.
Ensamblado
Matriz
1. Preparar los soportes de manera que los agüeros mecanizados queden fijados
a la pared con los tornillos de disparo, preparados para soportar un mínimo
de 50Kg.
2. Utilizando los pernos 50, 51, 52 y 53, fijar las dos barras de manera que observemos una paralela, visualizándolo desde al menos dos metros de distancia.
3. Fijar las barras laterales a la guía porta cabezales.
4. Asegurar el anclaje que fija el usillo, no olvide introducir los tornillos 23 y 24
de 12mm, apretándolos con una presión de torque mínima de 12nw.
5. Realice la operación de test antes de integrarlo en sistema alguno (siga las instrucciones del apartado de puesta en funcionamiento).
MATRIZ
gráfico 1.1
página
14
Robot dispensador para MSDN Vídeo
>>
Puesta en servicio
Prueba
1. Conectar el cable de alimentación a la toma de 220v.
2. Verificar que la luz “PowerLED” de color verde, está encendida (consultar el
apéndice “Resolución de problemas” en caso contrario).
3. Observar la intermitencia del indicador naranja de ignición, durante un tiempo aproximado de 30 segundos.
4. Esperar a que el indicador de “Ready” quede encendido.
5. Pulsar el botón “Test”.
En la pantalla LCD observará lo siguiente:
•
•
•
•
•
•
Test RISC processors.
Performing Memory test.
Align horizontal arm.
Align vertical head.
Dual Processing RTC system Startup.
Perform seeker test.
En este punto observaremos como el cabezal y el usillo recoge cápsulas se empiezan a desplazar a lo largo de los ejes (X, Y) iniciando a su velocidad mínima (una localización de cápsula por minuto) y finalizando a su velocidad máxima (10 localizaciones minuto).
Esta prueba tiene una duración de unos 10 minutos, al finalizar deberemos visualizar los resultados en la pantalla LCD, Test passed (80 seeks performet without errors).
página
Para otros mensajes consulte el apéndice de “Resolución de problemas”.
15
capítulo
2
El puerto serie
El puerto serie
Características
El dispensador Entregon+, incorpora dos UART 16C450 (Universal Asincronous
Receiver Transmiter) de Texas. Compartidas por los dos procesadores RISC. Su avanzado
diseño es tolerante a fallos, en cuyo caso, el primer procesador disponible conmuta las
líneas de entrada/salida del conector serie RS232/RS485 a unos circuitos que convierten
directamente las tensiones +/- 9/12V a lógica TTL, permitiendo que el sistema continue
trabajando sin interrumpir sus comunicaciones y lanzando una alerta al sistema central,
avisando de la anomalía en uno de los controladores.
La tarjeta de comunicaciones serie es intercambiable en caliente (HotSwap) de manera que el responsable de mantenimiento podrá sustituir dicha tarjeta sin tener que detener
o reiniciar el sistema. Pida su pieza de recambio PN=PS23MSDNVideo/3.
PATILLAS DEL CONECTOR SERIE
página
gráfico 2.1
17
<<
Robot dispensador para MSDN Vídeo
Pin Entrada/Salida
1
Entrada
2
Entrada
3
Salida
4
Salida
5
6
Entrada
7
Salida
8
Entrada
9
Entrada
Nombre
DCD
RD
SD
DTR
GND
DSR
RTS
CTS
RI
Descripción
Detección de Portadora
Recepción
Transmisión
Terminal Datos Apunto
Tierra
Datos Apunto
Solicitud de Envió
Listo para enviar
Indicador de Llamada
tabla 2.1
En inglés
Data Carrier Detect
Receive Data
Send Data
Data Terminal Ready
Ground
Data Set Ready
Request to Send
Clear to Send
Ring Indicator
Patillas del conector serie
En el desarrollo, y a efectos de prueba, deberemos disponer de un cable Test para
efectuar la emulación de acceso de nuestra aplicación al dispensador, o bien podremos
usar los dispensadores conectados al servidor de MSDN Vídeo, usando los servicios
construidos a tal propósito.
Las comunicaciones serie, cubren la necesidad de intercambiar información
entre dos dispositivos (de tú a tú), a baja velocidad, con líneas de transmisión y recepción diferenciadas, en distancias de hasta un máximo de 15 metros y sin necesidad de
implementar protocolo, si bien, como ya hemos precisado, es necesario definir los
parámetros de la misma en términos de velocidad, paridad, bits de datos, bits de parada...
En el gráfico 1 vemos cómo enviamos un byte:
ESQUEMA DE SERIALIZACIÓN DE UN BYTE
gráfico 2.2
página
18
Robot dispensador para MSDN Vídeo
>>
Un rápido análisis de la figura anterior nos clarifica los siguientes elementos:
Pensando en la unión de un transmisor y un receptor, el transmisor dispone de una
salida de datos SD, por la que entrega un voltaje comprendido entre -3 y -15 Vcc, para la
señal de marca (Mark) y un voltaje entre +3 y +15 Vcc para la señal de espacio (Space). El
proceso se inicia con una transición de reposo a espacio, el receptor espera el bit de inicio
(star), después del cual inicia la des-seriacion de datos, marcada en intervalos exactos definidos por la velocidad fijada en la operación de apertura del puerto. Una vez recibidos los
8 bits, comprueba que la paridad sea correcta y espera un periodo de paro correspondiente al definido en bits de parada, para continuar con la siguiente entrada de datos. El byte
recibido se almacena en un área tampón (búfer) del controlador de comunicaciones que
dispara una interrupción a la CPU para que ésta recoja el dato antes de que éste sea sobrescrito por la próxima recepción.
Llevar a cabo una comunicación serie nos obliga a concentrarnos en:
a) Características del enlace a datos.
b) Envío/recepción asíncrona.
c) Tratamiento de eventos de recepción y errores.
Protocolo de comunicaciones DispeDataLink
Descripción
Entregon+ dispone del protocolo de comunicaciones DDL (DispeDataLink) exclusivo de este dispensador. Este protocolo le permite realizar las principales funciones de posicionamiento, utilizando una sencilla comunicación de intercambio de tramas. Por lo que
incluso podemos hacerlo trabajar mandándole órdenes a través del sencillo Hyperterminal.
Como cualquier otro protocolo de comunicaciones, DDL define su estructura
OSI siguiendo el esquema del gráfico 2.3.
OSI Layer 1. La capa física
Laboratorio 1
Para llevar a cabo este laboratorio deberá disponer de un conector hembra de 9
contactos (ver gráfico 2.1), así como de un soldador con estaño y diversos hilos de
cobre para realizar los puentes que se detallan en el gráfico 2.4.
página
En caso de no disponer de estos medios diríjase a una tienda especializada de
electrónica y pida este servicio.
19
<<
Robot dispensador para MSDN Vídeo
ISO/OSI (OPEN SYSTEMS INTERCONNECT)
gráfico 2.3
ESQUEMA DEL CABLE DE BUCLES
gráfico 2.4
Corte un hilo de cobre de aproximadamente 4 cm, quite el recubrimiento plástico
de los extremos y continue soldándolo en el terminal número 2, el otro extremo del hilo
deberá soldarlo en terminal número 3. (en argot técnico “hacer un puente”). A continuación efectue los dos puentes restantes soldando los terminales 4 con 6 y 7 con 8,
según se indica en el dibujo anterior.
página
20
Robot dispensador para MSDN Vídeo
>>
No olvide colocar el conector de 9 contactos dentro de sus tapas originales, ello
contribuirá a una fácil inserción y extracción del conector, así como una correcta
manipulación.
OSI Layer 2. Enlace a datos
En la velocidad definimos a qué frecuencia encadenamos los bits, habitualmente a 9600, 19200 y 38400.
La velocidad es indicativa de bits por segundo (bps), por lo tanto a 19200bps, en un
segundo podremos enviar unos 1,7Kb (necesitamos un espacio de 11 bits para mandar 7
u 8 bits de datos). Observaremos que la velocidad se dobla en cada salto, ello se debe a la
naturaleza del controlador de comunicaciones que usa como patrón un cristal de cuarzo
que va dividiendo por dos la frecuencia de su reloj para cada salto de velocidad.
El primer nivel en la detección de errores está cubierto con el control de paridad; el transmisor efectúa una operación de apareamiento de bits dando como bit de
paridad que cuadre con la seleccionada1.
Por ejemplo, si hemos seleccionado paridad par, el bit de paridad se ajustará para
que coincidan por pares la cantidad de ceros y de unos.
Paridad par:
Datos - 0011 011, Bit de Paridad = 0
Datos - 1011 011, Bit de Paridad = 1
Este bit de paridad se utilizará para verificar la correcta recepción de los datos por el
receptor, siendo “dato bueno” si la paridad es coincidente. La paridad descarta algunos
errores producidos por el ruido en la transmisión de datos, pero no es un sistema 100%
efectivo, por lo que más adelante hablaremos de la aplicación de otros sistemas de detección y corrección de errores, tales como el CRC o verificación de redundancia cíclica.
Se puede seleccionar entre paridad “par”, “impar” o simplemente “sin”.
Los bits de datos, son la longitud que conformara el dato tanto para su transmisión
como para su recepción y está comprendida habitualmente entre 7 u 8 bits. (con 7 bits dispondremos de la primera tabla de 127 caracteres ASCII y con 8 el juego completo de 255).
Finalmente, deberemos indicar al puerto serie la cantidad de tiempo de parada al
final de cada transmisión, siendo sus valores los comprendidos entre 1, 1,5 ó 2 bits.
El control de paridad se efectúa a nivel de controlador y de una manera automática, así pues no va a ser necesario efectuar operación alguna desde nuestra aplicación.
página
1
21
<<
Robot dispensador para MSDN Vídeo
A modo de comentario, también deberemos saber que existen un conjunto de caracteres de control que coinciden con las primeras posiciones de la tabla ASCII; éstos eran
muy útiles en entornos de terminal. Veremos más de ellas en al apéndice.
OSI Layer 2. Definir características del enlace de datos
Descripción
En ayuda a los integradores de este sistema, vamos a definir la capa de enlace de
datos. Este nivel se caracteriza por llevar a cabo el intercambio de tramas entre dos o más
dispositivos usando una comunicación tú a tú o bien de enlace anfitrión. Una vez iniciado
el sistema, el Entregon+ se autoinicializa a las características definidas en los microinterruptores accesibles desde la parte inferior del ingenio (gráfico 2.5).
Para establecer cualquier comunicación serie, deberemos antes definir las características de la misma en término de los siguientes parámetros: velocidad, paridad, bits de datos y
paridad. Entregon+, trabaja de predeterminadamente a la velocidad definida en los conmutadores, sin paridad, ocho bits de datos y un bit de parada (vvvv,N,8,1).
MICROINTERRUPTORES SELECTORES DE VELOCIDAD
gráfico 2.5
La aplicación cliente, puede utilizar las librerías de comunicaciones suministradas con el Entregon+, desarrolladas para su uso en sistemas operativos de Microsoft
que soporten de, .NET Framework 2.0.
El siguiente laboratorio explicará cómo efectuar una pequeña aplicación de enlace usando el Visual Studio 2005 y su lenguaje Visual Basic 2005.
Conectáremos el Entregon+ utilizando un cable serie cruzado (ver esquema en el
“Apéndice A”), al conector macho de 9 pins de nuestro servidor (elemento 10), al conector hembra de nuestro ingenio, según observamos en el gráfico 2.6 y la tabla 2.2.
página
22
Robot dispensador para MSDN Vídeo
>>
CONEXIÓN A EL SERVIDOR
gráfico 2.6
1
2
3
4
5
6
Componente
Elemento
Componente
Conector del cable de alimentaBotón/indicador LED de ID de
7
ción
la unidad
Indicador LED de fuente de ali8
Conector de vídeo
mentación
Compartimiento para fuente de
alimentación redundante de cone9
Conector de puerto paralelo
xión en caliente (opcional)
Conector Ethernet RJ-45
10
Conector de puerto serie
Pestillos extraíbles del conector
11
Conector del teclado
SCSI
Conectores del puerto USB (2)
12
Conector del ratón
tabla 2.2
Componentes del panel posterior
página
Elemento
23
capítulo
3
La aplicación
OSI Layer 7. La aplicación
Una vez establecidos los parámetros de comunicaciones y antes de intercambiar información alguna, será necesario abrir el puerto. Esta operación de apertura es exclusiva, pues una vez efectuada, la aplicación toma el control del puerto de
comunicaciones y, a diferencia de otros recursos del sistema, ninguna otra aplicación podrá utilizarlo hasta que nuestra aplicación lo haya liberado.
El envío de datos es relativamente sencillo; anteriormente hablamos de envío de
un byte, sin embargo, en una comunicación entre dispositivos, hablaremos de tramas.
Las tramas son conjuntos de bytes que conforman bloques, que a su vez conforman
paquetes de información.
Nuestra dificultad “asíncrona” se produce al efectuar una transmisión de datos, vinculada a una respuesta. Efectivamente cuando nosotros solicitamos información a un dispositivo, esperamos una respuesta, he aquí donde se complica nuestra tarea. Deberemos
contemplar diversas situaciones inherentes a este tipo de comunicaciones, tales como:
¿El otro dispositivo recibió la petición?,
¿He recibido algún dato, en respuesta?,
¿Porque no contesta?,
¿Si no contesta cuando vuelvo a preguntar?,
¿Los datos recibidos están libres de errores?...
Como veréis, una simple pregunta/respuesta, puede llenarse de interrogantes,
cuando los datos no llegan... o llegan de manera incorrecta.
página
¿Alguien desconecto el cable?,
¿Estará averiado el Entregon+?,
¿Se fue la Luz?
25
<<
Robot dispensador para MSDN Vídeo
En ese sentido, no es cuestión de ver las cosas desde el lado complicado sino todo
lo contrario, se trata de conocer el medio y su problemática para poder construir una
aplicación robusta y eficiente. Sólo desde esta perspectiva conseguiremos enfocar
nuestra aplicación de una manera adecuada.
Tratamiento de eventos, recepción y errores
Sería una equivocación plantear nuestra aplicación sin la ayuda de eventos y a base
de bucles en espera de caracteres. El tratamiento de los eventos de recepción y errores va
a permitir gestionar de una manera desatendida las respuestas de nuestras peticiones. El
nuevo espacio de nombres System.IO.Port.SerialPort dispone de los eventos ReceivedEvent
y ErrorEvent que nos permitirá construir una sencilla pero sólida estructura, que gestione
la entrada y salida de tramas. Veamos el siguiente ejemplo:
EJEMPLO
gráfico 3.1
página
26
Robot dispensador para MSDN Vídeo
>>
System.IO.Ports ¡El Espacio!
Detalle del espacio de nombres:
Clases
SerialErrorEventArgs
SerialPinChangedEventArgs
SerialReceivedEventArgs
SerialReceivedEventArgs
Descripción
Especifica los argumentos enviados al evento ErrorEvent.
Especifica los argumentos enviados al evento PinChangedEvent.
Representa el recurso del puerto serie.
Especifica los argumentos enviados al evento ReceivedEvent.
Enumeraciones
Descripción
Handshake
Especifica el protocolo de control usado para establecer al objecto
SerialPort en las comunicaciones con el puerto serie.
Parity
SerialErrors
SerialPinChanges
SerialReceived
StopBit
Especifica el tipo de paridad utilizada en el objeto SerialPort.
Especifica los errores que pueden ocurrir en el objecto SerialPort.
Especifica cambios de señal detectados en el objecto SerialPort.
Especifica los caracteres que se han recibido en el puerto serie.
Especifica los bits de parada usados en el objecto SerialPort.
Delegados
Descripción
Representa el método que manipula el evento error del
SerialErrorEventHandler
SerialPort.
Representa el método que manipula el cambio de señales
SerialPinChangedEventHandler
de SerialPort.
Representa el método que manipula el ReceivedEvent
SerialReceivedEventHandler
del SerialPort.
tabla 3.1
Información del Longhorn SDK Beta 1
página
1
Espacio de nombres para la utilización de los puertos serie. Framework 2.01
27
capítulo
4
Programando
Nuestra primera aplicación
Bien, una vez adquiridos los conocimientos básicos teóricos, llega la hora de
ponerlos en práctica.
Nos concentraremos en la clase SerialPort. Esta clase, del espacio de nombres
System.IO.Ports, aglutina todas las funciones necesarias para manejar el puerto de
comunicaciones.
Una lección bien aprendida para los que trabajamos en comunicaciones, es realizar una simple operación de envío/recepción antes de dotar de cualquier otra complejidad a nuestro proyecto. La función de este laboratorio, es la de recorrer y verificar el correcto funcionamiento de las tres capas del modelo OSI, implicadas en nuestra aplicación.
Objetivo
Una vez completado este laboratorio seremos capaces de:
•
•
•
•
•
Abrir un puerto serie de nuestro servidor.
Configurar y diferenciar los diferentes puertos de nuestra máquina.
Escribir (enviar) información al puerto.
Leer el búfer de caracteres recibidos.
Recuperar los caracteres del búfer de recepción.
Preparación y requerimientos
página
Está claro que para poder efectuar este laboratorio, deberemos disponer de
un equipo con un mínimo de un puerto serie y el conector detallado en el gráfico
2.3 (capítulo 2, “El puerto serie”).
29
<<
Robot dispensador para MSDN Vídeo
Abrir una instancia de Visual Studio 2005 y empezar con un simple proyecto de
Windows Forms, añadiendo una etiqueta y un botón (label1, button1), según sigue en
la figura 4.1:
figura 4.1
En Visual Studio 2005, tenemos el código del diseñador separado del código del
formulario, por lo tanto nos interesa editar el código del formulario (Form1.vb)… [F7]
para los avanzados.
A continuación declararemos nuestro objeto de acceso al puerto serie Puerto
como clase de System.IO.Ports.SerialPort.
Para darle un aspecto más organizado, podemos incluir tres regiones. Es de
suponer que la mayoría de vosotros sabéis que directiva #Region, nos ayuda a agrupar
el código (ver el fuente 4.1).
Su aspecto sería el de la figura 4.2.
página
30
Robot dispensador para MSDN Vídeo
>>
#Region "Carga / Descarga del Formulario"
#End Region
.
.
#Region "Envio / Recepcion de tramas"
. etc.
Fuente 4.1
figura 4.2
Lo primero es ocuparnos de las tareas a realizar en tiempo de carga/descarga del
formulario (ver figura 4.3).
Dos operaciones aparentemente simples: abrir el puerto en el momento de carga
del formulario y, evidentemente, cerrarlo al descargar el formulario.
página
Con esto ya casi estamos a punto de conseguir enviar un dato, sólo nos falta la
orden de envío o escritura, para ello asociaremos el evento click del button1 a la acción
de escribir una trama.
31
<<
Robot dispensador para MSDN Vídeo
figura 4.3
Al pulsar el button1 llamaremos a la función WriteLine de nuestro Puerto (ver
figura 4.4).
Bien, ¡ha llegado el momento!, pulsaremos [F5]. Una vez compilado, aparecerá cuadro de diálogo del formulario en ejecución…
OPPSS… nuestro primer mensaje “COM1:
does not exist”… (ver figura 4.5) ¿Como que
does not exist!?
figura 4.4
página
32
figura 4.5
Robot dispensador para MSDN Vídeo
>>
Verifiquemos las salidas serie de nuestro equipo. Como veis, olvidamos de verificar la asignación de nuestros puertos en el equipo.
Vayamos al “administrador del equipo”/“administrador de dispositivos”, situémonos en el apartado de “Ports” y veamos…
figura 4.6
Obviamente COM1
OpenSerialPort("COM3").
no existe, entonces deberemos cambiar nuestra instrucción de
De nuevo pulsamos [F5]. Y de nuevo ¡OPPSS!
figura 4.7
¡El puerto está siendo utilizado por otra aplicación!
página
A simple vista puede parecer una tontería, pero obviar este tipo de detalles, a
menudo nos acarrea un montón de quebraderos de cabeza.
33
<<
Robot dispensador para MSDN Vídeo
Vamos probar con el COM4, ¡parece ser que todo va a ir bien!
figura 4.8
Pero la ilusión se va a desvanecer tan rápido como pulsemos el Button1… pues,
¡no ocurre nada!
Si aparentemente no ocurre nada, lo cierto es que han pasado un montón de
cosas, pues realmente la cadena “¡Hola Mundo!” ha sido enviada a nuestro controlador serie que con una escrupulosa paciencia ha seriado todos sus bits convirtiéndolos
en voltajes a la salida del conector del COM4.
Eso será muy bonito en teoría, pero el pragmatismo me obliga a decir que si no
lo veo, no lo creo.
Enchufaremos el conector de pruebas, a nuestra salida serie asignada (en nuestro laboratorio, COM4), este conector actúa como bucle, así pues cualquier dato enviado será inmediatamente recibido.
Deberemos también añadir un temporizador timer arrastrándolo de la caja de
herramientas (toolbox) a nuestro formulario form1.
En el Sub Form_Load, añadiremos dos líneas de inicialización del timer de manera
que leamos:
'Ejecutar cuando se Carga el formulario
Private Sub Form1_Load(ByVal sender As Object, ByVal e As . . .
Try
'Abrir el puerto numero 3
Puerto = My.Computer.Ports.OpenSerialPort("COM4") 'Llamar constructor
...
Timer1.Interval = 1000
Timer1.Enabled = True
...
...
Fuente 4.2
página
34
Robot dispensador para MSDN Vídeo
>>
En la región de Auxiliares añadiremos el siguiente código:
#Region "Rutinas Auxiliares"
'disparo de Tareas a realizar cada Segundo
Private Sub Timer1_Tick(ByVal sender As System.Object, ByVal e As
...system.EventArgs) Handles Timer1.Tick
'
Label1.Text = Puerto.BytesToRead.ToString
'Leer nº bytes recibidos
End Sub
Fuente 4.3
Pulsemos de nuevo [F5], cliqueando el button1... Veamos ¿qué ocurre?
figura 4.9
Increíble, ¡tengo 12 bytes para leer en el búffer!, realmente la cadena “¡Hola
Mundo!" ha dado la vuelta… y ¡a la vista esta!... en menos de ochenta días.
Bien, no lo demoremos más, vayamos a por ellos, modifiquemos nuestro código.
Para poder leer los caracteres recibidos en el búfer, verificaremos a cada latido del
timer si nuestro Puerto tiene bytes para leer. Puerto.BytesToRead, en cuyo caso procederemos a su recolección con la instrucción Puerto.ReadExisting, convirtiéndola en un
string para poder pasar su valor al texto de la Label1 (ver fuente 4.4 y figura 4.10).
¡Conseguido!, pero…
página
Hemos mandado una trama por el puerto serie, su bucle nos ha reenviado la
información, la hemos leído y visualizando en la etiqueta Label1.
35
<<
Robot dispensador para MSDN Vídeo
#Region "Rutinas Auxiliares"
'disparo de Tareas a realizar cada Segundo
Private Sub Timer1_Tick(ByVal sender As System.Object, ByVal e As System.Ev
'
'verificaremos que tenemos bytes para leer
If Puerto.BytesToRead > 0 Then
Label1.Text = Puerto.ReadExisting.ToString
'Leer bytes recibidos
End If
End Sub
Fuente 4.4
figura 4.10
Sin embargo, estamos muy lejos de una verdadera aplicación. Simplemente
hemos conseguido el objetivo de nuestro primer laboratorio: ¡verificar el circuito de
comunicaciones ISO/OSI a nivel de las tres capas ya es mucho!, pero no olvidemos
que no estamos siguiendo un esquema de gestión de eventos. Espero pues que me
acompañéis en el siguiente laboratorio.
página
36
capítulo
5
Programando con técnica
Programando con técnica
El laboratorio anterior nos demuestra que somos capaces de hacerlo, pero en el
siguiente vamos a demostrar que además somos capaces de hacerlo bien.
Siguiendo el organigrama descrito en el gráfico 3.1 (capítulo 3, “La aplicación”), abordaremos el desarrollo desde un planteamiento más técnico. De
hecho ninguna aplicación de comunicaciones que se precie, comprobará la
recepción de datos usando un temporizador que verifique si hay caracteres para
procesar; esto último podría verse como una técnica, pero está claro que no es
demasiado técnico.
En un medio donde lanzamos nuestras peticiones de comunicaciones a una nube,
la cual nos devuelve asíncronamente el resultado o la respuesta y de una manera
impredecible, se tercia utilizar los eventos de recepción. A pesar del miedo y desconfianza que a priori puedan suscitarnos, el hecho es que el uso de eventos nos permitirá construir una fiable y robusta aplicación de comunicaciones.
El anterior laboratorio era cuidadoso en añadir al código regiones y comentarios ricos, que nos ayuden a su comprensión. Este laboratorio, más técnico, va a
continuar con ellos pero se concentrará en la ejecución y la técnica, más que en la
forma y descripción.
Empezaremos de nuevo iniciando Visual Studio 2005, crearemos un nuevo proyecto de Windows Application, añadiendo tres etiquetas Labels, dos barras de progreso
progressbar y un botón button (ver figura 5.1).
página
La primera etiqueta Label1, nos servirá para visualizar los caracteres recibidos, las
dos barras de proceso juntamente con las etiquetas Label2 y Label3 nos indicarán la
cantidad de bits transmitidos y recibidos. El botón Button1' simplemente para iniciar
o detener la transmisión en cadena.
37
<<
Robot dispensador para MSDN Vídeo
figura 5.1
Empezando con el segundo Lab
El siguiente paso será añadir a nuestro proyecto una clase que gestione nuestras
comunicaciones, por lo tanto, desde el explorador de soluciones, añadiremos un
nuevo ítem Class.
Cambiaremos el nombre de la clase Class1 a Puerto ... Public Class Puerto. Para
hacerlo fácil, vamos a obviar el control y la gestión de excepciones, así como otras formalidades aplicadas por cualquier buen programador.
Nuestra clase empezará definiendo tres variables que contendrán la trama recibida, así como dos enteros que contarán los bits enviados y recibidos:
Public Class Puerto
Public Recepcion As String
'Cadena con los caracteres recibidos
Public enviados As Integer = 0 'Numero de bits enviados (11 bits por Byte)
Public Recibidos As Integer = 0 'Numero de bits recibidos (11 bits por Byte)
Fuente 5.1.
Crearemos nuestro enlace receptor de los eventos del System.IO.Ports.SerialPort,
además de declarar nuestro evento de Respuesta para avisar a nuestras instancias de que
se ha producido una recepción:
página
38
Robot dispensador para MSDN Vídeo
Public Event Respuesta()
WithEvents Serie As System.IO.Ports.SerialPort
>>
'Evento disparo respuesta
'Eventos del SerialPort
Fuente 5.2
New
Terminaremos de completar nuestra clase con las subfunciones de Enviar, Recibir,
y Finalize. Su aspecto será:
'
'Enviar una trama (el Chr(13) es el carácter que identifica el fin de trama)
Public Sub Enviar(ByVal Trama As String)
Serie.WriteLine(Trama + Chr(13))
'La trama sera enviada al objeto serie
enviados += Trama.Length * 11
'necesito 11 bits para mandar un byte
End Sub
'
'Recibir una trama
Public Sub Recibir(ByVal sender As Object, ByVal e As ...
... system.IO.Ports.SerialReceivedEventArgs) Handles Serie.ReceivedEvent
Recepcion = Serie.ReadExisting.ToString
'Leer los caracteres recibidos
Recibidos += Recepcion.Length * 11
'contabilizarlos para estadistica
RaiseEvent Respuesta()
'Lanzar el evento de recepcion
End Sub
'
'Abrir el puerto
Public Sub New()
'constructor del objecto serie
Serie = My.Computer.Ports.OpenSerialPort("COM4", 19200)
End Sub
'
'Liberar el puerto
Protected Overrides Sub Finalize()
'cerrar el objecto serie
Serie.Close()
MyBase.Finalize()
End Sub
End Class
página
Fuente 5.3
39
<<
Robot dispensador para MSDN Vídeo
Destaquemos la importancia de la función Recibir en nuestra clase. ¿Veis cómo
es la receptora de Serie.ReceivedEvent?, la aplicación que utilice nuestra clase podrá
continuar su ejecución a pesar de tener una petición pendiente de responder, por lo
tanto, estará utilizando el puerto serie de una manera desatendida. Nuestra clase recibirá el evento una vez se haya completado una recepción y a continuación lo notificara a sus instancias, disparándoles el evento respuesta… ¡Maravilloso!
Sobre el resto sólo comentar, que en New creamos el objeto serie que nos dará
acceso a todos sus métodos, eventos y propiedades, en finalize liberamos el puerto
serie y que Enviar es tan simple como hacer escribir la trama al objeto Serie.
Pasemos ahora a la funcionalidad en casa del cliente Class Form.
He elegido un ejemplo que trabaje un par de conceptos muy importantes, los
eventos (events) y los hilos (threats).
Existen dos maneras de afrontar los cambios en el versionado de lenguajes:
1) A bofetones (sensación de torpe, siempre buscando y sin haber perdido nada).
2) Esperar que los demás se peguen los bofetones y te lo cuenten (sensación
“seguro de ti mismo”, imagen limpia, siempre con el traje impecable).
Como ya sabéis Visual Basic .NET, incorpora una serie de avances cualitativos,
inherentes a los lenguajes orientados a objeto, además del mejorado manejo de eventos y entre otras mil, poder utilizar hilos para la ejecución; dicho esto me confieso un
aterrizado miembro del grupo 1, y es un orgullo contribuir en la rápida adhesión a esta
tecnología para los del grupo 2.
A los acostumbrados a las anteriores versiones de Visual Basic, no nos termina
de encajar del todo este modelo; antes de todo se ejecutaba sobre un plano, sin embargo con el nuevo Framework, podemos crear hilos hijos o llamar a otros hilos…
Menuda potencia… pero congones qué leches con lo de los hilos... lo único que yo
quiero es recibir la llamada de un evento y leer sus datos.
Bien aquí tenemos la parte formal, según nuestro modelo de ejecución disponemos de un árbol de hilos del que cuelgan nuestros procesos, una peculiaridad
de los formularios es que se ejecutan en un hilo, lo que no impide que a partir de
nuestro hilo, creemos otros hilos de ejecución que cuelguen del nuestro “hilos
hijos”, la interacción con ellos resulta ciertamente asequible. Sin embargo, las
complicaciones llegan solas. Acabamos de crear una clase que recibe la llamada de
un evento que se ejecuta en el espacio delegado de SerialReceivedEventHandler, o
sea, de otro hilo.
página
40
Robot dispensador para MSDN Vídeo
>>
La pregunta sería ¿y a qué viene todo esto? La respuesta sería:
“illegal cross-threat operation, control accessed from a threat other than the threat it was
created on”
Resumiendo: ¡que no podemos tocar con los hilos de otra guitarra!
Muchas veces me sorprendo a mí mismo intentando dar explicación a todo esto
de la OOP y siempre termino igual, pensando en el ensamblador; al fin y al cabo es
lo que el procesador entiende, una secuencia que recorre la memoria de arriba hasta
abajo.
Rápidamente me repongo, con ánimo y ayuda entiendo que efectivamente estas
son excelencias que requieren que nuestra mentalidad se adapte a los tiempos, no por
nada se inventaron los delegados. ¡Ya va siendo hora de que los usemos!
La mayoría de nosotros tiene un vago concepto sobre los delegados y su aplicación. Creo que este es un buen ejemplo sobre todo para entender el modelo de ejecución de nuestras aplicaciones, ¡vamos pues a coger el hilo por sus extremos!
En las declaraciones deberemos crear ahora una instancia al objeto puerto:
Public Class Form1
'
Private Comunica As Puerto = New Puerto 'Instancia nuestra clase Puerto
Private InicioFin As Boolean = False
'Encadenar las peticiones (si/no)
Private DatosRecibidos As String
'Datos recibidos
Fuente 5.4
Ahora el famoso delegado que nos servirá para el Invoke que permitirá al control
recibir datos desde un threat diferente al que fue creado.
'
'delegado para manejar la recepción de datos desde un 'threat' externo
Delegate Sub Refrescar()
página
Fuente 5.5
41
<<
Robot dispensador para MSDN Vídeo
Construimos un temporizador con el que controlaremos la actualización de
datos en pantalla una vez cada segundo.
'
'temporización de un segundo
WithEvents segundos As System.Windows.Forms.Timer = New System.Windows.Forms.Timer
Fuente 5.6
En tiempo de carga del formulario...
'
'Preparamos el entorno
Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs ...
'dirección de ejecución para el evento 'Respuesta' una vez recibidos los datos
AddHandler Comunica.Respuesta, AddressOf recibedatos
segundos.Interval = 1000
'Fijar el intervalo de disparo del temporizador
segundos.Enabled = True
'Temporizador en Marcha
Me.ProgressBar1.Maximum = 19200 'Fijar el valor maximo del progress bar 1
Me.ProgressBar2.Maximum = 19200 'Fijar el valor maximo del progress bar 2
End Sub
Fuente 5.7
Cuando nos pulsen el botón...
'
'Cuando nos pulsen el botón
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.Eve ...
Comunica.Enviar(System.DateTime.Now)
'Enviamos la fecha/Hora
InicioFin = Not InicioFin
'Memorizamos Inicio/Fin
End Sub
Fuente 5.8
página
42
Robot dispensador para MSDN Vídeo
>>
Cada segundo (tick del timer)...
'
'Cada Segundo efectuaremos un refresco de la información en pantalla
Public Sub tiempo(ByVal sender As Object, ByVal e ...) Handles segundos.Tick
'Visualizar el estado en el texto del botón
If InicioFin Then Button1.Text = "Fin Bucle" Else Button1.Text = "Inicio Bucle"
'Actualizar los valores actuales / estadísticas por segundo
ProgressBar1.Value = Comunica.enviados
Label2.Text = ProgressBar1.Value.ToString
ProgressBar2.Value = Comunica.Recibidos
Label3.Text = ProgressBar2.Value.ToString
Comunica.enviados = 0
Comunica.Recibidos = 0
End Sub
Fuente 5.8
Esta es la función que recibe el disparo del evento de trama recibida. Aquí es
donde utilizando el metodo invoke, llamamos al delegado refrescar que apunta a la
función actualizar_datos, la cual simplemente mueve el valor de DatosRecibidos a su
propiedad de texto.
'
'Disparo de la instancia del 'Puerto' llamada Comunica, cuando recibe una trama
Private Sub recibedatos()
DatosRecibidos = Comunica.Recepcion 'leer los datos recibidos
'
'invocar al delegado que rellene con los datosRecibidos el texto del label1
Me.Label1.Invoke(New Refrescar(AddressOf actualizar_Datos))
'
'enviar otra trama, Si la trama contiene el caracter CR y queremos bucle.
If DatosRecibidos.Contains(Chr(13)) And InicioFin Then
DatosRecibidos = ""
'Limpiar los datos recibidos
Comunica.Enviar(System.DateTime.Now)
End If
End Sub
'
'delegado para leer y asignar los datos de otro hilo
Public Sub actualizar_Datos()
Me.Label1.Text = DatosRecibidos
End Sub
End Class
página
Fuente 5.9
43
<<
Robot dispensador para MSDN Vídeo
Si intentáramos asignar directamente el valor de DatosRecibidos en la función
recibeDatos.
Me.Label1.Text = DatosRecibidos,
seríamos obsequiados con la original excepción:
figura 5.2
Uff... no sé si atreverme a pulsar [F5]... ¡Bien!
figura 5.3
Ahora pulsemos el botón de “Inicio Bucle”… ¡Bien!
figura 5.4
página
44
Robot dispensador para MSDN Vídeo
>>
¿Que es lo que está sucediendo?
Después de tanto esfuerzo hemos conseguido que pulsando el botón “Inicio Bucle”,
mandemos una trama que contiene la fecha y la hora a la salida del puerto serie, el bucle
en el conector del mismo hace que se reciban instantáneamente los mismos datos enviados, pero con la peculiaridad de que el retorno para la recepción se realiza disparando un
evento desde el delegado receivedevent, utilizando la técnica de invoke para poder rellenar
el texto del Label1 desde un hilo diferente al que fue creado.
Aunque a primera vista complejo, si lo comparamos con las técnicas de overlap
usando llamadas a las API32, esto es ¡simplemente genial! Con cuatro líneas somos
capaces de recibir llamadas desatendidas desde el puerto serie. Para quien le parezca
poco, explicaré que la anterior versión, Visual Studio 2003, necesitaba entorno a las
mil líneas de código para hacer algo similar. Sí, he dicho bien… MIL, 1.070 para ser
exactos.
Seguro que alguno de los participantes, estará echando remiendos… los progressbar sumados dan 29.249 bits… ¡No funciona!, hemos abierto el puerto a
19.200bps, entonces ¡no cuadra!
Les explicaré que el puerto serie puede trabajar en dos modalidades, en full
duplex o half duplex. Es probable que el bucle en la salida produzca un efecto reflejo
de sobrescritura, que traducido significa el envío de una trama antes de recibir respuesta completa, por lo que antes de finalizar la recepción ya estoy enviando otra,
argumentando esta razón sería capaz de mandar al filo de 19.200 bits mientras recibo
otros tantos, pues el modo full duplex me da la posibilidad de enviar y recibir simultáneamente.
Este efecto no ocurrirá si utilizamos un dispositivo real que conteste a nuestras
peticiones.
página
Es la hora del descanso, antes de entrar en trance con nuestras librerías de acceso a nuestro Entregon+ y el programa Dispensador de Vídeo, tómense un merecido café.
45
capítulo
6
ServidorComm
La Clase
ServidorComm. Introducción a la clase
La primera tarea del siguiente laboratorio, se centrará en efectuar un análisis preliminar de nuestras necesidades, a continuación lo plasmaremos gráficamente y finalmente, siguiendo dicho esquema, abordaremos la programación
módulo a módulo.
Para entender mejor este proyecto es necesario aclarar ciertos detalles:
Durante años hemos observado que todos nuestros equipos incorporaban
unos conectores de 9 contactos (antaño 25), que usamos para enchufar los
módems que nos permitían conectarnos a Internet. Esos conectores escondían
tras de sí las comunicaciones, esa herencia se remonta a los primeros equipos de
informática personal, por lo tanto, son anteriores a la aparición de las redes locales y su gran importancia se debe a que son los responsables directos del intercambio de datos con el exterior.
Además, durante años, los puertos serie han sido el único medio disponible para
conectar cientos de pequeños dispositivos. No olvidemos: lectores de códigos de
barras, impresoras, ratones, módems, tabletas digitadoras, mini controladores, automatismos, programadores de chips..., etc.
Gracias a la rápida evolución electrónica, sobre los pares trenzados de ethernet
y las líneas digitales, hoy en día disfrutamos de conexiones entre redes locales y remotas a unas velocidades de vértigo si las comparamos con los 300 baudios de los primeros modems.
página
Os preguntareis ¿a qué viene todo esto?, la respuesta es simple: la historia de los
entornos de programación demuestran la dificultad en realizar proyectos que usarán
estos puertos serie; para explicarlo mejor os diré que era una práctica habitual para los
antiguos programadores en DOS, usar directamente técnicas de captura de los vectores de interrupción (int3/int4) del chip controlador para hacerse con la UART y ¡cier-
47
<<
Robot dispensador para MSDN Vídeo
tamente complicadísimo! Versiones posteriores de lenguajes trataban el acceso al
puerto de comunicaciones como si de un archivo se tratara. open "COM1" for radom as
# 1 con su input # 1 y su print # 1, pero lo cierto es que no eran formas demasiado
eficientes. Más tarde, las primeras versiones de entornos visuales salieron desprovistas de acceso al puerto serie, nos alivió solucionarlo usando las instrucciones out/in
atacando directamente a los controladores serie. Más tarde, con la aparición de
Win32, tuvimos la posibilidad de personalizar nuestro acceso a los puertos utilizando
llamadas a las API, su Kernel32.dll y la odiosa estructura Win32Com.DCB. La llegada de los
OCX, nos regala un control, el MSCOMM32 que, por primera vez, da un cierto respiro para
poder acceder a los puertos, ya que podemos desarrollar aplicaciones suficientemente fiables con una sencillez justa. Finalmente, la cosa se vuelve a complicar con la llegada de .NET, las primeras betas permiten explotar los puertos con el uso de streams,
pero su versión final nos deja a dos velas de nuevo y sin acceso a los puertos serie; una
vez más la única alternativa son las antiguas llamadas a las API. ¡Eso en plena era
.NET! Efectivamente, este es el motivo de tanto alboroto. ¡Visual Studio 2005 incorpora un espacio de nombres para el acceso a los puertos serie!, ni más ni menos, una
excelente noticia para todos aquellos que necesitan continuar usando dispositivos con
la herencia del puerto serie.
Después de la explicación anterior, deberíamos quedarnos descansados, presentando y describiendo la funcionalidad básica y prestaciones de este espacio de nombres. Sin embargo, vamos a tratar este caso como un auténtico reto, vamos a desarrollar una aplicación al completo en torno al uso de los puertos serie. La idea es ayudar
a dar el salto y todos conocemos las dificultades asociadas al versionado en los entornos de programación. En plena ebullición de Visual Studio 2005, muchos desarrolladores aún no han cambiado a Visual Studio 2003. A pesar de que el salto de 2003 a
2005 no es tan espectacular como el de Visual Studio 6 a 2003, vale la pena darnos la
oportunidad de iniciar nuestras futuras aplicaciones con laboratorios que abarquen
diferentes aspectos en el desarrollo, más que concretar en profundidad los aspectos
mas relevantes de la implementación de nuevas características. Creo sinceramente que
este sencillo ejemplo debe servir para perder el miedo a este increíble entorno de programación, sin duda, el mejor de todos los tiempos.
Empezaremos escribiendo un conjunto de clases que además de cubrir el acceso al
puerto serie, añade clases con operaciones tan interesantes como el acceso a bases de datos
Access, archivos XML, apuntes al registro de eventos o envío automatizado de correos
electrónicos. Cabe destacar una interesante introducción al uso de enumeraciones y
estructuras; no es entonces de extrañar que nuestra librería ServidorComm tenga mucho
de “Servidor” y algo de “Comm”.
página
48
Robot dispensador para MSDN Vídeo
>>
Nuestro proyecto…(cuaderno de carga)
Descripción
Desarrollar una clase que enlace la aplicación del dispensador de vídeo y
nuestro Entregon+ para atender a las peticiones de alquiler, venta y devolución,
intercambiando tramas serie con el formato descrito en el protocolo
DispeDataLink, utilizaremos el puerto serie local, controlaremos y supervisaremos los mensajes de incidencias, así como el estado de nuestro dispensador en
tiempo real.
Detalles
Nuestra clase deberá estar compilada en un proyecto Class Library, para
poder usar su funcionalidad desde dispensador, simplemente añadiendo una referencia a la misma.
Para poder desarrollar este laboratorio, es imprescindible disponer de un equipo
con un puerto serie y el conector descrito en el gráfico 2.3 (capítulo 2, “El puerto
serie”). Es aconsejable utilizar Windows 2003 Server o Windows XP Profesional con
SP2 y Visual Studio 2005 Beta 2.
Después de muchos años de trabajo, continuo apasionándome cada vez que
afrontamos la fase de definición del proyecto. Es emocionante sentir nuestra imaginación en un torrente desbordado de ideas que fluyen descontroladamente, dando
forma y sentido a un montón de zambullidos neuronales, que poco a poco van componiendo ese fantástico puzzle al que terminamos llamando solución.
Una primera imagen de nuestro trabajo, es diferenciar sus dos elementos
esenciales: el mecanismo Entregon+ con su equipo controlador y nuestra aplicación. A pesar de las grandes habilidades, tanto mecánicas como de control de
nuestro Entregon+, debemos entender la importancia de esta simbiosis ya que
dichas versatilidades sólo afloraran con la ayuda de unas librerías que sean capaces de dotar al conjunto de la funcionalidad suficiente, para exponer toda la gestión del conjunto de una manera simple y eficiente.
Dediquemos unos minutos a recorrer visualmente la representación gráfica de
nuestro objetivo (ver gráfico 6.1).
página
A primera vista puede parecer que no olvidamos nada y como dibujito no está
nada mal, sin embargo, un análisis más detallado nos conducirá a descubrir que debemos ser mucho más rigurosos y efectuar una descripción más concreta.
49
<<
Robot dispensador para MSDN Vídeo
ENLACE DE COMUNICACIONES–DISPENSADOR/ENTREGON+
gráfico 6.1
Esta descripción detallada debe conducirnos directamente a la definición de las
clases necesarias que conformaran nuestras librerías.
Me gustaría insistir en el hecho de ser minuciosamente escrupulosos al diseñar
una librería en la que se fundamentara el éxito de las aplicaciones que la exploten. No
podemos dejar nada al azar, debemos ser capaces de construir un conjunto de funcionalidades “mantenimiento cero”.
página
50
Robot dispensador para MSDN Vídeo
>>
Nuestra librería deberá contener una clase capaz de reportar todos los mensajes
al eventlog del sistema, deberá incluir una clase tenga en cuenta las estructuras de
intercambio de datos, otra que efectúe la simulación del ingenio, también diseñaremos una clase que encapsule las notificaciones de incidencias por correo electrónico
y, ¡como no!, naturalmente deberá gestionar el puerto serie… Por lo pronto, un montón de cosas. Un último detalle: no estaría mal que además guardáramos todos los
valores de configuración y trabajo dentro del app.config de cada aplicación, que como
ya sabéis es nuevo en Visual Studio 2005, por lo que podríamos sugerir agrupar bajo
los siguientes módulos:
figura 6.1
Todo, bajo el nombre de la librería ServidorComm.
Antes de adentrarnos en tan ardua tarea, demos una ojeada a las diferentes maneras de conectar nuestro Entregon+ a una aplicación de dispensador, así como para
hacer un repaso a nuestro uso del protocolo DispeDataLink.
El protocolo DispeDataLink, está fundamentado en el intercambio de tramas
usando el juego de caracteres ASCII.
Está diseñado en base a una comunicación enlace anfitrión. Este tipo de enlaces se caracteriza por el intercambio de tramas bajo demanda del anfitrión (Host o
Master), el resto de componentes de la red se consideran esclavos y su única misión
es contestar a las peticiones del Maestro o Anfitrión, no toman nunca la iniciativa en
intercambio alguno. La topología de estas redes acostumbra a no sobrepasar los 32
esclavos y ello es debido a las limitaciones de impedancia de los dispositivos electrónicos. Normalmente RS485. En la actualidad existen numerosos conversores de
señal RS232 a RS485.
página
En nuestro caso, Entregon+ y el protocolo son esclavos a nivel de aplicación. Por lo
que podemos enlazar un único dispensador de vídeo a varios Entregon+ (ver gráfico 6.2).
51
<<
Robot dispensador para MSDN Vídeo
gráfico 6.2
También utilizando ciertas técnicas avanzadas, podríamos direccionarlos a través
de un servidor, mezclando diferentes redes.
Quizás estas técnicas puedan ser objeto de desarrollo utilizando las librerías de
remoting, ¡si es que después de WCF (antes Indigo) no se rompe éste y puedo continuar escribiendo el libro!
gráfico 6.3
página
52
capítulo
7
ServidorCom.dll
Nuestro laboratorio
ServidorCom.dll
Por fin nos adentraremos en los placeres de la codificación, no sin antes recordar que necesitaremos tener muy cerca la descripción de todos los formatos y códigos
que intervienen en nuestros procesos. Cuanto más detallados sean nuestros modelos
de formato y su descripción, más fácil será nuestra labor posterior.
Diferentes asociaciones de usuarios advierten que leer código “a pelo” puede producir somnolencia y aburrimientos de
diversas índoles. Queda terminantemente prohibido leer este
material mientras se conduce o utiliza maquinaria pesada. En
caso de sufrir cualquiera de los síntomas descritos, no visite
al médico ni tampoco al farmacéutico, pues está considerado
como un “cuadro sintomático habitual”. Si por el contrario la
lectura del código descrito a continuación le mantiene despierto, le impide conciliar el sueño, o le provoca ganas de
POgramar, resérvese urgentemente una sesión de reflexoterapia con un especialista que sea de su confianza.
página
nota
Nota del equipo de desarrollo: ésta es la versión lite shared
opensource freeware en la que el equipo técnico de desarrollo
sólo ha implementado tres operaciones básicas. Para la versión completa full equipment diríjase al departamento comercial, o bien copie/modifique y distribúyanos libremente su
aportación a: [email protected].
advertencia
A continuación vamos a detallar el formato de las tramas que intercambiaran el
Entregon+ y nuestro sistema, es importante entender su estructura, pues será hilo conductor que sustentara nuestra aplicación.. “son el corazón de nuestro proyecto”.
53
<<
Robot dispensador para MSDN Vídeo
Formato tramas envío/recepción
El protocolo DispeDataLink, está fundamentado en el intercambio de tramas
usando el juego de caracters ASCII.
gráfico 7.1
página
54
Robot dispensador para MSDN Vídeo
>>
Petición/respuesta entrega de cápsula - 01
Para poder cumplir los requisitos del protocolo, deberemos fundamentarnos en
la siguiente estructura de datos:
Sinónimo Tipo Posiciones Rango
Descripción
#
ASCII
1
Na Trama de inicio
UD
ASCII
2
01-32 Unidad de destino
UO
ASCII
2
01-32 Unidad de origen
COD
ISBN
NIFCIF
CRC
$
CR
ASCII
ASCII
ASCII
ASCII
ASCII
ASCII
2
13
9
2
1
1
00-99
X(13)
X(9)
00-FF
Na
Na
Código de operación
Código ISBN para encapsular y/o expulsar
NIF usuario
Cálculo para la verificación de redundancia cíclica
Fin de trama
Fin de transmisión
tabla 7.1
La siguiente trama solicita la expulsión del DVD con ISBN=0809101112134 y
el NIF=77666555X.
Petición
#010201080910111213477666555XFF$(Cr)
Sinónimo Tipo Posiciones Rango
Descripción
#
ASCII
1
Na Trama de inicio
UD
ASCII
2
01-32 Unidad de destino
UO
ASCII
2
01-32 Unidad de origen
COD
CRC
$
CR
ASCII
ASCII
ASCII
ASCII
2
2
1
1
01-32
00-FF
Na
Na
Código de fin1
Cálculo para la verificación de redundancia cíclica
Fin de trama
Fin de transmisión
tabla 7.2
Respuesta
1
CRC = Cálculo de redundancia cíclica
CR = Carriage return
página
#020100FF$(Cr)
55
<<
Robot dispensador para MSDN Vídeo
Petición/respuesta introducción de cápsula - 02
Para poder cumplir los requisitos del protocolo, deberemos fundamentarnos en
la siguiente estructura de datos:
Sinónimo Tipo Posiciones Rango
Descripción
#
ASCII
1
Na Trama de inicio
UD
ASCII
2
01-32 Unidad de destino
UO
ASCII
2
01-32 Unidad de origen
COD
ASCII
2
00-99 Código de operación
CRC
$
CR
ASCII
ASCII
ASCII
2
1
1
00-FF Cálculo para la verificación de redundancia cíclica
Na Fin de trama
Na Fin de transmisión
tabla 7.3
El código expulsión es “02”, entonces para componer una trama que acepte la
introducción de la cápsula deberemos enviar la siguiente trama:
Petición
#010202FF$(Cr)
Sinónimo Tipo Posiciones Rango
Descripción
#
ASCII
1
Na Trama de inicio
UD
ASCII
2
01-32 Unidad de destino
UO
ASCII
2
01-32 Unidad de origen
CF
ASCII
2
00-99 Código de fin1
ISBN
NIFCIF
ASCII
ASCII
13
9
X(13) Código ISBN para encapsular y/o expulsar
x(9) NIF usuario
CRC
$
CR
ASCII
ASCII
ASCII
2
1
1
00-FF Cálculo para la verificación de redundancia cíclica
Na Fin de trama
Na Fin de transmisión
tabla 7.4
Respuesta
#020100080910111213477666555FF$(Cr)
página
56
Robot dispensador para MSDN Vídeo
>>
Petición/respuesta estatus del sistema - 03
Para poder cumplir los requisitos del protocolo, deberemos fundamentarnos en
la siguiente estructura de datos:
Sinónimo Tipo Posiciones Rango
Descripción
#
ASCII
1
Na Trama de inicio
UD
ASCII
2
01-32 Unidad de destino
UO
ASCII
2
01-32 Unidad de origen
COD
ASCII
2
00-99 Código de operación
CRC
$
CR
ASCII
ASCII
ASCII
2
1
1
00-FF Cálculo para la verificación de redundancia cíclica
Na Fin de trama
Na Fin de transmisión
tabla 7.5
El código expulsión es “03”.
Petición
#010203FF$(Cr)
Sinónimo Tipo Posiciones Rango
Descripción
#
ASCII
1
Na Trama de inicio
UD
ASCII
2
01-32 Unidad de destino
UO
ASCII
2
01-32 Unidad de origen
CF
ASCII
2
00-32 Código de fin1
BIE
ASCII
4
CRC
$
CR
ASCII
ASCII
ASCII
2
1
1
0-FFFF 16 Bits de estatus (indicadores de incidencias)
00-FF Cálculo para la verificación de redundancia cíclica
Na Fin de trama
Na Fin de transmisión
tabla 7.6
Respuesta
página
#020100EEEEFF$(Cr)
57
<<
Robot dispensador para MSDN Vídeo
Códigos retorno y operación
Código
Descripción
00
Operación de comunicaciones completada con éxito
99
Operación de comunicación incompleta
tabla 7.7
Código
Descripción
01
Petición de expulsión de cápsula
02
03
Petición de Introducción de cápsula
Petición de banderas de estado controlador
10
11
Alertas
Todo Bien
Existe una alarma en el sistema leer estatus
20
Avisos
Producto solicitado en stock mínimo
21
Último producto
22
Últimas cápsulas
23
Errores intermitentes en el dispensador
24
No es posible localizar el producto
25
Imposible recoger producto
26
Imposible apilar cápsula en almacén
27
Eje del cabezal localizador estropeado
28
Atasco en el circuito de distribución
29
Falla el lector de etiquetas electrónicas
30
Falla la lectura de la etiqueta electrónica
tabla 7.8
página
58
Códigos de fin de operación
Códigos datos operación
Robot dispensador para MSDN Vídeo
>>
Códigos Indicadores de Incidencias
Hex
Binario
Descripción
0000
0000 0000 0000 0000 Sin incidencias
0001
0000 0000 0000 0001 Avería en la placa controladora 16C450 # 1
0002
0000 0000 0000 0010 Avería en la placa controladora 16C450 # 2
0004
0000 0000 0000 0100 Fallo en el procesador # 1
0008
0000 0000 0000 1000 Fallo en el procesador # 2
0010
0000 0000 0001 0000 Avería en el eje del cabezal
0020
0000 0000 0010 0000 Avería en la pinza dispensadora
0040
0000 0000 0100 0000 Lector de etiquetas averiado (lectura/escritura)
0080
0000 0000 1000 0000 Disparo del detector anti vandálico
0100
0000 0001 0000 0000 Almacén de cápsulas vacío
0200
0000 0010 0000 0000 Almacén de producto vacío
0400
0000 0100 0000 0000 Suciedad en la óptica del láser posicionador
0800
0000 1000 0000 0000 Paro en el ventilador del sistema de refrigeración
1000
0001 0000 0000 0000 Atasco en el dispensador de salida
2000
0010 0000 0000 0000 Atasco en el dispensador de entrada
4000
0100 0000 0000 0000 Atasco en pinza dispensadora
8000
1000 0000 0000 0000 Sobretemperatura del sistema ALARMA GRAVE
tabla 7.9
Cálculo del CRC (pasos previos)
El Cyclic Redundancy Check es un mecanismo de verificación de error, que permite a la aplicación determinar si la trama recibida está libre de errores.
página
Este checksum es generado por el remitente, sin embargo, el destinatario debe
volver a efectuar el cálculo del CRC con los datos recibidos; es evidente que si el cálculo del CRC del remitente coincide con el del destinatario, podemos casi afirmar que
los datos recibidos están libres de errores.
59
<<
Robot dispensador para MSDN Vídeo
Es necesario precisar que este sistema no es efectivo en la detección del 100% de
errores que se produzcan, aunque tampoco son frecuentes los errores en este tipo de
transmisiones. Por lo que podríamos considerar que para nuestro cometido es más
que suficiente.
Tampoco es preciso analizar este código, será suficiente entendiendo que a partir de una cadena de caracteres ASCII efectúa una suma binaria vertical y nos devuelve dos bytes en formato ASCII que corresponden al CRC que incluiremos al final de
la trama. No olvidemos que su complejidad añadida se debe la necesidad de entregar
el resultado de una suma binaria de 16 bits en dos caracteres ASCII que como ya
sabéis son de 8 bits.
'
'
' Calcular el crc de la variable Var
'
'
Shared Function Crc(ByVal Var) As String
Dim i As Integer, j As Integer
Dim CrcTmp As Integer, Ct As Integer, Ch As Integer, Cl As Integer
Dim C1 As Integer, C2 As Integer
CrcTmp = &HFFFF
For i = 1 To Len(Var)
CrcTmp = CrcTmp Xor Asc(Mid(Var, i, 1))
For j = 1 To 8
Ct = CrcTmp And &H1
If CrcTmp < 0 Then Ch = 1 Else Ch = 0
CrcTmp = CrcTmp And &H7FFF
CrcTmp = CrcTmp \ 2
If Ch = 1 Then CrcTmp = CrcTmp Or &H4000
If Ct = 1 Then CrcTmp = CrcTmp Xor &HA001
Next j, i
If CrcTmp < 0 Then Cl = 1 : CrcTmp = CrcTmp And &H7FFF Else Cl = 0
C1 = CrcTmp And &HFF& : C2 = (CrcTmp And &H7F00) \ 256
If Cl = 1 Then C2 = C2 Or &H80
Return (Chr("&h" + Hex(C1)) + Chr("&h" + Hex(C2)))
End Function
Fuente 7.1
Este fragmento de programa esta íntegramente desarrollado y diseñado en el año 89,
por Pep. Utiliza la técnica “Lo+Retorcida”, en esos años era la única forma de proteger
intelectualmente los desarrollos, se caracterizaba porque “no había quien la entendiera”.
página
60
Robot dispensador para MSDN Vídeo
>>
Componiendo estructuras básicas
Una de las labores más agradecidas en estos, los menesteres del codificador, son
la definición de estructuras. Cuanto más precisas, definidas y concretas sean, más sencillo será conformar una fluida comunicación en el código.
Unas estructuras desordenadas desembocaran en un caos de información
difícil de manejar, con el consecuente fracaso de nuestra aplicación. En clases que
manejan protocolos son de vital importancia ya que en ellas se fundamenta el
éxito; de hecho no por menos son los cimientos de todo lo que construyamos
después.
Visual Studio 2005 sorprende por su elevada capacidad para encapsular estructuras que contengan propiedades, funciones,... es magnífico ver qué bien recompensado queda tu código cuando lo pones a trabajar.
Según la definición del protocolo DispeDataLink de Entregon+, debemos ceñirnos a las tramas definidas tanto en el envío como en la recepción. Para tal efecto generaremos una clase a la que llamaremos EntrgnEstructuras.vb, la cual va a contener el
formato exacto requerido para efectuar el intercambio de tramas entre el sistema y el
Entregon+.
El siguiente es un ejercicio de estructuras, intenta jugar con diferentes posibilidades para ofrecer al lector una buena perspectiva de su uso, por lo que se aleja de lo
estrictamente necesario. No se sorprenda si le parece una puesta en escena un tanto
exagerada.
A menudo hablamos de encapsular, pero a menudo ese termino nos confunde.
En este ejemplo entenderemos cómo cualquier protocolo empaqueta diversos bloques
de información. La unidad y distribución de estos bloques de datos en un formato
concreto listo para ser interpretado por los interlocutores, las conocemos con el nombre de TRAMA. Por lo tanto, la trama es al bit, como la unidad básica de intercambio de datos entre dispositivos.
A tal efecto, anterior a la construcción de la trama, deberemos conformar las
estructuras de los bloques de datos que constituirían el contenido de las mismas.
página
El primer bloque encapsula la cabecera de unidad destino a la que va dirigida la
trama y qué unidad a lanzado la petición. Tal como se observa, la única función del
BloqueDatos1, es devolvernos, o memorizar en formato de una cadena de dos dígitos, la
UnidadDestino y la UnidadOrigen… Como veréis ¡con el simple uso de propiedades!, realmente organizado.
61
<<
Robot dispensador para MSDN Vídeo
#Region "** Datos de Transmision Bloque 1"
'
' Encapsular estructura y funcionalidad del bloque de datos 1
' 9999 9999
' -+-- -+-' |
|
' |
+----Unidad de Origen
' +---------Unidad de Destino
'
Private Structure BloqueDatos1
Private Unidad_Destino As String
Private Unidad_Origen As String
'
'
Propiedad UnidadDestino
Public Property UnidadDestino() As String
Get
Return Unidad_Destino
End Get
Set(ByVal Value As String)
' se Admiten valores entre 00 y 32
Unidad_Destino = Format(Val(Value), "00")
End Set
End Property
'
'
Propiedad UnidadOrigen
Public Property UnidadOrigen() As String
Get
Return Unidad_Origen
End Get
Set(ByVal Value As String)
' se Admiten valores entre 00 y 32
Unidad_Origen = Format(Val(Value), "00")
End Set
End Property
End Structure
#End Region
Fuente 7.2
A continuación pasaremos a definir el segundo bloque de datos que ayudará a
conformar la trama final. Usando la misma filosofía que en el bloque 1, definimos una
propiedad para cada una de las unidades de información que van a ser intercambiadas.
También es importante ver cómo los protocolos estructuran sus capas, empaquetando información de diferentes niveles. En nuestro caso ¿por qué dos bloques de
datos?, la respuesta reza muy sencilla:
página
62
Robot dispensador para MSDN Vídeo
>>
El primer bloque de datos encapsula información a nivel de dispositivos, el
segundo tan solo contiene información a nivel de aplicación.
¿Quizás nos esté recordando a un modelo de capas como el OSI? ¿o quizás NO?
Simplemente para desmitificar un poquito y quitarnos ciertos complejos, desvelar que incluso los famosos y misteriosos protocolos TCP conforman diferentes formatos definidos y divididos en bloques para el intercambio de su información... ¡son
los hermanos mayores!
#Region "** Datos de Transmision Bloque 2"
'
' Encapsular estructura y funcionalidad del bloque de datos 2
' 99 9999999999999 999999999
' +---------+------------+---' |
|
|
' |
|
+---- NIF
' |
+----------------- ISBN
' +--------------------------- 01 - Expulsar Capsula (ISBN+CIF)
'
02 - Introducción Capsula (Retorna ISBN+CIF)
' 99
' --(Retorno de Codigo operacion en respuesta) 03 - Status del Dispensador
'
Private Structure BloqueDatos2
Private Codigo_Operacion As Integer
Private Codigo_Fin As Integer
Private Indicador_Estado As Integer
Private Datos_ISBN As String
Private Datos_CIF As String
'
'
Propiedad CodigoOperacion
Public Property CodigoOperacion() As String
Get
Return Format(Codigo_Operacion, "00")
End Get
Set(ByVal value As String)
Codigo_Operacion = Val(value)
End Set
End Property
'
'
Propiedad CodigoFin
Public Property CodigoFin() As String
Get
Return Format(Codigo_Fin, "00")
End Get
página
Fuente 7.3 (continua...)
63
<<
Robot dispensador para MSDN Vídeo
Set(ByVal value As String)
Codigo_Fin = value
End Set
End Property
'
'
Propiedad IndicadorEstado
Public Property IndicadorEstado() As String
Get
Return Hex(Indicador_Estado).PadLeft(4, "0")
End Get
Set(ByVal value As String)
Indicador_Estado = Val(value)
End Set
End Property
'
'
Propiedad DatosISBN
Public Property DatosISBN() As String
Get
Return Datos_ISBN
End Get
Set(ByVal value As String)
Datos_ISBN = value.PadRight(13)
End Set
End Property
'
'
Propiedad DatosCIF
Public Property DatosCIF() As String
Get
Return Datos_CIF
End Get
Set(ByVal value As String)
Datos_CIF = value.PadRight(9)
End Set
End Property
End Structure
#End Region
(...continuación) Fuente 7.3
¡Eureka!, nuestros dos bloques de información definidos en el primer apartado,
ya están listos para ser usados.
Debemos componer una estructura para cada modelo de solicitud, a saber:
Expulsión, Introducción, Estado.
página
64
Robot dispensador para MSDN Vídeo
>>
Así que compuestos los bloques de datos, abordaremos la construcción de las tramas dándoles el formato adecuado.
Lo que pretende la siguiente estructura es abstraer el rígido protocolo DispeDataLink de la aplicación, lo que permite llamar a esta estructura sin necesidad de
concentrarte en los aspectos formales (CRC/CR/Carácter de Inicio/Fin..., etc.) de esa
forma la aplicación se concentra únicamente en los datos que maneja.
Esta sencilla fórmula es la responsable de componer los bloques, ponerlos en
orden, añadirles las cabeceras, calcular los bytes de redundancia cíclica y entregarnos
una cadena que contiene la trama lista para enviar. ¿A alguien le parece poco?
#Region "** Trama de Transmision - Sol.licitud de Expulsion"
Public Structure SolicitarExpulsion
Private miTrama As String
'
'Componer la trama de Expulsión partiendo del ISBN y el CIF, Unidad de
Origen / Unidad Destino, Retornamos una estructura compuesta según
Protocolo DispeDataLink
Public Function Trama(ByVal UDest As String, ByVal UOrig As String,_
ByVal ISBN As String, ByVal CIF As String) As String
'
blDatos1.UnidadDestino = UDest
'Unidad Destino
blDatos1.UnidadOrigen = UOrig
'Unidad Origen
blDatos2.CodigoOperacion = Operacion.Expulsion
'Codigo Expulsion
blDatos2.DatosISBN = ISBN
'Codigo ISBN
blDatos2.DatosCIF = CIF
'Codigo CIF
'
miTrama = Ini.ToString + _
blDatos1.UnidadDestino + _
blDatos1.UnidadOrigen + _
blDatos2.CodigoOperacion + _
blDatos2.DatosISBN + _
blDatos2.DatosCIF
Return miTrama + calcular_Crc(miTrama) + Fin.ToString
End Function
End Structure
#End Region
Fuente 7.4
página
Supongo que nadie tendrá dificultad en seguir la lectura del código y pueda distinguir cómo en las primeras líneas asignamos los datos recibidos en la llamada de la
65
<<
Robot dispensador para MSDN Vídeo
función a su correspondiente formateador de bloque y en su parte final los asignamos
a una cadena llamada “miTrama”. Para continuar calculando y añadiendo el CRC a la
cadena de fin de trama.
En el formato de trama de introducción hemos implementado una sobrecarga
para poder simular la lectura de las etiquetas electrónicas.
#Region "** Trama de Transmision - Sol.licitud de Introducción"
Public Structure SolicitarIntroduccion
Private miTrama As String
'
'Componer la trama de Expulsión partiendo del ISBN y el CIF, Unidad de
Origen / Unidad Destino
'Retornamos una estructura compuesta segun protocolo DispeDataLink
Public Overloads Function Trama(ByVal UDest As String, _
ByVal UOrig As String) As String
'
blDatos1.UnidadDestino = UDest
'Unidad Destino
blDatos1.UnidadOrigen = UOrig
'Unidad Origen
blDatos2.CodigoOperacion = Operacion.Introduccion 'Cod.Introducción
'
miTrama = Ini.ToString + _
blDatos1.UnidadDestino + _
blDatos1.UnidadOrigen + _
blDatos2.CodigoOperacion
Return miTrama + calcular_Crc(miTrama) + Fin.ToString
End Function
'
'Sobrecarga de la funcion para poder simular la lectura de la etiqueta
electronica
'
Public Overloads Function Trama(ByVal UDest As String, _
ByVal UOrig As String, _
ByVal ISBN As String, _
ByVal CIF As String) As String
'
blDatos1.UnidadDestino = UDest
'Unidad Destino
blDatos1.UnidadOrigen = UOrig
'Unidad Origen
blDatos2.CodigoOperacion = Operacion.Introduccion 'Cod.Introducción
blDatos2.DatosISBN = ISBN
'Codigo ISBN
blDatos2.DatosCIF = CIF
'Codigo CIF
'
miTrama = Ini.ToString + _
blDatos1.UnidadDestino + _
Fuente 7.5 (continua...)
página
66
Robot dispensador para MSDN Vídeo
>>
blDatos1.UnidadOrigen + _
blDatos2.CodigoOperacion + _
blDatos2.DatosISBN + _
blDatos2.DatosCIF
Return miTrama + calcular_Crc(miTrama) + Fin.ToString
End Function
End Structure
#End Region
(...continuación) Fuente 7.5
Por supuesto, no olvidaremos implementar la trama para la solicitar el estado del
dispensador.
#Region "** Trama de Transmision - Sol.licitud de Estado"
Public Structure SolicitarEstatus
Private miTrama As String
'
'Componer la trama de Estado
'Retornamos una estructura compuesta segun protocolo DispeDataLink
Public Function Trama(ByVal UDest As String, ByVal UOrig As String) As String
'
blDatos1.UnidadDestino = UDest
'Unidad Destino
blDatos1.UnidadOrigen = UOrig
'Unidad Origen
blDatos2.CodigoOperacion = Operacion.Estatus
'Codigo Estatus
'
miTrama = Ini.ToString + _
blDatos1.UnidadDestino + _
blDatos1.UnidadOrigen + _
blDatos2.CodigoOperacion
Return miTrama + calcular_Crc(miTrama) + Fin.ToString
End Function
End Structure
#End Region
Fuente 7.6
Debido a la naturaleza de nuestro proyecto, debemos también contemplar las
estructuras de respuesta, esta sobre-tarea nos viene impuesta por la necesidad de
implementar un simulador del dispensador.
página
Siguiendo el esquema descrito en el gráfico 6.1 (enlace de comunicaciones),
veremos cómo cada solicitud se convierte en un envío que recibe el simulador, gene-
67
<<
Robot dispensador para MSDN Vídeo
rando este último otro envío que recibe el solicitante en modo respuesta. O sea, dos
transmisiones y dos recepciones.
Iniciemos la codificación de las estructuras de respuestas que corresponderían a
las construidas en el ámbito del dispensador.
En el modelo de trama para las repuestas incluiremos un formato común con las
sobrecargas necesarias para dotarlas de todas sus formas.
#Region "** Trama de Respuesta - Modelo Comun"
Public Structure RespuestaModelo
Private miTrama As String
'
'Componer la trama de Expulsión partiendo del ISBN y el CIF, Unidad de
Origen / Unidad Destino
'Retornamos una estructura compuesta segun protocolo DispeDataLink
Friend Overloads Function Trama(ByVal UDest As String, _
ByVal UOrig As String, _
ByVal CodigoFin As String, _
ByVal CodigoOperacion As String) As String
'
blDatos1.UnidadDestino = UDest
'Unidad Destino
blDatos1.UnidadOrigen = UOrig
'Unidad Origen
blDatos2.CodigoFin = CodigoFin
blDatos2.CodigoOperacion = CodigoOperacion
'Cod.Retorno Operacion
'
miTrama = Ini.ToString + _
blDatos1.UnidadDestino + _
blDatos1.UnidadOrigen + _
blDatos2.CodigoFin + _
blDatos2.CodigoOperacion
Return miTrama + calcular_Crc(miTrama) + Fin.ToString
End Function
'
Friend Overloads Function Trama(ByVal UDest As String, _
ByVal UOrig As String, _
ByVal CodigoFin As String, _
ByVal IndicadorEstatus As Integer) As String
'
blDatos1.UnidadDestino = UDest
'Unidad Destino
blDatos1.UnidadOrigen = UOrig
'Unidad Origen
blDatos2.CodigoFin = CodigoFin
blDatos2.IndicadorEstado = IndicadorEstatus
'Cod.Retorno Operacion
Fuente 7.7 (continua...)
página
68
Robot dispensador para MSDN Vídeo
>>
'
miTrama = Ini.ToString + _
blDatos1.UnidadDestino + _
blDatos1.UnidadOrigen + _
blDatos2.CodigoFin + _
blDatos2.IndicadorEstado
Return miTrama + calcular_Crc(miTrama) + Fin.ToString
End Function
End Structure
#End Region
(...continuación) Fuente 7.7
Para los que no tengan demasiado claro, el tema de sobrecargas, justo comentar
que son diferentes maneras de llamar a una misma funcionalidad, o dicho de otro
modo sirven para llamar a una función con el mismo nombre pasando diferentes juegos de parámetros.
Desde luego debo pedir disculpas a los expertos por este pésimo ejemplo de
implementación de sobrecargas.
En esta región englobamos la parte dura, el desglose de campos... ¿alguien
recuerda la instrucción Field...? pues aquí estamos.
Del mismo modo con esta estructura descomponemos o desempaquetamos los
diferentes campos que componen la trama. Fijémonos en el uso de substring para
extraer la posición exacta reservada a cada valor.
#Region "** Recepcion de Tramas - Desglose de Respuestas"
Public Structure Recepcion
Private miTrama As String
Private TramaCrc, CrcRecv, CrcCalc As String
Public Property CamposDeRecepcion() As String
Get
Return miTrama
End Get
Set(ByVal value As String)
miTrama = value
End Set
End Property
página
Fuente 7.8 (continua...)
69
<<
Robot dispensador para MSDN Vídeo
Public Overloads ReadOnly Property Destino(ByVal Trama As String) As Integer
Get
Return Trama.Substring(1, 2)
End Get
End Property
Public Overloads ReadOnly Property Destino() As Integer
Get
Return miTrama.Substring(1, 2)
End Get
End Property
Public Overloads ReadOnly Property Origen(ByVal Trama As String) As Integer
Get
Return Trama.Substring(3, 2)
End Get
End Property
Public Overloads ReadOnly Property Origen() As Integer
Get
Return miTrama.Substring(3, 2)
End Get
End Property
Public Overloads ReadOnly Property CodigoOpe(ByVal Trama As String) As Integer
Get
Return Trama.Substring(5, 2)
End Get
End Property
Public Overloads ReadOnly Property CodigoOpe() As Integer
Get
Return miTrama.Substring(5, 2)
End Get
End Property
Public Overloads ReadOnly Property CodigoFin(ByVal Trama As String) As String
Get
Return Trama.Substring(7, 2)
End Get
End Property
Public Overloads ReadOnly Property CodigoFin() As String
Get
Fuente 7.8 (continua...)
página
70
Robot dispensador para MSDN Vídeo
>>
Return miTrama.Substring(7, 2)
End Get
End Property
Public Overloads ReadOnly Property CheckSum(ByVal Trama As String) As Boolean
Get
TramaCrc = Trama.Substring(0, Trama.Length - 4)
CrcRecv = Trama.Substring(Trama.Length - 4, 2)
CrcCalc = calcular_Crc(TramaCrc)
If CrcRecv = CrcCalc Then Return True Else Return False
End Get
End Property
Public Overloads ReadOnly Property CheckSum() As Boolean
Get
TramaCrc = miTrama.Substring(0, miTrama.Length - 4)
CrcRecv = miTrama.Substring(miTrama.Length - 4, 2)
CrcCalc = calcular_Crc(TramaCrc)
If CrcRecv = CrcCalc Then Return True Else Return False
End Get
End Property
Public Overloads ReadOnly Property ISBN(Trama as String) As String
Get
Return Trama.Substring(7, 13)
End Get
End Property
Public Overloads ReadOnly Property ISBN() As String
Get
Return miTrama.Substring(7, 13)
End Get
End Property
Public Overloads ReadOnly Property Indicadores(ByVal Trama As String) As String
Get
Return Trama.Substring(7, 4)
End Get
End Property
Public Overloads ReadOnly Property Indicadores() As String
Get
Return miTrama.Substring(7, 4)
End Get
End Property
página
Fuente 7.8 (continua...)
71
<<
Robot dispensador para MSDN Vídeo
Public Overloads ReadOnly Property NIFCIF(ByVal Trama As String) As String
Get
Return Trama.Substring(20, 9)
End Get
End Property
Public Overloads ReadOnly Property NIFCIF() As String
Get
Return miTrama.Substring(20, 9)
End Get
End Property
Public ReadOnly Property LaTramaEstaCompleta(ByVal Trama As String) As Boolean
Get
miTrama = Trama
If (Left(Trama, 1) = Ini) And (Right(Trama, 2) = "$" + Chr(13)) Then
Return True
Else
Return False
End If
End Get
End Property
End Structure
#End Region
(...continuación) Fuente 7.8
Vale la pena observar en todo este código de desglose, el uso de sobrecargas y las
propiedades de solo lectura (Overloads, ReadOnly, Property).
En las estructuras de recepción cabe destacar el alto grado de integración, usando llamadas a la función de calcular_Crc desde la propiedad de CheckSum, es evidente
que vamos a disfrutar empaquetando código.
También destacar el uso de la propiedad CheckSum y LaTramaEstaCompleta, la función
de la primera es devolver un “Verdadero” si la suma de verificación del remitente coincide con la calculada por el destinatario y la segunda de ellas comprueba si la trama
contiene las cadenas identificadoras de “Inicio” y “Final”. Después de verificar ambas
propiedades en una trama, estaremos en disposición de procesar sus datos.
La clase EntrgnEstructuras, incluirá ciertas enumeraciones, que en ciertos
contextos pueden ayudarnos en tener todas las descripciones muy bien ordenadas.
Veamos:
página
72
Robot dispensador para MSDN Vídeo
>>
#Region "** Enumeraciones"
'
'Enumerar las posibles Operaciones
Public Enum Operacion
'Acciones
Expulsion = 1
Introduccion = 2
Estatus = 3
'Alertas
TodoBien = 10
ExisteAlarma = 11
'Avisos
StockMinimo = 20
UltimoProducto = 21
UltimasCapsulas = 22
ErroresEnDispensador = 23
ProductoIlocalizable = 24
ImposibleRecoger = 25
ImposibleApilar = 26
EjeCabezalRoto = 27
AtascoDistribuidor = 28
FalloLectorEtiquetas = 29
FalloLecturaEtiqueta = 30
'Fin de operacion
TransmisionOk = 0
TransmisionKo = 99
End Enum
'
'Enumerar los Indicadores de estado (4 hex)
Public Enum Indicadores
AveriaEnPlacaControladora1 = &H1&
AveriaEnPlacaControladora2 = &H2&
FalloEnProcesador1 = &H4&
FalloEnProcesador2 = &H8&
AveriaEjeCabezal = &H10&
AveriaEnPinzaDispensadora = &H20&
LectorEtiquetasAveriado = &H40&
DisparoDetectorAntivandalico = &H80&
AlmacenCapsulasVacio = &H100&
AlmacenProductoVacio = &H200&
SuciedadOpticaPosicionadorLaser = &H400&
FalloVentiladorSistemaRefrigeracion = &H800&
AtascoDispensadorSalida = &H1000&
AtascoDispensadorEntrada = &H2000&
AtascoPinzaDispensadora = &H4000&
SobreTemperatura = &H8000&
End Enum
#End Region
página
Fuente 7.9
73
<<
Robot dispensador para MSDN Vídeo
Después de todo este esfuerzo, tenemos a punto nuestra primera clase, con este
magnífico aspecto:
figura 7.1
El simulador
A los más impacientes se nos hace difícil teclear código sin poderle dar al [F5],
sin embargo, es importante seguir un orden, a pesar del desespero, como en la mayoría de disciplinas, deberemos construir la infraestructura que nos va a permitir elevar
nuestro edificio hasta conseguir la altura diseñada.
Bien, una vez hemos completado la parte más aburrida (definir estructuras),
pasaremos a conformar una vital. Acabamos de construir un instrumento dispensador,
sin embargo, a todos los efectos requerimos disponer de un simulador que emule el
funcionamiento de nuestra máquina.
página
74
Robot dispensador para MSDN Vídeo
>>
La naturaleza y topología de las comunicaciones entre nuestro dispensador y el
equipo al que está unido, nos obliga a intercambiar tramas en ambos sentidos, debemos pues proveernos de todos los mecanismos necesarios para poder desarrollar nuestra aplicación sin el soporte del dispensador. Con la ayuda de este simulador podremos construir una aplicación resistente. Además podemos perfeccionar el simulador
hasta tal punto en que cambiar el simulador por el dispensador sea simplemente eso:
¡cambiar!
¡… Bienvenido a la virtualidad!
Conseguiremos nuestro propósito dotando a nuestro simulador de una función
distribuidora de solicitudes, cualquier operación externa será dirigida y distribuida
desde este punto; la función llamará a cada uno de los procedimientos contemplados,
o sea, solicitud de introducción, expulsión y estado. Codificaremos una función que
compondrá el modelo de respuesta. Finalizaremos nuestra clase con la función
AlarmaGrave encargada de simular y reportar los problemas relacionados con mensajes
de advertencia, informativos o de alarma.
página
figura 7.2
75
<<
Robot dispensador para MSDN Vídeo
Su aspecto:
En el proceso de solicitudes, recibiremos la llamada a la función incluyendo la
trama recibida. Como vemos asignaremos a la variable CodigoOpe la operación solicitada, siendo éste el que decida en la Select qué función ejecutaremos para procesar dicha
solicitud:
'
'Procesar las tramas recibidas por el simulador
Public Function ProcesoDeSolicitudes(ByVal TramaRecibida As String) As String
If Not Campos.LaTramaEstaCompleta(TramaRecibida) Then
'Trama no completa
TramaDeRespuesta = ""
'No procesamos Nada
Else
_Origen = Campos.Origen.ToString 'Asignacion campos recibidos
_Destino = Campos.Destino.ToString
_CodigoOpe = Campos.CodigoOpe.ToString
If Not Campos.CheckSum Then
'el checksum coincide.. error ?
'Componer respuesta fallo verificacion del CRC
_CodigoFin = 99
ComponerRespuestaModelo()
Else
Select Case Campos.CodigoOpe 'ejecutar proceso según operacion
Case 1 'Procesar solicitud de Expulsion
SolicitarExpulsion()
Case 2 'Procesar solicitud de Introduccion
SolicitarIntroduccion()
Case 3 'Procesar solicitud de Estado
SolicitarEstatus()
End Select
End If
End If
Return TramaDeRespuesta
End Function
Fuente 7.10
Anteriormente estuvimos viendo como implementábamos dos propiedades
ChekSum y la LaTramaEstaCompleta; bien, entonces analicemos su utilidad.
Hemos estado hablando de empaquetar, encapsular... y aquí tenemos el resultado, si
leemos detenidamente el código descrito en este fuente, nos daremos cuenta que de una
manera elegante o dicho de otra forma, con una sintaxis entendible, estamos procesando
sólo la trama que cumpla con dos condiciones básicas, la primera de ellas que contenga las
cadenas de inicio/fin y la segunda que coincidan los cálculos de verificación.
página
76
Robot dispensador para MSDN Vídeo
>>
Ahora nos toca la tarea de crear los procedimientos que ejecutaran la simulación
del proceso del dispensador.
'
'Simular la expulsion
Private Sub SolicitarExpulsion()
'
'Simular ordenes del mecanismo
'If LocalizarCapsula(_ISBN) then
' CodigoDeRetorno = PosicionarPinzasCabezal(): VerificarCodigosFinDeOperacion
' CodigoDeRetorno = RecogerProductoEnPinzas(): VerificarCodigosFinDeOperacion
' CodigoOperacion = GrabarEtiquetaElectronica(_ISBN,_NIFCIF):VerificarCodFinDeOp
' CodigoDeRetorno = DejarProductoEnBandejas(): VerificarCodigosFinDeOperacion
'else
' ReportarProblemaDelSistemaLocalizador
'end if
'
'SIMULADOR!!
_CodigoDeRetorno = SimuladorCodigoFin() 'Simular retorno incidencia entregon+
_CodigoFin = campos.CodigoOpe
'Operacion completada correctamente
ComponerRespuestaModelo()
'
'Insertar el ISBN al banco local de peliculas que podemos dispensar
End Sub
Fuente 7.11. Simular el proceso de expulsión de una cápsula: SolicitarExpulsion
'
'Simular la introduccion
Private Sub SolicitarIntroduccion()
'
'Simular ordenes del mecanismo
' Disparo interrupcion por deteccion de introduccion de capsula
' CodigoOperacion = LeerEtiquetaElectronica(_ISBN, _NIFCIF)
'If CodigoOperacion then
' CodigoDeRetorno = PosionCabezalEnRecogida(): VerificarCodigosFinDeOperacion
' CodigoDeRetorno = RecogerProductoDePinzas(): VerificarCodigosFinDeOperacion
' CodigoDeRetorno = ActualizarEtiquetaElectronica(_ISBN,_NIFCIF):VerCodFinDeOpe
' CodigoDeRetorno = PosicionProductoAlmacen() : VerificarCodigosFinDeOperacion()
' CodigoDeRetorno = DejarProducto(): VerificarCodigosFinDeOperacion
'else
página
Fuente 7.12. Simular el proceso de introducción de una cápsula: SolicitarIntroduccion
77
<<
Robot dispensador para MSDN Vídeo
' RechazarIntroduccionPorObjetoDesconocido
' LanzarAlertaAlUsuario
' ReportarCodigoErrorLecturaEtiqueta
'end if
'
_CodigoDeRetorno = SimuladorCodigoFin() '... ?
_CodigoFin = campos.CodigoOpe
'Operacion completada correctamente
'
_ISBN = campos.ISBN
_NIFCIF = campos.NIFCIF
ComponerRespuestaModelo()
End Sub
(...continuación) Fuente 7.12. Simular el proceso de introducción de una cápsula: SolicitarIntroduccion
'
'Simular el estado del mecanismo
Private Sub SolicitarEstatus()
'_CodigoDeRetorno = SimuladorCodigoFin() '... ?
'
'ComponerRespuestaModelo()
Dim _Respuesta As Tramas.RespuestaModelo = New Tramas.RespuestaModelo
TramaDeRespuesta =
_Respuesta.Trama(_Destino.ToString,_Origen.ToString, "03",IndicadoresEstado)
End Sub
Fuente 7.13. Simular el proceso de verificación de estado del dispensador: SolicitarEstatus
Si hemos seguido el código, habremos observado que tanto el procedimiento de
expulsión como el de introducción, efectúan una llamada a la composición de la trama
de respuesta. Su nombre “Componer Respuesta Modelo”. Ésta es la encargada de
devolver una respuesta en la forma definida para las respuestas.
Pues entonces :
'
'Formato de la respuesta
Private Sub ComponerRespuestaModelo()
Dim _Respuesta As Tramas.RespuestaModelo = New Tramas.RespuestaModelo
TramaDeRespuesta = Respuesta.Trama(_Destino.ToString, _Origen.ToString, _
_CodigoFin.ToString, _CodigoDeRetorno.ToString)
End Sub
Fuente 7.14
página
78
Robot dispensador para MSDN Vídeo
>>
Terminaremos nuestro recorrido, completando la clase Simulador con dos procedimientos más que gestionen el código de retorno y las alarmas.
Del siguiente procedimiento destacaremos la simulación de mensajes del ingenio, para dotar a la simulación de un cierto grado de realidad. Evidentemente es necesario que de una manera eventual se disparen errores o alarmas tal como ocurriría en
la realidad con el mecanismo.
Para tal efecto siguiendo el código observaremos cómo simulamos tres errores
diferentes en función de si los campos de NIFCIF o ISBN están parcialmente completos o incompletos en cualquiera de los dos casos. En condiciones normales se fuerza la generación de un número aleatorio entre el 1 y el 11, y a posteriori se rellena
asignando un número aleatorio a los indicadores de estado.
La última función de la clase AlaramaGrave rellena con mensajes de texto que
corresponden a los códigos de alarma.
'Simulador de codigo fin de operación
Private Function SimuladorCodigoFin() As Integer
Dim indice As Integer
' Simular Codigos de error usando el ISBN y el DNI/CIF
'
_ISBN = campos.ISBN.Replace(" ", "")
_NIFCIF = campos.NIFCIF.Replace(" ", "")
If (_ISBN.Length = 0) And (_NIFCIF.Length = 0) Then
'error = 29 si los campos ISBN y CIF aparecen vacios
indice = 29
Else
If _ISBN.Length < 13 And _NIFCIF.Length < 9 Then
'error = 24 si ninguno de los dos campos no esta completo
indice = 24
Else
If _NIFCIF.Length < 9 Then
'error = 30 si el campo NIF no tiene su longitud mínima
indice = 30
Else
If _ISBN.Length < 13 Then
'error = 23 si el campo ISBN no tiene su longitud mínima
indice = 23
Else
' Simulador de detección de error en Entregon+
indice = CInt(Int((11 * Rnd()) + 1)) 'Generar código Aleatorio
'entre 1 y 11
página
Fuente 7.15 (continua...)
79
<<
Robot dispensador para MSDN Vídeo
If indice = 11 Then
_IndicadoresEstado = _IndicadoresEstado Xor (32767 * Rnd())
AlarmaGrave()
Else
_IndicadoresEstado = 0
End If
End If
End If
End If
End If
'
Select Case indice
Case 11 :
'Case 20 :
'Case 21 :
'Case 22 :
Case 23 :
Case 24 :
'Case 25 :
'Case 26 :
'Case 27 :
'Case 28 :
Case 29 :
Case 30 :
Case Else
Return
Return
Return
Return
Return
Return
Return
Return
Return
Return
Return
Return
Tramas.Operacion.ExisteAlarma
Tramas.Operacion.StockMinimo
Tramas.Operacion.UltimoProducto
Tramas.Operacion.UltimasCapsulas
Tramas.Operacion.ErroresEnDispensador
Tramas.Operacion.ProductoIlocalizable
Tramas.Operacion.ImposibleRecoger
Tramas.Operacion.ImposibleApilar
Tramas.Operacion.EjeCabezalRoto
Tramas.Operacion.AtascoDistribuidor
Tramas.Operacion.FalloLectorEtiquetas
Tramas.Operacion.FalloLecturaEtiqueta
Return Tramas.Operacion.TodoBien
End Select
End Function
(...continuacion) Fuente 7.15
Con ganas de finalizar, completaremos la clase simulador con la codificación de
las alarmas.
'Tratamiento de Incidencias Graves
Private Sub AlarmaGrave()
Dim Codigo As Tramas.Indicadores = New Tramas.Indicadores
Dim LfCr As String = Chr(10) + Chr(13)
texto = ""
If _IndicadoresEstado And Codigo.AlmacenCapsulasVacio Then
texto += "RESPONSABLE DE PRODUCCION! - CAPSULAS AGOTADAS EN EL DISPENSADOR "
End If
If _IndicadoresEstado And Codigo.AlmacenProductoVacio Then
Fuente 7.16 (continua...)
página
80
Robot dispensador para MSDN Vídeo
>>
texto += "RESPONSABLE DE PRODUCCION! - DISPENSADOR nºxx, SIN PRODUCTO" + LfCr
End If
If _IndicadoresEstado And Codigo.AtascoDispensadorEntrada Then
texto += "RESPONSABLE DE MANTENIMIENTO! - ATASCO EN EL MECANISMO DE ENTRADA"
End If
If _IndicadoresEstado And Codigo.AtascoDispensadorSalida Then
texto += "RESPONSABLE DE MANTENIMIENTO! - ATASCO EN EL MECANISMO DE SALIDA"
End If
If _IndicadoresEstado And Codigo.AtascoPinzaDispensadora Then
texto += "RESPONSABLE DE MANTENIMIENTO! - PINZA DISPENSADORA ATASCADA"
End If
If _IndicadoresEstado And Codigo.AveriaEjeCabezal Then
texto += "RESPONSABLE DE MANTENIMIENTO! - EJE CABEZAL AVERIADO UNIDAD nºxx"
End If
If _IndicadoresEstado And Codigo.AveriaEnPinzaDispensadora Then
texto += "RESPONSABLE DE MANTENIMIENTO! - PINZA DISPENSADORA AVERIADA "
End If
If _IndicadoresEstado And Codigo.AveriaEnPlacaControladora1 Then
texto += "INGENIERO ELECTRONICO! - SUBSTITUIR PLACA CONTROLADORA 1 "
End If
If _IndicadoresEstado And Codigo.AveriaEnPlacaControladora2 Then
texto += "INGENIERO ELECTRONICO! - SUBSTITUIR PLACA CONTROLADORA 2 "
End If
If _IndicadoresEstado And Codigo.DisparoDetectorAntivandalico Then
texto += "JEFE DE SEGURIDAD! - DISPARO DE LA ALARMA DE ANTIVANDALICA" + LfCr
End If
If _IndicadoresEstado And Codigo.FalloEnProcesador1 Then
texto += "INGENIERO DE SISTEMAS!,FALLA EL PROCESADOR PRINCIPAL ENTREGON+ "
End If
If _IndicadoresEstado And Codigo.FalloEnProcesador2 Then
texto += "INGENIERO DE SISTEMAS!,FALLA EL PROCESADOR SECUNDARIO ENTREGON+"
End If
If _IndicadoresEstado And Codigo.SobreTemperatura Then
texto += "OPERARIO DE A.ACONDICIONADO!, SOBRETEMPERATURA ENTREGON+" + LfCr
End If
If _IndicadoresEstado And Codigo.SuciedadOpticaPosicionadorLaser Then
texto += "DEPARTAMENTO DE LASERES!, LIMPIAR/ALINEAR LASER DEL ENTREGON+ nºxx"
End If
'Solo enviar en caso de Incidencia
If texto.Length > 0 Then
Mensaje.Registra("Simulador",texto, EventLogEntryType.Error, 100 + _
_CodigoDeRetorno)
'lanzar un correo electronico con la incidencia
Alarmas.Avisar(texto)
End If
End Sub
página
(...continuación) Fuente 7.16
81
<<
Robot dispensador para MSDN Vídeo
Mensajes en el EventLog
Por fin, para nuestra librería, una clase “facilonga”. Básicamente se compone de
una función que genera una entrada en el registro del sistema, determinada por su origen, su tipo de entrada y un identificador.
Todo esto se materializa en una única llamada a la función WriteEntry del espacio
System.Diagnostics.EventLog.
'
'
'
'
'
'
'
'
'
'
Proyecto
Nombre
Tipo
Clase
Creado
Autor
Version
:
:
:
:
:
:
:
Entregon+
EntrgnEventLog.vb
Clase
RegistroAlertas
29.04.2005
Pep Lluis
1.0
Historia y Revisiones ----------------------------------------------------------05.2005 - Migrar el codigo a visual Studio 2005 - Beta 2
Public Class RegistroAlertas
'
Private UltimoIdentificador As Integer = 0
Public Shared MensajesEnRegistro As Boolean = False
'
Public Sub Registra(ByVal Origen As String, ByVal Mensaje As String, _
ByVal Tipo As System.Diagnostics.EventLogEntryType, _
ByVal Identificador As Integer)
'
MensajesEnRegistro = True
Try
'Nunca repetir el ultimo mensaje
If UltimoIdentificador <> Identificador Then
System.Diagnostics.EventLog.WriteEntry(Origen, Mensaje, Tipo, _
Identificador)
UltimoIdentificador = Identificador
End If
Catch ex As Exception
'Alertar de que no podemos registrar eventos al log
System.Diagnostics.EventLog.WriteEntry("RegistroAlertas", _
ex.Message, EventLogEntryType.Error, 0)
End Try
End Sub
End Class
Fuente 7.17
página
82
Robot dispensador para MSDN Vídeo
>>
En la mayoría del código anterior hemos omitido la captura de excepciones,
daros cuenta que prácticamente la función del código era asignar o manejar valores y
variables. Las siguientes clases incorporan llamadas a funciones de librerías externas,
pensando en los imprevistos, a partir de ahora incluiremos los bloques Try/Catch para
registrar un mensaje al EventLog, reportando el problema, cuyo detalle en forma de
texto lo encontraremos en ex.Message.
Avisos de incidencias automatizados
Un truco de viejo programador, es dotar a nuestros desarrollos de esos pequeños
detalles que diferencian nuestras aplicaciones de las de los aficionados. ¡Este es uno de
ellos!
A menudo no es difícil buscar utilidades que den versatilidad al conjunto. A simple vista puede parecer una pérdida inútil de tiempo dotar a nuestro sistema de una
gestión de avisos automatizados.
Sin embargo, si lo analizamos, nos daremos cuenta de que estamos desarrollando una aplicación pobre en interacción con el usuario. No olvidemos que hacemos de
enlace entre un mecanismo y una aplicación de un dispensador, por lo que no disponemos de una interacción directa con el usuario; sería ridículo caer en el error de presentar mensajes de error al usuario del vídeo club que simplemente está interesado en
recoger o devolver una película. Se imaginan la cara de estupor al leer: “Falla el procesador principal Entregon+”... ¡y a quién diablos le importa!... pues al departamento de
sistemas donde están los ingenieros de soporte.
Realmente el usuario del dispensador sólo debe recibir los mensajes relacionados con
las operaciones en curso y en caso de problemas, sólo uno: “Dispensador Fuera de Servicio”.
Este mensaje encubrirá el resto que serán automáticamente lanzados vía correo electrónico a cada uno de los departamentos que realizan el mantenimiento de la empresa que alquila y vende las películas. Organizar una cadena de establecimientos que tiene centralizados
sus servicios de mantenimiento de sus instalaciones, será cosa de niños. A partir de ahora
cada departamento recibirá las alarmas que se van produciendo en los dispensadores que
tienen a su cargo. Además una vez disponemos de esta implementación, no importa si los
dispensadores son 10 o si son 100, la infraestructura nos cubrirá cualquier dimensión.
página
Recuerdo en antaño cuando los cajeros de las entidades bancarias, tenían un
interfono para que el usuario llamara en caso de problemas, pero antes de detectar
estos problemas algún afortunado cliente tenía que caer en las garras del frío aluminio de los mecanismos… encalladas de tarjetas, lectores, dispensadores que se bloqueaban, sistemas se quedaban hang... ¡qué horror!
83
<<
Robot dispensador para MSDN Vídeo
Es un alivio pensar que las máquinas actuales son capaces de detectar problemas
incluso antes de que se produzcan, llegando a ser tolerantes a fallos, pero no nos engañemos, alguien deberá diseñar o implementar los algoritmos que realicen estas tareas… ¡cuestión de hacer las cosas bien!
Y qué menos: nosotros no tan solo lo hacemos bien sino que... ¡somos los
mejores!
Por lo tanto, aviso de incidencias automatizadas.
Casi no es necesario comentar el código, sin embargo, nos irá bien mencionar
esta primera parte donde dimensionamos las variables que usaremos para nuestro
aviso por correo, cargándolas con la configuración almacenada en el archivo app.config, para tal cometido usaremos la clase XmlConfig que se describe posteriormente. A
continuación crearemos el objeto correo. Como mensaje de mail, asignaremos los
valores del mismo y sencillamente llamaremos al send.
'
'Avisar de una Alerta utilizando el correo electonico
Public Sub Avisar(ByVal emailMensa As String)
'
'Lectura de las variables de configuracion del archivo app.config.
Dim email_From As String = _
Aplicacion.LeerConfig("app.config", "email_From", "emailOrigen").InnerText
Dim email_To As String = _
Aplicacion.LeerConfig("app.config", "email_To", "emailDestino").InnerText
Dim email_Cc As String = _
Aplicacion.LeerConfig("app.config", "email_Cc", "emailCopia").InnerText
Dim email_User As String = _
Aplicacion.LeerConfig("app.config", "email_User", "CuentaUsuario").InnerText
Dim email_Pwd As String = _
Aplicacion.LeerConfig("app.config", "email_Pwd", "password").InnerText
Dim email_smtp As String = _
Aplicacion.LeerConfig("app.config","email_Smtp","servidor").InnerText
'
'Conformar un mensage de mail
Dim correo As MailMessage = New MailMessage
'Definir el esquema CDO
Dim Esquema As String = "http://schemas.microsoft.com/cdo/configuration/"
'Llenar de contenido el mensage
correo.From = email_From
correo.To = email_To
correo.Cc = email_Cc
Fuente 7.18 (continua...)
página
84
Robot dispensador para MSDN Vídeo
>>
correo.Subject = "ENTREGON+, ALARMA - REQUIERE INTERVENCION"
correo.Body = emailMensa
'Asignar las credendiales
correo.Fields.Add(Esquema + "smtpauthenticate", "1")
correo.Fields.Add(Esquema + "sendusername", email_User)
correo.Fields.Add(Esquema + "sendpassword", email_Pwd)
'Asignar el nombre del servidor de correo
SmtpMail.SmtpServer = email_smtp
Try
SmtpMail.Send(correo)
'Enviar el correo
Catch ex As Exception
'reportar problemas al eventlog
Mensaje.Registra("Alertas", ex.Message, EventLogEntryType.Error, 4)
End Try
End Sub
(...continuación) Fuente 7.18
Como ya he comentado anteriormente es una buena práctica incluir la llamada
al registro del EventLog, bajo el Catch, de esta forma reportamos los errores capturados cuando falla algo mientras se ejecutan nuestras instrucciones. Así podemos seguir
el rastro de lo que está sucediendo tan solo visualizando en el EventLog.
Para la versión final se recomienda no usar el System.Web.Mail, el envío de correos con el nuevo Framework debe realizarse con System.Net.Mail, según podemos ver
en el siguiente fuente.
'-------------------------------------------------------------------'Version con nombre de espacios system.net.mail
'-------------------------------------------------------------------'Dim correo As MailMessage = New MailMessage(email_From, email_To)
'correo.Subject = "ENTREGON+, ALARMA - REQUIERE INTERVENCION"
'correo.Body = email_Mensa
'Dim ClienteSmtp As New SmtpClient(email_smtp)
'Try
' ClienteSmtp.UseDefaultCredentials = True
' ClienteSmtp.Send(correo)
' Catch ex As Exception
' Mensaje.Registra("Alertas", ex.Message, EventLogEntryType.Error, 4)
' End Try
página
Fuente 7.19
85
<<
Robot dispensador para MSDN Vídeo
Para no perder costumbre, he aquí el aspecto vista desde nuestro IDE.
figura 7.3
Los archivos de configuración
Si hemos estado atentos, la clase anterior llamaba a un procedimiento: Aplicacion.
te sorprendas, aún no hemos hablado de él. Cualquier aplicación que se
precie debe almacenar y leer sus valores de configuración. Como veremos, esta clase
nos permite realizar esta función utilizando un fichero en XML. Hemos decido usar
el app.config aunque quizás no sea la mejor forma, lo hacemos para entender que
podemos concentrar toda la informacion de configuración de la aplicación en un
mismo archivo. Para no complicar excesivamente el código, sólo trabajaremos el
documento XML a nivel de elemento.
LeerConfig, no
Según se describe en la siguiente figura, podemos distinguir tres funciones :
Existe, LeerConfig
página
86
y SalvarConfig.
Robot dispensador para MSDN Vídeo
>>
figura 7.4
Existe simplemente verifica que el fichero solicitado se encuentre en la carpeta
de la aplicación.
'
'Verificar la existencia del archivo
Private Function Existe(ByVal Nombre As String) As Boolean
Dim Doc As XmlDocument = New Xml.XmlDocument
'Documento XML
Try
'
Doc.Load(Nombre)
'Intentar Cargar el nombre
Doc = Nothing
'Liberar el Doc
Return True
'Archivo disponible
Catch ex As Exception
'En otro caso
Return False
'Archivo no disponible
End Try
End Function
página
Fuente 7.20
87
<<
Robot dispensador para MSDN Vídeo
LeerConfig lee un elemento del documento XML y si no existe crea uno nuevo
con el valor pasado por defecto.
'
'Leer un valor de Configuración de un Archivo en formato XML
'Parametros de la funcion :
Fichero = Nombre del Archivo en formato nnn.eee (Ejemplo: app.config)
'
Raiz
= Elemento
'
Defecto = Valor que retornara por defecto
'
Public Function LeerConfig(ByVal Fichero As String, ByVal Raiz As String, _
ByVal Defecto As String) As Xml.XmlElement
Dim Documento As XmlDocument = New Xml.XmlDocument 'Constructor Documento
Dim Elemento As XmlElement = Documento.CreateElement("Raiz")
Try
'Intentar abrir documento
Documento.Load(Fichero)
'Cargar el documento
Elemento = Documento.DocumentElement(Raiz) 'Cargar el elemento
If Elemento Is Nothing Then
'Si Elemento INExistente
' Crear el elemento con el valor por defecto
If Me.Existe(Fichero) Then
Elemento = Documento.CreateElement(Raiz)
Elemento.InnerText = Defecto
Documento.DocumentElement.AppendChild(Elemento)
Documento.Save(Fichero)
Else
'no creamos el elemento por inexistencia del archivo
'Reportamos el problema al EventLog
Mensaje.Registra("XmlConfig", "Archivo :" + Fichero + _
"Inexistente", EventLogEntryType.Warning, 1)
End If
End If
Catch ex As Exception
'Solo reportar el problema
Elemento.InnerText = Defecto
Mensaje.Registra("XmlConfig", ex.Message, EventLogEntryType.Error, 2)
End Try
Return Elemento
End Function
Fuente 7.21
SalvarConfig
documento.
página
88
simplemente guarda el valor del elemento modificado y salva el
Robot dispensador para MSDN Vídeo
>>
'
' Guardar un valor en el documento xml de configuracion
'
Public Function SalvarConfig(ByVal Fichero As String, ByVal Raiz As String, _
ByVal Valor As String) As Boolean
Dim Documento As XmlDocument = New Xml.XmlDocument
'Constructo del Documento
Dim Elemento As XmlElement = Documento.CreateElement("Raiz")
Try
'Intenta abrir el documento
Documento.Load(Fichero)
'Cargar el documento
Elemento = Documento.DocumentElement(Raiz)
'Cargar el elemento
Elemento.InnerText = Valor
'Asigna el valor a elemento
Documento.Save(Fichero)
'Guarda el Documento
Catch ex As Exception
'Solo reportar el problema
Mensaje.Registra("XmlConfig", ex.Message, EventLogEntryType.Error, 2)
End Try
End Function
Fuente 7.22
Hablando sobre los archivos de configuración. Continuamos disponiendo de llamadas al Registro del Sistema 'RegEdit'. Sin embargo parece ser que las buenas practicas no aconsejan el uso intensivo que muchas las aplicaciones hacen de el.
Muchos discuten y es cuestión de largos estiras y aflojas, intentando ponerse de
acuerdo entre si el 'RegEdit' o si el 'app.config'. Lo que esta claro es la apuesta de
'ellos' y como estaréis intuyendo termina en '.config'.
La gestión del puerto serie
Antes de empezar, os recomiendo el estudio de este glosario con las principales
instrucciones del objeto Port, nos ayudara a comprender mejor el código que posteriormente escribiremos para esta clase.
Estos son los mandatos e instrucciones más frecuentes para utilizar el puerto serie:
Private Sub EjemploDeLasPrincipalesInstruccionesDeSystem.IO.Port()
Dim Contador As Integer
'Llamar al constructor
Fuente 7.23 (continua...)
página
Serie = My.Computer.Ports.OpenSerialPort("COM1")
'
89
<<
Robot dispensador para MSDN Vídeo
'Definir las caracteristicas de la comunicacion
Serie.BaudRate = 19200
'Fijar velocidad de comunicaciones
Serie.DataBits = 8
'Longitud en bits para Byte de datos
Serie.Parity = Parity.Even
'Asignar paridad(enumeracion parity)
Serie.StopBits = StopBits.Two
'Bits parada despues byte de datos
'
'Abrir/Control/Liberar Puerto
Serie.Open()
'Abrir el puerto Serie
Serie.Close()
'Cerrar el Puerto Serie
Serie.Dispose()
'Liberar objecto
Dim SiNo As Integer
SiNo = Serie.IsOpen
'El Puerto esta abierto?
Dim Puerto As String
Puerto = Serie.PortName
'Nombre del puerto
'
'Manejo y Control de señales
Dim Estado As Boolean
'True=Activa / False=Inactiva
Estado = Serie.CDHolding
'Estado de la señal carrier detect
Estado = Serie.CtsHolding
'Señal Clear to Send
Estado = Serie.DsrHolding
'Señal Data Set Ready
Serie.DtrEnable = True
'Activar de Data Terminal Ready
Serie.RtsEnable = True
'Activar Request To Send
'
'Control Transmission/Recepcion
Serie.ReadBufferSize = 1024
'Dimensionar tamaño buffer recepcion
Serie.WriteBufferSize = 1024
'Dimensionar tamaño buffer envio
Serie.ReadTimeout = 10
'Fuera de tiempo para las lecturas
Serie.WriteTimeout = 10
'Fuera de tiempo para las escrituras
Serie.Handshake = Handshake.XOnXOff 'Tipo control para recepcion/envio
Serie.DiscardInBuffer()
'Borrar el buffer de entrada
Serie.DiscardOutBuffer()
'Borrar el buffer de salida
'
'Enviar datos
Contador = Serie.BytesToWrite
'Bytes en espera de ser escritos
Serie.Write("Hola Mundo")
'Enviar una cadena de caracteres
Serie.WriteLine("Hola Mundo")
'Enviar una linea
'
'Leer datos
Contador = Serie.BytesToRead
'Bytes en espera de ser leidos
Serie.ReadByte()
'Leer un byte
Serie.ReadChar()
'Leer un char
Serie.ReadLine()
'Leer una linea
Serie.ReadExisting()
'Leer los datos existentes en buffer
End Sub
(...continuación) Fuente 7.23
página
90
Robot dispensador para MSDN Vídeo
>>
Finalmente llegamos a la clase insignia. Está claro que va a ser la más compleja,
no olvidemos que todo este desarrollo gira entorno a los puertos serie.
Vale la pena detenernos en la lectura de la cabecera de la clase EntrgnComSerie.
figura 7.5
Observaremos cómo se han ordenado cinco apartados. El primero de ellos crea las
instancias que utilizaremos para acceder a los objetos de la librería (instancias a clases). El
segundo, tercero y cuarto, ordena las variables que utilizaremos en la aplicación en grupos
de configuración, banderas y proceso. Es una agrupación muy práctica, facilitando la comprensión, sobre todo cuando estamos en proyectos desarrollados en equipo. Salta a la vista
que el grupo de configuración definirá las variables usadas en la misma, las banderas son
flags usados en el control del proceso y finalmente variables utilizadas para contabilizar o
memorizar datos generados en el mismo.
página
El último grupo... ¡importante! es el de los eventos. En este punto asignaremos
los nombres que manipularán los eventos que nuestra aplicación deba conducir.
91
<<
Robot dispensador para MSDN Vídeo
La siguiente figura nos ilustra las clases necesarias para atender la transmisión y
recepción de tramas.
figura 7.6
Empezaremos detallando las operaciones a realizar en tiempo de carga: New,
y Tiempo, para continuar con los procedimientos que formarán nuestra columna vertebral: Enviar, Recibir y AbrirPuertoSerie.
Dispose
Por sí solos, todos los procedimientos descritos, contienen suficientes comentarios y son lo suficientemente sugerentes para poder seguir el hilo del código. Por lo
que animo al lector a leerlos detenidamente. Sin dudar añadiré cualquier explicación
que ayude a su entendimiento.
'Al crear la clase
Public Sub New()
'Temporizador para la puesta a cero de los contadores
Temporizador.Interval = 1000
'Base de 1 Segundo
Temporizador.Enabled = True
'Arrancar el temportizador
'
AbrirPuertoSerie()
'Abrir puerto serie
End Sub
Fuente 7.24. Sub New, Cuando creamos una instancia a la clase.
página
92
Robot dispensador para MSDN Vídeo
>>
'Liberar el puerto
Protected Overrides Sub Finalize()
'cerrar el objecto serie
If PuertoSerieAbierto Then Serie.Close()
MyBase.Finalize()
End Sub
Fuente 7.25. Función Finalize, cuando nuestra aplicación deja de usarla y liberamos la clase.
'Estadisticas de bits recebidos/enviados
Private Sub Tiempo(ByVal sender As Object, ByVal e As Timers.ElapsedEventArgs) _
Handles Temporizador.Elapsed
Static Seg1m As Integer, VecesEnvioEnCurso As Integer
'
'Responder fuera de tiempo al cliente, si no se obtiene respuesta entregon+
If EnvioEnCurso Then
'Transmissión en curso sin respuesta
If (VecesEnvioEnCurso > 2) Then
'Durante dos segundos
EnvioEnCurso = False
UltimoMensajeError = "Sin Repuesta"
Recepcion = ""
RaiseEvent Respuesta()
'Disparar Repuesta de un time out
Else
VecesEnvioEnCurso += 1
'contabilizar disparo time out
End If
Else
VecesEnvioEnCurso = 0
'Resetear indicador fuera de tiempo
End If
'
'Temporizacion de 1 minuto
Seg1m += 1
If Seg1m > 59 Then
Seg1m = 0
Recibidos = CuentaBitsR 'Bits Recibidos por Minuto
Enviados = CuentaBitsE 'Bits Enviados por Minuto
CuentaBitsR = 0
'Puesta a cero de los contadores
CuentaBitsE = 0
'para almacenar el siguiente periodo
UltimoMensajeError = "" 'Borra mensaje error
End If
End Sub
página
Fuente 7.26. Función Tiempo, cada segundo…
93
<<
Robot dispensador para MSDN Vídeo
La función Tiempo implementa la supervisión de las respuestas. Nuestra premisa es
recibir una respuesta para cada solicitud, técnicamente cuando se lanza una solicitud y no
recibimos una respuesta, estamos hablando de un time out. El System.IO.Port, dispone de
potentes mecanismos para implementar y controlar estas situaciones, sin embargo, he
considerado oportuno, siguiendo las directrices de la simplicidad, no complicar ni profundizar excesivamente en el uso de System.IO.Ports, simplemente lo manejaremos a un
nivel tan básico que cualquier iniciado pueda seguir su funcionamiento.
Por lo que sería una buena excusa para poder escribir un “librillo” con el sugerente título: “¡System.IO.Ports a FONDO!” (la idea... ¡es mía!).
Por el momento nos conformaremos en controlar este tipo de incidencias desde
la librería. Por consiguiente la función Tiempo forzará un “Sin respuesta” en el caso de
mantener durante dos segundos una situación de envío en curso. ¡Viva la simplicidad!
De las operaciones de envío y recepcion, debemos destacar el uso del
alzado a verdadero por la función de envío, para tratarlo en la función de recepción como bandera que indica si el envío/recepcion forma parte de una
solicitud de nuestra aplicación o por el contrario corresponde al envio/recepcion de
la simulación; a tal fin destaquen el uso de Serie.Write en la rutina de recepción.
SemaforoSimulador,
Observen también cómo disparamos el evento de respuesta al cliente con RaiseEvent
una vez completada toda la operación de solicitud/simulación/repuesta.
Tampoco debemos perder la pista de cómo recibimos la notificación de los datos
recibidos por el puerto serie, ello se debe al manejador del evento Serie.DataReceived en la
función de recepción. O dicho de otro modo, System.IO.Ports nos dispara el evento
DataReceived siempre que el puerto serie reciba algún carácter en su entrada.
'Enviar una trama
Public Sub Enviar(ByVal Trama As String)
SemaforoSimulador = True
'La proxima trama cierra el circuito simulacion
Try
Serie.Write(Trama)
'Escribimos la trama al puerto serie
CuentaBitsE += Trama.Length * 11
'Contabilizamos los bits enviados
EnvioEnCurso = True
'Señalizar el estado de envio
Catch ex As Exception
'registrar mensage en caso de error
Mensaje.Registra("ComSerie", ex.Message, EventLogEntryType.Error, 6)
UltimoMensajeError = ex.Message
End Try
End Sub
Fuente 7.27. Función Enviar
página
94
Robot dispensador para MSDN Vídeo
>>
'
'Recibir una trama
Public Sub Recibir(ByVal sender As Object, ByVal e As _
System.IO.Ports.SerialDataReceivedEventArgs) Handles Serie.DataReceived
Try
EnvioEnCurso = False
'Limpiar memoria estado Envio
Recepcion = Serie.ReadExisting.ToString 'Leer chars del buffer recepcion
CuentaBitsR += Recepcion.Length * 11
'contabilizar bits recibidos
Catch ex As Exception
'registrar mensage en caso de error
Mensaje.Registra("ComSerie", ex.Message, EventLogEntryType.Error, 7)
UltimoMensajeError = ex.Message
End Try
'
'Simulador Envio/Recepcion entre Entregon y Servidor conectado a Dispensador
'
If SemaforoSimulador Then
'Debemos simular la operacion?
Try
'Simular peticion
Serie.Write(miSimulador.ProcesoDeSolicitudes(Recepcion))
CuentaBitsE += Recepcion.Length * 11 'Contabilizar bits recibidos
EnvioEnCurso =
'Señalizador de envio en curso
Catch ex As Exception
'registrar mensage en caso de error
Mensaje.Registra("ComSerie", ex.Message, EventLogEntryType.Error, 8)
UltimoMensajeError = ex.Message
End Try
SemaforoSimulador = False
'Borrar indicador de simulacion
Else
RaiseEvent Respuesta()
'Disparar respuesta al cliente
End If
End Sub
Fuente 7.28. Función Recibir
En la función New llamamos a la función AbrirPuertoSerie. Es evidente que antes
de poder realizar cualquier operación sobre el puerto serie, es necesario abrirlo, ¡ah!
y como veréis en la primera línea, ¡lo cerramos si es que está abierto!
Como ya estamos acostumbrados, lo primero que llevamos a cabo es la lectura
de los valores de configuración almacenados en el archivo app.config. Es interesante,
fijarnos en un mandato muy especial:
página
Serie = My.Computer.Ports.OpenSerialPort _
(Puerto, VelocidadPuerto, IO.Ports.Parity.Even, 7, IO.Ports.StopBits.Two)
95
<<
Robot dispensador para MSDN Vídeo
Nuestro objeto Serie llama al constructor del espacio de nombres System.IO.Ports
usando My, es una manera muy elegante de encontrar y usar los recursos en nuestros
espacios de nombres. Vale la pena entretenerse un poquito en descubrir todo lo que
nos esconde My.Computer a toque de punto y click, todo ello en una sola línea. ¡Es espectacular!
El resto es rutinario, las formalidades de siempre, si hemos conseguido o no disponer de acceso al puerto serie que teníamos configurado. Casi no me atrevo a
comentar que devolvemos un boolean que responde a verdadero si la operación se ha
realizado con éxito y falso si ha fracasado. Cómo no, cualquier excepción quedará
registrada en el apartado de aplicación del visor de sucesos.
'
' Crear el objecto de acceso Serie
Public Function AbrirPuertoSerie() As Boolean
'
If PuertoSerieAbierto Then Serie.Close()
'constructor del objecto serie
Try
'Leer los valores para abrir el puerto desde archivo app.config
NombreDelPuerto = Aplicacion.LeerConfig _
("app.config", "PuertoSerie_Nombre", NombreDelPuerto).InnerText
VelocidadPuerto = Aplicacion.LeerConfig _
("app.config", "PuertoSerie_Velocidad", VelocidadPuerto).InnerText
'
NumeroPuertoAbierto = Val(NombreDelPuerto.Substring(3, 1))
Serie = My.Computer.Ports.OpenSerialPort _
(NombreDelPuerto, VelocidadPuerto, IO.Ports.Parity.Even, _
7, IO.Ports.StopBits.Two)
UltimoMensajeError = NombreDelPuerto & "/" & VelocidadPuerto & "Open"
PuertoSerieAbierto = True
Catch ex As Exception
'registrar mensage en caso de error
Mensaje.Registra("ComSerie", ex.Message, EventLogEntryType.Error, 5)
NumeroPuertoAbierto = 0
UltimoMensajeError = ex.Message
PuertoSerieAbierto = False
End Try
Return PuertoSerieAbierto
End Function
Fuente 7.29. Función AbrirPuertoSerie
página
96
Robot dispensador para MSDN Vídeo
>>
Acceso a una base de datos Access
Llegados a este punto podríamos pensar que ya hemos completado la codificación de todas las clases que serán necesarias, tales como enviar correos, leer y grabar
valores en documentos XML de configuración, simular los mecanismos del ingenio,
etc. Sin embargo, es habitual tener necesidad de intercambiar datos con algún otro
tipo de archivo… ¡cómo no!, los MDB.
Sabemos su utilidad en ciertos proyectos donde no se requieren complejas estructuras o gran cantidad de datos. Además las bases de datos de Access nos permiten disponer
un buen nivel de intercambio para compatibilizar aplicaciones que comparten datos.
Si bien, más a título de ejemplo, que como necesidad básica de esta librería,
vamos también a implementar una clase que nos añada la funcionalidad de acceso a
datos usando OLEDB.
En nuestra aplicación realizaremos simplemente el registro de las cápsulas introducidas, con su correspondiente ISBN y NIFCIF, anotando simplemente si la capsula en cuestión está "Dentro" o "Fuera" del dispensador.
figura 7.7
página
En esta clase simplemente necesitaremos diseñar dos funciones, la de expulsar y
la de introducir.
97
<<
Robot dispensador para MSDN Vídeo
Para el acceso a un archivo Access deberemos disponer de una conexión a
y un adaptador a OleDbAdapter.
OleDbConnection
Completado este punto será tan sencillo como usar el UpdateCommand del adaptador para definir el Update del CommandText y su correspondiente ExecuteNonQuery.
La función expulsar nos pide dos valores: el ISBN y el NIFCIF. Cuando lanzamos la expulsión de la cápsula desde el dispensador, éste intenta actualizar el
valor de la misma a FALSE (ver SET DENTRO = FALSE). Si lo consigue (ExecuteNonQuery
nos devuelve un valor distinto de 0) la operación se considera satisfactoria, en caso
contrario reportaremos un mensaje alertando de que no existe la cápsula solicitada al dispensador.
De nuevo, ¡destacar la gran sencillez!
'
'Expulsar capsula y actualizar estado a 'false'(solo si esta en el dispensador)
'
Public Function Expulsar(ByVal ISBN As String, ByVal NIFCIF As String) As Integer
Try
Conexion.Open()
Adaptador.UpdateCommand = New System.Data.OleDb.OleDbCommand
Adaptador.UpdateCommand.Connection = Conexion
Adaptador.UpdateCommand.CommandText = _
"UPDATE Capsulas SET DENTRO = False WHERE (ISBN = '" + ISBN + "') _
AND (NIFCIF = '" + NIFCIF + "') AND (DENTRO = True)"
Adaptador.UpdateCommand.CommandType = System.Data.CommandType.Text
Expulsar = Adaptador.UpdateCommand.ExecuteNonQuery()
Conexion.Close()
Catch ex As Exception
Return 0
End Try
End Function
Fuente 7.30
Abordaremos nuestra recta final, con la función Introducir. No podremos
evitar incrementar la complejidad, pues en esta operación necesitaremos realizar
algunas operaciones más que en la anterior. A diferencia de cuando expulsamos
una cápsula, que esta se encuentra registrada o no, cuando la introducimos pueden producirse otras situaciones. Como por ejemplo que la cápsula no esté registrada en la base, que la cápsula ya se encuentre dentro del dispensador o que la
cápsula no pueda actualizar su estado. Para cubrir todas las posibilidades deberemos prever estas situaciones dotando a esta función de la habilidad suficiente para
página
98
Robot dispensador para MSDN Vídeo
>>
insertar un nuevo registro cuando no exista el mismo o bien actualizarlo en caso
de encontrarse en la base.
Por este el motivo deberemos contemplar los mandatos de INSERT y UPDATE.
Veamos también cómo usamos el viejo recurso de retornar un valor “888” o “999” para
señalizar una situación de error.
En nuestra función Introducir.
'
' Introducir una capsula, comprovando si esta existe para marcarla a 'true' o
creando registro en caso contrario
'
Public Function Introducir(ByVal ISBN As String, _
ByVal NIFCIF As String) As Integer
'--------------------------------------------------------------'No se contempla que un cliente pueda alquilar la misma pelicula
' Retorna :
'
0 = Si no puede actualizar la Capsula
'
1 = Si el proceso se ha efectuado con normalidad
'
888 = Existe duplicacion de capsulas
'
999 = Si existe un conflicto de estado o capula registrada dentro
Dim Datos As DataSet = New DataSet()
Dim Encontrados() As DataRow
Try
Adaptador.Fill(Datos, "Capsulas")
Encontrados = Datos.Tables("Capsulas").Select("(ISBN = '" + ISBN + "') _
AND (NIFCIF = '" + NIFCIF + "')")
If Encontrados.Length = 0 Then
If ISBN.Length = 13 And NIFCIF.Length = 9 Then
Conexion.Open()
Adaptador.InsertCommand = New System.Data.OleDb.OleDbCommand
Adaptador.InsertCommand.Connection = Conexion
Adaptador.InsertCommand.CommandText = _
"INSERT INTO Capsulas (ISBN, NIFCIF, DENTRO) VALUES ('" + _
ISBN + "'," + " '" + NIFCIF + "', True)"
Adaptador.InsertCommand.CommandType=System.Data.CommandType.Text
Introducir = Adaptador.InsertCommand.ExecuteNonQuery()
Conexion.Close()
End If
Else
If Encontrados.Length > 1 Then
página
Fuente 7.30 (continua...)
99
<<
Robot dispensador para MSDN Vídeo
Introducir = 888
Else
If Encontrados(0).Item(3) = True Then
Introducir = 999
Else
Conexion.Open()
Adaptador.UpdateCommand = New System.Data.OleDb.OleDbCommand
Adaptador.UpdateCommand.Connection = Conexion
Adaptador.UpdateCommand.CommandText = _
"UPDATE Capsulas SET DENTRO = True WHERE (ISBN = '" + _
ISBN + "') AND (NIFCIF = '" + NIFCIF + "')"
Adaptador.UpdateCommand.CommandType = _
System.Data.CommandType.Text
Introducir = Adaptador.UpdateCommand.ExecuteNonQuery()
Conexion.Close()
End If
End If
End If
Catch ex As Exception
Return
End Try
End Function
(... continuación) Fuente 7.30
Bien, queda claro que todo tiene un final y por fin podemos decir que nuestra
librería contiene todas las clases.
Sólo nos restará ejecutar el proceso de “Build” para obtener nuestra elaborada y
merecida ServidorComm.dll.
Antes de generar nuestra librería, resulta de mucha utilidad rellenar la información del ensamblado, donde se detallan los datos más relevantes de nuestro desarrollo, tales como el título, descripción, compañía, producto, copyright, marca, versión y
el identificador GUID.
Todo ello podemos encontrarlo en la pestaña de “Application” dentro de “My
Project” (ver figura 7.8).
¡Tengo la librería!
Pero ¿cómo la puedo probar antes de usarla con el dispensador real de MSDN
Vídeo?
página
100
Robot dispensador para MSDN Vídeo
>>
página
figura 7.8
101
capítulo
8
El ClienteEPlus
La solución
ClienteEPlus
A estas alturas más de uno estará pensando que tiene una sensación un tanto
extraña, nos hemos dedicado a plantear una librería entera ¡sin tan siquiera probarla!,
me confesaré: esta situación idílica es sólo posible para unos pocos privilegiados, quizás sólo algunos de nuestros grandes maestros como..., por no hablar de otros. Pero
aún y así me resisto a pensar que sea cierto.
Los libros como las películas nos presentan una situación perfecta dendro de un escenario donde no cabe posibilidad de error. Es evidente que a pesar de que muchos arquitectos sean capaces de planificar su desarrollo a niveles tan elevados que incluso puedan codificar una librería a pelo, tal como si estubiéramos hablando de unos cimientos, la mayoría
de mortales necesitamos construir las bases de nuestros cimientos desde el primer piso. Sé
que esto suena muy mal y aún con riesgo de que me excomulgen de mi apreciada comunidad de MVP, es cierto que antes de abordar la organización de la librería resulta muy útil
contruirla y probrala desde el cliente, de manera que vamos organizando y encapsulando
los conjuntos de código o spnnipeds que resultan útiles y comunes al resto de la aplicación.
Por lo tanto, a estas alturas debo confesarme, el desarrollo práctico lo hubiéramos empezado definiendo las necesidades del cliente y, a partir de éstas, codificando
el contenido de la librería, resumiendo, estaríamos construiendo nuestro cliente y
nuestra librería de una manera paralela.
página
Sin embargo, debemos consolarnos pensando en la existencia de un montón de
“teóricas” y “buenas prácticas” que quedan muy bien entre comillas. Por de pronto,
lo interesante es avanzar en un increíble entorno integrado de desarrollo, tampoco era
nuestra intención abordar técnicas avanzadas de codificación, me gustará entonces
reforzar la idea de que este libro debe servirnos como modelo para iniciar o migrar
sin complejos nuestras aplicaciones. Eso sí, siendo conscientes de las limitaciones de
migracion de grandes aplicaciones.
103
<<
Robot dispensador para MSDN Vídeo
Me gustaría pues desde aquí dar ánimos para iniciar nuestra aventura a todos
aquellos que dudan y se sienten atrapados en antiguos diseños; no conozco ningún
entorno de programacion mas productivo que Visual Studio 2005 y os aseguro que no
estoy haciendo marketing. Me he pasado media vida programando y es increíble ver
cómo conseguimos nuestros objetivos practicamente codificando cuatro líneas, recordando la aureola de los programadores y su mito me cuesta creer que yo soy uno de
ellos, pues realmente con estos entornos más bien me asemejo a un usuario avanzado.
Os estoy diciendo que ¡es muy fácil!
Bien, vamos a darle forma a nuestro cliente, será una buena experiencia iniciar
la construccion de nuestros contenedores con el nuevo diseñador de WinForms
(impresionante la colección de controles, listos y a punto para ser usados). La gran
difierencia con otras versiones es que ¡menudos controles!, nada parecido hasta la
fecha, por lo tanto, ¡venga! Con cuatro arrastrar y soltar veremos un formulario
repleto de cositas…
figura 8.1
página
104
Robot dispensador para MSDN Vídeo
>>
Debo hacer hincapié y destacar la utilidad del Document Outline (ver a la izquierda del diseñador), imprescindible para llevar a cabo una nomenclatura eficiente y
coherente. Su navegación permite asignar los nombres con una facilidad y agilidad
suficiente para que nunca más utilicemos los por defecto Label1, Command1...
figura 8.2
La aplicación del cliente se ha divido en tres partes: una superior conteniendo un
control de tablas con un primer marco que contiene los botones de solicitud de introducción y expulsión, juntamente con las cajas de texto para informar el ISBN y el
NIFCIF; la segunda pestaña del control tab contiene una lista que se llenará con los
textos correspondientes a la solicitud de estado del Entregon+. Finalmente la última
pestaña contiene los textboxes necesarios para informar de la configuración de envío de
correos electrónicos automáticos al detectar una alarma grave.
página
La parte media contiene un marco con las etiquetas que se utilizan para visualizar todos los mensajes de la aplicación, así como las específicas para monitorizar las
tramas enviadas y recibidas. Este marco así como el inferior están siempre visibles.
105
<<
Robot dispensador para MSDN Vídeo
figura 8.3
figura 8.4
La parte inferior contiene el control del puerto serie y sus mensajes así como la
posibilidad de seleccionar el puerto con el que queremos trabajar.
Deberán permitirme no detallar paso a paso cómo crear este proyecto con
Visual Studio, pues está fuera del alcance de nuestro propósito, quizás motivo de
un futuro anexo. A grandes rasgos nos interesa crear una solución que contenga
dos proyectos: ServidorComm y por supuesto ClienteEPlus. He decido incluir la librería ServidorComm pues nos ayudará a depurar los errores cometidos al diseñar o
teclear nuestras clases, sin embargo, podríamos afrontar el desarrollo del cliente,
simplemente añadiendo la referencia a nuestra ServidorComm.dll… ¡pero es menester ser prácticos!
Nuestro proyecto ClienteEPlus va a incluir dos archivos vitales para su funcionamiento, estos son el app.config con todos los valores de configuración de la aplicación
(incluidos los nuestros) y el Dispensador.mdb que no es más que la base de datos que
contiene la referencia de las películas que están en el interior del dispensador o las que
han sido entregadas. Además como recurso también incluiremos el icono de la aplicación App.ico.
página
106
Robot dispensador para MSDN Vídeo
>>
Seguramente, por coherencia, estos ficheros deberían estar bajo el control de las
librerías ServidorComm.dll. Sin embargo, por cuestiones prácticas, he decidido dejarlas
bajo el proyecto del cliente, al fin y al cabo, el resultado de la compilación dejará todos
sus componentes en la misma carpeta.
Este va a ser el aspecto de nuestra solución:
figura 8.5
Esta es la estructura de la base de datos Dispensador.mdb.
página
figura 8.6
107
<<
Robot dispensador para MSDN Vídeo
Y este es su icono:
figura 8.7
Es un gustazo poder editar los recursos en Visual Basic tal como hacen los buenos
en C++. No olvidar el uso de un modelo de cabecera común en nuestros items, que
identifique cada pieza dentro de nuestro desarrollo. En esta solución, como ya habréis
observado, he optado por configuración que detalla la información mas relevante en
cuanto al curso del desarrollo. Ni más ni menos que nuestro historial clínico.
'
'
'
'
'
'
'
'
'
'
'
'
'
'
'
'
'
'
'
Proyecto
Nombre
Tipo
Clase
Creado
Autor
Version
:
:
:
:
:
:
:
Entregon+
ClienteEPlus.vb
Aplicacion Windows
frmClienteEPlus
8.05.2005
Pep Lluis
1.0
Historia y Revisiones -----------------------------------------------------08.05.2005 - Ejemplo cliente, aplicacion Entregon+ (Incluye librerias) para
enlazar con la aplicación del Dipensador DesarrollaConMsdn.
Esta aplicacion se entiende unica y exclusivamente como modelo de
utilizacion y pruebas de las librerias ServidorCom.dll
14/15.05.05- Fin de Escritura y testeo de la aplicacion
24.07.05- Incorporar control disponibilidad capsulas Introduccion/expulsion
08.05- Liberar la aplicacion al completo
Pendiente : Revisar el tratamiento de errores de la libreria ServidorComm
Fuente 8.1
página
108
Robot dispensador para MSDN Vídeo
>>
En la siguiente figura veremos una vista general del código. Quiero resaltar la
importancia de las primeras definiciones, donde creamos instancias a los objetos
expuestos por nuestra clase ServidorComm. Vista general del código ClienteEPlus.vb para
el formulario de nuestro cliente.
figura 8.8
Tal como comentábamos anteriormente es importante el uso de cabeceras que
describen nuestro “Historial Clínico”, tan importante como agrupar las funciones
dentro de nuestras regiones de la aplicación.
página
Fijaros que son dos aspectos muy simples de observar, sin embargo, de vital
importancia para el trabajo en equipo. Marcar un modelo que identifica un estilo de
programación, compromete a los ingenieros involucrados en el proyecto a usar una
metodología común, que facilita extraordinariamente la lectura y localización del
código. Condiciones primordiales para conseguir un desarrollo eficiente y un mantenimiento de aplicaciones asequible. Pero subrayo: aplicaciones hechas en equipo.
109
<<
Robot dispensador para MSDN Vídeo
No es de extrañar que haga uso de esta receta agrupando los procedimientos en
torno a cuatro regiones: Cargar/liberar formulario, recepción y procesado del evento respuesta, interacción con el usuario y, finalmente, rutinas de uso general.
El primer grupo contiene la ejecución del código en tiempo de carga y descarga
del formulario, que normalmente realiza operaciones de preparación efectuando las inicializaciones necesarias para la puesta en marcha de la aplicación. La segunda, recepción
y procesado, contiene la subrutina que recibirá el disparo del evento en tanto disponemos
de una respuesta a nuestra petición. Esta respuesta será analizada y posteriormente procesada, no sin comprobar la validez de sus datos. El tercer grupo contiene el código que
se ejecuta a petición del usuario, a saber, cuando pulsa el botón para la solicitud de
expulsión, el botón de abrir el puerto serie, etc. Finalmente no es mala idea incluir una
región que contiene todos los procedimientos de uso general en nuestro formulario y
que no son parientes directos de ninguno de los anteriores.
Veamos todo esto con mayor detalle. Repasaremos las operaciones que el cliente debe realizar en tiempo de carga/descarga del formulario.
'
' Al cargar el formulario
'
Private Sub frmClienteEPlus_Load(ByVal sender As Object, ByVal e As _
System.EventArgs) Handles Me.Load
'imagen estado ko. del puerto serie Visible
Me.pbxKo.Visible = True
'direccionar los eventos de respuesta a la sub DatosRecibidos
AddHandler PuertoSerie.Respuesta, AddressOf DatosRecibidos
End Sub
Fuente 8.2
'
' Liberar objetos instanciados
'
Private Sub frmClienteEPlus_Disposed(ByVal sender As Object, ByVal e As _
System.EventArgs) Handles Me.Disposed
PuertoSerie = Nothing
'Liberar puerto serie
Campos = Nothing
'liberar constructor formato tramas
Aplicacion = Nothing
'liberar acceso archivo configuraciones app.config
End Sub
Fuente 8.3
página
110
Robot dispensador para MSDN Vídeo
>>
Podría parecer exceso de recelo, pero no liberar el puerto serie, probablemente
provocaría que éste no pudiera ser usado por otra aplicación; son detalles aparentemente ligeros pero de cierta relevancia si tenemos en cuenta una buena interacción
con el sistema. A menudo caemos en la equivocación de desarrollar pensando solamente en la aplicación y no en su anfitrión.
Continuaremos describiendo cómo el cliente va a procesar las respuesta a sus
solicitudes.
'
' Recepcion de respuestas
'
Public Sub DatosRecibidos()
miRespuesta = Me.PuertoSerie.Recepcion
'los datos recibidos
'Procesar y Visualizar la recepcion en la etiqueta
Me.lblRespuesta.Invoke(New EntreHilos(AddressOf ProcesolblRespuesta))
If Campos.LaTramaEstaCompleta(miRespuesta) Then
'Si la trama es completa
'procesar la respuesta, mostrando incidencias
Me.lblInicidencia.Invoke(New EntreHilos(AddressOf procesolblInicidencias))
End If
End Sub
Fuente 8.4
Destacar el enlace definido con el manipulador del evento PuertoSerie.Respuesta
y la función DatosRecibidos en tiempo de carga. También es importante observar cómo
en el DatosRecibidos invocamos al procedimiento ProcesolblRespuesta y, si además la
trama esta completa, al procesolblInicidencias (fuente 8.5).
¡Salta a la vista! Si la respuesta recibida está completa, continuaremos leyendo y
procesando la misma según su código de operación; en caso contrario, visualizaremos
la respuesta mandando el “fuera de servicio” a la etiqueta de incidencias.
Lo sabíamos de antemano, pero aquí lo plasmamos.
Sólo hemos implementado tres operaciones: “01”, “02”, “03”.
página
Las dos primeras tienen la importante función de actualizar nuestra base de datos
para que ésta refleje el estado de nuestras capsulas... DENTRO=Verdadero o Falso, en función
de si se expulsa o se introduce, además asigna la trama recibida a la etiquetas de respuesta para que ésta sea visualizada en nuestra aplicación cliente. La tercera operación, además de visualizar la trama recibida efectúa una llamada LlenarListaIndicadores que traducirá los códigos de incidencia en recibidos en hexadecimal a cadenas de texto que sean
interpretables por los humanos.
111
<<
Robot dispensador para MSDN Vídeo
'
' Procesar la recepcion segun los codigos de respuesta
'
Private Sub ProcesolblRespuesta()
If Campos.LaTramaEstaCompleta(miRespuesta) Then
'Con la respuesta completa
Select Case Campos.CodigoOpe
Case "00"
'Codigos operacion 00
' Simplemente visualizamos respuesta
Me.lblRespuesta.Text = "(" + miRespuesta + ")"
Case "01"
'Codigo 01 - Expulsar
'
CRAccesoBDS = MarcarBD.Expulsar(Me.txtISBN.Text, Me.txtNIFCIF.Text)
Me.lblRespuesta.Text = "(" + miRespuesta + ")"
Case "02"
'Codigo 02 - Introducción
'
CRAccesoBDS = MarcarBD.Introducir(Me.txtISBN.Text, Me.txtNIFCIF.Text)
Me.lblRespuesta.Text = "(" + miRespuesta + ")"
Case "03"
'Codigo 03 - Estado
'Llenamos la lista de indicadores estado
Me.lblRespuesta.Text = "(" + miRespuesta + ")"
Indicadores = "&h" + Campos.Indicadores
LlenarListaIndicadores()
Me.lblInicidencia.BackColor = Me.BackColor
Case Else
'Ignorar los codigos de operacion desconocidos
Me.lblRespuesta.Text = "Error Codigo :" & Campos.CodigoOpe
End Select
Else
'Sin respuesta completa reportamos error
Me.PnlExpulsion.BackColor = Color.Red
Me.lblRespuesta.Text = "(" + miRespuesta + ")"
Me.lblInicidencia.Text = "El mecanismo entrega, esta fuera de Servicio"
End If
End Sub
Fuente 8.5. ProcesoRespuesta
'
' Procesar los codigos fin de operacion
'
Private Sub procesolblInicidencias()
'
' Asignar texto segun la correspondencia de codigos / literales
' Nota: este codigo deberia ser leido de una base externa, no se incluye
' aquí para simpificar su entendimiento
Fuente 8.6. ProcesolblInicidencia (continua...)
página
112
Robot dispensador para MSDN Vídeo
>>
Select Case Campos.CodigoFin
'
Case "10"
If Campos.CodigoOpe = "01" Then
If CRAccesoBDS = 1 Then
'Codigo retorno acceso a la BD
Me.txtISBN.Text = ""
Me.txtNIFCIF.Text = ""
Me.lblInicidencia.Text = "La Capsula ha sido Entregada."
Else
Me.lblInicidencia.Text = "Capsula no se encuentra en el _
dispensador."
End If
Else
If Campos.CodigoOpe = "02" Then
Select Case CRAccesoBDS
'Codigo retorno acceso a la BD
Case 0
Me.lblInicidencia.Text = "Imposibe actualizar la BD_
Vaya al mostrador."
Case 888
Me.lblInicidencia.Text = "Existen capsulas _
Duplicadas."
Case 999
Me.lblInicidencia.Text = "Conflicto, la capsula _
ya esta en el dispensador"
Case Else
Me.txtISBN.Text = ""
Me.txtNIFCIF.Text = ""
Me.lblInicidencia.Text = "La Capsula ha sido _
Introducida."
End Select
Else
Me.lblInicidencia.Text = "Codigo retorno: " + Campos.CodigoOpe
End If
End If
Case "11" : Me.lblInicidencia.Text = "Existe una alarma en el sistema, _
Lea Su estado!" + Chr(10) + Chr(13) + _
"Atencion: se ha enviado una alerta por email"
Case "20" : Me.lblInicidencia.Text = "Producto en Stock Minimo"
Case "21" : Me.lblInicidencia.Text = "Entregado el Ultimo producto"
Case "22" : Me.lblInicidencia.Text = "Se estan terminando las capsulas"
Case "23" : Me.lblInicidencia.Text = "Se han detectado errores _
intermitentes en el Dispensador"
Case "24" : Me.lblInicidencia.Text = "No es posible localizar el _
producto"
Case "25" : Me.lblInicidencia.Text = "Imposible recoger el producto"
Case "26" : Me.lblInicidencia.Text = "Imposible apilar capsula almacen"
página
Fuente 8.6. ProcesolblInicidencia (continua...)
113
<<
Robot dispensador para MSDN Vídeo
Case "27" : Me.lblInicidencia.Text = "Problemas con el eje del cabezal"
Case "28" : Me.lblInicidencia.Text = "Atasco en el circuito de Distribucion"
Case "29" : Me.lblInicidencia.Text = "Falla Lector Etiquetas electronicas"
Case "30" : Me.lblInicidencia.Text = "Falla lectura etiqueta electronica"
Case Else : Me.lblInicidencia.Text = ""
End Select
'
' Asignar colores de mensages segun categoria e incidencia
Select Case Campos.CodigoFin
Case "10"
Me.PnlExpulsion.BackColor = Color.GreenYellow
Case "11", "24", "25", "26", "27", "28"
Me.PnlExpulsion.BackColor = Color.OrangeRed
Case "21", "22"
Me.PnlExpulsion.BackColor = Color.Blue
Case Else
Me.PnlExpulsion.BackColor = Color.Orange
End Select
(...continuación) Fuente 8.6. ProcesolblInicidencia
En el proceso de incidencias implementamos la interpretación del código de fin
de operación, sabemos que el Entregon+ nos devuelve este valor después del intercambio de tramas. Este código nos informa realmente de lo que ha sucedido con nuestra solicitud.
Destacar la verificación que se efectúa con CRAccesoBDS (código de retorno después del acceso a la base de datos) y siempre dentro del CodigoFin=10, pues como bien sabéis
representa que la operación se ha realizado con éxito.
El resto de códigos simplemente asignan a la etiqueta de incidencias el texto de
error que corresponde a ese código y finalmente asigna el color amarillo, rojo naranja, azul o naranja al fondo del panel, según sea la gravedad del mensaje. La distribución del código dentro del formulario se ha llevado a cabo según las funciones del
mismo. Observaremos cuatro regiones: carga/descarga, interacción con el usuario, proceso
y rutinas generales.
Les toca el turno a las rutinas generales.
Evidentemente ésta respondería al proceso de una solicitud de estado “03”
(fuente 8.7).
Simplemente rellenamos la lista de indicadores con los mensajes de error reportados en la última trama recibida y que están activos.
página
114
Robot dispensador para MSDN Vídeo
>>
'
' Rellenar la lista de indicadores despues de una solicitud de Estatus
'
Private Sub LlenarListaIndicadores()
Static MemoriaIndicadores As Integer
'Evitar refrescos si no ha cambiado el contenido
If MemoriaIndicadores = Indicadores Then Exit Sub
MemoriaIndicadores = Indicadores
'memorizar el estado actual
Me.lstIndicadores.Items.Clear()
If Indicadores And Codigo.AlmacenCapsulasVacio Then
Me.lstIndicadores.Items.Add("CAPSULAS AGOTADAS EN EL DISPENSADOR")
If Indicadores And Codigo.AlmacenProductoVacio Then
Me.lstIndicadores.Items.Add("DISPENSADOR, SIN PRODUCTO")
If Indicadores And Codigo.AtascoDispensadorEntrada Then
Me.lstIndicadores.Items.Add("ATASCO MECANISMO DE ENTRADAS DISPENSADOR")
If Indicadores And Codigo.AtascoDispensadorSalida Then
Me.lstIndicadores.Items.Add("ATASCO MECANISMO DE SALIDAS DISPENSADOR")
If Indicadores And Codigo.AtascoPinzaDispensadora Then
Me.lstIndicadores.Items.Add("PINZA DISPENSADORA ATASCADA DISPENSADOR")
If Indicadores And Codigo.AveriaEjeCabezal Then
Me.lstIndicadores.Items.Add("EJE CABEZAL AVERIADO UNIDAD")
If Indicadores And Codigo.AveriaEnPinzaDispensadora Then
Me.lstIndicadores.Items.Add("PINZA DISPENSADORA AVERIADA UNIDAD")
If Indicadores And Codigo.AveriaEnPlacaControladora1 Then
Me.lstIndicadores.Items.Add("SUBSTITUIR PLACA CONTROLADORA 1")
If Indicadores And Codigo.AveriaEnPlacaControladora2 Then
Me.lstIndicadores.Items.Add("SUBSTITUIR PLACA CONTROLADORA 2")
If Indicadores And Codigo.DisparoDetectorAntivandalico Then
Me.lstIndicadores.Items.Add("SE HA DISPARADO LA ALARMA DE ANTIVANDALICA")
If Indicadores And Codigo.FalloEnProcesador1 Then
Me.lstIndicadores.Items.Add("HA FALLADO PROCESADOR PRINCIPAL ENTREGON+")
If Indicadores And Codigo.FalloEnProcesador2 Then
Me.lstIndicadores.Items.Add("HA FALLADO PROCESADOR SECUNDARIO ENTREGON+")
If Indicadores And Codigo.SobreTemperatura Then
Me.lstIndicadores.Items.Add("SOBRETEMPERATURA ENTREGON+")
If Indicadores And Codigo.SuciedadOpticaPosicionadorLaser Then
Me.lstIndicadores.Items.Add("LIMPIAR/ALINEAR LASE DEL ENTREGON+")
End Sub
Fuente 8.7
página
Nuestro formulario contiene un temporizador de un segundo, por lo tanto después de este intervalo se ejecuta esta función que básicamente se encargará de refrescar la información más relevante para el usuario, tales como la presentación de los bits
transmitidos/recibidos, así como hacer visibles o invisibles los paneles de errores, des-
115
<<
Robot dispensador para MSDN Vídeo
pués de haberlos visualizados durante un tiempo determinado, como también el control de pequeñas filigranas que hacen más atractiva la interacción de la aplicación con
el usuario.
No es necesario analizar el código con más detalle, pues a pesar de su aspecto
estético, no lo considero de interés técnico... recordemos que nuestro objetivo es el
correcto uso de las librerías del Entregon+. Tampoco quiero aconsejar estas técnicas
para mantener el aspecto y refresco de información en pantalla; como siempre es una
solución de compromiso y, como digo, nos interesa concentrarnos en el cómo.
'
' Actualizar los datos de nuestro formulario
'
Private Sub tmpRefresco_Tick(ByVal sender As System.Object, ByVal e As _
System.EventArgs) Handles tmpRefresco.Tick
On Error Resume Next
Static Veces As Integer, Mensaje As String, VecesRegistro As Integer
'Filigranas relacionadas con refresco, intermitencia de colores y mensajes
If Me.lblInicidencia.Text.Length > 0 Then
If Mensaje <> Me.lblInicidencia.Text Then Veces = 0
Veces += 1
If Veces < 11 Then
'Durante 10 segundos visualizar mensaje en el centro e intermitente
Mensaje = Me.lblInicidencia.Text
Me.lblInicidencia.TextAlign = ContentAlignment.MiddleCenter
If Me.lblInicidencia.ForeColor = Me.PnlExpulsion.BackColor Then
Me.lblInicidencia.ForeColor = Color.Black
Me.lblInicidencia.BackColor = Me.BackColor
Else
Me.lblInicidencia.ForeColor = Me.PnlExpulsion.BackColor
Me.lblInicidencia.BackColor = Color.Black
End If
Else
'Pasados 10 segundos apagar intermitencia y alinear a la izquierda
Me.lblInicidencia.TextAlign = ContentAlignment.TopLeft
End If
Else
Veces = 0
End If
'
If Me.PuertoSerie.PuertoSerieAbierto Then
'Permitir solicitudes y dejar visible la imagen de puerto OK.
Me.btnIntroducir.Enabled = True
Me.btnExpulsar.Enabled = True
Fuente 8.8. (continua...)
página
116
Robot dispensador para MSDN Vídeo
>>
Me.pbxKo.Visible = False
Me.pbxOk.Visible = True
Else
'Denegar solicitudes y dejar visible la imagen de puerto KO.
Me.btnIntroducir.Enabled = False
Me.btnExpulsar.Enabled = False
Me.pbxKo.Visible = True
Me.pbxOk.Visible = False
End If
Me.pbxTry.Visible = False
'Solo visible en el momento de abrir el puerto
'
'Asignar mensajes del PuertoSerie a las etiquetas de nuestro 'Form'
Me.lblTextoMensaje.Text = Me.PuertoSerie.UltimoMensajeError
Me.lblBitsRecibidos.Text = Me.PuertoSerie.Recibidos.ToString
Me.lblBitsEnviados.Text = Me.PuertoSerie.Recibidos.ToString
'
'Alertar de que existen mensajes en el EventLog
If ServidorComm.RegistroAlertas.MensajesEnRegistro Then
VecesRegistro += 1
Me.lblTextoMensaje.Text = Me.lblTextoMensaje.Text + _
Chr(10) + Chr(13) + "**Mensajes en Registro Aplicacion del Sistema"
If VecesRegistro > 10 Then
VecesRegistro = 0
ServidorComm.RegistroAlertas.MensajesEnRegistro = False
End If
End If
'
'Si no exiten mensajes, esconder la etiqueta visualizacion
If Me.PuertoSerie.UltimoMensajeError.Length = 0 And _
ServidorComm.RegistroAlertas.MensajesEnRegistro = False Then
Me.lblTextoMensaje.BorderStyle = BorderStyle.None
Me.lblMensaje.Visible = False
Else
Me.lblTextoMensaje.BorderStyle = BorderStyle.Fixed3D
Me.lblMensaje.Visible = True
End If
'Asignar el valor del puerto al numeric Up/Down
If Me.PuertoSerie.UltimoMensajeError.Substring(0, 3) = "COM" Then
Me.nudPuerto.Value = Val(Me.PuertoSerie.UltimoMensajeError.Substring(3, 1))
End If
If Me.nudPuerto.Enabled = False Then Me.nudPuerto.Enabled = True
End Sub
página
(...continua) Fuente 8.8
117
<<
Robot dispensador para MSDN Vídeo
En la recta final, sólo nos queda codificar la parte de interacción con el usuario,
todas las rutinas detalladas a continuación, tienen en común la ejecución de código
bajo demanda del usuario, o sea que sólo se ejecutan cuando el usuario ha llevado a
cabo una determinada acción sobre los controles.
'
' Al Incrementar / Disminuir seleccion de puerto de trabajo
'
Private Sub budPuerto_ValueChanged(ByVal sender As System.Object, ByVal e As _
System.EventArgs) Handles nudPuerto.ValueChanged
'Siempre que el valor se la seleccion se encuentre entre 1 i 8
If (Me.nudPuerto.Value < 9) And (Me.nudPuerto.Value > 0) Then
'guardar la seleccion de puerto en archivo app.config
'asi lo abriremos por defecto la proxima session
Aplicacion.SalvarConfig("app.config", "PuertoSerie_Nombre", "COM" & _
Me.nudPuerto.Value.ToString)
Else
'forzar a 1 cuando los valores esten fuera de rango
nudPuerto.Value = 1
End If
End Sub
Fuente 8.9. Cuando el usuario pulsa el Up/Down del control numérico o introduce un valor en su entrada.
'
' Abrir el puerto de comunicaciones
'
Private Sub btnAbrir_Click(ByVal sender As System.Object, ByVal e As _
System.EventArgs) Handles btnAbrir.Click
'a la pulsacion del boton de abrir
Me.pbxTry.Visible = True
'visualizar imagen : intento abrir puerto
Me.pbxKo.Visible = False
'esconder imagen Ko
Me.pbxOk.Visible = False
'esconder imagen Ok
Me.PuertoSerie.AbrirPuertoSerie() 'Abrir el puerto Serie
End Sub
Fuente 8.10. Cuando el usuario pulsa el botón de abrir el puerto seleccionado en el control Up/Down
página
118
Robot dispensador para MSDN Vídeo
>>
'
' Enviar un correo de pruebas
'
Private Sub btnPrueba_Click(ByVal sender As System.Object, ByVal e As _
System.EventArgs) Handles btnPrueba.Click
'a la pulsacion de btnPruebas
'Instanciar la clase para envio de correo
Dim Enviar As AlertasPorCorreo = New AlertasPorCorreo
Enviar.Avisar("Prueba de Correo Automatizado")
'enviar el correo
End Sub
Fuente 8.11. Cuando el usuario pulsa el botón de prueba en el panel “AutoeMail”
'
' Leer la configuracion personal para el envio de alertas por email
'
Private Sub btnLeer_Click(ByVal sender As System.Object, ByVal e As _
System.EventArgs) Handles btnLeer.Click
'a la pulsacion de btnLeer, asignar los contenidos guardados en app.config
Dim email_From As String = _
Aplicacion.LeerConfig("app.config", "email_From", "emailOrigen").InnerText
Dim email_To As String = _
Aplicacion.LeerConfig("app.config", "email_To", "emailDestino").InnerText
Dim email_Cc As String = _
Aplicacion.LeerConfig("app.config", "email_Cc", "emailCopia").InnerText
Dim email_User As String = _
Aplicacion.LeerConfig("app.config", "email_User", "CuentaUsuario").InnerText
Dim email_Pwd As String = _
Aplicacion.LeerConfig("app.config", "email_Pwd", "password").InnerText
'
Me.txtEmail_From.Text = email_From
'enseñar la cuenta de usuario
If txtEmail_Pwd.Text = email_Pwd Then
'enseñar resto de datos si coinciden
Me.txtEmail_To.Text = email_To
Me.txtEmail_Cc.Text = email_Cc
Me.txtEmail_User.Text = email_User
Else
Me.txtEmail_User.Text = "No coincide la clave"
End If
End Sub
página
Fuente 8.12.Al pulsar el botón leer (leemos la configuración para envío automatizado) panel “AutoeMail”
119
<<
Robot dispensador para MSDN Vídeo
'
'Guardar valores de configuracion personal, para el envio de alertas por email
'
Private Sub btnSalvar_Click(ByVal sender As Object, ByVal e As _
System.EventArgs) Handles btnSalvar.Click
'a la pulsacion de btnSalvar, guardar valores de configuracion de los textbox
Aplicacion.SalvarConfig("app.config", "email_From", Me.txtEmail_From.Text)
Aplicacion.SalvarConfig("app.config", "email_To", Me.txtEmail_To.Text)
Aplicacion.SalvarConfig("app.config", "email_Cc", Me.txtEmail_Cc.Text)
Aplicacion.SalvarConfig("app.config", "email_User", Me.txtEmail_User.Text)
Aplicacion.SalvarConfig("app.config", "email_Pwd", Me.txtEmail_Pwd.Text)
End Sub
Fuente 8.13. Guardaremos las entradas de texto informadas en el panel “Autoemail” al pulsar el botón “Salvar”
'
' Expulsar una capsula
'
Private Sub btnExpulsar_Click(ByVal sender As System.Object, ByVal e As _
System.EventArgs) Handles btnExpulsar.Click
'a la pulsacion del btnExpulsar
Dim peticion As Tramas.SolicitarExpulsion = New Tramas.SolicitarExpulsion
'Trama de expulsion
Dim miPregunta As String = ""'Inicializar variables
Me.lblInicidencia.Text = ""
Me.lblRespuesta.Text = ""
'Componer la peticion utilizando los campos de entrada ISBN/NIFCIF
miPregunta = peticion.Trama(2, 1, Me.txtISBN.Text, Me.txtNIFCIF.Text)
Try
Me.PuertoSerie.Enviar(miPregunta)
'Enviar peticion
Me.lblPregunta.Text = "(" + miPregunta + ")" 'testigo peticion etiqueta
Catch ex As Exception
Me.lblInicidencia.Text = ex.Message
'En caso de problema..
Me.lblPregunta.Text = "(" + miPregunta + ")"
End Try
End Sub
Fuente 8.14.Al pulsar el botón de expulsión, generaremos una solicitud de entregar una cápsula.
página
120
Robot dispensador para MSDN Vídeo
>>
' Introducir una capsula
Private Sub btnIntroducir_Click(ByVal sender As System.Object, ByVal e As _
System.EventArgs) Handles btnIntroducir.Click
'a la pulsacion del btnIntroducir
'Trama de introduccion
Dim peticion As Tramas.SolicitarIntroduccion = New Tramas.SolicitarIntroduccion
'Inicializar variables
Dim misDatos As String = ""
Me.lblInicidencia.Text = ""
Me.lblRespuesta.Text = ""
'
'Sobrecarga para emular los datos (ISBN/DNI) del lector de etiquetas
misDatos = peticion.Trama(2, 1, Me.txtISBN.Text, Me.txtNIFCIF.Text)
'Componer la peticion
Try
Me.PuertoSerie.Enviar(misDatos)
'Enviar peticion
Me.lblPregunta.Text = "(" + misDatos + ")"
'testigo de la peticion
Catch ex As Exception
Me.lblInicidencia.Text = ex.Message
'En caso de problemas..
Me.lblPregunta.Text = "(" + misDatos + ")"
End Try
End Sub
Fuente 8.15.Al pulsar el botón de introducción, generaremos la solicitud de devolución de una cápsula.
' Solicitar el estado del Entregon+, por aviso de incidencia
Private Sub verEstatus(ByVal sender As System.Object, ByVal e As _
System.EventArgs) Handles tabControl.Click, tabControl.Enter
'al efectuar click sobre la pestaña de estatus del tabcontrol
If Me.tabControl.SelectedIndex = 1 Then
'ver estado seleccionado
'Trama de estado
Dim peticion As Tramas.SolicitarEstatus = New Tramas.SolicitarEstatus
'Componer peticion
Dim misDatos As String = peticion.Trama(1, 2)
Try
PuertoSerie.Enviar(misDatos)
'Enviar peticion
Me.lblPregunta.Text = "(" + misDatos + ")"
'testigo de la peticion
Catch ex As Exception
Me.lblInicidencia.Text = ex.Message
'En caso de problemas..
Me.lblPregunta.Text = "(" + misDatos + ")"
End Try
End If
End Sub
página
Fuente 8.16.Al hacer un clic en la pestaña “Estatus” del control tab, generaremos una solicitud “03” para
conocer el estado del Entregon+.
121
<<
Robot dispensador para MSDN Vídeo
[F5]
¡No me lo puedo creer! En un tristrás hemos desarrollado una librería de enlace para el Entregon+ y además nos hemos entretenido implementado un cliente para
probar y explotar sus funciones.
Sí amigos, si hemos seguido los pasos correctamente, no hay nada que temer, he
aquí nuestra recompensa, nos merecemos un [F5].
figura 8.9
Espero que esto sea todo y no me esté olvidando de nada, pero por si acaso, a los que
después de sufrir, no obtengáis este resultado... me pongo a vuestra disposición para “lo
que haga farta”, para cualquier consulta o pregunta relacionada con Entregon+, podréis
contactar conmigo en [email protected].
página
122
capítulo
9
De beta 1 a beta 2
Después de este laboratorio, me gustaría comentaros unas sutiles diferencias con los
anteriores laboratorios. Los tres primeros están desarrollados con la versión beta 1 de
Visual Studio 2005, sin embargo, abordaremos la construcción de este último con la beta
2. Como siempre nos surgirá la pregunta: ¿qué ha cambiado ? y como siempre la respuesta: ¡pues no lo se!
Sabemos que cualquier producto en fase beta o releases para la comunidad, no
deben usarse con fines productivos, o sea nuestro caso, pero nos podemos permitir el
lujo de juguetear con nuestro pequeño proyecto.
La verdad es que este proyecto se inició con la Beta 1 y me gustaría comentaros
a título de curiosidad qué partes quedaron afectadas en esta transición. Lo que es cierto y tal como estamos acostumbrados, la beta 2 no fue capaz de hacerlo rodar, sin solucionar un par de tonterías reportadas en la pestaña de errores.
Y ellos fueron los siguientes:
página
figura 9.1
123
<<
Robot dispensador para MSDN Vídeo
Como veréis a continuación ¡nada grave! SerialReceivedEventArgs pasó a llamarse
SerialDataReceivedEventArgs.
figura 9.2
ReceivedEvent
paso a llamarse DataReceived
figura 9.3
Lo más significativo fueron los warnings avisando de que el espacio de nombres
dedicado a System.Web.Mail.SmtpMail está obsoleta y ésta va a ser deprecated (que no se lo
que significa exactamente).
página
124
Robot dispensador para MSDN Vídeo
>>
figura 9.4
página
Nos recomienda encarecidamente que usemos el nuevo espacio de nombres
System.Net.Mail.
125
Descargar