MERCI SPACE
Навчальні матеріали

Лабораторна робота 3

Лабораторна робота №03. Промислові цифрові сигнали та діагностика відмов

Мета: навчитися пояснювати 24 V промислову логіку та 3,3 V GPIO без небезпечного прямого з'єднання; розрізняти PNP/NPN, sourcing/sinking, NO/NC, COM і активний рівень; реалізувати симуляційний класифікатор станів; перевірити реакції на обрив, замикання, розбіжність каналів і відмови сервісів за data/fault-scenarios.json.

Результати навчання та передумови

Після виконання студент уміє:

  1. описати струмовий шлях для sourcing- і sinking-виходу без перенесення типової схеми на невідомий модуль;
  2. перетворити сирий рівень з урахуванням active_low у логічний стан;
  3. пояснити роль COM, підтягування, оптичної/гальванічної розв'язки та захисту;
  4. класифікувати inactive, active, ERROR, STOP_AND_ERROR у симуляції;
  5. довести, що null, розбіжність каналів і невідомий формат дають відмову за замовчуванням;
  6. пояснити, чому два звичайні GPIO не є safety-системою.

Передумови: ЛР-01–02 або знання безпечного стану й оцінювання ризику; базові закони електричного кола на концептуальному рівні; Python і JSON.

Середовище виконання

СередовищеСтатусПримітка
Google ColabдозволеноТреба завантажити незмінений fault-scenarios.json; фізичних інтерфейсів немає
Локальний ПКосновнеPython зі стандартною бібліотекою, запуск із кореня репозиторію
Raspberry PiдозволеноТільки файл даних і mock; GPIO не відкривається
Фізичний комплекс MERC-I5не потрібенЗаборонено створювати обриви, замикання або змінювати проводку

Необхідні знання та матеріали

Знання

  • напруга вимірюється відносно опорного провідника/COM;
  • вхід не повинен залишатися у невизначеному «плаваючому» стані;
  • розмикальний контакт NC і active_low — різні аспекти: механічна конфігурація й програмна інтерпретація;
  • null у тестових даних означає «немає достовірного рівня», а не 0.
  • Power Hub модуль живлення

Обладнання та ПЗ

  • фізичні сенсори, джерела 24 V і Raspberry Pi для цієї ЛР не потрібні;
  • локальний файл data/fault-scenarios.json;
  • Python зі стандартною бібліотекою і текстовий редактор.

Вхідні джерела

Ризики та правила безпеки

Ключові ризики

НебезпекаПричинаБезпечна дія
Пошкодження Raspberry Pi5 V/24 V або від'ємна напруга подана на GPIOне підключати; застосовувати лише затверджений перетворювач і розв'язку після перевірки схеми
Коротке замикання/перегрівневідомі COM, полярність, струм або навантаженняне вимірювати й не перепідключати студентом
Хибний активний станплаваючий вхід, неправильний active_low, NO/NCневідомий стан переводити в ERROR
Хибна довіра до двох каналівспільне живлення, ПЗ або загальна причина відмовине називати діагностичне дублювання safety-функцією

Заборонені дії

  • подавати 5 V або 24 V на GPIO Raspberry Pi;
  • створювати фізичні short_to_0v, short_to_24v або wire_break;
  • від'єднувати COM, сенсор, оптопару, реле або аварійний канал;
  • визначати pinout за кольором проводу чи типовою схемою;
  • керувати виходами MG400, конвеєра, подавача або грипера.

Умови негайної зупинки

Припинити роботу й повідомити оператора за нагріву, запаху, пошкодження ізоляції, невідомої напруги, руху механізму, суперечливих фактичних показів або будь-якого випадкового доступу програми до GPIO.

Безпечний стан

Безпечний стан: усі виконавчі виходи поза межами цієї програми; вхід None, розбіжність або невідомий формат → ERROR; для втрати зв'язку з роботом чи заклинювання → STOP_AND_ERROR; відновлення потребує підтвердження оператора.

