boost::optional<protobuf::ExperimentMetadataMsg::MetadataStruct> Experiment::read_metadata(ProjectHandlerErrorCode& err_out) { if (!meta_file_input_stream_) { err_out = ProjectHandlerErrorCode::IO_ERROR; return boost::none; } if (fseek(meta_file_handler_, 0, SEEK_SET) != 0) { // Set to start of file err_out = ProjectHandlerErrorCode::IO_ERROR; return boost::none; } io::CodedInputStream coded_input_stream(meta_file_input_stream_); uint32_t msg_size; bool result = coded_input_stream.ReadVarint32(&msg_size); //If false, => end of file protobuf::ExperimentMetadataMsg::MetadataStruct read_msg; if (result) { io::CodedInputStream::Limit msg_limit = coded_input_stream.PushLimit(msg_size); if (read_msg.ParseFromCodedStream(&coded_input_stream)) { coded_input_stream.PopLimit(msg_limit); err_out = ProjectHandlerErrorCode::SUCCESS; return read_msg; } else { err_out = ProjectHandlerErrorCode::IO_ERROR; return boost::none; } } else { err_out = ProjectHandlerErrorCode::END_OF_FILE; return boost::none; } }
/* * The metadata contains information about what sensors sensordata is stored in the given experiment. * The metadata also contains notes about the experiment that could identify the experiment later on. * * ExperimentMetadataStruct are written instead of GeneralMsg to avoid writing experiment name to file. * Writing GeneralMsg could be problematic when e.g. renaming because you then have to edit the binary data * that the medatada contains to the new name. */ ProjectHandlerErrorCode Experiment::write_metadata(const std::vector<std::string>& tags, const boost::optional<std::string>& notes) { if (fseek(meta_file_handler_, 0, SEEK_SET) != 0) { std::cout << "Could not fseek file" << std::endl; return ProjectHandlerErrorCode::IO_ERROR; } else { if (!meta_file_handler_) { std::cout << "!!meta_file_handler" << std::endl; return ProjectHandlerErrorCode::IO_ERROR; } protobuf::ExperimentMetadataMsg::MetadataStruct read_msg; protobuf::ExperimentMetadataMsg::MetadataStruct new_msg; io::CodedInputStream coded_input_stream(meta_file_input_stream_); uint32_t msg_size; bool read_result = coded_input_stream.ReadVarint32(&msg_size); if (read_result) { // If there's old metadata, read it and copy sensor_configurations only, discard other old metadata information. io::CodedInputStream::Limit msg_limit = coded_input_stream.PushLimit(msg_size); if (read_msg.ParseFromCodedStream(&coded_input_stream)) { coded_input_stream.PopLimit(msg_limit); // Copy sensor configurations google::protobuf::RepeatedPtrField< ::protobuf::SensorConfiguration > sensor_configurations = read_msg.sensor_configurations(); // If there were to be no sensor_configurations absent. The RepeatedField should be empty thus not adding any configurations. for (auto& sensor_config : sensor_configurations ) { protobuf::SensorConfiguration* sensor_configuration = new_msg.add_sensor_configurations(); sensor_configuration->CopyFrom(sensor_config); } } } for (auto& tag : tags) { new_msg.add_tags(tag); } if (notes) { new_msg.set_notes(notes.get()); } if (!::ftruncate(::fileno(meta_file_handler_), 0)) { // empty file std::cout << "Could not empty file" << std::endl; return ProjectHandlerErrorCode::IO_ERROR; } bool write_result; { io::FileOutputStream file_output_stream(::fileno(meta_file_handler_)); io::CodedOutputStream coded_output_stream(&file_output_stream); coded_output_stream.WriteVarint32(new_msg.ByteSize()); write_result = new_msg.SerializeToCodedStream(&coded_output_stream); } if (fflush(meta_file_handler_)) { std::cout << "Could not flush file" << std::endl; return ProjectHandlerErrorCode::IO_ERROR; } if (write_result) { return ProjectHandlerErrorCode::SUCCESS; } else { std::cout << "No write result" << std::endl; return ProjectHandlerErrorCode::IO_ERROR; } } }
/* * Experiment::read_protobuf_data(ProjectHandlerErrorCode& err_out) * * If it's possible to read the varin32, then there's probably data in the stream. * Because it's cheap to create and destroy google io streams, the codedinputstream * is declared locally. The varin32 that was previously read contains information about * how many bytes the stored binary message contains. You then have to * push the streams "start pointer"(limit) to then try to read the binary data until * that pointer and then try to parse the data. If the parsed data is valid you have to * pop the limit back to it's previous point. After the function goes out of scope * the io stream is destructed and when the function is called once again, the data will be * at the start of the new io stream. */ boost::optional<protobuf::GeneralMsg> Experiment::read_protobuf_data(ProjectHandlerErrorCode& err_out) { if (open_mode_ == experiment_open_mode::read) { if (!data_file_input_stream_) { err_out = ProjectHandlerErrorCode::IO_ERROR; return boost::none; } io::CodedInputStream coded_input_stream(data_file_input_stream_); uint32_t msg_size; bool result = coded_input_stream.ReadVarint32(&msg_size); protobuf::GeneralMsg read_msg; if (result) { io::CodedInputStream::Limit msg_limit = coded_input_stream.PushLimit(msg_size); if (read_msg.ParseFromCodedStream(&coded_input_stream)) { coded_input_stream.PopLimit(msg_limit); } else { err_out = ProjectHandlerErrorCode::IO_ERROR; return boost::none; } } else { err_out = ProjectHandlerErrorCode::END_OF_FILE; return boost::none; } err_out = ProjectHandlerErrorCode::SUCCESS; return read_msg; } else { err_out = ProjectHandlerErrorCode::IO_ERROR; return boost::none; } }
TEST_F(PluginCompatibilityTest, PreBourbaki) { // Read the entire hex data. std::fstream file = std::fstream( SOLUTION_DIR / "ksp_plugin_test" / "pre_bourbaki.proto.hex"); CHECK(file.good()); std::string hex; while (!file.eof()) { std::string line; std::getline(file, line); for (auto const c : line) { if ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'F')) { hex.append(1, c); } } } file.close(); // Parse it and convert to binary data. UniqueBytes bin(hex.size() / 2); HexadecimalDecode(Array<std::uint8_t const>( reinterpret_cast<const std::uint8_t*>(hex.c_str()), hex.size()), bin.get()); // Construct a protocol buffer from the binary data. google::protobuf::io::CodedInputStream coded_input_stream( bin.data.get(), static_cast<int>(bin.size)); serialization::Plugin pre_bourbaki_serialized_plugin; CHECK(pre_bourbaki_serialized_plugin.MergeFromCodedStream( &coded_input_stream)); // Construct a plugin from the protocol buffer. auto plugin = TestablePlugin::ReadFromMessage(pre_bourbaki_serialized_plugin); // Do some operations on the plugin. plugin->KeepAllVessels(); plugin->AdvanceTime(plugin->current_time() + 1 * Second, 2 * Radian); plugin->AdvanceTime(plugin->current_time() + 1 * Hour, 3 * Radian); // Serialize and deserialize it in the new format. serialization::Plugin post_bourbaki_serialized_plugin; plugin->WriteToMessage(&post_bourbaki_serialized_plugin); plugin = TestablePlugin::ReadFromMessage(post_bourbaki_serialized_plugin); }
/* * If, for some reason, you would like to be on the safe side and write all, for the time of writing, * known sensors to the metadata struct, this is the function to be called. * */ ProjectHandlerErrorCode Experiment::write_all_sensor_configurations_to_metadata() { if (fseek(meta_file_handler_, 0, SEEK_SET) != 0) { return ProjectHandlerErrorCode::IO_ERROR; } else { if (!meta_file_handler_) { return ProjectHandlerErrorCode::IO_ERROR; } protobuf::ExperimentMetadataMsg::MetadataStruct read_msg; protobuf::ExperimentMetadataMsg::MetadataStruct new_msg; io::CodedInputStream coded_input_stream(meta_file_input_stream_); uint32_t msg_size; bool read_result = coded_input_stream.ReadVarint32(&msg_size); if (read_result) { //If there's old metadata, read it and copy meta_daata only, discard other old sensor_configuration information. io::CodedInputStream::Limit msg_limit = coded_input_stream.PushLimit(msg_size); if (read_msg.ParseFromCodedStream(&coded_input_stream)) { coded_input_stream.PopLimit(msg_limit); google::protobuf::RepeatedPtrField< ::std::string> tags = read_msg.tags(); for (auto& tag : tags) { new_msg.add_tags(tag); } if (read_msg.has_notes()) { new_msg.set_notes(read_msg.notes()); } } } { protobuf::SensorConfiguration* sensor_configuration = new_msg.add_sensor_configurations(); sensor_configuration->set_sensor_id(0); sensor_configuration->set_name("imucam"); std::vector<std::string> v = {"accX","accY","accZ","gyrX","gyrY","gyrZ","magX","magY","magZ","roll","pitch","yaw"}; sensor_configuration->set_max_attributes(v.size()); int i = 0; for (auto& s : v) { protobuf::AttributeConfiguration* attribute_configuration = sensor_configuration->add_attribute_configurations(); attribute_configuration->set_index(i); attribute_configuration->set_name(s); i++; } } { protobuf::SensorConfiguration* sensor_configuration = new_msg.add_sensor_configurations(); sensor_configuration->set_sensor_id(1); sensor_configuration->set_name("imucar"); std::vector<std::string> v = {"accX","accY","accZ","gyrX","gyrY","gyrZ","magX","magY","magZ","roll","pitch","yaw"}; sensor_configuration->set_max_attributes(v.size()); int i = 0; for (auto& s : v) { protobuf::AttributeConfiguration* attribute_configuration = sensor_configuration->add_attribute_configurations(); attribute_configuration->set_index(i); attribute_configuration->set_name(s); i++; } } { protobuf::SensorConfiguration* sensor_configuration = new_msg.add_sensor_configurations(); sensor_configuration->set_sensor_id(2); sensor_configuration->set_name("carcan1"); std::vector<std::string> v = {"CAN_ID", "CAN_message_length"}; sensor_configuration->set_max_attributes(v.size()); int i = 0; for (auto& s : v) { protobuf::AttributeConfiguration* attribute_configuration = sensor_configuration->add_attribute_configurations(); attribute_configuration->set_index(i); attribute_configuration->set_name(s); i++; } } { protobuf::SensorConfiguration* sensor_configuration = new_msg.add_sensor_configurations(); sensor_configuration->set_sensor_id(3); sensor_configuration->set_name("can"); std::vector<std::string> v = {"CAN_ID", "CAN_message_length"}; sensor_configuration->set_max_attributes(v.size()); int i = 0; for (auto& s : v) { protobuf::AttributeConfiguration* attribute_configuration = sensor_configuration->add_attribute_configurations(); attribute_configuration->set_index(i); attribute_configuration->set_name(s); i++; } } { protobuf::SensorConfiguration* sensor_configuration = new_msg.add_sensor_configurations(); sensor_configuration->set_sensor_id(4); sensor_configuration->set_name("gps"); std::vector<std::string> v = {"UT-Time", "Lat", "Lon", "Speed(m/s)", "Course/Heading"}; sensor_configuration->set_max_attributes(v.size()); int i = 0; for (auto& s : v) { protobuf::AttributeConfiguration* attribute_configuration = sensor_configuration->add_attribute_configurations(); attribute_configuration->set_index(i); attribute_configuration->set_name(s); i++; } } { protobuf::SensorConfiguration* sensor_configuration = new_msg.add_sensor_configurations(); sensor_configuration->set_sensor_id(30); sensor_configuration->set_name("corrsys"); std::vector<std::string> v = {"abstime","starttime","h1","h2","h3","hc1","hc2","hc3","pitch","roll", "roll2","pitchrate","rollrate","roll2rate","vabs","vlat","vtrans","slipangle"}; sensor_configuration->set_max_attributes(v.size()); int i = 0; for (auto& s : v) { protobuf::AttributeConfiguration* attribute_configuration = sensor_configuration->add_attribute_configurations(); attribute_configuration->set_index(i); attribute_configuration->set_name(s); i++; } } if (::ftruncate(::fileno(meta_file_handler_), 0)) { // empty file return ProjectHandlerErrorCode::IO_ERROR; } bool write_result; { io::FileOutputStream file_output_stream(::fileno(meta_file_handler_)); io::CodedOutputStream coded_output_stream(&file_output_stream); coded_output_stream.WriteVarint32(new_msg.ByteSize()); write_result = new_msg.SerializeToCodedStream(&coded_output_stream); } if (fflush(meta_file_handler_)) { return ProjectHandlerErrorCode::IO_ERROR; } if (write_result) { return ProjectHandlerErrorCode::SUCCESS; } else { return ProjectHandlerErrorCode::IO_ERROR; } } return ProjectHandlerErrorCode::SUCCESS; }
/* * The metadata should contain data of which sensors were present during * a given data collection. This is so that the client should know which * sensors the user could choose to get before a playback of the collection. * * The function simply updates the MetadataStruct in the metadata message * in the metadata file(.meta) */ ProjectHandlerErrorCode Experiment::write_sensor_configuration_metadata(const std::vector<boost::shared_ptr<AbstractSensor>>& sensors) { if (fseek(meta_file_handler_, 0, SEEK_SET) != 0) { return ProjectHandlerErrorCode::IO_ERROR; } else { if (!meta_file_handler_) { return ProjectHandlerErrorCode::IO_ERROR; } protobuf::ExperimentMetadataMsg::MetadataStruct read_msg; protobuf::ExperimentMetadataMsg::MetadataStruct new_msg; io::CodedInputStream coded_input_stream(meta_file_input_stream_); uint32_t msg_size; bool read_result = coded_input_stream.ReadVarint32(&msg_size); if (read_result) { //If there's old metadata, read it and copy meta_daata only, discard other old sensor_configuration information. io::CodedInputStream::Limit msg_limit = coded_input_stream.PushLimit(msg_size); if (read_msg.ParseFromCodedStream(&coded_input_stream)) { coded_input_stream.PopLimit(msg_limit); google::protobuf::RepeatedPtrField< ::std::string> tags = read_msg.tags(); for (auto& tag : tags) { new_msg.add_tags(tag); } if (read_msg.has_notes()) { new_msg.set_notes(read_msg.notes()); } } } std::map<int,std::string> attribute_names; for (auto& sensor : sensors) { protobuf::SensorConfiguration* sensor_configuration = new_msg.add_sensor_configurations(); sensor_configuration->set_sensor_id(sensor->get_id()); sensor_configuration->set_name(sensor->get_name()); sensor_configuration->set_max_attributes(sensor->get_max_attributes()); auto iterators = sensor->get_attribute_iterators(); for (auto itr = iterators.first; itr != iterators.second; ++itr) { protobuf::AttributeConfiguration* attribute_configuration = sensor_configuration->add_attribute_configurations(); attribute_configuration->set_index(itr->first); attribute_configuration->set_name(itr->second.attr_name); attribute_names.insert(std::pair<int,std::string>(itr->first, itr->second.attr_name)); } } if (::ftruncate(::fileno(meta_file_handler_), 0)) { // empty file return ProjectHandlerErrorCode::IO_ERROR; } bool write_result; { io::FileOutputStream file_output_stream(::fileno(meta_file_handler_)); io::CodedOutputStream coded_output_stream(&file_output_stream); coded_output_stream.WriteVarint32(new_msg.ByteSize()); write_result = new_msg.SerializeToCodedStream(&coded_output_stream); } if (fflush(meta_file_handler_)) { return ProjectHandlerErrorCode::IO_ERROR; } if (write_result) { std::string text_file_header("time, ID"); for (auto& attr : attribute_names) { text_file_header.append(", " + attr.second); } return write_text_data(text_file_header); } else { return ProjectHandlerErrorCode::IO_ERROR; } } }