From 30806e72ac038aa37f29db77fc1b4929471da7ac Mon Sep 17 00:00:00 2001 From: Alyssa Date: Tue, 22 Oct 2024 16:08:52 -0400 Subject: [PATCH 1/2] Added dynamic data support to readValue, added new functions CommonData::getMemberTypeKindById and CommonData::getNestedMemberAndIdByName to make it simpler in readValue (and for future use of replayer) --- src/dds_data.cpp | 189 ++++++++++++++++++++++++++++++++++++++++++++++- src/dds_data.h | 20 ++++- 2 files changed, 207 insertions(+), 2 deletions(-) diff --git a/src/dds_data.cpp b/src/dds_data.cpp index 3388947..544ab39 100644 --- a/src/dds_data.cpp +++ b/src/dds_data.cpp @@ -78,6 +78,61 @@ std::shared_ptr CommonData::getTopicInfo(const QString& topicName) return topicInfo; } +//------------------------------------------------------------------------------ +OpenDDS::XTypes::TypeKind CommonData::getMemberTypeKindById(const DDS::DynamicData_var& currentData, + const DDS::MemberId memberId) +{ + DDS::ReturnCode_t rc = DDS::RETCODE_OK; + DDS::DynamicType_var memberType = currentData->type(); + DDS::DynamicTypeMember_var dtm; + rc = memberType->get_member(dtm, memberId); + if (rc != DDS::RETCODE_OK) { + std::cerr << "Failed to get DynamicTypeMember for member Id " << memberId << std::endl; + } + DDS::MemberDescriptor_var md; + rc = dtm->get_descriptor(md); + if (rc != DDS::RETCODE_OK) { + std::cerr << "Failed to get MemberDescriptor for member Id " << memberId << std::endl; + } + const DDS::DynamicType_var base_type = OpenDDS::XTypes::get_base_type(md->type()); + return base_type->get_kind(); +} + +//------------------------------------------------------------------------------ +DDS::MemberId CommonData::getNestedMemberAndIdByName(DDS::DynamicData_var& currentData, + const QString& memberName) +{ + // Split memberName by '.' + QStringList memberPath = memberName.split('.'); + DDS::ReturnCode_t rc = DDS::RETCODE_OK; + DDS::MemberId memberId = currentData->get_member_id_by_name(memberName.toUtf8().data()); + + // Traverse through the structure according to the member path + for (int i = 0; i < memberPath.size(); ++i) { + const QString& currentMemberName = memberPath.at(i); + + // Find the target member within this sample + memberId = currentData->get_member_id_by_name(currentMemberName.toUtf8().data()); + if (memberId == OpenDDS::XTypes::MEMBER_ID_INVALID) { + std::cerr << "Invalid member ID for: " << currentMemberName.toStdString() << std::endl; + } + + // If it's the last part of the member path, retrieve the value + if (i == memberPath.size() - 1) { + break; + } else { + // If it's not the last part, treat it as a nested structure (complex type) and keep going + DDS::DynamicData_var nextData; + rc = currentData->get_complex_value(nextData, memberId); + if (rc != DDS::RETCODE_OK) { + std::cerr << "Failed to get complex value for: " << currentMemberName.toStdString() << std::endl; + } + currentData = nextData; // Move to the next level of the nested structure + } + } + return memberId; +} + //------------------------------------------------------------------------------ QVariant CommonData::readValue(const QString& topicName, const QString& memberName, @@ -90,9 +145,141 @@ QVariant CommonData::readValue(const QString& topicName, QList>& sampleList = m_samples[topicName]; if ((int)index >= sampleList.count()) { - value = "NULL"; + // Check for dynamic data instead + + locker.unlock(); + QMutexLocker lockerDyn(&m_dynamicSamplesMutex); + + // Make sure the index is valid + QList& sampleListDynamic = (m_dynamicSamples[topicName]); + if ((int)index >= sampleListDynamic.count()) { + value = "NULL"; + return value; + } + + DDS::DynamicData_var targetSampleDynamic = sampleListDynamic.at(index); + if (!targetSampleDynamic) { + value = "NULL"; + return value; + } + + DDS::MemberId memberId = getNestedMemberAndIdByName(targetSampleDynamic, memberName); + OpenDDS::XTypes::TypeKind member_type_kind = getMemberTypeKindById(targetSampleDynamic, memberId); + // Check the type and get the value accordingly + switch (member_type_kind) { + case OpenDDS::XTypes::TK_INT8: { + int8_t tmpValue; + if (targetSampleDynamic->get_int8_value(tmpValue, memberId) == DDS::RETCODE_OK) { + value = tmpValue; + } + break; + } + case OpenDDS::XTypes::TK_INT16: { + int16_t tmpValue; + if (targetSampleDynamic->get_int16_value(tmpValue, memberId) == DDS::RETCODE_OK) { + value = tmpValue; + } + break; + } + case OpenDDS::XTypes::TK_INT32: { + int32_t tmpValue; + if (targetSampleDynamic->get_int32_value(tmpValue, memberId) == DDS::RETCODE_OK) { + value = tmpValue; + } + break; + } + case OpenDDS::XTypes::TK_INT64: { + int64_t tmpValue; + if (targetSampleDynamic->get_int64_value(tmpValue, memberId) == DDS::RETCODE_OK) { + value = tmpValue; + } + break; + } + case OpenDDS::XTypes::TK_UINT8: { + uint8_t tmpValue; + if (targetSampleDynamic->get_uint8_value(tmpValue, memberId) == DDS::RETCODE_OK) { + value = tmpValue; + } + break; + } + case OpenDDS::XTypes::TK_UINT16: { + uint16_t tmpValue; + if (targetSampleDynamic->get_uint16_value(tmpValue, memberId) == DDS::RETCODE_OK) { + value = tmpValue; + } + break; + } + case OpenDDS::XTypes::TK_UINT32: { + uint32_t tmpValue; + if (targetSampleDynamic->get_uint32_value(tmpValue, memberId) == DDS::RETCODE_OK) { + value = tmpValue; + } + break; + } + case OpenDDS::XTypes::TK_UINT64: { + uint64_t tmpValue; + if (targetSampleDynamic->get_uint64_value(tmpValue, memberId) == DDS::RETCODE_OK) { + value = tmpValue; + } + break; + } + case OpenDDS::XTypes::TK_FLOAT32: { + float tmpValue; + if (targetSampleDynamic->get_float32_value(tmpValue, memberId) == DDS::RETCODE_OK) { + value = tmpValue; + } + break; + } + case OpenDDS::XTypes::TK_FLOAT64: { + double tmpValue; + if (targetSampleDynamic->get_float64_value(tmpValue, memberId) == DDS::RETCODE_OK) { + value = tmpValue; + } + break; + } + case OpenDDS::XTypes::TK_BOOLEAN: { + bool tmpValue; + if (targetSampleDynamic->get_boolean_value(tmpValue, memberId) == DDS::RETCODE_OK) { + value = tmpValue; + } + break; + } + case OpenDDS::XTypes::TK_CHAR8: { + char tmpValue; + if (targetSampleDynamic->get_char8_value(tmpValue, memberId) == DDS::RETCODE_OK) { + value = tmpValue; + } + break; + } + case OpenDDS::XTypes::TK_CHAR16: { + wchar_t tmpValue; + if (targetSampleDynamic->get_char16_value(tmpValue, memberId) == DDS::RETCODE_OK) { + value = tmpValue; + } + break; + } + case OpenDDS::XTypes::TK_STRING8: { + CORBA::String_var tmpValue; + if (targetSampleDynamic->get_string_value(tmpValue.out(), memberId) == DDS::RETCODE_OK) { + value = QString::fromUtf8(tmpValue); + } + break; + } + case OpenDDS::XTypes::TK_ENUM: { + uint32_t tmpValue; + if (targetSampleDynamic->get_uint32_value(tmpValue, memberId) == DDS::RETCODE_OK) { + value = tmpValue; + } + break; + } + default: + std::cerr << "TypeKind '" << OpenDDS::XTypes::typekind_to_string(member_type_kind) << "' not handled in switch" << std::endl; + value = "NULL"; + break; + } return value; } + // If it gets to here, it's not dynamic data const std::shared_ptr targetSample = sampleList.at(index); if (!targetSample) diff --git a/src/dds_data.h b/src/dds_data.h index 69a831f..3e22f45 100644 --- a/src/dds_data.h +++ b/src/dds_data.h @@ -26,7 +26,7 @@ #include #include - +#include class DDSManager; class OpenDynamicData; @@ -245,6 +245,24 @@ class CommonData const QString& memberName, const unsigned int& index = 0); + /** + * @brief Get a dynamic data member's typeKind. + * @param[in] currentData The data sample of the topic (final level if nested). + * @param[in] memberId MemberId of interest. + * @return A OpenDDS::XTypes::TypeKind containing the member's typekind. + */ + static OpenDDS::XTypes::TypeKind getMemberTypeKindById(const DDS::DynamicData_var& currentData, + const DDS::MemberId memberId); + + /** + * @brief Get a dynamic data member's memberId from its full name path. + * @param[in] currentData The data sample of the topic - this will be edited to point to the final level of a nested sample. + * @param[in] memberId The member's full path/name, including parents (ie with periods). + * @return A DDS::MemberId containing the member's ID. + */ + static DDS::MemberId getNestedMemberAndIdByName(DDS::DynamicData_var& currentData, + const QString& memberName); + /** * @brief Delete all data samples for a specified topic. * @param[in] topicName The name of the topic. From 9ca0eedf648c67bb6992be333c2dbac873fa083c Mon Sep 17 00:00:00 2001 From: Alyssa Date: Tue, 22 Oct 2024 16:37:11 -0400 Subject: [PATCH 2/2] Attempt at publishing modified dynamic data samples in replayer - existing problems: 1. DynamicData::set_*_value is returning RETCODE::UNSUPPORTED when trying to populate the cloned dynamicData sample, despite the get_int16_value and other get_*_value functions returning the correct values (ie correct memberId and datatypes). 2. Trying to write the dynamic sample (even without trying to edit the sample values) to the dynamic data writer is giving a serialization error: (42232|15776) WARNING: DynamicSample::serialized_size: DynamicDataBase::serialized_size failed! (42232|15776) ERROR: DataWriterImpl::serialize_sample: failed to serialize sample data --- src/table_page.cpp | 11 +- src/topic_replayer.cpp | 56 +++++++-- src/topic_replayer.h | 4 + src/topic_table_model.cpp | 239 ++++++++++++++++++++++++++++++++++++++ src/topic_table_model.h | 2 + 5 files changed, 296 insertions(+), 16 deletions(-) diff --git a/src/table_page.cpp b/src/table_page.cpp index bf8ff8a..8ad8095 100644 --- a/src/table_page.cpp +++ b/src/table_page.cpp @@ -291,13 +291,14 @@ void TablePage::on_revertButton_clicked() void TablePage::on_publishButton_clicked() { const std::shared_ptr sample = m_tableModel->commitSample(); - if (!sample) - { - return; - } - + const DDS::DynamicData_var dynamicSample = m_tableModel->commitDynamicSample(); + if (dynamicSample) { + m_topicReplayer->publishSample(dynamicSample); + revertButton->setEnabled(false); + } else if (sample) { m_topicReplayer->publishSample(sample); revertButton->setEnabled(false); + } } diff --git a/src/topic_replayer.cpp b/src/topic_replayer.cpp index 28fe085..c1f5d33 100644 --- a/src/topic_replayer.cpp +++ b/src/topic_replayer.cpp @@ -5,7 +5,7 @@ #include "qos_dictionary.h" #include - +#include #include #include @@ -28,16 +28,6 @@ TopicReplayer::TopicReplayer(const QString& topicName) : return; } - // Make sure the type code is valid - m_typeCode = topicInfo->typeCode; - if (m_typeCode == nullptr) - { - std::cerr << "Unable to find type code information for " - << topicName.toStdString() - << std::endl; - return; - } - //store extensibility m_extensibility = topicInfo->extensibility; @@ -45,6 +35,8 @@ TopicReplayer::TopicReplayer(const QString& topicName) : OpenDDS::DCPS::Service_Participant* service = TheServiceParticipant; DDS::DomainParticipant* domain = CommonData::m_ddsManager->getDomainParticipant(); + if (topicInfo->typeCode) { + m_typeCode = topicInfo->typeCode; m_topic = service->create_typeless_topic(domain, topicInfo->name.c_str(), topicInfo->typeName.c_str(), @@ -72,6 +64,35 @@ TopicReplayer::TopicReplayer(const QString& topicName) : std::cerr << "Failed to created replayer" << std::endl; return; } + } else { + // Use DynamicDataWriter instead. + + m_topic = domain->create_topic(topicInfo->name.c_str(), + topicInfo->typeName.c_str(), + topicInfo->topicQos, + 0, + 0); + if (!m_topic) { + std::cerr << "Failed to create topic " << topicInfo->name << " in replayer" << std::endl; + return; + } + + DDS::Publisher_var publisher = domain->create_publisher(topicInfo->pubQos,0, + 0); + if (!publisher) { + std::cerr << "Failed to create publisher for topic " << topicInfo->name << std::endl; + return; + } + + m_dw = publisher->create_datawriter(m_topic, + topicInfo->writerQos, + DDS::DataWriterListener::_nil(), + OpenDDS::DCPS::DEFAULT_STATUS_MASK); + if (!m_dw) { + std::cerr << "Failed to create data writer for topic " << topicInfo->name << std::endl; + return; + } + } //std::cout << "DEBUG Created Replayer" << std::endl; } // End TopicReplayer::TopicReplayer @@ -174,6 +195,19 @@ void TopicReplayer::publishSample(const std::shared_ptr sample) } // End TopicReplayer::publishSample +//------------------------------------------------------------------------------ +void TopicReplayer::publishSample(const DDS::DynamicData_var sample) { + DDS::DynamicDataWriter_var w = DDS::DynamicDataWriter::_narrow(m_dw); + if (!w) { + std::cerr << "DataWriter narrowing failed, m_dw is invalid" << std::endl; + return; + } + DDS::ReturnCode_t response = w->write(sample, DDS::HANDLE_NIL); + if (response != DDS::RETCODE_OK) { + + std::cout << "write() failed; response: " << response << std::endl; + } +} //------------------------------------------------------------------------------ TopicReplayer::~TopicReplayer() diff --git a/src/topic_replayer.h b/src/topic_replayer.h index 0eb15cf..614b7ad 100644 --- a/src/topic_replayer.h +++ b/src/topic_replayer.h @@ -34,6 +34,7 @@ class TopicReplayer * @param[in] sample Publish this data sample. */ void publishSample(const std::shared_ptr sample); + void publishSample(const DDS::DynamicData_var sample); /** * @brief Destructor for the DDS topic replayer. @@ -56,6 +57,9 @@ class TopicReplayer /// The topic extensibility OpenDDS::DCPS::Extensibility m_extensibility; + + /// A dynamic data writer for this topic + DDS::DataWriter_var m_dw; }; #endif diff --git a/src/topic_table_model.cpp b/src/topic_table_model.cpp index 8bb8c75..55e9a15 100644 --- a/src/topic_table_model.cpp +++ b/src/topic_table_model.cpp @@ -138,6 +138,39 @@ const std::shared_ptr TopicTableModel::commitSample() return newSample; } +//------------------------------------------------------------------------------ +const DDS::DynamicData_var TopicTableModel::commitDynamicSample() +{ + // Reset the edited state + for (size_t i = 0; i < m_data.size(); i++) + { + m_data.at(i)->edited = false; + } + + // Get sample topicInfo + std::shared_ptr topicInfo = CommonData::getTopicInfo(m_topicName); + if (!topicInfo || topicInfo->typeCode) + { + return nullptr; + } + + // Create a copy of the current sample + DDS::DynamicData_var newDynamicSample = m_dynamicsample->clone(); + if(!newDynamicSample) { + std::cerr << "Copy of current sample invalid" << std::endl; + } + + // Populate the new sample from the user-edited changes + for (size_t i = 0; i < m_data.size(); i++) + { + populateSample(newDynamicSample, m_data.at(i)); + } + + // Replace and delete the old sample + setSample(newDynamicSample); + + return newDynamicSample; +} //------------------------------------------------------------------------------ int TopicTableModel::rowCount(const QModelIndex &) const @@ -1029,6 +1062,212 @@ bool TopicTableModel::populateSample(std::shared_ptr const samp } // End TopicTableModel::populateSample +//------------------------------------------------------------------------------ +bool TopicTableModel::populateSample(DDS::DynamicData_var memberData, + DataRow* const dataInfo) +{ + bool pass = false; + if (!memberData || !dataInfo) + { + return false; + } + + const QString memberName = dataInfo->name.toUtf8().data(); + DDS::MemberId memberId = CommonData::getNestedMemberAndIdByName(memberData, memberName);///need to make sure this changes targetSampleDynamic values!! + OpenDDS::XTypes::TypeKind tk = CommonData::getMemberTypeKindById(memberData, memberId); + std::cout << "DEBUG: tk: " << OpenDDS::XTypes::typekind_to_string(tk) << std::endl; + if (!memberData) + { + std::cerr << "TopicTableModel::populateSample: " + << "Unable to find member named " + << memberName.toStdString() + << std::endl; + + return false; + } + + DDS::ReturnCode_t rc = DDS::RETCODE_OK; + std::cout << "DEBUG: memberId: " << memberId << std::endl; + switch (dataInfo->type) + { + case CORBA::tk_long: + { + int32_t tmpValue = dataInfo->value.toInt(&pass); + if (pass) + { + rc = memberData->set_int32_value(memberId, tmpValue); + } + std::cout << "DEBUG: " << dataInfo->type << " " << tmpValue << std::endl; //debug + break; + } + case CORBA::tk_short: + { + CORBA::Int16 tmpValue = dataInfo->value.toInt(&pass); + short temp; //debug + memberData->get_int16_value(temp, memberId); //debug + std::cout << "DEBUG: get_int16_value before: " << temp << std::endl; //debug + if (pass) + { + rc = memberData->set_int16_value(memberId, tmpValue); + memberData->get_int16_value(temp, memberId); + std::cout << "DEBUG: get_int16_value after attempted set: " << temp << std::endl; //debug + } + std::cout << "DEBUG: " << dataInfo->type << " " << tmpValue << std::endl; //debug + break; + } + case CORBA::tk_ushort: + { + uint16_t tmpValue = dataInfo->value.toUInt(&pass); + if (pass) + { + rc = memberData->set_uint16_value(memberId, tmpValue); + } + std::cout << "DEBUG: " << dataInfo->type << " " << tmpValue << std::endl; //debug + break; + } + case CORBA::tk_ulong: + { + uint32_t tmpValue = dataInfo->value.toUInt(&pass); + if (pass) + { + rc = memberData->set_uint32_value(memberId, tmpValue); + } + std::cout << "DEBUG: " << dataInfo->type << " " << tmpValue << std::endl; //debug + break; + } + case CORBA::tk_float: + { + float tmpValue = dataInfo->value.toFloat(&pass); + if (pass) + { + rc = memberData->set_float32_value(memberId, tmpValue); + } + std::cout << "DEBUG: " << dataInfo->type << " " << tmpValue << std::endl; //debug + break; + } + case CORBA::tk_double: + { + double tmpValue = dataInfo->value.toDouble(&pass); + if (pass) + { + rc = memberData->set_float64_value(memberId, tmpValue); + } + std::cout << "DEBUG: " << dataInfo->type << " " << tmpValue << std::endl; //debug + break; + } + case CORBA::tk_boolean: + { + if (dataInfo->value.canConvert(QMetaType::Bool)) + { + bool tmpValue = dataInfo->value.toBool(); + rc = memberData->set_boolean_value(memberId, tmpValue); + pass = true; + std::cout << "DEBUG: " << dataInfo->type << " " << tmpValue << std::endl; //debug + } + break; + } + case CORBA::tk_char: + { + if (dataInfo->value.canConvert(QMetaType::Char)) + { + char tmpValue = dataInfo->value.toChar().toLatin1(); + rc = memberData->set_char8_value(memberId, tmpValue); + pass = true; + std::cout << "DEBUG: " << dataInfo->type << " " << tmpValue << std::endl; //debug + } + break; + } + case CORBA::tk_wchar: + { + if (dataInfo->value.canConvert(QMetaType::Char)) + { + char tmpValue = dataInfo->value.toChar().toLatin1(); + rc = memberData->set_char16_value(memberId, tmpValue); + pass = true; + std::cout << "DEBUG: " << dataInfo->type << " " << tmpValue << std::endl; //debug + } + break; + } + case CORBA::tk_octet: + { + bool tmpPass = false; + int32_t octetTestVar = dataInfo->value.toInt(&tmpPass); + if (tmpPass && octetTestVar >= 0 && octetTestVar <= 255) + { + uint8_t tmpValue = static_cast(octetTestVar); + rc = memberData->set_uint8_value(memberId, tmpValue); + pass = true; + std::cout << "DEBUG: " << dataInfo->type << " " << tmpValue << std::endl; //debug + } + break; + } + case CORBA::tk_longlong: + { + int64_t tmpValue = dataInfo->value.toLongLong(&pass); + if (pass) + { + rc = memberData->set_int64_value(memberId, tmpValue); + } + std::cout << "DEBUG: " << dataInfo->type << " " << tmpValue << std::endl; //debug + break; + } + case CORBA::tk_ulonglong: + { + uint64_t tmpValue = dataInfo->value.toULongLong(&pass); + if (pass) + { + rc = memberData->set_uint64_value(memberId, tmpValue); + } + std::cout << "DEBUG: " << dataInfo->type << " " << tmpValue << std::endl; //debug + break; + } + case CORBA::tk_string: + { + rc = memberData->set_string_value(memberId, dataInfo->value.toString().toUtf8().data()); + pass = true; + std::cout << "DEBUG: " << dataInfo->type << " " << dataInfo->value.toString().toUtf8().data() << std::endl; //debug + CORBA::String_var temp = ""; //debug + memberData->get_string_value(temp.out(), memberId); //debug + std::cout << "DEBUG: get_string_value after attempted set: " << temp << std::endl; //debug + break; + } + case CORBA::tk_enum: + { + std::cout << "Enum - figure out later" << std::endl; //TODO - other errors are occurring first + int64_t tmpValue = dataInfo->value.toUInt(&pass); + rc = memberData->set_uint32_value(memberId, tmpValue); +// memberData->setStringValue(dataInfo->value.toString().toUtf8().data()); + std::cout << "DEBUG: " << dataInfo->type << " " << tmpValue << std::endl; //debug + pass = true; +// const QString enumStringValue = dataInfo->value.toString(); +// const CORBA::TypeCode* enumTypeCode = memberData->getTypeCode(); +// const CORBA::ULong enumMemberCount = enumTypeCode->member_count(); +// +// // Find the enum int value of this enum string +// for (CORBA::ULong enumIndex = 0; enumIndex < enumMemberCount; enumIndex++) +// { +// QString searchValue = enumTypeCode->member_name(enumIndex); +// if (searchValue == enumStringValue) +// { +// memberData->setValue(enumIndex); +// pass = true; +// break; +// } +// } + break; + } // End enum block + default: + std::cerr << "TopicTableModel::populateSample " + << "Skipped " + << memberName.toStdString() + << std::endl; + break; + + } // End child type switch + std::cout << "return code: " << rc << std::endl; + return pass; + +} // End TopicTableModel::populateSample //------------------------------------------------------------------------------ TopicTableModel::DataRow::DataRow() : type(CORBA::tk_null), diff --git a/src/topic_table_model.h b/src/topic_table_model.h index eb3c7eb..f913384 100644 --- a/src/topic_table_model.h +++ b/src/topic_table_model.h @@ -51,6 +51,7 @@ class TopicTableModel : public QAbstractTableModel * @remarks The returned value is the user-edited sample data to publish. */ const std::shared_ptr commitSample(); + const DDS::DynamicData_var commitDynamicSample(); /** * @brief Standard row count for table. @@ -200,6 +201,7 @@ class TopicTableModel : public QAbstractTableModel */ bool populateSample(std::shared_ptr const sample, DataRow *dataInfo); + bool populateSample(DDS::DynamicData_var const sample, DataRow *dataInfo); /// The table view using this model QTableView* m_tableView;