# 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