You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
144 lines
5.2 KiB
144 lines
5.2 KiB
# License: Apache 2.0. See LICENSE file in root directory.
|
|
# Copyright(c) 2022 Intel Corporation. All Rights Reserved.
|
|
|
|
import pyrealdds as dds
|
|
from rspy import log
|
|
from time import sleep
|
|
import threading
|
|
from pyrealdds import timestr, no_suffix, rel, abs
|
|
|
|
|
|
class writer:
|
|
"""
|
|
Just to enable simple one-line syntax:
|
|
flexible.writer( participant, "blah" ).write( '{"field" : 1}' )
|
|
"""
|
|
|
|
def __init__( self, participant, topic, qos = None ):
|
|
if type(topic) is str:
|
|
self.handle = dds.message.flexible.create_topic( participant, topic )
|
|
elif type(topic) is dds.topic:
|
|
self.handle = topic
|
|
else:
|
|
raise RuntimeError( "invalid 'topic' argument: " + type(topic) )
|
|
self.n_readers = 0
|
|
self.name = self.handle.name()
|
|
self.writer = dds.topic_writer( self.handle )
|
|
self.writer.on_publication_matched( self._on_publication_matched )
|
|
self.writer.run( qos or dds.topic_writer.qos() )
|
|
|
|
def _on_publication_matched( self, writer, d_readers ):
|
|
n_readers = self.n_readers + d_readers
|
|
log.d( f'{self.name}.on_publication_matched {d_readers:+} -> {n_readers}' )
|
|
self.n_readers = n_readers
|
|
|
|
def wait_for_readers( self, n_readers = 1, timeout = 3.0 ):
|
|
while self.n_readers < n_readers:
|
|
timeout -= 0.25
|
|
if timeout > 0:
|
|
sleep( 0.25 )
|
|
else:
|
|
raise RuntimeError( f'timed out waiting for {n_readers} readers' )
|
|
|
|
def write( self, json_string ):
|
|
msg = dds.message.flexible( json_string )
|
|
now = dds.now()
|
|
msgs = str(msg)
|
|
msg.write_to( self.writer )
|
|
log.d( f'{self.name}.write {msgs} @{timestr(now,no_suffix)}{timestr(dds.now(),now)}' )
|
|
|
|
def stop( self ):
|
|
self.writer = self.handle = None
|
|
|
|
|
|
class reader:
|
|
"""
|
|
Just to enable simple one-line syntax:
|
|
msg = flexible.reader( participant, "blah" ).read()
|
|
"""
|
|
|
|
def __init__( self, participant, topic, qos = None ):
|
|
if type(topic) is str:
|
|
self.handle = dds.message.flexible.create_topic( participant, topic )
|
|
elif type(topic) is dds.topic:
|
|
self.handle = topic
|
|
else:
|
|
raise RuntimeError( "invalid 'topic' argument: " + type(topic) )
|
|
self.n_writers = 0
|
|
self.name = self.handle.name()
|
|
self.data = []
|
|
self.samples = []
|
|
self._got_something = threading.Event()
|
|
self.reader = dds.topic_reader( self.handle )
|
|
self.reader.on_data_available( self._on_data_available )
|
|
self.reader.on_subscription_matched( self._on_subscription_matched )
|
|
self.reader.run( qos or dds.topic_reader.qos() )
|
|
|
|
def _on_subscription_matched( self, reader, d_writers ):
|
|
n_writers = self.n_writers + d_writers
|
|
log.d( f'{self.name}.on_subscription_matched {d_writers:+} -> {n_writers}' )
|
|
self.n_writers = n_writers
|
|
|
|
def wait_for_writers( self, n_writers = 1, timeout = 3.0 ):
|
|
"""
|
|
Wait until a writer/publisher has our topic open
|
|
"""
|
|
while self.n_writers != n_writers:
|
|
timeout -= 0.25
|
|
if timeout > 0:
|
|
sleep( 0.25 )
|
|
else:
|
|
raise RuntimeError( f'{self.name} timed out waiting for {n_writers} writers' )
|
|
|
|
def _on_data_available( self, reader ):
|
|
now = dds.now()
|
|
#log.d( f'{topic_name}.on_data_available @{now}' )
|
|
got_something = False
|
|
while True:
|
|
sample = dds.message.sample_info()
|
|
msg = dds.message.flexible.take_next( reader, sample )
|
|
if not msg:
|
|
if not got_something:
|
|
raise RuntimeError( "expected message not received!" )
|
|
break
|
|
received = sample.reception_timestamp()
|
|
log.d( f'{self.name}.on_data_available @{timestr(received,no_suffix)}{timestr(now,received,no_suffix)}{timestr(dds.now(),now)} {msg}' )
|
|
self.data += [msg]
|
|
self.samples += [sample]
|
|
got_something = True
|
|
self._got_something.set()
|
|
|
|
def read_sample( self, timeout=3.0 ):
|
|
"""
|
|
:param timeout: if empty, wait for this many seconds then raise an error
|
|
:return: a tuple of the next flexible message, and the sample read
|
|
"""
|
|
self.wait_for_data( timeout=timeout )
|
|
msg = self.data.pop(0)
|
|
if self.empty():
|
|
self._got_something.clear()
|
|
sample = self.samples.pop(0)
|
|
log.d( f'{self.name}.read_sample @{timestr(dds.now(),sample.reception_timestamp())}' )
|
|
return (msg,sample)
|
|
|
|
def read( self, timeout=3.0 ):
|
|
"""
|
|
:param timeout: if empty, wait for this many seconds then raise an error
|
|
:return: the next flexible message read
|
|
"""
|
|
msg, sample = self.read_sample( timeout=timeout )
|
|
return msg
|
|
|
|
def wait_for_data( self, timeout = 3.0 ):
|
|
"""
|
|
Wait until data is available or timeout seconds. If still none, raises an error.
|
|
"""
|
|
if not self._got_something.wait( timeout ):
|
|
raise RuntimeError( "timed out waiting for data" )
|
|
|
|
def empty( self ):
|
|
"""
|
|
:return: True if no data is available
|
|
"""
|
|
return not self.data
|