dk.dma.epd.shore.EPDShore.java Source code

Java tutorial

Introduction

Here is the source code for dk.dma.epd.shore.EPDShore.java

Source

/* Copyright (c) 2011 Danish Maritime Authority.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package dk.dma.epd.shore;

import java.beans.beancontext.BeanContextServicesSupport;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Properties;
import java.util.concurrent.CountDownLatch;
import java.util.function.Consumer;

import javax.swing.JFrame;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;

import net.maritimecloud.core.id.MaritimeId;
import net.maritimecloud.core.id.MmsiId;

import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.bbn.openmap.proj.coords.LatLonPoint;
import com.jtattoo.plaf.hifi.HiFiLookAndFeel;

import dk.dma.ais.reader.AisReader;
import dk.dma.ais.virtualnet.transponder.gui.TransponderFrame;
import dk.dma.commons.app.OneInstanceGuard;
import dk.dma.enav.model.geometry.Position;
import dk.dma.epd.common.ExceptionHandler;
import dk.dma.epd.common.graphics.Resources;
import dk.dma.epd.common.prototype.Bootstrap;
import dk.dma.epd.common.prototype.EPD;
import dk.dma.epd.common.prototype.PluginLoader;
import dk.dma.epd.common.prototype.gui.SystemTrayCommon;
import dk.dma.epd.common.prototype.model.identity.IdentityHandler;
import dk.dma.epd.common.prototype.model.voyage.VoyageEventDispatcher;
import dk.dma.epd.common.prototype.sensor.nmea.NmeaFileSensor;
import dk.dma.epd.common.prototype.sensor.nmea.NmeaSensor;
import dk.dma.epd.common.prototype.sensor.nmea.NmeaSerialSensorFactory;
import dk.dma.epd.common.prototype.sensor.nmea.NmeaStdinSensor;
import dk.dma.epd.common.prototype.sensor.nmea.NmeaTcpSensor;
import dk.dma.epd.common.prototype.sensor.pnt.PntTime;
import dk.dma.epd.common.prototype.service.ChatServiceHandlerCommon;
import dk.dma.epd.common.prototype.service.IntendedRouteHandlerCommon;
import dk.dma.epd.common.prototype.service.MaritimeCloudService;
import dk.dma.epd.common.prototype.service.MaritimeCloudUtils;
import dk.dma.epd.common.prototype.service.MsiNmServiceHandlerCommon;
import dk.dma.epd.common.prototype.settings.SensorSettings;
import dk.dma.epd.common.prototype.shoreservice.ShoreServicesCommon;
import dk.dma.epd.common.util.VersionInfo;
import dk.dma.epd.shore.ais.AisHandler;
import dk.dma.epd.shore.event.DragMouseMode;
import dk.dma.epd.shore.event.NavigationMouseMode;
import dk.dma.epd.shore.event.SelectMouseMode;
import dk.dma.epd.shore.fal.FALManager;
import dk.dma.epd.shore.gui.notification.NotificationCenter;
import dk.dma.epd.shore.gui.utils.StaticImages;
import dk.dma.epd.shore.gui.views.MainFrame;
import dk.dma.epd.shore.route.RouteManager;
import dk.dma.epd.shore.service.FALHandler;
import dk.dma.epd.shore.service.IntendedRouteHandler;
import dk.dma.epd.shore.service.MonaLisaRouteOptimization;
import dk.dma.epd.shore.service.RouteSuggestionHandler;
import dk.dma.epd.shore.service.StrategicRouteHandler;
import dk.dma.epd.shore.service.VoctHandler;
import dk.dma.epd.shore.services.shore.ShoreServices;
import dk.dma.epd.shore.settings.EPDSensorSettings;
import dk.dma.epd.shore.settings.EPDSettings;
import dk.dma.epd.shore.voct.SRUManager;
import dk.dma.epd.shore.voct.VOCTManager;
import dk.dma.epd.shore.voyage.VoyageManager;

/**
 * Main class with main method.
 * 
 * Starts up components, bean context and GUI.
 * 
 */
public final class EPDShore extends EPD {

    private static Logger LOG;
    private MainFrame mainFrame;
    private BeanContextServicesSupport beanHandler;
    private NmeaSensor aisSensor;
    private AisReader aisReader;
    private ShoreServicesCommon shoreServicesCommon;
    private StaticImages staticImages;
    private TransponderFrame transponderFrame;
    private MonaLisaRouteOptimization monaLisaRouteExchange;

