gr-mcp/docs/grc-runtime-communication.md
Ryan Malloy 212832e7e4 feat: expose protocol analysis, OOT export tools; harden for release
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
2026-02-20 13:17:11 -07:00

8.5 KiB

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

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:

[ControlPort]
on = True
edges_list = True

[thrift]
port = 9090
nthreads = 2

Client Example

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
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

  • 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