Skip to content

Commit af0842f

Browse files
author
Michal Tichák
committed
[peanut] peanut now supports direct, fmq batched and single step modes
in TUI and CLI modes written using Claude Opus 4.6
1 parent adbed10 commit af0842f

File tree

5 files changed

+1027
-159
lines changed

5 files changed

+1027
-159
lines changed

cmd/peanut/main.go

Lines changed: 34 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,17 +25,46 @@
2525
package main
2626

2727
import (
28+
"flag"
29+
"fmt"
2830
"os"
29-
"strings"
3031

3132
"github.com/AliceO2Group/Control/occ/peanut"
3233
)
3334

3435
func main() {
35-
cmdArg := os.Args[1:]
36-
cmdString := strings.Trim(strings.Join(cmdArg, " "), " ")
36+
fs := flag.NewFlagSet("peanut", flag.ExitOnError)
37+
addr := fs.String("addr", "", "OCC gRPC address (host:port); if empty, OCC_CONTROL_PORT env var is used in direct mode")
38+
mode := fs.String("mode", "direct", "control mode: direct (default), fmq, or fmq-step")
39+
fs.Usage = func() {
40+
fmt.Fprint(os.Stderr, `peanut — process execution and control utility for OCC / FairMQ processes
3741
38-
if err := peanut.Run(cmdString); err != nil {
39-
panic(err)
42+
TUI mode (interactive, launched when no command is given):
43+
OCC_CONTROL_PORT=<port> peanut
44+
peanut -addr host:port -mode fmq
45+
46+
CLI mode (non-interactive, launched when a command is given):
47+
peanut [flags] <command> [args]
48+
Run "peanut -addr x get-state" for full CLI usage.
49+
50+
Flags:
51+
`)
52+
fs.PrintDefaults()
53+
}
54+
_ = fs.Parse(os.Args[1:])
55+
56+
if fs.NArg() > 0 {
57+
// CLI mode — pass all original args so RunCLI can re-parse its own flags
58+
if err := peanut.RunCLI(os.Args[1:]); err != nil {
59+
fmt.Fprintf(os.Stderr, "peanut: %v\n", err)
60+
os.Exit(1)
61+
}
62+
return
63+
}
64+
65+
// TUI mode
66+
if err := peanut.Run(peanut.Options{Addr: *addr, Mode: *mode}); err != nil {
67+
fmt.Fprintf(os.Stderr, "peanut: %v\n", err)
68+
os.Exit(1)
4069
}
4170
}

occ/peanut/README.md

Lines changed: 205 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,224 @@
11
# Process control and execution utility overview
22

33
`peanut` is the **p**rocess **e**xecution **a**nd co**n**trol **ut**ility for OCClib-based O² processes. Its purpose
4-
is to be a debugging and development aid for non-FairMQ O² devices, where FairMQ's interactive
5-
controller is not available.
4+
is to be a debugging and development aid for OCC-based and FairMQ O² devices.
65

76
In aliBuild it is part of the `coconut` package.
87

9-
`peanut` can connect to a running OCClib-based process, query its status, drive its state machine
8+
`peanut` can connect to a running OCClib-based or FairMQ process, query its status, drive its state machine
109
and push runtime configuration data.
1110

12-
`peanut` is an interactive tool, the only information it picks up from its environment is the
13-
`OCC_CONTROL_PORT` variable, which is used to connect to a running OCClib-based process.
11+
`peanut` runs in two modes depending on whether a command is passed:
12+
13+
* **TUI mode** — interactive terminal UI (launched when no command is given)
14+
* **CLI mode** — non-interactive, scriptable (launched when a command is given)
15+
16+
---
17+
18+
## TUI mode
19+
20+
```bash
21+
peanut [flags]
22+
```
23+
24+
| Flag | Default | Description |
25+
|------|---------|-------------|
26+
| `-addr` | `""` | gRPC address `host:port`; if empty, falls back to `OCC_CONTROL_PORT` env var (direct mode only) |
27+
| `-mode` | `direct` | `direct`, `fmq`, or `fmq-step` (see below) |
28+
29+
### Modes
30+
31+
#### `direct` — OCC protobuf (default)
32+
33+
Connects to an OCClib-based process using the standard OCC protobuf codec.
34+
The state machine operates on OCC states: `STANDBY`, `CONFIGURED`, `RUNNING`, `ERROR`.
1435

1536
```bash
16-
$ OCC_CONTROL_PORT=<some port> peanut
37+
OCC_CONTROL_PORT=47100 peanut
38+
# or
39+
peanut -addr localhost:47100 -mode direct
1740
```
1841

