• No se han encontrado resultados

DESARROLLO DE APLICACIONES PARA CONTROL Y MONITOREO DE DISPOSITIVOS REMOTOS

N/A
N/A
Protected

Academic year: 2021

Share "DESARROLLO DE APLICACIONES PARA CONTROL Y MONITOREO DE DISPOSITIVOS REMOTOS"

Copied!
93
0
0

Texto completo

(1)

D E S A R R O L L O D E

A P L I C A C I O N E S P A R A

C O N T R O L Y M O N I T O R E O D E

D I S P O S I T I V O S R E M O T O S

PRESENTAN:

CRISÓSTOMO BRAVO ENRIQUE

ORTEGA TORRES ANA LILIA

UNIDAD IZTAPALAPA

DIVISIÓN DE CIENCIAS BÁSICAS E INGENIERIA

PA R A O B T E N E R E L T I T U L O D E

L I C E N C I A D O E N C O M P U TA C I O N

México D. F. Julio 2007

____________________________

M en C. Omar L. Cabrera Jiménez

(2)

Resumen:

Este trabajo describe el entorno de control y monitoreo remoto de un Robot conectado al puerto paralelo de una PC. Este entorno, desarrollado en lenguaje Java permite enviar órdenes de movimientos y acciones que el robot es capaz de realizar de acuerdo al estado presente en el registro de datos del puerto conectado en una computadora Servidor y mas aún, se tiene una estructura que permite lanzar programas residentes en la computadora remota mediante comandos en la computadora Cliente. De modo que esto nos permite incorporar nuevos programas o utilizar los que existen en el sistema para darle un conjunto escalable de funciones que el robot puede realizar; principalmente multimedia, controlados todos de manera remota independientemente de la red de que se disponga pero cuya disponibilidad debe estar garantizada. Esta versión ha sido probada sobre diferentes tecnologías de red como 802.11 y Bluetooth. También se incluyen los resultados de las pruebas realizadas.

(3)

Indice Presentación . . . 3 Justificación . . . . . . 3 Objetivos . . . 4 1. Introducción . . . 5 2. Marco Teórico. . . 6

2.1 Programación con sockets . . . 6

2.2 Hilos . . . 10

2.3 Applets . . . 20

2.4 Ejemplo de construcción de un Applet . . . 26

2.5 Programación del Puerto Paralelo . . . 29

2.6 Driver UserPort.sys . . . 42

2.7 La API Genuts . . . 43

3. Especificación de requerimientos . . . 44

3.1 Estructura de las tramas de control del robot . . . 44

3.2 Comunicación entre cliente y servidor . . . 46

3.3 Plataforma de operación . . . 46

3.4 Velocidad de respuesta . . . 46

3.5 Control de programas remotos . . . 46

3.6 Interfaz gráfica . . . 46

4. Modelo de requerimientos . . . 47

4.1 Modelo de casos . . . 47

4.2 Modelo de interfaz . . . 49

4.3 Modelo de dominio del problema . . . 51

5. Aplicación desarrollada . . . 52

5.1 Descripción general . . . 52

5.2 Diseño conceptual . . . 52

5.3 Esquema general de comunicaciones . . . 53

5.4 Diagramas de clases . . . 54

6. Pruebas realizadas . . . 57

7. Recomendaciones . . . 58

8. Conclusiones . . . 59

9. Referencias . . . 60

Anexo A. Código fuente . . . 61

A.1. Programa Joystick . . . 61

A.2. Programa Monitor . . . 73

A.3. Programa controlador . . . 77

(4)

Presentación

Se desea desarrollar y evaluar aplicaciones de software para control y monitoreo de dispositivos electromecánicos mediante redes de computadoras conectadas con diferentes medios.

Justificación

El desarrollo de software ha sustituido en gran medida el proceso de desarrollo de hardware y esto, entre otras causas; ha hecho posible una disminución en costos y rápido desarrollo de nuevas tecnologías. La posibilidad de sustituir dispositivos electrónicos por un entorno programable de amplias posibilidades como una PC permite libertades en el diseño de algoritmos y diferentes soluciones para el control de dispositivos remotos que de otro modo nos restringirían a un diseño, el cual una vez llevado a la etapa de pruebas puede ser modificado pero con el costo de adquirir nuevos dispositivos y además con variaciones en las características eléctricas de los dispositivos que deben ser consideradas por mínimas que estas sean.

Aún cuando existen dispositivos programables como PICs y PLCs que nos permiten un avance en este sentido, la tecnología de computadoras ha evolucionado notablemente al grado de contar con puertos de comunicación como BlueTooth, puerto paralelo, serial, redes inalámbricas de alta velocidad y USB ademas de aplicaciones multimedia y todo esto con un funcionamiento eficiente probado en diferentes entornos.

De este modo entramos a un campo que originalmente dio soluciones con un punto de vista puramente electrónico mediante radio control y circuitos combinatorios y secuenciales en conjunto con dispositivos programables, memorias EPROM etc. Aprovechamos ahora el avance en el campo de nuevas tecnologías de información y comunicación para sustituir en gran medida la electrónica por el desarrollo de software, cambiando el laboratorio de electrónica por una computadora de escritorio y con un mínimo de interfaces electrónicas que incluso pueden ser modeladas por computadora.

(5)

Objetivos:

• Analizar diferentes esquemas para el control y monitoreo remoto de dispositivos electromecánicos proponiendo el uso de diferentes algoritmos.

• Establecer diversos medios de comunicación, los cuales permitan probar los programas de cómputo a desarrollar para el control y monitoreo de dispositivos electromecánicos. • Construir diferentes esquemas de control y monitoreo remoto para dispositivos

electromecáncios implementando diferentes mecanismos de comunicación.

• Programar diferentes algoritmos de control y monitoreo para dispositivos remotos conectados a una red de computadoras.

• Evaluar los resultados de diferentes pruebas de operación del sistema de control y monitoreo desarrollado sobre diferentes medios y condiciones de operación.

• Proponer algunas líneas de investigación y mejoras a los equemas y algoritmos que se analicen basados en los resultados de sus pruebas de operación.

(6)

1. INTRODUCCIÓN

Los avances en tecnologías de computo han tenido un mayor desarrollado en los últimos años a nivel mundial y se ha acentuado muchísimo más en los países del primer mundo, sin embargo en los países en vías de desarrollo es necesario el desarrollo y la investigación en este campo. En el caso de México el avance tecnológico se ha visto afectado al igual que en otros países en vías de desarrollo por la carencia del factor humano suficientemente preparado para analizar, diseñar, producir, coordinar, manejar o mantener estas tecnologías. Esta necesidad y la oportunidad de participar en este tipo de proyectos, nos ha motivado a trabajar, para lograr el resultado que estamos presentando.

El control de dispositivos remotos es un área en la que se pueden encontrar una gran variedad de aplicaciones. El creciente desarrollo de internet y de redes locales, no solo nos da un medio a través del cual podemos comunicarnos para este propósito, sino que además nos permite incorporar nuevas tecnologías de acuerdo a necesidades específicas. Mas aún, es tecnología que se encuentra disponible ampliamente y que evoluciona proporcionando mejores características y mejor desempeño en poco tiempo.

El esquema que presentaremos proporciona el control remoto específico de un robot conectado al puerto paralelo de una computadora (servidor) desde otra estación (cliente), comunicándose en un entorno de red local, convencional o inalámbrica; mediante el protocolo TCP/IP. Este esquema se puede aplicar para el control remoto de otros dispositivos que tengan una interfaz para conectarse al puerto paralelo con un mínimo de modificaciones al software desarrollado.

El diseño permite desarrollar e incorporar nuevas aplicaciones, a ejecutarse en la estación servidor, las cuales pueden ser invocadas remotamente desde la línea de comandos del cliente, o bien utilizar programas ya disponibles en el servidor.

En el cliente se cuenta con un monitor que recibe mensajes y los publica, estos mensajes pueden ser enviados por cualquier aplicación actual en el servidor o que sea desarrollada a futuro. El servidor envía la salida de cualquier aplicación invocada desde el cliente a este monitor lo que nos permite saber lo que se están ejecutando.

