exchanger/README.md

43 KiB
Raw Permalink Blame History

Exchanger

Exchanger — полноэкранное тач-приложение для напольного разменника, продающего и выдающего жетоны за наличную или безналичную оплату. Проект разрабатывается на Godot 4.7 Mono/C# для Windows x64 с расчётом на будущий перенос аппаратного транспорта на Android ARM64.

Целевой аппарат использует портретный тач-экран, показывает цену и бинарное состояние запаса жетонов («много»/«мало»), принимает наличные через купюро- и монетоприёмник либо запускает оплату на отдельном банковском терминале, после чего безопасно инициирует выдачу. События оплаты, баланса, запаса и выдачи должны поступать от внешнего контроллера автомата.

Текущее состояние

Завершены Этапы 0–3, программная часть Этапа 4 и весь программный пакет Этапа 6; приложение проходит полный пользовательский сценарий на типизированном mock-контроллере и документированный wire-профиль 2.3.5 на детерминированном симуляторе. После обновления авторизации физическая плата на COM3 прошла отдельный строгий auth-probe 1/1 и безопасную автоматическую приёмку 8/9: startup-баннер, AES-handshake, точный READY!, цифровые входы, четыре status-запроса и три цикла переподключения работают; отдельный запрос состояния Вендиста 160 дважды остался без ответа. Интерактивные платежи, выдача и отключение USB остаются отдельными явно подтверждаемыми проверками.

Указанные результаты COM3 относятся к предыдущему состоянию прошивки. После текущего обновления протокола плата отключена, поэтому новая разводка и команды пока проверены только симулятором; аппаратная приёмка отложена до её подключения.

  • главная сцена src/scenes/App/App.tscn;
  • портретный HomeScreen с ценой, запасом, двумя способами оплаты, промо-fallback и поддержкой;
  • рабочие ветки наличной и карточной оплаты, включая другую сумму, ожидание терминала, успех и отказ;
  • ScreenManager Autoload с централизованным реестром, fade-переходами и блокировкой повторного ввода;
  • единственная встроенная программная fallback-тема;
  • безопасная замена fallback-темы одним внешним DLC-пакетом .pck при запуске;
  • статический фон DLC-темы с отдельным слоем анимированных спрайтов;
  • облегчённый режим, отключающий необязательные фоновые анимации;
  • типизированная внешняя конфигурация;
  • файловый журнал с ротацией;
  • фильтрация вторичных касаний;
  • аппаратный интерфейс IHardwarePort;
  • рабочие MockMachineController, PeripheryControllerSimulator, Windows COM-транспорт и безопасная Android-заглушка;
  • конфигурируемые тарифы, бонусы, карточные пресеты, способы оплаты, пользовательские таймауты, брендинг, промо и оформление;
  • актуальная привязка цифровых входов платы: IN8 — сервисная кнопка, IN1 — сигнал «мало жетонов» первого хоппера, IN24 — его датчик выдачи, IN4 — сигнал «мало жетонов» второго хоппера; вход датчика выдачи второго хоппера пока не определён;
  • компонент состояния запаса жетонов Enough/Low/Unknown без фиктивного точного количества;
  • отдельный подтверждаемый владельцем учётный остаток жетонов: ручное пополнение, списание подтверждённой выдачи и автоматический пересчёт активного хоппера;
  • безопасное состояние соединения Disconnected/Authenticating/Ready/Faulted и ненавязчивый сервисный индикатор на домашнем экране;
  • SessionController с явными состояниями, таймаутами и защитой от повторной выдачи;
  • чистый SessionFlow, уникальный локальный контекст операции и управляемый монотонный источник времени для тестов;
  • типизированные безопасные ошибки и отдельный JSONL-журнал начала/итога операций;
  • AdvertisementPlayer с локальным плейлистом, постером/текстовым fallback и остановкой вне HomeScreen.
  • production kiosk-профиль с borderless fullscreen, скрытым курсором и восстановлением окна после потери фокуса либо изменения дисплея;
  • сервисный вход по аппаратному SERVICE: защита включается в самом приложении, первый защищённый вход предлагает дважды задать единый сервисный PIN, последующие входы требуют его проверки; допустимые изменения сохраняются автоматически, повторное нажатие сервисной кнопки возвращает на главный экран;
  • шестираздельный редактор настроек владельца автомата: общие данные, звук, промо, тарифы, оплата и сервисный учёт жетонов; технические параметры, диагностика и тестовая выдача в пользовательском UI отсутствуют;
  • Windows watchdog, задача автозапуска и process soak-runner.