19-
![Screenshot of peanut](peanut.png)
42+
Control buttons: **CONFIGURE**, **RESET**, **START**, **STOP**, **RECOVER**, **EXIT**
2043

21-
`peanut` commands are documented inline. Each transition is applied immediately and
22-
the state is updated in real time.
44+
#### `fmq` — FairMQ JSON codec with automatic multi-step sequencing
2345

24-
Compared to the raw gRPC API, the following limitations apply:
46+
Connects to a FairMQ device using the JSON codec. Each OCC-level button press
47+
automatically drives the full underlying FairMQ state machine sequence.
48+
The state is displayed as an OCC-mapped state (`STANDBY`, `CONFIGURED`, `RUNNING`…).
2549

26-
* It is not possible to perform a `GO_ERROR` transition, as this transition is only triggered from
27-
user code.
50+
```bash
51+
peanut -addr localhost:47100 -mode fmq
52+
```
53+
54+
Control buttons: **CONFIGURE**, **RESET**, **START**, **STOP**, **RECOVER**, **EXIT**
55+
56+
Sequences driven automatically:
57+
58+
| Button | FairMQ steps |
59+
|--------|-------------|
60+
| CONFIGURE | INIT DEVICE → COMPLETE INIT → BIND → CONNECT → INIT TASK |
61+
| RESET | RESET TASK → RESET DEVICE |
62+
| START | RUN |
63+
| STOP | STOP |
64+
| RECOVER | RESET DEVICE (from ERROR) |
65+
| EXIT | RESET (if needed) → END |
66+
67+
#### `fmq-step` — FairMQ JSON codec with granular per-step control
68+
69+
Connects to a FairMQ device using the JSON codec. Exposes each individual FairMQ
70+
state machine step as a separate button. The state is displayed as the raw FairMQ state.
71+
72+
```bash
73+
peanut -addr localhost:47100 -mode fmq-step
74+
```
75+
76+
| Key | Button | Transition |
77+
|-----|--------|-----------|
78+
| `1` | INIT DEVICE | IDLE → INITIALIZING DEVICE |
79+
| `2` | COMPLETE INIT | INITIALIZING DEVICE → INITIALIZED |
80+
| `3` | BIND | INITIALIZED → BOUND |
81+
| `4` | CONNECT | BOUND → DEVICE READY |
82+
| `5` | INIT TASK | DEVICE READY → READY |
83+
| `6` | RUN | READY → RUNNING |
84+
| `7` | STOP | RUNNING → READY |
85+
| `8` | RESET TASK | READY → DEVICE READY |
86+
| `9` | RESET DEVICE | → IDLE |
87+
| `0` | END | IDLE → EXITING |
88+
89+
### Common TUI controls (all modes)
90+
91+
| Key | Action |
92+
|-----|--------|
93+
| `n` | **Reconnect** — re-establish the gRPC connection to the controlled process. Use this when the process has been restarted after a crash or deliberate termination. |
94+
| `l` | **Load configuration** — open a file dialog to read a YAML or JSON configuration file. The path field supports tab-completion. Once loaded, the right panel shows `NOT PUSHED` until the next CONFIGURE transition, then `PUSHED`. |
95+
| `q` | **Quit** — disconnect and exit without sending any transitions. |
96+
97+
### Connection monitoring
98+
99+
While connected, peanut passively monitors the gRPC connection in a background goroutine and detects process termination without any button press. The strategy depends on what the controlled process supports:
100+
101+
1. **StateStream** (OCClib processes, `direct` mode) — subscribes to the state stream; any disconnect immediately triggers `UNREACHABLE` and an error modal. State updates from the stream are also reflected in the display in real time.
102+
2. **EventStream** (FairMQ processes, `fmq`/`fmq-step` modes) — subscribes to the event stream; disconnect is detected immediately when the stream breaks.
103+
3. **Polling** (fallback) — if neither stream is available, `GetState` is polled every 2 seconds.
104+
105+
When the process dies, the state display shows `UNREACHABLE` and an error modal appears. After restarting the controlled process, press `n` to reconnect.
106+
107+
### Runtime configuration files
108+
109+
Configuration files are YAML or JSON, with arbitrarily nested structure.
110+
`peanut` flattens them to dot-notation key=value pairs before pushing.
111+
Integer map keys and integer values are both handled correctly.
112+
113+
Example (channel configuration):
114+
```yaml
115+
chans:
116+
data:
117+
numSockets: 1
118+
0:
119+
address: ipc://@o2ipc-example
120+
method: bind
121+
type: push
122+
transport: shmem
123+
sndBufSize: 1000
124+
rcvBufSize: 1000
125+
sndKernelSize: 0
126+
rcvKernelSize: 0
127+
rateLogging: 0
128+
```
129+
130+
This flattens to entries like `chans.data.0.address=ipc://@o2ipc-example`.
131+
132+
---
133+
134+
## CLI mode
135+
136+
```bash
137+
peanut [flags] <command> [args]
138+
```
28139