La programación de esta aplicación ha sido desarrollada en Java, lenguaje que nos permite independencia de plataforma para el programa cliente. Por otro lado Java nos proporciona diferentes conjuntos de clases que ya han sido programadas y satisfacen una amplia variedad de necesidades que al ser reutilizadas nos permiten un rápido avance en el desarrollo de la aplicación. Dado que el servidor esta programado para interactuar con la línea de comandos de MS-DOS, y además utiliza bibliotecas de enlace dinámicas DLL para el control del puerto paralelo, este se ejecuta solo en plataforma Windows.

Durante el desarrollo del proyecto se enfrentaron diferentes problemáticas cuya solución nos llevó a la conclusión del trabajo que estamos presentando. Desde la elección del lenguaje de programación que se contempló como la mejor opción para resolver el problema de ejecución independiente del sistema operativo, la capacidad de interactuar con otros programas desarrollados en lenguaje nativo C si se requiriera, la capacidad de invocar programas ejecutables, etc., hasta la forma en que Windows XP, NT o 2000 bloquean los puertos por cuestiones de seguridad y que dificultan su interacción con el programador.

(7)

2. MARCO TEÓRICO

Hemos agregado los conceptos fundamentales que han hecho posible el desarrollo de este trabajo. Omitimos conceptos básicos de programación orientada a objetos, dado que no es el objetivo el presentar un tratado extensivo sobre paradigmas de programación. Aquí se muestra de forma general de conceptos que han sido considerados para el desarrollo de la aplicación y para poder extender la misma.

Iniciamos con los conceptos involucrados de la programación en Java referentes a la manera en que se llevan a cabo las comunicaciones entre los diferentes programas o módulos que conforman la aplicación. Posteriormente presentamos la estructura del puerto paralelo con su programación y finalmente se explica la teoría que nos ayuda a resolver el problema de lectura/escritura en los puertos en entorno Windows que por razones de seguridad es restringido a los usuarios en versiones XP, 2000 y NT

2.1. Programación con sockets

Para nadie es ya desconocida la necesidad existente de comunicación de datos independientemente de la distancia que separe las estaciones a ambos extremos y, sobre todo, independientemente de la red en que estén localizadas.

Desafortunadamente, la mayor parte de las redes son entidades independientes, establecidas para servir a las necesidades de un grupo único. Lo que es más, es imposible construir una red mundial con una única tecnología de hardware porque ninguna cumpliría con las necesidades de todos los usuarios (algunos necesitarían grandes velocidades de comunicación a corta distancia, mientras que otros desearían conectar computadoras a miles de kilómetros).

Es por tanto necesario proporcionar procedimientos que permitan la interconexión de redes distintas, independiente de la tecnología de hardware utilizada. Este tipo de problemas son los que se denominan internetworking (o interconexión de redes) y su solución ha permitido extender las posibilidades de intercambio de información entre los distintos usuarios de distintas redes independientemente de su tecnología y de su localización, a través de la Internet (también llamada red de redes).

La tecnología Internet es un ejemplo de interconexión de sistemas abiertos. Se llama sistema abierto porque, al contrario que los sistemas de comunicaciones propietarios, las especificaciones están al alcance del público. Así, cualquiera puede construir el software necesario para comunicarse a través de Internet.

Además, toda la tecnología ha sido diseñada para conseguir la comunicación entre máquinas de diferentes arquitecturas, con distintos sistemas operativos y usando casi cualquier tipo de red. TCP/IP ha sido implementado en muchos y diferentes tipos de redes comerciales. Cada una de estas redes, dependiendo de su tecnología y forma de funcionamiento, adapta el protocolo TCP/IP a sus características de una forma diferente. IP es un protocolo de red en el que los hosts “cliente y servidor” se identifican globalmente con la dirección IP, que consiste en octetos (32 bits) de la forma 156.35.14.2, donde parte de la dirección identifica a la red y parte al host dentro de esa red. No obstante, los programas utilizan usualmente direcciones del tipo hal.ugr.es que identifican al host y al dominio al que pertenece.

(8)

En la capa de transporte las entidades se identifican con el número de puerto (16 bits), que identifica el proceso dentro del host al que esta destinada o del que proviene la información. TCP/IP reserva una serie de puertos bien conocidos para las aplicaciones estándar (por ejemplo, 7 para el servidor de ECHO, 13 para el servidor de DAYTIME, 21 para el servidor FTP, 23 para el servidor TELNET y 25 para el servidor SMTP) y deja sin definir otros para las aplicaciones no estándares desarrolladas por los usuarios (advertencia: para aplicaciones de usuario se deben usar números de puerto por encima de 1000 ya que los primeros están reservados para root). La programación de aplicaciones sobre TCP/IP se basa en el modelo cliente-servidor. Básicamente, la idea consiste en que al iniciar un intercambio de información, una de las partes debe “iniciar” el diálogo (cliente) mientras que la otra debe estar indefinidamente preparada para recibir peticiones de establecimiento de dicho diálogo (servidor). Cada vez que un usuario cliente desee entablar un diálogo, primero deberá contactar con el servidor, mandar una petición y posteriormente esperar la respuesta.

Los servidores pueden clasificarse atendiendo a si están diseñados para admitir múltiples conexiones simultáneas (servidores concurrentes), por oposición a los servidores que admiten una sola conexión por aplicación ejecutada (llamados iterativos). Evidentemente, el diseño de estos últimos será mucho más sencillo que el de los primeros.

Otro concepto importante es la determinación del tipo de interacción entre el cliente y el servidor, que puede ser orientada a conexión o no orientada a conexión. Estas corresponden, respectivamente, con los dos protocolos característicos de la capa de transporte: TCP y UDP. TCP (orientado a conexión) garantiza toda la “seguridad” requerida para que la transmisión esté libre de errores: verifica que todos los datos se reciben, automáticamente retransmite aquellos que no fueron recibidos, garantiza que no hay errores de transmisión y además, numera los datos para garantizar que se reciben en el mismo orden con el que fueron transmitidos. Igualmente, elimina los datos que por algún motivo aparecen repetidos, realiza un control de flujo para evitar que el emisor envíe más rápido de lo que el receptor puede consumir y, finalmente, informa a las aplicaciones (tanto al cliente como al servidor) si los niveles inferiores de red no pueden entablar la conexión. Por el contrario, UDP (no orientado a conexión) no introduce ningún procedimiento que garantice la seguridad de los datos transmitidos, siendo en este caso responsabilidad de las aplicaciones la realización de los procedimientos necesarios para subsanar cualquier tipo de error. En el desarrollo de aplicaciones sobre TCP/IP es imprescindible conocer como éstas pueden intercambiar información con los niveles inferiores, es decir, conocer la interfaz con los protocolos TCP o UDP. Esta interfaz es bastante parecida al procedimiento de entrada/salida ordinario en el sistema operativo UNIX que, como se sabe, está basado en la secuencia abrir-leer/escribir-cerrar. En particular, la interfaz se basa en la definición de un elemento abstracto denominado “socket” concepto muy similar a los descriptores de archivos usados en las operaciones convencionales de entrada-salida en UNIX. Recuérdese que en las operaciones entrada/salida es necesario realizar la apertura del archivo antes de que la aplicación pueda acceder a dicho archivo a través del ente abstracto “descriptor de archivo”. En la interacción de las aplicaciones con los protocolos TCP o UDP, es necesario que éstas obtengan antes el descriptor o “socket”, y a partir de ese momento dichas aplicaciones intercambiarán información con el nivel inferior a través del socket creado. Una vez creados, los sockets pueden ser usados por el servidor para esperar indefinidamente el establecimiento de una conexión (sockets pasivos) o, por el contrario, pueden ser usados por el cliente para iniciar la conexión (socket activos).

(9)

Todos los elementos necesarios para llevar a cabo comunicaciones utilizando los sockets de lenguajes Java se encuentran definidos en el paquete java.net. A continuación revisaremos las clases contenidas en este y los conocimientos de programación necesarios para la comunicación en red.

2.1.1. La clase InetAddress

