Треки
/
Julia
Julia
/
Вправи
/
Датчики на фабриці
Датчики на фабриці

Датчики на фабриці

Навчальна вправа

Вступ

Програмісти зазвичай намагаються писати бездоганне програмне забезпечення і зазвичай зазнають невдачі.

Щось іде не так, несподівано, і нам потрібно вміти з цим давати раду.

Деякі творці мов уважають, що пріоритет - виявити помилку якнайшвидше, а потім перервати виконання з інформативним повідомленням, щоб допомогти з налагодженням.

Мови для науки про дані зазвичай мають виваженіший підхід. Деякі помилки настільки серйозні, що потрібне негайне завершення, але часто краще позначити проблему як таку, з якою розберемося пізніше, і продовжити виконання.

У концепції Ніщо ми бачили, що Julia надає різні заповнювачі для проблемних значень: missing, NaN та Inf. Чи кращий такий підхід за завершення програми в конкретній ситуації, вирішує сам програміст.

Кілька слів про термінологію перед тим, як заглибитися в деталі: документація Julia вважає слова «помилка» й «виняток» здебільшого взаємозамінними. Викладений нижче текст може бути таким самим непослідовним.

Стандартні типи помилок

На цьому місці силабусу ми, мабуть, уже бачили чимало повідомлень про помилки від Julia. Наприклад:

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

Спроба привести число з плаваючою комою до цілого числа спричиняє втрату точності, тож ми отримуємо InexactError.

InexactError - це тип, один із кількох (наразі 25), вбудованих у Julia як стандартні. Усі вони - підтипи Exception:

julia> supertype(InexactError)
Exception

throw()

Деякі зі стандартних типів помилок може бути корисно генерувати у власному коді.

Як і всі конкретні типи, помилки мають конструктори. Вони приймають найрізноманітніші аргументи, тож звірмося з документацією щодо того, який із них ми хочемо використати.

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

Щоб скористатися помилкою, обгорнімо конструктор у функцію throw():

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

error()

Для швидкого й простого підходу може стати в пригоді функція error(). Вона приймає як аргумент рядок тексту (англ. string), або складові цього рядка тексту:

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

Власні помилки

Створювати нові типи помилок у принципі дуже легко. Достатньо додати ще один підтип Exception:

julia> struct MyError <: Exception end

julia> throw(MyError)
ERROR: MyError

Твердження

Основна ідея твердження: «це твердження має бути правдою, тож гучно скаржмося, якщо воно неправда». Цінність цього виявляється переважно під час налагодження, адже продакшн-код ніколи не повинен провалювати твердження.

У концепції Типи ми бачили, що можна додавати твердження типу, наприклад, щоб перевірити тип поверненого значення функції.

julia> 42::Number
42

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

Загалом, макрос @assert дає змогу перевірити будь-який вираз, який дає булеве значення (англ. Boolean):

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

try...catch

Деякі помилки неминуче фатальні, але часто ми очікуємо, що програма зможе коректно відновити роботу.

Типово помилка негайно завершує поточну функцію, і цю помилку (разом із будь-яким інформативним повідомленням) передають функції, яка її викликала.

Так триває аж угору стеком викликів, доки код верхнього рівня не завершиться з повідомленням про помилку.

На будь-якому етапі помилку можна перехопити блоком try...catch, який намагається її обробити.

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

У наведеному вище прикладі log(n) потребує, щоб n було або додатним дійсним значенням, або будь-яким комплексним значенням. Конструкція try ... catch перехоплює проблеми з відʼємними дійсними значеннями й повертає правильну комплексну відповідь iπ у математичному записі.

Якщо ми передамо, наприклад, аргумент-рядок тексту, то відновити роботу не вдасться, хіба що попросити користувача його виправити.

Як останній загальний випадок ми додали rethrow() для всього, що не є ні DomainError, ні MethodError.

Примітка: Іноді try...catch - саме те, що нам потрібно, але, будь ласка, не зловживаймо ним. Якщо замість нього можна використати блок if...else, він буде значно продуктивнішим, ніж перехоплення винятків.

Логування

Зауважмо, що функцію error(), про яку йшлося вище, не варто плутати з макросом @error.

Ця функція породжує виняток, який передають угору стеком викликів, якщо його не перехопити.

Макрос @error, разом зі своїми відповідниками @debug, @info і @warn, належить до модуля Logging і призначений для створення інформативних повідомлень, не змінюючи хід виконання програми.

