Metadata-Version: 2.5
Name: dt5215-emulator
Version: 1.0.3
Summary: A DT5215 control API on localhost: the firmware emulator the DT5215 Control SDK is tested against
Author: Nuclear Instruments
License-Expression: MIT
License-File: LICENSE
Keywords: caen,dt5215,emulator,fers,nuclear instruments
Classifier: Intended Audience :: Science/Research
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: Scientific/Engineering :: Physics
Classifier: Topic :: Software Development :: Testing
Requires-Python: >=3.8
Provides-Extra: test
Requires-Dist: pytest>=7; extra == 'test'
Description-Content-Type: text/markdown

# DT5215 firmware emulator

A DT5215 control API on localhost. It answers every endpoint of `spec/dt5215-api.yaml` with
plausible, consistent data, so the SDKs of all seven languages — and your own program — can be
tested with no instrument in the loop.

```bash
pip install dt5215-emulator
dt5215-emulator --port 8080
```

```bash
curl -s -X POST http://127.0.0.1:8080/api-v1.0/login \
  -H 'Content-Type: application/json' -d '{"username":"user","password":"user"}'
```

```python
from dt5215_emulator import Emulator
import dt5215

with Emulator() as emu:                       # a free port
    with dt5215.connect(emu.url, username="user", password="user") as dev:
        print(dev.info().hostname)            # dt5215-emu
```

## What it reproduces on purpose

Three behaviours of the real firmware that a client has to get right, and that a hand-written
mock usually gets wrong:

* a refusal is **HTTP 200 with `result: false`**, not a 4xx;
* `/reboot` and `/restart_app` **answer nothing** and drop the connection;
* a bearer token is required everywhere except `/login` and `/alive`, and `/alive` reports
  whether the token it was given is valid (so a client can tell "instrument down" from
  "token expired").

It is also *stateful*: switching HV off changes what the next `/get_hv_status` says, disabling
a chain empties it, writing a register reads back, an enumeration walks its phases (in about a
second, not minutes), and a board firmware upgrade progresses board by board.

## Driving it from a test

`Emulator(state=...)` takes a prepared `State`, and every field of it can be changed while the
emulator runs:

```python
from dt5215_emulator import Emulator, State

state = State()
state.chains[0].los = True              # a chain with loss of signal
state.client["control"] = True          # Janus attached: a board upgrade must be refused
with Emulator(state=state) as emu:
    ...
```

## Options

| Option | Meaning |
|---|---|
| `--port N` | TCP port (default 8080; `Emulator(port=0)` takes a free one) |
| `--host H` | interface to bind (default `127.0.0.1`) |
| `--no-auth` | answer everything without a token, like the instrument's `/caen/config/nologin` |
| `--hostname NAME` | the hostname the emulator reports |
| `-v` | log every request |

Default users: `user`/`user` and `caen`/`caen`.

## What it is not

It does not pretend to be the instrument's data path: there is no event streaming, no FPGA, no
real timing. It answers the control API, and that is all the SDK needs.