    private SRUManager sruManager;
    private VOCTManager voctManager;

    private VoyageManager voyageManager;

    // Maritime Cloud services
    private IntendedRouteHandlerCommon intendedRouteHandler;
    // private VoctHandler voctHandler;

    private PluginLoader pluginLoader;

    /**
     * Event dispatcher used to notify listeners of voyage changes.
     */
    private VoyageEventDispatcher voyageEventDispatcher = new VoyageEventDispatcher();

    /**
     * Starts the program by initializing the various threads and spawning the main GUI
     * 
     * @param args
     */
    public static void main(String[] args) throws IOException {
        // Check if the home path has been specified via the command line
        String homePath = (args.length > 0) ? args[0] : null;
        new EPDShore(homePath);
    }

    /**
     * Constructor
     * 
     * @param path
     *            the home path to use
     */
    private EPDShore(String path) throws IOException {
        super();

        if (!StringUtils.isEmpty(path)) {
            homePath = Paths.get(path);
        } else {
            homePath = determineHomePath(Paths.get(System.getProperty("user.home"), ".epd-shore"));
        }

        new Bootstrap().run(this, new String[] { "epd-shore.properties", "settings.properties", "transponder.xml" },
                new String[] { "workspaces", "routes", "shape/GSHHS_shp", "identities" });

        // Set up log4j logging
        LOG = LoggerFactory.getLogger(EPDShore.class);

        // Set default exception handler
        Thread.setDefaultUncaughtExceptionHandler(new ExceptionHandler());

        LOG.info("Starting eNavigation Prototype Display Shore - version " + VersionInfo.getVersionAndBuild());
        LOG.info("Copyright (C) 2012 Danish Maritime Authority");
        LOG.info("This program comes with ABSOLUTELY NO WARRANTY.");
        LOG.info("This is free software, and you are welcome to redistribute it under certain conditions.");
        LOG.info("For details see LICENSE file.");

        // Create the bean context (map handler)
        // mapHandler = new MapHandler();
        beanHandler = new BeanContextServicesSupport();

        // Load settings or get defaults and add to bean context
        settings = new EPDSettings();
        LOG.info("Using settings file: " + getSettings().getSettingsFile());
        settings.loadFromFile();
        beanHandler.add(settings);

        // Determine if instance already running and if that is allowed

        OneInstanceGuard guard = new OneInstanceGuard(getHomePath().resolve("epd.lock").toString());
        if (guard.isAlreadyRunning()) {
            handleEpdAlreadyRunning();
        }

        // Enable GPS timer by adding it to bean context
        PntTime.init(settings.getSensorSettings().isUseTimeFromPnt());
        beanHandler.add(PntTime.getInstance());

        // aisHandler = new AisHandlerCommon();
        aisHandler = new AisHandler(settings.getAisSettings());
        aisHandler.loadView();
        EPD.startThread(aisHandler, "AisHandler");
        beanHandler.add(aisHandler);

        // Add StaticImages handler
        staticImages = new StaticImages();
        beanHandler.add(staticImages);

        // Load routeManager
        routeManager = RouteManager.loadRouteManager();
        beanHandler.add(routeManager);

        falManager = FALManager.loadFALManager();
        beanHandler.add(falManager);

        // To be changed to load similar to routeManager
        // voyageManager = new VoyageManager();
        voyageManager = VoyageManager.loadVoyageManager();
        beanHandler.add(voyageManager);

        sruManager = SRUManager.loadSRUManager();
        beanHandler.add(sruManager);

        // Create shore services
        shoreServicesCommon = new ShoreServices(getSettings().getEnavSettings());
        beanHandler.add(shoreServicesCommon);

        // Create mona lisa route exchange
        monaLisaRouteExchange = new MonaLisaRouteOptimization();
        beanHandler.add(monaLisaRouteExchange);

        // Create Maritime Cloud service
        maritimeCloudService = new MaritimeCloudService();
        beanHandler.add(maritimeCloudService);
        maritimeCloudService.start();

        // Create strategic route Handler;
        strategicRouteHandler = new StrategicRouteHandler();
        beanHandler.add(strategicRouteHandler);

        // Create intended route handler
        intendedRouteHandler = new IntendedRouteHandler();
        beanHandler.add(intendedRouteHandler);

        // Create the route suggestion handler
        // routeSuggestionHandler = new RouteSuggestionHandler();
        routeSuggestionHandler = RouteSuggestionHandler.loadRouteSuggestionHandler();
        beanHandler.add(routeSuggestionHandler);

        // Create a new MSI-NM handler
        msiNmHandler = new MsiNmServiceHandlerCommon();
        beanHandler.add(msiNmHandler);

        // Create a chat service handler
        chatServiceHandler = new ChatServiceHandlerCommon();
        beanHandler.add(chatServiceHandler);

        // Create identity handler
        identityHandler = new IdentityHandler();
        beanHandler.add(identityHandler);

        // Start sensors
        startSensors();

        pluginLoader = new PluginLoader(getProperties(), getHomePath(), getPropertyFileName());

        try {
            pluginLoader.createPluginComponents(new Consumer<Object>() {
                public void accept(Object comp) {
                    beanHandler.add(comp);
                }
            });
        } catch (Exception e) {
            LOG.error("Failed to load plugin container " + e.getMessage());
        }

        final CountDownLatch guiCreated = new CountDownLatch(1);

        // Create and show GUI
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                createAndShowGUI();
                guiCreated.countDown();
            }
        });

        // Wait for gui to be created
        try {
            guiCreated.await();

        } catch (InterruptedException e) {
            LOG.error("Interrupted while waiting for GUI to be created", e);
        }

        // Create vocthandler
        voctHandler = new VoctHandler();
        beanHandler.add(voctHandler);

        // Create voct manager
        voctManager = new VOCTManager();
        beanHandler.add(voctManager);
        voctManager.loadVOCTManager();

        // Create FAL Handler
        falHandler = new FALHandler();
        beanHandler.add(falHandler);

        // Create embedded transponder frame
        transponderFrame = new TransponderFrame(getHomePath().resolve("transponder.xml").toString(), true,
                mainFrame);
        mainFrame.getTopMenu().setTransponderFrame(transponderFrame);
        beanHandler.add(transponderFrame);

        if (settings.getSensorSettings().isStartTransponder()) {
            transponderFrame.startTransponder();
        }
    }

    /**
     * Returns the current {@code EPDShore} instance
     * 
     * @return the current {@code EPDShore} instance
     */
    public static EPDShore getInstance() {
        return (EPDShore) instance;
    }

    /**
     * Returns the settings associated with the EPD system
     * 
     * @return the settings associated with the EPD system
     */
    @Override
    public EPDSettings getSettings() {
        return (EPDSettings) settings;
    }

    /**
     * @return the voctManager
     */
    public VOCTManager getVoctManager() {
        return voctManager;
    }

    /**
     * Returns the default shore mouse mode service list
     * 
     * @return the default shore mouse mode service list
     */
    @Override
    public String[] getDefaultMouseModeServiceList() {
        String[] ret = new String[3];
        ret[0] = DragMouseMode.MODEID; // "DragMouseMode"
        ret[1] = NavigationMouseMode.MODEID; // "ZoomMouseMode"
        ret[2] = SelectMouseMode.MODEID; // "SelectMouseMode"
        return ret;
    }

    @Override
    public Path getHomePath() {
        return homePath;
    }

    /**
     * {@inheritDoc}
     */
    public RouteSuggestionHandler getRouteSuggestionHandler() {
        return (RouteSuggestionHandler) routeSuggestionHandler;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public StrategicRouteHandler getStrategicRouteHandler() {
        return (StrategicRouteHandler) strategicRouteHandler;
    }

    /**
     * Close app routine with possibility for restart - not implemented
     * 
     * @param restart
     *            - boolean value for program restart
     */
    public void closeApp(boolean restart) {
        // Shutdown routine

        // Chart panels

        // Window state

        // Window state has a
        // Name, Size, Location, Locked status, on top status
        // Chart panel has a zoom level, position

        // Main application

        String filename = "temp.workspace";
        mainFrame.saveWorkSpace(filename);

        mainFrame.saveSettings();
        settings.saveToFile();

        // GuiSettings
        // Handler settings
        voyageManager.saveToFile();
        routeManager.saveToFile();
        msiNmHandler.saveToFile();
        aisHandler.saveView();
        transponderFrame.shutdown();
        falManager.saveToFile();

        // Maritime cloud services
        maritimeCloudService.stop();
        strategicRouteHandler.shutdown();
        routeSuggestionHandler.shutdown();
        intendedRouteHandler.shutdown();
        chatServiceHandler.shutdown();
        msiNmHandler.shutdown();

        // Stop the system tray
        systemTray.shutdown();

        // Stop sensors
        stopSensors();

        // Close all plugins
        pluginLoader.closePlugins();

        LOG.info("Closing EPDShore");
        this.restart = restart;
        System.exit(restart ? 2 : 0);
    }

    /**
     * Creates and shows the GUI
     */
    private void createAndShowGUI() {
        // Set the look and feel.
        initLookAndFeel();

        // Make sure we have nice window decorations.
        JFrame.setDefaultLookAndFeelDecorated(false);

        // Create and set up the main window
        mainFrame = new MainFrame();
        mainFrame.setVisible(true);

        // Create the system tray
        systemTray = new SystemTrayCommon();
        beanHandler.add(systemTray);

        // Create the notification center
        notificationCenter = new NotificationCenter(getMainFrame());
        beanHandler.add(notificationCenter);

    }

    /**
     * Function used to measure time
     * 
     * @param start
     *            - Startime
     * @return - Elapsed time
     */
    public double elapsed(long start) {
        double elapsed = System.nanoTime() - start;
        return elapsed / 1000000.0;
    }

    /**
     * Returns a reference to the AIS handler
     * 
     * @return a reference to the AIS handler
     */
    @Override
    public AisHandler getAisHandler() {
        return (AisHandler) aisHandler;
    }

    /**
     * Returns a reference to the intended route handler
     * 
     * @return a reference to the intended route handler
     */
    public IntendedRouteHandlerCommon getIntendedRouteHandler() {
        return intendedRouteHandler;
    }

    /**
     * BeanHandler for program structure
     * 
     * @return - beanHandler
     */
    public BeanContextServicesSupport getBeanHandler() {
        return beanHandler;
    }

    // /**
    // * Return the GpsHandler
    // * @return - GpsHandler
    // */
    // public static GpsHandler getGpsHandler() {
    // return gpsHandler;
    // }

    /**
     * Return the mainFrame gui element
     * 
     * @return - mainframe gui
     */
    public MainFrame getMainFrame() {
        return mainFrame;
    }

    @Override
    public FALManager getFalManager() {
        return (FALManager) falManager;
    }

    /**
     * Returns the MMSI of the shore center, or null if not defined
     * 
     * @return the MMSI of the shore center
     */
    @Override
    public Long getMmsi() {
        String shoreID = getSettings().getEnavSettings().getShoreId();
        if (shoreID == null || !StringUtils.isNumeric(shoreID)
                || !shoreID.startsWith(MaritimeCloudUtils.STCC_MMSI_PREFIX)) {
            return null;
        }
        return Long.parseLong((String) shoreID.subSequence(0, 9));
    }

    /**
     * Returns the maritime id of the shore center, or null if not defined
     * 
     * @return the maritime id of the shore center
     */
    @Override
    public MaritimeId getMaritimeId() {
        Long mmsi = getMmsi();
        return (mmsi != null) ? new MmsiId(getMmsi().intValue()) : null;
    }

    /**
     * Returns the current position of the shore center
     * 
     * @return the current position of the shore center
     */
    @Override
    public Position getPosition() {
        LatLonPoint pos = getSettings().getEnavSettings().getShorePos();
        return Position.create(pos.getLatitude(), pos.getLongitude());
    }

    @Override
    public RouteManager getRouteManager() {
        return (RouteManager) routeManager;
    }

    /**
     * Return the shoreService used in shore connections like MSI
     * 
     * @return - shoreServicesCommon
     */
    public ShoreServicesCommon getShoreServices() {
        return shoreServicesCommon;
    }

    /**
     * Set the used theme using lookAndFeel
     */
    private void initLookAndFeel() {
        try {
            // UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());

            Properties props = new Properties();
            props.put("logoString", "EPD-Shore");
            props.put("backgroundPattern", "false");
            props.put("textAntiAliasingMode", "TEXT_ANTIALIAS_VBGR");
            // props.put("menuOpaque", "true");
            // props.put("tooltipCastShadow", "true");

            // small font
            props.setProperty("controlTextFont", "Dialog 10");
            props.setProperty("systemTextFont", "Dialog 10");
            props.setProperty("userTextFont", "Dialog 10");
            props.setProperty("menuTextFont", "Dialog 10");
            props.setProperty("windowTitleFont", "Dialog bold 10");
            props.setProperty("subTextFont", "Dialog 8");

            // props.put("tooltipBorderSize", "15");
            // props.put("tooltipShadowSize", "15");

            // NoireLookAndFeel laf = new NoireLookAndFeel();
            HiFiLookAndFeel laf = new HiFiLookAndFeel();
            // NoireLookAndFeel.setCurrentTheme(props);
            HiFiLookAndFeel.setCurrentTheme(props);

            UIManager.setLookAndFeel(laf);

        } catch (Exception e) {
            LOG.error("Failed to set look and feed: " + e.getMessage());
        }

    }

    @Override
    protected String getPropertyFileName() {
        return "epd-shore.properties";
    }

    @Override
    protected void propertyLoadError(String msg, IOException e) {
        LOG.error(msg + e.getMessage());
    }

    /**
     * Starts the sensors defined in the {@linkplain SensorSettings} and hook up listeners
     */
    @Override
    protected void startSensors() {
        EPDSensorSettings sensorSettings = getSettings().getSensorSettings();
        switch (sensorSettings.getAisConnectionType()) {
        case NONE:
            aisSensor = new NmeaStdinSensor();
            break;
        case TCP:
            aisSensor = new NmeaTcpSensor(sensorSettings.getAisHostOrSerialPort(),
                    sensorSettings.getAisTcpOrUdpPort());
            break;
        case SERIAL:
            aisSensor = NmeaSerialSensorFactory.create(sensorSettings.getAisHostOrSerialPort(),
                    sensorSettings.getAisSerialPortBaudRate());
            break;
        case FILE:
            aisSensor = new NmeaFileSensor(sensorSettings.getAisFilename(), sensorSettings);
            break;
        default:
            LOG.error("Unknown sensor connection type: " + sensorSettings.getAisConnectionType());
        }

        if (aisSensor != null) {
            aisSensor.setVessel(false);
            aisSensor.addAisListener(aisHandler);
            aisSensor.addPntListener(PntTime.getInstance());
            aisSensor.start();
            // Add ais sensor to bean context
            beanHandler.add(aisSensor);
        }

    }

    /**
     * Stops all sensors and remove listeners
     */
    @Override
    protected void stopSensors() {
        // Stop AIS sensor
        if (aisSensor != null) {
            beanHandler.remove(aisSensor);
            aisSensor.removeAisListener(aisHandler);
            stopSensor(aisSensor, 3000L);
            aisSensor = null;
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void settingsChanged(Type type) {
        if (type == Type.SENSOR) {
            LOG.warn("Restarting all sensors");
            stopSensors();
            startSensors();

        } else if (type == Type.CLOUD) {
            LOG.warn("Restarting all eNav Service");
            maritimeCloudService.stop();
            maritimeCloudService.start();
        } else if (type == Type.ENAV) {
            // Update the intended route handler such that it can re apply its filter.
            this.intendedRouteHandler.updateSettings(this.settings.getEnavSettings());
        }

    }

    public StaticImages getStaticImages() {
        return staticImages;
    }

    /**
     * Function used to call sleep on a thread
     * 
     * @param ms
     *            - time in ms of how long to sleep
     */
    public static void sleep(long ms) {
        try {
            Thread.sleep(ms);
        } catch (InterruptedException e) {
            LOG.error(e.getMessage());
        }
    }

    public AisReader getAisReader() {
        return aisReader;
    }

    public VoyageManager getVoyageManager() {
        return voyageManager;
    }

    /**
     * @return the sruManager
     */
    public SRUManager getSruManager() {
        return sruManager;
    }

    /**
     * @return the monaLisaRouteExchange
     */
    public MonaLisaRouteOptimization getMonaLisaRouteExchange() {
        return monaLisaRouteExchange;
    }

    /**
     * Get the application-wide voyage event dispatcher.
     * 
     * @return The application-wide voyage event dispatcher.
     */
    public VoyageEventDispatcher getVoyageEventDispatcher() {
        return voyageEventDispatcher;
    }

    /**
     * @return the voctHandler
     */
    public VoctHandler getVoctHandler() {
        return (VoctHandler) voctHandler;
    }

    /**
     * Returns a {@code Resource} instance which loads resource from the same class-loader/jar-file as the {@code EPDShore} class.
     * 
     * @return a new {@code Resource} instance
     */
    public static Resources res() {
        return Resources.get(EPDShore.class);
    }

}