Built-in Sources and Sinks

REDHAWK includes a variety of helpers that allow a developer to inject data into or extract data from components deployed through the sandbox. The following sections describe each of these helpers and how they are used.

Data Sources

The DataSource module provides a mechanism for producing Bulk Input/Output (BulkIO) data to be sent to a provides (input) port. Once instantiated, a Python vector of data can be pushed by the DataSource.

An example instantiation and use of the Data Source module can be seen below:

>>> input_source = sb.DataSource()
>>> input_source.connect(my_comp)
>>> my_data = range(10000)
>>> sb.start()
>>> input_source.push(my_data)

When the DataSource sends data, it attempts to match the data type to the type of the associated provides (input) port. Alternatively, the data type may be set explicitly in the DataSource constructor. Note that the default type for the DataSource is short, which implies that values over 32768 may induce an exception.

The default setting for number of bytes per pushPacket() is 512000 bytes. Data is broken up into chunks of this size before being sent via the port’s pushPacket() method. To change this default size, set the bytesPerPush argument in the DataSource constructor.

To generate continuous data, add loop=True to the push call.

>>> input_source.push(my_data,loop=True)

To stop looping data, enter the following command:

>>> input_source.stop()

A specific module is provided for reading data from a file. This module, FileSource, is used and instantiated much like the DataSource module. The most significant difference between the two modules is the presence of a file name in the FileSource constructor.

>>> input_file = sb.FileSource("~/short_file.tmp", dataFormat="short")

Signal Related Information (SRI) keywords may be generated and sent with data from the DataSource module.

An example generate/send can be seen below:

>>> kw = sb.SRIKeyword("SOME_RF",155000000.0,"double")
>>> kw2 = sb.SRIKeyword("EFFECTIVE_BITS_PER_SAMPLE",16,"long")
>>> keywords = [kw, kw2]
>>> input_data = sb.DataSource()
>>> data = range(1000)
>>> input_data.connect(my_comp)
>>> sb.start()
>>> input_data.push(data,SRIKeywords=keywords)

The SRIKeyword() constructor takes in the following arguments:

Messages can be sent to components using the MessageSource module. Messages sent using the sendMessage() method can be one of four types: struct, dictionary, CORBA Any, and data types that can be mapped to a CORBA Any.

The default message ID is sb_struct:

>>> mySource = sb.MessageSource()
>>> myComponent = sb.launch("test_message_rx_cpp")
>>> mySource.connect(myComponent)
>>> sb.start()
>>> msg = {"val1":"test string", "val2":123}
>>> mySource.sendMessage(msg)

Data Sinks

The sandbox provides a variety of data sinks including DataSink, FileSink, MessageSink, and the ability to plot data using one of the following plot types: LinePlot, LinePSD, RasterPSD, and XYPlot. For more information about the plot types, refer to Plotting Data.

DataSink Example

The sandbox provides a DataSink module, which simply reads data from a uses (output) port. Below is an example instantiation and use of the DataSink module. In this example, data sent from myComponent’s uses (output) port is written to the received_data variable.

SRI associated with the packet can be viewed using the SRI() method:

>>> output_data = sb.DataSink()
>>> myComponent.connect(output_data)
>>> stream = output_data.getCurrentStream()
>>> received_data = stream.read()
>>> received_SRI = stream.SRI()

To block until a certain amount of data is received, specify the data length as an argument to the read() method:

>>> received_data = stream.read(100)

The eos() method indicates whether or not an End of Stream (EOS) was received:

>>> stream.eos()
False

The consume argument may be used to configure the read() method to move the read pointer forward a different length than what was read.

FileSink Example

Similar to the DataSource’s FileSource counterpart, the DataSink has an associated FileSink module for writing data to a file:

>>> output_file = sb.FileSink("~/some_file.tmp")
>>> another_comp.connect(output_file)

MessageSink Example

Messages may be displayed using the MessageSink module. Data sent to a running MessageSink is printed in the Python interpreter.

Below is an example of MessageSink usage:

