Tethering; Operaciones básicas…
Una de las características más atractivas de la nueva versión de RAD Studio, y sobre la que seguro continuaremos hablando, pues están previstas ampliaciones y mejoras para las próximas versiones, es el AppThthering. Antes de continuar con el artículo, os dejo unos links por si queréis revisar documentación sobre esta característica. Si ya lo habéis revisado, pues podéis continuar con la introducción.
Lo primero a revisar es la DocWiki de Embarcadero. Con esta información ya podemos iniciar. No es que sea muy extensa, pero está la suficiente y básica del funcionamiento.
- Using App Tethering
- Adding App Tethering to Your Application
- Connecting to Remote Applications
- Sharing and Running Actions on Remote Applications
- Sharing Data with Remote Applications
También podéis revisar la entrada anterior que publiqué en el blog, en la que os mostré algunas preguntas y respuestas que aparecieron en la presentación de embarcadero sobre características del nuevo RAD Studio. Hay un bloque dedicado a AppTethtering bastante clarificador.
Añadir un par de videos que se pueden encontrar en el canal de embarcadero:
Por último, os remito a algunas entradas de los blogs de embarcadero, como la de Sarina DuPond titulada “Extend your Windows apps to mobile with Tethering in XE6”, o esta publicada en Fixed by Code titulada “Fun with Delphi XE6 App tethering and barcodes”.
INTRODUCCIÓN
Si a estas alturas no ha quedado claro (con todo lo anterior) qué es y para qué se usa el Tethering, sólo queda citar el siguiente párrafo (traducido):
Usando Tethering sus aplicaciones pueden fácilmente:
El tethering no depende de un protocolo específico de transporte de datos, de forma que se pueden implementar nuevos protocolos utilizando la API de tethering. La RTL proporciona soporte para conexiones ethernet que se ejecutan en la misma red local (LAN), incluso si se están ejecutando en el mismo dispositivo. |
Hay que decir que el tethering, aunque puede ser muy útil en determinados escenarios, tiene un funcionamiento bastante básico desde el punto de vista del programador. Toda la gestión la realizamos utilizando 2 componentes (iguales para todas las aplicaciones que lo utilizan).
TTetheringManager
Este componente es el que está más enfocado a las conexiones entre dispositivos.
TTetheringAppProfile
Es el componente que está más enfocado al intercambio de Datos. Es el que se utiliza para “publicar” acciones que posteriormente se van a compartir entre aplicaciones o para enviar y recibir recursos.
OPERACIONES BÁSICAS
Vamos a hacer un recorrido por las operaciones básicas que podemos realizar entre aplicaciones.
EXPLORACIÓN
Lo primero que podemos hacer cuando arrancamos una aplicación en la que estamos trabajando con Tethering es explorar la red en busca de aplicaciones a las que nos podamos conectar.
Hay 2 formas de conectarse a otras aplicaciones; Veremos que hay un modo «automático» y un modo «manual». Dejaré el «modo automático» para más adelante y ahora vamos a centrarnos en el manual.
Lo primero que podemos hacer al arrancar la aplicación es «explorar» la red en busca de aplicaciones con las que nos podamos «emparejar»; Para ello el componente de TTetheringManager cuenta con el método DiscoverManagers, al que le pasamos un parámetro opcional de Timeout.
// Buscar otras aplicaciones ttManager.DiscoverManagers(3000); |
En la imagen siguiente podemos ver 2 aplicaciones, cada una en la parte superior con su “Identificador”. Una vez realizamos la búsqueda, podemos recorrer la propiedad RemoteManagers para acceder a las aplicaciones que hemos encontrado. En la imagen inferior, podemos ver el resultado de la búsqueda.
Si ejecutamos varias aplicaciones (instancia FMX) y volvemos a realizar la búsqueda, veremos que el resultado es el esperado (cada aplicación al iniciar genera como identificador un nuevo GUID), de forma que el resultado es el siguiente:
Al finalizar la búsqueda “salta” el evento OnEndManagersDiscovery, que podemos capturar para saber cuando podemos continuar.
EMPAREJAR APLICACIONES
Si continuamos trabajando con la búsqueda “manual”, una vez tenemos el resultado de la búsqueda, si nos interesa podremos realizar el «pair» (como Delphi le llama) o emparejamiento de aplicaciones.
Para ello utilizamos el método PairManager de TTetheringManager, con una llamada tan simple como esta:
var mInfo:TTetheringManagerInfo; begin // Emparejar el seleccionado mInfo := ttManager.RemoteManagers[mmAplicaciones.ItemIndex]; ttManager.PairManager(mInfo); |
Utilizamos la lista obtenida y disponible en RemoteManager para la llamada.
Si todo funciona correctamente, en la aplicación que ha realizado la llamada «saltará» un evento OnPairedToRemote (con la información del Manager al que nos hemos conectado como parámetro) y en la aplicación a la que nos hemos conectado «saltará» el evento OnPairedFromLocal con la información igualmente del manager como parámetro.
Si se intenta realizar más de una vez este «emparejamiento» veremos que los eventos no vuelven a saltar, mientras ambas aplicaciones sigan conectadas.
EMPAREJAMIENTO AUTOMÁTICO
Como hemos visto, el emparejamiento «manual» es bastante sencillo. En el caso del automático, aun lo es más, pues basta con hacer una llamada al método AutoConnect en nuestra aplicación. Con ello, se hace una búsqueda de aplicaciones disponibles y se intenta el emparejamiento de forma automática.
UTILIZANDO CONEXION CON PASSWORD
Al realizar las conexiones (emparejamientos) tenemos la posibilidad de añadir algo de seguridad utilizando un password. El en componente TTetheringManager podemos rellenar la propiedad de Password.
Cuando una aplicación intenta realizar el emparejamiento con otra (con otro componente TTetheringManager), “protegido” con un password, “salta” el evento OnRequestManagerPassword en la aplicación que intenta hacerla conexión. Utilizando este evento podemos rellenar “in situ” el password (o solicitarlo al usuario) que necesita el emparejamiento.
procedure TFormMain.ttManagerRequestManagerPassword(const Sender: TObject; const RemoteIdentifier: string; var Password: string); begin _Log('OnRequestManagerPassword; ' + RemoteIdentifier); Password := 'EmbarcaderoXE6'; end; |
COMPARTIR DATOS CON RECURSOS
Hay varias formas de compartir datos entre aplicaciones. La primera que vamos a ver (y que me ha resultado muy intuitiva) es la que utiliza Recursos compartidos (públicos).
La idea es la siguiente; Desde una aplicación publicamos un recurso con un nombre único y desde cualquiera de las aplicaciones que tenemos “emparejada” a ella, podemos solicitar directamente el valor de ese recurso a partir del nombre único que hemos definido anteriormente.
En la aplicación que publica el recurso (SharedRes1), podemos modificar su valor accediendo a los recursos “locales” que hemos publicado:
// Modificar el valor de nuestro recurso compartido ttaProfiler.Resources.FindByName('SharedRes1').Value := ‘nuevo valor’; |
Y desde las aplicaciones conectadas podemos acceder a los recursos compartidos, primero accediendo al “RemoteProfile” y luego directamente al recurso (a partir del nombre único) o también podemos acceder a la lista que los contiene todos y recorrerla.
Para ello desde el TTetheringManager tenemos la propiedad RemoteProfiles, para acceder a los “RemoteProfiles”. Una vez que ye tenemos el “RemoteProfile” podemos acceder a un recurso de forma individual (GetRemoteResourceValue) o a la lista completa de los publicados (GetRemoteProfileResources).
var rRes:TRemoteResource; pInfo:TTetheringProfileInfo; begin // Primero accedemos al perfil remoto pInfo := ttManager.RemoteProfiles[0]; // Luego accedemos al Recurso a partir del nombre único. rRes := ttaProfile.GetRemoteResourceValue(pInfo, 'SharedRes1'); // Otra opción sería recorrer la lista de recursos _Log('Recursos remotos: ' + IntToStr(ttaProfile.GetRemoteProfileResources(pInfo).Count)); // Accedemos (en este caso) al primero rRes := ttaProfile.GetRemoteProfileResources(pInfo)[0]; // Valores del recurso _Log(' Name: ' + rRes.Name); _Log(' Hint: ' + rRes.Hint); _Log(' Value: ' + rRes.Value.AsString); // Finalmente accedemos al valor edtRes.Text := rRes.Value.AsString; |
Por último, queda una opción interesante que es la de “Suscribirse a los cambios de un recurso”. De forma, que los cambios que se producen en ese recurso, se reciben de forma automática por todos aquellos que se hayan suscrito.
Para ello, al igual que antes, basta con utilizar el “RemoteProfile” y el nombre único que hemos asignado a nuestro recurso. Un código tan simple como este:
// Para obtener el “RemoteProfile” y el “RecursoRemoto” se hace igual que hemos // en el código anterior… // Nos subscribimos para futuros cambios ttaProfile.SubscribeToRemoteItem(pInfo, rRes); |
Os adjunto un video sencillo donde se ve funcionando…
NOTA: Hay en la parte central un pequeño desfase entre el sonido y las acciones, pero no afectan a la compresión del video (creo –es muy pequeña-). Cosas de ser novato en esto de las presentaciones… ;-)
ENVIANDO DATOS A RECURSOS TEMPORALES
Hemos visto cómo intercambiar datos entre aplicaciones utilizando “Shared resources” (recursos compartidos), aunque también podemos hacerlo utilizando lo que se llaman “recursos temporales”. En este caso una aplicación envía directamente datos a otro (sin ninguna definición previa) y la segunda los recibe en un “recurso temporal”.
Para ello basta con que la aplicación utilice el método SendString o SendStream sobre uno de “profiles” de las aplicaciones a las que está conectada. Bastaría con un código como el siguiente:
var mInfo:TTetheringProfileInfo; begin // Acceder al primer Profile de los conectados mInfo := ttManager.RemoteProfiles[0]; // Enviar una cadena ttaProfiler.SendString(mInfo, 'Enviando cadena...', edtSend.Text {cadena a enviar}); |
La aplicación a la que realizamos el envío, recibe un primer evento OnAcceptResource, en el que tenemos datos del recurso y la posibilidad (mediante el parámetro Accept) de aceptar el envío o no. La cabecera del evento es la siguiente:
procedure TFormMain.ttaProfileAcceptResource(const Sender: TObject; const AProfileId: string; const AResource: TCustomRemoteItem; var AcceptResource: Boolean); |
Una vez que aceptamos el recurso, nos llega un segundo evento; OnResourceReceived en el que por parámetro tenemos acceso al recurso que se nos ha enviado.
EJECUTANDO ACCIONES
Por último, vamos a ver cómo ejecutar acciones en aplicaciones a las que estamos “emparejados”. De forma similar a como se hace en los recursos podemos ejecutar acciones “remotas” de dos formas.
Para la primera utilizamos una llamada directa a las acciones que se publican en otra aplicación y para la segunda veremos como hacerlo “mapeando” una acción en local con una en Remoto.
En cualquier caso lo primero que debemos hacer es crear una acción en nuestra aplicación y “publicarla” para que pueda ser llamada desde otras aplicaciones a las que nos hemos “emparejado”. Para ello creamos una Action utilizando un TActionList como lo hacemos siempre. Una vez la tenemos, en nuestro AppProfile creamos una “Action local” que enlaza con la que queremos publicar. Con esto ya tenemos nuestra acción lista para ser ejecuta “en remoto” (por una aplicación “emparejada” con la esta).
EJECUTAR UNA ACCIÓN REMOTA DIRECTAMENTE
La forma más directa de ejecutar una acción que tiene publicada una de las aplicaciones con las que nos hemos emparejado es utilizar el método GetRemoteProfileActions, desde el AppProfile de nuestra aplicación. Co ello podremos acceder a las acciones publicadas en otra aplicación.
Código de ejemplo:
var ra:TRemoteAction; begin // Accedemos a la acción publicada en la aplicación ra := ttaProfile.GetRemoteProfileActions(ttManager.RemoteProfiles[0]).Items[0]; // Si hemos podido encontrarla, la ejecutamos… if Assigned(ra) then begin ra.Execute; end; |
Así de simple y así de potente.
Hay que tener en cuenta que en estos ejemplos, yo estoy accediendo directamente a los elementos de la lista utilizando el índice [0]; En una aplicación real, habría que implementar algunas precauciones en función de los elementos creados o recorrer las listas (si hubiera más de un elemento) para acceder exactamente al elemento deseado. Es este caso lo hemos hecho así para simplificar.
EJECUTAR UNA ACIÓN REMOTA USANDO UNA LOCAL.
La alternativa para ejecutar una acción remota es crear una local y “mapear” en cierta manera ambas. De esta forma, cuando ejecutas la “acción local” en programa ejecuta la acción remota de la aplicación a la que está conectado.
Para ello debemos marcar la acción local como tipo “Mirror”; Es lo único necesario (y diferente) para ejecutar una acción de una aplicación “emparejada” a ejecutar una local.
En la imagen siguiente podemos ver, un TActionList con una TAction definida con el nombre ActionCerrar (1). Hasta aquí como siempre.
Luego nuestro componente ttaProfile en la propiedad Actions, posee una TActionLocal llamada ActionCerrar (2) y que se enlaza con la acción estándar definida antes (3).
Por último podemos ver como esta TActionLocal tiene la propiedad Kind a Mirror, que es lo que marca que al ejecutarla, se ejecute la Acción remota de otra aplicación.
Aunque en la explicación parece complejo, una vez que se hace es bastante sencillo, tal y como se ve en el vídeo anterior.
AÑADIDO: me había dejado por publicar el código de las aplicaciones.
Hasta aquí la entrada. Espero que os sea útil. Como siempre los comentarios, críticas, sugerencias,… será bien recibidas.
Un saludo.
Embarcadero MVP.
Analista y Programador de Sistemas Informáticos.
Estudios de Informática (Ingeniería Técnica Superior) en la UPC (Universidad Politécnica de Barcelona).
Llevo utilizando Delphi desde su versión 3. Especialista en diseño de componentes, Bases de Datos, Frameworks de Persistencia, Integración Continua, Desarrollo móvil,…
Muy interesante.
@casimiro
Gracias Antonio.
Y yo seguía preguntándome que era eso del Tethering y tenemos aquí un How to for dummies.
Muchas gracias.
super bien explicado!!!
gracias :)
¡Aquí otro dummy agradecido! Gracias Germán, vamos ahora al otro artículo:
http://neftali.clubdelphi.com/?p=3647
:)
Hola
Buen tutorial!.
Tengo una pregunta: haz usado estos componentes de tethering en un form modal? Me refiero a un form que no sea el principal y que no tenga AutoCreate, es decir un TForm que uno crea, hace ShowModal y luego Free.
La pregunta viene a que quedan resource leaks relacionados con los 2 componentes de tethering y no se como solucionarlos… Estoy probando con XE7 y uso la conexcion automática (uso AutoConnect) al estilo de los ejemoplos de Embarcadero!!
Saludos y gracias
@Angel
El form que he utilizado no era modal, pero a priori eso no debe tener nada que ver con la liberación de recursos. El Free del componente es el mismo método independientemente de quien lo llame (sea un form que se visualiza en modal o no).
Es más, podrías crear y destruir un componente sin necesidad de formulario.
Un saludo.
Hola, quisiera preguntarle si es razonable utilizar Tethering para pasar datos de un celular a una aplicacion delphi. Mi proyecto es recibir mensajes enviados desde celulares que no tienen plan de datos para reportar si se reviso el contador, enviar el valor de un contador, envia un mensaje de texto que incluye el numero de contador y el valor en el contador, el mensaje se recibe y se lee con delphi la cola de mensajes recibido(encontre un ejemplo en la web) ahora requiero enviarlo al computador , o suguiere usar otra tecnica para pasar datos de un telefono al sistema
@Carlos A Ramirez
Hola Carlos.
Si en teléfono y el computador están en la misma red wifi (o accesibles por bluetooth) sí que es posible.´
En cuando a razonable, lo único que debes tener en cuenta (y hacer algunas pruebas) es que si la cantidad de datos es muy grande, tal vez tengas que probar la velocidad para ver si es adecuada a tu proyecto.
Estoy probando con un escritorio y un supuesto móvil. El escritorio genera un contador de 1 a 100 y el móvil lo recibe perfectamente. Pero si pongo dos móviles la velocidad de recepción baja una burrada. Se ocurre algo?
La conexión es manual y solo comparto un recurso en el escritorio que es el numero
@Manolo
Hace tiempo que no hago pruebas, pero no debería pasar.
Prueba cobn la opción que se describe en: «ENVIANDO DATOS A RECURSOS TEMPORALES» a ver si el problema persiste.
Un saludo.