nz.co.fortytwo.freeboard.server.util.Util.java Source code

Java tutorial

Introduction

Here is the source code for nz.co.fortytwo.freeboard.server.util.Util.java

Source

/*
 * Copyright 2012,2013 Robert Huitema robert@42.co.nz
 *
 * This file is part of FreeBoard. (http://www.42.co.nz/freeboard)
 *
 *  FreeBoard is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
    
 *  FreeBoard is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
    
 *  You should have received a copy of the GNU General Public License
 *  along with FreeBoard.  If not, see <http://www.gnu.org/licenses/>.
 */
package nz.co.fortytwo.freeboard.server.util;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Collections;
import java.util.Date;
import java.util.Enumeration;
import java.util.Properties;
import java.util.Set;
import java.util.TreeSet;
import net.sf.marineapi.nmea.parser.DataNotAvailableException;

import net.sf.marineapi.nmea.sentence.RMCSentence;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.SystemUtils;
import org.apache.log4j.Logger;

/**
 * Place for all the left over bits that are used across freeboard
 *
 * @author robert
 *
 */
public class Util {

    private static Logger logger = Logger.getLogger(Util.class);
    private static Properties props;
    public static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd_hh:mm:ss");
    public static File cfg = null;
    private static boolean timeSet = false;

    /**
     * Smooth the data a bit
     *
     * @param prev
     * @param current
     * @return
     */
    public static double movingAverage(double ALPHA, double prev, double current) {
        prev = ALPHA * prev + (1 - ALPHA) * current;
        return prev;
    }

    /**
     * Load the config from the named dir, or if the named dir is null, from the
     * default location The config is cached, subsequent calls get the same
     * object
     *
     * @param dir
     * @return
     * @throws FileNotFoundException
     * @throws IOException
     */
    public static Properties getConfig(String dir) throws FileNotFoundException, IOException {
        if (props == null) {
            //we do a quick override so we get nice sorted output :-)
            System.out.println("Creating config...");
            props = new Properties() {
                /**
                 *
                 */
                private static final long serialVersionUID = 1L;

                @Override
                public Set<Object> keySet() {
                    return Collections.unmodifiableSet(new TreeSet<Object>(super.keySet()));
                }

                @Override
                public synchronized Enumeration<Object> keys() {
                    return Collections.enumeration(new TreeSet<Object>(super.keySet()));
                }
            };
            Util.setDefaults(props);
            if (StringUtils.isNotBlank(dir)) {
                //we provided a config dir, so we use it
                props.setProperty(Constants.CFG_DIR, dir);
                cfg = new File(props.getProperty(Constants.CFG_DIR) + props.getProperty(Constants.CFG_FILE));
            } else if (Util.getUSBFile() != null) {
                //nothing provided, but we have a usb config dir, so use it
                cfg = new File(Util.getUSBFile(),
                        props.getProperty(Constants.CFG_DIR) + props.getProperty(Constants.CFG_FILE));
            } else {
                //use the default config
                cfg = new File(props.getProperty(Constants.CFG_DIR) + props.getProperty(Constants.CFG_FILE));
            }

            if (cfg.exists()) {
                System.out.println("Loading config from " + cfg.getAbsolutePath());
                props.load(new FileReader(cfg));
            } else {
                saveConfig();
            }
        }
        return props;
    }

    /**
     * Save the current config to disk.
     *
     * @throws IOException
     */
    public static void saveConfig() throws IOException {
        if (props == null) {
            System.out.println("Saving config but its null!");
            return;
        }
        System.out.println("Saving config to " + cfg.getAbsolutePath());
        props.store(new FileWriter(cfg), null);

    }

    /**
     * Config defaults
     *
     * @param props
     */
    public static void setDefaults(Properties props) {
        System.out.println("Setting config defaults...");
        //populate sensible defaults here
        props.setProperty(Constants.FREEBOARD_URL, "/freeboard");
        props.setProperty(Constants.FREEBOARD_RESOURCE, "freeboard/");
        props.setProperty(Constants.MAPCACHE_RESOURCE, "./mapcache");
        props.setProperty(Constants.MAPCACHE, "/mapcache");
        props.setProperty(Constants.HTTP_PORT, "8080");
        props.setProperty(Constants.WEBSOCKET_PORT, "9090");
        props.setProperty(Constants.CFG_DIR, "./conf/");
        props.setProperty(Constants.CFG_FILE, "freeboard.cfg");
        props.setProperty(Constants.DEMO, "false");
        props.setProperty(Constants.SERIAL_URL,
                "./src/test/resources/motu.log&scanStream=true&scanStreamDelay=500");
        props.setProperty(Constants.VIRTUAL_URL, "");
        props.setProperty(Constants.USBDRIVE, "/media/usb0");
        props.setProperty(Constants.TRACKS, "/tracks");
        props.setProperty(Constants.TRACKS_RESOURCE, "./tracks");
        props.setProperty(Constants.TRACK_CURRENT, "current.gpx");
        props.setProperty(Constants.WAYPOINTS, "/tracks");
        props.setProperty(Constants.WAYPOINTS_RESOURCE, "./tracks");
        props.setProperty(Constants.WAYPOINT_CURRENT, "waypoints.gpx");
        props.setProperty(Constants.SERIAL_PORTS,
                "/dev/ttyUSB0,/dev/ttyUSB1,/dev/ttyUSB2,/dev/ttyACM0,/dev/ttyACM1,/dev/ttyACM2");
        props.setProperty(Constants.DEPTH_UNIT, "f");
        props.setProperty(Constants.DEPTH_ZERO_OFFSET, "1.5");
        props.setProperty(Constants.SOG_UNIT, "Mi/hr");
        props.setProperty(Constants.SOW_UNIT, "Kt");
        props.setProperty(Constants.ALARM_DEPTH, "6");
        props.setProperty(Constants.SPARKLINE_PTS, "200");
        props.setProperty(Constants.SPARKLINE_MIN, "3");
        props.setProperty(Constants.RADII_BOAT_CIRCLES, "100");
        props.setProperty(Constants.NUM_BOAT_CIRCLES, "3");
        props.setProperty(Constants.WIND_ZERO_OFFSET, "170");
        if (SystemUtils.IS_OS_WINDOWS) {
            props.setProperty(Constants.SERIAL_PORTS, "COM1,COM2,COM3,COM4");
        }
        props.setProperty(Constants.SERIAL_PORT_BAUD, "38400");
        props.setProperty(Constants.DNS_USE_CHOICE, Constants.DNS_USE_BOAT);
        props.setProperty(Constants.ENABLE_COMET, "false");

        props.setProperty(Constants.PREFER_RMC, "true");
        //add default charts

    }