Передпусковий чекліст

  • Файл має scope: simulation_only.
  • Програма не імпортує GPIO/Modbus/SDK і не відкриває мережу.
  • Жоден провід або модуль не від'єднують.
  • Дані null не перетворюють на 0.
  • Невідомий сценарій спричиняє контрольовану помилку.
  • Результати позначають як симуляційні.

Методичні вказівки й теоретичні відомості

1. Рівень напруги, струмовий шлях і COM

«24 V промислова логіка» описує клас промислових сигналів, але не доводить точну напругу або допустимий струм невідомого модуля. GPIO Raspberry Pi працює з 3,3 V логікою. Між ними потрібен перевірений інтерфейс: перетворення рівнів, обмеження струму, захист і, коли це вимагає архітектура, гальванічна розв'язка. Пряме з'єднання неприпустиме.

  • Sourcing/PNP-вихід у типовій концепції подає струм до навантаження/входу з позитивної шини.
  • Sinking/NPN-вихід у типовій концепції відводить струм до 0 V.
  • COM задає опорний/спільний вузол групи входів або виходів; його призначення треба читати зі схеми конкретного пристрою.
  • NO/NC описує стан контакту без активування, а active-high/active-low — програмне трактування рівня. Вони не є синонімами.

Текстовий опис рисунка: невідомий промисловий сенсор під'єднується лише через захист, перевірене узгодження рівнів та, якщо передбачено проєктом, ізоляційний бар'єр; після нормалізації логіки підтверджені 0/1 утворюють стан, а None чи розбіжність переходять у ERROR.

Mermaid
flowchart LR
    S["Промисловий сенсор: модель невідома"] --> P["Захист і перевірене узгодження рівнів"]
    P --> I["Ізоляційний бар'єр, якщо передбачено проєктом"]
    I --> G["Вхід 3,3 V GPIO через затверджений інтерфейс"]
    G --> L["Нормалізація active_low / діагностика"]
    L -->|"0 або 1, підтверджено"| A["Логічний стан"]
    L -->|"None / розбіжність"| E["ERROR і блокування виходів"]

Рис. 1. Ілюстративний, неелектричний ланцюжок узгодження й діагностики сигналу

Схема навмисно не містить номерів контактів, номіналів і конкретного типу оптопари: ці дані MERC-I5 не підтверджені.

2. Оптопара, підтягування та плаваючий вхід

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

3. Діагностичні патерни

Спостереження у симуляціїМожливі причиниРезультат цієї ЛР
обидва канали 0підтверджений неактивний стан у заданій моделіinactive
обидва канали 1підтверджений активний стан у заданій моделіactive
raw=0, active_low=trueінверсна програмна логікаactive
один канал nullобрив, недоступність або невизначеністьERROR
канали різнізамикання, затримка або розбіжністьERROR без часової поблажки
виконавчий механізм командовано, підтвердження не змінилосязаклинювання/втрата деталіSTOP_AND_ERROR

Ці відповідності задані навчальним JSON і не є результатом вимірювання MERC-I5.

Хід виконання роботи

  1. Побудувати таблицю термінів і струмових шляхів.
  2. Перевірити структуру набору сценаріїв.
  3. Реалізувати класифікатор.
  4. Запустити весь набір і негативні тести.
  5. Сформувати діагностичну таблицю без апаратних висновків.

Виконання лабораторної роботи

Крок 1. Розібрати концептуальні сигнали

Для PNP/sourcing, NPN/sinking, NO, NC, active-high, active-low і COM запишіть визначення, типову роль та факт, який треба підтвердити перед підключенням.

Код/команда: не потрібні.

Очікуваний результат: сім коректних рядків без номера GPIO, кольору проводу чи номіналу невідомого модуля.

Критерій правильності: PNP/NPN не ототожнено з NO/NC; явно заборонено пряме 24 V→GPIO.

Якщо результат не отримано: поверніться до офіційного datasheet лише після ідентифікації моделі; не використовуйте випадкову типову схему.

Крок 2. Перевірити набір даних

Відкрийте data/fault-scenarios.json як текст. Не редагуйте оригінал. Знайдіть scope, default_safe_reaction, 11 сценаріїв і channel_discrepancy_timeout_ms.

Text
python -m json.tool data/fault-scenarios.json

