Esempio n. 1
0
class MyProgram(Thread):  # pylint: disable=too-many-instance-attributes
    """My fancy program"""

    def __init__(self, args):
        super(MyProgram, self).__init__()

        # Form channel_id e.g: A1
        self.channel_id = args.power_supply + args.output

        ### Required by the stepped program runner
        # Accepted capabilities are: can_edit, can_play,
        # can_stop, can_quit
        self.capabilities = ('can_stop', 'can_start', 'can_edit')
        # Status fields (in order)
        self.status_fields = (
            # Status
            {'codename': 'status_field', 'title': 'Status'},
            # Voltage
            {'codename': self.channel_id + '_voltage',
             'title': 'Voltage', 'formatter': '{:.3f}', 'unit': 'V'},
            # Voltage setpoint
            {'codename': self.channel_id + '_voltage_setpoint',
             'title': 'Voltage Setpoint', 'formatter': '{:.3f}', 'unit': 'V'},
            # Current
            {'codename': self.channel_id + '_current',
             'title': 'Current', 'formatter': '{:.3f}', 'unit': 'A'},
            # Current limit
            {'codename': self.channel_id + '_current_limit',
             'title': 'Current limit', 'formatter': '{:.3f}', 'unit': 'A'},
            # Charge
            {'codename': self.channel_id + '_accum_charge',
             'title': 'Accumulated charge', 'formatter': '{:.3f}', 'unit': 'C'},
            # Time elapsed (step)
            {'codename': 'elapsed',
             'title': 'Time elapsed (step)', 'formatter': '{:.1f}', 'unit': 's'},
            # Time remaining (step)
            {'codename': 'remaining',
             'title': 'Time remaining (step)', 'formatter': '{:.1f}', 'unit': 's'},
            # Time elapsed (total)
            {'codename': 'elapsed_total',
             'title': 'Time elapsed (total)', 'formatter': '{:.1f}', 'unit': 's'},
            # Time remaining (total)
            {'codename': 'remaining_total',
             'title': 'Time remaining (total)', 'formatter': '{:.1f}', 'unit': 's'},
            # Iteration time
            {'codename': 'iteration_time',
             'title': 'Iteration time', 'formatter': '{:.2f}', 'unit': 's'},
        )
        # Queue for GUI updates
        self.message_queue = Queue()
        # The GUI also looks in self.config, see below

        ### Normal program
        # Setup my program
        with open(path.join(THIS_DIR, args.program_file)) as file__:
            self.config, self.steps = parse_ramp(file__)
        # The GUI will look for keys: program_title in config
        self.say('Using power supply channel: ' + self.channel_id)
        self.say('Loaded with config:\n' + pformat(self.config))
        self.active_step = 0
        self.send_steps()

        # Add completions for the edits
        self._completion_additions = []
        for number, step in enumerate(self.steps):
            base = 'edit {} '.format(number)
            self._completion_additions.append(base)
            for field in sorted(step.fields):
                self._completion_additions.append('{}{}='.format(base, field))

        # Base for the status
        self.status = {'status_field': 'Initialized'}

        # General variables
        self.stop = False
        self.ok_to_start = False

        # Setup power supply
        # Create a partial function with the output substitued in
        self.send_command = partial(_send_command, args.output, args.power_supply)
        self.power_supply_on_off(True, self.config['maxcurrent_start'])
        # Power supply commands, must match order with self.codenames
        self.power_supply_commands = (
            'read_actual_current', 'read_actual_voltage', 'read_set_voltage',
            'read_current_limit'
        )

        # Setup dataset saver and live socket
        self.codenames = [self.channel_id + id_ for id_ in
                          ('_current', '_voltage', '_voltage_setpoint',
                           '_current_limit')]
        self.live_socket = LiveSocket(
            'H2O2_proactive_' + self.channel_id,
            self.codenames + [self.channel_id + '_accum_charge'],
            no_internal_data_pull_socket=True
        )
        self.live_socket.reset(self.codenames)
        self.live_socket.start()

        self.data_set_saver = DataSetSaver(
            credentials.measurements, credentials.xy_values,
            username=credentials.username, password=credentials.password
        )
        self.data_set_saver.start()

        # Done with init, send status
        self.send_status()

    def command(self, command, args_str):
        """Process commands from the GUI"""
        if command == 'stop':  # stop is sent on quit
            self.stop = True
        elif command == 'start':
            self.ok_to_start = True
        elif command == 'edit':
            # Parse the edit line, start by splitting up in step_num, field and value
            try:
                num_step, field_value = [arg.strip() for arg in args_str.split(' ')]
                field, value = [arg.strip() for arg in field_value.split('=')]
            except ValueError:
                message = ('Bad edit command, must be on the form:\n'
                           'edit step_num field=value')
                self.say(message, message_type='error')
                return

            # Try to get the correct step
            try:
                step = self.steps[int(num_step)]
            except (ValueError, IndexError):
                message = 'Unable to convert step number {} to integer or nu such step '\
                    'exists'
                self.say(message.format(num_step), message_type='error')
                return

            # Edit the value
            try:
                step.edit_value(field, value)
            except (ValueError, AttributeError) as exception:
                self.say(str(exception.args[0]), message_type='error')
                return

            # Finally send the new steps to the GUI
            self.send_steps()

    def send_status(self, update_dict=None):
        """Send the status to the GUI"""
        if update_dict:
            self.status.update(update_dict)
        self.message_queue.put(('status', self.status.copy()))

    def send_steps(self):
        """Send the steps list to the GUI"""
        steps = [(index == self.active_step, str(step))
                 for index, step in enumerate(self.steps)]
        self.message_queue.put(('steps', steps))

    def say(self, text, message_type='message'):
        """Send a ordinary text message to the gui"""
        self.message_queue.put((message_type, text))

    def run(self):
        """The MAIN run method"""
        # Wait for start
        while not self.ok_to_start:
            if self.stop:
                self.send_status({'status_field': 'Stopping'})
                self.power_supply_on_off(False)
                self.send_status({'status_field': 'Stopped'})
                return
            sleep(0.1)

        # Start
        self.send_status({'status_field': 'Starting'})
        self.setup_data_set_saver()

        # Run the MAIN measurement loop
        # (This is where most of the time is spent)
        self.main_measure()

        # Shutdown powersupply, livesocket and possibly server
        self.send_status({'status_field': 'Stopping'})
        self.stop_everything()
        self.send_status({'status_field': 'Stopped'})

        sleep(0.1)
        self.say("I have stopped")

    def setup_data_set_saver(self):
        """Setup the data set saver"""
        sql_time = CustomColumn(time(), 'FROM_UNIXTIME(%s)')
        for codename in self.codenames:
            metadata = {
                'time': sql_time, 'comment': self.config['comment'],
                'label': codename[3:], 'type': 1,
                'power_supply_channel': self.channel_id,
            }
            self.data_set_saver.add_measurement(codename, metadata)

    def main_measure(self):  # pylint: disable=too-many-locals
        """The main measurement loop"""
        self.send_status({'status_field': 'Running'})
        # Initial setup
        last_set_voltage = None
        last_set_max_current = None
        last_time = time()
        iteration_time = 'N/A'
        self.status['elapsed'] = 0.0
        accum_charge_codename = self.channel_id + '_accum_charge'
        self.status[accum_charge_codename] = 0.0
        current_id = self.channel_id + '_current'
        last_measured_current = 0.0

        self.say('I started on step 0')
        for self.active_step, current_step in enumerate(self.steps):
            self.send_status({'status_field': 'Running step {}'.format(self.active_step)})
            # Also give the step an instance name (for steps list)
            if self.active_step > 0:
                self.say('Switched to step: {}'.format(self.active_step))
            self.send_steps()
            current_step.start()

            # While the step hasn't completed yet
            while current_step.elapsed() < current_step.duration:
                # Check if we should stop
                if self.stop:
                    self.say('I have been asked to stop')
                    return

                iteration_start = now = time()
                # Calculate the time for one iteration and update times in status
                iteration_time = now - last_time

                last_time = now
                self.status.update({
                    'elapsed': current_step.elapsed(),
                    'remaining': current_step.remaining(),
                    'iteration_time': iteration_time,
                    'elapsed_total': sum(step.elapsed() for step in self.steps),
                    'remaining_total': sum(step.remaining() for step in self.steps),
                })

                # Ask the power supply to set a new voltage if needed
                required_voltage, required_max_current = current_step.values()
                if required_max_current != last_set_max_current:
                    self.send_command('set_current_limit', required_max_current)
                    last_set_max_current = required_max_current
                if required_voltage != last_set_voltage:
                    self.send_command('set_voltage', required_voltage)
                    last_set_voltage = required_voltage

                # Read value from the power supply
                self._read_values_from_power_supply()

                # Calculate, set and send accumulated charge
                charge_addition = \
                    (last_measured_current + self.status[current_id])\
                    / 2 * iteration_time
                last_measured_current = self.status[current_id]
                self.status[accum_charge_codename] += charge_addition
                point = (self.status['elapsed_total'], self.status[accum_charge_codename])
                self.live_socket.set_point(accum_charge_codename, point)

                # Send the new status
                self.send_status()

                # Calculate time to sleep to use the proper probe interval
                time_to_sleep = current_step.probe_interval - (time() - iteration_start)
                if time_to_sleep > 0:
                    sleep(time_to_sleep)

            # Stop the step(s own time keeping)
            current_step.stop()

        # For loop over steps ended
        self.send_status({'status_field': 'Program Complete'})
        self.say('Stepped program completed')


    def _read_values_from_power_supply(self):
        """Read all required values from the power supply (used only from run)"""
        for command, codename in zip(self.power_supply_commands, self.codenames):
            # Get a value for the current command
            value = self.send_command(command)

            # Set/save it on the live_socket, database and in the GUI
            point = (self.status['elapsed_total'], value)
            self.live_socket.set_point(codename, point)
            self.data_set_saver.save_point(codename, point)
            self.status[codename] = value

    def stop_everything(self):
        """Stop power supply and live socket"""
        self.power_supply_on_off(False)
        self.live_socket.stop()
        self.data_set_saver.stop()

    def power_supply_on_off(self, state, current_limit=0.0):
        """Set power supply on off"""
        # Set voltage to 0
        LOG.debug('Stopping everything. Set voltage to 0.0')
        self.send_command('set_voltage', 0.0)
        start = time()
        # Anything < 1.0 Volt is OK
        while self.send_command('read_actual_voltage') > 1.0:
            if time() - start > 60:
                LOG.error('Unable to set voltage to 0')
                if state:
                    raise RuntimeError('Unable to set voltage to 0')
                else:
                    self.say('Unable to set voltage to 0')
                    break
            sleep(1)

        # Set current limit
        self.send_command('set_current_limit', current_limit)
        read_current_limit = self.send_command('read_current_limit')
        if not isclose(read_current_limit, current_limit):
            raise RuntimeError('Unable to set current limit')

        # Set state
        self.send_command('output_status', state)
        read_state = self.send_command('read_output_status').strip() == '1'
        if not read_state is state:
            raise RuntimeError('Could not set output state')
