DISCLAIMER: no seré responsable si alguien decide seguir mis pasos y se carga su aspiradora. En principio todo lo que cuento debería ser seguro, pero por motivos obvios no me puedo responsabilizar de lo que hagan otras personas, sólo de lo que haga yo.
¡Victoria, victoria! ¡Mi conga ya me hace caso!
Lo que hice fue escribir un nuevo servidor, esta vez desde cero, que combina HTTP y el protocolo específico de la conga. El código he decidido no hacerlo público hasta que termine el análisis completo, no vaya a ser que meta la pata en algo…
El motivo de no utilizar el código de http.server que ya tenía ha sido, fundamentalmente, porque debido a todos los trucos que tuve que utilizar para que funcionase, al final no usaba casi nada, con lo que me compensaba escribir mi propio servidor. Además, al hacerlo yo todo con select y sockets podía integrar mucho mejor en un único hilo la gestión del puerto 80 y la del 20008.
Básicamente creé una primera clase, BaseServer, que tiene un socket y que gestiona las funcionalidades básicas de éste, incluyendo un método vacío para cada vez que se reciben datos nuevos, y otros métodos para gestionar cuando se cierra. Sobre esta clase he construido absolutamente todo.
Luego creé la clase HTTPConnection, la cual es la que gestiona una conexión HTTP concreta. Cada vez que se hace una petición HTTP, se crea un objeto de este tipo, y es él quien analiza lo que llega. Su función es la misma que el código que hice antes con http.server. Para gestionar esto está la clase HTTPServer, que es la que abre el puerto 80 y crea los objetos anteriores tras cada conexión.
De la misma manera están las clases RobotConnection y RobotServer, pero esta vez para gestionar las conexiones al puerto 20008. El tenerlo dividido así permite controlar varios robots a la vez (aunque no creo que lo implemente de momento, no está de más que la arquitectura subyacente lo esté preparada).
La clase RobotConnection tiene un método llamado send_command, que lo que hace es, básicamente, emitir exactamente las mismas secuencias que registré anteriormente, copiadas byte a byte. Esto, al final, es bastante sencillo porque todos los comandos se emiten con el mismo tipo de paquete, y sólo cambia el número de comando y, en un par de casos, un parámetro extra.
Por otro lado, en base al contenido del segundo, tercero y quinto enteros de los paquetes enviados por la aspiradora puedo saber si es un paquete que espera una respuesta por mi parte, o un ACK de un comando enviado por mí, por lo que en el método de recepción de datos me limito a hacer una lista de comparaciones para ver qué tengo que responder; y si recibo una combinación desconocida, la imprimo.
Todas estas clases están gestionadas por una clase maestra llamada Multiplexer, que lanza primero una instancia de HTTPServer y otra de RobotServer, y va gestionando las nuevas instancias creadas por estos dos objetos mediante select.
Por último, hay una clase llamada Robot que está asociada permanentemente con un robot concreto, aunque éste no esté conectado. De momento se crea bajo demanda, pero en el futuro podría incluir algún tipo de persistencia, por ejemplo para que recuerde la configuración deseada por el usuario o los mapas, aunque se apague la aspiradora. Cada vez que un robot se conecta, se utiliza su identificador único para encontrar el objeto correspondiente y enlazarlo, y cada vez que se pierde la conexión, se informa de ello al objeto Robot para que borre la referencia al objeto RobotConnection.
Para la primera prueba decidí aprovechar el mismo servidor web que ya tengo para los robots e implementar un control sencillo por REST, mediante una sencilla página web. Es justo lo que se ve en el vídeo.
El siguiente paso es analizar y añadir los comandos para configurar el método de trabajo, la potencia del ventilador y el uso de la fregona, y por supuesto el control manual (aunque ya adelanto que parece que es en este caso donde entra en juego el puerto 8888 y la conexión directa con la tablet… a ver si hay suerte y me la puedo saltar).
También quiero analizar cómo se transmite la información de los mapas, pero eso será al final.