Очікуваний результат: валідний JSON із scope: simulation_only; час розбіжності — null.

Критерій правильності: нараховано 11 унікальних id; кожен має expected; null не замінено числом.

Якщо результат не отримано: перевірте запуск із кореня репозиторію та шлях; не створюйте замість файла власні «фактичні» дані.

Крок 3. Реалізувати безпечний класифікатор

Збережіть код як lab03_faults.py у корені робочої копії. Без аргументів він сам знаходить локальний набір і завершується після перевірки.

Python
# safe-smoke
from __future__ import annotations

import json
from pathlib import Path
from typing import Any


def locate_dataset() -> Path:
    starts = [Path.cwd(), Path(__file__).resolve().parent]
    for start in starts:
        for base in (start, *start.parents):
            candidate = base / "data" / "fault-scenarios.json"
            if candidate.is_file():
                return candidate
    raise FileNotFoundError("data/fault-scenarios.json not found")


def evaluate(scenario: dict[str, Any]) -> str:
    inputs = scenario.get("inputs", {})
    if isinstance(inputs, dict) and "raw" in inputs:
        raw = inputs["raw"]
        if raw not in (0, 1):
            return "ERROR"
        active = bool(raw) ^ bool(inputs.get("active_low", False))
        return "active" if active else "inactive"

    if isinstance(inputs, dict) and {"channel_a", "channel_b"} <= inputs.keys():
        a, b = inputs["channel_a"], inputs["channel_b"]
        if a not in (0, 1) or b not in (0, 1) or a != b:
            return "ERROR"
        return "active" if a == 1 else "inactive"

    if scenario.get("available") is False:
        return "STOP_AND_ERROR" if scenario.get("service") == "robot" else "ERROR"

    if scenario.get("commanded") is True:
        confirmations = [
            value for key, value in scenario.items()
            if key.endswith("_sensor_changed")
        ]
        if confirmations and not all(value is True for value in confirmations):
            return "STOP_AND_ERROR"

    raise ValueError(f"unsupported scenario format: {scenario.get('id', '<no id>')}")


def load_and_check(path: Path) -> dict[str, object]:
    payload = json.loads(path.read_text(encoding="utf-8"))
    if payload.get("scope") != "simulation_only":
        raise ValueError("dataset must be simulation_only")
    scenarios = payload.get("scenarios")
    if not isinstance(scenarios, list) or not scenarios:
        raise ValueError("non-empty scenarios list required")
    ids = [item.get("id") for item in scenarios if isinstance(item, dict)]
    if len(ids) != len(set(ids)):
        raise ValueError("scenario ids must be unique")

    mismatches = []
    for item in scenarios:
        actual = evaluate(item)
        if actual != item.get("expected"):
            mismatches.append({"id": item.get("id"), "actual": actual, "expected": item.get("expected")})
    if mismatches:
        raise AssertionError(mismatches)
    return {
        "scope": payload["scope"],
        "checked": len(scenarios),
        "mismatches": 0,
        "discrepancy_timeout_ms": payload.get("channel_discrepancy_timeout_ms"),
        "hardware_tested": False,
    }


def smoke_test() -> None:
    result = load_and_check(locate_dataset())
    assert result["checked"] == 11
    assert result["discrepancy_timeout_ms"] is None
    assert evaluate({"id": "boundary", "inputs": {"channel_a": None, "channel_b": None}}) == "ERROR"
    try:
        evaluate({"id": "unknown"})
    except ValueError:
        pass
    else:
        raise AssertionError("unknown format must fail closed")
    print(json.dumps(result, ensure_ascii=False))


if __name__ == "__main__":
    smoke_test()
Text
python lab03_faults.py

Очікуваний результат: JSON із checked: 11, mismatches: 0, discrepancy_timeout_ms: null, hardware_tested: false.

Критерій правильності: код завершення 0; усі очікувані реакції збігаються; невідомий формат відхиляється.

Якщо результат не отримано: не переходьте до GPIO; перевірте шлях, валідність JSON і виведіть id першої розбіжності.

Крок 4. Виконати негативні та граничні тести

