Skip to content
Seraj Dhaliwal edited this page Nov 17, 2020 · 10 revisions

AISParser

AIS Message Parser (1.0.3)

Why another parser? Reviewed a lot of outstanding parsers and learned a lot; only hang-up was either with conversion or parsing or older/incomplete message implementation. Not all of them had all message implemented, tag-block parsing, with NMEA 0183 TSA/VSI message linking to VDO/VDM was missing. Lots of good code is out there; full reference review is at the bottom of the page.

The focus is on simple maintenance and updates of the core parser.

  • Excel Spread-sheet (MessagePayloadBlockFormatter.xlsx) is provided to show how the blocks were created to create classes in under 10-20 mins.
  • Adding a sub-type message for Type 6 and Type 8 is simple using blocks.
  • Adding / Parsing data blocks follows same methodology as sub-type messages.
  • Adding NMEA 0183 messages is done using dynamic attributes; look at NMESMessageBase.java.

Requirements for AISParser

  • Support messages with Tag-block and non-tag-blocks.
  • Support messages with multiple fragments.
  • Support linking NMEA 0183 TSA/VSI sentence with VDO/VDM messages.
  • Support easy maintenance for updates when message specs are changed.
  • Provide event based notifications for Complete, Error, or Update (ie: TSA/VSI).
  • Support all ITU-R M.1371-5 Messages (1..27)
  • Support all GNSS data reporting formats for Type 17 as per Recommendation ITU-R M.823 (partial; in work)
  • Support all NMEA 0813 sentences (partial; in work)
  • Support command line interface for file processing.
  • Support network interface for multiple base station interface (in testing).
  • Support connection recovery, logging, and auto-rotation for base station interface (in testing).
  • Support live tracking for base station interface (in testing).
  • Support all sub-type for Type 6 & Type 8 messages (partial; in work).
  • Support encoding (planned).

Dependencies

  • ElsuFoundation
  • Commons-codec 1.12

Using the parser to parse file

  • Moved processing to connectors - File and Socket
  • Updated to use app.config vice command line arguments
  • Running parser "java -jar AISParser.jar"
  • Running local sender for socket testing "java -cp ./AISParser.jar elsu.parser.test.sender.SenderServer"

Example config services configuration

  • process.threads is used to configure parse threads; if there is backlog or slow performance increasing it may help - not the thread count should not exceed system processing limitations.
  • sender.xxx params are used by threaded sender to simulate base station sending messages
  • activeList - comma separated list of active connectors
  • file service is used by File Connector to read and parse the file
  • net service is used by Socket Connector to connect to host/port and parse recieved data
<application>
...
	<services>
		<key name="processing.threads">20</key>
		<!--  all/6, debug/5, info/4, warn/3, error/2, fatal/1, none/0 -->
		<key name="processing.debug">none</key>
		<key name="sender.port">31414</key>
		<key name="sender.file">E:\development\opt\nbspace-mars\AISParser\references\test\003669771_20200825.log</key>
		<key name="sender.interval">2</key>
		<activeList>localfile</activeList>
		<service name="localfile">
			<attributes>
				<key name="type">file</key>
				<key name="filename">E:\development\opt\nbspace-mars\AISParser\references\test\003669771_20200825.log</key>
				<key name="interval">2</key>
				<key name="loop">false</key>
			</attributes>
		</service>
		<service name="realtime">
			<attributes>
				<key name="type">net</key>
				<key name="site.host">localhost</key>
				<key name="site.port">31414</key>
				<key name="monitor.noDataTimeout">2500</key>
				<key name="monitor.idleTimeout">5000</key>
				<key name="localStore.mask">%s_%s_%s.txt</key>
				<key name="localStore.directory">services/data/</key>
				<key name="log.rollover.frequency">5</key>
				<key name="log.rollover.periodicity">MINUTE</key>
				<key name="site.name">NAIS</key>
				<key name="site.id">1004</key>
				<!--  raw, matched, none (note logging will affect performance) -->
				<key name="log.type">raw</key>
			</attributes>
		</service>
	</services>
...
</application>

Example parser

package elsu.parser;

import java.util.*;
import java.util.concurrent.TimeUnit;

