def get_station():
    # Create a station
    cryo_station = Station()

    # Associate the instruments with the station
    for instrument in setup_instruments():
        cryo_station.add_component(instrument)

    # Send the station to the user
    return cryo_station
def get_station():
    # Create a station
    magnetism_station = Station()

    # Get the associated instruments
    for instrument in setup_instruments():
        magnetism_station.add_component(instrument)

    # Give the user the station
    return magnetism_station
示例#3
0
def fill_station_zerodim(param_meas):
    station = Station()
    allinstr=qc.instrument.base.Instrument._all_instruments
    for key,val in allinstr.items():
        instr = qc.instrument.base.Instrument.find_instrument(key)
        station.add_component(instr)
    measparstring = ""
    for parameter in param_meas:
        station.add_component(parameter)
        measparstring += parameter.name + ',' 
    return measparstring
示例#4
0
    def take_buffer_keysight(self):
        """ takes a frequency sweep, not setting any values on the hardware """

        from qcodes import Station

        station = Station()
        station.add_component(self.vna)

        # import pdb; pdb.set_trace()
        meas = Measurement(station=station)  # qcodes measurement

        # self.vna.points.set(20)
        self.vna.auto_sweep(False)

        meas.register_parameter(self.vna.real)
        meas.register_parameter(self.vna.imaginary)

        meas.register_parameter(self.vna.magnitude)
        meas.register_parameter(self.vna.phase)

        # actually get the data
        with meas.run(
        ) as datasaver:  # try to run the measurement (? but this doesn't yet write to the database)
            self.vna.active_trace.set(1)  # there are Tr1 and Tr2
            # self.vna.traces.tr1.run_sweep()

            imag = self.vna.imaginary()
            real = self.vna.real()

            mag = self.vna.magnitude()
            phase = self.vna.phase()

            datasaver.add_result(
                (self.vna.magnitude, mag), (self.vna.phase, phase),
                (self.vna.real, real), (self.vna.imaginary, imag))

            dataid = datasaver.run_id

        pd = datasaver.dataset.get_parameter_data()
        snapshot = datasaver.dataset.snapshot

        plot_by_id(dataid)
示例#5
0
def fill_station(param_set, param_meas):
    station = Station()
    allinstr=qc.instrument.base.Instrument._all_instruments
    for key,val in allinstr.items():
        instr = qc.instrument.base.Instrument.find_instrument(key)
        station.add_component(instr)
    measparstring = ""
    for parameter in param_set:
        station.add_component(parameter)
        measparstring += parameter.name + ',' 
    for parameter in param_meas:
        try: # Prevent station crash when component of parameter is not unique
            station.add_component(parameter)
            measparstring += parameter.name + ',' 
        except Exception as e:
            print('Error ignored when filling station: \n', e)
            pass
    return measparstring
示例#6
0
# -*- coding: utf-8 -*-

#%% imports
import inspect

import numpy as np
from qcodes import Station, Instrument
from qcodes.utils import validators

from instrumentserver import QtWidgets
from instrumentserver.serialize import (saveParamsToFile, loadParamsFromFile,
                                        toParamDict, fromParamDict)

from instrumentserver.gui import widgetDialog
from instrumentserver.params import ParameterManager
from instrumentserver.gui.instruments import ParameterManagerGui
from instrumentserver.client import Client, ProxyInstrument

#%% run the PM locally
Instrument.close_all()
pm = ParameterManager('pm')
station = Station()
station.add_component(pm)
dialog = widgetDialog(ParameterManagerGui(pm))

#%% instantiate PM in the server.
Instrument.close_all()

cli = Client()
pm = ProxyInstrument('pm', cli=cli, remotePath='pm')
dialog = widgetDialog(ParameterManagerGui(pm))
示例#7
0
class StationServer(QtCore.QObject):
    """The main server object.

    Encapsulated in a separate object so we can run it in a separate thread.
    """

    # we use this to quit the server.
    # if this string is sent as message to the server, it'll shut down and close
    # the socket. Should only be used from within this module.
    # it's randomized in the instantiated server for a little bit of safety.
    SAFEWORD = 'BANANA'

    #: Signal(str, str) -- emit messages for display in the gui (or other stuff the gui
    #: wants to do with it.
    #: Arguments: the message received, and the reply sent.
    messageReceived = QtCore.Signal(str, str)

    #: Signal(int) -- emitted when the server is started.
    #: Arguments: the port.
    serverStarted = QtCore.Signal(str)

    #: Signal() -- emitted when we shut down
    finished = QtCore.Signal()

    #: Signal(Dict) -- emitted when a new instrument was created.
    #: Argument is the blueprint of the instrument
    instrumentCreated = QtCore.Signal(object)

    #: Signal(str, Any) -- emitted when a parameter was set
    #: Arguments: full parameter location as string, value
    parameterSet = QtCore.Signal(str, object)

    #: Signal(str, Any) -- emitted when a parameter was retrieved
    #: Arguments: full parameter location as string, value
    parameterGet = QtCore.Signal(str, object)

    #: Signal(str, List[Any], Dict[str, Any], Any) -- emitted when a function was called
    #: Arguments: full function location as string, arguments, kw arguments, return value
    funcCalled = QtCore.Signal(str, object, object, object)

    def __init__(self,
                 parent=None,
                 port=5555,
                 allowUserShutdown=False):  # , publisher_port=5554):
        super().__init__(parent)

        self.SAFEWORD = ''.join(
            random.choices([chr(i) for i in range(65, 91)], k=16))
        self.serverRunning = False
        self.port = port
        self.station = Station()
        self.allowUserShutdown = allowUserShutdown
        # self.publisher_port = publisher_port

        self.parameterSet.connect(
            lambda n, v: logger.info(f"Parameter '{n}' set to: {str(v)}"))
        self.parameterGet.connect(
            lambda n, v: logger.debug(f"Parameter '{n}' retrieved: {str(v)}"))
        self.funcCalled.connect(
            lambda n, args, kw, ret: logger.debug(f"Function called:"
                                                  f"'{n}', args: {str(args)}, "
                                                  f"kwargs: {str(kw)})'."))

    @QtCore.Slot()
    def startServer(self):
        """Start the server. This function does not return until the ZMQ server
        has been shut down."""

        addr = f"tcp://*:{self.port}"
        logger.info(f"Starting server at {addr}")
        logger.info(f"The safe word is: {self.SAFEWORD}")
        context = zmq.Context()
        socket = context.socket(zmq.REP)
        socket.bind(addr)

        #creating and binding publishing socket
        # publisher_addr = f"tcp://*:{self.publisher_port}"
        # publisher_socket = context.socket(zmq.PUB)
        # publisher_socket.bind(publisher_addr)

        self.serverRunning = True
        self.serverStarted.emit(addr)

        while self.serverRunning:
            message = recv(socket)
            message_ok = True
            response_to_client = None
            response_log = None

            # allow the test client from within the same process to make sure the
            # server shuts down. This is
            if message == self.SAFEWORD:
                response_log = 'Server has received the safeword and will shut down.'
                response_to_client = ServerResponse(message=response_log)
                self.serverRunning = False
                logger.warning(response_log)

            elif self.allowUserShutdown and message == 'SHUTDOWN':
                response_log = 'Server shutdown requested by client.'
                response_to_client = ServerResponse(message=response_log)
                self.serverRunning = False
                logger.warning(response_log)

            # if the message is a string we just echo it back.
            # this is used for testing sometimes, but has no functionality.
            elif isinstance(message, str):
                response_log = f"Server has received: {message}. No further action."
                response_to_client = ServerResponse(message=response_log)
                logger.info(response_log)

            # we assume this is a valid instruction set now.
            elif isinstance(message, ServerInstruction):
                instruction = message
                try:
                    instruction.validate()
                    logger.info(f"Received request for operation: "
                                f"{str(instruction.operation)}")
                    logger.debug(f"Instruction received: "
                                 f"{str(instruction)}")
                except Exception as e:
                    message_ok = False
                    response_log = f'Received invalid message. Error raised: {str(e)}'
                    response_to_client = ServerResponse(message=None, error=e)
                    logger.warning(response_log)

                if message_ok:
                    # we don't need to use a try-block here, because
                    # errors are already handled in executeServerInstruction
                    response_to_client = self.executeServerInstruction(
                        instruction)
                    response_log = f"Response to client: {str(response_to_client)}"
                    if response_to_client.error is None:
                        logger.info(f"Response sent to client.")
                        logger.debug(response_log)
                    else:
                        logger.warning(response_log)

            else:
                response_log = f"Invalid message type."
                response_to_client = ServerResponse(message=None,
                                                    error=response_log)
                logger.warning(f"Invalid message type: {type(message)}.")
                logger.debug(f"Invalid message received: {str(message)}")

            send(socket, response_to_client)

            # print("sending message")
            # publisher_socket.send_string("Anyone copy?")

            self.messageReceived.emit(str(message), response_log)

        # publisher_socket.close()
        socket.close()
        self.finished.emit()
        return True

    def executeServerInstruction(self, instruction: ServerInstruction) \
            -> ServerResponse:
        """
        This is the interpreter function that the server will call to translate the
        dictionary received from the proxy to instrument calls.

        :param instruction: The instruction object.
        :returns: the results returned from performing the operation.
        """
        args = []
        kwargs = {}

        # we call a helper function depending on the operation that is requested
        if instruction.operation == Operation.get_existing_instruments:
            func = self._getExistingInstruments
        elif instruction.operation == Operation.create_instrument:
            func = self._createInstrument
            args = [instruction.create_instrument_spec]
        elif instruction.operation == Operation.call:
            func = self._callObject
            args = [instruction.call_spec]
        elif instruction.operation == Operation.get_blueprint:
            func = self._getBluePrint
            args = [instruction.requested_path]
        elif instruction.operation == Operation.get_param_dict:
            func = self._toParamDict
            args = [instruction.serialization_opts]
        elif instruction.operation == Operation.set_params:
            func = self._fromParamDict
            args = [instruction.set_parameters]
        else:
            raise NotImplementedError

        try:
            returns = func(*args, **kwargs)
            response = ServerResponse(message=returns)

        except Exception as err:
            response = ServerResponse(message=None, error=err)

        return response

    def _getExistingInstruments(self) -> Dict:
        """
        Get the existing instruments in the station,

        :returns : a dictionary that contains the instrument name and its class name.
        """
        comps = self.station.components
        info = {k: v.__class__ for k, v in comps.items()}
        return info

    def _createInstrument(self, spec: InstrumentCreationSpec) -> None:
        """Create a new instrument on the server"""
        sep_class = spec.instrument_class.split('.')
        modName = '.'.join(sep_class[:-1])
        clsName = sep_class[-1]
        mod = importlib.import_module(modName)
        cls = getattr(mod, clsName)

        args = [] if spec.args is None else spec.args
        kwargs = dict() if spec.kwargs is None else spec.kwargs

        new_instrument = qc.find_or_create_instrument(cls,
                                                      name=spec.name,
                                                      *args,
                                                      **kwargs)
        if new_instrument.name not in self.station.components:
            self.station.add_component(new_instrument)

            self.instrumentCreated.emit(
                bluePrintFromInstrumentModule(new_instrument.name,
                                              new_instrument))

    def _callObject(self, spec: CallSpec) -> Any:
        """call some callable found in the station."""
        obj = nestedAttributeFromString(self.station, spec.target)
        args = spec.args if spec.args is not None else []
        kwargs = spec.kwargs if spec.kwargs is not None else {}
        ret = obj(*args, **kwargs)

        if isinstance(obj, Parameter):
            if len(args) > 0:
                self.parameterSet.emit(spec.target, args[0])
            else:
                self.parameterGet.emit(spec.target, ret)
        else:
            self.funcCalled.emit(spec.target, args, kwargs, ret)

        return ret

    def _getBluePrint(
        self, path: str
    ) -> Union[InstrumentModuleBluePrint, ParameterBluePrint, MethodBluePrint]:
        obj = nestedAttributeFromString(self.station, path)
        if isinstance(obj, tuple(INSTRUMENT_MODULE_BASE_CLASSES)):
            return bluePrintFromInstrumentModule(path, obj)
        elif isinstance(obj, tuple(PARAMETER_BASE_CLASSES)):
            return bluePrintFromParameter(path, obj)
        elif callable(obj):
            return bluePrintFromMethod(path, obj)
        else:
            raise ValueError(f'Cannot create a blueprint for {type(obj)}')

    def _toParamDict(self, opts: ParameterSerializeSpec) -> Dict[str, Any]:
        if opts.path is None:
            obj = self.station
        else:
            obj = [nestedAttributeFromString(self.station, opts.path)]

        includeMeta = [k for k in opts.attrs if k != 'value']
        return serialize.toParamDict(obj,
                                     *opts.args,
                                     includeMeta=includeMeta,
                                     **opts.kwargs)

    def _fromParamDict(self, params: Dict[str, Any]):
        return serialize.fromParamDict(params, self.station)
