Skip to content

Commit

Permalink
Merge pull request #32 from HSLdevcom/api-v2-migration
Browse files Browse the repository at this point in the history
Api v2 migration
  • Loading branch information
mjaakko authored Jan 26, 2023
2 parents bc566e8 + c3d1825 commit 1f01b8e
Show file tree
Hide file tree
Showing 5 changed files with 92 additions and 120 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,39 @@

@JsonIgnoreProperties(ignoreUnknown = true)
public class VesselLocation implements VehiclePosition {
public final Integer mmsi;
@JacksonInject("mmsi")
public Integer mmsi;
public final LatLng coordinates;
public final Properties properties;
public final Double speed; //Speed over ground in knots
public final Double course; //Course over ground in degrees from north
public final Integer status; //Status
public final Double rateOfTurn; //Rate of turn
public final Boolean positionAccurate;
public final Boolean raim;
public final Double heading; //Heading of the vessel
public final Long timestamp;

@JsonCreator(mode = JsonCreator.Mode.PROPERTIES)
public VesselLocation(@JsonProperty("mmsi") Integer mmsi,
@JsonProperty("geometry") @JsonDeserialize(using = LatLng.LatLngDeserializer.class) LatLng coordinates,
@JsonProperty("properties") Properties properties) {
this.mmsi = mmsi;
this.coordinates = coordinates;
this.properties = properties;
public VesselLocation(@JsonProperty("sog") Double speed,
@JsonProperty("cog") Double course,
@JsonProperty("navStat") Integer status,
@JsonProperty("rot") Double rateOfTurn,
@JsonProperty("posAcc") Boolean positionAccurate,
@JsonProperty("raim") Boolean raim,
@JsonProperty("heading") Double heading,
@JsonProperty("time") Long timestamp,
@JsonProperty("lat") Double latitude,
@JsonProperty("lon") Double longitude) {
this.speed = speed;
this.course = course;
this.status = status;
this.rateOfTurn = rateOfTurn;
this.positionAccurate = positionAccurate;
this.raim = raim;
this.heading = heading;
this.timestamp = timestamp;

this.coordinates = new LatLng(latitude, longitude);
}

@Override
Expand All @@ -31,20 +53,34 @@ public boolean equals(Object o) {
VesselLocation that = (VesselLocation) o;
return Objects.equals(mmsi, that.mmsi) &&
Objects.equals(coordinates, that.coordinates) &&
Objects.equals(properties, that.properties);
Objects.equals(speed, that.speed) &&
Objects.equals(course, that.course) &&
Objects.equals(status, that.status) &&
Objects.equals(rateOfTurn, that.rateOfTurn) &&
Objects.equals(positionAccurate, that.positionAccurate) &&
Objects.equals(raim, that.raim) &&
Objects.equals(heading, that.heading) &&
Objects.equals(timestamp, that.timestamp);
}

@Override
public int hashCode() {
return Objects.hash(mmsi, coordinates, properties);
return Objects.hash(mmsi, coordinates, speed, course, status, rateOfTurn, positionAccurate, raim, heading, timestamp);
}

@Override
public String toString() {
return "VesselLocation{" +
"mmsi=" + mmsi +
", coordinates=" + coordinates +
", properties=" + properties +
", speed=" + speed +
", course=" + course +
", status=" + status +
", rateOfTurn=" + rateOfTurn +
", positionAccurate=" + positionAccurate +
", raim=" + raim +
", heading=" + heading +
", timestamp=" + timestamp +
'}';
}

Expand All @@ -55,93 +91,22 @@ public String getId() {

@Override
public LatLng getCoordinates() {
return new LatLng(coordinates.getLatitude(), coordinates.getLongitude());
return new LatLng(coordinates.getLatitude(), coordinates.getLongitude()); //TODO: Is this "new" really necessary?
}

@Override
public double getSpeed() {
return SpeedUtils.knotsToMetresPerSecond(properties.speed);
return SpeedUtils.knotsToMetresPerSecond(speed);
}

@Override
public double getHeading() {
//If vessel heading is not available (special value 511), use vessel course for heading
return Math.round(properties.heading) == 511 ? properties.course : properties.heading;
return Math.round(heading) == 511 ? course : heading;
}

@Override
public long getTimestamp() {
return properties.timestamp;
}

@JsonIgnoreProperties(value = { "timestamp" }, ignoreUnknown = true)
public static class Properties {
//Speed over ground in knots
public final Double speed;
//Course over ground in degrees from north
public final Double course;
//Status
public final Integer status;
//Rate of turn
public final Double rateOfTurn;
public final Boolean positionAccurate;
public final Boolean raim;
//Heading of the vessel
public final Double heading;
//Timestamp in milliseconds
public final Long timestamp;

@JsonCreator(mode = JsonCreator.Mode.PROPERTIES)
public Properties(@JsonProperty("sog") Double speed,
@JsonProperty("cog") Double course,
@JsonProperty("navStat") Integer status,
@JsonProperty("rot") Double rateOfTurn,
@JsonProperty("posAcc") Boolean positionAccurate,
@JsonProperty("raim") Boolean raim,
@JsonProperty("heading") Double heading,
@JsonProperty("timestampExternal") Long timestamp) {
this.speed = speed;
this.course = course;
this.status = status;
this.rateOfTurn = rateOfTurn;
this.positionAccurate = positionAccurate;
this.raim = raim;
this.heading = heading;
this.timestamp = timestamp;
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Properties that = (Properties) o;
return Objects.equals(speed, that.speed) &&
Objects.equals(course, that.course) &&
Objects.equals(status, that.status) &&
Objects.equals(rateOfTurn, that.rateOfTurn) &&
Objects.equals(positionAccurate, that.positionAccurate) &&
Objects.equals(raim, that.raim) &&
Objects.equals(heading, that.heading) &&
Objects.equals(timestamp, that.timestamp);
}

@Override
public int hashCode() {
return Objects.hash(speed, course, status, rateOfTurn, positionAccurate, raim, heading, timestamp);
}

@Override
public String toString() {
return "Properties{" +
"speed=" + speed +
", course=" + course +
", status=" + status +
", rateOfTurn=" + rateOfTurn +
", positionAccurate=" + positionAccurate +
", raim=" + raim +
", heading=" + heading +
", timestamp=" + timestamp +
'}';
}
return timestamp * 1000;
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package fi.hsl.suomenlinna_hfp.digitraffic.model;

import com.fasterxml.jackson.annotation.JacksonInject;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
Expand All @@ -9,7 +10,10 @@

@JsonIgnoreProperties(ignoreUnknown = true)
public class VesselMetadata implements VehicleMetadata {
public final Integer mmsi;

@JacksonInject("mmsi")
public Integer mmsi;

public final String name;
public final Integer shipType;
public final Integer referencePointA;
Expand All @@ -25,21 +29,20 @@ public class VesselMetadata implements VehicleMetadata {
public final String destination;

@JsonCreator(mode = JsonCreator.Mode.PROPERTIES)
public VesselMetadata(@JsonProperty("mmsi") Integer mmsi,
@JsonProperty("name") String name,
@JsonProperty("shipType") Integer shipType,
@JsonProperty("referencePointA") Integer referencePointA,
@JsonProperty("referencePointB") Integer referencePointB,
@JsonProperty("referencePointC") Integer referencePointC,
@JsonProperty("referencePointD") Integer referencePointD,
public VesselMetadata(@JsonProperty("name") String name,
@JsonProperty("type") Integer shipType,
@JsonProperty("refA") Integer referencePointA,
@JsonProperty("refB") Integer referencePointB,
@JsonProperty("refC") Integer referencePointC,
@JsonProperty("refD") Integer referencePointD,
@JsonProperty("posType") Integer positionType,
@JsonProperty("draught") Integer draught,
@JsonProperty("imo") Integer imo,
@JsonProperty("callSign") String callSign,
@JsonProperty("eta") Long eta,
@JsonProperty("timestamp") Long timestamp,
@JsonProperty("destination") String destination) {
this.mmsi = mmsi;
this.mmsi = null; //TODO: Generate this.
this.name = name;
this.shipType = shipType;
this.referencePointA = referencePointA;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,48 +9,34 @@

@JsonIgnoreProperties(ignoreUnknown = true)
public class VesselsStatus {
public final int readErrors;
public final int sentErrors;
public final ZonedDateTime updateTime;
public final String status;

@JsonCreator(mode = JsonCreator.Mode.PROPERTIES)
public VesselsStatus(@JsonProperty("readErrors") int readErrors,
@JsonProperty("sentErrors") int sentErrors,
@JsonProperty("updateTime") ZonedDateTime updateTime,
@JsonProperty("status") String status) {
this.readErrors = readErrors;
this.sentErrors = sentErrors;
public VesselsStatus(@JsonProperty("updated") ZonedDateTime updateTime) {
this.updateTime = updateTime;
this.status = status;
}

public boolean everythingOk() {
return 0 == readErrors &&
0 == sentErrors &&
"CONNECTED".equals(status);
return updateTime != null;
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
VesselsStatus that = (VesselsStatus) o;
return readErrors == that.readErrors && sentErrors == that.sentErrors && Objects.equals(updateTime, that.updateTime) && Objects.equals(status, that.status);
return Objects.equals(updateTime, that.updateTime);
}

@Override
public int hashCode() {
return Objects.hash(readErrors, sentErrors, updateTime, status);
return updateTime != null ? updateTime.hashCode() : 0;
}

@Override
public String toString() {
return "VesselsStatus{" +
"readErrors=" + readErrors +
", sentErrors=" + sentErrors +
", updateTime=" + updateTime +
", status='" + status + '\'' +
"updateTime=" + updateTime +
'}';
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package fi.hsl.suomenlinna_hfp.digitraffic.provider;

import com.fasterxml.jackson.databind.InjectableValues;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import fi.hsl.suomenlinna_hfp.digitraffic.model.VesselLocation;
Expand All @@ -14,6 +15,8 @@
import java.util.Collection;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Consumer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class MqttVesselLocationProvider extends VesselLocationProvider {
private static final Logger LOG = LoggerFactory.getLogger(MqttVesselLocationProvider.class);
Expand Down Expand Up @@ -70,13 +73,13 @@ public void start(Consumer<VesselLocation> locationConsumer, Consumer<VesselMeta
public void connectComplete(boolean reconnect, String serverURI) {
LOG.info("Connected to {} (reconnect: {})", brokerUri, reconnect);

String[] topics = mmsis.stream().map(mmsi -> "vessels/" + mmsi + "/+/#").toArray(String[]::new);
String[] topics = mmsis.stream().map(mmsi -> "vessels-v2/" + mmsi + "/+/#").toArray(String[]::new);
int[] qos = new int[topics.length];
Arrays.fill(qos, 0);

try {
mqttAsyncClient.subscribe(topics, qos).waitForCompletion();
mqttAsyncClient.subscribe("vessels/status", 0).waitForCompletion();
mqttAsyncClient.subscribe("vessels-v2/status", 0).waitForCompletion();
} catch (MqttException e) {
LOG.error("Failed to subscribe MQTT topics {} with QoS {}", topics, qos);
onConnectionFailed.accept(e);
Expand All @@ -94,16 +97,24 @@ public void connectionLost(Throwable cause) {
public void messageArrived(String topic, MqttMessage message) {
if (topic.contains("metadata")) {
try {
injectMmsi(topic);
VesselMetadata vesselMetadata = objectMapper.readValue(message.getPayload(), VesselMetadata.class);
metadataConsumer.accept(objectMapper.readValue(message.getPayload(), VesselMetadata.class));
} catch (IOException e) {
LOG.warn("Failed to parse vessel metadata", e);
} finally {
objectMapper.setInjectableValues(new InjectableValues.Std());
}
}
if (topic.contains("locations")) {
if (topic.contains("location")) {
try {
locationConsumer.accept(objectMapper.readValue(message.getPayload(), VesselLocation.class));
injectMmsi(topic);
VesselLocation vesselLocation = objectMapper.readValue(message.getPayload(), VesselLocation.class);
locationConsumer.accept(vesselLocation);
} catch (IOException e) {
LOG.warn("Failed to parse vessel location", e);
} finally {
objectMapper.setInjectableValues(new InjectableValues.Std());
}
}
if (topic.contains("status")) {
Expand Down Expand Up @@ -135,6 +146,13 @@ public void onFailure(IMqttToken asyncActionToken, Throwable exception) {
});
}

private void injectMmsi(String topic) {
String mmsi = topic.split("/")[1];
InjectableValues injectableValues = new InjectableValues.Std().addValue("mmsi", Integer.parseInt(mmsi));
objectMapper.setInjectableValues(injectableValues);

}

@Override
public long getLastReceivedTime() {
return lastReceivedTime.get();
Expand Down
4 changes: 2 additions & 2 deletions src/main/java/fi/hsl/suomenlinna_hfp/hfp/utils/HfpUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
public class HfpUtils {
private static final DateTimeFormatter TST_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSX");

public static String formatTst(long timestampMillis) {
return TST_FORMATTER.format(Instant.ofEpochMilli(timestampMillis).atZone(ZoneId.of("UTC")));
public static String formatTst(long timeStampMillis) {
return TST_FORMATTER.format(Instant.ofEpochMilli(timeStampMillis).atZone(ZoneId.of("UTC")));
}

public static String formatStartTime(int seconds) {
Expand Down

0 comments on commit 1f01b8e

Please sign in to comment.