Skip to content

L2 Marking System — Gap Analysis & Implementation Plan

Scope: new technical specification for L2 Server (MARS Russia, LUZ / NOV / MIR / RND sites, DRY + WET variants). This document compares the requirements against the existing OKTO Terminal codebase (edge-service, factory-server, common, operator-ui, management-dashboard, OLD_APP) and defines the work required to close the gaps.

Target totals: 20 DRY cabinets (Industrial PC + power/PLC cabinet) and 31 WET cabinets (Industrial PC + power cabinet with UPS).


0. Reference architecture (from the spec)

Two physical variants share one logical architecture:

                              ┌────────────────────┐
                              │ Thin client (HMI)  │
                              │  Weintek / browser │
                              └──────────┬─────────┘
                                         │ HTTPS (web UI)
┌────────────────────────────────────────┴─────────────────────────────┐
│                            L2 SERVER                                  │
│   (RED OS / Debian, Industrial PC, optional co-located thin client)  │
│                                                                       │
│  ┌──────────┐  ┌───────────┐  ┌──────────┐  ┌─────────────────────┐ │
│  │ REST+WS  │  │ Driver    │  │ Batch /  │  │ Journal & audit log │ │
│  │ API      │  │ manager   │  │ aggreg.  │  │ (structured, HMI)   │ │
│  └──────────┘  │ (scanners,│  │ engine   │  └─────────────────────┘ │
│                │  printers,│  └──────────┘                           │
│  ┌──────────┐  │  PLCs)    │                 ┌────────────────────┐  │
│  │ L3 buffer│  └───────────┘                 │ Local file logger, │  │
│  │ sync     │                                │ CSV / JSON / XML   │  │
│  └──────────┘                                │ export             │  │
└───────────────────┬──────────────────────────┴────────────────────┬─┘
                    │                                               │
                    │ Modbus TCP / RTU, OPC UA, Profinet,           │
                    │ EtherNet/IP, TCP Socket, GPIO                 │
                    │                                               │
┌──────────┐  ┌───────────┐  ┌──────────┐  ┌──────────┐  ┌──────────┐
│ Up to 10 │  │ Printers  │  │ PLC      │  │ UPS      │  │ Network  │
│ scanners │  │ (Markem,  │  │ Inovance │  │ OWEN     │  │ switch   │
│ (Cognex, │  │  Videojet,│  │ AM521 /  │  │ IBP120K  │  │ Huawei   │
│ Datalogic│  │  Novexx,  │  │ AM522    │  │ (GPIO-   │  │ S5735    │
│ Hikrobot)│  │  mobile)  │  │          │  │  monitored)│  │          │
└──────────┘  └───────────┘  └──────────┘  └──────────┘  └──────────┘

The DRY variant adds a full Control cabinet with PLC + managed switch + modular distribution. The WET variant drops the PLC cabinet and keeps only Power cabinet (UPS, breakers, terminals, reserved space for remote Point I/O) — a L2 Server that talks to already-deployed line PLCs over the fieldbus.


1. Codebase baseline

