actfw: How to use

How to use actfw #

We’ll introduce actfw and show how to use it.

Libraries #

actfw provides the two following libraries:

Installation of actfw #

You can install actfw-* via pip. actdk init generates dependencies.json that installs actfw-raspberrypi by default.

Computation model of actfw #

actfw is a framework provided by Idein that models async computations. In this model, the user defines the flow of data by using the connect method to link a task’s output to the next one’s input.

actfw model

The class Application is the entry point of the application.

app = actfw_core.Application()
app.run()

Asynchronous tasks are classified into the three following categories:

  • Producer: produces resources continuously
  • Pipe: modifies resources and passes them to the next tasks continuously
  • Consumer: finally consumes resources continuously

For example, the following three tasks:

  • Take a photo from a camera (Producer)
    • A task getting RGB pictures continuously
  • Convert an image to grayscale (Pipe)
    • A task converting an RGB picture to grayscale continuously
  • Display (Consumer)

can be described as follows:

import actfw_core
from actfw_core.task import Pipe, Consumer
from actfw_core.capture import V4LCameraCapture
import actfw_raspberrypi
from actfw_raspberrypi.vc4 import Display

class Converter(Pipe):
  ...

class Presenter(Consumer):
  ...

app = actfw_core.Application()

# Capture task
cap = V4LCameraCapture('/dev/video0', (CAPTURE_WIDTH, CAPTURE_HEIGHT), CAPTURE_FRAMERATE, format_selector=V4LCameraCapture.FormatSelector.PROPER)
app.register_task(cap)

# Converter task
conv = Converter()
app.register_task(conv)

# Presentation task
pres = Presenter(settings, camera, cmd)
app.register_task(pres)

# Make task connection
cap.connect(conv)  # from `cap` to `conv`
conv.connect(pres) # from `conv` to `pres`

app.run()

This example is a simplified version of the grayscale example in Getting Started.

Camera configuration #

The camera types compatible with a Raspberry Pi are the following two kinds:

  • Official Raspberry Pi Camera Module
  • Generic USB Camera

Note that each one of them has a different interface. actfw_raspberrypi.capture.PiCameraCapture supports the former camera, actfw_core.capture.V4LCameraCapture supports both cameras. We use the latter in this tutorial.

In actfw, V4LCameraCapture is used as a Producer that takes pictures from a camera, like the following snippet:

app = actfw_core.Application()

# Capture task
cap = V4LCameraCapture('/dev/video0', (CAPTURE_WIDTH, CAPTURE_HEIGHT), CAPTURE_FRAMERATE, format_selector=V4LCameraCapture.FormatSelector.PROPER)
app.register_task(cap)

Communication with the Actcast device agent #

The Actcast device agent sends requests to the application using a UNIX Domain Socket. The agent passes the socket’s path to the application via the environment variable ACTCAST_COMMAND_SOCK. The application is expected to create a UNIX Domain Socket to the path specified in ACTCAST_COMMAND_SOCK and wait for requests. The agent connects to the socket and sends requests that the application needs to process and reply to.

While the processes in main are implemented at the developer’s discretion, we strongly recommend handling the following requests:

  • External command requests
    • Currently TakePhoto command only

For applications using cameras, we recommend implementing the TakePhoto command; so that users can check the position of their device.

In actfw, you can use the CommandServer class for handling the requests. A CommandServer instance holds an image internally and returns it when it receives a “send-image” request. Therefore you have to update the stored image by invoking its update_image method.

class Presenter(Consumer):
    def __init__(self, ..., cmd):
      self.cmd = cmd

    def proc(self, ...):
      ...
      self.cmd.update_image(rgb_image) # update `Take Photo` image
      ...

# Actcast application
app = actfw_core.Application()

# CommandServer (for `Take Photo` command)
cmd = actfw_core.CommandServer()
app.register_task(cmd)

# Presentation task
pres = Presenter(..., cmd)
app.register_task(cmd)

Alive monitoring of applications #

If an application seems unhealthy, for example, if it continuously fails to process images, the Actcast device agent restarts the application. healthchecker performs the health check.

Here we show an example of healthchecker; this script checks if an update of the timestamp of /root/heartbeat occurred within the last minute.

#!/bin/bash

HEARTBEAT_FILE='/root/heartbeat'

[ "$(find "${HEARTBEAT_FILE}" -mmin -1)" == "${HEARTBEAT_FILE}" ]

To make healthchecker work, main has to update the timestamp of /root/heartbeat at regular intervals by calling actfw_core.heartbeat().

class Presenter(Consumer):
  def __init__(self):
    ...

  def proc(self, image):
    # update image here
    actfw_core.heartbeat()

If the previous task hangs, the timestamp update does not occur, and therefore, healthchecker detects the unhealthiness.

Display #

In general, most Actcast applications are designed for advanced sensing; thus, implementing display is often unnecessary. In some cases, however, it is useful to display computation results, e.g., for demos or checking the functioning. In actfw-raspberrypi, the Display object helps display RGB images on screens connected via HDMI.

In the following example, the Presenter class displays an RGB image to a screen. The Presenter class extends the Consumer class; it consumes resources as an ending task. Therefore, its proc method does not return any value.

In this example, we handle an HDMI display as a 640x480 region; we scale and draw 320x240 RGB images to (0, 0, 640, 480).

class Presenter(Consumer):
  def __init__(self, camera):
    self.display = Display(camera, (640, 480))

  def proc(self, image):
    self.display.update((0, 0, 640, 480), image.tobytes(), (320, 240), 'rgb')

Back to actfw