diff --git a/cmd/cmd/speeddaemon/main.go b/cmd/cmd/speeddaemon/main.go new file mode 100644 index 0000000..7905807 --- /dev/null +++ b/cmd/cmd/speeddaemon/main.go @@ -0,0 +1,5 @@ +package main + +func main() { + +} diff --git a/cmd/cmd/speeddaemon/message.go b/cmd/cmd/speeddaemon/message.go new file mode 100644 index 0000000..f7a5592 --- /dev/null +++ b/cmd/cmd/speeddaemon/message.go @@ -0,0 +1,108 @@ +package main + +import ( + "bytes" + "encoding/binary" + "fmt" + "time" +) + +const ( + errorHeader byte = 0x10 + plateHeader byte = 0x20 + ticketHeader byte = 0x21 + wantHeartbeatHeader byte = 0x40 + heartbeatHeader byte = 0x41 + amCameraHeader byte = 0x80 + amDispatcherHeader byte = 0x81 +) + +func encodeErrorMessage(msg string) []byte { + b := bytes.NewBuffer([]byte{errorHeader}) + b.WriteByte(byte(len(msg))) + b.Write([]byte(msg)) + return b.Bytes() +} + +type plateMessage struct { + plate string + timestamp time.Time +} + +func decodePlateMessage(data []byte) (*plateMessage, error) { + if data[0] != plateHeader { + return nil, fmt.Errorf("attempted to decode plate message with header %0x", data[0]) + } + + pl := int(data[1]) + plate := string(data[2 : 2+pl]) + ts32 := binary.BigEndian.Uint32(data[2+pl:]) + + return &plateMessage{ + plate: plate, + timestamp: time.Unix(int64(ts32), 0), + }, nil +} + +type ticketMessage struct { + plate string + road uint16 + mileOne uint16 + timestampOne uint32 + mileTwo uint16 + timestampTwo uint32 + speed uint16 +} + +func encodeTicketMessage(data []byte) (*ticketMessage, error) { + panic("unimplemented") +} + +type wantHeartbeatMessage uint32 + +func decodeWantHeartbeatMessage(data []byte) (*wantHeartbeatMessage, error) { + if data[0] != wantHeartbeatHeader { + return nil, fmt.Errorf("attempted to decode wantHearbeatMessage with header %0x", data[0]) + } + d := wantHeartbeatMessage(binary.BigEndian.Uint32(data[1:])) + return &d, nil +} + +func encodeHeartbeat() []byte { + return []byte{heartbeatHeader} +} + +type amCameraMessage struct { + road, mile, limit uint16 +} + +func decodeAmCameraMessage(data []byte) (*amCameraMessage, error) { + if data[0] != amCameraHeader { + return nil, fmt.Errorf("attempted to decode amCameraMessage with header %0x", data[0]) + } + return &amCameraMessage{ + road: binary.BigEndian.Uint16(data[1:3]), + mile: binary.BigEndian.Uint16(data[3:5]), + limit: binary.BigEndian.Uint16(data[5:]), + }, nil +} + +type amDispatcherMessage struct { + numroads byte + roads []uint16 +} + +func decodeAmDispatcherMessage(data []byte) (*amDispatcherMessage, error) { + if data[0] != amDispatcherHeader { + return nil, fmt.Errorf("attempted to decode amDispatcherMessage with header %0x", data[0]) + } + var roads []uint16 + for i, j := 0, 2; i < int(data[1]); i++ { + roads = append(roads, binary.BigEndian.Uint16(data[j:j+2])) + j += 2 + } + return &amDispatcherMessage{ + data[1], + roads, + }, nil +} diff --git a/cmd/cmd/speeddaemon/message_test.go b/cmd/cmd/speeddaemon/message_test.go new file mode 100644 index 0000000..feaebcb --- /dev/null +++ b/cmd/cmd/speeddaemon/message_test.go @@ -0,0 +1,88 @@ +package main + +import ( + "testing" + "time" +) + +func TestPlateMessage(t *testing.T) { + data := []byte{0x20, 0x04, 0x55, 0x4e, 0x31, 0x58, 0x00, 0x00, 0x03, 0xe8} + p, err := decodePlateMessage(data) + if err != nil { + t.Error(err) + } + if p.plate != "UN1X" { + t.Error("plate wrong") + } + if p.timestamp != time.Unix(0x000003e8, 0) { + t.Error("timestamp wrong") + } +} + +func TestWantHeartbeatMessage(t *testing.T) { + data := []byte{0x40, 0x00, 0x00, 0x00, 0x0a} + h, err := decodeWantHeartbeatMessage(data) + if err != nil { + t.Error(err) + } + if *h != wantHeartbeatMessage(10) { + t.Error("Wrong duration") + } +} + +func TestAmCameraMessage(t *testing.T) { + data := []byte{0x80, 0x00, 0x42, 0x00, 0x64, 0x00, 0x3c} + m, err := decodeAmCameraMessage(data) + if err != nil { + t.Error(err) + } + if m.road != 66 { + t.Error("wrong road") + } + if m.mile != 100 { + t.Error("wrong mile") + } + if m.limit != 60 { + t.Error("wrong limit") + } +} + +func TestAmDispatcherMessage(t *testing.T) { + data := []byte{0x81, 0x01, 0x00, 0x42} + m, err := decodeAmDispatcherMessage(data) + if err != nil { + t.Error(err) + } + if m.numroads != 1 { + t.Error("wrong numroads") + } + if len(m.roads) != 1 { + t.Error("wrong length of roads") + } + if m.roads[0] != 66 { + t.Error("wrong roads[0]") + } +} + +func TestAmDispatcherMessageLong(t *testing.T) { + data := []byte{0x81, 0x03, 0x00, 0x42, 0x01, 0x70, 0x13, 0x88} + m, err := decodeAmDispatcherMessage(data) + if err != nil { + t.Error(err) + } + if m.numroads != 3 { + t.Error("wrong numroads") + } + if len(m.roads) != 3 { + t.Error("wrong length of roads") + } + if m.roads[0] != 66 { + t.Error("wrong roads[0]") + } + if m.roads[1] != 368 { + t.Error("wrong roads[0]") + } + if m.roads[2] != 5000 { + t.Error("wrong roads[0]") + } +}