Ken Roberts has proposed merging lp:~alisonken1/openlp/pjlink2-k into lp:openlp.
Commit message: PJLink2 update K Requested reviews: OpenLP Core (openlp-core) For more details, see: https://code.launchpad.net/~alisonken1/openlp/pjlink2-k/+merge/331191 PJlink2 update K - Update notes in PJLink-Notes.odt/update PDF - Add PJLink.get_socket() (process socket data) - Add PJLink.get_buffer() (process non-socket data) - Update PJLink.get_data() to work with get_socket/get_buffer - Cleanup ProjectorManager code - Fix incorrect note reference to song db in test_projector_db - Fix projector tests for change to PJLink() creation - Start PJLinkUDP class - Remove unused pjlink2.py module - Code oops cleanups [SUCCESS] https://ci.openlp.io/job/Branch-01-Pull/2199/ [SUCCESS] https://ci.openlp.io/job/Branch-02-Functional-Tests/2102/ [SUCCESS] https://ci.openlp.io/job/Branch-03-Interface-Tests/1989/ [SUCCESS] https://ci.openlp.io/job/Branch-04a-Code_Analysis/1359/ [SUCCESS] https://ci.openlp.io/job/Branch-04b-Test_Coverage/1192/ [SUCCESS] https://ci.openlp.io/job/Branch-04c-Code_Analysis2/322/ [FAILURE] https://ci.openlp.io/job/Branch-05-AppVeyor-Tests/162/ -- Your team OpenLP Core is requested to review the proposed merge of lp:~alisonken1/openlp/pjlink2-k into lp:openlp.
=== modified file 'documentation/PJLink_Notes.odt' Binary files documentation/PJLink_Notes.odt 2017-09-09 06:57:21 +0000 and documentation/PJLink_Notes.odt 2017-09-22 12:12:22 +0000 differ === modified file 'documentation/PJLink_Notes.pdf' Binary files documentation/PJLink_Notes.pdf 2017-09-09 06:57:21 +0000 and documentation/PJLink_Notes.pdf 2017-09-22 12:12:22 +0000 differ === modified file 'openlp/core/lib/projector/db.py' --- openlp/core/lib/projector/db.py 2017-06-25 02:21:07 +0000 +++ openlp/core/lib/projector/db.py 2017-09-22 12:12:22 +0000 @@ -341,9 +341,9 @@ """ old_projector = self.get_object_filtered(Projector, Projector.ip == projector.ip) if old_projector is not None: - log.warning('add_new() skipping entry ip="{ip}" (Already saved)'.format(ip=old_projector.ip)) + log.warning('add_projector() skipping entry ip="{ip}" (Already saved)'.format(ip=old_projector.ip)) return False - log.debug('add_new() saving new entry') + log.debug('add_projector() saving new entry') log.debug('ip="{ip}", name="{name}", location="{location}"'.format(ip=projector.ip, name=projector.name, location=projector.location)) === modified file 'openlp/core/lib/projector/pjlink.py' --- openlp/core/lib/projector/pjlink.py 2017-08-23 19:46:30 +0000 +++ openlp/core/lib/projector/pjlink.py 2017-09-22 12:12:22 +0000 @@ -72,6 +72,28 @@ PJLINK_SUFFIX = CR +class PJLinkUDP(QtNetwork.QUdpSocket): + """ + Socket service for PJLink UDP socket. + """ + # New commands available in PJLink Class 2 + pjlink_udp_commands = [ + 'ACKN', # Class 2 (cmd is SRCH) + 'ERST', # Class 1/2 + 'INPT', # Class 1/2 + 'LKUP', # Class 2 (reply only - no cmd) + 'POWR', # Class 1/2 + 'SRCH' # Class 2 (reply is ACKN) + ] + + def __init__(self, port=PJLINK_PORT): + """ + Initialize socket + """ + + self.port = port + + class PJLinkCommands(object): """ Process replies from PJLink projector. @@ -488,7 +510,7 @@ class PJLink(PJLinkCommands, QtNetwork.QTcpSocket): """ - Socket service for connecting to a PJLink-capable projector. + Socket service for PJLink TCP socket. """ # Signals sent by this module changeStatus = QtCore.pyqtSignal(str, int, str) @@ -499,43 +521,29 @@ 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_udp_commands = [ - 'ACKN', # Class 2 - 'ERST', # Class 1 or 2 - 'INPT', # Class 1 or 2 - 'LKUP', # Class 2 - 'POWR', # Class 1 or 2 - 'SRCH' # Class 2 - ] - - def __init__(self, port=PJLINK_PORT, *args, **kwargs): + def __init__(self, projector, *args, **kwargs): """ Setup for instance. Options should be in kwargs except for port which does have a default. - :param name: Display name - :param ip: IP address to connect to - :param port: Port to use. Default to PJLINK_PORT - :param pin: Access pin (if needed) + :param projector: Database record of projector Optional parameters - :param dbid: Database ID number - :param location: Location where projector is physically located - :param notes: Extra notes about the projector :param poll_time: Time (in seconds) to poll connected projector :param socket_timeout: Time (in seconds) to abort the connection if no response """ - log.debug('PJlink(args={args} kwargs={kwargs})'.format(args=args, kwargs=kwargs)) + log.debug('PJlink(projector={projector}, args={args} kwargs={kwargs})'.format(projector=projector, + args=args, + kwargs=kwargs)) super().__init__() - self.dbid = kwargs.get('dbid') - self.ip = kwargs.get('ip') - self.location = kwargs.get('location') - self.mac_adx = kwargs.get('mac_adx') - self.name = kwargs.get('name') - self.notes = kwargs.get('notes') - self.pin = kwargs.get('pin') - self.port = port + self.entry = projector + self.ip = self.entry.ip + self.location = self.entry.location + self.mac_adx = self.entry.mac_adx + self.name = self.entry.name + self.notes = self.entry.notes + self.pin = self.entry.pin + self.port = self.entry.port self.db_update = False # Use to check if db needs to be updated prior to exiting # Poll time 20 seconds unless called with something else self.poll_time = 20000 if 'poll_time' not in kwargs else kwargs['poll_time'] * 1000 @@ -751,7 +759,7 @@ self.change_status(E_AUTHENTICATION) log.debug('({ip}) emitting projectorAuthentication() signal'.format(ip=self.ip)) return - elif data_check[1] == '0' and self.pin is not None: + elif (data_check[1] == '0') and (self.pin): # Pin set and no authentication needed log.warning('({ip}) Regular connection but PIN set'.format(ip=self.name)) self.disconnect_from_host() @@ -761,7 +769,7 @@ return elif data_check[1] == '1': # Authenticated login with salt - if self.pin is None: + if not self.pin: log.warning('({ip}) Authenticated connection but no pin set'.format(ip=self.ip)) self.disconnect_from_host() self.change_status(E_AUTHENTICATION) @@ -776,7 +784,7 @@ else: data_hash = None # We're connected at this point, so go ahead and setup regular I/O - self.readyRead.connect(self.get_data) + self.readyRead.connect(self.get_socket) self.projectorReceivedData.connect(self._send_command) # Initial data we should know about self.send_command(cmd='CLSS', salt=data_hash) @@ -800,27 +808,51 @@ count=trash_count)) return + @QtCore.pyqtSlot(str, str) + def get_buffer(self, data, ip): + """ + Get data from somewhere other than TCP socket + + :param data: Data to process. buffer must be formatted as a proper PJLink packet. + :param ip: Destination IP for buffer. + """ + log.debug("({ip}) get_buffer(data='{buff}' ip='{ip_in}'".format(ip=self.ip, buff=data, ip_in=ip)) + if ip is None: + log.debug("({ip}) get_buffer() Don't know who data is for - exiting".format(ip=self.ip)) + return + return self.get_data(buff=data, ip=ip) + @QtCore.pyqtSlot() - def get_data(self): - """ - Socket interface to retrieve data. - """ - log.debug('({ip}) get_data(): Reading data'.format(ip=self.ip)) + def get_socket(self): + """ + Get data from TCP socket. + """ + log.debug('({ip}) get_socket(): Reading data'.format(ip=self.ip)) if self.state() != self.ConnectedState: - log.debug('({ip}) get_data(): Not connected - returning'.format(ip=self.ip)) + log.debug('({ip}) get_socket(): Not connected - returning'.format(ip=self.ip)) self.send_busy = False return # Although we have a packet length limit, go ahead and use a larger buffer read = self.readLine(1024) - log.debug("({ip}) get_data(): '{buff}'".format(ip=self.ip, buff=read)) + log.debug("({ip}) get_socket(): '{buff}'".format(ip=self.ip, buff=read)) if read == -1: # No data available - log.debug('({ip}) get_data(): No data available (-1)'.format(ip=self.ip)) + log.debug('({ip}) get_socket(): No data available (-1)'.format(ip=self.ip)) return self.receive_data_signal() self.socket_timer.stop() self.projectorNetwork.emit(S_NETWORK_RECEIVED) + return self.get_data(buff=read, ip=self.ip) + + def get_data(self, buff, ip): + """ + Process received data + + :param buff: Data to process. + :param ip: (optional) Destination IP. + """ + log.debug("({ip}) get_data(ip='{ip_in}' buffer='{buff}'".format(ip=self.ip, ip_in=ip, buff=buff)) # NOTE: Class2 has changed to some values being UTF-8 - data_in = decode(read, 'utf-8') + data_in = decode(buff, 'utf-8') data = data_in.strip() if (len(data) < 7) or (not data.startswith(PJLINK_PREFIX)): return self._trash_buffer(msg='get_data(): Invalid packet - length or prefix') @@ -990,7 +1022,7 @@ self.reset_information() self.disconnectFromHost() try: - self.readyRead.disconnect(self.get_data) + self.readyRead.disconnect(self.get_socket) except TypeError: pass if abort: === removed file 'openlp/core/lib/projector/pjlink2.py' --- openlp/core/lib/projector/pjlink2.py 2017-07-07 23:43:50 +0000 +++ openlp/core/lib/projector/pjlink2.py 1970-01-01 00:00:00 +0000 @@ -1,85 +0,0 @@ -# -*- coding: utf-8 -*- -# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4 - -############################################################################### -# OpenLP - Open Source Lyrics Projection # -# --------------------------------------------------------------------------- # -# Copyright (c) 2008-2017 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 # -############################################################################### -""" - :mod:`openlp.core.lib.projector.pjlink2` module provides the PJLink Class 2 - updates from PJLink Class 1. - - This module only handles the UDP socket functionality. Command/query/status - change messages will still be processed by the PJLink 1 module. - - Currently, the only variance is the addition of a UDP "search" command to - query the local network for Class 2 capable projectors, - and UDP "notify" messages from projectors to connected software of status - changes (i.e., power change, input change, error changes). - - Differences between Class 1 and Class 2 PJLink specifications are as follows. - - New Functionality: - * Search - UDP Query local network for Class 2 capabable projector(s). - * Status - UDP Status change with connected projector(s). Status change - messages consist of: - * Initial projector power up when network communication becomes available - * Lamp off/standby to warmup or on - * Lamp on to cooldown or off/standby - * Input source select change completed - * Error status change (i.e., fan/lamp/temp/cover open/filter/other error(s)) - - New Commands: - * Query serial number of projector - * Query version number of projector software - * Query model number of replacement lamp - * Query model number of replacement air filter - * Query current projector screen resolution - * Query recommended screen resolution - * Query name of specific input terminal (video source) - * Adjust projector microphone in 1-step increments - * Adjust projector speacker in 1-step increments - - Extended Commands: - * Addition of INTERNAL terminal (video source) for a total of 6 types of terminals. - * Number of terminals (video source) has been expanded from [1-9] - to [1-9a-z] (Addition of 26 terminals for each type of input). - - See PJLink Class 2 Specifications for details. - http://pjlink.jbmia.or.jp/english/dl_class2.html - - Section 5-1 PJLink Specifications - - Section 5-5 Guidelines for Input Terminals -""" -import logging -log = logging.getLogger(__name__) - -log.debug('pjlink2 loaded') - -from PyQt5 import QtNetwork - - -class PJLinkUDP(QtNetwork.QUdpSocket): - """ - Socket service for handling datagram (UDP) sockets. - """ - log.debug('PJLinkUDP loaded') - # Class varialbe for projector list. Should be replaced by ProjectorManager's - # projector list after being loaded there. - projector_list = None - projectors_found = None # UDP search found list === modified file 'openlp/core/ui/projector/manager.py' --- openlp/core/ui/projector/manager.py 2017-08-06 07:23:26 +0000 +++ openlp/core/ui/projector/manager.py 2017-09-22 12:12:22 +0000 @@ -38,8 +38,7 @@ E_NETWORK, E_NOT_CONNECTED, E_UNKNOWN_SOCKET_ERROR, STATUS_STRING, S_CONNECTED, S_CONNECTING, S_COOLDOWN, \ S_INITIALIZE, S_NOT_CONNECTED, S_OFF, S_ON, S_STANDBY, S_WARMUP from openlp.core.lib.projector.db import ProjectorDB -from openlp.core.lib.projector.pjlink import PJLink -from openlp.core.lib.projector.pjlink2 import PJLinkUDP +from openlp.core.lib.projector.pjlink import PJLink, PJLinkUDP from openlp.core.ui.projector.editform import ProjectorEditForm from openlp.core.ui.projector.sourceselectform import SourceSelectTabs, SourceSelectSingle @@ -700,16 +699,9 @@ :returns: PJLink() instance """ log.debug('_add_projector()') - return PJLink(dbid=projector.id, - ip=projector.ip, - port=int(projector.port), - name=projector.name, - location=projector.location, - notes=projector.notes, - pin=None if projector.pin == '' else projector.pin, + return PJLink(projector=projector, poll_time=self.poll_time, - socket_timeout=self.socket_timeout - ) + socket_timeout=self.socket_timeout) def add_projector(self, projector, start=False): """ === modified file 'tests/functional/openlp_core_lib/test_projector_db.py' --- tests/functional/openlp_core_lib/test_projector_db.py 2017-08-06 07:23:26 +0000 +++ tests/functional/openlp_core_lib/test_projector_db.py 2017-09-22 12:12:22 +0000 @@ -111,7 +111,7 @@ """ Test that we can upgrade an old song db to the current schema """ - # GIVEN: An old song db + # GIVEN: An old prjector db old_db = os.path.join(TEST_RESOURCES_PATH, "projector", TEST_DB_PJLINK1) tmp_db = os.path.join(self.tmp_folder, TEST_DB) shutil.copyfile(old_db, tmp_db) === modified file 'tests/functional/openlp_core_lib/test_projector_pjlink_base.py' --- tests/functional/openlp_core_lib/test_projector_pjlink_base.py 2017-08-06 07:23:26 +0000 +++ tests/functional/openlp_core_lib/test_projector_pjlink_base.py 2017-09-22 12:12:22 +0000 @@ -25,12 +25,13 @@ from unittest import TestCase from unittest.mock import call, patch, MagicMock +from openlp.core.lib.projector.db import Projector from openlp.core.lib.projector.pjlink import PJLink from openlp.core.lib.projector.constants import E_PARAMETER, ERROR_STRING, S_ON, S_CONNECTED -from tests.resources.projector.data import TEST_PIN, TEST_SALT, TEST_CONNECT_AUTHENTICATE, TEST_HASH +from tests.resources.projector.data import TEST_PIN, TEST_SALT, TEST_CONNECT_AUTHENTICATE, TEST_HASH, TEST1_DATA -pjlink_test = PJLink(name='test', ip='127.0.0.1', pin=TEST_PIN, no_poll=True) +pjlink_test = PJLink(Projector(**TEST1_DATA), no_poll=True) class TestPJLinkBase(TestCase): === modified file 'tests/functional/openlp_core_lib/test_projector_pjlink_cmd_routing.py' --- tests/functional/openlp_core_lib/test_projector_pjlink_cmd_routing.py 2017-08-12 20:38:50 +0000 +++ tests/functional/openlp_core_lib/test_projector_pjlink_cmd_routing.py 2017-09-22 12:12:22 +0000 @@ -27,6 +27,7 @@ from unittest.mock import patch, MagicMock import openlp.core.lib.projector.pjlink +from openlp.core.lib.projector.db import Projector from openlp.core.lib.projector.pjlink import PJLink from openlp.core.lib.projector.constants import PJLINK_ERRORS, \ E_AUTHENTICATION, E_PARAMETER, E_PROJECTOR, E_UNAVAILABLE, E_UNDEFINED @@ -35,9 +36,10 @@ from openlp.core.lib.projector.constants import ERROR_STRING, PJLINK_ERST_DATA, PJLINK_ERST_STATUS, \ PJLINK_POWR_STATUS, PJLINK_VALID_CMD, E_WARN, E_ERROR, S_OFF, S_STANDBY, S_ON ''' -from tests.resources.projector.data import TEST_PIN +from tests.resources.projector.data import TEST_PIN, TEST1_DATA -pjlink_test = PJLink(name='test', ip='127.0.0.1', pin=TEST_PIN, no_poll=True) +pjlink_test = PJLink(Projector(**TEST1_DATA), pin=TEST_PIN, no_poll=True) +pjlink_test.ip = '127.0.0.1' class TestPJLinkRouting(TestCase): === modified file 'tests/functional/openlp_core_lib/test_projector_pjlink_commands.py' --- tests/functional/openlp_core_lib/test_projector_pjlink_commands.py 2017-08-12 20:38:50 +0000 +++ tests/functional/openlp_core_lib/test_projector_pjlink_commands.py 2017-09-22 12:12:22 +0000 @@ -26,15 +26,17 @@ from unittest.mock import patch import openlp.core.lib.projector.pjlink +from openlp.core.lib.projector.db import Projector from openlp.core.lib.projector.pjlink import PJLink from openlp.core.lib.projector.constants import ERROR_STRING, PJLINK_ERST_DATA, PJLINK_ERST_STATUS, \ PJLINK_POWR_STATUS, \ E_ERROR, E_NOT_CONNECTED, E_SOCKET_ADDRESS_NOT_AVAILABLE, E_UNKNOWN_SOCKET_ERROR, E_WARN, \ S_CONNECTED, S_OFF, S_ON, S_NOT_CONNECTED, S_CONNECTING, S_STANDBY -from tests.resources.projector.data import TEST_PIN +from tests.resources.projector.data import TEST_PIN, TEST1_DATA -pjlink_test = PJLink(name='test', ip='127.0.0.1', pin=TEST_PIN, no_poll=True) +pjlink_test = PJLink(Projector(**TEST1_DATA), pin=TEST_PIN, no_poll=True) +pjlink_test.ip = '127.0.0.1' # Create a list of ERST positional data so we don't have to redo the same buildup multiple times PJLINK_ERST_POSITIONS = []
_______________________________________________ Mailing list: https://launchpad.net/~openlp-core Post to : openlp-core@lists.launchpad.net Unsubscribe : https://launchpad.net/~openlp-core More help : https://help.launchpad.net/ListHelp