Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • qutech-qdlabs/core_tools
1 result
Show changes
Commits on Source (42)
Showing
with 738 additions and 471 deletions
# Changelog
All notable changes to core_tools will be documented in this file.
## \[Unreleased\]
### Added
- Added optional functionality for synchronising data with a remote instance of SQDL. Note that this functionality depends on the internally developed ```sqdl_client``` package, and is therefor only accessible by users associated with QuTech.
- Added the ```SQDLWriter``` class, responsible for managing the data synchronisation process.
- Added the ```.../sqdl/export/``` sub-module, responsible for exporting core-tools data into an SQDL-compatible format.
- Added the ```.../sqdl/uploader/``` sub-module, responsible for uploading the exported datasets to a remote SQDL instance.
- Added the ```.../sqdl/model/``` sub-module, which defines the local database operations associated with the ```export``` and ```uploader``` sub-modules.
- Added ```.../startup/sqdl_sync.py``` and ```.../startup/launch_sqdl_sync.py```, which fill the role of ```.../startup/db_sync.py``` and ```.../startup/launch_db_sync.py``` for the purpose of synchronising data to SQDL.
- Added local database versioning.
- ...
### Changed
- Added a ```scope``` attribute to sample info.
- Updated ```sample_info``` class and functionality to include the optional ```scope``` parameter.
- Updated ```sample_info_overview``` table with a new "Scope" column for *local* database.
- Updated ```global_measurement_overview``` table with a new "Scope" column *local* database.
- Added a warning about setting the ```scope``` config parameter to the core-tools start-up configuration method.
- Note that updating your remote database to include these columns is not require at this stage. The synchronisation process as defined by ```db_sync``` allows for this descrepancy between databases, facilitating a non-breaking upgrade.
- ...
### Depricated
### Removed
### Fixed
- ...
## \[1.5.11] - 2025-02-20
- Added snapshot data_writer
- Added simple HTTP server to start scripts/functions based on ScriptRunner
- Added example of live exporter writing json files
## \[1.5.10] - 2025-02-17
- Removed unintended print statement.
## \[1.5.9] - 2025-02-13
- Update VideoMode for qcodes 0.49.
- Added software versions to snapshot.
## \[1.5.8] - 2025-02-11
- Fix flip-axes in VideoMode.
## \[1.5.7] - 2025-01-19
- Fix update of general settings in VideoMode.
## \[1.5.6] - 2025-01-14
- Always use package h5netcdf to load data from HDF5 file.
- Removed support for QT-DataViewer < 0.3.10
- Reduce station snapshot removing useless entries.
## \[1.5.5] - 2024-12-16
- Fixed direct writing of measurements to remote server.
......
BSD 2-Clause License
MIT License
Copyright (c) 2020, stephanlphilips
All rights reserved.
Copyright (c) 2021 QuTech
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
[![Documentation Status](https://readthedocs.org/projects/core-tools/badge/?version=latest)](https://core-tools.readthedocs.io/en/latest/?badge=latest)
# Core-tools
Core-tools is a light-weight dataset that supports common spin-qubit measurement practices.
It features a local data storage solution, as well as tools for remote storage.
# core tools
## Documentation
Core-tools documentation can be found in a couple of different locations.
### Project
Project documentation can be found as a set of markdown files in the ```docs/source``` directory, starting with ```docs/source/index.rst```.
For convenience, it is possible to compile these documents into a set of HTML files that you can read in your browser.
On Windows:
```bat
.\.venv\Scripts\activate
pip install sphinx
cd docs/
make.bat html
```
On Linux:
```bash
source .venv/bin/activate
pip install sphinx
cd docs/
make html
```
You can now access the documentation by opening ```docs/build/html/index.html``` in your web-browser of choice.
### Configuration
For users associated with QuTech or the TU Delft, there are wiki-pages documenting how to set up a measurement environment using core-tools.
These pages are located on the [QDLabs GitLab wiki](https://gitlab.tudelft.nl/qutech-qdlabs/measurement-systems/documentation/-/wikis/home).
read the docs at [https://core-tools.readthedocs.io/en/latest/](https://core-tools.readthedocs.io/en/latest/):
from typing import List, Optional
import numpy as np
......@@ -7,18 +6,18 @@ iq_mode2numpy = {
'Q': [('', np.imag, 'mV')],
'amplitude': [('', np.abs, 'mV')],
'phase': [('', np.angle, 'rad')],
'phase_deg': [('', lambda x:np.angle(x, deg=True), 'deg')],
'phase_deg': [('', lambda x: np.angle(x, deg=True), 'deg')],
'I+Q': [('_I', np.real, 'mV'), ('_Q', np.imag, 'mV')],
'amplitude+phase': [('_amp', np.abs, 'mV'), ('_phase', np.angle, 'rad')],
'abs': [('', np.abs, 'mV')],
'angle': [('', np.angle, 'rad')],
'angle_deg': [('', lambda x:np.angle(x, deg=True), 'deg')],
'angle_deg': [('', lambda x: np.angle(x, deg=True), 'deg')],
'abs+angle': [('_amp', np.abs, 'mV'), ('_phase', np.angle, 'rad')],
}
# TODO: Add to pulse-lib
def get_channel_map(pulse, iq_mode: str | None, channels: Optional[List[str]] = None):
def get_channel_map(pulse, iq_mode: str | None, channels: list[str] | None = None):
if iq_mode is None:
iq_mode = 'I'
iq_func = iq_mode2numpy[iq_mode]
......@@ -26,7 +25,7 @@ def get_channel_map(pulse, iq_mode: str | None, channels: Optional[List[str]] =
channel_map = {}
for name, dig_ch in pulse.digitizer_channels.items():
if channels and name not in channels:
continue
continue
if dig_ch.iq_input or dig_ch.frequency or dig_ch.iq_out:
# IQ data available
for suffix, f, unit in iq_func:
......@@ -39,14 +38,14 @@ def get_channel_map(pulse, iq_mode: str | None, channels: Optional[List[str]] =
return channel_map
def get_channel_map_dig_4ch(iq_mode: str | None, channel_numbers: Optional[List[int]] = None):
def get_channel_map_dig_4ch(iq_mode: str | None, channel_numbers: list[int] | None = None):
# Old Keysight and Tektronix code.
if iq_mode is None:
iq_mode = 'I'
iq_func = iq_mode2numpy[iq_mode]
if not channel_numbers:
channel_numbers = range(1,5)
channel_numbers = range(1, 5)
channels = [(f'ch{i}', i) for i in channel_numbers]
channel_map = {}
......
......@@ -17,7 +17,7 @@ class QbloxFastScanParameter(FastScanParameterBase):
scan_config: ScanConfigBase,
pulse_lib,
pulse_sequence,
):
):
"""
args:
pulse_lib (pulselib): pulse library object
......@@ -75,7 +75,7 @@ class FastScanGenerator(FastScanGeneratorBase):
gate: str, swing: float, n_pt: int, t_measure: float,
pulse_gates: dict[str, float] = {},
biasT_corr: bool = False,
) -> FastScanParameterBase:
) -> FastScanParameterBase:
"""Creates 1D fast scan parameter.
Args:
......@@ -106,7 +106,7 @@ class FastScanGenerator(FastScanGeneratorBase):
acq_channels = set(v[0] for v in config.channel_map.values())
seg = self.pulse_lib.mk_segment()
seg = self.pulse_lib.mk_segment()
g1 = seg[gate]
pulse_channels = []
for ch, v in pulse_gates.items():
......@@ -158,15 +158,15 @@ class FastScanGenerator(FastScanGeneratorBase):
config,
pulse_lib=self.pulse_lib,
pulse_sequence=my_seq,
)
)
def create_2D_scan(self,
gate1: str, swing1: float, n_pt1: int,
gate2: str, swing2: float, n_pt2: int,
t_measure: float,
pulse_gates: dict[str, float] = {},
biasT_corr: bool = True,
) -> FastScanParameterBase:
gate1: str, swing1: float, n_pt1: int,
gate2: str, swing2: float, n_pt2: int,
t_measure: float,
pulse_gates: dict[str, float] = {},
biasT_corr: bool = True,
) -> FastScanParameterBase:
"""Creates 2D fast scan parameter.
Args:
......@@ -205,7 +205,7 @@ class FastScanGenerator(FastScanGeneratorBase):
add_pulse_gate_correction = biasT_corr and len(pulse_gates) > 0
seg = self.pulse_lib.mk_segment()
seg = self.pulse_lib.mk_segment()
g1 = seg[gate1]
g2 = seg[gate2]
......@@ -233,7 +233,8 @@ class FastScanGenerator(FastScanGeneratorBase):
g1.add_ramp_ss(0, step_eff*config.n_ptx, -config.vpx, config.vpx)
g2.add_block(0, step_eff*config.n_ptx, v2)
for acq_ch in acq_channels:
seg[acq_ch].acquire(step_eff*config.line_margin+config.acquisition_delay_ns, n_repeat=n_pt1, interval=step_eff)
seg[acq_ch].acquire(step_eff*config.line_margin+config.acquisition_delay_ns,
n_repeat=n_pt1, interval=step_eff)
for g, v in pulse_channels:
g.add_block(0, step_eff*config.n_ptx, v)
seg.reset_time()
......@@ -273,7 +274,7 @@ class FastScanGenerator(FastScanGeneratorBase):
config,
pulse_lib=self.pulse_lib,
pulse_sequence=my_seq
)
)
def construct_1D_scan_fast(gate, swing, n_pt, t_step, biasT_corr, pulse_lib,
......
from typing import Any, Tuple, Dict
from abc import ABC, abstractmethod
from qcodes import MultiParameter
......@@ -8,7 +7,7 @@ class IDataSaver(ABC):
"""Specifies the interface data savers are to adhere to."""
@abstractmethod
def save_data(self, vm_data_parameter: MultiParameter, label: str) -> Tuple[Any, Dict[str, str]]:
def save_data(self, vm_data_parameter: MultiParameter, label: str) -> tuple[any, dict[str, str]]:
"""
Saves the data to disk.
......
from typing import Dict, Tuple
import logging
from qcodes import MultiParameter
from core_tools.data.ds.data_set_core import data_set
......@@ -11,9 +9,10 @@ from core_tools.GUI.keysight_videomaps.data_saver import IDataSaver
logger = logging.getLogger(__name__)
class CoreToolsDataSaver(IDataSaver):
def save_data(self, vm_data_parameter: MultiParameter, label: str) -> Tuple[data_set, Dict[str, str]]:
def save_data(self, vm_data_parameter: MultiParameter, label: str) -> tuple[data_set, dict[str, str]]:
"""
Performs a measurement using core tools and writes the data to disk.
......@@ -34,4 +33,11 @@ class CoreToolsDataSaver(IDataSaver):
logger.info(f'Saved {dataset.exp_uuid}')
print(f'\nSaved {dataset.exp_uuid} ({label})')
return dataset, {"dataset_id": dataset.exp_id, "dataset_uuid": dataset.exp_uuid, "sample_info": sample_info_str}
return (
dataset,
{
"dataset_id": dataset.exp_id,
"dataset_uuid": dataset.exp_uuid,
"sample_info": sample_info_str,
}
)
from typing import Dict, Tuple
from qcodes import MultiParameter
try:
from qcodes import Measure
from qcodes.data.data_set import DataSet
except:
except Exception:
from qcodes_loop.measure import Measure
from qcodes_loop.data.data_set import DataSet
......@@ -14,7 +12,7 @@ from core_tools.GUI.keysight_videomaps.data_saver import IDataSaver
class QCodesDataSaver(IDataSaver):
def save_data(self, vm_data_parameter: MultiParameter, label: str) -> Tuple[DataSet, Dict[str, str]]:
def save_data(self, vm_data_parameter: MultiParameter, label: str) -> tuple[DataSet, dict[str, str]]:
"""
Performs a measurement using qcodes and writes the data to disk.
......
......@@ -222,7 +222,7 @@ class live_plot(QThread):
def _read_dc_voltage(self, gate_name):
if self.gates is not None:
try:
return self.gates.get(gate_name)
return self.gates.parameters[gate_name].get()
except Exception:
logging.debug(f'Cannot read DC gate {gate_name}')
......
# -*- coding: utf-8 -*-
from typing import Optional
from core_tools.GUI.param_viewer.param_viewer_GUI_window import Ui_MainWindow
from PyQt5 import QtCore, QtWidgets
import qcodes as qc
......@@ -21,9 +19,9 @@ class param_data_obj:
class param_viewer(QtWidgets.QMainWindow, Ui_MainWindow):
def __init__(self, gates_object: Optional[object] = None,
def __init__(self, gates_object: object | None = None,
max_diff: float = 1000,
keysight_rf: Optional[object] = None,
keysight_rf: object | None = None,
locked=False):
self.real_gates = list()
self.virtual_gates = list()
......
import inspect
import logging
import os
from enum import Enum
from abc import ABC, abstractmethod
try:
from spyder_kernels.customize.spydercustomize import runcell
except Exception:
runcell = None
logger = logging.getLogger(__name__)
class Command(ABC):
def __init__(self, name, parameters, defaults={}):
self.name = name
self.parameters = parameters
self.defaults = defaults
@abstractmethod
def __call__(self):
pass
class Cell(Command):
'''
A reference to an IPython cell with Python code to be run as command in
ScriptRunner.
If command_name is not specified then cell+filename with be used as command name.
Args:
cell: name or number of cell to run.
python_file: filename of Python file.
command_name: Optional name to show for the command in ScriptRunner.
'''
def __init__(self, cell: str | int, python_file: str, command_name: str | None = None):
filename = os.path.basename(python_file)
name = f'{cell} ({filename})' if command_name is None else command_name
super().__init__(name, {})
self.cell = cell
self.python_file = python_file
if runcell is None:
raise Exception('runcell not available. Upgrade to Spyder 4+ to use Cell()')
def __call__(self):
command = f"runcell({self.cell}, '{self.python_file}')"
# print(command)
logger.info(command)
runcell(self.cell, self.python_file)
class Function(Command):
'''
A reference to a function to be run as command in ScriptRunner.
The argument types of the function are displayed in the GUI. The entered
data is converted to the specified type. Currently the types `str`, `int`, `float` and `bool`
are supported.
If the type of the argument is not specified, then a string will be passed to the function.
If command_name is not specified, then the name of func is used as
command name.
The additional keyword arguments will be entered as default values in the GUI.
Args:
func: function to execute.
command_name: Optional name to show for the command in ScriptRunner.
kwargs: default arguments to pass with function.
'''
def __init__(self, func: any, command_name: str | None = None, **kwargs):
if command_name is None:
command_name = func.__name__
signature = inspect.signature(func)
parameters = {p.name: p for p in signature.parameters.values()}
defaults = {}
for name, parameter in parameters.items():
if parameter.default is not inspect._empty:
defaults[name] = parameter.default
if parameter.annotation is inspect._empty:
logger.warning(f'No annotation for `{name}`, assuming string')
defaults.update(**kwargs)
super().__init__(command_name, parameters, defaults)
self.func = func
def _convert_arg(self, param_name, value):
annotation = self.parameters[param_name].annotation
if annotation is inspect._empty:
# no type specified. Pass string.
return value
if isinstance(annotation, str):
raise Exception('Cannot convert to type specified as a string')
if issubclass(annotation, bool):
return value in [True, 1, 'True', 'true', '1']
if issubclass(annotation, Enum):
return annotation[value]
return annotation(value)
def __call__(self, **kwargs):
call_args = {}
for name in self.parameters:
try:
call_args[name] = self.defaults[name]
except KeyError:
pass
try:
call_args[name] = self._convert_arg(name, kwargs[name])
except KeyError:
pass
args_list = [f'{name}={repr(value)}' for name, value in call_args.items()]
command = f'{self.func.__name__}({", ".join(args_list)})'
print(command)
logger.info(command)
return self.func(**call_args)
......@@ -2,18 +2,15 @@ import logging
import inspect
import os
from enum import Enum
from typing import Union, Any
from abc import ABC, abstractmethod
from PyQt5 import QtCore, QtWidgets
from core_tools.GUI.keysight_videomaps.liveplotting import liveplotting
from core_tools.GUI.script_runner.script_runner_gui import Ui_MainWindow
from core_tools.GUI.script_runner.commands import Function, Cell
from core_tools.GUI.script_runner.web_server import run_web_server
from core_tools.GUI.qt_util import qt_log_exception
from core_tools.GUI.keysight_videomaps.liveplotting import liveplotting
try:
from spyder_kernels.customize.spydercustomize import runcell
except:
runcell = None
logger = logging.getLogger(__name__)
......@@ -34,6 +31,7 @@ class ScriptRunner(QtWidgets.QMainWindow, Ui_MainWindow):
script_gui.add_cell('Say Hi', path+'/test_script.py')
script_gui.add_cell(2, path+'/test_script.py'),
'''
def __init__(self):
# set graphical user interface
self.app = QtCore.QCoreApplication.instance()
......@@ -45,10 +43,11 @@ class ScriptRunner(QtWidgets.QMainWindow, Ui_MainWindow):
super(QtWidgets.QMainWindow, self).__init__()
self.setupUi(self)
self.video_mode_running = False
self.video_mode_label = QtWidgets.QLabel("VideoMode: <unknown")
self.video_mode_label.setMargin(2)
self.statusbar.setContentsMargins(8,0,4,4)
self.statusbar.setContentsMargins(8, 0, 4, 4)
self.statusbar.addWidget(self.video_mode_label)
self.video_mode_paused = False
self._update_video_mode_status()
......@@ -57,14 +56,14 @@ class ScriptRunner(QtWidgets.QMainWindow, Ui_MainWindow):
self.commands = []
self.timer = QtCore.QTimer()
self.timer.timeout.connect(lambda:self._update_video_mode_status())
self.timer.timeout.connect(lambda: self._update_video_mode_status())
self.timer.start(500)
self.show()
if instance_ready == False:
if not instance_ready:
self.app.exec()
def add_function(self, func:Any, command_name:str=None, **kwargs):
def add_function(self, func: any, command_name: str | None = None, **kwargs):
'''
Adds a function to be run as command in ScriptRunner.
......@@ -85,7 +84,7 @@ class ScriptRunner(QtWidgets.QMainWindow, Ui_MainWindow):
'''
self._add_command(Function(func, command_name, **kwargs))
def add_cell(self, cell: Union[str, int], python_file:str, command_name:str = None):
def add_cell(self, cell: str | int, python_file: str, command_name: str | None = None):
'''
Add an IPython cell with Python code to be run as command in ScriptRunner.
......@@ -96,10 +95,11 @@ class ScriptRunner(QtWidgets.QMainWindow, Ui_MainWindow):
python_file: filename of Python file.
command_name: Optional name to show for the command in ScriptRunner.
'''
if runcell is None:
raise Exception('runcell not available. Upgrade to Spyder 4+ to use add_cell()')
self._add_command(Cell(cell, python_file, command_name))
def run_server(self):
run_web_server(self.commands)
@qt_log_exception
def closeEvent(self, event):
self.timer.stop()
......@@ -117,9 +117,9 @@ class ScriptRunner(QtWidgets.QMainWindow, Ui_MainWindow):
self.app.processEvents()
kwargs = {
name:(inp.currentText() if isinstance(inp, QtWidgets.QComboBox) else inp.text())
for name,inp in arg_inputs.items()
}
name: (inp.currentText() if isinstance(inp, QtWidgets.QComboBox) else inp.text())
for name, inp in arg_inputs.items()
}
command_result = command(**kwargs)
except Exception as ex:
command_result = ex
......@@ -141,7 +141,7 @@ class ScriptRunner(QtWidgets.QMainWindow, Ui_MainWindow):
layout.addWidget(cmd_btn, i, 0, 1, 1)
arg_inputs = {}
for j,(name,parameter) in enumerate(command.parameters.items()):
for j, (name, parameter) in enumerate(command.parameters.items()):
_label = QtWidgets.QLabel(self.commands_widget)
_label.setObjectName(f"{command.name}_label_{j}")
_label.setMinimumSize(QtCore.QSize(20, 0))
......@@ -161,11 +161,11 @@ class ScriptRunner(QtWidgets.QMainWindow, Ui_MainWindow):
_input.addItem(e.name, e)
if name in command.defaults:
default = command.defaults[name]
if isinstance(default,str):
if isinstance(default, str):
try:
# try match on value
default = annotation(default)
except:
except Exception:
# try match on name
default = annotation[default]
_input.setCurrentText(default.name)
......@@ -178,10 +178,10 @@ class ScriptRunner(QtWidgets.QMainWindow, Ui_MainWindow):
layout.addWidget(_input, i, 2*j+2, 1, 1)
arg_inputs[name] = _input
cmd_btn.clicked.connect(lambda:self._run_command(command, arg_inputs))
cmd_btn.clicked.connect(lambda: self._run_command(command, arg_inputs))
def add_commands(self, commands):
for i,command in enumerate(commands):
for i, command in enumerate(commands):
self._add_command(i, command)
def _update_video_mode_status(self):
......@@ -213,111 +213,9 @@ class ScriptRunner(QtWidgets.QMainWindow, Ui_MainWindow):
liveplotting.last_instance._2D_start_stop()
class Command(ABC):
def __init__(self, name, parameters, defaults={}):
self.name = name
self.parameters = parameters
self.defaults = defaults
@abstractmethod
def __call__(self):
pass
class Cell(Command):
'''
A reference to an IPython cell with Python code to be run as command in
ScriptRunner.
If command_name is not specified then cell+filename with be used as command name.
Args:
cell: name or number of cell to run.
python_file: filename of Python file.
command_name: Optional name to show for the command in ScriptRunner.
'''
def __init__(self, cell: Union[str, int], python_file:str, command_name:str = None):
filename = os.path.basename(python_file)
name = f'{cell} ({filename})' if command_name is None else command_name
super().__init__(name, {})
self.cell = cell
self.python_file = python_file
if runcell is None:
raise Exception('runcell not available. Upgrade to Spyder 4+ to use Cell()')
def __call__(self):
command = f"runcell({self.cell}, '{self.python_file}')"
print(command)
logger.info(command)
runcell(self.cell, self.python_file)
class Function(Command):
'''
A reference to a function to be run as command in ScriptRunner.
The argument types of the function are displayed in the GUI. The entered
data is converted to the specified type. Currently the types `str`, `int`, `float` and `bool`
are supported.
If the type of the argument is not specified, then a string will be passed to the function.
If command_name is not specified, then the name of func is used as
command name.
The additional keyword arguments will be entered as default values in the GUI.
Args:
func: function to execute.
command_name: Optional name to show for the command in ScriptRunner.
kwargs: default arguments to pass with function.
'''
def __init__(self, func:Any, command_name:str=None, **kwargs):
if command_name is None:
command_name = func.__name__
signature = inspect.signature(func)
parameters = {p.name:p for p in signature.parameters.values()}
defaults = {}
for name,parameter in parameters.items():
if parameter.default is not inspect._empty:
defaults[name] = parameter.default
if parameter.annotation is inspect._empty:
logger.warning(f'No annotation for `{name}`, assuming string')
defaults.update(**kwargs)
super().__init__(command_name, parameters, defaults)
self.func = func
def _convert_arg(self, param_name, value):
annotation = self.parameters[param_name].annotation
if annotation is inspect._empty:
# no type specified. Pass string.
return value
if isinstance(annotation, str):
raise Exception('Cannot convert to type specified as a string')
if issubclass(annotation, bool):
return value in [True, 1, 'True', 'true', '1']
if issubclass(annotation, Enum):
return annotation[value]
return annotation(value)
def __call__(self, **kwargs):
call_args = {}
for name in self.parameters:
try:
call_args[name] = self.defaults[name]
except KeyError: pass
try:
call_args[name] = self._convert_arg(name, kwargs[name])
except KeyError: pass
args_list = [f'{name}={repr(value)}' for name,value in call_args.items()]
command = f'{self.func.__name__}({", ".join(args_list)})'
print(command)
logger.info(command)
return self.func(**call_args)
if __name__ == "__main__":
def sayHi(name:str, times:int=1):
def sayHi(name: str, times: int = 1):
for _ in range(times):
print(f'Hi {name}')
return name
......@@ -330,7 +228,7 @@ if __name__ == "__main__":
def fit(x: float, mode: Mode):
print(f'fit {x}, {mode}')
path = os.path.dirname(__file__)
path = os.path.dirname(__file__)
ui = ScriptRunner()
ui.add_function(sayHi)
......@@ -339,7 +237,11 @@ if __name__ == "__main__":
ui.add_function(fit, 'Fit it', mode=Mode.CENTER, x=1.0)
ui.add_function(fit, 'Fit it', mode='center', x=1.0)
ui.add_function(fit, 'Fit it', mode='CENTER', x=1.0)
ui.add_cell('Say Hi', path+'/test_script.py')
ui.add_cell(2, path+'/test_script.py', 'Magic Button')
ui.add_cell('Oops', path+'/test_script.py')
ui.add_cell('Syntax Error', path+'/test_script.py')
# ui.add_cell('Say Hi', path+'/test_script.py')
# ui.add_cell(2, path+'/test_script.py', 'Magic Button')
# ui.add_cell('Oops', path+'/test_script.py')
# ui.add_cell('Syntax Error', path+'/test_script.py')
# NOTE:
# To start servicing http requests run:
ui.run_server()
import json
from datetime import datetime
from functools import cached_property
from http.cookies import SimpleCookie
from http.server import HTTPServer, BaseHTTPRequestHandler, HTTPStatus
from urllib.parse import parse_qsl, urlparse
from core_tools.GUI.script_runner.commands import Command
class WebRequestHandler(BaseHTTPRequestHandler):
@cached_property
def url(self):
return urlparse(self.path)
@cached_property
def query_data(self):
return dict(parse_qsl(self.url.query))
@cached_property
def post_data(self):
content_length = int(self.headers.get("Content-Length", 0))
return self.rfile.read(content_length)
@cached_property
def form_data(self):
return dict(parse_qsl(self.post_data.decode("utf-8")))
@cached_property
def cookies(self):
return SimpleCookie(self.headers.get("Cookie"))
def log_request(self, code='-', size='-'):
pass
def do_GET(self):
if self.url.path == "/functions":
# return list of functions with arguments
response = json.dumps(self.get_function_descriptions()).encode("utf-8")
self.send_response(HTTPStatus.OK)
self.send_header("Content-Type", "application/json")
self.end_headers()
self.wfile.write(response)
else:
self.send_error(HTTPStatus.NOT_FOUND)
def do_POST(self):
if self.url.path == "/run":
try:
data = self.form_data
cmd_name = data["__name__"]
kwargs = {
k: v
for k, v in data.items() if k != "__name__"
}
for command in self.server.commands:
if command.name == cmd_name:
try:
result = command(**kwargs)
except Exception as ex:
print(ex)
raise
break
else:
self.send_error(HTTPStatus.NOT_FOUND)
response = json.dumps(result).encode("utf-8")
self.send_response(HTTPStatus.OK)
self.send_header("Content-Type", "application/json")
self.end_headers()
self.wfile.write(response)
except Exception as ex:
response = json.dumps(str(ex)).encode("utf-8")
self.send_response(HTTPStatus.BAD_REQUEST)
self.send_header("Content-Type", "application/json")
self.end_headers()
self.wfile.write(response)
else:
self.send_error(HTTPStatus.NOT_FOUND)
def get_function_descriptions(self):
functions = []
for command in self.server.commands:
command: Command = command
parameters = command.parameters
functions.append({
"__name__": command.name,
"args": [{
"name": name,
"type": parameter.annotation.__name__,
}
for name, parameter in parameters.items()],
})
return functions
def get_response(self):
return json.dumps(
{
"now": datetime.now().isoformat(),
"path": self.url.path,
"query_data": self.query_data,
"post_data": self.post_data.decode("utf-8"),
"form_data": self.form_data,
"cookies": {
name: cookie.value
for name, cookie in self.cookies.items()
},
}
)
def run_web_server(command_list: list[Command]):
server_address = ('127.0.0.1', 8001)
httpd = HTTPServer(server_address, WebRequestHandler)
httpd.commands = command_list
try:
print(f"Server running at http://{server_address[0]}:{server_address[1]}")
print("Interrupt kernel to stop server")
httpd.serve_forever()
except KeyboardInterrupt:
httpd.shutdown()
# %%
if __name__ == "__main__":
from enum import Enum
from core_tools.GUI.script_runner.commands import Function
def sayHi(name: str, times: int = 1):
for _ in range(times):
print(f'Hi {name}')
return name
class Mode(str, Enum):
LEFT = 'left'
CENTER = 'center'
RIGHT = 'right'
def fit(x: float, mode: Mode):
print(f'fit {x}, {mode}')
command_list = [
Function(sayHi),
Function(fit),
]
run_web_server(command_list)
from core_tools.GUI.virt_gate_matrix_qml.models import attenuation_model, table_header_model, vg_matrix_model
from core_tools.drivers.hardware.hardware import hardware
from PyQt5 import QtCore, QtWidgets, QtQml, QtGui
import core_tools.GUI.virt_gate_matrix_qml as qml_in
import core_tools.GUI.virt_gate_matrix_qml as qml_in
import os
from typing import Union
import logging
import qcodes as qc
logger = logging.getLogger(__name__)
class virt_gate_matrix_GUI:
def __init__(self, virtual_gate_name : Union[str, int] = 0,
invert : bool = False):
def __init__(self, virtual_gate_name: str | int = 0,
invert: bool = False):
"""
Args:
virtual_gate_name: Name or index of virtual gate to display
......@@ -22,7 +21,6 @@ class virt_gate_matrix_GUI:
"""
super().__init__()
self.app = QtCore.QCoreApplication.instance()
self.instance_ready = True
if self.app is None:
......@@ -52,7 +50,7 @@ class virt_gate_matrix_GUI:
self.gates_header_model = table_header_model(vg.gates)
self.vgates_header_model = table_header_model(vg.v_gates)
root_context=self.engine.rootContext()
root_context = self.engine.rootContext()
root_context.setContextProperty('virt_gate_matrix_GUI', self)
root_context.setContextProperty('vg_matrix_model', self.vg_matrix_model)
root_context.setContextProperty('row_header_model', self.gates_header_model)
......@@ -76,13 +74,12 @@ class virt_gate_matrix_GUI:
self.vg_matrix_model.set_virtual_2_real(self._mat_inv_switch.property('checked'))
self.set_table_headers()
if self.instance_ready == False:
if self.instance_ready is False:
self.app.exec_()
print('exec')
def set_table_headers(self):
root_context=self.engine.rootContext()
root_context = self.engine.rootContext()
inverted = self._mat_inv_switch.property('checked')
logger.info(f'set_table_headers: mat_inv {inverted}')
......@@ -92,53 +89,3 @@ class virt_gate_matrix_GUI:
else:
root_context.setContextProperty('row_header_model', self.vgates_header_model)
root_context.setContextProperty('column_header_model', self.gates_header_model)
if __name__ == "__main__":
import numpy as np
from core_tools.data.SQL.connect import set_up_local_storage, set_up_remote_storage, set_up_local_and_remote_storage
from core_tools.drivers.hardware.hardware import hardware
set_up_local_storage('stephan', 'magicc', 'test', 'test_project1', 'test_set_up', 'test_sample')
h = hardware()
h.dac_gate_map = {
# dacs for creating the quantum dots -- syntax, "gate name": (dac module number, dac index)
'B0': (0, 1), 'P1': (0, 2),
'B1': (0, 3), 'P2': (0, 4),
'B2': (0, 5), 'P3': (0, 6),
'B3': (0, 7), 'P4': (0, 8),
'B4': (0, 9), 'P5': (0, 10),
'B5': (0, 11),'P6': (0, 12),
'B6': (0, 13)}
h.boundaries = {'B0' : (0, 2000), 'B1' : (0, 2500)}
h.awg2dac_ratios.add(['P1', 'P2', 'P3', 'P4', 'P5', 'P6', 'B0', 'B1', 'B2', 'B3', 'B4', 'B5', 'B6', 'S6', 'SD1_P', 'SD2_P'])
h.virtual_gates.add('test', ['B0', 'P1', 'B1', 'P2', 'B2', 'P3', 'B3', 'P4', 'B4', 'P5', 'B5', 'P6', 'B6', 'S6', 'SD1_P', 'SD2_P', 'COMP1', 'COMP2', 'COMP3'])
a = np.array([[ 1.00000000e+00, 0.00000000e+00, 0.00000000e+00,0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00],
[ 6.49707806e-02, 2.82481655e-01, 4.90827766e-02, 7.81982372e-02, 3.10296188e-02, 3.77927331e-02, 2.04070954e-02, 1.92595334e-02, 5.77896842e-03, 5.23641473e-03, 1.07451230e-03, 1.20437539e-03, 1.08393785e-04, 4.03374906e-01, -5.72633650e-18,-1.75608355e-18],
[ 0.00000000e+00, 0.00000000e+00, 1.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00],
[ 2.02412952e-02, 8.80056311e-02, 3.92602899e-02, 1.95568069e-01, 7.67383245e-02, 8.68695232e-02, 1.71876988e-02, 3.15420382e-02, 1.04634263e-02, 8.57586683e-03, 1.75976787e-03, 1.97244937e-03, 1.77520443e-04, 4.21638099e-01, -6.07292953e-18,-1.83177706e-18],
[ 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 1.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00],
[ 8.60939992e-03, 3.74321735e-02, 1.66989085e-02, 8.31826079e-02, 6.54671507e-02, 2.13066308e-01, 3.27095776e-02, 7.33179851e-02, 2.47674300e-02, 1.99341993e-02, 4.09049770e-03, 4.58486584e-03, 4.12637926e-04, 4.15726257e-01, -6.28931174e-18,-1.80691524e-18],
[ 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 1.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00],
[ 4.58831882e-03, 1.99492123e-02, 8.89956525e-03, 4.43315828e-02, 2.80054449e-02, 1.13552183e-01, 3.98079716e-02, 2.11194559e-01, 5.13356355e-02, 5.74210329e-02, 1.17827960e-02, 1.32068376e-02, 1.18861538e-03, 3.94736245e-01, -6.75019910e-18,-1.87957376e-18],
[-1.13747604e-18, -2.83748185e-18, -7.02765838e-18,-4.57657416e-18, -4.04289212e-18, 2.25759347e-18, 6.47263042e-19, 9.97668597e-19, 1.00000000e+00, 1.73576902e-18, 3.49023532e-18, 1.32946821e-19, 7.14528222e-19, -5.41853938e-18, -1.12757026e-17, 7.80625564e-18],
[ 2.21371708e-03, 9.62485689e-03, 4.29375560e-03, 2.13885709e-02, 1.35117315e-02, 5.47852965e-02, 1.92060731e-02, 1.01894620e-01, 7.54102430e-02, 1.96511948e-01, 4.03242517e-02, 4.51977480e-02, 4.06779732e-03, 4.11569391e-01, -8.93461466e-18,-3.14152001e-18],
[-8.35738386e-19, -6.33704886e-19, 4.92852218e-18, 1.05218305e-18, 7.04962177e-19, -5.48017677e-18,-5.04760983e-18, -4.34445449e-18, -4.09613329e-18, 6.19746588e-18, 1.00000000e+00, -1.69256457e-18, 1.76562364e-18, -2.67784151e-17, -1.60461922e-17,-3.46944695e-18],
[ 6.76113171e-04, 2.93962248e-03, 1.31139825e-03, 6.53249440e-03, 4.12675119e-03, 1.67325178e-02, 5.86591624e-03, 3.11206410e-02, 3.44094639e-02, 9.79442448e-02, 7.65184375e-02, 2.57611670e-01, 2.31850503e-02, 4.41025679e-01, -1.22523239e-17,-5.10733335e-18],
[-2.36436087e-18, -5.90153867e-18, -1.45662517e-17,-9.52365278e-18, -8.66682292e-18, 4.83219219e-18,-1.51760539e-18, -6.44912262e-18, -1.31916453e-18,-6.73012624e-18, -1.09864362e-18, -4.47246990e-19, 1.00000000e+00, -2.79469603e-17, -2.34187669e-17,-2.60208521e-18],
[-3.66373083e-17, -2.93006203e-17, -2.40292394e-17,-7.56924908e-17, -2.66398315e-17, 4.13863597e-17, 4.98254165e-18, -7.91675124e-18, -1.66866457e-16, 1.46595361e-16, 7.40710493e-16, 3.33829987e-17,-1.01078939e-16, 1.00000000e+00, -1.38777878e-17,-4.33680869e-18],
[ 3.94710178e-02, 4.92640444e-02, 4.00235624e-03, 9.13044283e-03, 3.35657030e-03, 3.06239735e-03, 3.05006789e-03, 2.15866106e-03, 6.00781938e-04, 5.86911653e-04, 1.20434271e-04, 1.34989680e-04, 1.21490712e-05, 2.30623884e-01, 6.54425292e-01,-2.13653863e-18],
[ 1.66746471e-04, 7.24984657e-04, 3.23423711e-04, 1.61107701e-03, 1.01776038e-03, 4.12665870e-03, 1.44668212e-03, 7.67513087e-03, 5.87574735e-03, 1.54538756e-02, 1.14956970e-02, 5.93898666e-02, 7.06073317e-02, 9.49489762e-02, -1.25916980e-17, 7.25136042e-01]])
# h.virtual_gates.test.matrix = a #np.linalg.inv(a)
g = virt_gate_matrix_GUI()
import logging
from typing import List, Union
from collections.abc import Iterable
from pulse_lib.base_pulse import pulselib
......@@ -20,13 +19,13 @@ logger = logging.getLogger(__name__)
class Hvi2ScheduleLoader(HardwareSchedule):
schedule_cache = {}
script_classes = {
Hvi2VideoMode,
Hvi2SingleShot,
Hvi2ContinuousMode,
}
Hvi2VideoMode,
Hvi2SingleShot,
Hvi2ContinuousMode,
}
def __init__(self, pulse_lib: pulselib, script_name: str,
digitizers: Union[None, SD_DIG, List[SD_DIG]] = None,
digitizers: SD_DIG | list[SD_DIG] | None = None,
acquisition_delay_ns=None, switch_los=False, enabled_los=None):
'''
Args:
......@@ -149,7 +148,8 @@ class Hvi2ScheduleLoader(HardwareSchedule):
dig_conf['raw_ch'] = [channel for channel, mode in modes.items() if mode == 0]
dig_conf['ds_ch'] = [channel for channel, mode in modes.items() if mode != 0]
dig_conf['iq_ch'] = [channel for channel, mode in modes.items() if mode in [2, 3]]
dig_conf['sequencer'] = hvi_params.get(f'use_digitizer_sequencers_{dig.name}', hasattr(dig, 'get_sequencer'))
dig_conf['sequencer'] = hvi_params.get(f'use_digitizer_sequencers_{dig.name}',
hasattr(dig, 'get_sequencer'))
if f'dig_trigger_channels_{dig.name}' in hvi_params:
dig_conf['trigger_ch'] = hvi_params[f'dig_trigger_channels_{dig.name}']
......
"""
@author: sdesnoo
"""
from typing import List, Union
from pulse_lib.base_pulse import pulselib
......@@ -10,15 +9,13 @@ from .hvi2_schedule_loader import Hvi2ScheduleLoader
from core_tools.drivers.M3102A import SD_DIG
class Hvi2Schedules:
def __init__(self, pulse_lib:pulselib, digitizers:Union[SD_DIG,List[SD_DIG]]):
def __init__(self, pulse_lib: pulselib, digitizers: SD_DIG | list[SD_DIG]):
self.schedules = {}
self._pulselib = pulse_lib
self._digitizers = digitizers
def get_single_shot(self, digitizer_mode=None, dig_channel_modes=None, awg_channel_los=[],
n_triggers=1, switch_los=False, enabled_los=None, hvi_queue_control=False,
trigger_out=False, n_waveforms=1, acquisition_delay_ns=0):
......@@ -38,10 +35,14 @@ class Hvi2Schedules:
hvi_queue_control (bool): if True enables waveform queueing by hvi script.
'''
print('Hvi2Schedules is deprecated. Use Hvi2ScheduleLoader')
return Hvi2ScheduleLoader(self._pulselib, 'SingleShot', self._digitizers, acquisition_delay_ns=acquisition_delay_ns)
return Hvi2ScheduleLoader(
self._pulselib, 'SingleShot',
self._digitizers,
acquisition_delay_ns=acquisition_delay_ns,
)
def get_video_mode(self, digitizer_mode:int, awg_channel_los=None, acquisition_delay_ns=500, hvi_queue_control=False,
trigger_out=False, enable_markers=[]):
def get_video_mode(self, digitizer_mode: int, awg_channel_los=None, acquisition_delay_ns=500,
hvi_queue_control=False, trigger_out=False, enable_markers=[]):
'''
Return a (cached) video mode schedule.
Args:
......@@ -57,12 +58,15 @@ class Hvi2Schedules:
For video mode the digitizer measurement should return 1 value per trigger.
'''
print('Hvi2Schedules is deprecated. Use Hvi2ScheduleLoader')
return Hvi2ScheduleLoader(self._pulselib, 'SingleShot', self._digitizers, acquisition_delay_ns=acquisition_delay_ns)
return Hvi2ScheduleLoader(
self._pulselib,
'SingleShot',
self._digitizers,
acquisition_delay_ns=acquisition_delay_ns
)
def clear(self):
Hvi2ScheduleLoader.close_all()
def close(self):
self.clear()
......@@ -6,10 +6,8 @@ from core_tools.startup.launch_qt_databrowser import launch_qt_databrowser
from core_tools.startup.sample_info import set_sample_info
from core_tools.startup.gui import (
start_parameter_viewer,
start_parameter_viewer_qml,
start_virtual_matrix_gui,
start_virtual_matrix_gui_qml,
start_script_runner,
)
__version__ = "1.5.5"
__version__ = "1.5.11"
......@@ -57,8 +57,12 @@ class SQL_database_manager(SQL_database_init):
if SQL_database_manager.__instance is not None:
db_mgr = SQL_database_manager.__instance
# check connections not closed
if (db_mgr.conn_local is None or db_mgr.conn_local.closed
or db_mgr.conn_remote is None or db_mgr.conn_remote.closed):
if (
db_mgr.conn_local is None
or db_mgr.conn_local.closed
or db_mgr.conn_remote is None
or db_mgr.conn_remote.closed
):
db_mgr._disconnect()
SQL_database_manager.__instance = None
logger.warning('Closed connections. Retry connection.')
......
'''
definition of storage locations and initializer for storage.
'''
from typing import Optional
from core_tools.data.name_validation import validate_data_identifier_value
class SQL_descriptor(object):
def __init__(self, required=False):
self.val = None
......@@ -15,21 +19,27 @@ class SQL_descriptor(object):
raise ConnectionError('No sample information provided\n\n** Please check the docs (set up section). \n\n')
return self.val
class sample_info:
project = SQL_descriptor(True)
set_up = SQL_descriptor(True)
sample = SQL_descriptor(True)
scope: Optional[str] = None
def __init__(self, project, set_up, sample):
def __init__(self, project, set_up, sample, scope):
if project is not None:
validate_data_identifier_value(project)
if set_up is not None:
validate_data_identifier_value(set_up)
if sample is not None:
validate_data_identifier_value(sample)
if scope is not None:
validate_data_identifier_value(scope)
sample_info.project = project
sample_info.set_up = set_up
sample_info.sample = sample
sample_info.scope = scope
def __str__(self) -> str:
return f"{sample_info.project}: {sample_info.set_up}-{sample_info.sample}"
......@@ -81,6 +91,7 @@ class SQL_conn_info_local:
def __repr__(self):
return f'{self.__class__}: host {self.host}, port {self.port}, user {self.user}, passwd *, dbname {self.dbname}, readonly {self.readonly}'
class SQL_conn_info_remote:
host = conn_info_descriptor()
port = conn_info_descriptor()
......@@ -100,7 +111,17 @@ class SQL_conn_info_remote:
def __repr__(self):
return f'{self.__class__}: host {self.host}, port {self.port}, user {self.user}, passwd *, dbname {self.dbname}, readonly {self.readonly}'
def set_up_local_storage(user, passwd, dbname, project, set_up, sample, readonly=False):
def set_up_local_storage(
user,
passwd,
dbname,
project,
set_up,
sample,
scope=None,
readonly=False
):
'''
Set up the specification for the datastorage needed to store/retrieve measurements.
......@@ -112,11 +133,24 @@ def set_up_local_storage(user, passwd, dbname, project, set_up, sample, readonly
project (str) : project for which the data will be saved
set_up (str) : set up at which the data has been measured
sample (str) : sample name
scope (str|None) : SQDL scope name, can be None for setups that do not use SQDL.
'''
SQL_conn_info_local('localhost', 5432, user, passwd, dbname, readonly)
sample_info(project, set_up, sample)
def set_up_remote_storage(host, port, user, passwd, dbname, project, set_up, sample, readonly=False):
sample_info(project, set_up, sample, scope)
def set_up_remote_storage(
host,
port,
user,
passwd,
dbname,
project,
set_up,
sample,
scope=None,
readonly=False
):
'''
Set up the specification for the datastorage needed to store/retrieve measurements.
......@@ -130,15 +164,28 @@ def set_up_remote_storage(host, port, user, passwd, dbname, project, set_up, sam
project (str) : project for which the data will be saved
set_up (str) : set up at which the data has been measured
sample (str) : sample name
scope (str|None) : SQDL scope name, can be None for setups that do not use SQDL.
'''
SQL_conn_info_remote(host, port, user, passwd, dbname, readonly)
sample_info(project, set_up, sample)
def set_up_local_and_remote_storage(host, port,
user_local, passwd_local, dbname_local,
user_remote, passwd_remote, dbname_remote,
project, set_up, sample,
local_readonly=False, remote_readonly=False):
sample_info(project, set_up, sample, scope)
def set_up_local_and_remote_storage(
host,
port,
user_local,
passwd_local,
dbname_local,
user_remote,
passwd_remote,
dbname_remote,
project,
set_up,
sample,
scope=None,
local_readonly=False,
remote_readonly=False
):
'''
Set up the specification for the datastorage needed to store/retrieve measurements.
......@@ -157,7 +204,8 @@ def set_up_local_and_remote_storage(host, port,
project (str) : project for which the data will be saved
set_up (str) : set up at which the data has been measured
sample (str) : sample name
scope (str|None) : SQDL scope name, can be None for setups that do not use SQDL.
'''
SQL_conn_info_local('localhost', 5432, user_local, passwd_local, dbname_local, local_readonly)
SQL_conn_info_remote(host, port, user_remote, passwd_remote, dbname_remote, remote_readonly)
sample_info(project, set_up, sample)
\ No newline at end of file
sample_info(project, set_up, sample, scope)