Trilhas
/
Julia
Julia
/
Exercícios
/
Sensores de fábrica
Sensores de fábrica

Sensores de fábrica

Exercício de aprendizagem

Introdução

Programadores em geral tentam escrever software perfeito, e em geral falham.

As coisas dão errado, de forma inesperada, e precisamos conseguir lidar com isso.

Alguns criadores de linguagens acreditam que a prioridade é detectar um erro o mais rápido possível e então encerrar a execução com uma mensagem informativa para ajudar na depuração.

Linguagens de ciência de dados tendem a adotar uma abordagem mais nuançada. Alguns erros são tão graves que o encerramento imediato é necessário, mas muitas vezes é melhor sinalizar um problema como algo a ser tratado mais tarde e então continuar a execução.

Vimos no Conceito Nada que o Julia oferece vários marcadores para valores problemáticos: missing, NaN e Inf. Se esses marcadores são uma abordagem melhor do que encerrar o programa em uma situação específica é uma questão de julgamento do programador.

Um ponto de nomenclatura antes de entrar nos detalhes: a documentação do Julia trata as palavras "erro" e "exceção" como amplamente intercambiáveis. O conteúdo abaixo pode ser igualmente inconsistente.

Tipos de erro padrão

Neste ponto do programa de estudos, você já deve ter visto muitas mensagens de erro do Julia. Por exemplo:

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

Tentar converter um número de ponto flutuante em um inteiro envolve uma perda de precisão, então obtemos um InexactError.

InexactError é um tipo, um de vários (atualmente 25) embutidos no Julia como padrão. Todos são subtipos de Exception:

julia> supertype(InexactError)
Exception

throw()

Talvez seja útil gerar alguns dos tipos de erro padrão no seu próprio código.

Como todos os tipos concretos, os erros têm construtores. Eles aceitam uma variedade de argumentos, então consulte a documentação para o que você quer usar.

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

Para usar o erro, envolva o construtor em uma função throw():

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

error()

Para uma abordagem rápida e improvisada, a função error() pode ser conveniente. Ela recebe uma string (ou os componentes de uma string) como argumento:

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

Erros personalizados

Criar novos tipos de erro é, em princípio, muito fácil. Basta adicionar outro subtipo de Exception:

julia> struct MyError <: Exception end

julia> throw(MyError)
ERROR: MyError

Asserções

A ideia básica de uma asserção é "esta afirmação deveria ser verdadeira, então reclame alto se ela for falsa". Isso tem valor principalmente durante a depuração, já que código em produção nunca deveria falhar em uma asserção.

Vimos no Conceito Tipos que podemos adicionar asserções de tipo, por exemplo para verificar o tipo de retorno de uma função.

julia> 42::Number
42

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

De modo mais geral, a macro @assert nos permite testar qualquer expressão que avalie para um Boolean:

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

try...catch

Alguns erros são necessariamente fatais, mas muitas vezes esperamos que o programa se recupere de forma elegante.

Por padrão, um erro encerra imediatamente a função atual, e o erro (com qualquer mensagem informativa) é passado para a função que fez a chamada.

Isso continua subindo pela pilha de chamadas, até que o código de nível superior termine com uma mensagem de erro.

Em qualquer etapa, o erro pode ser interceptado com um bloco try...catch que tenta tratá-lo.

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

No exemplo acima, log(n) precisa que n seja um valor real positivo ou qualquer valor complexo. O try ... catch captura problemas com valores reais negativos, retornando a resposta complexa correta iπ em notação matemática.

Se você fornecer, por exemplo, um argumento do tipo string, não há recuperação possível a não ser pedir ao usuário que o corrija.

Como uma captura final abrangente, adicionamos rethrow() para qualquer coisa que não seja DomainError nem MethodError.

Observação: Às vezes um try...catch é o que você precisa, mas evite usá-lo em excesso. Se um bloco if...else puder ser usado em vez disso, ele terá um desempenho muito melhor do que capturar exceções.

Registro de logs

Repare que a função error(), discutida acima, não deve ser confundida com a macro @error.

A função gera uma exceção, que será passada para cima na pilha de chamadas a menos que seja capturada.

A macro @error, junto com suas equivalentes @debug, @info e @warn, faz parte do módulo Logging e serve para gerar mensagens informativas sem alterar o fluxo do programa.

