go-ais icon indicating copy to clipboard operation
go-ais copied to clipboard

Handle multipart messages

Open VictorNine opened this issue 4 years ago • 9 comments

Looks like this package is having issues with multi part messages. This test can be added to nmea_test.go to show the issue:

func TestMultiMessage(t *testing.T) {
	nm := NMEACodecNew(ais.CodecNew(false, false))
	_, err := nm.ParseSentence("!AIVDM,2,1,0,B,539S:k40000000c3G04PPh63<00000000080000o1PVG2uGD:00000000000,0*34\n!AIVDM,2,2,0,B,00000000000,2*27")

	if err != nil {
		t.Error(err)
		t.Error("Can't handle multipart message")
	}
}

VictorNine avatar Jan 03 '21 15:01 VictorNine

@VictorNine ParseSentence parses one sentence at a time. To read a multi-sentence message, call it on each sentence, checking both for errors and whether the first return value is nil.

This test passes:

func TestMultiMessage(t *testing.T) {
	nm := NMEACodecNew(ais.CodecNew(false, false))
	_, err := nm.ParseSentence("!AIVDM,2,1,0,B,539S:k40000000c3G04PPh63<00000000080000o1PVG2uGD:00000000000,0*34")
	_, err = nm.ParseSentence("!AIVDM,2,2,0,B,00000000000,2*27")

	if err != nil {
		t.Error(err)
		t.Error("Can't handle multipart message")
	}
}

simeonmiteff avatar Jan 04 '21 03:01 simeonmiteff

That's true, but it does not read anything without both messages. Or am I missing something?


func TestMultiMessage(t *testing.T) {
	nm := NMEACodecNew(ais.CodecNew(false, false))
	msg, err := nm.ParseSentence("!AIVDM,2,1,0,B,539S:k40000000c3G04PPh63<00000000080000o1PVG2uGD:00000000000,0*34")

	if err != nil {
		t.Error(err)
		t.Error("Can't handle multipart message")
	}

	if msg == nil {
		t.Error("Can't handle multipart message")
	}
}

VictorNine avatar Jan 05 '21 18:01 VictorNine

Hi, I'm also interested in how to properly read multipart messages. @simeonmiteff could you let me know whether the below code looks sensible?

package internal

import (
    "testing"
    
    "github.com/BertoldVdb/go-ais"
    "github.com/BertoldVdb/go-ais/aisnmea"
)

func TestProperUseOfParseSentence(t *testing.T) {
    // given some (multipart) messages
    messages := []string{
        `\s:2573210,c:1610637868*0E\!BSVDM,2,1,9,B,55UmS>81VSWtcQPSJ00hth74000000000000001J58>544GS08R0EAiDmCQ0,0*15`,
        `\s:2573210,c:1610637868*0E\!BSVDM,2,2,9,B,00000000000,2*37`,
        `\s:2573205,c:1610637868*0A\!BSVDM,2,1,6,A,53n0LR42BoKthAMN221<d4p@V0l58uV2222222162Pv894G30=E0CPCQiD` + "`" + `8,0*57`,
        `\s:2573205,c:1610637868*0A\!BSVDM,2,2,6,A,88888888880,2*3B`,
        `\s:2573445,c:1610637868*08\!BSVDM,2,1,0,B,53mro7400000hhm0000P5=@TpL0000000000000N1p63340Ht:8888888888,0*5C`,
        `\s:2573445,c:1610637868*08\!BSVDM,2,2,0,B,88888888880,2*3E`,
    }
    
    // we'd like to parse messages into this slice
    decodedMessages := make([]aisnmea.VdmPacket, 0)
    
    nm := aisnmea.NMEACodecNew(ais.CodecNew(false, false))
    
    for _, message := range messages {
        decodedMessage, err := nm.ParseSentence(message)
        
        if err != nil {
            t.Error("Oops! Something went wrong while parsing:", err)
        }
        
        if decodedMessage == nil {
            // Nothing to do here, message we passed was probably a fragment of
            // a multipart message, and happened to not be the last fragment.
            // This is fine (?) 
            continue
        }
        
        decodedMessages = append(decodedMessages, *decodedMessage)
    }
    
    // Perform some basic checks against what we got back
    
    if len(decodedMessages) != 3 {
        t.Error("Incorrect number of parsed messages")
    }
    
    if decodedMessages[0].TagBlock.Time != 1610637868 {
        t.Error("Incorrect tagblock time for first message")
    }
}

tholok97 avatar Jan 14 '21 15:01 tholok97

The message says how many parts there are. !BSVDM,2,1 would mean first of 2. But the easiest is probably that the package receives the two messages with a \n between?

VictorNine avatar Jan 15 '21 11:01 VictorNine

The message says how many parts there are. !BSVDM,2,1 would mean first of 2. But the easiest is probably that the package receives the two messages with a \n between?

Hi @VictorNine. I'm curious: Why is parsing the individual parts one by one and expecting return values of nil when the parser has incomplete info about a multipart message not sufficient? I might use this lib, and your previuos comment makes it seem like it's a bad thing, but I don't understand why. It would be of great help if you could mention why the example in your comment (& the example I posted above) is not OK.

tholok97 avatar Jan 15 '21 11:01 tholok97

Basically there is no way to parse multi part messages now (as far as I know).

VictorNine avatar Jan 15 '21 16:01 VictorNine

Hello,

You should be able to pass the multipart messages one by one to ParseSentence. You will get a non-nil result when a message is completely decoded. If you supply only one part the message does not get decoded as it is not complete. You don't need to send the messages in order.

I will verify this in a few hours.

Sincerely, Bertold

BertoldVdb avatar Jan 15 '21 16:01 BertoldVdb

What's the status on this? Using this clip of live AIS data, fed to the decoder line by line, I get a nil response to both parts of the fragmented sentence, all others decode fine.

!BSVDM,1,1,,B,13mqtQ70001FmVlWnt37LFuP0<1F,0*3D
!BSVDM,1,1,,B,13nELB?P000qjJpUij:nBgwL05cH,0*57
!BSVDM,2,1,5,A,53cA4F02Ae>e0<eR221=@u8pE>2222222222221JC0G4F4RT0BTQDlk1CC@C,0*1A
!BSVDM,2,2,5,A,`8888888880,2*60
!BSVDM,1,1,,B,13nG3k001n0UC2lT59g35RQJ00S2,0*79
!BSVDM,1,1,,B,13mB7d0000Pr;EnVed9JjrWN0<0C,0*2A

EWersel avatar May 20 '22 18:05 EWersel

If you are feeding the lines one-by-one you should indeed get one result for the two-part message. If this does not happen you might have found a bug, I will investigate sometime next week!

BertoldVdb avatar May 20 '22 22:05 BertoldVdb