Example #1
0
    def __init__(self, *args):
        PluginBase.__init__(self, REDBrick, *args)

        try:
            self.session = REDSession(self.device,
                                      self.increase_error_count).create()
        except Exception as e:
            self.session = None

            label = QLabel('Could not create session:\n\n{0}'.format(e))
            label.setAlignment(Qt.AlignHCenter)

            layout = QVBoxLayout(self)
            layout.addStretch()
            layout.addWidget(label)
            layout.addStretch()

            return

        self.image_version = ImageVersion()
        self.label_version = None
        self.script_manager = ScriptManager(self.session)
        self.tabs = []

        self.setupUi(self)

        self.tab_widget.hide()

        for i in range(self.tab_widget.count()):
            tab = self.tab_widget.widget(i)

            tab.session = self.session
            tab.script_manager = self.script_manager
            tab.image_version = self.image_version

            self.tabs.append(tab)

        self.tab_widget.currentChanged.connect(self.tab_widget_current_changed)

        actions = []

        for param, name in enumerate([
                'Restart Brick Daemon', 'Reboot RED Brick',
                'Shut down RED Brick'
        ]):
            action = QAction(name, self)
            action.triggered.connect(
                functools.partial(self.perform_action, param))
            actions.append(action)

        self.set_actions(['System', actions])

        # FIXME: RED Brick doesn't do enumerate-connected callback correctly yet
        #        for Brick(let)s connected to it. Trigger a enumerate to pick up
        #        all devices connected to a RED Brick properly
        self.ipcon.enumerate()
Example #2
0
    def __init__(self, *args):
        PluginBase.__init__(self, REDBrick, *args)

        try:
            self.session = REDSession(self.device, self.increase_error_count).create()
        except Exception as e:
            self.session = None
            self.report_fatal_error('Could not create session', str(e), traceback.format_exc())
            return

        try:
            self.script_manager = ScriptManager(self.session)
        except Exception as e:
            self.session.expire()
            self.session = None
            self.report_fatal_error('Could not create script manager', str(e), traceback.format_exc())
            return

        self.image_version  = ImageVersion()
        self.label_version  = None
        self.tabs           = []

        self.setupUi(self)

        self.tab_widget.hide()

        for i in range(self.tab_widget.count()):
            tab = self.tab_widget.widget(i)

            tab.session        = self.session
            tab.script_manager = self.script_manager
            tab.image_version  = self.image_version

            self.tabs.append(tab)

        self.tab_widget.currentChanged.connect(self.tab_widget_current_changed)

        actions = []

        for param, name in enumerate(['Restart Brick Daemon', 'Reboot RED Brick', 'Shut down RED Brick', 'Update Tinkerforge Software']):
            action = QAction(name, self)
            action.triggered.connect(functools.partial(self.perform_action, param))
            actions.append(action)

        self.set_actions([(0, 'System', actions)])

        self.dialog_update_tinkerforge_software = None

        # FIXME: RED Brick doesn't do enumerate-connected callback correctly yet
        #        for Brick(let)s connected to it. Trigger a enumerate to pick up
        #        all devices connected to a RED Brick properly
        self.ipcon.enumerate()
Example #3
0
    def __init__(self, *args):
        PluginBase.__init__(self, REDBrick, *args)

        try:
            self.session = REDSession(self.device, self.increase_error_count).create()
        except Exception as e:
            self.session = None

            label = QLabel('Could not create session:\n\n{0}'.format(e))
            label.setAlignment(Qt.AlignHCenter)

            layout = QVBoxLayout(self)
            layout.addStretch()
            layout.addWidget(label)
            layout.addStretch()

            return

        self.image_version  = ImageVersion()
        self.label_version  = None
        self.script_manager = ScriptManager(self.session)
        self.tabs           = []

        self.setupUi(self)

        self.tab_widget.hide()

        for i in range(self.tab_widget.count()):
            tab = self.tab_widget.widget(i)

            tab.session        = self.session
            tab.script_manager = self.script_manager
            tab.image_version  = self.image_version

            self.tabs.append(tab)

        self.tab_widget.currentChanged.connect(self.tab_widget_current_changed)

        actions = []

        for param, name in enumerate(['Restart Brick Daemon', 'Reboot RED Brick', 'Shut down RED Brick']):
            action = QAction(name, self)
            action.triggered.connect(functools.partial(self.perform_action, param))
            actions.append(action)

        self.set_actions(['System', actions])

        # FIXME: RED Brick doesn't do enumerate-connected callback correctly yet
        #        for Brick(let)s connected to it. Trigger a enumerate to pick up
        #        all devices connected to a RED Brick properly
        self.ipcon.enumerate()