A saída vai para o terminal por padrão (com cores conforme a gravidade), embora em uma aplicação real existam muitas outras possibilidades.

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

Veja também o exemplo anterior, em try...catch.

Instruções

Elena é a nova gerente de qualidade de uma fábrica de jornais. Como acabou de chegar à empresa, decidiu revisar alguns dos processos da fábrica para ver o que pode ser melhorado. Ela descobriu que os técnicos fazem muitos controles de qualidade à mão. Ela percebe uma boa oportunidade de automação e pede a você, um desenvolvedor freelancer, que crie um software para monitorar algumas das máquinas.

1. Verifique o nível de umidade da sala

Sua primeira missão é escrever um software para monitorar o nível de umidade da sala de produção. Já existe um sensor conectado ao software da empresa que retorna periodicamente a porcentagem de umidade da sala.

Você precisa implementar, no software, uma função que lance um erro se a porcentagem de umidade estiver alta demais. Se a umidade estiver em um nível aceitável, um log Info será adicionado. A função deve se chamar humiditycheck e receber a porcentagem de umidade como argumento.

Você deve interromper com um ErrorException (a mensagem exata não importa, mas precisa conter o nível de umidade medido) se a porcentagem passar de 70%. Caso contrário, adicione um log Info com a mensagem "humidity level check passed: h%", em que h é a porcentagem de umidade.

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

2. Verifique se há superaquecimento

Elena ficou muito satisfeita com sua primeira tarefa e pede que você cuide do monitoramento da temperatura das máquinas. Enquanto conversa com um técnico, Greg, você fica sabendo que, se a temperatura de uma máquina passar de 500°C, os técnicos começam a se preocupar com superaquecimento.

A máquina é equipada com um sensor que mede sua temperatura interna. Você deve saber que o sensor é muito sensível e quebra com frequência. Nesse caso, os técnicos precisam trocá-lo.

Sua tarefa é implementar uma função temperaturecheck que recebe a temperatura como argumento e ou adiciona um log, se estiver tudo bem, ou lança um erro, se o sensor estiver quebrado ou se a máquina começar a superaquecer. Sabendo que depois você vai precisar reagir de forma diferente dependendo do erro, você precisa de um mecanismo para diferenciar os dois tipos de erro.

  • Se o sensor estiver quebrado, a temperatura será nothing. Nesse caso, você deve interromper com um ArgumentError (a mensagem não importa).
  • Quando o sensor estiver funcionando, se a temperatura passar de 500°C, você deve lançar um DomainError que inclua a temperatura medida.
  • Caso contrário, está tudo bem, então adicione um log Info com a mensagem "temperature check passed: t °C", em que t é a 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. Defina um erro personalizado

Para a próxima tarefa, você vai precisar definir um erro mais geral, que sirva para qualquer caso. Os detalhes de implementação não importam, desde que seja um erro e que o nome seja MachineError. Fique à vontade para incluir campos e mensagens que você achar úteis.

4. Monitore a máquina

Agora que sua máquina consegue detectar erros e você tem um erro de máquina personalizado, você adiciona uma função wrapper capaz de relatar como tudo está funcionando. Além de retornar os logs das funções anteriores, essa wrapper também vai precisar adicionar logs dependendo de quais tipos de falha ocorrerem.

  • Verifique a umidade e a temperatura.
  • Se a verificação de umidade lançar um ErrorException, um log Error deve ser adicionado com a mensagem "humidity level check failed: h%", em que h é a porcentagem de umidade.
  • Se a verificação de temperatura lançar um ArgumentError, um log Warn deve ser adicionado com a mensagem "sensor is broken".
  • Se a verificação de temperatura lançar um DomainError, um log Error deve ser adicionado com a mensagem "overheating detected: t °C", em que t é a temperatura.
  • Se uma das verificações falhar, ou as duas, um único MachineError deve ser lançado depois que os logs forem adicionados.
  • Se estiver tudo bem, apenas os logs de humiditycheck e temperaturecheck serão adicionados.

Implemente uma função machinemonitor() que recebe a umidade e a 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 via GitHub O link abre em uma nova janela ou aba
Julia Exercism

Tudo pronto para começar Sensores de fábrica?

Crie sua conta no Exercism para aprender e dominar Julia com 35 conceitos128 exercícios e mentoria humana de verdade, tudo de graça.