¿Quieres Patrocinar?
Envíanos un correo a contacto EN concursosoftwarelibre PUNTO org
Después de bastante tiempo sin actualizar, vuelvo para informar de que estoy trabajando en la documentación de mi Proyecto Fin de Carrera, Kora. También tengo a (menos de) medio hacer la comunicación con el entrenador de domótica.
Como siempre, a última hora y con prisas, pero esperemos que al final salga bien. Ya iré escribiendo posts en los que informe sobre el estado de la documentación y publique lo que lleve (por si a alguien le interesa o le viene bien). Todo esto estará además en el repositorio.
El verano ha sido una mierda, en resumen. Mucho trabajar en cosas-no-de-mi-campo y poco hacer, tanto para Kora como para las asignaturas que tengo para septiembre. De todas formas, me ha dado tiempo a publicar una aplicacioncilla en el Market de Android sobre la que os hablaré un día de estos, y cuyo código fuente pienso liberar antes o después, junto con unos cuantos posts describiendo cómo he hecho las cosas.
Ya me enrollo más otro día.
Oh, así me gusta, bien explicado:
The preferred mechanisms in CLIPS for ordering the execution of rules are salience and modules. Salience allows one to explicitly specify that one rule should be executed before another rule. Modules allow one to explicitly specify that all of the rules in a particular group (module) should be executed before all of the rules in a different group.
CUSL
Seguimos en activo pero nos cambiamos a partir de ahora nos podeis seguir en:
http://gexal.net
Este post es solamente para que sepáis que sigo vivo: el proyecto está más activo que nunca y dominous va en camino de adquirir consciencia de sí mismo, gracias a CLIPS, que está aportando el sistema experto a dominous.
Poco más, solamente dejar por aquí los links que estoy utilizando para aprender CLIPS y pyclips:
CUSL
Todo el mes de Julio me lo he tomado de vacaciones, como ha quedado patente por las entradas publicadas en el blog. El desarrollo ha vuelto a empezar ahora en Agosto, pero con un ritmo ligeramente inferior que durante el resto del año, en este caso simplemente quiero anunciar que todo sigue como antes, aunque la fecha de entrega de la version Beta va a retrasarse hasta finales de septiembre previsiblemente.
Durante estas semanas me ha dado tiempo de crear el asistente de creación de dietas, para poder hacerlo tanto de forma manual como de forma automática. La parte manual esta al 50% pero sin subir al repositorio, ya que no es funcional y la parte de automática esta por empezar. Creo que es mejor tener algo bien acabado y funcionando antes que medias partes sueltas.
Aparte del asistente, he aprovechado para corregir muchos bugs tontos que no lo estaban antes por falta de tiempo. La mayoría de la lista para hacer está terminada, ello no quiere decir que esté todo corregido.
Bueno, hasta aquí las novedades y espero poder seguir publicando novedades ahora ya con mayor brevedad.
Un saludo!
Saludos veraniegos. Con la calor, es difícil acordarse de todas las dependencias de cada fichero, y a menudo se nos escapan algunas al escribir nuestro Makefile. Mi compañero David me comentó la existencia de makedepend, una herramienta para la gestión automática de dependencias que había visto de refilón en algún makefile ajeno pero que apenas conocía.
Leyendo su entrada en Wikipedia y algunos comentarios en StackOverflow, vi que actualmente se desaconseja su uso, prefiriéndose las alternativas que la mayoría de compiladores ofrecen de forma nativa. Así fue como conocí la opción -MM de gcc, cuyo cometido es el mismo que el de makedepend: generar, en formato makefile, una lista de dependencias para cada fichero, basándose en las cabeceras incluidas. El funcionamiento es sencillo:
gcc -MM $(CXXFLAGS) ficherosFuente
Es importante pasarle las flags de compilación, ya que suelen incluir directivas como -I, que indican las carpetas en las que buscar ficheros de cabecera.
Problema 1: utilización de las dependencias generadas
La utilidad makedepend automáticamente actualizaba el makefile que la ejecutaba, añadiendo al final del fichero las dependencias generadas. Sin embargo, gcc -MM muestra su resultado en la salida estándar (o en un fichero que indiquemos), en lugar de añadirlo al Makefile actual, por lo que había que buscar una forma de incluir el resultado en nuestro Makefile. Con utilizar
-include dependencias.dep
Podremos incluir cualquier fichero en nuestro Makefile como parte del mismo.
Problema 2: rutas en el fichero generado
En mi caso, tengo tres carpetas que cuelgan de la raíz del proyecto: src, obj e include, en las que se guardan los cpp, los ficheros objeto y las cabeceras respectivamente. El problema de gcc -MM es que, al generar las dependencias, quita la ruta de la cabecera de las reglas, esto es, si tenemos un fichero objeto obj/main.o
que depende de main.cpp
, en el fichero generado aparecería así:
main.o: src/main.cpp
Por ahora, la única solución que he encontrado es utilizar sed para añadir al inicio la ruta correspondiente, aunque me habría gustado que gcc incluyera una opción al respecto.
gcc -MM $(CXXFLAGS) $(shell ls -t $(SRCDIR)/*.cpp) | sed 's/^\([a-zA-Z]\+.o:\)/$(OBJDIR)\/\1/g' > $(DEPFILE)
La explicación de la expresión regular es muy sencilla: Simplemente, cada vez que encuentre al inicio de la línea el nombre de un fichero objeto, que añada el nombre del directorio de los objetos (OBJDIR) y luego el nombre del fichero. Se utilizan grupos, primero agrupando el nombre con paréntesis y luego haciendo referencia al grupo con \1.
Por último, comentar que he puesto como dependencia del binario final al archivo de dependencias, y como dependencia del archivo de dependencias (valga la redundancia) todos los ficheros de código. El objetivo es que, si modifico algún fichero de código, puede que los includes cambien, así que hay que regenerar las dependencias. Tal vez acabe quitando esta dependencia cruzada, ya que estar regenerando las dependencias constantemente es un rollo. Otra opción que tal vez baraje es la de tener un archivo de dependencias por cada fichero objeto, así la regeneración no será tan pesada.
Opciones hay muchas.
Para poder probar todo lo contado en las anteriores entradas era necesario tener un elemento externo a los ya explicados que fuera el encargado de realizar la conexión entre los MMDevices.Para ello, desarrollé un pequeño (¡58 líneas!) programa en Python que únicamente solicitaba la conexión entre dos MMDevices, que eran pasados en forma de stringfied proxies al programa mediante argumentos.Además, siguiendo las directrices de AVStreams, también se debía crear un objeto de la clase StreamCtrl (control del flujo), que a su vez debería estar accesible públicamente. Esto último se ha "obviado" en cierto modo, ya que el programa termina nada más establecer la conexión entre los dos extremos, por lo que el objeto de tipo StreamCtrl desaparece al poco de ser creado. A fin de cuentas, este programa es el que pretendo utilizar para realizar algunas pruebas automáticas a los elementos del sistema (cosa que entrará, si FSM quiere, en otro post).Ahora, vamos a utilizarlo...Descarga de argos_configComo el resto de Argos, se puede encontrar en el repositorio Mercurial:$ hg clone http://arco.esi.uclm.es/~josel.segura/pfcDentro del directorio software/src/CliConfigurator está el ejecutable, que a la vez es el fichero de código fuente al tratarse de Python.Ejecución de argos_configPara ejecutarlo deberemos tener en cuenta que la sintaxis correcta para el comando es la siguiente:./argos_config [ICE options] A-Side-Proxy B-Side-ProxyLos argumentos A-Side-Proxy y B-Side-Proxy son intercambiables, ya que la conexión entre ellos se realiza igual independientemente de quien tenga el rol de sumidero y quien el de fuente.Los argumentos opcionales de Ice son los habituales vistos en los anteriores posts. Si no se pasa ninguno, argos_config intentará cargar el fichero de configuración Ice por defecto de Argos, que deberá estar en $HOME/.argos/ice_config (el equivalente a pasar como opción --Ice.Config=~/.argos/ice_config).Sobra decir que para poder probar esta herramienta de configuración será necesario tener dos MMDevices funcionando. Por ejemplo, se podría conectar una cámara Axis o un dispositivo V4L2 con un sumidero de vídeo en escritorio.
Tras un parón en mis entradas, vuelvo a la carga para contar como servir cámaras (o cualquier otro dispositivo) que funcionen utilizando la librería Video4Linux 2 (v4l2). Dentro de este grupo se engloban tanto cámaras USB, tarjetas capturadoras de televisión, TDT...Para desarrollar este servicio me he vuelto a apoyar del ya archi-comentado MMDeviceCreator. La implementación del V4L2Server se ha realizado en Python.El flujo multimedia del dispositivo V4L2 se sirve utilizando un servidor RTSP proporcionado por el proyecto Gstreamer. La implementación del servidor RTSP de Gstreamer, a día de hoy, es algo tosca, ya que no está integrado al 100% con el resto del framework, y además, a pesar de proveer unos bindings para Python que hubieran sido muy útiles para mi implementación, estos están poco/nada documentados y, por lo que he probado, son inusables a día de hoy.A pesar de ello he decidido utlizarlo en detrimento de otras alternativas como VideoLan VLC, ya que su servidor RTSP no era configurable tan a medida como promete, en un futuro, el de Gstreamer.Tras esta entrada sobre las tecnologías utilizadas, vamos al meollo del asunto:Servir un dispositivo V4L2 utilizando ArgosLo primero será obtener los ejecutables. Para ello y como siempre, debes descargar el código desde el respositorio:$ hg http://arco.esi.uclm.es/~josel.segura/pfcEn el directorio software/src encontraréis, entre otros, los directorios GstRTSPLaunch y v4l2Server. El primero de ellos contiene el código del servidor RTSP que vamos a utilizar, mientras que el segundo es la fuente de V4L2 de Argos.Compilar e instalar el servidor RTSPPara compilar el servidor RTSP necesitaremos tener instaladas las librerías de desarrollo siguientes:
Con ellos instalados, dentro del directorio GstRTSPLaunch indicado anteriormente, ejecutaremos:$ make$ make installEs posible que para el segundo paso (la instalación) hagan falta permisos de super-usuario.Otro modo de conseguirlo es a través del paquete Debian que se puede descargar desde el repositorio de Gnesis, añadiendo la siguiente línea al fichero /etc/apt/sources.list:deb http://babel.esi.uclm.es/gnesis sid mainTras añadir la línea solo hará falta hacer:# aptitude update# aptitude install gst-rtsp-serverEjecutando la fuente Argos de V4L2Para ello vayamos al directorio v4l2Server comentado anteriormente.Dentro de ese directorio está el fichero ejecutable "v4l2server.py". Al ejecutarlo podremos pasarle el fichero de configuración Ice de la forma habitual (con el argumento --Ice.Config=fichero) o bien dejarlo sin poner (toma por defecto el fichero de configuración de $HOME/.argos/ice_config).Dentro del fichero de configuración se deberán definir las siguientes variables de configuración:
En el repositorio podéis ver un ejemplo de este fichero de configuración guardado con el (original) nombre de "config".Para ejecutar:$ ./v4l2server.py --Ice.Config=configPor ahora, y a falta de más pruebas, se supone que el dispositivo a servir es el ubicado en /dev/video0. A no mucho tardar añadiré la posibilidad de configurar esto desde el propio fichero de configuración.¿Qué está ocurriendo tras todo esto?Detrás de todo este "lío" lo que está ocurriendo es que el programa v4l2server.py está notificando al servicio MMDeviceCreator indicado su capacidad para servir un dispositivo v4l2. MMDeviceCreator creará un MMDevice y demás objetos asociados a las negociaciones de AVStreams.Cuando alguien "conecte" este MMDevice con otro que tenga la capacidad de renderizar un flujo (por ejemplo, el RenderApplet), ambos MMDevices comenzarán el proceso de configuración del flujo. Cuando le toqué, el MMDevice creado para nuestro v4l2Server preguntará a éste la cadena de conexión RTSP (u otro tipo de protocolo streaming que se implementara) y se la pasará al sumidero multimedia para que sea capaz de consumir dicho flujo.
Justo ayer, peleándome un rato con freegemas, me surgió la necesidad de iterar por un vector y borrar los elementos que tuvieran cierta propiedad. A primera vista puede parecer sencillo, pero la solución no es trivial:
size_t i
) ya no vale.Estuve pensando un poco cómo hacerlo y se me ocurrieron algunas formas poco ortodoxas o que podrían llevar a error. Una de las formas que encontré por internet fue la siguiente, con iteradores y un for algo especial:
for (iterator it = v.begin();
it != v.end();
)
{
if (condition)
it = v.erase(it);
else
++it;
}
Si os fijáis, el for
no tiene tercer parámetro, sino que somos nosotros los que movemos el iterador manualmente. Para evitar la invalidación, utilizamos el valor de retorno de erase
.
Pero otra forma de hacerlo, más elegante a mi parecer, era utilizar el algoritmo remove_if
y un predicado adecuado. En mi caso, el contenedor tenía objetos de una clase con un método que devolvía un booleano tal que, si es verdadero, el elemento debería borrarse. En un primer intento hice
remove_if(contenedor.begin(), contenedor.end(), boost::bind<bool>(&Clase::Comprobar, _1));
Pero los elementos no se borraban. «Qué raro», pensé. Resulta que el funcionamiento de remove_if
no es el de borrar directamente los elementos, sino que reordena los elementos a borrar al final del contenedor y devuelve un iterador que apunta al primero de esos elementos a borrar. Así, junto al método erase
del vector, tenemos:
contenedor.erase(remove_if(contenedor.begin(), contenedor.end(), boost::bind<bool>(&Clase::Comprobar, _1)), contenedor.end());
Esto se conoce como el Erase-remove idiom y tiene hasta su propio artículo en la Wikipedia. Muy curioso.
Las tuplas en C++ son una versión generalizada de std::pair
, en las que podremos almacenar un número arbitrario de valores de distinto tipo.
Al formar parte de TR1, podréis encontrarlas:
boost/tuple/tuple.hpp
, como boost::tuple
.tr1/tuple
, como std::tr1::tuple
.Las tuplas se declaran de manera similar a los pares, indicando la cantidad y tipos de los datos contenidos como parámetros de plantilla. Podemos inicializar los datos pasándole los valores al constructor.
boost::tuple<int, string, string> miTupla (1, "Cisco", "Pepe");
Para los pares, se utilizaba first
y second
para acceder a los datos, pero al tener un número variable de elementos, con las tuplas hace falta alguna forma más genérica. Para ello se proporciona la función paramétrica get
, que recibe como parámetro de plantilla el índice el elemento al que queremos acceder. Al devolver una referencia, nos servirá tanto de getter
como de setter
.
string cadena = miTupla.get<1>();
miTupla.get<0>() = 5;
Otra de las utilidades que vienen con las tuplas es tie
, que nos permitirá darle valor a varias variables de una vez, incluso utilizando el valor de retorno de una función. Además, tie
funciona tanto con tuplas como con los pares tradicionales.
pair<int, int> fun (){
return make_pair(5, 3);
}
int main(int argc, char *argv[])
{
int a, b;
tr1::tie(a, b) = fun();
printf("a:%i, b:%i\n", a, b);
}
Las tuplas traen muchas más funciones y utilidades, como por ejemplo para personalizar los operadores de flujos facilitando la entrada y salida, y muchas otras opciones que podéis observar en la documentación oficial.