2. descripción general del sistema. el problema de la multilínea

Transcripción

2. descripción general del sistema. el problema de la multilínea
Capítulo 2: Descripción general del sistema. El problema de la multilínea
2. DESCRIPCIÓN GENERAL DEL
SISTEMA. EL PROBLEMA DE LA
MULTILÍNEA
2.1 INTRODUCCIÓN
En este capítulo veremos la arquitectura hardware del sistema, las dificultades
que surgieron cuando se planteó el desarrollo de una versión multilínea del sistema
telefónico ya existente y las soluciones adoptadas.
2.2 ARQUITECTURA HARDWARE
Cuando hay que plantearse la arquitectura hardware de un sistema basado en las
tecnologías del habla hay que tener en cuenta varios aspectos importantes: la
modularidad del sistema, el coste del sistema y el uso que vaya a tener. Hay dos
posibilidades de tipo genérico:
•
Solución interfaz: consiste en equipar un terminal telefónico normal con una
serie de interfaces, estándares si es posible, que permitan conectar al terminal
dispositivos externos. Esta solución tiene unos costes bastante bajos y esta
orientada a sistemas instalables en casa de los usuarios, los cuales suelen
tener alguna discapacidad. Las interfaces del terminal deben permitir la
conexión, por ejemplo, de un dispositivo de marcación controlado por voz,
un sistema de salida audio, pantallas tipo Braille, micrófonos y altavoces
especiales, etc.
•
Solución PC: utiliza un ordenador, tipo PC, complementado con las tarjetas
que sea necesario, normalmente una interfaz de línea telefónica y una o
varias tarjetas que soporten el reconocimiento y la síntesis de voz. Esta
solución es la más adecuada para aquellos sistemas que van a ser utilizados
por los usuarios a través de la línea telefónica.
Pág. 2-1
Capítulo 2: Descripción general del sistema. El problema de la multilínea
En nuestro caso se ha optado por la segunda alternativa. Sobre el PC recaen las
tareas de control del sistema y las tarjetas que hay que añadir son las siguientes:
•
Tarjeta interfaz de línea telefónica (IFTEL): esta tarjeta conecta el sistema
a la línea telefónica y se encarga de adaptar las señales entre la línea
telefónica y la tarjeta de procesado digital de señal. También permite
conectar dispositivos auxiliares al sistema, como auriculares, equipos de
grabación de voz, etc.
•
Tarjeta de procesado digital de señal (VISHA): está equipada con un
procesador de señal (DSP), un CODEC y memoria RAM. Realiza tareas de
reconocimiento, reproducción y grabación de voz.
2.2.1
Interfaz de línea telefónica
La principal función que realiza la interfaz de línea es realizar la conexión del
sistema con la línea telefónica, realizando la conversión de 2 a 4 hilos y adaptando los
niveles de señal a ambos lados de la interfaz.
Además, y debido a las necesidades del sistema, realiza otras muchas funciones,
entre las que se encuentran las siguientes:
•
•
•
•
•
•
Adaptación de señales para conectar dispositivos externos auxiliares de
entrada y de salida, como altavoces, micrófonos y auriculares.
Control de la conexión/desconexión con la línea telefónica.
Control del dispositivo externo para la grabación de la conversación
sistema/usuario.
Detección de llamadas.
Detección de tonos multifrecuencia (DTMF).
Detección de paso a falta (el usuario cuelga).
Esta interfaz de línea se conecta a uno de los conectores de expansión de la placa
principal del PC. La dirección base puede variarse a través de unos microinterruptores
existentes en la propia tarjeta de la interfaz, como se puede ver en el Apéndice A
Manual del usuario.
2.2.2
Placa VISHA
Es una tarjeta diseñada para el procesamiento digital de la señal de voz y utiliza
el DSP32C. Entre otras, implementa las siguientes funciones:
•
•
Reconocimiento de voz, concretamente la segmentación y la obtención de
los parámetros de análisis.
Reproducción/grabación, a través de un codificador/decodificador de voz
que utiliza codificación PCM.
Pág. 2-2
Capítulo 2: Descripción general del sistema. El problema de la multilínea
También se conecta a uno de los conectores de expansión de la placa principal
del PC. Ocupa 16 posiciones consecutivas en el mapa de entrada/salida y la dirección
base puede variarse a través de unos microinterruptores existentes en la propia tarjeta de
la interfaz (ver Apéndice A Manual del usuario).
2.2.3
Dispositivos auxiliares
Además de los elementos esenciales para el funcionamiento del sistema, es
posible conectar otros dispositivos auxiliares que permiten aumentar su funcionalidad,
como:
•
Un dispositivo externo de grabación: durante la fase de pruebas de una
determinada aplicación se suelen realizar estudios sobre el comportamiento
de los usuarios frente al sistema. Para ello es necesario grabar la
conversación entre el usuario y el sistema. Esto puede realizarse gracias a
una salida de la interfaz de línea que permite controlar un DAT (Digital
Audio Tape) externo. Cuando se detecta una llamada se puede activar el
DAT y se le proporciona la señal de audio con los niveles adecuados,
grabándose de esta forma la conversación usuario-sistema. Al finalizar la
llamada, se desactiva el DAT.
•
Micrófono y altavoz: el funcionamiento normal de las aplicaciones es a
través de la línea telefónica. Sin embargo, puede ser interesante permitir el
acceso a través del propio ordenador. Para ello es necesario disponer de un
micrófono y de un altavoz, de forma que sea posible la comunicación oral
con el sistema. Estos dispositivos se conectan a la tarjeta interfaz de línea,
encargándose ésta de realizar la adaptación de las señales a los niveles
requeridos.
Pág. 2-3
Capítulo 2: Descripción general del sistema. El problema de la multilínea
2.3 EL PROBLEMA DE LA MULTILÍNEA
Al abordar la conversión del sistema monolínea a multilínea surgieron las
siguientes limitaciones que dificultaban la labor:
Respecto al hardware:
•
Las tarjetas hardware utilizadas son monolínea (una tarjeta sólo es capaz de
atender una línea), y cada línea necesita dos: una tarjeta interfaz telefónica
(IFTEL) y una tarjeta de procesado digital de señal (VISHA). Para más
información sobre ellas, consultar el Apéndice A Manual del Usuario.
•
Las tarjetas son para bus ISA, con lo que la situación se agrava al coexistir
dos tipos de bus en los ordenadores personales actuales (ISA y PCI,
normalmente 4 slots de cada tipo). Por tanto, si no se utiliza un expansor de
bus, sólo será posible disponer de dos líneas por PC.
•
Dichas tarjetas no generan interrupciones, lo que obliga a consultarlas
continuamente en espera de algún evento. En el sistema monolínea, al
atender una sola línea, no planteaba problemas, pues el sistema podía
quedarse en un bucle realizando consultas. En el sistema multilínea esto es
inviable, pues tenemos otra línea que atender.
Respecto al software:
•
Algunas instrucciones del lenguaje, sencillas en apariencia, ocultaban código
de elevada complejidad, como era el caso de la instrucción
Verificar_Cadena, que hacía uso del reconocimiento y de la reproducción.
•
Enlazando con el tercer punto anterior, la presencia de bucles de consulta al
hardware era constante.
Pág. 2-4
Capítulo 2: Descripción general del sistema. El problema de la multilínea
2.4 SOLUCIONES ADOPTADAS
Respecto a las limitaciones hardware, poco se puede hacer si queremos seguir
utilizando las tarjetas hardware que se utilizaban en el sistema monolínea. Otra
posibilidad era utilizar otro tipo de hardware. La solución adoptada ha sido hacer el
nuevo sistema multilínea compatible tanto con el hardware descrito (VISHA + IFTEL)
como con las tarjetas DIALOGIC, que en una misma tarjeta integran la interfaz
telefónica y parte del procesado de señal, y además, permiten atender varias líneas
telefónicas con una sola tarjeta.
Es en el aspecto software donde están las novedades. Concretamente:
•
•
•
•
Se ha rediseñado completamente el compilador, con el objetivo de potenciar
el lenguaje y disponer de más instrucciones, más sencillas y más flexibles.
Las instrucciones complejas que existían en el sistema monolínea seguirán
disponibles en el sistema multilínea, pero en forma de subrutinas de alto
nivel.
Como se puede deducir de los dos puntos anteriores, el objetivo es dejar la
complejidad al lenguaje y simplificar los niveles inferiores, que es donde
debemos lograr la funcionalidad mutilínea buscada.
La ejecución de las aplicaciones está dirigida por eventos. Pronto veremos
las razones.
El resto del capítulo detallará la arquitectura software del sistema.
2.5 FILOSOFÍA MULTILÍNEA
2.5.1
Introducción
El sistema multilínea está diseñado para ejecutarse en entorno Windows. En su
desarrollo se ha utilizado el compilador Borland C++ v. 4.5 y se hace uso de la librería
de clases orientada a objeto ObjectWindows v. 2.5, que facilita enormemente la
programación en entorno Windows. También existe una versión EasyWin (una
aplicación DOS que puede ejecutarse en una ventana Windows), que implementa la
funcionalidad suficiente para interpretar aplicaciones.
ObjectWindows nos proporciona una función un tanto especial, denominada
IdleAction. Su funcionamiento es muy simple: se la llama automáticamente siempre que
no haya mensajes esperando ser procesados en la cola de mensajes de Windows. Sobre
la base de esta función se ha construido toda la arquitectura software del sistema. Esta
función está disponible en otros compiladores, como en Visual C++ de Microsoft, por
tanto la portabilidad o el cambio de compilador en el futuro no supondrán ningún
problema.
Pág. 2-5
Capítulo 2: Descripción general del sistema. El problema de la multilínea
Nos interesa que las llamadas a esta función IdleAction sean tan frecuentes como
sea posible, para ello:
2.5.2
•
Conviene que Windows tenga pocos mensajes que procesar. Los mensajes se
producen como resultado de acciones del usuario (movimiento del ratón,
pulsación del teclado) o bien son generados por las aplicaciones. Dado que el
sistema está diseñado para actuar de forma desatendida y ser la única
aplicación que se ejecute, sólo tendremos mensajes generados por el propio
sistema y por Windows. El sistema, si bien está diseñado para entorno
Windows y es normal que estuviera dirigido por mensajes, no es así, pues
está dirigido por eventos. Esto nos garantiza una baja tasa de mensajes a
procesar.
•
Las acciones que deben realizarse en la función IdleAction deben consumir
el menor tiempo que sea posible, pues es la única forma de garantizar que se
la llamará muchas veces por segundo. Esto lo conseguimos extendiendo la
filosofía idle al resto del programa, como veremos enseguida.
Extensión del concepto idle
La función IdleAction llamará a otra función, que llamaremos idle principal y
tiene el siguiente aspecto:
if (get_event (evento) != 0)
{
if (Realiza_Transicion (evento))
{
Ejecuta (evento);
idle();
}
while (get_event_nulo(evento) != 0)
{
if (Realiza_Transicion (evento))
{
Ejecuta (evento);
idle();
}
}
}
else
{
idle();
}
Pág. 2-6
Capítulo 2: Descripción general del sistema. El problema de la multilínea
Como se puede observar, el sistema maneja dos tipos de eventos:
•
Eventos ‘normales’: los generados por funciones que no se ejecutan de
forma inmediata. Es el caso de la mayoría de las funciones predefinidas.
•
Eventos nulos: los generan las funciones que se ejecutan de forma
inmediata. Por tanto, no hay que darles tiempo para que se produzcan. Dicho
de otra manera, si una función genera un evento nulo, se ejecuta de
inmediato, y se pasa a ejecutar la siguiente instrucción (por eso el bucle
while). Es el caso de todas las funciones internas y de algunas funciones
predefinidas.
La función Realiza_Transicion hace avanzar al autómata al siguiente estado, a la
siguiente instrucción, y la función Ejecuta, ejecuta dicha instrucción.
La función idle (le llamaremos idle general), nos permite seguir extendiendo el
concepto idle. Su misión es ejecutar, cuando no hay eventos, todas las funciones idle
que posee el sistema. Estas funciones realizan el procesamiento en segundo plano.
Por simplificar, supongamos que el sistema sólo tiene dos funciones idle:
llamada y reproducir. Está función sería:
void idle(void)
{
idle_llamada(); /* los llamaremos ‘idle de primer nivel’ */
idle_reproducir();
}
y estos idle de primer nivel son funciones de la forma:
void idle_llamada(void)
{
for (int linea = 0; linea < MAX_NUM_LINEAS; linea++)
{
//si estamos esperando llamada en esta línea
if (flag[linea].esperar_llamada.inicio)
{
if (idle_esperar_llamada_IFTEL(linea+1) == TODAVIA_NO)
{
//nada
}
else
{
flag[linea].esperar_llamada.inicio = 0;
//desactivamos el idle
fin_esperar_llamada_IFTEL(linea+1);
//finalizamos la detección
flag[linea].esperar_llamada.fin = 1;
//activamos cambio estado
}
}
}
}
Pág. 2-7
Capítulo 2: Descripción general del sistema. El problema de la multilínea
Para controlarlos se utiliza una variable flag, que es una estructura con tres
campos: inicio, estado y fin:
•
•
•
inicio: si está a 1 el idle de primer nivel está activo. Si está a 0 no está activo
y significará que no se requiere la funcionalidad que proporciona.
estado: guarda el estado en que se encuentra el idle de segundo nivel, pues
cada uno de ellos tiene la estructura de un pequeño autómata, que va
avanzando poco a poco hasta que cumple su función.
fin: cuando se pone a 1 genera el evento asociado a la función
correspondiente, de forma que el sistema pueda avanzar al siguiente estado.
Son estos idle de primer nivel los responsables de recorrer todas las líneas
activas e ir llamando, para cada línea, al idle de segundo nivel que es el nivel más bajo
en la jerarquía de funciones idle, y es el que realmente implementa la funcionalidad
(dependiente del hardware) deseada. Para que todo lo descrito funcione adecuadamente,
estos idle de segundo nivel deben ejecutarse rápidamente. Para lograrlo, se han
descompuesto en estados, de forma que son necesarias varias llamadas para que
completen su función. De forma general tienen la siguiente estructura:
int idle_esperar_llamada_IFTEL(int linea)
{
switch(flag[linea].esperar_llamada.estado)
{
case 0:
/* primer estado */
/* primera parte del procesamiento */
resultado_esperar_llamada[linea] = TODAVIA_NO;
/* pasamos al siguiente estado */
flag[linea].esperar_llamada.estado++;
break;
case 1:
/* segundo estado */
/* segunda parte del procesamiento */
resultado_esperar_llamada[linea] = TODAVIA_NO;
/* pasamos al siguiente estado */
flag[linea].esperar_llamada.estado++;
break;
case 2:
/* tercer estado */
/* tercera parte del procesamiento */
resultado_esperar_llamada[linea] = OK;
break;
}
// switch
return resultado_esperar_llamada[linea];
}
Esta función necesita tres llamadas para completarse. Por simplificar, se ha
mostrado un caso de desarrollo simple (pasa de un estado al siguiente), pero lo habitual
es que el desarrollo no sea simple, sino que se salte entre estados, hasta que se den las
condiciones necesarias para llegar al último estado y salir retornando OK, momento en
el que la función idle de primer nivel activará el evento asociado y la aplicación
avanzará al siguiente estado. Si el desarrollo no es simple, el número de llamadas a la
función, hasta que retorne OK, no coincidirá con el número de estados.
Pág. 2-8
Capítulo 2: Descripción general del sistema. El problema de la multilínea
En algunos casos especialmente complejos, no es suficiente con tener estados,
sino que hay que recurrir a crear subestados (estados dentro de un estado).
Todas las funciones idle de segundo nivel están acompañadas por dos funciones:
•
función inic: realiza labores de inicialización, casi siempre inicializando
variables o arrancando temporizadores que después utilizará la función idle
de segundo nivel para transitar adecuadamente entre sus estados. Por eso, se
ejecuta siempre antes de activar la función idle de segundo nivel. En el
siguiente apartado se puede ver un ejemplo de este tipo de funciones.
•
función fin: libera recursos, cierra ficheros o deja al procesador de señal en
un estado conocido. Es la complementaria a la anterior. Se ejecuta después
de que la función idle de segundo nivel haya terminado. Por ejemplo, la
función fin_esperar_llamada_IFTEL vacía la pila que se utiliza internamente
para guardar los retornos de subrutinas (necesario por si la llamada anterior
acabó haciendo un goto desde dentro de una subrutina), como se muestra a
continuación.
int fin_esperar_llamada_IFTEL(int linea)
{
// vaciamos la pila utilizada para guardar retornos de subrutinas
inicializa_pila_gosub(linea+1);
return 0;
}
En la siguiente figura se resume gráficamente todo lo anterior.
Pág. 2-9
Capítulo 2: Descripción general del sistema. El problema de la multilínea
Pág. 2-10
Capítulo 2: Descripción general del sistema. El problema de la multilínea
2.5.3
Utilización de las funciones idle
Cuando el intérprete encuentra una instrucción, busca la función que la
implementa y la ejecuta. Si se trata de una función interna o una función predefinida
que genera un evento nulo, cuando acaba su ejecución se avanza a la siguiente
instrucción. Pero si se trata de una función predefinida que genera un evento ‘normal’,
esta función debe llamar a la función ‘inic’ y activar la función idle de segundo nivel
poniendo a 1 el campo ‘inicio’ de la variable flag.
Siguiendo con el ejemplo anterior, cuando el intérprete encuentre la instrucción
esperar_llamada, llamará a la función __ESPERAR_LLAMADA, que tiene el siguiente
aspecto:
int __ESPERAR_LLAMADA (int linea, tipo_list tipos, arg_list arg)
{
// llamamos a la ‘función inic’
inic_esperar_llamada_IFTEL(linea+1);
// del resto se encarga 'idle_esperar_llamada'
return 0;
}
Y la función inic_esperar_llamada_IFTEL realiza las siguientes tareas:
int inic_esperar_llamada_IFTEL(int linea)
{
// configuramos la tarjeta IFTEL
configurar(DIRECC_IFTEL[linea]);
// contador de tonos de llamada recibidos a cero
rings_detectados[linea] = 0;
// empezaremos por el primer estado de la función idle de segundo nivel
flag[linea].esperar_llamada.estado = 0;
// activamos función idle de segundo nivel
flag[linea].esperar_llamada.inicio = 1;
return 0;
}
A partir de este momento la ejecución de esta función se hace en segundo plano.
El intérprete se queda esperando a que se active el evento asociado a esta función, cosa
que ocurrirá cuando la función idle de segundo nivel retorne OK, lo cual provocará que
la función idle de primer nivel ponga a 1 el campo ‘fin’ de la variable flag.
Si esta función debe retornar algún código, lo pondrá en la variable interna
resultado_funcion_predefinida en su función ‘fin’, de forma que pueda ser consultado
ejecutando la función interna RESULTADO_ANTERIOR inmediatamente después.
Esto se debe a que cuando acaba la función ESPERAR_LLAMADA no conocemos
todavía el resultado, pues se ejecuta en segundo plano. Este es el mecanismo que se
debe utilizar para todas las funciones predefinidas que generan eventos ‘normales’. Las
Pág. 2-11
Capítulo 2: Descripción general del sistema. El problema de la multilínea
funciones internas no alteran el valor de la variable resultado_funcion_predefinida. La
ventaja de utilizar la función RESULTADO_ANTERIOR es que, al tratarse de una
función interna, la consulta se hace con un switch y evitamos tener que hacer múltiples
if para consultar el valor de dicha variable interna.
La instrucción esperar_llamada no devuelve ningún código, pero para ilustrar lo
anterior, vamos a considerar la instrucción ODBC_CIERRA_BD, que sí retorna un
código para indicar si ha podido cerrar la base de datos o bien se ha producido algún
error:
ODBC_CIERRA_BD ("buzon","access";);
funcion_interna RESULTADO_ANTERIOR(;)
switch
case "-2" :
case "-3" :
case "-4" :
case "-5" :
GOSUB fallo_sistema:;
break;
fin_funcion_interna;
Pág. 2-12
Capítulo 2: Descripción general del sistema. El problema de la multilínea
2. DESCRIPCIÓN GENERAL DEL SISTEMA. EL PROBLEMA DE LA
MULTILÍNEA ______________________________________________________ 2-1
2.1
Introducción _______________________________________________________2-1
2.2
ARQUITECTURA HARDWARE ______________________________________2-1
2.2.1
2.2.2
2.2.3
Interfaz de línea telefónica _______________________________________________ 2-2
Placa VISHA__________________________________________________________ 2-2
Dispositivos auxiliares __________________________________________________ 2-3
2.3
EL PROBLEMA DE LA MULTILÍNEA________________________________2-4
2.4
SOLUCIONES ADOPTADAS ________________________________________2-5
2.5
FILOSOFIA MULTILINEA __________________________________________2-5
2.5.1
2.5.2
2.5.3
Introducción __________________________________________________________ 2-5
Extensión del concepto idle ______________________________________________ 2-6
Utilización de las funciones idle __________________________________________ 2-11
Pág. 2-13

Documentos relacionados