/**
* This file is protected by Copyright.
* Please refer to the COPYRIGHT file distributed with this source distribution.
*
* This file is part of REDHAWK IDE.
*
* All rights reserved. This program and the accompanying materials are made available under
* the terms of the Eclipse Public License v1.0 which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html.
*
*/
package gov.redhawk.frontend.ui.internal;
//TODO: reflection for access to the SriDataView code
import gov.redhawk.bulkio.ui.views.SriDataView;
import gov.redhawk.bulkio.util.BulkIOType;
import gov.redhawk.frontend.FrontendPackage;
import gov.redhawk.frontend.ListenerAllocation;
import gov.redhawk.frontend.TunerStatus;
import gov.redhawk.frontend.ui.FrontEndUIActivator;
import gov.redhawk.frontend.util.TunerProperties.ListenerAllocationProperties;
import gov.redhawk.frontend.util.TunerUtils;
import gov.redhawk.model.sca.RefreshDepth;
import gov.redhawk.model.sca.ScaDevice;
import gov.redhawk.model.sca.ScaDomainManagerRegistry;
import gov.redhawk.model.sca.ScaFactory;
import gov.redhawk.model.sca.ScaPort;
import gov.redhawk.model.sca.ScaSimpleProperty;
import gov.redhawk.model.sca.ScaStructProperty;
import gov.redhawk.model.sca.ScaUsesPort;
import gov.redhawk.model.sca.commands.ScaModelCommand;
import gov.redhawk.model.sca.provider.ScaItemProviderAdapterFactory;
import gov.redhawk.ui.port.nxmplot.IPlotView;
import gov.redhawk.ui.port.nxmplot.PlotSource;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.Callable;
import mil.jpeojtrs.sca.prf.PrfFactory;
import mil.jpeojtrs.sca.prf.PrfPackage;
import mil.jpeojtrs.sca.prf.Simple;
import mil.jpeojtrs.sca.util.CorbaUtils;
import mil.jpeojtrs.sca.util.ScaEcoreUtils;
import org.eclipse.core.commands.AbstractHandler;
import org.eclipse.core.commands.ExecutionEvent;
import org.eclipse.core.commands.ExecutionException;
import org.eclipse.core.commands.IHandler;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.SubMonitor;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.emf.common.notify.Adapter;
import org.eclipse.emf.common.notify.AdapterFactory;
import org.eclipse.emf.common.notify.Notifier;
import org.eclipse.emf.common.notify.impl.AdapterImpl;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.edit.provider.IItemLabelProvider;
import org.eclipse.emf.edit.ui.provider.AdapterFactoryLabelProvider;
import org.eclipse.jface.viewers.ArrayContentProvider;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.window.Window;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.IViewPart;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.dialogs.ListSelectionDialog;
import org.eclipse.ui.handlers.HandlerUtil;
import org.eclipse.ui.progress.UIJob;
import org.eclipse.ui.statushandlers.StatusManager;
import CF.DataType;
import CF.PropertiesHelper;
import CF.DevicePackage.InsufficientCapacity;
import CF.DevicePackage.InvalidCapacity;
import CF.DevicePackage.InvalidState;
public class FeiSriHandler extends AbstractHandler implements IHandler {
private SriDataView sriViewLocalRef; // Needed for declaration of dispose logic
private IWorkbenchWindow window;
@Override
public Object execute(final ExecutionEvent event) throws ExecutionException {
window = HandlerUtil.getActiveWorkbenchWindow(event);
// Launches from the plot view menu
IWorkbenchPart activePart = HandlerUtil.getActivePart(event);
if (activePart instanceof IPlotView) {
IPlotView plotView = (IPlotView) HandlerUtil.getActivePart(event);
List<PlotSource> sources = plotView.getPlotPageBook().getSources();
for (PlotSource source : sources) {
final ScaUsesPort usesPort = source.getInput();
final String connectionId = source.getBulkIOBlockSettings().getConnectionID();
IStatus retVal = displaySriView(event, usesPort, connectionId);
return retVal;
}
}
// Launches from a context menu selection
IStructuredSelection selection = (IStructuredSelection) HandlerUtil.getActiveMenuSelection(event);
if (selection == null) {
selection = (IStructuredSelection) HandlerUtil.getCurrentSelection(event);
if (selection == null) {
return null;
}
}
final List< ? > elements = selection.toList();
if (elements.isEmpty()) {
return null;
}
for (Object obj : elements) {
if (obj instanceof TunerStatus) {
// Get the tuner
final TunerStatus tuner = (TunerStatus) obj;
// Get the containing device for the tuner
final ScaDevice< ? > device = ScaEcoreUtils.getEContainerOfType(tuner, ScaDevice.class);
// Create the allocation property structure
final DataType[] props = createAllocationProperties(tuner);
// Core Job that handles capacity allocation and displaying the view
Job job = new Job("Displaying SRI Data for " + tuner.getAllocationID()) {
@Override
protected IStatus run(IProgressMonitor parentMonitor) {
final SubMonitor subMonitor = SubMonitor.convert(parentMonitor, "Displaying SRI Data for " + tuner.getAllocationID(),
IProgressMonitor.UNKNOWN);
try {
// First check to see if a SRI View is already listening on this tuner
for (ListenerAllocation listener : tuner.getListenerAllocations()) {
if (listener.getListenerID().startsWith("SRI")) {
return Status.OK_STATUS;
}
}
// Allocate capacity on the device for the listener if no SRI View is found
IStatus status = CorbaUtils.invoke(new Callable<IStatus>() {
@Override
public IStatus call() throws Exception {
try {
subMonitor.subTask("Allocating capacity...");
if (device.allocateCapacity(props)) {
return Status.OK_STATUS;
} else {
return new Status(IStatus.ERROR, FrontEndUIActivator.PLUGIN_ID, "Allocation failed, SRI data could not display.",
null);
}
} catch (InvalidCapacity e) {
return new Status(IStatus.ERROR, FrontEndUIActivator.PLUGIN_ID, "Invalid Capacity in SRI View allocation: " + e.msg, e);
} catch (InvalidState e) {
return new Status(IStatus.ERROR, FrontEndUIActivator.PLUGIN_ID, "Invalid State in SRI View allocation: " + e.msg, e);
} catch (InsufficientCapacity e) {
return new Status(IStatus.ERROR, FrontEndUIActivator.PLUGIN_ID, "Insufficient Capacity in SRI View allocation: "
+ e.msg, e);
}
}
}, subMonitor.newChild(1));
if (!status.isOK()) {
return status;
}
subMonitor.subTask("Refreshing device...");
device.refresh(subMonitor.newChild(1), RefreshDepth.SELF);
} catch (InterruptedException e) {
return Status.CANCEL_STATUS;
} catch (CoreException e) {
return new Status(e.getStatus().getSeverity(), FrontEndUIActivator.PLUGIN_ID, "Failed to allocate for SRI", e);
} finally {
subMonitor.done();
}
// Display the SRI View
UIJob uiJob = new UIJob("Launching SRI View...") {
@Override
public IStatus runInUIThread(IProgressMonitor monitor) {
try {
IStatus retVal = createSriView(event, props, elements, device, tuner);
// If the view was not created successfully then deallocate
if (!retVal.isOK()) {
deallocate(tuner, props, device);
}
return retVal;
} catch (ExecutionException e) {
deallocate(tuner, props, device);
return new Status(IStatus.ERROR, FrontEndUIActivator.PLUGIN_ID, "Failed to open SRI Data View", e);
}
}
};
uiJob.setUser(false);
uiJob.setSystem(true);
uiJob.schedule();
return Status.OK_STATUS;
}
};
job.setUser(true);
job.schedule();
}
}
return null;
}
/**
* Creates the SRI View...
* @param event - The original execution event
* @param props - The allocation structure for the listener
* @param elements - List containing the selection context
* @param device - Containing Device
* @param tuner - Containing Tuner
* @return IStatus for whether or not the view was created successfully
*/
private IStatus createSriView(final ExecutionEvent event, final DataType[] props, final List< ? > elements, final ScaDevice< ? > device,
final TunerStatus tuner) throws ExecutionException {
// Get all ports on the containing device
List<ScaPort< ? , ? >> devicePorts = device.getPorts();
// Get all the "supported" uses ports from the preceding list
List<ScaUsesPort> usesPorts = new ArrayList<ScaUsesPort>();
for (ScaPort< ? , ? > port : devicePorts) {
if (port instanceof ScaUsesPort && BulkIOType.isTypeSupported(port.getRepid())) {
usesPorts.add((ScaUsesPort) port);
}
}
// Assign the uses port that the SRI View will listen to
final ScaUsesPort usesPort;
if (usesPorts.size() == 1) {
usesPort = usesPorts.get(0);
} else if (usesPorts.size() > 1) {
// If there is more than one uses port, let the user specify which one they want
ScaItemProviderAdapterFactory factory = new ScaItemProviderAdapterFactory();
ListSelectionDialog dialog = new ListSelectionDialog(HandlerUtil.getActiveShellChecked(event), usesPorts,
ArrayContentProvider.getInstance(), new AdapterFactoryLabelProvider(factory), "Select output Port to use: ");
if (dialog.open() == Window.OK) {
Object[] result = dialog.getResult();
if (result.length >= 1) {
// Assume the first selected port
usesPort = (ScaUsesPort) result[0];
} else {
// User did not select a uses port
usesPort = null;
}
} else {
// User selected Cancel
usesPort = null;
}
factory.dispose();
} else {
// There are no uses ports for this device
usesPort = null;
}
if (usesPort == null) {
return Status.CANCEL_STATUS;
}
// Display the SRI View
final String listenerID = getListenerID(props);
IStatus retVal = displaySriView(event, usesPort, listenerID);
// Declare deallocate logic
if (sriViewLocalRef != null) {
// Deallocate listener if the view is closed first
sriViewLocalRef.getTreeViewer().getTree().addDisposeListener(new DisposeListener() {
@Override
public void widgetDisposed(DisposeEvent e) {
if (containsListener(tuner, props)) {
deallocate(tuner, props, device);
}
}
});
// Close the view if the listener is deallocated first
ScaModelCommand.execute(tuner, new ScaModelCommand() {
@Override
public void execute() {
for (ListenerAllocation a : tuner.getListenerAllocations()) {
if (a.getListenerID().equals(listenerID)) {
a.eAdapters().add(new AdapterImpl() {
@Override
public void notifyChanged(org.eclipse.emf.common.notify.Notification msg) {
if (msg.isTouch()) {
return;
}
switch (msg.getFeatureID(ListenerAllocation.class)) {
case FrontendPackage.LISTENER_ALLOCATION__TUNER_STATUS:
if (msg.getNewValue() == null) {
((Notifier) msg.getNotifier()).eAdapters().remove(this);
if (sriViewLocalRef.getTreeViewer().getTree().isDisposed()) {
return;
}
sriViewLocalRef.getTreeViewer().getTree().getDisplay().asyncExec(new Runnable() {
@Override
public void run() {
sriViewLocalRef.getViewSite().getPage().hideView(sriViewLocalRef);
}
});
}
break;
default:
break;
}
};
});
}
}
}
});
}
return retVal;
}
/**
* Creates the allocation structure that can be passed to the allocateCapacity(DataType[] capacities) method.
* Structure consists of a DataType containing a struct (cast to Any) which contains a single
* ScaSimpleProperty which defines the listener allocation properties.
* @param tuner - The containing tuner object to which the listener is tied
* @return listenerCapacity as a DataType array. Contains values for Existing_Allocation_ID and
* Listener_Allocation_ID
*/
private DataType[] createAllocationProperties(TunerStatus tuner) {
List<DataType> listenerCapacity = new ArrayList<DataType>();
DataType dt = new DataType();
ScaStructProperty struct = ScaFactory.eINSTANCE.createScaStructProperty();
// Cycle through enum of listener allocation properties and set values
for (ListenerAllocationProperties allocProp : ListenerAllocationProperties.values()) {
ScaSimpleProperty simple = ScaFactory.eINSTANCE.createScaSimpleProperty();
Simple definition = (Simple) PrfFactory.eINSTANCE.create(PrfPackage.Literals.SIMPLE);
definition.setType(allocProp.getType());
definition.setId(allocProp.getType().getLiteral());
definition.setName(allocProp.getType().getName());
simple.setDefinition(definition);
simple.setId(allocProp.getId());
switch (allocProp) {
case EXISTING_ALLOCATION_ID:
simple.setValue(TunerUtils.getControlId(tuner));
break;
case LISTENER_ALLOCATION_ID:
String listenerAllocationID = "SRI_" + System.getProperty("user.name") + ":" + System.currentTimeMillis();
simple.setValue(listenerAllocationID);
break;
default:
break;
}
struct.getFields().add(simple);
}
dt.id = "FRONTEND::listener_allocation";
dt.value = struct.toAny();
listenerCapacity.add(dt);
return listenerCapacity.toArray(new DataType[0]);
}
/**
* Calls deallocateCapacity for the provided listener
* @param tuner - Containing tuner
* @param props - DataType[] containing the listener allocation properties
* @param device - Containing device
*/
private void deallocate(TunerStatus tuner, final DataType[] props, final ScaDevice< ? > device) {
// First, confirm that the allocation properties contains a reference to a listener that is in the tuner
if (!containsListener(tuner, props)) {
return;
}
Job job = new Job("FEI - Deallocate Listener") {
@Override
protected IStatus run(IProgressMonitor parentMonitor) {
try {
SubMonitor subMonitor = SubMonitor.convert(parentMonitor, "Deallocating listener...", 2);
if (device != null && !device.isDisposed()) {
CorbaUtils.invoke(new Callable<IStatus>() {
@Override
public IStatus call() throws Exception {
try {
device.deallocateCapacity(props);
return Status.OK_STATUS;
} catch (InvalidCapacity e) {
return new Status(IStatus.ERROR, FrontEndUIActivator.PLUGIN_ID, "Invalid Capacity in SRI View deallocation: " + e.msg, e);
} catch (InvalidState e) {
return new Status(IStatus.ERROR, FrontEndUIActivator.PLUGIN_ID, "Invalid State in SRI View deallocation: " + e.msg, e);
}
}
}, subMonitor.newChild(1));
device.refresh(subMonitor.newChild(1), RefreshDepth.SELF);
}
} catch (InterruptedException e) {
return new Status(IStatus.ERROR, FrontEndUIActivator.PLUGIN_ID, "Interrupted Exception during SRI View deallocation", e);
} catch (CoreException e) {
return new Status(e.getStatus().getSeverity(), FrontEndUIActivator.PLUGIN_ID, "Failed to deallocate", e);
}
return Status.OK_STATUS;
}
};
job.setUser(false);
job.setSystem(true);
job.schedule();
}
/**
* Convenience method that extracts the listener ID from the allocation properties structure
* @param props - The allocation structure for the listener
* @return listener allocation ID as a String or empty string is a listener allocation ID is not found
*/
private String getListenerID(DataType[] props) {
for (DataType prop : props) {
DataType[] dt = PropertiesHelper.extract(prop.value);
for (DataType d : dt) {
if (d.id.equals(ListenerAllocationProperties.LISTENER_ALLOCATION_ID.getId())) {
return (d.value.toString());
}
}
}
return "";
}
/**
* Checks to see if the provided allocation properties contain a reference to
* a listener that is in the provided tuner
* @param tuner - Containing tuner
* @param props - DataType[] containing the allocation properties
* @return
*/
private boolean containsListener(TunerStatus tuner, DataType[] props) {
String listenerId = getListenerID(props);
for (ListenerAllocation a : tuner.getListenerAllocations()) {
if (a.getListenerID().equals(listenerId)) {
return true;
}
}
return false;
}
/**
* Displays the SRI View...
* @param event - Original execution event
* @param usesPort - Uses port that the SRI View is listening to
* @param connectionId - Existing connection ID for SRI View to piggyback on
* @return
*/
public IStatus displaySriView(final ExecutionEvent event, final ScaUsesPort usesPort, final String connectionId) {
// Create the name and tooltip for the view tab
final ScaItemProviderAdapterFactory factory = new ScaItemProviderAdapterFactory();
final StringBuilder name = new StringBuilder();
final StringBuilder tooltip = new StringBuilder();
createTooltip(factory, name, tooltip, usesPort);
factory.dispose();
try {
IViewPart view = window.getActivePage().showView(SriDataView.ID, SriDataView.createSecondaryId(usesPort), IWorkbenchPage.VIEW_ACTIVATE);
final SriDataView sriView = (SriDataView) view;
Job job = new Job("SRI View setup...") {
@Override
protected IStatus run(IProgressMonitor monitor) {
SubMonitor subMonitor = SubMonitor.convert(monitor, "Fetching SRI...", IProgressMonitor.UNKNOWN);
usesPort.fetchAttributes(subMonitor.newChild(1));
// Connect to the port
sriView.activateReceiver(usesPort, connectionId);
// Set view name and tooltip
if (name.length() > 0 || tooltip.length() > 0) {
Display display = window.getWorkbench().getDisplay();
display.asyncExec(new Runnable() {
@Override
public void run() {
if (name.length() > 0) {
sriView.setPartName(name.toString());
}
if (tooltip.length() > 0) {
sriView.setTitleToolTip(tooltip.toString());
}
}
});
}
return Status.OK_STATUS;
}
};
job.schedule();
// Set local reference to SRI Tree Viewer to give us a widget for dispose logic
sriViewLocalRef = sriView;
} catch (PartInitException e) {
StatusManager.getManager().handle(new Status(IStatus.ERROR, FrontEndUIActivator.PLUGIN_ID, "Failed to show SRI Data View", e),
StatusManager.LOG | StatusManager.SHOW);
}
return Status.OK_STATUS;
}
/**
* Creates the name and tooltip for the view
* @param factory
* @param name
* @param tooltip
* @param usesPort
*/
private void createTooltip(AdapterFactory factory, StringBuilder name, StringBuilder tooltip, ScaUsesPort usesPort) {
// Build a tmp list containing the strings of all containing elements
List<String> tmp = new LinkedList<String>();
for (EObject eObj = usesPort; !(eObj instanceof ScaDomainManagerRegistry) && eObj != null; eObj = eObj.eContainer()) {
Adapter adapter = factory.adapt(eObj, IItemLabelProvider.class);
if (adapter instanceof IItemLabelProvider) {
IItemLabelProvider lp = (IItemLabelProvider) adapter;
String text = lp.getText(eObj);
if (text != null && !text.isEmpty()) {
tmp.add(0, text);
}
}
}
// Create the view tab tooltip
if (!tmp.isEmpty()) {
for (Iterator<String> i = tmp.iterator(); i.hasNext();) {
tooltip.append(i.next());
if (i.hasNext()) {
tooltip.append(" -> "); // seperator between elements
} else {
tooltip.append(" -> SRI ");
}
}
tooltip.append("\n");
}
// Create the view tab text
String nameStr = usesPort.getName();
if (nameStr != null && !nameStr.isEmpty()) {
name.append(nameStr + " SRI");
}
}
}