nz.co.fortytwo.signalk.handler.AISHandler.java Source code

Java tutorial

Introduction

Here is the source code for nz.co.fortytwo.signalk.handler.AISHandler.java

Source

/*
 * 
 * Copyright (C) 2012-2014 R T Huitema. All Rights Reserved.
 * Web: www.42.co.nz
 * Email: robert@42.co.nz
 * Author: R T Huitema
 * 
 * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
 * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 * 
 * 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 nz.co.fortytwo.signalk.handler;

import static nz.co.fortytwo.signalk.util.SignalKConstants.communication_callsignVhf;
import static nz.co.fortytwo.signalk.util.SignalKConstants.dot;
import static nz.co.fortytwo.signalk.util.SignalKConstants.mmsi;
import static nz.co.fortytwo.signalk.util.SignalKConstants.name;
import static nz.co.fortytwo.signalk.util.SignalKConstants.nav_courseOverGroundTrue;
import static nz.co.fortytwo.signalk.util.SignalKConstants.nav_headingTrue;
import static nz.co.fortytwo.signalk.util.SignalKConstants.nav_position;
import static nz.co.fortytwo.signalk.util.SignalKConstants.nav_position_latitude;
import static nz.co.fortytwo.signalk.util.SignalKConstants.nav_position_longitude;
import static nz.co.fortytwo.signalk.util.SignalKConstants.nav_speedOverGround;
import static nz.co.fortytwo.signalk.util.SignalKConstants.nav_state;
import static nz.co.fortytwo.signalk.util.SignalKConstants.source;
import static nz.co.fortytwo.signalk.util.SignalKConstants.timestamp;
import static nz.co.fortytwo.signalk.util.SignalKConstants.value;
import static nz.co.fortytwo.signalk.util.SignalKConstants.vessels;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import nz.co.fortytwo.signalk.ais.AisVesselInfo;
import nz.co.fortytwo.signalk.model.SignalKModel;
import nz.co.fortytwo.signalk.model.impl.SignalKModelFactory;
import nz.co.fortytwo.signalk.util.Util;

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

import dk.dma.ais.message.AisMessage;
import dk.dma.ais.message.AisMessage18;
import dk.dma.ais.message.AisPositionMessage;
import dk.dma.ais.message.AisStaticCommon;
import dk.dma.ais.packet.AisPacket;
import dk.dma.ais.packet.AisPacketParser;
import dk.dma.ais.sentence.Abk;
import dk.dma.ais.sentence.SentenceException;

/**
 * Accepts AIVDM messages and translates the VDMs into AisMessages and sends the AisPositionMessages on to the browser.
 * Mostly we need 1,2,3,5, 18,19
 * 
 * @author robert
 * 
 */
public class AISHandler {

    private static Logger logger = Logger.getLogger(AISHandler.class);

    /**
     * Reader to parse lines and deliver complete AIS packets.
     * Updates them into model, and removes the key from the map.
     */
    private AisPacketParser packetParser = new AisPacketParser();

    private Map<Integer, String> navStatusMap = new HashMap<>();

    public AISHandler() {
        navStatusMap.put(0, "Under way using engine");
        navStatusMap.put(1, "At anchor");
        navStatusMap.put(2, "Not under command");
        navStatusMap.put(3, "Restricted manoeuverability");
        navStatusMap.put(4, "Constrained by her draught");
        navStatusMap.put(5, "Moored");
        navStatusMap.put(6, "Aground");
        navStatusMap.put(7, "Engaged in Fishing");
        navStatusMap.put(8, "Under way sailing");
        navStatusMap.put(9, "Reserved for future amendment of Navigational Status for HSC");
        navStatusMap.put(10, "Reserved for future amendment of Navigational Status for WIG");
        navStatusMap.put(11, "Reserved for future use");
        navStatusMap.put(12, "Reserved for future use");
        navStatusMap.put(13, "Reserved for future use");
        navStatusMap.put(14, "Reserved for future use");
        navStatusMap.put(15, "Not defined (default)");
    }

    // https://github.com/dma-ais/AisLib

    /**
     * Converts an AIS NMEA string to a signalK JSON object
     * See https://github.com/dma-ais/AisLib
     * 
     * HD-SF. Free raw AIS data feed for non-commercial use.
     * hd-sf.com:9009
     * 
     * @param bodyStr
     * @return
     * @throws Exception 
     */
    public SignalKModel handle(String bodyStr) throws Exception {
        return handle(bodyStr, null);
    }