У копії даних послідовно змоделюйте: channel_a=null; 0/1; 1/0; невідомий запис без полів; дублікат id; неправильний scope. Оригінал не змінюйте.

Код/команда: повторно запустіть python lab03_faults.py після додавання локальних assert у копії скрипту.

Очікуваний результат: сигнальні невизначеності дають ERROR; заклинювання/втрата робота — STOP_AND_ERROR; структурні помилки спричиняють контрольований виняток.

Критерій правильності: 6/6 тестів відмовляють за замовчуванням; жоден null не класифіковано активним або неактивним.

Якщо результат не отримано: мінімізуйте один сценарій, перевірте типи JSON і не додавайте мовчазне значення за замовчуванням.

Крок 5. Оформити діагностичну таблицю

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

Код/команда: використайте JSON-вивід програми; апаратна команда не потрібна.

Очікуваний результат: 11 рядків, чітка різниця між ERROR і STOP_AND_ERROR, усюди hardware_tested=no.

Критерій правильності: результат відділено від діагностичної гіпотези; відновлення не пропонується до встановлення причини.

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

Сценарії перевірки

ТипСценарійОчікуваноКритерій
Позитивнийsensor_normal_low/highinactive/active2/2
Позитивнийinverse_logic_ncactiveінверсія коректна
Негативнийwire_breakERRORnull не замінено 0
Негативнийshort_to_0v, short_to_24vERROR2/2 розбіжності
Негативнийвтрата робота/заклинюванняSTOP_AND_ERROR3/3
Граничнийобидва канали nullERRORfail-closed
Граничнийchannel_discrepancy_timeout_ms=nullчисло не використаноsafety-таймер не заявлено
Граничнийневідомий форматValueErrorнемає мовчазного стану

Таблиці вимірювань і метрики

Результати сценаріїв

IDВхідОчікуваноФактичноРеакціяАпаратно перевірено
ні

Підсумок

МетрикаРезультатКритерій
Перевірено сценаріїв11
Збігів із expected11/11
Невідомих форматів прийнято0
Вигаданих електричних параметрів0
Фізичних відмов створено0рівно 0

Вимоги до звіту

Дотримуйтеся загальних вимог. Додатково подайте таблицю термінів, пояснення різниці 24 V/3,3 V, ілюстративну архітектуру інтерфейсу, контрольну суму або незмінений фрагмент метаданих JSON, код, вивід 11 сценаріїв, усі негативні/граничні тести, діагностичну таблицю та фразу «Електричні з'єднання й фізичні відмови не перевірялися».

Критерії оцінювання

СкладоваЧасткаОзнака повного виконання
Підготовка10%джерела, терміни й simulation_only перевірено
Реалізація40%класифікатор підтримує всі 11 сценаріїв і fail-closed
Перевірка25%позитивні, негативні й граничні тести відтворюються
Аналіз15%струмовий шлях, інверсію, ізоляцію й межі safety правильно пояснено
Звіт10%повні таблиці, код, вивід, прогалини й межі перевірки
Разом100%

Критична помилка: пряме підключення 5/24 V до GPIO, фізичне створення відмови або вигаданий pinout.

Контрольні питання

  1. Чим PNP/NPN відрізняються від NO/NC?
  2. Яку роль виконує COM?
  3. Чому null не можна трактувати як логічний нуль?
  4. Для чого потрібен підтягувальний резистор?
  5. Чому оптопара не гарантує safety-рівень автоматично?
  6. Що робить active_low?
  7. Які спільні причини відмов можливі для двох GPIO?
  8. Чому час розбіжності не можна вигадати?
  9. Чим ERROR відрізняється від STOP_AND_ERROR у навчальній моделі?
  10. Які докази потрібні перед створенням фактичної схеми MERC-I5?

Висновки

У висновку наведіть кількість перевірених сценаріїв, частку збігів, поведінку на null і невідомому форматі, а також поясніть необхідність перевіреного інтерфейсу між 24 V і 3,3 V. Окремо зазначте, що файл описує симуляційні відмови, а pinout, час розбіжності та фізична реакція MERC-I5 не підтверджені.

MERCI SPACE