openlp-core team mailing list archive
-
openlp-core team
-
Mailing list archive
-
Message #31937
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