1689 lines
218 KiB
Markdown
1689 lines
218 KiB
Markdown
# Дорожная карта проекта Exchanger
|
||
|
||
## 1. Назначение документа
|
||
|
||
Этот файл — единая точка отслеживания разработки полноэкранного приложения вендингового аппарата для выдачи жетонов. Он фиксирует текущее состояние, принятые архитектурные решения, порядок реализации, критерии готовности, риски и вопросы, требующие согласования.
|
||
|
||
Дата формирования первоначального плана: **2026-08-20**.
|
||
|
||
Последняя актуализация состояния: **2026-09-07**.
|
||
|
||
## 2. Легенда статусов
|
||
|
||
- `[ ]` — задача не начата.
|
||
- `[~]` — задача выполняется.
|
||
- `[x]` — задача выполнена и проверена.
|
||
- `[!]` — задача заблокирована внешней зависимостью или решением.
|
||
- `[?]` — требуется уточнение или согласование.
|
||
|
||
Правило обновления: задача получает `[x]` только после выполнения указанных для неё проверок и критериев приёмки. При завершении этапа следует добавлять короткую запись в журнал прогресса в конце файла.
|
||
|
||
## 3. Цель продукта
|
||
|
||
Создать на Godot 4.7 Mono/C# надёжное портретное киоск-приложение для напольного разменника жетонов. Приложение должно:
|
||
|
||
1. Показывать интерфейс с единственной встроенной fallback-темой, заменяемой при запуске одним внешним DLC со статическим фоном и отдельными анимированными 2D-спрайтами.
|
||
2. Показывать цену жетона, бинарное состояние запаса «много/мало» и бонусные условия.
|
||
3. Поддерживать отдельные сценарии оплаты наличными и картой.
|
||
4. Безопасно отправлять команду выдачи жетонов через последовательный порт.
|
||
5. Дожидаться результата выдачи и показывать успех либо понятную ошибку.
|
||
6. Возвращаться в исходное состояние по таймаутам и после завершения сессии.
|
||
7. Работать прежде всего на Windows x64 и не связывать UI с конкретной реализацией порта, чтобы в будущем заменить её на Android USB-Serial.
|
||
8. Воспроизводить промо-контент на домашнем экране без влияния на основной сценарий.
|
||
9. Соответствовать портретному корпусу и пиксельной космической айдентике из подтверждённых референсов.
|
||
|
||
## 4. Текущее состояние проекта
|
||
|
||
- [x] Создан проект `Exchanger` на Godot 4.7.1 Mono.
|
||
- [x] Настроена сборка C# под .NET 8; Android-конфигурация проекта указывает .NET 9.
|
||
- [x] Выбран рендерер `mobile` (Vulkan): он нужен runtime-расширению Native Video для MP4/MOV/M4V.
|
||
- [x] C#-решение собирается без ошибок и предупреждений.
|
||
- [x] Подключены плагины `godot_ai`, `godotx_toast`, `log` и `reload_current_scene`.
|
||
- [x] Подключён runtime GDExtension `native_video`: настройки промо принимают OGV/MP4/MOV/M4V,
|
||
а export сохраняет его Windows/Linux библиотеки; внешние MP4/MOV/M4V сохраняют исходный путь.
|
||
Unicode-пути Windows передаются Native Video напрямую без копий, ссылок и ASCII-псевдонимов.
|
||
- [x] Устранён временный кэш player/stream: `Stop()`, `Stream = null`, смена источника и
|
||
`QueueFree()` проходят штатно; 50 циклов проверены в реальном `AdvertisementPlayer`.
|
||
- [x] Создана атомарная структура `src/core`, `src/scenes`, `src/ui`, `src/hardware` и каталогов ассетов; разработка тем полностью вынесена из репозитория приложения.
|
||
- [x] Созданы первые собственные C#-скрипты, ресурсы и сцены.
|
||
- [x] Главной сценой назначена `src/scenes/App/App.tscn`.
|
||
- [x] Добавлен пользовательский Input Action `kiosk_back`; устаревший `debug_cycle_theme` удалён вместе с переключением тем.
|
||
- [x] Корневой `README.md` полностью переписан под Exchanger.
|
||
- [x] Ошибочный корневой `LICENSE` от GodotX Toast удалён; лицензии сторонних компонентов сохранены рядом с ними.
|
||
- [x] Зафиксировано, что собственная лицензия Exchanger пока не объявлена.
|
||
- [x] Проанализированы восемь изображений и видео интерфейса; выводы сохранены в `docs/REFERENCE_ANALYSIS.md`.
|
||
- [x] Каталог `references/` исключён из Git через `.gitignore` и отмечен как непроизводственный источник концептов.
|
||
- [x] Динамический shader/particle-фон удалён; активная сцена использует `StaticBackground` со статическим цветом/изображением и отдельным слоем анимированных спрайтов.
|
||
- [x] Ландшафтный `StartScreen` удалён и заменён портретным `HomeScreen`; логический viewport синхронизирован с theme-preview на 720×1280.
|
||
- [x] Инициализирован локальный Git-репозиторий; remote и первый коммит будут настроены отдельным решением владельца проекта.
|
||
- [x] Полный runtime переведён на C#/.NET 8, девять атомарных экранов и один внешний
|
||
self-contained DLC; `space-pixel` принят по manifest v2 с 481 обязательным биндингом.
|
||
- [x] Подтверждён целевой `DualHopperAuto`: первый хоппер использует `30X`, `IN1` и `IN24`,
|
||
второй — `40X`, `IN4` и `IN25`, сервисная кнопка — `IN8`.
|
||
- [x] Windows x64 release собран как один `Exchanger.exe` со встроенными PCK и .NET outputs;
|
||
packaged smoke прошёл на mock и подтвердил авторизацию реального контроллера на COM3.
|
||
- [x] Windows export разделён на явные пресеты `Windows x64 Release` и `Windows x64 Debug`;
|
||
staging-скрипт жёстко выбирает соответствующий `--export-release` или `--export-debug`.
|
||
- [x] Этапы 4 и 5 закрыты: COM-транспорт и полный MVP-сценарий реализованы и покрыты
|
||
автоматическими проверками; оставшаяся физическая приёмка вынесена в раздел 4.1.
|
||
- [x] В разделе «Сервис» добавлено немедленное скрытие элементов Home: учётного остатка,
|
||
theme-owned персонажа справа и целого промо-блока; скрытые панели исключаются из маршрутов питомца.
|
||
- [x] Точный учёт жетонов и запрет покупки сверх остатка управляются раздельно: можно продолжать
|
||
показывать/списывать остаток, не ограничивая им оплату; отключение учёта скрывает сервисные действия.
|
||
- [~] Добавлен и проверен one-file Linux x86_64 export для Debian 13; платформенная валидация
|
||
serial-path завершена, udev/dialout и kiosk остаются открытыми.
|
||
|
||
### 4.1. Проверки на автомате
|
||
|
||
Этот раздел — единая очередь проверок, требующих физического автомата, его панели, контроллера,
|
||
платёжной периферии или целевого системного блока. Они не переоткрывают завершённые программные
|
||
этапы. Перед каждым аппаратным запуском требуется проверить фактическое подключение и занятость
|
||
порта, подготовить автомат и получить отдельное разрешение на конкретную группу тестов.
|
||
|
||
#### Интерфейс, панель и производительность
|
||
|
||
- [ ] Проверить читаемость с рабочего расстояния и при ярком освещении.
|
||
- [ ] Проверить управление пальцем и в перчатках: все девять экранов, прокрутку Settings,
|
||
текстовую и цифровую клавиатуры, первое касание и отсутствие действий по hover.
|
||
- [ ] Проверить масштабирование сетки 720×1280, ориентацию, калибровку touch и размеры зон
|
||
на фактическом разрешении панели.
|
||
- [ ] Повторить performance baseline на целевом ПК: FPS, CPU-время, память и draw calls
|
||
в fallback и `space-pixel`, в полном и облегчённом режимах.
|
||
- [ ] Профилировать одновременную работу анимированных спрайтов DLC и декодера реального MP4/OGV-промо-ролика в Mobile/Vulkan.
|
||
- [ ] Проверить отключение/возврат дисплея, потерю фокуса, kiosk/autostart и watchdog.
|
||
|
||
#### Контроллер, платежи и хопперы
|
||
|
||
- [ ] Повторить безопасную приёмку COM3: framing, AES-handshake, точный `READY!`, `INPUT`,
|
||
status-запросы, безопасное исходное состояние и несколько штатных reconnect-циклов.
|
||
- [ ] Проверить физическое отключение/возврат USB-COM: UI не зависает, worker не дублируется,
|
||
незавершённая выдача не повторяется, позднее подтверждение не завершает новую операцию.
|
||
- [ ] Проверить `IN8`/SERVICE, уровни запаса `IN1` и `IN4`, удержание кнопки без повторов и
|
||
открытие/закрытие Settings с включённым и выключенным PIN.
|
||
- [ ] Проверить монетник и купюроприёмник: включение/отключение каналов, `MONEY:*`, `ASK:*`,
|
||
накопление суммы, отмену, таймаут и отсутствие двойного учёта.
|
||
- [ ] Проверить безналичную оплату: точное совпадение суммы `163` и `MONEY:CASHLESS`,
|
||
`ASK:CASHLESS`, отмену, timeout/decline и keep-alive `161` реже 180 секунд.
|
||
- [ ] Проверить обычную выдачу обоих хопперов: `309N`/`409N`, пакеты 1–99, `APPEND=N`,
|
||
по одному подтверждению на каждый `HOPPER1/2 OK`, маршрутизацию и раздельное списание остатка.
|
||
- [ ] Проверить частичную/ошибочную выдачу: `ERROR` → `CREDIT=N`, запросы `3090/4090`,
|
||
отсутствие автоматического retry и единственный итог операции.
|
||
- [ ] Проверить сервисный пересчёт обоих хопперов: `301/300` и `401/400`, учёт только
|
||
`HOPPER1/2 PULSE=1`, завершение после 10 секунд тишины и безопасную остановку мотора.
|
||
- [ ] Проверить три сочетания политики на реальном автомате: учёт с лимитом, учёт без лимита и
|
||
полностью выключенный учёт; подтвердить блокировку card/cash до аппаратной команды, работу
|
||
бонусов, продолжение списания без лимита, скрытие сервисных действий и пересчёт перед включением.
|
||
- [ ] Проверить перезапуск/потерю питания во время операции: запись получает
|
||
`UnknownAfterRestart`, оплата и выдача автоматически не повторяются.
|
||
|
||
#### Длительная и установочная приёмка
|
||
|
||
- [ ] Выполнить непрерывный 24-часовой soak с повторными сессиями, оплатой, выдачей,
|
||
SERVICE, автосохранением, ротацией логов и периодическим отключением/возвратом дисплея.
|
||
- [ ] Провести чистую установку Windows-релиза на целевом устройстве; проверить доверие подписи,
|
||
конфигурацию, внешний DLC, автозапуск, обновление и откат.
|
||
- [ ] После программной Linux-адаптации запустить ELF на Debian 13 и принять графику, touch,
|
||
внешний DLC, USB-UART, udev/dialout, kiosk/autostart и watchdog.
|
||
Предварительное обнаружение выполнено: Debian распознал Silicon Labs CP2102 `10c4:ea60`
|
||
штатным `cp210x` и создал `/dev/ttyUSB0`; права и обмен с Exchanger ещё не проверялись.
|
||
- [ ] Если будет выбран Android-порт, провести на целевом ARM64-устройстве export/runtime,
|
||
USB permission/reconnect, Mobile renderer, touch, системные жесты, поворот,
|
||
энергосбережение и полный регрессионный сценарий.
|
||
|
||
## 5. Зафиксированные архитектурные решения
|
||
|
||
### 5.1. Рендерер
|
||
|
||
Использовать **Mobile renderer (Vulkan)** как базовый режим. Это обязательное требование
|
||
runtime-расширения Native Video для MP4/MOV/M4V; оно совместимо с 2D-пиксельным UI и целевой
|
||
Windows/Linux конфигурацией. Compatibility нельзя включать, пока требуется Native Video. Для
|
||
слабого GPU остаётся `ReducedEffects`; производительность и аппаратное декодирование должны быть
|
||
приняты на целевом железе в разделе 4.1.
|
||
|
||
### 5.2. Слои приложения
|
||
|
||
```text
|
||
Пользовательский интерфейс и экраны
|
||
↓
|
||
Контроллер сессии и конечный автомат операции
|
||
↓
|
||
Сервис протокола контроллера
|
||
↓
|
||
IHardwarePort
|
||
↙ ↘
|
||
MockHardwarePort SerialPortHardware
|
||
↘
|
||
UsbSerialHardware (stub)
|
||
```
|
||
|
||
UI не должен знать о `SerialPort`, потоках и формате строк протокола. Он реагирует на типизированные состояния и события контроллера сессии.
|
||
|
||
### 5.3. Состояния пользовательской сессии
|
||
|
||
```text
|
||
Home
|
||
├→ CashAccepting
|
||
│ → Dispensing
|
||
│ → Completed
|
||
└→ CardAmountSelection
|
||
→ CardCustomAmount (опционально)
|
||
→ CardTerminalWait
|
||
→ Dispensing
|
||
→ Completed
|
||
→ Home
|
||
|
||
Любое активное состояние
|
||
→ PaymentDeclined, Error или Cancelled
|
||
→ Home
|
||
```
|
||
|
||
Переход в `Completed` разрешён только после подтверждения выдачи контроллером. Сам факт отправки `DISPENSE` не считается успешной выдачей.
|
||
|
||
### 5.4. Сцены и глобальные сервисы
|
||
|
||
- Главная сцена: `src/scenes/App/App.tscn`.
|
||
- `App` создаёт два `CanvasLayer`: фон с `layer = 0` и UI с `layer = 1`.
|
||
- Экраны — отдельные атомарные сцены `Control` со своим C#-скриптом и локальными ресурсами.
|
||
- `ThemeManager` и `ScreenManager` регистрируются как Autoload, как требует ТЗ.
|
||
- `SessionController`, адаптер протокола и аппаратный сервис принадлежат главной сцене или отдельному композиционному узлу приложения; их не следует без необходимости превращать в глобальные Singleton.
|
||
- `ScreenManager` переключает сцены экранов и не содержит бизнес-логику оплаты или выдачи.
|
||
- `SessionController` является единственным владельцем состояния текущей операции.
|
||
- `HomeScreen` сразу показывает цену, запас, способы оплаты, промо-fallback и контакты поддержки.
|
||
- Платёжные сцены разделяются по назначению: `CashPaymentScreen`, `CardAmountScreen`, `CardCustomAmountScreen` и `CardTerminalScreen`.
|
||
- `AdvertisementPlayer` является пассивным UI-компонентом домашнего экрана и останавливает декодирование, когда экран скрыт.
|
||
|
||
### 5.5. Формат темы
|
||
|
||
В Exchanger существует одна программная fallback-тема. Разрабатываемые темы полностью вынесены в соседний `ExchangerThemeDlcBuilder` и подключаются только как один внешний Godot `.pck`. После загрузки runtime-представление `VendingThemeDefinition` содержит:
|
||
|
||
- стабильный идентификатор и отображаемое имя темы;
|
||
- `Theme` для Control-интерфейса;
|
||
- необязательную статическую `Texture2D` фона;
|
||
- необязательные `PackedScene` фоновых слоёв/декораций и именованных пассивных визуальных слотов экранов;
|
||
- для manifest v2 — полный набор из девяти пассивных визуальных сцен экранов с каноническими
|
||
именами и metadata binding-id;
|
||
- fallback-цвет; облегчённый режим хранится в конфигурации приложения.
|
||
|
||
Сам фон всегда статичен: `ColorRect` с fallback-цветом и, при наличии, неподвижный `TextureRect`. Анимированные спрайты темы добавляются отдельной `PackedScene`; такая сцена может содержать `AnimatedSprite2D` и `AnimationPlayer`, но обязана быть пассивной декорацией и не зависеть от экранов приложения. Фоновые шейдеры, частицы и видео не используются. Промо-ролик допускается только внутри отдельного UI-блока `AdvertisementPlayer` на `HomeScreen`.
|
||
|
||
Полносценовая тема не содержит бизнес-логику: приложение инстанцирует собственные C#-контроллеры,
|
||
до `_Ready` заменяет встроенный визуальный корень сценой DLC и разрешает кнопки, поля и динамические
|
||
области через стабильную metadata `exchanger_binding_id`. Вложенность узлов темы свободна. Manifest
|
||
v1 сохраняет обратную совместимость как стилевой пакет; v2 требует все девять сцен целиком и
|
||
точную копию каталога из 481 обязательного биндинга. Host закрепляет SHA-256 каталога и до
|
||
активации отклоняет неверные пути, root-имена, неизвестные или неверно типизированные binding-id
|
||
и дубли. Отсутствующий обязательный binding восстанавливается по каноническому `FallbackPath`
|
||
узлом старой сцены либо готовым элементом встроенной темы-заглушки.
|
||
|
||
В основном проекте нет каталога `themes/` и исходных ресурсов тем. Путь к единственному внешнему DLC берётся из пользовательского `appsettings.json`; пакет имеет версионированный manifest, изолированное пространство `res://exchanger_theme_dlc/`, монтируется без замены ресурсов приложения и требует перезапуска для обновления.
|
||
|
||
### 5.6. Потоки и последовательный порт
|
||
|
||
- Только реализация `SerialPortHardware` работает с `System.IO.Ports`.
|
||
- Чтение выполняется в отдельном потоке с корректной отменой и освобождением ресурсов.
|
||
- Фоновый поток не вызывает API Godot и не обращается к Node/Resource/UI.
|
||
- Принятые строки и изменения соединения передаются в поток Godot через `ConcurrentQueue` либо `CallDeferred`.
|
||
- Разбор строк протокола выполняется отдельно от транспорта и покрывается тестами.
|
||
- Повтор команды выдачи после неизвестного результата запрещён без идемпотентного идентификатора операции или явного подтверждения, что первая команда не была принята.
|
||
|
||
### 5.7. Конфигурация
|
||
|
||
- Встроенные безопасные значения: `res://config/default_settings.json`.
|
||
- Пользовательские настройки: `user://config/appsettings.json`.
|
||
- При первом запуске создаётся пользовательский файл на основе значений по умолчанию.
|
||
- Ошибочный файл не должен ломать запуск: ошибка логируется, применяются безопасные значения по умолчанию.
|
||
- Планируемые параметры: порт, baud rate, таймауты, путь и разрешение внешнего DLC темы, режим имитации оборудования, уровень эффектов, общая громкость и громкость промо-ролика, mute, параметры логирования, цена жетона, карточные суммы, бонусные правила, бинарное mock-состояние запаса, способы оплаты, телефон поддержки и плейлист промо-роликов.
|
||
- Секреты платёжного терминала в конфигурации приложения не хранятся; платёжная интеграция находится на стороне внешнего контроллера.
|
||
|
||
### 5.8. Критические эксплуатационные ограничения
|
||
|
||
- Godot может включить borderless fullscreen и перехватывать обычные действия приложения, но не может надёжно запретить Alt+Tab, Ctrl+Alt+Del или системные жесты Windows. Полная блокировка обеспечивается Windows Assigned Access/Shell Launcher, политиками ОС и учётной записью киоска.
|
||
- Тач-ввод принимает только первое касание (`Index == 0`). Дополнительные касания игнорируются централизованно.
|
||
- Минимальная интерактивная область — 64×64 px, основной текст — не меньше 24–32 px на целевом разрешении.
|
||
- В интерфейсе нет функций, доступных только по hover.
|
||
- Целевой UI проектируется в портретном 9:16 с нативным viewport 720×1280; панели большего разрешения используют масштабирование `canvas_items/expand`.
|
||
|
||
### 5.9. Требования из визуальных референсов
|
||
|
||
- Корпус — высокий напольный автомат с зелёной рамой, портретным экраном, физическими приёмниками справа и лотком выдачи ниже экрана.
|
||
- Основной стиль — пиксельная космическая тема: тёмно-синий фон, салатовые акценты, белые панели, НЛО и зелёный маскот.
|
||
- Домашний экран сразу показывает цену жетона, остаток, выбор наличной/карточной оплаты, действие выдачи, промо-ролик и контакты поддержки.
|
||
- Наличная оплата отображает накопленный баланс и инструкции для купюро- и монетоприёмника.
|
||
- Карточная оплата содержит пресеты сумм, ввод другой суммы, ожидание терминала и отдельный результат.
|
||
- Бонусные жетоны рассчитываются конфигурируемыми правилами, а не зашиваются в UI.
|
||
- Референсные файлы не являются production-ассетами; права, финальные исходники, тарифы и бренд требуют подтверждения.
|
||
|
||
## 6. Целевая структура каталогов
|
||
|
||
```text
|
||
res://
|
||
├── config/
|
||
│ └── default_settings.json
|
||
├── src/
|
||
│ ├── core/
|
||
│ │ ├── Configuration/
|
||
│ │ ├── Logging/
|
||
│ │ ├── ThemeManager/
|
||
│ │ ├── Navigation/
|
||
│ │ └── Session/
|
||
│ ├── hardware/
|
||
│ │ ├── Abstractions/
|
||
│ │ ├── MockHardware/
|
||
│ │ ├── SerialPortHardware/
|
||
│ │ ├── UsbSerialHardware/
|
||
│ │ └── ControllerProtocol/
|
||
│ ├── scenes/
|
||
│ │ ├── App/
|
||
│ │ ├── HomeScreen/
|
||
│ │ ├── CashPaymentScreen/
|
||
│ │ ├── CardAmountScreen/
|
||
│ │ ├── CardCustomAmountScreen/
|
||
│ │ ├── CardTerminalScreen/
|
||
│ │ ├── SuccessScreen/
|
||
│ │ └── ErrorScreen/
|
||
│ ├── ui/
|
||
│ │ ├── StaticBackground/
|
||
│ │ ├── AdvertisementPlayer/
|
||
│ │ ├── TokenStockStatus/
|
||
│ │ └── TouchInputGuard/
|
||
│ └── vfx/
|
||
│ └── SuccessEffect/
|
||
└── tests/
|
||
├── Unit/
|
||
├── Integration/
|
||
└── Fixtures/
|
||
```
|
||
|
||
Структура уточняется по мере реализации, но сохраняется правило атомарности: сцена, скрипт и специфичные ресурсы элемента лежат в одной тематической папке.
|
||
|
||
## 7. План реализации
|
||
|
||
### Этап 0. Базовые решения и гигиена репозитория — завершён
|
||
|
||
Цель: получить однозначную техническую основу до реализации UI и оборудования.
|
||
|
||
- [x] Зафиксировать продуктовые требования в `ROADMAP.md`.
|
||
- [x] Зафиксировать исходный Compatibility renderer как исторический baseline.
|
||
- [x] Обновить корневой `README.md`: назначение, требования, запуск, структура, конфигурация и состояние проекта.
|
||
- [x] Удалить ошибочную корневую лицензию GodotX Toast, сохранив лицензии сторонних аддонов; собственную лицензию пока не объявлять.
|
||
- [x] Инициализировать локальный Git-репозиторий; подключение remote и первый коммит оставить отдельным решением владельца.
|
||
- [x] Решить хранение проектных инструкций: `.agents/AGENTS.md` версионируется и не исключается через `.gitignore`.
|
||
- [x] Добавить файл архитектурных решений `docs/architecture/decisions.md`.
|
||
- [x] Зафиксировать границу протокола: до появления документации wire-формат и команды не выдумываются; документация является входом Этапа 3, реальный COM — Этапа 4.
|
||
- [x] Принять универсальную аппаратную стратегию: не привязывать UI, конфигурацию и бизнес-логику к отсутствующему целевому ПК/GPU/тач-контроллеру.
|
||
- [x] Отложить выбор модели контроллера и проверку реального COM-обмена до добавления владельцем списка команд и документации в `references/`.
|
||
- [x] Зафиксировать контроллер единственным источником сведений об оплате, запасе, приёмниках, выдаче и аппаратных ошибках; конкретные сообщения определит будущая документация.
|
||
|
||
Критерий выхода выполнен: стабильны платформонезависимая граница `IHardwarePort`, универсальные требования к UI и правило не проектировать неизвестный протокол. Аппаратно-зависимые детали оформлены как входные данные Этапов 3–4 и не блокируют UI, mock-сценарии и бизнес-логику ранних этапов.
|
||
|
||
### Этап 1. Первый вертикальный срез: запуск, тема и стартовый экран — завершён
|
||
|
||
Цель: получить запускаемое полноэкранное приложение с первой темой и безопасной заглушкой оборудования.
|
||
|
||
#### 1.1. Настройки приложения
|
||
|
||
- [x] Установить базовый viewport 1920×1080 и режим растяжения `canvas_items/expand`.
|
||
- [x] Включить borderless fullscreen для production-профиля.
|
||
- [x] Предусмотреть удобный оконный debug-режим через конфигурацию или аргумент запуска.
|
||
- [x] Сохранить исходный Compatibility renderer как исторический baseline.
|
||
- [x] Настроить ограничение/целевую частоту 60 FPS и поведение при потере фокуса.
|
||
- [x] Добавить главную сцену `App` и назначить её в `application/run/main_scene`.
|
||
- [x] Проверить запуск на 1920×1080 и другом соотношении сторон: 1024×768, а также fullscreen 2560×1440.
|
||
|
||
#### 1.2. Конфигурация и логирование
|
||
|
||
- [x] Реализовать типизированную модель `AppSettings` с валидацией диапазонов.
|
||
- [x] Реализовать загрузку defaults → пользовательский JSON → аргументы запуска.
|
||
- [x] Добавить безопасное восстановление после битого/неполного JSON.
|
||
- [x] Реализовать журнал ключевых событий с временными метками и категориями.
|
||
- [x] Добавить ротацию/ограничение размера файлов в `user://logs`.
|
||
- [x] Не записывать в лог платёжные секреты и лишние персональные данные.
|
||
|
||
#### 1.3. Тема, статический фон и анимированные спрайты
|
||
|
||
- [x] Создать `VendingThemeDefinition`.
|
||
- [x] Создать базовый UI Theme со шрифтами, размерами, отступами и состояниями кнопок.
|
||
- [x] Создать первую тему `cosmic`.
|
||
- [x] Реализовать `ThemeManager` Autoload; первоначальный реестр позднее заменён моделью fallback + один DLC.
|
||
- [x] Исключить жёсткие пути к теме из экранов.
|
||
- [x] Применять одну активную тему ко всем экранам; горячая смена позднее удалена в пользу замены DLC при перезапуске.
|
||
- [x] Создать `StaticBackground`: обязательный `ColorRect` и необязательный статический `TextureRect`.
|
||
- [x] Передавать fallback-цвет и статическую текстуру из определения темы.
|
||
- [x] Поддержать отдельную тематическую сцену анимированных 2D-спрайтов через `PackedScene`.
|
||
- [x] Отключать необязательные анимированные спрайты в облегчённом режиме, сохраняя статический фон.
|
||
- [x] Удалить из активной темы фоновые шейдеры и частицы как не соответствующие референсам.
|
||
|
||
#### 1.4. StartScreen и аппаратная заглушка
|
||
|
||
- [x] Создать атомарную сцену `StartScreen` с логотипом и кнопкой «Начать».
|
||
- [x] Обеспечить области касания не меньше 64×64 px и читаемый текст.
|
||
- [x] Временная debug-кнопка смены темы была реализована для прототипа и позднее полностью удалена.
|
||
- [x] Создать `IHardwarePort` без зависимости от Godot UI.
|
||
- [x] Создать `MockHardwarePort`, который логирует команды и воспроизводит ответы по сценарию.
|
||
- [x] Создать `UsbSerialHardware` как явно неподдерживаемый Android stub.
|
||
- [x] Создать каркас `SerialPortHardware`, не открывая реальный порт при старте сцены.
|
||
- [x] Проверить наличие `System.IO.Ports`: сборка отсутствует в установленных reference/shared packs .NET.
|
||
- [x] Получено разрешение устанавливать необходимые NuGet-пакеты; `System.IO.Ports` будет добавлен только при фактическом начале Этапа 4.
|
||
|
||
Критерии приёмки этапа:
|
||
|
||
- приложение запускается в главную сцену без ошибок;
|
||
- стартовый экран корректен в fullscreen и debug-window режимах;
|
||
- единая активная тема применяется к UI и фону; позднее источником стал fallback либо один внешний DLC при запуске;
|
||
- fallback-фон включается настройкой;
|
||
- mock получает тестовую команду, а UI не зависит от конкретного транспорта;
|
||
- повторная C#-сборка и запуск из Godot проходят без ошибок.
|
||
|
||
### Этап 2. Портретный интерфейс, навигация и жизненный цикл сессии — завершён
|
||
|
||
Цель: перестроить технический прототип под портретный аппарат из референсов и реализовать детерминированные переходы без реального оборудования.
|
||
|
||
- [x] Перевести viewport и debug-window в портретный режим; после сверки с theme-preview закрепить нативную сетку 720×1280.
|
||
- [x] Реализовать `ScreenManager` Autoload и контейнер экранов в `App`.
|
||
- [x] Загружать экраны через централизованный `ScreenRegistry`, а не из разрозненных жёстких путей.
|
||
- [x] Сохранить общий статический фон между экранами и добавить короткие fade-переходы только для UI.
|
||
- [x] Блокировать ввод на время перехода полноэкранным `TransitionInputBlocker`; устранить перенос отпускания тача на новый экран.
|
||
- [x] Заменить `StartScreen` на `HomeScreen` с ценой, запасом, двумя способами оплаты, промо-fallback и контактами поддержки.
|
||
- [x] Создать `CashPaymentScreen` с балансом, расчётом жетонов/бонуса и инструкциями физических приёмников.
|
||
- [x] Создать `CardAmountScreen` с конфигурируемыми пресетами сумм.
|
||
- [x] Создать `CardCustomAmountScreen` с цифровой клавиатурой, ограничениями и подтверждением.
|
||
- [x] Создать `CardTerminalScreen` с ожиданием, отменой и обратным отсчётом.
|
||
- [x] Создать `SuccessScreen` с режимами `Dispensing` и `Completed`.
|
||
- [x] Создать `ErrorScreen` с отдельными визуальными состояниями отказа оплаты, связи, запаса и выдачи.
|
||
- [x] Реализовать `AdvertisementPlayer` с локальным видео, циклом, fallback-постером и остановкой на скрытых экранах.
|
||
- [x] Добавить модели `PricingSettings`, `BonusRule`, `PaymentPreset`, `BrandingSettings` и `AdvertisementSettings`.
|
||
- [x] Добавить компонент `TokenStockStatus` только с состояниями `Enough/Low/Unknown`, без числового остатка и `Empty`.
|
||
- [x] Реализовать `SessionController` и явные состояния сессии.
|
||
- [x] Реализовать общий таймаут бездействия 30–60 секунд для платёжных экранов.
|
||
- [x] Реализовать отдельный таймаут карточного терминала 120 секунд.
|
||
- [x] Реализовать возврат после успеха через 10 секунд.
|
||
- [x] Сбрасывать выбранную сумму/способ оплаты, таймеры и временные данные при возврате в `Home`.
|
||
- [x] Централизованно фильтровать мультитач и принимать только первое касание.
|
||
- [x] Защитить навигацию, платёжные действия и выдачу от повторного тапа, переноса отпускания и повторной команды.
|
||
- [x] Восстанавливать безопасный HomeScreen после неизвестного состояния сессии.
|
||
|
||
Критерии приёмки этапа:
|
||
|
||
- весь сценарий проходится на mock без реального COM-порта;
|
||
- назад, отмена и каждый таймаут приводят к определённому состоянию;
|
||
- быстрые повторные касания не создают две сессии и не дублируют выдачу;
|
||
- все экраны используют текущую тему и не владеют бизнес-логикой.
|
||
- портретный UI соответствует композиции референса, нативно рендерится в 720×1280 и масштабируется на панели 9:16;
|
||
- промо-ролик не продолжает декодироваться вне HomeScreen и имеет рабочий fallback.
|
||
|
||
### Этап 3. Протокол контроллера и симулятор оборудования — завершён
|
||
|
||
Цель: отделить строковый протокол от транспорта и проверить все ветки сценария.
|
||
|
||
Условие входа выполнено: владелец проекта добавил в `references/` документацию контроллера 2.3.5 и список команд. Этап закрывает документированный wire-профиль и безопасное поведение для отсутствующих форматов. Production-команда силового канала, физические номера входов и проверка AES на плате относятся к последующей аппаратной интеграции и не подменяются предположениями.
|
||
|
||
- [x] Изучить спецификацию универсального контроллера 2.3.5 и описать поддерживаемую часть в `docs/hardware/PERIPHERY_CONTROLLER_2_3_5.md`.
|
||
- [x] Реализовать ASCII-фрейминг по `CR`/`LF` с лимитом буфера, минимальной длиной и timeout 100 мс.
|
||
- [x] Реализовать типизированные команды авторизации, оплаты, отмены, status-запросов и legacy-хоппера; отсутствующий production-профиль силового канала представлен явным безопасным `Unsupported`.
|
||
- [x] Реализовать типизированные ответы startup-баннера `periphery controller ver_<version>`, `CHAL`, `RAES`, `AUTH_PC_OK`, `AUTH_CNT_OK`, точного `READY!`, `MONEY:*`, `HOPPER1 APPEND/OK/ERROR/CREDIT/PULSE`, совместимые ответы второго хоппера, `SERVICE`, `INPUT` и обобщённые ошибки; недокументированные status/heartbeat-ответы безопасно классифицируются как неизвестные.
|
||
- [x] Привязать подтверждённые входы новой прошивки: active-low `IN8` публикует сервисный запрос по одному фронту, `IN1` задаёт бинарный запас первого хоппера, `IN4` — второго. `IN24` сохранён как физический датчик первого канала, но runtime считает подтверждения строками платы; оба канала `30X/40X` включены и остаются конфигурируемыми.
|
||
- [x] Добавить бинарное состояние запаса `Enough/Low` из реального `INPUT<bitmask>` (вариант с пробелом тоже принимается) без вымышленного точного количества; до первого снимка либо при отключённом входе используется `Unknown`.
|
||
- [x] Добавить события принятой наличной суммы и обязательные подтверждения `ASK:COINS/BILL`.
|
||
- [x] Добавить запуск/отмену Вендиста, keep-alive и `Approved` по `MONEY:CASHLESS`; несовпадение суммы даёт `Declined`, а `Cancelled/Timeout` воспроизводятся доменным mock и таймером сессии, поскольку отдельных wire-сообщений в спецификации нет.
|
||
- [x] Связать действия с аппаратными последовательностями: наличные `162 → 141 → 151`, остановка
|
||
наличных `142 → 152`, подтверждение карточной суммы `142 → 152 → 161 → 163<amount>`,
|
||
отмена/таймаут карты `162`; повторная отмена не засоряет FIFO одинаковыми командами.
|
||
- [x] Обработать частичные строки, несколько сообщений в одном чтении, timeout, переполнение, не-ASCII и неизвестные кадры.
|
||
- [x] Добавить доменную таблицу типизированных ошибок и безопасных текстов; документированные hopper/transport/controller ошибки сопоставлены, неизвестные коды сворачиваются в безопасный `Unavailable` без утечки сырого кадра.
|
||
- [x] Сырые строки оборудования и внутренние исключения не передаются в пользовательский интерфейс.
|
||
- [x] Mock покрывает успех, отказ, отмену, таймаут, disconnect, полную/частичную ошибку и задержку; `PeripheryControllerSimulator` дополнительно прогоняет настоящий handshake/parser, malformed-кадр, оплату, датчик и legacy-выдачу без COM-порта.
|
||
- [x] Добавлен debug-only режим ручной эмуляции оплаты, потери связи и ошибок выдачи.
|
||
- [x] Зафиксировать at-most-once-политику выдачи: адаптер подавляет повторную команду и не выполняет retry после обрыва или ошибки; новая явная операция разрешена без перезапуска приложения.
|
||
- [x] Для обоих хопперов отправлять пакеты `309N`/`409N` без разделителя (1–99 жетонов), считать `APPEND=N` подтверждением очереди и ждать N соответствующих кадров `OK`; после `ERROR` дождаться `CREDIT=N` активного канала и вернуть частичный результат. Большие выдачи разбиваются и маршрутизируются по раздельным учётным остаткам; `3090/4090` запрашивают остаток кредита.
|
||
|
||
Критерий выхода выполнен: все ветви конечного автомата воспроизводимы детерминированно без физического аппарата, parser/authenticator/simulator не зависят от Godot Node, а unsupported-возможности не активируются по предположениям.
|
||
|
||
### Этап 4. Реальный Windows COM-порт — завершён
|
||
|
||
Цель: подключить аппаратный транспорт без нарушения потоковой безопасности Godot.
|
||
|
||
- [x] Реализовать открытие/закрытие `SerialPort` с port name и параметрами протокола 115200 8N1/None.
|
||
- [x] Реализовать отдельный worker-поток чтения/записи и механизм отмены.
|
||
- [x] Реализовать потокобезопасные очереди входящих событий и исходящих команд.
|
||
- [x] Публиковать события соединения и кадры только в главном потоке Godot через `ProcessPendingEvents()`.
|
||
- [x] Реализовать последовательную очередь отправки команд с окончанием `CRLF`.
|
||
- [x] Добавить read/write timeout и периодическую попытку повторного открытия порта с конфигурируемым интервалом.
|
||
- [x] Не выполнять автоматический повтор выдачи, если результат первой команды неизвестен.
|
||
- [x] Корректно отменять worker, закрывать и освобождать порт при выходе/перезапуске.
|
||
- [x] Программно обрабатывать исчезновение USB-COM и повторное открытие устройства; логика и
|
||
200 циклов симуляции проверены, физическая приёмка кабеля вынесена в раздел 4.1.
|
||
- [x] Не журналировать сырые кадры и секрет handshake; неизвестные/developer-кадры логируются только по категории.
|
||
- [x] Добавить атомарный сервисный индикатор `Disconnected/Authenticating/Ready/Faulted`: в production штатный `Ready` скрыт, а обрыв и ошибка видимы без раскрытия COM-порта и протокольных данных.
|
||
- [x] Провести автоматический стресс-тест 200 последовательных переподключений с обрывом во время выдачи; команда не повторяется, отложенное подтверждение не принимается, переходы состояния не накапливаются.
|
||
- [x] Подготовить отдельный аппаратный NUnit-набор для платы: safe handshake/status/input/reconnect, ручные SERVICE/cash/sensor, отдельно подтверждаемые real payment, hopper и USB-cable сценарии с TRX-отчётом.
|
||
Критерии приёмки этапа:
|
||
|
||
- ни один фоновый поток не обращается к Godot API;
|
||
- отключение кабеля не замораживает UI;
|
||
- реконнект не создаёт лишних потоков и подписок;
|
||
- выдача не дублируется после таймаута или обрыва связи;
|
||
- приложение корректно закрывает порт.
|
||
|
||
Этап завершён: транспорт, потоки, reconnect, at-most-once и безопасное освобождение порта
|
||
реализованы и проверены автоматически; ранее COM3/framing/AES-handshake, `INPUT`, безопасные
|
||
status-запросы и три reconnect-цикла подтверждены на плате. Полная физическая приёмка автомата
|
||
ведётся отдельно по разделу 4.1 и не является незавершённой программной задачей этапа.
|
||
|
||
### Этап 5. Полный MVP-сценарий автомата — завершён
|
||
|
||
Цель: интегрировать оплату, выдачу, обратную связь и пользовательские состояния.
|
||
|
||
- [x] При выборе способа оплаты создавать уникальный локальный контекст операции и запись в журнале.
|
||
- [x] Реализовать расчёт жетонов по цене и бонусным правилам для наличной и карточной веток.
|
||
- [x] Блокировать начало оплаты при `Unknown`; разрешать при `Enough` и `Low`, не имитируя проверку несуществующего точного остатка.
|
||
- [x] Для наличной ветки обновлять баланс по типизированным событиям и явно определять момент начала выдачи.
|
||
- [x] Для карточной ветки передавать выбранную сумму аппаратной границе и различать Approved/Declined/Cancelled/Timeout.
|
||
- [x] На экране ожидания терминала разделить действия: верхняя кнопка «Назад» отменяет
|
||
активный безналичный запрос и возвращает к выбору суммы, красная «ОТМЕНА» завершает сессию.
|
||
- [x] Перейти в ожидание оплаты и принять только релевантный сигнал текущего состояния.
|
||
- [x] Игнорировать просроченное подтверждение от предыдущей mock-сессии.
|
||
- [x] После подтверждения оплаты один раз сформировать команду выдачи.
|
||
- [x] Поддержать крупные тарифы: общее количество выдаётся последовательными пакетами `309N`, следующий пакет отправляется после N кадров `HOPPER1 OK` предыдущего.
|
||
- [x] Показывать состояние «Идёт выдача», не преждевременный успех.
|
||
- [x] Обновлять прогресс/счётчик по каждому подтверждённому контроллером импульсу выдачи.
|
||
- [x] Показывать «Получите жетоны» только после подтверждения завершения.
|
||
- [x] Сопоставить ошибки с типизированными безопасными действиями `ReturnHome`/`TryAnotherPaymentMethod`/`ContactSupport`.
|
||
- [x] Не показывать действие «Повторить» после ошибок: экран ошибки разрешает только возврат
|
||
домой, смену способа оплаты либо обращение в поддержку и никогда не повторяет выдачу.
|
||
- [x] Логировать начало и единственный итог с локальным `OperationId` без платёжных секретов;
|
||
отсутствующий в протоколе transaction ID не эмулируется и не используется для небезопасной сверки.
|
||
- [x] Выполнить автоматический сброс после успеха и управляемый сброс после ошибки.
|
||
- [x] При старте закрывать незавершённые локальные записи как `UnknownAfterRestart` без
|
||
автоматического retry; при отсутствии документированного запроса статуса операция не повторяется.
|
||
|
||
Критерий готовности MVP выполнен программно: полный сценарий от `Home` до подтверждённой выдачи
|
||
проходит через типизированный mock и детерминированный wire-симулятор, а известные ошибки
|
||
завершаются безопасным и понятным состоянием. Сквозная физическая проверка на автомате вынесена
|
||
в раздел 4.1.
|
||
|
||
### Этап 6. Киоск-режим, настройки и эксплуатационная надёжность — завершён
|
||
|
||
Цель: подготовить приложение к работе без постоянного оператора и дать авторизованному сервисному
|
||
персоналу безопасный способ изменять эксплуатационные параметры без ручного редактирования JSON.
|
||
|
||
- [x] Настроить borderless fullscreen и скрытие курсора в production.
|
||
- [x] Подготовить инструкцию Windows kiosk/shell setup для блокировки системных действий.
|
||
- [x] Добавить автоматический запуск приложения после входа/загрузки Windows.
|
||
- [x] Добавить watchdog/перезапуск процесса средствами ОС.
|
||
- [x] Добавить обработку потери фокуса, смены разрешения и состава дисплеев.
|
||
- [x] Не позволять пользовательскому вводу открывать debug/service функции: `F10` работает только
|
||
в debug-сборке из `Home`, production-вход начинается только с аппаратного `SERVICE`.
|
||
- [x] Реализовать защищённый PIN-вход в сервисный режим с лимитом попыток и временной блокировкой;
|
||
единый PIN включается и впервые задаётся внутри приложения, в конфигурации хранится только PBKDF2-хеш с солью.
|
||
- [x] Исключить из настроек владельца версию, тему/DLC, порт и контроллер, окно/FPS, диагностику,
|
||
журналы и тестовую выдачу; технические параметры оставить только в deployment-конфигурации.
|
||
|
||
#### 6.1. Редактор настроек программы
|
||
|
||
- [x] Провести инвентаризацию `AppSettings` и разделить параметры на операторские, инженерные,
|
||
debug-only и секретные; AES-ключ, платёжные секреты и сырые команды контроллера никогда не
|
||
показывать и не сохранять через UI.
|
||
- [x] Ввести единый `SettingsService`/репозиторий конфигурации: экраны работают с валидированной
|
||
рабочей копией и неизменяемым активным снимком, а не записывают JSON напрямую.
|
||
- [x] Создать атомарный экран настроек владельца внутри защищённого режима с пятью разделами:
|
||
«Общие», «Звук», «Промо», «Тарифы» и «Оплата».
|
||
- [x] Добавить общую громкость 0–100%, mute и отдельную громкость промо-ролика; применять
|
||
значения через выделенные Godot audio buses и сохранять их в пользовательской конфигурации.
|
||
- [x] Добавить безопасную кнопку проверки звука, которая не запускает оплату, выдачу или
|
||
полноразмерный промо-ролик.
|
||
- [x] Сделать редактируемой стоимость одного жетона `TokenPriceRubles`; показывать рядом
|
||
предварительный расчёт количества жетонов для типовых сумм и запрещать нулевое/отрицательное
|
||
значение.
|
||
- [x] Реализовать редактор дополнительных жетонов `BonusRules`: строки «минимальная сумма →
|
||
бонусные жетоны», добавление, удаление и изменение правил, сортировка порогов, запрет дублей и
|
||
отрицательных значений.
|
||
- [x] Явно зафиксировать в UI семантику бонусов: применяется одно максимальное подходящее правило,
|
||
бонусы разных строк не суммируются; до сохранения показывать итог «базовые + бонусные» для
|
||
контрольных сумм.
|
||
- [x] Реализовать настройку карточных пресетов: добавление/удаление сумм, сортировка, отсутствие
|
||
дублей, максимальная сумма и шаг ручного ввода; валидировать суммы относительно цены жетона и
|
||
правил `PricingPolicy`.
|
||
- [x] Добавить отдельное включение/отключение наличной и карточной оплаты; не позволять сохранить
|
||
конфигурацию, в которой отключены оба пользовательских способа оплаты.
|
||
- [x] Сделать настраиваемыми пользовательские таймауты бездействия, ожидания оплаты и показа
|
||
результата в безопасных пределах; рядом объяснить влияние каждого параметра.
|
||
- [x] Добавить редактирование названия автомата, подзаголовка и телефона поддержки с ограничениями
|
||
длины и preview итогового текста.
|
||
- [x] Добавить настройки промо: включение, список промо-роликов, poster, fallback-текст, mute и
|
||
громкость; проверять существование и допустимость локальных путей до сохранения.
|
||
- [x] Добавить экранный ввод для настроек: RU/EN/спецсимволы для текстовых полей, отдельную
|
||
цифровую клавиатуру для `SpinBox`, Shift, каретку, удаление/очистку и кнопку скрытия справа
|
||
сверху; автоматически поднимать активное поле над клавиатурой и применять число при выходе.
|
||
- [x] Оставить путь к внешнему DLC, `ReducedEffects`, журналирование, параметры окна, mock-режим,
|
||
COM-порт и соединение только в deployment-конфигурации, полностью исключив их из UI владельца.
|
||
- [x] Выделить аппаратную deployment-конфигурацию в отдельный
|
||
`user://config/technical_settings.toml`: основной порт, безопасные параметры связи, наличие
|
||
обоих хопперов, их входы/полярности и сервисная кнопка валидируются целиком и применяются только
|
||
после перезапуска; при повреждении пользовательского файла используется встроенный mock-профиль.
|
||
- [x] Не давать изменять тарифы, бонусы, способы оплаты, тему и аппаратные параметры во время
|
||
активной операции; открытая сессия всегда завершается со снимком настроек, с которыми началась.
|
||
- [x] Реализовать валидируемое автосохранение с debounce 0,5 с: промежуточный некорректный ввод
|
||
не записывается, ошибки остаются рядом с полями, а повторный фронт `IN8` сохраняет ожидающие
|
||
допустимые изменения и закрывает настройки. Экранная кнопка «Сохранить и выйти» в неподвижном
|
||
нижнем футере использует тот же безопасный путь завершения редактирования.
|
||
- [x] Сохранять `appsettings.json` атомарно через временный файл и замену, перед изменением держать
|
||
последнюю корректную резервную копию и автоматически восстанавливаться после повреждения.
|
||
- [x] Валидировать весь набор настроек до записи и показывать ошибки рядом с конкретным полем;
|
||
частичное применение некорректной конфигурации запрещено.
|
||
- [x] Разделить параметры на применяемые немедленно и требующие перезапуска; после автосохранения
|
||
показывать понятный статус и не выполнять самопроизвольный restart во время операции.
|
||
- [x] Добавить безопасный журнал изменения конфигурации: дата/время, роль, раздел и факт изменения без
|
||
старого/нового секретного значения, полного пути, платёжных данных и сырых кадров контроллера.
|
||
- [x] Покрыть нормализацию, граничные значения, round-trip JSON, миграцию версии, резервную копию,
|
||
бонусные правила и запрет изменения активной операции unit/integration-тестами.
|
||
- [x] Провести runtime-проверку редактора владельца на 1080×1920 и 720×1280: пять доступных вкладок,
|
||
отсутствие технических панелей, тач-действия, PIN-клавиатура и длинные/некорректные значения;
|
||
собственные текстовая и цифровая клавиатуры проверяются в fallback и внешней теме, а работа
|
||
пальцем и в перчатках остаётся физической приёмкой Этапа 8.
|
||
|
||
Критерий готовности настроек: владелец может безопасно изменить звук, цену жетона, бонусы,
|
||
карточные суммы, доступность способов оплаты, таймауты, поддержку и промо; после
|
||
перезапуска значения сохраняются, повреждённый файл восстанавливается, а начатая операция никогда
|
||
не меняет тарифы или аппаратную конфигурацию посередине сценария.
|
||
|
||
#### 6.2. Эксплуатационная надёжность
|
||
|
||
- [x] Добавить очистку/ротацию старых логов и ограничение объёма `user://`.
|
||
- [x] Проверить восстановление после повреждения конфигурации, обрыва питания во время записи и
|
||
несовместимой версии файла без потери последней корректной резервной копии.
|
||
- [x] Добавить ускоренную модель 24 часов (1440 минут, 480 выдач), стресс 10 000 жизненных циклов
|
||
`SessionFlow` и процессный runner для 24-часового прогона; runner проверен минутным прогоном
|
||
12/12 сэмплов, пик working set 217.73 МБ. Реальный 24-часовой прогон переносится в физическую
|
||
приёмку Этапа 8, поскольку целевой ПК пока отсутствует.
|
||
|
||
### Этап 7. Производительность, UX и доступность — выполняется
|
||
|
||
Цель: обеспечить стабильные 60 FPS и понятный интерфейс на целевом оборудовании.
|
||
|
||
- [x] Локальный baseline FPS, CPU-времени, памяти и draw calls зафиксирован
|
||
(`docs/operations/PERFORMANCE_BASELINE.md`); повтор на автомате вынесен в раздел 4.1.
|
||
- [x] Проверить статический fallback-фон и актуальный `space-pixel` в полном/облегчённом режимах:
|
||
decoration переключается `1 → 0 → 1`, сохраняются 60 FPS, draw calls снижаются с 26 до 24.
|
||
- [x] Перевести production-темы на доверенный runtime: разрешить theme-owned GDScript и стандартные
|
||
Godot-узлы без прежнего decoration budget, лимитов узлов/кадров/FPS/скорости или размера PCK;
|
||
сохранить строгую проверку namespace, manifest, экранов, биндингов и запрет подмены ресурсов host.
|
||
- [x] Отключать обработку всех скрытых экранов через `ProcessMode.Disabled` и включать её только
|
||
для текущего экрана; переход Home → Settings проверен в runtime.
|
||
- [x] Добавить явную прокрутку Settings перетаскиванием одним пальцем и зажатой ЛКМ;
|
||
обработчик использует deadzone 12 px, не перехватывает короткое нажатие и не зависит от того,
|
||
передал ли вложенный Control drag штатному ScrollContainer. Физическая приёмка остаётся в 4.1.
|
||
- [x] Проверить контраст normal/pressed/disabled/error/success во fallback и `space-pixel`;
|
||
CheckButton дополнительно получил тёмный текст и встроенные крупные ON/OFF-индикаторы.
|
||
- [x] Убрать зависимость от hover и мелкие интерактивные элементы: все девять экранов пройдены
|
||
runtime-аудитом, цифровые клавиатуры имеют равные крупные колонки, логика не использует hover.
|
||
- [x] Синхронизировать runtime-композицию с девятью preview-сценами `space-pixel`: перевести host на
|
||
нативную сетку 720×1280, перенести геометрию панелей и подключить безопасные именованные visual slots.
|
||
- [x] Перевести DLC-контракт на manifest v2 с полными визуальными сценами всех девяти экранов,
|
||
app-owned контроллерами, стабильными element binding-id и обратно совместимым v1 fallback.
|
||
- [x] Реорганизовать `ExchangerThemeDlcBuilder` в `themes/example` и `themes/<theme-id>`, добавить
|
||
валидатор имён/типов/биндингов и preview-overlay, показывающий binding-id каждого элемента.
|
||
- [x] Адаптировать host к самодостаточным production-сценам Builder: проверять metadata, собственный
|
||
Theme/фон/decoration, единый режим всех девяти экранов и отсутствие дублирующих глобальных слоёв;
|
||
при `ReducedEffects` скрывать и отключать обработку только `BackgroundDecoration`, не ограничивая
|
||
остальную доверенную runtime-логику темы.
|
||
- [x] Запретить host переоформлять самодостаточные сцены: Settings больше не назначает внешним
|
||
узлам `WhitePanel`/семантические variations, keypad-контроллеры не меняют их размеры и стили,
|
||
`ThemeManager.ApplyTo` оставляет Theme/геометрию полноэкранного view нетронутыми.
|
||
- [x] Убрать runtime-достраивание самодостаточных сцен: Home, карточные суммы и изменяемые списки
|
||
Settings работают с готовыми узлами и фиксированными слотами темы; host только подключает
|
||
события и передаёт функциональные значения, не добавляя и не удаляя Control-узлы.
|
||
- [x] Добавить в Settings theme-owned текстовую и цифровую клавиатуры: 57 новых биндингов,
|
||
состояния preview `OFF/TEXT/NUM`, неизменяемое визуальное дерево и общую C#-логику поля ввода;
|
||
каталог DLC расширен до 460 обязательных биндингов.
|
||
- [x] Добавить владельцу раздел «Сервис» с отдельным атомарным учётом остатка, ручным добавлением
|
||
через двойное подтверждение и списанием только фактически подтверждённых выдач.
|
||
- [x] Реализовать автоматический пересчёт прямыми командами `301/300` или `401/400`: считать
|
||
только `HOPPER1/2 PULSE=1`, не дублировать учёт по `INPUT`, завершать после 10 секунд тишины, позволять исправить результат
|
||
перед записью и безопасно останавливать мотор при отмене/закрытии.
|
||
- [x] Расширить self-contained theme-контракт сервисными и четырьмя состояниями индикатора запаса до 477 элементов; template
|
||
и `space-pixel` содержат готовые Service-панель и диалог, host меняет только значения и события.
|
||
- [x] Проверить деградацию при временной просадке FPS: таймауты используют монотонное реальное
|
||
время и завершаются корректно даже при единственном обновлении после скачка на 120 секунд.
|
||
|
||
Программные ограничения, профили эффектов и локальный baseline реализованы. Проверка критерия
|
||
60 FPS, панели и декодера промо на автомате ведётся централизованно в разделе 4.1.
|
||
|
||
### Этап 8. Тестирование и контроль качества — выполняется
|
||
|
||
Цель: сделать регрессии воспроизводимыми до установки на автомат.
|
||
|
||
- [x] Выбрать C# test runner: NUnit 4 для чистого доменного ядра и проверок файлов проекта.
|
||
- [x] Конфигурация, тарифы, валидация суммы, переходы `SessionFlow`, framing, parser и handshake покрыты unit-тестами.
|
||
- [x] Добавить интеграционные тесты `SessionFlow + IMachineController test double`; production `MockMachineController` дополнительно проверять в Godot runtime.
|
||
- [x] Добавить автоматическую проверку наличия/заголовков всех сцен, валидатор manifest DLC и runtime smoke-загрузку каждого экрана через Godot Editor.
|
||
- [x] Добавить тесты таймаутов с управляемым источником времени, без реального ожидания 120 секунд.
|
||
- [x] Проверить duplicate action, повторный `DISPENSE`, поздние события, disconnect/reconnect и неизвестные/malformed wire-ответы.
|
||
- [x] Проверить полные наличный и безналичный wire-сценарии от действия сессии до `ASK:*`,
|
||
выключения приёмников и выдачи; отдельно проверить пакет 515 жетонов как пять `30999` и один `30920`, подтверждённые соответствующим числом `HOPPER1 OK`.
|
||
- [x] Проверить отсутствие накопления подписок и стабильность многократных последовательных сессий.
|
||
- [x] Проверять C#-сборку и NUnit-набор после каждого завершённого блока.
|
||
- [x] Проверять Godot editor/game logs после изменений сцен и ресурсов.
|
||
- [x] Составить ручной чек-лист приёмки на физическом аппарате.
|
||
Физические проверки панели, SERVICE, дисплея и 24-часовой soak вынесены в раздел 4.1.
|
||
|
||
### Этап 9. Сборка, установка и обновление Windows-версии — выполняется
|
||
|
||
Цель: получить воспроизводимый устанавливаемый релиз.
|
||
|
||
- [x] Создать export preset Windows x64 и воспроизводимый staging-сборщик.
|
||
- [x] Проверить включение self-contained .NET runtime, `GodotSharp.dll` и `System.IO.Ports.dll`.
|
||
- [x] Явно исключить `references/`, тесты, документацию, инструменты и dev-only addons из
|
||
production-экспорта; в проверенной сборке нет test DLL и addon-файлов.
|
||
- [x] Проверить `res://` и `user://` коротким headless smoke-run экспортированного EXE (exit code 0).
|
||
- [x] Исправить one-EXE runtime: встроенные JSON/TOML материализуются через Godot `FileAccess`,
|
||
`config/*.toml` включается в PCK, а runtime-autoload GodotxToast не удаляется export-фильтром.
|
||
- [x] Проверить итоговый one-file release: каталог `artifacts/windows/test_q` содержит ровно один
|
||
`Exchanger.exe` размером 190229328 байт; packaged mock и COM3 smoke завершились без ошибок.
|
||
- [x] Определить расположение пользовательской конфигурации и логов для сервисного персонала.
|
||
- [x] Добавить номер версии сборки в UI сервисного режима и журналы.
|
||
- [ ] Подготовить сценарий установки, обновления и отката.
|
||
- [~] Добавить цифровую подпись: текущий EXE подписан самоподписанным сертификатом `CN=Robotail`
|
||
с timestamp DigiCert; остаётся определить production-модель доверия и установить доверенный корень
|
||
на целевые устройства либо использовать сертификат доверенного CA.
|
||
- [ ] Зафиксировать контрольную версию конфигурации контроллера и приложения.
|
||
|
||
Чистая установка и проверка релиза на целевом устройстве вынесены в раздел 4.1.
|
||
|
||
### Этап 9.1. Linux x86_64 для Debian 13 — выполняется
|
||
|
||
Цель: перенести desktop runtime на Debian без изменения UI, бизнес-логики и wire-протокола.
|
||
|
||
- [x] Добавить Godot export preset `Linux x86_64 (Debian 13)` с production-фильтрами Windows-пресета.
|
||
- [x] Встроить PCK и managed build outputs; актуальный self-contained ELF x86_64 имеет размер
|
||
155351952 байта, RID `linux-x64` и native `System.IO.Ports`.
|
||
- [x] Проверить заголовок артефакта: ELF64, AMD64/x86_64.
|
||
- [x] Разрешить в технической конфигурации `/dev/ttyUSB*`, `/dev/ttyACM*` и стабильные
|
||
`/dev/serial/by-id/*`, сохранив строгую валидацию Windows COM-портов.
|
||
- [x] Собрать отдельный Linux Debug ELF с расширенной безопасной диагностикой UART и handshake,
|
||
не перезаписывая release-артефакт.
|
||
- [ ] Подготовить udev/dialout и доступ сервисного пользователя к USB-UART.
|
||
- [ ] Подготовить Linux kiosk/autostart/watchdog и процедуру обновления/отката.
|
||
Физический запуск и приёмка на Debian 13 вынесены в раздел 4.1.
|
||
|
||
### Этап 10. Подготовка Android-порта после MVP
|
||
|
||
Цель: заменить платформенный транспорт без переработки UI и бизнес-логики.
|
||
|
||
- [ ] Выбрать и согласовать USB-Serial Android-плагин.
|
||
- [ ] Реализовать `UsbSerialHardware` за существующим `IHardwarePort`.
|
||
- [ ] Добавить запрос USB permission и обработку переподключения устройства.
|
||
- [ ] Проверить ограничения Android kiosk/lock task mode.
|
||
- [ ] Адаптировать пути конфигурации/логов и жизненный цикл pause/resume.
|
||
|
||
Физическая Android-приёмка целевого ARM64-устройства вынесена в раздел 4.1.
|
||
|
||
## 8. Сквозные требования к реализации
|
||
|
||
- Все новые скрипты по возможности пишутся на C#.
|
||
- Скрипты Godot Node/Resource объявляются как `partial class`.
|
||
- Комментарии в C# ведутся единообразно; для проекта выбран русский язык документации и пояснений сложной логики.
|
||
- Экраны не открывают порт, не парсят аппаратные строки и не управляют потоками.
|
||
- Ресурсы экранов и компонентов хранятся атомарно в соответствующих папках.
|
||
- Пути тем и экранов централизованы в реестрах/конфигурации.
|
||
- Сигналы и подписки освобождаются при завершении жизненного цикла узла.
|
||
- Таймауты и переходы состояний должны быть детерминированы и тестируемы.
|
||
- Новые внешние зависимости и NuGet-пакеты добавляются только при фактической необходимости; разрешение владельца на необходимые NuGet-пакеты получено, назначение и версия каждой зависимости фиксируются в документации.
|
||
- После существенных изменений обновляются этот ROADMAP, документация и `agentmemory`.
|
||
|
||
## 9. Основные риски и меры снижения
|
||
|
||
| Риск | Последствие | Мера снижения |
|
||
| --- | --- | --- |
|
||
| Неопределённый протокол контроллера | Переделка транспорта и сценария выдачи | Зафиксировать протокол и реальные примеры обмена до интеграции COM |
|
||
| Повтор `DISPENSE` после обрыва | Двойная выдача жетонов | Transaction ID/статус операции; запрет слепого retry |
|
||
| Поздний ответ от старой сессии | Выдача не тому сценарию | Идентификатор и поколение сессии, сброс входного контекста |
|
||
| UI обращается к Godot из COM-потока | Падения и гонки | Queue/CallDeferred, строгая граница транспорта |
|
||
| Слабый GPU | Просадки FPS и задержки тача | Compatibility, лимиты эффектов, fallback-фон, профилирование |
|
||
| Ожидание, что Godot заблокирует Alt+Tab | Выход пользователя из киоска | Настройка Windows Assigned Access/Shell Launcher |
|
||
| Несовместимый или повреждённый DLC темы | UI не может загрузить внешние ресурсы | Версионированный `.pck`, строгий manifest и программная fallback-тема |
|
||
| Неограниченные логи | Заполнение диска | Ротация, лимит размера и срока хранения |
|
||
| Android-зависимости проникают в UI | Дорогой порт | `IHardwarePort` и platform-specific composition |
|
||
| Неясный результат операции при аварии | Финансовая/операционная ошибка | Журнал транзакций, запрос статуса, безопасный сервисный сценарий |
|
||
| Текущий UI остаётся ландшафтным | Несоответствие реальному корпусу | Первым шагом Этапа 2 перейти на 1080×1920 и провести визуальную QA 720×1280 |
|
||
| Промо-ролик декодируется всегда | Лишняя нагрузка и просадки FPS | Останавливать плеер вне HomeScreen, ограничить формат/разрешение, иметь постер |
|
||
| Референсные цифры приняты за спецификацию | Неверные тарифы и бонусы | Хранить всё в конфигурации и запросить подтверждение владельца |
|
||
| Референсные ассеты попали в релиз без прав | Лицензионный риск | `references/` в `.gitignore`; исключить каталог в export preset и использовать только подтверждённые production-исходники |
|
||
|
||
## 10. Вопросы для владельца продукта
|
||
|
||
- [?] Подтвердить цену жетона: 10 рублей в референсе или другое production-значение.
|
||
- [?] Подтвердить таблицу бонусов, включая пример «100 + 50 в подарок».
|
||
- [?] Подтвердить карточные пресеты 10/50/100/150/200/300/400/500 рублей и предел другой суммы.
|
||
- [?] Есть ли выдача сдачи/возврат наличных и что происходит при отмене наличной операции?
|
||
- [?] Что именно запускает кнопка «Выдать» на домашнем и наличном экранах?
|
||
- [?] Кто является источником подтверждения оплаты и в каком формате приходит событие?
|
||
- [?] Может ли контроллер подтвердить фактический результат выдачи, включая частичную выдачу?
|
||
- [?] Поддерживает ли протокол идентификатор транзакции и запрос статуса последней операции?
|
||
- [?] Что должен делать автомат после подтверждённой оплаты и частичной/неуспешной выдачи?
|
||
- [?] Нужны ли возврат средств или обращение к оператору, и кто их выполняет?
|
||
- [?] Требуется ли локальное хранение истории операций и каков срок хранения?
|
||
- [?] Нужны ли удалённая диагностика, телеметрия или выгрузка логов?
|
||
- [x] Сервисный вход открывается по аппаратному `SERVICE`; опциональный единый PIN настраивается
|
||
в приложении при первом входе, матрица разрешений зафиксирована в `docs/operations/SETTINGS_ACCESS_MATRIX.md`.
|
||
- [?] Подтвердить финальные бренд, логотип, телефон, персонажа и права на референсные материалы.
|
||
- [?] Уточнить форматы промо-роликов, плейлист, звук и способ обновления контента.
|
||
- [?] Требуется ли локализация кроме русского языка?
|
||
- [?] Какой максимально допустимый простой и кто отвечает за автоматический перезапуск?
|
||
|
||
## 11. Ближайший рабочий пакет
|
||
|
||
Этапы 0–6 закрыты. Этапы 7, 8, 9 и 9.1 остаются в работе. Все проверки, требующие
|
||
физического автомата, собраны в разделе 4.1. Ближайший программный пакет:
|
||
|
||
1. Подготовить установку, обновление и откат Windows-релиза.
|
||
2. Определить production-модель доверия цифровой подписи.
|
||
3. Зафиксировать контрольную пару версий приложения и прошивки.
|
||
4. Подготовить udev/dialout и доступ сервисного пользователя к USB-UART в Debian 13.
|
||
5. Подготовить Linux kiosk/autostart/watchdog и процедуру обновления/отката.
|
||
|
||
Аппаратная очередь не дублируется здесь и полностью ведётся в разделе 4.1.
|
||
|
||
## 12. Журнал прогресса
|
||
|
||
### 2026-09-07 — Формат «К ВЫДАЧЕ» унифицирован для наличной и карточной оплаты
|
||
|
||
- Готовая и ручная карточная сумма показывают базовые жетоны слева и бонус отдельно справа:
|
||
`+ N ЖЕТОНОВ\nВ ПОДАРОК` либо `БЕЗ БОНУСА`; длинная объединённая строка больше не используется.
|
||
- Заголовок `БАЛАНС ЖЕТОНОВ` на экране наличной оплаты заменён на `К ВЫДАЧЕ`.
|
||
- Для отдельного бонуса CardAmount добавлен supplemental runtime binding с побиндинговым fallback;
|
||
immutable JSON-каталог сохраняет 481 ID и прежний SHA-256. Документация Theme Builder и обе
|
||
внешние темы синхронизированы отдельно.
|
||
- Debug-сборка проходит без предупреждений, 487/487 NUnit-тестов, встроенная тема и свежие PCK
|
||
проходят headless `PaymentRewardFormattingSmoke` на mock-оборудовании без обращения к COM/UART.
|
||
- Предыдущий установленный `space-pixel` также проходит compatibility-smoke: отсутствующий новый
|
||
CardAmount Bonus подключается как один встроенный fallback binding без отказа всего DLC.
|
||
- Builder contract/validator/preview/PCK smoke и визуальный QA `720×1280` прошли для обеих тем.
|
||
Новые пакеты: `example.pck` — 464876 байт, SHA-256
|
||
`02B8505FB7F0EF4F4E9C2905D97E8F8751A1DDE86DDF460AFC958CCE6AF51994`;
|
||
`space-pixel.pck` — 1436988 байт, SHA-256
|
||
`4D9AA00FE2096794331B348916D1766B47A45D9EDFEE60BEAB0E424ED4761A67`. Пользовательский
|
||
`active_theme.pck` не заменялся.
|
||
|
||
### 2026-09-07 — Учёт жетонов и ограничение покупки разделены
|
||
|
||
- В разделе «Сервис» появились два включённых по умолчанию переключателя: ведение точного учёта
|
||
и независимый запрет покупки сверх остатка с учётом бонуса. При отключении самого учёта
|
||
скрываются точные остатки, кнопки пересчёта/добавления и приблизительное число на Home;
|
||
сохранённый файл остаётся для последующего восстановления режима.
|
||
- Если учёт включён, а ограничение выключено, остатки остаются видимыми и продолжают списываться,
|
||
но не блокируют оплату. При включённом ограничении карточная сумма отклоняется до запроса
|
||
терминалу, наличная — до команды выдачи; пресеты и ручной ввод показывают тот же лимит.
|
||
- Выключенный режим не списывает сохранённый остаток; `DualHopperAuto` использует hopper 1 и
|
||
сохраняет последовательное разбиение по 99 без автоматического failover.
|
||
- Конфигурация повышена до версии 23. Новые элементы темы оформлены в Builder как supplemental
|
||
runtime bindings; immutable JSON-каталог остаётся на 481 ID с прежним SHA-256.
|
||
- Debug/Release проходят 483/483 NUnit-теста. Встроенная тема и предыдущие `example.pck`/
|
||
`space-pixel.pck` прошли отдельный runtime-smoke без реального COM/UART; проверка на автомате
|
||
добавлена в раздел 4.1. Старые PCK получают новый переключатель побиндинговым fallback.
|
||
- Свежие Builder-пакеты прошли contract/validator/preview/PCK smoke, визуальную проверку
|
||
`720×1280` и host runtime-smoke без fallback-подстановок: `example.pck` — `464268` байт,
|
||
SHA-256 `AC03334354D285E36A25BFB81D60880F829700F083AF5D728F10AB15C764B63D`;
|
||
`space-pixel.pck` — `1436492` байта, SHA-256
|
||
`166660916913E4766FC91437155BB8B78A405150B3A599CF401CD78A83DFB727`. Установленный
|
||
пользовательский `active_theme.pck` не заменялся.
|
||
|
||
### 2026-09-07 — Упрощён раздел общего текста в настройках
|
||
|
||
- Поле «Название» скрыто, «Короткий текст» переименован в «Свой текст», отдельный
|
||
branding-preview удалён из отображения раздела «Общие».
|
||
- `Branding.ApplicationName` сохранён в модели и JSON для обратной совместимости, но больше не
|
||
редактируется и не может скрытой ошибкой заблокировать сохранение формы владельца.
|
||
- Binding ID, пути, типы и fallback не менялись: legacy-узлы остаются скрытыми контрактными
|
||
слотами, поэтому версия и SHA-256 binding-каталога не изменяются.
|
||
- Встроенная сцена, `example` и `space-pixel` синхронизированы. Debug/Release unit-наборы прошли
|
||
по `460/460`; host runtime-smoke прошёл для встроенной темы и нового `space-pixel.pck`, а обе
|
||
DLC-темы прошли Builder contract/validator/preview/PCK smoke и визуальную проверку `720×1280`.
|
||
- Новые пакеты: `example.pck` — `463324` байта, SHA-256
|
||
`25E90059652EC26BD1779F7D6761F1CD1248A458F8C93F8E242596D3988FDE9F`;
|
||
`space-pixel.pck` — `1435548` байт, SHA-256
|
||
`E5941A4E7437BA996945E85551714F8B6E11F77A64808C68B29A8B88F66629AB`. Активная
|
||
пользовательская тема автоматически не заменялась.
|
||
|
||
### 2026-09-04 — Сниженная высота питомца оставлена только для sitting
|
||
|
||
- Theme-owned компонент `space-pixel` сохраняет локальную поправку `(0,0)` для `sitting` и
|
||
применяет `(0,-24)` ко всем другим анимациям. Root, navigation, hit box и
|
||
`GROUND_OFFSET=(64,96)` не меняются.
|
||
- Builder verifier подтвердил переход `(0,0) → (0,-24) → (0,0)` и неподвижность root; contract,
|
||
validator, preview smoke, build и PCK verifier прошли. Preview `720×1280` учитывает union-envelope
|
||
состояний и подтвердил Nearest без clipping/пересечения с заголовком.
|
||
- Полный host `InteractivePetBehaviorSmoke` с явным новым PCK и mock-оборудованием прошёл:
|
||
drag/drop, поверхности и production-loader сохранили прежнее поведение.
|
||
- Новый PCK: 1435452 байта, SHA-256
|
||
`390C9E3F83B026304F0E75E5A6AAD4458684BBCDFC847FFB542B6CFB1AC278E9`; активная пользовательская
|
||
тема автоматически не заменялась.
|
||
|
||
### 2026-09-04 — Питомец space-pixel увеличен на 30%
|
||
|
||
- Theme-owned `RUNTIME_SCALE` изменён с `0.65` на `0.845`; host-код и сцены Exchanger не
|
||
переделывались. Точка лап и hit-area масштабируются существующей общей формулой.
|
||
- Builder contract/test/validator/preview smoke/build и PCK verifier прошли; preview `720×1280`
|
||
подтвердил Nearest без clipping. Новый PCK: 1435004 байта, SHA-256
|
||
`60E41BA7C1043348AB7C3FEED72C352AE3FBFA58BDACB00F4036F1CD0EB56088`.
|
||
- Production-loader Exchanger и полный `InteractivePetBehaviorSmoke` с явным новым PCK прошли на
|
||
mock-оборудовании: drag/drop, поверхности, смена сцен и четыре танца сохранены.
|
||
|
||
### 2026-09-04 — Физическая сервисная кнопка сохранена поверх клавиатуры
|
||
|
||
- Блокировка «Сохранить и выйти» ограничена её экранным обработчиком и GUI hit-test футера.
|
||
Аппаратный `ServiceRequested` по-прежнему напрямую вызывает `TryFinishEditing()`.
|
||
- Повторное физическое нажатие при открытой клавиатуре закрывает её, завершает сохранение и
|
||
возвращает Home по прежнему маршруту; незавершённые сервисные операции всё ещё блокируют выход.
|
||
- Runtime-smoke воспроизводит физическое нажатие через общий debug-action при открытой клавиатуре
|
||
на встроенной и установленной DLC-темах. Debug/Release без предупреждений, NUnit — 459/459.
|
||
|
||
### 2026-09-04 — Футер больше не блокирует нижний ряд экранной клавиатуры
|
||
|
||
- Установлено, что `Button.Disabled` не исключает кнопку и родительскую панель из hit-test, поэтому
|
||
перекрывающая геометрия фиксированного футера перехватывала физические нажатия нижнего ряда.
|
||
- Пока клавиатура открыта, футер и «Сохранить и выйти» используют `MouseFilter.Ignore`; после её
|
||
закрытия восстанавливаются исходные фильтры, а обработчик выхода остаётся защищён отдельно.
|
||
- Runtime-smoke проверяет реальный GUI hit-test нижних клавиш обеих панелей и координатный клик
|
||
текстовой клавиши. Встроенная и установленная DLC-темы прошли; Debug/Release без предупреждений,
|
||
NUnit — 458/458.
|
||
|
||
### 2026-09-04 — Ввод экранной клавиатуры применяется без задержки
|
||
|
||
- Исправлена рассинхронизация, при которой вставленный экранной клавиатурой текст был виден в
|
||
`LineEdit`, но не попадал в рабочую копию Settings до первого удаления.
|
||
- Каждая операция вставки, удаления, очистки и замены выделения теперь формирует ровно одно
|
||
уведомление `TextChanged`; числовое поле дополнительно немедленно применяет значение `SpinBox`.
|
||
- Runtime-smoke контролирует событие после первой буквы и после каждой введённой цифры на
|
||
встроенной и установленной DLC-темах. Debug/Release: 0 предупреждений, NUnit — 458/458.
|
||
|
||
### 2026-09-04 — Выход из Settings заблокирован поверх экранной клавиатуры
|
||
|
||
- `OnScreenKeyboardController` сообщает об открытии и закрытии любой своей панели; фиксированная
|
||
кнопка «Сохранить и выйти» отключается синхронно с этим состоянием.
|
||
- Обработчик выхода отдельно проверяет фактическую видимость клавиатуры и игнорирует программное
|
||
нажатие, даже если состояние `Button.Disabled` было обойдено.
|
||
- Runtime-smoke проверяет текстовую, обычную цифровую и сервисную цифровую клавиатуры на встроенной
|
||
и установленной DLC-темах. Debug/Release собраны без предупреждений, NUnit — 458/458.
|
||
|
||
### 2026-09-04 — Переподключение платы переведено на технический интервал 30 секунд
|
||
|
||
- Подтверждено, что авторитетный параметр уже находился в
|
||
`[controller].reconnect_interval_seconds`; шаблон и активная локальная конфигурация изменены
|
||
с 5 на 30 секунд, остальные монтажные параметры не затронуты.
|
||
- При отсутствии платы после первой немедленной попытки порт повторно открывается каждые 30 секунд.
|
||
После обрыва работавшего транспорта отсчёт начинается заново от момента обнаружения обрыва,
|
||
поэтому устаревший стартовый дедлайн больше не вызывает немедленный reconnect.
|
||
- Планировщик повторно ставит дедлайн после каждой неудачной попытки; оплату и выдачу не повторяет.
|
||
Оба сценария покрыты детерминированными unit-тестами без COM/UART.
|
||
- Debug/Release собраны без предупреждений, NUnit — 458/458; headless mock подтвердил фактическую
|
||
загрузку `reconnectSeconds=30` из текущего пользовательского TOML.
|
||
|
||
### 2026-09-04 — Ручной ввод суммы получил однозначное удаление цифры
|
||
|
||
- Левая кнопка нижнего ряда CardCustomAmount переименована из «ОТМЕНА»/«СБРОС» в «СТЕРЕТЬ» и
|
||
удаляет только последнюю введённую цифру; после удаления последней цифры экран показывает `0`.
|
||
- Legacy binding `clear_button` сохранён ради совместимости, а отдельный обязательный
|
||
`backspace_button` скрыт, чтобы в интерфейсе не было двух одинаковых действий.
|
||
- Runtime-smoke подтвердил последовательность `12 → 1 → 0` на старом установленном PCK;
|
||
Debug/Release собраны без предупреждений, NUnit — 455/455.
|
||
- Обе темы прошли Builder validator/preview/build/PCK verify. В `space-pixel` надпись полностью
|
||
помещается при 720×1280; новый PCK прошёл production-loader приложения на mock-оборудовании.
|
||
|
||
### 2026-09-04 — В Settings добавлен фиксированный футер сохранения
|
||
|
||
- Кнопка «Сохранить и выйти» вынесена из прокручиваемого содержимого в закреплённый нижний футер;
|
||
drag по футеру не прокручивает список, а нижний зазор позволяет показать последние поля над ним.
|
||
- Кнопка завершает ожидающее атомарное автосохранение через общий `TryFinishEditing()` и возвращает
|
||
Home только после успеха; физическая сервисная кнопка сохраняет прежнее поведение.
|
||
- Пока открыта текстовая или цифровая экранная клавиатура, кнопка отключена; обработчик также
|
||
отклоняет программное нажатие, чтобы клавиатура не могла случайно инициировать выход.
|
||
- Для новых DLC добавлены supplemental bindings футера, кнопки и нижнего зазора. Старые PCK
|
||
получают только эти отсутствующие элементы из встроенной темы-заглушки.
|
||
- Встроенная тема, ранее установленный PCK и новый полный `space-pixel.pck` прошли runtime-smoke
|
||
на mock-оборудовании; Debug/Release собраны без предупреждений, NUnit — 454/454.
|
||
|
||
### 2026-09-04 — Добавлена побиндинговая обратная совместимость DLC-тем
|
||
|
||
- Отсутствующий обязательный binding больше не отключает весь DLC: runtime сначала принимает
|
||
совместимый узел старой темы по каноническому `FallbackPath`, а при его отсутствии переносит
|
||
готовый элемент встроенной темы-заглушки вместе с её оформлением.
|
||
- Если точечно собрать экран невозможно, встроенным становится только этот экран; остальные
|
||
корректные сцены внешней темы сохраняются. Неизвестные ID, дубли, неверные типы, root и
|
||
повреждённый контракт по-прежнему отклоняются.
|
||
- Реальный ранее установленный `space-pixel.pck` принят без пересборки: Home восстановил один,
|
||
Settings — четыре новых биндинга. Запуск прошёл на mock без обращения к COM/UART.
|
||
- Release-сборка прошла без ошибок и предупреждений, NUnit — 452/452.
|
||
|
||
### 2026-09-04 — В сервис добавлено скрытие элементов домашнего экрана
|
||
|
||
- В новой панели «Скрытие элементов меню» независимо управляются учётный остаток жетонов,
|
||
theme-owned персонаж справа и весь промо-блок; старые настройки мигрируют с показом всех трёх.
|
||
- Изменения автосохраняются и применяются к уже созданному Home без перезапуска приложения.
|
||
- Host меняет только видимость готовых узлов fallback/DLC. Скрытые остаток и промо исключаются
|
||
вместе с маршрутами питомца, поэтому он не садится на невидимые поверхности.
|
||
- Обе темы прошли Builder validator/preview-smoke и PCK verifier; новый `space-pixel.pck`
|
||
дополнительно загружен production loader приложения на mock-оборудовании без ошибок.
|
||
- Release-сборка прошла без ошибок и предупреждений, NUnit — 448/448.
|
||
|
||
### 2026-09-03 — Debug-сборка получила расширенную диагностику подключения платы
|
||
|
||
- Добавлен компилируемый только в Debug уровень `[DEBUG]`; Release не содержит вызовов и строк
|
||
расширенной диагностики.
|
||
- Журнал фиксирует платформу и параметры UART, открытие устройства, reset DTR/RTS, запуск и
|
||
завершение worker, безопасную причину транспортной ошибки, reconnect и переходы handshake.
|
||
- Сырые кадры, AES challenge/response, ключи и платёжные payload не выводятся; показываются только
|
||
тип этапа, состояние, длина и безопасные счётчики.
|
||
- Runtime summary в разделе «Диагностика» Debug-сборки показывает transport/auth state.
|
||
- Debug и Release собраны без ошибок и предупреждений, обе конфигурации прошли NUnit 444/444;
|
||
Debug headless-smoke на mock подтвердил фактическую запись `[DEBUG]` в журнал.
|
||
- Собран отдельный Linux Debug ELF: 155630576 байт, SHA-256
|
||
`A1FB92126D6ED753DF968180075D7409678E5F0A7C454CB6D2B954DC024682B6`.
|
||
|
||
### 2026-09-03 — Последовательный порт адаптирован для Windows и Linux
|
||
|
||
- Общая валидация выбирает правила по фактической ОС процесса: Windows принимает только
|
||
`COM1`…`COM256`, Linux — `/dev/ttyUSB*`, `/dev/ttyACM*` и `/dev/serial/by-id/*`.
|
||
- При `use_mock = true` имя порта не проверяется и безопасный общий шаблон с `COM3` остаётся
|
||
пригодным для mock-запуска Linux-сборки.
|
||
- `SerialPortHardware` остался единым desktop-транспортом на `System.IO.Ports`; сообщения
|
||
транспорта больше не привязаны к термину COM.
|
||
- Unit-тесты отдельно покрывают обе платформенные ветки и недопустимые Linux-пути.
|
||
- Release-сборка прошла без ошибок и предупреждений, NUnit — 444/444.
|
||
- Linux release повторно экспортирован одним ELF64 AMD64-файлом размером 155351952 байта,
|
||
SHA-256 `7312E79514842EB5EA3193604F71CAAE412654957640032B62D66E202802284C`.
|
||
|
||
### 2026-09-02 — Экранная клавиатура подключена к ручному пополнению хоппера
|
||
|
||
- При открытии диалога «Добавить в хоппер» поле количества после появления модального слоя
|
||
автоматически получает фокус, выделяет текущее значение и показывает цифровую клавиатуру.
|
||
- Контроллер клавиатуры учитывает, что сервисное поле находится вне основного ScrollContainer,
|
||
и не пытается прокручивать скрытый список настроек.
|
||
- Сцены темы не менялись: host использует существующие binding-id поля, диалога и готовой
|
||
theme-owned цифровой клавиатуры.
|
||
- Runtime-smoke открывает сервисный диалог, проверяет видимость клавиатуры и фокус поля, затем
|
||
закрывает оба слоя. Fallback и `space-pixel.pck` прошли с mock-оборудованием.
|
||
- Сборка прошла без ошибок и предупреждений, NUnit — 410/410; 18 hardware-тестов пропущены.
|
||
|
||
### 2026-09-02 — Выдача двух хопперов переведена на строгий приоритет первого
|
||
|
||
- Для запроса `N` план выдачи теперь всегда равен `H1=min(N, остаток H1)`, `H2=N-H1`:
|
||
второй хоппер используется только для недостающей разницы после первого.
|
||
- Удалён прежний выбор стартового хоппера по датчикам уровня и сравнительному остатку, из-за
|
||
которого выдача могла начинаться со второго хоппера.
|
||
- При недостаточном суммарном учётном остатке ни одна команда выдачи не отправляется. Аппаратная
|
||
ошибка во время уже начатой выдачи по-прежнему не вызывает автоматический retry/failover.
|
||
- Mock приведён к тому же правилу и больше не создаёт фиктивное подтверждение сверх остатка.
|
||
- Интеграционный тест подтверждает последовательность `H1=6`, `H2=20`, запрос `10`:
|
||
сначала `3096`, после шести `HOPPER1 OK` — `4094`, итоговый раздельный учёт `(6, 4)`.
|
||
- Для обоих хопперов отдельно закреплена последовательная пакетная выдача 205 жетонов:
|
||
`30999 → 30999 → 3097` либо `40999 → 40999 → 4097`. `APPEND=N` не запускает следующий пакет;
|
||
он отправляется только после всех 99 `OK`. Смешанный план 180 при остатках `(150, 50)` даёт
|
||
`30999 → 30951 → 40930`, не запуская второй хоппер раньше завершения первого.
|
||
- Сборка прошла без ошибок и предупреждений, NUnit — 409/409 (18 hardware-тестов пропущены),
|
||
runtime-smoke с `space-pixel.pck` и mock-оборудованием прошёл без binding-ошибок.
|
||
|
||
### 2026-09-02 — Исправлено склонение слова «жетон» в пользовательском потоке
|
||
|
||
- Добавлен единый форматтер русских окончаний: `1 жетон`, `2–4 жетона`, `5–20 жетонов`, включая
|
||
исключение `11–14` и повторяющиеся разряды (`21`, `22`, `111`, `124`).
|
||
- Исправлены тексты выбора суммы, наличной оплаты, произвольной карточной суммы, ожидания терминала,
|
||
прогресса выдачи и финального сообщения, в том числе количество подарочных жетонов.
|
||
- Внешние сцены не изменялись: host обновляет только динамический текст существующих Label.
|
||
- Сборка прошла без ошибок и предупреждений, NUnit — 395/395 (18 hardware-тестов пропущены),
|
||
runtime-smoke с `space-pixel.pck` и mock-оборудованием прошёл без binding-ошибок.
|
||
|
||
### 2026-09-02 — Добавлен раздельный остаток жетонов по хопперам
|
||
|
||
- Settings показывает точный общий остаток и отдельные значения для хоппера 1 и хоппера 2;
|
||
данные берутся из сохранённого `TokenInventorySnapshot` и обновляются после подтверждённой
|
||
выдачи, пересчёта и ручного добавления.
|
||
- Host использует существующие theme-owned `Hopper1Label`/`Hopper2Label`; геометрия и оформление
|
||
внешней темы приложением не меняются.
|
||
- В контракт добавлены два Label-binding: всего 481 биндинг, SHA-256 каталога —
|
||
`DDDC89118BEEA78A820561464001656136ED42AC1AD5710FB109D25ADAF44143`.
|
||
- Сборка прошла без предупреждений и ошибок, NUnit — 376/376 (18 hardware-тестов пропущены).
|
||
`example` и `space-pixel` прошли contract/validator/preview smoke; fallback и собранный
|
||
`space-pixel.pck` прошли host runtime-smoke с mock-оборудованием. Сервисная панель `space-pixel`
|
||
визуально проверена в Theme Builder при `720×1280` без наложений и обрезания текста.
|
||
|
||
### 2026-09-02 — Windows Debug и Release разведены по отдельным пресетам
|
||
|
||
- Неоднозначный `Windows x64` заменён на `Windows x64 Release` и `Windows x64 Debug` с разными
|
||
output-путями, custom features, console wrapper и настройками debug symbols/C# sources.
|
||
- `tools/export-windows.ps1` принимает `-Configuration Release|Debug`, по умолчанию собирает
|
||
Release и передаёт Godot соответствующий `--export-release` либо `--export-debug`.
|
||
- Release создаёт `Exchanger.exe`, Debug — `Exchanger.Debug.exe`; каталоги содержат конфигурацию
|
||
и timestamp, поэтому варианты не перезаписывают друг друга.
|
||
- Оба режима фактически экспортированы официальным Godot 4.7.1 Mono и прошли packaged headless
|
||
smoke с mock-оборудованием: Debug содержит основной EXE и console wrapper, Release — один EXE.
|
||
Контрольные файлы не подписаны; Authenticode остаётся отдельным release-шагом.
|
||
|
||
### 2026-09-02 — Разделены таймауты наличной и безналичной оплаты
|
||
|
||
- Конфигурация v20 получила независимые `CashPaymentSeconds` и `CashlessPaymentSeconds`;
|
||
прежний `PaymentSeconds` мигрируется без потери значения, а наличный таймаут наследует прежний
|
||
`SessionIdleSeconds`.
|
||
- `SessionFlow` использует наличный таймаут на экране приёма денег и продлевает его при активности
|
||
или изменении баланса; ожидание карточного терминала использует отдельный безналичный таймаут.
|
||
- В Settings добавлены два самостоятельных поля с отдельной валидацией. Fallback, `example` и
|
||
`space-pixel` содержат готовые theme-owned элементы; C# только подключает их по биндингам.
|
||
- Host-контракт синхронизирован до 479 биндингов и одновременно закрыл прежний блокер
|
||
`card_terminal.content.top_bar.back_button`; SHA-256 каталога —
|
||
`B33F34A50837560AF8C3589B3E0AA19BB03B7B534AF6A27001C4FA19DE4155BF`.
|
||
- Release/Debug собираются без предупреждений, NUnit — 372/372. Обе темы прошли contract,
|
||
validator и preview smoke; установленный `space-pixel` прошёл Exchanger headless smoke с
|
||
mock-оборудованием без binding-ошибок и без доступа к реальному COM.
|
||
|
||
### 2026-09-02 — CardTerminal «Назад» возвращает к выбору суммы
|
||
|
||
- Host подключает существующий binding верхней кнопки `BackButton`, не изменяя сцену внешней темы.
|
||
- Перед возвратом из `CardTerminalWait` вызывается `CancelActiveOperation()`, поэтому Вендист
|
||
выключается и предыдущий результат оплаты игнорируется вне состояния ожидания терминала.
|
||
- Красная кнопка «ОТМЕНА» сохраняет прежнее поведение: завершает карточную сессию и возвращает Home.
|
||
- `kiosk_back` теперь соответствует верхней кнопке «Назад»; добавлен регрессионный тест перехода.
|
||
- Release-сборка прошла с 0 ошибок/0 предупреждений, NUnit — 367/367; headless Godot smoke
|
||
с `--mock-hardware=true` загрузил актуальную `space-pixel` без исключений и без обращения к COM3.
|
||
|
||
### 2026-09-01 — Этапы 4 и 5 закрыты, физическая приёмка централизована
|
||
|
||
- Этап 4 закрыт по готовности COM-транспорта, worker-потока, reconnect и at-most-once;
|
||
оставшиеся действия с реальным контроллером перенесены в раздел 4.1.
|
||
- Этап 5 закрыт по фактической безопасной политике UI: кнопки повторной выдачи нет, ошибки ведут
|
||
домой, к другому способу оплаты либо в поддержку, а `UnknownAfterRestart` не запускает retry.
|
||
- Все актуальные проверки панели, производительности, платежей, обоих хопперов, пересчёта,
|
||
USB-reconnect, soak и целевых платформ собраны в «4.1. Проверки на автомате» и удалены из этапов.
|
||
- Ближайший рабочий пакет теперь содержит только программные и эксплуатационные задачи.
|
||
|
||
### 2026-09-01 — Дорожная карта синхронизирована с one-file export и текущим контроллером
|
||
|
||
- Удалено устаревшее утверждение, что плата отключена: последний packaged Windows smoke успешно
|
||
авторизовал COM3, однако новые аппаратные действия по-прежнему требуют отдельного разрешения.
|
||
- Зафиксированы one-file Windows release, исправления embedded PCK/TOML/autoload и текущее состояние подписи.
|
||
- Добавлен отдельный этап Linux x86_64 для Debian 13: export готов, целевой runtime, serial-path,
|
||
udev/dialout, kiosk и физическая приёмка остаются открытыми.
|
||
- Ближайший рабочий пакет синхронизирован с фактическими задачами Этапов 5, 7, 8, 9 и 9.1.
|
||
|
||
### 2026-08-31 — Включён полноценный учёт и выдача двух хопперов
|
||
|
||
- Временный запрет второго хоппера отменён: целевой профиль `DualHopperAuto` использует `30X` и `40X`, подтверждения принимаются только от активного канала.
|
||
- Точный остаток хранится отдельно для каждого хоппера; ручное добавление и автоматический пересчёт доступны раздельно, а подтверждённая выдача списывается с фактически сработавшего канала.
|
||
- Покупателю на главном экране справа от полоски запаса показывается только приблизительный общий остаток:
|
||
`<100`, шаг 100 до 1000 включительно и шаг 500 выше 1000.
|
||
- Владельцу в разделе «Сервис» показывается точный учётный остаток с человекочитаемым склонением
|
||
слова «жетон»; приблизительные диапазоны из интерфейса настроек удалены.
|
||
- Главная индикация получила четыре состояния: пустая полоска «нет жетонов», красная 25% «мало», красная 50% «достаточно», зелёная 100% «много». Состояние вычисляется совместно по общему учёту и active-low датчикам `IN1/IN4`.
|
||
- Внешний DLC-контракт дополнен предсозданными визуалами четырёх состояний; приложение только переключает их видимость и не перестраивает сцену темы.
|
||
- Конфигурация поднята до версии 18 и мигрирует старые одноканальные профили в `DualHopperAuto`.
|
||
- Обычный набор расширен тестами двух остатков, округления, правил индикации, `40X`, второго пересчёта и двух датчиков. Аппаратные тесты и COM не запускались.
|
||
|
||
### 2026-08-31 — Интегрирован протокол новой прошивки первого хоппера
|
||
|
||
> Историческая запись промежуточного состояния. Ограничение одного хоппера отменено записью выше.
|
||
|
||
- На тот момент разводка: `IN8` — сервисная кнопка; `IN1/IN24` — запас и физическая выдача первого хоппера; `IN4` — запас второго. Вход выдачи второго хоппера пока неизвестен.
|
||
- До исправления прошивки второго канала runtime работает только в `Hopper1Auto`, отправляет только `30X` и отклоняет `Hopper2Auto` без записи `40X` в COM.
|
||
- Выдача использует `309N`, типизированные ответы `HOPPER1 APPEND=N`, `HOPPER1 OK`, `HOPPER1 ERROR`, `HOPPER1 CREDIT=N` и запрос остатка `3090`. Ошибка не защёлкивает хоппер до перезапуска и не вызывает автоматический повтор команды.
|
||
- Подтверждено ограничение одной команды: N = 1–99; `309100` возвращает `ERROR:30:invalid count (0-99)`. Runtime использует общую константу 99 и разбивает крупную выдачу на последовательные пакеты.
|
||
- Для будущего второго канала сохранён симметричный контракт `409100 → ERROR:40:invalid count (0-99)`; симулятор и тесты его знают, но runtime продолжает запрещать любые `40X`.
|
||
- Автоматический пересчёт запускается `301`, считает только строки `HOPPER1 PULSE=1` и останавливает мотор командой `300` после 10 секунд тишины.
|
||
- Вендиста поддерживается командой `161` в idle/card-состоянии с интервалом 120 секунд, то есть заведомо чаще требуемых прошивкой 180 секунд; на время наличной оплаты и выдачи используется `162`.
|
||
- Конфигурация поднята до версии 17 и мигрирует прежний целевой `Hopper2Auto` на `Hopper1Auto`; на том этапе второй датчик выдачи сохранялся как `-1`, позднее владелец подтвердил его привязку к `IN25`.
|
||
- Код, симулятор, unit-тесты, аппаратный runner и документация синхронизированы. После фиксации предела пакета обычный набор прошёл 323/323, решение собрано без предупреждений и ошибок. Аппаратные тесты и любые обращения к COM не выполнялись, потому что плата отключена.
|
||
|
||
### 2026-08-31 — Авторизация синхронизирована с актуальным plate_config_app
|
||
|
||
- После открытия COM-порта выполняется аппаратный reset: очистка буферов, DTR=false, RTS=true на 100 мс, затем RTS=false.
|
||
- Контроллер должен прислать точный startup-баннер `periphery controller ver_<version>`; после паузы 2000 мс выполняется `07 → CHAL → 08 → RAES`.
|
||
- Корректный `RAES`, `AUTH_PC_OK` и `AUTH_CNT_OK` являются промежуточными стадиями. Рабочие команды разрешаются только после точного кадра `READY!`.
|
||
- [x] Синхронизировать таймауты с актуальным `plate_config_app`: startup-баннер — 20 с, обязательная пауза — 2000 мс, `CHAL` и `RAES` — по 2000 мс, финальный `READY!` — 5 с. Конфигурация v19 переводит только старое стандартное значение 5000 мс на 2000 мс и сохраняет явно заданные пользовательские значения.
|
||
- Входящие `CHAL` и `RAES` разбираются совместимо с `plate_config_app`: поддерживаются слитный HEX-payload, пробел или двоеточие после префикса.
|
||
- Симулятор, unit/integration-тесты и безопасный аппаратный probe приведены к тому же контракту.
|
||
- Решение собирается без предупреждений; программный NUnit-набор проходит 310/310.
|
||
- Физический auth-probe на COM3 прошёл 1/1; `Safe` прошёл 8/9. Новый handshake, `91`/`IN1`,
|
||
`140`, `150`, `409?`, `99` и три reconnect-цикла подтверждены.
|
||
- Единственный открытый аппаратный результат — отсутствие ответа на `160` в двух независимых
|
||
запусках. Это не блокирует авторизацию, но требует проверки подключения/режима Вендиста.
|
||
|
||
### 2026-08-28 — Исправлена пакетная выдача второго хоппера
|
||
|
||
- Реальный обмен показал формат прошивки: `40910` ставит в очередь 10 жетонов, после чего плата отвечает `HOPPER2 queued 10 prizes` и десять раз `HOPPER2 OK`.
|
||
- `Hopper2Auto` переведён с ошибочной схемы «одна `409` → опрос `IN25`» на `409N → N × HOPPER2 OK`; короткие импульсы `IN25` больше не могут быть пропущены периодическим опросом и не дублируют подтверждения платы.
|
||
- Пакеты ограничены 99 жетонами; выдача 515 жетонов детерминированно разбивается на пять `40999` и один `40920`, следующий пакет отправляется только после завершения предыдущего.
|
||
- Parser типизирует `HOPPER2 queued N prizes`, симулятор воспроизводит реальный порядок кадров. Обычный набор прошёл 289/289; 17 аппаратных тестов безопасно пропущены без явного разрешения на запуск мотора.
|
||
|
||
### 2026-08-27 — Целевой хоппер исправлен на второй
|
||
|
||
- Владелец уточнил фактическую разводку: `IN1` и `IN25` принадлежат второму хопперу, а `IN2/IN24` — будущему первому.
|
||
- Добавлен режим `Hopper2Auto` и типизированные `HOPPER2 OK/ERROR`. Первоначальная трактовка одиночной `409` и подсчёта `IN25` позднее заменена подтверждённым пакетным форматом `409N`; актуальное поведение описано в записи от 2026-08-28.
|
||
- Status-запрос аппаратного runner переведён с исторического `309?` на `409?` для второго хоппера; формат ответа ещё нужно подтвердить безопасным прогоном COM3.
|
||
- Конфигурация версии 15 автоматически мигрирует прежний `Hopper1Auto` на `Hopper2Auto`, если он использовал целевую пару `IN1/IN25`; локальный COM3-профиль уже обновлён.
|
||
- Регрессии того блока покрывали прежнюю интерпретацию `409`/`IN25`; после проверки прошивки они заменены тестами `409N`, `HOPPER2 queued` и `HOPPER2 OK`. Актуальный набор и результаты указаны выше.
|
||
|
||
### 2026-08-27 — Платёжные действия повторно сверены с протоколом платы
|
||
|
||
- Для наличной ветки зафиксирована и протестирована последовательность `162 → 141 → 151`,
|
||
обязательные `ASK:COINS/BILL`, а перед выдачей/при отмене — `142 → 152`.
|
||
- Для безналичной ветки выбор способа не включает терминал до подтверждения суммы; подтверждение
|
||
отправляет `142 → 152 → 161 → 163<amount>`, `MONEY:CASHLESS` сначала получает обязательный
|
||
`ASK:CASHLESS`, затем терминал выключается `162`. Несовпадающая сумма выдачу не запускает.
|
||
- Платёжные каналы стали явно взаимоисключающими, а повторные Cancel/End не добавляют одинаковые
|
||
команды в последовательную очередь платы.
|
||
- Последующее уточнение разводки показало, что `IN1/IN25` относятся ко второму хопперу; поэтому прежняя реализация `309`/`Hopper1Auto` для целевого автомата была заменена на `409`/`Hopper2Auto` в следующем блоке работ.
|
||
- Полные cash/card сценарии и пакет 515 жетонов прошли через настоящий parser, authenticator,
|
||
`PeripheryMachineController`, `SessionFlow` и wire-симулятор. Набор: 283/283 unit/integration;
|
||
17 аппаратных тестов по-прежнему требуют явных флагов, реальные деньги и мотор не запускались.
|
||
- После изменений безопасная аппаратная группа повторно запущена на CP210x/COM3: 9/9, включая
|
||
handshake, `INPUT`, активный `IN1 → Low`, `140`, `150`, `160`, `309?`, `99` и три reconnect-цикла.
|
||
Отчёт: `artifacts/board-tests/board-safe-20260827-161454.trx`. Реальные деньги и мотор не
|
||
запускались; один краткий `UnauthorizedAccessException` при переоткрытии восстановлен retry.
|
||
|
||
### 2026-08-27 — Настраиваемый короткий текст в речевом bubble
|
||
|
||
- Добавлена отдельная настройка `Branding.ShortText` с понятным названием «Короткий текст», лимитом 48 символов, автосохранением, валидацией и preview.
|
||
- `HomeScreen` подставляет значение только в авторский Label речевого bubble по биндингу `home.scroll.content.speech_text`; старый `Subtitle` и его биндинги не возвращаются.
|
||
- Конфигурация поднята до версии 14: существующий профиль получает безопасный текст по умолчанию и атомарно сохраняется с резервной копией.
|
||
- Theme Builder добавил новый HOME-биндинг и три SETTINGS-биндинга `short_text`; актуальный контракт содержит 460 ID и имеет SHA-256 `1FA54C9B9A0A7DFF71A5FAD38A5E9036E6C2D8F3D0BD298F003524A3E752FA10`.
|
||
|
||
### 2026-08-26 — Исправлена полярность датчика запаса IN1
|
||
|
||
- Подтверждено по протоколу платы и утилите тестирования входов: цифровые входы используют pull-up, поэтому активный сигнал на `IN1` сбрасывает бит маски.
|
||
- Production-сопоставление исправлено на `IN1 bit 0 → Low` и `IN1 bit 1 → Enough`; обновлены defaults, аппаратные runner/tests и документация.
|
||
- Конфигурация поднята до версии 13: профиль с подтверждённым входом `IN1` автоматически мигрирует с ошибочной active-high полярности на active-low, а прежний JSON сохраняется в `.bak`.
|
||
- На подключённой плате COM3 безопасная группа прошла 9/9; текущий активный IN1 был интерпретирован как `Low`. Модульные тесты прошли 262/262.
|
||
|
||
### 2026-08-26 — Короткое описание полностью удалено
|
||
|
||
- Удалено поле короткого описания из модели `BrandingSettings`, нормализации, валидации, настроек владельца, preview и HOME.
|
||
- Конфигурация поднята до версии 12: старое JSON-поле `Subtitle` игнорируется и удаляется при автоматической миграции с сохранением предыдущего файла в `.bak`.
|
||
- Из контракта DLC и обеих тем были удалены четыре HOME/SETTINGS-биндинга короткого описания; на этом шаге контракт содержал 456 обязательных биндингов и имел SHA-256 `E77F754AB2C3913928B7DF18DD3D8F3205000D1531C414CC5B621E21CDF03ECF`.
|
||
|
||
### 2026-08-26 — Экранные клавиатуры настроек
|
||
|
||
- В `src/ui/OnScreenKeyboard` добавлен общий C#-контроллер: русский и английский алфавиты,
|
||
Shift, 33 спецсимвола, каретка, пробел, очистка/удаление и отдельный цифровой режим.
|
||
- Все `LineEdit` и `SpinBox` Settings, включая динамические fallback-строки и фиксированные слоты
|
||
внешней темы, регистрируются автоматически; native virtual keyboard отключён, числовой ввод
|
||
подтверждается через `SpinBox.Apply()`, а активное поле поднимается над нижней панелью.
|
||
- Fallback и обе темы содержат заранее созданные текстовую и цифровую панели с кнопкой скрытия
|
||
справа сверху. Runtime не создаёт и не перестраивает их Control-узлы.
|
||
- Theme Builder получил общий компонент, preview-состояния `KEYBOARD: OFF/TEXT/NUM` и 57 новых
|
||
metadata binding-id. Контракт расширен с 403 до 460 биндингов; SHA-256 каталога:
|
||
`4552F55F78E744DF90DC4D8153330D4A6A6C156673C12B384E828D99AA93CE3A`.
|
||
- NUnit-набор расширен до 262 тестов; fallback Settings импортируется и запускается headless без
|
||
ошибок. Отдельный runtime smoke подтвердил ввод букв и числа, показ и скрытие обеих панелей как
|
||
во fallback, так и в установленной внешней теме. Ручная проверка пальцем и в перчатках остаётся
|
||
частью физической приёмки Этапа 8.
|
||
- Установлен `space-pixel.pck` размером 672840 байт, SHA-256:
|
||
`752710CB91BD7D4A5FCC60A80B427671B2E98CE937203CD24A5648943008399A`. Предыдущий пакет сохранён
|
||
как `active_theme.pre-onscreen-keyboard.pck` (634888 байт, SHA-256:
|
||
`22359A8D488352BA3E2C5E6E294C6822EEDEA853BD66ADF429C861D6C7B00DD8`).
|
||
- У бонусного `SpinBox` удалена приписка «жет.» во fallback и во всех 16 фиксированных слотах
|
||
обеих тем: многозначное количество дополнительных жетонов теперь показывается полностью.
|
||
Установлен исправленный `space-pixel.pck` размером 672520 байт, SHA-256:
|
||
`5CC976A4B8EB07A15571F00DCB74BD7D46F1CBC8BDEAF97E1A994131763321E7`; предыдущий пакет сохранён
|
||
как `active_theme.pre-bonus-suffix-fix.pck`.
|
||
|
||
### 2026-08-26 — Host адаптирован к самодостаточным сценам Theme Builder
|
||
|
||
- Поддержана новая структура `themes/example`, `themes/space-pixel` и `themes/<theme-id>`;
|
||
production-сцены теперь сами содержат Theme, статический `ThemeBackground` и
|
||
`BackgroundDecoration` с metadata `exchanger_self_contained_visuals = true`.
|
||
- Loader проверяет структуру каждой сцены, запрещает смешивать два режима внутри одного PCK и
|
||
отклоняет глобальные background/decoration пути, которые дублировали бы экранную композицию.
|
||
- `ReducedEffects` теперь скрывает и полностью останавливает decoration внутри текущего экрана;
|
||
единый лимит узлов, CanvasItem, анимаций и кадров применяется и к самодостаточной decoration.
|
||
- Устранено runtime-переоформление готовых сцен: прежний проход Settings принудительно назначал
|
||
панелям `WhitePanel` и менял внешний вид относительно preview; теперь визуальные семантики и
|
||
размеры keypad применяются только к встроенному fallback, а self-contained view сохраняется.
|
||
- Root внешней сцены больше не переименовывается в `ThemeView` и не получает повторный layout
|
||
preset; бюджет decoration стал чистой проверкой без изменения FPS/`speed_scale` ресурса.
|
||
- Удалены семь зон runtime-достраивания: запас, связь и промо Home, кнопки карточных сумм, правила
|
||
бонусов, суммы оплаты и список промо Settings. На этом шаге контракт был расширен до 403
|
||
обязательных биндингов;
|
||
самодостаточная тема заранее содержит все визуальные элементы и фиксированные слоты.
|
||
- Актуальный `space-pixel.pck` размером 632992 байта установлен в стандартный `user://`; SHA-256:
|
||
`122A6F16ED727080AC1F8EC407E94CD5D9E6CDBDF0A0465F27E1AF6C26ADAF6E`. Предыдущий пакет сохранён
|
||
рядом как `active_theme.pre-binding-only.pck`; более ранняя копия также сохранена как
|
||
`active_theme.pre-self-contained.pck`.
|
||
- Solution собирается без предупреждений; 254 unit/integration-теста проходят, 17 физических
|
||
hardware-тестов безопасно пропущены без явного запуска на плате.
|
||
|
||
### 2026-08-25 — Завершён полносценовый DLC-контракт v2
|
||
|
||
- Все девять C#-контроллеров экранов отделены от визуального дерева: внешний DLC заменяет только
|
||
`SafeMargin` на собственный `ThemeView`, а оплата, навигация, настройки и оборудование остаются
|
||
в основном приложении.
|
||
- Зафиксированы шаблон имён `screen_<key>.tscn`, root-имена `ThemeScreen_<Name>`, metadata
|
||
`exchanger_screen_binding_id` и `exchanger_binding_id`; расположение элементов внутри темы
|
||
больше не влияет на привязку.
|
||
- В host добавлен закреплённый SHA-256 каталога из 187 обязательных биндингов. До активации PCK
|
||
проверяются девять сцен, ID, Godot-типы, полнота, отсутствие неизвестных биндингов, дубли и
|
||
полный запрет скриптов.
|
||
- Обратная совместимость manifest v1 сохранена как стилевой режим с единственной встроенной
|
||
fallback-темой.
|
||
- `ExchangerThemeDlcBuilder` реорганизован в `themes/_template` и `themes/space_pixel`; preview
|
||
использует реальные production-сцены, показывает 9 вкладок и overlay binding-id с live reload.
|
||
- `_template` и `space_pixel` проходят full-scene validator. Итоговый PCK размером 407832 байта
|
||
установлен в стандартный `user://`; SHA-256 пакета:
|
||
`CB1EC8F6C6F908139CD6B602E5A7A401E271F8AD5F69BF5C3943B4FD6D10FF47`.
|
||
- Runtime smoke подтвердил внешний `ThemeView` у всех девяти контроллеров, отсутствие app-owned
|
||
Timer/AudioStreamPlayer внутри темы и чистый журнал Godot. Solution собирается без
|
||
предупреждений; 247 unit/integration-тестов проходят, 17 физических hardware-тестов безопасно
|
||
пропущены без явного запуска на плате.
|
||
|
||
### 2026-08-25 — Runtime-интерфейс синхронизирован с preview темы
|
||
|
||
- Логический viewport приложения переведён с 1080×1920 на нативные для theme-builder 720×1280;
|
||
пиксельный текст больше не проходит через промежуточное уменьшение 2/3.
|
||
- Главная, наличная и карточная ветки, терминал, успех, ошибка и сервисный вход получили геометрию,
|
||
панели, кнопки и общий footer из соответствующих preview-сцен `space_pixel`.
|
||
- DLC manifest v1 обратно совместимо расширен именованными `VisualSlots` для пассивной графики
|
||
`home`, `cash`, `card` и `result`; host сохраняет поверх них собственные тексты и бизнес-логику.
|
||
- Для decoration добавлен явный `BackgroundDecorationScale`; текущий фон 1080×1920 масштабируется
|
||
до нативной сетки коэффициентом 0.666667 без влияния на screen-specific слоты.
|
||
- Настройки владельца пересчитаны в нативные координаты 720×1280, при этом пять разрешённых вкладок
|
||
и автоматическое сохранение сохранены; технические разделы не возвращались.
|
||
- Актуальный `space_pixel` PCK собран и установлен. Сборка без предупреждений; 234/234 модульных
|
||
теста прошли, 17 аппаратных тестов безопасно пропущены без явного запуска на плате.
|
||
|
||
### 2026-08-25 — Настройки переориентированы на владельца автомата
|
||
|
||
- Из навигации `SettingsScreen` удалены вкладки «Тема», «Оборудование» и «Диагностика»; в шапке оставлен только короткий заголовок «НАСТРОЙКИ» без ролей и поясняющего подзаголовка.
|
||
- Убраны внешний DLC и путь `.pck`, сводка применения темы, технический итог промо, COM/mock/reconnect, окно/FPS, состояние приложения, предупреждения, журналирование и тестовая выдача.
|
||
- Владельцу оставлены пять разделов: «Общие», «Звук», «Промо», «Тарифы» и «Оплата», включая PIN, оформление главного экрана, цены, бонусы, способы оплаты и время ожидания.
|
||
- Скрытые deployment-параметры исключены из валидации формы владельца и больше не могут заблокировать автосохранение доступных полей.
|
||
- Runtime QA на 720×1280 подтвердила пять вкладок, понятные формулировки и отсутствие всех технических панелей; сборка без предупреждений, 228/228 модульных тестов прошли, 17 аппаратных безопасно пропущены.
|
||
- Удалены избыточные пояснения о назначении проверочного звука и общем диапазоне карточных сумм; высота соответствующих карточек и шапки уменьшена без пустых зон.
|
||
- Тексты всех доступных владельцу разделов и экрана ввода PIN переписаны простым языком: убраны термины `fallback`, «таймаут», «порог», «конфигурация» и технические формулировки автосохранения; пример тарифа теперь показывает готовое количество жетонов с правильным склонением.
|
||
- В `space_pixel` и встроенной резервной теме полностью отключены тени текста и обводка кнопок; светлый текст переведён в чистый белый, а текст на светлых и зелёных поверхностях — в более тёмный контрастный оттенок. Обновлённый PCK собран и установлен в `user://theme_dlc/active_theme.pck`.
|
||
- Исторические записи ниже описывают прежний инженерный UI на момент его реализации и считаются заменёнными этой записью.
|
||
|
||
### 2026-08-24 — PIN перенесён в настройки приложения
|
||
|
||
- Удалена зависимость сервисного входа от `EXCHANGER_SERVICE_OPERATOR_PIN` и `EXCHANGER_SERVICE_ENGINEER_PIN`.
|
||
- На вкладке «Общие» добавлены включение защиты и сброс PIN; изменения по-прежнему сохраняются автоматически.
|
||
- Первый вход после включения или сброса предлагает дважды ввести 4–12 цифр, затем открывает инженерные настройки; последующие входы проверяют PIN и блокируются на 30 секунд после пяти ошибок.
|
||
- В конфигурации версии 11 сохраняются только случайная 16-байтовая соль, PBKDF2-SHA256-хеш длиной 32 байта и 210000 итераций; открытый PIN не попадает в JSON, audit или логи.
|
||
- Одинаковая политика применяется к физическому IN0 и debug-действию F10; при выключенной защите они открывают настройки напрямую.
|
||
- Runtime-проверка прошла полный цикл создания, подтверждения и повторной проверки PIN; тестовая пользовательская конфигурация после проверки восстановлена. Сборка без предупреждений, 225/225 модульных тестов прошли, 17 аппаратных тестов безопасно пропущены без явного профиля запуска.
|
||
|
||
### 2026-08-24 — Цифровые входы привязаны к хопперу
|
||
|
||
- Добавлена именованная production-разводка: `IN0` — active-low сервисная кнопка, активный `IN1` (`бит 0`) — мало жетонов, `IN25` — active-low импульс выдачи. Позднее владелец уточнил, что эта пара относится ко второму хопперу; `IN2/IN24` зарезервированы для первого.
|
||
- `IN0` публикует одно событие только при переходе в активный уровень; debug-сборка открывает `Settings` напрямую, release-сборка использует защищённый путь `ServiceAccess → Settings`, а следующий фронт закрывает настройки после автосохранения. При настроенном входе IN0 дублирующий кадр `SERVICE` игнорируется, поэтому один физический нажим не выполняет два перехода.
|
||
- `IN1` обновляет `Enough/Low` только при реальном изменении и никогда не создаёт числовой остаток.
|
||
- `IN25` считает один жетон на active-low фронт только во время активной выдачи. В целевом `Hopper2Auto` кадры `HOPPER1` игнорируются; без исходного снимка выдача блокируется безопасной ошибкой.
|
||
- Быстрые входы резервно опрашиваются командой `91` каждые 100 мс; индексы, полярности и интервал вынесены в конфигурацию. Конфигурация версии 13 исправляет production-полярность на active-low: `IN1 bit 0 → Low`, `IN1 bit 1 → Enough`.
|
||
- Безопасная проверка COM3 прошла 9/9; текущий `IN1=0` корректно преобразован в `Enough`. Мотор, платёжные приёмники и банковский терминал не запускались.
|
||
- Сборка завершилась с 0 предупреждений и ошибок; модульный набор прошёл 212/212.
|
||
|
||
### 2026-08-24 — Handshake сверен с Unity, Python и реальной платой
|
||
|
||
- На дату записи Unity использовался как источник криптографической проверки `RAES`. Позднее актуальный `plate_config_app` стал источником обязательной startup-последовательности с баннером, паузой и `READY!`; AES-проверка Exchanger при этом сохранена.
|
||
- Exchanger теперь восстанавливает ключ как Unity. Контрольная пара спецификации `8C70…0F7A → 99ED…B0E1` воспроизводится точно.
|
||
- Probe на COM3 подтвердил `RAES == AES-256-ECB-Encrypt(07)`. Исторический переход в Ready сразу после `RAES` впоследствии отменён записью от 2026-08-31: актуальный контракт требует точный `READY!`.
|
||
- Реальный ответ цифровых входов имеет вид `INPUT<decimal>` без обязательного пробела; parser принимает оба варианта и защищён регрессионными тестами.
|
||
- Безопасная аппаратная группа прошла 8/8: handshake, `91`, `140`, `150`, `160`, `309?`, `99` и три reconnect-цикла. Ни оплата, ни выдача не запускались.
|
||
- Сборка завершилась без предупреждений и ошибок; модульный набор прошёл 204/204.
|
||
|
||
### 2026-08-24 — Первый безопасный прогон реальной платы
|
||
|
||
- Windows определила контроллер как Silicon Labs CP210x на COM3; порт 115200 8N1/None открывается стабильно.
|
||
- Первый запуск обнаружил реальный Windows edge case: `SerialPort.Read` при штатном закрытии бросает `OperationCanceledException`. Worker теперь безопасно завершает поток; добавлены 6 регрессионных проверок, общий NUnit-набор вырос до 203/203.
|
||
- Первые восемь production-попыток стабильно получили `CHAL` и `RAES`, но завершились до `Ready`: обфусцированный массив ошибочно использовался как эффективный AES-ключ. Причина устранена последующей сверкой с Unity.
|
||
- Добавлен изолированный `HardwareProbe`: случайный `07`, ответ `08` через production AES-256 ECB/NoPadding, классификация `RAES` без сохранения сырых блоков.
|
||
- Первый probe подтвердил отсутствие `AUTH_CNT_OK` и несовпадение при ошибочном прямом ключе. После XOR `0x37` повторный probe подтвердил корректный `RAES`, а Safe прошёл 8/8 без обхода авторизации.
|
||
- Следующий аппаратный шаг: отдельно выполнить Interactive/Payment/Dispense/Cable с явными подтверждениями и физической подготовкой стенда.
|
||
|
||
### 2026-08-24 — Завершён Этап 6
|
||
|
||
- Release-профиль получил borderless fullscreen, скрытый курсор, восстановление окна после focus/display/resolution change; `F10` жёстко ограничен debug-сборкой и состоянием Home.
|
||
- Добавлены Windows watchdog, установка/удаление задачи автозапуска и эксплуатационная инструкция; PowerShell разобран без ошибок, installer проверен через `-WhatIf`, watchdog — одноразовым процессным запуском.
|
||
- Первоначально реализованы физический `SERVICE` → PIN-клавиатура → роли Operator/Engineer, пять попыток и 30-секундная блокировка; последующая запись выше заменила environment-PIN единым PIN приложения.
|
||
- `SettingsScreen` завершён восемью вкладками, полной валидацией, инженерными COM/window настройками, safe reset, подтверждением dirty-exit, отдельным повторным подтверждением restart-only сохранения и итогом без автоматического restart/reconnect.
|
||
- Диагностика показывает версию 0.6.0, тему, безопасное состояние порта/соединения/запаса, последние очищенные предупреждения и тестовую выдачу одного жетона с двойным подтверждением.
|
||
- Добавлены безопасный audit конфигурации, уровни/лимиты журналирования, очистка по сроку и общему объёму; пути, значения, PIN, платёжные данные и сырые протокольные кадры не записываются.
|
||
- Ускоренный soak моделирует 1440 минут, 480 подтверждённых выдач и 10 000 циклов `SessionFlow`; process-runner прошёл 12/12 пятиисекундных сэмплов с пиком working set 217.73 МБ, CSV дополнительно исправлен и повторно проверен под русской локалью.
|
||
- Runtime QA пройдена на 720×1280 и 1080×1920: роли, Equipment/Diagnostics, field validation, discard/continue, test dispense и двухэтапное restart-only сохранение. Тестовые изменения пользовательской конфигурации восстановлены; game/editor logs чистые.
|
||
- Контрольная сборка завершилась с 0 ошибками и 0 предупреждениями; 197/197 NUnit-тестов прошли, 15 hardware-тестов безопасно пропущены без подключённой платы.
|
||
- Реальный 24-часовой wall-clock soak, проверка собственной экранной клавиатуры на физической
|
||
панели и поведение дисплея перенесены в аппаратную приёмку Этапа 8 и явно отмечены как внешняя
|
||
зависимость, а не незавершённая программная задача Этапа 6.
|
||
|
||
### 2026-08-21 — Подтверждён профиль AES платы
|
||
|
||
- Рабочая реализация владельца подтвердила `AES-256`, `CipherMode.ECB`, `PaddingMode.None` и шифрование одного 16-байтового блока через `TransformFinalBlock`.
|
||
- Существующий `Aes256EcbBlockCipher` уже полностью соответствовал этой реализации; `ControllerAesMode: "ECB"` включён по умолчанию.
|
||
- Конфигурация повышена до версии 6; старые пользовательские настройки с пустым AES-режимом автоматически мигрируют на `ECB`.
|
||
- Позднейшая сверка с Unity показала, что сохранённый массив нужно декодировать XOR `0x37`; после исправления старый `RAES`-пример и реальная плата совпали.
|
||
|
||
### 2026-08-21 — Handshake приведён к фактическому порядку платы
|
||
|
||
- На основании предоставленного тогда рабочего метода порядок был уточнён до `07 → CHAL → 08 → RAES`. Эта историческая схема заменена 2026-08-31 строгим startup-контрактом `banner → 2000 ms → 07/CHAL/08/RAES → READY!`.
|
||
- Добавлены состояния ожидания `CHAL` и доказательства платы; обязательное ожидание `AUTH_CNT_OK` впоследствии удалено по фактической трассе.
|
||
- Реализованы быстрый CHAL-timeout `min(1200, max(250, timeout/4))`, общий настраиваемый timeout и отмена ожидания при disconnect.
|
||
- Конфигурация обновлена до версии 5 с `HandshakeTimeoutMilliseconds: 5000`.
|
||
- Wire-симулятор и интеграционные тесты переведены на новый порядок; набор расширен до 98 тестов, включая закрытие порта при неверном доказательстве платы.
|
||
|
||
### 2026-08-21 — Завершён Этап 3
|
||
|
||
- Документированный профиль протокола 2.3.5 закрыт: framing, parser, double handshake, платежи, обязательные `ASK:*`, Вендист, `SERVICE`, `INPUT` и legacy-хоппер отделены от транспорта и Godot Node.
|
||
- Добавлен `PeripheryControllerSimulator`, который проходит настоящий handshake и воспроизводит card success, malformed-кадр, бинарный датчик, полную и частичную выдачу без COM-порта.
|
||
- Протокольный адаптер игнорирует бизнес-кадры до криптографически подтверждённого `RAES`, подавляет повторный `DISPENSE`, не выполняет автоматический retry и блокирует legacy-хоппер после аппаратной ошибки.
|
||
- Добавлены типизированные команды status-запросов `140`, `150`, `160`, `99`; неизвестные форматы ответов остаются безопасно неподдерживаемыми.
|
||
- Внешние неопределённости AES, силового канала и физической распиновки вынесены в аппаратную проверку следующих этапов и не подменены предполагаемыми командами.
|
||
- Набор расширен до 91 теста; проверены все результаты card-сессии, несовпадение суммы, pre-auth-фильтрация, service/error, malformed recovery, границы параметров, at-most-once и partial hopper failure.
|
||
|
||
### 2026-08-21 — Фиктивный числовой остаток полностью удалён
|
||
|
||
- Аппаратная граница использует только `MachineStockLevel`: `Enough`, `Low` или технический `Unknown`.
|
||
- Числовой остаток, `MachineStockSnapshot`, `ExactCount`, `Empty`, `Pricing.InitialStock` и пороги удалены из real/mock-контроллеров, сессии, UI и конфигурации.
|
||
- Добавлен parser ответа `INPUT<bitmask>`/`INPUT <bitmask>`, polling командой `91` и конфигурация номера входа/полярности датчика.
|
||
- UI и mock работают только с «МНОГО»/«МАЛО»; успешная выдача не изменяет уровень без нового сигнала датчика.
|
||
- `Unknown` блокирует начало оплаты, `Low` разрешает работу без ложной проверки точного остатка.
|
||
- Тестовый набор расширен до 77 тестов, включая polling датчика и сброс уровня в `Unknown` при disconnect.
|
||
|
||
### 2026-08-21 — Добавлен предоставленный ключ контроллера
|
||
|
||
- 32-байтовый `AesEncryptedKey` добавлен в `PeripheryControllerSecurity` и документацию протокола.
|
||
- Переменная `EXCHANGER_CONTROLLER_AES_KEY` сохранена как необязательное переопределение для другой ревизии платы.
|
||
- Позднее выяснилось, что массив является обфусцированным: после XOR `0x37` AES-256 ECB/NoPadding воспроизводит пример `RAES` и обмен с реальной платой.
|
||
|
||
### 2026-08-21 — Начата интеграция периферийного контроллера 2.3.5
|
||
|
||
- Добавлен `System.IO.Ports` 8.0.0 и реальный Windows-транспорт 115200 8N1 без flow control.
|
||
- COM-чтение/запись выполняются одним worker-потоком; события передаются в Godot через очереди и главный `_Process`.
|
||
- Реализован ASCII-фреймер `CR`/`LF`, timeout 100 мс, лимит кадра и безопасное восстановление после мусора/переполнения.
|
||
- Добавлены parser/formatter команд `READY`, handshake, `MONEY:*`, `ASK:*`, `SERVICE`, Вендиста и legacy-хоппера.
|
||
- Реализован double-handshake state machine и AES-256 ECB cipher; на этом шаге активация ещё ожидала подтверждения режима, которое владелец предоставил позднее.
|
||
- `PeripheryMachineController` управляет smart-монетником, купюроприёмником, Вендистом, keep-alive и обязательными подтверждениями платежей.
|
||
- Приём оплаты блокируется, пока не настроен безопасный production-способ выдачи; автоматический retry выдачи отсутствует.
|
||
- Добавлен периодический reconnect COM-порта и повторная авторизация после подключения.
|
||
- Тестовый набор расширен до 72 тестов, включая framing, команды, handshake, AES-вектор, проверку встроенного ключа, платежи, keep-alive, disconnect во время выдачи и блокировку небезопасной выдачи.
|
||
|
||
### 2026-08-21 — Добавлен безопасный контекст и журнал операций
|
||
|
||
- Каждая cash/card-сессия получает уникальный локальный `OperationId`; начало и единственный итог публикуются типизированными снимками.
|
||
- Добавлены `SessionErrorCode`, безопасные пользовательские тексты и рекомендации `ReturnHome`/`TryAnotherPaymentMethod`/`ContactSupport`; сырая ошибка оборудования не попадает в UI.
|
||
- Отдельный JSONL-журнал `user://logs/operations.jsonl` хранит только безопасные поля и при старте закрывает незавершённые записи как `UnknownAfterRestart`, не повторяя оплату или выдачу.
|
||
- `IMachineController` и `MockMachineController` поддерживают событие потери связи; потеря во время выдачи приводит к `DispenseStatusUnknown` и запрещает предполагать результат.
|
||
- Debug-панель карточного сценария позволяет вручную воспроизвести успех, отказ, полную/частичную ошибку выдачи и потерю связи.
|
||
- Правила восстановления и ручная приёмка зафиксированы в `docs/operations/RECOVERY_AND_MOCK_QA.md`.
|
||
- Набор расширен до 45 тестов; проверены уникальность идентификаторов, единственный финальный исход, typed errors, очистка подписок и восстановление JSONL.
|
||
|
||
### 2026-08-21 — Автоматизировано тестирование mock-среза
|
||
|
||
- Подключены NUnit 4.6.1, NUnit3TestAdapter 6.2.0 и Microsoft.NET.Test.Sdk 18.9.0 в отдельном `Exchanger.Tests`.
|
||
- Бизнес-логика вынесена из Godot-узла в чистый `SessionFlow`; `SessionController` остался тонким адаптером `_Process` и журналирования.
|
||
- Добавлен `IMonotonicClock`, благодаря которому таймауты 45/120 секунд проверяются мгновенно управляемыми часами.
|
||
- `PricingPolicy` отделяет проверку суммы и расчёт базовых/бонусных жетонов от UI и Godot.
|
||
- Набор из 37 тестов покрывает конфигурацию, тарифы, cash/card success, decline, timeout, неподтверждённое состояние запаса, duplicate/late events, частичную выдачу, сброс данных и утечки подписок.
|
||
- Автоматически проверяется наличие восьми сцен и четырёх ресурсов тем; семь пользовательских экранов отдельно прошли runtime smoke-загрузку через `godot-ai`.
|
||
- В тот момент mock временно проверял выбранный пакет против числового запаса; позже это поведение удалено после уточнения, что датчик сообщает только «много/мало».
|
||
- `MockMachineController` получил настраиваемую задержку и детерминированные сценарии полной/частичной ошибки выдачи; полный отказ runtime-проверен до корректного `ErrorScreen`.
|
||
- Итоговые `dotnet test`, запуск проекта и Godot editor/game logs проходят без ошибок и предупреждений.
|
||
|
||
### 2026-08-21 — Завершён Этап 2
|
||
|
||
- Добавлена типизированная аппаратная граница `IMachineController` и `MockMachineController` для баланса, запаса, карточного результата и подтверждения выдачи без предположений о wire-протоколе.
|
||
- Реализован `SessionController` с явными cash/card состояниями, монотонными таймаутами, сбросом временных данных и защитой от повторных команд.
|
||
- Добавлены `CardCustomAmountScreen`, `CardTerminalScreen`, `SuccessScreen` и `ErrorScreen`; все экраны подключены через `ScreenRegistry`.
|
||
- Наличная ветка получила debug-ввод купюр и явное подтверждение выдачи; карточная ветка — debug-результаты терминала.
|
||
- Реализован автономный `AdvertisementPlayer`: локальный плейлист, постер/текстовый fallback, беззвучный режим по умолчанию и остановка декодирования вне `HomeScreen`.
|
||
- Runtime пройдены cash success, card success, card decline и custom amount; проверены выдача только после подтверждения, отображение запаса и возвраты на Home.
|
||
- Визуальная QA 720×1280 при логическом viewport 1080×1920 выявила и устранила неравномерную цифровую клавиатуру; финальная сетка состоит из трёх равных колонок.
|
||
- Контрольная C#-сборка и Godot runtime-логи чистые; новые NuGet-зависимости не потребовались.
|
||
|
||
### 2026-08-20 — Первый пакет Этапа 2
|
||
|
||
- Логический viewport переведён на 1080×1920, debug-window — на 720×1280; добавлена миграция пользовательской конфигурации до версии 2.
|
||
- Добавлены Input Actions `kiosk_back` и `debug_cycle_theme`.
|
||
- Реализованы Autoload `ScreenManager`, централизованный `ScreenRegistry`, общий `ScreenHost` и UI fade-переходы.
|
||
- После runtime-теста добавлен `TransitionInputBlocker`, устраняющий случайный выбор элемента под координатой отпущенного тача на новом экране.
|
||
- Удалён ландшафтный `StartScreen`; созданы `HomeScreen`, `CashPaymentScreen` и `CardAmountScreen`.
|
||
- Добавлены конфигурируемые тарифы, бонусы, карточные пресеты, брендинг, настройки промо и `TokenStockStatus`.
|
||
- Космическая тема переведена с фиолетового прототипа на тёмно-синюю/салатовую палитру референса.
|
||
- Визуально и через runtime UI проверены Home → Cash → Home → CardAmount → Home на 720×1280 при логическом viewport 1080×1920.
|
||
- Разрешение на необходимые NuGet-пакеты получено; в текущем пакете новые зависимости не потребовались.
|
||
|
||
### 2026-08-20 — Завершён Этап 0
|
||
|
||
- Инициализирован локальный Git-репозиторий без назначения remote и создания первого коммита.
|
||
- `.agents/AGENTS.md` признан проектным файлом и исключён из списка игнорирования.
|
||
- Создан `docs/architecture/decisions.md` с базовыми архитектурными решениями.
|
||
- До появления целевого оборудования принята универсальная, конфигурируемая стратегия без привязки к конкретному ПК, GPU, DPI или тач-контроллеру.
|
||
- Реализация wire-протокола и COM перенесена в Этапы 3–4; входом станет документация и список команд, которые владелец позднее добавит в `references/`.
|
||
- Зафиксировано, что все аппаратные данные поступают ПК от контроллера и преобразуются внутри приложения в типизированные события.
|
||
|
||
### 2026-08-20 — Инициализация дорожной карты
|
||
|
||
- Изучено исходное ТЗ и текущее состояние проекта.
|
||
- Исторически был зафиксирован Compatibility renderer вместо противоречащего ему Forward+; текущий режим — Mobile/Vulkan для Native Video.
|
||
- Сформирована архитектура тем, навигации, сессии и аппаратного слоя.
|
||
- Определены этапы разработки, критерии приёмки, риски и вопросы к владельцу продукта.
|
||
|
||
### 2026-08-20 — Завершён Этап 1
|
||
|
||
- Исторически были настроены viewport 1920×1080, Compatibility, production fullscreen и debug-window профиль; текущий рендерер — Mobile/Vulkan.
|
||
- Созданы `App`, `StartScreen`, `TouchInputGuard`, `AppSettings` и журнал с ротацией.
|
||
- Реализованы `VendingThemeDefinition`, Autoload `ThemeManager`, базовая и космическая темы.
|
||
- Добавлен анимированный shader-фон, космические частицы и статический fallback для облегчённого режима.
|
||
- Созданы `IHardwarePort`, рабочая mock-реализация и безопасные Windows/Android каркасы.
|
||
- Проверены C#-сборка без предупреждений, main scene, runtime-логи, смена тем, кнопка «Начать», mock PING и создание пользовательской конфигурации.
|
||
- Визуально проверены 1280×720, 1024×768 и borderless fullscreen 2560×1440.
|
||
- Подтверждено, что для реального Windows SerialPort потребуется предварительно согласованный NuGet-пакет `System.IO.Ports`.
|
||
|
||
### 2026-08-20 — Исправлена корневая документация
|
||
|
||
- `README.md` полностью заменён документацией Exchanger на русском языке.
|
||
- Добавлены описание продукта, текущее состояние, запуск, конфигурация, журналы, архитектура тем и аппаратного слоя.
|
||
- Удалён случайно установленный корневой `LICENSE` GodotX Toast.
|
||
- Лицензии сторонних компонентов в `addons/` и `godot-ai-LICENSE.txt` сохранены.
|
||
- Зафиксировано отсутствие объявленной собственной лицензии Exchanger.
|
||
|
||
### 2026-08-20 — Учтены визуальные референсы аппарата
|
||
|
||
- `references/` добавлен в `.gitignore` как локальный источник концептов, не предназначенный для production.
|
||
- Проанализированы семь макетов UI 720×1280, рендер корпуса и видео 1080×1920/50 FPS длительностью 32,16 секунды.
|
||
- Целевая ориентация изменена с ландшафтного технического прототипа на портретную 9:16.
|
||
- Зафиксированы домашний экран, наличная ветка, карточные суммы/клавиатура/терминал, отказ оплаты, запас жетонов, бонусы, промо-блок и поддержка.
|
||
- План Этапов 2, 3, 5 и 7 переработан с учётом реального корпуса и пользовательского потока.
|
||
- Подробный разбор сохранён в `docs/REFERENCE_ANALYSIS.md`.
|
||
|
||
### 2026-08-20 — Фон приведён к референсам
|
||
|
||
- `AnimatedBackground` заменён на `StaticBackground`.
|
||
- Из активной сцены и `VendingThemeDefinition` удалены фоновые shader-материалы и частицы.
|
||
- Удалены ставшие ненужными shader/particle-ресурсы обеих тем.
|
||
- Сохранена поддержка статической текстуры фона и отдельной `PackedScene` с анимированными спрайтами.
|
||
- Промо-блок отделён в архитектуре от фонового слоя приложения.
|
||
|
||
### 2026-08-21 — Завершена программная часть Этапа 4
|
||
|
||
- В `IMachineController` добавлено безопасное состояние соединения `Disconnected/Authenticating/Ready/Faulted`, одинаковое для mock и реального протокольного адаптера.
|
||
- Потеря связи переводит бинарный запас в `Unknown`, блокируя начало новой оплаты без вымышленного остатка.
|
||
- Добавлен атомарный `ConnectionStatusIndicator`: в production он не занимает внимание при `Ready`, но показывает авторизацию, обрыв и ошибку; debug также показывает штатное подключение.
|
||
- Интеграционный тест выполняет 200 reconnect-циклов с обрывом во время выдачи и подтверждает отсутствие автоматического повтора и позднего завершения операции.
|
||
- Полный набор расширен до 100 тестов; сборка без предупреждений, Godot 4.7.1 runtime и журналы чистые, индикатор визуально проверен на 720×1280.
|
||
- Физический длительный прогон и отключение USB-кабеля остаются внешней приёмкой до появления контроллера.
|
||
|
||
### 2026-08-21 — Подготовлена аппаратная приёмка платы
|
||
|
||
- Добавлен отдельный проект `Exchanger.HardwareTests` из 15 NUnit-проверок production-пути `SerialPortHardware → ControllerAuthenticator → PeripheryMachineController`.
|
||
- Безопасная группа проверяет COM 115200 8N1, production AES-handshake, `INPUT`, status-команды, закрытие и повторную авторизацию.
|
||
- Ручные группы покрывают SERVICE, монетник, купюроприёмник и оба бинарных состояния датчика; отсутствующую периферию можно пропустить независимо.
|
||
- Реальная карточная операция, физическая выдача и отключение USB требуют разных явных подтверждений и не могут стартовать из обычного `dotnet test`.
|
||
- PowerShell-runner создаёт локальный TRX-отчёт в `artifacts/board-tests/`; сырые кадры, AES-данные и секреты в отчёт не попадают.
|
||
- Полная инструкция запуска и порядок приёмки сохранены в `docs/hardware/BOARD_ACCEPTANCE_TESTS.md`.
|
||
|
||
### 2026-08-21 — Разработка тем полностью вынесена из Exchanger
|
||
|
||
- Каталог `themes/`, встроенные `.tres`-темы и debug-переключение удалены из основного проекта.
|
||
- Exchanger содержит только программную fallback-тему и runtime-контракт одного внешнего DLC; списка тем и горячего переключения больше нет.
|
||
- Конфигурация версии 7 читает `Themes.ExternalDlcPath` из пользовательского `appsettings.json`; по умолчанию используется `user://theme_dlc/active_theme.pck`.
|
||
- Реализован формат `ExchangerThemeDlc` v1: `.pck` до 512 МиБ, обязательный manifest и `Theme`, необязательные статическая текстура и `PackedScene` анимированных спрайтов.
|
||
- Пакет монтируется с `replace_files=false`; пути ограничены `res://exchanger_theme_dlc/`, повреждённый DLC безопасно оставляет fallback.
|
||
- В соседнем `../ExchangerThemeDlcBuilder` создан независимый Godot 4.7-проект с manifest, примером темы, статическим фоном и PowerShell-сборщиком/установщиком.
|
||
- Реальный `.pck` успешно собран и загружен Exchanger через production loader; fallback отдельно визуально проверен в Godot на 720×1280.
|
||
- Набор расширен до 105 автоматических тестов плюс 15 отключённых по умолчанию аппаратных проверок; solution собирается без предупреждений.
|
||
|
||
### 2026-08-21 — Создана первая внешняя тема `space_pixel`
|
||
|
||
- В независимом `../ExchangerThemeDlcBuilder` обработаны предоставленные 46 PNG, статический фон 1080×1920, кириллический pixel-font и восемь покадровых последовательностей.
|
||
- Оригиналы сохранены в `themes_src/space_pixel/`; runtime-копии получили стабильные ASCII-пути и не зависят от основного проекта.
|
||
- Реализованы `ui_theme.tres`, статический фон и пассивная decoration-сцена мерцающих звёзд и пролетающих НЛО без скриптов, шейдеров, частиц и видео.
|
||
- В manifest v1 добавлен обратно совместимый флаг `UseNearestTextureFilter`; `space_pixel` включает nearest, остальные DLC сохраняют стандартную фильтрацию.
|
||
- Экспорт текстовых `.tres/.tscn` в бинарный формат отключён, поэтому переносимый PCK загружается по стабильным путям без cross-project UID-предупреждений.
|
||
- `build/active_theme.pck` собран и независимо проверен production-загрузчиком Exchanger: тема активируется, приложение завершает smoke-run без ошибок и предупреждений.
|
||
- Контекстные анимации оплаты подготовлены в builder, но остаются не подключёнными до появления именованных screen-specific слотов в следующей версии DLC-контракта.
|
||
- В локальный игнорируемый каталог `themes_src/space_pixel/references/` скопированы восемь JPG и видео интерфейса; reference-медиа не экспортируются в PCK.
|
||
- После просмотра всех макетов и 64 кадров видео тема получила семантические вариации `ScreenTitle`, `PanelTitle`, `PanelText`, `SupportText`, `PrimaryButton`, `CompactButton`, `BackButton` и `WhitePanel`.
|
||
- Preview перестроен по наличному экрану референса в 720×1280; runtime-декорация при этом сохраняет координаты host-приложения 1080×1920 и масштабируется только внутри preview.
|
||
- Для полного reference-look основному Exchanger позднее потребуется назначить semantic `theme_type_variation`, вынести общий footer и добавить screen-specific слоты анимаций; точный список узлов зафиксирован в builder `docs/SPACE_PIXEL.md`.
|
||
- Builder-preview расширен до переключаемой галереи всех семи пользовательских экранов темы: Home, Cash, CardAmount, CardCustom, CardTerminal, Success и Error.
|
||
- В builder-preview добавлен live reload: сохранённые изменения Theme, изображений и preview-сцен автоматически появляются в запущенной галерее без ручного перезапуска и без сброса выбранной вкладки; preview-инструмент не экспортируется в DLC.
|
||
- Layout всех семи preview-сцен приведён к общему правилу: после зелёной шапки сохраняется заметный белый отступ, кнопки не прилипают к заголовкам, а изображения и экранные анимации центрируются относительно внутренней области `WhitePanel`.
|
||
|
||
### 2026-08-24 — Начата реализация сервисных настроек
|
||
|
||
- Конфигурация повышена до версии 8 и получила `AudioSettings`: master volume, master mute и отдельную громкость промо-ролика.
|
||
- Добавлен чистый C# `SettingsService` с изолированной рабочей копией и неизменяемым активным снимком, атомарной записью через временный файл, `.bak` предыдущей корректной версии и восстановлением после повреждения JSON.
|
||
- `App` загружает настройки через новый сервис и применяет master volume к Godot audio bus; `AdvertisementPlayer` отдельно применяет громкость промо-ролика, сохраняя существующий ad mute.
|
||
- Миграция реального пользовательского файла 7 → 8 проверена в runtime: создана резервная версия 7, временный файл удалён, домашний экран и журналы Godot чистые.
|
||
- NUnit-набор расширен до 119 тестов: добавлены аудиограницы, working-copy isolation, round-trip сохранения, backup, восстановление из backup/defaults, защита от конфигурации будущей версии, безопасный запуск при невозможности записи и smoke-проверка новой сцены.
|
||
- Добавлен атомарный `SettingsScreen` для звука: рабочая копия, live-preview, сохранение через `SettingsService`, отмена с восстановлением активного снимка и безопасный синусоидальный тестовый сигнал.
|
||
- Master и промо разведены по отдельным Godot audio buses; bus промо-ролика отправляется в Master и меняется независимо без двойного пересчёта громкости в проигрывателе.
|
||
- Вход по `F10` возможен только из Home и только при `OS.IsDebugBuild()`; production-вход остаётся закрытым до реализации отдельной авторизации сервисного персонала.
|
||
- Экран и действия сохранения/отмены проверены в runtime на 1080×1920 и 720×1280; повторный тестовый сигнал и оба аудиоканала не оставляют ошибок в Godot game/editor logs.
|
||
- `SettingsScreen` расширен вкладкой «Тарифы и бонусы»: цена жетона, динамические строки порог → бонус, добавление, удаление, сортировка перед сохранением и preview типовых сумм через production `PricingPolicy`.
|
||
- В интерфейсе явно показано правило расчёта: из подходящих строк берётся один наибольший бонус, строки не суммируются.
|
||
- Добавлен чистый `PricingSettingsValidator`: блокирует нулевую/чрезмерную цену, цену выше максимальной суммы, пороги вне диапазона, отрицательные/чрезмерные бонусы и дубли порогов; ошибки цены и каждой строки показываются рядом с полем.
|
||
- Сохранённые тарифы безопасно применяются только из Home: заново настраиваются `SessionController`, наличный, карточный и ручной экраны, после чего восстанавливаются stock/connection state; активную операцию редактор открыть не может.
|
||
- NUnit-набор расширен до 126 тестов; тарифный UI проверен в runtime на 720×1280 и 1080×1920, включая add/remove, duplicate validation, disabled Save, preview, Cancel и сохранение без ошибок Godot.
|
||
- Конфигурация повышена до версии 9 и получила отдельный блок `Payments`; runtime-миграция реального файла 8 → 9 создала корректную резервную версию 8 без оставшегося временного файла.
|
||
- В `SettingsScreen` добавлена вкладка «Оплата»: независимое включение наличной и карты, максимальная карточная сумма, шаг ручного ввода, динамический редактор пресетов и три пользовательских таймаута с пояснениями безопасных диапазонов.
|
||
- Карточные пресеты сортируются, проверяются на диапазон, кратность шагу и дубли. Пустой список разрешён, поскольку карточная ветка сохраняет действие «Другая сумма».
|
||
- `PaymentSettingsValidator` запрещает отключение обоих способов оплаты и проверяет таймауты; `PricingSettingsValidator` дополнен проверкой максимума, шага и каждой строки пресета.
|
||
- `SessionFlow` отклоняет отключённый способ до начала операции и обращения к контроллеру, а `HomeScreen` сразу блокирует соответствующую кнопку вместе с существующей блокировкой по запасу.
|
||
- Настройки безопасно применяются только из Home; runtime-проверка временно отключила наличную оплату, подтвердила блокировку кнопки и восстановила исходную конфигурацию.
|
||
- NUnit-набор расширен до 137 тестов; 15 тестов физической платы по-прежнему безопасно пропускаются без явного флага.
|
||
- Экран «Оплата», add/remove, ошибки способов оплаты, диапазонов и disabled Save проверены в Godot 4.7.1 на 720×1280 и 1080×1920; финальные game/editor logs чистые.
|
||
- `SettingsScreen` получил стартовую вкладку «Общие» с четырёхкнопочной навигацией, однострочными полями названия, подзаголовка и контакта техподдержки, ограничениями длины и отдельным preview домашнего экрана.
|
||
- Добавлен чистый `BrandingSettingsValidator`: обязательность, максимальная длина и запрет управляющих символов проверяются до записи; runtime-нормализация сворачивает пробелы и безопасно ограничивает текст из внешнего JSON.
|
||
- Сохранение общих данных проверено сквозным runtime-сценарием до `HomeScreen`; тестовые значения после проверки заменены исходными, пользовательская конфигурация версии 9 не оставила временного файла.
|
||
- NUnit-набор расширен до 146 тестов; вкладка «Общие», пустое поле, disabled Save, живой preview и сохранение проверены в Godot 4.7.1 на 720×1280 и 1080×1920, журналы чистые.
|
||
- `SettingsScreen` получил пятую вкладку «Промо»: включение блока, отдельный mute промо-роликов, fallback-текст, необязательный poster, динамический OGV-плейлист до 12 строк и сводный preview состояния.
|
||
- Новый `AdvertisementSettingsValidator` принимает только `res://`, `user://` или абсолютные локальные пути, запрещает traversal/сетевые URL, проверяет форматы PNG/JPG/JPEG/WEBP и OGV, существование файлов, дубли и длины до сохранения.
|
||
- Отключённое промо намеренно позволяет сохранить временно недоступные пути: `AdvertisementPlayer` в этом режиме не загружает poster, не создаёт видеопоток и показывает «ПРОМО ОТКЛЮЧЕНО».
|
||
- `AdvertisementPlayer` дополнен загрузкой внешнего raster-постера через `Image` и локального OGV через `VideoStreamTheora`; диагностическое предупреждение больше не раскрывает полный путь файла.
|
||
- Runtime-проверка покрыла пустой fallback, отсутствующий poster, пустой/отсутствующий промо-ролик, add/remove, disabled Save и безопасное сохранение отключённого блока; после теста исходные настройки промо восстановлены.
|
||
- NUnit-набор расширен до 157 тестов; вкладка «Промо» визуально проверена в Godot 4.7.1 на 720×1280 и 1080×1920, game/editor logs чистые.
|
||
- `SettingsScreen` получил шестую вкладку «Тема»: включение единственного внешнего DLC, путь к `.pck`, статус доступности пакета и облегчённый режим эффектов.
|
||
- Новый `ThemeSettingsValidator` принимает только `user://` либо абсолютный локальный путь к `.pck`, запрещает traversal и управляющие символы; отсутствие будущего пакета не мешает сохранить корректный путь и безопасно оставить fallback-тему.
|
||
- В UI и runtime зафиксирована граница применения: `ReducedEffects` переключается сразу и откатывается кнопкой «Отменить», а включение DLC и путь применяются только при следующем запуске без самопроизвольной перезагрузки темы.
|
||
- NUnit-набор расширен до 166 тестов; неверный путь, отключённый DLC, live-preview/откат и внешний вид вкладки проверены в Godot 4.7.1 на 720×1280 и 1080×1920, пользовательские настройки восстановлены, game/editor logs чистые.
|
||
|
||
### 2026-08-24 — Начат Этап 7 и синхронизирован host-контракт тем
|
||
|
||
- Все девять runtime-экранов получили семантические `theme_type_variation`; встроенная тема
|
||
регистрирует их как безопасные наследники базовых Godot-классов, поэтому fallback не изменился.
|
||
- Динамически создаваемые строки настроек также используют `PanelText` и `CompactButton`, при этом
|
||
`CheckButton` и `OptionButton` сохраняют собственные базовые типы.
|
||
- `ScreenManager` отключает processing скрытого экрана и включает его только перед показом нового;
|
||
runtime-переход Home → Settings подтвердил один активный экран и восемь отключённых.
|
||
- Зафиксирован debug-baseline на 720×1280 и 1080×1920: стабильные 60 FPS, до 37 draw calls и
|
||
менее 1,4 мс process time; условия и ограничения записаны в
|
||
`docs/operations/PERFORMANCE_BASELINE.md`.
|
||
- Визуально проверены fallback Home 720×1280 и Settings 1080×1920 после внедрения семантических
|
||
типов: элементы читаемы, панели и кнопки не пропали, наложений не обнаружено.
|
||
- Solution собирается без предупреждений; 223 unit/integration-теста проходят, 17 физических
|
||
hardware-тестов остаются безопасно пропущенными без явных разрешающих флагов.
|
||
- Decoration внешнего DLC получила жёсткий runtime-бюджет узлов/спрайтов/кадров и частоты; текущая
|
||
`space_pixel` занимает лишь 5 узлов, 4 CanvasItem, 1 AnimatedSprite2D и 2 кадра при 1 FPS.
|
||
- Runtime-аудит всех девяти экранов устранил неравные колонки цифровых клавиатур и подтвердил
|
||
отсутствие мелких пользовательских кнопок и логики, зависящей от hover.
|
||
- Соседний Theme Builder синхронизирован с девятью экранами и восемью разделами Settings,
|
||
расширенными variations, hot reload и export-аудитом; PCK установлен в стандартный `user://`.
|
||
- `space_pixel` загружен production loader без UID-предупреждений; визуально проверены Home,
|
||
ServiceAccess и Settings/Equipment на 720×1280, а full/reduced профиль сохраняет 60 FPS.
|
||
- После дополнительного host-QA исправлен контраст CheckButton; финальный PCK (167280 байт,
|
||
30 entries) установлен в `user://`, все 8 Settings-разделов и SERVICE проходят runtime smoke.
|
||
|
||
### 2026-08-24 — Начат Этап 9: воспроизводимый Windows export
|
||
|
||
- Добавлен preset `Windows x64` и `tools/export-windows.ps1`, который собирает release из
|
||
очищенной staging-копии, не меняя рабочий проект и не включая editor-autoload.
|
||
- Из PCK исключены `references/`, тесты, docs, tools, artifacts, agent-файлы и все addons;
|
||
проверенный каталог не содержит test DLL или addon-файлов.
|
||
- Первая self-contained сборка 0.6.0 успешно создана: 189 файлов, 180,8 МиБ, внутри присутствуют
|
||
`Exchanger.dll`, `GodotSharp.dll` и `System.IO.Ports.dll`.
|
||
- Экспортированный `Exchanger.exe` прошёл headless smoke-run с exit code 0; порядок повторной
|
||
сборки и pre-release проверки записан в `docs/operations/WINDOWS_EXPORT.md`.
|
||
- Финальный export после semantic/touch-правок повторно собран в
|
||
`artifacts/windows/Exchanger-0.6.0-20260824-163811`: 189 файлов, 180,8 МиБ, exit code 0.
|
||
### 2026-09-07 — Составные действия интерактивного питомца переданы теме
|
||
|
||
- Добавлен обратно совместимый optional composite contract: тема владеет последовательностью
|
||
клипов и визуальных преобразований, а Exchanger передаёт scoped runtime для проверенного движения
|
||
anchor, политик ввода/границ/опор и посадки на доступную поверхность.
|
||
- `space-pixel` реализует `interact`: условный поворот, `come_closer` с scale ×2, повтор `action`,
|
||
вычисляемый проход `falling` через низ/верх экрана без промежуточных опор, `landing` на самой
|
||
высокой опоре и возврат в `idle`; reduced-effects получает сокращённую ветку.
|
||
- Runtime автоматически инвалидируется при смене экрана/темы, drag и конфликтующем действии;
|
||
темы без нового API сохраняют прежний одиночный `interact` fallback.
|
||
- NUnit и полный `InteractivePetBehaviorSmoke` проверяют выбор верхней опоры, PCK-контракт,
|
||
scale ×2, вертикальный wrap, точный порядок клипов и сохранение прежних drag/drop, маршрутов,
|
||
смены экранов и четырёх циклов празднования.
|
||
|
||
### 2026-09-07 — Host синхронизирован с актуальными документами и PCK темы
|
||
|
||
- Подтверждён актуальный контракт: 481 immutable binding + 17 обязательных supplemental ID;
|
||
`Sync-HostBindingContract.ps1` теперь явно исключает supplemental-набор из JSON-генерации, а
|
||
`Test-PackagedBindingContract.ps1` защищает все 17 ID от случайного попадания в каталог.
|
||
- Зафиксирован trusted runtime без устаревшего decoration budget. `space-pixel` публикует
|
||
22 анимации/414 кадров и новые имена walking start/stop через semantic mapping; app-код не
|
||
привязывается к конкретным именам клипов темы.
|
||
- Свежий `space-pixel.pck` 1658512 байт, SHA-256
|
||
`76518563591159EC4B8087911D2BB29924BC29C12AAA4FF1544D97ED01648E91`, прошёл полный
|
||
`InteractivePetBehaviorSmoke` по явному пути на mock-оборудовании.
|
||
- `PaymentRewardFormattingSmoke` больше не сохраняет пользовательские Settings и сравнивает
|
||
наличный/карточный формат на фактическом пресете и бонусном правиле вместо жёсткого ожидания
|
||
нулевого бонуса.
|
||
|
||
### 2026-09-08 — Приземление и простой питомца включены в обычное поведение
|
||
|
||
- После падения при перетаскивании или смене экрана host проигрывает цепочку
|
||
`falling → landing → idle`; `landing` выполняется один полный цикл, а `idle` остаётся состоянием
|
||
ожидания до следующего автономного действия.
|
||
- Старые темы без анимации `landing` продолжают сразу переходить в `idle`.
|
||
- Запрошенные во время падения четыре цикла `dancing` начинаются только после завершения
|
||
приземления.
|
||
- Runtime-smoke с актуальным `space-pixel.pck` и mock-оборудованием подтвердил touch drag,
|
||
смену экранов, `falling → landing → idle`, отложенный танец и composite action. Debug и Release
|
||
прошли по 488/488 NUnit-тестов; 18 аппаратных тестов в каждой конфигурации пропущены.
|
||
|
||
### 2026-09-08 — Вероятностный простой после ходьбы питомца
|
||
|
||
- После завершения маршрута host с вероятностью 35% выбирает `idle` на 3–5 секунд, а в остальных
|
||
случаях — `sitting` на 1,5–3,5 секунды. Темы без `sitting` безопасно используют `idle`.
|
||
- `InteractivePetBehaviorSmoke` допускает оба корректных результата маршрута и продолжает
|
||
проверять `falling → landing → idle`. Unit-тесты фиксируют границу вероятности.
|
||
- Godot 4.7.1 начал штатно добавлять `unique_id` в заголовки узлов `.tscn`; хрупкая проверка
|
||
фиксированного футера Settings адаптирована к дополнительным атрибутам без изменения сцены.
|
||
- Debug и Release прошли по 496/496 NUnit-тестов; 18 аппаратных тестов в каждой конфигурации
|
||
пропущены. Runtime-smoke с новым `space-pixel.pck` на mock-оборудовании завершился успешно.
|
||
|
||
### 2026-09-09 — Действие питомца во время подъёма и спуска
|
||
|
||
- На каждом ненулевом ребре подъёма или спуска host выбирает случайную точку между 20% и 80%
|
||
пути, останавливает перемещение и выполняет `climbing_action_start → climbing_action →
|
||
climbing_action_stop`; средний клип проигрывается один полный цикл.
|
||
- После stop возобновляется исходная анимация направления и продолжается тот же маршрут. Для
|
||
старых тем возможность включается только при наличии всех трёх действий, поэтому частичная
|
||
последовательность не возникает.
|
||
- Reduced-effects теперь действительно исключает автономный подъём сразу после landing, но не
|
||
блокирует явно запрошенные маршруты и их climbing-action.
|
||
- Debug и Release прошли по 529/529 NUnit-тестов. Полный `InteractivePetBehaviorSmoke` с явным
|
||
актуальным `space-pixel.pck` и mock-оборудованием подтвердил порядок клипов, неподвижность
|
||
anchor во время sequence, продолжение маршрута, смены экранов и составное взаимодействие.
|