По документации контроллера 2.3.5 реализованы Windows COM-транспорт, framing, строгий startup-handshake и платёжные команды. Порядок авторизации сверен с plate_config_app: RTS-reset, баннер periphery controller ver_<version>, пауза 2000 мс, 07 → CHAL → 08 → RAES и допуск к работе только после точной строки READY!. Оба хоппера включены в целевой DualHopperAuto: первый использует 309N/3090 и 301/300, второй — 409N/4090 и 401/400; фактическая выдача подтверждается HOPPER1/2 OK, пересчёт — HOPPER1/2 PULSE=1. Учётные остатки хранятся отдельно, а выдача списывается с фактически сработавшего канала. Универсальная конфигурация репозитория сохраняет безопасный Unsupported, пока профиль конкретного автомата не выбран явно. Подробности: docs/hardware/PERIPHERY_CONTROLLER_2_3_5.md. Актуальный прогресс перечислен в ROADMAP.md.

После завершения Этапа 1 были изучены визуальные референсы реального корпуса и интерфейса. Они уточнили целевую портретную ориентацию и две разные платёжные ветки. Ландшафтный StartScreen удалён и заменён портретным домашним экраном. Подробные выводы находятся в docs/REFERENCE_ANALYSIS.md.

Этап 0 завершён. Базовые договорённости, включая универсальность до появления целевого железа и отложенную интеграцию контроллера, зафиксированы в docs/architecture/decisions.md.

Команды 309N и 409N ограничены 99 жетонами: 309100 и 409100 возвращают ошибки групп 30 и 40. Приложение автоматически разбивает крупную выдачу на последовательные пакеты максимум по 99 жетонов и маршрутизирует их между двумя учётными остатками.

Целевой интерфейс по референсам

  • Портретный формат 9:16; нативная сетка интерфейса и preview — 720×1280.
  • Пиксельная космическая тема, согласованная с оформлением корпуса: сам фон статичен, а НЛО, персонажи и другие движущиеся элементы реализуются отдельными анимированными спрайтами.
  • Домашний экран без дополнительного шага «Начать»: цена жетона, запас, способы оплаты, кнопка выдачи и промо-ролик.
  • Наличная ветка: текущий баланс, расчёт жетонов/бонуса, инструкции для купюр и монет.
  • Карточная ветка: предустановленные суммы, ввод другой суммы, ожидание прикладывания карты и результат.
  • Постоянно видимые брендовые элементы и телефон техподдержки.
  • Маскот и фоновые анимации, не перекрывающие функциональные панели.

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

Конкретные числа и графические материалы из референсов пока считаются демонстрационными. Перед production необходимо подтвердить тарифы, бонусы, бренд и права на ассеты.

Технологии

Компонент Значение
Движок Godot 4.7.1 Mono
Язык C#
.NET .NET 8; Android-конфигурация — .NET 9
Рендерер Mobile (Vulkan)
Основная платформа Windows x64
Перспективная платформа Android ARM64
Целевой профиль железа Пока не определён; приложение проектируется аппаратно-независимым и конфигурируемым
Логический viewport 720×1280, портретный 9:16, canvas_items/expand
Debug-window 720×1280
Аппаратный транспорт MVP COM/Serial на Windows

Требования для разработки

  • Godot 4.7.1 Mono или совместимая версия Godot 4.7 Mono.
  • .NET SDK 8.
  • Windows x64 для текущей целевой сборки.

