Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add features to pyopendds #23

Closed
wants to merge 12 commits into from
22 changes: 20 additions & 2 deletions pyopendds/DataReader.py
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -13,17 +15,33 @@ 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)

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
return datareader_wait_for(self, status, *normalize_time_duration(timeout))

def take_next_sample(self):
return self.topic._ts_package.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__)
if sample is not None:
self.listener(sample)
else:
print("------ Error, data not valid")

def update_reader_qos(self, qos: DataReaderQos):
from _pyopendds import update_reader_qos
return update_reader_qos(self, qos)
32 changes: 31 additions & 1 deletion pyopendds/DataWriter.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,32 @@
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:
pass

def __init__(self, publisher: Publisher, topic: Topic, qos=None, listener=None):
self.topic = topic
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)

def update_writer_qos(self, qos: DataWriterQos):
from _pyopendds import update_writer_qos
return update_writer_qos(self, qos)
2 changes: 1 addition & 1 deletion pyopendds/DomainParticipant.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
5 changes: 3 additions & 2 deletions pyopendds/Publisher.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from __future__ import annotations

from .DataWriter import DataWriter
from .Topic import Topic

from typing import TYPE_CHECKING
Expand All @@ -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, listener=None) -> DataWriter:
return DataWriter(self, topic, listener)
49 changes: 49 additions & 0 deletions pyopendds/Qos.py
Original file line number Diff line number Diff line change
@@ -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()
152 changes: 139 additions & 13 deletions pyopendds/dev/include/pyopendds/user.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,40 @@ class Type /*{
static void python_to_cpp(PyObject* py, T& cpp);
}*/;


template<typename T>
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<bool>: public BooleanType<bool> {};

template<typename T>
class IntegerType {
public:
Expand All @@ -30,39 +64,64 @@ class IntegerType {

static PyObject* get_python_class()
{
return PyLong_Type;
return PyLong_FromLong(0);
}

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)
{
LongType value;
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(
"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::LongLong i64;
template<> class Type<i64>: public IntegerType<i64> {};

typedef ::CORBA::Long i32;
template<> class Type<i32>: public IntegerType<i32> {};

typedef ::CORBA::Short i16;
template<> class Type<i16>: public IntegerType<i16> {};

typedef ::CORBA::Char c8;
template<> class Type<c8>: public IntegerType<c8> {};
// TODO: Put Other Integer Types Here

const char* string_data(const std::string& cpp)
Expand Down Expand Up @@ -90,7 +149,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)
Expand All @@ -101,9 +160,16 @@ 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_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);
Py_XDECREF(str);
}
};

Expand All @@ -119,6 +185,39 @@ typedef
template<> class Type<s8>: public StringType<s8> {};
// TODO: Put Other String/Char Types Here

template<typename T>
class FloatingType {
public:
typedef std::numeric_limits<T> limits;

static PyObject* get_python_class()
{
return PyFloat_FromDouble(0);
}

static void cpp_to_python(const T& cpp, PyObject*& py)
{
py = PyFloat_FromDouble((double)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<f32>: public FloatingType<f32> {};
template<> class Type<f64>: public FloatingType<f64> {};
// TODO: FloatingType for floating point type

class TopicTypeBase {
Expand All @@ -127,6 +226,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<TopicTypeBase> Ptr;
typedef std::map<PyObject*, Ptr> TopicTypes;
Expand Down Expand Up @@ -215,25 +315,51 @@ 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();
}
ws->detach_condition(read_condition);
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();
}

PyObject* rv = nullptr;
Type<IdlType>::cpp_to_python(sample, rv);
if (info.valid_data)
Type<IdlType>::cpp_to_python(sample, rv);
else
rv = Py_None;

return rv;
}

PyObject* write(PyObject* pywriter, PyObject* pysample)
{
DDS::DataWriter* writer = get_capsule<DDS::DataWriter>(pywriter);
if (!writer) throw Exception();

DataWriter* writer_impl = DataWriter::_narrow(writer);
if (!writer_impl) {
throw Exception("Could not narrow writer implementation", Errors::PyOpenDDS_Error());
}

IdlType rv;
Type<IdlType>::python_to_cpp(pysample, rv);

DDS::ReturnCode_t rc = writer_impl->write(rv, DDS::HANDLE_NIL);
if (rc != DDS::RETCODE_OK) {
throw Exception(
"WRITE ERROR", Errors::PyOpenDDS_Error());
}
if (Errors::check_rc(rc)) return nullptr;

return PyLong_FromLong(rc);
}

PyObject* get_python_class()
{
return Type<IdlType>::get_python_class();
Expand Down
Loading