Weblogs Código

Bitácora de Javier Gutiérrez Chamorro (Guti)

REBOOT para DOS

septiembre 22, 2017 11:52

Disfruté bastante haciendo UPTIME 2.0 para DOS, así que me puse unos minutos, y desarrollé REBOOT. Esta sencilla utilidad para DOS de 126 bytes en un archivo .COM, se encarga de lanzar un reboot o reinicio de equipos basados en DOS. En realidad, es un reinicio en caliente, o warm boot, similar al que obtendríamos […]

Artículo publicado originalmente en Bitácora de Javier Gutiérrez Chamorro (Guti)

La entrada REBOOT para DOS aparece primero en Bitácora de Javier Gutiérrez Chamorro (Guti).

» Leer más, comentarios, etc...

Poesía Binaria

Me mudo de casa, ¿qué tal me veis?

septiembre 21, 2017 08:10

Me cambio de casa, nuevo dominio

Lo sé, estoy loco. Cerca de 10 años con el blog y ahora me da por comprar un dominio para el mismo. Mucho tiempo con totaki.com, dominio al que le tengo cariño, de un proyecto anterior. Uno de esos proyectos que quieres retomar, y no me pongo.

Nunca he sido persona muy de SEO, ni me he preocupado demasiado por optimizar la web para buscadores. Eso sí, la mayoría de las visitas provienen del buscador por defecto de mucha gente, Mr. Google y lo más seguro que durante unos días pierda muchas de las visitas provenientes de buscadores.

Por otro lado, he intentado hacer redirecciones 301 (permanentes) a todas las páginas, en la misma ruta en que estaban en la antigua ubicación. Y ya de paso, he arreglado algunas ubicaciones que se quedaron colgadas hacia principios de este año cuando introduje HTTPS y que producían que el navegador dijera que la página no era totalmente segura. Ya sabéis, por eso de que estoy mezclando contenido HTTPS y contenido HTTP.

Aunque todo puede fallar, sólo espero que los planets y demás webs agregan mis contenidos hagan bien la redirección 301, de hecho, este es uno de los principales motivos por los que no he puesto el dominio antes… pero tenía que atreverme. Además, las redirecciones del antiguo dominio estuvieron un par de horas sin funcionar. Sólo espero que a Google no le diera por rastrear la antigua ubicación ese par de horas precisamente.

¿A que queda chulo el dominio? ¿Qué os parece?

Si todo sale bien y no pierdo muchos visitantes quiero hacer una guía de cómo he hecho la migración. Si la cosa no sale bien, me esconderé lentamente sin que nadie me vea, y aquí no ha pasado nada.

Foto principal: Scott Webb

The post Me mudo de casa, ¿qué tal me veis? appeared first on Poesía Binaria.

» Leer más, comentarios, etc...

Variable not found

Curso de ASP.NET Core 2 MVC en CampusMVP

septiembre 19, 2017 07:04

Curso online de MVC

Como ya sabréis, hace algunas semanas Microsoft anunció la disponibilidad de, entre otras tecnologías, ASP.NET Core 2.0, y me complace anunciaros que hace pocos días hemos lanzado desde CampusMVP el curso que muchos andabais esperando para dar el salto: Desarrollo Web con ASP.NET Core 2 MVC.

Se trata de una revisión del curso sobre ASP.NET Core MVC, que tan buena acogida y críticas ha tenido hasta el momento, actualizando conceptos y reflejando las novedades en APIs, funcionalidades y herramientas con las que esta segunda versión del framework promete revolucionar nuestra forma de desarrollar aplicaciones y servicios para la web.

En este post intentaremos responder a las siguientes preguntas:

¿En qué consiste el curso de ASP.NET Core 2 MVC?

Se trata de un curso impartido totalmente online a través de la prestigiosa plataforma de CampusMVP, sin horarios y a tu propio ritmo. La duración del curso está estimada en 12 semanas con una dedicación media de 7 u 8 horas semanales, y durante las cuales, dado que soy el tutor, contarás con mi asistencia para resolver tus dudas y ayudarte a lo largo de tu proceso de aprendizaje.

El temario consta de más de 150 temas que recorren exhaustivamente el framework ASP.NET Core 2.0 MVC desde su base, y que podrás descargar en formato PDF (más de 400 páginas) para utilizarlo como referencia al finalizar el curso. Además del contenido teórico, el curso está salpicado por numerosos ejercicios y prácticas propuestas que te ayudarán a asimilar mejor los conceptos principales, autoevaluaciones, recursos adicionales, aplicaciones de ejemplo, consejos y buenas prácticas, y más de tres horas de vídeo para ver en vivo el funcionamiento de algunas características.

Al finalizar el curso obtendrás un diploma que te permitirá acreditar su realización, así como incluirlo en tu perfil certificado de CampusMVP, un perfil online verificable por terceros donde puedes demostrar qué has aprendido con nosotros.

¿Cuáles son los contenidos del curso?

El curso realiza un recorrido completo por ASP.NET Core MVC partiendo desde cero. Y no podemos decir que nos andemos mucho por las ramas… Tras una breve bienvenida, empezamos presentando ASP.NET Core, describiendo su estructura, arquitectura y principios básicos de funcionamiento, y pasaremos rápidamente a la acción.

Veremos cómo se crean y configuran estos proyectos, y cómo utilizar su estructura modular para añadir componentes que aportarán funcionalidades a nuestras aplicaciones. Revisaremos muchas características que esta infraestructura proporciona a los frameworks de mayor abstracción como MVC: logging, caching, sessions, settings de aplicación, archivos estáticos, autenticación, etc.

Tras ello, nos tomaremos un pequeño respiro para presentar ASP.NET Core MVC, y volveremos al frente de batalla haciendo una pequeña incursión en el desarrollo con este framework. El objetivo en este punto es proporcionar una visión de alto nivel del funcionamiento de este tipo de aplicaciones que, sin entrar en demasiados detalles, nos permitan comprender sus principales “piezas” (modelo, vistas, controladores) y la forma de trabajar con ellas.

A continuación, recorreremos sucesivamente las capas Modelo, Controlador y Vista, detallando al máximo cómo trabajar con las herramientas que nos proporciona el framework MVC para la creación de aplicaciones web. Estudiaremos en profundidad aspectos como el sistema de routing, binders, validaciones, Razor, helpers, view components, buenas prácticas, uso de patrones…

BlogMachineCoreTambién tendremos tiempo para profundizar en el desarrollo de servicios o APIs HTTP/REST, consumibles tanto desde aplicaciones web vía Ajax como desde sistemas externos, utilizando para ello las nuevas y potentes características de negociación de contenidos y binding selectivo.

Por último, tras ver algunos otros aspectos como el uso de áreas, Razor Pages, la creación de tests o la internacionalización de aplicaciones  presentaremos BlogMachineCore, una la aplicación de ejemplo en la que podrás ver implementadas muchas características vistas en el curso, cuyos detalles puedes leer en este artículo de CampusMVP: arquitectura desacoplada, inyección de dependencias, uso de DTOs, autenticación y autorización, áreas, asincronía, URL amigables, tag helpers, Ajax, páginas de error personalizadas, etc.

Se trata de un completo temario en el que hemos intentado no dejar nada por detrás. El objetivo es que al finalizar el curso estés en disposición de utilizar ASP.NET Core MVC en tus proyectos profesionales y dar el salto definitivo a las tecnologías “Core”.

Para saber más, puedes ver la ficha completa del curso en la web de CampusMVP.

¿Es este curso para mí?

Como seguro sabrás, ASP.NET Core MVC no es la siguiente versión del framework ASP.NET MVC. Se trata de un reboot en toda regla de la tecnología, que de hecho empezó nuevo desde su versión 1.0 con la intención de convertirse en el mejor framework para el desarrollo de aplicaciones y servicios web, aportando importantes características, algunas de ellas impensables hace sólo unos meses en la pila de tecnologías de Microsoft, como el ser multiplataforma, alto rendimiento, escalabilidad, orientación a la nube, libertad de herramientas de desarrollo, etc.
La oleada de tecnologías "Core" es probablemente el cambio más drástico que hemos sufrido los desarrolladores ASP.NET desde sus inicios, allá por el año 2002.
Pero claro, para conseguirlo han tenido que romper muchas cosas, comenzando por las bases. El nuevo framework MVC corre sobre la nueva infraestructura ASP.NET Core que, de la misma forma, ha sido reescrita desde cero y replanteada para dar cabida a los nuevos requisitos.

Por tanto, si vienes de trabajar con frameworks basados en versiones de ASP.NET iguales o anteriores a 4.x, para saltar a ASP.NET Core tendrás que olvidar muchas de las cosas que ya sabes y aprender a manejar la nueva infraestructura, que ha cambiado radicalmente. Una vez teniendo controlados los cimientos, ya podrás pasar a aprender tecnologías con un nivel de abstracción superior, como el proporcionado por el nuevo framework MVC.

En MVC, a primera vista parece que han cambiado menos cosas, puesto que conceptualmente siguen manteniéndose aspectos que ya se han demostrado válidos en las versiones anteriores del framework. Seguiremos teniendo componentes como controladores, acciones, binding o filtros y continuaremos usando sintaxis Razor para escribir las vistas, pero la práctica totalidad de características ha sufrido cambios.

Aparte, hay novedades muy interesantes como view componentes o tag helpers, el nuevo enfoque para desarrollar APIs HTTP, o las derivadas de la adopción de ASP.NET Core, como el uso de variables de sesión, inyección de dependencias, la autorización o internacionalización, por citar sólo algunas.

Todos estos cambios son los que hacen que este curso sea especialmente interesante, puesto que te permitirá aprender todo lo necesario para dominar primero la nueva plataforma ASP.NET Core, y luego el framework MVC, de forma estructurada y partiendo desde cero. Por tanto, resumidamente;
  • Primero, independientemente de las tecnologías con las que hayas trabajado anteriormente, en el curso aprenderás a manejar con solvencia la nueva infraestructura ASP.NET Core.
  • Y después, dependiendo de tu punto de partida:
    • Si no has trabajado antes con MVC, aprenderás desde cero a construir aplicaciones basadas en el framework MVC sobre la infraestructura ASP.NET Core
    • Si has trabajado antes con MVC <=5, aunque te sonarán muchos conceptos, el curso también te será útil porque aprenderás:
      • Los cambios en características que ya conocías.
      • Cómo afecta la introducción de ASP.NET Core al desarrollo de aplicaciones con el framework MVC.
      • Y todas las novedades específicas del framework MVC, que no son pocas.
    • Si ya conoces algo de ASP.NET Core pero lo has aprendido “de oídas” o de forma desestructurada, quizás el curso también podría resultarte interesante porque te ofrecerá una visión ordenada y profunda de esta tecnología.
