/* Copyright (c) 2011 Danish Maritime Authority
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
package dk.dma.ais.packet;
import dk.dma.ais.data.AisClassAStatic;
import dk.dma.ais.data.AisTarget;
import dk.dma.ais.data.AisTargetDimensions;
import dk.dma.ais.data.AisVesselPosition;
import dk.dma.ais.data.AisVesselStatic;
import dk.dma.ais.data.AisVesselTarget;
import dk.dma.ais.message.AisMessage;
import dk.dma.ais.tracker.targetTracker.TargetInfo;
import dk.dma.ais.tracker.targetTracker.TargetTracker;
import dk.dma.enav.model.geometry.Position;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Date;
import java.util.function.Function;
import static org.apache.commons.lang3.StringUtils.isBlank;
/**
* Transform AisPackets into stateful csv objects with only specific columns
*
* @author Thomas Borg Salling
*/
public class AisPacketCSVStatefulOutputSink extends AisPacketCSVOutputSink {
private static final Logger LOG = LoggerFactory.getLogger(AisPacketCSVStatefulOutputSink.class);
private Function<AisMessage, Object> aisMessageObjectFunction;
{
LOG.info("AisPacketCSVStatefulOutputSink created.");
}
private TargetTracker tracker = new TargetTracker();
public AisPacketCSVStatefulOutputSink() {
this("timestamp_pretty;timestamp;targetType;mmsi;msgid;posacc;lat;lon;sog;cog;draught;name;dimBow;dimPort;dimStarboard;dimStern;shipTypeCargoTypeCode;shipType;shipCargo;destination;eta;imo;callsign", ";");
}
public AisPacketCSVStatefulOutputSink(String format) {
this(format, ";");
}
public AisPacketCSVStatefulOutputSink(String format, String separator) {
super(format, separator);
setNullValueForPositionAccuracy(cachedPositionAccuracy);
setNullValueForLatitude(cachedLatitude);
setNullValueForLongitude(cachedLongitude);
setNullValueForSog(cachedSog);
setNullValueForCog(cachedCog);
setNullValueForTrueHeading(cachedTrueHeading);
setNullValueForDraught(cachedDraught);
setNullValueForName(cachedName);
setNullValueForDimBow(cachedDimBow);
setNullValueForDimPort(cachedDimPort);
setNullValueForDimStarboard(cachedDimStarboard);
setNullValueForDimStern(cachedDimStern);
setNullValueForShipTypeCargoTypeCode(cachedShipTypeCargoTypeCode);
setNullValueForShipType(cachedShipType);
setNullValueForShipCargo(cachedShipCargo);
setNullValueForCallsign(cachedCallsign);
setNullValueForImo(cachedImo);
setNullValueForDestination(cachedDestination);
setNullValueForEta(cachedEta);
}
@Override
public void process(OutputStream stream, AisPacket packet, long count) throws IOException {
tracker.update(packet);
if (LOG.isDebugEnabled() && count % 100000 == 0) {
LOG.info("Tracking " + tracker.count(p -> true) + " tracks.");
}
super.process(stream, packet, count);
}
private Object extractFromDynamics(AisMessage m, Function<AisVesselPosition, Object> getter) {
TargetInfo targetInfo = tracker.get(m.getUserId());
if (targetInfo != null) {
AisTarget aisTarget = targetInfo.getAisTarget();
if (aisTarget instanceof AisVesselTarget) {
AisVesselPosition vesselPosition = ((AisVesselTarget) aisTarget).getVesselPosition();
if (vesselPosition != null) {
Object value = getter.apply(vesselPosition);
if (value != null) {
if (value instanceof Double) {
value = ((Double) value).floatValue();
}
return value;
}
}
}
}
return NULL_INDICATOR;
}
private Object extractFromPosition(AisMessage m, Function<Position, Object> getter) {
return extractFromDynamics(m, dynamics -> {
Position position = dynamics.getPos();
return position == null ? NULL_INDICATOR : getter.apply(position);
});
}
private Object extractFromStatic(AisMessage m, Function<AisVesselStatic, Object> getter) {
TargetInfo targetInfo = tracker.get(m.getUserId());
if (targetInfo != null) {
AisTarget aisTarget = targetInfo.getAisTarget();
if (aisTarget instanceof AisVesselTarget && ((AisVesselTarget) aisTarget).getVesselStatic() != null) {
return getter.apply(((AisVesselTarget) aisTarget).getVesselStatic());
}
}
return NULL_INDICATOR;
}
private Object extractFromDimensions(AisMessage m, Function<AisTargetDimensions, Object> getter) {
return extractFromStatic(m, s -> {
AisTargetDimensions dimensions = s.getDimensions();
if (dimensions != null) {
return getter.apply(dimensions);
} else {
return NULL_INDICATOR;
}
});
}
private Function<AisMessage, Object> cachedPositionAccuracy = m -> extractFromDynamics(m, dynamics -> dynamics.getPosAcc());
private Function<AisMessage, Object> cachedLatitude = m -> extractFromPosition(m, position -> position.getLatitude());
private Function<AisMessage, Object> cachedLongitude = m -> extractFromPosition(m, position -> position.getLatitude());
private Function<AisMessage, Object> cachedSog = m -> extractFromDynamics(m, dynamics -> dynamics.getSog());
private Function<AisMessage, Object> cachedCog = m -> extractFromDynamics(m, dynamics -> dynamics.getCog());
private Function<AisMessage, Object> cachedTrueHeading = m -> extractFromDynamics(m, dynamics -> dynamics.getHeading());
private Function<AisMessage, Object> cachedDimBow = m -> extractFromDimensions(m, d -> d.getDimBow());
private Function<AisMessage, Object> cachedDimPort = m -> extractFromDimensions(m, d -> d.getDimPort());
private Function<AisMessage, Object> cachedDimStarboard = m -> extractFromDimensions(m, d -> d.getDimStarboard());
private Function<AisMessage, Object> cachedDimStern = m -> extractFromDimensions(m, d -> d.getDimStern());
private Function<AisMessage, Object> cachedShipTypeCargoTypeCode = m -> extractFromStatic(m, s -> s.getShipTypeCargo().getCode());
private Function<AisMessage, Object> cachedShipType = m -> extractFromStatic(m, s -> s.getShipTypeCargo().getShipType().toString());
private Function<AisMessage, Object> cachedShipCargo = m -> extractFromStatic(m, s -> s.getShipTypeCargo().getShipCargo().toString());
private Function<AisMessage, Object> cachedCallsign = m -> extractFromStatic(m, s -> s.getCallsign());
private Function<AisMessage, Object> cachedName = m -> extractFromStatic(m, s -> {
String name = s.getName();
name = name != null ? name.trim() : null;
return isBlank(name) ? NULL_INDICATOR : name;
});
private Function<AisMessage, Object> cachedDraught = m -> extractFromStatic(m, s -> {
if (s instanceof AisClassAStatic) {
Double draught = ((AisClassAStatic) s).getDraught();
if (draught != null) {
return draught;
}
}
return NULL_INDICATOR;
});
private Function<AisMessage, Object> cachedImo = m -> extractFromStatic(m, s -> {
if (s instanceof AisClassAStatic) {
Integer imo = ((AisClassAStatic) s).getImoNo();
if (imo != null && imo > 0) {
return imo;
}
}
return NULL_INDICATOR;
});
private Function<AisMessage, Object> cachedDestination = m -> extractFromStatic(m, s -> {
if (s instanceof AisClassAStatic) {
String destination = ((AisClassAStatic) s).getDestination();
if (!isBlank(destination)) {
return destination;
}
}
return NULL_INDICATOR;
});
private Function<AisMessage, Object> cachedEta = m -> extractFromStatic(m, s -> {
if (s instanceof AisClassAStatic) {
Date eta = ((AisClassAStatic) s).getEta();
if (eta != null) {
return formatEta(eta);
}
}
return NULL_INDICATOR;
});
}