Как создать или записать карту в файл yaml? C ++ - PullRequest
0 голосов
/ 23 января 2020

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

Итак, сейчас я пытаюсь записать файл yaml, который выглядит следующим образом:

Headset:
  IdentificationNumber: b630cc42-9a03-42da-a039-0e023cf5b090
  GyroOffset:
    GyroX:
      Value: -0.013776619
    GyroY:
      Value: -0.016475508
    GyroZ:
      Value: -0.0114268782

, и это то, что я получаю на самом деле :

Headset2:
  IdentificationNumber: b630cc42-9a03-42da-a039-0e023cf5b090
? GyroOffset:
    GyroX:
      Value: -0.013776619
  ? GyroY:
      Value: -0.016475508
  : GyroZ:
      Value: -0.0114268782

Я не понимаю, что я делаю неправильно! .. вот моя функция, которая пишет в файл yaml:

void ParseInputDeviceYaml::addCalibrationToConfigFile(const char* identificationNumber, const float* in)
{
    try {
        std::ofstream updatedFile;
        updatedFile.open(m_filename.toStdString(), std::ios::app);

        std::map<std::string, std::string>                  IDNumber;
        std::map<std::string, std::map<std::string, float>> gyroXOffset;
        std::map<std::string, std::map<std::string, float>> gyroYOffset;
        std::map<std::string, std::map<std::string, float>> gyroZOffset;

        IDNumber["IdentificationNumber"] = identificationNumber;

        gyroXOffset["GyroX"]["Value"] = *in;
        gyroYOffset["GyroY"]["Value"] = *(in + 1);
        gyroZOffset["GyroZ"]["Value"] = *(in + 2);

        YAML::Emitter newNode;


        newNode << YAML::BeginMap;
        newNode << YAML::Key << "Headset2";
        newNode << YAML::Value << YAML::BeginMap << YAML::Key << "IdentificationNumber" << YAML::Value << identificationNumber << YAML::EndMap;
        newNode << YAML::BeginMap << YAML::Key << "GyroOffset" << YAML::Value << gyroXOffset << gyroYOffset << gyroZOffset << YAML::EndMap;
        newNode << YAML::EndMap;

        updatedFile << newNode.c_str() << "\n";

        updatedFile.close();
    } catch (std::exception& e) {
        LOG4CPLUS_FATAL(m_logger, e.what());
        throw std::runtime_error(QObject::tr("Writing gyroscope offsets ").toStdString());
    }
}

1 Ответ

0 голосов
/ 24 января 2020

Основная проблема, кажется, в том, что вы накапливаете много дезинформации. Я попытаюсь прояснить некоторые вещи:

  • Использование последовательностей или нет не имеет абсолютно никакого отношения к возможности изменять существующие значения или добавлять новые значения в файл. Проблема в том, что вы добавляете файл, используя std::ios::app, который всегда создает новую запись. Вместо этого вы должны загрузить файл в узел YAML, изменить содержимое этого узла и затем записать весь узел обратно.
  • Файл YAML без заданной вами последовательности, безусловно, не выполняет то, что вы думаете, он делает поскольку вы размещаете ? GyroOffset на той же глубине, что и Headset2:, что делает его родным братом Headset2. Также обратите внимание, что смешивание неявных (foo:) с явными (? foo) ключами в одном и том же отображении является своего рода угловым случаем, который может сбить с толку некоторые реализации. Файл YAML может выглядеть просто так:
Headset2:
  IdentificationNumber: b630cc42-9a03-42da-a039-0e023cf5b090
  GyroOffset:
    GyroX:
      Value: -0.012388126
    GyroY:
      Value: -0.0155748781
    GyroZ:
      Value: -0.0115196211

Чтобы сделать ваш код более читабельным, я предлагаю использовать вспомогательные классы для доступа к вашим значениям. Предполагая, что приведенный выше код представляет собой весь файл YAML, он может выглядеть следующим образом:

struct Value {
  YAML::Node data;
  // access existing node
  explicit Value(YAML::Node data): data(data) {
    assert(data.IsMapping());
  }
  // create new node
  explicit Value(float value) {
    data["Value"] = value;
  }

  float get() { return data["Value"].as<float>(); }
  void set(float value) { data["Value"] = value; }
};

struct GyroOffset {
  YAML::Node data;
  explicit GyroOffset(YAML::Node data): data(data) {
    assert(data.IsMapping());
  }
  GyroOffset(float x, float y, float z) {
    data["GyroX"] = Value(x).data;
    data["GyroY"] = Value(y).data;
    data["GyroZ"] = Value(z).data;
  }
  Value gyroX() { return Value(data["GyroX"]); }
  Value gyroX() { return Value(data["GyroY"]); }
  Value gyroZ() { return Value(data["GyroZ"]); }
};

struct Headset {
  YAML::Node data;
  Headset(YAML::Node data): data(data) {
    assert(data.IsMapping());
  }
  Headset(const char *id) {
    data["IdentificationNumber"] = id;
    // initialize with zero values
    data["GyroOffset"] = GyroOffset(0, 0, 0).data;
  }

  std::string id() { return data["IdentificationNumber"].as<std::string>(); }
  void setId(const char *value) { data["IdentificationNumber"] = value; }

  GyroOffset gyroOffset() { return GyroOffset(data["GyroOffset"]); }
}

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

// write found values to output of found
bool findHedasetGyroOffset(Yaml::Node &input /* the file as shown above */, const char *id, GyroOffset &output) {
  for (auto it = input.begin(); it != input.end(); ++it) {
    Headset hs(it->second);
    if (hs.id() == id) {
      output = hs.gyroOffset();
      return true;
    }
  }
  return false;
}

Поскольку YAML::Node в основном является ссылкой, при изменении значений внутри возвращенного GyroOffset исходные данные изменяются. Затем можно записать узел root обратно в файл ( не добавить его) и получить обновленный файл.

Добавление новой гарнитуры будет выглядеть следующим образом:

void addCalibrationToConfigFile(Yaml::Node &file, const char* identificationNumber, const float* in) {
  Headset newHs(identificationNumber);
  auto go = newHs.gyroOffset();
  go.gyroX().set(*in);
  go.gyroY().set(*(in + 1));
  go.gyroZ().set(*(in + 2));
  // note that this will overwrite an existing Headset2
  file["Headset2"] = newHs.data;
}

Пока я пытался придерживаться структуры, которую вы показываете, у меня есть ощущение, что фактический ключ в отображении должен быть не Headset2, а IdentificationNumber:

b630cc42-9a03-42da-a039-0e023cf5b090:
  Name: Headset2
  GyroOffset:
    GyroX:
      Value: -0.012388126
    GyroY:
      Value: -0.0155748781
    GyroZ:
      Value: -0.0115196211

Так как вы делаете поиск на основе идентификатора, это будет иметь больше смысла. Кроме того, создание новой конфигурации действительно будет работать (в настоящее время из-за жестко заданного значения "Headset2" она всегда будет перезаписывать эту гарнитуру, если она существует).

Осторожно, я написал код в качестве демонстрации и не тестировал Это; могут быть ошибки.

...