Cualquier máquina conectada a Internet, lo que se conoce también como host, ha de tener una dirección de 32 bits, denominada dirección IP, que la identifica de forma única, salvo ocasionales conflictos transitorios de direcciones. Por otra parte, dentro de un mismo host puede haber diferentes procesos comunicándose de manera independiente con otros procesos de otras máquinas, o incluso con diferentes threads (hilos) dentro de un mismo proceso en otra máquina. Por ejemplo, es frecuente que una computadora tenga establecidas simultáneamente varias sesiones FTP con distintos servidores, y a la vez éste recibiendo correo mediante SMTP y leyendo news TNP. Lo que se hace para poner de acuerdo a las distintas entidades que se van a comunicar y repartir la información entre ellas es asignar a cada servicio un identificador, también de 32 bits, denominado número de puerto (port number), De esta forma, una conexión queda completamente especificada por los números de puerto que emplean los interlocutores y las direcciones IP de las máquinas en que residen.

Como alternativa a las direcciones IP, existen unos identificadores simbólicos para las máquinas, basados en el servicio de nombre de dominio. De esta forma, podemos emplear nombre como

xamanek.uam.mx o www.iztapalapa.uam.mxen vez de las direcciones IP de estos servidores. La clase InetAddress permite encapsular una dirección IP y su nombre de servidor correspondiente, de un modo que facilita su uso. Esta clase no tiene constructores accesibles para el código de usuario, sino que para instanciarla hay que invocar a unos métodos estáticos especiales, denominados métodos fábrica (factory methods).

Existen tres de estos métodos:

• getLocalHost(): devuelve un objeto InetAddress conteniendo la dirección de la máquina sobre la que se está ejecutando el programa.

getByName(String nombre): devuelve un objeto InetAddress con la dirección de la máquina cuyo nombre se le pasa como parámetro. Si no se encuentra ninguna máquina con ese nombre, se produce una excepción de tipo UnKnowHostException.

• getAllByNAme(String Nombre): devuelve un arreglo de objetos InetAddress conteniendo las direcciones de todas las máquinas que tienen el nombre pasado como parámetro. Si no se encuentra ninguna se produce una excepción de tipo UNKnownHostException.

(10)

Además de los métodos fábrica que sirven para instanciar la clase, existen algunos métodos normales (de instancia):

getHostName(): devuelve un String que contiene el nombre de las máquina a la que corresponde la dirección.

• getAddress(): devuelve un arreglo de 4 bytes con la dirección IP. • topString(): devuelve un String con el nombre y la dirección IP.

2.1.2. Sockets del lado del cliente

El mecanismo de sockets se emplea para establecer una comunicación bidireccional fiable entre dos procesos. Estos procesos pueden residir en la misma máquina o en máquinas diferentes conectadas en red.

La creación de un objeto de tipo Socket también establece la conexión. Se pueden utilizar los siguientes constructores:

Socket(String host, int port): crea un socket que enlaza el host local con el host y el puerto especificados. Puede producir las excepciones UnkownHostException e

IOException.

Socket(InetAddress dirección, int port): análogo al anterior, sólo que en este caso el host se especifica pasando un objeto InetAddress. En caso de haber problemas se producirá una excepción de tipo IOException.

Se pueden conocer en cualquier momento los atributos de un objeto de tipo Socket mediante los siguientes métodos:

• getInetAddress(): devuelve un objeto InetAddress con la dirección del host remoto al que está conectado el socket.

getPort(): devuelve el número de puerto al que está conectado el socket en el host remoto.

• getLocalPort(): devuelve el número del puerto local al que está conectado el socket. Una vez creado un socket, el envío y recepción de datos a través del mismo son exactamente iguales que la escritura y la lectura de los datos de un archivo. Para obtener y cerrar los streams de entrada y salida asociados al socket, se invocan los siguientes métodos:

• getInputStream(): devuelve el objeto de tipo InputStream asociado con el socket. • getOutputStream(): devuelve el objeto de tipo OutputStream asociado con el socket. • close(): cierra tanto el stream de entrada como el de salida.

(11)

Con los streams obtenidos se pueden efectuar las operaciones que ya vimos en la sección anterior. Si se produce algún problema durante las transmisión o la recepción, como puede ser una pérdida de conexión, se generará una excepción IOException.

2.1.3. Sockets del lado del servidor

Si se desea que un programa actúe como servidor, es necesario algún mecanismo que permita esperar a recibir conexiones por tiempo indefinido.

El hilo principal del servidor puede estar permanentemente en un bucle, dentro del cual se queda bloqueado esperando a que una máquina remota establezca una conexión con él. En el momento en que comienza a existir dicha conexión, el servidor queda desbloqueado y crea un hilo hijo que atiende al socket establecido con ese cliente concreto. Una vez hecho esto, el hilo principal vuelve a quedar en espera de la siguiente conexión y el hilo hijo atiende a las peticiones del cliente hasta que se cierra el socket que le enlaza con él, momento en el cual se autodestruye. Esta arquitectura permite conseguir de un modo muy sencillo un reparto de carga equitativo entre los clientes que intentan acceder al servicio.

Para implementar esta funcionalidad se usa la clase ServerSocket, que admite los siguientes constructores:

• ServerSocket(int puerto): crea un ServerSocket sobre el puerto especificado.

ServerSocket(int puerto, int timeout): crea un ServerSocket sobre el puerto especificado. En caso de que ese puerto esté ocupado, espera un tiempo de hasta timeout

milisegundos a que quede libre. En caso de no quedar libre en este tiempo, se produce una excepción.

Ambos constructores genera una excepción IOException en caso de problemas.

Para esperar a que un cliente “llame” y establezca una conexión, se invoca el método accept(). Este método deja bloqueado el thread que lo llama hasta que se produce la conexión. Una vez que ésta queda establecida, la llamada a accept() devuelve un objeto de tipo Socket, que se puede usar para la comunicación de la forma descrita en el apartado anterior.

2.2. Hilos

2.2.1. Programas de flujo único

Un programa de flujo único o mono-hilado utiliza un único flujo de control (thread) para controlar su ejecución. Pues muchos programas no necesitan la potencia o utilidad de múltiples flujos de control.

(12)

public class HolaMundo {

static public void main( String args[] ) { System.out.println( "Hola Mundo!" ); }

}

Aquí, cuando se llama a main(), la aplicación imprime el mensaje y termina. Esto ocurre dentro de un único hilo de ejecución (thread).

Debido a que la mayor parte de los entornos operativos no solían ofrecer un soporte razonable para múltiples hilos de control, los lenguajes de programación tradicionales, tales como C++, no incorporaron mecanismos para describir de manera elegante situaciones de este tipo. La sincronización entre las múltiples partes de un programa se llevaba a cabo mediante un ciclo infinito. Estos entornos son de tipo síncrono, gestionados por sucesos. Entornos tales como el de Macintosh de Apple, Windows de Microsoft y X11/Motif fueron diseñados en torno al modelo de bucle de suceso.

2.2.2 Programas de flujo múltiple

En el ejemplo anterior “Hola mundo”, no se ve el hilo de ejecución que corre el programa. Sin embargo, Java posibilita la creación y control de hilos de ejecución explícitamente. La utilización de hilos (threads) en Java, permite una enorme flexibilidad a los programadores a la hora de plantearse el desarrollo de aplicaciones. La simplicidad para crear, configurar y ejecutar hilos, permite que se puedan implementar aplicaciones portables y robustas, lo cual no es posible con otros lenguajes de tercera generación.

Los navegadores utilizan diferentes hilos ejecutándose en paralelo para realizar varias tareas, "aparentemente" concurrentemente. Por ejemplo, en muchas páginas Web, se puede desplazar la página e ir leyendo el texto antes de que todas las imágenes estén presentes en la pantalla. En este caso, el navegador está obteniendo las imágenes en un hilo de ejecución y soportando el desplazamiento de la página en otro hilo.

Las aplicaciones multihilo utilizan muchos contextos de ejecución para cumplir su trabajo. Hacen uso del hecho de que muchas tareas contienen subtareas distintas e independientes. Se puede utilizar un hilo de ejecución para cada subtarea.

Mientras que los programas de flujo único pueden realizar su tarea ejecutando las subtareas secuencialmente, un programa multihilo permite que cada thread comience y termine tan pronto como sea posible. Este comportamiento presenta una mejor respuesta a la entrada en tiempo real. Vamos a modificar el programa de saludo creando tres hilos de ejecución individuales, que imprimen cada uno de ellos su propio mensaje de saludo, MultiHola.java:

class TestTh extends Thread { private String nombre; private int retardo;

(13)

// Constructor para almacenar nombre y retardo public TestTh( String s,int d ) {

nombre = s; retardo = d; }

// El metodo run() es similar al main(), pero para // threads. Cuando run() termina el thread muere public void run() {

// Retrasamos la ejecución el tiempo especificado try {

sleep( retardo );

} catch( InterruptedException e ) { ;

}

// Ahora imprimimos el nombre

System.out.println( "Hola Mundo! "+nombre+" "+retardo ); }

}

public class MultiHola {

public static void main( String args[] ) { TestTh t1,t2,t3;

// Creamos los threads

t1 = new TestTh( "Thread 1",(int)(Math.random()*2000) ); t2 = new TestTh( "Thread 2",(int)(Math.random()*2000) ); t3 = new TestTh( "Thread 3",(int)(Math.random()*2000) ); // Arrancamos los threads

t1.start(); t2.start(); t3.start(); } } 2.2.3. La clase Thread

Es la clase que encapsula todo el control necesario sobre los hilos de ejecución (threads) y para ello se sirve de los métodos que se exponen en las secciones siguientes.

2.2.3.1 Métodos de Clase

(14)

currentThread()

Este método devuelve el objeto thread que representa al hilo de ejecución que se está ejecutando actualmente.

yield()

Este método hace que el intérprete cambie de contexto entre el hilo actual y el siguiente hilo ejecutable disponible. Es una manera de asegurar que los hilos de menor prioridad no sufran inanición.

sleep( long )

El método sleep() provoca que el intérprete ponga al hilo en curso a dormir durante el número de milisegundos que se indiquen en el parámetro de invocación. Una vez transcurridos esos milisegundos, dicho hilo volverá a estar disponible para su ejecución.

2.2.3.2 Métodos de Instancia

Presentamos solo algunos de los métodos de la clase Thread, los cuales utilizaremos constantemente dada su importancia.

start()

Este método indica al intérprete de Java que cree un contexto del hilo del sistema y comience a ejecutarlo. A continuación, el método run() de este hilo será invocado en el nuevo contexto del hilo. Hay que tener precaución de no llamar al método start() más de una vez sobre un hilo determinado.

run()

El método run() constituye el cuerpo de un hilo en ejecución. Este es el único método del interfaz Runnable. Es llamado por el método start() después de que el hilo apropiado del sistema se haya inicializado. Siempre que el método run() devuelva el control, el hilo actual se detendrá.

stop()

Este método provoca que el hilo se detenga de manera inmediata. A menudo constituye una manera brusca de detener un hilo, especialmente si este método se ejecuta sobre el hilo en curso. En tal caso, la línea inmediatamente posterior a la llamada al método stop() no llega a ejecutarse jamás, pues el contexto del hilo muere antes de que stop() devuelva el control. Una forma más elegante de detener un hilo es utilizar alguna variable que ocasione que el método run() termine de manera ordenada. En realidad, nunca se debería recurrir al uso de este método.

suspend()

El método suspend() toma el hilo y provoca que se detenga su ejecución. Si la ejecución de un hilo se suspende, puede llamarse a resume() sobre el mismo hilo para lograr que vuelva a ejecutarse.

(15)

resume()

El método resume() se utiliza para revivir un hilo suspendido. No hay garantías de que el hilo comience a ejecutarse inmediatamente, ya que puede haber un hilo de mayor prioridad en ejecución actualmente, pero resume() ocasiona que el hilo vuelva a ser un candidato a ser ejecutado.

setPriority( int )

El método setPriority() asigna al hilo la prioridad indicada por el valor pasado como parámetro. Hay bastantes constantes predefinidas para la prioridad, definidas en la clase Thread, tales como MIN_PRIORITY, NORM_PRIORITY y MAX_PRIORITY, que toman los valores 1, 5 y 10, respectivamente.

getPriority()

Este método devuelve la prioridad del hilo de ejecución en curso, comprendido entre uno y diez.

setName( String )

Este método permite identificar al hilo con un nombre mnemónico. De esta manera se facilita la depuración de programas multihilo. Este nombre aparecerá en todas las líneas de trazado que se muestran cada vez que el intérprete Java imprime excepciones no capturadas.

getName()

Este método devuelve el valor actual, de tipo cadena, asignado como nombre al hilo en ejecución mediante setName().

2.2.4. Creación de un Thread

Hay dos modos de conseguir hilos de ejecución (threads) en Java. Una es implementando la interfaz Runnable, la otra es extender la clase Thread.

La implementación de la interfaz Runnable es la forma habitual de crear hilos. Las interfaces proporcionan al programador una forma de agrupar el trabajo, se utilizan para diseñar los requerimientos comunes. La interfaz define el trabajo y la clase, o clases, que implementan la interfaz para realizar ese trabajo

Es importante distinguir las diferencias entre una clase y una interfaz. Primero, una interfaz solamente puede contener métodos abstractos y/o variables estáticas y finales (constantes). Las clases, por otro lado, pueden implementar métodos y contener variables que no sean constantes. Segundo, una interfaz no puede implementar cualquier método. Una clase que implemente una interfaz debe implementar todos los métodos definidos en ese interfaz. Una interfaz tiene la posibilidad de poder extenderse de otras interfaces y, al contrario que las clases, puede extenderse de múltiples interfaces. Además, una interfaz no puede ser instanciada con el operador

new; por ejemplo, la siguiente sentencia no está permitida:

(16)

El primer método de crear un hilo de ejecución es simplemente extender la clase Thread:

class MiThread extends Thread { public void run() {

. . . }

}

El ejemplo anterior crea una nueva clase MiThread que extiende la clase Thread y sobrescribe el método Thread.run() por su propia implementación. El método run() es donde se realizará todo el trabajo de la clase. Extendiendo la clase Thread, se pueden heredar los métodos y variables de la clase padre. En este caso, solamente se puede extender o derivar una vez de la clase padre. Esta limitación de Java puede ser superada a través de la implementación de Runnable:

public class MiThread implements Runnable { Thread t;

public void run() {

// Ejecución del thread una vez creado }

}

En este caso necesitamos crear una instancia de Thread antes de que el sistema pueda ejecutar el proceso como un hilo. Además, el método abstracto run() está definido en la interfaz Runnable y tiene que ser implementado. La única diferencia entre los dos métodos es que este último es mucho más flexible. En el ejemplo anterior, todavía está la oportunidad de extender la clase MiThread, si fuese necesario. La mayoría de las clases creadas que necesiten ejecutarse como un hilo, implementarán el interfaz Runnable, ya que probablemente extenderán alguna de su funcionalidad a otras clases.

No se debe pensar que la interfaz Runnable está haciendo alguna cosa cuando la tarea se está ejecutando, solamente contiene métodos abstractos.

package java.lang;

public interface Runnable { public abstract void run() ; }

Como se ve, una interfaz sólo proporciona un diseño para las clases que vayan a ser implementadas. En el caso de Runnable, obliga a la definición del método run(), por lo tanto, la mayor parte del trabajo se hace en la clase Thread. Las siguientes líneas de código explican esto.

public class Thread implements Runnable { ...

public void run() { if( tarea != null ) tarea.run() ; }

} ...

(17)

De este segmento de código se desprende que la clase Thread también implemente la interfaz Runnable. “tarea.run()” se asegura de que la clase con que trabaja (la clase que va a ejecutarse como un hilo) no sea nula y ejecuta el método run() de esa clase. Cuando esto suceda, el método

run() de la clase hará que corra como un hilo.

A continuación se presenta un ejemplo que implementa la interfaz Runnable para crear un programa multihilo.

class javaMultiHilo {

static public void main( String args[] ) {

Thread hiloA = new Thread( new MiHilo(),"hiloA" ); Thread hiloB = new Thread( new MiHilo(),"hiloB" ); hiloA.start(); hiloB.start(); try { Thread.currentThread().sleep( 1000 ); }catch( InterruptedException e ){} System.out.println( Thread.currentThread() ); hiloA.stop(); hiloB.stop(); } } class NoHaceNada {

// Esta clase existe solamente para que sea heredada por la clase // MiHilo, para evitar que esta clase sea capaz de heredar la clase // Thread, y se pueda implementar el interfaz Runnable en su

// lugar }

class MiHilo extends NoHaceNada implements Runnable { public void run() {

// Presenta en pantalla información sobre este hilo en particular System.out.println( Thread.currentThread() );

} }

Como se puede observar, el programa define una clase MiHilo que extiende a la clase NoHaceNada e implementa el interfaz Runnable. Se redefine el método run() en la clase MiHilo para presentar información sobre el hilo.

La única razón de extender la clase NoHaceNada es proporcionar un ejemplo de situación en que haya que extender alguna otra clase, además de implementar el interfaz.

En el ejemplo javaMultiHilo2.java muestra el mismo programa básicamente, pero en este caso extendiendo la clase Thread, en lugar de implementar el interfaz Runnable para crear el programa multihilo.

(18)

class javaMultiHilo2 {

static public void main( String args[] ) {

Thread hiloA = new Thread( new MiHilo(),"hiloA" ); Thread hiloB = new Thread( new MiHilo(),"hiloB" ); hiloA.start(); hiloB.start(); try { Thread.currentThread().sleep( 1000 ); }catch( InterruptedException e ){} System.out.println( Thread.currentThread() ); hiloA.stop(); hiloB.stop(); } }

class MiHilo extends Thread { public void run() {

// Presenta en pantalla información sobre este hilo en particular System.out.println( Thread.currentThread() );

} }

En ese caso, la nueva clase MiHilo extiende la clase Thread y no implementa el interfaz Runnable directamente (la clase Thread implementa el interfaz Runnable, por lo que indirectamente MiHilo también está implementando ese interfaz). El resto del programa es similar al anterior.

Y todavía se puede presentar un ejemplo más simple, utilizando un constructor de la clase Thread que no necesita parámetros, tal como se presenta en el ejemplo javaMultiHilo3.java. En los ejemplos anteriores, el constructor utilizado para Thread necesitaba dos parámetros, el primero un objeto de cualquier clase que implemente el interfaz Runnable y el segundo una cadena que indica el nombre del hilo (este nombre es independiente del nombre de la variable que referencia al objeto Thread).

class javaMultiHilo3 {

static public void main( String args[] ) { Thread hiloA = new MiHilo();

Thread hiloB = new MiHilo(); hiloA.start(); hiloB.start(); try { Thread.currentThread().sleep( 1000 ); }catch( InterruptedException e ){}

(19)

System.out.println( Thread.currentThread() ); // Se detiene la ejecución de los dos hilos hiloA.stop();

hiloB.stop(); }

}

class MiHilo extends Thread { public void run() {

System.out.println( Thread.currentThread() ); }

}

Las sentencias en este ejemplo para instanciar objetos Thread, son mucho menos complejas, siendo el programa, en esencia, el mismo de los ejemplos anteriores.

2.2.5. Arranque de un Thread

Las aplicaciones ejecutan main() tras arrancar. Esta es la razón de que main() sea el lugar natural para crear y arrancar otros hilos. La línea de código:

t1 = new TestTh( "Thread 1",(int)(Math.random()*2000) );

crea un nuevo hilo de ejecución. Los dos argumentos representan el nombre del hilo y el tiempo que se desea que espere antes de imprimir el mensaje.

Al tener control directo sobre los hilos, hay que arrancarlos explícitamente. En el ejemplo con:

t1.start();

start(), en realidad es un método oculto en el hilo de ejecución que llama a run(). 2.2.6. Manipulación de un Thread

Si todo fue bien en la creación del hilo, t1 debería contener un thread válido, que controlaremos en el método run().

Una vez dentro de run(), se pueden comenzar las sentencias de ejecución como en otros programas. run() sirve como rutina main() para los hilos; cuando run() termina, también lo hace el hilo. Todo lo que se quiera que haga el hilo de ejecución ha de estar dentro de run(), por eso cuando se dice que un método es Runnable, es obligatorio escribir un método run().

En este ejemplo, se intenta inmediatamente esperar durante una cantidad de tiempo aleatoria (pasada a través del constructor):

(20)

El método sleep() simplemente le dice al hilo de ejecución que duerma durante los milisegundos especificados. Se debería utilizar sleep() cuando se pretenda retrasar la ejecución del hilo. sleep()

no consume recursos del sistema mientras el hilo duerme. De esta forma otros hilos pueden seguir funcionando. Una vez hecho el retardo, se imprime el mensaje "Hola Mundo!" con el nombre del hilo y el retardo.

2.2.7. Suspensión de un Thread

Puede resultar útil suspender la ejecución de un hilo sin marcar un límite de tiempo. Si, por ejemplo, está construyendo un applet con un hilo de animación, seguramente se querrá permitir al usuario la opción de detener la animación hasta que quiera continuar. No se trata de terminar la animación, sino desactivarla. Para este tipo de control de los hilos de ejecución se puede utilizar el método suspend().

t1.suspend();

Este método no detiene la ejecución permanentemente. El hilo es suspendido indefinidamente y para volver a activarlo de nuevo se necesita realizar una invocación al método resume():

t1.resume();

2.2.8. Parada de un Thread

El último elemento de control que se necesita sobre los hilos de ejecución es el método stop(). Se utiliza para terminar la ejecución de un hilo:

t1.stop();

Esta llamada no destruye el hilo, sino que detiene su ejecución. La ejecución no se puede reanudar ya con t1.start(). Cuando se liberen las variables que se usan en el hilo, el objeto Thread

(creado con new) quedará marcado para eliminarlo y el garbage collector se encargará de liberar la memoria que utilizaba.

En el ejemplo, no se necesita detener explícitamente el hilo de ejecución. Simplemente se le deja terminar. Los programas más complejos necesitarán un control sobre cada uno de los hilos que lancen, el método stop() puede utilizarse en esas situaciones.

Si se necesita, se puede comprobar si un hilo está vivo o no; considerando vivo un hilo que ha comenzado y no ha sido detenido.

t1.isAlive();

Este método devolverá true en caso de que el hilo t1 esté vivo, es decir, ya se haya llamado a su método run() y no haya sido parado con un stop() ni haya terminado el método run() en su ejecución.

En el ejemplo no hay problemas de realizar una parada incondicional, al estar todos los hilos vivos. Pero si a un hilo de ejecución, que puede no estar vivo, se le invoca su método stop(), se generará una excepción. En este caso, en los que el estado del hilo no puede conocerse de antemano es donde se requiere el uso del método isAlive().

(21)

2.3. APPLETS

2.3.1. Introducción a los applets

Los applets son programas que se incluyen en las páginas Web. Los applets son ejecutados en la máquina cliente, con lo que no existe un exceso de carga del servidor o saturación del ancho de banda. Permiten cargar a través de la red una aplicación portable que se ejecuta en el navegador. Para que esto ocurra tan sólo hace falta que el navegador sea capaz de interpretar Java.

A las páginas que contienen applets se las denomina páginas Java-Powered. Las applets pueden ser visualizadas con la herramienta appletviewer, incluido en el JDK de Java.

Los applets no son exactamente aplicaciones Java, ya que presentan las siguientes diferencias respecto a las aplicaciones normales Java:

• Se cargan mediante un navegador, no siendo lanzados por el intérprete Java.

• Son cargados a través de la red por medio de páginas HTML y no residen en el disco duro de la máquina que los ejecuta.

• Poseen un ciclo de vida diferente; mientras que una aplicación se lanza una vez, un applet

se arranca (inicia) cada vez que el usuario recarga la página en la que se encuentra dicho

applet.

• Tienen menos derechos que una aplicación clásica, por razones de seguridad. De modo predeterminado en el puesto que los ejecuta no pueden ni leer ni escribir archivos, ni lanzar programas, ni cargar DLLs. Sólo pueden comunicarse con el servidor Web en que se encuentra la página Web que las contiene.

2.3.2. Consideraciones sobre la seguridad en los applets

Como ya se ha dicho los applets tienen una serie de restricciones de programación que los hacen "seguros".

Estas restricciones de seguridad son especialmente importantes, ya que evitarán que se cargue por error un applet que destruya datos de la máquina, que obtenga información restringida, o que produzca otros daños inesperados.

Los applets no dejan de ser "ejecutables" que funcionan dentro de una aplicación, como puede ser un visualizador de páginas Web (browser). Este ejecutable puede obtenerse de una red, lo que significa que hay código posiblemente no fiable que se ejecuta dentro de la aplicación.

Java tiene muchas restricciones de seguridad que minimizan el riesgo de la ejecución de applets, pero estas también limitan a los programadores de applets en su capacidad de programación. El modelo de seguridad para los applets en Java lo trata como código no fiable ejecutándose dentro de un entorno fiable. Por ejemplo, cuando un usuario instala una copia de un navegador Web en una máquina cree que su código será funcional en el entorno. Normalmente los usuarios tienen cuidado de qué instalan cuando proviene de una red. Un applet, por el contrario, se carga desde la red sin ninguna comprobación de su fiabilidad.

(22)

Estas restricciones son implementadas para verificar que los códigos de byte de las clases de los applets, no rompen las reglas básicas del lenguaje ni las restricciones de acceso en tiempo de ejecución. Sólo cuando las mismas son satisfechas se le permite a la applet ejecutar su código. Cuando se ejecuta, se le marca para señalar que se encuentra dentro del intérprete. Esta marca permite a las clases de tiempo de ejecución determinar cuándo a una fracción del código se le permite invocar a cierto método. Por ejemplo, un applet está restringido en los hosts en los que se puede abrir una conexión de red o en un conjunto de URLs a las que puede acceder.

En su conjunto estas restricciones constituyen una política de seguridad. En el futuro, Java tendrá políticas más ricas, incluyendo algunas que usen encriptación y autentificación para permitir a los

applets una mayor capacidad.

La actual política de seguridad afecta a los recursos que un applet puede usar, cuyos principales puntos son:

• Los accesos que pueden realizar los applets a los archivos son restringidos. En particular escribir y/o leer en archivos no será una capacidad estándar que se pueda realizar en los navegadores que soporten applets de Java.

• Las conexiones de red serán restringidas a conectar solo con el host del que proviene el

applet.

• Un applet no es capaz de usar ningún método que pueda resultar en una ejecución arbitraria, código no revisado o ambos. Esto incluye métodos que ejecuten programas arbitrarios (métodos nativos) así como la carga de bibliotecas dinámicas.

Se anticipa en cualquier caso que en el futuro los modelos de seguridad permitirán a las applets

(23)

2.3.3. LA CLASE APPLET

2.3.3.1 Situación de la clase Applet en la API de Java

La clase Applet Java, de la cual han de heredar todos los programas Java que vayan a actuar como applets, es la única clase que contiene el paquete java.applet de la API de Java.

Esta clase hereda de Object (como todas las clases Java), pero además hereda de Component y

Container, que son dos clases del paquete gráfico AWT. Esto ya perfila las posibilidades gráficas de este tipo de aplicaciones Java.

2.3.3.2 Métodos del ciclo de vida

Como ya se ha indicado un applet no tiene un ciclo de vida tan "sencillo" como el de una aplicación, que simplemente se ejecuta hasta que finaliza su método main().

La siguiente figura representa el ciclo de vida de un applet:

Fig 3.1 Ciclo de vida de un applet

Cada círculo representa una fase en el ciclo de vida del applet. Las flechas representan transiciones y el texto representa la acción que causa la transición. Cada fase está marcada con una invocación a un método del applet:

void init(); Es invocado cuando se carga el applet. Aquí se suelen introducir las iniciaciones que el applet necesite.

void start(); Es invocado cuando el applet, después de haber sido cargado, ha sido parado (cambio de página Web, minimización del navegador,...), y de nuevo activado (vuelta a la página, restauración del navegador,...). Se informa al applet que tiene que empezar su funcionamiento.

void stop(); Es invocado para informar al applet que debe detener su ejecución. Así un

applet que utilice threads, debería detenerlos en el código de este método.

void destroy(); Es invocado para informar al applet que su espacio está siendo solicitado por el sistema, es decir el usuario abandona el navegador. El applet debe aprovechar este momento para liberar o destruir los recursos que está utilizando.

(24)

Al crear un applet no es necesario implementar todos estos métodos.

Cuando un navegador carga una página Web que contiene un applet, suele mostrar en su parte inferior un mensaje como:

initializing... starting...

Esto indica que el applet, se está cargando: 1. Una instancia de la clase applet es creada. 2. El applet es iniciado, mediante su método init().

3. El applet empieza a ejecutarse, mediante su método start().

Cuando el usuario se encuentra con una página Web, que contiene un applet y salta a otra página, entonces el applet se detiene invocando a su método stop(). Si el usuario retorna a la página donde reside el applet, éste vuelve a ejecutarse nuevamente invocando a su método start().

Cuando el usuario sale del navegador el applet tiene un tiempo para finalizar su ejecución y hacer una limpieza final, mediante el método destroy().

2.3.3.3 La clase URL

Una URL (Uniform Resource Locator) es una dirección de Internet. Cada recurso (archivo, página Web, imagen...) tiene uno propio. En Java existe una clase denominada URL que modela esta clase de objetos.

La clase URL pertenece al paquete java.net, y tiene una cierta importancia en el desarrollo de los

applets, puesto que muchos de los métodos de la clase Applet la utilizan para acceder a determinado recurso de Internet o para identificarse.

Podemos especificar un URL de manera absoluta:

URL URLabsoluto = new URL("http://www.host.com/dir/fich.htm");

O bien podemos especificar un URL de manera relativa:

URL URLhost = new URL("http://www.Javasoft.com/"); URL URLrelativo = new URL( URLhost, "dir/fich.htm");

(25)

2.3.3.4 Inclusión de un applet en una página Web

Para incluir un applet en una página Web, una vez compilado el applet, debe incluirse entre el código HTML de la página Web una etiqueta <APPLET>, e indispensablemente los siguientes tres parámetros:

code: Especifica el URL del archivo de clase Java (*.class) que contiene el applet. width: Especifica la anchura inicial del applet (en pixels).

heigth: Especifica la altura inicial del applet (en pixels).

Además, de la etiqueta inicial, un applet puede tener parámetros que se especificarán mediante etiquetas <PARAM>, y básicamente contener dos parámetros:

name: Indica el nombre del parámetro del applet al que esta etiqueta hace referencia. value: Establece este valor al parámetro indicado en name de la misma etiqueta. Así un ejemplo de esto sería:

<applet code="AppletDiagonal.class" width=200 height=200> <param name=Parametro1 value=Valor1>

<param name=Parametro2 value=Valor2> </applet>

En este ejemplo el applet puede entender los parámetros Parametro1 y Parametro2, mediante los métodos que se describen posteriormente, y se obtendría Valor1 y Valor2 respectivamente.

Se observa que además de la etiqueta <applet> en el código HTML también aparece una etiqueta

</applet>. Esto sucede porque HTML es un lenguaje, en el que casi todas las etiquetas de inicio de elemento (<etiq>) tienen una etiqueta de fin (</etiq>).

2.3.3.5 Obtención de los parámetros de un applet

Cuando se incluye un applet en una página Web se utiliza la etiqueta HTML <applet>. Las etiquetas HTML permiten utilizar parámetros, y la etiqueta <applet> hace lo propio, permitiendo al

applet recibir parámetros de ejecución, tal y como una aplicación los recibe en el parámetro s (un vector de cadenas) de su método main(String[] s).

Los siguientes métodos se utilizan para extraer información de los parámetros que recibió el applet

cuando fue llamado mediante código HTML:

URL getDocumentBase(); Devuelve el URL del documento que contiene el applet.

URL getCodeBase(); Devuelve el URL del applet.

String getParameter(String name); Devuelve el valor de un parámetro (etiquetas <param>) que aparece en el documento HTML.

(26)

<applet code="AppletParam.class" width=50 height=50> <param name=Color value="red">

</applet>

Una llamada en este applet al método getParameter("Color") devolverá "red". 2.3.3.6 Obtención de información sobre un applet

Algunos métodos se utilizan para comunicar información o mostrar mensajes en la pantalla referentes al applet:

boolean isActive(); Comprueba si el applet está activo.

void showStatus(String status); Muestra una cadena del estado en la pantalla.

String getAppletInfo(); Devuelve información relativa al applet como el autor, Copyright o versión.

String[ ][ ] getParameterInfo(); Devuelve un vector que describe algún parámetro específico del applet. Cada elemento en el vector es un vector de tres cadenas que tienen la forma: {nombre, tipo, comentario}.

Un ejemplo de como definir este método para un applet que permita un solo parámetro, color, sería:

public String[][] getParameterInfo() {

String info[][] = { {"Color","String","foreground color"} }; return info;

}

2.3.3.7 Manipulación del entorno de un applet

Algunos applets pueden afectar al entorno en que están ejecutándose. Para ello se utilizan los métodos:

AppletContext getAppletContext(); Devuelve un AppletContext, que permite al applet

afectar a su entorno de ejecución.

void resize( int ancho, int largo); Solicita que se modifique el tamaño del applet. También permite recibir un único parámetro Dimension.

Locale getLocale(); Devuelve el valor Locale del applet si este fue establecido.

void setStub( AppletStub s ); Establece el stub de este applet. 2.3.3.8 Soporte multimedia

La clase Applet también incluye métodos para trabajar con imágenes y archivos de sonido de Internet mediante la utilización de URLs. Para ello implementa los métodos:

Image getImage(URL u, String s); Obtiene una imagen de un URL u que será absoluto si no se especifica una ruta relativa s.

AudioClip getAudioClip(URL u, String s); Obtiene un clip de sonido de un URL u que será absoluto si no se especifica una ruta relativa s.

(27)

void play(URL ur1, String name); Ejecuta directamente un archivo de sonido de un URL u

que será absoluto si no se especifica una ruta relativa s.

static audioClip newAudioClip(URL u); Obtiene un nuevo archivo de sonido del URL u. Mediante el uso adecuado de varios de estos métodos se pueden combinar sonidos e imágenes para conseguir efectos espectaculares.

2.4. EJEMPLO DE CONSTRUCCIÓN DE UN APPLET 2.4.1 Código

Para crear un applet normalmente será necesario importar al menos las bibliotecas java.awt.* y

java.applet.*.

La clase que represente al applet se debe declarar como una subclase de la clase Applet, para poder sobrescribir los métodos de la clase Applet.

Siempre conviene sobrescribir al menos el método paint() que será llamado por los navegadores que soporten applets para mostrarles por pantalla.

Vamos a construir un applet denominada AppletDiagonal que simplemente dibuje una línea diagonal. Un posible código sería:

import java.awt.*; import java.applet.*;

public class AppletDiagonal extends Applet { public void paint(Graphics g) {

g.setColor( Color.red );

g.drawLine(0, 0, getWidth(), getHeight() ); }

}

A continuación se describe el funcionamiento de este código:

1. El método paint() recibe un objeto de la clase Graphics. La clase Graphics, incluida en el paquete AWT, contiene métodos para mostrar varios tipos de gráficos.

2. Mediante el método setColor() de la clase Graphics se establece el color de primer plano a rojo, que es uno de los colores predefinidos de la clase Color.

3. Por último, mediante drawLine() se dibuja una línea dadas las coordenadas de su esquina superior izquierda y de la inferior derecha. En este caso se indican la esquina superior izquierda del applet mediante las coordenadas (0,0), y la esquina inferior derecha se obtiene mediante dos métodos de la clase Dimension (getWidth(), getHeight() ).

2.4.2 Ejecución

Para ejecutar el applet, una vez compilado el archivo, se introduce la llamada al applet en una página Web (por ejemplo AppletDiagonal.htm), introduciendo entre su código HTML lo siguiente:

(28)

<applet code="AppletDiagonal.class" width=200 height=200> </applet>

Cuando se cargue esta página Web en un navegador compatible con Java o mediante el visualizador de applets (appletviewer) se verá algo como:

Fig. 4.1: Applet "Línea"

Se podría dibujar un rectángulo con cambiar la línea de código de drawLine() por otra que llamase al método drawRect():

g.drawRect(10, 10, r.width –20, r.height –20);

2.4.3 Creación de una aplicación que utilice un applet (AWT)

A continuación usaremos AWT para crear una aplicación que de un resultado igual que la ejecución del "applet Línea". Será una aplicación que creará un Frame de AWT para incluir el

applet que ya fue creado.

El main() de la aplicación sólo creará un objeto de este tipo (indicándole altura y anchura, como hacíamos en el applet mediante los parámetros de la etiqueta HTML).

El código fuente de la aplicación sería el siguiente:

import java.awt.*;

import java.awt.event.*;

class FrameLinea extends Frame {

private AppletDiagonal unApplet; // Se mostrará public static void main( String[] s ) {

new FrameLinea( 200, 230 ); }

(29)

public FrameLinea( int ancho, int largo ) { super(); // Constructor de Component

// Se añade un oyente que cerrara la aplicación addWindowListener( new OyenteLinea() );

// Se crea una applet de diagonal unApplet=new AppletDiagonal(); unApplet.init();

unApplet.start();

// Se mete la applet en frame add( unApplet );

setSize(ancho,largo); // ajusta frame setVisible(true); // muestra frame }

// Clase anidada

class OyenteLinea extends WindowAdapter {

// Sobreescribo el método de "cuando se cierra ventana" public void windowClosing(WindowEvent e) {

unApplet.stop(); unApplet.destroy(); System.exit(0); } } }

Se crea un Frame en el que se incluye el applet unApplet que será de la clase AppletDiagonal, creado anteriormente.

Lo que hace la aplicación es crear un oyente de la clase OyenteLinea, que será el encargado de capturar el evento de cerrar la ventana del Frame.

En el constructor se inicia el applet (init() y start()) y se añade al Frame mediante el método add()

de la clase Container (Frame es hija de Container).

Por último se establece el tamaño del Frame (recibido por parámetro) mediante setSize() y por último se muestra el Frame que ya tiene en su interior el applet (setVisible()).

Cuando se cierra la ventana, el OyenteLinea se encarga de cerrar el applet, mediante stop() y

destroy(), y de finalizar la aplicación mediante System.exit().

2.4.4 Creación de una aplicación que utilice el applet (Swing)

Esta misma aplicación se puede crear utilizando Swing con solo cambiar las siguientes cosas: 1. Se debe incluir la biblioteca de Swing:

import javax.swing.*;

(30)

3. Se crea un contentPane mediante un objeto JPanel, justo antes de llamar al oyente:

setContentPane( new JPanel() );

4. Para añadir el applet se debe agregar al contentPane:

getContentPane().add( unaApplet );

2.5. Programación del Puerto Paralelo

El puerto paralelo se apega al estándar IEEE 1284 liberado en 1994 y que define 4 modos de operación soportados aún en la actualidad:

1. Puerto Paralelo Estándar (SPP) 2. Puerto Paralelo PS/2 (Bidireccional) 3. Puerto Paralelo Mejorado (EPP)

4. Puerto Paralelo con Capacidades Extendidas (ECP).

La mayoría de las computadoras personales recientes, tanto de escritorio como portátiles, presentan por omisión una configuración del puerto paralelo en dos direcciones de datos (bidireccional) para cualquier sistema operativo. Los sistemas operativos menos recientes, hablando de Windows 98 y anteriores, también son capaces de soportar este tipo de esquema para recibir y enviar datos por el puerto de impresión, siempre y cuando se configure manualmente dicha característica, preferentemente desde el SETUP.

En esta sección se discutirá el modo de operación bidireccional. Para el análisis mostrado se consideran dos vertientes: la programación del puerto bajo el modo MS –DOS (Microsoft Disk Operating System – Sistema Operativo en Disco), y como segunda derivación, la programación en Modo Windows. En ambos casos se revisan interfaces unidireccionales y bidireccionales.

2.5.1 Programación del Puerto Paralelo en Modo MS - DOS.

El modo MS- DOS es válido en Windows 98 y versiones anteriores (Windows 95, Windows 3.1, etc.). En esta condición es posible escribir directamente a los registros del puerto.

2.5.1.1 Puerto Paralelo Unidireccional.

Considerando el modo de una sola dirección, comúnmente llamado Puerto Paralelo Estándar (SPP), existen tres direcciones consecutivas asociadas con un puerto paralelo; estas direcciones pertenecen al registro de datos (Data Register), el registro de estado (Status Register) y el registro de control (Control Register). Se le denomina dirección base a la que indica la propia del registro de datos, por lo general 0x378; así se tendría para el registro de estado la dirección inmediata siguiente 0x379 y para el registro de control la dirección 0x37A Existen alternativas diferentes para encontrar la dirección de los puertos, debido a que puede cambiar dependiendo de la arquitectura y organización interna de la PC. Es posible acceder directamente al panel de control de Windows y verificar el sistema; dentro de los recursos hardware se encuentra el administrador de dispositivos. El puerto paralelo se utiliza para la conexión de impresoras, por lo que aparecen etiquetados como LPT1, LPT2, LPT3 ó LPT4, según las características de la PC.

(31)

Es importante recordar que en el modo estándar, el puerto de datos sólo es de salida, de ahí que se le conozca como unidireccional, y es de 8 bits. El puerto de estado es de sólo entrada con 5 bits referidos en el conector y el propio de control tiene 4 bits de sólo salida. En resumen, tenemos que en las portátiles, se tiende a encaminar todos los periféricos hacia USB, eliminando en algunos casos el puerto paralelo y el puerto de juegos; así como la unidad de disco flexible, obligando a que ésta sea externa, o bien, utilizar la alternativa del Disco Compacto de este modo se tienen 12 líneas de salida (de las cuales, 3 son de tipo activa bajo) y sólo 5 de entrada (con una sola línea de tipo activo bajo), tal y como se aprecia en la figura 1.a.

El puerto paralelo utiliza un conector hembra clase D de 25 pines (DB-25), definido como TIPO A

por el estándar IEEE 1284

En las figuras 1.a y 1.b, se aprecia la distribución física de los pines en el conector DB-25. Para fines de análisis, se considera que los tres registros del puerto son de 8 bits, por lo que se tiene un orden significativo que es necesario respetar cuando se forma una palabra de configuración, por ejemplo, en el caso del registro de estado, se tiene disponible a partir del bit 4 y hasta el bit 8 (S7, S6, S5, S4, S3), los demás están comprometidos o reservados para otros propósitos. De acuerdo al diagrama interno aproximado mostrado en la figura 1.b, el bit más significativo del registro de estado (S7), trabaja con lógica negativa y está físicamente ubicado en el pin 11 del conector. Si se requiere leer una palabra de entrada a través de este registro, es importante considerar con qué lógica funciona cada pin.

El registro de control, para fines prácticos en el diseño de interfaces en modo estándar, es sólo de salida y utiliza los primeros cuatro bits (C3, C2, C1, C0) del registro, los restantes cuatro están reservados. En este registro, los bits C3, C1 y C0, trabajan con lógica invertida y están localizados físicamente en los pines 17, 14 y 1 del conector.

Como ya se comentó con anterioridad, los ocho bits del registro de datos se utilizan sólo como salidas y todos trabajan con lógica positiva, ubicándose en orden significativo del pin 2 al pin 9 del conector, tal y como se aprecia en la figura 1.a.

El siguiente programa escrito en Lenguaje C, muestra de manera sencilla cómo se envían y reciben datos. El circuito secuenciador de la figura 2 se activa con el botón externo conectado al bit 4 del registro de estado (S3), ubicado físicamente en el pin 15 del conector.

(32)

/*Se incluyen las bibliotecas generales, No se utilizan todas en este ejemplo*/ #include <stdio.h> #include <stdlib.h> #include <conio.h> #include <dos.h> void main(void) { int entrada;

/*Datos de la secuencia, en decimal*/ int leds[9] = {0,1,2,4,8,16,32,64,128}; int i;

while(1) {

entrada = inportb(0x379);

/*Si el bit S3 está en 0, el botón está presionado*/ if (((entrada)&0x08)==0)

{

for(i = 0; i < 9; i++) {

/*Recorre uno a uno los bits de izquierda a derecha*/ outport(0x378,leds[i]);

sleep(1); }

} else

outportb(0x378,0x00); /*Si el botón no es presionado, los LEDs se apagan*/

}

}

