43 KiB
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 и поддержкой; - рабочие ветки наличной и карточной оплаты, включая другую сумму, ожидание терминала, успех и отказ;
ScreenManagerAutoload с централизованным реестром, 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
- Открыть
project.godotв Godot 4.7.1 Mono. - Дождаться импорта ресурсов и сборки C#-проекта.
- Запустить главную сцену клавишей
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, удалён. Это не затрагивает лицензии сторонних компонентов.