130 lines
3.5 KiB
Python
130 lines
3.5 KiB
Python
|
|
|
|
import inspect
|
|
import collections
|
|
|
|
|
|
TYPE_MAP = {
|
|
'complex64': 'complex', 'complex': 'complex',
|
|
'float32': 'float', 'float': 'float',
|
|
'int32': 'int', 'uint32': 'int',
|
|
'int16': 'short', 'uint16': 'short',
|
|
'int8': 'byte', 'uint8': 'byte',
|
|
}
|
|
|
|
BlockIO = collections.namedtuple(
|
|
'BlockIO', 'name cls params sinks sources doc callbacks')
|
|
|
|
|
|
def _ports(sigs, msgs):
|
|
ports = list()
|
|
for i, dtype in enumerate(sigs):
|
|
port_type = TYPE_MAP.get(dtype.base.name, None)
|
|
if not port_type:
|
|
raise ValueError("Can't map {0!r} to GRC port type".format(dtype))
|
|
vlen = dtype.shape[0] if len(dtype.shape) > 0 else 1
|
|
ports.append((str(i), port_type, vlen))
|
|
for msg_key in msgs:
|
|
if msg_key == 'system':
|
|
continue
|
|
ports.append((msg_key, 'message', 1))
|
|
return ports
|
|
|
|
|
|
def _find_block_class(source_code, cls):
|
|
ns = {}
|
|
try:
|
|
exec(source_code, ns)
|
|
except Exception as e:
|
|
raise ValueError("Can't interpret source code: " + str(e))
|
|
for var in ns.values():
|
|
if inspect.isclass(var) and issubclass(var, cls):
|
|
return var
|
|
raise ValueError('No python block class found in code')
|
|
|
|
|
|
def extract(cls):
|
|
try:
|
|
from gnuradio import gr
|
|
import pmt
|
|
except ImportError:
|
|
raise EnvironmentError("Can't import GNU Radio")
|
|
|
|
if not inspect.isclass(cls):
|
|
cls = _find_block_class(cls, gr.gateway.gateway_block)
|
|
|
|
spec = inspect.getfullargspec(cls.__init__)
|
|
init_args = spec.args[1:]
|
|
defaults = [repr(arg) for arg in (spec.defaults or ())]
|
|
doc = cls.__doc__ or cls.__init__.__doc__ or ''
|
|
cls_name = cls.__name__
|
|
|
|
if len(defaults) + 1 != len(spec.args):
|
|
raise ValueError("Need all __init__ arguments to have default values")
|
|
|
|
try:
|
|
instance = cls()
|
|
except Exception as e:
|
|
raise RuntimeError("Can't create an instance of your block: " + str(e))
|
|
|
|
name = instance.name()
|
|
|
|
params = list(zip(init_args, defaults))
|
|
|
|
def settable(attr):
|
|
try:
|
|
# check for a property with setter
|
|
return callable(getattr(cls, attr).fset)
|
|
except AttributeError:
|
|
return attr in instance.__dict__ # not dir() - only the instance attribs
|
|
|
|
callbacks = [attr for attr in dir(
|
|
instance) if attr in init_args and settable(attr)]
|
|
|
|
sinks = _ports(instance.in_sig(),
|
|
pmt.to_python(instance.message_ports_in()))
|
|
sources = _ports(instance.out_sig(),
|
|
pmt.to_python(instance.message_ports_out()))
|
|
|
|
return BlockIO(name, cls_name, params, sinks, sources, doc, callbacks)
|
|
|
|
|
|
if __name__ == '__main__':
|
|
blk_code = """
|
|
import numpy as np
|
|
from gnuradio import gr
|
|
import pmt
|
|
|
|
class blk(gr.sync_block):
|
|
def __init__(self, param1=None, param2=None, param3=None):
|
|
"Test Docu"
|
|
gr.sync_block.__init__(
|
|
self,
|
|
name='Embedded Python Block',
|
|
in_sig = (np.float32,),
|
|
out_sig = (np.float32,np.complex64,),
|
|
)
|
|
self.message_port_register_in(pmt.intern('msg_in'))
|
|
self.message_port_register_out(pmt.intern('msg_out'))
|
|
self.param1 = param1
|
|
self._param2 = param2
|
|
self._param3 = param3
|
|
|
|
@property
|
|
def param2(self):
|
|
return self._param2
|
|
|
|
@property
|
|
def param3(self):
|
|
return self._param3
|
|
|
|
@param3.setter
|
|
def param3(self, value):
|
|
self._param3 = value
|
|
|
|
def work(self, inputs_items, output_items):
|
|
return 10
|
|
"""
|
|
from pprint import pprint
|
|
pprint(dict(extract(blk_code)._asdict()))
|