This repository was archived by the owner on Mar 12, 2026. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmessage.go
More file actions
157 lines (142 loc) · 5.48 KB
/
message.go
File metadata and controls
157 lines (142 loc) · 5.48 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
package mapache
import "fmt"
// A Message is a single data message from a vehicle, comprised of a list of Fields.
type Message []Field
// Length returns the number of fields in the message.
func (m Message) Length() int {
return len(m)
}
// Size returns the total number of bytes in the message.
func (m Message) Size() int {
size := 0
for _, field := range m {
size += field.Size
}
return size
}
// FillFromBytes fills the Fields of a Message with the provided byte array.
// It decodes the bytes into integer values and stores them in the Value of each Field.
// It returns an error if the data length does not match the size of the Message.
func (m Message) FillFromBytes(data []byte) error {
if len(data) != m.Size() {
return fmt.Errorf("invalid data length, expected %d bytes, got %d", m.Size(), len(data))
}
counter := 0
for i, field := range m {
field.Bytes = data[counter : counter+field.Size]
counter += field.Size
m[i] = field.Decode()
}
return nil
}
// FillFromInts fills the Fields of a Message with the provided integers.
// It encodes the integers into bytes and stores them in the Bytes of each Field.
// It returns an error if the number of integers does not match the number of Fields in the Message.
func (m Message) FillFromInts(ints []int) error {
if len(ints) != m.Length() {
return fmt.Errorf("invalid ints length, expected %d, got %d", m.Length(), len(ints))
}
for i, field := range m {
field.Value = ints[i]
field, err := field.Encode()
if err != nil {
return err
}
m[i] = field
}
return nil
}
// ExportSignals returns a list of all Signals contained in each Field of the Message.
// Basically just calls ExportSignals on each Field and concatenates the results.
func (m Message) ExportSignals() []Signal {
signals := []Signal{}
for _, field := range m {
signals = append(signals, field.ExportSignals()...)
}
return signals
}
// Field is a single field of a message. It will always be at least 1 byte in size.
// It is meant to be an intermediary structure, purely for encoding and decoding byte arrays.
// A single field may contain multiple signals (tpyically when it contains multiple errors as boolean flags).
type Field struct {
// Name of the field. Will be mapped to a signal name unless otherwise specified by ExportSignalFunc.
Name string
// Bytes, Size, Sign, and Endian are used to properly decode and encode the signal.
Bytes []byte
Size int
Sign SignMode
Endian Endian
// Value is the integer value of the field.
Value int
// ExportSignalFunc is the function that is used to export the field as an array of signals.
ExportSignalFunc ExportSignalFunc
}
// ExportSignalFunc is a function that indicates how a field should be exported as an array of signals.
// Any required scaling will be applied here. If ExportSignalFunc is not set, the field will be directly
// exported as a single signal without scaling.
type ExportSignalFunc func(Field) []Signal
// NewField creates a new Field object with the given name, size, sign, endian, and export function.
// If no export function is provided, DefaultSignalExportFunc will be used.
func NewField(name string, size int, sign SignMode, endian Endian, exportSignalFunc ExportSignalFunc) Field {
return Field{
Name: name,
Size: size,
Sign: sign,
Endian: endian,
ExportSignalFunc: exportSignalFunc,
}
}
// Decode takes a Field object, decodes the bytes into an integer value, and returns the decoded Field object.
func (f Field) Decode() Field {
if f.Sign == Signed && f.Endian == BigEndian {
f.Value = BigEndianBytesToSignedInt(f.Bytes)
} else if f.Sign == Signed && f.Endian == LittleEndian {
f.Value = LittleEndianBytesToSignedInt(f.Bytes)
} else if f.Sign == Unsigned && f.Endian == BigEndian {
f.Value = BigEndianBytesToUnsignedInt(f.Bytes)
} else if f.Sign == Unsigned && f.Endian == LittleEndian {
f.Value = LittleEndianBytesToUnsignedInt(f.Bytes)
}
return f
}
// Encode takes a Field object, encodes the integer value into bytes, and returns the encoded Field object.
func (f Field) Encode() (Field, error) {
var err error
if f.Sign == Signed && f.Endian == BigEndian {
f.Bytes, err = BigEndianSignedIntToBinary(f.Value, f.Size)
} else if f.Sign == Signed && f.Endian == LittleEndian {
f.Bytes, err = LittleEndianSignedIntToBinary(f.Value, f.Size)
} else if f.Sign == Unsigned && f.Endian == BigEndian {
f.Bytes, err = BigEndianUnsignedIntToBinary(f.Value, f.Size)
} else if f.Sign == Unsigned && f.Endian == LittleEndian {
f.Bytes, err = LittleEndianUnsignedIntToBinary(f.Value, f.Size)
} else {
return f, fmt.Errorf("invalid sign or endian")
}
return f, err
}
// CheckBit takes a Field object and a bit position, and returns the integer value of the bit at the given position (0 or 1).
// Bit positions are counted from left to right, where bit 0 is the leftmost bit.
func (f Field) CheckBit(bit int) int {
byteIndex := bit / 8
bitPosition := 7 - (bit % 8)
if byteIndex >= len(f.Bytes) {
return 0
}
return int((f.Bytes[byteIndex] >> bitPosition) & 1)
}
// ExportSignals takes a Field object and exports it as an array of signals.
func (f Field) ExportSignals() []Signal {
if f.ExportSignalFunc == nil {
return DefaultSignalExportFunc(f)
}
return f.ExportSignalFunc(f)
}
// DefaultSignalExportFunc is the default export function for a field. It exports the field as a single signal with no scaling.
func DefaultSignalExportFunc(f Field) []Signal {
return []Signal{{
Name: f.Name,
Value: float64(f.Value),
RawValue: f.Value,
}}
}