openlp-core team mailing list archive
-
openlp-core team
-
Mailing list archive
-
Message #31505
[Merge] lp:~alisonken1/openlp/pjlink2-a into lp:openlp
Ken Roberts has proposed merging lp:~alisonken1/openlp/pjlink2-a into lp:openlp with lp:~alisonken1/openlp/pjlink2-resource-data as a prerequisite.
Commit message:
Initial PJLink class 2 updates
Requested reviews:
OpenLP Core (openlp-core)
For more details, see:
https://code.launchpad.net/~alisonken1/openlp/pjlink2-a/+merge/323966
Initial PJLink class 2 updates
- Converted PJLINK_DEFAULT_CODES from a static dictionary to dynamically-built dictionary
- Added _not_implemented method to be able to list future methods while updating
- Added class list to hold future method functionality
- Added class list for UDP commands
- Added test for building PJLINK_DEFAULT_CODES dictionary
- Added test for _not_implemented method
--------------------------------
lp:~alisonken1/openlp/pjlink2-a (revision 2734)
[SUCCESS] https://ci.openlp.io/job/Branch-01-Pull/1996/
[SUCCESS] https://ci.openlp.io/job/Branch-02-Functional-Tests/1906/
[SUCCESS] https://ci.openlp.io/job/Branch-03-Interface-Tests/1845/
[SUCCESS] https://ci.openlp.io/job/Branch-04a-Code_Analysis/1225/
[SUCCESS] https://ci.openlp.io/job/Branch-04b-Test_Coverage/1088/
[SUCCESS] https://ci.openlp.io/job/Branch-04c-Code_Analysis2/217/
[FAILURE] https://ci.openlp.io/job/Branch-05-AppVeyor-Tests/65/
--
Your team OpenLP Core is requested to review the proposed merge of lp:~alisonken1/openlp/pjlink2-a into lp:openlp.
=== modified file 'openlp/core/lib/projector/constants.py'
--- openlp/core/lib/projector/constants.py 2016-12-31 11:01:36 +0000
+++ openlp/core/lib/projector/constants.py 2017-05-12 10:04:09 +0000
@@ -48,7 +48,8 @@
'S_INFO', 'S_NETWORK_SENDING', 'S_NETWORK_RECEIVED',
'ERROR_STRING', 'CR', 'LF', 'PJLINK_ERST_STATUS', 'PJLINK_POWR_STATUS',
'PJLINK_PORT', 'PJLINK_MAX_PACKET', 'TIMEOUT', 'ERROR_MSG', 'PJLINK_ERRORS',
- 'STATUS_STRING', 'PJLINK_VALID_CMD', 'CONNECTION_ERRORS']
+ 'STATUS_STRING', 'PJLINK_VALID_CMD', 'CONNECTION_ERRORS',
+ 'PJLINK_DEFAULT_SOURCES', 'PJLINK_DEFAULT_CODES', 'PJLINK_DEFAULT_ITEMS']
# Set common constants.
CR = chr(0x0D) # \r
@@ -321,53 +322,54 @@
'2': translate('OpenLP.DB', 'Video'),
'3': translate('OpenLP.DB', 'Digital'),
'4': translate('OpenLP.DB', 'Storage'),
- '5': translate('OpenLP.DB', 'Network')
-}
-
-PJLINK_DEFAULT_CODES = {
- '11': translate('OpenLP.DB', 'RGB 1'),
- '12': translate('OpenLP.DB', 'RGB 2'),
- '13': translate('OpenLP.DB', 'RGB 3'),
- '14': translate('OpenLP.DB', 'RGB 4'),
- '15': translate('OpenLP.DB', 'RGB 5'),
- '16': translate('OpenLP.DB', 'RGB 6'),
- '17': translate('OpenLP.DB', 'RGB 7'),
- '18': translate('OpenLP.DB', 'RGB 8'),
- '19': translate('OpenLP.DB', 'RGB 9'),
- '21': translate('OpenLP.DB', 'Video 1'),
- '22': translate('OpenLP.DB', 'Video 2'),
- '23': translate('OpenLP.DB', 'Video 3'),
- '24': translate('OpenLP.DB', 'Video 4'),
- '25': translate('OpenLP.DB', 'Video 5'),
- '26': translate('OpenLP.DB', 'Video 6'),
- '27': translate('OpenLP.DB', 'Video 7'),
- '28': translate('OpenLP.DB', 'Video 8'),
- '29': translate('OpenLP.DB', 'Video 9'),
- '31': translate('OpenLP.DB', 'Digital 1'),
- '32': translate('OpenLP.DB', 'Digital 2'),
- '33': translate('OpenLP.DB', 'Digital 3'),
- '34': translate('OpenLP.DB', 'Digital 4'),
- '35': translate('OpenLP.DB', 'Digital 5'),
- '36': translate('OpenLP.DB', 'Digital 6'),
- '37': translate('OpenLP.DB', 'Digital 7'),
- '38': translate('OpenLP.DB', 'Digital 8'),
- '39': translate('OpenLP.DB', 'Digital 9'),
- '41': translate('OpenLP.DB', 'Storage 1'),
- '42': translate('OpenLP.DB', 'Storage 2'),
- '43': translate('OpenLP.DB', 'Storage 3'),
- '44': translate('OpenLP.DB', 'Storage 4'),
- '45': translate('OpenLP.DB', 'Storage 5'),
- '46': translate('OpenLP.DB', 'Storage 6'),
- '47': translate('OpenLP.DB', 'Storage 7'),
- '48': translate('OpenLP.DB', 'Storage 8'),
- '49': translate('OpenLP.DB', 'Storage 9'),
- '51': translate('OpenLP.DB', 'Network 1'),
- '52': translate('OpenLP.DB', 'Network 2'),
- '53': translate('OpenLP.DB', 'Network 3'),
- '54': translate('OpenLP.DB', 'Network 4'),
- '55': translate('OpenLP.DB', 'Network 5'),
- '56': translate('OpenLP.DB', 'Network 6'),
- '57': translate('OpenLP.DB', 'Network 7'),
- '58': translate('OpenLP.DB', 'Network 8'),
- '59': translate('OpenLP.DB', 'Network 9')
-}
+ '5': translate('OpenLP.DB', 'Network'),
+ '6': translate('OpenLP.DB', 'Internal')
+}
+
+PJLINK_DEFAULT_ITEMS = {
+ '1': translate('OpenLP.DB', '1'),
+ '2': translate('OpenLP.DB', '2'),
+ '3': translate('OpenLP.DB', '3'),
+ '4': translate('OpenLP.DB', '4'),
+ '5': translate('OpenLP.DB', '5'),
+ '6': translate('OpenLP.DB', '6'),
+ '7': translate('OpenLP.DB', '7'),
+ '8': translate('OpenLP.DB', '8'),
+ '9': translate('OpenLP.DB', '9'),
+ 'A': translate('OpenLP.DB', 'A'),
+ 'B': translate('OpenLP.DB', 'B'),
+ 'C': translate('OpenLP.DB', 'C'),
+ 'D': translate('OpenLP.DB', 'D'),
+ 'E': translate('OpenLP.DB', 'E'),
+ 'F': translate('OpenLP.DB', 'F'),
+ 'G': translate('OpenLP.DB', 'G'),
+ 'H': translate('OpenLP.DB', 'H'),
+ 'I': translate('OpenLP.DB', 'I'),
+ 'J': translate('OpenLP.DB', 'J'),
+ 'K': translate('OpenLP.DB', 'K'),
+ 'L': translate('OpenLP.DB', 'L'),
+ 'M': translate('OpenLP.DB', 'M'),
+ 'N': translate('OpenLP.DB', 'N'),
+ 'O': translate('OpenLP.DB', 'O'),
+ 'P': translate('OpenLP.DB', 'P'),
+ 'Q': translate('OpenLP.DB', 'Q'),
+ 'R': translate('OpenLP.DB', 'R'),
+ 'S': translate('OpenLP.DB', 'S'),
+ 'T': translate('OpenLP.DB', 'T'),
+ 'U': translate('OpenLP.DB', 'U'),
+ 'V': translate('OpenLP.DB', 'V'),
+ 'W': translate('OpenLP.DB', 'W'),
+ 'X': translate('OpenLP.DB', 'X'),
+ 'Y': translate('OpenLP.DB', 'Y'),
+ 'Z': translate('OpenLP.DB', 'Z')
+}
+
+# Due to the expanded nature of PJLink class 2 video sources,
+# translate the individual types then build the video source
+# dictionary from the translations.
+PJLINK_DEFAULT_CODES = dict()
+for source in PJLINK_DEFAULT_SOURCES:
+ for item in PJLINK_DEFAULT_ITEMS:
+ label = "{source}{item}".format(source=source, item=item)
+ PJLINK_DEFAULT_CODES[label] = "{source} {item}".format(source=PJLINK_DEFAULT_SOURCES[source],
+ item=PJLINK_DEFAULT_ITEMS[item])
=== modified file 'openlp/core/lib/projector/pjlink1.py'
--- openlp/core/lib/projector/pjlink1.py 2016-12-31 11:01:36 +0000
+++ openlp/core/lib/projector/pjlink1.py 2017-05-12 10:04:09 +0000
@@ -78,6 +78,33 @@
projectorNoAuthentication = QtCore.pyqtSignal(str) # PIN set and no authentication needed
projectorReceivedData = QtCore.pyqtSignal() # Notify when received data finished processing
projectorUpdateIcons = QtCore.pyqtSignal() # Update the status icons on toolbar
+ # New commands available in PJLink Class 2
+ pjlink_future = [
+ 'ACKN', # UDP Reply to 'SRCH'
+ 'FILT', # Get current filter usage time
+ 'FREZ', # Set freeze/unfreeze picture being projected
+ 'INNM', # Get Video source input terminal name
+ 'IRES', # Get Video source resolution
+ 'LKUP', # UPD Linkup status notification
+ 'MVOL', # Set microphone volume
+ 'RFIL', # Get replacement air filter model number
+ 'RLMP', # Get lamp replacement model number
+ 'RRES', # Get projector recommended video resolution
+ 'SNUM', # Get projector serial number
+ 'SRCH', # UDP broadcast search for available projectors on local network
+ 'SVER', # Get projector software version
+ 'SVOL', # Set speaker volume
+ 'TESTMEONLY' # For testing when other commands have been implemented
+ ]
+
+ pjlink_udp_commands = [
+ 'ACKN',
+ 'ERST', # Class 1 or 2
+ 'INPT', # Class 1 or 2
+ 'LKUP',
+ 'POWR', # Class 1 or 2
+ 'SRCH'
+ ]
def __init__(self, name=None, ip=None, port=PJLINK_PORT, pin=None, *args, **kwargs):
"""
@@ -403,7 +430,8 @@
return
self.socket_timer.stop()
self.projectorNetwork.emit(S_NETWORK_RECEIVED)
- data_in = decode(read, 'ascii')
+ # NOTE: Class2 has changed to some values being UTF-8
+ data_in = decode(read, 'utf-8')
data = data_in.strip()
if len(data) < 7:
# Not enough data for a packet
@@ -510,11 +538,12 @@
self._send_command()
@QtCore.pyqtSlot()
- def _send_command(self, data=None):
+ def _send_command(self, data=None, utf8=False):
"""
Socket interface to send data. If data=None, then check queue.
:param data: Immediate data to send
+ :param utf8: Send as UTF-8 string otherwise send as ASCII string
"""
log.debug('({ip}) _send_string()'.format(ip=self.ip))
log.debug('({ip}) _send_string(): Connection status: {data}'.format(ip=self.ip, data=self.state()))
@@ -542,7 +571,7 @@
log.debug('({ip}) _send_string(): Queue = {data}'.format(ip=self.ip, data=self.send_queue))
self.socket_timer.start()
self.projectorNetwork.emit(S_NETWORK_SENDING)
- sent = self.write(out.encode('ascii'))
+ sent = self.write(out.encode('{string_encoding}'.format(string_encoding='utf-8' if utf8 else 'ascii')))
self.waitForBytesWritten(2000) # 2 seconds should be enough
if sent == -1:
# Network error?
@@ -556,7 +585,13 @@
:param cmd: Command to process
:param data: Data being processed
"""
- log.debug('({ip}) Processing command "{data}"'.format(ip=self.ip, data=cmd))
+ 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 in self.pjlink_future:
+ self._not_implemented(cmd)
+ return
if data in PJLINK_ERRORS:
# Oops - projector error
log.error('({ip}) Projector returned error "{data}"'.format(ip=self.ip, data=data))
@@ -568,9 +603,8 @@
self.projectorAuthentication.emit(self.name)
elif data.upper() == 'ERR1':
# Undefined command
- self.change_status(E_UNDEFINED, '{error} "{data}"'.format(error=translate('OpenLP.PJLink1',
- 'Undefined command:'),
- data=cmd))
+ 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)
@@ -681,6 +715,7 @@
:param data: Currently selected source
"""
+ # TODO: Class 2 change: verify input does not exceed 95 bytes
self.source = data
log.info('({ip}) Setting data source to "{data}"'.format(ip=self.ip, data=self.source))
return
@@ -962,3 +997,11 @@
log.debug('({ip}) Setting AVMT to "10" (shutter open)'.format(ip=self.ip))
self.send_command(cmd='AVMT', opts='10')
self.poll_loop()
+
+ def _not_implemented(self, cmd):
+ """
+ Log when a future PJLink command has not been implemented yet.
+ """
+ log.warn("({ip}) Future command '{cmd}' has not been implemented yet".format(ip=self.ip,
+ cmd=cmd))
+ return
=== added file 'tests/functional/openlp_core_lib/test_projector_constants.py'
--- tests/functional/openlp_core_lib/test_projector_constants.py 1970-01-01 00:00:00 +0000
+++ tests/functional/openlp_core_lib/test_projector_constants.py 2017-05-12 10:04:09 +0000
@@ -0,0 +1,44 @@
+# -*- coding: utf-8 -*-
+# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
+
+###############################################################################
+# OpenLP - Open Source Lyrics Projection #
+# --------------------------------------------------------------------------- #
+# Copyright (c) 2008-2015 OpenLP Developers #
+# --------------------------------------------------------------------------- #
+# This program is free software; you can redistribute it and/or modify it #
+# under the terms of the GNU General Public License as published by the Free #
+# Software Foundation; version 2 of the License. #
+# #
+# This program is distributed in the hope that it will be useful, but WITHOUT #
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
+# more details. #
+# #
+# You should have received a copy of the GNU General Public License along #
+# with this program; if not, write to the Free Software Foundation, Inc., 59 #
+# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
+###############################################################################
+"""
+Package to test the openlp.core.lib.projector.constants package.
+"""
+from unittest import TestCase, skip
+
+
+class TestProjectorConstants(TestCase):
+ """
+ Test specific functions in the projector constants module.
+ """
+ @skip('Waiting for merge of ~alisonken1/openlp/pjlink2-resource-data')
+ def build_pjlink_video_label_test(self):
+ """
+ Test building PJLINK_DEFAULT_CODES dictionary
+ """
+ # GIVEN: Test data
+ from tests.resources.projector.data import TEST_VIDEO_CODES
+
+ # WHEN: Import projector PJLINK_DEFAULT_CODES
+ from openlp.core.lib.projector.constants import PJLINK_DEFAULT_CODES
+
+ # THEN: Verify dictionary was build correctly
+ self.assertEquals(PJLINK_DEFAULT_CODES, TEST_VIDEO_CODES, 'PJLink video strings should match')
=== modified file 'tests/functional/openlp_core_lib/test_projector_pjlink1.py'
--- tests/functional/openlp_core_lib/test_projector_pjlink1.py 2017-04-24 05:17:55 +0000
+++ tests/functional/openlp_core_lib/test_projector_pjlink1.py 2017-05-12 10:04:09 +0000
@@ -366,3 +366,18 @@
# THEN: send_command should have the proper authentication
self.assertEquals("{test}".format(test=mock_send_command.call_args),
"call(data='{hash}%1CLSS ?\\r')".format(hash=TEST_HASH))
+
+ @patch.object(pjlink_test, '_not_implemented')
+ def not_implemented_test(self, mock_not_implemented):
+ """
+ Test pjlink1._not_implemented method being called
+ """
+ # GIVEN: test object
+ pjlink = pjlink_test
+ test_cmd = 'TESTMEONLY'
+
+ # WHEN: A future command is called that is not implemented yet
+ pjlink.process_command(test_cmd, "Garbage data for test only")
+
+ # THEN: pjlink1.__not_implemented should have been called with test_cmd
+ mock_not_implemented.assert_called_with(test_cmd)
Follow ups