# LabBridge Agent

Standalone clinic-side service that bridges lab analyzers (ASTM E1394 over TCP
or RS-232 serial) to the cloud LabCore API. Built as a single static Go binary
— no XAMPP, no PHP, no other runtime required on the clinic machine.

## What it does

- Listens locally for each registered analyzer (TCP or serial).
- Runs the ASTM E1381 low-level handshake (ENQ/ACK/STX framing + checksum)
  with native latency, so the analyzer never times out.
- Forwards each assembled E1394 record stream to the cloud `/api/lab/results`
  endpoint. Content parsing (E1394 records → sessions/results) happens on the
  cloud in LabCore — the agent is a thin transport relay.
- Buffers every message in an embedded SQLite queue (`data/queue.sqlite`).
  When the network drops or the cloud is unreachable, messages stay queued.
- On reconnect runs a **catch-up sweep**: pushes pending results + events,
  pulls the worklist, and sends a UUID manifest the cloud uses to confirm
  nothing was silently lost. Every push is **idempotent on UUID**, so replay
  after a lost confirmation never creates duplicates.
- Pings the cloud every `heartbeat_interval` seconds so the cloud's Device
  Monitor shows the agent + each analyzer as online/offline live.

## Architecture

```
   Analyzer ──ASTM E1381──┐                         CLOUD
   (TCP or serial)        ├──>  LabBridge Agent  ──HTTPS──>  LabCore API
                          │     (this binary)               │
                          │       │                         └─ E1394 parse + store
                          │       └─ SQLite queue
                          │          (offline buffer)
```

The agent does **not** parse ASTM records. That stays on the cloud so the same
content parser serves every vendor (Abaxis, IDEXX-via-VLS, future).

## Build

Requires **Go 1.22+**.

```bash
cd labbridge-agent
go mod tidy
go build -ldflags "-s -w" -o labbridge.exe ./cmd/labbridge      # Windows
go build -ldflags "-s -w" -o labbridge     ./cmd/labbridge      # Linux/macOS
```

The binary is fully static (modernc.org/sqlite is pure Go, no cgo).

## Configure

Copy `config.example.yaml` to `config.yaml` next to the binary:

```yaml
cloud_url: "https://erp.example.com"
agent_code: "main-clinic"
api_token: "<paste from cloud Lab → Agents screen>"
data_dir:  "./data"
heartbeat_interval: 60
retry_interval: 30
```

Devices and their listener specs (TCP port, COM port, etc.) are configured in
the cloud **Lab → Devices** screen and pulled down on startup. There's nothing
analyzer-specific to edit on the clinic machine.

## Install as a Windows Service

```bat
labbridge.exe install
labbridge.exe start
labbridge.exe status
```

Other actions: `stop`, `restart`, `uninstall`.

To run interactively for debugging:

```bat
labbridge.exe run
```

The service runs under `LocalSystem` by default; switch to a dedicated service
account if your `data_dir` lives in a user folder.

## Linux / systemd

```bash
sudo ./labbridge install     # writes /etc/systemd/system/labbridgeagent.service
sudo ./labbridge start
sudo journalctl -u labbridgeagent -f
```

## Layout

```
cmd/labbridge/             main entrypoint + service wrapper
internal/
  config/                  YAML config loader
  astm/                    ASTM E1381 transport (framing, checksum, handshake)
  queue/                   SQLite queue + event log + worklist cache
  sync/                    HTTP client, sync worker, heartbeat
  listener/                TCP + serial listeners per device
```

## How to verify a fresh install

1. Build and copy the binary + `config.yaml` to the clinic PC.
2. Create the agent in the cloud (Lab → Agents → "Add Bridge Agent"), copy
   the API token into `config.yaml`.
3. Add at least one device (Lab → Devices) pointing at the listener port
   (e.g. TCP `:9600` or `COM3`).
4. `labbridge run` — should log `registered as agent #N … N device(s)` and
   open the listeners.
5. Send a test message — easiest is the cloud-side Abaxis simulator with
   `--mode=tcp`:
   ```bash
   php artisan abaxis:simulate --mode=tcp --host=<clinic-ip> --port=9600
   ```
6. The agent should log `queued <uuid>` then `pushed <uuid> -> stored …`,
   and the session should appear in the cloud's Lab → Results screen.

## Limitations (V1)

- The agent picks up device config on startup only. To reconfigure, restart.
- HL7/MLLP listener isn't shipped — only ASTM. (IDEXX-via-VLS most likely
  uses ASTM too; if a future vendor needs HL7 the protocol layer is the only
  new piece.)
- Bidirectional worklist serving (analyzer queries the agent for orders) is
  not yet wired — the worklist is pulled and cached but not handed back via
  ASTM `Q` records.
- No auto-update channel yet. Replace the binary + restart the service.
