---
module: agent.nh-tcr-events
title: NH TCR Events — cassette + firmware monitoring for Nautilus Hyosung / Glory BRM30 recyclers
tab: Internal
order: 20
audience: dev
---

> **Internal · ops/support.** Written 2026-07-01 from `hiveops-module-nh-tcr-events` source (agent parent `4.4.3`). Verify against code before relying on specifics.

## What this module is

`hiveops-module-nh-tcr-events` (Maven artifact `hiveops-module-nh-tcr-events`, agent module id **`nh-tcr-events`**) is an **agent drop-in module**, not a backend service. It runs inside the HiveIQ Agent process on **Nautilus Hyosung TCR (Teller Cash Recycler) devices** that use the **Glory BRM30** external processor (EP). It self-disables unless the ATM's `device.type` is exactly `NH_TCR` (no legacy alias).

It tails log files that the Hyosung MoniPlus2S and Glory firmware already write to disk, turns specific lines into HiveIQ journal events, and additionally scrapes the daily APLog header for component firmware versions and pushes those to the incident hardware inventory. It does not talk to the recycler hardware directly, and it does not own cash totals / cassette reconciliation (that's reconciliation's job).

## What it monitors

Two independent monitors, both driven by one scheduler tick:

1. **MoniPlus2S APLog** (`ApLogMonitor`) — dir `nh.tcr.ap.log.dir`.
   - File pattern: `{nh.tcr.ap.log.dir}\{YYYYMMDD}\APLog{YYYYMMDD}.log` (each day gets its own **date subfolder**, e.g. `C:\Hyosung\MoniPlus2S\MoniPlus2Slog\20230330\APLog20230330.log`).
   - Parses `** IStackedBillsBlock = $|{denom}|{count}|{status}|{type}|{slot}|...` lines for per-slot cassette **level and status** (`OK` / `LOW` / `EMPTY` / `MISSING` / `MANIP`). Emits only when a slot's status **changes** (in-memory per-slot last-status guard), so a slot stuck `EMPTY` doesn't re-fire every poll.
   - On the first read of each daily file it parses the **header block** (bounded by `====` delimiter lines) for component firmware versions (SIU, BRM, SDK, Application, Nextware, etc.) and syncs them to the incident backend hardware inventory.
2. **Glory BRM30 FWLogSave** (`FwEventLogMonitor`) — dir `nh.tcr.fw.log.dir`.
   - Files: `00FWEvent00.log` … `00FWEvent09.log`, a **10-file circular buffer** in that directory.
   - Watches for `LogManager_StartUp()` lines, which indicate the Glory EP firmware process (re)started.

The two directories are configured separately and may point anywhere; either one being missing just disables that one monitor.

## Config properties it registers

| Property | Default | Meaning |
|---|---|---|
| `device.type` | *(none)* | Activation gate. Must equal `NH_TCR` (uppercased/trimmed). Any other value → module logs `device.type='<X>', not NH_TCR — module disabled` and does nothing. |
| `nh.tcr.ap.log.dir` | *(none)* | Directory root for the MoniPlus2S APLog (date subfolders live under it). If set but not an existing directory → `APLog monitoring disabled` warning, module keeps running for FW events. |
| `nh.tcr.fw.log.dir` | *(none)* | Directory for the Glory `00FWEventNN.log` files. If set but not an existing directory → `FW event monitoring disabled` warning. |
| `nh.tcr.events.poll.interval.sec` | `30` | Poll interval in seconds. **This module actually applies the configured value** (`start()` schedules at `pollSec`) — unlike the sibling `atec-tcr-events` module, where the same-named property is a no-op. |

If **both** `nh.tcr.ap.log.dir` and `nh.tcr.fw.log.dir` are unset, the module disables itself (`neither nh.tcr.ap.log.dir nor nh.tcr.fw.log.dir configured — module disabled`) — it never throws or crashes agent startup.

It reuses the shared agent HTTP settings — `agent.endpoint` (preferred) or `incident.endpoint` (legacy fallback), `agent.auth.token`, and the institution-key chain (`agent.institution.key` / `incident.institution.key`) — the same properties every other event-reporting module uses. There are no NH-TCR-specific auth properties. If neither endpoint is set the module disables itself.

## How it's enabled / deployed

- Activates only on ATMs configured with `device.type=NH_TCR`. On any other device type it is intentionally inert — this is expected, not a bug.
- Delivered like any other agent module: uploaded to the fleet artifact store and pushed via an **`INSTALL_MODULE`** fleet task, which drops the module JAR into the agent's `modules/` directory and restarts the agent (`ProcessInstallModule` in `hiveops-file-collection`). Standard rollout order applies: lab ATM → pilot device → fleet.
- Can be disabled fleet-wide **without** removing the JAR by adding `nh-tcr-events` to the comma-separated `modules.disabled` property and pushing a `CONFIG_UPDATE`. The module id for `modules.disabled` is the `getName()` value, **`nh-tcr-events`** (see the Claude doc for the exact string).
- Event types it emits (`CASSETTE_INVENTORY`, `CASSETTE_LOW`, `CASSETTE_EMPTY`, `TCR_CASSETTE_MISSING`, `TCR_CASSETTE_MANIP`, `TCR_EP_RESTART`) must exist as rows in hiveops-incident's `event_type` table (Incident → Settings → Event Types) to be recognized/labeled downstream. `CASSETTE_LOW` / `CASSETTE_EMPTY` are standard shared types; the `TCR_*` / `CASSETTE_INVENTORY` types may need adding on a fresh environment.

## Common failure modes / what to check

- **No NH TCR events ever appear for a customer ATM** — first check `device.type` on that ATM's config; if it isn't exactly `NH_TCR`, the module is intentionally inert. Not a bug.
- **Module loaded but silent** — check that `nh.tcr.ap.log.dir` / `nh.tcr.fw.log.dir` are set and point at real existing directories on that machine; a missing/wrong path disables that monitor at startup with a log warning, no crash. Confirm via agent logs: `APLog watching <path>` and `FWEvent monitor initialized`.
- **Events stopped after midnight** — the APLog monitor rolls to a new **date subfolder** at local midnight (`rotateDailyFileIfNeeded()`). If the Hyosung software's folder/file-naming convention ever changes (non-standard rotation, DST edge case), the module will watch a path that never appears and go silent.
- **No `TCR_EP_RESTART` for restarts that happened before the agent came up** — by design: on its first poll the FW monitor **seeks each `00FWEventNN.log` to its end**, so only `LogManager_StartUp()` lines written *after* agent start are emitted. Pre-existing startup entries are ignored.
- **Events sent but not visible/categorized in Incident** — confirm the emitted event types are registered in hiveops-incident's Event Types settings; these are DB-driven, not hardcoded in incident's code.
- **Firmware versions not updating on the device detail** — version sync only fires on the **first read of each daily APLog file** (header parse). It runs once per day per file; a partial/failed header parse still marks `headerParsed=true` so it won't retry until the next day's file. Check for `synced N peripheral version(s) from APLog header` or `failed to sync peripheral versions` in agent logs.
- **HTTP send failures** — `IncidentEventClient.sendEvent()` / `syncHardware()` failures are caught and logged (`failed to send ...` / `failed to sync ...`) but **not retried or queued**; a transient outage silently drops that single event/sync rather than backing it up.
- **Slot recovered (`OK`) events don't create tickets** — by design; a return to `OK` only writes an agent-side log line (`slot {} recovered`), no event is emitted.
