Rutas
/
Julia
Julia
/
Ejercicios
/
Sensores de fábrica
Sensores de fábrica

Sensores de fábrica

Ejercicio de aprendizaje

Introducción

Por lo general, los programadores intentan escribir software perfecto, y por lo general fracasan.

Las cosas salen mal, de manera inesperada, y necesitamos poder hacerles frente.

Algunos diseñadores de lenguajes creen que la prioridad es detectar un error lo antes posible y, después, terminar la ejecución con un mensaje informativo que ayude a depurar.

Los lenguajes de ciencia de datos tienden a adoptar un enfoque más matizado. Algunos errores son tan graves que es necesario terminar de inmediato, pero a menudo es mejor señalar un problema como algo que hay que resolver más adelante y continuar la ejecución.

Vimos en el concepto Nada que Julia ofrece varios marcadores de posición para valores problemáticos: missing, NaN e Inf. Que estos sean mejor opción que terminar el programa en una situación concreta es algo que queda al criterio de quien programa.

Una cuestión de nomenclatura antes de entrar en detalles: la documentación de Julia considera que las palabras «error» y «excepción» son en gran medida intercambiables. El contenido que sigue puede ser igualmente incoherente.

Tipos de error estándar

En este punto del temario, seguro que ya has visto muchos mensajes de error de Julia. Por ejemplo:

julia> Int(3.14)
ERROR: InexactError: Int64(3.14)

Intentar convertir un número decimal en un entero implica una pérdida de precisión, así que obtenemos un InexactError.

InexactError es un tipo, uno de los varios (actualmente 25) que Julia incluye de serie. Todos son subtipos de Exception:

julia> supertype(InexactError)
Exception

throw()

Puede que algunos de los tipos de error estándar te resulten útiles para generarlos en tu propio código.

Como todos los tipos concretos, los errores tienen constructores. Aceptan una variedad de argumentos, así que consulta la documentación del que quieras usar.

julia> DomainError(42, "out of range")
DomainError(42, "out of range")

Para usar el error, envuelve el constructor en una función throw():

julia> throw(DomainError(42, "out of range"))
ERROR: DomainError with 42:
out of range

error()

Para un enfoque rápido y poco cuidadoso, la función error() puede resultar cómoda. Toma como argumento un string (o los componentes de un string):

julia> happy = false;
julia> happy || error("😞 something went wrong")
ERROR: 😞 something went wrong

Errores personalizados

Crear nuevos tipos de error es, en principio, muy fácil. Basta con añadir otro subtipo de Exception:

julia> struct MyError <: Exception end

julia> throw(MyError)
ERROR: MyError

Aserciones

La idea básica de una aserción es «esta afirmación debería ser cierta, así que protesta bien alto si resulta falsa». Su valor radica sobre todo durante la depuración, ya que el código en producción nunca debería fallar una aserción.

Vimos en el concepto Tipos que podemos añadir aserciones de tipo, por ejemplo para comprobar el tipo devuelto por una función.

julia> 42::Number
42

julia> "two"::Number
ERROR: TypeError: in typeassert, expected Number, got a value of type String

De forma más general, la macro @assert nos permite comprobar cualquier expresión que se evalúe a un valor booleano:

julia> n = 22;
julia> @assert isodd(n) "n must be odd"
ERROR: AssertionError: n must be odd

try...catch

Algunos errores son necesariamente fatales, pero a menudo esperamos que el programa se recupere con elegancia.

De forma predeterminada, un error termina de inmediato la función actual, y el error (con cualquier mensaje informativo) se pasa a la función que hizo la llamada.

Esto continúa hacia arriba por la pila de llamadas, hasta que el código de nivel superior termina con un mensaje de error.

En cualquier punto, el error se puede interceptar con un bloque try...catch que intente gestionarlo.

julia> n = -1;
julia> try
           log_n = log(n)
       catch problem
           if problem isa DomainError # number out of range
               # See next section for more on @warn and @info
               @warn "you may have supplied a negative real number: $n"
               @info "trying with complex argument"
               log_n = log(Complex(n))  # fallback calculation

           elseif problem isa MethodError # no idea what n is
               @error "please supply a valid argument"
 
           else
              rethrow() # the error could be anything else
           end
      end
