Added support for handling various payload types, including GPS and alarms, with new structures and constants. Introduced helper methods for JSON marshalling/unmarshalling of GPS data and modularized the handling of certificates, configurations, and alarms. Implemented foundational server code for testing and expanded several package functionalities.
189 lines
4.1 KiB
Go
189 lines
4.1 KiB
Go
package n9m
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/binary"
|
|
"encoding/json"
|
|
"fmt"
|
|
"github.com/icza/bitio"
|
|
"log"
|
|
"time"
|
|
)
|
|
|
|
// Read package
|
|
func (e *Package) ReadPackage() bool {
|
|
if len(e.Accum) < 12 {
|
|
return false
|
|
}
|
|
|
|
r := bitio.NewReader(bytes.NewBuffer(e.Accum))
|
|
e.Version = uint8(r.TryReadBits(2))
|
|
e.EncryptionFlag = r.TryReadBool()
|
|
e.CompressFlag = r.TryReadBool()
|
|
e.CSRCCount = uint8(r.TryReadBits(4))
|
|
e.PayloadType = PayloadType(r.TryReadBits(8))
|
|
e.SSRC = SpecialPayloadType((r.TryReadBits(8) | (r.TryReadBits(8) << 8)))
|
|
|
|
if e.EncryptionFlag && e.CompressFlag {
|
|
// TODO: get snippet, that use this code
|
|
r.TryReadBits(8 * 4)
|
|
e.payloadLen = r.TryReadBits(8)
|
|
r.TryReadBits(3 * 8)
|
|
|
|
if uint64(len(e.Accum)) < e.payloadLen+12 {
|
|
return false
|
|
}
|
|
} else {
|
|
e.payloadLen = r.TryReadBits(32)
|
|
|
|
// WTF: e.CC is useless
|
|
for i := uint64(0); i < 1; i++ {
|
|
e.CSRC[i] = r.TryReadBits(32)
|
|
}
|
|
}
|
|
|
|
if e.payloadLen > 1e6 {
|
|
log.Printf("%v\n", e)
|
|
log.Panicln("CORRUPTED PACKAGE")
|
|
}
|
|
|
|
numOfBytes := 0
|
|
|
|
if e.payloadLen != 0 {
|
|
e.RawPayload = make([]byte, e.payloadLen)
|
|
numOfBytes = r.TryRead(e.RawPayload)
|
|
} else {
|
|
e.RawPayload = []byte{}
|
|
}
|
|
|
|
if numOfBytes != int(e.payloadLen) {
|
|
return false
|
|
}
|
|
|
|
e.Accum = e.Accum[12+e.payloadLen:]
|
|
|
|
switch e.PayloadType {
|
|
case PayloadTypeData:
|
|
if err := json.Unmarshal(e.RawPayload, &e.Payload); err != nil {
|
|
log.Printf("Error parsing JSON payload: %v", err)
|
|
return false
|
|
}
|
|
case PayloadTypeSpecial:
|
|
switch e.SSRC {
|
|
case SpecialPayloadTypeGPS:
|
|
e.GPS.GPSStatus = e.RawPayload[0]
|
|
e.GPS.Expand = e.RawPayload[1]
|
|
e.GPS.Real = e.RawPayload[2]
|
|
|
|
e.GPS.Longitude = float64(binary.BigEndian.Uint32(e.RawPayload[4:8])) / 1e6
|
|
e.GPS.Latitude = float64(binary.BigEndian.Uint32(e.RawPayload[8:12])) / 1e6
|
|
e.GPS.Speed = float64(binary.BigEndian.Uint32(e.RawPayload[12:16])) / 100
|
|
e.GPS.Direction = float64(binary.BigEndian.Uint32(e.RawPayload[16:20])) / 100
|
|
e.GPS.Altitude = int32(binary.BigEndian.Uint32(e.RawPayload[20:24]))
|
|
|
|
var err error
|
|
if e.GPS.Time, err = time.Parse("20060102150405", string(e.RawPayload[24:38])); err != nil {
|
|
log.Printf("Error parsing time: %v", err)
|
|
}
|
|
default:
|
|
fmt.Println("N9M parser warning: unknown special payload type", e.SSRC)
|
|
}
|
|
default:
|
|
fmt.Println("N9M parser warning: unknown payload type", e.PayloadType)
|
|
}
|
|
|
|
if r.TryError != nil {
|
|
log.Printf("TryError encountered: %v", r.TryError)
|
|
return false
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
func (e *Package) PackPayload() (err error) {
|
|
e.RawPayload, err = json.Marshal(e.Payload)
|
|
e.payloadLen = uint64(len(e.RawPayload))
|
|
|
|
if e.payloadLen != 0 {
|
|
e.RawPayload = append(e.RawPayload, 0)
|
|
e.payloadLen++
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
func (e *Package) PackPackage() []byte {
|
|
var err error
|
|
|
|
if err = e.PackPayload(); err != nil {
|
|
log.Printf("Error while packing payload: %v", err)
|
|
return []byte{}
|
|
}
|
|
|
|
return e.PackRawPackage()
|
|
}
|
|
|
|
func (e *Package) PackRawPackage() []byte {
|
|
var err error
|
|
|
|
b := &bytes.Buffer{}
|
|
w := bitio.NewWriter(b)
|
|
|
|
w.TryWriteBits(uint64(e.Version), 2)
|
|
|
|
w.TryWriteBool(e.EncryptionFlag)
|
|
w.TryWriteBool(e.CompressFlag)
|
|
|
|
w.TryWriteBits(uint64(e.CSRCCount), 4)
|
|
w.TryWriteBits(uint64(e.PayloadType), 8)
|
|
w.TryWriteBits(uint64(e.SSRC), 16)
|
|
|
|
w.TryWriteBits(e.payloadLen, 32)
|
|
|
|
// WTF: e.CC is useless
|
|
for i := uint64(0); i < 1; i++ {
|
|
w.TryWriteBits(e.CSRC[i], 32)
|
|
}
|
|
|
|
if e.payloadLen != 0 {
|
|
w.TryWrite(e.RawPayload)
|
|
}
|
|
|
|
if err = w.Close(); err != nil {
|
|
log.Printf("Error while closing writer: %v", err)
|
|
return []byte{}
|
|
}
|
|
|
|
return b.Bytes()
|
|
}
|
|
|
|
func (e *Package) GetParametersAs(parameters any) error {
|
|
marshal, err := json.Marshal(e.Payload.Parameter)
|
|
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return json.Unmarshal(marshal, parameters)
|
|
}
|
|
|
|
func (e *Package) SetParameters(parameters any) {
|
|
e.Payload.Response = struct{}{}
|
|
e.Payload.Parameter = parameters
|
|
}
|
|
|
|
func (e *Package) GetResponseAs(response any) error {
|
|
marshal, err := json.Marshal(e.Payload.Response)
|
|
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return json.Unmarshal(marshal, response)
|
|
}
|
|
|
|
func (e *Package) SetResponse(response any) {
|
|
e.Payload.Parameter = struct{}{}
|
|
e.Payload.Response = response
|
|
}
|