Las conexiones básicas requieren los pines 2, 3, 4, 5, 6, 7, 8 y 9 del conector como D0, D1, D2, D3, D4, D5, D6 Y D7 del registro de datos, por lo que se conectarán leds tal y como lo indica el diagrama siguiente (figura 2). Para la conexión del botón de activación se utilizará el pin 15 del conector. Se recomienda utilizar una fuente externa de 5 volts y acoplar las tierras.

(33)

Como ya se indicó con anterioridad, este programa sólo funciona en modo MS- DOS. Para que funcione también sobre sistemas operativos superiores a Windows 98, es necesaria una biblioteca de enlace dinámico (dll) que declare nuevas funciones para acceder al puerto paralelo, esta cuestión se detallará más tarde en este mismo documento.

2.5.1.2 Puerto Paralelo Bidireccional.

En algunas aplicaciones prácticas se requieren más líneas de entrada que las disponibles en el puerto de estado; por ejemplo, leer los 8 bits de un convertidor analógico – digital paralelo o interactuar con una pantalla de LCD. Dado que las entradas del puerto de estado están restringidas a sólo 5 bits, es necesario adecuar el programa escrito hacia una lógica de multiplexaje que lea un dato de 4 bits, se almacene en una localidad de memoria y después de un tiempo se lea la otra parte del dato, pensando en una entrada de 8 bits ó más. Es admisible configurar el puerto de datos para que sus ocho pines puedan ser también entradas. Esto se logra accediendo al puerto de control y cambiando el bit número 6 del registro de un estado natural bajo a un estado alto. Cuando C5 está a 0 lógico, las 8 líneas del puerto de datos son salidas y cuando C5 está a 1 lógico, se comportan como entradas. La figura 3, muestra la disposición física de los pines del registro del puerto de control.

