Trabajo Práctico

Técnicas de Programación Concurrente

WeatherMulator

Autores:

Leandro Lucarella 77891

Nicolás Dimov 77624

Ricardo Markiewicz 78226

Introducción:

El sistema simula el monitoreo de un centro que mide algunas variables climatológicas como ser, temperatura, humedad, presión, velocidad y dirección del viento, presentandolas en una interfaz gráfica para comodidad del usuario.

Objetivo:

Para realizar esta tarea, se simularon los sensores siguiendo el diseño propuesto y utilizando las técnicas de programación concurrente adecuadas para solucionar el tipo de problemas que se presentan.

Desarrollo e Implementación:

Se ha utilizado la siguiente estructura de clases:

Cabe aclarar que este es un diagrama híbrido, con la jerarquía de clases y los mensajes que se pasan los sensores con la interfaz gráfica (entrada y salida de usuario). No se modeló el teclado especial ni el display especial porque no lo creímos importante para los objetivos del trabajo. Hay tanto mensajes sincrónicos (obtener) como asincrónicos (actualizar) que serán explicados posteriormente.

Se han implementado los sensores correspondientes para la implementación del sistema y cada uno de ellos funciona como un hilo de ejecución independiente (pero no son un proceso, son hilos) el cual genera sus propios valores aleatorios simulando una medición real a través del método getValor() que simula el valor obtenido del dispositivo sensor real. Para obtener los valores aleatorios de la forma más realista posible se utilizó una distribución de gauss para que la probabilidad de que el valor cambie mucho sea pequeña (para esto se utilizan las propiedades protegidas sigma y mu; sigma es el centro y mu la desviación estándar; cada sensor utiliza el par de valores que creímos más realista para el caso).

El sistema entonces tiene cinco tipos de sensores que miden las distintas magnitudes climatológicas:

Temperatura

Humedad

Velocidad del Viento

Dirección del Viento

Presión

Cada uno de estos sensores tiene la capacidad de almacenar una serie de datos estadísticos permitiendo hallar los máximos y mínimos en un período de tiempo (excepto el sensor de dirección del viento, cuyos requerimientos no incluían esta capacidad). Los sensores de presión y temperatura tienen la capacidad de obtener un valor denominado tendenciam, una magnitud de punto flotante entre -1 y 1 que indica si es un período determinado de tiempo los valores tienden a aumentar (1) o disminuir (-1).

Como cada sensor aislado no sirve de mucho, por el contrario, el usuario (a través de la GUI) debe monitorizarlos e interactuar con ellos. Para Para cumplir esta tarea, en cada sensor deben realizarse escrituras y lecturas de estos valores concurrentemente, para lo cual utilizamos exclusión mutua (a través de las propiedades sincronizadas, SyncProp, o los métodos sincronizados, @syncrhronized; ambos envolviendo la propiedad o el método con un mutex).

Para obtener este efecto, los métodos actualizarHistorial, getMin y getMax de la clase SensorHistorico (como todos los otros métodos marcados como <<synchronized>> en el diagrama de clases), son modificados por el decorador @syncronized, cuya función es indicarle a la aplicación que esa porcion de código solo puede ser accedida por un solo hilo de ejecución a la vez, con lo cual, se garantiza la exclusión mutua para ese método.

De forma similar se comportan los atributos valorMinimo y valorMaximo de la clase SensorHistorico (al igual que el resto de los atributos SyncProp en el diagrama de clases), sólo que en este caso son “propiedades”, es decir, sintácticamente se ven como variables comunes pero por “detrás” utilizan un método (eventualmente sincronizado, como los mencionados anteriormente) en cada operación de lectura o escritura (si está permitido, hay propiedades de sólo lectura como la tendencia o el valorMaximo y valorMinimo, ya que son obtenidos de la colección de valores historicos “historial”). De esta manera también se garantiza la exclusión mutua aún utilizando lo que a simple vista parece un atributo común y corriente (y la integridad de los datos al no permitir escribir ciertas propiedades).

Responsabilidades:

Thread:

Esta clase es la encagada del manejo de hilos de ejecución.

Clase Sensor:

Al inicarse la aplicación se crean una instancia de cada tipo de sensor. Como se explico anteriormente, todos funcionan de manera similar, al ejecutarse el constructor de la clase base Sensor, se inicializan los valores para la simulación y se lanza un hilo (thread) de ejecución que pone en marcha la misma. Además el sensor tiene un método stop() para sugerirle al sensor que deje de funcionar para cerrar la aplicación correctamente.

Clase DireccionViento:

Utilizando un algoritmo aleatorio devuelve una dirección en 8 posibles para la medición de la dirección del viento. Esto se realiza utilizando la misma distribución de gauss, sólo que el rando de valores es limitado entre 0 y 2π. Además tiene funciones y constantes para referenciar estas 8 posibles direcciónes de forma más simple.

Clase SensorHistorico:

Se encarga de “muestrear”, cada intervalos de tiempo pequeños, los valores tomados por el sensor acompañando cada registro con la hora en que fue adquirido.