import elsu.base.IAISEventListener;
import elsu.parser.connector.ConnectorBase;
import elsu.parser.connector.StreamFileConnector;
import elsu.parser.connector.StreamSocketConnector;
import elsu.sentence.SentenceBase;
import elsu.support.*;

public class AISParser implements IAISEventListener {
	public AISParser(ConfigLoader config) throws Exception {
		initialize(config);

		for (ConnectorBase connector : this.connectors) {
			connector.addListener(this);
		}
		
		for (ConnectorBase connector : this.connectors) {
			connector.getWorkerPool().awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS);
		}
	}
	
	private void initialize(ConfigLoader config) throws Exception {
		try {
			String debugLevel = config.getProperty("application.services.key.processing.debug").toString();
			if (debugLevel.equals("debug")) {
				SentenceBase.logLevel = 6;
			} else if (debugLevel.equals("all")) {
				SentenceBase.logLevel = 6;
			} else if (debugLevel.equals("debug")) {
				SentenceBase.logLevel = 5;
			} else if (debugLevel.equals("info")) {
				SentenceBase.logLevel = 4;
			} else if (debugLevel.equals("warn")) {
				SentenceBase.logLevel = 3;
			} else if (debugLevel.equals("error")) {
				SentenceBase.logLevel = 2;
			} else if (debugLevel.equals("fatal")) {
				SentenceBase.logLevel = 1;
			} else if (debugLevel.equals("off")) {
				SentenceBase.logLevel = 0;
			}
		} catch (Exception exi) {
			System.out.println(getClass().getName() + ", initialize(), null, config item application.services.key.processing.debug not defined, debugLeve set to all");
		}
		
		String connectionList = config.getProperty("application.services.activeList").toString();
		String[] connections = connectionList.split(",");
		for (String connection : connections) {
			if (config.getProperty("application.services.service." + connection + ".attributes.key.type").toString().equals("file")) {
				connector = new StreamFileConnector(config, connection, "", 0);
			} else {
				connector = new StreamSocketConnector(config, connection, "", 0, "", "");
			}
			
			connector.start();
			connectors.add(connector);
		}
	}

	@Override
	public void onAISError(Exception ex, Object o, String message) {
		try {
			if (SentenceBase.logLevel >= 2) {
				System.out.println(getClass().toString() + ", " + "onAISError(), " + ex.getMessage() + ", " + o + ", " + message);
			}
		} catch (Exception exi) {
			System.out.println(getClass().toString() + ", " + "onAISError(), " + "message error notification exception, " + exi.getMessage());
		}
	}

	@Override
	public void onAISComplete(Object o) {
		try {
			if (SentenceBase.logLevel >= 4) {
				System.out.println(getClass().toString() + ", " + "onAISComplete(), " + "complete, " + o);
			}
		} catch (Exception exi) {
			System.out.println(getClass().toString() + ", " + "onAISComplete(), " + "message complete notification exception, " + exi.getMessage());
		}
	}

	@Override
	public void onAISUpdate(Object o) {
		try {
			if (SentenceBase.logLevel >= 4) {
				System.out.println(getClass().toString() + ", " + "onAISComplete(), " + "update, " + o);
			}
		} catch (Exception exi) {
			System.out.println(getClass().toString() + ", " + "onAISError(), " + "message error notification exception, " + exi.getMessage());
		}
	}

	public static void main(String[] args) throws Exception {
		ConfigLoader config = null;
		try {
			config = new ConfigLoader("config/app.config", null);
		} catch (Exception ex) {
			throw new Exception("elsu.parser, " + "main(), " + "config resource not found: config/app.config");
		}

		System.out.println("elsu.parser, " + "main(), start, " + (new Date()));

		try {
			AISParser parser = new AISParser(config);
			System.out.println("elsu.parser, " + "main(), complete, " + (new Date()));
		} catch (Exception ex) {
			System.out.println("elsu.parser, " + "main(), unknown, " + ex.getMessage());
		}
	}

	ConnectorBase connector = null;
	private ArrayList<ConnectorBase> connectors = new ArrayList<>();
}

Using the parser with base station connection

  • Use Socket Connector (see app.config realtime service)

Building live tracker for a base station

  • See AISStreams

References

AIS Libraries

Documents

Release notes