    /**
     * Converts an AIS NMEA string to a signalK JSON object
     * See https://github.com/dma-ais/AisLib
     * 
     * HD-SF. Free raw AIS data feed for non-commercial use.
     * hd-sf.com:9009
     * 
     * @param bodyStr
     * @param device - the serial or other device the data was recieved over.
     * @return
     * @throws Exception 
     */
    public SignalKModel handle(String bodyStr, String device) throws Exception {

        if (logger.isDebugEnabled())
            logger.debug("Processing AIS:" + bodyStr);

        if (StringUtils.isBlank(bodyStr) || !bodyStr.startsWith("!AIVDM")) {
            return null;
        }

        try {
            List<AisPacket> packets = handleLine(bodyStr);
            AisVesselInfo vInfo = null;
            SignalKModel model = SignalKModelFactory.getCleanInstance();
            for (AisPacket packet : packets) {
                if (packet != null && packet.isValidMessage()) {
                    // process message here
                    AisMessage message = packet.getAisMessage();
                    if (logger.isDebugEnabled())
                        logger.debug("AisMessage:" + message.getClass() + ":" + message.toString());

                    // 1,2,3
                    if (message instanceof AisPositionMessage) {
                        vInfo = new AisVesselInfo((AisPositionMessage) message);
                    }
                    // 5,19,24
                    if (message instanceof AisStaticCommon) {
                        vInfo = new AisVesselInfo((AisStaticCommon) message);
                    }
                    if (message instanceof AisMessage18) {
                        vInfo = new AisVesselInfo((AisMessage18) message);
                    }
                    if (vInfo != null) {
                        if (StringUtils.isBlank(device))
                            device = "unknown";
                        String ts = Util.getIsoTimeString(packet.getBestTimestamp());
                        String aisVessel = vessels + dot + String.valueOf(vInfo.getUserId()) + dot;
                        String sourceRef = aisVessel + "sources.ais";
                        //create ais source entry
                        model.put(sourceRef + dot + value, packet.getStringMessage());
                        model.put(sourceRef + dot + timestamp, ts);
                        model.put(sourceRef + dot + source, device);

                        model.put(aisVessel + name, vInfo.getName());
                        model.put(aisVessel + mmsi, String.valueOf(vInfo.getUserId()), sourceRef, ts);
                        model.put(aisVessel + nav_state, navStatusMap.get(vInfo.getNavStatus()), sourceRef, ts);
                        if (vInfo.getPosition() != null) {
                            model.put(aisVessel + nav_position + dot + timestamp, ts);
                            model.put(aisVessel + nav_position + dot + source, sourceRef);
                            model.put(aisVessel + nav_position_latitude, vInfo.getPosition().getLatitude());
                            model.put(aisVessel + nav_position_longitude, vInfo.getPosition().getLongitude());
                        }
                        model.put(aisVessel + nav_courseOverGroundTrue,
                                Math.toRadians(((double) vInfo.getCog()) / 10), sourceRef, ts);
                        model.put(aisVessel + nav_speedOverGround, Util.kntToMs(((double) vInfo.getSog()) / 10),
                                sourceRef, ts);
                        model.put(aisVessel + nav_headingTrue,
                                Math.toRadians(((double) vInfo.getTrueHeading()) / 10), sourceRef);
                        if (vInfo.getCallsign() != null)
                            model.put(aisVessel + communication_callsignVhf, vInfo.getCallsign(), sourceRef, ts);
                    }
                }
            }
            return model;

        } catch (Exception e) {
            logger.debug(e.getMessage(), e);
            logger.error(e.getMessage() + " : " + bodyStr);
            throw e;
        }

    }

    /**
     * Handle a String message, returning a list of AisPackets
     * 
     * @param line
     * @return
     */
    public List<AisPacket> handleLine(String messageString) throws IOException {
        if (logger.isDebugEnabled())
            logger.debug("AIS Received : " + messageString);
        // Check for ABK
        if (Abk.isAbk(messageString)) {
            if (logger.isDebugEnabled())
                logger.debug("AIS Received ABK: " + messageString);
            return null;
        }

        try {
            List<AisPacket> packets = new ArrayList<AisPacket>();
            String[] lines = messageString.split("\\r?\\n");

            for (String line : lines) {
                packets.add(packetParser.readLine(line));
            }
            return packets;
        } catch (SentenceException se) {
            if (logger.isDebugEnabled())
                logger.info("AIS Sentence error: " + se.getMessage() + " line: " + messageString);
            throw new IOException(se);

        }
    }

}