Esempio n. 2
0
count = 0
while True:
    count += 1
    now = time()

    # Set sines
    data = {
        'sine1': sin(now),
        'sine2': sin(now + pi),
    }
    ls.set_batch_now(data)

    # Set cosines
    if time() - start > 6.28:
        start = time()
        ls.reset(['cosine1', 'cosine2'])
    x = time() - start
    data = {
        'cosine1': [x, cos(x)],
        'cosine2': [x, cos(x + pi)],
    }
    ls.set_batch(data)


    if count % 2 == 0:
        ls.set_point_now('status', 'OK')
    else:
        ls.set_point_now('status', 'even better')
    #if count % 10 == 0:
    #    print(sock.recv(1024))
    sleep(0.1)
Esempio n. 3
0
class VoltageCurrentProgram(Thread):  # pylint: disable=too-many-instance-attributes
    """The Voltage Current Program

    This program uses the generic stepped program runner as a GUI and
    serves information to that

    """
    def __init__(self, args):
        super(VoltageCurrentProgram, self).__init__()

        # Form channel_id e.g: EA1
        self.channel_id = args.power_supply + args.output

        ### Required by the stepped program runner
        # Accepted capabilities are: can_edit, can_play,
        # can_stop, can_quit
        self.capabilities = ('can_stop', 'can_start', 'can_edit')
        # Status fields (in order)
        self.status_fields = (
            # Status
            {
                'codename': 'status_field',
                'title': 'Status'
            },
            # Voltage
            {
                'codename': self.channel_id + '_voltage',
                'title': 'Voltage',
                'formatter': '{:.3f}',
                'unit': 'V'
            },
            # Voltage setpoint
            {
                'codename': self.channel_id + '_voltage_setpoint',
                'title': 'Voltage Setpoint',
                'formatter': '{:.3f}',
                'unit': 'V'
            },
            # Current
            {
                'codename': self.channel_id + '_current',
                'title': 'Current',
                'formatter': '{:.3f}',
                'unit': 'A'
            },
            # Current limit
            {
                'codename': self.channel_id + '_current_limit',
                'title': 'Current limit',
                'formatter': '{:.3f}',
                'unit': 'A'
            },
            # Charge
            {
                'codename': self.channel_id + '_accum_charge',
                'title': 'Accumulated charge',
                'formatter': '{:.3f}',
                'unit': 'C'
            },
            # Time elapsed (step)
            {
                'codename': 'elapsed',
                'title': 'Time elapsed (step)',
                'formatter': '{:.1f}',
                'unit': 's'
            },
            # Time remaining (step)
            {
                'codename': 'remaining',
                'title': 'Time remaining (step)',
                'formatter': '{:.1f}',
                'unit': 's'
            },
            # Time elapsed (total)
            {
                'codename': 'elapsed_total',
                'title': 'Time elapsed (total)',
                'formatter': '{:.1f}',
                'unit': 's'
            },
            # Time remaining (total)
            {
                'codename': 'remaining_total',
                'title': 'Time remaining (total)',
                'formatter': '{:.1f}',
                'unit': 's'
            },
            # Iteration time
            {
                'codename': 'iteration_time',
                'title': 'Iteration time',
                'formatter': '{:.2f}',
                'unit': 's'
            },
        )
        self.extra_capabilities = {
            'psuchannel': {
                'help_text': ('Used for simple PSU control when not on\n'
                              'a ramp. Possibly usages are:\n'
                              '    psuchannel voltage=1.23\n'
                              'which will set the voltage and\n'
                              '    psuchannel off\n'
                              'which will set the output off'),
                'completions': [
                    'psuchannel',
                    'psuchannel voltage=',
                    'psuchannel off',
                ]
            }
        }
        # Queue for GUI updates
        self.message_queue = Queue()
        # The GUI also looks in self.config, see below

        ### Normal program
        # Setup my program
        with open(path.join(THIS_DIR, args.program_file)) as file__:
            self.config, self.steps = parse_ramp(file__)
        # The GUI will look for keys: program_title in config
        self.say('Using power supply channel: ' + self.channel_id)
        self.say('Loaded with config:\n' + pformat(self.config))
        self.active_step = 0
        self.send_steps()

        # Add completions for the edits
        self._completion_additions = []
        for number, step in enumerate(self.steps):
            base = 'edit {} '.format(number)
            self._completion_additions.append(base)
            for field in sorted(step.fields):
                self._completion_additions.append('{}{}='.format(base, field))

        # Base for the status
        self.status = {'status_field': 'Initialized'}

        # General variables
        self.stop = False
        self.ok_to_start = False

        # Create a partial function with the output substitued in
        self.send_command = partial(
            _send_command,
            args.output,
            args.power_supply,
        )
        # Setup power supply
        self.power_supply_on_off(True, self.config['maxcurrent_start'])
        # Power supply commands, must match order with self.codenames
        self.power_supply_commands = ('read_actual_current',
                                      'read_actual_voltage',
                                      'read_set_voltage', 'read_current_limit')

        # Setup dataset saver and live socket
        self.codenames = [
            self.channel_id + id_
            for id_ in ('_current', '_voltage', '_voltage_setpoint',
                        '_current_limit')
        ]
        self.live_socket = LiveSocket('H2O2_proactive_' + self.channel_id,
                                      self.codenames +
                                      [self.channel_id + '_accum_charge'],
                                      no_internal_data_pull_socket=True)
        self.live_socket.reset(self.codenames)
        self.live_socket.start()

        self.data_set_saver = DataSetSaver(credentials.measurements,
                                           credentials.xy_values,
                                           username=credentials.username,
                                           password=credentials.password)
        self.data_set_saver.start()

        # Done with init, send status
        self.send_status()

    def command(self, command, args_str):
        """Process commands from the GUI"""
        if command == 'stop':  # stop is sent on quit
            self.stop = True
        elif command == 'start':
            self.ok_to_start = True
        elif command == 'edit':
            # Parse the edit line, start by splitting up in step_num, field and value
            try:
                num_step, field_value = [
                    arg.strip() for arg in args_str.split(' ')
                ]
                field, value = [arg.strip() for arg in field_value.split('=')]
            except ValueError:
                message = ('Bad edit command, must be on the form:\n'
                           'edit step_num field=value')
                self.say(message, message_type='error')
                return

            # Try to get the correct step
            try:
                step = self.steps[int(num_step)]
            except (ValueError, IndexError):
                message = 'Unable to convert step number {} to integer or nu such step '\
                    'exists'
                self.say(message.format(num_step), message_type='error')
                return

            # Edit the value
            try:
                step.edit_value(field, value)
            except (ValueError, AttributeError) as exception:
                self.say(str(exception.args[0]), message_type='error')
                return

            # Finally send the new steps to the GUI
            self.send_steps()
        elif command == "psuchannel":
            if self.ok_to_start and not self.stop:
                message = "Using psuchannel during ramp not allowed"
                self.say(message, message_type='error')
                return
            if args_str.startswith('voltage='):
                voltage_str = args_str.replace('voltage=', '')
                try:
                    voltage = float(voltage_str)
                except ValueError:
                    message = 'Invalid voltage for psuchannel {}'.format(
                        voltage_str)
                    self.say(message, message_type='error')
                    return
                self.send_command('set_voltage', voltage)
                self.say('PSU channel set to {}.\n'
                         'NOTE: The GUI will not update to show\n'
                         'this, only the actual PSU.'.format(voltage))
            elif args_str == 'off':
                self.power_supply_on_off(False)
                self.say('PSU channel set to off')
            else:
                message = 'Invalid argument. psuchannel can do "voltage=" and "off"'
                self.say(message, message_type='error')
                return

    def send_status(self, update_dict=None):
        """Send the status to the GUI"""
        if update_dict:
            self.status.update(update_dict)
        self.message_queue.put(('status', self.status.copy()))

    def send_steps(self):
        """Send the steps list to the GUI"""
        steps = [(index == self.active_step, str(step))
                 for index, step in enumerate(self.steps)]
        self.message_queue.put(('steps', steps))

    def say(self, text, message_type='message'):
        """Send a ordinary text message to the gui"""
        self.message_queue.put((message_type, text))

    def run(self):
        """The MAIN run method"""
        # Wait for start
        while not self.ok_to_start:
            if self.stop:
                self.send_status({'status_field': 'Stopped'})
                return
            sleep(0.1)

        # Start
        self.send_status({'status_field': 'Starting'})
        self.setup_data_set_saver()

        # Run the MAIN measurement loop
        # (This is where most of the time is spent)
        self.main_measure()

        # Shutdown powersupply, livesocket and possibly server
        self.send_status({'status_field': 'Stopping'})
        self.stop_everything()
        self.send_status({'status_field': 'Stopped'})

        sleep(0.1)
        self.say("I have stopped")

    def setup_data_set_saver(self):
        """Setup the data set saver"""
        sql_time = CustomColumn(time(), 'FROM_UNIXTIME(%s)')
        for codename in self.codenames:
            metadata = {
                'time': sql_time,
                'comment': self.config['comment'],
                'label': codename[3:],
                'type': 1,
                'power_supply_channel': self.channel_id,
            }
            self.data_set_saver.add_measurement(codename, metadata)

    def main_measure(self):  # pylint: disable=too-many-locals
        """The main measurement loop"""
        self.send_status({'status_field': 'Running'})
        # Initial setup
        last_set_voltage = None
        last_set_max_current = None
        last_time = time()
        iteration_time = 'N/A'
        self.status['elapsed'] = 0.0
        accum_charge_codename = self.channel_id + '_accum_charge'
        self.status[accum_charge_codename] = 0.0
        current_id = self.channel_id + '_current'
        last_measured_current = 0.0

        self.say('I started on step 0')
        for self.active_step, current_step in enumerate(self.steps):
            self.send_status(
                {'status_field': 'Running step {}'.format(self.active_step)})
            # Also give the step an instance name (for steps list)
            if self.active_step > 0:
                self.say('Switched to step: {}'.format(self.active_step))
            self.send_steps()
            current_step.start()

            # While the step hasn't completed yet
            while current_step.elapsed() < current_step.duration:
                # Check if we should stop
                if self.stop:
                    self.say('I have been asked to stop')
                    return

                iteration_start = now = time()
                # Calculate the time for one iteration and update times in status
                iteration_time = now - last_time

                last_time = now
                self.status.update({
                    'elapsed':
                    current_step.elapsed(),
                    'remaining':
                    current_step.remaining(),
                    'iteration_time':
                    iteration_time,
                    'elapsed_total':
                    sum(step.elapsed() for step in self.steps),
                    'remaining_total':
                    sum(step.remaining() for step in self.steps),
                })

                # Ask the power supply to set a new voltage if needed
                required_voltage, required_max_current = current_step.values()
                if required_max_current != last_set_max_current:
                    self.send_command('set_current_limit',
                                      required_max_current)
                    last_set_max_current = required_max_current
                if required_voltage != last_set_voltage:
                    self.send_command('set_voltage', required_voltage)
                    last_set_voltage = required_voltage

                # Read value from the power supply
                self._read_values_from_power_supply()

                # Calculate, set and send accumulated charge
                charge_addition = \
                    (last_measured_current + self.status[current_id])\
                    / 2 * iteration_time
                last_measured_current = self.status[current_id]
                self.status[accum_charge_codename] += charge_addition
                point = (self.status['elapsed_total'],
                         self.status[accum_charge_codename])
                self.live_socket.set_point(accum_charge_codename, point)

                # Send the new status
                self.send_status()

                # Calculate time to sleep to use the proper probe interval
                time_to_sleep = current_step.probe_interval - (time() -
                                                               iteration_start)
                if time_to_sleep > 0:
                    sleep(time_to_sleep)

            # Stop the step(s own time keeping)
            current_step.stop()

        # For loop over steps ended
        self.send_status({'status_field': 'Program Complete'})
        self.say('Stepped program completed')

    def _read_values_from_power_supply(self):
        """Read all required values from the power supply (used only from run)"""
        for command, codename in zip(self.power_supply_commands,
                                     self.codenames):
            # Get a value for the current command
            value = self.send_command(command)

            # Set/save it on the live_socket, database and in the GUI
            point = (self.status['elapsed_total'], value)
            self.live_socket.set_point(codename, point)
            self.data_set_saver.save_point(codename, point)
            self.status[codename] = value

    def stop_everything(self):
        """Stop power supply and live socket"""
        self.live_socket.stop()
        self.data_set_saver.stop()

    def power_supply_on_off(self, state, current_limit=None):
        """Set power supply on off"""
        LOG.debug('Stop power supply')

        # Set current limit
        if current_limit is not None:
            self.send_command('set_current_limit', current_limit)
            read_current_limit = self.send_command('read_current_limit')
            if not isclose(read_current_limit, current_limit):
                raise RuntimeError('Unable to set current limit')

        # Set state
        self.send_command('output_status', state)
        read_state = self.send_command('read_output_status').strip() == '1'
        if not read_state is state:
            raise RuntimeError('Could not set output state')
Esempio n. 4
0
count = 0
while True:
    count += 1
    now = time()

    # Set sines
    data = {
        'sine1': sin(now),
        'sine2': sin(now + pi),
    }
    ls.set_batch_now(data)

    # Set cosines
    if time() - start > 6.28:
        start = time()
        ls.reset(['cosine1', 'cosine2'])
    x = time() - start
    data = {
        'cosine1': [x, cos(x)],
        'cosine2': [x, cos(x + pi)],
    }
    ls.set_batch(data)

    if count % 2 == 0:
        ls.set_point_now('status', 'OK')
    else:
        ls.set_point_now('status', 'even better')
    #if count % 10 == 0:
    #    print(sock.recv(1024))
    sleep(0.1)