Типово вивід іде в термінал (з кольоровим кодуванням за рівнем важливості), хоча в справжньому застосунку є багато інших можливостей.

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

Див. також попередній приклад у розділі try...catch.

Вказівки

Олена - новий менеджер із якості на фабриці газет. Щойно прийшовши до компанії, вона вирішила переглянути деякі процеси на фабриці, щоб зрозуміти, що можна покращити. Вона зʼясувала, що техніки виконують багато перевірок якості вручну. Вона бачить, що є хороша можливість для автоматизації, і доручає нам, як незалежному розробнику, розробити програмне забезпечення для стеження за частиною машин.

1. Перевірте рівень вологості в приміщенні

Перше завдання - написати програмне забезпечення, яке стежитиме за рівнем вологості у виробничому приміщенні. До програмного забезпечення компанії вже підʼєднано датчик, який періодично повертає відсоток вологості в приміщенні.

Потрібно реалізувати функцію в програмі, яка викидатиме помилку, якщо відсоток вологості зависокий. Якщо вологість на прийнятному рівні, буде додано Info-лог. Функція повинна мати назву humiditycheck і приймати відсоток вологості як аргумент.

Потрібно зупинитися з ErrorException (точне повідомлення не важливе, але воно мусить містити виміряний рівень вологості), якщо відсоток перевищує 70%. Інакше додайте Info-лог із повідомленням "humidity level check passed: h%", де h - це відсоток вологості.

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

2. Перевірте перегрів

Олена дуже задоволена першим завданням і просить нас зайнятися стеженням за температурою машин. Під час розмови з техніком Грегом ми дізнаємося, що якщо температура машини перевищує 500 °C, техніки починають хвилюватися через перегрів.

Машина обладнана датчиком, який вимірює її внутрішню температуру. Варто знати, що датчик дуже чутливий і часто ламається. У такому разі технікам доведеться його замінити.

Потрібно реалізувати функцію temperaturecheck, яка приймає температуру як аргумент і або додає лог, якщо все гаразд, або викидає помилку, якщо датчик зламався чи машина починає перегріватися. Знаючи, що згодом потрібно буде реагувати по-різному залежно від помилки, нам потрібен механізм, щоб розрізняти ці два види помилок.

  • Якщо датчик зламався, температура дорівнюватиме nothing. У такому разі потрібно зупинитися з ArgumentError (повідомлення не важливе).
  • Коли датчик працює, і якщо температура перевищує 500 °C, потрібно викинути DomainError, який містить виміряну температуру.
  • Інакше все гаразд, тож додайте Info-лог із повідомленням "temperature check passed: t °C", де t - це температура.
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. Визначте власну помилку

Для наступного завдання потрібно визначити загальнішу, всеохопну помилку. Деталі реалізації не важливі, головне, щоб це була помилка з назвою MachineError. Можна додавати поля й повідомлення на власний розсуд, якщо це видається корисним.

4. Стежте за машиною

Тепер, коли машина вміє виявляти помилки і ми маємо власну помилку машини, додамо функцію-обгортку, яка повідомлятиме, як усе працює. Окрім повернення логів із попередніх функцій, ця обгортка також має додавати логи залежно від типу (типів) збоїв, які трапляються.

  • Перевірте вологість і температуру.
  • Якщо перевірка вологості викидає ErrorException, потрібно додати Error-лог із повідомленням "humidity level check failed: h%", де h - це відсоток вологості.
  • Якщо перевірка температури викидає ArgumentError, потрібно додати Warn-лог із повідомленням "sensor is broken".
  • Якщо перевірка температури викидає DomainError, потрібно додати Error-лог із повідомленням "overheating detected: t °C", де t - це температура.
  • Якщо одна з перевірок або обидві зазнають невдачі, після додавання логів потрібно викинути одну MachineError.
  • Якщо все гаразд, буде додано лише логи з humiditycheck і temperaturecheck.

Реалізуйте функцію machinemonitor(), яка приймає вологість і температуру як аргументи.

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
Редагувати через GitHub Посилання відкривається в новому вікні або вкладці
Julia Exercism

Час розпочати Датчики на фабриці?

Зареєструйтеся на Exercism, щоб вивчати й опановувати Julia, а також 35 концепцій128 вправ та справжнє наставництво від людей, і все це безкоштовно.