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, удалён. Это не затрагивает лицензии сторонних компонентов.

Description
Программа для аппарата размена жетонов. Без тем будет выглядеть топорно
Readme 86 MiB
Languages
GDScript 63.5%
C# 35%
GAP 1%
PowerShell 0.5%