-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Signed-off-by: Christian Ege <[email protected]>
- Loading branch information
Showing
3 changed files
with
181 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
package pcic | ||
|
||
import "github.com/graugans/go-ovp8xx/pkg/chunk" | ||
|
||
type Frame struct { | ||
Chunks []chunk.ChunkData | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
package pcic | ||
|
||
import ( | ||
"bytes" | ||
"errors" | ||
"fmt" | ||
"io" | ||
|
||
"github.com/graugans/go-ovp8xx/pkg/chunk" | ||
) | ||
|
||
type PCIC struct { | ||
} | ||
|
||
const ( | ||
headerSize int = 20 | ||
minimumContentLength int = 6 | ||
ticketFieldLength int = 4 | ||
lengthFieldLength int = 10 | ||
delimiterFieldLength int = 2 | ||
) | ||
|
||
const ( | ||
firstTicketOffset int = 0 | ||
lengthOffset int = 4 | ||
secondTicketOffset int = 16 | ||
delimiterOffset int = 14 | ||
dataOffset int = 20 | ||
) | ||
|
||
const ( | ||
startMarker string = "star" | ||
endMarker string = "stop" | ||
) | ||
|
||
func (p *PCIC) Receive(reader io.Reader) (Frame, error) { | ||
frame := Frame{} | ||
header := make([]byte, headerSize) | ||
n, err := io.ReadFull(reader, header) | ||
if err != nil { | ||
return frame, err | ||
} | ||
if n < headerSize { | ||
return frame, fmt.Errorf("not enough data received: %d", n) | ||
} | ||
firstTicket := header[:ticketFieldLength] | ||
secondTicket := header[secondTicketOffset:dataOffset] | ||
if !bytes.Equal(firstTicket, secondTicket) { | ||
return frame, fmt.Errorf("mismatch in the tickets %s != %s ", | ||
string(firstTicket), | ||
string(secondTicket), | ||
) | ||
} | ||
lengthBuffer := string(header[lengthOffset:secondTicketOffset]) | ||
if lengthBuffer[0] != 'L' { | ||
return frame, fmt.Errorf("the length field does not start with 'L': %v", lengthBuffer) | ||
} | ||
length := 0 | ||
n, err = fmt.Sscanf(lengthBuffer, "L%09d\r\n", &length) | ||
if err != nil { | ||
return frame, err | ||
} | ||
if n != 1 { | ||
return frame, errors.New("no length in the length field detected") | ||
} | ||
if length < minimumContentLength { | ||
return frame, errors.New("the length information is too short") | ||
} | ||
data := make([]byte, length-ticketFieldLength) | ||
if _, err = io.ReadFull(reader, data); err != nil { | ||
return frame, err | ||
} | ||
trailer := data[len(data)-delimiterFieldLength:] | ||
if !bytes.Equal(trailer, []byte{'\r', '\n'}) { | ||
return frame, errors.New("invalid trailer detected") | ||
} | ||
contentDecorated := data[:len(data)-delimiterFieldLength] | ||
if len(startMarker)+len(endMarker) > len(contentDecorated) { | ||
return frame, fmt.Errorf("missing start (%s) and end markers (%s) buffer length: %d", | ||
startMarker, | ||
endMarker, | ||
len(contentDecorated), | ||
) | ||
} | ||
content := contentDecorated[len(endMarker) : len(contentDecorated)-len(endMarker)] | ||
if len(content) == 0 { | ||
// no content is available | ||
return frame, nil | ||
} | ||
remainingBytes := len(content) | ||
offset := 0 | ||
for remainingBytes > 0 { | ||
c := chunk.ChunkData{} | ||
if err := c.Parse(content[offset:]); err != nil { | ||
return frame, err | ||
} | ||
frame.Chunks = append(frame.Chunks, c) | ||
offset += c.Size() | ||
remainingBytes -= c.Size() | ||
} | ||
return frame, err | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
package pcic_test | ||
|
||
import ( | ||
"fmt" | ||
"strings" | ||
"testing" | ||
|
||
"github.com/graugans/go-ovp8xx/pkg/chunk" | ||
"github.com/graugans/go-ovp8xx/pkg/pcic" | ||
"github.com/stretchr/testify/assert" | ||
) | ||
|
||
const miniMalContentLength int = 14 | ||
|
||
func TestMinimalReceive(t *testing.T) { | ||
r := strings.NewReader("Hello, Reader!") | ||
p := pcic.PCIC{} | ||
_, err := p.Receive(r) | ||
assert.Error(t, err, "We expect an error while receiving malformed data") | ||
|
||
// Test the minimal possible PCIC message | ||
r = strings.NewReader("0001L000000014\r\n0001starstop\r\n") | ||
_, err = p.Receive(r) | ||
assert.NoError(t, err, "We expect no error while receiving data") | ||
} | ||
|
||
func TestReceiveWithChunk(t *testing.T) { | ||
c := chunk.ChunkData{} | ||
chunkData := []byte{ | ||
0x69, 0x00, 0x00, 0x00, /* CHUNK_TYPE */ | ||
0x34, 0x00, 0x00, 0x00, /* CHUNK_SIZE */ | ||
0x30, 0x00, 0x00, 0x00, /* HEADER_SIZE */ | ||
0x02, 0x00, 0x00, 0x00, /* HEADER_VERSION */ | ||
0x04, 0x00, 0x00, 0x00, /* IMAGE_WIDTH */ | ||
0x01, 0x00, 0x00, 0x00, /* IMAGE_HEIGTH */ | ||
0x00, 0x00, 0x00, 0x00, /* DATA_FORMAT */ | ||
0x00, 0x00, 0x00, 0x00, /* TIME_STAMP */ | ||
0x00, 0x00, 0x00, 0x00, /* FRAME_COUNT */ | ||
0x00, 0x00, 0x00, 0x00, /* STATUS_CODE */ | ||
0x00, 0x01, 0x00, 0x00, /* TIME_STAMP_SEC */ | ||
0x01, 0x01, 0x00, 0x00, /* TIME_STAMP_NSEC */ | ||
0xFF, 0xFF, 0xFF, 0xBB, /* DATA */ | ||
} | ||
assert.NoError(t, | ||
c.Parse(chunkData), | ||
"A successful parse expected", | ||
) | ||
p := pcic.PCIC{} | ||
buffer := fmt.Sprintf( | ||
"0001L%09d\r\n0001star%sstop\r\n", | ||
miniMalContentLength+len(chunkData), | ||
string(chunkData), | ||
) | ||
// Test the PCIC message with single chunk | ||
r := strings.NewReader(buffer) | ||
f, err := p.Receive(r) | ||
assert.NoError(t, err, "We expect no error while receiving data") | ||
|
||
assert.Equal(t, chunk.RADIAL_DISTANCE_NOISE, f.Chunks[0].Type()) | ||
|
||
// test with trailing XX after the chunk | ||
buffer = fmt.Sprintf( | ||
"0001L%09d\r\n0001star%sXXstop\r\n", | ||
miniMalContentLength+len(chunkData)+2, | ||
string(chunkData), | ||
) | ||
// Test the PCIC message with single chunk | ||
r = strings.NewReader(buffer) | ||
_, err = p.Receive(r) | ||
assert.Error(t, err, "We expect an error while receiving malformed data") | ||
|
||
} |