/* Copyright (c) 2011 Danish Maritime Authority.
*
* 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 dk.dma.ais.sentence;
import dk.dma.ais.binary.SixbitEncoder;
import dk.dma.ais.binary.SixbitException;
import dk.dma.ais.message.AisMessage;
/**
* Class to represent VDM and VDO sentences as defined by IEC 61162
*/
public class Vdm extends EncapsulatedSentence {
/**
* The number of six bit characters to allow in each message part. Number based on the maximum size of resulting sentence part
* which may not exceed 80 characters.
*/
private static final int DATA_SENTENCE_MAX_LENGTH = 61;
/**
* Determines is this is VDM or VDO
*/
private boolean ownMessage;
/**
* Helper method parsing line to SentenceLine and passing to parse
*
* @param line
* @return
* @throws SentenceException
*/
public int parse(String line) throws SentenceException {
return parse(new SentenceLine(line));
}
/**
* Implemented parse method. See {@link EncapsulatedSentence}
*/
@Override
public int parse(SentenceLine sl) throws SentenceException {
// Do common parsing
super.baseParse(sl);
// Check VDM / VDO
if (sl.isFormatter("VDO")) {
this.ownMessage = true;
} else {
if (!sl.isFormatter("VDM")) {
throw new SentenceException("Not VDM or VDO sentence");
}
}
// Check that there at least 8 fields
if (sl.getFields().size() < 8) {
throw new SentenceException("Sentence does not have at least 8 fields");
}
// Channel, relaxed may be null
if (sl.getFields().get(4).length() > 0) {
this.channel = sl.getFields().get(4).charAt(0);
} else {
this.channel = 0;
}
// Padding bits
int padBits = Sentence.parseInt(sl.getFields().get(6));
// Six bit field
this.sixbitString.append(sl.getFields().get(5));
try {
binArray.appendSixbit(sl.getFields().get(5), padBits);
} catch (SixbitException e) {
throw new SentenceException("Invalid sixbit in VDM: " + e.getMessage() + ": " + sl.getLine());
}
// Complete packet?
if (completePacket) {
try {
this.msgId = (int) binArray.getVal(6);
} catch (SixbitException e) {
throw new SentenceException("Not enough bits for msgid");
}
return 0;
}
return 1;
}
/**
* Determine if line seems to contain VDM or VDO sentence
*
* @param line
* @return
*/
public static boolean isVdm(String line) {
int sentenceStart = line.indexOf('!');
if (sentenceStart < 0) {
return false;
}
int formatterStart = line.indexOf("VDM");
if (formatterStart < 0) {
formatterStart = line.indexOf("VDO");
}
return formatterStart == sentenceStart + 3;
}
/**
* Determine if VDO instead of VDM
*
* @return
*/
public boolean isOwnMessage() {
return ownMessage;
}
/**
* Get encoded sentence as a single line
*/
@Override
public String getEncoded() {
formatter = isOwnMessage() ? "VDO" : "VDM";
super.encode();
return finalEncode();
}
/**
* Make max 80 chars length sentences from AIS message given sequence number
*
* If all VDM fields are used, 61 chars are left for encoded AIS message
*
* @param aisMessage
* @param sequence
* @return array of sentence parts
* @throws IllegalArgumentException
* @throws SixbitException
*/
public static String[] createSentences(AisMessage aisMessage, int sequence) throws SixbitException {
// Encode the AIS message to get full string
SixbitEncoder encoder = aisMessage.getEncoded();
String encoded = encoder.encode();
int padBits = encoder.getPadBits();
// Number of sentences necessary
int sentenceCount = encoded.length() / DATA_SENTENCE_MAX_LENGTH + 1;
String[] sentences = new String[sentenceCount];
// Split the string
for (int i = 0; i < sentenceCount; i++) {
int start = i * DATA_SENTENCE_MAX_LENGTH;
int end;
int partPadBits = 0;
if (i < sentenceCount - 1) {
end = start + DATA_SENTENCE_MAX_LENGTH;
} else {
end = encoded.length();
partPadBits = padBits;
}
String partEncoded = encoded.substring(start, end);
Vdm vdm = new Vdm();
vdm.setMsgId(aisMessage.getMsgId());
vdm.setTalker("AI");
vdm.setTotal(sentenceCount);
vdm.setNum(i + 1);
vdm.setSequence(sequence);
vdm.setSixbitString(partEncoded);
vdm.setPadBits(partPadBits);
sentences[i] = vdm.getEncoded();
}
return sentences;
}
/**
* Split single VDM into possible multiple VDM's to adherne to the 80 character max
*
* @return
*/
public Vdm[] createSentences() {
// Make sure there is a sequence number
if (sequence == null) {
sequence = 0;
}
int sentenceCount = sixbitString.length() / DATA_SENTENCE_MAX_LENGTH + 1;
Vdm[] sentences = new Vdm[sentenceCount];
// Split the string
for (int i = 0; i < sentenceCount; i++) {
int start = i * DATA_SENTENCE_MAX_LENGTH;
int end;
int partPadBits = 0;
if (i < sentenceCount - 1) {
end = start + DATA_SENTENCE_MAX_LENGTH;
} else {
end = sixbitString.length();
partPadBits = padBits;
}
String partEncoded = sixbitString.substring(start, end);
Vdm vdm = new Vdm();
vdm.setMsgId(msgId);
vdm.setTalker("AI");
vdm.setTotal(sentenceCount);
vdm.setNum(i + 1);
vdm.setSequence(sequence);
vdm.setSixbitString(partEncoded);
vdm.setPadBits(partPadBits);
vdm.setChannel(channel);
sentences[i] = vdm;
}
return sentences;
}
}