Tracks
/
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 suelen intentar escribir software perfecto y, por lo general, no lo logran.

Las cosas salen mal, de forma inesperada, y necesitamos poder lidiar con eso.

Algunos diseñadores de lenguajes creen que la prioridad es detectar un error lo más rápido posible y luego 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 se resolverá más tarde y continuar con 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 un mejor enfoque que terminar el programa en una situación concreta es algo que queda a criterio de quien programa.

Una aclaración sobre la nomenclatura antes de entrar en detalles: la documentación de Julia trata las palabras «error» y «excepción» como prácticamente intercambiables. El contenido que sigue puede ser igual de inconsistente.

Tipos de error estándar

A esta altura del temario, ya habrás visto muchos mensajes de error de Julia. Por ejemplo:

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

Intentar convertir un número de punto flotante 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 sin complicaciones, 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

En principio, crear nuevos tipos de error es muy fácil. Basta con agregar 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 a gritos si es falsa». Su utilidad está principalmente durante la depuración, ya que el código de producción nunca debería fallar una aserción.

Vimos en el concepto Tipos que podemos agregar aserciones de tipo, por ejemplo para comprobar el tipo de retorno de 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 como un Boolean:

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 etapa, el error se puede interceptar con un bloque try...catch que intenta manejarlo.

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 string, no hay forma de recuperarse salvo pedirle al usuario que lo corrija.

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

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

Registro

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

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

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 la sección try...catch.

Instrucciones

Elena es la nueva gerente 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 podría mejorar. Descubrió que los técnicos hacen muchos controles de calidad a mano. Ve una buena oportunidad para automatizar y te pide a ti, desarrollador freelance, que crees un programa para monitorear algunas de las máquinas.

1. Verifica el nivel de humedad de la sala

Tu primera misión es escribir un programa que monitoree 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.

Necesitas 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 agregará un log de Info. La función debe llamarse humiditycheck y recibir el porcentaje de humedad como argumento.

Debes detenerte con una ErrorException (el mensaje exacto no importa, pero debe contener el nivel de humedad medido) si el porcentaje supera el 70%. De lo contrario, agrega un log de Info 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. Verifica el sobrecalentamiento

Elena está muy contenta con tu primera tarea y te pide que te encargues de monitorear la temperatura de las máquinas. Mientras charlas con un técnico, Greg, este 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 con frecuencia. 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 agregue un log si todo está bien, o lance un error si el sensor está roto o si la máquina empieza a sobrecalentarse. Como más adelante necesitarás reaccionar de forma distinta según el error, necesitas un mecanismo que te permita 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 importa).
  • Cuando el sensor funciona, si la temperatura supera los 500°C, debes lanzar un DomainError que incluya la temperatura medida.
  • De lo contrario, todo está bien, así que agrega un log de Info 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. Define un error personalizado

Para la siguiente tarea, necesitarás definir un error más general que abarque todo. Los detalles de implementación no importan más allá de que sea un error y de que su nombre sea MachineError. Puedes incluir campos y mensajes como te resulte útil.

4. Monitorea la máquina

Ahora que tu máquina puede detectar errores y tienes un error de máquina personalizado, agrega una función contenedora que informe cómo funciona todo. Además de devolver los logs de las funciones anteriores, esta función contenedora también tendrá que agregar logs según los tipos de fallos que ocurran.

  • Verifica la humedad y la temperatura.
  • Si la verificación de humedad lanza una ErrorException, se debe agregar un log de Error con el mensaje "humidity level check failed: h%", donde h es el porcentaje de humedad.
  • Si la verificación de temperatura lanza un ArgumentError, se debe agregar un log de Warn con el mensaje "sensor is broken".
  • Si la verificación de temperatura lanza un DomainError, se debe agregar un log de Error con el mensaje "overheating detected: t °C", donde t es la temperatura.
  • Si una de las verificaciones falla o fallan ambas, se debe lanzar un único MachineError después de agregar los logs.
  • Si todo está bien, solo se agregarán los logs 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 una pestaña nuevas
Julia Exercism

¿Todo 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.