106 lines
2.6 KiB
Go
106 lines
2.6 KiB
Go
package canjson
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"strconv"
|
|
|
|
can "github.com/fiskerinc/cloud-services/pkg/can-go"
|
|
"github.com/fiskerinc/cloud-services/pkg/can-go/pkg/descriptor"
|
|
"github.com/fiskerinc/cloud-services/pkg/can-go/pkg/generated"
|
|
)
|
|
|
|
// preAllocatedBytesPerSignal is an estimate of how many bytes each signal needs.
|
|
const preAllocatedBytesPerSignal = 40
|
|
|
|
// Marshal a CAN message to JSON.
|
|
func Marshal(m generated.Message) ([]byte, error) {
|
|
f := m.Frame()
|
|
bytes := make([]byte, 0, len(m.Descriptor().Signals)*preAllocatedBytesPerSignal)
|
|
bytes = append(bytes, '{')
|
|
for i, s := range m.Descriptor().Signals {
|
|
s := s
|
|
bytes = append(bytes, '"')
|
|
bytes = append(bytes, s.Name...)
|
|
bytes = append(bytes, `":`...)
|
|
sig := &signal{}
|
|
sig.set(s, f)
|
|
jsonSig, err := json.Marshal(sig)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("marshal json: %w", err)
|
|
}
|
|
bytes = append(bytes, jsonSig...)
|
|
if i < len(m.Descriptor().Signals)-1 {
|
|
bytes = append(bytes, ',')
|
|
}
|
|
}
|
|
bytes = append(bytes, '}')
|
|
return bytes, nil
|
|
}
|
|
|
|
type signal struct {
|
|
Raw json.Number
|
|
Physical json.Number
|
|
Unit string `json:",omitempty"`
|
|
Description string `json:",omitempty"`
|
|
}
|
|
|
|
func (s *signal) set(desc *descriptor.Signal, f can.Frame) {
|
|
switch {
|
|
case desc.Length == 1: // bool
|
|
s.setBoolValue(desc.UnmarshalBool(f.Data), desc)
|
|
case desc.IsSigned: // signed
|
|
s.setSignedValue(desc.UnmarshalSigned(f.Data), desc)
|
|
default: // unsigned
|
|
s.setUnsignedValue(desc.UnmarshalUnsigned(f.Data), desc)
|
|
}
|
|
}
|
|
|
|
func (s *signal) setUnsignedValue(value uint64, desc *descriptor.Signal) {
|
|
s.Raw = uintToJSON(value)
|
|
s.Physical = floatToJSON(desc.ToPhysical(float64(value)))
|
|
s.Unit = desc.Unit
|
|
if value, ok := desc.ValueDescription(int(value)); ok {
|
|
s.Description = value
|
|
}
|
|
}
|
|
|
|
func (s *signal) setSignedValue(value int64, desc *descriptor.Signal) {
|
|
s.Raw = intToJSON(value)
|
|
s.Physical = floatToJSON(desc.ToPhysical(float64(value)))
|
|
s.Unit = desc.Unit
|
|
if value, ok := desc.ValueDescription(int(value)); ok {
|
|
s.Description = value
|
|
}
|
|
}
|
|
|
|
func (s *signal) setBoolValue(value bool, desc *descriptor.Signal) {
|
|
if value {
|
|
s.Raw = "1"
|
|
s.Physical = floatToJSON(desc.ToPhysical(1))
|
|
} else {
|
|
s.Raw = "0"
|
|
s.Physical = floatToJSON(desc.ToPhysical(0))
|
|
}
|
|
s.Unit = desc.Unit
|
|
intValue := 0
|
|
if value {
|
|
intValue = 1
|
|
}
|
|
if value, ok := desc.ValueDescription(intValue); ok {
|
|
s.Description = value
|
|
}
|
|
}
|
|
|
|
func floatToJSON(f float64) json.Number {
|
|
return json.Number(strconv.FormatFloat(f, 'f', -1, 64))
|
|
}
|
|
|
|
func intToJSON(i int64) json.Number {
|
|
return json.Number(strconv.Itoa(int(i)))
|
|
}
|
|
|
|
func uintToJSON(i uint64) json.Number {
|
|
return json.Number(strconv.Itoa(int(i)))
|
|
}
|