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/**+/wsfor 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 withCommandResult/CommandProgress/ arbitrary telemetry. - Offline queue, dead-letter, two connection modes (DIRECT_CLOUD / VIA_LOCAL_SERVER).
Notable gaps detected up-front¶
- No PLC protocols other than Modbus TCP (spec requires Modbus RTU, OPC UA, Profinet, EtherNet/IP, raw TCP Socket).
- Scanner drivers are generic TCP/serial only — no vendor-specific adapters (Cognex DataMan, Datalogic Matrix, Hikrobot ID, handheld Gryphon/QuickScan).
- Printer drivers are limited — no Novexx labeller, no SDX60/SDX65, no mobile-printer protocol (Zebra/TSPL over BT/USB).
- Only one Modbus client per device; the spec demands up to 10 scanners and multiple heterogeneous PLCs concurrently.
- No per-device parametrisation UI for "количество и функционал подключаемого оборудования" (e.g. 3 scanners: add / remove / verify- against-local-buffer). The current UI is mostly single-terminal.
- No GPIO integration (required for UPS-aware shutdown on WET and indicator lights / "Сброс аварии" button on both variants).
- No UPS monitoring (OWEN IBP120K) — neither serial/USB poll nor NUT integration. WET variant explicitly needs this.
- 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.
- No structured journal viewer in the operator UI (HMI-style events with acknowledge / filter / export to CSV/JSON/XML, microsecond timestamps).
- No thin-client web visualisation endpoint (two isolated iframes: L2 GUI + PLC GUI); the PLC part must be embeddable.
PrinterType.NONEused internally leaks to the public enum; printer count is hard-coded to 1; no simultaneous multi-printer orchestration.- No serial over RS-485 adapter for Modbus RTU, and no pluggable multi-PLC driver registry.
- No mobile / handheld scanner gateway (USB-HID barcode + wedge profile support in the kiosk UI).
- README and config docs describe MARS-style line but do not describe the physical cabinet variants (DRY / WET) nor rollout counts.
- 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
ScrollDialectsout ofPrinterService; 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>)mirroringScannerManagerwithgetPrinter(index)/getStatus()/printOn(index, code). - Persist multi-printer config in
edge-service.yamlas a list; exposePUT /api/v1/printersin 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. ScannerConfiggainsmodel: ScannerModel,role: ScannerRole,parameters: Map<String, String>.ScannerManager.getCombinedScanFlow()usesmerge(...flows)instead of the current for-each-collect that only yields from the first scanner.ScannerServiceroutes events by role:ADD→bottleService.createBottles(...)REMOVE→bottleService.cancelByIdentifier(...)VERIFY_LOCAL_BUFFER→ matches against a newSessionCodeBuffer(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 inStatusService.getPrometheusMetrics().
2.4 Operation modes ("Режимы работы L2")¶
Spec:
- Марк. единиц продукции (single-code marking)
- Агрегация (unit → box → pallet)
- Смешанный (marking + aggregation)
- Партионный учёт (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 toScannerTransport (TCP | SERIAL | MODBUS | ETHERNET_IP). AddScannerModel,ScannerRole.PrinterType→ addMARKEM_SDX,NOVEXX,MOBILE_ZPL,MOBILE_TSPL,LOOPBACK. ExtendPrinterConfigwithparameters: Map<String, String>,role: PrinterRole,transport: PrinterTransport.- New
PlcProtocol,PlcConfig,PlcBinding,TagRef. - New
LineVariant { DRY, WET }+Site { LUZ, NOV, MIR, RND }inProductionLine.ktfor 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,PlcClientFactorywith adapters above.PrinterManagerreplacing current single-printerPrinterService.ScannerManager.combineFlows()fix.EventService+eventstable + WS channel + export endpoints.LogSinkService(FTP/SMB/HTTP/Cloud/S3).UpsMonitorService+GpioService.BatchAccountingService.- Endpoint additions (see §5).
3.3 Factory-server backend¶
- Extend
devicestable withvariant(DRY/WET),site,upsPresent,plcModel,ipcModelcolumns. - Proxy
eventsstream from/ws/deviceinto/ws/dashboard(per-device and aggregated). Ensure backpressure. - Optional: aggregate
eventsper 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/wsevents channel.
3.5 Management dashboard¶
- Device list columns for variant (DRY/WET), site, UPS state.
- Per-device Journal tab (pulls from
/api/v1/eventsvia 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. kioskscript: 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:
/wson edge — existing; add event typesevent,event_ack,ups_state,scanner_state[index],printer_state[index],plc_state[id]./ws/dashboardon factory — propagate the same (filtered by device).
6. Roll-out ordering (dependencies)¶
Ordered by dependency rather than calendar time:
- Foundation refactor
- Split
ScannerTypeinto transport + model + role. - Extract
PrinterManagerfromPrinterService. - Introduce
PlcServiceabstraction (port existing Modbus TCP code). -
Add
eventstable +EventService+ WS propagation. -
New drivers (can be parallelised)
- Scanner drivers (Cognex / Hikrobot / Datalogic variants).
- Printer drivers (Markem SDX, Novexx, mobile ZPL/TSPL, loopback).
-
PLC drivers (Modbus RTU, OPC UA, EtherNet/IP, TCP socket, Profinet via gateway).
-
Hardware integrations
- GPIO service (buttons + LEDs).
- UPS service (OWEN + NUT).
-
Wire GPIO + UPS into
EventService. -
Operator UI
- Settings re-org (Scanners / Printers / PLCs / UPS / GPIO / PLC visualisation URL).
- Journal page + export.
- Status bar with multi-device badges.
-
PLC visualisation iframe.
-
Batch accounting mode (depends on scanner roles + L3 buffer endpoint).
-
Management-dashboard updates (variant column, events tab, mass operations).
-
Deployment assets
- RED OS RPM.
- Per-variant docker-compose files.
okto-migrateCLI for MARS Android installs.-
Updated install scripts.
-
QA & validation
- 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-servicebinary runs on RED OS 7 and Debian 12 with systemd units provided. -
edge-service.yamlaccepts 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).