Esta tarea debe ser sincrónica, es decir, no se debe permitir escribir los valores mientras se estan leyendo o viceversa. Por el mismo motivo los atributos valorMinimo y valorMaximo tienen la característica de ser “propiedades”, es decir, utilizan un método particular cuando se les asigna un valor o se las consulta. Estos métodos implementan “candados” sobre las variables para garantizar exclusión mutua, esto es, cuando se solicita escritura, se verifica que el candado esté abierto, si lo está se cierra, se escribe y se libera. Lo mismo sucede para la lectura de la variable.

Clase SensorTendencia:

Esta clase realiza una interpolación lineal entre los últimos N (parametrizable) valores del historial para indicar una tendencia en el próximo valor que devolverá el sensor.

Esta clase contiene un método getTendencia() (que es utilizado cuando se accede a la propiedad tendencia) el cual es también modificado por el decorador @synchronized pues los valores de la tendencia necesitan exclusión mutua ya que acceden a la colección de valores históricos que no puede ser modificado mientras se lo recorre para calcular la tendencia porque se perdería la integridad de los datos.

Clase SensorTemperatura:

Es la clase encargada de simular el sensor de temperatura, obteniendo valores aleatorios según una distribución gaussiana con media y desvío estándar parametrizable.

Clase SensorHumedad:

Al igual que el sensor de temperatura, el sensor de humedad utiliza esta clase para simular los valores.

Clase SensorVelocidadViento:

La clase simula valores de velocidad del viento de la misma forma que los otros sensores.

Clase SensorPresion:

El funcionamiento es análogo a los sensores anteriormente citados.

Interfaz Gráfica:

Los datos se presentan sobre una interfaz gráfica donde se presentan sobre la izquierda de la ventana los valores de las magnitudes variando en tiempo real según los cambios en los sensores. Por simplicidad y claridad de la interfaz se omitieron las unidades de medición, ya que no lo consideramos importante para el objetivo del TP. De todas formas cabe destacar que los valores iniciales de sigma y mu fueron pensados para las siguiente unidades:

Temperatura en grados centígrados

Presión en hectopascales

Humedad en porcentaje

Viento en kilómetros por hora

A la derecha se pueden ver una serie de botones que permiten realizar las consultas sobre los máximos y mínimos y tendencia de cada magnitud (que lo soporte) en tiempo real. Estos botones tienen un “tooltip” (que aparece dejando el mouse sobre el botón) que indica para qué sirve cada uno.

También posee un botón que permite calibrar los sensores, configurando manualmente los valores brutos y reales de la variable seleccionada. De esta manera se obtiene la recta de cambio de escala para los valores tal como se pide en los requerimientos del sistema.

La comunicación entre la itnerfaz gráfica y los sensores, como se adelanto previamente, se realiza tanto de forma sincrónica como asincrónica. Esto se debe a que el tookit gráfico utilizado (GTK) no tiene soporte para concurrencia, por lo tanto no se puede llamar a ningún método de la interfaz gráfica desde un hilo que no sea el hilo que maneja la interfaz. Es por esto que todos los mensajes que la interfaz envía a los sensores son sincrónicos (los sensores sí soportan concurrencia), pero los mensajes que recibe la interfaz de los sensores son asincrónicos. Los primeros (sincrónicos) son métodos para obtener los valores máximo, mínimo, tendencia y para calibrar el sensor (protegidos por exclusión mutua), todos mensajes que resultan de la interacción del usuario. Por el contrario, los segundos, producidos por la interacción de los propios sensores y son aquellos que reportan que su estado interno cambió. En realidad esto último se realiza utilizando un sólo mensaje: actualizar. Este mensaje es enviado a través de un dispatcher, que básicamente es una cola de mensajes asincrónicos, cuya notificación es enviada a través de un pipe del sistema operativo (evento que GTK puede monitorear de una forma segura). La cola de mensajes también está protegida por exclusión mutua. Entonces cada sensor tiene asociado una callback (una función, método o cualquier otro objeto “callable”; como el dispatcher que utlilizamos nosotros) que es utilizado para reportar cambios en el estado interno.

La interfaz gráfica proporciona un solo “handler” para esta “callback”, el método actualizar. Este método es el que se encarga luego obtener el nuevo estado interno del sensor y de, eventualmente, pasarselo a otra ventana (como la de máximo, mínimo, tendencia o de calibración).

Implementación:

El sistema fue desarrollado en python versión 2.4 sobre plataforma GNU/Linux con kernel 2.6.X.

La interfaz gráfica fue implementada con glade2 y pygtk. Sin embargo debería funcionar en cualquier sistema Unix (o POSIX compatible) que tenga instalado Python 2.4 y pygtk.

Modo de uso:

Para ejecutar el emulador solo basta con escribir la siguiente línea de comandos:

$>python2.4 weathemulator.py

Bibliografía:

Análisis y diseño orientado a objetos - Cap 9. - Ben Ari

2005-10-17T22:55:48