JXCT Soil Sensor 7-in-1 v3.4.9 (June 2025)
Professional IoT soil monitoring system with ESP32, Modbus RTU, MQTT, and advanced compensation algorithms
Загрузка...
Поиск...
Не найдено
validation_utils.cpp
См. документацию.
1
6
7#include "validation_utils.h"
8#include "jxct_constants.h"
9#include "logger.h"
10
11// ============================================================================
12// ВАЛИДАЦИЯ КОНФИГУРАЦИИ
13// ============================================================================
14
15ValidationResult validateSSID(const String& ssid)
16{
17 if (ssid.length() == 0)
18 {
19 return {false, "SSID не может быть пустым"};
20 }
21 if (ssid.length() > 32)
22 {
23 return {false, "SSID слишком длинный (максимум 32 символа)"};
24 }
25 return {true, ""};
26}
27
28ValidationResult validatePassword(const String& password)
29{
30 if (password.length() > 0 && password.length() < 8)
31 {
32 return {false, "Пароль должен содержать минимум 8 символов"};
33 }
34 if (password.length() > 63)
35 {
36 return {false, "Пароль слишком длинный (максимум 63 символа)"};
37 }
38 return {true, ""};
39}
40
42{
43 if (server.length() == 0)
44 {
45 return {false, "MQTT сервер не может быть пустым"};
46 }
47 if (server.length() > 253)
48 {
49 return {false, "MQTT сервер слишком длинный"};
50 }
51 // Простая проверка на валидность hostname/IP
52 if (server.indexOf(' ') >= 0)
53 {
54 return {false, "MQTT сервер содержит недопустимые символы"};
55 }
56 return {true, ""};
57}
58
60{
61 if (port < CONFIG_MQTT_PORT_MIN || port > CONFIG_MQTT_PORT_MAX)
62 {
63 return {false, "MQTT порт должен быть в диапазоне 1-65535"};
64 }
65 return {true, ""};
66}
67
69{
70 if (apiKey.length() == 0)
71 {
72 return {false, "ThingSpeak API ключ не может быть пустым"};
73 }
74 if (apiKey.length() != 16)
75 {
76 return {false, "ThingSpeak API ключ должен содержать 16 символов"};
77 }
78 // Проверяем, что содержит только допустимые символы
79 for (char c : apiKey)
80 {
81 if (!isAlphaNumeric(c))
82 {
83 return {false, "ThingSpeak API ключ содержит недопустимые символы"};
84 }
85 }
86 return {true, ""};
87}
88
89ValidationResult validateInterval(unsigned long interval, unsigned long min_val, unsigned long max_val,
90 const char* name)
91{
92 if (interval < min_val || interval > max_val)
93 {
94 String message = String(name) + " должен быть в диапазоне " + String(min_val) + "-" + String(max_val) + " мс";
95 return {false, message};
96 }
97 return {true, ""};
98}
99
101{
102 return validateInterval(interval, CONFIG_INTERVAL_MIN, CONFIG_INTERVAL_MAX, "Интервал чтения датчика");
103}
104
106{
107 return validateInterval(interval, CONFIG_INTERVAL_MIN, CONFIG_INTERVAL_MAX, "Интервал публикации MQTT");
108}
109
111{
112 return validateInterval(interval, CONFIG_THINGSPEAK_MIN, CONFIG_THINGSPEAK_MAX, "Интервал ThingSpeak");
113}
114
116{
117 return validateInterval(interval, 10000, 86400000, "Интервал обновления NTP");
118}
119
120// ============================================================================
121// ВАЛИДАЦИЯ ДАННЫХ ДАТЧИКА
122// ============================================================================
123
125{
126 if (temperature < SENSOR_TEMP_MIN || temperature > SENSOR_TEMP_MAX)
127 {
128 return {false, "Температура вне допустимого диапазона"};
129 }
130 return {true, ""};
131}
132
134{
135 if (humidity < SENSOR_HUMIDITY_MIN || humidity > SENSOR_HUMIDITY_MAX)
136 {
137 return {false, "Влажность вне допустимого диапазона"};
138 }
139 return {true, ""};
140}
141
143{
144 if (ph < SENSOR_PH_MIN || ph > SENSOR_PH_MAX)
145 {
146 return {false, "pH вне допустимого диапазона"};
147 }
148 return {true, ""};
149}
150
152{
153 if (ec < SENSOR_EC_MIN || ec > SENSOR_EC_MAX)
154 {
155 return {false, "EC вне допустимого диапазона"};
156 }
157 return {true, ""};
158}
159
160ValidationResult validateNPK(float value, const char* nutrient)
161{
162 if (value < SENSOR_NPK_MIN || value > SENSOR_NPK_MAX)
163 {
164 String message = String(nutrient) + " вне допустимого диапазона";
165 return {false, message};
166 }
167 return {true, ""};
168}
169
170// ============================================================================
171// КОМПЛЕКСНАЯ ВАЛИДАЦИЯ
172// ============================================================================
173
175{
177 result.isValid = true;
178
179 // Проверка SSID (всегда обязательно)
180 auto ssidResult = validateSSID(config.ssid);
181 if (!ssidResult.isValid)
182 {
183 result.isValid = false;
184 result.errors.push_back({"ssid", ssidResult.message});
185 }
186
187 // Проверка пароля WiFi
188 auto passwordResult = validatePassword(config.password);
189 if (!passwordResult.isValid)
190 {
191 result.isValid = false;
192 result.errors.push_back({"password", passwordResult.message});
193 }
194
195 if (checkRequired)
196 {
197 // Проверка MQTT настроек (если включен)
198 if (config.mqttEnabled)
199 {
200 auto mqttServerResult = validateMQTTServer(config.mqttServer);
201 if (!mqttServerResult.isValid)
202 {
203 result.isValid = false;
204 result.errors.push_back({"mqtt_server", mqttServerResult.message});
205 }
206
207 auto mqttPortResult = validateMQTTPort(config.mqttPort);
208 if (!mqttPortResult.isValid)
209 {
210 result.isValid = false;
211 result.errors.push_back({"mqtt_port", mqttPortResult.message});
212 }
213 }
214
215 // Проверка ThingSpeak настроек (если включен)
216 if (config.thingSpeakEnabled)
217 {
218 auto tsResult = validateThingSpeakAPIKey(config.thingSpeakAPIKey);
219 if (!tsResult.isValid)
220 {
221 result.isValid = false;
222 result.errors.push_back({"thingspeak_api_key", tsResult.message});
223 }
224 }
225 }
226
227 // Проверка интервалов
228 auto sensorIntervalResult = validateSensorReadInterval(config.sensorReadInterval);
229 if (!sensorIntervalResult.isValid)
230 {
231 result.isValid = false;
232 result.errors.push_back({"sensor_read_interval", sensorIntervalResult.message});
233 }
234
235 auto mqttIntervalResult = validateMQTTPublishInterval(config.mqttPublishInterval);
236 if (!mqttIntervalResult.isValid)
237 {
238 result.isValid = false;
239 result.errors.push_back({"mqtt_publish_interval", mqttIntervalResult.message});
240 }
241
242 auto tsIntervalResult = validateThingSpeakInterval(config.thingspeakInterval);
243 if (!tsIntervalResult.isValid)
244 {
245 result.isValid = false;
246 result.errors.push_back({"thingspeak_interval", tsIntervalResult.message});
247 }
248
249 auto ntpIntervalResult = validateNTPInterval(config.ntpUpdateInterval);
250 if (!ntpIntervalResult.isValid)
251 {
252 result.isValid = false;
253 result.errors.push_back({"ntp_update_interval", ntpIntervalResult.message});
254 }
255
256 return result;
257}
258
260{
262 result.isValid = true;
263
264 auto tempResult = validateTemperature(data.temperature);
265 if (!tempResult.isValid)
266 {
267 result.isValid = false;
268 result.errors.push_back({"temperature", tempResult.message});
269 }
270
271 auto humResult = validateHumidity(data.humidity);
272 if (!humResult.isValid)
273 {
274 result.isValid = false;
275 result.errors.push_back({"humidity", humResult.message});
276 }
277
278 auto phResult = validatePH(data.ph);
279 if (!phResult.isValid)
280 {
281 result.isValid = false;
282 result.errors.push_back({"ph", phResult.message});
283 }
284
285 auto ecResult = validateEC(data.ec);
286 if (!ecResult.isValid)
287 {
288 result.isValid = false;
289 result.errors.push_back({"ec", ecResult.message});
290 }
291
292 auto nitrogenResult = validateNPK(data.nitrogen, "Азот");
293 if (!nitrogenResult.isValid)
294 {
295 result.isValid = false;
296 result.errors.push_back({"nitrogen", nitrogenResult.message});
297 }
298
299 auto phosphorusResult = validateNPK(data.phosphorus, "Фосфор");
300 if (!phosphorusResult.isValid)
301 {
302 result.isValid = false;
303 result.errors.push_back({"phosphorus", phosphorusResult.message});
304 }
305
306 auto potassiumResult = validateNPK(data.potassium, "Калий");
307 if (!potassiumResult.isValid)
308 {
309 result.isValid = false;
310 result.errors.push_back({"potassium", potassiumResult.message});
311 }
312
313 return result;
314}
315
316// ============================================================================
317// УТИЛИТЫ ВАЛИДАЦИИ
318// ============================================================================
319
320bool isValidIPAddress(const String& ip)
321{
322 int parts = 0;
323 int start = 0;
324
325 for (int i = 0; i <= ip.length(); i++)
326 {
327 if (i == ip.length() || ip.charAt(i) == '.')
328 {
329 if (i == start) return false; // Пустая часть
330
331 String part = ip.substring(start, i);
332 int value = part.toInt();
333
334 if (value < 0 || value > 255) return false;
335 if (part != String(value)) return false; // Проверка на ведущие нули
336
337 parts++;
338 start = i + 1;
339 }
340 }
341
342 return parts == 4;
343}
344
345bool isValidHostname(const String& hostname)
346{
347 if (hostname.length() == 0 || hostname.length() > 253) return false;
348
349 for (char c : hostname)
350 {
351 if (!isAlphaNumeric(c) && c != '.' && c != '-')
352 {
353 return false;
354 }
355 }
356
357 // Не должен начинаться или заканчиваться точкой или дефисом
358 if (hostname.charAt(0) == '.' || hostname.charAt(0) == '-' || hostname.charAt(hostname.length() - 1) == '.' ||
359 hostname.charAt(hostname.length() - 1) == '-')
360 {
361 return false;
362 }
363
364 return true;
365}
366
368{
369 if (result.isValid) return "";
370
371 String formatted = "Ошибки валидации:\n";
372 for (const auto& error : result.errors)
373 {
374 formatted += "• " + error.field + ": " + error.message + "\n";
375 }
376 return formatted;
377}
378
380{
381 if (result.isValid) return "";
382
383 String formatted = "Ошибки валидации датчика:\n";
384 for (const auto& error : result.errors)
385 {
386 formatted += "• " + error.field + ": " + error.message + "\n";
387 }
388 return formatted;
389}
390
391// ============================================================================
392// ЛОГИРОВАНИЕ ВАЛИДАЦИИ
393// ============================================================================
394
395void logValidationResult(const ConfigValidationResult& result, const char* context)
396{
397 if (result.isValid)
398 {
399 logSuccess("Валидация %s прошла успешно", context);
400 }
401 else
402 {
403 logError("Валидация %s не пройдена:", context);
404 for (const auto& error : result.errors)
405 {
406 logError(" %s: %s", error.field.c_str(), error.message.c_str());
407 }
408 }
409}
410
411void logSensorValidationResult(const SensorValidationResult& result, const char* context)
412{
413 if (result.isValid)
414 {
415 logSuccess("Валидация датчика %s прошла успешно", context);
416 }
417 else
418 {
419 logWarn("Валидация датчика %s не пройдена:", context);
420 for (const auto& error : result.errors)
421 {
422 logWarn(" %s: %s", error.field.c_str(), error.message.c_str());
423 }
424 }
425}
Config config
Определения config.cpp:34
Централизованные константы системы JXCT.
constexpr int CONFIG_MQTT_PORT_MAX
Определения jxct_constants.h:95
constexpr float SENSOR_TEMP_MAX
Определения jxct_constants.h:79
constexpr float SENSOR_PH_MAX
Определения jxct_constants.h:83
constexpr uint16_t SENSOR_EC_MAX
Определения jxct_constants.h:85
constexpr unsigned long CONFIG_THINGSPEAK_MIN
Определения jxct_constants.h:92
constexpr float SENSOR_HUMIDITY_MAX
Определения jxct_constants.h:81
constexpr uint16_t SENSOR_NPK_MAX
Определения jxct_constants.h:87
constexpr unsigned long CONFIG_INTERVAL_MIN
Определения jxct_constants.h:90
constexpr unsigned long CONFIG_INTERVAL_MAX
Определения jxct_constants.h:91
constexpr unsigned long CONFIG_THINGSPEAK_MAX
Определения jxct_constants.h:93
void logWarn(const char *format,...)
Определения logger.cpp:78
void logSuccess(const char *format,...)
Определения logger.cpp:129
void logError(const char *format,...)
Определения logger.cpp:61
Система логгирования с красивым форматированием
Определения validation_utils.h:58
Результат валидации конфигурации
Определения validation_utils.h:38
std::vector< ValidationError > errors
Определения validation_utils.h:40
bool isValid
Определения validation_utils.h:39
Определения modbus_sensor.h:35
float phosphorus
Определения modbus_sensor.h:41
float temperature
Определения modbus_sensor.h:36
float nitrogen
Определения modbus_sensor.h:40
float humidity
Определения modbus_sensor.h:37
float ec
Определения modbus_sensor.h:38
float ph
Определения modbus_sensor.h:39
float potassium
Определения modbus_sensor.h:42
Результат валидации данных датчика
Определения validation_utils.h:47
std::vector< ValidationError > errors
Определения validation_utils.h:49
bool isValid
Определения validation_utils.h:48
Результат валидации одного поля
Определения validation_utils.h:20
bool isValidIPAddress(const String &ip)
Проверка валидности IP адреса
Определения validation_utils.cpp:320
ValidationResult validateMQTTPublishInterval(unsigned long interval)
Валидация интервала публикации MQTT.
Определения validation_utils.cpp:105
SensorValidationResult validateFullSensorData(const SensorData &data)
Полная валидация данных датчика
Определения validation_utils.cpp:259
ValidationResult validateMQTTPort(int port)
Валидация MQTT порта
Определения validation_utils.cpp:59
ValidationResult validateInterval(unsigned long interval, unsigned long min_val, unsigned long max_val, const char *name)
Валидация интервала (общая функция)
Определения validation_utils.cpp:89
void logValidationResult(const ConfigValidationResult &result, const char *context)
Логирование результата валидации конфигурации
Определения validation_utils.cpp:395
ValidationResult validateThingSpeakInterval(unsigned long interval)
Валидация интервала ThingSpeak.
Определения validation_utils.cpp:110
ValidationResult validateNPK(float value, const char *nutrient)
Валидация NPK значений
Определения validation_utils.cpp:160
ValidationResult validateHumidity(float humidity)
Валидация влажности
Определения validation_utils.cpp:133
ValidationResult validateSensorReadInterval(unsigned long interval)
Валидация интервала чтения датчика
Определения validation_utils.cpp:100
void logSensorValidationResult(const SensorValidationResult &result, const char *context)
Логирование результата валидации датчика
Определения validation_utils.cpp:411
ValidationResult validatePH(float ph)
Валидация pH.
Определения validation_utils.cpp:142
ValidationResult validatePassword(const String &password)
Валидация пароля WiFi.
Определения validation_utils.cpp:28
ValidationResult validateMQTTServer(const String &server)
Валидация MQTT сервера
Определения validation_utils.cpp:41
bool isValidHostname(const String &hostname)
Проверка валидности hostname.
Определения validation_utils.cpp:345
ValidationResult validateEC(float ec)
Валидация электропроводности
Определения validation_utils.cpp:151
String formatSensorValidationErrors(const SensorValidationResult &result)
Форматирование ошибок валидации датчика
Определения validation_utils.cpp:379
ValidationResult validateThingSpeakAPIKey(const String &apiKey)
Валидация ThingSpeak API ключа
Определения validation_utils.cpp:68
ConfigValidationResult validateFullConfig(const ConfigData &config, bool checkRequired)
Полная валидация конфигурации
Определения validation_utils.cpp:174
String formatValidationErrors(const ConfigValidationResult &result)
Форматирование ошибок валидации конфигурации
Определения validation_utils.cpp:367
ValidationResult validateSSID(const String &ssid)
Валидация SSID.
Определения validation_utils.cpp:15
ValidationResult validateTemperature(float temperature)
Валидация температуры
Определения validation_utils.cpp:124
ValidationResult validateNTPInterval(unsigned long interval)
Валидация интервала обновления NTP.
Определения validation_utils.cpp:115
Заголовочный файл утилит валидации