    /**
     * Round to specified decimals
     *
     * @param val
     * @param places
     * @return
     */
    public static double round(double val, int places) {
        double scale = Math.pow(10, places);
        long iVal = Math.round(val * scale);
        return iVal / scale;
    }

    /**
     * Updates and saves the scaling values for instruments
     *
     * @param scaleKey
     * @param amount
     * @param scaleValue
     * @return
     * @throws IOException
     * @throws FileNotFoundException
     */
    public static double updateScale(String scaleKey, double amount, double scaleValue)
            throws FileNotFoundException, IOException {
        scaleValue = scaleValue * amount;
        scaleValue = Util.round(scaleValue, 2);
        //logger.debug(" scale now = "+scale);

        //write out to config
        Util.getConfig(null).setProperty(scaleKey, String.valueOf(scaleValue));
        Util.saveConfig();

        return scaleValue;
    }

    /**
     * Checks if a usb drive is inserted, and returns the root dir. Returns null
     * if its not there
     *
     * @param file
     * @return
     * @throws IOException
     * @throws FileNotFoundException
     */
    public static File getUSBFile() throws FileNotFoundException, IOException {
        File usbDrive = new File(Util.getConfig(null).getProperty(Constants.USBDRIVE));
        if (usbDrive.exists() && usbDrive.list().length > 0) {
            //we return it
            return usbDrive;
        }
        return null;
    }

    /**
     * Attempt to set the system time using the GPS time
     *
     * @param sen
     */
    @SuppressWarnings("deprecation")
    public static void checkTime(RMCSentence sen) {
        if (timeSet) {
            return;
        }
        try {
            sen.getPosition();
        } catch (DataNotAvailableException e) {
            return;
        }
        try {
            net.sf.marineapi.nmea.util.Date dayNow = sen.getDate();
            //if we need to set the time, we will be WAAYYY out
            //we only try once, so we dont get lots of native processes spawning if we fail
            props = getConfig(null);
            if (props.getProperty(Constants.DEMO).equals("false")) {
                if (System.getProperty("os.name").startsWith("Windows")) {
                    // includes: Windows 2000,  Windows 95, Windows 98, Windows NT, Windows Vista, Windows XP
                    // a Win system will already have the time set.
                    return;
                }
            }
            timeSet = true;
            Date date = new Date();
            net.sf.marineapi.nmea.util.Date gpsDate;

            //so we need to set the date and time
            net.sf.marineapi.nmea.util.Time gpsTime = sen.getTime();
            gpsDate = sen.getDate();
            String hh = pad(2, String.valueOf(gpsTime.getHour()));
            String mm = pad(2, String.valueOf(gpsTime.getMinutes()));
            String ss = pad(2, String.valueOf((int) gpsTime.getSeconds()));
            if (logger.isDebugEnabled()) {
                logger.debug("Setting current date to " + dayNow + " " + gpsTime);
            }
            String cmd = "sudo date --utc " + pad(2, String.valueOf(gpsDate.getMonth()))
                    + pad(2, String.valueOf(gpsDate.getDay())) + hh + mm + gpsDate.getYear() + "." + ss;
            System.out.println("Setting date " + cmd);
            // only set the system time if we are not running demo
            if (props.getProperty(Constants.DEMO).equals("false")) {
                Runtime.getRuntime().exec(cmd.split(" "));// MMddhhmm[[yy]yy]
            }
            if (logger.isDebugEnabled()) {
                logger.debug("Executed date setting command:" + cmd);
            }
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
        }

    }

    /**
     * pad the value to i places, eg 2 >> 02
     *
     * @param i
     * @param valueOf
     * @return
     */
    private static String pad(int i, String value) {
        while (value.length() < i) {
            value = "0" + value;
        }
        return value;
    }

}