示例#8
0
class StationServer(QtCore.QObject):
    """The main server object.

    Encapsulated in a separate object so we can run it in a separate thread.

    Port should always be an odd number to allow the next even number to be its corresponding
    publishing port.
    """

    # We use this to quit the server.
    # If this string is sent as message to the server, it'll shut down and close
    # the socket. Should only be used from within this module.
    # It's randomized in the instantiated server for a little bit of safety.
    SAFEWORD = 'BANANA'

    #: Signal(str, str) -- emit messages for display in the gui (or other stuff the gui
    #: wants to do with it.
    #: Arguments: the message received, and the reply sent.
    messageReceived = QtCore.Signal(str, str)

    #: Signal(int) -- emitted when the server is started.
    #: Arguments: the port.
    serverStarted = QtCore.Signal(str)

    #: Signal() -- emitted when we shut down.
    finished = QtCore.Signal()

    #: Signal(Dict) -- emitted when a new instrument was created.
    #: Argument is the blueprint of the instrument.
    instrumentCreated = QtCore.Signal(object)

    #: Signal(str, Any) -- emitted when a parameter was set
    #: Arguments: full parameter location as string, value.
    parameterSet = QtCore.Signal(str, object)

    #: Signal(str, Any) -- emitted when a parameter was retrieved
    #: Arguments: full parameter location as string, value.
    parameterGet = QtCore.Signal(str, object)

    #: Signal(str, List[Any], Dict[str, Any], Any) -- emitted when a function was called
    #: Arguments: full function location as string, arguments, kw arguments, return value.
    funcCalled = QtCore.Signal(str, object, object, object)

    def __init__(
        self,
        parent: Optional[QtCore.QObject] = None,
        port: int = 5555,
        allowUserShutdown: bool = False,
        addresses: List[str] = [],
        initScript: Optional[str] = None,
    ) -> None:
        super().__init__(parent)

        if addresses is None:
            addresses = []
        if initScript == None:
            initScript = ''

        self.SAFEWORD = ''.join(
            random.choices([chr(i) for i in range(65, 91)], k=16))
        self.serverRunning = False
        self.port = int(port)
        self.station = Station()
        self.allowUserShutdown = allowUserShutdown
        self.listenAddresses = list(set(['127.0.0.1'] + addresses))
        self.initScript = initScript

        self.broadcastPort = self.port + 1
        self.broadcastSocket = None

        self.parameterSet.connect(
            lambda n, v: logger.info(f"Parameter '{n}' set to: {str(v)}"))
        self.parameterGet.connect(
            lambda n, v: logger.debug(f"Parameter '{n}' retrieved: {str(v)}"))
        self.funcCalled.connect(
            lambda n, args, kw, ret: logger.debug(f"Function called:"
                                                  f"'{n}', args: {str(args)}, "
                                                  f"kwargs: {str(kw)})'."))

    def _runInitScript(self):
        if os.path.exists(self.initScript):
            path = os.path.abspath(self.initScript)
            env = dict(station=self.station)
            exec(open(path).read(), env)
        else:
            logger.warning(
                f"path to initscript ({self.initScript}) not found.")

    @QtCore.Slot()
    def startServer(self) -> None:
        """Start the server. This function does not return until the ZMQ server
        has been shut down."""

        logger.info(f"Starting server.")
        logger.info(f"The safe word is: {self.SAFEWORD}")
        context = zmq.Context()
        socket = context.socket(zmq.REP)

        for a in self.listenAddresses:
            addr = f"tcp://{a}:{self.port}"
            socket.bind(addr)
            logger.info(f"Listening at {addr}")

        # creating and binding publishing socket to broadcast changes
        broadcastAddr = f"tcp://*:{self.broadcastPort}"
        logger.info(f"Starting publishing server at {broadcastAddr}")
        self.broadcastSocket = context.socket(zmq.PUB)
        self.broadcastSocket.bind(broadcastAddr)

        self.serverRunning = True
        if self.initScript not in ['', None]:
            logger.info(f"Running init script")
            self._runInitScript()
        self.serverStarted.emit(addr)

        while self.serverRunning:
            message = recv(socket)
            message_ok = True
            response_to_client = None
            response_log = None

            # Allow the test client from within the same process to make sure the
            # server shuts down. This is
            if message == self.SAFEWORD:
                response_log = 'Server has received the safeword and will shut down.'
                response_to_client = ServerResponse(message=response_log)
                self.serverRunning = False
                logger.warning(response_log)

            elif self.allowUserShutdown and message == 'SHUTDOWN':
                response_log = 'Server shutdown requested by client.'
                response_to_client = ServerResponse(message=response_log)
                self.serverRunning = False
                logger.warning(response_log)

            # If the message is a string we just echo it back.
            # This is used for testing sometimes, but has no functionality.
            elif isinstance(message, str):
                response_log = f"Server has received: {message}. No further action."
                response_to_client = ServerResponse(message=response_log)
                logger.info(response_log)

            # We assume this is a valid instruction set now.
            elif isinstance(message, ServerInstruction):
                instruction = message
                try:
                    instruction.validate()
                    logger.info(f"Received request for operation: "
                                f"{str(instruction.operation)}")
                    logger.debug(f"Instruction received: "
                                 f"{str(instruction)}")
                except Exception as e:
                    message_ok = False
                    response_log = f'Received invalid message. Error raised: {str(e)}'
                    response_to_client = ServerResponse(message=None, error=e)
                    logger.warning(response_log)

                if message_ok:
                    # We don't need to use a try-block here, because
                    # errors are already handled in executeServerInstruction.
                    response_to_client = self.executeServerInstruction(
                        instruction)
                    response_log = f"Response to client: {str(response_to_client)}"
                    if response_to_client.error is None:
                        logger.info(f"Response sent to client.")
                        logger.debug(response_log)
                    else:
                        logger.warning(response_log)

            else:
                response_log = f"Invalid message type."
                response_to_client = ServerResponse(message=None,
                                                    error=response_log)
                logger.warning(f"Invalid message type: {type(message)}.")
                logger.debug(f"Invalid message received: {str(message)}")

            send(socket, response_to_client)

            self.messageReceived.emit(str(message), response_log)

        self.broadcastSocket.close()
        socket.close()
        self.finished.emit()
        return True

    def executeServerInstruction(self, instruction: ServerInstruction) \
            -> Tuple[ServerResponse, str]:
        """
        This is the interpreter function that the server will call to translate the
        dictionary received from the proxy to instrument calls.

        :param instruction: The instruction object.
        :returns: The results returned from performing the operation.
        """
        args = []
        kwargs = {}

        # We call a helper function depending on the operation that is requested.
        if instruction.operation == Operation.get_existing_instruments:
            func = self._getExistingInstruments
        elif instruction.operation == Operation.create_instrument:
            func = self._createInstrument
            args = [instruction.create_instrument_spec]
        elif instruction.operation == Operation.call:
            func = self._callObject
            args = [instruction.call_spec]
        elif instruction.operation == Operation.get_blueprint:
            func = self._getBluePrint
            args = [instruction.requested_path]
        elif instruction.operation == Operation.get_param_dict:
            func = self._toParamDict
            args = [instruction.serialization_opts]
        elif instruction.operation == Operation.set_params:
            func = self._fromParamDict
            args = [instruction.set_parameters]
        else:
            raise NotImplementedError

        try:
            returns = func(*args, **kwargs)
            response = ServerResponse(message=returns)

        except Exception as err:
            response = ServerResponse(message=None, error=err)

        return response

    def _getExistingInstruments(self) -> Dict:
        """
        Get the existing instruments in the station.

        :returns: A dictionary that contains the instrument name and its class name.
        """
        comps = self.station.components
        info = {k: v.__class__ for k, v in comps.items()}
        return info

    def _createInstrument(self, spec: InstrumentCreationSpec) -> None:
        """Create a new instrument on the server."""
        sep_class = spec.instrument_class.split('.')
        modName = '.'.join(sep_class[:-1])
        clsName = sep_class[-1]
        mod = importlib.import_module(modName)
        cls = getattr(mod, clsName)

        args = [] if spec.args is None else spec.args
        kwargs = dict() if spec.kwargs is None else spec.kwargs

        new_instrument = qc.find_or_create_instrument(cls,
                                                      name=spec.name,
                                                      *args,
                                                      **kwargs)
        if new_instrument.name not in self.station.components:
            self.station.add_component(new_instrument)

            self.instrumentCreated.emit(
                bluePrintFromInstrumentModule(new_instrument.name,
                                              new_instrument))

    def _callObject(self, spec: CallSpec) -> Any:
        """Call some callable found in the station."""
        obj = nestedAttributeFromString(self.station, spec.target)
        args = spec.args if spec.args is not None else []
        kwargs = spec.kwargs if spec.kwargs is not None else {}
        ret = obj(*args, **kwargs)

        # Check if a new parameter is being created.
        self._newOrDeleteParameterDetection(spec, args, kwargs)

        if isinstance(obj, Parameter):
            if len(args) > 0:
                self.parameterSet.emit(spec.target, args[0])

                # Broadcast changes in parameter values.
                self._broadcastParameterChange(
                    ParameterBroadcastBluePrint(spec.target,
                                                'parameter-update', args[0]))
            else:
                self.parameterGet.emit(spec.target, ret)

                # Broadcast calls of parameters.
                self._broadcastParameterChange(
                    ParameterBroadcastBluePrint(spec.target, 'parameter-call',
                                                ret))
        else:
            self.funcCalled.emit(spec.target, args, kwargs, ret)

        return ret

    def _getBluePrint(
        self, path: str
    ) -> Union[InstrumentModuleBluePrint, ParameterBluePrint, MethodBluePrint]:
        obj = nestedAttributeFromString(self.station, path)
        if isinstance(obj, tuple(INSTRUMENT_MODULE_BASE_CLASSES)):
            return bluePrintFromInstrumentModule(path, obj)
        elif isinstance(obj, tuple(PARAMETER_BASE_CLASSES)):
            return bluePrintFromParameter(path, obj)
        elif callable(obj):
            return bluePrintFromMethod(path, obj)
        else:
            raise ValueError(f'Cannot create a blueprint for {type(obj)}')

    def _toParamDict(self, opts: ParameterSerializeSpec) -> Dict[str, Any]:
        if opts.path is None:
            obj = self.station
        else:
            obj = [nestedAttributeFromString(self.station, opts.path)]

        includeMeta = [k for k in opts.attrs if k != 'value']
        return serialize.toParamDict(obj,
                                     *opts.args,
                                     includeMeta=includeMeta,
                                     **opts.kwargs)

    def _fromParamDict(self, params: Dict[str, Any]):
        return serialize.fromParamDict(params, self.station)

    def _broadcastParameterChange(self,
                                  blueprint: ParameterBroadcastBluePrint):
        """
        Broadcast any changes to parameters in the server.
        The message is composed of a 2 part array. The first item is the name of the instrument the parameter is from,
        with the second item being the string of the blueprint in dict format.
        This is done to allow subscribers to subscribe to specific instruments.

        :param blueprint: The parameter broadcast blueprint that is being broadcast
        """
        self.broadcastSocket.send_string(blueprint.name.split('.')[0],
                                         flags=zmq.SNDMORE)
        self.broadcastSocket.send_string((blueprint.toDictFormat()))
        logger.info(
            f"Parameter {blueprint.name} has broadcast an update of type: {blueprint.action},"
            f" with a value: {blueprint.value}.")

    def _newOrDeleteParameterDetection(self, spec, args, kwargs):
        """
        Detects if the call action is being used to create a new parameter or deletes an existing parameter.
        If so, it creates the parameter broadcast blueprint and broadcast it.

        :param spec: CallSpec object being passed to the call method.
        :param args: args being passed to the call method.
        :param kwargs: kwargs being passed to the call method.
        """

        if spec.target.split('.')[-1] == 'add_parameter':
            name = spec.target.split('.')[0] + '.' + '.'.join(spec.args)
            pb = ParameterBroadcastBluePrint(name, 'parameter-creation',
                                             kwargs['initial_value'],
                                             kwargs['unit'])
            self._broadcastParameterChange(pb)
        elif spec.target.split('.')[-1] == 'remove_parameter':
            name = spec.target.split('.')[0] + '.' + '.'.join(spec.args)
            pb = ParameterBroadcastBluePrint(name, 'parameter-deletion')
            self._broadcastParameterChange(pb)
