So I have couple of virtual goggle .. each one of them has different calibration parameters. I decided to save these parameters into a yaml file (as a configuration file) .. each goggle has its own serial/identification number ... and based on this number, I select which one to use. If there is no pre-saved information for the goggle. I calibrate it and I add these parameters into the file
So right now I am trying to write to a yaml file which looks like this :
Headset:
IdentificationNumber: b630cc42-9a03-42da-a039-0e023cf5b090
GyroOffset:
GyroX:
Value: -0.013776619
GyroY:
Value: -0.016475508
GyroZ:
Value: -0.0114268782
and this is what I get actually:
Headset2:
IdentificationNumber: b630cc42-9a03-42da-a039-0e023cf5b090
? GyroOffset:
GyroX:
Value: -0.013776619
? GyroY:
Value: -0.016475508
: GyroZ:
Value: -0.0114268782
I do not figure out what I am doing wrong ! .. here is my function which writes to the yaml file:
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());
}
}
The main problem seems to be that you're building up on a lot of misinformation. I'll try to clear some things:
std::ios::app
, which will always create a new entry. Instead, you should load the file into a YAML node, modify the contents of that node, and then write back the whole node.? GyroOffset
at the same depth as Headset2:
, making it a sibling of Headset2
. Also note that mixing implicit (foo:
) with explicit (? foo
) keys in the same mapping is kind-of a corner case that can confuses some implementations. The YAML file could simply look like this:Headset2:
IdentificationNumber: b630cc42-9a03-42da-a039-0e023cf5b090
GyroOffset:
GyroX:
Value: -0.012388126
GyroY:
Value: -0.0155748781
GyroZ:
Value: -0.0115196211
To make your code more readable, I suggest to use helper classes to access your values. Assuming above code is the whole YAML file, it could look like this:
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"]); }
}
Now, finding the GyroOffset of a given identification number looks like this (I show a simple function because I don't know your class' fields as you don't show them):
// 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;
}
Since YAML::Node
is basically a reference, when you change values inside the returned GyroOffset
, the original data changes. You can then write the root node back to the file (not append it) and have an updated file.
Appending a new headset would look like this:
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;
}
While I tried to adhere to the structure you show, I have the feeling that the actual key in the mapping should not be Headset2
, but the IdentificationNumber:
b630cc42-9a03-42da-a039-0e023cf5b090:
Name: Headset2
GyroOffset:
GyroX:
Value: -0.012388126
GyroY:
Value: -0.0155748781
GyroZ:
Value: -0.0115196211
Since you do lookup based on ID, this would make more sense. Also, creating a new config will actually work (currently, due to the hardcoded "Headset2"
value, it will always overwrite that headset if it exists).
Beware, I wrote the code as demonstration and did not test it; there may be errors.