PJLink Updates N

This commit is contained in:
Ken Roberts 2017-12-25 00:44:30 -08:00
parent 6e2bc427a7
commit 598a373929
7 changed files with 1433 additions and 1185 deletions

View File

@ -32,9 +32,128 @@ log.debug('projector_constants loaded')
# Set common constants.
CR = chr(0x0D) # \r
LF = chr(0x0A) # \n
PJLINK_PORT = 4352
TIMEOUT = 30.0
PJLINK_CLASS = '1' # Default to class 1 until we query the projector
PJLINK_MAX_PACKET = 136
PJLINK_PREFIX = '%'
PJLINK_PORT = 4352
PJLINK_SUFFIX = CR
PJLINK_TIMEOUT = 30.0
# Error and status codes
S_OK = E_OK = 0 # E_OK included since I sometimes forget
# Error codes. Start at 200 so we don't duplicate system error codes.
E_GENERAL = 200 # Unknown error
E_NOT_CONNECTED = 201
E_UNDEFINED = 202 # PJLink ERR1
E_PARAMETER = 203 # PJLink ERR2
E_UNAVAILABLE = 204 # PJLink ERR3
E_PROJECTOR = 205 # PJLink ERR4
E_AUTHENTICATION = 206 # PJLink ERRA
E_NO_AUTHENTICATION = 207 # PJLink authentication mismatch between projector and program
E_PREFIX = 208 # PJLink invalid prefix for packet
E_CLASS = 209 # PJLink class version mismatch
E_INVALID_DATA = 210
E_WARN = 211
E_ERROR = 212
E_FAN = 213
E_LAMP = 214
E_TEMP = 215
E_COVER = 216
E_FILTER = 217
E_UNKNOWN = 218
# Remap Qt socket error codes to local error codes
E_CONNECTION_REFUSED = 230
E_REMOTE_HOST_CLOSED_CONNECTION = 231
E_HOST_NOT_FOUND = 232
E_SOCKET_ACCESS = 233
E_SOCKET_RESOURCE = 234
E_SOCKET_TIMEOUT = 235
E_DATAGRAM_TOO_LARGE = 236
E_NETWORK = 237
E_ADDRESS_IN_USE = 238
E_SOCKET_ADDRESS_NOT_AVAILABLE = 239
E_UNSUPPORTED_SOCKET_OPERATION = 240
E_PROXY_AUTHENTICATION_REQUIRED = 241
E_SLS_HANDSHAKE_FAILED = 242
E_UNFINISHED_SOCKET_OPERATION = 243
E_PROXY_CONNECTION_REFUSED = 244
E_PROXY_CONNECTION_CLOSED = 245
E_PROXY_CONNECTION_TIMEOUT = 246
E_PROXY_NOT_FOUND = 247
E_PROXY_PROTOCOL = 248
E_UNKNOWN_SOCKET_ERROR = 249
# Status codes start at 300
# Remap Qt socket states to local status codes
S_NOT_CONNECTED = 300
S_HOST_LOOKUP = 301
S_CONNECTING = 302
S_CONNECTED = 303
S_BOUND = 304
S_LISTENING = 305 # Listed as internal use only in QAbstractSocket
S_CLOSING = 306
# Projector states
S_INITIALIZE = 310
S_STATUS = 311
S_OFF = 312
S_STANDBY = 313
S_WARMUP = 314
S_ON = 315
S_COOLDOWN = 316
S_INFO = 317
# Information that does not affect status
S_NETWORK_IDLE = 400
S_NETWORK_SENDING = 401
S_NETWORK_RECEIVING = 402
# Map PJlink errors to local status
PJLINK_ERRORS = {
'ERRA': E_AUTHENTICATION, # Authentication error
'ERR1': E_UNDEFINED, # Undefined command error
'ERR2': E_PARAMETER, # Invalid parameter error
'ERR3': E_UNAVAILABLE, # Projector busy
'ERR4': E_PROJECTOR, # Projector or display failure
E_AUTHENTICATION: 'ERRA',
E_UNDEFINED: 'ERR1',
E_PARAMETER: 'ERR2',
E_UNAVAILABLE: 'ERR3',
E_PROJECTOR: 'ERR4'
}
# Map QAbstractSocketState enums to local status
QSOCKET_STATE = {
0: S_NOT_CONNECTED, # 'UnconnectedState',
1: S_HOST_LOOKUP, # 'HostLookupState',
2: S_CONNECTING, # 'ConnectingState',
3: S_CONNECTED, # 'ConnectedState',
4: S_BOUND, # 'BoundState',
5: S_LISTENING, # 'ListeningState' - Noted as "Internal Use Only" on Qt website
6: S_CLOSING, # 'ClosingState',
S_NOT_CONNECTED: 0,
S_HOST_LOOKUP: 1,
S_CONNECTING: 2,
S_CONNECTED: 3,
S_BOUND: 4,
S_LISTENING: 5,
S_CLOSING: 6
}
PROJECTOR_STATE = [
S_INITIALIZE,
S_STATUS,
S_OFF,
S_STANDBY,
S_WARMUP,
S_ON,
S_COOLDOWN,
S_INFO
]
# NOTE: Changed format to account for some commands are both class 1 and 2
PJLINK_VALID_CMD = {
'ACKN': {'version': ['2', ],
@ -144,227 +263,140 @@ PJLINK_VALID_CMD = {
}
}
# QAbstractSocketState enums converted to string
S_QSOCKET_STATE = {
0: 'QSocketState - UnconnectedState',
1: 'QSocketState - HostLookupState',
2: 'QSocketState - ConnectingState',
3: 'QSocketState - ConnectedState',
4: 'QSocketState - BoundState',
5: 'QSocketState - ListeningState (internal use only)',
6: 'QSocketState - ClosingState',
'UnconnectedState': 0,
'HostLookupState': 1,
'ConnectingState': 2,
'ConnectedState': 3,
'BoundState': 4,
'ListeningState': 5,
'ClosingState': 6
}
CONNECTION_ERRORS = [
E_ADDRESS_IN_USE,
E_CONNECTION_REFUSED,
E_DATAGRAM_TOO_LARGE,
E_HOST_NOT_FOUND,
E_NETWORK,
E_NOT_CONNECTED,
E_PROXY_AUTHENTICATION_REQUIRED,
E_PROXY_CONNECTION_CLOSED,
E_PROXY_CONNECTION_REFUSED,
E_PROXY_CONNECTION_TIMEOUT,
E_PROXY_NOT_FOUND,
E_PROXY_PROTOCOL,
E_REMOTE_HOST_CLOSED_CONNECTION,
E_SLS_HANDSHAKE_FAILED,
E_SOCKET_ACCESS,
E_SOCKET_ADDRESS_NOT_AVAILABLE,
E_SOCKET_RESOURCE,
E_SOCKET_TIMEOUT,
E_UNFINISHED_SOCKET_OPERATION,
E_UNKNOWN_SOCKET_ERROR,
E_UNSUPPORTED_SOCKET_OPERATION
]
# Error and status codes
S_OK = E_OK = 0 # E_OK included since I sometimes forget
# Error codes. Start at 200 so we don't duplicate system error codes.
E_GENERAL = 200 # Unknown error
E_NOT_CONNECTED = 201
E_FAN = 202
E_LAMP = 203
E_TEMP = 204
E_COVER = 205
E_FILTER = 206
E_NO_AUTHENTICATION = 207 # PIN set and no authentication set on projector
E_UNDEFINED = 208 # ERR1
E_PARAMETER = 209 # ERR2
E_UNAVAILABLE = 210 # ERR3
E_PROJECTOR = 211 # ERR4
E_INVALID_DATA = 212
E_WARN = 213
E_ERROR = 214
E_AUTHENTICATION = 215 # ERRA
E_CLASS = 216
E_PREFIX = 217
PROJECTOR_ERRORS = [
E_AUTHENTICATION,
E_CLASS,
E_INVALID_DATA,
E_NO_AUTHENTICATION,
E_PARAMETER,
E_PREFIX,
E_PROJECTOR,
E_UNAVAILABLE,
E_UNDEFINED,
E_UNKNOWN
]
# Remap Qt socket error codes to projector error codes
E_CONNECTION_REFUSED = 230
E_REMOTE_HOST_CLOSED_CONNECTION = 231
E_HOST_NOT_FOUND = 232
E_SOCKET_ACCESS = 233
E_SOCKET_RESOURCE = 234
E_SOCKET_TIMEOUT = 235
E_DATAGRAM_TOO_LARGE = 236
E_NETWORK = 237
E_ADDRESS_IN_USE = 238
E_SOCKET_ADDRESS_NOT_AVAILABLE = 239
E_UNSUPPORTED_SOCKET_OPERATION = 240
E_PROXY_AUTHENTICATION_REQUIRED = 241
E_SLS_HANDSHAKE_FAILED = 242
E_UNFINISHED_SOCKET_OPERATION = 243
E_PROXY_CONNECTION_REFUSED = 244
E_PROXY_CONNECTION_CLOSED = 245
E_PROXY_CONNECTION_TIMEOUT = 246
E_PROXY_NOT_FOUND = 247
E_PROXY_PROTOCOL = 248
E_UNKNOWN_SOCKET_ERROR = -1
# Status codes start at 300
S_NOT_CONNECTED = 300
S_CONNECTING = 301
S_CONNECTED = 302
S_INITIALIZE = 303
S_STATUS = 304
S_OFF = 305
S_STANDBY = 306
S_WARMUP = 307
S_ON = 308
S_COOLDOWN = 309
S_INFO = 310
# Information that does not affect status
S_NETWORK_SENDING = 400
S_NETWORK_RECEIVED = 401
CONNECTION_ERRORS = {
E_NOT_CONNECTED, E_NO_AUTHENTICATION, E_AUTHENTICATION, E_CLASS,
E_PREFIX, E_CONNECTION_REFUSED, E_REMOTE_HOST_CLOSED_CONNECTION,
E_HOST_NOT_FOUND, E_SOCKET_ACCESS, E_SOCKET_RESOURCE, E_SOCKET_TIMEOUT,
E_DATAGRAM_TOO_LARGE, E_NETWORK, E_ADDRESS_IN_USE, E_SOCKET_ADDRESS_NOT_AVAILABLE,
E_UNSUPPORTED_SOCKET_OPERATION, E_PROXY_AUTHENTICATION_REQUIRED,
E_SLS_HANDSHAKE_FAILED, E_UNFINISHED_SOCKET_OPERATION, E_PROXY_CONNECTION_REFUSED,
E_PROXY_CONNECTION_CLOSED, E_PROXY_CONNECTION_TIMEOUT, E_PROXY_NOT_FOUND,
E_PROXY_PROTOCOL, E_UNKNOWN_SOCKET_ERROR
}
PJLINK_ERRORS = {
'ERRA': E_AUTHENTICATION, # Authentication error
'ERR1': E_UNDEFINED, # Undefined command error
'ERR2': E_PARAMETER, # Invalid parameter error
'ERR3': E_UNAVAILABLE, # Projector busy
'ERR4': E_PROJECTOR, # Projector or display failure
E_AUTHENTICATION: 'ERRA',
E_UNDEFINED: 'ERR1',
E_PARAMETER: 'ERR2',
E_UNAVAILABLE: 'ERR3',
E_PROJECTOR: 'ERR4'
}
# Map error/status codes to string
ERROR_STRING = {
0: 'S_OK',
E_GENERAL: 'E_GENERAL',
E_NOT_CONNECTED: 'E_NOT_CONNECTED',
E_FAN: 'E_FAN',
E_LAMP: 'E_LAMP',
E_TEMP: 'E_TEMP',
E_COVER: 'E_COVER',
E_FILTER: 'E_FILTER',
E_AUTHENTICATION: 'E_AUTHENTICATION',
E_NO_AUTHENTICATION: 'E_NO_AUTHENTICATION',
E_UNDEFINED: 'E_UNDEFINED',
E_PARAMETER: 'E_PARAMETER',
E_UNAVAILABLE: 'E_UNAVAILABLE',
E_PROJECTOR: 'E_PROJECTOR',
E_INVALID_DATA: 'E_INVALID_DATA',
E_WARN: 'E_WARN',
E_ERROR: 'E_ERROR',
E_CLASS: 'E_CLASS',
E_PREFIX: 'E_PREFIX', # Last projector error
E_CONNECTION_REFUSED: 'E_CONNECTION_REFUSED', # First QtSocket error
E_REMOTE_HOST_CLOSED_CONNECTION: 'E_REMOTE_HOST_CLOSED_CONNECTION',
E_HOST_NOT_FOUND: 'E_HOST_NOT_FOUND',
E_SOCKET_ACCESS: 'E_SOCKET_ACCESS',
E_SOCKET_RESOURCE: 'E_SOCKET_RESOURCE',
E_SOCKET_TIMEOUT: 'E_SOCKET_TIMEOUT',
E_DATAGRAM_TOO_LARGE: 'E_DATAGRAM_TOO_LARGE',
E_NETWORK: 'E_NETWORK',
# Show status code as string
STATUS_CODE = {
E_ADDRESS_IN_USE: 'E_ADDRESS_IN_USE',
E_SOCKET_ADDRESS_NOT_AVAILABLE: 'E_SOCKET_ADDRESS_NOT_AVAILABLE',
E_UNSUPPORTED_SOCKET_OPERATION: 'E_UNSUPPORTED_SOCKET_OPERATION',
E_AUTHENTICATION: 'E_AUTHENTICATION',
E_CLASS: 'E_CLASS',
E_CONNECTION_REFUSED: 'E_CONNECTION_REFUSED',
E_COVER: 'E_COVER',
E_DATAGRAM_TOO_LARGE: 'E_DATAGRAM_TOO_LARGE',
E_ERROR: 'E_ERROR',
E_FAN: 'E_FAN',
E_FILTER: 'E_FILTER',
E_GENERAL: 'E_GENERAL',
E_HOST_NOT_FOUND: 'E_HOST_NOT_FOUND',
E_INVALID_DATA: 'E_INVALID_DATA',
E_LAMP: 'E_LAMP',
E_NETWORK: 'E_NETWORK',
E_NO_AUTHENTICATION: 'E_NO_AUTHENTICATION',
E_NOT_CONNECTED: 'E_NOT_CONNECTED',
E_PARAMETER: 'E_PARAMETER',
E_PREFIX: 'E_PREFIX',
E_PROJECTOR: 'E_PROJECTOR',
E_PROXY_AUTHENTICATION_REQUIRED: 'E_PROXY_AUTHENTICATION_REQUIRED',
E_SLS_HANDSHAKE_FAILED: 'E_SLS_HANDSHAKE_FAILED',
E_UNFINISHED_SOCKET_OPERATION: 'E_UNFINISHED_SOCKET_OPERATION',
E_PROXY_CONNECTION_REFUSED: 'E_PROXY_CONNECTION_REFUSED',
E_PROXY_CONNECTION_CLOSED: 'E_PROXY_CONNECTION_CLOSED',
E_PROXY_CONNECTION_REFUSED: 'E_PROXY_CONNECTION_REFUSED',
E_PROXY_CONNECTION_TIMEOUT: 'E_PROXY_CONNECTION_TIMEOUT',
E_PROXY_NOT_FOUND: 'E_PROXY_NOT_FOUND',
E_PROXY_PROTOCOL: 'E_PROXY_PROTOCOL',
E_UNKNOWN_SOCKET_ERROR: 'E_UNKNOWN_SOCKET_ERROR'
}
STATUS_STRING = {
S_NOT_CONNECTED: 'S_NOT_CONNECTED',
S_CONNECTING: 'S_CONNECTING',
S_CONNECTED: 'S_CONNECTED',
S_STATUS: 'S_STATUS',
S_OFF: 'S_OFF',
S_INITIALIZE: 'S_INITIALIZE',
S_STANDBY: 'S_STANDBY',
S_WARMUP: 'S_WARMUP',
S_ON: 'S_ON',
E_REMOTE_HOST_CLOSED_CONNECTION: 'E_REMOTE_HOST_CLOSED_CONNECTION',
E_SLS_HANDSHAKE_FAILED: 'E_SLS_HANDSHAKE_FAILED',
E_SOCKET_ACCESS: 'E_SOCKET_ACCESS',
E_SOCKET_ADDRESS_NOT_AVAILABLE: 'E_SOCKET_ADDRESS_NOT_AVAILABLE',
E_SOCKET_RESOURCE: 'E_SOCKET_RESOURCE',
E_SOCKET_TIMEOUT: 'E_SOCKET_TIMEOUT',
E_TEMP: 'E_TEMP',
E_UNAVAILABLE: 'E_UNAVAILABLE',
E_UNDEFINED: 'E_UNDEFINED',
E_UNFINISHED_SOCKET_OPERATION: 'E_UNFINISHED_SOCKET_OPERATION',
E_UNKNOWN: 'E_UNKNOWN',
E_UNKNOWN_SOCKET_ERROR: 'E_UNKNOWN_SOCKET_ERROR',
E_UNSUPPORTED_SOCKET_OPERATION: 'E_UNSUPPORTED_SOCKET_OPERATION',
E_WARN: 'E_WARN',
S_BOUND: 'S_BOUND',
S_COOLDOWN: 'S_COOLDOWN',
S_CLOSING: 'S_CLOSING',
S_CONNECTED: 'S_CONNECTED',
S_CONNECTING: 'S_CONNECTING',
S_HOST_LOOKUP: 'S_HOST_LOOKUP',
S_INFO: 'S_INFO',
S_INITIALIZE: 'S_INITIALIZE',
S_LISTENING: 'S_LISTENING',
S_NETWORK_RECEIVING: 'S_NETWORK_RECEIVING',
S_NETWORK_SENDING: 'S_NETWORK_SENDING',
S_NETWORK_RECEIVED: 'S_NETWORK_RECEIVED'
S_NETWORK_IDLE: 'S_NETWORK_IDLE',
S_NOT_CONNECTED: 'S_NOT_CONNECTED',
S_OFF: 'S_OFF',
S_OK: 'S_OK', # S_OK or E_OK
S_ON: 'S_ON',
S_STANDBY: 'S_STANDBY',
S_STATUS: 'S_STATUS',
S_WARMUP: 'S_WARMUP',
}
# Map error/status codes to message strings
ERROR_MSG = {
E_OK: translate('OpenLP.ProjectorConstants', 'OK'), # E_OK | S_OK
E_GENERAL: translate('OpenLP.ProjectorConstants', 'General projector error'),
E_NOT_CONNECTED: translate('OpenLP.ProjectorConstants', 'Not connected error'),
E_LAMP: translate('OpenLP.ProjectorConstants', 'Lamp error'),
E_FAN: translate('OpenLP.ProjectorConstants', 'Fan error'),
E_TEMP: translate('OpenLP.ProjectorConstants', 'High temperature detected'),
E_COVER: translate('OpenLP.ProjectorConstants', 'Cover open detected'),
E_FILTER: translate('OpenLP.ProjectorConstants', 'Check filter'),
E_AUTHENTICATION: translate('OpenLP.ProjectorConstants', 'Authentication Error'),
E_UNDEFINED: translate('OpenLP.ProjectorConstants', 'Undefined Command'),
E_PARAMETER: translate('OpenLP.ProjectorConstants', 'Invalid Parameter'),
E_UNAVAILABLE: translate('OpenLP.ProjectorConstants', 'Projector Busy'),
E_PROJECTOR: translate('OpenLP.ProjectorConstants', 'Projector/Display Error'),
E_INVALID_DATA: translate('OpenLP.ProjectorConstants', 'Invalid packet received'),
E_WARN: translate('OpenLP.ProjectorConstants', 'Warning condition detected'),
E_ERROR: translate('OpenLP.ProjectorConstants', 'Error condition detected'),
E_CLASS: translate('OpenLP.ProjectorConstants', 'PJLink class not supported'),
E_PREFIX: translate('OpenLP.ProjectorConstants', 'Invalid prefix character'),
E_CONNECTION_REFUSED: translate('OpenLP.ProjectorConstants',
'The connection was refused by the peer (or timed out)'),
E_REMOTE_HOST_CLOSED_CONNECTION: translate('OpenLP.ProjectorConstants',
'The remote host closed the connection'),
E_HOST_NOT_FOUND: translate('OpenLP.ProjectorConstants', 'The host address was not found'),
E_SOCKET_ACCESS: translate('OpenLP.ProjectorConstants',
'The socket operation failed because the application '
'lacked the required privileges'),
E_SOCKET_RESOURCE: translate('OpenLP.ProjectorConstants',
'The local system ran out of resources (e.g., too many sockets)'),
E_SOCKET_TIMEOUT: translate('OpenLP.ProjectorConstants',
'The socket operation timed out'),
E_DATAGRAM_TOO_LARGE: translate('OpenLP.ProjectorConstants',
'The datagram was larger than the operating system\'s limit'),
E_NETWORK: translate('OpenLP.ProjectorConstants',
'An error occurred with the network (Possibly someone pulled the plug?)'),
# Map status codes to message strings
STATUS_MSG = {
E_ADDRESS_IN_USE: translate('OpenLP.ProjectorConstants',
'The address specified with socket.bind() '
'is already in use and was set to be exclusive'),
E_SOCKET_ADDRESS_NOT_AVAILABLE: translate('OpenLP.ProjectorConstants',
'The address specified to socket.bind() '
'does not belong to the host'),
E_UNSUPPORTED_SOCKET_OPERATION: translate('OpenLP.ProjectorConstants',
'The requested socket operation is not supported by the local '
'operating system (e.g., lack of IPv6 support)'),
E_AUTHENTICATION: translate('OpenLP.ProjectorConstants', 'PJLink returned "ERRA: Authentication Error"'),
E_CONNECTION_REFUSED: translate('OpenLP.ProjectorConstants',
'The connection was refused by the peer (or timed out)'),
E_COVER: translate('OpenLP.ProjectorConstants', 'Projector cover open detected'),
E_CLASS: translate('OpenLP.ProjectorConstants', 'PJLink class not supported'),
E_DATAGRAM_TOO_LARGE: translate('OpenLP.ProjectorConstants',
"The datagram was larger than the operating system's limit"),
E_ERROR: translate('OpenLP.ProjectorConstants', 'Error condition detected'),
E_FAN: translate('OpenLP.ProjectorConstants', 'Projector fan error'),
E_FILTER: translate('OpenLP.ProjectorConstants', 'Projector check filter'),
E_GENERAL: translate('OpenLP.ProjectorConstants', 'General projector error'),
E_HOST_NOT_FOUND: translate('OpenLP.ProjectorConstants', 'The host address was not found'),
E_INVALID_DATA: translate('OpenLP.ProjectorConstants', 'PJLink invalid packet received'),
E_LAMP: translate('OpenLP.ProjectorConstants', 'Projector lamp error'),
E_NETWORK: translate('OpenLP.ProjectorConstants',
'An error occurred with the network (Possibly someone pulled the plug?)'),
E_NO_AUTHENTICATION: translate('OpenLP.ProjectorConstants', 'PJlink authentication Mismatch Error'),
E_NOT_CONNECTED: translate('OpenLP.ProjectorConstants', 'Projector not connected error'),
E_PARAMETER: translate('OpenLP.ProjectorConstants', 'PJLink returned "ERR2: Invalid Parameter"'),
E_PREFIX: translate('OpenLP.ProjectorConstants', 'PJLink Invalid prefix character'),
E_PROJECTOR: translate('OpenLP.ProjectorConstants', 'PJLink returned "ERR4: Projector/Display Error"'),
E_PROXY_AUTHENTICATION_REQUIRED: translate('OpenLP.ProjectorConstants',
'The socket is using a proxy, '
'and the proxy requires authentication'),
E_SLS_HANDSHAKE_FAILED: translate('OpenLP.ProjectorConstants',
'The SSL/TLS handshake failed'),
E_UNFINISHED_SOCKET_OPERATION: translate('OpenLP.ProjectorConstants',
'The last operation attempted has not finished yet '
'(still in progress in the background)'),
E_PROXY_CONNECTION_REFUSED: translate('OpenLP.ProjectorConstants',
'Could not contact the proxy server because the connection '
'to that server was denied'),
E_PROXY_CONNECTION_CLOSED: translate('OpenLP.ProjectorConstants',
'The connection to the proxy server was closed unexpectedly '
'(before the connection to the final peer was established)'),
E_PROXY_CONNECTION_REFUSED: translate('OpenLP.ProjectorConstants',
'Could not contact the proxy server because the connection '
'to that server was denied'),
E_PROXY_CONNECTION_TIMEOUT: translate('OpenLP.ProjectorConstants',
'The connection to the proxy server timed out or the proxy '
'server stopped responding in the authentication phase.'),
@ -373,51 +405,91 @@ ERROR_MSG = {
E_PROXY_PROTOCOL: translate('OpenLP.ProjectorConstants',
'The connection negotiation with the proxy server failed because the '
'response from the proxy server could not be understood'),
E_UNKNOWN_SOCKET_ERROR: translate('OpenLP.ProjectorConstants', 'An unidentified error occurred'),
S_NOT_CONNECTED: translate('OpenLP.ProjectorConstants', 'Not connected'),
S_CONNECTING: translate('OpenLP.ProjectorConstants', 'Connecting'),
E_REMOTE_HOST_CLOSED_CONNECTION: translate('OpenLP.ProjectorConstants',
'The remote host closed the connection'),
E_SLS_HANDSHAKE_FAILED: translate('OpenLP.ProjectorConstants',
'The SSL/TLS handshake failed'),
E_SOCKET_ADDRESS_NOT_AVAILABLE: translate('OpenLP.ProjectorConstants',
'The address specified to socket.bind() '
'does not belong to the host'),
E_SOCKET_ACCESS: translate('OpenLP.ProjectorConstants',
'The socket operation failed because the application '
'lacked the required privileges'),
E_SOCKET_RESOURCE: translate('OpenLP.ProjectorConstants',
'The local system ran out of resources (e.g., too many sockets)'),
E_SOCKET_TIMEOUT: translate('OpenLP.ProjectorConstants',
'The socket operation timed out'),
E_TEMP: translate('OpenLP.ProjectorConstants', 'Projector high temperature detected'),
E_UNAVAILABLE: translate('OpenLP.ProjectorConstants', 'PJLink returned "ERR3: Busy"'),
E_UNDEFINED: translate('OpenLP.ProjectorConstants', 'PJLink returned "ERR1: Undefined Command"'),
E_UNFINISHED_SOCKET_OPERATION: translate('OpenLP.ProjectorConstants',
'The last operation attempted has not finished yet '
'(still in progress in the background)'),
E_UNKNOWN: translate('OpenLP.ProjectorConstants', 'Unknown condiction detected'),
E_UNKNOWN_SOCKET_ERROR: translate('OpenLP.ProjectorConstants', 'An unidentified socket error occurred'),
E_UNSUPPORTED_SOCKET_OPERATION: translate('OpenLP.ProjectorConstants',
'The requested socket operation is not supported by the local '
'operating system (e.g., lack of IPv6 support)'),
E_WARN: translate('OpenLP.ProjectorConstants', 'Warning condition detected'),
S_BOUND: translate('OpenLP.ProjectorConstants', 'Socket is bount to an address or port'),
S_CLOSING: translate('OpenLP.ProjectorConstants', 'Socket is about to close'),
S_CONNECTED: translate('OpenLP.ProjectorConstants', 'Connected'),
S_STATUS: translate('OpenLP.ProjectorConstants', 'Getting status'),
S_OFF: translate('OpenLP.ProjectorConstants', 'Off'),
S_INITIALIZE: translate('OpenLP.ProjectorConstants', 'Initialize in progress'),
S_STANDBY: translate('OpenLP.ProjectorConstants', 'Power in standby'),
S_WARMUP: translate('OpenLP.ProjectorConstants', 'Warmup in progress'),
S_ON: translate('OpenLP.ProjectorConstants', 'Power is on'),
S_CONNECTING: translate('OpenLP.ProjectorConstants', 'Connecting'),
S_COOLDOWN: translate('OpenLP.ProjectorConstants', 'Cooldown in progress'),
S_HOST_LOOKUP: translate('OpenLP.ProjectorConstants', 'Performing a host name lookup'),
S_INFO: translate('OpenLP.ProjectorConstants', 'Projector Information available'),
S_INITIALIZE: translate('OpenLP.ProjectorConstants', 'Initialize in progress'),
S_LISTENING: translate('OpenLP.ProjectorConstants', 'Socket it listening (internal use only)'),
S_NETWORK_IDLE: translate('OpenLP.ProjectorConstants', 'No network activity at this time'),
S_NETWORK_RECEIVING: translate('OpenLP.ProjectorConstants', 'Received data'),
S_NETWORK_SENDING: translate('OpenLP.ProjectorConstants', 'Sending data'),
S_NETWORK_RECEIVED: translate('OpenLP.ProjectorConstants', 'Received data')
S_NOT_CONNECTED: translate('OpenLP.ProjectorConstants', 'Not Connected'),
S_OFF: translate('OpenLP.ProjectorConstants', 'Off'),
S_OK: translate('OpenLP.ProjectorConstants', 'OK'),
S_ON: translate('OpenLP.ProjectorConstants', 'Power is on'),
S_STANDBY: translate('OpenLP.ProjectorConstants', 'Power in standby'),
S_STATUS: translate('OpenLP.ProjectorConstants', 'Getting status'),
S_WARMUP: translate('OpenLP.ProjectorConstants', 'Warmup in progress'),
}
# Map ERST return code positions to equipment
# Map ERST reply positions to equipment
PJLINK_ERST_LIST = {
"FAN": translate('OpenLP.PJLink', 'Fan'),
"LAMP": translate('OpenLP.PJLink', 'Lamp'),
"TEMP": translate('OpenLP.PJLink', 'Temperature'),
"COVER": translate('OpenLP.PJLink', 'Cover'),
"FILTER": translate('OpenLP.PJLink', 'Filter'),
"OTHER": translate('OpenPL.PJLink', 'Other')
}
# Map projector item to ERST data position
PJLINK_ERST_DATA = {
'DATA_LENGTH': 6,
0: 'FAN',
1: 'LAMP',
2: 'TEMP',
3: 'COVER',
4: 'FILTER',
5: 'OTHER',
'DATA_LENGTH': 6, # Zero based so enums are 0-5
'FAN': 0,
'LAMP': 1,
'TEMP': 2,
'COVER': 3,
'FILTER': 4,
'OTHER': 5
'OTHER': 5,
0: 'FAN',
1: 'LAMP',
2: 'TEMP',
3: 'COVER',
4: 'FILTER',
5: 'OTHER'
}
# Map for ERST return codes to string
# Map ERST reply codes to string
PJLINK_ERST_STATUS = {
'0': 'OK',
'1': ERROR_STRING[E_WARN],
'2': ERROR_STRING[E_ERROR],
'OK': '0',
E_OK: '0',
'0': S_OK,
'1': E_WARN,
'2': E_ERROR,
S_OK: '0',
E_WARN: '1',
E_ERROR: '2'
}
# Map for POWR return codes to status code
# Map POWR return codes to status code
PJLINK_POWR_STATUS = {
'0': S_STANDBY,
'1': S_ON,
@ -485,3 +557,7 @@ for source in PJLINK_DEFAULT_SOURCES:
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])
# Remove temp variables so they don't become part of the module dict
del(source)
del(item)
del(label)

View File

@ -35,9 +35,25 @@ from openlp.core.common.registry import RegistryBase
from openlp.core.common.settings import Settings
from openlp.core.lib.ui import create_widget_action
from openlp.core.projectors import DialogSourceStyle
from openlp.core.projectors.constants import ERROR_MSG, ERROR_STRING, E_AUTHENTICATION, E_ERROR, \
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.projectors.constants import \
E_AUTHENTICATION, \
E_ERROR, \
E_NETWORK, \
E_NOT_CONNECTED, \
E_UNKNOWN_SOCKET_ERROR, \
S_CONNECTED, \
S_CONNECTING, \
S_COOLDOWN, \
S_INITIALIZE, \
S_NOT_CONNECTED, \
S_OFF, \
S_ON, \
S_STANDBY, \
S_WARMUP, \
STATUS_CODE, \
STATUS_MSG, \
QSOCKET_STATE
from openlp.core.projectors.db import ProjectorDB
from openlp.core.projectors.pjlink import PJLink, PJLinkUDP
from openlp.core.projectors.editform import ProjectorEditForm
@ -440,11 +456,12 @@ class ProjectorManager(QtWidgets.QWidget, RegistryBase, UiProjectorManager, LogM
:param opt: Needed by PyQt5
"""
projector = item.data(QtCore.Qt.UserRole)
if projector.link.state() != projector.link.ConnectedState:
if QSOCKET_STATE[projector.link.state()] != S_CONNECTED:
try:
log.debug('ProjectorManager: Calling connect_to_host() on "{ip}"'.format(ip=projector.link.ip))
projector.link.connect_to_host()
except:
pass
log.debug('ProjectorManager: "{ip}" already connected - skipping'.format(ip=projector.link.ip))
return
def on_connect_projector(self, opt=None):
@ -647,7 +664,7 @@ class ProjectorManager(QtWidgets.QWidget, RegistryBase, UiProjectorManager, LogM
'Other info'),
data=projector.link.other_info)
message += '<b>{title}</b>: {data}<br />'.format(title=translate('OpenLP.ProjectorManager', 'Power status'),
data=ERROR_MSG[projector.link.power])
data=STATUS_MSG[projector.link.power])
message += '<b>{title}</b>: {data}<br />'.format(title=translate('OpenLP.ProjectorManager', 'Shutter is'),
data=translate('OpenLP.ProjectorManager', 'Closed')
if projector.link.shutter
@ -692,7 +709,7 @@ class ProjectorManager(QtWidgets.QWidget, RegistryBase, UiProjectorManager, LogM
else:
message += '<b>{data}</b>'.format(data=translate('OpenLP.ProjectorManager', 'Current errors/warnings'))
for (key, val) in projector.link.projector_errors.items():
message += '<b>{key}</b>: {data}<br />'.format(key=key, data=ERROR_MSG[val])
message += '<b>{key}</b>: {data}<br />'.format(key=key, data=STATUS_MSG[val])
QtWidgets.QMessageBox.information(self, translate('OpenLP.ProjectorManager', 'Projector Information'), message)
def _add_projector(self, projector):
@ -817,31 +834,18 @@ class ProjectorManager(QtWidgets.QWidget, RegistryBase, UiProjectorManager, LogM
if ip == list_item.link.ip:
item = list_item
break
message = translate('OpenLP.ProjectorManager', 'No message') if msg is None else msg
if status in STATUS_STRING:
status_code = STATUS_STRING[status]
message = ERROR_MSG[status] if msg is None else msg
elif status in ERROR_STRING:
status_code = ERROR_STRING[status]
message = ERROR_MSG[status] if msg is None else msg
else:
status_code = status
message = ERROR_MSG[status] if msg is None else msg
log.debug('({name}) updateStatus(status={status}) message: "{message}"'.format(name=item.link.name,
status=status_code,
message=message))
if status in STATUS_ICONS:
if item.status == status:
return
item.status = status
item.icon = QtGui.QIcon(QtGui.QPixmap(STATUS_ICONS[status]))
if status in ERROR_STRING:
status_code = ERROR_STRING[status]
elif status in STATUS_STRING:
status_code = STATUS_STRING[status]
log.debug('({name}) Updating icon with {code}'.format(name=item.link.name, code=status_code))
item.widget.setIcon(item.icon)
self.update_icons()
if item is None:
log.error('ProjectorManager: Unknown item "{ip}" - not updating status'.format(ip=ip))
return
elif item.status == status:
log.debug('ProjectorManager: No status change for "{ip}" - not updating status'.format(ip=ip))
return
item.status = status
item.icon = QtGui.QIcon(QtGui.QPixmap(STATUS_ICONS[status]))
log.debug('({name}) Updating icon with {code}'.format(name=item.link.name, code=STATUS_CODE[status]))
item.widget.setIcon(item.icon)
return self.update_icons()
def get_toolbar_item(self, name, enabled=False, hidden=False):
item = self.one_toolbar.findChild(QtWidgets.QAction, name)
@ -877,7 +881,7 @@ class ProjectorManager(QtWidgets.QWidget, RegistryBase, UiProjectorManager, LogM
self.get_toolbar_item('show_projector_multiple', hidden=True)
elif count == 1:
projector = self.projector_list_widget.selectedItems()[0].data(QtCore.Qt.UserRole)
connected = projector.link.state() == projector.link.ConnectedState
connected = QSOCKET_STATE[projector.link.state()] == S_CONNECTED
power = projector.link.power == S_ON
self.get_toolbar_item('connect_projector_multiple', hidden=True)
self.get_toolbar_item('disconnect_projector_multiple', hidden=True)

View File

@ -54,11 +54,12 @@ from PyQt5 import QtCore, QtNetwork
from openlp.core.common import qmd5_hash
from openlp.core.common.i18n import translate
from openlp.core.projectors.constants import CONNECTION_ERRORS, CR, ERROR_MSG, ERROR_STRING, \
E_AUTHENTICATION, E_CONNECTION_REFUSED, E_GENERAL, E_INVALID_DATA, E_NETWORK, E_NOT_CONNECTED, E_OK, \
E_PARAMETER, E_PROJECTOR, E_SOCKET_TIMEOUT, E_UNAVAILABLE, E_UNDEFINED, PJLINK_ERRORS, PJLINK_ERST_DATA, \
PJLINK_ERST_STATUS, PJLINK_MAX_PACKET, PJLINK_PORT, PJLINK_POWR_STATUS, PJLINK_VALID_CMD, \
STATUS_STRING, S_CONNECTED, S_CONNECTING, S_INFO, S_NOT_CONNECTED, S_OFF, S_OK, S_ON, S_QSOCKET_STATE, S_STATUS
from openlp.core.projectors.constants import CONNECTION_ERRORS, PJLINK_CLASS, PJLINK_DEFAULT_CODES, PJLINK_ERRORS, \
PJLINK_ERST_DATA, PJLINK_ERST_STATUS, PJLINK_MAX_PACKET, PJLINK_PREFIX, PJLINK_PORT, PJLINK_POWR_STATUS, \
PJLINK_SUFFIX, PJLINK_VALID_CMD, PROJECTOR_STATE, STATUS_CODE, STATUS_MSG, QSOCKET_STATE, \
E_AUTHENTICATION, E_CONNECTION_REFUSED, E_GENERAL, E_INVALID_DATA, E_NETWORK, E_NOT_CONNECTED, \
E_OK, E_SOCKET_TIMEOUT, \
S_CONNECTED, S_CONNECTING, S_INFO, S_NOT_CONNECTED, S_OFF, S_OK, S_ON, S_STATUS
log = logging.getLogger(__name__)
log.debug('pjlink loaded')
@ -69,12 +70,9 @@ __all__ = ['PJLink']
SocketError = QtNetwork.QAbstractSocket.SocketError
SocketSTate = QtNetwork.QAbstractSocket.SocketState
PJLINK_PREFIX = '%'
PJLINK_CLASS = '1' # Default to class 1 until we query the projector
# Add prefix here, but defer linkclass expansion until later when we have the actual
# PJLink class for the command
PJLINK_HEADER = '{prefix}{{linkclass}}'.format(prefix=PJLINK_PREFIX)
PJLINK_SUFFIX = CR
class PJLinkUDP(QtNetwork.QUdpSocket):
@ -136,8 +134,9 @@ class PJLinkCommands(object):
"""
Initialize instance variables. Also used to reset projector-specific information to default.
"""
conn_state = STATUS_CODE[QSOCKET_STATE[self.state()]]
log.debug('({ip}) reset_information() connect status is {state}'.format(ip=self.ip,
state=S_QSOCKET_STATE[self.state()]))
state=conn_state))
self.fan = None # ERST
self.filter_time = None # FILT
self.lamp = None # LAMP
@ -183,42 +182,25 @@ class PJLinkCommands(object):
# Due to some replies should stay as mixed-case, validate using separate uppercase check
_data = data.upper()
# 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))
if cmd not in self.pjlink_functions:
log.warning('({ip}) Unable to process command="{cmd}" (Future option?)'.format(ip=self.ip, cmd=cmd))
return
elif _data == 'OK':
log.debug('({ip}) Command "{cmd}" returned OK'.format(ip=self.ip, cmd=cmd))
# A command returned successfully, so do a query on command to verify status
return self.send_command(cmd=cmd)
elif cmd not in self.pjlink_functions:
log.warning('({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 == PJLINK_ERRORS[E_AUTHENTICATION]:
# Authentication error
log.error('({ip}) {cmd}: {err}'.format(ip=self.ip,
cmd=cmd,
err=STATUS_MSG[PJLINK_ERRORS[_data]]))
if PJLINK_ERRORS[_data] == E_AUTHENTICATION:
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 == PJLINK_ERRORS[E_UNDEFINED]:
# Projector does not recognize command
self.change_status(E_UNDEFINED, '{error}: "{data}"'.format(error=ERROR_MSG[E_UNDEFINED],
data=cmd))
elif _data == PJLINK_ERRORS[E_PARAMETER]:
# Invalid parameter
self.change_status(E_PARAMETER)
elif _data == PJLINK_ERRORS[E_UNAVAILABLE]:
# Projector busy
self.change_status(E_UNAVAILABLE)
elif _data == PJLINK_ERRORS[E_PROJECTOR]:
# Projector/display error
self.change_status(E_PROJECTOR)
return
return self.change_status(status=E_AUTHENTICATION)
# Command checks already passed
log.debug('({ip}) Calling function for {cmd}'.format(ip=self.ip, cmd=cmd))
self.pjlink_functions[cmd](data)
self.pjlink_functions[cmd](data=data)
def process_avmt(self, data):
"""
@ -313,22 +295,22 @@ class PJLinkCommands(object):
data[PJLINK_ERST_DATA['COVER']],
data[PJLINK_ERST_DATA['FILTER']],
data[PJLINK_ERST_DATA['OTHER']])
if fan != PJLINK_ERST_STATUS[E_OK]:
if fan != PJLINK_ERST_STATUS[S_OK]:
self.projector_errors[translate('OpenLP.ProjectorPJLink', 'Fan')] = \
PJLINK_ERST_STATUS[fan]
if lamp != PJLINK_ERST_STATUS[E_OK]:
if lamp != PJLINK_ERST_STATUS[S_OK]:
self.projector_errors[translate('OpenLP.ProjectorPJLink', 'Lamp')] = \
PJLINK_ERST_STATUS[lamp]
if temp != PJLINK_ERST_STATUS[E_OK]:
if temp != PJLINK_ERST_STATUS[S_OK]:
self.projector_errors[translate('OpenLP.ProjectorPJLink', 'Temperature')] = \
PJLINK_ERST_STATUS[temp]
if cover != PJLINK_ERST_STATUS[E_OK]:
if cover != PJLINK_ERST_STATUS[S_OK]:
self.projector_errors[translate('OpenLP.ProjectorPJLink', 'Cover')] = \
PJLINK_ERST_STATUS[cover]
if filt != PJLINK_ERST_STATUS[E_OK]:
if filt != PJLINK_ERST_STATUS[S_OK]:
self.projector_errors[translate('OpenLP.ProjectorPJLink', 'Filter')] = \
PJLINK_ERST_STATUS[filt]
if other != PJLINK_ERST_STATUS[E_OK]:
if other != PJLINK_ERST_STATUS[S_OK]:
self.projector_errors[translate('OpenLP.ProjectorPJLink', 'Other')] = \
PJLINK_ERST_STATUS[other]
return
@ -373,8 +355,18 @@ class PJLinkCommands(object):
:param data: Currently selected source
"""
# First, see if we have a valid input based on what is installed (if available)
if self.source_available is not None:
# We have available inputs, so verify it's in the list
if data not in self.source_available:
log.warn('({ip}) Input source not listed in available sources - ignoring'.format(ip=self.ip))
return
elif data not in PJLINK_DEFAULT_CODES:
# Hmm - no sources available yet, so check with PJLink defaults
log.warn('({ip}) Input source not listed as a PJLink available source - ignoring'.format(ip=self.ip))
return
self.source = data
log.info('({ip}) Setting data source to "{data}"'.format(ip=self.ip, data=self.source))
log.debug('({ip}) Setting data source to "{data}"'.format(ip=self.ip, data=self.source))
return
def process_inst(self, data):
@ -390,7 +382,6 @@ class PJLinkCommands(object):
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
@ -551,17 +542,15 @@ class PJLinkCommands(object):
return
elif 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:
if self.sw_version != data:
log.warning('({ip}) Projector software version does not match saved '
'software version'.format(ip=self.ip))
log.warning('({ip}) Saved: "{old}"'.format(ip=self.ip, old=self.sw_version))
log.warning('({ip}) Received: "{new}"'.format(ip=self.ip, new=data))
log.warning('({ip}) Saving new serial number as sw_version_received'.format(ip=self.ip))
self.sw_version_received = data
log.warning('({ip}) Updating software version'.format(ip=self.ip))
self.sw_version = data
self.db_update = True
class PJLink(QtNetwork.QTcpSocket, PJLinkCommands):
@ -678,7 +667,7 @@ class PJLink(QtNetwork.QTcpSocket, PJLinkCommands):
Retrieve information from projector that changes.
Normally called by timer().
"""
if self.state() != S_QSOCKET_STATE['ConnectedState']:
if QSOCKET_STATE[self.state()] != S_CONNECTED:
log.warning('({ip}) poll_loop(): Not connected - returning'.format(ip=self.ip))
return
log.debug('({ip}) poll_loop(): Updating projector status'.format(ip=self.ip))
@ -688,13 +677,8 @@ class PJLink(QtNetwork.QTcpSocket, PJLinkCommands):
self.timer.setInterval(self.poll_time)
# Restart timer
self.timer.start()
# These commands may change during connection
check_list = ['POWR', 'ERST', 'LAMP', 'AVMT', 'INPT']
if self.pjlink_class == '2':
check_list.extend(['FILT', 'FREZ'])
for command in check_list:
self.send_command(command)
# The following commands do not change, so only check them once
# Call them first in case other functions rely on something here
if self.power == S_ON and self.source_available is None:
self.send_command('INST')
if self.other_info is None:
@ -715,22 +699,28 @@ class PJLink(QtNetwork.QTcpSocket, PJLinkCommands):
self.send_command('RFIL')
if self.model_lamp is None:
self.send_command('RLMP')
# These commands may change during connection
check_list = ['POWR', 'ERST', 'LAMP', 'AVMT', 'INPT']
if self.pjlink_class == '2':
check_list.extend(['FILT', 'FREZ'])
for command in check_list:
self.send_command(command)
def _get_status(self, status):
"""
Helper to retrieve status/error codes and convert to strings.
:param status: Status/Error code
:returns: (Status/Error code, String)
:returns: tuple (-1 if code not INT, None)
:returns: tuple (string: code as string, None if no description)
:returns: tuple (string: code as string, string: Status/Error description)
"""
if not isinstance(status, int):
return -1, 'Invalid status code'
elif status in ERROR_STRING:
return ERROR_STRING[status], ERROR_MSG[status]
elif status in STATUS_STRING:
return STATUS_STRING[status], ERROR_MSG[status]
return -1, None
elif status not in STATUS_MSG:
return None, None
else:
return status, translate('OpenLP.PJLink', 'Unknown status')
return STATUS_CODE[status], STATUS_MSG[status]
def change_status(self, status, msg=None):
"""
@ -740,19 +730,27 @@ class PJLink(QtNetwork.QTcpSocket, PJLinkCommands):
:param status: Status code
:param msg: Optional message
"""
message = translate('OpenLP.PJLink', 'No message') if msg is None else msg
(code, message) = self._get_status(status)
if msg is not None:
message = msg
if status in STATUS_CODE:
log.debug('({ip}) Changing status to {status} '
'"{msg}"'.format(ip=self.ip,
status=STATUS_CODE[status],
msg=msg if msg is not None else STATUS_MSG[status]))
else:
log.warning('({ip}) Unknown status change code: {code}'.format(ip=self.ip,
code=status))
return
if status in CONNECTION_ERRORS:
# Projector, connection state
self.projector_status = self.error_status = self.status_connect = E_NOT_CONNECTED
elif status >= S_NOT_CONNECTED and status < S_STATUS:
# Connection state error affects both socket and projector
self.error_status = status
self.status_connect = E_NOT_CONNECTED
elif status >= S_NOT_CONNECTED and status in QSOCKET_STATE:
# Socket connection status update
self.status_connect = status
self.projector_status = S_NOT_CONNECTED
elif status <= S_INFO:
self.status_connect = S_CONNECTED
elif status >= S_NOT_CONNECTED and status in PROJECTOR_STATE:
# Only affects the projector status
self.projector_status = status
# These log entries are for troubleshooting only
(status_code, status_message) = self._get_status(self.status_connect)
log.debug('({ip}) status_connect: {code}: "{message}"'.format(ip=self.ip,
code=status_code,
@ -765,6 +763,15 @@ class PJLink(QtNetwork.QTcpSocket, PJLinkCommands):
log.debug('({ip}) error_status: {code}: "{message}"'.format(ip=self.ip,
code=status_code,
message=status_message if msg is None else msg))
# Now that we logged extra information for debugging, broadcast the original change/message
(code, message) = self._get_status(status)
if msg is not None:
message = msg
elif message is None:
# No message for status code
message = translate('OpenLP.PJLink', 'No message') if msg is None else msg
self.changeStatus.emit(self.ip, status, message)
self.projectorUpdateIcons.emit()
@ -794,7 +801,7 @@ class PJLink(QtNetwork.QTcpSocket, PJLinkCommands):
data = decode(read, 'utf-8')
# Possibility of extraneous data on input when reading.
# Clean out extraneous characters in buffer.
self.readLine(self.max_size)
self.read(2048)
log.debug('({ip}) check_login() read "{data}"'.format(ip=self.ip, data=data.strip()))
# At this point, we should only have the initial login prompt with
# possible authentication
@ -805,6 +812,7 @@ class PJLink(QtNetwork.QTcpSocket, PJLinkCommands):
# Invalid initial packet - close socket
log.error('({ip}) Invalid initial packet received - closing socket'.format(ip=self.ip))
return self.disconnect_from_host()
# Convert the initial login prompt with the expected PJLink normal command format for processing
log.debug('({ip}) check_login(): Formatting initial connection prompt to PJLink packet'.format(ip=self.ip))
return self.get_data('{start}{clss}{data}'.format(start=PJLINK_PREFIX,
clss='1',
@ -895,7 +903,7 @@ class PJLink(QtNetwork.QTcpSocket, PJLinkCommands):
Get data from TCP socket.
"""
log.debug('({ip}) get_socket(): Reading data'.format(ip=self.ip))
if self.state() != self.ConnectedState:
if QSOCKET_STATE[self.state()] != S_CONNECTED:
log.debug('({ip}) get_socket(): Not connected - returning'.format(ip=self.ip))
self.send_busy = False
return
@ -907,8 +915,7 @@ class PJLink(QtNetwork.QTcpSocket, PJLinkCommands):
log.debug('({ip}) get_socket(): No data available (-1)'.format(ip=self.ip))
return self.receive_data_signal()
self.socket_timer.stop()
self.get_data(buff=read, ip=self.ip)
return self.receive_data_signal()
return self.get_data(buff=read, ip=self.ip)
def get_data(self, buff, ip=None):
"""
@ -927,13 +934,17 @@ class PJLink(QtNetwork.QTcpSocket, PJLinkCommands):
data = data_in.strip()
# Initial packet checks
if (len(data) < 7):
return self._trash_buffer(msg='get_data(): Invalid packet - length')
self._trash_buffer(msg='get_data(): Invalid packet - length')
return self.receive_data_signal()
elif len(data) > self.max_size:
return self._trash_buffer(msg='get_data(): Invalid packet - too long')
self._trash_buffer(msg='get_data(): Invalid packet - too long')
return self.receive_data_signal()
elif not data.startswith(PJLINK_PREFIX):
return self._trash_buffer(msg='get_data(): Invalid packet - PJLink prefix missing')
self._trash_buffer(msg='get_data(): Invalid packet - PJLink prefix missing')
return self.receive_data_signal()
elif '=' not in data:
return self._trash_buffer(msg='get_data(): Invalid reply - Does not have "="')
self._trash_buffer(msg='get_data(): Invalid reply - Does not have "="')
return self.receive_data_signal()
log.debug('({ip}) get_data(): Checking new data "{data}"'.format(ip=self.ip, data=data))
header, data = data.split('=')
# At this point, the header should contain:
@ -947,15 +958,17 @@ class PJLink(QtNetwork.QTcpSocket, PJLinkCommands):
except ValueError as e:
self.change_status(E_INVALID_DATA)
log.warning('({ip}) get_data(): Received data: "{data}"'.format(ip=self.ip, data=data_in))
return self._trash_buffer('get_data(): Expected header + command + data')
self._trash_buffer('get_data(): Expected header + command + data')
return self.receive_data_signal()
if cmd not in PJLINK_VALID_CMD:
log.warning('({ip}) get_data(): Invalid packet - unknown command "{data}"'.format(ip=self.ip, data=cmd))
return self._trash_buffer(msg='get_data(): Unknown command "{data}"'.format(data=cmd))
self._trash_buffer(msg='get_data(): Unknown command "{data}"'.format(data=cmd))
return self.receive_data_signal()
if int(self.pjlink_class) < int(version):
log.warning('({ip}) get_data(): Projector returned class reply higher '
'than projector stated class'.format(ip=self.ip))
self.send_busy = False
return self.process_command(cmd, data)
self.process_command(cmd, data)
return self.receive_data_signal()
@QtCore.pyqtSlot(QtNetwork.QAbstractSocket.SocketError)
def get_error(self, err):
@ -993,7 +1006,7 @@ class PJLink(QtNetwork.QTcpSocket, PJLinkCommands):
:param salt: Optional salt for md5 hash initial authentication
:param priority: Option to send packet now rather than queue it up
"""
if self.state() != self.ConnectedState:
if QSOCKET_STATE[self.state()] != S_CONNECTED:
log.warning('({ip}) send_command(): Not connected - returning'.format(ip=self.ip))
return self.reset_information()
if cmd not in PJLINK_VALID_CMD:
@ -1018,7 +1031,7 @@ class PJLink(QtNetwork.QTcpSocket, PJLinkCommands):
header=header,
command=cmd,
options=opts,
suffix=CR)
suffix=PJLINK_SUFFIX)
if out in self.priority_queue:
log.debug('({ip}) send_command(): Already in priority queue - skipping'.format(ip=self.ip))
elif out in self.send_queue:
@ -1044,9 +1057,10 @@ class PJLink(QtNetwork.QTcpSocket, PJLinkCommands):
"""
# Funny looking data check, but it's a quick check for data=None
log.debug('({ip}) _send_command(data="{data}")'.format(ip=self.ip, data=data.strip() if data else data))
conn_state = STATUS_CODE[QSOCKET_STATE[self.state()]]
log.debug('({ip}) _send_command(): Connection status: {data}'.format(ip=self.ip,
data=S_QSOCKET_STATE[self.state()]))
if self.state() != self.ConnectedState:
data=conn_state))
if QSOCKET_STATE[self.state()] != S_CONNECTED:
log.debug('({ip}) _send_command() Not connected - abort'.format(ip=self.ip))
self.send_busy = False
return self.disconnect_from_host()
@ -1088,10 +1102,11 @@ class PJLink(QtNetwork.QTcpSocket, PJLinkCommands):
"""
Initiate connection to projector.
"""
log.debug('{ip}) connect_to_host(): Starting connection'.format(ip=self.ip))
if self.state() == self.ConnectedState:
log.debug('({ip}) connect_to_host(): Starting connection'.format(ip=self.ip))
if QSOCKET_STATE[self.state()] == S_CONNECTED:
log.warning('({ip}) connect_to_host(): Already connected - returning'.format(ip=self.ip))
return
self.error_status = S_OK
self.change_status(S_CONNECTING)
self.connectToHost(self.ip, self.port if isinstance(self.port, int) else int(self.port))
@ -1100,12 +1115,13 @@ class PJLink(QtNetwork.QTcpSocket, PJLinkCommands):
"""
Close socket and cleanup.
"""
if abort or self.state() != self.ConnectedState:
if abort or QSOCKET_STATE[self.state()] != S_NOT_CONNECTED:
if abort:
log.warning('({ip}) disconnect_from_host(): Aborting connection'.format(ip=self.ip))
self.abort()
else:
log.warning('({ip}) disconnect_from_host(): Not connected'.format(ip=self.ip))
self.disconnectFromHost()
self.disconnectFromHost()
try:
self.readyRead.disconnect(self.get_socket)
except TypeError:
@ -1201,7 +1217,7 @@ class PJLink(QtNetwork.QTcpSocket, PJLinkCommands):
elif src not in self.source_available:
return
log.debug('({ip}) Setting input source to "{data}"'.format(ip=self.ip, data=src))
self.send_command(cmd='INPT', opts=src)
self.send_command(cmd='INPT', opts=src, priority=True)
self.poll_loop()
def set_power_on(self):
@ -1209,7 +1225,7 @@ class PJLink(QtNetwork.QTcpSocket, PJLinkCommands):
Send command to turn power to on.
"""
log.debug('({ip}) Setting POWR to 1 (on)'.format(ip=self.ip))
self.send_command(cmd='POWR', opts='1')
self.send_command(cmd='POWR', opts='1', priority=True)
self.poll_loop()
def set_power_off(self):
@ -1217,7 +1233,7 @@ class PJLink(QtNetwork.QTcpSocket, PJLinkCommands):
Send command to turn power to standby.
"""
log.debug('({ip}) Setting POWR to 0 (standby)'.format(ip=self.ip))
self.send_command(cmd='POWR', opts='0')
self.send_command(cmd='POWR', opts='0', priority=True)
self.poll_loop()
def set_shutter_closed(self):
@ -1225,7 +1241,7 @@ class PJLink(QtNetwork.QTcpSocket, PJLinkCommands):
Send command to set shutter to closed position.
"""
log.debug('({ip}) Setting AVMT to 11 (shutter closed)'.format(ip=self.ip))
self.send_command(cmd='AVMT', opts='11')
self.send_command(cmd='AVMT', opts='11', priority=True)
self.poll_loop()
def set_shutter_open(self):
@ -1233,8 +1249,9 @@ class PJLink(QtNetwork.QTcpSocket, PJLinkCommands):
Send command to set shutter to open position.
"""
log.debug('({ip}) Setting AVMT to "10" (shutter open)'.format(ip=self.ip))
self.send_command(cmd='AVMT', opts='10')
self.send_command(cmd='AVMT', opts='10', priority=True)
self.poll_loop()
self.projectorUpdateIcons.emit()
def receive_data_signal(self):
"""

View File

@ -23,12 +23,51 @@
Package to test the openlp.core.projectors.constants module.
"""
from unittest import TestCase
from openlp.core.projectors import constants
from openlp.core.projectors.constants import STATUS_CODE, STATUS_MSG
class TestProjectorConstants(TestCase):
"""
Test specific functions in the projector constants module.
"""
def test_defined_codes_in_status_code(self):
"""
Test defined status/error codes have equivalent strings
"""
check = []
missing_str = []
# GIVEN: List of defined E_* and S_* items defined in constants
for item in constants.__dict__:
if item.startswith('E_') or item.startswith('S_'):
check.append(item)
# WHEN: Verify defined list against STATUS_STR
for item in check:
if constants.__dict__[item] not in STATUS_CODE:
missing_str.append(item)
# THEN: There should be no missing items
assert 0 == len(missing_str), 'Status string missing: {msg}'.format(msg=missing_str)
def test_status_code_in_status_message(self):
"""
Test if entries in STATUS_CODE have equivalent descriptions in STATUS_MSG
"""
missing_msg = []
# GIVEN: Test items
check = STATUS_CODE
# WHEN: Verify each entry in STATUS_MSG against STATUS_CODE
for item in check:
if item not in STATUS_MSG:
missing_msg.append(item)
# THEN: There should be no missing items
assert 0 == len(missing_msg), 'Status message missing: {msg}'.format(msg=missing_msg)
def test_build_pjlink_video_label(self):
"""
Test building PJLINK_DEFAULT_CODES dictionary

View File

@ -25,7 +25,7 @@ Package to test the openlp.core.projectors.pjlink base package.
from unittest import TestCase
from unittest.mock import call, patch, MagicMock
from openlp.core.projectors.constants import E_PARAMETER, ERROR_STRING, S_ON, S_CONNECTED, S_QSOCKET_STATE
from openlp.core.projectors.constants import E_PARAMETER, STATUS_CODE, S_ON, S_CONNECTED, QSOCKET_STATE
from openlp.core.projectors.db import Projector
from openlp.core.projectors.pjlink import PJLink
@ -65,7 +65,7 @@ class TestPJLinkBase(TestCase):
# as first parameter
mock_change_status.called_with(E_PARAMETER,
'change_status should have been called with "{}"'.format(
ERROR_STRING[E_PARAMETER]))
STATUS_CODE[E_PARAMETER]))
@patch.object(pjlink_test, 'disconnect_from_host')
def test_socket_abort(self, mock_disconnect):
@ -104,7 +104,7 @@ class TestPJLinkBase(TestCase):
"""
# GIVEN: Mocks and test data
mock_state = patch.object(self.pjlink_test, 'state').start()
mock_state.return_value = S_QSOCKET_STATE['ConnectedState']
mock_state.return_value = QSOCKET_STATE[S_CONNECTED]
mock_timer = patch.object(self.pjlink_test, 'timer').start()
mock_timer.interval.return_value = 10
mock_send_command = patch.object(self.pjlink_test, 'send_command').start()
@ -117,7 +117,6 @@ class TestPJLinkBase(TestCase):
pjlink.manufacturer = None
pjlink.model = None
pjlink.pjlink_name = None
pjlink.ConnectedState = S_CONNECTED
call_list = [
call('POWR'),
call('ERST'),

View File

@ -24,209 +24,222 @@ Package to test the openlp.core.projectors.pjlink command routing.
"""
from unittest import TestCase
from unittest.mock import patch, MagicMock
from unittest.mock import MagicMock, call, patch
import openlp.core.projectors.pjlink
from openlp.core.projectors.constants import PJLINK_ERRORS, \
from openlp.core.projectors.constants import PJLINK_ERRORS, PJLINK_PREFIX, STATUS_MSG, \
E_AUTHENTICATION, E_PARAMETER, E_PROJECTOR, E_UNAVAILABLE, E_UNDEFINED
from openlp.core.projectors.db import Projector
from openlp.core.projectors.pjlink import PJLink
'''
from openlp.core.projectors.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, TEST1_DATA
pjlink_test = PJLink(Projector(**TEST1_DATA), pin=TEST_PIN, no_poll=True)
pjlink_test.ip = '127.0.0.1'
from tests.resources.projector.data import TEST1_DATA
class TestPJLinkRouting(TestCase):
"""
Tests for the PJLink module command routing
"""
def setUp(self):
'''
TestPJLinkCommands part 2 initialization
'''
self.pjlink_test = PJLink(Projector(**TEST1_DATA), no_poll=True)
def test_get_data_unknown_command(self):
"""
Test not a valid command
"""
# GIVEN: Test object
log_warning_text = [call('(111.111.111.111) get_data(): Invalid packet - unknown command "UNK"')]
log_debug_text = [call('(111.111.111.111) get_data(ip="111.111.111.111" buffer="b\'%1UNK=Huh?\'"'),
call('(111.111.111.111) get_data(): Checking new data "%1UNK=Huh?"')]
def tearDown(self):
'''
TestPJLinkCommands part 2 cleanups
'''
self.pjlink_test = None
with patch.object(openlp.core.projectors.pjlink, 'log') as mock_log, \
patch.object(openlp.core.projectors.pjlink.PJLink, '_trash_buffer') as mock_buffer:
@patch.object(openlp.core.projectors.pjlink, 'log')
def test_process_command_call_clss(self, mock_log):
pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True)
pjlink.pjlink_functions = MagicMock()
# WHEN: get_data called with an unknown command
pjlink.get_data(buff='{prefix}1UNK=Huh?'.format(prefix=PJLINK_PREFIX).encode('utf-8'))
# THEN: Appropriate log entries should have been made and methods called/not called
mock_log.debug.assert_has_calls(log_debug_text)
mock_log.warning.assert_has_calls(log_warning_text)
self.assertFalse(pjlink.pjlink_functions.called, 'Should not have accessed pjlink_functions')
self.assertTrue(mock_buffer.called, 'Should have called _trash_buffer')
def test_process_command_call_clss(self):
"""
Test process_command calls proper function
"""
# GIVEN: Test object
pjlink = pjlink_test
log_text = '(127.0.0.1) Calling function for CLSS'
mock_log.reset_mock()
mock_process_clss = MagicMock()
pjlink.pjlink_functions['CLSS'] = mock_process_clss
# GIVEN: Test object and mocks
log_debug_calls = [call('(111.111.111.111) Processing command "CLSS" with data "1"'),
call('(111.111.111.111) Calling function for CLSS')]
# WHEN: process_command is called with valid function and data
pjlink.process_command(cmd='CLSS', data='1')
with patch.object(openlp.core.projectors.pjlink, 'log') as mock_log, \
patch.object(openlp.core.projectors.pjlink.PJLink, 'process_clss') as mock_process_clss:
# THEN: Process method should have been called properly
mock_log.debug.assert_called_with(log_text)
mock_process_clss.assert_called_with('1')
pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True)
@patch.object(pjlink_test, 'change_status')
@patch.object(openlp.core.projectors.pjlink, 'log')
def test_process_command_err1(self, mock_log, mock_change_status):
"""
Test ERR1 - Undefined projector function
"""
# GIVEN: Test object
pjlink = pjlink_test
log_text = '(127.0.0.1) Projector returned error "ERR1"'
mock_change_status.reset_mock()
mock_log.reset_mock()
# WHEN: process_command is called with valid function and data
pjlink.process_command(cmd='CLSS', data='1')
# WHEN: process_command called with ERR1
pjlink.process_command(cmd='CLSS', data=PJLINK_ERRORS[E_UNDEFINED])
# THEN: Appropriate log entries should have been made and methods called
mock_log.debug.assert_has_calls(log_debug_calls)
mock_process_clss.assert_called_once_with(data='1')
# THEN: Error should be logged and status_change should be called
mock_change_status.assert_called_once_with(E_UNDEFINED, 'Undefined Command: "CLSS"')
mock_log.error.assert_called_with(log_text)
@patch.object(pjlink_test, 'change_status')
@patch.object(openlp.core.projectors.pjlink, 'log')
def test_process_command_err2(self, mock_log, mock_change_status):
"""
Test ERR2 - Parameter Error
"""
# GIVEN: Test object
pjlink = pjlink_test
log_text = '(127.0.0.1) Projector returned error "ERR2"'
mock_change_status.reset_mock()
mock_log.reset_mock()
# WHEN: process_command called with ERR2
pjlink.process_command(cmd='CLSS', data=PJLINK_ERRORS[E_PARAMETER])
# THEN: Error should be logged and status_change should be called
mock_change_status.assert_called_once_with(E_PARAMETER)
mock_log.error.assert_called_with(log_text)
@patch.object(pjlink_test, 'change_status')
@patch.object(openlp.core.projectors.pjlink, 'log')
def test_process_command_err3(self, mock_log, mock_change_status):
"""
Test ERR3 - Unavailable error
"""
# GIVEN: Test object
pjlink = pjlink_test
log_text = '(127.0.0.1) Projector returned error "ERR3"'
mock_change_status.reset_mock()
mock_log.reset_mock()
# WHEN: process_command called with ERR3
pjlink.process_command(cmd='CLSS', data=PJLINK_ERRORS[E_UNAVAILABLE])
# THEN: Error should be logged and status_change should be called
mock_change_status.assert_called_once_with(E_UNAVAILABLE)
mock_log.error.assert_called_with(log_text)
@patch.object(pjlink_test, 'change_status')
@patch.object(openlp.core.projectors.pjlink, 'log')
def test_process_command_err4(self, mock_log, mock_change_status):
"""
Test ERR3 - Unavailable error
"""
# GIVEN: Test object
pjlink = pjlink_test
log_text = '(127.0.0.1) Projector returned error "ERR4"'
mock_change_status.reset_mock()
mock_log.reset_mock()
# WHEN: process_command called with ERR3
pjlink.process_command(cmd='CLSS', data=PJLINK_ERRORS[E_PROJECTOR])
# THEN: Error should be logged and status_change should be called
mock_change_status.assert_called_once_with(E_PROJECTOR)
mock_log.error.assert_called_with(log_text)
@patch.object(pjlink_test, 'projectorAuthentication')
@patch.object(pjlink_test, 'change_status')
@patch.object(pjlink_test, 'disconnect_from_host')
@patch.object(openlp.core.projectors.pjlink, 'log')
def test_process_command_erra(self, mock_log, mock_disconnect, mock_change_status, mock_err_authenticate):
def test_process_command_erra(self):
"""
Test ERRA - Authentication Error
"""
# GIVEN: Test object
pjlink = pjlink_test
log_text = '(127.0.0.1) Projector returned error "ERRA"'
mock_change_status.reset_mock()
mock_log.reset_mock()
log_error_calls = [call('(111.111.111.111) PJLINK: {msg}'.format(msg=STATUS_MSG[E_AUTHENTICATION]))]
log_debug_calls = [call('(111.111.111.111) Processing command "PJLINK" with data "ERRA"')]
# WHEN: process_command called with ERRA
pjlink.process_command(cmd='CLSS', data=PJLINK_ERRORS[E_AUTHENTICATION])
with patch.object(openlp.core.projectors.pjlink, 'log') as mock_log, \
patch.object(openlp.core.projectors.pjlink.PJLink, 'process_pjlink') as mock_process_pjlink, \
patch.object(openlp.core.projectors.pjlink.PJLink, 'change_status') as mock_change_status, \
patch.object(openlp.core.projectors.pjlink.PJLink, 'disconnect_from_host') as mock_disconnect, \
patch.object(openlp.core.projectors.pjlink.PJLink, 'projectorAuthentication') as mock_authentication:
# THEN: Error should be logged and several methods should be called
self.assertTrue(mock_disconnect.called, 'disconnect_from_host should have been called')
mock_change_status.assert_called_once_with(E_AUTHENTICATION)
mock_log.error.assert_called_with(log_text)
pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True)
# WHEN: process_command called with ERRA
pjlink.process_command(cmd='PJLINK', data=PJLINK_ERRORS[E_AUTHENTICATION])
# THEN: Appropriate log entries should have been made and methods called/not called
mock_log.error.assert_has_calls(log_error_calls)
mock_log.debug.assert_has_calls(log_debug_calls)
self.assertTrue(mock_disconnect.called, 'disconnect_from_host should have been called')
mock_change_status.assert_called_once_with(status=E_AUTHENTICATION)
mock_authentication.emit.assert_called_once_with(pjlink.name)
mock_process_pjlink.assert_not_called()
def test_process_command_err1(self):
"""
Test ERR1 - Undefined projector function
"""
# GIVEN: Test object
log_error_text = [call('(111.111.111.111) CLSS: {msg}'.format(msg=STATUS_MSG[E_UNDEFINED]))]
log_debug_text = [call('(111.111.111.111) Processing command "CLSS" with data "ERR1"'),
call('(111.111.111.111) Calling function for CLSS')]
with patch.object(openlp.core.projectors.pjlink, 'log') as mock_log, \
patch.object(openlp.core.projectors.pjlink.PJLink, 'process_clss') as mock_process_clss:
pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True)
# WHEN: process_command called with ERR1
pjlink.process_command(cmd='CLSS', data=PJLINK_ERRORS[E_UNDEFINED])
# THEN: Appropriate log entries should have been made and methods called
mock_log.error.assert_has_calls(log_error_text)
mock_log.debug.assert_has_calls(log_debug_text)
mock_process_clss.assert_called_once_with(data=PJLINK_ERRORS[E_UNDEFINED])
def test_process_command_err2(self):
"""
Test ERR2 - Parameter Error
"""
# GIVEN: Test object
log_error_text = [call('(111.111.111.111) CLSS: {msg}'.format(msg=STATUS_MSG[E_PARAMETER]))]
log_debug_text = [call('(111.111.111.111) Processing command "CLSS" with data "ERR2"'),
call('(111.111.111.111) Calling function for CLSS')]
with patch.object(openlp.core.projectors.pjlink, 'log') as mock_log, \
patch.object(openlp.core.projectors.pjlink.PJLink, 'process_clss') as mock_process_clss:
pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True)
# WHEN: process_command called with ERR2
pjlink.process_command(cmd='CLSS', data=PJLINK_ERRORS[E_PARAMETER])
# THEN: Appropriate log entries should have been made and methods called/not called
mock_log.error.assert_has_calls(log_error_text)
mock_log.debug.assert_has_calls(log_debug_text)
mock_process_clss.assert_called_once_with(data=PJLINK_ERRORS[E_PARAMETER])
def test_process_command_err3(self):
"""
Test ERR3 - Unavailable error
"""
# GIVEN: Test object
log_error_text = [call('(111.111.111.111) CLSS: {msg}'.format(msg=STATUS_MSG[E_UNAVAILABLE]))]
log_debug_text = [call('(111.111.111.111) Processing command "CLSS" with data "ERR3"'),
call('(111.111.111.111) Calling function for CLSS')]
with patch.object(openlp.core.projectors.pjlink, 'log') as mock_log, \
patch.object(openlp.core.projectors.pjlink.PJLink, 'process_clss') as mock_process_clss:
pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True)
# WHEN: process_command called with ERR3
pjlink.process_command(cmd='CLSS', data=PJLINK_ERRORS[E_UNAVAILABLE])
# THEN: Appropriate log entries should have been made and methods called
mock_log.error.assert_has_calls(log_error_text)
mock_log.debug.assert_has_calls(log_debug_text)
mock_process_clss.assert_called_once_with(data=PJLINK_ERRORS[E_UNAVAILABLE])
def test_process_command_err4(self):
"""
Test ERR3 - Unavailable error
"""
# GIVEN: Test object
log_error_text = [call('(111.111.111.111) CLSS: {msg}'.format(msg=STATUS_MSG[E_PROJECTOR]))]
log_debug_text = [call('(111.111.111.111) Processing command "CLSS" with data "ERR4"'),
call('(111.111.111.111) Calling function for CLSS')]
with patch.object(openlp.core.projectors.pjlink, 'log') as mock_log, \
patch.object(openlp.core.projectors.pjlink.PJLink, 'process_clss') as mock_process_clss:
pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True)
# WHEN: process_command called with ERR4
pjlink.process_command(cmd='CLSS', data=PJLINK_ERRORS[E_PROJECTOR])
# THEN: Appropriate log entries should have been made and methods called
mock_log.error.assert_has_calls(log_error_text)
mock_log.debug.assert_has_calls(log_debug_text)
mock_process_clss.assert_called_once_with(data=PJLINK_ERRORS[E_PROJECTOR])
def test_process_command_future(self):
"""
Test command valid but no method to process yet
"""
# GIVEN: Initial mocks and data
mock_log = patch.object(openlp.core.projectors.pjlink, 'log').start()
mock_functions = patch.object(self.pjlink_test, 'pjlink_functions').start()
mock_functions.return_value = []
pjlink = self.pjlink_test
log_text = '(111.111.111.111) Unable to process command="CLSS" (Future option?)'
# WHEN: process_command called with an unknown command
pjlink.process_command(cmd='CLSS', data='DONT CARE')
# THEN: Error should be logged and no command called
self.assertFalse(mock_functions.called, 'Should not have gotten to the end of the method')
mock_log.warning.assert_called_once_with(log_text)
@patch.object(pjlink_test, 'pjlink_functions')
@patch.object(openlp.core.projectors.pjlink, 'log')
def test_process_command_invalid(self, mock_log, mock_functions):
"""
Test not a valid command
"""
# GIVEN: Test object
pjlink = pjlink_test
mock_functions.reset_mock()
mock_log.reset_mock()
log_warning_text = [call('(111.111.111.111) Unable to process command="CLSS" (Future option?)')]
log_debug_text = [call('(111.111.111.111) Processing command "CLSS" with data "Huh?"')]
# WHEN: process_command called with an unknown command
pjlink.process_command(cmd='Unknown', data='Dont Care')
log_text = '(127.0.0.1) Ignoring command="Unknown" (Invalid/Unknown)'
with patch.object(openlp.core.projectors.pjlink, 'log') as mock_log, \
patch.object(openlp.core.projectors.pjlink.PJLink, 'process_clss') as mock_process_clss:
# THEN: Error should be logged and no command called
self.assertFalse(mock_functions.called, 'Should not have gotten to the end of the method')
mock_log.error.assert_called_once_with(log_text)
pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True)
pjlink.pjlink_functions = MagicMock()
# WHEN: Processing a possible future command
pjlink.process_command(cmd='CLSS', data="Huh?")
# THEN: Appropriate log entries should have been made and methods called/not called
mock_log.debug.assert_has_calls(log_debug_text)
mock_log.warning.assert_has_calls(log_warning_text)
self.assertFalse(pjlink.pjlink_functions.called, 'Should not have accessed pjlink_functions')
self.assertFalse(mock_process_clss.called, 'Should not have called process_clss')
def test_process_command_ok(self):
"""
Test command returned success
"""
# GIVEN: Initial mocks and data
mock_log = patch.object(openlp.core.projectors.pjlink, 'log').start()
mock_send_command = patch.object(self.pjlink_test, 'send_command').start()
# GIVEN: Test object and mocks
log_debug_calls = [call('(111.111.111.111) Processing command "CLSS" with data "OK"'),
call('(111.111.111.111) Command "CLSS" returned OK')]
pjlink = self.pjlink_test
log_text = '(111.111.111.111) Command "POWR" returned OK'
with patch.object(openlp.core.projectors.pjlink, 'log') as mock_log, \
patch.object(openlp.core.projectors.pjlink.PJLink, 'send_command') as mock_send_command, \
patch.object(openlp.core.projectors.pjlink.PJLink, 'process_clss') as mock_process_clss:
# WHEN: process_command called with a command that returns OK
pjlink.process_command(cmd='POWR', data='OK')
pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True)
# THEN: Appropriate calls should have been made
mock_log.debug.assert_called_with(log_text)
mock_send_command.assert_called_once_with(cmd='POWR')
# WHEN: process_command is called with valid function and data
pjlink.process_command(cmd='CLSS', data='OK')
# THEN: Appropriate log entries should have been made and methods called
mock_log.debug.assert_has_calls(log_debug_calls)
mock_send_command.assert_called_once_with(cmd='CLSS')
mock_process_clss.assert_not_called()