Arduino ESP редактирует большой файл JSON — выравнивание износа

У меня есть большой файл JSON, содержащий около 1000 объектов.
Использование https://arduinojson.org/ для сериализации / десериализовать.

void ConfigFile_Save_Variable(String VarName, String VarValue) {
  Serial.print("ConfigFile_Save_Variable: ");
  Serial.print(VarName); Serial.print("="); Serial.print(VarValue);

  File configFile = SPIFFS.open("/config.json", "r");
  if (!configFile) {
    Serial.println("- failed to open config file for writing");
    return;
  }

  DynamicJsonDocument doc(2048);
  deserializeJson(doc, configFile);
  serializeJson(doc, Serial);
  configFile.close();
  //
  doc[VarName] = VarValue;
  //
  configFile = SPIFFS.open("/config.json", "w");
  serializeJson(doc, configFile);
  serializeJson(doc, Serial);
  configFile.close();
  Serial.println("");
  Serial.println(" - config.json saved - OK.");

Если я хочу отредактировать только 1 объект и сохранить файл JSON, весь файл записывается заново.
Это плохо для выравнивания износа, так как все данные записываются снова, хотя был изменен только 1 объект.

Поэтому я ищу способ записать только изменение, а не весь файл. Пробовал искать везде безуспешно.


person strange_esp    schedule 03.09.2020    source источник
comment
Возможно, Arduino недостаточно мощен для ваших целей. Рассматривали ли вы некоторые RaspBerryPi?   -  person Basile Starynkevitch    schedule 03.09.2020
comment
@BasileStarynkevitch Похоже, проблема не в мощности, а в том, как файловый API обрабатывает сохранение изменений. Но вопрос в основном сводится к «есть ли библиотека, которая может сохранять только измененные части файлов», а вопросы о библиотеках и т. д. не по теме. Конечно, без контекста того, какие изменения вносятся и как ожидается, будут обрабатываться таким образом, поскольку, например. вставка/удаление по определению потребует перезаписи всего после точки модификации.   -  person underscore_d    schedule 03.09.2020


Ответы (2)


Лучший способ справиться с этим — разбить JSON — записать каждый объект верхнего уровня в отдельный файл. У вас уже есть метки для объектов, используйте их в качестве имен файлов с префиксом /config/ или /c/, если вам нужно больше места для имени метки.

Давайте посмотрим, почему то, что вы просите от SPIFF, не работает.

JSON сериализуется для хранения в виде текста. Например, объект

{
  label_0: {
    label_a: "small string",
    label_b: 1
    },
  label_1: {
    label_c: "another string",
    label_d: "more string"
  }
}

сериализуется как

{"label_0":{"label_a":"small string","label_b":1},"label_1":{"label_c":"another string","label_d":"more string"}}

SPIFFS разбивает сохраненные файлы на страницы фиксированного размера. Он должен каждый раз записывать целую страницу во флэш-память - он не может просто записывать измененные байты; это не то, как работает флэш-память.

Предположим, что label_a становится короче - просто "s". Тогда все после label_a пришлось бы сдвигать в файле вниз. Чтобы SPIFFS сделал то, что вы просите, он должен написать страницу и помнить, что используется только часть страницы.

Если значение label_a станет больше - "this is a much longer string" то в файле все придется сдвигать вверх. SPIFFS должен был выделить новую страницу для хранения переполнения и помнить, что использовалась только ее часть.

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

Это слишком много для простой файловой системы.

Давайте также посмотрим на ситуацию. Насколько велик файл и как часто он записывается? Если у вас есть 1000 объектов, и каждый объект занимает, скажем, 200 байт для хранения, ваш JSON будет составлять около 20 000 байт. В среднем ESP8266 или ESP32 имеют 4 МБ флэш-памяти, из которых 1,5 МБ доступно для SPIFFS. Вы можете хранить 20 000 байт 75 раз в 1,5 МБ, поэтому у вас, вероятно, есть коэффициент около 75x или немного меньше для выравнивания износа.

Флэш-память в большинстве ESP8266 и ESP32 рассчитана на 100 000 операций записи (на практике она обычно длится намного дольше). Таким образом, с выравниванием износа вы, вероятно, могли бы записать файл размером 20 000 байт 750 000 раз, не ожидая появления ошибок.

Вы на самом деле собираетесь писать этот файл даже 1000 или 10 000 раз?

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

Если нет, то это на самом деле не проблема.

person romkey    schedule 03.09.2020

TLDR: ручное выравнивание износа очень сложно, и выравнивание в любом случае может быть автоматическим. Я бы выделил часто изменяемые параметры конфигурации в другой файл, чтобы вы каждый раз писали меньше и записывали на диск только тогда, когда это абсолютно необходимо (например, в конце программы).

Если длина файла json изменится, то есть: вы измените true на false, весь файл должен быть перезаписан, потому что пространство [диска], которое должно быть выделено для файла, теперь изменилось. Если вы зарезервируете определенное количество символов для каждого значения конфигурации или используете только x цифр в качестве значений конфигурации, вы сможете напрямую перезаписать двоичные данные на диске, вообще не взаимодействуя с файлом. Но для этого потребовалось бы очень низкоуровневое программирование и знание точных адресов памяти. Кроме того, поскольку вы используете флэш-память для хранения, вам все равно придется читать, обновлять и записывать полный блок памяти для каждого внесенного изменения. Со временем, особенно если вы регулярно обновляете только одно значение, это может нанести больше вреда диску в долгосрочной перспективе по сравнению с полной перезаписью.

Настольные твердотельные накопители автоматически выравнивают износ по всему диску. Если esp делает то же самое, вам не нужно беспокоиться о выравнивании износа самостоятельно, а лучше сохранить минимальный размер файла, когда вы его переписываете.

Учитывая вышеизложенное, одним из возможных решений было бы выделение часто переписываемых параметров конфигурации в отдельный файл и сохранение редко переписываемых в основном файле. Вы также можете хранить все данные конфигурации в памяти и перезаписывать файл только в конце программы, хотя это применимо только в том случае, если вы не запускаете/останавливаете свою программу постоянно. Доведя разделение до крайности, вы теоретически можете использовать отдельный файл для каждой опции конфигурации, что позволит вам переписывать только одно значение за раз.

person 61616    schedule 03.09.2020