main - feat: Make some initial MCP server work
This commit is contained in:
parent
61309d9b57
commit
9f96402e02
20
README.md
20
README.md
@ -21,4 +21,22 @@ python -m grc
|
|||||||
```
|
```
|
||||||
|
|
||||||
## Current Status
|
## Current Status
|
||||||
Currently, the project is only a port of GNURadio Companion (v3.10.12.0)
|
Currently, the project is only a port of GNURadio Companion (v3.10.12.0)
|
||||||
|
|
||||||
|
|
||||||
|
## Wanted API
|
||||||
|
1. Resources
|
||||||
|
- List available blocks
|
||||||
|
- List block params
|
||||||
|
|
||||||
|
- List current blocks
|
||||||
|
- List current connections
|
||||||
|
|
||||||
|
2. Tools
|
||||||
|
- Add block
|
||||||
|
- Remove block
|
||||||
|
- Set block params
|
||||||
|
- Add connection
|
||||||
|
- Remove connection
|
||||||
|
|
||||||
|
-[ ] use pydantic
|
||||||
|
|||||||
265
fm_radio_receiver.grc
Normal file
265
fm_radio_receiver.grc
Normal file
@ -0,0 +1,265 @@
|
|||||||
|
options:
|
||||||
|
parameters:
|
||||||
|
author: GNU Radio
|
||||||
|
catch_exceptions: 'True'
|
||||||
|
category: '[GRC Hier Blocks]'
|
||||||
|
cmake_opt: ''
|
||||||
|
comment: ''
|
||||||
|
copyright: ''
|
||||||
|
description: RTL-SDR FM Radio Receiver with frequency and volume controls
|
||||||
|
gen_cmake: 'On'
|
||||||
|
gen_linking: dynamic
|
||||||
|
generate_options: qt_gui
|
||||||
|
hier_block_src_path: '.:'
|
||||||
|
id: default
|
||||||
|
max_nouts: '0'
|
||||||
|
output_language: python
|
||||||
|
placement: (0,0)
|
||||||
|
qt_qss_theme: ''
|
||||||
|
realtime_scheduling: ''
|
||||||
|
run: 'True'
|
||||||
|
run_command: '{python} -u {filename}'
|
||||||
|
run_options: prompt
|
||||||
|
sizing_mode: fixed
|
||||||
|
thread_safe_setters: ''
|
||||||
|
title: FM Radio Receiver
|
||||||
|
window_size: 1280, 1024
|
||||||
|
states:
|
||||||
|
bus_sink: false
|
||||||
|
bus_source: false
|
||||||
|
bus_structure: null
|
||||||
|
coordinate: [8, 8]
|
||||||
|
rotation: 0
|
||||||
|
state: enabled
|
||||||
|
|
||||||
|
blocks:
|
||||||
|
- name: samp_rate
|
||||||
|
id: variable
|
||||||
|
parameters:
|
||||||
|
comment: ''
|
||||||
|
value: '2400000'
|
||||||
|
states:
|
||||||
|
bus_sink: false
|
||||||
|
bus_source: false
|
||||||
|
bus_structure: null
|
||||||
|
coordinate: [200, 12]
|
||||||
|
rotation: 0
|
||||||
|
state: enabled
|
||||||
|
- name: variable_qtgui_range_0
|
||||||
|
id: variable_qtgui_range
|
||||||
|
parameters:
|
||||||
|
comment: ''
|
||||||
|
gui_hint: ''
|
||||||
|
label: Frequency
|
||||||
|
min_len: '200'
|
||||||
|
orient: QtCore.Qt.Horizontal
|
||||||
|
rangeType: float
|
||||||
|
start: '87500000'
|
||||||
|
step: '100000'
|
||||||
|
stop: '108000000'
|
||||||
|
value: '95500000'
|
||||||
|
widget: counter_slider
|
||||||
|
states:
|
||||||
|
bus_sink: false
|
||||||
|
bus_source: false
|
||||||
|
bus_structure: null
|
||||||
|
state: enabled
|
||||||
|
- name: variable_qtgui_range_1
|
||||||
|
id: variable_qtgui_range
|
||||||
|
parameters:
|
||||||
|
comment: ''
|
||||||
|
gui_hint: ''
|
||||||
|
label: Volume
|
||||||
|
min_len: '200'
|
||||||
|
orient: QtCore.Qt.Horizontal
|
||||||
|
rangeType: float
|
||||||
|
start: '0'
|
||||||
|
step: '0.1'
|
||||||
|
stop: '2'
|
||||||
|
value: '1'
|
||||||
|
widget: slider
|
||||||
|
states:
|
||||||
|
bus_sink: false
|
||||||
|
bus_source: false
|
||||||
|
bus_structure: null
|
||||||
|
state: enabled
|
||||||
|
- name: analog_wfm_rcv_0
|
||||||
|
id: analog_wfm_rcv
|
||||||
|
parameters:
|
||||||
|
affinity: ''
|
||||||
|
alias: ''
|
||||||
|
audio_decimation: '10'
|
||||||
|
comment: ''
|
||||||
|
maxoutbuf: '0'
|
||||||
|
minoutbuf: '0'
|
||||||
|
quad_rate: '480000'
|
||||||
|
states:
|
||||||
|
bus_sink: false
|
||||||
|
bus_source: false
|
||||||
|
bus_structure: null
|
||||||
|
state: enabled
|
||||||
|
- name: audio_sink_0
|
||||||
|
id: audio_sink
|
||||||
|
parameters:
|
||||||
|
affinity: ''
|
||||||
|
alias: ''
|
||||||
|
comment: ''
|
||||||
|
device_name: ''
|
||||||
|
num_inputs: '1'
|
||||||
|
ok_to_block: 'True'
|
||||||
|
samp_rate: '48000'
|
||||||
|
states:
|
||||||
|
bus_sink: false
|
||||||
|
bus_source: false
|
||||||
|
bus_structure: null
|
||||||
|
state: enabled
|
||||||
|
- name: blocks_multiply_const_vxx_0
|
||||||
|
id: blocks_multiply_const_vxx
|
||||||
|
parameters:
|
||||||
|
affinity: ''
|
||||||
|
alias: ''
|
||||||
|
comment: ''
|
||||||
|
const: variable_qtgui_range_1
|
||||||
|
maxoutbuf: '0'
|
||||||
|
minoutbuf: '0'
|
||||||
|
type: float
|
||||||
|
vlen: '1'
|
||||||
|
states:
|
||||||
|
bus_sink: false
|
||||||
|
bus_source: false
|
||||||
|
bus_structure: null
|
||||||
|
state: enabled
|
||||||
|
- name: low_pass_filter_0
|
||||||
|
id: low_pass_filter
|
||||||
|
parameters:
|
||||||
|
affinity: ''
|
||||||
|
alias: ''
|
||||||
|
beta: '6.76'
|
||||||
|
comment: ''
|
||||||
|
cutoff_freq: '100000'
|
||||||
|
decim: '5'
|
||||||
|
gain: '1'
|
||||||
|
interp: '1'
|
||||||
|
maxoutbuf: '0'
|
||||||
|
minoutbuf: '0'
|
||||||
|
samp_rate: '2400000'
|
||||||
|
type: fir_filter_ccf
|
||||||
|
width: '30000'
|
||||||
|
win: window.WIN_HAMMING
|
||||||
|
states:
|
||||||
|
bus_sink: false
|
||||||
|
bus_source: false
|
||||||
|
bus_structure: null
|
||||||
|
state: enabled
|
||||||
|
- name: qtgui_freq_sink_x_0
|
||||||
|
id: qtgui_freq_sink_x
|
||||||
|
parameters:
|
||||||
|
affinity: ''
|
||||||
|
alias: ''
|
||||||
|
alpha1: '1.0'
|
||||||
|
alpha10: '1.0'
|
||||||
|
alpha2: '1.0'
|
||||||
|
alpha3: '1.0'
|
||||||
|
alpha4: '1.0'
|
||||||
|
alpha5: '1.0'
|
||||||
|
alpha6: '1.0'
|
||||||
|
alpha7: '1.0'
|
||||||
|
alpha8: '1.0'
|
||||||
|
alpha9: '1.0'
|
||||||
|
autoscale: 'False'
|
||||||
|
average: '1.0'
|
||||||
|
axislabels: 'True'
|
||||||
|
bw: '2400000'
|
||||||
|
color1: '"blue"'
|
||||||
|
color10: '"dark blue"'
|
||||||
|
color2: '"red"'
|
||||||
|
color3: '"green"'
|
||||||
|
color4: '"black"'
|
||||||
|
color5: '"cyan"'
|
||||||
|
color6: '"magenta"'
|
||||||
|
color7: '"yellow"'
|
||||||
|
color8: '"dark red"'
|
||||||
|
color9: '"dark green"'
|
||||||
|
comment: ''
|
||||||
|
ctrlpanel: 'False'
|
||||||
|
fc: variable_qtgui_range_0
|
||||||
|
fftsize: '1024'
|
||||||
|
freqhalf: 'True'
|
||||||
|
grid: 'False'
|
||||||
|
gui_hint: ''
|
||||||
|
label: Relative Gain
|
||||||
|
label1: ''
|
||||||
|
label10: ''''''
|
||||||
|
label2: ''''''
|
||||||
|
label3: ''''''
|
||||||
|
label4: ''''''
|
||||||
|
label5: ''''''
|
||||||
|
label6: ''''''
|
||||||
|
label7: ''''''
|
||||||
|
label8: ''''''
|
||||||
|
label9: ''''''
|
||||||
|
legend: 'True'
|
||||||
|
maxoutbuf: '0'
|
||||||
|
minoutbuf: '0'
|
||||||
|
name: RF Spectrum
|
||||||
|
nconnections: '1'
|
||||||
|
norm_window: 'False'
|
||||||
|
showports: 'False'
|
||||||
|
tr_chan: '0'
|
||||||
|
tr_level: '0.0'
|
||||||
|
tr_mode: qtgui.TRIG_MODE_FREE
|
||||||
|
tr_tag: '""'
|
||||||
|
type: complex
|
||||||
|
units: dB
|
||||||
|
update_time: '0.10'
|
||||||
|
width1: '1'
|
||||||
|
width10: '1'
|
||||||
|
width2: '1'
|
||||||
|
width3: '1'
|
||||||
|
width4: '1'
|
||||||
|
width5: '1'
|
||||||
|
width6: '1'
|
||||||
|
width7: '1'
|
||||||
|
width8: '1'
|
||||||
|
width9: '1'
|
||||||
|
wintype: window.WIN_BLACKMAN_hARRIS
|
||||||
|
ymax: '10'
|
||||||
|
ymin: '-140'
|
||||||
|
states:
|
||||||
|
bus_sink: false
|
||||||
|
bus_source: false
|
||||||
|
bus_structure: null
|
||||||
|
state: enabled
|
||||||
|
- name: soapy_rtlsdr_source_0
|
||||||
|
id: soapy_rtlsdr_source
|
||||||
|
parameters:
|
||||||
|
affinity: ''
|
||||||
|
agc: 'True'
|
||||||
|
alias: ''
|
||||||
|
bias: 'False'
|
||||||
|
bufflen: '16384'
|
||||||
|
center_freq: variable_qtgui_range_0
|
||||||
|
comment: ''
|
||||||
|
dev_args: ''
|
||||||
|
freq_correction: '0'
|
||||||
|
gain: '20'
|
||||||
|
maxoutbuf: '0'
|
||||||
|
minoutbuf: '0'
|
||||||
|
samp_rate: '2400000'
|
||||||
|
type: fc32
|
||||||
|
states:
|
||||||
|
bus_sink: false
|
||||||
|
bus_source: false
|
||||||
|
bus_structure: null
|
||||||
|
state: enabled
|
||||||
|
|
||||||
|
connections:
|
||||||
|
- [analog_wfm_rcv_0, '0', blocks_multiply_const_vxx_0, '0']
|
||||||
|
- [blocks_multiply_const_vxx_0, '0', audio_sink_0, '0']
|
||||||
|
- [low_pass_filter_0, '0', analog_wfm_rcv_0, '0']
|
||||||
|
- [soapy_rtlsdr_source_0, '0', low_pass_filter_0, '0']
|
||||||
|
- [soapy_rtlsdr_source_0, '0', qtgui_freq_sink_x_0, '0']
|
||||||
|
|
||||||
|
metadata:
|
||||||
|
file_format: 1
|
||||||
|
grc_version: 3.10.12.0
|
||||||
288
fm_radio_receiver_with_signal_meter.grc
Normal file
288
fm_radio_receiver_with_signal_meter.grc
Normal file
@ -0,0 +1,288 @@
|
|||||||
|
options:
|
||||||
|
parameters:
|
||||||
|
author: ''
|
||||||
|
catch_exceptions: 'True'
|
||||||
|
category: '[GRC Hier Blocks]'
|
||||||
|
cmake_opt: ''
|
||||||
|
comment: ''
|
||||||
|
copyright: ''
|
||||||
|
description: ''
|
||||||
|
gen_cmake: 'On'
|
||||||
|
gen_linking: dynamic
|
||||||
|
generate_options: qt_gui
|
||||||
|
hier_block_src_path: '.:'
|
||||||
|
id: default
|
||||||
|
max_nouts: '0'
|
||||||
|
output_language: python
|
||||||
|
placement: (0,0)
|
||||||
|
qt_qss_theme: ''
|
||||||
|
realtime_scheduling: ''
|
||||||
|
run: 'True'
|
||||||
|
run_command: '{python} -u {filename}'
|
||||||
|
run_options: prompt
|
||||||
|
sizing_mode: fixed
|
||||||
|
thread_safe_setters: ''
|
||||||
|
title: Not titled yet
|
||||||
|
window_size: (1000,1000)
|
||||||
|
states:
|
||||||
|
bus_sink: false
|
||||||
|
bus_source: false
|
||||||
|
bus_structure: null
|
||||||
|
coordinate: [296, 4.0]
|
||||||
|
rotation: 0
|
||||||
|
state: enabled
|
||||||
|
|
||||||
|
blocks:
|
||||||
|
- name: freq
|
||||||
|
id: variable_qtgui_range
|
||||||
|
parameters:
|
||||||
|
comment: ''
|
||||||
|
gui_hint: ''
|
||||||
|
label: Frequency
|
||||||
|
min_len: '200'
|
||||||
|
orient: QtCore.Qt.Horizontal
|
||||||
|
rangeType: float
|
||||||
|
start: '88000000'
|
||||||
|
step: '100000'
|
||||||
|
stop: '108000000'
|
||||||
|
value: '95700000'
|
||||||
|
widget: counter_slider
|
||||||
|
states:
|
||||||
|
bus_sink: false
|
||||||
|
bus_source: false
|
||||||
|
bus_structure: null
|
||||||
|
coordinate: [48, 44.0]
|
||||||
|
rotation: 0
|
||||||
|
state: enabled
|
||||||
|
- name: rf_gain
|
||||||
|
id: variable_qtgui_range
|
||||||
|
parameters:
|
||||||
|
comment: ''
|
||||||
|
gui_hint: ''
|
||||||
|
label: RF Gain
|
||||||
|
min_len: '200'
|
||||||
|
orient: QtCore.Qt.Horizontal
|
||||||
|
rangeType: float
|
||||||
|
start: '0'
|
||||||
|
step: '1'
|
||||||
|
stop: '47'
|
||||||
|
value: '20'
|
||||||
|
widget: counter_slider
|
||||||
|
states:
|
||||||
|
bus_sink: false
|
||||||
|
bus_source: false
|
||||||
|
bus_structure: null
|
||||||
|
coordinate: [568, 116.0]
|
||||||
|
rotation: 0
|
||||||
|
state: enabled
|
||||||
|
- name: samp_rate
|
||||||
|
id: variable
|
||||||
|
parameters:
|
||||||
|
comment: ''
|
||||||
|
value: '32000'
|
||||||
|
states:
|
||||||
|
bus_sink: false
|
||||||
|
bus_source: false
|
||||||
|
bus_structure: null
|
||||||
|
coordinate: [200, 12]
|
||||||
|
rotation: 0
|
||||||
|
state: enabled
|
||||||
|
- name: variable_qtgui_range_0
|
||||||
|
id: variable_qtgui_range
|
||||||
|
parameters:
|
||||||
|
comment: ''
|
||||||
|
gui_hint: ''
|
||||||
|
label: ''
|
||||||
|
min_len: '200'
|
||||||
|
orient: QtCore.Qt.Horizontal
|
||||||
|
rangeType: float
|
||||||
|
start: '0'
|
||||||
|
step: '1'
|
||||||
|
stop: '100'
|
||||||
|
value: '50'
|
||||||
|
widget: counter_slider
|
||||||
|
states:
|
||||||
|
bus_sink: false
|
||||||
|
bus_source: false
|
||||||
|
bus_structure: null
|
||||||
|
coordinate: [184, 104.0]
|
||||||
|
rotation: 0
|
||||||
|
state: enabled
|
||||||
|
- name: volume
|
||||||
|
id: variable
|
||||||
|
parameters:
|
||||||
|
comment: ''
|
||||||
|
value: '1'
|
||||||
|
states:
|
||||||
|
bus_sink: false
|
||||||
|
bus_source: false
|
||||||
|
bus_structure: null
|
||||||
|
coordinate: [384, 128.0]
|
||||||
|
rotation: 0
|
||||||
|
state: enabled
|
||||||
|
- name: analog_wfm_rcv_1
|
||||||
|
id: analog_wfm_rcv
|
||||||
|
parameters:
|
||||||
|
affinity: ''
|
||||||
|
alias: ''
|
||||||
|
audio_decimation: '8'
|
||||||
|
comment: ''
|
||||||
|
maxoutbuf: '0'
|
||||||
|
minoutbuf: '0'
|
||||||
|
quad_rate: '2048000'
|
||||||
|
states:
|
||||||
|
bus_sink: false
|
||||||
|
bus_source: false
|
||||||
|
bus_structure: null
|
||||||
|
coordinate: [400, 240.0]
|
||||||
|
rotation: 0
|
||||||
|
state: enabled
|
||||||
|
- name: audio_sink_0
|
||||||
|
id: audio_sink
|
||||||
|
parameters:
|
||||||
|
affinity: ''
|
||||||
|
alias: ''
|
||||||
|
comment: ''
|
||||||
|
device_name: ''
|
||||||
|
num_inputs: '1'
|
||||||
|
ok_to_block: 'True'
|
||||||
|
samp_rate: samp_rate
|
||||||
|
states:
|
||||||
|
bus_sink: false
|
||||||
|
bus_source: false
|
||||||
|
bus_structure: null
|
||||||
|
coordinate: [848, 204.0]
|
||||||
|
rotation: 0
|
||||||
|
state: enabled
|
||||||
|
- name: blocks_complex_to_mag_squared_0
|
||||||
|
id: blocks_complex_to_mag_squared
|
||||||
|
parameters:
|
||||||
|
affinity: ''
|
||||||
|
alias: ''
|
||||||
|
comment: ''
|
||||||
|
maxoutbuf: '0'
|
||||||
|
minoutbuf: '0'
|
||||||
|
vlen: '1'
|
||||||
|
states:
|
||||||
|
bus_sink: false
|
||||||
|
bus_source: false
|
||||||
|
bus_structure: null
|
||||||
|
coordinate: [456, 484.0]
|
||||||
|
rotation: 0
|
||||||
|
state: enabled
|
||||||
|
- name: blocks_multiply_const_vxx_0
|
||||||
|
id: blocks_multiply_const_vxx
|
||||||
|
parameters:
|
||||||
|
affinity: ''
|
||||||
|
alias: ''
|
||||||
|
comment: ''
|
||||||
|
const: volume
|
||||||
|
maxoutbuf: '0'
|
||||||
|
minoutbuf: '0'
|
||||||
|
type: float
|
||||||
|
vlen: '1'
|
||||||
|
states:
|
||||||
|
bus_sink: false
|
||||||
|
bus_source: false
|
||||||
|
bus_structure: null
|
||||||
|
coordinate: [696, 220.0]
|
||||||
|
rotation: 0
|
||||||
|
state: enabled
|
||||||
|
- name: qtgui_number_sink_0
|
||||||
|
id: qtgui_number_sink
|
||||||
|
parameters:
|
||||||
|
affinity: ''
|
||||||
|
alias: ''
|
||||||
|
autoscale: 'False'
|
||||||
|
avg: '0'
|
||||||
|
color1: ("black", "black")
|
||||||
|
color10: ("black", "black")
|
||||||
|
color2: ("black", "black")
|
||||||
|
color3: ("black", "black")
|
||||||
|
color4: ("black", "black")
|
||||||
|
color5: ("black", "black")
|
||||||
|
color6: ("black", "black")
|
||||||
|
color7: ("black", "black")
|
||||||
|
color8: ("black", "black")
|
||||||
|
color9: ("black", "black")
|
||||||
|
comment: ''
|
||||||
|
factor1: '1'
|
||||||
|
factor10: '1'
|
||||||
|
factor2: '1'
|
||||||
|
factor3: '1'
|
||||||
|
factor4: '1'
|
||||||
|
factor5: '1'
|
||||||
|
factor6: '1'
|
||||||
|
factor7: '1'
|
||||||
|
factor8: '1'
|
||||||
|
factor9: '1'
|
||||||
|
graph_type: qtgui.NUM_GRAPH_HORIZ
|
||||||
|
gui_hint: ''
|
||||||
|
label1: ''
|
||||||
|
label10: ''
|
||||||
|
label2: ''
|
||||||
|
label3: ''
|
||||||
|
label4: ''
|
||||||
|
label5: ''
|
||||||
|
label6: ''
|
||||||
|
label7: ''
|
||||||
|
label8: ''
|
||||||
|
label9: ''
|
||||||
|
max: '1'
|
||||||
|
min: '-1'
|
||||||
|
name: '""'
|
||||||
|
nconnections: '1'
|
||||||
|
type: float
|
||||||
|
unit1: ''
|
||||||
|
unit10: ''
|
||||||
|
unit2: ''
|
||||||
|
unit3: ''
|
||||||
|
unit4: ''
|
||||||
|
unit5: ''
|
||||||
|
unit6: ''
|
||||||
|
unit7: ''
|
||||||
|
unit8: ''
|
||||||
|
unit9: ''
|
||||||
|
update_time: '0.10'
|
||||||
|
states:
|
||||||
|
bus_sink: false
|
||||||
|
bus_source: false
|
||||||
|
bus_structure: null
|
||||||
|
coordinate: [856, 320.0]
|
||||||
|
rotation: 0
|
||||||
|
state: enabled
|
||||||
|
- name: soapy_rtlsdr_source_1
|
||||||
|
id: soapy_rtlsdr_source
|
||||||
|
parameters:
|
||||||
|
affinity: ''
|
||||||
|
agc: 'False'
|
||||||
|
alias: ''
|
||||||
|
bias: 'False'
|
||||||
|
bufflen: '16384'
|
||||||
|
center_freq: freq
|
||||||
|
comment: ''
|
||||||
|
dev_args: ''
|
||||||
|
freq_correction: '0'
|
||||||
|
gain: rf_gain
|
||||||
|
maxoutbuf: '0'
|
||||||
|
minoutbuf: '0'
|
||||||
|
samp_rate: '2048000'
|
||||||
|
type: fc32
|
||||||
|
states:
|
||||||
|
bus_sink: false
|
||||||
|
bus_source: false
|
||||||
|
bus_structure: null
|
||||||
|
coordinate: [88, 352.0]
|
||||||
|
rotation: 0
|
||||||
|
state: enabled
|
||||||
|
|
||||||
|
connections:
|
||||||
|
- [analog_wfm_rcv_1, '0', blocks_multiply_const_vxx_0, '0']
|
||||||
|
- [blocks_complex_to_mag_squared_0, '0', qtgui_number_sink_0, '0']
|
||||||
|
- [blocks_multiply_const_vxx_0, '0', audio_sink_0, '0']
|
||||||
|
- [soapy_rtlsdr_source_1, '0', analog_wfm_rcv_1, '0']
|
||||||
|
- [soapy_rtlsdr_source_1, '0', blocks_complex_to_mag_squared_0, '0']
|
||||||
|
|
||||||
|
metadata:
|
||||||
|
file_format: 1
|
||||||
|
grc_version: 3.10.12.0
|
||||||
126
fm_receiver.grc
Normal file
126
fm_receiver.grc
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
options:
|
||||||
|
parameters:
|
||||||
|
author: ''
|
||||||
|
catch_exceptions: 'True'
|
||||||
|
category: '[GRC Hier Blocks]'
|
||||||
|
cmake_opt: ''
|
||||||
|
comment: ''
|
||||||
|
copyright: ''
|
||||||
|
description: ''
|
||||||
|
gen_cmake: 'On'
|
||||||
|
gen_linking: dynamic
|
||||||
|
generate_options: qt_gui
|
||||||
|
hier_block_src_path: '.:'
|
||||||
|
id: default
|
||||||
|
max_nouts: '0'
|
||||||
|
output_language: python
|
||||||
|
placement: (0,0)
|
||||||
|
qt_qss_theme: ''
|
||||||
|
realtime_scheduling: ''
|
||||||
|
run: 'True'
|
||||||
|
run_command: '{python} -u {filename}'
|
||||||
|
run_options: prompt
|
||||||
|
sizing_mode: fixed
|
||||||
|
thread_safe_setters: ''
|
||||||
|
title: Not titled yet
|
||||||
|
window_size: (1000,1000)
|
||||||
|
states:
|
||||||
|
bus_sink: false
|
||||||
|
bus_source: false
|
||||||
|
bus_structure: null
|
||||||
|
coordinate: [8, 8]
|
||||||
|
rotation: 0
|
||||||
|
state: enabled
|
||||||
|
|
||||||
|
blocks:
|
||||||
|
- name: samp_rate
|
||||||
|
id: variable
|
||||||
|
parameters:
|
||||||
|
comment: ''
|
||||||
|
value: '32000'
|
||||||
|
states:
|
||||||
|
bus_sink: false
|
||||||
|
bus_source: false
|
||||||
|
bus_structure: null
|
||||||
|
coordinate: [200, 12]
|
||||||
|
rotation: 0
|
||||||
|
state: enabled
|
||||||
|
- name: analog_wfm_rcv_0
|
||||||
|
id: analog_wfm_rcv
|
||||||
|
parameters:
|
||||||
|
affinity: ''
|
||||||
|
alias: ''
|
||||||
|
audio_decimation: '10'
|
||||||
|
comment: ''
|
||||||
|
maxoutbuf: '0'
|
||||||
|
minoutbuf: '0'
|
||||||
|
quad_rate: '480000'
|
||||||
|
states:
|
||||||
|
bus_sink: false
|
||||||
|
bus_source: false
|
||||||
|
bus_structure: null
|
||||||
|
state: enabled
|
||||||
|
- name: audio_sink_0
|
||||||
|
id: audio_sink
|
||||||
|
parameters:
|
||||||
|
affinity: ''
|
||||||
|
alias: ''
|
||||||
|
comment: ''
|
||||||
|
device_name: ''
|
||||||
|
num_inputs: '1'
|
||||||
|
ok_to_block: 'True'
|
||||||
|
samp_rate: '44100'
|
||||||
|
states:
|
||||||
|
bus_sink: false
|
||||||
|
bus_source: false
|
||||||
|
bus_structure: null
|
||||||
|
state: enabled
|
||||||
|
- name: rational_resampler_xxx_0
|
||||||
|
id: rational_resampler_xxx
|
||||||
|
parameters:
|
||||||
|
affinity: ''
|
||||||
|
alias: ''
|
||||||
|
comment: ''
|
||||||
|
decim: '480'
|
||||||
|
fbw: '0'
|
||||||
|
interp: '441'
|
||||||
|
maxoutbuf: '0'
|
||||||
|
minoutbuf: '0'
|
||||||
|
taps: '[]'
|
||||||
|
type: fff
|
||||||
|
states:
|
||||||
|
bus_sink: false
|
||||||
|
bus_source: false
|
||||||
|
bus_structure: null
|
||||||
|
state: enabled
|
||||||
|
- name: soapy_rtlsdr_source_0
|
||||||
|
id: soapy_rtlsdr_source
|
||||||
|
parameters:
|
||||||
|
affinity: ''
|
||||||
|
agc: 'False'
|
||||||
|
alias: ''
|
||||||
|
bias: 'False'
|
||||||
|
bufflen: '16384'
|
||||||
|
center_freq: '95500000'
|
||||||
|
comment: ''
|
||||||
|
dev_args: ''
|
||||||
|
freq_correction: '0'
|
||||||
|
gain: '20'
|
||||||
|
maxoutbuf: '0'
|
||||||
|
minoutbuf: '0'
|
||||||
|
samp_rate: '2457600'
|
||||||
|
type: fc32
|
||||||
|
states:
|
||||||
|
bus_sink: false
|
||||||
|
bus_source: false
|
||||||
|
bus_structure: null
|
||||||
|
state: enabled
|
||||||
|
|
||||||
|
connections:
|
||||||
|
- [analog_wfm_rcv_0, '0', rational_resampler_xxx_0, '0']
|
||||||
|
- [rational_resampler_xxx_0, '0', audio_sink_0, '0']
|
||||||
|
- [soapy_rtlsdr_source_0, '0', analog_wfm_rcv_0, '0']
|
||||||
|
|
||||||
|
metadata:
|
||||||
|
file_format: 1
|
||||||
|
grc_version: 3.10.12.0
|
||||||
70
grc/main.py
70
grc/main.py
@ -69,6 +69,71 @@ log = logging.getLogger('grc')
|
|||||||
# The default console logging should be WARNING
|
# The default console logging should be WARNING
|
||||||
log.setLevel(logging.DEBUG)
|
log.setLevel(logging.DEBUG)
|
||||||
|
|
||||||
|
def run_api(args, log):
|
||||||
|
|
||||||
|
from .core.platform import Platform
|
||||||
|
from .core.FlowGraph import FlowGraph
|
||||||
|
|
||||||
|
platform = Platform(
|
||||||
|
version=gr.version(),
|
||||||
|
version_parts=(gr.major_version(), gr.api_version(), gr.minor_version()),
|
||||||
|
prefs=gr.prefs(),
|
||||||
|
# install_prefix=gr.prefix()
|
||||||
|
)
|
||||||
|
platform.build_library()
|
||||||
|
|
||||||
|
|
||||||
|
from .mcp.mcp import CoreMiddleware
|
||||||
|
mcp = CoreMiddleware(platform, "")
|
||||||
|
|
||||||
|
from fastmcp import FastMCP
|
||||||
|
|
||||||
|
app = FastMCP("Demo 🚀")
|
||||||
|
|
||||||
|
app.tool()(mcp.list_all_blocks)
|
||||||
|
app.tool()(mcp.list_block_input_data)
|
||||||
|
app.tool()(mcp.list_block_output_data)
|
||||||
|
app.tool()(mcp.list_block_parameters_data)
|
||||||
|
|
||||||
|
app.tool()(mcp.get_placed_blocks)
|
||||||
|
app.tool()(mcp.get_placed_connections)
|
||||||
|
app.tool()(mcp.get_placed_block_params)
|
||||||
|
|
||||||
|
app.tool()(mcp.validate_block)
|
||||||
|
app.tool()(mcp.validate_connection)
|
||||||
|
app.tool()(mcp.validate_flowgraph)
|
||||||
|
app.tool()(mcp.get_all_errors)
|
||||||
|
|
||||||
|
app.tool()(mcp.add_block)
|
||||||
|
app.tool()(mcp.remove_block)
|
||||||
|
app.tool()(mcp.connect_blocks)
|
||||||
|
app.tool()(mcp.update_block_params)
|
||||||
|
|
||||||
|
app.tool()(mcp.save_flowgraph)
|
||||||
|
|
||||||
|
|
||||||
|
app.run(transport='sse')
|
||||||
|
|
||||||
|
|
||||||
|
# print(mcp.flowgraph_blocks())
|
||||||
|
# print(mcp.flowgraph_connections())
|
||||||
|
# initial_state = platform.parse_flow_graph("")
|
||||||
|
# fg = FlowGraph(platform)
|
||||||
|
# fg.import_data(initial_state)
|
||||||
|
# source = fg.new_block('analog_const_source_x')
|
||||||
|
# source.params['id'].set_value('test1')
|
||||||
|
# sink = fg.new_block('virtual_sink')
|
||||||
|
# sink.params['id'].set_value('test2')
|
||||||
|
# conn = fg.connect(source.sources[0], sink.sinks[0])
|
||||||
|
# platform.save_flow_graph('test.grc', fg)
|
||||||
|
|
||||||
|
# mcp.add_block('analog_const_source_x')
|
||||||
|
# mcp.add_block('virtual_sink')
|
||||||
|
# mcp.connect_blocks('analog_const_source_x_0', 0, 'virtual_sink_0', 0)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def run_gtk(args, log):
|
def run_gtk(args, log):
|
||||||
''' Runs the GTK version of GNU Radio Companion '''
|
''' Runs the GTK version of GNU Radio Companion '''
|
||||||
@ -182,7 +247,6 @@ def run_qt(args, log):
|
|||||||
install_prefix=gr.prefix()
|
install_prefix=gr.prefix()
|
||||||
)
|
)
|
||||||
model.build_library()
|
model.build_library()
|
||||||
|
|
||||||
# Launch GRC
|
# Launch GRC
|
||||||
app = grc.Application(settings, model, args.flow_graphs)
|
app = grc.Application(settings, model, args.flow_graphs)
|
||||||
sys.exit(app.run())
|
sys.exit(app.run())
|
||||||
@ -285,6 +349,8 @@ def main():
|
|||||||
help="GNU Radio Companion (QT)")
|
help="GNU Radio Companion (QT)")
|
||||||
gui_group_exclusive.add_argument("--gtk", dest='framework', action='store_const', const='gtk',
|
gui_group_exclusive.add_argument("--gtk", dest='framework', action='store_const', const='gtk',
|
||||||
help="GNU Radio Companion (GTK)")
|
help="GNU Radio Companion (GTK)")
|
||||||
|
gui_group_exclusive.add_argument("--api", dest='framework', action='store_const', const='api',
|
||||||
|
help="GNU Radio Companion (API)")
|
||||||
|
|
||||||
# Default options if not already set with add_argument()
|
# Default options if not already set with add_argument()
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
@ -316,6 +382,8 @@ def main():
|
|||||||
run_qt(args, log)
|
run_qt(args, log)
|
||||||
elif args.framework == 'gtk':
|
elif args.framework == 'gtk':
|
||||||
run_gtk(args, log)
|
run_gtk(args, log)
|
||||||
|
elif args.framework == 'api':
|
||||||
|
run_api(args, log)
|
||||||
else: # args.framework == None
|
else: # args.framework == None
|
||||||
if grc_version_from_config == 'grc_qt':
|
if grc_version_from_config == 'grc_qt':
|
||||||
run_qt(args, log)
|
run_qt(args, log)
|
||||||
|
|||||||
0
grc/mcp/__init__.py
Normal file
0
grc/mcp/__init__.py
Normal file
247
grc/mcp/mcp.py
Normal file
247
grc/mcp/mcp.py
Normal file
@ -0,0 +1,247 @@
|
|||||||
|
from itertools import count
|
||||||
|
import re
|
||||||
|
from typing import Any, Dict, List, Optional, Set
|
||||||
|
|
||||||
|
from grc.core.FlowGraph import FlowGraph
|
||||||
|
from grc.core.blocks.block import Block
|
||||||
|
from grc.core.params.param import Param
|
||||||
|
from grc.core.Connection import Connection
|
||||||
|
from grc.core.platform import Platform
|
||||||
|
from grc.core.ports.port import Port
|
||||||
|
|
||||||
|
|
||||||
|
class CoreMiddleware:
|
||||||
|
def __init__(self, platform: Platform, filepath: Optional[str] = ""):
|
||||||
|
|
||||||
|
self._platform = platform
|
||||||
|
|
||||||
|
initial_state = platform.parse_flow_graph(filepath)
|
||||||
|
self._flowgraph = FlowGraph(platform)
|
||||||
|
self._flowgraph.import_data(initial_state)
|
||||||
|
|
||||||
|
##############################################
|
||||||
|
# Static Data
|
||||||
|
##############################################
|
||||||
|
|
||||||
|
def list_all_blocks(self) -> List[Dict[str, Any]]:
|
||||||
|
blocks_data = []
|
||||||
|
for block in self._platform.blocks.values():
|
||||||
|
blocks_data.append(
|
||||||
|
{
|
||||||
|
"key": block.key,
|
||||||
|
"label": block.label,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
return blocks_data
|
||||||
|
|
||||||
|
def list_block_parameters_data(self, block_key: str) -> List[str]:
|
||||||
|
block = self._platform.blocks[block_key]
|
||||||
|
return block.parameters_data
|
||||||
|
|
||||||
|
def list_block_output_data(self, block_key: str) -> List[Dict[str, Any]]:
|
||||||
|
block = self._platform.blocks[block_key]
|
||||||
|
return block.outputs_data
|
||||||
|
|
||||||
|
def list_block_input_data(self, block_key: str) -> List[Dict[str, Any]]:
|
||||||
|
block = self._platform.blocks[block_key]
|
||||||
|
return block.inputs_data
|
||||||
|
|
||||||
|
##############################################
|
||||||
|
# Flowgraph Data
|
||||||
|
##############################################
|
||||||
|
|
||||||
|
def get_placed_blocks(self) -> List[Dict[str, Any]]:
|
||||||
|
blocks_data = []
|
||||||
|
for block in self._flowgraph.blocks:
|
||||||
|
blocks_data.append(
|
||||||
|
{
|
||||||
|
"key": block.key,
|
||||||
|
"name": block.params["id"].get_value(),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
return blocks_data
|
||||||
|
|
||||||
|
def get_placed_block_params(self, block_name: str) -> Dict[str, Any]:
|
||||||
|
block = self._flowgraph.get_block(block_name)
|
||||||
|
return {param.key: param.get_value() for param in block.params.values()}
|
||||||
|
|
||||||
|
def get_placed_connections(self) -> Set[Dict[str, Any]]:
|
||||||
|
connections_data = []
|
||||||
|
for connection in self._flowgraph.connections:
|
||||||
|
connection: Connection = connection
|
||||||
|
source_port: Port = connection.source_port
|
||||||
|
sink_port: Port = connection.sink_port
|
||||||
|
connections_data.append(
|
||||||
|
{
|
||||||
|
"src_block": source_port.parent.key,
|
||||||
|
"src_port": source_port.key,
|
||||||
|
"dst_block": sink_port.parent.key,
|
||||||
|
"dst_port": sink_port.key,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
return connections_data
|
||||||
|
|
||||||
|
##############################################
|
||||||
|
# Flowgraph Operations
|
||||||
|
##############################################
|
||||||
|
|
||||||
|
def add_block(self, block_key: str, name: Optional[str] = None) -> Block:
|
||||||
|
name = name or self._get_unique_id(block_key)
|
||||||
|
block = self._flowgraph.new_block(block_key)
|
||||||
|
block.params["id"].set_value(name)
|
||||||
|
return "success"
|
||||||
|
|
||||||
|
def remove_block(self, block_name: str) -> None:
|
||||||
|
block = self._flowgraph.get_block(block_name)
|
||||||
|
self._flowgraph.remove_element(block)
|
||||||
|
return "success"
|
||||||
|
|
||||||
|
def connect_blocks(
|
||||||
|
self,
|
||||||
|
src_block_name: str,
|
||||||
|
src_port_index: int,
|
||||||
|
dst_block_name: str,
|
||||||
|
dst_port_index: int,
|
||||||
|
) -> Connection:
|
||||||
|
src_block = self._flowgraph.get_block(src_block_name)
|
||||||
|
dst_block = self._flowgraph.get_block(dst_block_name)
|
||||||
|
self._flowgraph.connect(
|
||||||
|
src_block.sources[src_port_index], dst_block.sinks[dst_port_index]
|
||||||
|
)
|
||||||
|
return "success"
|
||||||
|
|
||||||
|
def disconnect_blocks(
|
||||||
|
self,
|
||||||
|
src_block_name: str,
|
||||||
|
src_port_index: int,
|
||||||
|
dst_block_name: str,
|
||||||
|
dst_port_index: int,
|
||||||
|
) -> None:
|
||||||
|
src_block = self._flowgraph.get_block(src_block_name)
|
||||||
|
dst_block = self._flowgraph.get_block(dst_block_name)
|
||||||
|
self._flowgraph.disconnect(
|
||||||
|
src_block.sources[src_port_index], dst_block.sinks[dst_port_index]
|
||||||
|
)
|
||||||
|
return "success"
|
||||||
|
|
||||||
|
def update_block_params(self, block_name: str, params: Dict[str, Any]) -> None:
|
||||||
|
block = self._flowgraph.get_block(block_name)
|
||||||
|
for param_name, param_value in params.items():
|
||||||
|
block.params[param_name].set_value(param_value)
|
||||||
|
return "success"
|
||||||
|
|
||||||
|
##############################################
|
||||||
|
# Flowgraph Validation
|
||||||
|
##############################################
|
||||||
|
|
||||||
|
def validate_block(self, block_name: str) -> bool:
|
||||||
|
self._flowgraph.rewrite()
|
||||||
|
block = self._flowgraph.get_block(block_name)
|
||||||
|
block.validate()
|
||||||
|
return block.is_valid()
|
||||||
|
|
||||||
|
def validate_connection(
|
||||||
|
self,
|
||||||
|
src_block_name: str,
|
||||||
|
src_port_index: int,
|
||||||
|
dst_block_name: str,
|
||||||
|
dst_port_index: int,
|
||||||
|
) -> bool:
|
||||||
|
self._flowgraph.rewrite()
|
||||||
|
connections: Set[Connection] = self._flowgraph.connections
|
||||||
|
for connection in connections:
|
||||||
|
if (
|
||||||
|
connection.source_port.parent.key == src_block_name
|
||||||
|
and connection.sink_port.parent.key == dst_block_name
|
||||||
|
and connection.source_port.key == src_port_index
|
||||||
|
and connection.sink_port.key == dst_port_index
|
||||||
|
):
|
||||||
|
connection.validate()
|
||||||
|
return connection.is_valid()
|
||||||
|
raise ValueError("Connection not found")
|
||||||
|
|
||||||
|
def validate_flowgraph(self) -> bool:
|
||||||
|
self._flowgraph.rewrite()
|
||||||
|
self._flowgraph.validate()
|
||||||
|
return self._flowgraph.is_valid()
|
||||||
|
|
||||||
|
def get_all_errors(self) -> List[Dict[str, Any]]:
|
||||||
|
self._flowgraph.rewrite()
|
||||||
|
self._flowgraph.validate()
|
||||||
|
errors = []
|
||||||
|
for elem, msg in self._flowgraph.iter_error_messages():
|
||||||
|
msg = re.sub("[^A-Za-z0-9]+", " ", msg).strip()
|
||||||
|
if isinstance(elem, Connection):
|
||||||
|
connection: Connection = elem
|
||||||
|
source_port: Port = elem.source_port
|
||||||
|
sink_port: Port = elem.sink_port
|
||||||
|
errors.append(
|
||||||
|
{
|
||||||
|
"type": "connection",
|
||||||
|
"key": {
|
||||||
|
"src_block": source_port.parent.key,
|
||||||
|
"src_port": source_port.key,
|
||||||
|
"dst_block": sink_port.parent.key,
|
||||||
|
"dst_port": sink_port.key,
|
||||||
|
},
|
||||||
|
"message": msg,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
if isinstance(elem, Param):
|
||||||
|
errors.append(
|
||||||
|
{
|
||||||
|
"type": "param",
|
||||||
|
"key": f"{elem.parent.params["id"].get_value()}:{elem.key}",
|
||||||
|
"message": msg,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
if isinstance(elem, Port):
|
||||||
|
errors.append(
|
||||||
|
{
|
||||||
|
"type": "port",
|
||||||
|
"key": f"{elem.parent.params["id"].get_value()}:{elem.key}",
|
||||||
|
"message": msg,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
if isinstance(elem, Block):
|
||||||
|
errors.append(
|
||||||
|
{
|
||||||
|
"type": "block",
|
||||||
|
"key": elem.params["id"].get_value(),
|
||||||
|
"message": msg,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
return errors
|
||||||
|
|
||||||
|
##############################################
|
||||||
|
# Misc
|
||||||
|
##############################################
|
||||||
|
|
||||||
|
def save_flowgraph(self, filepath: str) -> None:
|
||||||
|
self._platform.save_flow_graph(filepath, self._flowgraph)
|
||||||
|
return "success"
|
||||||
|
|
||||||
|
##############################################
|
||||||
|
# Helper Functions
|
||||||
|
##############################################
|
||||||
|
|
||||||
|
def _get_unique_id(self, base_id=""):
|
||||||
|
"""
|
||||||
|
Get a unique id starting with the base id.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
base_id: the id starts with this and appends a count
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
a unique id
|
||||||
|
"""
|
||||||
|
block_ids = set(b.name for b in self._flowgraph.blocks)
|
||||||
|
for index in count():
|
||||||
|
block_id = "{}_{}".format(base_id, index)
|
||||||
|
if block_id not in block_ids:
|
||||||
|
break
|
||||||
|
return block_id
|
||||||
Loading…
x
Reference in New Issue
Block a user