1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216
|
#!/usr/bin/env python3
#
# Copyright 2017 Ettus Research, a National Instruments Company
#
# SPDX-License-Identifier: GPL-3.0-or-later
#
"""
Main executable for the USRP Hardware Daemon
"""
from __future__ import print_function
import sys
import time
import argparse
from gevent import signal
import usrp_mpm as mpm
import usrp_mpm.rpc_server
import usrp_mpm.discovery
from usrp_mpm.mpmtypes import SharedState
from usrp_mpm.sys_utils import watchdog
# pylint: disable=wrong-import-order
# We have to import threading here because it must be imported after
# gevent.monkey.patch_all is called in rpc_server.py.
# (imported in the usrp_mpm __init__.py)
from threading import Event, Thread
_PROCESSES = []
_KILL_EVENT = Event()
# This Global Variable is used by the Simulator to make the spawn_processes,
# and by extension the main method, exit without waiting for the simulator to stop.
# See process_manager.py:bootstrap() for more information.
JOIN_PROCESSES = True
def setup_arg_parser():
"""
Create an arg parser
"""
parser = argparse.ArgumentParser(description="USRP Hardware Daemon")
parser.add_argument(
'--no-logbuf',
dest='use_logbuf',
help="Do not send log messages to UHD",
action="store_false",
)
parser.add_argument(
'--daemon',
help="Run as daemon",
action="store_true",
)
parser.add_argument(
'--init-only',
help="Don't start the RPC server, terminate after running initialization",
action="store_true",
)
parser.add_argument(
'--override-db-pids',
help="Provide a comma-separated list of daughterboard PIDs that are " \
"used instead of whatever else the code may find",
default=None
)
parser.add_argument(
'--discovery-addr',
help="Bind discovery socket to this address only. Defaults to all " \
"addresses.",
default="0.0.0.0",
)
parser.add_argument(
'--default-args',
help="Provide a comma-separated list of key=value pairs that are" \
"used as defaults for device initialization.",
default=None
)
parser.add_argument(
'-v',
'--verbose',
help="Increase verbosity level",
action="count",
default=0
)
parser.add_argument(
'-q',
'--quiet',
help="Decrease verbosity level",
action="count",
default=0
)
return parser
def parse_args():
"""
Return a fully parse args object
"""
args = setup_arg_parser().parse_args()
args.default_args = args.default_args or ''
try:
args.default_args = {
x.split('=')[0].strip(): x.split('=')[1].strip()
if x.find('=') != -1 else ''
for x in args.default_args.split(',')
if len(x)
}
except IndexError:
print("Could not parse default device args: `{}'".format(
args.default_args))
return args
def kill_time(sig, frame):
"""
kill all processes by setting _KILL_EVENT
to be used in a signal handler
"""
_KILL_EVENT.set()
def kill_thread():
"""
Kill all processes after _KILL_EVENT is triggered
If all processes are properly terminated, this will exit
"""
_KILL_EVENT.wait()
log = mpm.get_main_logger().getChild('kill')
for proc in _PROCESSES:
proc.terminate()
log.info("Terminating pid: {0}".format(proc.pid))
for proc in _PROCESSES:
proc.join()
log.info("System exiting")
sys.exit(0)
def init_only(log, default_args):
"""
Run the full initialization immediately and return
"""
# Create the periph_manager for this device
# This call will be forwarded to the device specific implementation
# e.g. in periph_manager/n3xx.py
# Which implementation is called will be determined during
# configuration with cmake (-DMPM_DEVICE).
# mgr is thus derived from PeriphManagerBase
# (see periph_manager/base.py)
from usrp_mpm.periph_manager import periph_manager
log.info("Spawning periph manager...")
ctor_time_start = time.time()
mgr = periph_manager(default_args)
ctor_duration = time.time() - ctor_time_start
log.info("Ctor Duration: {:.02f} s".format(ctor_duration))
init_time_start = time.time()
init_result = mgr.init(default_args)
init_duration = time.time() - init_time_start
if init_result:
log.info("Initialization successful! Duration: {:.02f} s"
.format(init_duration))
else:
log.warning("Initialization failed! Duration: {:.02f} s"
.format(init_duration))
log.info("Terminating on user request before launching RPC server.")
mgr.deinit()
return init_result
def spawn_processes(log, args):
"""
Launch the subprocesses and hang until completion.
"""
shared = SharedState()
log.info("Spawning RPC process...")
_PROCESSES.append(
mpm.rpc_server.spawn_rpc_process(
shared, mpm.mpmtypes.MPM_RPC_PORT, args.default_args))
log.debug("RPC process has PID: %d", _PROCESSES[-1].pid)
if watchdog.has_watchdog():
watchdog.transfer_control(_PROCESSES[-1].pid)
log.info("Spawning discovery process...")
_PROCESSES.append(
mpm.discovery.spawn_discovery_process(shared, args.discovery_addr)
)
log.debug("Discovery process has PID: %d", _PROCESSES[-1].pid)
log.info("Processes launched. Registering signal handlers.")
# Launch the kill thread
# This is used because we cannot block in a signal handler,
# meaning we cannot join threads
Thread(target=kill_thread, daemon=False).start()
signal.signal(signal.SIGTERM, kill_time)
signal.signal(signal.SIGINT, kill_time)
if JOIN_PROCESSES:
for proc in _PROCESSES:
proc.join()
return True
def main():
"""
Go, go, go!
Main process loop.
"""
args = parse_args()
log = mpm.get_main_logger(
use_logbuf=args.use_logbuf,
log_default_delta=args.verbose-args.quiet
).getChild('main')
version_string = mpm.__version__
if mpm.__githash__:
version_string += "-g" + mpm.__githash__
log.info("Launching USRP/MPM, version: %s", version_string)
if args.init_only:
# If --init-only is provided, we force disable init during boot time so
# we can properly time it in init_only().
args.default_args['skip_boot_init'] = "1"
if args.override_db_pids is not None:
log.warning('Overriding daughterboard PIDs!')
args.default_args['override_db_pids'] = args.override_db_pids
if args.init_only:
return init_only(log, args.default_args)
return spawn_processes(log, args)
if __name__ == '__main__':
sys.exit(not main())
|