Получить большой get запрос и скормить его json библиотеке

ckret
Offline
Зарегистрирован: 04.05.2018

Парни, кто подскажет, два дня гоняю микроконтроллер, не хватает навыков дойти до решения...

Необходимо взять тут https://bank.gov.ua/NBUStatService/v1/statdirectory/exchange?date=20190705&json, потом распарсить.

Когда беру через GetString, в итоге получается обрубленный файл.

Нашел информацию, что можно большие данные получить через  getStreamPtr, в Serial выводит все, но как это собрать в String не допру...

Пните в нужном направлении пожалуйта!

void largeString ()
{
    std::unique_ptr<BearSSL::WiFiClientSecure>client(new BearSSL::WiFiClientSecure);

   client->setInsecure();
   HTTPClient https;




    if (https.begin(*client, "https://bank.gov.ua/NBUStatService/v1/statdirectory/exchange?date=20190705&json") ) 
    {
      Serial.print("[HTTPS] GET...\n");
      // start connection and send HTTP header
      int httpCode = https.GET();
      if (httpCode > 0) {
        // HTTP header has been send and Server response header has been handled
        Serial.printf("[HTTPS] GET... code: %d\n", httpCode);

        // file found at server
        if (httpCode == HTTP_CODE_OK) {

          // get lenght of document (is -1 when Server sends no Content-Length header)
          int len = https.getSize();

          // create buffer for read
          static uint8_t buff[128] = { 0 };

          // read all data from server
          while (https.connected() && (len > 0 || len == -1)) {
            // get available data size
            size_t size = client->available();

            if (size) {
              // read up to 128 byte
              int c = client->readBytes(buff, ((size > sizeof(buff)) ? sizeof(buff) : size));

              // write it to Serial
              Serial.write(buff, c);
            
              if (len > 0) {
                len -= c;
              }
            }
            delay(1);
          }

          Serial.println();
          Serial.print("[HTTPS] connection closed or file end.\n");
       
        }
      } else {
        Serial.printf("[HTTPS] GET... failed, error: %s\n", https.errorToString(httpCode).c_str());
      }

      https.end();
    } else {
      Serial.printf("Unable to connect\n");
    }
  
  Serial.printf("Free RAM: %d\n", ESP.getFreeHeap());
  Serial.println("Wait 5s before the next round...");

}

 

b707
Offline
Зарегистрирован: 26.05.2017

раз он через getString весь не берется - значит его в String собирать не нужно - все равно не войдет. Нужно приниматиь кусками и сразу парсить

ckret
Offline
Зарегистрирован: 04.05.2018

А не подскажите, как Serial.write(buff, c) в line запихнуть? 
DeserializationError error = deserializeJson(doc,line)

b707
Offline
Зарегистрирован: 26.05.2017

ckret пишет:

А не подскажите, как Serial.write(buff, c) в line запихнуть? 
DeserializationError error = deserializeJson(doc,line)

а какой тип у line ?

ckret
Offline
Зарегистрирован: 04.05.2018

String

b707
Offline
Зарегистрирован: 26.05.2017

сорри, сразу не вчитался в вопрос, теперь стало еще непонятнее.

Что куда запихнуть хотите?

ckret
Offline
Зарегистрирован: 04.05.2018

Если по частям парсить, то подойдет надо взять данные тут  Serial.write(buff, c), привести их к String line и уже дальше распарсить json. 

DeserializationError error = deserializeJson(doc,line);

b707
Offline
Зарегистрирован: 26.05.2017

ckret пишет:

Если по частям парсить, то подойдет надо взять данные тут  Serial.write(buff, c), привести их к String line и уже дальше распарсить json.

да вот я и не понял, как можно взять данные из write. Для того, чтобы взять данные из Сериал, используется функция read

ckret
Offline
Зарегистрирован: 04.05.2018

Serial.write(buf, len) забыли, то просто пример. Как мне получить эти же данные из приведенного кода в String line

Morroc
Offline
Зарегистрирован: 24.10.2016

ckret пишет:

Нашел информацию, что можно большие данные получить через  getStreamPtr, в Serial выводит все, но как это собрать в String не допру...

