Парсинг текстового файла от датчиков температуры
- Войдите на сайт для отправки комментариев
Добрый день.
Просьба помочь.
Показания датчиков температуры пишутся с MEGA2560 в data.txt по времени(часы:минуты), каждые 10 мин. По каждому датчику - две температуры: set и act. Сколько датчиков будет - заранее неизвестно, от 1 до 16, например. Затем идет запрос от юзера на показания конкретного датчика (по id) за заданный период времени - от (часы:минуты) до (часы:минуты).
Нужно выбрать из файла нужные строки, отвечающие заданному промежутку времени, и из каждой строки вытянуть set и act по запрашиваемому id.
Строку представляю себе такую: "18:22;id=1:14.50:28.20;id=2:23.60:29.10;...id=n:16.00:22.00;\n".
Могу написать другую.
Результат нужен в виде массивов hour[], minute[], set[], act[].
Читал про функцию strtok, но не могу сформировать правильный код.
Благодарен за любую помощь.
выделите время специальными маркерами, например #18:22# - будет проще искать
остальное можно оставить как у вас.
Далее просто - находите время, далее читаете данные до символа точка с запятой ';' - это будет показание одного датчика. Разбиваете на поля по символу ':'. извлекаете id.сравниваете с нужным, если надо сохраняете поля в массивы
Спасибо за быстрый ответ.
Логика мне понятна и не вызывает вопросов. Проблема - написать код. Хотя бы похожий пример нужен.
По определению, текстовый формат лога - он ближе к human readable, со всеми вытекающими, т.е., применительно к текущей задаче - относительной сложностью парсинга. Особенно, когда записи имеют разную длину, и формально разделяются только известными разделителями (например, переводом строки). В этом случае для того, чтобы вычитать только диапазон (по тому же времени) - приходится делать кучу оверхеда - вычитывать все строки, пропускать ненужные и т.п.
Решение по оптимизации, в принципе, лежит на поверхности: необязательно отказываться от текстового формата, достаточно постулировать, что все записи имеют одинаковую длину (пустое место забивать пробелами, например), а в начале записи лежит N символов метки времени.
Тогда по быстродействию всё сильно веселее в случае поиска диапазона (постулируем, что данные в логе идут по времени от меньшего к большему, т.е. сортированы): делим размер файла на 2, тыкаемся от этой позиции, находим начало записи (оно тоже должно быть формализовано, например, каким-нибудь набором символов). Нашли начало записи? Запомнили позицию этой записи в файле. Ну а далее, зная позицию средней записи в файле и длину записи, простым тупым делением пополам очень быстро находим начало диапазона. И от него уже вычитываем все строки до конца диапазона. Экономия по операциям чтения с файла - колоссальная: чтобы понять, подпадает ли строка под диапазон - достаточно вычитать N байт метки времени, вместо того, чтобы читать всё строку.
Быстрее - будет только бинарный формат. Но он - не human readable, от слова "совсем".
Евгений, я видел этот пример. Спасибо. Только не смог его доработать. Я из Java, мне тут перестраиваться сложно. Там насплитил и всё.
Делаю в стрингах пока, потом вышлю - может кто поможет оптимизировать под чары. А то на стринги все ругаются за память.
А что за клиент читает данные для построения выходных данных из лога? Куда именно этот файл данных data.txt пишется? Ну и в качестве "бреда" - имеется ли возможность все это передать на ПК?
Данные нужны для построения графика температур на ПК по запросу, не регулярно. Файл пишется на SD карту.
В общем получился у меня такой код. Проверил - работает.
Строка из файла имеет вид: "#7:18:22#id=1:14.50:28.20;id=2:23.60:29.10;...id=n:16.00:22.00;;". Первая "7" - это день недели.
Для извлечения подстроки используется отдельный метод, найденный на просторах интернета.
Однако, для парса используется String, а я столько начитался про то, что он съедает целиком плату Arduino и всё, что находится рядом с ней, что теперь у меня трясутся коленки.
Поэтому, если найдутся знатоки, которые помогут перевести этот код на сhar, буду очень признателен.
#include <SD.h> #include <SPI.h> #define NUMBER_OF_SENSORS 4 #define MAX_ROWS_NUMBER 100 File myFile; void setup() { Serial.begin(9600); pinMode(40, OUTPUT); if (!SD.begin(40)) { while (1); } dataFromSDCard("200515.txt", 2); } void dataFromSDCard(String askDataFile, byte id) { uint8_t askHourFrom = 10; uint8_t askMinFrom = 30; uint8_t askHourTo = 20; uint8_t askMinTo = 30; uint16_t indexRow = 0; String askId = "id=" + String(id); String buffer1, buffer2; uint8_t resultDoW; uint8_t resultHour[MAX_ROWS_NUMBER]; uint8_t resultMin[MAX_ROWS_NUMBER]; int8_t resultSetTemp[MAX_ROWS_NUMBER]; int8_t resultActTemp[MAX_ROWS_NUMBER]; myFile = SD.open(askDataFile, FILE_READ); if (myFile) { while (myFile.available()) { buffer1 = myFile.readStringUntil('\n'); buffer2 = getSplitFromString(buffer1, '#', 1); resultDoW = getSplitFromString(buffer2, ':' , 0).toInt(); resultHour[indexRow] = getSplitFromString(buffer2, ':' , 1).toInt(); resultMin[indexRow] = getSplitFromString(buffer2, ':' , 2).toInt(); if (resultHour[indexRow] * 60 + resultMin[indexRow] >= askHourFrom * 60 + askMinFrom && resultHour[indexRow] * 60 + resultMin[indexRow] <= askHourTo * 60 + askMinTo) { buffer2 = getSplitFromString(buffer1, '#', 2); if (buffer2.length() > 10) { for (byte b = 0; b < NUMBER_OF_SENSORS; b++) { buffer1 = getSplitFromString(buffer2, ';', b); if (getSplitFromString(buffer1, ':' , 0).equals(askId)) { char charSet[8]; char charAct[8]; getSplitFromString(buffer1, ':' , 1).toCharArray(charSet, 8); getSplitFromString(buffer1, ':' , 2).toCharArray(charAct, 8); resultSetTemp[indexRow] = round(atof(charSet)); resultActTemp[indexRow] = round(atof(charAct)); indexRow++; break; } } } } } } } } String getSplitFromString(String data, char separator, int index) { int found = 0; int strIndex[] = {0, -1}; int maxIndex = data.length() - 1; for (int i = 0; i <= maxIndex && found <= index; i++) { if (data.charAt(i) == separator || i == maxIndex) { found++; strIndex[0] = strIndex[1] + 1; strIndex[1] = (i == maxIndex) ? i + 1 : i; } } return found > index ? data.substring(strIndex[0], strIndex[1]) : ""; } void loop() { }Да, небось, всё не съест. Параметры только по значению не передавайте. Вот здесь почитайте, там прямо написано как и почему нужно передавать String.
Данные нужны для построения графика температур на ПК по запросу, не регулярно. Файл пишется на SD карту.
Я бы реализовал это так - передавал бы данные при любой возможности на ПК и загружал в СУБД, а из СУБД строил бы любые отчеты.
Спасибо, Евгений. Очень познавательно и весело )).
Код поправил - &.
"Фигурные скобки – великая вещь!"