Anteriormente vimos el ejemplo mínimo de un cliente para Arduino. En este post, se desarrollará el servidor de ese mismo ejemplo, escrito en Python para PC. En este caso, se utilizará el endpoint serie para Ice.
Python: servidor mínimo
Recordando la entrada anterior, tenías una interfaz Ice muy sencilla, definida en el fichero example.ice. También habías implementado un cliente para Arduino que usaba esa misma interfaz. El cliente mandaba una invocación al método sayHello cada segundo. En este post, implementarás el servidor en Python que recibirá esas invocaciones.
Nota: puedes descargarte todo el código fuente y los archivos de configuración para este ejemplo (y los demás de esta serie de artículos) desde el repositorio de bitbucket:
hg clone https://bitbucket.org/arco_group/arduino-icec-idm
Para realizar esta parte correctamente, dirígete al directorio que habías creado, llamado hello-world. Allí, genera un fichero para el servidor (por ejemplo server.py). Incluye en él el siguiente boilerplate:
import Ice import sys class Server(Ice.Application): def run(self, args): ic = self.communicator() if __name__ == "__main__": Server().main(sys.argv)
Este código es un esqueleto básico muy común en aplicaciones de Ice. Si lo ejecutas con el intérprete de Python, se lanzará correctamente, aunque no hará básicamente nada, y terminará:
python server.py
Añadir soporte para el Endpoint Serie
Ice, por defecto, solo proporciona soporte a los endpoints que trae de serie (TCP, UDP, SSL y WS). Para poder utilizar el endpoint serie, será necesario que configures el broker adecuadamente. Para ello se emplea un fichero de configuración (puedes llamarlo, por ejemplo, server.config), que contendrá lo siguiente:
Ice.Plugin.PyEndpoint = IcePyEndpoint:addPyEndpointSupport PyEndpoint.Module = SerialEndpoint PyEndpoint.Factory = EndpointFactoryI
Ahora podrás ejecutar de nuevo el servidor, especificando que la configuración del broker la debe tomar de ese fichero (aunque seguirá sin hacer gran cosa):
python server.py --Ice.Config=server.config
Instanciando el objeto Server
El servidor deberá crear un objecto (llamado Server) que implementará la interfaz Hello, y hacerlo disponible a través del puerto serie. Para ello, como primer paso, necesitas importar la interfaz (en el ámbito global del fichero):
Ice.loadSlice("example.ice") import Example
Lo siguiente será implementar el sirviente. Para este ejemplo, puedes simplemente imprimir por consola que la invocación ha llegado. Recuerda que el sirviente hereda de la interfaz Example.Hello, por lo que puedes definir la siguiente clase (a nivel global también) :
class HelloI(Example.Hello): def sayHello(self, current): print("sayHello was called!")
Por último, tendrás que crear el adaptador de objetos (con el endpoint serie), y añadir este sirviente. El código (incluido en el método run de la clase Server) será el siguiente:
adapter = ic.createObjectAdapterWithEndpoints( "Adapter", "serial -h 127.0.0.1 -p 1793") adapter.activate() adapter.add( HelloI(), ic.stringToIdentity("Server") ) print("Waiting events...") self.shutdownOnInterrupt() ic.waitForShutdown()
De todo ese código, llama la atención especialmente el endpoint del adaptador:
serial -h 127.0.0.1 -p 1793
Dado que este endpoint debe utilizar el puerto serie como mecanismo de comunicaciones, ¿qué significan los dos parámetros -h y -p en este caso? ¿Y dónde especificas el dispositivo serie que deseas emplear? La siguiente sección analizará esto en detalle.
Servicio de acceso al puerto serie
La comunicación serie con cualquier dispositivo se considera como un enlace punto a punto. Por las características específicas de este tipo de puertos, se requiere que el acceso a este dispositivo sea exclusivo. Esto implica que dos servicios (o el mismo servicio desde dos puntos diferentes) no puedan utilizar concurrentemente el mismo puerto serie. Eso supone una gran desventaja, pues evita (por ejemplo) que un mismo servicio pueda actuar como cliente y servidor al mismo tiempo (debido a la arquitectura del endpoint).
Para solucionar este problema, se ha diseñado un servicio que actúa como árbitro de acceso al medio, serializando y distribuyendo los mensajes entre los clientes y el dispositivo. Este servicio (llamado SerialService) es accesible mediante una conexión TCP.
El servicio SerialService está incluido en el paquete ice-serial. Para lanzarlo necesitas, de nuevo, un fichero de configuración (que puedes llamar serial-service.config). En este caso, el contenido será el siguiente:
SerialService.Device = /dev/ttyUSB0 SerialService.Speed = 115200 SerialService.Host = 127.0.0.1 SerialService.Port = 1793
Esta configuración asume que el puerto serie está disponible en el fichero /dev/ttyUSB0, pero puedes cambiarlo para adaptarlo a tus circunstancias. Por otro lado, vemos que escucha en la interfaz 127.0.0.1, en el puerto TCP 1793, que se corresponde con la configuración que pusiste en el endpoint serie del adaptador de objetos.
Una vez creado el fichero de configuración, puedes ejecutar el servicio con el comando:
serial-service --Ice.Config=serial-service.config
Ejecución y prueba del servidor
Si tienes el servicio SerialService funcionando correctamente, ya puedes ejecutar el servidor de ejemplo, de la siguiente forma:
python server.py --Ice.Config=server.config
Además, si tienes conectado el Arduino al puerto serie indicado, y subiste el sketch que vimos en la anterior entrada, deberías ver el mensaje ‘sayHello was called!‘ en la consola del servidor, una vez por segundo. Si así es, ¡enhorabuena! 😉
Ejecución del servidor