Что нибудь типа такого не работает ?
 
WiFiClient * stream = &https;
 
DeserializationError error = deserializeJson(doc, stream);
negavoid
Offline
Зарегистрирован: 09.07.2016

Просто поразительное у нас число людей, забаненных в гугле.

https://github.com/squix78/json-streaming-parser

 

Morroc
Offline
Зарегистрирован: 24.10.2016

В принципе конкретно в этом случае можно распарсить и самому прям при считывании посимвольно... делов то :)

ckret
Offline
Зарегистрирован: 04.05.2018

Йухуу 

    while (client->available()) {
     int dddd = client -> parseInt();
     
     if (dddd ==959)  Serial.println (client -> parseFloat()); 
     if (dddd ==840)  Serial.println (client -> parseFloat());
     if (dddd ==36)  Serial.println (client -> parseFloat());        
    }

Потратил три дня )) Проблема не в лени или нежелании набивать шишки, проблема в длительности поиска нужной дороги. Особенно обидно, когда на настройку сервера обновления ушло 30 минут, а на простой парсинг несколько суток.

b707
Offline
Зарегистрирован: 26.05.2017

ckret -  приведенный вами пример никакой не парсинг....

ckret
Offline
Зарегистрирован: 04.05.2018

Но ска. работает быстро. То что нужно.

Morroc
Offline
Зарегистрирован: 24.10.2016

Если я правильно понял, то проблема была в обработке большого объема json, а у вас вроде просто преобразование строки символов в число получилось. Да куда денется... конечно работать будет, просто выцепляет числа из потока символов, если формат json простой - может и сойдет, но вообще было бы очень желательно отлавливать в потоке метки/теги перед числами чтобы не промахнуться.

b707
Offline
Зарегистрирован: 26.05.2017

ckret пишет:

Но ска. работает быстро.

 "Я печатаю 3000 знаков в минуту... но такая фигня получается" :)

У вас парсинг получился очень не специфический. Например, практически в любом среднего размера HTML обязательно попадется комбинация цифр "36" - после которого ваш код выцепит из входящего потока какой-то флоат, скорее всего не имеющий ни малейшего отношения к тем данным, что вам нужны

 

b707
Offline
Зарегистрирован: 26.05.2017

ckret пишет:

Йухуу 

    while (client->available()) {
     int dddd = client -> parseInt();
     
     if (dddd ==959)  Serial.println (client -> parseFloat()); 
     if (dddd ==840)  Serial.println (client -> parseFloat());
     if (dddd ==36)  Serial.println (client -> parseFloat());        
    }

 

добавлю еще вот что - вышеприведенный код еще и работает не такк, как ожидается.  Вам, скорее всего, нужно извлечь из json флоат, расположенный после цифр "959", так? - А этот код ищет во входящем потоке любой флоат, а не расположенный после 959...

В общем, сорри, но парсингом это не назовешь. Мне даже кажется, что условия поиска цифр можно вообще выкинуть, результат не поменяется

ckret
Offline
Зарегистрирован: 04.05.2018

Файл json практически константа, поэтому нет ничего, что помешает логике поиска целого числа, с последующим взятием float. Как по мне, это один из наиболее быстрых способов, которые мне удалось получить за последние дни. 

 

b707
Offline
Зарегистрирован: 26.05.2017

ckret пишет:

Файл json практически константа, поэтому нет ничего, что помешает логике поиска целого числа, с последующим взятием float.

в вашем коде нет "последующего взятия" флоат

Цитата:
Как по мне, это один из наиболее быстрых способов, которые мне удалось получить за последние дни.

скажите лучше, что вы так и не поняли, как скопировать строковый буфер в переменную и провести в ней поиск строки, поэтому и придумали эту ерунду

Впрочем, дело ваше. Когда вы поймете, что ваш метод дает ответ "через два раза на третий" - может измените мнение.

Morroc
Offline
Зарегистрирован: 24.10.2016

Я бы такое

{ 
"r030":36,"txt":"Австралійський долар","rate":18.165588,"cc":"AUD","exchangedate":"05.07.2019"
 }

