blob: 80cb109e53aaa0eded2e0df9a2579a7da3b51d9c [file] [log] [blame]
/**
* Copyright (c) 2018 CEA.
* 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
*
* Contributors:
* CEA - initial API and implementation and/or initial documentation
*/
package org.eclipse.sensinact.studio.view.outdoor;
import java.util.Arrays;
import java.util.List;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import org.apache.log4j.Logger;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.sensinact.studio.http.server.GatewayHttpServer;
import org.eclipse.sensinact.studio.http.server.serverstarted.PortUpdate;
import org.eclipse.sensinact.studio.http.server.serverstarted.ResourceAddedUpdate;
import org.eclipse.sensinact.studio.http.server.serverstarted.ServerUpdatedListener;
import org.eclipse.sensinact.studio.http.server.serverstarted.ServerUpdatedManager;
import org.eclipse.sensinact.studio.http.server.serverstarted.UpdateDescriptor;
import org.eclipse.sensinact.studio.model.manager.listener.devicelocation.DeviceLocationListener;
import org.eclipse.sensinact.studio.model.manager.listener.devicelocation.DeviceLocationManager;
import org.eclipse.sensinact.studio.model.resource.utils.DeviceDescriptor;
import org.eclipse.sensinact.studio.model.resource.utils.GPScoordinates;
import org.eclipse.sensinact.studio.preferences.ConfigurationManager;
import org.eclipse.sensinact.studio.webapp.outdoor.Startup;
import org.eclipse.swt.SWT;
import org.eclipse.swt.browser.Browser;
import org.eclipse.swt.browser.ProgressEvent;
import org.eclipse.swt.browser.ProgressListener;
import org.eclipse.swt.dnd.DND;
import org.eclipse.swt.dnd.DropTarget;
import org.eclipse.swt.dnd.DropTargetEvent;
import org.eclipse.swt.dnd.DropTargetListener;
import org.eclipse.swt.dnd.TextTransfer;
import org.eclipse.swt.dnd.Transfer;
import org.eclipse.swt.events.ControlEvent;
import org.eclipse.swt.events.ControlListener;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.PlatformUI;
/**
* @author Nicolas Hili, Etienne Gandrille
*/
public class OutdoorDeploymentView implements ServerUpdatedListener, DeviceLocationListener {
private static final Logger logger = Logger.getLogger(OutdoorDeploymentView.class);
public static final String VIEW_ID = "org.eclipse.sensinact.studio.view.outdoor.view";
private static Browser browser;
private int studioPort = 0;
private static Composite dndComposite;
private static Shell dndShell;
private static boolean browserStarted = false;
public OutdoorDeploymentView() {
// stats server
GatewayHttpServer.getInstance();
}
@PostConstruct
public void createControls(final Composite parent) {
parent.setLayout(null);
parent.setLayoutData(new GridData(SWT.LEFT, SWT.TOP, true, true, 1, 1));
browser = new Browser(parent, SWT.NONE);
// Invisible shell and composite for Drag'n Drop feature
dndShell = new Shell(Display.getCurrent(), SWT.ON_TOP);
dndShell.open();
dndShell.setAlpha(125);
dndShell.setVisible(false);
dndComposite = new Composite(dndShell, SWT.NO_BACKGROUND);
dndComposite.setVisible(false);
// updates device position on the map
DeviceLocationManager.getInstance().addDeviceLocationListener(this);
// swap browserStarted flag to true when browser is ready
browser.addProgressListener(new BrowserProgressListener());
// updates dnd elements when browser (more precisely, parent) geometry
// changes
parent.addControlListener(new ControlSizePositionListener(parent));
// drop target configuration
int operations = DND.DROP_MOVE | DND.DROP_COPY | DND.DROP_DEFAULT;
Transfer[] transferTypes = new Transfer[] { TextTransfer.getInstance() };
DropTarget target = new DropTarget(dndComposite, operations);
target.setTransfer(transferTypes);
// when a device is dropped, updates its location
target.addDropListener(new DropListenerForDevices());
// when the server parameters are updated, the browser URL also
ServerUpdatedManager.getInstance().addServerUpdatedListener(this);
serverUpdated(null);
}
@PreDestroy
public void dispose() {
ServerUpdatedManager.getInstance().removeServerUpdatedListener(this);
}
public static void beginDrag() {
if (browserStarted) {
dndShell.setVisible(true);
dndComposite.setVisible(true);
updateDndInvisibleElementsGeometry();
}
}
public static void endDrag() {
dndShell.setVisible(false);
dndComposite.setVisible(false);
}
private static void updateDndInvisibleElementsGeometry() {
Point absolutePos = browser.toDisplay(0, 0);
dndShell.setBounds(absolutePos.x, absolutePos.y, browser.getBounds().width, browser.getBounds().height);
dndComposite.setBounds(0, 0, browser.getBounds().width, browser.getBounds().height);
}
/* ============== */
/* DeviceLocation */
/* ============== */
@Override
public void deviceLocationUpdated(final GPScoordinates coordinate, final DeviceDescriptor descriptor) {
Display.getDefault().asyncExec(new Runnable() {
@Override
public void run() {
String dev = "'" + descriptor.toString() + "'";
String cmd = "updateDeviceLocation(" + coordinate.getLat() + "," + coordinate.getLng() + "," + dev + ");";
browser.execute(cmd);
}
});
}
@Override
public void deviceRemoved(final DeviceDescriptor descriptor) {
Display.getDefault().asyncExec(new Runnable() {
@Override
public void run() {
String dev = "'" + descriptor.toString() + "'";
browser.execute("deleteMarker(" + dev + ");");
}
});
}
/* =========================== */
/* Gateway HTTP ServerListener */
/* =========================== */
@Override
public void serverUpdated(UpdateDescriptor descriptor) {
if (isUpdateNeeded(descriptor)) {
studioPort = ConfigurationManager.getStudioPort();
if (studioPort != 0) {
Display.getDefault().asyncExec(new Runnable() {
public void run() {
logger.debug("refresh browser");
browser.setUrl("http://localhost:" + studioPort + "/webapp/outdoor/static/outdoor.html");
}
});
}
}
}
private boolean isUpdateNeeded(UpdateDescriptor descriptor) {
if (descriptor == null) {
// server just started
return true;
} else {
switch (descriptor.getType()) {
case PORT_UPDATE:
PortUpdate pu = (PortUpdate) descriptor;
if (pu.getPort() != 0)
return true;
else
return false;
case RESOURCE_ADDED:
ResourceAddedUpdate ra = (ResourceAddedUpdate) descriptor;
List<String> paths = ra.getRegisteredPath();
if (! Arrays.asList(Startup.BUNDLE_PATHS).contains(ra.getCurPath()))
return false;
for (String bundlePath : Startup.BUNDLE_PATHS) {
if (!paths.contains(bundlePath))
return false;
}
return true;
default:
throw new RuntimeException("Should never happend (" + descriptor.getType() + ")");
}
}
}
/* ================= */
/* Control listeners */
/* ================= */
private final class BrowserProgressListener implements ProgressListener {
public void changed(ProgressEvent event) {
}
public void completed(ProgressEvent event) {
browserStarted = true;
}
}
private final class ControlSizePositionListener implements ControlListener {
private final Composite parent;
private ControlSizePositionListener(Composite parent) {
this.parent = parent;
}
private void update(ControlEvent e) {
browser.setBounds(0, 0, parent.getBounds().width, parent.getBounds().height);
if (e.widget instanceof Control) {
updateDndInvisibleElementsGeometry();
}
}
@Override
public void controlResized(ControlEvent e) {
update(e);
}
@Override
public void controlMoved(ControlEvent e) {
update(e);
}
}
private final class DropListenerForDevices implements DropTargetListener {
@Override
public void dragEnter(DropTargetEvent event) {
}
@Override
public void dragLeave(DropTargetEvent event) {
}
@Override
public void dragOperationChanged(DropTargetEvent event) {
}
@Override
public void dragOver(DropTargetEvent event) {
}
@Override
public void drop(DropTargetEvent event) {
endDrag();
if (TextTransfer.getInstance().isSupportedType(event.currentDataType)) {
String dropText = (String) event.data;
if (event.widget instanceof DropTarget) {
// Retrieve lat and lng
Point relativePos = ((DropTarget) event.widget).getControl().toControl(event.x, event.y);
String cmd1 = "return computeCoordinates(" + relativePos.x + "," + relativePos.y + ");";
Object[] coordinates = (Object[]) browser.evaluate(cmd1);
Double lat = (Double) coordinates[0];
Double lng = (Double) coordinates[1];
// Retrieve gateway and device
String[] tab = dropText.split("/");
String gateway = tab[0];
if (gateway.startsWith("["))
gateway = gateway.substring(1);
String device = tab[1];
// try to update on server
boolean locationUpdated = DeviceLocationManager.getInstance().updateLocationInServer(new DeviceDescriptor(gateway, device), new GPScoordinates(lat, lng));
if (locationUpdated) {
String dev = "'" + gateway + "/" + device + "'";
String cmd = "return updateDeviceLocation(" + lat + "," + lng + "," + dev + ");";
browser.evaluate(cmd);
} else {
Shell shell = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell();
MessageDialog.openWarning(shell, "Error", "The location of " + device + " device is NOT updatable");
}
}
}
}
@Override
public void dropAccept(DropTargetEvent event) {
}
}
}