Zentrale Kassenanalyse
Lade Daten...
Wird geladen...
Wird geladen...
Wird geladen...
Stand: 2026-04-17 · Zugangsdaten & Passwörter siehe internen Passwort-Manager
Der Waagen-Updater ermöglicht die zentrale Verwaltung und automatische Verteilung von Konfigurationsupdates für DIGI SM6000-Waagen. Die Waagen holen Updates selbstständig — kein manueller SSH-Zugriff nötig.
Das System besteht aus drei Ebenen:
┌─────────────────────────────┐ ┌─────────────────────────────┐ │ Filiale (Markt 96 usw.) │ │ FTP-Distribution │ │ DIGI-Waage, Debian 10 ARM │ │ ftp.mix-server.de │ │ /root/tools/ │────▶│ /httpdocs/digi/ │ │ ├─ get_data.py (Py2) │ │ ├─ get_data.py │ │ ├─ digi_updater.py (Py2) │ │ ├─ digi_updater.py │ │ └─ scale_updater.py (Py3) │ │ └─ scale_updater.py │ │ │ └─────────────────────────────┘ │ │ ┌─────────────────────────────┐ │ │────▶│ pos.mixmarkt.info │ │ │ │ (diese Zentrale) │ │ │ │ /api/scale/heartbeat │ │ │ │ /api/scale/updates/… │ └─────────────────────────────┘ └─────────────────────────────┘
| Skript | Python | Zweck |
|---|---|---|
get_data.py | 2 | Artikel-/WGR-/Allergen-Transfer aus der Polyhedra-DB der Waage per FTP an den Filial-Server; startet anschließend Auto-Updater und scale_updater. |
digi_updater.py | 2 | Zieht neue Versionen von mix-server.de, tauscht Dateien atomar mit Rollback-Backup aus. |
scale_updater.py | 3 | Sendet Heartbeat an die Zentrale, zieht zentrale Konfigurations-Updates (ZIP) mit Rollback. |
⚠️ Wichtig: get_data.py ist Python 2, scale_updater.py ist Python 3. Daher wird scale_updater.py als Subprocess aufgerufen — niemals importiert:
subprocess.call(['python3', '/root/tools/scale_updater.py'])
get_data.py aus (z.B. bei Artikelübertragung).digi_updater.py prüft, ob neue Versionen von get_data.py, digi_updater.py oder scale_updater.py auf dem FTP-Server liegen, und aktualisiert sie atomar (temp-file + rename, .bak-Backup).scale_updater.py sendet den Heartbeat an die Zentrale (pos.mixmarkt.info).Die Waage sendet bei jedem Lauf folgende Felder. Diese landen in der DB-Tabelle scale_heartbeats und werden im Tab Waagen-Status angezeigt:
{
"store_number": "96",
"scale_ip": "172.16.96.31",
"hostname": "SM6000",
"mac_address": "00:60:03:0a:a8:84",
"model": "TWS SM6000",
"firmware": "10",
"arch": "armv7l",
"os_version": "Debian GNU/Linux 10 (buster)",
"disk_free_mb": 4149,
"disk_total_mb": 13880,
"uptime_seconds": 60677,
"plu_count": 2358,
"printfmt_count": 74,
"txt_count": 230,
"ingdt_count": 955,
"allergen_count": -1,
"get_data_ver": "2.2",
"gefl_labelformat": "38",
"installed_version": "",
"last_update_at": "",
"scale_token": ""
}
| Endpunkt | Methode | Zweck |
|---|---|---|
/api/scale/heartbeat | POST | Waage meldet sich; Response enthält optionales Update |
/api/scale/updates/:id/zip | GET | Waage lädt Update-ZIP (authentifiziert via scale_token) |
/api/scale/updates/:id/report | POST | Waage meldet Install-Ergebnis |
/api/admin/scale-heartbeats | GET | Dashboard: Liste aller bekannten Waagen (Admin) |
Beim ersten Kontakt einer Waage wird automatisch ein sicheres Token generiert und zurückgegeben. Die Waage speichert es in /root/tools/marktinfo.ini unter dem Schlüssel scale_token. Alle weiteren Anfragen (ZIP-Download, Status-Meldung) werden mit diesem Token authentifiziert. Ohne gültiges Token wird der Zugriff verweigert.
Token zurücksetzen (Neuregistrierung erzwingen): UPDATE scale_heartbeats SET token_hash=NULL WHERE store_number='96' AND scale_ip='172.16.96.31';
update.zip
├── META.txt # MinFirmware=24.50 (optional)
├── csv/
│ ├── printfmt.csv # Etikettenformat-Tabelle
│ ├── printfmt2.csv
│ ├── text.csv # Texte
│ ├── freetext.csv
│ ├── dept.csv
│ ├── tax.csv
│ ├── allergen.csv
│ ├── preset.csv
│ └── cabinet.csv
└── files/ # (optional) Dateien zum Kopieren
└── opt/pcscale/...
Nur CSVs, die im ZIP vorhanden sind, werden importiert. Fehlende Tabellen bleiben unverändert. Spalten im CSV, die in der Waagen-Datenbank nicht existieren, werden automatisch ignoriert (Schema-Kompatibilität).
MinFirmware=24.50 — Update wird nur installiert, wenn die Firmware-Version der Waage ≥ diesem Wert ist.Gesicherte Tabellen: printfmt, printfmt2, printfmt3, printfmt4, text, freetext, dept, tax, allergen, preset, cabinet
Backups werden unter /root/tools/backups/backup_YYYYMMDD_HHMMSS/ gespeichert. Es werden maximal 5 Backups aufbewahrt — älteste werden automatisch gelöscht.
marktinfo.ini steuerbar).2025-04).24.50).stable oder canary).Ein bestehendes Update kann jederzeit mit ↑ ZIP ersetzt oder mit ✕ gelöscht werden.
| Status | Bedeutung |
|---|---|
downloaded | ZIP heruntergeladen, noch nicht installiert |
backup_created | Tabellen-Backup erfolgreich erstellt |
installed | Update erfolgreich installiert |
failed | Fehler beim Installieren (Details in Log) |
rolled_back | Rollback aus Backup durchgeführt |
skipped | Update war bereits installiert |
Die Python-Skripte werden über FTP verteilt. Öffentliche Download-URL: https://mix-server.de/digi/
FTP-Upload-Pfad: ftp.mix-server.de → /httpdocs/digi/ (Zugangsdaten im Passwort-Manager)
Beispiel-Upload:
curl -T get_data.py --ftp-ssl --insecure \ --user "<user>:<pass>" \ "ftp://ftp.mix-server.de/httpdocs/digi/get_data.py"
⚠️ Häufiger Fehler: Ohne /httpdocs/ liefert der Server 550 Access denied. Das Root des FTP-Users ist /, nicht /httpdocs/.
Beim nächsten Waagen-Lauf zieht sich digi_updater.py die neue Version automatisch.
/root/tools/marktinfo.ini — Konfiguration (kunden_nr, scale_token, gefl_labelformat, …)/root/tools/update_state.json — Zuletzt installierte Update-Version/root/tools/backups/ — Tabellen-Backups/root/tools/scale_updater.log — Log-Datei/root/tools/scale_updater.py — Updater-Skript (Python 3)/root/tools/get_data.py — Haupt-Skript (Python 2)/root/tools/digi_updater.py — Auto-Updater (Python 2)Python-Versionen auf DIGI-Waagen: 2.7.16 + 3.7.3 (beides installiert).
16.1 Dashboard zeigt leere Felder (Hostname, MAC, PLU-Count …)
Ursache: DB-Migration nicht gelaufen — Spalten fehlen in Postgres. Tritt auf, wenn eine alte server.js ohne die newCols-Einträge läuft.
Diagnose: Spaltenzahl prüfen (erwartet: 23; wenn nur 11 → Migration fehlt):
docker compose exec db psql -U cockpit -d cockpit -c \ "SELECT column_name FROM information_schema.columns \ WHERE table_name='scale_heartbeats' ORDER BY ordinal_position;"
Lösung: Neue server.js per scp hochladen, dann docker compose restart api. Der Restart triggert ensureScaleTables(), das per ALTER TABLE ADD COLUMN IF NOT EXISTS die fehlenden Spalten nachzieht.
16.2 scale_updater fehlgeschlagen: SyntaxError(…flush=True…)
Ursache: get_data.py (Python 2) versucht scale_updater.py (Python 3) zu importieren. Python 2 kann print(x, flush=True) nicht parsen.
Lösung: Niemals import scale_updater — immer als Subprocess: subprocess.call(['python3', '/root/tools/scale_updater.py']). Dieser Fix ist in get_data.py seit 2026-04-17 enthalten.
16.3 FTP-Upload: 550 Access denied
Ursache: Falscher Pfad. FTP-User landet in /, nicht in /httpdocs/. Ziel ist /httpdocs/digi/.
Lösung: Immer den vollen Pfad verwenden (ftp://…/httpdocs/digi/datei.py).
16.4 Waage nicht erreichbar (SSH-Timeout)
ping 172.16.96.XX — Waage im Netzwerk?16.5 Heartbeat kommt, aber available_update nicht gesetzt
Waage meldet installed_version = aktueller Ziel-Version, oder es gibt keinen aktiven scale_updates-Eintrag für den Kanal stable.
SELECT * FROM scale_updates WHERE active = TRUE AND channel = 'stable' ORDER BY released_at DESC LIMIT 5;
Vollständige technische Dokumentation (inkl. Zugangsdaten):
E:\PROJEKTE\DRS-Updater\central-cockpit\docs\waagen-de.md (Deutsch)E:\PROJEKTE\DRS-Updater\central-cockpit\docs\waagen-ru.md (Русский)E:\GitHub\mono\tools\waage\README.md (Quick-Reference im Quellcode)Дата: 2026-04-17 · Учётные данные и пароли — во внутреннем менеджере паролей
Апдейтер весов обеспечивает централизованное управление и автоматическое распространение обновлений конфигурации для весов DIGI SM6000. Весы загружают обновления самостоятельно — ручной доступ по SSH не требуется.
Система состоит из трёх уровней:
┌─────────────────────────────┐ ┌─────────────────────────────┐ │ Магазин (Markt 96 и т.д.) │ │ FTP-распространение │ │ Весы DIGI, Debian 10 ARM │ │ ftp.mix-server.de │ │ /root/tools/ │────▶│ /httpdocs/digi/ │ │ ├─ get_data.py (Py2) │ │ ├─ get_data.py │ │ ├─ digi_updater.py (Py2) │ │ ├─ digi_updater.py │ │ └─ scale_updater.py (Py3) │ │ └─ scale_updater.py │ │ │ └─────────────────────────────┘ │ │ ┌─────────────────────────────┐ │ │────▶│ pos.mixmarkt.info │ │ │ │ (эта центральная панель) │ │ │ │ /api/scale/heartbeat │ │ │ │ /api/scale/updates/… │ └─────────────────────────────┘ └─────────────────────────────┘
| Скрипт | Python | Назначение |
|---|---|---|
get_data.py | 2 | Передача артикулов/товарных групп/аллергенов из базы Polyhedra весов по FTP на сервер магазина; затем запускает авто-апдейтер и scale_updater. |
digi_updater.py | 2 | Скачивает новые версии с mix-server.de, атомарно меняет файлы с бэкапом для отката. |
scale_updater.py | 3 | Отправляет heartbeat в центр, получает центральные обновления конфигурации (ZIP) с возможностью отката. |
⚠️ Важно: get_data.py работает на Python 2, scale_updater.py — на Python 3. Поэтому scale_updater.py вызывается как подпроцесс — никогда не импортируется:
subprocess.call(['python3', '/root/tools/scale_updater.py'])
get_data.py (например, при передаче товаров).digi_updater.py проверяет, есть ли новые версии get_data.py, digi_updater.py или scale_updater.py на FTP-сервере, и обновляет их атомарно (временный файл + rename, бэкап .bak).scale_updater.py отправляет heartbeat в центр (pos.mixmarkt.info).Весы при каждом запуске отправляют следующие поля. Они сохраняются в таблицу БД scale_heartbeats и отображаются во вкладке Waagen-Status:
{
"store_number": "96",
"scale_ip": "172.16.96.31",
"hostname": "SM6000",
"mac_address": "00:60:03:0a:a8:84",
"model": "TWS SM6000",
"firmware": "10",
"arch": "armv7l",
"os_version": "Debian GNU/Linux 10 (buster)",
"disk_free_mb": 4149,
"disk_total_mb": 13880,
"uptime_seconds": 60677,
"plu_count": 2358,
"printfmt_count": 74,
"txt_count": 230,
"ingdt_count": 955,
"allergen_count": -1,
"get_data_ver": "2.2",
"gefl_labelformat": "38",
"installed_version": "",
"last_update_at": "",
"scale_token": ""
}
| Endpoint | Метод | Назначение |
|---|---|---|
/api/scale/heartbeat | POST | Весы отчитываются; ответ содержит опциональное обновление |
/api/scale/updates/:id/zip | GET | Весы скачивают ZIP (аутентификация через scale_token) |
/api/scale/updates/:id/report | POST | Весы отчитываются о результате установки |
/api/admin/scale-heartbeats | GET | Дашборд: список всех известных весов (admin/support) |
При первом обращении весов автоматически генерируется защищённый токен и возвращается в ответе. Весы сохраняют его в /root/tools/marktinfo.ini под ключом scale_token. Все последующие запросы (скачивание ZIP, отчёт о статусе) аутентифицируются этим токеном. Без действующего токена доступ запрещён.
Сброс токена (принудительная перерегистрация): UPDATE scale_heartbeats SET token_hash=NULL WHERE store_number='96' AND scale_ip='172.16.96.31';
update.zip
├── META.txt # MinFirmware=24.50 (необязательно)
├── csv/
│ ├── printfmt.csv # Таблица форматов этикеток
│ ├── printfmt2.csv
│ ├── text.csv # Тексты
│ ├── freetext.csv
│ ├── dept.csv
│ ├── tax.csv
│ ├── allergen.csv
│ ├── preset.csv
│ └── cabinet.csv
└── files/ # (необязательно) Файлы для копирования
└── opt/pcscale/...
Импортируются только те CSV, которые есть в ZIP. Отсутствующие таблицы не затрагиваются. Столбцы CSV, которых нет в базе данных весов, автоматически игнорируются (совместимость схемы).
MinFirmware=24.50 — обновление устанавливается только если версия прошивки весов ≥ этого значения.Таблицы, включаемые в бэкап: printfmt, printfmt2, printfmt3, printfmt4, text, freetext, dept, tax, allergen, preset, cabinet
Бэкапы хранятся в /root/tools/backups/backup_YYYYMMDD_HHMMSS/. Хранится не более 5 бэкапов — старые удаляются автоматически.
marktinfo.ini).2025-04).24.50).stable или canary).Существующее обновление можно заменить с помощью ↑ ZIP или удалить кнопкой ✕.
| Статус | Значение |
|---|---|
downloaded | ZIP скачан, ещё не установлен |
backup_created | Резервная копия таблиц успешно создана |
installed | Обновление успешно установлено |
failed | Ошибка при установке (подробности в журнале) |
rolled_back | Выполнен откат из резервной копии |
skipped | Обновление уже было установлено |
Python-скрипты распространяются через FTP. Публичный URL для скачивания: https://mix-server.de/digi/
FTP-путь для загрузки: ftp.mix-server.de → /httpdocs/digi/ (учётные данные — в менеджере паролей)
Пример загрузки:
curl -T get_data.py --ftp-ssl --insecure \ --user "<user>:<pass>" \ "ftp://ftp.mix-server.de/httpdocs/digi/get_data.py"
⚠️ Частая ошибка: без /httpdocs/ сервер возвращает 550 Access denied. Корень FTP-пользователя — это /, а не /httpdocs/.
При следующем запуске весов digi_updater.py автоматически загрузит новую версию.
/root/tools/marktinfo.ini — конфигурация (kunden_nr, scale_token, gefl_labelformat, …)/root/tools/update_state.json — последняя установленная версия обновления/root/tools/backups/ — резервные копии таблиц/root/tools/scale_updater.log — файл журнала/root/tools/scale_updater.py — скрипт апдейтера (Python 3)/root/tools/get_data.py — основной скрипт (Python 2)/root/tools/digi_updater.py — авто-апдейтер (Python 2)Версии Python на весах DIGI: 2.7.16 + 3.7.3 (оба установлены).
16.1 Дашборд показывает пустые поля (hostname, MAC, PLU-count …)
Причина: миграция БД не отработала — колонок нет в Postgres. Это происходит при запущенной старой server.js, у которой в ensureScaleTables() ещё не было новых записей newCols.
Диагностика: проверить количество колонок (ожидается 23; если только 11 — миграция отсутствует):
docker compose exec db psql -U cockpit -d cockpit -c \ "SELECT column_name FROM information_schema.columns \ WHERE table_name='scale_heartbeats' ORDER BY ordinal_position;"
Решение: загрузить новый server.js через scp, затем docker compose restart api. Рестарт триггерит ensureScaleTables(), который добавляет недостающие колонки через ALTER TABLE ADD COLUMN IF NOT EXISTS.
16.2 scale_updater fehlgeschlagen: SyntaxError(…flush=True…)
Причина: get_data.py (Python 2) пытается импортировать scale_updater.py (Python 3). Python 2 не может распарсить print(x, flush=True).
Решение: никогда не делать import scale_updater — всегда как подпроцесс: subprocess.call(['python3', '/root/tools/scale_updater.py']). Это исправление включено в get_data.py с 2026-04-17.
16.3 FTP-загрузка: 550 Access denied
Причина: неверный путь. FTP-пользователь оказывается в /, а не в /httpdocs/. Цель — /httpdocs/digi/.
Решение: всегда использовать полный путь (ftp://…/httpdocs/digi/file.py).
16.4 Весы недоступны (таймаут SSH)
ping 172.16.96.XX — весы в сети?16.5 Heartbeat приходит, но available_update не установлено
Весы сообщают installed_version равную текущей целевой версии, или нет активной записи в scale_updates для канала stable.
SELECT * FROM scale_updates WHERE active = TRUE AND channel = 'stable' ORDER BY released_at DESC LIMIT 5;
Полная техническая документация (включая учётные данные):
E:\PROJEKTE\DRS-Updater\central-cockpit\docs\waagen-de.md (Deutsch)E:\PROJEKTE\DRS-Updater\central-cockpit\docs\waagen-ru.md (Русский)E:\GitHub\mono\tools\waage\README.md (краткий справочник в репозитории)