Files
2026-07-11 17:19:14 +07:00

180 lines
5.3 KiB
Markdown

# Runtime Control — Start/Stop Counting
The counter can pause and resume object detection & counting **on the fly**,
without restarting the process. There are three interchangeable ways to control
it, and they all converge on a single source of truth: the **control file**.
- **Control file** — a small JSON file the counter polls.
- **TCP control socket** — line commands over the network that update the file.
- **Dashboard button** — a COUNTING ON/OFF toggle that writes the file via its API.
When counting is **OFF**, the counter skips inference entirely (no detection, no
counting, lower CPU/NPU load), the video/live stream keeps running, and a
`COUNTING PAUSED` badge is drawn on the frame. When **ON**, normal counting
resumes.
---
## 1. Enable runtime control
Runtime control is opt-in. In your `.env`:
```ini
# Master switch — required for ALL control methods (file, socket, dashboard).
CONTROL_ENABLED=true
# Shared control file. MUST be identical for the counter and the dashboard.
CONTROL_FILE=/opt/bytetrack-counter/control.json
# Counting state on startup / when the control file does not exist yet.
CONTROL_DEFAULT_COUNTING=true
# How often (seconds) the counter re-reads the control file.
CONTROL_POLL_SEC=1.0
```
When `CONTROL_ENABLED=false`, the counter always counts, the control file is
ignored, and the dashboard hides the toggle button.
> Changes take effect within `CONTROL_POLL_SEC` seconds (default 1s), because the
> counter re-reads the file on a timer.
---
## 2. Control file
### Format
```json
{ "counting": true }
```
- `"counting": true` → counting **ON**
- `"counting": false` → counting **OFF** (paused)
The counter creates this file on startup (seeded from `CONTROL_DEFAULT_COUNTING`)
if it does not exist. All writers (counter, dashboard, socket) write it
**atomically** (temp file + rename), so readers never see a half-written file.
### Toggle by editing the file
Pause counting:
```bash
printf '{"counting": false}\n' > /opt/bytetrack-counter/control.json
```
Resume counting:
```bash
printf '{"counting": true}\n' > /opt/bytetrack-counter/control.json
```
Check current state:
```bash
cat /opt/bytetrack-counter/control.json
```
> Use the exact path from your `CONTROL_FILE` setting. If you write it by hand,
> keep it valid JSON — an unreadable file falls back to `CONTROL_DEFAULT_COUNTING`.
---
## 3. TCP control socket
The socket lets you toggle counting over the network. It updates the same control
file, so changes still apply within `CONTROL_POLL_SEC`.
### Enable
```ini
# Requires CONTROL_ENABLED=true as well.
CONTROL_SOCKET_ENABLED=true
# 127.0.0.1 = local only. Use 0.0.0.0 to allow remote clients.
CONTROL_SOCKET_HOST=127.0.0.1
# TCP port.
CONTROL_SOCKET_PORT=5090
```
### Commands
Newline-terminated, case-insensitive. One connection can send multiple commands.
| Command | Effect | Reply |
|----------------------------|-------------------------------|-----------------------|
| `START` / `RESUME` / `ON` | Counting ON | `OK counting=on` |
| `STOP` / `PAUSE` / `OFF` | Counting OFF | `OK counting=off` |
| `TOGGLE` | Flip current state | `OK counting=on/off` |
| `STATUS` / `GET` | Report state (no change) | `OK counting=on/off` |
| *(anything else)* | — | `ERR unknown command` |
### Examples
Using `nc` (netcat):
```bash
printf 'STOP\n' | nc 127.0.0.1 5090
printf 'START\n' | nc 127.0.0.1 5090
printf 'TOGGLE\n' | nc 127.0.0.1 5090
printf 'STATUS\n' | nc 127.0.0.1 5090
```
Using bash `/dev/tcp` (no netcat needed):
```bash
exec 3<>/dev/tcp/127.0.0.1/5090
printf 'STATUS\n' >&3
head -n1 <&3
exec 3>&-
```
Python client:
```python
import socket
def control(cmd, host="127.0.0.1", port=5090):
with socket.create_connection((host, port), timeout=2) as s:
s.sendall((cmd + "\n").encode())
return s.recv(256).decode().strip()
print(control("STATUS")) # OK counting=on
print(control("STOP")) # OK counting=off
```
> **Security:** the socket has **no authentication**. Keep `CONTROL_SOCKET_HOST`
> on `127.0.0.1`, or restrict access with a firewall / trusted network if you
> bind to `0.0.0.0`.
---
## 4. Dashboard button
When `CONTROL_ENABLED=true`, the dashboard header shows a **COUNTING ON/OFF**
button (green when on, red when off). Clicking it flips the state immediately.
The dashboard must point at the **same** `CONTROL_FILE` as the counter (set it in
the dashboard's environment too). The dashboard exposes:
- `GET /api/control` → `{ "enabled": true, "counting": true }`
- `POST /api/control` with body `{ "counting": false }` → writes the control file
(returns `403` if `CONTROL_ENABLED=false`)
---
## Notes & behavior
- **Single source of truth:** the socket and dashboard both write the control
file; the counter reacts only to the file. This avoids race conditions between
control methods.
- **Latency:** expect up to `CONTROL_POLL_SEC` (default 1s) between issuing a
command and the counter reacting.
- **Live stream keeps running** while paused, so you still see the camera feed
with the `COUNTING PAUSED` overlay.
- **Shared path requirement:** counter and dashboard must use the same
`CONTROL_FILE`. If they run on different machines, use the TCP socket (or a
shared network path) instead.