示例#9
0
osc.ch1.t_stop.set(10)
osc.ch2.t_start.set(-10)
osc.ch2.t_stop.set(10)
osc.ch3.t_start.set(-10)
osc.ch3.t_stop.set(10)
osc.ch4.t_start.set(-10)
osc.ch4.t_stop.set(10)

amp = 1
PSG1.amp(amp)
PSG2.amp(amp)

station = Station()
station.snapshot()

station.add_component(osc)

# Criar um database
initialise_or_create_database_at("~/teste.db")

exp = load_or_create_experiment(experiment_name='osc realtime intro 2',
                                sample_name="osc realtime 1")


def calculateGain():
    Y = osc.ch3.wavesample()
    n_mean = int(len(Y) / 2)
    value = 2 * np.mean(Y[n_mean:])
    value = 10 * np.log(np.power(value, 2))
    return value
示例#10
0
            self.setpoint_names = (("I_channel", ), ("Q_channel", ))
            self.i = 2

        def get_raw(self):
            self.i += 1
            return (self.i, self.i + 100)

    import qcodes
    from qcodes import Parameter, Station
    from qcodes.tests.instrument_mocks import DummyInstrument

    station = Station()
    instr = DummyInstrument('instr', gates=['input', 'output', 'gain'])
    instr.gain(42)
    # station.add_component(p)
    station.add_component(instr)

    from core_tools.data.SQL.connect import SQL_conn_info_local, SQL_conn_info_remote, sample_info, set_up_local_storage
    # set_up_local_storage("xld_user", "XLDspin001", "vandersypen_data", "6dot", "XLD", "6D2S - SQ21-XX-X-XX-X")
    # set_up_local_storage("xld_user", "XLDspin001", "vandersypen_data", "6dot", "XLD", "testing")
    set_up_local_storage('stephan', 'magicc', 'test', 'Intel Project', 'F006',
                         'SQ38328342')

    now = str(datetime.datetime.now())
    path = os.path.join(os.getcwd(), 'test.db')
    initialise_or_create_database_at(path)
    load_or_create_experiment('tutorial ' + now, 'no sample')
    my_param1 = MyCounter('test_instr1')
    my_param2 = MyCounter('test_instr2')
    from qcodes.instrument.specialized_parameters import ElapsedTimeParameter
