# Аппаратные приёмочные тесты контроллера ## Назначение Проект `tests/Exchanger.HardwareTests` предназначен для проверки реальной платы через Windows USB-COM сразу после её подключения. Он использует те же `SerialPortHardware`, `ControllerAuthenticator`, AES-ключ, parser и `PeripheryMachineController`, что и приложение, поэтому проверяет production-путь, а не отдельную тестовую реализацию. Аппаратные тесты включены в solution для проверки компиляции, но без `EXCHANGER_BOARD_TESTS=1` пропускаются. Обычный `dotnet test Exchanger.sln` не открывает COM-порт, не включает приёмники оплаты и не запускает хоппер. ## Перед запуском 1. Подключить и запитать контроллер. 2. Узнать его COM-порт в диспетчере устройств Windows. 3. Закрыть Exchanger, терминал производителя и другие программы, которые могут удерживать этот порт. 4. Освободить лоток хоппера и подготовить тестовые деньги только перед интерактивными группами. 5. Начинать с группы `Safe`. Все команды ниже выполняются из корня репозитория. Результат каждого запуска сохраняется как TRX в локальном каталоге `artifacts/board-tests/`, который исключён из Git. ## Безопасная автоматическая проверка ```powershell powershell -ExecutionPolicy Bypass -File .\tests\Exchanger.HardwareTests\run-board-tests.ps1 ` -Port COM5 ` -Group Safe ``` Группа `HardwareSafe` проверяет: - открытие порта с профилем 115200 8N1/None; - аппаратный RTS-reset и полный startup-handshake `periphery controller ver_ → 2000 ms → 07 → CHAL → 08 → RAES → READY!` с production AES-256 ECB/NoPadding и обязательной проверкой proof; `AUTH_PC_OK` и `AUTH_CNT_OK` распознаются как промежуточные кадры, но не заменяют точный `READY!`; - переходы `Authenticating → Ready → Disconnected`; - ответ `INPUT` на команду `91`; - соответствие active-low входу: сигнал на `IN1` (`бит 0`) → `Low`, отсутствие сигнала (`бит 1`) → `Enough`; - ответы на `140`, `150`, `160`, `3090` и `99` без вывода сырых кадров в отчёт; - три цикла закрытия, повторного открытия и повторной авторизации; число циклов меняется параметром `-ReconnectCycles` от 1 до 20. Привязка кнопок и переходов приложения к `141/142`, `151/152`, `161/162/163`, обязательным `ASK:*` и пакетным командам `309N` проверяется обычным набором `Exchanger.Tests` через wire-симулятор. Группа Safe намеренно не включает приём денег и мотор. Status-тест намеренно требует хотя бы один ответный не-handshake кадр. Если реальная прошивка не отвечает на команду или формат отличается от документации, тест завершится ошибкой и тем самым зафиксирует несовместимость для корректировки parser/документации. На подключённой плате CP210x/COM3 группа `Safe` исторически пройдена 2026-08-27: 9/9 тестов, включая `INPUT`, active-low преобразование `IN1` в типизированный уровень запаса, ответы на status-команды и три повторные авторизации. Эти результаты относятся к прежней прошивке и старым запросам `309?/409?`; актуальный runner использует `3090`, поэтому после подключения обновлённой платы пакет требуется повторить. Повторный прогон 2026-08-31 подтвердил новый startup-контракт: отдельный `Probe` прошёл 1/1, а `Safe` — 8/9. Исторически были успешны авторизация, `INPUT`/`IN1`, `140`, `150`, старый запрос состояния хоппера, `99` и три reconnect-цикла. Только `160` дважды не получил ответ, поэтому эта проверка остаётся красной до уточнения наличия/состояния Вендиста или поведения прошивки. Результаты: `artifacts/board-tests/board-probe-20260831-105348.trx` и `artifacts/board-tests/board-safe-20260831-104124.trx`. Если плата возвращает `CHAL`/`RAES`, но production-проверка proof не проходит, используйте безопасный фиксированный probe. Он не запускает периферию и не пишет сырые handshake-блоки в отчёт: сравнивает только семантику proof и короткие SHA-256 fingerprints. ```powershell powershell -ExecutionPolicy Bypass -File .\tests\Exchanger.HardwareTests\run-board-tests.ps1 ` -Port COM5 ` -Group Probe ``` ## Кнопка SERVICE, монетник, купюроприёмник и датчик Эти тесты ждут ручное действие и не запускаются без `-ConfirmInteractive`. ```powershell powershell -ExecutionPolicy Bypass -File .\tests\Exchanger.HardwareTests\run-board-tests.ps1 ` -Port COM5 ` -Group Interactive ` -ConfirmInteractive ` -CoinRubles 10 ` -BillRubles 100 ` -StockInput 1 ` -ExpectedStock Enough ` -InteractionTimeoutSeconds 90 ``` Во время запуска тест попросит: - нажать физическую кнопку `SERVICE`; - внести одну монету указанного номинала; - внести одну купюру указанного номинала; - заранее привести датчик хоппера к ожидаемому состоянию. Для проверки обоих сигналов датчика тест запускается дважды: сначала с `-ExpectedStock Enough`, затем после физического изменения уровня с `-ExpectedStock Low`. По умолчанию используется подтверждённая active-low разводка: сигнал на `IN1` (`бит 0`) → `Low`; альтернативная active-high полярность задаётся как `-StockLowWhenHigh:$true`. Если конкретная периферия ещё не подключена, соответствующий номинал можно не передавать: такой тест будет отмечен пропущенным, остальные продолжат работу. ## Реальная банковская операция Тест Вендиста может списать реальные деньги и требует отдельного осознанного подтверждения: ```powershell powershell -ExecutionPolicy Bypass -File .\tests\Exchanger.HardwareTests\run-board-tests.ps1 ` -Port COM5 ` -Group Payment ` -CardRubles 10 ` -ConfirmRealPayment ` -InteractionTimeoutSeconds 120 ``` Проверяется команда оплаты, событие `MONEY:CASHLESS:<сумма>`, обязательный `ASK:CASHLESS:<сумма>` и типизированный итог `Approved`. Автоматический возврат денежных средств тест не выполняет. ## Физическая выдача жетонов Тест запускает мотор хоппера и требует отдельного подтверждения: ```powershell powershell -ExecutionPolicy Bypass -File .\tests\Exchanger.HardwareTests\run-board-tests.ps1 ` -Port COM5 ` -Group Dispense ` -DispenseCount 2 ` -ConfirmDispense ` -InteractionTimeoutSeconds 60 ``` За один тест разрешено от 1 до 10 жетонов. Текущий интерактивный runner физически проверяет первый хоппер командой `309N`; production-runtime дополнительно поддерживает второй канал `409N`, который покрыт программными тестами. Перед аппаратной приёмкой двух хопперов runner нужно запускать отдельными явно подтверждёнными сценариями, чтобы один запуск не включал два двигателя неожиданно. Прошивочный предел одной пакетной команды — 99 жетонов: приложение не отправляет `309100` или `409100`, на которые плата отвечает ошибками групп 30 и 40. Крупные выдачи обоих каналов проверяются программными тестами как последовательность пакетов максимум по 99. Отдельный интерактивный тест прямого мотора пока использует только `301/300` и считает строки `HOPPER1 PULSE=1`. Production-приложение уже поддерживает симметричный пересчёт второго хоппера `401/400 → HOPPER2 PULSE=1`; его физический прогон требует отдельного явного подтверждения владельца. Реальный приёмочный runner сохраняет предел 10 за один запуск, чтобы случайная проверка не опустошила хоппер. Многожетонная логика и суммарный partial-result проверяются детерминированно в симуляторе. ## Отключение USB-кабеля ```powershell powershell -ExecutionPolicy Bypass -File .\tests\Exchanger.HardwareTests\run-board-tests.ps1 ` -Port COM5 ` -Group Cable ` -ConfirmCable ` -InteractionTimeoutSeconds 120 ``` После приглашения нужно отключить USB, дождаться сообщения о потере связи и подключить кабель обратно. Тест периодически пытается открыть то же имя COM-порта и требует второй успешный handshake. Если Windows назначит другое имя порта, запуск завершится ошибкой; новый порт следует передать в следующем прогоне. ## Границы набора Набор проверяет все интерфейсы, которые сейчас использует Exchanger. Он специально не отправляет: - `00` RESET и команды обновления прошивки; - произвольные команды силовых выходов `20/21/22`; - команды световых каналов и конфигурации конкретного автомата; - автоматический повтор выдачи после обрыва; - недокументированные команды возврата платежа. Такие операции требуют отдельной схемы конкретного аппарата и явного решения владельца. Сырые handshake-кадры, AES-данные и полные ответы платы не включаются в NUnit/TRX-отчёты; сохраняются только безопасные категории сообщений и результаты проверок.