← Back to team overview

openlp-core team mailing list archive

Re: [Merge] lp:~alisonken1/openlp/pjlink2g into lp:openlp

 

Review: Needs Fixing

Some comments on the code as we are improving it !

Diff comments:

> 
> === renamed file 'openlp/core/lib/projector/pjlink1.py' => 'openlp/core/lib/projector/pjlink.py'
> --- openlp/core/lib/projector/pjlink1.py	2017-07-20 15:31:50 +0000
> +++ openlp/core/lib/projector/pjlink.py	2017-08-06 07:33:29 +0000
> @@ -69,7 +72,407 @@
>  PJLINK_SUFFIX = CR
>  
>  
> -class PJLink(QtNetwork.QTcpSocket):
> +class PJLinkCommands(object):
> +    """
> +    Process replies from PJLink projector.
> +    """
> +
> +    def __init__(self, *args, **kwargs):
> +        """
> +        Setup for the process commands
> +        """
> +        log.debug('PJlinkCommands(args={args} kwargs={kwargs})'.format(args=args, kwargs=kwargs))
> +        super().__init__()
> +        # Map command to function
> +        self.pjlink_functions = {
> +            'AVMT': self.process_avmt,
> +            'CLSS': self.process_clss,
> +            'ERST': self.process_erst,
> +            'INFO': self.process_info,
> +            'INF1': self.process_inf1,
> +            'INF2': self.process_inf2,
> +            'INPT': self.process_inpt,
> +            'INST': self.process_inst,
> +            'LAMP': self.process_lamp,
> +            'NAME': self.process_name,
> +            'PJLINK': self.check_login,
> +            'POWR': self.process_powr,
> +            'SNUM': self.process_snum,
> +            'SVER': self.process_sver,
> +            'RFIL': self.process_rfil,
> +            'RLMP': self.process_rlmp
> +        }
> +
> +    def reset_information(self):
> +        """
> +        Initialize instance variables. Also used to reset projector-specific information to default.
> +        """
> +        log.debug('({ip}) reset_information() connect status is {state}'.format(ip=self.ip, state=self.state()))
> +        self.fan = None  # ERST
> +        self.filter_time = None  # FILT
> +        self.lamp = None  # LAMP
> +        self.mac_adx_received = None  # ACKN
> +        self.manufacturer = None  # INF1
> +        self.model = None  # INF2
> +        self.model_filter = None  # RFIL
> +        self.model_lamp = None  # RLMP
> +        self.mute = None  # AVMT
> +        self.other_info = None  # INFO
> +        self.pjlink_class = PJLINK_CLASS  # Default class
> +        self.pjlink_name = None  # NAME
> +        self.power = S_OFF  # POWR
> +        self.serial_no = None  # SNUM
> +        self.serial_no_received = None
> +        self.sw_version = None  # SVER
> +        self.sw_version_received = None
> +        self.shutter = None  # AVMT
> +        self.source_available = None  # INST
> +        self.source = None  # INPT
> +        # These should be part of PJLink() class, but set here for convenience
> +        if hasattr(self, 'timer'):
> +            log.debug('({ip}): Calling timer.stop()'.format(ip=self.ip))
> +            self.timer.stop()
> +        if hasattr(self, 'socket_timer'):
> +            log.debug('({ip}): Calling socket_timer.stop()'.format(ip=self.ip))
> +            self.socket_timer.stop()
> +        self.send_busy = False
> +        self.send_queue = []
> +
> +    def process_command(self, cmd, data):
> +        """
> +        Verifies any return error code. Calls the appropriate command handler.
> +
> +        :param cmd: Command to process
> +        :param data: Data being processed
> +        """
> +        log.debug('({ip}) Processing command "{cmd}" with data "{data}"'.format(ip=self.ip,
> +                                                                                cmd=cmd,
> +                                                                                data=data))
> +        # Check if we have a future command not available yet
> +        if cmd not in PJLINK_VALID_CMD:
> +            log.error("({ip}) Ignoring command='{cmd}' (Invalid/Unknown)".format(ip=self.ip, cmd=cmd))
> +            return
> +        elif cmd not in self.pjlink_functions:
> +            log.warn("({ip}) Unable to process command='{cmd}' (Future option)".format(ip=self.ip, cmd=cmd))
> +            return
> +        elif data in PJLINK_ERRORS:
> +            # Oops - projector error
> +            log.error('({ip}) Projector returned error "{data}"'.format(ip=self.ip, data=data))
> +            if data.upper() == 'ERRA':