ловил по тегам и после ":" брал значение до "," или конца строки, но в принципе да... если нужно только 2-3 валюты, то можно ловить их код и float за ним - с какой то вероятностью работать будет, но это ж блин криво )

b707
Offline
Зарегистрирован: 26.05.2017

Morroc пишет:

Я бы такое

{ 
"r030":36,"txt":"Австралійський долар","rate":18.165588,"cc":"AUD","exchangedate":"05.07.2019"
 }

ловил по тегам и после ":" брал значение до "," или конца строки, но в принципе да... если нужно только 2-3 валюты, то можно ловить их код и float за ним - с какой то вероятностью работать будет, но это ж блин криво )

Morroc, как вы думаете, какой флоат извлечет ТС из такого json:


{ 
"examplefield":23.789,"r030":36,"txt":"Австралійський долар","rate":18.165588,"cc":"AUD","exchangedate":"05.07.2019"
 }

18.166 ? - а может 23.789 ?

ckret
Offline
Зарегистрирован: 04.05.2018

Может и есть способ, который более эффективнее, но повторюсь, этот результат самое быстрое и компактное, что у меня получилось.

Список состоит из 13 валют.

Их индексы > 100, что исключает ошибочное срабатывание, поэтому  в цикле сравниваем parseInt()), при совпадении берем первый за ним float и повторям. 
 

 State nbuState[13] = {
   {203, "CZK",0},
   {826, "USD",0},
   {978, "EUR",0},
   {643, "RUB",0},
   {756, "CHF",0},
   {826, "GBP",0},
   {985, "PLN",0},
   {933, "BYN",0},
   {946, "RON",0},
   {959, "XAU",0},
   {961, "XAG",0},
   {962, "XPT",0},
   {964, "XPD",0}
};

 

b707
Offline
Зарегистрирован: 26.05.2017

ckret пишет:

 в цикле сравниваем parseInt()), при совпадении берем первый за ним float

повторяю, ваш код работает не так

Morroc
Offline
Зарегистрирован: 24.10.2016

b707 пишет:

Morroc, как вы думаете, какой флоат извлечет ТС из такого json:

{ 
"examplefield":23.789,"r030":36,"txt":"Австралійський долар","rate":18.165588,"cc":"AUD","exchangedate":"05.07.2019"
 }

18.166 ? - а может 23.789 ?

Если последовательно выцопывать целое, потом флоат, опять целое... то по идее такого не будет, при сбое разве что (а даты не мешают читать числа, кстати ?), но идея ориентироваться только на числа в целом стремная.

ckret
Offline
Зарегистрирован: 04.05.2018

Не понимаю, почему " ваш код работает не так", вроде бы все данные получаются... Возможно время 6 сек на обработку не такое маленькое, но к сожалению лучше пока добиться не смог.
 

