forked from openlp/openlp
Initial PJLink class 2 updates
This commit is contained in:
parent
d74c9c0c81
commit
a5335adbc4
@ -48,7 +48,8 @@ __all__ = ['S_OK', 'E_GENERAL', 'E_NOT_CONNECTED', 'E_FAN', 'E_LAMP', 'E_TEMP',
|
||||
'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 @@ PJLINK_DEFAULT_SOURCES = {
|
||||
'2': translate('OpenLP.DB', 'Video'),
|
||||
'3': translate('OpenLP.DB', 'Digital'),
|
||||
'4': translate('OpenLP.DB', 'Storage'),
|
||||
'5': translate('OpenLP.DB', 'Network')
|
||||
'5': translate('OpenLP.DB', 'Network'),
|
||||
'6': translate('OpenLP.DB', 'Internal')
|
||||
}
|
||||
|
||||
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')
|
||||
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])
|
||||
|
@ -78,6 +78,33 @@ class PJLink1(QtNetwork.QTcpSocket):
|
||||
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 @@ class PJLink1(QtNetwork.QTcpSocket):
|
||||
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 @@ class PJLink1(QtNetwork.QTcpSocket):
|
||||
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 @@ class PJLink1(QtNetwork.QTcpSocket):
|
||||
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 @@ class PJLink1(QtNetwork.QTcpSocket):
|
||||
: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 @@ class PJLink1(QtNetwork.QTcpSocket):
|
||||
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 @@ class PJLink1(QtNetwork.QTcpSocket):
|
||||
|
||||
: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 @@ class PJLink1(QtNetwork.QTcpSocket):
|
||||
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
|
||||
|
44
tests/functional/openlp_core_lib/test_projector_constants.py
Normal file
44
tests/functional/openlp_core_lib/test_projector_constants.py
Normal file
@ -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')
|
@ -366,3 +366,18 @@ class TestPJLink(TestCase):
|
||||
# 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)
|
||||
|
Loading…
Reference in New Issue
Block a user