29-
* The `CONFIGURE` transition may be triggered both with and without runtime configuration data, which
30-
may or may not be suitable depending on user code. All other transitions send no payload.
140+
| Flag | Default | Description |
141+
|------|---------|-------------|
142+
| `-addr` | `localhost:47100` | gRPC address `host:port` |
143+
| `-mode` | `fmq` | `fmq` (JSON codec) or `direct` (protobuf) |
144+
| `-timeout` | `30s` | timeout for unary gRPC calls |
145+
| `-config` | `""` | path to YAML/JSON file; flattened key=value pairs are sent as arguments. Inline `key=val` arguments take precedence. |
146+
147+
### Commands
148+
149+
#### `get-state`
150+
151+
Print the current FSM state.
152+
153+
```bash
154+
peanut -addr localhost:47100 get-state
155+
```
156+
157+
#### `transition <fromState> <toState> [key=val ...]`
158+
159+
High-level state transition. In `fmq` mode drives the full multi-step FairMQ sequence automatically.
160+
161+
```bash
162+
# FairMQ: drive full configure sequence
163+
peanut -addr localhost:47100 -mode fmq transition STANDBY CONFIGURED \
164+
chans.data.0.address=ipc://@o2ipc-example
165+
166+
# FairMQ: with config file
167+
peanut -addr localhost:47100 -mode fmq -config stfsender-configure-args.yaml \
168+
transition STANDBY CONFIGURED
169+
170+
# Direct OCC
171+
peanut -addr localhost:47100 -mode direct transition STANDBY CONFIGURED
172+
```
173+
174+
FairMQ sequences driven automatically:
175+
176+
| From → To | Steps |
177+
|-----------|-------|
178+
| `STANDBY → CONFIGURED` | INIT DEVICE, COMPLETE INIT, BIND, CONNECT, INIT TASK |
179+
| `CONFIGURED → RUNNING` | RUN |
180+
| `RUNNING → CONFIGURED` | STOP |
181+
| `CONFIGURED → STANDBY` | RESET TASK, RESET DEVICE |
182+
183+
#### `direct-step <srcState> <event> [key=val ...]`
184+
185+
Low-level: send a single raw OCC gRPC Transition call (protobuf codec).
186+
187+
```bash
188+
peanut -addr localhost:47100 -mode direct direct-step STANDBY CONFIGURE key=val
189+
```
190+
191+
Events: `CONFIGURE`, `RESET`, `START`, `STOP`, `RECOVER`, `EXIT`
192+
193+
#### `fmq-step <srcFMQState> <fmqEvent> [key=val ...]`
194+
195+
Low-level: send a single raw FairMQ gRPC Transition call (JSON codec).
196+
State/event names that contain spaces must be quoted.
197+
198+
```bash
199+
peanut -addr localhost:47100 fmq-step IDLE "INIT DEVICE" chans.x.0.address=ipc://@foo
200+
peanut -addr localhost:47100 fmq-step READY RUN
201+
```
202+
203+
#### `state-stream`
204+
205+
Subscribe to `StateStream` and print state updates until interrupted (Ctrl-C).
206+
207+
```bash
208+
peanut -addr localhost:47100 state-stream
209+
```
210+
211+
#### `event-stream`
212+
213+
Subscribe to `EventStream` and print events until interrupted (Ctrl-C).
214+
215+
```bash
216+
peanut -addr localhost:47100 event-stream
217+
```
31218

32-
The last two commands are **not** transitions:
219+
---
33220

34-
* `Load configuration` allows the user to read in a JSON or YAML file containing sample
35-
configuration data that is then available to be pushed to the controlled process during a future
36-
`CONFIGURE` transition. On startup, there is no file loaded, so a `CONFIGURE` transition will push
37-
an empty payload. Once a runtime configuration file is loaded, its title bar reports `NOT PUSHED`
38-
until the next `CONFIGURE` transition, at which point it becomes `PUSHED`.
221+
## Limitations
39222

40-
* `Quit` disconnects from the controlled process and quits `peanut`, but it performs no transitions
41-
or other data exchange with the controlled process. A future instance of `peanut` may reattach itself
42-
to the same process and continue from there.
223+
* The `GO_ERROR` transition cannot be triggered from `peanut`, as it is only triggered from user code inside the controlled process.
224+
* `Quit` / `q` disconnects without sending any transition. A future instance of `peanut` can reattach to the same process and continue.

0 commit comments

Comments
 (0)