rusEFI
The most advanced open source ECU
Loading...
Searching...
No Matches
binary_mlg_logging.cpp
Go to the documentation of this file.
1/**
2 * binary_mlg_logging.cpp
3 *
4 * See also BinarySensorLog.java
5 * See also mlq_file_format.txt
6 *
7 * https://www.efianalytics.com/TunerStudio/docs/MLG_Binary_LogFormat_1.0.pdf
8 * https://www.efianalytics.com/TunerStudio/docs/MLG_Binary_LogFormat_2.0.pdf
9 *
10 * for opposite see mlg_reader.cpp
11 */
12
13#include "pch.h"
14
15#include "binary_mlg_logging.h"
16#include "mlg_field.h"
17#include "tunerstudio.h"
18#include "board_lookup.h"
19
20#if EFI_FILE_LOGGING || EFI_UNIT_TEST
21
22#define TIME_PRECISION 1000
23
24#if EFI_PROD_CODE
25extern bool main_loop_started;
26#endif
27
28// floating number of seconds with millisecond precision
29static scaled_channel<uint32_t, TIME_PRECISION> packedTime;
30
31// The list of logged fields lives in a separate file so it can eventually be tool-generated
32// We use angle brackets instead of quotes because for some boards we want to use header different from the one in this
33// directory
35
36namespace MLG
37{
38
40 return efi::size(fields);
41}
42
43static constexpr uint16_t computeFieldsRecordLength() {
44 uint16_t recLength = 0;
45 for (size_t i = 0; i < efi::size(fields); i++) {
46 recLength += fields[i].getSize();
47 }
48
49 return recLength;
50}
51
52static uint64_t binaryLogCount = 0;
53
54static const uint16_t recordLength = computeFieldsRecordLength();
55
56static size_t writeFileHeader(Writer& outBuffer) {
57 size_t writen = 0;
59 // File format: MLVLG\0
60 strncpy(buffer, "MLVLG", 6);
61
62 // Format version = 02
63 buffer[6] = 0;
64 buffer[7] = 2;
65
66 // Timestamp
67 buffer[8] = 0;
68 buffer[9] = 0;
69 buffer[10] = 0;
70 buffer[11] = 0;
71
72 // Info data start
73 buffer[12] = 0;
74 buffer[13] = 0;
75 buffer[14] = 0;
76 buffer[15] = 0;
77
78 size_t headerSize = Types::Header::Size + efi::size(fields) * Types::Field::DescriptorSize;
79
80 // Data begin index: begins immediately after the header
81 buffer[16] = (headerSize >> 24) & 0xFF;
82 buffer[17] = (headerSize >> 16) & 0xFF;
83 buffer[18] = (headerSize >> 8) & 0xFF;
84 buffer[19] = headerSize & 0xFF;
85
86 // Record length - length of a single data record: sum size of all fields
87 buffer[20] = recordLength >> 8;
88 buffer[21] = recordLength & 0xFF;
89
90 // Number of logger fields
91 int fieldsCount = efi::size(fields);
92 buffer[22] = fieldsCount >> 8;
93 buffer[23] = fieldsCount;
94
96 writen += Types::Header::Size;
97
98 // Write the actual logger fields, offset by header size (24)
99 for (size_t i = 0; i < efi::size(fields); i++) {
100 writen += fields[i].writeHeader(outBuffer);
101 }
102
103 return writen;
104}
105
106static uint8_t blockRollCounter = 0;
107
108static size_t writeSdBlock(Writer& outBuffer) {
109 size_t writen = 0;
110 static char buffer[16];
111
112 // Offset 0 = Block type, standard data block in this case
113 buffer[0] = 0;
114
115 // Offset 1 = rolling counter sequence number
117
118 // Offset 2, size 2 = Timestamp at 10us resolution
119 efitimeus_t nowUs = getTimeNowUs();
120 uint16_t timestamp = nowUs / 10;
121 buffer[2] = timestamp >> 8;
122 buffer[3] = timestamp & 0xFF;
123
124 // TODO: check ret value!
125 outBuffer.write(buffer, 4);
126 writen += 4;
127
128 // todo: add a log field for SD card period
129 // revSdCardLineTime = nowUs;
130
131 packedTime = getTimeNowMs() * 1.0 / TIME_PRECISION;
132
133 uint8_t sum = 0;
134 for (size_t fieldIndex = 0; fieldIndex < efi::size(fields); fieldIndex++) {
135 #if EFI_UNIT_TEST
136 if (engine == nullptr) {
137 throw std::runtime_error{"Engine pointer is nullptr in writeSdBlock"};
138 }
139
140 // For tests a global Engine pointer is initialised with the nullptr, and tests that do require it
141 // create their own instance and set up the global pointer accordingly.
142 // Global static const array of fields in the generated file log_fields_generated.h has fields with
143 // addresses const-evaluated against 'nullptr' engine, which effectively means offsets in Engine struct,
144 // so if that is the case, we need to account for the offset to whatever
145 // real current engine pointer is set to.
146 // In tests, we are dealing with ELF on linux, and as far as I'm aware, there are no distributions
147 // where the default linker config can map smth before the 4 MB address.
148 // If in doubt, check your system for a min text-segment with "ld --verbose | grep -A20 ENTRY"
149 // Engine struct is lower than 4MB in size, so we can compare field address against Engine size
150 // to find out whether field address was initialised against nullptr engine or not.
151
152 constexpr auto engineObjectSize{ sizeof(Engine) };
153 static_assert(engineObjectSize < 0x400000);
154
155 auto const currentFieldAddress{ reinterpret_cast<uintptr_t>(fields[fieldIndex].getAddr()) };
156 auto const fieldNeedsOffset{ currentFieldAddress < engineObjectSize };
157 void* const offset{ fieldNeedsOffset ? engine : nullptr };
158 #else
159 void* const offset{ nullptr };
160 #endif
161
162 size_t entrySize = fields[fieldIndex].writeData(buffer, offset);
163
164 for (size_t byteIndex = 0; byteIndex < entrySize; byteIndex++) {
165 // "CRC" at the end is just the sum of all bytes
166 sum += buffer[byteIndex];
167 }
168 // TODO: check ret value!
169 outBuffer.write(buffer, entrySize);
170 writen += entrySize;
171 }
172
173 buffer[0] = sum;
174 // 1 byte checksum footer
175 outBuffer.write(buffer, 1);
176 writen += 1;
177
178 return writen;
179}
180
181size_t writeSdLogLine(Writer& bufferedWriter) {
182#if EFI_PROD_CODE
184 return 0;
185#endif //EFI_PROD_CODE
186
187 if (binaryLogCount == 0) {
189
190 return writeFileHeader(bufferedWriter);
191 } else {
193
195 return writeSdBlock(bufferedWriter);
196 }
197}
198
200 binaryLogCount = 0;
202}
203
204} /* namespace MLG */
205
206#endif /* EFI_FILE_LOGGING */
bool main_loop_started
Definition rusefi.cpp:143
static scaled_channel< uint32_t, TIME_PRECISION > packedTime
constexpr const void * getAddr() const
Definition mlg_field.h:89
size_t writeHeader(Writer &outBuffer) const
Definition mlg_field.h:93
size_t writeData(char *buffer, void *offset) const
Definition mlg_field.h:136
constexpr size_t getSize() const
Definition mlg_field.h:88
efitimeus_t getTimeNowUs()
Definition efitime.cpp:26
efitimems_t getTimeNowMs()
Returns the 32 bit number of milliseconds since the board initialization.
Definition efitime.cpp:34
static EngineAccessor engine
Definition engine.h:413
static LOG_FIELD_CONSTNESS_SPECIFIER_STORAGE MLG::Entries::Field fields[]
constexpr size_t DescriptorSize
Definition mlg_types.h:71
constexpr size_t Size
Definition mlg_types.h:40
static uint64_t binaryLogCount
static const uint16_t recordLength
static uint8_t blockRollCounter
static size_t writeSdBlock(Writer &outBuffer)
void resetFileLogging()
static size_t writeFileHeader(Writer &outBuffer)
int getSdCardFieldsCount()
size_t writeSdLogLine(Writer &bufferedWriter)
static constexpr uint16_t computeFieldsRecordLength()
Definition writer.h:9
virtual size_t write(const char *buffer, size_t count)=0
static BigBufferHandle buffer
void updateTunerStudioState()
uint16_t offset
Definition tunerstudio.h:0