>>> myComponent = sb.launch("test_message_send_cpp")
>>> myMessageSink = sb.MessageSink()
>>> myComponent.connect(myMessageSink)
>>> sb.start() # assume that message_src sends a message

In the above example, the received message is printed to the screen. MessageSink can either use a callback or a polling mechanism to retrieve messages.

>>> from ossie.utils import sb
>>> message_src = sb.launch('test_message_send_cpp')
>>> def msgCallback(msg_id, msg):
...   print msg_id, msg
>>> callback_msg = sb.MessageSink(messageCallback=msgCallback)
>>> retrieve_msg = sb.MessageSink(messageCallback=None, storeMessages=True)
>>> message_src.connect(callback_msg)
>>> message_src.connect(retrieve_msg)
>>> sb.start() # assume that message_src sends a message
>>> rcv_message = retrieve_msg.getMessages()

In the above example, a message source component (created at some previous time), is connected to two instances of MessageSink, one instance implements a callback function and the other instance does not. When the message sink implementing the callback function receives a message, it triggers the callback function. The message sink that does not have a callback implementation stores the messages until they are retrieved through the getMessages() function.

Passing a Struct to sendMessage Example

The following example demonstrates how to generate and send messages in the sandbox.

from ossie.utils import sb
from ossie.properties import simple_property

class MessageProp(object):
    val1 = simple_property(id_="val1",
                           type_="string",
                           defvalue="trm")
    val2 = simple_property(id_="val2",
                           type_="double",
                           defvalue=1211)
    def __init__(self):
          """Construct an initialized instance of this struct
             definition"""
          for attrname, classattr in type(self).__dict__.items():
              if type(classattr) == simple_property:
                  classattr.initialize(self)

      def __str__(self):
          """Return a string representation of this structure"""
          d = {}
          d["val1"] = self.val1
          d["val2"] = self.val2
          return str(d)

      def getId(self):
          return "message_prop"

      def isStruct(self):
          return True

      def getMembers(self):
          return [("val1",self.val1),("val2",self.val2)]

testmessage = MessageProp()
testmessage.val1 = "test string"
testmessage.val2 = 123
a = sb.MessageSource()
b = sb.launch("test_message_rx_cpp")
a.connect(b)
sb.start()
a.sendMessage(testmessage)

Plotting Data Example

The following example demonstrates how to use the sb.Plot() feature as a DataSink. Note that for plotting data, the REDHAWK IDE must be installed and the path to the eclipse directory of the installed IDE must be specified to the sandbox.

This can be done through the IDELocation() function:

>>> sb.IDELocation("/path/to/ide/eclipse")

This can also be done by setting the RH_IDE environment variable prior to starting the Python session.

>>> input_source = sb.DataSource()
>>> my_data = range(10000)
>>> my_plot = sb.Plot()
>>> input_source.connect(my_plot)
>>> sb.start()
>>> input_source.push(my_data)

File Data Plotting Example

The following example demonstrates how to plot the contents of a data file in the sandbox. First, create a FileSource object. Then, connect it to a plot and start playing the data.

>>> input_file = sb.FileSource("~/short_file.tmp", dataFormat="short")
>>> my_plot = sb.Plot()
>>> input_file.connect(my_plot)
>>> input_file.start()

Custom Sinks

If there is a need to create a custom sink with specialized behavior, the DataSink object can be modified with a customized sink service function that allows tailoring the DataSink instance to special circumstances. The sink service function must inherit from bulkio_data_helpers.ArraySink and can overload any existing functions that need to be tailored.

The following example is a sink specialization in which the effective xdelta for the received data needs to change by a factor of two.

>>> from ossie.utils.bulkio import bulkio_data_helpers
>>> class customSink(bulkio_data_helpers.ArraySink):
...   def __init__(self, porttype):
...      bulkio_data_helpers.ArraySink.__init__(self, porttype)
...   def pushSRI(self, H):
...      _H = H
...      _H.xdelta = H.xdelta * 2
...      self.SRI = _H
...      self.SRIs.append([len(self.data), _H])

>>> src=sb.DataSource(dataFormat='float')
>>> snk = sb.DataSink(sinkClass=customSink)
>>> src.connect(snk)