diff --git a/scriptsapi/Readme.md b/scriptsapi/Readme.md
index 5073bae39..423d74010 100644
--- a/scriptsapi/Readme.md
+++ b/scriptsapi/Readme.md
@@ -70,6 +70,28 @@ Options are:
- `-f` or `--frequency` device center frequency (kHz). Mandatory. Ex: 491500
- `-s` or `--symbol-rate` symbol rate (kS/s). Mandatory. Ex: 1500
+
dump.py
+
+Dumps an instance setup to a sequence of commands in a JSON file that can be used as input to `config.py` (see next). The setup commands comprise so far:
+
+ - Deviceset instantiations (with default device)
+ - Deviceset device change (will get the first device of the type)
+ - Deviceset device settings
+ - Deviceset main spectrum settings
+ - Deviceset channels instantiations
+ - Deviceset channels settings
+ - Featureset instantiations
+ - Featureset features instantiations
+ - Featureset features settings
+
+Of course the exact scope of settings that can be manipulated depends of the settings covered by the API.
+
+Options are:
+
+ - `-h` or `--help` show help message and exit
+ - `-a` or `--address` address and port of SDRangel instance. Default is `127.0.0.1:8091`
+ - `-j` or `--json-file` JSON output file where commands are stored - mandatory.
+
config.py
Sends a sequence of commands recorded in a JSON file which is in the form of a list of commands.
@@ -79,6 +101,8 @@ Options are:
- `-h` or `--help` show help message and exit
- `-a` or `--address` address and port of SDRangel instance. Default is `127.0.0.1:8091`
- `-j` or `--json-file` JSON file containing description of API commands
+ - `-i` or `--init` Initialize instance before running script. This is useful when running a sequence of initialization commands
+ - `-1` or `--ignore-first-posts`. Ignore first deviceset or featureset post in sequence. This is useful when running a sequence of initialization commands for a GUI instance
Each command in the JSON file is a JSON document with the following keys:
diff --git a/scriptsapi/config.py b/scriptsapi/config.py
index 5e2c3f9bc..ed8232cc2 100755
--- a/scriptsapi/config.py
+++ b/scriptsapi/config.py
@@ -16,6 +16,9 @@ import requests, traceback, sys, json, time
from optparse import OptionParser
base_url = "http://127.0.0.1:8091/sdrangel"
+app_gui = False
+nb_devicesets = 0
+nb_featuresets = 0
requests_methods = {
"GET": requests.get,
@@ -31,6 +34,8 @@ def getInputOptions():
parser = OptionParser(usage="usage: %%prog [-t]\n")
parser.add_option("-a", "--address", dest="address", help="Address and port. Default: 127.0.0.1:8091", metavar="ADDRESS", type="string")
parser.add_option("-j", "--json-file", dest="json_file", help="JSON file containing commands. Mandatory", metavar="FILE", type="string")
+ parser.add_option("-i", "--init", dest="initialize", help="Initialize instance before running script", action="store_true")
+ parser.add_option("-1", "--ignore-first-posts", dest="ignore_first_posts", help="Ignore first deviceset or featureset post in sequence", action="store_true")
(options, args) = parser.parse_args()
@@ -39,6 +44,45 @@ def getInputOptions():
return options
+# ======================================================================
+def get_instance_details():
+ global app_gui
+ global nb_devicesets
+ global nb_featuresets
+ r = requests_methods["GET"](url=base_url)
+ if r.status_code // 100 == 2:
+ app_gui = r.json()["appname"] == "SDRangel"
+ nb_devicesets = r.json()["devicesetlist"]["devicesetcount"]
+ nb_featuresets = r.json()["featuresetlist"]["featuresetcount"]
+
+# ======================================================================
+def initialize():
+ global nb_devicesets
+ global nb_featuresets
+ for i_ds in reversed(range(nb_devicesets)):
+ if app_gui and i_ds == 0:
+ r_ds = requests_methods["GET"](url=f"{base_url}/deviceset/0")
+ if r_ds.status_code // 100 == 2:
+ nb_channels = r_ds.json()["channelcount"]
+ for i_chan in reversed(range(nb_channels)):
+ requests_methods["DELETE"](url=f"{base_url}/deviceset/0/channel/{i_chan}")
+ else:
+ r_del = requests_methods["DELETE"](url=f"{base_url}/deviceset")
+ if r_del.status_code // 100 == 2:
+ nb_devicesets -= 1
+ for i_fs in reversed(range(nb_featuresets)):
+ if app_gui and i_fs == 0:
+ r_fs = requests_methods["GET"](url=f"{base_url}/featureset/0")
+ if r_fs.status_code // 100 == 2:
+ nb_features = r_fs.json()["featurecount"]
+ for i_feat in reversed(range(nb_features)):
+ requests_methods["DELETE"](url=f"{base_url}/featureset/0/feature/{i_feat}")
+ else:
+ r_del = requests_methods["DELETE"](url=f"{base_url}/featureset")
+ if r_del.status_code // 100 == 2:
+ nb_featuresets -= 1
+
+
# ======================================================================
def main():
try:
@@ -47,6 +91,14 @@ def main():
global base_url
base_url = "http://%s/sdrangel" % options.address
+ get_instance_details()
+
+ if options.initialize:
+ initialize()
+
+ nb_featureset_posts = 0
+ nb_deviceset_posts = 0
+
with open(options.json_file) as json_file:
commands = json.load(json_file)
for command in commands:
@@ -55,6 +107,16 @@ def main():
continue
url = base_url + endpoint
http_method = command.get('method', None)
+ if endpoint == "/deviceset" and http_method == "POST":
+ nb_deviceset_posts += 1
+ if nb_deviceset_posts == 1 and options.ignore_first_posts:
+ print("First deviceset creation ignored")
+ continue
+ if endpoint == "/featureset" and http_method == "POST":
+ nb_featureset_posts += 1
+ if nb_featureset_posts == 1 and options.ignore_first_posts:
+ print("First featureset creation ignored")
+ continue
method = requests_methods.get(http_method, None) if http_method is not None else None
if method is not None:
request_params = command.get('params', None)
diff --git a/scriptsapi/dump.py b/scriptsapi/dump.py
new file mode 100755
index 000000000..eadcc1882
--- /dev/null
+++ b/scriptsapi/dump.py
@@ -0,0 +1,274 @@
+#!/usr/bin/env python3
+"""
+Dumps a SDRangel instance configuration to a sequence of commands in a JSON file
+compatible with config.py
+
+Each command is a JSON document with the following keys:
+ - endpoint: URL suffix (API function) - mandatory
+ - method: HTTP method (GET, PATCH, POST, PUT, DELETE) - mandatory
+ - params: pairs of argument and values - optional
+ - payload: request body in JSON format - optional
+ - msg: descriptive message for console display - optional
+ - delay: delay in milliseconds after command - optional
+"""
+
+import requests, traceback, sys, json, time
+from optparse import OptionParser
+
+base_url = "http://127.0.0.1:8091/sdrangel"
+
+
+# ======================================================================
+class Instance:
+ def __init__(self):
+ self.devicesets = []
+ self.featuresets = []
+
+ def add_deviceset(self, json_data):
+ ds = DeviceSet(len(self.devicesets), json_data["direction"], json_data["deviceHwType"], json_data)
+ self.devicesets.append(ds)
+ return ds
+
+ def add_featureset(self, json_data):
+ fs = FeatureSet(len(self.featuresets))
+ self.featuresets.append(fs)
+ return fs
+
+ def add_deviceset_item(self, direction):
+ return {
+ "endpoint": "/deviceset",
+ "method": "POST",
+ "params": {"direction": direction},
+ "msg": f"Add device set"
+ }
+
+ def add_featureset_item(self):
+ return {
+ "endpoint": "/featureset",
+ "method": "POST",
+ "msg": f"Add feature set"
+ }
+
+ def get_config_items(self):
+ items = []
+ for i_ds, deviceset in enumerate(self.devicesets):
+ items.append(self.add_deviceset_item(deviceset.direction))
+ items.append(deviceset.change_device_item())
+ items.append(deviceset.set_device_item())
+ for channel in deviceset.channels:
+ items.append(channel.add_channel_item())
+ items.append(channel.set_channel_item())
+ items.append(deviceset.set_spectrum_item())
+ for i_fs, featureset in enumerate(self.featuresets):
+ items.append(self.add_featureset_item())
+ for feature in featureset.features:
+ items.append(feature.add_feature_item())
+ items.append(feature.set_feature_item())
+ return items
+
+# ======================================================================
+class DeviceSet:
+ def __init__(self, index, direction, hwType, settings):
+ self.index = index
+ self.direction = direction
+ self.hwType = hwType
+ self.device_settings = settings
+ self.spectrum_settings = {}
+ self.channels = []
+
+ def set_spectrum_settings(self, settings):
+ self.spectrum_settings = settings
+
+ def add_channel(self, json_data):
+ ch = Channel(self.index, len(self.channels), json_data["direction"], json_data["channelType"], json_data)
+ self.channels.append(ch)
+ return ch
+
+ def change_device_item(self):
+ return {
+ "endpoint": f"/deviceset/{self.index}/device",
+ "method": "PUT",
+ "payload": {
+ "hwType": self.hwType,
+ "direction": self.direction
+ },
+ "msg": f"Change device to {self.hwType} on device set {self.index}"
+ }
+
+ def set_device_item(self):
+ return {
+ "endpoint": f"/deviceset/{self.index}/device/settings",
+ "method": "PUT",
+ "payload": self.device_settings,
+ "msg": f"Setup device {self.hwType} on device set {self.index}"
+ }
+
+ def set_spectrum_item(self):
+ return {
+ "endpoint": f"/deviceset/{self.index}/spectrum/settings",
+ "method": "PUT",
+ "payload": self.spectrum_settings,
+ "msg": f"Setup spectrum on device set {self.index}"
+ }
+
+ def delete_all_channels_items(self):
+ nb_channels = len(self.channels)
+ items = []
+ for i_chan in reversed(range(nb_channels)):
+ items.append({
+ "endpoint": f"/deviceset/{self.index}/channel/{i_chan}",
+ "method": "DELETE",
+ "msg": f"Delete channel at index {i_chan} in device set {self.index}"
+ })
+ return items
+
+# ======================================================================
+class Channel:
+ def __init__(self, ds_index, index, direction, type, settings):
+ self.ds_index = ds_index
+ self.index = index
+ self.direction = direction
+ self.type = type
+ self.channel_settings = settings
+
+ def add_channel_item(self):
+ return {
+ "endpoint": f"/deviceset/{self.ds_index}/channel",
+ "method": "POST",
+ "payload": {
+ "channelType": self.type,
+ "direction": self.direction
+ },
+ "msg": f"Add channel {self.type} in device set index {self.ds_index}"
+ }
+
+ def set_channel_item(self):
+ return {
+ "endpoint": f"/deviceset/{self.ds_index}/channel/{self.index}/settings",
+ "method": "PUT",
+ "payload": self.channel_settings,
+ "msg": f"Setup channel {self.type} at {self.index} in device set {self.ds_index}"
+ }
+
+# ======================================================================
+class FeatureSet:
+ def __init__(self, index):
+ self.index = index
+ self.features = []
+
+ def add_feature(self, json_data):
+ feat = Feature(self.index, len(self.features), json_data["featureType"], json_data)
+ self.features.append(feat)
+ return feat
+
+ def delete_all_features_items(self):
+ nb_features = len(self.features)
+ items = []
+ for i_feat in reversed(range(nb_features)):
+ items.append({
+ "endpoint": f"/featureset/{self.index}/feature/{i_feat}",
+ "method": "DELETE",
+ "msg": f"Delete feature at index {i_feat} in feature set {self.index}"
+ })
+ return items
+
+# ======================================================================
+class Feature:
+ def __init__(self, fs_index, index, type, settings):
+ self.fs_index = fs_index
+ self.index = index
+ self.type = type
+ self.feature_settings = settings
+
+ def add_feature_item(self):
+ return {
+ "endpoint": f"/featureset/{self.fs_index}/feature",
+ "method": "POST",
+ "payload": {
+ "featureType": self.type
+ },
+ "msg": f"Add feature {self.type} in feature set index {self.fs_index}"
+ }
+
+ def set_feature_item(self):
+ return {
+ "endpoint": f"/featureset/{self.fs_index}/feature/{self.index}/settings",
+ "method": "PUT",
+ "payload": self.feature_settings,
+ "msg": f"Setup feature {self.type} at {self.index} in feature set {self.fs_index}"
+ }
+
+
+# ======================================================================
+def getInputOptions():
+
+ parser = OptionParser(usage="usage: %%prog [-t]\n")
+ parser.add_option("-a", "--address", dest="address", help="Address and port. Default: 127.0.0.1:8091", metavar="ADDRESS", type="string")
+ parser.add_option("-j", "--json-file", dest="json_file", help="JSON output file where commands are stored. Mandatory", metavar="FILE", type="string")
+
+ (options, args) = parser.parse_args()
+
+ if (options.address == None):
+ options.address = "127.0.0.1:8091"
+
+ return options
+
+
+# ======================================================================
+def dump():
+ instance = None
+ r = requests.get(url=f"{base_url}")
+ if r.status_code // 100 == 2:
+ instance = Instance()
+ nb_devicesets = r.json()["devicesetlist"]["devicesetcount"]
+ for i_deviceset in range(nb_devicesets):
+ r_ds = requests.get(url=f"{base_url}/deviceset/{i_deviceset}")
+ if r_ds.status_code // 100 == 2:
+ nb_channels = r_ds.json()["channelcount"]
+ r_dev = requests.get(url=f"{base_url}/deviceset/{i_deviceset}/device/settings")
+ if r_dev.status_code // 100 == 2:
+ ds = instance.add_deviceset(r_dev.json())
+ for i_channel in range(nb_channels):
+ r_chan = requests.get(url=f"{base_url}/deviceset/{i_deviceset}/channel/{i_channel}/settings")
+ if r_chan.status_code // 100 == 2:
+ ds.add_channel(r_chan.json())
+ r_spec = requests.get(url=f"{base_url}/deviceset/{i_deviceset}/spectrum/settings")
+ if r_spec.status_code // 100 == 2:
+ ds.set_spectrum_settings(r_spec.json())
+ nb_featuresets = r.json()["featuresetlist"]["featuresetcount"]
+ for i_featureset in range(nb_featuresets):
+ r_fs = requests.get(url=f"{base_url}/featureset/{i_featureset}")
+ if r_fs.status_code // 100 == 2:
+ nb_features = r_fs.json()["featurecount"]
+ fs = instance.add_featureset(r_fs.json())
+ for i_feature in range(nb_features):
+ r_feat = requests.get(url=f"{base_url}/featureset/{i_featureset}/feature/{i_feature}/settings")
+ if r_feat.status_code // 100 == 2:
+ fs.add_feature(r_feat.json())
+ return instance
+
+
+# ======================================================================
+def main():
+ try:
+ options = getInputOptions()
+
+ global base_url
+ base_url = "http://%s/sdrangel" % options.address
+
+ instance = dump()
+ json_items = instance.get_config_items()
+
+ with open(options.json_file,'w') as json_file:
+ json.dump(json_items, json_file, indent=2)
+
+ print("All done!")
+
+ except Exception as ex:
+ tb = traceback.format_exc()
+ print(tb, file=sys.stderr)
+
+
+# ======================================================================
+if __name__ == "__main__":
+ main()