From 8d4d3ccd4b0492166ceb074121e5b727707bc3a2 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 29 Sep 2026 05:24:00 +0000 Subject: [PATCH 1/3] =?UTF-8?q?=D0=9F=D0=BB=D0=B0=D0=BD=20=D1=80=D0=B0?= =?UTF-8?q?=D0=B7=D0=B2=D0=B8=D1=82=D0=B8=D1=8F:=20=D0=B0=D1=80=D1=85?= =?UTF-8?q?=D0=B8=D1=82=D0=B5=D0=BA=D1=82=D1=83=D1=80=D0=B0=20=D1=81=D0=B8?= =?UTF-8?q?=D1=81=D1=82=D0=B5=D0=BC=D1=8B=20=D1=83=D1=87=D1=91=D1=82=D0=B0?= =?UTF-8?q?=20=D0=B8=20=D1=8D=D1=82=D0=B0=D0=BF=D1=8B=200=E2=80=935?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Утилита переходит от одного рабочего места к системе учёта: вход по ролям, зашифрованный журнал выработки, сессии, режим мастера, выгрузка в CSV, хранилище на сетевом диске. docs/ARCHITECTURE.md фиксирует решения и их причины: - журнал неизменяемых файлов вместо SQLite на сетевом диске; - AES-GCM средствами Windows (CNG) — без зависимостей, работает на Win7; - ключ данных и слоты пользователей: вход = расшифровка своего слота; - PIN оператора плюс секрет рабочего места против перебора; - сессия как именованная закладка, журнал — единственный источник правды; - весь ввод-вывод в фоновом потоке, автоподгрузка опросом. docs/ROADMAP.md: этапы 0–5 системы учёта перед прежними A–F. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01CW3WfPRekjK4x6M8VNRckz --- docs/ARCHITECTURE.md | 331 +++++++++++++++++++++++++++++++++++++++++++ docs/ROADMAP.md | 117 ++++++++++++++- 2 files changed, 442 insertions(+), 6 deletions(-) create mode 100644 docs/ARCHITECTURE.md diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md new file mode 100644 index 0000000..557cdcc --- /dev/null +++ b/docs/ARCHITECTURE.md @@ -0,0 +1,331 @@ +# Архитектура: от утилиты к системе учёта + +Утилита выросла из одной задачи — показать оператору, какие позиции +сменного задания закрыты. Следующий шаг превращает её в многопользовательскую +систему: вход по ролям, журнал выработки, сессии, просмотр для мастера. +Этот документ фиксирует решения, на которых всё это строится, и причины, +по которым выбраны именно они. + +Порядок внедрения — в [ROADMAP.md](ROADMAP.md). + +--- + +## Что остаётся неизменным + +- **Целевая среда.** Windows 7 32-bit, Python 3.8.10, один `.exe` через + PyInstaller, без интернета. +- **Без внешних зависимостей в рантайме.** Всё, что нужно, берётся из + стандартной библиотеки или из самой Windows. +- **Файлы программ только читаются.** В `.nc`, `.fms` и карты наладки + утилита не пишет, к стойке станка не обращается. +- **Ядро (`core/`) не знает ни про окно, ни про хранилище.** Разбор и свод + смены остаются чистыми функциями и проверяются тестами без окружения. + +## Что меняется в принципах + +Утилита начинает **вести историю**. Раньше она читала папку и ничего не +записывала, и часть решений в ROADMAP опиралась на это («утилита истории не +ведёт и не должна»). Теперь запись есть, но строго ограничена: только в +папку данных, которую задал администратор, и только в зашифрованном виде. + +Надпись «только чтение» в окне и README меняется на этапе 1: «файлы программ +не изменяются, станок не затрагивается; учёт ведётся в папке данных». + +--- + +## Роли + +| Возможность | Оператор | Мастер | Администратор | +|---|:-:|:-:|:-:| +| Открыть сменное задание, отмечать программы | ✓ | | | +| Сохранять и открывать сессии | ✓ | | | +| Выработка пишется от его имени | ✓ | | | +| Смотреть выработку всех операторов | | ✓ | ✓ | +| Выгружать выработку в CSV | | ✓ | ✓ | +| Задать папку данных, настроить рабочее место | | | ✓ | +| Заводить, блокировать пользователей, сбрасывать PIN | | | ✓ | + +Права описаны одной таблицей в коде (`services/roles.py`), экраны и сервисы +спрашивают её, а не проверяют роль каждый по-своему. + +**Оговорка.** Оператор и мастер расшифровывают данные одним ключом данных +(см. ниже). Разграничение «мастер не пишет выработку, оператор не видит +чужую» обеспечивает программа, а не криптография. Для цеха этого +достаточно; криптографическое разделение ролей — возможное расширение. + +--- + +## Хранилище + +### Почему не SQLite на сетевом диске + +Предполагается, что данные лежат на сетевом диске и пишут в них несколько +рабочих мест. SQLite в своей документации прямо предупреждает: блокировки +файлов на сетевых файловых системах ненадёжны, и одновременная запись с +разных машин приводит к порче базы. Второй довод — в стандартной библиотеке +нет SQLite с шифрованием. + +### Журнал событий + +Вместо общей базы — журнал из неизменяемых файлов: + +- **Одно событие — один файл.** Файл пишется под временным именем и + переименовывается, когда записан целиком. Наполовину записанных файлов + читатели не видят. +- **Каждое рабочее место пишет только в свою подпапку.** Два компьютера + никогда не пишут в один файл, поэтому конфликтов записи нет в принципе. +- **Файлы только добавляются.** Исправление — это новое событие, которое + отменяет прежнее, а не правка старого файла. +- **У события есть идентификатор** (случайный UUID). Часы на машинах могут + расходиться, поэтому ни дедупликация, ни порядок не опираются только на + время. + +### Локальный индекс + +Читать сотни файлов с сетевого диска при каждом открытии медленно. У каждого +компьютера есть локальный индекс — обычная SQLite, но на **своём** диске +(`%LOCALAPPDATA%`), а не на сетевом. Индекс дочитывает только новые файлы +журнала и хранит уже расшифрованные итоги. Потерять индекс не страшно: он +пересобирается из журнала. + +### Очередь на отправку + +Если сетевой диск недоступен, события пишутся в локальную очередь +(`%LOCALAPPDATA%\FinnPowerCounter\outbox`) и дописываются в журнал, когда +связь вернётся. Оператор при обрыве сети не теряет отметок; в окне виден +значок «нет связи с папкой данных, N событий ждут отправки». + +### Раскладка папки данных + +``` +<папка данных>/ + FORMAT версия формата хранилища, открытым текстом + keys.json ключевые слоты пользователей (см. «Шифрование») + users.enc список пользователей и ролей + journal/ + <рабочее место>/ + 2026-09/ + 20260929T142305Z-.evt + sessions/ + / + 2026-09-29_14-30-05-.session +``` + +Имена файлов не содержат ничего, что стоило бы шифровать: только время и +случайные идентификаторы. Имя сессии, которое вводит пользователь, имя +оператора и содержимое лежат внутри зашифрованного файла. + +### Локальная настройка рабочего места + +Путь к папке данных нужен до входа, поэтому он хранится на самом компьютере: +`%PROGRAMDATA%\FinnPowerCounter\workstation.ini` — путь к папке данных, имя +рабочего места и секрет рабочего места (см. ниже). Пишет его только мастер +первичной настройки под администратором. + +--- + +## Шифрование + +### Цели + +Посторонний с доступом к сетевой папке не должен: + +1. **прочитать** данные — кто, когда, что и сколько сделал; +2. **незаметно изменить** выработку — подделать или исправить событие. + +Защита от подделки «своими» (оператор пишет выработку от чужого имени) в +цели не входит: для неё нужна личная подпись каждого события. Это возможное +расширение, архитектура его не исключает. + +### Шифр + +Стандартная библиотека Python шифров не содержит. Библиотека `cryptography` +в современных версиях собирается на Rust, а Rust больше не поддерживает +Windows 7; старые версии без Rust давно не обновляются, и тащить их ради +шифрования плохо. + +Решение: **AES-256-GCM средствами самой Windows** (CNG, `bcrypt.dll`) через +`ctypes`. Это шифрование ОС: без зависимостей, обновляется вместе с +системой, есть в Windows 7. GCM одновременно шифрует и проверяет +целостность, то есть закрывает обе цели. + +Шифр спрятан за интерфейсом `storage/cipher.py` с двумя реализациями: +CNG для Windows и тестовая на `cryptography` для Linux и CI (только для +тестов, в сборку не попадает). Тесты проверяют, что обе дают одинаковый +результат на эталонных векторах. + +**Проверка на реальной машине — первое, что делается на этапе 0.** Если CNG +на цеховой Windows 7 32-bit поведёт себя не так, как ожидается, этот раздел +пересматривается до начала остальной работы. + +### Ключи + +``` +ключ данных (случайный, 256 бит) + ├─ слот администратора: ключ из пароля администратора (scrypt) + ├─ слот восстановления: ключ восстановления (печатается при настройке) + ├─ слот оператора А: ключ из PIN А (scrypt) + секрет рабочего места + └─ ... +``` + +- Все данные шифруются одним **ключом данных**. +- Для каждого пользователя ключ данных хранится зашифрованным его личным + ключом — это **слот** в `keys.json`. Личный ключ выводится из пароля или + PIN медленной функцией (`hashlib.scrypt` или `hashlib.pbkdf2_hmac`). +- **Вход = успешная расшифровка своего слота.** Отдельной таблицы хешей + паролей нет: неверный PIN просто не открывает слот. +- **Регистрация = администратор добавляет слот.** Для этого нужен ключ + данных, поэтому заводить пользователей может только вошедший + администратор. +- **Ключ восстановления** печатается при первичной настройке и хранится + у руководства на бумаге. Если потеряны все пароли администраторов и ключ + восстановления, данные не восстановить — это цена шифрования, о ней + предупреждает мастер настройки. + +### PIN и секрет рабочего места + +Оператор входит по имени из списка и PIN: у стойки, в перчатках, пароль +неудобен. Но PIN короткий, и если кто-то скопирует `keys.json`, он может +перебирать PIN у себя сколько угодно. + +Поэтому слот оператора закрыт не только PIN, но и **секретом рабочего +места** — случайным ключом, который администратор кладёт на каждый цеховой +компьютер при его настройке и который хранится там под защитой Windows +(DPAPI, область машины). Копии сетевой папки для перебора недостаточно: +нужен ещё и сам цеховой компьютер. Внутри программы после нескольких неверных +PIN вход блокируется на время. + +Слоты администратора и восстановления секрета рабочего места не требуют: +администратор должен иметь возможность войти с любого компьютера, в том +числе чтобы настроить новый. + +--- + +## Выработка + +### Событие + +Отметка «программа выполнена» записывает событие: + +| Поле | Пример | +|---|---| +| `id` | UUID | +| `type` | `program_done` / `program_undone` | +| `task` | идентификатор задания (см. ниже) | +| `position`, `program` | 7, `PRG_07` | +| `sheets` | 5 | +| `pieces` | `{"555001_zz2": 20, ...}` | +| `operator` | UUID пользователя | +| `workstation` | имя рабочего места | +| `at` | время по часам рабочего места, UTC | + +Снятая отметка — событие `program_undone`, а не удаление прежнего. Итог по +заданию: для каждой программы действует последнее событие по ней. + +### Идентификатор задания + +Одно и то же задание может открываться с разных компьютеров и по разным +путям. Поэтому идентификатор — не путь, а хеш набора программ: имена `.nc` +и их содержимое. Одинаковые файлы на двух машинах дают один идентификатор, +и отметки сходятся. + +### Итоги + +Итоги считаются из событий, а не хранятся отдельно: по оператору, дню, +смене, заданию; листы, программы, позиции, штуки. Так итог всегда +пересчитываем и проверяем, а исправление ошибки — это новое событие, а не +правка цифр. + +### Восстановление отметок + +Из журнала само собой восстанавливается состояние задания на любом +компьютере: сменщик открывает ту же папку и видит отметки предыдущей смены. +Это закрывает исходную проблему пересменки. + +--- + +## Сессии + +Журнал — единственный источник правды об отметках. Сессия — **именованная +закладка**, а не отдельная копия состояния: + +- пользователь вводит имя при сохранении, к нему добавляются дата и время + сохранения; +- сессия хранит: имя, автора, время, идентификатор и путь задания, набор + отметок на момент сохранения; +- открыть сессию — открыть её задание с текущими отметками из журнала; + отметки на момент сохранения показываются для сравнения («с момента + сохранения отмечено ещё 3 программы»). + +Два источника правды не возникают: сессия ничего не отменяет и не +перезаписывает. + +--- + +## Асинхронность + +Tk можно трогать только из главного потока, а сетевой диск может отвечать +секундами. Поэтому: + +- **Весь ввод-вывод — в фоновом потоке** (`ui/worker.py`): чтение и запись + журнала, сессий, индекса. Окно ставит задачу в очередь и получает + результат через `root.after()`. Окно не зависает никогда. +- **Автоподгрузка без нажатий.** Фоновый поток раз в несколько секунд + проверяет подпапки журнала на новые файлы и дочитывает только их. + Уведомления файловой системы по SMB ненадёжны, поэтому опрос. +- **Состояние связи видно.** Нет связи с папкой — значок и число событий в + очереди на отправку; связь вернулась — очередь дописывается сама. + +--- + +## Структура кода + +``` +finnpower_counter/ + core/ разбор .nc/.fms и свод смены — без изменений + storage/ + cipher.py AES-GCM: CNG для Windows, тестовая для Linux + keys.py ключ данных, слоты, секрет рабочего места + journal.py атомарная запись событий, чтение новых + index.py локальный индекс на SQLite + outbox.py очередь на отправку + services/ + roles.py таблица прав + auth.py вход, регистрация, блокировка + production.py события выработки и итоги + sessions.py сессии-закладки + export.py выгрузка в CSV + ui/ + worker.py фоновый исполнитель + login.py окно входа + operator.py окно оператора (нынешний gui.py) + master.py окно мастера + admin.py панель администратора, мастер первичной настройки + config.py настройка рабочего места + cli.py инструмент разработчика, как сейчас +``` + +Зависимости идут в одну сторону: `ui` → `services` → `storage`, `core`. +Сервисы не знают про окно, хранилище не знает про роли. + +--- + +## Версия формата + +В корне папки данных лежит `FORMAT` с номером версии. Утилита старой версии, +увидев более новый формат, отказывается писать и сообщает, что нужно +обновиться. Миграции формата — отдельные шаги с резервной копией перед +запуском. + +--- + +## Риски + +| Риск | Что с ним делать | +|---|---| +| CNG на Windows 7 32-bit ведёт себя не так | Проверка на реальной машине до начала работ, этап 0 | +| Сетевой диск в цеху медленный или рвётся | Фоновый поток, очередь на отправку, значок связи | +| Разные часы на машинах | Идентификаторы событий; время только для показа и группировки | +| Потеряны все пароли и ключ восстановления | Предупреждение в мастере настройки, бумажный ключ, резервные копии | +| Копия `keys.json` для перебора PIN | Секрет рабочего места, медленная функция вывода ключа | +| Два окна утилиты на одной машине | Блокировка на уровне рабочего места, второе окно только читает | diff --git a/docs/ROADMAP.md b/docs/ROADMAP.md index b7cacfc..0fab201 100644 --- a/docs/ROADMAP.md +++ b/docs/ROADMAP.md @@ -1,9 +1,109 @@ # План развития -Какие ещё данные есть в файлах станка, что из них полезно оператору или -мастеру участка и что брать не стоит. +Два направления: + +1. **Система учёта** — вход по ролям, журнал выработки, сессии, просмотр для + мастера. Утилита перестаёт быть узким инструментом одного рабочего места. + Этапы 0–5 ниже, архитектура — в [ARCHITECTURE.md](ARCHITECTURE.md). +2. **Данные из файлов станка** — какие ещё поля полезны оператору или + мастеру участка и что брать не стоит. Этапы A–F. Основано на разборе + выхода постпроцессора NCeXpress FMS. + +Сначала идёт система учёта: этапы A–F добавляют колонки и расчёты, а куда +их показывать и кому, решает она. + +--- + +## Система учёта + +### Что входит -Основано на разборе выхода постпроцессора NCeXpress FMS. +| № | Возможность | Этап | +|---|---|---| +| 1 | Сохранение сессии: имя от пользователя, дата и время сохранения, шифрование | 3 | +| 2 | Зашифрованный журнал выработки: листы, программы, детали, имя оператора | 2 | +| 3 | Оператор представляется при запуске: имя из списка и PIN | 1 | +| 4 | Зашифрованная система входа и регистрации пользователей | 1 | +| 5 | Всё хранится в папке, которую задаёт администратор | 1 | +| 6 | Режимы работы: администратор, оператор, мастер | 1 | +| 7 | Мастер смотрит выработку, сам ничего не пишет | 4 | +| 8 | Выгрузка выработки в CSV | 4 | +| 9 | Сетевой диск: асинхронные чтение и запись, автоподгрузка без нажатий | 0, 2, 4 | + +### Принятые решения + +- **Вход оператора** — имя из списка и PIN. Слот оператора дополнительно + закрыт секретом рабочего места, чтобы копии сетевой папки не хватало для + перебора PIN. +- **Шифрование защищает** от чтения и незаметной подделки посторонним с + доступом к сетевой папке. Подделка выработки «своими» пока вне цели. +- **Сессия — именованная закладка.** Единственный источник правды об + отметках — журнал выработки; сессия его не дублирует. +- **Хранилище — журнал неизменяемых файлов, а не SQLite на сетевом диске.** + Почему — в ARCHITECTURE.md. + +### Этап 0. Фундамент + +Внешне ничего не меняется. + +- Проверить AES-GCM через CNG (`bcrypt.dll`) и DPAPI на цеховой + Windows 7 32-bit со сборкой PyInstaller. **Если не проходит — архитектура + шифрования пересматривается до начала остальных этапов.** +- Разделить `gui.py` на пакет `ui/`: окно оператора отдельно от общего. +- Фоновый исполнитель: весь ввод-вывод вне главного потока, результаты + в окно через `after()`. +- Атомарная запись файлов (временное имя, затем переименование). +- Настройка рабочего места: путь к папке данных, имя рабочего места. + +### Этап 1. Пользователи и хранилище + +- Мастер первичной настройки: администратор выбирает папку данных, создаёт + свою учётную запись, получает ключ восстановления на печать. +- Ключ данных и слоты пользователей; секрет рабочего места. +- Окно входа: имя из списка и PIN для оператора и мастера, пароль для + администратора. Блокировка после нескольких неверных попыток. +- Роли и таблица прав; администратор заводит и блокирует пользователей. +- Каркас экранов по ролям. Окно оператора — нынешнее, с именем вошедшего. +- Надпись «только чтение» меняется: файлы программ не изменяются, учёт + ведётся в папке данных. + +### Этап 2. Журнал выработки + +- Каждая отметка и снятие отметки пишут событие в журнал. +- Идентификатор задания по содержимому программ: одно задание на разных + машинах сходится. +- Отметки восстанавливаются из журнала при открытии задания — сменщик видит, + что сделано до него. +- Очередь на отправку при обрыве сети, значок состояния связи. +- Локальный индекс для быстрого чтения. + +### Этап 3. Сессии + +- «Сохранить сессию»: имя вводит пользователь, к нему добавляются дата и + время сохранения. +- Список сессий: открыть и продолжить; сравнение с моментом сохранения. + +### Этап 4. Режим мастера + +- Выработка по операторам, дням, сменам и заданиям: листы, программы, + позиции, штуки. +- Автоподгрузка: новые события появляются без нажатий. +- Выгрузка в CSV (`;`, UTF-8 с BOM — как нынешняя выгрузка позиций). + +### Этап 5. Администрирование + +- Сброс PIN, блокировка и разблокировка пользователя. +- Смена ключа данных, перенос папки данных. +- Резервная копия, версия формата и миграции. + +--- + +## Данные из файлов станка + +Какие ещё данные есть в файлах станка, что из них полезно оператору или +мастеру участка и что брать не стоит. Разделы ниже — «Что уже используется», +«Предложения», «Что брать не стоит», «Порядок работ» — относятся к этому +направлению. --- @@ -136,8 +236,10 @@ **Удары, пройденное расстояние, смены инструмента.** Это про износ пуансонов и машинное время. Чтобы это было полезно, нужен накопительный счётчик по -инструменту между сменами, то есть хранимая история. Утилита истории не ведёт -и не должна: она читает папку и ничего не записывает. +инструменту между сменами, то есть хранимая история. Раньше утилита истории +не вела; с журналом выработки (этап 2) она появится, и к этому пункту можно +будет вернуться — но только если на участке реально ведут учёт износа +инструмента. **Наладочные параметры станка** — прижимы, ход, ускорение, скорость, разгрузка, адреса. Оператор видит их на стойке при наладке. @@ -153,6 +255,8 @@ ## Порядок работ +Сначала этапы 0–5 системы учёта, затем этапы данных из файлов станка. + **Этап A.** Материал и толщина, расход листов, габариты детали в окне позиции. Данные уже разбираются рядом. @@ -168,7 +272,8 @@ **Этап E.** Формовка и гибка, заказ и заказчик — по правилу «показывать, если заполнено». -**Этап F.** Автоматическое определение выполненных программ. +**Этап F.** Автоматическое определение выполненных программ. С журналом +выработки это ещё один источник событий рядом с ручными отметками. --- From 1f189fbedca3a077f2982cf32ef2cab7bc0fe8bf Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 29 Sep 2026 05:40:36 +0000 Subject: [PATCH 2/3] Plan the tracking system as client and server; English-first docs The planned tracking system moves from a shared network folder to a server with a database, with the utility as the operator's client and a web page for the supervisor. Nothing is implemented yet: this commit only records the plan. - docs/ARCHITECTURE.md: client-server design and the reasons for it (why not a shared folder, FastAPI + SQLite, SSE for live updates, TLS with a pinned self-signed certificate, PIN sign-in only from registered workstations, offline outbox, versioned API). - docs/ROADMAP.md: stages 0-6 of the tracking system, then machine-data stages A-F. - docs/TASKS.md: task breakdown with scope, acceptance criteria, dependencies and size; maintenance tasks from the audit; open questions. - docs/FINDINGS.md: audit results (fixed and open) and planning findings. - README: short roadmap in English and Russian. The repository is English-first from now on: English documents are the primary ones, Russian versions move to *.ru.md as translations. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01CW3WfPRekjK4x6M8VNRckz --- README.md | 33 ++- README.ru.md | 30 ++- docs/ARCHITECTURE.md | 506 +++++++++++++++++++-------------------- docs/ARCHITECTURE.ru.md | 315 ++++++++++++++++++++++++ docs/FINDINGS.md | 63 +++++ docs/ROADMAP.md | 438 ++++++++++++++++----------------- docs/ROADMAP.ru.md | 305 +++++++++++++++++++++++ docs/TASKS.md | 519 ++++++++++++++++++++++++++++++++++++++++ 8 files changed, 1729 insertions(+), 480 deletions(-) create mode 100644 docs/ARCHITECTURE.ru.md create mode 100644 docs/FINDINGS.md create mode 100644 docs/ROADMAP.ru.md create mode 100644 docs/TASKS.md diff --git a/README.md b/README.md index 620ed1d..1e62628 100644 --- a/README.md +++ b/README.md @@ -256,11 +256,36 @@ locally; the path is given by `FINNPOWER_DATASET`. --- -## Further work +## Roadmap -An analysis of what else the machine files contain, what is actually useful -to an operator or a shift supervisor and what is not worth taking, is in -[docs/ROADMAP.md](docs/ROADMAP.md) (in Russian). +The utility is growing from a single-workstation tool into a small production +tracking system: a server with a database, the utility as the operator's +client, and a web page for the shift supervisor. Nothing below is implemented +yet. + +| Stage | What it brings | +|---|---| +| 0 | Windows 7 compatibility checks, client groundwork — no visible change | +| 1 | Server: users and roles, workstations, admin web panel, Docker deployment | +| 2 | Client connects: workstation registration, operator sign-in with name and PIN, offline sign-in | +| 3 | Production log: every mark recorded on the server, outbox when offline, live updates across workstations | +| 4 | Supervisor web page: production per operator, day, shift and task; CSV export | +| 5 | Saved sessions: named bookmarks with date and time | +| 6 | Operations: backups, installation guide, update notices | +| A–F | More data from the machine files: material, time, weight, order, automatic detection of executed programs | + +Standalone mode stays: without a server the utility works exactly as it does +now, and program files remain read-only. + +Details: + +- [docs/ROADMAP.md](docs/ROADMAP.md) — stages and decisions +- [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md) — client–server design and why +- [docs/TASKS.md](docs/TASKS.md) — task breakdown with acceptance criteria +- [docs/FINDINGS.md](docs/FINDINGS.md) — audit results and planning findings + +The repository is maintained in English; Russian versions (`*.ru.md`) are +translations and may lag behind. --- diff --git a/README.ru.md b/README.ru.md index 34015ae..5163cd0 100644 --- a/README.ru.md +++ b/README.ru.md @@ -263,9 +263,33 @@ python -m pytest tests -q ## Дальнейшее развитие -Разбор того, какие ещё данные есть в файлах станка, что из них полезно -оператору и мастеру участка, а что брать не стоит — в отдельном документе: -[docs/ROADMAP.md](docs/ROADMAP.md). +Утилита вырастает из инструмента одного рабочего места в небольшую систему +учёта: сервер с базой данных, утилита как клиент оператора и веб-страница +для мастера участка. Ничего из перечисленного ещё не реализовано. + +| Этап | Что появляется | +|---|---| +| 0 | Проверки на Windows 7, подготовка клиента — внешне без изменений | +| 1 | Сервер: пользователи и роли, рабочие места, веб-панель администратора, установка в Docker | +| 2 | Клиент подключается: регистрация рабочего места, вход оператора по имени и PIN, вход без связи | +| 3 | Журнал выработки: каждая отметка на сервере, очередь при обрыве связи, изменения сразу видны на всех местах | +| 4 | Веб-страница мастера: выработка по операторам, дням, сменам и заданиям; выгрузка в CSV | +| 5 | Сессии: именованные закладки с датой и временем | +| 6 | Эксплуатация: резервные копии, инструкция по установке, уведомления об обновлениях | +| A–F | Больше данных из файлов станка: материал, время, масса, заказ, автоматический учёт выполненных программ | + +Автономный режим остаётся: без сервера утилита работает так же, как сейчас, +а файлы программ по-прежнему только читаются. + +Подробности: + +- [docs/ROADMAP.ru.md](docs/ROADMAP.ru.md) — этапы и решения +- [docs/ARCHITECTURE.ru.md](docs/ARCHITECTURE.ru.md) — устройство клиента и сервера и почему так +- [docs/TASKS.md](docs/TASKS.md) — задачи с критериями готовности (на английском) +- [docs/FINDINGS.md](docs/FINDINGS.md) — результаты аудита и находки при планировании (на английском) + +Репозиторий ведётся на английском; русские версии (`*.ru.md`) — переводы и +могут отставать от основных. --- diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index 557cdcc..3b55745 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -1,331 +1,321 @@ -# Архитектура: от утилиты к системе учёта +# Architecture: client and server -Утилита выросла из одной задачи — показать оператору, какие позиции -сменного задания закрыты. Следующий шаг превращает её в многопользовательскую -систему: вход по ролям, журнал выработки, сессии, просмотр для мастера. -Этот документ фиксирует решения, на которых всё это строится, и причины, -по которым выбраны именно они. +**English** · [Русский](ARCHITECTURE.ru.md) -Порядок внедрения — в [ROADMAP.md](ROADMAP.md). +The utility grew out of one job: show the operator which positions of a +shift task are complete. The next step turns it into a production tracking +system — role-based sign-in, a production log, saved sessions, a live view +for the shift supervisor. All of that is built around a **server**, and the +utility becomes the **client** at the operator's workstation. ---- - -## Что остаётся неизменным - -- **Целевая среда.** Windows 7 32-bit, Python 3.8.10, один `.exe` через - PyInstaller, без интернета. -- **Без внешних зависимостей в рантайме.** Всё, что нужно, берётся из - стандартной библиотеки или из самой Windows. -- **Файлы программ только читаются.** В `.nc`, `.fms` и карты наладки - утилита не пишет, к стойке станка не обращается. -- **Ядро (`core/`) не знает ни про окно, ни про хранилище.** Разбор и свод - смены остаются чистыми функциями и проверяются тестами без окружения. - -## Что меняется в принципах +This document records the decisions and the reasons behind them. The order +of work is in [ROADMAP.md](ROADMAP.md), the task breakdown in +[TASKS.md](TASKS.md). -Утилита начинает **вести историю**. Раньше она читала папку и ничего не -записывала, и часть решений в ROADMAP опиралась на это («утилита истории не -ведёт и не должна»). Теперь запись есть, но строго ограничена: только в -папку данных, которую задал администратор, и только в зашифрованном виде. - -Надпись «только чтение» в окне и README меняется на этапе 1: «файлы программ -не изменяются, станок не затрагивается; учёт ведётся в папке данных». +Status: **planned, not implemented.** --- -## Роли +## Overview -| Возможность | Оператор | Мастер | Администратор | -|---|:-:|:-:|:-:| -| Открыть сменное задание, отмечать программы | ✓ | | | -| Сохранять и открывать сессии | ✓ | | | -| Выработка пишется от его имени | ✓ | | | -| Смотреть выработку всех операторов | | ✓ | ✓ | -| Выгружать выработку в CSV | | ✓ | ✓ | -| Задать папку данных, настроить рабочее место | | | ✓ | -| Заводить, блокировать пользователей, сбрасывать PIN | | | ✓ | +``` + Shop-floor PC (Windows 7) Server (Linux / Docker, or Windows) +┌─────────────────────────────┐ ┌───────────────────────────────────┐ +│ Operator client (.exe) │ HTTPS │ API │ +│ core/ — .nc/.fms parsing │ ──────► │ sign-in, roles, production, sess.│ +│ marks, PIN sign-in │ ◄────── │ Event stream (SSE) │ +│ outbox │ SSE │ Database │ +│ local cache (DPAPI) │ │ Supervisor and admin web pages │ +└─────────────────────────────┘ └───────────────────────────────────┘ + ▲ + Supervisor's and ─────┘ HTTPS + admin's browser +``` -Права описаны одной таблицей в коде (`services/roles.py`), экраны и сервисы -спрашивают её, а не проверяют роль каждый по-своему. +| Who | Uses | Does | +|---|---|---| +| Operator | Client on the shop-floor PC | Opens a shift task, marks programs, saves sessions | +| Supervisor | Server web page in a browser | Watches production live, exports CSV | +| Administrator | Server web page | Users, workstations, backups | -**Оговорка.** Оператор и мастер расшифровывают данные одним ключом данных -(см. ниже). Разграничение «мастер не пишет выработку, оператор не видит -чужую» обеспечивает программа, а не криптография. Для цеха этого -достаточно; криптографическое разделение ролей — возможное расширение. +Only the operator runs the client: they need the folder with `.nc` files on +the shop-floor PC, and a browser cannot read it. The supervisor and the +administrator install nothing. --- -## Хранилище +## Why a server rather than a shared network folder -### Почему не SQLite на сетевом диске +The first draft of this plan kept the data on a shared network drive with +no server: a log of encrypted files, keys in a file, folder polling. It +would work, but nearly all of its complexity comes from having no central +node: -Предполагается, что данные лежат на сетевом диске и пишут в них несколько -рабочих мест. SQLite в своей документации прямо предупреждает: блокировки -файлов на сетевых файловых системах ненадёжны, и одновременная запись с -разных машин приводит к порче базы. Второй довод — в стандартной библиотеке -нет SQLite с шифрованием. +| Concern | Shared folder | Server | +|---|---|---| +| Concurrent writes from several PCs | A log of files — SQLite on a network drive gets corrupted | A real database with transactions | +| Seeing other clients' changes | Poll the folder every few seconds | The server pushes events immediately | +| Encryption | Hand-rolled on Windows CNG via ctypes — the main Win7 risk | TLS from the standard library | +| Brute-forcing a PIN from a copied key file | Mitigated by a workstation secret | Nothing to brute-force: the server checks the PIN and limits attempts | +| Role permissions | Enforced by the client only | Enforced by the server | +| Recording production under someone else's name | Not prevented | The server sets the author from the sign-in session | +| Clock drift between PCs | Event IDs; timestamps unreliable | The server sets the time | -### Журнал событий +The cost: a machine for the server and someone to look after it, two +programs instead of one, and a contract between them (the API). For a shop +with several workstations and a supervisor who wants live data it is worth +it. -Вместо общей базы — журнал из неизменяемых файлов: +The shared folder stays as a **fallback** if a server turns out to be +impossible: the client's storage layer sits behind an interface, and the +second option can be plugged in without touching the window or the core. -- **Одно событие — один файл.** Файл пишется под временным именем и - переименовывается, когда записан целиком. Наполовину записанных файлов - читатели не видят. -- **Каждое рабочее место пишет только в свою подпапку.** Два компьютера - никогда не пишут в один файл, поэтому конфликтов записи нет в принципе. -- **Файлы только добавляются.** Исправление — это новое событие, которое - отменяет прежнее, а не правка старого файла. -- **У события есть идентификатор** (случайный UUID). Часы на машинах могут - расходиться, поэтому ни дедупликация, ни порядок не опираются только на - время. +--- -### Локальный индекс +## What stays the same + +- **Client: Windows 7 32-bit, Python 3.8.10, a single `.exe`, no external + dependencies.** HTTPS and JSON are in the standard library (`http.client`, + `ssl`, `json`). +- **Program files are read-only.** The client never writes to `.nc`, `.fms` + or setup sheets and never talks to the machine control. +- **The core (`core/`) does not change** and knows nothing about windows or + networks. +- **Standalone mode.** Without a configured server the client works as it + does today: locally, no sign-in, no tracking. The utility's value must not + depend on infrastructure. + +## What changes + +- **The client needs the network.** `app.spec` currently excludes `ssl`, + `http`, `socket`, `email` and `urllib` — the utility was standalone. They + have to come back and the `.exe` grows a little. +- **History is kept.** The utility used to keep none. Now it does, on the + server rather than on the shop-floor PC. +- The "read-only" notice becomes "program files are never modified, the + machine is not touched". -Читать сотни файлов с сетевого диска при каждом открытии медленно. У каждого -компьютера есть локальный индекс — обычная SQLite, но на **своём** диске -(`%LOCALAPPDATA%`), а не на сетевом. Индекс дочитывает только новые файлы -журнала и хранит уже расшифрованные итоги. Потерять индекс не страшно: он -пересобирается из журнала. +--- -### Очередь на отправку +## Server -Если сетевой диск недоступен, события пишутся в локальную очередь -(`%LOCALAPPDATA%\FinnPowerCounter\outbox`) и дописываются в журнал, когда -связь вернётся. Оператор при обрыве сети не теряет отметок; в окне виден -значок «нет связи с папкой данных, N событий ждут отправки». +### Platform -### Раскладка папки данных +The server is not bound by the shop-floor constraints: modern Python, +ordinary dependencies. -``` -<папка данных>/ - FORMAT версия формата хранилища, открытым текстом - keys.json ключевые слоты пользователей (см. «Шифрование») - users.enc список пользователей и ролей - journal/ - <рабочее место>/ - 2026-09/ - 20260929T142305Z-.evt - sessions/ - / - 2026-09-29_14-30-05-.session -``` +- **Primary platform: Linux in Docker.** One command brings the server up + on a test VM and on the shop floor. The reference deployment is + `docker compose`. +- **Windows is supported too.** The code uses nothing Linux-only, and CI + runs the server tests on Windows as well. Running as a Windows service is + documented when it is needed. -Имена файлов не содержат ничего, что стоило бы шифровать: только время и -случайные идентификаторы. Имя сессии, которое вводит пользователь, имя -оператора и содержимое лежат внутри зашифрованного файла. +### Technology -### Локальная настройка рабочего места +| What | Choice | Why | +|---|---|---| +| Web framework | FastAPI + Uvicorn | Async, generates the API description | +| Database | SQLite in WAL mode on the server's local disk | Tens of events per shift; plenty of headroom, backup is a file copy | +| Data access | SQLAlchemy Core + Alembic | Schema migrations; moving to PostgreSQL is a connection-string change | +| Password and PIN hashes | `hashlib.scrypt` | Standard library, slow by design | +| Web pages | Jinja2 templates + a little JavaScript | No heavy front end: tables, filters, CSV | +| Live updates | Server-Sent Events | Plain HTTP: `EventSource` in the browser, `http.client` in the client | -Путь к папке данных нужен до входа, поэтому он хранится на самом компьютере: -`%PROGRAMDATA%\FinnPowerCounter\workstation.ini` — путь к папке данных, имя -рабочего места и секрет рабочего места (см. ниже). Пишет его только мастер -первичной настройки под администратором. +**Why SSE, not WebSocket.** The stream goes one way — from the server to the +clients; the client sends marks as ordinary requests. SSE is an ordinary HTTP +response that never ends: both a browser and a Python 3.8 client can read it +without third-party libraries. WebSocket is not in the standard library. ---- - -## Шифрование +**Caching.** A separate server-side cache (Redis and the like) is not needed: +the volumes are small and the database answers faster than the network to +the shop floor. Caching belongs on the client, for working offline (below). -### Цели +### Data -Посторонний с доступом к сетевой папке не должен: +| Table | Holds | +|---|---| +| `users` | Name, role, PIN or password hash, blocked flag | +| `workstations` | Registered shop-floor PCs and their keys | +| `auth_sessions` | Sign-in sessions: who, from which workstation, until when | +| `tasks` | Shift tasks: content-based ID, program names | +| `events` | Production log: marks and unmarks | +| `bookmarks` | Users' saved sessions | +| `audit` | Sign-ins, failed attempts, administrator actions | + +Where the database and backups live is set by the administrator in the +server configuration (a Docker volume or a folder on the server's disk). + +### Encryption + +- **In transit — TLS.** The server speaks HTTPS only. +- **At rest — by the OS**: disk or volume encryption on the server (LUKS, + BitLocker). Encrypting individual database fields buys little: the server + has to decrypt them anyway, and key management gets harder. +- **Certificate.** Shop networks rarely have their own certificate + authority, so the server generates a self-signed certificate and the + client pins its fingerprint when the workstation is registered, trusting + nothing else afterwards. The server cannot be impersonated on the network. -1. **прочитать** данные — кто, когда, что и сколько сделал; -2. **незаметно изменить** выработку — подделать или исправить событие. +--- -Защита от подделки «своими» (оператор пишет выработку от чужого имени) в -цели не входит: для неё нужна личная подпись каждого события. Это возможное -расширение, архитектура его не исключает. +## Sign-in and workstations -### Шифр +- **Workstation registration.** The administrator creates a workstation in + the web panel and gets a one-time code. The code is entered in the client + once; the client receives a workstation key and the certificate + fingerprint and stores them protected by Windows (DPAPI). +- **Operator sign-in: name from a list plus PIN.** The server accepts a PIN + only from a registered workstation and locks sign-in after several wrong + attempts. A stolen PIN is useless without a shop-floor PC. +- **Supervisor and administrator sign-in**: user name and password in the + browser. +- **Roles are enforced by the server.** The client only hides what is not + available; permission comes from the server. -Стандартная библиотека Python шифров не содержит. Библиотека `cryptography` -в современных версиях собирается на Rust, а Rust больше не поддерживает -Windows 7; старые версии без Rust давно не обновляются, и тащить их ради -шифрования плохо. +### Offline sign-in -Решение: **AES-256-GCM средствами самой Windows** (CNG, `bcrypt.dll`) через -`ctypes`. Это шифрование ОС: без зависимостей, обновляется вместе с -системой, есть в Windows 7. GCM одновременно шифрует и проверяет -целостность, то есть закрывает обе цели. +If the server is unreachable at start-up, an operator can sign in if they +have signed in on this PC before: the client keeps a PIN verifier from the +last successful sign-in, protected by DPAPI. Marks made this way are flagged +"offline sign-in" and accepted by the server once the connection is back, +on the strength of the workstation key. -Шифр спрятан за интерфейсом `storage/cipher.py` с двумя реализациями: -CNG для Windows и тестовая на `cryptography` для Linux и CI (только для -тестов, в сборку не попадает). Тесты проверяют, что обе дают одинаковый -результат на эталонных векторах. +--- -**Проверка на реальной машине — первое, что делается на этапе 0.** Если CNG -на цеховой Windows 7 32-bit поведёт себя не так, как ожидается, этот раздел -пересматривается до начала остальной работы. +## Production -### Ключи +### Event -``` -ключ данных (случайный, 256 бит) - ├─ слот администратора: ключ из пароля администратора (scrypt) - ├─ слот восстановления: ключ восстановления (печатается при настройке) - ├─ слот оператора А: ключ из PIN А (scrypt) + секрет рабочего места - └─ ... -``` +Marking a program as done is an event: -- Все данные шифруются одним **ключом данных**. -- Для каждого пользователя ключ данных хранится зашифрованным его личным - ключом — это **слот** в `keys.json`. Личный ключ выводится из пароля или - PIN медленной функцией (`hashlib.scrypt` или `hashlib.pbkdf2_hmac`). -- **Вход = успешная расшифровка своего слота.** Отдельной таблицы хешей - паролей нет: неверный PIN просто не открывает слот. -- **Регистрация = администратор добавляет слот.** Для этого нужен ключ - данных, поэтому заводить пользователей может только вошедший - администратор. -- **Ключ восстановления** печатается при первичной настройке и хранится - у руководства на бумаге. Если потеряны все пароли администраторов и ключ - восстановления, данные не восстановить — это цена шифрования, о ней - предупреждает мастер настройки. - -### PIN и секрет рабочего места - -Оператор входит по имени из списка и PIN: у стойки, в перчатках, пароль -неудобен. Но PIN короткий, и если кто-то скопирует `keys.json`, он может -перебирать PIN у себя сколько угодно. - -Поэтому слот оператора закрыт не только PIN, но и **секретом рабочего -места** — случайным ключом, который администратор кладёт на каждый цеховой -компьютер при его настройке и который хранится там под защитой Windows -(DPAPI, область машины). Копии сетевой папки для перебора недостаточно: -нужен ещё и сам цеховой компьютер. Внутри программы после нескольких неверных -PIN вход блокируется на время. - -Слоты администратора и восстановления секрета рабочего места не требуют: -администратор должен иметь возможность войти с любого компьютера, в том -числе чтобы настроить новый. +| Field | Set by | Example | +|---|---|---| +| `id` | client | UUID — safe resending without duplicates | +| `type` | client | `program_done` / `program_undone` | +| `task` | client | task ID (below) | +| `position`, `program` | client | 7, `PRG_07` | +| `sheets`, `pieces` | client | 5, `{"555001_zz2": 20}` | +| `occurred_at` | client | when the button was pressed, by the PC clock | +| `operator`, `workstation` | **server** | from the sign-in session | +| `received_at` | **server** | by the server clock | ---- +The server sets the author and the receive time: a client cannot record +production under someone else's name. Resending an event with the same `id` +changes nothing. -## Выработка +Unmarking is a `program_undone` event, not a deletion. For each program of a +task the last event in the server's receive order wins. -### Событие +### Task ID -Отметка «программа выполнена» записывает событие: +The same task is opened from different PCs via different paths. So the ID is +a hash of the program set — `.nc` names and contents. The client does the +parsing with the same `core/`; the server receives ready sheet and piece +counts. -| Поле | Пример | -|---|---| -| `id` | UUID | -| `type` | `program_done` / `program_undone` | -| `task` | идентификатор задания (см. ниже) | -| `position`, `program` | 7, `PRG_07` | -| `sheets` | 5 | -| `pieces` | `{"555001_zz2": 20, ...}` | -| `operator` | UUID пользователя | -| `workstation` | имя рабочего места | -| `at` | время по часам рабочего места, UTC | +### Totals and recovery -Снятая отметка — событие `program_undone`, а не удаление прежнего. Итог по -заданию: для каждой программы действует последнее событие по ней. +- Totals are computed from events: per operator, day, shift, task; sheets, + programs, positions, pieces. A correction is a new event, not an edit. +- The next shift opens the same task and sees the previous shift's marks — + from any workstation. That closes the original shift-handover problem. +- The supervisor sees each mark immediately: the server broadcasts the event + to every subscribed client and page. -### Идентификатор задания +--- -Одно и то же задание может открываться с разных компьютеров и по разным -путям. Поэтому идентификатор — не путь, а хеш набора программ: имена `.nc` -и их содержимое. Одинаковые файлы на двух машинах дают один идентификатор, -и отметки сходятся. +## Sessions -### Итоги +The log is the single source of truth about marks. A session is a **named +bookmark** on the server: -Итоги считаются из событий, а не хранятся отдельно: по оператору, дню, -смене, заданию; листы, программы, позиции, штуки. Так итог всегда -пересчитываем и проверяем, а исправление ошибки — это новое событие, а не -правка цифр. +- the user types a name; the save date and time are added to it; +- the session stores its author, the task (ID and path) and the marks at the + moment of saving; +- opening a session opens its task with the current marks; the marks at + save time are shown for comparison. -### Восстановление отметок +--- -Из журнала само собой восстанавливается состояние задания на любом -компьютере: сменщик открывает ту же папку и видит отметки предыдущей смены. -Это закрывает исходную проблему пересменки. +## Client ---- +### Asynchrony -## Сессии +Tk may only be touched from the main thread, and the network may take +seconds to answer. -Журнал — единственный источник правды об отметках. Сессия — **именованная -закладка**, а не отдельная копия состояния: +- **All server traffic runs on a background thread** (`client/worker.py`). + The window queues a job and gets the result through `root.after()`. The + window never freezes. +- **The event stream** has its own background thread: it reads SSE and hands + changes to the window. On a drop it reconnects with a back-off. +- **Connection state is visible** in the window: "connected", + "offline, N marks waiting to be sent". -- пользователь вводит имя при сохранении, к нему добавляются дата и время - сохранения; -- сессия хранит: имя, автора, время, идентификатор и путь задания, набор - отметок на момент сохранения; -- открыть сессию — открыть её задание с текущими отметками из журнала; - отметки на момент сохранения показываются для сравнения («с момента - сохранения отмечено ещё 3 программы»). +### Working offline -Два источника правды не возникают: сессия ничего не отменяет и не -перезаписывает. +- Marks go to a **local outbox** and are sent when the connection returns. + Resending is safe thanks to the event `id`. +- A **local cache** holds the last known task state and the operator list for + sign-in. The outbox and the cache are encrypted with DPAPI: copied off the + shop-floor PC, they cannot be read elsewhere. +- If two operators mark the same thing while offline, the server's receive + order wins and the supervisor sees such marks flagged. --- -## Асинхронность +## Client–server contract -Tk можно трогать только из главного потока, а сетевой диск может отвечать -секундами. Поэтому: - -- **Весь ввод-вывод — в фоновом потоке** (`ui/worker.py`): чтение и запись - журнала, сессий, индекса. Окно ставит задачу в очередь и получает - результат через `root.after()`. Окно не зависает никогда. -- **Автоподгрузка без нажатий.** Фоновый поток раз в несколько секунд - проверяет подпапки журнала на новые файлы и дочитывает только их. - Уведомления файловой системы по SMB ненадёжны, поэтому опрос. -- **Состояние связи видно.** Нет связи с папкой — значок и число событий в - очереди на отправку; связь вернулась — очередь дописывается сама. +- **The API is versioned**: `/api/v1/...`. Shop-floor clients are not updated + all at once, so the server keeps the previous API version while it is in + use. +- On connect the client reports its version and the server its minimum + supported one. A client that is too old gets a clear "please update" + message, not an obscure error. +- The server generates the API description (OpenAPI), and it is kept in the + repository so that changes show up in pull requests. --- -## Структура кода +## Repository layout ``` -finnpower_counter/ - core/ разбор .nc/.fms и свод смены — без изменений - storage/ - cipher.py AES-GCM: CNG для Windows, тестовая для Linux - keys.py ключ данных, слоты, секрет рабочего места - journal.py атомарная запись событий, чтение новых - index.py локальный индекс на SQLite - outbox.py очередь на отправку - services/ - roles.py таблица прав - auth.py вход, регистрация, блокировка - production.py события выработки и итоги - sessions.py сессии-закладки - export.py выгрузка в CSV - ui/ - worker.py фоновый исполнитель - login.py окно входа - operator.py окно оператора (нынешний gui.py) - master.py окно мастера - admin.py панель администратора, мастер первичной настройки - config.py настройка рабочего места - cli.py инструмент разработчика, как сейчас +finnpower_counter/ client + core/ .nc/.fms parsing and shift summary — unchanged + client/ + api.py server requests (http.client + ssl) + events.py SSE stream reader + outbox.py outgoing queue + cache.py local cache + dpapi.py local file protection via Windows + worker.py background executor + ui/ windows: sign-in, operator (today's gui.py), setup + cli.py developer tool +server/ server — separate package with its own dependencies + fpc_server/ + api/ API routes + web/ supervisor and admin pages + db/ schema, migrations + services/ sign-in, roles, production, sessions, export + tests/ + Dockerfile + docker-compose.yml +docs/ ``` -Зависимости идут в одну сторону: `ui` → `services` → `storage`, `core`. -Сервисы не знают про окно, хранилище не знает про роли. - ---- - -## Версия формата - -В корне папки данных лежит `FORMAT` с номером версии. Утилита старой версии, -увидев более новый формат, отказывается писать и сообщает, что нужно -обновиться. Миграции формата — отдельные шаги с резервной копией перед -запуском. +Client and server live in one repository: the contract between them changes +in a single pull request, and the tests check both sides at once. --- -## Риски +## Risks -| Риск | Что с ним делать | +| Risk | Mitigation | |---|---| -| CNG на Windows 7 32-bit ведёт себя не так | Проверка на реальной машине до начала работ, этап 0 | -| Сетевой диск в цеху медленный или рвётся | Фоновый поток, очередь на отправку, значок связи | -| Разные часы на машинах | Идентификаторы событий; время только для показа и группировки | -| Потеряны все пароли и ключ восстановления | Предупреждение в мастере настройки, бумажный ключ, резервные копии | -| Копия `keys.json` для перебора PIN | Секрет рабочего места, медленная функция вывода ключа | -| Два окна утилиты на одной машине | Блокировка на уровне рабочего места, второе окно только читает | +| Python 3.8 on Windows 7 cannot negotiate TLS with the server | Check on a real machine first, stage 0 | +| DPAPI via ctypes misbehaves on Win7 | Same check, stage 0 | +| The server is unreachable | Offline work, outbox, standalone mode | +| Losing the server database | Scheduled backups, restore drills | +| Mixed client versions on the floor | API versioning, minimum client version | +| Nobody to look after the server | One-command Docker deployment, instructions for IT | diff --git a/docs/ARCHITECTURE.ru.md b/docs/ARCHITECTURE.ru.md new file mode 100644 index 0000000..87b0ff7 --- /dev/null +++ b/docs/ARCHITECTURE.ru.md @@ -0,0 +1,315 @@ +# Архитектура: клиент и сервер + +[English](ARCHITECTURE.md) · **Русский** + +Утилита выросла из одной задачи — показать оператору, какие позиции +сменного задания закрыты. Следующий шаг превращает её в систему учёта: +вход по ролям, журнал выработки, сессии, просмотр для мастера. Всё это +строится вокруг **сервера**, а утилита становится **клиентом** на рабочем +месте оператора. + +Этот документ фиксирует решения и причины, по которым выбраны именно они. +Порядок внедрения — в [ROADMAP.ru.md](ROADMAP.ru.md), разбивка на задачи — +в [TASKS.md](TASKS.md) (на английском). + +Состояние: **запланировано, не реализовано.** + +--- + +## Общая картина + +``` + Цеховой компьютер (Windows 7) Сервер (Linux / Docker, либо Windows) +┌─────────────────────────────┐ ┌───────────────────────────────────┐ +│ Клиент оператора (.exe) │ HTTPS │ API │ +│ core/ — разбор .nc/.fms │ ──────► │ вход, права, выработка, сессии │ +│ отметки, вход по PIN │ ◄────── │ Поток событий (SSE) │ +│ очередь на отправку │ SSE │ База данных │ +│ локальный кэш (DPAPI) │ │ Веб-страницы мастера и админа │ +└─────────────────────────────┘ └───────────────────────────────────┘ + ▲ + Браузер мастера ──────┘ HTTPS + и администратора +``` + +| Кто | Чем пользуется | Что делает | +|---|---|---| +| Оператор | Клиент на цеховом компьютере | Открывает задание, отмечает программы, сохраняет сессии | +| Мастер | Веб-страница сервера в браузере | Смотрит выработку в реальном времени, выгружает CSV | +| Администратор | Веб-страница сервера | Пользователи, рабочие места, резервные копии | + +Клиент остаётся только у оператора: ему нужен доступ к папке с `.nc` на +цеховом компьютере, а браузер его не даёт. Мастеру и администратору ничего +устанавливать не нужно. + +--- + +## Почему сервер, а не общая сетевая папка + +Первый вариант плана держал данные на общем сетевом диске без сервера: +журнал из зашифрованных файлов, ключи в файле, опрос папки. Он работал бы, +но почти вся его сложность — следствие отсутствия центрального узла: + +| Задача | Сетевая папка | Сервер | +|---|---|---| +| Одновременная запись с нескольких машин | Журнал из файлов: SQLite на сетевом диске портится | Настоящая база с транзакциями | +| Изменения у других клиентов | Опрос папки раз в несколько секунд | Сервер сам присылает события сразу | +| Шифрование | Своё, на CNG через ctypes — главный риск на Win7 | TLS из стандартной библиотеки | +| Перебор PIN по копии файла ключей | Защита секретом рабочего места | Перебирать нечего: PIN проверяет сервер и ограничивает попытки | +| Права ролей | Только на стороне программы | Проверяет сервер | +| Выработка от чужого имени | Не защищено | Сервер сам ставит автора по сеансу входа | +| Расхождение часов на машинах | Идентификаторы событий, время ненадёжно | Время ставит сервер | + +Цена: нужна машина под сервер и её обслуживание, две программы вместо +одной, договорённость между ними (API). Для цеха с несколькими рабочими +местами и мастером, которому нужны живые данные, это оправдано. + +Сетевая папка остаётся **запасным путём**, если сервер окажется +невозможен: слой хранения в клиенте спрятан за интерфейсом, и второй +вариант можно подключить, не трогая окно и ядро. + +--- + +## Что остаётся неизменным + +- **Клиент — Windows 7 32-bit, Python 3.8.10, один `.exe`, без внешних + зависимостей.** HTTPS и разбор JSON есть в стандартной библиотеке + (`http.client`, `ssl`, `json`). +- **Файлы программ только читаются.** В `.nc`, `.fms` и карты наладки клиент + не пишет, к стойке станка не обращается. +- **Ядро (`core/`) не меняется** и не знает ни про окно, ни про сеть. +- **Автономный режим.** Если сервер не настроен, клиент работает как + сейчас: локально, без входа и без учёта. Ценность утилиты не должна + зависеть от инфраструктуры. + +## Что меняется + +- **Клиенту нужна сеть.** В `app.spec` сейчас исключены `ssl`, `http`, + `socket`, `email`, `urllib` — утилита была автономной. Их придётся вернуть, + `.exe` немного вырастет. +- **Учёт ведётся.** Раньше утилита истории не вела. Теперь история есть, но + на сервере, а не на цеховом компьютере. +- Надпись «только чтение» меняется на «файлы программ не изменяются, станок + не затрагивается». + +--- + +## Сервер + +### Платформа + +Сервер не связан ограничениями цеховых компьютеров: современный Python, +обычные зависимости. + +- **Основная платформа — Linux в Docker.** Одна команда поднимает сервер + на виртуалке для тестов и в цеху. Эталонная поставка — `docker compose`. +- **Windows тоже поддерживается.** Код не использует ничего, что есть только + в Linux, CI гоняет тесты сервера и на Windows. Запуск как службы Windows + описывается отдельно, когда до него дойдёт. + +### Технологии + +| Что | Выбор | Почему | +|---|---|---| +| Веб-фреймворк | FastAPI + Uvicorn | Асинхронный, описание API генерируется само | +| База | SQLite в режиме WAL на локальном диске сервера | Нагрузки — десятки событий за смену; хватит с запасом, бэкап — копия файла | +| Слой доступа к базе | SQLAlchemy Core + Alembic | Миграции схемы; замена на PostgreSQL — смена строки подключения | +| Хеши паролей и PIN | `hashlib.scrypt` | В стандартной библиотеке, медленный по построению | +| Веб-страницы | Шаблоны Jinja2 + немного JavaScript | Без тяжёлого фронтенда: таблицы, фильтры, CSV | +| Живые обновления | Server-Sent Events | Обычный HTTP: браузер — `EventSource`, клиент — чтение потока через `http.client` | + +**Почему SSE, а не WebSocket.** Поток нужен в одну сторону — от сервера к +клиентам; отметки клиент шлёт обычными запросами. SSE — это обычный HTTP-ответ, +который не заканчивается: его читает и браузер, и клиент на Python 3.8 без +сторонних библиотек. WebSocket в стандартной библиотеке нет. + +**Кэширование.** Отдельный кэш на сервере (Redis и т. п.) не нужен: объёмы +малы, база отвечает быстрее, чем сеть до цеха. Кэш нужен на стороне +клиента — для работы без связи (см. ниже). + +### Данные + +| Таблица | Что хранит | +|---|---| +| `users` | Имя, роль, хеш PIN или пароля, признак блокировки | +| `workstations` | Зарегистрированные цеховые компьютеры и их ключи | +| `auth_sessions` | Сеансы входа: кто, с какого места, до какого времени | +| `tasks` | Сменные задания: идентификатор по содержимому, имена программ | +| `events` | Журнал выработки: отметки и снятия отметок | +| `bookmarks` | Сессии-закладки пользователей | +| `audit` | Вход, неудачные попытки, действия администратора | + +Где лежит база и резервные копии, задаёт администратор в настройке сервера +(том Docker или папка на диске сервера). + +### Шифрование + +- **В пути — TLS.** Сервер работает только по HTTPS. +- **На диске сервера — средствами ОС**: шифрование диска виртуалки или + тома (LUKS, BitLocker). Шифровать отдельные поля в базе смысла мало: + сервер всё равно должен их расшифровывать, а управление ключами + усложняется. +- **Сертификат.** В цеховой сети обычно нет своего удостоверяющего центра, + поэтому сервер создаёт самоподписанный сертификат, а клиент при + регистрации рабочего места запоминает его отпечаток и дальше доверяет + только ему. Подменить сервер в сети не получится. + +--- + +## Вход и рабочие места + +- **Регистрация рабочего места.** Администратор в веб-панели создаёт + рабочее место и получает одноразовый код. Код вводится в клиенте один раз; + клиент получает ключ рабочего места и отпечаток сертификата и хранит их + под защитой Windows (DPAPI). +- **Вход оператора — имя из списка и PIN.** Сервер принимает PIN только с + зарегистрированного рабочего места и блокирует вход после нескольких + неверных попыток. Украденный PIN без цехового компьютера бесполезен. +- **Вход мастера и администратора** — логин и пароль в браузере. +- **Роли проверяет сервер.** Клиент только прячет недоступное, но + разрешение даёт сервер. + +### Вход без связи + +Если сервер недоступен при запуске, оператор может войти, если он уже +входил на этом компьютере: клиент хранит проверочное значение PIN, +полученное при последнем входе, под защитой DPAPI. Отметки, сделанные в +таком режиме, помечаются «вход без связи» и принимаются сервером после +восстановления связи — по ключу рабочего места. + +--- + +## Выработка + +### Событие + +Отметка «программа выполнена» — событие: + +| Поле | Кто ставит | Пример | +|---|---|---| +| `id` | клиент | UUID — для повторной отправки без дублей | +| `type` | клиент | `program_done` / `program_undone` | +| `task` | клиент | идентификатор задания (см. ниже) | +| `position`, `program` | клиент | 7, `PRG_07` | +| `sheets`, `pieces` | клиент | 5, `{"555001_zz2": 20}` | +| `occurred_at` | клиент | когда нажали, по часам компьютера | +| `operator`, `workstation` | **сервер** | по сеансу входа | +| `received_at` | **сервер** | по часам сервера | + +Автора и время приёма ставит сервер: клиент не может записать выработку от +чужого имени. Повторная отправка события с тем же `id` ничего не меняет. + +Снятая отметка — событие `program_undone`, а не удаление прежнего. Для +каждой программы задания действует последнее событие по порядку приёма +сервером. + +### Идентификатор задания + +Одно задание открывается с разных компьютеров по разным путям. Поэтому +идентификатор — хеш набора программ: имена `.nc` и их содержимое. Разбор +делает клиент тем же `core/`, серверу приходят готовые листы и штуки. + +### Итоги и восстановление + +- Итоги считаются из событий: по оператору, дню, смене, заданию; листы, + программы, позиции, штуки. Исправление — новое событие, а не правка цифр. +- Сменщик открывает то же задание и видит отметки предыдущей смены — + с любого рабочего места. Это закрывает исходную проблему пересменки. +- Мастер видит каждую отметку сразу: сервер рассылает событие всем + подписанным клиентам и страницам. + +--- + +## Сессии + +Журнал — единственный источник правды об отметках. Сессия — **именованная +закладка** на сервере: + +- пользователь вводит имя, к нему добавляются дата и время сохранения; +- сессия хранит автора, задание (идентификатор и путь) и отметки на момент + сохранения; +- открыть сессию — открыть её задание с текущими отметками; отметки на + момент сохранения показываются для сравнения. + +--- + +## Клиент + +### Асинхронность + +Tk можно трогать только из главного потока, а сеть может отвечать секундами. + +- **Весь обмен с сервером — в фоновом потоке** (`client/worker.py`). Окно + ставит задачу в очередь и получает результат через `root.after()`. Окно + не зависает никогда. +- **Поток событий** держит отдельный фоновый поток: читает SSE и передаёт + изменения окну. Обрыв — переподключение с паузой. +- **Состояние связи видно** в окне: «на связи», «нет связи, N отметок ждут + отправки». + +### Работа без связи + +- Отметки пишутся в **локальную очередь на отправку** и досылаются, когда + связь вернётся. Повторная отправка безопасна благодаря `id` события. +- **Локальный кэш** — последнее известное состояние заданий и список + операторов для входа. Очередь и кэш зашифрованы DPAPI: скопировав файлы + с цехового компьютера, их не прочитать на другом. +- Если два оператора без связи отметили одно и то же, действует порядок + приёма сервером, а мастер видит такие отметки помеченными. + +--- + +## Договорённость клиента и сервера + +- **API версионируется**: `/api/v1/...`. Цеховые клиенты обновляются не + одновременно, поэтому сервер держит предыдущую версию API, пока ею + пользуются. +- При подключении клиент сообщает свою версию, сервер — минимальную + поддерживаемую. Слишком старый клиент получает понятное сообщение + «обновите утилиту», а не непонятную ошибку. +- Описание API генерируется сервером (OpenAPI) и лежит в репозитории — + изменения в нём видны в PR. + +--- + +## Структура репозитория + +``` +finnpower_counter/ клиент + core/ разбор .nc/.fms и свод смены — без изменений + client/ + api.py обращения к серверу (http.client + ssl) + events.py чтение потока SSE + outbox.py очередь на отправку + cache.py локальный кэш + dpapi.py защита локальных файлов средствами Windows + worker.py фоновый исполнитель + ui/ окна: вход, оператор (нынешний gui.py), настройка + cli.py инструмент разработчика +server/ сервер — отдельный пакет со своими зависимостями + fpc_server/ + api/ маршруты API + web/ страницы мастера и администратора + db/ схема, миграции + services/ вход, права, выработка, сессии, выгрузка + tests/ + Dockerfile + docker-compose.yml +docs/ +``` + +Клиент и сервер живут в одном репозитории: договорённость между ними +меняется в одном PR, и тесты проверяют обе стороны сразу. + +--- + +## Риски + +| Риск | Что с ним делать | +|---|---| +| Python 3.8 на Windows 7 не договорится с сервером по TLS | Проверка на реальной машине в самом начале, этап 0 | +| DPAPI через ctypes ведёт себя на Win7 не так | Там же, этап 0 | +| Сервер недоступен | Работа без связи, очередь на отправку, автономный режим | +| Потеря базы на сервере | Резервные копии по расписанию, проверка восстановления | +| Разные версии клиентов в цеху | Версия API, минимальная поддерживаемая версия клиента | +| Кто обслуживает сервер | Поставка в Docker одной командой, инструкция для ИТ | diff --git a/docs/FINDINGS.md b/docs/FINDINGS.md new file mode 100644 index 0000000..5f86a6c --- /dev/null +++ b/docs/FINDINGS.md @@ -0,0 +1,63 @@ +# Findings + +Results of the code audit and of the planning work that shaped +[ROADMAP.md](ROADMAP.md) and [ARCHITECTURE.md](ARCHITECTURE.md). Each open +item links to the task that addresses it in [TASKS.md](TASKS.md). + +--- + +## Audit — 2026-09-28 + +Security review, code review and a dead-code pass over the whole code base. +At the time: 218 tests passing, 6 skipped (they need the private dataset). + +### Fixed + +Fixed in [#2](https://github.com/malinkin-s/finnpower-counter/pull/2). + +| ID | Severity | Finding | Where | +|---|---|---|---| +| FND-01 | High | Windows paths in crash reports were cut at the first space, leaking the rest of the path — including user names such as `Ivan Petrov\Desktop\...` — into a report meant for public issues | `report.py` | +| FND-02 | Medium | A crash before a shift was loaded (always the case for CLI crashes) left real program numbers in file names inside the report | `report.py` | +| FND-03 | Medium | If the cross-check failed while loading a new shift, the previous shift's marks were applied to the new one | `gui.py` | +| FND-04 | Medium | One unreadable `.nc` file prevented the whole shift from opening | `core/nc_parser.py` | +| FND-05 | Low | Typing `²` into the program search or the done field crashed (`isdigit` accepts it, `int()` does not) | `presentation.py`, `core/balance.py` | +| FND-06 | — | Dead code: `FONT_BIG`, `var_lang`, `status_at`, a redundant check in `_save_report`; duplicated `dimension()` helpers | `gui.py`, `core/` | +| FND-07 | — | Tests were not run in CI at all | `.github/workflows/` | + +### Open + +| ID | Severity | Finding | Where | Task | +|---|---|---|---|---| +| FND-08 | Low | The report scrubber replaces names as plain substrings: a short part name such as `1` or `10` also rewrites line numbers in the traceback. No leak, but the report becomes hard to read | `report.py` `Scrubber.text` | M-01 | +| FND-09 | Low | Switching the interface language resets the table sort | `gui.py` `_retranslate` → `_change_mode` | M-02 | +| FND-10 | Low | `--done 0` answers "matches several programs" instead of "shift not started" | `core/balance.py` `resolve_position` | M-03 | +| FND-11 | Low | CSV export writes part names as they are: a name starting with `=`, `+`, `-` or `@` is executed by Excel as a formula. Names come from CAM, so the risk is low | `presentation.py` `to_csv` | M-04, S4-04 | +| FND-12 | Low | Two reports saved within the same second overwrite each other | `report.py` `save` | M-05 | +| FND-13 | Low | On Linux, `PRG_1.nc` and `PRG_1.NC` in one folder produce the same table row ID and crash the table | `gui.py` `_refresh_table` | M-06 | +| FND-14 | Low | GitHub Actions are pinned by tag (`@v4`, `@v1`), not by commit SHA | `.github/workflows/` | M-07 | +| FND-15 | Low | On Linux the fallback report folder in `/tmp` has a predictable name. Irrelevant on the Windows target | `report.py` `reports_dir` | M-08 | +| FND-16 | — | `ProgramNest.number` and `reader.program_number` are computed but no logic or view uses them any more | `core/model.py`, `core/reader.py` | M-09 | +| FND-17 | — | The GUI has no tests; `gui.py` changes are only checked by compiling | `gui.py` | S0-06 | +| FND-18 | — | The `Claude Code Review` job finished in under a minute on #2 without a single comment. It may have found nothing, or it may not run properly (e.g. the `CLAUDE_CODE_OAUTH_TOKEN` secret). Needs a look at the job log | `.github/workflows/claude-code-review.yml` | M-10 | + +--- + +## Planning — 2026-09-29 + +Facts found while planning the tracking system. Each one changed or +constrained a decision. + +| ID | Finding | Consequence | +|---|---|---| +| PLN-01 | SQLite's own documentation warns that file locking on network file systems is unreliable and concurrent writes from several machines can corrupt the database | No shared database file on a network drive. Led first to a log-of-files design, then to a server | +| PLN-02 | The Python standard library has no symmetric cipher. Current `cryptography` releases are built with Rust, and current Rust toolchains no longer target Windows 7; older releases that would run are unmaintained | Application-level encryption on the client is expensive. With a server, TLS from the standard library covers data in transit | +| PLN-03 | Python 3.8 bundles its own OpenSSL, so TLS does not depend on Windows 7's own TLS stack | The client can speak modern TLS on Windows 7 — to be confirmed on a real machine (S0-01) | +| PLN-04 | Python 3.8 has been end-of-life since October 2024, and the OpenSSL it bundles is end-of-life too. The client is pinned to it by Windows 7 | Keep the client's network surface minimal: it talks only to its own server, pinned by certificate fingerprint. The server is not bound by this and uses a current Python | +| PLN-05 | `app.spec` excludes `ssl`, `http`, `socket`, `email` and `urllib` — the utility was built to be fully offline | They must come back into the build (S0-04); the `.exe` grows | +| PLN-06 | WebSocket is not in the standard library; Server-Sent Events are plain HTTP and can be read with `http.client` | Live updates use SSE | +| PLN-07 | Tk may only be used from the main thread | A background executor is required before any network I/O (S0-03) | +| PLN-08 | `gui.py` is a single ~790-line module that mixes layout and behaviour | Split it before adding sign-in, setup and other windows (S0-02) | +| PLN-09 | The previous roadmap stated that the utility "keeps no history and should not" | Reversed on purpose: history is kept on the server. Program files remain read-only | +| PLN-10 | Recording each operator's output is processing of personal data and may fall under labour and data-protection law | Open question for the shop's management before rollout (see TASKS.md, open questions) | +| PLN-11 | Code comments, docstrings and commit history are in Russian; the repository is now English-first | New code and documents in English; existing comments translated gradually (M-11) | diff --git a/docs/ROADMAP.md b/docs/ROADMAP.md index 0fab201..516b551 100644 --- a/docs/ROADMAP.md +++ b/docs/ROADMAP.md @@ -1,285 +1,293 @@ -# План развития +# Roadmap -Два направления: +**English** · [Русский](ROADMAP.ru.md) -1. **Система учёта** — вход по ролям, журнал выработки, сессии, просмотр для - мастера. Утилита перестаёт быть узким инструментом одного рабочего места. - Этапы 0–5 ниже, архитектура — в [ARCHITECTURE.md](ARCHITECTURE.md). -2. **Данные из файлов станка** — какие ещё поля полезны оператору или - мастеру участка и что брать не стоит. Этапы A–F. Основано на разборе - выхода постпроцессора NCeXpress FMS. +Two directions: -Сначала идёт система учёта: этапы A–F добавляют колонки и расчёты, а куда -их показывать и кому, решает она. +1. **Production tracking system** — a server with a database, role-based + sign-in, a production log, saved sessions, a supervisor web page. The + utility becomes the client at the operator's workstation. Stages 0–6 + below; architecture in [ARCHITECTURE.md](ARCHITECTURE.md); task breakdown + in [TASKS.md](TASKS.md). +2. **More data from the machine files** — which other fields are useful to + an operator or a supervisor and which are not worth taking. Stages A–F. + Based on the output of the NCeXpress FMS postprocessor. ---- - -## Система учёта +The tracking system comes first: stages A–F add columns and calculations, +and the system decides where and to whom they are shown. -### Что входит - -| № | Возможность | Этап | -|---|---|---| -| 1 | Сохранение сессии: имя от пользователя, дата и время сохранения, шифрование | 3 | -| 2 | Зашифрованный журнал выработки: листы, программы, детали, имя оператора | 2 | -| 3 | Оператор представляется при запуске: имя из списка и PIN | 1 | -| 4 | Зашифрованная система входа и регистрации пользователей | 1 | -| 5 | Всё хранится в папке, которую задаёт администратор | 1 | -| 6 | Режимы работы: администратор, оператор, мастер | 1 | -| 7 | Мастер смотрит выработку, сам ничего не пишет | 4 | -| 8 | Выгрузка выработки в CSV | 4 | -| 9 | Сетевой диск: асинхронные чтение и запись, автоподгрузка без нажатий | 0, 2, 4 | - -### Принятые решения - -- **Вход оператора** — имя из списка и PIN. Слот оператора дополнительно - закрыт секретом рабочего места, чтобы копии сетевой папки не хватало для - перебора PIN. -- **Шифрование защищает** от чтения и незаметной подделки посторонним с - доступом к сетевой папке. Подделка выработки «своими» пока вне цели. -- **Сессия — именованная закладка.** Единственный источник правды об - отметках — журнал выработки; сессия его не дублирует. -- **Хранилище — журнал неизменяемых файлов, а не SQLite на сетевом диске.** - Почему — в ARCHITECTURE.md. - -### Этап 0. Фундамент - -Внешне ничего не меняется. - -- Проверить AES-GCM через CNG (`bcrypt.dll`) и DPAPI на цеховой - Windows 7 32-bit со сборкой PyInstaller. **Если не проходит — архитектура - шифрования пересматривается до начала остальных этапов.** -- Разделить `gui.py` на пакет `ui/`: окно оператора отдельно от общего. -- Фоновый исполнитель: весь ввод-вывод вне главного потока, результаты - в окно через `after()`. -- Атомарная запись файлов (временное имя, затем переименование). -- Настройка рабочего места: путь к папке данных, имя рабочего места. - -### Этап 1. Пользователи и хранилище - -- Мастер первичной настройки: администратор выбирает папку данных, создаёт - свою учётную запись, получает ключ восстановления на печать. -- Ключ данных и слоты пользователей; секрет рабочего места. -- Окно входа: имя из списка и PIN для оператора и мастера, пароль для - администратора. Блокировка после нескольких неверных попыток. -- Роли и таблица прав; администратор заводит и блокирует пользователей. -- Каркас экранов по ролям. Окно оператора — нынешнее, с именем вошедшего. -- Надпись «только чтение» меняется: файлы программ не изменяются, учёт - ведётся в папке данных. - -### Этап 2. Журнал выработки - -- Каждая отметка и снятие отметки пишут событие в журнал. -- Идентификатор задания по содержимому программ: одно задание на разных - машинах сходится. -- Отметки восстанавливаются из журнала при открытии задания — сменщик видит, - что сделано до него. -- Очередь на отправку при обрыве сети, значок состояния связи. -- Локальный индекс для быстрого чтения. - -### Этап 3. Сессии - -- «Сохранить сессию»: имя вводит пользователь, к нему добавляются дата и - время сохранения. -- Список сессий: открыть и продолжить; сравнение с моментом сохранения. - -### Этап 4. Режим мастера - -- Выработка по операторам, дням, сменам и заданиям: листы, программы, - позиции, штуки. -- Автоподгрузка: новые события появляются без нажатий. -- Выгрузка в CSV (`;`, UTF-8 с BOM — как нынешняя выгрузка позиций). - -### Этап 5. Администрирование - -- Сброс PIN, блокировка и разблокировка пользователя. -- Смена ключа данных, перенос папки данных. -- Резервная копия, версия формата и миграции. +Status of everything below: **planned, not implemented.** Findings that +shaped this plan are in [FINDINGS.md](FINDINGS.md). --- -## Данные из файлов станка - -Какие ещё данные есть в файлах станка, что из них полезно оператору или -мастеру участка и что брать не стоит. Разделы ниже — «Что уже используется», -«Предложения», «Что брать не стоит», «Порядок работ» — относятся к этому -направлению. +## Production tracking system + +### Scope + +| # | Feature | Where | Stage | +|---|---|---|---| +| 1 | Save a session: user-given name plus save date and time | client + server | 5 | +| 2 | Production log: sheets, programs, parts, operator name | server | 3 | +| 3 | Operator identifies at start-up: name from a list plus PIN | client | 2 | +| 4 | Secure sign-in and user registration | server | 1, 2 | +| 5 | Data stored where the administrator decides | server | 1 | +| 6 | Roles: operator in the client, supervisor and admin in a browser | client + server | 1, 2 | +| 7 | Supervisor views production and writes nothing | web | 4 | +| 8 | Production export to CSV | web | 4 | +| 9 | Clients work concurrently, changes show up immediately | server + client | 3, 4 | + +### Decisions + +- **Client and server**, not a shared network folder. The shared folder is + the fallback if a server turns out to be impossible. +- **Server: Linux in Docker** as the primary platform, OS-agnostic code, + tests on Windows too. +- **Supervisor and administrator use a browser**; only the operator runs the + client. +- **Operator sign-in: name from a list plus PIN**, only from a registered + workstation; the server limits attempts. +- **Protection**: TLS in transit, the server sets the author and time of each + event, server disk encrypted by the OS, client-local files protected with + DPAPI. +- **Offline, the client keeps working** and sends queued marks later. +- **A session is a named bookmark**; the production log is the single source + of truth about marks. +- **Standalone mode stays**: without a server the utility works as today. + +### Stage 0. Checks and client groundwork + +Nothing visible changes. + +- On a shop-floor Windows 7 32-bit PC, in a PyInstaller build, check: HTTPS + to a test server (`http.client` + `ssl`, TLS 1.2, self-signed certificate + pinned by fingerprint), reading an SSE stream, DPAPI via ctypes. **If any + of it fails, the client architecture is revisited before anything else.** +- Split `gui.py` into a `ui/` package. +- Background executor: all I/O off the main thread. +- Bring `ssl`, `http`, `socket`, `email`, `urllib` back into the build. + +### Stage 1. Server skeleton + +- FastAPI, SQLite, migrations; `docker compose up` brings everything up on a + VM. +- Users and roles, password sign-in to the web panel, first-run setup (the + first administrator). +- Workstations: creation, one-time registration code. +- Audit log of sign-ins and administrator actions. +- CI: server tests on Linux and Windows. + +### Stage 2. The client connects + +- Client setup: server address and workstation registration code. +- Sign-in window: name from a list plus PIN. Offline sign-in for those who + have signed in on this PC before. +- The operator window — today's, with the signed-in name and a connection + indicator. +- Standalone mode when no server is configured. + +### Stage 3. Production log + +- Every mark and unmark is an event on the server; the server sets the author + and receive time. +- Outbox for connection drops, resending without duplicates. +- Content-based task ID; marks are restored when the task is opened on any + workstation. +- Event stream: a mark on one workstation shows up on the others at once. + +### Stage 4. Supervisor page + +- Production per operator, day, shift, task: sheets, programs, positions, + pieces. +- Live updates without clicking. +- CSV export (`;`, UTF-8 with BOM — like today's positions export). + +### Stage 5. Sessions + +- "Save session" in the client: the user types a name, the save date and time + are added; stored on the server. +- Session list: open and continue; compare with the moment of saving. + +### Stage 6. Operations + +- PIN reset, blocking and unblocking users and workstations. +- Scheduled backups and restore checks. +- Server installation guide for IT; running as a Windows service. +- Client notification about a new version. --- -## Что уже используется +## More data from the machine files -| Поле | Откуда | Зачем | -|---|---|---| -| `SHEET_COUNT` | `.nc` | Число листов, множитель тиража | -| `PART_NAME`, `QUANTITY` | `.nc`, блок `PART_DATA` | Деталь и количество на листе | -| `X_DIM`, `Y_DIM` | `.nc` | Размер листа | -| `NUMBER OF SHEETS`, `SHEET SIZE X/Y` | `.fms`, `#GENERAL` | Сверка | -| `#RSCUT`, `#COMPONENTS` | `.fms` | Два независимых среза для сверки | +Which other data the machine files contain, what is useful to an operator or +a supervisor, and what is not worth taking. The sections "Already used", +"Proposals", "Not worth taking" and "Order of work" below belong to this +direction. ---- +### Already used -## Предложения - -### 1. Время до конца сменного задания - -**Данные:** `TOTAL TIME per SHEET` × `NUMBER OF SHEETS` по каждой программе. +| Field | Source | Purpose | +|---|---|---| +| `SHEET_COUNT` | `.nc` | Number of sheets, the run multiplier | +| `PART_NAME`, `QUANTITY` | `.nc`, `PART_DATA` block | Part and quantity per sheet | +| `X_DIM`, `Y_DIM` | `.nc` | Sheet size | +| `NUMBER OF SHEETS`, `SHEET SIZE X/Y` | `.fms`, `#GENERAL` | Cross-check | +| `#RSCUT`, `#COMPONENTS` | `.fms` | Two independent views for the cross-check | -**Кому:** мастеру участка в первую очередь, оператору во вторую. +### Proposals -**Зачем:** сумма по невыполненным программам даёт прогноз до конца задания. -Отвечает на два рабочих вопроса — успеет ли смена закрыть задание и что -придётся передавать сменщику. +#### 1. Time to the end of the shift task -**Оговорка:** это норма CAM, а не факт. Реальное время расходится из-за -загрузки листа, заминов, смены инструмента и простоев. Показывать как -ориентир, а не как обещание. +**Data:** `TOTAL TIME per SHEET` × `NUMBER OF SHEETS` for each program. -### 2. Масса готовых деталей +**For:** the supervisor first, the operator second. -**Данные:** `PART WEIGHT` из `#COMPONENTS`, умноженный на количество. +**Why:** the sum over unfinished programs forecasts the end of the task. It +answers two working questions: will the shift close the task, and what will +be handed over to the next shift. -**Кому:** оператору. +**Caveat:** this is the CAM estimate, not a measurement. Real time differs +because of sheet loading, jams, tool changes and downtime. Show it as a +guide, not a promise. -**Зачем:** масса нужна в сопроводительных документах и ограничивает -комплектацию поддона по грузоподъёмности тары. +#### 2. Weight of finished parts -**Оговорка:** CAM считает вес по геометрии и плотности. С фактическим -взвешиванием расходится на проценты. +**Data:** `PART WEIGHT` from `#COMPONENTS`, times the quantity. -### 3. Материал и толщина +**For:** the operator. -**Данные:** `MATERIAL`, `THICKNESS` из `#GENERAL`. +**Why:** weight goes into shipping documents and limits how a pallet is +filled by the container's capacity. -**Зачем:** в однородном задании смысла мало, но в смешанной смене это главный -признак, по которому нельзя путать поддоны и позиции. Колонка в режиме -программ плюс отбор по материалу. +**Caveat:** CAM computes weight from geometry and density. It differs from +actual weighing by a few per cent. -**Стоимость:** два поля рядом с теми, что уже разбираются. Самое дешёвое -из полезного. +#### 3. Material and thickness -### 4. Габариты детали +**Data:** `MATERIAL`, `THICKNESS` from `#GENERAL`. -**Данные:** `PART_X_DIM` / `PART_Y_DIM` из `.nc`, `Size` из `#RSCUT`. +**Why:** in a uniform task it matters little, but in a mixed shift it is the +main thing that keeps pallets and positions from being mixed up. A column in +the programs view plus a material filter. -**Зачем:** опознать деталь в стопке и понять, во что её укладывать. +**Cost:** two fields next to those already parsed. The cheapest useful item. -**Куда:** в окно позиции, не в главную таблицу — там и так шесть колонок. +#### 4. Part dimensions -### 5. Расход листов +**Data:** `PART_X_DIM` / `PART_Y_DIM` from `.nc`, `Size` from `#RSCUT`. -**Данные:** уже разбираются, считать нового ничего не нужно. +**Why:** to recognise a part in a stack and decide what to pack it in. -**Зачем:** сумма листов по выполненным программам — израсходованный металл, -по невыполненным — сколько ещё потребуется. +**Where:** in the position window, not the main table — it already has six +columns. -### 6. Что закроет позицию +#### 5. Sheet consumption -**Данные:** уже есть, плюс время из пункта 1. +**Data:** already parsed, nothing new to compute. -**Зачем:** для незакрытой позиции показывать не только крайнюю программу, -но и сколько программ до неё осталось и сколько это примерно по времени. -Формулировка «осталось 2 программы, около 40 минут» напрямую закрывает -сценарий пересменки, ради которого утилита и делалась. +**Why:** the sheet total over completed programs is the metal used; over +unfinished ones, what is still needed. -### 7. Признак формовки и гибки +#### 6. What will close a position -**Данные:** `FORMING`, `BENDING MODE`. +**Data:** already available, plus the time from item 1. -**Зачем бы:** пометка «деталь идёт на гибку» — это тот смежный процесс, -из-за требований которого и нужен стопроцентный комплект. +**Why:** for an open position show not only the last program but how many +programs remain before it and roughly how long that is. "2 programs left, +about 40 minutes" directly serves the shift-handover scenario the utility was +built for. -**Что сделать сначала:** выяснить, заполняются ли эти поля в вашем CAM. -Если везде ноль — пункт вычёркивается. +#### 7. Forming and bending flag -### 8. Заказ и заказчик +**Data:** `FORMING`, `BENDING MODE`. -**Данные:** `CUSTOMER`, `ASSEMBLY` в `#COMPONENTS`, колонка `Order ID` -в карте наладки. +**Why it might matter:** "this part goes to bending" is exactly the +downstream process whose requirements make a complete set necessary. -**Состояние:** в разобранных файлах пусты. Заполняются они или нет — -зависит от того, как ведётся работа в конкретном CAM. +**First:** find out whether these fields are filled in your CAM. If they are +always zero, drop the item. -**Зачем бы:** появилась бы группировка по заказу, и это было бы сильнее -любого другого пункта, потому что комплектность проверяют по заказу, -а не по смене. +#### 8. Order and customer -**Разумный подход:** читать поля и показывать колонку только тогда, когда -они непусты. Дёшево и никому не мешает. +**Data:** `CUSTOMER`, `ASSEMBLY` in `#COMPONENTS`, the `Order ID` column in the +setup sheet. -### 9. Автоматическое определение выполненных программ +**Status:** empty in the files analysed. Whether they are filled depends on +how the particular CAM is used. -**Состояние: отложено.** +**Why it might matter:** grouping by order would be stronger than any other +item, because completeness is checked per order, not per shift. -ПО стойки ведёт собственный учёт: очередь программ с числом выполненных -листов и журналы сбора данных, куда строка пишется на каждый обработанный -лист. Оттуда можно брать факт выполнения вместо ручных отметок. +**Sensible approach:** read the fields and show the column only when they are +not empty. Cheap and harmless. -Выполнимо, но требует доступа к файловой системе станка и ответа на -вопросы, которые решаются только на месте: доступна ли цеховому компьютеру -нужная папка, соответствует ли очередь сменному заданию, совпадают ли имена -файлов. +#### 9. Automatic detection of executed programs -**Побочное следствие для модели:** такие источники дают число выполненных -листов, а не только «программа выполнена целиком». Это меняет расчёт -выработки и потребует правки ядра. +**Status: deferred.** -Ручной ввод в любом случае остаётся: источник может быть недоступен, -а утилита должна работать и без него. +The machine control software keeps its own records: a program queue with the +number of sheets done, and data-collection logs with a line per processed +sheet. The fact of execution could be taken from there instead of manual +marks. ---- +Feasible, but it needs access to the machine's file system and answers that +can only be found on site: can the shop-floor PC reach the folder, does the +queue match the shift task, do the file names match. -## Что брать не стоит +**Side effect on the model:** such sources give the number of sheets done, +not just "program fully done". That changes how production is counted and +will need a core change. -**КИМ (`SHEET USAGE EFFICIENCY`).** Оператор на раскрой не влияет — его делает -технолог в CAM. Показатель не порождает действий на рабочем месте. +Manual marking stays in any case: the source may be unavailable, and the +utility must work without it. -**Удары, пройденное расстояние, смены инструмента.** Это про износ пуансонов -и машинное время. Чтобы это было полезно, нужен накопительный счётчик по -инструменту между сменами, то есть хранимая история. Раньше утилита истории -не вела; с журналом выработки (этап 2) она появится, и к этому пункту можно -будет вернуться — но только если на участке реально ведут учёт износа -инструмента. +### Not worth taking -**Наладочные параметры станка** — прижимы, ход, ускорение, скорость, -разгрузка, адреса. Оператор видит их на стойке при наладке. +**Sheet usage efficiency (`SHEET USAGE EFFICIENCY`).** The operator has no +influence on nesting — the CAM engineer does it. The figure does not lead to +any action at the workstation. -**Служебные поля** — внутренние идентификаторы, адреса, углы, число -подпрограмм, размер программы, имена файлов и дата генерации. +**Hits, travel distance, tool changes.** These are about punch wear and +machine time. To be useful they need a cumulative per-tool counter across +shifts, i.e. stored history. The utility used to keep no history; with the +production log (stage 3) it will, and this item can be revisited — but only +if the shop actually tracks tool wear. -**Список инструмента (`#TOOLS`).** При наладке он нужен, но оператор -открывает карту наладки двойным щелчком, и там тот же список в читаемом -виде с размерами и станциями. +**Machine setup parameters** — clamps, stroke, acceleration, speed, +unloading, addresses. The operator sees them on the control during setup. ---- +**Service fields** — internal IDs, addresses, angles, subprogram counts, +program size, file names and generation date. -## Порядок работ +**Tool list (`#TOOLS`).** Needed during setup, but the operator opens the +setup sheet with a double click, and the same list is there in readable form +with sizes and stations. -Сначала этапы 0–5 системы учёта, затем этапы данных из файлов станка. +### Order of work -**Этап A.** Материал и толщина, расход листов, габариты детали в окне -позиции. Данные уже разбираются рядом. +Tracking system stages 0–6 first, then the machine-data stages. -**Этап B.** Расширение генератора фикстур: разное время на лист, разные -материалы и толщины, веса, признак формовки. Без этого следующие этапы -непроверяемы тестами. +**Stage A.** Material and thickness, sheet consumption, part dimensions in +the position window. The data is already parsed nearby. -**Этап C.** Время: норма на программу, прогноз до конца задания, -«что закроет позицию». +**Stage B.** Extend the fixture generator: different time per sheet, +different materials and thicknesses, weights, forming flag. Without it the +following stages cannot be tested. -**Этап D.** Масса позиции и массa по программе, выгрузка в маршрутный лист. +**Stage C.** Time: estimate per program, forecast to the end of the task, +"what will close the position". -**Этап E.** Формовка и гибка, заказ и заказчик — по правилу «показывать, -если заполнено». +**Stage D.** Weight per position and per program, export to the route sheet. -**Этап F.** Автоматическое определение выполненных программ. С журналом -выработки это ещё один источник событий рядом с ручными отметками. +**Stage E.** Forming and bending, order and customer — shown only when +filled. ---- +**Stage F.** Automatic detection of executed programs. With the production +log it is simply another source of events next to manual marks. -## Общий принцип отбора +### Selection principle -Поле попадает в утилиту, если отвечает на вопрос, который оператор или -мастер задаёт себе в течение смены, и если ответ меняет их действия. -Остальное — данные технолога и наладчика, им место в CAM и в карте наладки, -а не в блокноте учёта готовности. +A field gets into the utility if it answers a question the operator or the +supervisor asks during a shift, and if the answer changes what they do. +Everything else is data for the CAM engineer and the setter; it belongs in +the CAM system and the setup sheet, not in a completion tracker. diff --git a/docs/ROADMAP.ru.md b/docs/ROADMAP.ru.md new file mode 100644 index 0000000..cbed854 --- /dev/null +++ b/docs/ROADMAP.ru.md @@ -0,0 +1,305 @@ +# План развития + +[English](ROADMAP.md) · **Русский** + +Основная версия — английская; эта может отставать от неё. Разбивка на +задачи — [TASKS.md](TASKS.md), находки аудита и планирования — +[FINDINGS.md](FINDINGS.md) (оба на английском). + +Два направления: + +1. **Система учёта** — сервер с базой данных, вход по ролям, журнал + выработки, сессии, веб-страница для мастера. Утилита становится клиентом + на рабочем месте оператора. + Этапы 0–6 ниже, архитектура — в [ARCHITECTURE.ru.md](ARCHITECTURE.ru.md). +2. **Данные из файлов станка** — какие ещё поля полезны оператору или + мастеру участка и что брать не стоит. Этапы A–F. Основано на разборе + выхода постпроцессора NCeXpress FMS. + +Сначала идёт система учёта: этапы A–F добавляют колонки и расчёты, а куда +их показывать и кому, решает она. + +--- + +## Система учёта + +Сервер с базой данных, веб-страницами для мастера и администратора; утилита +становится клиентом оператора. Архитектура и причины решений — в +[ARCHITECTURE.ru.md](ARCHITECTURE.ru.md). + +### Что входит + +| № | Возможность | Где | Этап | +|---|---|---|---| +| 1 | Сохранение сессии: имя от пользователя, дата и время сохранения | клиент + сервер | 5 | +| 2 | Журнал выработки: листы, программы, детали, имя оператора | сервер | 3 | +| 3 | Оператор представляется при запуске: имя из списка и PIN | клиент | 2 | +| 4 | Вход и регистрация пользователей, защищённые | сервер | 1, 2 | +| 5 | Данные хранятся там, где задаёт администратор | сервер | 1 | +| 6 | Роли: оператор в клиенте, мастер и администратор в браузере | клиент + сервер | 1, 2 | +| 7 | Мастер смотрит выработку, сам ничего не пишет | веб | 4 | +| 8 | Выгрузка выработки в CSV | веб | 4 | +| 9 | Одновременная работа клиентов, изменения видны сразу | сервер + клиент | 3, 4 | + +### Принятые решения + +- **Клиент и сервер**, а не общая сетевая папка. Сетевая папка — запасной + путь, если сервер окажется невозможен. +- **Сервер — Linux в Docker** как основная платформа, код без привязки к ОС, + тесты и на Windows. +- **Мастер и администратор — в браузере**, клиент только у оператора. +- **Вход оператора — имя из списка и PIN**, только с зарегистрированного + рабочего места; попытки ограничивает сервер. +- **Защита**: TLS в пути, автора и время события ставит сервер, диск + сервера шифруется средствами ОС, локальные файлы клиента — DPAPI. +- **Без связи клиент работает и досылает** отметки из очереди. +- **Сессия — именованная закладка**, журнал выработки — единственный + источник правды об отметках. +- **Автономный режим остаётся**: без сервера утилита работает как сейчас. + +### Этап 0. Проверки и фундамент клиента + +Внешне ничего не меняется. + +- Проверить на цеховой Windows 7 32-bit в сборке PyInstaller: HTTPS до + тестового сервера (`http.client` + `ssl`, TLS 1.2, самоподписанный + сертификат по отпечатку), чтение потока SSE, DPAPI через ctypes. **Если + что-то не проходит — архитектура клиента пересматривается до начала + остальных этапов.** +- Разделить `gui.py` на пакет `ui/`. +- Фоновый исполнитель: весь ввод-вывод вне главного потока. +- Вернуть в сборку `ssl`, `http`, `socket`, `email`, `urllib`. + +### Этап 1. Скелет сервера + +- FastAPI, SQLite, миграции; `docker compose up` поднимает всё на виртуалке. +- Пользователи и роли, вход в веб-панель по паролю, первичная настройка + (первый администратор). +- Рабочие места: создание, одноразовый код регистрации. +- Журнал аудита входов и действий администратора. +- CI: тесты сервера на Linux и Windows. + +### Этап 2. Клиент подключается + +- Настройка клиента: адрес сервера и код регистрации рабочего места. +- Окно входа: имя из списка и PIN. Вход без связи для тех, кто уже входил + на этом компьютере. +- Окно оператора — нынешнее, с именем вошедшего и значком связи. +- Автономный режим, если сервер не настроен. + +### Этап 3. Журнал выработки + +- Каждая отметка и снятие отметки — событие на сервере; автора и время + приёма ставит сервер. +- Очередь на отправку при обрыве связи, повторная отправка без дублей. +- Идентификатор задания по содержимому программ; отметки восстанавливаются + при открытии задания на любом рабочем месте. +- Поток событий: отметка на одном месте сразу видна на других. + +### Этап 4. Страница мастера + +- Выработка по операторам, дням, сменам, заданиям: листы, программы, + позиции, штуки. +- Обновление в реальном времени без нажатий. +- Выгрузка в CSV (`;`, UTF-8 с BOM — как нынешняя выгрузка позиций). + +### Этап 5. Сессии + +- «Сохранить сессию» в клиенте: имя вводит пользователь, к нему добавляются + дата и время сохранения; хранится на сервере. +- Список сессий: открыть и продолжить; сравнение с моментом сохранения. + +### Этап 6. Эксплуатация + +- Сброс PIN, блокировка и разблокировка пользователей и рабочих мест. +- Резервные копии по расписанию и проверка восстановления. +- Инструкция по установке сервера для ИТ; запуск как службы Windows. +- Уведомление клиента о новой версии. + +--- + +## Данные из файлов станка + +Какие ещё данные есть в файлах станка, что из них полезно оператору или +мастеру участка и что брать не стоит. Разделы ниже — «Что уже используется», +«Предложения», «Что брать не стоит», «Порядок работ» — относятся к этому +направлению. + +--- + +## Что уже используется + +| Поле | Откуда | Зачем | +|---|---|---| +| `SHEET_COUNT` | `.nc` | Число листов, множитель тиража | +| `PART_NAME`, `QUANTITY` | `.nc`, блок `PART_DATA` | Деталь и количество на листе | +| `X_DIM`, `Y_DIM` | `.nc` | Размер листа | +| `NUMBER OF SHEETS`, `SHEET SIZE X/Y` | `.fms`, `#GENERAL` | Сверка | +| `#RSCUT`, `#COMPONENTS` | `.fms` | Два независимых среза для сверки | + +--- + +## Предложения + +### 1. Время до конца сменного задания + +**Данные:** `TOTAL TIME per SHEET` × `NUMBER OF SHEETS` по каждой программе. + +**Кому:** мастеру участка в первую очередь, оператору во вторую. + +**Зачем:** сумма по невыполненным программам даёт прогноз до конца задания. +Отвечает на два рабочих вопроса — успеет ли смена закрыть задание и что +придётся передавать сменщику. + +**Оговорка:** это норма CAM, а не факт. Реальное время расходится из-за +загрузки листа, заминов, смены инструмента и простоев. Показывать как +ориентир, а не как обещание. + +### 2. Масса готовых деталей + +**Данные:** `PART WEIGHT` из `#COMPONENTS`, умноженный на количество. + +**Кому:** оператору. + +**Зачем:** масса нужна в сопроводительных документах и ограничивает +комплектацию поддона по грузоподъёмности тары. + +**Оговорка:** CAM считает вес по геометрии и плотности. С фактическим +взвешиванием расходится на проценты. + +### 3. Материал и толщина + +**Данные:** `MATERIAL`, `THICKNESS` из `#GENERAL`. + +**Зачем:** в однородном задании смысла мало, но в смешанной смене это главный +признак, по которому нельзя путать поддоны и позиции. Колонка в режиме +программ плюс отбор по материалу. + +**Стоимость:** два поля рядом с теми, что уже разбираются. Самое дешёвое +из полезного. + +### 4. Габариты детали + +**Данные:** `PART_X_DIM` / `PART_Y_DIM` из `.nc`, `Size` из `#RSCUT`. + +**Зачем:** опознать деталь в стопке и понять, во что её укладывать. + +**Куда:** в окно позиции, не в главную таблицу — там и так шесть колонок. + +### 5. Расход листов + +**Данные:** уже разбираются, считать нового ничего не нужно. + +**Зачем:** сумма листов по выполненным программам — израсходованный металл, +по невыполненным — сколько ещё потребуется. + +### 6. Что закроет позицию + +**Данные:** уже есть, плюс время из пункта 1. + +**Зачем:** для незакрытой позиции показывать не только крайнюю программу, +но и сколько программ до неё осталось и сколько это примерно по времени. +Формулировка «осталось 2 программы, около 40 минут» напрямую закрывает +сценарий пересменки, ради которого утилита и делалась. + +### 7. Признак формовки и гибки + +**Данные:** `FORMING`, `BENDING MODE`. + +**Зачем бы:** пометка «деталь идёт на гибку» — это тот смежный процесс, +из-за требований которого и нужен стопроцентный комплект. + +**Что сделать сначала:** выяснить, заполняются ли эти поля в вашем CAM. +Если везде ноль — пункт вычёркивается. + +### 8. Заказ и заказчик + +**Данные:** `CUSTOMER`, `ASSEMBLY` в `#COMPONENTS`, колонка `Order ID` +в карте наладки. + +**Состояние:** в разобранных файлах пусты. Заполняются они или нет — +зависит от того, как ведётся работа в конкретном CAM. + +**Зачем бы:** появилась бы группировка по заказу, и это было бы сильнее +любого другого пункта, потому что комплектность проверяют по заказу, +а не по смене. + +**Разумный подход:** читать поля и показывать колонку только тогда, когда +они непусты. Дёшево и никому не мешает. + +### 9. Автоматическое определение выполненных программ + +**Состояние: отложено.** + +ПО стойки ведёт собственный учёт: очередь программ с числом выполненных +листов и журналы сбора данных, куда строка пишется на каждый обработанный +лист. Оттуда можно брать факт выполнения вместо ручных отметок. + +Выполнимо, но требует доступа к файловой системе станка и ответа на +вопросы, которые решаются только на месте: доступна ли цеховому компьютеру +нужная папка, соответствует ли очередь сменному заданию, совпадают ли имена +файлов. + +**Побочное следствие для модели:** такие источники дают число выполненных +листов, а не только «программа выполнена целиком». Это меняет расчёт +выработки и потребует правки ядра. + +Ручной ввод в любом случае остаётся: источник может быть недоступен, +а утилита должна работать и без него. + +--- + +## Что брать не стоит + +**КИМ (`SHEET USAGE EFFICIENCY`).** Оператор на раскрой не влияет — его делает +технолог в CAM. Показатель не порождает действий на рабочем месте. + +**Удары, пройденное расстояние, смены инструмента.** Это про износ пуансонов +и машинное время. Чтобы это было полезно, нужен накопительный счётчик по +инструменту между сменами, то есть хранимая история. Раньше утилита истории +не вела; с журналом выработки (этап 3) она появится, и к этому пункту можно +будет вернуться — но только если на участке реально ведут учёт износа +инструмента. + +**Наладочные параметры станка** — прижимы, ход, ускорение, скорость, +разгрузка, адреса. Оператор видит их на стойке при наладке. + +**Служебные поля** — внутренние идентификаторы, адреса, углы, число +подпрограмм, размер программы, имена файлов и дата генерации. + +**Список инструмента (`#TOOLS`).** При наладке он нужен, но оператор +открывает карту наладки двойным щелчком, и там тот же список в читаемом +виде с размерами и станциями. + +--- + +## Порядок работ + +Сначала этапы 0–6 системы учёта, затем этапы данных из файлов станка. + +**Этап A.** Материал и толщина, расход листов, габариты детали в окне +позиции. Данные уже разбираются рядом. + +**Этап B.** Расширение генератора фикстур: разное время на лист, разные +материалы и толщины, веса, признак формовки. Без этого следующие этапы +непроверяемы тестами. + +**Этап C.** Время: норма на программу, прогноз до конца задания, +«что закроет позицию». + +**Этап D.** Масса позиции и массa по программе, выгрузка в маршрутный лист. + +**Этап E.** Формовка и гибка, заказ и заказчик — по правилу «показывать, +если заполнено». + +**Этап F.** Автоматическое определение выполненных программ. С журналом +выработки это ещё один источник событий рядом с ручными отметками. + +--- + +## Общий принцип отбора + +Поле попадает в утилиту, если отвечает на вопрос, который оператор или +мастер задаёт себе в течение смены, и если ответ меняет их действия. +Остальное — данные технолога и наладчика, им место в CAM и в карте наладки, +а не в блокноте учёта готовности. diff --git a/docs/TASKS.md b/docs/TASKS.md new file mode 100644 index 0000000..37c85c5 --- /dev/null +++ b/docs/TASKS.md @@ -0,0 +1,519 @@ +# Tasks + +Detailed breakdown of [ROADMAP.md](ROADMAP.md). Architecture and the reasons +behind it are in [ARCHITECTURE.md](ARCHITECTURE.md); the facts that shaped the +plan are in [FINDINGS.md](FINDINGS.md). + +Status of every task: **not started**, unless marked otherwise. + +## Conventions + +- **ID** — `S-` for the tracking system (stages 0–6), `M-` for + maintenance items from the audit, `D-` for machine-data stages A–F. +- **Size** — rough effort: **S** up to a day, **M** two to four days, + **L** one to two weeks. +- **Done when** — acceptance criteria. A task is not done until they hold and + the tests for it are in CI. +- One task, one pull request where practical. +- New code, comments, commit messages and documents are in English. + +## Order at a glance + +``` +S0-01 ──► (gate) ──► S1 ──► S2 ──► S3 ──► S4 +S0-02..06 ─────────────────► S2 S3 ──► S5 + S6 runs alongside S3–S5 +M-xx: any time, independent +``` + +- **S0-01 is a gate.** If Windows 7 cannot do what the client needs, the + architecture is revisited before S1 starts. +- S0-02…S0-06 do not depend on the server and can start at once. +- S1 can start as soon as S0-01 passes; it does not need S0-02…06. + +--- + +## Stage 0 — Checks and client groundwork + +### S0-01 Windows 7 compatibility probe · L · gate + +**Goal.** Prove on a real shop-floor PC that the client can do everything the +architecture expects of it. + +**Scope.** +- A throw-away probe server (any machine, e.g. a VM): HTTPS with a + self-signed certificate, one JSON endpoint, one SSE endpoint that emits an + event every second. +- A probe client built with the same PyInstaller settings as the real + `.exe` (Python 3.8.10, 32-bit), no third-party packages: + 1. HTTPS request with TLS 1.2 and certificate pinning by SHA-256 + fingerprint (reject any other certificate); + 2. read the SSE stream for a minute, survive a server restart, reconnect; + 3. DPAPI `CryptProtectData` / `CryptUnprotectData` round-trip via ctypes, + machine and user scope; + 4. report everything into a text file. +- Run on the target PC (Windows 7 32-bit), and for comparison on a + current Windows. + +**Done when.** All four checks pass on the target PC, results are recorded in +FINDINGS.md (as PLN-xx), and the probe code is kept under `tools/probe/` for +re-runs. + +**If it fails.** Record what failed; revisit ARCHITECTURE.md before S1. + +### S0-02 Split `gui.py` into a `ui/` package · M + +**Goal.** Make room for new windows without growing a monolith (PLN-08). + +**Scope.** `ui/app.py` (start-up, crash hook), `ui/operator.py` (today's main +window), `ui/part_window.py`, `ui/widgets.py` (shared helpers). No behaviour +change. `app.py` and `app.spec` updated. + +**Done when.** The application looks and behaves exactly as before; all tests +pass; the PyInstaller build runs. + +### S0-03 Background executor · M + +**Goal.** No I/O on the Tk main thread (PLN-07). + +**Scope.** +- `client/worker.py`: a worker thread with a job queue; results delivered to + Tk through `root.after()` polling; errors delivered as results, never + raised in the worker. +- Move shift loading and the cross-check onto it; show a "loading…" state; + ignore stale results if the user opened another folder meanwhile. + +**Done when.** Loading a shift from a slow or unreachable network folder does +not freeze the window; unit tests cover the worker without Tk. + +**Depends on.** S0-02. + +### S0-04 Network modules back in the build · S + +**Goal.** Let the `.exe` use HTTPS (PLN-05). + +**Scope.** Remove `ssl`, `http`, `socket`, `email`, `urllib` from `excludes` in +`app.spec`; record the `.exe` size before and after. + +**Done when.** The build runs on the target; size change noted in the PR. + +**Depends on.** S0-01 (confirms which modules are actually needed). + +### S0-05 Client settings store · S + +**Goal.** A place for per-machine settings: server URL, workstation +registration, standalone flag. + +**Scope.** `client/settings.py`: an INI file in `%LOCALAPPDATA%\FinnPowerCounter` +(fallback next to the user profile on other OSes); secrets are not stored here +(they go through DPAPI in S2-02). Absent file = standalone mode. + +**Done when.** Settings round-trip in tests; no settings file means the +utility behaves exactly as today. + +### S0-06 GUI smoke tests · M + +**Goal.** Close the GUI test gap (FND-17) before the GUI starts changing. + +**Scope.** Tests that build the operator window under a virtual display +(Xvfb on Linux CI, a real desktop on the Windows runner), load the +`shift_ok` fixture, switch modes, toggle a mark, and check the table content. + +**Done when.** The smoke tests run in CI on both runners. + +**Depends on.** S0-02. + +--- + +## Stage 1 — Server skeleton + +### S1-01 Server package and deployment · M + +**Scope.** `server/` with its own `pyproject.toml`; FastAPI app with +`/api/v1/health`; configuration via environment variables (data directory, +listen address); `Dockerfile` and `docker-compose.yml` with a volume for +data; a CI job running server tests on Linux and Windows. + +**Done when.** `docker compose up` on a clean Linux VM serves the health +endpoint; the same code starts with `python -m fpc_server` on Windows. + +**Depends on.** S0-01 passed. + +### S1-02 Database schema v1 and migrations · M + +**Scope.** SQLAlchemy Core tables `users`, `workstations`, `auth_sessions`, +`audit`; Alembic migrations; SQLite in WAL mode; database file inside the +configured data directory. + +**Done when.** A fresh start creates the schema; migrations run up and down +in tests. + +**Depends on.** S1-01. + +### S1-03 Users, roles and permissions · M + +**Scope.** +- Roles `operator`, `supervisor`, `admin`; a single permission table in + `services/roles.py`; every route declares the permission it needs. +- Password and PIN hashes with `hashlib.scrypt`, per-user salt, parameters + stored with the hash. +- First-run setup: a CLI command `fpc-server create-admin` (no web page + reachable without an admin). + +**Done when.** Permission checks are covered by tests for every role and +route; hashes verify and reject correctly. + +**Depends on.** S1-02. + +### S1-04 Web sign-in for supervisor and admin · M + +**Scope.** Sign-in page; server-side sessions with an HTTP-only, `Secure`, +`SameSite=Strict` cookie; CSRF protection on forms; lockout after N failed +attempts per user and per address; sign-out. + +**Done when.** Tests cover success, wrong password, lockout, expiry, CSRF +rejection. + +**Depends on.** S1-03. + +### S1-05 Admin pages: users and workstations · M + +**Scope.** +- Users: create, change role, block and unblock, set or reset PIN/password. +- Workstations: create, name, block; generate a one-time registration code + (short-lived, single use). + +**Done when.** All actions work through the web page and are recorded in the +audit log (S1-07). + +**Depends on.** S1-04. + +### S1-06 TLS with a self-signed certificate · S + +**Scope.** On first start, generate a key and a self-signed certificate into +the data directory unless the administrator supplies their own; show the +SHA-256 fingerprint on the admin page; HTTP only redirects to HTTPS or is +disabled. + +**Done when.** The server serves HTTPS out of the box; the fingerprint shown +matches the certificate. + +**Depends on.** S1-01. + +### S1-07 Audit log · S + +**Scope.** `audit` table: sign-ins, failed attempts, lockouts, every +administrator action; an admin page to view it with filters. + +**Done when.** Each action from S1-04 and S1-05 leaves exactly one audit +entry. + +**Depends on.** S1-02. + +### S1-08 API contract and version handshake · S + +**Scope.** `/api/v1/version` returns the server version and the minimum +supported client version; the OpenAPI description is exported into +`server/openapi.json` and checked in CI (the build fails if it is stale). + +**Done when.** A change to any route shows up as a diff in `openapi.json`. + +**Depends on.** S1-01. + +--- + +## Stage 2 — The client connects + +### S2-01 API client · M + +**Scope.** `client/api.py` on `http.client` + `ssl`: certificate pinning by +fingerprint, timeouts, JSON in and out, a small error model (offline, auth +failed, forbidden, too old, server error); version handshake on connect +(S1-08). + +**Done when.** Tested against a real test server started in CI; pinning +rejects a different certificate. + +**Depends on.** S0-01, S0-04, S1-08. + +### S2-02 DPAPI wrapper · S + +**Scope.** `client/dpapi.py`: protect and unprotect bytes via ctypes; a +non-Windows fallback for development and tests that is clearly marked as +insecure. + +**Done when.** Round-trip tests on the Windows CI runner. + +**Depends on.** S0-01. + +### S2-03 Workstation registration · M + +**Scope.** +- Server: `POST /api/v1/workstations/register` exchanges a one-time code for + a workstation key. +- Client: a setup window — server address and code; stores the key + (DPAPI) and the certificate fingerprint (S0-05). + +**Done when.** A code works once and expires; a registered workstation shows +up as active on the admin page. + +**Depends on.** S1-05, S2-01, S2-02. + +### S2-04 Operator sign-in · M + +**Scope.** +- Server: list of operators allowed on a workstation; PIN sign-in only with + a valid workstation key; attempt limit and lockout; a token bound to the + workstation, expiring at a configured time. +- Client: sign-in window (name from a list, PIN pad usable with gloves); + signed-in name shown in the operator window; sign-out. + +**Done when.** A PIN is rejected from an unregistered workstation; lockout +works; tests on both sides. + +**Depends on.** S2-03. + +### S2-05 Offline sign-in · M + +**Scope.** After an online sign-in the client stores a PIN verifier (scrypt) +under DPAPI; if the server is unreachable, those operators can sign in +locally; the session is flagged "offline" until the server confirms it. + +**Done when.** Sign-in works with the server stopped, only for operators who +signed in on this PC before. + +**Depends on.** S2-04. + +### S2-06 Connection indicator and standalone mode · S + +**Scope.** Indicator in the operator window: connected, offline (with the +outbox size from S3-03), standalone. With no server configured, no sign-in +window appears and nothing is sent. + +**Done when.** All three states are visible and covered by GUI smoke tests. + +**Depends on.** S0-06, S2-04. + +--- + +## Stage 3 — Production log + +### S3-01 Task identity · S + +**Scope.** A stable ID of a shift task: SHA-256 over sorted `.nc` names and +their contents, in `core/`; independent of the folder path and line endings. + +**Done when.** Same files in different folders give the same ID; any content +change gives a different one. + +### S3-02 Server: tasks and events · M + +**Scope.** Tables `tasks`, `events`; `POST /api/v1/events` accepts a batch, +idempotent by event `id`; the server sets `operator`, `workstation`, +`received_at`; `GET /api/v1/tasks/{id}` returns the current marks (last event +per program in receive order). + +**Done when.** Resending a batch changes nothing; an event cannot name +another operator. + +**Depends on.** S1-02, S2-04. + +### S3-03 Client: marks become events, outbox · M + +**Scope.** Each mark or unmark writes an event to a persistent outbox +(DPAPI-protected file); the worker sends batches with back-off; the outbox +survives restarts. + +**Done when.** With the server stopped, marks queue up; after it starts they +arrive exactly once. + +**Depends on.** S0-03, S3-01, S3-02. + +### S3-04 Restore marks on open · S + +**Scope.** Opening a task asks the server for its marks and applies them; +offline, the local cache is used and the window says so. + +**Done when.** A task marked on one workstation opens with the same marks on +another. + +**Depends on.** S3-02, S3-03. + +### S3-05 Live event stream · M + +**Scope.** Server: `GET /api/v1/stream` (SSE) broadcasts new events to +subscribers, with `Last-Event-ID` resume. Client: a stream reader thread that +updates the open task live and reconnects with back-off. + +**Done when.** A mark on one client appears on another within a couple of +seconds; a dropped connection resumes without losing events. + +**Depends on.** S3-02, S0-03. + +### S3-06 Offline conflicts · S + +**Scope.** When events for the same program from different workstations +cross while one side is offline, flag them for the supervisor instead of +silently picking one. + +**Done when.** The conflict case is covered by a test and visible via the +API. + +**Depends on.** S3-02. + +--- + +## Stage 4 — Supervisor page + +### S4-01 Production totals · M + +**Scope.** Queries over events: per operator, day, shift, task — sheets, +programs, positions, pieces; only the effective event per program counts. + +**Done when.** Totals match a hand-computed fixture. + +**Depends on.** S3-02, S4-02. + +### S4-02 Shift definition · S + +**Scope.** Configurable shift boundaries (e.g. 08:00–20:00, 20:00–08:00) in +the server settings; events are assigned to a shift by `received_at`. + +**Done when.** Night shifts that cross midnight count as one shift. + +**Depends on.** Open question Q-02. + +### S4-03 Supervisor page · M + +**Scope.** Server-rendered page: filters (dates, operator, task), tables of +totals, drill-down to programs; live updates via `EventSource` on the S3-05 +stream. + +**Done when.** A mark made on the floor shows up on the page without a +reload. + +**Depends on.** S4-01, S3-05, S1-04. + +### S4-04 CSV export · S + +**Scope.** Export of the current page view; `;` separator, UTF-8 with BOM; +cells starting with `=`, `+`, `-`, `@` are escaped (FND-11). + +**Done when.** The file opens correctly in a Russian-locale Excel; formula +injection is covered by a test. + +**Depends on.** S4-03. + +--- + +## Stage 5 — Sessions + +### S5-01 Server: bookmarks · S + +**Scope.** Table `bookmarks`; API to create, list (own), open; stores name, +author, task ID and path, marks at save time, timestamp. + +**Done when.** Users see only their own bookmarks; tests cover it. + +**Depends on.** S3-02. + +### S5-02 Client: save and open sessions · M + +**Scope.** "Save session" dialog: the user types a name, the save date and +time are added (shown as `2026-09-29 14:30 — `); session list; opening +restores the task and shows what changed since saving. + +**Done when.** A session saved on one workstation opens on another. + +**Depends on.** S5-01, S3-04. + +--- + +## Stage 6 — Operations + +### S6-01 Backups · M + +**Scope.** Scheduled online backups with SQLite's backup API into a +configured folder, retention policy, a documented restore procedure and a +test that restores a backup. + +**Done when.** A restore drill passes in CI. + +**Depends on.** S1-02. + +### S6-02 Installation guide · S + +**Scope.** `docs/SERVER.md`: Docker on Linux, running as a Windows service, +ports, certificate, backups, upgrades. Written for an IT department. + +**Depends on.** S1-01, S1-06, S6-01. + +### S6-03 Client update notice · S + +**Scope.** On connect, if the client is older than the latest release the +server knows of, show a notice with the version; if older than the minimum, +refuse to send data and say why. + +**Depends on.** S1-08, S2-01. + +### S6-04 Security review before rollout · M + +**Scope.** Review of the server and client before the first shop-floor use: +authentication, session handling, permission table, TLS setup, logging of +personal data, dependency audit of the server. + +**Done when.** Findings are recorded in FINDINGS.md and blocking ones fixed. + +**Depends on.** S1–S5. + +--- + +## Maintenance — from the audit + +Small, independent; can be done at any time. + +| ID | Size | Task | Finding | +|---|---|---|---| +| M-01 | S | Report scrubber: replace names only at word boundaries, never inside `line N` | FND-08 | +| M-02 | S | Keep the table sort when the language is switched | FND-09 | +| M-03 | S | `--done 0` means "shift not started" | FND-10 | +| M-04 | S | Escape formula-like cells in the positions CSV export | FND-11 | +| M-05 | S | Unique report file names (add milliseconds or a counter) | FND-12 | +| M-06 | S | Table row IDs by position, not by program name | FND-13 | +| M-07 | S | Pin GitHub Actions by commit SHA | FND-14 | +| M-08 | S | Private report folder on Linux (`mkdtemp`-style permissions) | FND-15 | +| M-09 | S | Decide on `ProgramNest.number`: remove it or give it a use | FND-16 | +| M-10 | S | Check the `Claude Code Review` job log and its secret | FND-18 | +| M-11 | L | Translate code comments and docstrings to English, module by module, alongside other changes | PLN-11 | + +--- + +## Machine-data stages A–F + +Broken down further when the tracking system reaches stage 4. Details and +reasoning in [ROADMAP.md](ROADMAP.md#more-data-from-the-machine-files). + +| ID | Size | Task | +|---|---|---| +| DA-01 | S | Material and thickness: parse, show a column in the programs view, filter | +| DA-02 | S | Sheet consumption: done and remaining sheets | +| DA-03 | S | Part dimensions in the position window | +| DB-01 | M | Fixture generator: time per sheet, materials, thicknesses, weights, forming flag | +| DC-01 | M | Time estimate per program and to the end of the task | +| DC-02 | S | "What will close this position": programs and time left | +| DD-01 | M | Weight per position and per program, route-sheet export | +| DE-01 | S | Forming and bending, order and customer — shown only when filled | +| DF-01 | L | Research on site: executed programs from the machine control's queue and logs | + +--- + +## Open questions + +| ID | Question | Blocks | +|---|---|---| +| Q-01 | Where will the server run on the shop floor, and who maintains it? For development a local VM is enough | S6-02, rollout | +| Q-02 | Shift boundaries: times, night shift, weekends | S4-02 | +| Q-03 | How many workstations and operators; do operators move between PCs? | S2-04 (operator list per workstation) | +| Q-04 | How long to keep production data; who may see individual operators' figures | S4, S6-01 | +| Q-05 | Recording each operator's output is personal-data processing (PLN-10). Does the shop need operator notice or consent under local law? | Rollout | +| Q-06 | PIN length and lockout policy (attempts, duration) | S2-04 | From 5fc0a0340181823d74e67f83c78786f9aac8f61d Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 29 Sep 2026 05:54:06 +0000 Subject: [PATCH 3/3] Plan directory and ERP integration, test benches, system diagrams - docs/INTEGRATIONS.md: where the system sits in ISA-95, the mapping problem, integration patterns, and the integration hub: connector plugins configured in the admin panel, encrypted secrets, routing, mapping tables, an integration outbox with retries and a delivery log, and an Integration API for ERPs that read from us. Research notes on Infor SyteLine (IDO REST, ION), 1C (OData, or 1C reading our event feed) and Active Directory (LDAPS, group-to-role mapping), with sources. - docs/TEST_BENCH.md: bench nodes and what runs on each, topology, bench configurations and test scenarios. - docs/ARCHITECTURE.md: target-system diagram and an end-to-end sequence diagram (Mermaid); integrations section; settings and hub tables. - docs/TASKS.md: administrator settings (shifts, time format, retention, visibility), stage 7 (directory), stage 8 (integration hub), research and test-bench tasks; open questions resolved from the owner's answers. - docs/ROADMAP.md, FINDINGS.md, READMEs: stages 7-8, open-source scope, new planning findings. Russian versions updated. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01CW3WfPRekjK4x6M8VNRckz --- README.md | 10 +- README.ru.md | 10 +- docs/ARCHITECTURE.md | 142 ++++++++++++++++++++++++- docs/ARCHITECTURE.ru.md | 136 ++++++++++++++++++++++++ docs/FINDINGS.md | 5 +- docs/INTEGRATIONS.md | 230 ++++++++++++++++++++++++++++++++++++++++ docs/ROADMAP.md | 44 +++++++- docs/ROADMAP.ru.md | 39 ++++++- docs/TASKS.md | 222 +++++++++++++++++++++++++++++++++++--- docs/TEST_BENCH.md | 113 ++++++++++++++++++++ 10 files changed, 922 insertions(+), 29 deletions(-) create mode 100644 docs/INTEGRATIONS.md create mode 100644 docs/TEST_BENCH.md diff --git a/README.md b/README.md index 1e62628..769505d 100644 --- a/README.md +++ b/README.md @@ -271,16 +271,22 @@ yet. | 3 | Production log: every mark recorded on the server, outbox when offline, live updates across workstations | | 4 | Supervisor web page: production per operator, day, shift and task; CSV export | | 5 | Saved sessions: named bookmarks with date and time | -| 6 | Operations: backups, installation guide, update notices | +| 6 | Operations: backups, installation guide, update notices, data retention | +| 7 | Directory: sign-in with Active Directory accounts, roles from groups | +| 8 | Integration hub: pluggable ERP connectors configured by the administrator — Infor SyteLine, 1C, webhook, files — and an API for ERPs to read from | | A–F | More data from the machine files: material, time, weight, order, automatic detection of executed programs | Standalone mode stays: without a server the utility works exactly as it does -now, and program files remain read-only. +now, and program files remain read-only. The project is open source and not +tied to a particular shop: shifts, operators, retention, directory and ERP +are configured by the administrator. Details: - [docs/ROADMAP.md](docs/ROADMAP.md) — stages and decisions - [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md) — client–server design and why +- [docs/INTEGRATIONS.md](docs/INTEGRATIONS.md) — ERP and Active Directory research, integration hub design +- [docs/TEST_BENCH.md](docs/TEST_BENCH.md) — test bench nodes, configurations and scenarios - [docs/TASKS.md](docs/TASKS.md) — task breakdown with acceptance criteria - [docs/FINDINGS.md](docs/FINDINGS.md) — audit results and planning findings diff --git a/README.ru.md b/README.ru.md index 5163cd0..a33d11f 100644 --- a/README.ru.md +++ b/README.ru.md @@ -275,16 +275,22 @@ python -m pytest tests -q | 3 | Журнал выработки: каждая отметка на сервере, очередь при обрыве связи, изменения сразу видны на всех местах | | 4 | Веб-страница мастера: выработка по операторам, дням, сменам и заданиям; выгрузка в CSV | | 5 | Сессии: именованные закладки с датой и временем | -| 6 | Эксплуатация: резервные копии, инструкция по установке, уведомления об обновлениях | +| 6 | Эксплуатация: резервные копии, инструкция по установке, уведомления об обновлениях, срок хранения данных | +| 7 | Каталог: вход под учётными записями Active Directory, роли по группам | +| 8 | Интеграционный модуль: подключаемые коннекторы к ERP, настраиваемые администратором, — Infor SyteLine, 1С, webhook, файлы — и API, из которого ERP забирает данные сама | | A–F | Больше данных из файлов станка: материал, время, масса, заказ, автоматический учёт выполненных программ | Автономный режим остаётся: без сервера утилита работает так же, как сейчас, -а файлы программ по-прежнему только читаются. +а файлы программ по-прежнему только читаются. Проект открытый и не привязан +к конкретному цеху: смены, операторы, срок хранения, каталог и ERP +настраивает администратор. Подробности: - [docs/ROADMAP.ru.md](docs/ROADMAP.ru.md) — этапы и решения - [docs/ARCHITECTURE.ru.md](docs/ARCHITECTURE.ru.md) — устройство клиента и сервера и почему так +- [docs/INTEGRATIONS.md](docs/INTEGRATIONS.md) — исследование ERP и Active Directory, устройство интеграционного модуля (на английском) +- [docs/TEST_BENCH.md](docs/TEST_BENCH.md) — тестовые стенды: узлы, конфигурации, сценарии (на английском) - [docs/TASKS.md](docs/TASKS.md) — задачи с критериями готовности (на английском) - [docs/FINDINGS.md](docs/FINDINGS.md) — результаты аудита и находки при планировании (на английском) diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index 3b55745..3587d84 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -38,10 +38,126 @@ Status: **planned, not implemented.** | Supervisor | Server web page in a browser | Watches production live, exports CSV | | Administrator | Server web page | Users, workstations, backups | +Test benches for all of this: [TEST_BENCH.md](TEST_BENCH.md). + Only the operator runs the client: they need the folder with `.nc` files on the shop-floor PC, and a browser cannot read it. The supervisor and the administrator install nothing. +### Target system + +How the whole system is meant to work once all stages are done: abstract +nodes, the concrete technology on each, and the links between them. +Dashed links are optional or later stages. + +```mermaid +flowchart LR + subgraph FLOOR["Shop floor"] + CAM["CAM system
NCeXpress FMS"] + SHARE[("File share
.nc .fms .pdf")] + MACHINE["Punching machine
control"] + subgraph WS["Operator workstation
Windows 7+"] + CLIENT["Client .exe
Python 3.8 · Tk
core/ parser"] + LOCAL[("Outbox + cache
DPAPI")] + end + end + + subgraph SRV["Server · Linux Docker or Windows service"] + HTTP["Uvicorn · TLS
pinned self-signed cert"] + API["REST API /api/v1
FastAPI"] + SSE["Event stream
SSE"] + WEB["Web pages
supervisor · admin"] + SVC["Services
auth · roles · production
sessions · settings"] + DB[("Database
SQLite WAL
SQLAlchemy · Alembic")] + AUTHP["Auth providers
local · LDAP"] + subgraph HUB["Integration hub"] + REG["Connectors
plugins + settings"] + MAP["Mapping
parts to items · tasks to jobs
operators to employees"] + OUT["Integration outbox
retries · dead letter · log"] + IAPI["Integration API
tokens · event feed"] + end + BACKUP["Backup job"] + end + + subgraph OFFICE["Office"] + SUP["Supervisor
browser"] + ADM["Administrator
browser"] + end + + subgraph ENT["Enterprise systems"] + AD["Active Directory"] + ERP["Infor SyteLine"] + ONEC["1C:Enterprise"] + OTHER["Other ERP / MES
webhook · files"] + BSTORE[("Backup storage")] + end + + CAM -- "writes programs" --> SHARE + SHARE -- "programs" --> MACHINE + CLIENT -- "SMB read-only" --> SHARE + CLIENT --- LOCAL + CLIENT -- "HTTPS: sign-in, events" --> HTTP + HTTP -- "SSE: live marks" --> CLIENT + MACHINE -. "queue, logs · stage F" .-> CLIENT + + HTTP --> API + HTTP --> SSE + HTTP --> WEB + API --> SVC + WEB --> SVC + SVC --> SSE + SVC --> DB + SVC --> AUTHP + SVC --> OUT + REG --> OUT + MAP --> OUT + IAPI --> SVC + BACKUP --> DB + + SUP -- "HTTPS" --> HTTP + ADM -- "HTTPS" --> HTTP + + AUTHP -. "LDAPS · stage 7" .-> AD + OUT -. "IDO REST / ION · stage 8" .-> ERP + OUT -. "OData · stage 8" .-> ONEC + OUT -. "webhook, CSV/XML" .-> OTHER + ONEC -. "pulls event feed" .-> IAPI + BACKUP --> BSTORE +``` + +### One mark, end to end + +```mermaid +sequenceDiagram + autonumber + actor OP as Operator + participant CL as Client + participant OB as Client outbox + participant API as Server API + participant DB as Database + participant SSE as Event stream + participant SUP as Supervisor page + participant INT as Integration worker + participant ERP as ERP + + OP->>CL: ticks program PRG_07 + CL->>OB: store event (UUID, task, program, pieces) + CL-->>OP: mark shown at once + OB->>API: POST /api/v1/events (batch) + API->>DB: insert if UUID is new, set operator, workstation, received_at + API-->>OB: accepted — remove from outbox + API->>SSE: publish event + SSE-->>CL: other workstations update the task + SSE-->>SUP: totals update live + API->>INT: queue for integration + INT->>ERP: job transaction per job operation (idempotency key) + ERP-->>INT: ok, or retry later +``` + +If the server is unreachable, steps 4–11 wait in the client outbox; if the +ERP is unreachable, steps 12–13 wait in the integration outbox. Neither +stops the operator. + --- ## Why a server rather than a shared network folder @@ -141,7 +257,9 @@ the shop floor. Caching belongs on the client, for working offline (below). | `tasks` | Shift tasks: content-based ID, program names | | `events` | Production log: marks and unmarks | | `bookmarks` | Users' saved sessions | +| `settings` | Administrator settings: shifts, time format, retention, visibility | | `audit` | Sign-ins, failed attempts, administrator actions | +| `connections`, `mappings`, `integration_outbox`, `api_tokens` | Integration hub (stage 8) | Where the database and backups live is set by the administrator in the server configuration (a Docker volume or a folder on the server's disk). @@ -265,6 +383,24 @@ seconds to answer. --- +## Integrations + +Two kinds, both on the server and both configured by the administrator: + +- **Directory (stage 7).** An authentication provider interface with `local` + and `ldap` implementations: supervisors and administrators sign in with + Active Directory accounts, roles follow domain groups. Operators keep + name + PIN at the machine. +- **Integration hub (stage 8).** ERP and other systems are connected through + connector plugins configured in the admin panel: connection profiles, + encrypted secrets, routing of event types, mapping tables, an integration + outbox with retries and a delivery log, and an Integration API for systems + that prefer to read from us (typical for 1C). + +Details and research: [INTEGRATIONS.md](INTEGRATIONS.md). + +--- + ## Client–server contract - **The API is versioned**: `/api/v1/...`. Shop-floor clients are not updated @@ -297,10 +433,14 @@ server/ server — separate package with its own dependencies api/ API routes web/ supervisor and admin pages db/ schema, migrations - services/ sign-in, roles, production, sessions, export + services/ sign-in, roles, production, sessions, settings, export + auth/ authentication providers: local, ldap + integrations/ hub: registry, outbox, mapping, integration API + connectors/ built-in: webhook, filedrop, syteline, onec tests/ Dockerfile docker-compose.yml +bench/ test bench: containers and Windows VM checklists docs/ ``` diff --git a/docs/ARCHITECTURE.ru.md b/docs/ARCHITECTURE.ru.md index 87b0ff7..b06e8dc 100644 --- a/docs/ARCHITECTURE.ru.md +++ b/docs/ARCHITECTURE.ru.md @@ -42,6 +42,122 @@ цеховом компьютере, а браузер его не даёт. Мастеру и администратору ничего устанавливать не нужно. +Тестовые стенды — в [TEST_BENCH.md](TEST_BENCH.md) (на английском). + +### Целевая система + +Как система должна работать после всех этапов: узлы, конкретные решения на +каждом и связи между ними. Пунктиром — необязательное или поздние этапы. +Подписи на схемах — на английском, как и в основной версии документа. + +```mermaid +flowchart LR + subgraph FLOOR["Shop floor"] + CAM["CAM system
NCeXpress FMS"] + SHARE[("File share
.nc .fms .pdf")] + MACHINE["Punching machine
control"] + subgraph WS["Operator workstation
Windows 7+"] + CLIENT["Client .exe
Python 3.8 · Tk
core/ parser"] + LOCAL[("Outbox + cache
DPAPI")] + end + end + + subgraph SRV["Server · Linux Docker or Windows service"] + HTTP["Uvicorn · TLS
pinned self-signed cert"] + API["REST API /api/v1
FastAPI"] + SSE["Event stream
SSE"] + WEB["Web pages
supervisor · admin"] + SVC["Services
auth · roles · production
sessions · settings"] + DB[("Database
SQLite WAL
SQLAlchemy · Alembic")] + AUTHP["Auth providers
local · LDAP"] + subgraph HUB["Integration hub"] + REG["Connectors
plugins + settings"] + MAP["Mapping
parts to items · tasks to jobs
operators to employees"] + OUT["Integration outbox
retries · dead letter · log"] + IAPI["Integration API
tokens · event feed"] + end + BACKUP["Backup job"] + end + + subgraph OFFICE["Office"] + SUP["Supervisor
browser"] + ADM["Administrator
browser"] + end + + subgraph ENT["Enterprise systems"] + AD["Active Directory"] + ERP["Infor SyteLine"] + ONEC["1C:Enterprise"] + OTHER["Other ERP / MES
webhook · files"] + BSTORE[("Backup storage")] + end + + CAM -- "writes programs" --> SHARE + SHARE -- "programs" --> MACHINE + CLIENT -- "SMB read-only" --> SHARE + CLIENT --- LOCAL + CLIENT -- "HTTPS: sign-in, events" --> HTTP + HTTP -- "SSE: live marks" --> CLIENT + MACHINE -. "queue, logs · stage F" .-> CLIENT + + HTTP --> API + HTTP --> SSE + HTTP --> WEB + API --> SVC + WEB --> SVC + SVC --> SSE + SVC --> DB + SVC --> AUTHP + SVC --> OUT + REG --> OUT + MAP --> OUT + IAPI --> SVC + BACKUP --> DB + + SUP -- "HTTPS" --> HTTP + ADM -- "HTTPS" --> HTTP + + AUTHP -. "LDAPS · stage 7" .-> AD + OUT -. "IDO REST / ION · stage 8" .-> ERP + OUT -. "OData · stage 8" .-> ONEC + OUT -. "webhook, CSV/XML" .-> OTHER + ONEC -. "pulls event feed" .-> IAPI + BACKUP --> BSTORE +``` + +### Одна отметка от начала до конца + +```mermaid +sequenceDiagram + autonumber + actor OP as Operator + participant CL as Client + participant OB as Client outbox + participant API as Server API + participant DB as Database + participant SSE as Event stream + participant SUP as Supervisor page + participant INT as Integration worker + participant ERP as ERP + + OP->>CL: ticks program PRG_07 + CL->>OB: store event (UUID, task, program, pieces) + CL-->>OP: mark shown at once + OB->>API: POST /api/v1/events (batch) + API->>DB: insert if UUID is new, set operator, workstation, received_at + API-->>OB: accepted — remove from outbox + API->>SSE: publish event + SSE-->>CL: other workstations update the task + SSE-->>SUP: totals update live + API->>INT: queue for integration + INT->>ERP: job transaction per job operation (idempotency key) + ERP-->>INT: ok, or retry later +``` + +Если сервер недоступен, шаги 4–11 ждут в очереди клиента; если недоступна +ERP, шаги 12–13 ждут в очереди интеграции. Ни то, ни другое не мешает +оператору. + --- ## Почему сервер, а не общая сетевая папка @@ -259,6 +375,26 @@ Tk можно трогать только из главного потока, а --- +## Интеграции + +Два вида, оба на сервере и оба настраивает администратор: + +- **Каталог (этап 7).** Интерфейс поставщиков входа с реализациями `local` + и `ldap`: мастер и администратор входят под учётными записями Active + Directory, роли следуют доменным группам. Операторы у станка входят + по имени и PIN. +- **Интеграционный модуль (этап 8).** ERP и другие системы подключаются + через коннекторы-модули, настраиваемые в панели администратора: профили + подключений, зашифрованные секреты, маршрутизация событий, таблицы + сопоставления, очередь интеграции с повторами и журналом доставки, API + интеграции для систем, которые предпочитают забирать данные сами + (типично для 1С). + +Подробности и исследование — [INTEGRATIONS.md](INTEGRATIONS.md) (на +английском). + +--- + ## Договорённость клиента и сервера - **API версионируется**: `/api/v1/...`. Цеховые клиенты обновляются не diff --git a/docs/FINDINGS.md b/docs/FINDINGS.md index 5f86a6c..85cffaf 100644 --- a/docs/FINDINGS.md +++ b/docs/FINDINGS.md @@ -59,5 +59,8 @@ constrained a decision. | PLN-07 | Tk may only be used from the main thread | A background executor is required before any network I/O (S0-03) | | PLN-08 | `gui.py` is a single ~790-line module that mixes layout and behaviour | Split it before adding sign-in, setup and other windows (S0-02) | | PLN-09 | The previous roadmap stated that the utility "keeps no history and should not" | Reversed on purpose: history is kept on the server. Program files remain read-only | -| PLN-10 | Recording each operator's output is processing of personal data and may fall under labour and data-protection law | Open question for the shop's management before rollout (see TASKS.md, open questions) | +| PLN-10 | Recording each operator's output is processing of personal data and may fall under labour and data-protection law | The project is open source and not tied to a shop: it provides retention and visibility settings (S1-09, S6-05), and compliance is the deployer's responsibility, stated in the documentation | | PLN-11 | Code comments, docstrings and commit history are in Russian; the repository is now English-first | New code and documents in English; existing comments translated gradually (M-11) | +| PLN-12 | Infor SyteLine exposes a documented IDO REST API (load, update, invoke per IDO) and ION BODs, but has no public sandbox; cloud access goes through the ION API Gateway | The SyteLine connector is built against a mock (TB-04) and verified on a real instance later (R-01, Q-07) | +| PLN-13 | 1C:Enterprise can publish a standard OData v3 interface per infobase; 1C teams also commonly pull from external HTTP APIs with their own scheduled jobs | The integration hub supports both: an OData connector and a generic event feed (S8-04, S8-08) | +| PLN-14 | NC files carry no order or job number (`CUSTOMER` and `Order ID` are empty in the files analysed) | ERP integration needs mapping tables maintained by the administrator (S8-03) | diff --git a/docs/INTEGRATIONS.md b/docs/INTEGRATIONS.md new file mode 100644 index 0000000..aabcbd7 --- /dev/null +++ b/docs/INTEGRATIONS.md @@ -0,0 +1,230 @@ +# Integrations: ERP and directory + +Research notes on connecting the tracking system to an ERP (Infor SyteLine +first, others later) and to a corporate directory (Active Directory). + +Status: **research, not implemented.** Details about third-party products +come from public sources listed at the end and must be verified against a +real instance before any connector is built (tasks R-01…R-03 in +[TASKS.md](TASKS.md)). + +--- + +## Where the system sits + +In ISA-95 terms — the standard model of manufacturing IT — the system is a +lightweight **level-3 (manufacturing operations)** application between the +machine (level 2) and the ERP (level 4): + +| Level | What | Here | +|---|---|---| +| 4 — Business planning | ERP: orders, items, job routings, costing | SyteLine, 1C, others | +| 3 — Manufacturing operations | Execution tracking, production reporting | **This system** | +| 2 — Control | Machine control, NC programs | Punching machine, NCeXpress output | + +That position defines the two useful directions of integration: + +- **Down from the ERP (plan):** which job orders and operations a shift task + belongs to, which item each part name is. +- **Up to the ERP (actual):** quantities completed per job operation, by whom + and when — what the ERP otherwise gets from paper route sheets typed in by + hand. + +--- + +## The core problem: mapping + +Integration is mostly a mapping problem, not a transport problem. + +| Our side | ERP side | How to link | +|---|---|---| +| Part name from `PART_NAME` (e.g. a drawing number) | Item | Naming convention, or an admin-maintained mapping table | +| Shift task (content-based ID) | One or more job orders | Not in the NC files: `CUSTOMER` and `Order ID` are empty in the files analysed (ROADMAP item 8). Needs a mapping table, or the order number in the file name / CAM fields | +| Program executed on the punching machine | A job operation (routing step at the punching work centre) | Operation sequence of the punching step in the job's routing | +| Pieces of a part in a completed program | Quantity completed at that operation | Direct, once the part→item and task→job links exist | +| Operator | ERP employee | Mapping table or a shared directory identity (AD) | + +Consequences for the design: + +- **Mapping tables live on our server** and are edited by the administrator; + a connector may pre-fill them from the ERP. +- **Unmapped data is never dropped:** production is recorded regardless and + sent later, once the mapping exists. +- One NC program usually contains parts from **several jobs** (nesting), so + one "program done" event may turn into several ERP transactions. + +--- + +## Integration patterns + +| Pattern | Who calls whom | Direction | Use | +|---|---|---|---| +| **Push through a connector** | We call the ERP | up | Production events are queued in an integration outbox on the server; a connector delivers them with retries and idempotency keys. The ERP being down never blocks the shop floor | +| **Scheduled pull through a connector** | We call the ERP | down | The connector periodically loads jobs, routings, items into local reference tables | +| **Event feed** | The ERP calls us | up | The ERP side runs its own scheduled job that reads new production events from our Integration API by cursor. No write access to the ERP needed on our side — often the easiest route with 1C | +| **Reference push** | The ERP calls us | down | The ERP sends jobs and items to our Integration API when they change | +| **Webhook** | We call any URL | up | Generic HTTP POST of each event with a configurable template — for middleware and low-code tools | +| **File drop (CSV / XML)** | Shared folder | both | Universal fallback: any ERP can import a file. First connector to build — it needs no ERP access to develop | + +--- + +## Integration hub + +A server module that connects any number of external systems and is +configured by the administrator in the web panel — no code changes and no +server restart to add, change or disable a connection. + +### Parts + +| Part | What it does | +|---|---| +| **Connector registry** | Connector types are plugins. Built-in: file drop, webhook, Infor SyteLine, 1C OData. Third parties add their own as a Python package registered under the entry-point group `fpc_server.connectors` — no fork needed | +| **Connection profiles** | An administrator creates a connection from a connector type: name, base URL, authentication, TLS options, timeouts, schedule. Several connections of one type are allowed (e.g. a test and a live ERP) | +| **Settings schema** | Each connector declares its settings as a typed model; the admin form is generated from it, validated on save, and has a **Test connection** button | +| **Secrets** | Passwords, tokens and keys are encrypted at rest with a server master key supplied at deployment (environment variable or Docker secret). They are never shown again after saving and never written to logs | +| **Routing** | Which event types go to which connection (`program_done`, `program_undone`, shift totals, …), with filters by workstation or work centre | +| **Mapping** | Tables for parts → items, tasks → jobs, operators → employees, workstations → work centres. Edited in the panel, imported from CSV, or pre-filled by the connector's pull. An "unmapped" list shows what is waiting | +| **Templates** | For the webhook and file-drop connectors the payload is a template the administrator edits, with a preview on a sample event | +| **Integration outbox** | Every outgoing message is stored first, then delivered: retries with back-off, an idempotency key per message, a dead-letter list after N failures, manual retry | +| **Delivery log** | Each attempt: time, connection, result, response summary (secrets redacted). A status tile per connection: last success, backlog, last error | +| **Integration API** | For systems that call us: scoped API tokens (read production, read reference, write reference); an event feed read by cursor; reference-data upload; webhook subscriptions | + +### Connector interface + +``` +Connector + settings_model -> typed settings, used to render the admin form + test(settings) -> reachable, authenticated, version + pull_reference(settings) -> jobs, operations, items, employees + push(settings, messages) -> result per message: ok / retry / reject +``` + +A connector only translates: it receives already-mapped messages and returns +a result per message. Retries, idempotency, logging and scheduling belong to +the hub, so every connector gets them for free. + +### Authentication methods offered to connectors + +None, Basic, Bearer token, API key in a header, OAuth 2.0 client credentials, +and the Infor ION API credentials file (`.ionapi`) for SyteLine in the cloud. + +--- + +## Infor SyteLine (CloudSuite Industrial) + +### Relevant concepts + +- **Jobs** — production orders, with a **routing** of operations (work + centre, sequence). Paper **job travellers / route sheets** are printed from + them. +- **Job transactions** — reported quantities (completed, scrapped) and hours + per job operation. This is where "actual" production lands. +- **Items** — the part master. + +### Access options + +| Option | What | Fit | +|---|---|---| +| **IDO REST API** (Mongoose `IDORequestService`) | Standard CRUD per Intelligent Data Object: `…/IDORequestService/ido/load/{IDO}`, `…/ido/update/{IDO}`, `…/ido/invoke/{IDO}`; a configuration header `X-Infor-MongooseConfig`; token-based authentication on premises, OAuth 2.0 via the ION API Gateway in the cloud | **Preferred.** Synchronous, well documented, on-premises and cloud | +| **ION + BODs** | Event-driven XML Business Object Documents through Infor ION; public sources mention a job-transaction BOD for operation completions | For sites that already run ION; heavier to set up | +| **SOAP web services** | Older IDO web service | Only if REST is unavailable | +| **Direct SQL** | Reading or writing the SyteLine database | **No.** Bypasses business logic and validation; unsupported | + +### IDOs of interest (to verify) + +| IDO | Purpose | Direction | +|---|---|---| +| `SLJobs` | Job orders | pull | +| `SLJobRoutes` | Job routing operations | pull | +| `SLItems` | Items | pull | +| `SLJobtrans` | Job transactions: quantity completed or scrapped per operation | push | + +An existing commercial MES connector publicly documents writing closed +labour tickets as `SLJobtrans` records, which supports this approach. + +### Open points + +- **No public sandbox.** SyteLine is licensed; developing against it needs + access to a customer or partner instance. Until then the connector is + built against a **mock** that follows the documented API shape (see + [TEST_BENCH.md](TEST_BENCH.md)). +- Which transaction type the site uses to post punching output: a job + transaction per operation, or a Factory Track data-collection + transaction. +- Whether the job number can be carried in the NC file or its name, which + would remove most of the manual mapping. + +--- + +## Other ERPs + +| ERP | Access | Notes | +|---|---|---| +| **1C:Enterprise** (1C:ERP, 1C:UPP) | Standard OData v3 REST interface published per infobase; custom HTTP services | Common in Russia and CIS; OData gives catalogs, documents and registers without custom code | +| **Generic** | File drop, webhook, event feed | Covers anything that can import a file, accept HTTP or call HTTP | + +The connector interface stays the same; only the adapter changes. + +### 1C in more detail + +1C offers two routes, and the hub supports both: + +- **We write to 1C through OData.** The administrator publishes the + standard OData interface of the infobase; our 1C connector reads catalogs + (items, employees) and creates documents that record production output. + Which document that is depends on the 1C configuration (for example, + production stages in 1C:ERP) and must be agreed with the site's 1C team. +- **1C reads from us.** A 1C developer adds a scheduled job on the 1C side + that calls our Integration API, reads new events by cursor and posts them + with 1C's own logic. This keeps all 1C business rules inside 1C and needs + no write access for us — often what a 1C team prefers. + +The second route needs nothing 1C-specific on our side: it is the generic +event feed, documented in the API description. + +--- + +## Active Directory + +### Why + +Shops with a Windows domain do not want a second list of users and +passwords. Supervisors and administrators should sign in with their domain +accounts, and roles should follow domain groups. + +### Design + +- **Authentication provider interface** on the server: `local` (built-in + users, from stage 1) and `ldap` (AD). Designed in stage 1, LDAP added in + stage 7. +- **LDAP over TLS** (LDAPS, port 636, or StartTLS) with a service account + for look-ups; the user's own credentials verified by a bind. Library: + `ldap3` (pure Python) on the server. +- **Group → role mapping** configured by the administrator: e.g. + `CN=MES-Supervisors` → `supervisor`. +- **Operators keep PINs.** A shop-floor PIN is not a domain password. An + operator record can be *linked* to a domain account for identity, while + sign-in at the machine stays name + PIN. +- **Optional later:** single sign-on in the browser via Kerberos/SPNEGO. + +### Open points + +- Whether operators have domain accounts at all. +- Whether user records are provisioned on first sign-in or synchronised on a + schedule. + +--- + +## Sources + +Public sources consulted; verify details against a real instance. + +- [Infor docs — LoadCollection, REST API version 2](https://docs.infor.com/csi/2022.x/en-us/csbiolh/mgiiea_cl_sl/dwn1576796415221.html) +- [Infor docs — Mongoose LoadCollection](https://docs.infor.com/mg/latest/en-us/mongooseolh/mgiiea/scb1573247746553.html) +- [Infor Community — What is X-INFOR-MongooseConfig](https://community.infor.com/discussion/446/what-is-x-infor-mongooseconfig) +- [Infor Community — IDO REST API](https://community.infor.com/discussion/19177/ido-rest-api) +- [MachineMetrics — Infor SyteLine cloud connector](https://docs.machinemetrics.com/docs/integrations/erp-connectors/infor-syteline-cloud-connector/) +- [Visual South — SyteLine integration methods](https://www.visualsouth.com/blog/infor-cloudsuite-industrial-integrate-existing-systems) +- [Netray — SyteLine REST API integration](https://www.netray.co/resources/syteline-api-integration-rest) +- [1C Developer Network — REST interface](https://1c-dn.com/1c_enterprise/rest_interface/) +- [1C knowledge base — Publishing standard REST API for your infobase](https://kb.1ci.com/1C_Enterprise_Platform/FAQ/Development/Integration/Publishing_standard_REST_API_for_your_infobase/) diff --git a/docs/ROADMAP.md b/docs/ROADMAP.md index 516b551..918da5d 100644 --- a/docs/ROADMAP.md +++ b/docs/ROADMAP.md @@ -5,10 +5,12 @@ Two directions: 1. **Production tracking system** — a server with a database, role-based - sign-in, a production log, saved sessions, a supervisor web page. The - utility becomes the client at the operator's workstation. Stages 0–6 - below; architecture in [ARCHITECTURE.md](ARCHITECTURE.md); task breakdown - in [TASKS.md](TASKS.md). + sign-in, a production log, saved sessions, a supervisor web page, + directory and ERP integration. The utility becomes the client at the + operator's workstation. Stages 0–8 below; architecture in + [ARCHITECTURE.md](ARCHITECTURE.md); integrations in + [INTEGRATIONS.md](INTEGRATIONS.md); test benches in + [TEST_BENCH.md](TEST_BENCH.md); task breakdown in [TASKS.md](TASKS.md). 2. **More data from the machine files** — which other fields are useful to an operator or a supervisor and which are not worth taking. Stages A–F. Based on the output of the NCeXpress FMS postprocessor. @@ -19,6 +21,10 @@ and the system decides where and to whom they are shown. Status of everything below: **planned, not implemented.** Findings that shaped this plan are in [FINDINGS.md](FINDINGS.md). +The project is open source and not tied to a particular shop. Everything +site-specific — shifts, operators, retention, directory, ERP — is configured +by the administrator. + --- ## Production tracking system @@ -36,6 +42,9 @@ shaped this plan are in [FINDINGS.md](FINDINGS.md). | 7 | Supervisor views production and writes nothing | web | 4 | | 8 | Production export to CSV | web | 4 | | 9 | Clients work concurrently, changes show up immediately | server + client | 3, 4 | +| 10 | Administrator settings: shifts, time format, retention, visibility | server | 1 | +| 11 | Sign-in with Active Directory accounts, roles from groups | server | 7 | +| 12 | Integration hub: pluggable, configurable ERP connectors (SyteLine, 1C, webhook, files) and an API for ERPs to read from | server | 8 | ### Decisions @@ -54,6 +63,10 @@ shaped this plan are in [FINDINGS.md](FINDINGS.md). - **A session is a named bookmark**; the production log is the single source of truth about marks. - **Standalone mode stays**: without a server the utility works as today. +- **Site-specific settings belong to the administrator**: shift boundaries, + time format, operators per workstation, retention, visibility. +- **Integrations are plugins** configured in the admin panel, never + hard-wired; mapping between our data and the ERP's is data, not code. ### Stage 0. Checks and client groundwork @@ -75,6 +88,7 @@ Nothing visible changes. first administrator). - Workstations: creation, one-time registration code. - Audit log of sign-ins and administrator actions. +- System settings: shift boundaries, time format, retention, visibility. - CI: server tests on Linux and Windows. ### Stage 2. The client connects @@ -114,6 +128,26 @@ Nothing visible changes. - Scheduled backups and restore checks. - Server installation guide for IT; running as a Windows service. - Client notification about a new version. +- Data retention job. + +### Stage 7. Directory + +- Supervisor and administrator sign-in with Active Directory accounts + (LDAPS); roles from domain groups. +- Operators may be linked to domain accounts; sign-in at the machine stays + name + PIN. +- Optional browser single sign-on (Kerberos). + +### Stage 8. Integration hub + +- Connector plugins configured in the admin panel: connection profiles, + secrets, test button, routing of event types. +- Integration outbox: retries, idempotency, dead letters, delivery log. +- Mapping tables: parts to items, tasks to jobs, operators to employees. +- Integration API: tokens, event feed by cursor, reference-data upload. +- Built-in connectors: webhook, file drop, Infor SyteLine (IDO REST), + 1C (OData). Research first, against a mock ERP until a real one is + available. --- @@ -265,7 +299,7 @@ with sizes and stations. ### Order of work -Tracking system stages 0–6 first, then the machine-data stages. +Tracking system stages 0–8 first, then the machine-data stages. **Stage A.** Material and thickness, sheet consumption, part dimensions in the position window. The data is already parsed nearby. diff --git a/docs/ROADMAP.ru.md b/docs/ROADMAP.ru.md index cbed854..d15b029 100644 --- a/docs/ROADMAP.ru.md +++ b/docs/ROADMAP.ru.md @@ -9,9 +9,9 @@ Два направления: 1. **Система учёта** — сервер с базой данных, вход по ролям, журнал - выработки, сессии, веб-страница для мастера. Утилита становится клиентом - на рабочем месте оператора. - Этапы 0–6 ниже, архитектура — в [ARCHITECTURE.ru.md](ARCHITECTURE.ru.md). + выработки, сессии, веб-страница для мастера, интеграция с каталогом и + ERP. Утилита становится клиентом на рабочем месте оператора. + Этапы 0–8 ниже, архитектура — в [ARCHITECTURE.ru.md](ARCHITECTURE.ru.md). 2. **Данные из файлов станка** — какие ещё поля полезны оператору или мастеру участка и что брать не стоит. Этапы A–F. Основано на разборе выхода постпроцессора NCeXpress FMS. @@ -40,6 +40,9 @@ | 7 | Мастер смотрит выработку, сам ничего не пишет | веб | 4 | | 8 | Выгрузка выработки в CSV | веб | 4 | | 9 | Одновременная работа клиентов, изменения видны сразу | сервер + клиент | 3, 4 | +| 10 | Настройки администратора: смены, формат времени, срок хранения, видимость | сервер | 1 | +| 11 | Вход под учётными записями Active Directory, роли по группам | сервер | 7 | +| 12 | Интеграционный модуль: подключаемые и настраиваемые коннекторы к ERP (SyteLine, 1С, webhook, файлы) и API, из которого ERP забирает данные сама | сервер | 8 | ### Принятые решения @@ -56,6 +59,12 @@ - **Сессия — именованная закладка**, журнал выработки — единственный источник правды об отметках. - **Автономный режим остаётся**: без сервера утилита работает как сейчас. +- **Проект открытый и не привязан к цеху**: всё, что зависит от площадки, — + границы смен, формат времени, операторы по рабочим местам, срок хранения, + видимость цифр — настраивает администратор. +- **Интеграции — подключаемые модули**, настраиваемые в панели + администратора; сопоставление наших данных с данными ERP — это данные, + а не код. ### Этап 0. Проверки и фундамент клиента @@ -115,6 +124,28 @@ - Резервные копии по расписанию и проверка восстановления. - Инструкция по установке сервера для ИТ; запуск как службы Windows. - Уведомление клиента о новой версии. +- Удаление данных старше срока хранения. + +### Этап 7. Каталог + +- Вход мастера и администратора под учётными записями Active Directory + (LDAPS), роли по доменным группам. +- Операторов можно связать с доменными учётными записями; у станка вход + по-прежнему по имени и PIN. +- По желанию — вход в браузере без пароля (Kerberos). + +### Этап 8. Интеграционный модуль + +- Коннекторы-модули настраиваются в панели администратора: профили + подключений, секреты, кнопка проверки связи, какие события куда уходят. +- Очередь интеграции: повторы, защита от дублей, список недоставленного, + журнал доставки. +- Таблицы сопоставления: детали — номенклатура, задания — заказы, операторы + — сотрудники. +- API интеграции: токены, лента событий по курсору, загрузка справочников. +- Встроенные коннекторы: webhook, файлы, Infor SyteLine (IDO REST), 1С + (OData). Сначала исследование, до доступа к настоящей ERP — работа с + имитацией. --- @@ -275,7 +306,7 @@ ## Порядок работ -Сначала этапы 0–6 системы учёта, затем этапы данных из файлов станка. +Сначала этапы 0–8 системы учёта, затем этапы данных из файлов станка. **Этап A.** Материал и толщина, расход листов, габариты детали в окне позиции. Данные уже разбираются рядом. diff --git a/docs/TASKS.md b/docs/TASKS.md index 37c85c5..ca39a62 100644 --- a/docs/TASKS.md +++ b/docs/TASKS.md @@ -8,8 +8,9 @@ Status of every task: **not started**, unless marked otherwise. ## Conventions -- **ID** — `S-` for the tracking system (stages 0–6), `M-` for - maintenance items from the audit, `D-` for machine-data stages A–F. +- **ID** — `S-` for the tracking system (stages 0–8), `R-` for + research, `TB-` for test benches, `M-` for maintenance items from + the audit, `D-` for machine-data stages A–F. - **Size** — rough effort: **S** up to a day, **M** two to four days, **L** one to two weeks. - **Done when** — acceptance criteria. A task is not done until they hold and @@ -23,6 +24,9 @@ Status of every task: **not started**, unless marked otherwise. S0-01 ──► (gate) ──► S1 ──► S2 ──► S3 ──► S4 S0-02..06 ─────────────────► S2 S3 ──► S5 S6 runs alongside S3–S5 + S1 ──► S7 (directory, after R-03) + S3 ──► S8 (integration hub, after R-01/R-02) +TB-xx: built ahead of the stage that needs them M-xx: any time, independent ``` @@ -159,6 +163,8 @@ in tests. stored with the hash. - First-run setup: a CLI command `fpc-server create-admin` (no web page reachable without an admin). +- An **authentication provider interface** with one implementation, `local`, + so that LDAP (S7-02) plugs in without touching the rest. **Done when.** Permission checks are covered by tests for every role and route; hashes verify and reject correctly. @@ -182,6 +188,7 @@ rejection. - Users: create, change role, block and unblock, set or reset PIN/password. - Workstations: create, name, block; generate a one-time registration code (short-lived, single use). +- Operators per workstation: which operators may sign in where. **Done when.** All actions work through the web page and are recorded in the audit log (S1-07). @@ -220,6 +227,21 @@ supported client version; the OpenAPI description is exported into **Depends on.** S1-01. +### S1-09 System settings · M + +**Scope.** An admin page and a settings table for everything the +administrator configures: +- shift boundaries (any number of shifts, crossing midnight allowed); +- time format (24-hour or 12-hour) and date format for pages and exports; +- data retention period for production events; +- who may see individual operators' figures (supervisors, admins only, or + totals only). + +**Done when.** Each setting is applied everywhere it matters and covered by +tests; changes are audited (S1-07). + +**Depends on.** S1-04. + --- ## Stage 2 — The client connects @@ -376,12 +398,13 @@ programs, positions, pieces; only the effective event per program counts. ### S4-02 Shift definition · S -**Scope.** Configurable shift boundaries (e.g. 08:00–20:00, 20:00–08:00) in -the server settings; events are assigned to a shift by `received_at`. +**Scope.** Events are assigned to a shift by `received_at`, using the shift +boundaries configured by the administrator (S1-09). -**Done when.** Night shifts that cross midnight count as one shift. +**Done when.** Night shifts that cross midnight count as one shift; changing +the boundaries recomputes the totals. -**Depends on.** Open question Q-02. +**Depends on.** S1-09. ### S4-03 Supervisor page · M @@ -397,7 +420,8 @@ reload. ### S4-04 CSV export · S **Scope.** Export of the current page view; `;` separator, UTF-8 with BOM; -cells starting with `=`, `+`, `-`, `@` are escaped (FND-11). +dates and times in the configured format (S1-09); cells starting with `=`, +`+`, `-`, `@` are escaped (FND-11). **Done when.** The file opens correctly in a Russian-locale Excel; formula injection is covered by a test. @@ -466,6 +490,174 @@ personal data, dependency audit of the server. **Depends on.** S1–S5. +### S6-05 Data retention · S + +**Scope.** A scheduled job removes or anonymises production events older +than the configured retention period (S1-09); the audit log keeps a record +of each run. + +**Depends on.** S1-09, S3-02. + +--- + +## Stage 7 — Directory (Active Directory) + +Design in [INTEGRATIONS.md](INTEGRATIONS.md#active-directory). + +### S7-01 LDAP settings · S + +**Scope.** Admin page for the directory: server, LDAPS or StartTLS, CA +certificate, service account (stored as a secret), base DN, user filter; +**Test connection** button. + +**Depends on.** R-03, S1-09. + +### S7-02 LDAP authentication provider · M + +**Scope.** Supervisor and administrator sign-in with domain credentials via +`ldap3`; a user record is created on first sign-in; local accounts keep +working, at least one local administrator always remains. + +**Done when.** Sign-in works against the bench domain (TB-03); wrong +password and disabled domain accounts are rejected. + +**Depends on.** S1-03, S7-01. + +### S7-03 Group → role mapping · S + +**Scope.** Admin table mapping domain groups to roles; roles re-evaluated at +every sign-in; removing a user from the group removes access. + +**Depends on.** S7-02. + +### S7-04 Link operators to domain accounts · S + +**Scope.** Optional link between an operator record and a domain account for +identity and ERP employee mapping; sign-in at the machine stays name + PIN. + +**Depends on.** S7-02. + +### S7-05 Browser single sign-on · M · optional + +**Scope.** Kerberos/SPNEGO sign-in for the web pages on domain-joined PCs. + +**Depends on.** S7-02. + +--- + +## Stage 8 — Integration hub + +Design in [INTEGRATIONS.md](INTEGRATIONS.md#integration-hub). + +### S8-01 Hub core · L + +**Scope.** Connector registry (built-ins plus the `fpc_server.connectors` +entry-point group); connection profiles; settings models rendered as admin +forms with validation and **Test connection**; secrets encrypted with the +server master key; enable, disable, delete. + +**Done when.** A dummy connector installed as a separate package appears in +the panel, can be configured and tested, and its secrets never appear in +pages, logs or backups in clear text. + +**Depends on.** S1-05, S1-07. + +### S8-02 Integration outbox and delivery · M + +**Scope.** Outgoing messages stored before delivery; routing by event type +and filters; retries with back-off; idempotency keys; dead-letter list and +manual retry; delivery log and a status tile per connection. + +**Done when.** With the target down for an hour, messages are delivered +exactly once afterwards (scenario T-10). + +**Depends on.** S8-01, S3-02. + +### S8-03 Mapping tables · M + +**Scope.** Parts → items, tasks → jobs, operators → employees, workstations +→ work centres; editing in the panel, CSV import and export, pre-fill from a +connector's `pull_reference`; an "unmapped" list; messages wait until mapped. + +**Depends on.** S8-01. + +### S8-04 Integration API · M + +**Scope.** Scoped API tokens managed in the panel; event feed by cursor; +reference-data upload (jobs, items); webhook subscriptions; all documented in +the OpenAPI description. + +**Done when.** An external script can read every event exactly once by +cursor across restarts. + +**Depends on.** S1-08, S3-02. + +### S8-05 Webhook connector · S + +**Scope.** POST to a configurable URL with an admin-edited payload template +and preview; authentication methods from INTEGRATIONS.md. + +**Depends on.** S8-02. + +### S8-06 File-drop connector · S + +**Scope.** CSV or XML files written to a configured folder, one per batch, +atomically; optional import of reference data from files. + +**Depends on.** S8-02, S8-03. + +### S8-07 Infor SyteLine connector · L + +**Scope.** Pull jobs, routings and items through the IDO REST API; push +completed quantities as job transactions per job operation; on-premises +token and ION API (`.ionapi`) authentication. + +**Done when.** Scenario T-09 passes against ERP-MOCK; verified against a real +instance when one is available. + +**Depends on.** R-01, S8-02, S8-03, TB-04. + +### S8-08 1C OData connector · L + +**Scope.** Read catalogs (items, employees) and create production-output +documents through the published OData interface; which document is agreed +per site. + +**Depends on.** R-02, S8-02, S8-03. + +### S8-09 Connector developer guide · S + +**Scope.** `docs/CONNECTORS.md`: the interface, settings models, testing +against the bench, packaging as a plugin; an example connector in the +repository. + +**Depends on.** S8-01. + +--- + +## Research + +| ID | Size | Question | Output | +|---|---|---|---| +| R-01 | M | Infor SyteLine: confirm IDO names and properties for jobs, routings, items and job transactions; authentication on premises and via ION; which transaction the site would use for punching output | A verified section in INTEGRATIONS.md; the ERP-MOCK contract | +| R-02 | M | 1C: OData publishing, which catalogs and documents carry items, employees and production output in 1C:ERP; how a 1C scheduled job would read our event feed | A verified section in INTEGRATIONS.md | +| R-03 | S | Active Directory: LDAPS requirements, service account rights, group layout, whether operators have domain accounts | A verified section in INTEGRATIONS.md | + +--- + +## Test benches + +Layout and scenarios in [TEST_BENCH.md](TEST_BENCH.md). + +| ID | Size | Task | Needed by | +|---|---|---|---| +| TB-01 | S | `bench/docker-compose.yml` with profiles per bench configuration; SRV-LIN setup notes | S1 | +| TB-02 | S | Windows VM checklist `bench/windows.md` (WS-W7, WS-W10, SRV-WIN) | S0-01 | +| TB-03 | S | Samba AD DC container with test users and groups | S7 | +| TB-04 | M | ERP-MOCK: IDO REST imitation of jobs, routings, items; records job transactions; built from R-01 | S8-07 | +| TB-05 | S | Toxiproxy between clients and the server, with scripted fault scenarios | S3 | +| TB-06 | S | Samba share with synthetic shift folders from `tools/make_fixtures.py` | S2 | + --- ## Maintenance — from the audit @@ -509,11 +701,13 @@ reasoning in [ROADMAP.md](ROADMAP.md#more-data-from-the-machine-files). ## Open questions -| ID | Question | Blocks | +| ID | Question | Status | |---|---|---| -| Q-01 | Where will the server run on the shop floor, and who maintains it? For development a local VM is enough | S6-02, rollout | -| Q-02 | Shift boundaries: times, night shift, weekends | S4-02 | -| Q-03 | How many workstations and operators; do operators move between PCs? | S2-04 (operator list per workstation) | -| Q-04 | How long to keep production data; who may see individual operators' figures | S4, S6-01 | -| Q-05 | Recording each operator's output is personal-data processing (PLN-10). Does the shop need operator notice or consent under local law? | Rollout | -| Q-06 | PIN length and lockout policy (attempts, duration) | S2-04 | +| Q-01 | Where does the server run? | **Resolved for development:** a local VM (TEST_BENCH.md). Deployment stays site-specific; S6-02 documents Linux and Windows | +| Q-02 | Shift boundaries | **Resolved:** configured by the administrator, including the time format (S1-09) | +| Q-03 | Operators and workstations | **Resolved:** configured by the administrator (S1-05) | +| Q-04 | Retention and visibility of production data | **Resolved:** configured by the administrator (S1-09, S6-05); directory integration likely (stage 7) | +| Q-05 | Personal data and local law | **Resolved:** the project is open source and not tied to a particular shop. It provides retention and visibility settings; compliance is the responsibility of whoever deploys it, as stated in the documentation | +| Q-06 | PIN length and lockout policy | Open — default proposed in S2-04, configurable in S1-09 | +| Q-07 | Access to a real SyteLine instance for verification | Open — until then, ERP-MOCK (TB-04) | +| Q-08 | Which ERPs besides SyteLine and 1C are worth a built-in connector | Open | diff --git a/docs/TEST_BENCH.md b/docs/TEST_BENCH.md new file mode 100644 index 0000000..1d41478 --- /dev/null +++ b/docs/TEST_BENCH.md @@ -0,0 +1,113 @@ +# Test benches + +Which machines are needed to develop and test the system, what each one +runs and what it is for. Status: **planned, not built.** + +Real shop-floor equipment is not available to an open-source project, so +every external system is represented by a node that can be set up on one +computer with virtual machines and containers. + +--- + +## Nodes + +| Node | Plays the role of | OS | Runs | Used for | +|---|---|---|---|---| +| **DEV** | Developer workstation | Any (Linux, Windows, macOS) | Source code, unit tests, the client from source, `docker compose` for the server | Everyday development | +| **SRV-LIN** | Production server | Ubuntu LTS VM | The server in Docker: API, web pages, SQLite volume, backups | Reference deployment; integration tests | +| **SRV-WIN** | Production server on Windows | Windows Server VM | The server as a Windows service | Checks that nothing is Linux-only (stage 6) | +| **WS-W7** | Shop-floor operator PC | Windows 7 SP1 32-bit VM | The client `.exe` built with PyInstaller; no Python installed | The real target: TLS, SSE, DPAPI, PIN sign-in, offline work | +| **WS-W10** | Newer shop-floor PC | Windows 10/11 VM | The same client `.exe` | Behaviour on current Windows | +| **FILES** | CAM output share | Samba container on SRV-LIN, or a Windows share | Shift folders with synthetic `.nc`, `.fms`, `.pdf` from `tools/make_fixtures.py` | Operators open tasks over the network, as on the floor | +| **AD** | Corporate domain controller | Samba AD DC container, or Windows Server AD DS VM | Test domain, users, groups `MES-Supervisors`, `MES-Admins` | LDAP sign-in and group → role mapping (stage 7) | +| **ERP-MOCK** | Infor SyteLine | Container on SRV-LIN | A small HTTP service imitating the IDO REST API: jobs, routings, items; records received job transactions | ERP connector development without a SyteLine licence (stage 8) | +| **NET** | Unreliable shop network | Toxiproxy container between clients and SRV-LIN | Latency, drops, bandwidth limits on demand | Offline mode, outbox, SSE reconnects | +| **BROWSER** | Supervisor's and admin's PC | Any | A current browser | Supervisor page, admin panel | +| **CI** | Automated bench | GitHub Actions: Ubuntu and Windows runners | Client and server tests, GUI smoke tests, server started as a service for integration tests | Every pull request | + +Windows 7 is not available as a CI runner; WS-W7 checks are manual and +recorded in the pull request of each release candidate. + +--- + +## Topology + +```mermaid +flowchart LR + subgraph HOST["Host machine / hypervisor"] + DEV["DEV
source, unit tests"] + subgraph SRVLIN["SRV-LIN · Ubuntu VM"] + SERVER["Server container
FastAPI + SQLite"] + FILES["FILES
Samba share"] + ERP["ERP-MOCK
IDO REST imitation"] + AD["AD
Samba AD DC"] + NET["NET
Toxiproxy"] + end + W7["WS-W7 · Windows 7 32-bit VM
client .exe"] + W10["WS-W10 · Windows 10 VM
client .exe"] + SRVWIN["SRV-WIN · Windows Server VM
server as a service"] + end + BROWSER["BROWSER
supervisor, admin"] + + W7 -- "HTTPS + SSE" --> NET + W10 -- "HTTPS + SSE" --> NET + NET -- "HTTPS + SSE" --> SERVER + W7 -- "SMB: shift folders" --> FILES + W10 -- "SMB: shift folders" --> FILES + BROWSER -- "HTTPS" --> SERVER + SERVER -- "LDAPS" --> AD + SERVER -- "HTTPS: IDO REST" --> ERP + DEV -- "docker compose, tests" --> SRVLIN + DEV -. "service install · bench F" .-> SRVWIN + NET -. "HTTPS + SSE · bench F" .-> SRVWIN +``` + +--- + +## Bench configurations + +Not every task needs every node. + +| Bench | Nodes | For tasks | +|---|---|---| +| **A — Probe** | DEV, a throw-away probe server, WS-W7 | S0-01 | +| **B — Minimal** | DEV, SRV-LIN (server only), WS-W7, BROWSER | Stages 1–4 | +| **C — Network** | B + FILES + NET + WS-W10 | Stage 3 offline and concurrency tests; stage 5 | +| **D — Directory** | B + AD | Stage 7 | +| **E — ERP** | B + ERP-MOCK | Stage 8 | +| **F — Full** | All nodes, including SRV-WIN | Release candidates | + +--- + +## Scenarios + +Each scenario lists the bench and what "pass" means. + +| # | Scenario | Bench | Pass when | +|---|---|---|---| +| T-01 | Probe: TLS with pinning, SSE, DPAPI on Windows 7 | A | All checks pass in the probe report | +| T-02 | First run: create admin, register WS-W7 with a one-time code | B | WS-W7 appears as active; the code cannot be reused | +| T-03 | Operator signs in with PIN, marks programs; supervisor page updates live | B | Marks appear on the page within seconds | +| T-04 | Two operators on WS-W7 and WS-W10 work on the same task | C | Both see each other's marks; totals are correct | +| T-05 | Network drop during a shift (NET cuts the link for 10 minutes) | C | Marks queue up, arrive exactly once after recovery; the window never freezes | +| T-06 | Server restart during a shift | C | Clients reconnect; no lost or duplicated events | +| T-07 | Offline sign-in with the server stopped | C | Only operators who signed in before on this PC can sign in | +| T-08 | Supervisor signs in with a domain account; role from group | D | Role matches the group mapping; removing the group removes access | +| T-09 | Completed programs become job transactions in the ERP | E | ERP-MOCK records one transaction per job operation, no duplicates on retry | +| T-10 | ERP unavailable for an hour | E | Production keeps being recorded; transactions are delivered afterwards | +| T-11 | Backup and restore of the server database | B | Restored server shows the same totals | +| T-12 | Mixed client versions against one server | F | Older client gets a clear update message; nothing is corrupted | + +--- + +## Building the bench + +- **Containers** (server, Samba, Samba AD, ERP mock, Toxiproxy) are described + in a `bench/docker-compose.yml`, one command per bench configuration. +- **Windows VMs** are built by hand from a written checklist in + `bench/windows.md`: OS version, updates, no Python, where the client + `.exe` goes. Windows images cannot be shipped in an open-source repository. +- **Synthetic data only.** Shift folders come from `tools/make_fixtures.py`; + no real plant data on any bench. + +Tasks: TB-01…TB-06 in [TASKS.md](TASKS.md).