From 900c777ec0a4afd6fc52292f2a67a5938d5818b3 Mon Sep 17 00:00:00 2001 From: David Pierret Date: Tue, 4 May 2021 13:06:56 +0200 Subject: [PATCH 01/12] Add publisher capability to pyopendds Add basic support for publisher know issue : Segfault after write() call Signed-off-by: David Pierret --- pyopendds/DataWriter.py | 31 ++++- pyopendds/DomainParticipant.py | 2 +- pyopendds/Publisher.py | 7 +- pyopendds/dev/include/pyopendds/user.hpp | 57 ++++++--- pyopendds/dev/itl2py/CppOutput.py | 30 ++++- pyopendds/dev/itl2py/PythonOutput.py | 2 +- pyopendds/dev/itl2py/templates/user.cpp | 37 +++++- pyopendds/ext/_pyopendds.cpp | 89 ++++++++++++++ pyopendds/init_opendds.py | 4 +- tests/basic_test/CMakeLists.txt | 7 ++ tests/basic_test/DataReaderListenerImpl.cpp | 93 ++++++++++++++ tests/basic_test/DataReaderListenerImpl.h | 48 ++++++++ tests/basic_test/publisher.py | 34 +++++ tests/basic_test/run_test.sh | 29 ++++- tests/basic_test/subscriber.cpp | 130 ++++++++++++++++++++ 15 files changed, 569 insertions(+), 31 deletions(-) create mode 100644 tests/basic_test/DataReaderListenerImpl.cpp create mode 100644 tests/basic_test/DataReaderListenerImpl.h create mode 100644 tests/basic_test/publisher.py create mode 100644 tests/basic_test/subscriber.cpp diff --git a/pyopendds/DataWriter.py b/pyopendds/DataWriter.py index a6beee8..c418265 100644 --- a/pyopendds/DataWriter.py +++ b/pyopendds/DataWriter.py @@ -1,2 +1,29 @@ -class DataWriter: - pass +from __future__ import annotations + +from .Topic import Topic +from .constants import StatusKind +from .util import TimeDurationType, normalize_time_duration + +from typing import TYPE_CHECKING +if TYPE_CHECKING: + from .Publisher import Publisher + + +class DataWriter(object): + + def __init__(self, publisher: Publisher, topic: Topic, qos=None, listener=None): + self.topic = topic + self.qos = qos + self.listener = listener + self.publisher = publisher + publisher.writers.append(self) + + from _pyopendds import create_datawriter + create_datawriter(self, publisher, topic) + + def wait_for(self, status: StatusKind, timeout: TimeDurationType): + from _pyopendds import datareader_wait_for + return datareader_wait_for(self, status, *normalize_time_duration(timeout)) + + def write(self, sample): + return self.topic._ts_package.write(self, sample) diff --git a/pyopendds/DomainParticipant.py b/pyopendds/DomainParticipant.py index 862b423..64d9bd8 100644 --- a/pyopendds/DomainParticipant.py +++ b/pyopendds/DomainParticipant.py @@ -3,7 +3,7 @@ from .Publisher import Publisher -class DomainParticipant: +class DomainParticipant(object): def __init__(self, domain: int, qos=None, listener=None): self.domain = int(domain) diff --git a/pyopendds/Publisher.py b/pyopendds/Publisher.py index a292b85..e3a65e1 100644 --- a/pyopendds/Publisher.py +++ b/pyopendds/Publisher.py @@ -1,5 +1,6 @@ from __future__ import annotations +from .DataWriter import DataWriter from .Topic import Topic from typing import TYPE_CHECKING @@ -7,7 +8,7 @@ from .DomainParticipant import DomainParticipant -class Publisher: +class Publisher(object): def __init__(self, participant: DomainParticipant, qos=None, listener=None): participant.publishers.append(self) @@ -18,5 +19,5 @@ def __init__(self, participant: DomainParticipant, qos=None, listener=None): from _pyopendds import create_publisher create_publisher(self, participant) - def create_datawriter(self, topic: Topic, qos=None, listener=None): - pass + def create_datawriter(self, topic: Topic, qos=None, listener=None) -> DataWriter: + return DataWriter(self, topic, qos, listener) diff --git a/pyopendds/dev/include/pyopendds/user.hpp b/pyopendds/dev/include/pyopendds/user.hpp index aa7c7fd..387b457 100644 --- a/pyopendds/dev/include/pyopendds/user.hpp +++ b/pyopendds/dev/include/pyopendds/user.hpp @@ -30,7 +30,7 @@ class IntegerType { static PyObject* get_python_class() { - return PyLong_Type; + return PyLong_FromLong(0); } static void cpp_to_python(const T& cpp, PyObject*& py) @@ -45,24 +45,23 @@ class IntegerType { static void python_to_cpp(PyObject* py, T& cpp) { - LongType value; + long value; if (limits::is_signed) { - value = PyLong_AsLong(py); + value = PyLong_AsLong(py); } else { - value = PyLong_AsUnsignedLong(py); + value = PyLong_AsUnsignedLong(py); } - if (value < limits::min() || value > limits::max()) { - throw Exception( - "Integer Value is Out of Range for IDL Type", PyExc_ValueError); - } - if (value == -1 && PyErr_Occurred()) throw Exception(); - cpp = value; + cpp = T(value); } + }; typedef ::CORBA::Long i32; template<> class Type: public IntegerType {}; +typedef ::CORBA::Short i16; +template<> class Type: public IntegerType {}; + // TODO: Put Other Integer Types Here const char* string_data(const std::string& cpp) @@ -90,7 +89,7 @@ class StringType { public: static PyObject* get_python_class() { - return PyUnicode_Type; + return PyUnicode_FromString(""); } static void cpp_to_python(const T& cpp, PyObject*& py, const char* encoding) @@ -101,9 +100,14 @@ class StringType { py = o; } - static void python_to_cpp(PyObject* py, T& cpp) + static void python_to_cpp(PyObject* py, T& cpp, const char* encoding) { - // TODO: Encode or Throw Unicode Error + PyObject* repr = PyObject_Repr(py); + PyObject* str = PyUnicode_AsEncodedString(repr, "utf-8", "~E~"); + const char *bytes = PyBytes_AS_STRING(str); + cpp = T(bytes); + Py_XDECREF(repr); + Py_XDECREF(str); } }; @@ -127,6 +131,7 @@ class TopicTypeBase { virtual const char* type_name() = 0; virtual void register_type(PyObject* pyparticipant) = 0; virtual PyObject* take_next_sample(PyObject* pyreader) = 0; + virtual PyObject* write(PyObject* pywriter, PyObject* pysample) = 0; typedef std::shared_ptr Ptr; typedef std::map TopicTypes; @@ -215,7 +220,7 @@ class TopicType : public TopicTypeBase { DDS::WaitSet_var ws = new DDS::WaitSet; ws->attach_condition(read_condition); DDS::ConditionSeq active; - const DDS::Duration_t max_wait_time = {10, 0}; + const DDS::Duration_t max_wait_time = {60, 0}; if (Errors::check_rc(ws->wait(active, max_wait_time))) { throw Exception(); } @@ -234,6 +239,30 @@ class TopicType : public TopicTypeBase { return rv; } + PyObject* write(PyObject* pywriter, PyObject* pysample) + { + DDS::DataWriter* writer = get_capsule(pywriter); + if (!writer) PyErr_SetString(PyExc_Exception, "writer is a NULL pointer"); + + DataWriter* writer_impl = DataWriter::_narrow(writer); + if (!writer_impl) { + throw Exception( + "Could not narrow writer implementation", Errors::PyOpenDDS_Error()); + } + + IdlType rv; + Type::python_to_cpp(pysample, rv); + + DDS::ReturnCode_t rc = writer_impl->write(rv, DDS::HANDLE_NIL); + throw Exception( + "ERROR", Errors::PyOpenDDS_Error()); + if (Errors::check_rc(rc)) { + throw Exception(); + } + + return pysample; + } + PyObject* get_python_class() { return Type::get_python_class(); diff --git a/pyopendds/dev/itl2py/CppOutput.py b/pyopendds/dev/itl2py/CppOutput.py index b922eb0..0c7589e 100644 --- a/pyopendds/dev/itl2py/CppOutput.py +++ b/pyopendds/dev/itl2py/CppOutput.py @@ -44,7 +44,9 @@ def visit_struct(self, struct_type): struct_to_lines = [ 'Ref field_value;', ] - struct_from_lines = [] + struct_from_lines = [ + 'Ref field_value;', + ] for field_name, field_node in struct_type.fields.items(): to_lines = [] from_lines = [] @@ -61,16 +63,35 @@ def visit_struct(self, struct_type): + (', "{default_encoding}"' if is_string else '') + ');', ] + from_lines = [ + 'if (PyObject_HasAttrString(py, "{field_name}")) {{', + ' *field_value = PyObject_GetAttrString(py, "{field_name}");', + '}}', + 'if (!field_value) {{', + ' throw Exception();', + '}}' + ] + pyopendds_type = cpp_type_name(field_node.type_node) if to_lines: to_lines.extend([ - 'if (!field_value || PyObject_SetAttrString(' + 'if (!field_value || PyObject_SetAttrString(', 'py, "{field_name}", *field_value)) {{', ' throw Exception();', '}}' ]) + if from_lines: + from_lines.extend([ + 'Type<{pyopendds_type}>::python_to_cpp(*field_value, cpp.{field_name}', + '#ifdef CPP11_IDL', + ' ()', + '#endif', + ' ' + + (', "{default_encoding}"' if is_string else '') + ');' + ]) + def line_process(lines): return [''] + [ s.format( @@ -107,9 +128,6 @@ def visit_enum(self, enum_type): 'args = PyTuple_Pack(1, PyLong_FromLong(static_cast(cpp)));', ]), 'to_lines': '', - 'from_lines': '\n'.join([ - '', - '// left unimplemented' - ]), + 'from_lines': '', 'is_topic_type': False, }) diff --git a/pyopendds/dev/itl2py/PythonOutput.py b/pyopendds/dev/itl2py/PythonOutput.py index 42920d3..9efaf6a 100644 --- a/pyopendds/dev/itl2py/PythonOutput.py +++ b/pyopendds/dev/itl2py/PythonOutput.py @@ -98,4 +98,4 @@ def visit_enum(self, enum_type): dict(name=name, value=value) for name, value in enum_type.members.items() ], ), - )) + )) \ No newline at end of file diff --git a/pyopendds/dev/itl2py/templates/user.cpp b/pyopendds/dev/itl2py/templates/user.cpp index e57c9ca..fa5e882 100644 --- a/pyopendds/dev/itl2py/templates/user.cpp +++ b/pyopendds/dev/itl2py/templates/user.cpp @@ -59,7 +59,21 @@ class Type { static void python_to_cpp(PyObject* py, /*{{ type.cpp_name }}*/& cpp) { PyObject* cls = get_python_class(); - /*{{ type.from_lines | indent(4) }}*/ + /*{% if type.to_replace %}*/ + cpp = static_cast(PyLong_AsLong(py)); + /*{% else %}*/ + if (py) { + + if (PyObject_IsInstance(py, cls) != 1) { + throw Exception("Not a {{ type.py_name }}", PyExc_TypeError); + } + } else { + PyObject* args; + /*{{ type.new_lines | indent(6) }}*/ + py = PyObject_CallObject(cls, args); + } + /*{% if type.from_lines %}*//*{{ type.from_lines | indent(4) }}*//*{% endif %}*/ + /*{% endif %}*/ } }; @@ -119,9 +133,30 @@ PyObject* pytake_next_sample(PyObject* self, PyObject* args) } } +PyObject* pywrite(PyObject* self, PyObject* args) +{ + Ref pywriter; + Ref pysample; + if (!PyArg_ParseTuple(args, "OO", &*pywriter, &*pysample)) return nullptr; + pywriter++; + + // Try to Get Reading Type and Do write + Ref pytopic = PyObject_GetAttrString(*pywriter, "topic"); + if (!pytopic) return nullptr; + Ref pytype = PyObject_GetAttrString(*pytopic, "type"); + if (!pytype) return nullptr; + + try { + return TopicTypeBase::find(*pytype)->write(*pywriter, *pysample); + } catch (const Exception& e) { + return e.set(); + } +} + PyMethodDef /*{{ native_package_name }}*/_Methods[] = { {"register_type", pyregister_type, METH_VARARGS, ""}, {"type_name", pytype_name, METH_VARARGS, ""}, + {"write", pywrite, METH_VARARGS, ""}, {"take_next_sample", pytake_next_sample, METH_VARARGS, ""}, {nullptr, nullptr, 0, nullptr} }; diff --git a/pyopendds/ext/_pyopendds.cpp b/pyopendds/ext/_pyopendds.cpp index 0f16aaa..34b7785 100644 --- a/pyopendds/ext/_pyopendds.cpp +++ b/pyopendds/ext/_pyopendds.cpp @@ -366,6 +366,59 @@ PyObject* create_datareader(PyObject* self, PyObject* args) Py_RETURN_NONE; } +/** + * Callback for Python to Call when the DataWriter Capsule is Deleted + */ +void delete_datawriter_var(PyObject* writer_capsule) +{ + if (PyCapsule_CheckExact(writer_capsule)) { + DDS::DataWriter_var writer = static_cast( + PyCapsule_GetPointer(writer_capsule, nullptr)); + writer = nullptr; + } +} + +/** + * create_datawriter(datawriter: DataWriter, publisher: Publisher, topic: Topic) -> None + */ +PyObject* create_datawriter(PyObject* self, PyObject* args) +{ + Ref pydatawriter; + Ref pypublisher; + Ref pytopic; + if (!PyArg_ParseTuple(args, "OOO", + &*pydatawriter, &*pypublisher, &*pytopic)) { + return nullptr; + } + pydatawriter++; + pypublisher++; + pytopic++; + + // Get Publisher + DDS::Publisher* publisher = get_capsule(*pypublisher); + if (!publisher) return nullptr; + + // Get Topic + DDS::Topic* topic = get_capsule(*pytopic); + if (!topic) return nullptr; + + // Create DataWriter + DDS::DataWriter* datawriter = publisher->create_datawriter( + topic, DATAWRITER_QOS_DEFAULT, nullptr, + OpenDDS::DCPS::DEFAULT_STATUS_MASK); + if (!datawriter) { + PyErr_SetString(Errors::PyOpenDDS_Error(), "Failed to Create DataWriter"); + return nullptr; + } + + // Attach OpenDDS DataWriter to DataWriter Python Object + if (set_capsule(*pydatawriter, datawriter, delete_datawriter_var)) { + return nullptr; + } + + Py_RETURN_NONE; +} + /** * datareader_wait_for( * datareader: DataReader, status: StatusKind, @@ -400,6 +453,40 @@ PyObject* datareader_wait_for(PyObject* self, PyObject* args) Py_RETURN_NONE; } +/** + * datawriter_wait_for( + * datawriter: DataWriter, status: StatusKind, + * seconds: int, nanoseconds: int) -> None + */ +PyObject* datawriter_wait_for(PyObject* self, PyObject* args) +{ + Ref pydatawriter; + unsigned status; + int seconds; + unsigned nanoseconds; + if (!PyArg_ParseTuple(args, "OIiI", + &*pydatawriter, &status, &seconds, &nanoseconds)) { + return nullptr; + } + pydatawriter++; + + // Get DataWriter + DDS::DataWriter* writer = get_capsule(*pydatawriter); + if (!writer) return nullptr; + + // Wait + DDS::StatusCondition_var condition = writer->get_statuscondition(); + condition->set_enabled_statuses(status); + DDS::WaitSet_var waitset = new DDS::WaitSet; + if (!waitset) return PyErr_NoMemory(); + waitset->attach_condition(condition); + DDS::ConditionSeq active; + DDS::Duration_t max_duration = {seconds, nanoseconds}; + if (Errors::check_rc(waitset->wait(active, max_duration))) return nullptr; + + Py_RETURN_NONE; +} + /// Documentation for Internal Python Objects const char* internal_docstr = "Internal to PyOpenDDS, not for use directly!"; @@ -414,7 +501,9 @@ PyMethodDef pyopendds_Methods[] = { {"create_publisher", create_publisher, METH_VARARGS, internal_docstr}, {"create_topic", create_topic, METH_VARARGS, internal_docstr}, {"create_datareader", create_datareader, METH_VARARGS, internal_docstr}, + {"create_datawriter", create_datawriter, METH_VARARGS, internal_docstr}, {"datareader_wait_for", datareader_wait_for, METH_VARARGS, internal_docstr}, + {"datawriter_wait_for", datawriter_wait_for, METH_VARARGS, internal_docstr}, {nullptr, nullptr, 0, nullptr} }; diff --git a/pyopendds/init_opendds.py b/pyopendds/init_opendds.py index 8c537ad..986d947 100644 --- a/pyopendds/init_opendds.py +++ b/pyopendds/init_opendds.py @@ -1,6 +1,7 @@ '''Manage the initialization of OpenDDS and related functionality. ''' +import sys def init_opendds(*args, default_rtps=True, @@ -19,12 +20,13 @@ def init_opendds(*args, verbose). It is printed to stdout. ''' - args = list(args) + args = list(sys.argv[1:]) if opendds_debug_level > 0: if not (1 <= opendds_debug_level <= 10): raise ValueError('OpenDDS debug level must be between 0 and 10!') args.extend(['-DCPSDebugLevel', str(opendds_debug_level)]) + print (f" arguments = {args}\n") from _pyopendds import init_opendds_impl init_opendds_impl(*args, default_rtps=default_rtps) diff --git a/tests/basic_test/CMakeLists.txt b/tests/basic_test/CMakeLists.txt index 4b8b942..a5a8bb9 100644 --- a/tests/basic_test/CMakeLists.txt +++ b/tests/basic_test/CMakeLists.txt @@ -23,3 +23,10 @@ if(${CPP11_IDL}) set_target_properties(publisher PROPERTIES COMPILE_DEFINITIONS "CPP11_IDL") endif() + +add_executable(subscriber subscriber.cpp DataReaderListenerImpl.cpp) +target_link_libraries(subscriber OpenDDS::OpenDDS basic_idl) +if(${CPP11_IDL}) + set_target_properties(subscriber PROPERTIES + COMPILE_DEFINITIONS "CPP11_IDL") +endif() diff --git a/tests/basic_test/DataReaderListenerImpl.cpp b/tests/basic_test/DataReaderListenerImpl.cpp new file mode 100644 index 0000000..a164871 --- /dev/null +++ b/tests/basic_test/DataReaderListenerImpl.cpp @@ -0,0 +1,93 @@ +/* + * + * + * Distributed under the OpenDDS License. + * See: http://www.opendds.org/license.html + */ + +#include +#include + +#include "DataReaderListenerImpl.h" +#include "basicTypeSupportC.h" +#include "basicTypeSupportImpl.h" + +#include + +void +DataReaderListenerImpl::on_requested_deadline_missed( + DDS::DataReader_ptr /*reader*/, + const DDS::RequestedDeadlineMissedStatus& /*status*/) +{ +} + +void +DataReaderListenerImpl::on_requested_incompatible_qos( + DDS::DataReader_ptr /*reader*/, + const DDS::RequestedIncompatibleQosStatus& /*status*/) +{ +} + +void +DataReaderListenerImpl::on_sample_rejected( + DDS::DataReader_ptr /*reader*/, + const DDS::SampleRejectedStatus& /*status*/) +{ +} + +void +DataReaderListenerImpl::on_liveliness_changed( + DDS::DataReader_ptr /*reader*/, + const DDS::LivelinessChangedStatus& /*status*/) +{ +} + +void +DataReaderListenerImpl::on_data_available(DDS::DataReader_ptr reader) +{ + basic::ReadingDataReader_var reader_i = + basic::ReadingDataReader::_narrow(reader); + + if (!reader_i) { + ACE_ERROR((LM_ERROR, + ACE_TEXT("ERROR: %N:%l: on_data_available() -") + ACE_TEXT(" _narrow failed!\n"))); + ACE_OS::exit(1); + } + + basic::Reading sample; + DDS::SampleInfo info; + + DDS::ReturnCode_t error = reader_i->take_next_sample(sample, info); + + if (error == DDS::RETCODE_OK) { + std::cout << "SampleInfo.sample_rank = " << info.sample_rank << std::endl; + std::cout << "SampleInfo.instance_state = " << info.instance_state << std::endl; + + if (info.valid_data) { + std::cout << "Message: kind = " << sample.kind << std::endl + << " value = " << sample.value << std::endl + << " where = " << sample.where << std::endl; + + } + + } else { + ACE_ERROR((LM_ERROR, + ACE_TEXT("ERROR: %N:%l: on_data_available() -") + ACE_TEXT(" take_next_sample failed!\n"))); + } +} + +void +DataReaderListenerImpl::on_subscription_matched( + DDS::DataReader_ptr /*reader*/, + const DDS::SubscriptionMatchedStatus& /*status*/) +{ +} + +void +DataReaderListenerImpl::on_sample_lost( + DDS::DataReader_ptr /*reader*/, + const DDS::SampleLostStatus& /*status*/) +{ +} \ No newline at end of file diff --git a/tests/basic_test/DataReaderListenerImpl.h b/tests/basic_test/DataReaderListenerImpl.h new file mode 100644 index 0000000..79955a5 --- /dev/null +++ b/tests/basic_test/DataReaderListenerImpl.h @@ -0,0 +1,48 @@ +/* + * + * + * Distributed under the OpenDDS License. + * See: http://www.opendds.org/license.html + */ + +#ifndef DATAREADER_LISTENER_IMPL_H +#define DATAREADER_LISTENER_IMPL_H + +#include + +#include +#include +#include + +class DataReaderListenerImpl + : public virtual OpenDDS::DCPS::LocalObject { +public: + virtual void on_requested_deadline_missed( + DDS::DataReader_ptr reader, + const DDS::RequestedDeadlineMissedStatus& status); + + virtual void on_requested_incompatible_qos( + DDS::DataReader_ptr reader, + const DDS::RequestedIncompatibleQosStatus& status); + + virtual void on_sample_rejected( + DDS::DataReader_ptr reader, + const DDS::SampleRejectedStatus& status); + + virtual void on_liveliness_changed( + DDS::DataReader_ptr reader, + const DDS::LivelinessChangedStatus& status); + + virtual void on_data_available( + DDS::DataReader_ptr reader); + + virtual void on_subscription_matched( + DDS::DataReader_ptr reader, + const DDS::SubscriptionMatchedStatus& status); + + virtual void on_sample_lost( + DDS::DataReader_ptr reader, + const DDS::SampleLostStatus& status); +}; + +#endif /* DATAREADER_LISTENER_IMPL_H */ \ No newline at end of file diff --git a/tests/basic_test/publisher.py b/tests/basic_test/publisher.py new file mode 100644 index 0000000..ab09dfb --- /dev/null +++ b/tests/basic_test/publisher.py @@ -0,0 +1,34 @@ +import sys +import time +from datetime import timedelta + +from pyopendds import \ + init_opendds, DomainParticipant, StatusKind, PyOpenDDS_Error +from pybasic.basic import Reading, ReadingKind + +if __name__ == "__main__": + try: + # Initialize OpenDDS and Create DDS Entities + init_opendds(opendds_debug_level=1) + domain = DomainParticipant(34) + topic = domain.create_topic('Readings', Reading) + publisher = domain.create_publisher() + writer = publisher.create_datawriter(topic) + + # Wait for Publisher to Connect + print('Waiting for Subscriber...') + writer.wait_for(StatusKind.PUBLICATION_MATCHED, timedelta(seconds=60)) + print('Found subscriber!') + + sample = Reading() + sample.kind = ReadingKind.acceleration + sample.value = 123 + sample.where = "somewhere" + + time.sleep(1) + # Read and Print Sample + writer.write(sample) + print('Done!') + + except PyOpenDDS_Error as e: + sys.exit(e) diff --git a/tests/basic_test/run_test.sh b/tests/basic_test/run_test.sh index 1591bda..a527fe2 100644 --- a/tests/basic_test/run_test.sh +++ b/tests/basic_test/run_test.sh @@ -5,20 +5,45 @@ sub=$! cd $dir ./publisher -DCPSConfigFile ../rtps.ini & pub=$! +cd - exit_status=0 wait $pub pub_status=$? if [ $pub_status -ne 0 ] then - echo "Publisher exited with status $pub_status" 1>&2 + echo "Cpp publisher exited with status $pub_status" 1>&2 exit_status=1 fi wait $sub sub_status=$? if [ $sub_status -ne 0 ] then - echo "Subscriber exited with status $sub_status" 1>&2 + echo "Python subscriber exited with status $sub_status" 1>&2 + exit_status=1 +fi + +cd $dir +./subscriber -DCPSConfigFile ../rtps.ini & +sub=$! +cd - + +LD_LIBRARY_PATH="$LD_LIBRARY_PATH:$dir" python3 publisher.py & +pub=$! + +exit_status=0 +wait $pub +pub_status=$? +if [ $pub_status -ne 0 ] +then + echo "Python publisher exited with status $pub_status" 1>&2 + exit_status=1 +fi +wait $sub +sub_status=$? +if [ $sub_status -ne 0 ] +then + echo "Cpp subscriber exited with status $sub_status" 1>&2 exit_status=1 fi exit $exit_status diff --git a/tests/basic_test/subscriber.cpp b/tests/basic_test/subscriber.cpp new file mode 100644 index 0000000..36789d4 --- /dev/null +++ b/tests/basic_test/subscriber.cpp @@ -0,0 +1,130 @@ +#include + +#include +#include +#include +#include +#include +#include + +#include "DataReaderListenerImpl.h" +#include "basicTypeSupportImpl.h" + +#include + +using OpenDDS::DCPS::retcode_to_string; + +int main(int argc, char* argv[]) { + + try { + // Init OpenDDS + TheServiceParticipant->default_configuration_file("rtps.ini"); + DDS::DomainParticipantFactory_var opendds = + TheParticipantFactoryWithArgs(argc, argv); + + DDS::DomainParticipantQos part_qos; + opendds->get_default_participant_qos(part_qos); + DDS::DomainParticipant_var participant = opendds->create_participant( + 34, part_qos, 0, OpenDDS::DCPS::DEFAULT_STATUS_MASK); + if (!participant) { + std::cerr << "Error: Failed to create participant" << std::endl; + return 1; + } + + basic::ReadingTypeSupport_var ts = new basic::ReadingTypeSupportImpl(); + DDS::ReturnCode_t rc = ts->register_type(participant.in(), ""); + if (rc != DDS::RETCODE_OK) { + std::cerr + << "Error: Failed to register type: " + << retcode_to_string(rc) << std::endl; + return 1; + } + + CORBA::String_var type_name = ts->get_type_name(); + DDS::Topic_var topic = participant->create_topic( + "Readings", type_name.in(), TOPIC_QOS_DEFAULT, 0, + OpenDDS::DCPS::DEFAULT_STATUS_MASK); + if (!topic) { + std::cerr << "Error: Failed to create topic" << std::endl; + return 1; + } + + DDS::Subscriber_var subscriber = participant->create_subscriber( + SUBSCRIBER_QOS_DEFAULT, 0, + OpenDDS::DCPS::DEFAULT_STATUS_MASK); + if (!subscriber) { + std::cerr << "Error: Failed to create subscriber" << std::endl; + return 1; + } + + DDS::DataReaderListener_var listener(new DataReaderListenerImpl); + DDS::DataReaderQos qos; + subscriber->get_default_datareader_qos(qos); + DDS::DataReader_var reader = subscriber->create_datareader( + topic.in(), qos, listener, + OpenDDS::DCPS::DEFAULT_STATUS_MASK); + if (!reader) { + std::cerr << "Error: Failed to create reader" << std::endl; + return 1; + } + basic::ReadingDataReader_var reader_i = + basic::ReadingDataReader::_narrow(reader); + + if (!reader_i) { + ACE_ERROR_RETURN((LM_ERROR, + ACE_TEXT("ERROR: %N:%l: main() -") + ACE_TEXT(" _narrow failed!\n")), + 1); + } + + // Wait for Subscriber + std::cout << "Wating for Subscriber..." << std::endl; + DDS::StatusCondition_var sc = reader->get_statuscondition(); + sc->set_enabled_statuses(DDS::SUBSCRIPTION_MATCHED_STATUS); + DDS::WaitSet_var ws = new DDS::WaitSet; + ws->attach_condition(sc); + const DDS::Duration_t max_wait = {10, 0}; + DDS::SubscriptionMatchedStatus status = {0, 0, 0, 0, 0}; + while (status.current_count < 1) { + DDS::ConditionSeq active; + if (ws->wait(active, max_wait) != DDS::RETCODE_OK) { + std::cerr << "Error: Timedout waiting for subscriber" << std::endl; + return 1; + } + if (reader->get_subscription_matched_status(status) != DDS::RETCODE_OK) { + std::cerr << "Error: Failed to get pub matched status" << std::endl; + return 1; + } + } + ws->detach_condition(sc); + std::cout << "Found Publisher..." << std::endl; + + DDS::SubscriptionMatchedStatus matches; + if (reader->get_subscription_matched_status(matches) != DDS::RETCODE_OK) { + ACE_ERROR_RETURN((LM_ERROR, + ACE_TEXT("ERROR: %N:%l: main() -") + ACE_TEXT(" get_subscription_matched_status failed!\n")), + 1); + } + + DDS::ConditionSeq conditions; + DDS::Duration_t timeout = { 60, 0 }; + if (ws->wait(conditions, timeout) != DDS::RETCODE_OK) { + ACE_ERROR_RETURN((LM_ERROR, + ACE_TEXT("ERROR: %N:%l: main() -") + ACE_TEXT(" wait failed!\n")), + 1); + } + + // Cleanup + participant->delete_contained_entities(); + opendds->delete_participant(participant.in()); + TheServiceParticipant->shutdown(); + + } catch (const CORBA::Exception& e) { + e._tao_print_exception("Exception caught in main():"); + return 1; + } + + return 0; +} From da5df2ef0b6f32810602242f52f4e513fe1a3ff6 Mon Sep 17 00:00:00 2001 From: David Pierret Date: Tue, 25 May 2021 23:30:28 +0200 Subject: [PATCH 02/12] add Sequences support manage sequences as list --- pyopendds/dev/include/pyopendds/user.hpp | 8 ++- pyopendds/dev/itl2py/CppOutput.py | 69 +++++++++++++++++------- pyopendds/dev/itl2py/PythonOutput.py | 4 +- pyopendds/dev/itl2py/ast.py | 3 ++ pyopendds/dev/itl2py/itl.py | 17 ++++-- pyopendds/dev/itl2py/templates/user.py | 1 + 6 files changed, 77 insertions(+), 25 deletions(-) diff --git a/pyopendds/dev/include/pyopendds/user.hpp b/pyopendds/dev/include/pyopendds/user.hpp index 387b457..5705b2b 100644 --- a/pyopendds/dev/include/pyopendds/user.hpp +++ b/pyopendds/dev/include/pyopendds/user.hpp @@ -254,11 +254,15 @@ class TopicType : public TopicTypeBase { Type::python_to_cpp(pysample, rv); DDS::ReturnCode_t rc = writer_impl->write(rv, DDS::HANDLE_NIL); - throw Exception( - "ERROR", Errors::PyOpenDDS_Error()); if (Errors::check_rc(rc)) { throw Exception(); } + // Wait for samples to be acknowledged + DDS::Duration_t timeout = { 30, 0 }; + if (writer_impl->wait_for_acknowledgments(timeout) != DDS::RETCODE_OK) { + throw Exception( + "wait_for_acknowledgments error : ", Errors::PyOpenDDS_Error()); + } return pysample; } diff --git a/pyopendds/dev/itl2py/CppOutput.py b/pyopendds/dev/itl2py/CppOutput.py index 0c7589e..5a71e25 100644 --- a/pyopendds/dev/itl2py/CppOutput.py +++ b/pyopendds/dev/itl2py/CppOutput.py @@ -1,6 +1,6 @@ from jinja2 import Environment -from .ast import PrimitiveType, StructType, EnumType +from .ast import PrimitiveType, StructType, EnumType, SequenceType from .Output import Output @@ -13,6 +13,8 @@ def cpp_type_name(type_node): return type_node.kind.name elif isinstance(type_node, (StructType, EnumType)): return cpp_name(type_node.name.parts) + elif isinstance(type_node, (SequenceType)): + return cpp_name(type_node.name.parts); else: raise NotImplementedError @@ -53,15 +55,32 @@ def visit_struct(self, struct_type): pyopendds_type = '' is_string = isinstance(field_node.type_node, PrimitiveType) and \ field_node.type_node.is_string() - - to_lines = [ - 'Type<{pyopendds_type}>::cpp_to_python(cpp.{field_name}', - '#ifdef CPP11_IDL', - ' ()', - '#endif', - ' , *field_value' - + (', "{default_encoding}"' if is_string else '') + ');', - ] + is_sequence = isinstance(field_node.type_node, SequenceType) + + if is_sequence: + to_lines = [ + 'Ref field_elem;', + 'field_value = PyList_New(0);', + 'for (int i = 0; i < cpp.{field_name}.length(); i++) {{', + ' {pyopendds_type} elem = cpp.{field_name}[i];', + ' field_elem = nullptr;', + ' Type<{pyopendds_type}>::cpp_to_python(elem', + ' #ifdef CPP11_IDL', + ' ()', + ' #endif', + ' , *field_elem' + (', "{default_encoding}"' if is_string else '') + ');', + ' PyList_Append(*field_value, *field_elem);', + '}}' + ] + else: + to_lines = [ + 'Type<{pyopendds_type}>::cpp_to_python(cpp.{field_name}', + '#ifdef CPP11_IDL', + ' ()', + '#endif', + ' , *field_value' + + (', "{default_encoding}"' if is_string else '') + ');', + ] from_lines = [ 'if (PyObject_HasAttrString(py, "{field_name}")) {{', @@ -83,14 +102,28 @@ def visit_struct(self, struct_type): ]) if from_lines: - from_lines.extend([ - 'Type<{pyopendds_type}>::python_to_cpp(*field_value, cpp.{field_name}', - '#ifdef CPP11_IDL', - ' ()', - '#endif', - ' ' - + (', "{default_encoding}"' if is_string else '') + ');' - ]) + if is_sequence: + from_lines.extend([ + 'cpp.{field_name}.length(PyList_Size(*field_value));', + 'for (int i = 0; i < PyList_Size(*field_value); i++) {{', + ' ::ContTrajSegment elem = cpp.{field_name}[i];', + ' Type<{pyopendds_type}>::python_to_cpp(PyList_GetItem(*field_value, i), elem', + '#ifdef CPP11_IDL', + ' ()', + '#endif', + ' ' + (', "{default_encoding}"' if is_string else '') + ');', + ' cpp.{field_name}[i] = elem;', + '}}' + ]) + else: + from_lines.extend([ + 'Type<{pyopendds_type}>::python_to_cpp(*field_value, cpp.{field_name}', + '#ifdef CPP11_IDL', + ' ()', + '#endif', + ' ' + + (', "{default_encoding}"' if is_string else '') + ');' + ]) def line_process(lines): return [''] + [ diff --git a/pyopendds/dev/itl2py/PythonOutput.py b/pyopendds/dev/itl2py/PythonOutput.py index 9efaf6a..ae624a2 100644 --- a/pyopendds/dev/itl2py/PythonOutput.py +++ b/pyopendds/dev/itl2py/PythonOutput.py @@ -1,4 +1,4 @@ -from .ast import PrimitiveType, StructType, EnumType +from .ast import PrimitiveType, StructType, EnumType, SequenceType from .Output import Output @@ -72,6 +72,8 @@ def get_python_default_value_string(self, field_type): return type_name + '()' elif isinstance(field_type, EnumType): return type_name + '.' + field_type.default_member + elif isinstance(field_type, SequenceType): + return 'field(default_factory=list)' else: raise NotImplementedError(repr(field_type) + " is not supported") diff --git a/pyopendds/dev/itl2py/ast.py b/pyopendds/dev/itl2py/ast.py index 28a59a4..1e201cc 100644 --- a/pyopendds/dev/itl2py/ast.py +++ b/pyopendds/dev/itl2py/ast.py @@ -215,6 +215,9 @@ def __repr__(self): return self.repr_template(repr(self.base_type) + ("max " + str(self.max_count) if self.max_count else "no max")) + def repr_name(self): + if self.name: + return '::' + self.name.join('::') + '::_tao_seq_' + self.base_type + '_' class NodeVisitor: diff --git a/pyopendds/dev/itl2py/itl.py b/pyopendds/dev/itl2py/itl.py index d447ee3..f248211 100644 --- a/pyopendds/dev/itl2py/itl.py +++ b/pyopendds/dev/itl2py/itl.py @@ -74,7 +74,7 @@ def parse_string(details): def parse_sequence(types, details): - base_type = parse_type(types, details["type"]) + base_type = parse_type(types, list(types)[0]) sequence_max_count = details.get("capacity", None) array_dimensions = details.get("size", None) if array_dimensions is not None: @@ -86,9 +86,16 @@ def parse_sequence(types, details): def parse_record(types, details): struct_type = StructType() for field_dict in details['fields']: - struct_type.add_field( - field_dict['name'], parse_type(types, field_dict['type']), - field_dict.get('optional', False)) + if 'sequence' in field_dict['type']: + sequence = parse_sequence(types, {'type': field_dict['type'], 'capacity': 1, 'size': None}) + sequence.set_name(itl_name=sequence.base_type.name.itl_name) + struct_type.add_field( + field_dict['name'], sequence, + field_dict.get('optional', False)) + else: + struct_type.add_field( + field_dict['name'], parse_type(types, field_dict['type']), + field_dict.get('optional', False)) return struct_type @@ -132,6 +139,8 @@ def parse_type(types, details): if details_type is str: if details in types: return types[details] + elif 'sequence' in details : + return parse_sequence(types, {'type':types, 'capacity': 1, 'size': None}) else: raise ValueError("Invalid Type: " + details) elif details_type is dict: diff --git a/pyopendds/dev/itl2py/templates/user.py b/pyopendds/dev/itl2py/templates/user.py index 63aaf37..0d4391d 100644 --- a/pyopendds/dev/itl2py/templates/user.py +++ b/pyopendds/dev/itl2py/templates/user.py @@ -1,5 +1,6 @@ {% if has_struct -%} from dataclasses import dataclass as _pyopendds_struct +from dataclasses import field {%- endif %} {% if has_enum -%} from enum import IntFlag as _pyopendds_enum From 5773636577e8e652bad0d257c05e91968314ed62 Mon Sep 17 00:00:00 2001 From: David Pierret Date: Tue, 15 Jun 2021 15:43:32 +0200 Subject: [PATCH 03/12] fix after review Signed-off-by: David Pierret --- pyopendds/DataWriter.py | 2 +- pyopendds/DomainParticipant.py | 2 +- pyopendds/Publisher.py | 2 +- pyopendds/dev/include/pyopendds/user.hpp | 21 ++++++++++++++------- pyopendds/init_opendds.py | 1 - tests/basic_test/publisher.py | 2 +- 6 files changed, 18 insertions(+), 12 deletions(-) diff --git a/pyopendds/DataWriter.py b/pyopendds/DataWriter.py index c418265..ba8ff61 100644 --- a/pyopendds/DataWriter.py +++ b/pyopendds/DataWriter.py @@ -9,7 +9,7 @@ from .Publisher import Publisher -class DataWriter(object): +class DataWriter: def __init__(self, publisher: Publisher, topic: Topic, qos=None, listener=None): self.topic = topic diff --git a/pyopendds/DomainParticipant.py b/pyopendds/DomainParticipant.py index 64d9bd8..862b423 100644 --- a/pyopendds/DomainParticipant.py +++ b/pyopendds/DomainParticipant.py @@ -3,7 +3,7 @@ from .Publisher import Publisher -class DomainParticipant(object): +class DomainParticipant: def __init__(self, domain: int, qos=None, listener=None): self.domain = int(domain) diff --git a/pyopendds/Publisher.py b/pyopendds/Publisher.py index e3a65e1..8c19715 100644 --- a/pyopendds/Publisher.py +++ b/pyopendds/Publisher.py @@ -8,7 +8,7 @@ from .DomainParticipant import DomainParticipant -class Publisher(object): +class Publisher: def __init__(self, participant: DomainParticipant, qos=None, listener=None): participant.publishers.append(self) diff --git a/pyopendds/dev/include/pyopendds/user.hpp b/pyopendds/dev/include/pyopendds/user.hpp index 5705b2b..06b1a1a 100644 --- a/pyopendds/dev/include/pyopendds/user.hpp +++ b/pyopendds/dev/include/pyopendds/user.hpp @@ -45,12 +45,17 @@ class IntegerType { static void python_to_cpp(PyObject* py, T& cpp) { - long value; + LongType value; if (limits::is_signed) { - value = PyLong_AsLong(py); + value = PyLong_AsLong(py); } else { - value = PyLong_AsUnsignedLong(py); + value = PyLong_AsUnsignedLong(py); } + if (value < limits::min() || value > limits::max()) { + throw Exception( + "Integer Value is Out of Range for IDL Type", PyExc_ValueError); + } + if (value == -1 && PyErr_Occurred()) throw Exception(); cpp = T(value); } @@ -102,8 +107,10 @@ class StringType { static void python_to_cpp(PyObject* py, T& cpp, const char* encoding) { - PyObject* repr = PyObject_Repr(py); - PyObject* str = PyUnicode_AsEncodedString(repr, "utf-8", "~E~"); + PyObject* repr = PyObject_Str(py); + if (!repr) throw Exception(); + PyObject* str = PyUnicode_AsEncodedString(repr, encoding, NULL); + if (!str) throw Exception(); const char *bytes = PyBytes_AS_STRING(str); cpp = T(bytes); Py_XDECREF(repr); @@ -228,7 +235,7 @@ class TopicType : public TopicTypeBase { reader_impl->delete_readcondition(read_condition); IdlType sample; - DDS::SampleInfo info; + DDS::SampleInfo info; if (Errors::check_rc(reader_impl->take_next_sample(sample, info))) { throw Exception(); } @@ -242,7 +249,7 @@ class TopicType : public TopicTypeBase { PyObject* write(PyObject* pywriter, PyObject* pysample) { DDS::DataWriter* writer = get_capsule(pywriter); - if (!writer) PyErr_SetString(PyExc_Exception, "writer is a NULL pointer"); + if (!writer) throw Exception(); DataWriter* writer_impl = DataWriter::_narrow(writer); if (!writer_impl) { diff --git a/pyopendds/init_opendds.py b/pyopendds/init_opendds.py index 986d947..ecf1bec 100644 --- a/pyopendds/init_opendds.py +++ b/pyopendds/init_opendds.py @@ -27,6 +27,5 @@ def init_opendds(*args, raise ValueError('OpenDDS debug level must be between 0 and 10!') args.extend(['-DCPSDebugLevel', str(opendds_debug_level)]) - print (f" arguments = {args}\n") from _pyopendds import init_opendds_impl init_opendds_impl(*args, default_rtps=default_rtps) diff --git a/tests/basic_test/publisher.py b/tests/basic_test/publisher.py index ab09dfb..4bdb919 100644 --- a/tests/basic_test/publisher.py +++ b/tests/basic_test/publisher.py @@ -15,7 +15,7 @@ publisher = domain.create_publisher() writer = publisher.create_datawriter(topic) - # Wait for Publisher to Connect + # Wait for Subscriber to Connect print('Waiting for Subscriber...') writer.wait_for(StatusKind.PUBLICATION_MATCHED, timedelta(seconds=60)) print('Found subscriber!') From 7feb5b2006a48b2ecb89a4fd41f09147fe3377cb Mon Sep 17 00:00:00 2001 From: David Pierret Date: Mon, 31 May 2021 16:33:18 +0200 Subject: [PATCH 04/12] add Floating type support Signed-off-by: David Pierret --- pyopendds/dev/include/pyopendds/user.hpp | 33 ++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/pyopendds/dev/include/pyopendds/user.hpp b/pyopendds/dev/include/pyopendds/user.hpp index 06b1a1a..393b4d5 100644 --- a/pyopendds/dev/include/pyopendds/user.hpp +++ b/pyopendds/dev/include/pyopendds/user.hpp @@ -130,6 +130,39 @@ typedef template<> class Type: public StringType {}; // TODO: Put Other String/Char Types Here +template +class FloatingType { +public: + typedef std::numeric_limits limits; + + static PyObject* get_python_class() + { + return PyFloat_FromDouble(0); + } + + static void cpp_to_python(const T& cpp, PyObject*& py) + { + py = PyFloat_FromDouble(cpp); + if (!py) throw Exception(); + } + + static void python_to_cpp(PyObject* py, T& cpp) + { + double value; + value = PyFloat_AsDouble(py); + if (value < limits::min() || value > limits::max()) { + throw Exception( + "Floating Value is Out of Range for IDL Type", PyExc_ValueError); + } + if (value == -1 && PyErr_Occurred()) throw Exception(); + cpp = value; + } +}; + +typedef ::CORBA::Float f32; +typedef ::CORBA::Double f64; +template<> class Type: public FloatingType {}; +template<> class Type: public FloatingType {}; // TODO: FloatingType for floating point type class TopicTypeBase { From 510b9a4200ebce1e6ec7b4b0dcd5edd554a0a8cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pierre-Etienne=20Sall=C3=A9?= Date: Thu, 17 Jun 2021 11:36:42 +0200 Subject: [PATCH 05/12] Add DataReaderListener for callback. --- pyopendds/DataReader.py | 9 +++- pyopendds/ext/_pyopendds.cpp | 97 ++++++++++++++++++++++++++++++++-- tests/basic_test/subscriber.py | 16 ++++-- 3 files changed, 113 insertions(+), 9 deletions(-) diff --git a/pyopendds/DataReader.py b/pyopendds/DataReader.py index f411ce7..502dc7f 100644 --- a/pyopendds/DataReader.py +++ b/pyopendds/DataReader.py @@ -18,8 +18,9 @@ def __init__(self, subscriber: Subscriber, topic: Topic, qos=None, listener=None self.subscriber = subscriber subscriber.readers.append(self) + print(str(listener)) from _pyopendds import create_datareader - create_datareader(self, subscriber, topic) + create_datareader(self, subscriber, topic, self.onDataAvailCallback) def wait_for(self, status: StatusKind, timeout: TimeDurationType): from _pyopendds import datareader_wait_for @@ -27,3 +28,9 @@ def wait_for(self, status: StatusKind, timeout: TimeDurationType): def take_next_sample(self): return self.topic._ts_package.take_next_sample(self) + + def onDataAvailCallback(self): + print("Python Callback") + sample = self.topic._ts_package.take_next_sample(self) + self.listener(sample) + diff --git a/pyopendds/ext/_pyopendds.cpp b/pyopendds/ext/_pyopendds.cpp index 34b7785..9b29ef9 100644 --- a/pyopendds/ext/_pyopendds.cpp +++ b/pyopendds/ext/_pyopendds.cpp @@ -17,6 +17,78 @@ PyObject* Errors::PyOpenDDS_Error_ = nullptr; PyObject* Errors::ReturnCodeError_ = nullptr; namespace { +class DataReaderListenerImpl : public virtual OpenDDS::DCPS::LocalObject { +public: + + DataReaderListenerImpl(PyObject * self, PyObject *callback); + + virtual void on_requested_deadline_missed( + DDS::DataReader_ptr reader, + const DDS::RequestedDeadlineMissedStatus& status) {} + + virtual void on_requested_incompatible_qos( + DDS::DataReader_ptr reader, + const DDS::RequestedIncompatibleQosStatus& status) {} + + virtual void on_sample_rejected( + DDS::DataReader_ptr reader, + const DDS::SampleRejectedStatus& status) {} + + virtual void on_liveliness_changed( + DDS::DataReader_ptr reader, + const DDS::LivelinessChangedStatus& status) {} + + virtual void on_data_available( + DDS::DataReader_ptr reader); + + virtual void on_subscription_matched( + DDS::DataReader_ptr reader, + const DDS::SubscriptionMatchedStatus& status) {} + + virtual void on_sample_lost( + DDS::DataReader_ptr reader, + const DDS::SampleLostStatus& status) {} + + private: + PyObject *_callback; + PyObject * _self; +}; + +DataReaderListenerImpl::DataReaderListenerImpl(PyObject * self, PyObject *callback): OpenDDS::DCPS::LocalObject() { + _self = self; + Py_XINCREF(_self); + _callback = callback; + Py_XINCREF(_callback); +} + +void +DataReaderListenerImpl::on_data_available(DDS::DataReader_ptr reader) +{ + PyObject *callable = _callback; + //PyObject *arglist = NULL; + PyObject *result = NULL; + + PyGILState_STATE gstate; + gstate = PyGILState_Ensure(); + + if(PyCallable_Check(callable)) { + std::cerr << "function is callback" << std::endl; + //arglist = Py_BuildValue("()", null); + //result = PyEval_CallObject(callable, nullptr); + result = PyObject_CallFunctionObjArgs(callable,nullptr); + //Py_DECREF(arglist); + + if(result == NULL) + PyErr_Print(); + + Py_XDECREF(result); + } else { + std::cerr << "function is not a callback" << std::endl; + } + + Py_XDECREF(callable); + PyGILState_Release(gstate); +} /// Global Participant Factory DDS::DomainParticipantFactory_var participant_factory; @@ -326,15 +398,19 @@ void delete_datareader_var(PyObject* reader_capsule) } /** - * create_datareader(datareader: DataReader, subscriber: Subscriber, topic: Topic) -> None + * create_datareader(datareader: DataReader, subscriber: Subscriber, topic: Topic, listener: pyObject) -> None */ PyObject* create_datareader(PyObject* self, PyObject* args) { Ref pydatareader; Ref pysubscriber; Ref pytopic; - if (!PyArg_ParseTuple(args, "OOO", - &*pydatareader, &*pysubscriber, &*pytopic)) { + PyObject *pycallback; + + std::cout<<"create_datareader function" << std::endl; + + if (!PyArg_ParseTuple(args, "OOOO", + &*pydatareader, &*pysubscriber, &*pytopic, &pycallback)) { return nullptr; } pydatareader++; @@ -349,9 +425,22 @@ PyObject* create_datareader(PyObject* self, PyObject* args) DDS::Topic* topic = get_capsule(*pytopic); if (!topic) return nullptr; + + + DataReaderListenerImpl * listener = nullptr; + if(pycallback != Py_None) { + if(PyCallable_Check(pycallback)) { + listener = new DataReaderListenerImpl(*pydatareader, pycallback); + } + else { + throw Exception("Callback provided is not a callable object", PyExc_TypeError); + } + + } + // Create DataReader DDS::DataReader* datareader = subscriber->create_datareader( - topic, DATAREADER_QOS_DEFAULT, nullptr, + topic, DATAREADER_QOS_DEFAULT, listener, OpenDDS::DCPS::DEFAULT_STATUS_MASK); if (!datareader) { PyErr_SetString(Errors::PyOpenDDS_Error(), "Failed to Create DataReader"); diff --git a/tests/basic_test/subscriber.py b/tests/basic_test/subscriber.py index f5bb33b..0e22dd8 100644 --- a/tests/basic_test/subscriber.py +++ b/tests/basic_test/subscriber.py @@ -1,26 +1,34 @@ import sys +import time from datetime import timedelta from pyopendds import \ init_opendds, DomainParticipant, StatusKind, PyOpenDDS_Error from pybasic.basic import Reading + +def listener_func(sample: Reading): + print("main callback !") + print(sample) + + if __name__ == "__main__": try: # Initialize OpenDDS and Create DDS Entities - init_opendds(opendds_debug_level=1) + init_opendds(opendds_debug_level=10) domain = DomainParticipant(34) topic = domain.create_topic('Readings', Reading) subscriber = domain.create_subscriber() - reader = subscriber.create_datareader(topic) + reader = subscriber.create_datareader(topic=topic, listener=listener_func) # Wait for Publisher to Connect print('Waiting for Publisher...') - reader.wait_for(StatusKind.SUBSCRIPTION_MATCHED, timedelta(seconds=5)) + reader.wait_for(StatusKind.SUBSCRIPTION_MATCHED, timedelta(seconds=30)) print('Found Publisher!') # Read and Print Sample - print(reader.take_next_sample()) + # print(reader.take_next_sample()) + time.sleep(60) print('Done!') From 6e5c98af865861acb601dded68e3a4eae9b9d46b Mon Sep 17 00:00:00 2001 From: David Pierret Date: Thu, 24 Jun 2021 14:31:43 +0200 Subject: [PATCH 06/12] cleanup and more tests Signed-off-by: David Pierret --- pyopendds/DataReader.py | 15 +++- pyopendds/DomainParticipant.py | 2 +- pyopendds/dev/include/pyopendds/user.hpp | 9 +- pyopendds/ext/_pyopendds.cpp | 31 ++++--- tests/basic_test/DataReaderListenerImpl.cpp | 93 --------------------- tests/basic_test/DataReaderListenerImpl.h | 48 ----------- tests/basic_test/subscriber.py | 14 ++-- 7 files changed, 43 insertions(+), 169 deletions(-) delete mode 100644 tests/basic_test/DataReaderListenerImpl.cpp delete mode 100644 tests/basic_test/DataReaderListenerImpl.h diff --git a/pyopendds/DataReader.py b/pyopendds/DataReader.py index 502dc7f..18a1b24 100644 --- a/pyopendds/DataReader.py +++ b/pyopendds/DataReader.py @@ -1,5 +1,7 @@ from __future__ import annotations +import sys + from .Topic import Topic from .constants import StatusKind from .util import TimeDurationType, normalize_time_duration @@ -18,7 +20,6 @@ def __init__(self, subscriber: Subscriber, topic: Topic, qos=None, listener=None self.subscriber = subscriber subscriber.readers.append(self) - print(str(listener)) from _pyopendds import create_datareader create_datareader(self, subscriber, topic, self.onDataAvailCallback) @@ -30,7 +31,13 @@ def take_next_sample(self): return self.topic._ts_package.take_next_sample(self) def onDataAvailCallback(self): - print("Python Callback") - sample = self.topic._ts_package.take_next_sample(self) - self.listener(sample) + sample = None + if hasattr(self, 'topic'): + sample = self.take_next_sample() + else: + print("Error, no topic in self => " + self.__qualname__, file=sys.stderr) + if sample is not None: + self.listener(sample) + else: + print("Error, data not valid", file=sys.stderr) diff --git a/pyopendds/DomainParticipant.py b/pyopendds/DomainParticipant.py index 862b423..935f57c 100644 --- a/pyopendds/DomainParticipant.py +++ b/pyopendds/DomainParticipant.py @@ -22,7 +22,7 @@ def __del__(self): participant_cleanup(self) def create_topic(self, - name: str, topic_type: type, qos=None, listener=None) -> Topic: + name: str, topic_type: type, qos=None, listener=None) -> Topic: return Topic(self, name, topic_type, qos, listener) def create_subscriber(self, qos=None, listener=None) -> Subscriber: diff --git a/pyopendds/dev/include/pyopendds/user.hpp b/pyopendds/dev/include/pyopendds/user.hpp index 393b4d5..e97e3d2 100644 --- a/pyopendds/dev/include/pyopendds/user.hpp +++ b/pyopendds/dev/include/pyopendds/user.hpp @@ -274,7 +274,10 @@ class TopicType : public TopicTypeBase { } PyObject* rv = nullptr; - Type::cpp_to_python(sample, rv); + if (info.valid_data) + Type::cpp_to_python(sample, rv); + else + rv = Py_None; return rv; } @@ -295,7 +298,9 @@ class TopicType : public TopicTypeBase { DDS::ReturnCode_t rc = writer_impl->write(rv, DDS::HANDLE_NIL); if (Errors::check_rc(rc)) { - throw Exception(); + std::cerr << "write return error " << rc << std::endl; + throw Exception( + "WRITE ERROR", Errors::PyOpenDDS_Error()); } // Wait for samples to be acknowledged DDS::Duration_t timeout = { 30, 0 }; diff --git a/pyopendds/ext/_pyopendds.cpp b/pyopendds/ext/_pyopendds.cpp index 9b29ef9..b75f34a 100644 --- a/pyopendds/ext/_pyopendds.cpp +++ b/pyopendds/ext/_pyopendds.cpp @@ -65,28 +65,25 @@ void DataReaderListenerImpl::on_data_available(DDS::DataReader_ptr reader) { PyObject *callable = _callback; - //PyObject *arglist = NULL; PyObject *result = NULL; PyGILState_STATE gstate; gstate = PyGILState_Ensure(); - + try{ if(PyCallable_Check(callable)) { - std::cerr << "function is callback" << std::endl; - //arglist = Py_BuildValue("()", null); - //result = PyEval_CallObject(callable, nullptr); result = PyObject_CallFunctionObjArgs(callable,nullptr); - //Py_DECREF(arglist); - if(result == NULL) PyErr_Print(); - Py_XDECREF(result); } else { - std::cerr << "function is not a callback" << std::endl; + throw Exception("function is not a callback", PyExc_TypeError); } Py_XDECREF(callable); + } catch (Exception& e ) { + PyGILState_Release(gstate); + throw e; + } PyGILState_Release(gstate); } @@ -393,6 +390,9 @@ void delete_datareader_var(PyObject* reader_capsule) if (PyCapsule_CheckExact(reader_capsule)) { DDS::DataReader_var reader = static_cast( PyCapsule_GetPointer(reader_capsule, nullptr)); + DDS::DataReaderListener_ptr listener = DDS::DataReader::_narrow(reader)->get_listener(); + free(listener); + listener = nullptr; reader = nullptr; } } @@ -405,17 +405,18 @@ PyObject* create_datareader(PyObject* self, PyObject* args) Ref pydatareader; Ref pysubscriber; Ref pytopic; - PyObject *pycallback; + Ref pycallback; std::cout<<"create_datareader function" << std::endl; if (!PyArg_ParseTuple(args, "OOOO", - &*pydatareader, &*pysubscriber, &*pytopic, &pycallback)) { + &*pydatareader, &*pysubscriber, &*pytopic, &*pycallback)) { return nullptr; } pydatareader++; pysubscriber++; pytopic++; + pycallback++; // Get Subscriber DDS::Subscriber* subscriber = get_capsule(*pysubscriber); @@ -425,12 +426,10 @@ PyObject* create_datareader(PyObject* self, PyObject* args) DDS::Topic* topic = get_capsule(*pytopic); if (!topic) return nullptr; - - DataReaderListenerImpl * listener = nullptr; - if(pycallback != Py_None) { - if(PyCallable_Check(pycallback)) { - listener = new DataReaderListenerImpl(*pydatareader, pycallback); + if(*pycallback != Py_None) { + if(PyCallable_Check(*pycallback)) { + listener = new DataReaderListenerImpl(*pydatareader, *pycallback); } else { throw Exception("Callback provided is not a callable object", PyExc_TypeError); diff --git a/tests/basic_test/DataReaderListenerImpl.cpp b/tests/basic_test/DataReaderListenerImpl.cpp deleted file mode 100644 index a164871..0000000 --- a/tests/basic_test/DataReaderListenerImpl.cpp +++ /dev/null @@ -1,93 +0,0 @@ -/* - * - * - * Distributed under the OpenDDS License. - * See: http://www.opendds.org/license.html - */ - -#include -#include - -#include "DataReaderListenerImpl.h" -#include "basicTypeSupportC.h" -#include "basicTypeSupportImpl.h" - -#include - -void -DataReaderListenerImpl::on_requested_deadline_missed( - DDS::DataReader_ptr /*reader*/, - const DDS::RequestedDeadlineMissedStatus& /*status*/) -{ -} - -void -DataReaderListenerImpl::on_requested_incompatible_qos( - DDS::DataReader_ptr /*reader*/, - const DDS::RequestedIncompatibleQosStatus& /*status*/) -{ -} - -void -DataReaderListenerImpl::on_sample_rejected( - DDS::DataReader_ptr /*reader*/, - const DDS::SampleRejectedStatus& /*status*/) -{ -} - -void -DataReaderListenerImpl::on_liveliness_changed( - DDS::DataReader_ptr /*reader*/, - const DDS::LivelinessChangedStatus& /*status*/) -{ -} - -void -DataReaderListenerImpl::on_data_available(DDS::DataReader_ptr reader) -{ - basic::ReadingDataReader_var reader_i = - basic::ReadingDataReader::_narrow(reader); - - if (!reader_i) { - ACE_ERROR((LM_ERROR, - ACE_TEXT("ERROR: %N:%l: on_data_available() -") - ACE_TEXT(" _narrow failed!\n"))); - ACE_OS::exit(1); - } - - basic::Reading sample; - DDS::SampleInfo info; - - DDS::ReturnCode_t error = reader_i->take_next_sample(sample, info); - - if (error == DDS::RETCODE_OK) { - std::cout << "SampleInfo.sample_rank = " << info.sample_rank << std::endl; - std::cout << "SampleInfo.instance_state = " << info.instance_state << std::endl; - - if (info.valid_data) { - std::cout << "Message: kind = " << sample.kind << std::endl - << " value = " << sample.value << std::endl - << " where = " << sample.where << std::endl; - - } - - } else { - ACE_ERROR((LM_ERROR, - ACE_TEXT("ERROR: %N:%l: on_data_available() -") - ACE_TEXT(" take_next_sample failed!\n"))); - } -} - -void -DataReaderListenerImpl::on_subscription_matched( - DDS::DataReader_ptr /*reader*/, - const DDS::SubscriptionMatchedStatus& /*status*/) -{ -} - -void -DataReaderListenerImpl::on_sample_lost( - DDS::DataReader_ptr /*reader*/, - const DDS::SampleLostStatus& /*status*/) -{ -} \ No newline at end of file diff --git a/tests/basic_test/DataReaderListenerImpl.h b/tests/basic_test/DataReaderListenerImpl.h deleted file mode 100644 index 79955a5..0000000 --- a/tests/basic_test/DataReaderListenerImpl.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - * - * - * Distributed under the OpenDDS License. - * See: http://www.opendds.org/license.html - */ - -#ifndef DATAREADER_LISTENER_IMPL_H -#define DATAREADER_LISTENER_IMPL_H - -#include - -#include -#include -#include - -class DataReaderListenerImpl - : public virtual OpenDDS::DCPS::LocalObject { -public: - virtual void on_requested_deadline_missed( - DDS::DataReader_ptr reader, - const DDS::RequestedDeadlineMissedStatus& status); - - virtual void on_requested_incompatible_qos( - DDS::DataReader_ptr reader, - const DDS::RequestedIncompatibleQosStatus& status); - - virtual void on_sample_rejected( - DDS::DataReader_ptr reader, - const DDS::SampleRejectedStatus& status); - - virtual void on_liveliness_changed( - DDS::DataReader_ptr reader, - const DDS::LivelinessChangedStatus& status); - - virtual void on_data_available( - DDS::DataReader_ptr reader); - - virtual void on_subscription_matched( - DDS::DataReader_ptr reader, - const DDS::SubscriptionMatchedStatus& status); - - virtual void on_sample_lost( - DDS::DataReader_ptr reader, - const DDS::SampleLostStatus& status); -}; - -#endif /* DATAREADER_LISTENER_IMPL_H */ \ No newline at end of file diff --git a/tests/basic_test/subscriber.py b/tests/basic_test/subscriber.py index 0e22dd8..f32c9ec 100644 --- a/tests/basic_test/subscriber.py +++ b/tests/basic_test/subscriber.py @@ -7,19 +7,23 @@ from pybasic.basic import Reading -def listener_func(sample: Reading): - print("main callback !") - print(sample) +class TestClass: + def listener_func(self, sample: Reading): + print("main callback !", file=sys.stderr) + print(sample) + # todo: investigate the need of this sleep + time.sleep(1) if __name__ == "__main__": try: + listener = TestClass() # Initialize OpenDDS and Create DDS Entities init_opendds(opendds_debug_level=10) domain = DomainParticipant(34) topic = domain.create_topic('Readings', Reading) subscriber = domain.create_subscriber() - reader = subscriber.create_datareader(topic=topic, listener=listener_func) + reader = subscriber.create_datareader(topic=topic, listener=listener.listener_func) # Wait for Publisher to Connect print('Waiting for Publisher...') @@ -32,5 +36,5 @@ def listener_func(sample: Reading): print('Done!') - except PyOpenDDS_Error as e: + except Exception as e: sys.exit(e) From 8a50cad6bb634e141940970bffee116cab2c9c66 Mon Sep 17 00:00:00 2001 From: David Pierret Date: Tue, 29 Jun 2021 13:12:10 +0200 Subject: [PATCH 07/12] add listener and fix publisher Signed-off-by: David Pierret --- pyopendds/dev/include/pyopendds/user.hpp | 15 +++------ pyopendds/dev/itl2py/templates/user.cpp | 1 + pyopendds/ext/_pyopendds.cpp | 22 ++++++------- tests/basic_test/CMakeLists.txt | 7 ----- tests/basic_test/publisher.py | 40 +++++++++++++++++++----- 5 files changed, 47 insertions(+), 38 deletions(-) diff --git a/pyopendds/dev/include/pyopendds/user.hpp b/pyopendds/dev/include/pyopendds/user.hpp index e97e3d2..8b0453f 100644 --- a/pyopendds/dev/include/pyopendds/user.hpp +++ b/pyopendds/dev/include/pyopendds/user.hpp @@ -289,27 +289,20 @@ class TopicType : public TopicTypeBase { DataWriter* writer_impl = DataWriter::_narrow(writer); if (!writer_impl) { - throw Exception( - "Could not narrow writer implementation", Errors::PyOpenDDS_Error()); + throw Exception("Could not narrow writer implementation", Errors::PyOpenDDS_Error()); } IdlType rv; Type::python_to_cpp(pysample, rv); DDS::ReturnCode_t rc = writer_impl->write(rv, DDS::HANDLE_NIL); - if (Errors::check_rc(rc)) { - std::cerr << "write return error " << rc << std::endl; + if (rc != DDS::RETCODE_OK) { throw Exception( "WRITE ERROR", Errors::PyOpenDDS_Error()); } - // Wait for samples to be acknowledged - DDS::Duration_t timeout = { 30, 0 }; - if (writer_impl->wait_for_acknowledgments(timeout) != DDS::RETCODE_OK) { - throw Exception( - "wait_for_acknowledgments error : ", Errors::PyOpenDDS_Error()); - } + if (Errors::check_rc(rc)) return nullptr; - return pysample; + return PyLong_FromLong(rc); } PyObject* get_python_class() diff --git a/pyopendds/dev/itl2py/templates/user.cpp b/pyopendds/dev/itl2py/templates/user.cpp index fa5e882..4a93fa0 100644 --- a/pyopendds/dev/itl2py/templates/user.cpp +++ b/pyopendds/dev/itl2py/templates/user.cpp @@ -139,6 +139,7 @@ PyObject* pywrite(PyObject* self, PyObject* args) Ref pysample; if (!PyArg_ParseTuple(args, "OO", &*pywriter, &*pysample)) return nullptr; pywriter++; + pysample++; // Try to Get Reading Type and Do write Ref pytopic = PyObject_GetAttrString(*pywriter, "topic"); diff --git a/pyopendds/ext/_pyopendds.cpp b/pyopendds/ext/_pyopendds.cpp index b75f34a..034076c 100644 --- a/pyopendds/ext/_pyopendds.cpp +++ b/pyopendds/ext/_pyopendds.cpp @@ -70,20 +70,20 @@ DataReaderListenerImpl::on_data_available(DDS::DataReader_ptr reader) PyGILState_STATE gstate; gstate = PyGILState_Ensure(); try{ - if(PyCallable_Check(callable)) { - result = PyObject_CallFunctionObjArgs(callable,nullptr); - if(result == NULL) - PyErr_Print(); - Py_XDECREF(result); - } else { - throw Exception("function is not a callback", PyExc_TypeError); - } - - Py_XDECREF(callable); + if(PyCallable_Check(callable)) { + result = PyObject_CallFunctionObjArgs(callable,nullptr); + if(result == NULL) + PyErr_Print(); + Py_XDECREF(result); + } else { + throw Exception("function is not a callback", PyExc_TypeError); + } } catch (Exception& e ) { + // Py_XDECREF(callable); PyGILState_Release(gstate); throw e; } + //Py_XDECREF(callable); PyGILState_Release(gstate); } @@ -407,8 +407,6 @@ PyObject* create_datareader(PyObject* self, PyObject* args) Ref pytopic; Ref pycallback; - std::cout<<"create_datareader function" << std::endl; - if (!PyArg_ParseTuple(args, "OOOO", &*pydatareader, &*pysubscriber, &*pytopic, &*pycallback)) { return nullptr; diff --git a/tests/basic_test/CMakeLists.txt b/tests/basic_test/CMakeLists.txt index a5a8bb9..4b8b942 100644 --- a/tests/basic_test/CMakeLists.txt +++ b/tests/basic_test/CMakeLists.txt @@ -23,10 +23,3 @@ if(${CPP11_IDL}) set_target_properties(publisher PROPERTIES COMPILE_DEFINITIONS "CPP11_IDL") endif() - -add_executable(subscriber subscriber.cpp DataReaderListenerImpl.cpp) -target_link_libraries(subscriber OpenDDS::OpenDDS basic_idl) -if(${CPP11_IDL}) - set_target_properties(subscriber PROPERTIES - COMPILE_DEFINITIONS "CPP11_IDL") -endif() diff --git a/tests/basic_test/publisher.py b/tests/basic_test/publisher.py index 4bdb919..dc18922 100644 --- a/tests/basic_test/publisher.py +++ b/tests/basic_test/publisher.py @@ -10,25 +10,49 @@ try: # Initialize OpenDDS and Create DDS Entities init_opendds(opendds_debug_level=1) + time.sleep(1) domain = DomainParticipant(34) + time.sleep(1) topic = domain.create_topic('Readings', Reading) + time.sleep(1) publisher = domain.create_publisher() + time.sleep(1) writer = publisher.create_datawriter(topic) + time.sleep(1) # Wait for Subscriber to Connect print('Waiting for Subscriber...') writer.wait_for(StatusKind.PUBLICATION_MATCHED, timedelta(seconds=60)) print('Found subscriber!') - sample = Reading() - sample.kind = ReadingKind.acceleration - sample.value = 123 - sample.where = "somewhere" + write_sample_speed = Reading() + write_sample_accel = Reading() + write_sample_dist = Reading() + while True: + time.sleep(1) + write_sample_speed.kind = ReadingKind.speed + write_sample_speed.value = 123 + write_sample_speed.where = "somewhere" + # Read and Print Sample + rc = writer.write(write_sample_speed) + print(rc) - time.sleep(1) - # Read and Print Sample - writer.write(sample) - print('Done!') + time.sleep(1) + write_sample_accel.kind = ReadingKind.acceleration + write_sample_accel.value = 2 + write_sample_accel.where = "everywhere" + # Read and Print Sample + rc = writer.write(write_sample_accel) + print(rc) + + time.sleep(1) + write_sample_dist.kind = ReadingKind.distance + write_sample_dist.value = 543 + write_sample_dist.where = "anywhere" + # Read and Print Sample + rc = writer.write(write_sample_dist) + print(rc) + print('Done!') except PyOpenDDS_Error as e: sys.exit(e) From 6c67cb7c583841359d9d206a13b59522e6bb4b08 Mon Sep 17 00:00:00 2001 From: David Pierret Date: Fri, 2 Jul 2021 08:53:33 +0200 Subject: [PATCH 08/12] add boolean type support Signed-off-by: David Pierret --- pyopendds/dev/include/pyopendds/user.hpp | 34 ++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/pyopendds/dev/include/pyopendds/user.hpp b/pyopendds/dev/include/pyopendds/user.hpp index 8b0453f..861a40f 100644 --- a/pyopendds/dev/include/pyopendds/user.hpp +++ b/pyopendds/dev/include/pyopendds/user.hpp @@ -22,6 +22,40 @@ class Type /*{ static void python_to_cpp(PyObject* py, T& cpp); }*/; + +template +class BooleanType { +public: + static PyObject* get_python_class() + { + return Py_False; + } + + static void cpp_to_python(const T& cpp, PyObject*& py) + { + if ( ! cpp ) { + py = Py_False; + } else { + py = Py_True; + } + } + + static void python_to_cpp(PyObject* py, T& cpp) + { + if (PyBool_Check(py)) { + throw Exception("Not a boolean", PyExc_ValueError); + } + if(py) { + cpp = true; + } else { + cpp = false; + } + } +}; + +//typedef ::CORBA::Boolean bool; +template<> class Type: public BooleanType {}; + template class IntegerType { public: From cdffefa72d64e6d7aef3d2825b802fa5b7cbc94d Mon Sep 17 00:00:00 2001 From: David Pierret Date: Fri, 2 Jul 2021 10:09:27 +0200 Subject: [PATCH 09/12] fix wrong type in itl2py Signed-off-by: David Pierret --- pyopendds/dev/include/pyopendds/user.hpp | 2 +- pyopendds/dev/itl2py/CppOutput.py | 125 ++++++++++++++--------- pyopendds/dev/itl2py/PythonOutput.py | 17 ++- pyopendds/dev/itl2py/ast.py | 6 +- pyopendds/dev/itl2py/itl.py | 5 +- pyopendds/dev/itl2py/templates/user.cpp | 5 +- pyopendds/dev/itl2py/templates/user.py | 7 ++ tests/basic_test/CMakeLists.txt | 12 +++ tests/basic_test/airbusdds.idl | 14 +++ tests/basic_test/basic.idl | 8 ++ tests/basic_test/publisher.py | 14 ++- tests/basic_test/subscriber.py | 2 +- 12 files changed, 160 insertions(+), 57 deletions(-) create mode 100644 tests/basic_test/airbusdds.idl diff --git a/pyopendds/dev/include/pyopendds/user.hpp b/pyopendds/dev/include/pyopendds/user.hpp index 861a40f..314c56f 100644 --- a/pyopendds/dev/include/pyopendds/user.hpp +++ b/pyopendds/dev/include/pyopendds/user.hpp @@ -79,7 +79,7 @@ class IntegerType { static void python_to_cpp(PyObject* py, T& cpp) { - LongType value; + int value; //todo: change to LongType if (limits::is_signed) { value = PyLong_AsLong(py); } else { diff --git a/pyopendds/dev/itl2py/CppOutput.py b/pyopendds/dev/itl2py/CppOutput.py index 5a71e25..869d7e4 100644 --- a/pyopendds/dev/itl2py/CppOutput.py +++ b/pyopendds/dev/itl2py/CppOutput.py @@ -1,6 +1,6 @@ from jinja2 import Environment -from .ast import PrimitiveType, StructType, EnumType, SequenceType +from .ast import PrimitiveType, StructType, EnumType, SequenceType, ArrayType from .Output import Output @@ -15,6 +15,8 @@ def cpp_type_name(type_node): return cpp_name(type_node.name.parts) elif isinstance(type_node, (SequenceType)): return cpp_name(type_node.name.parts); + elif isinstance(type_node, (ArrayType)): + return cpp_name(type_node.name.parts); else: raise NotImplementedError @@ -57,30 +59,14 @@ def visit_struct(self, struct_type): field_node.type_node.is_string() is_sequence = isinstance(field_node.type_node, SequenceType) - if is_sequence: - to_lines = [ - 'Ref field_elem;', - 'field_value = PyList_New(0);', - 'for (int i = 0; i < cpp.{field_name}.length(); i++) {{', - ' {pyopendds_type} elem = cpp.{field_name}[i];', - ' field_elem = nullptr;', - ' Type<{pyopendds_type}>::cpp_to_python(elem', - ' #ifdef CPP11_IDL', - ' ()', - ' #endif', - ' , *field_elem' + (', "{default_encoding}"' if is_string else '') + ');', - ' PyList_Append(*field_value, *field_elem);', - '}}' - ] - else: - to_lines = [ - 'Type<{pyopendds_type}>::cpp_to_python(cpp.{field_name}', - '#ifdef CPP11_IDL', - ' ()', - '#endif', - ' , *field_value' - + (', "{default_encoding}"' if is_string else '') + ');', - ] + to_lines = [ + 'Type<{pyopendds_type}>::cpp_to_python(cpp.{field_name}', + '#ifdef CPP11_IDL', + ' ()', + '#endif', + ' , *field_value' + + (', "{default_encoding}"' if is_string else '') + ');', + ] from_lines = [ 'if (PyObject_HasAttrString(py, "{field_name}")) {{', @@ -102,28 +88,14 @@ def visit_struct(self, struct_type): ]) if from_lines: - if is_sequence: - from_lines.extend([ - 'cpp.{field_name}.length(PyList_Size(*field_value));', - 'for (int i = 0; i < PyList_Size(*field_value); i++) {{', - ' ::ContTrajSegment elem = cpp.{field_name}[i];', - ' Type<{pyopendds_type}>::python_to_cpp(PyList_GetItem(*field_value, i), elem', - '#ifdef CPP11_IDL', - ' ()', - '#endif', - ' ' + (', "{default_encoding}"' if is_string else '') + ');', - ' cpp.{field_name}[i] = elem;', - '}}' - ]) - else: - from_lines.extend([ - 'Type<{pyopendds_type}>::python_to_cpp(*field_value, cpp.{field_name}', - '#ifdef CPP11_IDL', - ' ()', - '#endif', - ' ' - + (', "{default_encoding}"' if is_string else '') + ');' - ]) + from_lines.extend([ + 'Type<{pyopendds_type}>::python_to_cpp(*field_value, cpp.{field_name}', + '#ifdef CPP11_IDL', + ' ()', + '#endif', + ' ' + + (', "{default_encoding}"' if is_string else '') + ');' + ]) def line_process(lines): return [''] + [ @@ -164,3 +136,62 @@ def visit_enum(self, enum_type): 'from_lines': '', 'is_topic_type': False, }) + + def visit_sequence(self, sequence_type): + sequence_to_lines = [ + 'Ref field_value;', + ] + sequence_from_lines = [] + to_lines = [ + 'Ref field_elem;', + 'field_value = PyList_New(0);', + 'for (int i = 0; i < cpp.length(); i++) {{', + ' {pyopendds_type} elem = cpp[i];', + ' field_elem = nullptr;', + ' Type<{pyopendds_type}>::cpp_to_python(elem', + ' #ifdef CPP11_IDL', + ' ()', + ' #endif', + ' , *field_elem);', + ' PyList_Append(py, *field_elem);', + '}}' + ] + + pyopendds_type = cpp_type_name(sequence_type.base_type) + from_lines = [ + 'cpp.length(PyList_Size(py));', + 'for (int i = 0; i < PyList_Size(py); i++) {{', + ' {pyopendds_type} elem = cpp[i];', + ' Type<{pyopendds_type}>::python_to_cpp(PyList_GetItem(py, i), elem', + '#ifdef CPP11_IDL', + ' ()', + '#endif', + ' );', + ' cpp[i] = elem;', + '}}' + ] + + def line_process(lines): + return [''] + [ + s.format( + default_encoding=self.context['default_encoding'], + pyopendds_type=pyopendds_type, + ) for s in lines + ] + + sequence_to_lines.extend(line_process(to_lines)) + sequence_from_lines.extend(line_process(from_lines)) + + self.context['types'].append({ + 'cpp_name': cpp_name(sequence_type.name.parts), + 'name_parts': sequence_type.parent_name().parts, + 'local_name': sequence_type.local_name(), + 'to_lines': '\n'.join(sequence_to_lines), + 'from_lines': '\n'.join(sequence_from_lines), + 'new_lines': '\n'.join([ + 'args = nullptr;' + ]), + 'is_topic_type': sequence_type.is_topic_type, + 'sequence': True, + 'to_replace': False, + }) diff --git a/pyopendds/dev/itl2py/PythonOutput.py b/pyopendds/dev/itl2py/PythonOutput.py index ae624a2..b57ca86 100644 --- a/pyopendds/dev/itl2py/PythonOutput.py +++ b/pyopendds/dev/itl2py/PythonOutput.py @@ -1,4 +1,4 @@ -from .ast import PrimitiveType, StructType, EnumType, SequenceType +from .ast import PrimitiveType, StructType, EnumType, SequenceType, ArrayType from .Output import Output @@ -74,6 +74,8 @@ def get_python_default_value_string(self, field_type): return type_name + '.' + field_type.default_member elif isinstance(field_type, SequenceType): return 'field(default_factory=list)' + elif isinstance(field_type, ArrayType): + return 'field(default_factory=list)' else: raise NotImplementedError(repr(field_type) + " is not supported") @@ -100,4 +102,15 @@ def visit_enum(self, enum_type): dict(name=name, value=value) for name, value in enum_type.members.items() ], ), - )) \ No newline at end of file + )) + + def visit_sequence(self, sequence_type): + self.context['has_sequence'] = True + self.context['types'].append(dict( + local_name=sequence_type.local_name(), + type_support=self.context['native_package_name'] if sequence_type.is_topic_type else None, + sequence=dict( + type=sequence_type.base_type, + len=sequence_type.max_count, + ), + )) diff --git a/pyopendds/dev/itl2py/ast.py b/pyopendds/dev/itl2py/ast.py index 1e201cc..76d5d5b 100644 --- a/pyopendds/dev/itl2py/ast.py +++ b/pyopendds/dev/itl2py/ast.py @@ -195,6 +195,7 @@ def __init__(self, base_type, dimensions): def accept(self, visitor): visitor.visit_array(self) + pass def __repr__(self): return self.repr_template( @@ -210,6 +211,7 @@ def __init__(self, base_type, max_count): def accept(self, visitor): visitor.visit_sequence(self) + pass def __repr__(self): return self.repr_template(repr(self.base_type) @@ -234,10 +236,12 @@ def visit_enum(self, enum_type): raise NotImplementedError def visit_array(self, array_type): - raise NotImplementedError + pass + #array_type.accept(self) def visit_sequence(self, sequence_type): raise NotImplementedError + #sequence_type.accept(self) def get_ast(types: dict) -> Module: diff --git a/pyopendds/dev/itl2py/itl.py b/pyopendds/dev/itl2py/itl.py index f248211..1ffb186 100644 --- a/pyopendds/dev/itl2py/itl.py +++ b/pyopendds/dev/itl2py/itl.py @@ -74,7 +74,7 @@ def parse_string(details): def parse_sequence(types, details): - base_type = parse_type(types, list(types)[0]) + base_type = parse_type(types, details["type"]) sequence_max_count = details.get("capacity", None) array_dimensions = details.get("size", None) if array_dimensions is not None: @@ -139,8 +139,6 @@ def parse_type(types, details): if details_type is str: if details in types: return types[details] - elif 'sequence' in details : - return parse_sequence(types, {'type':types, 'capacity': 1, 'size': None}) else: raise ValueError("Invalid Type: " + details) elif details_type is dict: @@ -157,3 +155,4 @@ def parse_itl(types, itl): # just use the first definition we found. if parsed_type.name.itl_name not in types: types[parsed_type.name.itl_name] = parsed_type + diff --git a/pyopendds/dev/itl2py/templates/user.cpp b/pyopendds/dev/itl2py/templates/user.cpp index 4a93fa0..9275491 100644 --- a/pyopendds/dev/itl2py/templates/user.cpp +++ b/pyopendds/dev/itl2py/templates/user.cpp @@ -43,6 +43,10 @@ class Type { /*{{ type.new_lines | indent(4) }}*/ py = PyObject_CallObject(cls, args); /*{% else %}*/ + /*{% if type.sequence %}*/ + if (py) Py_DECREF(py); + py = nullptr; + /*{% endif %}*/ if (py) { if (PyObject_IsInstance(cls, py) != 1) { throw Exception("Not a {{ type.py_name }}", PyExc_TypeError); @@ -63,7 +67,6 @@ class Type { cpp = static_cast(PyLong_AsLong(py)); /*{% else %}*/ if (py) { - if (PyObject_IsInstance(py, cls) != 1) { throw Exception("Not a {{ type.py_name }}", PyExc_TypeError); } diff --git a/pyopendds/dev/itl2py/templates/user.py b/pyopendds/dev/itl2py/templates/user.py index 0d4391d..6ca3313 100644 --- a/pyopendds/dev/itl2py/templates/user.py +++ b/pyopendds/dev/itl2py/templates/user.py @@ -5,7 +5,10 @@ {% if has_enum -%} from enum import IntFlag as _pyopendds_enum {%- endif %} +{% if has_sequence -%} +{%- endif %} {% for type in types -%} + {%- if type.struct %} @_pyopendds_struct @@ -21,6 +24,10 @@ class {{ type.local_name }}(_pyopendds_enum): {%- for member in type.enum.members %} {{ member.name }} = {{ member.value }} {%- endfor %} +{%- elif type.sequence %} + +class {{ type.local_name }}(list): + pass {%- else %} # {{ type.local_name }} was left unimplmented {% endif -%} diff --git a/tests/basic_test/CMakeLists.txt b/tests/basic_test/CMakeLists.txt index 4b8b942..c370ca0 100644 --- a/tests/basic_test/CMakeLists.txt +++ b/tests/basic_test/CMakeLists.txt @@ -17,6 +17,18 @@ export( FILE "${CMAKE_CURRENT_BINARY_DIR}/basic_idlConfig.cmake" ) +add_library(airbus_idl SHARED) +if(${CPP11_IDL}) + set(opendds_idl_mapping_option "-Lc++11") +endif() +OPENDDS_TARGET_SOURCES(airbus_idl "airbusdds.idl" + OPENDDS_IDL_OPTIONS "-Gitl" "${opendds_idl_mapping_option}") +target_link_libraries(airbus_idl PUBLIC OpenDDS::Dcps) +export( + TARGETS airbus_idl + FILE "${CMAKE_CURRENT_BINARY_DIR}/airbus_idlConfig.cmake" +) + add_executable(publisher publisher.cpp) target_link_libraries(publisher OpenDDS::OpenDDS basic_idl) if(${CPP11_IDL}) diff --git a/tests/basic_test/airbusdds.idl b/tests/basic_test/airbusdds.idl new file mode 100644 index 0000000..23ee831 --- /dev/null +++ b/tests/basic_test/airbusdds.idl @@ -0,0 +1,14 @@ +struct ContTrajSegment { + double altitude; + double CAStgt; + double centerLLat; + double centerLLong; +}; + + + +struct ContingencyTrajectory { + string dest; + sequence traj; + long trajIndex; +}; \ No newline at end of file diff --git a/tests/basic_test/basic.idl b/tests/basic_test/basic.idl index 67dbc63..82a7b1c 100644 --- a/tests/basic_test/basic.idl +++ b/tests/basic_test/basic.idl @@ -5,10 +5,18 @@ module basic { acceleration }; + struct Sample { + long value; + string where; + }; + + typedef sequence seqSample; + @topic struct Reading { ReadingKind kind; long value; string where; + seqSample sampleSeq; }; }; diff --git a/tests/basic_test/publisher.py b/tests/basic_test/publisher.py index dc18922..9c8f51e 100644 --- a/tests/basic_test/publisher.py +++ b/tests/basic_test/publisher.py @@ -4,7 +4,7 @@ from pyopendds import \ init_opendds, DomainParticipant, StatusKind, PyOpenDDS_Error -from pybasic.basic import Reading, ReadingKind +from pybasic.basic import * if __name__ == "__main__": try: @@ -26,13 +26,23 @@ print('Found subscriber!') write_sample_speed = Reading() + s1 = Sample() + s1.value = 1 + s1.where = "toto1" + + s2 = Sample() + s2.value = 2 + s2.where = "toto2" + write_sample_accel = Reading() write_sample_dist = Reading() + while True: time.sleep(1) write_sample_speed.kind = ReadingKind.speed write_sample_speed.value = 123 write_sample_speed.where = "somewhere" + write_sample_speed.sampleSeq = seqSample([s1, s2]) # Read and Print Sample rc = writer.write(write_sample_speed) print(rc) @@ -41,6 +51,7 @@ write_sample_accel.kind = ReadingKind.acceleration write_sample_accel.value = 2 write_sample_accel.where = "everywhere" + write_sample_accel.sampleSeq = seqSample([s1, s2]) # Read and Print Sample rc = writer.write(write_sample_accel) print(rc) @@ -49,6 +60,7 @@ write_sample_dist.kind = ReadingKind.distance write_sample_dist.value = 543 write_sample_dist.where = "anywhere" + write_sample_dist.sampleSeq = seqSample([s1, s2]) # Read and Print Sample rc = writer.write(write_sample_dist) print(rc) diff --git a/tests/basic_test/subscriber.py b/tests/basic_test/subscriber.py index f32c9ec..6a3c49e 100644 --- a/tests/basic_test/subscriber.py +++ b/tests/basic_test/subscriber.py @@ -4,7 +4,7 @@ from pyopendds import \ init_opendds, DomainParticipant, StatusKind, PyOpenDDS_Error -from pybasic.basic import Reading +from pybasic.basic import * class TestClass: From 27cf7223043f93b99f63b8b80589c30387b2f629 Mon Sep 17 00:00:00 2001 From: David Pierret Date: Mon, 5 Jul 2021 17:16:12 +0200 Subject: [PATCH 10/12] Add Char type to IntegerType --- pyopendds/dev/include/pyopendds/user.hpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pyopendds/dev/include/pyopendds/user.hpp b/pyopendds/dev/include/pyopendds/user.hpp index 314c56f..5e92bfc 100644 --- a/pyopendds/dev/include/pyopendds/user.hpp +++ b/pyopendds/dev/include/pyopendds/user.hpp @@ -101,6 +101,8 @@ template<> class Type: public IntegerType {}; typedef ::CORBA::Short i16; template<> class Type: public IntegerType {}; +typedef ::CORBA::Char c8; +template<> class Type: public IntegerType {}; // TODO: Put Other Integer Types Here const char* string_data(const std::string& cpp) From c9593f70bd7fc796d71d47fc31df808c0334f508 Mon Sep 17 00:00:00 2001 From: David Pierret Date: Fri, 9 Jul 2021 15:53:28 +0200 Subject: [PATCH 11/12] Add more supported types --- pyopendds/dev/include/pyopendds/user.hpp | 31 +++++++++++++++++++----- 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/pyopendds/dev/include/pyopendds/user.hpp b/pyopendds/dev/include/pyopendds/user.hpp index 5e92bfc..d5d7348 100644 --- a/pyopendds/dev/include/pyopendds/user.hpp +++ b/pyopendds/dev/include/pyopendds/user.hpp @@ -70,20 +70,36 @@ class IntegerType { static void cpp_to_python(const T& cpp, PyObject*& py) { if (limits::is_signed) { - py = PyLong_FromLong(cpp); + if (sizeof(cpp) > sizeof(long)) { + py = PyLong_FromLongLong(cpp); + } else { + py = PyLong_FromLong(cpp); + } } else { - py = PyLong_FromUnsignedLong(cpp); + if (sizeof(cpp) > sizeof(long)) { + py = PyLong_FromUnsignedLongLong(cpp); + } else { + py = PyLong_FromUnsignedLong(cpp); + } } if (!py) throw Exception(); } static void python_to_cpp(PyObject* py, T& cpp) { - int value; //todo: change to LongType + T value; //todo: change to LongType if (limits::is_signed) { - value = PyLong_AsLong(py); + if (sizeof(cpp) > sizeof(long)) { + value = PyLong_AsLongLong(py); + } else { + value = PyLong_AsLong(py); + } } else { - value = PyLong_AsUnsignedLong(py); + if (sizeof(cpp) > sizeof(long)) { + value = PyLong_AsUnsignedLongLong(py); + } else { + value = PyLong_AsUnsignedLong(py); + } } if (value < limits::min() || value > limits::max()) { throw Exception( @@ -95,6 +111,9 @@ class IntegerType { }; +typedef ::CORBA::LongLong i64; +template<> class Type: public IntegerType {}; + typedef ::CORBA::Long i32; template<> class Type: public IntegerType {}; @@ -178,7 +197,7 @@ class FloatingType { static void cpp_to_python(const T& cpp, PyObject*& py) { - py = PyFloat_FromDouble(cpp); + py = PyFloat_FromDouble((double)cpp); if (!py) throw Exception(); } From 7046ac457d3cec45d7768b6172d9ed6ff2da65a6 Mon Sep 17 00:00:00 2001 From: David Pierret Date: Mon, 26 Jul 2021 14:32:57 +0200 Subject: [PATCH 12/12] partial Qos addition to pyOpenDDS Signed-off-by: David Pierret --- pyopendds/DataReader.py | 10 ++- pyopendds/DataWriter.py | 5 +- pyopendds/Publisher.py | 4 +- pyopendds/Qos.py | 49 +++++++++++ pyopendds/ext/_pyopendds.cpp | 149 +++++++++++++++++++++++++++++++++ tests/basic_test/airbusdds.idl | 14 ---- 6 files changed, 211 insertions(+), 20 deletions(-) create mode 100644 pyopendds/Qos.py delete mode 100644 tests/basic_test/airbusdds.idl diff --git a/pyopendds/DataReader.py b/pyopendds/DataReader.py index 18a1b24..2db34c2 100644 --- a/pyopendds/DataReader.py +++ b/pyopendds/DataReader.py @@ -15,7 +15,6 @@ class DataReader: def __init__(self, subscriber: Subscriber, topic: Topic, qos=None, listener=None): self.topic = topic - self.qos = qos self.listener = listener self.subscriber = subscriber subscriber.readers.append(self) @@ -32,12 +31,17 @@ def take_next_sample(self): def onDataAvailCallback(self): sample = None + #print(f"------ onDataAvailCallback") if hasattr(self, 'topic'): sample = self.take_next_sample() + #print(f"---------- Sample {sample}") else: - print("Error, no topic in self => " + self.__qualname__, file=sys.stderr) + print("------ Error, no topic in self => " + self.__qualname__) if sample is not None: self.listener(sample) else: - print("Error, data not valid", file=sys.stderr) + print("------ Error, data not valid") + def update_reader_qos(self, qos: DataReaderQos): + from _pyopendds import update_reader_qos + return update_reader_qos(self, qos) \ No newline at end of file diff --git a/pyopendds/DataWriter.py b/pyopendds/DataWriter.py index ba8ff61..3527112 100644 --- a/pyopendds/DataWriter.py +++ b/pyopendds/DataWriter.py @@ -13,7 +13,6 @@ class DataWriter: def __init__(self, publisher: Publisher, topic: Topic, qos=None, listener=None): self.topic = topic - self.qos = qos self.listener = listener self.publisher = publisher publisher.writers.append(self) @@ -27,3 +26,7 @@ def wait_for(self, status: StatusKind, timeout: TimeDurationType): def write(self, sample): return self.topic._ts_package.write(self, sample) + + def update_writer_qos(self, qos: DataWriterQos): + from _pyopendds import update_writer_qos + return update_writer_qos(self, qos) \ No newline at end of file diff --git a/pyopendds/Publisher.py b/pyopendds/Publisher.py index 8c19715..d5bc2d4 100644 --- a/pyopendds/Publisher.py +++ b/pyopendds/Publisher.py @@ -19,5 +19,5 @@ def __init__(self, participant: DomainParticipant, qos=None, listener=None): from _pyopendds import create_publisher create_publisher(self, participant) - def create_datawriter(self, topic: Topic, qos=None, listener=None) -> DataWriter: - return DataWriter(self, topic, qos, listener) + def create_datawriter(self, topic: Topic, listener=None) -> DataWriter: + return DataWriter(self, topic, listener) diff --git a/pyopendds/Qos.py b/pyopendds/Qos.py new file mode 100644 index 0000000..92f50fa --- /dev/null +++ b/pyopendds/Qos.py @@ -0,0 +1,49 @@ +from enum import IntEnum + + +class DurabilityQosPolicyKind(IntEnum): + VOLATILE_DURABILITY_QOS = 0, + TRANSIENT_LOCAL_DURABILITY_QOS = 1, + TRANSIENT_DURABILITY_QOS = 2, + PERSISTENT_DURABILITY_QOS = 3 + + +class ReliabilityQosPolicyKind(IntEnum): + BEST_EFFORT_RELIABILITY_QOS = 0, + RELIABLE_RELIABILITY_QOS = 1 + + +class HistoryQosPolicyKind(IntEnum): + KEEP_LAST_HISTORY_QOS = 0, + KEEP_ALL_HISTORY_QOS = 1 + + +class DurabilityQosPolicy: + def __init__(self): + self.kind = DurabilityQosPolicyKind.PERSISTENT_DURABILITY_QOS + + +class ReliabilityQosPolicy: + def __init__(self): + self.kind = ReliabilityQosPolicyKind.RELIABLE_RELIABILITY_QOS + self.max_blocking_time = 0 + + +class HistoryQosPolicy: + def __init__(self): + self.kind = HistoryQosPolicyKind.KEEP_LAST_HISTORY_QOS + self.depth = 0 + + +class DataWriterQos: + def __init__(self): + self.durability = DurabilityQosPolicy() + self.reliability = ReliabilityQosPolicy() + self.history = HistoryQosPolicy() + + +class DataReaderQos: + def __init__(self): + self.durability = DurabilityQosPolicy() + self.reliability = ReliabilityQosPolicy() + self.history = HistoryQosPolicy() diff --git a/pyopendds/ext/_pyopendds.cpp b/pyopendds/ext/_pyopendds.cpp index 034076c..273ab86 100644 --- a/pyopendds/ext/_pyopendds.cpp +++ b/pyopendds/ext/_pyopendds.cpp @@ -573,6 +573,153 @@ PyObject* datawriter_wait_for(PyObject* self, PyObject* args) Py_RETURN_NONE; } +PyObject* update_writer_qos(PyObject* self, PyObject* args) +{ + Ref pydatawriter; + Ref pyQos; + + Ref pydurability; + Ref pyreliability; + Ref pyhistory; + Ref pydurabilityKind; + Ref pyreliabilityKind; + Ref pyhistoryKind; + + if (!PyArg_ParseTuple(args, "OO", + &*pydatawriter, &*pyQos)) { + return nullptr; + } + pydatawriter++; + pyQos++; + + // Get DataWriter + DDS::DataWriter* writer = get_capsule(*pydatawriter); + if (!writer) return nullptr; + + std::cerr << "get default qos" << std::endl; + // Create Qos for the data writer according to the spec + DDS::DataWriterQos qos; + writer->get_publisher()->get_default_datawriter_qos(qos); + + + std::cerr << "get durability" << std::endl; + pydurability = PyObject_GetAttrString(*pyQos, "durability"); + if (!pydurability) return nullptr; + pydurability ++; + + std::cerr << "get reliability" << std::endl; + pyreliability = PyObject_GetAttrString(*pyQos, "reliability"); + if (!pyreliability) return nullptr; + pyreliability ++; + + std::cerr << "get history" << std::endl; + pyhistory = PyObject_GetAttrString(*pyQos, "history"); + if (!pyhistory) return nullptr; + pyhistory ++; + + + std::cerr << "get dura kind" << std::endl; + pydurabilityKind = PyObject_GetAttrString(*pydurability, "kind"); + if (!pydurabilityKind) return nullptr; + pydurabilityKind ++; + std::cerr << "AsLong" << std::endl; + qos.durability.kind = (DDS::DurabilityQosPolicyKind) PyLong_AsLong(*pydurabilityKind); + + std::cerr << "get rela kind" << std::endl; + pyreliabilityKind = PyObject_GetAttrString(*pyreliability, "kind"); + if (!pyreliabilityKind) return nullptr; + pyreliabilityKind ++; + std::cerr << "AsLong" << std::endl; + qos.reliability.kind = (DDS::ReliabilityQosPolicyKind) PyLong_AsLong(*pyreliabilityKind); + + std::cerr << "get histo kind" << std::endl; + pyhistoryKind = PyObject_GetAttrString(*pyhistory, "kind"); + if (!pyhistoryKind) return nullptr; + pyhistoryKind ++; + + std::cerr << "AsLong" << std::endl; + qos.history.kind = (DDS::HistoryQosPolicyKind) PyLong_AsLong(*pyhistoryKind); + + std::cerr << "set QOS" << std::endl; + writer->set_qos (qos); + + std::cerr << "return" << std::endl; + Py_RETURN_NONE; +} + +PyObject* update_reader_qos(PyObject* self, PyObject* args) +{ + Ref pydatareader; + Ref pyQos; + + Ref pydurability; + Ref pyreliability; + Ref pyhistory; + Ref pydurabilityKind; + Ref pyreliabilityKind; + Ref pyhistoryKind; + Ref pyhistorydepth; + Ref pyreliabilitymax; + + if (!PyArg_ParseTuple(args, "OO", + &*pydatareader, &*pyQos)) { + return nullptr; + } + pydatareader++; + pyQos++; + + // Get DataReader + DDS::DataReader* reader = get_capsule(*pydatareader); + if (!reader) return nullptr; + + // Create Qos for the data writer according to the spec + DDS::DataReaderQos qos; + reader->get_subscriber()->get_default_datareader_qos(qos); + + pydurability = PyObject_GetAttrString(*pyQos, "durability"); + if (!pydurability) return nullptr; + pydurability ++; + + pyreliability = PyObject_GetAttrString(*pyQos, "reliability"); + if (!pyreliability) return nullptr; + pyreliability ++; + + pyhistory = PyObject_GetAttrString(*pyQos, "history"); + if (!pyhistory) return nullptr; + pyhistory ++; + + + pydurabilityKind = PyObject_GetAttrString(*pydurability, "kind"); + if (!pydurabilityKind) return nullptr; + pydurabilityKind ++; + qos.durability.kind = (DDS::DurabilityQosPolicyKind) PyLong_AsLong(*pydurabilityKind); + + pyreliabilityKind = PyObject_GetAttrString(*pyreliability, "kind"); + if (!pyreliabilityKind) return nullptr; + pyreliabilityKind ++; + qos.reliability.kind = (DDS::ReliabilityQosPolicyKind) PyLong_AsLong(*pyreliabilityKind); + + pyreliabilitymax = PyObject_GetAttrString(*pyreliability, "max_blocking_time"); + if (!pyreliabilitymax) return nullptr; + pyreliabilitymax ++; + qos.history.depth = PyLong_AsLong(*pyreliabilitymax); + + + pyhistoryKind = PyObject_GetAttrString(*pyhistory, "kind"); + if (!pyhistoryKind) return nullptr; + pyhistoryKind ++; + + qos.history.kind = (DDS::HistoryQosPolicyKind) PyLong_AsLong(*pyhistoryKind); + + pyhistorydepth = PyObject_GetAttrString(*pyhistory, "depth"); + if (!pyhistorydepth) return nullptr; + pyhistorydepth ++; + qos.history.depth = PyLong_AsLong(*pyhistorydepth); + + reader->set_qos (qos); + Py_RETURN_NONE; +} + /// Documentation for Internal Python Objects const char* internal_docstr = "Internal to PyOpenDDS, not for use directly!"; @@ -590,6 +737,8 @@ PyMethodDef pyopendds_Methods[] = { {"create_datawriter", create_datawriter, METH_VARARGS, internal_docstr}, {"datareader_wait_for", datareader_wait_for, METH_VARARGS, internal_docstr}, {"datawriter_wait_for", datawriter_wait_for, METH_VARARGS, internal_docstr}, + {"update_writer_qos", update_writer_qos, METH_VARARGS, internal_docstr}, + {"update_reader_qos", update_reader_qos, METH_VARARGS, internal_docstr}, {nullptr, nullptr, 0, nullptr} }; diff --git a/tests/basic_test/airbusdds.idl b/tests/basic_test/airbusdds.idl deleted file mode 100644 index 23ee831..0000000 --- a/tests/basic_test/airbusdds.idl +++ /dev/null @@ -1,14 +0,0 @@ -struct ContTrajSegment { - double altitude; - double CAStgt; - double centerLLat; - double centerLLong; -}; - - - -struct ContingencyTrajectory { - string dest; - sequence traj; - long trajIndex; -}; \ No newline at end of file