View Javadoc

1   /*
2    *  
3    *  Fosstrak LLRP Commander (www.fosstrak.org)
4    * 
5    *  Copyright (C) 2008 ETH Zurich
6    *
7    *  This program is free software: you can redistribute it and/or modify
8    *  it under the terms of the GNU General Public License as published by
9    *  the Free Software Foundation, either version 3 of the License, or
10   *  (at your option) any later version.
11   *
12   *  This program is distributed in the hope that it will be useful,
13   *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14   *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15   *  GNU General Public License for more details.
16   *
17   *  You should have received a copy of the GNU General Public License
18   *  along with this program.  If not, see <http://www.gnu.org/licenses/> 
19   *
20   */
21  
22  package org.fosstrak.llrp.commander.views;
23  
24  import java.util.ArrayList;
25  
26  import org.apache.log4j.Logger;
27  import org.eclipse.jface.action.Action;
28  import org.eclipse.jface.action.IMenuManager;
29  import org.eclipse.jface.action.IToolBarManager;
30  import org.eclipse.jface.action.Separator;
31  import org.eclipse.jface.resource.ImageDescriptor;
32  import org.eclipse.jface.viewers.ColumnLayoutData;
33  import org.eclipse.jface.viewers.ColumnWeightData;
34  import org.eclipse.jface.viewers.DoubleClickEvent;
35  import org.eclipse.jface.viewers.IDoubleClickListener;
36  import org.eclipse.jface.viewers.ISelection;
37  import org.eclipse.jface.viewers.IStructuredSelection;
38  import org.eclipse.jface.viewers.TableViewer;
39  import org.eclipse.jface.viewers.Viewer;
40  import org.eclipse.jface.viewers.ViewerComparator;
41  import org.eclipse.jface.viewers.ViewerFilter;
42  import org.eclipse.jface.window.Window;
43  import org.eclipse.swt.widgets.Composite;
44  import org.eclipse.swt.widgets.Display;
45  import org.eclipse.ui.ISelectionListener;
46  import org.eclipse.ui.ISharedImages;
47  import org.eclipse.ui.IWorkbenchPart;
48  import org.eclipse.ui.PlatformUI;
49  import org.fosstrak.llrp.client.Constants;
50  import org.fosstrak.llrp.client.LLRPMessageItem;
51  import org.fosstrak.llrp.client.Repository;
52  import org.fosstrak.llrp.commander.ResourceCenter;
53  import org.fosstrak.llrp.commander.dialogs.MessageboxViewOptionsDialog;
54  import org.fosstrak.llrp.commander.util.MessageBoxRefresh;
55  
56  /**
57   * This sample class demonstrates how to plug-in a new workbench view. The view
58   * shows data obtained from the model. The sample creates a dummy model on the
59   * fly, but a real implementation would connect to the model available either in
60   * this or another plug-in (e.g. the workspace). The view is connected to the
61   * model using a content provider.
62   * <p>
63   * The view uses a label provider to define how model objects should be
64   * presented in the view. Each view can present the same model objects using
65   * different labels and icons, if needed. Alternatively, a single label provider
66   * can be shared between views in order to ensure that objects of the same type
67   * are presented in the same way everywhere.
68   * <p>
69   * 
70   * 
71   * @author zhanghao
72   * @author sawielan
73   */
74  
75  public class MessageboxView extends TableViewPart implements ISelectionListener {
76  	
77  	/**
78  	 * Log4j instance.
79  	 */
80  	private static Logger log = Logger.getLogger(MessageboxView.class);
81  	
82  	/**
83  	 * Column ID for User Marks.
84  	 */
85  	public static final int COL_MSG_MARK = 0;
86  	
87  	/**
88  	 * Column ID for Message ID.
89  	 */
90  	public static final int COL_MSG_ID = 1;
91  	
92  	/** column for the adapter. */
93  	public static final int COL_MSG_ADAPTER = 2;
94  	
95  	/**
96  	 * Column ID for Message Type.
97  	 */
98  	public static final int COL_MSG_READER = 3;
99  	
100 	/**
101 	 * Column ID for Message Type.
102 	 */
103 	public static final int COL_MSG_TYPE = 4;
104 	
105 	/**
106 	 * Column ID for Status Code.
107 	 */
108 	public static final int COL_STATUS_CODE = 5;
109 
110 	/**
111 	 * Column ID for Message Issue Time.
112 	 */
113 	public static final int COL_MSG_TIME = 6;
114 
115 	/**
116 	 * Column ID for Message Comments.
117 	 */
118 	public static final int COL_MSG_COMMENT = 7;
119 	
120 	/** the number of messages to display in the message box. */
121 	private int displayNumMessages = ResourceCenter.GET_MAX_MESSAGES;
122 
123 	private String columnHeaders[] = { "", "ID", "Adapter", "Reader", "Message Type", "Status Code",
124 			"Time", "Comment" };
125 	private ColumnLayoutData columnLayouts[] = { new ColumnWeightData(12),
126 			new ColumnWeightData(80), new ColumnWeightData(60), 
127 			new ColumnWeightData(60), new ColumnWeightData(100), 
128 			new ColumnWeightData(80), new ColumnWeightData(80), 
129 			new ColumnWeightData(100) };
130 
131 	private Action deleteAction;
132 	
133 	private Action rOAccessReportFilterAction;
134 
135 	/** action to enable/disable auto refresh */
136 	private Action autoRefreshAction;
137 	
138 	/** action to set the refresh options. */
139 	private Action optionsAction;
140 	
141 	private MessageFilter filter;
142 	
143 	private ROAccessReportFilter rOAccessReportFilter;
144 	
145 	private String selectedAdapter, selectedReader;
146 
147 	/** the name of the auto refresh icon. */
148 	public static final String ICON_AUTO_REFRESH = "autorefresh.gif";
149 	
150 	/** 
151 	 * access to the display is needed by the asyncExec/syncExec API 
152 	 * to allow multi-threaded access to the SWT widget
153 	 */
154 	protected Display display = null;
155 	
156 	/** access to this pointer for inner classes. */
157 	private MessageboxView mbv = this;
158 	
159 	/**
160 	 * filter according the selected adapter. 
161 	 * @author sawielan
162 	 *
163 	 */
164 	private class MessageFilter extends ViewerFilter {
165 		
166 		private String adapterName, readerName;
167 		
168 		public void setCondition(String aAdapterName, String aReaderName) {
169 			adapterName = aAdapterName;
170 			readerName = aReaderName;
171 		}
172 		
173 		public boolean select(Viewer aViewer, Object aParentElement, Object aElement) {
174 			// special filter for the root adapter...
175 			if ((null == adapterName) || (
176 					Constants.ROOT_NAME.
177 						equals(adapterName))) {
178 				
179 				return true;
180 			}
181 			
182 			if ((null == adapterName) && (null == readerName)) {
183 				return true;
184 			}
185 			
186 			if (aElement instanceof LLRPMessageItem) {
187 				LLRPMessageItem item = (LLRPMessageItem) aElement;
188 				
189 				if ((adapterName == null) || (item.getAdapter() == null)) {
190 					return false;
191 				} else {
192 					if (adapterName.trim().equals(item.getAdapter().trim())) {
193 						
194 						// compare the reader. if the readerName is null then 
195 						// the user selected a whole adapter and this means 
196 						// that we don't care about the readerName.
197 						if (readerName == null) {
198 							return true;
199 						}
200 						
201 						if ((item.getReader() != null) && 
202 								(item.getReader().trim().equals(
203 										readerName.trim()))) {
204 							return true;
205 						}
206 					}
207 				}
208 			}
209 			return false;
210 		}
211 		
212 	}
213 	
214 	/**
215 	 * This filter can be used to hide RO_ACCESS_REPORT messages in the message box view.
216 	 * This might be useful for the user, because RO_ACCESS_REPORT messages usually
217 	 * occur in high numbers and render the message box view overcrowded.
218 	 *
219 	 * @author Ulrich Etter, ETHZ
220 	 *
221 	 */
222 	private class ROAccessReportFilter extends ViewerFilter {
223 		
224 		public boolean select(Viewer aViewer, Object aParentElement, Object aElement) {
225 			if (aElement instanceof LLRPMessageItem) {
226 				LLRPMessageItem item = (LLRPMessageItem) aElement;
227 				String msg = item.getMessageType();
228 				if (null == msg) {
229 					return true;
230 				}
231 				if ("RO_ACCESS_REPORT".equals(msg.trim())) {
232 					return false;
233 				}
234 			}
235 			return true;
236 		}
237 		
238 	}
239 	
240 	/**
241 	 * comparator to compare two llrp message item elements. if 
242 	 * the timestamp of the second element is smaller than the timestamp 
243 	 * of the first, then a negative number is returned. otherwise 
244 	 * a positive value.
245 	 * @author sawielan
246 	 *
247 	 */
248 	private class NewMessageComparator extends ViewerComparator {
249 		
250 		/**
251 		 * compares two elements and returns a negative number if element2 is 
252 		 * more recent than element1.
253 		 * @param viewer the viewer.
254 		 * @param element1 the first element to compare.
255 		 * @param element2 the second element to compare.
256 		 */
257 		public int compare(Viewer viewer, Object element1, Object element2) {
258 			if ((element1 instanceof LLRPMessageItem) && 
259 					(element2 instanceof LLRPMessageItem)) {
260 				
261 				LLRPMessageItem msg1 = (LLRPMessageItem) element1;
262 				LLRPMessageItem msg2 = (LLRPMessageItem) element2;
263 
264 				// compare the two timestamps...
265 				return (int)(msg2.getTime().getTime() - msg1.getTime().getTime());				
266 			}
267 			return 0;
268 		}
269 	}
270 	
271 	/**
272 	 * The constructor.
273 	 */
274 	public MessageboxView() {
275 		super();
276 		setColumnHeaders(columnHeaders);
277 		setColumnLayouts(columnLayouts);
278 	}
279 	
280 	/**
281 	 * This is a callback that will allow us to create the viewer and initialize
282 	 * it.
283 	 */
284 	public void createPartControl(Composite parent) {
285 	
286 		super.createPartControl(parent);
287 		
288 		display = parent.getDisplay();
289 		
290 		filter = new MessageFilter();
291 		rOAccessReportFilter = new ROAccessReportFilter();
292 		
293 		TableViewer viewer = getViewer();
294 		viewer.setLabelProvider(new MessageboxViewLabelProvider());
295 		viewer.addFilter(filter);
296 		viewer.setSorter(null);
297 		viewer.setComparator(new NewMessageComparator());
298 
299 		viewer.refresh();
300 		
301 		getViewSite().getPage().addSelectionListener(this);
302 		
303 		ResourceCenter.getInstance().setMessageboxView(this);
304 	}
305 	
306 	/**
307 	 * return a handle to the display of this widget.
308 	 * @return the display of this widget.
309 	 */
310 	public Display getDisplay() {
311 		return display;
312 	}
313 
314 	protected void fillContextMenu(IMenuManager manager) {
315 		super.fillContextMenu(manager);
316 		manager.add(rOAccessReportFilterAction);
317 		manager.add(deleteAction);
318 		manager.add(autoRefreshAction);
319 		manager.add(optionsAction);
320 		manager.add(new Separator("Additions"));
321 	}
322 
323 	protected void fillLocalToolBar(IToolBarManager manager) {
324 		super.fillLocalToolBar(manager);
325 		manager.add(rOAccessReportFilterAction);
326 		manager.add(autoRefreshAction);
327 		manager.add(deleteAction);
328 	}
329 
330 	protected void createActions() {
331 		super.createActions();
332 		deleteAction = new Action() {
333 			public void run() {
334 				String adapter = ResourceCenter.getInstance().
335 					getReaderExplorerView().getSelectedAdapter();
336 				String reader = ResourceCenter.getInstance().
337 					getReaderExplorerView().getSelectedReader();
338 				
339 				if (Constants.ROOT_NAME.
340 						equals(adapter) || (null == adapter)) {
341 					
342 					ResourceCenter.getInstance().getRepository().clearAll();
343 				} else if (null == reader) {
344 					ResourceCenter.getInstance().getRepository().clearAdapter(adapter);
345 				} else {
346 					ResourceCenter.getInstance().getRepository().clearReader(
347 							adapter, reader);
348 				}
349 				ResourceCenter.getInstance().clearMessageMetadataList();
350 				
351 				getTable().setRedraw(false);
352 				// model.clear();
353 				getViewer().refresh(true);
354 				getTable().setRedraw(true);
355 			}
356 		};
357 		String deleteText = String.format(
358 				"Remove LLRP messages from the repository:\n" +
359 				" - Select reader in reader explorer to delete messages of the reader.\n" + 
360 				" - Select adapter in reader explorer to delete messages of the adapter.\n" + 
361 				" - Select root in reader explorer to delete all messages."
362 				);
363 		deleteAction.setText("Remove Messages");
364 		deleteAction.setToolTipText(deleteText);
365 		deleteAction.setImageDescriptor(PlatformUI.getWorkbench()
366 				.getSharedImages().getImageDescriptor(
367 						ISharedImages.IMG_TOOL_DELETE));
368 		
369 		rOAccessReportFilterAction = new Action() {
370 			public void run() {
371 				if (rOAccessReportFilterAction.isChecked()){
372 					getViewer().addFilter(rOAccessReportFilter);
373 					updateViewer(true);
374 				}
375 				else{
376 					getViewer().removeFilter(rOAccessReportFilter);
377 					updateViewer(true);
378 				}
379 			}
380 		};
381 		String filterText = "Hide RO_ACCESS_REPORT messages";
382 		rOAccessReportFilterAction.setText(filterText);
383 		rOAccessReportFilterAction.setToolTipText(filterText);
384 		ImageDescriptor imageDescriptor = ResourceCenter.getInstance().getImageDescriptor("filter.gif");
385 		rOAccessReportFilterAction.setImageDescriptor(imageDescriptor);
386 		rOAccessReportFilterAction.setChecked(false);
387 
388 		getViewer().addDoubleClickListener(new IDoubleClickListener() {
389 			public void doubleClick(DoubleClickEvent event) {
390 				
391 				// Select the Message Item
392 				IStructuredSelection sel = (IStructuredSelection) event.getSelection();
393 				LLRPMessageItem msg = (LLRPMessageItem) sel.getFirstElement();
394 				
395 				// Write to file system within Eclipse Workspace
396 				if (msg != null) {
397 					ResourceCenter.getInstance().writeMessageToFile(msg.getId());
398 				}
399 			}
400 		});
401 		
402 		autoRefreshAction = new Action() {
403 			public void run() {
404 				boolean refresh = false;
405 				if (autoRefreshAction.isChecked()) {
406 					refresh = true;
407 				}
408 				ResourceCenter.getInstance().getMessageBoxRefresh().setRefresh(refresh);
409 			}
410 		};
411 		final String autoRefreshText = String.format("Auto-Refresh (%d ms)", 
412 				MessageBoxRefresh.DEFAULT_REFRESH_INTERVAL_MS);
413 		autoRefreshAction.setText(autoRefreshText);
414 		autoRefreshAction.setToolTipText(autoRefreshText);
415 		autoRefreshAction.setChecked(MessageBoxRefresh.DEFAULT_REFRESH_BEHAVIOR);
416 		autoRefreshAction.setImageDescriptor(
417 				ResourceCenter.getInstance().getImageDescriptor(ICON_AUTO_REFRESH));
418 		
419 		optionsAction = new Action() {
420 			public void run() {
421 				MessageboxViewOptionsDialog dlg = new MessageboxViewOptionsDialog(display.getActiveShell(), mbv);
422 				if (Window.OK == dlg.open()) {
423 					try {
424 						final long rt = dlg.getRefreshTime();
425 						
426 						ResourceCenter.getInstance().getMessageBoxRefresh().setRefreshTime(rt);
427 						final String autoRefreshText = String.format("Auto-Refresh (%d ms)", 
428 								rt);
429 						autoRefreshAction.setText(autoRefreshText);
430 						autoRefreshAction.setToolTipText(autoRefreshText);
431 						
432 						final int nMsg = dlg.getNumberOfMessages();
433 						displayNumMessages = nMsg;
434 						if (nMsg <= 0) {
435 							displayNumMessages = Repository.RETRIEVE_ALL;
436 						}
437 					} catch (Exception e) {
438 						log.error("could not change the refresh time.");
439 					}
440 				}
441 			}
442 		};
443 		final String optionsText = "Options";
444 		optionsAction.setText(optionsText);
445 		optionsAction.setToolTipText(optionsText);
446 	}
447 	
448 	/**
449 	 * Dispose the view.
450 	 */
451 	public void dispose() {
452 		// model.removeListener(modelListener);
453 		super.dispose();
454 	}
455 
456 	/**
457 	 * Triggered when user change the reader in Reader Management View.
458 	 * The corresponding messages need to be updated.
459 	 */
460 	public void selectionChanged(IWorkbenchPart part, ISelection selection) {
461 		if (selection instanceof IStructuredSelection) {
462 			Object selectedObj = ((IStructuredSelection) selection).getFirstElement();
463 			if (selectedObj instanceof ReaderTreeObject) {
464 				
465 				ReaderTreeObject readerNode = (ReaderTreeObject) selectedObj;
466 	
467 				if (readerNode.isReader()) {
468 					String aAdapterName = readerNode.getParent().getName();
469 					
470 					selectedAdapter = aAdapterName;
471 					selectedReader = readerNode.getName();
472 					
473 					updateViewer(true);
474 				} else {
475 					selectedAdapter = readerNode.getName();
476 					selectedReader = null;
477 					
478 					updateViewer(true);
479 				}
480 			}
481 		}
482 	}
483 	
484 	/**
485 	 * Refresh the message list in the viewer from selected adapter or reader.
486 	 * @param reload flag whether to reload the whole list of messages. if set 
487 	 * to true all the messages get loaded from the backend, otherwise just the 
488 	 * new messages get added to the view.
489 	 */
490 	public synchronized void updateViewer(boolean reload) {
491 
492 		// we want to know how long this takes...
493 		long st = System.currentTimeMillis();
494 		
495 		synchronized (this) {
496 			filter.setCondition(selectedAdapter, selectedReader);
497 			if (reload) {
498 	
499 				// first load all the messages from the database backend.
500 				Repository repo = ResourceCenter.getInstance().getRepository();
501 				ArrayList<LLRPMessageItem> msgs = repo.get(
502 						selectedAdapter, 
503 						selectedReader, 
504 						displayNumMessages,
505 						false);
506 				
507 				getViewer().getTable().removeAll();
508 				
509 				getViewer().add(msgs.toArray());
510 				ResourceCenter.getInstance().clearMessageMetadataList();
511 				
512 			} else {
513 				
514 				ArrayList<LLRPMessageItem> list = 
515 					ResourceCenter.getInstance().getMessageMetadataList();
516 				
517 				if (list.size() > 0) {
518 					getViewer().add(list.toArray());
519 					ResourceCenter.getInstance().clearMessageMetadataList();
520 					
521 					//bound the number of displayed messages.
522 					if (0 < displayNumMessages) {
523 						getViewer().setItemCount(Math.min(
524 							getViewer().getTable().getItemCount(), 
525 							getDisplayNumMessages()));
526 					} 
527 				}
528 			}
529 			
530 			// inform all the other threads about the free lock.
531 			notifyAll();
532 		}
533 		
534 		long et = System.currentTimeMillis();
535 		log.debug(String.format("Messagebox redraw time: %d", 
536 				et - st));
537 	}
538 	
539 	/**
540 	 * @param displayNumMessages the displayNumMessages to set
541 	 */
542 	public void setDisplayNumMessages(int displayNumMessages) {
543 		this.displayNumMessages = displayNumMessages;
544 	}
545 
546 	/**
547 	 * @return the displayNumMessages
548 	 */
549 	public int getDisplayNumMessages() {
550 		return displayNumMessages;
551 	}
552 	
553 	/**
554 	 * @return the selectedAdapter
555 	 */
556 	public final String getSelectedAdapter() {
557 		return selectedAdapter;
558 	}
559 
560 	/**
561 	 * @return the selectedReader
562 	 */
563 	public final String getSelectedReader() {
564 		return selectedReader;
565 	}
566 }