Example #4
0
    def __init__(self, *args):
        PluginBase.__init__(self, REDBrick, *args)

        try:
            self.session = REDSession(self.device, self.increase_error_count).create()
        except Exception as e:
            self.session = None

            label = QLabel('Could not create session:\n\n{0}'.format(e))
            label.setAlignment(Qt.AlignHCenter)

            layout = QVBoxLayout(self)
            layout.addStretch()
            layout.addWidget(label)
            layout.addStretch()

            return

        self.image_version  = ImageVersion()
        self.label_version  = None
        self.script_manager = ScriptManager(self.session)
        self.tabs           = []

        self.setupUi(self)

        self.tab_widget.hide()

        for i in range(self.tab_widget.count()):
            tab = self.tab_widget.widget(i)

            tab.session        = self.session
            tab.script_manager = self.script_manager
            tab.image_version  = self.image_version

            self.tabs.append(tab)

        self.tab_widget.currentChanged.connect(self.tab_widget_current_changed)

        # FIXME: RED Brick doesn't do enumerate-connected callback correctly yet
        #        for Brick(let)s connected to it. Trigger a enumerate to pick up
        #        all devices connected to a RED Brick properly
        self.ipcon.enumerate()
Example #5
0
class RED(PluginBase, Ui_RED):
    def __init__(self, *args):
        PluginBase.__init__(self, REDBrick, *args)

        try:
            self.session = REDSession(self.device, self.increase_error_count).create()
        except Exception as e:
            self.session = None

            label = QLabel('Could not create session:\n\n{0}'.format(e))
            label.setAlignment(Qt.AlignHCenter)

            layout = QVBoxLayout(self)
            layout.addStretch()
            layout.addWidget(label)
            layout.addStretch()

            return

        self.image_version  = ImageVersion()
        self.label_version  = None
        self.script_manager = ScriptManager(self.session)
        self.tabs           = []

        self.setupUi(self)

        self.tab_widget.hide()

        for i in range(self.tab_widget.count()):
            tab = self.tab_widget.widget(i)

            tab.session        = self.session
            tab.script_manager = self.script_manager
            tab.image_version  = self.image_version

            self.tabs.append(tab)

        self.tab_widget.currentChanged.connect(self.tab_widget_current_changed)

        # FIXME: RED Brick doesn't do enumerate-connected callback correctly yet
        #        for Brick(let)s connected to it. Trigger a enumerate to pick up
        #        all devices connected to a RED Brick properly
        self.ipcon.enumerate()

    def start(self):
        if self.session == None:
            return

        if self.image_version.string == None:
            # FIXME: this is should actually be sync to ensure that the image
            #        version is known before it'll be used
            def read_image_version_async(red_file):
                return red_file.open('/etc/tf_image_version',
                                     REDFile.FLAG_READ_ONLY | REDFile.FLAG_NON_BLOCKING,
                                     0, 0, 0).read(256).decode('utf-8').strip()

            def cb_success(image_version):
                if self.label_version != None:
                    self.label_version.setText(image_version)

                m = re.match(r'(\d+)\.(\d+)\s+\((.+)\)', image_version)

                if m != None:
                    try:
                        self.image_version.string = image_version
                        self.image_version.number = (int(m.group(1)), int(m.group(2)))
                        self.image_version.flavor = m.group(3)
                    except:
                        pass

                self.label_discovering.hide()
                self.tab_widget.show()
                self.tab_widget_current_changed(self.tab_widget.currentIndex())

            async_call(read_image_version_async, REDFile(self.session), cb_success, None)
        else:
            self.tab_widget_current_changed(self.tab_widget.currentIndex())

    def stop(self):
        if self.session == None:
            return

        for tab in self.tabs:
            tab.tab_off_focus()

    def destroy(self):
        if self.session == None:
            return

        for tab in self.tabs:
            tab.tab_destroy()

        self.script_manager.destroy()
        self.session.expire()

    def has_reset_device(self):
        return False # FIXME: will have reboot, instead of reset

    def reset_device(self):
        pass

    def has_drop_down(self):
        return ['System', 'Restart Brick Daemon', 'Reboot RED Brick', 'Shut down RED Brick']

    def drop_down_triggered(self, action):
        if self.session == None:
            return

        def cb(result):
            if result == None or result.stderr != '':
                pass # TODO: Error popup?

        t = action.text()
        param = -1

        if t == 'Restart Brick Daemon':
            param = 0
        elif t == 'Reboot RED Brick':
            param = 1
        elif t == 'Shut down RED Brick':
            param = 2

        if param != -1:
            self.script_manager.execute_script('restart_reboot_shutdown', cb, [str(param)])

    def has_custom_version(self, label_version_name, label_version):
        label_version_name.setText('Image Version: ')

        self.label_version = label_version

        if hasattr(self, 'image_version') and self.image_version.string != None:
            self.label_version.setText(self.image_version.string)

        return True

    def is_brick(self):
        return True

    def get_url_part(self):
        return 'red'

    @staticmethod
    def has_device_identifier(device_identifier):
        return device_identifier == BrickRED.DEVICE_IDENTIFIER

    def tab_widget_current_changed(self, index):
        for i, tab in enumerate(self.tabs):
            if i == index:
                tab.tab_on_focus()
            else:
                tab.tab_off_focus()
