Merge lp:~alisonken1/openlp/pjlink2-q into lp:openlp
- pjlink2-q
- Merge into trunk
Status: | Merged |
---|---|
Merged at revision: | 2818 |
Proposed branch: | lp:~alisonken1/openlp/pjlink2-q |
Merge into: | lp:openlp |
Diff against target: |
1482 lines (+437/-501) 8 files modified
openlp/core/projectors/constants.py (+53/-26) openlp/core/projectors/editform.py (+42/-27) openlp/core/projectors/manager.py (+3/-18) openlp/core/projectors/pjlink.py (+158/-149) tests/openlp_core/projectors/test_projector_db.py (+35/-2) tests/openlp_core/projectors/test_projector_pjlink_cmd_routing.py (+21/-21) tests/openlp_core/projectors/test_projector_pjlink_commands_02.py (+115/-4) tests/openlp_core/projectors/test_projector_pjlink_udp.py (+10/-254) |
To merge this branch: | bzr merge lp:~alisonken1/openlp/pjlink2-q |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Tim Bentley | Approve | ||
Phill | Approve | ||
Review via email: mp+343669@code.launchpad.net |
This proposal supersedes a proposal from 2018-04-20.
Commit message
PJLink2 update Q
Description of the change
PJLink2 update Q
-------
lp:~alisonken1/openlp/pjlink2-q (revision 2818)
https:/
https:/
https:/
https:/
https:/
https:/
https:/
https:/
-------
- Fix test_projector_
creating self.main_window steps
- Pep8 on openlp/
- Collapse import from projector.constants from line-per-item to multi-items-
- Change pjlink_functions to include class version for projector instance in command
- Set projector.editform to only allow editing IP address field for new entry only (used as db key entry)
- Collapse projector.manager imports from entry-per-line to compact import
- projector.pjlink:
- Merge pjlink_
- Change pjlink_functions to add instance-specific version to commands
- Fix command checks to changed pjlink_funcions
- Update process_clss to update PJLink version in pjlink_functions
- Update reset_information to update PJLink version in pjlink_functions
- renamed test_projectors
- renamed test_projectore
- Fix projector tests
- Fix list creation in constants (remove unneeded comma)
- Rename editform ip_text_show label to ip_text_label
- Refactor editform IP address check
- Add TODO on commented code block for breaking incoming packet into parts
Tim Bentley (trb143) wrote : Posted in a previous version of this proposal | # |
Ken Roberts (alisonken1) wrote : Posted in a previous version of this proposal | # |
> Sorry will clash with one of my fixes which has been merged
>
> See question
>> 'ACKN': {'version': ['2', ],
>> + 'default': '2',
>Why do we have a missing slot?
2 new commands in PJLink are only valid for version 2 and have no version 1 equivalent.
Missing slot is to maintain compatibility with version checker.
>> - def process_clss(self, data):
>> + def process_clss(self, data, *args, **kwargs):
>why args
Some commands take host,port arguments but not all of them do.
Since there is a common entry point, need to keep availability of consuming extra args/kwargs without causing exception due to extra parameters.
(Yes, there should be no args passed only keyword args, but better safe than sorry)
Ken Roberts (alisonken1) wrote : Posted in a previous version of this proposal | # |
which fix is clashing? I tried a merge from trunk and it merged fine.
Tim Bentley (trb143) wrote : Posted in a previous version of this proposal | # |
Looks OK but projectors is not my strong point.
Phill (phill-ridout) wrote : Posted in a previous version of this proposal | # |
A few issues / questions. See in line.
Phill (phill-ridout) wrote : | # |
Looks good to me. Thanks!
Tim Bentley (trb143) : | # |
Preview Diff
1 | === modified file 'openlp/core/projectors/constants.py' | |||
2 | --- openlp/core/projectors/constants.py 2018-01-03 00:35:14 +0000 | |||
3 | +++ openlp/core/projectors/constants.py 2018-04-20 06:16:23 +0000 | |||
4 | @@ -154,110 +154,137 @@ | |||
5 | 154 | S_INFO | 154 | S_INFO |
6 | 155 | ] | 155 | ] |
7 | 156 | 156 | ||
9 | 157 | # NOTE: Changed format to account for some commands are both class 1 and 2 | 157 | # NOTE: Changed format to account for some commands are both class 1 and 2. |
10 | 158 | # Make sure the sequence of 'version' is lowest-to-highest. | ||
11 | 158 | PJLINK_VALID_CMD = { | 159 | PJLINK_VALID_CMD = { |
13 | 159 | 'ACKN': {'version': ['2', ], | 160 | 'ACKN': {'version': ['2'], |
14 | 161 | 'default': '2', | ||
15 | 160 | 'description': translate('OpenLP.PJLinkConstants', | 162 | 'description': translate('OpenLP.PJLinkConstants', |
16 | 161 | 'Acknowledge a PJLink SRCH command - returns MAC address.') | 163 | 'Acknowledge a PJLink SRCH command - returns MAC address.') |
17 | 162 | }, | 164 | }, |
19 | 163 | 'AVMT': {'version': ['1', ], | 165 | 'AVMT': {'version': ['1'], |
20 | 166 | 'default': '1', | ||
21 | 164 | 'description': translate('OpenLP.PJLinkConstants', | 167 | 'description': translate('OpenLP.PJLinkConstants', |
22 | 165 | 'Blank/unblank video and/or mute audio.') | 168 | 'Blank/unblank video and/or mute audio.') |
23 | 166 | }, | 169 | }, |
25 | 167 | 'CLSS': {'version': ['1', ], | 170 | 'CLSS': {'version': ['1'], |
26 | 171 | 'default': '1', | ||
27 | 168 | 'description': translate('OpenLP.PJLinkConstants', | 172 | 'description': translate('OpenLP.PJLinkConstants', |
28 | 169 | 'Query projector PJLink class support.') | 173 | 'Query projector PJLink class support.') |
29 | 170 | }, | 174 | }, |
30 | 171 | 'ERST': {'version': ['1', '2'], | 175 | 'ERST': {'version': ['1', '2'], |
31 | 176 | 'default': '1', | ||
32 | 172 | 'description': translate('OpenLP.PJLinkConstants', | 177 | 'description': translate('OpenLP.PJLinkConstants', |
33 | 173 | 'Query error status from projector. ' | 178 | 'Query error status from projector. ' |
34 | 174 | 'Returns fan/lamp/temp/cover/filter/other error status.') | 179 | 'Returns fan/lamp/temp/cover/filter/other error status.') |
35 | 175 | }, | 180 | }, |
37 | 176 | 'FILT': {'version': ['2', ], | 181 | 'FILT': {'version': ['2'], |
38 | 182 | 'default': '1', | ||
39 | 177 | 'description': translate('OpenLP.PJLinkConstants', | 183 | 'description': translate('OpenLP.PJLinkConstants', |
40 | 178 | 'Query number of hours on filter.') | 184 | 'Query number of hours on filter.') |
41 | 179 | }, | 185 | }, |
43 | 180 | 'FREZ': {'version': ['2', ], | 186 | 'FREZ': {'version': ['2'], |
44 | 187 | 'default': '1', | ||
45 | 181 | 'description': translate('OpenLP.PJLinkConstants', | 188 | 'description': translate('OpenLP.PJLinkConstants', |
46 | 182 | 'Freeze or unfreeze current image being projected.') | 189 | 'Freeze or unfreeze current image being projected.') |
47 | 183 | }, | 190 | }, |
49 | 184 | 'INF1': {'version': ['1', ], | 191 | 'INF1': {'version': ['1'], |
50 | 192 | 'default': '1', | ||
51 | 185 | 'description': translate('OpenLP.PJLinkConstants', | 193 | 'description': translate('OpenLP.PJLinkConstants', |
52 | 186 | 'Query projector manufacturer name.') | 194 | 'Query projector manufacturer name.') |
53 | 187 | }, | 195 | }, |
55 | 188 | 'INF2': {'version': ['1', ], | 196 | 'INF2': {'version': ['1'], |
56 | 197 | 'default': '1', | ||
57 | 189 | 'description': translate('OpenLP.PJLinkConstants', | 198 | 'description': translate('OpenLP.PJLinkConstants', |
58 | 190 | 'Query projector product name.') | 199 | 'Query projector product name.') |
59 | 191 | }, | 200 | }, |
61 | 192 | 'INFO': {'version': ['1', ], | 201 | 'INFO': {'version': ['1'], |
62 | 202 | 'default': '1', | ||
63 | 193 | 'description': translate('OpenLP.PJLinkConstants', | 203 | 'description': translate('OpenLP.PJLinkConstants', |
64 | 194 | 'Query projector for other information set by manufacturer.') | 204 | 'Query projector for other information set by manufacturer.') |
65 | 195 | }, | 205 | }, |
67 | 196 | 'INNM': {'version': ['2', ], | 206 | 'INNM': {'version': ['2'], |
68 | 207 | 'default': '2', | ||
69 | 197 | 'description': translate('OpenLP.PJLinkConstants', | 208 | 'description': translate('OpenLP.PJLinkConstants', |
70 | 198 | 'Query specified input source name') | 209 | 'Query specified input source name') |
71 | 199 | }, | 210 | }, |
73 | 200 | 'INPT': {'version': ['1', ], | 211 | 'INPT': {'version': ['1'], |
74 | 212 | 'default': '1', | ||
75 | 201 | 'description': translate('OpenLP.PJLinkConstants', | 213 | 'description': translate('OpenLP.PJLinkConstants', |
76 | 202 | 'Switch to specified video source.') | 214 | 'Switch to specified video source.') |
77 | 203 | }, | 215 | }, |
79 | 204 | 'INST': {'version': ['1', ], | 216 | 'INST': {'version': ['1'], |
80 | 217 | 'default': '1', | ||
81 | 205 | 'description': translate('OpenLP.PJLinkConstants', | 218 | 'description': translate('OpenLP.PJLinkConstants', |
82 | 206 | 'Query available input sources.') | 219 | 'Query available input sources.') |
83 | 207 | }, | 220 | }, |
85 | 208 | 'IRES': {'version:': ['2', ], | 221 | 'IRES': {'version': ['2'], |
86 | 222 | 'default': '2', | ||
87 | 209 | 'description': translate('OpenLP.PJLinkConstants', | 223 | 'description': translate('OpenLP.PJLinkConstants', |
88 | 210 | 'Query current input resolution.') | 224 | 'Query current input resolution.') |
89 | 211 | }, | 225 | }, |
91 | 212 | 'LAMP': {'version': ['1', ], | 226 | 'LAMP': {'version': ['1'], |
92 | 227 | 'default': '1', | ||
93 | 213 | 'description': translate('OpenLP.PJLinkConstants', | 228 | 'description': translate('OpenLP.PJLinkConstants', |
94 | 214 | 'Query lamp time and on/off status. Multiple lamps supported.') | 229 | 'Query lamp time and on/off status. Multiple lamps supported.') |
95 | 215 | }, | 230 | }, |
97 | 216 | 'LKUP': {'version': ['2', ], | 231 | 'LKUP': {'version': ['2'], |
98 | 232 | 'default': '2', | ||
99 | 217 | 'description': translate('OpenLP.PJLinkConstants', | 233 | 'description': translate('OpenLP.PJLinkConstants', |
100 | 218 | 'UDP Status - Projector is now available on network. Includes MAC address.') | 234 | 'UDP Status - Projector is now available on network. Includes MAC address.') |
101 | 219 | }, | 235 | }, |
103 | 220 | 'MVOL': {'version': ['2', ], | 236 | 'MVOL': {'version': ['2'], |
104 | 237 | 'default': '1', | ||
105 | 221 | 'description': translate('OpenLP.PJLinkConstants', | 238 | 'description': translate('OpenLP.PJLinkConstants', |
106 | 222 | 'Adjust microphone volume by 1 step.') | 239 | 'Adjust microphone volume by 1 step.') |
107 | 223 | }, | 240 | }, |
109 | 224 | 'NAME': {'version': ['1', ], | 241 | 'NAME': {'version': ['1'], |
110 | 242 | 'default': '1', | ||
111 | 225 | 'description': translate('OpenLP.PJLinkConstants', | 243 | 'description': translate('OpenLP.PJLinkConstants', |
112 | 226 | 'Query customer-set projector name.') | 244 | 'Query customer-set projector name.') |
113 | 227 | }, | 245 | }, |
115 | 228 | 'PJLINK': {'version': ['1', ], | 246 | 'PJLINK': {'version': ['1'], |
116 | 247 | 'default': '1', | ||
117 | 229 | 'description': translate('OpenLP.PJLinkConstants', | 248 | 'description': translate('OpenLP.PJLinkConstants', |
118 | 230 | 'Initial connection with authentication/no authentication request.') | 249 | 'Initial connection with authentication/no authentication request.') |
119 | 231 | }, | 250 | }, |
121 | 232 | 'POWR': {'version': ['1', ], | 251 | 'POWR': {'version': ['1'], |
122 | 252 | 'default': '1', | ||
123 | 233 | 'description': translate('OpenLP.PJLinkConstants', | 253 | 'description': translate('OpenLP.PJLinkConstants', |
124 | 234 | 'Turn lamp on or off/standby.') | 254 | 'Turn lamp on or off/standby.') |
125 | 235 | }, | 255 | }, |
127 | 236 | 'RFIL': {'version': ['2', ], | 256 | 'RFIL': {'version': ['2'], |
128 | 257 | 'default': '2', | ||
129 | 237 | 'description': translate('OpenLP.PJLinkConstants', | 258 | 'description': translate('OpenLP.PJLinkConstants', |
130 | 238 | 'Query replacement air filter model number.') | 259 | 'Query replacement air filter model number.') |
131 | 239 | }, | 260 | }, |
133 | 240 | 'RLMP': {'version': ['2', ], | 261 | 'RLMP': {'version': ['2'], |
134 | 262 | 'default': '2', | ||
135 | 241 | 'description': translate('OpenLP.PJLinkConstants', | 263 | 'description': translate('OpenLP.PJLinkConstants', |
136 | 242 | 'Query replacement lamp model number.') | 264 | 'Query replacement lamp model number.') |
137 | 243 | }, | 265 | }, |
139 | 244 | 'RRES': {'version': ['2', ], | 266 | 'RRES': {'version': ['2'], |
140 | 267 | 'default': '2', | ||
141 | 245 | 'description': translate('OpenLP.PJLinkConstants', | 268 | 'description': translate('OpenLP.PJLinkConstants', |
142 | 246 | 'Query recommended resolution.') | 269 | 'Query recommended resolution.') |
143 | 247 | }, | 270 | }, |
145 | 248 | 'SNUM': {'version': ['2', ], | 271 | 'SNUM': {'version': ['2'], |
146 | 272 | 'default': '2', | ||
147 | 249 | 'description': translate('OpenLP.PJLinkConstants', | 273 | 'description': translate('OpenLP.PJLinkConstants', |
148 | 250 | 'Query projector serial number.') | 274 | 'Query projector serial number.') |
149 | 251 | }, | 275 | }, |
151 | 252 | 'SRCH': {'version': ['2', ], | 276 | 'SRCH': {'version': ['2'], |
152 | 277 | 'default': '2', | ||
153 | 253 | 'description': translate('OpenLP.PJLinkConstants', | 278 | 'description': translate('OpenLP.PJLinkConstants', |
154 | 254 | 'UDP broadcast search request for available projectors. Reply is ACKN.') | 279 | 'UDP broadcast search request for available projectors. Reply is ACKN.') |
155 | 255 | }, | 280 | }, |
157 | 256 | 'SVER': {'version': ['2', ], | 281 | 'SVER': {'version': ['2'], |
158 | 282 | 'default': '2', | ||
159 | 257 | 'description': translate('OpenLP.PJLinkConstants', | 283 | 'description': translate('OpenLP.PJLinkConstants', |
160 | 258 | 'Query projector software version number.') | 284 | 'Query projector software version number.') |
161 | 259 | }, | 285 | }, |
163 | 260 | 'SVOL': {'version': ['2', ], | 286 | 'SVOL': {'version': ['2'], |
164 | 287 | 'default': '2', | ||
165 | 261 | 'description': translate('OpenLP.PJLinkConstants', | 288 | 'description': translate('OpenLP.PJLinkConstants', |
166 | 262 | 'Adjust speaker volume by 1 step.') | 289 | 'Adjust speaker volume by 1 step.') |
167 | 263 | } | 290 | } |
168 | 264 | 291 | ||
169 | === modified file 'openlp/core/projectors/editform.py' | |||
170 | --- openlp/core/projectors/editform.py 2017-12-29 09:15:48 +0000 | |||
171 | +++ openlp/core/projectors/editform.py 2018-04-20 06:16:23 +0000 | |||
172 | @@ -58,10 +58,15 @@ | |||
173 | 58 | # IP Address | 58 | # IP Address |
174 | 59 | self.ip_label = QtWidgets.QLabel(edit_projector_dialog) | 59 | self.ip_label = QtWidgets.QLabel(edit_projector_dialog) |
175 | 60 | self.ip_label.setObjectName('projector_edit_ip_label') | 60 | self.ip_label.setObjectName('projector_edit_ip_label') |
178 | 61 | self.ip_text = QtWidgets.QLineEdit(edit_projector_dialog) | 61 | self.ip_text_edit = QtWidgets.QLineEdit(edit_projector_dialog) |
179 | 62 | self.ip_text.setObjectName('projector_edit_ip_text') | 62 | self.ip_text_edit.setObjectName('projector_edit_ip_text') |
180 | 63 | self.ip_text_label = QtWidgets.QLabel(edit_projector_dialog) | ||
181 | 64 | self.ip_text_label.setObjectName('projector_show_ip_text') | ||
182 | 63 | self.dialog_layout.addWidget(self.ip_label, 0, 0) | 65 | self.dialog_layout.addWidget(self.ip_label, 0, 0) |
184 | 64 | self.dialog_layout.addWidget(self.ip_text, 0, 1) | 66 | # For new projector, use edit widget |
185 | 67 | self.dialog_layout.addWidget(self.ip_text_edit, 0, 1) | ||
186 | 68 | # For edit projector, use show widget | ||
187 | 69 | self.dialog_layout.addWidget(self.ip_text_label, 0, 1) | ||
188 | 65 | # Port number | 70 | # Port number |
189 | 66 | self.port_label = QtWidgets.QLabel(edit_projector_dialog) | 71 | self.port_label = QtWidgets.QLabel(edit_projector_dialog) |
190 | 67 | self.port_label.setObjectName('projector_edit_ip_label') | 72 | self.port_label.setObjectName('projector_edit_ip_label') |
191 | @@ -111,8 +116,8 @@ | |||
192 | 111 | title = translate('OpenLP.ProjectorEditForm', 'Edit Projector') | 116 | title = translate('OpenLP.ProjectorEditForm', 'Edit Projector') |
193 | 112 | edit_projector_dialog.setWindowTitle(title) | 117 | edit_projector_dialog.setWindowTitle(title) |
194 | 113 | self.ip_label.setText(translate('OpenLP.ProjectorEditForm', 'IP Address')) | 118 | self.ip_label.setText(translate('OpenLP.ProjectorEditForm', 'IP Address')) |
197 | 114 | self.ip_text.setText(self.projector.ip) | 119 | self.ip_text_edit.setText(self.projector.ip) |
198 | 115 | self.ip_text.setFocus() | 120 | self.ip_text_label.setText(self.projector.ip) |
199 | 116 | self.port_label.setText(translate('OpenLP.ProjectorEditForm', 'Port Number')) | 121 | self.port_label.setText(translate('OpenLP.ProjectorEditForm', 'Port Number')) |
200 | 117 | self.port_text.setText(str(self.projector.port)) | 122 | self.port_text.setText(str(self.projector.port)) |
201 | 118 | self.pin_label.setText(translate('OpenLP.ProjectorEditForm', 'PIN')) | 123 | self.pin_label.setText(translate('OpenLP.ProjectorEditForm', 'PIN')) |
202 | @@ -131,7 +136,7 @@ | |||
203 | 131 | Class to add or edit a projector entry in the database. | 136 | Class to add or edit a projector entry in the database. |
204 | 132 | 137 | ||
205 | 133 | Fields that are editable: | 138 | Fields that are editable: |
207 | 134 | ip = Column(String(100)) | 139 | ip = Column(String(100)) (Only edit for new projector) |
208 | 135 | port = Column(String(8)) | 140 | port = Column(String(8)) |
209 | 136 | pin = Column(String(20)) | 141 | pin = Column(String(20)) |
210 | 137 | name = Column(String(20)) | 142 | name = Column(String(20)) |
211 | @@ -154,9 +159,16 @@ | |||
212 | 154 | if projector is None: | 159 | if projector is None: |
213 | 155 | self.projector = Projector() | 160 | self.projector = Projector() |
214 | 156 | self.new_projector = True | 161 | self.new_projector = True |
215 | 162 | self.ip_text_edit.setVisible(True) | ||
216 | 163 | self.ip_text_edit.setFocus() | ||
217 | 164 | self.ip_text_label.setVisible(False) | ||
218 | 157 | else: | 165 | else: |
219 | 158 | self.projector = projector | 166 | self.projector = projector |
220 | 159 | self.new_projector = False | 167 | self.new_projector = False |
221 | 168 | self.ip_text_edit.setVisible(False) | ||
222 | 169 | self.ip_text_label.setVisible(True) | ||
223 | 170 | # Since it's already defined, IP address is unchangeable, so focus on port number | ||
224 | 171 | self.port_text.setFocus() | ||
225 | 160 | self.retranslateUi(self) | 172 | self.retranslateUi(self) |
226 | 161 | reply = QtWidgets.QDialog.exec(self) | 173 | reply = QtWidgets.QDialog.exec(self) |
227 | 162 | return reply | 174 | return reply |
228 | @@ -187,30 +199,32 @@ | |||
229 | 187 | record=record.id))) | 199 | record=record.id))) |
230 | 188 | valid = False | 200 | valid = False |
231 | 189 | return | 201 | return |
240 | 190 | adx = self.ip_text.text() | 202 | if self.new_projector: |
241 | 191 | valid = verify_ip_address(adx) | 203 | # Only validate a new entry - otherwise it's been previously verified |
242 | 192 | if valid: | 204 | adx = self.ip_text_edit.text() |
243 | 193 | ip = self.projectordb.get_projector_by_ip(adx) | 205 | valid = verify_ip_address(adx) |
244 | 194 | if ip is None: | 206 | if valid: |
245 | 195 | valid = True | 207 | # With a valid IP - check if it's already in database so we don't duplicate |
246 | 196 | self.new_projector = True | 208 | ip = self.projectordb.get_projector_by_ip(adx) |
247 | 197 | elif ip.id != self.projector.id: | 209 | if ip is None: |
248 | 210 | valid = True | ||
249 | 211 | self.new_projector = True | ||
250 | 212 | elif ip.id != self.projector.id: | ||
251 | 213 | QtWidgets.QMessageBox.warning(self, | ||
252 | 214 | translate('OpenLP.ProjectorWizard', 'Duplicate IP Address'), | ||
253 | 215 | translate('OpenLP.ProjectorWizard', | ||
254 | 216 | 'IP address "{ip}"<br />is already in the database ' | ||
255 | 217 | 'as ID {data}.<br /><br />Please Enter a different ' | ||
256 | 218 | 'IP address.'.format(ip=adx, data=ip.id))) | ||
257 | 219 | return | ||
258 | 220 | else: | ||
259 | 198 | QtWidgets.QMessageBox.warning(self, | 221 | QtWidgets.QMessageBox.warning(self, |
261 | 199 | translate('OpenLP.ProjectorWizard', 'Duplicate IP Address'), | 222 | translate('OpenLP.ProjectorWizard', 'Invalid IP Address'), |
262 | 200 | translate('OpenLP.ProjectorWizard', | 223 | translate('OpenLP.ProjectorWizard', |
266 | 201 | 'IP address "{ip}"<br />is already in the database ' | 224 | 'IP address "{ip}"<br>is not a valid IP address.' |
267 | 202 | 'as ID {data}.<br /><br />Please Enter a different ' | 225 | '<br /><br />Please enter a valid IP address.'.format(ip=adx))) |
265 | 203 | 'IP address.'.format(ip=adx, data=ip.id))) | ||
268 | 204 | valid = False | 226 | valid = False |
269 | 205 | return | 227 | return |
270 | 206 | else: | ||
271 | 207 | QtWidgets.QMessageBox.warning(self, | ||
272 | 208 | translate('OpenLP.ProjectorWizard', 'Invalid IP Address'), | ||
273 | 209 | translate('OpenLP.ProjectorWizard', | ||
274 | 210 | 'IP address "{ip}"<br>is not a valid IP address.' | ||
275 | 211 | '<br /><br />Please enter a valid IP address.'.format(ip=adx))) | ||
276 | 212 | valid = False | ||
277 | 213 | return | ||
278 | 214 | port = int(self.port_text.text()) | 228 | port = int(self.port_text.text()) |
279 | 215 | if port < 1000 or port > 32767: | 229 | if port < 1000 or port > 32767: |
280 | 216 | QtWidgets.QMessageBox.warning(self, | 230 | QtWidgets.QMessageBox.warning(self, |
281 | @@ -223,7 +237,8 @@ | |||
282 | 223 | 'Default PJLink port is {port}'.format(port=PJLINK_PORT))) | 237 | 'Default PJLink port is {port}'.format(port=PJLINK_PORT))) |
283 | 224 | valid = False | 238 | valid = False |
284 | 225 | if valid: | 239 | if valid: |
286 | 226 | self.projector.ip = self.ip_text.text() | 240 | if self.new_projector: |
287 | 241 | self.projector.ip = self.ip_text_edit.text() | ||
288 | 227 | self.projector.pin = self.pin_text.text() | 242 | self.projector.pin = self.pin_text.text() |
289 | 228 | self.projector.port = int(self.port_text.text()) | 243 | self.projector.port = int(self.port_text.text()) |
290 | 229 | self.projector.name = self.name_text.text() | 244 | self.projector.name = self.name_text.text() |
291 | 230 | 245 | ||
292 | === modified file 'openlp/core/projectors/manager.py' | |||
293 | --- openlp/core/projectors/manager.py 2018-02-11 11:42:13 +0000 | |||
294 | +++ openlp/core/projectors/manager.py 2018-04-20 06:16:23 +0000 | |||
295 | @@ -35,24 +35,9 @@ | |||
296 | 35 | from openlp.core.common.settings import Settings | 35 | from openlp.core.common.settings import Settings |
297 | 36 | from openlp.core.lib.ui import create_widget_action | 36 | from openlp.core.lib.ui import create_widget_action |
298 | 37 | from openlp.core.projectors import DialogSourceStyle | 37 | from openlp.core.projectors import DialogSourceStyle |
317 | 38 | from openlp.core.projectors.constants import \ | 38 | from openlp.core.projectors.constants import E_AUTHENTICATION, E_ERROR, E_NETWORK, E_NOT_CONNECTED, \ |
318 | 39 | E_AUTHENTICATION, \ | 39 | E_UNKNOWN_SOCKET_ERROR, S_CONNECTED, S_CONNECTING, S_COOLDOWN, S_INITIALIZE, S_NOT_CONNECTED, S_OFF, S_ON, \ |
319 | 40 | E_ERROR, \ | 40 | S_STANDBY, S_WARMUP, STATUS_CODE, STATUS_MSG, QSOCKET_STATE |
302 | 41 | E_NETWORK, \ | ||
303 | 42 | E_NOT_CONNECTED, \ | ||
304 | 43 | E_UNKNOWN_SOCKET_ERROR, \ | ||
305 | 44 | S_CONNECTED, \ | ||
306 | 45 | S_CONNECTING, \ | ||
307 | 46 | S_COOLDOWN, \ | ||
308 | 47 | S_INITIALIZE, \ | ||
309 | 48 | S_NOT_CONNECTED, \ | ||
310 | 49 | S_OFF, \ | ||
311 | 50 | S_ON, \ | ||
312 | 51 | S_STANDBY, \ | ||
313 | 52 | S_WARMUP, \ | ||
314 | 53 | STATUS_CODE, \ | ||
315 | 54 | STATUS_MSG, \ | ||
316 | 55 | QSOCKET_STATE | ||
320 | 56 | 41 | ||
321 | 57 | from openlp.core.projectors.db import ProjectorDB | 42 | from openlp.core.projectors.db import ProjectorDB |
322 | 58 | from openlp.core.projectors.editform import ProjectorEditForm | 43 | from openlp.core.projectors.editform import ProjectorEditForm |
323 | 59 | 44 | ||
324 | === modified file 'openlp/core/projectors/pjlink.py' | |||
325 | --- openlp/core/projectors/pjlink.py 2018-02-11 11:42:13 +0000 | |||
326 | +++ openlp/core/projectors/pjlink.py 2018-04-20 06:16:23 +0000 | |||
327 | @@ -57,8 +57,7 @@ | |||
328 | 57 | from openlp.core.projectors.constants import CONNECTION_ERRORS, PJLINK_CLASS, PJLINK_DEFAULT_CODES, PJLINK_ERRORS, \ | 57 | from openlp.core.projectors.constants import CONNECTION_ERRORS, PJLINK_CLASS, PJLINK_DEFAULT_CODES, PJLINK_ERRORS, \ |
329 | 58 | PJLINK_ERST_DATA, PJLINK_ERST_STATUS, PJLINK_MAX_PACKET, PJLINK_PREFIX, PJLINK_PORT, PJLINK_POWR_STATUS, \ | 58 | PJLINK_ERST_DATA, PJLINK_ERST_STATUS, PJLINK_MAX_PACKET, PJLINK_PREFIX, PJLINK_PORT, PJLINK_POWR_STATUS, \ |
330 | 59 | PJLINK_SUFFIX, PJLINK_VALID_CMD, PROJECTOR_STATE, STATUS_CODE, STATUS_MSG, QSOCKET_STATE, \ | 59 | PJLINK_SUFFIX, PJLINK_VALID_CMD, PROJECTOR_STATE, STATUS_CODE, STATUS_MSG, QSOCKET_STATE, \ |
333 | 60 | E_AUTHENTICATION, E_CONNECTION_REFUSED, E_GENERAL, E_INVALID_DATA, E_NETWORK, E_NOT_CONNECTED, \ | 60 | E_AUTHENTICATION, E_CONNECTION_REFUSED, E_GENERAL, E_NETWORK, E_NOT_CONNECTED, E_SOCKET_TIMEOUT, \ |
332 | 61 | E_SOCKET_TIMEOUT, \ | ||
334 | 62 | S_CONNECTED, S_CONNECTING, S_NOT_CONNECTED, S_OFF, S_OK, S_ON | 61 | S_CONNECTED, S_CONNECTING, S_NOT_CONNECTED, S_OFF, S_OK, S_ON |
335 | 63 | 62 | ||
336 | 64 | log = logging.getLogger(__name__) | 63 | log = logging.getLogger(__name__) |
337 | @@ -93,22 +92,9 @@ | |||
338 | 93 | self.projector_list = projector_list | 92 | self.projector_list = projector_list |
339 | 94 | self.port = port | 93 | self.port = port |
340 | 95 | # Local defines | 94 | # Local defines |
341 | 96 | self.ackn_list = {} # Replies from online projetors | ||
342 | 97 | self.search_active = False | 95 | self.search_active = False |
343 | 98 | self.search_time = 30000 # 30 seconds for allowed time | 96 | self.search_time = 30000 # 30 seconds for allowed time |
344 | 99 | self.search_timer = QtCore.QTimer() | 97 | self.search_timer = QtCore.QTimer() |
345 | 100 | # New commands available in PJLink Class 2 | ||
346 | 101 | # ACKN/SRCH is processed here since it's used to find available projectors | ||
347 | 102 | # Other commands are processed by the individual projector instances | ||
348 | 103 | self.pjlink_udp_functions = { | ||
349 | 104 | 'ACKN': self.process_ackn, # Class 2, command is 'SRCH' | ||
350 | 105 | 'ERST': None, # Class 1/2 | ||
351 | 106 | 'INPT': None, # Class 1/2 | ||
352 | 107 | 'LKUP': None, # Class 2 (reply only - no cmd) | ||
353 | 108 | 'POWR': None, # Class 1/2 | ||
354 | 109 | 'SRCH': self.process_srch # Class 2 (reply is ACKN) | ||
355 | 110 | } | ||
356 | 111 | |||
357 | 112 | self.readyRead.connect(self.get_datagram) | 98 | self.readyRead.connect(self.get_datagram) |
358 | 113 | log.debug('(UDP) PJLinkUDP() Initialized') | 99 | log.debug('(UDP) PJLinkUDP() Initialized') |
359 | 114 | 100 | ||
360 | @@ -118,88 +104,26 @@ | |||
361 | 118 | Retrieve packet and basic checks | 104 | Retrieve packet and basic checks |
362 | 119 | """ | 105 | """ |
363 | 120 | log.debug('(UDP) get_datagram() - Receiving data') | 106 | log.debug('(UDP) get_datagram() - Receiving data') |
367 | 121 | read = self.pendingDatagramSize() | 107 | read_size = self.pendingDatagramSize() |
368 | 122 | if read < 0: | 108 | if read_size < 0: |
369 | 123 | log.warn('(UDP) No data (-1)') | 109 | log.warning('(UDP) No data (-1)') |
370 | 124 | return | 110 | return |
373 | 125 | if read < 1: | 111 | if read_size < 1: |
374 | 126 | log.warn('(UDP) get_datagram() called when pending data size is 0') | 112 | log.warning('(UDP) get_datagram() called when pending data size is 0') |
375 | 127 | return | 113 | return |
376 | 128 | data, peer_address, peer_port = self.readDatagram(self.pendingDatagramSize()) | 114 | data, peer_address, peer_port = self.readDatagram(self.pendingDatagramSize()) |
377 | 129 | log.debug('(UDP) {size} bytes received from {adx} on port {port}'.format(size=len(data), | 115 | log.debug('(UDP) {size} bytes received from {adx} on port {port}'.format(size=len(data), |
378 | 130 | adx=peer_address, | 116 | adx=peer_address, |
379 | 131 | port=peer_port)) | 117 | port=peer_port)) |
380 | 132 | log.debug('(UDP) packet "{data}"'.format(data=data)) | 118 | log.debug('(UDP) packet "{data}"'.format(data=data)) |
451 | 133 | if len(data) < 0: | 119 | # Send to appropriate instance to process packet |
452 | 134 | log.warn('(UDP) No data (-1)') | 120 | log.debug('(UDP) Checking projector list for ip {host} to process'.format(host=peer_address)) |
453 | 135 | return | 121 | for projector in self.projector_list: |
454 | 136 | elif len(data) < 8: | 122 | if peer_address == projector.ip: |
455 | 137 | # Minimum packet is '%2CCCC=' | 123 | # Dispatch packet to appropriate remote instance |
456 | 138 | log.warn('(UDP) Invalid packet - not enough data') | 124 | log.debug('(UDP) Dispatching packet to {host}'.format(host=projector.entry.name)) |
457 | 139 | return | 125 | return projector.get_data(buff=data, ip=peer_address, host=peer_address, port=peer_port) |
458 | 140 | elif data is None: | 126 | log.warning('(UDP) Could not find projector with ip {ip} to process packet'.format(ip=peer_address)) |
389 | 141 | log.warn('(UDP) No data (None)') | ||
390 | 142 | return | ||
391 | 143 | elif len(data) > PJLINK_MAX_PACKET: | ||
392 | 144 | log.warn('(UDP) Invalid packet - length too long') | ||
393 | 145 | return | ||
394 | 146 | elif not data.startswith(PJLINK_PREFIX): | ||
395 | 147 | log.warn('(UDP) Invalid packet - does not start with PJLINK_PREFIX') | ||
396 | 148 | return | ||
397 | 149 | elif data[1] != '2': | ||
398 | 150 | log.warn('(UDP) Invalid packet - missing/invalid PJLink class version') | ||
399 | 151 | return | ||
400 | 152 | elif data[6] != '=': | ||
401 | 153 | log.warn('(UDP) Invalid packet - separator missing') | ||
402 | 154 | return | ||
403 | 155 | # First two characters are header information we don't need at this time | ||
404 | 156 | cmd, data = data[2:].split('=') | ||
405 | 157 | if cmd not in self.pjlink_udp_functions: | ||
406 | 158 | log.warn('(UDP) Invalid packet - not a valid PJLink UDP reply') | ||
407 | 159 | return | ||
408 | 160 | if self.pjlink_udp_functions[cmd] is not None: | ||
409 | 161 | log.debug('(UDP) Processing {cmd} with "{data}"'.format(cmd=cmd, data=data)) | ||
410 | 162 | return self.pjlink_udp_functions[cmd](data=data, host=peer_address, port=peer_port) | ||
411 | 163 | else: | ||
412 | 164 | log.debug('(UDP) Checking projector list for ip {host} to process'.format(host=peer_address)) | ||
413 | 165 | for projector in self.projector_list: | ||
414 | 166 | if peer_address == projector.ip: | ||
415 | 167 | if cmd not in projector.pjlink_functions: | ||
416 | 168 | log.error('(UDP) Could not find method to process ' | ||
417 | 169 | '"{cmd}" in {host}'.format(cmd=cmd, host=projector.ip)) | ||
418 | 170 | return | ||
419 | 171 | log.debug('(UDP) Calling "{cmd}" in {host}'.format(cmd=cmd, host=projector.ip)) | ||
420 | 172 | return projector.pjlink_functions[cmd](data=data) | ||
421 | 173 | log.warn('(UDP) Could not find projector with ip {ip} to process packet'.format(ip=peer_address)) | ||
422 | 174 | return | ||
423 | 175 | |||
424 | 176 | def process_ackn(self, data, host, port): | ||
425 | 177 | """ | ||
426 | 178 | Process the ACKN command. | ||
427 | 179 | |||
428 | 180 | :param data: Data in packet | ||
429 | 181 | :param host: IP address of sending host | ||
430 | 182 | :param port: Port received on | ||
431 | 183 | """ | ||
432 | 184 | log.debug('(UDP) Processing ACKN packet') | ||
433 | 185 | if host not in self.ackn_list: | ||
434 | 186 | log.debug('(UDP) Adding {host} to ACKN list'.format(host=host)) | ||
435 | 187 | self.ackn_list[host] = {'data': data, | ||
436 | 188 | 'port': port} | ||
437 | 189 | else: | ||
438 | 190 | log.warn('(UDP) Host {host} already replied - ignoring'.format(host=host)) | ||
439 | 191 | |||
440 | 192 | def process_srch(self, data, host, port): | ||
441 | 193 | """ | ||
442 | 194 | Process the SRCH command. | ||
443 | 195 | |||
444 | 196 | SRCH is processed by terminals so we ignore any packet. | ||
445 | 197 | |||
446 | 198 | :param data: Data in packet | ||
447 | 199 | :param host: IP address of sending host | ||
448 | 200 | :param port: Port received on | ||
449 | 201 | """ | ||
450 | 202 | log.debug('(UDP) SRCH packet received - ignoring') | ||
459 | 203 | return | 127 | return |
460 | 204 | 128 | ||
461 | 205 | def search_start(self): | 129 | def search_start(self): |
462 | @@ -224,6 +148,8 @@ | |||
463 | 224 | """ | 148 | """ |
464 | 225 | Process replies from PJLink projector. | 149 | Process replies from PJLink projector. |
465 | 226 | """ | 150 | """ |
466 | 151 | # List of IP addresses and mac addresses found via UDP search command | ||
467 | 152 | ackn_list = [] | ||
468 | 227 | 153 | ||
469 | 228 | def __init__(self, *args, **kwargs): | 154 | def __init__(self, *args, **kwargs): |
470 | 229 | """ | 155 | """ |
471 | @@ -231,24 +157,47 @@ | |||
472 | 231 | """ | 157 | """ |
473 | 232 | log.debug('PJlinkCommands(args={args} kwargs={kwargs})'.format(args=args, kwargs=kwargs)) | 158 | log.debug('PJlinkCommands(args={args} kwargs={kwargs})'.format(args=args, kwargs=kwargs)) |
474 | 233 | super().__init__() | 159 | super().__init__() |
476 | 234 | # Map PJLink command to method | 160 | # Map PJLink command to method and include pjlink class version for this instance |
477 | 161 | # Default initial pjlink class version is '1' | ||
478 | 235 | self.pjlink_functions = { | 162 | self.pjlink_functions = { |
495 | 236 | 'AVMT': self.process_avmt, | 163 | 'ACKN': {"method": self.process_ackn, # Class 2 (command is SRCH) |
496 | 237 | 'CLSS': self.process_clss, | 164 | "version": "2"}, |
497 | 238 | 'ERST': self.process_erst, | 165 | 'AVMT': {"method": self.process_avmt, |
498 | 239 | 'INFO': self.process_info, | 166 | "version": "1"}, |
499 | 240 | 'INF1': self.process_inf1, | 167 | 'CLSS': {"method": self.process_clss, |
500 | 241 | 'INF2': self.process_inf2, | 168 | "version": "1"}, |
501 | 242 | 'INPT': self.process_inpt, | 169 | 'ERST': {"method": self.process_erst, |
502 | 243 | 'INST': self.process_inst, | 170 | "version": "1"}, |
503 | 244 | 'LAMP': self.process_lamp, | 171 | 'INFO': {"method": self.process_info, |
504 | 245 | 'NAME': self.process_name, | 172 | "version": "1"}, |
505 | 246 | 'PJLINK': self.process_pjlink, | 173 | 'INF1': {"method": self.process_inf1, |
506 | 247 | 'POWR': self.process_powr, | 174 | "version": "1"}, |
507 | 248 | 'SNUM': self.process_snum, | 175 | 'INF2': {"method": self.process_inf2, |
508 | 249 | 'SVER': self.process_sver, | 176 | "version": "1"}, |
509 | 250 | 'RFIL': self.process_rfil, | 177 | 'INPT': {"method": self.process_inpt, |
510 | 251 | 'RLMP': self.process_rlmp | 178 | "version": "1"}, |
511 | 179 | 'INST': {"method": self.process_inst, | ||
512 | 180 | "version": "1"}, | ||
513 | 181 | 'LAMP': {"method": self.process_lamp, | ||
514 | 182 | "version": "1"}, | ||
515 | 183 | 'LKUP': {"method": self.process_lkup, # Class 2 (reply only - no cmd) | ||
516 | 184 | "version": "2"}, | ||
517 | 185 | 'NAME': {"method": self.process_name, | ||
518 | 186 | "version": "1"}, | ||
519 | 187 | 'PJLINK': {"method": self.process_pjlink, | ||
520 | 188 | "version": "1"}, | ||
521 | 189 | 'POWR': {"method": self.process_powr, | ||
522 | 190 | "version": "1"}, | ||
523 | 191 | 'SNUM': {"method": self.process_snum, | ||
524 | 192 | "version": "1"}, | ||
525 | 193 | 'SRCH': {"method": self.process_srch, # Class 2 (reply is ACKN) | ||
526 | 194 | "version": "2"}, | ||
527 | 195 | 'SVER': {"method": self.process_sver, | ||
528 | 196 | "version": "1"}, | ||
529 | 197 | 'RFIL': {"method": self.process_rfil, | ||
530 | 198 | "version": "1"}, | ||
531 | 199 | 'RLMP': {"method": self.process_rlmp, | ||
532 | 200 | "version": "1"} | ||
533 | 252 | } | 201 | } |
534 | 253 | 202 | ||
535 | 254 | def reset_information(self): | 203 | def reset_information(self): |
536 | @@ -287,8 +236,11 @@ | |||
537 | 287 | self.send_busy = False | 236 | self.send_busy = False |
538 | 288 | self.send_queue = [] | 237 | self.send_queue = [] |
539 | 289 | self.priority_queue = [] | 238 | self.priority_queue = [] |
540 | 239 | # Reset default version in command routing dict | ||
541 | 240 | for cmd in self.pjlink_functions: | ||
542 | 241 | self.pjlink_functions[cmd]["version"] = PJLINK_VALID_CMD[cmd]['default'] | ||
543 | 290 | 242 | ||
545 | 291 | def process_command(self, cmd, data): | 243 | def process_command(self, cmd, data, *args, **kwargs): |
546 | 292 | """ | 244 | """ |
547 | 293 | Verifies any return error code. Calls the appropriate command handler. | 245 | Verifies any return error code. Calls the appropriate command handler. |
548 | 294 | 246 | ||
549 | @@ -320,9 +272,25 @@ | |||
550 | 320 | return self.change_status(status=E_AUTHENTICATION) | 272 | return self.change_status(status=E_AUTHENTICATION) |
551 | 321 | # Command checks already passed | 273 | # Command checks already passed |
552 | 322 | log.debug('({ip}) Calling function for {cmd}'.format(ip=self.entry.name, cmd=cmd)) | 274 | log.debug('({ip}) Calling function for {cmd}'.format(ip=self.entry.name, cmd=cmd)) |
556 | 323 | self.pjlink_functions[cmd](data=data) | 275 | self.pjlink_functions[cmd]["method"](data=data, *args, **kwargs) |
557 | 324 | 276 | ||
558 | 325 | def process_avmt(self, data): | 277 | def process_ackn(self, data, host, port): |
559 | 278 | """ | ||
560 | 279 | Process the ACKN command. | ||
561 | 280 | |||
562 | 281 | :param data: Data in packet | ||
563 | 282 | :param host: IP address of sending host | ||
564 | 283 | :param port: Port received on | ||
565 | 284 | """ | ||
566 | 285 | log.debug('({ip}) Processing ACKN packet'.format(ip=self.entry.name)) | ||
567 | 286 | if host not in self.ackn_list: | ||
568 | 287 | log.debug('({ip}) Adding {host} to ACKN list'.format(ip=self.entry.name, host=host)) | ||
569 | 288 | self.ackn_list[host] = {'data': data, | ||
570 | 289 | 'port': port} | ||
571 | 290 | else: | ||
572 | 291 | log.warning('({ip}) Host {host} already replied - ignoring'.format(ip=self.entry.name, host=host)) | ||
573 | 292 | |||
574 | 293 | def process_avmt(self, data, *args, **kwargs): | ||
575 | 326 | """ | 294 | """ |
576 | 327 | Process shutter and speaker status. See PJLink specification for format. | 295 | Process shutter and speaker status. See PJLink specification for format. |
577 | 328 | Update self.mute (audio) and self.shutter (video shutter). | 296 | Update self.mute (audio) and self.shutter (video shutter). |
578 | @@ -351,7 +319,7 @@ | |||
579 | 351 | self.projectorUpdateIcons.emit() | 319 | self.projectorUpdateIcons.emit() |
580 | 352 | return | 320 | return |
581 | 353 | 321 | ||
583 | 354 | def process_clss(self, data): | 322 | def process_clss(self, data, *args, **kwargs): |
584 | 355 | """ | 323 | """ |
585 | 356 | PJLink class that this projector supports. See PJLink specification for format. | 324 | PJLink class that this projector supports. See PJLink specification for format. |
586 | 357 | Updates self.class. | 325 | Updates self.class. |
587 | @@ -367,12 +335,13 @@ | |||
588 | 367 | # Due to stupid projectors not following standards (Optoma, BenQ comes to mind), | 335 | # Due to stupid projectors not following standards (Optoma, BenQ comes to mind), |
589 | 368 | # AND the different responses that can be received, the semi-permanent way to | 336 | # AND the different responses that can be received, the semi-permanent way to |
590 | 369 | # fix the class reply is to just remove all non-digit characters. | 337 | # fix the class reply is to just remove all non-digit characters. |
594 | 370 | try: | 338 | chk = re.findall('\d', data) |
595 | 371 | clss = re.findall('\d', data)[0] # Should only be the first match | 339 | if len(chk) < 1: |
593 | 372 | except IndexError: | ||
596 | 373 | log.error('({ip}) No numbers found in class version reply "{data}" - ' | 340 | log.error('({ip}) No numbers found in class version reply "{data}" - ' |
597 | 374 | 'defaulting to class "1"'.format(ip=self.entry.name, data=data)) | 341 | 'defaulting to class "1"'.format(ip=self.entry.name, data=data)) |
598 | 375 | clss = '1' | 342 | clss = '1' |
599 | 343 | else: | ||
600 | 344 | clss = chk[0] # Should only be the first match | ||
601 | 376 | elif not data.isdigit(): | 345 | elif not data.isdigit(): |
602 | 377 | log.error('({ip}) NAN CLSS version reply "{data}" - ' | 346 | log.error('({ip}) NAN CLSS version reply "{data}" - ' |
603 | 378 | 'defaulting to class "1"'.format(ip=self.entry.name, data=data)) | 347 | 'defaulting to class "1"'.format(ip=self.entry.name, data=data)) |
604 | @@ -383,6 +352,11 @@ | |||
605 | 383 | log.debug('({ip}) Setting pjlink_class for this projector ' | 352 | log.debug('({ip}) Setting pjlink_class for this projector ' |
606 | 384 | 'to "{data}"'.format(ip=self.entry.name, | 353 | 'to "{data}"'.format(ip=self.entry.name, |
607 | 385 | data=self.pjlink_class)) | 354 | data=self.pjlink_class)) |
608 | 355 | # Update method class versions | ||
609 | 356 | for cmd in self.pjlink_functions: | ||
610 | 357 | if self.pjlink_class in PJLINK_VALID_CMD[cmd]['version']: | ||
611 | 358 | self.pjlink_functions[cmd]['version'] = self.pjlink_class | ||
612 | 359 | |||
613 | 386 | # Since we call this one on first connect, setup polling from here | 360 | # Since we call this one on first connect, setup polling from here |
614 | 387 | if not self.no_poll: | 361 | if not self.no_poll: |
615 | 388 | log.debug('({ip}) process_pjlink(): Starting timer'.format(ip=self.entry.name)) | 362 | log.debug('({ip}) process_pjlink(): Starting timer'.format(ip=self.entry.name)) |
616 | @@ -391,7 +365,7 @@ | |||
617 | 391 | 365 | ||
618 | 392 | return | 366 | return |
619 | 393 | 367 | ||
621 | 394 | def process_erst(self, data): | 368 | def process_erst(self, data, *args, **kwargs): |
622 | 395 | """ | 369 | """ |
623 | 396 | Error status. See PJLink Specifications for format. | 370 | Error status. See PJLink Specifications for format. |
624 | 397 | Updates self.projector_errors | 371 | Updates self.projector_errors |
625 | @@ -443,7 +417,7 @@ | |||
626 | 443 | PJLINK_ERST_STATUS[other] | 417 | PJLINK_ERST_STATUS[other] |
627 | 444 | return | 418 | return |
628 | 445 | 419 | ||
630 | 446 | def process_inf1(self, data): | 420 | def process_inf1(self, data, *args, **kwargs): |
631 | 447 | """ | 421 | """ |
632 | 448 | Manufacturer name set in projector. | 422 | Manufacturer name set in projector. |
633 | 449 | Updates self.manufacturer | 423 | Updates self.manufacturer |
634 | @@ -455,7 +429,7 @@ | |||
635 | 455 | data=self.manufacturer)) | 429 | data=self.manufacturer)) |
636 | 456 | return | 430 | return |
637 | 457 | 431 | ||
639 | 458 | def process_inf2(self, data): | 432 | def process_inf2(self, data, *args, **kwargs): |
640 | 459 | """ | 433 | """ |
641 | 460 | Projector Model set in projector. | 434 | Projector Model set in projector. |
642 | 461 | Updates self.model. | 435 | Updates self.model. |
643 | @@ -466,7 +440,7 @@ | |||
644 | 466 | log.debug('({ip}) Setting projector model to "{data}"'.format(ip=self.entry.name, data=self.model)) | 440 | log.debug('({ip}) Setting projector model to "{data}"'.format(ip=self.entry.name, data=self.model)) |
645 | 467 | return | 441 | return |
646 | 468 | 442 | ||
648 | 469 | def process_info(self, data): | 443 | def process_info(self, data, *args, **kwargs): |
649 | 470 | """ | 444 | """ |
650 | 471 | Any extra info set in projector. | 445 | Any extra info set in projector. |
651 | 472 | Updates self.other_info. | 446 | Updates self.other_info. |
652 | @@ -477,7 +451,7 @@ | |||
653 | 477 | log.debug('({ip}) Setting projector other_info to "{data}"'.format(ip=self.entry.name, data=self.other_info)) | 451 | log.debug('({ip}) Setting projector other_info to "{data}"'.format(ip=self.entry.name, data=self.other_info)) |
654 | 478 | return | 452 | return |
655 | 479 | 453 | ||
657 | 480 | def process_inpt(self, data): | 454 | def process_inpt(self, data, *args, **kwargs): |
658 | 481 | """ | 455 | """ |
659 | 482 | Current source input selected. See PJLink specification for format. | 456 | Current source input selected. See PJLink specification for format. |
660 | 483 | Update self.source | 457 | Update self.source |
661 | @@ -499,7 +473,7 @@ | |||
662 | 499 | log.debug('({ip}) Setting data source to "{data}"'.format(ip=self.entry.name, data=self.source)) | 473 | log.debug('({ip}) Setting data source to "{data}"'.format(ip=self.entry.name, data=self.source)) |
663 | 500 | return | 474 | return |
664 | 501 | 475 | ||
666 | 502 | def process_inst(self, data): | 476 | def process_inst(self, data, *args, **kwargs): |
667 | 503 | """ | 477 | """ |
668 | 504 | Available source inputs. See PJLink specification for format. | 478 | Available source inputs. See PJLink specification for format. |
669 | 505 | Updates self.source_available | 479 | Updates self.source_available |
670 | @@ -516,7 +490,7 @@ | |||
671 | 516 | data=self.source_available)) | 490 | data=self.source_available)) |
672 | 517 | return | 491 | return |
673 | 518 | 492 | ||
675 | 519 | def process_lamp(self, data): | 493 | def process_lamp(self, data, *args, **kwargs): |
676 | 520 | """ | 494 | """ |
677 | 521 | Lamp(s) status. See PJLink Specifications for format. | 495 | Lamp(s) status. See PJLink Specifications for format. |
678 | 522 | Data may have more than 1 lamp to process. | 496 | Data may have more than 1 lamp to process. |
679 | @@ -542,7 +516,18 @@ | |||
680 | 542 | self.lamp = lamps | 516 | self.lamp = lamps |
681 | 543 | return | 517 | return |
682 | 544 | 518 | ||
684 | 545 | def process_name(self, data): | 519 | def process_lkup(self, data, host, port): |
685 | 520 | """ | ||
686 | 521 | Process reply indicating remote is available for connection | ||
687 | 522 | |||
688 | 523 | :param data: Data packet from remote | ||
689 | 524 | :param host: Remote IP address | ||
690 | 525 | :param port: Local port packet received on | ||
691 | 526 | """ | ||
692 | 527 | # TODO: Check if autoconnect is enabled and connect? | ||
693 | 528 | pass | ||
694 | 529 | |||
695 | 530 | def process_name(self, data, *args, **kwargs): | ||
696 | 546 | """ | 531 | """ |
697 | 547 | Projector name set in projector. | 532 | Projector name set in projector. |
698 | 548 | Updates self.pjlink_name | 533 | Updates self.pjlink_name |
699 | @@ -553,7 +538,7 @@ | |||
700 | 553 | log.debug('({ip}) Setting projector PJLink name to "{data}"'.format(ip=self.entry.name, data=self.pjlink_name)) | 538 | log.debug('({ip}) Setting projector PJLink name to "{data}"'.format(ip=self.entry.name, data=self.pjlink_name)) |
701 | 554 | return | 539 | return |
702 | 555 | 540 | ||
704 | 556 | def process_pjlink(self, data): | 541 | def process_pjlink(self, data, *args, **kwargs): |
705 | 557 | """ | 542 | """ |
706 | 558 | Process initial socket connection to terminal. | 543 | Process initial socket connection to terminal. |
707 | 559 | 544 | ||
708 | @@ -594,7 +579,7 @@ | |||
709 | 594 | # Since this is an initial connection, make it a priority just in case | 579 | # Since this is an initial connection, make it a priority just in case |
710 | 595 | return self.send_command(cmd="CLSS", salt=data_hash, priority=True) | 580 | return self.send_command(cmd="CLSS", salt=data_hash, priority=True) |
711 | 596 | 581 | ||
713 | 597 | def process_powr(self, data): | 582 | def process_powr(self, data, *args, **kwargs): |
714 | 598 | """ | 583 | """ |
715 | 599 | Power status. See PJLink specification for format. | 584 | Power status. See PJLink specification for format. |
716 | 600 | Update self.power with status. Update icons if change from previous setting. | 585 | Update self.power with status. Update icons if change from previous setting. |
717 | @@ -617,7 +602,7 @@ | |||
718 | 617 | log.warning('({ip}) Unknown power response: "{data}"'.format(ip=self.entry.name, data=data)) | 602 | log.warning('({ip}) Unknown power response: "{data}"'.format(ip=self.entry.name, data=data)) |
719 | 618 | return | 603 | return |
720 | 619 | 604 | ||
722 | 620 | def process_rfil(self, data): | 605 | def process_rfil(self, data, *args, **kwargs): |
723 | 621 | """ | 606 | """ |
724 | 622 | Process replacement filter type | 607 | Process replacement filter type |
725 | 623 | """ | 608 | """ |
726 | @@ -628,7 +613,7 @@ | |||
727 | 628 | log.warning('({ip}) Saved model: "{old}"'.format(ip=self.entry.name, old=self.model_filter)) | 613 | log.warning('({ip}) Saved model: "{old}"'.format(ip=self.entry.name, old=self.model_filter)) |
728 | 629 | log.warning('({ip}) New model: "{new}"'.format(ip=self.entry.name, new=data)) | 614 | log.warning('({ip}) New model: "{new}"'.format(ip=self.entry.name, new=data)) |
729 | 630 | 615 | ||
731 | 631 | def process_rlmp(self, data): | 616 | def process_rlmp(self, data, *args, **kwargs): |
732 | 632 | """ | 617 | """ |
733 | 633 | Process replacement lamp type | 618 | Process replacement lamp type |
734 | 634 | """ | 619 | """ |
735 | @@ -639,7 +624,7 @@ | |||
736 | 639 | log.warning('({ip}) Saved lamp: "{old}"'.format(ip=self.entry.name, old=self.model_lamp)) | 624 | log.warning('({ip}) Saved lamp: "{old}"'.format(ip=self.entry.name, old=self.model_lamp)) |
737 | 640 | log.warning('({ip}) New lamp: "{new}"'.format(ip=self.entry.name, new=data)) | 625 | log.warning('({ip}) New lamp: "{new}"'.format(ip=self.entry.name, new=data)) |
738 | 641 | 626 | ||
740 | 642 | def process_snum(self, data): | 627 | def process_snum(self, data, *args, **kwargs): |
741 | 643 | """ | 628 | """ |
742 | 644 | Serial number of projector. | 629 | Serial number of projector. |
743 | 645 | 630 | ||
744 | @@ -659,7 +644,20 @@ | |||
745 | 659 | log.warning('({ip}) NOT saving serial number'.format(ip=self.entry.name)) | 644 | log.warning('({ip}) NOT saving serial number'.format(ip=self.entry.name)) |
746 | 660 | self.serial_no_received = data | 645 | self.serial_no_received = data |
747 | 661 | 646 | ||
749 | 662 | def process_sver(self, data): | 647 | def process_srch(self, data, host, port): |
750 | 648 | """ | ||
751 | 649 | Process the SRCH command. | ||
752 | 650 | |||
753 | 651 | SRCH is processed by terminals so we ignore any packet. | ||
754 | 652 | |||
755 | 653 | :param data: Data in packet | ||
756 | 654 | :param host: IP address of sending host | ||
757 | 655 | :param port: Port received on | ||
758 | 656 | """ | ||
759 | 657 | log.warning('(UDP) SRCH packet received from {host} - ignoring'.format(host=host)) | ||
760 | 658 | return | ||
761 | 659 | |||
762 | 660 | def process_sver(self, data, *args, **kwargs): | ||
763 | 663 | """ | 661 | """ |
764 | 664 | Software version of projector | 662 | Software version of projector |
765 | 665 | """ | 663 | """ |
766 | @@ -716,6 +714,7 @@ | |||
767 | 716 | self.pin = self.entry.pin | 714 | self.pin = self.entry.pin |
768 | 717 | self.port = self.entry.port | 715 | self.port = self.entry.port |
769 | 718 | self.pjlink_class = PJLINK_CLASS if self.entry.pjlink_class is None else self.entry.pjlink_class | 716 | self.pjlink_class = PJLINK_CLASS if self.entry.pjlink_class is None else self.entry.pjlink_class |
770 | 717 | self.ackn_list = {} # Replies from online projectors (Class 2 option) | ||
771 | 719 | self.db_update = False # Use to check if db needs to be updated prior to exiting | 718 | self.db_update = False # Use to check if db needs to be updated prior to exiting |
772 | 720 | # Poll time 20 seconds unless called with something else | 719 | # Poll time 20 seconds unless called with something else |
773 | 721 | self.poll_time = 20000 if 'poll_time' not in kwargs else kwargs['poll_time'] * 1000 | 720 | self.poll_time = 20000 if 'poll_time' not in kwargs else kwargs['poll_time'] * 1000 |
774 | @@ -916,7 +915,10 @@ | |||
775 | 916 | """ | 915 | """ |
776 | 917 | Clean out extraneous stuff in the buffer. | 916 | Clean out extraneous stuff in the buffer. |
777 | 918 | """ | 917 | """ |
779 | 919 | log.warning('({ip}) {message}'.format(ip=self.entry.name, message='Invalid packet' if msg is None else msg)) | 918 | log.debug('({ip}) Cleaning buffer - msg = "{message}"'.format(ip=self.entry.name, message=msg)) |
780 | 919 | if msg is None: | ||
781 | 920 | msg = 'Invalid packet' | ||
782 | 921 | log.warning('({ip}) {message}'.format(ip=self.entry.name, message=msg)) | ||
783 | 920 | self.send_busy = False | 922 | self.send_busy = False |
784 | 921 | trash_count = 0 | 923 | trash_count = 0 |
785 | 922 | while self.bytesAvailable() > 0: | 924 | while self.bytesAvailable() > 0: |
786 | @@ -960,7 +962,7 @@ | |||
787 | 960 | self.socket_timer.stop() | 962 | self.socket_timer.stop() |
788 | 961 | return self.get_data(buff=read, ip=self.ip) | 963 | return self.get_data(buff=read, ip=self.ip) |
789 | 962 | 964 | ||
791 | 963 | def get_data(self, buff, ip=None): | 965 | def get_data(self, buff, ip=None, *args, **kwargs): |
792 | 964 | """ | 966 | """ |
793 | 965 | Process received data | 967 | Process received data |
794 | 966 | 968 | ||
795 | @@ -973,45 +975,61 @@ | |||
796 | 973 | ip = self.ip | 975 | ip = self.ip |
797 | 974 | log.debug('({ip}) get_data(ip="{ip_in}" buffer="{buff}"'.format(ip=self.entry.name, ip_in=ip, buff=buff)) | 976 | log.debug('({ip}) get_data(ip="{ip_in}" buffer="{buff}"'.format(ip=self.entry.name, ip_in=ip, buff=buff)) |
798 | 975 | # NOTE: Class2 has changed to some values being UTF-8 | 977 | # NOTE: Class2 has changed to some values being UTF-8 |
800 | 976 | data_in = decode(buff, 'utf-8') | 978 | if isinstance(buff, bytes): |
801 | 979 | data_in = decode(buff, 'utf-8') | ||
802 | 980 | else: | ||
803 | 981 | data_in = buff | ||
804 | 977 | data = data_in.strip() | 982 | data = data_in.strip() |
805 | 978 | # Initial packet checks | 983 | # Initial packet checks |
806 | 979 | if (len(data) < 7): | 984 | if (len(data) < 7): |
807 | 980 | self._trash_buffer(msg='get_data(): Invalid packet - length') | 985 | self._trash_buffer(msg='get_data(): Invalid packet - length') |
808 | 981 | return self.receive_data_signal() | 986 | return self.receive_data_signal() |
809 | 982 | elif len(data) > self.max_size: | 987 | elif len(data) > self.max_size: |
811 | 983 | self._trash_buffer(msg='get_data(): Invalid packet - too long') | 988 | self._trash_buffer(msg='get_data(): Invalid packet - too long ({length} bytes)'.format(length=len(data))) |
812 | 984 | return self.receive_data_signal() | 989 | return self.receive_data_signal() |
813 | 985 | elif not data.startswith(PJLINK_PREFIX): | 990 | elif not data.startswith(PJLINK_PREFIX): |
814 | 986 | self._trash_buffer(msg='get_data(): Invalid packet - PJLink prefix missing') | 991 | self._trash_buffer(msg='get_data(): Invalid packet - PJLink prefix missing') |
815 | 987 | return self.receive_data_signal() | 992 | return self.receive_data_signal() |
817 | 988 | elif '=' not in data: | 993 | elif data[6] != '=': |
818 | 989 | self._trash_buffer(msg='get_data(): Invalid reply - Does not have "="') | 994 | self._trash_buffer(msg='get_data(): Invalid reply - Does not have "="') |
819 | 990 | return self.receive_data_signal() | 995 | return self.receive_data_signal() |
820 | 991 | log.debug('({ip}) get_data(): Checking new data "{data}"'.format(ip=self.entry.name, data=data)) | 996 | log.debug('({ip}) get_data(): Checking new data "{data}"'.format(ip=self.entry.name, data=data)) |
821 | 992 | header, data = data.split('=') | 997 | header, data = data.split('=') |
822 | 998 | log.debug('({ip}) get_data() header="{header}" data="{data}"'.format(ip=self.entry.name, | ||
823 | 999 | header=header, data=data)) | ||
824 | 993 | # At this point, the header should contain: | 1000 | # At this point, the header should contain: |
825 | 994 | # "PVCCCC" | 1001 | # "PVCCCC" |
826 | 995 | # Where: | 1002 | # Where: |
827 | 996 | # P = PJLINK_PREFIX | 1003 | # P = PJLINK_PREFIX |
828 | 997 | # V = PJLink class or version | 1004 | # V = PJLink class or version |
829 | 998 | # C = PJLink command | 1005 | # C = PJLink command |
830 | 1006 | version, cmd = header[1], header[2:].upper() | ||
831 | 1007 | log.debug('({ip}) get_data() version="{version}" cmd="{cmd}"'.format(ip=self.entry.name, | ||
832 | 1008 | version=version, cmd=cmd)) | ||
833 | 1009 | # TODO: Below commented for now since it seems to cause issues with testing some invalid data. | ||
834 | 1010 | # Revisit after more refactoring is finished. | ||
835 | 1011 | ''' | ||
836 | 999 | try: | 1012 | try: |
837 | 1000 | version, cmd = header[1], header[2:].upper() | 1013 | version, cmd = header[1], header[2:].upper() |
838 | 1014 | log.debug('({ip}) get_data() version="{version}" cmd="{cmd}"'.format(ip=self.entry.name, | ||
839 | 1015 | version=version, cmd=cmd)) | ||
840 | 1001 | except ValueError as e: | 1016 | except ValueError as e: |
841 | 1002 | self.change_status(E_INVALID_DATA) | 1017 | self.change_status(E_INVALID_DATA) |
842 | 1003 | log.warning('({ip}) get_data(): Received data: "{data}"'.format(ip=self.entry.name, data=data_in)) | 1018 | log.warning('({ip}) get_data(): Received data: "{data}"'.format(ip=self.entry.name, data=data_in)) |
843 | 1004 | self._trash_buffer('get_data(): Expected header + command + data') | 1019 | self._trash_buffer('get_data(): Expected header + command + data') |
844 | 1005 | return self.receive_data_signal() | 1020 | return self.receive_data_signal() |
845 | 1021 | ''' | ||
846 | 1006 | if cmd not in PJLINK_VALID_CMD: | 1022 | if cmd not in PJLINK_VALID_CMD: |
848 | 1007 | log.warning('({ip}) get_data(): Invalid packet - unknown command "{data}"'.format(ip=self.entry.name, | 1023 | self._trash_buffer('get_data(): Invalid packet - unknown command "{data}"'.format(ip=self.entry.name, |
849 | 1008 | data=cmd)) | 1024 | data=cmd)) |
853 | 1009 | self._trash_buffer(msg='get_data(): Unknown command "{data}"'.format(data=cmd)) | 1025 | return self.receive_data_signal() |
854 | 1010 | return self.receive_data_signal() | 1026 | elif version not in PJLINK_VALID_CMD[cmd]['version']: |
855 | 1011 | if int(self.pjlink_class) < int(version): | 1027 | self._trash_buffer(msg='get_data() Command reply version does not match a valid command version') |
856 | 1028 | return self.receive_data_signal() | ||
857 | 1029 | elif int(self.pjlink_class) < int(version): | ||
858 | 1012 | log.warning('({ip}) get_data(): Projector returned class reply higher ' | 1030 | log.warning('({ip}) get_data(): Projector returned class reply higher ' |
859 | 1013 | 'than projector stated class'.format(ip=self.entry.name)) | 1031 | 'than projector stated class'.format(ip=self.entry.name)) |
861 | 1014 | self.process_command(cmd, data) | 1032 | self.process_command(cmd, data, *args, **kwargs) |
862 | 1015 | return self.receive_data_signal() | 1033 | return self.receive_data_signal() |
863 | 1016 | 1034 | ||
864 | 1017 | @QtCore.pyqtSlot(QtNetwork.QAbstractSocket.SocketError) | 1035 | @QtCore.pyqtSlot(QtNetwork.QAbstractSocket.SocketError) |
865 | @@ -1063,16 +1081,7 @@ | |||
866 | 1063 | data=opts, | 1081 | data=opts, |
867 | 1064 | salt='' if salt is None | 1082 | salt='' if salt is None |
868 | 1065 | else ' with hash')) | 1083 | else ' with hash')) |
879 | 1066 | cmd_ver = PJLINK_VALID_CMD[cmd]['version'] | 1084 | header = PJLINK_HEADER.format(linkclass=self.pjlink_functions[cmd]["version"]) |
870 | 1067 | if self.pjlink_class in PJLINK_VALID_CMD[cmd]['version']: | ||
871 | 1068 | header = PJLINK_HEADER.format(linkclass=self.pjlink_class) | ||
872 | 1069 | elif len(cmd_ver) == 1 and (int(cmd_ver[0]) < int(self.pjlink_class)): | ||
873 | 1070 | # Typically a class 1 only command | ||
874 | 1071 | header = PJLINK_HEADER.format(linkclass=cmd_ver[0]) | ||
875 | 1072 | else: | ||
876 | 1073 | # NOTE: Once we get to version 3 then think about looping | ||
877 | 1074 | log.error('({ip}): send_command(): PJLink class check issue? Aborting'.format(ip=self.entry.name)) | ||
878 | 1075 | return | ||
880 | 1076 | out = '{salt}{header}{command} {options}{suffix}'.format(salt="" if salt is None else salt, | 1085 | out = '{salt}{header}{command} {options}{suffix}'.format(salt="" if salt is None else salt, |
881 | 1077 | header=header, | 1086 | header=header, |
882 | 1078 | command=cmd, | 1087 | command=cmd, |
883 | 1079 | 1088 | ||
884 | === modified file 'tests/openlp_core/projectors/test_projector_db.py' | |||
885 | --- tests/openlp_core/projectors/test_projector_db.py 2018-01-13 05:41:42 +0000 | |||
886 | +++ tests/openlp_core/projectors/test_projector_db.py 2018-04-20 06:16:23 +0000 | |||
887 | @@ -29,12 +29,15 @@ | |||
888 | 29 | import shutil | 29 | import shutil |
889 | 30 | from tempfile import mkdtemp | 30 | from tempfile import mkdtemp |
890 | 31 | from unittest import TestCase | 31 | from unittest import TestCase |
892 | 32 | from unittest.mock import patch | 32 | from unittest.mock import MagicMock, patch |
893 | 33 | 33 | ||
894 | 34 | from openlp.core.common.registry import Registry | ||
895 | 34 | from openlp.core.lib.db import upgrade_db | 35 | from openlp.core.lib.db import upgrade_db |
896 | 35 | from openlp.core.projectors import upgrade | 36 | from openlp.core.projectors import upgrade |
897 | 36 | from openlp.core.projectors.constants import PJLINK_PORT | 37 | from openlp.core.projectors.constants import PJLINK_PORT |
898 | 37 | from openlp.core.projectors.db import Manufacturer, Model, Projector, ProjectorDB, ProjectorSource, Source | 38 | from openlp.core.projectors.db import Manufacturer, Model, Projector, ProjectorDB, ProjectorSource, Source |
899 | 39 | from openlp.core.ui.mainwindow import MainWindow | ||
900 | 40 | from tests.helpers.testmixin import TestMixin | ||
901 | 38 | from tests.resources.projector.data import TEST_DB_PJLINK1, TEST_DB, TEST1_DATA, TEST2_DATA, TEST3_DATA | 41 | from tests.resources.projector.data import TEST_DB_PJLINK1, TEST_DB, TEST1_DATA, TEST2_DATA, TEST3_DATA |
902 | 39 | from tests.utils.constants import TEST_RESOURCES_PATH | 42 | from tests.utils.constants import TEST_RESOURCES_PATH |
903 | 40 | 43 | ||
904 | @@ -122,7 +125,7 @@ | |||
905 | 122 | assert updated_to_version == latest_version, 'The projector DB should have been upgrade to the latest version' | 125 | assert updated_to_version == latest_version, 'The projector DB should have been upgrade to the latest version' |
906 | 123 | 126 | ||
907 | 124 | 127 | ||
909 | 125 | class TestProjectorDB(TestCase): | 128 | class TestProjectorDB(TestCase, TestMixin): |
910 | 126 | """ | 129 | """ |
911 | 127 | Test case for ProjectorDB | 130 | Test case for ProjectorDB |
912 | 128 | """ | 131 | """ |
913 | @@ -131,6 +134,33 @@ | |||
914 | 131 | """ | 134 | """ |
915 | 132 | Set up anything necessary for all tests | 135 | Set up anything necessary for all tests |
916 | 133 | """ | 136 | """ |
917 | 137 | # Create a test app to keep from segfaulting | ||
918 | 138 | Registry.create() | ||
919 | 139 | self.registry = Registry() | ||
920 | 140 | self.setup_application() | ||
921 | 141 | # Mock cursor busy/normal methods. | ||
922 | 142 | self.app.set_busy_cursor = MagicMock() | ||
923 | 143 | self.app.set_normal_cursor = MagicMock() | ||
924 | 144 | self.app.args = [] | ||
925 | 145 | Registry().register('application', self.app) | ||
926 | 146 | Registry().set_flag('no_web_server', True) | ||
927 | 147 | # Mock classes and methods used by mainwindow. | ||
928 | 148 | with patch('openlp.core.ui.mainwindow.SettingsForm'), \ | ||
929 | 149 | patch('openlp.core.ui.mainwindow.ImageManager'), \ | ||
930 | 150 | patch('openlp.core.ui.mainwindow.LiveController'), \ | ||
931 | 151 | patch('openlp.core.ui.mainwindow.PreviewController'), \ | ||
932 | 152 | patch('openlp.core.ui.mainwindow.OpenLPDockWidget'), \ | ||
933 | 153 | patch('openlp.core.ui.mainwindow.QtWidgets.QToolBox'), \ | ||
934 | 154 | patch('openlp.core.ui.mainwindow.QtWidgets.QMainWindow.addDockWidget'), \ | ||
935 | 155 | patch('openlp.core.ui.mainwindow.ServiceManager'), \ | ||
936 | 156 | patch('openlp.core.ui.mainwindow.ThemeManager'), \ | ||
937 | 157 | patch('openlp.core.ui.mainwindow.ProjectorManager'), \ | ||
938 | 158 | patch('openlp.core.ui.mainwindow.Renderer'), \ | ||
939 | 159 | patch('openlp.core.ui.mainwindow.websockets.WebSocketServer'), \ | ||
940 | 160 | patch('openlp.core.ui.mainwindow.server.HttpServer'): | ||
941 | 161 | self.main_window = MainWindow() | ||
942 | 162 | |||
943 | 163 | # Create a temporary database directory and database | ||
944 | 134 | self.tmp_folder = mkdtemp(prefix='openlp_') | 164 | self.tmp_folder = mkdtemp(prefix='openlp_') |
945 | 135 | tmpdb_url = 'sqlite:///{db}'.format(db=os.path.join(self.tmp_folder, TEST_DB)) | 165 | tmpdb_url = 'sqlite:///{db}'.format(db=os.path.join(self.tmp_folder, TEST_DB)) |
946 | 136 | mocked_init_url.return_value = tmpdb_url | 166 | mocked_init_url.return_value = tmpdb_url |
947 | @@ -139,9 +169,12 @@ | |||
948 | 139 | def tearDown(self): | 169 | def tearDown(self): |
949 | 140 | """ | 170 | """ |
950 | 141 | Clean up | 171 | Clean up |
951 | 172 | |||
952 | 173 | Delete all the C++ objects at the end so that we don't have a segfault | ||
953 | 142 | """ | 174 | """ |
954 | 143 | self.projector.session.close() | 175 | self.projector.session.close() |
955 | 144 | self.projector = None | 176 | self.projector = None |
956 | 177 | del self.main_window | ||
957 | 145 | # Ignore errors since windows can have problems with locked files | 178 | # Ignore errors since windows can have problems with locked files |
958 | 146 | shutil.rmtree(self.tmp_folder, ignore_errors=True) | 179 | shutil.rmtree(self.tmp_folder, ignore_errors=True) |
959 | 147 | 180 | ||
960 | 148 | 181 | ||
961 | === renamed file 'tests/openlp_core/projectors/test_projectoreditform.py' => 'tests/openlp_core/projectors/test_projector_editform.py' | |||
962 | === modified file 'tests/openlp_core/projectors/test_projector_pjlink_cmd_routing.py' | |||
963 | --- tests/openlp_core/projectors/test_projector_pjlink_cmd_routing.py 2018-01-13 05:41:42 +0000 | |||
964 | +++ tests/openlp_core/projectors/test_projector_pjlink_cmd_routing.py 2018-04-20 06:16:23 +0000 | |||
965 | @@ -39,30 +39,31 @@ | |||
966 | 39 | """ | 39 | """ |
967 | 40 | Tests for the PJLink module command routing | 40 | Tests for the PJLink module command routing |
968 | 41 | """ | 41 | """ |
970 | 42 | def test_get_data_unknown_command(self): | 42 | @patch.object(openlp.core.projectors.pjlink, 'log') |
971 | 43 | def test_get_data_unknown_command(self, mock_log): | ||
972 | 43 | """ | 44 | """ |
973 | 44 | Test not a valid command | 45 | Test not a valid command |
974 | 45 | """ | 46 | """ |
975 | 46 | # GIVEN: Test object | 47 | # GIVEN: Test object |
995 | 47 | with patch.object(openlp.core.projectors.pjlink, 'log') as mock_log, \ | 48 | pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True) |
996 | 48 | patch.object(openlp.core.projectors.pjlink.PJLink, '_trash_buffer') as mock_buffer: | 49 | pjlink.pjlink_functions = MagicMock() |
997 | 49 | 50 | log_warning_text = [call('({ip}) get_data(): Invalid packet - ' | |
998 | 50 | pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True) | 51 | 'unknown command "UNKN"'.format(ip=pjlink.name))] |
999 | 51 | pjlink.pjlink_functions = MagicMock() | 52 | log_debug_text = [call('(___TEST_ONE___) get_data(ip="111.111.111.111" buffer="%1UNKN=Huh?"'), |
1000 | 52 | log_warning_text = [call('({ip}) get_data(): Invalid packet - ' | 53 | call('(___TEST_ONE___) get_data(): Checking new data "%1UNKN=Huh?"'), |
1001 | 53 | 'unknown command "UNK"'.format(ip=pjlink.name))] | 54 | call('(___TEST_ONE___) get_data() header="%1UNKN" data="Huh?"'), |
1002 | 54 | log_debug_text = [call('({ip}) get_data(ip="111.111.111.111" ' | 55 | call('(___TEST_ONE___) get_data() version="1" cmd="UNKN"'), |
1003 | 55 | 'buffer="b\'%1UNK=Huh?\'"'.format(ip=pjlink.name)), | 56 | call('(___TEST_ONE___) Cleaning buffer - msg = "get_data(): ' |
1004 | 56 | call('({ip}) get_data(): Checking new data "%1UNK=Huh?"'.format(ip=pjlink.name))] | 57 | 'Invalid packet - unknown command "UNKN""'), |
1005 | 57 | 58 | call('(___TEST_ONE___) Finished cleaning buffer - 0 bytes dropped')] | |
1006 | 58 | # WHEN: get_data called with an unknown command | 59 | |
1007 | 59 | pjlink.get_data(buff='{prefix}1UNK=Huh?'.format(prefix=PJLINK_PREFIX).encode('utf-8')) | 60 | # WHEN: get_data called with an unknown command |
1008 | 60 | 61 | pjlink.get_data(buff='{prefix}1UNKN=Huh?'.format(prefix=PJLINK_PREFIX)) | |
1009 | 61 | # THEN: Appropriate log entries should have been made and methods called/not called | 62 | |
1010 | 62 | mock_log.debug.assert_has_calls(log_debug_text) | 63 | # THEN: Appropriate log entries should have been made and methods called/not called |
1011 | 63 | mock_log.warning.assert_has_calls(log_warning_text) | 64 | mock_log.warning.assert_has_calls(log_warning_text) |
1012 | 64 | assert pjlink.pjlink_functions.called is False, 'Should not have accessed pjlink_functions' | 65 | mock_log.debug.assert_has_calls(log_debug_text) |
1013 | 65 | assert mock_buffer.called is True, 'Should have called _trash_buffer' | 66 | assert pjlink.pjlink_functions.called is False, 'Should not have accessed pjlink_functions' |
1014 | 66 | 67 | ||
1015 | 67 | def test_process_command_call_clss(self): | 68 | def test_process_command_call_clss(self): |
1016 | 68 | """ | 69 | """ |
1017 | @@ -219,7 +220,6 @@ | |||
1018 | 219 | """ | 220 | """ |
1019 | 220 | Test command returned success | 221 | Test command returned success |
1020 | 221 | """ | 222 | """ |
1021 | 222 | # GIVEN: Initial mocks and data | ||
1022 | 223 | # GIVEN: Test object and mocks | 223 | # GIVEN: Test object and mocks |
1023 | 224 | with patch.object(openlp.core.projectors.pjlink, 'log') as mock_log, \ | 224 | with patch.object(openlp.core.projectors.pjlink, 'log') as mock_log, \ |
1024 | 225 | patch.object(openlp.core.projectors.pjlink.PJLink, 'send_command') as mock_send_command, \ | 225 | patch.object(openlp.core.projectors.pjlink.PJLink, 'send_command') as mock_send_command, \ |
1025 | 226 | 226 | ||
1026 | === modified file 'tests/openlp_core/projectors/test_projector_pjlink_commands_02.py' | |||
1027 | --- tests/openlp_core/projectors/test_projector_pjlink_commands_02.py 2018-01-13 05:41:42 +0000 | |||
1028 | +++ tests/openlp_core/projectors/test_projector_pjlink_commands_02.py 2018-04-20 06:16:23 +0000 | |||
1029 | @@ -22,14 +22,14 @@ | |||
1030 | 22 | """ | 22 | """ |
1031 | 23 | Package to test the openlp.core.projectors.pjlink commands package. | 23 | Package to test the openlp.core.projectors.pjlink commands package. |
1032 | 24 | """ | 24 | """ |
1034 | 25 | from unittest import TestCase | 25 | from unittest import TestCase, skip |
1035 | 26 | from unittest.mock import patch, call | 26 | from unittest.mock import patch, call |
1036 | 27 | 27 | ||
1037 | 28 | import openlp.core.projectors.pjlink | 28 | import openlp.core.projectors.pjlink |
1039 | 29 | from openlp.core.projectors.constants import S_CONNECTED, S_OFF, S_ON | 29 | from openlp.core.projectors.constants import PJLINK_PORT, S_CONNECTED, S_OFF, S_ON |
1040 | 30 | from openlp.core.projectors.db import Projector | 30 | from openlp.core.projectors.db import Projector |
1043 | 31 | from openlp.core.projectors.pjlink import PJLink | 31 | from openlp.core.projectors.pjlink import PJLink, PJLinkUDP |
1044 | 32 | from tests.resources.projector.data import TEST_HASH, TEST_PIN, TEST_SALT, TEST1_DATA | 32 | from tests.resources.projector.data import TEST_HASH, TEST_PIN, TEST_SALT, TEST1_DATA, TEST2_DATA |
1045 | 33 | 33 | ||
1046 | 34 | 34 | ||
1047 | 35 | class TestPJLinkCommands(TestCase): | 35 | class TestPJLinkCommands(TestCase): |
1048 | @@ -235,3 +235,114 @@ | |||
1049 | 235 | mock_log.error.assert_has_calls(log_check) | 235 | mock_log.error.assert_has_calls(log_check) |
1050 | 236 | assert 1 == mock_disconnect_from_host.call_count, 'Should have only been called once' | 236 | assert 1 == mock_disconnect_from_host.call_count, 'Should have only been called once' |
1051 | 237 | mock_send_command.assert_not_called() | 237 | mock_send_command.assert_not_called() |
1052 | 238 | |||
1053 | 239 | @skip('Change to pjlink_udp.get_datagram() call') | ||
1054 | 240 | @patch.object(openlp.core.projectors.pjlink, 'log') | ||
1055 | 241 | def test_process_ackn_duplicate(self, mock_log): | ||
1056 | 242 | """ | ||
1057 | 243 | Test process_ackn method with multiple calls with same data | ||
1058 | 244 | """ | ||
1059 | 245 | # TODO: Change this to call pjlink_udp.get_datagram() so ACKN can be processed properly | ||
1060 | 246 | |||
1061 | 247 | # GIVEN: Test setup | ||
1062 | 248 | pjlink = PJLink(projector=self.test_list[0]) | ||
1063 | 249 | check_list = {TEST1_DATA['ip']: {'data': TEST1_DATA['mac_adx'], 'port': PJLINK_PORT}} | ||
1064 | 250 | log_warn_calls = [call('(___TEST_ONE___) Host {host} already replied - ' | ||
1065 | 251 | 'ignoring'.format(host=TEST1_DATA['ip']))] | ||
1066 | 252 | log_debug_calls = [call('PJlinkCommands(args=() kwargs={})'), | ||
1067 | 253 | call('(___TEST_ONE___) reset_information() connect status is S_NOT_CONNECTED'), | ||
1068 | 254 | call('(___TEST_ONE___) Processing ACKN packet'), | ||
1069 | 255 | call('(___TEST_ONE___) Adding {host} to ACKN list'.format(host=TEST1_DATA['ip'])), | ||
1070 | 256 | call('(___TEST_ONE___) Processing ACKN packet')] | ||
1071 | 257 | |||
1072 | 258 | # WHEN: process_ackn called twice with same data | ||
1073 | 259 | pjlink.process_ackn(data=TEST1_DATA['mac_adx'], host=TEST1_DATA['ip'], port=PJLINK_PORT) | ||
1074 | 260 | pjlink.process_ackn(data=TEST1_DATA['mac_adx'], host=TEST1_DATA['ip'], port=PJLINK_PORT) | ||
1075 | 261 | |||
1076 | 262 | # THEN: pjlink_udp.ack_list should equal test_list | ||
1077 | 263 | # NOTE: This assert only returns AssertionError - does not list differences. Maybe add a compare function? | ||
1078 | 264 | if pjlink.ackn_list != check_list: | ||
1079 | 265 | # Check this way so we can print differences to stdout | ||
1080 | 266 | print('\nackn_list: ', pjlink.ackn_list) | ||
1081 | 267 | print('test_list: ', check_list, '\n') | ||
1082 | 268 | assert pjlink.ackn_list == check_list | ||
1083 | 269 | mock_log.debug.assert_has_calls(log_debug_calls) | ||
1084 | 270 | mock_log.warning.assert_has_calls(log_warn_calls) | ||
1085 | 271 | |||
1086 | 272 | @skip('Change to pjlink_udp.get_datagram() call') | ||
1087 | 273 | @patch.object(openlp.core.projectors.pjlink, 'log') | ||
1088 | 274 | def test_process_ackn_multiple(self, mock_log): | ||
1089 | 275 | """ | ||
1090 | 276 | Test process_ackn method with multiple calls | ||
1091 | 277 | """ | ||
1092 | 278 | # TODO: Change this to call pjlink_udp.get_datagram() so ACKN can be processed properly | ||
1093 | 279 | |||
1094 | 280 | # GIVEN: Test setup | ||
1095 | 281 | pjlink_udp = PJLinkUDP(projector_list=self.test_list) | ||
1096 | 282 | check_list = {TEST1_DATA['ip']: {'data': TEST1_DATA['mac_adx'], 'port': PJLINK_PORT}, | ||
1097 | 283 | TEST2_DATA['ip']: {'data': TEST2_DATA['mac_adx'], 'port': PJLINK_PORT}} | ||
1098 | 284 | log_debug_calls = [call('(UDP) PJLinkUDP() Initialized'), | ||
1099 | 285 | call('(UDP) Processing ACKN packet'), | ||
1100 | 286 | call('(UDP) Adding {host} to ACKN list'.format(host=TEST1_DATA['ip'])), | ||
1101 | 287 | call('(UDP) Processing ACKN packet'), | ||
1102 | 288 | call('(UDP) Adding {host} to ACKN list'.format(host=TEST2_DATA['ip']))] | ||
1103 | 289 | |||
1104 | 290 | # WHEN: process_ackn called twice with different data | ||
1105 | 291 | pjlink_udp.process_ackn(data=TEST1_DATA['mac_adx'], host=TEST1_DATA['ip'], port=PJLINK_PORT) | ||
1106 | 292 | pjlink_udp.process_ackn(data=TEST2_DATA['mac_adx'], host=TEST2_DATA['ip'], port=PJLINK_PORT) | ||
1107 | 293 | |||
1108 | 294 | # THEN: pjlink_udp.ack_list should equal test_list | ||
1109 | 295 | # NOTE: This assert only returns AssertionError - does not list differences. Maybe add a compare function? | ||
1110 | 296 | if pjlink_udp.ackn_list != check_list: | ||
1111 | 297 | # Check this way so we can print differences to stdout | ||
1112 | 298 | print('\nackn_list: ', pjlink_udp.ackn_list) | ||
1113 | 299 | print('test_list: ', check_list) | ||
1114 | 300 | assert pjlink_udp.ackn_list == check_list | ||
1115 | 301 | mock_log.debug.assert_has_calls(log_debug_calls) | ||
1116 | 302 | |||
1117 | 303 | @skip('Change to pjlink_udp.get_datagram() call') | ||
1118 | 304 | @patch.object(openlp.core.projectors.pjlink, 'log') | ||
1119 | 305 | def test_process_ackn_single(self, mock_log): | ||
1120 | 306 | """ | ||
1121 | 307 | Test process_ackn method with single call | ||
1122 | 308 | """ | ||
1123 | 309 | # TODO: Change this to call pjlink_udp.get_datagram() so ACKN can be processed properly | ||
1124 | 310 | |||
1125 | 311 | # GIVEN: Test setup | ||
1126 | 312 | pjlink_udp = PJLinkUDP(projector_list=self.test_list) | ||
1127 | 313 | check_list = {TEST1_DATA['ip']: {'data': TEST1_DATA['mac_adx'], 'port': PJLINK_PORT}} | ||
1128 | 314 | log_debug_calls = [call('(UDP) PJLinkUDP() Initialized'), | ||
1129 | 315 | call('(UDP) Processing ACKN packet'), | ||
1130 | 316 | call('(UDP) Adding {host} to ACKN list'.format(host=TEST1_DATA['ip']))] | ||
1131 | 317 | |||
1132 | 318 | # WHEN: process_ackn called twice with different data | ||
1133 | 319 | pjlink_udp.process_ackn(data=TEST1_DATA['mac_adx'], host=TEST1_DATA['ip'], port=PJLINK_PORT) | ||
1134 | 320 | |||
1135 | 321 | # THEN: pjlink_udp.ack_list should equal test_list | ||
1136 | 322 | # NOTE: This assert only returns AssertionError - does not list differences. Maybe add a compare function? | ||
1137 | 323 | if pjlink_udp.ackn_list != check_list: | ||
1138 | 324 | # Check this way so we can print differences to stdout | ||
1139 | 325 | print('\nackn_list: ', pjlink_udp.ackn_list) | ||
1140 | 326 | print('test_list: ', check_list) | ||
1141 | 327 | assert pjlink_udp.ackn_list == check_list | ||
1142 | 328 | mock_log.debug.assert_has_calls(log_debug_calls) | ||
1143 | 329 | |||
1144 | 330 | @skip('Change to pjlink_udp.get_datagram() call') | ||
1145 | 331 | @patch.object(openlp.core.projectors.pjlink, 'log') | ||
1146 | 332 | def test_process_srch(self, mock_log): | ||
1147 | 333 | """ | ||
1148 | 334 | Test process_srch method | ||
1149 | 335 | """ | ||
1150 | 336 | # TODO: Change this to call pjlink_udp.get_datagram() so ACKN can be processed properly | ||
1151 | 337 | |||
1152 | 338 | # GIVEN: Test setup | ||
1153 | 339 | log_warn_calls = [call('(UDP) SRCH packet received from {ip} - ignoring'.format(ip=TEST1_DATA['ip'])), ] | ||
1154 | 340 | log_debug_calls = [call('(UDP) PJLinkUDP() Initialized'), ] | ||
1155 | 341 | pjlink_udp = PJLinkUDP(projector_list=self.test_list) | ||
1156 | 342 | |||
1157 | 343 | # WHEN: process_srch called | ||
1158 | 344 | pjlink_udp.process_srch(data=None, host=TEST1_DATA['ip'], port=PJLINK_PORT) | ||
1159 | 345 | |||
1160 | 346 | # THEN: log entries should be entered | ||
1161 | 347 | mock_log.warning.assert_has_calls(log_warn_calls) | ||
1162 | 348 | mock_log.debug.assert_has_calls(log_debug_calls) | ||
1163 | 238 | 349 | ||
1164 | === modified file 'tests/openlp_core/projectors/test_projector_pjlink_udp.py' | |||
1165 | --- tests/openlp_core/projectors/test_projector_pjlink_udp.py 2018-02-11 11:42:13 +0000 | |||
1166 | +++ tests/openlp_core/projectors/test_projector_pjlink_udp.py 2018-04-20 06:16:23 +0000 | |||
1167 | @@ -28,10 +28,10 @@ | |||
1168 | 28 | from unittest.mock import call, patch | 28 | from unittest.mock import call, patch |
1169 | 29 | 29 | ||
1170 | 30 | import openlp.core.projectors.pjlink | 30 | import openlp.core.projectors.pjlink |
1172 | 31 | from openlp.core.projectors.constants import PJLINK_MAX_PACKET, PJLINK_PORT, PJLINK_PREFIX | 31 | from openlp.core.projectors.constants import PJLINK_PORT |
1173 | 32 | 32 | ||
1174 | 33 | from openlp.core.projectors.db import Projector | 33 | from openlp.core.projectors.db import Projector |
1176 | 34 | from openlp.core.projectors.pjlink import PJLinkUDP | 34 | from openlp.core.projectors.pjlink import PJLinkUDP, PJLink |
1177 | 35 | from tests.resources.projector.data import TEST1_DATA, TEST2_DATA | 35 | from tests.resources.projector.data import TEST1_DATA, TEST2_DATA |
1178 | 36 | 36 | ||
1179 | 37 | 37 | ||
1180 | @@ -43,7 +43,8 @@ | |||
1181 | 43 | """ | 43 | """ |
1182 | 44 | Setup generic test conditions | 44 | Setup generic test conditions |
1183 | 45 | """ | 45 | """ |
1185 | 46 | self.test_list = [Projector(**TEST1_DATA), Projector(**TEST2_DATA)] | 46 | self.test_list = [PJLink(projector=Projector(**TEST1_DATA)), |
1186 | 47 | PJLink(projector=Projector(**TEST2_DATA))] | ||
1187 | 47 | 48 | ||
1188 | 48 | def tearDown(self): | 49 | def tearDown(self): |
1189 | 49 | """ | 50 | """ |
1190 | @@ -52,132 +53,6 @@ | |||
1191 | 52 | self.test_list = None | 53 | self.test_list = None |
1192 | 53 | 54 | ||
1193 | 54 | @patch.object(openlp.core.projectors.pjlink, 'log') | 55 | @patch.object(openlp.core.projectors.pjlink, 'log') |
1194 | 55 | def test_get_datagram_data_invalid_class(self, mock_log): | ||
1195 | 56 | """ | ||
1196 | 57 | Test get_datagram with invalid class number | ||
1197 | 58 | """ | ||
1198 | 59 | # GIVEN: Test setup | ||
1199 | 60 | pjlink_udp = PJLinkUDP(projector_list=self.test_list) | ||
1200 | 61 | log_warn_calls = [call('(UDP) Invalid packet - missing/invalid PJLink class version')] | ||
1201 | 62 | log_debug_calls = [call('(UDP) PJLinkUDP() Initialized'), | ||
1202 | 63 | call('(UDP) get_datagram() - Receiving data'), | ||
1203 | 64 | call('(UDP) 24 bytes received from 111.111.111.111 on port 4352'), | ||
1204 | 65 | call('(UDP) packet "%1ACKN=11:11:11:11:11:11"')] | ||
1205 | 66 | with patch.object(pjlink_udp, 'pendingDatagramSize') as mock_datagram, \ | ||
1206 | 67 | patch.object(pjlink_udp, 'readDatagram') as mock_read: | ||
1207 | 68 | mock_datagram.return_value = 24 | ||
1208 | 69 | mock_read.return_value = ('{prefix}1ACKN={mac}'.format(prefix=PJLINK_PREFIX, mac=TEST1_DATA['mac_adx']), | ||
1209 | 70 | TEST1_DATA['ip'], PJLINK_PORT) | ||
1210 | 71 | |||
1211 | 72 | # WHEN: get_datagram called with 0 bytes ready | ||
1212 | 73 | pjlink_udp.get_datagram() | ||
1213 | 74 | |||
1214 | 75 | # THEN: Log entries should be made and method returns | ||
1215 | 76 | mock_log.debug.assert_has_calls(log_debug_calls) | ||
1216 | 77 | mock_log.warn.assert_has_calls(log_warn_calls) | ||
1217 | 78 | |||
1218 | 79 | @patch.object(openlp.core.projectors.pjlink, 'log') | ||
1219 | 80 | def test_get_datagram_data_invalid_command(self, mock_log): | ||
1220 | 81 | """ | ||
1221 | 82 | Test get_datagram with invalid PJLink UDP command | ||
1222 | 83 | """ | ||
1223 | 84 | # GIVEN: Test setup | ||
1224 | 85 | pjlink_udp = PJLinkUDP(projector_list=self.test_list) | ||
1225 | 86 | log_warn_calls = [call('(UDP) Invalid packet - not a valid PJLink UDP reply')] | ||
1226 | 87 | log_debug_calls = [call('(UDP) PJLinkUDP() Initialized'), | ||
1227 | 88 | call('(UDP) get_datagram() - Receiving data'), | ||
1228 | 89 | call('(UDP) 24 bytes received from 111.111.111.111 on port 4352'), | ||
1229 | 90 | call('(UDP) packet "%2DUMB=11:11:11:11:11:11"')] | ||
1230 | 91 | with patch.object(pjlink_udp, 'pendingDatagramSize') as mock_datagram, \ | ||
1231 | 92 | patch.object(pjlink_udp, 'readDatagram') as mock_read: | ||
1232 | 93 | mock_datagram.return_value = 24 | ||
1233 | 94 | mock_read.return_value = ('{prefix}2DUMB={mac}'.format(prefix=PJLINK_PREFIX, mac=TEST1_DATA['mac_adx']), | ||
1234 | 95 | TEST1_DATA['ip'], PJLINK_PORT) | ||
1235 | 96 | |||
1236 | 97 | # WHEN: get_datagram called with 0 bytes ready | ||
1237 | 98 | pjlink_udp.get_datagram() | ||
1238 | 99 | |||
1239 | 100 | # THEN: Log entries should be made and method returns | ||
1240 | 101 | mock_log.debug.assert_has_calls(log_debug_calls) | ||
1241 | 102 | mock_log.warn.assert_has_calls(log_warn_calls) | ||
1242 | 103 | |||
1243 | 104 | @patch.object(openlp.core.projectors.pjlink, 'log') | ||
1244 | 105 | def test_get_datagram_data_invalid_prefix(self, mock_log): | ||
1245 | 106 | """ | ||
1246 | 107 | Test get_datagram when prefix != PJLINK_PREFIX | ||
1247 | 108 | """ | ||
1248 | 109 | # GIVEN: Test setup | ||
1249 | 110 | pjlink_udp = PJLinkUDP(projector_list=self.test_list) | ||
1250 | 111 | log_warn_calls = [call('(UDP) Invalid packet - does not start with PJLINK_PREFIX')] | ||
1251 | 112 | log_debug_calls = [call('(UDP) PJLinkUDP() Initialized'), | ||
1252 | 113 | call('(UDP) get_datagram() - Receiving data'), | ||
1253 | 114 | call('(UDP) 24 bytes received from 111.111.111.111 on port 4352'), | ||
1254 | 115 | call('(UDP) packet "$2ACKN=11:11:11:11:11:11"')] | ||
1255 | 116 | with patch.object(pjlink_udp, 'pendingDatagramSize') as mock_datagram, \ | ||
1256 | 117 | patch.object(pjlink_udp, 'readDatagram') as mock_read: | ||
1257 | 118 | mock_datagram.return_value = 24 | ||
1258 | 119 | mock_read.return_value = ('{prefix}2ACKN={mac}'.format(prefix='$', mac=TEST1_DATA['mac_adx']), | ||
1259 | 120 | TEST1_DATA['ip'], PJLINK_PORT) | ||
1260 | 121 | |||
1261 | 122 | # WHEN: get_datagram called with 0 bytes ready | ||
1262 | 123 | pjlink_udp.get_datagram() | ||
1263 | 124 | |||
1264 | 125 | # THEN: Log entries should be made and method returns | ||
1265 | 126 | mock_log.debug.assert_has_calls(log_debug_calls) | ||
1266 | 127 | mock_log.warn.assert_has_calls(log_warn_calls) | ||
1267 | 128 | |||
1268 | 129 | @patch.object(openlp.core.projectors.pjlink, 'log') | ||
1269 | 130 | def test_get_datagram_data_invalid_separator(self, mock_log): | ||
1270 | 131 | """ | ||
1271 | 132 | Test get_datagram when separator not equal to = | ||
1272 | 133 | """ | ||
1273 | 134 | # GIVEN: Test setup | ||
1274 | 135 | pjlink_udp = PJLinkUDP(projector_list=self.test_list) | ||
1275 | 136 | log_warn_calls = [call('(UDP) Invalid packet - separator missing')] | ||
1276 | 137 | log_debug_calls = [call('(UDP) PJLinkUDP() Initialized'), | ||
1277 | 138 | call('(UDP) get_datagram() - Receiving data'), | ||
1278 | 139 | call('(UDP) 24 bytes received from 111.111.111.111 on port 4352'), | ||
1279 | 140 | call('(UDP) packet "%2ACKN 11:11:11:11:11:11"')] | ||
1280 | 141 | with patch.object(pjlink_udp, 'pendingDatagramSize') as mock_datagram, \ | ||
1281 | 142 | patch.object(pjlink_udp, 'readDatagram') as mock_read: | ||
1282 | 143 | mock_datagram.return_value = 24 | ||
1283 | 144 | mock_read.return_value = ('{prefix}2ACKN {mac}'.format(prefix=PJLINK_PREFIX, mac=TEST1_DATA['mac_adx']), | ||
1284 | 145 | TEST1_DATA['ip'], PJLINK_PORT) | ||
1285 | 146 | |||
1286 | 147 | # WHEN: get_datagram called with 0 bytes ready | ||
1287 | 148 | pjlink_udp.get_datagram() | ||
1288 | 149 | |||
1289 | 150 | # THEN: Log entries should be made and method returns | ||
1290 | 151 | mock_log.debug.assert_has_calls(log_debug_calls) | ||
1291 | 152 | mock_log.warn.assert_has_calls(log_warn_calls) | ||
1292 | 153 | |||
1293 | 154 | @patch.object(openlp.core.projectors.pjlink, 'log') | ||
1294 | 155 | def test_get_datagram_data_long(self, mock_log): | ||
1295 | 156 | """ | ||
1296 | 157 | Test get_datagram when datagram > PJLINK_MAX_PACKET | ||
1297 | 158 | """ | ||
1298 | 159 | # GIVEN: Test setup | ||
1299 | 160 | pjlink_udp = PJLinkUDP(projector_list=self.test_list) | ||
1300 | 161 | log_warn_calls = [call('(UDP) Invalid packet - length too long')] | ||
1301 | 162 | log_debug_calls = [call('(UDP) PJLinkUDP() Initialized'), | ||
1302 | 163 | call('(UDP) get_datagram() - Receiving data'), | ||
1303 | 164 | call('(UDP) 143 bytes received from 111.111.111.111 on port 4352'), | ||
1304 | 165 | call('(UDP) packet "%2ACKN={long}"'.format(long='X' * PJLINK_MAX_PACKET))] | ||
1305 | 166 | with patch.object(pjlink_udp, 'pendingDatagramSize') as mock_datagram, \ | ||
1306 | 167 | patch.object(pjlink_udp, 'readDatagram') as mock_read: | ||
1307 | 168 | mock_datagram.return_value = PJLINK_MAX_PACKET + 7 | ||
1308 | 169 | mock_read.return_value = ('{prefix}2ACKN={long}'.format(prefix=PJLINK_PREFIX, | ||
1309 | 170 | long='X' * PJLINK_MAX_PACKET), | ||
1310 | 171 | TEST1_DATA['ip'], PJLINK_PORT) | ||
1311 | 172 | |||
1312 | 173 | # WHEN: get_datagram called with 0 bytes ready | ||
1313 | 174 | pjlink_udp.get_datagram() | ||
1314 | 175 | |||
1315 | 176 | # THEN: Log entries should be made and method returns | ||
1316 | 177 | mock_log.debug.assert_has_calls(log_debug_calls) | ||
1317 | 178 | mock_log.warn.assert_has_calls(log_warn_calls) | ||
1318 | 179 | |||
1319 | 180 | @patch.object(openlp.core.projectors.pjlink, 'log') | ||
1320 | 181 | def test_get_datagram_data_negative_zero_length(self, mock_log): | 56 | def test_get_datagram_data_negative_zero_length(self, mock_log): |
1321 | 182 | """ | 57 | """ |
1322 | 183 | Test get_datagram when pendingDatagramSize = 0 | 58 | Test get_datagram when pendingDatagramSize = 0 |
1323 | @@ -196,7 +71,7 @@ | |||
1324 | 196 | pjlink_udp.get_datagram() | 71 | pjlink_udp.get_datagram() |
1325 | 197 | 72 | ||
1326 | 198 | # THEN: Log entries should be made and method returns | 73 | # THEN: Log entries should be made and method returns |
1328 | 199 | mock_log.warn.assert_has_calls(log_warn_calls) | 74 | mock_log.warning.assert_has_calls(log_warn_calls) |
1329 | 200 | mock_log.debug.assert_has_calls(log_debug_calls) | 75 | mock_log.debug.assert_has_calls(log_debug_calls) |
1330 | 201 | 76 | ||
1331 | 202 | @patch.object(openlp.core.projectors.pjlink, 'log') | 77 | @patch.object(openlp.core.projectors.pjlink, 'log') |
1332 | @@ -206,41 +81,18 @@ | |||
1333 | 206 | """ | 81 | """ |
1334 | 207 | # GIVEN: Test setup | 82 | # GIVEN: Test setup |
1335 | 208 | pjlink_udp = PJLinkUDP(projector_list=self.test_list) | 83 | pjlink_udp = PJLinkUDP(projector_list=self.test_list) |
1339 | 209 | log_warn_calls = [call('(UDP) Invalid packet - not enough data')] | 84 | log_warn_calls = [call('(UDP) get_datagram() called when pending data size is 0')] |
1340 | 210 | log_debug_calls = [call('(UDP) PJLinkUDP() Initialized'), | 85 | log_debug_calls = [call('(UDP) get_datagram() - Receiving data')] |
1338 | 211 | call('(UDP) get_datagram() - Receiving data')] | ||
1341 | 212 | with patch.object(pjlink_udp, 'pendingDatagramSize') as mock_datagram, \ | 86 | with patch.object(pjlink_udp, 'pendingDatagramSize') as mock_datagram, \ |
1342 | 213 | patch.object(pjlink_udp, 'readDatagram') as mock_read: | 87 | patch.object(pjlink_udp, 'readDatagram') as mock_read: |
1344 | 214 | mock_datagram.return_value = 1 | 88 | mock_datagram.return_value = 0 |
1345 | 215 | mock_read.return_value = ('', TEST1_DATA['ip'], PJLINK_PORT) | 89 | mock_read.return_value = ('', TEST1_DATA['ip'], PJLINK_PORT) |
1346 | 216 | 90 | ||
1347 | 217 | # WHEN: get_datagram called with 0 bytes ready | 91 | # WHEN: get_datagram called with 0 bytes ready |
1348 | 218 | pjlink_udp.get_datagram() | 92 | pjlink_udp.get_datagram() |
1349 | 219 | 93 | ||
1350 | 220 | # THEN: Log entries should be made and method returns | 94 | # THEN: Log entries should be made and method returns |
1374 | 221 | mock_log.warn.assert_has_calls(log_warn_calls) | 95 | mock_log.warning.assert_has_calls(log_warn_calls) |
1352 | 222 | mock_log.debug.assert_has_calls(log_debug_calls) | ||
1353 | 223 | |||
1354 | 224 | @patch.object(openlp.core.projectors.pjlink, 'log') | ||
1355 | 225 | def test_get_datagram_data_short(self, mock_log): | ||
1356 | 226 | """ | ||
1357 | 227 | Test get_datagram when data length < 8 | ||
1358 | 228 | """ | ||
1359 | 229 | # GIVEN: Test setup | ||
1360 | 230 | pjlink_udp = PJLinkUDP(projector_list=self.test_list) | ||
1361 | 231 | log_warn_calls = [call('(UDP) Invalid packet - not enough data')] | ||
1362 | 232 | log_debug_calls = [call('(UDP) PJLinkUDP() Initialized'), | ||
1363 | 233 | call('(UDP) get_datagram() - Receiving data')] | ||
1364 | 234 | with patch.object(pjlink_udp, 'pendingDatagramSize') as mock_datagram, \ | ||
1365 | 235 | patch.object(pjlink_udp, 'readDatagram') as mock_read: | ||
1366 | 236 | mock_datagram.return_value = 6 | ||
1367 | 237 | mock_read.return_value = ('{prefix}2ACKN'.format(prefix=PJLINK_PREFIX), TEST1_DATA['ip'], PJLINK_PORT) | ||
1368 | 238 | |||
1369 | 239 | # WHEN: get_datagram called with 0 bytes ready | ||
1370 | 240 | pjlink_udp.get_datagram() | ||
1371 | 241 | |||
1372 | 242 | # THEN: Log entries should be made and method returns | ||
1373 | 243 | mock_log.warn.assert_has_calls(log_warn_calls) | ||
1375 | 244 | mock_log.debug.assert_has_calls(log_debug_calls) | 96 | mock_log.debug.assert_has_calls(log_debug_calls) |
1376 | 245 | 97 | ||
1377 | 246 | @patch.object(openlp.core.projectors.pjlink, 'log') | 98 | @patch.object(openlp.core.projectors.pjlink, 'log') |
1378 | @@ -260,101 +112,5 @@ | |||
1379 | 260 | pjlink_udp.get_datagram() | 112 | pjlink_udp.get_datagram() |
1380 | 261 | 113 | ||
1381 | 262 | # THEN: Log entries should be made and method returns | 114 | # THEN: Log entries should be made and method returns |
1383 | 263 | mock_log.warn.assert_has_calls(log_warn_calls) | 115 | mock_log.warning.assert_has_calls(log_warn_calls) |
1384 | 264 | mock_log.debug.assert_has_calls(log_debug_calls) | 116 | mock_log.debug.assert_has_calls(log_debug_calls) |
1385 | 265 | |||
1386 | 266 | @patch.object(openlp.core.projectors.pjlink, 'log') | ||
1387 | 267 | def test_process_ackn_duplicate(self, mock_log): | ||
1388 | 268 | """ | ||
1389 | 269 | Test process_ackn method with multiple calls with same data | ||
1390 | 270 | """ | ||
1391 | 271 | # GIVEN: Test setup | ||
1392 | 272 | pjlink_udp = PJLinkUDP(projector_list=self.test_list) | ||
1393 | 273 | check_list = {TEST1_DATA['ip']: {'data': TEST1_DATA['mac_adx'], 'port': PJLINK_PORT}} | ||
1394 | 274 | log_warn_calls = [call('(UDP) Host {host} already replied - ignoring'.format(host=TEST1_DATA['ip']))] | ||
1395 | 275 | log_debug_calls = [call('(UDP) PJLinkUDP() Initialized'), | ||
1396 | 276 | call('(UDP) Processing ACKN packet'), | ||
1397 | 277 | call('(UDP) Adding {host} to ACKN list'.format(host=TEST1_DATA['ip'])), | ||
1398 | 278 | call('(UDP) Processing ACKN packet')] | ||
1399 | 279 | |||
1400 | 280 | # WHEN: process_ackn called twice with same data | ||
1401 | 281 | pjlink_udp.process_ackn(data=TEST1_DATA['mac_adx'], host=TEST1_DATA['ip'], port=PJLINK_PORT) | ||
1402 | 282 | pjlink_udp.process_ackn(data=TEST1_DATA['mac_adx'], host=TEST1_DATA['ip'], port=PJLINK_PORT) | ||
1403 | 283 | |||
1404 | 284 | # THEN: pjlink_udp.ack_list should equal test_list | ||
1405 | 285 | # NOTE: This assert only returns AssertionError - does not list differences. Maybe add a compare function? | ||
1406 | 286 | if pjlink_udp.ackn_list != check_list: | ||
1407 | 287 | # Check this way so we can print differences to stdout | ||
1408 | 288 | print('\nackn_list: ', pjlink_udp.ackn_list) | ||
1409 | 289 | print('test_list: ', check_list) | ||
1410 | 290 | assert pjlink_udp.ackn_list == check_list | ||
1411 | 291 | mock_log.debug.assert_has_calls(log_debug_calls) | ||
1412 | 292 | mock_log.warn.assert_has_calls(log_warn_calls) | ||
1413 | 293 | |||
1414 | 294 | @patch.object(openlp.core.projectors.pjlink, 'log') | ||
1415 | 295 | def test_process_ackn_multiple(self, mock_log): | ||
1416 | 296 | """ | ||
1417 | 297 | Test process_ackn method with multiple calls | ||
1418 | 298 | """ | ||
1419 | 299 | # GIVEN: Test setup | ||
1420 | 300 | pjlink_udp = PJLinkUDP(projector_list=self.test_list) | ||
1421 | 301 | check_list = {TEST1_DATA['ip']: {'data': TEST1_DATA['mac_adx'], 'port': PJLINK_PORT}, | ||
1422 | 302 | TEST2_DATA['ip']: {'data': TEST2_DATA['mac_adx'], 'port': PJLINK_PORT}} | ||
1423 | 303 | log_debug_calls = [call('(UDP) PJLinkUDP() Initialized'), | ||
1424 | 304 | call('(UDP) Processing ACKN packet'), | ||
1425 | 305 | call('(UDP) Adding {host} to ACKN list'.format(host=TEST1_DATA['ip'])), | ||
1426 | 306 | call('(UDP) Processing ACKN packet'), | ||
1427 | 307 | call('(UDP) Adding {host} to ACKN list'.format(host=TEST2_DATA['ip']))] | ||
1428 | 308 | |||
1429 | 309 | # WHEN: process_ackn called twice with different data | ||
1430 | 310 | pjlink_udp.process_ackn(data=TEST1_DATA['mac_adx'], host=TEST1_DATA['ip'], port=PJLINK_PORT) | ||
1431 | 311 | pjlink_udp.process_ackn(data=TEST2_DATA['mac_adx'], host=TEST2_DATA['ip'], port=PJLINK_PORT) | ||
1432 | 312 | |||
1433 | 313 | # THEN: pjlink_udp.ack_list should equal test_list | ||
1434 | 314 | # NOTE: This assert only returns AssertionError - does not list differences. Maybe add a compare function? | ||
1435 | 315 | if pjlink_udp.ackn_list != check_list: | ||
1436 | 316 | # Check this way so we can print differences to stdout | ||
1437 | 317 | print('\nackn_list: ', pjlink_udp.ackn_list) | ||
1438 | 318 | print('test_list: ', check_list) | ||
1439 | 319 | assert pjlink_udp.ackn_list == check_list | ||
1440 | 320 | mock_log.debug.assert_has_calls(log_debug_calls) | ||
1441 | 321 | |||
1442 | 322 | @patch.object(openlp.core.projectors.pjlink, 'log') | ||
1443 | 323 | def test_process_ackn_single(self, mock_log): | ||
1444 | 324 | """ | ||
1445 | 325 | Test process_ackn method with single call | ||
1446 | 326 | """ | ||
1447 | 327 | # GIVEN: Test setup | ||
1448 | 328 | pjlink_udp = PJLinkUDP(projector_list=self.test_list) | ||
1449 | 329 | check_list = {TEST1_DATA['ip']: {'data': TEST1_DATA['mac_adx'], 'port': PJLINK_PORT}} | ||
1450 | 330 | log_debug_calls = [call('(UDP) PJLinkUDP() Initialized'), | ||
1451 | 331 | call('(UDP) Processing ACKN packet'), | ||
1452 | 332 | call('(UDP) Adding {host} to ACKN list'.format(host=TEST1_DATA['ip']))] | ||
1453 | 333 | |||
1454 | 334 | # WHEN: process_ackn called twice with different data | ||
1455 | 335 | pjlink_udp.process_ackn(data=TEST1_DATA['mac_adx'], host=TEST1_DATA['ip'], port=PJLINK_PORT) | ||
1456 | 336 | |||
1457 | 337 | # THEN: pjlink_udp.ack_list should equal test_list | ||
1458 | 338 | # NOTE: This assert only returns AssertionError - does not list differences. Maybe add a compare function? | ||
1459 | 339 | if pjlink_udp.ackn_list != check_list: | ||
1460 | 340 | # Check this way so we can print differences to stdout | ||
1461 | 341 | print('\nackn_list: ', pjlink_udp.ackn_list) | ||
1462 | 342 | print('test_list: ', check_list) | ||
1463 | 343 | assert pjlink_udp.ackn_list == check_list | ||
1464 | 344 | mock_log.debug.assert_has_calls(log_debug_calls) | ||
1465 | 345 | |||
1466 | 346 | @patch.object(openlp.core.projectors.pjlink, 'log') | ||
1467 | 347 | def test_process_srch(self, mock_log): | ||
1468 | 348 | """ | ||
1469 | 349 | Test process_srch method | ||
1470 | 350 | """ | ||
1471 | 351 | # GIVEN: Test setup | ||
1472 | 352 | pjlink_udp = PJLinkUDP(projector_list=self.test_list) | ||
1473 | 353 | log_debug_calls = [call('(UDP) PJLinkUDP() Initialized'), | ||
1474 | 354 | call('(UDP) SRCH packet received - ignoring')] | ||
1475 | 355 | |||
1476 | 356 | # WHEN: process_srch called | ||
1477 | 357 | pjlink_udp.process_srch(data=None, host=None, port=None) | ||
1478 | 358 | |||
1479 | 359 | # THEN: debug log entry should be entered | ||
1480 | 360 | mock_log.debug.assert_has_calls(log_debug_calls) | ||
1481 | 361 | 117 | ||
1482 | === renamed file 'tests/openlp_core/projectors/test_projectorsourceform.py' => 'tests/openlp_core/projectors/test_projector_sourceform.py' |
Sorry will clash with one of my fixes which has been merged
See question