The purpose of this page is to layout exactly what elements of SIC are being run and where , why, and how, to gain more insight into the design of the framework. It is not to analyze how face detection works.
...
FaceDetection service is ran in a separate shell, creating starting an associated ComponentManager
Code Block | ||
---|---|---|
| ||
run-face-detection |
within a shell
This runs the main() function of face_detection.py, which starts a ComponentManager
assigned to the FaceDetectionComponent
. In this example, this script is ran on the same computer as the rest of the application, although it may be ran on another computer.
...
Code Block | ||
---|---|---|
| ||
# Connect to the services desktop = Desktop(camera_conf=conf) |
within desktop_camera_facedetection.py
Rather than running a ComponentManager
in a separate terminal, the The Desktop class (inherits from Device) creates a new thread where its ComponentManager
runs.
...
Components like
FaceDetection
do not run just by themselves. They consist of aConnector
which is basically a remote control for the actual component, aComponentManager
which runs them, and then the component itself.Connectors send the actual requests to ComponentManagers to start/stop components and tell components to start listening to certain channels.
Level 2
FaceDetection service is ran in a separate shell, creating starting an associated ComponentManager
The ComponentManager
registers a request handler on a Redis channel with the same name as its IP. This means whenever a message is published on this its channel, it will process the message according to its function _handle_request
(see ComponentManager
script). As of writing this, it only expects two types of requests: it either starts a specific component given a SICStartComponentRequest
, or given a SICStopRequest
, sets a stop event to stop all of its associated components given a SICStopRequest
.
Desktop device is instantiated
Same as above, except rather than being its own process this ComponentManager
is run in a thread belonging to the main application process. Moreover, it also manages a different set of components (microphone, camera, speakers, TTS) than the FaceDetection ComponentManager
.
FaceDetection is connected to with FaceDetection Connector
The Connector
stores the names of the following Redis channels (does not necessarily use them yet):
input_channel: “FaceDetectionComponent:input:{IP}”
_request_reply_channel: “FaceDetectionComponent:reqreply:{IP}”
output_channel: “FaceDetectionComponent:{IP}”
...
Initially tries to ping the actual component. Since the actual component has not been started yet, it publishes a message on Redis which is then picked up by its ComponentManager
. The associated ComponentManager
then starts the actual component.
In this example, there are two ComponentManagers ComponentManager
s running on the same host. However, they have different sets of components. In the _handle_request
function, each ComponentManager
will check to see if the component that’s being requested belongs to it before it starts it. This way, multiple instances of the same component are not started if multiple ComponentManagers ComponentManager
s are running on the same host.
The connector then subscribes to the input channel so that messages can be manually sent if needed. **not exactly sure why we need this or if it is used.
Desktop camera feed is connected to FaceDetection
...
Code Block | ||
---|---|---|
| ||
face_rec.connect(desktop.camera) |
A component (desktop.camera) is passed into the connect() method of a Connector
Desktop.camera is passed into the connect()
method of a Connector
. In the connect()
method, the FaceDetection connector stores the output channel of the desktop.camera component in a ConnectRequest
and publishes it on the request/reply channel of the actual FaceDetection component.
Code Block | ||
---|---|---|
| ||
def connect(self, component):
"""
Connect the output of a component to the input of this component.
:param component: The component connector providing the input to this component
:type component: SICConnector
:return:
"""
assert isinstance(
component, SICConnector
), "Component connector is not a SICConnector " "(type:{})".format(
type(component)
)
request = ConnectRequest(component.output_channel)
self._redis.request(self._request_reply_channel, request) |
within connector.py
The _handle_request
function of the actual FaceDetection component then picks up the request. It calls its _connect
method which adds the output channel of desktop.camera to its list of input channels. It also registers its _handle_message
function with Redis on this channel.
Code Block | ||
---|---|---|
| ||
def _connect(self, connection_request):
"""
Connect the output of a component to the input of this component, by registering the output channel
to the on_message handler.
:param connection_request: The component serving as an input to this component.
:type connection_request: ConnectRequest
:return:
"""
channel = connection_request.channel
if channel in self._input_channels:
self.logger.debug_framework(
"Channel {} is already connected to this component".format(channel)
)
return
self._input_channels.append(channel)
self._redis.register_message_handler(channel, self._handle_message) |
within component_python2.py
So now the FaceDetection component is subscribed to the output channel of the desktop.camera, and every time it receives a message on this channel, it runs the message through its _handle_message
function.
Callback functions are registered for Desktop camera and FaceDetection component
The register_callback()
Connector
method is called for both desktop.camera and FaceDetection. The register_callback()
function takes the passed in function, and uses Redis to subscribe the function to the output channel of its respective Component
. So in this case, every time the FaceDetection finishes handling a message (where it creates a bounding box message), it publishes it on its output channel, and the callback function places it in the corresponding buffer within the application.
Buffers are continuously read from, bounding boxes are drawn on and displayed
This is the same.
...
Takeaways:
Components do not actually send message to each other directly, but rather publish messages/requests on Redis channels which are seen by other components that are subscribed to those channels.
Redis connections create separate threads where message and request handlers run.