Example #6
0
    def __init__(self, *args):
        PluginBase.__init__(self, REDBrick, *args)

        try:
            self.session = REDSession(self.device,
                                      self.increase_error_count).create()
        except Exception as e:
            self.session = None
            self.report_fatal_error('Could not create session', str(e),
                                    traceback.format_exc())
            return

        try:
            self.script_manager = ScriptManager(self.session)
        except Exception as e:
            self.session.expire()
            self.session = None
            self.report_fatal_error('Could not create script manager', str(e),
                                    traceback.format_exc())
            return

        self.image_version = ImageVersion()
        self.label_version = None
        self.tabs = []

        self.setupUi(self)

        self.tab_widget.hide()

        for i in range(self.tab_widget.count()):
            tab = self.tab_widget.widget(i)

            tab.session = self.session
            tab.script_manager = self.script_manager
            tab.image_version = self.image_version

            self.tabs.append(tab)

        self.tab_widget.currentChanged.connect(self.tab_widget_current_changed)

        actions = []

        for param, name in enumerate([
                'Restart Brick Daemon', 'Reboot RED Brick',
                'Shut down RED Brick', 'Update Tinkerforge Software'
        ]):
            action = QAction(name, self)
            action.triggered.connect(
                functools.partial(self.perform_action, param))
            actions.append(action)

        self.set_actions([(0, 'System', actions)])

        self.dialog_update_tinkerforge_software = None

        # FIXME: RED Brick doesn't do enumerate-connected callback correctly yet
        #        for Brick(let)s connected to it. Trigger a enumerate to pick up
        #        all devices connected to a RED Brick properly
        self.ipcon.enumerate()

        self.extension_configs = []
        self.completed_counter = 0

        QTimer.singleShot(
            250,
            functools.partial(
                self.query_image_version,
                functools.partial(self.query_extensions,
                                  self.query_bindings_versions)))
