first
This commit is contained in:
162
README.md
Normal file
162
README.md
Normal file
@@ -0,0 +1,162 @@
|
||||
# Arnold — Terminator I/O Server
|
||||
|
||||
Fast-poll Modbus TCP server for AutomationDirect Terminator I/O systems.
|
||||
Reads all digital inputs at 20 Hz, exposes a REST API for signal state,
|
||||
and executes timed output sequences.
|
||||
|
||||
## Layout
|
||||
|
||||
```
|
||||
server.py entrypoint — wires everything together
|
||||
config.yaml edit this to describe your hardware
|
||||
config_with_outputs.yaml example with input + output modules
|
||||
runs.log JSON-lines sequence run history (created at runtime)
|
||||
|
||||
arnold/
|
||||
config.py YAML loader, dataclasses, config validation
|
||||
terminator_io.py Terminator I/O driver: Modbus TCP, signal cache, poll thread
|
||||
sequencer.py Sequence execution engine
|
||||
api.py FastAPI REST application
|
||||
```
|
||||
|
||||
## Quick start
|
||||
|
||||
```bash
|
||||
pip3 install pymodbus fastapi uvicorn pyyaml --break-system-packages
|
||||
python3 server.py # uses config.yaml, port 8000
|
||||
python3 server.py --config config_with_outputs.yaml --log-level debug
|
||||
```
|
||||
|
||||
Interactive API docs: `http://<pi-ip>:8000/docs`
|
||||
|
||||
## API
|
||||
|
||||
| Method | Path | Description |
|
||||
|--------|------|-------------|
|
||||
| GET | `/status` | Device comms health, poll rates, active sequence |
|
||||
| GET | `/io` | All signal states (name → value/stale/updated_at) |
|
||||
| GET | `/io/{signal}` | Single signal with device/slot/point/modbus_address |
|
||||
| GET | `/sequences` | List sequences from config |
|
||||
| GET | `/sequences/{name}` | Sequence detail with step list |
|
||||
| POST | `/sequences/{name}/run` | Start a sequence → `{run_id}` (409 if one is running) |
|
||||
| GET | `/runs/{run_id}` | Run result: pending/running/success/failed/error |
|
||||
| GET | `/runs` | Recent run history (most recent first, `?limit=N`) |
|
||||
|
||||
## Config file
|
||||
|
||||
```yaml
|
||||
devices:
|
||||
- id: ebc100_main
|
||||
host: 192.168.3.202
|
||||
port: 502 # default Modbus TCP port
|
||||
unit_id: 1 # EBC100 responds to any unit ID over TCP; use 1
|
||||
poll_interval_ms: 50
|
||||
modules:
|
||||
- slot: 1
|
||||
type: T1H-08TDS # 8-pt 24VDC sinking input
|
||||
points: 8
|
||||
- slot: 3
|
||||
type: T1K-16TD2-1 # 16-pt sourcing output
|
||||
points: 16
|
||||
|
||||
logical_io:
|
||||
- name: sensor_a
|
||||
device: ebc100_main
|
||||
slot: 1
|
||||
point: 1
|
||||
direction: input
|
||||
- name: valve_1
|
||||
device: ebc100_main
|
||||
slot: 3
|
||||
point: 1
|
||||
direction: output
|
||||
|
||||
sequences:
|
||||
- name: actuate
|
||||
description: "Open valve, verify sensor, close valve"
|
||||
steps:
|
||||
- { t_ms: 0, action: set_output, signal: valve_1, state: true }
|
||||
- { t_ms: 500, action: check_input, signal: sensor_a, expected: true }
|
||||
- { t_ms: 1000, action: set_output, signal: valve_1, state: false }
|
||||
```
|
||||
|
||||
**Timing:** `t_ms` is absolute from sequence T=0 (not relative delays).
|
||||
Steps are sorted by `t_ms` at load time; order in the file doesn't matter.
|
||||
Multiple steps with the same `t_ms` execute in file order.
|
||||
|
||||
**Failure:** a failed `check_input` aborts the sequence immediately.
|
||||
Remaining steps — including output resets — are skipped.
|
||||
Add an explicit reset sequence (`all_outputs_off`) and call it after a failure.
|
||||
|
||||
## Supported module types
|
||||
|
||||
| Type | Direction | Points |
|
||||
|------|-----------|--------|
|
||||
| T1H-08TDS, T1K-08TDS | input | 8 |
|
||||
| T1H-08ND3, T1K-08ND3 | input | 8 |
|
||||
| T1H-16ND3, T1K-16ND3 | input | 16 |
|
||||
| T1H-08NA, T1K-08NA | input | 8 |
|
||||
| T1H-08TD1, T1K-08TD1 | output | 8 |
|
||||
| T1H-08TD2, T1K-08TD2 | output | 8 |
|
||||
| T1H-16TD1, T1K-16TD1 | output | 16 |
|
||||
| T1H-16TD2, T1K-16TD2, T1K-16TD2-1 | output | 16 |
|
||||
| T1H-08TA, T1K-08TA | output | 8 |
|
||||
| T1H-08TRS, T1K-08TRS | output | 8 |
|
||||
|
||||
## T1H-EBC100 hardware quirks
|
||||
|
||||
### Unified coil address space
|
||||
|
||||
The EBC100 maps **all modules — inputs and outputs — into a single flat
|
||||
address space** ordered by physical slot number. There is no separate
|
||||
"input base address" and "output base address".
|
||||
|
||||
Example: slot 1 = 8-pt input, slot 2 = 8-pt input, slot 3 = 16-pt output:
|
||||
|
||||
| Slot | Module | Points | Coil addresses |
|
||||
|------|--------|--------|----------------|
|
||||
| 1 | T1H-08TDS (input) | 8 | 0–7 |
|
||||
| 2 | T1H-08TDS (input) | 8 | 8–15 |
|
||||
| 3 | T1K-16TD2-1 (output) | 16 | **16–31** |
|
||||
|
||||
FC05/FC15 output writes must use these unified addresses.
|
||||
The config loader computes `modbus_address` for every module and signal
|
||||
automatically — you never write raw addresses in YAML.
|
||||
|
||||
### FC02 input reads start at address 0
|
||||
|
||||
FC02 (read discrete inputs) returns only input bits, starting at bit index 0,
|
||||
regardless of where inputs sit in the unified space. The poll thread reads
|
||||
`total_input_points` bits from FC02 address 0. Because `modbus_address` for
|
||||
input signals equals their FC02 bit index (inputs occupy the lowest slots in
|
||||
practice), no remapping is needed.
|
||||
|
||||
### No exception on out-of-range addresses
|
||||
|
||||
The EBC100 returns zeros for any FC02 read address beyond the installed
|
||||
modules — it never raises Modbus exception code 2 (illegal data address).
|
||||
Module presence **cannot** be auto-detected from protocol errors.
|
||||
The `modules` list in the config is authoritative.
|
||||
|
||||
### FC05 write echo
|
||||
|
||||
`write_coil` (FC05) echoes back `True` for any address, even unmapped ones.
|
||||
There is no error feedback for writes to non-existent output points.
|
||||
Config validation at startup prevents invalid addresses from being used.
|
||||
|
||||
### Unit ID is ignored
|
||||
|
||||
The EBC100 accepts and echoes back any Modbus unit/slave ID over TCP.
|
||||
Set `unit_id: 1` in the config (standard default).
|
||||
|
||||
### No unsolicited push
|
||||
|
||||
Modbus TCP is a strictly polled protocol; the EBC100 has no push capability.
|
||||
The server polls at `poll_interval_ms` (default 50 ms = 20 Hz).
|
||||
At 24 input points a single FC02 read takes ~1 ms on a local network.
|
||||
|
||||
### Web interface
|
||||
|
||||
The EBC100 hosts a minimal HTTP server on port 80 (firmware by Host Engineering).
|
||||
It exposes IP/subnet/gateway config and serial port mode only — no I/O data.
|
||||
Port 443, 503, and 8080 are closed. UDP port 502 is not active.
|
||||
Reference in New Issue
Block a user