Wire up protocol analysis (parse_protocol_spec, generate_decoder_chain, get_missing_oot_modules), signal analysis (analyze_iq_file), and OOT export (generate_oot_skeleton, export_block_to_oot, export_from_flowgraph) as MCP tools with integration tests. Security fixes from Hamilton review: - Remove `from __future__ import annotations` from tool registration files (breaks FastMCP schema generation) - Add blocklist guard to evaluate_expression (was unsandboxed eval) - Replace string interpolation with base64 encoding in Docker test harness (prevents code injection) - Add try/finally cleanup for temp files and Docker containers - Replace assert with proper ValueError in flowgraph block creation - Log OOT auto-discovery failures instead of swallowing silently Packaging: - Move entry point to src/gnuradio_mcp/server.py with script entry point (uv run gnuradio-mcp) - Add PyPI metadata (authors, license, classifiers, urls) - Add MIT LICENSE file - Rewrite README for current feature set (80+ tools) - Document single-session limitation
243 lines
8.5 KiB
Markdown
243 lines
8.5 KiB
Markdown
# GRC Runtime Communication with Flowgraph Processes
|
|
|
|
This document explains how GNU Radio Companion (GRC) communicates with running flowgraph processes and the two mechanisms available for runtime control.
|
|
|
|
## Key Insight: GRC is a Code Generator, Not a Runtime Controller
|
|
|
|
GRC runs flowgraphs as **completely separate subprocesses** via `subprocess.Popen()`. It does not have built-in runtime control capabilities.
|
|
|
|
```
|
|
+--------------------+ subprocess.Popen() +---------------------+
|
|
| GNU Radio | -----------------------------------> | Generated Python |
|
|
| Companion (GRC) | | Flowgraph Script |
|
|
| | <----------------------------------- | |
|
|
| (Qt/GTK GUI) | stdout/stderr pipe | (gr.top_block) |
|
|
+--------------------+ +---------------------+
|
|
```
|
|
|
|
The generated Python script runs independently. To control parameters at runtime, you must use one of the two communication mechanisms described below.
|
|
|
|
## GRC Execution Flow
|
|
|
|
```
|
|
.grc file (YAML)
|
|
|
|
|
v Platform.load_and_generate_flow_graph()
|
|
Generator (Mako templates)
|
|
|
|
|
v generator.write()
|
|
Python script (with set_*/get_* methods)
|
|
|
|
|
v ExecFlowGraphThread -> subprocess.Popen()
|
|
Running flowgraph process
|
|
|
|
|
v stdout/stderr piped back to GRC console
|
|
```
|
|
|
|
### Key GRC Execution Files
|
|
|
|
| File | Purpose |
|
|
|------|---------|
|
|
| `grc/main.py` | Entry point |
|
|
| `grc/gui_qt/components/executor.py` | ExecFlowGraphThread subprocess launcher |
|
|
| `grc/core/platform.py` | Block registry, flowgraph loading |
|
|
| `grc/core/generator/Generator.py` | Generator factory |
|
|
| `grc/workflows/common.py` | Base generator classes |
|
|
| `grc/workflows/python_nogui/flow_graph_nogui.py.mako` | Mako template for Python |
|
|
|
|
---
|
|
|
|
## Two Runtime Control Mechanisms
|
|
|
|
### 1. XML-RPC Server (Simple, HTTP-based)
|
|
|
|
A **block-based approach** - add the `xmlrpc_server` block to your flowgraph to expose GRC variables over HTTP.
|
|
|
|
| Aspect | Details |
|
|
|--------|---------|
|
|
| Protocol | HTTP (XML-RPC) |
|
|
| Default Port | 8080 |
|
|
| Setup | Add `XMLRPC Server` block to flowgraph |
|
|
| Naming | `set_varname()` / `get_varname()` |
|
|
| Type Support | Basic Python types |
|
|
|
|
#### How It Works
|
|
|
|
1. Add `XMLRPC Server` block to flowgraph
|
|
2. GRC variables automatically become `set_X()` / `get_X()` methods
|
|
3. Connect with any XML-RPC client (Python, C++, curl, etc.)
|
|
|
|
#### Client Example
|
|
|
|
```python
|
|
import xmlrpc.client
|
|
|
|
# Connect to running flowgraph
|
|
server = xmlrpc.client.ServerProxy('http://localhost:8080')
|
|
|
|
# Read and write variables
|
|
print(server.get_freq()) # Read a variable
|
|
server.set_freq(145.5e6) # Set a variable
|
|
|
|
# Flowgraph control
|
|
server.stop() # Stop flowgraph
|
|
server.start() # Start flowgraph
|
|
server.lock() # Lock flowgraph for modifications
|
|
server.unlock() # Unlock flowgraph
|
|
```
|
|
|
|
#### Key Files
|
|
|
|
| File | Purpose |
|
|
|------|---------|
|
|
| `gr-blocks/grc/xmlrpc_server.block.yml` | Server block definition |
|
|
| `gr-blocks/grc/xmlrpc_client.block.yml` | Client block definition |
|
|
| `gr-blocks/examples/xmlrpc/` | Example flowgraphs |
|
|
|
|
---
|
|
|
|
### 2. ControlPort/Thrift (Advanced, Binary)
|
|
|
|
A **configuration-based approach** - blocks register their parameters via `setup_rpc()` in C++ code. See `docs/doxygen/other/ctrlport.dox` for detailed block implementation.
|
|
|
|
| Aspect | Details |
|
|
|--------|---------|
|
|
| Protocol | Thrift Binary TCP |
|
|
| Default Port | 9090 |
|
|
| Setup | Enable in config, blocks call `setup_rpc()` |
|
|
| Naming | `block_alias::varname` |
|
|
| Type Support | Rich (complex, vectors, PMT types) |
|
|
| Metadata | Units, min/max, display hints |
|
|
|
|
#### Architecture
|
|
|
|
```
|
|
+------------------------------------------------------------------+
|
|
| Running Flowgraph Process |
|
|
+-----------------------------------------------------------------+
|
|
| Block A Block B |
|
|
| +------------------+ +------------------+ |
|
|
| | setup_rpc() { | | setup_rpc() { | |
|
|
| | add_rpc_var( | | add_rpc_var( | |
|
|
| | "gain", | | "freq", | |
|
|
| | &get_gain, | | &get_freq, | |
|
|
| | &set_gain); | | &set_freq); | |
|
|
| | } | | } | |
|
|
| +--------+---------+ +--------+---------+ |
|
|
| | | |
|
|
| v v |
|
|
| +----------------------------------------------------------+ |
|
|
| | rpcserver_thrift (port 9090) | |
|
|
| | +-----------------+ +-----------------+ | |
|
|
| | | setcallbackmap | | getcallbackmap | | |
|
|
| | | "blockA::gain" | | "blockA::gain" | | |
|
|
| | | "blockB::freq" | | "blockB::freq" | | |
|
|
| | +-----------------+ +-----------------+ | |
|
|
| +----------------------------------------------------------+ |
|
|
+------------------------------------------------------------------+
|
|
^
|
|
| Thrift Binary Protocol (TCP)
|
|
v
|
|
+------------------------------------------------------------------+
|
|
| Python Client |
|
|
| from gnuradio.ctrlport import GNURadioControlPortClient |
|
|
| |
|
|
| client = GNURadioControlPortClient(host='localhost', port=9090) |
|
|
| knobs = client.getKnobs(['blockA::gain', 'blockB::freq']) |
|
|
| client.setKnobs({'blockA::gain': 2.5}) |
|
|
+------------------------------------------------------------------+
|
|
```
|
|
|
|
#### Enabling ControlPort
|
|
|
|
**~/.gnuradio/config.conf:**
|
|
```ini
|
|
[ControlPort]
|
|
on = True
|
|
edges_list = True
|
|
|
|
[thrift]
|
|
port = 9090
|
|
nthreads = 2
|
|
```
|
|
|
|
#### Client Example
|
|
|
|
```python
|
|
from gnuradio.ctrlport.GNURadioControlPortClient import GNURadioControlPortClient
|
|
|
|
# Connect to running flowgraph
|
|
client = GNURadioControlPortClient(host='localhost', port=9090)
|
|
|
|
# Get knobs (read values)
|
|
knobs = client.getKnobs(['analog_sig_source_0::frequency'])
|
|
print(knobs)
|
|
|
|
# Set knobs (write values)
|
|
client.setKnobs({'analog_sig_source_0::frequency': 1500.0})
|
|
|
|
# Regex-based retrieval - get all frequency knobs
|
|
all_freq_knobs = client.getRe(['.*::frequency'])
|
|
|
|
# Get metadata (units, min, max, description)
|
|
props = client.properties(['analog_sig_source_0::frequency'])
|
|
print(props['analog_sig_source_0::frequency'].units)
|
|
print(props['analog_sig_source_0::frequency'].min)
|
|
```
|
|
|
|
#### GUI Monitoring Tools
|
|
|
|
- **gr-ctrlport-monitor** - Real-time variable inspection
|
|
- **gr-perf-monitorx** - Performance profiling visualization
|
|
|
|
```bash
|
|
gr-ctrlport-monitor localhost 9090
|
|
gr-perf-monitorx localhost 9090
|
|
```
|
|
|
|
#### Key Files
|
|
|
|
| File | Purpose |
|
|
|------|---------|
|
|
| `gnuradio-runtime/lib/controlport/thrift/gnuradio.thrift` | Thrift IDL definition |
|
|
| `gnuradio-runtime/include/gnuradio/rpcserver_thrift.h` | Server implementation |
|
|
| `gnuradio-runtime/include/gnuradio/rpcregisterhelpers.h` | Registration templates |
|
|
| `gnuradio-runtime/python/gnuradio/ctrlport/GNURadioControlPortClient.py` | Python client |
|
|
| `gnuradio-runtime/python/gnuradio/ctrlport/RPCConnectionThrift.py` | Thrift connection |
|
|
|
|
---
|
|
|
|
## Comparison: XML-RPC vs ControlPort
|
|
|
|
| Feature | XML-RPC | ControlPort/Thrift |
|
|
|---------|---------|-------------------|
|
|
| Setup | Add block to flowgraph | Enable in config.conf |
|
|
| Protocol | HTTP | Binary TCP |
|
|
| Performance | Slower (text-based) | Faster (binary) |
|
|
| Type support | Basic Python types | Complex, vectors, PMT |
|
|
| Metadata | None | Units, min/max, hints |
|
|
| Tooling | Any HTTP client | Specialized monitors |
|
|
| Use case | Simple control | Performance monitoring |
|
|
|
|
### When to Use Each
|
|
|
|
**Use XML-RPC when:**
|
|
- You need quick, simple remote control
|
|
- Integration with web applications
|
|
- Language-agnostic client access
|
|
- Minimal configuration
|
|
|
|
**Use ControlPort when:**
|
|
- You need performance monitoring
|
|
- Working with complex data types
|
|
- Block-level control granularity
|
|
- Need metadata about parameters
|
|
|
|
---
|
|
|
|
## Related Documentation
|
|
|
|
- `docs/doxygen/other/ctrlport.dox` - Detailed ControlPort block implementation guide
|
|
- `gr-blocks/examples/xmlrpc/` - XML-RPC usage examples
|
|
- `docs/usage-manual/(exported from wiki) Performance Counters.txt` - Performance monitoring
|