Example #7
0
class RED(PluginBase, Ui_RED):
    def __init__(self, *args):
        PluginBase.__init__(self, REDBrick, *args)

        try:
            self.session = REDSession(self.device,
                                      self.increase_error_count).create()
        except Exception as e:
            self.session = None
            self.report_fatal_error('Could not create session', str(e),
                                    traceback.format_exc())
            return

        try:
            self.script_manager = ScriptManager(self.session)
        except Exception as e:
            self.session.expire()
            self.session = None
            self.report_fatal_error('Could not create script manager', str(e),
                                    traceback.format_exc())
            return

        self.image_version = ImageVersion()
        self.label_version = None
        self.tabs = []

        self.setupUi(self)

        self.tab_widget.hide()

        for i in range(self.tab_widget.count()):
            tab = self.tab_widget.widget(i)

            tab.session = self.session
            tab.script_manager = self.script_manager
            tab.image_version = self.image_version

            self.tabs.append(tab)

        self.tab_widget.currentChanged.connect(self.tab_widget_current_changed)

        actions = []

        for param, name in enumerate([
                'Restart Brick Daemon', 'Reboot RED Brick',
                'Shut down RED Brick', 'Update Tinkerforge Software'
        ]):
            action = QAction(name, self)
            action.triggered.connect(
                functools.partial(self.perform_action, param))
            actions.append(action)

        self.set_actions([(0, 'System', actions)])

        self.dialog_update_tinkerforge_software = None

        # FIXME: RED Brick doesn't do enumerate-connected callback correctly yet
        #        for Brick(let)s connected to it. Trigger a enumerate to pick up
        #        all devices connected to a RED Brick properly
        self.ipcon.enumerate()

        self.extension_configs = []
        self.completed_counter = 0

        QTimer.singleShot(
            250,
            functools.partial(
                self.query_image_version,
                functools.partial(self.query_extensions,
                                  self.query_bindings_versions)))

    def show_extension(self, ext):
        self.tab_widget.setCurrentWidget(self.tab_extension)
        self.tab_widget.currentWidget().tab_widget.setCurrentIndex(ext)

    def query_extensions(self, next_function=None):
        red_file = [None, None]

        def cb_file_read(ext, result):
            red_file[ext].release()
            self.completed_counter += 1

            if result.error == None:
                config = config_parser.parse(result.data.decode('utf-8'))

                try:
                    t = int(config['type'])
                except:
                    t = 0

                names = {
                    1: 'Chibi Extension',
                    2: 'RS485 Extension',
                    3: 'WIFI Extension',
                    4: 'Ethernet Extension',
                    5: 'WIFI Extension 2.0'
                }

                name = names[t] if t in names else 'Unknown'

                extension = 'ext' + str(ext)
                self.device_info.extensions[ext] = ExtensionInfo()
                self.device_info.extensions[ext].name = name
                self.device_info.extensions[
                    ext].extension_type = name + ' Extension'
                self.device_info.extensions[ext].position = extension
                self.device_info.extensions[ext].master_info = self.device_info

                self.extension_configs.append((ext, config))

                inventory.sync()

            if self.completed_counter == len(red_file):
                self.tab_extension.extension_query_finished(
                    self.extension_configs)

                if next_function != None:
                    QTimer.singleShot(250, next_function)

        def cb_file_open(ext, result):
            if not isinstance(result, REDFile):
                return

            red_file[ext] = result
            red_file[ext].read_async(red_file[ext].length,
                                     lambda x: cb_file_read(ext, x))

        def cb_file_open_error(ext):
            self.extension_configs.append((ext, None))
            self.completed_counter += 1

            if self.completed_counter == len(red_file):
                self.tab_extension.extension_query_finished(
                    self.extension_configs)

                if next_function != None:
                    QTimer.singleShot(250, next_function)

        red_file[0] = REDFile(self.session)
        async_call(red_file[0].open,
                   ("/tmp/extension_position_0.conf", REDFile.FLAG_READ_ONLY
                    | REDFile.FLAG_NON_BLOCKING, 0, 0, 0),
                   lambda x: cb_file_open(0, x), lambda: cb_file_open_error(0))

        red_file[1] = REDFile(self.session)
        async_call(red_file[1].open,
                   ("/tmp/extension_position_1.conf", REDFile.FLAG_READ_ONLY
                    | REDFile.FLAG_NON_BLOCKING, 0, 0, 0),
                   lambda x: cb_file_open(1, x), lambda: cb_file_open_error(1))

    def report_fatal_error(self, title, message, trace):
        trace = '<pre>{}</pre>'.format(
            html.escape(trace).replace('\n', '<br>'))

        label_title = QLabel(title)

        font = label_title.font()
        font.setBold(True)
        label_title.setFont(font)

        label_message = QLabel('Error: ' + message)
        browser_trace = QTextBrowser()
        browser_trace.setHtml(trace)

        layout = QVBoxLayout(self)
        layout.addWidget(label_title)
        layout.addWidget(label_message)
        layout.addWidget(browser_trace)

    def query_image_version(self, next_function=None):
        def read_image_version_async(red_file):
            return red_file.open(
                '/etc/tf_image_version',
                REDFile.FLAG_READ_ONLY | REDFile.FLAG_NON_BLOCKING, 0, 0,
                0).read(256).decode('utf-8').strip()

        def cb_success(image_version):
            if self.label_version != None:
                self.label_version.setText(image_version)

            m = re.match(r'(\d+)\.(\d+)\s+\((.+)\)', image_version)

            if m != None:
                try:
                    self.image_version.number = (int(m.group(1)),
                                                 int(m.group(2)))
                    self.image_version.flavor = m.group(3)
                    self.image_version.string = image_version  # set this last, because it is used as validity check
                except:
                    self.label_discovering.setText(
                        'Error: Could not parse Image Version: {0}'.format(
                            image_version))
                else:
                    self.widget_discovering.hide()
                    self.tab_widget.show()

                    if self.plugin_state == PluginBase.PLUGIN_STATE_RUNNING:
                        self.tab_widget_current_changed(
                            self.tab_widget.currentIndex())

                    self.device_info.firmware_version_installed = self.image_version.number + (
                        0, )
                    inventory.sync()
            else:
                self.label_discovering.setText(
                    'Error: Could not parse Image Version: {0}'.format(
                        image_version))

            if next_function != None:
                QTimer.singleShot(250, next_function)

        self.label_discovering.setText('Discovering Image Version...')
        self.widget_discovering.show()

        async_call(read_image_version_async, REDFile(self.session), cb_success,
                   None)

    def bindings_version_success(self, result):
        okay, message = check_script_result(result)

        if not okay:
            get_main_window().show_status(
                'Failed to query RED Brick bindings versions: ' + message,
                message_id='red_bindings_version_success_error')
            return

        try:
            versions = json.loads(result.stdout)
        except Exception as e:
            get_main_window().show_status(
                'Failed to parse RED Brick bindings versions as JSON: ' +
                str(e),
                message_id='red_bindings_version_success_error')
            return

        get_main_window().hide_status('red_bindings_version_success_error')

        self.device_info.bindings_infos = []

        for url_part, version in versions['bindings'].items():
            bindings_info = BindingsInfo()
            bindings_info.name = get_bindings_name(url_part)
            bindings_info.url_part = url_part
            bindings_info.firmware_version_installed = tuple(
                int(i) for i in version.split('.'))

            bindings_info.update_firmware_version_latest()

            self.device_info.bindings_infos.append(bindings_info)

        brickv_info = ToolInfo()
        brickv_info.name = 'Brick Viewer'
        brickv_info.url_part = 'brickv'
        brickv_info.firmware_version_installed = tuple(
            int(i) for i in versions['brickv'].split('.'))

        brickv_info.update_firmware_version_latest()

        self.device_info.brickv_info = brickv_info

        inventory.sync()

    def query_bindings_versions(self):
        self.script_manager.execute_script(
            'update_tf_software_get_installed_versions',
            self.bindings_version_success)

    def show_bindings_update(self, bindings=True, brickv=False):
        button_text = ""
        tool_tip_text = ""

        if bindings and brickv:
            button_text = "Update Bindings and Brick Viewer for RED Brick"
            tool_tip_text = "Binding and Brick Viewer Updates for RED Brick available"
        elif bindings:
            button_text = "Update Bindings for RED Brick"
            tool_tip_text = "Binding Updates for RED Brick available"
        elif brickv:
            button_text = "Update Brick Viewer for RED Brick"
            tool_tip_text = "Brick Viewer Update for RED Brick available"

        # Show "normal" update button and customize it
        self.show_update()

        self.device_info.tab_window.button_update.setText(button_text)
        self.device_info.tab_window.button_update.clicked.disconnect()
        self.device_info.tab_window.button_update.clicked.connect(
            lambda: self.perform_action(3))

        self.device_info.tab_window.show_update_tab_button(
            tool_tip_text, lambda: self.perform_action(3))

    def show_image_update(self):
        self.show_update()

        # Maybe a bindings update was shown the last time,
        # revert changes to the update buttons.
        self.device_info.tab_window.button_update.setText(
            "Update Image for RED Brick")
        self.device_info.tab_window.button_update.clicked.disconnect()
        self.device_info.tab_window.button_update.clicked.connect(
            get_main_window().show_red_brick_update)

        self.device_info.tab_window.show_update_tab_button(
            'Image Update for RED Brick available',
            lambda: get_main_window().show_red_brick_update())

    # Overrides PluginBase.device_info_changed
    def device_info_changed(self, uid):
        if uid != self.device_info.uid:
            return

        if self.device_info.tab_window is None:
            return

        # The rationale here is the same as with the Master Brick:
        # Prioritize bindings (and brickv) updates over image updates,
        # as they are easier to install. Also when they are updated,
        # device_info_changed is triggered again, so the image update
        # will be shown afterwards.
        # A special case is the update to image 1.14: This adds
        # support for the Brick Viewer version 2.4.0 and should therefore
        # be prioritized.

        brickv_update = (self.device_info.brickv_info.firmware_version_installed != (0, 0, 0)) \
                        and (self.device_info.brickv_info.firmware_version_installed < self.device_info.brickv_info.firmware_version_latest) \
                        and self.device_info.firmware_version_installed >= (1, 14, 0) # Don't show brickv update if the image does not contain Qt5

        bindings_update = any(
            info.firmware_version_installed < info.firmware_version_latest
            for info in self.device_info.bindings_infos)

        image_update = self.device_info.firmware_version_installed != (0, 0, 0) \
                       and self.device_info.firmware_version_installed < self.device_info.firmware_version_latest

        if image_update and self.device_info.firmware_version_installed < (
                1, 14, 0):
            self.show_image_update()
        elif brickv_update or bindings_update:
            self.show_bindings_update(bindings=bindings_update,
                                      brickv=brickv_update)
        elif image_update:
            self.show_image_update()
        else:
            self.hide_update()

    def start(self):
        if self.session == None:
            return

        if self.image_version.string != None:
            self.tab_widget_current_changed(self.tab_widget.currentIndex())

    def stop(self):
        if self.session == None:
            return

        for tab in self.tabs:
            tab.tab_off_focus()

    def destroy(self):
        if self.session == None:
            return

        if self.dialog_update_tinkerforge_software is not None:
            self.dialog_update_tinkerforge_software.close()

        for tab in self.tabs:
            tab.tab_destroy()

        self.script_manager.destroy()
        self.session.expire()

    def has_custom_version(self, label_version_name, label_version):
        label_version_name.setText('Image Version:')

        self.label_version = label_version

        if hasattr(self,
                   'image_version') and self.image_version.string != None:
            self.label_version.setText(self.image_version.string)

        return True

    @staticmethod
    def has_device_identifier(device_identifier):
        return device_identifier == BrickRED.DEVICE_IDENTIFIER

    def tab_widget_current_changed(self, index):
        for i, tab in enumerate(self.tabs):
            if i == index:
                tab.tab_on_focus()
            else:
                tab.tab_off_focus()

    def perform_action(self, param):
        if self.session == None or self.image_version.string == None:
            return

        # Restart Brick Daemon
        if param == 0:
            self.script_manager.execute_script('restart_brickd', None)

        # Reboot or shutdown RED Brick
        elif param == 1 or param == 2:

            def cb_success(result):
                okay, message = check_script_result(result)

                if not okay:
                    op = 'reboot' if param == 1 else 'shutdown'
                    QMessageBox.critical(get_main_window(),
                                         'Failed to {} RED Brick'.format(op),
                                         message)

            self.script_manager.execute_script(
                'restart_reboot_shutdown_systemd', cb_success, [str(param)])

        # Update Tinkerforge software
        elif param == 3:
            self.dialog_update_tinkerforge_software = REDUpdateTinkerforgeSoftwareDialog(
                self, self.session, self.script_manager)
            self.dialog_update_tinkerforge_software.exec_()
            self.dialog_update_tinkerforge_software = None

    def get_health_metric_names(self):
        return []

    def get_health_metric_values(self):
        return {}