示例#11
0
    def record_S21_sweep_power_sweep_frequency(self):
        # -- setting vna parameters
        # vna.sweep_mode.set('CONT')
        self.vna.power.set(self.vnapower)
        self.vna.center.set(self.center_frequency)
        self.vna.span.set(self.frequency_span)
        self.vna.points.set(self.num_freq_points)
        self.vna.if_bandwidth.set(self.ifbandwidth)
        self.vna.trace.set(self.measuredtrace)
        self.vna.auto_sweep.set(False)

        from qcodes import Station

        station = Station()
        station.add_component(self.vna)

        # import pdb; pdb.set_trace()
        meas = Measurement(station=station)  # qcodes measurement

        # vna.groupdelay.set(groupdelayref) #does not work yet
        meas = Measurement()
        # register the first independent parameter
        meas.register_parameter(self.vna.power)
        # register the second independent parameter
        meas.register_parameter(self.vna.real, setpoints=(self.vna.power, ))
        # ^ (? Why would vna.real be an independent parameter?) Does it not get measured (dependent) as a function of freq?
        meas.register_parameter(
            self.vna.imaginary,
            setpoints=(self.vna.power, ))  # now register the dependent one
        meas.register_parameter(
            self.vna.phase,
            setpoints=(self.vna.power, ))  # now register the dependent one
        meas.register_parameter(
            self.vna.magnitude,
            setpoints=(self.vna.power, ))  # now register the dependent one

        # -- taking data
        with meas.run() as datasaver:
            for v1 in np.linspace(self.powersweepstart,
                                  self.powersweepstop,
                                  self.num_power_points,
                                  endpoint=True):
                self.vna.active_trace.set(1)

                power = self.vna.power.set(v1)

                print(self.vna.power.get())  # check

                # vna.auto_sweep.set(False)
                # vna.auto_sweep.set(True)
                # some bug not taking the last row therefore two sweeps
                self.vna.traces.tr1.run_sweep()

                # power=vna.power()
                # vna.auto_sweep.set(False)
                imag = self.vna.imaginary()
                real = self.vna.real()
                phase = self.vna.phase()
                mag = self.vna.magnitude()

                # vna.active_trace.set(2)
                # vna.traces.tr2.run_sweep()
                power = self.vna.power(
                )  # should still be the same as a few lines above

                # time.sleep(2)
                datasaver.add_result(
                    (self.vna.magnitude, mag), (self.vna.phase, phase),
                    (self.vna.real, real), (self.vna.imaginary, imag),
                    (self.vna.power, power))

                print(self.vna.power.get())

        plot_by_id(datasaver.run_id)

        pd = datasaver.dataset.get_parameter_data()

        # import pdb; pdb.set_trace()  # noqa BREAKPOINT

        magnitude_table = np.vstack(
            (np.ravel(pd[self.vna_name + "_tr1_magnitude"][self.vna_name +
                                                           "_power"]),
             np.ravel(pd[self.vna_name + "_tr1_magnitude"][self.vna_name +
                                                           "_tr1_frequency"]),
             np.ravel(pd[self.vna_name + "_tr1_magnitude"][self.vna_name +
                                                           "_tr1_magnitude"])))

        phase_table = np.vstack(
            (np.ravel(pd[self.vna_name + "_tr1_phase"][self.vna_name +
                                                       "_power"]),
             np.ravel(pd[self.vna_name + "_tr1_phase"][self.vna_name +
                                                       "_tr1_frequency"]),
             np.ravel(pd[self.vna_name + "_tr1_phase"][self.vna_name +
                                                       "_tr1_phase"])))

        real_table = np.vstack(
            (np.ravel(pd[self.vna_name + "_tr1_real"][self.vna_name +
                                                      "_power"]),
             np.ravel(pd[self.vna_name + "_tr1_real"][self.vna_name +
                                                      "_tr1_frequency"]),
             np.ravel(pd[self.vna_name + "_tr1_real"][self.vna_name +
                                                      "_tr1_real"])))

        imaginary_table = np.vstack(
            (np.ravel(pd[self.vna_name + "_tr1_imaginary"][self.vna_name +
                                                           "_power"]),
             np.ravel(pd[self.vna_name + "_tr1_imaginary"][self.vna_name +
                                                           "_tr1_frequency"]),
             np.ravel(pd[self.vna_name + "_tr1_imaginary"][self.vna_name +
                                                           "_tr1_imaginary"])))

        np.savetxt(
            os.path.join(
                self.raw_path_with_date,
                str(datasaver.run_id) + '_powersweep' + '_' +
                str(self.exp_name) + '_magnitude.txt'), magnitude_table)

        np.savetxt(
            os.path.join(
                self.raw_path_with_date,
                str(datasaver.run_id) + '_powersweep' + '_' +
                str(self.exp_name) + '_phase.txt'), phase_table)

        np.savetxt(
            os.path.join(
                self.raw_path_with_date,
                str(datasaver.run_id) + '_powersweep' + '_' +
                str(self.exp_name) + '_real.txt'), real_table)

        np.savetxt(
            os.path.join(
                self.raw_path_with_date,
                str(datasaver.run_id) + '_powersweep' + '_' +
                str(self.exp_name) + '_imaginary.txt'), imaginary_table)
