1 | // Copyright (C) 2017 The Qt Company Ltd. |
2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only |
3 | |
4 | #ifndef QCANBUSFRAME_H |
5 | #define QCANBUSFRAME_H |
6 | |
7 | #include <QtCore/qmetatype.h> |
8 | #include <QtCore/qobject.h> |
9 | #include <QtSerialBus/qtserialbusglobal.h> |
10 | |
11 | QT_BEGIN_NAMESPACE |
12 | |
13 | class QDataStream; |
14 | |
15 | class Q_SERIALBUS_EXPORT QCanBusFrame |
16 | { |
17 | public: |
18 | using FrameId = quint32; |
19 | |
20 | class TimeStamp { |
21 | public: |
22 | constexpr TimeStamp(qint64 s = 0, qint64 usec = 0) noexcept |
23 | : secs(s), usecs(usec) {} |
24 | |
25 | constexpr static TimeStamp fromMicroSeconds(qint64 usec) noexcept |
26 | { return TimeStamp(usec / 1000000, usec % 1000000); } |
27 | |
28 | constexpr qint64 seconds() const noexcept { return secs; } |
29 | constexpr qint64 microSeconds() const noexcept { return usecs; } |
30 | |
31 | private: |
32 | qint64 secs; |
33 | qint64 usecs; |
34 | }; |
35 | |
36 | enum FrameType { |
37 | UnknownFrame = 0x0, |
38 | DataFrame = 0x1, |
39 | ErrorFrame = 0x2, |
40 | RemoteRequestFrame = 0x3, |
41 | InvalidFrame = 0x4 |
42 | }; |
43 | |
44 | explicit QCanBusFrame(FrameType type = DataFrame) noexcept : |
45 | isExtendedFrame(0x0), |
46 | version(Qt_5_10), |
47 | isFlexibleDataRate(0x0), |
48 | isBitrateSwitch(0x0), |
49 | isErrorStateIndicator(0x0), |
50 | isLocalEcho(0x0), |
51 | reserved0(0x0) |
52 | { |
53 | Q_UNUSED(reserved0); |
54 | ::memset(s: reserved, c: 0, n: sizeof(reserved)); |
55 | setFrameId(0x0); |
56 | setFrameType(type); |
57 | } |
58 | |
59 | enum FrameError { |
60 | NoError = 0, |
61 | TransmissionTimeoutError = (1 << 0), |
62 | LostArbitrationError = (1 << 1), |
63 | ControllerError = (1 << 2), |
64 | ProtocolViolationError = (1 << 3), |
65 | TransceiverError = (1 << 4), |
66 | MissingAcknowledgmentError = (1 << 5), |
67 | BusOffError = (1 << 6), |
68 | BusError = (1 << 7), |
69 | ControllerRestartError = (1 << 8), |
70 | UnknownError = (1 << 9), |
71 | AnyError = 0x1FFFFFFFU |
72 | //only 29 bits usable |
73 | }; |
74 | Q_DECLARE_FLAGS(FrameErrors, FrameError) |
75 | Q_FLAGS(FrameErrors) |
76 | |
77 | explicit QCanBusFrame(QCanBusFrame::FrameId identifier, const QByteArray &data) : |
78 | format(DataFrame), |
79 | isExtendedFrame(0x0), |
80 | version(Qt_5_10), |
81 | isFlexibleDataRate(data.size() > 8 ? 0x1 : 0x0), |
82 | isBitrateSwitch(0x0), |
83 | isErrorStateIndicator(0x0), |
84 | isLocalEcho(0x0), |
85 | reserved0(0x0), |
86 | load(data) |
87 | { |
88 | ::memset(s: reserved, c: 0, n: sizeof(reserved)); |
89 | setFrameId(identifier); |
90 | } |
91 | |
92 | bool isValid() const noexcept |
93 | { |
94 | if (format == InvalidFrame) |
95 | return false; |
96 | |
97 | // long id used, but extended flag not set |
98 | if (!isExtendedFrame && (canId & 0x1FFFF800U)) |
99 | return false; |
100 | |
101 | if (!isValidFrameId) |
102 | return false; |
103 | |
104 | // maximum permitted payload size in CAN or CAN FD |
105 | const qsizetype length = load.size(); |
106 | if (isFlexibleDataRate) { |
107 | if (format == RemoteRequestFrame) |
108 | return false; |
109 | |
110 | return length <= 8 || length == 12 || length == 16 || length == 20 |
111 | || length == 24 || length == 32 || length == 48 || length == 64; |
112 | } |
113 | |
114 | return length <= 8; |
115 | } |
116 | |
117 | constexpr FrameType frameType() const noexcept |
118 | { |
119 | switch (format) { |
120 | case 0x1: return DataFrame; |
121 | case 0x2: return ErrorFrame; |
122 | case 0x3: return RemoteRequestFrame; |
123 | case 0x4: return InvalidFrame; |
124 | // no default to trigger warning |
125 | } |
126 | |
127 | return UnknownFrame; |
128 | } |
129 | |
130 | constexpr void setFrameType(FrameType newFormat) noexcept |
131 | { |
132 | switch (newFormat) { |
133 | case DataFrame: |
134 | format = 0x1; return; |
135 | case ErrorFrame: |
136 | format = 0x2; return; |
137 | case RemoteRequestFrame: |
138 | format = 0x3; return; |
139 | case UnknownFrame: |
140 | format = 0x0; return; |
141 | case InvalidFrame: |
142 | format = 0x4; return; |
143 | } |
144 | } |
145 | |
146 | constexpr bool hasExtendedFrameFormat() const noexcept { return (isExtendedFrame & 0x1); } |
147 | constexpr void setExtendedFrameFormat(bool isExtended) noexcept |
148 | { |
149 | isExtendedFrame = (isExtended & 0x1); |
150 | } |
151 | |
152 | constexpr QCanBusFrame::FrameId frameId() const noexcept |
153 | { |
154 | if (Q_UNLIKELY(format == ErrorFrame)) |
155 | return 0; |
156 | return (canId & 0x1FFFFFFFU); |
157 | } |
158 | constexpr void setFrameId(QCanBusFrame::FrameId newFrameId) |
159 | { |
160 | if (Q_LIKELY(newFrameId < 0x20000000U)) { |
161 | isValidFrameId = true; |
162 | canId = newFrameId; |
163 | setExtendedFrameFormat(isExtendedFrame || (newFrameId & 0x1FFFF800U)); |
164 | } else { |
165 | isValidFrameId = false; |
166 | canId = 0; |
167 | } |
168 | } |
169 | |
170 | void setPayload(const QByteArray &data) |
171 | { |
172 | load = data; |
173 | if (data.size() > 8) |
174 | isFlexibleDataRate = 0x1; |
175 | } |
176 | constexpr void setTimeStamp(TimeStamp ts) noexcept { stamp = ts; } |
177 | |
178 | QByteArray payload() const { return load; } |
179 | constexpr TimeStamp timeStamp() const noexcept { return stamp; } |
180 | |
181 | constexpr FrameErrors error() const noexcept |
182 | { |
183 | if (format != ErrorFrame) |
184 | return NoError; |
185 | |
186 | return FrameErrors(canId & 0x1FFFFFFFU); |
187 | } |
188 | constexpr void setError(FrameErrors e) |
189 | { |
190 | if (format != ErrorFrame) |
191 | return; |
192 | canId = (e & AnyError).toInt(); |
193 | } |
194 | |
195 | QString toString() const; |
196 | |
197 | constexpr bool hasFlexibleDataRateFormat() const noexcept { return (isFlexibleDataRate & 0x1); } |
198 | constexpr void setFlexibleDataRateFormat(bool isFlexibleData) noexcept |
199 | { |
200 | isFlexibleDataRate = (isFlexibleData & 0x1); |
201 | if (!isFlexibleData) { |
202 | isBitrateSwitch = 0x0; |
203 | isErrorStateIndicator = 0x0; |
204 | } |
205 | } |
206 | |
207 | constexpr bool hasBitrateSwitch() const noexcept { return (isBitrateSwitch & 0x1); } |
208 | constexpr void setBitrateSwitch(bool bitrateSwitch) noexcept |
209 | { |
210 | isBitrateSwitch = (bitrateSwitch & 0x1); |
211 | if (bitrateSwitch) |
212 | isFlexibleDataRate = 0x1; |
213 | } |
214 | |
215 | constexpr bool hasErrorStateIndicator() const noexcept { return (isErrorStateIndicator & 0x1); } |
216 | constexpr void setErrorStateIndicator(bool errorStateIndicator) noexcept |
217 | { |
218 | isErrorStateIndicator = (errorStateIndicator & 0x1); |
219 | if (errorStateIndicator) |
220 | isFlexibleDataRate = 0x1; |
221 | } |
222 | constexpr bool hasLocalEcho() const noexcept { return (isLocalEcho & 0x1); } |
223 | constexpr void setLocalEcho(bool localEcho) noexcept |
224 | { |
225 | isLocalEcho = (localEcho & 0x1); |
226 | } |
227 | |
228 | #ifndef QT_NO_DATASTREAM |
229 | friend Q_SERIALBUS_EXPORT QDataStream &operator<<(QDataStream &, const QCanBusFrame &); |
230 | friend Q_SERIALBUS_EXPORT QDataStream &operator>>(QDataStream &, QCanBusFrame &); |
231 | #endif |
232 | |
233 | private: |
234 | enum Version { |
235 | Qt_5_8 = 0x0, |
236 | Qt_5_9 = 0x1, |
237 | Qt_5_10 = 0x2 |
238 | }; |
239 | |
240 | quint32 canId:29; // acts as container for error codes too |
241 | quint8 format:3; // max of 8 frame types |
242 | |
243 | quint8 isExtendedFrame:1; |
244 | quint8 version:5; |
245 | quint8 isValidFrameId:1; |
246 | quint8 isFlexibleDataRate:1; |
247 | |
248 | quint8 isBitrateSwitch:1; |
249 | quint8 isErrorStateIndicator:1; |
250 | quint8 isLocalEcho:1; |
251 | quint8 reserved0:5; |
252 | |
253 | // reserved for future use |
254 | quint8 reserved[2]; |
255 | |
256 | QByteArray load; |
257 | TimeStamp stamp; |
258 | }; |
259 | |
260 | Q_DECLARE_TYPEINFO(QCanBusFrame, Q_RELOCATABLE_TYPE); |
261 | Q_DECLARE_TYPEINFO(QCanBusFrame::FrameError, Q_PRIMITIVE_TYPE); |
262 | Q_DECLARE_TYPEINFO(QCanBusFrame::FrameType, Q_PRIMITIVE_TYPE); |
263 | Q_DECLARE_TYPEINFO(QCanBusFrame::TimeStamp, Q_PRIMITIVE_TYPE); |
264 | |
265 | Q_DECLARE_OPERATORS_FOR_FLAGS(QCanBusFrame::FrameErrors) |
266 | |
267 | #ifndef QT_NO_DATASTREAM |
268 | Q_SERIALBUS_EXPORT QDataStream &operator<<(QDataStream &, const QCanBusFrame &); |
269 | Q_SERIALBUS_EXPORT QDataStream &operator>>(QDataStream &, QCanBusFrame &); |
270 | #endif |
271 | |
272 | QT_END_NAMESPACE |
273 | |
274 | Q_DECLARE_METATYPE(QCanBusFrame::FrameType) |
275 | Q_DECLARE_METATYPE(QCanBusFrame::FrameErrors) |
276 | |
277 | #endif // QCANBUSFRAME_H |
278 | |