Example #8
0
class RED(PluginBase, Ui_RED):
    def __init__(self, *args):
        PluginBase.__init__(self, REDBrick, *args)

        try:
            self.session = REDSession(self.device, self.increase_error_count).create()
        except Exception as e:
            self.session = None
            self.report_fatal_error('Could not create session', str(e), traceback.format_exc())
            return

        try:
            self.script_manager = ScriptManager(self.session)
        except Exception as e:
            self.session.expire()
            self.session = None
            self.report_fatal_error('Could not create script manager', str(e), traceback.format_exc())
            return

        self.image_version  = ImageVersion()
        self.label_version  = None
        self.tabs           = []

        self.setupUi(self)

        self.tab_widget.hide()

        for i in range(self.tab_widget.count()):
            tab = self.tab_widget.widget(i)

            tab.session        = self.session
            tab.script_manager = self.script_manager
            tab.image_version  = self.image_version

            self.tabs.append(tab)

        self.tab_widget.currentChanged.connect(self.tab_widget_current_changed)

        actions = []

        for param, name in enumerate(['Restart Brick Daemon', 'Reboot RED Brick', 'Shut down RED Brick', 'Update Tinkerforge Software']):
            action = QAction(name, self)
            action.triggered.connect(functools.partial(self.perform_action, param))
            actions.append(action)

        self.set_actions([(0, 'System', actions)])

        self.dialog_update_tinkerforge_software = None

        # FIXME: RED Brick doesn't do enumerate-connected callback correctly yet
        #        for Brick(let)s connected to it. Trigger a enumerate to pick up
        #        all devices connected to a RED Brick properly
        self.ipcon.enumerate()

    def report_fatal_error(self, title, message, trace):
        trace = '<pre>{}</pre>'.format(html.escape(trace).replace('\n', '<br>'))

        label_title = QLabel(title)

        font = label_title.font()
        font.setBold(True)
        label_title.setFont(font)

        label_message = QLabel('Error: ' + message)
        browser_trace = QTextBrowser()
        browser_trace.setHtml(trace)

        layout = QVBoxLayout(self)
        layout.addWidget(label_title)
        layout.addWidget(label_message)
        layout.addWidget(browser_trace)

    def get_image_version_async(self):
        if self.session == None:
            return

        def read_image_version_async(red_file):
            return red_file.open('/etc/tf_image_version',
                                 REDFile.FLAG_READ_ONLY | REDFile.FLAG_NON_BLOCKING,
                                 0, 0, 0).read(256).decode('utf-8').strip()

        def cb_success(image_version):
            if self.label_version != None:
                self.label_version.setText(image_version)

            m = re.match(r'(\d+)\.(\d+)\s+\((.+)\)', image_version)

            if m != None:
                try:
                    self.image_version.number = (int(m.group(1)), int(m.group(2)))
                    self.image_version.flavor = m.group(3)
                    self.image_version.string = image_version # set this last, because it is used as validity check
                except:
                    self.label_discovering.setText('Error: Could not parse Image Version: {0}'.format(image_version))
                else:
                    self.widget_discovering.hide()
                    self.tab_widget.show()
                    self.tab_widget_current_changed(self.tab_widget.currentIndex())
                    self.device_info.firmware_version_installed = self.image_version.number + (0, )
                    brickv.infos.update_info(self.device_info.uid)
            else:
                self.label_discovering.setText('Error: Could not parse Image Version: {0}'.format(image_version))

        self.label_discovering.setText('Discovering Image Version...')
        self.widget_discovering.show()
        async_call(read_image_version_async, REDFile(self.session), cb_success, None)

    def bindings_version_success(self, result):
        okay, message = check_script_result(result)
        if not okay:
            get_main_window().show_status('Failed to query RED Brick bindings versions: '+ message, message_id='red_bindings_version_success_error')
            return

        try:
            versions = json.loads(result.stdout)
        except Exception as e:
            get_main_window().show_status('Failed to parse RED Brick bindings versions as JSON: '+ str(e), message_id='red_bindings_version_success_error')
            return

        get_main_window().hide_status('red_bindings_version_success_error')

        self.device_info.bindings_infos = []
        for url_part, version in versions['bindings'].items():
            info = brickv.infos.BindingInfo()
            info.name = brickv.infos.get_bindings_name(url_part)
            info.url_part = url_part
            info.firmware_version_installed = tuple(int(i) for i in version.split('.'))

            brickv.infos.add_latest_fw(info)
            self.device_info.bindings_infos.append(info)

        red_brickv_info = brickv.infos.ToolInfo()
        red_brickv_info.name = 'Brick Viewer'
        red_brickv_info.url_part = 'brickv'
        red_brickv_info.firmware_version_installed = tuple(int(i) for i in versions['brickv'].split('.'))

        brickv.infos.add_latest_fw(red_brickv_info)
        self.device_info.brickv_info = red_brickv_info

        brickv.infos.get_infos_changed_signal().emit(self.device_info.uid)

    def get_bindings_versions_async(self):
        if self.session == None:
            return

        self.script_manager.execute_script('update_tf_software_get_installed_versions',
                                           self.bindings_version_success)

    def show_bindings_update(self, bindings=True, brickv=False):
        button_text = ""
        tool_tip_text = ""

        if bindings and brickv:
            button_text = "Update Bindings and Brick Viewer for RED Brick"
            tool_tip_text = "Binding and Brick Viewer Updates for RED Brick available"
        elif bindings:
            button_text = "Update Bindings for RED Brick"
            tool_tip_text = "Binding Updates for RED Brick available"
        elif brickv:
            button_text = "Update Brick Viewer for RED Brick"
            tool_tip_text = "Brick Viewer Update for RED Brick available"

        # Show "normal" update button and customize it
        self.show_update()

        self.device_info.tab_window.button_update.setText(button_text)
        self.device_info.tab_window.button_update.clicked.disconnect()
        self.device_info.tab_window.button_update.clicked.connect(lambda: self.perform_action(3))

        self.update_tab_button.setToolTip(tool_tip_text)
        self.update_tab_button.clicked.disconnect()
        self.update_tab_button.clicked.connect(lambda: self.perform_action(3))

    def show_image_update(self):
        self.show_update()

        # Maybe a bindings update was shown the last time,
        # revert changes to the update buttons.
        self.device_info.tab_window.button_update.setText("Update Image for RED Brick")
        self.device_info.tab_window.button_update.clicked.disconnect()
        self.device_info.tab_window.button_update.clicked.connect(get_main_window().show_red_brick_update)

        self.update_tab_button.setToolTip('Image Update for RED Brick available')
        self.update_tab_button.clicked.disconnect()
        self.update_tab_button.clicked.connect(lambda: get_main_window().show_red_brick_update())

    # Overrides PluginBase.device_infos_changed
    def device_infos_changed(self, uid):
        if uid != self.device_info.uid:
            return

        if self.device_info.tab_window is None:
            return

        # The rationale here is the same as with the Master Brick:
        # Prioritize bindings (and brickv) updates over image updates,
        # as they are easier to install. Also when they are updated,
        # device_infos_changed is triggered again, so the image update
        # will be shown afterwards.
        # A special case is the update to image 1.14: This adds
        # support for the Brick Viewer version 2.4.0 and should therefore
        # be prioritized.

        brickv_update = (self.device_info.brickv_info.firmware_version_installed != (0, 0, 0)) \
                        and (self.device_info.brickv_info.firmware_version_installed < self.device_info.brickv_info.firmware_version_latest) \
                        and self.device_info.firmware_version_installed >= (1, 14, 0) # Don't show brickv update if the image does not contain Qt5

        bindings_update = any(info.firmware_version_installed < info.firmware_version_latest for info in self.device_info.bindings_infos)

        image_update = self.device_info.firmware_version_installed != (0, 0, 0) \
                       and self.device_info.firmware_version_installed < self.device_info.firmware_version_latest

        if image_update and self.device_info.firmware_version_installed < (1, 14, 0):
            self.show_image_update()
        elif brickv_update or bindings_update:
            self.show_bindings_update(bindings=bindings_update, brickv=brickv_update)
        elif image_update:
            self.show_image_update()
        else:
            self.hide_update()

    def start(self):
        if self.session == None:
            return

        if self.image_version.string == None:
            # FIXME: this is should actually be sync to ensure that the image
            #        version is known before it'll be used
            self.get_image_version_async()
        else:
            self.tab_widget_current_changed(self.tab_widget.currentIndex())

    def stop(self):
        if self.session == None:
            return

        for tab in self.tabs:
            tab.tab_off_focus()

    def destroy(self):
        if self.session == None:
            return

        if self.dialog_update_tinkerforge_software is not None:
            self.dialog_update_tinkerforge_software.close()

        for tab in self.tabs:
            tab.tab_destroy()

        self.script_manager.destroy()
        self.session.expire()

    def has_custom_version(self, label_version_name, label_version):
        label_version_name.setText('Image Version:')

        self.label_version = label_version

        if hasattr(self, 'image_version') and self.image_version.string != None:
            self.label_version.setText(self.image_version.string)

        return True

    @staticmethod
    def has_device_identifier(device_identifier):
        return device_identifier == BrickRED.DEVICE_IDENTIFIER

    def tab_widget_current_changed(self, index):
        for i, tab in enumerate(self.tabs):
            if i == index:
                tab.tab_on_focus()
            else:
                tab.tab_off_focus()

    def perform_action(self, param):
        if self.session == None or self.image_version.string == None:
            return

        # Restart Brick Daemon
        if param == 0:
            self.script_manager.execute_script('restart_brickd', None)

        # Reboot or shutdown RED Brick
        elif param == 1 or param == 2:
            def cb_success(result):
                okay, message = check_script_result(result)

                if not okay:
                    op = 'reboot' if param == 1 else 'shutdown'
                    QMessageBox.critical(get_main_window(), 'Failed to {} RED Brick'.format(op), message)

            self.script_manager.execute_script('restart_reboot_shutdown_systemd', cb_success, [str(param)])

        # Update Tinkerforge software
        elif param == 3:
            self.dialog_update_tinkerforge_software = REDUpdateTinkerforgeSoftwareDialog(self, self.session, self.script_manager)
            self.dialog_update_tinkerforge_software.exec_()
            self.dialog_update_tinkerforge_software = None