High-level snapshot of what we already have on master:

  • edge-service — Kotlin/Ktor L2 runtime.
  • Device drivers: ModbusClient (Modbus TCP only), ScannerClient (TCP + Serial + matrix framing), PrinterClient (Videojet, Markem, Solmark, Lineko, Domino, ZPL).
  • Services: BottleService, BatchService, PalletService, ScannerService (incl. duplicate detection / rejection), PrinterService, OperationModeService, TaskService, OfflineQueueService, CloudOperationService, DeviceProvisioningService, ConnectionModeService, HardwareStatusService, ScrollerService, CommandHandlerService, ServerConnectionService (persistent WS to factory).
  • Persistence: SQLite via Exposed (bottles, batches, pallets, op_queue, config).
  • API: REST under /api/v1/** + /ws for events.
  • factory-server — Kotlin/Ktor multi-device server.
  • REST + JWT auth (ADMIN / MANAGER / OPERATOR / VIEWER), AuthService, CommandDispatchService, DeviceConnectionRegistry, FirmwareService, CloudSyncService → OKTO cloud.
  • /ws/device (device push/pull), /ws/dashboard.
  • Tables: devices, aggregated_*, sync_log, cloud_sync_queue, copacking_tasks, device_metrics, alerts, device_commands, device_logs, device_groups, device_group_members, firmware_releases, firmware_deployments, device_configs, audit_log.
  • operator-ui — React/TS SPA for the terminal (scan / batch / modes / settings / scroller / pallets / admin).
  • management-dashboard — React/TS multi-device console (devices, groups, firmware, audit, overview, sync, command palette).
  • OLD_APP — legacy Android codebase kept as reference for MARS migration.

Already in place (good news for migration)

  • Modbus register set matching MARS PLC (watchdog, print status, rejection, GTIN/GRD/BOX_ID, counters, settings @ 0x1211–0x1237).
  • All MARS operation modes present: copacking, task, task-codes, scroll, aggregation (standard, separated, CPC, manual, two-terminals, four-scanners, group-scan), pallets, watchdog, Mars vs Default profile, AFK timeouts.
  • Scanner framing (RE$#…#$PST$#, #BOX#…#CODE#), error codes (U000000/B000000/UMULT/BMULT), rejection feedback messages (#ERROR#/#BOX_ERROR#/#LINESENDERROR#/#BOX_CREATED#), 5s heartbeat with exponential-backoff reconnect.
  • Device management (remote commands, firmware staging with sha256, log pull, OS reboot/shutdown, push_config, exec_shell allow-list).
  • Structured ActionLogger (MDC → Loki) and OpenTelemetry export stubs.
  • Persistent WS (/ws/device) between edge and factory with CommandResult / CommandProgress / arbitrary telemetry.
  • Offline queue, dead-letter, two connection modes (DIRECT_CLOUD / VIA_LOCAL_SERVER).

Notable gaps detected up-front

  1. No PLC protocols other than Modbus TCP (spec requires Modbus RTU, OPC UA, Profinet, EtherNet/IP, raw TCP Socket).
  2. Scanner drivers are generic TCP/serial only — no vendor-specific adapters (Cognex DataMan, Datalogic Matrix, Hikrobot ID, handheld Gryphon/QuickScan).
  3. Printer drivers are limited — no Novexx labeller, no SDX60/SDX65, no mobile-printer protocol (Zebra/TSPL over BT/USB).
  4. Only one Modbus client per device; the spec demands up to 10 scanners and multiple heterogeneous PLCs concurrently.
  5. No per-device parametrisation UI for "количество и функционал подключаемого оборудования" (e.g. 3 scanners: add / remove / verify- against-local-buffer). The current UI is mostly single-terminal.
  6. No GPIO integration (required for UPS-aware shutdown on WET and indicator lights / "Сброс аварии" button on both variants).
  7. No UPS monitoring (OWEN IBP120K) — neither serial/USB poll nor NUT integration. WET variant explicitly needs this.
  8. No explicit batch-accounting mode ("Партионный учёт" — mark secondary packaging with batch code + verify scanned codes belong to an L3 buffer). The closest we have is batch fixation inside pallets.
  9. No structured journal viewer in the operator UI (HMI-style events with acknowledge / filter / export to CSV/JSON/XML, microsecond timestamps).
  10. No thin-client web visualisation endpoint (two isolated iframes: L2 GUI + PLC GUI); the PLC part must be embeddable.
  11. PrinterType.NONE used internally leaks to the public enum; printer count is hard-coded to 1; no simultaneous multi-printer orchestration.
  12. No serial over RS-485 adapter for Modbus RTU, and no pluggable multi-PLC driver registry.
  13. No mobile / handheld scanner gateway (USB-HID barcode + wedge profile support in the kiosk UI).
  14. README and config docs describe MARS-style line but do not describe the physical cabinet variants (DRY / WET) nor rollout counts.
  15. No migration path from the existing production L2 install (MARS Android app) — today we have OLD_APP/ as a reference tree only; no tooling exists for data / settings import.

These items are broken down in the next sections, each with a concrete implementation plan.


2. Functional gap analysis

Legend for Status:

  • Implemented — meets the spec.
  • 🟡 Partial — present but missing features / different scope.
  • Missing — not implemented.

2.1 Architecture & deployment

# Requirement Status Notes / current location Action
A1 Client-server L2 (thick server + thin client web UI) edge-service (Ktor) + operator-ui SPA, kiosk mode scripts Keep.
A2 OS: RED OS / Debian, aarch64/x86_64 🟡 Debian/Ubuntu supported via install.sh, Docker. No RED OS image / package yet. Add RED OS install profile (RPM + systemd unit), CI job to build .rpm.
A3 UPS-aware behaviour when co-located L2 Server on Industrial PC No UPS integration See §2.9 GPIO/UPS.
A4 GPIO for equipment control (UPS battery shutdown, indicator LEDs, "Сброс аварии") No GPIO code See §2.9 GPIO/UPS.
A5 Migrate existing MARS L2 installs 🟡 OLD_APP/ kept as reference. No import tool. Build okto-migrate CLI that consumes SharedPreferences + SQLite of the Android app and writes to edge-service config + SQLite.
A6 Industrial PC doubles as thin client (local browser) kiosk/install-kiosk.sh starts Chromium on operator-ui. Document as supported topology.
A7 Operator can view current job, event log, device state 🟡 Dashboard pages exist; journal is ad-hoc. See §2.8 Journaling.
A8 Web visualisation must let the PLC team embed their own PLC UI Single SPA, no sub-iframe. Add plc-visualisation section with iframe from configurable URL (line.plcVisualisationUrl).
A9 Architecture diagram (components + protocols) Old diagram in README covers terminal, not full L2 Add this doc (§0) and link from README.

2.2 Printing of marking codes

Spec requires:

  • Markem Image 2200, 5800, SDX60, SDX65
  • Novexx labellers
  • Videojet (TTO + direct labellers)
  • Mobile printers over industrial protocols
  • Per-model parametrisation: resolution, orientation, fonts, label format…
# Requirement Status Notes Action
P1 Markem 2200/5800 (GEM 9040 protocol) 🟡 MarkemPrinterClient handles STX/ETX command + ~JS1 template select. Not validated on SDX60/65 or 5800. Validate on each model, add model-aware dialect (SDX uses slightly different templates + port/TLS on newer firmware).
P2 Markem SDX60 / SDX65 No entry in PrinterType Add MARKEM_SDX variant, default port 21000, confirm new request/response framing from SDX manual.
P3 Novexx labellers (XLP / DPM / XDT ALX 92) Not in PrinterType Add NOVEXX type. Implement ESSI / JetScript driver over TCP 9100 or RS-232. Support setData, print, getStatus (?$ACK, ?$FEH). Provide template upload.
P4 Videojet TTO + direct-labellers 🟡 VideojetPrinterClient handles the classic ASCII protocol @ 8888. Add matrix of Videojet-specific commands for 6230/6330 TTO (JOB, STARTPRINT, STOPPRINT, SETDATE).
P5 Mobile printers (BT/USB) Only TCP supported. Add USB printer adapter (Linux /dev/usb/lp*, CUPS raw), BT-SPP adapter (via BlueZ), ZPL + TSPL dialects.
P6 Configurable per-model parameters (resolution, orientation, fonts, label format) PrinterConfig has only host/port/timeout Extend PrinterConfig with parameters: Map<String,String> + typed sub-configs per driver. Expose in Settings UI.
P7 Multi-printer per line (spec requires several printers simultaneously) PrinterService assumes single printer Replace with PrinterManager (like ScannerManager): indexed printers + per-index template/form command/state.
P8 Simulator / loopback printer for testing 🟡 Test mode exists; no printer emulator Add LoopbackPrinterClient implementing PrinterClient for e2e tests and smoke.
P9 Template library stored on L2 (not just the printer) Printer-side templates only Add print_templates table + /api/v1/printer/{index}/templates upload/select/delete.

Implementation plan (printers):

  • Extract ScrollDialects out of PrinterService; add driver registry:
interface PrinterDriver {
    val type: PrinterType
    suspend fun open(config: PrinterConfig): PrinterClient
    fun defaults(): PrinterConfig
    fun paramSchema(): List<PrinterParam>  // drives UI settings form
}

object PrinterDrivers {
    fun forType(t: PrinterType): PrinterDriver = ...
}
  • Add drivers for MARKEM_SDX, NOVEXX, VIDEOJET_TTO, MOBILE_ZPL, MOBILE_TSPL, LOOPBACK.
  • Introduce PrinterManager(val configs: List<PrinterConfig>) mirroring ScannerManager with getPrinter(index) / getStatus() / printOn(index, code).
  • Persist multi-printer config in edge-service.yaml as a list; expose PUT /api/v1/printers in the REST API.
  • Add Prometheus metrics per printer: printer_jobs_total, printer_errors_total, printer_buffer_size.

2.3 Scanner connectivity

Spec requires up to 10 scanners simultaneously from: Cognex DataMan (DM series — 475, 370), Hikrobot ID (3000, 5000…), Datalogic Matrix (300N, 450), plus Gryphon / QuickScan handhelds. Supported transports: TCP Socket, EtherNet/IP, Modbus, RS-232, USB, TCP/IP.

# Requirement Status Notes Action
S1 TCP Socket scanner TcpScannerClient Keep.
S2 RS-232 / USB serial scanner SerialScannerClient Keep; extend with custom baud / parity from config.
S3 Vendor frames: Cognex DataMan 🟡 Generic matrix framing works with many DataMan setups Add CognexDataManClient with |>TRIGGER<|, CONFIG, LON/LOFF Lua commands, calibration endpoint.
S4 Vendor: Hikrobot ID Not supported Add HikrobotIdClient (proprietary TCP or via Modbus TCP; supports JSON events on port 2001).
S5 Vendor: Datalogic Matrix + handhelds 🟡 Matrix works via default framing; handheld (HID) needs wedge Add DatalogicMatrixClient (SCOMM/Host Mode protocol), DatalogicHidScannerClient (Linux evdev keyboard wedge).
S6 Up to 10 scanners per L2 🟡 ScannerManager supports many entries, but combined flow has a bug (see below) and most services assume 1 Fix combined flow; expose per-scanner function role (ADD / REMOVE / VERIFY).
S7 EtherNet/IP transport for scanners Not implemented Introduce EtherNetIpTransport (using libplctag JNI or etherip Java library).
S8 Modbus-based scanners Not implemented Add ModbusScannerClient reading code registers.
S9 Per-scanner role (add, remove, verify) Not in config Add ScannerRole enum (ADD, REMOVE, VERIFY_LOCAL_BUFFER, REJECTION, PACKAGE) + route events through service.
S10 Parametrisation UI for scanners 🟡 Minimal settings dialog exists Build a first-class "Settings → Scanners → [count] + per-scanner {IP, model, role, port}" page.
S11 Vendor enum in ScannerType 🟡 Currently MATRIX/BOX/REJECTION/TCP/SERIAL (transport-only) Split: keep transport in ScannerTransport, add ScannerModel (COGNEX_DATAMAN, HIKROBOT_ID, DATALOGIC_MATRIX, DATALOGIC_HANDHELD, GENERIC).

Implementation plan (scanners):

  • New module edge-service/device/scanner/drivers/ with one file per vendor.
  • ScannerConfig gains model: ScannerModel, role: ScannerRole, parameters: Map<String, String>.
  • ScannerManager.getCombinedScanFlow() uses merge(...flows) instead of the current for-each-collect that only yields from the first scanner.
  • ScannerService routes events by role:
  • ADDbottleService.createBottles(...)
  • REMOVEbottleService.cancelByIdentifier(...)
  • VERIFY_LOCAL_BUFFER → matches against a new SessionCodeBuffer (in-memory LRU with DB mirror) and triggers rejection on mismatch.
  • REJECTION → continues current behaviour.
  • Multi-scanner status exposed on GET /api/v1/scanners (returns per-index state) and in StatusService.getPrometheusMetrics().

2.4 Operation modes ("Режимы работы L2")

Spec:

  1. Марк. единиц продукции (single-code marking)
  2. Агрегация (unit → box → pallet)
  3. Смешанный (marking + aggregation)
  4. Партионный учёт (batch codes on secondary + verify against L3 buffer)
# Requirement Status Notes Action
M1 Marking units ScannerService + BottleService Keep.
M2 Aggregation hierarchy PalletService, aggregation modes in OperationModes Keep.
M3 Mixed mode 🟡 Possible by enabling scan + aggregation Validate + add explicit "MIXED" UI toggle.
M4 Batch accounting — mark secondary, verify against L3 buffer Not present New feature BatchAccountingService: (1) pull batch/party buffer from L3; (2) let an ADD scanner enqueue codes; (3) let a VERIFY scanner match against the buffer; reject/stop on mismatch. Buffer cached in SQLite.
M5 Parametrisable scanner roles inside a mode See §2.3 Part of same effort.

2.5 PLC integration & industrial protocols

Spec requires: Modbus TCP/RTU, OPC UA, Profinet, EtherNet/IP, TCP Socket.

# Protocol Status Notes Action
PL1 Modbus TCP ModbusClient (single) Keep, but allow multiple simultaneous PLCs.
PL2 Modbus RTU (serial RS-485) jlibmodbus supports RTU but we don't wire it Add ModbusRtuClient using jSerialComm + jlibmodbus. Expose ModbusTransport = TCP \| RTU.
PL3 OPC UA Not implemented Add OpcUaClient using Eclipse Milo (org.eclipse.milo:sdk-client:0.6.x). Minimal ops: browse, read, write, subscribe.
PL4 Profinet Not implemented Integrate Profinet-IO-Stack via JNI (or libnodave-style open implementation). If acceptable, wrap a dedicated gateway (e.g. MOXA) over Modbus TCP.
PL5 EtherNet/IP Not implemented Use libplctag (JNI) or etherip. Support tag read/write + CIP services.
PL6 Raw TCP Socket (custom) PLC 🟡 Printers use this Extract a generic TcpSocketPlcClient (line-delimited or framed).
PL7 Configurable L2↔PLC I/O (push codes to add/remove, receive state, system params) 🟡 Partially — Modbus register map only Introduce PLC binding DSL in config: plc.bindings[i] = { direction, address, meaning }. Wire it to a generic PlcBindingService that translates edge events ↔ PLC registers.
PL8 Watchdog / print status / counters ModbusWatchdogService Keep; make configurable across transports.
PL9 Simultaneous support of PLC from MARS (AM521/AM522) + legacy line PLCs Only one modbus config Allow list of PLC configs; service router picks one per binding.

New component: PlcService (replaces ModbusService) with:

interface PlcClient {
    val id: String
    suspend fun connect(): Boolean
    suspend fun disconnect()
    suspend fun readTag(ref: TagRef): Int?
    suspend fun writeTag(ref: TagRef, value: Int): Boolean
    fun subscribe(ref: TagRef): Flow<Int>
}

enum class PlcProtocol { MODBUS_TCP, MODBUS_RTU, OPC_UA, PROFINET, ETHERNET_IP, TCP_SOCKET }

PLC adapters live in edge-service/device/plc/ with one subpackage per protocol. PlcBindingService binds tags → domain events and vice-versa.

2.6 Industrial PC hardware compatibility

# Requirement Status Action
H1 RED OS / Debian support 🟡 Debian supported.
H2 ≥8 GB RAM, ≥128 GB SSD Service fits
H3 GPIO See §2.9
H4 ≥2 Ethernet ports used (factory network + internal network) 🟡 Not enforced
H5 ≥2 × USB 3.0, optional RS-232/485 🟡 Relies on host
H6 IP65 monoblock N/A Hardware specification
H7 Temperature range -10…+55 °C N/A Hardware
H8 Cooling strategy N/A Hardware

2.7 Physical controls (push-buttons + LEDs)

Spec: "Сброс аварии" physical button, LED indicators, coloured buttons.

# Requirement Status Action
C1 "Reset alarm" hardware button Implement GPIO input on a configurable line, map to AlertService.acknowledgeAll().
C2 Status LEDs (running / warning / error / maintenance) GPIO outputs driven by StatusService.
C3 Backlit buttons (colour-coded) GPIO PWM / tri-state output.
C4 Ergonomic placement N/A Hardware

New config:

gpio:
  buttons:
    - line: "gpiochip0:17"
      action: ACK_ALARM
  leds:
    - line: "gpiochip0:22"
      status: RUNNING

2.8 Journaling, diagnostics, logging

Spec: - Event/alarm journal with precise (microsecond) timestamps. - Operator acknowledgement of critical events. - HMI-style filterable journal. - Export to CSV / JSON / XML. - Automatic + manual logging of marking / scanning / aggregation / errors. - Save to local disk + external server (FTP / SMB / HTTP API) + cloud.

# Requirement Status Action
J1 Structured event log 🟡 ActionLogger writes to SLF4J/MDC → Loki, no SQL-query-able journal
J2 Microsecond timestamps Clock.System.now() returns Instant (nanosecond) but DB stores ms
J3 Operator acknowledge No UI
J4 HMI-style filterable journal management-dashboard/Audit.tsx is close
J5 Export CSV / JSON / XML Not present
J6 Automatic + manual logging of all ops 🟡 Automatic — yes; manual — no
J7 Save to local disk 🟡 file_save_log_statuses mode exists
J8 Save to FTP / SMB / HTTP / Cloud Only our cloud
J9 Search + filter UI

2.9 UPS & GPIO integration (mandatory for WET + helpful for DRY)

# Requirement Status Action
U1 Talk to OWEN ИБП120К (USB or RS-232 protocol — vendor "Owen UPS protocol") Implement OwenUpsClient reading SET:.. / GET:.. commands (RS-232 defaults 9600-8N1 or USB CDC-ACM). Poll every 2 s: state, battery_percent, input_voltage, on_battery.
U2 React to UPS "on battery" event → signal shutdown / safe-mode UpsMonitorService → emits UPS_ON_BATTERY, UPS_LOW_BATTERY events; GpioService drives shutdown relay (line configurable); once threshold hit, runs systemctl poweroff.
U3 Fall back to NUT (Network UPS Tools) where supported Add NutUpsClient (TCP 3493). Configurable.
U4 Expose UPS state via API + UI badge GET /api/v1/ups/status; status badge in Layout.
U5 Handle UPS events from line PLC too (when GPIO not wired) Map Modbus register to UPS_ON_BATTERY via PlcBindingService.

GPIO integration uses libgpiod via JNI (e.g. com.github.mhendred.java-periphery or java-gpiod). New module: edge-service/hardware/gpio/.

2.10 Web visualisation & thin-client architecture

Spec: Web HMI shows both the L2 GUI and the PLC GUI; they can be delivered as one device with L2 visualisation, the PLC visualisation being developed separately by the MARS team.

# Requirement Status Action
W1 Web UI served by the L2 server operator-ui (Vite SPA, served by nginx or embedded Ktor static routes)
W2 Same device can also run the thin client (kiosk browser) kiosk/install-kiosk-tauri.sh
W3 Embed third-party PLC visualisation
W4 Separate open-parameter panel for connected equipment See §2.3 / §2.5
W5 Multi-language UI (RU default) i18n setup

2.11 Variant packaging (DRY vs WET)

# Variant Components Status Action
V1 DRY cabinet: PromPC + control cabinet with PLC + switch Inovance AM521/AM522, OWEN UPS, Huawei S5735 🟡 Produce deployment doc + hardware BOM + wiring diagram.
V2 WET cabinet: PromPC + power cabinet with UPS only UPS, breakers, reserved Point I/O space 🟡 Produce deployment doc + BOM + topology drawing.
V3 Line counts per site (LUZ 12+11, NOV 8+6, MIR 7, RND 7 = 20 DRY + 31 WET) Capture in docs/ROLLOUT.md with per-site roll-out schedule.

2.12 MARS migration

# Requirement Status Action
G1 Run on the same lines that run the Android app today 🟡 Functional match documented in README
G2 Keep all existing OLD_APP features Tracked in OLD_APP/ reference

3. Cross-cutting work items

3.1 Domain model changes (module common)

  • ScannerType → rename to ScannerTransport (TCP | SERIAL | MODBUS | ETHERNET_IP). Add ScannerModel, ScannerRole.
  • PrinterType → add MARKEM_SDX, NOVEXX, MOBILE_ZPL, MOBILE_TSPL, LOOPBACK. Extend PrinterConfig with parameters: Map<String, String>, role: PrinterRole, transport: PrinterTransport.
  • New PlcProtocol, PlcConfig, PlcBinding, TagRef.
  • New LineVariant { DRY, WET } + Site { LUZ, NOV, MIR, RND } in ProductionLine.kt for cabinet identification.
  • New UpsConfig (type: OWEN_IBP120K | NUT, transport, poll interval, low-battery threshold %).
  • New GpioConfig (buttons + leds).

3.2 Edge-service backend

  • PlcService, PlcBindingService, PlcClientFactory with adapters above.
  • PrinterManager replacing current single-printer PrinterService.
  • ScannerManager.combineFlows() fix.
  • EventService + events table + WS channel + export endpoints.
  • LogSinkService (FTP/SMB/HTTP/Cloud/S3).
  • UpsMonitorService + GpioService.
  • BatchAccountingService.
  • Endpoint additions (see §5).

3.3 Factory-server backend

  • Extend devices table with variant (DRY/WET), site, upsPresent, plcModel, ipcModel columns.
  • Proxy events stream from /ws/device into /ws/dashboard (per-device and aggregated). Ensure backpressure.
  • Optional: aggregate events per site for cross-line reporting.
  • Device commands additions: ack_alarm, reset_ups_event, test_gpio, reload_printer_templates.

3.4 Operator UI (thin client)

  • New pages:
  • Journal (§2.8 J4 / J5): real-time table, filters, export.
  • Settings → Scanners: add / remove scanners (up to 10), per-scanner model, IP, port, role.
  • Settings → Printers: list + per-printer parameters form (model- specific schema from backend).
  • Settings → PLCs: list (multi-PLC), protocol + endpoint.
  • Settings → UPS.
  • Settings → GPIO.
  • PLC Visualisation (iframe).
  • Status bar additions: UPS, events unacked count, each scanner's badge, each printer's badge.
  • New hook useEvents() subscribing to /ws events channel.

3.5 Management dashboard

  • Device list columns for variant (DRY/WET), site, UPS state.
  • Per-device Journal tab (pulls from /api/v1/events via factory).
  • Bulk operations filtering by variant.

3.6 Deployment / infra

  • Add RED OS build target (RPM + systemd).
  • Reconsider Docker base image for Debian 12 compatibility with GPIO (--device /dev/gpiochip0).
  • install.sh / install-desktop.sh — detect variant and install only the services needed.
  • Provide per-site docker-compose.<site>.yml.
  • kiosk script: make PLC visualisation URL configurable via env.

3.7 Testing & validation

  • Unit tests for each new driver (mock server per protocol).
  • Hardware-in-the-loop test plan:
  • 1× Inovance AM521 rig (OPC UA + Modbus TCP).
  • 1× Cognex DataMan + 1× Hikrobot ID + 1× Datalogic Matrix.
  • 1× Markem SDX + 1× Novexx labeller.
  • 1× OWEN UPS.
  • Add load-test/ scenarios for 10-scanner concurrency.
  • End-to-end test: simulate scanner flood → PLC status transitions → printer → operator UI updates.

4. Proposed module & package layout

edge-service/src/main/kotlin/ru/okto/edge/
  device/
    scanner/
      TcpScannerClient.kt          (existing)
      SerialScannerClient.kt       (existing)
      drivers/
        CognexDataManClient.kt          [new]
        HikrobotIdClient.kt             [new]
        DatalogicMatrixClient.kt        [new]
        DatalogicHandheldClient.kt      [new]
        ModbusScannerClient.kt          [new]
        EthernetIpScannerClient.kt      [new]
      ScannerDriverRegistry.kt          [new]
      ScannerManager.kt                 (refactor)
    printer/
      BaseTcpPrinterClient.kt      (existing; extract)
      VideojetPrinterClient.kt
      MarkemPrinterClient.kt
      MarkemSdxPrinterClient.kt         [new]
      NovexxPrinterClient.kt            [new]
      MobileZplPrinterClient.kt         [new]
      MobileTsplPrinterClient.kt        [new]
      LoopbackPrinterClient.kt          [new]
      PrinterManager.kt                 [new]
      PrinterDriverRegistry.kt          [new]
    plc/
      PlcClient.kt                 [new]
      PlcClientFactory.kt          [new]
      ModbusTcpPlcClient.kt        [new] (wraps existing ModbusClient)
      ModbusRtuPlcClient.kt        [new]
      OpcUaPlcClient.kt            [new]
      ProfinetPlcClient.kt         [new]
      EthernetIpPlcClient.kt       [new]
      TcpSocketPlcClient.kt        [new]
    ups/
      UpsMonitorService.kt         [new]
      OwenUpsClient.kt             [new]
      NutUpsClient.kt              [new]
    gpio/
      GpioService.kt               [new]
  service/
    BatchAccountingService.kt      [new]
    EventService.kt                [new]
    LogSinkService.kt              [new]
    PrinterService.kt              (refactor to orchestrator)
  persistence/
    EventsRepository.kt            [new]
    BatchBufferRepository.kt       [new]

5. New & changed HTTP endpoints

# Multi-device configuration
GET/PUT /api/v1/scanners
GET/PUT /api/v1/printers
GET/PUT /api/v1/plcs

# Events / Journal
GET     /api/v1/events
POST    /api/v1/events                 # operator-created entry
GET     /api/v1/events/{id}
POST    /api/v1/events/{id}/ack
GET     /api/v1/events/export?format=csv|json|xml

# UPS
GET     /api/v1/ups/status
POST    /api/v1/ups/test

# GPIO
GET     /api/v1/gpio/status
POST    /api/v1/gpio/buttons/{name}/test
POST    /api/v1/gpio/leds/{name}/set

# Batch accounting
POST    /api/v1/batch-accounting/buffer/load   # pull from L3
GET     /api/v1/batch-accounting/buffer        # list + counts
POST    /api/v1/batch-accounting/verify        # single-code verify

# Printer templates
GET/POST/PUT/DELETE /api/v1/printers/{index}/templates

# Plc bindings
GET/PUT /api/v1/plc/bindings

WebSocket channels:

  • /ws on edge — existing; add event types event, event_ack, ups_state, scanner_state[index], printer_state[index], plc_state[id].
  • /ws/dashboard on factory — propagate the same (filtered by device).

6. Roll-out ordering (dependencies)

Ordered by dependency rather than calendar time:

  1. Foundation refactor
  2. Split ScannerType into transport + model + role.
  3. Extract PrinterManager from PrinterService.
  4. Introduce PlcService abstraction (port existing Modbus TCP code).
  5. Add events table + EventService + WS propagation.

  6. New drivers (can be parallelised)

  7. Scanner drivers (Cognex / Hikrobot / Datalogic variants).
  8. Printer drivers (Markem SDX, Novexx, mobile ZPL/TSPL, loopback).
  9. PLC drivers (Modbus RTU, OPC UA, EtherNet/IP, TCP socket, Profinet via gateway).

  10. Hardware integrations

  11. GPIO service (buttons + LEDs).
  12. UPS service (OWEN + NUT).
  13. Wire GPIO + UPS into EventService.

  14. Operator UI

  15. Settings re-org (Scanners / Printers / PLCs / UPS / GPIO / PLC visualisation URL).
  16. Journal page + export.
  17. Status bar with multi-device badges.
  18. PLC visualisation iframe.

  19. Batch accounting mode (depends on scanner roles + L3 buffer endpoint).

  20. Management-dashboard updates (variant column, events tab, mass operations).

  21. Deployment assets

  22. RED OS RPM.
  23. Per-variant docker-compose files.
  24. okto-migrate CLI for MARS Android installs.
  25. Updated install scripts.

  26. QA & validation

  27. HIL benches, load-tests, pilot at one LUZ line, field rollout.

7. Risks & open questions

Risk Mitigation
Profinet lacks a robust pure-Java stack Prefer OPC UA on AM521/AM522 (supported) or use Modbus TCP gateway (MOXA MGate 5103). Document the recommended path.
EtherNet/IP requires JNI (libplctag) Ship libplctag in the Debian & RED OS packages; isolate behind the PlcClient interface.
libgpiod availability on all target IPCs For IPCs without GPIO, support a USB-IO option (Numato Labs, SiLabs CP2112); keep GPIO abstraction pluggable.
OWEN IBP120K protocol documentation Confirm against OWEN factory doc; fall back to NUT if OWEN publishes a USB-HID UPS descriptor.
MARS migration: SharedPreferences schema drift Ship one-off importer per OLD_APP version, covered by snapshot tests.
10-scanner concurrency on cheap IPCs Benchmark with loopback scanner; tune coroutine dispatchers + socket buffer sizes.
Operator acknowledgement latency with noisy lines Add "batch acknowledge" button and keyboard shortcut in Journal page.
Legal / regulatory L3 integration Keep L3 client pluggable; start with the existing OKTO cloud wrapper, add site-specific adapters later.

8. Acceptance criteria

A release is considered compliant with the new spec when:

  • Single edge-service binary runs on RED OS 7 and Debian 12 with systemd units provided.
  • edge-service.yaml accepts up to 10 scanners and at least 4 printers
    • at least 2 PLCs, with heterogeneous models / protocols.
  • All four operation modes (marking, aggregation, mixed, batch accounting) are available and covered by integration tests.
  • Device roles (ADD / REMOVE / VERIFY / REJECTION / PACKAGE) can be assigned per scanner from the UI and drive the expected behaviour.
  • Journal page lists events with μs timestamps, filters, acknowledge, and exports in CSV / JSON / XML.
  • UPS service reports state on WET cabinets and triggers safe-mode shutdown via GPIO when on battery + below threshold.
  • "Сброс аварии" physical button acknowledges all active events; LEDs reflect system state.
  • Web UI shows the PLC visualisation iframe alongside the L2 UI.
  • MARS migration importer converts a reference OLD_APP dump without manual intervention.
  • Management dashboard exposes variant (DRY/WET), site, UPS state, firmware version for each device and can trigger commands in bulk.
  • CI passes (backend tests + JaCoCo target + type-checks both SPAs).
  • Pilot lines at LUZ (one DRY + one WET) run for 7 consecutive days without unrecovered errors.

9. References

  • Inovance AM521/AM522 motion controller — supports Modbus TCP/RTU, OPC UA, EtherNet/IP, CANopen (product page).
  • OWEN IBP120K — RS-232/USB UPS from ОВЕН (see vendor docs for protocol).
  • Huawei CloudEngine S5735L-S8T4XV-V2 — L3 managed switch (PoE optional).
  • OKTO Terminal repository documents:
  • docs/SERVER_MANAGEMENT.md (command protocol, RBAC, firmware flow).
  • docs/API_REFERENCE.md, docs/USER_GUIDE.md, docs/DEPLOYMENT.md.
  • OLD_APP/ — Android reference implementation used on MARS lines today.
  • Spec source: MARS e-mail + schemas (L2 architecture, WET and DRY cabinet drawings, Settings mock-up).