Затраченное время: 6243
Элемент CZK: 1.15
Элемент USD: 25.87
Элемент EUR: 29.20
Элемент RUB: 0.41
Элемент CHF: 26.26
Элемент GBP: 32.55
Элемент PLN: 6.88
Элемент BYN: 12.63
Элемент RON: 6.19
Элемент XAU: 36613.70
Элемент XAG: 396.08
Элемент XPT: 21550.40
Элемент XPD: 40280.89
 

   while (client->available()) {
     int dddd = client -> parseInt();
    
    for(int i=0; i<13;i++){
     if (dddd == nbuState[i].value)  nbuState[i].data = client -> parseFloat(); 
  }    
   }

    for(int i=0; i<13;i++){
    Serial.print("Элемент ");
    Serial.print(nbuState[i].name);
    Serial.print(": ");
    Serial.println(nbuState[i].data);

 

Morroc
Offline
Зарегистрирован: 24.10.2016

Че то много 6 секунд или это вместе со временем запроса ?

ckret пишет:

Не понимаю, почему " ваш код работает не так", вроде бы все данные получаются...

Пока нет сбоев при получени ответа. Прилетит что то скособоченное - может распарситься криво.

ckret
Offline
Зарегистрирован: 04.05.2018
Время обработки: 5106
Затраченное время: 6176
:(
 
Я видел на сайте JSON  deserializeJson(doc, stream), но не разобрался как  HTTPClient передается, возможно такой вариант был бы шустрее.
 

 

b707
Offline
Зарегистрирован: 26.05.2017

Morroc пишет:

Если последовательно выцопывать целое, потом флоат, опять целое... то по идее такого не будет

тут весь вопрос в том, смещается ли указатель буфера после выполнения client->parseInt() или следующий запрос снова начинает сначала? - я  этого не знаю

По моей логике должен с начала начинать, тогда код ТС глючить будет. Если нет - тогда работать будет, но неустойчиво и криво, до первого сбоя. Или пока, к примеру, во каком-нить другом поле не попадется "36"

BOOM
BOOM аватар
Offline
Зарегистрирован: 14.11.2018

Правильно (на мой взгляд) искать уникальные идентификаторы и отталкиваться от них. А цифры в качестве идентификатоа нужного элемента - это до первого обновления выдачи (как верно выше заметили - само значение может быть равно 36 и что вернёт функция тогда?). Есть хорошая функция lastIndexOf() и substring(). Ими я бы и оперировал. Найти нужное выражение и отталкиваться от него. 

Странно такое долгое время обработки. Или это вместе с запросами и ожиданием ответа?

ckret
Offline
Зарегистрирован: 04.05.2018

5 секунд это без запроса, скорее всего это связанно с тем, что проверяется каждое полученное число на совпадению с 13  вариантами, думаю используя уникальные индентификаторы, как вы подметили, поможет сделать функцию более универсальной и сократить время в разы, скоро попробую.

b707
Offline
Зарегистрирован: 26.05.2017

ckret пишет:

скорее всего это связанно с тем, что проверяется каждое полученное число на совпадению с 13  вариантами

нет, сравнение каждого числа с 13 вариантами занимает буквально микросекунды. Основное время уходит на команды parseInt() и parseFloat(), поскольку в них есть встроенный таймаут. Каждый раз, когда эта команда не находит числа в тексте - она зависает на целую секунду. Время таймаута можно уменьшить, если вызвать функцию SetTimeout(x), где х - таймаут в мс   Но еще правильнее эти две функции в серьезных программах избегать, это костыль для вебдизайнеров (читай для тех, кто Си не знает :).

BOOM
BOOM аватар
Offline
Зарегистрирован: 14.11.2018

Странное поведение функций - зависание на секунду. Зачем вообще такое может быть нужно? Тайм-аут для чего?

ckret
Offline
Зарегистрирован: 04.05.2018

:))) Недооценил я мощности ESP ... Нормально так удочка помогла ))

Поставил 10 . 

Затраченное время с запросом: 1250
Время обработки буфера: 115

Поставил 0

Затраченное время: 1179
Время обработки: 109

b707
Offline
Зарегистрирован: 26.05.2017

BOOM пишет:
Тайм-аут для чего?

для уверенности, что число принято полностью. Например parseInt ждет на входе цифры, за которыми обязательно должны идти НЕ_ЦИФРОВЫЕ символы. Это нужно для определения, что ввод закончен. А если это не так - функция будет ждать секунду. чтобы не оказалось, что принятые цифры "25" - это на самом деле начало числа 25678

b707
Offline
Зарегистрирован: 26.05.2017

ckret пишет:

:))) Недооценил я мощности ESP ... Нормально так удочка помогла ))

вы о чем? - что за удочка?

BOOM
BOOM аватар
Offline
Зарегистрирован: 14.11.2018

b707 пишет:

что за удочка?

Тараканов из головы «выуживать» может?))

ckret
Offline
Зарегистрирован: 04.05.2018

Каждая подксазка, это своего рода удочка рыбаку, а рыбу он должен сам ловить.
Вообще сарказм с тараканами не уместен. Написать код - это сложно, написать код не зная всех тонкостей, сложно втройне.

BOOM
BOOM аватар
Offline
Зарегистрирован: 14.11.2018

ckret пишет:

:))) Недооценил я мощности ESP ... Нормально так удочка помогла ))

Поставил 10 . 

Затраченное время с запросом: 1250
Время обработки буфера: 115

Поставил 0

Затраченное время: 1179
Время обработки: 109

А куда вы эти цифры ставите? К иконе ближе? Я что то не соображу....