Example #9
0
class RED(PluginBase, Ui_RED):
    def __init__(self, *args):
        PluginBase.__init__(self, REDBrick, *args)

        try:
            self.session = REDSession(self.device,
                                      self.increase_error_count).create()
        except Exception as e:
            self.session = None

            label = QLabel('Could not create session:\n\n{0}'.format(e))
            label.setAlignment(Qt.AlignHCenter)

            layout = QVBoxLayout(self)
            layout.addStretch()
            layout.addWidget(label)
            layout.addStretch()

            return

        self.image_version = ImageVersion()
        self.label_version = None
        self.script_manager = ScriptManager(self.session)
        self.tabs = []

        self.setupUi(self)

        self.tab_widget.hide()

        for i in range(self.tab_widget.count()):
            tab = self.tab_widget.widget(i)

            tab.session = self.session
            tab.script_manager = self.script_manager
            tab.image_version = self.image_version

            self.tabs.append(tab)

        self.tab_widget.currentChanged.connect(self.tab_widget_current_changed)

        actions = []

        for param, name in enumerate([
                'Restart Brick Daemon', 'Reboot RED Brick',
                'Shut down RED Brick'
        ]):
            action = QAction(name, self)
            action.triggered.connect(
                functools.partial(self.perform_action, param))
            actions.append(action)

        self.set_actions(['System', actions])

        # FIXME: RED Brick doesn't do enumerate-connected callback correctly yet
        #        for Brick(let)s connected to it. Trigger a enumerate to pick up
        #        all devices connected to a RED Brick properly
        self.ipcon.enumerate()

    def start(self):
        if self.session == None:
            return

        if self.image_version.string == None:
            # FIXME: this is should actually be sync to ensure that the image
            #        version is known before it'll be used
            def read_image_version_async(red_file):
                return red_file.open(
                    '/etc/tf_image_version',
                    REDFile.FLAG_READ_ONLY | REDFile.FLAG_NON_BLOCKING, 0, 0,
                    0).read(256).decode('utf-8').strip()

            def cb_success(image_version):
                if self.label_version != None:
                    self.label_version.setText(image_version)

                m = re.match(r'(\d+)\.(\d+)\s+\((.+)\)', image_version)

                if m != None:
                    try:
                        self.image_version.string = image_version
                        self.image_version.number = (int(m.group(1)),
                                                     int(m.group(2)))
                        self.image_version.flavor = m.group(3)
                    except:
                        pass

                self.label_discovering.hide()
                self.tab_widget.show()
                self.tab_widget_current_changed(self.tab_widget.currentIndex())

            async_call(read_image_version_async, REDFile(self.session),
                       cb_success, None)
        else:
            self.tab_widget_current_changed(self.tab_widget.currentIndex())

    def stop(self):
        if self.session == None:
            return

        for tab in self.tabs:
            tab.tab_off_focus()

    def destroy(self):
        if self.session == None:
            return

        for tab in self.tabs:
            tab.tab_destroy()

        self.script_manager.destroy()
        self.session.expire()

    def has_custom_version(self, label_version_name, label_version):
        label_version_name.setText('Image Version: ')

        self.label_version = label_version

        if hasattr(self,
                   'image_version') and self.image_version.string != None:
            self.label_version.setText(self.image_version.string)

        return True

    def get_url_part(self):
        return 'red'

    @staticmethod
    def has_device_identifier(device_identifier):
        return device_identifier == BrickRED.DEVICE_IDENTIFIER

    def tab_widget_current_changed(self, index):
        for i, tab in enumerate(self.tabs):
            if i == index:
                tab.tab_on_focus()
            else:
                tab.tab_off_focus()

    def perform_action(self, param):
        if self.session == None:
            return

        def cb(result):
            if result == None or result.stderr != '':
                pass  # TODO: Error popup?

        self.script_manager.execute_script('restart_reboot_shutdown', cb,
                                           [str(param)])