Considerando una dirección base 378H para el puerto de datos, se lista el siguiente fragmento en Lenguaje C para explicar de manera más concreta la idea anterior.

{

unsigned int Valor, temp; outportb(0x37A, 0x20); Valor=inport(0x378);

printf ("Valor Leído: %u \n", Valor); getch();

}

Obsérvese que en la primera instrucción outportb(0x37A, 0x20) se escribe al puerto de control con la dirección 0x37A un valor hexadecimal 0x20, traducido a binario de 8 bits como 00100000, especificando que el bit número 6 se establece a un nivel lógico alto por lo que el puerto de datos (0x378) está configurado como entrada. En la siguiente instrucción Valor=inport(0x378) se leen las 8 líneas de datos y se asignan a una variable sin signo previamente definida. Como ejemplo práctico, se considera un ADC0804 supervisado a través del puerto paralelo en modo bidireccional, como lo expone el diagrama de la figura 4. En este caso no se expone a detalle el funcionamiento del convertidor por lo que se recomienda consultar la hoja de especificaciones del dispositivo. Para este diseño en particular, la entrada analógica a convertir proviene directamente de una resistencia variable; sin embargo, ésta se puede sustituir (con las adecuaciones necesarias) por algún sensor, por ejemplo, un dispositivo LM35 para concebir un termómetro digital.

Referencias

Documento similar

Fuente de emisión secundaria que afecta a la estación: Combustión en sector residencial y comercial Distancia a la primera vía de tráfico: 3 metros (15 m de ancho)..

La campaña ha consistido en la revisión del etiquetado e instrucciones de uso de todos los ter- mómetros digitales comunicados, así como de la documentación técnica adicional de

Debido al riesgo de producir malformaciones congénitas graves, en la Unión Europea se han establecido una serie de requisitos para su prescripción y dispensación con un Plan

Como medida de precaución, puesto que talidomida se encuentra en el semen, todos los pacientes varones deben usar preservativos durante el tratamiento, durante la interrupción

Cedulario se inicia a mediados del siglo XVIL, por sus propias cédulas puede advertirse que no estaba totalmente conquistada la Nueva Gali- cia, ya que a fines del siglo xvn y en

Abstract: This paper reviews the dialogue and controversies between the paratexts of a corpus of collections of short novels –and romances– publi- shed from 1624 to 1637:

por unidad de tiempo (throughput) en estado estacionario de las transiciones.. de una red de Petri

Por lo tanto, en base a su perfil de eficacia y seguridad, ofatumumab debe considerarse una alternativa de tratamiento para pacientes con EMRR o EMSP con enfermedad activa