Would not _data = data.upper() be better then test _data.

> +                # Authentication error
> +                self.disconnect_from_host()
> +                self.change_status(E_AUTHENTICATION)
> +                log.debug('({ip}) emitting projectorAuthentication() signal'.format(ip=self.ip))
> +                self.projectorAuthentication.emit(self.name)
> +            elif data.upper() == 'ERR1':
> +                # Undefined command
> +                self.change_status(E_UNDEFINED, '{error}: "{data}"'.format(error=ERROR_MSG[E_UNDEFINED],
> +                                                                           data=cmd))
> +            elif data.upper() == 'ERR2':
> +                # Invalid parameter
> +                self.change_status(E_PARAMETER)
> +            elif data.upper() == 'ERR3':
> +                # Projector busy
> +                self.change_status(E_UNAVAILABLE)
> +            elif data.upper() == 'ERR4':
> +                # Projector/display error
> +                self.change_status(E_PROJECTOR)
> +            self.receive_data_signal()
> +            return
> +        # Command succeeded - no extra information
> +        elif data.upper() == 'OK':
> +            log.debug('({ip}) Command returned OK'.format(ip=self.ip))
> +            # A command returned successfully
> +            self.receive_data_signal()
> +            return
> +        # Command checks already passed
> +        log.debug('({ip}) Calling function for {cmd}'.format(ip=self.ip, cmd=cmd))
> +        self.receive_data_signal()
> +        self.pjlink_functions[cmd](data)
> +
> +    def process_avmt(self, data):
> +        """
> +        Process shutter and speaker status. See PJLink specification for format.
> +        Update self.mute (audio) and self.shutter (video shutter).
> +
> +        :param data: Shutter and audio status
> +        """
> +        shutter = self.shutter
> +        mute = self.mute

Could this not be a dictionary look up?

> +        if data == '11':
> +            shutter = True
> +            mute = False
> +        elif data == '21':
> +            shutter = False
> +            mute = True
> +        elif data == '30':
> +            shutter = False
> +            mute = False
> +        elif data == '31':
> +            shutter = True
> +            mute = True
> +        else:
> +            log.warning('({ip}) Unknown shutter response: {data}'.format(ip=self.ip, data=data))
> +        update_icons = shutter != self.shutter

Need Asprin can we have a comment, please!

