Publisher-Subscriber Example
This example provides a classic publisher-subscriber pattern example. In this model, a Publisher reactor is responsible for generating data, while a Subscriber reactor listens for these updates and react accordingly.
The Data Models
The simulation is built upon two distinct data models, one for the publisher and one for the subscriber. These models define the structure of the data that each component manages.
Publisher Data Model (publisher.yml)
The publisher's data model is straightforward, containing a single word node. The Publisher reactor will periodically update the value of this node with a new word from a predefined list.
name: "publisher"
description: "Publisher"
root:
!!FolderNode
name: "root"
children:
- !!StringVariableNode
name: "word"
initial_value: ""
Subscriber Data Model (subscriber.yml)
The subscriber's data model contains a sentence node. The Subscriber reactor's goal is to build a sentence by concatenating the words it receives from the publisher.
name: "subscriber"
description: "Subscriber"
root:
!!FolderNode
name: "root"
children:
- !!StringVariableNode
name: "sentence"
initial_value: ""
The Publisher Reactor
The Publisher reactor is the source of information in this system. It is designed to periodically generate a new piece of data and publish it. The underlying Frost framework ensures that any component that has subscribed to this data is automatically notified of the update.
reactor Publisher extends FrostDataModel {
state words = ["Hello", "world", "this", "is", "a", "test", "message"]
state word_node
reaction(startup) {=
self.word_node = self.data_model.get_node("root/word")
=}
timer publish_timer(1 sec, 2 sec)
reaction(publish_timer) {=
if not self.words:
lf.request_stop()
return
word = self.words.pop(0)
self.word_node.value = word
=}
}
The Subscriber Reactor
The Subscriber reactor's function is to listen for and consume the data published by the Publisher. To do this, it must first express its interest by sending a subscription request. At startup, it sends a message to the Publisher to subscribe to the root/word node. From that point on, whenever the Publisher updates the word node, the Subscriber will receive a notification and append the new word to its sentence node.
reactor Subscriber extends FrostDataModel {
state sentence
reaction(startup) -> channel_out {=
self.sentence = self.data_model.get_node("root/sentence")
// Send a subscription request to the publisher for "root/word"
message = FrostMessage(
sender=self.name,
target="publisher",
identifier=str(uuid.uuid4()),
header=FrostHeader(
type=MsgType.REQUEST,
version=(1, 0, 0),
namespace=MsgNamespace.VARIABLE,
msg_name=VariableMsgName.SUBSCRIBE,
),
payload=VariablePayload(node="root/word"),
)
self._set_output_port(message, channel_out)
=}
reaction(message_filter.responses) {=
message = message_filter.responses.value[0][1]
if message.header.namespace == MsgNamespace.VARIABLE and message.header.msg_name == VariableMsgName.UPDATE:
self.sentence.value += " " + message.payload.value
=}
}
The main Reactor
The main reactor instantiates and connects the Publisher and Subscriber reactors. It establishes the communication channels that allow the exchange of messages between the two components.