┌ Warning: you may have supplied a negative real number: -1
└ @ Main REPL[3]:5
[ Info: trying with complex argument
0.0 + 3.141592653589793im  # success

En el ejemplo anterior, log(n) necesita que n sea un valor real positivo, o cualquier valor complejo. El try ... catch atrapa los problemas con valores reales negativos y devuelve la respuesta compleja correcta, iπ, en notación matemática.

Si proporcionas, por ejemplo, un argumento de tipo string, no hay recuperación posible salvo pedirle al usuario que lo corrija.

Como último recurso general, añadimos rethrow() para cualquier cosa que no sea ni DomainError ni MethodError.

Nota: A veces un try...catch es justo lo que necesitas, pero por favor evita usarlo en exceso. Si en su lugar puedes usar un bloque if...else, tendrá un rendimiento mucho mejor que capturar excepciones.

Registro

Ten en cuenta que la función error(), comentada antes, no debe confundirse con la macro @error.

La función genera una excepción, que se pasará hacia arriba por la pila de llamadas a menos que se capture.

La macro @error, junto con sus equivalentes @debug, @info y @warn, forma parte del módulo Logging y está pensada para generar mensajes informativos sin alterar el flujo del programa.

De forma predeterminada, la salida va a la terminal (codificada por colores según la gravedad), aunque en una aplicación real hay muchas otras posibilidades.

julia> @warn "Something looks not quite right"
┌ Warning: Something looks not quite right
└ @ Main REPL[55]:1

julia> @error "Panic!"
┌ Error: Panic!
└ @ Main REPL[56]:1

Consulta también el ejemplo anterior, en try...catch.

Instrucciones

Elena es la nueva responsable de calidad de una fábrica de periódicos. Como acaba de llegar a la empresa, ha decidido revisar algunos de los procesos de la fábrica para ver qué se puede mejorar. Ha descubierto que los técnicos hacen muchos controles de calidad a mano. Ve que hay una buena oportunidad para la automatización y te pide a ti, desarrollador freelance, que desarrolles un programa para monitorizar algunas de las máquinas.

1. Comprobar el nivel de humedad de la sala

Tu primera misión es escribir un programa para monitorizar el nivel de humedad de la sala de producción. Ya hay un sensor conectado al software de la empresa que devuelve periódicamente el porcentaje de humedad de la sala.

Tienes que implementar una función en el software que lance un error si el porcentaje de humedad es demasiado alto. Si la humedad está en un nivel aceptable, se añadirá un registro de información. La función debe llamarse humiditycheck y recibir el porcentaje de humedad como argumento.

Debes detenerte con una ErrorException (el mensaje exacto no es importante, pero debe contener el nivel de humedad medido) si el porcentaje supera el 70 %. En caso contrario, añade un registro de información con el mensaje "humidity level check passed: h%", donde h es el porcentaje de humedad.

julia> humiditycheck(60)
[ Info: humidity level check passed: 60%
julia> humiditycheck(100)
ERROR: humidity check failed: 100%

2. Comprobar el sobrecalentamiento

Elena está muy contenta con tu primera tarea y te pide que te encargues de monitorizar la temperatura de las máquinas. Mientras charlas con un técnico, Greg, te cuenta que si la temperatura de una máquina supera los 500 °C, los técnicos empiezan a preocuparse por el sobrecalentamiento.

La máquina está equipada con un sensor que mide su temperatura interna. Debes saber que el sensor es muy sensible y se rompe a menudo. En ese caso, los técnicos tendrán que cambiarlo.

Tu trabajo es implementar una función temperaturecheck que reciba la temperatura como argumento y que añada un registro si todo va bien o lance un error si el sensor está roto o si la máquina empieza a sobrecalentarse. Sabiendo que más adelante tendrás que reaccionar de forma distinta según el error, necesitas un mecanismo para diferenciar los dos tipos de errores.

  • Si el sensor está roto, la temperatura será nothing. En ese caso, debes detenerte con un ArgumentError (el mensaje no es importante).
  • Cuando el sensor funciona, si la temperatura supera los 500 °C, debes lanzar un DomainError que incluya la temperatura medida.
  • En caso contrario, todo va bien, así que añade un registro de información con el mensaje "temperature check passed: t °C", donde t es la temperatura.
julia> temperaturecheck(nothing)
ERROR: ArgumentError: sensor is broken

julia> temperaturecheck(800)
ERROR: DomainError with 800:
"overheating detected"

julia> temperaturecheck(500)
[ Info: temperature check passed: 500 °C

3. Definir un error personalizado

Para la siguiente tarea, tendrás que definir un error más general que sirva para todo. Los detalles de implementación no son importantes más allá de que sea un error y de que se llame MachineError. Puedes incluir los campos y mensajes que te resulten útiles.

4. Monitorizar la máquina

Ahora que tu máquina puede detectar errores y tienes un error de máquina personalizado, añades una función envoltorio que pueda informar de cómo funciona todo. Además de devolver los registros de las funciones anteriores, este envoltorio también tendrá que añadir registros según el tipo o los tipos de fallos que se produzcan.

  • Comprueba la humedad y la temperatura.
  • Si la comprobación de humedad lanza una ErrorException, se debe añadir un registro de error con el mensaje "humidity level check failed: h%", donde h es el porcentaje de humedad.
  • Si la comprobación de temperatura lanza un ArgumentError, se debe añadir un registro de advertencia con el mensaje "sensor is broken".
  • Si la comprobación de temperatura lanza un DomainError, se debe añadir un registro de error con el mensaje "overheating detected: t °C", donde t es la temperatura.
  • Si falla una de las comprobaciones o ambas, se debe lanzar un único MachineError después de añadir los registros.
  • Si todo va bien, solo se añadirán los registros de humiditycheck y temperaturecheck.

Implementa una función machinemonitor() que reciba la humedad y la temperatura como argumentos.

julia> machinemonitor(42, 450)
[ Info: humidity level check passed: 42%
[ Info: temperature check passed: 450 °C

julia> machinemonitor(42, 550)
[ Info: humidity level check passed: 42%
┌ Error: overheating detected: 550 °C
└ @ Main # output truncated

Error: MachineError

julia> machinemonitor(82, 521)
┌ Error: humidity level check failed: 82%
└ @ Main # output truncated
┌ Error: overheating detected: 521 °C
└ @ Main # output truncated

Error: MachineError

julia> machinemonitor(42, nothing)
[ Info: humidity level check passed: 42%
┌ Warning: sensor is broken
└ @ Main # output truncated

Error: MachineError
Editar en GitHub El enlace se abre en una ventana o pestaña nueva
Julia Exercism

¿Listo para empezar Sensores de fábrica?

Regístrate en Exercism para aprender y dominar Julia con 35 conceptos128 ejercicios y mentoría humana real, todo gratis.