En cambio, si ya hiciste el curso de ASP.NET Core 1.x con nosotros, probablemente no te valga la pena, pues aunque hay bastantes cambios, no son realmente rompedores. En este artículo de la documentación oficial puedes leer algo sobre ellos, y ver el detalle completo en Github.

    ¿Qué conocimientos previos necesito para seguir el curso?

    Requisitos del cursoObviamente, al tratarse de un curso sobre una tecnología web, lo primero que necesitarás conocer, al menos a un nivel básico, es HTML, CSS y Javascript.

    También es importante comprender las bases del funcionamiento de las aplicaciones web, es decir, saber identificar cliente y servidor, entender las responsabilidades de cada uno de estos extremos y conocer mínimamente los mecanismos de comunicación entre ellos, básicamente el protocolo HTTP. Si has trabajado antes con alguna tecnología de desarrollo para la web (ASP, ASP.NET Web Forms, MVC, PHP, Express…), probablemente ya conozcas todo lo necesario de estos aspectos.

    También debes tener un cierto nivel de programación con .NET y C#. Por ejemplo, en el curso no vamos a explicar conceptos básicos como qué es una clase o una interfaz, ni otros aspectos del lenguaje como expresiones lambda, métodos extensores, tipos anulables, o tipos genéricos; se supone que todo eso debes conocerlo ya. Tampoco explicaremos qué es un List<T> o un StringBuilder, son piezas del framework con las que ya debes haber trabajado antes.

    Aunque no es absolutamente imprescindible para completar el curso, sí que es totalmente recomendable conocer tecnologías de acceso a datos, pues para construir aplicaciones web que jueguen con datos seguro que vas a necesitarlas. A lo largo del temario veremos algunos ejemplos de uso de Entity Framework Core, pero no profundizaremos en ellos al escapar del objetivo del curso.

    Por último, indicar que el curso comienza de cero en lo relativo al framework ASP.NET Core y MVC, por lo que en ningún momento se asume que tienes conocimientos en versiones anteriores de ASP.NET, ASP.NET Core u otras tecnologías concretas de desarrollo de aplicaciones web. Sin embargo, si las conoces tendrás algo de ventaja porque podrás "mapear" mentalmente algunos conceptos al nuevo framework y te costará mucho menos trabajo aprenderlos.

    ¿Y qué ocurre con MVC 5 y los demás frameworks basados en ASP.NET 4.x?

    ASP.NET 4.x y los frameworks que descansan sobre él, como Web Forms, MVC, Web API o SignalR, continuarán siendo soportados e incluso podrán ser evolucionados ligeramente, por lo que su vida previsiblemente aún será larga. Hay muchas aplicaciones creadas con estos marcos de trabajo que durante mucho tiempo necesitarán mejoras, ampliaciones o mantenimiento, por lo que siguen siendo opciones razonables a día de hoy e incluso en determinados escenarios pueden ser idóneos para aplicaciones nuevas.
    Oficialmente, Microsoft recomienda seguir utilizando tecnologías basadas en ASP.NET 4.x para aplicaciones existentes y ASP.NET Core 2.0 para aplicaciones nuevas.
    Sin embargo, muy probablemente no veremos nunca nuevos productos versionados como ASP.NET 5, MVC 6 o Web API 3, puesto que la apuesta de Microsoft está principalmente centrada en la familia tecnológica "Core": .NET Core, ASP.NET Core, ASP.NET Core MVC y otros frameworks que irán apareciendo. Por tanto, la adopción de estas nuevas tecnologías de forma global es sólo cuestión de tiempo.

    Y ya, sé que no es muy científico ni tiene validez para probar gran cosa, pero ahí os dejo una consulta rápida a Google Trends sobre las tendencias actuales de búsqueda sobre distintos "sabores" de ASP.NET, que os puede dar una idea del interés que está despertando el nuevo framework y por dónde pueden ir los tiros en el futuro:
    ASP.NET Core en Google Trends
    Si por cualquier motivo debes aprender ASP.NET MVC 5, recuerda que en CampusMVP también tenemos un curso apropiado para tí ;) Pero recuerda que sólo una parte de los conocimientos que adquieras serán válidos cuando saltes a ASP.NET Core MVC más adelante.

    Me convence, ¿cuándo empezamos?

    Pues cuando tú quieras :) Sólo tienes que visitar la web de CampusMVP, inscribirte, y casi sobre la marcha te facilitarán las credenciales de acceso a la plataforma.

    Y bueno, pues creo que con esto quedarán despejadas todas vuestras dudas sobre el curso, o al menos muchas de ellas. Si no es así, no dudéis en contactar con CampusMVP, donde seguro os atenderán de maravilla, o conmigo a través de un comentario, correo electrónico, o el formulario de contacto del blog.

    ¡Espero veros a muchos por allí!

    Publicado en Variable not found.

    » Leer más, comentarios, etc...

    Poesía Binaria

    Bash: ¿Cómo ejecutar código antes y después de cada comando? Logging, monitorización, notificaciones y mucho más

    septiembre 18, 2017 07:46

    Ejecutar código antes y después de cada comando

    Con el fin de hacer nuestro sistema (o servidor) más flexible. Aunque podemos hacer muchas cosas con estas técnicas. Es de gran utilidad poder ejecutar un script justo antes de la ejecución de cualquier orden de Bash y que, justo cuando esta orden termine, podamos ejecutar cualquier otra cosa. Esto nos abre las puertas, por ejemplo a notificaciones de inicio/finalización, logging, monitorización y muchas más posibilidades que veremos en futuros posts. Cosas que podremos hacer tanto en el ámbito local, como en servidores de forma muy sencilla.

    Aunque otros intérpretes, como ksh, tcsh, zsh y algunos más tienen facilidades para estas tareas. Vamos a hacerlo en bash, ¡porque somos unos valientes! Porque se instala por defecto con muchas distribuciones y porque es el que me he encontrado con más frecuencia.

    Ejecutar algo cuando termina un comando

    Cuando un comando se ha ejecutado y Bash nos vuelve a pedir una nueva orden, justo antes de darnos el control, y para mostrar el prompt (ese texto que suele ser usuario@host /directorio/actual y termina en un dólar o una almohadilla), Bash, ejecutará lo que contenga la variable PROMPT_COMMAND, ya sea una función, un alias, o la ejecución de un programa. Vamos a hacer una prueba:

    PROMPT_COMMAND=”date”
    lun sep 18 10:23:47 CEST 2017
    pwd
    /home/gaspy
    lun sep 18 10:24:47 CEST 2017

    Incluso si sólo pulsamos enter, como se vuelve a generar el prompt, justo antes nos mostrará la fecha (porque el comando a ejecutar es date)

    Como ejemplo práctico, vamos a hacer un pequeño script que nos notificará cuando alguna partición de nuestro disco duro llegue a un punto crítico, en este caso, vamos a revisar todas las particiones de /dev/sdb y avisar al usuario cuando alguna de estas sobrepase el 90% (opciones que podremos configurar luego), para ello, vamos a crear un script llamado critp.sh con lo siguiente:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    #!/bin/sh

    PARTITION_PATTERN="/dev/sdb*"
    WARNING_PERCENT=90
    __RED='\033[1;31m'
    __YELLOW='\033[1;33m'
    __NC='\033[0m' # Color end

    function space_notifier()
    {
            local OLDIFS=$IFS
            IFS=$'\n'
            local STATUS=($(df -h $PARTITION_PATTERN | awk 'NR > 1'))
            IFS=$' \n\t\r'
            for st in "${STATUS[@]}"; do
                    local _ST=($st)
                    local _PERCENT=$(echo ${_ST[4]}| tr -d '%')
                    if (("$_PERCENT">"$WARNING_PERCENT")); then
                            echo -e $__RED"El dispositivo "$__YELLOW"${_ST[0]}"$__RED" está al ${_ST[4]}$
                    fi
            done 2>/dev/null
            IFS=$OLDIFS
    }

    PROMPT_COMMAND="
    space_notifier"

    Y lo cargaremos de la siguiente manera:

    source critp.sh

    Con esto, cada vez que pulsemos enter o finalice un comando, veremos el conjunto de dispositivos que ha alcanzado ese porcentaje de espacio:
    Ejemplo de salida de terminal

    Una buena idea sería también notificar sólo a veces, tal vez con una probabilidad del 50%. Para que no siempre esté calculando ni nos sintamos abrumados por los mensajes de notificación. Para ello, podemos dejar el script así:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    #!/bin/sh

    PARTITION_PATTERN="/dev/sdb*"
    WARNING_PERCENT=90
    PROBABILITY=50
    __RED='\033[1;31m'
    __YELLOW='\033[1;33m'
    __NC='\033[0m' # Color end

    function space_notifier()
    {
            if (($(($RANDOM%100))<$PROBABILITY)); then
                    return
            fi
            local OLDIFS=$IFS
            IFS=$'\n'
            local STATUS=($(df -h $PARTITION_PATTERN | awk 'NR > 1'))
            IFS=$' \n\t\r'
            for st in "${STATUS[@]}"; do
                    local _ST=($st)
                    local _PERCENT=$(echo ${_ST[4]}| tr -d '%')
                    if (("$_PERCENT">"$WARNING_PERCENT")); then
                            echo -e $__RED"El dispositivo "$__YELLOW"${_ST[0]}"$__RED" está al ${_ST[4]}$
                    fi
            done 2>/dev/null
            IFS=$OLDIFS
    }

    PROMPT_COMMAND="
    space_notifier"

    Ya tenemos lo básico. Aunque estaría muy bien conocer información sobre el comando que se acaba de ejecutar y que, por supuesto, tenemos a nuestra disposición como es el propio comando ejecutado y su estado de salida. Más adelante podremos saber si se ha ejecutado realmente un comando, porque claro, cuando pulsas enter, vuelve a llamarse a la función. Ahora mismo, está muy bien si queremos realizar tareas de mantenimiento, notificar al usuario si está mal de espacio en disco, etc. Como el siguiente ejemplo que servirá para notificar al usuario cuando termine una determinada tarea. Suponemos que estamos en un entorno gráfico, así que avisaremos con zenity:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    #!/bin/bash

    function notify() {
            NOTIFY=1
    }

    function pcom() {
            local _PIPESTATUS=( ${PIPESTATUS[@]} )
            if [ -n "$NOTIFY" ]; then
                    local OLDIFS=$IFS
                    IFS=' '
                    local STATUS="${_PIPESTATUS[@]}"
                    IFS=$'\n'
                    local HIST=($(HISTTIMEFORMAT= history 1 | awk '{ print $1"\n"substr($0, index($0,$2)) }'))
                    IFS=$OLDIFS
                    zenity --info --text "$(echo -e "COMANDO FINALIZADO: ${HIST[1]}\n ESTADO: $STATUS")"
            fi
            unset NOTIFY
    }

    PROMPT_COMMAND="pcom"

    Ahora, cargaremos el archivo con source. Y podremos ejecutar cualquier comando con normalidad, aunque, cuando vayamos a hacer algo que vaya para largo podremos hacer lo siguiente:

    notify; sleep 100

    De esta forma, cuando termine la ejecución de sleep 100, o de cualquier otra cosa que creamos que va a tardar mucho, recibiremos un mensaje parecido a este:
    Diálogo GUI provocado por PROMPT_COMMAND
    Este script tiene algunas cosas curiosas. Para hallar el estado de la ejecución de las órdenes, utilizo PIPESTATUS. Esta variable, si el comando es sencillo, devolverá sólo un valor, haciendo lo mismo que $?. Pero cuando hemos llamado varios comandos unidos por una pipe o tubería, nos va a devolver el estado de todos los comandos ejecutados. Eso sí, como PIPESTATUS se actualiza tras cada cosa que ejecutamos, lo primero que haremos nada más entrar en la función será guardar su valor en otra variable, y así nos aseguramos de no perderlo.

    Y esto está muy bien cuando estamos haciendo varias cosas y se nos puede ir la cabeza con una tarea. Nos puede servir para cuando estemos creando o restaurando un dump de base de datos, cuando estemos haciendo una copia de archivos (local o remota), o estemos realizando alguna tarea de cálculo intenso. Tan solo tenemos que llamar antes de la tarea en cuestión a notify y ya se encargará él de todo. Podemos extender la funcionalidad de notify, incluso aprovechar lo que voy a contar a continuación para hacerlo más potente (te adelanto que en unos días publicaré un script mucho más completo).

    Ejecutar algo justo antes de llamar al comando

    Para tener control total sobre los comandos ejecutados es interesante poder ejecutar código antes de la ejecución de los comandos. Esto, principalmente nos ayuda a depurar nuestros scripts, porque sabremos en qué orden se ejecuta cada cosa, pero también puede ser una buena herramienta de control, gran ayuda para hacer una visualización interactiva de comandos o, para saber lo que ejecutan nuestras visitas o los comandos que se ejecutan en un servidor además de muchas otras utilidades.

    Como ejemplo, empezaremos con algo básico, simplemente haciendo echo antes y después de una llamada. Y, para dar un valor añadido, ya que somos capaces de ejecutar algo antes de hacer cualquier llamada, podremos detectar cuando simplemente se pulsa enter. Ya que, cuando pulsamos enter, la primera función, precmd, no se va a ejecutar:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    #!/bin/bash
    function postcmd() {
            if [ "${#COMANDOS[@]}" -gt 1 ]; then
                    HISTORY=$(HISTTIMEFORMAT= history 1)
                    echo "COMANDO EJECUTADO: "${HISTORY:7}
            fi;
            COMANDOS=();
    }

    function precmd() {
            COMANDOS+=("$BASH_COMMAND");
            if [[ "$BASH_COMMAND" != "$PROMPT_COMMAND" ]]; then
                    echo "INICIA EJECUCIÓN: "$BASH_COMMAND
            fi
    }

    PROMPT_COMMAND="postcmd"

    trap 'precmd' debug

    Trabajando un poco en este código podremos ser capaces, por ejemplo de guardar un log de todo lo que los usuarios ejecutan, simplemente introduciendo:

    1
    logger $BASH_COMMAND

    En lugar del echo “INICIA EJECUCION”. Así podemos hacer que el sistema sea totalmente transparente al usuario. Además, para completar el registro podremos incluir otra función:

    1
    2
    3
    4
    5
    function errcmd() {
            echo "ERROR EJECUTANDO: $BASH_COMMAND en linea $LINENO Codigo de error: $?"
    }

    trap 'errcmd' err

    Que vinculamos a cualquier error. Es decir, cancelación de ejecución o que el código de salida de lo que sea que hayamos llamado no sea 0. Por supuesto, podríamos ayudarnos de más traps como int (para Control+C), quit (para Control+\), term (para cuando se mata el proceso), etc.

    Bueno, y, hagamos algo interesante como evitar que el usuario ejecute cosas que no queremos que ejecute:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    shopt -s extdebug

    function denyaccess () {
            if [ "$BASH_COMMAND" = "$PROMPT_COMMAND" ]; then
                    return
            fi
            local this_command=`HISTTIMEFORMAT= history 1 | sed -e "s/^[ ]*[0-9]*[ $

            if [ "
    shopt -u extdebug" == "$this_command" ]; then
                    return 0
            elif [[ "
    $this_command" =~ ping ]]; then
                    return 0
            fi
            echo "
    ACCESO DENEGADO para: $this_command"
            return 1
    }

    trap 'denyaccess' DEBUG

    En este caso, haremos return 0, sólo cuando ejecutemos shopt -u extdebug y el comando ping. Mostrando el texto “ACCESO DENEGADO” si queremos ejecutar cualquier otra cosa. Esto en la práctica no vale de nada, tendríamos que introducir muchas restricciones o tal vez denegar (return 1) ciertas palabras, manteniendo el return 0 por defecto. Pero nos da una idea de cómo funciona.
    Gracias a la opción extdebug, esta función es capaz de denegar la ejecución de la orden que hemos llamado en función del valor de retorno de la función denyaccess().
    En este caso, el argumento -s de shopt, activa (set) esta capacidad. Del mismo modo, cuando utilizamos -u, la desactiva, y por eso esa orden está permitida, porque es un programa de ejemplo.

    Para todo lo demás, incluso nos podemos servir de expresiones regulares para Bash, o incluso la llamada a una función.

    Y, para terminar, vamos a hacer sustituciones de órdenes dentro de un comando. O lo que es lo mismo, cambiar el comando que vamos a ejecutar. Podríamos afectar a cualquier parte de la orden ejecutada, aunque para hacer un sencillo ejemplo, vamos a cambiar las llamadas a sudo por gksudo, así vemos un bonito entorno gráfico y será más llevadero para el usuario:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    #!/bin/bash
    shopt -s extdebug

    function changesudo() {
            if [ "$BASH_COMMAND" = "$PROMPT_COMMAND" ]; then
                    return
            fi
            local this_command=`HISTTIMEFORMAT= history 1 | sed -e "s/^[ ]*[0-9]*[ ]*/$

            if [ "
    shopt -u extdebug" == "$this_command" ]; then
                    return 0
            fi
            eval ${this_command/sudo/gksudo}
            return 1
    }

    trap 'changesudo' DEBUG

    Incorporar a nivel de sistema estos scripts

    Los scripts que hagamos utilizando estas técnicas podemos cargarlos a nivel de usuario, introduciendo:

    1
    source script.sh

    en el archivo ~/.bashrc o a nivel de sistema añadiendo esa línea en /etc/profile , /etc/bash.bashrc , o, mejor aún, en un nuevo archivo dentro de /etc/profile.d/

    En las próximas semanas quiero publicar algunos ejemplos en los que veremos, aún más, la potencia de estas acciones.

    Andrew Worley

    The post Bash: ¿Cómo ejecutar código antes y después de cada comando? Logging, monitorización, notificaciones y mucho más appeared first on Poesía Binaria.

    » Leer más, comentarios, etc...

    Variable not found

    De vuelta & Enlaces interesantes 292

    septiembre 18, 2017 06:55

    Enlaces interesantesPues sí, amigos, ¡ya estamos de vuelta! Como el amigo Gustavo se encarga de recordarme todos los años, el regreso se estaba retrasando demasiado. Y no por falta de ganas, sino porque andaba enfrascado en un proyecto del que pronto tendréis noticia :)

    Pero bueno, para empezar la temporada con buen pie, nada como unos cuantos enlaces, que, como siempre, espero que os resulten interesantes. :-)

    .NET/.NET Core

    ASP.NET/ASP.NET Core

    Azure / Cloud

    Conceptos/Patrones/Buenas prácticas

    Data

    Html/Css/Javascript

    Visual Studio/Complementos/Herramientas

    Publicado en Variable not found

    » Leer más, comentarios, etc...

    proyectos Ágiles

    Impacto de Agile en los Modelos Organizativos Tradicionales

    septiembre 18, 2017 05:31

    A continuación se muestran algunas diapositivas de la presentación “Impacto de Agile en los Modelos Organizativos Tradicionales” que tuvo lugar en la Conferencia ITSMF Agile 2017, donde se compara el modelo tradicional organizativo (departamentos / funciones) y de gestión (Project Management) con el Agile (equipos autónomos orientados a iniciativas estratégicas) y cómo empezar la transformación.

    slideshare La presentación completa en español (incluyendo las últimas actualizaciones) se puede encontrar en Slideshare:  aquí.

     

    Diapositiva4

    Diapositiva6

    Diapositiva7

    Diapositiva8

    Diapositiva9

    Diapositiva10

    Diapositiva12

    Diapositiva13

    Diapositiva14

    Diapositiva15

    Diapositiva16

    Diapositiva17

    Diapositiva18

     

    slideshare La presentación completa en español (incluyendo las últimas actualizaciones) se puede encontrar en Slideshare:  aquí.

    Presentaciones relacionadas:

     Artículos relacionados:


    » Leer más, comentarios, etc...

    Koalite

    “Primitive Obsession” Obsession

    septiembre 18, 2017 05:06

    Hace poco escribía Jorge Sánchez un interesante artículo sobre code smells que ha ido encontrado al aplicar Clean Architecture. En él menciona en un par de ocasiones la obsesión por los tipos primitivos como un factor que nos puede impedir aplicar correctamente Clean Architecture. Esto es algo que he tocado en el blog de pasada en alguna ocasión, como cuando veíamos cómo crear modelos de dominio más ricos evitando usar tipos enumerados.

    Pese a que esto del primitive obsession es algo muy manido, quiero aprovechar para darle una vuelta al tema y ver si realmente es tan util evitar tipos primitivos em>spoiler: sí, es muy útil) y qué contrapartidas hay que asumir para evitar primitive obsession.

    Qué aporta evitar primitive obsession

    La obsesión por los tipos primitivos consiste en utilizar datos de tipos básicos para modelar conceptos de nuestra aplicación. Vamos, que en lugar de tener un tipo Age para representar la edad de una persona, utilizamos un tipo int. O que andamos moviendo un par de objetos DateTime por todas partes llamados Start y End, en lugar de tener un objeto DateRange.

    Cuando creamos tipos específicos para representar conceptos de la aplicación ganamos varias cosas.

    Si estamos trabajando con un lenguaje estático y tenemos tipos distintos para representar conceptos distintos, podemos aprovecharnos del compilador y asegurarnos de que estamos pasando los datos adecuados de un método a otro.

    Así, evitaríamos que un método que trabaja con edades de usuarios recibiera por error su Id (de tipo int) en lugar de su Age (de tipo Age). De hecho, podríamos recurrir a técnicas como tipos fantasmas para diferenciar entre distintos tipos estructuralmente idénticos pero con distinto valor semántico de forma fácil.

    Incluso con un lenguaje dinámico donde perdemos esta ayuda del compilador, tener tipos más específicos para representar cada concepto simplifica el mantenimiento de invariantes. Podemos asegurar que al construir un valor de tipo Age contiene una edad válida (es decir, mayor o igual que cero), que al construir un DateRange siempre se cumple que la fecha de inicio no es posterior a la fecha de fin, o que no sumamos dos valores Money que tienen un Currency distinto sin hacer una conversión previa.

    En los lenguajes orientados a objetos también nos pueden servir para asociar lógica a estos tipos, aumentando la cohesión del diseño. Podríamos incluir en nuestro tipo DateRange un método IsContainedIn(DateRange other) que nos indique si un rango de fechas está contenido dentro de otro.

    Todos estos factores hacen que reducir el uso de tipos primitivos en favor de tipos más específicos sea algo bastante recomendable si queremos tener poder modelar nuestra aplicación de una forma más expresiva y sólida, ya que será mucho más fácil saber qué representa cada valor y qué invariantes cumple.

    Sin embargo, no todo son ventajas (si no, no estaría escribiendo esto) y hay que tener en cuenta las implicaciones que tienen.

    Qué problemas puede conllevar

    Utilizar tipos primitivos en lugar de contruir tipos más específicos puede achacarse a una cuestión de pereza. Es verdad. Sobre todo en los lenguajes más habituales (C#, Java y amigos), definir nuevos tipos es una tarea pesada, y hacerlo bien, con su Equals, GetHashCode, inmutabilidad si es necesaria, etc., aún más. Si tienes la suerte de trabajar con otro tipo de lenguajes (Haskell, Scala y compañía), es mucho más cómodo. Pese a todo, no creo que esto sea un problema reseñable. Si es un concepto que vas a estar manejando con frecuencia en tu aplicación, es fácil amortizar el tiempo invertido en modelarlo bien.

    El principal problema que pueden introducir los nuevos tipos creados es un incremento del acoplamiento entre distintas partes del sistema. Especialmente si el sistema está compuesto por distintos módulos relativamente independientes.

    Si yo tenía un API para incrementar el saldo de una cuenta que recibía un decimal, algo así como void IncreaseBalance(decimal amount) podía utilizarlo desde cualquier módulo que fuera capaz de crear valores de tipo decimal que, por definición de tipo primitivo, es cualquier módulo.

    Al cambiar ese API para utilizar tipos más descriptivos y pasar a void IncreaseBalance(Money money), los consumidores de ese API ya no dependen sólo del componente que la expone, sino también de un tipo adicional, el tipo Money.

    ¿Cómo de grave es esto? Pues depende, claro. Si los tipos que se están creando son ubícuos y estables, no parece muy grave. Habremos introducido un acoplamiento indirecto entre todos los módulos que usan esos tipos, pero probablemente sea llevadero.

    Lo malo es cuando los tipos ya no son tan universales, cosa que es lo habitual. Quizá el tipo Money de una aplicación no sea idéntico al de otra. O el concepto de Name no sea idéntico en dos partes de una misma aplicación, porque una requiere que los nombres no sean vacíos, y otra que además de no ser vacíos sólo sean caracteres alfanuméricos. En este caso se hace necesario tener varios tipos diferentes y se complica la integración entre distintos componentes porque hace falta realizar conversiones de unos tipos a otros.

    Esto ocurre también cuando queremos aprovechar las librerías estándar del lenguaje que estemos empleando. Estas librerías trabajan con tipos primitivos, pero no con nuestros tipos específicos. Si tengo un tipo Name, no puedo usar directamente las funciones típicas de string, como ToUpper, ToLower, Trim, etc. Tampoco puedo sumar valores de Weight a menos que redefina manualmente el operador + o cree una función específica para ello.

    Un ejemplo habitual de este problema lo podemos ver en Javascript. Cuando en lugar de usar un tipo primitivo como es un Array se utiliza una colección específica como un NodeList, no podemos usar los métodos típicos de array y necesitamos andar convirtiendo nuestro tipo específico a un array primitivo usando Array.prototype.slice.call(...) o el más moderno Array.from(...) para poder emplear los forEach, map o filter de turno.

    Dependiendo del lenguaje y las abstracciones que soporte, esto puede mitigarse más o menos haciendo que los tipos especificos que hemos creado implementen interfaces concretos, como IComparable o IEnumerable en C#, protocolos como ISeq en clojure o type classes como Ord en Haskell.

    También hay que tener cuidado en la granularidad que vamos a manejar o podemos caer en el típico caso de “yo solo quería un plátano, y me han dado la selva entera con un gorila sosteniendo el plátano”.

    En su post, Jorge pone un ejemplo en el que para saber si un usuario puede grabar un vídeo o no, en lugar de devolver un valor de tipo bool que nos lo indique, propone devolver una entidad AccountPlan que es la que contiene esa información.

    Esto seguramente sea perfectamente válido, pero hay que tener en cuenta que ahora estamos exponiendo mucha más información de la realmente necesaria hacia el exterior, acoplando los consumidores del API al tipo AccountPlan y dificultándonos futuros cambios en el modelo si, por ejemplo, necesitamos que la decisión ya no dependa sólo del AccountPlan o incluso si queremos cambiar nuestro modelo para que AccountPlan deje de existir.

    Resumen

    Para modelar un problema es bueno aprovechar las herramientas que tenemos a nuestro alcance y el sistema de tipos es una de las más poderosas. Por ello, introducir abstracciones en lugar de usar tipos primitivos nos puede ayudar a construir modelos más expresivos y sólidos, en los que es más fácil entender la intención de cada valor que estamos manejando y asegurar que los invariantes se cumplen.

    Sin embargo, la introducción de nuestros tipos puede aumentar el acoplamiento entre diferentes partes del sistema, dificultar la interactuación entre ellas o con las librerías estándar, y penalizar la ocultación de información, haciendo que seas más complicado evolucionar el sistema.

    El contexto, como siempre, es clave para decidir cuándo nos conviene introducir un tipo específico en lugar de utilizar un tipo primitivo. Como regla general, yo diría que cuanto más simple e independiente del contexto es el tipo que vamos a introducir, más nos beneficiaremos de introducirlo, porque debería ser más estable en el tiempo y más reutilizable, lo que compensa el acoplamiento y el posible esfuerzo de añadirle el comportamiento que no podemos aprovechar de librerías estándar.

    No hay posts relacionados.

    » Leer más, comentarios, etc...

    Blog Bitix

    Cambiar y personalizar los estilos de la documentación Javadoc

    septiembre 16, 2017 08:00

    Java

    La documentación generada por la herramienta Javadoc se puede adaptar a las necesidades propias ya que tiene mecanismos para extenderla. Crear un taglet propio es una forma pero también se pueden modificar los estilos aplicados al HTML generado, por ejemplo, para adaptar el Javadoc a los colores de la organización o para incluir un logotipo en la cabecera o un texto en el pie de página.

    El comando javadoc con el que se genera la documentación posee opciones para modificar la cabecera, el pie de página o usar una hoja de estilos propia. Estas opciones son -header, -footer y -stylesheetfile. Un punto de partida para modificar los estilos es usar la hoja de estilos generada por defecto, realizar en ella las modificaciones oportunas y finalmente indicar que se use con el opción -stylesheetfile.

    En el caso de este ejemplo en vez de usar el color azul por defecto he modificado la hoja de estilos para que sea uno verde. En los cambios he partido de la hoja de estilos original del javadoc e inspeccionando los elementos con las herramientas del navegador para sustituir sus colores por otros de una paleta de colores basada en el verde en la que cambia la tonalidad de color hacia más claro. También se puede modificar la cabecera y el pie de página para incluir una nota con un mensaje de derechos de autor o un enlace y añadir nuevos estilos para el taglet todo.

    La tarea de Gradle para generar la documentación Javadoc posee opciones equivalentes al comando javadoc, unas de ellas como ejemplo son footer y bottom que añaden un texto en en el pie de página de cada página.

    Estilos personalizados de la documentación Javadoc
    Estilos por defecto de la documentación Javadoc

    El comando javadoc tiene una buena cantidad de opciones, con la opción -help se obtiene una listado y descripción de cada una de ellas. Dependiendo del destinatario de la documentación, se pueden limitar las clases que son incluidas en el Javadoc si por ejemplo solo se quiere ofrecer la documentación de la parte pública.

    El código fuente completo del ejemplo puedes descargarlo del repositorio de ejemplos de Blog Bitix alojado en GitHub y probarlo en tu equipo ejecutando el comando ./gradlew javadoc.

    » Leer más, comentarios, etc...

    Blog Bitix

    Crear anotaciones de Javadoc personalizadas con taglets

    septiembre 16, 2017 08:00

    Java

    La herramienta de documentación es Javadoc de Java permite a partir del código fuente de un programa o librería generar un conjunto de documentos en formato HTML enlazados entre si consultables con un navegador web y accesibles desde internet si son accesibles con un servidor web. La documentación se genera a partir de las clases y métodos del código fuente y también a partir de los comentarios de las clases y métodos.

    En los comentarios se pueden incluir anotaciones que enriquecen la documentación, por ejemplo, para indicar el autor o en qué versión se incluyó una clase o método, incluir enlaces, … en el propio JDK ya se incluye un amplio conjunto completo de anotaciones. Pero además de usar las anotaciones ya incorporados por defecto en la herramienta también es posible añadir nuevos propios, escribiendo un taglet. Con la API de los taglets basta implementar una clase que implemente la interfaz Taglet. La interfaz Taglet de Java 9 ha sido modificada ligeramente pero en esencia proporciona la misma información, en vez de un método para indicar si es posible el taglet en una localización hay un único método que devuelve un Set con todas las posibles localizaciones, en vez de necesitar un método register hay un método init y un único método para generar el contenido, toString.

    La clase tiene varios métodos uno que indica el nombre único del taglet que identificará la anotación en los comentarios de Javadoc, varios métodos para indicar en que localizaciones es usable y dos métodos que generan el contenido a incluir en el HTML resultante. Las clases Tag que recibe el método Taglet.toString() o ParamTag permite obtener diversa información utilizable para generar el contenido apropiado.

    Los taglets pueden ser de tipo bloque con su propia entidad o ser embebidos en linea en un comentario del javadoc. En ejemplo de taglet de bloque siguiente consiste en permitir incluir elementos que quedan por hacer en el código, una anotación todo. Con esta anotación el desarrollador incluye un comentario descriptivo de cuales son las cosas pendientes para un futuro. El código del taglet sería el siguiente.

    }

    Una vez escrito el código fuente del taglet hay que compilarlo e indicar su ubicación al generar la documentación con la herramienta javadoc. Hay que indicar varias opciones (tagletPath y taglets) que también se usarían como parámetros empleando directamente la herramienta javadoc, los comandos serían los siguientes usando Gradle. También hay que incluir de forma explícita como dependencia la librería tools.jar ubicado en el JDK.

    }

    Contenido del taglet todo en el javadoc

    El código fuente completo del ejemplo puedes descargarlo del repositorio de ejemplos de Blog Bitix alojado en GitHub y probarlo en tu equipo ejecutando el comando ./gradlew javadoc.

    » Leer más, comentarios, etc...

    Adrianistán

    Leer de teclado en Rust

    septiembre 15, 2017 11:15

    En muchas aplicaciones es necesario leer datos de teclado. Si bien esto podría considerarse sencillo en lenguaje como Python o Ruby, lo cierto es que sí se quiere hacer bien es complicado. Funciones como scanf de C son consideradas inseguras y en Java, hasta la llegada de la clase java.util.Scanner era un dolor de cabeza. En Rust no es distinto, es por ello que muchos tutoriales de Rust obvian esta parte. No obstante, leer de teclado no es tan difícil, como veremos a continuación.

    read_line

    El método principal para leer de teclado es read_line, que nos lee una línea como String. Para acceder a read_line primero necesitamos tener on objeto stdin. La manera más fácil de hacerlo es usar el módulo std::io.

    El procedimiento es el siguiente, en primer lugar creamos una variable de tipo String vacía y mutable donde se va a alojar el resultado, posteriormente leemos y tratamos el resultado.

    use std::io;
    
    fn main() {
        println!("Dime tu nombre: ");
        let mut input = String::new();
        io::stdin().read_line(&mut input);
        println!("Tu nombre es {}",input.trim());
    }
    

    Como vemos, al leer la línea también se nos guarda el salto de línea. Si queremos quitarlo podemos usar trim.

    Este código sin embargo generará una advertencia por el compilador y es que read_line genera devuelve un valor, concretamente un Result, que como vimos, sirven para el manejo de errores en Rust. Si no queremos tratar este Result con especial interés, podemos usar ok y opcionalmente especificar un mensaje de error con expect.

    use std::io;
    
    fn main() {
        println!("Dime tu nombre: ");
        let mut input = String::new();
        io::stdin().read_line(&mut input).ok().expect("Error al leer de teclado");
        println!("Tu nombre es {}",input.trim());
    }
    

    Si quieres tratar el error mejor puedes, pero read_line no suele fallar.

    Leyendo enteros

    Hasta aquí todo sencillo, porque leíamos String, en lo que entra todo lo que el usuario puede meter. Pero, ¿y si queremos leer un número de teclado? La cosa se complica. Normalmente se lee de teclado como String y luego se intenta pasar a número. Veamos como.

    use std::io;
    use std::str::FromStr;
    
    fn main() {
        println!("Dime tu edad: ");
        let mut input = String::new();
        io::stdin().read_line(&mut input).ok().expect("Error al leer de teclado");
        let edad: u32 = u32::from_str(&input.trim()).unwrap();
        let frase = if edad >= 18 {
            "Mayor de edad"
        }else{
            "Menor de edad"
        };
        println!("{}",frase);
    }
    

    Como vemos, hay que importar std::str::FromStr para tener disponible las operaciones from_str en los tipos elementales. También se observa que hemos hecho un unwrap, porque from_str devuelve un Result. Este error sin embargo conviene que lo tratemos con más cuidado, pues es bastante probable que salte.

    Un ejemplo ideal

    En este código vamos a ver como pedir un entero, asegurándonos de que el usuario realmente introduce un entero e insistiendo hasta que finalmente introduce un entero válido.

    use std::io;
    use std::io::Write;
    use std::str::FromStr;
    use std::num::ParseIntError;
    
    fn read_input() -> Result<u32,ParseIntError> {
        print!("Dime tu edad: ");
        io::stdout().flush().ok();
        let mut input = String::new();
        io::stdin().read_line(&mut input).ok().expect("Error al leer de teclado");
        let input = input.trim();
        let edad: u32 = u32::from_str(&input)?;
        Ok(edad)
    }
    
    fn main() {
        let edad;
        loop {
            if let Ok(e) = read_input(){
                edad = e;
                break;
            }else{
                println!("Introduce un número, por favor");
            }
        }
        let frase = if edad >= 18 {
            "Mayor de edad"
        }else{
            "Menor de edad"
        };
        println!("{}",frase);
    }
    

    He decidido separar la parte de pedir el número a otra función que devuelve Result para así poder usar el operador ?. También he usado print! y io::stdout().flush() en vez de println! para que tanto el mensaje como la entrada se realice en la misma línea y quede más bonito.

     

    La entrada Leer de teclado en Rust aparece primero en Blog - Adrianistan.eu.

    » Leer más, comentarios, etc...

    xailer.info

    Lanzamiento de Xailer 5.0.2

    septiembre 14, 2017 08:53

    Estimados usuarios de Xailer,

    Hoy publicamos una nueva actualización de Xailer, la versión 5.0.2 que básicamente corrige todos los errores que nos habéis encontrado, aunque también incluye alguna mejora y un buen ejemplo de creación de miniaturas que esperamos os guste.

    Más información en los siguientes enlaces:

    https://www.xailer.com/?lonuevo
    http://www2.xailer.com/download/?es&file=1

    Un cordial saludo
    [El equipo de Xailer]

    » Leer más, comentarios, etc...

    Blog Bitix

    Cómo generar la documentación Javadoc con Gradle

    septiembre 14, 2017 08:00

    Java

    Habiendo explicado que es la herramienta Javadoc, y en otros artículos como crear taglets para incluirlos en los comentarios y generar contenido con los mecanismos de extensión que ofrece y explicado como cambiar los estilos que se usan por defecto en el Javadoc para por ejemplo cambiar los colores según la organización o incluir un texto de derechos de autor en el pie de página, hay que generar la documentación javadoc usando la herramienta de construcción que usemos. En este artículo muestro como generar la documentación javadoc con Gradle.

    Lo primero es incluir en el proyecto el plugin de java. Con el plugin incluido se añade una tarea con la que generar la documentación javadoc.

    Para indicar en Gradle las opciones del comando javadoc hay que ver cuales son en las clases MinimalJavadocOptions, CoreJavadocOptions y StandardJavadocDocletOptions. Las opciones permiten indicar la clase de taglet propio y lo mismo para usar una hoja de estilos propia que puede estar basada pero con pequeñas modificaciones sobre la que usa javadoc por defecto, también para incluir un texto en cada página en el pie. Lo mismo sería para usar cualquiera de las otras opciones que tiene el comando javadoc. En el ejemplo se usan dos opciones para la codificación de caracteres.

    Para generar un artefacto con la documentación comprimida en un archivo zip hay que incluir la siguiente configuración en el archivo build.gradle.

    Con Gradle la documentación Javadoc se genera en el directorio build/xxx y el artefacto en el directorio build/xxx. Con las opciones anteriores este es el resultado del HTML generado.

    Artefacto generado con la documentación javadoc

    El código fuente completo del ejemplo puedes descargarlo del repositorio de ejemplos de Blog Bitix alojado en GitHub y probarlo en tu equipo ejecutando el comando ./gradlew build.

    » Leer más, comentarios, etc...

    Poesía Binaria

    Lanzando servidores TCP/IP en puertos libres aleatorios haciendo bind al puerto 0

    septiembre 13, 2017 06:23

    Servidores TCP en el puerto 0
    ¿Cómo? ¡Que los puertos TCP empiezan en el 1! Aunque algo sospechaba, porque en informática, el cero siempre ha valido para algo. Por eso se dice que los humanos empiezan a contar desde el uno y los informáticos desde el cero. Con esta cara me quedé cuando vi que puedes lanzar un servidor y que éste escuche en el puerto 0. Pero si el 0 no es un puerto válido, ¿para qué vale?

    Cuando lanzamos un servidor y lo asociamos al puerto 0, el sistema escogerá un puerto aleatorio y libre con permiso para crear un servidor (por ejemplo los puertos menores al 1024 requieren de privilegios de administración o root). Así que nuestro servidor escuchará donde primero pille. Además, esto funciona tanto en sistemas Unix como en Windows, no tiene pérdida.
    ¿Y qué sentido tiene escuchar en un puerto aleatorio?¿Cómo sabrán nuestros usuarios en qué puerto acceder a los servicios? De primeras a mí se me ocurren ciertas cosas:

    • Para las prácticas de redes, o más bien para cuando estás programando un servidor. Si alguno de vosotros está haciendo prácticas de redes, éste es un buen consejo. Yo recuerdo hace muchos años que tenía que crear un servidor TCP con unas ciertas características y, claro, creas un programa, compilas, ejecutas, y… algo no va bien, lo cierras, cambias algo muy rápidamente, compilas, ejecutas y… ¡el puerto está en uso! Tenía que esperar al sistema operativo para que se diera cuenta de que el puerto no lo iba a utilizar más y lo cerrara. Entonces, si cada vez que lanzo el servidor lo lanzo en un puerto diferente… ¡problema solucionado! Que el SO cierre puertos cuando tenga tiempo. Otra forma de hacerlo sería poder especificar el puerto en el que asociamos el servidor cada vez que lo ejecutamos.
    • Lanzamiento de servidores de prueba. Por ejemplo, voy a abrir un servidor web temporal para que un compañero de piso/de trabajo/amigo o yo mismo desde otro ordenador me pueda descargar un fichero o enviarme un mensaje. Podemos acordar un puerto, por ejemplo el 40563, pero seguro que si tienes muchas cosas en el ordenador tirando de Internet, está en uso.
    • Como en el punto anterior… en lugar de un servidor web, puede ser un servicio de backup, un FTP, un servicio de correo…
    • Lanzar servicios experimentales en las máquinas de producción pruebas. Y así evitamos visitantes no deseados, y calentamientos de cabeza por puertos bloqueados.
    • Tener un servidor en un puerto localizado para la conexión de control, y crear un servidor por un puerto aleatorio para enviar o recibir datos (o cualquier cosa), por la conexión de control comunicar el puerto del otro servidor y empezar a transmitir por el otro lado. Algo parecido al FTP…
    • Más sugerencias en los comentarios!!

    Aquí traigo un pequeño programa hecho en Python que nos devuelve un puerto libre y aleatorio, extraído de: The port 0 trick..

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    import socket;

    def findFreePorts(howMany=1):
        """Return a list of n free port numbers on localhost"""
        results = []
        sockets = []
        for x in range(howMany):
            s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            s.bind(('localhost', 0))
            # work out what the actual port number it's bound to is
            addr, port = s.getsockname()
            results.append(port)
            sockets.append(s)

        for s in sockets:
            s.close()

        return results

    print (findFreePorts(10));

    Y podemos montar una prueba rápida con netcat.

    nc -l 0

    Luego desde otro terminal podemos hacer:

    netstat -tanp | grep nc
    tcp        0      0 0.0.0.0:38321       0.0.0.0:*        ESCUCHAR    9042/nc

    De todas formas, hay programas a los que no les gusta que le des el puerto 0, como Apache, que se niega a encenderse en ese puerto. Otros servidores como MySQL, te dejan especificar el puerto 0 y de hecho escuchan en un puerto aleatorio, aunque podrían decirte aunque sea en logs en qué puerto están escuchando (podemos averiguarlo con la línea de antes del netstat). Y servidores como redis que si les das el puerto 0, no escuchan en ningún puerto TCP.

    Foto principal: Scott Walsh

    The post Lanzando servidores TCP/IP en puertos libres aleatorios haciendo bind al puerto 0 appeared first on Poesía Binaria.

    » Leer más, comentarios, etc...

    Bitácora de Javier Gutiérrez Chamorro (Guti)

    ¿Cómo mejorar la compresión de NSIS?

    septiembre 13, 2017 09:53

    Trabajando en FileOptimizer me sugirieron disminuir el tamaño del instalador. Cómo sabéis, está basado en Nullsoft Scriptable Install System (NSIS), el instalador que se hizo famoso con el desaparecido WinAMP, y estaba configurado para comprimir usando LZMA, que es el algoritmo que aporta mejores resultados sobre ZLib y BZip2. Como podéis apreciar en Setup\FileOptimizerSetup.nsi, lo […]

    Artículo publicado originalmente en Bitácora de Javier Gutiérrez Chamorro (Guti)

    La entrada ¿Cómo mejorar la compresión de NSIS? aparece primero en Bitácora de Javier Gutiérrez Chamorro (Guti).

    » Leer más, comentarios, etc...

    Picando Código

    ¡Feliz día del programador!

    septiembre 13, 2017 09:00

    Como es tradición desde hace 10 años en este blog*, aprovecho el día del programador para desearle feliz día a mis colegas y hacer un repaso de lo que he estado haciendo como programador.
    *excepto el año pasado que me olvidé o no me sentía motivado para escribir algo al respecto…

    El día del programador se celebra en el día número 256 (28) del año por ser la cantidad de valores representables en un byte de datos (13/9 en los años comunes y 12/9 años bisiestos).

    Code Monkeys

    Este año me agarra en un muy buen momento en lo que se refiere a la programación. Hace poco más de un mes me mudé a Escocia, trabajando en Cultivate. Estuve casi 3 meses trabajando en un proyecto para cliente con Ruby On Rails, y tuvo un cierre bastante exitoso con las partes conformes con el trabajo realizado.

    Sigo usando Emacs como editor de texto y aprendiendo como siempre con él. Pero como en Cultivate hacemos mucho pair programming, buscamos usar una herramienta común. Por eso he estado trabajando con Spacemacs, una “distribución” de Emacs que une a Emacs y Vim en un sólo ambiente. Lo hemos incorporado de manera bastante exitosa junto a tmux y tmate para hacer pairing remoto.

                          ░░░░░░░░░▄░░░░░░░░░░░░░░▄░░░░
                          ░░░░░░░░▌▒█░░░░░░░░░░░▄▀▒▌░░░
         Such powerful    ░░░░░░░░▌▒▒█░░░░░░░░▄▀▒▒▒▐░░░
                          ░░░░░░░▐▄▀▒▒▀▀▀▀▄▄▄▀▒▒▒▒▒▐░░░
                          ░░░░░▄▄▀▒░▒▒▒▒▒▒▒▒▒█▒▒▄█▒▐░░░  Vim and Emacs
                          ░░░▄▀▒▒▒░░░▒▒▒░░░▒▒▒▀██▀▒▌░░░    So love
                          ░░▐▒▒▒▄▄▒▒▒▒░░░▒▒▒▒▒▒▒▀▄▒▒▌░░      Much forbidden
                          ░░▌░░▌█▀▒▒▒▒▒▄▀█▄▒▒▒▒▒▒▒█▒▐░░
    Very modes            ░▐░░░▒▒▒▒▒▒▒▒▌██▀▒▒░░░▒▒▒▀▄▌░
                          ░▌░▒▄██▄▒▒▒▒▒▒▒▒▒░░░░░░▒▒▒▒▌░
                          ▀▒▀▐▄█▄█▌▄░▀▒▒░░░░░░░░░░▒▒▒▐░
                          ▐▒▒▐▀▐▀▒░▄▄▒▄▒▒▒▒▒▒░▒░▒░▒▒▒▒▌
                          ▐▒▒▒▀▀▄▄▒▒▒▄▒▒▒▒▒▒▒▒░▒░▒░▒▒▐░
                          ░▌▒▒▒▒▒▒▀▀▀▒▒▒▒▒▒░▒░▒░▒░▒▒▒▌░
                          ░▐▒▒▒▒▒▒▒▒▒▒▒▒▒▒░▒░▒░▒▒▄▒▒▐░░
                          ░░▀▄▒▒▒▒▒▒▒▒▒▒▒░▒░▒░▒▄▒▒▒▒▌░░
                          ░░░░▀▄▒▒▒▒▒▒▒▒▒▒▄▄▄▀▒▒▒▒▄▀░░░      Wow.
                          ░░░░░░▀▄▄▄▄▄▄▀▀▀▒▒▒▒▒▄▄▀░░░░░
                          ░░░░░░░░░▒▒▒▒▒▒▒▒▒▒▀▀░░░░░░░░
      ██████╗  ██████╗  ██████╗ ███████╗███╗   ███╗ █████╗  ██████╗███████╗
      ██╔══██╗██╔═══██╗██╔════╝ ██╔════╝████╗ ████║██╔══██╗██╔════╝██╔════╝
      ██║  ██║██║   ██║██║  ███╗█████╗  ██╔████╔██║███████║██║     ███████╗
      ██║  ██║██║   ██║██║   ██║██╔══╝  ██║╚██╔╝██║██╔══██║██║     ╚════██║
      ██████╔╝╚██████╔╝╚██████╔╝███████╗██║ ╚═╝ ██║██║  ██║╚██████╗███████║
      ╚═════╝  ╚═════╝  ╚═════╝ ╚══════╝╚═╝     ╚═╝╚═╝  ╚═╝ ╚═════╝╚══════╝
    

    Como toda nueva herramienta al principio hay que acostumbrarse y aprenderse atajos de teclado, meter mano en alguna configuración y demás. Pero tmux es un camino sólo de ida, sumamente recomendable empezar a usarlo.

    Curiosamente en el post de 2015 comentaba que había estado jugando con Elixir, y sería un tema al que volvería. Al terminar el proyecto anterior, empecé de lleno a entrarle a Elixir. En Cultivate se usa y hay algunos proyectos internos, así que estoy estudiando y aprendiendo Elixir. Es un lenguaje funcional, diseñado por José Valim (viejo conocido del mundo Ruby). Esto te lleva a cambiar un poco la forma de programar respecto al paradigma de orientación a objetos.

    Lenguaje nuevo, ecosistema nuevo, paradigma nuevo, pero con algunas cosas familiares. Se puede hacer un paralelismo con lo que viene siendo mi vida en un nuevo país. Generalmente me siento perdido, y las cosas no funcionan como antes, pero le termino encontrando la vuelta y sigo adelante. Un desafío bastante grande, entre adaptarse a la nueva cultura y al reto tecnológico. Creo que mi cerebro ha estado absorbiendo y procesando más información en el último mes que muchos meses juntos antes. Pero es una buena experiencia tanto laboral como personal, así que seguro es para bien.

    Por otro lado, después de unos cuántos años, vuelvo a asistir a una conferencia de Ruby. A fin de mes voy a ir a Euruko, la conferencia Ruby europea. Si no recuerdo mal, mis últimas conferencias Ruby fueron Scottish Ruby Conference, RubyConf Uruguay y RubyConf Argentina todas en 2014. Desde entonces he ido a conferencias de otras tecnologías pero les perdí bastante el interés (comentaba algo en los 10 años del blog).

    Voy a Euruko con mucha expectativa, el keynote inicial está a cargo del mismísimo Matz, creador de Ruby. También hablan Charles Nutter, Sebastián Sogamoso y Netto Farah a quienes he tenido el gusto de ver en RubyConf Uruguay, y Bozhidar Batsov creador de Rubocop, hablando sobre Ruby 4, y otras tantas charlas más muy interesantes. El interés por Elixir medio que renueva las ganas de asistir a eventos también.

    Así que una vez más este año apreté “Reset” y arranqué varios aspectos, incluida la programación, desde cero. Así están las cosas por Picando Código. Feliz día del programador. A los que sigan leyendo del otro lado, ¿en qué andan por estos días? ¿Qué están programando?

    El día del programador otros años: 200720082009201020112012201320142015

    » Leer más, comentarios, etc...

    Blog Bitix

    La herramienta de documentación Javadoc de Java

    septiembre 11, 2017 11:00

    Java

    Programar y desarrollar requiere además de poseer diversos conocimientos disponer de una buena documentación de consulta y referencia. Una de las cosas buenas que me gustaron de Java cuando empecé a programar en este lenguaje, cuando aún estaba lejos de tener internet y aún me lo sigue pareciendo, fue su documentación Javadoc de toda la API de clases incluidas en el JDK.

    La documentación Javadoc es una colección de páginas HTML de todas las clases, métodos, parámetros y retornos junto con la información y especificaciones que quiera incluir el desarrollador de la API que en el caso de las clases de JDK incluye abundantes e interesantes detalles de implementación a tener en cuenta al usar las clases.

    Se genera a partir del propio código fuente de las clases con los comentarios incluidos que siguen cierto formato precediendo la definición de las clases y métodos. Al estar código y documentación en el propio archivo de código fuente es más fácil mantener sincronizados el código y su documentación.

    Documentación Javadoc del JDK

    La documentación en el código fuente se incluye en comentarios que preceden una clase o método, además, con anotaciones se pueden documentar los parámetros y el valor de retorno. Se pueden incluir etiquetas HTML junto con algunas de las anotaciones o doclets/taglets, algunas anotaciones Javadoc incluidas en el JDK son las siguientes pero también se pueden desarrollar doclets/taglets propios o personalizar los estilos de la documentación para cambiar el contenido, información incluida o adaptar los estilos a unos según los colores de la organización.

    • @author: indica el autor de la clase o método.
    • {@code}: incluye en el comentario un trozo de código que se formatea de forma especial.
    • {@docRoot}: incluye una ruta relativa al directorio raíz donde se genera la documentación.
    • @deprecated: indica que un método ha quedado obsoleto, se desaconseja su uso y puede que en futuras versiones desaparezca.
    • @exception: es sinónima de throws.
    • {@inheritDoc}: hereda el comentario Javadoc de la clase o método superior en la jerarquía de clases.
    • {@link}: incluye un enlace a otra sección de la documentación, método o clase.
    • {@linkplain}: es idéntica a @link pero el enlace es un texto plano.
    • {@literal}: muestra un texto sin interpretar el texto como HTML.
    • @param: documenta un parámetro de un método.
    • @return: documenta el valor de retorno de un método.
    • @see: incluye un enlace con documentación adicional en la sección final de la documentación.
    • @serial
    • @serialData
    • @serialField
    • @since: indica a partir de que versión de la API fue incluida la clase o método.
    • @throws: documenta una posible excepción que puede ser lanzada por el método.
    • {@value}: muestra el valor de un campo estático.
    • @version: para documentar la versión de cuando se hizo checkout del sistema de control de versiones.

    Un ejemplo usando estas anotaciones en una clase sería el siguiente.

    Una vez documentado el código fuente hay que usar la herramienta Javadoc para generar la documentación. Mediante la herramienta de construcción Gradle se hace con la tarea javadoc.

    La propia documentación de las clases del JDK está generada con la herramienta Javadoc. Este es el aspecto de la documentación de este ejemplo que tiene exactamente el mismo aspecto que la del JDK.

    Documentación Javadoc del ejemplo
    Archivos de la documentación Javadoc

    La documentación Javadoc al ser una colección de archivos HTML y demás recursos estáticos pueden copiarse a cualquier servidor web si es necesario que estén disponibles a través de internet y accesibles con cualquier navegador web.

    El código fuente completo del ejemplo puedes descargarlo del repositorio de ejemplos de Blog Bitix alojado en GitHub y probarlo en tu equipo ejecutando el comando ./gradlew javadoc.

    » Leer más, comentarios, etc...

    Blog Bitix

    Los tipos de referencias débiles soft, weak y phantom en Java

    septiembre 10, 2017 11:00

    Java

    Cuando un objeto ya no es alcanzable a través de ninguna referencia directa o cadena de referencias fuertes el objeto es seleccionable para reclamar su memoria y el recolector de basura o garbage collector de Java lo hace cuando estima oportuno, liberándonos a los programadores de esta tarea, simplificando el código y evitando fugas de memoria. El lenguaje Java le debe al recolector de basura entre otras varias cosas una buena parte de su éxito.

    En Java en realidad hay 4 tipos de referencias a objetos, además de las fuertes hay otras 3 más débiles que no impiden al recolector de basura reclamar el objeto referenciado. Es raro tener la necesidad de usar otra que no sean las fuertes o strong pero es interesante conocerlas por si en algún caso nos resultase de utilidad. Los otros 3 tipos de referencias denominadas débiles son SoftReference, WeakReference y PhantomReference que extienden de Reference. Usar una de estas otras 3 referencias es muy simple basta usar el constructor de cada tipo de referencia.

    Después de la llamada de varias veces al recolector de basura en este caso de forma explícita con el método System.gc() las referencias son encoladas.

    El objeto de una referencia soft es recolectable a discreción del recolector de basura ante necesidades de memoria, el objeto de una referencias weak es recolectable si solo es alcanzable por referencias weak y las referencias phantom son una mejor y más flexible alternativa al mecanismo de finalización de los objetos.

    Algunos usos prácticos de las referencias soft y weak son como caches de datos posiblemente usando la clase WeakHashMap, en el caso de las referencias phantom como mecanismo alternativo a la finalización de objetos incorporada en los objetos desde la versión inicial de Java.

    El mecanismo de finalización de los objetos Java con el método finalize que puede ser implementado por cualquier clase presenta los siguientes problemas:

    • La llamada al método finalize es impredecible ya que depende de cuando del recolector de basura reclame el objeto.
    • No hay garantía de que el método finalize sea llamado ya que puede perdurar durante toda la vida de la JVM.
    • Una referencia fuerte al objeto puede ser revivida en el método finalize si se implementa de forma inadecuada.

    En los constructores de las referencias débiles se puede indicar un ReferenceQueue en el que se encolará la referencia cuando el objeto al que referencia cambia su alcanzabilidad. Este mecanismo de notificación es utilizado con las referencias phantom para proporcionar el mecanismo de finalización alternativo. En la documentación javadoc con la descripción del paquete de las referencias se comenta este proceso de notificación. Las referencias son encoladas cuando el recolector de basura determina que son solo alcanzables por referencias soft, weak o phantom.

    En el artículo Replacing Finalizers With Phantom References se explica junto con su código como implementar el mecanismo alternativo al método finalize. La librería Guava proporciona las clases FinalizablePhantomReference y FinalizableReferenceQueue con una forma un poco más sencilla de usar las referencias phantom, en esa documentación también hay un ejemplo de código con su uso para liberar un recurso (ServerSocket) asociado a un objeto (MyServer).

    Las referencias débiles añaden una indirección a la referencia que contienen, usando el método get() se accede al objeto referenciado pero hay que tener en en cuenta que el método get puede devolver un null ya que no impiden al recolector de basura reclamar el objeto referenciado, en el caso de las PhantomReferences el método get siempre devuelve null para evitar que la referencia a un objeto sea revivida.

    El código fuente completo del ejemplo puedes descargarlo del repositorio de ejemplos de Blog Bitix alojado en GitHub y probarlo en tu equipo ejecutando el comando ./gradlew run.

    » Leer más, comentarios, etc...

    Blog Bitix

    Los tipos de referencias débiles soft, weak y phantom a objetos en Java

    septiembre 10, 2017 09:00

    Java

    Cuando un objeto ya no es alcanzable a través de ninguna referencia directa o cadena de referencias fuertes el objeto es seleccionable para reclamar su memoria y el recolector de basura o garbage collector de Java lo hace cuando estima oportuno, liberándonos a los programadores de esta tarea, simplificando el código y evitando fugas de memoria. El lenguaje Java le debe al recolector de basura entre otras varias cosas una buena parte de su éxito.

    En Java en realidad hay 4 tipos de referencias a objetos, además de las fuertes hay otras 3 más débiles que no impiden al recolector de basura reclamar el objeto referenciado. Es raro tener la necesidad de usar otra que no sean las fuertes o strong pero es interesante conocerlas por si en algún caso nos resultase de utilidad. Los otros 3 tipos de referencias denominadas débiles son SoftReference, WeakReference y PhantomReference que extienden de Reference. Usar una de estas otras 3 referencias es muy simple basta usar el constructor de cada tipo de referencia.

    El objeto de una referencia soft es recolectable a discreción del recolector de basura ante necesidades de memoria, el objeto de una referencias weak es recolectable si solo es alcanzable por referencias weak y las referencias phantom son una mejor y más flexible alternativa al mecanismo de finalización de los objetos.

    El mecanismo de finalización de los objetos Java con el método finalize que puede ser implementado por cualquier clase presenta los siguientes problemas:

    • La llamada al método finalize es impredecible ya que depende del recolector de basura.
    • No hay garantía de que el método finalize sea llamado ya que puede perdurar durante toda la vida de la JVM.
    • Una referencia fuerte al objeto puede ser revivida en el método finalize si se implementa de forma inadecuada.

    En los constructores de las referencias débiles se puede indicar un ReferenceQueue en el que se encolará la referencia cuando el objeto al referencia cambia su alcanzabilidad. Este mecanismo de notificación es utilizado con las referencias phantom para proporcionar el mecanismo de finalización alternativo. En la documentación javadoc con la descripción del paquete de las referencias se comenta este proceso de notificación. Las referencias son encoladas cuando el recolector de basura determina que son solo alcanzables por referencias soft, weak o phantom.

    En el artículo Replacing Finalizers With Phantom References se explica junto con su código como implementar el mecanismo alternativo al método finalize.

    Las referencias débiles añaden una indirección a la referencia que contienen, usando el método get() se accede al objeto referenciado pero hay que tener en en cuenta que el método get puede devolver un null ya que no impiden al recolector de basura reclamar el objeto referenciado, en el caso de las PhantomReferences el método get siempre devuelve null para evitar que la referencia a un objeto sea revivida.

    El código fuente completo del ejemplo puedes descargarlo del repositorio de ejemplos de Blog Bitix alojado en GitHub y probarlo en tu equipo ejecutando el comando ./gradlew run.

    » Leer más, comentarios, etc...

    Poesía Binaria

    Píldora: Recupera los nombres de los interfaces de red de tu equipo, eth0, wlan0, etc

    septiembre 06, 2017 08:17

    Network interfaces

    A medida que una tecnología crece, siempre tenemos el mismo problema, terminamos metiendo muchos elementos de un mismo tipo en un equipo. Y terminamos teniendo el mismo problema: si cambiamos algo de sitio, o un driver se carga antes que otro, estamos perdidos porque nada funcionará igual.

    Preámbulo

    El caso más claro es el de los discos duros, las placas base suelen tener un montón de puertos para conectar discos duros. Y, simplemente tendría que dar igual dónde enchufemos cada uno. Incluso si cambiamos un disco duro de sitio una vez instalado el sistema (porque se ha roto el puerto, porque nos viene mejor cambiar las conexiones por la longitud de los cables… o ¡porque lo desarmamos todo y tenemos que enchufar séis discos duros y no queremos apuntar dónde estaba cada uno! Por eso, si miramos un /etc/fstab más o menos reciente, veremos que los discos duros están identificados por un UUID (que podemos obtener si ejecutamos blkid como superusuario).

    Del mismo modo pasa con los interfaces de red. Podemos tener varias tarjetas de red enchufadas, varios WiFi y que, cada uno tenga un cometido diferente. Los dispositivos de red, por cable, históricamente han sido siempre eth0, eth1, eth2… y así hasta el final. Pero, ¿qué sucedería si cuando reiniciemos el que antes era eth0, ahora es eth2? seguramente el cable que está conectado a eth0 no tenga acceso a los equipos de eth2 y no funcione nada.

    Personalmente, tuve un problema parecido en un miniPC casero que tengo funcionando de router. Este equipo tiene dos interfaces de red: uno integrado en la placa base que está conectado a Internet, y otro enchufado por USB que está conectado a un switch y punto de acceso WiFi. Un día, tras actualizar el equipo en remoto no volví a tener acceso a la máquina hasta que físicamente le conecté un monitor y me di cuenta de que los nombres de los interfaces de red habían cambiado (eth0 pasó a ser eth1 y eth1 se convirtió en eth0). En aquel momento, lo que hice fue cambiar la configuración de los interfaces, cambiar los interfaces en el firewall y listo. Pero, desde udev versión 197 estos dispositivos pasaron a tener un nombre fijo y muy feo, pero pongas el dispositivo como lo pongas, el nombre se mantendrá.

    ¡Al grano!

    Por un lado está bien, por ejemplo, para el caso que comenté antes. Aunque en el ordenador de trabajo, que solo tengo un eth0 y wlan0, pero muchísimos scripts que utilizan interfaces de red fijos, no me gustaba la idea de tener que cambiar los nombres de dispositivos, así que decidí volver a utilizar eth0 y wlan0 como antes.

    Nota: Estas modificaciones están hechas para una Ubuntu, para otra distribución, si usamos grub tendremos que buscar el archivo de configuración.

    Para ello, editamos el archivo /etc/default/grub y en la línea de GRUB_CMDLINE_LINUX_DEFAULT añadimos net.ifnames=0. Por ejemplo esta línea puede quedarse así (depende mucho del contenido anterior):

    1
    GRUB_CMDLINE_LINUX_DEFAULT="quiet splash net.ifnames=0"

    Y luego ejecutamos:

    sudo update-grub

    Cuando reiniciemos el sistema, volveremos a tener todo como antes.

    Más info: Systemd/PredictableNetworkInterfaceNames en Freedesktop.org

    Actualizado 07/09/2017: Añadido enlace de Freedesktop. Gracias Osqui.

    Foto principal: Seb Zurcher

    The post Píldora: Recupera los nombres de los interfaces de red de tu equipo, eth0, wlan0, etc appeared first on Poesía Binaria.

    » Leer más, comentarios, etc...

    Poesía Binaria

    Aprende a utilizar Emacs. Abre tu mente, desdobla tus dedos, trabaja a gusto y sé productivo

    septiembre 04, 2017 08:11

    Finger Twister. Emacs, y tus dedosMi historia con Emacs se remonta a 2005, momento en que yo pasaba una crisis de editor de texto. Llevaba varios años buscando un editor de texto que se ajustara a mis necesidades. Primero para Windows, donde me tiré años probando editores que venían en CDs de revistas, tanto libres, gratis o shareware. Hasta entonces había trabajado con varios IDEs y editores, pero estaban orientados a un lenguaje determinado, eran poco personalizables o no eran todo lo cómodos que a mi me gustaría (eso de, el editor A me gusta, y el editor B no, pero si A tuviera una cosilla que tiene B que está muy bien…)

    La elección de un editor de texto es algo muy personal de cualquier desarrollador (o aspirante a serlo) y, ya que es una herramienta con la que va a echar muchas horas, es imprescindible sentirse cómodo y productivo y, a ser posible, contar con herramientas que eviten tener que estar cambiando de aplicación y tener que recargar archivos en varios sitios, cosa que al final puede jugarnos malas pasadas.

    Conociendo Emacs

    Aunque Emacs lo había abierto antes, en mis deslices iniciales con GNU/Linux, era un programa que me asustaba un poco. Tenía un logotipo horrible y me resultaba excesivamente feo y poco útil. Lo había abierto alguna que otra vez por accidente y, aunque había intentado usarlo un poco, terminaba desistiendo y cerrándolo y hasta desinstalándolo preguntándome por qué se me instalaba automáticamente con el sistema operativo.

    Fue un compañero de Universidad el que me hizo darle otra oportunidad, me pasó un fichero de configuración donde le cambiaba el tema y me comentó algunos atajos de teclado básicos. Así que decidí darle otra oportunidad. Me gustó bastante el hecho de que casi no hay que utilizar el ratón, en realidad sin el casi, no hay que utilizarlo, aunque algunas veces puede que sea más rápido e intuitivo utilizarlo, pero tenemos la posibilidad de no hacerlo, hay teclas para todo, y cuando no hay tecla o no te acuerdas, tienes un comando. Además, puedes configurar el anidado o indentación como quieras, de acuerdo al proyecto en el que estés y existe coloreado de sintaxis para casi cualquier lenguaje de programación.

    He de decir que la curva de aprendizaje no es muy rápida, de hecho al principio cuesta un poco, y necesitas tiempo, leer mucha documentación, probar configuraciones y extensiones, cabrearte y borrar toda la configuración y volver a empezar y quedarte con la sensación de no saber si es frustrante o divertido. De hecho, creo que si tuviera que empezar a utilizarlo ahora, no lo usaría. Me he vuelto impaciente y siempre ando demasiado ocupado y creo que actualmente tenemos muchas opciones. Pero me encanta Emacs, a menudo descubro nuevas posibilidades y opciones que me hacen la vida más fácil. También descubro alguna que otra pega, pero al final compensa.

    Iniciándose en la configuración

    Si finalmente decides probar Emacs o quieres crear una nueva configuración y personalizar el entorno a fondo mi consejo es probar alguna de las configuraciones que muchos usuarios publican en GitHub. Os dejo algunos ejemplos:

    Por supuesto hay muchos más. Éstos son sólo algunos que me ha gustado revisar. O, al menos, me ha parecido que tienen algunas cosas curiosas. Y, claro, a la hora de crear nuestras configuraciones, está muy bien copiarnos de ellos, porque seguro que tienen cosas muy chulas.

    Matizando nuestra configuración


    Una de las cosas muy buenas (y muy malas) de este editor, es que todo se programa. Es decir, tanto los ficheros de configuración, como las extensiones están programados en Emacs Lisp. Por un lado está muy bien porque tendremos la potencia necesaria para añadir y cambiar las funcionalidades que queramos, aunque por otro lado, haya que ponerse a programar y sea complicado. Bueno, más que complicado es que da una pereza enorme ponerse, porque no es perderse en una ventana de botones, checkboxes y demás. Ni es editar un archivo tipo INI con claves y valores. La ventaja es que en Emacs, la configuración no tiene límite: podemos definir valores, por supuesto, decir si activamos o desactivamos una característica; pero también podemos definir los comportamientos de todas las teclas y combinaciones de teclas que se pulsen (incluso botones de ratón), podemos añadir lógica a algunas acciones y eso nos permite perfilar muchas cosas y adaptar algunas características que no nos gustan del todo para que nos convenzan un poco más. Por ejemplo, podemos asignar una tecla para recargar un fichero y modificar la acción para que no nos pida confirmación (¿estás seguro de que quieres recargar…?), o incluso programar la ejecución de cualquier programa externo ante cualquier evento.
    Por si fuera poco, encuentras para Emacs, miles de extensiones para todo lo que te puedas imaginar, anotaciones, lenguajes, temas, juegos, IRC, correo electrónico, gestión del entorno, autocompletar, cifrado, información meteorológica, navegación por Internet, blogging, portapapeles, menús, finanzas, matemáticas, búsquedas, conexiones con servidores, control del entorno de ventanas, control de reproducción multimedia, lectura de pantalla, interacción con bases de datos, interacción con repositorios de código, compiladores, y miles de cosas más.

    Ficheros de configuración

    Emacs, una de las primeras cosas que hace nada más arrancar es leer los ficheros de configuración, que pueden estar en varios sitios. Si tenemos poca cosa, podemos meterlo todo en ~/.emacs (fichero oculto dentro de nuestro HOME). Aunque desde la versión 22, si no se encuentra dicho archivo se cargará ~/.emacs.d/init.el. Y dentro de ~/.emacs.d podemos meter todos los archivos que queramos para la configuración, separar los módulos a nuestro gusto y complicar el asunto hasta el infinito y más allá.
    La configuración separada es muy útil si cargamos muchas extensiones y tenemos que configurarlas todas, además tenemos configuraciones específicas para cada lenguaje de programación que solemos utilizar y tenemos además ciertas funciones propias. Asimismo en dicho directorio se almacena información sobre abreviaturas, documentos recientes, disposiciones de ventanas, cachés, información temporal y módulos; por si fuera poco, Emacs aprovecha y compila muchos de los ficheros de los módulos para poder cargarlos antes y hacer nuestra experiencia de usuario más fluida.

    Creando nuestra configuración personal

    En el siguiente post veremos un ejemplo de la configuración real que me he montado para mi Emacs, aunque aquí veremos una pequeña introducción. A pesar de que Emacs realmente no está limitado a ningún lenguaje de programación porque gracias a sus extensiones podemos hacer que soporte múltiples lenguajes, cuando vamos a crear nuestra configuración debemos pensar en los lenguajes que vamos a utilizar más a menudo. Aunque siempre podemos coger e instalar un modo nuevo para cualquier lenguaje, al menos para colorear el código y controlar el anidado, tal vez nos interese, para nuestro trabajo diario configurar el autocompletado, revisión de sintaxis o revisar la configuración específica de algún modo para tenerlo todo a nuestro gusto.
    Luego tenemos que preguntarnos qué nos gustaría tener en nuestro editor: números de línea, plegado de código, textos predefinidos, herramientas predictivas, etc. Aunque seguro que en el siguiente post veremos un montón de cosas que tal vez no sabías que necesitabas, pero en cuanto las ves sabes que las necesitas. A mí me ha pasado, llevo mucho tiempo elaborando una nueva configuración y buscando información sobre muchas extensiones.

    Múltiples configuraciones

    Emacs, normalmente soporta una sola configuración por usuario, por lo que resulta muy difícil utilizar Emacs para trabajar y cuando termina la jornada laboral, para crear una nueva configuración. O simplemente para probar configuraciones de otras personas para ver cuál es la que más se ajusta a nosotros. Aunque podemos aprovechar la flexibilidad que tiene Emacs para crear un script de configuración muy sencillo que sea capaz de cargar la configuración desde un directorio especificado. Para ello, editamos ~/.emacs e introducimos lo siguiente:

    1
    2
    3
    4
    5
    6
    (defvar user-custom-dir (getenv "EMACS_USER_DIR"))

    (when (/= (length user-custom-dir) 0)
      (setq user-emacs-directory (file-name-as-directory user-custom-dir)))

    (load (expand-file-name "init.el" user-emacs-directory))

    Con esto leeremos la variable de entorno EMACS_USER_DIR antes de cargar la configuración y cambiaremos .emacs.d por dicho valor. Seguidamente, cargaremos el archivo init.el situado en dicho directorio. Así que podremos ejecutar Emacs de la siguiente forma:

    EMACS_USER_DIR=~/.emacs.d_new/ emacs

    Y se cargará la configuración dentro del directorio .emacs.d_new. Eso sí, tenemos que tener cuidado, los ficheros de configuración deberán referirse al directorio de comunicación como user-emacs-directory ya que si lo hacen como ~/.emacs.d/ como suele ser costumbre puede que se carguen cosas que no deben.

    Referencia, para ir abriendo boca

    Si quieres ponerte a leer un rato y ver material sobre Emacs, aquí te dejo algunos enlaces:

    The post Aprende a utilizar Emacs. Abre tu mente, desdobla tus dedos, trabaja a gusto y sé productivo appeared first on Poesía Binaria.

    » Leer más, comentarios, etc...

    Koalite

    7 consejos para elegir una librería

    septiembre 04, 2017 05:06

    Hace no tanto tiempo, para los que trabajábamos en determinadas plataformas (ejem, Microsoft) era complicado justificar el uso de liberías de terceros, pero por fortuna hoy en día la situación ha cambiado. En casi cualquier plataforma existe una amplia varidad de librerías disponibles para hacer las tareas más diversas, y eso, que parece una maravilla, también tiene conlleva sus problemas.

    Es fácil acabar agobiado ante la cantidad de cosas que creemos que necesitamos conocer y que el número de opciones disponibles nos lleve a una parálisis que no nos deje decidir (lo que se conoce como paradoja de la elección).

    Elegir qué librerías utilizar no es fácil y, al igual que elegir un lenguaje de programación, depende muchos factores. Muchos de ellos, además, son muy dependientes del equipo de desarrollo y del contexto en que se toma la decisión (gustos personales, conocimientos previos, tipo de proyecto, etc.).

    Pese a ello, creo que es interesante ver algunas cosas que deberíamos tener en cuenta a la hora de elegir una librería. El que sean más o menos determinantes, dependerá en gran medida de ese contexto que mencionaba antes.

    Intenta no usar librerías

    Puede parecer una tontería, pero más de una vez me he visto usando una librería que realmente no necesitaba. Es una consecuencia de ese lado oscuro que tienen los gestores de paquetes actuales. Es tan fácil instalar una librería, que ni nos lo pensamos.

    Lo malo es que cada librería que instalas es una dependencia nueva que tienes que mantener actualizada y por la que tienes que responder ante tus clientes. Para ellos, un fallo en la librería es un fallo en tu aplicación, así que más te vale confiar en ella.

    Si lo que vas a usar de la librería lo puedes implementar tú en unas cuantas líneas de código, seguramente sea mejor empezar por ahí. Ya tendrás tiempo de recurrir a la librería si realmente se complica la cosa.

    Esto no quiere decir que no debas usar librerías. Es complicado justificar una inversión de 3 años para escribir tu propio ORM, tu framework SPA y tu framework para servidor web en lugar de utilizar cosas que ya existen.

    Trata de buscar alternativas

    Es muy tentador quedarse con la primera librería que encontremos para hacer algo, y hay veces, sobre todo cuando ese algo no es muy crítico, que es razonable hacerlo así.

    Pero si estás eligiendo una librería que va a condicionar tu forma de desarrollar la aplicación, que va a tener impacto en partes importantes de la misma, o a la que se va a acoplar gran parte de ella, merece la pena dedicar un poco de esfuerzo a considerar alternativas en lugar de quedarte con el primer resultado de Google o lo que hayas visto en Reddit ese día.

    Que tenga cierta madurez

    Usar cosas antiguas está mal visto, pero elegir una librería que no es lo último de lo último al empezar un proyecto nuevo, parece algo de locos. Sin embargo, optar por lo último de lo último del hype de esta semana, es probablemente una de las peores decisiones que puedes tomar.

    En primer lugar, porque lo último de lo último todavía no estará terminado, será una versión alpha, o beta, o release candidate o 1.0. Con los tiempos que corren, es más o menos lo mismo: algo poco estable. Y ya no sólo hablo de bugs, que puede que no sean excesivos (o que los corrijan rápido), sino también de API. Puedes perder mucho tiempo readaptando tu aplicación a los cambios de API que se van introduciendo en las nuevas versiones, y además te verás obligado a actualizarte para corregir los bugs que has ido encontrando. Un escenario nada agradable.

    Por si eso fuera poco, algo que lleva unos meses disponible es complicado que tenga tracción suficiente para tener muchos usuarios, y cabe la posibilidad de que acabe descontinuado pronto.

    Personalmente, prefiero utilizar una librería más antigua, que tenga cierta estabilidad y haya sobrevivido al mundo real, aunque sea más “fea”, que una librería supernovedosa que no sé cómo acabará. Eso no quiere decir que no esté bien conocer las librerías novedosas, pero a la hora de desarrollar profesionalmente, un poco de cautela viene bien.

    No seas el único usuario y que haya alguien detrás

    Es muy desesperante tener un problema y que no aparezca en ningún sitio en Google. O peor aun, que el único resultado que encuentres sea la pregunta que hiciste en StackOverflow hace 2 semanas y que no tiene ninguna respuesta.

    Una librería con una buena base de usuarios suele implicar dos cosas: que probablemente esté más pulida porque haya habido más gente reportando fallos, y que será más fácil encontrar a alguien que ya haya pasado por el mismo problema que tienes que tú ahora y tal vez te pueda ayudar.

    También es importante ver quién está detrás de la librería. No es igual una empresa grande (Facebook, Google, …) que un desarrollador freelance, que una comunidad establecida de gente colaborando. Cada caso es diferente, pero lo importante es pensar qué continuidad puede tener esa librería en el futuro. Y, ojo, el hecho de que sea de una empresa grande no garantiza más continuidad que un par de desarrolladores apasionados por su trabajo. Puede que para la empresa no sea algo estratégico y la acabe descontinuando, mientras que esos desarrolladores podrían acabar creando una comunidad capaz de mantener la librería si les pasara algo.

    En este aspecto, si el proyecto es de código abierto y tiene una comunidad lo bastante grande, puedes tener cierta esperanza de que si los creadores originales desaparecen, alguien dé un paso al frente y siga con el proyecto (que también podrías ser tú, pero ese es otro tema).

    Fácil de integrar o de extender

    Suelo preferir librerías pequeñas. Normalmente están más focalizadas en resolver un problema concreto y hacerlo bien, y además suelen ser más simples por lo que es más fácil que estén bien depuradas y sean sólidas. Lo malo de elegir librerías pequeñas es que acabarás necesitando más (con los problemas que implica el aumento de dependencias) y es importante que sea fácil integrar unas con otras.

    Si optas por una librería o framework de mayor tamaño, asegúrate de que sea lo bastante flexible y extensible como para poder cambiar su comportamiento en el futuro, sobre todo si vas a acabar con mucho (incluso todo) el código de tu aplicación dependiendo de ella. Tener una única dependencia grande tiene sus ventajas frente a muchas dependencias pequeñas, pero si al final te acaba impidiendo hacer lo que necesitas y tienes que desecharla entera, también es más costoso.

    (Razonablemente) Bien documentada

    Seamos realistas, esto hoy en día esto es complicado de encontrar. Entre la cantidad de proyectos que corren más que su documentación oficial (algunos incluso procedentes de empresas con muchos recursos), y la velocidad a la que se sacan versiones rompiendo APIs y dejando posts obsoletos, no siempre es fácil encontrar una documentación adecuada. Pese a todo, es un factor que valoro bastante porque puede ahorrarte (o hacerte perder) mucho tiempo. Un punto adicional para las librerías que mantienen la documentación versionada y te permiten acceder a la documentación de la versión que estás usando, no a la última disponible.

    En los proyectos de código abierto tienes la ventaja de que siempre puedes mirar el código. Y eso está muy bien. Si tienes ganas y tiempo, claro. Puedes ponerte a leer el código para ver cómo se hace algo, y con un poco de suerte aprenderás cosas por el camino, pero no es la forma más rápida de resolver una duda.

    Con una licencia que te sirva

    No se trata de que todo tenga que ser de código de abierto y gratis, pero ahora que casi todo es así, hay que tener cuidado con la licencia de la librería que vas a usar. El mundo de las licencias de software es complicado y, si quieres ser legal (cosa que doy por hecho), es necesario prestar un poco de atención. Y a veces, la cosa se lía y entender las implicaciones de una licencia es realmente difícil (como el caso de React y las patentes).

    Que la librería sea de pago no debería ser un factor determinante, se supone que te va a ahorrar un trabajo que cuesta más que la librería. Por desgracia, en el mundo real, el precio de las cosas importa y si el coste es muy alto, o si el coste es por instalación y estás vendiendo licencias de un producto, puede que ya no salga tan rentable.

    Conclusiones

    Básicamente, mi guía a la hora de elegir una librería es asegurarme de que la necesito, de que puedo (legal y económicamente) usarla, de que no me va a volver loco, y que va a seguir existiendo dentro de unos años. Todos los puntos anteriores llevan de una u otra forma a cumplir con alguna de estas cuatro premisas.

    Quizá alguien eche de menos una mención a la usabilidad o la curva de aprendizaje, pero en mi caso concreto son dos factores que puedo llegar a gestionar bien. Al desarrollar productos (no proyectos ni produyectos), el proceso de desarrollo y vida del software son lo bastante largos como para que hacer una inversión inicial mayor en aprender una librería, o en crear un wrapper sobre su API para hacerla más usable, se diluya en los (esperables) beneficios de utilizarla a medio y largo plazo.

    Posts relacionados:

    1. TinyTwitter: Una micro librería para twittear desde C#
    2. Cómo elegir un lenguaje de programación
    3. Cómo elegir aggregate roots

    » Leer más, comentarios, etc...

    Poesía Binaria

    Compilando y subiendo los primeros programas al ESP8266. Usando GPIO y UART-

    septiembre 01, 2017 01:54

    Me ha gustado mucho la facilidad de uso (y sobre todo el precio) de este dispositivo. El ESP8266 puede dotar a nuestros proyectos de la conectividad que necesitaban y así adentrarnos en el Internet de las cosas o IoT.

    Lo primero es sentirnos cómodos con el entorno. Así que voy a proponer dos opciones. La primera, multiplataforma será utilizando el mismo entorno Arduino. En este caso, al final del proceso cuando estemos compilando o subiendo el código a nuestro chip seleccionaremos la placa ESP8266 y compilaremos para ella. Podremos conectar el ESP8266 con un adaptador USB-UART o incluso desde el propio Arduino. Para ver cómo debemos conectarlo, visita el post sobre programación del ESP8266.

    Instalación en el entorno Arduino

    Como dije antes es la opción más cómoda y nos va a funcionar sea cual sea nuestro sistema operativo. De manera muy parecida a como hice en este post sobre la programación de ATtinys, podemos añadir esta URL para gestionar tarjetas en Archivo / Preferencias:

    http://arduino.esp8266.com/stable/package_esp8266com_index.json


    Tras esto, vamos a Herramientas / Placa / Gestor de tarjetas y buscamos ESP8266. Con lo que debería salirnos algo así:

    Seguidamente seleccionamos el módulo y lo instalamos. El software ocupa cerca de 150Mb, entre bibliotecas, programas para compilar y programar, cabeceras y demás, así que tenemos que tener en cuenta el espacio que necesitaremos. Y tardará un rato entre la descarga de los módulos y la configuración.

    Con Arduino no es muy duro, ¿eh? En este punto ya podemos crear cualquier programa para nuestro chip y subirlo sin complicación desde el entorno del que estamos acostumbrados. Además, desde el entorno Arduino podemos aprovecharnos de muchas facilidades que nos brinda el lenguaje, por ejemplo el Serial lo podemos programar de forma más parecida a como lo hacemos en un Atmega con las abstracciones que nos proporciona Arduino. El programa ocupará un poco más, ya que tenemos las abstracciones propias de Arduino y algunas bibliotecas y atajos de enlazado muy interesantes. Aunque los compiladores son muy listos a estas alturas y todo lo que subamos estará optimizado en tamaño.

    Instalación independiente

    Pero claro, no todo es Arduino y su ecosistema. Si de verdad queremos tener algo más de control sobre las biliotecas, la compilación, la programación y demás, nos vendrá realmente bien la instalación manual del software.
    He utilizado Linux Mint 18.2 para realizar esta instalación, aunque debería ser muy parecido en Ubuntu, Debian y otras. Los nombres de los paquetes también suelen ser parecidos entre distribuciones (no los mismos, pero al menos se pueden encontrar más o menos fácil).

    Instalar dependencias

    Antes de nada vamos a ver qué necesitamos para instalar nuestro entorno. Lo primero, unos 4Gb de disco libres, porque vamos a copiar mucho código fuente y documentación.

    Tras ello, instalaremos el software necesario que puede venir con nuestra distribución GNU/Linux:

    sudo apt-get install git build-essential gperf bison flex libtool libtool-bin libncurses5-dev gawk libc6-dev-amd64 python-serial libexpat-dev help2man

    Instalar software

    Aunque podemos realizar el proceso de forma manual, vamos a utilizar esp-open-sdk. Aquí voy a poner algunas instrucciones sencillas para instalarlo. Pero podremos personalizar la instalación y hacer algunas cosas más. Leed el manual, que hay cosas muy interesantes, pero si queréis algo rápido:

    git clone --recursive https://github.com/pfalcon/esp-open-sdk.git
    cd esp-open-sdk
    make
    …. esto tarda un rato …..
    export PATH=$(pwd)/xtensa-lx106-elf/bin:$PATH

    Tardará un rato porque tendremos que descargar muchas cosas (a mí me tardó unos 10 minutos más o menos).

    Luego, es buena idea incluir en nuestro archivo /home/USUARIO/.profile lo siguiente:

    PATH=”$HOME/local/opt/stand/esp-open-sdk/xtensa-lx106-elf/bin:$PATH”

    En mi caso, yo instalé todos los programas en $HOME/local/opt/stand/esp-open-sdk/ deberás cambiar este directorio por tu directorio de instalación. Esta línea en .profile sirve para que podamos ejecutar las utilidades instaladas en las próximas sesiones estemos donde estemos sin necesidad de hacer export PATH=…

    Compilando ejemplos

    Ya que tenemos el entorno preparado, vamos a proceder a compilar algunos de los ejemplos que vienen (y algunos otros que me he inventado) para hacer pruebas. Primero, vamos a compilar un programa de led intermitente, un blink para ver que todo funciona bien. Al menos, si vemos el led haciendo intermitencia, es que todo funciona perfectamente. De todas formas, al final del post veremos algunos problemas con los que me he encontrado.

    Blinky a mano

    Una vez instalamos esp-open-sdk dentro del directorio examples encontramos blinky. El famoso ejemplo de un led intermitente, algo así como un hola mundo, en el que podemos ver si nuestros programas compilan bien y si se suben bien, además, veremos el chip funcionando.
    Puede que en futuras versiones no encontremos este ejemplo, o incluso aparezca modificado, aśi que pondré aquí el contenido de blinky.c:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    #include "ets_sys.h"
    #include "osapi.h"
    #include "gpio.h"
    #include "os_type.h"

    static const int pin = 1;
    static volatile os_timer_t some_timer;

    void some_timerfunc(void *arg)
    {
      //Do blinky stuff
      if (GPIO_REG_READ(GPIO_OUT_ADDRESS) & (1 << pin))
      {
        // set gpio low
        gpio_output_set(0, (1 << pin), 0, 0);
      }
      else
      {
        // set gpio high
        gpio_output_set((1 << pin), 0, 0, 0);
      }
    }

    void ICACHE_FLASH_ATTR user_init()
    {
      // init gpio subsytem
      gpio_init();

      // configure UART TXD to be GPIO1, set as output
      PIN_FUNC_SELECT(PERIPHS_IO_MUX_U0TXD_U, FUNC_GPIO1);
      gpio_output_set(0, 0, (1 << pin), 0);

      // setup timer (500ms, repeating)
      os_timer_setfn(&some_timer, (os_timer_func_t *)some_timerfunc, NULL);
      os_timer_arm(&some_timer, 500, 1);
    }

    Está pensado para cargarlo en un ESP8266-01 por lo que el led está en el GPIO01, que es el led azul que indica transmisión de datos. Al mismo tiempo ese puerto coincide con la transmisión de datos del puerto serie. Así que, cuando lo ejecutemos, al mismo tiempo que veremos el led parpadeando (si todo funciona bien), si abrimos una ventana serie mientras tenemos el chip enchufado en el ordenador veremos cómo recibimos datos en la consola.

    Aunque el ejemplo trae un fichero Makefile, con el que simplemente haciendo make debe funcionar, quiero repasar todo lo necesario para compilar, generar un fichero binario y subir el fichero a nuestro dispositivo:

    xtensa-lx106-elf-gcc -I. -mlongcalls -o blinky blinky.c -Teagle.app.v6.ld -nostdlib -Wl,--start-group -lmain -lnet80211 -lwpa -llwip -lpp -lphy -lc -Wl,--end-group -lgcc

    ¿Por qué tanta complicación? Casi todo son bibliotecas, aunque veamos:
    • xtensa-lx106-elf-gcc : Es el compilador, la versión que hemos instalado de gcc.
    • -mlongcalls : A veces, cuando se traduce el código C a ensamblador, hay llamadas a funciones o a direcciones de memoria que no se pueden hacer directamente, porque están en posiciones de memoria lejanas.
    • -o blinky : El fichero binario de destino se llamará sólo blinky.
    • blinky.c : Es nuestro fichero fuente. Esta vez sólo tenemos uno. Aunque cuando tengamos más archivos utilizaremos siempre un Makefle en lugar de pelearnos con todos aquí, aunque podríamos incluir una sere de archivos .c desde aquí.
    • -Teagle.app.v6.ld : Para realizar el linkado vamos a utilizar un script del SDK del ESP8266 llamado eagle.app.v6.ld. Esto podrá variar en el futuro.
    • -nostdlib : No incluiremos la biblioteca estándar de C, ya que el SDK tiene su propia implementación.
    • -Wl,–start-group …. -Wl,–end-group : Lo que empieza por -Wl,… serán argumentos que le pasaremos al linker, es decir, el que asocia las llamadas que hacemos a funciones de biblioteca con las bibliotecas en cuestión. El hecho de utilizar grupos en el linker de GCC evita que tengamos que repetir el linkado de bibliotecas si, por ejemplo hay referencias cruzadas entre las propias bibliotecas. Es decir, que las bibliotecas hagan llamadas a funciones que están en otras bibliotecas por lo que tendríamos que linkarlas de nuevo. Se pueden añadir de nuevo en la línea de GCC, pero queda feo hacerlo.
    • -lmail , -lnet80211 , -lwpa … : Todas estas serán bibliotecas que encontramos dentro del SDK. Dentro de sdk/lib están todas. Y seguramente para nuestros proyectos futuros utilizaremos alguna de ellas.

    esptool.py elf2image blinky

    Esto generará la imagen que tenemos que grabar en la memoria flash de nuestro chip. Seguidamente, conectaremos nuestro ESP8266 tal y como lo hicimos en este post para flashear el firmware:

    Y ejecutamos:

    esptool.py -p /dev/ttyACM1 write_flash 0x00000 blinky-0x00000.bin 0x10000 blinky-0x10000.bin

    Tras ello, desconectamos los pins GPIO0 y GPIO2 (que estaban en modo programación). Desconectamos la corriente y la volvemos a conectar. Debemos ver el dispositivo con el led intermitente.

    Utilizando C99, C11 y C++

    Podemos utilizar también revisiones nuevas del lenguaje de programación C. Sin problema. El caso es que, como todavía quedan resquicios privativos en las bibliotecas del chip y, parece ser, que no se han publicado aún muchas cosas, hay funciones cuya cabecera no está disponible. Así que varias comunidades dedicadas al ESP8266 (y ESP32, que es la siguiente versión), han decidido crear un archivo llamado espmissingincludes.h; se puede buscar por Internet, aunque una de las versiones que más me convence es esta:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    #ifndef ESPMISSINGINCLUDES_H
    #define ESPMISSINGINCLUDES_H

    #include <user_interface.h>
    #include <eagle_soc.h>
    #include <stdint.h>
    #include <c_types.h>
    #include <ets_sys.h>
    #include <stdarg.h>

    #ifdef __cplusplus
    extern "C"
    {
    #endif

    //Missing function prototypes in include folders. Gcc will warn on these if we don't define 'em anywhere.
    //MOST OF THESE ARE GUESSED! but they seem to work and shut up the compiler.
    typedef struct espconn espconn;

    /*bool wifi_station_set_hostname(char *);
    char *wifi_station_get_hostname(void); */


    int atoi(const char *nptr);

    //void ets_install_putc1(void *routine); // necessary for #define os_xxx -> ets_xxx
    //void ets_isr_attach(int intr, void *handler, void *arg);
    void ets_isr_mask(unsigned intr);
    void ets_isr_unmask(unsigned intr);

    int ets_memcmp(const void *s1, const void *s2, size_t n);
    void *ets_memcpy(void *dest, const void *src, size_t n);
    void *ets_memmove(void *dest, const void *src, size_t n);
    void *ets_memset(void *s, int c, size_t n);
    int ets_sprintf(char *str, const char *format, ...)  __attribute__ ((format (printf, 2, 3)));
    int ets_str2macaddr(void *, void *);
    int ets_strcmp(const char *s1, const char *s2);
    char *ets_strcpy(char *dest, const char *src);
    //size_t ets_strlen(const char *s);
    //int ets_strncmp(const char *s1, const char *s2, int len);
    char *ets_strncpy(char *dest, const char *src, size_t n);
    char *ets_strstr(const char *haystack, const char *needle);

    void ets_timer_arm_new(volatile ETSTimer *a, int b, int c, int isMstimer);
    void ets_timer_disarm(volatile ETSTimer *a);
    void ets_timer_setfn(volatile ETSTimer *t, ETSTimerFunc *fn, void *parg);

    void ets_update_cpu_frequency(int freqmhz);

    #ifdef SDK_DBG
    #define DEBUG_SDK true
    #else
    #define DEBUG_SDK false
    #endif

    int ets_vsprintf(char *str, const char *format, va_list argptr);
    int ets_vsnprintf(char *buffer, size_t sizeOfBuffer,  const char *format, va_list argptr);
    int os_snprintf(char *str, size_t size, const char *format, ...) __attribute__((format(printf, 3, 4)));
    int os_printf_plus(const char *format, ...)  __attribute__((format(printf, 1, 2)));

    // memory allocation functions are "different" due to memory debugging functionality
    // added in SDK 1.4.0
    //void vPortFree(void *ptr, const char * file, int line);
    //void *pvPortMalloc(size_t xWantedSize, const char * file, int line);
    //void *pvPortZalloc(size_t, const char * file, int line);
    void *vPortMalloc(size_t xWantedSize);
    void pvPortFree(void *ptr);

    //void uart_div_modify(int no, unsigned int freq);
    //uint32 system_get_time();
    int rand(void);
    void ets_bzero(void *s, size_t n);
    void ets_delay_us(int ms);

    // disappeared in SDK 1.1.0:
    #define os_timer_done ets_timer_done
    #define os_timer_handler_isr ets_timer_handler_isr
    #define os_timer_init ets_timer_init

    // This is not missing in SDK 1.1.0 but causes a parens error
    #undef PIN_FUNC_SELECT
    #define PIN_FUNC_SELECT(PIN_NAME, FUNC)  do { \
        WRITE_PERI_REG(PIN_NAME,   \
            (READ_PERI_REG(PIN_NAME) & ~(PERIPHS_IO_MUX_FUNC<<PERIPHS_IO_MUX_FUNC_S))  \
                |( (((FUNC&BIT2)<<2)|(FUNC&0x3))<<PERIPHS_IO_MUX_FUNC_S) );  \
        } while (0)



    // Shortcuts for memory functions
    //#define os_malloc   pvPortMalloc // defined in SDK 1.4.0 onwards
    //#define os_free     vPortFree    // defined in SDK 1.4.0 onwards
    //#define os_zalloc   pvPortZalloc // defined in SDK 1.4.0 onwards
    //uint8 wifi_get_opmode(void); // defined in SDK 1.0.0 onwards
    //int os_random();             // defined in SDK 1.1.0 onwards
    #ifdef __cplusplus
    }
    #endif

    #endif

    Este archivo debemos incluirlo en nuestro archivo .c del programa. Y ahora sí que nos dejará compilar con algo así:

    xtensa-lx106-elf-gcc -I. -mlongcalls -o blinky blinky.c -Teagle.app.v6.ld -nostdlib -Wl,--start-group -lmain -lnet80211 -lwpa -llwip -lpp -lphy -lc -Wl,--end-group -lgcc -std=c11

    Con lo que podremos utilizar características del lenguaje C mucho más modernas.

    ¿Qué hay de C++?
    Bueno, el SDK del ESP8266 está hecho en C y las funciones que se llamen desde la biblioteca (como por ejemplo user_init(), deben estar enlazadas en C. Por eso, vamos a utilizar:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    extern "C"
    {
      // Aquí van los includes de la biblioteca
    }

    // Aquí va el código C++ que vayamos a utilizar.

    extern "C"
    {
      // Aquí van las funciones que se llamen desde la biblioteca.
    }

    Así, nuestro blinky.cpp quedaría así:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    extern "C"
    {
    #include "ets_sys.h"
    #include "osapi.h"
    #include "gpio.h"
    #include "os_type.h"
    #include "espmissingincludes.h"
    }

    // ESP-12 modules have LED on GPIO2. Change to another GPIO
    // for other boards.
    static const int pin = 1;
    static volatile os_timer_t some_timer;

    void some_timerfunc(void *arg)
    {
      //Do blinky stuff
      if (GPIO_REG_READ(GPIO_OUT_ADDRESS) & (1 << pin))
      {
        // set gpio low
        gpio_output_set(0, (1 << pin), 0, 0);
      }
      else
      {
        // set gpio high
        gpio_output_set((1 << pin), 0, 0, 0);
      }
    }

    LOCAL void ICACHE_FLASH_ATTR set_blink_timer() {
        // Start a timer for the flashing of the LED on GPIO 4, running continuously.
      os_timer_setfn(&some_timer, (os_timer_func_t *)some_timerfunc, NULL);
      os_timer_arm(&some_timer, 500, 1);
    }


    extern "C"
    {
      void ICACHE_FLASH_ATTR user_init()
      {
        // init gpio subsytem
        gpio_init();

        // configure UART TXD to be GPIO1, set as output
        PIN_FUNC_SELECT(PERIPHS_IO_MUX_U0TXD_U, FUNC_GPIO1);
        gpio_output_set(0, 0, (1 << pin), 0);

        // setup timer (500ms, repeating)
        set_blink_timer();
      }
    }

    Y… ¡ya podemos utilizar clases! Bueno, y algunas características de C++, incluso de C++11. la pega es que no podemos utilizar la biblioteca std, ni string, ni map, ni vector… una pena. De todas formas, no es imposible. Es un problema al enlazar la biblioteca estándar de C++, porque necesita funciones de C. Podríamos crear dichas funciones con las herramientas que tenemos o buscar versiones reducidas de las clases que necesitamos (que seguramente ocupen menos en memoria), o utilizar la construcción desde Arduino (aunque he de escudriñar un poco más el proceso que sigue Arduino para incluir la biblioteca de C++)

    Un Makefile y estructura genericos para nuestros programas

    Después de investigar un tiempo con el dispositivo en mis manos y probar algunas formas de construir fácilmente nuestros programas. Además, hacerlos de una forma independiente del editor o IDE que estemos utilizando. Lo primero será crear una estructura de directorios y archivos lógica y manejable para poder utilizar sin problemas. Muchas de las estructuras y ejemplos las estoy publicando en GitHub por lo que, si no hay ningún post nuevo sobre el tema, aquí encontraréis la última versión.

    La estructura básica de un proyecto, es decir, dentro del directorio del proyecto encontraremos:

    |
    |- user/ (directorio con los ficheros de nuestro programa)
    | |- user_main.c (fichero principal del programa)
    | |- user_config.h (fichero de configuración (claves, ssid, contraseñas, o todas esas cosas relativas a configuración. Este archivo podrá estar vacío.)
    | |_ (resto de ficheros del programa, .c y .h)
    |- include/ (ficheros .h interesantes)
    | |- espmissingincludes.h (el fichero que vimos antes con los símbolos que no tenía el SDK)
    | |- driver/ (includes de drivers)
    | |_ (resto de includes, ficheros de definiciones de bibliotecas, etc)
    |- driver/ (drivers de dispositivos, tanto software como hardware que usemos en nuestro proyecto)
    |- build/ (ficheros compilados, será un directorio interno donde se almacene todo lo que vayamos compilando)
    |- firmware/ (ficheros de firmware, listos para ser copiados al chip)
    |- Makefile (fichero con las instrucciones para construir el programa)
    |- README (fichero de texto con instrucciones, explicaciones y demás)
    |- …

    Podremos incluir ficheros como LICENSE (con la licencia), CHANGELOG (con el registro de cambios) o algún otro script más que nos ayude a construir. Así como un directorio lib/ donde podamos copiar todas esas bibliotecas extra que vayamos a utilizar en el proyecto. Pero creo que ésta es una buena estructura básica para empezar.

    Con drivers me refiero a todo lo referente al UART, SPI, temporizador, o incluso herramientas de control o incluso programas que obtengan resultados de sensores, hablen con displays, etc.

    Ahora os pongo un ejemplo de Makefile:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    # Makefile for ESP8266 projects
    #
    # Thanks to:
    # - zarya
    # - Jeroen Domburg (Sprite_tm)
    # - Christian Klippel (mamalala)
    # - Tommie Gannert (tommie)
    #
    # Changelog:
    # - 2014-10-06: Changed the variables to include the header file directory
    # - 2014-10-06: Added global var for the Xtensa tool root
    # - 2014-11-23: Updated for SDK 0.9.3
    # - 2014-12-25: Replaced esptool by esptool.py

    # Output directors to store intermediate compiled files
    # relative to the project directory
    BUILD_BASE  = build
    FW_BASE     = firmware

    # base directory for the compiler
    XTENSA_TOOLS_ROOT ?= ~/local/opt/stand/esp-open-sdk/xtensa-lx106-elf/bin

    # base directory of the ESP8266 SDK package, absolute
    SDK_BASE    ?= ~/local/opt/stand/esp-open-sdk/sdk

    # esptool.py path and port
    ESPTOOL     ?= esptool.py
    ESPPORT     ?= /dev/ttyACM0

    # name for the target project
    TARGET      = app

    # which modules (subdirectories) of the project to include in compiling
    MODULES     = driver user
    EXTRA_INCDIR    = driver include

    # libraries used in this project, mainly provided by the SDK
    LIBS        = c gcc hal pp phy net80211 lwip wpa main

    # compiler flags using during compilation of source files
    CFLAGS      = -Os -O2 -Wpointer-arith -Wundef -Werror -Wl,-EL -fno-inline-functions -nostdlib -mlongcalls -mtext-section-literals  -D__ets__ -DICACHE_FLASH

    # linker flags used to generate the main object file
    LDFLAGS     = -nostdlib -Wl,--no-check-sections -u call_user_start -Wl,-static

    # linker script used for the above linkier step
    LD_SCRIPT   = eagle.app.v6.ld

    # various paths from the SDK used in this project
    SDK_LIBDIR  = lib
    SDK_LDDIR   = ld
    SDK_INCDIR  = include include/json

    # we create two different files for uploading into the flash
    # these are the names and options to generate them
    FW_FILE_1_ADDR  = 0x00000
    FW_FILE_2_ADDR  = 0x10000

    # select which tools to use as compiler, librarian and linker
    CC      := $(XTENSA_TOOLS_ROOT)/xtensa-lx106-elf-gcc
    AR      := $(XTENSA_TOOLS_ROOT)/xtensa-lx106-elf-ar
    LD      := $(XTENSA_TOOLS_ROOT)/xtensa-lx106-elf-gcc



    ####
    #### no user configurable options below here
    ####
    SRC_DIR     := $(MODULES)
    BUILD_DIR   := $(addprefix $(BUILD_BASE)/,$(MODULES))

    SDK_LIBDIR  := $(addprefix $(SDK_BASE)/,$(SDK_LIBDIR))
    SDK_INCDIR  := $(addprefix -I$(SDK_BASE)/,$(SDK_INCDIR))

    SRC     := $(foreach sdir,$(SRC_DIR),$(wildcard $(sdir)/*.c))
    OBJ     := $(patsubst %.c,$(BUILD_BASE)/%.o,$(SRC))
    LIBS        := $(addprefix -l,$(LIBS))
    APP_AR      := $(addprefix $(BUILD_BASE)/,$(TARGET)_app.a)
    TARGET_OUT  := $(addprefix $(BUILD_BASE)/,$(TARGET).out)

    LD_SCRIPT   := $(addprefix -T$(SDK_BASE)/$(SDK_LDDIR)/,$(LD_SCRIPT))

    INCDIR  := $(addprefix -I,$(SRC_DIR))
    EXTRA_INCDIR    := $(addprefix -I,$(EXTRA_INCDIR))
    MODULE_INCDIR   := $(addsuffix /include,$(INCDIR))

    FW_FILE_1   := $(addprefix $(FW_BASE)/,$(FW_FILE_1_ADDR).bin)
    FW_FILE_2   := $(addprefix $(FW_BASE)/,$(FW_FILE_2_ADDR).bin)

    V ?= $(VERBOSE)
    ifeq ("$(V)","1")
    Q :=
    vecho := @true
    else
    Q := @
    vecho := @echo
    endif

    vpath %.c $(SRC_DIR)

    define compile-objects
    $1/%.o: %.c
        $(vecho) "CC $$<"
        $(Q) $(CC) $(INCDIR) $(MODULE_INCDIR) $(EXTRA_INCDIR) $(SDK_INCDIR) $(CFLAGS) -c $$< -o $$@
    endef

    .PHONY: all checkdirs flash clean

    all: checkdirs $(TARGET_OUT) $(FW_FILE_1) $(FW_FILE_2)

    $(FW_BASE)/%.bin: $(TARGET_OUT) | $(FW_BASE)
        $(vecho) "FW $(FW_BASE)/"
        $(Q) $(ESPTOOL) elf2image -o $(FW_BASE)/ $(TARGET_OUT)

    $(TARGET_OUT): $(APP_AR)
        $(vecho) "LD $@"
        $(Q) $(LD) -L$(SDK_LIBDIR) $(LD_SCRIPT) $(LDFLAGS) -Wl,--start-group $(LIBS) $(APP_AR) -Wl,--end-group -o $@

    $(APP_AR): $(OBJ)
        $(vecho) "AR $@"
        $(Q) $(AR) cru $@ $^

    checkdirs: $(BUILD_DIR) $(FW_BASE)

    $(BUILD_DIR):
        $(Q) mkdir -p $@

    $(FW_BASE):
        $(Q) mkdir -p $@

    flash: $(FW_FILE_1) $(FW_FILE_2)
        $(ESPTOOL) --port $(ESPPORT) write_flash $(FW_FILE_1_ADDR) $(FW_FILE_1) $(FW_FILE_2_ADDR) $(FW_FILE_2)

    clean:
        $(Q) rm -rf $(FW_BASE) $(BUILD_BASE)

    $(foreach bdir,$(BUILD_DIR),$(eval $(call compile-objects,$(bdir))))

    Para este archivo, en cada proyecto tendremos que cambiar XTENSA_TOOLS_ROOT y SDK_BASE para que apunten a las rutas correctas dentro de nuestro ordenador.

    Con este Makefile haremos que con sencillos comandos podamos realizar diferentes tareas:

    • make : Construye el programa (compila, linka y crea los binarios que vamos a flashear).
    • make clean : Limpia los ficheros objeto creados, para volver a construir desde cero.
    • make flash : Sube los binarios al chip.
    • make flash ESPPORT=/dev/ttyUSB0 : Cambia el puerto serie para flashear.

    Ejemplo de uso del UART (el serial USB)

    Encontramos el ejemplo aquí. Para hacer la comunicación por los puertos RX y TX del UART0 he copiado el driver UART que encontramos en el SDK:

    • sdk/driver_lib/include/driver/uart.h a include/driver/
    • sdk/driver_lib/include/driver/uart_register.h a include/driver/
    • sdk/driver_lib/driver/uart.c a driver/

    De esta forma tendremos acceso al UART. Podríamos programarlo nosotros, pero el SDK ya trae una buena forma de hacerlo, y una serie de constantes predefinidas que serán de mucha ayuda. Eso sí, ahora tendremos que hacer algunos cambios en el driver uart.c:

    Establecer prioridad de la tarea

    Debemos ajustar la prioridad de acuerdo a nuestro programa, encontraremos al principio del archivo algo así:

    1
    2
    /*it might conflict with your task, if so,please arrange the priority of different task,  or combine it to a different event in the same task. */
    #define uart_recvTaskPrio        1

    La directiva uart_recvTaskPrio deberá valer 0, 1, 2 o 3. Intenta que no haya conflictos con las tareas que ya tenga tu programa.

    Declaración de la tarea externa

    Lo siguiente será crear una declaración de la tarea de recepción. Aunque el driver ya trae una tarea que se lanza cuando se reciben mensajes desde el UART, queremos tener una tarea propia para procesarlos a nuestro gusto. Y esa tarea la definiremos en el código fuente de nuestro programa más adelante. La tarea actual se llama uart_recvTask, así que podemos borrarlo si queremos.

    1
    2
    // uart_rx_task is defined in the main code.
    void uart_rx_task(os_event_t *);

    Inicialización de la tarea

    En uart_init() debemos llamar a la tarea correcta:

    1
        system_os_task(uart_rx_task, uart_recvTaskPrio, uart_recvTaskQueue, uart_recvTaskQueueLen);  //demo with a task to process the uart data

    Código del programa

    Dejo aquí el código fuente de user_main.c:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    /* Picked up a blinky.c and converted it in an ECHO por ESP8266 UART. */
    #include "ets_sys.h"
    #include "osapi.h"
    #include "gpio.h"
    #include "os_type.h"
    #include "user_config.h"
    #include "driver/uart.h"
    #include "user_interface.h"

    #define BLINK_BIT BIT2
    #define MAX_RX_DATA_SIZE 32

    #define user_procTaskPrio        2
    #define user_procTaskQueueLen    1
    os_event_t    user_procTaskQueue[user_procTaskQueueLen];

    static void user_procTask(os_event_t *events);

    static volatile os_timer_t some_timer;

    void some_timerfunc(void *arg)
    {
        //Do blinky stuff
        if (GPIO_REG_READ(GPIO_OUT_ADDRESS) & BLINK_BIT)
        {
                //Set GPIO2 to LOW
                gpio_output_set(0, BLINK_BIT, BLINK_BIT, 0);
        }
        else
        {
                //Set GPIO2 to HIGH
                gpio_output_set(BLINK_BIT, 0, BLINK_BIT, 0);
        }
    }

    void post_rx_action(char* data)
    {
        ets_printf ("Received: %s\n", data);
    }

    void ICACHE_FLASH_ATTR uart_rx_task(os_event_t *events)
    {
        if (events->sig == 0) {
            // Sig 0 is a normal receive. Get how many bytes have been received.
            uint8_t rx_len = (READ_PERI_REG(UART_STATUS(UART0)) >> UART_RXFIFO_CNT_S) & UART_RXFIFO_CNT;

            // Parse the characters, taking any digits as the new timer interval.
            char rx_data[MAX_RX_DATA_SIZE];
            uint8_t i;
            for (i=0; i < MAX_RX_DATA_SIZE-1; i++) {
                rx_data[i] = READ_PERI_REG(UART_FIFO(UART0)) & 0xFF;
            }
            rx_data[(rx_len>MAX_RX_DATA_SIZE-1)?MAX_RX_DATA_SIZE-1:rx_len]='\0';
            post_rx_action(rx_data);

            // Clear the interrupt condition flags and re-enable the receive interrupt.
            WRITE_PERI_REG(UART_INT_CLR(UART0), UART_RXFIFO_FULL_INT_CLR | UART_RXFIFO_TOUT_INT_CLR);
            uart_rx_intr_enable(UART0);
        }
    }

    //Do nothing function
    static void ICACHE_FLASH_ATTR
    user_procTask(os_event_t *events)
    {
        os_delay_us(10);
    }

    //Init function
    void ICACHE_FLASH_ATTR user_init()
    {
        // Disable debug
        system_set_os_print(0);

        // Initialize the GPIO subsystem.
        gpio_init();
        uart_init(BIT_RATE_115200, BIT_RATE_115200);

        // Disable WiFi
        wifi_station_disconnect();
        wifi_set_opmode(NULL_MODE);
        wifi_set_sleep_type(MODEM_SLEEP_T);
           
        //Set GPIO2 to output mode (BLINK_BIT)
        PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO2_U, FUNC_GPIO2);

        //Set GPIO2 low
        gpio_output_set(0, BLINK_BIT, BLINK_BIT, 0);

        //Disarm timer
        os_timer_disarm(&some_timer);

        //Setup timer
        os_timer_setfn(&some_timer, (os_timer_func_t *)some_timerfunc, NULL);

        //Arm the timer
        os_timer_arm(&some_timer, 1000, 1);
       
        //Start os task
        /* system_os_task(user_procTask, user_procTaskPrio,user_procTaskQueue, user_procTaskQueueLen); */
    }

    Vemos que en este ejemplo desactivo por completo la red inalámbrica (como no la queremos, ahorra energía. También desactivo la depuración por lo que os_printf() no tendría efecto, además, nos quitamos muchos mensajes que envía el dispositivo con el fin de saber lo que está haciendo el chip o por qué ha fallado… no está bien para hacer pruebas, pero sí para producción y, por ejemplo si nos vamos a comunicar mediante este puerto serie con otro chip (al otro chip tal vez no le guste demasiado tener información que no sabe interpretar).

    Intérprete de comandos

    Vamos a implementar un pequeño intérprete de comandos en el que podamos enviar y recibir texto por el UART y obtengamos un resultado. Como puede ser una respuesta en función de lo pedido. Este ejemplo puede generar varios comandos, cada uno asociado a un callback:

    • HELLO: Responderá “Hello Dude!”
    • REPEAT [loquesea]: Responderá “Repeating: loquesea
    • INFO: Responderá con información sobre algunas variables del sistema como tiempo online, memoria libre, versión del SDK, etc.

    Los comandos son ampliables fácilmente, sólo hay que añadir un callback y llamar a add_command(). De todas formas es algo muy sencillo y no creo que sea apto para estar en producción. He creado varios archivos.

    utils.h:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    #ifndef UTILS_H
    #define UTILS_H

    #include <c_types.h>

    char * dtostrf(double number, signed char width, unsigned char prec, char *s);
    int os_snprintf(char* buffer, size_t size, const char* format, ...);
    char* timeInterval(char* buffer, size_t bufferSize, unsigned long seconds);

    #endif

    utils.c:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    #include "espmissingincludes.h"
    #include "math.h"
    #include "ets_sys.h"
    #include "osapi.h"
    #include <stdarg.h>

    char * dtostrf(double number, signed char width, unsigned char prec, char *s) {
        bool negative = false;

        if (isnan(number)) {
            strcpy(s, "nan");
            return s;
        }
        if (isinf(number)) {
            strcpy(s, "inf");
            return s;
        }

        char* out = s;

        int fillme = width; // how many cells to fill for the integer part
        if (prec > 0) {
            fillme -= (prec+1);
        }
       // Handle negative numbers
        if (number < 0.0) {
            negative = true;
            fillme--;
            number = -number;
        }

        // Round correctly so that print(1.999, 2) prints as "2.00"
        // I optimized out most of the divisions
        double rounding = 2.0;
        for (uint8_t i = 0; i < prec; ++i)
            rounding *= 10.0;
        rounding = 1.0 / rounding;

        number += rounding;

        // Figure out how big our number really is
        double tenpow = 1.0;
        int digitcount = 1;
        while (number >= 10.0 * tenpow) {
            tenpow *= 10.0;
            digitcount++;
        }
        number /= tenpow;
        fillme -= digitcount;

        // Pad unused cells with spaces
        while (fillme-- > 0) {
            *out++ = ' ';
        }

        // Handle negative sign
        if (negative) *out++ = '-';

        // Print the digits, and if necessary, the decimal point
        digitcount += prec;
        int8_t digit = 0;
        while (digitcount-- > 0) {
            digit = (int8_t)number;
            if (digit > 9) digit = 9; // insurance
            *out++ = (char)('0' | digit);
            if ((digitcount == prec) && (prec > 0)) {
                *out++ = '.';
            }
            number -= digit;
            number *= 10.0;
        }

        // make sure the string is terminated
        *out = 0;
        return s;
    }

    int os_snprintf(char* buffer, size_t size, const char* format, ...) {
        int ret;
        va_list arglist;
        va_start(arglist, format);
        ret = ets_vsnprintf(buffer, size, format, arglist);
        va_end(arglist);
        return ret;
    }

    char* timeInterval(char* buffer, size_t bufferSize, unsigned long seconds)
    {
        uint16_t hours = seconds / 3600,
                rem = seconds % 3600,
                minutes = rem / 60,
                secs = rem % 60;
       
        os_snprintf (buffer, bufferSize, "%d:%d:%d", hours, minutes, secs);
       
        return buffer;
    }

    La función dtostrf() la he cogido de las bibliotecas de Arduino.

    user_main.c:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    /* Picked up a blinky.c and converted it in an ECHO por ESP8266 UART. */
    #include "ets_sys.h"
    #include "osapi.h"
    #include "gpio.h"
    #include "os_type.h"
    #include "user_config.h"
    #include "driver/uart.h"
    #include "user_interface.h"
    #include "mem.h"
    #include "espmissingincludes.h"
    #include <ctype.h>
    #include "utils.h"

    #define BLINK_BIT BIT2
    #define MAX_RX_DATA_SIZE 32
    #define MAX_CMD_LEN 9
    #define MAX_COMMANDS 10

    #define user_procTaskPrio        2
    #define user_procTaskQueueLen    1
    os_event_t    user_procTaskQueue[user_procTaskQueueLen];

    static void user_procTask(os_event_t *events);
    static volatile os_timer_t some_timer;

    typedef int (*command_function)(char*);

    struct TCommand
    {
        int len;
        char command[MAX_CMD_LEN];
        command_function callback;
    };

    struct TCommand* commands[MAX_COMMANDS];
    uint8_t Ncommands = 0;

    int command_hello(char* args)
    {
        ets_printf("Hello dude\n");
    }

    int command_repeat(char* args)
    {
        if ( (args) && (*args) )
            ets_printf("Repeating: %s\n", args);
        else
            ets_printf("Nothing to repeat\n");
    }

    char* humanSize(char* buffer, size_t bufferSize, long double size, short precission)
    {
        static const char* units[10]={"bytes","Kb","Mb","Gb","Tb","Pb","Eb","Zb","Yb","Bb"};

        uint8_t i= 0;

        while (size>1024) {
            size = size /1024;
            i++;
        }

        if (precission < 0)
            precission=3;

        char temp[32];
       
        os_snprintf(buffer, bufferSize, "%s%s", dtostrf(size, 6, precission, temp), units[i]);
        return buffer;
    }

    int command_info(char* args)
    {
        char tempbuffer[32];
        ets_printf ("SDK version: %s \n", system_get_sdk_version());
        ets_printf ("Chip ID: %d \n", system_get_chip_id());
        ets_printf ("Free heap: %lu\n", system_get_free_heap_size());
        ets_printf ("Free heap: %s\n", humanSize(tempbuffer, 32, system_get_free_heap_size(), 3));
        ets_printf ("System time: %lu (%s)\n", system_get_time(), timeInterval(tempbuffer, 32, system_get_time()/1000000));
        ets_printf ("CPU Frequency: %uMHz\n", system_get_cpu_freq());
    }

    void some_timerfunc(void *arg)
    {
        //Do blinky stuff
        if (GPIO_REG_READ(GPIO_OUT_ADDRESS) & BLINK_BIT)
        {
                //Set GPIO2 to LOW
                gpio_output_set(0, BLINK_BIT, BLINK_BIT, 0);
        }
        else
        {
                //Set GPIO2 to HIGH
                gpio_output_set(BLINK_BIT, 0, BLINK_BIT, 0);
        }
    }

    void add_command(char* command, command_function callback)
    {
       
        if (Ncommands<MAX_COMMANDS) {
            commands[Ncommands] = (struct TCommand*)os_malloc(sizeof(struct TCommand));
            commands[Ncommands]->len = strlen(command);
            strcpy(commands[Ncommands]->command, command);
            commands[Ncommands]->callback = callback;
            Ncommands++;
        }
    }

    // https://poesiabinaria.net/2010/03/trim-un-gran-amigo-php-c-c/
    char *trim(char *s)
    {
      char *start = s;

      /* Nos comemos los espacios al inicio */
      while(*start && isspace(*start))
        ++start;

      char *i = start;
      char *end = start;

      /* Nos comemos los espacios al final */
      while(*i)
      {
        if( !isspace(*(i++)) )
          end = i;
      }

      /* Escribimos el terminados */
      *end = 0;

      return start;
    }

    void post_rx_action(char* data)
    {
        char* _data = trim(data);
        char* command=_data;
        char* args = strchr(_data, ' ');
        if (args!=NULL)
            {
                *args = '\0';
                ++args;
            }
        size_t cmdlen = os_strlen(_data);
        uint8_t i;
        for (i=0; i<Ncommands; ++i)
            {
                if ( (commands[i]->len == cmdlen) && (strcmp (commands[i]->command, _data)==0) )
                    commands[i]->callback(args);                                                               
            }
    }

    void ICACHE_FLASH_ATTR uart_rx_task(os_event_t *events)
    {
        if (events->sig == 0) {
            // Sig 0 is a normal receive. Get how many bytes have been received.
            uint8_t rx_len = (READ_PERI_REG(UART_STATUS(UART0)) >> UART_RXFIFO_CNT_S) & UART_RXFIFO_CNT;

            // Parse the characters, taking any digits as the new timer interval.
            char rx_data[MAX_RX_DATA_SIZE];
            uint8_t i;
            for (i=0; i < MAX_RX_DATA_SIZE-1; i++) {
                rx_data[i] = READ_PERI_REG(UART_FIFO(UART0)) & 0xFF;
            }
            rx_data[(rx_len>MAX_RX_DATA_SIZE-1)?MAX_RX_DATA_SIZE-1:rx_len]='\0';
            post_rx_action(rx_data);

            // Clear the interrupt condition flags and re-enable the receive interrupt.
            WRITE_PERI_REG(UART_INT_CLR(UART0), UART_RXFIFO_FULL_INT_CLR | UART_RXFIFO_TOUT_INT_CLR);
            uart_rx_intr_enable(UART0);
        }
    }

    //Do nothing function
    static void ICACHE_FLASH_ATTR
    user_procTask(os_event_t *events)
    {
        os_delay_us(10);
    }

    //Init function
    void ICACHE_FLASH_ATTR user_init()
    {
        // Disable debug
        system_set_os_print(1);

        // Initialize the GPIO subsystem.
        gpio_init();
        uart_init(BIT_RATE_115200, BIT_RATE_115200);

        // Disable WiFi
        wifi_station_disconnect();
        wifi_set_opmode(NULL_MODE);
        wifi_set_sleep_type(MODEM_SLEEP_T);

        // Welcome message
        ets_printf("\n\nInitialized... please insert command\n");
        // Add commands
        add_command("HELLO", command_hello);
        add_command("REPEAT", command_repeat);
        add_command("INFO", command_info);
       
        //Set GPIO2 to output mode (BLINK_BIT)
        PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO2_U, FUNC_GPIO2);

        //Set GPIO2 low
        gpio_output_set(0, BLINK_BIT, BLINK_BIT, 0);

        //Disarm timer
        os_timer_disarm(&some_timer);

        //Setup timer
        os_timer_setfn(&some_timer, (os_timer_func_t *)some_timerfunc, NULL);

        //Arm the timer
        os_timer_arm(&some_timer, 1000, 1);
       
        //Start os task
        /* system_os_task(user_procTask, user_procTaskPrio,user_procTaskQueue, user_procTaskQueueLen); */
    }

    De todas formas, en la página de GitHub lo encontráis con Makefile y todo.

    Problemas y soluciones

    Todo puede fallar. Incluso esas cosas que aparentemente funcionan bien pueden romperse sin venir a cuento de la forma más tonta.

    Fallos intermitentes recién programado

    El principal problema que me he encontrado es la estabilidad de la tensión en los pins de entrada del chip. A veces fluctúa y causa un mal funcionamiento. Parece que no carga bien los programas, o no los ejecuta por completo. La solución fue colocar un condensador entre VCC y GND, así cuando haya un pico de consumo y la fuente no pueda entregar corriente (un Arduino, o una pila) el ESP8266 seguirá funcionando. Y por ejemplo, se puede utilizar más corriente de lo normal cuando se están buscando redes, o se está intentando conectar con alguna WiFi no muy cercana. Sí, podemos limitar la potencia, pero no tiene gracia.

    Se reinicia solo de vez en cuando

    El otro problema es el Watchdog Timer. Eso es, un detector de que el chip se ha quedado colgado y esto puede suceder si no se le envía una señal al watchdog cada cierto tiempo. Si utilizamos algoritmos muy pesados puede que nos suceda. En proyectos definitivos es muy aconsejable tener el watchdog activo, pero para hacer pruebas, podemos desactivarlo llamando a:

    1
      ets_wdt_disable();

    Fallos al ejecutar un programa recién flasheado

    En ocasiones, sobre todo si estamos haciendo muchas pruebas, o nos hemos confundido en las direcciones del chip que había que flashear, puede que todo deje de funcionar y que al conectar el chip dé un fallo de depuración, o se vuelva loco a mandar cosas sin sentido por el UART, o se caliente mucho de repente y no haga lo que queremos. Puede que hayamos subido algo mal. Podríamos empezar flasheando de nuevo un firmware oficial y cuando funcione volver a flashear nuestro programa.

    The post Compilando y subiendo los primeros programas al ESP8266. Usando GPIO y UART- appeared first on Poesía Binaria.

    » Leer más, comentarios, etc...

    Una sinfonía en C#

    ¿Cómo hacer un test de HttpClient?

    agosto 30, 2017 10:14

    Cuando hacemos prueba unitarias (AKA unit testing) uno de los objetivos es aislarnos de los agentes externos para crear un ambiente controlado y que nuestros test se ejecuten bajo condiciones repetibles. Bien, dicho esto uno de las clases que al menos yo me encuentro más seguido es HttpClient, que no hace otra cosa que hacer llamadas HTTP, por desgracia al día de hoy no existe una interfaz para usarla para hacer un mock, entonces tenemos que echar mano de cosas como ésta:

    Primera aproximación, hacer un wrapper:

    public class HttpClientWrapper
    {
        public Uri BaseAddress { get; set; }
        public HttpRequestHeaders DefaultRequestHeaders { get; }
        public TimeSpan Timeout { get; set; }
        public long MaxResponseContentBufferSize { get; set; }
    
        public void CancelPendingRequests();
        public Task DeleteAsync(string requestUri);
        public Task DeleteAsync(string requestUri, CancellationToken cancellationToken);
    ....
    

    Algo así pero bien, copiar toooodos los métodos que queremos usar (pero marcados como virtuales) y tener dentro una instancia del verdadero HttpClient a la cual simplemente llamamos en cada uno de nuestro métodos, entonces cuando queremos probar simplemente hacemos un mock donde sobre-escribimos los método que queremos probar.

    var mocked = new Mock();
    var client = mocked.Object;
    
    mocked.Setup(i => i.SendAsync(It.IsAny())).Returns(Task.FromResult( new HttpResponseMessage()));
    

    Como imaginamos hacer un wrapper suena a mucho trabajo y, seamos realistas, es poco elegante.

    Opción dos, heredar HttpMessageHandler

    Algo que aprendí hace poco es la existencia de la clase HttpMessageHandler, que es internamente utilizado por HttpClient para hacer las llamadas, y además se puede sobre-escribir.

    Entonces, podemos hacer dos cosas, primero crear una versión para probar, simplemente una clase para testing, que nos permita configurar cómo deber responder y después simplemente inyectar la clase en el constructor de HttpClient

    public class OffLineResponseHandler : DelegatingHandler
    {
        private readonly Dictionary responses = new Dictionary();
        public void AddResponse(Uri uri, HttpResponseMessage responseMessage)
        {
            responses.Add(uri, responseMessage);
        }
        public void AddOkResponse(Uri uri, string content)
        {
            var message = new HttpResponseMessage(HttpStatusCode.OK);
            message.Content = new StringContent(content);
            responses.Add(uri, message);
        }
        public void AddOkResponse(Uri uri)
        {
            this.AddOkResponse(uri, string.Empty);
        }
        public void AddServerErrorResponse(Uri uri)
        {
            var message = new HttpResponseMessage(HttpStatusCode.InternalServerError);
            responses.Add(uri, message);
        }
        public void RemoveAll()
        {
            this.responses.Clear();
        }
        protected async override Task SendAsync(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)
        {
            if (responses.ContainsKey(request.RequestUri))
            {
                return responses[request.RequestUri];
            }
            else
            {
                return await Task.FromResult(new HttpResponseMessage(HttpStatusCode.NotFound) { RequestMessage = request });
            }
        }
    }
    

    Esta solución es bastante elegante, permite configurar respuestas con los métodos AddOkResponse y AddServerErrorResponse se agregan a un diccionario que permite grabar respuestas, entonces cuando queremos probar hacemos algo así:

    [TestMethod]
    public void AddOkResponse()
    {
        var target = new HttpClientTest.OffLineResponseHandler();
        var url = new Uri("http://www.fake.com");
    
        var client = new System.Net.Http.HttpClient(target);
    
        target.AddOkResponse(url);
    
        var result = client.GetAsync(url).Result;
    
        Assert.AreEqual(HttpStatusCode.OK, result.StatusCode);
    }
    

    En este ejemplo inyectamos el OfflineResponseHandler y lo inyectamos en el constructor de HttpClient, por otro lado configuramos las respuestas que queremos que de dependiendo de la URL.

    La otra opción (es menos trabajo) es una clase que herede de HttpMessageHandler con los métodos Send y SendAsync marcados como virtuales para poder usar un framework de mocking del siguiente modo:

    public class FakeHttpMessageHandler : HttpMessageHandler
    {
        public virtual HttpResponseMessage Send(HttpRequestMessage request)
        {
            throw new NotImplementedException("Mock this please");
        }
    
        protected override Task SendAsync(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)
        {
            return Task.FromResult(Send(request));
        }
    }
    

    Entonces cuando queremos probar con simplemente configuramos el framework de mocking así:

    var fakeHttpMessageHandler = new Mock { CallBase = true };
    
    fakeHttpMessageHandler.Setup(f => f.Send(It.IsAny())).Returns(new HttpResponseMessage
    {
        StatusCode = HttpStatusCode.OK,
        Content = new StringContent("Hi!")
    });
    
    

    Y listo, mismo efecto, simplemente inyectamos en el constructor del HttpClient y tirar millas, podemos probar con bastante facilidad.

    Dejo un link con el código de ambas utilidades, nos leemos.

    » Leer más, comentarios, etc...

    Adrianistán

    Autómatas celulares unidimensionales en Python

    agosto 28, 2017 10:19

    Estaba yo leyendo este verano un libro titulado Think Complexity cuando en un capítulo empezó a hablar de los autómatas celulares unidimensionales. El tema me interesó y por eso esta entrada. Veamos primero a qué nos referimos cuando hablamos de esto.

    Cuando hablamos a autómatas celulares, nos referimos a pequeñas entidades independientes pero que interaccionan entre sí. Celulares porque son la unidad elemental del universo donde van a existir y autómatas porque deciden por ellas mismas, basadas en un conjunto de reglas predefinido, cuando el tiempo avanza de forma discreta (es decir, a pasos).

    Este concepto abstracto puede visualizarse con facilidad si nos imaginamos una rejilla. Cada celda es una célula capaz de cambiar su estado según su entorno.

    Los autómatas celulares fueron objeto de estudio de Stephen Wolfram, matemático conocido por haber diseñado el programa Mathemathica y Wolfram Alpha.

    Los autómatas celulares unidimensionales son aquellos que forman parte de un universo unidimensional. Es decir, cada célula tiene una vecina a su izquierda y a su derecha. En los bordes se pueden definir varios comportamientos pero el concepto no varía. Pensemos en ello como una tira de celdas.

    El estudio de estos autómatas es interesante, ya que pueden generarse patrones/situaciones muy complejas en base a unas reglas sencillas.

    ¿Cómo se definen las reglas?

    Wolfram usó un sistema para definir las reglas de estos autómatas que hoy conocemos como Wolfram Code. Se basa en definir una tabla con los estados presentes de la célula y sus vecinas así como el valor que deberá adoptar en esa situación la célula. Como Wolfram usó células con solo dos estados, todo está en binario, y la parte baja de la tabla es un número de 8 bits. Este número se suele pasar a decimal y así se identifica.

    Estados presentes 111 110 101 100 011 010 001 000
    Estado futuro 0 0 1 1 0 0 1 0

    Esta tabla representa la Regla 50, porque 00110010 en binario es 50.

    ¿Cómo se representan?

    Una manera muy interesante de representar estos autómatas es poner cada paso en una fila distinta dentro de una imagen.

    Una vez que sabemos esto vamos a hacer un programa en Python que nos permita observar la evolución de estos autómatas.

    Usaremos el procedimiento original, que es empezar con todos los estados de los autómatas en 0 salvo el del autómata central, que será 1.

    La clase Automata

    La clase autómata va a contener las reglas, así como un ID y el estado que posee. Además, por cuestiones técnicas conviene guardar el estado anterior que tuvo.

    class Automata(object):
    
        rules = list()
    
        def __init__(self,idx=0):
            self.idx = idx
            self.state = False
            self.statePrev = False
    

    Como podéis ver, rules no lleva self, es decir, va a ser una variable compartida entre todas las instancias de Automata. Esto es porque las reglas son idénticas a todos los autómatas.

    La clase World

    Ahora vamos a definir el universo donde residen estos autómatas. Este universo almacena una lista con los autómatas, se encarga de actualizarlos según las normas y de dibujarlos usando PIL. También he insertado el código que codifica las normas según el número en decimal.

    class World(object):
        def __init__(self,rule=50):
            self.rule = rule
            self.im = Image.new("L",(WIDTH,HEIGHT))
            self.data = np.zeros(WIDTH*HEIGHT,dtype=np.uint8)
            b = bin(rule)[2:].zfill(8)
            Automata.rules = [True if c == "1" else False for c in b]
            self.list = list()
            self.step = 0
        
        def add(self):
            automata = Automata(len(self.list))
            self.list.append(automata)
    
        def update(self):
            for automata in self.list:
    
                automata.statePrev = automata.state
                p = self.list[automata.idx - 1].statePrev if automata.idx > 0 else False
                n = self.list[automata.idx + 1].state if automata.idx < len(self.list)-1 else False
                s = automata.state
    
                if p and s and n:
                    automata.state = automata.rules[0]
                elif p and s and not n:
                    automata.state = automata.rules[1]
                elif p and not s and n:
                    automata.state = automata.rules[2]
                elif p and not s and not n:
                    automata.state = automata.rules[3]
                elif not p and s and n:
                    automata.state = automata.rules[4]
                elif not p and s and not n:
                    automata.state = automata.rules[5]
                elif not p and not s and n:
                    automata.state = automata.rules[6]
                elif not p and not s and not n:
                    automata.state = automata.rules[7]
                
    
        def draw_row(self):
            if self.step == 0:
                middle = len(self.list) // 2
                self.list[middle].state = True
            for i,automata in enumerate(self.list):
                if automata.state:
                    self.data[self.step*HEIGHT+i] = 255
            self.step += 1
        def save(self):
            self.im.putdata(self.data)
            self.im.save("RULE-%d.png" % self.rule)
    

    Con esto ya lo tenemos casi todo. Ahora faltaría poner en marcha todo. La idea es simplemente crear una instancia de World, hacer unas cuantas llamadas a add, y después ir haciendo el ciclo update/draw_row. Una vez hayamos acabado, hacemos save y obtendremos un PNG con la imagen.

    Código completo

    import numpy as np
    from PIL import Image
    
    WIDTH = 5001
    HEIGHT = 5001
    
    class Automata(object):
    
        rules = list()
    
        def __init__(self,idx=0):
            self.idx = idx
            self.state = False
            self.statePrev = False
    
    class World(object):
        def __init__(self,rule=50):
            self.rule = rule
            self.im = Image.new("L",(WIDTH,HEIGHT))
            self.data = np.zeros(WIDTH*HEIGHT,dtype=np.uint8)
            b = bin(rule)[2:].zfill(8)
            Automata.rules = [True if c == "1" else False for c in b]
            print(Automata.rules)
            self.list = list()
            self.step = 0
        
        def add(self):
            automata = Automata(len(self.list))
            self.list.append(automata)
    
        def update(self):
            for automata in self.list:
    
                automata.statePrev = automata.state
                p = self.list[automata.idx - 1].statePrev if automata.idx > 0 else False
                n = self.list[automata.idx + 1].state if automata.idx < len(self.list)-1 else False
                s = automata.state
    
                if p and s and n:
                    automata.state = automata.rules[0]
                elif p and s and not n:
                    automata.state = automata.rules[1]
                elif p and not s and n:
                    automata.state = automata.rules[2]
                elif p and not s and not n:
                    automata.state = automata.rules[3]
                elif not p and s and n:
                    automata.state = automata.rules[4]
                elif not p and s and not n:
                    automata.state = automata.rules[5]
                elif not p and not s and n:
                    automata.state = automata.rules[6]
                elif not p and not s and not n:
                    automata.state = automata.rules[7]
                
    
        def draw_row(self):
            if self.step == 0:
                middle = (len(self.list) // 2)
                self.list[middle].state = True
            for i,automata in enumerate(self.list):
                if automata.state:
                    self.data[self.step*HEIGHT+i] = 255
            self.step += 1
        def save(self):
            self.im.putdata(self.data)
            self.im.save("RULE-%d.png" % self.rule)
        def __str__(self):
            s = str()
            for l in self.list:
                s += "T" if l.state else "F"
            return s
    
    def world_run(rule):
        world = World(rule)
        for _ in range(WIDTH):
            world.add()
    
        for _ in range(HEIGHT):
            world.draw_row()
            world.update()
        world.save()
    
    def main():
        rule = input("Rule: ")
        try:
            rule = int(rule)
            if 255 >= rule >= 0:
                world_run(rule)
                print("Check for the generated RULE-%d.png file" % rule)
            else:
                raise ValueError
        except ValueError:
            print("Please, insert a number between 0 and 255")
            main()
    
    if __name__ == "__main__":
        main()
    

    Algunas reglas importantes

    Regla 30

    Una de las más importantes a nivel matemático. Ha sido objeto de mucho estudio, sin embargo no vamos a entrar en detalles más allá de su aspecto visual.

    Vista ampliada

    Regla 110

    Esta regla es también muy interesante. ¡Se demostró que era Turing completa!

    Vista en detalle

    Regla 126

    Esta regla no es tan importante, pero personalmente me parece muy bonita.

    Vista ampliada

    Reglas 57 y 99

    Son dos reglas isomorfas. Es decir, son en realidad la misma regla pero aplicada a lados distintos. Elijo estas dos porque se aprecia muy bien el isomorfismo.

    Regla 57
    Regla 99

    Regla 169

    Vista en detalle

    Regla 129

    Regla 90

    Es el famoso triángulo de Sierpinski.

    Regla 150

    Regla 105

    Esta regla no tiene isomorfo.

    En este artículo no he querido entrar en las complejidades matemáticas de todo esto. Es algo que todavía no entiendo así que no sería sincero por mi parte exponerlo.

    Bonus: Richard Feynman y Steve Jobs

    Quien me conoce sabe de sobra que uno de los personajes de la historia que más ha influido en mi vida es Richard Feynman. Debo reconocer que entré en un estado de éxtasis al descubrir que Feynman y Wolfram no solo trabajaron juntos, sino que lo hicieron alrededor de la regla 30 antes mostrada. También me sorprendió que Steve Jobs y Wolfram resultasen ser amigos de toda la vida. No dejo de sorprenderme de los contactos de ciertos personajes históricos entre sí.

    Feynman a la izquierda, Wolfram a la derecha

    La entrada Autómatas celulares unidimensionales en Python aparece primero en Blog - Adrianistan.eu.

    » Leer más, comentarios, etc...

    Bitácora de Javier Gutiérrez Chamorro (Guti)

    FileOptimizer y la comunidad

    agosto 25, 2017 07:42

    Hace unos meses publicaba Reflexiones acerca de FileOptimizer, donde os explicaba el gran esfuerzo que hace falta para mantener un producto como FileOptimizer, siendo totalmente gratis. En aquel momento, acababa de aparecer FileOptimizer 9, y desde entonces, hemos llegado ya a FileOptimizer 10, que además, celebra su quinto aniversario. 5 años en software, son un […]

    Artículo publicado originalmente en Bitácora de Javier Gutiérrez Chamorro (Guti)

    La entrada FileOptimizer y la comunidad aparece primero en Bitácora de Javier Gutiérrez Chamorro (Guti).

    » Leer más, comentarios, etc...

    Meta-Info

    ¿Que es?

    Planeta Código es un agregador de weblogs sobre programación y desarrollo en castellano. Si eres lector te permite seguirlos de modo cómodo en esta misma página o mediante el fichero de subscripción.

    rss subscripción

    Sponsors

    Puedes utilizar las siguientes imagenes para enlazar PlanetaCodigo:
    planetacodigo

    planetacodigo

    Si tienes un weblog de programación y quieres ser añadido aquí, envíame un email solicitándolo.

    Idea: Juanjo Navarro

    Diseño: Albin