Путь к редактору, используемый локальной конфигурацией VS Code, хранится в .vscode/settings.json. На другой машине его необходимо изменить на фактический путь к Godot Mono.

Запуск

Через Godot Editor

  1. Открыть project.godot в Godot 4.7.1 Mono.
  2. Дождаться импорта ресурсов и сборки C#-проекта.
  3. Запустить главную сцену клавишей F6 либо весь проект клавишей F5.

В debug- и production-сборках интерфейс использует нативную сетку 720×1280. В полноэкранном режиме Godot масштабирует её на портретную панель через canvas_items/expand.

Проверка C#-сборки

dotnet build Exchanger.sln

Автоматические тесты

dotnet test Exchanger.sln

Отдельный проект tests/Exchanger.Tests использует NUnit 4. Обычный набор тестов выполняется без запуска окна Godot и проверяет конфигурацию, миграции, атомарное сохранение и восстановление настроек, сервисный доступ, учётный остаток и автоматический пересчёт жетонов, безопасный audit, ротацию журналов, настройки оборудования, штатную отмену реального COM-чтения, аудиопараметры, брендинг, промо, DLC-тему, полносценовый binding-контракт, owner-only состав настроек, production export, тарифы, бонусы, карточные суммы, способы оплаты и таймауты, конечный автомат, framing COM-протокола, команды и полные cash/card-сценарии контроллера, обязательные ASK:*, взаимоисключение приёмников, строгий startup-handshake banner → 2000 ms → 07/CHAL/08/RAES → READY!, AES и восстановление ключа, wire-симулятор, оба формата INPUT, актуальную разводку IN8/IN1/IN4/IN24, keep-alive Вендиста, at-most-once, раздельный учёт и выдачу обоими хопперами через 309N/409N → APPEND/OK либо ERROR/CREDIT, строковые импульсы раздельного сервисного пересчёта, malformed-кадры, ошибки соединения, JSONL-восстановление и отсутствие накопления подписок. Ускоренный soak моделирует 1440 минут и 480 выдач, а отдельный стресс выполняет 10 000 жизненных циклов SessionFlow. Загрузка сцен и ресурсов дополнительно проверяется в Godot Editor через разрешённый проектом MCP-сервер godot-ai.

Для физической платы подготовлен отдельный Exchanger.HardwareTests с 18 безопасными и подтверждаемыми приёмочными проверками, включая изолированный handshake probe. Без явного флага они пропускаются и никогда не открывают COM-порт. Порядок проверки handshake, status-команд, SERVICE, приёмников оплаты, Вендиста, датчика, хоппера и USB-reconnect описан в docs/hardware/BOARD_ACCEPTANCE_TESTS.md.

Headless smoke-тест

Godot_v4.7.1-stable_mono_win64.exe --headless --path . --quit-after 10

Windows x64 export

.\tools\export-windows.ps1 -Configuration Release
.\tools\export-windows.ps1 -Configuration Debug

Пресеты Windows x64 Release и Windows x64 Debug разведены также в редакторе. Скрипт использует очищенную staging-копию и жёстко выбирает --export-release либо --export-debug; без параметра собирается Release. Готовый self-contained каталог создаётся в artifacts/windows/. Подробности и smoke-проверка: docs/operations/WINDOWS_EXPORT.md.

Конфигурация

Настройки, доступные владельцу автомата, по умолчанию находятся в:

res://config/default_settings.json

При первом запуске приложение создаёт пользовательскую копию:

user://config/appsettings.json

В пользовательской конфигурации задаются:

  • fullscreen/debug-window режим;
  • целевой FPS;
  • разрешение и путь к внешнему DLC-пакету темы;
  • облегчённый режим эффектов;
  • таймауты сессии;
  • общая громкость, master mute и отдельная громкость промо-ролика;
  • доступность наличной и карточной оплаты, карточные пресеты, максимальная сумма и шаг ручного ввода;
  • безопасные таймауты бездействия, ожидания оплаты и показа результата;
  • включение промо, звук промо-роликов, fallback-текст, локальный постер и плейлист OGV/MP4/MOV/M4V;
  • ограничения файлового журнала.