> +        update_icons = update_icons or mute != self.mute
> +        self.shutter = shutter
> +        self.mute = mute
> +        if update_icons:
> +            self.projectorUpdateIcons.emit()
> +        return
> +
> +    def process_clss(self, data):
> +        """
> +        PJLink class that this projector supports. See PJLink specification for format.
> +        Updates self.class.
> +
> +        :param data: Class that projector supports.
> +        """
> +        # bug 1550891: Projector returns non-standard class response:
> +        #            : Expected: '%1CLSS=1'
> +        #            : Received: '%1CLSS=Class 1'  (Optoma)
> +        #            : Received: '%1CLSS=Version1'  (BenQ)
> +        if len(data) > 1:
> +            log.warn("({ip}) Non-standard CLSS reply: '{data}'".format(ip=self.ip, data=data))
> +            # Due to stupid projectors not following standards (Optoma, BenQ comes to mind),
> +            # AND the different responses that can be received, the semi-permanent way to
> +            # fix the class reply is to just remove all non-digit characters.
> +            try:
> +                clss = re.findall('\d', data)[0]  # Should only be the first match
> +            except IndexError:
> +                log.error("({ip}) No numbers found in class version reply - defaulting to class '1'".format(ip=self.ip))
> +                clss = '1'
> +        elif not data.isdigit():
> +            log.error("({ip}) NAN class version reply - defaulting to class '1'".format(ip=self.ip))
> +            clss = '1'
> +        else:
> +            clss = data
> +        self.pjlink_class = clss
> +        log.debug('({ip}) Setting pjlink_class for this projector to "{data}"'.format(ip=self.ip,
> +                                                                                      data=self.pjlink_class))
> +        return
> +
> +    def process_erst(self, data):
> +        """
> +        Error status. See PJLink Specifications for format.
> +        Updates self.projector_errors
> +
> +\        :param data: Error status
> +        """
> +        try:
> +            datacheck = int(data)
> +        except ValueError:
> +            # Bad data - ignore
> +            return
> +        if datacheck == 0:
> +            self.projector_errors = None
> +        else:
> +            self.projector_errors = {}
> +            # Fan
> +            if data[0] != '0':
> +                self.projector_errors[translate('OpenLP.ProjectorPJLink', 'Fan')] = \
> +                    PJLINK_ERST_STATUS[data[0]]
> +            # Lamp
> +            if data[1] != '0':
> +                self.projector_errors[translate('OpenLP.ProjectorPJLink', 'Lamp')] =  \
> +                    PJLINK_ERST_STATUS[data[1]]
> +            # Temp
> +            if data[2] != '0':
> +                self.projector_errors[translate('OpenLP.ProjectorPJLink', 'Temperature')] =  \
> +                    PJLINK_ERST_STATUS[data[2]]
> +            # Cover
> +            if data[3] != '0':
> +                self.projector_errors[translate('OpenLP.ProjectorPJLink', 'Cover')] =  \
> +                    PJLINK_ERST_STATUS[data[3]]
> +            # Filter
> +            if data[4] != '0':
> +                self.projector_errors[translate('OpenLP.ProjectorPJLink', 'Filter')] =  \
> +                    PJLINK_ERST_STATUS[data[4]]
> +            # Other
> +            if data[5] != '0':
> +                self.projector_errors[translate('OpenLP.ProjectorPJLink', 'Other')] =  \
> +                    PJLINK_ERST_STATUS[data[5]]
> +        return
> +
> +    def process_inf1(self, data):
> +        """
> +        Manufacturer name set in projector.
> +        Updates self.manufacturer
> +
> +        :param data: Projector manufacturer
> +        """
> +        self.manufacturer = data
> +        log.debug('({ip}) Setting projector manufacturer data to "{data}"'.format(ip=self.ip, data=self.manufacturer))
> +        return
> +
> +    def process_inf2(self, data):
> +        """
> +        Projector Model set in projector.
> +        Updates self.model.
> +
> +        :param data: Model name
> +        """
> +        self.model = data
> +        log.debug('({ip}) Setting projector model to "{data}"'.format(ip=self.ip, data=self.model))
> +        return
> +
> +    def process_info(self, data):
> +        """
> +        Any extra info set in projector.
> +        Updates self.other_info.
> +
> +        :param data: Projector other info
> +        """
> +        self.other_info = data
> +        log.debug('({ip}) Setting projector other_info to "{data}"'.format(ip=self.ip, data=self.other_info))
> +        return
> +
> +    def process_inpt(self, data):
> +        """
> +        Current source input selected. See PJLink specification for format.
> +        Update self.source
> +
> +        :param data: Currently selected source
> +        """
> +        self.source = data
> +        log.info('({ip}) Setting data source to "{data}"'.format(ip=self.ip, data=self.source))
> +        return
> +
> +    def process_inst(self, data):
> +        """
> +        Available source inputs. See PJLink specification for format.
> +        Updates self.source_available
> +
> +        :param data: Sources list
> +        """
> +        sources = []
> +        check = data.split()
> +        for source in check:
> +            sources.append(source)
> +        sources.sort()
> +        self.source_available = sources
> +        self.projectorUpdateIcons.emit()
> +        log.debug('({ip}) Setting projector sources_available to "{data}"'.format(ip=self.ip,
> +                                                                                  data=self.source_available))
> +        return
> +
> +    def process_lamp(self, data):
> +        """
> +        Lamp(s) status. See PJLink Specifications for format.
> +        Data may have more than 1 lamp to process.
> +        Update self.lamp dictionary with lamp status.
> +
> +        :param data: Lamp(s) status.
> +        """
> +        lamps = []
> +        data_dict = data.split()
> +        while data_dict:
> +            try:
> +                fill = {'Hours': int(data_dict[0]), 'On': False if data_dict[1] == '0' else True}
> +            except ValueError:
> +                # In case of invalid entry
> +                log.warning('({ip}) process_lamp(): Invalid data "{data}"'.format(ip=self.ip, data=data))
> +                return
> +            lamps.append(fill)
> +            data_dict.pop(0)  # Remove lamp hours
> +            data_dict.pop(0)  # Remove lamp on/off
> +        self.lamp = lamps
> +        return
> +
> +    def process_name(self, data):
> +        """
> +        Projector name set in projector.
> +        Updates self.pjlink_name
> +
> +        :param data: Projector name
> +        """
> +        self.pjlink_name = data
> +        log.debug('({ip}) Setting projector PJLink name to "{data}"'.format(ip=self.ip, data=self.pjlink_name))
> +        return
> +
> +    def process_powr(self, data):
> +        """
> +        Power status. See PJLink specification for format.
> +        Update self.power with status. Update icons if change from previous setting.
> +
> +        :param data: Power status
> +        """
> +        log.debug('({ip}: Processing POWR command'.format(ip=self.ip))
> +        if data in PJLINK_POWR_STATUS:
> +            power = PJLINK_POWR_STATUS[data]
> +            update_icons = self.power != power
> +            self.power = power
> +            self.change_status(PJLINK_POWR_STATUS[data])
> +            if update_icons:
> +                self.projectorUpdateIcons.emit()
> +                # Update the input sources available
> +                if power == S_ON:
> +                    self.send_command('INST')
> +        else:
> +            # Log unknown status response
> +            log.warning('({ip}) Unknown power response: {data}'.format(ip=self.ip, data=data))
> +        return
> +
> +    def process_rfil(self, data):
> +        """
> +        Process replacement filter type
> +        """
> +        if self.model_filter is None:
> +            self.model_filter = data
> +        else:
> +            log.warn("({ip}) Filter model already set".format(ip=self.ip))
> +            log.warn("({ip}) Saved model: '{old}'".format(ip=self.ip, old=self.model_filter))
> +            log.warn("({ip}) New model: '{new}'".format(ip=self.ip, new=data))
> +
> +    def process_rlmp(self, data):
> +        """
> +        Process replacement lamp type
> +        """
> +        if self.model_lamp is None:
> +            self.model_lamp = data
> +        else:
> +            log.warn("({ip}) Lamp model already set".format(ip=self.ip))
> +            log.warn("({ip}) Saved lamp: '{old}'".format(ip=self.ip, old=self.model_lamp))
> +            log.warn("({ip}) New lamp: '{new}'".format(ip=self.ip, new=data))
> +
> +    def process_snum(self, data):
> +        """
> +        Serial number of projector.
> +
> +        :param data: Serial number from projector.
> +        """
> +        if self.serial_no is None:
> +            log.debug("({ip}) Setting projector serial number to '{data}'".format(ip=self.ip, data=data))
> +            self.serial_no = data
> +            self.db_update = False
> +        else:
> +            # Compare serial numbers and see if we got the same projector
> +            if self.serial_no != data:
> +                log.warn("({ip}) Projector serial number does not match saved serial number".format(ip=self.ip))
> +                log.warn("({ip}) Saved:    '{old}'".format(ip=self.ip, old=self.serial_no))
> +                log.warn("({ip}) Received: '{new}'".format(ip=self.ip, new=data))
> +                log.warn("({ip}) NOT saving serial number".format(ip=self.ip))
> +                self.serial_no_received = data
> +
> +    def process_sver(self, data):
> +        """
> +        Software version of projector
> +        """
> +        if self.sw_version is None:
> +            log.debug("({ip}) Setting projector software version to '{data}'".format(ip=self.ip, data=data))
> +            self.sw_version = data
> +            self.db_update = True
> +        else:
> +            # Compare software version and see if we got the same projector
> +            if self.serial_no != data:
> +                log.warn("({ip}) Projector software version does not match saved software version".format(ip=self.ip))
> +                log.warn("({ip}) Saved:    '{old}'".format(ip=self.ip, old=self.sw_version))
> +                log.warn("({ip}) Received: '{new}'".format(ip=self.ip, new=data))
> +                log.warn("({ip}) NOT saving serial number".format(ip=self.ip))
> +                self.sw_version_received = data
> +
> +
> +class PJLink(PJLinkCommands, QtNetwork.QTcpSocket):
>      """
>      Socket service for connecting to a PJLink-capable projector.
>      """


-- 
https://code.launchpad.net/~alisonken1/openlp/pjlink2g/+merge/328634
Your team OpenLP Core is subscribed to branch lp:openlp.


References