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