Монтажные параметры оборудования вынесены в отдельный TOML-файл:

res://config/technical_settings.toml
user://config/technical_settings.toml

Пользовательская копия создаётся при первом запуске и не показывается в интерфейсе приложения. Она задаёт основной последовательный порт (COM* в Windows, /dev/ttyUSB*, /dev/ttyACM* или /dev/serial/by-id/* в Linux), mock-режим, параметры связи, наличие обоих хопперов, индексы и полярность их датчиков, сервисную кнопку и интервалы опроса. TOML применяется поверх устаревшего раздела Hardware в JSON; аргумент --mock-hardware остаётся последним безопасным переопределением. Изменения вступают в силу только после полного перезапуска. Схема, диапазоны и порядок проверки: docs/operations/TECHNICAL_CONFIGURATION.md.

Параметр [controller].reconnect_interval_seconds задаёт паузу между повторными открытиями порта. Штатно это 30 секунд: приложение повторяет подключение, если платы не было при запуске, и начинает новый полный отсчёт после отключения работавшей платы. Платёжные и выдающие команды при reconnect никогда не повторяются автоматически.

Модель настроек содержит цену жетона, бонусные правила, карточные суммы, телефон поддержки и плейлист промо-роликов. Датчики обоих хопперов сообщают только «достаточно» или «мало». Точные подтверждаемые остатки хранятся раздельно в user://data/token_inventory.json, начинаются с нуля, уменьшаются после подтверждённой выдачи и меняются отдельными действиями для каждого хоппера в разделе «Сервис». Два независимых переключателя управляют поведением: «Учитывать остаток жетонов» включает ведение и показ точного остатка, а «Не продавать больше остатка» отдельно запрещает покупку сверх сохранённого количества с учётом бонуса. Если ограничение выключено, остаток продолжает отображаться и списываться, но не блокирует оплату. Если выключен сам учёт, скрываются пересчёт, пополнение и приблизительное количество Home, а файл остатка не изменяется. Перед повторным включением учёта после реальных выдач требуется пересчёт.

Поддерживаются аргументы запуска:

--windowed
--fullscreen
--theme-dlc=D:\ExchangerThemes\active_theme.pck
--no-theme-dlc
--mock-hardware=true
--reduced-effects

Конфигурация версии 19 сохраняется через SettingsService атомарной заменой файла. Перед изменением создаётся appsettings.json.bak; повреждённый пользовательский JSON восстанавливается из последней корректной копии либо безопасных значений по умолчанию и не прерывает запуск.

В debug-сборке с домашнего экрана F10 переключает тот же сервисный вход; если защита PIN выключена, редактор открывается напрямую, а при включённой защите сначала используется общая PIN-клавиатура. В редакторе владельца доступны свой текст для речевого bubble и контакт техподдержки без отдельного предпросмотра раздела, общая громкость, master mute, отдельная громкость промо-ролика, короткий проверочный сигнал, стоимость жетона, бонусные правила, карточные пресеты, предел и шаг ручной суммы, доступность наличной/карточной оплаты, пользовательские таймауты, промо-poster/fallback/плейлист, защита PIN, предварительный расчёт и автоматическое сохранение. Из всех подходящих бонусных правил применяется один наибольший бонус; значения разных строк не суммируются. Нулевая цена, значения вне диапазона, повторяющиеся суммы/пороги и отключение обоих способов оплаты блокируют сохранение и показываются рядом с соответствующим полем. Пустой набор карточных пресетов допустим: пользователь сохраняет доступ к пункту «Другая сумма». Обычная release-сборка игнорирует действие F10. В production экран PIN открывается только аппаратным событием SERVICE, только из Home и только при отсутствии операции. Единый сервисный PIN включается в разделе «Общие» и дважды задаётся на экране приложения при следующем входе. Открытый PIN нигде не сохраняется: в JSON находятся только случайная соль, PBKDF2-SHA256-хеш и число итераций; PIN и производные значения не записываются в журналы. Тема/DLC, COM-порт, mock-режим, параметры окна/FPS, журналирование, диагностика и тестовая выдача не показываются владельцу. Аппаратные параметры изменяются только через отдельный TOML при техническом развёртывании. Полная матрица: docs/operations/SETTINGS_ACCESS_MATRIX.md. При тапе на текстовое поле открывается встроенная в тему экранная клавиатура с русской и английской раскладками, Shift и отдельной страницей спецсимволов; для числового поля открывается цифровая клавиатура. Обе панели можно скрыть кнопкой справа сверху, а поле автоматически прокручивается выше клавиатуры. Системная виртуальная клавиатура для этих полей отключена. Подробности: docs/operations/ON_SCREEN_KEYBOARD.md. Master и промо разведены по Godot audio buses Master и Advertisement, поэтому громкость промо-ролика не влияет на остальные звуки.

Внешняя тема остаётся частью deployment-конфигурации: отсутствующий или не прошедший проверку пакет оставит встроенную fallback-тему. Путь к .pck, включение DLC и ReducedEffects намеренно не показываются в настройках владельца.

Промо-ресурсы принимаются только из res://, user:// либо по абсолютному локальному пути. Постеры поддерживают PNG/JPG/JPEG/WEBP, видео — OGV (Ogg Theora), MP4, MOV и M4V. MP4/MOV/M4V воспроизводятся GDExtension addons/native_video через тот же VideoStreamPlayer; для них нужен Mobile или Forward+ (Vulkan), Compatibility не поддерживается. При выборе внешнего MP4/MOV/M4V ролик остаётся на месте и передаётся Native Video напрямую по исходному пути, включая Unicode и пробелы; копии, служебные ссылки и ASCII-псевдонимы не создаются. Существование файла проверяется до сохранения. Отключённое промо может временно хранить недоступный плейлист, но не читает файлы, не запускает декодирование и показывает на Home безопасный текст «ПРОМО ОТКЛЮЧЕНО».

Смена плейлиста использует штатный lifecycle Godot: активный player получает Stop(), его Stream очищается, а новый источник начинается с позиции 0. То же завершение выполняется при скрытии HomeScreen и освобождении узла; старый decoder и его аудио не сохраняются в runtime-кэше. Регрессионная сцена tests/runtime/NativeVideoLifecycleSmoke.tscn проверяет 50 смен двух внешних MP4, скрытие/возврат и многократное удаление активного AdvertisementPlayer.

Журналы

Операции приложения записываются в:

user://logs/exchanger.log
user://logs/operations.jsonl
user://logs/settings-audit.jsonl

exchanger.log содержит диагностические сообщения с уровнем и категорией. operations.jsonl хранит структурированные события начала и единственного итога по локальному OperationId. settings-audit.jsonl фиксирует только время, роль, названия изменённых разделов и необходимость перезапуска. При перезапуске незавершённые операции безопасно закрываются как UnknownAfterRestart, без автоматического повтора оплаты или выдачи. Журналы автоматически очищаются по сроку хранения и общему лимиту; платёжные секреты, значения настроек, полные пути и сырые ответы оборудования не записываются. Подробные правила и mock-чек-лист: docs/operations/RECOVERY_AND_MOCK_QA.md.

Структура проекта

res://
├── docs/                           # Архитектурные решения и анализ референсов
├── config/                         # Встроенная конфигурация
├── tests/Exchanger.Tests/          # NUnit-тесты чистого ядра и smoke-проверки файлов
├── src/
│   ├── core/
│   │   ├── Configuration/          # AppSettings и загрузка JSON
│   │   ├── TechnicalConfiguration/ # Монтажный TOML и его строгая валидация
│   │   ├── Logging/                # Файловый журнал
│   │   ├── Navigation/             # Реестр и менеджер экранов
│   │   ├── Session/                # Конечный автомат операции
│   │   └── ThemeManager/           # Runtime-контракт, fallback и загрузчик DLC
│   ├── hardware/
│   │   ├── Abstractions/           # IHardwarePort и IMachineController
│   │   ├── MockHardware/           # Имитация сырого транспорта
│   │   ├── MockMachine/            # Типизированный симулятор автомата
│   │   ├── SerialPortHardware/     # Desktop-порт Windows/Linux
│   │   └── UsbSerialHardware/      # Android-заглушка
│   ├── scenes/
│   │   ├── App/                    # Композиционный корень
│   │   ├── ServiceAccessScreen/    # Защищённый PIN-вход
│   │   ├── SettingsScreen/         # Настройки владельца и сервисный учёт
│   │   ├── HomeScreen/             # Домашний экран
│   │   ├── CashPaymentScreen/      # Оплата наличными
│   │   ├── CardAmountScreen/       # Выбор карточной суммы
│   │   ├── CardCustomAmountScreen/ # Цифровая клавиатура
│   │   ├── CardTerminalScreen/     # Ожидание банковского терминала
│   │   ├── SuccessScreen/          # Выдача и успех
│   │   └── ErrorScreen/            # Отказ и ошибки
│   └── ui/
│       ├── OnScreenKeyboard/       # Логика текстовой и цифровой клавиатур
│       ├── StaticBackground/       # Статический фон и слой анимированных спрайтов
│       ├── AdvertisementPlayer/     # Локальное видео и fallback
│       ├── TokenStockStatus/       # Состояние запаса жетонов
│       └── TouchInputGuard/        # Защита от мультитача
└── addons/                         # Сторонние плагины

Сцены, их C#-скрипты и специфичные ресурсы хранятся атомарно в одной тематической папке.

Темы и DLC

В Exchanger нет каталога разработки тем и встроенных .tres-тем. Единственная минимальная fallback-тема создаётся кодом, поэтому приложение сохраняет рабочий интерфейс без внешних файлов. При старте ThemeManager ищет один .pck по пути из пользовательского appsettings.json; успешно проверенный DLC полностью заменяет fallback до создания экранов. Manifest v2 предоставляет полные визуальные сцены всех девяти экранов: app-owned C#-контроллеры связываются с ними через metadata exchanger_binding_id, не передавая теме бизнес-логику.

Исходники тем, manifest, фоновые изображения и сцены находятся только в соседнем Godot-проекте ../ExchangerThemeDlcBuilder: themes/example/ служит готовым шаблоном, themes/space-pixel/ — текущей оформленной темой, остальные темы лежат в themes/<theme-id>/. Основное приложение содержит runtime-контракт имён сцен, binding-resolver и безопасный загрузчик. Для manifest v2 приложение сверяет закреплённый SHA-256 машиночитаемого каталога и проверяет все 481 обязательный биндинг девяти сцен по ID и Godot-типу. Горячего переключения и списка тем нет; после замены .pck приложение нужно перезапустить.

Современная production-сцена темы самодостаточна: её корень содержит metadata exchanger_self_contained_visuals = true, собственный Theme, прямой TextureRect ThemeBackground и прямой слой BackgroundDecoration. Все девять сцен пакета обязаны использовать один и тот же режим. Для такого пакета глобальные BackgroundTexturePath и BackgroundDecorationPath оставляют пустыми; приложение отклоняет смешанную или дублирующую визуальную композицию.

Runtime не переоформляет самодостаточную сцену: он не назначает ей другой Theme, type variations, размеры или theme overrides. Приложение находит готовые элементы по binding-id, подключает события и обновляет только функциональные значения — текст, видимость, доступность и состояние полей.

Тематические PCK считаются доверенными. Внутри production-сцен разрешены GDScript, сигналы, AnimatedSprite2D, AnimationPlayer, шейдеры, частицы, аудио, видео и другие runtime-возможности Godot. Формат, binding-id, пользовательские пути и сборка пакета описаны в docs/THEME_DLC.md.

Аппаратный слой

UI не обращается к порту напрямую. SessionController работает через типизированный IMachineController, а будущий протокольный адаптер будет использовать низкоуровневый IHardwarePort; поэтому экраны не зависят от COM-порта, Android USB API или строк wire-протокола.

Сейчас доступны:

  • MockHardwarePort — минимальная служебная заглушка транспорта для UI mock-режима;
  • PeripheryControllerSimulator — детерминированный wire-симулятор handshake, INPUT, платежей и обоих хопперов для интеграционных тестов без COM-порта;
  • MockMachineController — детерминированно имитирует наличный баланс, карточный результат, бинарный уровень запаса и подтверждение выдачи;
  • SerialPortHardware — реальный Windows/Linux-транспорт с отдельным worker, очередями и reconnect;
  • PeripheryMachineController — типизированный адаптер протокола универсального контроллера 2.3.5;
  • UsbSerialHardware — явно неподдерживаемая Android-заглушка.

COM-порт использует System.IO.Ports 8.0.0 и параметры 115200 8N1/None. Авторизация и платежи реализованы по спецификации 2.3.5. Обфусцированное представление ключа встроено в PeripheryControllerSecurity и восстанавливается XOR 0x37. Целевой режим — DualHopperAuto: 309N обслуживает первый хоппер, 409N — второй, пакеты ограничены 99 жетонами, а подтверждения OK/ERROR/CREDIT принимаются только от активного канала. Шаблон TOML включает оба хоппера, но остаётся безопасным благодаря use_mock = true; реальный профиль активируется только техническим специалистом. Команда выдачи автоматически не повторяется.

В актуальной разводке IN8 active-low переключает сервисный вход, IN1 сообщает уровень первого хоппера, IN4 — второго. Обычная выдача использует 309N и 409N и считает только соответствующие кадры HOPPER1/2 OK. Сервисный пересчёт запускается отдельно командами 301/300 или 401/400, считает только HOPPER1/2 PULSE=1 выбранного канала и завершается после 10 секунд тишины. Физические импульсные входы не участвуют в программном счёте. Общая полоска объединяет точный учёт и бинарные датчики в состояния «нет жетонов», «мало», «достаточно» и «много».

IMachineController также публикует безопасное сервисное состояние соединения. На главном экране штатный статус скрыт в production и виден в debug; авторизация, обрыв или аппаратная ошибка отображаются всегда, но без имени COM-порта, сырых кадров и деталей handshake. При потере связи запас становится Unknown, поэтому запуск новой оплаты блокируется.

Универсальность оборудования

Пока целевой ПК и тач-панель отсутствуют, проект не оптимизируется под одну модель устройства. UI проверяется на нескольких портретных разрешениях, параметры оборудования и сценариев выносятся в конфигурацию, а платформенные транспорты остаются за IHardwarePort. Фактические DPI, GPU, производительность, системный киоск-режим и работа в перчатках будут проверены на реальном аппарате без изменения границ UI и бизнес-логики.

Киоск-режим

Release-сборка настраивает borderless fullscreen, скрывает курсор и повторно применяет оконный профиль после потери фокуса либо изменения состава/разрешения дисплеев. Godot не может надёжно заблокировать Alt+Tab, Ctrl+Alt+Del и системные жесты, поэтому полный киоск дополнительно настраивается средствами Windows. Скрипты автозапуска/watchdog и безопасная процедура установки находятся в tools/windows; инструкция — docs/operations/WINDOWS_KIOSK_SETUP.md. Ускоренный и процессный soak описаны в docs/operations/SOAK_TEST.md.

Сторонние компоненты

В проекте подключены:

  • Godot AI;
  • GodotX Toast;
  • Log.gd;
  • Reload Current Scene.

Эти editor/development addons исключаются из production export; приложение не зависит от них.

Лицензии сторонних компонентов находятся рядом с ними в addons/ либо в отдельных сопроводительных файлах, например godot-ai-LICENSE.txt.

Лицензия проекта

Собственная лицензия Exchanger в настоящий момент не объявлена. Ошибочный корневой LICENSE, автоматически скопированный из GodotX Toast, удалён. Это не затрагивает лицензии сторонних компонентов.