示例#12
0
class UImain(QtWidgets.QMainWindow):
    def __init__(self, parent=None, config_file=None):
        super(UImain, self).__init__(parent)
        self.ui = Ui_MeasureIt()
        self.ui.setupUi(self)
        self.setWindowTitle("MeasureIt")
        self.ui.scanValue.setText('False')
        self.ui.scanValue.setStyleSheet('color: red')
        self.ui.scanParameterBox.addItem('time', 'time')
        self.sweep_settings = {
            'time': {
                'start': '',
                'end': '',
                'step': '',
                'step_sec': '',
                'continual': False,
                'bidirectional': False,
                'plot_bin': 1,
                'save_data': True,
                'plot_data': True,
                'ramp_to_start': True
            }
        }

        self.set_param_index = 0
        self.init_tables()
        self.make_connections()

        self.station = None
        self.sweep = None
        self.sweep_queue = SweepQueue(inter_delay=3)

        self.devices = {}
        self.track_params = {}
        self.set_params = {}
        self.shown_follow_params = []
        self.shown_set_params = []

        self.device_init = {}

        self.db = ''
        self.exp_name = ''
        self.sample_name = ''
        self.db_set = False
        self.datasets = []

        self.update_instrument_menu()
        self.load_station_and_connect_instruments(config_file)
        self.update_datasets()

        self.start_logs()

        # Create Queue and redirect sys.stdout to this queue
        queue = Queue()
        sys.stdout = WriteStream(queue)

        self.thread = OutputThread(queue)
        self.thread.mysignal.connect(self.append_stdout)
        self.thread.start()

        self.show()

    def init_tables(self):
        follow_header = self.ui.followParamTable.horizontalHeader()
        follow_header.setSectionResizeMode(0, QHeaderView.ResizeToContents)
        follow_header.resizeSection(0, 60)
        follow_header.setSectionResizeMode(1, QHeaderView.Fixed)
        follow_header.resizeSection(1, 60)
        follow_header.setSectionResizeMode(2, QHeaderView.Stretch)
        follow_header.setSectionResizeMode(3, QHeaderView.ResizeToContents)
        follow_header.setSectionResizeMode(4, QHeaderView.Fixed)
        follow_header.resizeSection(4, 40)

        output_header = self.ui.outputParamTable.horizontalHeader()
        output_header.setSectionResizeMode(0, QHeaderView.ResizeToContents)
        output_header.resizeSection(0, 60)
        output_header.setSectionResizeMode(1, QHeaderView.Fixed)
        output_header.resizeSection(1, 60)
        output_header.setSectionResizeMode(2, QHeaderView.Stretch)
        output_header.setSectionResizeMode(3, QHeaderView.Fixed)
        output_header.resizeSection(3, 40)
        output_header.setSectionResizeMode(4, QHeaderView.Fixed)
        output_header.resizeSection(4, 40)

    def make_connections(self):
        self.ui.editParameterButton.clicked.connect(self.edit_parameters)
        self.ui.startButton.clicked.connect(self.start_sweep)
        self.ui.pauseButton.clicked.connect(self.pause_resume_sweep)
        self.ui.flipDirectionButton.clicked.connect(self.flip_direction)
        self.ui.endButton.clicked.connect(self.end_sweep)
        self.ui.saveButton.clicked.connect(self.setup_save)

        self.ui.addSweepButton.clicked.connect(self.add_sweep_to_queue)
        self.ui.removeActionButton.clicked.connect(
            lambda: self.remove_action_from_queue(self.ui.sequenceWidget.
                                                  currentRow()))
        self.ui.startSequenceButton.clicked.connect(self.start_sequence)
        self.ui.addSaveButton.clicked.connect(self.add_save_to_sequence)
        self.ui.upSequenceButton.clicked.connect(
            lambda: self.move_action_in_sequence(-1))
        self.ui.downSequenceButton.clicked.connect(
            lambda: self.move_action_in_sequence(+1))

        self.ui.actionSaveStation.triggered.connect(self.save_station)
        self.ui.actionLoadStation.triggered.connect(self.load_station)
        self.ui.actionQuit.triggered.connect(self.close)

        self.ui.actionLoad_Sweep.triggered.connect(self.load_sweep)
        self.ui.actionSave_Sweep.triggered.connect(self.save_sweep)
        self.ui.actionSave_Sequence.triggered.connect(self.save_sequence)
        self.ui.actionLoad_Sequence.triggered.connect(self.load_sequence)

        self.ui.sequenceWidget.itemDoubleClicked.connect(
            self.edit_sequence_item)

        self.ui.scanParameterBox.currentIndexChanged.connect(
            self.update_param_combobox)
        self.update_param_combobox(0)

    def update_param_combobox(self, index):
        old_set_param = self.ui.scanParameterBox.itemData(self.set_param_index)
        self.sweep_settings[old_set_param] = {
            'start': self.ui.startEdit.text(),
            'end': self.ui.endEdit.text(),
            'step': self.ui.stepEdit.text(),
            'step_sec': self.ui.stepsecEdit.text(),
            'save_data': self.ui.saveBox.isChecked(),
            'plot_data': self.ui.livePlotBox.isChecked(),
            'plot_bin': self.ui.plotbinEdit.text(),
            'bidirectional': self.ui.bidirectionalBox.isChecked(),
            'continual': self.ui.continualBox.isChecked()
        }

        if index == 0:
            self.ui.startEdit.setReadOnly(True)
            p = self.ui.startEdit.palette()
            p.setColor(self.ui.startEdit.backgroundRole(), Qt.gray)
            self.ui.startEdit.setPalette(p)
            self.ui.stepEdit.setReadOnly(True)
            q = self.ui.stepEdit.palette()
            q.setColor(self.ui.stepEdit.backgroundRole(), Qt.gray)
            self.ui.stepEdit.setPalette(q)
        else:
            self.ui.startEdit.setReadOnly(False)
            p = self.ui.startEdit.palette()
            p.setColor(self.ui.startEdit.backgroundRole(), Qt.white)
            self.ui.startEdit.setPalette(p)
            self.ui.stepEdit.setReadOnly(False)
            q = self.ui.stepEdit.palette()
            q.setColor(self.ui.stepEdit.backgroundRole(), Qt.white)
            self.ui.stepEdit.setPalette(q)

        self.update_sweep_box(
            self.sweep_settings[self.ui.scanParameterBox.currentData()])
        self.set_param_index = index

    def start_logs(self):
        self.stdout_filename = os.environ[
            'MeasureItHome'] + '\\logs\\stdout\\' + datetime.now().strftime(
                "%Y-%m-%d") + '.txt'
        self.stderr_filename = os.environ[
            'MeasureItHome'] + '\\logs\\stderr\\' + datetime.now().strftime(
                "%Y-%m-%d") + '.txt'

        self.stdout_file = open(self.stdout_filename, 'a')
        sys.stderr = open(self.stderr_filename, 'a')

        self.stdout_file.write('Started program at  ' +
                               datetime.now().strftime("%H:%M:%S") + '\n')
        print('Started program at  ' + datetime.now().strftime("%H:%M:%S"),
              file=sys.stderr)
        self.stdout_file.close()

        start_all_logging()

    def load_station(self):
        (fileName, x) = QFileDialog.getOpenFileName(
            self, "Load Station", os.environ['MeasureItHome'] + "\\cfg\\",
            "Stations (*.station.yaml)")

        if len(fileName) == 0:
            return

        for name in list(self.devices.keys()):
            self.do_remove_device(name)
        self.load_station_and_connect_instruments(fileName)

    def load_station_and_connect_instruments(self, config_file=None):
        self.station = Station()
        try:
            self.station.load_config_file(config_file)
        except Exception as e:
            self.show_error(
                "Error",
                "Couldn't open the station configuration file. Started new station.",
                e)
            return
        if self.station.config is None:
            return

        for name, instr in self.station.config['instruments'].items():
            try:
                dev = self.station.load_instrument(name)
                self.devices[str(name)] = dev
            except Exception as e:
                self.show_error(
                    'Instrument Error', f'Error connecting to {name}, '
                    'either the name is already in use or the device is unavailable.',
                    e)
        self.update_instrument_menu()

    def save_station(self):
        ss_ui = SaveStationGUI(self)
        if ss_ui.exec_():
            fp = ss_ui.get_file()
            default = ss_ui.ui.defaultBox.isChecked()

            if len(fp) > 0:
                self.do_save_station(fp, default)

    def do_save_station(self, filename, set_as_default=False):
        def add_field(ss, instr, field, value):
            try:
                ss[field] = instr[value]
            except KeyError:
                pass

        if '.station.yaml' not in filename:
            filename += '.station.yaml'

        snap = {'instruments': {}}

        for name, instr in self.station.snapshot()['instruments'].items():
            snap['instruments'][name] = {}
            add_field(snap['instruments'][name], instr, 'type', '__class__')
            add_field(snap['instruments'][name], instr, 'address', 'address')
            snap['instruments'][name]['enable_forced_reconnect'] = True
            if name in self.device_init:
                snap['instruments'][name]['init'] = {}
                for key, value in self.device_init[name].items():
                    snap['instruments'][name]['init'][key] = value

            # Could also save parameter information here

        with open(filename, 'w') as file:
            yaml = YAML()
            yaml.dump(snap, file)
            if set_as_default:
                qc.config['station']['default_file'] = filename
                qc.config.save_config(os.environ['MeasureItHome'] +
                                      '\\cfg\\qcodesrc.json')

    def edit_parameters(self):
        param_ui = EditParameterGUI(self.devices, self.track_params,
                                    self.set_params, self)
        if param_ui.exec_():
            self.track_params = param_ui.new_track_params
            self.set_params = param_ui.new_set_params
            print('Here are the parameters currently being tracked:')
            for name, p in self.track_params.items():
                print(name)
            print('Here are the parameters available for sweeping:')
            for name, p in self.set_params.items():
                print(name)
            self.update_parameters()

    def update_parameters(self):

        # Set up the follow parameter table
        self.ui.followParamTable.clearContents()
        self.ui.followParamTable.setRowCount(0)
        for m, (name, p) in enumerate(self.track_params.items()):
            self.ui.followParamTable.insertRow(m)

            paramitem = QTableWidgetItem(name)
            paramitem.setData(32, p)
            paramitem.setFlags(Qt.ItemIsSelectable)
            self.ui.followParamTable.setItem(m, 0, paramitem)

            labelitem = QLineEdit(p.label)
            labelitem.editingFinished.connect(
                lambda p=p, labelitem=labelitem: self.update_labels(
                    p, labelitem.text()))
            self.ui.followParamTable.setCellWidget(m, 1, labelitem)

            valueitem = QTableWidgetItem(str(self.get_param(p)))
            self.ui.followParamTable.setItem(m, 2, valueitem)

            includeBox = QCheckBox()
            includeBox.setChecked(True)
            self.ui.followParamTable.setCellWidget(m, 3, includeBox)

            updateButton = QPushButton("Get")
            updateButton.clicked.connect(
                lambda checked, m=m, p=p, valueitem=valueitem: valueitem.
                setText(str(self.get_param(p))))
            self.ui.followParamTable.setCellWidget(m, 4, updateButton)

        # Set up the output parameter table
        self.ui.outputParamTable.clearContents()
        self.ui.outputParamTable.setRowCount(0)
        self.ui.scanParameterBox.clear()
        self.ui.scanParameterBox.addItem('time', 'time')
        for n, (name, p) in enumerate(self.set_params.items()):
            self.ui.outputParamTable.insertRow(n)

            paramitem = QTableWidgetItem(name)
            paramitem.setData(32, p)
            paramitem.setFlags(Qt.ItemIsSelectable)
            self.ui.outputParamTable.setItem(n, 0, paramitem)

            labelitem = QLineEdit(p.label)
            labelitem.editingFinished.connect(
                lambda p=p, labelitem=labelitem: self.update_labels(
                    p, labelitem.text()))
            self.ui.outputParamTable.setCellWidget(n, 1, labelitem)

            valueitem = QLineEdit(str(self.get_param(p)))
            self.ui.outputParamTable.setCellWidget(n, 2, valueitem)

            setButton = QPushButton("Set")
            setButton.clicked.connect(lambda checked, p=p, valueitem=valueitem:
                                      self.set_param(p, valueitem))
            self.ui.outputParamTable.setCellWidget(n, 3, setButton)

            getButton = QPushButton("Get")
            getButton.clicked.connect(
                lambda checked, p=p, valueitem=valueitem: valueitem.setText(
                    str(self.get_param(p))))
            self.ui.outputParamTable.setCellWidget(n, 4, getButton)

            self.ui.scanParameterBox.addItem(p.label, p)
            if p not in list(self.sweep_settings.keys()):
                self.sweep_settings[p] = {
                    'start': '',
                    'end': '',
                    'step': '',
                    'step_sec': '',
                    'continual': False,
                    'bidirectional': False,
                    'plot_bin': 1,
                    'save_data': True,
                    'plot_data': True,
                    'ramp_to_start': True
                }

    def set_param(self, p, valueitem):
        try:
            if "Int" in repr(p.vals) or "Number" in repr(p.vals):
                safe_set(p, _value_parser(valueitem.text()))
            elif "String" in repr(p.vals):
                safe_set(p, str(valueitem.text()))
            elif "Bool" in repr(p.vals) or 'False' in repr(p.vals):
                value = valueitem.text()
                if value == "false" or value == "False" or value == "0":
                    safe_set(p, False)
                elif value == "true" or value == "True" or value == "1":
                    safe_set(p, True)
                else:
                    safe_set(p, value)
            else:
                safe_set(p, valueitem.text())
        except ParameterException:
            self.show_error(
                'Error',
                f'Could not set {p.label} to {valueitem.text()}. Check the command and try '
                'again.')

    def get_param(self, p):
        try:
            return str(safe_get(p))
        except ParameterException as e:
            self.show_error(
                'Error',
                f'Could not get {p.label}. Check the exception and try again.',
                e)
            return ''

    def update_labels(self, p, newlabel):
        p.label = newlabel

        for n, (name, param) in enumerate(list(self.track_params.items())):
            self.ui.followParamTable.cellWidget(n, 1).setText(param.label)
        for n, (name, param) in enumerate(list(self.set_params.items())):
            self.ui.outputParamTable.cellWidget(n, 1).setText(param.label)
        for n in range(self.ui.scanParameterBox.count() - 1):
            param = self.ui.scanParameterBox.itemData(n + 1)
            self.ui.scanParameterBox.setItemText(n + 1, param.label)

    def create_sweep(self):
        # Check if we're scanning time, then if so, do Sweep0D
        if self.ui.scanParameterBox.currentText() == 'time':
            stop = _value_parser(self.ui.endEdit.text())
            stepsec = _value_parser(self.ui.stepsecEdit.text())
            plotbin = self.ui.plotbinEdit.text()
            plotbin = int(plotbin)
            if plotbin < 1:
                self.ui.plotbinEdit.setText('1')
                raise ValueError

            save = self.ui.saveBox.isChecked()
            plot = self.ui.livePlotBox.isChecked()

            sweep = Sweep0D(max_time=stop,
                            inter_delay=1 / stepsec,
                            save_data=save,
                            plot_data=plot,
                            plot_bin=plotbin)
        # Set up Sweep1D if we're not sweeping time
        else:
            start = _value_parser(self.ui.startEdit.text())
            stop = _value_parser(self.ui.endEdit.text())
            step = _value_parser(self.ui.stepEdit.text())
            stepsec = _value_parser(self.ui.stepsecEdit.text())
            plotbin = self.ui.plotbinEdit.text()
            plotbin = int(plotbin)
            if plotbin < 1:
                self.ui.plotbinEdit.setText('1')
                raise ValueError

            set_param = self.ui.scanParameterBox.currentData()
            twoway = self.ui.bidirectionalBox.isChecked()
            continuous = self.ui.continualBox.isChecked()
            save = self.ui.saveBox.isChecked()
            plot = self.ui.livePlotBox.isChecked()

            sweep = Sweep1D(set_param,
                            start,
                            stop,
                            step,
                            inter_delay=1.0 / stepsec,
                            bidirectional=twoway,
                            continual=continuous,
                            save_data=save,
                            plot_data=plot,
                            x_axis_time=0,
                            plot_bin=plotbin)

        for n in range(self.ui.followParamTable.rowCount()):
            if self.ui.followParamTable.cellWidget(n, 3).isChecked():
                param = self.ui.followParamTable.item(n, 0).data(32)
                if param is not sweep.set_param:
                    sweep.follow_param(param)

        sweep.update_signal.connect(self.receive_updates)
        sweep.dataset_signal.connect(self.receive_dataset)

        if isinstance(sweep, Sweep0D) and len(
                sweep._params) == 0 and sweep.plot_data:
            self.show_error(
                "Error",
                "Can't plot time against nothing. Either select some parameters to follow or "
                "unselect \'plot data\'.")
            sweep = None

        return sweep

    def start_sweep(self):
        if self.sweep is not None:
            if self.sweep.is_running:
                alert = QMessageBox()
                new_sweep = alert.question(
                    self, "Warning!",
                    "A sweep is already running! Stop the current sweep and start a new one?",
                    alert.Yes | alert.No)

                if new_sweep == alert.Yes:
                    self.sweep.stop()
                    self.sweep.kill()
                    self.sweep = None
                else:
                    return
            elif self.sweep.set_param == self.ui.scanParameterBox.currentData() \
                    and self.ui.rampToStartBox.isChecked() is False:
                alert = QMessageBox()
                new_sweep = alert.question(
                    self, "Warning!",
                    "You are about to start a new sweep of the parameter you just swept, "
                    "without ramping from the current setpoint to the start value. Are you "
                    "sure you wish to do so?", alert.Yes | alert.No)

                if new_sweep == alert.Yes:
                    self.sweep.kill()
                    self.sweep = None
                else:
                    return
            else:
                self.sweep.kill()

        try:
            self.sweep = self.create_sweep()
            if self.sweep is None:
                return
        except ValueError:
            self.show_error(
                "Error", "One or more of the sweep input values are invalid. "
                "Valid inputs consist of a number optionally followed by "
                "suffix f/p/n/u/m/k/M/G.")
            return

        save = self.ui.saveBox.isChecked()
        if save and self.db_set is False:
            if not self.setup_save():
                self.show_error(
                    'Error',
                    "Database was not opened. Set save information before running the sweep again."
                )
                return

        self.sweep.start(ramp_to_start=self.ui.rampToStartBox.isChecked())

    def pause_resume_sweep(self):
        if self.sweep is None:
            return

        if self.sweep.is_running:
            self.sweep.stop()
            self.ui.pauseButton.setText("Resume")
        else:
            self.sweep.resume()
            self.ui.pauseButton.setText("Pause")

    def flip_direction(self):
        if self.sweep is None:
            return

        self.sweep.flip_direction()

    def end_sweep(self):
        if self.sweep is None:
            return

        print('trying to kill the sweep')
        self.sweep.kill()
        self.sweep = None

    def add_sweep_to_queue(self):
        try:
            sweep = self.create_sweep()
            if sweep is None:
                return
        except ValueError as e:
            self.show_error(
                "Error", "One or more of the sweep input values are invalid. "
                "Valid inputs consist of a number optionally followed by "
                "suffix f/p/n/u/m/k/M/G.", e)
            return

        self.sweep_queue.append(sweep)
        self.update_sequence_table()

    def update_sequence_table(self, cursor=-1):
        self.ui.sequenceWidget.clear()
        for n, action in enumerate(self.sweep_queue.queue):
            item = QListWidgetItem(action.__repr__())
            item.setData(32, action)
            self.ui.sequenceWidget.addItem(item)

        self.ui.sequenceWidget.setCurrentRow(cursor)

    def remove_action_from_queue(self, action):
        if isinstance(action, int) and action >= 0:
            self.sweep_queue.delete(action)
            self.update_sequence_table()

    def move_action_in_sequence(self, change):
        row = self.ui.sequenceWidget.currentRow()
        if row == -1:
            return
        action = self.ui.sequenceWidget.currentItem().data(32)

        new_pos = self.sweep_queue.move(action, change)
        self.update_sequence_table(cursor=new_pos)

    def add_save_to_sequence(self):
        default_db = self.db
        default_exp = self.exp_name
        default_sample = self.sample_name
        for action in self.sweep_queue.queue:
            if isinstance(action, DatabaseEntry):
                default_db = action.db
                default_exp = action.exp
                default_sample = action.samp

        save_data_ui = SaveDataGUI(self, default_db, default_exp,
                                   default_sample)
        if save_data_ui.exec_():
            (db, exp_name, sample_name) = save_data_ui.get_save_info()
            db_entry = DatabaseEntry(db, exp_name, sample_name,
                                     self.sweep_queue.begin_next)
            self.sweep_queue.append(db_entry)
            self.update_sequence_table()

    def edit_sequence_item(self, action):
        n = -1
        for num, item in enumerate(self.sweep_queue.queue):
            if action.data(32) is item:
                n = num

        if n == -1:
            return

        if isinstance(self.sweep_queue.queue[n], BaseSweep):
            edit_sweep_ui = EditSweepGUI(self, self.sweep_queue.queue[n])
            r = edit_sweep_ui.exec_()
            if r == 1:
                self.sweep_queue.replace(n, edit_sweep_ui.return_sweep())
                self.update_sequence_table()
            elif r == 2:
                self.sweep_queue.delete(n)
                self.update_sequence_table()
        elif isinstance(self.sweep_queue.queue[n], DatabaseEntry):
            entry = self.sweep_queue.queue[n]
            save_data_ui = SaveDataGUI(self, entry.db, entry.exp, entry.samp)
            if save_data_ui.exec_():
                (db, exp_name, sample_name) = save_data_ui.get_save_info()
                db_entry = DatabaseEntry(db, exp_name, sample_name,
                                         self.sweep_queue.begin_next)
                self.sweep_queue.replace(n, db_entry)
                self.update_sequence_table()

    def start_sequence(self):
        if self.sweep is not None:
            if self.sweep.is_running:
                alert = QMessageBox()
                new_sweep = alert.question(
                    self, "Warning!",
                    "A sweep is already running! Stop the current sweep and start a new one?",
                    alert.Yes | alert.No)

                if new_sweep == alert.Yes:
                    self.sweep.stop()
                    self.sweep.kill()
                    self.sweep = None
                else:
                    return
            else:
                self.sweep.kill()

        save_configured = False
        for s in self.sweep_queue.queue:
            if isinstance(s, DatabaseEntry):
                save_configured = True
            elif isinstance(s, BaseSweep):
                if s.save_data is True and save_configured is False:
                    self.show_error(
                        'Error',
                        'A sweep will try to save data before a database location is set. Fix '
                        'and try again.')
                    return

        self.sweep_queue.newSweepSignal.connect(
            lambda sweep: self.new_queue_sweep(sweep))
        self.sweep_queue.start()

    def new_queue_sweep(self, sweep):
        self.sweep = sweep
        self.update_sequence_table()

    def setup_save(self):
        save_data_ui = SaveDataGUI(self, self.db, self.exp_name,
                                   self.sample_name)
        if save_data_ui.exec_():
            (self.db, self.exp_name,
             self.sample_name) = save_data_ui.get_save_info()

            try:
                initialise_or_create_database_at(self.db)
                qc.new_experiment(self.exp_name, self.sample_name)
                self.db_set = True
                return True
            except Exception as e:
                self.show_error('Error',
                                "Error opening up database. Try again.", e)
                return False
        else:
            return False

    def save_sweep(self):
        if self.sweep is None:
            self.sweep = self.create_sweep()

        (filename, x) = QFileDialog.getSaveFileName(
            self, "Save Sweep as JSON",
            f"{os.environ['MeasureItHome']}\\Experiments\\untitled_sweep.json",
            "JSON (*.txt *.json)")

        if len(filename) > 0:
            self.sweep.export_json(filename)

    def load_sweep(self):
        (filename, x) = QFileDialog.getOpenFileName(
            self, "Load Sweep from JSON",
            f"{os.environ['MeasureItHome']}\\Experiments\\",
            "JSON (*.txt *.json)")

        if len(filename) > 0:
            try:
                new_sweep = BaseSweep.init_from_json(filename, self.station)
                self.sweep = new_sweep

                settings = {
                    'start': self.sweep.begin,
                    'end': self.sweep.end,
                    'step': self.sweep.step,
                    'step_sec': 1 / self.sweep.inter_delay,
                    'save_data': self.sweep.save_data,
                    'plot_data': self.sweep.plot_data,
                    'plot_bin': self.sweep.plot_bin,
                    'bidirectional': self.sweep.bidirectional,
                    'continual': self.sweep.continuous
                }

                self.update_sweep_box(settings)

                # Load parameters
                # TODO: set the set_param box

            except Exception as e:
                self.show_error('Error', "Could not load the sweep.", e)

    def update_sweep_box(self, settings):
        self.ui.startEdit.setText(str(settings['start']))
        self.ui.endEdit.setText(str(settings['end']))
        self.ui.stepEdit.setText(str(settings['step']))
        self.ui.stepsecEdit.setText(str(settings['step_sec']))
        self.ui.saveBox.setChecked(settings['save_data'])
        self.ui.livePlotBox.setChecked(settings['plot_data'])
        self.ui.plotbinEdit.setText(str(settings['plot_bin']))
        self.ui.bidirectionalBox.setChecked(settings['bidirectional'])
        self.ui.continualBox.setChecked(settings['continual'])

    def save_sequence(self):
        (filename, x) = QFileDialog.getSaveFileName(
            self, "Save Sequence as JSON",
            f"{os.environ['MeasureItHome']}\\Experiments\\untitled.json",
            "JSON (*.txt *.json)")

        if len(filename) > 0:
            self.sweep_queue.export_json(filename)

    def load_sequence(self):
        (filename, x) = QFileDialog.getOpenFileName(
            self, "Load Sequence from JSON",
            f"{os.environ['MeasureItHome']}\\Experiments\\",
            "JSON (*.txt *.json)")

        if len(filename) > 0:
            try:
                new_queue = SweepQueue.init_from_json(filename, self.station)
                self.sweep_queue = new_queue
                self.update_sequence_table()
                for sweep in self.sweep_queue.queue:
                    sweep.dataset_signal.connect(self.receive_dataset)
                    sweep.update_signal.connect(self.receive_updates)
            except Exception as e:
                self.show_error('Error', "Could not load the sequence.", e)

    def add_device(self):
        # TODO:
        #   Add in ability to pass args and kwargs to the constructor

        instrument_ui = AddInstrumentGUI(self)
        if instrument_ui.exec_():
            d = instrument_ui.get_selected()
            try:
                d['name'] = _name_parser(d['name'])
            except ValueError as e:
                self.show_error("Error",
                                "Instrument name must start with a letter.", e)
                return

            if instrument_ui.ui.nameEdit.text() in self.devices.keys():
                self.show_error(
                    "Error",
                    "Already have an instrument with that name in the station."
                )
                return

            # Now, set up our initialization for each device, if it doesn't follow the standard initialization
            new_dev = self.connect_device(d['device'], d['class'], d['name'],
                                          d['address'], d['args'], d['kwargs'])

            if new_dev is not None:
                self.devices[d['name']] = new_dev
                self.station.add_component(new_dev, update_snapshot=False)
                self.update_instrument_menu()

    def connect_device(self,
                       device,
                       classtype,
                       name,
                       address,
                       args=[],
                       kwargs={}):
        new_dev = None
        if name in Instrument.instances():
            self.show_error(
                "Error",
                f'Instrument name is already in use. Try again with a new name.'
            )
            return None
        try:
            if device == 'Dummy' or device == 'Test':
                new_dev = classtype(name)
            else:
                new_dev = classtype(name, address, *args, **kwargs)
                if len(kwargs.keys()) > 0:
                    self.device_init[name] = kwargs
        except Exception as e:
            self.show_error(
                "Error",
                f'Couldn\'t connect to the instrument. Check address and try again.',
                e)
            print(e, file=sys.stderr)
            if hasattr(new_dev, 'close'):
                new_dev.close()
            new_dev = None

        return new_dev

    def update_instrument_menu(self):
        # TODO:
        #   Add some clickable action to the name hanging out in the device menu
        self.ui.menuInstruments.clear()

        self.ui.addInstrumentAction = QAction("Add instrument...",
                                              self.ui.menuInstruments)
        self.ui.addInstrumentAction.setStatusTip("Connect to a new instrument")
        self.ui.addInstrumentAction.triggered.connect(self.add_device)

        self.ui.removeInstrumentAction = QAction("Remove instrument...",
                                                 self.ui.menuInstruments)
        self.ui.removeInstrumentAction.setStatusTip("Disconnect an instrument")
        self.ui.removeInstrumentAction.triggered.connect(self.remove_device)

        self.ui.menuInstruments.addAction(self.ui.addInstrumentAction)
        self.ui.menuInstruments.addAction(self.ui.removeInstrumentAction)
        self.ui.menuInstruments.addSeparator()

        for name, dev in self.devices.items():
            act = self.ui.menuInstruments.addAction(
                f"{dev.name} ({dev.__class__.__name__})")
            act.setData(dev)

    def remove_device(self):
        remove_ui = RemoveInstrumentGUI(self.devices, self)
        if remove_ui.exec_():
            dev = remove_ui.ui.instrumentBox.currentText()
            if len(dev) > 0:
                self.do_remove_device(dev)

    def do_remove_device(self, name):
        self.station.remove_component(name)
        dev = self.devices.pop(name)
        dev.close()
        if name in self.device_init.keys():
            self.device_init.pop(name)
        self.update_instrument_menu()

    @pyqtSlot(str)
    def append_stdout(self, text):
        self.ui.consoleEdit.moveCursor(QTextCursor.End)
        self.ui.consoleEdit.insertPlainText(text)

        self.stdout_file = open(self.stdout_filename, 'a')
        time = datetime.now().strftime("%H:%M:%S")
        self.stdout_file.write(time + '\t' + text)
        self.stdout_file.close()

    @pyqtSlot(dict)
    def receive_updates(self, update_dict):
        is_running = update_dict['status']
        set_param = update_dict['set_param']
        setpoint = update_dict['setpoint']
        direction = update_dict['direction']

        self.ui.scanValue.setText(f'{is_running}')
        if is_running:
            self.ui.scanValue.setStyleSheet('color: green')
            self.ui.pauseButton.setText('Pause')
        else:
            self.ui.scanValue.setStyleSheet('color: red')
            self.ui.pauseButton.setText('Resume')
        if set_param == 'time':
            self.ui.paramValue.setText('time')
        else:
            self.ui.paramValue.setText(f'{set_param.label}')
        if setpoint is not None:
            self.ui.setpointValue.setText(f'{str(setpoint)}')
        if direction:
            self.ui.directionValue.setText('Backward')
        else:
            self.ui.directionValue.setText('Forward')

    @pyqtSlot(dict)
    def receive_dataset(self, dataset):
        self.datasets.append(dataset)
        self.update_datasets()

    def update_datasets(self):
        self.ui.menuData.clear()

        self.ui.loadDatabaseAction = QAction("Load Database", self)
        self.ui.loadDatabaseAction.setStatusTip("Load runs from database")
        self.ui.loadDatabaseAction.triggered.connect(self.load_database)

        self.ui.exportDatasetAction = QAction("Export All", self)
        self.ui.exportDatasetAction.setStatusTip(
            "Export all datasets currently loaded to csv")
        self.ui.exportDatasetAction.triggered.connect(self.export_all_datasets)

        self.ui.removeDatasetAction = QAction("Remove All", self)
        self.ui.removeDatasetAction.setStatusTip(
            "Remove all datasets currently loaded (does not delete data")
        self.ui.removeDatasetAction.triggered.connect(self.remove_all_datasets)

        self.ui.menuData.addAction(self.ui.loadDatabaseAction)
        self.ui.menuData.addAction(self.ui.exportDatasetAction)
        self.ui.menuData.addAction(self.ui.removeDatasetAction)
        self.ui.menuData.addSeparator()

        for ds in self.datasets:
            act = self.ui.menuData.addAction(
                f"{ds['run id']} - {ds['exp name']} / {ds['sample name']}")
            act.setData(ds)
            act.triggered.connect(partial(self.view_dataset, ds))

    def load_database(self):
        def check_existing_ds(ds):
            for old_ds in self.datasets:
                if str(ds['run id']) == str(old_ds['run id']) and str(ds['exp name']) == str(old_ds['exp name']) \
                        and str(ds['sample name']) == str(old_ds['sample name']) and str(ds['db']) == str(old_ds['db']):
                    return False
            return True

        gui = SaveDataGUI(self)

        if gui.exec_():
            (db, exp_name, sample_name) = gui.get_save_info()
            initialise_or_create_database_at(db)

            exps = experiments()
            new_datasets = 0
            for exp in exps:
                for ds in exp.data_sets():
                    if len(exp_name) == 0 or ds.exp_name == exp_name:
                        if len(sample_name
                               ) == 0 or ds.sample_name == sample_name:
                            new_ds = {}
                            new_ds['run id'] = ds.run_id
                            new_ds['exp name'] = ds.exp_name
                            new_ds['sample name'] = ds.sample_name
                            new_ds['db'] = ds.path_to_db

                            if check_existing_ds(new_ds) is True:
                                self.datasets.append(new_ds)
                                new_datasets += 1

            self.update_datasets()
            if new_datasets == 0:
                self.show_error(
                    'Error',
                    'No (new) data sets found with the specified experiment and sample name!'
                )

    def view_dataset(self, ds):
        dataset_gui = ViewDatasetGUI(self, ds)
        dataset_gui.show()
        dataset_gui.activateWindow()

    def export_all_datasets(self):
        directory = QFileDialog.getExistingDirectory(
            self, "Save Data to .csv",
            f'{os.environ["MeasureItHome"]}\\Origin Files\\')
        if len(directory) == 0:
            return

        unsaved_sets = []
        for ds_info in self.datasets:
            try:
                ds = load_by_run_spec(experiment_name=ds_info['exp name'],
                                      sample_name=ds_info['sample name'],
                                      captured_run_id=ds_info['run id'])

                filename = f"{directory}\\{ds.run_id}_{ds.exp_name}_{ds.sample_name}.csv"
                save_to_csv(ds, filename)
            except:
                unsaved_sets.append(
                    f"{ds.run_id}_{ds.exp_name}_{ds.sample_name}")

        if len(unsaved_sets) > 0:
            error_text = 'Failed to export the following datasets:\n\n'
            for i, ds in enumerate(unsaved_sets):
                error_text += ds
                if i + 1 != len(unsaved_sets):
                    error_text += ', '
            error_text += '.\n\nThis is possibly due to a file name conflict or due to no data being stored in that run.'
            self.show_error('Error', error_text)

    def remove_all_datasets(self):
        alert = QMessageBox()
        removal = alert.question(
            self, "Warning!",
            "You are about to remove all the currently loaded datasets. This will not delete "
            "the data, but you will have to reload each run to access them again here. Are you "
            "sure you want to do so?", alert.Yes | alert.No)

        if removal == alert.Yes:
            self.datasets = []
            self.update_datasets()

    def show_error(self, title, message, e=None):
        msg_box = QMessageBox()
        msg_box.setWindowTitle(title)
        if e is not None:
            message += f'\n\nPython message: {e}'
        msg_box.setText(message)
        msg_box.setStandardButtons(QMessageBox.Ok)
        msg_box.exec_()

    def exit(self):
        for key, dev in self.devices.items():
            dev.close()

        if self.sweep is not None:
            self.sweep.kill()

        self.thread.stop = True
        self.thread.exit()
        if not self.thread.wait(5000):
            self.thread.terminate()
            print("Forced stdout thread to terminate.", file=sys.stderr)

        self.stdout_file = open(self.stdout_filename, 'a')
        self.stdout_file.write("Program exited at " +
                               datetime.now().strftime("%H:%M:%S") + '\n')
        print("Program exited at " + datetime.now().strftime("%H:%M:%S"),
              file=sys.stderr)
        self.stdout_file.close()

        app = QtGui.QGuiApplication.instance()
        app.closeAllWindows()

    def closeEvent(self, event):
        are_you_sure = QMessageBox()
        close = are_you_sure.question(
            self, "Exit",
            "Are you sure you want to close all windows and exit the application?",
            are_you_sure.Yes | are_you_sure.No)

        if close == are_you_sure.Yes:
            self.exit()
            event.accept()
        else:
            event.ignore()
            return