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.

242 lines
11 KiB

# License: Apache 2.0. See LICENSE file in root directory.
# Copyright(c) 2023 Intel Corporation. All Rights Reserved.
#test:donotrun:!dds
#test:retries:gha 2
# We disable under Linux for now, pending feedback from FastDDS team:
# Having two participants in the same process ("client" and "librs" below) usually works, but in this case the former
# is from pyrealdds and the latter from pyrealsense2. The two somehow interfere so the server doesn't even see the
# latter and we have a problem where the broadcaster does not work.
#test:donotrun:linux
import pyrealdds as dds
from rspy import log, test
from time import sleep
from rspy.timer import Timer
dds.debug( log.is_debug_on(), 'C ' )
log.nested = 'C '
import d435i
participant = dds.participant()
participant.init( 123, "client" )
# run the server in another process: GHA has some issue with it in the same process...
import os.path
cwd = os.path.dirname(os.path.realpath(__file__))
remote_script = os.path.join( cwd, 'metadata-server.py' )
with test.remote( remote_script, nested_indent=" S" ) as remote:
remote.wait_until_ready()
# set up the client device and keep all its streams - this is connected directly and we can get notifications on it!
device_direct = dds.device( participant, d435i.device_info )
device_direct.wait_until_ready()
test.check( device_direct.is_ready(), on_fail=test.ABORT )
for stream_direct in device_direct.streams():
pass # should be only one
topic_name = 'rt/' + d435i.device_info.topic_root + '_' + stream_direct.name()
import threading
image_received = threading.Event()
image_content = []
def on_image_available( stream, image ):
log.d( f'----> image {image}')
image_content.append( image )
image_received.set()
stream_direct.on_data_available( on_image_available )
stream_direct.open( topic_name, dds.subscriber( participant ) )
stream_direct.start_streaming()
def detect_image():
image_content.clear()
image_received.clear()
def wait_for_image( timeout=1, count=None ):
timer = Timer( timeout )
while not timer.has_expired():
if not image_received.wait( timer.time_left() ):
raise TimeoutError( 'timeout waiting for image' )
if count is None or count <= len(image_content):
return
image_received.clear()
if count is None:
raise TimeoutError( 'timeout waiting for image' )
raise TimeoutError( f'timeout waiting for {count} images; {len(image_content)} received' )
class image_expected:
def __init__( self, timeout=1, count=None ):
self._timeout = timeout
self._count = count
def __enter__( self ):
detect_image()
def __exit__( self, type, value, traceback ):
if type is None: # If an exception is thrown, don't do anything
wait_for_image( timeout=self._timeout, count=self._count )
metadata_received = threading.Event()
metadata_content = []
def on_metadata_available( device, md ):
log.d( f'----> metadata[{len(metadata_content)}]= {md}')
metadata_content.append( md )
metadata_received.set()
metadata_subscription = device_direct.on_metadata_available( on_metadata_available )
def detect_metadata():
metadata_content.clear()
metadata_received.clear()
def wait_for_metadata( timeout=1, count=None ):
timer = Timer( timeout )
while not timer.has_expired():
if not metadata_received.wait( timer.time_left() ):
raise TimeoutError( 'timeout waiting for metadata' )
if count is None or count <= len(metadata_content):
return
metadata_received.clear()
if count is None:
raise TimeoutError( 'timeout waiting for metadata' )
raise TimeoutError( f'timeout waiting for {count} metadata; {len(metadata_content)} received' )
class metadata_expected:
def __init__( self, expected_md=None, timeout=1, count=None ):
self._md = expected_md
self._timeout = timeout
self._count = count
def __enter__( self ):
detect_metadata()
def __exit__( self, type, value, traceback ):
if type is None: # If an exception is thrown, don't do anything
wait_for_metadata( timeout=self._timeout, count=self._count )
if self._md is not None:
if test.check( len(metadata_content), description = 'Expected metadata but got none' ):
test.check_equal( metadata_content[0], self._md )
#############################################################################################
#
with test.closure( "No librs syncer; direct from server" ):
md = { 'stream-name' : 'Color', 'invalid-metadata' : True }
with metadata_expected( md ):
remote.run( f'device_server.publish_metadata( {md} )' )
#
#############################################################################################
#
with test.closure( "Broadcast the device" ): # otherwise librs won't see it
remote.run( 'broadcast()' )
#
#############################################################################################
#
with test.closure( "Initialize librs device", on_fail=test.ABORT ):
import pyrealsense2 as rs
if log.is_debug_on():
rs.log_to_console( rs.log_severity.debug )
from dds import wait_for_devices
context = rs.context( { 'dds': { 'enabled': True, 'domain': 123, 'participant': 'librs' }} )
only_sw_devices = int(rs.product_line.sw_only) | int(rs.product_line.any_intel)
device = wait_for_devices( context, only_sw_devices, n=1. )
sensors = device.sensors
test.check_equal( len(sensors), 1, on_fail=test.RAISE )
sensor = sensors[0]
test.check_equal( sensor.get_info( rs.camera_info.name ), 'RGB Camera', on_fail=test.RAISE )
del sensors
profile = rs.video_stream_profile( sensor.get_stream_profiles()[0] ) # take the first one
log.d( f'using profile {profile}')
encoding = dds.video_encoding.from_rs2( profile.format() )
remote.run( f'img = new_image( {profile.width()}, {profile.height()}, {profile.bytes_per_pixel()} )', on_fail='abort' )
sensor.open( [profile] )
queue = rs.frame_queue( 100 )
sensor.start( queue )
#
#############################################################################################
#
with test.closure( "Metadata alone should not come out" ):
with metadata_expected( count=20 ):
for i in range(20):
md = { 'stream-name' : 'Color', 'header' : { 'i' : i }, 'metadata' : {} }
remote.run( f'device_server.publish_metadata( {md} )' )
sleep( 0.25 ) # plus some extra for librs...
test.check_false( queue.poll_for_frame() ) # we didn't send any images, shouldn't get any frames!
#
#############################################################################################
#
with test.closure( 'MD after an image, without frame-number' ):
timestamp = dds.now()
remote.run( f'color_stream.start_streaming( dds.video_encoding( "{encoding}" ), img.width, img.height )' )
# It will take the image a lot longer to get anywhere than the metadata
with image_expected():
remote.run( f'publish_image( img, dds.time.from_ns( {timestamp.to_ns()} ))' )
sleep( 0.25 ) # plus some extra for librs...
test.check_false( queue.poll_for_frame() ) # the image should still be pending in the syncer
with metadata_expected():
md = {
'stream-name' : 'Color',
'header' : {
'timestamp' : timestamp.to_ns()
},
'metadata': {
'White Balance' : 0xbaad
}
}
remote.run( f'device_server.publish_metadata( {md} )' )
f = queue.wait_for_frame( 250 ) # A frame should now be available
log.d( '---->', f )
if test.check( f ) and test.check_equal( f.get_frame_number(), 1 ): # first frame so far!
test.check_approx_abs( f.get_timestamp() * 1e-3, image_content[0].timestamp.to_double(), 1e-6 ) # frames are in ms
test.check_false( f.supports_frame_metadata( rs.frame_metadata_value.actual_fps ) )
if test.check( f.supports_frame_metadata( rs.frame_metadata_value.white_balance ) ):
test.check_equal( f.get_frame_metadata( rs.frame_metadata_value.white_balance ), 0xbaad )
test.check_false( queue.poll_for_frame() ) # the image should still be pending in the syncer
#
#############################################################################################
#
with test.closure( 'Image after MD, with frame-number' ):
timestamp = dds.now()
with metadata_expected():
md = {
'stream-name' : 'Color',
'header' : {
'frame-number' : 1234,
'timestamp' : timestamp.to_ns()
},
'metadata': {
'Temperature' : 0xf00d
}
}
remote.run( f'device_server.publish_metadata( {md} )' )
sleep( 0.25 )
test.check_false( queue.poll_for_frame() )
with image_expected():
remote.run( f'publish_image( img, dds.time.from_ns( {timestamp.to_ns()} ))' )
f = queue.wait_for_frame( 250 )
log.d( '---->', f )
if test.check( f ) and test.check_equal( f.get_frame_number(), 1234 ):
test.check_approx_abs( f.get_timestamp() * 1e-3, image_content[0].timestamp.to_double(), 1e-6 ) # frames are in ms
test.check_false( f.supports_frame_metadata( rs.frame_metadata_value.white_balance ) )
if test.check( f.supports_frame_metadata( rs.frame_metadata_value.temperature ) ):
test.check_equal( f.get_frame_metadata( rs.frame_metadata_value.temperature ), 0xf00d )
test.check_false( queue.poll_for_frame() ) # the image should still be pending in the syncer
#
#############################################################################################
#
with test.closure( "Stop streaming" ):
remote.run( 'color_stream.stop_streaming()', on_fail='log' )
#
#############################################################################################
#
with test.closure( "Metadata without a stream name is ignored" ):
pass
#
#############################################################################################
#
test.print_results_and_exit()