-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathLog.cpp
More file actions
194 lines (169 loc) · 6.5 KB
/
Log.cpp
File metadata and controls
194 lines (169 loc) · 6.5 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
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
/*
* WaterTankController project
* Copyright (c) 2026 Fedir Vilhota <fredy31415@gmail.com>
* This software is released under the MIT License.
* See the LICENSE file in the project root for full license information.
*/
#include "Log.h"
#include <time.h>
Log::Log(size_t bufsize)
: BUFFER_SIZE(bufsize)
{
lastFileDate = getFileDate();
currentFileName = makeFileName(lastFileDate);
bufferString.reserve(64 * bufsize); // Reserve some memory to avoid frequent reallocations
}
void Log::begin() {
// 1. Налаштовуємо часовий пояс (Україна)
configTzTime("EET-2EEST,M3.5.0/3,M10.5.0/4", "pool.ntp.org", "time.google.com");
// 2. Запускаємо задачу синхронізації
// Передаємо "this" як параметр, щоб статична функція мала доступ до об'єкта
xTaskCreate(
this->timeSyncTask, // Функція
"NTP_Sync", // Назва
3072, // Стек (для NTP достатньо)
this, // Передаємо вказівник на поточний екземпляр класу
1, // Пріоритет (низький)
NULL // Хендл (не потрібен)
);
}
void Log::timeSyncTask(void *pvParameters) {
// Отримуємо вказівник на наш об'єкт класу Log
Log* instance = (Log*)pvParameters;
time_t now;
struct tm timeinfo;
while (true) {
time(&now);
localtime_r(&now, &timeinfo);
// Перевіряємо, чи рік вже адекватний (синхронізація успішна)
if (timeinfo.tm_year > (2020 - 1900)) {
instance->_isSynced = true;
Serial.println("[NTP] Час успішно синхронізовано.");
// Після успіху перевіряємо час дуже рідко (наприклад, раз на добу)
// 24 години * 60 хв * 60 сек * 1000 мс
vTaskDelay(pdMS_TO_TICKS(24 * 60 * 60 * 1000));
} else {
instance->_isSynced = false;
Serial.println("[NTP] Очікування синхронізації...");
// Якщо не вийшло, пробуємо знову через 30 секунд
vTaskDelay(pdMS_TO_TICKS(30000));
}
}
}
bool Log::isSynced() {
return _isSynced;
}
String Log::getFileDate() {
time_t now = time(nullptr);
struct tm *tm_struct = localtime(&now);
if (tm_struct) {
String date = String(tm_struct->tm_year + 1900) + "-";
if (tm_struct->tm_mon + 1 < 10) date += '0';
date += String(tm_struct->tm_mon + 1) + "-";
if (tm_struct->tm_mday < 10) date += '0';
date += String(tm_struct->tm_mday);
return date;
} else {
return String("1970-01-01");
}
}
String Log::makeFileName(const String& date) {
return "/log_" + date + ".csv";
}
void Log::add(float volume) {
// Не зберігаємо логи до синхронізації часу
if (!isSynced()) {
return;
}
time_t now = time(nullptr);
struct tm *tm_struct = localtime(&now);
if (!tm_struct)
return;
// Manual rounding
int roundedVolume = (int)(volume + 0.5);
// Якщо значення таке саме і не минула година — не логувати
if (lastLoggedVolume != -1 && roundedVolume == lastLoggedVolume && (now - lastLoggedTime) < 3600) {
return;
}
lastLoggedVolume = roundedVolume;
lastLoggedTime = now;
tm_struct->tm_sec = 0;
int currentMinute = tm_struct->tm_min + tm_struct->tm_hour * 60 + tm_struct->tm_mday * 24 * 60;
if (currentMinute == lastWriteMinute)
return;
lastWriteMinute = currentMinute;
String line = "";
if(tm_struct->tm_hour < 10) line += '0';
line += String(tm_struct->tm_hour) + ":";
if(tm_struct->tm_min < 10) line += '0';
line += String(tm_struct->tm_min) + "," + String(roundedVolume) + "\n";
bufferString += line;
linesInBuffer++;
if (linesInBuffer >= BUFFER_SIZE) {
flush();
}
}
void Log::flush() {
if (bufferString.length() == 0) return;
// Оновлення імені файлу, якщо дата змінилася
String fileDate = getFileDate();
if (fileDate != lastFileDate) {
lastFileDate = fileDate;
currentFileName = makeFileName(lastFileDate);
}
File file = LittleFS.open(currentFileName.c_str(), "a");
if (file) {
file.print(bufferString);
file.close();
}
bufferString = "";
linesInBuffer = 0;
cleanupOldLogs();
}
void Log::cleanupOldLogs() {
int fileCount = 0;
// Count files first to see if we need to delete anything
File root = LittleFS.open("/");
File file = root.openNextFile();
while (file) {
String fname = file.name();
if (fname.indexOf("log_") >= 0 && fname.endsWith(".csv")) {
fileCount++;
}
file.close();
file = root.openNextFile();
}
root.close();
const int toKeep = 7;
if (fileCount <= toKeep) return; // Nothing to do
int filesToDelete = fileCount - toKeep;
// Simple iterative deletion of oldest files
// We run the loop 'filesToDelete' times. In each pass, we find the oldest file and delete it.
// This avoids storing all filenames in a vector.
for (int i = 0; i < filesToDelete; i++) {
String oldestFile = "";
String oldestDate = "9999-99-99";
root = LittleFS.open("/");
file = root.openNextFile();
while (file) {
String fname = file.name();
if (fname.indexOf("log_") >= 0 && fname.endsWith(".csv")) {
// Extract date from log_YYYY-MM-DD.csv
// "log_" is at index X. date starts at X+4. length is 10.
int logIndex = fname.indexOf("log_");
String date = fname.substring(logIndex + 4, logIndex + 14);
if (date < oldestDate) {
oldestDate = date;
oldestFile = fname;
}
}
file.close();
file = root.openNextFile();
}
root.close();
if (oldestFile.length() > 0) {
if (!oldestFile.startsWith("/")) oldestFile = "/" + oldestFile;
LittleFS.remove(oldestFile.c_str());
}
}
}